summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNobuhiko Tanibata <ntanibata@jp.adit-jv.com>2013-12-05 22:12:17 +0900
committerNobuhiko Tanibata <ntanibata@jp.adit-jv.com>2013-12-05 22:12:17 +0900
commit16d36d6da92eff18cea390acdcdc910c7fa41a3f (patch)
tree76f338aee76a4d662e4c0110a9b6441a6a6c98ce
downloadweston-16d36d6da92eff18cea390acdcdc910c7fa41a3f.tar.gz
Weston 1.3.1
-rw-r--r--.gitignore33
-rw-r--r--COPYING48
-rw-r--r--Makefile.am9
-rw-r--r--README15
-rwxr-xr-xautogen.sh9
-rw-r--r--clients/.gitignore47
-rw-r--r--clients/Makefile.am269
-rw-r--r--clients/calibrator.c277
-rw-r--r--clients/clickdot.c310
-rw-r--r--clients/cliptest.c903
-rw-r--r--clients/desktop-shell.c1323
-rw-r--r--clients/dnd.c648
-rw-r--r--clients/editor.c1250
-rw-r--r--clients/eventdemo.c417
-rw-r--r--clients/flower.c198
-rw-r--r--clients/fullscreen.c368
-rw-r--r--clients/gears.c485
-rw-r--r--clients/glmatrix.c1062
-rw-r--r--clients/image.c426
-rw-r--r--clients/keyboard.c922
-rw-r--r--clients/matrix3.xpm692
-rw-r--r--clients/multi-resource.c600
-rw-r--r--clients/nested-client.c363
-rw-r--r--clients/nested.c632
-rw-r--r--clients/resizor.c294
-rw-r--r--clients/screenshot.c309
-rw-r--r--clients/simple-egl.c781
-rw-r--r--clients/simple-shm.c427
-rw-r--r--clients/simple-touch.c340
-rw-r--r--clients/smoke.c331
-rw-r--r--clients/subsurfaces.c801
-rw-r--r--clients/tablet-shell.c478
-rw-r--r--clients/terminal.c2826
-rw-r--r--clients/transformed.c313
-rw-r--r--clients/view.c314
-rw-r--r--clients/weston-info.c457
-rw-r--r--clients/weston-simple-im.c518
-rw-r--r--clients/window.c5658
-rw-r--r--clients/window.h599
-rw-r--r--clients/wscreensaver-glue.c148
-rw-r--r--clients/wscreensaver-glue.h120
-rw-r--r--clients/wscreensaver.c344
-rw-r--r--clients/wscreensaver.h61
-rw-r--r--configure.ac498
-rw-r--r--data/.gitignore1
-rw-r--r--data/COPYING11
-rw-r--r--data/Makefile.am19
-rw-r--r--data/border.pngbin0 -> 1969 bytes
-rw-r--r--data/icon_window.pngbin0 -> 161 bytes
-rw-r--r--data/pattern.pngbin0 -> 1846 bytes
-rw-r--r--data/sign_close.pngbin0 -> 235 bytes
-rw-r--r--data/sign_maximize.pngbin0 -> 204 bytes
-rw-r--r--data/sign_minimize.pngbin0 -> 191 bytes
-rw-r--r--data/terminal.pngbin0 -> 1005 bytes
-rw-r--r--data/wayland.svg103
-rw-r--r--man/.gitignore4
-rw-r--r--man/Makefile.am25
-rw-r--r--man/weston-drm.man130
-rw-r--r--man/weston.ini.man378
-rw-r--r--man/weston.man283
-rw-r--r--notes.txt77
-rw-r--r--protocol/Makefile.am11
-rw-r--r--protocol/desktop-shell.xml112
-rw-r--r--protocol/input-method.xml273
-rw-r--r--protocol/screenshooter.xml12
-rw-r--r--protocol/subsurface.xml244
-rw-r--r--protocol/tablet-shell.xml40
-rw-r--r--protocol/text-cursor-position.xml11
-rw-r--r--protocol/text.xml346
-rw-r--r--protocol/wayland-test.xml55
-rw-r--r--protocol/workspaces.xml27
-rw-r--r--protocol/xserver.xml18
-rw-r--r--shared/Makefile.am32
-rw-r--r--shared/cairo-util.c511
-rw-r--r--shared/cairo-util.h89
-rw-r--r--shared/config-parser.c434
-rw-r--r--shared/config-parser.h114
-rw-r--r--shared/image-loader.c405
-rw-r--r--shared/image-loader.h31
-rw-r--r--shared/matrix.c273
-rw-r--r--shared/matrix.h82
-rw-r--r--shared/option-parser.c85
-rw-r--r--shared/os-compatibility.c180
-rw-r--r--shared/os-compatibility.h54
-rw-r--r--shared/zalloc.h42
-rw-r--r--src/.gitignore24
-rw-r--r--src/Makefile.am326
-rw-r--r--src/animation.c336
-rw-r--r--src/bindings.c342
-rw-r--r--src/clipboard.c300
-rw-r--r--src/cms-colord.c554
-rw-r--r--src/cms-helper.c132
-rw-r--r--src/cms-helper.h72
-rw-r--r--src/cms-static.c113
-rw-r--r--src/compositor-drm.c2736
-rw-r--r--src/compositor-fbdev.c973
-rw-r--r--src/compositor-headless.c206
-rw-r--r--src/compositor-rdp.c1094
-rw-r--r--src/compositor-rpi.c844
-rw-r--r--src/compositor-wayland.c808
-rw-r--r--src/compositor-x11.c1621
-rw-r--r--src/compositor.c3595
-rw-r--r--src/compositor.h1278
-rw-r--r--src/data-device.c677
-rw-r--r--src/evdev-touchpad.c800
-rw-r--r--src/evdev.c719
-rw-r--r--src/evdev.h139
-rw-r--r--src/filter.c338
-rw-r--r--src/filter.h67
-rw-r--r--src/gl-renderer.c1865
-rw-r--r--src/gl-renderer.h106
-rw-r--r--src/input.c1851
-rw-r--r--src/launcher-util.c397
-rw-r--r--src/launcher-util.h48
-rw-r--r--src/libbacklight.c309
-rw-r--r--src/libbacklight.h49
-rw-r--r--src/log.c132
-rw-r--r--src/noop-renderer.c98
-rw-r--r--src/pixman-renderer.c753
-rw-r--r--src/pixman-renderer.h37
-rw-r--r--src/rpi-bcm-stubs.h314
-rw-r--r--src/rpi-renderer.c1594
-rw-r--r--src/rpi-renderer.h48
-rw-r--r--src/screenshooter.c591
-rw-r--r--src/shell.c4799
-rw-r--r--src/spring-tool.c66
-rw-r--r--src/tablet-shell.c575
-rw-r--r--src/text-backend.c967
-rw-r--r--src/udev-seat.c380
-rw-r--r--src/udev-seat.h56
-rw-r--r--src/vaapi-recorder.c1154
-rw-r--r--src/vaapi-recorder.h35
-rw-r--r--src/version.h.in37
-rw-r--r--src/vertex-clipping.c317
-rw-r--r--src/vertex-clipping.h65
-rw-r--r--src/weston-egl-ext.h80
-rw-r--r--src/weston-launch.c750
-rw-r--r--src/weston-launch.h46
-rw-r--r--src/weston.pc.in11
-rw-r--r--src/xwayland/Makefile.am40
-rw-r--r--src/xwayland/dnd.c274
-rw-r--r--src/xwayland/hash.c309
-rw-r--r--src/xwayland/hash.h49
-rw-r--r--src/xwayland/launcher.c389
-rw-r--r--src/xwayland/selection.c712
-rw-r--r--src/xwayland/window-manager.c2186
-rw-r--r--src/xwayland/xwayland.h175
-rw-r--r--src/zoom.c397
-rw-r--r--tests/.gitignore13
-rw-r--r--tests/Makefile.am158
-rw-r--r--tests/button-test.c55
-rw-r--r--tests/config-parser-test.c203
-rw-r--r--tests/event-test.c418
-rw-r--r--tests/keyboard-test.c65
-rw-r--r--tests/matrix-test.c419
-rw-r--r--tests/setbacklight.c186
-rw-r--r--tests/subsurface-test.c549
-rw-r--r--tests/surface-global-test.c80
-rw-r--r--tests/surface-test.c63
-rw-r--r--tests/text-test.c214
-rw-r--r--tests/vertex-clip-test.c219
-rw-r--r--tests/weston-test-client-helper.c539
-rw-r--r--tests/weston-test-client-helper.h123
-rw-r--r--tests/weston-test-runner.c167
-rw-r--r--tests/weston-test-runner.h76
-rw-r--r--tests/weston-test.c250
-rwxr-xr-xtests/weston-tests-env43
-rw-r--r--tests/xwayland-test.c142
-rw-r--r--wayland-scanner.mk8
-rw-r--r--wcap/.gitignore3
-rw-r--r--wcap/Makefile.am9
-rw-r--r--wcap/README99
-rw-r--r--wcap/main.c304
-rw-r--r--wcap/wcap-decode.c152
-rw-r--r--wcap/wcap-decode.h63
-rw-r--r--weston.ini66
176 files changed, 76276 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..f96bbd19
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,33 @@
+*.deps
+*.jpg
+*.la
+*.lo
+*.o
+*.pc
+*.so
+*.swp
+*~
+ctags
+cscope.out
+.libs
+/aclocal.m4
+/autom4te.cache
+/compile
+/config.guess
+/config.h
+/config.h.in
+/config.log
+/config.mk
+/config.status
+/config.sub
+/configure
+/depcomp
+/install-sh
+/libtool
+/ltmain.sh
+/missing
+/stamp-h1
+/test-driver
+Makefile
+Makefile.in
+TAGS
diff --git a/COPYING b/COPYING
new file mode 100644
index 00000000..e0b605d0
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,48 @@
+Copyright © 2008-2012 Kristian Høgsberg
+Copyright © 2010-2012 Intel Corporation
+Copyright © 2010-2011 Benjamin Franzke
+Copyright © 2011-2012 Collabora, Ltd.
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+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.
+
+
+For libbacklight.c:
+
+Copyright © 2012 Intel Corporation
+Copyright © 2010 Red Hat <mjg@redhat.com>
+
+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.
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 00000000..e9ecc380
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,9 @@
+if BUILD_WCAP_TOOLS
+wcap_subdir = wcap
+endif
+
+SUBDIRS = shared src clients data protocol tests $(wcap_subdir) man
+
+DISTCHECK_CONFIGURE_FLAGS = --disable-setuid-install
+
+EXTRA_DIST = weston.ini wayland-scanner.mk
diff --git a/README b/README
new file mode 100644
index 00000000..b3be4c99
--- /dev/null
+++ b/README
@@ -0,0 +1,15 @@
+Weston
+
+Weston is the reference implementation of a Wayland compositor, and a
+useful compositor in its own right. Weston has various backends that
+lets it run on Linux kernel modesetting and evdev input as well as
+under X11. Weston ships with a few example clients, from simple
+clients that demonstrate certain aspects of the protocol to more
+complete clients and a simplistic toolkit. There is also a quite
+capable terminal emulator (weston-terminal) and an toy/example desktop
+shell. Finally, weston also provides integration with the Xorg server
+and can pull X clients into the Wayland desktop and act as a X window
+manager.
+
+Refer to http://wayland.freedesktop.org/building.html for buiding
+weston and its dependencies.
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 00000000..916169a4
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,9 @@
+#! /bin/sh
+
+test -n "$srcdir" || srcdir=`dirname "$0"`
+test -n "$srcdir" || srcdir=.
+(
+ cd "$srcdir" &&
+ autoreconf --force -v --install
+) || exit
+test -n "$NOCONFIGURE" || "$srcdir/configure" "$@"
diff --git a/clients/.gitignore b/clients/.gitignore
new file mode 100644
index 00000000..23959cce
--- /dev/null
+++ b/clients/.gitignore
@@ -0,0 +1,47 @@
+weston-calibrator
+weston-clickdot
+weston-cliptest
+weston-dnd
+weston-editor
+weston-eventdemo
+weston-flower
+weston-fullscreen
+weston-gears
+weston-image
+weston-nested
+weston-nested-client
+weston-resizor
+weston-simple-egl
+weston-simple-shm
+weston-simple-touch
+weston-smoke
+weston-subsurfaces
+weston-transformed
+weston-view
+
+desktop-shell-client-protocol.h
+desktop-shell-protocol.c
+input-method-protocol.c
+input-method-client-protocol.h
+weston-keyboard
+libtoytoolkit.a
+screenshooter-client-protocol.h
+screenshooter-protocol.c
+subsurface-client-protocol.h
+subsurface-protocol.c
+tablet-shell-client-protocol.h
+tablet-shell-protocol.c
+text-client-protocol.h
+text-cursor-position-client-protocol.h
+text-cursor-position-protocol.c
+text-protocol.c
+weston-desktop-shell
+weston-info
+weston-screensaver
+weston-screenshooter
+weston-tablet-shell
+weston-terminal
+weston-multi-resource
+workspaces-client-protocol.h
+workspaces-protocol.c
+weston-simple-im
diff --git a/clients/Makefile.am b/clients/Makefile.am
new file mode 100644
index 00000000..4f9dc481
--- /dev/null
+++ b/clients/Makefile.am
@@ -0,0 +1,269 @@
+bin_PROGRAMS = \
+ weston-info \
+ $(terminal)
+
+demo_clients = \
+ $(clients_programs) \
+ $(pango_programs) \
+ $(poppler_programs) \
+ $(simple_clients_programs) \
+ $(simple_egl_clients_programs)
+
+if ENABLE_DEMO_CLIENTS
+bin_PROGRAMS += $(demo_clients)
+else
+noinst_PROGRAMS = $(demo_clients)
+endif
+
+libexec_PROGRAMS = \
+ $(desktop_shell) \
+ $(tablet_shell) \
+ $(screenshooter) \
+ $(screensaver) \
+ $(keyboard) \
+ weston-simple-im
+
+AM_CFLAGS = $(GCC_CFLAGS)
+AM_CPPFLAGS = \
+ -DDATADIR='"$(datadir)"' \
+ -DBINDIR='"$(bindir)"' \
+ $(CLIENT_CFLAGS) $(CAIRO_EGL_CFLAGS)
+
+if BUILD_SIMPLE_CLIENTS
+simple_clients_programs = \
+ weston-simple-shm \
+ weston-simple-touch \
+ weston-multi-resource
+
+weston_simple_shm_SOURCES = simple-shm.c \
+ ../shared/os-compatibility.c \
+ ../shared/os-compatibility.h
+weston_simple_shm_CPPFLAGS = $(SIMPLE_CLIENT_CFLAGS)
+weston_simple_shm_LDADD = $(SIMPLE_CLIENT_LIBS)
+
+weston_simple_touch_SOURCES = simple-touch.c \
+ ../shared/os-compatibility.c \
+ ../shared/os-compatibility.h
+weston_simple_touch_CPPFLAGS = $(SIMPLE_CLIENT_CFLAGS)
+weston_simple_touch_LDADD = $(SIMPLE_CLIENT_LIBS)
+
+weston_multi_resource_SOURCES = multi-resource.c \
+ ../shared/os-compatibility.c \
+ ../shared/os-compatibility.h
+weston_multi_resource_CPPFLAGS = $(SIMPLE_CLIENT_CFLAGS)
+weston_multi_resource_LDADD = $(SIMPLE_CLIENT_LIBS) -lm
+endif
+
+if BUILD_SIMPLE_EGL_CLIENTS
+simple_egl_clients_programs = \
+ weston-simple-egl
+
+weston_simple_egl_SOURCES = simple-egl.c
+weston_simple_egl_CPPFLAGS = $(SIMPLE_EGL_CLIENT_CFLAGS)
+weston_simple_egl_LDADD = $(SIMPLE_EGL_CLIENT_LIBS) -lm
+endif
+
+if BUILD_CLIENTS
+terminal = weston-terminal
+
+clients_programs = \
+ weston-flower \
+ weston-image \
+ weston-cliptest \
+ weston-dnd \
+ weston-smoke \
+ weston-resizor \
+ weston-eventdemo \
+ weston-clickdot \
+ weston-transformed \
+ weston-fullscreen \
+ weston-calibrator \
+ $(subsurfaces) \
+ $(full_gl_client_programs) \
+ $(cairo_glesv2_programs)
+
+desktop_shell = weston-desktop-shell
+
+if ENABLE_TABLET_SHELL
+tablet_shell = weston-tablet-shell
+endif
+
+screenshooter = weston-screenshooter
+
+noinst_LTLIBRARIES = libtoytoolkit.la
+
+libtoytoolkit_la_SOURCES = \
+ window.c \
+ window.h \
+ text-cursor-position-protocol.c \
+ text-cursor-position-client-protocol.h \
+ subsurface-protocol.c \
+ subsurface-client-protocol.h \
+ workspaces-protocol.c \
+ workspaces-client-protocol.h
+
+libtoytoolkit_la_LIBADD = \
+ $(CLIENT_LIBS) \
+ $(CAIRO_EGL_LIBS) \
+ ../shared/libshared-cairo.la -lrt -lm
+
+weston_flower_SOURCES = flower.c
+weston_flower_LDADD = libtoytoolkit.la
+
+weston_screenshooter_SOURCES = \
+ screenshot.c \
+ screenshooter-protocol.c \
+ screenshooter-client-protocol.h \
+ ../shared/os-compatibility.c \
+ ../shared/os-compatibility.h
+weston_screenshooter_LDADD = $(CLIENT_LIBS)
+
+weston_terminal_SOURCES = terminal.c
+weston_terminal_LDADD = libtoytoolkit.la -lutil
+
+weston_image_SOURCES = image.c
+weston_image_LDADD = libtoytoolkit.la
+
+weston_cliptest_SOURCES = cliptest.c
+weston_cliptest_CPPFLAGS = $(AM_CPPFLAGS) $(PIXMAN_CFLAGS)
+weston_cliptest_LDADD = libtoytoolkit.la $(PIXMAN_LIBS)
+
+weston_dnd_SOURCES = dnd.c
+weston_dnd_LDADD = libtoytoolkit.la
+
+weston_smoke_SOURCES = smoke.c
+weston_smoke_LDADD = libtoytoolkit.la
+
+weston_resizor_SOURCES = resizor.c
+weston_resizor_LDADD = libtoytoolkit.la
+
+if HAVE_CAIRO_GLESV2
+cairo_glesv2_programs = weston-nested weston-nested-client
+
+weston_nested_SOURCES = nested.c
+weston_nested_LDADD = libtoytoolkit.la $(SERVER_LIBS)
+
+weston_nested_client_SOURCES = nested-client.c
+weston_nested_client_LDADD = $(SIMPLE_EGL_CLIENT_LIBS) -lm
+endif
+
+weston_eventdemo_SOURCES = eventdemo.c
+weston_eventdemo_LDADD = libtoytoolkit.la
+
+weston_clickdot_SOURCES = clickdot.c
+weston_clickdot_LDADD = libtoytoolkit.la
+
+weston_transformed_SOURCES = transformed.c
+weston_transformed_LDADD = libtoytoolkit.la
+
+weston_fullscreen_SOURCES = fullscreen.c
+weston_fullscreen_LDADD = libtoytoolkit.la
+
+weston_calibrator_SOURCES = calibrator.c \
+ ../shared/matrix.c \
+ ../shared/matrix.h
+weston_calibrator_LDADD = libtoytoolkit.la
+
+if BUILD_SUBSURFACES_CLIENT
+subsurfaces = weston-subsurfaces
+weston_subsurfaces_SOURCES = subsurfaces.c
+weston_subsurfaces_CPPFLAGS = $(AM_CPPFLAGS) $(SIMPLE_EGL_CLIENT_CFLAGS)
+weston_subsurfaces_LDADD = libtoytoolkit.la $(SIMPLE_EGL_CLIENT_LIBS) -lm
+endif
+
+if HAVE_PANGO
+pango_programs = weston-editor
+weston_editor_SOURCES = \
+ editor.c \
+ text-protocol.c \
+ text-client-protocol.h
+weston_editor_LDADD = libtoytoolkit.la $(PANGO_LIBS)
+weston_editor_CPPFLAGS = $(AM_CPPFLAGS) $(PANGO_CFLAGS)
+endif
+
+keyboard = weston-keyboard
+weston_keyboard_SOURCES = \
+ keyboard.c \
+ desktop-shell-client-protocol.h \
+ desktop-shell-protocol.c \
+ input-method-protocol.c \
+ input-method-client-protocol.h
+weston_keyboard_LDADD = libtoytoolkit.la
+
+weston_simple_im_SOURCES = \
+ weston-simple-im.c \
+ input-method-protocol.c \
+ input-method-client-protocol.h
+weston_simple_im_LDADD = $(CLIENT_LIBS)
+
+weston_info_SOURCES = \
+ weston-info.c \
+ ../shared/os-compatibility.c \
+ ../shared/os-compatibility.h
+weston_info_LDADD = $(WESTON_INFO_LIBS)
+
+weston_desktop_shell_SOURCES = \
+ desktop-shell.c \
+ desktop-shell-client-protocol.h \
+ desktop-shell-protocol.c
+weston_desktop_shell_LDADD = libtoytoolkit.la
+
+weston_tablet_shell_SOURCES = \
+ tablet-shell.c \
+ tablet-shell-client-protocol.h \
+ tablet-shell-protocol.c
+weston_tablet_shell_LDADD = libtoytoolkit.la
+
+BUILT_SOURCES = \
+ screenshooter-client-protocol.h \
+ screenshooter-protocol.c \
+ text-cursor-position-client-protocol.h \
+ text-cursor-position-protocol.c \
+ text-protocol.c \
+ text-client-protocol.h \
+ input-method-protocol.c \
+ input-method-client-protocol.h \
+ desktop-shell-client-protocol.h \
+ desktop-shell-protocol.c \
+ tablet-shell-client-protocol.h \
+ tablet-shell-protocol.c \
+ subsurface-client-protocol.h \
+ subsurface-protocol.c \
+ workspaces-client-protocol.h \
+ workspaces-protocol.c
+
+CLEANFILES = $(BUILT_SOURCES)
+endif
+
+if BUILD_FULL_GL_CLIENTS
+full_gl_client_programs = weston-gears
+
+weston_gears_SOURCES = gears.c
+weston_gears_LDADD = libtoytoolkit.la
+
+if HAVE_GLU
+screensaver = weston-screensaver
+weston_screensaver_SOURCES = \
+ wscreensaver.c \
+ wscreensaver.h \
+ desktop-shell-client-protocol.h \
+ desktop-shell-protocol.c \
+ wscreensaver-glue.c \
+ wscreensaver-glue.h \
+ glmatrix.c \
+ matrix3.xpm
+weston_screensaver_LDADD = libtoytoolkit.la $(GLU_LIBS)
+weston_screensaver_CFLAGS = $(GLU_CFLAGS)
+endif
+
+endif
+
+wayland_protocoldir = $(top_srcdir)/protocol
+include $(top_srcdir)/wayland-scanner.mk
+
+if HAVE_POPPLER
+poppler_programs = weston-view
+weston_view_SOURCES = view.c
+weston_view_LDADD = libtoytoolkit.la $(POPPLER_LIBS)
+weston_view_CPPFLAGS = $(AM_CPPFLAGS) $(POPPLER_CFLAGS)
+endif
diff --git a/clients/calibrator.c b/clients/calibrator.c
new file mode 100644
index 00000000..783cdecc
--- /dev/null
+++ b/clients/calibrator.c
@@ -0,0 +1,277 @@
+/*
+ * Copyright © 2012 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
+ * 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.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <cairo.h>
+#include <math.h>
+#include <assert.h>
+
+#include <linux/input.h>
+#include <wayland-client.h>
+
+#include "window.h"
+#include "../shared/matrix.h"
+
+#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0])
+
+/* Our points for the calibration must be not be on a line */
+static const struct {
+ float x_ratio, y_ratio;
+} test_ratios[] = {
+ { 0.20, 0.40 },
+ { 0.80, 0.60 },
+ { 0.40, 0.80 }
+};
+
+struct calibrator {
+ struct tests {
+ int32_t drawn_x, drawn_y;
+ int32_t clicked_x, clicked_y;
+ } tests[ARRAY_LENGTH(test_ratios)];
+ int current_test;
+
+ struct display *display;
+ struct window *window;
+ struct widget *widget;
+};
+
+/*
+ * Calibration algorithm:
+ *
+ * The equation we want to apply at event time where x' and y' are the
+ * calibrated co-ordinates.
+ *
+ * x' = Ax + By + C
+ * y' = Dx + Ey + F
+ *
+ * For example "zero calibration" would be A=1.0 B=0.0 C=0.0, D=0.0, E=1.0,
+ * and F=0.0.
+ *
+ * With 6 unknowns we need 6 equations to find the constants:
+ *
+ * x1' = Ax1 + By1 + C
+ * y1' = Dx1 + Ey1 + F
+ * ...
+ * x3' = Ax3 + By3 + C
+ * y3' = Dx3 + Ey3 + F
+ *
+ * In matrix form:
+ *
+ * x1' x1 y1 1 A
+ * x2' = x2 y2 1 x B
+ * x3' x3 y3 1 C
+ *
+ * So making the matrix M we can find the constants with:
+ *
+ * A x1'
+ * B = M^-1 x x2'
+ * C x3'
+ *
+ * (and similarly for D, E and F)
+ *
+ * For the calibration the desired values x, y are the same values at which
+ * we've drawn at.
+ *
+ */
+static void
+finish_calibration (struct calibrator *calibrator)
+{
+ struct weston_matrix m;
+ struct weston_matrix inverse;
+ struct weston_vector x_calib, y_calib;
+ int i;
+
+
+ /*
+ * x1 y1 1 0
+ * x2 y2 1 0
+ * x3 y3 1 0
+ * 0 0 0 1
+ */
+ memset(&m, 0, sizeof(m));
+ for (i = 0; i < (int)ARRAY_LENGTH(test_ratios); i++) {
+ m.d[i] = calibrator->tests[i].clicked_x;
+ m.d[i + 4] = calibrator->tests[i].clicked_y;
+ m.d[i + 8] = 1;
+ }
+ m.d[15] = 1;
+
+ weston_matrix_invert(&inverse, &m);
+
+ memset(&x_calib, 0, sizeof(x_calib));
+ memset(&y_calib, 0, sizeof(y_calib));
+
+ for (i = 0; i < (int)ARRAY_LENGTH(test_ratios); i++) {
+ x_calib.f[i] = calibrator->tests[i].drawn_x;
+ y_calib.f[i] = calibrator->tests[i].drawn_y;
+ }
+
+ /* Multiples into the vector */
+ weston_matrix_transform(&inverse, &x_calib);
+ weston_matrix_transform(&inverse, &y_calib);
+
+ printf ("Calibration values: %f %f %f %f %f %f\n",
+ x_calib.f[0], x_calib.f[1], x_calib.f[2],
+ y_calib.f[0], y_calib.f[1], y_calib.f[2]);
+
+ exit(0);
+}
+
+
+static void
+button_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ uint32_t button,
+ enum wl_pointer_button_state state, void *data)
+{
+ struct calibrator *calibrator = data;
+ int32_t x, y;
+
+ if (state == WL_POINTER_BUTTON_STATE_PRESSED && button == BTN_LEFT) {
+ input_get_position(input, &x, &y);
+ calibrator->tests[calibrator->current_test].clicked_x = x;
+ calibrator->tests[calibrator->current_test].clicked_y = y;
+
+ calibrator->current_test--;
+ if (calibrator->current_test < 0)
+ finish_calibration(calibrator);
+ }
+
+ widget_schedule_redraw(widget);
+}
+
+static void
+touch_handler(struct widget *widget, struct input *input, uint32_t serial,
+ uint32_t time, int32_t id, float x, float y, void *data)
+{
+ struct calibrator *calibrator = data;
+
+ calibrator->tests[calibrator->current_test].clicked_x = x;
+ calibrator->tests[calibrator->current_test].clicked_y = y;
+ calibrator->current_test--;
+
+ if (calibrator->current_test < 0)
+ finish_calibration(calibrator);
+
+ widget_schedule_redraw(widget);
+}
+
+static void
+redraw_handler(struct widget *widget, void *data)
+{
+ struct calibrator *calibrator = data;
+ struct rectangle allocation;
+ cairo_surface_t *surface;
+ cairo_t *cr;
+ int32_t drawn_x, drawn_y;
+
+ widget_get_allocation(calibrator->widget, &allocation);
+ surface = window_get_surface(calibrator->window);
+
+ cr = cairo_create(surface);
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0);
+ cairo_paint(cr);
+
+ drawn_x = test_ratios[calibrator->current_test].x_ratio * allocation.width;
+ drawn_y = test_ratios[calibrator->current_test].y_ratio * allocation.height;
+
+ calibrator->tests[calibrator->current_test].drawn_x = drawn_x;
+ calibrator->tests[calibrator->current_test].drawn_y = drawn_y;
+
+ cairo_translate(cr, drawn_x, drawn_y);
+ cairo_set_line_width(cr, 2.0);
+ cairo_set_source_rgb(cr, 1.0, 0.0, 0.0);
+ cairo_move_to(cr, 0, -10.0);
+ cairo_line_to(cr, 0, 10.0);
+ cairo_stroke(cr);
+ cairo_move_to(cr, -10.0, 0);
+ cairo_line_to(cr, 10.0, 0.0);
+ cairo_stroke(cr);
+
+ cairo_destroy(cr);
+ cairo_surface_destroy(surface);
+}
+
+static struct calibrator *
+calibrator_create(struct display *display)
+{
+ struct calibrator *calibrator;
+
+ calibrator = malloc(sizeof *calibrator);
+ if (calibrator == NULL)
+ return NULL;
+
+ calibrator->window = window_create(display);
+ calibrator->widget = window_add_widget(calibrator->window, calibrator);
+ window_set_title(calibrator->window, "Wayland calibrator");
+ calibrator->display = display;
+
+ calibrator->current_test = ARRAY_LENGTH(test_ratios) - 1;
+
+ widget_set_button_handler(calibrator->widget, button_handler);
+ widget_set_touch_down_handler(calibrator->widget, touch_handler);
+ widget_set_redraw_handler(calibrator->widget, redraw_handler);
+
+ window_set_fullscreen(calibrator->window, 1);
+
+ return calibrator;
+}
+
+static void
+calibrator_destroy(struct calibrator *calibrator)
+{
+ widget_destroy(calibrator->widget);
+ window_destroy(calibrator->window);
+ free(calibrator);
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ struct display *display;
+ struct calibrator *calibrator;
+
+ display = display_create(&argc, argv);
+
+ if (display == NULL) {
+ fprintf(stderr, "failed to create display: %m\n");
+ return -1;
+ }
+
+ calibrator = calibrator_create(display);
+
+ if (!calibrator)
+ return -1;
+
+ display_run(display);
+
+ calibrator_destroy(calibrator);
+ display_destroy(display);
+
+ return 0;
+}
+
diff --git a/clients/clickdot.c b/clients/clickdot.c
new file mode 100644
index 00000000..aebe4db1
--- /dev/null
+++ b/clients/clickdot.c
@@ -0,0 +1,310 @@
+/*
+ * Copyright © 2010 Intel Corporation
+ * Copyright © 2012 Collabora, Ltd.
+ * Copyright © 2012 Jonas Ådahl
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * 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.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <cairo.h>
+#include <math.h>
+#include <assert.h>
+
+#include <linux/input.h>
+#include <wayland-client.h>
+
+#include "window.h"
+
+struct clickdot {
+ struct display *display;
+ struct window *window;
+ struct widget *widget;
+
+ cairo_surface_t *buffer;
+
+ struct {
+ int32_t x, y;
+ } dot;
+
+ struct {
+ int32_t x, y;
+ int32_t old_x, old_y;
+ } line;
+
+ int reset;
+};
+
+static void
+draw_line(struct clickdot *clickdot, cairo_t *cr,
+ struct rectangle *allocation)
+{
+ cairo_t *bcr;
+ cairo_surface_t *tmp_buffer = NULL;
+
+ if (clickdot->reset) {
+ tmp_buffer = clickdot->buffer;
+ clickdot->buffer = NULL;
+ clickdot->line.x = -1;
+ clickdot->line.y = -1;
+ clickdot->line.old_x = -1;
+ clickdot->line.old_y = -1;
+ clickdot->reset = 0;
+ }
+
+ if (clickdot->buffer == NULL) {
+ clickdot->buffer =
+ cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
+ allocation->width,
+ allocation->height);
+ bcr = cairo_create(clickdot->buffer);
+ cairo_set_source_rgba(bcr, 0, 0, 0, 0);
+ cairo_rectangle(bcr,
+ 0, 0,
+ allocation->width, allocation->height);
+ cairo_fill(bcr);
+ }
+ else
+ bcr = cairo_create(clickdot->buffer);
+
+ if (tmp_buffer) {
+ cairo_set_source_surface(bcr, tmp_buffer, 0, 0);
+ cairo_rectangle(bcr, 0, 0,
+ allocation->width, allocation->height);
+ cairo_clip(bcr);
+ cairo_paint(bcr);
+
+ cairo_surface_destroy(tmp_buffer);
+ }
+
+ if (clickdot->line.x != -1 && clickdot->line.y != -1) {
+ if (clickdot->line.old_x != -1 &&
+ clickdot->line.old_y != -1) {
+ cairo_set_line_width(bcr, 2.0);
+ cairo_set_source_rgb(bcr, 1, 1, 1);
+ cairo_translate(bcr,
+ -allocation->x, -allocation->y);
+
+ cairo_move_to(bcr,
+ clickdot->line.old_x,
+ clickdot->line.old_y);
+ cairo_line_to(bcr,
+ clickdot->line.x,
+ clickdot->line.y);
+
+ cairo_stroke(bcr);
+ }
+
+ clickdot->line.old_x = clickdot->line.x;
+ clickdot->line.old_y = clickdot->line.y;
+ }
+ cairo_destroy(bcr);
+
+ cairo_set_source_surface(cr, clickdot->buffer,
+ allocation->x, allocation->y);
+ cairo_set_operator(cr, CAIRO_OPERATOR_ADD);
+ cairo_rectangle(cr,
+ allocation->x, allocation->y,
+ allocation->width, allocation->height);
+ cairo_clip(cr);
+ cairo_paint(cr);
+}
+
+static void
+redraw_handler(struct widget *widget, void *data)
+{
+ static const double r = 10.0;
+ struct clickdot *clickdot = data;
+ cairo_surface_t *surface;
+ cairo_t *cr;
+ struct rectangle allocation;
+
+ widget_get_allocation(clickdot->widget, &allocation);
+
+ surface = window_get_surface(clickdot->window);
+
+ cr = cairo_create(surface);
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ cairo_rectangle(cr,
+ allocation.x,
+ allocation.y,
+ allocation.width,
+ allocation.height);
+ cairo_set_source_rgba(cr, 0, 0, 0, 0.8);
+ cairo_fill(cr);
+
+ draw_line(clickdot, cr, &allocation);
+
+ cairo_translate(cr, clickdot->dot.x + 0.5, clickdot->dot.y + 0.5);
+ cairo_set_line_width(cr, 1.0);
+ cairo_set_source_rgb(cr, 0.1, 0.9, 0.9);
+ cairo_move_to(cr, 0.0, -r);
+ cairo_line_to(cr, 0.0, r);
+ cairo_move_to(cr, -r, 0.0);
+ cairo_line_to(cr, r, 0.0);
+ cairo_arc(cr, 0.0, 0.0, r, 0.0, 2.0 * M_PI);
+ cairo_stroke(cr);
+
+ cairo_destroy(cr);
+
+ cairo_surface_destroy(surface);
+}
+
+static void
+keyboard_focus_handler(struct window *window,
+ struct input *device, void *data)
+{
+ struct clickdot *clickdot = data;
+
+ window_schedule_redraw(clickdot->window);
+}
+
+static void
+key_handler(struct window *window, struct input *input, uint32_t time,
+ uint32_t key, uint32_t sym,
+ enum wl_keyboard_key_state state, void *data)
+{
+ struct clickdot *clickdot = data;
+
+ if (state == WL_KEYBOARD_KEY_STATE_RELEASED)
+ return;
+
+ switch (sym) {
+ case XKB_KEY_Escape:
+ display_exit(clickdot->display);
+ break;
+ }
+}
+
+static void
+button_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ uint32_t button,
+ enum wl_pointer_button_state state, void *data)
+{
+ struct clickdot *clickdot = data;
+
+ if (state == WL_POINTER_BUTTON_STATE_PRESSED && button == BTN_LEFT)
+ input_get_position(input, &clickdot->dot.x, &clickdot->dot.y);
+
+ widget_schedule_redraw(widget);
+}
+
+static int
+motion_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ float x, float y, void *data)
+{
+ struct clickdot *clickdot = data;
+ clickdot->line.x = x;
+ clickdot->line.y = y;
+
+ window_schedule_redraw(clickdot->window);
+
+ return CURSOR_LEFT_PTR;
+}
+
+static void
+resize_handler(struct widget *widget,
+ int32_t width, int32_t height,
+ void *data)
+{
+ struct clickdot *clickdot = data;
+
+ clickdot->reset = 1;
+}
+
+static void
+leave_handler(struct widget *widget,
+ struct input *input, void *data)
+{
+ struct clickdot *clickdot = data;
+
+ clickdot->reset = 1;
+}
+
+static struct clickdot *
+clickdot_create(struct display *display)
+{
+ struct clickdot *clickdot;
+
+ clickdot = xzalloc(sizeof *clickdot);
+ clickdot->window = window_create(display);
+ clickdot->widget = frame_create(clickdot->window, clickdot);
+ window_set_title(clickdot->window, "Wayland ClickDot");
+ clickdot->display = display;
+ clickdot->buffer = NULL;
+
+ window_set_key_handler(clickdot->window, key_handler);
+ window_set_user_data(clickdot->window, clickdot);
+ window_set_keyboard_focus_handler(clickdot->window,
+ keyboard_focus_handler);
+
+ widget_set_redraw_handler(clickdot->widget, redraw_handler);
+ widget_set_button_handler(clickdot->widget, button_handler);
+ widget_set_motion_handler(clickdot->widget, motion_handler);
+ widget_set_resize_handler(clickdot->widget, resize_handler);
+ widget_set_leave_handler(clickdot->widget, leave_handler);
+
+ widget_schedule_resize(clickdot->widget, 500, 400);
+ clickdot->dot.x = 250;
+ clickdot->dot.y = 200;
+ clickdot->line.x = -1;
+ clickdot->line.y = -1;
+ clickdot->line.old_x = -1;
+ clickdot->line.old_y = -1;
+ clickdot->reset = 0;
+
+ return clickdot;
+}
+
+static void
+clickdot_destroy(struct clickdot *clickdot)
+{
+ if (clickdot->buffer)
+ cairo_surface_destroy(clickdot->buffer);
+ widget_destroy(clickdot->widget);
+ window_destroy(clickdot->window);
+ free(clickdot);
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct display *display;
+ struct clickdot *clickdot;
+
+ display = display_create(&argc, argv);
+ if (display == NULL) {
+ fprintf(stderr, "failed to create display: %m\n");
+ return -1;
+ }
+
+ clickdot = clickdot_create(display);
+
+ display_run(display);
+
+ clickdot_destroy(clickdot);
+ display_destroy(display);
+
+ return 0;
+}
diff --git a/clients/cliptest.c b/clients/cliptest.c
new file mode 100644
index 00000000..4b778087
--- /dev/null
+++ b/clients/cliptest.c
@@ -0,0 +1,903 @@
+/*
+ * Copyright © 2012 Collabora, Ltd.
+ * Copyright © 2012 Rob Clark
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * 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.
+ */
+
+/* cliptest: for debugging calculate_edges() function, which is copied
+ * from compositor.c.
+ * controls:
+ * clip box position: mouse left drag, keys: w a s d
+ * clip box size: mouse right drag, keys: i j k l
+ * surface orientation: mouse wheel, keys: n m
+ * surface transform disable key: r
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <unistd.h>
+#include <math.h>
+#include <time.h>
+#include <pixman.h>
+#include <cairo.h>
+#include <float.h>
+#include <assert.h>
+
+#include <linux/input.h>
+#include <wayland-client.h>
+
+#include "window.h"
+
+typedef float GLfloat;
+
+struct geometry {
+ pixman_box32_t clip;
+
+ pixman_box32_t surf;
+ float s; /* sin phi */
+ float c; /* cos phi */
+ float phi;
+};
+
+struct weston_surface {
+ struct {
+ int enabled;
+ } transform;
+
+ struct geometry *geometry;
+};
+
+static void
+weston_surface_to_global_float(struct weston_surface *surface,
+ GLfloat sx, GLfloat sy, GLfloat *x, GLfloat *y)
+{
+ struct geometry *g = surface->geometry;
+
+ /* pure rotation around origin by sine and cosine */
+ *x = g->c * sx + g->s * sy;
+ *y = -g->s * sx + g->c * sy;
+}
+
+/* ---------------------- copied begins -----------------------*/
+
+struct polygon8 {
+ GLfloat x[8];
+ GLfloat y[8];
+ int n;
+};
+
+struct clip_context {
+ struct {
+ GLfloat x;
+ GLfloat y;
+ } prev;
+
+ struct {
+ GLfloat x1, y1;
+ GLfloat x2, y2;
+ } clip;
+
+ struct {
+ GLfloat *x;
+ GLfloat *y;
+ } vertices;
+};
+
+static GLfloat
+float_difference(GLfloat a, GLfloat b)
+{
+ /* http://www.altdevblogaday.com/2012/02/22/comparing-floating-point-numbers-2012-edition/ */
+ static const GLfloat max_diff = 4.0f * FLT_MIN;
+ static const GLfloat max_rel_diff = 4.0e-5;
+ GLfloat diff = a - b;
+ GLfloat adiff = fabsf(diff);
+
+ if (adiff <= max_diff)
+ return 0.0f;
+
+ a = fabsf(a);
+ b = fabsf(b);
+ if (adiff <= (a > b ? a : b) * max_rel_diff)
+ return 0.0f;
+
+ return diff;
+}
+
+/* A line segment (p1x, p1y)-(p2x, p2y) intersects the line x = x_arg.
+ * Compute the y coordinate of the intersection.
+ */
+static GLfloat
+clip_intersect_y(GLfloat p1x, GLfloat p1y, GLfloat p2x, GLfloat p2y,
+ GLfloat x_arg)
+{
+ GLfloat a;
+ GLfloat diff = float_difference(p1x, p2x);
+
+ /* Practically vertical line segment, yet the end points have already
+ * been determined to be on different sides of the line. Therefore
+ * the line segment is part of the line and intersects everywhere.
+ * Return the end point, so we use the whole line segment.
+ */
+ if (diff == 0.0f)
+ return p2y;
+
+ a = (x_arg - p2x) / diff;
+ return p2y + (p1y - p2y) * a;
+}
+
+/* A line segment (p1x, p1y)-(p2x, p2y) intersects the line y = y_arg.
+ * Compute the x coordinate of the intersection.
+ */
+static GLfloat
+clip_intersect_x(GLfloat p1x, GLfloat p1y, GLfloat p2x, GLfloat p2y,
+ GLfloat y_arg)
+{
+ GLfloat a;
+ GLfloat diff = float_difference(p1y, p2y);
+
+ /* Practically horizontal line segment, yet the end points have already
+ * been determined to be on different sides of the line. Therefore
+ * the line segment is part of the line and intersects everywhere.
+ * Return the end point, so we use the whole line segment.
+ */
+ if (diff == 0.0f)
+ return p2x;
+
+ a = (y_arg - p2y) / diff;
+ return p2x + (p1x - p2x) * a;
+}
+
+enum path_transition {
+ PATH_TRANSITION_OUT_TO_OUT = 0,
+ PATH_TRANSITION_OUT_TO_IN = 1,
+ PATH_TRANSITION_IN_TO_OUT = 2,
+ PATH_TRANSITION_IN_TO_IN = 3,
+};
+
+static void
+clip_append_vertex(struct clip_context *ctx, GLfloat x, GLfloat y)
+{
+ *ctx->vertices.x++ = x;
+ *ctx->vertices.y++ = y;
+}
+
+static enum path_transition
+path_transition_left_edge(struct clip_context *ctx, GLfloat x, GLfloat y)
+{
+ return ((ctx->prev.x >= ctx->clip.x1) << 1) | (x >= ctx->clip.x1);
+}
+
+static enum path_transition
+path_transition_right_edge(struct clip_context *ctx, GLfloat x, GLfloat y)
+{
+ return ((ctx->prev.x < ctx->clip.x2) << 1) | (x < ctx->clip.x2);
+}
+
+static enum path_transition
+path_transition_top_edge(struct clip_context *ctx, GLfloat x, GLfloat y)
+{
+ return ((ctx->prev.y >= ctx->clip.y1) << 1) | (y >= ctx->clip.y1);
+}
+
+static enum path_transition
+path_transition_bottom_edge(struct clip_context *ctx, GLfloat x, GLfloat y)
+{
+ return ((ctx->prev.y < ctx->clip.y2) << 1) | (y < ctx->clip.y2);
+}
+
+static void
+clip_polygon_leftright(struct clip_context *ctx,
+ enum path_transition transition,
+ GLfloat x, GLfloat y, GLfloat clip_x)
+{
+ GLfloat yi;
+
+ switch (transition) {
+ case PATH_TRANSITION_IN_TO_IN:
+ clip_append_vertex(ctx, x, y);
+ break;
+ case PATH_TRANSITION_IN_TO_OUT:
+ yi = clip_intersect_y(ctx->prev.x, ctx->prev.y, x, y, clip_x);
+ clip_append_vertex(ctx, clip_x, yi);
+ break;
+ case PATH_TRANSITION_OUT_TO_IN:
+ yi = clip_intersect_y(ctx->prev.x, ctx->prev.y, x, y, clip_x);
+ clip_append_vertex(ctx, clip_x, yi);
+ clip_append_vertex(ctx, x, y);
+ break;
+ case PATH_TRANSITION_OUT_TO_OUT:
+ /* nothing */
+ break;
+ default:
+ assert(0 && "bad enum path_transition");
+ }
+
+ ctx->prev.x = x;
+ ctx->prev.y = y;
+}
+
+static void
+clip_polygon_topbottom(struct clip_context *ctx,
+ enum path_transition transition,
+ GLfloat x, GLfloat y, GLfloat clip_y)
+{
+ GLfloat xi;
+
+ switch (transition) {
+ case PATH_TRANSITION_IN_TO_IN:
+ clip_append_vertex(ctx, x, y);
+ break;
+ case PATH_TRANSITION_IN_TO_OUT:
+ xi = clip_intersect_x(ctx->prev.x, ctx->prev.y, x, y, clip_y);
+ clip_append_vertex(ctx, xi, clip_y);
+ break;
+ case PATH_TRANSITION_OUT_TO_IN:
+ xi = clip_intersect_x(ctx->prev.x, ctx->prev.y, x, y, clip_y);
+ clip_append_vertex(ctx, xi, clip_y);
+ clip_append_vertex(ctx, x, y);
+ break;
+ case PATH_TRANSITION_OUT_TO_OUT:
+ /* nothing */
+ break;
+ default:
+ assert(0 && "bad enum path_transition");
+ }
+
+ ctx->prev.x = x;
+ ctx->prev.y = y;
+}
+
+static void
+clip_context_prepare(struct clip_context *ctx, const struct polygon8 *src,
+ GLfloat *dst_x, GLfloat *dst_y)
+{
+ ctx->prev.x = src->x[src->n - 1];
+ ctx->prev.y = src->y[src->n - 1];
+ ctx->vertices.x = dst_x;
+ ctx->vertices.y = dst_y;
+}
+
+static int
+clip_polygon_left(struct clip_context *ctx, const struct polygon8 *src,
+ GLfloat *dst_x, GLfloat *dst_y)
+{
+ enum path_transition trans;
+ int i;
+
+ clip_context_prepare(ctx, src, dst_x, dst_y);
+ for (i = 0; i < src->n; i++) {
+ trans = path_transition_left_edge(ctx, src->x[i], src->y[i]);
+ clip_polygon_leftright(ctx, trans, src->x[i], src->y[i],
+ ctx->clip.x1);
+ }
+ return ctx->vertices.x - dst_x;
+}
+
+static int
+clip_polygon_right(struct clip_context *ctx, const struct polygon8 *src,
+ GLfloat *dst_x, GLfloat *dst_y)
+{
+ enum path_transition trans;
+ int i;
+
+ clip_context_prepare(ctx, src, dst_x, dst_y);
+ for (i = 0; i < src->n; i++) {
+ trans = path_transition_right_edge(ctx, src->x[i], src->y[i]);
+ clip_polygon_leftright(ctx, trans, src->x[i], src->y[i],
+ ctx->clip.x2);
+ }
+ return ctx->vertices.x - dst_x;
+}
+
+static int
+clip_polygon_top(struct clip_context *ctx, const struct polygon8 *src,
+ GLfloat *dst_x, GLfloat *dst_y)
+{
+ enum path_transition trans;
+ int i;
+
+ clip_context_prepare(ctx, src, dst_x, dst_y);
+ for (i = 0; i < src->n; i++) {
+ trans = path_transition_top_edge(ctx, src->x[i], src->y[i]);
+ clip_polygon_topbottom(ctx, trans, src->x[i], src->y[i],
+ ctx->clip.y1);
+ }
+ return ctx->vertices.x - dst_x;
+}
+
+static int
+clip_polygon_bottom(struct clip_context *ctx, const struct polygon8 *src,
+ GLfloat *dst_x, GLfloat *dst_y)
+{
+ enum path_transition trans;
+ int i;
+
+ clip_context_prepare(ctx, src, dst_x, dst_y);
+ for (i = 0; i < src->n; i++) {
+ trans = path_transition_bottom_edge(ctx, src->x[i], src->y[i]);
+ clip_polygon_topbottom(ctx, trans, src->x[i], src->y[i],
+ ctx->clip.y2);
+ }
+ return ctx->vertices.x - dst_x;
+}
+
+#define max(a, b) (((a) > (b)) ? (a) : (b))
+#define min(a, b) (((a) > (b)) ? (b) : (a))
+#define clip(x, a, b) min(max(x, a), b)
+
+/*
+ * Compute the boundary vertices of the intersection of the global coordinate
+ * aligned rectangle 'rect', and an arbitrary quadrilateral produced from
+ * 'surf_rect' when transformed from surface coordinates into global coordinates.
+ * The vertices are written to 'ex' and 'ey', and the return value is the
+ * number of vertices. Vertices are produced in clockwise winding order.
+ * Guarantees to produce either zero vertices, or 3-8 vertices with non-zero
+ * polygon area.
+ */
+static int
+calculate_edges(struct weston_surface *es, pixman_box32_t *rect,
+ pixman_box32_t *surf_rect, GLfloat *ex, GLfloat *ey)
+{
+ struct polygon8 polygon;
+ struct clip_context ctx;
+ int i, n;
+ GLfloat min_x, max_x, min_y, max_y;
+ struct polygon8 surf = {
+ { surf_rect->x1, surf_rect->x2, surf_rect->x2, surf_rect->x1 },
+ { surf_rect->y1, surf_rect->y1, surf_rect->y2, surf_rect->y2 },
+ 4
+ };
+
+ ctx.clip.x1 = rect->x1;
+ ctx.clip.y1 = rect->y1;
+ ctx.clip.x2 = rect->x2;
+ ctx.clip.y2 = rect->y2;
+
+ /* transform surface to screen space: */
+ for (i = 0; i < surf.n; i++)
+ weston_surface_to_global_float(es, surf.x[i], surf.y[i],
+ &surf.x[i], &surf.y[i]);
+
+ /* find bounding box: */
+ min_x = max_x = surf.x[0];
+ min_y = max_y = surf.y[0];
+
+ for (i = 1; i < surf.n; i++) {
+ min_x = min(min_x, surf.x[i]);
+ max_x = max(max_x, surf.x[i]);
+ min_y = min(min_y, surf.y[i]);
+ max_y = max(max_y, surf.y[i]);
+ }
+
+ /* First, simple bounding box check to discard early transformed
+ * surface rects that do not intersect with the clip region:
+ */
+ if ((min_x >= ctx.clip.x2) || (max_x <= ctx.clip.x1) ||
+ (min_y >= ctx.clip.y2) || (max_y <= ctx.clip.y1))
+ return 0;
+
+ /* Simple case, bounding box edges are parallel to surface edges,
+ * there will be only four edges. We just need to clip the surface
+ * vertices to the clip rect bounds:
+ */
+ if (!es->transform.enabled) {
+ for (i = 0; i < surf.n; i++) {
+ ex[i] = clip(surf.x[i], ctx.clip.x1, ctx.clip.x2);
+ ey[i] = clip(surf.y[i], ctx.clip.y1, ctx.clip.y2);
+ }
+ return surf.n;
+ }
+
+ /* Transformed case: use a general polygon clipping algorithm to
+ * clip the surface rectangle with each side of 'rect'.
+ * The algorithm is Sutherland-Hodgman, as explained in
+ * http://www.codeguru.com/cpp/misc/misc/graphics/article.php/c8965/Polygon-Clipping.htm
+ * but without looking at any of that code.
+ */
+ polygon.n = clip_polygon_left(&ctx, &surf, polygon.x, polygon.y);
+ surf.n = clip_polygon_right(&ctx, &polygon, surf.x, surf.y);
+ polygon.n = clip_polygon_top(&ctx, &surf, polygon.x, polygon.y);
+ surf.n = clip_polygon_bottom(&ctx, &polygon, surf.x, surf.y);
+
+ /* Get rid of duplicate vertices */
+ ex[0] = surf.x[0];
+ ey[0] = surf.y[0];
+ n = 1;
+ for (i = 1; i < surf.n; i++) {
+ if (float_difference(ex[n - 1], surf.x[i]) == 0.0f &&
+ float_difference(ey[n - 1], surf.y[i]) == 0.0f)
+ continue;
+ ex[n] = surf.x[i];
+ ey[n] = surf.y[i];
+ n++;
+ }
+ if (float_difference(ex[n - 1], surf.x[0]) == 0.0f &&
+ float_difference(ey[n - 1], surf.y[0]) == 0.0f)
+ n--;
+
+ if (n < 3)
+ return 0;
+
+ return n;
+}
+
+
+/* ---------------------- copied ends -----------------------*/
+
+static void
+geometry_set_phi(struct geometry *g, float phi)
+{
+ g->phi = phi;
+ g->s = sin(phi);
+ g->c = cos(phi);
+}
+
+static void
+geometry_init(struct geometry *g)
+{
+ g->clip.x1 = -50;
+ g->clip.y1 = -50;
+ g->clip.x2 = -10;
+ g->clip.y2 = -10;
+
+ g->surf.x1 = -20;
+ g->surf.y1 = -20;
+ g->surf.x2 = 20;
+ g->surf.y2 = 20;
+
+ geometry_set_phi(g, 0.0);
+}
+
+struct ui_state {
+ uint32_t button;
+ int down;
+
+ int down_pos[2];
+ struct geometry geometry;
+};
+
+struct cliptest {
+ struct window *window;
+ struct widget *widget;
+ struct display *display;
+ int fullscreen;
+
+ struct ui_state ui;
+
+ struct geometry geometry;
+ struct weston_surface surface;
+};
+
+static void
+draw_polygon_closed(cairo_t *cr, GLfloat *x, GLfloat *y, int n)
+{
+ int i;
+
+ cairo_move_to(cr, x[0], y[0]);
+ for (i = 1; i < n; i++)
+ cairo_line_to(cr, x[i], y[i]);
+ cairo_line_to(cr, x[0], y[0]);
+}
+
+static void
+draw_polygon_labels(cairo_t *cr, GLfloat *x, GLfloat *y, int n)
+{
+ char str[16];
+ int i;
+
+ for (i = 0; i < n; i++) {
+ snprintf(str, 16, "%d", i);
+ cairo_move_to(cr, x[i], y[i]);
+ cairo_show_text(cr, str);
+ }
+}
+
+static void
+draw_coordinates(cairo_t *cr, double ox, double oy, GLfloat *x, GLfloat *y, int n)
+{
+ char str[64];
+ int i;
+ cairo_font_extents_t ext;
+
+ cairo_font_extents(cr, &ext);
+ for (i = 0; i < n; i++) {
+ snprintf(str, 64, "%d: %14.9f, %14.9f", i, x[i], y[i]);
+ cairo_move_to(cr, ox, oy + ext.height * (i + 1));
+ cairo_show_text(cr, str);
+ }
+}
+
+static void
+draw_box(cairo_t *cr, pixman_box32_t *box, struct weston_surface *surface)
+{
+ GLfloat x[4], y[4];
+
+ if (surface) {
+ weston_surface_to_global_float(surface, box->x1, box->y1, &x[0], &y[0]);
+ weston_surface_to_global_float(surface, box->x2, box->y1, &x[1], &y[1]);
+ weston_surface_to_global_float(surface, box->x2, box->y2, &x[2], &y[2]);
+ weston_surface_to_global_float(surface, box->x1, box->y2, &x[3], &y[3]);
+ } else {
+ x[0] = box->x1; y[0] = box->y1;
+ x[1] = box->x2; y[1] = box->y1;
+ x[2] = box->x2; y[2] = box->y2;
+ x[3] = box->x1; y[3] = box->y2;
+ }
+
+ draw_polygon_closed(cr, x, y, 4);
+}
+
+static void
+draw_geometry(cairo_t *cr, struct weston_surface *surface,
+ GLfloat *ex, GLfloat *ey, int n)
+{
+ struct geometry *g = surface->geometry;
+ GLfloat cx, cy;
+
+ draw_box(cr, &g->surf, surface);
+ cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.4);
+ cairo_fill(cr);
+ weston_surface_to_global_float(surface, g->surf.x1 - 4, g->surf.y1 - 4, &cx, &cy);
+ cairo_arc(cr, cx, cy, 1.5, 0.0, 2.0 * M_PI);
+ if (surface->transform.enabled == 0)
+ cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.8);
+ cairo_fill(cr);
+
+ draw_box(cr, &g->clip, NULL);
+ cairo_set_source_rgba(cr, 0.0, 0.0, 1.0, 0.4);
+ cairo_fill(cr);
+
+ draw_polygon_closed(cr, ex, ey, n);
+ cairo_set_source_rgb(cr, 0.0, 1.0, 0.0);
+ cairo_stroke(cr);
+
+ cairo_set_source_rgba(cr, 0.0, 1.0, 0.0, 0.5);
+ draw_polygon_labels(cr, ex, ey, n);
+}
+
+static void
+redraw_handler(struct widget *widget, void *data)
+{
+ struct cliptest *cliptest = data;
+ struct geometry *g = cliptest->surface.geometry;
+ struct rectangle allocation;
+ cairo_t *cr;
+ cairo_surface_t *surface;
+ GLfloat ex[8];
+ GLfloat ey[8];
+ int n;
+
+ n = calculate_edges(&cliptest->surface, &g->clip, &g->surf, ex, ey);
+
+ widget_get_allocation(cliptest->widget, &allocation);
+
+ surface = window_get_surface(cliptest->window);
+ cr = cairo_create(surface);
+ widget_get_allocation(cliptest->widget, &allocation);
+ cairo_rectangle(cr, allocation.x, allocation.y,
+ allocation.width, allocation.height);
+ cairo_clip(cr);
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ cairo_set_source_rgba(cr, 0, 0, 0, 1);
+ cairo_paint(cr);
+
+ cairo_translate(cr, allocation.x, allocation.y);
+ cairo_set_line_width(cr, 1.0);
+ cairo_move_to(cr, allocation.width / 2.0, 0.0);
+ cairo_line_to(cr, allocation.width / 2.0, allocation.height);
+ cairo_move_to(cr, 0.0, allocation.height / 2.0);
+ cairo_line_to(cr, allocation.width, allocation.height / 2.0);
+ cairo_set_source_rgba(cr, 0.5, 0.5, 0.5, 1.0);
+ cairo_stroke(cr);
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+ cairo_push_group(cr);
+ cairo_translate(cr, allocation.width / 2.0,
+ allocation.height / 2.0);
+ cairo_scale(cr, 4.0, 4.0);
+ cairo_set_line_width(cr, 0.5);
+ cairo_set_line_join(cr, CAIRO_LINE_JOIN_BEVEL);
+ cairo_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL,
+ CAIRO_FONT_WEIGHT_BOLD);
+ cairo_set_font_size(cr, 5.0);
+ draw_geometry(cr, &cliptest->surface, ex, ey, n);
+ cairo_pop_group_to_source(cr);
+ cairo_paint(cr);
+
+ cairo_set_source_rgba(cr, 0.0, 1.0, 0.0, 1.0);
+ cairo_select_font_face(cr, "monospace", CAIRO_FONT_SLANT_NORMAL,
+ CAIRO_FONT_WEIGHT_NORMAL);
+ cairo_set_font_size(cr, 12.0);
+ draw_coordinates(cr, 10.0, 10.0, ex, ey, n);
+
+ cairo_destroy(cr);
+
+ cairo_surface_destroy(surface);
+}
+
+static int
+motion_handler(struct widget *widget, struct input *input,
+ uint32_t time, float x, float y, void *data)
+{
+ struct cliptest *cliptest = data;
+ struct ui_state *ui = &cliptest->ui;
+ struct geometry *ref = &ui->geometry;
+ struct geometry *geom = &cliptest->geometry;
+ float dx, dy;
+
+ if (!ui->down)
+ return CURSOR_LEFT_PTR;
+
+ dx = (x - ui->down_pos[0]) * 0.25;
+ dy = (y - ui->down_pos[1]) * 0.25;
+
+ switch (ui->button) {
+ case BTN_LEFT:
+ geom->clip.x1 = ref->clip.x1 + dx;
+ geom->clip.y1 = ref->clip.y1 + dy;
+ /* fall through */
+ case BTN_RIGHT:
+ geom->clip.x2 = ref->clip.x2 + dx;
+ geom->clip.y2 = ref->clip.y2 + dy;
+ break;
+ default:
+ return CURSOR_LEFT_PTR;
+ }
+
+ widget_schedule_redraw(cliptest->widget);
+ return CURSOR_BLANK;
+}
+
+static void
+button_handler(struct widget *widget, struct input *input,
+ uint32_t time, uint32_t button,
+ enum wl_pointer_button_state state, void *data)
+{
+ struct cliptest *cliptest = data;
+ struct ui_state *ui = &cliptest->ui;
+
+ ui->button = button;
+
+ if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
+ ui->down = 1;
+ input_get_position(input, &ui->down_pos[0], &ui->down_pos[1]);
+ } else {
+ ui->down = 0;
+ ui->geometry = cliptest->geometry;
+ }
+}
+
+static void
+axis_handler(struct widget *widget, struct input *input, uint32_t time,
+ uint32_t axis, wl_fixed_t value, void *data)
+{
+ struct cliptest *cliptest = data;
+ struct geometry *geom = &cliptest->geometry;
+
+ if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL)
+ return;
+
+ geometry_set_phi(geom, geom->phi +
+ (M_PI / 12.0) * wl_fixed_to_double(value));
+ cliptest->surface.transform.enabled = 1;
+
+ widget_schedule_redraw(cliptest->widget);
+}
+
+static void
+key_handler(struct window *window, struct input *input, uint32_t time,
+ uint32_t key, uint32_t sym,
+ enum wl_keyboard_key_state state, void *data)
+{
+ struct cliptest *cliptest = data;
+ struct geometry *g = &cliptest->geometry;
+
+ if (state == WL_KEYBOARD_KEY_STATE_RELEASED)
+ return;
+
+ switch (sym) {
+ case XKB_KEY_Escape:
+ display_exit(cliptest->display);
+ return;
+ case XKB_KEY_w:
+ g->clip.y1 -= 1;
+ g->clip.y2 -= 1;
+ break;
+ case XKB_KEY_a:
+ g->clip.x1 -= 1;
+ g->clip.x2 -= 1;
+ break;
+ case XKB_KEY_s:
+ g->clip.y1 += 1;
+ g->clip.y2 += 1;
+ break;
+ case XKB_KEY_d:
+ g->clip.x1 += 1;
+ g->clip.x2 += 1;
+ break;
+ case XKB_KEY_i:
+ g->clip.y2 -= 1;
+ break;
+ case XKB_KEY_j:
+ g->clip.x2 -= 1;
+ break;
+ case XKB_KEY_k:
+ g->clip.y2 += 1;
+ break;
+ case XKB_KEY_l:
+ g->clip.x2 += 1;
+ break;
+ case XKB_KEY_n:
+ geometry_set_phi(g, g->phi + (M_PI / 24.0));
+ cliptest->surface.transform.enabled = 1;
+ break;
+ case XKB_KEY_m:
+ geometry_set_phi(g, g->phi - (M_PI / 24.0));
+ cliptest->surface.transform.enabled = 1;
+ break;
+ case XKB_KEY_r:
+ geometry_set_phi(g, 0.0);
+ cliptest->surface.transform.enabled = 0;
+ break;
+ default:
+ return;
+ }
+
+ widget_schedule_redraw(cliptest->widget);
+}
+
+static void
+keyboard_focus_handler(struct window *window,
+ struct input *device, void *data)
+{
+ struct cliptest *cliptest = data;
+
+ window_schedule_redraw(cliptest->window);
+}
+
+static void
+fullscreen_handler(struct window *window, void *data)
+{
+ struct cliptest *cliptest = data;
+
+ cliptest->fullscreen ^= 1;
+ window_set_fullscreen(window, cliptest->fullscreen);
+}
+
+static struct cliptest *
+cliptest_create(struct display *display)
+{
+ struct cliptest *cliptest;
+
+ cliptest = xzalloc(sizeof *cliptest);
+ cliptest->surface.geometry = &cliptest->geometry;
+ cliptest->surface.transform.enabled = 0;
+ geometry_init(&cliptest->geometry);
+ geometry_init(&cliptest->ui.geometry);
+
+ cliptest->window = window_create(display);
+ cliptest->widget = frame_create(cliptest->window, cliptest);
+ window_set_title(cliptest->window, "cliptest");
+ cliptest->display = display;
+
+ window_set_user_data(cliptest->window, cliptest);
+ widget_set_redraw_handler(cliptest->widget, redraw_handler);
+ widget_set_button_handler(cliptest->widget, button_handler);
+ widget_set_motion_handler(cliptest->widget, motion_handler);
+ widget_set_axis_handler(cliptest->widget, axis_handler);
+
+ window_set_keyboard_focus_handler(cliptest->window,
+ keyboard_focus_handler);
+ window_set_key_handler(cliptest->window, key_handler);
+ window_set_fullscreen_handler(cliptest->window, fullscreen_handler);
+
+ /* set minimum size */
+ widget_schedule_resize(cliptest->widget, 200, 100);
+
+ /* set current size */
+ widget_schedule_resize(cliptest->widget, 500, 400);
+
+ return cliptest;
+}
+
+static struct timespec begin_time;
+
+static void
+reset_timer(void)
+{
+ clock_gettime(CLOCK_MONOTONIC, &begin_time);
+}
+
+static double
+read_timer(void)
+{
+ struct timespec t;
+
+ clock_gettime(CLOCK_MONOTONIC, &t);
+ return (double)(t.tv_sec - begin_time.tv_sec) +
+ 1e-9 * (t.tv_nsec - begin_time.tv_nsec);
+}
+
+static int
+benchmark(void)
+{
+ struct weston_surface surface;
+ struct geometry geom;
+ GLfloat ex[8], ey[8];
+ int i;
+ double t;
+ const int N = 1000000;
+
+ geom.clip.x1 = -19;
+ geom.clip.y1 = -19;
+ geom.clip.x2 = 19;
+ geom.clip.y2 = 19;
+
+ geom.surf.x1 = -20;
+ geom.surf.y1 = -20;
+ geom.surf.x2 = 20;
+ geom.surf.y2 = 20;
+
+ geometry_set_phi(&geom, 0.0);
+
+ surface.transform.enabled = 1;
+ surface.geometry = &geom;
+
+ reset_timer();
+ for (i = 0; i < N; i++) {
+ geometry_set_phi(&geom, (float)i / 360.0f);
+ calculate_edges(&surface, &geom.clip, &geom.surf, ex, ey);
+ }
+ t = read_timer();
+
+ printf("%d calls took %g s, average %g us/call\n", N, t, t / N * 1e6);
+
+ return 0;
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct display *d;
+ struct cliptest *cliptest;
+
+ if (argc > 1)
+ return benchmark();
+
+ d = display_create(&argc, argv);
+ if (d == NULL) {
+ fprintf(stderr, "failed to create display: %m\n");
+ return -1;
+ }
+
+ cliptest = cliptest_create(d);
+ display_run(d);
+
+ widget_destroy(cliptest->widget);
+ window_destroy(cliptest->window);
+ free(cliptest);
+
+ return 0;
+}
diff --git a/clients/desktop-shell.c b/clients/desktop-shell.c
new file mode 100644
index 00000000..599c0a5b
--- /dev/null
+++ b/clients/desktop-shell.c
@@ -0,0 +1,1323 @@
+/*
+ * Copyright © 2011 Kristian Høgsberg
+ * Copyright © 2011 Collabora, Ltd.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * 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.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <math.h>
+#include <cairo.h>
+#include <sys/wait.h>
+#include <sys/timerfd.h>
+#include <sys/epoll.h>
+#include <linux/input.h>
+#include <libgen.h>
+#include <ctype.h>
+#include <time.h>
+
+#include <wayland-client.h>
+#include "window.h"
+#include "../shared/cairo-util.h"
+#include "../shared/config-parser.h"
+
+#include "desktop-shell-client-protocol.h"
+
+extern char **environ; /* defined by libc */
+
+struct desktop {
+ struct display *display;
+ struct desktop_shell *shell;
+ uint32_t interface_version;
+ struct unlock_dialog *unlock_dialog;
+ struct task unlock_task;
+ struct wl_list outputs;
+
+ struct window *grab_window;
+ struct widget *grab_widget;
+
+ struct weston_config *config;
+ int locking;
+
+ enum cursor_type grab_cursor;
+
+ int painted;
+};
+
+struct surface {
+ void (*configure)(void *data,
+ struct desktop_shell *desktop_shell,
+ uint32_t edges, struct window *window,
+ int32_t width, int32_t height);
+};
+
+struct panel {
+ struct surface base;
+ struct window *window;
+ struct widget *widget;
+ struct wl_list launcher_list;
+ struct panel_clock *clock;
+ int painted;
+ uint32_t color;
+};
+
+struct background {
+ struct surface base;
+ struct window *window;
+ struct widget *widget;
+ int painted;
+
+ char *image;
+ int type;
+ uint32_t color;
+};
+
+struct output {
+ struct wl_output *output;
+ struct wl_list link;
+
+ struct panel *panel;
+ struct background *background;
+};
+
+struct panel_launcher {
+ struct widget *widget;
+ struct panel *panel;
+ cairo_surface_t *icon;
+ int focused, pressed;
+ char *path;
+ struct wl_list link;
+ struct wl_array envp;
+ struct wl_array argv;
+};
+
+struct panel_clock {
+ struct widget *widget;
+ struct panel *panel;
+ struct task clock_task;
+ int clock_fd;
+};
+
+struct unlock_dialog {
+ struct window *window;
+ struct widget *widget;
+ struct widget *button;
+ int button_focused;
+ int closing;
+ struct desktop *desktop;
+};
+
+static void
+panel_add_launchers(struct panel *panel, struct desktop *desktop);
+
+static void
+sigchild_handler(int s)
+{
+ int status;
+ pid_t pid;
+
+ while (pid = waitpid(-1, &status, WNOHANG), pid > 0)
+ fprintf(stderr, "child %d exited\n", pid);
+}
+
+static void
+menu_func(struct window *window, int index, void *data)
+{
+ printf("Selected index %d from a panel menu.\n", index);
+}
+
+static void
+show_menu(struct panel *panel, struct input *input, uint32_t time)
+{
+ int32_t x, y;
+ static const char *entries[] = {
+ "Roy", "Pris", "Leon", "Zhora"
+ };
+
+ input_get_position(input, &x, &y);
+ window_show_menu(window_get_display(panel->window),
+ input, time, panel->window,
+ x - 10, y - 10, menu_func, entries, 4);
+}
+
+static int
+is_desktop_painted(struct desktop *desktop)
+{
+ struct output *output;
+
+ wl_list_for_each(output, &desktop->outputs, link) {
+ if (output->panel && !output->panel->painted)
+ return 0;
+ if (output->background && !output->background->painted)
+ return 0;
+ }
+
+ return 1;
+}
+
+static void
+check_desktop_ready(struct window *window)
+{
+ struct display *display;
+ struct desktop *desktop;
+
+ display = window_get_display(window);
+ desktop = display_get_user_data(display);
+
+ if (!desktop->painted && is_desktop_painted(desktop)) {
+ desktop->painted = 1;
+
+ if (desktop->interface_version >= 2)
+ desktop_shell_desktop_ready(desktop->shell);
+ }
+}
+
+static void
+panel_launcher_activate(struct panel_launcher *widget)
+{
+ char **argv;
+ pid_t pid;
+
+ pid = fork();
+ if (pid < 0) {
+ fprintf(stderr, "fork failed: %m\n");
+ return;
+ }
+
+ if (pid)
+ return;
+
+ argv = widget->argv.data;
+ if (execve(argv[0], argv, widget->envp.data) < 0) {
+ fprintf(stderr, "execl '%s' failed: %m\n", argv[0]);
+ exit(1);
+ }
+}
+
+static void
+panel_launcher_redraw_handler(struct widget *widget, void *data)
+{
+ struct panel_launcher *launcher = data;
+ struct rectangle allocation;
+ cairo_t *cr;
+
+ cr = widget_cairo_create(launcher->panel->widget);
+
+ widget_get_allocation(widget, &allocation);
+ if (launcher->pressed) {
+ allocation.x++;
+ allocation.y++;
+ }
+
+ cairo_set_source_surface(cr, launcher->icon,
+ allocation.x, allocation.y);
+ cairo_paint(cr);
+
+ if (launcher->focused) {
+ cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 0.4);
+ cairo_mask_surface(cr, launcher->icon,
+ allocation.x, allocation.y);
+ }
+
+ cairo_destroy(cr);
+}
+
+static int
+panel_launcher_motion_handler(struct widget *widget, struct input *input,
+ uint32_t time, float x, float y, void *data)
+{
+ struct panel_launcher *launcher = data;
+
+ widget_set_tooltip(widget, basename((char *)launcher->path), x, y);
+
+ return CURSOR_LEFT_PTR;
+}
+
+static void
+set_hex_color(cairo_t *cr, uint32_t color)
+{
+ cairo_set_source_rgba(cr,
+ ((color >> 16) & 0xff) / 255.0,
+ ((color >> 8) & 0xff) / 255.0,
+ ((color >> 0) & 0xff) / 255.0,
+ ((color >> 24) & 0xff) / 255.0);
+}
+
+static void
+panel_redraw_handler(struct widget *widget, void *data)
+{
+ cairo_surface_t *surface;
+ cairo_t *cr;
+ struct panel *panel = data;
+
+ cr = widget_cairo_create(panel->widget);
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ set_hex_color(cr, panel->color);
+ cairo_paint(cr);
+
+ cairo_destroy(cr);
+ surface = window_get_surface(panel->window);
+ cairo_surface_destroy(surface);
+ panel->painted = 1;
+ check_desktop_ready(panel->window);
+}
+
+static int
+panel_launcher_enter_handler(struct widget *widget, struct input *input,
+ float x, float y, void *data)
+{
+ struct panel_launcher *launcher = data;
+
+ launcher->focused = 1;
+ widget_schedule_redraw(widget);
+
+ return CURSOR_LEFT_PTR;
+}
+
+static void
+panel_launcher_leave_handler(struct widget *widget,
+ struct input *input, void *data)
+{
+ struct panel_launcher *launcher = data;
+
+ launcher->focused = 0;
+ widget_destroy_tooltip(widget);
+ widget_schedule_redraw(widget);
+}
+
+static void
+panel_launcher_button_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ uint32_t button,
+ enum wl_pointer_button_state state, void *data)
+{
+ struct panel_launcher *launcher;
+
+ launcher = widget_get_user_data(widget);
+ widget_schedule_redraw(widget);
+ if (state == WL_POINTER_BUTTON_STATE_RELEASED)
+ panel_launcher_activate(launcher);
+
+}
+
+static void
+panel_launcher_touch_down_handler(struct widget *widget, struct input *input,
+ uint32_t serial, uint32_t time, int32_t id,
+ float x, float y, void *data)
+{
+ struct panel_launcher *launcher;
+
+ launcher = widget_get_user_data(widget);
+ launcher->focused = 1;
+ widget_schedule_redraw(widget);
+}
+
+static void
+panel_launcher_touch_up_handler(struct widget *widget, struct input *input,
+ uint32_t serial, uint32_t time, int32_t id,
+ void *data)
+{
+ struct panel_launcher *launcher;
+
+ launcher = widget_get_user_data(widget);
+ launcher->focused = 0;
+ widget_schedule_redraw(widget);
+ panel_launcher_activate(launcher);
+}
+
+static void
+clock_func(struct task *task, uint32_t events)
+{
+ struct panel_clock *clock =
+ container_of(task, struct panel_clock, clock_task);
+ uint64_t exp;
+
+ if (read(clock->clock_fd, &exp, sizeof exp) != sizeof exp)
+ abort();
+ widget_schedule_redraw(clock->widget);
+}
+
+static void
+panel_clock_redraw_handler(struct widget *widget, void *data)
+{
+ struct panel_clock *clock = data;
+ cairo_t *cr;
+ struct rectangle allocation;
+ cairo_text_extents_t extents;
+ cairo_font_extents_t font_extents;
+ time_t rawtime;
+ struct tm * timeinfo;
+ char string[128];
+
+ time(&rawtime);
+ timeinfo = localtime(&rawtime);
+ strftime(string, sizeof string, "%a %b %d, %I:%M %p", timeinfo);
+
+ widget_get_allocation(widget, &allocation);
+ if (allocation.width == 0)
+ return;
+
+ cr = widget_cairo_create(clock->panel->widget);
+ cairo_select_font_face(cr, "sans",
+ CAIRO_FONT_SLANT_NORMAL,
+ CAIRO_FONT_WEIGHT_NORMAL);
+ cairo_set_font_size(cr, 14);
+ cairo_text_extents(cr, string, &extents);
+ cairo_font_extents (cr, &font_extents);
+ cairo_move_to(cr, allocation.x + 5,
+ allocation.y + 3 * (allocation.height >> 2) + 1);
+ cairo_set_source_rgb(cr, 0, 0, 0);
+ cairo_show_text(cr, string);
+ cairo_move_to(cr, allocation.x + 4,
+ allocation.y + 3 * (allocation.height >> 2));
+ cairo_set_source_rgb(cr, 1, 1, 1);
+ cairo_show_text(cr, string);
+ cairo_destroy(cr);
+}
+
+static int
+clock_timer_reset(struct panel_clock *clock)
+{
+ struct itimerspec its;
+
+ its.it_interval.tv_sec = 60;
+ its.it_interval.tv_nsec = 0;
+ its.it_value.tv_sec = 60;
+ its.it_value.tv_nsec = 0;
+ if (timerfd_settime(clock->clock_fd, 0, &its, NULL) < 0) {
+ fprintf(stderr, "could not set timerfd\n: %m");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void
+panel_destroy_clock(struct panel_clock *clock)
+{
+ widget_destroy(clock->widget);
+
+ close(clock->clock_fd);
+
+ free(clock);
+}
+
+static void
+panel_add_clock(struct panel *panel)
+{
+ struct panel_clock *clock;
+ int timerfd;
+
+ timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
+ if (timerfd < 0) {
+ fprintf(stderr, "could not create timerfd\n: %m");
+ return;
+ }
+
+ clock = xzalloc(sizeof *clock);
+ clock->panel = panel;
+ panel->clock = clock;
+ clock->clock_fd = timerfd;
+
+ clock->clock_task.run = clock_func;
+ display_watch_fd(window_get_display(panel->window), clock->clock_fd,
+ EPOLLIN, &clock->clock_task);
+ clock_timer_reset(clock);
+
+ clock->widget = widget_add_widget(panel->widget, clock);
+ widget_set_redraw_handler(clock->widget, panel_clock_redraw_handler);
+}
+
+static void
+panel_button_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ uint32_t button,
+ enum wl_pointer_button_state state, void *data)
+{
+ struct panel *panel = data;
+
+ if (button == BTN_RIGHT && state == WL_POINTER_BUTTON_STATE_PRESSED)
+ show_menu(panel, input, time);
+}
+
+static void
+panel_resize_handler(struct widget *widget,
+ int32_t width, int32_t height, void *data)
+{
+ struct panel_launcher *launcher;
+ struct panel *panel = data;
+ int x, y, w, h;
+
+ x = 10;
+ y = 16;
+ wl_list_for_each(launcher, &panel->launcher_list, link) {
+ w = cairo_image_surface_get_width(launcher->icon);
+ h = cairo_image_surface_get_height(launcher->icon);
+ widget_set_allocation(launcher->widget,
+ x, y - h / 2, w + 1, h + 1);
+ x += w + 10;
+ }
+ h=20;
+ w=170;
+
+ if (panel->clock)
+ widget_set_allocation(panel->clock->widget,
+ width - w - 8, y - h / 2, w + 1, h + 1);
+}
+
+static void
+panel_configure(void *data,
+ struct desktop_shell *desktop_shell,
+ uint32_t edges, struct window *window,
+ int32_t width, int32_t height)
+{
+ struct surface *surface = window_get_user_data(window);
+ struct panel *panel = container_of(surface, struct panel, base);
+
+ window_schedule_resize(panel->window, width, 32);
+}
+
+static void
+panel_destroy_launcher(struct panel_launcher *launcher)
+{
+ wl_array_release(&launcher->argv);
+ wl_array_release(&launcher->envp);
+
+ free(launcher->path);
+
+ cairo_surface_destroy(launcher->icon);
+
+ widget_destroy(launcher->widget);
+ wl_list_remove(&launcher->link);
+
+ free(launcher);
+}
+
+static void
+panel_destroy(struct panel *panel)
+{
+ struct panel_launcher *tmp;
+ struct panel_launcher *launcher;
+
+ panel_destroy_clock(panel->clock);
+
+ wl_list_for_each_safe(launcher, tmp, &panel->launcher_list, link)
+ panel_destroy_launcher(launcher);
+
+ widget_destroy(panel->widget);
+ window_destroy(panel->window);
+
+ free(panel);
+}
+
+static struct panel *
+panel_create(struct desktop *desktop)
+{
+ struct panel *panel;
+ struct weston_config_section *s;
+
+ panel = xzalloc(sizeof *panel);
+
+ panel->base.configure = panel_configure;
+ panel->window = window_create_custom(desktop->display);
+ panel->widget = window_add_widget(panel->window, panel);
+ wl_list_init(&panel->launcher_list);
+
+ window_set_title(panel->window, "panel");
+ window_set_user_data(panel->window, panel);
+
+ widget_set_redraw_handler(panel->widget, panel_redraw_handler);
+ widget_set_resize_handler(panel->widget, panel_resize_handler);
+ widget_set_button_handler(panel->widget, panel_button_handler);
+
+ panel_add_clock(panel);
+
+ s = weston_config_get_section(desktop->config, "shell", NULL, NULL);
+ weston_config_section_get_uint(s, "panel-color",
+ &panel->color, 0xaa000000);
+
+ panel_add_launchers(panel, desktop);
+
+ return panel;
+}
+
+static cairo_surface_t *
+load_icon_or_fallback(const char *icon)
+{
+ cairo_surface_t *surface = cairo_image_surface_create_from_png(icon);
+ cairo_status_t status;
+ cairo_t *cr;
+
+ status = cairo_surface_status(surface);
+ if (status == CAIRO_STATUS_SUCCESS)
+ return surface;
+
+ cairo_surface_destroy(surface);
+ fprintf(stderr, "ERROR loading icon from file '%s', error: '%s'\n",
+ icon, cairo_status_to_string(status));
+
+ /* draw fallback icon */
+ surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
+ 20, 20);
+ cr = cairo_create(surface);
+
+ cairo_set_source_rgba(cr, 0.8, 0.8, 0.8, 1);
+ cairo_paint(cr);
+
+ cairo_set_source_rgba(cr, 0, 0, 0, 1);
+ cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
+ cairo_rectangle(cr, 0, 0, 20, 20);
+ cairo_move_to(cr, 4, 4);
+ cairo_line_to(cr, 16, 16);
+ cairo_move_to(cr, 4, 16);
+ cairo_line_to(cr, 16, 4);
+ cairo_stroke(cr);
+
+ cairo_destroy(cr);
+
+ return surface;
+}
+
+static void
+panel_add_launcher(struct panel *panel, const char *icon, const char *path)
+{
+ struct panel_launcher *launcher;
+ char *start, *p, *eq, **ps;
+ int i, j, k;
+
+ launcher = xzalloc(sizeof *launcher);
+ launcher->icon = load_icon_or_fallback(icon);
+ launcher->path = strdup(path);
+
+ wl_array_init(&launcher->envp);
+ wl_array_init(&launcher->argv);
+ for (i = 0; environ[i]; i++) {
+ ps = wl_array_add(&launcher->envp, sizeof *ps);
+ *ps = environ[i];
+ }
+ j = 0;
+
+ start = launcher->path;
+ while (*start) {
+ for (p = start, eq = NULL; *p && !isspace(*p); p++)
+ if (*p == '=')
+ eq = p;
+
+ if (eq && j == 0) {
+ ps = launcher->envp.data;
+ for (k = 0; k < i; k++)
+ if (strncmp(ps[k], start, eq - start) == 0) {
+ ps[k] = start;
+ break;
+ }
+ if (k == i) {
+ ps = wl_array_add(&launcher->envp, sizeof *ps);
+ *ps = start;
+ i++;
+ }
+ } else {
+ ps = wl_array_add(&launcher->argv, sizeof *ps);
+ *ps = start;
+ j++;
+ }
+
+ while (*p && isspace(*p))
+ *p++ = '\0';
+
+ start = p;
+ }
+
+ ps = wl_array_add(&launcher->envp, sizeof *ps);
+ *ps = NULL;
+ ps = wl_array_add(&launcher->argv, sizeof *ps);
+ *ps = NULL;
+
+ launcher->panel = panel;
+ wl_list_insert(panel->launcher_list.prev, &launcher->link);
+
+ launcher->widget = widget_add_widget(panel->widget, launcher);
+ widget_set_enter_handler(launcher->widget,
+ panel_launcher_enter_handler);
+ widget_set_leave_handler(launcher->widget,
+ panel_launcher_leave_handler);
+ widget_set_button_handler(launcher->widget,
+ panel_launcher_button_handler);
+ widget_set_touch_down_handler(launcher->widget,
+ panel_launcher_touch_down_handler);
+ widget_set_touch_up_handler(launcher->widget,
+ panel_launcher_touch_up_handler);
+ widget_set_redraw_handler(launcher->widget,
+ panel_launcher_redraw_handler);
+ widget_set_motion_handler(launcher->widget,
+ panel_launcher_motion_handler);
+}
+
+enum {
+ BACKGROUND_SCALE,
+ BACKGROUND_SCALE_CROP,
+ BACKGROUND_TILE
+};
+
+static void
+background_draw(struct widget *widget, void *data)
+{
+ struct background *background = data;
+ cairo_surface_t *surface, *image;
+ cairo_pattern_t *pattern;
+ cairo_matrix_t matrix;
+ cairo_t *cr;
+ double im_w, im_h;
+ double sx, sy, s;
+ double tx, ty;
+ struct rectangle allocation;
+ struct display *display;
+ struct wl_region *opaque;
+
+ surface = window_get_surface(background->window);
+
+ cr = widget_cairo_create(background->widget);
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ cairo_set_source_rgba(cr, 0.0, 0.0, 0.2, 1.0);
+ cairo_paint(cr);
+
+ widget_get_allocation(widget, &allocation);
+ image = NULL;
+ if (background->image)
+ image = load_cairo_surface(background->image);
+
+ if (image && background->type != -1) {
+ im_w = cairo_image_surface_get_width(image);
+ im_h = cairo_image_surface_get_height(image);
+ sx = im_w / allocation.width;
+ sy = im_h / allocation.height;
+
+ pattern = cairo_pattern_create_for_surface(image);
+
+ switch (background->type) {
+ case BACKGROUND_SCALE:
+ cairo_matrix_init_scale(&matrix, sx, sy);
+ cairo_pattern_set_matrix(pattern, &matrix);
+ break;
+ case BACKGROUND_SCALE_CROP:
+ s = (sx < sy) ? sx : sy;
+ /* align center */
+ tx = (im_w - s * allocation.width) * 0.5;
+ ty = (im_h - s * allocation.height) * 0.5;
+ cairo_matrix_init_translate(&matrix, tx, ty);
+ cairo_matrix_scale(&matrix, s, s);
+ cairo_pattern_set_matrix(pattern, &matrix);
+ break;
+ case BACKGROUND_TILE:
+ cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
+ break;
+ }
+
+ cairo_set_source(cr, pattern);
+ cairo_pattern_destroy (pattern);
+ cairo_surface_destroy(image);
+ } else {
+ set_hex_color(cr, background->color);
+ }
+
+ cairo_paint(cr);
+ cairo_destroy(cr);
+ cairo_surface_destroy(surface);
+
+ display = window_get_display(background->window);
+ opaque = wl_compositor_create_region(display_get_compositor(display));
+ wl_region_add(opaque, allocation.x, allocation.y,
+ allocation.width, allocation.height);
+ wl_surface_set_opaque_region(window_get_wl_surface(background->window), opaque);
+ wl_region_destroy(opaque);
+
+ background->painted = 1;
+ check_desktop_ready(background->window);
+}
+
+static void
+background_configure(void *data,
+ struct desktop_shell *desktop_shell,
+ uint32_t edges, struct window *window,
+ int32_t width, int32_t height)
+{
+ struct background *background =
+ (struct background *) window_get_user_data(window);
+
+ widget_schedule_resize(background->widget, width, height);
+}
+
+static void
+unlock_dialog_redraw_handler(struct widget *widget, void *data)
+{
+ struct unlock_dialog *dialog = data;
+ struct rectangle allocation;
+ cairo_surface_t *surface;
+ cairo_t *cr;
+ cairo_pattern_t *pat;
+ double cx, cy, r, f;
+
+ cr = widget_cairo_create(widget);
+
+ widget_get_allocation(dialog->widget, &allocation);
+ cairo_rectangle(cr, allocation.x, allocation.y,
+ allocation.width, allocation.height);
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ cairo_set_source_rgba(cr, 0, 0, 0, 0.6);
+ cairo_fill(cr);
+
+ cairo_translate(cr, allocation.x, allocation.y);
+ if (dialog->button_focused)
+ f = 1.0;
+ else
+ f = 0.7;
+
+ cx = allocation.width / 2.0;
+ cy = allocation.height / 2.0;
+ r = (cx < cy ? cx : cy) * 0.4;
+ pat = cairo_pattern_create_radial(cx, cy, r * 0.7, cx, cy, r);
+ cairo_pattern_add_color_stop_rgb(pat, 0.0, 0, 0.86 * f, 0);
+ cairo_pattern_add_color_stop_rgb(pat, 0.85, 0.2 * f, f, 0.2 * f);
+ cairo_pattern_add_color_stop_rgb(pat, 1.0, 0, 0.86 * f, 0);
+ cairo_set_source(cr, pat);
+ cairo_pattern_destroy(pat);
+ cairo_arc(cr, cx, cy, r, 0.0, 2.0 * M_PI);
+ cairo_fill(cr);
+
+ widget_set_allocation(dialog->button,
+ allocation.x + cx - r,
+ allocation.y + cy - r, 2 * r, 2 * r);
+
+ cairo_destroy(cr);
+
+ surface = window_get_surface(dialog->window);
+ cairo_surface_destroy(surface);
+}
+
+static void
+unlock_dialog_button_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ uint32_t button,
+ enum wl_pointer_button_state state, void *data)
+{
+ struct unlock_dialog *dialog = data;
+ struct desktop *desktop = dialog->desktop;
+
+ if (button == BTN_LEFT) {
+ if (state == WL_POINTER_BUTTON_STATE_RELEASED &&
+ !dialog->closing) {
+ display_defer(desktop->display, &desktop->unlock_task);
+ dialog->closing = 1;
+ }
+ }
+}
+
+static void
+unlock_dialog_touch_down_handler(struct widget *widget, struct input *input,
+ uint32_t serial, uint32_t time, int32_t id,
+ float x, float y, void *data)
+{
+ struct unlock_dialog *dialog = data;
+
+ dialog->button_focused = 1;
+ widget_schedule_redraw(widget);
+}
+
+static void
+unlock_dialog_touch_up_handler(struct widget *widget, struct input *input,
+ uint32_t serial, uint32_t time, int32_t id,
+ void *data)
+{
+ struct unlock_dialog *dialog = data;
+ struct desktop *desktop = dialog->desktop;
+
+ dialog->button_focused = 0;
+ widget_schedule_redraw(widget);
+ display_defer(desktop->display, &desktop->unlock_task);
+ dialog->closing = 1;
+}
+
+static void
+unlock_dialog_keyboard_focus_handler(struct window *window,
+ struct input *device, void *data)
+{
+ window_schedule_redraw(window);
+}
+
+static int
+unlock_dialog_widget_enter_handler(struct widget *widget,
+ struct input *input,
+ float x, float y, void *data)
+{
+ struct unlock_dialog *dialog = data;
+
+ dialog->button_focused = 1;
+ widget_schedule_redraw(widget);
+
+ return CURSOR_LEFT_PTR;
+}
+
+static void
+unlock_dialog_widget_leave_handler(struct widget *widget,
+ struct input *input, void *data)
+{
+ struct unlock_dialog *dialog = data;
+
+ dialog->button_focused = 0;
+ widget_schedule_redraw(widget);
+}
+
+static struct unlock_dialog *
+unlock_dialog_create(struct desktop *desktop)
+{
+ struct display *display = desktop->display;
+ struct unlock_dialog *dialog;
+
+ dialog = xzalloc(sizeof *dialog);
+
+ dialog->window = window_create_custom(display);
+ dialog->widget = frame_create(dialog->window, dialog);
+ window_set_title(dialog->window, "Unlock your desktop");
+
+ window_set_user_data(dialog->window, dialog);
+ window_set_keyboard_focus_handler(dialog->window,
+ unlock_dialog_keyboard_focus_handler);
+ dialog->button = widget_add_widget(dialog->widget, dialog);
+ widget_set_redraw_handler(dialog->widget,
+ unlock_dialog_redraw_handler);
+ widget_set_enter_handler(dialog->button,
+ unlock_dialog_widget_enter_handler);
+ widget_set_leave_handler(dialog->button,
+ unlock_dialog_widget_leave_handler);
+ widget_set_button_handler(dialog->button,
+ unlock_dialog_button_handler);
+ widget_set_touch_down_handler(dialog->button,
+ unlock_dialog_touch_down_handler);
+ widget_set_touch_up_handler(dialog->button,
+ unlock_dialog_touch_up_handler);
+
+ desktop_shell_set_lock_surface(desktop->shell,
+ window_get_wl_surface(dialog->window));
+
+ window_schedule_resize(dialog->window, 260, 230);
+
+ return dialog;
+}
+
+static void
+unlock_dialog_destroy(struct unlock_dialog *dialog)
+{
+ window_destroy(dialog->window);
+ free(dialog);
+}
+
+static void
+unlock_dialog_finish(struct task *task, uint32_t events)
+{
+ struct desktop *desktop =
+ container_of(task, struct desktop, unlock_task);
+
+ desktop_shell_unlock(desktop->shell);
+ unlock_dialog_destroy(desktop->unlock_dialog);
+ desktop->unlock_dialog = NULL;
+}
+
+static void
+desktop_shell_configure(void *data,
+ struct desktop_shell *desktop_shell,
+ uint32_t edges,
+ struct wl_surface *surface,
+ int32_t width, int32_t height)
+{
+ struct window *window = wl_surface_get_user_data(surface);
+ struct surface *s = window_get_user_data(window);
+
+ s->configure(data, desktop_shell, edges, window, width, height);
+}
+
+static void
+desktop_shell_prepare_lock_surface(void *data,
+ struct desktop_shell *desktop_shell)
+{
+ struct desktop *desktop = data;
+
+ if (!desktop->locking) {
+ desktop_shell_unlock(desktop->shell);
+ return;
+ }
+
+ if (!desktop->unlock_dialog) {
+ desktop->unlock_dialog = unlock_dialog_create(desktop);
+ desktop->unlock_dialog->desktop = desktop;
+ }
+}
+
+static void
+desktop_shell_grab_cursor(void *data,
+ struct desktop_shell *desktop_shell,
+ uint32_t cursor)
+{
+ struct desktop *desktop = data;
+
+ switch (cursor) {
+ case DESKTOP_SHELL_CURSOR_NONE:
+ desktop->grab_cursor = CURSOR_BLANK;
+ break;
+ case DESKTOP_SHELL_CURSOR_BUSY:
+ desktop->grab_cursor = CURSOR_WATCH;
+ break;
+ case DESKTOP_SHELL_CURSOR_MOVE:
+ desktop->grab_cursor = CURSOR_DRAGGING;
+ break;
+ case DESKTOP_SHELL_CURSOR_RESIZE_TOP:
+ desktop->grab_cursor = CURSOR_TOP;
+ break;
+ case DESKTOP_SHELL_CURSOR_RESIZE_BOTTOM:
+ desktop->grab_cursor = CURSOR_BOTTOM;
+ break;
+ case DESKTOP_SHELL_CURSOR_RESIZE_LEFT:
+ desktop->grab_cursor = CURSOR_LEFT;
+ break;
+ case DESKTOP_SHELL_CURSOR_RESIZE_RIGHT:
+ desktop->grab_cursor = CURSOR_RIGHT;
+ break;
+ case DESKTOP_SHELL_CURSOR_RESIZE_TOP_LEFT:
+ desktop->grab_cursor = CURSOR_TOP_LEFT;
+ break;
+ case DESKTOP_SHELL_CURSOR_RESIZE_TOP_RIGHT:
+ desktop->grab_cursor = CURSOR_TOP_RIGHT;
+ break;
+ case DESKTOP_SHELL_CURSOR_RESIZE_BOTTOM_LEFT:
+ desktop->grab_cursor = CURSOR_BOTTOM_LEFT;
+ break;
+ case DESKTOP_SHELL_CURSOR_RESIZE_BOTTOM_RIGHT:
+ desktop->grab_cursor = CURSOR_BOTTOM_RIGHT;
+ break;
+ case DESKTOP_SHELL_CURSOR_ARROW:
+ default:
+ desktop->grab_cursor = CURSOR_LEFT_PTR;
+ }
+}
+
+static const struct desktop_shell_listener listener = {
+ desktop_shell_configure,
+ desktop_shell_prepare_lock_surface,
+ desktop_shell_grab_cursor
+};
+
+static void
+background_destroy(struct background *background)
+{
+ widget_destroy(background->widget);
+ window_destroy(background->window);
+
+ free(background->image);
+ free(background);
+}
+
+static struct background *
+background_create(struct desktop *desktop)
+{
+ struct background *background;
+ struct weston_config_section *s;
+ char *type;
+
+ background = xzalloc(sizeof *background);
+ background->base.configure = background_configure;
+ background->window = window_create_custom(desktop->display);
+ background->widget = window_add_widget(background->window, background);
+ window_set_user_data(background->window, background);
+ widget_set_redraw_handler(background->widget, background_draw);
+ window_set_preferred_format(background->window,
+ WINDOW_PREFERRED_FORMAT_RGB565);
+
+ s = weston_config_get_section(desktop->config, "shell", NULL, NULL);
+ weston_config_section_get_string(s, "background-image",
+ &background->image,
+ DATADIR "/weston/pattern.png");
+ weston_config_section_get_uint(s, "background-color",
+ &background->color, 0xff002244);
+
+ weston_config_section_get_string(s, "background-type",
+ &type, "tile");
+ if (strcmp(type, "scale") == 0) {
+ background->type = BACKGROUND_SCALE;
+ } else if (strcmp(type, "scale-crop") == 0) {
+ background->type = BACKGROUND_SCALE_CROP;
+ } else if (strcmp(type, "tile") == 0) {
+ background->type = BACKGROUND_TILE;
+ } else {
+ background->type = -1;
+ fprintf(stderr, "invalid background-type: %s\n",
+ type);
+ }
+
+ free(type);
+
+ return background;
+}
+
+static int
+grab_surface_enter_handler(struct widget *widget, struct input *input,
+ float x, float y, void *data)
+{
+ struct desktop *desktop = data;
+
+ return desktop->grab_cursor;
+}
+
+static void
+grab_surface_destroy(struct desktop *desktop)
+{
+ widget_destroy(desktop->grab_widget);
+ window_destroy(desktop->grab_window);
+}
+
+static void
+grab_surface_create(struct desktop *desktop)
+{
+ struct wl_surface *s;
+
+ desktop->grab_window = window_create_custom(desktop->display);
+ window_set_user_data(desktop->grab_window, desktop);
+
+ s = window_get_wl_surface(desktop->grab_window);
+ desktop_shell_set_grab_surface(desktop->shell, s);
+
+ desktop->grab_widget =
+ window_add_widget(desktop->grab_window, desktop);
+ /* We set the allocation to 1x1 at 0,0 so the fake enter event
+ * at 0,0 will go to this widget. */
+ widget_set_allocation(desktop->grab_widget, 0, 0, 1, 1);
+
+ widget_set_enter_handler(desktop->grab_widget,
+ grab_surface_enter_handler);
+}
+
+static void
+output_destroy(struct output *output)
+{
+ background_destroy(output->background);
+ panel_destroy(output->panel);
+ wl_output_destroy(output->output);
+ wl_list_remove(&output->link);
+
+ free(output);
+}
+
+static void
+desktop_destroy_outputs(struct desktop *desktop)
+{
+ struct output *tmp;
+ struct output *output;
+
+ wl_list_for_each_safe(output, tmp, &desktop->outputs, link)
+ output_destroy(output);
+}
+
+static void
+output_handle_geometry(void *data,
+ struct wl_output *wl_output,
+ int x, int y,
+ int physical_width,
+ int physical_height,
+ int subpixel,
+ const char *make,
+ const char *model,
+ int transform)
+{
+ struct output *output = data;
+
+ window_set_buffer_transform(output->panel->window, transform);
+ window_set_buffer_transform(output->background->window, transform);
+}
+
+static void
+output_handle_mode(void *data,
+ struct wl_output *wl_output,
+ uint32_t flags,
+ int width,
+ int height,
+ int refresh)
+{
+}
+
+static void
+output_handle_done(void *data,
+ struct wl_output *wl_output)
+{
+}
+
+static void
+output_handle_scale(void *data,
+ struct wl_output *wl_output,
+ int32_t scale)
+{
+ struct output *output = data;
+
+ window_set_buffer_scale(output->panel->window, scale);
+ window_set_buffer_scale(output->background->window, scale);
+}
+
+static const struct wl_output_listener output_listener = {
+ output_handle_geometry,
+ output_handle_mode,
+ output_handle_done,
+ output_handle_scale
+};
+
+static void
+output_init(struct output *output, struct desktop *desktop)
+{
+ struct wl_surface *surface;
+
+ output->panel = panel_create(desktop);
+ surface = window_get_wl_surface(output->panel->window);
+ desktop_shell_set_panel(desktop->shell,
+ output->output, surface);
+
+ output->background = background_create(desktop);
+ surface = window_get_wl_surface(output->background->window);
+ desktop_shell_set_background(desktop->shell,
+ output->output, surface);
+}
+
+static void
+create_output(struct desktop *desktop, uint32_t id)
+{
+ struct output *output;
+
+ output = calloc(1, sizeof *output);
+ if (!output)
+ return;
+
+ output->output =
+ display_bind(desktop->display, id, &wl_output_interface, 2);
+
+ wl_output_add_listener(output->output, &output_listener, output);
+
+ wl_list_insert(&desktop->outputs, &output->link);
+
+ /* On start up we may process an output global before the shell global
+ * in which case we can't create the panel and background just yet */
+ if (desktop->shell)
+ output_init(output, desktop);
+}
+
+static void
+global_handler(struct display *display, uint32_t id,
+ const char *interface, uint32_t version, void *data)
+{
+ struct desktop *desktop = data;
+
+ if (!strcmp(interface, "desktop_shell")) {
+ desktop->interface_version = (version < 2) ? version : 2;
+ desktop->shell = display_bind(desktop->display,
+ id, &desktop_shell_interface,
+ desktop->interface_version);
+ desktop_shell_add_listener(desktop->shell, &listener, desktop);
+ } else if (!strcmp(interface, "wl_output")) {
+ create_output(desktop, id);
+ }
+}
+
+static void
+panel_add_launchers(struct panel *panel, struct desktop *desktop)
+{
+ struct weston_config_section *s;
+ char *icon, *path;
+ const char *name;
+ int count;
+
+ count = 0;
+ s = NULL;
+ while (weston_config_next_section(desktop->config, &s, &name)) {
+ if (strcmp(name, "launcher") != 0)
+ continue;
+
+ weston_config_section_get_string(s, "icon", &icon, NULL);
+ weston_config_section_get_string(s, "path", &path, NULL);
+
+ if (icon != NULL && path != NULL) {
+ panel_add_launcher(panel, icon, path);
+ count++;
+ } else {
+ fprintf(stderr, "invalid launcher section\n");
+ }
+
+ free(icon);
+ free(path);
+ }
+
+ if (count == 0) {
+ /* add default launcher */
+ panel_add_launcher(panel,
+ DATADIR "/weston/terminal.png",
+ BINDIR "/weston-terminal");
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ struct desktop desktop = { 0 };
+ struct output *output;
+ struct weston_config_section *s;
+
+ desktop.unlock_task.run = unlock_dialog_finish;
+ wl_list_init(&desktop.outputs);
+
+ desktop.config = weston_config_parse("weston.ini");
+ s = weston_config_get_section(desktop.config, "shell", NULL, NULL);
+ weston_config_section_get_bool(s, "locking", &desktop.locking, 1);
+
+ desktop.display = display_create(&argc, argv);
+ if (desktop.display == NULL) {
+ fprintf(stderr, "failed to create display: %m\n");
+ return -1;
+ }
+
+ display_set_user_data(desktop.display, &desktop);
+ display_set_global_handler(desktop.display, global_handler);
+
+ /* Create panel and background for outputs processed before the shell
+ * global interface was processed */
+ wl_list_for_each(output, &desktop.outputs, link)
+ if (!output->panel)
+ output_init(output, &desktop);
+
+ grab_surface_create(&desktop);
+
+ signal(SIGCHLD, sigchild_handler);
+
+ display_run(desktop.display);
+
+ /* Cleanup */
+ grab_surface_destroy(&desktop);
+ desktop_destroy_outputs(&desktop);
+ if (desktop.unlock_dialog)
+ unlock_dialog_destroy(desktop.unlock_dialog);
+ desktop_shell_destroy(desktop.shell);
+ display_destroy(desktop.display);
+
+ return 0;
+}
diff --git a/clients/dnd.c b/clients/dnd.c
new file mode 100644
index 00000000..19cc243c
--- /dev/null
+++ b/clients/dnd.c
@@ -0,0 +1,648 @@
+/*
+ * Copyright © 2010 Kristian Høgsberg
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * 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.
+ */
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <math.h>
+#include <sys/time.h>
+#include <cairo.h>
+#include <sys/epoll.h>
+
+#include <wayland-client.h>
+#include <wayland-cursor.h>
+
+#include "window.h"
+#include "../shared/cairo-util.h"
+
+struct dnd_drag;
+
+struct dnd {
+ struct window *window;
+ struct widget *widget;
+ struct display *display;
+ uint32_t key;
+ struct item *items[16];
+ int self_only;
+ struct dnd_drag *current_drag;
+};
+
+struct dnd_drag {
+ cairo_surface_t *translucent;
+ cairo_surface_t *opaque;
+ int hotspot_x, hotspot_y;
+ struct dnd *dnd;
+ struct input *input;
+ uint32_t time;
+ struct item *item;
+ int x_offset, y_offset;
+ int width, height;
+ const char *mime_type;
+
+ struct wl_surface *drag_surface;
+ struct wl_data_source *data_source;
+};
+
+struct item {
+ cairo_surface_t *surface;
+ int seed;
+ int x, y;
+};
+
+struct dnd_flower_message {
+ int seed, x_offset, y_offset;
+};
+
+
+static const int item_width = 64;
+static const int item_height = 64;
+static const int item_padding = 16;
+
+static const char flower_mime_type[] = "application/x-wayland-dnd-flower";
+static const char text_mime_type[] = "text/plain;charset=utf-8";
+
+static struct item *
+item_create(struct display *display, int x, int y, int seed)
+{
+ struct item *item;
+ struct timeval tv;
+
+ item = malloc(sizeof *item);
+ if (item == NULL)
+ return NULL;
+
+
+ gettimeofday(&tv, NULL);
+ item->seed = seed ? seed : tv.tv_usec;
+ srandom(item->seed);
+
+ const int petal_count = 3 + random() % 5;
+ const double r1 = 20 + random() % 10;
+ const double r2 = 5 + random() % 12;
+ const double u = (10 + random() % 90) / 100.0;
+ const double v = (random() % 90) / 100.0;
+
+ cairo_t *cr;
+ int i;
+ double t, dt = 2 * M_PI / (petal_count * 2);
+ double x1, y1, x2, y2, x3, y3;
+ struct rectangle rect;
+
+
+ rect.width = item_width;
+ rect.height = item_height;
+ item->surface =
+ display_create_surface(display, NULL, &rect, SURFACE_SHM);
+
+ item->x = x;
+ item->y = y;
+
+ cr = cairo_create(item->surface);
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ cairo_set_source_rgba(cr, 0, 0, 0, 0);
+ cairo_paint(cr);
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+ cairo_translate(cr, item_width / 2, item_height / 2);
+ t = random();
+ cairo_move_to(cr, cos(t) * r1, sin(t) * r1);
+ for (i = 0; i < petal_count; i++, t += dt * 2) {
+ x1 = cos(t) * r1;
+ y1 = sin(t) * r1;
+ x2 = cos(t + dt) * r2;
+ y2 = sin(t + dt) * r2;
+ x3 = cos(t + 2 * dt) * r1;
+ y3 = sin(t + 2 * dt) * r1;
+
+ cairo_curve_to(cr,
+ x1 - y1 * u, y1 + x1 * u,
+ x2 + y2 * v, y2 - x2 * v,
+ x2, y2);
+
+ cairo_curve_to(cr,
+ x2 - y2 * v, y2 + x2 * v,
+ x3 + y3 * u, y3 - x3 * u,
+ x3, y3);
+ }
+
+ cairo_close_path(cr);
+
+ cairo_set_source_rgba(cr,
+ 0.5 + (random() % 50) / 49.0,
+ 0.5 + (random() % 50) / 49.0,
+ 0.5 + (random() % 50) / 49.0,
+ 0.5 + (random() % 100) / 99.0);
+
+ cairo_fill_preserve(cr);
+
+ cairo_set_line_width(cr, 1);
+ cairo_set_source_rgba(cr,
+ 0.5 + (random() % 50) / 49.0,
+ 0.5 + (random() % 50) / 49.0,
+ 0.5 + (random() % 50) / 49.0,
+ 0.5 + (random() % 100) / 99.0);
+ cairo_stroke(cr);
+
+ cairo_destroy(cr);
+
+ return item;
+}
+
+static void
+dnd_redraw_handler(struct widget *widget, void *data)
+{
+ struct dnd *dnd = data;
+ struct rectangle allocation;
+ cairo_t *cr;
+ cairo_surface_t *surface;
+ unsigned int i;
+
+ surface = window_get_surface(dnd->window);
+ cr = cairo_create(surface);
+ widget_get_allocation(dnd->widget, &allocation);
+ cairo_rectangle(cr, allocation.x, allocation.y,
+ allocation.width, allocation.height);
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ cairo_set_source_rgba(cr, 0, 0, 0, 0.8);
+ cairo_fill(cr);
+
+ cairo_rectangle(cr, allocation.x, allocation.y,
+ allocation.width, allocation.height);
+ cairo_clip(cr);
+ cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+ for (i = 0; i < ARRAY_LENGTH(dnd->items); i++) {
+ if (!dnd->items[i])
+ continue;
+ cairo_set_source_surface(cr, dnd->items[i]->surface,
+ dnd->items[i]->x + allocation.x,
+ dnd->items[i]->y + allocation.y);
+ cairo_paint(cr);
+ }
+
+ cairo_destroy(cr);
+ cairo_surface_destroy(surface);
+}
+
+static void
+keyboard_focus_handler(struct window *window,
+ struct input *device, void *data)
+{
+ struct dnd *dnd = data;
+
+ window_schedule_redraw(dnd->window);
+}
+
+static int
+dnd_add_item(struct dnd *dnd, struct item *item)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_LENGTH(dnd->items); i++) {
+ if (dnd->items[i] == 0) {
+ dnd->items[i] = item;
+ return i;
+ }
+ }
+ return -1;
+}
+
+static struct item *
+dnd_get_item(struct dnd *dnd, int32_t x, int32_t y)
+{
+ struct item *item;
+ struct rectangle allocation;
+ unsigned int i;
+
+ widget_get_allocation(dnd->widget, &allocation);
+
+ x -= allocation.x;
+ y -= allocation.y;
+
+ for (i = 0; i < ARRAY_LENGTH(dnd->items); i++) {
+ item = dnd->items[i];
+ if (item &&
+ item->x <= x && x < item->x + item_width &&
+ item->y <= y && y < item->y + item_height)
+ return item;
+ }
+
+ return NULL;
+}
+
+static void
+data_source_target(void *data,
+ struct wl_data_source *source, const char *mime_type)
+{
+ struct dnd_drag *dnd_drag = data;
+ struct dnd *dnd = dnd_drag->dnd;
+ cairo_surface_t *surface;
+ struct wl_buffer *buffer;
+
+ dnd_drag->mime_type = mime_type;
+ if (mime_type)
+ surface = dnd_drag->opaque;
+ else
+ surface = dnd_drag->translucent;
+
+ buffer = display_get_buffer_for_surface(dnd->display, surface);
+ wl_surface_attach(dnd_drag->drag_surface, buffer, 0, 0);
+ wl_surface_damage(dnd_drag->drag_surface, 0, 0,
+ dnd_drag->width, dnd_drag->height);
+ wl_surface_commit(dnd_drag->drag_surface);
+}
+
+static void
+data_source_send(void *data, struct wl_data_source *source,
+ const char *mime_type, int32_t fd)
+{
+ struct dnd_flower_message dnd_flower_message;
+ struct dnd_drag *dnd_drag = data;
+ char buffer[128];
+ int n;
+
+ if (strcmp(mime_type, flower_mime_type) == 0) {
+ dnd_flower_message.seed = dnd_drag->item->seed;
+ dnd_flower_message.x_offset = dnd_drag->x_offset;
+ dnd_flower_message.y_offset = dnd_drag->y_offset;
+
+ if (write(fd, &dnd_flower_message,
+ sizeof dnd_flower_message) < 0)
+ abort();
+ } else if (strcmp(mime_type, text_mime_type) == 0) {
+ n = snprintf(buffer, sizeof buffer, "seed=%d x=%d y=%d\n",
+ dnd_drag->item->seed,
+ dnd_drag->x_offset,
+ dnd_drag->y_offset);
+
+ if (write(fd, buffer, n) < 0)
+ abort();
+ }
+
+ close(fd);
+}
+
+static void
+data_source_cancelled(void *data, struct wl_data_source *source)
+{
+ struct dnd_drag *dnd_drag = data;
+
+ /* The 'cancelled' event means that the source is no longer in
+ * use by the drag (or current selection). We need to clean
+ * up the drag object created and the local state. */
+
+ wl_data_source_destroy(dnd_drag->data_source);
+
+ /* Destroy the item that has been dragged out */
+ cairo_surface_destroy(dnd_drag->item->surface);
+ free(dnd_drag->item);
+
+ wl_surface_destroy(dnd_drag->drag_surface);
+
+ cairo_surface_destroy(dnd_drag->translucent);
+ cairo_surface_destroy(dnd_drag->opaque);
+ free(dnd_drag);
+}
+
+static const struct wl_data_source_listener data_source_listener = {
+ data_source_target,
+ data_source_send,
+ data_source_cancelled
+};
+
+static cairo_surface_t *
+create_drag_cursor(struct dnd_drag *dnd_drag,
+ struct item *item, int32_t x, int32_t y, double opacity)
+{
+ struct dnd *dnd = dnd_drag->dnd;
+ cairo_surface_t *surface;
+ struct wl_cursor_image *pointer;
+ struct rectangle rectangle;
+ cairo_pattern_t *pattern;
+ cairo_t *cr;
+
+ pointer = display_get_pointer_image(dnd->display, CURSOR_DRAGGING);
+ if (!pointer) {
+ fprintf(stderr, "WARNING: grabbing cursor image not found\n");
+ pointer = display_get_pointer_image(dnd->display,
+ CURSOR_LEFT_PTR);
+ assert(pointer && "no cursor image found");
+ }
+
+ rectangle.width = item_width + 2 * pointer->width;
+ rectangle.height = item_height + 2 * pointer->height;
+ surface = display_create_surface(dnd->display, NULL, &rectangle,
+ SURFACE_SHM);
+
+ cr = cairo_create(surface);
+ cairo_translate(cr, pointer->width, pointer->height);
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ cairo_set_source_rgba(cr, 0, 0, 0, 0);
+ cairo_paint(cr);
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+ cairo_set_source_surface(cr, item->surface, 0, 0);
+ pattern = cairo_pattern_create_rgba(0, 0, 0, opacity);
+ cairo_mask(cr, pattern);
+ cairo_pattern_destroy(pattern);
+
+ /* FIXME: more cairo-gl brokeness */
+ surface_flush_device(surface);
+ cairo_destroy(cr);
+
+ dnd_drag->hotspot_x = pointer->width + x - item->x;
+ dnd_drag->hotspot_y = pointer->height + y - item->y;
+ dnd_drag->width = rectangle.width;
+ dnd_drag->height = rectangle.height;
+
+ return surface;
+}
+
+static void
+dnd_button_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ uint32_t button, enum wl_pointer_button_state state,
+ void *data)
+{
+ struct dnd *dnd = data;
+ int32_t x, y;
+ struct item *item;
+ struct rectangle allocation;
+ struct dnd_drag *dnd_drag;
+ struct display *display;
+ struct wl_compositor *compositor;
+ struct wl_buffer *buffer;
+ unsigned int i;
+ uint32_t serial;
+ cairo_surface_t *icon;
+
+ widget_get_allocation(dnd->widget, &allocation);
+ input_get_position(input, &x, &y);
+ item = dnd_get_item(dnd, x, y);
+ x -= allocation.x;
+ y -= allocation.y;
+
+ if (item && state == WL_POINTER_BUTTON_STATE_PRESSED) {
+ dnd_drag = xmalloc(sizeof *dnd_drag);
+ dnd_drag->dnd = dnd;
+ dnd_drag->input = input;
+ dnd_drag->time = time;
+ dnd_drag->item = item;
+ dnd_drag->x_offset = x - item->x;
+ dnd_drag->y_offset = y - item->y;
+
+ for (i = 0; i < ARRAY_LENGTH(dnd->items); i++) {
+ if (item == dnd->items[i]){
+ dnd->items[i] = 0;
+ break;
+ }
+ }
+
+ display = window_get_display(dnd->window);
+ compositor = display_get_compositor(display);
+ serial = display_get_serial(display);
+ dnd_drag->drag_surface =
+ wl_compositor_create_surface(compositor);
+
+ input_ungrab(input);
+
+ if (dnd->self_only) {
+ dnd_drag->data_source = NULL;
+ } else {
+ dnd_drag->data_source =
+ display_create_data_source(dnd->display);
+ wl_data_source_add_listener(dnd_drag->data_source,
+ &data_source_listener,
+ dnd_drag);
+ wl_data_source_offer(dnd_drag->data_source,
+ flower_mime_type);
+ wl_data_source_offer(dnd_drag->data_source,
+ text_mime_type);
+ }
+
+ wl_data_device_start_drag(input_get_data_device(input),
+ dnd_drag->data_source,
+ window_get_wl_surface(dnd->window),
+ dnd_drag->drag_surface,
+ serial);
+
+ input_set_pointer_image(input, CURSOR_DRAGGING);
+
+ dnd_drag->opaque =
+ create_drag_cursor(dnd_drag, item, x, y, 1);
+ dnd_drag->translucent =
+ create_drag_cursor(dnd_drag, item, x, y, 0.2);
+
+ if (dnd->self_only)
+ icon = dnd_drag->opaque;
+ else
+ icon = dnd_drag->translucent;
+
+ buffer = display_get_buffer_for_surface(dnd->display, icon);
+ wl_surface_attach(dnd_drag->drag_surface, buffer,
+ -dnd_drag->hotspot_x, -dnd_drag->hotspot_y);
+ wl_surface_damage(dnd_drag->drag_surface, 0, 0,
+ dnd_drag->width, dnd_drag->height);
+ wl_surface_commit(dnd_drag->drag_surface);
+
+ dnd->current_drag = dnd_drag;
+ window_schedule_redraw(dnd->window);
+ }
+}
+
+static int
+lookup_cursor(struct dnd *dnd, int x, int y)
+{
+ struct item *item;
+
+ item = dnd_get_item(dnd, x, y);
+ if (item)
+ return CURSOR_HAND1;
+ else
+ return CURSOR_LEFT_PTR;
+}
+
+static int
+dnd_enter_handler(struct widget *widget,
+ struct input *input, float x, float y, void *data)
+{
+ struct dnd *dnd = data;
+
+ dnd->current_drag = NULL;
+
+ return lookup_cursor(dnd, x, y);
+}
+
+static int
+dnd_motion_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ float x, float y, void *data)
+{
+ return lookup_cursor(data, x, y);
+}
+
+static void
+dnd_data_handler(struct window *window,
+ struct input *input,
+ float x, float y, const char **types, void *data)
+{
+ struct dnd *dnd = data;
+ int i, has_flower = 0;
+
+ if (!types)
+ return;
+ for (i = 0; types[i]; i++)
+ if (strcmp(types[i], flower_mime_type) == 0)
+ has_flower = 1;
+
+ if (dnd_get_item(dnd, x, y) || dnd->self_only || !has_flower) {
+ input_accept(input, NULL);
+ } else {
+ input_accept(input, flower_mime_type);
+ }
+}
+
+static void
+dnd_receive_func(void *data, size_t len, int32_t x, int32_t y, void *user_data)
+{
+ struct dnd *dnd = user_data;
+ struct dnd_flower_message *message = data;
+ struct item *item;
+ struct rectangle allocation;
+
+ if (len == 0) {
+ return;
+ } else if (len != sizeof *message) {
+ fprintf(stderr, "odd message length %zu, expected %zu\n",
+ len, sizeof *message);
+ return;
+ }
+
+ widget_get_allocation(dnd->widget, &allocation);
+ item = item_create(dnd->display,
+ x - message->x_offset - allocation.x,
+ y - message->y_offset - allocation.y,
+ message->seed);
+
+ dnd_add_item(dnd, item);
+ window_schedule_redraw(dnd->window);
+}
+
+static void
+dnd_drop_handler(struct window *window, struct input *input,
+ int32_t x, int32_t y, void *data)
+{
+ struct dnd *dnd = data;
+ struct dnd_flower_message message;
+
+ if (dnd_get_item(dnd, x, y)) {
+ fprintf(stderr, "got 'drop', but no target\n");
+ return;
+ }
+
+ if (!dnd->self_only) {
+ input_receive_drag_data(input,
+ flower_mime_type,
+ dnd_receive_func, dnd);
+ } else if (dnd->current_drag) {
+ message.seed = dnd->current_drag->item->seed;
+ message.x_offset = dnd->current_drag->x_offset;
+ message.y_offset = dnd->current_drag->y_offset;
+ dnd_receive_func(&message, sizeof message, x, y, dnd);
+ dnd->current_drag = NULL;
+ } else {
+ fprintf(stderr, "ignoring drop from another client\n");
+ }
+}
+
+static struct dnd *
+dnd_create(struct display *display)
+{
+ struct dnd *dnd;
+ int x, y;
+ int32_t width, height;
+ unsigned int i;
+
+ dnd = xzalloc(sizeof *dnd);
+ dnd->window = window_create(display);
+ dnd->widget = frame_create(dnd->window, dnd);
+ window_set_title(dnd->window, "Wayland Drag and Drop Demo");
+
+ dnd->display = display;
+ dnd->key = 100;
+
+ for (i = 0; i < ARRAY_LENGTH(dnd->items); i++) {
+ x = (i % 4) * (item_width + item_padding) + item_padding;
+ y = (i / 4) * (item_height + item_padding) + item_padding;
+ if ((i ^ (i >> 2)) & 1)
+ dnd->items[i] = item_create(display, x, y, 0);
+ else
+ dnd->items[i] = NULL;
+ }
+
+ window_set_user_data(dnd->window, dnd);
+ window_set_keyboard_focus_handler(dnd->window,
+ keyboard_focus_handler);
+ window_set_data_handler(dnd->window, dnd_data_handler);
+ window_set_drop_handler(dnd->window, dnd_drop_handler);
+
+ widget_set_redraw_handler(dnd->widget, dnd_redraw_handler);
+ widget_set_enter_handler(dnd->widget, dnd_enter_handler);
+ widget_set_motion_handler(dnd->widget, dnd_motion_handler);
+ widget_set_button_handler(dnd->widget, dnd_button_handler);
+
+ width = 4 * (item_width + item_padding) + item_padding;
+ height = 4 * (item_height + item_padding) + item_padding;
+
+ frame_set_child_size(dnd->widget, width, height);
+
+ return dnd;
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct display *d;
+ struct dnd *dnd;
+ int i;
+
+ d = display_create(&argc, argv);
+ if (d == NULL) {
+ fprintf(stderr, "failed to create display: %m\n");
+ return -1;
+ }
+
+ dnd = dnd_create(d);
+
+ for (i = 1; i < argc; i++)
+ if (strcmp("--self-only", argv[i]) == 0)
+ dnd->self_only = 1;
+
+ display_run(d);
+
+ return 0;
+}
diff --git a/clients/editor.c b/clients/editor.c
new file mode 100644
index 00000000..12650f32
--- /dev/null
+++ b/clients/editor.c
@@ -0,0 +1,1250 @@
+/*
+ * Copyright © 2012 Openismus GmbH
+ * Copyright © 2012 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 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.
+ */
+
+#include "config.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+
+#include <linux/input.h>
+#include <cairo.h>
+
+#include <pango/pangocairo.h>
+
+#include "window.h"
+#include "text-client-protocol.h"
+
+struct text_entry {
+ struct widget *widget;
+ struct window *window;
+ char *text;
+ int active;
+ uint32_t cursor;
+ uint32_t anchor;
+ struct {
+ char *text;
+ int32_t cursor;
+ char *commit;
+ PangoAttrList *attr_list;
+ } preedit;
+ struct {
+ PangoAttrList *attr_list;
+ int32_t cursor;
+ } preedit_info;
+ struct {
+ int32_t cursor;
+ int32_t anchor;
+ uint32_t delete_index;
+ uint32_t delete_length;
+ bool invalid_delete;
+ } pending_commit;
+ struct wl_text_input *text_input;
+ PangoLayout *layout;
+ struct {
+ xkb_mod_mask_t shift_mask;
+ } keysym;
+ uint32_t serial;
+ uint32_t reset_serial;
+ uint32_t content_purpose;
+ uint32_t click_to_show;
+ char *preferred_language;
+ bool button_pressed;
+};
+
+struct editor {
+ struct wl_text_input_manager *text_input_manager;
+ struct display *display;
+ struct window *window;
+ struct widget *widget;
+ struct text_entry *entry;
+ struct text_entry *editor;
+ struct text_entry *active_entry;
+};
+
+static const char *
+utf8_end_char(const char *p)
+{
+ while ((*p & 0xc0) == 0x80)
+ p++;
+ return p;
+}
+
+static const char *
+utf8_prev_char(const char *s, const char *p)
+{
+ for (--p; p >= s; --p) {
+ if ((*p & 0xc0) != 0x80)
+ return p;
+ }
+ return NULL;
+}
+
+static const char *
+utf8_next_char(const char *p)
+{
+ if (*p != 0)
+ return utf8_end_char(++p);
+ return NULL;
+}
+
+static void text_entry_redraw_handler(struct widget *widget, void *data);
+static void text_entry_button_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ uint32_t button,
+ enum wl_pointer_button_state state, void *data);
+static int text_entry_motion_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ float x, float y, void *data);
+static void text_entry_insert_at_cursor(struct text_entry *entry, const char *text,
+ int32_t cursor, int32_t anchor);
+static void text_entry_set_preedit(struct text_entry *entry,
+ const char *preedit_text,
+ int preedit_cursor);
+static void text_entry_delete_text(struct text_entry *entry,
+ uint32_t index, uint32_t length);
+static void text_entry_delete_selected_text(struct text_entry *entry);
+static void text_entry_reset_preedit(struct text_entry *entry);
+static void text_entry_commit_and_reset(struct text_entry *entry);
+static void text_entry_get_cursor_rectangle(struct text_entry *entry, struct rectangle *rectangle);
+static void text_entry_update(struct text_entry *entry);
+
+static void
+text_input_commit_string(void *data,
+ struct wl_text_input *text_input,
+ uint32_t serial,
+ const char *text)
+{
+ struct text_entry *entry = data;
+
+ if ((entry->serial - serial) > (entry->serial - entry->reset_serial)) {
+ fprintf(stderr, "Ignore commit. Serial: %u, Current: %u, Reset: %u\n",
+ serial, entry->serial, entry->reset_serial);
+ return;
+ }
+
+ if (entry->pending_commit.invalid_delete) {
+ fprintf(stderr, "Ignore commit. Invalid previous delete_surrounding event.\n");
+ memset(&entry->pending_commit, 0, sizeof entry->pending_commit);
+ return;
+ }
+
+ text_entry_reset_preedit(entry);
+
+ if (entry->pending_commit.delete_length) {
+ text_entry_delete_text(entry,
+ entry->pending_commit.delete_index,
+ entry->pending_commit.delete_length);
+ } else {
+ text_entry_delete_selected_text(entry);
+ }
+
+ text_entry_insert_at_cursor(entry, text,
+ entry->pending_commit.cursor,
+ entry->pending_commit.anchor);
+
+ memset(&entry->pending_commit, 0, sizeof entry->pending_commit);
+
+ widget_schedule_redraw(entry->widget);
+}
+
+static void
+clear_pending_preedit(struct text_entry *entry)
+{
+ memset(&entry->pending_commit, 0, sizeof entry->pending_commit);
+
+ pango_attr_list_unref(entry->preedit_info.attr_list);
+
+ entry->preedit_info.cursor = 0;
+ entry->preedit_info.attr_list = NULL;
+
+ memset(&entry->preedit_info, 0, sizeof entry->preedit_info);
+}
+
+static void
+text_input_preedit_string(void *data,
+ struct wl_text_input *text_input,
+ uint32_t serial,
+ const char *text,
+ const char *commit)
+{
+ struct text_entry *entry = data;
+
+ if ((entry->serial - serial) > (entry->serial - entry->reset_serial)) {
+ fprintf(stderr, "Ignore preedit_string. Serial: %u, Current: %u, Reset: %u\n",
+ serial, entry->serial, entry->reset_serial);
+ clear_pending_preedit(entry);
+ return;
+ }
+
+ if (entry->pending_commit.invalid_delete) {
+ fprintf(stderr, "Ignore preedit_string. Invalid previous delete_surrounding event.\n");
+ clear_pending_preedit(entry);
+ return;
+ }
+
+ if (entry->pending_commit.delete_length) {
+ text_entry_delete_text(entry,
+ entry->pending_commit.delete_index,
+ entry->pending_commit.delete_length);
+ } else {
+ text_entry_delete_selected_text(entry);
+ }
+
+ text_entry_set_preedit(entry, text, entry->preedit_info.cursor);
+ entry->preedit.commit = strdup(commit);
+ entry->preedit.attr_list = pango_attr_list_ref(entry->preedit_info.attr_list);
+
+ clear_pending_preedit(entry);
+
+ text_entry_update(entry);
+
+ widget_schedule_redraw(entry->widget);
+}
+
+static void
+text_input_delete_surrounding_text(void *data,
+ struct wl_text_input *text_input,
+ int32_t index,
+ uint32_t length)
+{
+ struct text_entry *entry = data;
+ uint32_t text_length;
+
+ entry->pending_commit.delete_index = entry->cursor + index;
+ entry->pending_commit.delete_length = length;
+ entry->pending_commit.invalid_delete = false;
+
+ text_length = strlen(entry->text);
+
+ if (entry->pending_commit.delete_index > text_length ||
+ length > text_length ||
+ entry->pending_commit.delete_index + length > text_length) {
+ fprintf(stderr, "delete_surrounding_text: Invalid index: %d," \
+ "length %u'; cursor: %u text length: %u\n", index, length, entry->cursor, text_length);
+ entry->pending_commit.invalid_delete = true;
+ return;
+ }
+}
+
+static void
+text_input_cursor_position(void *data,
+ struct wl_text_input *text_input,
+ int32_t index,
+ int32_t anchor)
+{
+ struct text_entry *entry = data;
+
+ entry->pending_commit.cursor = index;
+ entry->pending_commit.anchor = anchor;
+}
+
+static void
+text_input_preedit_styling(void *data,
+ struct wl_text_input *text_input,
+ uint32_t index,
+ uint32_t length,
+ uint32_t style)
+{
+ struct text_entry *entry = data;
+ PangoAttribute *attr1 = NULL;
+ PangoAttribute *attr2 = NULL;
+
+ if (!entry->preedit_info.attr_list)
+ entry->preedit_info.attr_list = pango_attr_list_new();
+
+ switch (style) {
+ case WL_TEXT_INPUT_PREEDIT_STYLE_DEFAULT:
+ case WL_TEXT_INPUT_PREEDIT_STYLE_UNDERLINE:
+ attr1 = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
+ break;
+ case WL_TEXT_INPUT_PREEDIT_STYLE_INCORRECT:
+ attr1 = pango_attr_underline_new(PANGO_UNDERLINE_ERROR);
+ attr2 = pango_attr_underline_color_new(65535, 0, 0);
+ break;
+ case WL_TEXT_INPUT_PREEDIT_STYLE_SELECTION:
+ attr1 = pango_attr_background_new(0.3 * 65535, 0.3 * 65535, 65535);
+ attr2 = pango_attr_foreground_new(65535, 65535, 65535);
+ break;
+ case WL_TEXT_INPUT_PREEDIT_STYLE_HIGHLIGHT:
+ case WL_TEXT_INPUT_PREEDIT_STYLE_ACTIVE:
+ attr1 = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
+ attr2 = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
+ break;
+ case WL_TEXT_INPUT_PREEDIT_STYLE_INACTIVE:
+ attr1 = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
+ attr2 = pango_attr_foreground_new(0.3 * 65535, 0.3 * 65535, 0.3 * 65535);
+ break;
+ }
+
+ if (attr1) {
+ attr1->start_index = entry->cursor + index;
+ attr1->end_index = entry->cursor + index + length;
+ pango_attr_list_insert(entry->preedit_info.attr_list, attr1);
+ }
+
+ if (attr2) {
+ attr2->start_index = entry->cursor + index;
+ attr2->end_index = entry->cursor + index + length;
+ pango_attr_list_insert(entry->preedit_info.attr_list, attr2);
+ }
+}
+
+static void
+text_input_preedit_cursor(void *data,
+ struct wl_text_input *text_input,
+ int32_t index)
+{
+ struct text_entry *entry = data;
+
+ entry->preedit_info.cursor = index;
+}
+
+static void
+text_input_modifiers_map(void *data,
+ struct wl_text_input *text_input,
+ struct wl_array *map)
+{
+ struct text_entry *entry = data;
+
+ entry->keysym.shift_mask = keysym_modifiers_get_mask(map, "Shift");
+}
+
+static void
+text_input_keysym(void *data,
+ struct wl_text_input *text_input,
+ uint32_t serial,
+ uint32_t time,
+ uint32_t key,
+ uint32_t state,
+ uint32_t modifiers)
+{
+ struct text_entry *entry = data;
+ const char *state_label = "release";
+ const char *key_label = "Unknown";
+ const char *new_char;
+
+ if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
+ state_label = "pressed";
+ }
+
+ if (key == XKB_KEY_Left ||
+ key == XKB_KEY_Right) {
+ if (state != WL_KEYBOARD_KEY_STATE_RELEASED)
+ return;
+
+ if (key == XKB_KEY_Left)
+ new_char = utf8_prev_char(entry->text, entry->text + entry->cursor);
+ else
+ new_char = utf8_next_char(entry->text + entry->cursor);
+
+ if (new_char != NULL) {
+ entry->cursor = new_char - entry->text;
+ }
+
+ if (!(modifiers & entry->keysym.shift_mask))
+ entry->anchor = entry->cursor;
+ widget_schedule_redraw(entry->widget);
+
+ return;
+ }
+
+ if (key == XKB_KEY_BackSpace) {
+ const char *start, *end;
+
+ if (state != WL_KEYBOARD_KEY_STATE_RELEASED)
+ return;
+
+ text_entry_commit_and_reset(entry);
+
+ start = utf8_prev_char(entry->text, entry->text + entry->cursor);
+ if (start == NULL)
+ return;
+
+ end = utf8_next_char(start);
+
+ text_entry_delete_text(entry,
+ start - entry->text,
+ end - start);
+
+ return;
+ }
+
+ switch (key) {
+ case XKB_KEY_Tab:
+ key_label = "Tab";
+ break;
+ case XKB_KEY_KP_Enter:
+ case XKB_KEY_Return:
+ key_label = "Enter";
+ break;
+ }
+
+ fprintf(stderr, "%s key was %s.\n", key_label, state_label);
+}
+
+static void
+text_input_enter(void *data,
+ struct wl_text_input *text_input,
+ struct wl_surface *surface)
+{
+ struct text_entry *entry = data;
+
+ if (surface != window_get_wl_surface(entry->window))
+ return;
+
+ entry->active = 1;
+
+ text_entry_update(entry);
+ entry->reset_serial = entry->serial;
+
+ widget_schedule_redraw(entry->widget);
+}
+
+static void
+text_input_leave(void *data,
+ struct wl_text_input *text_input)
+{
+ struct text_entry *entry = data;
+
+ text_entry_commit_and_reset(entry);
+
+ entry->active = 0;
+
+ wl_text_input_hide_input_panel(text_input);
+
+ widget_schedule_redraw(entry->widget);
+}
+
+static void
+text_input_input_panel_state(void *data,
+ struct wl_text_input *text_input,
+ uint32_t state)
+{
+}
+
+static void
+text_input_language(void *data,
+ struct wl_text_input *text_input,
+ uint32_t serial,
+ const char *language)
+{
+ fprintf(stderr, "input language is %s \n", language);
+}
+
+static void
+text_input_text_direction(void *data,
+ struct wl_text_input *text_input,
+ uint32_t serial,
+ uint32_t direction)
+{
+ struct text_entry *entry = data;
+ PangoContext *context = pango_layout_get_context(entry->layout);
+ PangoDirection pango_direction;
+
+
+ switch (direction) {
+ case WL_TEXT_INPUT_TEXT_DIRECTION_LTR:
+ pango_direction = PANGO_DIRECTION_LTR;
+ break;
+ case WL_TEXT_INPUT_TEXT_DIRECTION_RTL:
+ pango_direction = PANGO_DIRECTION_RTL;
+ break;
+ case WL_TEXT_INPUT_TEXT_DIRECTION_AUTO:
+ default:
+ pango_direction = PANGO_DIRECTION_NEUTRAL;
+ }
+
+ pango_context_set_base_dir(context, pango_direction);
+}
+
+static const struct wl_text_input_listener text_input_listener = {
+ text_input_enter,
+ text_input_leave,
+ text_input_modifiers_map,
+ text_input_input_panel_state,
+ text_input_preedit_string,
+ text_input_preedit_styling,
+ text_input_preedit_cursor,
+ text_input_commit_string,
+ text_input_cursor_position,
+ text_input_delete_surrounding_text,
+ text_input_keysym,
+ text_input_language,
+ text_input_text_direction
+};
+
+static struct text_entry*
+text_entry_create(struct editor *editor, const char *text)
+{
+ struct text_entry *entry;
+
+ entry = xmalloc(sizeof *entry);
+ memset(entry, 0, sizeof *entry);
+
+ entry->widget = widget_add_widget(editor->widget, entry);
+ entry->window = editor->window;
+ entry->text = strdup(text);
+ entry->active = 0;
+ entry->cursor = strlen(text);
+ entry->anchor = entry->cursor;
+ entry->text_input = wl_text_input_manager_create_text_input(editor->text_input_manager);
+ wl_text_input_add_listener(entry->text_input, &text_input_listener, entry);
+
+ widget_set_redraw_handler(entry->widget, text_entry_redraw_handler);
+ widget_set_button_handler(entry->widget, text_entry_button_handler);
+ widget_set_motion_handler(entry->widget, text_entry_motion_handler);
+
+ return entry;
+}
+
+static void
+text_entry_destroy(struct text_entry *entry)
+{
+ widget_destroy(entry->widget);
+ wl_text_input_destroy(entry->text_input);
+ g_clear_object(&entry->layout);
+ free(entry->text);
+ free(entry);
+}
+
+static void
+redraw_handler(struct widget *widget, void *data)
+{
+ struct editor *editor = data;
+ cairo_surface_t *surface;
+ struct rectangle allocation;
+ cairo_t *cr;
+
+ surface = window_get_surface(editor->window);
+ widget_get_allocation(editor->widget, &allocation);
+
+ cr = cairo_create(surface);
+ cairo_rectangle(cr, allocation.x, allocation.y, allocation.width, allocation.height);
+ cairo_clip(cr);
+
+ cairo_translate(cr, allocation.x, allocation.y);
+
+ /* Draw background */
+ cairo_push_group(cr);
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ cairo_set_source_rgba(cr, 1, 1, 1, 1);
+ cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
+ cairo_fill(cr);
+
+ cairo_pop_group_to_source(cr);
+ cairo_paint(cr);
+
+ cairo_destroy(cr);
+ cairo_surface_destroy(surface);
+}
+
+static void
+text_entry_allocate(struct text_entry *entry, int32_t x, int32_t y,
+ int32_t width, int32_t height)
+{
+ widget_set_allocation(entry->widget, x, y, width, height);
+}
+
+static void
+resize_handler(struct widget *widget,
+ int32_t width, int32_t height, void *data)
+{
+ struct editor *editor = data;
+ struct rectangle allocation;
+
+ widget_get_allocation(editor->widget, &allocation);
+
+ text_entry_allocate(editor->entry,
+ allocation.x + 20, allocation.y + 20,
+ width - 40, height / 2 - 40);
+ text_entry_allocate(editor->editor,
+ allocation.x + 20, allocation.y + height / 2 + 20,
+ width - 40, height / 2 - 40);
+}
+
+static void
+text_entry_activate(struct text_entry *entry,
+ struct wl_seat *seat)
+{
+ struct wl_surface *surface = window_get_wl_surface(entry->window);
+
+ if (entry->click_to_show && entry->active) {
+ wl_text_input_show_input_panel(entry->text_input);
+
+ return;
+ }
+
+ if (!entry->click_to_show)
+ wl_text_input_show_input_panel(entry->text_input);
+
+ wl_text_input_activate(entry->text_input,
+ seat,
+ surface);
+}
+
+static void
+text_entry_deactivate(struct text_entry *entry,
+ struct wl_seat *seat)
+{
+ wl_text_input_deactivate(entry->text_input,
+ seat);
+}
+
+static void
+text_entry_update_layout(struct text_entry *entry)
+{
+ char *text;
+ PangoAttrList *attr_list;
+
+ assert(entry->cursor <= (strlen(entry->text) +
+ (entry->preedit.text ? strlen(entry->preedit.text) : 0)));
+
+ if (entry->preedit.text) {
+ text = malloc(strlen(entry->text) + strlen(entry->preedit.text) + 1);
+ strncpy(text, entry->text, entry->cursor);
+ strcpy(text + entry->cursor, entry->preedit.text);
+ strcpy(text + entry->cursor + strlen(entry->preedit.text),
+ entry->text + entry->cursor);
+ } else {
+ text = strdup(entry->text);
+ }
+
+ if (entry->cursor != entry->anchor) {
+ int start_index = MIN(entry->cursor, entry->anchor);
+ int end_index = MAX(entry->cursor, entry->anchor);
+ PangoAttribute *attr;
+
+ attr_list = pango_attr_list_copy(entry->preedit.attr_list);
+
+ if (!attr_list)
+ attr_list = pango_attr_list_new();
+
+ attr = pango_attr_background_new(0.3 * 65535, 0.3 * 65535, 65535);
+ attr->start_index = start_index;
+ attr->end_index = end_index;
+ pango_attr_list_insert(attr_list, attr);
+
+ attr = pango_attr_foreground_new(65535, 65535, 65535);
+ attr->start_index = start_index;
+ attr->end_index = end_index;
+ pango_attr_list_insert(attr_list, attr);
+ } else {
+ attr_list = pango_attr_list_ref(entry->preedit.attr_list);
+ }
+
+ if (entry->preedit.text && !entry->preedit.attr_list) {
+ PangoAttribute *attr;
+
+ if (!attr_list)
+ attr_list = pango_attr_list_new();
+
+ attr = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
+ attr->start_index = entry->cursor;
+ attr->end_index = entry->cursor + strlen(entry->preedit.text);
+ pango_attr_list_insert(attr_list, attr);
+ }
+
+ if (entry->layout) {
+ pango_layout_set_text(entry->layout, text, -1);
+ pango_layout_set_attributes(entry->layout, attr_list);
+ }
+
+ free(text);
+ pango_attr_list_unref(attr_list);
+}
+
+static void
+text_entry_update(struct text_entry *entry)
+{
+ struct rectangle cursor_rectangle;
+
+ wl_text_input_set_content_type(entry->text_input,
+ WL_TEXT_INPUT_CONTENT_HINT_NONE,
+ entry->content_purpose);
+
+ wl_text_input_set_surrounding_text(entry->text_input,
+ entry->text,
+ entry->cursor,
+ entry->anchor);
+
+ if (entry->preferred_language)
+ wl_text_input_set_preferred_language(entry->text_input,
+ entry->preferred_language);
+
+ text_entry_get_cursor_rectangle(entry, &cursor_rectangle);
+ wl_text_input_set_cursor_rectangle(entry->text_input, cursor_rectangle.x, cursor_rectangle.y,
+ cursor_rectangle.width, cursor_rectangle.height);
+
+ wl_text_input_commit_state(entry->text_input, ++entry->serial);
+}
+
+static void
+text_entry_insert_at_cursor(struct text_entry *entry, const char *text,
+ int32_t cursor, int32_t anchor)
+{
+ char *new_text = malloc(strlen(entry->text) + strlen(text) + 1);
+
+ strncpy(new_text, entry->text, entry->cursor);
+ strcpy(new_text + entry->cursor, text);
+ strcpy(new_text + entry->cursor + strlen(text),
+ entry->text + entry->cursor);
+
+ free(entry->text);
+ entry->text = new_text;
+ if (anchor >= 0)
+ entry->anchor = entry->cursor + strlen(text) + anchor;
+ else
+ entry->anchor = entry->cursor + 1 + anchor;
+
+ if (cursor >= 0)
+ entry->cursor += strlen(text) + cursor;
+ else
+ entry->cursor += 1 + cursor;
+
+ text_entry_update_layout(entry);
+
+ widget_schedule_redraw(entry->widget);
+
+ text_entry_update(entry);
+}
+
+static void
+text_entry_reset_preedit(struct text_entry *entry)
+{
+ entry->preedit.cursor = 0;
+
+ free(entry->preedit.text);
+ entry->preedit.text = NULL;
+
+ free(entry->preedit.commit);
+ entry->preedit.commit = NULL;
+
+ pango_attr_list_unref(entry->preedit.attr_list);
+ entry->preedit.attr_list = NULL;
+}
+
+static void
+text_entry_commit_and_reset(struct text_entry *entry)
+{
+ char *commit = NULL;
+
+ if (entry->preedit.commit)
+ commit = strdup(entry->preedit.commit);
+
+ text_entry_reset_preedit(entry);
+ if (commit) {
+ text_entry_insert_at_cursor(entry, commit, 0, 0);
+ free(commit);
+ }
+
+ wl_text_input_reset(entry->text_input);
+ text_entry_update(entry);
+ entry->reset_serial = entry->serial;
+}
+
+static void
+text_entry_set_preedit(struct text_entry *entry,
+ const char *preedit_text,
+ int preedit_cursor)
+{
+ text_entry_reset_preedit(entry);
+
+ if (!preedit_text)
+ return;
+
+ entry->preedit.text = strdup(preedit_text);
+ entry->preedit.cursor = preedit_cursor;
+
+ text_entry_update_layout(entry);
+
+ widget_schedule_redraw(entry->widget);
+}
+
+static uint32_t
+text_entry_try_invoke_preedit_action(struct text_entry *entry,
+ int32_t x, int32_t y,
+ uint32_t button,
+ enum wl_pointer_button_state state)
+{
+ int index, trailing;
+ uint32_t cursor;
+ const char *text;
+
+ if (!entry->preedit.text)
+ return 0;
+
+ pango_layout_xy_to_index(entry->layout,
+ x * PANGO_SCALE, y * PANGO_SCALE,
+ &index, &trailing);
+
+ text = pango_layout_get_text(entry->layout);
+ cursor = g_utf8_offset_to_pointer(text + index, trailing) - text;
+
+ if (cursor < entry->cursor ||
+ cursor > entry->cursor + strlen(entry->preedit.text)) {
+ return 0;
+ }
+
+ if (state == WL_POINTER_BUTTON_STATE_RELEASED)
+ wl_text_input_invoke_action(entry->text_input,
+ button,
+ cursor - entry->cursor);
+
+ return 1;
+}
+
+static bool
+text_entry_has_preedit(struct text_entry *entry)
+{
+ return entry->preedit.text && (strlen(entry->preedit.text) > 0);
+}
+
+static void
+text_entry_set_cursor_position(struct text_entry *entry,
+ int32_t x, int32_t y,
+ bool move_anchor)
+{
+ int index, trailing;
+ const char *text;
+ uint32_t cursor;
+
+ pango_layout_xy_to_index(entry->layout,
+ x * PANGO_SCALE, y * PANGO_SCALE,
+ &index, &trailing);
+
+ text = pango_layout_get_text(entry->layout);
+
+ cursor = g_utf8_offset_to_pointer(text + index, trailing) - text;
+
+ if (move_anchor)
+ entry->anchor = cursor;
+
+ if (text_entry_has_preedit(entry)) {
+ text_entry_commit_and_reset(entry);
+
+ assert(!text_entry_has_preedit(entry));
+ }
+
+ if (entry->cursor == cursor)
+ return;
+
+ entry->cursor = cursor;
+
+ text_entry_update_layout(entry);
+
+ widget_schedule_redraw(entry->widget);
+
+ text_entry_update(entry);
+}
+
+static void
+text_entry_delete_text(struct text_entry *entry,
+ uint32_t index, uint32_t length)
+{
+ uint32_t l;
+
+ assert(index <= strlen(entry->text));
+ assert(index + length <= strlen(entry->text));
+ assert(index + length >= length);
+
+ l = strlen(entry->text + index + length);
+ memmove(entry->text + index,
+ entry->text + index + length,
+ l + 1);
+
+ if (entry->cursor > (index + length))
+ entry->cursor -= length;
+ else if (entry->cursor > index)
+ entry->cursor = index;
+
+ entry->anchor = entry->cursor;
+
+ text_entry_update_layout(entry);
+
+ widget_schedule_redraw(entry->widget);
+
+ text_entry_update(entry);
+}
+
+static void
+text_entry_delete_selected_text(struct text_entry *entry)
+{
+ uint32_t start_index = entry->anchor < entry->cursor ? entry->anchor : entry->cursor;
+ uint32_t end_index = entry->anchor < entry->cursor ? entry->cursor : entry->anchor;
+
+ if (entry->anchor == entry->cursor)
+ return;
+
+ text_entry_delete_text(entry, start_index, end_index - start_index);
+
+ entry->anchor = entry->cursor;
+}
+
+static void
+text_entry_get_cursor_rectangle(struct text_entry *entry, struct rectangle *rectangle)
+{
+ struct rectangle allocation;
+ PangoRectangle extents;
+ PangoRectangle cursor_pos;
+
+ widget_get_allocation(entry->widget, &allocation);
+
+ if (entry->preedit.text && entry->preedit.cursor < 0) {
+ rectangle->x = 0;
+ rectangle->y = 0;
+ rectangle->width = 0;
+ rectangle->height = 0;
+ return;
+ }
+
+
+ pango_layout_get_extents(entry->layout, &extents, NULL);
+ pango_layout_get_cursor_pos(entry->layout,
+ entry->cursor + entry->preedit.cursor,
+ &cursor_pos, NULL);
+
+ rectangle->x = allocation.x + (allocation.height / 2) + PANGO_PIXELS(cursor_pos.x);
+ rectangle->y = allocation.y + 10 + PANGO_PIXELS(cursor_pos.y);
+ rectangle->width = PANGO_PIXELS(cursor_pos.width);
+ rectangle->height = PANGO_PIXELS(cursor_pos.height);
+}
+
+static void
+text_entry_draw_cursor(struct text_entry *entry, cairo_t *cr)
+{
+ PangoRectangle extents;
+ PangoRectangle cursor_pos;
+
+ if (entry->preedit.text && entry->preedit.cursor < 0)
+ return;
+
+ pango_layout_get_extents(entry->layout, &extents, NULL);
+ pango_layout_get_cursor_pos(entry->layout,
+ entry->cursor + entry->preedit.cursor,
+ &cursor_pos, NULL);
+
+ cairo_set_line_width(cr, 1.0);
+ cairo_move_to(cr, PANGO_PIXELS(cursor_pos.x), PANGO_PIXELS(cursor_pos.y));
+ cairo_line_to(cr, PANGO_PIXELS(cursor_pos.x), PANGO_PIXELS(cursor_pos.y) + PANGO_PIXELS(cursor_pos.height));
+ cairo_stroke(cr);
+}
+
+static const int text_offset_left = 10;
+
+static void
+text_entry_redraw_handler(struct widget *widget, void *data)
+{
+ struct text_entry *entry = data;
+ cairo_surface_t *surface;
+ struct rectangle allocation;
+ cairo_t *cr;
+
+ surface = window_get_surface(entry->window);
+ widget_get_allocation(entry->widget, &allocation);
+
+ cr = cairo_create(surface);
+ cairo_rectangle(cr, allocation.x, allocation.y, allocation.width, allocation.height);
+ cairo_clip(cr);
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+
+ cairo_push_group(cr);
+ cairo_translate(cr, allocation.x, allocation.y);
+
+ cairo_set_source_rgba(cr, 1, 1, 1, 1);
+ cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
+ cairo_fill(cr);
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+
+ if (entry->active) {
+ cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
+ cairo_set_line_width (cr, 3);
+ cairo_set_source_rgba(cr, 0, 0, 1, 1.0);
+ cairo_stroke(cr);
+ }
+
+ cairo_set_source_rgba(cr, 0, 0, 0, 1);
+
+ cairo_translate(cr, text_offset_left, allocation.height / 2);
+
+ if (!entry->layout)
+ entry->layout = pango_cairo_create_layout(cr);
+ else
+ pango_cairo_update_layout(cr, entry->layout);
+
+ text_entry_update_layout(entry);
+
+ pango_cairo_show_layout(cr, entry->layout);
+
+ text_entry_draw_cursor(entry, cr);
+
+ cairo_pop_group_to_source(cr);
+ cairo_paint(cr);
+
+ cairo_destroy(cr);
+ cairo_surface_destroy(surface);
+}
+
+static int
+text_entry_motion_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ float x, float y, void *data)
+{
+ struct text_entry *entry = data;
+ struct rectangle allocation;
+
+ if (!entry->button_pressed) {
+ return CURSOR_IBEAM;
+ }
+
+ widget_get_allocation(entry->widget, &allocation);
+
+ text_entry_set_cursor_position(entry,
+ x - allocation.x - text_offset_left,
+ y - allocation.y - text_offset_left,
+ false);
+
+ return CURSOR_IBEAM;
+}
+
+static void
+text_entry_button_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ uint32_t button,
+ enum wl_pointer_button_state state, void *data)
+{
+ struct text_entry *entry = data;
+ struct rectangle allocation;
+ struct editor *editor;
+ int32_t x, y;
+ uint32_t result;
+
+ widget_get_allocation(entry->widget, &allocation);
+ input_get_position(input, &x, &y);
+
+ x -= allocation.x + text_offset_left;
+ y -= allocation.y + text_offset_left;
+
+ editor = window_get_user_data(entry->window);
+
+ if (button == BTN_LEFT) {
+ entry->button_pressed = (state == WL_POINTER_BUTTON_STATE_PRESSED);
+
+ if (state == WL_POINTER_BUTTON_STATE_PRESSED)
+ input_grab(input, entry->widget, button);
+ else
+ input_ungrab(input);
+ }
+
+ if (text_entry_has_preedit(entry)) {
+ result = text_entry_try_invoke_preedit_action(entry, x, y, button, state);
+
+ if (result)
+ return;
+ }
+
+ if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
+ struct wl_seat *seat = input_get_seat(input);
+
+ text_entry_activate(entry, seat);
+ editor->active_entry = entry;
+
+ text_entry_set_cursor_position(entry, x, y, true);
+ }
+}
+
+static void
+editor_button_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ uint32_t button,
+ enum wl_pointer_button_state state, void *data)
+{
+ struct editor *editor = data;
+
+ if (button != BTN_LEFT) {
+ return;
+ }
+
+ if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
+ struct wl_seat *seat = input_get_seat(input);
+
+ text_entry_deactivate(editor->entry, seat);
+ text_entry_deactivate(editor->editor, seat);
+ editor->active_entry = NULL;
+ }
+}
+
+static void
+key_handler(struct window *window,
+ struct input *input, uint32_t time,
+ uint32_t key, uint32_t sym, enum wl_keyboard_key_state state,
+ void *data)
+{
+ struct editor *editor = data;
+ struct text_entry *entry;
+ const char *new_char;
+ char text[16];
+
+ if (!editor->active_entry)
+ return;
+
+ entry = editor->active_entry;
+
+ if (state != WL_KEYBOARD_KEY_STATE_PRESSED)
+ return;
+
+ switch (sym) {
+ case XKB_KEY_BackSpace:
+ text_entry_commit_and_reset(entry);
+
+ new_char = utf8_prev_char(entry->text, entry->text + entry->cursor);
+ if (new_char != NULL)
+ text_entry_delete_text(entry,
+ new_char - entry->text,
+ (entry->text + entry->cursor) - new_char);
+ break;
+ case XKB_KEY_Delete:
+ text_entry_commit_and_reset(entry);
+
+ new_char = utf8_next_char(entry->text + entry->cursor);
+ if (new_char != NULL)
+ text_entry_delete_text(entry,
+ entry->cursor,
+ new_char - (entry->text + entry->cursor));
+ break;
+ case XKB_KEY_Left:
+ text_entry_commit_and_reset(entry);
+
+ new_char = utf8_prev_char(entry->text, entry->text + entry->cursor);
+ if (new_char != NULL) {
+ entry->cursor = new_char - entry->text;
+ if (!(input_get_modifiers(input) & MOD_SHIFT_MASK))
+ entry->anchor = entry->cursor;
+ widget_schedule_redraw(entry->widget);
+ }
+ break;
+ case XKB_KEY_Right:
+ text_entry_commit_and_reset(entry);
+
+ new_char = utf8_next_char(entry->text + entry->cursor);
+ if (new_char != NULL) {
+ entry->cursor = new_char - entry->text;
+ if (!(input_get_modifiers(input) & MOD_SHIFT_MASK))
+ entry->anchor = entry->cursor;
+ widget_schedule_redraw(entry->widget);
+ }
+ break;
+ case XKB_KEY_Escape:
+ break;
+ default:
+ if (xkb_keysym_to_utf8(sym, text, sizeof(text)) <= 0)
+ break;
+
+ text_entry_commit_and_reset(entry);
+
+ text_entry_insert_at_cursor(entry, text, 0, 0);
+ break;
+ }
+
+ widget_schedule_redraw(entry->widget);
+}
+
+static void
+global_handler(struct display *display, uint32_t name,
+ const char *interface, uint32_t version, void *data)
+{
+ struct editor *editor = data;
+
+ if (!strcmp(interface, "wl_text_input_manager")) {
+ editor->text_input_manager =
+ display_bind(display, name,
+ &wl_text_input_manager_interface, 1);
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct editor editor;
+ int i;
+ uint32_t click_to_show = 0;
+ const char *preferred_language = NULL;
+
+ for (i = 1; i < argc; i++) {
+ if (strcmp("--click-to-show", argv[i]) == 0)
+ click_to_show = 1;
+ else if (strcmp("--preferred-language", argv[i]) == 0) {
+ if (i + 1 < argc) {
+ preferred_language = argv[i + 1];
+ i++;
+ }
+ }
+ }
+
+ memset(&editor, 0, sizeof editor);
+
+#ifdef HAVE_PANGO
+ g_type_init();
+#endif
+
+ editor.display = display_create(&argc, argv);
+ if (editor.display == NULL) {
+ fprintf(stderr, "failed to create display: %m\n");
+ return -1;
+ }
+
+ display_set_user_data(editor.display, &editor);
+ display_set_global_handler(editor.display, global_handler);
+
+ editor.window = window_create(editor.display);
+ editor.widget = frame_create(editor.window, &editor);
+
+ editor.entry = text_entry_create(&editor, "Entry");
+ editor.entry->click_to_show = click_to_show;
+ if (preferred_language)
+ editor.entry->preferred_language = strdup(preferred_language);
+ editor.editor = text_entry_create(&editor, "Numeric");
+ editor.editor->content_purpose = WL_TEXT_INPUT_CONTENT_PURPOSE_NUMBER;
+ editor.editor->click_to_show = click_to_show;
+
+ window_set_title(editor.window, "Text Editor");
+ window_set_key_handler(editor.window, key_handler);
+ window_set_user_data(editor.window, &editor);
+
+ widget_set_redraw_handler(editor.widget, redraw_handler);
+ widget_set_resize_handler(editor.widget, resize_handler);
+ widget_set_button_handler(editor.widget, editor_button_handler);
+
+ window_schedule_resize(editor.window, 500, 400);
+
+ display_run(editor.display);
+
+ text_entry_destroy(editor.entry);
+ text_entry_destroy(editor.editor);
+
+ return 0;
+}
diff --git a/clients/eventdemo.c b/clients/eventdemo.c
new file mode 100644
index 00000000..05ad5dcb
--- /dev/null
+++ b/clients/eventdemo.c
@@ -0,0 +1,417 @@
+/*
+ * Copyright © 2011 Tim Wiederhake
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * 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.
+ */
+
+/**
+ * \file eventdemo.c
+ * \brief Demonstrate the use of Wayland's toytoolkit.
+ *
+ * Heavily commented demo program that can report all events that are
+ * dispatched to the window. For other functionality, eg. opengl/egl,
+ * drag and drop, etc. have a look at the other demos.
+ * \author Tim Wiederhake
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <cairo.h>
+
+#include "window.h"
+
+/** window title */
+static char *title = "EventDemo";
+
+/** window width */
+static int width = 500;
+
+/** window height */
+static int height = 400;
+
+/** set if window has no borders */
+static int noborder = 0;
+
+/** if non-zero, maximum window width */
+static int width_max = 0;
+
+/** if non-zero, maximum window height */
+static int height_max = 0;
+
+/** set to log redrawing */
+static int log_redraw = 0;
+
+/** set to log resizing */
+static int log_resize = 0;
+
+/** set to log keyboard focus */
+static int log_focus = 0;
+
+/** set to log key events */
+static int log_key = 0;
+
+/** set to log button events */
+static int log_button = 0;
+
+/** set to log axis events */
+static int log_axis = 0;
+
+/** set to log motion events */
+static int log_motion = 0;
+
+/**
+ * \struct eventdemo
+ * \brief Holds all data the program needs per window
+ *
+ * In this demo the struct holds the position of a
+ * red rectangle that is drawn in the window's area.
+ */
+struct eventdemo {
+ struct window *window;
+ struct widget *widget;
+ struct display *display;
+
+ int x, y, w, h;
+};
+
+/**
+ * \brief CALLBACK function, Wayland requests the window to redraw.
+ * \param widget widget to be redrawn
+ * \param data user data associated to the window
+ *
+ * Draws a red rectangle as demonstration of per-window data.
+ */
+static void
+redraw_handler(struct widget *widget, void *data)
+{
+ struct eventdemo *e = data;
+ cairo_surface_t *surface;
+ cairo_t *cr;
+ struct rectangle rect;
+
+ if (log_redraw)
+ printf("redraw\n");
+
+ widget_get_allocation(e->widget, &rect);
+ surface = window_get_surface(e->window);
+
+ cr = cairo_create(surface);
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+
+ cairo_rectangle(cr, rect.x, rect.y, rect.width, rect.height);
+ cairo_set_source_rgba(cr, 0, 0, 0, 0.8);
+ cairo_fill(cr);
+
+ cairo_rectangle(cr, e->x, e->y, e->w, e->h);
+ cairo_set_source_rgba(cr, 1.0, 0, 0, 1);
+ cairo_fill(cr);
+
+ cairo_destroy(cr);
+ cairo_surface_destroy(surface);
+}
+
+/**
+ * \brief CALLBACK function, Wayland requests the window to resize.
+ * \param widget widget to be resized
+ * \param width desired width
+ * \param height desired height
+ * \param data user data associated to the window
+ */
+
+static void
+resize_handler(struct widget *widget,
+ int32_t width, int32_t height, void *data)
+{
+ struct eventdemo *e = data;
+ if (log_resize)
+ printf("resize width: %d, height: %d\n", width, height);
+
+ /* if a maximum width is set, constrain to it */
+ if (width_max && width_max < width)
+ width = width_max;
+
+ /* if a maximum height is set, constrain to it */
+ if (height_max && height_max < height)
+ height = height_max;
+
+ /* set the new window dimensions */
+ widget_set_size(e->widget, width, height);
+}
+
+/**
+ * \brief CALLBACK function, Wayland informs about keyboard focus change
+ * \param window window
+ * \param device device that caused the focus change
+ * \param data user data associated to the window
+ */
+static void
+keyboard_focus_handler(struct window *window,
+ struct input *device, void *data)
+{
+ int32_t x, y;
+ struct eventdemo *e = data;
+
+ if(log_focus) {
+ if(device) {
+ input_get_position(device, &x, &y);
+ printf("focus x: %d, y: %d\n", x, y);
+ } else {
+ printf("focus lost\n");
+ }
+ }
+
+ window_schedule_redraw(e->window);
+}
+
+/**
+ * \brief CALLBACK function, Wayland informs about key event
+ * \param window window
+ * \param key keycode
+ * \param unicode associated character
+ * \param state pressed or released
+ * \param modifiers modifiers: ctrl, alt, meta etc.
+ * \param data user data associated to the window
+ */
+static void
+key_handler(struct window *window, struct input *input, uint32_t time,
+ uint32_t key, uint32_t unicode, enum wl_keyboard_key_state state,
+ void *data)
+{
+ uint32_t modifiers = input_get_modifiers(input);
+
+ if(!log_key)
+ return;
+
+ printf("key key: %d, unicode: %d, state: %s, modifiers: 0x%x\n",
+ key, unicode,
+ (state == WL_KEYBOARD_KEY_STATE_PRESSED) ? "pressed" :
+ "released",
+ modifiers);
+}
+
+/**
+ * \brief CALLBACK function, Wayland informs about button event
+ * \param widget widget
+ * \param input input device that caused the button event
+ * \param time time the event happened
+ * \param button button
+ * \param state pressed or released
+ * \param data user data associated to the window
+ */
+static void
+button_handler(struct widget *widget, struct input *input, uint32_t time,
+ uint32_t button, enum wl_pointer_button_state state, void *data)
+{
+ int32_t x, y;
+
+ if (!log_button)
+ return;
+
+ input_get_position(input, &x, &y);
+ printf("button time: %d, button: %d, state: %s, x: %d, y: %d\n",
+ time, button,
+ (state == WL_POINTER_BUTTON_STATE_PRESSED) ? "pressed" :
+ "released",
+ x, y);
+}
+
+/**
+ * \brief CALLBACK function, Wayland informs about axis event
+ * \param widget widget
+ * \param input input device that caused the axis event
+ * \param time time the event happened
+ * \param axis vertical or horizontal
+ * \param value amount of scrolling
+ * \param data user data associated to the widget
+ */
+static void
+axis_handler(struct widget *widget, struct input *input, uint32_t time,
+ uint32_t axis, wl_fixed_t value, void *data)
+{
+ if (!log_axis)
+ return;
+
+ printf("axis time: %d, axis: %s, value: %f\n",
+ time,
+ axis == WL_POINTER_AXIS_VERTICAL_SCROLL ? "vertical" :
+ "horizontal",
+ wl_fixed_to_double(value));
+}
+
+/**
+ * \brief CALLBACK function, Waylands informs about pointer motion
+ * \param widget widget
+ * \param input input device that caused the motion event
+ * \param time time the event happened
+ * \param x absolute x position
+ * \param y absolute y position
+ * \param sx x position relative to the window
+ * \param sy y position relative to the window
+ * \param data user data associated to the window
+ *
+ * Demonstrates the use of different cursors
+ */
+static int
+motion_handler(struct widget *widget, struct input *input, uint32_t time,
+ float x, float y, void *data)
+{
+ struct eventdemo *e = data;
+
+ if (log_motion) {
+ printf("motion time: %d, x: %f, y: %f\n", time, x, y);
+ }
+
+ if (x > e->x && x < e->x + e->w)
+ if (y > e->y && y < e->y + e->h)
+ return CURSOR_HAND1;
+
+ return CURSOR_LEFT_PTR;
+}
+
+/**
+ * \brief Create and initialise a new eventdemo window.
+ * The returned eventdemo instance should be destroyed using \c eventdemo_destroy().
+ * \param d associated display
+ */
+static struct eventdemo *
+eventdemo_create(struct display *d)
+{
+ struct eventdemo *e;
+
+ e = malloc(sizeof (struct eventdemo));
+ if(e == NULL)
+ return NULL;
+
+ e->window = window_create(d);
+
+ if (noborder) {
+ /* Demonstrate how to create a borderless window.
+ * Move windows with META + left mouse button.
+ */
+ e->widget = window_add_widget(e->window, e);
+ } else {
+ e->widget = frame_create(e->window, e);
+ window_set_title(e->window, title);
+ }
+ e->display = d;
+
+ /* The eventdemo window draws a red rectangle as a demonstration
+ * of per-window data. The dimensions of that rectangle are set
+ * here.
+ */
+ e->x = width * 1.0 / 4.0;
+ e->w = width * 2.0 / 4.0;
+ e->y = height * 1.0 / 4.0;
+ e->h = height * 2.0 / 4.0;
+
+ /* Connect the user data to the window */
+ window_set_user_data(e->window, e);
+
+ /* Set the callback redraw handler for the window */
+ widget_set_redraw_handler(e->widget, redraw_handler);
+
+ /* Set the callback resize handler for the window */
+ widget_set_resize_handler(e->widget, resize_handler);
+
+ /* Set the callback focus handler for the window */
+ window_set_keyboard_focus_handler(e->window,
+ keyboard_focus_handler);
+
+ /* Set the callback key handler for the window */
+ window_set_key_handler(e->window, key_handler);
+
+ /* Set the callback button handler for the window */
+ widget_set_button_handler(e->widget, button_handler);
+
+ /* Set the callback motion handler for the window */
+ widget_set_motion_handler(e->widget, motion_handler);
+
+ /* Set the callback axis handler for the window */
+ widget_set_axis_handler(e->widget, axis_handler);
+
+ /* Initial drawing of the window */
+ window_schedule_resize(e->window, width, height);
+
+ return e;
+}
+/**
+ * \brief Destroy eventdemo instance previously created by \c eventdemo_create().
+ * \param eventdemo eventdemo instance to destroy
+ */
+static void eventdemo_destroy(struct eventdemo * eventdemo)
+{
+ widget_destroy(eventdemo->widget);
+ window_destroy(eventdemo->window);
+ free(eventdemo);
+}
+/**
+ * \brief command line options for eventdemo
+ */
+static const struct weston_option eventdemo_options[] = {
+ { WESTON_OPTION_STRING, "title", 0, &title },
+ { WESTON_OPTION_INTEGER, "width", 'w', &width },
+ { WESTON_OPTION_INTEGER, "height", 'h', &height },
+ { WESTON_OPTION_INTEGER, "max-width", 0, &width_max },
+ { WESTON_OPTION_INTEGER, "max-height", 0, &height_max },
+ { WESTON_OPTION_BOOLEAN, "no-border", 'b', &noborder },
+ { WESTON_OPTION_BOOLEAN, "log-redraw", '0', &log_redraw },
+ { WESTON_OPTION_BOOLEAN, "log-resize", '0', &log_resize },
+ { WESTON_OPTION_BOOLEAN, "log-focus", '0', &log_focus },
+ { WESTON_OPTION_BOOLEAN, "log-key", '0', &log_key },
+ { WESTON_OPTION_BOOLEAN, "log-button", '0', &log_button },
+ { WESTON_OPTION_BOOLEAN, "log-axis", '0', &log_axis },
+ { WESTON_OPTION_BOOLEAN, "log-motion", '0', &log_motion },
+};
+
+/**
+ * \brief Connects to the display, creates the window and hands over
+ * to the main loop.
+ */
+int
+main(int argc, char *argv[])
+{
+ struct display *d;
+ struct eventdemo *e;
+
+ parse_options(eventdemo_options,
+ ARRAY_LENGTH(eventdemo_options), &argc, argv);
+
+ /* Connect to the display and have the arguments parsed */
+ d = display_create(&argc, argv);
+ if (d == NULL) {
+ fprintf(stderr, "failed to create display: %m\n");
+ return -1;
+ }
+
+ /* Create new eventdemo window */
+ e = eventdemo_create(d);
+ if (e == NULL) {
+ fprintf(stderr, "failed to create eventdemo: %m\n");
+ return -1;
+ }
+
+ display_run(d);
+
+ /* Release resources */
+ eventdemo_destroy(e);
+ display_destroy(d);
+
+ return 0;
+}
diff --git a/clients/flower.c b/clients/flower.c
new file mode 100644
index 00000000..825c833e
--- /dev/null
+++ b/clients/flower.c
@@ -0,0 +1,198 @@
+/*
+ * Copyright © 2008 Kristian Høgsberg
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * 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.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <math.h>
+#include <cairo.h>
+#include <sys/time.h>
+
+#include <linux/input.h>
+#include <wayland-client.h>
+#include "window.h"
+
+struct flower {
+ struct display *display;
+ struct window *window;
+ struct widget *widget;
+ int width, height;
+};
+
+static void
+set_random_color(cairo_t *cr)
+{
+ cairo_set_source_rgba(cr,
+ 0.5 + (random() % 50) / 49.0,
+ 0.5 + (random() % 50) / 49.0,
+ 0.5 + (random() % 50) / 49.0,
+ 0.5 + (random() % 100) / 99.0);
+}
+
+
+static void
+draw_stuff(cairo_surface_t *surface, int width, int height)
+{
+ const int petal_count = 3 + random() % 5;
+ const double r1 = 60 + random() % 35;
+ const double r2 = 20 + random() % 40;
+ const double u = (10 + random() % 90) / 100.0;
+ const double v = (random() % 90) / 100.0;
+
+ cairo_t *cr;
+ int i;
+ double t, dt = 2 * M_PI / (petal_count * 2);
+ double x1, y1, x2, y2, x3, y3;
+
+ cr = cairo_create(surface);
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ cairo_set_source_rgba(cr, 0, 0, 0, 0);
+ cairo_paint(cr);
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+ cairo_translate(cr, width / 2, height / 2);
+ cairo_move_to(cr, cos(0) * r1, sin(0) * r1);
+ for (t = 0, i = 0; i < petal_count; i++, t += dt * 2) {
+ x1 = cos(t) * r1;
+ y1 = sin(t) * r1;
+ x2 = cos(t + dt) * r2;
+ y2 = sin(t + dt) * r2;
+ x3 = cos(t + 2 * dt) * r1;
+ y3 = sin(t + 2 * dt) * r1;
+
+ cairo_curve_to(cr,
+ x1 - y1 * u, y1 + x1 * u,
+ x2 + y2 * v, y2 - x2 * v,
+ x2, y2);
+
+ cairo_curve_to(cr,
+ x2 - y2 * v, y2 + x2 * v,
+ x3 + y3 * u, y3 - x3 * u,
+ x3, y3);
+ }
+
+ cairo_close_path(cr);
+ set_random_color(cr);
+ cairo_fill_preserve(cr);
+ set_random_color(cr);
+ cairo_stroke(cr);
+
+ cairo_destroy(cr);
+}
+
+static void
+resize_handler(struct widget *widget,
+ int32_t width, int32_t height, void *data)
+{
+ struct flower *flower = data;
+
+ /* Dont resize me */
+ widget_set_size(flower->widget, flower->width, flower->height);
+}
+
+static void
+redraw_handler(struct widget *widget, void *data)
+{
+ struct flower *flower = data;
+ cairo_surface_t *surface;
+
+ surface = window_get_surface(flower->window);
+ if (surface == NULL ||
+ cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
+ fprintf(stderr, "failed to create cairo egl surface\n");
+ return;
+ }
+
+ draw_stuff(surface, flower->width, flower->height);
+ cairo_surface_destroy(surface);
+}
+
+static void
+button_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ uint32_t button, enum wl_pointer_button_state state, void *data)
+{
+ struct flower *flower = data;
+
+ switch (button) {
+ case BTN_LEFT:
+ if (state == WL_POINTER_BUTTON_STATE_PRESSED)
+ window_move(flower->window, input,
+ display_get_serial(flower->display));
+ break;
+ case BTN_MIDDLE:
+ if (state == WL_POINTER_BUTTON_STATE_PRESSED)
+ widget_schedule_redraw(widget);
+ break;
+ case BTN_RIGHT:
+ if (state == WL_POINTER_BUTTON_STATE_PRESSED)
+ window_show_frame_menu(flower->window, input, time);
+ break;
+ }
+}
+
+static void
+touch_down_handler(struct widget *widget, struct input *input,
+ uint32_t serial, uint32_t time, int32_t id,
+ float x, float y, void *data)
+{
+ struct flower *flower = data;
+ window_touch_move(flower->window, input,
+ display_get_serial(flower->display));
+}
+
+int main(int argc, char *argv[])
+{
+ struct flower flower;
+ struct display *d;
+ struct timeval tv;
+
+ d = display_create(&argc, argv);
+ if (d == NULL) {
+ fprintf(stderr, "failed to create display: %m\n");
+ return -1;
+ }
+
+ gettimeofday(&tv, NULL);
+ srandom(tv.tv_usec);
+
+ flower.width = 200;
+ flower.height = 200;
+ flower.display = d;
+ flower.window = window_create(d);
+ flower.widget = window_add_widget(flower.window, &flower);
+ window_set_title(flower.window, "Flower");
+
+ widget_set_resize_handler(flower.widget, resize_handler);
+ widget_set_redraw_handler(flower.widget, redraw_handler);
+ widget_set_button_handler(flower.widget, button_handler);
+ widget_set_default_cursor(flower.widget, CURSOR_HAND1);
+ widget_set_touch_down_handler(flower.widget, touch_down_handler);
+
+ window_schedule_resize(flower.window, flower.width, flower.height);
+
+ display_run(d);
+
+ return 0;
+}
diff --git a/clients/fullscreen.c b/clients/fullscreen.c
new file mode 100644
index 00000000..72e2c81f
--- /dev/null
+++ b/clients/fullscreen.c
@@ -0,0 +1,368 @@
+/*
+ * Copyright © 2008 Kristian Høgsberg
+ * Copyright © 2012 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
+ * 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.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <math.h>
+#include <cairo.h>
+
+#include <linux/input.h>
+#include <wayland-client.h>
+#include "window.h"
+
+struct fullscreen {
+ struct display *display;
+ struct window *window;
+ struct widget *widget;
+ int width, height;
+ int fullscreen;
+ float pointer_x, pointer_y;
+ enum wl_shell_surface_fullscreen_method fullscreen_method;
+};
+
+static void
+fullscreen_handler(struct window *window, void *data)
+{
+ struct fullscreen *fullscreen = data;
+
+ fullscreen->fullscreen ^= 1;
+ window_set_fullscreen(window, fullscreen->fullscreen);
+}
+
+static void
+resize_handler(struct widget *widget, int width, int height, void *data)
+{
+ struct fullscreen *fullscreen = data;
+
+ widget_set_size(widget, fullscreen->width, fullscreen->height);
+}
+
+static void
+draw_string(cairo_t *cr,
+ const char *fmt, ...)
+{
+ char buffer[4096];
+ char *p, *end;
+ va_list argp;
+ cairo_text_extents_t text_extents;
+ cairo_font_extents_t font_extents;
+
+ cairo_save(cr);
+
+ cairo_select_font_face(cr, "sans",
+ CAIRO_FONT_SLANT_NORMAL,
+ CAIRO_FONT_WEIGHT_NORMAL);
+ cairo_set_font_size(cr, 14);
+
+ cairo_font_extents (cr, &font_extents);
+
+ va_start(argp, fmt);
+
+ vsnprintf(buffer, sizeof(buffer), fmt, argp);
+
+ p = buffer;
+ while (*p) {
+ end = strchr(p, '\n');
+ if (end)
+ *end = 0;
+
+ cairo_show_text(cr, p);
+ cairo_text_extents (cr, p, &text_extents);
+ cairo_rel_move_to (cr, -text_extents.x_advance, font_extents.height);
+
+ if (end)
+ p = end + 1;
+ else
+ break;
+ }
+
+ va_end(argp);
+
+ cairo_restore(cr);
+
+}
+
+static void
+redraw_handler(struct widget *widget, void *data)
+{
+ struct fullscreen *fullscreen = data;
+ struct rectangle allocation;
+ cairo_surface_t *surface;
+ cairo_t *cr;
+ int i;
+ double x, y, border;
+ const char *method_name[] = { "default", "scale", "driver", "fill" };
+
+ surface = window_get_surface(fullscreen->window);
+ if (surface == NULL ||
+ cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
+ fprintf(stderr, "failed to create cairo egl surface\n");
+ return;
+ }
+
+ widget_get_allocation(fullscreen->widget, &allocation);
+
+ cr = widget_cairo_create(widget);
+
+ cairo_set_source_rgb(cr, 0, 0, 0);
+ cairo_paint (cr);
+
+ cairo_set_source_rgb(cr, 0, 0, 1);
+ cairo_set_line_width (cr, 10);
+ cairo_rectangle(cr, 5, 5, allocation.width - 10, allocation.height - 10);
+ cairo_stroke (cr);
+
+ cairo_move_to(cr,
+ allocation.x + 15,
+ allocation.y + 25);
+ cairo_set_source_rgb(cr, 1, 1, 1);
+
+ draw_string(cr,
+ "Surface size: %d, %d\n"
+ "Scale: %d, transform: %d\n"
+ "Pointer: %f,%f\n"
+ "Fullscreen: %d, method: %s\n"
+ "Keys: (s)cale, (t)ransform, si(z)e, (m)ethod, (f)ullscreen, (q)uit\n",
+ fullscreen->width, fullscreen->height,
+ window_get_buffer_scale (fullscreen->window),
+ window_get_buffer_transform (fullscreen->window),
+ fullscreen->pointer_x, fullscreen->pointer_y,
+ fullscreen->fullscreen, method_name[fullscreen->fullscreen_method]);
+
+ y = 100;
+ i = 0;
+ while (y + 60 < fullscreen->height) {
+ border = (i++ % 2 == 0) ? 1 : 0.5;
+
+ x = 50;
+ cairo_set_line_width (cr, border);
+ while (x + 70 < fullscreen->width) {
+ if (fullscreen->pointer_x >= x && fullscreen->pointer_x < x + 50 &&
+ fullscreen->pointer_y >= y && fullscreen->pointer_y < y + 40) {
+ cairo_set_source_rgb(cr, 1, 0, 0);
+ cairo_rectangle(cr,
+ x, y,
+ 50, 40);
+ cairo_fill(cr);
+ }
+ cairo_set_source_rgb(cr, 0, 1, 0);
+ cairo_rectangle(cr,
+ x + border/2.0, y + border/2.0,
+ 50, 40);
+ cairo_stroke(cr);
+ x += 60;
+ }
+
+ y += 50;
+ }
+
+ cairo_destroy(cr);
+}
+
+static void
+key_handler(struct window *window, struct input *input, uint32_t time,
+ uint32_t key, uint32_t sym, enum wl_keyboard_key_state state,
+ void *data)
+{
+ struct fullscreen *fullscreen = data;
+ int transform, scale;
+ static int current_size = 0;
+ int widths[] = { 640, 320, 800, 400 };
+ int heights[] = { 480, 240, 600, 300 };
+
+ if (state == WL_KEYBOARD_KEY_STATE_RELEASED)
+ return;
+
+ switch (sym) {
+ case XKB_KEY_t:
+ transform = window_get_buffer_transform (window);
+ transform = (transform + 1) % 8;
+ window_set_buffer_transform(window, transform);
+ window_schedule_redraw(window);
+ break;
+
+ case XKB_KEY_s:
+ scale = window_get_buffer_scale (window);
+ if (scale == 1)
+ scale = 2;
+ else
+ scale = 1;
+ window_set_buffer_scale(window, scale);
+ window_schedule_redraw(window);
+ break;
+
+ case XKB_KEY_z:
+ current_size = (current_size + 1) % 4;
+ fullscreen->width = widths[current_size];
+ fullscreen->height = heights[current_size];
+ window_schedule_resize(fullscreen->window,
+ fullscreen->width, fullscreen->height);
+ break;
+
+ case XKB_KEY_m:
+ fullscreen->fullscreen_method = (fullscreen->fullscreen_method + 1) % 4;
+ window_set_fullscreen_method(fullscreen->window,
+ fullscreen->fullscreen_method);
+ window_schedule_redraw(window);
+ break;
+
+ case XKB_KEY_f:
+ fullscreen->fullscreen ^= 1;
+ window_set_fullscreen(window, fullscreen->fullscreen);
+ break;
+
+ case XKB_KEY_q:
+ exit (0);
+ break;
+ }
+}
+
+static int
+motion_handler(struct widget *widget,
+ struct input *input,
+ uint32_t time,
+ float x,
+ float y, void *data)
+{
+ struct fullscreen *fullscreen = data;
+
+ fullscreen->pointer_x = x;
+ fullscreen->pointer_y = y;
+
+ widget_schedule_redraw(widget);
+ return 0;
+}
+
+
+static void
+button_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ uint32_t button, enum wl_pointer_button_state state, void *data)
+{
+ struct fullscreen *fullscreen = data;
+
+ switch (button) {
+ case BTN_LEFT:
+ if (state == WL_POINTER_BUTTON_STATE_PRESSED)
+ window_move(fullscreen->window, input,
+ display_get_serial(fullscreen->display));
+ break;
+ case BTN_RIGHT:
+ if (state == WL_POINTER_BUTTON_STATE_PRESSED)
+ window_show_frame_menu(fullscreen->window, input, time);
+ break;
+ }
+}
+
+static void
+touch_handler(struct widget *widget, struct input *input,
+ uint32_t serial, uint32_t time, int32_t id,
+ float x, float y, void *data)
+{
+ struct fullscreen *fullscreen = data;
+ window_touch_move(fullscreen->window, input,
+ display_get_serial(fullscreen->display));
+}
+
+static void
+usage(int error_code)
+{
+ fprintf(stderr, "Usage: fullscreen [OPTIONS]\n\n"
+ " -w <width>\tSet window width to <width>\n"
+ " -h <height>\tSet window height to <height>\n"
+ " --help\tShow this help text\n\n");
+
+ exit(error_code);
+}
+
+int main(int argc, char *argv[])
+{
+ struct fullscreen fullscreen;
+ struct display *d;
+ int i;
+
+ fullscreen.width = 640;
+ fullscreen.height = 480;
+ fullscreen.fullscreen = 0;
+ fullscreen.fullscreen_method =
+ WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT;
+
+ for (i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-w") == 0) {
+ if (++i >= argc)
+ usage(EXIT_FAILURE);
+
+ fullscreen.width = atol(argv[i]);
+ } else if (strcmp(argv[i], "-h") == 0) {
+ if (++i >= argc)
+ usage(EXIT_FAILURE);
+
+ fullscreen.height = atol(argv[i]);
+ } else if (strcmp(argv[i], "--help") == 0)
+ usage(EXIT_SUCCESS);
+ else
+ usage(EXIT_FAILURE);
+ }
+
+ d = display_create(&argc, argv);
+ if (d == NULL) {
+ fprintf(stderr, "failed to create display: %m\n");
+ return -1;
+ }
+
+ fullscreen.display = d;
+ fullscreen.window = window_create(d);
+ fullscreen.widget =
+ window_add_widget(fullscreen.window, &fullscreen);
+
+ window_set_title(fullscreen.window, "Fullscreen");
+ window_set_fullscreen_method(fullscreen.window,
+ fullscreen.fullscreen_method);
+
+ widget_set_transparent(fullscreen.widget, 0);
+ widget_set_default_cursor(fullscreen.widget, CURSOR_LEFT_PTR);
+
+ widget_set_resize_handler(fullscreen.widget, resize_handler);
+ widget_set_redraw_handler(fullscreen.widget, redraw_handler);
+ widget_set_button_handler(fullscreen.widget, button_handler);
+ widget_set_motion_handler(fullscreen.widget, motion_handler);
+
+ widget_set_touch_down_handler(fullscreen.widget, touch_handler);
+
+ window_set_key_handler(fullscreen.window, key_handler);
+ window_set_fullscreen_handler(fullscreen.window, fullscreen_handler);
+
+ window_set_user_data(fullscreen.window, &fullscreen);
+ /* Hack to set minimum allocation so we can shrink later */
+ window_schedule_resize(fullscreen.window,
+ 1, 1);
+ window_schedule_resize(fullscreen.window,
+ fullscreen.width, fullscreen.height);
+
+ display_run(d);
+
+ return 0;
+}
diff --git a/clients/gears.c b/clients/gears.c
new file mode 100644
index 00000000..30f4e688
--- /dev/null
+++ b/clients/gears.c
@@ -0,0 +1,485 @@
+/*
+ * Copyright © 2008 Kristian Høgsberg
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * 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.
+ */
+
+#include "config.h"
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <time.h>
+
+#include <GL/gl.h>
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include <linux/input.h>
+#include <wayland-client.h>
+
+#include "window.h"
+
+struct gears {
+ struct window *window;
+ struct widget *widget;
+
+ struct display *d;
+
+ EGLDisplay display;
+ EGLDisplay config;
+ EGLContext context;
+ GLfloat angle;
+
+ struct {
+ GLfloat rotx;
+ GLfloat roty;
+ } view;
+
+ int button_down;
+ int last_x, last_y;
+
+ GLint gear_list[3];
+ int fullscreen;
+ int frames;
+ uint32_t last_fps;
+};
+
+struct gear_template {
+ GLfloat material[4];
+ GLfloat inner_radius;
+ GLfloat outer_radius;
+ GLfloat width;
+ GLint teeth;
+ GLfloat tooth_depth;
+};
+
+static const struct gear_template gear_templates[] = {
+ { { 0.8, 0.1, 0.0, 1.0 }, 1.0, 4.0, 1.0, 20, 0.7 },
+ { { 0.0, 0.8, 0.2, 1.0 }, 0.5, 2.0, 2.0, 10, 0.7 },
+ { { 0.2, 0.2, 1.0, 1.0 }, 1.3, 2.0, 0.5, 10, 0.7 },
+};
+
+static GLfloat light_pos[4] = {5.0, 5.0, 10.0, 0.0};
+
+static void die(const char *msg)
+{
+ fprintf(stderr, "%s", msg);
+ exit(EXIT_FAILURE);
+}
+
+static void
+make_gear(const struct gear_template *t)
+{
+ GLint i;
+ GLfloat r0, r1, r2;
+ GLfloat angle, da;
+ GLfloat u, v, len;
+
+ glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, t->material);
+
+ r0 = t->inner_radius;
+ r1 = t->outer_radius - t->tooth_depth / 2.0;
+ r2 = t->outer_radius + t->tooth_depth / 2.0;
+
+ da = 2.0 * M_PI / t->teeth / 4.0;
+
+ glShadeModel(GL_FLAT);
+
+ glNormal3f(0.0, 0.0, 1.0);
+
+ /* draw front face */
+ glBegin(GL_QUAD_STRIP);
+ for (i = 0; i <= t->teeth; i++) {
+ angle = i * 2.0 * M_PI / t->teeth;
+ glVertex3f(r0 * cos(angle), r0 * sin(angle), t->width * 0.5);
+ glVertex3f(r1 * cos(angle), r1 * sin(angle), t->width * 0.5);
+ if (i < t->teeth) {
+ glVertex3f(r0 * cos(angle), r0 * sin(angle), t->width * 0.5);
+ glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), t->width * 0.5);
+ }
+ }
+ glEnd();
+
+ /* draw front sides of teeth */
+ glBegin(GL_QUADS);
+ da = 2.0 * M_PI / t->teeth / 4.0;
+ for (i = 0; i < t->teeth; i++) {
+ angle = i * 2.0 * M_PI / t->teeth;
+
+ glVertex3f(r1 * cos(angle), r1 * sin(angle), t->width * 0.5);
+ glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), t->width * 0.5);
+ glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), t->width * 0.5);
+ glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), t->width * 0.5);
+ }
+ glEnd();
+
+ glNormal3f(0.0, 0.0, -1.0);
+
+ /* draw back face */
+ glBegin(GL_QUAD_STRIP);
+ for (i = 0; i <= t->teeth; i++) {
+ angle = i * 2.0 * M_PI / t->teeth;
+ glVertex3f(r1 * cos(angle), r1 * sin(angle), -t->width * 0.5);
+ glVertex3f(r0 * cos(angle), r0 * sin(angle), -t->width * 0.5);
+ if (i < t->teeth) {
+ glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -t->width * 0.5);
+ glVertex3f(r0 * cos(angle), r0 * sin(angle), -t->width * 0.5);
+ }
+ }
+ glEnd();
+
+ /* draw back sides of teeth */
+ glBegin(GL_QUADS);
+ da = 2.0 * M_PI / t->teeth / 4.0;
+ for (i = 0; i < t->teeth; i++) {
+ angle = i * 2.0 * M_PI / t->teeth;
+
+ glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -t->width * 0.5);
+ glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), -t->width * 0.5);
+ glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -t->width * 0.5);
+ glVertex3f(r1 * cos(angle), r1 * sin(angle), -t->width * 0.5);
+ }
+ glEnd();
+
+ /* draw outward faces of teeth */
+ glBegin(GL_QUAD_STRIP);
+ for (i = 0; i < t->teeth; i++) {
+ angle = i * 2.0 * M_PI / t->teeth;
+
+ glVertex3f(r1 * cos(angle), r1 * sin(angle), t->width * 0.5);
+ glVertex3f(r1 * cos(angle), r1 * sin(angle), -t->width * 0.5);
+ u = r2 * cos(angle + da) - r1 * cos(angle);
+ v = r2 * sin(angle + da) - r1 * sin(angle);
+ len = sqrt(u * u + v * v);
+ u /= len;
+ v /= len;
+ glNormal3f(v, -u, 0.0);
+ glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), t->width * 0.5);
+ glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -t->width * 0.5);
+ glNormal3f(cos(angle), sin(angle), 0.0);
+ glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), t->width * 0.5);
+ glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), -t->width * 0.5);
+ u = r1 * cos(angle + 3 * da) - r2 * cos(angle + 2 * da);
+ v = r1 * sin(angle + 3 * da) - r2 * sin(angle + 2 * da);
+ glNormal3f(v, -u, 0.0);
+ glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), t->width * 0.5);
+ glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -t->width * 0.5);
+ glNormal3f(cos(angle), sin(angle), 0.0);
+ }
+
+ glVertex3f(r1 * cos(0), r1 * sin(0), t->width * 0.5);
+ glVertex3f(r1 * cos(0), r1 * sin(0), -t->width * 0.5);
+
+ glEnd();
+
+ glShadeModel(GL_SMOOTH);
+
+ /* draw inside radius cylinder */
+ glBegin(GL_QUAD_STRIP);
+ for (i = 0; i <= t->teeth; i++) {
+ angle = i * 2.0 * M_PI / t->teeth;
+ glNormal3f(-cos(angle), -sin(angle), 0.0);
+ glVertex3f(r0 * cos(angle), r0 * sin(angle), -t->width * 0.5);
+ glVertex3f(r0 * cos(angle), r0 * sin(angle), t->width * 0.5);
+ }
+ glEnd();
+}
+
+static void
+update_fps(struct gears *gears, uint32_t time)
+{
+ long diff_ms;
+
+ gears->frames++;
+
+ diff_ms = time - gears->last_fps;
+
+ if (diff_ms > 5000) {
+ float seconds = diff_ms / 1000.0;
+ float fps = gears->frames / seconds;
+
+ printf("%d frames in %6.3f seconds = %6.3f FPS\n", gears->frames, seconds, fps);
+ fflush(stdout);
+
+ gears->frames = 0;
+ gears->last_fps = time;
+ }
+}
+
+static void
+frame_callback(void *data, struct wl_callback *callback, uint32_t time)
+{
+ struct gears *gears = data;
+
+ update_fps(gears, time);
+
+ gears->angle = (GLfloat) (time % 8192) * 360 / 8192.0;
+
+ window_schedule_redraw(gears->window);
+
+ if (callback)
+ wl_callback_destroy(callback);
+}
+
+static const struct wl_callback_listener listener = {
+ frame_callback
+};
+
+static int
+motion_handler(struct widget *widget, struct input *input,
+ uint32_t time, float x, float y, void *data)
+{
+ struct gears *gears = data;
+ int offset_x, offset_y;
+ float step = 0.5;
+
+ if (gears->button_down) {
+ offset_x = x - gears->last_x;
+ offset_y = y - gears->last_y;
+ gears->last_x = x;
+ gears->last_y = y;
+ gears->view.roty += offset_x * step;
+ gears->view.rotx += offset_y * step;
+ if (gears->view.roty >= 360)
+ gears->view.roty = gears->view.roty - 360;
+ if (gears->view.roty <= 0)
+ gears->view.roty = gears->view.roty + 360;
+ if (gears->view.rotx >= 360)
+ gears->view.rotx = gears->view.rotx - 360;
+ if (gears->view.rotx <= 0)
+ gears->view.rotx = gears->view.rotx + 360;
+ }
+
+ return CURSOR_LEFT_PTR;
+}
+
+static void
+button_handler(struct widget *widget, struct input *input,
+ uint32_t time, uint32_t button,
+ enum wl_pointer_button_state state, void *data)
+{
+ struct gears *gears = data;
+
+ if (button == BTN_LEFT) {
+ if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
+ gears->button_down = 1;
+ input_get_position(input,
+ &gears->last_x, &gears->last_y);
+ } else {
+ gears->button_down = 0;
+ }
+ }
+}
+
+static void
+redraw_handler(struct widget *widget, void *data)
+{
+ struct rectangle window_allocation;
+ struct rectangle allocation;
+ struct wl_callback *callback;
+ struct gears *gears = data;
+
+ widget_get_allocation(gears->widget, &allocation);
+ window_get_allocation(gears->window, &window_allocation);
+
+ if (display_acquire_window_surface(gears->d,
+ gears->window,
+ gears->context) < 0) {
+ die("Unable to acquire window surface, "
+ "compiled without cairo-egl?\n");
+ }
+
+ glViewport(allocation.x,
+ window_allocation.height - allocation.height - allocation.y,
+ allocation.width, allocation.height);
+ glScissor(allocation.x,
+ window_allocation.height - allocation.height - allocation.y,
+ allocation.width, allocation.height);
+
+ glEnable(GL_SCISSOR_TEST);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ glPushMatrix();
+
+ glTranslatef(0.0, 0.0, -50);
+
+ glRotatef(gears->view.rotx, 1.0, 0.0, 0.0);
+ glRotatef(gears->view.roty, 0.0, 1.0, 0.0);
+
+ glPushMatrix();
+ glTranslatef(-3.0, -2.0, 0.0);
+ glRotatef(gears->angle, 0.0, 0.0, 1.0);
+ glCallList(gears->gear_list[0]);
+ glPopMatrix();
+
+ glPushMatrix();
+ glTranslatef(3.1, -2.0, 0.0);
+ glRotatef(-2.0 * gears->angle - 9.0, 0.0, 0.0, 1.0);
+ glCallList(gears->gear_list[1]);
+ glPopMatrix();
+
+ glPushMatrix();
+ glTranslatef(-3.1, 4.2, 0.0);
+ glRotatef(-2.0 * gears->angle - 25.0, 0.0, 0.0, 1.0);
+ glCallList(gears->gear_list[2]);
+ glPopMatrix();
+
+ glPopMatrix();
+
+ glFlush();
+
+ display_release_window_surface(gears->d, gears->window);
+
+ callback = wl_surface_frame(window_get_wl_surface(gears->window));
+ wl_callback_add_listener(callback, &listener, gears);
+}
+
+static void
+resize_handler(struct widget *widget,
+ int32_t width, int32_t height, void *data)
+{
+ struct gears *gears = data;
+ int32_t size, big, small;
+
+ /* Constrain child size to be square and at least 300x300 */
+ if (width < height) {
+ small = width;
+ big = height;
+ } else {
+ small = height;
+ big = width;
+ }
+
+ if (gears->fullscreen)
+ size = small;
+ else
+ size = big;
+
+ widget_set_size(gears->widget, size, size);
+}
+
+static void
+keyboard_focus_handler(struct window *window,
+ struct input *device, void *data)
+{
+ window_schedule_redraw(window);
+}
+
+static void
+fullscreen_handler(struct window *window, void *data)
+{
+ struct gears *gears = data;
+
+ gears->fullscreen ^= 1;
+ window_set_fullscreen(window, gears->fullscreen);
+}
+
+static struct gears *
+gears_create(struct display *display)
+{
+ const int width = 450, height = 500;
+ struct gears *gears;
+ struct timeval tv;
+ int i;
+
+ gears = zalloc(sizeof *gears);
+ gears->d = display;
+ gears->window = window_create(display);
+ gears->widget = frame_create(gears->window, gears);
+ window_set_title(gears->window, "Wayland Gears");
+
+ gears->display = display_get_egl_display(gears->d);
+ if (gears->display == NULL)
+ die("failed to create egl display\n");
+
+ eglBindAPI(EGL_OPENGL_API);
+
+ gears->config = display_get_argb_egl_config(gears->d);
+
+ gears->context = eglCreateContext(gears->display, gears->config,
+ EGL_NO_CONTEXT, NULL);
+ if (gears->context == NULL)
+ die("failed to create context\n");
+
+ if (!eglMakeCurrent(gears->display, NULL, NULL, gears->context))
+ die("failed to make context current\n");
+
+ for (i = 0; i < 3; i++) {
+ gears->gear_list[i] = glGenLists(1);
+ glNewList(gears->gear_list[i], GL_COMPILE);
+ make_gear(&gear_templates[i]);
+ glEndList();
+ }
+
+ gears->button_down = 0;
+ gears->last_x = 0;
+ gears->last_y = 0;
+
+ gears->view.rotx = 20.0;
+ gears->view.roty = 30.0;
+
+ gettimeofday(&tv, NULL);
+ gears->last_fps = tv.tv_sec * 1000 + tv.tv_usec / 1000;
+ printf("Warning: FPS count is limited by the wayland compositor or monitor refresh rate\n");
+
+ glEnable(GL_NORMALIZE);
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glFrustum(-1.0, 1.0, -1.0, 1.0, 5.0, 200.0);
+ glMatrixMode(GL_MODELVIEW);
+
+ glLightfv(GL_LIGHT0, GL_POSITION, light_pos);
+ glEnable(GL_CULL_FACE);
+ glEnable(GL_LIGHTING);
+ glEnable(GL_LIGHT0);
+ glEnable(GL_DEPTH_TEST);
+ glClearColor(0, 0, 0, 0.92);
+
+ window_set_user_data(gears->window, gears);
+ widget_set_resize_handler(gears->widget, resize_handler);
+ widget_set_redraw_handler(gears->widget, redraw_handler);
+ widget_set_button_handler(gears->widget, button_handler);
+ widget_set_motion_handler(gears->widget, motion_handler);
+ window_set_keyboard_focus_handler(gears->window,
+ keyboard_focus_handler);
+ window_set_fullscreen_handler(gears->window, fullscreen_handler);
+
+ window_schedule_resize(gears->window, width, height);
+
+ return gears;
+}
+
+int main(int argc, char *argv[])
+{
+ struct display *d;
+
+ d = display_create(&argc, argv);
+ if (d == NULL) {
+ fprintf(stderr, "failed to create display: %m\n");
+ return -1;
+ }
+ gears_create(d);
+ display_run(d);
+
+ return 0;
+}
diff --git a/clients/glmatrix.c b/clients/glmatrix.c
new file mode 100644
index 00000000..4c5754e9
--- /dev/null
+++ b/clients/glmatrix.c
@@ -0,0 +1,1062 @@
+/* glmatrix, Copyright (c) 2003, 2004 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * GLMatrix -- simulate the text scrolls from the movie "The Matrix".
+ *
+ * This program does a 3D rendering of the dropping characters that
+ * appeared in the title sequences of the movies. See also `xmatrix'
+ * for a simulation of what the computer monitors actually *in* the
+ * movie did.
+ */
+
+#define DEFAULTS "*delay: 30000 \n" \
+ "*showFPS: False \n" \
+ "*wireframe: False \n" \
+
+# define refresh_matrix 0
+# define release_matrix 0
+#undef countof
+#define countof(x) (sizeof((x))/sizeof((*x)))
+
+#undef BELLRAND
+#define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
+
+#include "wscreensaver-glue.h"
+
+#ifdef __GNUC__
+ __extension__ /* don't warn about "string length is greater than the length
+ ISO C89 compilers are required to support" when including
+ the following XPM file... */
+#endif
+#include "matrix3.xpm"
+
+
+#define DEF_SPEED "1.0"
+#define DEF_DENSITY "20"
+#define DEF_CLOCK "False"
+#define DEF_FOG "True"
+#define DEF_WAVES "True"
+#define DEF_ROTATE "True"
+#define DEF_TEXTURE "True"
+#define DEF_MODE "Matrix"
+#define DEF_TIMEFMT " %l%M%p "
+
+
+#define CHAR_COLS 16
+#define CHAR_ROWS 13
+
+static const int matrix_encoding[] = {
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+# if 0
+ 192, 193, 194, 195, 196, 197, 198, 199,
+ 200, 201, 202, 203, 204, 205, 206, 207
+# else
+ 160, 161, 162, 163, 164, 165, 166, 167,
+ 168, 169, 170, 171, 172, 173, 174, 175
+# endif
+ };
+static const int decimal_encoding[] = {
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 };
+static const int hex_encoding[] = {
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 33, 34, 35, 36, 37, 38 };
+static const int binary_encoding[] = { 16, 17 };
+static const int dna_encoding[] = { 33, 35, 39, 52 };
+
+static const unsigned char char_map[256] = {
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 0 */
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 16 */
+ 0, 1, 2, 96, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* 32 */
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, /* 48 */
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, /* 64 */
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, /* 80 */
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, /* 96 */
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, /* 112 */
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 128 */
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 144 */
+ 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111, /* 160 */
+ 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, /* 176 */
+ 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, /* 192 */
+ 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, /* 208 */
+#if 0
+ 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, /* 224 */
+ 176,177,178,195,180,181,182,183,184,185,186,187,188,189,190,191 /* 240 */
+#else /* see spank_image() */
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 224 */
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 240 */
+#endif
+};
+
+#define CURSOR_GLYPH 97
+
+/* #define DEBUG */
+
+#define GRID_SIZE 70 /* width and height of the arena */
+#define GRID_DEPTH 35 /* depth of the arena */
+#define WAVE_SIZE 22 /* periodicity of color (brightness) waves */
+#define SPLASH_RATIO 0.7 /* ratio of GRID_DEPTH where chars hit the screen */
+
+static const struct { GLfloat x, y; } nice_views[] = {
+ { 0, 0 },
+ { 0, -20 }, /* this is a list of viewer rotations that look nice. */
+ { 0, 20 }, /* every now and then we switch to a new one. */
+ { 25, 0 }, /* (but we only use the first one at start-up.) */
+ {-25, 0 },
+ { 25, 20 },
+ {-25, 20 },
+ { 25, -20 },
+ {-25, -20 },
+
+ { 10, 0 },
+ {-10, 0 },
+ { 0, 0 }, /* prefer these */
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+};
+
+
+typedef struct {
+ GLfloat x, y, z; /* position of strip */
+ GLfloat dx, dy, dz; /* velocity of strip */
+
+ Bool erasing_p; /* Whether this strip is on its way out. */
+
+ int spinner_glyph; /* the bottommost glyph -- the feeder */
+ GLfloat spinner_y; /* where on the strip the bottom glyph is */
+ GLfloat spinner_speed; /* how fast the bottom glyph drops */
+
+ int glyphs[GRID_SIZE]; /* the other glyphs on the strip, which will be
+ revealed by the dropping spinner.
+ 0 means no glyph; negative means "spinner".
+ If non-zero, real value is abs(G)-1. */
+
+ Bool highlight[GRID_SIZE];
+ /* some glyphs may be highlighted */
+
+ int spin_speed; /* Rotate all spinners every this-many frames */
+ int spin_tick; /* frame counter */
+
+ int wave_position; /* Waves of brightness wash down the strip. */
+ int wave_speed; /* every this-many frames. */
+ int wave_tick; /* frame counter. */
+
+} strip;
+
+
+typedef struct {
+ GLXContext *glx_context;
+ Bool button_down_p;
+ GLuint texture;
+ int nstrips;
+ strip *strips;
+ const int *glyph_map;
+ int nglyphs;
+ GLfloat tex_char_width, tex_char_height;
+
+ /* auto-tracking direction of view */
+ int last_view, target_view;
+ GLfloat view_x, view_y;
+ int view_steps, view_tick;
+ Bool auto_tracking_p;
+ int track_tick;
+
+ int real_char_rows;
+ GLfloat brightness_ramp[WAVE_SIZE];
+
+} matrix_configuration;
+
+static matrix_configuration *mps = NULL;
+
+static GLfloat speed = 1.0;
+static GLfloat density = 20.0;
+static Bool do_clock;
+static char *timefmt;
+static Bool do_fog = 1;
+static Bool do_waves;
+static Bool do_rotate = 1;
+static Bool do_texture = 1;
+static char *mode_str;
+
+#if 0
+static XrmOptionDescRec opts[] = {
+ { "-speed", ".speed", XrmoptionSepArg, 0 },
+ { "-density", ".density", XrmoptionSepArg, 0 },
+ { "-mode", ".mode", XrmoptionSepArg, 0 },
+ { "-binary", ".mode", XrmoptionNoArg, "binary" },
+ { "-hexadecimal", ".mode", XrmoptionNoArg, "hexadecimal" },
+ { "-decimal", ".mode", XrmoptionNoArg, "decimal" },
+ { "-dna", ".mode", XrmoptionNoArg, "dna" },
+ { "-clock", ".clock", XrmoptionNoArg, "True" },
+ { "+clock", ".clock", XrmoptionNoArg, "False" },
+ { "-timefmt", ".timefmt", XrmoptionSepArg, 0 },
+ { "-fog", ".fog", XrmoptionNoArg, "True" },
+ { "+fog", ".fog", XrmoptionNoArg, "False" },
+ { "-waves", ".waves", XrmoptionNoArg, "True" },
+ { "+waves", ".waves", XrmoptionNoArg, "False" },
+ { "-rotate", ".rotate", XrmoptionNoArg, "True" },
+ { "+rotate", ".rotate", XrmoptionNoArg, "False" },
+ {"-texture", ".texture", XrmoptionNoArg, "True" },
+ {"+texture", ".texture", XrmoptionNoArg, "False" },
+};
+
+static argtype vars[] = {
+ {&mode_str, "mode", "Mode", DEF_MODE, t_String},
+ {&speed, "speed", "Speed", DEF_SPEED, t_Float},
+ {&density, "density", "Density", DEF_DENSITY, t_Float},
+ {&do_clock, "clock", "Clock", DEF_CLOCK, t_Bool},
+ {&timefmt, "timefmt", "Timefmt", DEF_TIMEFMT, t_String},
+ {&do_fog, "fog", "Fog", DEF_FOG, t_Bool},
+ {&do_waves, "waves", "Waves", DEF_WAVES, t_Bool},
+ {&do_rotate, "rotate", "Rotate", DEF_ROTATE, t_Bool},
+ {&do_texture, "texture", "Texture", DEF_TEXTURE, t_Bool},
+};
+
+ENTRYPOINT ModeSpecOpt matrix_opts = {countof(opts), opts, countof(vars), vars, NULL};
+#endif
+
+/* Re-randomize the state of one strip.
+ */
+static void
+reset_strip (ModeInfo *mi, strip *s)
+{
+ matrix_configuration *mp = &mps[MI_SCREEN(mi)];
+ int i;
+ Bool time_displayed_p = False; /* never display time twice in one strip */
+
+ memset (s, 0, sizeof(*s));
+ s->x = (GLfloat) (frand(GRID_SIZE) - (GRID_SIZE/2));
+ s->y = (GLfloat) (GRID_SIZE/2 + BELLRAND(0.5)); /* shift top slightly */
+ s->z = (GLfloat) (GRID_DEPTH * 0.2) - frand (GRID_DEPTH * 0.7);
+ s->spinner_y = 0;
+
+ s->dx = 0;
+/* s->dx = ((BELLRAND(0.01) - 0.005) * speed); */
+ s->dy = 0;
+ s->dz = (BELLRAND(0.02) * speed);
+
+ s->spinner_speed = (BELLRAND(0.3) * speed);
+
+ s->spin_speed = (int) BELLRAND(2.0 / speed) + 1;
+ s->spin_tick = 0;
+
+ s->wave_position = 0;
+ s->wave_speed = (int) BELLRAND(3.0 / speed) + 1;
+ s->wave_tick = 0;
+
+ for (i = 0; i < GRID_SIZE; i++)
+ if (do_clock &&
+ !time_displayed_p &&
+ (i < GRID_SIZE-5) && /* display approx. once per 5 strips */
+ !(random() % (GRID_SIZE-5)*5))
+ {
+ unsigned int j;
+ char text[80];
+ time_t now = time ((time_t *) 0);
+ struct tm *tm = localtime (&now);
+ strftime (text, sizeof(text)-1, timefmt, tm);
+
+ /* render time into the strip */
+ for (j = 0; j < strlen(text) && i < GRID_SIZE; j++, i++)
+ {
+ s->glyphs[i] = char_map [((unsigned char *) text)[j]] + 1;
+ s->highlight[i] = True;
+ }
+
+ time_displayed_p = True;
+ }
+ else
+ {
+ int draw_p = (random() % 7);
+ int spin_p = (draw_p && !(random() % 20));
+ int g = (draw_p
+ ? mp->glyph_map[(random() % mp->nglyphs)] + 1
+ : 0);
+ if (spin_p) g = -g;
+ s->glyphs[i] = g;
+ s->highlight[i] = False;
+ }
+
+ s->spinner_glyph = - (mp->glyph_map[(random() % mp->nglyphs)] + 1);
+}
+
+
+/* Animate the strip one step. Reset if it has reached the bottom.
+ */
+static void
+tick_strip (ModeInfo *mi, strip *s)
+{
+ matrix_configuration *mp = &mps[MI_SCREEN(mi)];
+ int i;
+
+ if (mp->button_down_p)
+ return;
+
+ s->x += s->dx;
+ s->y += s->dy;
+ s->z += s->dz;
+
+ if (s->z > GRID_DEPTH * SPLASH_RATIO) /* splashed into screen */
+ {
+ reset_strip (mi, s);
+ return;
+ }
+
+ s->spinner_y += s->spinner_speed;
+ if (s->spinner_y >= GRID_SIZE)
+ {
+ if (s->erasing_p)
+ {
+ reset_strip (mi, s);
+ return;
+ }
+ else
+ {
+ s->erasing_p = True;
+ s->spinner_y = 0;
+ s->spinner_speed /= 2; /* erase it slower than we drew it */
+ }
+ }
+
+ /* Spin the spinners. */
+ s->spin_tick++;
+ if (s->spin_tick > s->spin_speed)
+ {
+ s->spin_tick = 0;
+ s->spinner_glyph = - (mp->glyph_map[(random() % mp->nglyphs)] + 1);
+ for (i = 0; i < GRID_SIZE; i++)
+ if (s->glyphs[i] < 0)
+ {
+ s->glyphs[i] = -(mp->glyph_map[(random() % mp->nglyphs)] + 1);
+ if (! (random() % 800)) /* sometimes they stop spinning */
+ s->glyphs[i] = -s->glyphs[i];
+ }
+ }
+
+ /* Move the color (brightness) wave. */
+ s->wave_tick++;
+ if (s->wave_tick > s->wave_speed)
+ {
+ s->wave_tick = 0;
+ s->wave_position++;
+ if (s->wave_position >= WAVE_SIZE)
+ s->wave_position = 0;
+ }
+}
+
+
+/* Draw a single character at the given position and brightness.
+ */
+static void
+draw_glyph (ModeInfo *mi, int glyph, Bool highlight,
+ GLfloat x, GLfloat y, GLfloat z,
+ GLfloat brightness)
+{
+ matrix_configuration *mp = &mps[MI_SCREEN(mi)];
+ int wire = MI_IS_WIREFRAME(mi);
+ GLfloat w = mp->tex_char_width;
+ GLfloat h = mp->tex_char_height;
+ GLfloat cx = 0, cy = 0;
+ GLfloat S = 1;
+ Bool spinner_p = (glyph < 0);
+
+ if (glyph == 0) abort();
+ if (glyph < 0) glyph = -glyph;
+
+ if (spinner_p)
+ brightness *= 1.5;
+
+ if (!do_texture)
+ {
+ S = 0.8;
+ x += 0.1;
+ y += 0.1;
+ }
+ else
+ {
+ int ccx = ((glyph - 1) % CHAR_COLS);
+ int ccy = ((glyph - 1) / CHAR_COLS);
+
+ cx = ccx * w;
+ cy = (mp->real_char_rows - ccy - 1) * h;
+
+ if (do_fog)
+ {
+ GLfloat depth;
+ depth = (z / GRID_DEPTH) + 0.5; /* z ratio from back/front */
+ depth = 0.2 + (depth * 0.8); /* scale to range [0.2 - 1.0] */
+ brightness *= depth; /* so no row goes all black. */
+ }
+ }
+
+ {
+ GLfloat r, g, b, a;
+
+ if (highlight)
+ brightness *= 2;
+
+ if (!do_texture && !spinner_p)
+ r = b = 0, g = 1;
+ else
+ r = g = b = 1;
+
+ a = brightness;
+
+ /* If the glyph is very close to the screen (meaning it is very large,
+ and is about to splash into the screen and vanish) then start fading
+ it out, proportional to how close to the glass it is.
+ */
+ if (z > GRID_DEPTH/2)
+ {
+ GLfloat ratio = ((z - GRID_DEPTH/2) /
+ ((GRID_DEPTH * SPLASH_RATIO) - GRID_DEPTH/2));
+ int i = ratio * WAVE_SIZE;
+
+ if (i < 0) i = 0;
+ else if (i >= WAVE_SIZE) i = WAVE_SIZE-1;
+
+ a *= mp->brightness_ramp[i];
+ }
+
+ glColor4f (r,g,b,a);
+ }
+
+ glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
+ glNormal3f (0, 0, 1);
+ glTexCoord2f (cx, cy); glVertex3f (x, y, z);
+ glTexCoord2f (cx+w, cy); glVertex3f (x+S, y, z);
+ glTexCoord2f (cx+w, cy+h); glVertex3f (x+S, y+S, z);
+ glTexCoord2f (cx, cy+h); glVertex3f (x, y+S, z);
+ glEnd ();
+
+ if (wire && spinner_p)
+ {
+ glBegin (GL_LINES);
+ glVertex3f (x, y, z);
+ glVertex3f (x+S, y+S, z);
+ glVertex3f (x, y+S, z);
+ glVertex3f (x+S, y, z);
+ glEnd();
+ }
+
+ mi->polygon_count++;
+}
+
+
+/* Draw all the visible glyphs in the strip.
+ */
+static void
+draw_strip (ModeInfo *mi, strip *s)
+{
+ matrix_configuration *mp = &mps[MI_SCREEN(mi)];
+ int i;
+ for (i = 0; i < GRID_SIZE; i++)
+ {
+ int g = s->glyphs[i];
+ Bool below_p = (s->spinner_y >= i);
+
+ if (s->erasing_p)
+ below_p = !below_p;
+
+ if (g && below_p) /* don't draw cells below the spinner */
+ {
+ GLfloat brightness;
+ if (!do_waves)
+ brightness = 1.0;
+ else
+ {
+ int j = WAVE_SIZE - ((i + (GRID_SIZE - s->wave_position))
+ % WAVE_SIZE);
+ brightness = mp->brightness_ramp[j];
+ }
+
+ draw_glyph (mi, g, s->highlight[i],
+ s->x, s->y - i, s->z, brightness);
+ }
+ }
+
+ if (!s->erasing_p)
+ draw_glyph (mi, s->spinner_glyph, False,
+ s->x, s->y - s->spinner_y, s->z, 1.0);
+}
+
+
+/* qsort comparator for sorting strips by z position */
+static int
+cmp_strips (const void *aa, const void *bb)
+{
+ const strip *a = *(strip **) aa;
+ const strip *b = *(strip **) bb;
+ return ((int) (a->z * 10000) -
+ (int) (b->z * 10000));
+}
+
+
+/* Auto-tracking
+ */
+
+static void
+auto_track_init (ModeInfo *mi)
+{
+ matrix_configuration *mp = &mps[MI_SCREEN(mi)];
+ mp->last_view = 0;
+ mp->target_view = 0;
+ mp->view_x = nice_views[mp->last_view].x;
+ mp->view_y = nice_views[mp->last_view].y;
+ mp->view_steps = 100;
+ mp->view_tick = 0;
+ mp->auto_tracking_p = False;
+}
+
+
+static void
+auto_track (ModeInfo *mi)
+{
+ matrix_configuration *mp = &mps[MI_SCREEN(mi)];
+
+ if (! do_rotate)
+ return;
+ if (mp->button_down_p)
+ return;
+
+ /* if we're not moving, maybe start moving. Otherwise, do nothing. */
+ if (! mp->auto_tracking_p)
+ {
+ if (++mp->track_tick < 20/speed) return;
+ mp->track_tick = 0;
+ if (! (random() % 20))
+ mp->auto_tracking_p = True;
+ else
+ return;
+ }
+
+
+ {
+ GLfloat ox = nice_views[mp->last_view].x;
+ GLfloat oy = nice_views[mp->last_view].y;
+ GLfloat tx = nice_views[mp->target_view].x;
+ GLfloat ty = nice_views[mp->target_view].y;
+
+ /* move from A to B with sinusoidal deltas, so that it doesn't jerk
+ to a stop. */
+ GLfloat th = sin ((M_PI / 2) * (double) mp->view_tick / mp->view_steps);
+
+ mp->view_x = (ox + ((tx - ox) * th));
+ mp->view_y = (oy + ((ty - oy) * th));
+ mp->view_tick++;
+
+ if (mp->view_tick >= mp->view_steps)
+ {
+ mp->view_tick = 0;
+ mp->view_steps = (350.0 / speed);
+ mp->last_view = mp->target_view;
+ mp->target_view = (random() % (countof(nice_views) - 1)) + 1;
+ mp->auto_tracking_p = False;
+ }
+ }
+}
+
+
+/* Window management, etc
+ */
+ENTRYPOINT void
+reshape_matrix (ModeInfo *mi, int width, int height)
+{
+ GLfloat h = (GLfloat) height / (GLfloat) width;
+
+ glViewport (0, 0, (GLint) width, (GLint) height);
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ gluPerspective (80.0, 1/h, 1.0, 100);
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ gluLookAt( 0.0, 0.0, 25.0,
+ 0.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0);
+}
+
+
+#if 0
+ENTRYPOINT Bool
+matrix_handle_event (ModeInfo *mi, XEvent *event)
+{
+ matrix_configuration *mp = &mps[MI_SCREEN(mi)];
+
+ if (event->xany.type == ButtonPress &&
+ event->xbutton.button == Button1)
+ {
+ mp->button_down_p = True;
+ return True;
+ }
+ else if (event->xany.type == ButtonRelease &&
+ event->xbutton.button == Button1)
+ {
+ mp->button_down_p = False;
+ return True;
+ }
+
+ return False;
+}
+#endif
+
+#if 0
+static Bool
+bigendian (void)
+{
+ union { int i; char c[sizeof(int)]; } u;
+ u.i = 1;
+ return !u.c[0];
+}
+#endif
+
+
+/* The image with the characters in it is 512x598, meaning that it needs to
+ be copied into a 512x1024 texture. But some machines can't handle textures
+ that large... And it turns out that we aren't using most of the characters
+ in that image anyway, since this program doesn't do anything that makes use
+ of the full range of Latin1 characters. So... this function tosses out the
+ last 32 of the Latin1 characters, resulting in a 512x506 image, which we
+ can then stuff in a 512x512 texture. Voila.
+
+ If this hack ever grows into something that displays full Latin1 text,
+ well then, Something Else Will Need To Be Done.
+ */
+static void
+spank_image (matrix_configuration *mp, XImage *xi)
+{
+ int ch = xi->height / CHAR_ROWS;
+ int cut = 2;
+ unsigned char *bits = (unsigned char *) xi->data;
+ unsigned char *from, *to, *s, *end;
+ int L = xi->bytes_per_line * ch;
+/* int i;*/
+
+ /* Copy row 12 into 10 (which really means, copy 2 into 0,
+ since texture data is upside down.).
+ */
+ to = bits + (L * cut);
+ from = bits;
+ end = from + L;
+ s = from;
+ while (s < end)
+ *to++ = *s++;
+
+ /* Then, pull all the bits down by 2 rows.
+ */
+ to = bits;
+ from = bits + (L * cut);
+ end = bits + (L * CHAR_ROWS);
+ s = from;
+ while (s < end)
+ *to++ = *s++;
+
+ /* And clear out the rest, for good measure.
+ */
+ from = bits + (L * (CHAR_ROWS - cut));
+ end = bits + (L * CHAR_ROWS);
+ s = from;
+ while (s < end)
+ *s++ = 0;
+
+ xi->height -= (cut * ch);
+ mp->real_char_rows -= cut;
+
+# if 0
+ /* Finally, pull the map indexes back to match the new bits.
+ */
+ for (i = 0; i < countof(matrix_encoding); i++)
+ if (matrix_encoding[i] > (CHAR_COLS * (CHAR_ROWS - cut)))
+ matrix_encoding[i] -= (cut * CHAR_COLS);
+# endif
+}
+
+
+static void
+load_textures (ModeInfo *mi, Bool flip_p)
+{
+ matrix_configuration *mp = &mps[MI_SCREEN(mi)];
+ XImage *xi;
+ int x, y;
+ int cw, ch;
+ int orig_w, orig_h;
+
+ /* The Matrix XPM is 512x598 -- but GL texture sizes must be powers of 2.
+ So we waste some padding rows to round up.
+ */
+ xi = xpm_to_ximage (matrix3_xpm);
+ orig_w = xi->width;
+ orig_h = xi->height;
+ mp->real_char_rows = CHAR_ROWS;
+ spank_image (mp, xi);
+
+ if (xi->height != 512 && xi->height != 1024)
+ {
+ xi->height = (xi->height < 512 ? 512 : 1024);
+ xi->data = realloc (xi->data, xi->height * xi->bytes_per_line);
+ if (!xi->data)
+ {
+ fprintf(stderr, "%s: out of memory\n", progname);
+ exit(1);
+ }
+ }
+
+ if (xi->width != 512) abort();
+ if (xi->height != 512 && xi->height != 1024) abort();
+
+ /* char size in pixels */
+ cw = orig_w / CHAR_COLS;
+ ch = orig_h / CHAR_ROWS;
+
+ /* char size in ratio of final (padded) texture size */
+ mp->tex_char_width = (GLfloat) cw / xi->width;
+ mp->tex_char_height = (GLfloat) ch / xi->height;
+
+ /* Flip each character's bits horizontally -- we could also just do this
+ by reversing the texture coordinates on the quads, but on some systems
+ that slows things down a lot.
+ */
+ if (flip_p)
+ {
+ int xx, col;
+ unsigned long buf[100];
+ for (y = 0; y < xi->height; y++)
+ for (col = 0, xx = 0; col < CHAR_COLS; col++, xx += cw)
+ {
+ for (x = 0; x < cw; x++)
+ buf[x] = XGetPixel (xi, xx+x, y);
+ for (x = 0; x < cw; x++)
+ XPutPixel (xi, xx+x, y, buf[cw-x-1]);
+ }
+ }
+
+ /* The pixmap is a color image with no transparency. Set the texture's
+ alpha to be the green channel, and set the green channel to be 100%.
+ */
+ {
+ int rpos, gpos, bpos, apos; /* bitfield positions */
+#if 0
+ /* #### Cherub says that the little-endian case must be taken on MacOSX,
+ or else the colors/alpha are the wrong way around. How can
+ that be the case?
+ */
+ if (bigendian())
+ rpos = 24, gpos = 16, bpos = 8, apos = 0;
+ else
+#endif
+ rpos = 0, gpos = 8, bpos = 16, apos = 24;
+
+ for (y = 0; y < xi->height; y++)
+ for (x = 0; x < xi->width; x++)
+ {
+ unsigned long p = XGetPixel (xi, x, y);
+ unsigned char r = (p >> rpos) & 0xFF;
+ unsigned char g = (p >> gpos) & 0xFF;
+ unsigned char b = (p >> bpos) & 0xFF;
+ unsigned char a = g;
+ g = 0xFF;
+ p = (r << rpos) | (g << gpos) | (b << bpos) | (a << apos);
+ XPutPixel (xi, x, y, p);
+ }
+ }
+
+ /* Now load the texture into GL.
+ */
+ clear_gl_error();
+ glGenTextures (1, &mp->texture);
+
+ glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
+ glPixelStorei (GL_UNPACK_ROW_LENGTH, xi->width);
+ glBindTexture (GL_TEXTURE_2D, mp->texture);
+ check_gl_error ("texture init");
+ glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, xi->width, xi->height, 0, GL_RGBA,
+ GL_UNSIGNED_INT_8_8_8_8_REV, xi->data);
+ {
+ char buf[255];
+ sprintf (buf, "creating %dx%d texture:", xi->width, xi->height);
+ check_gl_error (buf);
+ }
+
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+
+ /* I'd expect CLAMP to be the thing to do here, but oddly, we get a
+ faint solid green border around the texture if it is *not* REPEAT!
+ */
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+
+ glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+ glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
+ check_gl_error ("texture param");
+
+ XDestroyImage (xi);
+}
+
+
+ENTRYPOINT void
+init_matrix (ModeInfo *mi)
+{
+ matrix_configuration *mp;
+ int wire = MI_IS_WIREFRAME(mi);
+ Bool flip_p = 0;
+ int i;
+
+ if (wire)
+ do_texture = False;
+
+ if (!mps) {
+ mps = (matrix_configuration *)
+ calloc (MI_NUM_SCREENS(mi), sizeof (matrix_configuration));
+ if (!mps) {
+ fprintf(stderr, "%s: out of memory\n", progname);
+ exit(1);
+ }
+ }
+
+ mp = &mps[MI_SCREEN(mi)];
+ mp->glx_context = init_GL(mi);
+
+ if (!mode_str || !*mode_str || !strcasecmp(mode_str, "matrix"))
+ {
+ flip_p = 1;
+ mp->glyph_map = matrix_encoding;
+ mp->nglyphs = countof(matrix_encoding);
+ }
+ else if (!strcasecmp (mode_str, "dna"))
+ {
+ flip_p = 0;
+ mp->glyph_map = dna_encoding;
+ mp->nglyphs = countof(dna_encoding);
+ }
+ else if (!strcasecmp (mode_str, "bin") ||
+ !strcasecmp (mode_str, "binary"))
+ {
+ flip_p = 0;
+ mp->glyph_map = binary_encoding;
+ mp->nglyphs = countof(binary_encoding);
+ }
+ else if (!strcasecmp (mode_str, "hex") ||
+ !strcasecmp (mode_str, "hexadecimal"))
+ {
+ flip_p = 0;
+ mp->glyph_map = hex_encoding;
+ mp->nglyphs = countof(hex_encoding);
+ }
+ else if (!strcasecmp (mode_str, "dec") ||
+ !strcasecmp (mode_str, "decimal"))
+ {
+ flip_p = 0;
+ mp->glyph_map = decimal_encoding;
+ mp->nglyphs = countof(decimal_encoding);
+ }
+ else
+ {
+ fprintf (stderr,
+ "%s: `mode' must be matrix, dna, binary, or hex: not `%s'\n",
+ progname, mode_str);
+ exit (1);
+ }
+
+ reshape_matrix (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
+
+ glShadeModel(GL_SMOOTH);
+
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_CULL_FACE);
+ glEnable(GL_NORMALIZE);
+
+ if (do_texture)
+ {
+ load_textures (mi, flip_p);
+ glEnable(GL_TEXTURE_2D);
+ glEnable(GL_BLEND);
+
+ /* Jeff Epler points out:
+ By using GL_ONE instead of GL_SRC_ONE_MINUS_ALPHA, glyphs are
+ added to each other, so that a bright glyph with a darker one
+ in front is a little brighter than the bright glyph alone.
+ */
+ glBlendFunc (GL_SRC_ALPHA, GL_ONE);
+ }
+
+ /* to scale coverage-percent to strips, this number looks about right... */
+ mp->nstrips = (int) (density * 2.2);
+ if (mp->nstrips < 1) mp->nstrips = 1;
+ else if (mp->nstrips > 2000) mp->nstrips = 2000;
+
+
+ mp->strips = calloc (mp->nstrips, sizeof(strip));
+ for (i = 0; i < mp->nstrips; i++)
+ {
+ strip *s = &mp->strips[i];
+ reset_strip (mi, s);
+
+ /* If we start all strips from zero at once, then the first few seconds
+ of the animation are much denser than normal. So instead, set all
+ the initial strips to erase-mode with random starting positions.
+ As these die off at random speeds and are re-created, we'll get a
+ more consistent density. */
+ s->erasing_p = True;
+ s->spinner_y = frand(GRID_SIZE);
+ memset (s->glyphs, 0, sizeof(s->glyphs)); /* no visible glyphs */
+ }
+
+ /* Compute the brightness ramp.
+ */
+ for (i = 0; i < WAVE_SIZE; i++)
+ {
+ GLfloat j = ((WAVE_SIZE - i) / (GLfloat) (WAVE_SIZE - 1));
+ j *= (M_PI / 2); /* j ranges from 0.0 - PI/2 */
+ j = sin (j); /* j ranges from 0.0 - 1.0 */
+ j = 0.2 + (j * 0.8); /* j ranges from 0.2 - 1.0 */
+ mp->brightness_ramp[i] = j;
+ /* printf("%2d %8.2f\n", i, j); */
+ }
+
+
+ auto_track_init (mi);
+}
+
+
+#ifdef DEBUG
+
+static void
+draw_grid (ModeInfo *mi)
+{
+ if (!MI_IS_WIREFRAME(mi))
+ {
+ glDisable(GL_TEXTURE_2D);
+ glDisable(GL_BLEND);
+ }
+ glPushMatrix();
+
+ glColor3f(1, 1, 1);
+ glBegin(GL_LINES);
+ glVertex3f(-GRID_SIZE, 0, 0); glVertex3f(GRID_SIZE, 0, 0);
+ glVertex3f(0, -GRID_SIZE, 0); glVertex3f(0, GRID_SIZE, 0);
+ glEnd();
+ glBegin(GL_LINE_LOOP);
+ glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, 0);
+ glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, 0);
+ glVertex3f( GRID_SIZE/2, GRID_SIZE/2, 0);
+ glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, 0);
+ glEnd();
+ glBegin(GL_LINE_LOOP);
+ glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, -GRID_DEPTH/2);
+ glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, GRID_DEPTH/2);
+ glVertex3f( GRID_SIZE/2, GRID_SIZE/2, GRID_DEPTH/2);
+ glVertex3f( GRID_SIZE/2, GRID_SIZE/2, -GRID_DEPTH/2);
+ glEnd();
+ glBegin(GL_LINE_LOOP);
+ glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
+ glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, GRID_DEPTH/2);
+ glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, GRID_DEPTH/2);
+ glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
+ glEnd();
+ glBegin(GL_LINES);
+ glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
+ glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, -GRID_DEPTH/2);
+ glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, GRID_DEPTH/2);
+ glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, GRID_DEPTH/2);
+ glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
+ glVertex3f( GRID_SIZE/2, GRID_SIZE/2, -GRID_DEPTH/2);
+ glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, GRID_DEPTH/2);
+ glVertex3f( GRID_SIZE/2, GRID_SIZE/2, GRID_DEPTH/2);
+ glEnd();
+ glPopMatrix();
+ if (!MI_IS_WIREFRAME(mi))
+ {
+ glEnable(GL_TEXTURE_2D);
+ glEnable(GL_BLEND);
+ }
+}
+#endif /* DEBUG */
+
+
+ENTRYPOINT void
+draw_matrix (ModeInfo *mi)
+{
+ matrix_configuration *mp = &mps[MI_SCREEN(mi)];
+ int i;
+
+ if (!mp->glx_context)
+ return;
+
+ glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(mp->glx_context));
+
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ glPushMatrix ();
+
+ if (do_rotate)
+ {
+ glRotatef (mp->view_x, 1, 0, 0);
+ glRotatef (mp->view_y, 0, 1, 0);
+ }
+
+#ifdef DEBUG
+# if 0
+ glScalef(0.5, 0.5, 0.5);
+# endif
+# if 0
+ glRotatef(-30, 0, 1, 0);
+# endif
+ draw_grid (mi);
+#endif
+
+ mi->polygon_count = 0;
+
+ /* Render (and tick) each strip, starting at the back
+ (draw the ones farthest from the camera first, to make
+ the alpha transparency work out right.)
+ */
+ {
+ strip **sorted = malloc (mp->nstrips * sizeof(*sorted));
+ for (i = 0; i < mp->nstrips; i++)
+ sorted[i] = &mp->strips[i];
+ qsort (sorted, i, sizeof(*sorted), cmp_strips);
+
+ for (i = 0; i < mp->nstrips; i++)
+ {
+ strip *s = sorted[i];
+ tick_strip (mi, s);
+ draw_strip (mi, s);
+ }
+ free (sorted);
+ }
+
+ auto_track (mi);
+
+#if 0
+ glBegin(GL_QUADS);
+ glColor3f(1,1,1);
+ glTexCoord2f (0,0); glVertex3f(-15,-15,0);
+ glTexCoord2f (0,1); glVertex3f(-15,15,0);
+ glTexCoord2f (1,1); glVertex3f(15,15,0);
+ glTexCoord2f (1,0); glVertex3f(15,-15,0);
+ glEnd();
+#endif
+
+ glPopMatrix ();
+
+ if (mi->fps_p) do_fps (mi);
+ glFinish();
+
+ glXSwapBuffers(MI_DISPLAY(mi), MI_WINDOW(mi));
+}
+
+WL_EXPORT struct wscreensaver_plugin glmatrix_screensaver = {
+ "GLMatrix",
+ init_matrix,
+ draw_matrix,
+ reshape_matrix
+};
diff --git a/clients/image.c b/clients/image.c
new file mode 100644
index 00000000..4d3f3b87
--- /dev/null
+++ b/clients/image.c
@@ -0,0 +1,426 @@
+/*
+ * Copyright © 2008 Kristian Høgsberg
+ * Copyright © 2009 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * 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.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <unistd.h>
+#include <math.h>
+#include <time.h>
+#include <cairo.h>
+#include <assert.h>
+#include <linux/input.h>
+
+#include <wayland-client.h>
+
+#include "window.h"
+#include "../shared/cairo-util.h"
+
+struct image {
+ struct window *window;
+ struct widget *widget;
+ struct display *display;
+ char *filename;
+ cairo_surface_t *image;
+ int fullscreen;
+ int *image_counter;
+ int32_t width, height;
+
+ struct {
+ double x;
+ double y;
+ } pointer;
+ bool button_pressed;
+
+ bool initialized;
+ cairo_matrix_t matrix;
+};
+
+static double
+get_scale(struct image *image)
+{
+ assert(image->matrix.xy == 0.0 &&
+ image->matrix.yx == 0.0 &&
+ image->matrix.xx == image->matrix.yy);
+ return image->matrix.xx;
+}
+
+static void
+clamp_view(struct image *image)
+{
+ struct rectangle allocation;
+ double scale = get_scale(image);
+ double sw, sh;
+
+ sw = image->width * scale;
+ sh = image->height * scale;
+ widget_get_allocation(image->widget, &allocation);
+
+ if (sw < allocation.width) {
+ image->matrix.x0 =
+ (allocation.width - image->width * scale) / 2;
+ } else {
+ if (image->matrix.x0 > 0.0)
+ image->matrix.x0 = 0.0;
+ if (sw + image->matrix.x0 < allocation.width)
+ image->matrix.x0 = allocation.width - sw;
+ }
+
+ if (sh < allocation.width) {
+ image->matrix.y0 =
+ (allocation.height - image->height * scale) / 2;
+ } else {
+ if (image->matrix.y0 > 0.0)
+ image->matrix.y0 = 0.0;
+ if (sh + image->matrix.y0 < allocation.height)
+ image->matrix.y0 = allocation.height - sh;
+ }
+}
+
+static void
+redraw_handler(struct widget *widget, void *data)
+{
+ struct image *image = data;
+ struct rectangle allocation;
+ cairo_t *cr;
+ cairo_surface_t *surface;
+ double width, height, doc_aspect, window_aspect, scale;
+ cairo_matrix_t matrix;
+ cairo_matrix_t translate;
+
+ surface = window_get_surface(image->window);
+ cr = cairo_create(surface);
+ widget_get_allocation(image->widget, &allocation);
+ cairo_rectangle(cr, allocation.x, allocation.y,
+ allocation.width, allocation.height);
+ cairo_clip(cr);
+ cairo_push_group(cr);
+ cairo_translate(cr, allocation.x, allocation.y);
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ cairo_set_source_rgba(cr, 0, 0, 0, 1);
+ cairo_paint(cr);
+
+ if (!image->initialized) {
+ image->initialized = true;
+ width = cairo_image_surface_get_width(image->image);
+ height = cairo_image_surface_get_height(image->image);
+
+ doc_aspect = width / height;
+ window_aspect = (double) allocation.width / allocation.height;
+ if (doc_aspect < window_aspect)
+ scale = allocation.height / height;
+ else
+ scale = allocation.width / width;
+
+ image->width = width;
+ image->height = height;
+ cairo_matrix_init_scale(&image->matrix, scale, scale);
+
+ clamp_view(image);
+ }
+
+ matrix = image->matrix;
+ cairo_matrix_init_translate(&translate, allocation.x, allocation.y);
+ cairo_matrix_multiply(&matrix, &matrix, &translate);
+ cairo_set_matrix(cr, &matrix);
+
+ cairo_set_source_surface(cr, image->image, 0, 0);
+ cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+ cairo_paint(cr);
+
+ cairo_pop_group_to_source(cr);
+ cairo_paint(cr);
+ cairo_destroy(cr);
+
+ cairo_surface_destroy(surface);
+}
+
+static void
+resize_handler(struct widget *widget,
+ int32_t width, int32_t height, void *data)
+{
+ struct image *image = data;
+
+ clamp_view(image);
+}
+
+static void
+keyboard_focus_handler(struct window *window,
+ struct input *device, void *data)
+{
+ struct image *image = data;
+
+ window_schedule_redraw(image->window);
+}
+
+static int
+enter_handler(struct widget *widget,
+ struct input *input,
+ float x, float y, void *data)
+{
+ struct image *image = data;
+ struct rectangle allocation;
+
+ widget_get_allocation(image->widget, &allocation);
+ x -= allocation.x;
+ y -= allocation.y;
+
+ image->pointer.x = x;
+ image->pointer.y = y;
+
+ return 1;
+}
+
+static void
+move_viewport(struct image *image, double dx, double dy)
+{
+ double scale = get_scale(image);
+
+ if (!image->initialized)
+ return;
+
+ cairo_matrix_translate(&image->matrix, -dx/scale, -dy/scale);
+ clamp_view(image);
+
+ window_schedule_redraw(image->window);
+}
+
+static int
+motion_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ float x, float y, void *data)
+{
+ struct image *image = data;
+ struct rectangle allocation;
+
+ widget_get_allocation(image->widget, &allocation);
+ x -= allocation.x;
+ y -= allocation.y;
+
+ if (image->button_pressed)
+ move_viewport(image, image->pointer.x - x,
+ image->pointer.y - y);
+
+ image->pointer.x = x;
+ image->pointer.y = y;
+
+ return image->button_pressed ? CURSOR_DRAGGING : CURSOR_LEFT_PTR;
+}
+
+static void
+button_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ uint32_t button,
+ enum wl_pointer_button_state state,
+ void *data)
+{
+ struct image *image = data;
+
+ if (button == BTN_LEFT) {
+ image->button_pressed =
+ state == WL_POINTER_BUTTON_STATE_PRESSED;
+
+ if (state == WL_POINTER_BUTTON_STATE_PRESSED)
+ input_set_pointer_image(input, CURSOR_DRAGGING);
+ else
+ input_set_pointer_image(input, CURSOR_LEFT_PTR);
+ }
+}
+
+static void
+zoom(struct image *image, double scale)
+{
+ double x = image->pointer.x;
+ double y = image->pointer.y;
+ cairo_matrix_t scale_matrix;
+
+ if (!image->initialized)
+ return;
+
+ if (get_scale(image) * scale > 20.0 ||
+ get_scale(image) * scale < 0.02)
+ return;
+
+ cairo_matrix_init_identity(&scale_matrix);
+ cairo_matrix_translate(&scale_matrix, x, y);
+ cairo_matrix_scale(&scale_matrix, scale, scale);
+ cairo_matrix_translate(&scale_matrix, -x, -y);
+
+ cairo_matrix_multiply(&image->matrix, &image->matrix, &scale_matrix);
+ clamp_view(image);
+}
+
+static void
+key_handler(struct window *window, struct input *input, uint32_t time,
+ uint32_t key, uint32_t sym, enum wl_keyboard_key_state state,
+ void *data)
+{
+ struct image *image = data;
+
+ if (state == WL_KEYBOARD_KEY_STATE_RELEASED)
+ return;
+
+ switch (sym) {
+ case XKB_KEY_minus:
+ zoom(image, 0.8);
+ window_schedule_redraw(image->window);
+ break;
+ case XKB_KEY_equal:
+ case XKB_KEY_plus:
+ zoom(image, 1.2);
+ window_schedule_redraw(image->window);
+ break;
+ case XKB_KEY_1:
+ image->matrix.xx = 1.0;
+ image->matrix.xy = 0.0;
+ image->matrix.yx = 0.0;
+ image->matrix.yy = 1.0;
+ clamp_view(image);
+ window_schedule_redraw(image->window);
+ break;
+ }
+}
+
+static void
+axis_handler(struct widget *widget, struct input *input, uint32_t time,
+ uint32_t axis, wl_fixed_t value, void *data)
+{
+ struct image *image = data;
+
+ if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL &&
+ input_get_modifiers(input) == MOD_CONTROL_MASK) {
+ /* set zoom level to 2% per 10 axis units */
+ zoom(image, (1.0 - wl_fixed_to_double(value) / 500.0));
+
+ window_schedule_redraw(image->window);
+ } else if (input_get_modifiers(input) == 0) {
+ if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL)
+ move_viewport(image, 0, wl_fixed_to_double(value));
+ else if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL)
+ move_viewport(image, wl_fixed_to_double(value), 0);
+ }
+}
+
+static void
+fullscreen_handler(struct window *window, void *data)
+{
+ struct image *image = data;
+
+ image->fullscreen ^= 1;
+ window_set_fullscreen(window, image->fullscreen);
+}
+
+static void
+close_handler(struct window *window, void *data)
+{
+ struct image *image = data;
+
+ *image->image_counter -= 1;
+
+ if (*image->image_counter == 0)
+ display_exit(image->display);
+
+ widget_destroy(image->widget);
+ window_destroy(image->window);
+
+ free(image);
+}
+
+static struct image *
+image_create(struct display *display, const char *filename,
+ int *image_counter)
+{
+ struct image *image;
+ char *b, *copy, title[512];;
+
+ image = zalloc(sizeof *image);
+ if (image == NULL)
+ return image;
+
+ copy = strdup(filename);
+ b = basename(copy);
+ snprintf(title, sizeof title, "Wayland Image - %s", b);
+ free(copy);
+
+ image->filename = strdup(filename);
+ image->image = load_cairo_surface(filename);
+
+ if (!image->image) {
+ fprintf(stderr, "could not find the image %s!\n", b);
+ free(image->filename);
+ free(image);
+ return NULL;
+ }
+
+ image->window = window_create(display);
+ image->widget = frame_create(image->window, image);
+ window_set_title(image->window, title);
+ image->display = display;
+ image->image_counter = image_counter;
+ *image_counter += 1;
+ image->initialized = false;
+
+ window_set_user_data(image->window, image);
+ widget_set_redraw_handler(image->widget, redraw_handler);
+ widget_set_resize_handler(image->widget, resize_handler);
+ window_set_keyboard_focus_handler(image->window,
+ keyboard_focus_handler);
+ window_set_fullscreen_handler(image->window, fullscreen_handler);
+ window_set_close_handler(image->window, close_handler);
+
+ widget_set_enter_handler(image->widget, enter_handler);
+ widget_set_motion_handler(image->widget, motion_handler);
+ widget_set_button_handler(image->widget, button_handler);
+ widget_set_axis_handler(image->widget, axis_handler);
+ window_set_key_handler(image->window, key_handler);
+ widget_schedule_resize(image->widget, 500, 400);
+
+ return image;
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct display *d;
+ int i;
+ int image_counter = 0;
+
+ d = display_create(&argc, argv);
+ if (d == NULL) {
+ fprintf(stderr, "failed to create display: %m\n");
+ return -1;
+ }
+
+ for (i = 1; i < argc; i++)
+ image_create(d, argv[i], &image_counter);
+
+ if (image_counter > 0)
+ display_run(d);
+
+ return 0;
+}
diff --git a/clients/keyboard.c b/clients/keyboard.c
new file mode 100644
index 00000000..9ee4a848
--- /dev/null
+++ b/clients/keyboard.c
@@ -0,0 +1,922 @@
+/*
+ * Copyright © 2012 Openismus GmbH
+ * Copyright © 2012 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 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <linux/input.h>
+#include <cairo.h>
+
+#include "window.h"
+#include "input-method-client-protocol.h"
+#include "text-client-protocol.h"
+
+struct keyboard;
+
+struct virtual_keyboard {
+ struct wl_input_panel *input_panel;
+ struct wl_input_method *input_method;
+ struct wl_input_method_context *context;
+ struct display *display;
+ struct output *output;
+ char *preedit_string;
+ uint32_t preedit_style;
+ struct {
+ xkb_mod_mask_t shift_mask;
+ } keysym;
+ uint32_t serial;
+ uint32_t content_hint;
+ uint32_t content_purpose;
+ char *preferred_language;
+ char *surrounding_text;
+ uint32_t surrounding_cursor;
+ struct keyboard *keyboard;
+};
+
+enum key_type {
+ keytype_default,
+ keytype_backspace,
+ keytype_enter,
+ keytype_space,
+ keytype_switch,
+ keytype_symbols,
+ keytype_tab,
+ keytype_arrow_up,
+ keytype_arrow_left,
+ keytype_arrow_right,
+ keytype_arrow_down,
+ keytype_style
+};
+
+struct key {
+ enum key_type key_type;
+
+ char *label;
+ char *alt;
+
+ unsigned int width;
+};
+
+struct layout {
+ const struct key *keys;
+ uint32_t count;
+
+ uint32_t columns;
+ uint32_t rows;
+
+ const char *language;
+ uint32_t text_direction;
+};
+
+static const struct key normal_keys[] = {
+ { keytype_default, "q", "Q", 1},
+ { keytype_default, "w", "W", 1},
+ { keytype_default, "e", "E", 1},
+ { keytype_default, "r", "R", 1},
+ { keytype_default, "t", "T", 1},
+ { keytype_default, "y", "Y", 1},
+ { keytype_default, "u", "U", 1},
+ { keytype_default, "i", "I", 1},
+ { keytype_default, "o", "O", 1},
+ { keytype_default, "p", "P", 1},
+ { keytype_backspace, "<--", "<--", 2},
+
+ { keytype_tab, "->|", "->|", 1},
+ { keytype_default, "a", "A", 1},
+ { keytype_default, "s", "S", 1},
+ { keytype_default, "d", "D", 1},
+ { keytype_default, "f", "F", 1},
+ { keytype_default, "g", "G", 1},
+ { keytype_default, "h", "H", 1},
+ { keytype_default, "j", "J", 1},
+ { keytype_default, "k", "K", 1},
+ { keytype_default, "l", "L", 1},
+ { keytype_enter, "Enter", "Enter", 2},
+
+ { keytype_switch, "ABC", "abc", 2},
+ { keytype_default, "z", "Z", 1},
+ { keytype_default, "x", "X", 1},
+ { keytype_default, "c", "C", 1},
+ { keytype_default, "v", "V", 1},
+ { keytype_default, "b", "B", 1},
+ { keytype_default, "n", "N", 1},
+ { keytype_default, "m", "M", 1},
+ { keytype_default, ",", ",", 1},
+ { keytype_default, ".", ".", 1},
+ { keytype_switch, "ABC", "abc", 1},
+
+ { keytype_symbols, "?123", "?123", 1},
+ { keytype_space, "", "", 5},
+ { keytype_arrow_up, "/\\", "/\\", 1},
+ { keytype_arrow_left, "<", "<", 1},
+ { keytype_arrow_right, ">", ">", 1},
+ { keytype_arrow_down, "\\/", "\\/", 1},
+ { keytype_style, "", "", 2}
+};
+
+static const struct key numeric_keys[] = {
+ { keytype_default, "1", "1", 1},
+ { keytype_default, "2", "2", 1},
+ { keytype_default, "3", "3", 1},
+ { keytype_default, "4", "4", 1},
+ { keytype_default, "5", "5", 1},
+ { keytype_default, "6", "6", 1},
+ { keytype_default, "7", "7", 1},
+ { keytype_default, "8", "8", 1},
+ { keytype_default, "9", "9", 1},
+ { keytype_default, "0", "0", 1},
+ { keytype_backspace, "<--", "<--", 2},
+
+ { keytype_space, "", "", 4},
+ { keytype_enter, "Enter", "Enter", 2},
+ { keytype_arrow_up, "/\\", "/\\", 1},
+ { keytype_arrow_left, "<", "<", 1},
+ { keytype_arrow_right, ">", ">", 1},
+ { keytype_arrow_down, "\\/", "\\/", 1},
+ { keytype_style, "", "", 2}
+};
+
+static const struct key arabic_keys[] = {
+ { keytype_default, "ض", "ض", 1},
+ { keytype_default, "ص", "ص", 1},
+ { keytype_default, "ث", "ث", 1},
+ { keytype_default, "ق", "ق", 1},
+ { keytype_default, "ف", "ف", 1},
+ { keytype_default, "غ", "إ", 1},
+ { keytype_default, "ع", "ع", 1},
+ { keytype_default, "ه", "ه", 1},
+ { keytype_default, "خ", "خ", 1},
+ { keytype_default, "ح", "ح", 1},
+ { keytype_default, "ج", "ج", 1},
+ { keytype_backspace, "-->", "-->", 2},
+
+ { keytype_tab, "->|", "->|", 1},
+ { keytype_default, "ش", "ش", 1},
+ { keytype_default, "س", "س", 1},
+ { keytype_default, "ي", "ي", 1},
+ { keytype_default, "ب", "ب", 1},
+ { keytype_default, "ل", "ل", 1},
+ { keytype_default, "ا", "أ", 1},
+ { keytype_default, "ت", "ت", 1},
+ { keytype_default, "ن", "ن", 1},
+ { keytype_default, "م", "م", 1},
+ { keytype_default, "ك", "ك", 1},
+ { keytype_default, "د", "د", 1},
+ { keytype_enter, "Enter", "Enter", 2},
+
+ { keytype_switch, "ABC", "abc", 2},
+ { keytype_default, "ئ", "ئ", 1},
+ { keytype_default, "ء", "ء", 1},
+ { keytype_default, "ؤ", "ؤ", 1},
+ { keytype_default, "ر", "ر", 1},
+ { keytype_default, "ى", "آ", 1},
+ { keytype_default, "ة", "ة", 1},
+ { keytype_default, "و", "و", 1},
+ { keytype_default, "ز", "ز", 1},
+ { keytype_default, "ظ", "ظ", 1},
+ { keytype_switch, "ABC", "abc", 2},
+
+ { keytype_symbols, "؟٣٢١", "؟٣٢١", 1},
+ { keytype_default, "ذ", "ذ", 1},
+ { keytype_default, "،", "،", 1},
+ { keytype_space, "", "", 6},
+ { keytype_default, ".", ".", 1},
+ { keytype_default, "ط", "ط", 1},
+ { keytype_style, "", "", 2}
+};
+
+
+static const struct layout normal_layout = {
+ normal_keys,
+ sizeof(normal_keys) / sizeof(*normal_keys),
+ 12,
+ 4,
+ "en",
+ WL_TEXT_INPUT_TEXT_DIRECTION_LTR
+};
+
+static const struct layout numeric_layout = {
+ numeric_keys,
+ sizeof(numeric_keys) / sizeof(*numeric_keys),
+ 12,
+ 2,
+ "en",
+ WL_TEXT_INPUT_TEXT_DIRECTION_LTR
+};
+
+static const struct layout arabic_layout = {
+ arabic_keys,
+ sizeof(arabic_keys) / sizeof(*arabic_keys),
+ 13,
+ 4,
+ "ar",
+ WL_TEXT_INPUT_TEXT_DIRECTION_RTL
+};
+
+static const char *style_labels[] = {
+ "default",
+ "none",
+ "active",
+ "inactive",
+ "highlight",
+ "underline",
+ "selection",
+ "incorrect"
+};
+
+static const double key_width = 60;
+static const double key_height = 50;
+
+enum keyboard_state {
+ keyboardstate_default,
+ keyboardstate_uppercase
+};
+
+struct keyboard {
+ struct virtual_keyboard *keyboard;
+ struct window *window;
+ struct widget *widget;
+
+ enum keyboard_state state;
+};
+
+static const char *
+label_from_key(struct keyboard *keyboard,
+ const struct key *key)
+{
+ if (key->key_type == keytype_style)
+ return style_labels[keyboard->keyboard->preedit_style];
+
+ if (keyboard->state == keyboardstate_default)
+ return key->label;
+ else
+ return key->alt;
+}
+
+static void
+draw_key(struct keyboard *keyboard,
+ const struct key *key,
+ cairo_t *cr,
+ unsigned int row,
+ unsigned int col)
+{
+ const char *label;
+ cairo_text_extents_t extents;
+
+ cairo_save(cr);
+ cairo_rectangle(cr,
+ col * key_width, row * key_height,
+ key->width * key_width, key_height);
+ cairo_clip(cr);
+
+ /* Paint frame */
+ cairo_rectangle(cr,
+ col * key_width, row * key_height,
+ key->width * key_width, key_height);
+ cairo_set_line_width(cr, 3);
+ cairo_stroke(cr);
+
+ /* Paint text */
+ label = label_from_key(keyboard, key);
+ cairo_text_extents(cr, label, &extents);
+
+ cairo_translate(cr,
+ col * key_width,
+ row * key_height);
+ cairo_translate(cr,
+ (key->width * key_width - extents.width) / 2,
+ (key_height - extents.y_bearing) / 2);
+ cairo_show_text(cr, label);
+
+ cairo_restore(cr);
+}
+
+static const struct layout *
+get_current_layout(struct virtual_keyboard *keyboard)
+{
+ switch (keyboard->content_purpose) {
+ case WL_TEXT_INPUT_CONTENT_PURPOSE_DIGITS:
+ case WL_TEXT_INPUT_CONTENT_PURPOSE_NUMBER:
+ return &numeric_layout;
+ default:
+ if (keyboard->preferred_language &&
+ strcmp(keyboard->preferred_language, "ar") == 0)
+ return &arabic_layout;
+ else
+ return &normal_layout;
+ }
+}
+
+static void
+redraw_handler(struct widget *widget, void *data)
+{
+ struct keyboard *keyboard = data;
+ cairo_surface_t *surface;
+ struct rectangle allocation;
+ cairo_t *cr;
+ unsigned int i;
+ unsigned int row = 0, col = 0;
+ const struct layout *layout;
+
+ layout = get_current_layout(keyboard->keyboard);
+
+ surface = window_get_surface(keyboard->window);
+ widget_get_allocation(keyboard->widget, &allocation);
+
+ cr = cairo_create(surface);
+ cairo_rectangle(cr, allocation.x, allocation.y, allocation.width, allocation.height);
+ cairo_clip(cr);
+
+ cairo_select_font_face(cr, "sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
+ cairo_set_font_size(cr, 16);
+
+ cairo_translate(cr, allocation.x, allocation.y);
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ cairo_set_source_rgba(cr, 1, 1, 1, 0.75);
+ cairo_rectangle(cr, 0, 0, layout->columns * key_width, layout->rows * key_height);
+ cairo_paint(cr);
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+
+ for (i = 0; i < layout->count; ++i) {
+ cairo_set_source_rgb(cr, 0, 0, 0);
+ draw_key(keyboard, &layout->keys[i], cr, row, col);
+ col += layout->keys[i].width;
+ if (col >= layout->columns) {
+ row += 1;
+ col = 0;
+ }
+ }
+
+ cairo_destroy(cr);
+ cairo_surface_destroy(surface);
+}
+
+static void
+resize_handler(struct widget *widget,
+ int32_t width, int32_t height, void *data)
+{
+ /* struct keyboard *keyboard = data; */
+}
+
+static char *
+insert_text(const char *text, uint32_t offset, const char *insert)
+{
+ char *new_text = xmalloc(strlen(text) + strlen(insert) + 1);
+
+ strncat(new_text, text, offset);
+ new_text[offset] = '\0';
+ strcat(new_text, insert);
+ strcat(new_text, text + offset);
+
+ return new_text;
+}
+
+static void
+virtual_keyboard_commit_preedit(struct virtual_keyboard *keyboard)
+{
+ char *surrounding_text;
+
+ if (!keyboard->preedit_string ||
+ strlen(keyboard->preedit_string) == 0)
+ return;
+
+ wl_input_method_context_cursor_position(keyboard->context,
+ 0, 0);
+ wl_input_method_context_commit_string(keyboard->context,
+ keyboard->serial,
+ keyboard->preedit_string);
+
+ if (keyboard->surrounding_text) {
+ surrounding_text = insert_text(keyboard->surrounding_text,
+ keyboard->surrounding_cursor,
+ keyboard->preedit_string);
+ free(keyboard->surrounding_text);
+ keyboard->surrounding_text = surrounding_text;
+ keyboard->surrounding_cursor += strlen(keyboard->preedit_string);
+ } else {
+ keyboard->surrounding_text = strdup(keyboard->preedit_string);
+ keyboard->surrounding_cursor = strlen(keyboard->preedit_string);
+ }
+
+ free(keyboard->preedit_string);
+ keyboard->preedit_string = strdup("");
+}
+
+static void
+virtual_keyboard_send_preedit(struct virtual_keyboard *keyboard,
+ int32_t cursor)
+{
+ uint32_t index = strlen(keyboard->preedit_string);
+
+ if (keyboard->preedit_style)
+ wl_input_method_context_preedit_styling(keyboard->context,
+ 0,
+ strlen(keyboard->preedit_string),
+ keyboard->preedit_style);
+ if (cursor > 0)
+ index = cursor;
+ wl_input_method_context_preedit_cursor(keyboard->context,
+ index);
+ wl_input_method_context_preedit_string(keyboard->context,
+ keyboard->serial,
+ keyboard->preedit_string,
+ keyboard->preedit_string);
+}
+
+static const char *
+prev_utf8_char(const char *s, const char *p)
+{
+ for (--p; p >= s; --p) {
+ if ((*p & 0xc0) != 0x80)
+ return p;
+ }
+ return NULL;
+}
+
+static void
+delete_before_cursor(struct virtual_keyboard *keyboard)
+{
+ const char *start, *end;
+
+ if (!keyboard->surrounding_text) {
+ fprintf(stderr, "delete_before_cursor: No surrounding text available\n");
+ return;
+ }
+
+ start = prev_utf8_char(keyboard->surrounding_text,
+ keyboard->surrounding_text + keyboard->surrounding_cursor);
+ if (!start) {
+ fprintf(stderr, "delete_before_cursor: No previous character to delete\n");
+ return;
+ }
+
+ end = keyboard->surrounding_text + keyboard->surrounding_cursor;
+
+ wl_input_method_context_delete_surrounding_text(keyboard->context,
+ (start - keyboard->surrounding_text) - keyboard->surrounding_cursor,
+ end - start);
+ wl_input_method_context_commit_string(keyboard->context,
+ keyboard->serial,
+ "");
+
+ /* Update surrounding text */
+ keyboard->surrounding_cursor = start - keyboard->surrounding_text;
+ keyboard->surrounding_text[keyboard->surrounding_cursor] = '\0';
+ if (*end)
+ memmove(keyboard->surrounding_text + keyboard->surrounding_cursor, end, strlen(end));
+}
+
+static void
+keyboard_handle_key(struct keyboard *keyboard, uint32_t time, const struct key *key, struct input *input, enum wl_pointer_button_state state)
+{
+ const char *label = keyboard->state == keyboardstate_default ? key->label : key->alt;
+ xkb_mod_mask_t mod_mask = keyboard->state == keyboardstate_default ? 0 : keyboard->keyboard->keysym.shift_mask;
+ uint32_t key_state = (state == WL_POINTER_BUTTON_STATE_PRESSED) ? WL_KEYBOARD_KEY_STATE_PRESSED : WL_KEYBOARD_KEY_STATE_RELEASED;
+
+ switch (key->key_type) {
+ case keytype_default:
+ if (state != WL_POINTER_BUTTON_STATE_PRESSED)
+ break;
+
+ keyboard->keyboard->preedit_string = strcat(keyboard->keyboard->preedit_string,
+ label);
+ virtual_keyboard_send_preedit(keyboard->keyboard, -1);
+ break;
+ case keytype_backspace:
+ if (state != WL_POINTER_BUTTON_STATE_PRESSED)
+ break;
+
+ if (strlen(keyboard->keyboard->preedit_string) == 0) {
+ delete_before_cursor(keyboard->keyboard);
+ } else {
+ keyboard->keyboard->preedit_string[strlen(keyboard->keyboard->preedit_string) - 1] = '\0';
+ virtual_keyboard_send_preedit(keyboard->keyboard, -1);
+ }
+ break;
+ case keytype_enter:
+ virtual_keyboard_commit_preedit(keyboard->keyboard);
+ wl_input_method_context_keysym(keyboard->keyboard->context,
+ display_get_serial(keyboard->keyboard->display),
+ time,
+ XKB_KEY_Return, key_state, mod_mask);
+ break;
+ case keytype_space:
+ if (state != WL_POINTER_BUTTON_STATE_PRESSED)
+ break;
+ keyboard->keyboard->preedit_string = strcat(keyboard->keyboard->preedit_string,
+ " ");
+ virtual_keyboard_commit_preedit(keyboard->keyboard);
+ break;
+ case keytype_switch:
+ if (state != WL_POINTER_BUTTON_STATE_PRESSED)
+ break;
+ if (keyboard->state == keyboardstate_default)
+ keyboard->state = keyboardstate_uppercase;
+ else
+ keyboard->state = keyboardstate_default;
+ break;
+ case keytype_symbols:
+ if (state != WL_POINTER_BUTTON_STATE_PRESSED)
+ break;
+ break;
+ case keytype_tab:
+ virtual_keyboard_commit_preedit(keyboard->keyboard);
+ wl_input_method_context_keysym(keyboard->keyboard->context,
+ display_get_serial(keyboard->keyboard->display),
+ time,
+ XKB_KEY_Tab, key_state, mod_mask);
+ break;
+ case keytype_arrow_up:
+ virtual_keyboard_commit_preedit(keyboard->keyboard);
+ wl_input_method_context_keysym(keyboard->keyboard->context,
+ display_get_serial(keyboard->keyboard->display),
+ time,
+ XKB_KEY_Up, key_state, mod_mask);
+ break;
+ case keytype_arrow_left:
+ virtual_keyboard_commit_preedit(keyboard->keyboard);
+ wl_input_method_context_keysym(keyboard->keyboard->context,
+ display_get_serial(keyboard->keyboard->display),
+ time,
+ XKB_KEY_Left, key_state, mod_mask);
+ break;
+ case keytype_arrow_right:
+ virtual_keyboard_commit_preedit(keyboard->keyboard);
+ wl_input_method_context_keysym(keyboard->keyboard->context,
+ display_get_serial(keyboard->keyboard->display),
+ time,
+ XKB_KEY_Right, key_state, mod_mask);
+ break;
+ case keytype_arrow_down:
+ virtual_keyboard_commit_preedit(keyboard->keyboard);
+ wl_input_method_context_keysym(keyboard->keyboard->context,
+ display_get_serial(keyboard->keyboard->display),
+ time,
+ XKB_KEY_Down, key_state, mod_mask);
+ break;
+ case keytype_style:
+ if (state != WL_POINTER_BUTTON_STATE_PRESSED)
+ break;
+ keyboard->keyboard->preedit_style = (keyboard->keyboard->preedit_style + 1) % 8; /* TODO */
+ virtual_keyboard_send_preedit(keyboard->keyboard, -1);
+ break;
+ }
+}
+
+static void
+button_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ uint32_t button,
+ enum wl_pointer_button_state state, void *data)
+{
+ struct keyboard *keyboard = data;
+ struct rectangle allocation;
+ int32_t x, y;
+ int row, col;
+ unsigned int i;
+ const struct layout *layout;
+
+ layout = get_current_layout(keyboard->keyboard);
+
+ if (button != BTN_LEFT) {
+ return;
+ }
+
+ input_get_position(input, &x, &y);
+
+ widget_get_allocation(keyboard->widget, &allocation);
+ x -= allocation.x;
+ y -= allocation.y;
+
+ row = y / key_height;
+ col = x / key_width + row * layout->columns;
+ for (i = 0; i < layout->count; ++i) {
+ col -= layout->keys[i].width;
+ if (col < 0) {
+ keyboard_handle_key(keyboard, time, &layout->keys[i], input, state);
+ break;
+ }
+ }
+
+ widget_schedule_redraw(widget);
+}
+
+static void
+touch_down_handler(struct widget *widget, struct input *input,
+ uint32_t serial, uint32_t time, int32_t id,
+ float x, float y, void *data)
+{
+
+ struct keyboard *keyboard = data;
+ struct rectangle allocation;
+ int row, col;
+ unsigned int i;
+ const struct layout *layout;
+
+ layout = get_current_layout(keyboard->keyboard);
+
+ widget_get_allocation(keyboard->widget, &allocation);
+
+ x -= allocation.x;
+ y -= allocation.y;
+
+ row = (int)y / key_height;
+ col = (int)x / key_width + row * layout->columns;
+ for (i = 0; i < layout->count; ++i) {
+ col -= layout->keys[i].width;
+ if (col < 0) {
+ keyboard_handle_key(keyboard, time, &layout->keys[i], input, WL_POINTER_BUTTON_STATE_PRESSED);
+ break;
+ }
+ }
+
+ widget_schedule_redraw(widget);
+}
+
+static void
+touch_up_handler(struct widget *widget, struct input *input,
+ uint32_t serial, uint32_t time, int32_t id,
+ void *data)
+{
+
+}
+
+static void
+handle_surrounding_text(void *data,
+ struct wl_input_method_context *context,
+ const char *text,
+ uint32_t cursor,
+ uint32_t anchor)
+{
+ struct virtual_keyboard *keyboard = data;
+
+ free(keyboard->surrounding_text);
+ keyboard->surrounding_text = strdup(text);
+
+ keyboard->surrounding_cursor = cursor;
+}
+
+static void
+handle_reset(void *data,
+ struct wl_input_method_context *context)
+{
+ struct virtual_keyboard *keyboard = data;
+
+ fprintf(stderr, "Reset pre-edit buffer\n");
+
+ if (strlen(keyboard->preedit_string)) {
+ free(keyboard->preedit_string);
+ keyboard->preedit_string = strdup("");
+ }
+}
+
+static void
+handle_content_type(void *data,
+ struct wl_input_method_context *context,
+ uint32_t hint,
+ uint32_t purpose)
+{
+ struct virtual_keyboard *keyboard = data;
+
+ keyboard->content_hint = hint;
+ keyboard->content_purpose = purpose;
+}
+
+static void
+handle_invoke_action(void *data,
+ struct wl_input_method_context *context,
+ uint32_t button,
+ uint32_t index)
+{
+ struct virtual_keyboard *keyboard = data;
+
+ if (button != BTN_LEFT)
+ return;
+
+ virtual_keyboard_send_preedit(keyboard, index);
+}
+
+static void
+handle_commit_state(void *data,
+ struct wl_input_method_context *context,
+ uint32_t serial)
+{
+ struct virtual_keyboard *keyboard = data;
+ const struct layout *layout;
+
+ keyboard->serial = serial;
+
+ layout = get_current_layout(keyboard);
+
+ if (keyboard->surrounding_text)
+ fprintf(stderr, "Surrounding text updated: %s\n", keyboard->surrounding_text);
+
+ window_schedule_resize(keyboard->keyboard->window,
+ layout->columns * key_width,
+ layout->rows * key_height);
+
+ wl_input_method_context_language(context, keyboard->serial, layout->language);
+ wl_input_method_context_text_direction(context, keyboard->serial, layout->text_direction);
+
+ widget_schedule_redraw(keyboard->keyboard->widget);
+}
+
+static void
+handle_preferred_language(void *data,
+ struct wl_input_method_context *context,
+ const char *language)
+{
+ struct virtual_keyboard *keyboard = data;
+
+ if (keyboard->preferred_language)
+ free(keyboard->preferred_language);
+
+ keyboard->preferred_language = NULL;
+
+ if (language)
+ keyboard->preferred_language = strdup(language);
+}
+
+static const struct wl_input_method_context_listener input_method_context_listener = {
+ handle_surrounding_text,
+ handle_reset,
+ handle_content_type,
+ handle_invoke_action,
+ handle_commit_state,
+ handle_preferred_language
+};
+
+static void
+input_method_activate(void *data,
+ struct wl_input_method *input_method,
+ struct wl_input_method_context *context)
+{
+ struct virtual_keyboard *keyboard = data;
+ struct wl_array modifiers_map;
+ const struct layout *layout;
+
+ keyboard->keyboard->state = keyboardstate_default;
+
+ if (keyboard->context)
+ wl_input_method_context_destroy(keyboard->context);
+
+ if (keyboard->preedit_string)
+ free(keyboard->preedit_string);
+
+ keyboard->preedit_string = strdup("");
+ keyboard->content_hint = 0;
+ keyboard->content_purpose = 0;
+ free(keyboard->preferred_language);
+ keyboard->preferred_language = NULL;
+ free(keyboard->surrounding_text);
+ keyboard->surrounding_text = NULL;
+
+ keyboard->serial = 0;
+
+ keyboard->context = context;
+ wl_input_method_context_add_listener(context,
+ &input_method_context_listener,
+ keyboard);
+
+ wl_array_init(&modifiers_map);
+ keysym_modifiers_add(&modifiers_map, "Shift");
+ keysym_modifiers_add(&modifiers_map, "Control");
+ keysym_modifiers_add(&modifiers_map, "Mod1");
+ wl_input_method_context_modifiers_map(context, &modifiers_map);
+ keyboard->keysym.shift_mask = keysym_modifiers_get_mask(&modifiers_map, "Shift");
+ wl_array_release(&modifiers_map);
+
+ layout = get_current_layout(keyboard);
+
+ window_schedule_resize(keyboard->keyboard->window,
+ layout->columns * key_width,
+ layout->rows * key_height);
+
+ wl_input_method_context_language(context, keyboard->serial, layout->language);
+ wl_input_method_context_text_direction(context, keyboard->serial, layout->text_direction);
+
+ widget_schedule_redraw(keyboard->keyboard->widget);
+}
+
+static void
+input_method_deactivate(void *data,
+ struct wl_input_method *input_method,
+ struct wl_input_method_context *context)
+{
+ struct virtual_keyboard *keyboard = data;
+
+ if (!keyboard->context)
+ return;
+
+ wl_input_method_context_destroy(keyboard->context);
+ keyboard->context = NULL;
+}
+
+static const struct wl_input_method_listener input_method_listener = {
+ input_method_activate,
+ input_method_deactivate
+};
+
+static void
+global_handler(struct display *display, uint32_t name,
+ const char *interface, uint32_t version, void *data)
+{
+ struct virtual_keyboard *keyboard = data;
+
+ if (!strcmp(interface, "wl_input_panel")) {
+ keyboard->input_panel =
+ display_bind(display, name, &wl_input_panel_interface, 1);
+ } else if (!strcmp(interface, "wl_input_method")) {
+ keyboard->input_method =
+ display_bind(display, name,
+ &wl_input_method_interface, 1);
+ wl_input_method_add_listener(keyboard->input_method, &input_method_listener, keyboard);
+ }
+}
+
+static void
+keyboard_create(struct output *output, struct virtual_keyboard *virtual_keyboard)
+{
+ struct keyboard *keyboard;
+ const struct layout *layout;
+ struct wl_input_panel_surface *ips;
+
+ layout = get_current_layout(virtual_keyboard);
+
+ keyboard = xzalloc(sizeof *keyboard);
+ keyboard->keyboard = virtual_keyboard;
+ keyboard->window = window_create_custom(virtual_keyboard->display);
+ keyboard->widget = window_add_widget(keyboard->window, keyboard);
+
+ virtual_keyboard->keyboard = keyboard;
+
+ window_set_title(keyboard->window, "Virtual keyboard");
+ window_set_user_data(keyboard->window, keyboard);
+
+ widget_set_redraw_handler(keyboard->widget, redraw_handler);
+ widget_set_resize_handler(keyboard->widget, resize_handler);
+ widget_set_button_handler(keyboard->widget, button_handler);
+ widget_set_touch_down_handler(keyboard->widget, touch_down_handler);
+ widget_set_touch_up_handler(keyboard->widget, touch_up_handler);
+
+
+ window_schedule_resize(keyboard->window,
+ layout->columns * key_width,
+ layout->rows * key_height);
+
+
+ ips = wl_input_panel_get_input_panel_surface(virtual_keyboard->input_panel,
+ window_get_wl_surface(keyboard->window));
+
+ wl_input_panel_surface_set_toplevel(ips,
+ output_get_wl_output(output),
+ WL_INPUT_PANEL_SURFACE_POSITION_CENTER_BOTTOM);
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct virtual_keyboard virtual_keyboard;
+ struct output *output;
+
+ memset(&virtual_keyboard, 0, sizeof virtual_keyboard);
+
+ virtual_keyboard.display = display_create(&argc, argv);
+ if (virtual_keyboard.display == NULL) {
+ fprintf(stderr, "failed to create display: %m\n");
+ return -1;
+ }
+
+ display_set_user_data(virtual_keyboard.display, &virtual_keyboard);
+ display_set_global_handler(virtual_keyboard.display, global_handler);
+
+ output = display_get_output(virtual_keyboard.display);
+ keyboard_create(output, &virtual_keyboard);
+
+ display_run(virtual_keyboard.display);
+
+ return 0;
+}
diff --git a/clients/matrix3.xpm b/clients/matrix3.xpm
new file mode 100644
index 00000000..ef42f81d
--- /dev/null
+++ b/clients/matrix3.xpm
@@ -0,0 +1,692 @@
+/* XPM */
+static char * matrix3_xpm[] = {
+"512 598 91 1",
+" c None",
+". c #020202",
+"+ c #020602",
+"@ c #020A02",
+"# c #061606",
+"$ c #071E07",
+"% c #061206",
+"& c #020E02",
+"* c #061A06",
+"= c #0A220A",
+"- c #0B310E",
+"; c #0E3E12",
+"> c #0A260A",
+", c #0E2A0E",
+"' c #134A16",
+") c #0E5A0E",
+"! c #104616",
+"~ c #124E1A",
+"{ c #166A12",
+"] c #127212",
+"^ c #1E921A",
+"/ c #168A16",
+"( c #1A5E2A",
+"_ c #165626",
+": c #227232",
+"< c #3A965A",
+"[ c #268A3E",
+"} c #227A37",
+"| c #1E662F",
+"1 c #1E6A32",
+"2 c #227E3A",
+"3 c #16320E",
+"4 c #36822A",
+"5 c #4A9E72",
+"6 c #4AAA6E",
+"7 c #42862A",
+"8 c #4DAE7B",
+"9 c #1A3612",
+"0 c #224616",
+"a c #264E16",
+"b c #32621E",
+"c c #1A3E12",
+"d c #265A1A",
+"e c #367622",
+"f c #3E7626",
+"g c #5A8636",
+"h c #4A962E",
+"i c #669A3E",
+"j c #82AE52",
+"k c #3E9222",
+"l c #4AA232",
+"m c #66AA3E",
+"n c #6ABA3E",
+"o c #82BE4A",
+"p c #76AA4A",
+"q c #96BE5E",
+"r c #1A9E1A",
+"s c #22BE22",
+"t c #2AD62A",
+"u c #6AEE8A",
+"v c #4AEE4A",
+"w c #50CE94",
+"x c #26CB26",
+"y c #26D226",
+"z c #1EAE1E",
+"A c #2ADE2A",
+"B c #2AE62A",
+"C c #2EEE2E",
+"D c #56F24A",
+"E c #66F65E",
+"F c #127E12",
+"G c #42EE42",
+"H c #3AEE3A",
+"I c #2B9F4D",
+"J c #3EBA6E",
+"K c #2AAA4E",
+"L c #56F262",
+"M c #66C29E",
+"N c #68D6B2",
+"O c #52BE8A",
+"P c #3CAE67",
+"Q c #2A9646",
+"R c #6AC6AA",
+"S c #6ACAA2",
+"T c #C2D672",
+"U c #B2D26A",
+"V c #4EAA2E",
+"W c #9ECE5A",
+"X c #5EB632",
+"Y c #62C299",
+"Z c #8ACA4A",
+"..............................................................................................................++@@@@@@+++....................+@@@@@@@+........................................................................................................................++@@@@@@+....................+@@@@@@@+................................................................................................................................................................................................+@@@+.......",
+".............................................................................................................+@##$$$$$#%@+..................+&##$$$$#&+......................................................................................................................+&#$$$$$#%+..................+&#$$$$$#&+..............................................................................................................................................................................................+&#$#%+......",
+"............................................+@&%#***%&@+...................+@@@@@@@@@+....................@##=--;;;;;;;->$%@...............%#>-;;;;;-,*&+...................+&%&&@+............................................................@&@@+.......................@%*--')))!;-=%@+..............%*-;;!!!!;-*&+.....................+&%###%@+............................................................................................................................................................@#$-;;;-=*@+...",
+"............................................%**==,-->=*%@.................@%*$$$$$$$*##%&+................%$--;~'))){))!;-=*&.............&#--;)){)~!--=#@...............+&###$$**%@+........................................................@%**$*##&@....................&=,;']^^/])';,*@.............&$-;)){//]);-=#@+...................@#*==>=*#%@.........................................................................................................................................................&*>;!~))!->*@...",
+"...........................................+%**===,,>**%%+..............+@&%************%@+..............@#*,;!~~))())_';-=*@............+%*=-;~({_~;-=**%+.............+&%*$**$**%@+........................................................&&#*$*#$%%+...................@*=-!:<<[_~!-,#@.............@=-;'(}[<}_-=*#@+..................@%#**>>>*%%&+.......................................................................................................................................................+%$=-!)))!-,$@...",
+"..........................................+&*$>,-;;-->$$%+..............@&$$$$>$$$$$>$$*#%&@.............@#>-;'((|(||1(~!--$%+..........+&**,-;_:}2(~;->$#&@............@%*$=>>=>$$#%+......................................................+&$*>=>=**%@..................@&*>3_456<:(~;,*@............+&=;!(:786<(;,$*%+.................@&#*>---,>$##@+......................................................................................................................................................@%*-;_|1|(;-*&...",
+"..........................................@*=,90abbba3,*$&+............@%#*,333333333393>$#@+............&*,cdbbeeff4febd0;=*+.........+@%>-cabfghifbdc9=$#&+.........+&%$,39c09c3,=$@.......................++@@@++.......................+&#,39c993=*%+................+&*=3;bijjifbac,*@............@&=;adfijjifc9>*#@................+%*>,9abdac3*$&+......................................................................................................................................................&*>9aefggfa;=%...",
+".........................................&*=;abgijjigbc9=#@...........@%*,c0db{b_0_db:bdc3=$&+..........&%=9de47kkklllk4ebac=#........+%$>c'be7mnonphebd03=*&+.......+&%=3cd:eeee(a3=*@....................+&&*=*$*##%@+..................+%*30d:1e(d09=%+..............+&$>9cdfmqom7bd09*&............@*>9db4ioqpgbac3=%+..............@#*>;afgmiifac,*%+...................+@&%#&@+........................................................................................................................+&*,;abipnpif03#+..",
+"........................................&$-~]rstuuuvws/{;-*+........+&#-;)]rsxyszrrssysz/{';$&........+%=-;{/yABCCBCACCtyz^];,+......+%,;!{/sABDvuEvBAsrF{~-$&......@#=-;)/zxBBAAs^{!-*&.................@%$,-;;;!;;;-,*&+...............@*-;)/zytAsr/{;$%+............+%>;~F^sxAvGAs/]);>*+...........@*-~F^sxGvHAsrF);>%+...........@%$-;~]IxDuvDJ/{';>$%@...............&%$>----,$&+......................................................................................................................&=-~]^sADEDtKF',@..",
+"........................................&=;~FIyHDEvDAsr]~-$&........+#,;)]/zsABxszssAAysrF);-#+.......@#--!]ryBCBCBCCCBBxsrF~,%.....@#>-!)]ryAHGEuELHBxzrF);-#.....@#*-;'{/yBACBByr]!;,*&...............@#=--!!)))))~!;-=%+.............+%=-!]rsBBBtz/];,*@............&=-;]^zxABCByrF);-=%@...........+#=;)FrsACBBysrF~-=#@.........&#>-;!{/KyDDDGAz])';-,=&.............&%=>;;!;;->=%@.....................................................................................................................&=;~]rJAGLDGs])>&..",
+"........................................&#=-~][6MNNOP[{!->#&........+%*-;)(F^rI^/FF/IrQ/])'-=%........+&>,;~]^rIzKzKsKzr/F(~;=@.....&*>,;!)][IKOwNwOPKQF|~;,*&.....@#$>-;~]/rrzKr/F)->*%@..............+&*=--''~___~';,=*%@.............+&*,-']/rKr/F_!-*%@............@*=-!{2QIIIQ/(;->*&+.............&%=-!(}^IIr/[]);>$&@.........&*>,-;!(1<ONwO<}_!;--=*&.............@%$=-;!!;-=*%&.....................................................................................................................@*=-~{[POwO5}!-%...",
+"........................................+*>-a|75RNNR5}('-*#@........@&$-!__:[55<}1}}5554|~;-=%+.......&#*,-_1[QQQIIIIIQ[}1('-*@....+&$=-!(:}[QI68OOO66<}(_;-=&.....+%*=;;_|[[QQQ[2|~!->#&.............+@%$-;~(:f774e1_;,$%@.............@#$,;_:<865[1_;,$&@............@#>-~|}}[QQ[|~;>*%@...............&**-'(}[Q[[}|_;=*%@........+&*,;;c~(e<OSSO5:((_!--$&............+%$=;~b2e|a->#@+...................................................................................................................+&$>3~|}<P65[(c=#+..",
+".......................................+@*=9afgjTTTUjge0-=%@.......+&%=9cbbgiqqjg7ggjqqggba;=&.......+%$=-;a17khkhhllkh7feb09>@....@#>-abe7khhlmVmmjqqjgedc9>&.....@*>;adbe47k4k77ebac,*%+...........+&#*,9a:gijqqqjgea3=#@.............@*=;0bgpqUqifdc,$&+............@*,cde7kk777ba3>#&+...............@**,0de4kh7hfb0,$%@........&*,;abbbefiqUUqi4bbbba;,#............@&$3afjjjifc,*&+...................................................................................................................@%=,;df7hhh4fd3*@...",
+"........................................%*9c:kiqTTTWjl7bc>$&........&$>0d:4ijWqoikhmqTqp7eb0,#+......&*=9cde4hlVVVVmVXVVk72b',&...+&*3a14klVVVVVVVXqWWoi7bdc>#+...+&$3cde4k<lllllkk4ea9>*&+..........&*=3;d:75jqTTWomk:03=%+...........+&*30b4iqqUWpgba3*%+...........+%=30e[llVVlke_9=*&+...............+#=3cb4kllVlked;,$@.......@%=3d1e47hlpWUUWjhkk44ed9$@..........+%$,cbgqUWqgbc,$&...................................................................................................................&*,9dehllllheac=%...",
+".......................................+$-~]zxCDuuEEvAyr{;,*+.......*-!{rsxADEEGABBGuEuGsz/{;=@....+%$;~]/zsAACGHvGvGGHCCAyz/~$...%>;~FzAABCCCBCCCCGvDHtsz/{;$@...&$-~]rzxBCCBCCCCBAs/{;-$%.........@*-')FrstCGLuDvGHtzF~->&...........&=-~FzyBGDEGAs/{;,*@...........&=-~FztACBABxr]'-=@................+*-'{^stBCBCBs/);,*+.....@#,;)/zytBHHDEEEEvvBBtyz^{-#..........@$-;{IxDEEDyI{!-*@.................................................................................................................@$-'{rxAGCAtsr]~-%...",
+".......................................@#;~]ryADEEEEvBs^]!-#@......+%-;]^zyBHLLvCAAHvuDHsz/{!*+.....#-;{/zsACCBGLLLLLLGCCBBs/)=+.+*,;~FstCCCBCBtyxytBAAysz/{;*+..+%=-~]rstAABACBBBCtxr]'->*&........@=-)F^syBCCAAxyAAAzF~;=%..........+%-;)FsBACCBAs/]~->#+...........@=;)FzACCCBBs/{;,#+.................@$;)FsABCCCAs/{!-*&.....+$-;)/syBHLLEEuEuELGCBxs/);*.........+#,;~{IALLLDyzF);=%+...................................................+...........................................................@#>;~]IAGvHysz/{;-*+..",
+".......................................+@*-;12IOSNSS8I});,*@........@*,;{F2IPwwPI[QIOwO<2|);,%......&=,!{F^IKKJwwNuNwwwJsKr}_;%...@*>;~FIKzKrI^/2F/[QQ^2]|);=&....&*>-!(]/^IKrKrKrrr^]'-=*%@........@*=;)]}^IKIQ2}}/[/]'-**@..........+@*=;(/QIQQ[[]);-*#+............+#>-~2PJwJK[]~;=#@...................&$-!{/IJJwP[(;,=#@......&$,;)F[QPwNwNuNNuNwJI^F);,#.........&**,;~:5wwNO<1_;,$%+............................................+@+++@@&@@@@@@@@+++................................................@#$-;):5wJP[F_!->#+...",
+"........................................&*=;_2g8RNNR6[:(;=*&........@#=-~(1[5OO6[2}<6M6<|_;-=@......@*,;_1}[QKKOwNNNNSw6KQ[:_-#...@#*-':[IIIQ[[21::}22}:__!->&....+&*,;_(12[[QQQQQQ[2|~->$#@........+%>9'(}}QQQ}:1111|~;>$%&...........@#>-'|[2}}:(~;,*#+.............@%=-_26OYO<:_;,$%@...................@*,3~|[8YS6<(->$&+......@%=-'(14KONNNSNNNSO5[}(~-*&........@&#>,3_|<YRN6<1!;>*$%@...........................................&%###%#%#%#####%##%@...............................................&#=,9_1<OO<}(';=#+....",
+"........................................&*=9aegjTTTqpgea;>#@........@#=3adb4impifffgpjigdc9>$&.....+&*-;d:4kimpoqWUUUWoplkkea-%...@$*3c:7hhhhhg7feef4febbac3=%....+&*,;a(be7k7h4h7<44ba;>=$%+.......@#>9abe4hik7ebbbbba-=$%%+..........@&*-cde4ebddc->%@+.............@#=9dgjWWjiea3=*&....................+&=-cbgqqUqgb;,$&+......@%*,cd:fijqUTTTTUqji4eda-*&......+@%**>3caegqWWjiba;9==**@........................................+@%$>>>=>=========***%@.............................................+&$>90dfippgba;3*&+....",
+".......................................+%*30|7ijTTTUpl4d0=*&........&*>c_be4hiig4ee7iiifdc3=%@......&=3abfhlXqZZZoZWUUZnXVk4d3#...@%$3aehVVlnnnVllhkkk4eba09=%.....@*>3ad:4khllIlVllk7eda9=*&+......&*>0db4hmnmh4}ee:(ac3=$#&.........+@#>,0debdaa93,*#@..............&*>9dhoWWZifd0,$@.....................%,cabgqUWqifa3=#+......@#$,cab4hmoqTTTUZnmk4bdc,*@.....+#*=,c!a(b7mqUUqi7bdcc33=#+.....................................@%#=,333;0cc0;0c;cc;c9,>*%...........................................@%$,;de4hlnmhedc,*%.....",
+"........................................#-']rxBLuEuEvts/);=%........&$3)F/rzssszrrzsyxs/{!-,#+.....+#-~]rxAGvEEGHHvLEEEGCCts^)$...&$-;{rxCCHvvDGBBCCBAysr/{'-=+....@#,;~F^sACCCCCCACCtyz/]',#@.....+*,;{/zsAGDvtyszrr^F{~;-,=@.........@#=;)F/^])';;-*&..............+*-;(^JvEELtK/{;>#....................+#-~2IyEEEEtK]~3=@......+*-;~]^zxACLEEEELGByz/]~;=&....@*--~)F^rzsxGEEEEHssr^F])!,#....................................@*-;~){]{/^^////////FF]{);=@.........................................@*>;)FzsxABCAs/]_;$%.....",
+".......................................+*-;_^stDEEELGAs/)-=&.........%,'{F^/rrr/FF/rzz^]);-*@+.....@*-~{rsAAGLLvCBCHLLLGCCAsF'*+..@#,;)/stBBHvGHCCCCCCBsz/{);>%....@#=-!{/stCCCCCACBBAxz/]!-$&....+@*>;{/zsAvLDBxsszr//F]~!-=#.........+&=-!]]])!;-$%++..............@*-;)^xGEEuAs/);-&....................@$-'{IxGEEDAz]~-#+.......#=;~]^zyBHvLEuLGCBxz^]~;$@....%--!{/IzsJyAvEuELGAtyssr/);*...................................+#-;']/rKzzzsssKszszszzK/])-#.........................................%*-;)/sABCCBys/)!-$+.....",
+"........................................+*-;~:[PSNNOP}|!-=#@.........+&$-;;;~!!;;;;!'~'-,$#@.......@%>-'(][QPwwJKKKJwwOIQ}F{;>@....+*>-~{F2[QPPKKKKKJJKI/})!-*@....+&*=,;)FQrKKKzKzrr/F{~;>*%+....++%*=;~:2<OwwPII^[F]|_)~!-=&...........@%*--;,=*#@+................+&>-;([OwNO<:~;=*@....................+%>-!(<OwNO5:~-=%........&*>-;_]QPOJ86PPJJPQ}{);,#.....&$-;|[68OJOYSNNNNNOOO665[_-*...................................+&=-!1<588866666O6666668<}_,%.........................................&*=;~]/IrIQQ2{!-=*@......",
+"........................................+&*=;_:<ORN6<|~;,$&...........+&**>==,>====>,,*$%&+........@%*>;~(|[5OO6<[[<OY6<1|_;-$@....+&#--;_(:2[QIIIKPOO8<[}('-*+.....@#$=-!|}QQQPPIQQ[2(_;,*#@......+&$=;~(1g8MM6KQQ}1||11(~;=%............@@@&&%%@....................@#=-a46YSO<1;,$#@....................@#*,;d26YSO<|;,*@........+%*,;_|<6O6<24<5Y8<:(!;=%+...+%>-ab5RRRRRNSNNSNNNRNNRM5b9#....................................#=3af6MMNMNNRNNMNRNNNRRMg(-#.........................................&$,;_:[IIQ2:(;-$#&+......",
+"........................................+&*>30biqUUjgbc9=*@.............@&*##**$#$*$#$#%@..........@%$=9cdbgpqqmg7giqqjgbbac,%+.....%*>>30dbef7hhlmmoZoik7e09#.....+@#$=90:7hhVmmlkh4edac-=#@......+&$=cabfiqUUjmll74eeeeb_c=#...............+++++...................+&*,9agpWWqgb;,>#@...................+@#*,;dgjWWjib;,$&.......+@#*,cabijqpg7fgjqjibdac=&+...+%>3agjqTTTTTTTTTTTTTTTTUifc#...................................+#>;dgjTTTTTTTTTTTTTTTTTUibc&........................................+#=30b4hhkkeba-=#@@.......",
+".........................................@$=3abiqUUqib03>#&..............+@@@&&&%&&&%&&@...........&*,9abe7kpqWjlkipqWoi7e|a3$+....+&*=3c0|e47klVVXXZZoXVk7ea>@.....&*,3cdeklVmXXmVlkke|dc3=%......@#=90d:7iqWWZonXlhk7kk71a9#+......................................+%$-0bgjWUWifa9>*&+...................%*,90eioWUqibc3*%........@#>-0dfioZp57kipWqi4bac>*+...@#>!cgjTTTTTTTTTTTTTTTTTTjf0*...............+@%%%%%@+...........+%30agqTTTTTTTTTTTTTTTTTTjb0#.......................................@#$9a14kVVVk4(c3$&@........",
+"........................................+%=;~FIADEDDyQ);-*@..................+@@@@@@@@+...........&=-!{^xtvDEEEEvGHDuEDHtyz^)-%...+&=-;)F^sxxxtACCCHGLGHCBAs/!#....@$,~{FzsABCHCHCCCCtxszF);$+....+#,;)FrsxHvvvDvvDGGCHABts/'>@.....................................+&*-!{ryuEEEtKF)'-$@..................+$-;)FrtLEELAK{!-$@......+%=3!{^ztvvHxssyHvGAs/]~3>@...&=30|<YuuEuEuEuTuuEuEuuuNXQ_>.............+&*>--;-->$%@+........+#,~e5wuuuuuuuuuuEuuuuuuSX[0$...............+&##**%&+..............+$-!{^sAACCCAs/)!-*@........",
+".........................................&--'{rwvLLGy/{;>#+......................+...............+%>;)FKGLuEEuEuELEEEEEGCAyr);%...+*=-~]rzxAAyAAytBACCCBCCBs/)$....&>;{FrsxBBCBGBCCCBCBtsr]!,%....&*,;)/syBACHHvLLLELLHCBBs/),@......................................&=-;)[stvvDAs^{!-=#+.................+*-!)FIyGLLDxr{'-$&.......&$-~{/rsyAyzzrzxAxsr/{~-=&...+#,;~]rsXxwvLEEEEEEGtxxss^]!*+...........+#*,;!~~!;--=*@.........&>;~FrVsXsXxxXxXxxxXsssz/{;#..............@#*=>-,-=#@.............&$;)]zAACCBByzF);,#@........",
+".........................................+%*-;|QJYw6[(;>#@........................................&*>;(<8NNNSNNNNNNNNNwJKI/|;=&...+%$=-~]//^^/////^[QIKKKKrF'-#....+*=;~{]F/QIJJKKKzKzIr/|!-*@....@#=-'1/QIIKKJJwwwNNNwKKr/(;*.......................................+&$,;_25PJPI[]~-,$&+.................+&*,;(2<PJPP[(;-$%@.......+&#>;~(:]11_~_)|2](~;->#@.....+%=,-c~(||e[6wNSR542:b(a!->&...........+&#$>-!~)~'-,*#&..........@$>3!!))()d|((|(dd)0_!';-%+.............@&**>=,==*$%+............&*>;~F^IrrQ/F(;-*#@.........",
+"..........................................@#*-cb<55<1;,*%+........................................+%*-0:58OMRSRRRwMMRO85[}|~-=+...+&*>;_1}[Q<<<[[}[[Q5KIIQ[(!,&....+&*-;!_(|}[QKIIQQQQQ[}|;,$@....@#$-'_}[QQQQIPJYORNSO5[2:~-#+......................................+&%*,!([<I<Q}|~->#&@.................@#$-0(1[<I<Q:~;=*&..........&=,-;!!';----;';;-,=*@+......+&*=>--c_(:5RNSO<:d~!;->=%+...........@&#=-'(|1|_!->*@..........+%*=>,3,-3c;c-33----,,>=*%+............+&$=,-;;;-,*#&...........+&*=-!|[QQ}}}(~-$#@..........",
+"..........................................@#*>cabebb0->*&.........................................+%*,;dfg5i5mm8mm8m5ig7:da9=#+...@%*,cd47<imjpi5hhijopmkh4bc=&.....@&*,90_db4khlhk747774ba-$@....@#=9ae4hhhhhhhilmjWqjge1b0-%........................................@#=,cbf7khh7eac=$&+.................@**3abf7hhh7ba3=#@..........+&>>,,-,,==>,,3,=*$#%+........+&**>,39afiqUUjiba09-=$%+...........+&*$,9~bee:bd;,*%+..........+&***>=>=>=,=>===>=>*$*#@.............@%>ccadbda;,$%+.........+&#>=cae4hhfebd0,*%+..........",
+"..........................................+%$,-0dbbd93=$&..........................................@=,3df7hlVVXXXVmVlVh4ba;3=%....+*>30ekVlXoZWommXnZZZmVh4b0,@.....@&**-99_b4hVVVkkk<llked9=&....@*>cd4lVVVllhhhlmoWUol7f1d9$+.......................................@#$9'b4klllk7eac=$&................+%=3017klllk7:a9>#@...........&&#*$>$*$$#$*=$$*%&+..........+&#*,90dfiqUUqifd;3=$&@............@*$-9d:kkkk4bac>$&...........@&#$*$*$*$***$*$#*##&@@+............+#=;abe474ed9=*%+........+#>30~ehmpl7eda9=$&+..........",
+".........................................+@*,;_{/^^2{'3,#.........................................+#=c_}zxtAHHCCCCCCCCts^F';>#....+$-'{^yCCCvLvvvGGvGvGBtxrF',%.....+&*>;')]^stCACAyAACBAs/)3%...+#-;{/sCCCBCBCBBCCvLEDHysz^];&.......................................@#,'{^stAACCAs/{;-=&..............+%>;~FzAACCBts^]!-$&...........@&##$>,=*#&##$=*%&+............@#$3!|[KwEEEvAKF);->%+...........+$-;)]ryBHHBAs/{;-#+...........+@%%#%##*=#%#&&&&#%&..............+%-;)FrsABAsr{!-$&.......&=-~]/rsALGtJ/F);-*%...........",
+"..........................................@*-;'FQzr/{!;,%+.........................................%,;'FryACBHBCCCBCBAyzF)'-=&+....%=;)^sACCBHGLLLLLGCAtzrF)',@.......@#-;)]^sAACBAABCABAz/);*+..@#>;)/stBCCCCCCCBGGLLGHCAsr{;%........................................%=;)FzyBCACAs/{!-=*&.............&=-;)/zACAABAz/);>#&.............+@&%%%&&@.@%%%+...............@*-;)FztLELDxr{~-=#@............&$;~]^sAvLvHts/);,=&..............+.+.+@+++..+.+++...............+*-!{/zxACBAzF~;=%.......#=;~FrzxtGvAz/{~;$%............",
+"..........................................+&*-;|[I<[(-=*@..........................................&*>-'{F[IKPKKKKzKrQ/]'->$%+.....@%=-)]F2/QIKOwNNwPQ[])';=*&.........+#=-!)F^KzKrrrrKr/F'->@....+#=;)F^KKKJJwwOwOOOOJJO6P2~-&.........................................&*-')F/rIzI/{~;>#@@............+@#=-!(FrKz^//]'->*@...................+.........................+#>-!1<ONwOQ(;,*%+.............@%>-~1[6wNSJI2(;,$&+..............................................@*-!{FQKJJP[_->*@.......&*>;~:2QIPI<:~;,>%+............",
+"...........................................@#=c15O8<|;>#@..........................................+%*-;_|2}QIIKIIIQ[}|_;-*&+.......@$=-;~_((:[8RNN6<:(!-=**%@..........+#*=;(2QIIIQQQ[[2(;-*@....+@*>;(}[<5YRMMNMMO6P6YRR62a,&.........................................+*=-'(:2QK<[1~;-*#+............+%*,;~|}<<<[:_~;,*&+..............................................@*>9_78YR840->$&+.............@#=-~1[56O852(!-*&+...............................................+#>;(2<8YR6ea-=%@.......@#=-_:2[[<[:(;,*&+.............",
+"...........................................@#=0bijjib3$%@..........................................+%*>9abe47hlllhh<7ebd9>$&+.......+%*=-339cafiqqqpfd0->$&@............+%$*3_e47477744ebd3=$@....+&*=9a:47ijUUqTqqjiimjqqjg0-+..........................................&*,9cdfgjpieda;>#@............+%$>;dbgppi7ba;,$%@...............................................@#=3agiqjifc3*#@.............@%#=9af7giig7ba3>#&.................................................#=9aegjqqpfa,*%@.......@*=cdf77h77ba;>*&+.............",
+"...........................................@#>9agggfa9*#&..........................................@#$3c(e4klVVVVlVVlkea9,*&........+@&%#*>>9abhmooifa3>*%&.............+%#>3cdee44ee:bbd03$%@.....@#>,0dbfggiijjjiih4ggjggb3=&..........................................@#=-caemoom7eda3*@............&#,cdb7loZp7ba9,*%+...............................................+%$,9dggggd0,$&+.............+&*-c|4ll5hhfd09=*@+................................................%=9cbggiggb0,$%+.......&$,0|7klllke_3=$@..............",
+"...........................................&$30ab1bd09>#@..........................................+$-'{/zxtCCCCCCCBBtzF~-=#........@@@&#$--!{/xtHCxK]~-=#@.............+%$,;!)]F///F]()~'->#@.....@#>-!(]}[4hhhkhkQ^^Ik7fbd0,@..........................................@*,;!{rtvvtJz^])-%...........+&=;{/zsADLGK^{!->#@...............................................+%$30_|::(a;-$&+.............%=-'{rxCBBAxzF);-=@.................................................%>;~{:474bdc->#+......+%-!{rxAAAAs/{;-*@..............",
+"............................................%$-;;'';--$&...........................................+&>;)/rsAACCCCCABByzF~-=@.............+#>;'FzAAxs^]~-=%................%*,;!~'))))~';;->#&+......@#,;;~))){){{]{F]{){(__;,*@..........................................+%=-'(^JGGAsr/{!>#+...........@*;)F^syBwAzF);-#&+................................................+*,-;!'~;;-$%+..............@*,;]rtABxsz^]!;>%+.................................................+#-;!))))~;--#+........*-;{rsyByszF);>#+..............",
+".............................................+%*====%&+..............................................%=-~{]FQrKKrIrr^F|'-*&+..............@#=-!{//2]';,$#@.................+&%*=====,,=***@+..........@#$>,--------;-,-,-=,=#+............................................+%*>;~1241(~'-,%+.............@=-;!(14}:~;,#&+...................................................+&#*>=>*#&@.................&*=;)FFFF1)!;>*%+....................................................&#$>-,,==#&+.........&=,;)]FFF{);->*@...............",
+"..............................................+&&%%&@+...............................................+#-;;~(|}}2}221(~!-**@................@**-'_(_!->#&+....................@&#*&%%&&%#&+.............+@&##*****==*=>***$#&+..............................................+@**-99'c-->$%&+.............+@*=,,-cc3-=#&+......................................................+&&#%&@+.................+@#>>;_(_!--,$%%+......................................................+&&&%##&@...........@#=-;_((_!-->%@+...............",
+"................................................+..+.................................................+@#,-3cc_db_d_0!;-,*@+.................@#=-9;3-=$%+......................++++.+@@++.................++&%%%%%%#%%%&&@@@+................................................+%**>>,,=**%@+..............+@&#*$>>>**%%@..........................................................+@+....................@&*=3ccc9,>*#@+.........................................................+++@++............+%*39ca_cc,=*&.................",
+"......................................................................................................+&**=,,3993,>>==>*%&..................+@%*>=>**%&+..................................................++@@@@@@+++@@.......................................................&***$*#%&+.................+%%#%#%%*%&@+.................................................................................+%*=-9c->=*%@+............................................................................+&#>3999c,,*#&.................",
+"........................................................................................................&#*$$>>$#&&@%#*#@.....................+%***#&@+.......................................................................................................................+&%%#%&@+...................@@&@%&&@++....................................................................................@&*$>>>*%#&..............................................................................+@&#$>>==**%@+.................",
+"............................................................................................................++++......+...........................+......................................................................................................................................................................................................................................................+.++++++..................................................................................+.+++++.+....................",
+"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................",
+"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................",
+".............++@@@++..........................................................++@@&@@++....................+@@&&&&&@@@@@+.......................+@@@@@++...................++@&&&%@@@@++........................+@@@@@++................++@@&&&&&&&&&@@++....................+@@&&@@@++....................+@@@@@@@@+...........................................................................................................................................................................................................",
+"..........+&&#*====####@+.................+@%#######@+...................+@####==,=>=*##&+...............+&#*$===>===**%&@.....................@&**$*$*#%@+............+@###==>>,,,>>=*##%@+.................+@#****===###&@........+@###==>,,,,,=,,=>=*#&+...............+@#*====>$**&&@.................@%%**$>$=**#&++................................................................................................................................................................................++@@@@++...............",
+".........@#=>-;!~~)!!;-,$*%@+..........@%*$>-;;;!;;;>$#&@..............+&#>-;;;)){)~!;;-,$%@+..........+@$>-;!){{))~!;;-,=#+.................+&*,-;;;;;--$#@.........@#$,-;!)){)))))))!;;-$#@..............+&$=-;;!!~))!;--=%+....+&$>;;!')){{{{))){)~!!;,=%@..........+%*$-;;!)))~';;->=#&+............+%$,-;;!~~~!;--$$%@+...........................................................................................................................................................................+@#$=>>,>$****&+.........",
+".......&%#*-;!))]F]])~!;--=*@.........%#=-;;'))))()~';>*&@............@#*,-;'){]//F]{)'!--=*%@.......+&#=,-;'{F//^F]{)~;;->$%+..............@%=--!~))))';-=*&.......@#=;;~)]F///////F]{)';-=%............@%#=,;;!)){]]]]));-=&....@#>;!)]FFFF////F///]{)~!-=*&........@#$--;~)]F/F]{)~';->*%@..........+#*,;!))]]]]))!;--,*&.........................................................................................................................................................................@&#*=---;-;-;---=#&........",
+"......+&#*$>;;~{]1]1(~;;,>**@+......+@%$*=-;!))(({{);,$%&+............@**=--~){]}/}]|)~;-,>$#@.......@&#*>-;!)]1F}F:{();-,=*#@..............&#$,;!~)~)~!;,*&@......+&$=-;')(F}:}11F:]|_)~;-*&...........+&#*>>-;!)(]F1]()~;-*&...+&$=-')({:F}F}F}F}F}]_)~;-*#@......+@&#*>-;!~(]1F1{)!;-,>$#&.........+&**>-!)({{]|)~!;-=>#%+.......................................................................................................................................................................+&&*$>,----;--->=>*#+.......",
+"......@%**--;(_:}[}211_!-=>$#+......+&**,-;!((1}2}}(~3=#&+...........@#$>--!_|}}[[[}}2(~;-,=*@.......+%*>-;;_|}}[[[}}:(!;-,=*&.............@#*=-'_((111(!->$&+.....@%*>;!((}}[[}[[[[22:1(!->%+..........@%*$=-;'_(}}[[}21(!-=@....&*>;_(|}}[[[[}[}[[}}:1(!->#+......+&%*,-;;_|:}2}21(_';--=*&+........+%*>-;_(}}2}:11_'-,=*#@......................................................................................................................................................................++%#*=,;;;!!!!;;3-=$*#@......",
+".....+&*>,cabff77hk77ebba9,=*@......&#>3;adbeggghigeb;,*%+..........+&*=9cabf7ghhhhhhg7bda;,>&......+&$=3cabf7g7h4hh4g7bda9,=&.............@$>-cbegggg7eb;,=&+....+&#=,;abfghhhhhh7k77eeba-=*+..........@*=3;0befgghhh77fba;>%....&*3cbef77k7hhhh7hh774ebd9=*@.....+&#>,;adbefgkh7k74fbba;3=*@........&$,cabff74k74febba09=*&........................................................................................+..................................................+@++.......................+@#$,3cdbbebeebbb0;,=*&+.....",
+"....+&=-cabegmmmVVVVmpm7ebac>#.....+%=-'b47kmmoononm7d9>#&..........%*>cd:47lmnmVlVVXnml7eba,#+.....%*,0(e77lpmnVVVmXmmh7eb03$+...........+#=cabgijjjjji7b9>*&....@%$-0de7lpnXlVVlVVVlkk4b0,*&.........@#>9abgipppoonXVVk4b0,#+..+*,c{4hIhlVVVhVVVlVVVllkea9=&.....&#>3abe4hmmmmVVVmpml7eedc>%.......@#=9dgiimlVVVlVlkk7}d0,#+....................................................................................+@%%#%%@+.........................................+@&%*%#&@+.....................&#>90bgippjjppppi7bac,>#+....",
+"...+%>;)FrzyHvvvHCHHHDGtysrF~-&....&=;)/sABHvEuEuEuvs^);,*+.......+@=-~]zytHGDvvHCHGvvvHBAsr{-%....+$-)FzyBBvDLDGCHGDDvHAys/)-%..........+#-;{^swEEuEEEDy^{;-%....#=-)]rstHDEDHCCCBCBCCAsr{!-%.......+&$-~{^syuuEvEvDGCCBy^]!=+.+%-']zxCCCCCCCCBCCCBCBCCts/);=+...@*-!]rsAAHGDGHCHHHvvGCtsr]!>.....+%*-'{rxDvGCCCCBCCCCBtz]',&.................................................................................@%*$>----->$*&.....................................&#*=---;-->*#@+.................+#-!{/zxDuuuuEuDuHtsrF);,#+...",
+"...@#-;)/zstHDLvBCBCGLDCtxz/);#...+&=;)ryBCHDEEuEEEuA/{;,*@.......+#=;)/sACBGLLGCBCHDLvHCAyr];*....@*;'/sABHGLLvHCCvLLGCCBsr(;#..........@$;~]rstuEEEELDA/{!-#+..+#,;)/zsyHLLvGBCCCCCCAAxr{;-%.......@*,;)FKsHEEEuLLLGCCBxr]!$+..&>!]ryBCCCCCCCCCCCCCCBCBs/);>&..+%=-)FzyACGvLvHCCCvDLGCCAz/'=@....@#,;'FIAGvvCCBCCBCCBCBs/);*...............+@&&&@+............................+@%%&@+.....................+&##,--;!~~!!;->%..........@&&&&&&&&&&&&&&&&@........&#=--;!'!~!;-,=##&...............&$-!{^sADEEEuEuELEAsz/]~-=%...",
+"...&*=-!(F[QPJwJJKKJJwOPI/F|;>#....+#=;)2/IPOSNSNSNOQ|!-*&+........&*=;)F^IKJwwJPKKJJwwPKI^]'-&....@%>-)F^IKJOwJPKPJwwOPKr^]!-&..........&*>-~:[6YNSNNwO<1;,=@....&*,;~][QPOwOPKKKKKzrr^F);,*&......+&*$-!)2Q6MNNNNwwJKI^F);,%...+&=;{FrIKQKKKKKKKKJJsKKr});>&....@*>;)F[rIJJwJJKKJJwwJKIrF(;*.....&#=-;(25wwJKKKKKzKsKKK/(!-#..............+@%###%&@..........................+@&&##%&+..................+@&%**=--;!!~'';->#.........+@&&%%%%%%##%%%%%%&+.......@%*,--!'~'!;-,=**#@+.............%*,-!(25OwNNNNNSwOP[2]);-*&...",
+"..+&*>-'(}[QQ66<Q[[QIPP<[[}(!,&....+%$>;_1:[<88RNNS67(;-=&.........&#=-_}[Q<5665Q[[Q586PIQ[1~-%....@%*-'_}[Q<55<[[[<K6PI<Q[{'>%.........+&*>;~|2<6ORMRS8<(;=*&....@$=-__1}<6JPI[[2[[[[}|(!-=#@.......@#>-~:7<88OOO65<Q}}|_;-*@....&*,;_|}}2}}}}[[[II5KIQ[:~-=%+...&#=-~|[Q<I6P5QQ[Q<K6IIQ[}(;*+...+@%>9~(}<66<Q[2[[Q<6JP5[(;,&.............&%*$*$*##%&........................+%*$$$*##&+................@&%%$>,-;!_{2ee1_c-$+.......+&%%###$$$$$$$$$$###%@......@#>-ab1e:1(~!-->**#&@+..........+&*>-!(1Q5O866868O8<[21(',*@...",
+"...&*,;de7khhh77ebef4ghhhhhed9%.....@**3c~abffgjqUWjgbc-*&.........@$=-aehhhh77feebe77ihlhhea3%....@#*,0df474eeebeef7hllkh4ba,&.........@#=,0behhihimjZqgb9=*%....@*=3abf4hhik7eeeeee:bba;->*@.......%*,;agpjjmiig44febda93=#&....&*=3c0a_dddbbbee7hklkh4e03*@....@*=3de7hhlkh4fef47hhilkh4b;%....@#*-cdbehhl4eeebe4ipqZpiba9#...........+@%$*>,,>=$*#@......................@&$*==>,=$#&+..............+&#$==-9cabfgjjjpgd9,@......+@&$$>>======,=====**$*&.....@#,cgipjpg7bbacc,,=$%@+..........&*,;abf7hihgffghhlihh7e09>%...",
+"...&*9a|kVVVlhk4eebe4khVVVlkbc$+....@#*=,90adffpqUWqgb03=%.........@*>3d4klhk4f1b|eekhllVll4b;#....+%$,cdef4eebee1e7hllVVVl4d3#........+&*,;aehlVVV6XoZqibc3>&+..+@$,cde7kllVlhk444444e|d!9=$@......%#30dfioZoV5l<k4ee1(a;3>*@....@%$=,3c090!a(b14klVVVlkea9=%....%$30_7VVVVVhk4ee4khlVVVlke0>+...@*,0de47lVlk4e1ee7loWWqm7d9*+..........@*=33aadd09,=#@....................&*=39cadda93*#@...........+%*==3c'd(147hmoWZjifa9#.....@&#,3c0c0aadadadaaaa0c9,$&....&*3agqqZnXlk4e:bdcc,=$#@.........&=3ade4kVVh7fe4klVlVVl71a,*+..",
+"..+$-'FrtCBCBBxszrrzstBBCBByz{,+.....%#--!_|/QsGLEuDyI{;-#+.......+&=-!{rsxszzrrrrzstACCCCCxr(>....+#>;)]//rzrzrrzsxAHHHCCAs^)=.......&*=-~]rsAHHBCBHvLDAK];-#+..+&,;)/zyAHGGGGGGHHHAAysrF);-%.....&>;)FQxGuLGGBCBAtxsz/F{!->%....+&*=--;;!'){F^rxtBCCABxr{'-*+..@$-;{rsCCBCCAtxsssxCACCCAyz];&...#-'{^zxtCCCtsszzssCvEEvts^(-.........+%$-~{/rxxsz/~;,*@.................@#>;~]/rsyys/);-*@........+%*--~)]/rsyyAACHvGGHsrF!*....@*-;)F/rrrzzzsszszzzzr/F{;$+...&=c1KAGGGCCBHtAAsrF{!;->#@......@#-~]^zstACtxsssACCCCBCAr]',@..",
+"..+*;~]zyCCCBBysrrrrzyABCCCxr{-+......@#--!){^KtLLELxr)!-=&........&$-;'{F////^^//rsABCCCCAyr)=+...+@#-;)){]F/^/rzsAGvGCCABs/)=+......&=-!)/ztvvGCBBCGLLtz]~-=@...#=;)/zsACLELELLLLLGHAxz/{;-#....@#-']^zxHvLvBCBCCBAyzr/]';,#.....+@**>-;-;''{/rsACAABts^)!,#...+#-'{/sBCCCACAysssAACCCCCtz];%..+#-!]rzyABBBAszzrsstGLELHs/{=@........+*-!{/ztHLLtK]'-*#.................@$-;]/sxGGDAK]!-,%......+@%#-;!)FrsxBABCCCCAAysrF)-#...&*=-!]rsyAAyyAAABAAAAtAsz/)-%...@=-!{^zsyBCCLLLvAxrF)!;--=&+....@*-!]/rzyAAAsszsytBBCCCxzF'>&..",
+"..+&=-!][KzKI^/]_)({]F^IKzK^|;*........+%*=-'(25SwwOQ|;-*%+........+@#$=--;;;;!'~){F^IzKKKr2(;*......@%*=,--;~)(|12IJwwJKK^F)-%.......&*>-!(2KOwJIQQIJwO<1!-=&....@*>;)][IPwwNNNNuNNwJIQF(!-*&....+&=;'{2[PwwJKKIrKrQ/2()!->*&.......+&&#$**>-;~{FrKzr^FF~;=$+....@*>;)FQrKKKrQ/FF/^rKzKIr^1!$+..+&*-!{F/rIKK^2]1112IOwwO52(;*..........%$-'{[<wwwO[_->#@..................%=-'|[POwwO4_-=*&.....+@&**=--!{F[rKzKzKKI^}}1_;>%+...+%*>-)F//rrrrrrrKrr^rrrQ/(;=@....&==-c(]}QKJwNNwJQ}{~;;,=$%+....+&#=;~)]FF//}]]2/^rKKKK^{~-#...",
+"...&$-!([QIIQ[|(~';_(|}[IIQ2(;*+........@%*-9_1<YSR84_;,$&+.........+@&%*===>,,;;'(|}QQIIIQ1~-%......+&%#*==-;'_(11<6OOPIQ[|!-%......+&$,-'(|<6O8<}}[5O8<1'-*%+...@%*-;(1[6MNSNNSNNNY6Q[}(;-*&.....&*-'_|[<OwO<QQQQQ[}:((;-=#@..........@&&%*=-!_:2QQ[2:_'->@+....@%*-;|[[QQQQ[}1:12[QQQQ[}_;*....&*,-_(}}QIQ}:|((1:<6RSO<:~;*+........+@*=;(2<OSR84~-=%@.................+&$-;(}5OSR84~-$#@.....@&%$>--;!(1}QIIKIIQ[}((';>*@....@%$=-_:[QQQQQQQQQQQQQ[Q[}(!,%....+&*=-;~1}Q8YNNR8<}((~;-,=*%....+&#>=;!_(1||((((:2[QIIQ2(;>%+..",
+"..+&*3cb7hkhk4ba!ccadb44hhk4d;#.........+&#=30biqWWjgdc,*@...........+@%%#*$=>,;aab17khlhh4d9=&.......+&&%*=,;abffgiqqomk7fd9=&......@%*39abfipomh77gpqjib09=&....@%*,;abeioqWUUUqWqopih7fa;=#....+%=3abb7pqqomikhkhk77fbd;,*@...........@&%$*3cde75h7b|a;=$@.....+#$3cb47khikiggeggiklkh7e03#....&*,;abe4hkh7bbbbbfijWWqifa3*.........+&*,cdfijZqpga9>$@.................+&*-0bfijZqpea3*&+.....@#*=9aadbf77hhhhhh7fbaa;9=#+....@#>-9df7<k<kh<kk5k<k5kh77ba-&.....@%#>,9ae7ijqqqpg4febba0,=#+....@&*>99aadbbb(bbe77khhk4dc=&...",
+"..+*=9_e<VVllk41a00d14klVVVke0*+........+@*=9afiqUUjib03>#+...........+@##$>>-ca|e4hmnnVlk4b0=&.......+@%$=-9dfgijjqWUZmlk4ba3%......&*-;be7klmmmh7klnZqiedc,#....@%$30db4hmMnppjoonZXopmib09#+...+#=c(e7hmZWZooonmVlmmpigd9,%..........+@&#*,c_b7mnmhfda3=*@.....@#>9~b4khmnooojjjjoomlk4ed9*+...@*>0d:4hmnnk7ee47hpZUTqpgdc*+........+%>3ab7hXnnm7d9=%&.................+%*3a14lnnnm7a9>$@.....&*>;de77hmpXmVVVkk41d0c9=*&+....@*=9deklVVVVVVVVVVVVVVVVk4d9#......@#$=900e7ipppmVVimiigedc>@.....@#=,9c0_d1ee4hlmnXVVh4bc3#...",
+"..&=;~/ztCCCAAsr/F]/zstCBCBxzF,+.........&>-_:IAEEEvyQ{;-$@............+%*=-;~{^zyAAvvvAAszF);&........&*>-~]rsHuuuuEEvGtysrF'$....+#>-)]zyBACCCBBAAAGLDts/]!>@...&*,;)F/zxBGHtAxtHBGLLDDtKF',&...%-!{^sxCGDEEELLDGHHGDvvyK]!=+.........@&#>;~{/zxGvvtz/{;-=&.....&>;)]^zxtGuEuuuEEuEDGHxxz^(>+...+=;{/zxAvDDHysyyBBGEEEuDs^(-.........&#-']rsACHHyr]'-=%+................@*-!]rsABHHxK]~-=@....+*;~FzxCAHGvDHBCBxs^F{!;;-$@.....&,;)/ztCCCCBCCCBCBCCCBCCBs^(>......@%>,;~{FrssAAHHHHDvDHxz/~$.....+%=-;!)]/zsxttGDDGHCts/)-*+..",
+"..@=;'FzACCCCtsrF]]/zsABCCBAr{,+.........&=;)FKxLLELyr)!-=%.............&*>-~)/rxABHGvvAsz/]'>&.......+@*,;~]rJxuuuuELvAAxsrF)=+...&$-')/sACCCBCCAAtBGLLHsr]);%...@*=;!)F^stAxszsyxCCGLLDGzF)-#...&-;{rsAAGLELLvvGHCHvLLEtz]'>+.........+%>-;)F/syHGGyz]);-#+....+%>;)FzsABHGvDLuDLLLGHAyyz^{-+...+%;)FzsAHGGHtAABCHvEEuuGs^),@........+*>;)FzstAts/)!-*&+................+%=;)/zsAAxs/);-#@....+=;)FsACCHLLLGCBBsr/]~';-=#+....+#,;)FsAACBCCBCCCCCCBCCCCBxr)=+.....+%*=-;']/rssxACCGLLLGAs/)=+.....+%>-;)]/zxBCCGGGACAyr]'-*...",
+"..+%>-']QKKKr[F|_;'_]/QzKKK/:!*..........@%=-!:5wNNOQ|'->#@.............@%=,;~:[6OOJ8P<[1)'-,#........+@&#>-;_:4h5lPPJPKIQ/})-%....+#>-!:QJwOJJzKrIIKOww6[})!-&....@&$=-;'){]:|(1]F[IJwww6}(;,&..+@=>;)F[IPwwO6PPIQIKOwww5[_;*...........@%>-!(:}QPPI}(;-=*&......@*,;)}^IIKPJ66P6P6JJPKr^/|;$.....@=-')12IPJOJJJJPJJwNNR5}(;*.........+@%>-;~){{{)!->#@+...................&=-;~){]{)!-=#@.....+%-;)/IKKJwwwPK^/F|';-=$%&+......@*=;)F^IrKKKKKzKKzKKKKzIr2);%.......+&%$>-;_(]2/QKJOwNwOI2{;%.......+%*=-'([IJJJ6PPQ[F{);,*&...",
+"...&*-'|[QII[}1_;-;__:}QIIQ2(;*+.........@$*-0(<OSR64(;,*&@............+&#$,!_|58MR85[:(!-=>%@.........+%#*==9'_b124<<IIIQ[|'-%....+&*,;(<OMwOPIIIQI5OYY5[:|!-&.....@%#*=,-;;;;;!_(|}5YSY6f~;>%...@%>,_(}[6YM6<[}[22<6YSY51~;$+..........+%*-'(:}[Q<[1!-=#&+......@#>;_1[QQ<II<[[}[Q<II<QQ}(;*+....@&$,c~|}<6YRRRO6P8RSNO5:~;%..........+&%*=------>$#&+....................@&*=------>*#@+......&=;~1QIKPOYO6[}}|~;;==*%@.......@#>-!(2[Q[QQQIQIIQQQQQQQ[:~-#........+&#*=-;'~|:[IPOSNS8<:_-%........@%#=-_28YRO6<}|(~;,>*&+...",
+"..+%*,cd4hhlk4bbaccad:77hlh4b;#..........@$=30bgqWWjgd;,$%@............@#*=9abfiqTUqigba;,*$%@.......+&%*$$>=,90abbe7hhllkhea-#.....&$-cbiqUqomhhhhlmjqqpkfba9%....+@%*$=>,,,,-39aabfiqWqp7d0,&..+&$>90bfgmqqjffbfbf7pqWqpfd9=@..........@#*3abe77h47bc9$*&......+@*=3dekhlhhhhefff7hhlllk7bc$+....@&#$-cdfhiqTTTqpmnqWWqifa;#..........++&%*$>>==**#&+.....................+@%*$>>=*=$$%@......+&=cae4lVnoqomg4fbbc;,$#&@.......@*,cabf7khhhhhhhhhhkhhhk4ea3#.......+@&$*=3cadbe7hmoWTUjiea3%........+%$>3agjUUqi7bdcc,=*&+....",
+"..+#=9~ekVVVlk4:dd(bb4kVVVVk|0*+.........@*>3aeiqWWqiba9=$&+..........+#*-c_b7ijqTUqm71dc3>*&@.......+%*>=,3-3c0dbe4kVVVVVl7b9%....+#>caepqUWZXXVVVVXZWWolh7b0*....+%*>,,3--3,-90_bbhpZWWpgba,%...&*-0(e4lpWqjif4eefhpZWZpgb03&..........&*30(47khllk:a3=#@.......%*30(7VVXVVVhk444klVVlVVkea,@....+&#=,cd4kmqTTTZnXXZWWZi7d9*+..........+@%#*=,>=>$#&@.....................+&%#$==>>$*%@+.......#>cd4hVVXZZoXlh74:ba;3>*%+......&*3cbeQllllVVVVVVVVVlVVlh4d9%.......+&*=,90dbe44hVXoWUWoi4d9#........@#*,9bgqWWZp7edc-,$%@.....",
+"..&=;)FztCBCAAszr/^rzxACBCBtz],+........+#-;(FKtEEELtK/);=#&.........+%-!)]rstGLLELGtsrF);->#@......%=-;)){))~)]//zyACBCCBBs^{=...+%=;{^KtEELvCCBCCCGLEEvHByz]-...+%>;!)({~~~'!~{/rzyGEEEvyrF!*...%-;)^sxAvEvvtsssssyGEEEHsr];%.........@$-~FzxACCCAyr]~;=#......+*-~]ryCBCCCBAyssstCCCCCCBzF;&....@%$-;)/zyAGDEEEGHvLEELHK^(>+..........+&#>,-;---=$*@....................+&#$=,---;-,=@........#;)FzxABHGvGGHHBAyzr]{!;>*&.....%,;)/stACCCBCBCCBCCCCBCCBsr(>......+*=-')F/zytHAHBHGvDGGxzF!*........@=-;(^yGvDDCsrF~;-=%+.....",
+"..&>-~FzyCCCCByzr^^rzytCCCCyz{,+.......@#=-!{/sALLEGAs/);->*&.......@#-!)F/KAGvGGHHBys^]);-,%+.....+#>;)]//^]{{]/^zsxBCBCCAy/)=+..+#,!{^sABHHCCCCCCCGLELGCBtsF-@..%>-!{F//F))~~){/rKxGLLLDxz]'*..+%>!)/zyBHLvGyszzssyGLLuGyr];#.........&>-)FzABAACBxr]'-#@.......*-!{rxBCCCCBAxsssyBCCBCCtz];&....+%#>-!{^zsAtHACBCCGLLDts/)=+..........+%**>--;--,*#%+....................&#*>---;---=%........@=;)FrzsxACCvGLvHBBsrF{~;-=%....%=;)/zACCCACCCCCCCCCCCCCBx^(=+....&#=;;)]ryAGLLGHCCBBtssr/)-%........+%-;)}sAHHAxs/]~->%+......",
+"..+#=-~]QKKKK/[F|{(]F2/IKKK/]!*......+@&%*,-!_25wNNO<1(;-=*%@......@&%=;~(][8wwJPII[2]~;-,**@......@#*-!):]|(~'))(]F/rIzKKr[);#....@#,;_][IKKJKJJJJsJwwwJPK/F)=...&*>;~{]|(~'';!'(1:Q6SNS8[|!-#...+#>;):2IPwYPQ}2F:}Q6wNw6[|'=+.........&*=;)F^rKKrQF(;=$&+.......&=-;(^IzKKrr//FFFF/IzKKKr:'=......+&$>,;~1:22[[Q^QPwwww5}~-%...........+&$**=>=,>$*%&+....................@%#$>>,>>=$$%+........%=-;!)(]2QIOwwwwKK^])~;-,*&....&$=-']^zKzKzKzKKKKKKKzKrI/);%....+%*=,-!(FQPONNNJPI[2]_~;;-*+........+@*,;_:QIII2{!;,*%+.......",
+"..+@>,;(}QIIQ[}:1((_:22QQIQ2_-#+......@%$>,-0_15YSS8<1_;->$*&+.....+&$>-~(|}5YR6<Q[}:(~;->>$&+.....@#>-c(|:1(~_~__(1:[QQKIQ2~-%....+%*,c_:[QIIQKIPKPPOSROI[[1'$+..@*=-__|:1(~~~___(1[8YNR8}(;=@....&=-!(145YY5}1(|(:48YSY5e_;*+........+@*>3!|[[QQ[2:~->%@........%*,;_}QIQIQ[}||||}2QQIIQ[1;*+.....+@**=-;_((||:2}[56SS87(;=#...........+%#>>-,-===$%@+....................@%**=>--->**%@........+&#>,-;~(:<6wwwJPQ[:(~';,>%....+%$-'(}[QQQQIIIQQQQQQQQQ[{~-#....+%*>-;'_:[5OSNS8<2:_!c-=$*&..........@*$-!|}[Q2|~->*%+........",
+"..+@*,;de4hhhh7feb:bf77hkh7ea,%.....+@#**,;cabfiqTUqiebac3>=#@.....@%*3cdffgjqUjih74fbba;3==%@.....@*=9abf7ff1bdbdbf4ghhlkkea-%.....@#=9abe77khlllmmoqWqjihfb0>...@*=3_beeeebbbdbbffgjqWqpfdc>@....@*,;abgiqqigebebfgpqWqmed9*..........&*,;af75hhkfb'-$%@........&*=caekhlhhg7febf47hhllh7bc*......+&*>=3cdbeeef77gmjZqogd9=%..........+&*=-;0a0!c3>$%@...................+&*>,9;a~c0,=*%+........+&#*>39ab7iqqopmkh7fbd0;,*+...@%*,;a:f44747777k4k74774fba,%...+@$,cabbf7kmjqqqp7fd03-,*#&+..........@*$9cb77k4ba3>#&+........",
+"...@*>9aekhVVmmikkkhlmmVlhkb03#.....&#*-0ad:4gijUTTqph7e1dac,#+....&*,0bgimjqWWomVlkkk4ebdac>#+...+#=-aeimmmikk4[4khmXXXVVh4d3%.....+%>-0dbe7khllXVXZZUWoVlk4a$+..&$-'b4khilihk7kkimpnZoqifa9=@....@*>cad4pqWjlkhhklmoUWWpgb0=+........+%*,cbgmXnmh4bc3=%@........&*-c(4hVVXXmmh<khinnnlVlk:a$+.....@#>,0a1477kkhlmmnonomgbc>&..........+%>9abeggged9=$%@.................+%*,;_b7gigedc>%&.........@%#$>90df4iimVVVVmmiifbc3#...+#*=3adbe4e4e44e4e4e4eeeba;>&...+#>0bgiiihlVmjjihfba3,=*#&+..........+&*=3_eklh7:a;=*#+........",
+"...&*-!]^sxBGGvGBBBHGvvHAxs/{;*.....#,;)/zsyBBHvuEuEvHABxsr]~,&...&$-~2swDLLEEEGHCCCBCAtsz^]!,&...%>;)/sHDDGHABCCABHvDvGCCAzF!*......&=;!)]/rzxBBBCBvLELGCCAs]*..+#-!]zxACHvGHHBBHGDDGGHHAI]'-&....%=-;_FItEELvCACACGLEEvtJ/{-&.........*-!{rxDDvvyz/);-#@........%>;)/sABHHLGvCCACHvDGHCAxr{,@....+#,;)]rsAABACBHvGDGBHAz2)9$.........+#-!{rsAvDvwV]!-=%................+#>;~FzxAvDDwz1;-#@........+#*=-;~|:/rKsxAHGDDGGyzF'*....%=-;!)]F///F/////F//FFF{!;-&...%>!2yGDGvHHBtysKr/{~;--$#@...........+#,;~FzxBBxzF);,*+........",
+"...@=,;)/zstAGvvBCCCGGvAAsz/);*+...+*-;]/zyABCCvEEEDGCCBAxz/);*...%>;~FsHGLLLLLvCCCACBCBAxzF)-#...&=;)}swGLvGCCCCCCHGvGBBtxr{-#......+*$;;)]FrsxBCCCCvLLHCCyr)*..+#,']ryBAHvvGBCCHGLvGCBAs^)!>@...+@#=;;{IyGvvHHHCCBHGGLDAzF),@........+%-;{QyvLvGs^{~-=#+........@>-!]ryABBvvGHCCCGGvHCBysF)>@....%$;'{/ryACCCCCHvvHCCtxr{!-&.........@#-~]rxtGvDHy/)-=#+...............%=-;{/sAGHDDGsF'-=%........++&#*>-;){F/rstttGGDGyzF)$+...+@*=;;'_)){{){({{{){))~;;-*+...#,'FJtvvHCAyszr/F)~!->#&+.............%=-!]ryBByzF~;>%+........",
+"....&%*-;~(:[<PPKKKKPPIQ2{)!-#@....+&>-'{F/QrIKPJOOOPKzrr/});=%...+#>;_2<8JOwOJJJzKzKzzr^/F);=&...+%*-~1QI5PKKKKKKKPJPPI[F{'-$+.......@&#==-!)]/^IKKJJJJKKr[];#...@%=;~{/[IPJPKKKKPJJIQ[F{~-$%.....+&%$=-_1<IPKJPKKKPPPP<[(!-%..........@=,;([PP6I2_;,*&...........@$=;~]F[IPJPPKKKPJPI[/]);-%.....&*-;!)]^rKzKKKKPPIQ[F(~-=%+.........@%$-~1[IPJJI4_;=*@...............+&$,-~2I8JPPP<2_-$&+............+@#*=>,;')12[<<<}:_;,@......+@#**,-=,-,->>>->-,,==*#@....&*,014<<[Q}:(~';;->=*@+...............@*>-!{//r2{!,=%+.........",
+".....&#*=-;_(2[QQQIQ<[21_!->*%......&*=;(:2[QIIIIPPPIIQQ[}:~-$@...+@=-c|[<IKIPIIIIIIIQQQ[2|~-=&....@*=3~(:2}[QQIIIIII<[:('-->&.........+%%==-;_1}[IIIIKI<Q[2(;#....@#$-;~(}[QIII<II<Q2:__;-=%+.......@#>>3a(:}QQIIII[Q}}:(!-=%..........@%=3!|[<<2(;,>#@...........+&*=-;!(1[QQIIIQI<Q21_;->*&.....@*>;!(|}QQIQII<[}1(_';,=#&..........+%*,;(1[<<<[|!->#@...............+%*=-_45O65Q4:_;=*&...............+&#$>=,,;cca_a_9->#+.......++&%%%**%**$$$%$#%%#%&@+....+&*,90_____;;->=**%@++................+@#=-!:22}_->*&..........",
+".....+&#$>,3abe4hhhh7ffdc9,**&......@*=9(ee77hhlkhkhkhk77f:a3$&...+&*90b4khhhhlllhlk5kh47f1a-*&....+%*-9adbf7hhhkhhhh4eda9-=#&..........@&$$-0_b47khhlhhk77eb3%....+#$>-9ab47hhlhhhh4bd03,>*@.........@#=-cabf7hhhhh74bbda9,*&..........+%*,cdf74fdc=*&@............@#*=39ab47hhhlhhh7e003>*%@.....@*=90dbf4hhhhkk7edcc-=$*&+...........&*=cdb47hh4b0,*%+...............+%$=9agjqpk7fb03=#@.................+@#**$>-33999,$*&..........@+++@@@@@@@&@&@+++........+@#=,399333,==$*&&@....................@#=3a14fb_3$*@..........",
+".....+@%*>,9cde7kllk74:dc3=*@.......&*-0be7kllVVVVVVVVllk4edc=@...+%=-cb4hlVVllVVVVVVIl<k4ea9=@....+@#,9ab1e7kllVVVlkk4dc-,*%+..........+@%*,9d:4klVVlVVlhk4bc#....+@#*>9a|7klVVVVlk7|ac9,*%+.........+#*=3ade4kklllh4ebd03**@..........+&*,cde44e0;,$#@............+@%*=90d4klVVlllk7|a33=$#@.....@#>3cd:[klVllhk4ed03>>*%&+...........@*,cae4kkk4b;,$&@...............&#=9afioZol7e|a3>#@...................+@***===,*=**#&............++.+.....................@&#=9993,,,$**&@+.....................@*>3ab44ea9=*&..........",
+"......+%#>-')F^zxxtxzr/]'-=#+.......#=;~FrsstttBAABBtAAyyzr]'-&...@$-')/zxtAAAAtBtBABtAysz/]!-&.....@*9'{F^rsxtAtAAAyzrF~;3$@...........+@*=-~]^zsAtBttAxszrF~*....+@#>-')/zsttAAtAxzF)!;-=%...........%=-;)]rzxtAtxsz^/]_;=*@...........#,;~F^zr^{'-=#+..............@*-;)FrsxABAAtyz/)!;-#&......@#-')F^zxxAttysr^F);->*&@............+$;)]/zsxsr/~;=#+..............+*-!{FztvvHtsz/{;-=@....................+&%*$>,,=$$%&@.....................................@#*=----->,=#&+......................+&$-!{/rr/{!-=%..........",
+"........@#--!)]F/rr^F{{~;=#+........+#-!){/rrzszzzszzzzr/F]);$@...+%,-~)Frzzszzszszszzzr^F]';*+......%$;!){{Frzzszzzr/]);->%+............+@*-;'{F/rzzzzzr/F])-@......@%>-~)F/rzzrrr^F)'->*%+..........+&%*-;){F/rrr^/F])';-*&+...........&$>;'{]]{~;,%+................@$;;)]/rrzzzr/F)';-##+......+%=;!){Frzzrr/FF));;$#@+..............&>;){F///]);-$@...............@$-']/syBBtz/F{';$%+......................++@%&%#&+++.......................................@%%*=>=*=#@+.........................+%>;')]])~-=#@..........",
+"..........&**=--;;;;-->=*@+.........+@*=--;''~~))')~)!!;;-->*&.....+&*>--;~~~)~~~)'~)~'~;;-=*&........+%*=---;!~~)~~!;-,=*%+...............@%$=--;'~~)~'!;;-,%.........@%$>-;;!~';!;--$#@+..............++@*=---!;!;-,--$#&+..............@#==---,=*&...................@#*>--!!~~!!;--$#%+..........+##---;'~';;;--=##+.................+@%=---;-->=#@+...............+%*-'(1}[[2)!--$#+...........................................................................++.+@&@++.............................@#$>-->=$@+...........",
+"..........+@@%%%**#%%%&@++...........+@&#**=,,,=$>$>>,===**%&........@%*>>=,-,---$$=$==$=**#&..........+@&##=>,>-,>>=$#&&@+.................+@%#====,>>>=**#%@..........+&***$=$$**%%&@+..................@@@%*%***#*%%&@..................+@%%%*#%@.....................+&%*=>=>$*#%%&@+.............+&%*>>=>>==**%%&.....................@&#**#%%%&@@.................+**-;;~((!;,=*&+...................................................................................................................+%#*%**&+............",
+"...........+..++@@@++++...............+@@&&%#%%&++@@&#%&&&@+..........@&%#####%%%&@+@+@%&&+...............+&&%%&%###%&&+......................++%%#%%###%&&@+.............@%%%#%@+@+++.....................++.+++++++.+......................@&@@@++.....................+@+@@#&&@@++++.................+@&%%#%&&&&&++......................++&@@++.+...................+@&*=>,,,=*%%&+.....................................................................................................................@@&@@+..............",
+".............................................++.....+++...+.............+.....++........+.....................++....................................+....+..................+...............................................................................................+..+............................+.+++..............................+.........................+++.+++++++++..........................................................................................................................................",
+"...........................................................................+................................................................................................+.+.................................................................................................................................+..............................+................................................................................................................................................................................",
+".............++@@&@@@@....................................................+@@+.+.........................................................................................++@@@@&@@@@++...................++@&&@@@@++@+.....................................................................................+@@@@@@@+........................+@@@@&@@+................................+..........................................................................................................................................",
+"............+@%#&%%&&%@...................++++.........................+@&+&@@@@@@+.........................++++@+++++++++.............+@@@@@+@+.......................++&&@&&&%%&&&@@@+..............+@@@@&&%&&&&&&&&+.......................++.+@+....................++@@++....+.+@&@+...............+++@&&&&&&&&+.......................+&@&&&&&&++...............+@@+@@+++++++@++@@+................++@@@+++............................................................................................+..@+..............",
+"..........+@&%#*$*###%@+................+@&&%&@@&&&@+.................@&%&%&%%&&&&&%&++....................++@&%&%&&&&&&&++.........+@@%&&%&&#&&&@@@+.................@&%&&%%&%%%#%%&%@&@+...........+@&&%%%###%%%%#*#%&@+..................+@&&%%&&&@@@++............+@&#%%%&@@@@&%&&&%&@@@.........+&&&&&&&&%%%%&&&&@+...................&%&%%&%%%%%&@+...........+@&&&%%&&&&&&&&&&%&&%&+............+&&%&%&##&@@@@+...............@@@+............@&&@@@+..........@@@@@@+.....@&@@@@++..................@&&%@&@.............",
+".........+%%#*$=>===$**&+..............+@&###$****##&@..............+@%#$$*$$$$$****#%&@+.................+&%%#$$$$**#####&+.......@%#%*$*$$*$*$**#*&@+.............+@&#**$$$**=*=*$$$$#%&++.........&%###*******$>$=****#%@+.............@&%%##*$$$***#%&++........@&#*$*$*#$#%#%#*#*##$%%#@........@%%#$$****$*$*$*###@@+..............+@&*#******$$*#%%&@......+@@%##***$#$###$####$#*#&+..........&%*#**$**$$*%%%&+.............@%%%#%&@+......+@@##%%#%+.......+&#%#%%%&@@+@@&#%%#*%%@@.............+@&%$####*&@@..........",
+"........&**=,990aaa0c9,**@+...........+&#*==,3333,,==#%............+&*>>33c9cc0cc33,,>*$%@+.............+@#*=,>,,3933,,=,>$*&.....+#=>3339ccccc3933=>*#&+..........+&*>=3393c99c;ccc9933,=*&+......@%*=,,-39ccc0009c9cc933==#+..........+@#*=>,3333333,,>=*#@......&#$=>=333-,=,=>>>,,,3,-,=*+......@%*>>,,339cc9cc333>=$*%&............@##>>33c9ccc939-3>>*#+....@%$==,,3933,,,>==,,,,,,>=*@.......+%*=,3399cc93,=>*#&+..........+%*=>,=>==##&@@@@%*$=>,,=*#+.....@#*=>,,>***%#*$==>,,,>=>#&...........+&**>=,,,===$*%+........",
+"......+#,;'){]/rrzzr^F{;-$#@.........@#>;;~))]F]]{{~!--&+.........+#=-!){]FFF/^^/]]{))!;,$#&+.........+&#>-;')){{]FF]{{)))';;#+...#-;))FFFF///FFF]{)~;--*&+.......&*,;~)]FFFF/F/^/^/FF]])~;,#+....@*-;~){{FFFF//^^//FF//]{)!-$+......+&%$-;'~){{FFFFF]{))~!;=@....@=;'){{{F]]{))~~)){{{{{{{);$+...@%=;;)){)]]////FFF{{))!;,=@..........&$-;~){]/F////FFF{));,#...+#-;~){{]]]]{))~~)){{{{{)'-*+.....&*-;~)]FFFF^/]])~;-$%.........+#,;!~{{{)~;->=*==--')){{)!-*...@%=-!)))))';;---;'){){{))';=@.......&#*=-!!)){{{{))!;>$%@+.....",
+"......%=-')]/zxtBHHAxr/);,=*&........@=;~{]F/rzzrr//{'->%.........&$-;{Frzzzzsssszrr/F{~;--*&........@#*,-~)]F/rrrzzz///FF]]~-&..@>']F^rzzzssszzzrr/F)'--=%+......%=-']/rzszszszssszzzzr^F~;$&...+#>-)]//zzsszzssssszzszz/F]),%......@#==;!{]//zzszzr///F]]);=+...#-']F/rrzzrr/F]]]F/rrrrr//]-@..+**-~]F^/rzzzssszszr//F]~;=#+........+#=!)F//zzzzssszzzr/]]!$...+$;)]//rrzzr//F]]F//rrr//]'-%....+%>-)F/rzzzzsszr/F)!-$#+.......#>!)]//r//]);;,-,-;~)F^r//F),&..%=-!{F/^/F])~;;!)]F/rr//F]);%......&#*,;))F/^rr^//F)!;-=*#@....",
+".....+&*=;!(F[KJwwwOI}{'-=#%@.......@%*-;)(]}}/2/[F]~;-**+........@*=-)]}/2/[/^/Q/[/2{)';,=*&........@#$=-;~_F}/2^Q/[/}]1{_)'-&..&=;){F/2//Q//[/[/}/|);-=$#@.....+&=,;)]}//Q/Q/Q^/rQ//[/F{!-=&....&*-;(F//[//Q/Q//Q//[/[/F])!>&.....@%#>=-;)_F}/[/Q^[F]1]_)~;$@..+%,;)(]}//[/2F|_)|]}//^[/F]_-@..+%*,;)]F2//Q^I/Q/Q//}]|);-*@.........+#$-~{FF[//[/Q/^[//]|~-*...@#-!):F[^[^[}]|(({]}/[//]);=&....+&*-;(F/2^/[/Q/[F_~;,*@+.......&>;~(]:F/})';->>>,-!)]2/2F(~-&++%=,;)]}F:](~!;--!)]1F[FF|);-%.....+&**=-;)(FF}/2F1{~;->$*#&+...",
+"....+&$*-;~(}Q56MSSO5[|_->*%@.......+*=-'~|:2[Q[Q[[1(;,>#&........@*=;_1[[Q[Q[QQQ[[[}1(~;->*%+......&**=>-;~1:}[[Q[[[}1}1|((!-%..@,'_(:[Q[[Q[QQ[[[}}:(!--,$*%.....%*-;(1}[[[[QQQ[Q[Q[2[}1|~-=%....&*,!(:[[[QQQ[[QQ[QQQQ[[}|(;-&....+&*$=--!_12}QQ[[[[}}:11(_;=&..+#>;~1}}[[Q[[:|(((:}[[[[2}|(-@..+%$=;_|}}}[[Q[QQQ[[}}1|(!,*%.........@%=-~(::[[[[Q[Q2[[}1(~-#...&$-;(1}2[[[[21(|(|:22[[[1(;=&....+&*>!(}[[Q[QQ2[}}1_;,$%+.......%=-~|:}}}}|_'--=>-;~(1}2}11~-&.+%*,;_122}1|(!;-;'_|2[2[}|('-&.....&#$>,-!_|:}[[[}21('--,$*%+...",
+"....@%>,;adb4hloqWqqpgfbc->%@.......@$>-adee77hkhgheba-=*%+......+&>,cdf77hhhhh7hh7k74bbba;,*@.....@%$>9;adbf77kkkhk7h77ffeba;%..@>adf47hhhhhk7hhh77febaac,=*+...+#>3abf7khhkhhhhkkhhhkh7fdc=#....#=9aefhhhhkhhh<kkhkhhhh7fba-&....&*=,9cabbf477hk7h7777ffbb0>&..@*30de77hhh74febbbe7hhhhh7ed3@..+&$-cdbe77kkhhghhhh77fbdc9*%.........&*=3a:f7hk<hhhhh777ebd9#...&=9ade77hhh77febbe7ghh77fba,%....&*=,0bf7khhhhh774bd0-*#@.......%,;abf7g77fbdc;9-;ade7gh77bd,&++#=3ab7g77febdac0dbf7gh77fba-&...+&**,9c0dbff7kkk74fbb~c03>*&...",
+"....&*,cde4khllnoZqqoml7b03>#+......&*30(e4klXpppnpm7bac>*&+......#=9d4klVVmXmVllVVlliligfbc,#+...+%=-!d:e7hilllllVlVlmimlkkea$+.&>de7hmpXmVVVllllllhhhg7fdc=#...+$,0|4<llmpnmlVlVVVVmjmmked9$+..@*-!b4<hlVXmXVVVllVlmmmmVh7:0*...&*=;~b:fkhillVVlVVVmimllk4|c%..&=9_e7kmmmmVlk42e4khhVmmmihe0%..+#=9ab4kkllVmnnpmVlVlk[ed9=&+........&$,0b4khllVmmpmVVVlk4ba$+..#>9de7lXppmVlk4447ippmVVk4dc*....&*,c(4<llmpmmVllk7:a9>#&......+#3a14hmnpmh7eb_a~db47mmppl7bc#.@$;abhlXXmmihebb|e7hlmmmiked9#...@#=ccd:f4hihllllVllhhg7fa9,#+..",
+"..+%=-!]ryBACCCHGHHGGvGxz/{;,#......%>!]rxtBGDLEEuEvAyrF~-$@.....+>;{^sACAGGLGHCCCCBCGGvvAJF),#...%-;{/syBHGDvHBCCCHHHGvvvHAx/-&.&;/stvDLvvHBCCBBBBCCHGvGwzF'>@.+%-~]rtCCHvDLGHCCCHHGvLDDtJr];%..&,~FzyCCHvvGGHCCCCHHGLLvGCtsF-..+*-~FrsABGGGGHACCCHGvDuvGAAs)*.+*;)/stGGLDvHCBxssyACHGLvDGtsF>+.&=-~FzsAACCGvuvEDHCACCxzF);$@........%-;{ryBCCCBGDLvGCBCBxz{,+..$!]rstGLDLDHCCBAtBvuLvHBBxr(-...&$3~]rxACHGLDHHCCAsz/~;*#+.....+=!]zxBDELvGCAsr^rzxAHvELEvAz(=+@-{/ztvuDDvGtyzzzstHDELLGAyr(>..+#-']rsyBHGvGHBCCCCHHDvGwz]~,&..",
+"...%>;~]zxBCBCBCCCBCGDLAs/]~;*+....+%,;]rsAAGGLLELuuvts/);,%.....@>!{rxBBCGvLvHCBCCCCGLLDvs^);%..&$-!]zxCBGGvvCCCABCCGLLLvHBy^;+.&;/sBGLLLGHCCCCCCBCHGDDDts/{;#..%,~FryACBvLvGCCCCABHvEuEGyr];#..&,']zyACHGLLGBCCCCCCGGLvGCBsF-@.&>;)/sBBHGvGGBCCAACGvDDLGHAs)%..%;)/zAGLLLvCCAAysxBCCvLLLvHsF-++@$-']rsABACHvLELLGBBBAyzF'-#+.......+%-;]rsAAACCGGLvHCCBByr]-+.+$;{/sAGLLLGBCABABHGLuvCBtsr)=+...*-!]ryBCHLLLvCBBAsrF)-$&+.....+>~FzyGLuELHBAxzrrsyAGLuEuvtz]$+@,(/JAGLDuELGAszsyBAGLELvHsr)=+.+*-)FztCCGvGGCCCABBCGLDvtzF);#..",
+"...%*-;)FrIKzIrIrQIIJwOP}|';=#......+#=;)][QIJOwYwwOPI[{~->#.....+#=;)FQKKJJwJPIIQIKKJwwwOQ(;,%..@*,-_2KJJJJJPIQQQQQQPJOOJPK/)=..@>_}QPOwOJKIIrIrIQrIJwNw8<|'-&..@#>')/QKKJJJPIQQrIIIJwNSP[|'-&...#-!{/IQKJOJPIQIQrQIJJOJJKIF~#+.&*-!1QPJJJJPPIQQQQQIPJOOJKI};&..@=-~:[PJwOJPK^/FF2^IKJOwO8<2_$...@*,;){F/[QIJNNNOII^/F]);-#+.........+#=;)]F/[QKPJJJPKIr/F_;*...%>;(2QPOOJJKKzrIIKPwJPIQ/:);#....@*-;)F[/IJSwOKQ^/]);-*&........*-~1[6YNNNOKI/F]1/QIJwNNN8<1!%.+$;)2<JOwwww8I[2/[QIJwNNOI2{;*..+#,;1[KPJJJPPQQIQrQIJwww8Q(;-&..",
+"...&*=;(2[QQQQ[[2[[<8OO5}(_;,&+.....+&*>9!(|Q<PPP6PPIQ}|;-*%+.....&=-'(:[Q<5IQQ[}2[}Q8YSR84(;,&..@*-9([6OO6<<}}:|(||:[<<5IQQ}!%...#;(1Q<P6IQ[}::1:1:[5OSY6[|_-#...&>-'(}[QI5P<22:::}[8YSY64(c=@...%=-!|}[Q<P5Q2:1:::2[Q<<QQ[|-@..@*,01<OOO85Q2:||||:2<<I<K<[1-@..@%-'_1[5PPIQ[}1||:}[<<PPP<2(;%..+@&*>-;!~|:[6YSY8<:|(!;->*@..........+@**-;~((:}QKPQQ[[2_~;>&...@$-'(}QIP6K<QQQQ<I<K5[2{(!-$@....+&*,;!(:25OO8[21_!3-*%.........%>;_1<MNNNOPQ[||1:[<6RNNM82(,%++%-c([<P6OYY6<2}1:}[6YSY871~,%..+#,;1<6OO8<Q}::1:1}[5YSR84|'-%..",
+"...%$,;d4khhh4k777hipqoi7bbc9*+......@*=-cab7hhlhlllkh4bc3=%@.....@*>;_b47hlhh4eeeefgmoWWjgba-&..+*>cbiqqqpg7bbd_0aade77hhk7e9@..+*9ab45lmi74bb(ddbbgiqWWoied3%...@*3cdef4hlihf:b:bfgpqWqpfd;=&..+@*,cdefkhlk7e:bbbbef7k4h74b,+..&*3agpqWqmgfbb_a_adbf7hhhk7e3@..@%=9ab4klllk74:b|be7kkllhhed9#...+@%$=,39abfiWUqpgbd0;>=*#@...........+%#=,9c~db7hhhk4f:d0-=&...@$39df7llmih4hlmmVlhhfdac-=#+.....@&$,30dfiqjpfbdac,=*&.........%,cafgqTTTqik7eb1fkimqTTTjfd3@.+*3ab7hlmoWqji774ffgiqWqjgba3#..@#-cfiqWqpgfed(ddbbgiqWqogfd3%..",
+"..+&=3cekVVVllQkkkkmnWopk4ea9=%......&*>90de4lmXXXVVVlhed03$&+...+%*,9de7klVVlk74444hpZWWoiea3%..&*9afpZWWoV7ebd_000ae4kkhhh49&..+$cdekVXXml74bdddde4pZWWomkb0#...&>9'bekkllXlk44e44hnZWWpgb0,&...&=30bekhlVVl7e}eee4khkhllk13@..&=9dgoWWZnl4eddaaad:7kllllke9@..@$>cdekhVVVlhk4ee4[klVmVlh4d;#....+@%$,9cdbgpWUWjg:da3,$$&+...........++%$>,90de7lVlhk4ed09$&...@*30d4lVXXVlllVnXXVVl4d03,>%.......&*=90dfmoqmgeba;,$#@........+#-0dgiqTTTWnVk774klXoUTTUjgb9&.+*;aekVXXqWUomlhk74inWWWohed9%..+#3afpZWZnl7eb0addb7nZWWomke0*..",
+"..&$;~]zyCBCCCCBCCHHvEDGAyz/);#.....&*=-)]^zxCHHGHHCCCBsr])-$@...+$-;)/ztBCCBCAAtAtABHEEELtsF'%..*,)FKAEELGAsz/{))~){/zsxyxxz)@.+&-)FzxCCBCBysr/]{F^sHDLLGHtzF=..+%-!{rsACCHHAtxssxtAGLLDAK/'-%..+#-!{rstCBCCAxsssyxyABAAttyr;@.+*;(^xDLLvGtsr/{)){FrstAABAy^'@..@>;)FzACCCCCCtyxsxAACHGHBts/)*.....+#,-;~FrsvuEEvyI/('-=*@.............+&$-;!)/zxBCCAAxzF);-&..+*-;(/stCHCHCCHHHGHBCts/(;;>#+......&=-!)2KALDGsz^]_;>*%........@,!]rswEuuuEvHAAABBCHDEuuuus/;&.+=)FzxCCHDEEGHBCCtytvEEEGAz/)$..@>)/JtEEEGtsr^]{)FrsHvELGHts/-+.",
+"..@=;)FztCCCCCCCGvGLLLLHtxz/);*+...+@*,;']^zAHGGHCCCGGCyz/);,#...+#-;)/zyACGvGCBBBBBCvLLLLAz/;&..#;)^sHDLLvAs^F)'!;!'{F^^rrrF;&..%-)FzABCCCtxz^F{){/zAAHGBCBs]-@..%-!]/syACBCCAyxABHGHHCAs/{!,@...#-;]rsAABCAByyxytCCAAtszrr{-@..*;FryGLLDHyz/]))')]^zsyyxyz/;@..&=-~FzxCBCCBCBBBBBCCCBCCCBs/)$......&*>-~{^stLELGsrF);,*@+..............@#=-!)]ryACCBtszF);=%...#>;)/sABCBCGvGGvGvCtyrF);-=&.......+*-;)/KtGLtyr]]~->$@........@$!]^stEEEELLHCAAACHvEEEEDGs/;@.+=!FzyCCHvGGCCCCABAAHLLLLAsF)$+.@-(^sGLLvGyz^])'){FzACHHCCBsF-@.",
+"..+&=-):rKKzKKKJwNNNNNw8Q2]);=&.....@%*=-'|}PJwJIQIKJwOQ1(!-=%....+*=;)]/IKwwwJKzrKKJwNwNO<}~>@..%,;([6www6Q:);->=>>,-;;;;;;-*+.+&*-_]QIKKr^F|);;--!(2IIKIIr[)=...@*,;)F/QrKKKQ^[I8wJKQ2}(!-=%....@=,;)F^rIzKrQ/}IPOJI/]()!;-*...%-':<Owww5[]_;---;!){]]F]{)!>....#=-~:/KKKKzKKrIrIzKKJKKKI2)-%.......&*$-;_26wNS6[_'-=%@................+@#*=-;)/IKzI^F|'-=%@...&*=-'FQKJJwwwwvwwwK[]);-=*%.........@#,-_:5OwP[1~;>*#&+........+%>!(2<ONwNwwwPI^IKJwNSNNYP[_$..+#-!]/KKJJKJKJzKzI[Q6wNwJ<:_,%..@,!(46www6[:';-,,-!(}IKKIIr2)=..",
+"...%$-!|[QQII<I8ONNNNYO5[}(~->%....++&$,;~(}<OO5Q2}<6O6[:~';=&....+&=,!(1[<OYOK<QIQI5ONNRY<1~>+..@,9_15YSY6}_;-=*$###$$$>>>$*#...@*-;(}QIIQ2|_;->=,;_:}QQQQ[}_$+..@#=-!(:[<I<Q<[<<6YO<}(_;->#@...+@#=;_(2[<K5<[4[<8Y8[1~;-,>*&...%-0|[8SSO5:_9-,--3;_((|((~!-*+...@$-;(2QIIIIIQIQIQQQKIIKI[:!=@........@*=-c15YSY5}~-=$%+.................&%$$>-~|[QQ221(;->#+...&*>-'(}IIJwNNNNNSO<1(!-=#%+.........+%*-;b<8O<:(;-$$%@+.........%>-a:[ONNNNwO6<[<<OOSNNSY5:!#+.+%>;([QIKIIIPPJP<[[[8YNM6[_;-%..@$c(26YSO5:_;->*=,9_12[QQQ[:!*..",
+"...&*,0b4hlllllppoojoomi4fba3=&.....@&$-0dbfkmmkgffgipmgfba;,#+....%*30bb7ijWqplhlhVmjUUUjgfa>+..&,cdfpqUqpfd9->#%%%%%##$*$$&@...&*30b4hhh74bac,,==3cbf7kkh7fd$..+&*=9abe7ijqjjjjjqqqifda9,*#@...+@*=;_bf7iqqjjjjjqqjgbac,=*&+...&3abgjWUqibdc-,,;0dbeeeebda3*...+@*30d4hlmmllklhllhlVmmlh7e0$+.......+@%=90biqWqpea9*#&..............+@@@@%*$3c_e4hk4ebdc,*%....@*=9ab7hmmZZUUUUWqifdc-$#&@.........+&*,cbijqiea9-**#%+.........&>9abgjWUWqoppjjjppooWUWqif0*..+#3;b7hllhhhmjoopi7gpqWqjgbc3&..@=cbfpqWqiea33>$=,3adf77kk7e0$..",
+"..+%=9~ekVVXVXlXXXXnXXXVk4edc=%....+&$,cd14klVVlkkkhlVVlke:ac=@...+#=;a:4hmZWZoXVVXVXZWUUqp7b9&.+&3ab7pWUUjgdc9*#%&&&%%%#$*%%@...@=ca:kVVVlk}d;3,>33a:7khlll7b>+..&$3c_e4ijUTUUWUTUWqifb09,>#@....&$=0dekijWUTUUUUUWoiea9,$#@...+%;afioUUqp4bac99a14kkkhk74|a,@...%=9_bklXXXXXVVlVVVVXnnXVhea>+........@$>3aepqWWpgd;3*&...........+@&%#$*$*$,9ab4hllk7eba9=&+...@#3;dekVXnZUTTTUUqm7bac,*#@.........+%*-0eioqp7ba9,>$##%@+.....+#,9d4ioWUWZXnoqUUqnnoWWUZp7a=+..#,a1kVVVhhlmZWZonlmnWUWogea3%..+>abhpWWZpfac3==>,3a:4kllVl7d=+.",
+"..&=;~FzABHGHHCBHHGBGGGHAys^{;*....@*-'{^sxBBHCCBAtACHCBAys/);#...&>;)/zxtHvEEGHBCCBHGEEELGxz(%.+*!]rsGDEEtJ^]~-,*%%%#$#*$*$$&...#-)FztBBCAAs^{~!;;)]zyACCCAx^;+..%-;{/zxHDEEEuEEEEEvGs^]);-,%...+%-;{/stHDuEuuuEEEELyI{'-=%+...+=)/ztDEEDAJ/]))]^sACCBCCByz/;&..+*;)/ztBHHHBCCCCCCCHCCHCCtz];@........%,;_:KHuEEGs[{!,#.........+&#>---;--;-!)]rsABBBxsr]'-*+..+&=;)/stCHvLEEEEEELGys^{';=#+........%>3!{ItEGGszF(!;----=*&+...@='{/stDuDLvHGLEEEEvGvLEELGJ/-@.+>!]zxCCCCBBGLEEvvHHvLELvAKF~*..@;FKyGLELGs/_~;;;')/zxtCCBCx^-@.",
+"..&>-)FzACBCBCCGBLLLvLvCCBxr{;*+..@#-;)FzsAACCCCBtABCCCCBxs/{;*+..&=;)FzsAHLLGHCCBCCCvLEEGHyr{#..*']IsGLELHsr]~;,$#%###*$*>*%&..+&;'FzyCCCCxs/{!;;!)FzxBBBBCy/;+.+%-!)/sttGEuEEEELELLtsr/)~;-*+...*-!]^sytEEEEEEEEEEGx^)!-=&+....>~[sAGLELAs^]{)]rsAACCCCAxz/'*...%;)FzyCCBGCCCCBBCCCBGBCCAsF-&.......+%>;)FItELuHs^);-%+........&$--;!)~~';;')]rsBBCAysrF~-*....%,;)FzABCCLLEEEEELHtzr]~;-$@........%,;)FKyGLtyr/{~;;;----=&...+*;)^sBLLLLvCHLEuELvBGLLEvts^;@.@>!FryCCHCCCCGLLLHBBGLLLvtzF)$+.&-FrxGELuHs/{';-;~{/zABACBAy^;+.",
+"..@*=-_]QKKJKKPJwNNNNwwJKr/|'-&...+&=-!)2^rKKJJJKKPPJJJKK/}{;-%...&%=;_]}[IOOJPKIIIIKJOwOOPI}'*++%-!([8www6Q|!-=*%&&%##*$###&@...@=-~FQKKKI/F(;-,,--!]/rrrKr/)>...@*-;)1^QPORSMYwwwNOK[1{~!-,%....@*,;(F[Q6YRSMMMSwwOQ(;-*#+.....#;~:<Owww5[|''!'{/IKKJKKKQF|;#..+&=-~]^KJJJJKKKKIKKKJJsPKr})>+........@$=;_25SNw6[(;-$&.........&*,-!!)~!;----!{/IKr^^F|'-=%....@%=;)FQKKJJwwSwwwO8KQ1);->$@........@#>-~:5OO6[|)';->--;--=&....%>;_:IOwNwJII6wOYOPIPwwww6[)=+..$-'F[KKJKIIKJJwJPKPJwNwJ<2),&..@=~1[6wNw52);->=,-;)F^rrKzI/_>..",
+"..&#=-;|[QIIIIIPOwwNwwOP<[}(;$%...@%$>;(2[<I6OMYYYOYMRY6KQ2|'-%...+%$,!(:}Q<PQ[2:}}}}[I56IQQ1_$..@=;(15MNM8}(!->$#%%##$$**$$#&...@>-!|}QIQQ2|~;->>,-'(:}[QQ[2'*+.+@$=;_1:[<PP6P5<5OYO5}1((~!3*+..+&*,;_(}[<PP55<55OR8f~-*$@......%>~|[6SSO<:(_!'_|}QIPOO8PQ}(;%...&*-;(}QIIIIQ[22}}[[<IQIQ[:!=+.......+&*>-~15YSY8}(;=*&+........&=-;_1:}(!;---;_1QQQ[:|('-*%+...+$,-!|[IIII5I55686665[(!-=*&........+%=-c|<8O51(~!---;~~!;,#+..+&=,a|7OSNY8<[<58P<[[<6SSO5}~>+.@#>;(2QII<QQ<<I5II<POSNR84(!,&..+*;d}8YRY52_;->$=-;_:}QQQQ[2~=+.",
+"..@#=-0b4hhlhhhVmpojjomlkh4bc$@...@&=,cb7hlmoqUUUUqTUUqjmlhfd,%...+%*-0bef7hhhf1bbbbe7hhhkhkfa,++&=cafiqUqpfba3,***#*==-3==>*&...@=9cb4klhhf1a0-,,-9abf7<hh7:0*...@*=3abe44hkhh47imoji7fbbbdc>....&*=9abe4khlhk77ipjpg0-*#@......%,0bgjWWqifbdaaab4hmoqZqpi4ec*..+&*90bfkllhh74eb:bf47hlhhkea,%.......@&*=9cfiqWqifd;3*&+........%,;de4hk7ba;;9cdekhk7eba;-*%+...+#=cab7hmmlhh<hh5mnqjieda9=#........@#=90biqjpfbda00a_bbbac>@...%>9abgjWUqige7khkh7fijWUqif0>..+#30d7hhlhhkhhhhhhlpoWUqjgbc,+..+$0dgiqUqieba;3=,3cde7h5hk7e0*..",
+"..@*>3a1kVVVVVlVVVXXXXXVlhk:a>@..+%$,cdfhmnoqWUqUUUqUUWqnmVke0#...&$,9d14klVVk7e:e:e47<llVVlkb>+.&,cbfpoZojh4bdc;,>,9cdddda'0>+.+&>c_ekVVVhk4ed~c;0a17hmlllhfa$..+@*-cde4klVVlhkklmonmhk[44e(,@...@*-01e7klVlVhQ4hmppfa,$#@......%9dbhjWWqmhee|b1e4lVnZZZnVh4a*..@#99d:kVVVllk44eee4klVVXlhedc%......+@$>3cdfpqUWjgba9>$%@.......%9ab7lmml7:bb(bekm6Vkke|03$%+...&*3cdekVXXmlhhhklmnonmh4|dc$@......+#=3cdfmoZphebb{de7hhhfd9*..+&-0(fmoWWZpk47khhk44ioUTqp7b,+..#-0:kVVVVlhkhkhlmXnZWUWohfa>@..@>cbgpqWWphe1d!09~dehVmmVVke0*+.",
+"..%>;~]ztCBCCCBCCCCCBCCCBCyz/!$...*3)]rxGDDLvDDvvvvvvvDvDvvts]>+.@#-')^zyAACCByszzzzstCCCCCBy/;++%;)}stvvvHCAsr/])))]/zxyyszF~&+@*;{^zACBCCAxsr///^rstGvDGAxz{-..+%-'{/sxACCCAByxABHHBBCCBAAz~&..+#-']rstACCCCtyxxAAs[(;,*&.....+*;FrxHGDHHAyszzrzstCCGGBvCAs]*..#>'{/zACBCCCtxszzsxACCCCCAsr{=......#$-;~F^sHEEEGxr/{!->$&+....+>)/stGGvGtszr^rsAGvGBysr]!,*+...#-!]rstCHBCBCAtytCHGGBBAsr];#.....+#=;'{/KAEDHxsszzzsADHGyI{=+.+$;{rsALEEDGysssssssxtDEEEGs/;+.+='FzxCCCBCCBxxyACGDLEEEvyKF;@..%!{^zAGGGHCxsr/F]/rstDGvHByz]-..",
+"..@$-)FzyBCCCBCCCCCCCCBCCBtzF'$+..#;)FzyvLELvCCBBABBCCCvLELGz]=..+*-!]^syBCCCBAyzzzzyAAACACBx^-@.#-~FryCCCBCAAz//F]F/zABBABxz~&+@*;]rstCCBCBysr^//rssHvLLvAy/{=+.+%-!]rsABCCCCAxyyytABCACBBAz)&...#-)FrsACCCCAAxssssrF~;=*@......*!{/sACCCBCAyszzzssxCCCCCCAs)%.+%-!]rsACCCAAByszrzytBCCCCAsr{-+....#>-;~{/IsGLEuvyr/{)'->*@....+=)/sAvLLvAyzzrzstvvvAxzr]!-#+..+#;~]rsACCCCCBByxyxCBCCBCtzF'=+....&$-;){/JAGLHAszzzsytDLDtz{-+.+$!]rstvLLLBxszzrzzssBHLLvGs/;@.@=)FzACCCCBBxyssxALLEEEuvtzF;%..%-)/zyCCCCCByz///rzxHLLLvAsr{,+.",
+"..+%=-~]QKKKKrIrIrIrIKrIr^/|!-&..+&>;_:<OwNwJII/[^^QQQKwNNS8[!%...@=-~{F^IrKKr/2F:F1/^IzKzKK/(>+.@>-'{^KzKKKr//|_({(F//rrrr[];@.+#,!{FQKKKzI//1|{1|}QPwwwJI2);%..+&=-']/^IIKPKQF//2/^^rKrzIr};&..+%=;)F}^IrKKI^F2::1)!-*%@.......&=3_]QKKKKKr[F1F:F2/rKKzKIr};&..&$-;)F/KKzKr^/F]]]/^IKKKKI/|;*...++%=,-;'_:[6RNw8Q1(';-,*#@.....*;_25wwww<Q:2]2[5wwJI[]_',*@...+&=;'{/IKKKKr^/FFF/IKKKrIr}{;%.....&*=-!~{}POOPI[}}}2<8wwwP[_=+..#-'1[5JNwOP[:]|(({:2IJwNw6[(=+.+*;']^KJJKIr/F}2[PwNNNuNOQ:_>@..+*-':^KKsKKr^211||2[6wNwJ<2)-%..",
+"...&*-;(}[QIQQ[[[222[[[[}:(~-=&...%>-_1[8SNRO5[[2}}}[<5OMNR5e!&..+%>-!|}[Q<I5<[2e1e:2[<<IIQ[}!#..@%=-_1[QIQQ[[2::1|1}}QQQQ[}),+.@&,;(:QKPI<<[}2:11:275OYO82(!-%...%=-_|}[Q<PI5[4}}4[}QQ<<<I[};@...%=3_:2[<I5K<[2:||_!-=$%+.......@#,c_}QIII<Q[}::}:e}[QQQQ[2_,+..@*-;_:[I5PI<[}||(11}QP6PP<}(;#...+%*>-;~(b:45MNR872|(~;->*%+...+&,~|<8SRO5[2e:1[56O5[|('-=%@....&>-'(}QIPI<Q[}:1|:2QI<<<<2|;$+...@%*-9_b175OY6<[422f<6MSY51~>..+%>;_2<OSSO5}|(_~_~(:78MSY5}_>+..#-~|[IIP5<[2:(12<OSNSNR87(;,&..+%=;_}[<QQ<<[2:1:e2<5YRY52(!$@..",
+"...%*>3a17khh<h4ffef474f:bd0-$@...%>9afiqTTUqjmg7f47imjWUTUjg0%...&=;dfipjjqqjjijiijijpppmhf|9%..+%*,0be47kkhiiiiiiiiikhh7eea-+.+%3abgijqqjjpppiiijijjjpmiba9>@...%>9bgipjjjqjpjijijipppppi4f9@...#>9bgipjjqjjjiigebac=*@........+%=9ab47himmiiiiipiiiihh44ba>..+&*9abgijqjjpigfbbfgipjZojpge0*...@#*9adbfgijqTTTqjigfeba9=*@...+%,0dgmjojpjpiiiippmieba;,*%@....&=;aegpjqjjpigebbee7lmpjpied=+...@*=9afgijqUUqjimippjqUUqjf0$...#3cbgiqTUqig:bacade4iqUUUjgb,+.+%3abgjqojjigfbbfgmjWTTTjgbc,&...&*30be7himppiiiiijjpppmgba;#+..",
+"..+@*>-0b4kllmVl5ihhihh7f|dc3#@...%-afhjqTTTWWZmhk7lpZUTTTTqhb#..+#,0biqqUUUUUTqUTqUWWZWZXifd9#...&$,cd1e4hlVnqqUUWqonmVll7|a,@..#;b4mqUTTUWUqUTUUUUWonmlhba9$+..+#3afiqqUUUUTqUUUUUWWWWWonl40%..+%9afpqWUTUUUUUqoifbc,$&.........&$9!be4hmoWWWWqqUUqXXllk410$@..%,cd4ioUUUWWZph447moWWUWWqp4b=+.+&*-a1khiqqTTTTTTUZpmhked9*&....%>9afkmXnqqUUWWZoXmked03>$@+....#,ab7pqTTTUWom4e1e4hioWWWqm4c&..+%$9agjqUqTTTUWWqWqUUTTTUjg0=+.+%,_ehjWTTUol7ed~ab7hjqTTUqmf9+..$9d7pqUUUTqjhfefkljqTTTqiba3#...&$,9de4kmoZZWWWqWqWomVl7bc3*+..",
+"...&=-;)/zsAHGDvvvvvvvHtsr/{!>+..+$!FzxvEEEEEEDGAxxBGDEEuEEEA^-+.@=;|IwEEEEEEuuuuuEuEEELEvxz:'$...&*-!{^zzxACGDEuEEELvCCAxs^{-@..*)rsGvEuEEEEuEuuuuuEDCAts/(;>+..+>~2stEEEEEEEuuuuuEEEEEEvHts]*..@=~}zGuEEEEEEuEEvAs/{3>*+........&>;)F/zsHvEEEEEEuEEvHCBxz/(-+.@*;{^xBDEEEEELHxssxGvEEEEEDHxF>..+*-)FsABGDuuEEuEuEEGHAAs/)-*....#,'{rxBHHDEuEuEEGCBsr]);>=@.....$;]zxGEEEEEEvAJrrrzsADuEEDtsF=..+%-!1VNEuEuEEEEEEEEuEuEuuZz:-..+>~FsAGEEEDDHxz/]F^sAGEEEEDts)#.@>~/zHEEEEEuGJrrzytGLuuuuXI];*...&=;~]/rstDDEDEEEEEvvHAyzF~;=@..",
+"...&*>;~{/zytvvvuLuLLvvAsrF);#+..+$;{rsAtDvDGHHtszsxAtGuvuvAx^;@.@=;{/JtvtHvvDvDvHGGGGHGAAz/]!*...+&>;)]/^zsxBtHGGGAHBAyyz/])-@.+%!FzxyvvGDAGDtDvHvtCBtyz^]~;$+..@=!{rwtGGGNvDDDGGGvtHHAHBtsr(=+.+$!{IytGGvGGvvGHwsr])-$@+........@#-!)F/rsxGtGHGGGGBABtsz/]~=@..&-)/zxAHDvGHGxyzzJytHGHHHBtz]>+.+*-~]rsxttGDDEuDvGGBBAsr]!-#+...%-;)FzsxttHHHHHGHAxz/);,$&+....+%;{^sytvvNGvAsr/F//zsxGtGtyz(*...#>;]zXHvDDGuGGHHHGGDDDDAy^(=+.+$;]rsAHvvGAxs/]){FzyAtDHNAyr)%++>;{^JtvNvHGxz^/^zyytGGGxzF{;*...+%-;)]/rsytGtHGtHGAAszrF{!-#+..",
+"...+&&$,-!_:Q5JOJOOOO66[|_;-*+....%=;~(:}e}}}}::{(((:}}4}2221~=+.+%=-~{e2442}4}}ee}e2}2}:1(;-*&.....&*=-;;))1:2e}}2}22]()~;;>%+..+*;'|ee24[44[eee24}e:{();-=*&...+%=-'{ee}4}4}}4}4}[4}2}}:](~-&..+&=-!1e}f[}4}}e2:1~;,$@...........+#=,-;!(|:ee}4}}}2:{))';-=%...+%-'(|}42}}e2:1(({:}f}}}22:);%...&*,;)1]2}e:}424[4[}F]{';,#@.....%=--~)1:e}}4[[}2:(~;->#@......+&=-~{:e4[}}}e1~;;;'~(|e1e:|_-@...@$>-~{:ee}}}}[}2}4}4}}}e1'-#..+%>;~{::e222:(~;;;;~(1ee}}:|'>+.+#=,~{e}}ee2:(~!~)({:e}e|_;-=&.....&=,-;;)11:e}24}}2:(~'--=#@...",
+".....@%*>,-0_::4[[[[22:_;->*&+....@*>-;0____(__!;9;c'_______'-%...+#>3c~__(((((_((_(___a!;-,*%......@%##*>-9;;'_d____'!;-,=**&...+%*-3c__(((({((d___!!;->=*$&+....@%=-c~__(((((((((((d____';-*+...&*=-c__((((d(_~!9-**@@............+&**>>-9c_~_(((_'!;--==#%.....%=-9a__d(__a!;-990__((_(_!;$+...+%*,-c~~__|(((((|((~!;-=*&+.....@%$>=-;!___((|(~;;-=*%&........@*=-9~__(d__~;-==>>-9;00~!;-#+...@&#>-9'___d((((((((((__~;-=@...@*>-9!____~';-===--30'__~!c-#...@$>-c~___'_!;-,--3;!'_~!-=*&......+@&**=--cca_____~;-,==*#@....",
+"......+@**=-30aabbbba009,,*#+......&$,3390a00cc93>-339000c9c;,%....&*=330c00aaa000c009cc3,=$%@......+@@%#$==,39c000ccc3-=$$#&@....&*>-39900aaaaa0c0c93->>$#%@.....+&*,39ccaaa0aaaaa000909c;3$#....@#$>9990a0a0a0c9>>$%+...............&&#$=-,99cc090c93>>$$&+.....@#=-3900a00;39,>33909000993#+....@#$,-9cccaaaaaada00;3>*#@......+@%$$>3390aaaaac-=>$#@+........@*$-339cacc9c-,>*$*>>,9993,=&....+@%*-93900caaaaaaaaaacc;3=#+...+&$=,339ccc9->$**=>,-39cc3,=&...+%$>99c0c993=>*>>=,93c93,*%@.......+@%$*>=-,999cc9;3,>$%&+.....",
+".......+@**=,3300a00039>=$#%@......@%$>9933099-,==>=>3333999,$&....+#$=3,9339993,3cc999,>=**&+.......+@@&#$$>,,333399,==**&@+.....@#*=,999cc90ccc939->,***@@+......&%*-93990;99990c33393-3,=*&.....@*$,-93ccc9c93,,*%@.................@&#**,,99999-3=>***&+......+&*>-93999333==>==399,3393=#.....+@**>3,9999cc0ac0c-3,=#%+.......+&%**>,999cc0c->=*#%@..........&#*,99ccc9-,,**$#$*===-3,>*&.....@&**,93339990c000;cc99,=*&+....@%$,,399,-,==*#*$=,,-9,3,>*@....@&==9339-3,,*$**>*=,,,>$$&.........++@%*$=>3993-3,=**%&.......",
+"........&**>-3;!'__'!9->*#&&+......+&$>--;;!;;,>>$$---;-;;-;-*+.....@#$>=>----,----;;;--$$%#@............@#=>--;-;;-;-$*##&@......+%*$,-;;;!!;!;;;-;--=$#%@+.......+&*,-;;;;;;;;;!;;;-;,-3-,*&.....@#$=-;;;;;;;';-,=%+..................+&*$=,-;-;---,=##%@+......+&#>--;;'';--,=,,--;;-;---$&.....++#*>-3-;;'!!'~!;;;--=#@.........+&#%$=,;'';!;--**%@...........+%$>-;;;;;--=$%%%##==>,-->%+.....+&#$-->-;;;;!;!;;!;!;--=$&.....+%#>-,-->-=**##%#$=-,----$%.....+&$,--;---$$*#$*=>>=-,>=%&............+%=,----33->$##&@.......",
+".........@##*$===>----$%@+++........+&%*==$=>**&@@@&#*$=**=*#&+.....+@&@&@&@@&&@@%==$#*#&++.+............+&&%#$>-,>>##@++.+.......+++%#$$$>>=>===*=$**%+............+@&$=>--->=>=>---,==**%%&@......+&%**$==>==$#*%@+.....................+.@#$=-,->=%@+...........+&%**$$=,=>***##%*$=-,>$%&+......+@%#*>>=>$>>---->,>*&+.............++%$>>--==$#&++.............+&#*$===$*%&++++.@@#%%%*&+........+&**#%*==========$*$%#@+......+%#==$%&@@@++++@&#%**$$*%+.......+%*=$-=*%++++@%&%%%%#&&+.............+&%**=$**%@@...........",
+".............++@+@%&&@+................+..+.........+..++++++.....................+.+++......................+.+@&+@......................+++@++++.+....................@@&&&@.+.+@&&@+..++..............++++@++..............................++@@&++.................+...+@@++.....++++&@++.............++.++@&#%&&@&@@...................@+@@++....................+.+++++++........+...+.................++++++.++++.+.+.............+............++................++@@.......+.++......................+.....+.............",
+"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................",
+"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................",
+"...........................................................................................................................................................................................................................................................................................................................................................................++@+@@+@++.................++@&@++............................+@@&%%*#*##%&+...........................+.............................................",
+".......+@&&&&%####&&&&@+....................+@&&&&&&&@+................@&&&&&@+++@&&@@+.....................+@&%%&&@++..+.............+&&&&%%#&&&@+++@&&@+................+@@+.......+@&&@+..............+@&@+.........................+@&&&&@+.....+@@&@+..............+@+...+++...+&%&&@@+..........+@&&&&@+...+&&&&&&&%@+.............+@&&&&&&&&&&&&+................+%$--;-;-;---$*&+..........+%*=>>-,$#&@+........................@#>-;!'''''!;-$&+..................+@&%*#**#%@..........................................",
+".....+%#*==$>===$>$=>=*#%&@...............@%#**=*=*=*%&@.............+%**=**==$*#*=*$*&@.................+&#$*=*>>==##**%#@+.........&##=$>$>=>==*$%$$**$#%@+.......@%##*#$**#%%#####$==*$##&.......+%#*#$==*##&@@@&%####*##@........@#$***=**&@@@@&&*=**$##&+......@%##*#$#*##*#####$=*==*#&+.......+%*=>=*=######*===$>**#@..........+%#==$>=>=>====*$$##&+..........@#,-'){]]F{{);;,*%+........@#$--;;;--==*&@.....................+&*,-!)]FFF]])~;-$&+................@#*=,,-,>=*%&.........................................",
+"....+@%*$$=**=$===>*$>**#%%+............+&&%*$$>$>*$$##%@+..........@&%$$>>>=>=>=$>=*$#&+...............+@&%*$=*=$=>=$$**$&@.......++%$$**=**=>>>>>=$>>$$*#@+......+@%*$**==**&&%%*****>$**%%+.....@@%#****$$#%%&%&&**==$==*&+.....@&&%*>>$$*$%%&&&%%$$>$=*$%@.....+&%*$=*=$=******>*$>$$>**%+......+&***$>*>=**#$$*$*=*>>**&........+@&**$=*$=>*>>$>>$*=*$#@..........@#=-!){]]1](~;->$%@........&*=---;;-->*#@+.....................@%*>-;){]}F({)!-=#@.................@#$>=,-->$$%&+........................................",
+"...+%*$>==,------,-,,=>***%%+..........+@&%*$===,=,,=>**#%@........+&#$=,,-,---->,=>>***#@.............@%#$$>=>-----,,=>=*$%&......+#**=>,>--;-------====$$%@......@#$$=>,>>>$$$*$*>=>>==>=$#@.....&#*$>=,>===*#$##$*>>>=>>>=@.....&#$$>===,>***#$#$$====>==*&.....&#$>=>>>,>,=>>>>=>==,,=,=*%.....&**=,>,=,=>>$=*=>=>>,==,=#+......+&%%**=>--,--,---,,=>=**%..........&*=;_|}}[221(!-=*%@.......+&*,;_((~~;->$%@.....................@%$=-;_|}}}2}|_-,*&+................@#*,-;';;-*#%@........................................",
+"...@#*=39cc00c000cc9c9c3,=*#&+........+&#$>,3cc9cc9933>=$*%@+.....+&*=,3ccc0c0;9cc9993>=$*&+..........@%*==39cc0c0c0ccc93,=*#@....+&*>,399c;0aaacaac0ccc33=$%+....+%*=>3399933=>=>,9399c93,==&....+*$=339cc9933=*>*>=33c39933%....@%*=339c9393>=*=*=339c9c93=#....@*==9ccc;999933339c999c993=%....@*=,399c99c3939339c39cc993=&.....+@#$$>339c0c0c0c0ccc9993=*@........+%>3abgiimk7fba;>*&+........%*3abe7ebdc3$%&+...................+&*>-;0bf7iiiigb0-=%+...............@%$,;a_bba0,*#%+.......................................",
+"..+#=c0_bbeeeeeeeeeeb|bdd09>#@.......+#=,99adb|eebe:bdac33>*&+....@$3c_bbbeeeefeeeb|bdcc3=*#@........&*=39ab1beefeeeeeb:bda3=#+...@#-9db:eeeefeefeffeeebbd03=%....%#3c_bb:b:bda9c!a(b1eb1da09$@..+%=-'db1beb{b~0c39c!d|b:b|b03&..+#>c'db:be1b_acc9c~db:b1bbdc>@..+#=9~d|beeebe|bd_db|be1be1b0>+..+%=cadb:b:eb:b(ddd1be1beb1b0,+....@%*,cc~dbeeeeeeeeeeeb:bd03%.......+@*>9bgoqZonVl4bac>$&+......+#,0|7kkk7e{03>*&...................+#*,ca14<VXnoopibc3*&..............+&=3cde777eba9=%&.......................................",
+"..%-~FzyHtGtGtHtHAAHABtyzr]~;$+.....&$-!)F^syAtHHtGAtyr^F{~-,%...+$;{^sxBHAGGtGHAHHAAsz/{';-=&......@*;~{FzxHtHAGAHAHtGGtyr{!,&..@>;{^stAGttGtHHGHAGtGAGty/{;$+..@-;{/sAtGtGtsz^^rzyyABGAtszF;#..@=']rsABtHtAys/]{]^zyAAGHAAsF,+.@,!FrytAHtAtsr/FF/rsxBBHAAyz],++#,)FzytHtAGtAtsszsyAGAHtGAyz)&..@-'FzytBGtHtHyszzsxAAGtGtAyz{=...@$-;)/zsxxtHAGGAGHtGAHHAx/{=+......&#-;{rwvuELGHBxr])->%@......&>;{^sCCCAys/);,=@.................+@=-'{/zyACGvEEvxI{'-$+............+*-!{/zxGGGts/{'-*&+.....................................",
+".+*;(/xGLLELuEuLEELELHCBAs/{!>@....&*-;)]^zyACGLLLELLAxzrF)'-*+..+$!FzyCLLLLuLEELLLLHByz/{);-$+....&*-!)/ryHLLLLLELELLLLDHs/);*..&,)FKALLEuLEELLLELELELLLts/)-+..%-)/KtDLELLGCszrzyBBGDLLHBxr)=+.%,)/sBHLLLGCByz/F/zxAHDDLGHyr;@+*-)/sAGvLLvCByzr/rsACHDLLvHy^;@+#;)/stHLLEEELHAxsxHDLEELLGAx{#..@-(ryBGDLEELvHtssxGLELLELGCx/-+..%>-!]zyBCHLLLuELuEELLLLLAz];@......@=-~{IxEEELGBBsr]~;>%+......&$;)/zyBBCBxr]'-=&..................&$-!]/zyBCGLLLDAr{!-#&............%$;~]^JALLLGyrF~-=*@.....................................",
+".+%-'1<OwNNNwNNNwNNNwJKKr^]'->@....&*=-;){FrKJwwNwNwwPI[F('-=&...+#-)}QPwwNNNNNNNNNwJKQF_~;-=*+...+&*>-!|}<OwNNwNNwNNwNww8[_',%..&=;_[6wNNNwNNNNNNwNNNwNw8[_;>...&=;([8wwNNNwPQ1FF[IKJwNwJP^]!%..%=;(/IJwwNwJI^F_)|FQKJwNNwJIF-++&,;{[IJwNwwJK^2]{F[IKJwNwwJI]>+.&,'{[KOwNwNNwJI[[IPwNNNwNwK[~&..+*;{[IOwNNNNwJI}[QPwNNwNwwP/(=...&*--~FQKKwwNwNNNNNNwNNww8[_-@......@%>;':<YNNwJKIF_!;=#+.......+#=;)]//rrI/]'-=$@..................+%*=;)(FQKJwNNO5}'->%+............&*=;~:[8SNw8Q:);,*#@.....................................",
+"..#=c(<OSNNNNNNNNNSwROIQQ21_;-#...+&*--!_|}[<POSSSNSO8Q}1(~;=#+...%>!|Q5YNNNNSNNNSSO8[Q11(!;>*&...+&*=;~(1<8YSNNNSNNwNNNR67(;,@..&=c_e5YNNNNNNNNNNNNNNNNS841;*+..&=3a28YNNNSO5}1(|}Q5OSNSO<[|;*..@>-~}<8YNSOPQ}|___1[<PYNwO6[(,+.&*;_}<8SNSOPI[}1||}QIOwNSO6[(>+.&=3(}<6SNNNNR8<}}46ONNNNSOP[!&...*;(2<OSNNNNY8[22[6ONNNNSO5[_>+.+&*>;_1[IIPYNNNNSNNNNNNNY84_,@......+%*-a1<OSSR5[1_;-=*&........+@=>'_:2[QQ[('->*&...................+%$>-!_:}5OSSO<1!-*#@...........+&*>-~_[5YNR6[|~;>$&@.....................................",
+"..#>cbfijqoooooojojojpih574ba;#...@*=3abbf7hhmpjqjqojpiiieba3*+...#,0b4iqUUWWoqqjjopmhh4eeba3*@...@#>3abe7hpoqjqjqjoqqWUWjgbc3&..&=0dgmqWUWqqTTTTTUqqWWUWjgdc$+..@>3aepqUWWqmgebdbe4hjWUWji4bc$..@>9afgpqUUqi7fbd0dbfhpqUWqmgb>+.%=3dehpWUUomh7feeefkipqUUqigb>+.&=3degjqUUWqqigfegijqWUTUji4c&..+*;dfgjWUUUqqigff7ijZWUUWjif0>...&*=;deklVmoqqjqqqqqWUUUqpfd=+......@#*-cbgqUWqgfd03>$&@........+&$=;a|e4iikfa3=$#+..................+&%$=39degjWWqibc9$#+...........@#=3;dfgijqqpgebc3$#@.....................................",
+"..&,cdeimXnnnnmmVlmlmmVVVVhkea$+..%=9d1kklllllmlmVmmmnoqojhba>+..+%-cd7ioWUWonXmmmlVVVVllh710,%..+&*,c17klVXXXmmmmXmnZUUUqhfa9%+.@3cb7pZUWqojqTTTUqpnoWUWogea>+..&=9d7pWWWZml7eb|be7hnqUWol4bc%..&=cdehnWWWom4ebddb:ehpWWUZmh|,++&>cdehnWWWomhkkkkkkklnWUWZm7b,+.&>caehoWUUWonVk444lnoZWUUoi40&..+#9aehjWWWWonlk74klXnZWUUolfd$...&=30bklVVXmmmmlmmpooWUWWjgd,+......@#>90eiqUUqifdc3=#@..........+*>30aehmomgea03=%...................+%$>-0aeiqUTqifa3=%&...........&=-9deklXnnXXVk4b03=%+....................................",
+".+*3':rxBCBCBCBBAAtACCBCCCCAxF=..+$!{^sBBCBCBCBAtyAACHDEEvAs/;&..+$!{/stvELLGHBCABBBBCBCBCBs/'$..+*;)/sACCCCCCBAtBBCCvEEEDyK/'$.+*!]ryDEELvGHvuEEEEGGDLELGxKF-@..%;_/sGEEDDHtsz^^rrsyDDEEGts^)>+.%-~FzyHDEEGByzr/F/rsyBDEEvHs/;++%;)/sAGEEEDHABBABBBtBGEEEvHs^;@.%-~]^sGEEELGHBAxAtAHGLEEEDtz0%..@-)]ryGvEEvvHAtyytBHvDEEEGAz(-...#-!]rxCCCBCBCCABCCHGDEELGs2!&......@$3)FKtEEEDts/{'-=@..........+*-;~]^xGDvtsrF);-#+.................@#=-;{FzxvEEEAKF~-=%.........+&$-'{rsACGvHHCCByz]);=&....................................",
+".+%-!)^sBACCCCBtyyyyABCCCBCCxF$..+>;]zyCGHCCBAxyssxytHGGvvAsr~%..+*;{/zAHvGvHCCtAxyBBBCCCCBy/)=+.+>;{/sACBCCCCAAyxxtBHGLvHxzF'*+.*!FzJGLLLvHAGEEEEGCCGLELvyz];@..*;)}zAGGGHCBsr/F/rzxBLLLvys/)$..@>'FrsBGGvGtsz^F{FrzyAGGGGtsF-@.*-)FzxHLLLGCBACGGHBBAvLLLLAsF-+.&>!{^KxGLvvvHCBBBCCBHvvGvHx^~&..@=;]/ztGvvGCBCBxyACCCGGGDAs/)$+.+%-!]ryBBCCCCBABABBHGvvvHtzF-&......@$;~FKAGLLLAz/);-*&...........&=-!)}stGCByzr]';$%+................+&*,;)FItGLEEtzF~;=#+........@*,;)]zACHHCCBHCHBzF);-*&...................................",
+"..@$,;)][IIzKK^FF]]]F^rIKrKzQ~%...$-~2IOwwJKQFF1{{]F[QPPPPIQ};@...&=-~|[KPJJKI^//}F/^IrKzKr[{;%..+#,'{/IKKKKrQ//2FF[/IPPP<2{~-&..&-;1[6wNwJI<PwNNw6IIJSwwOQ1'>+..@>;(:IPJKIQ/2)';!~1[Pwww8Q:);%...#>'(1IKJPKQ[]_~!'(12QPKJK[:!*+.&=-~{[PwwNJPIIJwwwPIIJwwNOI2)>@.+%>-'{}[IPJwwJJKKKJwwJP<<[:'=+...#=;~1}Q<IPKKrQ^^IPwwJP<Q4:~,@...@*,;)FQrIr^Q^/[^IJwwJ5IQ[('$........&=-!:<ONwO<1_;=*&.............%*=;':QIIII/});,*&+..................@%*-!|QJwwO51_-,*@.........@#=-;)}IJOPIIIKJOP[_!-=&@...................................",
+"..@%$-;(}2QQQQ}:(((_(1}[QQQ[2~%..+%-~1<YSSO<}1~';;!_|}QIIIQ[};&...@*-c_}[IIIQ[[211||}[QI<QQ2~-#...%,-_:QIIQQ[[1|111|}}QQ[[|~;=&..@=9_}6OSY6[[5RSNR54[5RSY64(!$+..&=9'|[QIIQ21(!--;c(:5OSY621!-&..+&$-'(}QIQQ[|(_'9;~(:[QQ<Q}_,%..&*-c(15YNSO<<<OSNM6<78MNS8<1!*...@*=-c_|2[<OYOII<KJYM6[}1|~-*+...@#=3c(|}[QIQQ[}}[POO5[}|(!-#+...@#=-'_:[[}}212:}[<OY5[}:(!-%.......+&=-0(<ONR8[_;,$%@.............+%=-c_:[[Q[[1(;,$#@..................+&*=c_e8MSO<1!->#@.........@#$,!_:<OO5[2}<6O6}(;-$#+...................................",
+"..@*$9cd144hhhebbdddbe477hhh40&..+*9afiqUWjifda;9-90bfhhllhkfa*...+%>3ae4hlik44ebb:bf47hlhhed3#...&=9de7hlhh47ebbbbbefhk74bcc$&..@>;afpqqqpggiqUTqiggpqWqpfd0*...@*30b7hhhkfbd;3,-cafiqWqpgdc3&..+%*,;dfhhhhfedacccabe4hlh7eac#..&*30dfiqUWqigiqUTqjiipqUWjgbc*...+%$=90df7ijWqpllmoqqpgfbdc,%....+%*,9abb47hhk4f7gppqmgfba9-&....+&*,cdbf44eeeeefgpqqpgfba9>%.......+&*3abiqUUjgd3,$&@..............&*>cab77h47fba9=*&...................@*>9dgjUWqib0-=%@.........+%=-9dfiqjifffipjpfbc3>*@...................................",
+"..&#=9ab4klV6lk4:bbb:4klllll7d#...*cd7mqWUoi4bac399c(4lVVVVlkb=+..&$=9aellXXmlkk7447hklVVll7d9%..+#>9dellVlVVhhk77447kllh4edc,%..&*3abioZZm7gpqUTUjghmoZqpgdc$+..&=30ekVlVlk4bac99cd4mWWZjhba,#..@#$30d4lVVVk4f1dd_be4lVVVl4d3%..@=9cb4pZUWoXVjqTTUjmljZUWZifa*....&*>30de4moZZnmmnqWZphfbdc>%....+&*=3abe4hVllhhhlnZomhebdc>%.....%*,cd|4f444447kloqoj7fbac=&........%=caeiqTTqib0,=$@..............+&=30b4kllVk41a9>#+.................+&$,cbgjWTqjfa9>$@.........@#>90b7moom7e4inopgbac=*@...................................",
+"..@=;~FzstACBBxszzrrzxtCBCCCyF$..+-{^sHEEEGts^{~''~)/sACCBCAxr;@.+%-;)/sACHHHBBCBtACCHHCCCAs^)=..+$;~FzyACCHvGHGHAABAAAAtsrF(;%+.#-;(^JtHGtxywEEuuGyxAHGAsQ{~>+..#-~FztCCCCAsr])~~{/JtEELvyKF'*..@=-;{^sBBCBCAxzr^rzxtCCBCts/'*++#-~]^sHvEEDHGvEEEEEHHGEEEvAz}-....&=-!)F^stHDDvGvDvDvAsz/]~;#.....@$-!)/rsACCHGGvvDvGtsr/]'-%.....#>;!{F^rzzsxABCBHGvtsr/{!-&.......+$;~FKtEEEDxI{;-$@..............+&=;)FzsACCCxsr]';#@................&#-;([JDEEEwz4);>%.........&=;_]^ztHHtszzxCGAsr])3,&...................................",
+"..@*;~FryABCCBAxzzzzsyBACCBBt]*..+-(/stGLLGAzF)!;;;{/ztCCCCCAr;@..*-!)/sBBBCCCBCACCCGLGHCCAz/'*+.+#-!{/sABHGLLuEELGBBBBAyzrF~-*..@>;~)/rsssssGLEEDwxssssz^]';=@.+%-)/zACCCBxs/]~'){^sAvLDGxrF'$+.+#>;)/stCBCBBAsrrrzyBBBCABzF'*..#-~]rJtLLLLGHLuEuEGGCGLLLLtz],+..+&*=-;{FrsACGLLELvGBxz^]);>%+....+@#-~)/zxBBHLLLLvGAyz/{~-$&....+&*-;')]F/rzABCCCBCByz/{~;$@.......&,-~{IyLLEDyr)!-$@...............@%-;)/zAABCBts/]!,$#@..............@>-;{^sHLDDHz]!;,#.........@$-']/zstxszrrzsAyz^]~;$&...................................",
+"..+&>-~{F^IKKKIQ2[2[^QrKrKzIF~&...$'([6wwwOQ|';,>,-;)][/IrIrQ]-+..@*,;)}rKKKJsKKKKKJwwwJIr/]~-&..+&*=;)]FQIJYwwwNwOJKr^^[F(!-*&...&>=-;'(1::[5ONSwP4:|((_!;,*@...@$-)FIKzKr^F(~---!(26wwYP[|~-%...+#=-)F^rIzIr^/]1]F^rKKzI/F~-@..@=-!{[PwwNwJJOSNNNwJwwwNwOK2~*.....&*>=-!(F[IPwNNwJPQ2]~;->*+.......+%$-;)}IKJwNNwOPI2(!->*@.......+#$,-;~)|F^KKJKKKQ}(!;>$#........&*>-!1<OwwO<(;,*@.................+%=-!{/rIzIz[]);=*&@..............+#=-!|Q8www51~->%@.........+&=-;~((F1(___(]1](~;-$%+...................................",
+"...&*-;(|}Q58O85<5<5<<IIIQ[}|-@..+*3_:<OSR84(;->,>=-!(}[[QQQ2),+..&#=-~:QKIIPPOJJ8PJ6PKQ[:1~3*&...+*=-;'(|[<5888O888855<<f|c-*@...+&$*>-9'~(|[OSRY5:('!;-,=*&@...+#-;|[<QIQ21~;---ca15OSY84(;>&....@#-;_:}[[QQ[:(((}[Q[[[2:_,#+..@*-c_:5OwNwOO86M86OJOYNNSO7|!*.....+@*>,-~_}QP66O65<[1_'-=*&+........+#=-'(:Q5YNNM6Q:(!-=*@.........+%*=,;~(1Q6JJ8I[}1~;,>*&........&*==cd<OSR87~;=*%..................@%*-;(|}[<<5}~-,*%+...............@$-c(46RRY51'-*#@..........@%*,--;;;;----;';;-=$%@....................................",
+"..+%*9cdefijWUUWqWqqqpmhhk4:d,@..+*3afiqWWjgba;3-33cabf7kkhkfd=+.+&*=3ab7llmnqqWWWWopmh4ed00-*@...@#=>30abbf77h5imnqZqqqqifc9$&....+%#*==99afgqWUjib_09->$%&+.....%-cb4hh54fba9->3;afiqWqpfb0=@....&**3a|f44hkhfebbe7hk4feda-#....&>9afiqWUWWqjmgiipqWUWUWjgba*.....+&*=-9ab7hhm6m6lhkfdc9=*%.........+&*>9abfiqqUqifba;,$#@.........@&#=-;ab7ijWqjifbda;3=*%........@#>,cbijUWjgdc,*&..................@%*,9adbeijjgbc,$*@+..............@*,;bgpWWqibc9>#@..........+&%*>>,-=>,>==,-==>*#&+....................................",
+"...&=3ae4kmqUTTTUTUUWZXXVl7ed,@..+*0d7pqUUql7:da'00db[kVVlVhkb,+.+%$,9_4hVXXZZWUUUUWnll41bac3=&..+@*>330d|e47kklVXoWWUUWWjga9=&.....+&#$>-cdfiqUUqpfdc3>$#%@.....@$3~ekVVVlke(c9399afpZWWpgea3%....@#>3ab44klV<k4e4klVlkke|a3#+..+#30b7pWUUUUWZm6hmoWUUUUWqmf0>.....+%*,9abeklVVVVVVVl4edc3=#+.........@*=cab4iqUTqpebac,*&+........+@#>>3a:7hnZWUom4e|d03,=#@.......&*=9abiZUUoibc9=#+..................@*=90d:7moomfdc3>*+.............+#*30dgjWUqjfa9=*@..........++&%#*$$$$*$***$>$#%&+.....................................",
+"..@$;~FryAHDEEEEEEEEELCCCAsrF-&..&-(^sGvEELHxszr///rsAACCBCAxr'&.+*,!)/stHHHGEEEEEEEGHAAsrF)'-%...#>;~{F^zsxytCCCBGLELEELGsQ{;#......+#>9')FztLEELAK^{~;=$&+.....%,~FzABCCBAsr]{~)]^stEELvyzF'*...+&=-!]^sxACCBAxsxACCBtysr{!,@..@=!{^stEEEEEELGBBADEEEEEEGAK]-...+%$-;~{/rxACHHHCCBCCxs^]);-&........+#,-~]^sGuEEEGK^]~;,*+........@*,;')FztAGLEEvHssz/F{~!,#.......&=;~FKAuEELyK]~-=%..................+#-;(F^stGvAs^F{!-$@...........+#-;~{rxGEEvAKF!;>%............+&#%#**###*##*==*%+..........+++++&&&&%@+++&&&&%@+.......",
+"..&$;)FzxtHvLEELLELLGGCCAAzF)-@..@>)/KyvvGHHHAssrzzsxCCGGCCCsF;@+@*-'(^sBBHHCvvvvLvLvGCAsz/]!-#..+%-;{FrzssyxABABCCvGLLLLvyr]'*......+%>-;_]IyLEEEts/]~-,#&+.....%-)FzyCCCBtsr/]{{F^JHLLLHxrF'*....@#=;)/zsACCCAyyABCCCysr/{!*@..@>;{/sAvLvuELLHBBCHLELLLvvxz]=+..@*>-!)]rzyCHCHCCCCCCBsz/{!-*.........&=;!{^KtLEELts/]~->%+.......+#=-;'{^sBCGDLDvAxszsr/])!$+......+*-~{ItLvEDAr]~;=*&..................&$;;{/ryBCBsz/]);-#...........+#-;)FKyvDEutz]~;,#...............+@@@+.+++@@@&&@.........+@&*#%##$$$**#%##$$$**####+...",
+"..@%>-~1//IPKJ6KI5I5<Q[2F]_;-*+..+#-!|[IPJJJJJI[[/[^IPwwwOI[]_>..+&$-!)2IKJJKJKPQKPPOwJK[F|~;=%...%>;'F[Q/^/F/F^[QIIPJwNw8<1~-#.......@*=-;~:<OwNO<}_!->#&@......@=-~]QKzKrQ/F__~~(1[Pwww8Q|~-%.....@#=-!)]QPJJK^^QKJJK[])!->&...+#=-_:QPJJwwNwPIQIJwNwwJPP[{;%...@$=-;)(F[IJwJPIIIPJwKQ/|);-#.........&#=-;_:<ONNw52(!-=*@........+&#=-;'|QPwJJKPIQ2}/Q/}{~;%........&$-!1<wNNO<1~;,*%@...................@*=-!(2IKI[F|';,*&...........+&=-;_:<OwwO5}_;>%@......................................+@&%#$$$*=*$$$$>$==*>>$**%#@...",
+"..@$=-!(:2QI5I<[[[222|(_;;-,*%+..+%=3'|[QI5OOJ5Q[[QQ<6YSN6<:_;%...&*-;_:QIII<Q[42}[56OO<[:1_;,&..+#-;(1Q<Q[2111|:122<6YSY84(!-&......+&%$=;_|<OSNO5|_;-**%@......@%-;(}QQQQ2}|((_((|}5YSY5}(;=@......@#=-;_}5O85[}[5OO5:~;,*&+...+@*-9(}IIPOYY6<[}[5YSOPPIQ}'-%...&*>-'(:}Q5OY8<[}Q6OOP<}2|'-$+........@%>-!_1<OYSO5:~;->*&.......+@%*,-!(:<OOO<<<[2}2[QQQ}_;=+......+&*-;(7OSS6<(_-=$#@...................+#$=-_1[[[21(_;,*&...........+%$=-0|78RSY5b!-=#@......................................+%##$*==,---,==,-,,--,,>$>*@...",
+"..@*=9ab44hiVih777ffeda933**%&....&*=9affhimqomiiikhipqqqjgf09#...%$90bfhmmmi44feffipjjphg7ba;%...%3ab4immh7eeeeeeffgpZWWjgbc3&......+%*=3;dfijUUjifac9>*&@......+&=;dekhhk74febbbbfgiqqoifd;,@......+%$>-0fippmiiimjpgbc3>$&+...+%$-cdfhlmmoophf77ioqqmmk7ea>&...&=-cdfgiijqqpg7fgiqqpmigfbc,@........&*=9abfgqTUqifd0->$#+.......@*$90dbgiqqjmhh74f7hmlihfa,+......+#*30bgqWWjied0->*&+...................@*>-0b4474eeda-$%..........+@%>-9abgqWWqiba-$%+.....................................+@*=,339c0aac0c;9000c0cc99,>#...",
+".+%>;a:khVmnonmlhkk4ed09,>$#&@....@$>9ade4lmXoZqoqoXVmppppi4b0*+.+#-0b4inooonmh74efhmonoXopifa>+.+=9bfmoqqjmkhkkkkklmoWUWniea3%.....+&*>30begjWTUWph4dc3>$&+.....+#=90eklllllhkkkhkhlmnnXifa9=%......+@*=3abhXnopqooXmkea9=#%+....%*30b7VlXonnmhk4kVnoonXXlfa3&..@#30bfijqqqqnmlkkhmnoooqqphea*.......&*=9ab4ijqTTUjgeb03,*%+.....+&$-a17hmjWWomVllhhlmoZom7b9%......@&$3cfiqUUqmh4{a3>*@...................@#=3~b4klllk7|a3#+.........@#=-0dekmqWUqpea9=#@......................................#,;ad(b1ee2ee:e:eeee4ee:bdc>@..",
+".+*;)/stCCHvDvvCCCtxz/('--#%+.....&=3~{FrsxBHDEEEuvGCCCCCAAsrF=+.&>!]rsADEEEGHAtszsxAHGDLvvts/;@.@-]rJGuEEuGCCCBACACCDEEEDyKF'*.....%=-~{/zyBvuEEEvttzF{;-$&+....+*-~]rxACBCBCCCCBBBBHHHCtz]~,&......+&>;~FryCGLEuEDGBxrF_;=%....+*-!]ryCCGHGBBBxsxBHBGGHCts/!*..&,)/zxuuEEEvHBtAttBHHvEuEvAsF-.....+&=-;)/zxADEEEEGBxz/)'-$&.....%>;~/stBCvEEDHCCBCCHGLvDHxr{*.....+%=3'{ItEEEEGAtz/{!->&..................@*-'{/zxACCByz/),#........+$-!{/zytGLEEEwz:~;=&.....................................@-)FrzsyxAABtxtyxtyBtBBByxz/~#..",
+"..#;)/sACCCvLLGBCABsz/);-$#.......&=;!(/rsyBCvLELLLvBGBCCCAyr{,@+&-!]rxHGLLLGCCAszzsBBHvLLLGxr;@.@-{rsGuEELGCCBCCCCCHLEELGtr]!*....+%,;)]^stCGEEEuvCxz/{!->*@....+$-']rsAACACCBBBCCCCCCCCyr]',&.......@$-!{ryABvEELvHAs^{~-=&....+%-~]ryCCBCBCBysytBCCCBCCBzF!#..#-)/sAvEEELGBBAyytBBCvLEuDAs/-+....@#=-~{/sACDuEuLvByz/{!->#@...+#>;)^sBCCHLLLBCCCCBHLELDvyr)=......&*-!{IAvELLGCAsrF);-*&..................%=;)FzyBCCCBs/);%........&$;)]/zyCGLLEDtzF~-=@.....................................#;(/zyyABABCCBBABBABBCCABysr)>..",
+"..&=;_FrKJJwwwJKKr^}{';=$&+.......@&$-;)12KJwwOOJOwwwwwwJJI[]!*..+*-!{2IJwwwJKI/F|]}^IPJwwOP[|-+.+$;([6YNNwPKKIKrKIKKJwwOP[(!>&....+&*>;~{FQKJwNNSJKQ/(~;=*%+.....&*,;)]/^rKzKzKKrIKKKKKI^|'-%.........@*,;':QKOwSwOKQF'--*&@.....&=-;{/QKsKKKI/}FFQzKsKKK/F~>@..@=-_1<6SNNwJKI^//^IKJJwNwOI[)$.....+&*,;!)2^IJwNNwOKQF_~-=#@.....&*,;)FQKKwwwOPKKzIKKJwww6[|;&......@#$-;1<OwwwPKQF|);,=%@..................@%=-!)/IJwwKQ_;=&........&*,;)1}QKPwwwO<|!-=%+.....................................%-~)F//rrrrrrrrrrrrrrrzrr^/|;$..",
+"..&=-':}QIKKJPI<QQ}|('-$&@+........@#>;_(}<6MMOPPJYwNSSYYO<}(-&..+%=;_1<P8OJPI[2((((:2QP8J8<}~,+..%-a128686PIQQQQQIIIK565[1!-#+.....+*=-!_1[<66OO85<2:(~->#&@.....@#=,;~(1[QIIIQQIQQQQ[[}|~-=%.........@&*,;(}[P8OO5[}_;=*%@......@#*-!|[QQIIQ[:((|}[QQIQQ2(;*+..&*3!12<OOOOPI[211}2QK8OO88[1'$......&$=-'(|[I56OM8P<[}(~;,*&.....@#$,;(2QQK6O8IQQQI<I66O6<1~>@......+%*>,_25OOPPQ[}('-,$%+..................+&#$-0|4OwY84_->&........@*>-'(1[QP68O6[d;,*&......................................&-!(1}[QQIIIQIQQQQIQIQQQQQ}:!*..",
+"..&=9ab4hlllmVllhh41ac,$@+.........@#=3abfijWqqmmpZWUUWWWjied3&...&=cdb7kllmlhkfdaa_dbehhli7fd>+.+%>;dfg5i5hkllllhhhhh74fba9,#......+%>-0a:7hlm5mPlh7eda;,*%+.....+&*$,-c_e75hlhllhhh74ebd;=*&........+@&*>9ab4h5l5hfba9>#@.......+%*=cde47777ebdade777k77e0c*...+*3abfh5mmllh4:bb:7hlllmlh7ba*......@*,30be7kllmmmlk7fba;>*&.....@#*-cde47hlmllhlhlhlVllh7ba>........&*>9abhhmlhhkfba;=$#@..................+&%$=9dgpWWpga-*&........@*,90(e4hlVllhea3>*&+.....................................&3abe7h5lihl5ll5h5l5lk5khh4e0$..",
+"..&=9d:kllVlVVVVVh7ed0,*&..........+%>c_bfhqWWoXXnZWWUWWWom7b3@...%>cde4hVlVVhked00c0b:7llkked,+.+%-9de7khllVllVlVVVlk4fbba9=&......@%$99dbklVVVVVVlk4ba9,$%+......+%*=-9abkhVVVlVlVhk4eba9,*@.........@%$=9abfkllh74|ac=*@.......@&*>9de4k7k4ebaaab47774eba3%+..+%3~bf4hVlVllkebbe4hVVVVVk7b0*......@#=;adekVVVVVVVlk4bac3*&.....@&$,0de7klVVVVVVVVVVVVVl7b0$+.......@*-9aehVVXVVkke(09=#@...................@%*>9agoqZjga9=&.......+%=,cd:4lVVVXVl7d0,$#+.....................................%9(eklVXXXXVVVVXVlVlVXVXVVl4d$..",
+"..%,!]/zsAytyAAtAsz/]);=&..........@*-~{/rxtvvHABBHvLvLGvtJrF-&..+*-~]/rzyxtysz/])~){{^zsssz/{,+.@$9_{/rzssxxyAAtBBtysr^F{~;=#......@#>;'{/zsttBtAtxsrF{~;$%+.......&*--')FrsxAABBAAxsr/]);>#+.........@#>-;)F^zsssz^F)!-=%.......+&$;!{/rzzrr/]{)]F/rrrr^F);#...+*;(F^zsyAAtys^/F/rsxtAtysrF~=......@=-!{F^sxttAtAtyz^]{';=#+....@%=;'F/rsxAABABAABtAAAtyz/)=+.......&=-~FzxACCBCBxz/{~-=&...................@#=-!|IxvGxI{~,&.......@*-!{FzsACCCCBtsF);>*@.....................................*'/zxBCCHCBCBCCCCCCCCBCBCCBsF-..",
+"..@$;')F/rrrrrrrr^F)~;-%+...........%,;~)FIzsssssxyAAByyyz/])-@..+&,;~){F/rrr/F)~;;;!){]F/F]{!=+..#-;~_{]/^rrrrrrrrrr^F{)!;,=&.......&$-;!)]/rrrrrrr/])!;=%+........+@*,-!)]/rrrrrrrr/F)~'-$@...........&#>;!){F/F/]{~!-*#@........@*,;'){]F]{)'!!!))){{)));,%+..@#-!))]//rrrr/]))){F/rrrr/])-@......+&>-!']/rrrrrrr/F))!;,$&+.....@&=-')]/^rrrrrrrrzrrrr^F);=........@%-;)^yABBBBtsrF{!,*@....................+*=;~F^rzrF);,&.......+%-;)F/sAAABAByr]!-=#@.....................................*'FrstAACCCCCCBCCCCACBCBCAtz{=+.",
+"...@#=>-;;'''!'!;;;,=*#+.............@#=,-~(|(()()]]]F]:{)!-$%.....@#=>--;;';;--,>>>>=--;-;->*&...+%*,,--;;''!'~~~'!!;;-=**&+.........+%$=>-;;'''''!;-,##&+...........@&*=,-;;'!''''!;--==%+............+@&**>--;-;--==*++..........+&$=,---,-,>====->>->,=$%.....@#$>,-;;'';;;--->-;;;';!;-,%.........%*>=-;;'!~!'!;-->$*&+.........@#$--;;!!'~~~)~'~~'!;;-$&.........+*=-')]FF]]{)~;->%+......................@#==;!__';,=%.........+%*-;!)]F}F2]{~;=*@+......................................&=;~){]F}F2/2//[/[/[/2F2FF{)>@..",
+"....@%***>>>,>>>=>$$#&+..............+@**=-;;!;;;;'~_'~!'c-=*&.....+@%**=>>=>=>*%&&%%$***=*=#&......&&*$===,=,,>=>,>==**#%+............+&#$*>>,,,>=>=*#%+...............&%%*>=,>,,>=>=*#&@+...............++&*#*$=***#%@.............+@%%***$%#**#&#%**##&%&+......+&%**$=====*=**$**>==,==$%@.........+&%$*$>,,>,=>=**#%@............+&%**=>,,=------==>=*#@...........@%>=--;;;;-,=**%+.......................+@#$=,-9->$&+..........@#*$,-;;;';;-,=*&+.......................................+#$>--;;!!!~~_~~_~_'__~!';;>#...",
+".....+@##**$***$*#%@@+.................&**=,99->>-9;c9c33>>$%@.......+@%###$###%@..+@@%&%*##%+......++@%#**$***$***$*#&@@+..............+@&%**$**$*$$%@+.................@@&$*$$**$%%%&@+...................+@%&#**#&&@@...............@@&#&#&@+++..+@@+++..........+@&%#####&&&&@&&&##%#%%&+...........+@&%##$*$$*$$#&&@+..............+#**$*$*$*$******#&&+...........@&$*==,-,>>$*#%@+........................+&#$==>=#%@...........+@%#$*,,,-,,=$#&+........................................+%***=,,-33-339-9333,333-,>=#...",
+".......+@&%&##%%%%&@...................@@%#**>**$*,>,,,>>=#&@.........@%%%&&%%&&+....@+@@@&&@.........+++@@&&%%&&@&@@@+.....................@&&##%&&&&@...................+++&&@&&@@@@+......................++@@@@@@@@..................++@+........++...............++..++++++.+.++.++.+...............++@&&@&@&&@&&@+.................+@&@%&&&%&&&%%&@@+.............+@%##*==*=*#%&+..........................@&%&###%&+..............+&&%*$=$**#%&@..........................................+@%***>$*=$========$=**=**%+...",
+"..........+@&&@&&&@....................+.@%#&%%&%%#****%%#&+..........+&&%@&@@+@+....+....................++.++..............................+@&&&@@%&@.......................+++++.+.........................+.++.+..+.......................................................................................++@+@+.++....................++++++@@+++.+................@&%##%#*#%#&+++..........................+&&&&&@+..................++@%%*#%&@+.............................................+&%%**#*##%*%#*#*#**#*#%&....",
+"..............+.....................................+..+..............+++++++..................................................................+..+++++.................................................................................................................................................................................................................+++.++++.................................+.+...........................+........................................................+....+.+.+...+..........",
+"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................",
+".................................................................................................................................................................................................................++++@+++.......................................................................................+...............................................................................................................................................................................................................",
+".....................................................................++@%%&%@++..................................................................+@&&&&@@+....................................................+&%*#$#$*##&+.........................................++&&&&&@++..............................+&%%%&@+...........................+@&&@+.................+@&%&%&@+..........................++&%%%&%&&+............................................................................................................",
+"..............+&&%&&@@.............................................@%#===>,>=*&@...............................................................+&*$=====*#%&@..............................................@&#*=,,3-3,3,,=*%@.....................................+&#*=,,3,=*#@+..........................+@#*===>=*%+......................+@#*=>=*##@+............@%#*=,3,=*#%@......................+@&*==,,3>==*%...........................................................................................................",
+"...........+%$$,----=$&+..........................................%=-;;~){)));->%@...........................................................+%=,;;~){))!;->$&...........................................@%=-;')){{{]{{{)';,$@...................................+#--;!)){)~;;,*&+......................+@*>-;;'~~~;>$&+..................+&$-;!)))!;-,*&+........+%$,;!~){))!;-=&...................+@#--;)){{{{)~!-#@.........................................................................................................",
+"...........%$--;!~'!-,*&+.........................................%>;~)]//^^])!->#@..........................................................%$-;!{F/^/F]);-=#..........................................+#=-!~{F//rrrr//F)~;=%...................................+#-!))F^/^F)!;-=%+.....................+*=;!){]FF])'-=%..................&$-;)]F//])!--=&+.......&*-;~{F^//F)!;-$%+................+&*--!)]^/r^^/F);-#&........................................................................................................",
+"..........+%*--;!''!-,$#@.........................................&=;~(2[5<Q:_;-*#&+.........................................................%*,-~|[Q[}}{);>*@.........................................+@*=>;!(F}[QPPP<<Q1~->&...................................+%-;)|}<<<[('-,$%+.....................@#=-!~1[<Q}(;=*#+................@%*>;(4<<Q}('-=*&+.......@*>;~}[5QQ}(!->*&+................+&#=-;(1Q<I5K<[_;=*&........................................................................................................",
+"..........@$=-!~(((_'-=#&.........................................%,;(}[8OO6<|~3>#&@........................................................+%=,c(4565Q[}(;-*%.........................................+%#$-;(}[QI8OOYYO67(;=@...................................+$-~1[58O652~;,=#&....................+&#>;~:[5Y65e;-$%@...............+@%$-!f58O6<:~;>$%@......+&$-c(<O8O5Q1(;=$%&+...............+@*-;~1<8OMYM651c,*&........................................................................................................",
+".........+&>9;abe4fbd0-*#+........................................&>cb7ijqqjgfa;,$%&+.......................................................@#=3cbijomih4ea3*#........................................+&*==;ae4iimjqqoqqpgb9>&...................................@$9afgmqZoigbc->*%+....................&*=cdfipqqjga3>#@...............+%#$-agjqqjifdc,*&@.......@*-0fgjqjmi4ba->#%@...............+&=39abgjqqWWqjf09*%........................................................................................................",
+".........&*,cdegmppi4b09=#@.....................+@@@+............+#30bflXnZnl4:ac,**&+...............................+.....................&*=,cabhmnXVVl4b03#+...................+++.................&%>9cd7hmnnonnnnnpiga3>#....................++@+...........&=9dekmnoXmkedc3>*%+...................#*9ab7lXoYmgdc=*@...............&%=,cd4mnZnl7:a9>$@......+%*30binnXXVk4ac,=#&@+..............@*,0ab7mnZWUWpgdc,#+......................+@++............................................................++...............",
+".........*-']rsHDuEDxz/);=*@.............+&%%%*%$=*$#&+...........*;{/zxHHHHCxs/{~;->$%%@+..............+@&###*****$**$*%&@..............@%$,-')/KxACBCCCxr]'-&..........+%#*#***%#$*#@+............@#=-!{/ztGvELvGHHBBAsQ{'-*+.........+&%###**#*$*$$*##%%&+....#-)FrsCCGCHAyr]);--*%@+..............+@=-~FrxBCHBHs^{;,&.............&#=-;~]QyAHHBHxzF~-=%......+$,!(/sBHBHCAs/)';,=#$#%%@..........&$3'(/KxADLELDx[{;-&............+%###*#***$=$$****%%%@........+&###****#*#*#*##*&+..................+%##*$*=$#*%&+.........",
+"........+#-!]rytDuEuAs/{;-=&...........&%#*=,--->----=#&@+.......+*;)/zyBBBCABsrF)';--,**%@+...........@%*==,----;-----,-=#%@..........@%#*,;;~]FryACCCCBAzF),&.......+&&#$>,-------->=#@..........@%*,;)]rxAGLELLHCAAysz/{;-#.......+&%##=>,-------------,*#@..+&-!FryACCBBBxs/{~!---=#&@+...........@#-;)FrtABCCAs/);-#+...........@%*,-!)FryCBCCAyzF);,%......+#-;)FzxBCCCByr]);;-->-,=*&.........@#-;)]rsAHLLLGyr]!-#+.........+%#$=,----;;----;----,*#&......+%#=>-,-,--------->=$%&@+..........@&%%$$----->--,,=#%&+......",
+".........&*-!1[5wNNw52|!-*#@.........++&%*$>==,-=,,==$$$%@+.......@=;'][rKKKIr^}{~;-->,=*$%&..........+&*#=>=>,--;-----===*%@+.......+&%%*$=-;;~{][^IKKKK/]!-$+.....+@&###$=,>---->,>=**#+.........@%$=-;)][IJwNwwPI^^[F1);=*&......+@&#$$>>,>--;;----->==>$*@...@*-!(FIKzKKr^F|)';-,=*$#%@+..........@%*-;~]2rKKKIF(;,*&............@#$>-;~(][IKzKQF]~-=*&.......&*>-_]}^IKKK^F_;--,,=>>**&+.........+%$-;(1QPwNwO[_!-*@.........+&**=>==>-----;------,>$*@......@%#$=,,,,=>=>>>=>,,>**%&+........+@%%*#==>,,---,,,=$*%%@+.....",
+"........+&*=3!b<6OY6<2('->%@.........+&%$>---;;;;;;;--=**%@.......&=-!_:QIIIQ[}:((~;;;->$*$#&+.......+&*$>>--;;;!!~~~;;;-,=*%@......+&%*$=--;;__(:}[QIIIQ[(!-#+.....@%#$*>---;;!!;;-;-,>*#@........@#=,-;_:[Q5OOO6<[}}1|(!-=*@.....+@%$**=,--;!!!~~!!;;;;--,$&...+#=;_|[QIIQQ[2:(_~!;--=$#%@..........&$>,;_|}[[IQ[:~-=*@...........@#*=-c!_(:}QIIQ[}(!->#@.......@#>,;(:}[[QQ[|~!;;;;;--,>*@..........&%=-;(}5MNR87_;=*&.........+*=,---;;;!'~!!!;!!!;;-,*#+.....@*=---;;3;-;;;;;;---=*$%&+.......+&#$$=,--;;'!;;;--,=$*%@.....",
+".........&%$39afgipig7ea-*%@........+&#*=9;aaa|bbbdda0c,>#&+......&*,;de7hllhh74febb_aa;3==$%@.....+&#$==3;aa_bbbbbbbbdda0->*@.....+&#$=,-;adbbeef7khhllh7bc-%.....@%$*>,3;aadbbbbdda0;,=$*@......+%$,cadb47llmXmmhh74eeba9=#@.....@%*$>,3caddbbbbbbbbbddacc3#...+&*30b4hllhh744eebbda;3=$*&+........+&*=30db47hk74ba->*&+.........+&$>3cadbee7khh47fbd9=*@.......@&$=ca1e4kkh4bbda~dad0a03=&..........+&*>9cbiqWWjgdc,*&........@%>9;aadaddbbbbebbbbbbda;,=&....+#,30a~d_ddaaddbddda09==*%@......@%#*=,-;aa_dbbbbb~cc,>$%&.....",
+".........@%*>90b7llVlh4bc3*@........@%>-cab1e444k774eeda3=*&......@*3cb4lVVVVlllkhkk74ed~093=%+....%*=,ca(be4444kkkkk7444bac,#+....%$,;'a(be47kkhhlllVVVVke_3*+...+%*,;'0_b4444k74k44:da03==%+....@*=cd1ekhVlXXnXXllhkk4ed;,*&....@%=99!ad1e4477kk7k7k7k74:d0$+...%>9_bklVlVllhkhkk74eba09,$@........&*>c0(e7kllll4edc,$%+.........@#>9a1e4[kkllhllkk4ba9=#+......@#*30b44kllVk4eee447ee:ba3$+..........&*,9afmqWWogbc9>%+.......&=9_be44747khhkkkk77474eda3$+...%=9_be47744e4474744e|dac3=*&....+%*,;'a_be4447kk74eedac3,#&....",
+".........@*=-;~/KxtAABsr{!,%.......&*,;)/rsxAAHBHBBAAAs/{!--%+...+#-'{rxBCCBHHHHHHHBBAAyzr/{!,&...@$;!{/rsxyBCCBBBBCHAHAByzF)-%...&>;~FrzsAtCBHHBHHHHGBCCtzF'>+...&,;)/rzsxABCBCCBBBtxsr/]~;>&....%-']rsxACGHHvvvGHCCBBts^{;,*...+#-;{/rzsAACBCBBCCHACHABtyr];+..+$;)FzxCCCHHGHHHHABBAsz^F)!>&......&$-!{/rytCCHHHts^{;-*&........@#-;{^zxtACACHHHHHAAz/);>&......%=-;{^stACBCByxsyAACAysz/);%..........#=;~]KtEEELyI{!-$@......@=;{/zytBBCBHBCHBCBCCBAAAsr]!#..+>!]rsxABABtAyABABAAAxzrF]~-$+..+%-;)FrzsytBBBBBACCAysz/]);>&...",
+".........+&*,-')]rzsyxs/);,#+......&=-~]zytBHGGLGLLLGLBs/)!-$&....&>!{^sACCHvvLLvGLLLLGCBts/);*...%-!)/sxBCHvHCCCBGvLLELuGs^{;*..+%-;]rytBCvGLLvLLLLLGHBCAzF),@..+$-!]rytBCvvHCBCBvDGCBAsr]~-*+..+*-!FzxCCHHLLLLLLGBCBCAxr{'-#+...*-~FzyABBvGCCCBHvLLvLLGHts/-@..+$;)FzyCCCGLLLGLGLLGHAyzrF);*......+=-'(/zxBCHvLLHxr{!-=@........+#-;]rxABCCCBGLvLvGHs/);>#......@*-;{rsyBCCCBBABCHLvHBAxr{;*.........+&>;~]IAvEuvyr]!-=&......@=']zyCHLLLLGLLGLvLLLLLGGBxr{>+.+='FzxCGLLGCBCCCCCCvHHAsr/]!>&..+*-!{rytBCGGHCBCCHGGCBtsr]'-#...",
+"...........+%*=-;'~{]F]);=#@.......@*>-~FQKKJwwNwNwNwwOQ|!->*&....@$=;)/IKKJwNwNNNwNNwwJKK/F!-%..+%=-~F^KKJwwwJJKJwwNwNNw8<|~-&...&=-)F^KKJwNwNwNwNNNwJJKIF'-*...+%=;)2^IKJwwwJsKJwwwJJz/}';,%....&=-~F^IKJwwNuNwwJJzKzr/|;-*&....%>;(/IKKwwwwJzJJwNwNNNwJK[)-+...%>;_]QKJJwwNNNNwNNwwPI}F(;,#.......%>-;)]/IKwONNOI|;-*%+.........@$-;{/rIzKJJwwNNwwOQ1;,*&.......&$,;)F/^IzJKrQ/PJwwJPr2{!=#..........@#>-':5wNwOQ|'-$#+......+#-)FQJwNwNNNNNNwNNwNwNwwPIF~$..+*;)2IJwNNwJsKsKsPwwwJK^}]~-=@..+%=;)FQrPJwwwJsKJwwwJKKr1);,%...",
+"............+@**>--'_(_;,*&+.......@#*-~1[QPJwwwNNNNNRO41';-*@....@%>-~:[QI6ONNNwNNwNNYPK<[1~-%...#=;_1[<IJYSYPIKKONNNNNRM7b;>%...#,9_1[QPPwNNNNNNNNSOJPP[|!-%+...#=;_1QIPJYSO8PKPOSOOII[:_;,#+...@*='(}QIJwNNNNNROKIQQ[}(;=#@...+#-;(}[IKJYwJPKP8RNNNNNw8Q}_=+...&>-'_}QIJONNNwNwNNNR6Q[1(!,%.......&#--~(}QI8SNNR<1;,*&..........@*=;(}2QQIKJwwwNNSO7_->*@......+@*=;_(:}Q<K<Q[[<ORR8<[}(;=@..........+%$,c_<OSR64_;=*@+......+#-'|[5ONNNNNNwNNNNNNNNwOPQ}~*+..&-!|[PONNSOKPIPIIPYMO<[}|~-*@...#,;_:QIPOwSOPIIPOSYJPIQ2_;=%...",
+"..............@#*>=3c_a9>$&........&*=9de4hinqZWZZZWZqogeac9>&....@#=3a:7lVmoWUWWqZWqqommlhfd3%...%,;dfhlmXqqqmlhmpqUTTTTjgb0>&...%,;bfkimpqWZqZoWWUWjXmk7bc-#...+#-cbfkmmooWqpmlmoqopmlh7d;,%....@*=;de7hpoWWUUUqpi<77e1a9=%@...@*-ab7himqqqqphmpqUUTUWqphf0=....@*30d4hlmnqWWWZWqWWqmh7f:a3#.......&*,30df7ipWTUqgbc9$%+.........@$=9d1447hlmoZqZWWjgd3>*&......+@#=3ab:4hiVihhiijqqp77bd9>&..........+&*,cbgqWqjgd;,*&........%>cb4mnqWUUUUUUWWUUUUUZjmgea>..+%-0b7pqWUWqnmmmlmmoqoi47eb0,@...%>9bfkimpoqpmhlmpqopmlh4dc,#...",
+"..............+@#*>999;3=%@........@$3'dekklXnooZooZZWqm7edc3#+...@*>c04hVXnnqZooppnXooonomhea>+.+$9aehmnonZopmhhhmnqTTTTqiea9#..+$9dfhXnonoononnooZooXVVhe_3*+...$9dflpnonooXXmlVXnoooonkfd9*+...@#>cabfklnooZWqqmhk4eeda3>*+...%,cb7innooooXmllmoZZZZZnmked>+...%=3a1kVlXnXZononnnooXVlh4:c$+......@*=9cd:7kjqUTUifa3=%+.........+&>30dbe4khlpnooZWqib0,*&+.....+&*>0_e7klnooXpnjqoolkeba3=&..........@$>9afiqUWqgb03=%.......+#3ab7lnZZZZZZZZZZZZZZZoXVVkb,+.+#,ab7loZZZonXmmllmnnnVVkked,#..+$cdeiXXonZoXlhklXnoonomifdc=+..",
+"...............+%#>---;-=*@........%,!]/zxtBCCCCCHCDLLvAysrF',&..+&>;)/sAHHvGvCHAyyAHBHGLDDtsF;+.+-{^sGDDLvGHBtxxsAAvLuuuuxKF;$..+-(rsGvLLvHHAtxtCCvCHHCBAzF'=+..+-]rxGvEEDGHHAAABCBHGvDDGxr]-+...%*,;)FrsxHHGGvGCAssr/F{~;-$+...$~FztHDvDvHHtysAAHHGGGHCAs^(-@..+*-~]zABCBCCBHBAtAHBHHBCCxzF-+......@=-;~{/zsGEuEuwz{'-=&.........+%=-'){F^rzsytBCvLvtI]'-*+.....&*-;{^sxBGvLLEvLDvHAys^]);>&..........@$;)FztEEEvxI{'-*.......&=;{FzxCHBGHGHHvCHCGvGGHHBCyr;@.+=!{/ztHHHGBHCCtxtAHHCHCBAs^)$..@-{rsHDEDGCHBxysAACCGvLDHyr];#..",
+".................@%#$**%#@+.......+#-;]^zsyBCCCCCCCCLLLHxszF)-%...#>;)/stCBCCCCtssssxACLLELGs^-@.+-(/sHLLLvCttzzzzsytGEuEGy/{!*+.@-]rxHLELvBBysssxACCCBCCtzF)-@..&-{IyGLLLLHCBBABCCCCLLEuGyr{-@...@*,;~{/rsACBCCCCyz/F])!;-$@...+$'FzAvLLLGCtyszsyACCGCCCxzF),+...%;)]zyCCCCCCBxssyxBBCCCCAz]-@......+%$-;){^sAvEDvtr]~-=&..........+%>-;!){F/rzsytGLLyz])-#@.....+$-!)^zyBHLLEEEELGAAsr/{!-$@..........&=-~}KAvEEvyr]'-#+......+=;~{rtABCCBCCCCBCCCCCBCCCCt^;@.+=;)]rtACBCCCBAysstABBCCCBy/)=+.@-]rxGLELvBAxszzsytCvEDLGyrF~*..",
+"...................+@.+............@=-'{F^/KKJJJsKJJwwRPQ2]_-=@...@$>;~/IKKKKrI/2]1F2[IOwww6}(-...$'([6wNwJQ}F{)_){:}QP65<2_;=@..+=!([6wNwJQ[2]1]2^rKKKKKQF)-*...@>'1<8NNNwwJJPJKJOJwwNNR8[|'=.....@%*-;!1F^IrKzKI/:);;,=*%&.....#-_:<JwwwJKQ/}]}/IKJJJKK/|!-#...+@*-']QKJJJKI^2:]:/^QrKKzI]~$........+%*>-;(:5ONwO5|!->*+...........@&##=>--'_(]2[POO<1!->%.......@=-;)F[IJwNNNNNw8Q}|_;;,*%............%>-~:5wNwO<('-=@........&=-'(2IJJsKKKKKKzKKzKKzIKrQ],+..%=-'(/IKsKKrI/F]}F^IrKKKr[);%..+=;1[6wNwPI2]]|{]2}Q8www6Q|~-%..",
+"...................................&$,;(2QIKPOOP6IKPYSM5[:_!-*@...@%=-~1[IIQQ[21((((|:48RSO5:'=+.+*9_15YNR841_~;;-;~_|1}}1_;,*&...*c(}POSY541(__(_:[[IIIQ[|!3*+..+=;(g8NSNNMYYRRRSSSSNNNR64(;*+.....@#*=;~1}QQQQQ[1_;-=*%@+......%-;(78SNY8<[}1|1}QIIKIIQ2(;,%....%$>;(1QIK<Q2:(_((1:}QIIQ[1;$+........+%*-9'b[OSSO<1;-*%@..............&&#*=,;'_125O8<|;,*&.......&$=;_(}[PMNSNNNR5[|_!-=*#@...........+&=-c|<ONR64(;,*%........&$-;_:QKPKQ[[QQIIIQQQ[QQQQ2)>..+&*-;(:QIIIQ[}1_(((:2[III[:~-%..+*0d}6YSY52((~~~~_1[5RSY84(;-&..",
+"..................................+&*-0b4hlmoWWqnmmpqWqigfba3$@...@#=-ab7hlhg4fbbaaddbgpqWqie0>..+$9depqWqpfbaac93990dbbbd0c,*@..+$cdfpqWqpfbbdaddb47hllh7ba,#...+=cbgjTTTTUUUUTTTTUUTTTTjgbc=......@%$>9a174hhkk4ba;,$#&+.......@=;dgpqWqnih4fffghmpmmlhfd;=#....&$,;d4hlli4febaadbe4hhlh7b0*.........@%*=3;bgjWWqgb0-=%@...............+@%#*,;0bfpqjgd9,*&......+@*=30bf7pqTTTTTqigba0-$#&@...........&*>90bgqUWjgdc>*&+.......@$=;0ehlmhhk7khhhhhhhh7hhhfd>...&*,;0e7hlhh4:bdadb:47hhlhea3%..+*cdgiqWqifbaacccadfiqWqpfbc,&..",
+"..................................+#=9~ehVXXZWWZonnoWUWph7:dc=&...%*30d4lVVXVk7e:bbb|fhpWWZpgd,+.+=0b7pWUWogeba09-3cadb:bbac3$&..+=abgjZWZpkebdbb:4klVXXlhe_9*+..@,0fgqTTTTUUTUTTTTTTTTTTqgb0=+.....@%*,cdekllVllked9,=$%@.......&>3a7poqZonpmmimmnZZnnXVkb03%....&=3aekVVlVk4e|bbbe4klVVlh4a>@........+#$39abioWUqpfdc,*&+...............@&$>,cab7pZqib09$&+.....@#$3'be7hnWTTTTUWp74b_93=%@..........+&*,9afiqWWoieac>*@.......&*,cd4hVlVlllllVVVVhllllllkb,+..@*3ab4lVVVhk4ebdb:fkkVVVl7b9*..+=0bhnWWWphbbda_ade4pZWWpiba3#..",
+"..................................%-;)^stHvvDvDvBCHvLEEGBxz^{;%...*-~]rsAHHGHCAyzrrrssHGEELGs/;+.@,]QsHvELGtsr/])!~){/rzrrF)!-%..@-FryGEEEGAszrrrsxtACHGHts/)=+..@-2IwuEEEEEELELEEEEEEEEuuJr:-@.....%=-;(^sACBCBCts^{'-->#@......#9~FrAHHHGDDuvDEDEDDGGGHyr{!>+..+*-']zxCCCCAtsz^/rzstACCCts/;#........@=-!(/zyEEEDtK^]~;=#+.............+&*=;!{/rsHDvtKF'3$@.....&*-;{^stAGLEEEEEEGAtz/{!;=%+........+#>-'{[stEEEvxK/{;-=&.....+%-;{/sACCCCCCBBCBCCCBCCBCCtr;@.+#-)FrxACCBBBxzr^rzxABCCBCyr(>..@-FIxvLELHysr^^/^rzxGLEEGyKF'$..",
+".................................+%-~]rxHvLLvGHCCCCHLELGCys/);*+.+#;)FrsACBCBCBxsrrzsxCvELuGs/;@.@-{rJGLELLAyz^])~))F/rzzrF{~;*..&-{IsHLuLvAyszzzsyBCCBHCAsF)-@..@;]rxuEEELvvBHCHCGGvLDvGts/(-@....+%>-!)/sAABCBCAs/])!-=*%&.....%,;{^sABCGvLLDuvELLvvHCBtr]'=+..+*;)FzyCCBCByzrr^/zsACBCBBs/~*.......@#=;~{/KtGDEEAs/]~;-=*+...........@&*=-;){/zJtGDyr]~-*&.....&=-'{rstCBGvLLvvvvGCxrF)'-$@.......@&$-;~]^JALEuvAzF{!;,*&.....*-;)^sBCBCCAACCCCCCACBCCACxz;@.+#;)FryBCCCCtysrr/zttBCCCBtr{=+.+-{ryGLEEvAxzr^^rzyAvLLLGxr]'*..",
+".................................+&=-~1QJwwwJI[^[/QIJOwPQ[F_!,&...&=;'{/QKKKKzK^FF]2^IPwwNwP}(>+.+$;([PMNNOIQ}|)';'!)1F2]]_~;=&..+>;1[8wNNOII/2FFFrKzKKKKQF_;*+..+>~:QJwNNwJKKIIIQIQIIIIKQ:);*......@*=-!)FIrKKKrIF|~;->$$&@.....+#=-'{F}QI8886O8JOOJJKKK^]!-%...+%=-~]/KKKrQ/F1){(F/QrKKKr[);%......+&**,;!(2<YNww52(~;-=*#@..........+@%#$=-;~(:Q8OO<|!-=&+.....@#=-;)F^IKJJJKKPJJwJI}(!-,*@.......&#*>-;'_2PwNNO<}_!-,=*@.....&=-;_/KKJKKrrIzKKzKzIrKzKK/F,+.+&,;)][rKJKK^2F(((]F^IKzKK^]'*..+=;1[8wNNOPQ/](||}[POwNwJQ|',&..",
+"..................................&$-;148wSO54}::1}[6OO<[}|(;-%...%=-'_:[IIPI<Q[:||:}Q<OYYY5:;=..+#;_:5OSYO<[}|_~~~__(1111(~;,%..+*c_}5YNR8<[:||(}2<IPIIQ[|_;*...+*9_f8RNSO5I[[[}[}[[QQQ[}('-*+....+&*>-;(:[QQQQQ[2(~;--=*#&......@%>,3!(|2[[<[[[[<<IIIIQ}~;=#+..+#=;~(2QIIQ[2|(__(_:2QIKI2:(-#......&#$>-;'~1<YSSO<:(~;->$#@.........@%$$$=>-;~(1[6M67|;-*@......&*$,;(1}[QIPPI<IPOY8<}(_;-=@.......@#>>-;_(:<MNS8[|(~;->$#+....%==;_}[IPP<Q[QQQQQQQQQQQQQ[_>+..&*-;(1QIPP<[2()_((}}[QIIQ2|;$+..$;_28YNYO<[}:1(1:[QORSM52(;$+..",
+"..................................&*9cbgpWWqpgf7ffggjqqphffbc,%...%=-0dfhmmpXmihffffghmoqWqib0$...%,abiqZqnmh7ffbbbbff7f7fbba-@...*9afiqqWjikffff7hmmnmmi4fdc>+..+$3afpqWqqmihg77g7ghhilhfbc,#.....&*=,;abf7hhlikh7bbdc09,*#+.....+&*=-30dbffff7777hhhlhhedc=&....#>;db7ilihgffb(dbef7hiVihfd9*....+@**,-0adbggqTTqigbbaa;3$&........+%**,,9;cadbfgmjjgd;,$&......@*=9abf7hhlmikhhmoWqigfbd09%......@%*-;cabffiqTUqifb_ac9>*@....&=-0dfhlmmlh4hhhhhhhhh4hhhhb,+..%=;abehmmmi47bbddbf7hilik7ba=...%3afiqqqjmg4feff7gipWqqpfd9$+..",
+"..................................&=3aegoWUWommimmmoqWWopmi7d;%...%,0b4hmnoZZooppmimmpooZqjif0$+.+#3aeiXoZoXmmiiiiiiiiimmhkeb9#..+%;aeinZoonmmmiimpoZooonmked,+..+%9afioZZooonpppmXmmXnnmhea9*+...@*=9_beklmjononpmih74eda3*&+.....@#$,c0de4k<khkhlllVVVl7d;,#...+$9de7mooonpmh7ee4glpnoonmhea$....@**3c(e4ggpqqTTUjmi44:ba9$@......@%=-0!d(be44kklnnngb0,*&......%=9ab7immXlVllllmoWUjmii7ed>+....@&=3_be7gijqWTTqjih74eda,#+...%,0d4hmoonmVlllVlnnnnVVVnpm49+.+#,ab4hmnonnmiheee7lmXooonh4b,@.+#3afmnZZonXmliiimXXoqZqifac*+..",
+"..................................&,']ryDuEEuvLDuvvuEEEEDDHxI(>+.+$!]zABDDEEEuLEuvuDEvDGGGAs[)=..+>;{/yBHHvLvLDDGLvGLDLDGGByr{=..@=~{rsCHHGLLuvvvDDEEEvEEvAs^'&..@>!{rxHHDvvEEDuDuDDDDDvDAsF'=+..+%-!]rxAHGLEuEuEDDDGHtAs^);$@.....&=-'{/rsxtCACCCCCCCCBBs^);$+..@-)^xtvuLuEuvGxssxtDvEEuEvty/;+...%,;)^stBHGuuuEuEuuDHAAxr{-#.....+$-']/rzsyxBBBACHHCs/);-#+....+$;'FsADLDGHCAtxtAEuEEEGGtxz~%...+#-;{rytBvuuuuEuEuEvABts/)-%..+=;FrtHvEELvHCCCCCGvuvGHHvDDJ(@.@>'/ztHDuEEvDGtssstvDuEuEEGtz~@.+>!{rxBHBGLvuvvvDvuDvHGHtr1~,@..",
+"..................................@>;{^sGvuEuDuLDuLLEEuLGuDyr{-..+>;FrxHGLLELuDDLDDLLLGHCByzF'$+.+*;)FzyACHvLLLLvuDLGuDuDvtxr{>+.+$;)FsxBCGGDLLuLuLLLLLuuvBxr;&..+*;)/sxACHvDvLDLvuLuDDDutzF'>@...*-~]zyBBHvLLEEuuuDGCCAyr);-%.....%=-~]/zsBBBCCBBCCCBCBxzF';*+..+>)rsAGuEuvuDGyssyAvDLvuuDHxr-+..+#>;)^xBBHvEuEEuEEDGHCAyz]!$+....&$;)FrsxAABCBCCCBCts/);>#+....@$;~}zwvGvHCBAtssxvEuuvuDGAz]$...@$-']zxBAGvEEuuEEuvvHCAyr)-*..+$~FzAAGLLLGCBCCCBGvvvHCHvvvx)&.+>)/sAHGDLuuLLAxsxAGvvuEuLHAr~&.@*;)/ztACHGLLLuLuDLvvCCxs/);$@..",
+"...................................%=-!|[5P666J68O666JO86P<4(;%...#,!1}<588O666686668PIQ[2]_;=&...&*-;):/[<56P8P6K8668P8P<[:~-%...&$-;)]}[I6686JO8O8O6O6P<Q:)=+...&=-;(F}[IP8666OJ6666PP<4|;-%....&=-!{F[IIP6O6OJOPPPIQ/F)-=&+.....&>,;~|/^rKzKzKzKKKr^/F);,=@...+*-(:Q<6O6J65<[::2Q566J665Q})>....@$>;)F[[IP6OOY6O66PII/F(;=%.....&*>;_F///rIzKKKKIQ[|!,$#@......&>-'1QP6PII^/F::4<668OPPI[];&....&$-!{F[Q<66OYY6OO8PIQ/]);#@..+%>!12<6686IIQ^QQQIPPPQQIPP<4;+..%-'][QP66J6PP[}:2[<PJ6J66<[1-+.+%$-;(F2[<P68686888PI[[F);-$@...",
+"...................................@*=-0(:}}[4[[[[[[4}[[2}1_;=#...%*-;(12}[[[[2[[[[[[}21(_;-=#+...@%$>-;'_1:2}}}[[[[}[}2:1(;-=@...+&*,-'_(|22[[4[[[[[[4[[:(';*+...@#*--;_(|}}[[[4[[[}}111_;,*&+...@#=-!(|:}42[<[[[[[}:1(~;,*&.....@#=-;_1}QQQII<KIIIQ}|_!-=*&@...+%-;_(}[[[[[}1(__~|[[[4[[:|_9%+...@&*=;~_|:e}477[7[[4:1|_!-$&....+&#>;(1[[QIIIIIIQ[:(!-=%@.......&*>-!b}2}21(__~__:2}}[4[:_!>@....@**-;_(|}}}}727[4[21|(~;=#+...@$-!(:2[[[2:11|1}}4}}2::}}}_,@..&=-!(:}[[[[4}:___|2[4[[[}:(;=...@#*,-;__|:}[}[[[[}e1(_;9-*#@...",
+"...................................+%*,300dbbbbfbfbbbbbfbba0,*&...&*,90abbbbbfbbebbbbbda9c-=*&....+&*=,,390dbbbbbbbfbbbdda;-=*+...+&*>=390abbbbfbfbbbbebbba9-#.....&#>,33cadbbbfbfbbbddaac3=#@....@%>-9addbbfbffffbfbbda;9=&+.....@#=3ade45llhllmmlh7edcc,$*@.....&=3cabbffbbbd000adbbfbbbbd0-&....@#$$3c0adbbb1fbfbebbda09=*@.....@*>cdf7hhhhllllk4ba;>$&+.......@%$-;abbbbdaaa9c0abbbfbbba;*.....+#$,30addbbbbfbebbbbdac3$%....@*,cadbbfbbb(b(dbbebbbbbbbba=..+@*,c0dbbbffbbd000abbbfbbbda9#....&#$,-3cadbbbbbbbbba0;3=>#&....",
+"....................................&#*,ca_dbbbb:bebfb:bbba9-=&...@$>3;adb1bb(bb|b:bbdac9,=$*@.....+&#*,-9c0bbebe:b1bbdacc3,=%+....+%**>-cadbbb:bbb:b|b:bdac3#+....@##$,,30ad:b:bbebbdaac3,$#@....@%*>c0ab1b:eeeef1bbaa09,*%......@*>c_b4kVVllXXXXml4ed09>*%+.....%*,cadbeef1ba'9c0ab|be:bda0>&....@&%>,9caab1bbb:b:bbdac;3=%+.....&*=0b4<lVVXVVVVh4|a9,*%........+%*=3ddbbd_a0c;99abbb::bda9*+.....&*>3;0adbb:b:bbebbaa0;,=&...+&*>-cab1be1dbddddbb:bdddb:dc$+..@*=30adb:bb:ba0330db:b1bd_0,%....@%*$=399abbfbbb1b003,=>*%+....",
+"....................................&$=3;_(]//[//[///[///F{~-$@...@$-;~(]2/2F]F}////2]_';3,*%+.....+&#$>-;!)]2//}^//2F:{_';->%......&#*--;)]/[////[//}///](';=@....+&#$33;~(]/}////2F1_~';-=#+....@#,-'))F2/^/rzr^^/F{)~!;-%+....+&=-)F^sxBCCCABCBAAsr]~;-*#@.....%-3!){F/^^/](~';~{F//}//F)!,&....+%#-;'~){F2^/[F[//F{)~!;-#+....+#-;)/sxAABCCBABxzF)!-*&........@%*-!{F/F])_!;;;'_]/^^^F]);*......@*>-!~)]F[/[/////F{)!';>%...+%=-;~(F^^^/F]]]]]F//F{{{]F:)=...@#>;~{F}//[FF{)'~)F}/[FF()~;#.....&#>--;_)]F[//[/]{';;-$#&+....",
+".....................................&$=-;!))))_){{{{{()))!;=%+...+&*,;!))))))){)()))~;--,#&..........@*>>;;~))){){){))!;;-=%+.......+&*$-;))))_({))){){)~!;-$@.....++%*>,;!)){({){))~;;--$&+......@#--;!))|)]]{F]]{)~;;-$%%+.....&*,;)]/zsysxysyssr/]);,*&+......+#,-!))(]{)~!;;;;!_{){)));;*+......@%=-;!~){){{{_{))~';->#@......%$-~]^zzsyyxyyzrF{~-=#+.........@*>-~)))~!;----;'~)(]){);,%.......@#>,;!~)){){({)))!;;,$%+....&#=-;~){{)))~'~!))())'!!)))-#+...@*-;!))){)_)!;;;!)_))))~;;$@......+&%$,-!))_){)~);-=#*%@......",
+"......................................+@@&#==-,->-=,>--===##@.......+@#*==,==>=>->>==>*%&&+.............@&#$$>>>>=>>>$**%&&+...........@@@&$=,,>=>,>=>====#&@+.........++@%#$=>=>$>$$$%%%&@+........+%***>>->--,-,->>=##&++........@#=>-;~)))))))));;,=*&+..........&#$=,,>>==>****==>=>==>*%+........+@@##==>==-,>>>===#&+........+&*=--'~))))))';-,=*&@...........+%*====>*$$*#**>=>,->$*%%+.........&#*==>>->-,-=>>=**%+......+@@#*==->->=>====>>>,===$**&......+&**=>>,,,==****>>->===##@...........@##=,-$=$**%@@..........",
+"........................................++@@@@&@&&%@%&&%@@@+..........++@&@&&&&&&%&&@@+....................+@&%&&&%&@++...................++&@%%%%%%&&&@&@@+..............+@@@&&&%&@+.+.............+++&&&%&&&&%%%&&%&@+.............@%%%**$====>=**%%&@.............+@&&&%&&@@@@@&&&%&@&@@+............+.+@@&&&@&%&@&@@@............@&&%%##$$***$#%&&@..............++@&%&@@@+@@@&&##%%%@++............+&&&#%%##*%#&&@&+..........+.@&%&*#%%%%%&&&&@&%%@@+++.......++@&%%&%@&@@@&&@&@&&&@@+..............@@@@%@+++.............",
+"..............................................+...+..........................+........................................................................+...........................+........................+++...++...................++..++++++++++......................+....................................+...+...................++++..+++.+++.............................+@@@@@++.................+..+@&@&&@@@.+..............+++@&&@@+@++.+...+....................................................+...................",
+".....................................................................................................................................................................................................................................................................................................................................................................................++............................+............................................................................................................",
+"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................",
+"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................",
+".................................................................................................................................................................................................................................................................................................................................................................................+@@++..........................................................................................................................................",
+".............................................................................................................................................................................................................................................................................................................................................................................+@&#*$==#&@.....................+%#***%@...................+@#***##@+..............................................................................",
+"............................................................................................................................................................................................................................................................................................................................................................................@%%*$3c99>=*&+.................+&#=3999=*&.................@%#=9c;,=#%@@............................................................................",
+"........................................................................................................................................+@@&%#&@+..........................................................................................................................................................................................................................&**>,30dda9>=*%+...............@%#,;addac,*%...............&*=>3cdd093=>*%+..........................................................................",
+"......................................................................................................................................+%*=>----=#%&@.....................................................................................................................................................................................................................+%>-!~)]Frr/)';,#&..............+#-;)]/rr/{!-=@.............@*-;')Frr/])~;->%@.........................................................................",
+".....................................................................................................................................@&*>-;;!'!--$*%&....................................................................................................................................................................................................................%>-!)]F/rzr/)!--*@.............+%$-!]rzssr]~;-#.............@*--!)/zsz/F])!--#@........................................................................",
+".....................................................................................................................................@%$,-;;'~'-,$%&@...................................................................................................................................................................................................................@#=,;~|F}}/}(;-=*&...............&*>-_2IPIQ|;-*%..............&*>,;)F2/}F|);,*#@........................................................................",
+"....................................................................................................................................+&$=-;!~(1(!-*$%@...................................................................................................................................................................................................................@#*,'(}[[[}:~;=#&+...............&*=9_48OO5:!,=%+.............+#*=-_|[[[[}1~->#&........................................................................",
+"..........++@++................................+@++@@@++@++..............++++...................................++++...............+@#>-ca(beeea;,$*#&++................++..............................................................................................+@@+++.....+@@++..................................................+@+++@@++++++++...............%$>;dfhhhhfea9>$&+...............@$>9dgqqqjea-=%+..............&$=3abfhkihed;=*%+.......................................................................",
+"......+@&#***######*##&&@+...............+&&%###********%##&+..........@&%*#&##%*#%#%%%&&@+................+@&&##*##%#&&&@+........+%*30d:44kkkedcc,=*#&@............+@%%#%@+.....+@&%%@+............+@%#%#%%%%&&%&&%##*%#%%@........@@&%%%#&&@++...+@&%%%%@.........+@%#%**#&&&&&%#%**#&+..........+@&%%%&&&@++..+&%%%%%%&+...........+@&#%************%@+............+&=3abhmonV7:d;=*&+..............+%*,0biqWWqgdc>*&.............@%#>3d:7innXifa9>*@.......................................................................",
+"...+%%=----;-;;;;;-;-;->$#%&@+........+%##=---;-;;-;-;---,,$#+.......@%*=,-;------;-;-;--$*@+............@%$=,--;-;;-;--->$%@......&$-~]rzytBBBsr/{)!;-=$%+.......+%*=>----$##%%##$----,=*%%@.....+&%$>-;>-;---->--,--;;--;->&.....@%$=,--;-->=*=###$-------*@.....&*$$--;----,->---;--;-$*@+....+%%$>------==#$#$$>-------$%+.......@&#>---;-;;;;;;-;--->*&+.........+%$;'FstEvvHs^]);>%..............+&=-'1KtEEEDxI{!-%.............&*=;!{^stDDDGs/)3,%.......................................................................",
+"...@#,-;!!~~~'))''~!!!!--,>*#@.......@#*=-;;!!!~'~~~~'~'!;;->%.....+%$>--;''!!!!!!!!!!!!;-,=%+.........@%*>,-;;''~!!!!!;;;--=&.....#,;)FzsyBBCAyz/F{)!;-,=#%+....@*--;;;;;;-->,>$---;;;;--,*#&....@#=-;;!~'!~!!;;;;;!!!!~!!;-=@...&$>--;!!!!;--->----;;!!!;;,#+...&$>-;;''~'';;;;;;'!~';;-==#+...@$,-;!!!!;;---=-,-;;!!!;;--=&......@#>--;'''~'~))~~~''!;--=#@.........%,-~FJAvuvHs/{';=&..............+*=-!{IAGELGyr]!-#+............+&=-')FJALLLGs/);=#.......................................................................",
+"..+%*=-;'!!~~~'~'~~'';;--=$$%@......+&*$>>-;;'!!'~!!~~~'!';-,#.....+%*,-;;;;;;;;;;!''';;--=*%+.........@#$>,-;!!!~!';';;;--=*&....+%*,;)F/QrKzK^/:{)~!;-=$*%@....@*=,-;;;;-->==>*>--;;;;-=>$%@....@#*,;;!'!!'';;--;;!!'!'!!;-$@...@*>,--!'';;-->==>>--;!!!;;-*@...&*=,-;!!!!;;---;!''!!!;-=$#@...&*=,-;';!;-->===>,-;!!!!;--=&......&#=,-;;!!~~~)_~'~!;;;-,>*@.........@*,-~[6www5}_;-=%+..............+&#>-'15wNwO<('-=&..............+&*,;!2IOwwO[_;>*@.......................................................................",
+"..+%=-;_(1111|({:1:1(((';--=*%+.....&$*,-;;~((1::111({:1{|(!;*@...+#*>-;~_)|{1)__)(||(((~;->#@.........&*=--!~(1{::|)((__~;->%+...@%>-c|}<<I5P5<[4}e:1_;-,*%@....&*-;0~||b~;-,>=--c_|)(~!;,*#@....&*>;!(b|{b1||_!!!_b{1b1{1_'-#..+&*,;;_(b||(~;;---;;~(1b1)~;-&..+&=-!~(b:1b1)~!!_)|b:1b|~;=#@..+%,;;~1b11(_;-->,-;~({11b(!;,#.....@%*>-!~((1:1e1e:11{1(~!;=*@.........&*,3ae5MSY5:~-=#&...............+@#*-;(<OMR64~-=*&...............+#*,'b5YSR8f_3=#@..................++@@@+++.............................................",
+"..@*3adeiippghgiijpii7ebda;9=*+....+&>3;aabe4gimppikgiijiigbd9&...@#=,adf7giiiffefgihig4edc3*&........@#>3cabe4iijpig7fffeba-*@...@#>9aeijqqqqqjjjjppigba9,*%+...%,cbegipied03-3cafgig7fba;=*@...+#,;dbgijppijgebdbeipppijigbc*..+#>cdegimiigebaa0adbegipiigbc*..+#=cbgimjpiigbbbfgpijmiiea9*@..+%3abgiipigebac9;0degiijigba;*....@%$=cabf7gijpjpjjjpig7fbd9>@........+#*-cafpqUqiea3=%@...............+&#$9cbijWWjgd9=*&................%=3cbiqUqjgdc-$@...............+@&#&%###&%&@&@@@++.....................................",
+"..&,cb4ioqWZomXXoWWonVlkeeba9=%....%=3cde47hVXoqWZnmVXqWWomhe0*..+@*>c(7klmoqomkhhmqZnmVh7bc3#+......&*=3cd:4hmoZWWoXVVllkkea,&...&*3abioWWUTTUUWWWWWqmgba9>%@...#cb4ioqqqmebda0dfiqopVhkedc=%+...*9dfkmqqWWWqoi777iqqWWWWomhb,+.+*cdehpqWqomh7eb{b4hhpqWWjmhb,+.+*3agjqqWWWoi7f7ijqWWUWjib9,#...$_ehmpWWonh7ed_db4hmoqWnphea>+...@*=-aeklVoZqWWWqWWWqnVhkea>#........@*,9abgjWUUpfa9>$@................@*=90fiZWWogbc3$%+..............+#=9afjqUUqibac>#+...........+&&#*==>>===>=*==>=**#&....................................",
+".+$!]zxHGDEEvCGDDEDvDBCAtsz/{;$...%,;{/rsxACCGDEEEvGGvELEDHts/-..+*-;]rtCCHvDvHCCCGDEGHBCyz]',&.....%$-!)/rstBHGvEDDHCBCCBtsF~$..+%-~FKtvEEEEEEEEEEEEEGyzF);-%..+>{zyADEEvGxzr/^^sAEDGHCAy/{;,@..+=)^yBGDEEEELDHxtAGDEEEEEvvAz'@.+-(rxBGDEEDCCAszzsyBCHvEEvHtz;@.@-)^JDEEEEEEHAxyBDLEEEEDwr:;=..+,FsAGvEEvvCAszrrstBHvEEDDAx/;@...%,;)/sACCGLEEEEEEEEDGHCtzF!$........#,;)/ztDEEvGKF~;=&................%,;~FItEEEvyr]~-$@..............@$3~:KwEEEvAs/{;-*@........+%#,-;;'))~)))~~'~~~!;;;-%+..................................",
+".+*!]zyCGLLLvGGHLvDLHCCCCAyzF~$+.+*-)]rsxBCCCCGGDvGvCvLLLvHAs],+.+%-']rtACHDLvGCCCvvvGCCBAzF);*....+%-!)F/zyBCHvLGLGHCCCBCBs/)$+..#-~]IyvLEEEuEELLLvLvAxrF);$@...>(ryCHDLDHysrr^rKtHvHCCBxr]!-#..@,)/sBHLELLLDHAByAHGLLLLLvHAr;@.+-)^yBGLLLLBCByssstCCGLLLvHAz;@.@=)^sGvvLLLvHAABCvLLLDvGtr{;$+.@-FstHLLLDHCAyzzzyACBLLLLGBsF;&...%-')/sBBCGGLLLLLLLLLvBBAsF;*+......+#-!{/syGLLEAsF~;,%................%,;)FKyLLELyr]~-=&..............@$;~FztLEuLts/]~-=#+.......%=-;!~)]F///FFF]{]]]])~;;,&..................................",
+"..&>;{}IJwwwwwwJJJJJPKJKJKI[|;*...%>;~2IKKPsPJPJJJJwwwwwwOP[]~$..+@*=;{F^QPwwwwwJJJJJPsKK^F~-=&....+&>-~{F[IKJwJJJJJKKKzKzI/)-%...@*-;([8JOwOOOOJ6PP85Q|~!-=%....$;{2IJwwwP[2{((1:QPJJKKr^{!-*@..+*;)}QPwwNwOJPI[/[IPOwwNNwK[1-...*;)}IJwNNwKIr/Q^^QIKJwNwwP[{=+.+*-_}<6JOwwwPI[QKOwwOJ65[(;,#..+$!:[PwwwwJKI/F:F[IKJwwwwPQF~=....@*-;(FIKKJJJJJJwwwwwJKQ/]'-&.......+&$-!|[5OwNw6}~->*@................&*=-~2<YwwO<(;-*&+..............&%>-_e5wNwwPQ:!->%@.......+&*=-!~)|}[[}}:]_))_]]1)!-$@..................................",
+"..&$-!|28OwwwOO6<[[QQQIPOO8<|;*...#>c([8Ow65QQQ[[IP6OYRwO6<1_,#...&#>-!(12<OSYSSYYP<QQQQ[}(;-*@.....&*-'|[QQPOOP5<QQIKIKIQQ:'-&...&#=-a|[<Q5PPI<<<[[21d'->>#&+...&,_|[8RRM5[1(_a_(:[QQKIQ2(;,*@..+%=;(15OSwO8<Q2}:}[<P6YSMO<:;*+.+#-~1[6YNS8I[[QQQQ[[QPYSSO<}~>...%>c~:2Q56YO6[}[<OOO6<[2(;-$&...%-'|[6OOOP<<[1||eQ5POOO6<1~-%+...+#=-_:QIII<I5I5IKYSR8[}|_3=%.......@#*,'|[5ORMO51'-*%@................+%$3c(<ONR84(;,*@...............@$>3c|<OSSO6Q1~-*#@.......@#$-;_1145665[[}2||:}[[2~;>%..................................",
+"..+#,cdfhmppppig7fbee7hpqWqigc>...%-abiqWWoi7fffef4mmpnmni4b0,#...@%*,;abfijWUWWqqmg44774ea3=#......&$3ae7hmnqZpikhhhhlhhk7ba,%...@#*,;df7kkhlhh4eebbba9,*$&@....#>cbfpqZqmg:bdaabb77hhlkfb;,*@...#,9abijqZomk7feef47inqWqjgbc*..+*,cbfpjWWji47hlmhhghpqWqpgfc$...%=9adefhponig4gijopi7fbac=*@..+#>cafgmmmjppgfbeginjXmihfdc,&.....%=-a17hhhhhhilmmoqqigbba-=&.......@**3ae7moWWqiec9*&+................@%*30biqqUjgd9-*&...............+%=30biqWWomhea->%@.......&*>9~bf7gpjopihg77f77kh7b0,&..................................",
+"..+#,cdfhVVXXVVkeeeee4hnWUWj7b,@.+*9dgpqUWqm441eef4hVVVXVV7ba3%...@#$>9ab4ioWWWqopi4ffeeeda9>&+....+&=3deklloZWonpmmVlVlVh7:c3&...@#$,0dekhlVVVkkeeb_a09,>#&+....&30b7mXoomk4bddbb4klVVVVk1a9*@..+@=;abhXoonmVlk4e4kVmnoZophb0%..+%90d7mXZonVlmmoonmllpoZZnhfd$+..&*,cabeglnoommmmoXXh7bdc3>#@...@=90:7klXnZom744ioZZmVh7ba9=%....+#=9ae7klllVmnjoooqqi7edc3*@.......&$>9dellZWWqpfac>$@................@*>30fiqUWqgb03>&+..............@#>9ceiqWWZXl4dc=*&......+&=3afhmmmnZoononpmmlVVVl4d9#..................................",
+"..+$;~FzACCCCCBxszrzzsADEEEvy^!@.@-(^svuEEDHsszzzsxACCCCCBs^{;*...@#=;'{/sADLvvHHAszr^//])~;-%....+%=-~FrxBHGvEEDvDvHCCCAts^{;*...@*,;)FzxBCCBCtxz//(~';-,$&@....#;)/sBGBCCtsz^/^rsxACCCAAz]~-%..+#-'{/sACBGCBBxssstCCHHHCtz2~*..@>!)/zAHGHHHBvDuEEGHBHHHGHyr],...+$-!)FrzxHHvDvDGGHBsz/{~;-=@...&$!{^sxtHGvDHtyttDDDHAts/(!,&....&=-!{/zxytBCHEEuDDHHxz/]~;>&.......@=-~FsAHGLEvAJF)3=@................&>;~:QAEEEvyI]'-=@.............+#=;~{rtDEEDGts/~->*+.....%-;{^xHuvvGGHGvDuEuDvHCAAs/)*..................................",
+"..@$;)FztCBCBCCBszszsyAGLELGx^;&.+-{/JAvELvHxsszzsyCBCCCCBs/);*+..+#=-!)/ztvDDCAxsz^F]{))!;-$%....+*=;'FrsACvLLEuLLvCABAAyz^{!*...&*=-'FryABCCBAsrF{)!;-,=*%+....%-(/ztCCBBysr]FF^zyACCCCxzF)-%...&$;~FryBCCCCBAssyABBCBAysr];*..+#;)FzyBBCHCHGEEELGCCCBCCtsr)=+..+*-;)]/zstCGLLEvHAxs^]);-$%+...+#>~FrzyAGLLvCACHGGHByzr]~-#+....&$-;)FrzsyABHLELLGCtsrF]~;$@.......@#-!{ryAvLEDHzF~;=&................%>;)FKALEuDxr]';*@..............%=;)FKALLLGAyz]'-$&+.....%-']IyGLLLvBCvLLEEELvHBCBs/'$..................................",
+"..+&=-):^KKJPJJIQ/}[/QKOwww82(-+..#;)25wwwOPQ/[[/QIJJJJKKr[);$&....+%$,-!1QJwJK^}]_~;-->>**#@+.....&#>-'{/^KJwNNNNwwPK^^//F(;-%...+&*>-~{F/rIzI/})!;->==$%&@+....@>;)2rKKKI/F(_')(]F^QKKz[F';=@...+&=-'(]/^IKKrQFF//IzKr^[F);=&..+&=-~]^KKJJKKJYSNNOPKKJsKIF(;%....@*=-;'{2QKJwNNwPI}F~;-->%+.....+@*-')|}I6OJOOJOJJ5[F(!;$&+.....+&*,;~{]FF[Q8wNNwJKQF1~;-*%.........+%=-~:QOwww6}_->#@................+#=-_:<ONwOQ_!->@...............@*>;~:5wNwOQ}(;-$&.......@=-!|<OwNwJPPwwwNNwNwPKI/F'-&..................................",
+"...%*-'1[IIP6YO6Q[[[Q<POSRY5e'=..+#-'b<OSROPIQQ}[[5OYOPIIQ1~->&.....@**-;d46Y8<}1(';>=*$*$%@.......@#*,;(|}[KOwwSwOOP<Q[[}|_;=&....@%*-;(:2[QQQ2(_;-,>>$*$%&@....+%-'([QIIQ2|(~!!(|}}QQIQ}|~,*+....@%=-!(|}[QI<}}:1[<I<[}(_9-#+...%*-c(}QIIII<6YNNSO6<P6PK2:'-#....@%$=-;(:}<8YSSOI[:(_;-*%@+.......@*>-;_2Q88RRRY8<}(;->*&+.......@$*-;_(|}4<6YSSO5[}|_'->>&+.........@*>-_e6RSO5:;-=#@................+&*-;b<OSR84(;=*@...............+%*,0|<YNR8e(;-$&+.......+#=3_f5YO6<<Q68YOOYO6<Q[:(3#+..................................",
+"...&=3017hlmjqqjikhhlmpoZqjibc=...%,;bgjqqomlhh4hijqqjmlhkea3=&.....@*=3cbgpqjifbd09,>*$##&@.......@#*,;abefgimVmXmpnjmih7ebc,&...+@%*-;dbe4khk4bd0;-,=>***%@....+%>cb7hhh77ebddadbe4khlh7ba3#.....+%*,;abb7ipmg4ffhppifbdc9=&....%*,;afhhllllpqWUUqqjpmmh7ea,%....@%$,;0de7ipWWWjmkfba;,>$&+.......+%$,;af7ijUTUqigea9,*%@........@**-cdbfgijjjjpmh7ebbdc9>*+.........@#=3afjqUqiec3**@................+%*,0bgqUWjgdc=$@................#*-0biqWqjfd3=#&+.......+&*,cbgggheeffgigihi77b1dc-#...................................",
+"..+#=9aehVVXoWZZnpnmXnXooXpgb;%+..&=3afinnnXVlVVlmnZZoXXVh4b0,%....+#>,;afhoZoikeed!c3>**%%+.......&*,9abe4kkklVVVXXZZZnVlk4d9#....@#*3ab4[hlVlkeb(a~c09c3>=*&...+*9_ekVVVlk7ee1b:4khVXVVk4dc=@....+@*=3adfhnZomhkhmoom7ba;=$%....&*,017lVXmXnnZWUUWWZoXXVhea3&...@#$,30_e4hVoWWWZmlk4dac,=#@.......@#=99bekmqUTUqmkba9=$&........+&$,9'b:7ioZonXVlh444ee{ac=#........+&*>3afjWUqpfac,*%@...............+*,30fiqUUqgbc3*&...............+#,9aeiWUWogb03=&.........&#=90bbfbbbbbffefff4ebdac,%+..................................",
+"..+$;)/zACCHHHGvvDvvGHCHtysQ{!*...@>c{^sxtGCHCCCCCHGGCCBCByr]'*...+#,;_]^stDLDAAssr/{);-,*$@.......#-')/zxtBCACBCHCGvLGvHHBy^{=....&=-!{^sxACHHAyszzrrr^F])!-$+..@='FztCCCCCAyssssytBGGGHAsr]-#.....&*-;{FrxHvvHHBHGDDtK/{'-$%....*-!{^xACBGvvLLLLLEELGHBCtzF;%...%>;'{/rxtCGLEELDGHByz/]);-*+......%=-'(/stCGEEEDys^{;-#@........@#-'{/zstCDLvHHCCCAxtyyzr{;*........@*,;)2JGEEEGK/{;,*@..............+%=;~]KtDEEvyQ);-%...............%-;_[VwEEEvJr]~-*+........@*,;_1///}//}/}//^//^F]);-%...................................",
+"..&*-)/sABCCCCCGvLvvGCAtsz/F)-*+..@*,!]/zsyACCBBCBCCBCCCCAsr]!$+..&=-~]/zyALLGCBtxsz/]~;-,*&.......#-!]rxBBCCCCCCCCCBHGGGLGtr(>+...+%-;(^zyAHGGCBtyyAAyxzr/]!>%..+>'FzyCCCBCBByyxABCCGvLGHyrF~#.....+#=-!{[zABGvvLvGHHAzF);-$@...+%,;{/sABCBGLLvCHGLELLCCAAz{;@..+*-!{/zsACGGGHHHHGGCAAsr]~-$%.....@#--']rsACHGvGtsrF);$#+........&=-~FzyACvGLvCCCCACBBBAtz/~=+.......&*,;)/KALvuts/)'-=%...............%=-;]KyvvLvy/);-*+.............+%>!{/sALELGxrF~-*.........+%$-!~)){))){)]){]]]))~',*&...................................",
+"..+#=-~FIKJJJKKKP6PPIQ/:{)!;-#&....@#>c'){F2^QrQIrKKKJJJzI}{!-&...&*=-)|}<POwwPK^Q/F|)'-,*#&+.....+&=-~FQzKsKJKKKIKKKPJwNNwP]!$....+@$>;){}IJwwJKIrQrKrI/[]_-=@..+*-!{/KzKzKzrQ^/rIKJwwwwJQ:)-&......+%*=-~:QIPwwwNwKI2(!-=%+.....+*=;~]/IKJwwwPIQIJwNOPI^/{;*+..+&=-']/QKJwwJQQIPJwwKI/2);,*@.....+#=--~{[IIKPKKQ}(;-$&+.........@*=;'FQKKwwwwKKrKrrzzKzr[{;*.........&*-;_}8Mww6}_;->*@...............@#$-;1<POJK[(;=*%...............&$,;)}5OwS8[_!-*&..........+@%#$=>>>>=->-,--,--==*#+....................................",
+"..+&,-~|[QIQQQ[[}21:|(_~9--**%......&#$>-;!((|11:}2QQIPPI[}_;>&...+$>;~:2[5OYO6Q[[22:(!;>$#&+......%*-;|[QIIPPIIQQQQQI6YNNY<:;*+....@%$-;(|[5OYP<QQQIIIQQ}:_-*@...&*-!1[QIII<Q[[[[QIKJwwY5[(!-%.......+#$-;_}Q5OSNYO5[1~-,*&+.....+@#=;_:}[5YM65}2[5YY6<[}(!-#....%=-_|}QI6OO5[2[<6OOP<[}_;>$&.....&*>-;_1}[III<Q}(!-=*@..........&#>-~|[<IORS65QQQQIQIIIQ}_;%.........@$$,a:5MSY5e_;-*#@...............@%$=3_2<I5[:'->#@..............@&*-9_:5OSR8ea-=#@.............@@&%%&&&@&%*#$$#%%@@+.....................................",
+"..+%,;ab4hhlhh7ffbbda0c99>$#&@......@%*$=3390addbee44ilmlke0c,%...@*,0bf7kpqWqmhk774ebac3=#&+.....+#*,0|45lllmlhhkhhhipqUUjibc*.....@&*,3adfiqoplkhkhhkk74e03*@...&$,0b4hhhVlih7hhhlmoWWqpgba-@.......+%*,9af4ijWZopl7b0->*&.......@#*30bf7ijqjgffgiqqphfeb;,*+...%=;dfghioqqigf7ioqomhh7bc,*&....&%=3adbe7hilllh4bc-**@..........@#=-ae7hmjqWomhhhh5hllkked9#.........@*$,0fiqWoiedc-*#&+..............@@#>3abkhh7bc3*%@.............+&#=3cdfiqWqpf0-*#@.................+..+.+@@&&&&+.+.......................................",
+"..+#,a14lVXlllh7e:bdac9,=**#&........+@#$$>-cadb:e4khVVVVl7ba3#...%,9d7kmmoZWqoVlhlmh7ed03=#@.....@&$-c:kVVVVXlXmnmnnnnjqqjgb9%.....+%*=3cbfinonmnmXXVVlk71a9=@...@*,9de4kVmXnmmmlmVXZWWWoke03#........@*>9ae7lnoZZnh41c,*#@.......&%>3a1f4inomh7egmoomk4ed03*@..+*3afkXmooZomk74loWZonmi7bc,%...+#>cdeghimpnXonXhea9>#&..........&*>9deklXnWZqnnmXmnXmVVl4d9*.........@#>3a7pWWWphbac>$#@..............+%$=3aekllkb0,>#@.............@#*>9db7mZWWjgd9>*@........................++++...........................................",
+"..#-)/zxAHHHCCCtssr^]);-,=#&@.........+&*=-;!{FrzsABCHHHHBAz/)=..+*;(/yvDDuLEvGGCHGGvAs/]~-=%.....&#-;)^xCCCCCHvvDvvDvvvGGxK2)$......%=-;~]/stGGDLGvHCCtxz^]!-@..+%>;~{FrstCHvLDDGGHHvLELDts/'*........&#-!{/zxHHHHAszF);-#+.......%$-'{/zsxAHAyzzzyHBAssr/);$@..+>)/sGDDvvvHAAxxHvvEvvvHy/(;*...&,~]KtDvDEEEDvDDAKF';=*+.........%>-~FstCCGLEvvLDvDDvHCCAs/!$.........@$-!{KAvDDGyz^{!-=%+.............+%=-;{rxtys/);>*&............+#=-!)/zxGDLLAK:)3=&.......................................................................",
+".+*;)^sAGvLLLvGCBssr]~--*&+............+&*>-!{/zxxCGGvLLLvBtr(>+..*;)^JGGvDvLGHCCAHvGxz/]~-$&+....+#=;)/sAACBCBGGDLvLDGAxsz^];#......+#,-!~FzstGvGvGCBtyzr/);*+...+$-;'{FrstCCvGLvHCBGvLGGAzF!#.........#>-;{/zxBBAyz/]~-$%+.......+%--)]^rsttyzr^rsxAxsr/])-$+..@='1zwGLDHCAAssstGvLLDDvy^);*+..#-'/swvLvuuuEuvutz]~->&+.........&=-']ztACHvLLLvLLGLGHBAxz];*+........@*-;)[sAHBAAyrF~;,#@..............@*>;~]rzz/{!-=#+............+#-;'_rsABAGAyr{;-#+.......................................................................",
+"..%-;|[PONNwwwwJK[}]~;$#@................@#$,;)F[IPwwNwNNwKQ]'*...@=-~1<56P6PIIQQQ<<<2(!--*&.......&#=-')]F/[[[Q565655<[1();-$@.......@%*=,-_(}Q<85IQ2F{)~!-=&.....@#$=-;)1/[I5P6I<QQI5P5[2(;=+.........@%$=-'(F2/2](!;>$&+.........+%*-;!){{]{)!'!({]1))~;-*&....%,!|[55KIQ[/]1:}<55655[:0-*&...@$-~2I8JOOOOOO8P<1'-=#+...........%*,;)]}^<I88888666I<[/]);,&..........+#$-!(:}2/^[])-,$#@...............@%*--;!!;-=*&@..............&*>-;|2Q[[}1(!-=*+........................................................................",
+"..&$;_}[POSSSYO5Q}:~;>*&+.................&*=3~(}[IOwSNSY852(;*+..+%*-c_(1|||1((((|(a'9->*&.........@%*,-;;~_(((|111|1(_;--,*@.........@&#$=--ca||1_(!;;-->=&+......+&*=>-;_((|111((((|||_;-,#+..........&%$==-;!'!c->=#+............+#**,--;---->--;---,==#&+....&$>9'_|1((~~';;0_1111(_9-*#+....&=9a14[[2<<[<[}|';=*@............+%*>-;;_(111|||1111(_';->*&...........+%#-9!_((|(~;-**%+................@%#>$>>=$*&@...............@%=>;'||1(_~;-$*@.........................................................................",
+"..&*3abfgimipi5g4b1a9=#@..................@#=3cdbfgiipipii4ba3%....&%$,,3c00000ccc;c9,>*%&+.........+%#*>,-39ccc;cc0;cc99>**%+..........@&#$>>,39;0cc,,>$*##@.........&#$=>39cc0c0c00;c;993>*&...........+@%$$=,-3,>=$#&..............+%#$*===*=$>>*,,=>$##&+.....@%*>,99cccc3-,,399ccc39=>*&.....&$,0abfbfffffbbd;3*%+.............+%*$>,99c0000c0c0cc9,,=*&+............&%$33c00a0c=>$&@..................@@#%%%%%@.................+&**,9'ac0c9,>#%+.........................................................................",
+"..&=30d:47hhhkk4ebd03$%@..................@#$,0ab|47khhhh7fba3#....@%**,,-99c9c939,,,=*#&+...........@%***==,,,3>--3>-,,,*$#@............@%%$*=,,,,,,,****&+...........+&#$,,3-399933,33->=**@............+&#$$>$>==**%+...............@@&##$**%*##*#%*#%%@@......@%#*=,3,,,,,>>=>,,,9,,>$*#&....+&*39dbeeeeeeeeba9>*&+..............@%*$=,3,-3-399c99,,=*$#&............+@#*$=>--3-,>*%&+...................@@@&&&+..................+@#$=,3-93,,=*%+..........................................................................",
+"..#,;)]^zssyyxsz^F])!-*#+.................@#=-~{F^zssysxssrF(;*....+&#$>3,9;;;;-,---->=#+............@%%#*>>--,333-3,---=*#&+............+&#*=>,>,3---=*##&+...........+&*=>--,--;;3;,-,-=$%#+............+@#**===$$#&&+.................+@&%%%%&&&@#%##%&++......@&##*>------>==-----3-,=##@.....%>;_F^rIrrKr^^/{~3$&...............@#%*$------->-;;---$###@............+&**=>------,$%+.....................+.++.....................@#=>--->--=#%&+..........................................................................",
+"..@*,!)]/^zzrz^/{{~;>*&...................+&*>;~){F^zrzz^^/{'-&.....++@#*$$*#*#$$$**%%@+...............+++&***=$$==$$*#%#&@+...............+&%%%$$*$##@+................++&%%***$****$**&@@+...............+@&%%#%&@.+..........................++.+..++...........+.+&&%*%*%%##%%%#%%*%##&+......+%>;)({{]]]{{()~->#+.................++@&#**$#$=$****%%+................+&&&%*#$***%+.................................................+&**#$$**&@+............................................................................",
+"...+@#*=---;;;-->$$%@++......................+&#=>--;--;---=$%+............+.+.+.+.+........................+.+..+.................................+.........................++...+...++.................................................................................+.++..+..++..++............+@#*$=$=$>=**#&+.......................++.+..+..+.+........................+.++........................................................+..+.+...............................................................................",
+".....+++++++.+@+.................................+..+@@+@+++....................................................................................................................................................................................................................................................................................................................................................................................................................................................................",
+"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................",
+"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................",
+"...+@&&&&&&&&&%&&&&&&&@+........................................................+...........................................................................................................................................................+&*%%%%*%%%@+.......................................................................................................................................................................................................................................................................",
+"..+&*%#**#*$$>*=*$=>=**&+....................................................&%#%###%&+........................@&&&&%&@+................................................@&%%%@+.....@&%%@+.................+&#####&@+....................@&#$>>---->-,=*#@+.................................................+@&&&&&&@+........................+@@@@+.......................................................................................................................+@%&%%&@+............................................",
+"...+&&&&&&%#*-,>=>,--==%@..................................................++@%#***%#%@......................+@#%%#%%%@@+.............................................+@#%%%#%%@@&@@&&%##&&@+..............@%**#%##&@+..................+&#*$>==,=,=,>=*$&&@..............................................+@@&%%##%%&@+.....................@@+@&%%&+.....................................................................................................................+@@%&%##&@++..........................................",
+"...@&&&&%#*=3;;;-;!;c;-,*@.................................................+&#**>>=>**&@...................++&#*$$$$$*#%&@...........................................+&#*$$$$$*##%###$#$*$*%@.............@%#$$=>>*#%+.................+@**>,----;----->=$*&@............................................+&&%####$$#%%%&....................@%%%#%%%&+...................................................................................................................@&%%##$$*$#%@@+...................+@&@@@@@+............",
+"..+&&&%#$,c0adbddaddaaa0;,#+..............................................+&%$>,3c93,>#&+................+@%**$=,,,->>*$%@+........................................+&&#$,-3,,,,=$*$>=>,,,=>$#+...........+&*=3-c33>*$@+...............@%#>=39aad~dddaaa;9,>$@................+++++++++@+...............+&%*$$>>,=,=>>**#@+................+@%$**===$$*&+...............................................................................................................++&#$*>==,====*#%@+...............@&#*$#$#**#&@+.........",
+"..@%%*>30_b|447744eb:b:ebd9$..............................................@%>,ca1e1bac,$%+..............+%*=3ccaaadada03=$&+.......................................@%$,9!adddaa0ccccaaadaac9=#...........&$=3~be:bac=*@..............&%$>3'be7giiiiig74eba3=#+............+&%%#####*#*%%@+............+%$=>39;aadaaac;9,*#&@.............+#*-900a00c3=**&............................................................................................................+@%*$-90caaaaa00c3=*#&.............&#**>=3=3,,==*#%@.......",
+".+#=>-)FrzyxBCCHABAxxxxxsz/~%...........................................+#=-!)FzyBByrF);>*@...........+&#,;'{F^rzzzzzr/{!;-*@..........@&%%%%%@+....+%#%%%@+......@#-;)Frzszszz/F]F/rzzzzr/])>@........+&#-'{rxBAxr]~-=@...........+&=-;']/sBGGDvNvGGABAsr);>&..........+&#=,,-,,,,-----$$%@........+%$>-!)]/^rrzzrr^/]{!;-=%+..........&*-;)]//rr//])!-=%+.........................................................................................................@#>-;')F/rrzzzrr/F{~;->%+.........@%>-;!)){)){))!;-=$&......",
+"..%==;)rzyAGGvLGLGvGvGGCtyz{>...........................................@#-;)FrsACCtzF)!->#&+.........@*>-')FrzsxAtxxsrF)'-=#.........&#=$>>>=*#%&%##$=>>==#&.....%>-;{rsBAtAxszr^/zsxytAyz/{;#.........%=;'FsABCBzF~;>#............#,-!)/sAHGGDDvDDvHCBAr]!-#.........@&*>-;;;;;--;;'!!;-=#+......+&*>;')]/rssxxyxszr^F]);-$@.........&*,;)]/rzzszrr/{!-$&+.......................................................................................................@*=-!'{FrzssyyyxzzrF])!;,%........+#>-!){F//^^/^F{~;->*&.....",
+"..%$=-~][IJwwwwNNwNwNwwwK[F~>@.........................................+&#=-;_2IJJJKQ|~;-=*&@.........@%*-;~2QIPIPJKPI[|!;-*%........&%#**=*$$**#**$***=$=$*#+....&*,-~}IJJJJPK[}]2[IPPPJKI}_-%.........%*,;)[KJJK})->*&...........+#=;!~:[PJJJJJJJJJJKKrF~-=%.......++&%*=,-;!;;->-;~~'-,*%+.....+&&$,;;~1}QIKPKPKPIQ}:)';,$@.........@#*-'(F2F/[/QQ[(!>*&+.......................................................................................................@*=-;~):[IPIKPPKI<[1(~;-=%.........%=,;)12Q<<<Q<[1!;-=*%.....",
+".+&=-;'|[POwNNNwNNNNNNSY6<1(;#.........................................@%*>3~(}5OwY6[1_;-,*#%@.......@#$>-!176OYwOYYOO5}(!-=#+......@@#>,-,--->=>*>=>>,---,=%@....@*,;(78OYRRM6<}:}<8YSMRR8[(;*.........@$>3([OOO84~-=#@...........+*,;~(:<6O85<<<<<IIIQ[1~-=&........@&$$>-;~()!--;_{:(!,$%+.....@&#*,;(12<8OYOMwYYO8<}1(~3=%........+&*>-'|}[[}2Q565}'-$#@......................................................................................................@&$>;__1[58OYOYOMOO6[:(_;-#+........&$-~:[<8OOOYO5}('->*@+....",
+"..&*,;abgpqWUUWUWUUTUTUWoifbc*.......................................+&#$=-cdbgmqUWoifba;3>*&+.......@#=,;afiqWWWUWWWqjgba9=*+.....+%**3900cc;c3939399;c0cc3*@....&*3cbgqTTTTTqigffiqTTTTUqgb3#........+&*,;bgqWqjgd9=%@..........+%=-;dbgpqojlhhhhhkhll7ec9*%........@&*=-;adbba!c;deee0-*%@.....@#$=;abf7iqWWWWUWWWqp74bdc>%.......+@%*-cde47777ipqjga9=#@...........................................+++....+++++..............................................+&#*30defkjqWWUWWWWqji7ebc-%........+#*9afimqWWWWWqgba;>*&.....",
+".+&-,0d4moWUTTTUUUUTTUTUqnhfd,@...............+@&%&@+................@#=,c~be7ijUUUomh4|d0c>$&.......@*>3abfpZWUUWUUUWjhed;3*&.....@#*-;d(bb:bbd(ddd(dbb1ddc3#....&$9cepqUTTTTUjhgijWTTTTTqib9%........+#=3abiqUUoib03>&..........%#>cdbehpWWnmlllllVVVVkea-=&........@%$,c0dbee(0cab4ked3$#@....+&*3ca:7klnZWUUUWUUWWnVkked9*.......+%$,9_ekllQllVoZqib9,*%................+@+++++&&@@@@@@+........+@&%%&#%###**###%&&&+........................................@*=9cd4khmnWWUUUUUUWZXhk410,@.......&#,c|7lnZWUWUWqmed03=&.....",
+".+%-!{^sGDEEEEEEEEEEEEEEEvts^'&.............&&**>-=$$#&@...........+&*-!{FrzyBHDEEEEHAAsz/]'-%......@#-'{FzyGLELLLLEELGsrF);-%.....&>-~]rsssxysxsxsysyyxssrF~;&...%-'1IwDEEEEEuvAttvuEEEEEEAI)=........&$-)FKAEEEDyr]~-$+........@=-!{^zxAvEEDCBCCCCBCCCAz]~;*........&*-;)]/rzr/{)]^sxzF~-=%....&>-~]rsBHHGvLLLEELLLLDHHAtrF-&.....&*=-~{^stACACBHGLvyI{'-=@............&#*$>=$*$=>,,,>==**&.....+@#=,---;;;;;;;;;;;--,>*%&+....................................%,;)FsyCCGLLEELELELLvGHAAs/($.......#-;)^yAGLLLLLLGtz}{!-=@....",
+"..%,;{/sHLEEEEEEEEEEEEEEELBsF;&............@#>=---;--=*%@..........@#-;)FrsACGLLEEELELGtsrF);*+.....@>;)FrKxCLLGCCCAHAys/{';$&....+#-')/stBBHHGGGGGGGHCCBAs/);*...%>;)^sAGGvvLvHByAHLEEvvGts/)*.......+&=-']KtGLuLxr]!-$&........@$;~FrsyCvLLLCCCABABAAAxzF~-*+.......@%-;))]F^/])))Frr/]!-=&....#,!{/stGGGLCvGLLLLLGHGvGGts/~#.....&*-;){/sACCCCCBvLDAr]~-=%..........+%*=--;-;-;--;;;;---=#&...+%==-;;'!!))))))))~~~!'--=*@...................................+%-!{/sGvGLLLLEELLLGGGGLGHtz{,+.....+#>-)/zyBBCBHCHAsr]);,*&....",
+"..+#>;_48wNNNuNuNuNuTuNSNw52)=+............@&$>=,---,$$%@+.........@%$-!{/[IJwwNwwNwNwwI[|);,%.....+@*>;)12QPwwPQ[2}}}|(!;=*%+....+&$-!{/rKKwwwwNwwwwwJJzI^]!-&...+#=-~|}QIPOwOPQ[QPOwOKIQ}|!-@........@#>-':<OwwO<(',$%+........+#,;)FQQKOwwOPKrr^/^//FF)!-*&........++#=,--;'!;---;~)!;,$%@....&*-!([PwwwwJJwwwNwwsJwwwwP[(;%.....@#>-;)]/IKzKKKJJwO<1~-=#@.........+&%**,,----;----;--,,>*@...@%$=,-;!!!~~)_)~~)~!;;;--*$@.................+.++..............+%=-~:IJwwwNuwNuNwwJJJwwwOK})=.......+%*-'(]}22[}}22(~;,>$&+....",
+"...&>c_}5RwNNNNNNTuNTNNNSO<:~=+............@#*=---;;-,*%&+........+&%=-!(}[<POYMOOOOYR8<1(~;=%+.....&*,;(12QIO6<}|(((a~;-=$#@.....@#*-')[QIP6OOwOwOwOOOPII[|!-%....@#=-~|:}<6OO<}}[5Ow6<}:1~,#+........@%*=c(<YSS84(->*@.........+%-9_e[<P6OYOP5K<[222}:(~-=#@..........+%**=>,,=**=---,=*%@+....@=-!(}<OwwOJJJwSwOPPPPOOO64|;#.....+&$-;~(}[QIIIIPOY6<1;->%@.........@%$>--;'!!!!!'!_~~;;--*@...+*=-;_(1b:1::b:1:111{|1_c-*&..............++@@%&&%&&@@@+........%=;_|<6OwwwwNwOwwOJJJOOw8<:~>+.......+#=-;___((___~;-=$#@+.....",
+"...@=9afpqWUUTTUTTTTTTTTUjibc$.............&$>3caa_ac9>#&@........+%$=;de4hhlmmiigiimmiged0;=@.....@%$,;dbf7imi7fbaa0;93,=#&+.....@#*3ab7hllVnmmmmmmnmmllk7ec3&....@%$3;dbfgpqqmh4gmjqpgfbdc,*.........@%$,9bgqWWjgd;=$&.........+#-0fipjojppjjojpih7e:bba3=%@...........@%**$$$$***>>>$#&@.....+&=9abgkmmXoqqppppmlmllmmmkge0*.....@%>c0bb4hhk77khpqjib09>$&........+%*=3cdbbeeebbdbeefbba0-%...&*-cbgiijijijpijpijpiiigbc,#........+@@&%&%###**#**#%%#%+.......#,0bfhimmnqWqopmmmooommmikfb>.........&*=-,cc;cc993,>**&++.....",
+"...&=9d4oZUUTUTUTTTTTTTUWZif0#............&%=-ab1eee1a3$#@........@*=9abklVlVVlh744khVl74:d0,#.....+*=3a|4kklVlh4:ba!99,>$#@......@*>9aekVlVXVXVllVVVXlXVVhed3#....+%*>ca|ehnZZpllVXZZnhebac,#+........@#*-0biqUWqgb03=%.........&,0bgpZWZoXXoWWWZnllhk4|dc,$@............+@%%#%%%%##**#&+.......%,017lVmXXoWWZnVVVVlXXVVVlh4d=....+%>-~be4klVVlhhVoZqi7dc3*&.......&#*3;a:4khkhkk4ekklll7eb0*..+%,0biqqWWWWWWWWWWWWWUWWjgbc$@......@%**===,,,,333,,,,>==*%+....+#;_eklVVVnZWWoXVmnoWonmVVlhe9&........+%*=,=,,,>-,,***&+.......",
+"...#-_/JGEEEEuEEEuEuEEEEELw/!#...........@#,;)Fzzsssr/'-*#+......+%=;)FztBCHCBBtsssxACBxsr/);*.....&>;)/zxACCCCBxsr/{);;-,*&......%>;)/sACCCCCCCCCCBCCCBCBBs^)=....+%*-!{/zxvDDGHCHvDEvxz^]~;$@........&=3c(kAEEEGxQ);-=&.......+*!FztDLELDHGvEEEELHCAtyz/{~-%..............+@@@&%&&%&%&@........*!]zxCBCCHDvEGHHCCCCCCCCCCCxr;....@$-~FrsxCBCCACBHGDDAs^{~-$@....+%>;;)]rxACCBCBAyyABCCCAxr{-..+$!{IxEEEEEEuEEEEEEEELEEDyKF!#....+&*,-;!))))(){]]{)))~';->*+...&,)/sABCCCGLELGHBCHDEEGHCCBAs{#.........&#*$>,----==**&@........",
+"..+%-_/sHLLDEELEEuuEuEEEEvy^;%...........+#,;)/zssxszF);>*@.......&=;)FzACCCACByszzytBtsz^]);*+...@#-;)/sACCCCCAAssr/{~;-=*@.....+%-;)/sACCBCCBCBABCCCCCCCAs^)$+...+&>-;)/zsHLvvCHCvGvHyz/]~-*+.......+&$-;{rJvLuvy/{;-$@........$'FKAGLLLvCHvLEELGCBCBAxr]);*+...................++.++.........+='/zACCCCCGLLvBCACCCCCCCBCAA^-+...@*-)FrstABCCCACCGLLts/]~;=@....%=-;~]/zyCCCCCCAAABCACAAyr{=+.+$;{kyHGvLDvLDDDLDLvLLLLDAzF)$....@#-;'~{]F//^^^/^/^//F])~;-#...&!{ryBCCCCHGLLGCCCGvvvCBCCCAs{$...........+@@%***%##%&@.........",
+"...&=;_[6wNNNuSNuuSuSuNNNw<1;&............&*-;~]FFFF|)-,*&........+#=-):rKKKKI^/2F:F}//]_~;=*%....@#>-':QKJJJwJJKII[1);,$%@.......@*--~FIKzKKIrI^rQrIKKKKKr});%.....+%*,;!1[<JJwwwOwOJ<[1)'-=&.........+%*,;([5P6<[_;,$&.........%>!1QOwNwwJPJJOOJJPKJKPI[_;,%...................................*;)F/KKzKJwwwOKQ/QrIKIIrIKr^|>+....&*-!(]/QzKzrKIKJOJ<2{';=&.....%=-;!):[IKJKIrIrrrKzKzK/F_;*...%=;_}<5P6P6J6666JOJwwNwO<})-#....&%=;')|}}<QQ<Q<Q<[<[[}1!-*&...+=']^IrIKKPwwwJJKKJJJJPIQrKr};@.................+...............",
+"...&$-0}6wNSNNNNSNTNNSNNSO<19*............@#=-;_)(_()'-=#@.........&=-!|[IIIQ[21((((1((~'-==*@....@%=-~15OOSSYSYYY652(!-*&@.......&$=-~1[IIQQ[}}2:}2[[IIIKQ:'-&.....+@**-;_|[I6YMMYO65[:(;-=$&.........+&$>-~1}[[2(!-=%@.........@>9_48SwYOPII<<I5IIPOYO8<1;,#..................................+&,_:[QI<IIPYR8[}::}[[QQQQQQ[(=....+&$,;_(|}QQIIIII5K<[2(~;>%+...+&*-;(1}Q5OP<[[[QQIIIII[}('-%...@#>3_12}[[[[[[[[[Q<6YwS6<1!,%...+&$>!(|2<66OOOOOY8Y6YY6<(3=%...+#;(}QQQQQ5OSYOPIIIIPIIQQQQ[}-+.................................",
+"...&=9afpqWUUUTTTTTTTTUUWqgb0*............&*=3cadbbbda-=#&+.......+%=3ab7klhk4fe1bbdbbdac9>$#&....@#$9agpqWUUTUWWqqpgba-$#@.......@$>-a:7hlhh74feeee7khhlhhe09%......+%*,3abfhpqTTTqmh4bdc3=%@..........&%>90bffffdc,>#@.........@*30fiqWqjmkhkhhhhlpqWWqiea3#...................................%>0b7hllmmooqpif4e4khkkhkhk4b>....+&*,;a(efhlllmlVlmh7eba;=%+...+#,;de7gpjqoi44775ilhlh7ba;=%...@%$3cadbbbbbebfff7gpqWWjgba3#...+#$-abf7ijqqqqqqWqWqqqjif0,%....*9de4hkhkijWWqpmmlllkhhhkhkf9+.................................",
+"..+%,0d7jWUUUUTTTTTTTTUUWqmfc*............@*30de47744(0,$%+.......+*=9_}kVVVll<7444444e:dc9,*&....@*,3afpoqqUUUWZZom4eac,*%+......@*,ca4llVlVVhkk7kkllVVVlh4d3%.......&=3cdeklnqTTTqXVk4bac>$&..........@$>9dekhk7eac=*&.........@*,cbgpnnnXmnmpmmmXnWUUqp7b9*+..................................%301klmnnnZZWZpmilimmVVllll7b=+...@%$3adb4hXnXnXonXnXh4:ba9$@...%=3dfimmoooomlllmXnnVllk:d9,&...@#*,30aadddddbbbe4hmZWWqmfd3%...@%>9aeehlXoZoZZoZoZoZojibc>%...+*0bQkhhlVXqUUonnXXVVVVllllkec&.................................",
+"..&>;(^JvEEEEEEEuEEuEEEELDyI)*...........@#-~FzyBCCAxz]~->#+......&=;~FzACBCCCCBAytABAAsrF{!-*....&$;_2zyHHGDEEEGBCAsrF);-=%+....+%>;)/sACCCCCCABBCABCCCCCAs^)$......@$;)]^stBGLEEEEvCAsrF)!-%.........+#-;)^stCAAzF);,*@........&=9~:^zxAHCHvDDGGGHDEEEDts^{,+.................................+*;{/sAGDvDEEEELvLEGvvvHCCCAsF-+...&*,!{^zxBDLEDvLEDDvAsz^]!>@..+*;{^xGvDvvGCCCCBHDDDCAysrF~;*...@#$,3!~_))_{]]]F^zsGDEEvAK^)=...&$-!]/zsxAACCCBCBBCHBByK2~9$...+=)/sACCBHGDEEDHHCBCCHCCCBBtz~%.................................",
+"..&-!{/svLEEEuuuuuEuEEELELy^'*...........&>;)/sACCCCBs/);,#@......@=-'FzyACCCCACBBBBACAyz/{'-#+...+=-~]rstCCGLLvCCBxzrF{!--*&.....#-')/zACCBCBCCCCACCCCBCCAs/)=+.....&=;)FrsACvLEEELCCtsrF);-%.........&$-')/sBCCtz/);-*+........@*-;~]^zsxtABHGGGGLLGGGGAzF)=+..................................*;{^sAGLDEELLEEuLEuLLvBCCAAs/-+...&*,;)/ztAvLLLELLELvHxzr]!>@...*-{QyDLLLGCBCCBCHLLGCAxzr])-*+...@#=>--;;;;!'))]F/sALLLDAz/)>+..+#>!{/rzssyyxAAAAAtxysz^]'-%+..+=~/zyBCCBHLLvGCBCCCHCBCCCByr'&.................................",
+"..@*,;([6NNSuuuuNuNuSNuwNO<|-&..........+%*-;1[KJJJJPQ|!->&+.......&$,!{^rKzKzKzQ/rrIKr/]_!-=&.....&=-'(F[IKJwwJIIQ[]|';-==*#+....+*,;)2rKKKKzKzrKrKzKsKKKr})-%......@*=;!(F[KPwNNNwPK^/('-,*&.........@&*-;_/IKzI}~->*&.........+%=,-;~(]F22[QQIPwwwwJKI[(!-#...................................%$;)}QJwNwNwNNNNNNNNNwJKzK/F!*....+&*=-~(25wwNNNNwNwOK}:)!-%....&=;(<ONwwJKKQrKKJwwJI^/1{!-=&.....+@&%***>$$=>>-'_}<OwwO<1_,%....@#=-'~({]}F}F/2//F:F]{~;,=&...+#-']/QzKJJwwwJKKKKKJJJJsKI/(-+.................................",
+"..@#=-_}6NNNSTNNNSNNNNNNSO<_-#...........@=,!1<OYYRR6<b'-*%+.......@%*-':}[QIIIQ<<<QQQ[1('-=*&.....%*,c_(}[<8RR8<2211~';-->$%@....+&=-!1}QKIPPI<Q<I5P5PIII[1~-&......&#=-!(|}Q5MSNNO5Q}|('-=$@.........+%$=3_1Q5KQ1~-=#@..........@$>-9'(11:::1[[<ORR6I<[1_-=%+..................................@*-'(}<OwwNwwwNSwSSwOOPII[2(;*.....@%>>;_1[6OwwwwwMO6[1_!;=%....&=9~[8ORY8PIQQQ<6YY8<[}1~;-*&......+.++@&&&%%#$==9_26OO52(;-%.....@*>--;'______(_((_~!!;-=#+....#=;_|[QKPOwSwP<QQIIPPPP6I[1~=..................................",
+"..@#>9agpqTTTTTTTTUTTTTUWjgb;%..........+&*-aepqWUTTqiec9=@........@#$,9d:f4hhmpjjjjmk7eda9>*&.....%*,;dbf7ioqqpgeebbba~0;3,*&+....&*,cb7khlmpojjjoojopmhk7b0,&......@$=-cdbf7ijUTUji7ebdc-=*@.........+&$-cdgmjjiea9=#@..........@*=;abbfeeeeef7gjqqpik4b03*@...................................+%=9abgmjnpnoXpnnooqomlhh7:a3%.....@&$=3cdfgiiiVimiihfda9,*&....&*-0fgmppjjpih7hippm5k74bc9=&............+@@&&%$=,0bfgggba9=&....+@%*=>--;99cc9;cc;cc9-,$#@.....&*-0de7hmXoWqplhhippnmmlkfb0*..................................",
+"..&*,abgjUTTTTTUUUUTTTTUUqmfa$..........@*,0a7jZUTTTUjfa9>#+.......@&*>9ad:ekVXqUUUqoml4bc9,*&....+%=3db[klmoWZph44eee:b|bd03=+....&*9aeklVXoZZWZWWWUWZXVlkea,&......@*>cabekhpqUTTqmh[ebac>$@.........@#=3abioZWoib03=%..........&=caekkkk444e47loWZnhkf(c,>&...................................+%=90b7lXXXVXXXXmXnnXXXVV7e03&.....+%*,90abe44hhhhh7fbac9,*&....@=,9d44lXoZoXlhlVVXXVVVhedc=&...............+@%%$,9abfebd;,*+.....+&%#$*==>,-,-,,,3,,==$*@......&*=3dekVVnZZZnmlVXZZnXXVh4d9*..................................",
+".+*-'FztvEEuEEELEuEEEEEELvtK]-+.........&,;{QsDEEEEuuuz});>@.......@%=-;)]/zyBHDEEEEDCxz/)!->%....@>;)FztCHHvEEvAAxyysxysyz^{;%...@*,']zxBBCGLLELLEELDGHCAyr{;%.....+#-;)FrsxBGDuuEEGAtsrF{;-%.........@*-']rtLEvvyr]~-=@........+$;{rsBCCCAxxsxxHvDDGByz/);,#...................................@#-;)FztBCCCCHCBHCGHCBCBBx^{-%.....+#*-;~)F^zzsssszr^:(';-*&....@$9~FrsAHGDvGBBBCCCBCCCBsr{!$................+&#=-'(F//F(!-*+......@#*>=,-----,-,>---,$#&+......#,;~FzxBCCDDvDHHBGvDvGCCAz/~>..................................",
+"..*-']IyBLLELLLLELELLELLvHyzF-+.........#,!{[sHLEEEEEtzF~;,#........+@*,;)]^zyBGuEELHtsr]~;-$+....#-!]^sBvLLEEELvHGHGGHGvHAs/'$...@*,;{/sxtABCCCBCAAHHABtys^),%.....&#-!)/rxBGLEEEELLGBsz/]'-*.........&=-~]IyGLLvtr]~-=&........&>;]ryBAACBByxyAAvLvBtsrF);=&...................................+#=;']^sABCCCCCLvvCBCCByxzF),+.....+@*=-;!){F/F////F{)';-$%@....@$-'(/rsxBHCCCBCCACCCCCCyzF)=+.................@#>;')())!;=%.......++&&%####%*#%%#%**%%+........&$-!)/zxyBCCGGGvvvvGCBAysr{;#..................................",
+"..&=-~1}IPwwNNNNNNNNuwwwP<}(;$..........@*=;_[6wNNSNN8}_;=*@.........+@#=,-!)1QPwNwO<2{);,*%@.....@=-!|<OwNNNNNNwNwwwNwNwwO<|;#....&*=-~{]F22}}}:::11:F:F]{'-$+.....@&$-!(]Q6wwNuSNNNwPQ1(~;=&.........+%=-':<ONNO<|'-*%+........@*-!]^rKzKKKKIIPJOwOK[F(!-=*@...................................+@&*=-~)F}/IKJwwwwJJrQ/]{~;-#........+@&$*=----;-;-,-==*&@+......&*>-;~1}[IKKKKzI^QrKKKK/}_;*...................+&*=,-==#%@................+...+....+...........+@*=-'){F[QKPJwwwwsKI[F{)!-*+..................................",
+".+%*,;(1}Q<OOwwwwwwwOOO5Q2(!-%..........&#*-0:5SNNNNY51!->#@...........+&#=,;_1<YNR8[1'-=$%@......@#=9_78NNNNNNNNNNNNNNNNSO<(,%.....&*>-''___~~';;;;;;'__';;=&......@%*,;_|[8MNNNNNNwY52(~;-*@..........@*-'b<YNY84(;,$&+........+#=-_}QKI6OOOO8OOYw64|_!-=#@.....................................+@&#=-;!(1}[PONwwP<[}(_;-,#@.........+@@#***>*****$*#%@+.........@%*=-;(:}[[QQQ[21[QIIQ[:(;*....................+@&%%%&&+.......................................+@*=>-!~|1[IJwNwOPQ}:_!;-=%+..................................",
+"..&*>;_b:f7g5iiiiii<iih7bbac=%..........@**,0fiqTTTTqgb0-*#@............@%$=90bgjqqpfd;3*%@.......@#=3agjWTTTTTUWZWWZWWWWqjgb9%.....+%$=9399ccc39-3-3-39c9-,*&......+%*,90_fiqWTTTTTWqieba3,*@.........+&*,;bgjUWjgd;=*@.........+#=9dfhhmpqWqWWqWqqjgbac,*&+.......................................@%$=-c_df4ioqqomhf|a03=$%+...........++@%%##*#%&&&@@+..........+&*$>30be47k774eef4hhkeba3#......................+@++............................................@#>,30defimqqqpi7e(a0->*&...................................",
+"..@#=3abb}ef7474477777feba09=@..........@#>9afpqWUqUqifc3>%@.............@#$,cdgpqjifc3>*&........@*>cdfpqqWWWWqZoZoZZZZZopgb3%......@*>>-9-3-,,,>>>=-9;93-$*@......@#$>90bfmoqqUUWWqoi7dac>$@.........+%*,cbiqUWqgbc3*@..........&=cdeklVnZWWqZZZooifd;9>*@........................................+@#$>99abegmpopi4ed~33=#@..............++@&@&&@+................+&#=,9ade444eeb|e4774edc3%......................................................................+%*>330de4imjnmh4bda3,*#@...................................",
+".+&=-;)/^rrzrQ^QrKrrKrr^])!;-@..........%=-!:rtGvvvDGyQ{!-=&.............@*,9!{rxtyP^|;3=&........&>3~:rxtGGHHGHGHHHHGHHHAtKF'*......+%$------->======-;--=##@......@#,-')FQstGGGvGGAAsK/{~-=@........+@=3c{IxDEEGx[);,%.........+*-~FrsyBHGGvHHHBAxK/{';>#@.........................................+%*,;!)]/rzsKzK/]_~;--#+...............+@@@++...................@%>;!){]/^///FF/^/^/F{~;#.......................................................................&*--;')F/zsszKr/{)~;-*#@...................................",
+"..&$-;)_]]///FF///^^//F]);;-#+..........@*-;)^stCBCCtsr);-$&..............%$-;'F^zz/{';-%+........&*-!)/rssxsxxxyxxAttxxssr/(;#.......+@&*$$$*$####%&%%*#*%@........+#=-;~_FrssxsxsxssrF])!-#@.........@#,')QyGGLvs/);,#+.........#-!]/zsxtBBBAAyssrF{!;,#&...........................................+@#=;;!){]FFF]{~';->%+..........................................+%=-;;)){{)))))){)))!;$@........................................................................@#>--!~{]1FF]{)!;->%@.....................................",
+"..+&$=--,;-;;;;;;;-!!;;,-=*%+............&*=-~][^QrQ^F_;,*@...............+@#>,;~~~!-,*&+..........+%=-;'(({{{{]]]]]]]1{()~'-*+............++.@+...+.+.+.............+%**,-;!(({1{1{({~;-,=*@...........&*,;([58P52_-=*@..........@#=-!)1]F////}/1(~;-=*&...............................................+@##=>-------=$$%@..............................................@%$*=>>-->>->,--=>=*&..........................................................................@%$*==>--,3--->$#&+......................................",
+"...@%#%%%%#%#%%#**$***%%%&@+.............+%$=;_|:}12|~;>*&+.................+&*>>-->>#&+............+&#>,-;--3;;;;3;-;;;--->*&+........................................@%**=--;-3--;---=**%&+...........@#$,c(}[2:_;,*&............@#>-;!__(|(((_~';,=*@+..................................................&&##**=***#%@+................................................+@%%%#&&##%###%$#%&+............................................................................+@&#%#*$***%&&@+.......................................",
+"......++++++..+++...+++..+................&*$,caa_da09,$&@...................@&*#**%%&&..............@%#*$=,,,,=,,,-,>=>=**%&...........................................@%##*$=,,,>===$$%@++.............&*>9adbbdc9>%@...........+@%#>,339;000cc3-=$%&......................................................++&@&@@&++........................................................+.+@&&@%@@@@+.................................................................................++@&&&&@@+.........................................",
+"..........................................+&#=,,99399,=*&.....................@&%%%&@@...............+&%#***$=*>=*******#*&@+............................................+@&#****$***#*&@+...............@%=,00bdac=*%@............+@%*$>,,3>,,,,>**&@..........................................................+..................................................................++@+.........................................................................................+@+.............................................",
+"..........................................++&%=>>-,=>>*%+......................+@@@+..................@@&##%#%%#%%%***#%#%&.................................................@%#%%####%%@.................+&#=-;~~';=*%+.............++%**$$>==>=**##%@..........................................................................................................................................................................................................................................................................",
+"............................................+++++@@@+++..........................+......................+..+.++.++++++.+.......................................................++.+..+...................+++@&%##&&@+...................@@++@@+@++..............................................................................................................................................................................................................................................................................",
+"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................",
+"................................................................................................................................................................................................................+...++...............................................................................................................................................................................+@&&&&@+.......................+@&&@+..........................+@%%%%%%&@+.................................................",
+"............+@&&&&&@@+......................................................@&%&%%%%&@+...................+&&%&&&&@++....................................................................................+@&&%#####%*####%&@..................................................................................+@@@@+..............................................................................+%##==,,>$$%@+.................+%%$=,3>$*%@+....................&%$>3-3333=$##&@..............................................",
+"..........+@#=>333,>*#@...................................................@#*=,3--33=*%@................+@*=>-3333==#@+.................................................................................+%*=,3,39;9c;9c9;33$%+.............................................................................+&%***$*#@+............................................................................#=,ccddddac3=#@................%>9cadddac3=#@+.................+*3caddb{b(a0c,=*&+............................................",
+"........+%=,;~{]]]{';->#@...............................................@#=-;){]{F]]);->%+............+&=-;~{]FFF])!-$*@+.......................+&&&@+................................................+%$,;')]F/^^^^r^^//F{~-#...........................................................................@%$>-;'~!!;,*&+....................@%##**%%@+...........................................%>']/rsxysr/]'-$%+.............&,)Frzxxxz^F);$#@.......+@+.....+$~]/zxyxAxysrF]~;,##@@@@@+.....................................",
+"........%=-;']F/r/F]~;-=#@.......................@@@+..................@#>-!)]Frr^//]);-=#@...........&=>;!{]//^//])!-=*%@..................+&%%##==*#@.............................................@#$>-;;)F^rrzzszzszzr//{;=+.........................................................................@&*,-!){{])!;-=@.................@%#*$=>,>,==##@........................................+%-)FrstBAAxrF);,*&+............&-]rzxBBBxzF]~-=*@..+@&%##$#&...@,{/zyBBCCBBxsr/]~-,*$#%%*#*&..................+@@&@+...........",
+"......+&*=,-!)(]]{_);-,=*@...................+@&@&&&@+.................&*=,-!){]]F]_);-=$%@..........+&$=-;~{|F1]|)~;-$*@@..................+&%#$$$>*#&+...........................................++%$==-;!)]F]FF/FF/}FF]_'-%.........................................................................+&#*>-;!)))~;=*$@................+&#$$=>=,,-=>**%&.......................................+%=;)F/rrKr/F_'-=#@++....@@+....@$;{F/rKr^/|!;=*%&@+@%%%#***%+..+$;)]/rzzzzK^F]_~;,*#%%%#$$*%.................@@%&%&&@+.........",
+"......@&#=-;!_(|||1(_;->*&+..................@%#*$$*#&+...............+%#>-!_(||::11('-,**&+.........@%*>;~((|::||((!-,$#@+................+&%***===>*#%+..........................................@%*=,;!~((:}}4}44[}4}}:1!-#+........................................................................@#*$,-!_|||('->%&................@%*=,--;;;;--,$*&&.......................................%=;_|2QI<<}|_;=$%&@.@+@&@%&@...+$!(}[QI<[:(!->$%%&%##$==>>=#@...*!(:[QQIIIQ[}|(~;,>*$$$*===#+...............+&##$#$#%&+........",
+"......&*$-0dbeeefeeebd;,>*@.................+%*>333,>$&@..............+%*=cdbefeef4eeba;,=%@.........&*=9abeee4444ebbc9>*&+...............@&*>339cccc>*%@.........................................+%$>90abefgipjjjjijijipigb;#........................................................................+&#=-cabfgiigbc-*#+..............+&#$3;adbdbdaa;3>$#@......................................%=3ab7ipmphfd0,*#&@@&&&##$$*+...*cbfimpmi7ba;=$##%#*=,9cc09=@..+$0be47hhkhh77eedc->**=339cc-&..............+&**====$$%@........",
+"....+&#>-0|7klhklkklk4d03=*@...............+#*3caadac,$#@.............@#$90ekkkkhklVk4edc3>%+.......+%*-c|khlkhkllhk2bac>*&..............+&=>;ad(bbedc3$&+........................................&*=3_e7hlmoqqqTTTUUUWWqqpgd,@.......................................................................&#>30degmqUqjgb9,*&+............@#*=3!be7kkkk4ebac,*%@.....................................#,0beinZZomke0;,$##%#*>>333,*..+>_bkmoZZXh4b0-==*=>3;adb4ebc#..+>dek<lllVVVllhked0-33;0d|eba=..............&*=-0'a0c=*&+.......",
+"...+#=-;)FztACCCBCBCAxrF)!;$&............+%$-;)Frzzr]~;-$%............&=-)/sBCACCCCCCtz^]~;=@.......@=-;{rtABCCCABCAyz/]~-*@.............%=;~]rzzssszF~-=@.......................................+#-'{/sACHHvLEuuTuuuuEEEvvx[;#.......................................................................%>-~{/stHDuEuw^{'-#@..........+&=-;~{^stBCBCCAAsr]);,$@...................................+*;{/stDEEDHtz/);-,---;!){FF]-@.+-FzxHLELvAs^{';;;!~{FrzsAyz],..@;FsxACCCACCCCCByrF{))F/rsxsr;@............+*-~]zssrF)-=%.......",
+"...@*-!)F/sACBBAABBCCAz/]);-#@...........&=,;!]zxHAxr{;->*%+..........&=;~FztABABABCAAs^])-=&.......&$-']ryBBACBCHGByz/])-=&............&*-!)/zyAAtsz]~-=&..........+&%%%##%&@@...+@%%##&+.......@$;']rAGGvLvDvEEEEEEvvGGvtsF;%.......................................................................&=-;)FzyCDEEDtr]~-=&..........@*>-~{FzxBBABBABBts/{~;=#+...................................#;)FzyBHHGHyz/);;--;')]F/rr/;@.@-]/stGGGHAsr]~;;')]/zsyAyyz{>+.@-FrytAACCCCBBBAsz/F]/rzsAAxr;@............&=;)/sAAx/);=*@......",
+"...@%>;!(][rKrr//^rKzQ/()~->&+..........+&*=-;(Q6wwP[~;,*#&@..........@#>-~{/^^/^rIzK^F)!->*@........&*,;)F^^rIKJwwKQ]_;-*%@............&#=-~]/rrr^F(!-$%+..........@&%#%#*%%%%&@@&&%&%#%&@......&*,;)}6wNNw6IPwSNSwOPKIIQ4:~=+.......................................................................+%*=-'(}<OwNO51~->%@..........@#=-;){/rKr/^^rrKr/]);-=&............+++.....+..............+&=-'([IKPKI[F~;>>=>,-~(]F[^F-@.+$;_:QKPKKQF_~;--;~)F//^//F{;$..@=;)]F^QKJwwJK^/F](_({F//r^/(=+............@#>;_F^^/(;,#%+......",
+"...@%*-'(12QQQ221}[Q[[}1(~-*&+.........+@#$>-c(7OSM64(;->*#%&.........@#*,;_11222}[QQ[(~--*&........+@*,;_|2[2QQIOO84_!-=$#@...........+@*$-!|[IQ[1(~->*&+.........@&##*$=$$*####*###$$>$$%%+....+&>3_b5MNNM5<5YSNSO6I<Q2:1!-*+........................................................................+%$,9~1<6SwO<1;-*%@.........+@#>-!~|}QQ[2222QQQ}|~!-=%+.......++@@@@@&@@@&@&@@+...........@*-;_|[IQI[}(_;->>=,;_|:}}[1-@.+*,'1}QII[[}(!;--;_(:[QQQ}1_-%...#-;(|}[QPOO8<2::(((|:}2[[[2~>.............&*,-_|221~-=*&+......",
+"...@#>9adefkhh7e1f7hh74bda3$@.........@%*=,90dfiqWWjgba;9>*##+........+%*,9_be:e447hkfba9,*&........+&*-9abe477hpoqpgbac=$%@............@*=30b4h<7edc3$%&..........&**>-33->==**$*>>,-33,,=#&....+%,cafiqTTqigijUTWqolh77bbc3%.................+++++...................................................+&*,9abijUUjgb0-$%@..........&*,;ab:7kh7fee44hh4bba;=*+......+&&%#$#%*&%%%##%&%@+.........&*,cde4hhk74bd03>>>3adee477f9@.+$,0b4khhh4ebd00cadbf<kh7e:d9#..+#-c_ee4hpqqphffebbdbf477h4e0$............+&*,;ab1bba9>#%+......",
+"...+*>9abeklVVh74kkVll7eba9=&.......+%*=-9cdbbgpWUWqm7ed~c93*%........@#$-0be47kkkllkk1a;3*&+.......+#*-cd:4kkllXoonhedc9=%&............&*>3_e4khk:ac,*#@.........&*,c!ad(da093,=,3cddbddac3$&...+#-cbfjUTTTjmmqUTTWZnVhk41a-#+...............@&##%&@+..................................................&>,cafiqWUWieac,*@..........@*9'be4kllIk[kklVlk4:ba9*&....+%*>,3333,=,,-3,-,,=*%@........&>9ab4lVVVlk7ed'c;0d|47kh<k49@..*9dekVVVlk74b(dd|e[kllVl7ed;#..+$ca:4klVoqWoVhkk[447k<llhkea$+...........@**9!de44eb'3$#@......",
+"...&=-~{/rxtCBCBAACCCAyr/{!-%......&*-;~{F^zsstDuEEDHxssr^F{;>@......+&=-'{/zxBBBCCCAsrF~;-*@.......@$-;)/zsABCBHHvHtszF);=*+...........#>;~]^ssyz/{~;=#+........+=;{/zsxAxzr/])~)]zxAtxszr]!$+..+$!1QJwuuTEGHHvEEEEGHCAtyrF~,@.............@#>>---->$*%@..............................................+%-;{FzADEEEtKF);,*+........@$-~]rzyACCAtyxBACCByz^]~-#...&#-;){FF]]{){{]]]]{)';-=%@.....+*;)FzsCCCCAAyzz^//rzxtCCCAxz'&.+-~^zACCCCCyyzrrzstACCCCAsz/(>..@;FrsttBCHvGGHCACBAABCACBys^(=............%,;~]rzyyyrF)-=%......",
+"...@$-;)]rsABCBBBBBCAAz/]);>#+....&*>-')/rzsxABvEEELHBAxyzr{);#.......#=-!]^stACCBBtxz/{~;,=%.......&$-;)/zxBACCBCBCBsr/);>*@...........&=-;{F^rr/]~;-*@.........&>'FryABBBAxr/F){/JGLHCBts/)-%..+$!]^svEEEuvBGLEEEvGCBBAxzF'>@............@#>-;;'~;;->=#+..............................................%-;)FKAvLEuAzF);,#+........@#-~]^syBBCByxtABBCBxsrF~,%...&=-!{/rrr/FFF/F//r/F{';->#&....+*;)FrxBCBCBAxszrrrsyABACAys/;@.@-]^stCCBCAAxszzstBCACAAAsrF)=+.@;/sAACBACCCCCCCACBBCBCAAxrF~$+..........+#-;)FryBBtzF);,#+.....",
+"....@**,-'{/IrrQ^^rI^/1~;-=#+.....@%*=-~1/QQQIKONSNwJPIrQ[2(!>%.......&$=-!(/IrKKzr^/F(;-=*%@.......@%>-;|2[rIrKKKKI^F]);,$%+............%$=-;~)~;-,*#+..........@*-)F^IzsKK^F|)!!([PJJKKr/{!>&..+#=;_25MNNwJJJwNNwwJKzQ/F{~-#............+&*=,;!!!;;->#&+.............................................+@==;!:<Owww5}(;-*%.........@%*-!(]/QrKr^//IrKIr/](!-#+...+&=-~{]}F1{|{1]::}}|);-,=*%+...+%,;):QPJsKr^//}FFF//rKzKr/])=+.+$'{FIKzKKr^/}/2/QzKzKrKr/:~;*..+,)F/^^rIrKKKKzKzrKrrKzK^F_'-&..........+&#=-;)F/zKrF~-,*@......",
+".....+#*,-!(|2}}}}22:(;-=*#+......+#$,c~[568688RNNNSYO8666<}_-%.......@*$-~|}QIIIQQQ}1(!->$&+.......@**-a:<5<Q[[QIQ[21(';=#@.............+@%==-,,>>#&+............#-9(2<8OJ8[:(_~~_1<5IKQQ[|;,%...@=-c(}56O68OYNNNSOPIQ[2|(;-%+...........+&*,;!(1:1~;=*$&..............................................@$,-a|<RSNY5f~;,*&+........@&$=;~(|[QQQ[22[QIQ[1(~;,%+....%=-_:2[[[}1::|}[<<41~!;,*#@....#>;(1[Q5IIIQ[[[}:[[[QQQQ[}(;>..+$;|}[<IPK<QQ[[[[QQIIIIQ[[:(;#..+$'(:}[QQIQQQQQQQQQQIQQQ2|~-*&..........+%$>-!(1QI<Q1~-*#@......",
+"......&#>=3~a|:e2f1d_0->>#&.......+%$,0eiqqWWWWUTTTTTWqWWqjibc$.......@*=9_ek<hhh<hh7fba;,$#+.......@$=3agjjih7khhhhfbd0-*%@...............@##***#%@+............+#-cdeioqqoifbdaadb7hkhlh7ec-&...@*>9afh5lnoqWUTTUqmhk7ebb9-%...........@%#>9ad:777ea;=#%+............................................+@*,;dfiqTUqpgdc-*%+........@&$>30_b4hhh<77khhh4bdc-=&+...+%=;d7hhhhk74ff7ijji7bda9=$&....%>0bfhhllh5lkh777hh<hhhh7fb0>..+>dekhllmmlklkhlklllhlhlhk4b0$..+*cd:7khhlhlkkhkhh<hhhk74bac=@..........@%=90dfginpifa9=$@......",
+"......+%*,-0ade4eeeba93=*%+.......@#,3agpZUWWUUTTTTTTUWUWWWif0*.......&*,0bklVVVlVlVVkebc3=%@.......&*>9dioonlhkllVk[edc9>%@................+@&&&@@+.............+#3ab7mZWWqmhfbdbbekhVVVll4d3#..+&*-9dehlVnZWUTTTUWnVlk441a3#+..........@#>9~beklVlkea3$%@............................................+&>3abgiqTTWp7ea9>#&........@%#=9!beklVVllllVVlkeda9=*+...&=,0|kllVVVVhkkhXqZjl741ac>#+...%-aekVVVVXlVVVVVlVVVVVlllked,+.+,bklVVXlXXXVVXVXXVVXV6Vlll7b>+..=0b4kVVXVVVVllllVVXVlllk4bc3%.........&#>-ab4kmoZZohbc>*%+.....",
+"......+&$-;_{F^zzr^]{~;>=%@.......&$-~FKGLEEuEEEEEuEEEEEEvvAk(>......+*-!]rxBCACBCCCByz^{'->&+......&=-!2JDvGBBCCCCAsz/);-*%................+@@@&@+..............%,!]rsHvELvHxsz^rrsACCCCCAs^'$...%,;)/ztACHvEEEEEEEDHCBxsr]~,@..........&=-)FrsABCCtzF~->&............................................%$-~FrxGDEEEGxzF);,*@.......@#=-~]^stBCCCCCBCBBxs^{~-=@...%-!]ryCCCCCCCACHvDEGHAAsr])-%..+$;FzxBCCCCCCBCCBCBCCCCCCByzF;@.@;/xBCBCCBCCCCGHHHHBCBCCBCBy^;@.@,(^zxCBCCCCCBBCBCHHHBCCAsrF'*.......&%>-')/zACHLLEDxQ{;-#@.....",
+"........@#,;;){F/F])';-$%&........@$-!{IxtBHGGLuEuEEEvGHGGGs^)=+......#-;)rsABBABBABAsrF)!-#&.......&=-!FztBABBABBAyz/{);=%......................................@$!{/stHvGGAyzrr^rsAACBCCBs/)$+..+$-!]rsytCHGDuEEELvCCAAsr]!>@..........&$-)FzyBCHHBzF~;=#+................+@&&@+....................+%>;']rsyGDGGAszF~;-*&.......+#=;~{/zyBCACACBCCByz/]~-$&...%-!)^yACBCGHCCBCHGvvvHHBs/{!*+.+>;]/syABACBCBCCCCCCBCCCGCAz/;@.@-FzxBBBCBCCCBCBHCHCCCCCCCCAz!@.+-)FrstBACBCCCvCGHBBGGHCCyrF!*.......%,;;)]/sACHLvLDy/{;,*+.....",
+".........+%*=,-;---=>#&+...........%*=-~1:}}[2<ORNNRPQ2[}::_;-&.......@*=-~]F/^^r^^/F]~;->$@+.......+#*>;d:2[////FF]_!-=*&+......................................+%>;_:QKPJKI[2{(((]/QKKKKI/~-%....@#=-;){][[IJwNwNwJKK^/F|!-%...........+&*-~][PwwwK['-=%@................+@@&&&@+...................+%*>-'1}[4[[<[2]'-=$%@........+%=-;){FQrKzKzKKrQF]_;-=%+...@#=;'{F[IJwwJKKKIPPOwwwJQ:'-%...#=-;){FF/2/Q^QKKKKKKPwwwOK}(=+.+*;~]FF//QrKKKKKKKKII/QIIJOP}-...%-;')]F/2^QPJwwwJJPwwwwJQ1~-%......+&=-;!)1/IKJOSO8Q(;>$&......",
+"...........&&%*****%&@+.............&*=,3c!_(12<MNS6<:1(a;c-,#+.......+%$=-!((1:1:|1(~-=$&@+.........+%*>-c_((||:((!;==$#+........................................%$-;(}QIIQ[}|(__(_:}[III[:_-&.....@%$>,;!_12<6SNYOPIQ[}|(;>#+...........&*>;_1Q6O65|;>%&................+&######&+..................+&**-;~((||||1(_'->*#+.........@*=;!_:[QQ<IIIQQ[:_~;,$&....+#>,;_(:QKOOPIQQI<I8YSS8<|_;#...@#*--;!((11}[QIPPIII<OYwY5:~>..+%,-!__|:}[QIQQQ[Q[Q[22[<885|>+..@%*--'_(|:[<OwwOPKI6SSO8[(!-%......@*>-'(::[Q<<555[1~->*%+.....",
+"............+@%%&&&++...............+&$>=,90abfgpqjpgbd093-=*&........+@#$,3c_____0_cc,*#@+...........+#$>99aa_0dac3-$*&&.........................................&$,cdfhhlh7f1bdddde47hlhhed,%......@%*=-30dfgjUUUqplk4fbd0>%............&$=30dfgigfd9>&+................+&$*$*>$*&+..................@#*=9;aaaaaaaac3=$#&.........+%=,;ab7gmmmmpmmii7bac3*%+...+&=39abbginXmlhhhkimWUWqifd9#...+%*=,;0adbe47hhmlikhijWWqpf0>...&=9adb:f47hh77774477777hmigb$...@#$>,30d:f7imjnpmiljWUqjgbc,&.....@%*-cb47hhhhh7efbbc9=**+.....",
+"..............+@+.+..................@%*>99adbehmnnmkfda93=*#@.........&#**>999c9cc99,$*%@.............@*==--c9c3-9,$*%@..........................................%*3c|7lVVllk742ee47hVVmXlkb0*.......@#$,90bfioWUUWnVlk7e|a9#+...........@%$,c0abbbac,*&................@*>,9c0c93>*%+................+&%$=33393!93-9>$#@@.........@*=0d:7inqZWZZWqZXi7ed03#+...@#>30d|4klmXVXVllllnWUUqp7bc$...@&#*3cad|ekhlVVVVVlVnZWUZpgb,+.+%-9_b4kklVhlk7e4f4kklVVllkgd=+..&#*>3c!b4klVmXXVllXoWUWjhea9#....+&*>cb4hVlVVVl[4eedac3>*#+....",
+".....................................%>-3;!)FrstCCCAyz/]~;-,$%.........+%#=>--;;;;;;--$#&@.............+%$---;;;;-->=#&...........................................*-'{rxBCCCCCBtyyxABBHHGGGtz{-.......&==;!)/zxvEEEEDHCAysr]~>@...........@#=,c!a_a09c3=#...............@=-;)Frzr/{';>*@................@&*>--;;;;;;-->=*&+........@%-;]/zxGvEEuEEEEELHszr]'-@...%=-~]^zsyCCHBCCCCBHvEEEEGyr{-...@#=-;(FrzstBCBCBCCCHHLEEEGs^;@.+=;~FzxACCCCAxszzzsxACCBCAxz],+..&=,-!)FrxBCCCBCHCHBvEEEDAs2)*....@$-'{ryBCCBCCAysszr/{~!;-*@...",
+"....................................+%=-;;!)]^sABBCBxz/]~;;,=&+.........+&&#$---->>$$%%++...............+%%#*$$=>-=*&+...........................................+*-;{^yCCCBCCABBABBCCBLLLGAzF,+......&*,-!)FIyGLEELvCCBAsrF',@............+%=,-;!;;--$#@+.............+#>;)Frytyz/)!-=#.................@%#*,>,-,->>$##&+.........+%>!(/zstvLEEuEEELLtxz/]!>&...&>;~]/zsyACBCCCBCCBGLEELtsr{=+...@$-!{FrzyBCCCCCCCCBHLLuLGyr;@.+$;)FryBACBAAszzrzzyBBCCBByz]-+.+%$-;~]^zyACCCCCCCCCvLELGts/)$+...+=-~]ryBCCCCCBtszzr//])~;-*...",
+".....................................&#*>--!_(FQIKKI/}_~;->*%@............++@@&@%%&+++....................++.+++@&&@..............................................%=,;)/IJJsKzKrrrrrKJJwwwOP}~=.......&#*>-;):Q6NNwOJJKr^/1);*...............+&%**##%&@+...............@#=-;(/KKK/|';,*%...................++&&@&&&@++.............+&%=-!(2<6OwwOwOwOJ<:(~;-&+...+#=-~(F[QKKzKKKKKKKJwwwO<2(!*.....&=,;)]F/IrKKrIrIIKPwwNw6[|-+.+*,;)F[rrIrI/F|{(]:[IPPKIr}];*...@%=,;'|F^IKKrIIKrKKwwNwO<}_;%.....%=-'{/IKKKrI^/}]:F}F|)!;,#...",
+"....................................+@#>-;!!(1}}Q[Q221(~!;-,*@.................+..................................................................................&*=;_}QPPKIKIQ[[[[IKJwwSO<|!*+.....@&#$,-!_|46RNNYJP<Q[}:~;*+................+@&&+++.................&*>-~14P66<:~;-=%+......................+....................+%*=-c~12<[<<<[<421~;-,*&.....&,;_:}[[QIIIQ[[<866665<}(!,%.....@*,;~1}[QQQQQQQQQIKOSNR84_,+..%-!_:[QQQ[21__~(|}[QIIQQ}:_;#...@#>-!(}}[Q[QQ[QQQ<IJSNR8<:(;#.....&$,;(2QIIIQ[21|(|:}}1(~;-*+..",
+"....................................@&*-cadbbee4k774febbda09=&....................................................................................................&*=3aehkVmmmmmimiilmjZWWqifd$......+%*=3cabfgjUTTqomkhk7eb9*........................................+&=3cbfiqWqpgba9=*@............................................@%*>,93caddd0daaa;9,=*%@....+#>cb4khllhhk777iqqomh7eb03=&....+&*-0b44hkkhhhhkhhhmoWUqpgb,+.+%9abf<hh7e|da0ade4khhh77f:a9#...&*>;df7kkk4k7k7khhmjWTUjgfd9%.....@*,;dekhlhk7f:ebef7kebd03*...",
+"...................................+&$,cd:44[kkhkllkkk4ee:da3#+..................................................................................................+%*,cb4lXVXoZZqZoonXXZZWWZphb,+....+%$,cabe7hmqUTUZoXXmVlhea,@.......................................&$>9dehpWUWoi4dc3*&.............................................&#%>=99990c0c399,>*$%@+....&$9d4hVlVVVVlkhhXoWomhk4ba3$&....+#=9_ekVVlklhllhlVlnZWUWol49@..*cd:kllVh4:bddbe4hlllllkkeb0*..+&*3ceklllIlhkklkhVXZWTUqm7e0*.....&*3!b[lVl6VVhk47kIllk4eda>@..",
+"..................................+%>-~]rsytBACCCACBBBAyysr/)-%..................................................................................................@=-'{^sBHHGLLEELDvvHBvLEEDHtr;@...+%=-~]/zxAGvLEEEELGvGGGHs/!#......................................+#-;)/stvEEEDBsr{!-=@............................................+%#=3-cc00~0'0;;3-=*@.....+*-)FstCCCCBCBBABGvLGAysz/);>&....%>;~/sABBCABACBCBCHGEEELvAs)@.@-(^zxBCBxzr//F/zxACCCBCAtyr(-...#-']ztBCAABAAtACACHGEEEDHxz{=....+#,!]rxCCCCBCCBAAACCCBxs/];%..",
+"..................................+*>;)/stCCCCBCBCCCCCCCBCxz]!$..................................................................................................@$-!)rxCBHGGGvGvGHHABBHHGHxsF;+...+%=;)/rsAHLLEEEELLLLLLLGx^~*......................................@#>!{/sBvvELGHsr]'-=&.............................................++%$,>----;->--$*%@+......*;~FzABBCBBBAtyAAHGtysr/]~-$+...+%,;~FstAAAyyAtBABBABLLLLvAz)&.+,{/zyAAAsr/]]F^zyCCBCBCCCtsF,+.+%>;]ryAAAyysssyxAABvLLLvCyr(=+...+#=;)^stCCCCCCBBBBCBCAysrF;#..",
+"...................................%*,!|5JwwwwwwwwwwwwwwwOK[(-%..................................................................................................+&*-;)}rPJKP5<Q[[[[22:}}}:(_-#....+@*>;)|}QJwNNNuNwNNNNwwO<|;&.......................................%*-~1[IOwNwOK}(!-*#+.................................................&@@&#&#%&@@++.........@$-;(F^^^//FFFF2[[[2|)~;-=%@.....@#=-')FFF]]]]]]]]/2IJwNwJQ1-@..*;')]]]]{!;;;;_:QJJJKIKKJPQ($...@*=;)FFFF]]))){]FFQPwNwJI[]!*....+&#=;)F/rIKsKKIrIKKzKI/F(~,+..",
+"...................................@$=9|IOOYYwYwYwSwOwYOOM84_-#...................................................................................................&$=-_:QQI<Q[2||(_~'!!0c0c9>#+.....+&*=;_|[5OwwSNNNwwwSwR84~,&.......................................&$,;(}<8RSR6[:~-=$%+......................................................+................+&=-!(::(__~___(||(~;-,=*#%+.....+&#=-!!(_';;-;;!!_145OYO52(-+.+&=-;;;!;--==,-c([6OPQQQ<865_$+..+&*-;~__!;;;;-;;~(}<6wY8<:~-&.....+&#=-_11[<PJ666668O6[:(~-*+..",
+"...................................@*=9agijpppjjjppjpjpjppgfa>%...................................................................................................@#=3a:7hlh7fbd0c033->>===#%+.......&*>3cafgijjjjjjjjpjppgfc=+.......................................@$>;af7ijqjigb0-*%&+.......................................................................+&*,;00_a9,390000a0;3=>$#&&.......&%$,99cc9-,=,,-3cabgimigea$...&*>,3,-,,>**>-3afgii7f47iiga#....&#>390099>>-=,-90bgiipgfa03&.....+@%>,;adbgjqZqqWqZqjgba9,%...",
+"...................................+%$,3dbffg777g7gg7g7gfgba3=&...................................................................................................@*>-aehlVh4eb09,>*%&%&&&&&.........@#$,-0affg7g7g7g7ggffb09*........................................@%>3db4hmpihedc->#&.........................................................................&#>>999999,9,3,;99,>>*&@+.........&*=--9,->>>*$>-90cdfffba3*+..+**=>>$=**$$=*33abfbebbfbbd3#....+%*,93>-=>**>=,330abffba03=@......@#$=30aehoZWWZWWWZoibc9>#+..",
+"...................................&*=39ad12F4///F///[F::1dac>%..................................................................................................+%=-!]rytBxr/]_9,#%+................@#=-9'_1]}/[//F2FF1:(d!9$+.......................................%=-!]/zsxtys^]~;=*&........................................................................+&#=>-;;;;------;--,==*&++.........@#$--->===>===--;~a(b{(!3%....%*==-=,==**=,9ca_|{]]{|1da,%....+%*=>--==>=*=,-;;'ad(1{_';-&......@#=-;_(FIyGHHHHHGCxKF~;,#...",
+"...................................+&#=-;!~){())))))({))~~;--#+...................................................................................................&*,;)FrszzF{~;-*%...................+&$-;;!~~'~)))))~)~~;;-#+......................................+&#>;)F/zsysr/);-$&+..........................................................................+%$>,>->,=,=>$$$*%%%+.............+%*#*#$**=$**=,--;;';;;>&....+&%*%$##%&%#*=,-;;''''!!!->@.....+&%#**%%@&&#%$->-;;!;;;-,#+.......+%>,;')F/rrrzzrrr^])'-*+...",
+".....................................+@%*>==-->===,-->>,===*&+....................................................................................................@%#=-'){{)'->=*++.....................+@%#==,>=>>=,,>,>=#%&+........................................+@#=-;'({]1)'-=*&+.............................................................................+@@@&&&@&@&&+.++.+...................+@@@@&&@@#*&%*$*%%@........+.++.+....@%#*$*#***#$#@...........+++...+.+&%&%*$*%%&&+.........+&%#$>3;;!;!!c!;;-==#@....",
+".......................................+@@&&#%**#*%%%##%#%&@@.....................................................................................................+@&#=-';!!;=$%@+........................++@%##$*%$##%%%%@.............................................+%*$--;;;-==%@......................................................................................+.....................................+++++...+......................+&@+@++.+++......................+.++..+................+@%#*=>=======**&@.....",
+".........................................+++@@@@@&@@@@@@++...........................................................................................................@%*,,,=*%&++............................+@@@@+@@+...+...............................................+&&%%#%*%&%@..............................................................................................................................................................+.......................................................+@&&%%##%%%%@@+......",
+"...............................................+..++...................................................................................................................++@@@++.+...........................................................................................+++++.++..........................................................................................................................................................................................................................+.+.......+........",
+"..........+++@++++.............................@@@+@@@+@+....................++++@@++.........................+++++++..................................................++@@&&&@@&@@++...........................+.......................................................+@+@@@@@@++............................+++@@@++....................+++&&&@++.....................................................+++++++................................+++++++++..................++&#$$$#&++..........................................",
+".......+&*>=-,-->$*&+........................@#*$=>----=*#+...............+%*$------>*%+................@&%%#*$$>>>>$*#%@..............................................+@#=,-;!;;-,>$#%+.....................@+@@&@@@@@@&&@...........................................+##>>,--,==$*&.........................+%*>>,-,>>$#@................&*>--!;--=*#@................@&&&%%%##%%%%%@+...............+%*==----=*#%@.........................+&#*$---->>*#&@.............+%$>-)~))~;>$*#&+.............................+........",
+".......+#*-;!~';-=$%@+......................+@%=,-;;~'~;**@...............+*=-;!)));-=*@...............+&%#*$=,>>>>>,=$##@..............+++++++++@@++++...............+&%=-;'))({)!->$&@...................+@%%&&%%%%&%&&&&++..............++@@@@&&@@+@&@+............+%*,;'~!;-,=*#+........................&#*--!~;--=$#+..............+%=-;')~!--=*%+...............@&%&#$%%#*#$##*&+..............+#*,-!~';-***&+........................+%*=>;!';;-=$%@.............@#,-!)]1|)';=*#%+................++++@++.++@@@+........",
+".......&*=-;~(1_;-=$#@......................@%$>-;;((1~;=#@..............@#*>;!((||_;-*#&..............+&$$=-;;;;;;;-,=*%&..............+@&&%%%%%&&%&&&@..............+&#>-!(|221|';>*#+..................+&##*#*#**##$####%@.............+@%##**##*##%%%&+...........@#*-!(1(~;-->*&......................+&%$=-;((_!-,*%&.............@%*>;_(||(!-,>*&+.............&&%#$$*>>$*$*>$*#%@+............&*>-;~|(~-=*$#%+.....................@&%#*=-;(1(;-,=#@............@#>-;~|}[[:_!-=*%@..............+@&%%&&&&&&%&&&&&.......",
+"......@%$=90deeba;,*#%+...................+@#*=3ca_bbedc,*%+............+&$=30abeeebc9=*%+............@%#*>3;aaaaaaaa;3>*#@............+&&##***######%%&+.............@%*,;abe474edc3*#@.................@%*=>>>=>>,,=>>==>>#+..........+@%#**>>==>=>==*$$&+..........@#*-abeb_a0;,>*@+...................+%#*>,;abeba;3>#&+...........@&*>3abeeebda3>$%+............+%#$=3-3-3>3-33,,=>#&+..........@%*=3ade:dc9=$*#@+...................+&%#$>3cdbeb_c3=#@...........+%*,;ade77kfbd03=*@.............@%%%#$*#%%#%####&%@+.....",
+"......@$=,;de4k4bdcc,*%+.................+&*=,ca1ee444:a3>%+...........+&$=9~b}7khk[b~c,$#@..........+%=,99dbe44eeee:bac,=#&..........+#*=>,-3-,,==,,-=$*@...........+&*>3_b2khlhk4(c3*#@..............+%*,30caaaaaaadaaaacc>#........++&#=>3ccaaaaaaac93,,=#+.......@%$,c(4444e|dc3=*&+................+&**,9ca(ekk4|a9=>#@..........&#>-9a{4khkke|d93=*&..........@%*>-ca1bd~aa_dbdaa93=#+........+&#>3;de[7eb~ac,>*&+..................&%$>3;a(e4k4ea;3=&..........+&*,9a14khllk41d03=#+...........&*=>,,,,=*>>,,3-==>*#@....",
+".....+&>;;)FsxBAxsr/{;-*@...............+#=-')FrsAAtysr]!->&..........@$>;~]rsABBCCAsr]~-=*+........+%>;)]/zxACBHHtAAyr/);,*+........@#-;~){]FF{~~){]F{;-=&.........+&=-;{^sxCACBtyrF);=#+...........+&*>;){/^rrrrzrrrzrr^/F)-@....+&%$$--!~{F^^rrrr^/F]]{)!,%.......%=-'{/syAtAxz/{);-$%+.............@#=-;~]rzxyBByr]);-=*+.......+%=-;)]rsyCACAAyrF);-*@........@#=-!)/zyyzzrrzsxsr^F{!,#@.......&$,-~)/stBAssrF);->#&+..............+%=,;!{/zsxAAyz/{'-#+.......+@#=;~FrsACCCCCBxzF)!-#@........@#,;~){{]])!!~)]]]{)~;-=&...",
+"......#--!)/zAABBAsrF)-=&...............%=-;)F^stBAByz/{'-=%+........&*>-!{/sBACCCBBAz/);-*&.......@%*-)]rzyHvLLLLLHCAsr]~-=%........&=-'{F^rzr/]{]/zz/);,*&........@*=-~]rxBABBBBys/{;-#&...........@%=-!]/rzzzzssssszszzz/];#....@#=,-;~)]/rzzzzzzzzr/FFF)!>+.....@#-;'{/zyABCByz/]~;-=#@.........@&##$-;!]^stBBABxrF);-,*&......@#=,;'{rstBACCCAByr]~;>*&@.....+&=-!)FryttyszsxyAxsr/F)-=%......+&*=;'{/zACBBAxzF);-,#&@...........+@#*--!)/sytBBByrF);-=&.......@*=-')^sBAACACBCByr]~;-#@......+&>;']F/rr/F{)]//r//F]~;>#...",
+"......&*,-;)]^rrIr/F_;=*&..............@%*,-!)]/rKzr/]~;-*&+........+&#=,-~{/rKzKzKr/}{!-*%+.......@#*=;~]}QPwwwNwwOJI/|!-=&@.......+&*,;!)]}/}])')]}F(;-*%+.......+&$=-;)]^rrr/^^FF(;,$%@...........@%$>-']}/[Q[/^Q/[[^[/}:_;&....@#*,--'~(]}/[/[/[/22F:|();*......@#>,;'(]F^KzK/F|~;-==*%+.......+&#*$>--;)]/rrrI^/|);->$%@......@&*=-;~]/rIzKzKKr/]_;-=$&@.....@#>,;~(]^r^^/}//rr^/F|)',*&.......@#$=-')F/rrIr/})!-=>$#&+..........@%**,-;)]/rrrr//(~;=*%+.......@#*=-~{/IzKrKzIzI/F~;>=*@......@&*,;'_{]2]|)~){F2F{_~;>*&...",
+".....+%>>;!_|2}[[[[1_-,*&+............+&$>-;~(|}[[Q}|_;-$#&+........+@#>-!~|}QQQQQ[[2:~;=$&+.......+&$,;_(:[<P6JOOOPQ[|~-=$%+........&*>;!(|22}|((((||);=$%+.......@&*>-;_12[Q[Q[21(~;$%@+...........@%*>c(}<<55555<555<555<bc*...@%*,-;~~14<<<<<5<<<<<<4}:_!>@....+%$>;;__:}}QQQQ}|(!;-->*&.......@#*$-;;!~(1}[Q[[21(~!;,*%@.....+&$>-;'(1}[Q[QQQQQ2|(~;-*#@.....&*=-'(|1[[[[}2}[[[[}1|_;,$%+......+#*,;;_|}}QQQ[21~;;,=*#@.........+&#$>-;!(1[[QQ[2:(!-->#@.......&*>-;~(}[QQ[[[QQQ[|~;->*@......@#$>;_(|:}}1(((|:}::(_;-=&...",
+"....+&#>3cddb:f7k77fb0-$*+............@%>9cabbe4777fda-=$%@.........@%*3;dbef4k47444f1dc,$%+.......@&$,;dee47hhihihh4ed0-*#%+.......@*=3cadee77fbbbbbba;=*%+.......&#=3cad1f4<7<47bdc9=%&+...........@#*9agiqqqUqUqWqqqqqqUqgb*..+&#>9adbfijjqqqqqqqqqqqjpgfd,&...@&*-;aab:f477hk774ebdac9=#+.....@&$,3;adbbe74h<474fbbda9>*&.....@#,9!db:7477447k7h7febac,*&....+%=3;0bee4h4h44e7777eeedc-=%+.....+@$$-cdbbe44k774fbda;3=*#+.......+&#=-30abb74<k77ebbdc->*@.....+@#=-0_b:477[7444h77ebba3=&.....+&$>-0dbef77feeee474ebba;=#...",
+"....@%=3adee44k<lVlkedc,*%+..........%*=-0db2kkkIlk4eac3*#&@........&*>cd1e7Illlhk4k7eb03=#@......+@$=3ae4khhklVVVVh7edc3>#&+......+%*,cd:e4klkk[444e1dc,$%@......@%=30d144khlVVVh71a;>*%@...........@*,3agoWUTTTTTTUUWWUTTTjb>..@#>3~b47hpZWWWWUUUUUUWWZnXhea*..+#>3abe47kkhhhllVlhh774eda,#+...@*>-adee47kklllllhhkk74edc3$@...+#3cde47khlllhhkllllkk74ba3$+...&#,cde4khllllllh5lllhh471a9=%....+&*=30d|e44k<lVVkk4ebdac3*&......+&%=3cdbe2kkllVlk44e|dc3=#+....+%=30d:47kklkkkhklllk4eda3#+.....&*3a(e4[hkkk7[4kklkk4eba3#+..",
+"...+#,;)FrzsxAABCCCts/]~-$&........+%=-'{FrzyBBCBBBxs/]~-,*@.......+#-']rsxtBCABCCABxsr]'-=&......+%,;)FsxACCCBCCCBBxs^{!-=#+......&=-!{/zstBCCBBtyyyz/{;-=@.....+#,;)FrzyABBCBBBBxz/);-*&..........+#-;)[sGvEDEEEEEEvLvDDuuw7;+.#-!{ryAHHGGvvvvvDDDDLLLvGBBsF-+.&,!{rxBHHGGHGvGHGGGGGGAGs^];$+..%-!{rxBHHHHGHHGHHHHGGHttK/'-#...#-)/ztHHHGGGGHHGGGGvvGHAs^];#...$;~FzyHtvvGGHHGHHHGGGGHts/);#....&*,;)/rssxABCCBCBBxysr/]);$+....+#=-'{FrzsytBCCCCCtysz^F);,&...+&=-~]rsxAACCCACCCCBBByszF),&....@$-~FrxxACCCCABACACABysrF'-&..",
+"...+%-;)FrsyBCCCCCCCsrF);-#+.......+%>;)]rzxACCCCCBByrF);-$&.......&=-!]rsyBCCCCCCCCBsr]~-=&.......#,-)/sABCCBCCCCCCAzr]~->%@......%=-~]rsxBCBCBCCCByz/{!-*&......%-;)FzsABCBCCGCByz^{',$%+..........&-;(/stGLEEEEvHBCCBAHBAx/-@+#-~FPtvLGGCHBAyyxAtCCCCCCCAs/,++*-'FsGGLLLLELLLLLLLLEELutzF),&.+*;'/sGGLLuLLLLLLLLLEuEEExr]!=+.+*;)/JGGLLLLELLLLLLLLEEEvwzF)=+.+$;{/JGLLLDELLLGLLLLEEuEDwr{;*+...&=-']/zsABBCCCHCCBBAxsz^]!,%....@#,;)FrzyxBCCCCCCCCBysz/{!-#....#=;~FzsABCCCCCBCCCCCAAxzF)-%...+#>;)FzyBBCCCCCCCCCCCBtyzF),&..",
+"....@*-!{}/IKJJwwwwOI}_'-=&.........&$=;(F[IKJwwwwOPQ}(',=#+.......@%=-'][rKJJwwwwOPI[{;->*@.......@*=-)/QrKJwwwwwOPI2{~;=$&.......@*>-)]/QKJwwwwOJKIF{~->#@......@*-!(}/IKJwwwwwJI}{!-*&@...........@*,;~2QPONNNwJKI/[[[rIK/(=++&=-)[8wwwJKI/}]1|1:[QrKKIIr})*.+&>;_[6wwNwNNNwwwNwwNNNNS6}(;=@..&,;_[8wwwwNNNwwwwwNNNNNS52~-%...&=;(QOwNwNNNNwwwwwwNNNNw5}_;%...%-':QJwwNNwNNwwwwwwNNNNw5{~-%....+%*,'{/[IKJJwwwwwwJKK^[]~-$@....+%*,!{F[QKJwwOwwwwJJI/[]~-$@....@#=-']/IKJJwwwwwwwJJKr/F_-$+....&#=-)]^rKJJwwOwwwwJJzI/F(;$+..",
+"...+@$>;(}QI6YMNRNRY52(;,*&+........@%-;(2[<6YMNNMN65:(;,$%@.......+%$,'|}[I6OSSNNS8<1('-*#@.......@*=-!:[QKJMMNRNN6<1(~->&@.......@%*-'1}QIPOSYMNM8<:(!-=#@......@$>;(2[IPOMNNNMY5}(;,$#@...........+$>-a1[<8SNNS8<Q[::}4Q[2;*..&=9_f8RwR8[}|_~';'~(}[Q[QQ[|;%..&*-0|<688OOYO8886888NNNY5:~;*@..&*-a2<68OOOOO866886YNNNY5|~,*...&=-~1566OOOYO868688OSNSY51!-%...%=;_2588OOOOO88866OMSNNO<1!-#.....@*=;(}[<6YSRNNNNSYO5Q}1~-#+.....&$,;(}[<POYRNNNNYOJ5Q[|'-#+....@%$-'|2[IKOYSSYYNSOOPQ[:~-#.....+%$-!1[QIPwYNYNYSSYJIQ[:_-#+..",
+"....+*=;d:7ipqWUTTTqifbc9>&+........@#=;de4hjqWTTTTqjeba-=#&.......+&*,0b47hpoWUTTTqieba;>$@.......+%*3a|44ipqUTTTTjifba3>&@.......@%*3a:fhhjqWTTTTjifba9=#@......@*>cde4hmoWUTTTqigbc3$%@...........@*=9afgmjWUWqpi77fe47k7bc%..&>cdgpqWqmgeba99-90def7774fb3#..@*,cdfgg5i5mgggffggijUWqieac*@..&*-cdf7hhi5mi77fgggiqWWjiea9#...&$-9dfg5ilmmi77777giqWWqgfc9#...&=3abekhi5mmh77777gjqUWjiba-&....+&*>cdeehpjqUTTTTTqjikfb_3#......@*,;de4hmoqWUTTTqqji4fba,#.....@%*,0be7hpoqUTTTUWjmi7fba,#.....@%*3a|f7lpoqTTTTTqqph74b0,#...",
+"....+&>3cbe7mooWUTTWjl71c3>#+......+@#>3!be7mjqWUTTUoi71c3=&+.......@*=;db4hmnoUTTTWmh71a3*#+......+@*,9db4kmoqUTTUqmk4103>&+......+%$,cde4hXnqTTTTqmk4(c3*&+.....@#*30df7lpoTTTTUjh4(03>%+.........@&$-0b4mooqZoonVVllkkkk4b;#..#3aehoWWZmhedc93339cbe4444ed9&..@%-;ab4khVVVI74ef}4ioWWqifd9=@..&*,cae7khlVlhkf44k<moWWqiba,*+..@*,cae7khPmml74fe7hmoWWqib0-%...@*-cbf4kllmmh44e47hnZWWoifc3#.....@#=3cbffipjqTTTUqjlg4edc=&......&*=90befkipqUTTUopk74:d;,&.....+&#=cabe7hmjqUTTWoph74bdc,#+....@%$,cabe7kmjqTTUWomh7fbac,%+..",
+"....+&-;~{^zxHGLEEuEGBAz/);>#+.....+&*,;~]rztAvDEEuuGHxzF~;>%.......+$-;)FrstCvvEEuEGBtzF);>%+......&=-;(F^stHvLEEELHBxz]';$@......@*>-!{FrsAHvEEuuEGAyr]~->%+....@*>-~]rstHvDEuEuvHyzF);=%+.......+#=-~]rxDDDvGGHCCCCCCCtsz/'$+.*~FKADEEEtxrF)';;;'~]/rrrr/]!%..&$3_FrsBACCBAysssxBAGvLGxKF_-&..&=;_FzsACBCCAyssytAHvLLGyI{;$...&=9_FzyACBHHAxssxxBGvvvGyQ{;$...%-;)/zxtCCHGAxssxtBGvLvGsQ{;*.....&=-;~{/rzswvEEEEAxsK^F{!3*......+*,;~{F^zstGEEEvGysr^])!-*.....@%$-;){F^zstDEuEvAysI/])'-=@....@&$,;~)F^KxtuEEEEHyKr^]);-*+..",
+".....@$-')]zyCCvLEELGHAs/)'-=&......%=>;'{/zyCGvLEDvvCBs/);-*&.......#=;~]/sACHDvELLvHAzF);-$&......@*-;!{/zABGvuELGGCAzF);,#......@#=-!)/zsBCvLEEEvGCtzF);,*@....&$*-;{/zyBGLEuEvvHAs/)!-=%+......@*-;)/KAvLvCCBCCBCCCCCBsrF'*.+>'/ztvLEuHsr]);;--;;)]F/^^F)-%..+$-!FrxABCCCAyssxACCCBCts^{!,@..@$-'FryAACCCAxssyBCCBCCAs/);*+..&=-)FryAACCBByssyBCCCBBszF);*+.+#>;)FzyABBCCAyssyBABCCtxzF);*+....+%=-;'{F^zstEEEuAsr/F)!;,%+......@#-;!){/zsHLEuDAsz^])!-,%+....+%#=-;')F/KyHLEuDAzr^])';,%+.....@#>-;;)]^zyGEEuHAsz/]);;,%...",
+".....+#>-;_1QJJJP6P6JOPQ|~-=*&......+%*>-!_2<JOOP6PJOJPQ{~-,*@.......&*=-'(2IJJJ6P8OOJP[|'->*@......@#*,-!)2KJJJP68JJJI[('-=%+.....+%$=-!):[KJJJ866JOJK}('-=$@....@%#=-;(2QJJJJ888JOKQ1';=*%@......&*>-;(:5JwwJsKsKKKKsKKr2{~-%..%-_:<ONww52('->$*$$=-;;!!'!-$+...&=-'1F/QIKK^/2F[rKKIQ^[{~-=%...+#=-'{F^rIKrQF2F[IKrIQ^}{!-=%...@$=;~{F^IKKKI/}/^QKIQ^[1(;-$&...@*>;_1/[rIKKQ/}2^IKIQ^2:);-#@......+&#==-;_(45wwNO<}(!;->$#+.........%$=-;;1[5MNNO<}(';-=*%+......+@%*>,-;~1[8wNw6<:_';-*#&........+&*>=-;_|[6wNw6[1)!;-=*&+...",
+".....+&=;!_:<6O5<[4<8Y6<1~!3=#+.....@&$=;'_:58Y5<Q[<6Y6[|_;-=%......+&$>-'(}<6O5<[Q56Y6[1~;-*&.....+&%$,;~(156O5<[Q<6O841~;-=@.....+&#>;!(1[6OO5<[[56Y5[(!;-*&....+&*>-!(1<8O6<[Q<6Y8<:('->#@......@#>3!_:Q6JPPIII6666665<:_;=@..#,!b[6MSO<:~;=*##%#$*=,>,=>>&+...%*,;__}[QQQ[}::}[QQQ221~-=&+....&*,;_:22QIQ[}11}[<Q[}}1~;=#+...@%*-;(|}}QQQ[}1:}[QQ2}|(!-**&...+&=,!(:2[QQQ}21}2QQQ}}1(9-*#+........+&*=-9~1<YSS67(!;,$#&@...........&**,c_15YNM64(!;,*$%@........++%#*=,c_15MNR8}('->*#+...........@%$=-9_e5YRY5}_;-=*%+.....",
+"....+%=-cdbfiqqmgffgpqjifbd03*@....+&#>3cdbgiqqpgffgjqjieba0,#+.....@%=-adbgpjqigffijqjifba0,#.....+&*=3adbgmqoige7ijqpiebd;,%......@*,;abegpqoig7fijqjgfba;,#+...@#=30dbfiqqpgffgpqoiebbc3=#+.....@*,;abb7imlliVmoqUWWqqifd9>@..%3cbgjWWqifa;,=$###*$>=>>=**@....%*-cdbe4hmhh4}f7hhh7feda3>&.....%*-cde44hmi74e44hlhhfed03$&....+%*-9db44lih4ff475ihfedac3*&+....&=,cde4hhli7fee4hlhfeda9-*&..........@#$>90biqUUjgb03=*#@............@**>30biqWqpfbc3>*%@...........@%#$>-0fiqUqjfa0-*%&............+&#=9;0fiqUqifdc,*%@......",
+"...+&#>0de7hnoZph[7koZZnh7edc,&....+%*30be7hpZophkkinWZph4ed9>&....+#>3a(e7hnZZnh4hloWomh4edc$@....@#>9abekkpZqXhhhloZomk7ed9$+....+#=3_b47koWqmh7hmqZom74bd9$@...&*9c(e7hnoqnhkkhoZqnl72dc,*&....@*>9d:44lVlXVXXoZUUUUUqjgb0,&..#3aeioWWqp7bd03-,>,,3-3-3-,*%....&*,~b4klmonmlllhnnXh7:dc3=#+....&=-a|ehlmoomllhVnnmh4eda3=#+...@#>3_b4klXoomllhmonmkebac,>&....+%=9a1ekhnonmlllmnpl71bac-$%+.........@%*,cafiqUUjiba;,$%+............@%$,9dfiqWWjgba9=#%+............@#*390fpqUUjgb9,>#+.............@%$>0bfjWUWjgd;,>#.......",
+"..+#,;~FzxAHGELvHABHDEDGCtxz]~=...+#=;~FzxCCGELvGBAHDEvGCtyr];$...+#,;)/zAAHGEvvCAAGvLvGAAsr];#...+#,;)/syCHvELvHAHHDLDHCtsr{;%...+#-;{^stBGvEEvHtAvLLvHAAsr{;%..&=-)FzyBHGDEDHBBHvEvGBAyzF)3$+..+*-']rstBBHGGGGHGDEEEvDvts/{;%..*_FztGELDHssr/{)~~){{{]]{{);>+...*-!{^stHvLLvGCCGvvvtszF{~;>&...@$-~FzstGvuEvHCGHvDvtsr/]~;=&...&=-~FrstHvLLvHBHvvvGxz^]);-=@...@=-~FzsAHGLLGACCGDDGyz^]);,=@.........@*,;(/KtEEEDyI]~-=*@...........+&=,;)}ztEEEGJr{~-=*@............&>,;(FKtuEEvJ/(;-*+.............&$3;(}JtuEvvs});-$+......",
+"..@#-;)^sBCBGLLLHCCHLLLHCCAs/)>+..&$-;]^yBCCGLEvHCCHLLLHCBts/)>+..+*-~]rxBCHvLLvCCHGLELHCCtsF!*...&$-!]rxBCCLLLvHHGGLLGCCBtzF'=...&=-~]zyBCHLLEvBCHGLEGCCBtzF'$++%>!)/sACBvLLLHCBCLLLGCBAs^)!>&..&$;)FztBCHLLLLLLvGHCHHHAyr]),%..*;FIyGLLLHAszrF]]]FF/rrrr/F),&..+*-!]^syAGLLvBCCGvLvAsr/]~;-*+..+*;~FrxAAGLLvCCBGvLGysr/]);>%...&=-~]zsAAGLLGCCBGLDGyzrF)~;,%...%=;~FryxHvvLvCCBGLLtxz/F)~;-#.........@$-;)/KAuEEDxr]!;=#+............&*-;)FPALEEvy^{'->$@............&*,!)/JALEvGs^);-$+.............@$-;)/VALuuGs/);-$&......",
+"..+&=-!{/IKPwwNwwJKJwNwJJKr/|;*...@#=-!]/IKJwwNwJJKJwNwJJzQF(;*...+%=-!]/IKJwwNwJJJJwwwJJKr});%...&*>-~FQrPJwwNwJJJJwNwJKKrF_;#...&*,;'FQKKJwwNwJJJwwNwJsK/F)-%..@*-'{/IzJwwwwwJJJwwwJJzr}{!-*@..&*=;(/rIKwwNwNuNwJPKQIQ[2(~;$+..@$;([6wwwJIQ}]|)~)1]}}}/2F_'>+...&=-!)F[IJwwJKKKJwwO<}]_~;->&...+&=-'1/[KJwwJPKPJwwOQ}:_~;-=&...@*>-~12QIJwwJPKKJwO8Q}|)~;-=&...@#>-~]2QIOwwJKKPJwwPQ:{(!;,*&..........@*-;_e5wNwOQ|'->#@.............@&*,;~}5wNwJ<|!-=*%@............@%*,;~26wwwJ[|;-=%@..............&=--)26wNw6[(;,>#@......",
+"...&$,;(}QI5OSSOJPIPYSSOK<Q2~;*+..@#$,;(}QIIJRSO6PK8YMw6KIQ}_;*+...&*-'1}QI5OYSwJIP8RSSPII[}~-#...@#*-!|2QIPOSYOPPPOYSOPK<[1_-%...@*=-~|[QIPOwSYJPPORSOPIQ[:'-%..&$-;(}QQPJSwYJPP8OSROI<Q2(;,*@..@*-;(:QQPJSNNNNNS6QQ[[2:1(!-*+..+#,_:<88O6P<Q}1::::}[<<<}:(!*+...%$,;_:2QPOY85QI5OY6<}((_';>#+...&*-;(:2<8OO6III6YY6[1(__;3=%...+%=-~_:[<8YO6I<<8OY6}1((~;-=#...@%>-!(:}<8YO8III6OY54:(_~;-=%..........&*=-c|5YSR84(;-=#&.............@%$=9_|<YSR82('->#&+...........+@#$=-a:5YSY84(;-$$@+............+&*>-a15YSR5e_;,*%&......",
+"..+&=3cb7hmmjZqommlmjqqoplh7d9*...@*=9ab4hmmoqqqpmmpoqoXmlhfbc*....#=-cbkhmmoWqomlmmqWqommhfd3%...@$=9af7immoqoopnmjqqZXmlhfdc$...@*=9aekimmoqWjmmpoqWopmlgfd9#..@$3cdfilpoqWopmmnqWWomlh7bc3*@..@*3cb7himjqWWUUqqjih77febba0$+...%>0d7hilpopmhk77khimjjjifbc$+...@*-cdefgpoomlkhmpqjgfbbdaa9*+..+&*3cdffhpoomihhmjqpgfbbdda3*...+%*-0de4hpoomihhmjqpgbbbbac3#...@%=3cde4ipoomhhimjqigfbbbaa-&........+@%*,9abiqWWjgb;3=$#+...........+&%>-cafiqUqjgd0->*%@...........+&#>-cafpqUqjg_c9=$&+...........@&*$3;dfiqTqjeb03=#%+.....",
+"..@=-'b7inXoYZoXXVVVnZZoZomifd=+..@*-!b7mXoonZZnnXXXnoZooonlfd,+..@$3ab7mnnooZnnXVVXnoZoooXie0$...%=9~b4mXonZZoXXXXnoZoooonhfa=+..#>c_bhmnXoZZonXXXnnZXZnomkea*+.%-0d4iXooZZoXXVXnooqZooXi4dc,&..%30d7inXoooqoqqZoXVlk7k444e_9%..+%>9de4lmnZZoXVVVVVXZZWZph:a,@..+#,cdb4klXnXmlhklnopmhkk44ed,&..+*=9~b4klXnXVlhhmmnmVkk444e_=+..&*>9ae4hlXonVllhmnjmik7444e0$+..&$,0de4klXXXmhklmnomlk4444:a$........@*=33adgiWUUoiea03,=*@.........+@*>,30bfpqUUogedc3>=&+.........@#*>,0abgjUUWjged;3,$#@.........@&$=39abgjWUWjhbdc93*#@....",
+".+*;)FzxHEEDDCCBCBCCHHGDLEDHs/'@.+$;)FzyvLLLLvHGCBCCBHvLvLDGs/;@..*-)/zyDEELvvvCBCCCCGvvLDDts/;@.+=!{/sADvEEvGCCBHCBCGGLvLDAsF;+.+=!{/stvEEvGGHHCBCHBvvLLLDtKF-@+$'FzxGvDEDvHCBCCCBGGDLEDGyrF!$++$'FzxGDDDvGHHGGGHBCCAxyyAAAz{=..+#-!]rstAGDLDHHCHHHHLEEvGyzF;%..%,'{^zxABHvBHtAttBHCCBBBABxr{*.+*-;(/zytBHGHBABtCCCCCBCCABxr;@.+*-!)/zxABCHHBttAtCHBCCBCBBy^;+.+*-~FrsyBCHGCCAxtACHHACCBAty/;+....+&#=;'){FrsHuEELtsrF{)'-$@.......&#>;!)]/rxGuEEvtK^]{~'-#+......+&$,;'{F/zsvuEEDAz^]{'!-=@......+%=-;~)]/zyGEEEvxzr/]);-$&...",
+".+#;)/zyvLELLGCCCCCCCBGGLEuGxr;&.+$!(/KtLEEEGCCBCCCCCCBLEEDHxr~&.@$!{^KtLLELLCCCCCCCCBvLLuLGyr;@.+>;{/stLLLLLCCCCBCCCBCLEEuGs^;@.+>;]/KGLEELLCCBCCCCCCGLEELHs/;@+*)^zyvLELLCCCCCCCCCCGELuvxzF)$++=)/zAGLELGBBCBCBGCCABAtABCCsF-+.+%-!{rzyAHGGGGCGvBBCGGvvtsr]-&..#-']rsACCBGGCAtyABCCBCCCCCAs{>++%;~]rsACCBCCCBtytBBCCCBCCAAr~&..*;)]rsACCBHHCBAxBCCCCCCCCBAr;&.+#;)FzxCCCBHCAtyABCBCCCCCCCxr;@....&*=;!{F/rzxHLEuEBxzr/])!-#......&#>-~{F/rKyGEEuLByz^/]);,#......+*>;!)]^KsxGLEEGtsr/F]);>#......@=>;~]F/zzxvLELuAszr/]);=#...",
+".+%>;(2<ONNNwJKKrIrIKKJwwNNOI}-@.+%-!(25ONNNwJKKIrIrKKJwNNw8I1-+..#-!1[6YwNNwJKI/QrIKKJwNNw8I1-@.+#-'1[8wNNNwJKIrIrIKJJwNwwJQ(-+.+$-~:Q6wNNNJJKIrIrKKJJNwNw8Q(,@+%;|[5ONNNwJKIrIrIzKJwwNNOQ})-%..%;_[5ONwNOKKKJwwwwwJzKrKKJJ<_>....%$-')]}QIPJwwwwJJKPI5<21_-$+..&>;)}IPJwwwwJIrIrIKKJJJJKzQF'*.+&,-~}<PJwwwJJKKIIrKKJJJJsKQF-@..&=;~}IPJwwwwPKrrIKKKJJJJsI/],+.+&=;([IJJwwwJJIrIrKKKJJJJKz^{=+...+&#*-;)1F[Q<JNNNw8I[}F|~;=#......@*=-!)12[[IONNNMPI/:](~;=#.....+@*=-!)(F}[KOSNNwPI^}](~;=#.....+&$=;!(]}[Q5ONNNOPQ}}]_~;=%...",
+"..%=;(}5YNNNOPI[2}}2QIPMNNMO<1-@..%=;([5YNNSOII[22}[QK8YSNN8<:-@.+%-'125MNNS6PQ[2}}[QK8RNNS6<:-+..#>'1[5MNNYOPQQ2}2[QIPRNNR6<|,+..%-~|[5MNNSOKQQ2}}[QIORNNR8[|-+.%-(}<ONNNOPIQ}}}2QI6ONNS8<}_;#..#9(2<ONNRO5IPOSYSSYYP5IP8R85($+....&#>-9~|:[QPJJJPQ[}:(_0,$#+...%,9_25OYwSYw8PKIIIIPOwwO6<[|!*..%=;_26OYYSSY8PPPIKPKOwYOPI[(-@..&=3_48OSYYwYJ5KIIIPKOYwO6<[)>+..#=9(48OSMSNY8PKIIIPJOwwOP<[(=....+%$>-_(}}[Q58SNNR8IQ[}1(;-#+.....&#>;_|2}[<58SNNR6<QQ}](;,&.....+%*>;_|:[Q<5MNNNM6<Q[21(;,&......@*>;_|}[[Q6MSNNY6QQ}}1(;=&...",
+"..%>cbfiqUTUqmlkfee4kimqUTUqif9@..%,cb7iqUTUjmkkfee4hhmqTTUjie3+.+%3ae7mqUTUomh74ef7hlpqTTUjhf-+..#-0bgjUTUUjpk7eee4hloqTTUjgb3+..*9afhpWTTqomh4eee4hloqTTUjgb,@+%cd7iqUUUqmkkfef4hhpqUTUqigd3%+.#;bgiqTTWjmmjWUUUUWWopnpjWqid>.....+&#$,cabfhmmVmikfbac3>*&.....@>;dgjWWUUUWopmmmpmnqWWZni71c*..%,0bgjWWUUUWqmmmmmppqWWZjl7b3+..&=cbgqWWUUUWqnlmmmnoZWWZni4b>+..#,0bgqWWUUUqopnpmppoqWWZph4d>....+&*,;defkh5mqTTTTjmkh74ba-*@.....@*=9de4khlpqTTTqjlhh4fba-#.....+%*,;de47klpqTTTqjlhk74ba-#......&*,cde47klpqTTTqplh77eba3#...",
+"..%,abhmqUUUqXll7444lVXZUTUqm7c@.+#,a:hXqUTUZnVh444kllnWTTUoVec@..#3_ehpZTTUnXVk74kklVnWUTWoie9+.+%3d4ipWUTUZXVk447klXnWTUUom49@.+*;b4ljWTTWoXVk447hVXoUUUWome9+.*;e4moUUUZnVl7447lVXqUTUqmkbc#..*cehjqUUWZmmoWTUUUUWZnnXoUqjb,......@#$,cdeklVXXXlhed03=*&+.....%=9bgoWWUUUWZnnnXnnoZUWWoVhec#..%3abiZWUWUUWoXVXXnnZZUWWnmke9@.+&,;bioWWUUUWZnXXXnnoZWWZnVkb,+..%9cbioWUUUUZonnXnnnZWUUZoVkb,+...+#*3ab4khVlnqTTTUoXVlhk4dc=&....+%*3a:7kllVnqTTTWoXVlkke(9$+....@#>3ae7klVVXqTTTUoXVVkked3$+....+#=9_e7hlVmnqTTTZXVVVkkea,*+..",
+".+*!]rstvEELDHCtysyxACHLEEEDHs)%.+=']zyHvEELDHBBsssxCHGDEEEGAs)&.+=!FzyHvEEEDHCAxsxtCCGDEELGAs~&.@=~FzAHDDEEvGCAssxtACGDEEEGtz!@.+='/ztvLEEEGBCtssxAAHvEEELvtz'%+=_rxAvvEEvGCtysxxBCGDEEEGHxr{=++>(rxGDEEEDGHvDEEDLEDGGGCGDDwe,+.....@#,c)/sxBHGvGHxzF~;,=@......%-_/xHvLEEEvGHCGCHCvvLLvGByr)*.+*;]IsGDLLLLLGCBCBCHvLELLGCxz'&.+*;_/xGvLDLvvvBCCCCCGvLLvvAy/-@.+*!1IyDLLLEELHBCCCCHvLELGGAs/-+..+&=-!{rxtACCHvEEEEvGBCBxs/)3%....&=-'{rstCCBGDEEEEGBCCBxs/)-%....&$-~FzxAACCGvEEELvCCCAyzF~-&....@$-~FztACCBGDEEEEvBCCAyzF~,@..",
+"..$;)/zyAGHHBBAyszssABAAHBHtxz)&.+*;{/sxBACHACBxsssytAAAHHHtyr~&.+=;{/stAHGGCCtyszsttABBGHHBs^~@.+$!]rsxAHHHBBtxsssyAACAHAHts^'@.@>;]rstHHHHCBAysssyBACAGGAty^;@+$)^zyAHAHBCAysszyAABCAHHAxs^)$++>)^sxtHHBCBACCBCCHBCCBCBCHtsF-......@$-;)/sBHGLvvtsr{'->%+......&>!1ztCCCHGCCBCCCBCCCHHCAyz/'*+.#;)/sABCCCCHCCCCCCCCCCCCBxsF;@.+%;']zACCCHHCCCBCBCCCCBHCByzF-+.+*;)/stCBHCHBCCCCBCCCBHCBAyz]-+...@*-;)rzyABBACAHHHCABtyyzF),%....@%>;]/zyABCCBGGGHBCABxsrF)-&....+*-;]/syBAACHHGGHACAAxsrF',&....+*-~]rsttBBCAGGGACBBtysr]~>&..",
+"..@=-_(:}[[[Q^/F]{]]F2[[[Q2}:_,+..&>;~(}2[[Q[^/F]|{]F/[[2[[2{_-...%>;~1:[[[[[^F]{(]{F[/[[[}2]~=+.+%>;_1:}[2[^^/F{{{]/[^[[[[2{~>..+#,;_11}[2[[^FF{{]]2[[[[[[}{~=+.%-~(:}[[Q[//F{){]F2[^[[[2]{',&..%-~1:}[[[[//[^QQQQIIrIrQ^[}_-%.......@*,;(2IJwwwJQ:_;-=%+.......+%-;(:}[Q[III^rrQ^Q/QQ[[/:(~>@..+*,;(2[/QIQQrQQ^Q/QQQQ[/2](!$...@=,;_:[[QQIIQ^Q^I/Q^QQ^[2F(;$...&*,!(2[/IIIIQrQrQ^QQQQQ[F:)!*.....@*,;~]]F/^[Q[[[[Q^/FF]_!-$+.....@#=;))]F/[^QQ[Q[Q^//F{(!-*+.....+#,;){]F/[^2Q[[Q^[/F]])!-#+.....@*-;)]]F/[Q[Q2QQQ//FF])!,&...",
+"..+#,-!_(_(||(__!!!!~(((|((_!,*+..@%=3'~((((|(__!!!'__(|1((_!-%+..&*-;!___|||((';;!__((((|(~';*...@*-;!_((1(((_'!!!'_((((((_';*+..#*-;'_((1(((_'!!!__(|_||(_!-%.+@=-!~_(|(1__!;;!!~(_((((_';;$+..&>3;~_(||(__((({|||:::::|(_;=@.......@%*-'(}<668<:~;,$%@.........@*>;~)(|::::1111:|:||((_~;-#+..+%*-;~_(1|1:|:1]|1{|||__~';-%+...&#-;~(((::1::1:|1|1(|(__!;-#+..+#*-;~((11::11|1:||||||(_~;>%.....+&#=;;__(((||1|||1((~~'-=@.......@*>;''_((_||1||(((_~';-$&.......&#,;;__((|1|1|||(((_!;-#&......+&*--!__((|||1(|((((~~;-*&...",
+"...&*>33cc0;0cc9-9939c90cccc9=&...+%$>,9900c0c99-333c9c909cc3=&...+&*,39cc;c0;9-3--9cc009ccc9,#....&*,33c90090c933333000c9c9-=&...@%=,39;9c09cc93339ccc0c0;c-#+..+*>339c090099--3999cc09093,=#+...#,-39cc0cccca00a0c~a_aac0c3%........+@#*9;abbffba3,>#&+..........%$,39c!a_aaaa0c'0a0!0cc93=&....@%$>;c9_aaaa~aa~c00000cc9-=&....@&=3-c0cc'a'a~a~0cc000c99,*@....&#>>;cc0aaaa~cc0'0000c099,=&......+%$=,330900~a0;0cccc-3=*&.......&*$=333c0c0~a000;9c;3,=#+.......@%$,339cc0c000aa0cc33,>&+.......&%>,3939c0a0000!;c993,>%@...",
+"...+&#=,,3,3,3>->,=,3,9,3-3,,=&....@%*$>-,>-3,-,>,,,,3,-39,,>=&....@#$=,-,33>3-,>>==>>-3-3-,=*@....@#*=,,9>>-,,,,>,=,>>>--,3>*&...+&#*=,,9-,-3-=,==>,,-,3,3==%+...%*=,-,,-,>-,$>>,,9,,3-,,>**@....@#>>-,-,99399999999933-9,>=#+.......+%%*=3-00a003,**%+...........+%*>-933339;939333;93-,=**&....+%#$,>3-33;993999-3-3-,=>*#@....+&%*>--939-39c9-3--3>-,-=*#+....+&#>,,-3-3;999993--3,9>-=$%+.......+%*=>,,-33399939-,=>$*&........+&#$$=,-,-33993339,=>*#@.........&***,>-333-3333-9,,>$*&.........&#*=>-9--939,-33,-,>$%&....",
+"...+@%$->---------,-------->-$&....@%=>=---,-----,>>-------->*@....+&**-,----,-,>,=>->-----,>#@....+&%$>----,---,----3----->>#+....+&#$>--------,-->------->=%....&**>------>,==>----------#%+....@#$>--->----;;;;;;;;;;;--,#@.........@&#=-;;!cc;;-*%&............+%$>--;;;;-;;;-;;,----,$#&@.....@#$,--;-;;;;;;;;-;-----==#@.....+%*>---;;;;;;;;-;-;---->#&......@#$>---;;;;;;;>-;-;>--->*#+.......+#$>-----;-;;-;----=#&+.........@#*=,------;;;---->=*%@.........+%#$>---;,;;;;--,-->##@.........+#*>-,--;;-;-;---->=*%@....",
+".....+&%**%*%*%#%**#%#***#*#&@.....+&&%***$**#%#%%##%*$###%*%&+.....++&%##*#**%%#%##%****%**%@.......++&**$**%*%**%**$$**%%*#@......+.@&##$*%*%%%&%#**#**#**#&.....+%##$$***%%%%##%*#$*##%%%+.......@%%%*=*$*$#$*=$$>>====*%@...........+@#$==>>>>##&+..............+&%**$$===$=$*#$>*#*%%@++.......+&*%#$$***$==*#=$=$**%%@+........@%#*=====$*$$>$=#**%%&+........+&#**$*$=$##$=*#=$$*%%##@..........+@%%*#*$=***$$**%%@+...........@%#**$==$**$*#**%#@+++.........+.++%%##*$$$$$=$*%%%&+............&%%*$==$=$===$#%#%&+.....",
+"...........+..++..+.+.+++.++............+.+.+.+++....++++...............+.+.++........+.+..................++.+..+.......++.............+....+..+....++++.+.............+..+.++.+..+.++++..................+.+.++.+..++++..................+++++++........................+++...++..++.++..............+...++.+.++.......+..............+++++......++..+++.................+.++...+...++.................+.++.++.+....++....................+..+.++..+....................++........++...................+..+++++.+.++.+........",
+"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................",
+"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................",
+"..........................................+@&%%%**%%&++++..................@&%&@+...............................+@%%%@+.....................+@#****&+.......................+@&%%%%%%&++................................................................................................................+@%%#*%%@+.............................+@%%%%@+....................@%%****%@+............................................................+@%%&++........................................................................",
+"...................++...................+&=-;!))~{)'!--,-=$&+..........+%*=-~);;--=*&+.......................%*=>;''!-,$%&+.............+%**=-~({~);->*&+..............+%*>>-;!))))'~!-->>*&+..................................................................................+++.+@&&@+.............+&*-;!')!;-,$%@+......................@%$,;;;~!-,%&+...............@#,!))())'-,*%@+...................+++..............................+%*>>;;';-=*%@................................................+%*****%&+...........",
+"......@&%%&%##%%&%#%%%&@+.............+%#>-;))F/^//F])))'!--#@.........%>-;)]]]~)';--%+....................+%=>;!)]]])~-,*&+............&$-;!]/^//])~;-*%+............+%*-;!))(F/FFF]]))';-=%+...........+&%&#%%%#%&%%@+....................................................+@%#*###**==###@..........@$-;~]]]{)~'-,*@.....................+#>-;!)]]]~;-*%.............+#$-!)F/r/F])!-,*%@..............+&%##*%%%##%%%@.....................+&$-;!))))';-=*&...........@&%&&@+...........................+@#$>--;--*##&@........",
+"......@&%$#%**#$*#*$#%%&@.............+#*=-;)_]}/}F]_()))!-=*@.........&*-;~{{1{)~;-=#+....................@%>=;;'{]]~'->$&............+@*=-;)F//}]{';-*&+............@%*=;!)(]1F}]({_()~;>*%+..........+@%#%###%*$#%%@+..................................................+@&&#%#*#*$$=>**%%+........+&*=-!){({()!-=*@+..................+&&*,-;!){{)!->*%+...........+@%*,;)]}/}F();->*@+.............+@%##%#%%%%*%&&@+....................@*=-;!)({{';,*%@..........+&###$%%&@@........................&%*>,--;-,$##%@+.......",
+".....&%#$*>$**>**=$*$*##%&+..........+&%$>;~(:}[2[}}:1}1(~;>*@........+&$=;(1}}1}('->#@...................+%#=-;~(1}}1!-=*&............+#$,-~|}Q[Q}|(!->*&+..........+&#$-;(|}[[[[}21::1(!->#@..........@#$$$*#*$**$$$#%@................................................@%&#$*$=>,>---->**%@.........@#>;_|}2}21~;>$#@..................@&#*-;~(|}[1_;,$#@...........+%$,-;_}[QQ[:(~;=$#@............+&%#**$*$$$$$$$#%&+..................&#$>;'_(}}1(;-$#&.......+&&%*$$$*$*#%%&......................+&*>,--!;;-,=$#%@.......",
+"...+&*$*>=333333,-3,,>=*$$#&+........&%*=-0de44hhh74f44fedc-*&........+&$,cbe7774e~9-*#+..................@*$-;adbe74ea9=*%...........+&%>-0df75kh7eba;,$%@..........+%*>9abe4hhhk7eeeeeba-=#@........+@%*=,,,>=>,-=,>**%+.............................................+@%$*=>,,-3cc90c0c>=*&........+@#=;de4774e_c,*#%@................+&#$=;0dbe47edc3**@+..........@#*-cab4khkkeedc3$$%@..........+&#*$,-,==*=,-==>**%@+...............@%*>3a~be47eb0-=#&+.....+&$$>,,3>,>=**##@....................+%*=,;0ad_dc93=$#&+......",
+"...&=,3cc_a_d~dd_dddaa0c3,==*&......&%>,30_e7klVVlllhhhk4|a3=%........@#=9'e[lllhkea93*&+................+%$30d|e4klhedc3*#+..........+&>3ad:klVVVlk4:dc,$%+........+%*,3a14kkllVVlhkkk4edc>*@........+*$,;!aac;cc0aa993>*%..........................................@%#=>39ccadd(bbbee1da9=#+.......&$=99b4klllk7d09=*#&+.............+&**3c_be[kll4e_;3=#+.........+%$-0d:4klVVVk4ed03=>#&........+&*>>;0~aac;c!aa0933==#+...........++&#$>ca(e4kkVked03=*@.....%*,3ca0_daac9-,=*%+.................+&#-0dd1ef4ee(a03=*#+.....",
+"..+*!{F^zssssssxsssssz/F]{)!-$+....+$-')]/zxBCBCCCBHCCBysrF~-=+......+%>-']rxBACCAyr]);>#&+.............@%,;)/zyyBACts/{'-=@.........+#$;)/zyACCBCCCxs^F';>#+......@#,;~]/sACCCCCCCHHCAyz^);,%.......+#>-~Frzr^/F/rzz/F{)!-#+.........@@@&&@+.........+@@@@+........@*-;!){F^rssssyyAABBxrF!-&.....+%$-;)]rstBCCAyz/{~;-=#%@+.......@%#=-;;)FrsxAACBxr/{';,*@......+&*,;)FzyACCCCCBAyz/])!-=@......+%*-;)]rzzrFFF^zzrF]{);-#+........+&$=,;;~]rsytBCBts/]);-#....&>-)]rzssssz^FF{~;,$%+..............@#,;)/zstBBHAAxsrF);-*&+...",
+"..%-~FrsxBACCBBBBAABAAyz//]{;-%...+#=;)]/rsBCCCCCCGvGHBxs^{~;>%......@#=-!{/zxBCBAyz/]~->*&+............#>-~FryABCBtyz/);-=#+........@#,;]ryABBACCCABxz/]~-$#+.....+*-!{/ztACCCBCBHGGBtsrF)-,#.......%*-;)/ytyzzzzxAysr/F);>%........&%$>==*%%@+..+@%%#*$***&......@$>;!)]/zsABBBBACCGLDGy/{;$+....@*>;~)FrzyAACCBxr/)!--=**&......&*=--;;){/yBCCBBxsrF)!--=%......%>-;~{rsABBAAAACBAsr/]);>#+....@&*,;']rsyysr/zsyyyzr/]);=%......@%#*=-;;~{/stCBBBAsr/{~;-*+..+%-!]ryBABAAAxzr/]~->*%+............@#=-!]rxBCGGLLHCAs/{';,*@...",
+"..@$;)F^IzKsKsKzKzKzzKr^F1(~;>&...+&*,;)1F[rKKJJJJwwwJQ2F)~-=*@......@&$>-;){/rzKzQF|';>>#&@..........+@#>,-)FrKzzr^F|);-*#@.........@#=-~F^rKrrrrKzr[]_~;,$%@.....+#,;!)]/IKzPJJJwwwI[]_!-=%@......+&#=-']^r//1FF/rQ/]()!-*#+.....+@&#*$$>$*#&@@@@&%#*****#%+....+&*=-;~{F/QrzKzKsJJwwwO<|!>%....+@%$=-!~{]F^KzrI/F{~;-,>*%&......@%$==--!)]/Izzrr/F|)!;-=*&.....+%*>-;~]/rKrrrIrKzr[F]);->#+....@&$=-;)]/Ir/F|F/rr//F|);-=&.....+&#*$=>-;;']/rzKzr/F])!;-$*+...&=-)F^zKzKzKr/F|);-=*#&@...........@#=,;']^IJwwwwwKK^]~;-*#@...",
+"..&$-!(2<PPPPIIIIIIQQQ<<[7}1c-*...@%$-;~(1}QIKIPKPOOO6[:(('-,$&......+&#*=-'(1[QIQ[}|~;-**#&@.........@&$*,;~|[II<[}1)~;,>%&+.......+&#$,!|}Q[[[}[QQQ}:(~;>*%+......&=;'(|}[IKIPPPOOO<}(_;-$%@......@%$$-;|[Q[}:|2[QQ}:(~;,$%@......+%#>----=*#%%&%##*>--->>*@....+%*-;((:}<<IIIIKPPOYNSO<1;,#....+%$=-;'~((12QQQ[}|(~!;-,>$&+....@%$=>--;'_|[QIQQ22|1~_;->*%+....@%*,-!_|2QQ[[22QQQ[}:(('-=$&...+&#*,;!_:[Q[:|1|}[[[}1|(!-=#+....&#*>=---;!_|}QIQQ[[1|(_;-=*&..+%*-!1[QIPPP5Q[}1(~-,$$#%@.........+&*>-;(1<P666O86J5<}(!->*@...",
+"..&=9abgmqoqnmlhkhh<khlpjqjgbc=+..@*=9abb47kklliVmoqopgfebd0-=&......&%$>-;abe4h<k7eedcc,>*%@........+#*>,90be45h<h4fbdc9,*%@.......+&*>-0:4hk7747hkh7ebdc-*#@.....+#=;adb44hllmVmoojifbac-$%+.....+&#=>;ab7h744f77<77ebdc3$#@......&*=9caa09=$*$#**$39aaac,=@...+&=,cbefgmjjphkhmoqWWWWqifa3#....@#>9!adb:ee44kk44eebdda;33*@....&*>-caadbbe7kk<774ebbbdcc,$@...+%$=90dbe7<h74444h<7ffebd0-=@...@#>,;adbe7hkf:bb44h774eed0-*@...@%$,9;aa_dbb:77<hk47febbd09,%...#*3c17hhnnoph77ebda;>=**&+........+%=3cdbgioopigimjoigbda3$&...",
+"..#=;_ehnZWZoXVVllllVlXoWUWpgb,+.+%=-a:4kklVVVlllmXnXVVkk74|dc#.....&%=,39d:4klVVVlhk4eda93$#+......@*=,-c_e4kVVVVllk4e(d03>%+.....@#*>3abekllVlllVVVk4:ba93*&....+&*,c~b}7lVVXXXXXXXl7:ba9,*&.....+*=,ca14<llhlllVllk4eba;,*@.....&%=9'bb1ba09,=>>33;abebd0,#...%*30b4klVoZZnmllXZZUUTUqp7b0$+..+#>9de4k7kkkkk7k7kkkkk74e(a9*...@#>9a|44kkkk<kkhhkkhkk742ba9%...+*-c~e[khlVVk744klllQlkk7:d9#...&*30de4k<Vlh74e4kllVllhk7:a9%...&>9ad|e47k7kkkllllIVlkkk4410>+.+&=3aeklmnZWoXVkk44bd0c3=$%+.......&*,0de4hmZomlhlmoZni4ed0>%...",
+"..*;{^stDEELDCCCCBCCCCHLEEEGs^!@+&=;{/sBCCCCCBAtxABCHBHCCAAsr{=+..+%=-;~]FzsABBCCBCCAysz^F);-%.....&=-!){/ryABCCCCCCCxsz/F)!,&....+#-;~{/zxtCCCCBCCBCBysr^{~-*+...%>;!{/rsxBCCCGBGCGHAxsrF{!-$+...@*-;~]/zxACBCBBCCCCtysz^]~;#.....#-;)^zsszrF{)~~))]FrzssrF'>+..*-)FzACCHGLEGHCHHDEEEEEDGsrF-#..%-!]rxBCBCCAAysyyBBBACAAAsr],+.+$-!]zyBABCCAAyxxABBCCBCBAyr],+.+%-~FzxBBCCCCAyssxCCCCBCAtsr];+++$;'FztBCBCCAxsssxACCACCAtsr{=+.+$;{/zxBBBBBCCABABCCCCCCBAAs^;@.+*;']ztCHGLEvHCCBtysr/F{)!;=&......*-']^zxBvLDHBACHDLDBxzr]!>+..",
+".+$-)/zAGGLGGCCABBABBABGGLDGA^'%+%,')^yBCBCCBCByttBCCCCCCCBxr{-+..@*,-~]/rsACCCCCCCCCCAysr]);$+...@*-!)F/rsACCBHHCCCBCyysrF~;=+...%,-!)FrzyCBCCHHBCCCCAysz/{!>%..@#-;)]rzsACCBCCCCCCBBAysr]~;>&..+#=;~{FrsABACCCCCCBCCAyszF);$+...+$-~]ryBBxzr/F]{]//zsABAs/)=+..*;)FstBGCvGvHCCBCGLLELELtxrF~#..#;~FsBCCBCCCAsssytCCCCCCByz{-@.&$;)FsACCCCCBAyssyACCCCCBBxz{-+.+*;)/sCCCCCBBysssyABBCHCCBxr{;%.+>;{/sCCCCCCByszsyBCCCBCCByr{-..+=']rsBCBCCCCBBAAtBCCBCCCCByz~@.+*-~]ztBCvLLLGCBCCAxszr/F)!-$@.....%-!]rstAGGvCBBBCLLGAysrF'=+..",
+"..#,;~:[IJJJJIrI^^^^[QQPJJ6KQF-@.@*-'(QJwwwwJJrQ//QrPJwwwJK[{;*...+&=-!(F[IKJJKKKzKKzKJKIQ|~-=+...@#>;~]}[QKJJJKJKKKKKPPK[:!-*@...&=,;~|F^QrKKKKPKKKKJJPKQ|~;=@..@#*-!(F[/rKKKKKKKKKKJJKI[|!-=@..@**,;)]}^rKzKKKKKKKKJJPI[('-#.....%=-~2IJPKQ2F|))(]}QIJPJQ1;*...&=;)[PJwwJPPIIIIKPJwJOOJPQ})-%+.&,-)[KwwwJKr[FFFF[IKJwwJK^]~=+.@%,;([JwwJJKr/FFFF^IKJwwJK/1!=...%,;(QKwwJJKr^F]F2^IJOwwJK[]!=@..%-;1QJwwJKKr[F1F2/IJwwwJI[{;*..+#-']/IJwwwJJzQ//F^IKJwwwJKQ(-+..&=-;{/QKJwNNwwwJJJJIQ/}]);-*+....+&=-!)}[QPJPIQQQPwwOK/}{~-#...",
+".+%*-'_:QKPPQQ[}:1||11}Q5PIQ[1;@+@#=-(<6OwSSY8<[}}[[<6OSYO<}(c#...&$>;_1}Q5JOPPQQQQ<I6OOY54~;=&...@$,;(:[Q5OO8IQQQQIIPOYO5}~3=&..+%*-'|}Q[QIQQQQQQQQIPOR85}_;=&..@%>-'|[QQIIIIIQQQIIK8MMO<:'-*@..@#>-~|}QQIIQQQ[Q[QQP8OYO<1!-$+...+&=3(78OOOI<}:_((2[QKOOO5:c*+..&=c([6wSOP<[[[}[[IIIPPP<Q[2_-%..&=3_[8OJOP<Q}(_(|}[5JYSO5}(!*+.+%>3_46OwOP5[:(_(11[KOYYO52(;*+.+%>,d[OYwOP<[1(((|}[8JSYO<}(;=@..%=;(<6OwOP<2|(_(|2Q5YYYO<}(;*+..&>;_1<8YNYO<Q[:1|}[IPOYSY5}(,+..@*=-~(}<5RNNSwSwSYO8Q<[}|~-=&.....&*=;(11[QIIQ[[[5YR8<2:(;>%+..",
+"..&=90bfimpXik77eb:bbbfgillhhfc@++#>cbgqWWUUUji7ff44hpWUUqieb;%...&=-cbf4hmoqomk7khhljqWWjgbc3&..+&*-ab4khmoZomh4khhmpZWWjgbc>%..+*>3ae4<hllhhhk7khhmoWUqjgdc3&..@*>;ae7hlhllkhk7khhmjWWqpfdc>@..@*,;df7<hllkhkkkkkhpqWUqifa9=@....#=9dgpqjommh7bee4hipoqjib3%...%>0bgjWUqpg477774hhlmmlik74b9%+.&=3dgqWWZp57ebadd1fiqWWqm7bc*+.+&>9dgjWWqnk7ebad1b7iqUUqi7bc*...%>cbgqWZqmh7ed0ddbgmqWWqieb;=@.+%>cbiqWWomk4bd_abegpqWUqiedc%...&=9abgjqUqqik4:bb47hpqqWqiea>...&*>-abfgiqqUWqqqqqqmllh7fbc3&.....@*,;def4hihhghijqUjifed0-#...",
+"..#,cdehmoZoopnlk7444e4hmVVVhk0%.@*,ceiZWUUTUZmhkkkhlnWUUqpkba*+..#,0(4hVVnZZomlhkllXoWWUoifd9*..@*3cb4hlXXZZnmhkklVVoWUWoiedc#..&*3017lVlVVVlhkhhlVVoWUWjhea3#..@=3a:7VVVVVVVhkhklVmqWWWjhba3#++&=3a:klXVVVVVlhkhlVXoUUWjgba3%...+&*9cegmnmnnmlhhhmXonmmigbc#+..%9afioWUWpikkllVlllVmmVVVVkea*..&,cbioWWZnVke1bdbf7pqUUZj4ea>+.@$>cbiZZWZnVk4|bdbfkmZWUqmhba$+..%3cbioWWZnV<eb(beekpZUUZpkba=@..#,0eioWZZnVkeddbbehmZUUWpgbc*...%=9abhmqqqnVlk7447hVmooZjif0$+.+&$3cde4hmoqqjjpmjjmXXVVVk41c*....+&$3!b4kllXnnpojqWWolk4e_3*+..",
+"..*!]rxADEEEELLGHAAysssyBCBCCx]*@%-c]KtDvLEELvGCABBtBvEEEDHsr]=+.+=']zxHHGGGvGAtyxAACCLELGAsr{=+.#-~]rxHHvvDDGAAyxyACGvEvvAs/(>+.%;)FzAHGGCCCCtyttBBCGvLvvAs^)=++#;)/ztHHGBCCAAxxxtACGDLvGts/)*..#;)/stHHHCCCAAAxytBHvDEDGAz/'*...+#>;_2rsyGDEvGBAHvDLGAys[{'=+.+=)/stLLELGAtACCCCBCBCCBCBCBs/,++*!{rtvLLDGCts^/^^zsHEEEEvxz]-@.@>'{zyvLLvvCtz^//rzxHvEEDGyr{-+..*!{KtGvLGHCtzr/FrsyGDEEEGyz];%.+=!{rtvDvGHBxs^/^rsyHvEEDHxr]-...%>;(^sACHGCHCAysstACCHHHHxr(>...%,;)/zytCBHHAyyyyAAACBCCCBsF-....+*-~]rytBBGvvDLEEEELHtxz/);&..",
+".+$;]ryHLLEEEELGCBtxszsxABCCBy]#+&>']ryCCCCHGCBCCCABAHLLELAsr{=+.+>)/zAHLvvHCtszzzssAABHGHCAz{-+.%;)/sAGLvGHCxyzzzssxACHHHBxz]>++%;{/sAGDLGCBtsszssxxCCHHCAyr{=+.&;{/sHvLLGCAAszzzsyABBHHCAyr(>++*;]rsHGLLCCAAsszzsxAAHHHCtyr)=...+#=;~]^zsAGLLGHCHDLLvAsr/{;$..+>_/KAvLLvHyABACACBABCCCCCCAs]-@+=!FryCCCCCAyz/]F^rstGELLHyr]-@.@=;{rxCBCCCAsz/{{FrstGLLLtsr],+.+$;]ryCCCCBAyzF]]FrstLLEGHyr];%+@$!]zACHCCCByr/FF/KsALLLLtsr{=+..&=;)FzyCBCBCCBtyxyCCBCCCAs^)=+.+#,')/zsABCBAyssszsyBACCCCAy^-@...+#-)]zsACCCHLLLEuEEGHByz/{;*..",
+"..%,!1[IONNNNNwJKQ/FF{]/^QrIr[~%+@*-!]QKKJsPKKKKKKr[QPwNwOP}(;*...*-(}PwNwwI[/:()(]{2QKKKKIr}_=+.@,;(}PwwwJI[F]()({]2QKKKKKQF_=..@>'1[6wNwJK^F]{){)F}QKKKKIQF~*++&-!|Q6wwwJI^F]()({]2QKKKKI^]'*..%-!:QJwNwJI^F])))(][IKKKKI/1'*....@%=,;~1}<OwwwJJJwSYP[}(';=#...#;_2<OwwwPQ/rKzKzrr/rrIrKIIF~#++#-!|[KKJKI^2]);;~(:<OwwO52|!>+++*=;([KKJKI^}|~;;'(2<wwNw5}(;#...%>;1^IKJrI/:{';;'(25wNwOP}|!>@..%-;|[KKKKQ/}('!!~{25wwwOK2(;*....&$-;(F/^IPJJKI/^IJJJKI^2]~-#...@*,;)F2^IKKr[2]:F:/QrIKKzI^{=+...+%*-!]F/rKKJwwwwNwNOPI/2{~-&..",
+".+%=;(1<6SSNNYJ5[221__(|}[[[Q[!&+@#=c_}QQIIIIKIPP5Q[[5OSS8<|_-%..+%-0:5YSS6[:('!';;_(2[QIIQ[2(>+.@=9_15OwY6[1(!;;;'~(}[QIQQ[}~>+.&=;_ePRNS6[1(_!;;;~(}[QIIQ[1~=+.&=;(}6RNR8[:__'';!_12QQIIQ[1~$+.&,c(46SNY5[1(~;;;;_(}QQIQQ[:;*.....@#=,3!125OwYOO6OY8<1(~;-*&...%-01<YwSO5[:[QI<Q[2[[QQQQQ[:;*..@=;_}[IQQQ2__;--;;1[6SSO<:_;%..+&=9_2[IIQ[21_;--;c1<8SwO<:~3*+..%>c_}QIIQ[}(~---;_1QOSSO<1_3*@..&,9(}QIQQ[1(~---;~{<OSS8<|~-%....@&*-;_(:}<OOP<[[<PO8<}1('-=@...@#=-'(}[[QQ[}||(_||2[QQQQ[}~=.....&*,;_|}}QIIK66O8OO65<<[:'-%..",
+"..&=;aegjWWWqX5hfebbdadbe47khec&+@#=;afkhlhhhmppopi47iqWWjiba3#..+*9afiqWWjgba;---3;ae77hhhkfd>+.@$cdfiqWqpgba9-9-90ae7hkhh7fd=+.&=cdfpqWqpfb_c-3-30de7khkhkfa=..&,cbfjqWqifba;3-33cde7khhhkf0$+.@,cbgpqWqifd0;9--3cde47hkk7ea*.....@#*,9cbfhioqWUqjpigba0->*&..+%3abgjWWqi7f7hlh74ff74hkhh4e0*.+&=3afkhlk7edac,--9dgjWWqieac#...%=9afkhhh7eda3-,9;bgjWWqgea3%...%=cdfhhhh4:b03,>9abgqqWjgbac*@.+&,0d4hhhh4edc3,,-0bijWWjiba3#....@%#=3aab7ioqmi4hioqpgeda9,#+...@#*9abe4hhhkfbbbdbb47khhhkfd>.....%*3cdb47hhlhhg7gimpjjjpgd3%..",
+"..#,cbflnWWWoXlh4ee1bbb4kklllka&.@*30|7lXVVVVVnZonmhhXZUWZifdc%...$0d4pZWWjhbac33,39deklllllkb,+.&,cbgpWWWphba99>,-c(ekkllllkb,+.&,ab7pWWZj7ba99>-99beklllVl7b>+.%30egnWWWp7b~93-,3cb4kkllVh7d>+.#9aegoWUZpfdc9,3,9adekklVll4d>+....&*>30d14hmnWTUUoXVk4bac3*&...$9demoUUZphkklVll[4kklllllh40*+.&=cd4lVVVh4e0;339abhoZWqm7dc*+.+%,;b4lVVVk41ac39cabioWWqmfdc*...%3cb4VVllk71ac3-caeiZUWZm7d9=+..#3cb7VVllh4|dc-39abiZWWoifd3%....+@*>-0db4poZnVllmoZohedc9=#....@*=;a14klVVlk42ee44klllVll7b,+...+&=-ab4kkVVVl744<hVnZWWqif0*..",
+"..*;)/stGLLvvHCtxzrr^rrxtBCCBy]*+%,!]rtBBCCCCHGHHHBHHvLEELts/)>..@-{rsHLELvyr]);;;~)/ztAABBCtr'@.%;]/yHDEEGyKF)!;;'{^stCCCBBx^;&+*!FIxvEEEGyrF_';;~{^stCCCCBy/;@+*_FKxDEEvtJ^]_!;;~]^sACBBCAy/;@+*~FzAvELLHK^(~;;;~]rstCCCCAs/-@...+*-;)FrsxBHGEEEEGGHAsr/{~-*...>)/zAEEEEGCCBCCCAyxxACCCCCts]-++*;{FsBBCCAyz/(~)(/rxDEEvtJ^(-+.@$;{^sBCBCCyzF{'){/zyvEEvHs^{=+..*;{rxBCCCAtz/(~~)FzADEEDts^)-&.+$!]rxBCCBByz/(~~)2zAEEEvtK^)=....+@#=-']^ztDvGGGGGDDHxIF);>#....#-;)/zxACHHHBtyyyxAAHHBCCAsF-+...+*3~FrytACCCAxssxAHHLELvtKF-+.",
+".+%-~{rsACCCCBtyzrr^^^zsBCACBx]#@&>!]ryCCCCCBCBCBHCBCHLLLLAs^(=+.+-F^JtLLLvxz/)~;'~]^sACCCBBA^;&+*!]rsGLELvxz/)~;'~{/sAACBBCyr;@.*;FrsGLLuGyrF)!;')]rsBACCABx/;@.*;FryvLELHsrF)!;'){rsBBBCBBx/-+.='FKxGLLLAsrF)!;!)]ryBBACCBx/;&..+%=-~]/zxBGvvGvHvGvvCxsr/);*+.+=)}stLuELGCACCBABxyABBBCCCBsF,++*;)^sBCCCBxz^])){/KAGLLLBs/{=+.@=;{/xBCCCByz/])){/KtvLLLts/(>+.+$!{ryBCCCBxz/])){/KtLLLuAs^{-&++>!]ryCCCCtxrF{)){/KAvLLLAzF)>+.....@*-')]KyBGGLELLGHts/{'->%+..+&,;)/zyBHGLGCBCBBCCGGLvGCyzF-+...+*;~]zsBBBCCAysssxxCGLEDHsF-@.",
+"..@=-;(2IKJKKr^FF()))(]}rIrKr['%+@*-!]/KKJKKIrIKKPKPJwwNww52(;#..+=;([8SNwO<11~;--;!)/rzzKzI^],++&,'([6wNw8Q})';-;;'{/rKzzKr/(>+.&>!1[6wNw6Q1(';--;']/rKzKzr/)=+.&,!1<6wNY8Q|);;--;~]/IzKzKr/(>++%-_:<JwwwP[|)';--;']/QzKzI^/_$....&*,-~{2[IOwwJPKJwwwJQ/1)'-%...%9_:<ONwwwKKKrr^^/F^rKzKKrQ}'*+.%=;)/IKKKr/F|)';!):<ONNw52);*+.+%=;)/IKKKr[F_~;;~(:KOwwO5}(;#...%>;)/IKzK^/](~;;!(2<wwwOK}(;#+..%>!{/KKKK^/|)';;!(}PONwOI2_,%......+@*=-'(}IPOwNNSJ<[]_;=*%@...+@*,;):/QJwwwJJzzKsJwwwwJI[{-*.....&>-~]FQrIKr^F:F:F[KOwww<2_=..",
+"..@*=-~|[IIIQ[2:__~;!_1}[QQ[Q};@.@#=c(2QIP5Q[[[[[QIIJOwNS6<:_-%...*;_:5RSR8[:(~;;-;~(}[QIIQ[2~>+.@=c_15YSM821~!;;;;~(}QQIQQ[1'$+.&>cd}8RSY6[1(!;-;;_(}QQIQQ[:!*+.&=c_28YSR6[|(';-;;_1}QIIQQ[:'$+.&-;(48RRY5}1~!;-;!_|}QIIIQ[|!*+...@#>-'(|[<ORO6QIP6RY6[}1(;>#...#-~1<OSNSJPIQ[[[}::}[IIQQQ}1;#..&=3':[IIQ[}|(~!'c_|[PSSO5:_-#..+#>-~:[IQQ[}(('!;'_:[OSSO<{~-%+..&>;_}QI<Q[}1~';!~~1<OSSY<|!-#..+%=;_}[IIQ[11~~!;'(b<OwSO71!-%.......+%$=-'(}<6SNSY5[:_;,$#&+....@*=-!(|[5OSYPPKIIQPOSNS8Q:_;#....+%*-;(:}[QQQ}1|(1|:<OSSO5:'$+.",
+"..@*,;dbglmih44ebd0_adb7hh5k7b9&+@*=cb4hmmlh7f4777hiXoWWWqgea3#..+%9aeijWqjgfbda~aadb7h5llk7ec*..@*9afiqWqjgfba~aaadb7hlm5k7e0*+.@=cafiqqqmgeb_a0aabb7hlli<fec*..&=3afiqqqpgebaa!aade4hll574bc#.+&>0bgpqWqigbbaaa0ad:7hilk77d9%...+&*,;ab7gijqoihhimqjpi7fba>#...%-cbgjqWqomlh774eee4hillkhfd3&..&=3abkhhh74bbdaaabbijWUqiba3%...&>9a:7hhh7ebbaaaadfgjWWqiba3%...@=3abkhhk7eeddaaabfgoWWjiba-%...%=3aekhhk7f:daaaabeiqWWjgba-&........@*=-caegpUUUjieba9,$%&+....@#=;abfgpqWWomllhlmpqqopfba,%....@*=3cdee7hh<7ebbbefgjZWqgfc$..",
+".+%>30d4mnonmlk74ee:e47hmnXmhe9@.+$3abhnnnplhk774klVXoUTUqifdc*...$9abinoZnVk74e:1e47lpXoml7ea*+.@=9afioZonlk44e1e24khnnnmh7ba$..&=3dfioZoXVk7e:e1e47lpnnmh7ba*+.@=9afmnooXlk4ee:eeekinonmh4bc*..%,3dfmoqonlk4e:1eef7lXnnmh4dc#...+*=3afimXnonnlkkhmnonmmmgbc*+..&,0bgpZZZoooml<k7Q7hmnonVked3%..&$9'ekVVVVhk4eb1eehioWWqpfd9*+..&=9dekVVhlhk4eeeef7mZWUqifd;#...%>9_ehIVlll74eee:f7mqWWqifc3#...&=c_ekVVllhkeee1efhmqWWoifa3%........@$>9a(e7pWUUqi7:d03**%@....%=9ab4klpWWZonXVVVVXmpigfac,&...&#=30(4[lllVl<kkk77hmZWWqmfd>+.",
+".+*;~]^svvELGHBtyysxxABvDEDHxz~%@&,!]zxDvvDHCAxyxxBCGDEEEEts[)=..+=!:rxHCHHCCAtyyxxAAHDDLvts/_>+.&-~]rxHvHHCCAAyxyxAAGvDvvAs/_>+.#-~|IxHCHBHCBAxyxyAAvDLvvtz/)=+.%-'FzyBGCHBCAyxxxttBGvvDGtz^)*..&9~FztHHHBCCAyyxxAtHGDLDHxKF'*..+%-;{ryvvvvGHtysssxCHGvvGGK],+.+*c)/sAHGGGLvvHAAAAAHGvDDtyrF;$..%,~]rxACCCCAAyssxyAGDEEGtK/~=+..%-'FzyACCBCAyxsxxtAGEEEEtIF~>...%-)FzyCCCBCAtysyyAAHDEEvyI{;$+.+#-)/zyCCCCBAxsssytBGDEEvyI{'$.......@*-!)FrsAGuEELAxz^]~;,*%...+>!{/syAHGEELGBCCBCCByssQF)!,%...%>;~FzxtCCCBCCBBBCCAvDEEvGKF-+.",
+"..*;)FzxvLELLCCCBBABBCHvLvvGs^~&+%-']IADLLGvCByyyABCHvEEEuAsF)>+..$;(/stCBCCCCCABBCCCGvLGGyz/)=+.@>!{rsABCBCCCABBACCHGvLvGxzF~$++@>;)/ztCCCCCCABBBBCHvvLvHyzF'*..@>'{ryBCBCCCCABBCCCCHvDvHsz];*+.&,;]rtACCCCCCCBBCCCCGLvGtsr]!*...+$!{/JGDvHAxszrrzsytHGvDAK{=+..*;)^sBCBCvDLGCCCCCCGLLLLAs/{;%+.@>;]^sAABBCCCABABCCHGGLDyr]'>+.+&>;{rsBBBCCCBAxACCCGvLLDyr{!$+..%,!]ryAAACCCBBABACCHvLLvxr);*+..%-']rxAACBCCCABABCBGvLLGxr{;*.......%>;~]/stHLLEELHAsrF);-,#@..+=;]/sBCCvLLLHCCCBBAxsrF{)!-$+...#-;)FzyCCBCCCCCCBBCCvLELDts/-@.",
+".+&>;(:<OwNNwwJJJPJPJJO6O6P<2~>+.@=-~15wNNNwJKIFF/^IPwwNNY52(;%...&$-~1F[^QKKKJPJPJJJJP6P<}(!-#...@$-~]2^QrKKKJJPJPJJOP6P<2(!>&..+&*-'(F[^QrKKKJJJPJJ868P<:(;>&...#>;)]2^QrKKJJJKJPJJPP65Q:_;>@..+#=;~F}^QrKKKJPJKJJJJJ6PQ:_;,&...+&=;_}[5<<[2{_'~~)]}[<5<4(;*...&>;)2rKKJJJJJJJJJJJJO685[:);=+...&=-)]F^QKJJJJPJ6JJJJJJP[_;-&...@*=-)]/^IKJJJJPPJJJOOJ6P[(;>&...+%=-)]/^IKJJJJPJJJJJJJJK[_;=%...+*=;)]/^KJJJJJPJJJOJJJ8K}_->&.......#>-;~:QIJwNwNNOPQ}(~;,*@....*-~]QPJJwNwwJKKrQ/2{~~;-==*@....&*-!([IJJwwwJJKrKKJJwNNwO5:_$..",
+"..%=;(2<OSNNSSwYwYYMMMO8<Q[:_-*..@*-a1QOSSNNY8<}1|}[<6MNSO<1!-#...+#=-;_(|2[IPOOYYYOO85[}|(c-$+...@#>-!(|:}QKPOOYYYYY65Q[2(;-*@...@*=-;(_:2QKPOOOYYOO6<[}|_;-#+..+@$=-!((1}QKPOYYYYYO5Q[}(~9-#+...@*>-'((12QIKOwOMYY86<[2(_;>#+....@#>3a(1:((';;->--c'(111_c-#+..&*,'([QIIPK6OYSYSYYO8<[2|~-=%+...&*=-'(([5OwYYYRSRYO6<<[|~-=&....&*,-'_|}POYYRMRYYY8P<<41'-#+...+%*,-'(:[6OwYYYRRYYO6<<4|;,>@...+&*=;~(|[6wYSRRRRRROP<<}('-#+......+&,;~(158MRNNNNMY5}|_;-*@...+%-_:<OYSSNSO5<[[1(!c-,=**#@+....@$,c|[8YSSNMY6<QQ5ORwNNSO7|;*+.",
+"..%>0b7ioWUUUUUUWWWUTUqpg7ed0,%..+*-cbiqWUUUWjgeb1e4hmqWWjgba-%....&$,90_(e7hmqWUUUWqmhfbd09*%+....&*,90_be4hmZUUUWWWph4fd09=%+....%*,-0a1e7lpZWUUUWqigbbdc3=%....+%*,9a_bekhpWWUUUUqigfba0-$&....+%>=9cdbehinqUUUUWqmgffdc3*%.....@%*,;00aa;3,>>$==,30caa03=%...&*,cbf7hhmmnoWUUWWqoi47bd9-#@....@#>>9adfioWWWUUUUWjmh4fd0,$&....@#>,;abfpqWWUUUWWWjihffac=#.....&*=-9abepqUUUUUUWWjih4fac=$&....&*$-9abgpqWUUUUUUqjik7fa3=%.......+#,cabfiqWTTUUTTWjgeda-=@...@#,0fiqWUUUWjmk4eba;3==$#&@......@*-;bijWUUUWqph7hpqWUWWWjgb3%..",
+"..%,0ekmoWWUUTUUTUUUUTWph4edc>%..@#30fiZWWUUWoifebf7hnZWWqibc>%....+%=-0adekVpZUUUUWZph4edc->&....+&$=-0adehVnZWUUUWZjh7eb09>#.....@*=39abfhVnZUUUUWZmh4bdc,=&....+&*,90ab4kXnZUUUUUZm7f1d9,=%.....@*,-0d14kVoZWUUUWZmh4edc,=&....+@#$,9c00c9>=*$$$$,,900093*&...&>>017llVXnXZWUUUWWomh4edc,$@....+%*--cdfpZWWUWUUUWZmhkebc,*&....@&*,30d7pZWUUUUUUWomkked9=%+....@%$=9cdgmWWUUUWUUUomh4ed9>#+....@#*39cb7oZUUUUUUUWoVh7ed9=&.......@*3cde7pZWUUUUUUUoiebac=&...@>9aepZWUUUWZVlk4ba3>$$%@........@*,cbiZWUWUWZphhlpWUWUUWqgd3#..",
+".+*;{rsAGLLvLLEELELELEvtxKrF)3*.+&>;(/yDvELEvvyz^^rsxHvLDGs[);*....+%=-!)]rsAHDDELEDDtsI/F_;-#.....@=-;!)FrstHGLELLLvHxzr/);>#.....+*-;!)FrxAHGLDEELGtJr/]_;>%....+&$-;~)FzsBHDLEDLvvtsr/{~;$&.....@$-!)F/zyAGGLLELDDAsz^F~->&....+&#=-;''';-->=***>,3;!!~;;>%...%>;)^sBCCCCHHLLLLLLHtsz/]'-=@....@%$-'_]rxDLLvLLLELHAtszF~-$&....@%$3;(FKALLLEELLLLGBxsrF~;*+....@#>-')FzADLELLLLDDGBAsz]~-$+....@#>;')FKADLLvLLEELGAysr]',$.......&=;~F^sAvLLELLELvHxz/]);%...@,;{rxDELLLDGtAszF);-$#@+.......+%>;_/sGvLLLDGHttAADDLLLDHK:-#..",
+".+*;~FrsxBABBBBAHABBBtwsrF])',&..+*;):rsAttAtsz/]]F/rsytxs^{',&.....+%=;!)FrzsyAAAAtxsr/{)!-=&+....+&$-;!)]/zsABABtAysr/])',$@......+#>;;)F^zsAABBAAsz^F{';,%+......+#=;')FrzsABAAtAyz/F{~;,%+.....+@$-'){FrzsABBAtAysrF{)',%+......+&#--;;--$%%&&##=>--;;->#+...@=;'/zytAtytBCBBBAtyz/F{~;,#+.....+#=;!(FKyABBBBBtAysr/]);,#+.....+#>;;(/zyABAHBBBAxsr/F)!,%+.....+%-;!{2zxAABBBBBxyzr/])-*&......@%>;'{^zAABBBBABxyzr/]);,&+......@#,;)]/KytBAABAtysr/{);>%...@$;)1ryAABAtssr/])!->#+..........@$-'{rsAABBAyszzzsxAAAyxz/)-&..",
+"..@#=-;~)({|{|11:1|((()a!;-,*&+...@*,-!_(((((_!;---;;~_(_~;-=#+.......@*=-;'~)((1(((_~;;->=&@........@&*=--;'~)((((((_!;-,=%+........+@*=,-;'_((((((_!;--=*&+.........+%=,-;~)(({(((__!-->*%.........+%*,--'~_((((((_~!--=*&.........++&&&%&@@++..+.@&#$***%@....+&>-']FFFF{){{{{{(((~;;->$&+........@#$,-!_({({({((__';--$&+.......+@#=,;!_(((11{_()_!--=#%+........@*=-;'()(({{{1))~!;-,*@........+&*=,;~(((1111((_~;;-=%@........+&#*=--!)({|{(1(((!;-,*%+...+@#,;!_((((((~!;--=#&+...........+&*=-;_()(|(((_~__)((((~0-=%...",
+"...@#*=--------;;;-99---,**%&+....+%*=>,--,3--,==***=,,-,>**&+.........+%#*=>--;;3---,=**%@++.........+@%*>>>------,,,,**#&............+#*=>,-,-----,>**#%++...........@%#$>>,---9,-->>**&+............@%*>>,------>-,>=*#&+.........................++++++.......@%=-!___!;--->---,--=**#&@..........+&#>>,,--------,>>*%&@..........@@*$>>--------,,>=*%+..........+@%**,-----;----=>$$#&..........+@&%>,>----;9---==**%&...........+&#*==-3-----3-,,=*#%......+&*$>=-----,->**#&&..............+%**>>---------,3----,==*#@...",
+"....+%#*$*>*>,>>,,==>$$***#%@.......&%*$$=$**$$######***$$%&@...........+&%***>=>>=$>$*&&@+.............@&#**$>>$>==$$$*&@+.............@&%##$*>>>$>*$*&@+..............@&#**$$**>*$=**&@+.............+&&%**$>$>$>$***%&@.........................................@*=39333,>=,=*$==$$*&&&+............+@&#$*>*=$$$$*=**#&+.............&*$$$>**$>>>**$*%@..............&%*$$====,>*>***%&+............@@%*$$=>>=-=*>**#%@.............@%#$***,*,$$*$$*#&@+........@%*$$>$$>*=$*&@.................@%%#***$>$=$>>**=$*$=*$%@....",
+"......@&*$*$***********%%@++.........@@%*#****%&@&&@&%&%%#&+..............+@#*##$***%*&+.................+++&##%**##**%++..................+%#$****%*#@+.................+@&%$$$$*$*##+..................+@@%%#*****$*%%@..........................................+@**>>,=$>$$***##**%&+................@&#***$=****#&@++..............+&##*****###$*%&@@..............+@%**##*$#**##&@@...............+&%#*#***%$$%$#@@+.............++@%#$$**$=*$*%&@@+..........+&%*%**%*#%@+...................+@&#*$*****#*$*****##&@.....",
+".......+@%&%%%%**##%%&@................+&&%%%&@+..+..+.+@&@.................&%%&%%%%&@+...................+.+@%%&%%&&%&.....................+@#####%%@+...................+@@%######%%+.....................&&%%&%&&%%&&+...........................................+%*$$*%%%%**#%%%%&@+..................+&%#%%*%%#&@+...................@%&#**%%%&%%&+.................@&&%&%&%%%%&&+...................+&%%%##&&#&&&+................++&&&&%&#%*##@...............+@#%%#%&#&+......................+&&&%&%&%&&%%&%&#%%&+.....",
+"..............+.+........................+...................................+.+................................++++++......................................................+@&&&&&&@...........................+.+...................................................++++++.++++++..+......................++++.++.........................+.++++..........................++..............................++.......+.......................+.+.+.......................+..+............................+.+.++++..+.+..........",
+".............................................................................................................................................................................++++++++........................................................................................................................................................................................................................+..................................................................................................................",
+"...........................................................................................................................................................................+@@@@&&@&+.......................................................................................................................................................................................................................+++.................................................................................................................",
+".........+@@@@@++.................................+@+@+........................+@++........................................................................................+@%####%%&+...................................................................................+++@+@+..................................+@@@@++....................+@@@++++....................................................++@@+++.................................+@++@++.........................+@+............................................",
+"........+&%##**#&@+...........................+@&%%**%%@...................+@%%*#*#&+.....................................................................................+%$*,,>,=>$%@.................................................................................@%##**#*%+..............................+&####%##&+.................+@***$*#&@+.................................................@&*#*$#%@+..............................@&#****%%+.................+@&%#%**%@...........................................",
+"........&=--;;;;-$#&+.......................+@#=--;;;;,$&+...............+%=--;!;;;>$%@...................+&#******%%&@..................................................+#-;'~{){)!;=#+..............................................................................+%=--;;;;--*&+..........................@%*=-;;';--$#@..............+&==-';';;>##@..............................................+&=>-;;;;-$#@...........................@&*>-;;;;--*@..............+@#=-;;;;;-=%+.........................................",
+".......%$-;!)]])!-,=#@....................+&#*-;;~)]]);>*@..............+%>>;')]]]~;,*&................+&&#$$-------->*%+.................+@@&&&@+..++...................%=;'{F/^//]~-$#+.............................................................................@*,-!~{])!;-=&.........................+&*=-;)]])~;-=#@.............@*,;~)]]]~;-*#&................+@&@@@@@+@@@.................&$>;!)]{);-=$+.........................&*$--~)]{~!->$&............+%==-;){]{)'-$%+.................+@@@@@@@&&@+...........",
+"......+&#=-;!)(~;,=$%@+...................+&#*=--;~)~;-*%@..............@%$=-!){_)'->*&................@%%#>=->>,,,,->=#&+..............@@@&&&&&@@@@@@+.................+%*-;)(F[F});,*%+............................................................................+&*>-;!)();,=$%+........................@&#*,-'))';-=$#@............+&*$-;')_)'-=$%@..............@@&@&&&&@@@@@&++..............+%$=-;~_)!-=**@.........................@%$>-;)))';-**&+...........+&*=,-~)))!;>*%@................++&&&&&&&&&&@+..........",
+".....++%*$-;_((_;->*#&+..................+%#*$>;!~~((',$#+.............+&$=-;~~(1(!-=*%+..............+@#*>,-;;;-;;-;-,=*#@............+@&%#%%%&&&%&&&@@................@%*,;_1}}[1(;->*&............................................................................+&#$>;'((_;-,=#&+......................+&#$>-;_((_;->*&+............@%*=-;((1(~;-**&+............++&%%#&%&&&&&%&%@@+............+%*>-!(11~;->*#+.......................+&#*=-'~|(~-,>#%+..........+@%$>-;_(|(~;,$#%+..............+@&&##%&%%%%%%&@+........",
+"......@%*>-cdbbda;9,$#@+................+@%$>-cadbbb_a-*#@............@%#$,;adbeebdc9=*@.............+&%$=,9cdddadaaa0;3=$%+..........+@%$$$=**$$#$$##%@+...............@#>3cdb4774ba3>$#+...........................................................................@%*>,;0beba!93=%&+.....................@#*=-;abbbd!3>*&.............&#>,;abeebd0;>*#&...........+%%#$***$###$#*###%@+...........@%*>-cbbeda;9,*%.......................@*>>-cabbb_c-*#%+..........+%*=-;0bbebbc9>*#&.............+&%#$$$$*####*$$#&+.......",
+"......@#>=cae[k4ee(ac=$&................+%=,3'be4444ea9>*&............@%*-ade47kkke(a9=*&............&*,30~bf7ggkgg74ebac=$#+.......+@%=,3ccc93=,33cc3=#@...............@*,cd47kkk74bac=*@...........................................................................@%$>90b4444e{ac3*%@...................&*=30db14k4ea93$&............&*>3ab47kkk4eda3=*&.........+%*>-cc093,,,3cc-3=*$&+..........+#$,ca:474eeda9$%....................+%*>9aa|e4k4ba9>*&@.........+%*>9d:27kkk41d93$%@...........+%*,39c'c3-,,-c99=#@+......",
+"......@*-;)FzxCAAAsz]~-=%+............+%*>;~]rsACBAsrF'-,#+..........@$-;)/sACBABAAsr]!-=@..........&=-~]/zstGvvvvGHAAsr]~;=%......@#=-;){/^rF{)){F//{;-$@............+&$-']ryBCCAyszr]!>#+............+&&&&&&%@@@&&&&&%@+.................+@&%%%%&%&&&%@+...........@#-;~{^xABAxyz/);-#%+...............+&=-~{/zyyAAAs/);-#..........+%=-']rstBBCCAAs^);-$@......+@#-;~{F/r/])))]/^F)~;-$%+.........@=-;)]zxCAAxsr]!-#.................+&*,;~]rzyABByr{~->*@........+%>;!]rxBCCBBAsr]~-,#+.........&*-;)]F/^^{)))]//F~-$&+.....",
+"......&*,;~FzyACBBts/);-*@............&#=-!]/zACBBtsr]~;=#+.........@#>-']rxBBCCCCAxrF~;=#+........+%=;)/zsAHvDLDLDvCCyz/);,%+.....@*,;)]/rzzrF]]FrzrF~-=%+...........@#>-)/zxBABAyyzrF~-$&.........+&##$$>>>$*$$$*>$$$**%+............+@###*====>,=>>$**&@@.........+#>-!)rsBACBAyr]~-,*#@..............&*-;)FzxyBABtz/);-%+.........+#=;)]zyBCBCBCCxz]~;,%+.....+*>-'{F/zzr/F]F/zz^])!;-$&.........&#-;~FzyBCAByzF)-$%................@*>-!)/zytBBAsr{'-$&+........&=-;)/sBCCCBCAyz/);,#@........+#,-~)F^zsz^F]F/rz/);>*+.....",
+"......&%$=-!{/^rrr//(;,=*@...........+&#=,-~{F/rKr^F(~->$@.........+&#=,;~]/rKKrIrr/1);>#&+.........&*-!):[QP8JOOOJJKI/])!,*@......+%*-;'(]F}F{_){{F]);>*@+...........@#=,;)F/r^r/^FF|~->%@........++&#*****=$*##$***=$***%@+..........&&%**>>>>=>>=*=$*$#@@.........+&*>-;)F^rrrr[]);-=#%&+............@#*,-!)F/rrrQ/]';=*&..........@#*,;)F/rIKzIrr/F);-$%+.....+#*,-!){F}F|_({{FF{)';,>#%+........+&$>-~]F^rrr/F~;>*&................&$=,;~{F/rr^/F~->#&.........@%*=-!(/QrKrIrI/F);-$#+.........@*=-;')]FF]_((]F1);=*&+.....",
+"......@%#>,;_(2}[Q[}(;->#&+.........+@%#$,;~_:2[[21_~-,*%&.........+&$>-;_(2[[[[[[[}:_-=$&+.........&$=;(:1[[<<<PKP<Q[1(;->*@.......&*=;'(_:1:|(|(:1_!-*#%+...........+%$=;~1}2[[Q[}(~;-*#@........@%#*$,>,,>,,===>,,>,,>=**%+.......+@&#*$>=,,--,-,->,>=*$&+.........@#=-;_(:2[Q[[1~!;>*#&+...........&**=-;~(}[[[2}|~;,>*&.........+&#>,;_}[[[[[[[[2|~!-,*&.....+%$,-;~_|}::(((|111(~;-=*%+........+@#=-'~112[[}|_-,$&...............+&*>-;~|}[[[:|~;,*#+.........+&$,;'(1[[[[[[21|~->*@..........@*>-;_(|::|1((_|(_->#&......",
+"......&#*=-cdb:e7hk4ba;,*%@..........@$*,-0d:f4474eda9,$#@.........+&*-;0dbe474f4e74eb0,>#@........+&$,;def4747khhhk4fbd;>$#@......@#=-cabbeeeeee1eeba;>$%+..........+&*>-cab44<kh4ebdc-$#+.......+%*$,399c9c;c9999c9c;c93>*#+.....+&##$>,3cccc;c00;09cc3=*%@........&%$$-;ad1f4kk7fbac9=>*&+........+&%*>,;abb47<ke:ba;,>=%+.......+&$*,90bf74444477f:bac9=&.....@%$,90abbeebbbb:febbaa9>*%@........+&$=-0d1ee774edc,*%...............@%*-cabe4<74bd0->%&+.........@%$,;0be4744ff4eba3>*&.........+@#=-cabeefeeeebeda;=*@......",
+"....+&%=>c'dbe4<lVlk4|a93$*@........@*=-ca1e4k<lkk4ed!9=*%.........&$,c_be4kkkkkkkkkked;,*&+.......@#=cd:4kkkkkhVVVlh4eda9=#&.....@#=3ad:e4kkk7k77774:a3=*&.........+%#=3cae[khllVlk4dac,*%.......&*>3cadb(bbddddd(b(bbd(ac9=#.....&**,39c_dbb:b1b:b(bbda03>#+......&*$=3cd1eekhlVhk4:dac3>$%.......@#*,3;a_b}kklllh4e1dac3=%.......@*=,3a(4hIlhkkk<hk4e1d03#+...+&$=3a(:ek4kk[474k74ee(ac>*@........@%*,ca|e4<llh7ba3>#+.............&*>3;db2klllkedc3=$&..........@%=3~be7klhkkkk4e(03*&.........+%*>9abe4kkk77744ed9,$&......",
+"....&=,;)]^zsxtBCCHAAsr]);-$&......@*-~{/rsxACACCBtxr/{;--%+......+*-!{rzyACCCABCCCAts/{!-$&......@#,;{^sACCBCCCCHGBCAsrF~;,*+....%,;)FrssAACHBBCCCAxz/);-#+.......+%-;!{/rstCCCHCBAxz/{~->%+....+$-!{/zsssssszszssssssssz^]'>@...%>-')]F/zzssyyxsxyssssz/{!,&.....&$-;~]/rsyyCCHBCBAyzr/]);-%.....@*-;~{F^zsAACHCCAAxsz^]);-%.....@*-;)]/zyACACABHCBBAszrF);%...+*;']/zsyBBBABABBCBxyzz/]'-*+......@*,;~{^zytACBCxzF);=&............&$;'{FrsxtCBAAz/);-$@..........#,;)FrstBCABBBBys/{;-%.........&*-!)FrstCCCCBByxz^);-%......",
+"...@%=-)]rsyBGvGLGLHCAs/{!-,#+....+%-;]/zsAHvGLvGHCByrF);-$%+.....&=-)FzyBBGGLLGvHCCBy^{'->*&.....@=-;{/xtCCCGLLvLGHCCxz]);,*&....&-;)/zyBCCvvGLGCCCAz/);-$&.......@$-;]/zyACvvLvLvCByzF)!-=#@...&=;)/sAHGGHHAAAAABHGGGGHAxr];#..+%-;)FrzstCHGHGGGGGHGGGts/);*....+#=;~]/zytBGGvGvvGHCtxzr/);$+...@#=;']rzstCGvGLLvGGHAyszF);*....@#,;)FrsACGvGLvLGvvGCBys/{;*+..%>;)FrxyBCGvvLGLGHHCAAsrF~-=%......&*-~)/rxGGLvvGHs/);>#...........@*,;)FzyHGvLLGHs^{!-#@..........%-'{/zxBGGLLLGHxz/);=#+........&=-']/zxBGGvGvHAyz/);>#+.....",
+"..+@%*,;{}QKJwNwNNNwJKr:);-$%+....+&=-)][QKwwNNNNwJKI/_;->*&+.....&*>;(/QIJwwNNNwwwKr[{'-=*&@......&*=;)/IrKwwNwNNNwJK/}~;,$#&...+@*-'{/IKJwwNNNwwJJr[('->*&......+@*=-'][IKJwNwNNwwKI/('-=$%+...@=-_}POwwNNwJJzKJJwwNNwwJI[{;%..@%>-~|F^IPwwwwwNNwNwwNNY5}~-#....+#*-;(F[IPwwNNwNwNwwPI/}]~-%....@#*-!)}/IPwwwNwNNNwwJKQ/1'-#....@%=-;1/QIJwwwNwNNNwwJKI^]'-%...%*>;(FQIPwwwNNNNNwwJKI^});-*@......@#>;!(25wwNNNw6[_-=*@...........@#=-!(2IOwNNNwJ[|'-=&..........+@*-!)1QPwNNwNwJQ1~-=#&.........@%>-'(:[6wNNNNwP[|~->$&......",
+"...@&$=;(}[<8OSSwSSOOPQ}(;-=$&.....%=3~1}Q5OYSwwSwOP<[(!-=$#+.....@*=;_}[Q5OSwSNSOOPQ[|~;-$#@.....+@*>-_:[QI6YSNNwYO6KQ:(;-=*@....@$>;(}[<8OSSNSSYOP<}(~-=$&.......@%=-_1}Q5OOSwSSYO6<[1';-*%@...@*-015OwRNSYO8KPPOOwYMRY65[:;*..@*,9_1[Q<6YSSRRMRSSSNNNY51~-%+...@*>-'1}[<6OYNNNwSwYOP5<Qe(;*+...@*>3!:}[<6OwSSNwwSSOP<<[:~;$+...@$>-~1}<K8MNwwSNNSSY65<[:~;%...&=-!|}Q<KORwNwSNNSRPP<Q[|!-=&......+&*-c~1<8RNNNN64_3>#&...........+&>-;_|<8YNNNM64~-=$&...........@*=-;(15OSNNNR52(;-$@+.........+%*-'_(}5OSNNNR5}_;-*&++.....",
+"...@#*,;de47iipppmpmppmgbd0-*&....+%=;a:7hhipppppmpppied0;=*@....+&$>cae4hiipppjjpppmgfda3>*@......@%>30|f7h5ppppjpmppi7bd;3*&....@*>cde4hipppppppjppheda3=#+......@%=-ae44hippppppjnmifba;,*@...@*3cbgippjjjoqqqjXppppppppmga$+.@*-afgppppjjjppippjqUTTqjed9*....@*=9dgijjjopjppijjpompppgfc=+...&*-0dgijppjjpjipijjjXppigbc=+..+@*,!bgipjojjjjpijjojopppgb0=...%>9afippjojppippjjojjjpied9,&......+&#>9cdfipqqTWjgd9>#&...........+&*-30dfimqWTWjgac,*&...........@#$-90deijqUUqjea9,$@...........&%=-0abgijqUUqpba;,*&.......",
+"...&%>3a14klll5hilhmooomked03*@...+#-0bekkllhh5ihmXZomhe|ac>&.....%*,cb4k<h5hih5kmnZoXkeba9=%+....+&$>cd:47khhhhhilXoZXi7edc,&...+@>3a17kh5h5hihimoZom7eda9*&+.....&*>c(4kll5hhh5hmnooph4bdc=&..+&=3ab7lVVVXmqWUWWnmlhhhmmoqib>+.%,0bgpqZZomlggf4ghVjUTTTjgb0$+..+%*90bioZWqnmmhihhVVXZZWZngb3%..+%,9dfpoWZoXm5hhghhVnoZWZpgd3#..+%=3afmqZWonm5hghhlmnoZWqngb,@.+%3afioZWZnm6ihghlmnoWWWoiea3#.......@*=-9abfgjWUWqgb03=&............@*$,cabegpWUUjgbc3*&...........+&*,,0abfijWUWjgb;=*&...........@%$=3;abfipWUWjgb9,$&.......",
+"..+#,;~]zyAACCCBBABBDDvHxsr/~-%..+#-'FryBACCBBtBBBGvDGAyz/{;-@....*-'{rsACCCABABAHHLDvtyz/{;>&....&#-;{/zxtBCABBtABGDLDAxzr]!-@..+%-)/zyABCBAABBBAvEvGtszF);$&...+&=-!]rsACCCBBABBAGLDGAyz^]'>@..#-~FrxBCCCCCDEEEEGABBBBCvEEwQ-&+*!]zADEELGCAsI^rzsAHuuEuuV/{,+..@=3~FKtEEELvCAAyxABBGDEEEvAr(=++#-!{/sGEEELHCtyyyyBAGDEEEvyr($+.&=3)FsHvEELGCBtyxAACCLEEEGyr'@.+=)/stEuELvHCAyxAAHGvEEEvxK]'=.......@#>3'(}rsGLEuvyI{!-$+...........@#>-'(FKsGuEuDy[{'-*@..........+%=-;!|2ryGuEEvs^{;-#...........@&=,;~|/zyvuEuGK^);-#.......",
+"..+*>;)/ztBCABCCABBBGLvGAxz/);*+..%-)]ryABBCCBBBACHLLGBss^]!-#...+*;!]ryBBBBBABBBCGLLGBys/{!-#+...&$,;(/zyBABCBABBCHGLGBAsrF~-#..+*;)FzyBBCCBCBABHvLLHAxz^);-#....%>-~]rxAABCCBBBACGLLvBxsr]!-%..%;)FryBACCCCvEEELvCBtAABGvEA^;++$)FztHLLLGAsr/FF^rsxtuvGtz/)>@..@$;)FKALELLCCBAxyyACCLLELvtz]>++*-~]rsGLEEGHCBAxxyACHLLELGtr{=..&>;_/KGLELGHCCAxyACCCGLLuGx^;&.@-)/JALEELHCCBxyxBCBvELEDAzF'$+.......@$-;~]^JtLEEvxr]'-$&...........@#*-;)F^stLLuvyr{!-=&..........+&*=-;)]QsGLEuGs^{!>#+...........+%>-;)FIsGLEDHs/{!>%.......",
+"...%=-;(F^QrKIKKPKPJwwwPQ}]);$&...&=-~]FrIKKKKIPIPwwwOI[F(!-*&....&=-!(//rIKKKIPI8OwwPI2F)!-=@....@&=-;)F/QrIKPKPIPOwwOP[/]_-#&..+%=;~]/QrKKKIKIPPwwMJQ[F);,*%....@*=-!(/^IrIKKIPKPOwwOI}F|'-=&..@=;_]/QrIKPJMNSNwJIIQrIIJMY81>+.%-_:<JwwwP[}(!;;'_(:[[4[2(!-%...+#=;~26SwNwJKI^^Q^IKPwwwNO<1;*..%=-'1[8wNNwKKIrQ^QIKPOwNwJQ|!#+.@*,;([6wNNwJKrI^[/IKJJNww8Q|-+..*;(}6wNNwJKKrQ^QrKPwwNNO51_;%.........&*>-!)25wNwOQ|!->&@............@&$>-!([8www8[|!-$#@...........+%#*=-'(}6www6[(;-*%+............+%$>-!|[6wSSP}(;,$@.......",
+"..+&*=;_:2[Q5688Y6YRMNM5[:)!-$&..+%=-;(}[QI66OM8MMMNM6[}|(;=*&....%>-'(}[QI686M6YMRNM8[21_;=*&....@#*-;(1}[Q868R6RMRNRO52|_;-$@...#,-_|}[Q58OY8OORRNR5[:__-=#@....@%$-;(}[Q<8O6Y6YYMNM6<2|(;=*@..%,;_1}QQI66MRNNNR8KQQQ<<8MM5b=..&-!(<8SSO<}(_;;-c!_d|:21(~;-%+...#=3a15RNNOPIQQ[QQQI5ONNS8[(c*..&*-c_e5RNNOPI<QQQQQQPOSNR84(;%..+*=9_f8YNSOKIQQQQQQQ6OSSY84~,+.+%;_b5MNNR65QQQQQQIIOMNNO<1'-%.........@%=,-'1<YNR84_;-*#+............+&%$=9_15OSR64_;,*%+............+&$>,-_|5MNR8e_;>$&+............+@**--a15YNR5e~->%@.......",
+"...&>,cdekhimqUUUTUUTTqjgfba3=&...%=;0b44iioqUUUUUTTqjgfbd0,*&...+#=3ab77imqWUUTUUTTUjgfbac>*&....+#*-0bekhipWUUUUUTTTqifbbc3$@...%>;de4himqqUUUqTTTUjgeba9=*@....@*=9ab4ghijqUUUUUTTTjgfe_0,%@..&-0be4hhmoqTTTTTUjmhkhlmjWqib>++&,0bgjWWqigbbdacaadbbefbbac-*...+#-cdfjqTTqpl5lkhhllmjUTUjgb9#..&*-0dgpqTUqpVlmlhllhmjWTUjgd;%+.@$-0dgjUTWqmll5hhlhlpqUUqpg0,+.+%3afiqTTWjVh5hhhllloqTUjiba3#.........@%*>3cfijUWjgd9-*#@.............@%$=-abiqUqjgdc-*#@.............@#*,3afiqUqpf09,$#@.............@$$,9afiqUqpea;,$%+......",
+"..+#=caehlmnoqWqUqUqUUqjh7ed9=&...#,0b7hmmnoqUUqUqUUUqik4b!9>%...@$9a|7lVmnqWqUqUWWUqji7e1c3>&....&*3cdehmmXoqWqUqWWWUqph7:a9=&..+*3ae7lVmXqWqWWWWWUqjh7ed;3*&...+&*-0b7kVmoqWUqWqqUUWolkf1a9>@.+*0(4<VVXmoWUTTTTWoXVXVVXqUqpf,+.&-abhpWUqnlh742eeee477h77eba$+..@=30bgjUTTWoXlVXVVVlXoqUWjiba*+.&=3cbgjUTTWoXnXXXlVVXoWWWjgb0#.+%=30bgjUTUZnXVlVlVVVnqUUWp7b,@.+$cdgjqTTWoXVVlVlVlXnZWUqifd;%.........@#>3cdfpqUUjiba9,*%@............&#=,caeiWUUogbc9,*&@............@*>30dgpqUWjgba9=*&+...........+&*=-abgpWUWjgb09=#@......",
+"..&>!{/stGGLvDDDDDDDDvDGAys^{;*..+>)/ztvLvDDDGDDDvDvvHBxsrF'-=@..%-)/sxDDDvvDDDvvvvDDGCtsr]!-#...+#-~FzyGGvLvGvDvvvvDvvHByz/)-%..@-)/stvDDLGvDDvGvvDGGCtsr{'-%...+*-)FzAvvvvDDvvDDvvDDGBtsrF~-#.@,/sABBCBCGvDEEuEELHCBCBHHvGx[;++%9)/sAGDvDvGHABBABABHGHGtAzF;+..%-~]QJvuuEEGHCBCBCCCHGGDDHyz],+.#-_FQsuuEuEvHHGHCCCCCGvDvHxr{=++*;_FKxuuEEEGBCCCCCCCHGDvvHsr!@.@-(/JGuEEEvHBCCCCHBHGGvvHAz/_>.......+%*,-~{^sGLEEvAK/)'-,*&..........+#,;~{/JtEEEvAK])'-=*@.........+@#-;~]rsGEEEvyrF);-=%+.........+&*-;~]rsGEEEGsrF);-=%+....",
+"..%-!FrxGLLLvCAAAxAACCBCBts^);*+.+,)^JALuEGHBtyytBACBCABszF);=&..&;{/sHGLLvHCtAyytCHCBAAsr]~-*+...*;)FKtvLLLvCBtAxAACCCCAxz/);*..+,{rsHLLLvCBAtxAACCCCAAs^{!-*+...*;)/stvLELGBAAxtBBBCCCAxzF)-%.@-FsBCCCCBCtALEEEEvBCCCCCCCAy^-@.#-~:rxBCHvvLGCCCCCCHvvDLGAs^-@..&-~]^sGEEEEvCCCCCCCBBCCBCAsr{=++&-~FrsGEEELGCCBCCCCCCCCBCtsr(>++#;)FryvuEELGCCBCCCCBCCCCCBs^'&.+,{^KvuEELvCCCCCCCCBCCCCAxz/)=+......&#=-;)]^JtLEEvAz/{~;,=*&........@#>-')F/JALELvAzF{!;-*%+.......+%*=-;)FrJtLEuvxzF{'->*@........@%*>-;)FrsGLuuGy/F)'-,*#@...",
+"..@>-~1[8wwwJ<^[/[/QIKKI^/F|!-%..+*;(2PwNNOKQ/[/[^IKKKrQFF(;-*@..@=;(28wNwOII[/[[[KJKK^^/1~-=%....&=;_2<ONNwPI[[/[^QIKIrQ/1_;=%..+$!|[8wNwJIQ/[/[/IKKI^/F{!-*&...+%,;_25OwwwPQ[/[/QIIKKr^}]~;>&.+=)}rIKKKIrQQ6SNSNOJKKKKKKKI[)$+.@#>;_F[II8OwJJJsJzKJJOwJPQF)=+..+*-!([6SNSNJJKzKKKzKKKKKKI[|!*..@*-'|46SNSwJJzKKKKIKKKKKKQ}{!%.+@*-!|7ONSNwJKKzKKzKKKKKKIQF_-+.+*;(}5NSNNwJKKzKKKzKKKKKI[]);*......+&**,-;'(2PYwwO<}(!--=*%@.......+@#=-;!)1[8YNwOQ:)!-,=*&+.......@%*=--;'|[6Mww8Q1_!-,=$%+.......@&#>--;~1[6www6[|_;--=#%@...",
+"..@*,0(48SSR8[}}:}2}<KIQ[[1(!-#...#;_1<YNSO5[2:2}[[<I<Q}}|~;,*@..@*c_15YNS8<[}}:}[<5P<Q}21~;=&....%=-!1<OwSY6[}}}:}[<I<Q[}:(;,&..+$9_}5YSR8<}2}:22QII<[2}(!-=&....&=-_1<OSRY5[2:2}2Q<I<Q[2|_;>&..*;:[QIII<Q4<8RNNROPIIQQQQ<[}~$..+#*>3!(|}Q56OJOO6PI<II5<[|_,*...+%=ca:5MNNYOPIIIQQQQI<II<[1(;*..+*=c_}5MNSOPPIIIQQQQQ<K<<[:_-#..@*,c_f5RNSOJPKQQIQQQ<I<I<[:_=+..%-~b<RNNYJPI<IQQQQQ<I<<[}(c-%......+%$,-;!(|}5MNS8<:(_;-,$*@.......@%$>,9~((f5MNR6[:(~;-,$*@.......@%*>-;~_1}5RNR841(!;-,*#@......+@#$,;;_(|45MNM84|(~;->>*@...",
+"..@*,0dgpqWqji74777hppplk74ea3#..+%3afiqWWqig77ggiijjpih7eb;3*&..&>cafiqUWqih7777gmpjph47ed0,#....&=9cbgjUUqph7777gipjjih74ba-%..+$cdgiWUqji47777gjjjmh77edc=%....&=3afiqWUqig7777hijopik74b0,&++$0ekimpppmipjTTTUqjmmhhhmmiga$...@*$>3cabf4imqWZomlhhh74ba0,#...@*=30bijjqjopmlkhhlhlmpjpied3#..+#=9abijqjoopmlklhhhlpjppied9#..+%=30bijqqojjpikhlmppojopgf0,+..%>0bgjqqqjoXmklhhlhmjjjigbc3%.....+&*>;abbbfgjqTTqigeeb~c3*%......+%*=;0abffgjqTTqigebdac9$&......+%$-cabbefijqTUji7fbba0-=@......+#*,;abbffijUTqqifeb_a;,$&...",
+".+&=901gnWUWZmVVVVmnoWZoXVl7bc*+..$cbemqUUWoXVVXnnqUUoXVlked9>%+.@>0bgpZUWZnVVVVVmoUWqnVlkea,*+...#=cdfiqWWWnmVVVVXXWUWXmVk7d9#..+=0bgjWUWZXVlVVVnoUWqXllkba3#+..+#30dfmqWUWnXVVVlmoWUqnVVhed3#..=d4kXnZZWqWWUTTTUWZZnXVXnooib$+..+&*=,30(ekVnZWWZnVlhk771ac=%...@#>30b7mnXoZZoXXVVVVXnWWWjgbc%..+%$30bhVnnoZZoXXVlVVnoWUqjgb;#..@#$90bgmmXoZWZnXlXXZZWWUqp7d,+.+#,3dfinnXZZonXVXlVmoWUUqiba-#.....@#>9_e4khlmoUTTUoVlkk7:a9#+.....&*=0d:7kkVmqUTTWpVhkk4ba3#+.....@#,9de7khlmqUTTWnVlk72ba3#+.....%*9cb27khlmqUTUqnVlk74ba,%...",
+"..#-']rxvEEEGHCCHHHvEELGHCCxr]=+.+=(^stDEEvvHHCGGvDEEDHHCtz/),*..#;{^stEELvvHCCHGvDEEDvCCtz/'=@...#3~/ztDEELDCCBCGGDEEEGCCAy^)=..@-]rsGLEEGHCCHGHGvEELvCAAz]'=+..+*;_/zAEEELvHCHHGvDEEEvHBAs/'$+@-FsAGDLELELEEEEEEEEvGCHCGDDxe,....&=-;!)/zyBvLEEvGCBCAxz^{~-#...@$-;(^sACHLLEvGBCBCHHvEEEDJ/)*..@*-!|^sACHGvLvCCCCCCvDEEEDP^(=++&=-9_}ztCHGvLvHCHGvDEEEEEwse-+..*9_FzxCGvDLLGCCCCHHDEEEuwr{9$....+#-'{^sACBCHvEEEEGHCCAAs/);&....+$-!]rsACACGDEEEEGHCCAAs/)-&....+$-~]rxABCBGDEEEEGCCCBAs/),&....+*-']rxABCBHDEEELHCCCAAz/~>+..",
+"..%>'{^KAGvvHCCCCBHHGGGHBCAyr(=+.+=)FKAAHGHCCCCBCBGGGGCBBAzF~;%+.@>)FKyBGvvBCCCCBHHGGGCCCyzF'>+..+%-']QyAGvHCBCCCBHGGvGCBBAs/)$+.+-_/sxGvGHCCBCBHHGvGGCCBxr]'>@...%-~FKxHGvGBCBCBHBHGGGCCCAz/'*++>]zyBHGGvGvGvDvDvvGHHBCCCGtz{$+...+&*>;)]rstHCvGGCCBysr/]';>%...+%$;)FryAAAHGHCCCCBCCGvHvAzF;%...&$-)FzyAABHHHCBCCCCHHGGtxzF;#..@#=;']^syACHHHCCCCHGGvvGGxz{-+.+#-!)^sxABHGvHCBCCCCHvGvtJ/);*+....*-!)rsyABCBHvDvGCCABBtsr);*....+$-']rsyBCCBHvLvGCBCBBxs/);*....@$-~]rsxBACCGvLvGCBCBBts/);%....+#;~FrsyBBCCGvLvHBCCBBtsF)=+..",
+"..+%=-'1}QQQIKIKIKKKIIKKIrQF);*..+%>;):[[IIKKKKKKKKIIIIrQ/|!-=@+.+%>;(}[QIIIQIKKKII<IIIrQF{'-%....+*=-~1[QQIKKIKKIIIIIIKIr/]_-%...*-!(2[<IIKIKKKKII<IIII^/_'-%....&$,-~:[[IKIKKKKKIQIIIIrQ^]~-@..%;)1[IIKIIII<Q[[QQIKKIIQ[2:_>@......+%*>;)]2IKJJKKI/F{~;-$#@+....+&*>;~(]22QIIKKIKKKIQ[4e1d;$+...+%*,;~)]2^QKIKKKKKrQQ[72b';$+..+&#$,-'){:[QQIIIKKIIIQ4}e|_,*....%=;!)]F}[QIIKKKKIIQ442e(!-*@.....@*-;)]F^QrIIIIIIKIrr^/F_;*@.....%=,;)FF^QrIIIIIKKIzr^/])-*@.....#=-!{FFQ^KIKIIIIKIr^^/])>$+....+&=-!{F/QrIKIIIIKIrrr//]'-*...",
+"...@%*-9(1}[}Q[[Q[[QQQ[[Q[}(',&...@*,9_|2}[[2[Q[Q[[}[[[Q}:~->#@...+*-c_|:2[[[[[Q[Q[[}[[[}|!-=%+...+&*>3~(:}[[[QQ[QQ[[[[[[2:_;=&...&*-0(12}[[Q[Q[Q[[[[[[}}(!-*%+...+*$,9_|22[[[[[Q[Q[}}[[[}|~3*&..%,;_(}[[Q<Q[}:|::2Q[[Q[2:(!3*........@#>-;(1[IP6IQ[2('-=*%+........@#=--;'(:}2[[Q[[[[::(__c-#+....+&**-;;~|2[Q[[[Q[Q[}1__!->@.....+&*==-;~(:2[Q[[[QQ4:|_~;3>&....&**--;!(:}[[[[/[Q[}:(_!c-*#+.....@#=-'_|}}[QQQQQQ[Q[221_!-$@.....@%=;'_|}}QQQQQQ[Q[[}}|_;-#+.....@$=;~(:2}[QQQQQQ[Q[}}|_;-%......@#=;'((:[[QQ<QQQQ[[221('>&+..",
+"...+%$-;abeef777k7777744f4ea;=%....%$30dbe477777774fff44eb'-=%+....&=30bbf47777774ffefffebc,*@.....+#$,00bff4777777fffef4eb03*@...@#>9abee44777774feee4eed;,$&....+&*$30deef47k77774ffffeeba-*&..&>9cdb74477ffbdbbf47774fb0c=#........@#$9abfipoomhfba;=*&+.........+@#>>39abef7477774bba09,*@......@%#$,9;db47777477feda0c=$&......@%$*=39abef77k774fbdc03,*&.....&#>>,30dbf4777774ebdac->#@......@%=-0d(b47777777747f:bdc,%......@%*-0ab:f7777777744f:bdc,#......@#>3addee7777777744eebd;>#......+%>30db:f477777h47fe:bdc>%...",
+"...+&*=9adbee4477744eeeeeeba9=&....&=3cdbee447744febeeeebdc,*&.....%*90dbee4474744ee:eef|dc3=%......@=,cdbbee4444744ebeeee|09=@...@*=3adbeef477744eeeeeeba9,*&....+%#$30abeee47777eee:eeeed09*@..@*,c'befeeeebdddbbeeeee:d03=%+......+@*,;b7lnZWWXie1c3>#@...........@&%=>90dbee4444febda;9,*@......+@#*>,9abee4444efebda;3,*@.......@&#$>9adee444eefebac9,*%+.....+&#=,9cdbee4774eebda03>*%+......+%$30adbe47k77k774eebbac,%+.....@#>30adbe47k7k7k44ef1bd0,#+.....+%*-00dbe47k7kk774eebbd0>&......+#=30adbf4k7k7k7k44bbbac=&...",
+"....@$-;){]F]^rrzrr/^FFFFFF)',%...+&=;~{{]/^rzrrr^^/]FFFF)'3=%.....#=;~{{F/rrrrrrr^F///FF{';>#.....+&=-')]FF^rrrrr^///]FFF{);=@...@#-;~{]]/F^r^rr/^F/F/FF)'-$@.....&#$-'){{//^rrrr^^FFFFFF{';=%..&=-;~{FF/FFFF(({{]/F/FF](!;>&.......+#=;{rxBGDvDvtz/);-#+...........+&%$,-~)]FF^^F/FF{_);3>#@......+@%#>-;~{F/rr^//FF]()!3$#@.......+@#>-;~)]]///F/FF{)!;-=%.......@#$-;!){{F/F//]F]{)';-*&.......+&$-')){F/^rrrzrr///]])!-%......+#>;~){]]^rr^rrrr//FF{)~,*......+*=;~){{F/rrrrrrrr//]])'-#.......%,;~)]]F^rzr^rrr^/F]])'-%...",
+"....+&*-;!')))(FF{{{{{)))'';>*@....&#=;;~~)]F]F]]{){))'~!'-,$&.....@$--;')){{{{{{]{){))~~;;-=&......&#>-;!~){{]{({{{{))'~~;;>%+....@%=;;!~~){{{]{]{){))~''-$%+......+#,-!~~')){{]{]{)())~'!->#+...%=-;!'_)~)'~'~!''~~~~'~;->*@.......+%=-)FzxBtHGyz/]~;=#+.............+@#,;!'))){{))'~;--,%&.........+@#=;;!)){{{)))~~;;->%+..........@#$-;!~)))))))'';-,$%+.........@#=-;!)'))))))~;;->*&+........&#=--'!_){{(]{]{))~)!'-=%......+@#$,;!!)){{]{]{{{))~'!->&+......+#$-;!~){{{{{F{{){)~!;->%+......@#$;;!~_{{{{(){{))~!!;;$&...",
+"......+@#==>-,,-----,->==$*&@.......+@%%==>>------,,=>>==**&++......+&%#$=,--------=>=>==*%&@........@@##$==,-,---->-=>>==#%@.......+@%===,=>----,->,===***@+.........&%*===>,,>,,---=====*%@......@##=>=,=,=====>=>=,>=>*%@..........+%*>;~(::|:b_;-=#&@.................@*$=>>>,>=,=>$%&@.............+@%$==---->=>==$%@++............@@@&*===,=>==$*%&&+............+@#*=>====>==>$%%@+...........+@%**>=>-----,-,-==*=*+.........+@%*=>=>->--,,,,===*$%@.........++%*===,,------>>,=*$&&+........++%$>=>----->-->===$#%@+...",
+".........+&&&%&&%&%&%&&&%&+............@+&&%&&&%%%%%&%%%&&+...........++@&%%*#%%%&##%&&&&@+...........+.+@&&&&%&&&%&&#%&&@+...........++&#%%%&&%&%&%%%%&%&+............@&&##$#%##%%#%*%%&@+..........&&@&&%&%%&%%&%&%&&&&&+.............@#$=--3;,-$*%%&+..................++@&@%%&%%%&@@+.................+@@&@&%&#%&&@+...................++@&&&#%%%@+++.................@&&%#%#%&%@@+................+@%&%%&%%%%##%%%%%@@+............+@&&%&%&&#%%%#%&@+.............+&&&&%%*#%#&&&&@&&.............+++@&&%%#%&&##%#%&&+......",
+"................+.+.++..........................+..+.....................++.+..++++.+.......................+.+...++..+.+.................++.+.+.++.......................++@@++@@@@+@++..............+..++.......+......+...............++@%%%#%&+++........................+..+.+..+.......................+..+.++.+............................+...........................+.++.+......................+++++.+.++@@....................+..+++..+......................++++.+++@@+++.........................++.++............",
+"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................",
+".........+..+++.+............................................................................................................................+.+..................................+.............................................................................................................................................................................................................................................................................................................................................",
+".....+@&#%##***#%*#*#*####&@+................+@@@@@@@@+........................................................+@@&@@++....................@%%#%###%@......................+@@&&%%&&+++++......................................................................................................................................................+&&%%%&@+..................@@&&%&@+..............................................................................................................................................",
+"....+@&&****==,===*=>=**%%%@+..............+@@&&&&&&&%&@+................++&@@++..............................+@&&%&&@@@..................@&#**=***#&+....................+@&&%&&&&&&&&@+................................................................................................................+@@@@@@..............................+@@&&&&&@@+................+&&%&%%&@@+...............................................................+@@++...........@%######&&+..................................................",
+"...+&%#$$>,,--;--------=$$##@.............+&&####*#*#%#%&@..............@&#&%%#&@+............................+@&%##%%#@&................@&#*$>>,>=*#&+...................&%#$#$*$####%%&@...............................................................................................................@&%%##%&............................+@%##*#$*#&+................@&%%*#*##&%@...........................................................+@%%%&#&@.........@**=>---=>*%@.................................................",
+"...@#$*,39;ccaaa000c0cc;,=**&............+&##*==>>>>=***%&+...........+&%#$$$*$*&&+.........................+@@%#**=>*$%&+..............+&#$=33999->$*&+................+&#$*==>,,==*=*$#%+............................................................................................................+@%#*=*=$*&+..........................&%$*$>=>>*#&+.............+@&*$>>>>=>*%@+........................................................+@#*=$>*$*&+.......&%*=3;acc93>$*@................................................",
+"..+#=3cad:beee4e:e1eeeebdac3$@..........@&*>3ccaac09cc9,>**@..........@%=>3cc03,>*#@........................+%$=,-0aac3=*%@.............%$,90~be:bdcc,>#@...............&*>3c'adaaaccc33,**@..........................................................................................................+&*$-9c;c3,=*%+.......................@*>3c9cc0c3>*&.............@#=,99aaac93>*@.......................................................@&*=,caaa9,>%+.....+%=-0dbfee|acc=*&...............................................",
+"..&,!]/zxAGAGAGAAAAtABttsr^{;$+.......@%=-;']/rrzrr^rr/{~;-=%.......+%=-!)FrrrF{~;->%+.....................@$-;!)FzsyrF'-,*@..........+%>;)]rsyAHysrF);,*&............+%$-'{/rssszz^//FF]~-=%+.............+....+@&&%&@+......................+@&&&@+................................................+#>;!{FrrrF)~;>$%@...................@#>;)]F/rrr^{!-=%+.........+%$-')F/zssz/)!;=%....................+++..+@@@@@@+...................&#>-;~{/sys^{!-*&+...&,'{/stGAAAsrF)-=%+......................+&%%%&%&@&&&%&@@+......",
+".+#;)/zAGLLLELLGCCCCGGLCByz/)-%......@#=--!]/sttyxysssz/)';-#&......@*-')FrstxrF)~;-$%+...................@#>;~)FIyvGy/);-=%..........&$-!{/sAGLLLAs/]!-=*&...........&$-!)FzstABAxzzrrr/]',#@........@%###%#####**$***#&...................@##**$**#@............................+@@+...............&$-!)FzytsrF)!;-=*&................+&#$-!]/zsytxs/);,*@........+%=-;)FrsAABxzF~;-$#..............+&####*####=$$$$*#@+................&*=-;'{/KAGAsF~;-*&...@>']KwuEEvCAs/{!-=#@..................@&%##$$$>*=$*$>**$*#&+....",
+"..&,;(}IJwNwNNNwJKJJwwwJI/F_;>&......@%==-;~]/^r^^/^Q/F{!-,$%+......@&*-!)F/r^/_~;->*&+..................+%*=-'~(}5OO<1'-*%@.........@%*>-~12PwwwwP[]~;=*&@...........&*>-~{F^rKrr//}F{]_~-$%+.......+@%*********>*$$**##+................+@&**>*>***&+........................+@@@&&@++............+%*,;~)F^rQF_~;->**@...............+@@**>-~]F/^rr/]'-=#@.......+&%*=-;)F/rKzIF(!->*%+............+@%*******$****=**#&@+.............+@&$$=-;!1[8O6[_;>=*@...+*;~}6wNwwJK/{!-=*%&+.................@@%#****$$*$**$>****&@....",
+"..%=;_:[8ONNNNYOPIKPJwO6Q2:_;>%.....+&$$--;(1[Q[[Q[QQ[:(!-=*&+......+%$,3~|}[[:1('-,>*&..................@%*>;!((2<66<(;,$%+.........@#*=;'(}<ORRO5[|~;,$#@..........@%$,;'(}[QQIQ[}21:((;-*&+......+%#*$>>,,,>>==,,=>$*#%@...............@%$$>,--->$$%+.....................+&%%%%%%%%&@+..........&#$,-!_:[[}1(_'-=$*%&+............@%%#$>-;_(2}[[[[(;-$#@.......@&#>,-!(:}QQIQ}|';,$$&+.........+&#*$$>>,,,>=>>,,,,>*##@+..........+@%%#*=--9_|46O54_;->*&....%-~b5RSYOPQ[('->$#%&+...............@###$>,-,,=,=,>,=,>>**&+...",
+"..&=cafgmjoqqWqoojpooomi7f|a3=&....+&#=,;ad:fk<iiiiikheba->*@.......@&#>-0be77efeba;>>#@................@#$=-0dee7ipigd0-$#+........+&$>30bbgipjqpigeba3>*&..........&*=-0dee75hl5iggfebdc,*&......+@#$=,9c;0c9939c0cc3,=*@+.............+%*=3c0aac9=*#@...................@&%%###******##%&@......+&*$-90db7774ebba;,>=#&@.........@@%$$*=,9abf7<774ebc-=*%+.....@#$=,30dee4khkh7eba;=>#&+........@#$=3399;ccc339caaa03*$#@.......+&&%%$$$>,;abbfgipifdc9>*&....%,0bgjqjplh7bac3=$*&&+............+@%$*==c0aa0999900c999,=#@...",
+"..%,cb4hlXXnnooqWWWZqnXlk4bac=%....@*=-0be7klVVXooZnmVke_9,*&.......+%*,9abeQkllkkedc3=#&+.............&%*,3ab4khhlihfdc3=#&........@#=3;d4khVlmmmllk4(a9=%+........+&=3cdekVVVXVXnXni4:b03$&......+#>-0a(b4ee(ddd|e2bdac,$&+...........@**,0d|e444d0,*&+................+#*$=>-33-333333,==#+.....&*$-9ad|e[kkhlk7ed093>=*%+.....@%**>,39;ad:4klVlk7e1dc3>#&....+#=3;adbekIlVlVVlk7edc3,**@......@*=,;!dd:e4:b(ddfgigfac3=#+.....+&*==>,3330dekhlllih4ed03,#+..+#,cb7imXXVlk4b_cc3,,**%+.........&%*>,99abgii7bdd1e4ee{da03*+..",
+".+*;]rsBBCBCCBGvEEEEEGHAxsr/);*...@$;'{/sABACCHGvLELGCAz/);-=@.....+%$,;~FrsxAABCByzF)!-$#@..........+&*-;)]rstCBCCBxz/)'-*#@......+#,;)FryACBBAtBBCByz/{;=#@......+#-;)FrsACBCCHGDLvAsr^{!>%.....&#>;)F/zxtAyzzzsxAAxz^])-#@..........+*,;~FrstBBtz/~;=#@.............@%=-;;~){]]]]]]]]])~!;$+..+#=;;){/rzsxACBCCAsr/]{)!;,%+...+#>-!~{]]F^rstCBCAAyzz/F)';=&...&-!)F^zsxACCCCACCBBxr/{~;,$@....@$-!)//zsxyBAszzzyGDGsI]);-%....+*>-'~)){]F/ryACBCCAAsz//]{;#...*c_FQtAAHCCtyzrFF]{)!;-=%&.....+&$-;~)]F/sHvGysrrsxBBxysr/{;#..",
+"..*!)^syBCCCCCCGDvuvvGBCAyz/];*+..%,!)/zAACCCBBGvLvHHCts/{';>&......&=-;)]rxBCCCCBAsrF);-=#+.........&*,-!]/zyCCCCCAyzF{!->%@......&$-!{/zyBCCCBBACCAyz/{;-*@......&$-!)FzxBCCBCHGvvGtsrF{;,%.....%$-~]/rsyBAByyxyBBAyzrF);=&..........&*,;)FzxCCCCs/);-*@...........+&#=-;'{]F^rrrrrzrrr/F{)-%..&$-!)]/zssxyyABBBBAxzr/]]~;,&...&>-')]/rrzzssyABABBxxsz/]);-#..+=;)FrzsyABAABACBACCAsrF]~-=%....&=;)FrssxBBAByzzzxtGGAz/]);$@...&>;~)F///rrrzyABBBABtxszrrF),+.+*-!{^sACBCCCxyszrr/F])';-*%.....%-;){//zsytGGtzzzsACABtAsz/)=..",
+"..&,;)]/QKKKKII<<I5POwwPIr2]_;%...%=;~]FrKzKIrIKPKJJKKQF)!->$@......@*=,;~]}rKzKzKrQ/|~-,*%@........+&*=-;)F/IrKzKKr[});-=$%@......@*=-!)]/QKzKKzKzKr2])!-*#@......@*>-;'{/IKzKKJKJPKQ1(';>$@.....@%*-'){F/rrKrrrrzr^F]_~-,*@..........@%$-!)2IJwwJI1!-=%@...........+%*=-;~{{}//[/[/[/[/}F_);%..&*-!)1F[/Q//F/////^/^[F:{~-=&...@=-;){1/[/}FFFF//^QrQ/[F|~;=%...$;)|FQrrIr^/^//^rrKrQ/1)',*&...+%=;~{2^rrrr/FF1::[<IPQ^}]~-$+...@$-~)]:/}/[F}////[^^rIrQ/}]~=+..&>,;)F/QKKzKrI/Q^^}](~';,*&....+%>-~_F2[QII5<[1]F//rKrrrQF{~>..",
+"..@*-;_1[QIQQQ[[[24<8YY6<Q[:_-#...&=;_1}[QIQ[[2Q<I<QQQ[:(;-=*&.....+@*>-;~12[QIIII[[}1~;->*&+......+&#$>-'(}[QIIIIQQ[:~'->*%@.....+&*,-!_(}[QQIQQIQQ[}(!;,>*&+.....@#*=-'(1[QKIIIII<Q}1~'-=*&+....@#>,;~(1}QQQQQIIQI[1(';-$#&..........@#=-;(:QPYS8<(;=$#@..........+&#$>-;(1[[QQQQQ[QQQQ[}:(;*..&*-'(2}QQQ[}||((|1[[QQ[}:~;>%...&*-'_:[2Q[21_|(1|:2QQQ[21~;=&..+%;(::[QQ[[2::|:12QIQ[[2|!-=%+...&=-_|2[QQQ[}1(1(1:[QQQ[}:_;=%...@$;__}[[QQ[}21|||:2[[QQ[Q}|_>+.+&*=3~(}2QI5KIQQQQQ2}((~;->#+....#=;_12QQQQQQ[2(((1[[QQI[[[:_=+.",
+"..@*-cde4hlhh77ff47gjqWjmh7ed3#..+%=9d:4hhlhk447hhhhhk4edc-=*&.....+&*,;0de4khhlllhh4ebcc,=*&......+#*,9;a:77hhlllhh4fbac9>$#+....@#>-cabbehkhllhlhh7fbac3>$%+....+&*>-;abekhllllllh7fbda;=$%+....@%=30db:7kkhl5kl5k7eba;3>*&.........+%*=9abfijWUqib0,**&+.........&#*-90abekkh5hhlhhkhhhkfba*+.%*3abf7<kh4ebbbdb147hkk44b0-&...%=;abe7hkk7ebbddb:4khhk7eb0,%..+*cde7<khk4e|:b1efkhhk77ea9=#+...&,9ab77khh4ebbbbbf4hhhh4edc=&...+*9defkhhh74e:bb|b}77h5hh7ea>+..@*>-abe44lmmhhhhkhk4bbba;>*@....#>cde47klhhh4fbdbe4khhlhh4ed>+.",
+"..@*30d4kVVllhhhhhhmqWWomVl7ba$+.+*-0b4klVVVlhhlVVVlVlh4ed0c,#+....@$9cd1e7klVVVVVVVhke:d0c>$&....+#=9cd|e7lVVVVVVlVVk4e(a;3*&....@*,cd1e[hlVVVVVVXVVk4e1ac3$%....@#>9a|e4klVVVVVVVVVk4ebdc3$&....&>-cd147hlVVVVVVVVlk4eba3=#+.......+&*>3c_ehmoWWqied;3>$#@.......@%=30d1e[hlVVVVVVVVVXVVlked$..&,cd}klVXVlk41b|bekhlVllk4b9$+.+&,9(eklVVVh741b|beklVVllk4b9*...=a1khVVVlh7eeee}klVVlVk7e'3=%...#-014kVVVVh72e:be4hVVVllked9#...@=ab4<lVVVllk4ebe4kklVmVVh7|3+..&$,9_e7hVmXmXVVVVVVkk74eda3=@..+*3ae4hVXmVllk4e1e4klVXVlVh4b3@.",
+"..%=;{/zACCCHHGGGGvDEEEEvvGtz{,@+&,']zsAHGGvvGHHBCCHHGHAtsrF),&...@*-{^sxABHGGHCCHHGGHBxsrF);$+...#-;{^sxAAHGGHBCHHGGBAtsr]~;$+..@$;'FrxAACHGGHBCCHHHCBAsr]';*+..&=;)FryAABGGHCCHBGGHCAAxrF)-#...&$;)/zyABHGGHCCCHHGHCAAsr{!-&......@*>;'{]^zxtHvGGxK^])!;,=&+....@$-;{/sxBAHGHCCBCGGvHBCCCyzF,++*;{/stAGGGHAssrrrsyCCCCCCyz]-%.+*;{/sAHGGGCAxzrrrsxACCCCByr(-+.@-FztAvvvHAAsszzsxCCCBCBAz/~;#..+$!]rsAGGGGAAszrzzxtBBCCCAx^)=...&-{^sAHvGGHCtyszzytBCHGvvAy^'@..%,;{/sxBHGvHHCCCCBCHHHGtsr]'$..+>!]rsAHGvHHCByszsyACHGvvHAxr'#.",
+"..&,;)FzyCCBGvLLLLuEuEEEELGGsF-@.%,!]rxBGLLLLLvGCCCCGLvCCAxr{!*..@*-~]zyCCCGLGBCCBGGvGBCByr]!-%..&$;~FztCCGGvGCCCCGLvGCCAs/);>%..@=;{/sACCGGLGCCCBHLvHCBBs/);=+..&>;{/yACCvGLvHBCCGLGCCCAy/{;$@..%-;]rxBCBGGLvBCCBGLGCCAAzF)-*....@#$=;!)]/rzsytBAAsz^F)~!;,$@....%>;~]zyCCGvvvHCCCCLLvCBCBxz{-@+*;)/sAGvLvHByzrrrsyABCCCBtz]!%.+*!{rsAGvLLGBysr^/zyCCCBCByz]-+.@-]zyHGLLGCyxzrzzyACCCCCtsF);*+.+>;]ryBvLLLHAyzrrzsACCCCBByr(>+..@-(/sAHGGGBCCyszsyACCGvDGAs/;@..#,;)/zyAHLLLHCCCABBHvDuvAsr{=+.+$!{/stHGGGCCCyszsyBCHvvGGBsF;@.",
+"..&*=-~{/IKKwwNNNNuNSuNNNNwP[_>+.@*,;{}IOwNNwNwwJKKJwwwJKI/]_-#..@*>;)/IKJJwwwJKKKJwwwJzK/:~-=&..@*,;)/IKJJwwwJKKJJwwwsKK/|;-*@..&=-'{^KzJJwwwJKJJJwwwJzI/{!-%...&=-']/IKJJwwwKKKJJwwJJzI/{!-%...@=-!{/KKJwwwwJKKJwwwwsKz/_;>#....&%$=-;~)_{]2F[/[/}F(()';->$&....@*,;)2rKJJwwwJKKJJwwwJKKr}{;#+.&>;)2IJwwwJI[F(((]^IzKzKK^]',&.+&=;(}IJwwwJI[F|{(F[rKzKzK^{;*..+>_:[PwwwwKQ/|{]][rKKzKKr});=&...#-!([KONNwPI/}{){F/IKzKzI/(;*....#-'(}IPPPKKK^/}F/QIKPPPI[1'$...@$=;)F[IPwwOI[^[^[QIPPJPIQF'=...&=-~1}IPPJKKrQF2/[QKPPPPI[1;$..",
+"..@#>-'(}[QI6MNSNSNNNNNSNSY6}_=@.@#,;_:<8wNNNww6KIIIOwOPIQ[:'-%..+*,3~}QKIJwYOPIQI8OOOPII[:!-*@..@*=;_2QKIJOw6IQQIPwJOPKQ[1;-$@..@*-;(}QIKJwOOIQQIJYw6PII2(;>#+..+$,;(2IIPOwY6IQQ<JYOJPIQ[(;>#+..@=-'12QIKJJw6IQIKJwOP5KQ}_;>%....@$=>-!_((|::::e::||{(_(';-=%....%=-;~}QIIOOYJPI<IOwSOPII[:'-%..&>-!|25MwY6[1((___:QQIIIQ[|;=@..%=;~:[6RwO6[}(_~_(}QQKIKQ}(;*+.+*;(}5Oww8<}|(___:[QIQKIQ:!-$&...#=9~:<OwSY6[}(~__(2QIIKIQ}_;%...+%=,;(}[I<IIQ[111:QIIQ<<2:_-%...@%=-'_:}5OO6Q2||||1[<I<IQ[}~$+..@#>-'1}QQIQIQ[2|}}QIII<Q21~-@..",
+"..@*,9abgimpnqWqqqqqqqUUTUqpga=@.+%=3abgpoZqZqomikhkpppmhh7ed3#..+*-cdfimpjqopi77hijqjpmmkea9>&..@$,cd7immpojmh47hmpoppmm7ec3>@+.&=cabgijppommhh7gmjqjpmm4ba-*...@$3cb7lmpjqoph47hppoppmi7b0-%...&>9degmppnommk77ipooXpmi7bc,#...+@*39cbffggggggggggggfgeebc,#....&=9ab7impoqqpmkhljoqoommkfa,%..@>30beiooomgebdad(e7hhllk4b;=@..&$3cbgmoooi7fb_ddbe7hhllk4b3*..+*cdfijqophfbb0ad:4hhllk7ea3=&...%>9abgmoqoigebdadbekhlllhe_3#....@#=-cbe7hhlih7ff7iilh74bdc,%...@#=3abf7ipji7bb(bbefhhhhh4fd=....@%$9abe47ili44ee7ilih7fbd9$@..",
+"..&*9ab7ioqooojjjiipipjWUUUpgb,+.@%>3a1kmnnZnnXlh47hlXXXVVl4b9*++&>9deiXoZojmik477hmnoZoomgba,&..&,cdfiXZZnXmik777kmpoZZop7b0,&..@,cb7moqZommh4474kmpoZoom7b0$@.+&-0b7mooZopmh7777hmnoZZom4d0=+.+%-ab7moqZnpmh777khmpooZnifdc$+...#-!bfijjjojqqqqqqojjjjjjif0=+..+#,cdeioqZoppmVVVXnZZZZqomhb0%..&*9cb7lnnnVh7e|bdbeklVVVVkea3&..&=9~b7lnXnlh4ebdbb}klVVVlk:0*+..*cdehXnnXlke1bbbe7lVVVVl4dc>%...%=90:4mnnXlk4ebdb14klVVVVkb0*....+&$>c_bekmnXm5hkipoph4bdc9=&...@*>9a:4kmnnmh4eeee4khVVVll7d>+....@*,0abekmXnmhhhlnnmh4bdc3#...",
+"..#-~FrxGvEEvGCAxJsJxAGvEEEvxr!&+&*-!]^sAHHGHHBtysssABCBCCCyr(>+.*!]/sHDELDHxsszzzsyAGDEEvtzF)$+.%;]rsGDEEvHtsszzzstHvEELDAzF~$++*!]rsHvEvvAyszzzssyCvvEEHyz]-@..*'FrxGEEvvAyszzzssACDLEGGxr]-@..*~FzADEEEvtyszzzzstHvEEvHsr]-@..+>'/syvuuEEuEuEuEuEEEEuuuNs4-@..%-!]rsHDEEvHBCCCBCGHDLEEEvAK]-.+#,')^stHBHCAysrr/rsxBCCCCtzF;%.+#>;{/stGHHCtxzr^rrsxACCCBAz],+.+=~/ztHHHHAysz^rrzxtCCCCAs^{;*..+#-'{^sABHBCyyzr^rrstACBCCyz(>+...+@*,;~]/stvGGHvGGDGGyz/{;-*@..+#>;)/zxAGvLGAAyxyxyAGvHHAtsF-+.....%>;)FrsAvGvGHvHGvAsrF);-#...",
+"..%-~]zyGLLGvCxxsszzsstvLLLGy^;%.+*-~]rsCCBHCCBysszsxAACCCCyr),++%!]rJtLELGAssrrrrzsAGLELLAz/)>++#!{rJtLELGtyzr/rrzstHLELutzF)$+.*!FrxGLELvAszrrrzzstHLLuvxzF;@.+*!FryGLEDDyszrr/rzstHLLDGyr{-+..*'/KxHLLuGAszr/rrzsxvLLLGsr{-+..+=~FzxAGHuDvuDuEuuuvuDDvDAz]-+..&-~]rsHLELvCCBBCCCCBCLEELGtz{=@.&=;)FzACCBCBxsrr^rzytCCCCAzF;#+.&-~_/zACBCAAszr/^rzyBCCCBAz]-@.+>~]zyBCBCBxsr/^/zsyBCCCBs/);*+..%-']^sBCCCAAxzrr/rsyBCCCCxr{-.....+%=-;)FzyCGvLLLLGHts/]~-*@....%,')/zyBGLLLBBABBBAGLLEvByr]>+.....@*;!{/zyCCGLLLvGCAzF);-$&...",
+"..@=;'1Q8wNwPI2F||]1:2Q6wNw8[(,@.+%$-;{2rKJKKQ^/]]]:F^IKKKK^{!*.+&-'1[6wNwO<2]((((1:[PwwNO<1_-%..@-!{[8Yww8Q}{1((({:[PwwwO<:_,%.+#-~1[8www6Q2:((({1:[PwNw8Q|;=+..&-~1Q6wNwP[}](((({2[6Sww6[1~>..+%-):QOwNw642{()({:2Q6wNw6[(!=+...%>;_|e472<<<<<h<<k<<7777e(;*...@*-'|[6wNwOKIrIzKKIIKJwNwJ<1~*..@*>-']QKKKrr/2{()|]/^QKKKr}~-@..@*-;):QKzKI^/]1(({]F^QKKKQF_=+..&>;1[KzKIr2F1){)]F/IzKKIF{!-%...&*-;)}IKKKr^}]((({F2/KKKz/];#.......&*=-;1[IKwwNNwJKQ](;-$+.....@*-;)2[QPYwwJKKKrIKJYNwOI}(;*......+@*,;_|[IJJNwNwKI[|!-,*&+...",
+"..&*-c(46YSY5}1(____(|}5YSM52_>+.+&#=-'1[IIIQ[}|(~_(:}[QIQQ2_;%+.@=;~:5ORY8[|((~___11<OSY8<1!-%..&=9_25YSR8[((_~___1:<OSR84|!-%+.@=!d}8RSM5}|____~_1}5YSM64(;>+..%,c(}6YYY54((_____|}8YSY54_;*+..%-c146YMO52___!~~(128YSY5}(;$@...@*>3c!a_|b|||1::1|||d__!;-$&+..@$-c_15YSSO<QQQIIQQ[Q5OSR8[(9*+.@%*-;_}QI<Q[:____((}[QIIQ[1'-%..@#$-!|[QIIQ[:_____(}}[IIQ[1;>+.+%=;_}QIIQ2}(____(|}QQIQQ}_;=%...@$=-~:[III[21____((}2[I<Q2_;#.......+%$>;~|[<6RNSO5[:(;-$%+.....@%*-!(1[5ORYO8865868OOO5[|_-&.......@#*>;_:2<OSNSOI2:~;,*#@....",
+"..%*9;dgpqWqpfbbdaa0dbgiqTWjfd>@.+&#=3ab7hlhh4fbdaad:e4khlk4bc*..&=cafiqWqpgbbd0aaabfiqWWjgba-%..%>cdeiqWqpgbdaaadabfiqWWjgbc,%..&,cdfjqWqpfbdaaaadbfiqWqpgbc$+..&,0bgpqWqigbdaaaadbfpqWqpfbc*+..&3cbgpqWqifbaaccadbgpqWqigdc*....+%*>,,30aadbbbbbbbda033,-=*@...@*>cdfiqWWjihhhllh77gpqWWjgbc*+.+#*-cd4hlh<74ed_ad:f4khlk7e03%..@#*3cb7<lhk7f|daddbe4khlk7bc$...#=;aekhl<7e:da0ddbf7hhl7e0c=&...+#=3ab7hhh44bbda0dbe4khlh7bc*.......@&*,3abfijUTUjieba3=*@......@*$3abf4iqWUUUqUqUqpmil7edc3&......+@#$,9ab4ijUTUjgfda3,$%+....",
+"..&=3abhjWUWph4e|bb1eehjWUWjgb,@.+%$3;d4llVVlk44:bbeekkVVVlke0=++@,0b7mZWWoi7ebbb1bf7mqWWoifa3%+.&-abgnZWWph4e|b1bee7pZWWoiba3%..&30b7jZWZpheebbbbefhXZWWjhba=+..#9abgoUWZpkeebbbbefhpZWWjgb0>+..#9abhoWWZp7f:dd(befhpWWZp4b0$+....@%#=,390dbeef77febdc9,,=*#@...@*30bgmWWWZXmlmXXVhhhpWUWoifa*..@%>9a:kVVVlhk4e1eeekklVVVkebc*+.+*>-a:kVVVIlk4eb|e}khVVVVked>@..%,914llVVh74e:b1e[kVVVVl4ba3#...&*>9a}kVVVlk72b::e4klVVVVk:a>+.....+&#>-0d1kijUTUoi4da9>#&......&$=c_e7hmqUUUTUUUUWoVlh7eac=&......@%$=3'be7ljWUUjieb0->%@.....",
+"..*-_FryvEELvAxyzzzzsyBGLEEGx^c%.@=-~]^sBCCCCBAszrrzsBACCCCyzF;@+#;{/sGDELGAAyszzzsxBGLEELxK{~$+.%;]rsGLELvAtssrzzstAGLELGyI];*+.*!:rxGEEEvAxyzzzzsxBvEEEvyQ(-@..*;FKxDEEEGAxszzrzsxHvEEEGy^(-+..*)FztDEEEvAyszrrzsxHGEELGsI{,+....@&**39~(/rsttHGAJrF(!;,=*#@..+#-~]^sGLEEvvGGHBGCCBHvEEELyr_>.+@=-~]zyCBCBCtysszsxBCBCCCAsr(=+.&=-~]zyCCCCBAxszzsyACCCCCAs/;@.+*!{^yCCBCBAtsszsytBCCBCCyr])=+..&=;)FzBCBCCCtyszssyACBCCBxzF;+.....&=,;)F^stCCvEvHtz/{!-#@.....+#-;{/sxCHGDEDDDvDvvHAxsrF)!,@.....+#=-!)FrsAHvEvDGsrF);>*&.....",
+"..#-'FrxHLLLvCCtxsssACCvLLLGsr;@+@*;)FryACBCCBtxzzzsytCCCCCAs^;@+#;]^stLLLGCCAysssACCvLEEDyr{;*+.%;{/sGvLLDCBAxssxABCGLLLvy^{;#.+*;FrJBLELDCCAxsssABCvLEEvs/),@.+*!]ryGLLLGCBAsssxACHvLLuHs/)=+..*!FIyGLLLGHAyszsstCCHLDuts/(-@.....+@*=-;)FzACGuLLAr]~;-$#&+....&>']rsGLEEELLvGCCACCBvLLLGy^)*+.&$-']rxBCCCCCAyssxACCCBCCBsr{>++&$;~]rxBBCCCAAyxsytBBCCCCAsr;&.+$;)^sACCCCCBxsssxCCBCCCAxr]'>+..%=;~FzxCCCBCBAsssAACCCCBCAs/;@....@*=;!{/ryACCCHHtyzF);>%@.....@=-!{/sAABCHCHtAyxysszrF])!,#+.....@*,;~]/zyBCCCHHtsr]~-=%+.....",
+"..@*,;(}6OwwJJKI^[[QQKJJwwOP[_>+.+#,;)][rKJKKIr[F1]F2^KJJJKQF_-+.&=;_25ONwwJKKr[^QQKPwwwYJ[(;>&+.@$;_}5OwwwJKKQ[[^IKJJwwwP[_-$@..&=;(2PwwwOJKrI[[[IKPJwwO8[(;*+..&=;(}6OwwwJKrQ[[[IKJOwwO5}~-*...@$!([6wwwOJKI[[2[IKJwwwO5}~;#.......+&%*=-'1[PJwwO<|!-=#@.......+#-!([5wwNNNwwPQIIKKJwwww6[_-&..+&*=;)/IKzKKKrQ/[QrKJJJJKK[{;*..+&$-!{^IKKKzKr[/^QrKJJJJKI/{-+.+%=-~F^KKzKKKI/[QIKKJwJJK/:';%...@#=-~]QKzKKzKrQQ^QrKJJJJKr}_-.....@#*--'{F[QKPJKKI2(!-=%+......+#=-'{F^IKKJII[22::{(~!;-==%+......@#>-;)(FQKKPJKKQ1);,$&@......",
+"..@#=-c|[P8OJ8P85555888OO654|;*..@#=;_1<5O6O8554:1|}[586OO8<}(,@++*=;([58888P855556666668<1!-$&..+*,!([P8PJP8K655566PO6O5<|;-#+..@#-0|[586O88P555586O8888[1;,%...@*,;|[PPJ6P8K5555668O886[1c>#+..@#-~1<588J885555556P6JP54(;=%+.......+&&$=;(:[88854(-=*@.........%=;_1<6OOwSSY655568PP6P5<:!$@...+&$=;(}[QIPPP5<556PPOYO8<[|!#...@#*-~|}[Q<P655<<5PP6OOO8<[_,@..@*=;_1[[<KPP55<5PPOJOY88<1(;*....@#=-~|}[QIPP65555PPPOOO8<2(=+...+@*>-!(|}<88J6J6<}_;>#&........%,;_(}[QP6P5Q}}:|_!;->=**@........&*=-~(:}<66OJ68Q:!-=%@.......",
+"...&*,caf7hipoWWUUqTUqqjih7bd9#+.+$,cb7pqUTTUqqigbfgpqUUUUqjge-+++$=9abghimjqWUqoUqUqjmh7fd9,#@..+%=cab7ilpoqWqUqUUqqpli4fd9,#...+&=cae7hipoqWqUqUUqqjih4bd3=#...&*,3cbghinoWWWWqWWWqji77bac=%...+%,;df7hipqWqTTqTqWqpih7ba9=&.........+&*>3abe7i5fba9>#@........+#=9ab75mmoUUUWqWUqWqpmh7fd9$+...+&*>-adefipqqWqqqqWqWUUUjied*....&*>cabe7ijqqqqWqWqWWUUUjie9@..+#*,cdbegpqqWqqqqWqWUUTqjgbc*....+%$,;ab:gijqZqqqqWWWWUUqjge3+...+%*3abfgijUUUqWqifa3=#@.......+#=;de7hijqqoih7f|a93=*$%%@.......+%>3ae47ijqUqWqoie03$#@.......",
+"...&*,9ab4klnZUTTTTTTTWolk4b09#..+*3dfljqTTTTTqjk77ijUTTTTTZm4c@.+&>-a1ekhXWUTTTTTTUUomh7fac>*@...&$-ab4hkXZWUTTTTTUWolk7eac=#+..+%>3ab4hloWTTTTTTTTWolh7ea9>&...@#=9a|4kVpZWUTTTTUUWolh7ba9=%...+%>9ae7hlnWUTTTTTTUWXVh4ba3*&..........@$=3~be77<7ba3**@........+#=!aekVVXoWTTTTTTTUWolh7:a9*.....@#=3ade7mZWUTTTTUUWUTTTWphb,+..+&$=9~be4mZWUTTUUUUUTTTTqm7a&..@%>,c!b4koWUUTTTUUUUTTTUjl7d>+...+%*=9adb7mZWUTTTTUUUTTTUqm4c@...&*-017lVmoUTTTUWogdc>*&+......@*,a|kVXXZUUZXVlkeac,>$&&+........%$,a|7lVnZUTUUUqpfa9=*@.......",
+"...&=3!|^stBGDELEEEEEEEGAys/{;$+.&>'FsBvEEEuEEuDtxxBDuEEEuEEGx(#+@%>;(/stAGvEEEEEEEEEvAAszF~-#@...%>;(/zxtGvEEEEEEEEEGBxsr{'-$+...%-;{^zxAGEEEEEEEEEDvtysr]!-#...&=,~{^sxCGLEEEEEEEELvAxs^{!,%...+*3']rstBHDEETEEEEEEDAxs/{;-#.........+%=-!{/zxyxs^('-*@........@>;)FzxCCHGEEEEEEEEEEvHtsr1!$+....&=,;{FrstDEEEEEEEEEEEEEEDtr;@...&=-'{FrsHvEEEEEEEEEEEEEEGx{%..@#,;')FzxDEEEEEEEEEEEEEEEHs/-+...+%$,!{FrsGvEEEEEEEEEEEuEEAx)&..+%-!]zxCCHDEEEEEEGs2)3,*+......&-!]zxACCvEEvHCBtzF);-*&+........+*-~FztCHGEEEEEEvGKF~;>&.......",
+"...+#-!{/zsAtGDuuuuuDvGHyxz/)-#..@-~FzxBDEuuuuuttssxGuuuEuuHAz)&.+#>-~]rsytGDEuuEuuvGBysz^{!-$+...%>;)FrzttGDvEuuuDvHHtyz/);-%@..+%=;)FrzytGvEuuuEuDvtysr/{;,%+..+#-;)/zsAAGuEuuEuvvGHyyz/);#@...+%-;)/zyAtGLEuuuuvuvAyszF);=&..........@$-;)]/zssrF);-#+........&,;)/zABCBGvEEuEuuEuvtxsz/];*.....+%>-;]/zyGGLvEELLDDEuuuvty/;@...+%$-']^zxGGEuEuEEvEEuuuGAx)&...+#>;~{^stGDvEEEELvEEuuDAts]-+.....&>;)]/KxGvvuEEuEDEEuuDGAs)&...#-!]zyACHDvEDDDvts/~;=#+......@>']ryCCCHvvGHCByr{!-=#+..........#-!FzAABHvEEELDDxz]~-*@.......",
+"....+%*-;~(:}4[k<h<<4[4}:)_;-#+...%-;{:}4<hhi<4}::11e7hh<k[42(-+..+&$,;~(124[7<k<<<7[[1(~!-$%@....+@#-;~~{2[[[<k<<<[[2:{_!-$%+....+%$-;~)1}4[<<h<<[[[2|(~;-=&+....@*=-;~(:}[2<<<Q<[7[}:(~;>$+.....+#=-!~):24<<5kh<7[42:)~;-=%@...........+%*-;')))~;>*#@.........+#,;(F[KKKP<<<<<<<<[[2:(~;-$+.......%$=-;~1}[<<Q<<Q<<<<<7[2|;#.....+&*=-!_:2[<<<<<<<Q<k<7[}(-@....@#*=-;d2[<Q<<[<<<<<<<42:);*.......@#=-;_:4[<<<<<<<<<k<7[:(,....@*=;{/rIKP6J6PP5Q|'-=%@........#-!)/QKKKPJKKI^/|;,$*@..........+&=,!{^rIKKJJPJPK[|;-=%........",
+".....+&*$--c0_a__0_(___';-==$&....@#=-9;a__~__a~';''_~_a_a_~;-%+...@&*$>-;00_~__d___a';-,>*%+......+%%*--99aa~__a__a~!;--=*&+.......+#=,-;0aaa~_da_d_~;-,=*%@.....+@%#=--;0aa_0~__~_~!;-,=*@......+@%*$-3c~a~ad_d____';->=*&@.............+&%$>>,->$$%@...........&>;~12QQQ[}:|(_d(d_a~;-->$&+........&#*=-90_a________d__a!-$+......@&**=-90a_a_a__(_d~__~;;#+.....+&**>9c0a__d(a____d__';-$@.......+&#=,-90_a__d__d__d~_'!-#+...@%>-!:}[[QQ<QQQ21~-,#+........+&=-'_:[Q[QQ[[[}|~-*#&+...........@#=-_:2[Q<Q<<[[f|~->#+........",
+"......+%**==->,3999033,-,>$*%@....+%$>,3,9c99993-3,3333333333=@.....@%#*$=-393399933,9-=**#@........+&%$>,,3339033333,,=$*%@........+&#*$,,3,939c09333==$$*@.......+@%#*$,-9-cc999333,==$$%+.......+&##*=,,-339c9333,,>>$#%@...............@&****#*%&+............%,;_bbf4fbbaaa0909933,=**&@.........+&#*$,,33,3333399c3933=&........+&%*$,,3333333909993,,=&......+@%$*=,,339cc99333cc93,*#+........+@%*>,>339999c9c3c093,#@....@&*-9def44fffffba0,$&..........@#>cdbe4777febbda-*%&............@#=-abef44f4ffeba;,*&+........",
+".......@&##==->,,>-33>,=$*%%@......@&*>=>,3--,>=>=,=>>,,=>==**@......+%%**=,=,3-9,>,>==$*#@+.........+%#%$=,>9,3,,>,>,=$#%%+.........@%%**==,=9-9>-3,=>**%&+........+@%#**==,9-9,,,=,=>*%&@.........+@%$$*=>,=-39,,>>,$$&%@.................++&&%%&@+............+&*99addbddc0c933-9,>,*$#%&...........+&#***>,>,,,,,,-3,,>=$&.........+@&*=>=,,,,,3,39>>,>>*@........@&#$*>>3-3,-,,,39,,=$*@...........&%*=,=,--9-39,,3,3,=#+....+&*>9aadbbe|bbda03=*&..........&*=99dbbbbebddaa9>>#@+............&#=3!dbbbebddaac9**@.........",
+".......+@&##$>---,,-,,,>=#&&+......+&#=,>,33>>==$==>3-,-3,,-$%........@&#%*>,,-,3--,,==*%&+...........@%%#*>>3,-3--,,>=#%@&...........&&##=>-,--3->,,==#&@@..........+&&%#=>>-,-3,,,,==#@+..........+&&#*>=>,3,,3--,,,=*%&+...................++@@@+..............&$,;!''_~~'!!;---,,>,=$#&+............+%#**$,,,-3--3-,,>,=*&..........+@##$>-33333-3-333>=#@........@&@#*=,-,-,3-3--,-,,=#+...........+&#>=>3,3-,9-3-,,>==%......+%>;;)~]{{(){);';,*&..........&$>;;))(]{{{();!;-$%+.............&=-;;'){({({~~!;-=%+.........",
+".........++@%#**#$$*%#%#@@+..........@%%#$#*###%%&##%%$$**%%%+........+..+&%%#*$***%##%%+.................&&###%#**%%%&&++...............@&#%##*%%#%#%&+++.............+++@%%%#%%%%%%%&@+............@&@@@%$$*$$$**##%%&+........................+.................&*==-,-,->=>=>***%#%%&+...............+.+@%%##%%****#%##%&..............+@%%%%%*##*****%%@...........++&%%*$*****#*$%%%&@..............@&*#*$#$===**#%#%&+.......@#$>=-,,---,=-=$#@............&*$>,,->-;---->#%&@+..............@*=>-,---->-->$*%@..........",
+".....................+.+.................+......+........+..................+.....+............................+..+........................+++....+.............................++++...........................+.....................................................+.+.+.++++.+.++.+................................++..........................++.....+.....................+...+...+.+.....................+..+++++..+............+++++@@+@+.+.+...............+++++@@++++..+++..................+..+++@@@+.+...............",
+"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................",
+"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................",
+"......................................................+@%%%%@.......................................................................................................................................................................................................................................................................+&%%%%%%#%%%%#%%#%@+........................................................................................................................................................................",
+"................................................+&%**=>-;;;--$%...........................................................................................................................................................................+@*****#%+...................@%***$$**$**$*#%@.................+@@+.....................+#>,;;;!;;;;;;!!;!;;->=*#&.......................................................................................................................+@&%%%%+.....................................",
+"........................................+@@&%%%##*=--;!)))~~;-*+.........+@@&&@+@@&%%@+..................................................................................................................................................+%=>----=##@.................&#*=,---;;-;--->=*%@..........+&%%%#**#@....................%,;~))))))))))))))))'!;,=#@...........................................++@@@@++..+@@@+..........................................................@%#*=$>=$##@...................................",
+"......................................++@&&&%#***$>,-;~)(()';-*+.......@@%&%%&&&&&%%%&@+++...........................++...............................................................................+++@@&&+...........................@#$,---,=**@+................@#$>,----------,>$%@..........@%%&#*$$#&@+..................%-;)(((()~~~_~)_~)((~!-=>%@.......................................+@@@&@&&%&@&@@@@&%@@@@+.....................................................+%#*$>>,==**@...................................",
+".....................................+&%###$>*$$>=--!(17<7}:(;$+......&&##*#####***$$$#%&+........................++&@@@@+...................................................++&&@@+.................@&%#&%#&#&+........................@#$,-;~!;->#&................+&#=-;!!!!!!!!!;;-=#@..........@&#$*>,=*#&@+................+$;b}4<4}::::|:::2}4[7:_9=$@...................+@+................@%%#%###*#$#*#######%##%&+...................................................@%*>--3;;;-*%+..................................",
+".....+++@@+......+@@@@+.............@%**$$=,333333;abeijqjpgfd-+.....+#*>>,>=*==,>>,==*$%&+..................+@@@@%&##$#$%&@+..............................................@&%%##%%&+..............+&***$*>$*$&@+......................+&$=3cbebdc,*#&+..............+%*,0dbbbbbbbbbddc-$&+.........&%$=3c99,>$%&+...............@-agjjqjihh777774hijqjjec-*&+.................+@&&&&++...........+*$***>=>,>,=>>==>>===$**%&+.................................................+#*=9;a0ddac>*@..................................",
+"....@&#*%%#%@@@&%%%####&...........&=>,339caada_addb4hjWWWoPl49&....+#=,9cc0999c90c00c9,**&..........++@@&&&&#*$=>=,33,3,>**&+......+@@&&&&&@+.++@@@@@@@@+...............+&*==,,>==$#%++..........+%$,39399393>*&+....................+%$=3014k7edc-=#&@............+#=3cd4444444744eedc=#&........+#>3cadd~a-=>*&+..............#9biqWWZoVVVVVVVVVoZWWqgb9,*@...............+%*$=****&@.........+#=3-ccc0c0c0cc9c0c00;c93,>*&................................................&*=30dbbeeeed;,#@.............+@#%&&+.............",
+"..+#=---;--,=$*==---;;->%@........&>!){]]F/rzzrrrrrsxBGLELGHAs)#...&*-;~]/^^F]F]///^//]);-=%+......+#*$>-,,--;;!~~)){{]{{)!;-*+....&*$>,--,-,>=>>---->->,$%@............+%>;!){{{{)'-,$$@+........#-!){]FF/FF{~;,*&+................+&$-;!)/zABBxrF)';-=%@..........%>;~FzAABAABBCBBys/{'-*@......+#-;'{/rzz^{~;->#&@+...........-]KALEELvCBCCBCCCHGLEELxQ(;-#+............+&$>;;!''!;-=%+.......&,~{]F/^/^^^^/^/^^^^///]{)!;*+........+@%**$*$$*$$**#%@+....................%>-!{FrssyAtsr])-*+.........@%*>>;---=%+...........",
+"..%>-;')~~';--,,-;~))~!--*&+......#;{F/rrzzyytyzzssyBCvLLLvHBz)%..&=-;~]rsysrr/rrrzzzzrF);>*&.....+#=-;-;;;!'~)){]F//rrr/F{);=%...&=--;!!!;;;;!!;;;!;!;;---$#&........+##-;~]/rr//F{!;-=*#@......+=!{F/zsxxssQ]~->*@...............&$--;!)]/xACCAsrF)!--=##+.......&$-;)/yACCBABCBCBAsr]!-=#+.....@>;!)]rsyyz/])!-,$*#%%@@+.....+,FztDLLLHCCCCCCBCCGLLLGyI]~-%.............&*-;!){{))~;-*&+......$~FrsssxyxyxyyAyyxxyyxJsrF{'=+.......+&*,---;-;;;;--=$*@+.........+@@@.....%$-!)FrxtCCCCAs/{!>+........&*=-;'~)~;-=#&..........",
+"..%==;!~~);-=>*=-;~)();-=*%@......$;(]}/[/IPJJII[//QKKOwNwwKQF;&..@*=-;([PPI[FF2/[/Q^QF(;,*$&.....+#=---;;;;'~))){1F:/[F}F(~-=#...@*=-;;;;;;;;;;;;!!!!;;-->*%&+.......@#*=-':<P65[:~;-=$$#&@+.....*!{}IPJOOO65:;,>#@..............+#*==--;~:IJwwKQ|);;-,=**&+......+%$,;1IJJKKrKzzr//F]'-**&+.....@$=-!)}<66<:(!;,>$*#&&&&@+.....>~}PwwNwJJKKIIrKKKwwNNw<2~->&............+&*>-~({{{)~;,*%+......#_[K6OJOJOJOOwOOOJOOJOO6<}|!$+......+@%$>>>-----;--,=*#&&+........@&&&+...+%*>-'([PJwJwwPQ:)-*........+&#$>-'~~!;,*#&+.........",
+"..#=-;_(1(';>=>,-;)1:(~;-*%@.....+>_12[[QQ<6YY8<[[[QQPOwwYP<2(=+..@$,-~|5665[:::}Q[QQQ}|!->*%+....%=-3;'~~~_~((1::}[QQQQQ[1_;-&...&$>3'_(_~~~~~~~~_____!;;-=>#@......+%*=-;(48OY6<}(~;,=**#%&....+=!|[5OYMSSR5:c-**@..............&*=,-;!~(}<YRY8[|_!;;--,**&+.....@#*,;|<8OP<QQIQQ[2}_;,$#%+.....&*-;'(e5Y851|_;-=*$*####%%@...+*!:<YSNSJIIIQIQIIIJOwSO<|~9*&............@%#=;_:}}1|_;=$&@......*d<8MOYYYYSSNMSSSSSRRRRO5[1'$+.....+@%**,-;;;!~~~!;-->**&@.......+&%%%&@+@&%$=;~|[6YRSSO6Q|'-%+.......+%$=-!~(((;->*&@.........",
+"..*30df4gebc99-9!df7gedc-=$&.....+>b47hhimpjqjmi74hhiXpjopm7e0=+..@=3cdeijqp7ff47hhhkh4ea;,=*@....%3abefeegffgggggiiipipiigb0;%..+&>0affgefffegeeffegefebbc3,*&....+@%**,9abgpjopi7bdc;9-,>*%@+..@,0b7ioqqUqqjfa9>*&..............#,90ad_begiqUUji7bbbdaa;3>*&.....@#*9ceiqqphkhk<h44:d0-=*%+.....&*9cdbejqqigbdc;,,,,>,>==$&@...$cfiqTTUjmilimmmmpooWqjgba9=#+...........@&$-0be47fedc-$&@......*agipjjojqqqqqWWqqqjqjjjigbc*+....+@%$>3;adbbeefebbac9,>#&+......@$,==#%%&%#*,0dfgjWUUWjpgd0>%+.......@#*,;dbfeed;-=#&.........",
+".&,a17ipjmged~adbfipnhedc3*%+....&c:klXnZoooojmhhhlmoonXmm7fba>+..#,0de7pZZnhhQhlVlVVlI7edc9=%+...>bgpjjjqjjqjooooooZZWWqqmhfd=+.+=cbgppjjjjjjqjjjqjqjjjig4bc,%+..+%*=3;cade7mXnmlkk4eb(daac,*@..@>ab7lmnooopi7ba3>*&............@,a:e47kkkloUTTWphhkkk4e|a9>%+...&*=3cd7mqZXlllVVVlk4edc3>*&.....#,cdb4ijWqpheeba'aaaaaaa09>*@.+=afjqTTTqXXXoooooZooXomhed03*@...........@$=cdekllk2ba9=$@......%3dfggghiiiipoooopimiiiggba0$.....@%>3cd:ekhijjqjjg7e|d9-*&.....+>adac-****$,3~e7mZUTUjpgfd9-%........&*,cbe7imigba9=&.........",
+".$~rsBGvDvAsrrrrzsHDDys/{~3$&....%(rtBGvLELvGHCBAACDLvGHtxsr/)-+..=)/zsxGLEDBttAACCCCCCtxrF);,#..+;VwEuuETuuuEDDLLvLEDuuEEDAxr;@+&;{IxvDEEEEEuuEuuuuEUuuuGAz]~$+..%>;~)]/rrsxBCCBCCAyyzzzrr]);*+.%;{^stCBCBHCBxKF(!-=&...........=]rsABBBBHHvEuEEGHBCBBABsr]~>@..+*;!)F^sHDDGBBABCBBtys/])'-$&...+,!]/zytGLLHtszr^//^rrrrr/]'-%++;2suuuuEDHGDvEEELEGGHGCyz^{'-%..........+#-;{/sBBAtyzF~;,%+.....#9_b}^rKsssyHCHHHAAsssKzQ2]~,+...@$-;)FzstAHDEuuuDvBtyr]~;,#@...&!rz/]);;-3;!)^stGEEEEGys^]~3%+......@*-']/yAHDDtz/);$@........",
+"+={rxHDLLLtJrrzzsyHGvAJ/{';=@....%!zABHLLLLGCCCBBBCGLLHAAsz^]'=@.+>(^zsAGLLGtyABCABCCAACyz/{!-*+.@~zGDEEEEEEEEEEEEEELEEEuLvHy^;@.#-]zAuEEuEuuEEEEEEEEEEEEGtz/'$+.&=;~)F/rzzsyBCCCCCCByssszr/);*+.&-)/zyACCCCCByr/]'-=#@.........+-]zyABCBCCCLEEEEGCCCBCCBxz/)>%..%,!))FrsGDLGBABCCCABAsr/)~;;*%..%,!]rzyBvLLCAyszzrzzzsszzrF~-#+&-^svEEEEvGCvLLLEELLCBBCts/F);%..........@*=;)/stBCBxrF);$%......&-;))F/^rzsytCBCCAyszKr/F])!>+...#,-')/sABCHvLEEEuDHByz/{';=#+..&~^sr/{!;;;;']ryBHvvELHsKF);,%......&*,;~]rxBvLvAs/{;>&........",
+".*;1IPJwwOP[:F1F2QPwOK1);-=%+....+-(/IJwNwNJKrQ^/QIOwwPI^2F(;-*+..>~F}[KOwYOIQIrKKKKKKKK/}]~;,%..+-15OYRNSNNNNwNNwNNNSNSNS6I})>+.&=':8wNNNNSNNSSRNNNNNuSMPQ2)-%..@=;'(|:}}2[IKKPKKKKI^QQQ/}{!-%..@=;_]^IKKJKKI[]{~;-*%+..........=):/rKzKzKJONNNwJPKKKJzI/F);=%..&,;!_{}QPOwPIQIKKKr^^/F{~;--=&..@=-~]2[IOww8I[[[222[Q[[/[1)-=&.+,{[6SNNNwJJwwNwNwwwwwJJI[:(;-&..........+%#,;_//Kr^F{)->#+......+$>-;;!)_|F2QKJJKQ2}]]())';-#....%=-;_]/^IzJwwNSNROKIQ}{);-*#+..+-]F}]~;-->--!|/IPwNwO5[(!-=#+......+&*=;_FQPJwO5}_-=%@........",
+"+*_25PJOJ8<42:::2[6O6<(0-=#@......$~:[5OSNY6<}}:}}<6RY6Q[}1_;=&..+>(4<56YMSY855586JO86885<}_;-*+..*_:<<5558RRNSOOOwwNSO855[:(9*++@,015MNNMO88556558pMSNNM<}(~-#+.&>;(:4<<<5558OO8886656555<1_-&..+$-'|[58O8J65Q}1(!-$$+.........+$'1[QQQIIIPJMNNR6II6JJPK[:_;=%+.*;_(1:}[6YY6<Q<PPIQQ[[}:|~';-%..@=;_}}Q<8RMO855555555I55<[|;=&.+>(45MNNROPIPOOOOOOJOOYJ5<4:~-%+.........+&*>;_1[Q[2:(;-*%+......+#*=-,;!_(|}[IKIQ}11((((__!-*+..+%=-!(}[QI<IP6OO886655Q}(!;>#+..@-(24[2d;->,-!(1[<6OO8<|!3=*@.......@%$,;(}5P8654_9-$%+........",
+".=dijjXmmlh774474ijqjgd9=$%@......$0begoqWqmfeb|bfgjqqpi74ea;=%...3fpjqqUTTUqWqqZqWqWUWqojgfac*+..%,cadbffgpqqon6mnqqjigffba9,#+.@3afiqWqjig7ffffffgjqUqjgba;,&..%3agijqqqqqqTUTUUUqUqUqWqjib9%..+$3abijqUWWqomh7fb;=*@+.........*d47hklllhmjWUUqpmmoWqqpifb;=%.+>0ee77hiqqWpimpoqjmmk5h74bbd;*+.@>0b4hhmoqUUqUqUqUqUqqqWjje0=&.+3egpqUUWjmmlhilllmoqUUWqqjgb0*+..........@*>;de77771d03*&.......+#,9c0aaabef7kl5h4e1bbbbbbb0,@..@$-abf4hhkhlmmmmiipoqopgf_0,*@..+,bgijiga;--9ade7glli5fd;,*#@.......@#$,;bijjmigba9>*%+........",
+"+$biqZnVVVVlkkhhhVoZoibc,*%+......*014loZWZm7eeeefioWWnmVh7ba3*+.@9gpWWUUTTTUWWWWUWUTTUWWomfb0>+..%=990dbfgmoZXXVXnZZXifbdac-=%..%cbgjWUWohk4eeebffgpqTWjied0>#..%cbhpZWWUUTTTTTTTUUTTTTUWZpe0$+.@=cb7pqUUUUZZXlV<ea9=*@........+>bkVVVVVlVXoWUUWoXnZWUWoVh:a,&.+,:7kllVXoUUonXoWWZnnXVVVhke1d=++#3d4hVXXZZWUUUTTTTTTTUUWWjgc,&+@9elnWUUWZnXVVVVVXXoWUUWWWoif0$..........+%*30b4hVlkeb_9=%+......%90_db1be47klVXmVhk4444k[74(9#..&3a:7klVVVVVXXXVlhmoWZomkfbc=@+.@3ekpWoibd00~de4kllVVl7b0,>#@......+&*=3cfpqZnl7109,*&@........",
+"+=eJtHCCCCCCCHHGGvvGHx^(;-*@......>{ryAGGvGtszrrrsxvuEDvGHtz/)-@.@;KtGvLEEEEDLvDDGGDDEDDDHyrF{;@..#>c'_1/IsAvGvGCHGHvAsr/](!3,&.+$)/sHEEEvHyszr^^rzsvLELvtz/{;*++$_^sAHvLvEEEEEEEEDEEDEvLvDtz:,+.&;(/sAHLDELvDHCCBzF);=%........+;^AACCCCCCHvEEELGHGDEEEDHtsF'=@&;ztBCCCGvEEvGHGEEvGGHHCBAAys^;@+>(rsBCCHBGLGDvEEEEEEDDvDGAIbc%+&~sBHDLDLDvGHHBCCBHGDvvvDDHtr{-+.........@$-!{rxBCBtsrF)-=@.....+-]rzsxsyABCBHvvvGHAtBBBBBAxr)$.+=)/ytHCCACCBHCBCBAHGDvvGGAzF;#+.@'^xGLvHsr/F/zsACHCCCAs^(;-*@......@#>-'FVADDGts/(!;,=%@.......",
+".=)FrsxtBCCCBHvLLGGCxz/';=#+.....+-{zxCCCCByzrrrrzyvuEELLGAsr{-+.+-]zABHvEELHCCCBCCCCCCCxsr:);=+..&=,-!)]/rsACCCCCCByzrF(~!--#@..#~/sHuLLvAysr^]FFrsGLLLvys/(;$+.%;]rsyBCCCHvEEEEEHAACCCHCBtr(-@+&-{/zxBCCCCCCBCCtz/~->%+.......@;rtCCBCCABAGLLLLBAAvLELvBAs/)*+@'rtCACBCvLLGBHvLLvCCCCBCCBAs^;%@,]rxBCBCCCCCCCCGLELGtxysz/F~-#+&~rACBCCvvLvHBCCCCCBCCCCCCBs/~*..........%=-~]ryABCAsrF';=&.....@;/sBBBAABACCGLEELGCCCACCCByr)$++>{rxDGHABCCCCCBAyyACBCHGvHJF;#+.@;/sGvLtxzr^rztGLGHCCAs/);-%+......+#=-!FstLLAyzF{~;->*+.......",
+".&>-'(]2^rKJJwwNwwJQ[:~->#&.......$'2^rIKII/1((((1<8wNNwNw6[1'=@..*;]2[IJwwPI//IrKzKKKI/F{~;,=%+...+&*=,;!|}QKKsKKr[](~--==*%+...+>~:5wNwJI[](';;'([6wwwJQ:~->&..+*-~(2[^IKKJNNNRPQ[QIIIKKI[{'$+.@,;(][QIKKKKJsKKQF)-,*@.........-1/IrIrI/^QJwNNOPQKONNwJKQ2);%.+-|^rrrKPOwwJJJwwwwJJsKKKrQ/F)-@+$']/KKKzIQ^Q^^IPOwO<[::|_';>*+.+-1^IKKJJwwwJKKzKKKKKKKKKQ[F~,&..........+%>-!{/^Kr/F{~->&+.....+-]^rrIrrKKzJwwNNwJKKzKrKrr^];*+.*;:5OwPIQrKKI^Q/[/^QIKPwwJ[_-%...>!26OOPQ21||}<JwJPIQ/]!-=#+........@%*-_}PwOP[:_'->*$%@+......",
+".+&=-;~1:2<IPJwwwO6[|_;=*%@......+*!|2[Q[Q}:_'!!_(26RNNNwO6[('>...#c_|}46MM522}}QQIQQ[[:(!3=*&+.....+&**-;~|}[QQII[1_;->*#%&.....+*0:5MSR6[:(!---'_:<OMY84('-*&...%=-_(:}}[Q6SNNM<}}}[Q[QQ[2(9#..@=;~(:[[QQQPPI<Q[|;-*#@........+=)2[QQQ[[225OSNO<4<8MSwO<[:'-&++=~2[[QQ5ORRPIIJwwJKIIQIQ[2:(~>@+*!(2QIIQQ}}}}}[<OO64|1_'9=$%&+.+=_2[QIIPOww6I<Q<QQQQQIQ[[1(;*...........@&*,;~:QQQ2:(!-*#+.....+=_2[[QQQQQIP8SNNY8IQQQQ[[[1(;%.+*;|<OO5[[[QQQ[}}|11[}QIOO6f_>&..+%-(<8O52:(((1<8YOIQ21_-=*@+........@*>-~:5MO<e(_;-,>**#&+.....",
+"..@$>9abbf4hmoWUWqmgba3=$#@.......*cb477kk7edaaadbgpqWWWqjmgba>+..#3abbgpqqjgff7hhhhhk7ed9,=*&+......+%$,3ab4khlhh4:a-,**%@.......=aepqTqjgfd99-9c0fiqWqpgbc3=&...&*3cab:f7hmqqWji7f7khhhhkfac#+.&>cde47khkmppmhked;,*#@.........=b7khkkk74gpqTUqiggjWWWpi7ba3&..*cbe777kpqqnimponmmhhh77eeb_c$++*0bf7hk74eeef47ijpigbac;>$$%@...=ae4kllmoWWopmmmimmmmihh7ba9#+..........@&$,cd7kih7:dc,*#+......=dbef44777k5pqWWjmh774444e|a-%++>cfijpi47kllh7ebbb:e4hijqpga>@...&,0gipgfebbbeijqji7f10-=*@........+#$,9dfiqjieb_ac;3->>*%@....",
+"..@*=3ad:4kVnoWUUWphedc3=#@.......=d4klllhke1dddbehjZWZXonXh4b,+..%9a|fhjqUjhh7kllVlllk4dc9$$&.......+#*30(e7kVVVlkba3=$%+.......@,dgpWTWohfb_c99abfpqWZog:ac,%...@*,cade4klmXZoni4khlVlVlh4b9$.+%cb4khVlVXnZomh7edc,$#@........+,1kllVllllVoWTUqpmmqUUWomk4d9#.+%cdb44kioWZXVVXnnXVlkk4ebdac3#++,b47[77k447khlhVnXm4ba0,=#&@...+>deklVXnWUUWWqqqZqooooXi7b~,=@..........@$=3afinoni7ba3>*@.....+*cad|beee[7hmXoonlh74eebbdac=@.@,b7lnnVlhmnom44e:ee7khXZWjgd9#...%>cbggefee4kkmoZol7eac,*&+.......+%*=9dekmopiffe4e|d_a9,>#@...",
+"..&>-'{F^stCHvEEEuvyz/{~;,$@.....+;rxBCBBCAszrrrzsyHLLvvCCCAy^;&..$~FrzyDuEvAtBBCCCCCCtz/);-=&.......@=-'{/stCCCCAx^{!-=&........&'QsvEEuvAsz^{)(]^JtLLEvtz/(;*+..&,-')]rstCCCGGGAttBBCBCCAs/)>++=]zABCBCCGDDGCyzrF);-=%&@......+;zAACCCCCCGDuEEEGHHEEEELCAs/)=.+%!{/rzsyvEDDHACCCCCBAxz^]{~!-*+@'ryyssssssxtCBCCHAtz/{~3=*&+....-(rsACCGEELEuEEEEDEEEvDtz/{'-%+.........%>-~FKtvuvHsr]~-=%+.....*-~({1F//zsxBCGGBtsr^/F]{)!;=&.@!rACHCCACvDDAxzzrrsABHvvEvxr(>+..%,0(2^^zzsABBHLDGxzF)!;=#+......+&=-;{^stCHAAssyBtysr/F)~->%+.",
+"..%=,;)FrzACGvLEEEGxzrF);->#@....+;^yACBBAAxzrrzsstGvGHBCBBAsr;@.+*'F^zxvuDGBAAABACACAAz]';,$&.......&>-!{/sAACAAAs/_;-$+........&!/xGuELGAyz/]))F^zALELHAK/);*...@*-;~]/sBACCCCGCAACCCHBCyzF;*++={sABCCHHGGGGtsz/]);--=*%&&+...@;ryCCCCCCAHGEEEEvBHvLLGvByz/!*+.#-']/zsxvGLHACBCBCCAysrF)';;,&.@;^zzrrrzzssBCCCCBByr]~;,$&+....+=)^sBCCHGDDuEvuDuEuELvvAK]~;,&..........%>;)/sGLLvtJr]~-#@......@,!'~~){F/rsxCCCCyz^F))'!;>=#+.&~zxACBCCBHGHttzrrrytCCCvvGy[~*...+$;')F/rzxACCGHGAJ/]';,$@.......+*>-'{rsBCBByxstBBByyzr/)!-$@.",
+"..@#*-;'{}IJwwNNNw6I[2(!;-=*%+....-(/IrrIr^/]|F2/[IPPKI[/F]])!$+..%-~(148OwPIQ/IrzKrKr^])-=$&+.......@*=-!)}rrK^^F]'->#@.........+>_[8wNwOI[:)';!_([PwwwOQ:)-=&...+&$,-!{/rKKsKJKQQIJJwwPIF{!>%+.#!FIKJwwwwwJPQ2F(~;,=>$#%&&++..+>(/^IrQrQQIJNNNYPIIIJJJKI2{!-#..@=-'(F}QPwOPIQrKIzQ^F]);-,=#&+.+*;~)~'){]F[PJwJJI[]_;,*%@+......&,)FQKKKKPP555I5666OJJP[{!-=#+..........%*-;([6wwwP[(;-*&+.......#=>==--!_(F^KKKr[])!--==#&@...@-:^KKKKIKKPP<2|||]FQrKKJPI2(,%+...%*=-')(2IPJwJP5[1~;-=%+........@#$=;)]QrIrQ[F2/^KKPPIQ1~;,*@.",
+"..@*$,;_(2<8SNNNSSO<Q2(_;->>&+...+*~:[Q[[[2:|(|1}[[QQ[:|_!;--=%+..&>;~125YY522}}[QQQQ[}(;->*@.......+%$>-;(2[Q[}11'-=#%@.........+>~:5MSM6[}|_!!~_(}5ORY6}|0->&...+%=>-;(:[III<QQQ[Q6YSY64|!->%..%;_[IJOwwJPIQ}:__'3->=*$$##%&+..*!(12[[[2}[5YNSO<}2Q<IIQ}|_;*@..@*-!(12[6OO<}2[QIQ[}|(;,**%@....+$,---;')|}5YYO6[(_-,*%@........@=!|[[QQ[4}}:224[Q<II5Q}_3=*@..........+%=,;_}5YSY5|~3=#@........@%#$$$=-;_|2QII[}_;->**&+.....&31}QIIQQQQIQ}((~~(}[QQQII[:0,@....@#>,;;(:<OYO6<}(~;>*&+.........@*=-;(}[QQ[}}(|12[6OYO5:_c,*+.",
+"..@$=-cabeiqWTTTUqolh4bbac9>#&...+$~b747hk7f:bef774747bdc-,,=*&+..&=9abeiqqjg7fe7khhh7fb93*#+.......@%*>;ab4k<7:b_c-=#&+.........+$0fpqUWjg7bdaaadbfiqWqpgba3=@...@#=,;abeklhhkhh77gpqWqpgba9=@..#cbhmnWWWqnmh4ebda09--,-=*$#%+..#9dbeeeeebfiqWUjiff7hlh4eb03*@..@*3cbf4gpqjiff7hh74ebd9-$*&......%$>=-;adbgjqWqjfda3=*%+........+*cb7khk74ffeeff77hhlhkfa3=*@..........@*=9c_gpqWqif03=*@........+&%##*>-cab4hhl7:a3,*$%@......%9b4khhkkhhh7fbdadbf7hkhhh7b0=&....@%*3;abeiqWqpgba;,$*&..........@$3;dbekk44eebb:fhpqWqigdc-=&+",
+"..#=30(e4hpqUUTUUWoXVhk4e1d'-*...+=aeklVllhk747klklll7e_9,,$$#@...&,0de7pWqoikkk<llVIlk:a9>*&......+%=3ca|4kllk4bd03>*&+.........@3dgjWTWoVh44eee}4hpqWWohbac=&..+%>9ad|eklVVVVVllhVoZWZnkb'3=&..*a4hXnZWWZZXlh74feebb_aa'0c-=&..#9ab:ee4e47pWUUqih7hlVlked!3*&..&3014khlnqomh4hllVkkeb03=#@+.....&*=>90d:7hjWWWpgba3$*&+........+*cd4khhk774447khkVVVVl4dc,$&.........%*=,;aehpWUqpbc3$$@........+@@%*>,ca14kVVVledc,>#&+......%ceklVXVVVVVl4ebb1e7lVVVVV7b0>&....&$3;abe7pZWZnhe_9,=#+..........*,0_b4hVllhk7[47hmoWWWohfd0-%.",
+".@>!)/sAAHGvLELDvLDvvGGGAts^{-@..@-{rytCHGHCAACCCCCAAsr]!c->=*@...*;{/zxGEEvHBBACCCCCBtz/);-%......#>;)/rsyBCCxz/F)!-#@..........&'/JHuEEvHAAAtxAtAAGEEEDxK/)-#+.@=)]rzsxBBCCCCCCHHvGvLvGs^{!-%.+>{zACHLDvvvGHHHHHBBtysszzr/]'$+.%;{F//rzzzxGEEEuGxxtAAxs^F);>#++>)/sAHHGGvGtxxBCCCBys^{;-=%......#>-;'{/sxCHDLLts^{;,=%.........@,~FrsyyxxyAttBACHCHCCts/);-&.......@%>-;)FrsAGDDvAK]~3=&+........&%=,;']rsyBCCBts/);-*@.......#~rsAHGGGGCAtxszzzytHHGHBAs/)-#...@$;~]^zytGGDDHs/{~c=&..........+-{FzsABHHHCCBABAGDDuEEvHtz/)=+",
+".#;)FzyDDGGHCBBtBHGLLLEuDGAs/;&.+&-)/zAGvGvGCCCACACByzF);->=*@+...*;{/zsHuDvHCCCCCCCBBys/);=&.....+*-)FzyAACAByr/]'-*+...........%!/sHDELLHCCCBCACCCvLELHAKF);#.+%;{/stBCCCCCCACCGLLDHCCxzF);$&++=]rsxCCCCCCCHGDLLLLLCCABAxsr{>++&>;'){]//zstuLEvtsszzzr/]'!-=@.+={zxHLGvGHAysstCBBBxzr)'-*&+.....%,-;~]rsBBCCBAsr]';>#@.........@>!)]/rzzsAABBCCvvvBCBAz/)-=@......@#>-;~{/sBCCHHHyr]'->&.........@%>-')/ztBCCCCAs/);=%+.......@;/sALLGLGHByszzzsAHvLGGAsr]'-&...%,;)FzxACCCCAyzF);-#+..........@;FzxtCGGvGBCBCCGLEEEEuLvByr),+",
+".%-~_[6OwwPKQ[2F2QIwwNNNwOKQ{-@..+*-~1QPOwwJJzKKzKr/])!-=$#&+.....&=;~|[8MwOKIrKKzKKrI/F~->%+......#-'(/QrzIr^F)!-=$@.............-([PMwwJPKKKKzKzKPJwwwJ[|~-*@..&-'|/rzKzIrr^/QIJwYwPKI[1!-=#+..&;~F[rKKJzJKJwwNwwwwJKKIrI/]'*...&*=,--!_126wwwO<21_)~;;-=>#@...$'2<JwwOPQ2F:FQrKr^F]);,*&&......&$,-;)][rI^[}1(~-,*&@+..........#=,-;!~(]/^zzKJwwwPKI^]!-=%.......@#*=-!)FQIKKKKI}(;,=#@.........+&$=-!(}rKzKzKIF~;=*&+........*!1<OwwwwKQ[/2F2/KJwww6Q1);,*@...%>;'{}^KKKIIQ:(;,=#+...........+-(2^KJJwwJJKzKJJwNNNSNwJKQ1;$+",
+"+*;b4<5665<}:((_(|4558O685<:~=@...%=;~|<586KIIQQQ[2:('-*#%@+......&*-!(}5YY6IQQQIIQQ[2}(;-*&+.....@*;~|}[Q[[21(;->#@+............+$;1<6OOPIQ[2[22[[Q5J88<}_0-*@.+#;(:2QQQ[}}::::[5885Q22(~-=*@+..&>~|}[[Q[Q[Q<5688OOPPIQQ[[}|;*+..@%#$$=,-!1<ORR8<(~;->>>*#&++..+>~1[566P[}((_(2Q[2}|(!-*#@.......%*,;_(1}2[}|(~;->*&+............+&#==-;!(|}QQIORS6Q[}1_3=%@.......&#>-;_(}<II<[}:(!-*%+..........@&#>-;):[QIIIQ[|'-*#@.........%-~:<5665Q2:}[4[<<<5P<[|~3,*&....#3'14[<Q[[}1(_;>$#&............@-_2Q5POJJKIIIIIPP6OMMMO6P[|;*.",
+"+>bgppig74bbdaaccadfegiiig7ba>@...&*390be777hk444e:(93=#%&+.......@*3cbeiqZpmhhhlhhk74:a;=*&......&,abe7hh44e(03=$&@+.............*9bglmlh7eb|b|b1be4hih7ba;=%@.+*0e77k74eb(dd_bf7gg7fbda9>$#+...@3ae747f4eeeffhghillllhh44ed;*...++&%#*>3cbijUWqgb09>$$*%@+.....,b7ghhg7fd000de474:ba;,*%@+......&=;abbee4e|ac-->$%+..............+&*$>-9_bf<hmoqWjhfbd;,*%@......+%*,;aefkpjpgebda3>%&...........+&#=9cdeklllhh4bc,*$@.........&=3abbffefbfgjjqpi7febdc3-=%.....>0bgppm44ebd~;=$*&@............+,d7ioonmmlhhhkh77ghi5ipojifc*.",
+".,fpqoik4ebac9999ccabf4Q4[f:a,...+&#>30d:f4h<hhk4ebac=$%&@........&=9ab7pqZoXVVVVVVlk7ed9-*&+.....#9beklllh4bd0,>#%+..............*0bklVVh7e|bddadbb4khk4ba9,$+..=bkhlllk4|ddaadbe774eb(09=*&+...%317kk44eebbee477klVVlVll7edc%....+@@%>=90fiWWUjgb03,$*%+......@3ellhk74ed0c00b774ebd03*&+.......%,0|447k4ed03,=*%++...............+&#*-9abklVXoWWjheba9,$%@+.....@*>3de4lmoZoied09>*#@...........+&$,9'b4llVVVVkbc3>%+.........@*,90abeebe7pZWWom7ebac9,*#@+...@,b7pqoml7:d09,>*%%+............@,bgjWWoXVVVlllk747[hhlnoZpf0*+",
+"@;IwDDAsz/F)';;;;'~1/zsyxszr]-+...@$,;~F^zsyBAtysz/]'-=#&+........&;~F^sGLLGCCCCCCBCtxzF)3$@......={rstBBBts/F~;-*&+..............>)^sACCtsr/F]{){]^zyyys^F~9=@.+-ryBAAAsz/F]({{Fzssz^F]);-=%....%~/xAyszrr^/F/zssxAACCCBAxz/_=+....++#>-'{IyDEEux[_93=#+.......@'sBCBxys^]'!~{rsxsz/]~;=#+.......*;{rsyyxsz/)!-,*&+.................@#$;!{/sBBHGEDHxz^]~;>*&......%>;)/stCHDDDyrF)!-=#@...........+%=-~{^sBCCCCBs^)'-#+.........@=-;'_//r/rJtDLEvts^](!93=%@....@'rxHDLGtsr])!-,=*&.............@!^xHEvGHCCBBCCtysssxtAGvEtI|=+",
+"+;rtHAxKr/{!;;---;'~{/rzzrr/{-@...@%=-~{//rssyszz/F);-$+..........@>']^sAGGHBBCBACBAyzr]'-$+.....+=)/zyAAAyzF{!-$#@..............+$;FzAAttr^{{'!!~)]^zssr/{!,#@.+-Fsxyyyzr{'~'~~]FrzrF(~;-$&+....@;/zxszr/F](]F^rzyABAAAttzrF~=+......+$-;)/yGGGxzF)-,#++.......@'ryAxszrF)!;!)/zsz/])!-$+.......+*;{Fzsssz/{';,*%@...................@$-;~FzABCCGGts/F)'-#%+.....+#=;)FzyACHGtJ/{!,>#+............+&=-')/stBBABAs/);=%+.........+&*>;!{FFFFryHDGttI])!;,#@+.....&'^sAGGAyzF);--=*@..............&;/stvvHAABBBtxyzr^zzyytGtw^)=+",
+"+$ae2e1_!;-=*#%#***=-;!~~~!;-%.....+&*,-;;')))))~';,#%+............#,;~1[<KQQ2//2/F])~';,*@.......&>;))){])~;-*#@+................&=;)]]])~!--=>>,--!)()';-*&+...#-)){{)~;-,,=,,-;'!;;->*#+.......$;~))~!;;---;!')){{F]]]{)~;>@........@#=-~:[<<410-*#@..........$!]])))!;>=>,-;))~;--=*&+........+$-!~)))~;-=*&+......................@%*,;)]F[[[[e1';-=#@+.......@%=-']F^[Q[e(;-=#&................&*>-!(]]F]]{);-$%@............+&*=-;;;;_:}[7}:~;-=*&+.......+-)2[Q4}(~;-=**&+................=~(e4[[}2FF]]])~~!~))1224:~,%.",
+".&>;;c3,,$$#%+.++@&&**>,=>=*#@......+@&*==------,>>#@..............&*=-'(:::(__'_~!;--,*%@........+*==-----,>*%+..................+#>--;--,==**%*#**=,>>>=%&+....+*,-----,=*###**=>==*##&+........&=,---,===*$>>,,--;;;;--,,=%+........+&*=-0_((a9=*#&+..........+$-----,*$#%#$=>>>=*%%@+.........+@#,-->>->**@+........................@#$=-;!!_~ac3,$#%@.........@%$-;~(||(c99,*#+..................@*=,-;;;;;--=*%&...............&%**>>,-9cc009,$#&@+........+=_(__ac3,=**&+.................+#=9'__a!'!;;;-;--,-,--;0;3>>%+",
+".+#*==**$#%&+......+@&**$**%&.........+&&*$*******%@...............@&*>9cdba9333333,==$#%@.........@%$$$>=*$$%@....................&*==,=>>*#@@@&&%#*$*##&&@+.....@#$=$*$$%%&&&&&####&@+..........@&**$**##%##*#*$**==,>>$$**@..........@&$>9c009=*$%@............#**=*$*%%@@&&%****%%&+............&**$*$$%%@..........................+&%*>,,3933=>$#%@..........+@#=-0adac3->*%@+..................+&%$==,-,,=**#@+...............++&%%#**=,-3,=**%&+..........=0a033,==*%&....................@#=,9,333-,,,=>$$*$*=>,===$*@.",
+"..&*#$**%&&+........+@&&@&@+............+@%#****#%@+...............+&#*,99c993,>,===*#&&&@.........@%%*#*$*%@@+....................+&%##$##&@+..++@@&@&&@@+.......+&##*#&&&+@++..+++.++............&%%%&&&@&@@@@&&#*******%&&...........+%#=>33,>=$%&+............&**#*&@@....+@@&&@&@+.............+&###&@@++............................&#**====>**%%%@..........+@&*,90cc3==*%@+....................+@&#*******#&@...................+&&%##$*==**&@...........+%33,,==$#&@.....................+%*=====$=$=*$**#$%##**>*$*&+.",
+"..+&%%%#&@+............+.+...............+@&%#%&&@+.................+&**=>>>=$*****##&&&&...........+&&&%#%%+......................+@&%&&&&+..........++@@+.........@&&&&&++.......................@@&@++.......+@%&#**%&&&@+...........+&&&%***$%#&@+............&&&&+........+++..+.................@@@+++..............................@&%#****%%#&@@+............+%*>>=>$**@+......................+@&&%###*%##@+....................+@&&%#%**%%+.............&$$$$**%@+.......................&#***#%%*#*%&%&&@@%&%**%#&@..",
+"....+++................................................................++@@++++.+.++......................+.............................+..........................................................................+.+++....................++++.+..........................................................................................+.++++++...................+.+@+++++................................++.............................++++................++....+...........................+++.++.+.++++.+..++++++...."};
diff --git a/clients/multi-resource.c b/clients/multi-resource.c
new file mode 100644
index 00000000..c4a0c18c
--- /dev/null
+++ b/clients/multi-resource.c
@@ -0,0 +1,600 @@
+/*
+ * Copyright © 2011 Benjamin Franzke
+ * Copyright © 2010, 2013 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
+ * 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.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <signal.h>
+#include <time.h>
+#include <sys/poll.h>
+#include <float.h>
+#include <math.h>
+
+#include <wayland-client.h>
+#include "../shared/os-compatibility.h"
+
+struct device {
+ enum { KEYBOARD, POINTER } type;
+
+ int start_time;
+ int end_time;
+ struct wl_list link;
+
+ union {
+ struct wl_keyboard *keyboard;
+ struct wl_pointer *pointer;
+ } p;
+};
+
+struct display {
+ struct wl_display *display;
+ struct wl_registry *registry;
+ struct wl_compositor *compositor;
+ struct wl_shell *shell;
+ struct wl_seat *seat;
+ struct wl_shm *shm;
+ uint32_t formats;
+ struct wl_list devices;
+};
+
+struct window {
+ struct display *display;
+ int width, height;
+ struct wl_surface *surface;
+ struct wl_shell_surface *shell_surface;
+};
+
+static void
+buffer_release(void *data, struct wl_buffer *buffer)
+{
+ wl_buffer_destroy(buffer);
+}
+
+static const struct wl_buffer_listener buffer_listener = {
+ buffer_release
+};
+
+static inline void *
+xzalloc(size_t s)
+{
+ void *p;
+
+ p = calloc(1, s);
+ if (p == NULL) {
+ fprintf(stderr, "%s: out of memory\n", program_invocation_short_name);
+ exit(EXIT_FAILURE);
+ }
+
+ return p;
+}
+
+static int
+attach_buffer(struct window *window, int width, int height)
+{
+ struct wl_shm_pool *pool;
+ struct wl_buffer *buffer;
+ int fd, size, stride;
+
+ stride = width * 4;
+ size = stride * height;
+
+ fd = os_create_anonymous_file(size);
+ if (fd < 0) {
+ fprintf(stderr, "creating a buffer file for %d B failed: %m\n",
+ size);
+ return -1;
+ }
+
+ pool = wl_shm_create_pool(window->display->shm, fd, size);
+ buffer = wl_shm_pool_create_buffer(pool, 0,
+ width, height,
+ stride,
+ WL_SHM_FORMAT_XRGB8888);
+ wl_surface_attach(window->surface, buffer, 0, 0);
+ wl_buffer_add_listener(buffer, &buffer_listener, buffer);
+ wl_shm_pool_destroy(pool);
+ close(fd);
+
+ return 0;
+}
+
+static void
+handle_ping(void *data, struct wl_shell_surface *shell_surface,
+ uint32_t serial)
+{
+ wl_shell_surface_pong(shell_surface, serial);
+}
+
+static void
+handle_configure(void *data, struct wl_shell_surface *shell_surface,
+ uint32_t edges, int32_t width, int32_t height)
+{
+}
+
+static void
+handle_popup_done(void *data, struct wl_shell_surface *shell_surface)
+{
+}
+
+static const struct wl_shell_surface_listener shell_surface_listener = {
+ handle_ping,
+ handle_configure,
+ handle_popup_done
+};
+
+static struct window *
+create_window(struct display *display, int width, int height)
+{
+ struct window *window;
+
+ window = xzalloc(sizeof *window);
+ window->display = display;
+ window->width = width;
+ window->height = height;
+ window->surface = wl_compositor_create_surface(display->compositor);
+ window->shell_surface = wl_shell_get_shell_surface(display->shell,
+ window->surface);
+
+ if (window->shell_surface)
+ wl_shell_surface_add_listener(window->shell_surface,
+ &shell_surface_listener, window);
+
+ wl_shell_surface_set_title(window->shell_surface, "simple-shm");
+
+ wl_shell_surface_set_toplevel(window->shell_surface);
+
+ wl_surface_damage(window->surface, 0, 0, width, height);
+ attach_buffer(window, width, height);
+ wl_surface_commit(window->surface);
+
+ return window;
+}
+
+static void
+destroy_window(struct window *window)
+{
+ wl_shell_surface_destroy(window->shell_surface);
+ wl_surface_destroy(window->surface);
+ free(window);
+}
+
+static void
+shm_format(void *data, struct wl_shm *wl_shm, uint32_t format)
+{
+ struct display *d = data;
+
+ d->formats |= (1 << format);
+}
+
+struct wl_shm_listener shm_listener = {
+ shm_format
+};
+
+static void
+registry_handle_global(void *data, struct wl_registry *registry,
+ uint32_t id, const char *interface, uint32_t version)
+{
+ struct display *d = data;
+
+ if (strcmp(interface, "wl_compositor") == 0) {
+ d->compositor =
+ wl_registry_bind(registry,
+ id, &wl_compositor_interface, 1);
+ } else if (strcmp(interface, "wl_shell") == 0) {
+ d->shell = wl_registry_bind(registry,
+ id, &wl_shell_interface, 1);
+ } else if (strcmp(interface, "wl_shm") == 0) {
+ d->shm = wl_registry_bind(registry,
+ id, &wl_shm_interface, 1);
+ wl_shm_add_listener(d->shm, &shm_listener, d);
+ } else if (strcmp(interface, "wl_seat") == 0 &&
+ d->seat == NULL) {
+ d->seat = wl_registry_bind(registry,
+ id, &wl_seat_interface, 3);
+ }
+}
+
+static void
+registry_handle_global_remove(void *data, struct wl_registry *registry,
+ uint32_t name)
+{
+}
+
+static const struct wl_registry_listener registry_listener = {
+ registry_handle_global,
+ registry_handle_global_remove
+};
+
+static struct display *
+create_display(void)
+{
+ struct display *display;
+
+ display = xzalloc(sizeof *display);
+ display->display = wl_display_connect(NULL);
+ assert(display->display);
+
+ display->formats = 0;
+ display->registry = wl_display_get_registry(display->display);
+ wl_registry_add_listener(display->registry,
+ &registry_listener, display);
+ wl_display_roundtrip(display->display);
+ if (display->shm == NULL) {
+ fprintf(stderr, "No wl_shm global\n");
+ exit(1);
+ }
+
+ wl_display_roundtrip(display->display);
+
+ if (!(display->formats & (1 << WL_SHM_FORMAT_XRGB8888))) {
+ fprintf(stderr, "WL_SHM_FORMAT_XRGB32 not available\n");
+ exit(1);
+ }
+
+ wl_display_get_fd(display->display);
+
+ wl_list_init(&display->devices);
+
+ return display;
+}
+
+static void
+pointer_handle_enter(void *data, struct wl_pointer *pointer,
+ uint32_t serial, struct wl_surface *surface,
+ wl_fixed_t sx_w, wl_fixed_t sy_w)
+{
+}
+
+static void
+pointer_handle_leave(void *data, struct wl_pointer *pointer,
+ uint32_t serial, struct wl_surface *surface)
+{
+}
+
+static void
+pointer_handle_motion(void *data, struct wl_pointer *pointer,
+ uint32_t time, wl_fixed_t sx_w, wl_fixed_t sy_w)
+{
+}
+
+static void
+pointer_handle_button(void *data, struct wl_pointer *pointer, uint32_t serial,
+ uint32_t time, uint32_t button, uint32_t state_w)
+{
+}
+
+static void
+pointer_handle_axis(void *data, struct wl_pointer *pointer,
+ uint32_t time, uint32_t axis, wl_fixed_t value)
+{
+}
+
+static const struct wl_pointer_listener pointer_listener = {
+ pointer_handle_enter,
+ pointer_handle_leave,
+ pointer_handle_motion,
+ pointer_handle_button,
+ pointer_handle_axis,
+};
+
+static void
+keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard,
+ uint32_t format, int fd, uint32_t size)
+{
+}
+
+static void
+keyboard_handle_enter(void *data, struct wl_keyboard *keyboard,
+ uint32_t serial, struct wl_surface *surface,
+ struct wl_array *keys)
+{
+}
+
+static void
+keyboard_handle_leave(void *data, struct wl_keyboard *keyboard,
+ uint32_t serial, struct wl_surface *surface)
+{
+}
+
+static void
+keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
+ uint32_t serial, uint32_t time, uint32_t key,
+ uint32_t state_w)
+{
+}
+
+static void
+keyboard_handle_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)
+{
+}
+
+static const struct wl_keyboard_listener keyboard_listener = {
+ keyboard_handle_keymap,
+ keyboard_handle_enter,
+ keyboard_handle_leave,
+ keyboard_handle_key,
+ keyboard_handle_modifiers,
+};
+
+static void
+start_device(struct display *display, struct device *device)
+{
+ if (display->seat == NULL)
+ return;
+
+ switch (device->type) {
+ case KEYBOARD:
+ if (device->p.keyboard == NULL) {
+ device->p.keyboard =
+ wl_seat_get_keyboard(display->seat);
+ wl_keyboard_add_listener(device->p.keyboard,
+ &keyboard_listener,
+ NULL);
+ }
+ break;
+ case POINTER:
+ if (device->p.pointer == NULL) {
+ device->p.pointer =
+ wl_seat_get_pointer(display->seat);
+ wl_pointer_add_listener(device->p.pointer,
+ &pointer_listener,
+ NULL);
+ }
+ break;
+ }
+}
+
+static void
+destroy_device(struct device *device)
+{
+ switch (device->type) {
+ case KEYBOARD:
+ if (device->p.keyboard)
+ wl_keyboard_release(device->p.keyboard);
+ break;
+ case POINTER:
+ if (device->p.pointer)
+ wl_pointer_release(device->p.pointer);
+ break;
+ }
+
+ wl_list_remove(&device->link);
+ free(device);
+}
+
+static void
+destroy_devices(struct display *display)
+{
+ struct device *device, *tmp;
+
+ wl_list_for_each_safe(device, tmp, &display->devices, link)
+ destroy_device(device);
+}
+
+static void
+destroy_display(struct display *display)
+{
+ destroy_devices(display);
+
+ if (display->shm)
+ wl_shm_destroy(display->shm);
+
+ if (display->shell)
+ wl_shell_destroy(display->shell);
+
+ if (display->seat)
+ wl_seat_destroy(display->seat);
+
+ if (display->compositor)
+ wl_compositor_destroy(display->compositor);
+
+ wl_registry_destroy(display->registry);
+ wl_display_flush(display->display);
+ wl_display_disconnect(display->display);
+ free(display);
+}
+
+static int running = 1;
+
+static void
+signal_int(int signum)
+{
+ running = 0;
+}
+
+static int
+create_device(struct display *display, const char *time_desc, int type)
+{
+ int start_time;
+ int end_time = -1;
+ char *tail;
+ struct device *device;
+
+ if (time_desc == NULL) {
+ fprintf(stderr, "missing time description\n");
+ return -1;
+ }
+
+ errno = 0;
+ start_time = strtoul(time_desc, &tail, 10);
+ if (errno)
+ goto error;
+
+ if (*tail == ':') {
+ end_time = strtoul(tail + 1, &tail, 10);
+ if (errno || *tail != '\0')
+ goto error;
+ } else if (*tail != '\0') {
+ goto error;
+ }
+
+ device = xzalloc(sizeof *device);
+ device->type = type;
+ device->start_time = start_time;
+ device->end_time = end_time;
+ wl_list_insert(&display->devices, &device->link);
+
+ return 0;
+
+error:
+ fprintf(stderr, "invalid time description\n");
+ return -1;
+}
+
+static struct timespec begin_time;
+
+static void
+reset_timer(void)
+{
+ clock_gettime(CLOCK_MONOTONIC, &begin_time);
+}
+
+static double
+read_timer(void)
+{
+ struct timespec t;
+
+ clock_gettime(CLOCK_MONOTONIC, &t);
+ return (double)(t.tv_sec - begin_time.tv_sec) +
+ 1e-9 * (t.tv_nsec - begin_time.tv_nsec);
+}
+
+static void
+main_loop(struct display *display)
+{
+ reset_timer();
+
+ while (running) {
+ struct device *device, *tmp;
+ struct pollfd fds[1];
+ double sleep_time = DBL_MAX;
+ double now;
+
+ if (wl_display_dispatch_pending(display->display) == -1)
+ break;
+ if (wl_display_flush(display->display) == -1)
+ break;
+
+ now = read_timer();
+
+ wl_list_for_each(device, &display->devices, link) {
+ double next_time = device->start_time - now;
+ if (next_time < 0.0) {
+ sleep_time = 0.0;
+ break;
+ } else if (next_time < sleep_time) {
+ sleep_time = next_time;
+ }
+ next_time = device->end_time - now;
+ if (next_time < 0.0) {
+ sleep_time = 0.0;
+ break;
+ } else if (next_time < sleep_time) {
+ sleep_time = next_time;
+ }
+ }
+
+ fds[0].fd = wl_display_get_fd(display->display);
+ fds[0].events = POLLIN;
+ fds[0].revents = 0;
+
+ poll(fds,
+ sizeof fds / sizeof fds[0],
+ sleep_time == DBL_MAX ? -1 : ceil(sleep_time * 1000.0));
+
+ if (fds[0].revents &&
+ wl_display_dispatch(display->display) == -1)
+ break;
+
+ now = read_timer();
+
+ wl_list_for_each_safe(device, tmp, &display->devices, link) {
+ if (device->start_time <= now)
+ start_device(display, device);
+ if (device->end_time >= 0 && device->end_time <= now)
+ destroy_device(device);
+ }
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ struct sigaction sigint;
+ struct display *display;
+ struct window *window;
+ int i;
+
+ display = create_display();
+ window = create_window(display, 250, 250);
+
+ for (i = 1; i < argc; i++) {
+ if (!strncmp(argv[i], "-p", 2)) {
+ char *arg;
+ if (argv[i][2]) {
+ arg = argv[i] + 2;
+ } else {
+ arg = argv[i + 1];
+ i++;
+ }
+ if (create_device(display, arg, POINTER) == -1)
+ return 1;
+ } else if (!strncmp(argv[i], "-k", 2)) {
+ char *arg;
+ if (argv[i][2]) {
+ arg = argv[i] + 2;
+ } else {
+ arg = argv[i + 1];
+ i++;
+ }
+ if (create_device(display, arg, KEYBOARD) == -1)
+ return 1;
+ } else {
+ fprintf(stderr, "unknown argument %s\n", argv[i]);
+ return 1;
+ }
+ }
+
+ sigint.sa_handler = signal_int;
+ sigemptyset(&sigint.sa_mask);
+ sigint.sa_flags = SA_RESETHAND;
+ sigaction(SIGINT, &sigint, NULL);
+
+ main_loop(display);
+
+ fprintf(stderr, "multi-resource exiting\n");
+ destroy_window(window);
+ destroy_display(display);
+
+ return 0;
+}
diff --git a/clients/nested-client.c b/clients/nested-client.c
new file mode 100644
index 00000000..1161a992
--- /dev/null
+++ b/clients/nested-client.c
@@ -0,0 +1,363 @@
+/*
+ * Copyright © 2013 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
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <wayland-egl.h>
+#include <wayland-cursor.h>
+
+#include <GLES2/gl2.h>
+#include <EGL/egl.h>
+
+struct window;
+struct seat;
+
+struct nested_client {
+ struct wl_display *display;
+ struct wl_registry *registry;
+ struct wl_compositor *compositor;
+
+ EGLDisplay egl_display;
+ EGLContext egl_context;
+ EGLConfig egl_config;
+ EGLSurface egl_surface;
+ struct program *color_program;
+
+ GLuint vert, frag, program;
+ GLuint rotation;
+ GLuint pos;
+ GLuint col;
+
+ struct wl_surface *surface;
+ struct wl_egl_window *native;
+ int width, height;
+};
+
+#define POS 0
+#define COL 1
+
+static GLuint
+create_shader(const char *source, GLenum shader_type)
+{
+ GLuint shader;
+ GLint status;
+
+ shader = glCreateShader(shader_type);
+ if (shader == 0)
+ return 0;
+
+ glShaderSource(shader, 1, (const char **) &source, NULL);
+ glCompileShader(shader);
+
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
+ if (!status) {
+ char log[1000];
+ GLsizei len;
+ glGetShaderInfoLog(shader, 1000, &len, log);
+ fprintf(stderr, "Error: compiling %s: %*s\n",
+ shader_type == GL_VERTEX_SHADER ? "vertex" : "fragment",
+ len, log);
+ return 0;
+ }
+
+ return shader;
+}
+
+static void
+create_program(struct nested_client *client,
+ const char *vert, const char *frag)
+{
+ GLint status;
+
+ client->vert = create_shader(vert, GL_VERTEX_SHADER);
+ client->frag = create_shader(frag, GL_FRAGMENT_SHADER);
+
+ client->program = glCreateProgram();
+ glAttachShader(client->program, client->frag);
+ glAttachShader(client->program, client->vert);
+ glBindAttribLocation(client->program, POS, "pos");
+ glBindAttribLocation(client->program, COL, "color");
+ glLinkProgram(client->program);
+
+ glGetProgramiv(client->program, GL_LINK_STATUS, &status);
+ if (!status) {
+ char log[1000];
+ GLsizei len;
+ glGetProgramInfoLog(client->program, 1000, &len, log);
+ fprintf(stderr, "Error: linking:\n%*s\n", len, log);
+ exit(1);
+ }
+
+ client->rotation =
+ glGetUniformLocation(client->program, "rotation");
+}
+
+static const char vertex_shader_text[] =
+ "uniform mat4 rotation;\n"
+ "attribute vec4 pos;\n"
+ "attribute vec4 color;\n"
+ "varying vec4 v_color;\n"
+ "void main() {\n"
+ " gl_Position = rotation * pos;\n"
+ " v_color = color;\n"
+ "}\n";
+
+static const char color_fragment_shader_text[] =
+ "precision mediump float;\n"
+ "varying vec4 v_color;\n"
+ "void main() {\n"
+ " gl_FragColor = v_color;\n"
+ "}\n";
+
+static void
+render_triangle(struct nested_client *client, uint32_t time)
+{
+ static const GLfloat verts[3][2] = {
+ { -0.5, -0.5 },
+ { 0.5, -0.5 },
+ { 0, 0.5 }
+ };
+ static const GLfloat colors[3][3] = {
+ { 1, 0, 0 },
+ { 0, 1, 0 },
+ { 0, 0, 1 }
+ };
+ GLfloat angle;
+ GLfloat rotation[4][4] = {
+ { 1, 0, 0, 0 },
+ { 0, 1, 0, 0 },
+ { 0, 0, 1, 0 },
+ { 0, 0, 0, 1 }
+ };
+ static const int32_t speed_div = 5;
+ static uint32_t start_time = 0;
+
+ if (client->program == 0)
+ create_program(client, vertex_shader_text,
+ color_fragment_shader_text);
+
+ if (start_time == 0)
+ start_time = time;
+
+ angle = ((time - start_time) / speed_div) % 360 * M_PI / 180.0;
+ rotation[0][0] = cos(angle);
+ rotation[0][2] = sin(angle);
+ rotation[2][0] = -sin(angle);
+ rotation[2][2] = cos(angle);
+
+ glClearColor(0.4, 0.4, 0.4, 1.0);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ glUseProgram(client->program);
+
+ glViewport(0, 0, client->width, client->height);
+
+ glUniformMatrix4fv(client->rotation, 1, GL_FALSE,
+ (GLfloat *) rotation);
+
+ glVertexAttribPointer(POS, 2, GL_FLOAT, GL_FALSE, 0, verts);
+ glVertexAttribPointer(COL, 3, GL_FLOAT, GL_FALSE, 0, colors);
+ glEnableVertexAttribArray(POS);
+ glEnableVertexAttribArray(COL);
+
+ glDrawArrays(GL_TRIANGLES, 0, 3);
+
+ glDisableVertexAttribArray(POS);
+ glDisableVertexAttribArray(COL);
+
+ glFlush();
+}
+
+static void
+frame_callback(void *data, struct wl_callback *callback, uint32_t time);
+
+static const struct wl_callback_listener frame_listener = {
+ frame_callback
+};
+
+static void
+frame_callback(void *data, struct wl_callback *callback, uint32_t time)
+{
+ struct nested_client *client = data;
+
+ if (callback)
+ wl_callback_destroy(callback);
+
+ callback = wl_surface_frame(client->surface);
+ wl_callback_add_listener(callback, &frame_listener, client);
+
+ render_triangle(client, time);
+
+ eglSwapBuffers(client->egl_display, client->egl_surface);
+}
+
+static void
+registry_handle_global(void *data, struct wl_registry *registry,
+ uint32_t name, const char *interface, uint32_t version)
+{
+ struct nested_client *client = data;
+
+ if (strcmp(interface, "wl_compositor") == 0) {
+ client->compositor =
+ wl_registry_bind(registry, name,
+ &wl_compositor_interface, 1);
+ }
+}
+
+static void
+registry_handle_global_remove(void *data, struct wl_registry *registry,
+ uint32_t name)
+{
+}
+
+static const struct wl_registry_listener registry_listener = {
+ registry_handle_global,
+ registry_handle_global_remove
+};
+
+static struct nested_client *
+nested_client_create(void)
+{
+ static const EGLint context_attribs[] = {
+ EGL_CONTEXT_CLIENT_VERSION, 2,
+ EGL_NONE
+ };
+
+ static const EGLint config_attribs[] = {
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_RED_SIZE, 1,
+ EGL_GREEN_SIZE, 1,
+ EGL_BLUE_SIZE, 1,
+ EGL_ALPHA_SIZE, 1,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_NONE
+ };
+
+ EGLint major, minor, n;
+ EGLBoolean ret;
+
+ struct nested_client *client;
+
+ client = malloc(sizeof *client);
+ if (client == NULL)
+ return NULL;
+
+ client->width = 250;
+ client->height = 250;
+
+ client->display = wl_display_connect(NULL);
+
+ client->registry = wl_display_get_registry(client->display);
+ wl_registry_add_listener(client->registry,
+ &registry_listener, client);
+
+ /* get globals */
+ wl_display_roundtrip(client->display);
+
+ client->egl_display = eglGetDisplay(client->display);
+ if (client->egl_display == NULL)
+ return NULL;
+
+ ret = eglInitialize(client->egl_display, &major, &minor);
+ if (!ret)
+ return NULL;
+ ret = eglBindAPI(EGL_OPENGL_ES_API);
+ if (!ret)
+ return NULL;
+
+ ret = eglChooseConfig(client->egl_display, config_attribs,
+ &client->egl_config, 1, &n);
+ if (!ret || n != 1)
+ return NULL;
+
+ client->egl_context = eglCreateContext(client->egl_display,
+ client->egl_config,
+ EGL_NO_CONTEXT,
+ context_attribs);
+ if (!client->egl_context)
+ return NULL;
+
+ client->surface = wl_compositor_create_surface(client->compositor);
+
+ client->native = wl_egl_window_create(client->surface,
+ client->width, client->height);
+
+ client->egl_surface =
+ eglCreateWindowSurface(client->egl_display,
+ client->egl_config,
+ client->native, NULL);
+
+ eglMakeCurrent(client->egl_display, client->egl_surface,
+ client->egl_surface, client->egl_context);
+
+ wl_egl_window_resize(client->native,
+ client->width, client->height, 0, 0);
+
+ frame_callback(client, NULL, 0);
+
+ return client;
+}
+
+static void
+nested_client_destroy(struct nested_client *client)
+{
+ eglMakeCurrent(client->egl_display,
+ EGL_NO_SURFACE, EGL_NO_SURFACE,
+ EGL_NO_CONTEXT);
+
+ wl_egl_window_destroy(client->native);
+
+ wl_surface_destroy(client->surface);
+
+ if (client->compositor)
+ wl_compositor_destroy(client->compositor);
+
+ wl_registry_destroy(client->registry);
+ wl_display_flush(client->display);
+ wl_display_disconnect(client->display);
+}
+
+int
+main(int argc, char **argv)
+{
+ struct nested_client *client;
+ int ret = 0;
+
+ if (getenv("WAYLAND_SOCKET") == NULL) {
+ fprintf(stderr,
+ "must be run by nested, don't run standalone\n");
+ return EXIT_FAILURE;
+ }
+
+ client = nested_client_create();
+ if (client == NULL)
+ return EXIT_FAILURE;
+
+ while (ret != -1)
+ ret = wl_display_dispatch(client->display);
+
+ nested_client_destroy(client);
+
+ return 0;
+}
diff --git a/clients/nested.c b/clients/nested.c
new file mode 100644
index 00000000..50852340
--- /dev/null
+++ b/clients/nested.c
@@ -0,0 +1,632 @@
+/*
+ * Copyright © 2013 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
+ * 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.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <cairo.h>
+#include <math.h>
+#include <assert.h>
+#include <pixman.h>
+#include <sys/epoll.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include <cairo-gl.h>
+
+#include <wayland-client.h>
+#define WL_HIDE_DEPRECATED
+#include <wayland-server.h>
+
+#include "window.h"
+
+#define MIN(x,y) (((x) < (y)) ? (x) : (y))
+
+struct nested {
+ struct display *display;
+ struct window *window;
+ struct widget *widget;
+ struct wl_display *child_display;
+ struct task child_task;
+
+ EGLDisplay egl_display;
+ struct program *texture_program;
+
+ struct wl_list surface_list;
+ struct wl_list frame_callback_list;
+};
+
+struct nested_region {
+ struct wl_resource *resource;
+ pixman_region32_t region;
+};
+
+struct nested_surface {
+ struct wl_resource *resource;
+ struct wl_resource *buffer_resource;
+ struct nested *nested;
+ EGLImageKHR *image;
+ GLuint texture;
+ struct wl_list link;
+ cairo_surface_t *cairo_surface;
+};
+
+struct nested_frame_callback {
+ struct wl_resource *resource;
+ struct wl_list link;
+};
+
+static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC image_target_texture_2d;
+static PFNEGLCREATEIMAGEKHRPROC create_image;
+static PFNEGLDESTROYIMAGEKHRPROC destroy_image;
+static PFNEGLBINDWAYLANDDISPLAYWL bind_display;
+static PFNEGLUNBINDWAYLANDDISPLAYWL unbind_display;
+static PFNEGLQUERYWAYLANDBUFFERWL query_buffer;
+
+static void
+frame_callback(void *data, struct wl_callback *callback, uint32_t time)
+{
+ struct nested *nested = data;
+ struct nested_frame_callback *nc, *next;
+
+ if (callback)
+ wl_callback_destroy(callback);
+
+ wl_list_for_each_safe(nc, next, &nested->frame_callback_list, link) {
+ wl_callback_send_done(nc->resource, time);
+ wl_resource_destroy(nc->resource);
+ }
+ wl_list_init(&nested->frame_callback_list);
+
+ /* FIXME: toytoolkit need a pre-block handler where we can
+ * call this. */
+ wl_display_flush_clients(nested->child_display);
+}
+
+static const struct wl_callback_listener frame_listener = {
+ frame_callback
+};
+
+static void
+redraw_handler(struct widget *widget, void *data)
+{
+ struct nested *nested = data;
+ cairo_surface_t *surface;
+ cairo_t *cr;
+ struct rectangle allocation;
+ struct wl_callback *callback;
+ struct nested_surface *s;
+
+ widget_get_allocation(nested->widget, &allocation);
+
+ surface = window_get_surface(nested->window);
+
+ cr = cairo_create(surface);
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ cairo_rectangle(cr,
+ allocation.x,
+ allocation.y,
+ allocation.width,
+ allocation.height);
+ cairo_set_source_rgba(cr, 0, 0, 0, 0.8);
+ cairo_fill(cr);
+
+ wl_list_for_each(s, &nested->surface_list, link) {
+ display_acquire_window_surface(nested->display,
+ nested->window, NULL);
+
+ glBindTexture(GL_TEXTURE_2D, s->texture);
+ image_target_texture_2d(GL_TEXTURE_2D, s->image);
+
+ display_release_window_surface(nested->display,
+ nested->window);
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+ cairo_set_source_surface(cr, s->cairo_surface,
+ allocation.x + 10,
+ allocation.y + 10);
+ cairo_rectangle(cr, allocation.x + 10,
+ allocation.y + 10,
+ allocation.width - 10,
+ allocation.height - 10);
+
+ cairo_fill(cr);
+ }
+
+ cairo_destroy(cr);
+
+ cairo_surface_destroy(surface);
+
+ callback = wl_surface_frame(window_get_wl_surface(nested->window));
+ wl_callback_add_listener(callback, &frame_listener, nested);
+}
+
+static void
+keyboard_focus_handler(struct window *window,
+ struct input *device, void *data)
+{
+ struct nested *nested = data;
+
+ window_schedule_redraw(nested->window);
+}
+
+static void
+handle_child_data(struct task *task, uint32_t events)
+{
+ struct nested *nested = container_of(task, struct nested, child_task);
+ struct wl_event_loop *loop;
+
+ loop = wl_display_get_event_loop(nested->child_display);
+
+ wl_event_loop_dispatch(loop, -1);
+ wl_display_flush_clients(nested->child_display);
+}
+
+struct nested_client {
+ struct wl_client *client;
+ pid_t pid;
+};
+
+static struct nested_client *
+launch_client(struct nested *nested, const char *path)
+{
+ int sv[2];
+ pid_t pid;
+ struct nested_client *client;
+
+ client = malloc(sizeof *client);
+ if (client == NULL)
+ return NULL;
+
+ if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) {
+ fprintf(stderr, "launch_client: "
+ "socketpair failed while launching '%s': %m\n",
+ path);
+ free(client);
+ return NULL;
+ }
+
+ pid = fork();
+ if (pid == -1) {
+ close(sv[0]);
+ close(sv[1]);
+ free(client);
+ fprintf(stderr, "launch_client: "
+ "fork failed while launching '%s': %m\n", path);
+ return NULL;
+ }
+
+ if (pid == 0) {
+ int clientfd;
+ char s[32];
+
+ /* SOCK_CLOEXEC closes both ends, so we dup the fd to
+ * get a non-CLOEXEC fd to pass through exec. */
+ clientfd = dup(sv[1]);
+ if (clientfd == -1) {
+ fprintf(stderr, "compositor: dup failed: %m\n");
+ exit(-1);
+ }
+
+ snprintf(s, sizeof s, "%d", clientfd);
+ setenv("WAYLAND_SOCKET", s, 1);
+
+ execl(path, path, NULL);
+
+ fprintf(stderr, "compositor: executing '%s' failed: %m\n",
+ path);
+ exit(-1);
+ }
+
+ close(sv[1]);
+
+ client->client = wl_client_create(nested->child_display, sv[0]);
+ if (!client->client) {
+ close(sv[0]);
+ free(client);
+ fprintf(stderr, "launch_client: "
+ "wl_client_create failed while launching '%s'.\n",
+ path);
+ return NULL;
+ }
+
+ client->pid = pid;
+
+ return client;
+}
+
+static void
+destroy_surface(struct wl_resource *resource)
+{
+ struct nested_surface *surface = wl_resource_get_user_data(resource);
+
+ free(surface);
+}
+
+static void
+surface_destroy(struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static void
+surface_attach(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *buffer_resource, int32_t sx, int32_t sy)
+{
+ struct nested_surface *surface = wl_resource_get_user_data(resource);
+ struct nested *nested = surface->nested;
+ EGLint format, width, height;
+ cairo_device_t *device;
+
+ if (surface->buffer_resource)
+ wl_buffer_send_release(surface->buffer_resource);
+
+ surface->buffer_resource = buffer_resource;
+ if (!query_buffer(nested->egl_display, (void *) buffer_resource,
+ EGL_TEXTURE_FORMAT, &format)) {
+ fprintf(stderr, "attaching non-egl wl_buffer\n");
+ return;
+ }
+
+ if (surface->image != EGL_NO_IMAGE_KHR)
+ destroy_image(nested->egl_display, surface->image);
+ if (surface->cairo_surface)
+ cairo_surface_destroy(surface->cairo_surface);
+
+ switch (format) {
+ case EGL_TEXTURE_RGB:
+ case EGL_TEXTURE_RGBA:
+ break;
+ default:
+ fprintf(stderr, "unhandled format: %x\n", format);
+ return;
+ }
+
+ surface->image = create_image(nested->egl_display, NULL,
+ EGL_WAYLAND_BUFFER_WL, buffer_resource,
+ NULL);
+ if (surface->image == EGL_NO_IMAGE_KHR) {
+ fprintf(stderr, "failed to create img\n");
+ return;
+ }
+
+ query_buffer(nested->egl_display,
+ (void *) buffer_resource, EGL_WIDTH, &width);
+ query_buffer(nested->egl_display,
+ (void *) buffer_resource, EGL_HEIGHT, &height);
+
+ device = display_get_cairo_device(nested->display);
+ surface->cairo_surface =
+ cairo_gl_surface_create_for_texture(device,
+ CAIRO_CONTENT_COLOR_ALPHA,
+ surface->texture,
+ width, height);
+
+ window_schedule_redraw(nested->window);
+}
+
+static void
+surface_damage(struct wl_client *client,
+ struct wl_resource *resource,
+ int32_t x, int32_t y, int32_t width, int32_t height)
+{
+}
+
+static void
+destroy_frame_callback(struct wl_resource *resource)
+{
+ struct nested_frame_callback *callback = wl_resource_get_user_data(resource);
+
+ wl_list_remove(&callback->link);
+ free(callback);
+}
+
+static void
+surface_frame(struct wl_client *client,
+ struct wl_resource *resource, uint32_t id)
+{
+ struct nested_frame_callback *callback;
+ struct nested_surface *surface = wl_resource_get_user_data(resource);
+ struct nested *nested = surface->nested;
+
+ callback = malloc(sizeof *callback);
+ if (callback == NULL) {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ callback->resource = wl_resource_create(client,
+ &wl_callback_interface, 1, id);
+ wl_resource_set_implementation(callback->resource, NULL, callback,
+ destroy_frame_callback);
+
+ wl_list_insert(nested->frame_callback_list.prev, &callback->link);
+}
+
+static void
+surface_set_opaque_region(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *region_resource)
+{
+ fprintf(stderr, "surface_set_opaque_region\n");
+}
+
+static void
+surface_set_input_region(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *region_resource)
+{
+ fprintf(stderr, "surface_set_input_region\n");
+}
+
+static void
+surface_commit(struct wl_client *client, struct wl_resource *resource)
+{
+}
+
+static void
+surface_set_buffer_transform(struct wl_client *client,
+ struct wl_resource *resource, int transform)
+{
+ fprintf(stderr, "surface_set_buffer_transform\n");
+}
+
+static const struct wl_surface_interface surface_interface = {
+ surface_destroy,
+ surface_attach,
+ surface_damage,
+ surface_frame,
+ surface_set_opaque_region,
+ surface_set_input_region,
+ surface_commit,
+ surface_set_buffer_transform
+};
+
+static void
+compositor_create_surface(struct wl_client *client,
+ struct wl_resource *resource, uint32_t id)
+{
+ struct nested *nested = wl_resource_get_user_data(resource);
+ struct nested_surface *surface;
+
+ surface = zalloc(sizeof *surface);
+ if (surface == NULL) {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ surface->nested = nested;
+
+ display_acquire_window_surface(nested->display,
+ nested->window, NULL);
+
+ glGenTextures(1, &surface->texture);
+ glBindTexture(GL_TEXTURE_2D, surface->texture);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+ display_release_window_surface(nested->display, nested->window);
+
+ surface->resource =
+ wl_resource_create(client, &wl_surface_interface, 1, id);
+
+ wl_resource_set_implementation(surface->resource,
+ &surface_interface, surface,
+ destroy_surface);
+
+ wl_list_insert(nested->surface_list.prev, &surface->link);
+}
+
+static void
+destroy_region(struct wl_resource *resource)
+{
+ struct nested_region *region = wl_resource_get_user_data(resource);
+
+ pixman_region32_fini(&region->region);
+ free(region);
+}
+
+static void
+region_destroy(struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static void
+region_add(struct wl_client *client, struct wl_resource *resource,
+ int32_t x, int32_t y, int32_t width, int32_t height)
+{
+ struct nested_region *region = wl_resource_get_user_data(resource);
+
+ pixman_region32_union_rect(&region->region, &region->region,
+ x, y, width, height);
+}
+
+static void
+region_subtract(struct wl_client *client, struct wl_resource *resource,
+ int32_t x, int32_t y, int32_t width, int32_t height)
+{
+ struct nested_region *region = wl_resource_get_user_data(resource);
+ pixman_region32_t rect;
+
+ pixman_region32_init_rect(&rect, x, y, width, height);
+ pixman_region32_subtract(&region->region, &region->region, &rect);
+ pixman_region32_fini(&rect);
+}
+
+static const struct wl_region_interface region_interface = {
+ region_destroy,
+ region_add,
+ region_subtract
+};
+
+static void
+compositor_create_region(struct wl_client *client,
+ struct wl_resource *resource, uint32_t id)
+{
+ struct nested_region *region;
+
+ region = malloc(sizeof *region);
+ if (region == NULL) {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ pixman_region32_init(&region->region);
+
+ region->resource =
+ wl_resource_create(client, &wl_region_interface, 1, id);
+ wl_resource_set_implementation(region->resource, &region_interface,
+ region, destroy_region);
+}
+
+static const struct wl_compositor_interface compositor_interface = {
+ compositor_create_surface,
+ compositor_create_region
+};
+
+static void
+compositor_bind(struct wl_client *client,
+ void *data, uint32_t version, uint32_t id)
+{
+ struct nested *nested = data;
+ struct wl_resource *resource;
+
+ resource = wl_resource_create(client, &wl_compositor_interface,
+ MIN(version, 3), id);
+ wl_resource_set_implementation(resource, &compositor_interface,
+ nested, NULL);
+}
+
+static int
+nested_init_compositor(struct nested *nested)
+{
+ const char *extensions;
+ struct wl_event_loop *loop;
+ int fd, ret;
+
+ wl_list_init(&nested->surface_list);
+ wl_list_init(&nested->frame_callback_list);
+ nested->child_display = wl_display_create();
+ loop = wl_display_get_event_loop(nested->child_display);
+ fd = wl_event_loop_get_fd(loop);
+ nested->child_task.run = handle_child_data;
+ display_watch_fd(nested->display, fd,
+ EPOLLIN, &nested->child_task);
+
+ if (!wl_global_create(nested->child_display,
+ &wl_compositor_interface, 1,
+ nested, compositor_bind))
+ return -1;
+
+ wl_display_init_shm(nested->child_display);
+
+ nested->egl_display = display_get_egl_display(nested->display);
+ extensions = eglQueryString(nested->egl_display, EGL_EXTENSIONS);
+ if (strstr(extensions, "EGL_WL_bind_wayland_display") == NULL) {
+ fprintf(stderr, "no EGL_WL_bind_wayland_display extension\n");
+ return -1;
+ }
+
+ bind_display = (void *) eglGetProcAddress("eglBindWaylandDisplayWL");
+ unbind_display = (void *) eglGetProcAddress("eglUnbindWaylandDisplayWL");
+ create_image = (void *) eglGetProcAddress("eglCreateImageKHR");
+ destroy_image = (void *) eglGetProcAddress("eglDestroyImageKHR");
+ query_buffer = (void *) eglGetProcAddress("eglQueryWaylandBufferWL");
+ image_target_texture_2d =
+ (void *) eglGetProcAddress("glEGLImageTargetTexture2DOES");
+
+ ret = bind_display(nested->egl_display, nested->child_display);
+ if (!ret) {
+ fprintf(stderr, "failed to bind wl_display\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static struct nested *
+nested_create(struct display *display)
+{
+ struct nested *nested;
+
+ nested = zalloc(sizeof *nested);
+ if (nested == NULL)
+ return nested;
+
+ nested->window = window_create(display);
+ nested->widget = frame_create(nested->window, nested);
+ window_set_title(nested->window, "Wayland Nested");
+ nested->display = display;
+
+ window_set_user_data(nested->window, nested);
+ widget_set_redraw_handler(nested->widget, redraw_handler);
+ window_set_keyboard_focus_handler(nested->window,
+ keyboard_focus_handler);
+
+ nested_init_compositor(nested);
+
+ widget_schedule_resize(nested->widget, 400, 400);
+
+ return nested;
+}
+
+static void
+nested_destroy(struct nested *nested)
+{
+ widget_destroy(nested->widget);
+ window_destroy(nested->window);
+ free(nested);
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct display *display;
+ struct nested *nested;
+
+ display = display_create(&argc, argv);
+ if (display == NULL) {
+ fprintf(stderr, "failed to create display: %m\n");
+ return -1;
+ }
+
+ nested = nested_create(display);
+
+ launch_client(nested, "weston-nested-client");
+
+ display_run(display);
+
+ nested_destroy(nested);
+ display_destroy(display);
+
+ return 0;
+}
diff --git a/clients/resizor.c b/clients/resizor.c
new file mode 100644
index 00000000..68e4bf9a
--- /dev/null
+++ b/clients/resizor.c
@@ -0,0 +1,294 @@
+/*
+ * Copyright © 2010 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
+ * 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.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <cairo.h>
+#include <math.h>
+#include <assert.h>
+
+#include <linux/input.h>
+#include <wayland-client.h>
+
+#include "window.h"
+
+struct spring {
+ double current;
+ double target;
+ double previous;
+};
+
+struct resizor {
+ struct display *display;
+ struct window *window;
+ struct widget *widget;
+ struct window *menu;
+ struct spring width;
+ struct spring height;
+ struct wl_callback *frame_callback;
+};
+
+static void
+spring_update(struct spring *spring)
+{
+ double current, force;
+
+ current = spring->current;
+ force = (spring->target - current) / 20.0 +
+ (spring->previous - current);
+
+ spring->current = current + (current - spring->previous) + force;
+ spring->previous = current;
+}
+
+static int
+spring_done(struct spring *spring)
+{
+ return fabs(spring->previous - spring->target) < 0.1;
+}
+
+static const struct wl_callback_listener listener;
+
+static void
+frame_callback(void *data, struct wl_callback *callback, uint32_t time)
+{
+ struct resizor *resizor = data;
+
+ assert(!callback || callback == resizor->frame_callback);
+
+ spring_update(&resizor->width);
+ spring_update(&resizor->height);
+
+ widget_schedule_resize(resizor->widget,
+ resizor->width.current + 0.5,
+ resizor->height.current + 0.5);
+
+ if (resizor->frame_callback) {
+ wl_callback_destroy(resizor->frame_callback);
+ resizor->frame_callback = NULL;
+ }
+
+ if (!spring_done(&resizor->width) || !spring_done(&resizor->height)) {
+ resizor->frame_callback =
+ wl_surface_frame(
+ window_get_wl_surface(resizor->window));
+ wl_callback_add_listener(resizor->frame_callback, &listener,
+ resizor);
+ }
+}
+
+static const struct wl_callback_listener listener = {
+ frame_callback
+};
+
+static void
+redraw_handler(struct widget *widget, void *data)
+{
+ struct resizor *resizor = data;
+ cairo_surface_t *surface;
+ cairo_t *cr;
+ struct rectangle allocation;
+
+ widget_get_allocation(resizor->widget, &allocation);
+
+ surface = window_get_surface(resizor->window);
+
+ cr = cairo_create(surface);
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ cairo_rectangle(cr,
+ allocation.x,
+ allocation.y,
+ allocation.width,
+ allocation.height);
+ cairo_set_source_rgba(cr, 0, 0, 0, 0.8);
+ cairo_fill(cr);
+ cairo_destroy(cr);
+
+ cairo_surface_destroy(surface);
+}
+
+static void
+keyboard_focus_handler(struct window *window,
+ struct input *device, void *data)
+{
+ struct resizor *resizor = data;
+
+ window_schedule_redraw(resizor->window);
+}
+
+static void
+key_handler(struct window *window, struct input *input, uint32_t time,
+ uint32_t key, uint32_t sym, enum wl_keyboard_key_state state,
+ void *data)
+{
+ struct resizor *resizor = data;
+ struct rectangle allocation;
+
+ if (state == WL_KEYBOARD_KEY_STATE_RELEASED)
+ return;
+
+ window_get_allocation(resizor->window, &allocation);
+ resizor->width.current = allocation.width;
+ if (spring_done(&resizor->width))
+ resizor->width.target = allocation.width;
+ resizor->height.current = allocation.height;
+ if (spring_done(&resizor->height))
+ resizor->height.target = allocation.height;
+
+ switch (sym) {
+ case XKB_KEY_Up:
+ if (allocation.height < 400)
+ break;
+
+ resizor->height.target = allocation.height - 200;
+ break;
+
+ case XKB_KEY_Down:
+ if (allocation.height > 1000)
+ break;
+
+ resizor->height.target = allocation.height + 200;
+ break;
+
+ case XKB_KEY_Left:
+ if (allocation.width < 400)
+ break;
+
+ resizor->width.target = allocation.width - 200;
+ break;
+
+ case XKB_KEY_Right:
+ if (allocation.width > 1000)
+ break;
+
+ resizor->width.target = allocation.width + 200;
+ break;
+
+ case XKB_KEY_Escape:
+ display_exit(resizor->display);
+ break;
+ }
+
+ if (!resizor->frame_callback)
+ frame_callback(resizor, NULL, 0);
+}
+
+static void
+menu_func(struct window *window, int index, void *user_data)
+{
+ fprintf(stderr, "picked entry %d\n", index);
+}
+
+static void
+show_menu(struct resizor *resizor, struct input *input, uint32_t time)
+{
+ int32_t x, y;
+ static const char *entries[] = {
+ "Roy", "Pris", "Leon", "Zhora"
+ };
+
+ input_get_position(input, &x, &y);
+ window_show_menu(resizor->display, input, time, resizor->window,
+ x - 10, y - 10, menu_func, entries, 4);
+}
+
+static void
+button_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ uint32_t button, enum wl_pointer_button_state state, void *data)
+{
+ struct resizor *resizor = data;
+
+ switch (button) {
+ case BTN_RIGHT:
+ if (state == WL_POINTER_BUTTON_STATE_PRESSED)
+ show_menu(resizor, input, time);
+ break;
+ }
+}
+
+static struct resizor *
+resizor_create(struct display *display)
+{
+ struct resizor *resizor;
+
+ resizor = xzalloc(sizeof *resizor);
+ resizor->window = window_create(display);
+ resizor->widget = frame_create(resizor->window, resizor);
+ window_set_title(resizor->window, "Wayland Resizor");
+ resizor->display = display;
+
+ window_set_key_handler(resizor->window, key_handler);
+ window_set_user_data(resizor->window, resizor);
+ widget_set_redraw_handler(resizor->widget, redraw_handler);
+ window_set_keyboard_focus_handler(resizor->window,
+ keyboard_focus_handler);
+
+ widget_set_button_handler(resizor->widget, button_handler);
+
+ resizor->height.previous = 400;
+ resizor->height.current = 400;
+ resizor->height.target = 400;
+
+ resizor->width.previous = 400;
+ resizor->width.current = 400;
+ resizor->width.target = 400;
+
+ widget_schedule_resize(resizor->widget, 400, 400);
+
+ return resizor;
+}
+
+static void
+resizor_destroy(struct resizor *resizor)
+{
+ if (resizor->frame_callback)
+ wl_callback_destroy(resizor->frame_callback);
+
+ widget_destroy(resizor->widget);
+ window_destroy(resizor->window);
+ free(resizor);
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct display *display;
+ struct resizor *resizor;
+
+ display = display_create(&argc, argv);
+ if (display == NULL) {
+ fprintf(stderr, "failed to create display: %m\n");
+ return -1;
+ }
+
+ resizor = resizor_create(display);
+
+ display_run(display);
+
+ resizor_destroy(resizor);
+ display_destroy(display);
+
+ return 0;
+}
diff --git a/clients/screenshot.c b/clients/screenshot.c
new file mode 100644
index 00000000..2a6d9b32
--- /dev/null
+++ b/clients/screenshot.c
@@ -0,0 +1,309 @@
+/*
+ * Copyright © 2008 Kristian Høgsberg
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * 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.
+ */
+
+#include "config.h"
+
+#include <stdint.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <cairo.h>
+
+#include <wayland-client.h>
+#include "screenshooter-client-protocol.h"
+#include "../shared/os-compatibility.h"
+
+/* The screenshooter is a good example of a custom object exposed by
+ * the compositor and serves as a test bed for implementing client
+ * side marshalling outside libwayland.so */
+
+static struct wl_shm *shm;
+static struct screenshooter *screenshooter;
+static struct wl_list output_list;
+int min_x, min_y, max_x, max_y;
+int buffer_copy_done;
+
+struct screenshooter_output {
+ struct wl_output *output;
+ struct wl_buffer *buffer;
+ int width, height, offset_x, offset_y;
+ void *data;
+ struct wl_list link;
+};
+
+static void
+display_handle_geometry(void *data,
+ struct wl_output *wl_output,
+ int x,
+ int y,
+ int physical_width,
+ int physical_height,
+ int subpixel,
+ const char *make,
+ const char *model,
+ int transform)
+{
+ struct screenshooter_output *output;
+
+ output = wl_output_get_user_data(wl_output);
+
+ if (wl_output == output->output) {
+ output->offset_x = x;
+ output->offset_y = y;
+ }
+}
+
+static void *
+xmalloc(size_t size)
+{
+ void *p;
+
+ p = malloc(size);
+ if (p == NULL) {
+ fprintf(stderr, "%s: out of memory\n",
+ program_invocation_short_name);
+ exit(EXIT_FAILURE);
+ }
+
+ return p;
+}
+
+static void
+display_handle_mode(void *data,
+ struct wl_output *wl_output,
+ uint32_t flags,
+ int width,
+ int height,
+ int refresh)
+{
+ struct screenshooter_output *output;
+
+ output = wl_output_get_user_data(wl_output);
+
+ if (wl_output == output->output && (flags & WL_OUTPUT_MODE_CURRENT)) {
+ output->width = width;
+ output->height = height;
+ }
+}
+
+static const struct wl_output_listener output_listener = {
+ display_handle_geometry,
+ display_handle_mode
+};
+
+static void
+screenshot_done(void *data, struct screenshooter *screenshooter)
+{
+ buffer_copy_done = 1;
+}
+
+static const struct screenshooter_listener screenshooter_listener = {
+ screenshot_done
+};
+
+static void
+handle_global(void *data, struct wl_registry *registry,
+ uint32_t name, const char *interface, uint32_t version)
+{
+ static struct screenshooter_output *output;
+
+ if (strcmp(interface, "wl_output") == 0) {
+ output = xmalloc(sizeof *output);
+ output->output = wl_registry_bind(registry, name,
+ &wl_output_interface, 1);
+ wl_list_insert(&output_list, &output->link);
+ wl_output_add_listener(output->output, &output_listener, output);
+ } else if (strcmp(interface, "wl_shm") == 0) {
+ shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
+ } else if (strcmp(interface, "screenshooter") == 0) {
+ screenshooter = wl_registry_bind(registry, name,
+ &screenshooter_interface, 1);
+ }
+}
+
+static void
+handle_global_remove(void *data, struct wl_registry *registry, uint32_t name)
+{
+ /* XXX: unimplemented */
+}
+
+static const struct wl_registry_listener registry_listener = {
+ handle_global,
+ handle_global_remove
+};
+
+static struct wl_buffer *
+create_shm_buffer(int width, int height, void **data_out)
+{
+ struct wl_shm_pool *pool;
+ struct wl_buffer *buffer;
+ int fd, size, stride;
+ void *data;
+
+ stride = width * 4;
+ size = stride * height;
+
+ fd = os_create_anonymous_file(size);
+ if (fd < 0) {
+ fprintf(stderr, "creating a buffer file for %d B failed: %m\n",
+ size);
+ return NULL;
+ }
+
+ data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (data == MAP_FAILED) {
+ fprintf(stderr, "mmap failed: %m\n");
+ close(fd);
+ return NULL;
+ }
+
+ pool = wl_shm_create_pool(shm, fd, size);
+ close(fd);
+ buffer = wl_shm_pool_create_buffer(pool, 0, width, height, stride,
+ WL_SHM_FORMAT_XRGB8888);
+ wl_shm_pool_destroy(pool);
+
+ *data_out = data;
+
+ return buffer;
+}
+
+static void
+write_png(int width, int height)
+{
+ int output_stride, buffer_stride, i;
+ cairo_surface_t *surface;
+ void *data, *d, *s;
+ struct screenshooter_output *output, *next;
+
+ buffer_stride = width * 4;
+
+ data = xmalloc(buffer_stride * height);
+ if (!data)
+ return;
+
+ wl_list_for_each_safe(output, next, &output_list, link) {
+ output_stride = output->width * 4;
+ s = output->data;
+ d = data + (output->offset_y - min_y) * buffer_stride +
+ (output->offset_x - min_x) * 4;
+
+ for (i = 0; i < output->height; i++) {
+ memcpy(d, s, output_stride);
+ d += buffer_stride;
+ s += output_stride;
+ }
+
+ free(output);
+ }
+
+ surface = cairo_image_surface_create_for_data(data,
+ CAIRO_FORMAT_ARGB32,
+ width, height, buffer_stride);
+ cairo_surface_write_to_png(surface, "wayland-screenshot.png");
+ cairo_surface_destroy(surface);
+ free(data);
+}
+
+static int
+set_buffer_size(int *width, int *height)
+{
+ struct screenshooter_output *output;
+ min_x = min_y = INT_MAX;
+ max_x = max_y = INT_MIN;
+ int position = 0;
+
+ wl_list_for_each_reverse(output, &output_list, link) {
+ output->offset_x = position;
+ position += output->width;
+ }
+
+ wl_list_for_each(output, &output_list, link) {
+ min_x = MIN(min_x, output->offset_x);
+ min_y = MIN(min_y, output->offset_y);
+ max_x = MAX(max_x, output->offset_x + output->width);
+ max_y = MAX(max_y, output->offset_y + output->height);
+ }
+
+ if (max_x <= min_x || max_y <= min_y)
+ return -1;
+
+ *width = max_x - min_x;
+ *height = max_y - min_y;
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ struct wl_display *display;
+ struct wl_registry *registry;
+ struct screenshooter_output *output;
+ int width, height;
+
+ if (getenv("WAYLAND_SOCKET") == NULL) {
+ fprintf(stderr, "%s must be launched by weston.\n"
+ "Use the MOD+S shortcut to take a screenshot.\n",
+ program_invocation_short_name);
+ return -1;
+ }
+
+ display = wl_display_connect(NULL);
+ if (display == NULL) {
+ fprintf(stderr, "failed to create display: %m\n");
+ return -1;
+ }
+
+ wl_list_init(&output_list);
+ registry = wl_display_get_registry(display);
+ wl_registry_add_listener(registry, &registry_listener, NULL);
+ wl_display_dispatch(display);
+ wl_display_roundtrip(display);
+ if (screenshooter == NULL) {
+ fprintf(stderr, "display doesn't support screenshooter\n");
+ return -1;
+ }
+
+ screenshooter_add_listener(screenshooter, &screenshooter_listener, screenshooter);
+
+ if (set_buffer_size(&width, &height))
+ return -1;
+
+
+ wl_list_for_each(output, &output_list, link) {
+ output->buffer = create_shm_buffer(output->width, output->height, &output->data);
+ screenshooter_shoot(screenshooter, output->output, output->buffer);
+ buffer_copy_done = 0;
+ while (!buffer_copy_done)
+ wl_display_roundtrip(display);
+ }
+
+ write_png(width, height);
+
+ return 0;
+}
diff --git a/clients/simple-egl.c b/clients/simple-egl.c
new file mode 100644
index 00000000..1fd41379
--- /dev/null
+++ b/clients/simple-egl.c
@@ -0,0 +1,781 @@
+/*
+ * Copyright © 2011 Benjamin Franzke
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <math.h>
+#include <assert.h>
+#include <signal.h>
+
+#include <linux/input.h>
+
+#include <wayland-client.h>
+#include <wayland-egl.h>
+#include <wayland-cursor.h>
+
+#include <GLES2/gl2.h>
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#ifndef EGL_EXT_swap_buffers_with_damage
+#define EGL_EXT_swap_buffers_with_damage 1
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC)(EGLDisplay dpy, EGLSurface surface, EGLint *rects, EGLint n_rects);
+#endif
+
+#ifndef EGL_EXT_buffer_age
+#define EGL_EXT_buffer_age 1
+#define EGL_BUFFER_AGE_EXT 0x313D
+#endif
+
+struct window;
+struct seat;
+
+struct display {
+ struct wl_display *display;
+ struct wl_registry *registry;
+ struct wl_compositor *compositor;
+ struct wl_shell *shell;
+ struct wl_seat *seat;
+ struct wl_pointer *pointer;
+ struct wl_touch *touch;
+ struct wl_keyboard *keyboard;
+ struct wl_shm *shm;
+ struct wl_cursor_theme *cursor_theme;
+ struct wl_cursor *default_cursor;
+ struct wl_surface *cursor_surface;
+ struct {
+ EGLDisplay dpy;
+ EGLContext ctx;
+ EGLConfig conf;
+ } egl;
+ struct window *window;
+
+ PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC swap_buffers_with_damage;
+};
+
+struct geometry {
+ int width, height;
+};
+
+struct window {
+ struct display *display;
+ struct geometry geometry, window_size;
+ struct {
+ GLuint rotation_uniform;
+ GLuint pos;
+ GLuint col;
+ } gl;
+
+ struct wl_egl_window *native;
+ struct wl_surface *surface;
+ struct wl_shell_surface *shell_surface;
+ EGLSurface egl_surface;
+ struct wl_callback *callback;
+ int fullscreen, configured, opaque;
+};
+
+static const char *vert_shader_text =
+ "uniform mat4 rotation;\n"
+ "attribute vec4 pos;\n"
+ "attribute vec4 color;\n"
+ "varying vec4 v_color;\n"
+ "void main() {\n"
+ " gl_Position = rotation * pos;\n"
+ " v_color = color;\n"
+ "}\n";
+
+static const char *frag_shader_text =
+ "precision mediump float;\n"
+ "varying vec4 v_color;\n"
+ "void main() {\n"
+ " gl_FragColor = v_color;\n"
+ "}\n";
+
+static int running = 1;
+
+static void
+init_egl(struct display *display, int opaque)
+{
+ static const EGLint context_attribs[] = {
+ EGL_CONTEXT_CLIENT_VERSION, 2,
+ EGL_NONE
+ };
+ const char *extensions;
+
+ EGLint config_attribs[] = {
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_RED_SIZE, 1,
+ EGL_GREEN_SIZE, 1,
+ EGL_BLUE_SIZE, 1,
+ EGL_ALPHA_SIZE, 1,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_NONE
+ };
+
+ EGLint major, minor, n;
+ EGLBoolean ret;
+
+ if (opaque)
+ config_attribs[9] = 0;
+
+ display->egl.dpy = eglGetDisplay(display->display);
+ assert(display->egl.dpy);
+
+ ret = eglInitialize(display->egl.dpy, &major, &minor);
+ assert(ret == EGL_TRUE);
+ ret = eglBindAPI(EGL_OPENGL_ES_API);
+ assert(ret == EGL_TRUE);
+
+ ret = eglChooseConfig(display->egl.dpy, config_attribs,
+ &display->egl.conf, 1, &n);
+ assert(ret && n == 1);
+
+ display->egl.ctx = eglCreateContext(display->egl.dpy,
+ display->egl.conf,
+ EGL_NO_CONTEXT, context_attribs);
+ assert(display->egl.ctx);
+
+ display->swap_buffers_with_damage = NULL;
+ extensions = eglQueryString(display->egl.dpy, EGL_EXTENSIONS);
+ if (extensions &&
+ strstr(extensions, "EGL_EXT_swap_buffers_with_damage") &&
+ strstr(extensions, "EGL_EXT_buffer_age"))
+ display->swap_buffers_with_damage =
+ (PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC)
+ eglGetProcAddress("eglSwapBuffersWithDamageEXT");
+
+ if (display->swap_buffers_with_damage)
+ printf("has EGL_EXT_buffer_age and EGL_EXT_swap_buffers_with_damage\n");
+
+}
+
+static void
+fini_egl(struct display *display)
+{
+ eglTerminate(display->egl.dpy);
+ eglReleaseThread();
+}
+
+static GLuint
+create_shader(struct window *window, const char *source, GLenum shader_type)
+{
+ GLuint shader;
+ GLint status;
+
+ shader = glCreateShader(shader_type);
+ assert(shader != 0);
+
+ glShaderSource(shader, 1, (const char **) &source, NULL);
+ glCompileShader(shader);
+
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
+ if (!status) {
+ char log[1000];
+ GLsizei len;
+ glGetShaderInfoLog(shader, 1000, &len, log);
+ fprintf(stderr, "Error: compiling %s: %*s\n",
+ shader_type == GL_VERTEX_SHADER ? "vertex" : "fragment",
+ len, log);
+ exit(1);
+ }
+
+ return shader;
+}
+
+static void
+init_gl(struct window *window)
+{
+ GLuint frag, vert;
+ GLuint program;
+ GLint status;
+
+ frag = create_shader(window, frag_shader_text, GL_FRAGMENT_SHADER);
+ vert = create_shader(window, vert_shader_text, GL_VERTEX_SHADER);
+
+ program = glCreateProgram();
+ glAttachShader(program, frag);
+ glAttachShader(program, vert);
+ glLinkProgram(program);
+
+ glGetProgramiv(program, GL_LINK_STATUS, &status);
+ if (!status) {
+ char log[1000];
+ GLsizei len;
+ glGetProgramInfoLog(program, 1000, &len, log);
+ fprintf(stderr, "Error: linking:\n%*s\n", len, log);
+ exit(1);
+ }
+
+ glUseProgram(program);
+
+ window->gl.pos = 0;
+ window->gl.col = 1;
+
+ glBindAttribLocation(program, window->gl.pos, "pos");
+ glBindAttribLocation(program, window->gl.col, "color");
+ glLinkProgram(program);
+
+ window->gl.rotation_uniform =
+ glGetUniformLocation(program, "rotation");
+}
+
+static void
+handle_ping(void *data, struct wl_shell_surface *shell_surface,
+ uint32_t serial)
+{
+ wl_shell_surface_pong(shell_surface, serial);
+}
+
+static void
+handle_configure(void *data, struct wl_shell_surface *shell_surface,
+ uint32_t edges, int32_t width, int32_t height)
+{
+ struct window *window = data;
+
+ if (window->native)
+ wl_egl_window_resize(window->native, width, height, 0, 0);
+
+ window->geometry.width = width;
+ window->geometry.height = height;
+
+ if (!window->fullscreen)
+ window->window_size = window->geometry;
+}
+
+static void
+handle_popup_done(void *data, struct wl_shell_surface *shell_surface)
+{
+}
+
+static const struct wl_shell_surface_listener shell_surface_listener = {
+ handle_ping,
+ handle_configure,
+ handle_popup_done
+};
+
+static void
+redraw(void *data, struct wl_callback *callback, uint32_t time);
+
+static void
+configure_callback(void *data, struct wl_callback *callback, uint32_t time)
+{
+ struct window *window = data;
+
+ wl_callback_destroy(callback);
+
+ window->configured = 1;
+
+ if (window->callback == NULL)
+ redraw(data, NULL, time);
+}
+
+static struct wl_callback_listener configure_callback_listener = {
+ configure_callback,
+};
+
+static void
+toggle_fullscreen(struct window *window, int fullscreen)
+{
+ struct wl_callback *callback;
+
+ window->fullscreen = fullscreen;
+ window->configured = 0;
+
+ if (fullscreen) {
+ wl_shell_surface_set_fullscreen(window->shell_surface,
+ WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT,
+ 0, NULL);
+ } else {
+ wl_shell_surface_set_toplevel(window->shell_surface);
+ handle_configure(window, window->shell_surface, 0,
+ window->window_size.width,
+ window->window_size.height);
+ }
+
+ callback = wl_display_sync(window->display->display);
+ wl_callback_add_listener(callback, &configure_callback_listener,
+ window);
+}
+
+static void
+create_surface(struct window *window)
+{
+ struct display *display = window->display;
+ EGLBoolean ret;
+
+ window->surface = wl_compositor_create_surface(display->compositor);
+ window->shell_surface = wl_shell_get_shell_surface(display->shell,
+ window->surface);
+
+ wl_shell_surface_add_listener(window->shell_surface,
+ &shell_surface_listener, window);
+
+ window->native =
+ wl_egl_window_create(window->surface,
+ window->window_size.width,
+ window->window_size.height);
+ window->egl_surface =
+ eglCreateWindowSurface(display->egl.dpy,
+ display->egl.conf,
+ window->native, NULL);
+
+ wl_shell_surface_set_title(window->shell_surface, "simple-egl");
+
+ ret = eglMakeCurrent(window->display->egl.dpy, window->egl_surface,
+ window->egl_surface, window->display->egl.ctx);
+ assert(ret == EGL_TRUE);
+
+ toggle_fullscreen(window, window->fullscreen);
+}
+
+static void
+destroy_surface(struct window *window)
+{
+ /* Required, otherwise segfault in egl_dri2.c: dri2_make_current()
+ * on eglReleaseThread(). */
+ eglMakeCurrent(window->display->egl.dpy, EGL_NO_SURFACE, EGL_NO_SURFACE,
+ EGL_NO_CONTEXT);
+
+ eglDestroySurface(window->display->egl.dpy, window->egl_surface);
+ wl_egl_window_destroy(window->native);
+
+ wl_shell_surface_destroy(window->shell_surface);
+ wl_surface_destroy(window->surface);
+
+ if (window->callback)
+ wl_callback_destroy(window->callback);
+}
+
+static const struct wl_callback_listener frame_listener;
+
+static void
+redraw(void *data, struct wl_callback *callback, uint32_t time)
+{
+ struct window *window = data;
+ struct display *display = window->display;
+ static const GLfloat verts[3][2] = {
+ { -0.5, -0.5 },
+ { 0.5, -0.5 },
+ { 0, 0.5 }
+ };
+ static const GLfloat colors[3][3] = {
+ { 1, 0, 0 },
+ { 0, 1, 0 },
+ { 0, 0, 1 }
+ };
+ GLfloat angle;
+ GLfloat rotation[4][4] = {
+ { 1, 0, 0, 0 },
+ { 0, 1, 0, 0 },
+ { 0, 0, 1, 0 },
+ { 0, 0, 0, 1 }
+ };
+ static const int32_t speed_div = 5;
+ static uint32_t start_time = 0;
+ struct wl_region *region;
+ EGLint rect[4];
+ EGLint buffer_age = 0;
+
+ assert(window->callback == callback);
+ window->callback = NULL;
+
+ if (callback)
+ wl_callback_destroy(callback);
+
+ if (!window->configured)
+ return;
+
+ if (start_time == 0)
+ start_time = time;
+
+ angle = ((time-start_time) / speed_div) % 360 * M_PI / 180.0;
+ rotation[0][0] = cos(angle);
+ rotation[0][2] = sin(angle);
+ rotation[2][0] = -sin(angle);
+ rotation[2][2] = cos(angle);
+
+ if (display->swap_buffers_with_damage)
+ eglQuerySurface(display->egl.dpy, window->egl_surface,
+ EGL_BUFFER_AGE_EXT, &buffer_age);
+
+ glViewport(0, 0, window->geometry.width, window->geometry.height);
+
+ glUniformMatrix4fv(window->gl.rotation_uniform, 1, GL_FALSE,
+ (GLfloat *) rotation);
+
+ glClearColor(0.0, 0.0, 0.0, 0.5);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ glVertexAttribPointer(window->gl.pos, 2, GL_FLOAT, GL_FALSE, 0, verts);
+ glVertexAttribPointer(window->gl.col, 3, GL_FLOAT, GL_FALSE, 0, colors);
+ glEnableVertexAttribArray(window->gl.pos);
+ glEnableVertexAttribArray(window->gl.col);
+
+ glDrawArrays(GL_TRIANGLES, 0, 3);
+
+ glDisableVertexAttribArray(window->gl.pos);
+ glDisableVertexAttribArray(window->gl.col);
+
+ if (window->opaque || window->fullscreen) {
+ region = wl_compositor_create_region(window->display->compositor);
+ wl_region_add(region, 0, 0,
+ window->geometry.width,
+ window->geometry.height);
+ wl_surface_set_opaque_region(window->surface, region);
+ wl_region_destroy(region);
+ } else {
+ wl_surface_set_opaque_region(window->surface, NULL);
+ }
+
+ window->callback = wl_surface_frame(window->surface);
+ wl_callback_add_listener(window->callback, &frame_listener, window);
+
+ if (display->swap_buffers_with_damage && buffer_age > 0) {
+ rect[0] = window->geometry.width / 4 - 1;
+ rect[1] = window->geometry.height / 4 - 1;
+ rect[2] = window->geometry.width / 2 + 2;
+ rect[3] = window->geometry.height / 2 + 2;
+ display->swap_buffers_with_damage(display->egl.dpy,
+ window->egl_surface,
+ rect, 1);
+ } else {
+ eglSwapBuffers(display->egl.dpy, window->egl_surface);
+ }
+}
+
+static const struct wl_callback_listener frame_listener = {
+ redraw
+};
+
+static void
+pointer_handle_enter(void *data, struct wl_pointer *pointer,
+ uint32_t serial, struct wl_surface *surface,
+ wl_fixed_t sx, wl_fixed_t sy)
+{
+ struct display *display = data;
+ struct wl_buffer *buffer;
+ struct wl_cursor *cursor = display->default_cursor;
+ struct wl_cursor_image *image;
+
+ if (display->window->fullscreen)
+ wl_pointer_set_cursor(pointer, serial, NULL, 0, 0);
+ else if (cursor) {
+ image = display->default_cursor->images[0];
+ buffer = wl_cursor_image_get_buffer(image);
+ wl_pointer_set_cursor(pointer, serial,
+ display->cursor_surface,
+ image->hotspot_x,
+ image->hotspot_y);
+ wl_surface_attach(display->cursor_surface, buffer, 0, 0);
+ wl_surface_damage(display->cursor_surface, 0, 0,
+ image->width, image->height);
+ wl_surface_commit(display->cursor_surface);
+ }
+}
+
+static void
+pointer_handle_leave(void *data, struct wl_pointer *pointer,
+ uint32_t serial, struct wl_surface *surface)
+{
+}
+
+static void
+pointer_handle_motion(void *data, struct wl_pointer *pointer,
+ uint32_t time, wl_fixed_t sx, wl_fixed_t sy)
+{
+}
+
+static void
+pointer_handle_button(void *data, struct wl_pointer *wl_pointer,
+ uint32_t serial, uint32_t time, uint32_t button,
+ uint32_t state)
+{
+ struct display *display = data;
+
+ if (button == BTN_LEFT && state == WL_POINTER_BUTTON_STATE_PRESSED)
+ wl_shell_surface_move(display->window->shell_surface,
+ display->seat, serial);
+}
+
+static void
+pointer_handle_axis(void *data, struct wl_pointer *wl_pointer,
+ uint32_t time, uint32_t axis, wl_fixed_t value)
+{
+}
+
+static const struct wl_pointer_listener pointer_listener = {
+ pointer_handle_enter,
+ pointer_handle_leave,
+ pointer_handle_motion,
+ pointer_handle_button,
+ pointer_handle_axis,
+};
+
+static void
+touch_handle_down(void *data, struct wl_touch *wl_touch,
+ uint32_t serial, uint32_t time, struct wl_surface *surface,
+ int32_t id, wl_fixed_t x_w, wl_fixed_t y_w)
+{
+ struct display *d = (struct display *)data;
+
+ wl_shell_surface_move(d->window->shell_surface, d->seat, serial);
+}
+
+static void
+touch_handle_up(void *data, struct wl_touch *wl_touch,
+ uint32_t serial, uint32_t time, int32_t id)
+{
+}
+
+static void
+touch_handle_motion(void *data, struct wl_touch *wl_touch,
+ uint32_t time, int32_t id, wl_fixed_t x_w, wl_fixed_t y_w)
+{
+}
+
+static void
+touch_handle_frame(void *data, struct wl_touch *wl_touch)
+{
+}
+
+static void
+touch_handle_cancel(void *data, struct wl_touch *wl_touch)
+{
+}
+
+static const struct wl_touch_listener touch_listener = {
+ touch_handle_down,
+ touch_handle_up,
+ touch_handle_motion,
+ touch_handle_frame,
+ touch_handle_cancel,
+};
+
+static void
+keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard,
+ uint32_t format, int fd, uint32_t size)
+{
+}
+
+static void
+keyboard_handle_enter(void *data, struct wl_keyboard *keyboard,
+ uint32_t serial, struct wl_surface *surface,
+ struct wl_array *keys)
+{
+}
+
+static void
+keyboard_handle_leave(void *data, struct wl_keyboard *keyboard,
+ uint32_t serial, struct wl_surface *surface)
+{
+}
+
+static void
+keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
+ uint32_t serial, uint32_t time, uint32_t key,
+ uint32_t state)
+{
+ struct display *d = data;
+
+ if (key == KEY_F11 && state)
+ toggle_fullscreen(d->window, d->window->fullscreen ^ 1);
+ else if (key == KEY_ESC && state)
+ running = 0;
+}
+
+static void
+keyboard_handle_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)
+{
+}
+
+static const struct wl_keyboard_listener keyboard_listener = {
+ keyboard_handle_keymap,
+ keyboard_handle_enter,
+ keyboard_handle_leave,
+ keyboard_handle_key,
+ keyboard_handle_modifiers,
+};
+
+static void
+seat_handle_capabilities(void *data, struct wl_seat *seat,
+ enum wl_seat_capability caps)
+{
+ struct display *d = data;
+
+ if ((caps & WL_SEAT_CAPABILITY_POINTER) && !d->pointer) {
+ d->pointer = wl_seat_get_pointer(seat);
+ wl_pointer_add_listener(d->pointer, &pointer_listener, d);
+ } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && d->pointer) {
+ wl_pointer_destroy(d->pointer);
+ d->pointer = NULL;
+ }
+
+ if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !d->keyboard) {
+ d->keyboard = wl_seat_get_keyboard(seat);
+ wl_keyboard_add_listener(d->keyboard, &keyboard_listener, d);
+ } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && d->keyboard) {
+ wl_keyboard_destroy(d->keyboard);
+ d->keyboard = NULL;
+ }
+
+ if ((caps & WL_SEAT_CAPABILITY_TOUCH) && !d->touch) {
+ d->touch = wl_seat_get_touch(seat);
+ wl_touch_set_user_data(d->touch, d);
+ wl_touch_add_listener(d->touch, &touch_listener, d);
+ } else if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && d->touch) {
+ wl_touch_destroy(d->touch);
+ d->touch = NULL;
+ }
+}
+
+static const struct wl_seat_listener seat_listener = {
+ seat_handle_capabilities,
+};
+
+static void
+registry_handle_global(void *data, struct wl_registry *registry,
+ uint32_t name, const char *interface, uint32_t version)
+{
+ struct display *d = data;
+
+ if (strcmp(interface, "wl_compositor") == 0) {
+ d->compositor =
+ wl_registry_bind(registry, name,
+ &wl_compositor_interface, 1);
+ } else if (strcmp(interface, "wl_shell") == 0) {
+ d->shell = wl_registry_bind(registry, name,
+ &wl_shell_interface, 1);
+ } else if (strcmp(interface, "wl_seat") == 0) {
+ d->seat = wl_registry_bind(registry, name,
+ &wl_seat_interface, 1);
+ wl_seat_add_listener(d->seat, &seat_listener, d);
+ } else if (strcmp(interface, "wl_shm") == 0) {
+ d->shm = wl_registry_bind(registry, name,
+ &wl_shm_interface, 1);
+ d->cursor_theme = wl_cursor_theme_load(NULL, 32, d->shm);
+ d->default_cursor =
+ wl_cursor_theme_get_cursor(d->cursor_theme, "left_ptr");
+ }
+}
+
+static void
+registry_handle_global_remove(void *data, struct wl_registry *registry,
+ uint32_t name)
+{
+}
+
+static const struct wl_registry_listener registry_listener = {
+ registry_handle_global,
+ registry_handle_global_remove
+};
+
+static void
+signal_int(int signum)
+{
+ running = 0;
+}
+
+static void
+usage(int error_code)
+{
+ fprintf(stderr, "Usage: simple-egl [OPTIONS]\n\n"
+ " -f\tRun in fullscreen mode\n"
+ " -o\tCreate an opaque surface\n"
+ " -h\tThis help text\n\n");
+
+ exit(error_code);
+}
+
+int
+main(int argc, char **argv)
+{
+ struct sigaction sigint;
+ struct display display = { 0 };
+ struct window window = { 0 };
+ int i, ret = 0;
+
+ window.display = &display;
+ display.window = &window;
+ window.window_size.width = 250;
+ window.window_size.height = 250;
+
+ for (i = 1; i < argc; i++) {
+ if (strcmp("-f", argv[i]) == 0)
+ window.fullscreen = 1;
+ else if (strcmp("-o", argv[i]) == 0)
+ window.opaque = 1;
+ else if (strcmp("-h", argv[i]) == 0)
+ usage(EXIT_SUCCESS);
+ else
+ usage(EXIT_FAILURE);
+ }
+
+ display.display = wl_display_connect(NULL);
+ assert(display.display);
+
+ display.registry = wl_display_get_registry(display.display);
+ wl_registry_add_listener(display.registry,
+ &registry_listener, &display);
+
+ wl_display_dispatch(display.display);
+
+ init_egl(&display, window.opaque);
+ create_surface(&window);
+ init_gl(&window);
+
+ display.cursor_surface =
+ wl_compositor_create_surface(display.compositor);
+
+ sigint.sa_handler = signal_int;
+ sigemptyset(&sigint.sa_mask);
+ sigint.sa_flags = SA_RESETHAND;
+ sigaction(SIGINT, &sigint, NULL);
+
+ while (running && ret != -1)
+ ret = wl_display_dispatch(display.display);
+
+ fprintf(stderr, "simple-egl exiting\n");
+
+ destroy_surface(&window);
+ fini_egl(&display);
+
+ wl_surface_destroy(display.cursor_surface);
+ if (display.cursor_theme)
+ wl_cursor_theme_destroy(display.cursor_theme);
+
+ if (display.shell)
+ wl_shell_destroy(display.shell);
+
+ if (display.compositor)
+ wl_compositor_destroy(display.compositor);
+
+ wl_registry_destroy(display.registry);
+ wl_display_flush(display.display);
+ wl_display_disconnect(display.display);
+
+ return 0;
+}
diff --git a/clients/simple-shm.c b/clients/simple-shm.c
new file mode 100644
index 00000000..81bb54ea
--- /dev/null
+++ b/clients/simple-shm.c
@@ -0,0 +1,427 @@
+/*
+ * Copyright © 2011 Benjamin Franzke
+ * Copyright © 2010 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
+ * 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.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <signal.h>
+
+#include <wayland-client.h>
+#include "../shared/os-compatibility.h"
+
+struct display {
+ struct wl_display *display;
+ struct wl_registry *registry;
+ struct wl_compositor *compositor;
+ struct wl_shell *shell;
+ struct wl_shm *shm;
+ uint32_t formats;
+};
+
+struct buffer {
+ struct wl_buffer *buffer;
+ void *shm_data;
+ int busy;
+};
+
+struct window {
+ struct display *display;
+ int width, height;
+ struct wl_surface *surface;
+ struct wl_shell_surface *shell_surface;
+ struct buffer buffers[2];
+ struct buffer *prev_buffer;
+ struct wl_callback *callback;
+};
+
+static void
+buffer_release(void *data, struct wl_buffer *buffer)
+{
+ struct buffer *mybuf = data;
+
+ mybuf->busy = 0;
+}
+
+static const struct wl_buffer_listener buffer_listener = {
+ buffer_release
+};
+
+static int
+create_shm_buffer(struct display *display, struct buffer *buffer,
+ int width, int height, uint32_t format)
+{
+ struct wl_shm_pool *pool;
+ int fd, size, stride;
+ void *data;
+
+ stride = width * 4;
+ size = stride * height;
+
+ fd = os_create_anonymous_file(size);
+ if (fd < 0) {
+ fprintf(stderr, "creating a buffer file for %d B failed: %m\n",
+ size);
+ return -1;
+ }
+
+ data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (data == MAP_FAILED) {
+ fprintf(stderr, "mmap failed: %m\n");
+ close(fd);
+ return -1;
+ }
+
+ pool = wl_shm_create_pool(display->shm, fd, size);
+ buffer->buffer = wl_shm_pool_create_buffer(pool, 0,
+ width, height,
+ stride, format);
+ wl_buffer_add_listener(buffer->buffer, &buffer_listener, buffer);
+ wl_shm_pool_destroy(pool);
+ close(fd);
+
+ buffer->shm_data = data;
+
+ return 0;
+}
+
+static void
+handle_ping(void *data, struct wl_shell_surface *shell_surface,
+ uint32_t serial)
+{
+ wl_shell_surface_pong(shell_surface, serial);
+}
+
+static void
+handle_configure(void *data, struct wl_shell_surface *shell_surface,
+ uint32_t edges, int32_t width, int32_t height)
+{
+}
+
+static void
+handle_popup_done(void *data, struct wl_shell_surface *shell_surface)
+{
+}
+
+static const struct wl_shell_surface_listener shell_surface_listener = {
+ handle_ping,
+ handle_configure,
+ handle_popup_done
+};
+
+static struct window *
+create_window(struct display *display, int width, int height)
+{
+ struct window *window;
+
+ window = calloc(1, sizeof *window);
+ if (!window)
+ return NULL;
+
+ window->callback = NULL;
+ window->display = display;
+ window->width = width;
+ window->height = height;
+ window->surface = wl_compositor_create_surface(display->compositor);
+ window->shell_surface = wl_shell_get_shell_surface(display->shell,
+ window->surface);
+
+ if (window->shell_surface)
+ wl_shell_surface_add_listener(window->shell_surface,
+ &shell_surface_listener, window);
+
+ wl_shell_surface_set_title(window->shell_surface, "simple-shm");
+
+ wl_shell_surface_set_toplevel(window->shell_surface);
+
+ return window;
+}
+
+static void
+destroy_window(struct window *window)
+{
+ if (window->callback)
+ wl_callback_destroy(window->callback);
+
+ if (window->buffers[0].buffer)
+ wl_buffer_destroy(window->buffers[0].buffer);
+ if (window->buffers[1].buffer)
+ wl_buffer_destroy(window->buffers[1].buffer);
+
+ wl_shell_surface_destroy(window->shell_surface);
+ wl_surface_destroy(window->surface);
+ free(window);
+}
+
+static struct buffer *
+window_next_buffer(struct window *window)
+{
+ struct buffer *buffer;
+ int ret = 0;
+
+ if (!window->buffers[0].busy)
+ buffer = &window->buffers[0];
+ else if (!window->buffers[1].busy)
+ buffer = &window->buffers[1];
+ else
+ return NULL;
+
+ if (!buffer->buffer) {
+ ret = create_shm_buffer(window->display, buffer,
+ window->width, window->height,
+ WL_SHM_FORMAT_XRGB8888);
+
+ if (ret < 0)
+ return NULL;
+
+ /* paint the padding */
+ memset(buffer->shm_data, 0xff,
+ window->width * window->height * 4);
+ }
+
+ return buffer;
+}
+
+static void
+paint_pixels(void *image, int padding, int width, int height, uint32_t time)
+{
+ const int halfh = padding + (height - padding * 2) / 2;
+ const int halfw = padding + (width - padding * 2) / 2;
+ int ir, or;
+ uint32_t *pixel = image;
+ int y;
+
+ /* squared radii thresholds */
+ or = (halfw < halfh ? halfw : halfh) - 8;
+ ir = or - 32;
+ or *= or;
+ ir *= ir;
+
+ pixel += padding * width;
+ for (y = padding; y < height - padding; y++) {
+ int x;
+ int y2 = (y - halfh) * (y - halfh);
+
+ pixel += padding;
+ for (x = padding; x < width - padding; x++) {
+ uint32_t v;
+
+ /* squared distance from center */
+ int r2 = (x - halfw) * (x - halfw) + y2;
+
+ if (r2 < ir)
+ v = (r2 / 32 + time / 64) * 0x0080401;
+ else if (r2 < or)
+ v = (y + time / 32) * 0x0080401;
+ else
+ v = (x + time / 16) * 0x0080401;
+ v &= 0x00ffffff;
+
+ /* cross if compositor uses X from XRGB as alpha */
+ if (abs(x - y) > 6 && abs(x + y - height) > 6)
+ v |= 0xff000000;
+
+ *pixel++ = v;
+ }
+
+ pixel += padding;
+ }
+}
+
+static const struct wl_callback_listener frame_listener;
+
+static void
+redraw(void *data, struct wl_callback *callback, uint32_t time)
+{
+ struct window *window = data;
+ struct buffer *buffer;
+
+ buffer = window_next_buffer(window);
+ if (!buffer) {
+ fprintf(stderr,
+ !callback ? "Failed to create the first buffer.\n" :
+ "Both buffers busy at redraw(). Server bug?\n");
+ abort();
+ }
+
+ paint_pixels(buffer->shm_data, 20, window->width, window->height, time);
+
+ wl_surface_attach(window->surface, buffer->buffer, 0, 0);
+ wl_surface_damage(window->surface,
+ 20, 20, window->width - 40, window->height - 40);
+
+ if (callback)
+ wl_callback_destroy(callback);
+
+ window->callback = wl_surface_frame(window->surface);
+ wl_callback_add_listener(window->callback, &frame_listener, window);
+ wl_surface_commit(window->surface);
+ buffer->busy = 1;
+}
+
+static const struct wl_callback_listener frame_listener = {
+ redraw
+};
+
+static void
+shm_format(void *data, struct wl_shm *wl_shm, uint32_t format)
+{
+ struct display *d = data;
+
+ d->formats |= (1 << format);
+}
+
+struct wl_shm_listener shm_listener = {
+ shm_format
+};
+
+static void
+registry_handle_global(void *data, struct wl_registry *registry,
+ uint32_t id, const char *interface, uint32_t version)
+{
+ struct display *d = data;
+
+ if (strcmp(interface, "wl_compositor") == 0) {
+ d->compositor =
+ wl_registry_bind(registry,
+ id, &wl_compositor_interface, 1);
+ } else if (strcmp(interface, "wl_shell") == 0) {
+ d->shell = wl_registry_bind(registry,
+ id, &wl_shell_interface, 1);
+ } else if (strcmp(interface, "wl_shm") == 0) {
+ d->shm = wl_registry_bind(registry,
+ id, &wl_shm_interface, 1);
+ wl_shm_add_listener(d->shm, &shm_listener, d);
+ }
+}
+
+static void
+registry_handle_global_remove(void *data, struct wl_registry *registry,
+ uint32_t name)
+{
+}
+
+static const struct wl_registry_listener registry_listener = {
+ registry_handle_global,
+ registry_handle_global_remove
+};
+
+static struct display *
+create_display(void)
+{
+ struct display *display;
+
+ display = malloc(sizeof *display);
+ if (display == NULL) {
+ fprintf(stderr, "out of memory\n");
+ exit(1);
+ }
+ display->display = wl_display_connect(NULL);
+ assert(display->display);
+
+ display->formats = 0;
+ display->registry = wl_display_get_registry(display->display);
+ wl_registry_add_listener(display->registry,
+ &registry_listener, display);
+ wl_display_roundtrip(display->display);
+ if (display->shm == NULL) {
+ fprintf(stderr, "No wl_shm global\n");
+ exit(1);
+ }
+
+ wl_display_roundtrip(display->display);
+
+ if (!(display->formats & (1 << WL_SHM_FORMAT_XRGB8888))) {
+ fprintf(stderr, "WL_SHM_FORMAT_XRGB32 not available\n");
+ exit(1);
+ }
+
+ wl_display_get_fd(display->display);
+
+ return display;
+}
+
+static void
+destroy_display(struct display *display)
+{
+ if (display->shm)
+ wl_shm_destroy(display->shm);
+
+ if (display->shell)
+ wl_shell_destroy(display->shell);
+
+ if (display->compositor)
+ wl_compositor_destroy(display->compositor);
+
+ wl_registry_destroy(display->registry);
+ wl_display_flush(display->display);
+ wl_display_disconnect(display->display);
+ free(display);
+}
+
+static int running = 1;
+
+static void
+signal_int(int signum)
+{
+ running = 0;
+}
+
+int
+main(int argc, char **argv)
+{
+ struct sigaction sigint;
+ struct display *display;
+ struct window *window;
+ int ret = 0;
+
+ display = create_display();
+ window = create_window(display, 250, 250);
+ if (!window)
+ return 1;
+
+ sigint.sa_handler = signal_int;
+ sigemptyset(&sigint.sa_mask);
+ sigint.sa_flags = SA_RESETHAND;
+ sigaction(SIGINT, &sigint, NULL);
+
+ /* Initialise damage to full surface, so the padding gets painted */
+ wl_surface_damage(window->surface, 0, 0,
+ window->width, window->height);
+
+ redraw(window, NULL, 0);
+
+ while (running && ret != -1)
+ ret = wl_display_dispatch(display->display);
+
+ fprintf(stderr, "simple-shm exiting\n");
+ destroy_window(window);
+ destroy_display(display);
+
+ return 0;
+}
diff --git a/clients/simple-touch.c b/clients/simple-touch.c
new file mode 100644
index 00000000..b5a84d78
--- /dev/null
+++ b/clients/simple-touch.c
@@ -0,0 +1,340 @@
+/*
+ * Copyright © 2011 Benjamin Franzke
+ * Copyright © 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
+ * 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.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+#include <wayland-client.h>
+#include "../shared/os-compatibility.h"
+
+#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0])
+
+struct touch {
+ struct wl_display *display;
+ struct wl_registry *registry;
+ struct wl_compositor *compositor;
+ struct wl_shell *shell;
+ struct wl_shm *shm;
+ struct wl_seat *seat;
+ struct wl_touch *wl_touch;
+ struct wl_pointer *pointer;
+ struct wl_keyboard *keyboard;
+ struct wl_surface *surface;
+ struct wl_shell_surface *shell_surface;
+ struct wl_buffer *buffer;
+ int has_argb;
+ int width, height;
+ void *data;
+};
+
+static void
+create_shm_buffer(struct touch *touch)
+{
+ struct wl_shm_pool *pool;
+ int fd, size, stride;
+
+ stride = touch->width * 4;
+ size = stride * touch->height;
+
+ fd = os_create_anonymous_file(size);
+ if (fd < 0) {
+ fprintf(stderr, "creating a buffer file for %d B failed: %m\n",
+ size);
+ exit(1);
+ }
+
+ touch->data =
+ mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (touch->data == MAP_FAILED) {
+ fprintf(stderr, "mmap failed: %m\n");
+ close(fd);
+ exit(1);
+ }
+
+ pool = wl_shm_create_pool(touch->shm, fd, size);
+ touch->buffer =
+ wl_shm_pool_create_buffer(pool, 0,
+ touch->width, touch->height, stride,
+ WL_SHM_FORMAT_ARGB8888);
+ wl_shm_pool_destroy(pool);
+
+ close(fd);
+}
+
+static void
+shm_format(void *data, struct wl_shm *wl_shm, uint32_t format)
+{
+ struct touch *touch = data;
+
+ if (format == WL_SHM_FORMAT_ARGB8888)
+ touch->has_argb = 1;
+}
+
+struct wl_shm_listener shm_listener = {
+ shm_format
+};
+
+
+static void
+touch_paint(struct touch *touch, int32_t x, int32_t y, int32_t id)
+{
+ uint32_t *p, c;
+ static const uint32_t colors[] = {
+ 0xffff0000,
+ 0xffffff00,
+ 0xff0000ff,
+ 0xffff00ff,
+ 0xff00ff00,
+ 0xff00ffff,
+ };
+
+ if (id < (int32_t) ARRAY_LENGTH(colors))
+ c = colors[id];
+ else
+ c = 0xffffffff;
+
+ if (x < 2 || x >= touch->width - 2 ||
+ y < 2 || y >= touch->height - 2)
+ return;
+
+ p = (uint32_t *) touch->data + (x - 2) + (y - 2) * touch->width;
+ p[2] = c;
+ p += touch->width;
+ p[1] = c;
+ p[2] = c;
+ p[3] = c;
+ p += touch->width;
+ p[0] = c;
+ p[1] = c;
+ p[2] = c;
+ p[3] = c;
+ p[4] = c;
+ p += touch->width;
+ p[1] = c;
+ p[2] = c;
+ p[3] = c;
+ p += touch->width;
+ p[2] = c;
+
+ wl_surface_attach(touch->surface, touch->buffer, 0, 0);
+ wl_surface_damage(touch->surface, x - 2, y - 2, 5, 5);
+ /* todo: We could queue up more damage before committing, if there
+ * are more input events to handle.
+ */
+ wl_surface_commit(touch->surface);
+}
+
+static void
+touch_handle_down(void *data, struct wl_touch *wl_touch,
+ uint32_t serial, uint32_t time, struct wl_surface *surface,
+ int32_t id, wl_fixed_t x_w, wl_fixed_t y_w)
+{
+ struct touch *touch = data;
+ float x = wl_fixed_to_double(x_w);
+ float y = wl_fixed_to_double(y_w);
+
+ touch_paint(touch, x, y, id);
+}
+
+static void
+touch_handle_up(void *data, struct wl_touch *wl_touch,
+ uint32_t serial, uint32_t time, int32_t id)
+{
+}
+
+static void
+touch_handle_motion(void *data, struct wl_touch *wl_touch,
+ uint32_t time, int32_t id, wl_fixed_t x_w, wl_fixed_t y_w)
+{
+ struct touch *touch = data;
+ float x = wl_fixed_to_double(x_w);
+ float y = wl_fixed_to_double(y_w);
+
+ touch_paint(touch, x, y, id);
+}
+
+static void
+touch_handle_frame(void *data, struct wl_touch *wl_touch)
+{
+}
+
+static void
+touch_handle_cancel(void *data, struct wl_touch *wl_touch)
+{
+}
+
+static const struct wl_touch_listener touch_listener = {
+ touch_handle_down,
+ touch_handle_up,
+ touch_handle_motion,
+ touch_handle_frame,
+ touch_handle_cancel,
+};
+
+static void
+seat_handle_capabilities(void *data, struct wl_seat *seat,
+ enum wl_seat_capability caps)
+{
+ struct touch *touch = data;
+
+ if ((caps & WL_SEAT_CAPABILITY_TOUCH) && !touch->wl_touch) {
+ touch->wl_touch = wl_seat_get_touch(seat);
+ wl_touch_set_user_data(touch->wl_touch, touch);
+ wl_touch_add_listener(touch->wl_touch, &touch_listener, touch);
+ } else if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && touch->wl_touch) {
+ wl_touch_destroy(touch->wl_touch);
+ touch->wl_touch = NULL;
+ }
+}
+
+static const struct wl_seat_listener seat_listener = {
+ seat_handle_capabilities,
+};
+
+static void
+handle_ping(void *data, struct wl_shell_surface *shell_surface,
+ uint32_t serial)
+{
+ wl_shell_surface_pong(shell_surface, serial);
+}
+
+static void
+handle_configure(void *data, struct wl_shell_surface *shell_surface,
+ uint32_t edges, int32_t width, int32_t height)
+{
+}
+
+static void
+handle_popup_done(void *data, struct wl_shell_surface *shell_surface)
+{
+}
+
+static const struct wl_shell_surface_listener shell_surface_listener = {
+ handle_ping,
+ handle_configure,
+ handle_popup_done
+};
+
+static void
+handle_global(void *data, struct wl_registry *registry,
+ uint32_t name, const char *interface, uint32_t version)
+{
+ struct touch *touch = data;
+
+ if (strcmp(interface, "wl_compositor") == 0) {
+ touch->compositor =
+ wl_registry_bind(registry, name,
+ &wl_compositor_interface, 1);
+ } else if (strcmp(interface, "wl_shell") == 0) {
+ touch->shell =
+ wl_registry_bind(registry, name,
+ &wl_shell_interface, 1);
+ } else if (strcmp(interface, "wl_shm") == 0) {
+ touch->shm = wl_registry_bind(registry, name,
+ &wl_shm_interface, 1);
+ wl_shm_add_listener(touch->shm, &shm_listener, touch);
+ } else if (strcmp(interface, "wl_seat") == 0) {
+ touch->seat = wl_registry_bind(registry, name,
+ &wl_seat_interface, 1);
+ wl_seat_add_listener(touch->seat, &seat_listener, touch);
+ }
+}
+
+static void
+handle_global_remove(void *data, struct wl_registry *registry, uint32_t name)
+{
+}
+
+static const struct wl_registry_listener registry_listener = {
+ handle_global,
+ handle_global_remove
+};
+
+static struct touch *
+touch_create(int width, int height)
+{
+ struct touch *touch;
+
+ touch = malloc(sizeof *touch);
+ if (touch == NULL) {
+ fprintf(stderr, "out of memory\n");
+ exit(1);
+ }
+ touch->display = wl_display_connect(NULL);
+ assert(touch->display);
+
+ touch->has_argb = 0;
+ touch->registry = wl_display_get_registry(touch->display);
+ wl_registry_add_listener(touch->registry, &registry_listener, touch);
+ wl_display_dispatch(touch->display);
+ wl_display_roundtrip(touch->display);
+
+ if (!touch->has_argb) {
+ fprintf(stderr, "WL_SHM_FORMAT_ARGB32 not available\n");
+ exit(1);
+ }
+
+ touch->width = width;
+ touch->height = height;
+ touch->surface = wl_compositor_create_surface(touch->compositor);
+ touch->shell_surface = wl_shell_get_shell_surface(touch->shell,
+ touch->surface);
+ create_shm_buffer(touch);
+
+ if (touch->shell_surface) {
+ wl_shell_surface_add_listener(touch->shell_surface,
+ &shell_surface_listener, touch);
+ wl_shell_surface_set_toplevel(touch->shell_surface);
+ }
+
+ wl_surface_set_user_data(touch->surface, touch);
+ wl_shell_surface_set_title(touch->shell_surface, "simple-touch");
+
+ memset(touch->data, 64, width * height * 4);
+ wl_surface_attach(touch->surface, touch->buffer, 0, 0);
+ wl_surface_damage(touch->surface, 0, 0, width, height);
+ wl_surface_commit(touch->surface);
+
+ return touch;
+}
+
+int
+main(int argc, char **argv)
+{
+ struct touch *touch;
+ int ret = 0;
+
+ touch = touch_create(600, 500);
+
+ while (ret != -1)
+ ret = wl_display_dispatch(touch->display);
+
+ return 0;
+}
diff --git a/clients/smoke.c b/clients/smoke.c
new file mode 100644
index 00000000..dd5f4bd4
--- /dev/null
+++ b/clients/smoke.c
@@ -0,0 +1,331 @@
+/*
+ * Copyright © 2010 Kristian Høgsberg
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * 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.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <math.h>
+#include <cairo.h>
+
+#include <wayland-client.h>
+#include "window.h"
+
+struct smoke {
+ struct display *display;
+ struct window *window;
+ struct widget *widget;
+ int width, height;
+ int current;
+ uint32_t time;
+ struct { float *d, *u, *v; } b[2];
+};
+
+static void diffuse(struct smoke *smoke, uint32_t time,
+ float *source, float *dest)
+{
+ float *s, *d;
+ int x, y, k, stride;
+ float t, a = 0.0002;
+
+ stride = smoke->width;
+
+ for (k = 0; k < 5; k++) {
+ for (y = 1; y < smoke->height - 1; y++) {
+ s = source + y * stride;
+ d = dest + y * stride;
+ for (x = 1; x < smoke->width - 1; x++) {
+ t = d[x - 1] + d[x + 1] +
+ d[x - stride] + d[x + stride];
+ d[x] = (s[x] + a * t) / (1 + 4 * a) * 0.995;
+ }
+ }
+ }
+}
+
+static void advect(struct smoke *smoke, uint32_t time,
+ float *uu, float *vv, float *source, float *dest)
+{
+ float *s, *d;
+ float *u, *v;
+ int x, y, stride;
+ int i, j;
+ float px, py, fx, fy;
+
+ stride = smoke->width;
+
+ for (y = 1; y < smoke->height - 1; y++) {
+ d = dest + y * stride;
+ u = uu + y * stride;
+ v = vv + y * stride;
+
+ for (x = 1; x < smoke->width - 1; x++) {
+ px = x - u[x];
+ py = y - v[x];
+ if (px < 0.5)
+ px = 0.5;
+ if (py < 0.5)
+ py = 0.5;
+ if (px > smoke->width - 0.5)
+ px = smoke->width - 0.5;
+ if (py > smoke->height - 0.5)
+ py = smoke->height - 0.5;
+ i = (int) px;
+ j = (int) py;
+ fx = px - i;
+ fy = py - j;
+ s = source + j * stride + i;
+ d[x] = (s[0] * (1 - fx) + s[1] * fx) * (1 - fy) +
+ (s[stride] * (1 - fx) + s[stride + 1] * fx) * fy;
+ }
+ }
+}
+
+static void project(struct smoke *smoke, uint32_t time,
+ float *u, float *v, float *p, float *div)
+{
+ int x, y, k, l, s;
+ float h;
+
+ h = 1.0 / smoke->width;
+ s = smoke->width;
+ memset(p, 0, smoke->height * smoke->width);
+ for (y = 1; y < smoke->height - 1; y++) {
+ l = y * s;
+ for (x = 1; x < smoke->width - 1; x++) {
+ div[l + x] = -0.5 * h * (u[l + x + 1] - u[l + x - 1] +
+ v[l + x + s] - v[l + x - s]);
+ p[l + x] = 0;
+ }
+ }
+
+ for (k = 0; k < 5; k++) {
+ for (y = 1; y < smoke->height - 1; y++) {
+ l = y * s;
+ for (x = 1; x < smoke->width - 1; x++) {
+ p[l + x] = (div[l + x] +
+ p[l + x - 1] +
+ p[l + x + 1] +
+ p[l + x - s] +
+ p[l + x + s]) / 4;
+ }
+ }
+ }
+
+ for (y = 1; y < smoke->height - 1; y++) {
+ l = y * s;
+ for (x = 1; x < smoke->width - 1; x++) {
+ u[l + x] -= 0.5 * (p[l + x + 1] - p[l + x - 1]) / h;
+ v[l + x] -= 0.5 * (p[l + x + s] - p[l + x - s]) / h;
+ }
+ }
+}
+
+static void render(struct smoke *smoke, cairo_surface_t *surface)
+{
+ unsigned char *dest;
+ int x, y, width, height, stride;
+ float *s;
+ uint32_t *d, c, a;
+
+ dest = cairo_image_surface_get_data(surface);
+ width = cairo_image_surface_get_width(surface);
+ height = cairo_image_surface_get_height(surface);
+ stride = cairo_image_surface_get_stride(surface);
+
+ for (y = 1; y < height - 1; y++) {
+ s = smoke->b[smoke->current].d + y * smoke->height;
+ d = (uint32_t *) (dest + y * stride);
+ for (x = 1; x < width - 1; x++) {
+ c = (int) (s[x] * 800);
+ if (c > 255)
+ c = 255;
+ a = c;
+ if (a < 0x33)
+ a = 0x33;
+ d[x] = (a << 24) | (c << 16) | (c << 8) | c;
+ }
+ }
+}
+
+static void
+frame_callback(void *data, struct wl_callback *callback, uint32_t time)
+{
+ struct smoke *smoke = data;
+
+ window_schedule_redraw(smoke->window);
+ smoke->time = time;
+
+ if (callback)
+ wl_callback_destroy(callback);
+}
+
+static const struct wl_callback_listener listener = {
+ frame_callback,
+};
+
+static void
+redraw_handler(struct widget *widget, void *data)
+{
+ struct smoke *smoke = data;
+ uint32_t time = smoke->time;
+ struct wl_callback *callback;
+ cairo_surface_t *surface;
+
+ diffuse(smoke, time / 30, smoke->b[0].u, smoke->b[1].u);
+ diffuse(smoke, time / 30, smoke->b[0].v, smoke->b[1].v);
+ project(smoke, time / 30,
+ smoke->b[1].u, smoke->b[1].v,
+ smoke->b[0].u, smoke->b[0].v);
+ advect(smoke, time / 30,
+ smoke->b[1].u, smoke->b[1].v,
+ smoke->b[1].u, smoke->b[0].u);
+ advect(smoke, time / 30,
+ smoke->b[1].u, smoke->b[1].v,
+ smoke->b[1].v, smoke->b[0].v);
+ project(smoke, time / 30,
+ smoke->b[0].u, smoke->b[0].v,
+ smoke->b[1].u, smoke->b[1].v);
+
+ diffuse(smoke, time / 30, smoke->b[0].d, smoke->b[1].d);
+ advect(smoke, time / 30,
+ smoke->b[0].u, smoke->b[0].v,
+ smoke->b[1].d, smoke->b[0].d);
+
+ surface = window_get_surface(smoke->window);
+
+ render(smoke, surface);
+
+ window_damage(smoke->window, 0, 0, smoke->width, smoke->height);
+
+ cairo_surface_destroy(surface);
+
+ callback = wl_surface_frame(window_get_wl_surface(smoke->window));
+ wl_callback_add_listener(callback, &listener, smoke);
+ wl_surface_commit(window_get_wl_surface(smoke->window));
+}
+
+static void
+smoke_motion_handler(struct smoke *smoke, float x, float y)
+{
+ int i, i0, i1, j, j0, j1, k, d = 5;
+
+ if (x - d < 1)
+ i0 = 1;
+ else
+ i0 = x - d;
+ if (i0 + 2 * d > smoke->width - 1)
+ i1 = smoke->width - 1;
+ else
+ i1 = i0 + 2 * d;
+
+ if (y - d < 1)
+ j0 = 1;
+ else
+ j0 = y - d;
+ if (j0 + 2 * d > smoke->height - 1)
+ j1 = smoke->height - 1;
+ else
+ j1 = j0 + 2 * d;
+
+ for (i = i0; i < i1; i++)
+ for (j = j0; j < j1; j++) {
+ k = j * smoke->width + i;
+ smoke->b[0].u[k] += 256 - (random() & 512);
+ smoke->b[0].v[k] += 256 - (random() & 512);
+ smoke->b[0].d[k] += 1;
+ }
+}
+
+static int
+mouse_motion_handler(struct widget *widget, struct input *input,
+ uint32_t time, float x, float y, void *data)
+{
+ smoke_motion_handler(data, x, y);
+
+ return CURSOR_HAND1;
+}
+
+static void
+touch_motion_handler(struct widget *widget, struct input *input,
+ uint32_t time, int32_t id, float x, float y, void *data)
+{
+ smoke_motion_handler(data, x, y);
+}
+
+static void
+resize_handler(struct widget *widget,
+ int32_t width, int32_t height, void *data)
+{
+ struct smoke *smoke = data;
+
+ /* Dont resize me */
+ widget_set_size(smoke->widget, smoke->width, smoke->height);
+}
+
+int main(int argc, char *argv[])
+{
+ struct timespec ts;
+ struct smoke smoke;
+ struct display *d;
+ int size;
+
+ d = display_create(&argc, argv);
+ if (d == NULL) {
+ fprintf(stderr, "failed to create display: %m\n");
+ return -1;
+ }
+
+ smoke.width = 200;
+ smoke.height = 200;
+ smoke.display = d;
+ smoke.window = window_create(d);
+ smoke.widget = window_add_widget(smoke.window, &smoke);
+ window_set_title(smoke.window, "smoke");
+
+ window_set_buffer_type(smoke.window, WINDOW_BUFFER_TYPE_SHM);
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ srandom(ts.tv_nsec);
+
+ smoke.current = 0;
+ size = smoke.height * smoke.width;
+ smoke.b[0].d = calloc(size, sizeof(float));
+ smoke.b[0].u = calloc(size, sizeof(float));
+ smoke.b[0].v = calloc(size, sizeof(float));
+ smoke.b[1].d = calloc(size, sizeof(float));
+ smoke.b[1].u = calloc(size, sizeof(float));
+ smoke.b[1].v = calloc(size, sizeof(float));
+
+ widget_set_motion_handler(smoke.widget, mouse_motion_handler);
+ widget_set_touch_motion_handler(smoke.widget, touch_motion_handler);
+ widget_set_resize_handler(smoke.widget, resize_handler);
+ widget_set_redraw_handler(smoke.widget, redraw_handler);
+
+ window_set_user_data(smoke.window, &smoke);
+
+ widget_schedule_resize(smoke.widget, smoke.width, smoke.height);
+
+ display_run(d);
+
+ return 0;
+}
diff --git a/clients/subsurfaces.c b/clients/subsurfaces.c
new file mode 100644
index 00000000..101ff17e
--- /dev/null
+++ b/clients/subsurfaces.c
@@ -0,0 +1,801 @@
+/*
+ * Copyright © 2010 Intel Corporation
+ * Copyright © 2011 Benjamin Franzke
+ * Copyright © 2012-2013 Collabora, Ltd.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * 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.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <cairo.h>
+#include <math.h>
+#include <assert.h>
+
+#include <linux/input.h>
+#include <wayland-client.h>
+
+#include <wayland-egl.h>
+#include <GLES2/gl2.h>
+#include <EGL/egl.h>
+
+#include "window.h"
+
+#if 0
+#define DBG(fmt, ...) \
+ fprintf(stderr, "%d:%s " fmt, __LINE__, __func__, ##__VA_ARGS__)
+#else
+#define DBG(...) do {} while (0)
+#endif
+
+static int32_t option_red_mode;
+static int32_t option_triangle_mode;
+static int32_t option_no_triangle;
+static int32_t option_help;
+
+static const struct weston_option options[] = {
+ { WESTON_OPTION_INTEGER, "red-mode", 'r', &option_red_mode },
+ { WESTON_OPTION_INTEGER, "triangle-mode", 't', &option_triangle_mode },
+ { WESTON_OPTION_BOOLEAN, "no-triangle", 'n', &option_no_triangle },
+ { WESTON_OPTION_BOOLEAN, "help", 'h', &option_help },
+};
+
+static enum subsurface_mode
+int_to_mode(int32_t i)
+{
+ switch (i) {
+ case 0:
+ return SUBSURFACE_DESYNCHRONIZED;
+ case 1:
+ return SUBSURFACE_SYNCHRONIZED;
+ default:
+ fprintf(stderr, "error: %d is not a valid commit mode.\n", i);
+ exit(1);
+ }
+}
+
+static const char help_text[] =
+"Usage: %s [options]\n"
+"\n"
+" -r, --red-mode=MODE\t\tthe commit mode for the red sub-surface (0)\n"
+" -t, --triangle-mode=MODE\tthe commit mode for the GL sub-surface (0)\n"
+" -n, --no-triangle\t\tDo not create the GL sub-surface.\n"
+"\n"
+"The MODE is the wl_subsurface commit mode used by default for the\n"
+"given sub-surface. Valid values are the integers:\n"
+" 0\tfor desynchronized, i.e. free-running\n"
+" 1\tfor synchronized\n"
+"\n"
+"This program demonstrates sub-surfaces with the toytoolkit.\n"
+"The main surface contains the decorations, a green canvas, and a\n"
+"green spinner. One sub-surface is red with a red spinner. These\n"
+"are rendered with Cairo. The other sub-surface contains a spinning\n"
+"triangle rendered in EGL/GLESv2, without Cairo, i.e. it is a raw GL\n"
+"widget.\n"
+"\n"
+"The GL widget animates on its own. The spinners follow wall clock\n"
+"time and update only when their surface is repainted, so you see\n"
+"which surfaces get redrawn. The red sub-surface animates on its own,\n"
+"but can be toggled with the spacebar.\n"
+"\n"
+"Even though the sub-surfaces attempt to animate on their own, they\n"
+"are subject to the commit mode. If commit mode is synchronized,\n"
+"they will need a commit on the main surface to actually display.\n"
+"You can trigger a main surface repaint, without a resize, by\n"
+"hovering the pointer over the title bar buttons.\n"
+"\n"
+"Resizing will temporarily toggle the commit mode of all sub-surfaces\n"
+"to guarantee synchronized rendering on size changes. It also forces\n"
+"a repaint of all surfaces.\n"
+"\n"
+"Using -t1 -r1 is especially useful for trying to catch inconsistent\n"
+"rendering and deadlocks, since free-running sub-surfaces would\n"
+"immediately hide the problem.\n"
+"\n"
+"Key controls:\n"
+" space - toggle red sub-surface animation loop\n"
+" up - step window size shorter\n"
+" down - step window size taller\n"
+"\n";
+
+struct egl_state {
+ EGLDisplay dpy;
+ EGLContext ctx;
+ EGLConfig conf;
+};
+
+struct triangle_gl_state {
+ GLuint rotation_uniform;
+ GLuint pos;
+ GLuint col;
+};
+
+struct triangle {
+ struct egl_state *egl;
+
+ struct wl_surface *wl_surface;
+ struct wl_egl_window *egl_window;
+ EGLSurface egl_surface;
+ int width;
+ int height;
+
+ struct triangle_gl_state gl;
+
+ struct widget *widget;
+ uint32_t time;
+ struct wl_callback *frame_cb;
+};
+
+/******** Pure EGL/GLESv2/libwayland-client component: ***************/
+
+static const char *vert_shader_text =
+ "uniform mat4 rotation;\n"
+ "attribute vec4 pos;\n"
+ "attribute vec4 color;\n"
+ "varying vec4 v_color;\n"
+ "void main() {\n"
+ " gl_Position = rotation * pos;\n"
+ " v_color = color;\n"
+ "}\n";
+
+static const char *frag_shader_text =
+ "precision mediump float;\n"
+ "varying vec4 v_color;\n"
+ "void main() {\n"
+ " gl_FragColor = v_color;\n"
+ "}\n";
+
+static void
+egl_print_config_info(struct egl_state *egl)
+{
+ EGLint r, g, b, a;
+
+ printf("Chosen EGL config details:\n");
+
+ printf("\tRGBA bits");
+ if (eglGetConfigAttrib(egl->dpy, egl->conf, EGL_RED_SIZE, &r) &&
+ eglGetConfigAttrib(egl->dpy, egl->conf, EGL_GREEN_SIZE, &g) &&
+ eglGetConfigAttrib(egl->dpy, egl->conf, EGL_BLUE_SIZE, &b) &&
+ eglGetConfigAttrib(egl->dpy, egl->conf, EGL_ALPHA_SIZE, &a))
+ printf(": %d %d %d %d\n", r, g, b, a);
+ else
+ printf(" unknown\n");
+
+ printf("\tswap interval range");
+ if (eglGetConfigAttrib(egl->dpy, egl->conf, EGL_MIN_SWAP_INTERVAL, &a) &&
+ eglGetConfigAttrib(egl->dpy, egl->conf, EGL_MAX_SWAP_INTERVAL, &b))
+ printf(": %d - %d\n", a, b);
+ else
+ printf(" unknown\n");
+}
+
+static struct egl_state *
+egl_state_create(struct wl_display *display)
+{
+ struct egl_state *egl;
+
+ static const EGLint context_attribs[] = {
+ EGL_CONTEXT_CLIENT_VERSION, 2,
+ EGL_NONE
+ };
+
+ EGLint config_attribs[] = {
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_RED_SIZE, 1,
+ EGL_GREEN_SIZE, 1,
+ EGL_BLUE_SIZE, 1,
+ EGL_ALPHA_SIZE, 1,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_NONE
+ };
+
+ EGLint major, minor, n;
+ EGLBoolean ret;
+
+ egl = calloc(1, sizeof *egl);
+ assert(egl);
+
+ egl->dpy = eglGetDisplay(display);
+ assert(egl->dpy);
+
+ ret = eglInitialize(egl->dpy, &major, &minor);
+ assert(ret == EGL_TRUE);
+ ret = eglBindAPI(EGL_OPENGL_ES_API);
+ assert(ret == EGL_TRUE);
+
+ ret = eglChooseConfig(egl->dpy, config_attribs, &egl->conf, 1, &n);
+ assert(ret && n == 1);
+
+ egl->ctx = eglCreateContext(egl->dpy, egl->conf,
+ EGL_NO_CONTEXT, context_attribs);
+ assert(egl->ctx);
+ egl_print_config_info(egl);
+
+ return egl;
+}
+
+static void
+egl_state_destroy(struct egl_state *egl)
+{
+ /* Required, otherwise segfault in egl_dri2.c: dri2_make_current()
+ * on eglReleaseThread(). */
+ eglMakeCurrent(egl->dpy, EGL_NO_SURFACE, EGL_NO_SURFACE,
+ EGL_NO_CONTEXT);
+
+ eglTerminate(egl->dpy);
+ eglReleaseThread();
+ free(egl);
+}
+
+static void
+egl_make_swapbuffers_nonblock(struct egl_state *egl)
+{
+ EGLint a = EGL_MIN_SWAP_INTERVAL;
+ EGLint b = EGL_MAX_SWAP_INTERVAL;
+
+ if (!eglGetConfigAttrib(egl->dpy, egl->conf, a, &a) ||
+ !eglGetConfigAttrib(egl->dpy, egl->conf, b, &b)) {
+ fprintf(stderr, "warning: swap interval range unknown\n");
+ } else
+ if (a > 0) {
+ fprintf(stderr, "warning: minimum swap interval is %d, "
+ "while 0 is required to not deadlock on resize.\n", a);
+ }
+
+ /*
+ * We rely on the Wayland compositor to sync to vblank anyway.
+ * We just need to be able to call eglSwapBuffers() without the
+ * risk of waiting for a frame callback in it.
+ */
+ if (!eglSwapInterval(egl->dpy, 0)) {
+ fprintf(stderr, "error: eglSwapInterval() failed.\n");
+ }
+}
+
+static GLuint
+create_shader(const char *source, GLenum shader_type)
+{
+ GLuint shader;
+ GLint status;
+
+ shader = glCreateShader(shader_type);
+ assert(shader != 0);
+
+ glShaderSource(shader, 1, (const char **) &source, NULL);
+ glCompileShader(shader);
+
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
+ if (!status) {
+ char log[1000];
+ GLsizei len;
+ glGetShaderInfoLog(shader, 1000, &len, log);
+ fprintf(stderr, "Error: compiling %s: %*s\n",
+ shader_type == GL_VERTEX_SHADER ? "vertex" : "fragment",
+ len, log);
+ exit(1);
+ }
+
+ return shader;
+}
+
+static void
+triangle_init_gl(struct triangle_gl_state *trigl)
+{
+ GLuint frag, vert;
+ GLuint program;
+ GLint status;
+
+ frag = create_shader(frag_shader_text, GL_FRAGMENT_SHADER);
+ vert = create_shader(vert_shader_text, GL_VERTEX_SHADER);
+
+ program = glCreateProgram();
+ glAttachShader(program, frag);
+ glAttachShader(program, vert);
+ glLinkProgram(program);
+
+ glGetProgramiv(program, GL_LINK_STATUS, &status);
+ if (!status) {
+ char log[1000];
+ GLsizei len;
+ glGetProgramInfoLog(program, 1000, &len, log);
+ fprintf(stderr, "Error: linking:\n%*s\n", len, log);
+ exit(1);
+ }
+
+ glUseProgram(program);
+
+ trigl->pos = 0;
+ trigl->col = 1;
+
+ glBindAttribLocation(program, trigl->pos, "pos");
+ glBindAttribLocation(program, trigl->col, "color");
+ glLinkProgram(program);
+
+ trigl->rotation_uniform = glGetUniformLocation(program, "rotation");
+}
+
+static void
+triangle_draw(const struct triangle_gl_state *trigl, uint32_t time)
+{
+ static const GLfloat verts[3][2] = {
+ { -0.5, -0.5 },
+ { 0.5, -0.5 },
+ { 0, 0.5 }
+ };
+ static const GLfloat colors[3][3] = {
+ { 1, 0, 0 },
+ { 0, 1, 0 },
+ { 0, 0, 1 }
+ };
+ GLfloat angle;
+ GLfloat rotation[4][4] = {
+ { 1, 0, 0, 0 },
+ { 0, 1, 0, 0 },
+ { 0, 0, 1, 0 },
+ { 0, 0, 0, 1 }
+ };
+ static const int32_t speed_div = 5;
+
+ angle = (time / speed_div) % 360 * M_PI / 180.0;
+ rotation[0][0] = cos(angle);
+ rotation[0][2] = sin(angle);
+ rotation[2][0] = -sin(angle);
+ rotation[2][2] = cos(angle);
+
+ glUniformMatrix4fv(trigl->rotation_uniform, 1, GL_FALSE,
+ (GLfloat *) rotation);
+
+ glClearColor(0.0, 0.0, 0.0, 0.5);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ glVertexAttribPointer(trigl->pos, 2, GL_FLOAT, GL_FALSE, 0, verts);
+ glVertexAttribPointer(trigl->col, 3, GL_FLOAT, GL_FALSE, 0, colors);
+ glEnableVertexAttribArray(trigl->pos);
+ glEnableVertexAttribArray(trigl->col);
+
+ glDrawArrays(GL_TRIANGLES, 0, 3);
+
+ glDisableVertexAttribArray(trigl->pos);
+ glDisableVertexAttribArray(trigl->col);
+}
+
+static void
+triangle_frame_callback(void *data, struct wl_callback *callback,
+ uint32_t time);
+
+static const struct wl_callback_listener triangle_frame_listener = {
+ triangle_frame_callback
+};
+
+static void
+triangle_frame_callback(void *data, struct wl_callback *callback,
+ uint32_t time)
+{
+ struct triangle *tri = data;
+
+ DBG("%stime %u\n", callback ? "" : "artificial ", time);
+ assert(callback == tri->frame_cb);
+ tri->time = time;
+
+ if (callback)
+ wl_callback_destroy(callback);
+
+ eglMakeCurrent(tri->egl->dpy, tri->egl_surface,
+ tri->egl_surface, tri->egl->ctx);
+
+ glViewport(0, 0, tri->width, tri->height);
+
+ triangle_draw(&tri->gl, tri->time);
+
+ tri->frame_cb = wl_surface_frame(tri->wl_surface);
+ wl_callback_add_listener(tri->frame_cb, &triangle_frame_listener, tri);
+
+ eglSwapBuffers(tri->egl->dpy, tri->egl_surface);
+}
+
+static void
+triangle_create_egl_surface(struct triangle *tri, int width, int height)
+{
+ EGLBoolean ret;
+
+ tri->wl_surface = widget_get_wl_surface(tri->widget);
+ tri->egl_window = wl_egl_window_create(tri->wl_surface, width, height);
+ tri->egl_surface = eglCreateWindowSurface(tri->egl->dpy,
+ tri->egl->conf,
+ tri->egl_window, NULL);
+
+ ret = eglMakeCurrent(tri->egl->dpy, tri->egl_surface,
+ tri->egl_surface, tri->egl->ctx);
+ assert(ret == EGL_TRUE);
+
+ egl_make_swapbuffers_nonblock(tri->egl);
+ triangle_init_gl(&tri->gl);
+}
+
+/********* The widget code interfacing the toolkit agnostic code: **********/
+
+static void
+triangle_resize_handler(struct widget *widget,
+ int32_t width, int32_t height, void *data)
+{
+ struct triangle *tri = data;
+
+ DBG("to %dx%d\n", width, height);
+ tri->width = width;
+ tri->height = height;
+
+ if (tri->egl_surface) {
+ wl_egl_window_resize(tri->egl_window, width, height, 0, 0);
+ } else {
+ triangle_create_egl_surface(tri, width, height);
+ triangle_frame_callback(tri, NULL, 0);
+ }
+}
+
+static void
+triangle_redraw_handler(struct widget *widget, void *data)
+{
+ struct triangle *tri = data;
+ int w, h;
+
+ wl_egl_window_get_attached_size(tri->egl_window, &w, &h);
+
+ DBG("previous %dx%d, new %dx%d\n", w, h, tri->width, tri->height);
+
+ /* If size is not changing, do not redraw ahead of time.
+ * That would risk blocking in eglSwapbuffers().
+ */
+ if (w == tri->width && h == tri->height)
+ return;
+
+ if (tri->frame_cb) {
+ wl_callback_destroy(tri->frame_cb);
+ tri->frame_cb = NULL;
+ }
+ triangle_frame_callback(tri, NULL, tri->time);
+}
+
+static void
+set_empty_input_region(struct widget *widget, struct display *display)
+{
+ struct wl_compositor *compositor;
+ struct wl_surface *surface;
+ struct wl_region *region;
+
+ compositor = display_get_compositor(display);
+ surface = widget_get_wl_surface(widget);
+ region = wl_compositor_create_region(compositor);
+ wl_surface_set_input_region(surface, region);
+ wl_region_destroy(region);
+}
+
+static struct triangle *
+triangle_create(struct window *window, struct egl_state *egl)
+{
+ struct triangle *tri;
+
+ tri = xmalloc(sizeof *tri);
+ memset(tri, 0, sizeof *tri);
+
+ tri->egl = egl;
+ tri->widget = window_add_subsurface(window, tri,
+ int_to_mode(option_triangle_mode));
+ widget_set_resize_handler(tri->widget, triangle_resize_handler);
+ widget_set_redraw_handler(tri->widget, triangle_redraw_handler);
+
+ set_empty_input_region(tri->widget, window_get_display(window));
+
+ return tri;
+}
+
+static void
+triangle_destroy(struct triangle *tri)
+{
+ if (tri->egl_surface)
+ eglDestroySurface(tri->egl->dpy, tri->egl_surface);
+
+ if (tri->egl_window)
+ wl_egl_window_destroy(tri->egl_window);
+
+ widget_destroy(tri->widget);
+ free(tri);
+}
+
+/************** The toytoolkit application code: *********************/
+
+struct demoapp {
+ struct display *display;
+ struct window *window;
+ struct widget *widget;
+ struct widget *subsurface;
+
+ struct egl_state *egl;
+ struct triangle *triangle;
+
+ int animate;
+};
+
+static void
+draw_spinner(cairo_t *cr, const struct rectangle *rect, uint32_t time)
+{
+ double cx, cy, r, angle;
+ unsigned t;
+
+ cx = rect->x + rect->width / 2;
+ cy = rect->y + rect->height / 2;
+ r = (rect->width < rect->height ? rect->width : rect->height) * 0.3;
+ t = time % 2000;
+ angle = t * (M_PI / 500.0);
+
+ cairo_set_line_width(cr, 4.0);
+
+ if (t < 1000)
+ cairo_arc(cr, cx, cy, r, 0.0, angle);
+ else
+ cairo_arc(cr, cx, cy, r, angle, 0.0);
+
+ cairo_stroke(cr);
+}
+
+static void
+sub_redraw_handler(struct widget *widget, void *data)
+{
+ struct demoapp *app = data;
+ cairo_t *cr;
+ struct rectangle allocation;
+ uint32_t time;
+
+ widget_get_allocation(app->subsurface, &allocation);
+
+ cr = widget_cairo_create(widget);
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+
+ /* debug: paint whole surface magenta; no magenta should show */
+ cairo_set_source_rgba(cr, 0.9, 0.0, 0.9, 1.0);
+ cairo_paint(cr);
+
+ cairo_rectangle(cr,
+ allocation.x,
+ allocation.y,
+ allocation.width,
+ allocation.height);
+ cairo_clip(cr);
+
+ cairo_set_source_rgba(cr, 0.8, 0, 0, 0.8);
+ cairo_paint(cr);
+
+ time = widget_get_last_time(widget);
+ cairo_set_source_rgba(cr, 1.0, 0.5, 0.5, 1.0);
+ draw_spinner(cr, &allocation, time);
+
+ cairo_destroy(cr);
+
+ if (app->animate)
+ widget_schedule_redraw(app->subsurface);
+ DBG("%dx%d @ %d,%d, last time %u\n",
+ allocation.width, allocation.height,
+ allocation.x, allocation.y, time);
+}
+
+static void
+sub_resize_handler(struct widget *widget,
+ int32_t width, int32_t height, void *data)
+{
+ DBG("%dx%d\n", width, height);
+ widget_input_region_add(widget, NULL);
+}
+
+static void
+redraw_handler(struct widget *widget, void *data)
+{
+ struct demoapp *app = data;
+ cairo_t *cr;
+ struct rectangle allocation;
+ uint32_t time;
+
+ widget_get_allocation(app->widget, &allocation);
+
+ cr = widget_cairo_create(widget);
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ cairo_rectangle(cr,
+ allocation.x,
+ allocation.y,
+ allocation.width,
+ allocation.height);
+ cairo_set_source_rgba(cr, 0, 0.8, 0, 0.8);
+ cairo_fill(cr);
+
+ time = widget_get_last_time(widget);
+ cairo_set_source_rgba(cr, 0.5, 1.0, 0.5, 1.0);
+ draw_spinner(cr, &allocation, time);
+
+ cairo_destroy(cr);
+
+ DBG("%dx%d @ %d,%d, last time %u\n",
+ allocation.width, allocation.height,
+ allocation.x, allocation.y, time);
+}
+
+static void
+resize_handler(struct widget *widget,
+ int32_t width, int32_t height, void *data)
+{
+ struct demoapp *app = data;
+ struct rectangle area;
+ int side, h;
+
+ widget_get_allocation(widget, &area);
+
+ side = area.width < area.height ? area.width / 2 : area.height / 2;
+ h = area.height - side;
+
+ widget_set_allocation(app->subsurface,
+ area.x + area.width - side,
+ area.y,
+ side, h);
+
+ if (app->triangle) {
+ widget_set_allocation(app->triangle->widget,
+ area.x + area.width - side,
+ area.y + h,
+ side, side);
+ }
+
+ DBG("green %dx%d, red %dx%d, GL %dx%d\n",
+ area.width, area.height, side, h, side, side);
+}
+
+static void
+keyboard_focus_handler(struct window *window,
+ struct input *device, void *data)
+{
+ struct demoapp *app = data;
+
+ window_schedule_redraw(app->window);
+}
+
+static void
+key_handler(struct window *window, struct input *input, uint32_t time,
+ uint32_t key, uint32_t sym,
+ enum wl_keyboard_key_state state, void *data)
+{
+ struct demoapp *app = data;
+ struct rectangle winrect;
+
+ if (state == WL_KEYBOARD_KEY_STATE_RELEASED)
+ return;
+
+ switch (sym) {
+ case XKB_KEY_space:
+ app->animate = !app->animate;
+ window_schedule_redraw(window);
+ break;
+ case XKB_KEY_Up:
+ window_get_allocation(window, &winrect);
+ winrect.height -= 100;
+ if (winrect.height < 150)
+ winrect.height = 150;
+ window_schedule_resize(window, winrect.width, winrect.height);
+ break;
+ case XKB_KEY_Down:
+ window_get_allocation(window, &winrect);
+ winrect.height += 100;
+ if (winrect.height > 600)
+ winrect.height = 600;
+ window_schedule_resize(window, winrect.width, winrect.height);
+ break;
+ case XKB_KEY_Escape:
+ display_exit(app->display);
+ break;
+ }
+}
+
+static struct demoapp *
+demoapp_create(struct display *display)
+{
+ struct demoapp *app;
+
+ app = xmalloc(sizeof *app);
+ memset(app, 0, sizeof *app);
+
+ app->egl = egl_state_create(display_get_display(display));
+
+ app->display = display;
+ display_set_user_data(app->display, app);
+
+ app->window = window_create(app->display);
+ app->widget = frame_create(app->window, app);
+ window_set_title(app->window, "Wayland Sub-surface Demo");
+
+ window_set_key_handler(app->window, key_handler);
+ window_set_user_data(app->window, app);
+ window_set_keyboard_focus_handler(app->window, keyboard_focus_handler);
+
+ widget_set_redraw_handler(app->widget, redraw_handler);
+ widget_set_resize_handler(app->widget, resize_handler);
+
+ app->subsurface = window_add_subsurface(app->window, app,
+ int_to_mode(option_red_mode));
+ widget_set_redraw_handler(app->subsurface, sub_redraw_handler);
+ widget_set_resize_handler(app->subsurface, sub_resize_handler);
+
+ if (app->egl && !option_no_triangle)
+ app->triangle = triangle_create(app->window, app->egl);
+
+ /* minimum size */
+ widget_schedule_resize(app->widget, 100, 100);
+
+ /* initial size */
+ widget_schedule_resize(app->widget, 400, 300);
+
+ app->animate = 1;
+
+ return app;
+}
+
+static void
+demoapp_destroy(struct demoapp *app)
+{
+ if (app->triangle)
+ triangle_destroy(app->triangle);
+
+ if (app->egl)
+ egl_state_destroy(app->egl);
+
+ widget_destroy(app->subsurface);
+ widget_destroy(app->widget);
+ window_destroy(app->window);
+ free(app);
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct display *display;
+ struct demoapp *app;
+
+ parse_options(options, ARRAY_LENGTH(options), &argc, argv);
+ if (option_help) {
+ printf(help_text, argv[0]);
+ return 0;
+ }
+
+ display = display_create(&argc, argv);
+ if (display == NULL) {
+ fprintf(stderr, "failed to create display: %m\n");
+ return -1;
+ }
+
+ if (!display_has_subcompositor(display)) {
+ fprintf(stderr, "compositor does not support "
+ "the subcompositor extension\n");
+ return -1;
+ }
+
+ app = demoapp_create(display);
+
+ display_run(display);
+
+ demoapp_destroy(app);
+ display_destroy(display);
+
+ return 0;
+}
diff --git a/clients/tablet-shell.c b/clients/tablet-shell.c
new file mode 100644
index 00000000..45733b11
--- /dev/null
+++ b/clients/tablet-shell.c
@@ -0,0 +1,478 @@
+/*
+ * Copyright © 2011, 2012 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
+ * 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.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/wait.h>
+
+#include "window.h"
+#include "../shared/cairo-util.h"
+#include "../shared/config-parser.h"
+
+#include "tablet-shell-client-protocol.h"
+
+struct tablet {
+ struct display *display;
+ struct tablet_shell *tablet_shell;
+ struct rectangle allocation;
+ struct window *switcher;
+
+ struct homescreen *homescreen;
+ struct lockscreen *lockscreen;
+};
+
+struct homescreen {
+ struct window *window;
+ struct widget *widget;
+ struct wl_list launcher_list;
+};
+
+struct lockscreen {
+ struct window *window;
+ struct widget *widget;
+};
+
+struct launcher {
+ struct widget *widget;
+ struct homescreen *homescreen;
+ cairo_surface_t *icon;
+ int focused, pressed;
+ char *path;
+ struct wl_list link;
+};
+
+static char *key_lockscreen_icon;
+static char *key_lockscreen_background;
+static char *key_homescreen_background;
+
+static void
+sigchild_handler(int s)
+{
+ int status;
+ pid_t pid;
+
+ while (pid = waitpid(-1, &status, WNOHANG), pid > 0)
+ fprintf(stderr, "child %d exited\n", pid);
+}
+
+static void
+paint_background(cairo_t *cr, const char *path, struct rectangle *allocation)
+{
+ cairo_surface_t *image = NULL;
+ cairo_pattern_t *pattern;
+ cairo_matrix_t matrix;
+ double sx, sy;
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ if (path)
+ image = load_cairo_surface(path);
+ if (image) {
+ pattern = cairo_pattern_create_for_surface(image);
+ sx = (double) cairo_image_surface_get_width(image) /
+ allocation->width;
+ sy = (double) cairo_image_surface_get_height(image) /
+ allocation->height;
+ cairo_matrix_init_scale(&matrix, sx, sy);
+ cairo_pattern_set_matrix(pattern, &matrix);
+ cairo_set_source(cr, pattern);
+ cairo_pattern_destroy (pattern);
+ cairo_surface_destroy(image);
+ cairo_paint(cr);
+ } else {
+ fprintf(stderr, "couldn't load background image: %s\n", path);
+ cairo_set_source_rgb(cr, 0.2, 0, 0);
+ cairo_paint(cr);
+ }
+}
+
+static void
+homescreen_draw(struct widget *widget, void *data)
+{
+ struct homescreen *homescreen = data;
+ cairo_surface_t *surface;
+ struct rectangle allocation;
+ cairo_t *cr;
+ struct launcher *launcher;
+ const int rows = 4, columns = 5, icon_width = 128, icon_height = 128;
+ int x, y, i, width, height, vmargin, hmargin, vpadding, hpadding;
+
+ surface = window_get_surface(homescreen->window);
+ cr = cairo_create(surface);
+
+ widget_get_allocation(widget, &allocation);
+ paint_background(cr, key_homescreen_background, &allocation);
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+
+ width = allocation.width - columns * icon_width;
+ hpadding = width / (columns + 1);
+ hmargin = (width - hpadding * (columns - 1)) / 2;
+
+ height = allocation.height - rows * icon_height;
+ vpadding = height / (rows + 1);
+ vmargin = (height - vpadding * (rows - 1)) / 2;
+
+ x = hmargin;
+ y = vmargin;
+ i = 0;
+
+ wl_list_for_each(launcher, &homescreen->launcher_list, link) {
+ widget_set_allocation(launcher->widget,
+ x, y, icon_width, icon_height);
+ x += icon_width + hpadding;
+ i++;
+ if (i == columns) {
+ x = hmargin;
+ y += icon_height + vpadding;
+ i = 0;
+ }
+ }
+
+ cairo_destroy(cr);
+ cairo_surface_destroy(surface);
+}
+
+static void
+lockscreen_draw(struct widget *widget, void *data)
+{
+ struct lockscreen *lockscreen = data;
+ cairo_surface_t *surface;
+ cairo_surface_t *icon;
+ struct rectangle allocation;
+ cairo_t *cr;
+ int width, height;
+
+ surface = window_get_surface(lockscreen->window);
+ cr = cairo_create(surface);
+
+ widget_get_allocation(widget, &allocation);
+ paint_background(cr, key_lockscreen_background, &allocation);
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+ icon = load_cairo_surface(key_lockscreen_icon);
+ if (icon) {
+ width = cairo_image_surface_get_width(icon);
+ height = cairo_image_surface_get_height(icon);
+ cairo_set_source_surface(cr, icon,
+ allocation.x + (allocation.width - width) / 2,
+ allocation.y + (allocation.height - height) / 2);
+ } else {
+ fprintf(stderr, "couldn't load lockscreen icon: %s\n",
+ key_lockscreen_icon);
+ cairo_set_source_rgb(cr, 0.2, 0, 0);
+ }
+ cairo_paint(cr);
+ cairo_destroy(cr);
+ cairo_surface_destroy(icon);
+ cairo_surface_destroy(surface);
+}
+
+static void
+lockscreen_button_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ uint32_t button,
+ enum wl_pointer_button_state state, void *data)
+{
+ struct lockscreen *lockscreen = data;
+
+ if (state == WL_POINTER_BUTTON_STATE_PRESSED && lockscreen->window) {
+ window_destroy(lockscreen->window);
+ lockscreen->window = NULL;
+ }
+}
+
+static struct homescreen *
+homescreen_create(struct tablet *tablet)
+{
+ struct homescreen *homescreen;
+
+ homescreen = zalloc (sizeof *homescreen);
+ homescreen->window = window_create_custom(tablet->display);
+ homescreen->widget =
+ window_add_widget(homescreen->window, homescreen);
+ window_set_user_data(homescreen->window, homescreen);
+ window_set_title(homescreen->window, "homescreen");
+ widget_set_redraw_handler(homescreen->widget, homescreen_draw);
+
+ return homescreen;
+}
+
+static struct lockscreen *
+lockscreen_create(struct tablet *tablet)
+{
+ struct lockscreen *lockscreen;
+
+ lockscreen = zalloc (sizeof *lockscreen);
+ lockscreen->window = window_create_custom(tablet->display);
+ lockscreen->widget =
+ window_add_widget(lockscreen->window, lockscreen);
+ window_set_user_data(lockscreen->window, lockscreen);
+ window_set_title(lockscreen->window, "lockscreen");
+ widget_set_redraw_handler(lockscreen->widget, lockscreen_draw);
+ widget_set_button_handler(lockscreen->widget,
+ lockscreen_button_handler);
+
+ return lockscreen;
+}
+
+static void
+show_lockscreen(void *data, struct tablet_shell *tablet_shell)
+{
+ struct tablet *tablet = data;
+
+ tablet->lockscreen = lockscreen_create(tablet);
+ tablet_shell_set_lockscreen(tablet->tablet_shell,
+ window_get_wl_surface(tablet->lockscreen->window));
+
+ widget_schedule_resize(tablet->lockscreen->widget,
+ tablet->allocation.width,
+ tablet->allocation.height);
+}
+
+static void
+show_switcher(void *data, struct tablet_shell *tablet_shell)
+{
+ struct tablet *tablet = data;
+
+ tablet->switcher = window_create_custom(tablet->display);
+ window_set_user_data(tablet->switcher, tablet);
+ tablet_shell_set_switcher(tablet->tablet_shell,
+ window_get_wl_surface(tablet->switcher));
+}
+
+static void
+hide_switcher(void *data, struct tablet_shell *tablet_shell)
+{
+}
+
+static const struct tablet_shell_listener tablet_shell_listener = {
+ show_lockscreen,
+ show_switcher,
+ hide_switcher
+};
+
+static int
+launcher_enter_handler(struct widget *widget, struct input *input,
+ float x, float y, void *data)
+{
+ struct launcher *launcher = data;
+
+ launcher->focused = 1;
+ widget_schedule_redraw(widget);
+
+ return CURSOR_LEFT_PTR;
+}
+
+static void
+launcher_leave_handler(struct widget *widget,
+ struct input *input, void *data)
+{
+ struct launcher *launcher = data;
+
+ launcher->focused = 0;
+ widget_schedule_redraw(widget);
+}
+
+static void
+launcher_activate(struct launcher *widget)
+{
+ pid_t pid;
+
+ pid = fork();
+ if (pid < 0) {
+ fprintf(stderr, "fork failed: %m\n");
+ return;
+ }
+
+ if (pid)
+ return;
+
+ if (execl(widget->path, widget->path, NULL) < 0) {
+ fprintf(stderr, "execl '%s' failed: %m\n", widget->path);
+ exit(1);
+ }
+}
+
+static void
+launcher_button_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ uint32_t button,
+ enum wl_pointer_button_state state, void *data)
+{
+ struct launcher *launcher;
+
+ launcher = widget_get_user_data(widget);
+ widget_schedule_redraw(widget);
+ if (state == WL_POINTER_BUTTON_STATE_RELEASED) {
+ launcher_activate(launcher);
+ launcher->pressed = 0;
+ } else if (state == WL_POINTER_BUTTON_STATE_PRESSED)
+ launcher->pressed = 1;
+}
+
+static void
+launcher_redraw_handler(struct widget *widget, void *data)
+{
+ struct launcher *launcher = data;
+ cairo_surface_t *surface;
+ struct rectangle allocation;
+ cairo_t *cr;
+
+ surface = window_get_surface(launcher->homescreen->window);
+ cr = cairo_create(surface);
+
+ widget_get_allocation(widget, &allocation);
+ if (launcher->pressed) {
+ allocation.x++;
+ allocation.y++;
+ }
+
+ cairo_set_source_surface(cr, launcher->icon,
+ allocation.x, allocation.y);
+ cairo_paint(cr);
+
+ if (launcher->focused) {
+ cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 0.4);
+ cairo_mask_surface(cr, launcher->icon,
+ allocation.x, allocation.y);
+ }
+
+ cairo_destroy(cr);
+}
+
+static void
+tablet_shell_add_launcher(struct tablet *tablet,
+ const char *icon, const char *path)
+{
+ struct launcher *launcher;
+ struct homescreen *homescreen = tablet->homescreen;
+
+ launcher = xmalloc(sizeof *launcher);
+ launcher->icon = load_cairo_surface(icon);
+ if ( !launcher->icon ||
+ cairo_surface_status (launcher->icon) != CAIRO_STATUS_SUCCESS) {
+ fprintf(stderr, "couldn't load %s\n", icon);
+ free(launcher);
+ return;
+ }
+ launcher->path = strdup(path);
+
+ launcher->homescreen = homescreen;
+ launcher->widget = widget_add_widget(homescreen->widget, launcher);
+ widget_set_enter_handler(launcher->widget,
+ launcher_enter_handler);
+ widget_set_leave_handler(launcher->widget,
+ launcher_leave_handler);
+ widget_set_button_handler(launcher->widget,
+ launcher_button_handler);
+ widget_set_redraw_handler(launcher->widget,
+ launcher_redraw_handler);
+
+ wl_list_insert(&homescreen->launcher_list, &launcher->link);
+}
+
+static void
+global_handler(struct display *display, uint32_t name,
+ const char *interface, uint32_t version, void *data)
+{
+ struct tablet *tablet = data;
+
+ if (!strcmp(interface, "tablet_shell")) {
+ tablet->tablet_shell =
+ display_bind(display, name,
+ &tablet_shell_interface, 1);
+ tablet_shell_add_listener(tablet->tablet_shell,
+ &tablet_shell_listener, tablet);
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ struct tablet tablet = { 0 };
+ struct display *display;
+ struct output *output;
+ struct weston_config *config;
+ struct weston_config_section *s;
+ char *icon, *path;
+ const char *name;
+
+ display = display_create(&argc, argv);
+ if (display == NULL) {
+ fprintf(stderr, "failed to create display: %m\n");
+ return -1;
+ }
+
+ tablet.display = display;
+
+ display_set_user_data(tablet.display, &tablet);
+ display_set_global_handler(tablet.display, global_handler);
+
+ tablet.homescreen = homescreen_create(&tablet);
+ tablet_shell_set_homescreen(tablet.tablet_shell,
+ window_get_wl_surface(tablet.homescreen->window));
+
+ wl_display_roundtrip (display_get_display(tablet.display));
+
+ wl_list_init(&tablet.homescreen->launcher_list);
+
+ config = weston_config_parse("weston.ini");
+ s = weston_config_get_section(config, "shell", NULL, NULL);
+ weston_config_section_get_string(s, "lockscreen-icon",
+ &key_lockscreen_icon, NULL);
+ weston_config_section_get_string(s, "lockscreen",
+ &key_lockscreen_background, NULL);
+ weston_config_section_get_string(s, "homescreen",
+ &key_homescreen_background, NULL);
+
+ s = NULL;
+ while (weston_config_next_section(config, &s, &name)) {
+ if (strcmp(name, "launcher") != 0)
+ continue;
+
+ weston_config_section_get_string(s, "icon", &icon, NULL);
+ weston_config_section_get_string(s, "path", &path, NULL);
+
+ if (icon != NULL && path != NULL)
+ tablet_shell_add_launcher(&tablet, icon, path);
+ else
+ fprintf(stderr, "invalid launcher section\n");
+
+ free(icon);
+ free(path);
+ }
+
+ weston_config_destroy(config);
+
+ signal(SIGCHLD, sigchild_handler);
+
+ output = display_get_output(tablet.display);
+ output_get_allocation(output, &tablet.allocation);
+ widget_schedule_resize(tablet.homescreen->widget,
+ tablet.allocation.width,
+ tablet.allocation.height);
+ display_run(display);
+
+ return 0;
+}
diff --git a/clients/terminal.c b/clients/terminal.c
new file mode 100644
index 00000000..cec1d67e
--- /dev/null
+++ b/clients/terminal.c
@@ -0,0 +1,2826 @@
+/*
+ * Copyright © 2008 Kristian Høgsberg
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * 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.
+ */
+
+#include <config.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <math.h>
+#include <time.h>
+#include <pty.h>
+#include <ctype.h>
+#include <cairo.h>
+#include <sys/epoll.h>
+#include <wchar.h>
+#include <locale.h>
+
+#include <wayland-client.h>
+
+#include "../shared/config-parser.h"
+#include "window.h"
+
+static int option_fullscreen;
+static char *option_font;
+static int option_font_size;
+static char *option_term;
+static char *option_shell;
+
+static struct wl_list terminal_list;
+
+static struct terminal *
+terminal_create(struct display *display);
+static void
+terminal_destroy(struct terminal *terminal);
+static int
+terminal_run(struct terminal *terminal, const char *path);
+
+#define TERMINAL_DRAW_SINGLE_WIDE_CHARACTERS \
+ " !\"#$%&'()*+,-./" \
+ "0123456789" \
+ ":;<=>?@" \
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
+ "[\\]^_`" \
+ "abcdefghijklmnopqrstuvwxyz" \
+ "{|}~" \
+ ""
+
+#define MOD_SHIFT 0x01
+#define MOD_ALT 0x02
+#define MOD_CTRL 0x04
+
+#define ATTRMASK_BOLD 0x01
+#define ATTRMASK_UNDERLINE 0x02
+#define ATTRMASK_BLINK 0x04
+#define ATTRMASK_INVERSE 0x08
+#define ATTRMASK_CONCEALED 0x10
+
+/* Buffer sizes */
+#define MAX_RESPONSE 256
+#define MAX_ESCAPE 255
+
+/* Terminal modes */
+#define MODE_SHOW_CURSOR 0x00000001
+#define MODE_INVERSE 0x00000002
+#define MODE_AUTOWRAP 0x00000004
+#define MODE_AUTOREPEAT 0x00000008
+#define MODE_LF_NEWLINE 0x00000010
+#define MODE_IRM 0x00000020
+#define MODE_DELETE_SENDS_DEL 0x00000040
+#define MODE_ALT_SENDS_ESC 0x00000080
+
+union utf8_char {
+ unsigned char byte[4];
+ uint32_t ch;
+};
+
+enum utf8_state {
+ utf8state_start,
+ utf8state_accept,
+ utf8state_reject,
+ utf8state_expect3,
+ utf8state_expect2,
+ utf8state_expect1
+};
+
+struct utf8_state_machine {
+ enum utf8_state state;
+ int len;
+ union utf8_char s;
+ uint32_t unicode;
+};
+
+static void
+init_state_machine(struct utf8_state_machine *machine)
+{
+ machine->state = utf8state_start;
+ machine->len = 0;
+ machine->s.ch = 0;
+}
+
+static enum utf8_state
+utf8_next_char(struct utf8_state_machine *machine, unsigned char c)
+{
+ switch(machine->state) {
+ case utf8state_start:
+ case utf8state_accept:
+ case utf8state_reject:
+ machine->s.ch = 0;
+ machine->len = 0;
+ if(c == 0xC0 || c == 0xC1) {
+ /* overlong encoding, reject */
+ machine->state = utf8state_reject;
+ } else if((c & 0x80) == 0) {
+ /* single byte, accept */
+ machine->s.byte[machine->len++] = c;
+ machine->state = utf8state_accept;
+ machine->unicode = c;
+ } else if((c & 0xC0) == 0x80) {
+ /* parser out of sync, ignore byte */
+ machine->state = utf8state_start;
+ } else if((c & 0xE0) == 0xC0) {
+ /* start of two byte sequence */
+ machine->s.byte[machine->len++] = c;
+ machine->state = utf8state_expect1;
+ machine->unicode = c & 0x1f;
+ } else if((c & 0xF0) == 0xE0) {
+ /* start of three byte sequence */
+ machine->s.byte[machine->len++] = c;
+ machine->state = utf8state_expect2;
+ machine->unicode = c & 0x0f;
+ } else if((c & 0xF8) == 0xF0) {
+ /* start of four byte sequence */
+ machine->s.byte[machine->len++] = c;
+ machine->state = utf8state_expect3;
+ machine->unicode = c & 0x07;
+ } else {
+ /* overlong encoding, reject */
+ machine->state = utf8state_reject;
+ }
+ break;
+ case utf8state_expect3:
+ machine->s.byte[machine->len++] = c;
+ machine->unicode = (machine->unicode << 6) | (c & 0x3f);
+ if((c & 0xC0) == 0x80) {
+ /* all good, continue */
+ machine->state = utf8state_expect2;
+ } else {
+ /* missing extra byte, reject */
+ machine->state = utf8state_reject;
+ }
+ break;
+ case utf8state_expect2:
+ machine->s.byte[machine->len++] = c;
+ machine->unicode = (machine->unicode << 6) | (c & 0x3f);
+ if((c & 0xC0) == 0x80) {
+ /* all good, continue */
+ machine->state = utf8state_expect1;
+ } else {
+ /* missing extra byte, reject */
+ machine->state = utf8state_reject;
+ }
+ break;
+ case utf8state_expect1:
+ machine->s.byte[machine->len++] = c;
+ machine->unicode = (machine->unicode << 6) | (c & 0x3f);
+ if((c & 0xC0) == 0x80) {
+ /* all good, accept */
+ machine->state = utf8state_accept;
+ } else {
+ /* missing extra byte, reject */
+ machine->state = utf8state_reject;
+ }
+ break;
+ default:
+ machine->state = utf8state_reject;
+ break;
+ }
+
+ return machine->state;
+}
+
+static uint32_t
+get_unicode(union utf8_char utf8)
+{
+ struct utf8_state_machine machine;
+ int i;
+
+ init_state_machine(&machine);
+ for (i = 0; i < 4; i++) {
+ utf8_next_char(&machine, utf8.byte[i]);
+ if (machine.state == utf8state_accept ||
+ machine.state == utf8state_reject)
+ break;
+ }
+
+ if (machine.state == utf8state_reject)
+ return 0xfffd;
+
+ return machine.unicode;
+}
+
+static bool
+is_wide(union utf8_char utf8)
+{
+ uint32_t unichar = get_unicode(utf8);
+ return wcwidth(unichar) > 1;
+}
+
+struct char_sub {
+ union utf8_char match;
+ union utf8_char replace;
+};
+/* Set last char_sub match to NULL char */
+typedef struct char_sub *character_set;
+
+struct char_sub CS_US[] = {
+ {{{0, }}, {{0, }}}
+};
+static struct char_sub CS_UK[] = {
+ {{{'#', 0, }}, {{0xC2, 0xA3, 0, }}}, /* POUND: £ */
+ {{{0, }}, {{0, }}}
+};
+static struct char_sub CS_SPECIAL[] = {
+ {{{'`', 0, }}, {{0xE2, 0x99, 0xA6, 0}}}, /* diamond: ♦ */
+ {{{'a', 0, }}, {{0xE2, 0x96, 0x92, 0}}}, /* 50% cell: ▒ */
+ {{{'b', 0, }}, {{0xE2, 0x90, 0x89, 0}}}, /* HT: ␉ */
+ {{{'c', 0, }}, {{0xE2, 0x90, 0x8C, 0}}}, /* FF: ␌ */
+ {{{'d', 0, }}, {{0xE2, 0x90, 0x8D, 0}}}, /* CR: ␍ */
+ {{{'e', 0, }}, {{0xE2, 0x90, 0x8A, 0}}}, /* LF: ␊ */
+ {{{'f', 0, }}, {{0xC2, 0xB0, 0, }}}, /* Degree: ° */
+ {{{'g', 0, }}, {{0xC2, 0xB1, 0, }}}, /* Plus/Minus: ± */
+ {{{'h', 0, }}, {{0xE2, 0x90, 0xA4, 0}}}, /* NL: ␤ */
+ {{{'i', 0, }}, {{0xE2, 0x90, 0x8B, 0}}}, /* VT: ␋ */
+ {{{'j', 0, }}, {{0xE2, 0x94, 0x98, 0}}}, /* CN_RB: ┘ */
+ {{{'k', 0, }}, {{0xE2, 0x94, 0x90, 0}}}, /* CN_RT: ┐ */
+ {{{'l', 0, }}, {{0xE2, 0x94, 0x8C, 0}}}, /* CN_LT: ┌ */
+ {{{'m', 0, }}, {{0xE2, 0x94, 0x94, 0}}}, /* CN_LB: └ */
+ {{{'n', 0, }}, {{0xE2, 0x94, 0xBC, 0}}}, /* CROSS: ┼ */
+ {{{'o', 0, }}, {{0xE2, 0x8E, 0xBA, 0}}}, /* Horiz. Scan Line 1: ⎺ */
+ {{{'p', 0, }}, {{0xE2, 0x8E, 0xBB, 0}}}, /* Horiz. Scan Line 3: ⎻ */
+ {{{'q', 0, }}, {{0xE2, 0x94, 0x80, 0}}}, /* Horiz. Scan Line 5: ─ */
+ {{{'r', 0, }}, {{0xE2, 0x8E, 0xBC, 0}}}, /* Horiz. Scan Line 7: ⎼ */
+ {{{'s', 0, }}, {{0xE2, 0x8E, 0xBD, 0}}}, /* Horiz. Scan Line 9: ⎽ */
+ {{{'t', 0, }}, {{0xE2, 0x94, 0x9C, 0}}}, /* TR: ├ */
+ {{{'u', 0, }}, {{0xE2, 0x94, 0xA4, 0}}}, /* TL: ┤ */
+ {{{'v', 0, }}, {{0xE2, 0x94, 0xB4, 0}}}, /* TU: ┴ */
+ {{{'w', 0, }}, {{0xE2, 0x94, 0xAC, 0}}}, /* TD: ┬ */
+ {{{'x', 0, }}, {{0xE2, 0x94, 0x82, 0}}}, /* V: │ */
+ {{{'y', 0, }}, {{0xE2, 0x89, 0xA4, 0}}}, /* LE: ≤ */
+ {{{'z', 0, }}, {{0xE2, 0x89, 0xA5, 0}}}, /* GE: ≥ */
+ {{{'{', 0, }}, {{0xCF, 0x80, 0, }}}, /* PI: π */
+ {{{'|', 0, }}, {{0xE2, 0x89, 0xA0, 0}}}, /* NEQ: ≠ */
+ {{{'}', 0, }}, {{0xC2, 0xA3, 0, }}}, /* POUND: £ */
+ {{{'~', 0, }}, {{0xE2, 0x8B, 0x85, 0}}}, /* DOT: ⋅ */
+ {{{0, }}, {{0, }}}
+};
+
+static void
+apply_char_set(character_set cs, union utf8_char *utf8)
+{
+ int i = 0;
+
+ while (cs[i].match.byte[0]) {
+ if ((*utf8).ch == cs[i].match.ch) {
+ *utf8 = cs[i].replace;
+ break;
+ }
+ i++;
+ }
+}
+
+struct key_map {
+ int sym;
+ int num;
+ char escape;
+ char code;
+};
+/* Set last key_sub sym to NULL */
+typedef struct key_map *keyboard_mode;
+
+static struct key_map KM_NORMAL[] = {
+ { XKB_KEY_Left, 1, '[', 'D' },
+ { XKB_KEY_Right, 1, '[', 'C' },
+ { XKB_KEY_Up, 1, '[', 'A' },
+ { XKB_KEY_Down, 1, '[', 'B' },
+ { XKB_KEY_Home, 1, '[', 'H' },
+ { XKB_KEY_End, 1, '[', 'F' },
+ { 0, 0, 0, 0 }
+};
+static struct key_map KM_APPLICATION[] = {
+ { XKB_KEY_Left, 1, 'O', 'D' },
+ { XKB_KEY_Right, 1, 'O', 'C' },
+ { XKB_KEY_Up, 1, 'O', 'A' },
+ { XKB_KEY_Down, 1, 'O', 'B' },
+ { XKB_KEY_Home, 1, 'O', 'H' },
+ { XKB_KEY_End, 1, 'O', 'F' },
+ { XKB_KEY_KP_Enter, 1, 'O', 'M' },
+ { XKB_KEY_KP_Multiply, 1, 'O', 'j' },
+ { XKB_KEY_KP_Add, 1, 'O', 'k' },
+ { XKB_KEY_KP_Separator, 1, 'O', 'l' },
+ { XKB_KEY_KP_Subtract, 1, 'O', 'm' },
+ { XKB_KEY_KP_Divide, 1, 'O', 'o' },
+ { 0, 0, 0, 0 }
+};
+
+static int
+function_key_response(char escape, int num, uint32_t modifiers,
+ char code, char *response)
+{
+ int mod_num = 0;
+ int len;
+
+ if (modifiers & MOD_SHIFT_MASK) mod_num |= 1;
+ if (modifiers & MOD_ALT_MASK) mod_num |= 2;
+ if (modifiers & MOD_CONTROL_MASK) mod_num |= 4;
+
+ if (mod_num != 0)
+ len = snprintf(response, MAX_RESPONSE, "\e[%d;%d%c",
+ num, mod_num + 1, code);
+ else if (code != '~')
+ len = snprintf(response, MAX_RESPONSE, "\e%c%c",
+ escape, code);
+ else
+ len = snprintf(response, MAX_RESPONSE, "\e%c%d%c",
+ escape, num, code);
+
+ if (len >= MAX_RESPONSE) return MAX_RESPONSE - 1;
+ else return len;
+}
+
+/* returns the number of bytes written into response,
+ * which must have room for MAX_RESPONSE bytes */
+static int
+apply_key_map(keyboard_mode mode, int sym, uint32_t modifiers, char *response)
+{
+ struct key_map map;
+ int len = 0;
+ int i = 0;
+
+ while (mode[i].sym) {
+ map = mode[i++];
+ if (sym == map.sym) {
+ len = function_key_response(map.escape, map.num,
+ modifiers, map.code,
+ response);
+ break;
+ }
+ }
+
+ return len;
+}
+
+struct terminal_color { double r, g, b, a; };
+struct attr {
+ unsigned char fg, bg;
+ char a; /* attributes format:
+ * 76543210
+ * cilub */
+ char s; /* in selection */
+};
+struct color_scheme {
+ struct terminal_color palette[16];
+ char border;
+ struct attr default_attr;
+};
+
+static void
+attr_init(struct attr *data_attr, struct attr attr, int n)
+{
+ int i;
+ for (i = 0; i < n; i++) {
+ data_attr[i] = attr;
+ }
+}
+
+enum escape_state {
+ escape_state_normal = 0,
+ escape_state_escape,
+ escape_state_dcs,
+ escape_state_csi,
+ escape_state_osc,
+ escape_state_inner_escape,
+ escape_state_ignore,
+ escape_state_special
+};
+
+#define ESC_FLAG_WHAT 0x01
+#define ESC_FLAG_GT 0x02
+#define ESC_FLAG_BANG 0x04
+#define ESC_FLAG_CASH 0x08
+#define ESC_FLAG_SQUOTE 0x10
+#define ESC_FLAG_DQUOTE 0x20
+#define ESC_FLAG_SPACE 0x40
+
+enum {
+ SELECT_NONE,
+ SELECT_CHAR,
+ SELECT_WORD,
+ SELECT_LINE
+};
+
+struct terminal {
+ struct window *window;
+ struct widget *widget;
+ struct display *display;
+ union utf8_char *data;
+ struct task io_task;
+ char *tab_ruler;
+ struct attr *data_attr;
+ struct attr curr_attr;
+ uint32_t mode;
+ char origin_mode;
+ char saved_origin_mode;
+ struct attr saved_attr;
+ union utf8_char last_char;
+ int margin_top, margin_bottom;
+ character_set cs, g0, g1;
+ character_set saved_cs, saved_g0, saved_g1;
+ keyboard_mode key_mode;
+ int data_pitch, attr_pitch; /* The width in bytes of a line */
+ int width, height, start, row, column;
+ int saved_row, saved_column;
+ int send_cursor_position;
+ int fd, master;
+ uint32_t modifiers;
+ char escape[MAX_ESCAPE+1];
+ int escape_length;
+ enum escape_state state;
+ enum escape_state outer_state;
+ int escape_flags;
+ struct utf8_state_machine state_machine;
+ int margin;
+ struct color_scheme *color_scheme;
+ struct terminal_color color_table[256];
+ cairo_font_extents_t extents;
+ double average_width;
+ cairo_scaled_font_t *font_normal, *font_bold;
+ uint32_t hide_cursor_serial;
+
+ struct wl_data_source *selection;
+ uint32_t button_time;
+ int dragging, click_count;
+ int selection_start_x, selection_start_y;
+ int selection_end_x, selection_end_y;
+ int selection_start_row, selection_start_col;
+ int selection_end_row, selection_end_col;
+ struct wl_list link;
+};
+
+/* Create default tab stops, every 8 characters */
+static void
+terminal_init_tabs(struct terminal *terminal)
+{
+ int i = 0;
+
+ while (i < terminal->width) {
+ if (i % 8 == 0)
+ terminal->tab_ruler[i] = 1;
+ else
+ terminal->tab_ruler[i] = 0;
+ i++;
+ }
+}
+
+static void
+terminal_init(struct terminal *terminal)
+{
+ terminal->curr_attr = terminal->color_scheme->default_attr;
+ terminal->origin_mode = 0;
+ terminal->mode = MODE_SHOW_CURSOR |
+ MODE_AUTOREPEAT |
+ MODE_ALT_SENDS_ESC |
+ MODE_AUTOWRAP;
+
+ terminal->row = 0;
+ terminal->column = 0;
+
+ terminal->g0 = CS_US;
+ terminal->g1 = CS_US;
+ terminal->cs = terminal->g0;
+ terminal->key_mode = KM_NORMAL;
+
+ terminal->saved_g0 = terminal->g0;
+ terminal->saved_g1 = terminal->g1;
+ terminal->saved_cs = terminal->cs;
+
+ terminal->saved_attr = terminal->curr_attr;
+ terminal->saved_origin_mode = terminal->origin_mode;
+ terminal->saved_row = terminal->row;
+ terminal->saved_column = terminal->column;
+
+ if (terminal->tab_ruler != NULL) terminal_init_tabs(terminal);
+}
+
+static void
+init_color_table(struct terminal *terminal)
+{
+ int c, r;
+ struct terminal_color *color_table = terminal->color_table;
+
+ for (c = 0; c < 256; c ++) {
+ if (c < 16) {
+ color_table[c] = terminal->color_scheme->palette[c];
+ } else if (c < 232) {
+ r = c - 16;
+ color_table[c].b = ((double)(r % 6) / 6.0); r /= 6;
+ color_table[c].g = ((double)(r % 6) / 6.0); r /= 6;
+ color_table[c].r = ((double)(r % 6) / 6.0);
+ color_table[c].a = 1.0;
+ } else {
+ r = (c - 232) * 10 + 8;
+ color_table[c].r = ((double) r) / 256.0;
+ color_table[c].g = color_table[c].r;
+ color_table[c].b = color_table[c].r;
+ color_table[c].a = 1.0;
+ }
+ }
+}
+
+static union utf8_char *
+terminal_get_row(struct terminal *terminal, int row)
+{
+ int index;
+
+ index = (row + terminal->start) % terminal->height;
+
+ return &terminal->data[index * terminal->width];
+}
+
+static struct attr*
+terminal_get_attr_row(struct terminal *terminal, int row)
+{
+ int index;
+
+ index = (row + terminal->start) % terminal->height;
+
+ return &terminal->data_attr[index * terminal->width];
+}
+
+union decoded_attr {
+ struct attr attr;
+ uint32_t key;
+};
+
+static void
+terminal_decode_attr(struct terminal *terminal, int row, int col,
+ union decoded_attr *decoded)
+{
+ struct attr attr;
+ int foreground, background, tmp;
+
+ decoded->attr.s = 0;
+ if (((row == terminal->selection_start_row &&
+ col >= terminal->selection_start_col) ||
+ row > terminal->selection_start_row) &&
+ ((row == terminal->selection_end_row &&
+ col < terminal->selection_end_col) ||
+ row < terminal->selection_end_row))
+ decoded->attr.s = 1;
+
+ /* get the attributes for this character cell */
+ attr = terminal_get_attr_row(terminal, row)[col];
+ if ((attr.a & ATTRMASK_INVERSE) ||
+ decoded->attr.s ||
+ ((terminal->mode & MODE_SHOW_CURSOR) &&
+ window_has_focus(terminal->window) && terminal->row == row &&
+ terminal->column == col)) {
+ foreground = attr.bg;
+ background = attr.fg;
+ if (attr.a & ATTRMASK_BOLD) {
+ if (foreground <= 16) foreground |= 0x08;
+ if (background <= 16) background &= 0x07;
+ }
+ } else {
+ foreground = attr.fg;
+ background = attr.bg;
+ }
+
+ if (terminal->mode & MODE_INVERSE) {
+ tmp = foreground;
+ foreground = background;
+ background = tmp;
+ if (attr.a & ATTRMASK_BOLD) {
+ if (foreground <= 16) foreground |= 0x08;
+ if (background <= 16) background &= 0x07;
+ }
+ }
+
+ decoded->attr.fg = foreground;
+ decoded->attr.bg = background;
+ decoded->attr.a = attr.a;
+}
+
+
+static void
+terminal_scroll_buffer(struct terminal *terminal, int d)
+{
+ int i;
+
+ d = d % (terminal->height + 1);
+ terminal->start = (terminal->start + d) % terminal->height;
+ if (terminal->start < 0) terminal->start = terminal->height + terminal->start;
+ if(d < 0) {
+ d = 0 - d;
+ for(i = 0; i < d; i++) {
+ memset(terminal_get_row(terminal, i), 0, terminal->data_pitch);
+ attr_init(terminal_get_attr_row(terminal, i),
+ terminal->curr_attr, terminal->width);
+ }
+ } else {
+ for(i = terminal->height - d; i < terminal->height; i++) {
+ memset(terminal_get_row(terminal, i), 0, terminal->data_pitch);
+ attr_init(terminal_get_attr_row(terminal, i),
+ terminal->curr_attr, terminal->width);
+ }
+ }
+
+ terminal->selection_start_row -= d;
+ terminal->selection_end_row -= d;
+}
+
+static void
+terminal_scroll_window(struct terminal *terminal, int d)
+{
+ int i;
+ int window_height;
+ int from_row, to_row;
+
+ // scrolling range is inclusive
+ window_height = terminal->margin_bottom - terminal->margin_top + 1;
+ d = d % (window_height + 1);
+ if(d < 0) {
+ d = 0 - d;
+ to_row = terminal->margin_bottom;
+ from_row = terminal->margin_bottom - d;
+
+ for (i = 0; i < (window_height - d); i++) {
+ memcpy(terminal_get_row(terminal, to_row - i),
+ terminal_get_row(terminal, from_row - i),
+ terminal->data_pitch);
+ memcpy(terminal_get_attr_row(terminal, to_row - i),
+ terminal_get_attr_row(terminal, from_row - i),
+ terminal->attr_pitch);
+ }
+ for (i = terminal->margin_top; i < (terminal->margin_top + d); i++) {
+ memset(terminal_get_row(terminal, i), 0, terminal->data_pitch);
+ attr_init(terminal_get_attr_row(terminal, i),
+ terminal->curr_attr, terminal->width);
+ }
+ } else {
+ to_row = terminal->margin_top;
+ from_row = terminal->margin_top + d;
+
+ for (i = 0; i < (window_height - d); i++) {
+ memcpy(terminal_get_row(terminal, to_row + i),
+ terminal_get_row(terminal, from_row + i),
+ terminal->data_pitch);
+ memcpy(terminal_get_attr_row(terminal, to_row + i),
+ terminal_get_attr_row(terminal, from_row + i),
+ terminal->attr_pitch);
+ }
+ for (i = terminal->margin_bottom - d + 1; i <= terminal->margin_bottom; i++) {
+ memset(terminal_get_row(terminal, i), 0, terminal->data_pitch);
+ attr_init(terminal_get_attr_row(terminal, i),
+ terminal->curr_attr, terminal->width);
+ }
+ }
+}
+
+static void
+terminal_scroll(struct terminal *terminal, int d)
+{
+ if(terminal->margin_top == 0 && terminal->margin_bottom == terminal->height - 1)
+ terminal_scroll_buffer(terminal, d);
+ else
+ terminal_scroll_window(terminal, d);
+}
+
+static void
+terminal_shift_line(struct terminal *terminal, int d)
+{
+ union utf8_char *row;
+ struct attr *attr_row;
+
+ row = terminal_get_row(terminal, terminal->row);
+ attr_row = terminal_get_attr_row(terminal, terminal->row);
+
+ if ((terminal->width + d) <= terminal->column)
+ d = terminal->column + 1 - terminal->width;
+ if ((terminal->column + d) >= terminal->width)
+ d = terminal->width - terminal->column - 1;
+
+ if (d < 0) {
+ d = 0 - d;
+ memmove(&row[terminal->column],
+ &row[terminal->column + d],
+ (terminal->width - terminal->column - d) * sizeof(union utf8_char));
+ memmove(&attr_row[terminal->column], &attr_row[terminal->column + d],
+ (terminal->width - terminal->column - d) * sizeof(struct attr));
+ memset(&row[terminal->width - d], 0, d * sizeof(union utf8_char));
+ attr_init(&attr_row[terminal->width - d], terminal->curr_attr, d);
+ } else {
+ memmove(&row[terminal->column + d], &row[terminal->column],
+ (terminal->width - terminal->column - d) * sizeof(union utf8_char));
+ memmove(&attr_row[terminal->column + d], &attr_row[terminal->column],
+ (terminal->width - terminal->column - d) * sizeof(struct attr));
+ memset(&row[terminal->column], 0, d * sizeof(union utf8_char));
+ attr_init(&attr_row[terminal->column], terminal->curr_attr, d);
+ }
+}
+
+static void
+terminal_resize_cells(struct terminal *terminal, int width, int height)
+{
+ size_t size;
+ union utf8_char *data;
+ struct attr *data_attr;
+ char *tab_ruler;
+ int data_pitch, attr_pitch;
+ int i, l, total_rows;
+ struct rectangle allocation;
+ struct winsize ws;
+
+ if (terminal->width == width && terminal->height == height)
+ return;
+
+ data_pitch = width * sizeof(union utf8_char);
+ size = data_pitch * height;
+ data = zalloc(size);
+ attr_pitch = width * sizeof(struct attr);
+ data_attr = malloc(attr_pitch * height);
+ tab_ruler = zalloc(width);
+ attr_init(data_attr, terminal->curr_attr, width * height);
+ if (terminal->data && terminal->data_attr) {
+ if (width > terminal->width)
+ l = terminal->width;
+ else
+ l = width;
+
+ if (terminal->height > height) {
+ total_rows = height;
+ i = 1 + terminal->row - height;
+ if (i > 0) {
+ terminal->start = (terminal->start + i) % terminal->height;
+ terminal->row = terminal->row - i;
+ }
+ } else {
+ total_rows = terminal->height;
+ }
+
+ for (i = 0; i < total_rows; i++) {
+ memcpy(&data[width * i],
+ terminal_get_row(terminal, i),
+ l * sizeof(union utf8_char));
+ memcpy(&data_attr[width * i],
+ terminal_get_attr_row(terminal, i),
+ l * sizeof(struct attr));
+ }
+
+ free(terminal->data);
+ free(terminal->data_attr);
+ free(terminal->tab_ruler);
+ }
+
+ terminal->data_pitch = data_pitch;
+ terminal->attr_pitch = attr_pitch;
+ terminal->margin_bottom =
+ height - (terminal->height - terminal->margin_bottom);
+ terminal->width = width;
+ terminal->height = height;
+ terminal->data = data;
+ terminal->data_attr = data_attr;
+ terminal->tab_ruler = tab_ruler;
+ terminal->start = 0;
+ terminal_init_tabs(terminal);
+
+ /* Update the window size */
+ ws.ws_row = terminal->height;
+ ws.ws_col = terminal->width;
+ widget_get_allocation(terminal->widget, &allocation);
+ ws.ws_xpixel = allocation.width;
+ ws.ws_ypixel = allocation.height;
+ ioctl(terminal->master, TIOCSWINSZ, &ws);
+}
+
+static void
+resize_handler(struct widget *widget,
+ int32_t width, int32_t height, void *data)
+{
+ struct terminal *terminal = data;
+ int32_t columns, rows, m;
+
+ m = 2 * terminal->margin;
+ columns = (width - m) / (int32_t) terminal->average_width;
+ rows = (height - m) / (int32_t) terminal->extents.height;
+
+ if (!window_is_fullscreen(terminal->window) &&
+ !window_is_maximized(terminal->window)) {
+ width = columns * terminal->average_width + m;
+ height = rows * terminal->extents.height + m;
+ widget_set_size(terminal->widget, width, height);
+ }
+
+ terminal_resize_cells(terminal, columns, rows);
+}
+
+static void
+terminal_resize(struct terminal *terminal, int columns, int rows)
+{
+ int32_t width, height, m;
+
+ if (window_is_fullscreen(terminal->window) ||
+ window_is_maximized(terminal->window))
+ return;
+
+ m = 2 * terminal->margin;
+ width = columns * terminal->average_width + m;
+ height = rows * terminal->extents.height + m;
+
+ frame_set_child_size(terminal->widget, width, height);
+}
+
+struct color_scheme DEFAULT_COLORS = {
+ {
+ {0, 0, 0, 1}, /* black */
+ {0.66, 0, 0, 1}, /* red */
+ {0 , 0.66, 0, 1}, /* green */
+ {0.66, 0.33, 0, 1}, /* orange (nicer than muddy yellow) */
+ {0 , 0 , 0.66, 1}, /* blue */
+ {0.66, 0 , 0.66, 1}, /* magenta */
+ {0, 0.66, 0.66, 1}, /* cyan */
+ {0.66, 0.66, 0.66, 1}, /* light grey */
+ {0.22, 0.33, 0.33, 1}, /* dark grey */
+ {1, 0.33, 0.33, 1}, /* high red */
+ {0.33, 1, 0.33, 1}, /* high green */
+ {1, 1, 0.33, 1}, /* high yellow */
+ {0.33, 0.33, 1, 1}, /* high blue */
+ {1, 0.33, 1, 1}, /* high magenta */
+ {0.33, 1, 1, 1}, /* high cyan */
+ {1, 1, 1, 1} /* white */
+ },
+ 0, /* black border */
+ {7, 0, 0, } /* bg:black (0), fg:light gray (7) */
+};
+
+static void
+terminal_set_color(struct terminal *terminal, cairo_t *cr, int index)
+{
+ cairo_set_source_rgba(cr,
+ terminal->color_table[index].r,
+ terminal->color_table[index].g,
+ terminal->color_table[index].b,
+ terminal->color_table[index].a);
+}
+
+static void
+terminal_send_selection(struct terminal *terminal, int fd)
+{
+ int row, col;
+ union utf8_char *p_row;
+ union decoded_attr attr;
+ FILE *fp;
+ int len;
+
+ fp = fdopen(fd, "w");
+ if (fp == NULL){
+ close(fd);
+ return;
+ }
+ for (row = 0; row < terminal->height; row++) {
+ p_row = terminal_get_row(terminal, row);
+ for (col = 0; col < terminal->width; col++) {
+ if (p_row[col].ch == 0x200B) /* space glyph */
+ continue;
+ /* get the attributes for this character cell */
+ terminal_decode_attr(terminal, row, col, &attr);
+ if (!attr.attr.s)
+ continue;
+ len = strnlen((char *) p_row[col].byte, 4);
+ if (len > 0)
+ fwrite(p_row[col].byte, 1, len, fp);
+ if (len == 0 || col == terminal->width - 1) {
+ fwrite("\n", 1, 1, fp);
+ break;
+ }
+ }
+ }
+ fclose(fp);
+}
+
+struct glyph_run {
+ struct terminal *terminal;
+ cairo_t *cr;
+ unsigned int count;
+ union decoded_attr attr;
+ cairo_glyph_t glyphs[256], *g;
+};
+
+static void
+glyph_run_init(struct glyph_run *run, struct terminal *terminal, cairo_t *cr)
+{
+ run->terminal = terminal;
+ run->cr = cr;
+ run->g = run->glyphs;
+ run->count = 0;
+ run->attr.key = 0;
+}
+
+static void
+glyph_run_flush(struct glyph_run *run, union decoded_attr attr)
+{
+ cairo_scaled_font_t *font;
+
+ if (run->count > ARRAY_LENGTH(run->glyphs) - 10 ||
+ (attr.key != run->attr.key)) {
+ if (run->attr.attr.a & (ATTRMASK_BOLD | ATTRMASK_BLINK))
+ font = run->terminal->font_bold;
+ else
+ font = run->terminal->font_normal;
+ cairo_set_scaled_font(run->cr, font);
+ terminal_set_color(run->terminal, run->cr,
+ run->attr.attr.fg);
+
+ if (!(run->attr.attr.a & ATTRMASK_CONCEALED))
+ cairo_show_glyphs (run->cr, run->glyphs, run->count);
+ run->g = run->glyphs;
+ run->count = 0;
+ }
+ run->attr = attr;
+}
+
+static void
+glyph_run_add(struct glyph_run *run, int x, int y, union utf8_char *c)
+{
+ int num_glyphs;
+ cairo_scaled_font_t *font;
+
+ num_glyphs = ARRAY_LENGTH(run->glyphs) - run->count;
+
+ if (run->attr.attr.a & (ATTRMASK_BOLD | ATTRMASK_BLINK))
+ font = run->terminal->font_bold;
+ else
+ font = run->terminal->font_normal;
+
+ cairo_move_to(run->cr, x, y);
+ cairo_scaled_font_text_to_glyphs (font, x, y,
+ (char *) c->byte, 4,
+ &run->g, &num_glyphs,
+ NULL, NULL, NULL);
+ run->g += num_glyphs;
+ run->count += num_glyphs;
+}
+
+
+static void
+redraw_handler(struct widget *widget, void *data)
+{
+ struct terminal *terminal = data;
+ struct rectangle allocation;
+ cairo_t *cr;
+ int top_margin, side_margin;
+ int row, col, cursor_x, cursor_y;
+ union utf8_char *p_row;
+ union decoded_attr attr;
+ int text_x, text_y;
+ cairo_surface_t *surface;
+ double d;
+ struct glyph_run run;
+ cairo_font_extents_t extents;
+ double average_width;
+ double unichar_width;
+
+ surface = window_get_surface(terminal->window);
+ widget_get_allocation(terminal->widget, &allocation);
+ cr = widget_cairo_create(terminal->widget);
+ cairo_rectangle(cr, allocation.x, allocation.y,
+ allocation.width, allocation.height);
+ cairo_clip(cr);
+ cairo_push_group(cr);
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ terminal_set_color(terminal, cr, terminal->color_scheme->border);
+ cairo_paint(cr);
+
+ cairo_set_scaled_font(cr, terminal->font_normal);
+
+ extents = terminal->extents;
+ average_width = terminal->average_width;
+ side_margin = (allocation.width - terminal->width * average_width) / 2;
+ top_margin = (allocation.height - terminal->height * extents.height) / 2;
+
+ cairo_set_line_width(cr, 1.0);
+ cairo_translate(cr, allocation.x + side_margin,
+ allocation.y + top_margin);
+ /* paint the background */
+ for (row = 0; row < terminal->height; row++) {
+ p_row = terminal_get_row(terminal, row);
+ for (col = 0; col < terminal->width; col++) {
+ /* get the attributes for this character cell */
+ terminal_decode_attr(terminal, row, col, &attr);
+
+ if (attr.attr.bg == terminal->color_scheme->border)
+ continue;
+
+ if (is_wide(p_row[col]))
+ unichar_width = 2 * average_width;
+ else
+ unichar_width = average_width;
+
+ terminal_set_color(terminal, cr, attr.attr.bg);
+ cairo_move_to(cr, col * average_width,
+ row * extents.height);
+ cairo_rel_line_to(cr, unichar_width, 0);
+ cairo_rel_line_to(cr, 0, extents.height);
+ cairo_rel_line_to(cr, -unichar_width, 0);
+ cairo_close_path(cr);
+ cairo_fill(cr);
+ }
+ }
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+
+ /* paint the foreground */
+ glyph_run_init(&run, terminal, cr);
+ for (row = 0; row < terminal->height; row++) {
+ p_row = terminal_get_row(terminal, row);
+ for (col = 0; col < terminal->width; col++) {
+ /* get the attributes for this character cell */
+ terminal_decode_attr(terminal, row, col, &attr);
+
+ glyph_run_flush(&run, attr);
+
+ text_x = col * average_width;
+ text_y = extents.ascent + row * extents.height;
+ if (attr.attr.a & ATTRMASK_UNDERLINE) {
+ terminal_set_color(terminal, cr, attr.attr.fg);
+ cairo_move_to(cr, text_x, (double)text_y + 1.5);
+ cairo_line_to(cr, text_x + average_width, (double) text_y + 1.5);
+ cairo_stroke(cr);
+ }
+
+ glyph_run_add(&run, text_x, text_y, &p_row[col]);
+ }
+ }
+
+ attr.key = ~0;
+ glyph_run_flush(&run, attr);
+
+ if ((terminal->mode & MODE_SHOW_CURSOR) &&
+ !window_has_focus(terminal->window)) {
+ d = 0.5;
+
+ cairo_set_line_width(cr, 1);
+ cairo_move_to(cr, terminal->column * average_width + d,
+ terminal->row * extents.height + d);
+ cairo_rel_line_to(cr, average_width - 2 * d, 0);
+ cairo_rel_line_to(cr, 0, extents.height - 2 * d);
+ cairo_rel_line_to(cr, -average_width + 2 * d, 0);
+ cairo_close_path(cr);
+
+ cairo_stroke(cr);
+ }
+
+ cairo_pop_group_to_source(cr);
+ cairo_paint(cr);
+ cairo_destroy(cr);
+ cairo_surface_destroy(surface);
+
+ if (terminal->send_cursor_position) {
+ cursor_x = side_margin + allocation.x +
+ terminal->column * average_width;
+ cursor_y = top_margin + allocation.y +
+ terminal->row * extents.height;
+ window_set_text_cursor_position(terminal->window,
+ cursor_x, cursor_y);
+ terminal->send_cursor_position = 0;
+ }
+}
+
+static void
+terminal_write(struct terminal *terminal, const char *data, size_t length)
+{
+ if (write(terminal->master, data, length) < 0)
+ abort();
+ terminal->send_cursor_position = 1;
+}
+
+static void
+terminal_data(struct terminal *terminal, const char *data, size_t length);
+
+static void
+handle_char(struct terminal *terminal, union utf8_char utf8);
+
+static void
+handle_sgr(struct terminal *terminal, int code);
+
+static void
+handle_term_parameter(struct terminal *terminal, int code, int sr)
+{
+ int i;
+
+ if (terminal->escape_flags & ESC_FLAG_WHAT) {
+ switch(code) {
+ case 1: /* DECCKM */
+ if (sr) terminal->key_mode = KM_APPLICATION;
+ else terminal->key_mode = KM_NORMAL;
+ break;
+ case 2: /* DECANM */
+ /* No VT52 support yet */
+ terminal->g0 = CS_US;
+ terminal->g1 = CS_US;
+ terminal->cs = terminal->g0;
+ break;
+ case 3: /* DECCOLM */
+ if (sr)
+ terminal_resize(terminal, 132, 24);
+ else
+ terminal_resize(terminal, 80, 24);
+
+ /* set columns, but also home cursor and clear screen */
+ terminal->row = 0; terminal->column = 0;
+ for (i = 0; i < terminal->height; i++) {
+ memset(terminal_get_row(terminal, i),
+ 0, terminal->data_pitch);
+ attr_init(terminal_get_attr_row(terminal, i),
+ terminal->curr_attr, terminal->width);
+ }
+ break;
+ case 5: /* DECSCNM */
+ if (sr) terminal->mode |= MODE_INVERSE;
+ else terminal->mode &= ~MODE_INVERSE;
+ break;
+ case 6: /* DECOM */
+ terminal->origin_mode = sr;
+ if (terminal->origin_mode)
+ terminal->row = terminal->margin_top;
+ else
+ terminal->row = 0;
+ terminal->column = 0;
+ break;
+ case 7: /* DECAWM */
+ if (sr) terminal->mode |= MODE_AUTOWRAP;
+ else terminal->mode &= ~MODE_AUTOWRAP;
+ break;
+ case 8: /* DECARM */
+ if (sr) terminal->mode |= MODE_AUTOREPEAT;
+ else terminal->mode &= ~MODE_AUTOREPEAT;
+ break;
+ case 12: /* Very visible cursor (CVVIS) */
+ /* FIXME: What do we do here. */
+ break;
+ case 25:
+ if (sr) terminal->mode |= MODE_SHOW_CURSOR;
+ else terminal->mode &= ~MODE_SHOW_CURSOR;
+ break;
+ case 1034: /* smm/rmm, meta mode on/off */
+ /* ignore */
+ break;
+ case 1037: /* deleteSendsDel */
+ if (sr) terminal->mode |= MODE_DELETE_SENDS_DEL;
+ else terminal->mode &= ~MODE_DELETE_SENDS_DEL;
+ break;
+ case 1039: /* altSendsEscape */
+ if (sr) terminal->mode |= MODE_ALT_SENDS_ESC;
+ else terminal->mode &= ~MODE_ALT_SENDS_ESC;
+ break;
+ case 1049: /* rmcup/smcup, alternate screen */
+ /* Ignore. Should be possible to implement,
+ * but it's kind of annoying. */
+ break;
+ default:
+ fprintf(stderr, "Unknown parameter: ?%d\n", code);
+ break;
+ }
+ } else {
+ switch(code) {
+ case 4: /* IRM */
+ if (sr) terminal->mode |= MODE_IRM;
+ else terminal->mode &= ~MODE_IRM;
+ break;
+ case 20: /* LNM */
+ if (sr) terminal->mode |= MODE_LF_NEWLINE;
+ else terminal->mode &= ~MODE_LF_NEWLINE;
+ break;
+ default:
+ fprintf(stderr, "Unknown parameter: %d\n", code);
+ break;
+ }
+ }
+}
+
+static void
+handle_dcs(struct terminal *terminal)
+{
+}
+
+static void
+handle_osc(struct terminal *terminal)
+{
+ char *p;
+ int code;
+
+ terminal->escape[terminal->escape_length++] = '\0';
+ p = &terminal->escape[2];
+ code = strtol(p, &p, 10);
+ if (*p == ';') p++;
+
+ switch (code) {
+ case 0: /* Icon name and window title */
+ case 1: /* Icon label */
+ case 2: /* Window title*/
+ window_set_title(terminal->window, p);
+ break;
+ case 7: /* shell cwd as uri */
+ break;
+ default:
+ fprintf(stderr, "Unknown OSC escape code %d, text %s\n",
+ code, p);
+ break;
+ }
+}
+
+static void
+handle_escape(struct terminal *terminal)
+{
+ union utf8_char *row;
+ struct attr *attr_row;
+ char *p;
+ int i, count, x, y, top, bottom;
+ int args[10], set[10] = { 0, };
+ char response[MAX_RESPONSE] = {0, };
+ struct rectangle allocation;
+
+ terminal->escape[terminal->escape_length++] = '\0';
+ i = 0;
+ p = &terminal->escape[2];
+ while ((isdigit(*p) || *p == ';') && i < 10) {
+ if (*p == ';') {
+ if (!set[i]) {
+ args[i] = 0;
+ set[i] = 1;
+ }
+ p++;
+ i++;
+ } else {
+ args[i] = strtol(p, &p, 10);
+ set[i] = 1;
+ }
+ }
+
+ switch (*p) {
+ case '@': /* ICH */
+ count = set[0] ? args[0] : 1;
+ if (count == 0) count = 1;
+ terminal_shift_line(terminal, count);
+ break;
+ case 'A': /* CUU */
+ count = set[0] ? args[0] : 1;
+ if (count == 0) count = 1;
+ if (terminal->row - count >= terminal->margin_top)
+ terminal->row -= count;
+ else
+ terminal->row = terminal->margin_top;
+ break;
+ case 'B': /* CUD */
+ count = set[0] ? args[0] : 1;
+ if (count == 0) count = 1;
+ if (terminal->row + count <= terminal->margin_bottom)
+ terminal->row += count;
+ else
+ terminal->row = terminal->margin_bottom;
+ break;
+ case 'C': /* CUF */
+ count = set[0] ? args[0] : 1;
+ if (count == 0) count = 1;
+ if ((terminal->column + count) < terminal->width)
+ terminal->column += count;
+ else
+ terminal->column = terminal->width - 1;
+ break;
+ case 'D': /* CUB */
+ count = set[0] ? args[0] : 1;
+ if (count == 0) count = 1;
+ if ((terminal->column - count) >= 0)
+ terminal->column -= count;
+ else
+ terminal->column = 0;
+ break;
+ case 'E': /* CNL */
+ count = set[0] ? args[0] : 1;
+ if (terminal->row + count <= terminal->margin_bottom)
+ terminal->row += count;
+ else
+ terminal->row = terminal->margin_bottom;
+ terminal->column = 0;
+ break;
+ case 'F': /* CPL */
+ count = set[0] ? args[0] : 1;
+ if (terminal->row - count >= terminal->margin_top)
+ terminal->row -= count;
+ else
+ terminal->row = terminal->margin_top;
+ terminal->column = 0;
+ break;
+ case 'G': /* CHA */
+ y = set[0] ? args[0] : 1;
+ y = y <= 0 ? 1 : y > terminal->width ? terminal->width : y;
+
+ terminal->column = y - 1;
+ break;
+ case 'f': /* HVP */
+ case 'H': /* CUP */
+ x = (set[1] ? args[1] : 1) - 1;
+ x = x < 0 ? 0 :
+ (x >= terminal->width ? terminal->width - 1 : x);
+
+ y = (set[0] ? args[0] : 1) - 1;
+ if (terminal->origin_mode) {
+ y += terminal->margin_top;
+ y = y < terminal->margin_top ? terminal->margin_top :
+ (y > terminal->margin_bottom ? terminal->margin_bottom : y);
+ } else {
+ y = y < 0 ? 0 :
+ (y >= terminal->height ? terminal->height - 1 : y);
+ }
+
+ terminal->row = y;
+ terminal->column = x;
+ break;
+ case 'I': /* CHT */
+ count = set[0] ? args[0] : 1;
+ if (count == 0) count = 1;
+ while (count > 0 && terminal->column < terminal->width) {
+ if (terminal->tab_ruler[terminal->column]) count--;
+ terminal->column++;
+ }
+ terminal->column--;
+ break;
+ case 'J': /* ED */
+ row = terminal_get_row(terminal, terminal->row);
+ attr_row = terminal_get_attr_row(terminal, terminal->row);
+ if (!set[0] || args[0] == 0 || args[0] > 2) {
+ memset(&row[terminal->column],
+ 0, (terminal->width - terminal->column) * sizeof(union utf8_char));
+ attr_init(&attr_row[terminal->column],
+ terminal->curr_attr, terminal->width - terminal->column);
+ for (i = terminal->row + 1; i < terminal->height; i++) {
+ memset(terminal_get_row(terminal, i),
+ 0, terminal->data_pitch);
+ attr_init(terminal_get_attr_row(terminal, i),
+ terminal->curr_attr, terminal->width);
+ }
+ } else if (args[0] == 1) {
+ memset(row, 0, (terminal->column+1) * sizeof(union utf8_char));
+ attr_init(attr_row, terminal->curr_attr, terminal->column+1);
+ for (i = 0; i < terminal->row; i++) {
+ memset(terminal_get_row(terminal, i),
+ 0, terminal->data_pitch);
+ attr_init(terminal_get_attr_row(terminal, i),
+ terminal->curr_attr, terminal->width);
+ }
+ } else if (args[0] == 2) {
+ for (i = 0; i < terminal->height; i++) {
+ memset(terminal_get_row(terminal, i),
+ 0, terminal->data_pitch);
+ attr_init(terminal_get_attr_row(terminal, i),
+ terminal->curr_attr, terminal->width);
+ }
+ }
+ break;
+ case 'K': /* EL */
+ row = terminal_get_row(terminal, terminal->row);
+ attr_row = terminal_get_attr_row(terminal, terminal->row);
+ if (!set[0] || args[0] == 0 || args[0] > 2) {
+ memset(&row[terminal->column], 0,
+ (terminal->width - terminal->column) * sizeof(union utf8_char));
+ attr_init(&attr_row[terminal->column], terminal->curr_attr,
+ terminal->width - terminal->column);
+ } else if (args[0] == 1) {
+ memset(row, 0, (terminal->column+1) * sizeof(union utf8_char));
+ attr_init(attr_row, terminal->curr_attr, terminal->column+1);
+ } else if (args[0] == 2) {
+ memset(row, 0, terminal->data_pitch);
+ attr_init(attr_row, terminal->curr_attr, terminal->width);
+ }
+ break;
+ case 'L': /* IL */
+ count = set[0] ? args[0] : 1;
+ if (count == 0) count = 1;
+ if (terminal->row >= terminal->margin_top &&
+ terminal->row < terminal->margin_bottom)
+ {
+ top = terminal->margin_top;
+ terminal->margin_top = terminal->row;
+ terminal_scroll(terminal, 0 - count);
+ terminal->margin_top = top;
+ } else if (terminal->row == terminal->margin_bottom) {
+ memset(terminal_get_row(terminal, terminal->row),
+ 0, terminal->data_pitch);
+ attr_init(terminal_get_attr_row(terminal, terminal->row),
+ terminal->curr_attr, terminal->width);
+ }
+ break;
+ case 'M': /* DL */
+ count = set[0] ? args[0] : 1;
+ if (count == 0) count = 1;
+ if (terminal->row >= terminal->margin_top &&
+ terminal->row < terminal->margin_bottom)
+ {
+ top = terminal->margin_top;
+ terminal->margin_top = terminal->row;
+ terminal_scroll(terminal, count);
+ terminal->margin_top = top;
+ } else if (terminal->row == terminal->margin_bottom) {
+ memset(terminal_get_row(terminal, terminal->row),
+ 0, terminal->data_pitch);
+ }
+ break;
+ case 'P': /* DCH */
+ count = set[0] ? args[0] : 1;
+ if (count == 0) count = 1;
+ terminal_shift_line(terminal, 0 - count);
+ break;
+ case 'S': /* SU */
+ terminal_scroll(terminal, set[0] ? args[0] : 1);
+ break;
+ case 'T': /* SD */
+ terminal_scroll(terminal, 0 - (set[0] ? args[0] : 1));
+ break;
+ case 'X': /* ECH */
+ count = set[0] ? args[0] : 1;
+ if (count == 0) count = 1;
+ if ((terminal->column + count) > terminal->width)
+ count = terminal->width - terminal->column;
+ row = terminal_get_row(terminal, terminal->row);
+ attr_row = terminal_get_attr_row(terminal, terminal->row);
+ memset(&row[terminal->column], 0, count * sizeof(union utf8_char));
+ attr_init(&attr_row[terminal->column], terminal->curr_attr, count);
+ break;
+ case 'Z': /* CBT */
+ count = set[0] ? args[0] : 1;
+ if (count == 0) count = 1;
+ while (count > 0 && terminal->column >= 0) {
+ if (terminal->tab_ruler[terminal->column]) count--;
+ terminal->column--;
+ }
+ terminal->column++;
+ break;
+ case '`': /* HPA */
+ y = set[0] ? args[0] : 1;
+ y = y <= 0 ? 1 : y > terminal->width ? terminal->width : y;
+
+ terminal->column = y - 1;
+ break;
+ case 'b': /* REP */
+ count = set[0] ? args[0] : 1;
+ if (count == 0) count = 1;
+ if (terminal->last_char.byte[0])
+ for (i = 0; i < count; i++)
+ handle_char(terminal, terminal->last_char);
+ terminal->last_char.byte[0] = 0;
+ break;
+ case 'c': /* Primary DA */
+ terminal_write(terminal, "\e[?6c", 5);
+ break;
+ case 'd': /* VPA */
+ x = set[0] ? args[0] : 1;
+ x = x <= 0 ? 1 : x > terminal->height ? terminal->height : x;
+
+ terminal->row = x - 1;
+ break;
+ case 'g': /* TBC */
+ if (!set[0] || args[0] == 0) {
+ terminal->tab_ruler[terminal->column] = 0;
+ } else if (args[0] == 3) {
+ memset(terminal->tab_ruler, 0, terminal->width);
+ }
+ break;
+ case 'h': /* SM */
+ for(i = 0; i < 10 && set[i]; i++) {
+ handle_term_parameter(terminal, args[i], 1);
+ }
+ break;
+ case 'l': /* RM */
+ for(i = 0; i < 10 && set[i]; i++) {
+ handle_term_parameter(terminal, args[i], 0);
+ }
+ break;
+ case 'm': /* SGR */
+ for(i = 0; i < 10; i++) {
+ if (i <= 7 && set[i] && set[i + 1] &&
+ set[i + 2] && args[i + 1] == 5)
+ {
+ if (args[i] == 38) {
+ handle_sgr(terminal, args[i + 2] + 256);
+ break;
+ } else if (args[i] == 48) {
+ handle_sgr(terminal, args[i + 2] + 512);
+ break;
+ }
+ }
+ if(set[i]) {
+ handle_sgr(terminal, args[i]);
+ } else if(i == 0) {
+ handle_sgr(terminal, 0);
+ break;
+ } else {
+ break;
+ }
+ }
+ break;
+ case 'n': /* DSR */
+ i = set[0] ? args[0] : 0;
+ if (i == 0 || i == 5) {
+ terminal_write(terminal, "\e[0n", 4);
+ } else if (i == 6) {
+ snprintf(response, MAX_RESPONSE, "\e[%d;%dR",
+ terminal->origin_mode ?
+ terminal->row+terminal->margin_top : terminal->row+1,
+ terminal->column+1);
+ terminal_write(terminal, response, strlen(response));
+ }
+ break;
+ case 'r':
+ if(!set[0]) {
+ terminal->margin_top = 0;
+ terminal->margin_bottom = terminal->height-1;
+ terminal->row = 0;
+ terminal->column = 0;
+ } else {
+ top = (set[0] ? args[0] : 1) - 1;
+ top = top < 0 ? 0 :
+ (top >= terminal->height ? terminal->height - 1 : top);
+ bottom = (set[1] ? args[1] : 1) - 1;
+ bottom = bottom < 0 ? 0 :
+ (bottom >= terminal->height ? terminal->height - 1 : bottom);
+ if(bottom > top) {
+ terminal->margin_top = top;
+ terminal->margin_bottom = bottom;
+ } else {
+ terminal->margin_top = 0;
+ terminal->margin_bottom = terminal->height-1;
+ }
+ if(terminal->origin_mode)
+ terminal->row = terminal->margin_top;
+ else
+ terminal->row = 0;
+ terminal->column = 0;
+ }
+ break;
+ case 's':
+ terminal->saved_row = terminal->row;
+ terminal->saved_column = terminal->column;
+ break;
+ case 't': /* windowOps */
+ if (!set[0]) break;
+ switch (args[0]) {
+ case 4: /* resize px */
+ if (set[1] && set[2]) {
+ widget_schedule_resize(terminal->widget,
+ args[2], args[1]);
+ }
+ break;
+ case 8: /* resize ch */
+ if (set[1] && set[2]) {
+ terminal_resize(terminal, args[2], args[1]);
+ }
+ break;
+ case 13: /* report position */
+ widget_get_allocation(terminal->widget, &allocation);
+ snprintf(response, MAX_RESPONSE, "\e[3;%d;%dt",
+ allocation.x, allocation.y);
+ terminal_write(terminal, response, strlen(response));
+ break;
+ case 14: /* report px */
+ widget_get_allocation(terminal->widget, &allocation);
+ snprintf(response, MAX_RESPONSE, "\e[4;%d;%dt",
+ allocation.height, allocation.width);
+ terminal_write(terminal, response, strlen(response));
+ break;
+ case 18: /* report ch */
+ snprintf(response, MAX_RESPONSE, "\e[9;%d;%dt",
+ terminal->height, terminal->width);
+ terminal_write(terminal, response, strlen(response));
+ break;
+ case 21: /* report title */
+ snprintf(response, MAX_RESPONSE, "\e]l%s\e\\",
+ window_get_title(terminal->window));
+ terminal_write(terminal, response, strlen(response));
+ break;
+ default:
+ if (args[0] >= 24)
+ terminal_resize(terminal, terminal->width, args[0]);
+ else
+ fprintf(stderr, "Unimplemented windowOp %d\n", args[0]);
+ break;
+ }
+ case 'u':
+ terminal->row = terminal->saved_row;
+ terminal->column = terminal->saved_column;
+ break;
+ default:
+ fprintf(stderr, "Unknown CSI escape: %c\n", *p);
+ break;
+ }
+}
+
+static void
+handle_non_csi_escape(struct terminal *terminal, char code)
+{
+ switch(code) {
+ case 'M': /* RI */
+ terminal->row -= 1;
+ if(terminal->row < terminal->margin_top) {
+ terminal->row = terminal->margin_top;
+ terminal_scroll(terminal, -1);
+ }
+ break;
+ case 'E': /* NEL */
+ terminal->column = 0;
+ // fallthrough
+ case 'D': /* IND */
+ terminal->row += 1;
+ if(terminal->row > terminal->margin_bottom) {
+ terminal->row = terminal->margin_bottom;
+ terminal_scroll(terminal, +1);
+ }
+ break;
+ case 'c': /* RIS */
+ terminal_init(terminal);
+ break;
+ case 'H': /* HTS */
+ terminal->tab_ruler[terminal->column] = 1;
+ break;
+ case '7': /* DECSC */
+ terminal->saved_row = terminal->row;
+ terminal->saved_column = terminal->column;
+ terminal->saved_attr = terminal->curr_attr;
+ terminal->saved_origin_mode = terminal->origin_mode;
+ terminal->saved_cs = terminal->cs;
+ terminal->saved_g0 = terminal->g0;
+ terminal->saved_g1 = terminal->g1;
+ break;
+ case '8': /* DECRC */
+ terminal->row = terminal->saved_row;
+ terminal->column = terminal->saved_column;
+ terminal->curr_attr = terminal->saved_attr;
+ terminal->origin_mode = terminal->saved_origin_mode;
+ terminal->cs = terminal->saved_cs;
+ terminal->g0 = terminal->saved_g0;
+ terminal->g1 = terminal->saved_g1;
+ break;
+ case '=': /* DECPAM */
+ terminal->key_mode = KM_APPLICATION;
+ break;
+ case '>': /* DECPNM */
+ terminal->key_mode = KM_NORMAL;
+ break;
+ default:
+ fprintf(stderr, "Unknown escape code: %c\n", code);
+ break;
+ }
+}
+
+static void
+handle_special_escape(struct terminal *terminal, char special, char code)
+{
+ int i, numChars;
+
+ if (special == '#') {
+ switch(code) {
+ case '8':
+ /* fill with 'E', no cheap way to do this */
+ memset(terminal->data, 0, terminal->data_pitch * terminal->height);
+ numChars = terminal->width * terminal->height;
+ for(i = 0; i < numChars; i++) {
+ terminal->data[i].byte[0] = 'E';
+ }
+ break;
+ default:
+ fprintf(stderr, "Unknown HASH escape #%c\n", code);
+ break;
+ }
+ } else if (special == '(' || special == ')') {
+ switch(code) {
+ case '0':
+ if (special == '(')
+ terminal->g0 = CS_SPECIAL;
+ else
+ terminal->g1 = CS_SPECIAL;
+ break;
+ case 'A':
+ if (special == '(')
+ terminal->g0 = CS_UK;
+ else
+ terminal->g1 = CS_UK;
+ break;
+ case 'B':
+ if (special == '(')
+ terminal->g0 = CS_US;
+ else
+ terminal->g1 = CS_US;
+ break;
+ default:
+ fprintf(stderr, "Unknown character set %c\n", code);
+ break;
+ }
+ } else {
+ fprintf(stderr, "Unknown special escape %c%c\n", special, code);
+ }
+}
+
+static void
+handle_sgr(struct terminal *terminal, int code)
+{
+ switch(code) {
+ case 0:
+ terminal->curr_attr = terminal->color_scheme->default_attr;
+ break;
+ case 1:
+ terminal->curr_attr.a |= ATTRMASK_BOLD;
+ if (terminal->curr_attr.fg < 8)
+ terminal->curr_attr.fg += 8;
+ break;
+ case 4:
+ terminal->curr_attr.a |= ATTRMASK_UNDERLINE;
+ break;
+ case 5:
+ terminal->curr_attr.a |= ATTRMASK_BLINK;
+ break;
+ case 8:
+ terminal->curr_attr.a |= ATTRMASK_CONCEALED;
+ break;
+ case 2:
+ case 21:
+ case 22:
+ terminal->curr_attr.a &= ~ATTRMASK_BOLD;
+ if (terminal->curr_attr.fg < 16 && terminal->curr_attr.fg >= 8)
+ terminal->curr_attr.fg -= 8;
+ break;
+ case 24:
+ terminal->curr_attr.a &= ~ATTRMASK_UNDERLINE;
+ break;
+ case 25:
+ terminal->curr_attr.a &= ~ATTRMASK_BLINK;
+ break;
+ case 7:
+ case 26:
+ terminal->curr_attr.a |= ATTRMASK_INVERSE;
+ break;
+ case 27:
+ terminal->curr_attr.a &= ~ATTRMASK_INVERSE;
+ break;
+ case 28:
+ terminal->curr_attr.a &= ~ATTRMASK_CONCEALED;
+ break;
+ case 39:
+ terminal->curr_attr.fg = terminal->color_scheme->default_attr.fg;
+ break;
+ case 49:
+ terminal->curr_attr.bg = terminal->color_scheme->default_attr.bg;
+ break;
+ default:
+ if(code >= 30 && code <= 37) {
+ terminal->curr_attr.fg = code - 30;
+ if (terminal->curr_attr.a & ATTRMASK_BOLD)
+ terminal->curr_attr.fg += 8;
+ } else if(code >= 40 && code <= 47) {
+ terminal->curr_attr.bg = code - 40;
+ } else if (code >= 90 && code <= 97) {
+ terminal->curr_attr.fg = code - 90 + 8;
+ } else if (code >= 100 && code <= 107) {
+ terminal->curr_attr.bg = code - 100 + 8;
+ } else if(code >= 256 && code < 512) {
+ terminal->curr_attr.fg = code - 256;
+ } else if(code >= 512 && code < 768) {
+ terminal->curr_attr.bg = code - 512;
+ } else {
+ fprintf(stderr, "Unknown SGR code: %d\n", code);
+ }
+ break;
+ }
+}
+
+/* Returns 1 if c was special, otherwise 0 */
+static int
+handle_special_char(struct terminal *terminal, char c)
+{
+ union utf8_char *row;
+ struct attr *attr_row;
+
+ row = terminal_get_row(terminal, terminal->row);
+ attr_row = terminal_get_attr_row(terminal, terminal->row);
+
+ switch(c) {
+ case '\r':
+ terminal->column = 0;
+ break;
+ case '\n':
+ if (terminal->mode & MODE_LF_NEWLINE) {
+ terminal->column = 0;
+ }
+ /* fallthrough */
+ case '\v':
+ case '\f':
+ terminal->row++;
+ if(terminal->row > terminal->margin_bottom) {
+ terminal->row = terminal->margin_bottom;
+ terminal_scroll(terminal, +1);
+ }
+
+ break;
+ case '\t':
+ while (terminal->column < terminal->width) {
+ if (terminal->mode & MODE_IRM)
+ terminal_shift_line(terminal, +1);
+
+ if (row[terminal->column].byte[0] == '\0') {
+ row[terminal->column].byte[0] = ' ';
+ row[terminal->column].byte[1] = '\0';
+ attr_row[terminal->column] = terminal->curr_attr;
+ }
+
+ terminal->column++;
+ if (terminal->tab_ruler[terminal->column]) break;
+ }
+ if (terminal->column >= terminal->width) {
+ terminal->column = terminal->width - 1;
+ }
+
+ break;
+ case '\b':
+ if (terminal->column >= terminal->width) {
+ terminal->column = terminal->width - 2;
+ } else if (terminal->column > 0) {
+ terminal->column--;
+ } else if (terminal->mode & MODE_AUTOWRAP) {
+ terminal->column = terminal->width - 1;
+ terminal->row -= 1;
+ if (terminal->row < terminal->margin_top) {
+ terminal->row = terminal->margin_top;
+ terminal_scroll(terminal, -1);
+ }
+ }
+
+ break;
+ case '\a':
+ /* Bell */
+ break;
+ case '\x0E': /* SO */
+ terminal->cs = terminal->g1;
+ break;
+ case '\x0F': /* SI */
+ terminal->cs = terminal->g0;
+ break;
+ case '\0':
+ break;
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+static void
+handle_char(struct terminal *terminal, union utf8_char utf8)
+{
+ union utf8_char *row;
+ struct attr *attr_row;
+
+ if (handle_special_char(terminal, utf8.byte[0])) return;
+
+ apply_char_set(terminal->cs, &utf8);
+
+ /* There are a whole lot of non-characters, control codes,
+ * and formatting codes that should probably be ignored,
+ * for example: */
+ if (strncmp((char*) utf8.byte, "\xEF\xBB\xBF", 3) == 0) {
+ /* BOM, ignore */
+ return;
+ }
+
+ /* Some of these non-characters should be translated, e.g.: */
+ if (utf8.byte[0] < 32) {
+ utf8.byte[0] = utf8.byte[0] + 64;
+ }
+
+ /* handle right margin effects */
+ if (terminal->column >= terminal->width) {
+ if (terminal->mode & MODE_AUTOWRAP) {
+ terminal->column = 0;
+ terminal->row += 1;
+ if (terminal->row > terminal->margin_bottom) {
+ terminal->row = terminal->margin_bottom;
+ terminal_scroll(terminal, +1);
+ }
+ } else {
+ terminal->column--;
+ }
+ }
+
+ row = terminal_get_row(terminal, terminal->row);
+ attr_row = terminal_get_attr_row(terminal, terminal->row);
+
+ if (terminal->mode & MODE_IRM)
+ terminal_shift_line(terminal, +1);
+ row[terminal->column] = utf8;
+ attr_row[terminal->column++] = terminal->curr_attr;
+
+ /* cursor jump for wide character. */
+ if (is_wide(utf8))
+ row[terminal->column++].ch = 0x200B; /* space glyph */
+
+ if (utf8.ch != terminal->last_char.ch)
+ terminal->last_char = utf8;
+}
+
+static void
+escape_append_utf8(struct terminal *terminal, union utf8_char utf8)
+{
+ int len, i;
+
+ if ((utf8.byte[0] & 0x80) == 0x00) len = 1;
+ else if ((utf8.byte[0] & 0xE0) == 0xC0) len = 2;
+ else if ((utf8.byte[0] & 0xF0) == 0xE0) len = 3;
+ else if ((utf8.byte[0] & 0xF8) == 0xF0) len = 4;
+ else len = 1; /* Invalid, cannot happen */
+
+ if (terminal->escape_length + len <= MAX_ESCAPE) {
+ for (i = 0; i < len; i++)
+ terminal->escape[terminal->escape_length + i] = utf8.byte[i];
+ terminal->escape_length += len;
+ } else if (terminal->escape_length < MAX_ESCAPE) {
+ terminal->escape[terminal->escape_length++] = 0;
+ }
+}
+
+static void
+terminal_data(struct terminal *terminal, const char *data, size_t length)
+{
+ unsigned int i;
+ union utf8_char utf8;
+ enum utf8_state parser_state;
+
+ for (i = 0; i < length; i++) {
+ parser_state =
+ utf8_next_char(&terminal->state_machine, data[i]);
+ switch(parser_state) {
+ case utf8state_accept:
+ utf8.ch = terminal->state_machine.s.ch;
+ break;
+ case utf8state_reject:
+ /* the unicode replacement character */
+ utf8.byte[0] = 0xEF;
+ utf8.byte[1] = 0xBF;
+ utf8.byte[2] = 0xBD;
+ utf8.byte[3] = 0x00;
+ break;
+ default:
+ continue;
+ }
+
+ /* assume escape codes never use non-ASCII characters */
+ switch (terminal->state) {
+ case escape_state_escape:
+ escape_append_utf8(terminal, utf8);
+ switch (utf8.byte[0]) {
+ case 'P': /* DCS */
+ terminal->state = escape_state_dcs;
+ break;
+ case '[': /* CSI */
+ terminal->state = escape_state_csi;
+ break;
+ case ']': /* OSC */
+ terminal->state = escape_state_osc;
+ break;
+ case '#':
+ case '(':
+ case ')': /* special */
+ terminal->state = escape_state_special;
+ break;
+ case '^': /* PM (not implemented) */
+ case '_': /* APC (not implemented) */
+ terminal->state = escape_state_ignore;
+ break;
+ default:
+ terminal->state = escape_state_normal;
+ handle_non_csi_escape(terminal, utf8.byte[0]);
+ break;
+ }
+ continue;
+ case escape_state_csi:
+ if (handle_special_char(terminal, utf8.byte[0]) != 0) {
+ /* do nothing */
+ } else if (utf8.byte[0] == '?') {
+ terminal->escape_flags |= ESC_FLAG_WHAT;
+ } else if (utf8.byte[0] == '>') {
+ terminal->escape_flags |= ESC_FLAG_GT;
+ } else if (utf8.byte[0] == '!') {
+ terminal->escape_flags |= ESC_FLAG_BANG;
+ } else if (utf8.byte[0] == '$') {
+ terminal->escape_flags |= ESC_FLAG_CASH;
+ } else if (utf8.byte[0] == '\'') {
+ terminal->escape_flags |= ESC_FLAG_SQUOTE;
+ } else if (utf8.byte[0] == '"') {
+ terminal->escape_flags |= ESC_FLAG_DQUOTE;
+ } else if (utf8.byte[0] == ' ') {
+ terminal->escape_flags |= ESC_FLAG_SPACE;
+ } else {
+ escape_append_utf8(terminal, utf8);
+ if (terminal->escape_length >= MAX_ESCAPE)
+ terminal->state = escape_state_normal;
+ }
+
+ if (isalpha(utf8.byte[0]) || utf8.byte[0] == '@' ||
+ utf8.byte[0] == '`')
+ {
+ terminal->state = escape_state_normal;
+ handle_escape(terminal);
+ } else {
+ }
+ continue;
+ case escape_state_inner_escape:
+ if (utf8.byte[0] == '\\') {
+ terminal->state = escape_state_normal;
+ if (terminal->outer_state == escape_state_dcs) {
+ handle_dcs(terminal);
+ } else if (terminal->outer_state == escape_state_osc) {
+ handle_osc(terminal);
+ }
+ } else if (utf8.byte[0] == '\e') {
+ terminal->state = terminal->outer_state;
+ escape_append_utf8(terminal, utf8);
+ if (terminal->escape_length >= MAX_ESCAPE)
+ terminal->state = escape_state_normal;
+ } else {
+ terminal->state = terminal->outer_state;
+ if (terminal->escape_length < MAX_ESCAPE)
+ terminal->escape[terminal->escape_length++] = '\e';
+ escape_append_utf8(terminal, utf8);
+ if (terminal->escape_length >= MAX_ESCAPE)
+ terminal->state = escape_state_normal;
+ }
+ continue;
+ case escape_state_dcs:
+ case escape_state_osc:
+ case escape_state_ignore:
+ if (utf8.byte[0] == '\e') {
+ terminal->outer_state = terminal->state;
+ terminal->state = escape_state_inner_escape;
+ } else if (utf8.byte[0] == '\a' && terminal->state == escape_state_osc) {
+ terminal->state = escape_state_normal;
+ handle_osc(terminal);
+ } else {
+ escape_append_utf8(terminal, utf8);
+ if (terminal->escape_length >= MAX_ESCAPE)
+ terminal->state = escape_state_normal;
+ }
+ continue;
+ case escape_state_special:
+ escape_append_utf8(terminal, utf8);
+ terminal->state = escape_state_normal;
+ if (isdigit(utf8.byte[0]) || isalpha(utf8.byte[0])) {
+ handle_special_escape(terminal, terminal->escape[1],
+ utf8.byte[0]);
+ }
+ continue;
+ default:
+ break;
+ }
+
+ /* this is valid, because ASCII characters are never used to
+ * introduce a multibyte sequence in UTF-8 */
+ if (utf8.byte[0] == '\e') {
+ terminal->state = escape_state_escape;
+ terminal->outer_state = escape_state_normal;
+ terminal->escape[0] = '\e';
+ terminal->escape_length = 1;
+ terminal->escape_flags = 0;
+ } else {
+ handle_char(terminal, utf8);
+ } /* if */
+ } /* for */
+
+ window_schedule_redraw(terminal->window);
+}
+
+static void
+data_source_target(void *data,
+ struct wl_data_source *source, const char *mime_type)
+{
+ fprintf(stderr, "data_source_target, %s\n", mime_type);
+}
+
+static void
+data_source_send(void *data,
+ struct wl_data_source *source,
+ const char *mime_type, int32_t fd)
+{
+ struct terminal *terminal = data;
+
+ terminal_send_selection(terminal, fd);
+}
+
+static void
+data_source_cancelled(void *data, struct wl_data_source *source)
+{
+ wl_data_source_destroy(source);
+}
+
+static const struct wl_data_source_listener data_source_listener = {
+ data_source_target,
+ data_source_send,
+ data_source_cancelled
+};
+
+static const char text_mime_type[] = "text/plain;charset=utf-8";
+
+static void
+data_handler(struct window *window,
+ struct input *input,
+ float x, float y, const char **types, void *data)
+{
+ int i, has_text = 0;
+
+ if (!types)
+ return;
+ for (i = 0; types[i]; i++)
+ if (strcmp(types[i], text_mime_type) == 0)
+ has_text = 1;
+
+ if (!has_text) {
+ input_accept(input, NULL);
+ } else {
+ input_accept(input, text_mime_type);
+ }
+}
+
+static void
+drop_handler(struct window *window, struct input *input,
+ int32_t x, int32_t y, void *data)
+{
+ struct terminal *terminal = data;
+
+ input_receive_drag_data_to_fd(input, text_mime_type, terminal->master);
+}
+
+static void
+fullscreen_handler(struct window *window, void *data)
+{
+ struct terminal *terminal = data;
+
+ window_set_fullscreen(window, !window_is_fullscreen(terminal->window));
+}
+
+static void
+close_handler(struct window *window, void *data)
+{
+ struct terminal *terminal = data;
+
+ terminal_destroy(terminal);
+}
+
+static int
+handle_bound_key(struct terminal *terminal,
+ struct input *input, uint32_t sym, uint32_t time)
+{
+ struct terminal *new_terminal;
+
+ switch (sym) {
+ case XKB_KEY_X:
+ /* Cut selection; terminal doesn't do cut, fall
+ * through to copy. */
+ case XKB_KEY_C:
+ terminal->selection =
+ display_create_data_source(terminal->display);
+ wl_data_source_offer(terminal->selection,
+ "text/plain;charset=utf-8");
+ wl_data_source_add_listener(terminal->selection,
+ &data_source_listener, terminal);
+ input_set_selection(input, terminal->selection,
+ display_get_serial(terminal->display));
+ return 1;
+ case XKB_KEY_V:
+ input_receive_selection_data_to_fd(input,
+ "text/plain;charset=utf-8",
+ terminal->master);
+
+ return 1;
+
+ case XKB_KEY_N:
+ new_terminal = terminal_create(terminal->display);
+ if (terminal_run(new_terminal, option_shell))
+ terminal_destroy(new_terminal);
+
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+static void
+key_handler(struct window *window, struct input *input, uint32_t time,
+ uint32_t key, uint32_t sym, enum wl_keyboard_key_state state,
+ void *data)
+{
+ struct terminal *terminal = data;
+ char ch[MAX_RESPONSE];
+ uint32_t modifiers, serial;
+ int ret, len = 0;
+ bool convert_utf8 = true;
+
+ modifiers = input_get_modifiers(input);
+ if ((modifiers & MOD_CONTROL_MASK) &&
+ (modifiers & MOD_SHIFT_MASK) &&
+ state == WL_KEYBOARD_KEY_STATE_PRESSED &&
+ handle_bound_key(terminal, input, sym, time))
+ return;
+
+ /* Map keypad symbols to 'normal' equivalents before processing */
+ switch (sym) {
+ case XKB_KEY_KP_Space:
+ sym = XKB_KEY_space;
+ break;
+ case XKB_KEY_KP_Tab:
+ sym = XKB_KEY_Tab;
+ break;
+ case XKB_KEY_KP_Enter:
+ sym = XKB_KEY_Return;
+ break;
+ case XKB_KEY_KP_Left:
+ sym = XKB_KEY_Left;
+ break;
+ case XKB_KEY_KP_Up:
+ sym = XKB_KEY_Up;
+ break;
+ case XKB_KEY_KP_Right:
+ sym = XKB_KEY_Right;
+ break;
+ case XKB_KEY_KP_Down:
+ sym = XKB_KEY_Down;
+ break;
+ case XKB_KEY_KP_Equal:
+ sym = XKB_KEY_equal;
+ break;
+ case XKB_KEY_KP_Multiply:
+ sym = XKB_KEY_asterisk;
+ break;
+ case XKB_KEY_KP_Add:
+ sym = XKB_KEY_plus;
+ break;
+ case XKB_KEY_KP_Separator:
+ /* Note this is actually locale-dependent and should mostly be
+ * a comma. But leave it as period until we one day start
+ * doing the right thing. */
+ sym = XKB_KEY_period;
+ break;
+ case XKB_KEY_KP_Subtract:
+ sym = XKB_KEY_minus;
+ break;
+ case XKB_KEY_KP_Decimal:
+ sym = XKB_KEY_period;
+ break;
+ case XKB_KEY_KP_Divide:
+ sym = XKB_KEY_slash;
+ break;
+ case XKB_KEY_KP_0:
+ case XKB_KEY_KP_1:
+ case XKB_KEY_KP_2:
+ case XKB_KEY_KP_3:
+ case XKB_KEY_KP_4:
+ case XKB_KEY_KP_5:
+ case XKB_KEY_KP_6:
+ case XKB_KEY_KP_7:
+ case XKB_KEY_KP_8:
+ case XKB_KEY_KP_9:
+ sym = (sym - XKB_KEY_KP_0) + XKB_KEY_0;
+ break;
+ default:
+ break;
+ }
+
+ switch (sym) {
+ case XKB_KEY_BackSpace:
+ if (modifiers & MOD_ALT_MASK)
+ ch[len++] = 0x1b;
+ ch[len++] = 0x7f;
+ break;
+ case XKB_KEY_Tab:
+ case XKB_KEY_Linefeed:
+ case XKB_KEY_Clear:
+ case XKB_KEY_Pause:
+ case XKB_KEY_Scroll_Lock:
+ case XKB_KEY_Sys_Req:
+ case XKB_KEY_Escape:
+ ch[len++] = sym & 0x7f;
+ break;
+
+ case XKB_KEY_Return:
+ if (terminal->mode & MODE_LF_NEWLINE) {
+ ch[len++] = 0x0D;
+ ch[len++] = 0x0A;
+ } else {
+ ch[len++] = 0x0D;
+ }
+ break;
+
+ case XKB_KEY_Shift_L:
+ case XKB_KEY_Shift_R:
+ case XKB_KEY_Control_L:
+ case XKB_KEY_Control_R:
+ case XKB_KEY_Alt_L:
+ case XKB_KEY_Alt_R:
+ case XKB_KEY_Meta_L:
+ case XKB_KEY_Meta_R:
+ case XKB_KEY_Super_L:
+ case XKB_KEY_Super_R:
+ case XKB_KEY_Hyper_L:
+ case XKB_KEY_Hyper_R:
+ break;
+
+ case XKB_KEY_Insert:
+ len = function_key_response('[', 2, modifiers, '~', ch);
+ break;
+ case XKB_KEY_Delete:
+ if (terminal->mode & MODE_DELETE_SENDS_DEL) {
+ ch[len++] = '\x04';
+ } else {
+ len = function_key_response('[', 3, modifiers, '~', ch);
+ }
+ break;
+ case XKB_KEY_Page_Up:
+ len = function_key_response('[', 5, modifiers, '~', ch);
+ break;
+ case XKB_KEY_Page_Down:
+ len = function_key_response('[', 6, modifiers, '~', ch);
+ break;
+ case XKB_KEY_F1:
+ len = function_key_response('O', 1, modifiers, 'P', ch);
+ break;
+ case XKB_KEY_F2:
+ len = function_key_response('O', 1, modifiers, 'Q', ch);
+ break;
+ case XKB_KEY_F3:
+ len = function_key_response('O', 1, modifiers, 'R', ch);
+ break;
+ case XKB_KEY_F4:
+ len = function_key_response('O', 1, modifiers, 'S', ch);
+ break;
+ case XKB_KEY_F5:
+ len = function_key_response('[', 15, modifiers, '~', ch);
+ break;
+ case XKB_KEY_F6:
+ len = function_key_response('[', 17, modifiers, '~', ch);
+ break;
+ case XKB_KEY_F7:
+ len = function_key_response('[', 18, modifiers, '~', ch);
+ break;
+ case XKB_KEY_F8:
+ len = function_key_response('[', 19, modifiers, '~', ch);
+ break;
+ case XKB_KEY_F9:
+ len = function_key_response('[', 20, modifiers, '~', ch);
+ break;
+ case XKB_KEY_F10:
+ len = function_key_response('[', 21, modifiers, '~', ch);
+ break;
+ case XKB_KEY_F12:
+ len = function_key_response('[', 24, modifiers, '~', ch);
+ break;
+ default:
+ /* Handle special keys with alternate mappings */
+ len = apply_key_map(terminal->key_mode, sym, modifiers, ch);
+ if (len != 0) break;
+
+ if (modifiers & MOD_CONTROL_MASK) {
+ if (sym >= '3' && sym <= '7')
+ sym = (sym & 0x1f) + 8;
+
+ if (!((sym >= '!' && sym <= '/') ||
+ (sym >= '8' && sym <= '?') ||
+ (sym >= '0' && sym <= '2'))) sym = sym & 0x1f;
+ else if (sym == '2') sym = 0x00;
+ else if (sym == '/') sym = 0x1F;
+ else if (sym == '8' || sym == '?') sym = 0x7F;
+ }
+ if (modifiers & MOD_ALT_MASK) {
+ if (terminal->mode & MODE_ALT_SENDS_ESC) {
+ ch[len++] = 0x1b;
+ } else {
+ sym = sym | 0x80;
+ convert_utf8 = false;
+ }
+ }
+
+ if ((sym < 128) ||
+ (!convert_utf8 && sym < 256)) {
+ ch[len++] = sym;
+ } else {
+ ret = xkb_keysym_to_utf8(sym, ch + len,
+ MAX_RESPONSE - len);
+ if (ret < 0)
+ fprintf(stderr,
+ "Warning: buffer too small to encode "
+ "UTF8 character\n");
+ else
+ len += ret;
+ }
+
+ break;
+ }
+
+ if (state == WL_KEYBOARD_KEY_STATE_PRESSED && len > 0) {
+ terminal_write(terminal, ch, len);
+
+ /* Hide cursor, except if this was coming from a
+ * repeating key press. */
+ serial = display_get_serial(terminal->display);
+ if (terminal->hide_cursor_serial != serial) {
+ input_set_pointer_image(input, CURSOR_BLANK);
+ terminal->hide_cursor_serial = serial;
+ }
+ }
+}
+
+static void
+keyboard_focus_handler(struct window *window,
+ struct input *device, void *data)
+{
+ struct terminal *terminal = data;
+
+ window_schedule_redraw(terminal->window);
+}
+
+static int wordsep(int ch)
+{
+ const char extra[] = "-,./?%&#:_=+@~";
+
+ if (ch > 127)
+ return 1;
+
+ return ch == 0 || !(isalpha(ch) || isdigit(ch) || strchr(extra, ch));
+}
+
+static int
+recompute_selection(struct terminal *terminal)
+{
+ struct rectangle allocation;
+ int col, x, width, height;
+ int start_row, end_row;
+ int word_start, eol;
+ int side_margin, top_margin;
+ int start_x, end_x;
+ int cw, ch;
+ union utf8_char *data;
+
+ cw = terminal->average_width;
+ ch = terminal->extents.height;
+ widget_get_allocation(terminal->widget, &allocation);
+ width = terminal->width * cw;
+ height = terminal->height * ch;
+ side_margin = allocation.x + (allocation.width - width) / 2;
+ top_margin = allocation.y + (allocation.height - height) / 2;
+
+ start_row = (terminal->selection_start_y - top_margin + ch) / ch - 1;
+ end_row = (terminal->selection_end_y - top_margin + ch) / ch - 1;
+
+ if (start_row < end_row ||
+ (start_row == end_row &&
+ terminal->selection_start_x < terminal->selection_end_x)) {
+ terminal->selection_start_row = start_row;
+ terminal->selection_end_row = end_row;
+ start_x = terminal->selection_start_x;
+ end_x = terminal->selection_end_x;
+ } else {
+ terminal->selection_start_row = end_row;
+ terminal->selection_end_row = start_row;
+ start_x = terminal->selection_end_x;
+ end_x = terminal->selection_start_x;
+ }
+
+ eol = 0;
+ if (terminal->selection_start_row < 0) {
+ terminal->selection_start_row = 0;
+ terminal->selection_start_col = 0;
+ } else {
+ x = side_margin + cw / 2;
+ data = terminal_get_row(terminal,
+ terminal->selection_start_row);
+ word_start = 0;
+ for (col = 0; col < terminal->width; col++, x += cw) {
+ if (col == 0 || wordsep(data[col - 1].ch))
+ word_start = col;
+ if (data[col].ch != 0)
+ eol = col + 1;
+ if (start_x < x)
+ break;
+ }
+
+ switch (terminal->dragging) {
+ case SELECT_LINE:
+ terminal->selection_start_col = 0;
+ break;
+ case SELECT_WORD:
+ terminal->selection_start_col = word_start;
+ break;
+ case SELECT_CHAR:
+ terminal->selection_start_col = col;
+ break;
+ }
+ }
+
+ if (terminal->selection_end_row >= terminal->height) {
+ terminal->selection_end_row = terminal->height;
+ terminal->selection_end_col = 0;
+ } else {
+ x = side_margin + cw / 2;
+ data = terminal_get_row(terminal, terminal->selection_end_row);
+ for (col = 0; col < terminal->width; col++, x += cw) {
+ if (terminal->dragging == SELECT_CHAR && end_x < x)
+ break;
+ if (terminal->dragging == SELECT_WORD &&
+ end_x < x && wordsep(data[col].ch))
+ break;
+ }
+ terminal->selection_end_col = col;
+ }
+
+ if (terminal->selection_end_col != terminal->selection_start_col ||
+ terminal->selection_start_row != terminal->selection_end_row) {
+ col = terminal->selection_end_col;
+ if (col > 0 && data[col - 1].ch == 0)
+ terminal->selection_end_col = terminal->width;
+ data = terminal_get_row(terminal, terminal->selection_start_row);
+ if (data[terminal->selection_start_col].ch == 0)
+ terminal->selection_start_col = eol;
+ }
+
+ return 1;
+}
+
+static void
+button_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ uint32_t button,
+ enum wl_pointer_button_state state, void *data)
+{
+ struct terminal *terminal = data;
+
+ switch (button) {
+ case 272:
+ if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
+
+ if (time - terminal->button_time < 500)
+ terminal->click_count++;
+ else
+ terminal->click_count = 1;
+
+ terminal->button_time = time;
+ terminal->dragging =
+ (terminal->click_count - 1) % 3 + SELECT_CHAR;
+
+ input_get_position(input,
+ &terminal->selection_start_x,
+ &terminal->selection_start_y);
+ terminal->selection_end_x = terminal->selection_start_x;
+ terminal->selection_end_y = terminal->selection_start_y;
+ if (recompute_selection(terminal))
+ widget_schedule_redraw(widget);
+ } else {
+ terminal->dragging = SELECT_NONE;
+ }
+ break;
+ }
+}
+
+static int
+enter_handler(struct widget *widget,
+ struct input *input, float x, float y, void *data)
+{
+ return CURSOR_IBEAM;
+}
+
+static int
+motion_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ float x, float y, void *data)
+{
+ struct terminal *terminal = data;
+
+ if (terminal->dragging) {
+ input_get_position(input,
+ &terminal->selection_end_x,
+ &terminal->selection_end_y);
+
+ if (recompute_selection(terminal))
+ widget_schedule_redraw(widget);
+ }
+
+ return CURSOR_IBEAM;
+}
+
+static void
+output_handler(struct window *window, struct output *output, int enter,
+ void *data)
+{
+ if (enter)
+ window_set_buffer_transform(window, output_get_transform(output));
+ window_set_buffer_scale(window, window_get_output_scale(window));
+ window_schedule_redraw(window);
+}
+
+#ifndef howmany
+#define howmany(x, y) (((x) + ((y) - 1)) / (y))
+#endif
+
+static struct terminal *
+terminal_create(struct display *display)
+{
+ struct terminal *terminal;
+ cairo_surface_t *surface;
+ cairo_t *cr;
+ cairo_text_extents_t text_extents;
+
+ terminal = xzalloc(sizeof *terminal);
+ terminal->color_scheme = &DEFAULT_COLORS;
+ terminal_init(terminal);
+ terminal->margin_top = 0;
+ terminal->margin_bottom = -1;
+ terminal->window = window_create(display);
+ terminal->widget = frame_create(terminal->window, terminal);
+ window_set_title(terminal->window, "Wayland Terminal");
+ widget_set_transparent(terminal->widget, 0);
+
+ init_state_machine(&terminal->state_machine);
+ init_color_table(terminal);
+
+ terminal->display = display;
+ terminal->margin = 5;
+
+ window_set_user_data(terminal->window, terminal);
+ window_set_key_handler(terminal->window, key_handler);
+ window_set_keyboard_focus_handler(terminal->window,
+ keyboard_focus_handler);
+ window_set_fullscreen_handler(terminal->window, fullscreen_handler);
+ window_set_output_handler(terminal->window, output_handler);
+ window_set_close_handler(terminal->window, close_handler);
+
+ window_set_data_handler(terminal->window, data_handler);
+ window_set_drop_handler(terminal->window, drop_handler);
+
+ widget_set_redraw_handler(terminal->widget, redraw_handler);
+ widget_set_resize_handler(terminal->widget, resize_handler);
+ widget_set_button_handler(terminal->widget, button_handler);
+ widget_set_enter_handler(terminal->widget, enter_handler);
+ widget_set_motion_handler(terminal->widget, motion_handler);
+
+ surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 0, 0);
+ cr = cairo_create(surface);
+ cairo_set_font_size(cr, option_font_size);
+ cairo_select_font_face (cr, option_font,
+ CAIRO_FONT_SLANT_NORMAL,
+ CAIRO_FONT_WEIGHT_BOLD);
+ terminal->font_bold = cairo_get_scaled_font (cr);
+ cairo_scaled_font_reference(terminal->font_bold);
+
+ cairo_select_font_face (cr, option_font,
+ CAIRO_FONT_SLANT_NORMAL,
+ CAIRO_FONT_WEIGHT_NORMAL);
+ terminal->font_normal = cairo_get_scaled_font (cr);
+ cairo_scaled_font_reference(terminal->font_normal);
+
+ cairo_font_extents(cr, &terminal->extents);
+
+ /* Compute the average ascii glyph width */
+ cairo_text_extents(cr, TERMINAL_DRAW_SINGLE_WIDE_CHARACTERS,
+ &text_extents);
+ terminal->average_width = howmany
+ (text_extents.width,
+ strlen(TERMINAL_DRAW_SINGLE_WIDE_CHARACTERS));
+ terminal->average_width = ceil(terminal->average_width);
+
+ cairo_destroy(cr);
+ cairo_surface_destroy(surface);
+
+ terminal_resize(terminal, 20, 5); /* Set minimum size first */
+ terminal_resize(terminal, 80, 25);
+
+ wl_list_insert(terminal_list.prev, &terminal->link);
+
+ return terminal;
+}
+
+static void
+terminal_destroy(struct terminal *terminal)
+{
+ display_unwatch_fd(terminal->display, terminal->master);
+ window_destroy(terminal->window);
+ close(terminal->master);
+ wl_list_remove(&terminal->link);
+
+ if (wl_list_empty(&terminal_list))
+ display_exit(terminal->display);
+
+ free(terminal);
+}
+
+static void
+io_handler(struct task *task, uint32_t events)
+{
+ struct terminal *terminal =
+ container_of(task, struct terminal, io_task);
+ char buffer[256];
+ int len;
+
+ if (events & EPOLLHUP) {
+ terminal_destroy(terminal);
+ return;
+ }
+
+ len = read(terminal->master, buffer, sizeof buffer);
+ if (len < 0)
+ terminal_destroy(terminal);
+ else
+ terminal_data(terminal, buffer, len);
+}
+
+static int
+terminal_run(struct terminal *terminal, const char *path)
+{
+ int master;
+ pid_t pid;
+
+ pid = forkpty(&master, NULL, NULL, NULL);
+ if (pid == 0) {
+ setenv("TERM", option_term, 1);
+ setenv("COLORTERM", option_term, 1);
+ if (execl(path, path, NULL)) {
+ printf("exec failed: %m\n");
+ exit(EXIT_FAILURE);
+ }
+ } else if (pid < 0) {
+ fprintf(stderr, "failed to fork and create pty (%m).\n");
+ return -1;
+ }
+
+ terminal->master = master;
+ fcntl(master, F_SETFL, O_NONBLOCK);
+ terminal->io_task.run = io_handler;
+ display_watch_fd(terminal->display, terminal->master,
+ EPOLLIN | EPOLLHUP, &terminal->io_task);
+
+ window_set_fullscreen(terminal->window, option_fullscreen);
+ if (!window_is_fullscreen(terminal->window))
+ terminal_resize(terminal, 80, 24);
+
+ return 0;
+}
+
+static const struct weston_option terminal_options[] = {
+ { WESTON_OPTION_BOOLEAN, "fullscreen", 'f', &option_fullscreen },
+ { WESTON_OPTION_STRING, "font", 0, &option_font },
+ { WESTON_OPTION_STRING, "shell", 0, &option_shell },
+};
+
+int main(int argc, char *argv[])
+{
+ struct display *d;
+ struct terminal *terminal;
+ struct weston_config *config;
+ struct weston_config_section *s;
+
+ /* as wcwidth is locale-dependent,
+ wcwidth needs setlocale call to function properly. */
+ setlocale(LC_ALL, "");
+
+ option_shell = getenv("SHELL");
+ if (!option_shell)
+ option_shell = "/bin/bash";
+
+ config = weston_config_parse("weston.ini");
+ s = weston_config_get_section(config, "terminal", NULL, NULL);
+ weston_config_section_get_string(s, "font", &option_font, "mono");
+ weston_config_section_get_int(s, "font-size", &option_font_size, 14);
+ weston_config_section_get_string(s, "term", &option_term, "xterm");
+ weston_config_destroy(config);
+
+ d = display_create(&argc, argv);
+ if (d == NULL) {
+ fprintf(stderr, "failed to create display: %m\n");
+ return -1;
+ }
+
+ wl_list_init(&terminal_list);
+ terminal = terminal_create(d);
+ if (terminal_run(terminal, option_shell))
+ exit(EXIT_FAILURE);
+
+ display_run(d);
+
+ return 0;
+}
diff --git a/clients/transformed.c b/clients/transformed.c
new file mode 100644
index 00000000..54212dd4
--- /dev/null
+++ b/clients/transformed.c
@@ -0,0 +1,313 @@
+/*
+ * Copyright © 2008 Kristian Høgsberg
+ * Copyright © 2012 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
+ * 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.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <cairo.h>
+
+#include <linux/input.h>
+#include <wayland-client.h>
+#include "window.h"
+
+struct transformed {
+ struct display *display;
+ struct window *window;
+ struct widget *widget;
+ int width, height;
+ int fullscreen;
+ enum wl_shell_surface_fullscreen_method fullscreen_method;
+};
+
+static void
+draw_stuff(cairo_t *cr, int width, int height)
+{
+ cairo_matrix_t m;
+ cairo_get_matrix (cr, &m);
+
+ cairo_translate(cr, width / 2, height / 2);
+ cairo_scale(cr, width / 2, height / 2);
+
+ cairo_set_source_rgba(cr, 0, 0, 0.3, 1.0);
+ cairo_set_source_rgba(cr, 0, 0, 0, 1.0);
+ cairo_rectangle(cr, -1, -1, 2, 2);
+ cairo_fill(cr);
+
+ cairo_set_source_rgb(cr, 1, 0, 0);
+ cairo_move_to(cr, 0, 0);
+ cairo_line_to(cr, 0, -1);
+
+ cairo_save(cr);
+ cairo_set_matrix(cr, &m);
+ cairo_set_line_width(cr, 2.0);
+ cairo_stroke(cr);
+ cairo_restore(cr);
+
+ cairo_set_source_rgb(cr, 0, 1, 0);
+ cairo_move_to(cr, 0, 0);
+ cairo_line_to(cr, 1, 0);
+
+ cairo_save(cr);
+ cairo_set_matrix(cr, &m);
+ cairo_set_line_width(cr, 2.0);
+ cairo_stroke(cr);
+ cairo_restore(cr);
+
+ cairo_set_source_rgb(cr, 1, 1, 1);
+ cairo_move_to(cr, 0, 0);
+ cairo_line_to(cr, 0, 1);
+ cairo_move_to(cr, 0, 0);
+ cairo_line_to(cr, -1, 0);
+
+ cairo_save(cr);
+ cairo_set_matrix(cr, &m);
+ cairo_set_line_width(cr, 2.0);
+ cairo_stroke(cr);
+ cairo_restore(cr);
+
+ cairo_destroy(cr);
+}
+
+static void
+fullscreen_handler(struct window *window, void *data)
+{
+ struct transformed *transformed = data;
+
+ transformed->fullscreen ^= 1;
+ window_set_fullscreen(window, transformed->fullscreen);
+}
+
+static void
+resize_handler(struct widget *widget, int width, int height, void *data)
+{
+ struct transformed *transformed = data;
+
+ if (transformed->fullscreen_method !=
+ WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT)
+ widget_set_size(widget, transformed->width, transformed->height);
+}
+
+static void
+redraw_handler(struct widget *widget, void *data)
+{
+ struct transformed *transformed = data;
+ struct rectangle allocation;
+ cairo_surface_t *surface;
+ cairo_t *cr;
+
+ surface = window_get_surface(transformed->window);
+ if (surface == NULL ||
+ cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
+ fprintf(stderr, "failed to create cairo egl surface\n");
+ return;
+ }
+
+ widget_get_allocation(transformed->widget, &allocation);
+
+ cr = widget_cairo_create(widget);
+ draw_stuff(cr, allocation.width, allocation.height);
+
+ cairo_surface_destroy(surface);
+}
+
+static void
+output_handler(struct window *window, struct output *output, int enter,
+ void *data)
+{
+ if (!enter)
+ return;
+
+ window_set_buffer_transform(window, output_get_transform(output));
+ window_set_buffer_scale(window, output_get_scale(output));
+ window_schedule_redraw(window);
+}
+
+static void
+key_handler(struct window *window, struct input *input, uint32_t time,
+ uint32_t key, uint32_t sym, enum wl_keyboard_key_state state,
+ void *data)
+{
+ int transform, scale;
+
+ if (state == WL_KEYBOARD_KEY_STATE_RELEASED)
+ return;
+
+ transform = window_get_buffer_transform (window);
+ scale = window_get_buffer_scale (window);
+ switch (sym) {
+ case XKB_KEY_Left:
+ if (transform == 0)
+ transform = 3;
+ else if (transform == 4)
+ transform = 7;
+ else
+ transform--;
+ break;
+
+ case XKB_KEY_Right:
+ if (transform == 3)
+ transform = 0;
+ else if (transform == 7)
+ transform = 4;
+ else
+ transform++;
+ break;
+
+ case XKB_KEY_space:
+ if (transform >= 4)
+ transform -= 4;
+ else
+ transform += 4;
+ break;
+
+ case XKB_KEY_z:
+ if (scale == 1)
+ scale = 2;
+ else
+ scale = 1;
+ break;
+ }
+
+ printf ("setting buffer transform to %d\n", transform);
+ printf ("setting buffer scale to %d\n", scale);
+ window_set_buffer_transform(window, transform);
+ window_set_buffer_scale(window, scale);
+ window_schedule_redraw(window);
+}
+
+static void
+button_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ uint32_t button, enum wl_pointer_button_state state, void *data)
+{
+ struct transformed *transformed = data;
+
+ switch (button) {
+ case BTN_LEFT:
+ if (state == WL_POINTER_BUTTON_STATE_PRESSED)
+ window_move(transformed->window, input,
+ display_get_serial(transformed->display));
+ break;
+ case BTN_MIDDLE:
+ if (state == WL_POINTER_BUTTON_STATE_PRESSED)
+ widget_schedule_redraw(widget);
+ break;
+ case BTN_RIGHT:
+ if (state == WL_POINTER_BUTTON_STATE_PRESSED)
+ window_show_frame_menu(transformed->window, input, time);
+ break;
+ }
+}
+
+static void
+touch_handler(struct widget *widget, struct input *input,
+ uint32_t serial, uint32_t time, int32_t id,
+ float x, float y, void *data)
+{
+ struct transformed *transformed = data;
+ window_touch_move(transformed->window, input,
+ display_get_serial(transformed->display));
+}
+
+static void
+usage(int error_code)
+{
+ fprintf(stderr, "Usage: transformed [OPTIONS]\n\n"
+ " -d\t\tUse \"driver\" fullscreen method\n"
+ " -w <width>\tSet window width to <width>\n"
+ " -h <height>\tSet window height to <height>\n"
+ " --help\tShow this help text\n\n");
+
+ exit(error_code);
+}
+
+int main(int argc, char *argv[])
+{
+ struct transformed transformed;
+ struct display *d;
+ int i;
+
+ transformed.width = 500;
+ transformed.height = 250;
+ transformed.fullscreen = 0;
+ transformed.fullscreen_method =
+ WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT;
+
+ for (i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-d") == 0) {
+ transformed.fullscreen_method =
+ WL_SHELL_SURFACE_FULLSCREEN_METHOD_DRIVER;
+ } else if (strcmp(argv[i], "-w") == 0) {
+ if (++i >= argc)
+ usage(EXIT_FAILURE);
+
+ transformed.width = atol(argv[i]);
+ } else if (strcmp(argv[i], "-h") == 0) {
+ if (++i >= argc)
+ usage(EXIT_FAILURE);
+
+ transformed.height = atol(argv[i]);
+ } else if (strcmp(argv[i], "--help") == 0)
+ usage(EXIT_SUCCESS);
+ else
+ usage(EXIT_FAILURE);
+ }
+
+ d = display_create(&argc, argv);
+ if (d == NULL) {
+ fprintf(stderr, "failed to create display: %m\n");
+ return -1;
+ }
+
+ transformed.display = d;
+ transformed.window = window_create(d);
+ transformed.widget =
+ window_add_widget(transformed.window, &transformed);
+
+ window_set_title(transformed.window, "Transformed");
+ window_set_fullscreen_method(transformed.window,
+ transformed.fullscreen_method);
+
+ widget_set_transparent(transformed.widget, 0);
+ widget_set_default_cursor(transformed.widget, CURSOR_BLANK);
+
+ widget_set_resize_handler(transformed.widget, resize_handler);
+ widget_set_redraw_handler(transformed.widget, redraw_handler);
+ widget_set_button_handler(transformed.widget, button_handler);
+
+ widget_set_touch_down_handler(transformed.widget, touch_handler);
+
+ window_set_key_handler(transformed.window, key_handler);
+ window_set_fullscreen_handler(transformed.window, fullscreen_handler);
+ window_set_output_handler(transformed.window, output_handler);
+
+ window_set_user_data(transformed.window, &transformed);
+ window_schedule_resize(transformed.window,
+ transformed.width, transformed.height);
+
+ display_run(d);
+
+ return 0;
+}
diff --git a/clients/view.c b/clients/view.c
new file mode 100644
index 00000000..f5b1843f
--- /dev/null
+++ b/clients/view.c
@@ -0,0 +1,314 @@
+/*
+ * Copyright © 2008 Kristian Høgsberg
+ * Copyright © 2009 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * 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.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <math.h>
+#include <time.h>
+#include <cairo.h>
+#include <glib.h>
+#include <gio/gio.h>
+#include <linux/input.h>
+
+#include <glib/poppler-document.h>
+#include <glib/poppler-page.h>
+
+#include <wayland-client.h>
+
+#include "window.h"
+
+struct view {
+ struct window *window;
+ struct widget *widget;
+ struct display *display;
+
+ PopplerDocument *document;
+ int page;
+ int fullscreen;
+ int *view_counter;
+};
+
+static void
+redraw_handler(struct widget *widget, void *data)
+{
+ struct view *view = data;
+
+ struct rectangle allocation;
+ cairo_surface_t *surface;
+ cairo_t *cr;
+ PopplerPage *page;
+ double width, height, doc_aspect, window_aspect, scale;
+
+ widget_get_allocation(view->widget, &allocation);
+
+ surface = window_get_surface(view->window);
+
+ cr = cairo_create(surface);
+ cairo_rectangle(cr, allocation.x, allocation.y,
+ allocation.width, allocation.height);
+ cairo_clip(cr);
+
+ cairo_set_source_rgba(cr, 0, 0, 0, 0.8);
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ cairo_paint(cr);
+
+ if(!view->document) {
+ cairo_destroy(cr);
+ cairo_surface_destroy(surface);
+ return;
+ }
+
+ page = poppler_document_get_page(view->document, view->page);
+ poppler_page_get_size(page, &width, &height);
+ doc_aspect = width / height;
+ window_aspect = (double) allocation.width / allocation.height;
+ if (doc_aspect < window_aspect)
+ scale = allocation.height / height;
+ else
+ scale = allocation.width / width;
+ cairo_translate(cr, allocation.x, allocation.y);
+ cairo_scale(cr, scale, scale);
+ cairo_translate(cr,
+ (allocation.width - width * scale) / 2 / scale,
+ (allocation.height - height * scale) / 2 / scale);
+ cairo_rectangle(cr, 0, 0, width, height);
+ cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+ cairo_set_source_rgb(cr, 1, 1, 1);
+ cairo_fill(cr);
+ poppler_page_render(page, cr);
+ cairo_destroy(cr);
+ cairo_surface_destroy(surface);
+ g_object_unref(G_OBJECT(page));
+}
+
+static void
+resize_handler(struct widget *widget,
+ int32_t width, int32_t height, void *data)
+{
+ struct view *view = data;
+
+ widget_set_size(view->widget, width, height);
+}
+
+static void
+view_page_up(struct view *view)
+{
+ if(view->page <= 0)
+ return;
+
+ view->page--;
+ window_schedule_redraw(view->window);
+}
+
+static void
+view_page_down(struct view *view)
+{
+ if(!view->document ||
+ view->page >= poppler_document_get_n_pages(view->document) - 1) {
+ return;
+ }
+
+ view->page++;
+ window_schedule_redraw(view->window);
+}
+
+static void
+button_handler(struct widget *widget, struct input *input, uint32_t time,
+ uint32_t button, enum wl_pointer_button_state state,
+ void *data)
+{
+ struct view *view = data;
+
+ if (state == WL_POINTER_BUTTON_STATE_RELEASED)
+ return;
+
+ switch(button) {
+ case 275:
+ view_page_up(view);
+ break;
+ case 276:
+ view_page_down(view);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+fullscreen_handler(struct window *window, void *data)
+{
+ struct view *view = data;
+
+ view->fullscreen ^= 1;
+ window_set_fullscreen(window, view->fullscreen);
+}
+
+static void
+close_handler(struct window *window, void *data)
+{
+ struct view *view = data;
+
+ *view->view_counter -= 1;
+ if (*view->view_counter == 0)
+ display_exit(view->display);
+
+ widget_destroy(view->widget);
+ window_destroy(view->window);
+ if (view->document)
+ g_object_unref(view->document);
+
+ free(view);
+}
+
+static void
+key_handler(struct window *window, struct input *input, uint32_t time,
+ uint32_t key, uint32_t unicode,
+ enum wl_keyboard_key_state state, void *data)
+{
+ struct view *view = data;
+
+ if (state == WL_KEYBOARD_KEY_STATE_RELEASED)
+ return;
+
+ switch (key) {
+ case KEY_SPACE:
+ case KEY_PAGEDOWN:
+ case KEY_RIGHT:
+ case KEY_DOWN:
+ view_page_down(view);
+ break;
+ case KEY_BACKSPACE:
+ case KEY_PAGEUP:
+ case KEY_LEFT:
+ case KEY_UP:
+ view_page_up(view);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+keyboard_focus_handler(struct window *window,
+ struct input *device, void *data)
+{
+ struct view *view = data;
+ window_schedule_redraw(view->window);
+}
+
+static struct view *
+view_create(struct display *display,
+ uint32_t key, const char *filename, int fullscreen, int *view_counter)
+{
+ struct view *view;
+ gchar *basename;
+ gchar *title;
+ GFile *file = NULL;
+ GError *error = NULL;
+
+ view = zalloc(sizeof *view);
+ if (view == NULL)
+ return view;
+
+ file = g_file_new_for_commandline_arg(filename);
+ basename = g_file_get_basename(file);
+ if(!basename) {
+ title = g_strdup("Wayland View");
+ } else {
+ title = g_strdup_printf("Wayland View - %s", basename);
+ g_free(basename);
+ }
+
+ view->document = poppler_document_new_from_file(g_file_get_uri(file),
+ NULL, &error);
+
+ if(error) {
+ title = g_strdup("File not found");
+ }
+
+ view->window = window_create(display);
+ view->widget = frame_create(view->window, view);
+ window_set_title(view->window, title);
+ g_free(title);
+ view->display = display;
+
+ window_set_user_data(view->window, view);
+ window_set_key_handler(view->window, key_handler);
+ window_set_keyboard_focus_handler(view->window,
+ keyboard_focus_handler);
+ window_set_fullscreen_handler(view->window, fullscreen_handler);
+ window_set_close_handler(view->window, close_handler);
+
+ widget_set_button_handler(view->widget, button_handler);
+ widget_set_resize_handler(view->widget, resize_handler);
+ widget_set_redraw_handler(view->widget, redraw_handler);
+
+ view->page = 0;
+
+ view->fullscreen = fullscreen;
+ window_set_fullscreen(view->window, view->fullscreen);
+
+ window_schedule_resize(view->window, 500, 400);
+ view->view_counter = view_counter;
+ *view_counter += 1;
+
+ return view;
+}
+
+static int option_fullscreen;
+
+static const struct weston_option view_options[] = {
+ { WESTON_OPTION_BOOLEAN, "fullscreen", 0, &option_fullscreen },
+};
+
+int
+main(int argc, char *argv[])
+{
+ struct display *d;
+ int i;
+ int view_counter = 0;
+
+#if !GLIB_CHECK_VERSION(2, 35, 0)
+ g_type_init();
+#endif
+
+ parse_options(view_options, ARRAY_LENGTH(view_options), &argc, argv);
+
+ d = display_create(&argc, argv);
+ if (d == NULL) {
+ fprintf(stderr, "failed to create display: %m\n");
+ return -1;
+ }
+
+ for (i = 1; i < argc; i++)
+ view_create (d, i, argv[i], option_fullscreen, &view_counter);
+
+ if (view_counter > 0)
+ display_run(d);
+
+ return 0;
+}
diff --git a/clients/weston-info.c b/clients/weston-info.c
new file mode 100644
index 00000000..5d928f54
--- /dev/null
+++ b/clients/weston-info.c
@@ -0,0 +1,457 @@
+/*
+ * Copyright © 2012 Philipp Brüschweiler
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that 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.
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <wayland-client.h>
+
+#include "../shared/os-compatibility.h"
+
+typedef void (*print_info_t)(void *info);
+
+struct global_info {
+ struct wl_list link;
+
+ uint32_t id;
+ uint32_t version;
+ char *interface;
+
+ print_info_t print;
+};
+
+struct output_mode {
+ struct wl_list link;
+
+ uint32_t flags;
+ int32_t width, height;
+ int32_t refresh;
+};
+
+struct output_info {
+ struct global_info global;
+
+ struct wl_output *output;
+
+ struct {
+ int32_t x, y;
+ int32_t physical_width, physical_height;
+ enum wl_output_subpixel subpixel;
+ enum wl_output_transform output_transform;
+ char *make;
+ char *model;
+ } geometry;
+
+ struct wl_list modes;
+};
+
+struct shm_format {
+ struct wl_list link;
+
+ uint32_t format;
+};
+
+struct shm_info {
+ struct global_info global;
+ struct wl_shm *shm;
+
+ struct wl_list formats;
+};
+
+struct seat_info {
+ struct global_info global;
+ struct wl_seat *seat;
+
+ uint32_t capabilities;
+ char *name;
+};
+
+struct weston_info {
+ struct wl_display *display;
+ struct wl_registry *registry;
+
+ struct wl_list infos;
+ bool roundtrip_needed;
+};
+
+static void *
+xmalloc(size_t s)
+{
+ void *p = malloc(s);
+
+ if (p == NULL) {
+ fprintf(stderr, "out of memory\n");
+ exit(1);
+ }
+
+ return p;
+}
+
+static void
+print_global_info(void *data)
+{
+ struct global_info *global = data;
+
+ printf("interface: '%s', version: %u, name: %u\n",
+ global->interface, global->version, global->id);
+}
+
+static void
+init_global_info(struct weston_info *info,
+ struct global_info *global, uint32_t id,
+ const char *interface, uint32_t version)
+{
+ global->id = id;
+ global->version = version;
+ global->interface = strdup(interface);
+
+ wl_list_insert(info->infos.prev, &global->link);
+}
+
+static void
+print_output_info(void *data)
+{
+ struct output_info *output = data;
+ struct output_mode *mode;
+ const char *subpixel_orientation;
+ const char *transform;
+
+ print_global_info(data);
+
+ switch (output->geometry.subpixel) {
+ case WL_OUTPUT_SUBPIXEL_UNKNOWN:
+ subpixel_orientation = "unknown";
+ break;
+ case WL_OUTPUT_SUBPIXEL_NONE:
+ subpixel_orientation = "none";
+ break;
+ case WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB:
+ subpixel_orientation = "horizontal rgb";
+ break;
+ case WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR:
+ subpixel_orientation = "horizontal bgr";
+ break;
+ case WL_OUTPUT_SUBPIXEL_VERTICAL_RGB:
+ subpixel_orientation = "vertical rgb";
+ break;
+ case WL_OUTPUT_SUBPIXEL_VERTICAL_BGR:
+ subpixel_orientation = "vertical bgr";
+ break;
+ default:
+ fprintf(stderr, "unknown subpixel orientation %u\n",
+ output->geometry.subpixel);
+ subpixel_orientation = "unexpected value";
+ break;
+ }
+
+ switch (output->geometry.output_transform) {
+ case WL_OUTPUT_TRANSFORM_NORMAL:
+ transform = "normal";
+ break;
+ case WL_OUTPUT_TRANSFORM_90:
+ transform = "90°";
+ break;
+ case WL_OUTPUT_TRANSFORM_180:
+ transform = "180°";
+ break;
+ case WL_OUTPUT_TRANSFORM_270:
+ transform = "270°";
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED:
+ transform = "flipped";
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+ transform = "flipped 90°";
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+ transform = "flipped 180°";
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+ transform = "flipped 270°";
+ break;
+ default:
+ fprintf(stderr, "unknown output transform %u\n",
+ output->geometry.output_transform);
+ transform = "unexpected value";
+ break;
+ }
+
+ printf("\tx: %d, y: %d,\n",
+ output->geometry.x, output->geometry.y);
+ printf("\tphysical_width: %d mm, physical_height: %d mm,\n",
+ output->geometry.physical_width,
+ output->geometry.physical_height);
+ printf("\tmake: '%s', model: '%s',\n",
+ output->geometry.make, output->geometry.model);
+ printf("\tsubpixel_orientation: %s, output_tranform: %s,\n",
+ subpixel_orientation, transform);
+
+ wl_list_for_each(mode, &output->modes, link) {
+ printf("\tmode:\n");
+
+ printf("\t\twidth: %d px, height: %d px, refresh: %.f Hz,\n",
+ mode->width, mode->height,
+ (float) mode->refresh / 1000);
+
+ printf("\t\tflags:");
+ if (mode->flags & WL_OUTPUT_MODE_CURRENT)
+ printf(" current");
+ if (mode->flags & WL_OUTPUT_MODE_PREFERRED)
+ printf(" preferred");
+ printf("\n");
+ }
+}
+
+static void
+print_shm_info(void *data)
+{
+ struct shm_info *shm = data;
+ struct shm_format *format;
+
+ print_global_info(data);
+
+ printf("\tformats:");
+
+ wl_list_for_each(format, &shm->formats, link)
+ printf(" %s", (format->format == WL_SHM_FORMAT_ARGB8888) ?
+ "ARGB8888" : "XRGB8888");
+
+ printf("\n");
+}
+
+static void
+print_seat_info(void *data)
+{
+ struct seat_info *seat = data;
+
+ print_global_info(data);
+
+ printf("\tname: %s\n", seat->name);
+ printf("\tcapabilities:");
+
+ if (seat->capabilities & WL_SEAT_CAPABILITY_POINTER)
+ printf(" pointer");
+ if (seat->capabilities & WL_SEAT_CAPABILITY_KEYBOARD)
+ printf(" keyboard");
+ if (seat->capabilities & WL_SEAT_CAPABILITY_TOUCH)
+ printf(" touch");
+
+ printf("\n");
+}
+
+static void
+seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
+ enum wl_seat_capability caps)
+{
+ struct seat_info *seat = data;
+ seat->capabilities = caps;
+}
+
+static void
+seat_handle_name(void *data, struct wl_seat *wl_seat,
+ const char *name)
+{
+ struct seat_info *seat = data;
+ seat->name = strdup(name);
+}
+
+static const struct wl_seat_listener seat_listener = {
+ seat_handle_capabilities,
+ seat_handle_name,
+};
+
+static void
+add_seat_info(struct weston_info *info, uint32_t id, uint32_t version)
+{
+ struct seat_info *seat = xmalloc(sizeof *seat);
+
+ init_global_info(info, &seat->global, id, "wl_seat", version);
+ seat->global.print = print_seat_info;
+
+ seat->seat = wl_registry_bind(info->registry,
+ id, &wl_seat_interface, 2);
+ wl_seat_add_listener(seat->seat, &seat_listener, seat);
+
+ info->roundtrip_needed = true;
+}
+
+static void
+shm_handle_format(void *data, struct wl_shm *wl_shm, uint32_t format)
+{
+ struct shm_info *shm = data;
+ struct shm_format *shm_format = xmalloc(sizeof *shm_format);
+
+ wl_list_insert(&shm->formats, &shm_format->link);
+ shm_format->format = format;
+}
+
+static const struct wl_shm_listener shm_listener = {
+ shm_handle_format,
+};
+
+static void
+add_shm_info(struct weston_info *info, uint32_t id, uint32_t version)
+{
+ struct shm_info *shm = xmalloc(sizeof *shm);
+
+ init_global_info(info, &shm->global, id, "wl_shm", version);
+ shm->global.print = print_shm_info;
+ wl_list_init(&shm->formats);
+
+ shm->shm = wl_registry_bind(info->registry,
+ id, &wl_shm_interface, 1);
+ wl_shm_add_listener(shm->shm, &shm_listener, shm);
+
+ info->roundtrip_needed = true;
+}
+
+static void
+output_handle_geometry(void *data, struct wl_output *wl_output,
+ int32_t x, int32_t y,
+ int32_t physical_width, int32_t physical_height,
+ int32_t subpixel,
+ const char *make, const char *model,
+ int32_t output_transform)
+{
+ struct output_info *output = data;
+
+ output->geometry.x = x;
+ output->geometry.y = y;
+ output->geometry.physical_width = physical_width;
+ output->geometry.physical_height = physical_height;
+ output->geometry.subpixel = subpixel;
+ output->geometry.make = strdup(make);
+ output->geometry.model = strdup(model);
+ output->geometry.output_transform = output_transform;
+}
+
+static void
+output_handle_mode(void *data, struct wl_output *wl_output,
+ uint32_t flags, int32_t width, int32_t height,
+ int32_t refresh)
+{
+ struct output_info *output = data;
+ struct output_mode *mode = xmalloc(sizeof *mode);
+
+ mode->flags = flags;
+ mode->width = width;
+ mode->height = height;
+ mode->refresh = refresh;
+
+ wl_list_insert(output->modes.prev, &mode->link);
+}
+
+static const struct wl_output_listener output_listener = {
+ output_handle_geometry,
+ output_handle_mode,
+};
+
+static void
+add_output_info(struct weston_info *info, uint32_t id, uint32_t version)
+{
+ struct output_info *output = xmalloc(sizeof *output);
+
+ init_global_info(info, &output->global, id, "wl_output", version);
+ output->global.print = print_output_info;
+
+ wl_list_init(&output->modes);
+
+ output->output = wl_registry_bind(info->registry, id,
+ &wl_output_interface, 1);
+ wl_output_add_listener(output->output, &output_listener,
+ output);
+
+ info->roundtrip_needed = true;
+}
+
+static void
+add_global_info(struct weston_info *info, uint32_t id,
+ const char *interface, uint32_t version)
+{
+ struct global_info *global = xmalloc(sizeof *global);
+
+ init_global_info(info, global, id, interface, version);
+ global->print = print_global_info;
+}
+
+static void
+global_handler(void *data, struct wl_registry *registry, uint32_t id,
+ const char *interface, uint32_t version)
+{
+ struct weston_info *info = data;
+
+ if (!strcmp(interface, "wl_seat"))
+ add_seat_info(info, id, version);
+ else if (!strcmp(interface, "wl_shm"))
+ add_shm_info(info, id, version);
+ else if (!strcmp(interface, "wl_output"))
+ add_output_info(info, id, version);
+ else
+ add_global_info(info, id, interface, version);
+}
+
+static void
+global_remove_handler(void *data, struct wl_registry *registry, uint32_t name)
+{
+}
+
+static const struct wl_registry_listener registry_listener = {
+ global_handler,
+ global_remove_handler
+};
+
+static void
+print_infos(struct wl_list *infos)
+{
+ struct global_info *info;
+
+ wl_list_for_each(info, infos, link)
+ info->print(info);
+}
+
+int
+main(int argc, char **argv)
+{
+ struct weston_info info;
+
+ info.display = wl_display_connect(NULL);
+ if (!info.display) {
+ fprintf(stderr, "failed to create display: %m\n");
+ return -1;
+ }
+
+ wl_list_init(&info.infos);
+
+ info.registry = wl_display_get_registry(info.display);
+ wl_registry_add_listener(info.registry, &registry_listener, &info);
+
+ do {
+ info.roundtrip_needed = false;
+ wl_display_roundtrip(info.display);
+ } while (info.roundtrip_needed);
+
+ print_infos(&info.infos);
+
+ return 0;
+}
diff --git a/clients/weston-simple-im.c b/clients/weston-simple-im.c
new file mode 100644
index 00000000..ded6a04d
--- /dev/null
+++ b/clients/weston-simple-im.c
@@ -0,0 +1,518 @@
+/*
+ * Copyright © 2012 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 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.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+
+#include <linux/input.h>
+
+#include "window.h"
+#include "input-method-client-protocol.h"
+
+enum compose_state {
+ state_normal,
+ state_compose
+};
+
+struct compose_seq {
+ uint32_t keys[4];
+
+ const char *text;
+};
+
+struct simple_im;
+
+typedef void (*keyboard_input_key_handler_t)(struct simple_im *keyboard,
+ uint32_t serial,
+ uint32_t time, uint32_t key, uint32_t unicode,
+ enum wl_keyboard_key_state state);
+
+struct simple_im {
+ struct wl_input_method *input_method;
+ struct wl_input_method_context *context;
+ struct wl_display *display;
+ struct wl_registry *registry;
+ struct wl_keyboard *keyboard;
+ enum compose_state compose_state;
+ struct compose_seq compose_seq;
+
+ struct xkb_context *xkb_context;
+
+ uint32_t modifiers;
+
+ 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;
+
+ keyboard_input_key_handler_t key_handler;
+
+ uint32_t serial;
+};
+
+static const struct compose_seq compose_seqs[] = {
+ { { XKB_KEY_quotedbl, XKB_KEY_A, 0 }, "Ä" },
+ { { XKB_KEY_quotedbl, XKB_KEY_O, 0 }, "Ö" },
+ { { XKB_KEY_quotedbl, XKB_KEY_U, 0 }, "Ü" },
+ { { XKB_KEY_quotedbl, XKB_KEY_a, 0 }, "ä" },
+ { { XKB_KEY_quotedbl, XKB_KEY_o, 0 }, "ö" },
+ { { XKB_KEY_quotedbl, XKB_KEY_u, 0 }, "ü" },
+ { { XKB_KEY_apostrophe, XKB_KEY_A, 0 }, "Á" },
+ { { XKB_KEY_apostrophe, XKB_KEY_a, 0 }, "á" },
+ { { XKB_KEY_slash, XKB_KEY_O, 0 }, "Ø" },
+ { { XKB_KEY_slash, XKB_KEY_o, 0 }, "ø" },
+ { { XKB_KEY_less, XKB_KEY_3, 0 }, "♥" },
+ { { XKB_KEY_A, XKB_KEY_A, 0 }, "Å" },
+ { { XKB_KEY_A, XKB_KEY_E, 0 }, "Æ" },
+ { { XKB_KEY_O, XKB_KEY_C, 0 }, "©" },
+ { { XKB_KEY_O, XKB_KEY_R, 0 }, "®" },
+ { { XKB_KEY_s, XKB_KEY_s, 0 }, "ß" },
+ { { XKB_KEY_a, XKB_KEY_e, 0 }, "æ" },
+ { { XKB_KEY_a, XKB_KEY_a, 0 }, "å" },
+};
+
+static const uint32_t ignore_keys_on_compose[] = {
+ XKB_KEY_Shift_L,
+ XKB_KEY_Shift_R
+};
+
+static void
+handle_surrounding_text(void *data,
+ struct wl_input_method_context *context,
+ const char *text,
+ uint32_t cursor,
+ uint32_t anchor)
+{
+ fprintf(stderr, "Surrounding text updated: %s\n", text);
+}
+
+static void
+handle_reset(void *data,
+ struct wl_input_method_context *context)
+{
+ struct simple_im *keyboard = data;
+
+ fprintf(stderr, "Reset pre-edit buffer\n");
+
+ keyboard->compose_state = state_normal;
+}
+
+static void
+handle_content_type(void *data,
+ struct wl_input_method_context *context,
+ uint32_t hint,
+ uint32_t purpose)
+{
+}
+
+static void
+handle_invoke_action(void *data,
+ struct wl_input_method_context *context,
+ uint32_t button,
+ uint32_t index)
+{
+}
+
+static void
+handle_commit_state(void *data,
+ struct wl_input_method_context *context,
+ uint32_t serial)
+{
+ struct simple_im *keyboard = data;
+
+ keyboard->serial = serial;
+}
+
+static void
+handle_preferred_language(void *data,
+ struct wl_input_method_context *context,
+ const char *language)
+{
+}
+
+static const struct wl_input_method_context_listener input_method_context_listener = {
+ handle_surrounding_text,
+ handle_reset,
+ handle_content_type,
+ handle_invoke_action,
+ handle_commit_state,
+ handle_preferred_language
+};
+
+static void
+input_method_keyboard_keymap(void *data,
+ struct wl_keyboard *wl_keyboard,
+ uint32_t format,
+ int32_t fd,
+ uint32_t size)
+{
+ struct simple_im *keyboard = data;
+ char *map_str;
+
+ if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
+ close(fd);
+ return;
+ }
+
+ map_str = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
+ if (map_str == MAP_FAILED) {
+ close(fd);
+ return;
+ }
+
+ keyboard->keymap =
+ xkb_map_new_from_string(keyboard->xkb_context,
+ map_str,
+ XKB_KEYMAP_FORMAT_TEXT_V1,
+ 0);
+
+ munmap(map_str, size);
+ close(fd);
+
+ if (!keyboard->keymap) {
+ fprintf(stderr, "failed to compile keymap\n");
+ return;
+ }
+
+ keyboard->state = xkb_state_new(keyboard->keymap);
+ if (!keyboard->state) {
+ fprintf(stderr, "failed to create XKB state\n");
+ xkb_map_unref(keyboard->keymap);
+ return;
+ }
+
+ keyboard->control_mask =
+ 1 << xkb_map_mod_get_index(keyboard->keymap, "Control");
+ keyboard->alt_mask =
+ 1 << xkb_map_mod_get_index(keyboard->keymap, "Mod1");
+ keyboard->shift_mask =
+ 1 << xkb_map_mod_get_index(keyboard->keymap, "Shift");
+}
+
+static void
+input_method_keyboard_key(void *data,
+ struct wl_keyboard *wl_keyboard,
+ uint32_t serial,
+ uint32_t time,
+ uint32_t key,
+ uint32_t state_w)
+{
+ struct simple_im *keyboard = data;
+ uint32_t code;
+ uint32_t num_syms;
+ const xkb_keysym_t *syms;
+ xkb_keysym_t sym;
+ enum wl_keyboard_key_state state = state_w;
+
+ if (!keyboard->state)
+ return;
+
+ code = key + 8;
+ num_syms = xkb_key_get_syms(keyboard->state, code, &syms);
+
+ sym = XKB_KEY_NoSymbol;
+ if (num_syms == 1)
+ sym = syms[0];
+
+ if (keyboard->key_handler)
+ (*keyboard->key_handler)(keyboard, serial, time, key, sym,
+ state);
+}
+
+static void
+input_method_keyboard_modifiers(void *data,
+ struct wl_keyboard *wl_keyboard,
+ uint32_t serial,
+ uint32_t mods_depressed,
+ uint32_t mods_latched,
+ uint32_t mods_locked,
+ uint32_t group)
+{
+ struct simple_im *keyboard = data;
+ struct wl_input_method_context *context = keyboard->context;
+ xkb_mod_mask_t mask;
+
+ xkb_state_update_mask(keyboard->state, mods_depressed,
+ mods_latched, mods_locked, 0, 0, group);
+ mask = xkb_state_serialize_mods(keyboard->state,
+ XKB_STATE_DEPRESSED |
+ XKB_STATE_LATCHED);
+
+ keyboard->modifiers = 0;
+ if (mask & keyboard->control_mask)
+ keyboard->modifiers |= MOD_CONTROL_MASK;
+ if (mask & keyboard->alt_mask)
+ keyboard->modifiers |= MOD_ALT_MASK;
+ if (mask & keyboard->shift_mask)
+ keyboard->modifiers |= MOD_SHIFT_MASK;
+
+ wl_input_method_context_modifiers(context, serial,
+ mods_depressed, mods_depressed,
+ mods_latched, group);
+}
+
+static const struct wl_keyboard_listener input_method_keyboard_listener = {
+ input_method_keyboard_keymap,
+ NULL, /* enter */
+ NULL, /* leave */
+ input_method_keyboard_key,
+ input_method_keyboard_modifiers
+};
+
+static void
+input_method_activate(void *data,
+ struct wl_input_method *input_method,
+ struct wl_input_method_context *context)
+{
+ struct simple_im *keyboard = data;
+
+ if (keyboard->context)
+ wl_input_method_context_destroy(keyboard->context);
+
+ keyboard->compose_state = state_normal;
+
+ keyboard->serial = 0;
+
+ keyboard->context = context;
+ wl_input_method_context_add_listener(context,
+ &input_method_context_listener,
+ keyboard);
+ keyboard->keyboard = wl_input_method_context_grab_keyboard(context);
+ wl_keyboard_add_listener(keyboard->keyboard,
+ &input_method_keyboard_listener,
+ keyboard);
+}
+
+static void
+input_method_deactivate(void *data,
+ struct wl_input_method *input_method,
+ struct wl_input_method_context *context)
+{
+ struct simple_im *keyboard = data;
+
+ if (!keyboard->context)
+ return;
+
+ wl_input_method_context_destroy(keyboard->context);
+ keyboard->context = NULL;
+}
+
+static const struct wl_input_method_listener input_method_listener = {
+ input_method_activate,
+ input_method_deactivate
+};
+
+static void
+registry_handle_global(void *data, struct wl_registry *registry,
+ uint32_t name, const char *interface, uint32_t version)
+{
+ struct simple_im *keyboard = data;
+
+ if (!strcmp(interface, "wl_input_method")) {
+ keyboard->input_method =
+ wl_registry_bind(registry, name,
+ &wl_input_method_interface, 1);
+ wl_input_method_add_listener(keyboard->input_method,
+ &input_method_listener, keyboard);
+ }
+}
+
+static void
+registry_handle_global_remove(void *data, struct wl_registry *registry,
+ uint32_t name)
+{
+}
+
+static const struct wl_registry_listener registry_listener = {
+ registry_handle_global,
+ registry_handle_global_remove
+};
+
+static int
+compare_compose_keys(const void *c1, const void *c2)
+{
+ const struct compose_seq *cs1 = c1;
+ const struct compose_seq *cs2 = c2;
+ int i;
+
+ for (i = 0; cs1->keys[i] != 0 && cs2->keys[i] != 0; i++) {
+ if (cs1->keys[i] != cs2->keys[i])
+ return cs1->keys[i] - cs2->keys[i];
+ }
+
+ if (cs1->keys[i] == cs2->keys[i]
+ || cs1->keys[i] == 0)
+ return 0;
+
+ return cs1->keys[i] - cs2->keys[i];
+}
+
+static void
+simple_im_key_handler(struct simple_im *keyboard,
+ uint32_t serial, uint32_t time, uint32_t key, uint32_t sym,
+ enum wl_keyboard_key_state state)
+{
+ struct wl_input_method_context *context = keyboard->context;
+ char text[64];
+
+ if (sym == XKB_KEY_Multi_key &&
+ state == WL_KEYBOARD_KEY_STATE_RELEASED &&
+ keyboard->compose_state == state_normal) {
+ keyboard->compose_state = state_compose;
+ memset(&keyboard->compose_seq, 0, sizeof(struct compose_seq));
+ return;
+ }
+
+ if (keyboard->compose_state == state_compose) {
+ uint32_t i = 0;
+ struct compose_seq *cs;
+
+ if (state == WL_KEYBOARD_KEY_STATE_PRESSED)
+ return;
+
+ for (i = 0; i < sizeof(ignore_keys_on_compose) / sizeof(ignore_keys_on_compose[0]); i++) {
+ if (sym == ignore_keys_on_compose[i]) {
+ wl_input_method_context_key(context, keyboard->serial, time, key, state);
+ return;
+ }
+ }
+
+ for (i = 0; keyboard->compose_seq.keys[i] != 0; i++);
+
+ keyboard->compose_seq.keys[i] = sym;
+
+ cs = bsearch (&keyboard->compose_seq, compose_seqs,
+ sizeof(compose_seqs) / sizeof(compose_seqs[0]),
+ sizeof(compose_seqs[0]), compare_compose_keys);
+
+ if (cs) {
+ if (cs->keys[i + 1] == 0) {
+ wl_input_method_context_preedit_cursor(keyboard->context,
+ 0);
+ wl_input_method_context_preedit_string(keyboard->context,
+ keyboard->serial,
+ "", "");
+ wl_input_method_context_cursor_position(keyboard->context,
+ 0, 0);
+ wl_input_method_context_commit_string(keyboard->context,
+ keyboard->serial,
+ cs->text);
+ keyboard->compose_state = state_normal;
+ } else {
+ uint32_t j = 0, idx = 0;
+
+ for (; j <= i; j++) {
+ idx += xkb_keysym_to_utf8(cs->keys[j], text + idx, sizeof(text) - idx);
+ }
+
+ wl_input_method_context_preedit_cursor(keyboard->context,
+ strlen(text));
+ wl_input_method_context_preedit_string(keyboard->context,
+ keyboard->serial,
+ text,
+ text);
+ }
+ } else {
+ uint32_t j = 0, idx = 0;
+
+ for (; j <= i; j++) {
+ idx += xkb_keysym_to_utf8(keyboard->compose_seq.keys[j], text + idx, sizeof(text) - idx);
+ }
+ wl_input_method_context_preedit_cursor(keyboard->context,
+ 0);
+ wl_input_method_context_preedit_string(keyboard->context,
+ keyboard->serial,
+ "", "");
+ wl_input_method_context_cursor_position(keyboard->context,
+ 0, 0);
+ wl_input_method_context_commit_string(keyboard->context,
+ keyboard->serial,
+ text);
+ keyboard->compose_state = state_normal;
+ }
+ return;
+ }
+
+ if (xkb_keysym_to_utf8(sym, text, sizeof(text)) <= 0) {
+ wl_input_method_context_key(context, serial, time, key, state);
+ return;
+ }
+
+ if (state == WL_KEYBOARD_KEY_STATE_PRESSED)
+ return;
+
+ wl_input_method_context_cursor_position(keyboard->context,
+ 0, 0);
+ wl_input_method_context_commit_string(keyboard->context,
+ keyboard->serial,
+ text);
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct simple_im simple_im;
+ int ret = 0;
+
+ memset(&simple_im, 0, sizeof(simple_im));
+
+ simple_im.display = wl_display_connect(NULL);
+ if (simple_im.display == NULL) {
+ fprintf(stderr, "failed to connect to server: %m\n");
+ return -1;
+ }
+
+ simple_im.registry = wl_display_get_registry(simple_im.display);
+ wl_registry_add_listener(simple_im.registry,
+ &registry_listener, &simple_im);
+ wl_display_roundtrip(simple_im.display);
+ if (simple_im.input_method == NULL) {
+ fprintf(stderr, "No input_method global\n");
+ exit(1);
+ }
+
+ simple_im.xkb_context = xkb_context_new(0);
+ if (simple_im.xkb_context == NULL) {
+ fprintf(stderr, "Failed to create XKB context\n");
+ return -1;
+ }
+
+ simple_im.context = NULL;
+ simple_im.key_handler = simple_im_key_handler;
+
+ while (ret != -1)
+ ret = wl_display_dispatch(simple_im.display);
+
+ if (ret == -1) {
+ fprintf(stderr, "Dispatch error: %m\n");
+ exit(1);
+ }
+
+ return 0;
+}
diff --git a/clients/window.c b/clients/window.c
new file mode 100644
index 00000000..56de5d78
--- /dev/null
+++ b/clients/window.c
@@ -0,0 +1,5658 @@
+/*
+ * Copyright © 2008 Kristian Høgsberg
+ * Copyright © 2012-2013 Collabora, Ltd.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * 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.
+ */
+
+#include "config.h"
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <math.h>
+#include <assert.h>
+#include <time.h>
+#include <cairo.h>
+#include <sys/mman.h>
+#include <sys/epoll.h>
+#include <sys/timerfd.h>
+
+#ifdef HAVE_CAIRO_EGL
+#include <wayland-egl.h>
+
+#ifdef USE_CAIRO_GLESV2
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#else
+#include <GL/gl.h>
+#endif
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include <cairo-gl.h>
+#else /* HAVE_CAIRO_EGL */
+typedef void *EGLDisplay;
+typedef void *EGLConfig;
+typedef void *EGLContext;
+#define EGL_NO_DISPLAY ((EGLDisplay)0)
+#endif /* no HAVE_CAIRO_EGL */
+
+#include <xkbcommon/xkbcommon.h>
+#include <wayland-cursor.h>
+
+#include <linux/input.h>
+#include <wayland-client.h>
+#include "../shared/cairo-util.h"
+#include "text-cursor-position-client-protocol.h"
+#include "workspaces-client-protocol.h"
+#include "../shared/os-compatibility.h"
+
+#include "window.h"
+
+struct shm_pool;
+
+struct global {
+ uint32_t name;
+ char *interface;
+ uint32_t version;
+ struct wl_list link;
+};
+
+struct display {
+ struct wl_display *display;
+ struct wl_registry *registry;
+ struct wl_compositor *compositor;
+ struct wl_subcompositor *subcompositor;
+ struct wl_shell *shell;
+ struct wl_shm *shm;
+ struct wl_data_device_manager *data_device_manager;
+ struct text_cursor_position *text_cursor_position;
+ struct workspace_manager *workspace_manager;
+ EGLDisplay dpy;
+ EGLConfig argb_config;
+ EGLContext argb_ctx;
+ cairo_device_t *argb_device;
+ uint32_t serial;
+
+ int display_fd;
+ uint32_t display_fd_events;
+ struct task display_task;
+
+ int epoll_fd;
+ struct wl_list deferred_list;
+
+ int running;
+
+ struct wl_list global_list;
+ struct wl_list window_list;
+ struct wl_list input_list;
+ struct wl_list output_list;
+
+ struct theme *theme;
+
+ struct wl_cursor_theme *cursor_theme;
+ struct wl_cursor **cursors;
+
+ display_output_handler_t output_configure_handler;
+ display_global_handler_t global_handler;
+
+ void *user_data;
+
+ struct xkb_context *xkb_context;
+
+ uint32_t workspace;
+ uint32_t workspace_count;
+
+ /* A hack to get text extents for tooltips */
+ cairo_surface_t *dummy_surface;
+ void *dummy_surface_data;
+
+ int has_rgb565;
+ int seat_version;
+};
+
+enum {
+ TYPE_NONE,
+ TYPE_TOPLEVEL,
+ TYPE_FULLSCREEN,
+ TYPE_MAXIMIZED,
+ TYPE_TRANSIENT,
+ TYPE_MENU,
+ TYPE_CUSTOM
+};
+
+struct window_output {
+ struct output *output;
+ struct wl_list link;
+};
+
+struct toysurface {
+ /*
+ * Prepare the surface for drawing. Makes sure there is a surface
+ * of the right size available for rendering, and returns it.
+ * dx,dy are the x,y of wl_surface.attach.
+ * width,height are the new buffer size.
+ * If flags has SURFACE_HINT_RESIZE set, the user is
+ * doing continuous resizing.
+ * Returns the Cairo surface to draw to.
+ */
+ cairo_surface_t *(*prepare)(struct toysurface *base, int dx, int dy,
+ int32_t width, int32_t height, uint32_t flags,
+ enum wl_output_transform buffer_transform, int32_t buffer_scale);
+
+ /*
+ * Post the surface to the server, returning the server allocation
+ * rectangle. The Cairo surface from prepare() must be destroyed
+ * after calling this.
+ */
+ void (*swap)(struct toysurface *base,
+ enum wl_output_transform buffer_transform, int32_t buffer_scale,
+ struct rectangle *server_allocation);
+
+ /*
+ * Make the toysurface current with the given EGL context.
+ * Returns 0 on success, and negative of failure.
+ */
+ int (*acquire)(struct toysurface *base, EGLContext ctx);
+
+ /*
+ * Release the toysurface from the EGL context, returning control
+ * to Cairo.
+ */
+ void (*release)(struct toysurface *base);
+
+ /*
+ * Destroy the toysurface, including the Cairo surface, any
+ * backing storage, and the Wayland protocol objects.
+ */
+ void (*destroy)(struct toysurface *base);
+};
+
+struct surface {
+ struct window *window;
+
+ struct wl_surface *surface;
+ struct wl_subsurface *subsurface;
+ int synchronized;
+ int synchronized_default;
+ struct toysurface *toysurface;
+ struct widget *widget;
+ int redraw_needed;
+ struct wl_callback *frame_cb;
+ uint32_t last_time;
+
+ struct rectangle allocation;
+ struct rectangle server_allocation;
+
+ struct wl_region *input_region;
+ struct wl_region *opaque_region;
+
+ enum window_buffer_type buffer_type;
+ enum wl_output_transform buffer_transform;
+ int32_t buffer_scale;
+
+ cairo_surface_t *cairo_surface;
+
+ struct wl_list link;
+};
+
+struct window {
+ struct display *display;
+ struct window *parent;
+ struct wl_list window_output_list;
+ char *title;
+ struct rectangle saved_allocation;
+ struct rectangle min_allocation;
+ struct rectangle pending_allocation;
+ int x, y;
+ int resize_edges;
+ int redraw_needed;
+ int redraw_task_scheduled;
+ struct task redraw_task;
+ int resize_needed;
+ int saved_type;
+ int type;
+ int focus_count;
+
+ int resizing;
+ int fullscreen_method;
+ int configure_requests;
+
+ enum preferred_format preferred_format;
+
+ window_key_handler_t key_handler;
+ window_keyboard_focus_handler_t keyboard_focus_handler;
+ window_data_handler_t data_handler;
+ window_drop_handler_t drop_handler;
+ window_close_handler_t close_handler;
+ window_fullscreen_handler_t fullscreen_handler;
+ window_output_handler_t output_handler;
+
+ struct surface *main_surface;
+ struct wl_shell_surface *shell_surface;
+
+ struct frame *frame;
+
+ /* struct surface::link, contains also main_surface */
+ struct wl_list subsurface_list;
+
+ void *user_data;
+ struct wl_list link;
+};
+
+struct widget {
+ struct window *window;
+ struct surface *surface;
+ struct tooltip *tooltip;
+ struct wl_list child_list;
+ struct wl_list link;
+ struct rectangle allocation;
+ widget_resize_handler_t resize_handler;
+ widget_redraw_handler_t redraw_handler;
+ widget_enter_handler_t enter_handler;
+ widget_leave_handler_t leave_handler;
+ widget_motion_handler_t motion_handler;
+ widget_button_handler_t button_handler;
+ widget_touch_down_handler_t touch_down_handler;
+ widget_touch_up_handler_t touch_up_handler;
+ widget_touch_motion_handler_t touch_motion_handler;
+ widget_touch_frame_handler_t touch_frame_handler;
+ widget_touch_cancel_handler_t touch_cancel_handler;
+ widget_axis_handler_t axis_handler;
+ void *user_data;
+ int opaque;
+ int tooltip_count;
+ int default_cursor;
+};
+
+struct touch_point {
+ int32_t id;
+ struct widget *widget;
+ struct wl_list link;
+};
+
+struct input {
+ struct display *display;
+ struct wl_seat *seat;
+ struct wl_pointer *pointer;
+ struct wl_keyboard *keyboard;
+ struct wl_touch *touch;
+ struct wl_list touch_point_list;
+ struct window *pointer_focus;
+ struct window *keyboard_focus;
+ struct window *touch_focus;
+ int current_cursor;
+ uint32_t cursor_anim_start;
+ struct wl_callback *cursor_frame_cb;
+ struct wl_surface *pointer_surface;
+ uint32_t modifiers;
+ uint32_t pointer_enter_serial;
+ uint32_t cursor_serial;
+ float sx, sy;
+ struct wl_list link;
+
+ struct widget *focus_widget;
+ struct widget *grab;
+ uint32_t grab_button;
+
+ struct wl_data_device *data_device;
+ struct data_offer *drag_offer;
+ struct data_offer *selection_offer;
+
+ struct {
+ 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;
+ } xkb;
+
+ struct task repeat_task;
+ int repeat_timer_fd;
+ uint32_t repeat_sym;
+ uint32_t repeat_key;
+ uint32_t repeat_time;
+};
+
+struct output {
+ struct display *display;
+ struct wl_output *output;
+ struct rectangle allocation;
+ struct wl_list link;
+ int transform;
+ int scale;
+
+ display_output_handler_t destroy_handler;
+ void *user_data;
+};
+
+enum frame_button_action {
+ FRAME_BUTTON_NULL = 0,
+ FRAME_BUTTON_ICON = 1,
+ FRAME_BUTTON_CLOSE = 2,
+ FRAME_BUTTON_MINIMIZE = 3,
+ FRAME_BUTTON_MAXIMIZE = 4,
+};
+
+enum frame_button_pointer {
+ FRAME_BUTTON_DEFAULT = 0,
+ FRAME_BUTTON_OVER = 1,
+ FRAME_BUTTON_ACTIVE = 2,
+};
+
+enum frame_button_align {
+ FRAME_BUTTON_RIGHT = 0,
+ FRAME_BUTTON_LEFT = 1,
+};
+
+enum frame_button_decoration {
+ FRAME_BUTTON_NONE = 0,
+ FRAME_BUTTON_FANCY = 1,
+};
+
+struct frame_button {
+ struct widget *widget;
+ struct frame *frame;
+ cairo_surface_t *icon;
+ enum frame_button_action type;
+ enum frame_button_pointer state;
+ struct wl_list link; /* buttons_list */
+ enum frame_button_align align;
+ enum frame_button_decoration decoration;
+};
+
+struct frame {
+ struct widget *widget;
+ struct widget *child;
+ struct wl_list buttons_list;
+};
+
+struct menu {
+ struct window *window;
+ struct widget *widget;
+ struct input *input;
+ const char **entries;
+ uint32_t time;
+ int current;
+ int count;
+ int release_count;
+ menu_func_t func;
+};
+
+struct tooltip {
+ struct widget *parent;
+ struct window *window;
+ struct widget *widget;
+ char *entry;
+ struct task tooltip_task;
+ int tooltip_fd;
+ float x, y;
+};
+
+struct shm_pool {
+ struct wl_shm_pool *pool;
+ size_t size;
+ size_t used;
+ void *data;
+};
+
+enum {
+ CURSOR_DEFAULT = 100,
+ CURSOR_UNSET
+};
+
+enum window_location {
+ WINDOW_INTERIOR = 0,
+ WINDOW_RESIZING_TOP = 1,
+ WINDOW_RESIZING_BOTTOM = 2,
+ WINDOW_RESIZING_LEFT = 4,
+ WINDOW_RESIZING_TOP_LEFT = 5,
+ WINDOW_RESIZING_BOTTOM_LEFT = 6,
+ WINDOW_RESIZING_RIGHT = 8,
+ WINDOW_RESIZING_TOP_RIGHT = 9,
+ WINDOW_RESIZING_BOTTOM_RIGHT = 10,
+ WINDOW_RESIZING_MASK = 15,
+ WINDOW_EXTERIOR = 16,
+ WINDOW_TITLEBAR = 17,
+ WINDOW_CLIENT_AREA = 18,
+};
+
+static const cairo_user_data_key_t shm_surface_data_key;
+
+/* #define DEBUG */
+
+#ifdef DEBUG
+
+static void
+debug_print(void *proxy, int line, const char *func, const char *fmt, ...)
+__attribute__ ((format (printf, 4, 5)));
+
+static void
+debug_print(void *proxy, int line, const char *func, const char *fmt, ...)
+{
+ va_list ap;
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+ fprintf(stderr, "%8ld.%03ld ",
+ (long)tv.tv_sec & 0xffff, (long)tv.tv_usec / 1000);
+
+ if (proxy)
+ fprintf(stderr, "%s@%d ",
+ wl_proxy_get_class(proxy), wl_proxy_get_id(proxy));
+
+ /*fprintf(stderr, __FILE__ ":%d:%s ", line, func);*/
+ fprintf(stderr, "%s ", func);
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+}
+
+#define DBG(fmt, ...) \
+ debug_print(NULL, __LINE__, __func__, fmt, ##__VA_ARGS__)
+
+#define DBG_OBJ(obj, fmt, ...) \
+ debug_print(obj, __LINE__, __func__, fmt, ##__VA_ARGS__)
+
+#else
+
+#define DBG(...) do {} while (0)
+#define DBG_OBJ(...) do {} while (0)
+
+#endif
+
+static void
+surface_to_buffer_size (enum wl_output_transform buffer_transform, int32_t buffer_scale, int32_t *width, int32_t *height)
+{
+ int32_t tmp;
+
+ switch (buffer_transform) {
+ case WL_OUTPUT_TRANSFORM_90:
+ case WL_OUTPUT_TRANSFORM_270:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+ tmp = *width;
+ *width = *height;
+ *height = tmp;
+ break;
+ default:
+ break;
+ }
+
+ *width *= buffer_scale;
+ *height *= buffer_scale;
+}
+
+static void
+buffer_to_surface_size (enum wl_output_transform buffer_transform, int32_t buffer_scale, int32_t *width, int32_t *height)
+{
+ int32_t tmp;
+
+ switch (buffer_transform) {
+ case WL_OUTPUT_TRANSFORM_90:
+ case WL_OUTPUT_TRANSFORM_270:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+ tmp = *width;
+ *width = *height;
+ *height = tmp;
+ break;
+ default:
+ break;
+ }
+
+ *width /= buffer_scale;
+ *height /= buffer_scale;
+}
+
+#ifdef HAVE_CAIRO_EGL
+
+struct egl_window_surface {
+ struct toysurface base;
+ cairo_surface_t *cairo_surface;
+ struct display *display;
+ struct wl_surface *surface;
+ struct wl_egl_window *egl_window;
+ EGLSurface egl_surface;
+};
+
+static struct egl_window_surface *
+to_egl_window_surface(struct toysurface *base)
+{
+ return container_of(base, struct egl_window_surface, base);
+}
+
+static cairo_surface_t *
+egl_window_surface_prepare(struct toysurface *base, int dx, int dy,
+ int32_t width, int32_t height, uint32_t flags,
+ enum wl_output_transform buffer_transform, int32_t buffer_scale)
+{
+ struct egl_window_surface *surface = to_egl_window_surface(base);
+
+ surface_to_buffer_size (buffer_transform, buffer_scale, &width, &height);
+
+ wl_egl_window_resize(surface->egl_window, width, height, dx, dy);
+ cairo_gl_surface_set_size(surface->cairo_surface, width, height);
+
+ return cairo_surface_reference(surface->cairo_surface);
+}
+
+static void
+egl_window_surface_swap(struct toysurface *base,
+ enum wl_output_transform buffer_transform, int32_t buffer_scale,
+ struct rectangle *server_allocation)
+{
+ struct egl_window_surface *surface = to_egl_window_surface(base);
+
+ cairo_gl_surface_swapbuffers(surface->cairo_surface);
+ wl_egl_window_get_attached_size(surface->egl_window,
+ &server_allocation->width,
+ &server_allocation->height);
+
+ buffer_to_surface_size (buffer_transform, buffer_scale,
+ &server_allocation->width,
+ &server_allocation->height);
+}
+
+static int
+egl_window_surface_acquire(struct toysurface *base, EGLContext ctx)
+{
+ struct egl_window_surface *surface = to_egl_window_surface(base);
+ cairo_device_t *device;
+
+ device = cairo_surface_get_device(surface->cairo_surface);
+ if (!device)
+ return -1;
+
+ if (!ctx) {
+ if (device == surface->display->argb_device)
+ ctx = surface->display->argb_ctx;
+ else
+ assert(0);
+ }
+
+ cairo_device_flush(device);
+ cairo_device_acquire(device);
+ if (!eglMakeCurrent(surface->display->dpy, surface->egl_surface,
+ surface->egl_surface, ctx))
+ fprintf(stderr, "failed to make surface current\n");
+
+ return 0;
+}
+
+static void
+egl_window_surface_release(struct toysurface *base)
+{
+ struct egl_window_surface *surface = to_egl_window_surface(base);
+ cairo_device_t *device;
+
+ device = cairo_surface_get_device(surface->cairo_surface);
+ if (!device)
+ return;
+
+ if (!eglMakeCurrent(surface->display->dpy, NULL, NULL,
+ surface->display->argb_ctx))
+ fprintf(stderr, "failed to make context current\n");
+
+ cairo_device_release(device);
+}
+
+static void
+egl_window_surface_destroy(struct toysurface *base)
+{
+ struct egl_window_surface *surface = to_egl_window_surface(base);
+ struct display *d = surface->display;
+
+ cairo_surface_destroy(surface->cairo_surface);
+ eglDestroySurface(d->dpy, surface->egl_surface);
+ wl_egl_window_destroy(surface->egl_window);
+ surface->surface = NULL;
+
+ free(surface);
+}
+
+static struct toysurface *
+egl_window_surface_create(struct display *display,
+ struct wl_surface *wl_surface,
+ uint32_t flags,
+ struct rectangle *rectangle)
+{
+ struct egl_window_surface *surface;
+
+ if (display->dpy == EGL_NO_DISPLAY)
+ return NULL;
+
+ surface = calloc(1, sizeof *surface);
+ if (!surface)
+ return NULL;
+
+ surface->base.prepare = egl_window_surface_prepare;
+ surface->base.swap = egl_window_surface_swap;
+ surface->base.acquire = egl_window_surface_acquire;
+ surface->base.release = egl_window_surface_release;
+ surface->base.destroy = egl_window_surface_destroy;
+
+ surface->display = display;
+ surface->surface = wl_surface;
+
+ surface->egl_window = wl_egl_window_create(surface->surface,
+ rectangle->width,
+ rectangle->height);
+
+ surface->egl_surface = eglCreateWindowSurface(display->dpy,
+ display->argb_config,
+ surface->egl_window,
+ NULL);
+
+ surface->cairo_surface =
+ cairo_gl_surface_create_for_egl(display->argb_device,
+ surface->egl_surface,
+ rectangle->width,
+ rectangle->height);
+
+ return &surface->base;
+}
+
+#else
+
+static struct toysurface *
+egl_window_surface_create(struct display *display,
+ struct wl_surface *wl_surface,
+ uint32_t flags,
+ struct rectangle *rectangle)
+{
+ return NULL;
+}
+
+#endif
+
+struct shm_surface_data {
+ struct wl_buffer *buffer;
+ struct shm_pool *pool;
+};
+
+struct wl_buffer *
+display_get_buffer_for_surface(struct display *display,
+ cairo_surface_t *surface)
+{
+ struct shm_surface_data *data;
+
+ data = cairo_surface_get_user_data(surface, &shm_surface_data_key);
+
+ return data->buffer;
+}
+
+static void
+shm_pool_destroy(struct shm_pool *pool);
+
+static void
+shm_surface_data_destroy(void *p)
+{
+ struct shm_surface_data *data = p;
+
+ wl_buffer_destroy(data->buffer);
+ if (data->pool)
+ shm_pool_destroy(data->pool);
+
+ free(data);
+}
+
+static struct wl_shm_pool *
+make_shm_pool(struct display *display, int size, void **data)
+{
+ struct wl_shm_pool *pool;
+ int fd;
+
+ fd = os_create_anonymous_file(size);
+ if (fd < 0) {
+ fprintf(stderr, "creating a buffer file for %d B failed: %m\n",
+ size);
+ return NULL;
+ }
+
+ *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (*data == MAP_FAILED) {
+ fprintf(stderr, "mmap failed: %m\n");
+ close(fd);
+ return NULL;
+ }
+
+ pool = wl_shm_create_pool(display->shm, fd, size);
+
+ close(fd);
+
+ return pool;
+}
+
+static struct shm_pool *
+shm_pool_create(struct display *display, size_t size)
+{
+ struct shm_pool *pool = malloc(sizeof *pool);
+
+ if (!pool)
+ return NULL;
+
+ pool->pool = make_shm_pool(display, size, &pool->data);
+ if (!pool->pool) {
+ free(pool);
+ return NULL;
+ }
+
+ pool->size = size;
+ pool->used = 0;
+
+ return pool;
+}
+
+static void *
+shm_pool_allocate(struct shm_pool *pool, size_t size, int *offset)
+{
+ if (pool->used + size > pool->size)
+ return NULL;
+
+ *offset = pool->used;
+ pool->used += size;
+
+ return (char *) pool->data + *offset;
+}
+
+/* destroy the pool. this does not unmap the memory though */
+static void
+shm_pool_destroy(struct shm_pool *pool)
+{
+ munmap(pool->data, pool->size);
+ wl_shm_pool_destroy(pool->pool);
+ free(pool);
+}
+
+/* Start allocating from the beginning of the pool again */
+static void
+shm_pool_reset(struct shm_pool *pool)
+{
+ pool->used = 0;
+}
+
+static int
+data_length_for_shm_surface(struct rectangle *rect)
+{
+ int stride;
+
+ stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32,
+ rect->width);
+ return stride * rect->height;
+}
+
+static cairo_surface_t *
+display_create_shm_surface_from_pool(struct display *display,
+ struct rectangle *rectangle,
+ uint32_t flags, struct shm_pool *pool)
+{
+ struct shm_surface_data *data;
+ uint32_t format;
+ cairo_surface_t *surface;
+ cairo_format_t cairo_format;
+ int stride, length, offset;
+ void *map;
+
+ data = malloc(sizeof *data);
+ if (data == NULL)
+ return NULL;
+
+ if (flags & SURFACE_HINT_RGB565 && display->has_rgb565)
+ cairo_format = CAIRO_FORMAT_RGB16_565;
+ else
+ cairo_format = CAIRO_FORMAT_ARGB32;
+
+ stride = cairo_format_stride_for_width (cairo_format, rectangle->width);
+ length = stride * rectangle->height;
+ data->pool = NULL;
+ map = shm_pool_allocate(pool, length, &offset);
+
+ if (!map) {
+ free(data);
+ return NULL;
+ }
+
+ surface = cairo_image_surface_create_for_data (map,
+ cairo_format,
+ rectangle->width,
+ rectangle->height,
+ stride);
+
+ cairo_surface_set_user_data(surface, &shm_surface_data_key,
+ data, shm_surface_data_destroy);
+
+ if (flags & SURFACE_HINT_RGB565 && display->has_rgb565)
+ format = WL_SHM_FORMAT_RGB565;
+ else {
+ if (flags & SURFACE_OPAQUE)
+ format = WL_SHM_FORMAT_XRGB8888;
+ else
+ format = WL_SHM_FORMAT_ARGB8888;
+ }
+
+ data->buffer = wl_shm_pool_create_buffer(pool->pool, offset,
+ rectangle->width,
+ rectangle->height,
+ stride, format);
+
+ return surface;
+}
+
+static cairo_surface_t *
+display_create_shm_surface(struct display *display,
+ struct rectangle *rectangle, uint32_t flags,
+ struct shm_pool *alternate_pool,
+ struct shm_surface_data **data_ret)
+{
+ struct shm_surface_data *data;
+ struct shm_pool *pool;
+ cairo_surface_t *surface;
+
+ if (alternate_pool) {
+ shm_pool_reset(alternate_pool);
+ surface = display_create_shm_surface_from_pool(display,
+ rectangle,
+ flags,
+ alternate_pool);
+ if (surface) {
+ data = cairo_surface_get_user_data(surface,
+ &shm_surface_data_key);
+ goto out;
+ }
+ }
+
+ pool = shm_pool_create(display,
+ data_length_for_shm_surface(rectangle));
+ if (!pool)
+ return NULL;
+
+ surface =
+ display_create_shm_surface_from_pool(display, rectangle,
+ flags, pool);
+
+ if (!surface) {
+ shm_pool_destroy(pool);
+ return NULL;
+ }
+
+ /* make sure we destroy the pool when the surface is destroyed */
+ data = cairo_surface_get_user_data(surface, &shm_surface_data_key);
+ data->pool = pool;
+
+out:
+ if (data_ret)
+ *data_ret = data;
+
+ return surface;
+}
+
+static int
+check_size(struct rectangle *rect)
+{
+ if (rect->width && rect->height)
+ return 0;
+
+ fprintf(stderr, "tried to create surface of "
+ "width: %d, height: %d\n", rect->width, rect->height);
+ return -1;
+}
+
+cairo_surface_t *
+display_create_surface(struct display *display,
+ struct wl_surface *surface,
+ struct rectangle *rectangle,
+ uint32_t flags)
+{
+ if (check_size(rectangle) < 0)
+ return NULL;
+
+ assert(flags & SURFACE_SHM);
+ return display_create_shm_surface(display, rectangle, flags,
+ NULL, NULL);
+}
+
+struct shm_surface_leaf {
+ cairo_surface_t *cairo_surface;
+ /* 'data' is automatically destroyed, when 'cairo_surface' is */
+ struct shm_surface_data *data;
+
+ struct shm_pool *resize_pool;
+ int busy;
+};
+
+static void
+shm_surface_leaf_release(struct shm_surface_leaf *leaf)
+{
+ if (leaf->cairo_surface)
+ cairo_surface_destroy(leaf->cairo_surface);
+ /* leaf->data already destroyed via cairo private */
+
+ if (leaf->resize_pool)
+ shm_pool_destroy(leaf->resize_pool);
+
+ memset(leaf, 0, sizeof *leaf);
+}
+
+#define MAX_LEAVES 3
+
+struct shm_surface {
+ struct toysurface base;
+ struct display *display;
+ struct wl_surface *surface;
+ uint32_t flags;
+ int dx, dy;
+
+ struct shm_surface_leaf leaf[MAX_LEAVES];
+ struct shm_surface_leaf *current;
+};
+
+static struct shm_surface *
+to_shm_surface(struct toysurface *base)
+{
+ return container_of(base, struct shm_surface, base);
+}
+
+static void
+shm_surface_buffer_state_debug(struct shm_surface *surface, const char *msg)
+{
+#ifdef DEBUG
+ struct shm_surface_leaf *leaf;
+ char bufs[MAX_LEAVES + 1];
+ int i;
+
+ for (i = 0; i < MAX_LEAVES; i++) {
+ leaf = &surface->leaf[i];
+
+ if (leaf->busy)
+ bufs[i] = 'b';
+ else if (leaf->cairo_surface)
+ bufs[i] = 'a';
+ else
+ bufs[i] = ' ';
+ }
+
+ bufs[MAX_LEAVES] = '\0';
+ DBG_OBJ(surface->surface, "%s, leaves [%s]\n", msg, bufs);
+#endif
+}
+
+static void
+shm_surface_buffer_release(void *data, struct wl_buffer *buffer)
+{
+ struct shm_surface *surface = data;
+ struct shm_surface_leaf *leaf;
+ int i;
+ int free_found;
+
+ shm_surface_buffer_state_debug(surface, "buffer_release before");
+
+ for (i = 0; i < MAX_LEAVES; i++) {
+ leaf = &surface->leaf[i];
+ if (leaf->data && leaf->data->buffer == buffer) {
+ leaf->busy = 0;
+ break;
+ }
+ }
+ assert(i < MAX_LEAVES && "unknown buffer released");
+
+ /* Leave one free leaf with storage, release others */
+ free_found = 0;
+ for (i = 0; i < MAX_LEAVES; i++) {
+ leaf = &surface->leaf[i];
+
+ if (!leaf->cairo_surface || leaf->busy)
+ continue;
+
+ if (!free_found)
+ free_found = 1;
+ else
+ shm_surface_leaf_release(leaf);
+ }
+
+ shm_surface_buffer_state_debug(surface, "buffer_release after");
+}
+
+static const struct wl_buffer_listener shm_surface_buffer_listener = {
+ shm_surface_buffer_release
+};
+
+static cairo_surface_t *
+shm_surface_prepare(struct toysurface *base, int dx, int dy,
+ int32_t width, int32_t height, uint32_t flags,
+ enum wl_output_transform buffer_transform, int32_t buffer_scale)
+{
+ int resize_hint = !!(flags & SURFACE_HINT_RESIZE);
+ struct shm_surface *surface = to_shm_surface(base);
+ struct rectangle rect = { 0};
+ struct shm_surface_leaf *leaf = NULL;
+ int i;
+
+ surface->dx = dx;
+ surface->dy = dy;
+
+ /* pick a free buffer, preferrably one that already has storage */
+ for (i = 0; i < MAX_LEAVES; i++) {
+ if (surface->leaf[i].busy)
+ continue;
+
+ if (!leaf || surface->leaf[i].cairo_surface)
+ leaf = &surface->leaf[i];
+ }
+ DBG_OBJ(surface->surface, "pick leaf %d\n",
+ (int)(leaf - &surface->leaf[0]));
+
+ if (!leaf) {
+ fprintf(stderr, "%s: all buffers are held by the server.\n",
+ __func__);
+ exit(1);
+ return NULL;
+ }
+
+ if (!resize_hint && leaf->resize_pool) {
+ cairo_surface_destroy(leaf->cairo_surface);
+ leaf->cairo_surface = NULL;
+ shm_pool_destroy(leaf->resize_pool);
+ leaf->resize_pool = NULL;
+ }
+
+ surface_to_buffer_size (buffer_transform, buffer_scale, &width, &height);
+
+ if (leaf->cairo_surface &&
+ cairo_image_surface_get_width(leaf->cairo_surface) == width &&
+ cairo_image_surface_get_height(leaf->cairo_surface) == height)
+ goto out;
+
+ if (leaf->cairo_surface)
+ cairo_surface_destroy(leaf->cairo_surface);
+
+#ifdef USE_RESIZE_POOL
+ if (resize_hint && !leaf->resize_pool) {
+ /* Create a big pool to allocate from, while continuously
+ * resizing. Mmapping a new pool in the server
+ * is relatively expensive, so reusing a pool performs
+ * better, but may temporarily reserve unneeded memory.
+ */
+ /* We should probably base this number on the output size. */
+ leaf->resize_pool = shm_pool_create(surface->display,
+ 6 * 1024 * 1024);
+ }
+#endif
+
+ rect.width = width;
+ rect.height = height;
+
+ leaf->cairo_surface =
+ display_create_shm_surface(surface->display, &rect,
+ surface->flags,
+ leaf->resize_pool,
+ &leaf->data);
+ wl_buffer_add_listener(leaf->data->buffer,
+ &shm_surface_buffer_listener, surface);
+
+out:
+ surface->current = leaf;
+
+ return cairo_surface_reference(leaf->cairo_surface);
+}
+
+static void
+shm_surface_swap(struct toysurface *base,
+ enum wl_output_transform buffer_transform, int32_t buffer_scale,
+ struct rectangle *server_allocation)
+{
+ struct shm_surface *surface = to_shm_surface(base);
+ struct shm_surface_leaf *leaf = surface->current;
+
+ server_allocation->width =
+ cairo_image_surface_get_width(leaf->cairo_surface);
+ server_allocation->height =
+ cairo_image_surface_get_height(leaf->cairo_surface);
+
+ buffer_to_surface_size (buffer_transform, buffer_scale,
+ &server_allocation->width,
+ &server_allocation->height);
+
+ wl_surface_attach(surface->surface, leaf->data->buffer,
+ surface->dx, surface->dy);
+ wl_surface_damage(surface->surface, 0, 0,
+ server_allocation->width, server_allocation->height);
+ wl_surface_commit(surface->surface);
+
+ DBG_OBJ(surface->surface, "leaf %d busy\n",
+ (int)(leaf - &surface->leaf[0]));
+
+ leaf->busy = 1;
+ surface->current = NULL;
+}
+
+static int
+shm_surface_acquire(struct toysurface *base, EGLContext ctx)
+{
+ return -1;
+}
+
+static void
+shm_surface_release(struct toysurface *base)
+{
+}
+
+static void
+shm_surface_destroy(struct toysurface *base)
+{
+ struct shm_surface *surface = to_shm_surface(base);
+ int i;
+
+ for (i = 0; i < MAX_LEAVES; i++)
+ shm_surface_leaf_release(&surface->leaf[i]);
+
+ free(surface);
+}
+
+static struct toysurface *
+shm_surface_create(struct display *display, struct wl_surface *wl_surface,
+ uint32_t flags, struct rectangle *rectangle)
+{
+ struct shm_surface *surface;
+ DBG_OBJ(wl_surface, "\n");
+
+ surface = xmalloc(sizeof *surface);
+ memset(surface, 0, sizeof *surface);
+
+ if (!surface)
+ return NULL;
+
+ surface->base.prepare = shm_surface_prepare;
+ surface->base.swap = shm_surface_swap;
+ surface->base.acquire = shm_surface_acquire;
+ surface->base.release = shm_surface_release;
+ surface->base.destroy = shm_surface_destroy;
+
+ surface->display = display;
+ surface->surface = wl_surface;
+ surface->flags = flags;
+
+ return &surface->base;
+}
+
+/*
+ * The following correspondences between file names and cursors was copied
+ * from: https://bugs.kde.org/attachment.cgi?id=67313
+ */
+
+static const char *bottom_left_corners[] = {
+ "bottom_left_corner",
+ "sw-resize",
+ "size_bdiag"
+};
+
+static const char *bottom_right_corners[] = {
+ "bottom_right_corner",
+ "se-resize",
+ "size_fdiag"
+};
+
+static const char *bottom_sides[] = {
+ "bottom_side",
+ "s-resize",
+ "size_ver"
+};
+
+static const char *grabbings[] = {
+ "grabbing",
+ "closedhand",
+ "208530c400c041818281048008011002"
+};
+
+static const char *left_ptrs[] = {
+ "left_ptr",
+ "default",
+ "top_left_arrow",
+ "left-arrow"
+};
+
+static const char *left_sides[] = {
+ "left_side",
+ "w-resize",
+ "size_hor"
+};
+
+static const char *right_sides[] = {
+ "right_side",
+ "e-resize",
+ "size_hor"
+};
+
+static const char *top_left_corners[] = {
+ "top_left_corner",
+ "nw-resize",
+ "size_fdiag"
+};
+
+static const char *top_right_corners[] = {
+ "top_right_corner",
+ "ne-resize",
+ "size_bdiag"
+};
+
+static const char *top_sides[] = {
+ "top_side",
+ "n-resize",
+ "size_ver"
+};
+
+static const char *xterms[] = {
+ "xterm",
+ "ibeam",
+ "text"
+};
+
+static const char *hand1s[] = {
+ "hand1",
+ "pointer",
+ "pointing_hand",
+ "e29285e634086352946a0e7090d73106"
+};
+
+static const char *watches[] = {
+ "watch",
+ "wait",
+ "0426c94ea35c87780ff01dc239897213"
+};
+
+struct cursor_alternatives {
+ const char **names;
+ size_t count;
+};
+
+static const struct cursor_alternatives cursors[] = {
+ {bottom_left_corners, ARRAY_LENGTH(bottom_left_corners)},
+ {bottom_right_corners, ARRAY_LENGTH(bottom_right_corners)},
+ {bottom_sides, ARRAY_LENGTH(bottom_sides)},
+ {grabbings, ARRAY_LENGTH(grabbings)},
+ {left_ptrs, ARRAY_LENGTH(left_ptrs)},
+ {left_sides, ARRAY_LENGTH(left_sides)},
+ {right_sides, ARRAY_LENGTH(right_sides)},
+ {top_left_corners, ARRAY_LENGTH(top_left_corners)},
+ {top_right_corners, ARRAY_LENGTH(top_right_corners)},
+ {top_sides, ARRAY_LENGTH(top_sides)},
+ {xterms, ARRAY_LENGTH(xterms)},
+ {hand1s, ARRAY_LENGTH(hand1s)},
+ {watches, ARRAY_LENGTH(watches)},
+};
+
+static void
+create_cursors(struct display *display)
+{
+ struct weston_config *config;
+ struct weston_config_section *s;
+ int size;
+ char *theme = NULL;
+ unsigned int i, j;
+ struct wl_cursor *cursor;
+
+ config = weston_config_parse("weston.ini");
+ s = weston_config_get_section(config, "shell", NULL, NULL);
+ weston_config_section_get_string(s, "cursor-theme", &theme, NULL);
+ weston_config_section_get_int(s, "cursor-size", &size, 32);
+ weston_config_destroy(config);
+
+ display->cursor_theme = wl_cursor_theme_load(theme, size, display->shm);
+ free(theme);
+ display->cursors =
+ xmalloc(ARRAY_LENGTH(cursors) * sizeof display->cursors[0]);
+
+ for (i = 0; i < ARRAY_LENGTH(cursors); i++) {
+ cursor = NULL;
+ for (j = 0; !cursor && j < cursors[i].count; ++j)
+ cursor = wl_cursor_theme_get_cursor(
+ display->cursor_theme, cursors[i].names[j]);
+
+ if (!cursor)
+ fprintf(stderr, "could not load cursor '%s'\n",
+ cursors[i].names[0]);
+
+ display->cursors[i] = cursor;
+ }
+}
+
+static void
+destroy_cursors(struct display *display)
+{
+ wl_cursor_theme_destroy(display->cursor_theme);
+ free(display->cursors);
+}
+
+struct wl_cursor_image *
+display_get_pointer_image(struct display *display, int pointer)
+{
+ struct wl_cursor *cursor = display->cursors[pointer];
+
+ return cursor ? cursor->images[0] : NULL;
+}
+
+static void
+surface_flush(struct surface *surface)
+{
+ if (!surface->cairo_surface)
+ return;
+
+ if (surface->opaque_region) {
+ wl_surface_set_opaque_region(surface->surface,
+ surface->opaque_region);
+ wl_region_destroy(surface->opaque_region);
+ surface->opaque_region = NULL;
+ }
+
+ if (surface->input_region) {
+ wl_surface_set_input_region(surface->surface,
+ surface->input_region);
+ wl_region_destroy(surface->input_region);
+ surface->input_region = NULL;
+ }
+
+ surface->toysurface->swap(surface->toysurface,
+ surface->buffer_transform, surface->buffer_scale,
+ &surface->server_allocation);
+
+ cairo_surface_destroy(surface->cairo_surface);
+ surface->cairo_surface = NULL;
+}
+
+int
+window_has_focus(struct window *window)
+{
+ return window->focus_count > 0;
+}
+
+static void
+window_flush(struct window *window)
+{
+ struct surface *surface;
+
+ if (window->type == TYPE_NONE) {
+ window->type = TYPE_TOPLEVEL;
+ if (window->shell_surface)
+ wl_shell_surface_set_toplevel(window->shell_surface);
+ }
+
+ wl_list_for_each(surface, &window->subsurface_list, link) {
+ if (surface == window->main_surface)
+ continue;
+
+ surface_flush(surface);
+ }
+
+ surface_flush(window->main_surface);
+}
+
+struct display *
+window_get_display(struct window *window)
+{
+ return window->display;
+}
+
+static void
+surface_create_surface(struct surface *surface, int dx, int dy, uint32_t flags)
+{
+ struct display *display = surface->window->display;
+ struct rectangle allocation = surface->allocation;
+
+ if (!surface->toysurface && display->dpy &&
+ surface->buffer_type == WINDOW_BUFFER_TYPE_EGL_WINDOW) {
+ surface->toysurface =
+ egl_window_surface_create(display,
+ surface->surface,
+ flags,
+ &allocation);
+ }
+
+ if (!surface->toysurface)
+ surface->toysurface = shm_surface_create(display,
+ surface->surface,
+ flags, &allocation);
+
+ surface->cairo_surface = surface->toysurface->prepare(
+ surface->toysurface, dx, dy,
+ allocation.width, allocation.height, flags,
+ surface->buffer_transform, surface->buffer_scale);
+}
+
+static void
+window_create_main_surface(struct window *window)
+{
+ struct surface *surface = window->main_surface;
+ uint32_t flags = 0;
+ int dx = 0;
+ int dy = 0;
+
+ if (window->resizing)
+ flags |= SURFACE_HINT_RESIZE;
+
+ if (window->preferred_format == WINDOW_PREFERRED_FORMAT_RGB565)
+ flags |= SURFACE_HINT_RGB565;
+
+ if (window->resize_edges & WINDOW_RESIZING_LEFT)
+ dx = surface->server_allocation.width -
+ surface->allocation.width;
+
+ if (window->resize_edges & WINDOW_RESIZING_TOP)
+ dy = surface->server_allocation.height -
+ surface->allocation.height;
+
+ window->resize_edges = 0;
+
+ surface_create_surface(surface, dx, dy, flags);
+}
+
+int
+window_get_buffer_transform(struct window *window)
+{
+ return window->main_surface->buffer_transform;
+}
+
+void
+window_set_buffer_transform(struct window *window,
+ enum wl_output_transform transform)
+{
+ window->main_surface->buffer_transform = transform;
+ wl_surface_set_buffer_transform(window->main_surface->surface,
+ transform);
+}
+
+void
+window_set_buffer_scale(struct window *window,
+ int32_t scale)
+{
+ window->main_surface->buffer_scale = scale;
+ wl_surface_set_buffer_scale(window->main_surface->surface,
+ scale);
+}
+
+uint32_t
+window_get_buffer_scale(struct window *window)
+{
+ return window->main_surface->buffer_scale;
+}
+
+uint32_t
+window_get_output_scale(struct window *window)
+{
+ struct window_output *window_output;
+ struct window_output *window_output_tmp;
+ int scale = 1;
+
+ wl_list_for_each_safe(window_output, window_output_tmp,
+ &window->window_output_list, link) {
+ if (window_output->output->scale > scale)
+ scale = window_output->output->scale;
+ }
+
+ return scale;
+}
+
+static void frame_destroy(struct frame *frame);
+
+static void
+surface_destroy(struct surface *surface)
+{
+ if (surface->frame_cb)
+ wl_callback_destroy(surface->frame_cb);
+
+ if (surface->input_region)
+ wl_region_destroy(surface->input_region);
+
+ if (surface->opaque_region)
+ wl_region_destroy(surface->opaque_region);
+
+ if (surface->subsurface)
+ wl_subsurface_destroy(surface->subsurface);
+
+ wl_surface_destroy(surface->surface);
+
+ if (surface->toysurface)
+ surface->toysurface->destroy(surface->toysurface);
+
+ wl_list_remove(&surface->link);
+ free(surface);
+}
+
+void
+window_destroy(struct window *window)
+{
+ struct display *display = window->display;
+ struct input *input;
+ struct window_output *window_output;
+ struct window_output *window_output_tmp;
+
+ wl_list_remove(&window->redraw_task.link);
+
+ wl_list_for_each(input, &display->input_list, link) {
+ if (input->touch_focus == window)
+ input->touch_focus = NULL;
+ if (input->pointer_focus == window)
+ input->pointer_focus = NULL;
+ if (input->keyboard_focus == window)
+ input->keyboard_focus = NULL;
+ if (input->focus_widget &&
+ input->focus_widget->window == window)
+ input->focus_widget = NULL;
+ }
+
+ wl_list_for_each_safe(window_output, window_output_tmp,
+ &window->window_output_list, link) {
+ free (window_output);
+ }
+
+ if (window->frame)
+ frame_destroy(window->frame);
+
+ if (window->shell_surface)
+ wl_shell_surface_destroy(window->shell_surface);
+
+ surface_destroy(window->main_surface);
+
+ wl_list_remove(&window->link);
+
+ free(window->title);
+ free(window);
+}
+
+static struct widget *
+widget_find_widget(struct widget *widget, int32_t x, int32_t y)
+{
+ struct widget *child, *target;
+
+ wl_list_for_each(child, &widget->child_list, link) {
+ target = widget_find_widget(child, x, y);
+ if (target)
+ return target;
+ }
+
+ if (widget->allocation.x <= x &&
+ x < widget->allocation.x + widget->allocation.width &&
+ widget->allocation.y <= y &&
+ y < widget->allocation.y + widget->allocation.height) {
+ return widget;
+ }
+
+ return NULL;
+}
+
+static struct widget *
+window_find_widget(struct window *window, int32_t x, int32_t y)
+{
+ struct surface *surface;
+ struct widget *widget;
+
+ wl_list_for_each(surface, &window->subsurface_list, link) {
+ widget = widget_find_widget(surface->widget, x, y);
+ if (widget)
+ return widget;
+ }
+
+ return NULL;
+}
+
+static struct widget *
+widget_create(struct window *window, struct surface *surface, void *data)
+{
+ struct widget *widget;
+
+ widget = xzalloc(sizeof *widget);
+ widget->window = window;
+ widget->surface = surface;
+ widget->user_data = data;
+ widget->allocation = surface->allocation;
+ wl_list_init(&widget->child_list);
+ widget->opaque = 0;
+ widget->tooltip = NULL;
+ widget->tooltip_count = 0;
+ widget->default_cursor = CURSOR_LEFT_PTR;
+
+ return widget;
+}
+
+struct widget *
+window_add_widget(struct window *window, void *data)
+{
+ struct widget *widget;
+
+ widget = widget_create(window, window->main_surface, data);
+ wl_list_init(&widget->link);
+ window->main_surface->widget = widget;
+
+ return widget;
+}
+
+struct widget *
+widget_add_widget(struct widget *parent, void *data)
+{
+ struct widget *widget;
+
+ widget = widget_create(parent->window, parent->surface, data);
+ wl_list_insert(parent->child_list.prev, &widget->link);
+
+ return widget;
+}
+
+void
+widget_destroy(struct widget *widget)
+{
+ struct display *display = widget->window->display;
+ struct surface *surface = widget->surface;
+ struct input *input;
+
+ /* Destroy the sub-surface along with the root widget */
+ if (surface->widget == widget && surface->subsurface)
+ surface_destroy(widget->surface);
+
+ if (widget->tooltip) {
+ free(widget->tooltip);
+ widget->tooltip = NULL;
+ }
+
+ wl_list_for_each(input, &display->input_list, link) {
+ if (input->focus_widget == widget)
+ input->focus_widget = NULL;
+ }
+
+ wl_list_remove(&widget->link);
+ free(widget);
+}
+
+void
+widget_set_default_cursor(struct widget *widget, int cursor)
+{
+ widget->default_cursor = cursor;
+}
+
+void
+widget_get_allocation(struct widget *widget, struct rectangle *allocation)
+{
+ *allocation = widget->allocation;
+}
+
+void
+widget_set_size(struct widget *widget, int32_t width, int32_t height)
+{
+ widget->allocation.width = width;
+ widget->allocation.height = height;
+}
+
+void
+widget_set_allocation(struct widget *widget,
+ int32_t x, int32_t y, int32_t width, int32_t height)
+{
+ widget->allocation.x = x;
+ widget->allocation.y = y;
+ widget_set_size(widget, width, height);
+}
+
+void
+widget_set_transparent(struct widget *widget, int transparent)
+{
+ widget->opaque = !transparent;
+}
+
+void *
+widget_get_user_data(struct widget *widget)
+{
+ return widget->user_data;
+}
+
+static cairo_surface_t *
+widget_get_cairo_surface(struct widget *widget)
+{
+ struct surface *surface = widget->surface;
+ struct window *window = widget->window;
+
+ if (!surface->cairo_surface) {
+ if (surface == window->main_surface)
+ window_create_main_surface(window);
+ else
+ surface_create_surface(surface, 0, 0, 0);
+ }
+
+ return surface->cairo_surface;
+}
+
+static void
+widget_cairo_update_transform(struct widget *widget, cairo_t *cr)
+{
+ struct surface *surface = widget->surface;
+ double angle;
+ cairo_matrix_t m;
+ enum wl_output_transform transform;
+ int surface_width, surface_height;
+ int translate_x, translate_y;
+ int32_t scale;
+
+ surface_width = surface->allocation.width;
+ surface_height = surface->allocation.height;
+
+ transform = surface->buffer_transform;
+ scale = surface->buffer_scale;
+
+ switch (transform) {
+ case WL_OUTPUT_TRANSFORM_FLIPPED:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+ cairo_matrix_init(&m, -1, 0, 0, 1, 0, 0);
+ break;
+ default:
+ cairo_matrix_init_identity(&m);
+ break;
+ }
+
+ switch (transform) {
+ case WL_OUTPUT_TRANSFORM_NORMAL:
+ default:
+ angle = 0;
+ translate_x = 0;
+ translate_y = 0;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED:
+ angle = 0;
+ translate_x = surface_width;
+ translate_y = 0;
+ break;
+ case WL_OUTPUT_TRANSFORM_90:
+ angle = M_PI_2;
+ translate_x = surface_height;
+ translate_y = 0;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+ angle = M_PI_2;
+ translate_x = surface_height;
+ translate_y = surface_width;
+ break;
+ case WL_OUTPUT_TRANSFORM_180:
+ angle = M_PI;
+ translate_x = surface_width;
+ translate_y = surface_height;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+ angle = M_PI;
+ translate_x = 0;
+ translate_y = surface_height;
+ break;
+ case WL_OUTPUT_TRANSFORM_270:
+ angle = M_PI + M_PI_2;
+ translate_x = 0;
+ translate_y = surface_width;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+ angle = M_PI + M_PI_2;
+ translate_x = 0;
+ translate_y = 0;
+ break;
+ }
+
+ cairo_scale(cr, scale, scale);
+ cairo_translate(cr, translate_x, translate_y);
+ cairo_rotate(cr, angle);
+ cairo_transform(cr, &m);
+}
+
+cairo_t *
+widget_cairo_create(struct widget *widget)
+{
+ struct surface *surface = widget->surface;
+ cairo_surface_t *cairo_surface;
+ cairo_t *cr;
+
+ cairo_surface = widget_get_cairo_surface(widget);
+ cr = cairo_create(cairo_surface);
+
+ widget_cairo_update_transform(widget, cr);
+
+ cairo_translate(cr, -surface->allocation.x, -surface->allocation.y);
+
+ return cr;
+}
+
+struct wl_surface *
+widget_get_wl_surface(struct widget *widget)
+{
+ return widget->surface->surface;
+}
+
+uint32_t
+widget_get_last_time(struct widget *widget)
+{
+ return widget->surface->last_time;
+}
+
+void
+widget_input_region_add(struct widget *widget, const struct rectangle *rect)
+{
+ struct wl_compositor *comp = widget->window->display->compositor;
+ struct surface *surface = widget->surface;
+
+ if (!surface->input_region)
+ surface->input_region = wl_compositor_create_region(comp);
+
+ if (rect) {
+ wl_region_add(surface->input_region,
+ rect->x, rect->y, rect->width, rect->height);
+ }
+}
+
+void
+widget_set_resize_handler(struct widget *widget,
+ widget_resize_handler_t handler)
+{
+ widget->resize_handler = handler;
+}
+
+void
+widget_set_redraw_handler(struct widget *widget,
+ widget_redraw_handler_t handler)
+{
+ widget->redraw_handler = handler;
+}
+
+void
+widget_set_enter_handler(struct widget *widget, widget_enter_handler_t handler)
+{
+ widget->enter_handler = handler;
+}
+
+void
+widget_set_leave_handler(struct widget *widget, widget_leave_handler_t handler)
+{
+ widget->leave_handler = handler;
+}
+
+void
+widget_set_motion_handler(struct widget *widget,
+ widget_motion_handler_t handler)
+{
+ widget->motion_handler = handler;
+}
+
+void
+widget_set_button_handler(struct widget *widget,
+ widget_button_handler_t handler)
+{
+ widget->button_handler = handler;
+}
+
+void
+widget_set_touch_up_handler(struct widget *widget,
+ widget_touch_up_handler_t handler)
+{
+ widget->touch_up_handler = handler;
+}
+
+void
+widget_set_touch_down_handler(struct widget *widget,
+ widget_touch_down_handler_t handler)
+{
+ widget->touch_down_handler = handler;
+}
+
+void
+widget_set_touch_motion_handler(struct widget *widget,
+ widget_touch_motion_handler_t handler)
+{
+ widget->touch_motion_handler = handler;
+}
+
+void
+widget_set_touch_frame_handler(struct widget *widget,
+ widget_touch_frame_handler_t handler)
+{
+ widget->touch_frame_handler = handler;
+}
+
+void
+widget_set_touch_cancel_handler(struct widget *widget,
+ widget_touch_cancel_handler_t handler)
+{
+ widget->touch_cancel_handler = handler;
+}
+
+void
+widget_set_axis_handler(struct widget *widget,
+ widget_axis_handler_t handler)
+{
+ widget->axis_handler = handler;
+}
+
+static void
+window_schedule_redraw_task(struct window *window);
+
+void
+widget_schedule_redraw(struct widget *widget)
+{
+ DBG_OBJ(widget->surface->surface, "widget %p\n", widget);
+ widget->surface->redraw_needed = 1;
+ window_schedule_redraw_task(widget->window);
+}
+
+cairo_surface_t *
+window_get_surface(struct window *window)
+{
+ cairo_surface_t *cairo_surface;
+
+ cairo_surface = widget_get_cairo_surface(window->main_surface->widget);
+
+ return cairo_surface_reference(cairo_surface);
+}
+
+struct wl_surface *
+window_get_wl_surface(struct window *window)
+{
+ return window->main_surface->surface;
+}
+
+struct wl_shell_surface *
+window_get_wl_shell_surface(struct window *window)
+{
+ return window->shell_surface;
+}
+
+static void
+tooltip_redraw_handler(struct widget *widget, void *data)
+{
+ cairo_t *cr;
+ const int32_t r = 3;
+ struct tooltip *tooltip = data;
+ int32_t width, height;
+
+ cr = widget_cairo_create(widget);
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.0);
+ cairo_paint(cr);
+
+ width = widget->allocation.width;
+ height = widget->allocation.height;
+ rounded_rect(cr, 0, 0, width, height, r);
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+ cairo_set_source_rgba(cr, 0.0, 0.0, 0.4, 0.8);
+ cairo_fill(cr);
+
+ cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
+ cairo_move_to(cr, 10, 16);
+ cairo_show_text(cr, tooltip->entry);
+ cairo_destroy(cr);
+}
+
+static cairo_text_extents_t
+get_text_extents(struct tooltip *tooltip)
+{
+ cairo_t *cr;
+ cairo_text_extents_t extents;
+
+ /* Use the dummy_surface because tooltip's surface was not
+ * created yet, and parent does not have a valid surface
+ * outside repaint, either.
+ */
+ cr = cairo_create(tooltip->window->display->dummy_surface);
+ cairo_text_extents(cr, tooltip->entry, &extents);
+ cairo_destroy(cr);
+
+ return extents;
+}
+
+static int
+window_create_tooltip(struct tooltip *tooltip)
+{
+ struct widget *parent = tooltip->parent;
+ struct display *display = parent->window->display;
+ struct window *window;
+ const int offset_y = 27;
+ const int margin = 3;
+ cairo_text_extents_t extents;
+
+ if (tooltip->widget)
+ return 0;
+
+ window = window_create_transient(display, parent->window, tooltip->x,
+ tooltip->y + offset_y,
+ WL_SHELL_SURFACE_TRANSIENT_INACTIVE);
+ if (!window)
+ return -1;
+
+ tooltip->window = window;
+ tooltip->widget = window_add_widget(tooltip->window, tooltip);
+
+ extents = get_text_extents(tooltip);
+ widget_set_redraw_handler(tooltip->widget, tooltip_redraw_handler);
+ window_schedule_resize(window, extents.width + 20, 20 + margin * 2);
+
+ return 0;
+}
+
+void
+widget_destroy_tooltip(struct widget *parent)
+{
+ struct tooltip *tooltip = parent->tooltip;
+
+ parent->tooltip_count = 0;
+ if (!tooltip)
+ return;
+
+ if (tooltip->widget) {
+ widget_destroy(tooltip->widget);
+ window_destroy(tooltip->window);
+ tooltip->widget = NULL;
+ tooltip->window = NULL;
+ }
+
+ close(tooltip->tooltip_fd);
+ free(tooltip->entry);
+ free(tooltip);
+ parent->tooltip = NULL;
+}
+
+static void
+tooltip_func(struct task *task, uint32_t events)
+{
+ struct tooltip *tooltip =
+ container_of(task, struct tooltip, tooltip_task);
+ uint64_t exp;
+
+ if (read(tooltip->tooltip_fd, &exp, sizeof (uint64_t)) != sizeof (uint64_t))
+ abort();
+ window_create_tooltip(tooltip);
+}
+
+#define TOOLTIP_TIMEOUT 500
+static int
+tooltip_timer_reset(struct tooltip *tooltip)
+{
+ struct itimerspec its;
+
+ its.it_interval.tv_sec = 0;
+ its.it_interval.tv_nsec = 0;
+ its.it_value.tv_sec = TOOLTIP_TIMEOUT / 1000;
+ its.it_value.tv_nsec = (TOOLTIP_TIMEOUT % 1000) * 1000 * 1000;
+ if (timerfd_settime(tooltip->tooltip_fd, 0, &its, NULL) < 0) {
+ fprintf(stderr, "could not set timerfd\n: %m");
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+widget_set_tooltip(struct widget *parent, char *entry, float x, float y)
+{
+ struct tooltip *tooltip = parent->tooltip;
+
+ parent->tooltip_count++;
+ if (tooltip) {
+ tooltip->x = x;
+ tooltip->y = y;
+ tooltip_timer_reset(tooltip);
+ return 0;
+ }
+
+ /* the handler might be triggered too fast via input device motion, so
+ * we need this check here to make sure tooltip is fully initialized */
+ if (parent->tooltip_count > 1)
+ return 0;
+
+ tooltip = malloc(sizeof *tooltip);
+ if (!tooltip)
+ return -1;
+
+ parent->tooltip = tooltip;
+ tooltip->parent = parent;
+ tooltip->widget = NULL;
+ tooltip->window = NULL;
+ tooltip->x = x;
+ tooltip->y = y;
+ tooltip->entry = strdup(entry);
+ tooltip->tooltip_fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
+ if (tooltip->tooltip_fd < 0) {
+ fprintf(stderr, "could not create timerfd\n: %m");
+ return -1;
+ }
+
+ tooltip->tooltip_task.run = tooltip_func;
+ display_watch_fd(parent->window->display, tooltip->tooltip_fd,
+ EPOLLIN, &tooltip->tooltip_task);
+ tooltip_timer_reset(tooltip);
+
+ return 0;
+}
+
+static void
+workspace_manager_state(void *data,
+ struct workspace_manager *workspace_manager,
+ uint32_t current,
+ uint32_t count)
+{
+ struct display *display = data;
+
+ display->workspace = current;
+ display->workspace_count = count;
+}
+
+static const struct workspace_manager_listener workspace_manager_listener = {
+ workspace_manager_state
+};
+
+static void
+frame_resize_handler(struct widget *widget,
+ int32_t width, int32_t height, void *data)
+{
+ struct frame *frame = data;
+ struct widget *child = frame->child;
+ struct rectangle allocation;
+ struct display *display = widget->window->display;
+ struct surface *surface = widget->surface;
+ struct frame_button * button;
+ struct theme *t = display->theme;
+ int x_l, x_r, y, w, h;
+ int decoration_width, decoration_height;
+ int opaque_margin, shadow_margin;
+
+ switch (widget->window->type) {
+ case TYPE_FULLSCREEN:
+ decoration_width = 0;
+ decoration_height = 0;
+
+ allocation.x = 0;
+ allocation.y = 0;
+ allocation.width = width;
+ allocation.height = height;
+ opaque_margin = 0;
+
+ wl_list_for_each(button, &frame->buttons_list, link)
+ button->widget->opaque = 1;
+ break;
+ case TYPE_MAXIMIZED:
+ decoration_width = t->width * 2;
+ decoration_height = t->width + t->titlebar_height;
+
+ allocation.x = t->width;
+ allocation.y = t->titlebar_height;
+ allocation.width = width - decoration_width;
+ allocation.height = height - decoration_height;
+
+ opaque_margin = 0;
+
+ wl_list_for_each(button, &frame->buttons_list, link)
+ button->widget->opaque = 0;
+ break;
+ default:
+ decoration_width = (t->width + t->margin) * 2;
+ decoration_height = t->width +
+ t->titlebar_height + t->margin * 2;
+
+ allocation.x = t->width + t->margin;
+ allocation.y = t->titlebar_height + t->margin;
+ allocation.width = width - decoration_width;
+ allocation.height = height - decoration_height;
+
+ opaque_margin = t->margin + t->frame_radius;
+
+ wl_list_for_each(button, &frame->buttons_list, link)
+ button->widget->opaque = 0;
+ break;
+ }
+
+ widget_set_allocation(child, allocation.x, allocation.y,
+ allocation.width, allocation.height);
+
+ if (child->resize_handler)
+ child->resize_handler(child,
+ allocation.width,
+ allocation.height,
+ child->user_data);
+
+ width = child->allocation.width + decoration_width;
+ height = child->allocation.height + decoration_height;
+
+ shadow_margin = widget->window->type == TYPE_MAXIMIZED ? 0 : t->margin;
+
+ surface->input_region =
+ wl_compositor_create_region(display->compositor);
+ if (widget->window->type != TYPE_FULLSCREEN) {
+ wl_region_add(surface->input_region,
+ shadow_margin, shadow_margin,
+ width - 2 * shadow_margin,
+ height - 2 * shadow_margin);
+ } else {
+ wl_region_add(surface->input_region, 0, 0, width, height);
+ }
+
+ widget_set_allocation(widget, 0, 0, width, height);
+
+ if (child->opaque)
+ wl_region_add(surface->opaque_region,
+ opaque_margin, opaque_margin,
+ widget->allocation.width - 2 * opaque_margin,
+ widget->allocation.height - 2 * opaque_margin);
+
+ /* frame internal buttons */
+ x_r = frame->widget->allocation.width - t->width - shadow_margin;
+ x_l = t->width + shadow_margin;
+ y = t->width + shadow_margin;
+ wl_list_for_each(button, &frame->buttons_list, link) {
+ const int button_padding = 4;
+ w = cairo_image_surface_get_width(button->icon);
+ h = cairo_image_surface_get_height(button->icon);
+
+ if (button->decoration == FRAME_BUTTON_FANCY)
+ w += 10;
+
+ if (button->align == FRAME_BUTTON_LEFT) {
+ widget_set_allocation(button->widget,
+ x_l, y , w + 1, h + 1);
+ x_l += w;
+ x_l += button_padding;
+ } else {
+ x_r -= w;
+ widget_set_allocation(button->widget,
+ x_r, y , w + 1, h + 1);
+ x_r -= button_padding;
+ }
+ }
+}
+
+static int
+frame_button_enter_handler(struct widget *widget,
+ struct input *input, float x, float y, void *data)
+{
+ struct frame_button *frame_button = data;
+
+ widget_schedule_redraw(frame_button->widget);
+ frame_button->state = FRAME_BUTTON_OVER;
+
+ return CURSOR_LEFT_PTR;
+}
+
+static void
+frame_button_leave_handler(struct widget *widget, struct input *input, void *data)
+{
+ struct frame_button *frame_button = data;
+
+ widget_schedule_redraw(frame_button->widget);
+ frame_button->state = FRAME_BUTTON_DEFAULT;
+}
+
+static void
+frame_button_button_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ uint32_t button,
+ enum wl_pointer_button_state state, void *data)
+{
+ struct frame_button *frame_button = data;
+ struct window *window = widget->window;
+ int was_pressed = (frame_button->state == FRAME_BUTTON_ACTIVE);
+
+ if (button != BTN_LEFT)
+ return;
+
+ switch (state) {
+ case WL_POINTER_BUTTON_STATE_PRESSED:
+ frame_button->state = FRAME_BUTTON_ACTIVE;
+ widget_schedule_redraw(frame_button->widget);
+
+ if (frame_button->type == FRAME_BUTTON_ICON)
+ window_show_frame_menu(window, input, time);
+ return;
+ case WL_POINTER_BUTTON_STATE_RELEASED:
+ frame_button->state = FRAME_BUTTON_DEFAULT;
+ widget_schedule_redraw(frame_button->widget);
+ break;
+ }
+
+ if (!was_pressed)
+ return;
+
+ switch (frame_button->type) {
+ case FRAME_BUTTON_CLOSE:
+ if (window->close_handler)
+ window->close_handler(window->parent,
+ window->user_data);
+ else
+ display_exit(window->display);
+ break;
+ case FRAME_BUTTON_MINIMIZE:
+ fprintf(stderr,"Minimize stub\n");
+ break;
+ case FRAME_BUTTON_MAXIMIZE:
+ window_set_maximized(window, window->type != TYPE_MAXIMIZED);
+ break;
+ default:
+ /* Unknown operation */
+ break;
+ }
+}
+
+static void
+frame_button_touch_down_handler(struct widget *widget, struct input *input,
+ uint32_t serial, uint32_t time, int32_t id,
+ float x, float y, void *data)
+{
+ struct frame_button *frame_button = data;
+ struct window *window = widget->window;
+
+ switch (frame_button->type) {
+ case FRAME_BUTTON_CLOSE:
+ if (window->close_handler)
+ window->close_handler(window->parent,
+ window->user_data);
+ else
+ display_exit(window->display);
+ break;
+ case FRAME_BUTTON_MINIMIZE:
+ fprintf(stderr,"Minimize stub\n");
+ break;
+ case FRAME_BUTTON_MAXIMIZE:
+ window_set_maximized(window, window->type != TYPE_MAXIMIZED);
+ break;
+ default:
+ /* Unknown operation */
+ break;
+ }
+}
+
+
+static int
+frame_button_motion_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ float x, float y, void *data)
+{
+ struct frame_button *frame_button = data;
+ enum frame_button_pointer previous_button_state = frame_button->state;
+
+ /* only track state for a pressed button */
+ if (input->grab != widget)
+ return CURSOR_LEFT_PTR;
+
+ if (x > widget->allocation.x &&
+ x < (widget->allocation.x + widget->allocation.width) &&
+ y > widget->allocation.y &&
+ y < (widget->allocation.y + widget->allocation.height)) {
+ frame_button->state = FRAME_BUTTON_ACTIVE;
+ } else {
+ frame_button->state = FRAME_BUTTON_DEFAULT;
+ }
+
+ if (frame_button->state != previous_button_state)
+ widget_schedule_redraw(frame_button->widget);
+
+ return CURSOR_LEFT_PTR;
+}
+
+static void
+frame_button_redraw_handler(struct widget *widget, void *data)
+{
+ struct frame_button *frame_button = data;
+ cairo_t *cr;
+ int width, height, x, y;
+
+ x = widget->allocation.x;
+ y = widget->allocation.y;
+ width = widget->allocation.width;
+ height = widget->allocation.height;
+
+ if (!width)
+ return;
+ if (!height)
+ return;
+ if (widget->opaque)
+ return;
+
+ cr = widget_cairo_create(widget);
+
+ if (frame_button->decoration == FRAME_BUTTON_FANCY) {
+ cairo_set_line_width(cr, 1);
+
+ cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
+ cairo_rectangle (cr, x, y, 25, 16);
+
+ cairo_stroke_preserve(cr);
+
+ switch (frame_button->state) {
+ case FRAME_BUTTON_DEFAULT:
+ cairo_set_source_rgb(cr, 0.88, 0.88, 0.88);
+ break;
+ case FRAME_BUTTON_OVER:
+ cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
+ break;
+ case FRAME_BUTTON_ACTIVE:
+ cairo_set_source_rgb(cr, 0.7, 0.7, 0.7);
+ break;
+ }
+
+ cairo_fill (cr);
+
+ x += 4;
+ }
+
+ cairo_set_source_surface(cr, frame_button->icon, x, y);
+ cairo_paint(cr);
+
+ cairo_destroy(cr);
+}
+
+static struct widget *
+frame_button_create(struct frame *frame, void *data, enum frame_button_action type,
+ enum frame_button_align align, enum frame_button_decoration style)
+{
+ struct frame_button *frame_button;
+ const char *icon = data;
+
+ frame_button = xzalloc (sizeof *frame_button);
+ frame_button->icon = cairo_image_surface_create_from_png(icon);
+ frame_button->widget = widget_add_widget(frame->widget, frame_button);
+ frame_button->frame = frame;
+ frame_button->type = type;
+ frame_button->align = align;
+ frame_button->decoration = style;
+
+ wl_list_insert(frame->buttons_list.prev, &frame_button->link);
+
+ widget_set_redraw_handler(frame_button->widget, frame_button_redraw_handler);
+ widget_set_enter_handler(frame_button->widget, frame_button_enter_handler);
+ widget_set_leave_handler(frame_button->widget, frame_button_leave_handler);
+ widget_set_touch_down_handler(frame_button->widget, frame_button_touch_down_handler);
+ widget_set_button_handler(frame_button->widget, frame_button_button_handler);
+ widget_set_motion_handler(frame_button->widget, frame_button_motion_handler);
+ return frame_button->widget;
+}
+
+static void
+frame_button_destroy(struct frame_button *frame_button)
+{
+ widget_destroy(frame_button->widget);
+ wl_list_remove(&frame_button->link);
+ cairo_surface_destroy(frame_button->icon);
+ free(frame_button);
+
+ return;
+}
+
+static void
+frame_redraw_handler(struct widget *widget, void *data)
+{
+ cairo_t *cr;
+ struct window *window = widget->window;
+ struct theme *t = window->display->theme;
+ uint32_t flags = 0;
+
+ if (window->type == TYPE_FULLSCREEN)
+ return;
+
+ cr = widget_cairo_create(widget);
+
+ if (window->focus_count)
+ flags |= THEME_FRAME_ACTIVE;
+ if (window->type == TYPE_MAXIMIZED)
+ flags |= THEME_FRAME_MAXIMIZED;
+ theme_render_frame(t, cr, widget->allocation.width,
+ widget->allocation.height, window->title, flags);
+
+ cairo_destroy(cr);
+}
+
+static int
+frame_get_pointer_image_for_location(struct frame *frame, struct input *input)
+{
+ struct theme *t = frame->widget->window->display->theme;
+ struct window *window = frame->widget->window;
+ int location;
+
+ if (window->type != TYPE_TOPLEVEL)
+ return CURSOR_LEFT_PTR;
+
+ location = theme_get_location(t, input->sx, input->sy,
+ frame->widget->allocation.width,
+ frame->widget->allocation.height,
+ window->type == TYPE_MAXIMIZED ?
+ THEME_FRAME_MAXIMIZED : 0);
+
+ switch (location) {
+ case THEME_LOCATION_RESIZING_TOP:
+ return CURSOR_TOP;
+ case THEME_LOCATION_RESIZING_BOTTOM:
+ return CURSOR_BOTTOM;
+ case THEME_LOCATION_RESIZING_LEFT:
+ return CURSOR_LEFT;
+ case THEME_LOCATION_RESIZING_RIGHT:
+ return CURSOR_RIGHT;
+ case THEME_LOCATION_RESIZING_TOP_LEFT:
+ return CURSOR_TOP_LEFT;
+ case THEME_LOCATION_RESIZING_TOP_RIGHT:
+ return CURSOR_TOP_RIGHT;
+ case THEME_LOCATION_RESIZING_BOTTOM_LEFT:
+ return CURSOR_BOTTOM_LEFT;
+ case THEME_LOCATION_RESIZING_BOTTOM_RIGHT:
+ return CURSOR_BOTTOM_RIGHT;
+ case THEME_LOCATION_EXTERIOR:
+ case THEME_LOCATION_TITLEBAR:
+ default:
+ return CURSOR_LEFT_PTR;
+ }
+}
+
+static void
+frame_menu_func(struct window *window, int index, void *data)
+{
+ struct display *display;
+
+ switch (index) {
+ case 0: /* close */
+ if (window->close_handler)
+ window->close_handler(window->parent,
+ window->user_data);
+ else
+ display_exit(window->display);
+ break;
+ case 1: /* move to workspace above */
+ display = window->display;
+ if (display->workspace > 0)
+ workspace_manager_move_surface(
+ display->workspace_manager,
+ window->main_surface->surface,
+ display->workspace - 1);
+ break;
+ case 2: /* move to workspace below */
+ display = window->display;
+ if (display->workspace < display->workspace_count - 1)
+ workspace_manager_move_surface(
+ display->workspace_manager,
+ window->main_surface->surface,
+ display->workspace + 1);
+ break;
+ case 3: /* fullscreen */
+ /* we don't have a way to get out of fullscreen for now */
+ if (window->fullscreen_handler)
+ window->fullscreen_handler(window, window->user_data);
+ break;
+ }
+}
+
+void
+window_show_frame_menu(struct window *window,
+ struct input *input, uint32_t time)
+{
+ int32_t x, y;
+ int count;
+
+ static const char *entries[] = {
+ "Close",
+ "Move to workspace above", "Move to workspace below",
+ "Fullscreen"
+ };
+
+ if (window->fullscreen_handler)
+ count = ARRAY_LENGTH(entries);
+ else
+ count = ARRAY_LENGTH(entries) - 1;
+
+ input_get_position(input, &x, &y);
+ window_show_menu(window->display, input, time, window,
+ x - 10, y - 10, frame_menu_func, entries, count);
+}
+
+static int
+frame_enter_handler(struct widget *widget,
+ struct input *input, float x, float y, void *data)
+{
+ return frame_get_pointer_image_for_location(data, input);
+}
+
+static int
+frame_motion_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ float x, float y, void *data)
+{
+ return frame_get_pointer_image_for_location(data, input);
+}
+
+static void
+frame_button_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ uint32_t button, enum wl_pointer_button_state state,
+ void *data)
+
+{
+ struct frame *frame = data;
+ struct window *window = widget->window;
+ struct display *display = window->display;
+ int location;
+
+ if (state != WL_POINTER_BUTTON_STATE_PRESSED)
+ return;
+
+ location = theme_get_location(display->theme, input->sx, input->sy,
+ frame->widget->allocation.width,
+ frame->widget->allocation.height,
+ window->type == TYPE_MAXIMIZED ?
+ THEME_FRAME_MAXIMIZED : 0);
+
+ if (window->display->shell && button == BTN_LEFT &&
+ window->type == TYPE_TOPLEVEL) {
+ switch (location) {
+ case THEME_LOCATION_TITLEBAR:
+ if (!window->shell_surface)
+ break;
+ input_ungrab(input);
+ wl_shell_surface_move(window->shell_surface,
+ input_get_seat(input),
+ display->serial);
+ break;
+ case THEME_LOCATION_RESIZING_TOP:
+ case THEME_LOCATION_RESIZING_BOTTOM:
+ case THEME_LOCATION_RESIZING_LEFT:
+ case THEME_LOCATION_RESIZING_RIGHT:
+ case THEME_LOCATION_RESIZING_TOP_LEFT:
+ case THEME_LOCATION_RESIZING_TOP_RIGHT:
+ case THEME_LOCATION_RESIZING_BOTTOM_LEFT:
+ case THEME_LOCATION_RESIZING_BOTTOM_RIGHT:
+ if (!window->shell_surface)
+ break;
+ input_ungrab(input);
+
+ window->resizing = 1;
+ wl_shell_surface_resize(window->shell_surface,
+ input_get_seat(input),
+ display->serial, location);
+ break;
+ }
+ } else if (button == BTN_RIGHT &&
+ (window->type == TYPE_TOPLEVEL ||
+ window->type == TYPE_MAXIMIZED)) {
+ window_show_frame_menu(window, input, time);
+ }
+}
+
+static void
+frame_touch_down_handler(struct widget *widget, struct input *input,
+ uint32_t serial, uint32_t time, int32_t id,
+ float x, float y, void *data)
+{
+ struct window *window = widget->window;
+ struct display *display = window->display;
+
+ wl_shell_surface_move(window->shell_surface,
+ input_get_seat(input),
+ display->serial);
+}
+
+struct widget *
+frame_create(struct window *window, void *data)
+{
+ struct frame *frame;
+
+ frame = xzalloc(sizeof *frame);
+ frame->widget = window_add_widget(window, frame);
+ frame->child = widget_add_widget(frame->widget, data);
+
+ widget_set_redraw_handler(frame->widget, frame_redraw_handler);
+ widget_set_resize_handler(frame->widget, frame_resize_handler);
+ widget_set_enter_handler(frame->widget, frame_enter_handler);
+ widget_set_motion_handler(frame->widget, frame_motion_handler);
+ widget_set_button_handler(frame->widget, frame_button_handler);
+ widget_set_touch_down_handler(frame->widget, frame_touch_down_handler);
+
+ /* Create empty list for frame buttons */
+ wl_list_init(&frame->buttons_list);
+
+ frame_button_create(frame, DATADIR "/weston/icon_window.png",
+ FRAME_BUTTON_ICON, FRAME_BUTTON_LEFT, FRAME_BUTTON_NONE);
+
+ frame_button_create(frame, DATADIR "/weston/sign_close.png",
+ FRAME_BUTTON_CLOSE, FRAME_BUTTON_RIGHT, FRAME_BUTTON_FANCY);
+
+ frame_button_create(frame, DATADIR "/weston/sign_maximize.png",
+ FRAME_BUTTON_MAXIMIZE, FRAME_BUTTON_RIGHT, FRAME_BUTTON_FANCY);
+
+ frame_button_create(frame, DATADIR "/weston/sign_minimize.png",
+ FRAME_BUTTON_MINIMIZE, FRAME_BUTTON_RIGHT, FRAME_BUTTON_FANCY);
+
+ window->frame = frame;
+
+ return frame->child;
+}
+
+void
+frame_set_child_size(struct widget *widget, int child_width, int child_height)
+{
+ struct display *display = widget->window->display;
+ struct theme *t = display->theme;
+ int decoration_width, decoration_height;
+ int width, height;
+ int margin = widget->window->type == TYPE_MAXIMIZED ? 0 : t->margin;
+
+ if (widget->window->type != TYPE_FULLSCREEN) {
+ decoration_width = (t->width + margin) * 2;
+ decoration_height = t->width +
+ t->titlebar_height + margin * 2;
+
+ width = child_width + decoration_width;
+ height = child_height + decoration_height;
+ } else {
+ width = child_width;
+ height = child_height;
+ }
+
+ window_schedule_resize(widget->window, width, height);
+}
+
+static void
+frame_destroy(struct frame *frame)
+{
+ struct frame_button *button, *tmp;
+
+ wl_list_for_each_safe(button, tmp, &frame->buttons_list, link)
+ frame_button_destroy(button);
+
+ /* frame->child must be destroyed by the application */
+ widget_destroy(frame->widget);
+ free(frame);
+}
+
+static void
+input_set_focus_widget(struct input *input, struct widget *focus,
+ float x, float y)
+{
+ struct widget *old, *widget;
+ int cursor;
+
+ if (focus == input->focus_widget)
+ return;
+
+ old = input->focus_widget;
+ if (old) {
+ widget = old;
+ if (input->grab)
+ widget = input->grab;
+ if (widget->leave_handler)
+ widget->leave_handler(old, input, widget->user_data);
+ input->focus_widget = NULL;
+ }
+
+ if (focus) {
+ widget = focus;
+ if (input->grab)
+ widget = input->grab;
+ input->focus_widget = focus;
+ if (widget->enter_handler)
+ cursor = widget->enter_handler(focus, input, x, y,
+ widget->user_data);
+ else
+ cursor = widget->default_cursor;
+
+ input_set_pointer_image(input, cursor);
+ }
+}
+
+void
+input_grab(struct input *input, struct widget *widget, uint32_t button)
+{
+ input->grab = widget;
+ input->grab_button = button;
+}
+
+void
+input_ungrab(struct input *input)
+{
+ struct widget *widget;
+
+ input->grab = NULL;
+ if (input->pointer_focus) {
+ widget = window_find_widget(input->pointer_focus,
+ input->sx, input->sy);
+ input_set_focus_widget(input, widget, input->sx, input->sy);
+ }
+}
+
+static void
+input_remove_pointer_focus(struct input *input)
+{
+ struct window *window = input->pointer_focus;
+
+ if (!window)
+ return;
+
+ input_set_focus_widget(input, NULL, 0, 0);
+
+ input->pointer_focus = NULL;
+ input->current_cursor = CURSOR_UNSET;
+}
+
+static void
+pointer_handle_enter(void *data, struct wl_pointer *pointer,
+ uint32_t serial, struct wl_surface *surface,
+ wl_fixed_t sx_w, wl_fixed_t sy_w)
+{
+ struct input *input = data;
+ struct window *window;
+ struct widget *widget;
+ float sx = wl_fixed_to_double(sx_w);
+ float sy = wl_fixed_to_double(sy_w);
+
+ if (!surface) {
+ /* enter event for a window we've just destroyed */
+ return;
+ }
+
+ input->display->serial = serial;
+ input->pointer_enter_serial = serial;
+ input->pointer_focus = wl_surface_get_user_data(surface);
+ window = input->pointer_focus;
+
+ if (window->resizing) {
+ window->resizing = 0;
+ /* Schedule a redraw to free the pool */
+ window_schedule_redraw(window);
+ }
+
+ input->sx = sx;
+ input->sy = sy;
+
+ widget = window_find_widget(window, sx, sy);
+ input_set_focus_widget(input, widget, sx, sy);
+}
+
+static void
+pointer_handle_leave(void *data, struct wl_pointer *pointer,
+ uint32_t serial, struct wl_surface *surface)
+{
+ struct input *input = data;
+
+ input->display->serial = serial;
+ input_remove_pointer_focus(input);
+}
+
+static void
+pointer_handle_motion(void *data, struct wl_pointer *pointer,
+ uint32_t time, wl_fixed_t sx_w, wl_fixed_t sy_w)
+{
+ struct input *input = data;
+ struct window *window = input->pointer_focus;
+ struct widget *widget;
+ int cursor;
+ float sx = wl_fixed_to_double(sx_w);
+ float sy = wl_fixed_to_double(sy_w);
+
+ input->sx = sx;
+ input->sy = sy;
+
+ if (!window)
+ return;
+
+ /* when making the window smaller - e.g. after a unmaximise we might
+ * still have a pending motion event that the compositor has picked
+ * based on the old surface dimensions
+ */
+ if (sx > window->main_surface->allocation.width ||
+ sy > window->main_surface->allocation.height)
+ return;
+
+ if (!(input->grab && input->grab_button)) {
+ widget = window_find_widget(window, sx, sy);
+ input_set_focus_widget(input, widget, sx, sy);
+ }
+
+ if (input->grab)
+ widget = input->grab;
+ else
+ widget = input->focus_widget;
+ if (widget) {
+ if (widget->motion_handler)
+ cursor = widget->motion_handler(input->focus_widget,
+ input, time, sx, sy,
+ widget->user_data);
+ else
+ cursor = widget->default_cursor;
+ } else
+ cursor = CURSOR_LEFT_PTR;
+
+ input_set_pointer_image(input, cursor);
+}
+
+static void
+pointer_handle_button(void *data, struct wl_pointer *pointer, uint32_t serial,
+ uint32_t time, uint32_t button, uint32_t state_w)
+{
+ struct input *input = data;
+ struct widget *widget;
+ enum wl_pointer_button_state state = state_w;
+
+ input->display->serial = serial;
+ if (input->focus_widget && input->grab == NULL &&
+ state == WL_POINTER_BUTTON_STATE_PRESSED)
+ input_grab(input, input->focus_widget, button);
+
+ widget = input->grab;
+ if (widget && widget->button_handler)
+ (*widget->button_handler)(widget,
+ input, time,
+ button, state,
+ input->grab->user_data);
+
+ if (input->grab && input->grab_button == button &&
+ state == WL_POINTER_BUTTON_STATE_RELEASED)
+ input_ungrab(input);
+}
+
+static void
+pointer_handle_axis(void *data, struct wl_pointer *pointer,
+ uint32_t time, uint32_t axis, wl_fixed_t value)
+{
+ struct input *input = data;
+ struct widget *widget;
+
+ widget = input->focus_widget;
+ if (input->grab)
+ widget = input->grab;
+ if (widget && widget->axis_handler)
+ (*widget->axis_handler)(widget,
+ input, time,
+ axis, value,
+ widget->user_data);
+}
+
+static const struct wl_pointer_listener pointer_listener = {
+ pointer_handle_enter,
+ pointer_handle_leave,
+ pointer_handle_motion,
+ pointer_handle_button,
+ pointer_handle_axis,
+};
+
+static void
+input_remove_keyboard_focus(struct input *input)
+{
+ struct window *window = input->keyboard_focus;
+ struct itimerspec its;
+
+ its.it_interval.tv_sec = 0;
+ its.it_interval.tv_nsec = 0;
+ its.it_value.tv_sec = 0;
+ its.it_value.tv_nsec = 0;
+ timerfd_settime(input->repeat_timer_fd, 0, &its, NULL);
+
+ if (!window)
+ return;
+
+ window->focus_count--;
+ if (window->keyboard_focus_handler)
+ (*window->keyboard_focus_handler)(window, NULL,
+ window->user_data);
+
+ input->keyboard_focus = NULL;
+}
+
+static void
+keyboard_repeat_func(struct task *task, uint32_t events)
+{
+ struct input *input =
+ container_of(task, struct input, repeat_task);
+ struct window *window = input->keyboard_focus;
+ uint64_t exp;
+
+ if (read(input->repeat_timer_fd, &exp, sizeof exp) != sizeof exp)
+ /* If we change the timer between the fd becoming
+ * readable and getting here, there'll be nothing to
+ * read and we get EAGAIN. */
+ return;
+
+ if (window && window->key_handler) {
+ (*window->key_handler)(window, input, input->repeat_time,
+ input->repeat_key, input->repeat_sym,
+ WL_KEYBOARD_KEY_STATE_PRESSED,
+ window->user_data);
+ }
+}
+
+static void
+keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard,
+ uint32_t format, int fd, uint32_t size)
+{
+ struct input *input = data;
+ char *map_str;
+
+ if (!data) {
+ close(fd);
+ return;
+ }
+
+ if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
+ close(fd);
+ return;
+ }
+
+ map_str = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
+ if (map_str == MAP_FAILED) {
+ close(fd);
+ return;
+ }
+
+ input->xkb.keymap = xkb_map_new_from_string(input->display->xkb_context,
+ map_str,
+ XKB_KEYMAP_FORMAT_TEXT_V1,
+ 0);
+ munmap(map_str, size);
+ close(fd);
+
+ if (!input->xkb.keymap) {
+ fprintf(stderr, "failed to compile keymap\n");
+ return;
+ }
+
+ input->xkb.state = xkb_state_new(input->xkb.keymap);
+ if (!input->xkb.state) {
+ fprintf(stderr, "failed to create XKB state\n");
+ xkb_map_unref(input->xkb.keymap);
+ input->xkb.keymap = NULL;
+ return;
+ }
+
+ input->xkb.control_mask =
+ 1 << xkb_map_mod_get_index(input->xkb.keymap, "Control");
+ input->xkb.alt_mask =
+ 1 << xkb_map_mod_get_index(input->xkb.keymap, "Mod1");
+ input->xkb.shift_mask =
+ 1 << xkb_map_mod_get_index(input->xkb.keymap, "Shift");
+}
+
+static void
+keyboard_handle_enter(void *data, struct wl_keyboard *keyboard,
+ uint32_t serial, struct wl_surface *surface,
+ struct wl_array *keys)
+{
+ struct input *input = data;
+ struct window *window;
+
+ input->display->serial = serial;
+ input->keyboard_focus = wl_surface_get_user_data(surface);
+
+ window = input->keyboard_focus;
+ window->focus_count++;
+ if (window->keyboard_focus_handler)
+ (*window->keyboard_focus_handler)(window,
+ input, window->user_data);
+}
+
+static void
+keyboard_handle_leave(void *data, struct wl_keyboard *keyboard,
+ uint32_t serial, struct wl_surface *surface)
+{
+ struct input *input = data;
+
+ input->display->serial = serial;
+ input_remove_keyboard_focus(input);
+}
+
+static void
+keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
+ uint32_t serial, uint32_t time, uint32_t key,
+ uint32_t state_w)
+{
+ struct input *input = data;
+ struct window *window = input->keyboard_focus;
+ uint32_t code, num_syms;
+ enum wl_keyboard_key_state state = state_w;
+ const xkb_keysym_t *syms;
+ xkb_keysym_t sym;
+ struct itimerspec its;
+
+ input->display->serial = serial;
+ code = key + 8;
+ if (!window || !input->xkb.state)
+ return;
+
+ num_syms = xkb_key_get_syms(input->xkb.state, code, &syms);
+
+ sym = XKB_KEY_NoSymbol;
+ if (num_syms == 1)
+ sym = syms[0];
+
+ if (sym == XKB_KEY_F5 && input->modifiers == MOD_ALT_MASK) {
+ if (state == WL_KEYBOARD_KEY_STATE_PRESSED)
+ window_set_maximized(window,
+ window->type != TYPE_MAXIMIZED);
+ } else if (sym == XKB_KEY_F11 &&
+ window->fullscreen_handler &&
+ state == WL_KEYBOARD_KEY_STATE_PRESSED) {
+ window->fullscreen_handler(window, window->user_data);
+ } else if (sym == XKB_KEY_F4 &&
+ input->modifiers == MOD_ALT_MASK &&
+ state == WL_KEYBOARD_KEY_STATE_PRESSED) {
+ if (window->close_handler)
+ window->close_handler(window->parent,
+ window->user_data);
+ else
+ display_exit(window->display);
+ } else if (window->key_handler) {
+ (*window->key_handler)(window, input, time, key,
+ sym, state, window->user_data);
+ }
+
+ if (state == WL_KEYBOARD_KEY_STATE_RELEASED &&
+ key == input->repeat_key) {
+ its.it_interval.tv_sec = 0;
+ its.it_interval.tv_nsec = 0;
+ its.it_value.tv_sec = 0;
+ its.it_value.tv_nsec = 0;
+ timerfd_settime(input->repeat_timer_fd, 0, &its, NULL);
+ } else if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
+ input->repeat_sym = sym;
+ input->repeat_key = key;
+ input->repeat_time = time;
+ its.it_interval.tv_sec = 0;
+ its.it_interval.tv_nsec = 25 * 1000 * 1000;
+ its.it_value.tv_sec = 0;
+ its.it_value.tv_nsec = 400 * 1000 * 1000;
+ timerfd_settime(input->repeat_timer_fd, 0, &its, NULL);
+ }
+}
+
+static void
+keyboard_handle_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 input *input = data;
+ xkb_mod_mask_t mask;
+
+ /* If we're not using a keymap, then we don't handle PC-style modifiers */
+ if (!input->xkb.keymap)
+ return;
+
+ xkb_state_update_mask(input->xkb.state, mods_depressed, mods_latched,
+ mods_locked, 0, 0, group);
+ mask = xkb_state_serialize_mods(input->xkb.state,
+ XKB_STATE_DEPRESSED |
+ XKB_STATE_LATCHED);
+ input->modifiers = 0;
+ if (mask & input->xkb.control_mask)
+ input->modifiers |= MOD_CONTROL_MASK;
+ if (mask & input->xkb.alt_mask)
+ input->modifiers |= MOD_ALT_MASK;
+ if (mask & input->xkb.shift_mask)
+ input->modifiers |= MOD_SHIFT_MASK;
+}
+
+static const struct wl_keyboard_listener keyboard_listener = {
+ keyboard_handle_keymap,
+ keyboard_handle_enter,
+ keyboard_handle_leave,
+ keyboard_handle_key,
+ keyboard_handle_modifiers,
+};
+
+static void
+touch_handle_down(void *data, struct wl_touch *wl_touch,
+ uint32_t serial, uint32_t time, struct wl_surface *surface,
+ int32_t id, wl_fixed_t x_w, wl_fixed_t y_w)
+{
+ struct input *input = data;
+ struct widget *widget;
+ float sx = wl_fixed_to_double(x_w);
+ float sy = wl_fixed_to_double(y_w);
+
+ input->display->serial = serial;
+ input->touch_focus = wl_surface_get_user_data(surface);
+ if (!input->touch_focus) {
+ DBG("Failed to find to touch focus for surface %p\n", surface);
+ return;
+ }
+
+ widget = window_find_widget(input->touch_focus,
+ wl_fixed_to_double(x_w),
+ wl_fixed_to_double(y_w));
+ if (widget) {
+ struct touch_point *tp = xmalloc(sizeof *tp);
+ if (tp) {
+ tp->id = id;
+ tp->widget = widget;
+ wl_list_insert(&input->touch_point_list, &tp->link);
+
+ if (widget->touch_down_handler)
+ (*widget->touch_down_handler)(widget, input,
+ serial, time, id,
+ sx, sy,
+ widget->user_data);
+ }
+ }
+}
+
+static void
+touch_handle_up(void *data, struct wl_touch *wl_touch,
+ uint32_t serial, uint32_t time, int32_t id)
+{
+ struct input *input = data;
+ struct touch_point *tp, *tmp;
+
+ if (!input->touch_focus) {
+ DBG("No touch focus found for touch up event!\n");
+ return;
+ }
+
+ wl_list_for_each_safe(tp, tmp, &input->touch_point_list, link) {
+ if (tp->id != id)
+ continue;
+
+ if (tp->widget->touch_up_handler)
+ (*tp->widget->touch_up_handler)(tp->widget, input, serial,
+ time, id,
+ tp->widget->user_data);
+
+ wl_list_remove(&tp->link);
+ free(tp);
+
+ return;
+ }
+}
+
+static void
+touch_handle_motion(void *data, struct wl_touch *wl_touch,
+ uint32_t time, int32_t id, wl_fixed_t x_w, wl_fixed_t y_w)
+{
+ struct input *input = data;
+ struct touch_point *tp;
+ float sx = wl_fixed_to_double(x_w);
+ float sy = wl_fixed_to_double(y_w);
+
+ DBG("touch_handle_motion: %i %i\n", id, wl_list_length(&input->touch_point_list));
+
+ if (!input->touch_focus) {
+ DBG("No touch focus found for touch motion event!\n");
+ return;
+ }
+
+ wl_list_for_each(tp, &input->touch_point_list, link) {
+ if (tp->id != id)
+ continue;
+
+ if (tp->widget->touch_motion_handler)
+ (*tp->widget->touch_motion_handler)(tp->widget, input, time,
+ id, sx, sy,
+ tp->widget->user_data);
+ return;
+ }
+}
+
+static void
+touch_handle_frame(void *data, struct wl_touch *wl_touch)
+{
+ struct input *input = data;
+ struct touch_point *tp, *tmp;
+
+ DBG("touch_handle_frame\n");
+
+ if (!input->touch_focus) {
+ DBG("No touch focus found for touch frame event!\n");
+ return;
+ }
+
+ wl_list_for_each_safe(tp, tmp, &input->touch_point_list, link) {
+ if (tp->widget->touch_frame_handler)
+ (*tp->widget->touch_frame_handler)(tp->widget, input,
+ tp->widget->user_data);
+
+ wl_list_remove(&tp->link);
+ free(tp);
+ }
+}
+
+static void
+touch_handle_cancel(void *data, struct wl_touch *wl_touch)
+{
+ struct input *input = data;
+ struct touch_point *tp, *tmp;
+
+ DBG("touch_handle_cancel\n");
+
+ if (!input->touch_focus) {
+ DBG("No touch focus found for touch cancel event!\n");
+ return;
+ }
+
+ wl_list_for_each_safe(tp, tmp, &input->touch_point_list, link) {
+ if (tp->widget->touch_cancel_handler)
+ (*tp->widget->touch_cancel_handler)(tp->widget, input,
+ tp->widget->user_data);
+
+ wl_list_remove(&tp->link);
+ free(tp);
+ }
+}
+
+static const struct wl_touch_listener touch_listener = {
+ touch_handle_down,
+ touch_handle_up,
+ touch_handle_motion,
+ touch_handle_frame,
+ touch_handle_cancel,
+};
+
+static void
+seat_handle_capabilities(void *data, struct wl_seat *seat,
+ enum wl_seat_capability caps)
+{
+ struct input *input = data;
+
+ if ((caps & WL_SEAT_CAPABILITY_POINTER) && !input->pointer) {
+ input->pointer = wl_seat_get_pointer(seat);
+ wl_pointer_set_user_data(input->pointer, input);
+ wl_pointer_add_listener(input->pointer, &pointer_listener,
+ input);
+ } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && input->pointer) {
+ wl_pointer_destroy(input->pointer);
+ input->pointer = NULL;
+ }
+
+ if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !input->keyboard) {
+ input->keyboard = wl_seat_get_keyboard(seat);
+ wl_keyboard_set_user_data(input->keyboard, input);
+ wl_keyboard_add_listener(input->keyboard, &keyboard_listener,
+ input);
+ } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && input->keyboard) {
+ wl_keyboard_destroy(input->keyboard);
+ input->keyboard = NULL;
+ }
+
+ if ((caps & WL_SEAT_CAPABILITY_TOUCH) && !input->touch) {
+ input->touch = wl_seat_get_touch(seat);
+ wl_touch_set_user_data(input->touch, input);
+ wl_touch_add_listener(input->touch, &touch_listener, input);
+ } else if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && input->touch) {
+ wl_touch_destroy(input->touch);
+ input->touch = NULL;
+ }
+}
+
+static void
+seat_handle_name(void *data, struct wl_seat *seat,
+ const char *name)
+{
+
+}
+
+static const struct wl_seat_listener seat_listener = {
+ seat_handle_capabilities,
+ seat_handle_name
+};
+
+void
+input_get_position(struct input *input, int32_t *x, int32_t *y)
+{
+ *x = input->sx;
+ *y = input->sy;
+}
+
+struct display *
+input_get_display(struct input *input)
+{
+ return input->display;
+}
+
+struct wl_seat *
+input_get_seat(struct input *input)
+{
+ return input->seat;
+}
+
+uint32_t
+input_get_modifiers(struct input *input)
+{
+ return input->modifiers;
+}
+
+struct widget *
+input_get_focus_widget(struct input *input)
+{
+ return input->focus_widget;
+}
+
+struct data_offer {
+ struct wl_data_offer *offer;
+ struct input *input;
+ struct wl_array types;
+ int refcount;
+
+ struct task io_task;
+ int fd;
+ data_func_t func;
+ int32_t x, y;
+ void *user_data;
+};
+
+static void
+data_offer_offer(void *data, struct wl_data_offer *wl_data_offer, const char *type)
+{
+ struct data_offer *offer = data;
+ char **p;
+
+ p = wl_array_add(&offer->types, sizeof *p);
+ *p = strdup(type);
+}
+
+static const struct wl_data_offer_listener data_offer_listener = {
+ data_offer_offer,
+};
+
+static void
+data_offer_destroy(struct data_offer *offer)
+{
+ char **p;
+
+ offer->refcount--;
+ if (offer->refcount == 0) {
+ wl_data_offer_destroy(offer->offer);
+ for (p = offer->types.data; *p; p++)
+ free(*p);
+ wl_array_release(&offer->types);
+ free(offer);
+ }
+}
+
+static void
+data_device_data_offer(void *data,
+ struct wl_data_device *data_device,
+ struct wl_data_offer *_offer)
+{
+ struct data_offer *offer;
+
+ offer = xmalloc(sizeof *offer);
+
+ wl_array_init(&offer->types);
+ offer->refcount = 1;
+ offer->input = data;
+ offer->offer = _offer;
+ wl_data_offer_add_listener(offer->offer,
+ &data_offer_listener, offer);
+}
+
+static void
+data_device_enter(void *data, struct wl_data_device *data_device,
+ uint32_t serial, struct wl_surface *surface,
+ wl_fixed_t x_w, wl_fixed_t y_w,
+ struct wl_data_offer *offer)
+{
+ struct input *input = data;
+ struct window *window;
+ void *types_data;
+ float x = wl_fixed_to_double(x_w);
+ float y = wl_fixed_to_double(y_w);
+ char **p;
+
+ input->pointer_enter_serial = serial;
+ window = wl_surface_get_user_data(surface);
+ input->pointer_focus = window;
+
+ if (offer) {
+ input->drag_offer = wl_data_offer_get_user_data(offer);
+
+ p = wl_array_add(&input->drag_offer->types, sizeof *p);
+ *p = NULL;
+
+ types_data = input->drag_offer->types.data;
+ } else {
+ input->drag_offer = NULL;
+ types_data = NULL;
+ }
+
+ window = input->pointer_focus;
+ if (window->data_handler)
+ window->data_handler(window, input, x, y, types_data,
+ window->user_data);
+}
+
+static void
+data_device_leave(void *data, struct wl_data_device *data_device)
+{
+ struct input *input = data;
+
+ if (input->drag_offer) {
+ data_offer_destroy(input->drag_offer);
+ input->drag_offer = NULL;
+ }
+}
+
+static void
+data_device_motion(void *data, struct wl_data_device *data_device,
+ uint32_t time, wl_fixed_t x_w, wl_fixed_t y_w)
+{
+ struct input *input = data;
+ struct window *window = input->pointer_focus;
+ float x = wl_fixed_to_double(x_w);
+ float y = wl_fixed_to_double(y_w);
+ void *types_data;
+
+ input->sx = x;
+ input->sy = y;
+
+ if (input->drag_offer)
+ types_data = input->drag_offer->types.data;
+ else
+ types_data = NULL;
+
+ if (window->data_handler)
+ window->data_handler(window, input, x, y, types_data,
+ window->user_data);
+}
+
+static void
+data_device_drop(void *data, struct wl_data_device *data_device)
+{
+ struct input *input = data;
+ struct window *window = input->pointer_focus;
+
+ if (window->drop_handler)
+ window->drop_handler(window, input,
+ input->sx, input->sy, window->user_data);
+}
+
+static void
+data_device_selection(void *data,
+ struct wl_data_device *wl_data_device,
+ struct wl_data_offer *offer)
+{
+ struct input *input = data;
+ char **p;
+
+ if (input->selection_offer)
+ data_offer_destroy(input->selection_offer);
+
+ if (offer) {
+ input->selection_offer = wl_data_offer_get_user_data(offer);
+ p = wl_array_add(&input->selection_offer->types, sizeof *p);
+ *p = NULL;
+ } else {
+ input->selection_offer = NULL;
+ }
+}
+
+static const struct wl_data_device_listener data_device_listener = {
+ data_device_data_offer,
+ data_device_enter,
+ data_device_leave,
+ data_device_motion,
+ data_device_drop,
+ data_device_selection
+};
+
+static void
+input_set_pointer_image_index(struct input *input, int index)
+{
+ struct wl_buffer *buffer;
+ struct wl_cursor *cursor;
+ struct wl_cursor_image *image;
+
+ if (!input->pointer)
+ return;
+
+ cursor = input->display->cursors[input->current_cursor];
+ if (!cursor)
+ return;
+
+ if (index >= (int) cursor->image_count) {
+ fprintf(stderr, "cursor index out of range\n");
+ return;
+ }
+
+ image = cursor->images[index];
+ buffer = wl_cursor_image_get_buffer(image);
+ if (!buffer)
+ return;
+
+ wl_pointer_set_cursor(input->pointer, input->pointer_enter_serial,
+ input->pointer_surface,
+ image->hotspot_x, image->hotspot_y);
+ wl_surface_attach(input->pointer_surface, buffer, 0, 0);
+ wl_surface_damage(input->pointer_surface, 0, 0,
+ image->width, image->height);
+ wl_surface_commit(input->pointer_surface);
+}
+
+static const struct wl_callback_listener pointer_surface_listener;
+
+static void
+pointer_surface_frame_callback(void *data, struct wl_callback *callback,
+ uint32_t time)
+{
+ struct input *input = data;
+ struct wl_cursor *cursor;
+ int i;
+
+ if (callback) {
+ assert(callback == input->cursor_frame_cb);
+ wl_callback_destroy(callback);
+ input->cursor_frame_cb = NULL;
+ }
+
+ if (!input->pointer)
+ return;
+
+ if (input->current_cursor == CURSOR_BLANK) {
+ wl_pointer_set_cursor(input->pointer,
+ input->pointer_enter_serial,
+ NULL, 0, 0);
+ return;
+ }
+
+ if (input->current_cursor == CURSOR_UNSET)
+ return;
+ cursor = input->display->cursors[input->current_cursor];
+ if (!cursor)
+ return;
+
+ /* FIXME We don't have the current time on the first call so we set
+ * the animation start to the time of the first frame callback. */
+ if (time == 0)
+ input->cursor_anim_start = 0;
+ else if (input->cursor_anim_start == 0)
+ input->cursor_anim_start = time;
+
+ if (time == 0 || input->cursor_anim_start == 0)
+ i = 0;
+ else
+ i = wl_cursor_frame(cursor, time - input->cursor_anim_start);
+
+ if (cursor->image_count > 1) {
+ input->cursor_frame_cb =
+ wl_surface_frame(input->pointer_surface);
+ wl_callback_add_listener(input->cursor_frame_cb,
+ &pointer_surface_listener, input);
+ }
+
+ input_set_pointer_image_index(input, i);
+}
+
+static const struct wl_callback_listener pointer_surface_listener = {
+ pointer_surface_frame_callback
+};
+
+void
+input_set_pointer_image(struct input *input, int pointer)
+{
+ int force = 0;
+
+ if (!input->pointer)
+ return;
+
+ if (input->pointer_enter_serial > input->cursor_serial)
+ force = 1;
+
+ if (!force && pointer == input->current_cursor)
+ return;
+
+ input->current_cursor = pointer;
+ input->cursor_serial = input->pointer_enter_serial;
+ if (!input->cursor_frame_cb)
+ pointer_surface_frame_callback(input, NULL, 0);
+ else if (force) {
+ /* The current frame callback may be stuck if, for instance,
+ * the set cursor request was processed by the server after
+ * this client lost the focus. In this case the cursor surface
+ * might not be mapped and the frame callback wouldn't ever
+ * complete. Send a set_cursor and attach to try to map the
+ * cursor surface again so that the callback will finish */
+ input_set_pointer_image_index(input, 0);
+ }
+}
+
+struct wl_data_device *
+input_get_data_device(struct input *input)
+{
+ return input->data_device;
+}
+
+void
+input_set_selection(struct input *input,
+ struct wl_data_source *source, uint32_t time)
+{
+ wl_data_device_set_selection(input->data_device, source, time);
+}
+
+void
+input_accept(struct input *input, const char *type)
+{
+ wl_data_offer_accept(input->drag_offer->offer,
+ input->pointer_enter_serial, type);
+}
+
+static void
+offer_io_func(struct task *task, uint32_t events)
+{
+ struct data_offer *offer =
+ container_of(task, struct data_offer, io_task);
+ unsigned int len;
+ char buffer[4096];
+
+ len = read(offer->fd, buffer, sizeof buffer);
+ offer->func(buffer, len,
+ offer->x, offer->y, offer->user_data);
+
+ if (len == 0) {
+ close(offer->fd);
+ data_offer_destroy(offer);
+ }
+}
+
+static void
+data_offer_receive_data(struct data_offer *offer, const char *mime_type,
+ data_func_t func, void *user_data)
+{
+ int p[2];
+
+ if (pipe2(p, O_CLOEXEC) == -1)
+ return;
+
+ wl_data_offer_receive(offer->offer, mime_type, p[1]);
+ close(p[1]);
+
+ offer->io_task.run = offer_io_func;
+ offer->fd = p[0];
+ offer->func = func;
+ offer->refcount++;
+ offer->user_data = user_data;
+
+ display_watch_fd(offer->input->display,
+ offer->fd, EPOLLIN, &offer->io_task);
+}
+
+void
+input_receive_drag_data(struct input *input, const char *mime_type,
+ data_func_t func, void *data)
+{
+ data_offer_receive_data(input->drag_offer, mime_type, func, data);
+ input->drag_offer->x = input->sx;
+ input->drag_offer->y = input->sy;
+}
+
+int
+input_receive_drag_data_to_fd(struct input *input,
+ const char *mime_type, int fd)
+{
+ if (input->drag_offer)
+ wl_data_offer_receive(input->drag_offer->offer, mime_type, fd);
+
+ return 0;
+}
+
+int
+input_receive_selection_data(struct input *input, const char *mime_type,
+ data_func_t func, void *data)
+{
+ char **p;
+
+ if (input->selection_offer == NULL)
+ return -1;
+
+ for (p = input->selection_offer->types.data; *p; p++)
+ if (strcmp(mime_type, *p) == 0)
+ break;
+
+ if (*p == NULL)
+ return -1;
+
+ data_offer_receive_data(input->selection_offer,
+ mime_type, func, data);
+ return 0;
+}
+
+int
+input_receive_selection_data_to_fd(struct input *input,
+ const char *mime_type, int fd)
+{
+ if (input->selection_offer)
+ wl_data_offer_receive(input->selection_offer->offer,
+ mime_type, fd);
+
+ return 0;
+}
+
+void
+window_move(struct window *window, struct input *input, uint32_t serial)
+{
+ if (!window->shell_surface)
+ return;
+
+ wl_shell_surface_move(window->shell_surface, input->seat, serial);
+}
+
+void
+window_touch_move(struct window *window, struct input *input, uint32_t serial)
+{
+ if (!window->shell_surface)
+ return;
+
+ wl_shell_surface_move(window->shell_surface, input->seat,
+ window->display->serial);
+}
+
+static void
+surface_set_synchronized(struct surface *surface)
+{
+ if (!surface->subsurface)
+ return;
+
+ if (surface->synchronized)
+ return;
+
+ wl_subsurface_set_sync(surface->subsurface);
+ surface->synchronized = 1;
+}
+
+static void
+surface_set_synchronized_default(struct surface *surface)
+{
+ if (!surface->subsurface)
+ return;
+
+ if (surface->synchronized == surface->synchronized_default)
+ return;
+
+ if (surface->synchronized_default)
+ wl_subsurface_set_sync(surface->subsurface);
+ else
+ wl_subsurface_set_desync(surface->subsurface);
+
+ surface->synchronized = surface->synchronized_default;
+}
+
+static void
+surface_resize(struct surface *surface)
+{
+ struct widget *widget = surface->widget;
+ struct wl_compositor *compositor = widget->window->display->compositor;
+
+ if (surface->input_region) {
+ wl_region_destroy(surface->input_region);
+ surface->input_region = NULL;
+ }
+
+ if (surface->opaque_region)
+ wl_region_destroy(surface->opaque_region);
+
+ surface->opaque_region = wl_compositor_create_region(compositor);
+
+ if (widget->resize_handler)
+ widget->resize_handler(widget,
+ widget->allocation.width,
+ widget->allocation.height,
+ widget->user_data);
+
+ if (surface->subsurface &&
+ (surface->allocation.x != widget->allocation.x ||
+ surface->allocation.y != widget->allocation.y)) {
+ wl_subsurface_set_position(surface->subsurface,
+ widget->allocation.x,
+ widget->allocation.y);
+ }
+ if (surface->allocation.width != widget->allocation.width ||
+ surface->allocation.height != widget->allocation.height) {
+ window_schedule_redraw(widget->window);
+ }
+ surface->allocation = widget->allocation;
+
+ if (widget->opaque)
+ wl_region_add(surface->opaque_region, 0, 0,
+ widget->allocation.width,
+ widget->allocation.height);
+}
+
+static void
+hack_prevent_EGL_sub_surface_deadlock(struct window *window)
+{
+ /*
+ * This hack should be removed, when EGL respects
+ * eglSwapInterval(0).
+ *
+ * If this window has sub-surfaces, especially a free-running
+ * EGL-widget, we need to post the parent surface once with
+ * all the old state to guarantee, that the EGL-widget will
+ * receive its frame callback soon. Otherwise, a forced call
+ * to eglSwapBuffers may end up blocking, waiting for a frame
+ * event that will never come, because we will commit the parent
+ * surface with all new state only after eglSwapBuffers returns.
+ *
+ * This assumes, that:
+ * 1. When the EGL widget's resize hook is called, it pauses.
+ * 2. When the EGL widget's redraw hook is called, it forces a
+ * repaint and a call to eglSwapBuffers(), and maybe resumes.
+ * In a single threaded application condition 1 is a no-op.
+ *
+ * XXX: This should actually be after the surface_resize() calls,
+ * but cannot, because then it would commit the incomplete state
+ * accumulated from the widget resize hooks.
+ */
+ if (window->subsurface_list.next != &window->main_surface->link ||
+ window->subsurface_list.prev != &window->main_surface->link)
+ wl_surface_commit(window->main_surface->surface);
+}
+
+static void
+idle_resize(struct window *window)
+{
+ struct surface *surface;
+
+ window->resize_needed = 0;
+ window->redraw_needed = 1;
+
+ DBG("from %dx%d to %dx%d\n",
+ window->main_surface->server_allocation.width,
+ window->main_surface->server_allocation.height,
+ window->pending_allocation.width,
+ window->pending_allocation.height);
+
+ hack_prevent_EGL_sub_surface_deadlock(window);
+
+ widget_set_allocation(window->main_surface->widget,
+ window->pending_allocation.x,
+ window->pending_allocation.y,
+ window->pending_allocation.width,
+ window->pending_allocation.height);
+
+ surface_resize(window->main_surface);
+
+ /* The main surface is in the list, too. Main surface's
+ * resize_handler is responsible for calling widget_set_allocation()
+ * on all sub-surface root widgets, so they will be resized
+ * properly.
+ */
+ wl_list_for_each(surface, &window->subsurface_list, link) {
+ if (surface == window->main_surface)
+ continue;
+
+ surface_set_synchronized(surface);
+ surface_resize(surface);
+ }
+}
+
+void
+window_schedule_resize(struct window *window, int width, int height)
+{
+ /* We should probably get these numbers from the theme. */
+ const int min_width = 200, min_height = 200;
+
+ window->pending_allocation.x = 0;
+ window->pending_allocation.y = 0;
+ window->pending_allocation.width = width;
+ window->pending_allocation.height = height;
+
+ if (window->min_allocation.width == 0) {
+ if (width < min_width && window->frame)
+ window->min_allocation.width = min_width;
+ else
+ window->min_allocation.width = width;
+ if (height < min_height && window->frame)
+ window->min_allocation.height = min_height;
+ else
+ window->min_allocation.height = height;
+ }
+
+ if (window->pending_allocation.width < window->min_allocation.width)
+ window->pending_allocation.width = window->min_allocation.width;
+ if (window->pending_allocation.height < window->min_allocation.height)
+ window->pending_allocation.height = window->min_allocation.height;
+
+ window->resize_needed = 1;
+ window_schedule_redraw(window);
+}
+
+void
+widget_schedule_resize(struct widget *widget, int32_t width, int32_t height)
+{
+ window_schedule_resize(widget->window, width, height);
+}
+
+static void
+handle_ping(void *data, struct wl_shell_surface *shell_surface,
+ uint32_t serial)
+{
+ wl_shell_surface_pong(shell_surface, serial);
+}
+
+static void
+handle_configure(void *data, struct wl_shell_surface *shell_surface,
+ uint32_t edges, int32_t width, int32_t height)
+{
+ struct window *window = data;
+
+ window->resize_edges = edges;
+ window_schedule_resize(window, width, height);
+}
+
+static void
+menu_destroy(struct menu *menu)
+{
+ widget_destroy(menu->widget);
+ window_destroy(menu->window);
+ free(menu);
+}
+
+static void
+handle_popup_done(void *data, struct wl_shell_surface *shell_surface)
+{
+ struct window *window = data;
+ struct menu *menu = window->main_surface->widget->user_data;
+
+ /* FIXME: Need more context in this event, at least the input
+ * device. Or just use wl_callback. And this really needs to
+ * be a window vfunc that the menu can set. And we need the
+ * time. */
+
+ input_ungrab(menu->input);
+ menu_destroy(menu);
+}
+
+static const struct wl_shell_surface_listener shell_surface_listener = {
+ handle_ping,
+ handle_configure,
+ handle_popup_done
+};
+
+void
+window_get_allocation(struct window *window,
+ struct rectangle *allocation)
+{
+ *allocation = window->main_surface->allocation;
+}
+
+static void
+widget_redraw(struct widget *widget)
+{
+ struct widget *child;
+
+ if (widget->redraw_handler)
+ widget->redraw_handler(widget, widget->user_data);
+ wl_list_for_each(child, &widget->child_list, link)
+ widget_redraw(child);
+}
+
+static void
+frame_callback(void *data, struct wl_callback *callback, uint32_t time)
+{
+ struct surface *surface = data;
+
+ assert(callback == surface->frame_cb);
+ DBG_OBJ(callback, "done\n");
+ wl_callback_destroy(callback);
+ surface->frame_cb = NULL;
+
+ surface->last_time = time;
+
+ if (surface->redraw_needed || surface->window->redraw_needed) {
+ DBG_OBJ(surface->surface, "window_schedule_redraw_task\n");
+ window_schedule_redraw_task(surface->window);
+ }
+}
+
+static const struct wl_callback_listener listener = {
+ frame_callback
+};
+
+static void
+surface_redraw(struct surface *surface)
+{
+ DBG_OBJ(surface->surface, "begin\n");
+
+ if (!surface->window->redraw_needed && !surface->redraw_needed)
+ return;
+
+ /* Whole-window redraw forces a redraw even if the previous has
+ * not yet hit the screen.
+ */
+ if (surface->frame_cb) {
+ if (!surface->window->redraw_needed)
+ return;
+
+ DBG_OBJ(surface->frame_cb, "cancelled\n");
+ wl_callback_destroy(surface->frame_cb);
+ }
+
+ surface->frame_cb = wl_surface_frame(surface->surface);
+ wl_callback_add_listener(surface->frame_cb, &listener, surface);
+ DBG_OBJ(surface->frame_cb, "new\n");
+
+ surface->redraw_needed = 0;
+ DBG_OBJ(surface->surface, "-> widget_redraw\n");
+ widget_redraw(surface->widget);
+ DBG_OBJ(surface->surface, "done\n");
+}
+
+static void
+idle_redraw(struct task *task, uint32_t events)
+{
+ struct window *window = container_of(task, struct window, redraw_task);
+ struct surface *surface;
+
+ DBG(" --------- \n");
+
+ wl_list_init(&window->redraw_task.link);
+ window->redraw_task_scheduled = 0;
+
+ if (window->resize_needed) {
+ /* throttle resizing to the main surface display */
+ if (window->main_surface->frame_cb) {
+ DBG_OBJ(window->main_surface->frame_cb, "pending\n");
+ return;
+ }
+
+ idle_resize(window);
+ }
+
+ wl_list_for_each(surface, &window->subsurface_list, link)
+ surface_redraw(surface);
+
+ window->redraw_needed = 0;
+ window_flush(window);
+
+ wl_list_for_each(surface, &window->subsurface_list, link)
+ surface_set_synchronized_default(surface);
+}
+
+static void
+window_schedule_redraw_task(struct window *window)
+{
+ if (window->configure_requests)
+ return;
+ if (!window->redraw_task_scheduled) {
+ window->redraw_task.run = idle_redraw;
+ display_defer(window->display, &window->redraw_task);
+ window->redraw_task_scheduled = 1;
+ }
+}
+
+void
+window_schedule_redraw(struct window *window)
+{
+ struct surface *surface;
+
+ DBG_OBJ(window->main_surface->surface, "window %p\n", window);
+
+ wl_list_for_each(surface, &window->subsurface_list, link)
+ surface->redraw_needed = 1;
+
+ window_schedule_redraw_task(window);
+}
+
+int
+window_is_fullscreen(struct window *window)
+{
+ return window->type == TYPE_FULLSCREEN;
+}
+
+static void
+configure_request_completed(void *data, struct wl_callback *callback, uint32_t time)
+{
+ struct window *window = data;
+
+ wl_callback_destroy(callback);
+ window->configure_requests--;
+
+ if (!window->configure_requests)
+ window_schedule_redraw(window);
+}
+
+static struct wl_callback_listener configure_request_listener = {
+ configure_request_completed,
+};
+
+static void
+window_defer_redraw_until_configure(struct window* window)
+{
+ struct wl_callback *callback;
+
+ if (window->redraw_task_scheduled) {
+ wl_list_remove(&window->redraw_task.link);
+ window->redraw_task_scheduled = 0;
+ }
+
+ callback = wl_display_sync(window->display->display);
+ wl_callback_add_listener(callback, &configure_request_listener, window);
+ window->configure_requests++;
+}
+
+void
+window_set_fullscreen(struct window *window, int fullscreen)
+{
+ if (!window->display->shell)
+ return;
+
+ if ((window->type == TYPE_FULLSCREEN) == fullscreen)
+ return;
+
+ if (fullscreen) {
+ window->saved_type = window->type;
+ if (window->type == TYPE_TOPLEVEL) {
+ window->saved_allocation = window->main_surface->allocation;
+ }
+ window->type = TYPE_FULLSCREEN;
+ wl_shell_surface_set_fullscreen(window->shell_surface,
+ window->fullscreen_method,
+ 0, NULL);
+ window_defer_redraw_until_configure (window);
+ } else {
+ if (window->saved_type == TYPE_MAXIMIZED) {
+ window_set_maximized(window, 1);
+ } else {
+ window->type = TYPE_TOPLEVEL;
+ wl_shell_surface_set_toplevel(window->shell_surface);
+ window_schedule_resize(window,
+ window->saved_allocation.width,
+ window->saved_allocation.height);
+ }
+
+ }
+}
+
+void
+window_set_fullscreen_method(struct window *window,
+ enum wl_shell_surface_fullscreen_method method)
+{
+ window->fullscreen_method = method;
+}
+
+int
+window_is_maximized(struct window *window)
+{
+ return window->type == TYPE_MAXIMIZED;
+}
+
+void
+window_set_maximized(struct window *window, int maximized)
+{
+ if (!window->display->shell)
+ return;
+
+ if ((window->type == TYPE_MAXIMIZED) == maximized)
+ return;
+
+ if (window->type == TYPE_TOPLEVEL) {
+ window->saved_allocation = window->main_surface->allocation;
+ wl_shell_surface_set_maximized(window->shell_surface, NULL);
+ window->type = TYPE_MAXIMIZED;
+ window_defer_redraw_until_configure(window);
+ } else if (window->type == TYPE_FULLSCREEN) {
+ wl_shell_surface_set_maximized(window->shell_surface, NULL);
+ window->type = TYPE_MAXIMIZED;
+ window_defer_redraw_until_configure(window);
+ } else {
+ wl_shell_surface_set_toplevel(window->shell_surface);
+ window->type = TYPE_TOPLEVEL;
+ window_schedule_resize(window,
+ window->saved_allocation.width,
+ window->saved_allocation.height);
+ }
+}
+
+void
+window_set_user_data(struct window *window, void *data)
+{
+ window->user_data = data;
+}
+
+void *
+window_get_user_data(struct window *window)
+{
+ return window->user_data;
+}
+
+void
+window_set_key_handler(struct window *window,
+ window_key_handler_t handler)
+{
+ window->key_handler = handler;
+}
+
+void
+window_set_keyboard_focus_handler(struct window *window,
+ window_keyboard_focus_handler_t handler)
+{
+ window->keyboard_focus_handler = handler;
+}
+
+void
+window_set_data_handler(struct window *window, window_data_handler_t handler)
+{
+ window->data_handler = handler;
+}
+
+void
+window_set_drop_handler(struct window *window, window_drop_handler_t handler)
+{
+ window->drop_handler = handler;
+}
+
+void
+window_set_close_handler(struct window *window,
+ window_close_handler_t handler)
+{
+ window->close_handler = handler;
+}
+
+void
+window_set_fullscreen_handler(struct window *window,
+ window_fullscreen_handler_t handler)
+{
+ window->fullscreen_handler = handler;
+}
+
+void
+window_set_output_handler(struct window *window,
+ window_output_handler_t handler)
+{
+ window->output_handler = handler;
+}
+
+void
+window_set_title(struct window *window, const char *title)
+{
+ free(window->title);
+ window->title = strdup(title);
+ if (window->shell_surface)
+ wl_shell_surface_set_title(window->shell_surface, title);
+}
+
+const char *
+window_get_title(struct window *window)
+{
+ return window->title;
+}
+
+void
+window_set_text_cursor_position(struct window *window, int32_t x, int32_t y)
+{
+ struct text_cursor_position *text_cursor_position =
+ window->display->text_cursor_position;
+
+ if (!text_cursor_position)
+ return;
+
+ text_cursor_position_notify(text_cursor_position,
+ window->main_surface->surface,
+ wl_fixed_from_int(x),
+ wl_fixed_from_int(y));
+}
+
+void
+window_damage(struct window *window, int32_t x, int32_t y,
+ int32_t width, int32_t height)
+{
+ wl_surface_damage(window->main_surface->surface, x, y, width, height);
+}
+
+static void
+surface_enter(void *data,
+ struct wl_surface *wl_surface, struct wl_output *wl_output)
+{
+ struct window *window = data;
+ struct output *output;
+ struct output *output_found = NULL;
+ struct window_output *window_output;
+
+ wl_list_for_each(output, &window->display->output_list, link) {
+ if (output->output == wl_output) {
+ output_found = output;
+ break;
+ }
+ }
+
+ if (!output_found)
+ return;
+
+ window_output = xmalloc(sizeof *window_output);
+ window_output->output = output_found;
+
+ wl_list_insert (&window->window_output_list, &window_output->link);
+
+ if (window->output_handler)
+ window->output_handler(window, output_found, 1,
+ window->user_data);
+}
+
+static void
+surface_leave(void *data,
+ struct wl_surface *wl_surface, struct wl_output *output)
+{
+ struct window *window = data;
+ struct window_output *window_output;
+ struct window_output *window_output_found = NULL;
+
+ wl_list_for_each(window_output, &window->window_output_list, link) {
+ if (window_output->output->output == output) {
+ window_output_found = window_output;
+ break;
+ }
+ }
+
+ if (window_output_found) {
+ wl_list_remove(&window_output_found->link);
+
+ if (window->output_handler)
+ window->output_handler(window, window_output->output,
+ 0, window->user_data);
+
+ free(window_output_found);
+ }
+}
+
+static const struct wl_surface_listener surface_listener = {
+ surface_enter,
+ surface_leave
+};
+
+static struct surface *
+surface_create(struct window *window)
+{
+ struct display *display = window->display;
+ struct surface *surface;
+
+ surface = xmalloc(sizeof *surface);
+ memset(surface, 0, sizeof *surface);
+ if (!surface)
+ return NULL;
+
+ surface->window = window;
+ surface->surface = wl_compositor_create_surface(display->compositor);
+ surface->buffer_scale = 1;
+ wl_surface_add_listener(surface->surface, &surface_listener, window);
+
+ wl_list_insert(&window->subsurface_list, &surface->link);
+
+ return surface;
+}
+
+static struct window *
+window_create_internal(struct display *display,
+ struct window *parent, int type)
+{
+ struct window *window;
+ struct surface *surface;
+
+ window = xzalloc(sizeof *window);
+ wl_list_init(&window->subsurface_list);
+ window->display = display;
+ window->parent = parent;
+
+ surface = surface_create(window);
+ window->main_surface = surface;
+
+ if (type != TYPE_CUSTOM && display->shell) {
+ window->shell_surface =
+ wl_shell_get_shell_surface(display->shell,
+ surface->surface);
+ fail_on_null(window->shell_surface);
+ }
+
+ window->type = type;
+ window->fullscreen_method = WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT;
+ window->configure_requests = 0;
+ window->preferred_format = WINDOW_PREFERRED_FORMAT_NONE;
+
+ if (display->argb_device)
+#ifdef HAVE_CAIRO_EGL
+ surface->buffer_type = WINDOW_BUFFER_TYPE_EGL_WINDOW;
+#else
+ surface->buffer_type = WINDOW_BUFFER_TYPE_SHM;
+#endif
+ else
+ surface->buffer_type = WINDOW_BUFFER_TYPE_SHM;
+
+ wl_surface_set_user_data(surface->surface, window);
+ wl_list_insert(display->window_list.prev, &window->link);
+ wl_list_init(&window->redraw_task.link);
+
+ if (window->shell_surface) {
+ wl_shell_surface_set_user_data(window->shell_surface, window);
+ wl_shell_surface_add_listener(window->shell_surface,
+ &shell_surface_listener, window);
+ }
+
+ wl_list_init (&window->window_output_list);
+
+ return window;
+}
+
+struct window *
+window_create(struct display *display)
+{
+ return window_create_internal(display, NULL, TYPE_NONE);
+}
+
+struct window *
+window_create_custom(struct display *display)
+{
+ return window_create_internal(display, NULL, TYPE_CUSTOM);
+}
+
+struct window *
+window_create_transient(struct display *display, struct window *parent,
+ int32_t x, int32_t y, uint32_t flags)
+{
+ struct window *window;
+
+ window = window_create_internal(parent->display,
+ parent, TYPE_TRANSIENT);
+
+ window->x = x;
+ window->y = y;
+
+ if (display->shell)
+ wl_shell_surface_set_transient(
+ window->shell_surface,
+ window->parent->main_surface->surface,
+ window->x, window->y, flags);
+
+ return window;
+}
+
+static void
+menu_set_item(struct menu *menu, int sy)
+{
+ int next;
+
+ next = (sy - 8) / 20;
+ if (menu->current != next) {
+ menu->current = next;
+ widget_schedule_redraw(menu->widget);
+ }
+}
+
+static int
+menu_motion_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ float x, float y, void *data)
+{
+ struct menu *menu = data;
+
+ if (widget == menu->widget)
+ menu_set_item(data, y);
+
+ return CURSOR_LEFT_PTR;
+}
+
+static int
+menu_enter_handler(struct widget *widget,
+ struct input *input, float x, float y, void *data)
+{
+ struct menu *menu = data;
+
+ if (widget == menu->widget)
+ menu_set_item(data, y);
+
+ return CURSOR_LEFT_PTR;
+}
+
+static void
+menu_leave_handler(struct widget *widget, struct input *input, void *data)
+{
+ struct menu *menu = data;
+
+ if (widget == menu->widget)
+ menu_set_item(data, -200);
+}
+
+static void
+menu_button_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ uint32_t button, enum wl_pointer_button_state state,
+ void *data)
+
+{
+ struct menu *menu = data;
+
+ if (state == WL_POINTER_BUTTON_STATE_RELEASED &&
+ (menu->release_count > 0 || time - menu->time > 500)) {
+ /* Either relase after press-drag-release or
+ * click-motion-click. */
+ menu->func(menu->window->parent,
+ menu->current, menu->window->parent->user_data);
+ input_ungrab(input);
+ menu_destroy(menu);
+ } else if (state == WL_POINTER_BUTTON_STATE_RELEASED) {
+ menu->release_count++;
+ }
+}
+
+static void
+menu_redraw_handler(struct widget *widget, void *data)
+{
+ cairo_t *cr;
+ const int32_t r = 3, margin = 3;
+ struct menu *menu = data;
+ int32_t width, height, i;
+
+ cr = widget_cairo_create(widget);
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.0);
+ cairo_paint(cr);
+
+ width = widget->allocation.width;
+ height = widget->allocation.height;
+ rounded_rect(cr, 0, 0, width, height, r);
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+ cairo_set_source_rgba(cr, 0.0, 0.0, 0.4, 0.8);
+ cairo_fill(cr);
+
+ for (i = 0; i < menu->count; i++) {
+ if (i == menu->current) {
+ cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
+ cairo_rectangle(cr, margin, i * 20 + margin,
+ width - 2 * margin, 20);
+ cairo_fill(cr);
+ cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
+ cairo_move_to(cr, 10, i * 20 + 16);
+ cairo_show_text(cr, menu->entries[i]);
+ } else {
+ cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
+ cairo_move_to(cr, 10, i * 20 + 16);
+ cairo_show_text(cr, menu->entries[i]);
+ }
+ }
+
+ cairo_destroy(cr);
+}
+
+void
+window_show_menu(struct display *display,
+ struct input *input, uint32_t time, struct window *parent,
+ int32_t x, int32_t y,
+ menu_func_t func, const char **entries, int count)
+{
+ struct window *window;
+ struct menu *menu;
+ const int32_t margin = 3;
+
+ menu = malloc(sizeof *menu);
+ if (!menu)
+ return;
+
+ window = window_create_internal(parent->display, parent, TYPE_MENU);
+ if (!window) {
+ free(menu);
+ return;
+ }
+
+ menu->window = window;
+ menu->widget = window_add_widget(menu->window, menu);
+ window_set_buffer_scale (menu->window, window_get_buffer_scale (parent));
+ window_set_buffer_transform (menu->window, window_get_buffer_transform (parent));
+ menu->entries = entries;
+ menu->count = count;
+ menu->release_count = 0;
+ menu->current = -1;
+ menu->time = time;
+ menu->func = func;
+ menu->input = input;
+ window->type = TYPE_MENU;
+ window->x = x;
+ window->y = y;
+
+ input_ungrab(input);
+ wl_shell_surface_set_popup(window->shell_surface, input->seat,
+ display_get_serial(window->display),
+ window->parent->main_surface->surface,
+ window->x, window->y, 0);
+
+ widget_set_redraw_handler(menu->widget, menu_redraw_handler);
+ widget_set_enter_handler(menu->widget, menu_enter_handler);
+ widget_set_leave_handler(menu->widget, menu_leave_handler);
+ widget_set_motion_handler(menu->widget, menu_motion_handler);
+ widget_set_button_handler(menu->widget, menu_button_handler);
+
+ input_grab(input, menu->widget, 0);
+ window_schedule_resize(window, 200, count * 20 + margin * 2);
+}
+
+void
+window_set_buffer_type(struct window *window, enum window_buffer_type type)
+{
+ window->main_surface->buffer_type = type;
+}
+
+void
+window_set_preferred_format(struct window *window,
+ enum preferred_format format)
+{
+ window->preferred_format = format;
+}
+
+struct widget *
+window_add_subsurface(struct window *window, void *data,
+ enum subsurface_mode default_mode)
+{
+ struct widget *widget;
+ struct surface *surface;
+ struct wl_surface *parent;
+ struct wl_subcompositor *subcompo = window->display->subcompositor;
+
+ surface = surface_create(window);
+ widget = widget_create(window, surface, data);
+ wl_list_init(&widget->link);
+ surface->widget = widget;
+
+ parent = window->main_surface->surface;
+ surface->subsurface = wl_subcompositor_get_subsurface(subcompo,
+ surface->surface,
+ parent);
+ surface->synchronized = 1;
+
+ switch (default_mode) {
+ case SUBSURFACE_SYNCHRONIZED:
+ surface->synchronized_default = 1;
+ break;
+ case SUBSURFACE_DESYNCHRONIZED:
+ surface->synchronized_default = 0;
+ break;
+ default:
+ assert(!"bad enum subsurface_mode");
+ }
+
+ return widget;
+}
+
+static void
+display_handle_geometry(void *data,
+ struct wl_output *wl_output,
+ int x, int y,
+ int physical_width,
+ int physical_height,
+ int subpixel,
+ const char *make,
+ const char *model,
+ int transform)
+{
+ struct output *output = data;
+
+ output->allocation.x = x;
+ output->allocation.y = y;
+ output->transform = transform;
+}
+
+static void
+display_handle_done(void *data,
+ struct wl_output *wl_output)
+{
+}
+
+static void
+display_handle_scale(void *data,
+ struct wl_output *wl_output,
+ int32_t scale)
+{
+ struct output *output = data;
+
+ output->scale = scale;
+}
+
+static void
+display_handle_mode(void *data,
+ struct wl_output *wl_output,
+ uint32_t flags,
+ int width,
+ int height,
+ int refresh)
+{
+ struct output *output = data;
+ struct display *display = output->display;
+
+ if (flags & WL_OUTPUT_MODE_CURRENT) {
+ output->allocation.width = width;
+ output->allocation.height = height;
+ if (display->output_configure_handler)
+ (*display->output_configure_handler)(
+ output, display->user_data);
+ }
+}
+
+static const struct wl_output_listener output_listener = {
+ display_handle_geometry,
+ display_handle_mode,
+ display_handle_done,
+ display_handle_scale
+};
+
+static void
+display_add_output(struct display *d, uint32_t id)
+{
+ struct output *output;
+
+ output = xzalloc(sizeof *output);
+ output->display = d;
+ output->scale = 1;
+ output->output =
+ wl_registry_bind(d->registry, id, &wl_output_interface, 2);
+ wl_list_insert(d->output_list.prev, &output->link);
+
+ wl_output_add_listener(output->output, &output_listener, output);
+}
+
+static void
+output_destroy(struct output *output)
+{
+ if (output->destroy_handler)
+ (*output->destroy_handler)(output, output->user_data);
+
+ wl_output_destroy(output->output);
+ wl_list_remove(&output->link);
+ free(output);
+}
+
+void
+display_set_global_handler(struct display *display,
+ display_global_handler_t handler)
+{
+ struct global *global;
+
+ display->global_handler = handler;
+ if (!handler)
+ return;
+
+ wl_list_for_each(global, &display->global_list, link)
+ display->global_handler(display,
+ global->name, global->interface,
+ global->version, display->user_data);
+}
+
+void
+display_set_output_configure_handler(struct display *display,
+ display_output_handler_t handler)
+{
+ struct output *output;
+
+ display->output_configure_handler = handler;
+ if (!handler)
+ return;
+
+ wl_list_for_each(output, &display->output_list, link) {
+ if (output->allocation.width == 0 &&
+ output->allocation.height == 0)
+ continue;
+
+ (*display->output_configure_handler)(output,
+ display->user_data);
+ }
+}
+
+void
+output_set_user_data(struct output *output, void *data)
+{
+ output->user_data = data;
+}
+
+void *
+output_get_user_data(struct output *output)
+{
+ return output->user_data;
+}
+
+void
+output_set_destroy_handler(struct output *output,
+ display_output_handler_t handler)
+{
+ output->destroy_handler = handler;
+ /* FIXME: implement this, once we have way to remove outputs */
+}
+
+void
+output_get_allocation(struct output *output, struct rectangle *base)
+{
+ struct rectangle allocation = output->allocation;
+
+ switch (output->transform) {
+ case WL_OUTPUT_TRANSFORM_90:
+ case WL_OUTPUT_TRANSFORM_270:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+ /* Swap width and height */
+ allocation.width = output->allocation.height;
+ allocation.height = output->allocation.width;
+ break;
+ }
+
+ *base = allocation;
+}
+
+struct wl_output *
+output_get_wl_output(struct output *output)
+{
+ return output->output;
+}
+
+enum wl_output_transform
+output_get_transform(struct output *output)
+{
+ return output->transform;
+}
+
+uint32_t
+output_get_scale(struct output *output)
+{
+ return output->scale;
+}
+
+static void
+fini_xkb(struct input *input)
+{
+ xkb_state_unref(input->xkb.state);
+ xkb_map_unref(input->xkb.keymap);
+}
+
+#define MAX(a,b) ((a) > (b) ? a : b)
+
+static void
+display_add_input(struct display *d, uint32_t id)
+{
+ struct input *input;
+
+ input = xzalloc(sizeof *input);
+ input->display = d;
+ input->seat = wl_registry_bind(d->registry, id, &wl_seat_interface,
+ MAX(d->seat_version, 3));
+ input->touch_focus = NULL;
+ input->pointer_focus = NULL;
+ input->keyboard_focus = NULL;
+ wl_list_init(&input->touch_point_list);
+ wl_list_insert(d->input_list.prev, &input->link);
+
+ wl_seat_add_listener(input->seat, &seat_listener, input);
+ wl_seat_set_user_data(input->seat, input);
+
+ input->data_device =
+ wl_data_device_manager_get_data_device(d->data_device_manager,
+ input->seat);
+ wl_data_device_add_listener(input->data_device, &data_device_listener,
+ input);
+
+ input->pointer_surface = wl_compositor_create_surface(d->compositor);
+
+ input->repeat_timer_fd = timerfd_create(CLOCK_MONOTONIC,
+ TFD_CLOEXEC | TFD_NONBLOCK);
+ input->repeat_task.run = keyboard_repeat_func;
+ display_watch_fd(d, input->repeat_timer_fd,
+ EPOLLIN, &input->repeat_task);
+}
+
+static void
+input_destroy(struct input *input)
+{
+ input_remove_keyboard_focus(input);
+ input_remove_pointer_focus(input);
+
+ if (input->drag_offer)
+ data_offer_destroy(input->drag_offer);
+
+ if (input->selection_offer)
+ data_offer_destroy(input->selection_offer);
+
+ wl_data_device_destroy(input->data_device);
+
+ if (input->display->seat_version >= 3) {
+ if (input->pointer)
+ wl_pointer_release(input->pointer);
+ if (input->keyboard)
+ wl_keyboard_release(input->keyboard);
+ }
+
+ fini_xkb(input);
+
+ wl_surface_destroy(input->pointer_surface);
+
+ wl_list_remove(&input->link);
+ wl_seat_destroy(input->seat);
+ close(input->repeat_timer_fd);
+ free(input);
+}
+
+static void
+init_workspace_manager(struct display *d, uint32_t id)
+{
+ d->workspace_manager =
+ wl_registry_bind(d->registry, id,
+ &workspace_manager_interface, 1);
+ if (d->workspace_manager != NULL)
+ workspace_manager_add_listener(d->workspace_manager,
+ &workspace_manager_listener,
+ d);
+}
+
+static void
+shm_format(void *data, struct wl_shm *wl_shm, uint32_t format)
+{
+ struct display *d = data;
+
+ if (format == WL_SHM_FORMAT_RGB565)
+ d->has_rgb565 = 1;
+}
+
+struct wl_shm_listener shm_listener = {
+ shm_format
+};
+
+static void
+registry_handle_global(void *data, struct wl_registry *registry, uint32_t id,
+ const char *interface, uint32_t version)
+{
+ struct display *d = data;
+ struct global *global;
+
+ global = xmalloc(sizeof *global);
+ global->name = id;
+ global->interface = strdup(interface);
+ global->version = version;
+ wl_list_insert(d->global_list.prev, &global->link);
+
+ if (strcmp(interface, "wl_compositor") == 0) {
+ d->compositor = wl_registry_bind(registry, id,
+ &wl_compositor_interface, 3);
+ } else if (strcmp(interface, "wl_output") == 0) {
+ display_add_output(d, id);
+ } else if (strcmp(interface, "wl_seat") == 0) {
+ d->seat_version = version;
+ display_add_input(d, id);
+ } else if (strcmp(interface, "wl_shell") == 0) {
+ d->shell = wl_registry_bind(registry,
+ id, &wl_shell_interface, 1);
+ } else if (strcmp(interface, "wl_shm") == 0) {
+ d->shm = wl_registry_bind(registry, id, &wl_shm_interface, 1);
+ wl_shm_add_listener(d->shm, &shm_listener, d);
+ } else if (strcmp(interface, "wl_data_device_manager") == 0) {
+ d->data_device_manager =
+ wl_registry_bind(registry, id,
+ &wl_data_device_manager_interface, 1);
+ } else if (strcmp(interface, "text_cursor_position") == 0) {
+ d->text_cursor_position =
+ wl_registry_bind(registry, id,
+ &text_cursor_position_interface, 1);
+ } else if (strcmp(interface, "workspace_manager") == 0) {
+ init_workspace_manager(d, id);
+ } else if (strcmp(interface, "wl_subcompositor") == 0) {
+ d->subcompositor =
+ wl_registry_bind(registry, id,
+ &wl_subcompositor_interface, 1);
+ }
+
+ if (d->global_handler)
+ d->global_handler(d, id, interface, version, d->user_data);
+}
+
+static void
+registry_handle_global_remove(void *data, struct wl_registry *registry,
+ uint32_t name)
+{
+ struct display *d = data;
+ struct global *global;
+ struct global *tmp;
+
+ wl_list_for_each_safe(global, tmp, &d->global_list, link) {
+ if (global->name != name)
+ continue;
+
+ /* XXX: Should destroy bound globals, and call
+ * the counterpart of display::global_handler
+ */
+ wl_list_remove(&global->link);
+ free(global->interface);
+ free(global);
+ }
+}
+
+void *
+display_bind(struct display *display, uint32_t name,
+ const struct wl_interface *interface, uint32_t version)
+{
+ return wl_registry_bind(display->registry, name, interface, version);
+}
+
+static const struct wl_registry_listener registry_listener = {
+ registry_handle_global,
+ registry_handle_global_remove
+};
+
+#ifdef HAVE_CAIRO_EGL
+static int
+init_egl(struct display *d)
+{
+ EGLint major, minor;
+ EGLint n;
+
+#ifdef USE_CAIRO_GLESV2
+# define GL_BIT EGL_OPENGL_ES2_BIT
+#else
+# define GL_BIT EGL_OPENGL_BIT
+#endif
+
+ static const EGLint argb_cfg_attribs[] = {
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_RED_SIZE, 1,
+ EGL_GREEN_SIZE, 1,
+ EGL_BLUE_SIZE, 1,
+ EGL_ALPHA_SIZE, 1,
+ EGL_DEPTH_SIZE, 1,
+ EGL_RENDERABLE_TYPE, GL_BIT,
+ EGL_NONE
+ };
+
+#ifdef USE_CAIRO_GLESV2
+ static const EGLint context_attribs[] = {
+ EGL_CONTEXT_CLIENT_VERSION, 2,
+ EGL_NONE
+ };
+ EGLint api = EGL_OPENGL_ES_API;
+#else
+ EGLint *context_attribs = NULL;
+ EGLint api = EGL_OPENGL_API;
+#endif
+
+ d->dpy = eglGetDisplay(d->display);
+ if (!eglInitialize(d->dpy, &major, &minor)) {
+ fprintf(stderr, "failed to initialize EGL\n");
+ return -1;
+ }
+
+ if (!eglBindAPI(api)) {
+ fprintf(stderr, "failed to bind EGL client API\n");
+ return -1;
+ }
+
+ if (!eglChooseConfig(d->dpy, argb_cfg_attribs,
+ &d->argb_config, 1, &n) || n != 1) {
+ fprintf(stderr, "failed to choose argb EGL config\n");
+ return -1;
+ }
+
+ d->argb_ctx = eglCreateContext(d->dpy, d->argb_config,
+ EGL_NO_CONTEXT, context_attribs);
+ if (d->argb_ctx == NULL) {
+ fprintf(stderr, "failed to create EGL context\n");
+ return -1;
+ }
+
+ d->argb_device = cairo_egl_device_create(d->dpy, d->argb_ctx);
+ if (cairo_device_status(d->argb_device) != CAIRO_STATUS_SUCCESS) {
+ fprintf(stderr, "failed to get cairo EGL argb device\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void
+fini_egl(struct display *display)
+{
+ cairo_device_destroy(display->argb_device);
+
+ eglMakeCurrent(display->dpy, EGL_NO_SURFACE, EGL_NO_SURFACE,
+ EGL_NO_CONTEXT);
+
+ eglTerminate(display->dpy);
+ eglReleaseThread();
+}
+#endif
+
+static void
+init_dummy_surface(struct display *display)
+{
+ int len;
+ void *data;
+
+ len = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, 1);
+ data = malloc(len);
+ display->dummy_surface =
+ cairo_image_surface_create_for_data(data, CAIRO_FORMAT_ARGB32,
+ 1, 1, len);
+ display->dummy_surface_data = data;
+}
+
+static void
+handle_display_data(struct task *task, uint32_t events)
+{
+ struct display *display =
+ container_of(task, struct display, display_task);
+ struct epoll_event ep;
+ int ret;
+
+ display->display_fd_events = events;
+
+ if (events & EPOLLERR || events & EPOLLHUP) {
+ display_exit(display);
+ return;
+ }
+
+ if (events & EPOLLIN) {
+ ret = wl_display_dispatch(display->display);
+ if (ret == -1) {
+ display_exit(display);
+ return;
+ }
+ }
+
+ if (events & EPOLLOUT) {
+ ret = wl_display_flush(display->display);
+ if (ret == 0) {
+ ep.events = EPOLLIN | EPOLLERR | EPOLLHUP;
+ ep.data.ptr = &display->display_task;
+ epoll_ctl(display->epoll_fd, EPOLL_CTL_MOD,
+ display->display_fd, &ep);
+ } else if (ret == -1 && errno != EAGAIN) {
+ display_exit(display);
+ return;
+ }
+ }
+}
+
+static void
+log_handler(const char *format, va_list args)
+{
+ vfprintf(stderr, format, args);
+}
+
+struct display *
+display_create(int *argc, char *argv[])
+{
+ struct display *d;
+
+ wl_log_set_handler_client(log_handler);
+
+ d = zalloc(sizeof *d);
+ if (d == NULL)
+ return NULL;
+
+ d->display = wl_display_connect(NULL);
+ if (d->display == NULL) {
+ fprintf(stderr, "failed to connect to Wayland display: %m\n");
+ free(d);
+ return NULL;
+ }
+
+ d->xkb_context = xkb_context_new(0);
+ if (d->xkb_context == NULL) {
+ fprintf(stderr, "Failed to create XKB context\n");
+ free(d);
+ return NULL;
+ }
+
+ d->epoll_fd = os_epoll_create_cloexec();
+ d->display_fd = wl_display_get_fd(d->display);
+ d->display_task.run = handle_display_data;
+ display_watch_fd(d, d->display_fd, EPOLLIN | EPOLLERR | EPOLLHUP,
+ &d->display_task);
+
+ wl_list_init(&d->deferred_list);
+ wl_list_init(&d->input_list);
+ wl_list_init(&d->output_list);
+ wl_list_init(&d->global_list);
+
+ d->workspace = 0;
+ d->workspace_count = 1;
+
+ d->registry = wl_display_get_registry(d->display);
+ wl_registry_add_listener(d->registry, &registry_listener, d);
+
+ if (wl_display_dispatch(d->display) < 0) {
+ fprintf(stderr, "Failed to process Wayland connection: %m\n");
+ return NULL;
+ }
+
+#ifdef HAVE_CAIRO_EGL
+ if (init_egl(d) < 0)
+ fprintf(stderr, "EGL does not seem to work, "
+ "falling back to software rendering and wl_shm.\n");
+#endif
+
+ create_cursors(d);
+
+ d->theme = theme_create();
+
+ wl_list_init(&d->window_list);
+
+ init_dummy_surface(d);
+
+ return d;
+}
+
+static void
+display_destroy_outputs(struct display *display)
+{
+ struct output *tmp;
+ struct output *output;
+
+ wl_list_for_each_safe(output, tmp, &display->output_list, link)
+ output_destroy(output);
+}
+
+static void
+display_destroy_inputs(struct display *display)
+{
+ struct input *tmp;
+ struct input *input;
+
+ wl_list_for_each_safe(input, tmp, &display->input_list, link)
+ input_destroy(input);
+}
+
+void
+display_destroy(struct display *display)
+{
+ if (!wl_list_empty(&display->window_list))
+ fprintf(stderr, "toytoolkit warning: %d windows exist.\n",
+ wl_list_length(&display->window_list));
+
+ if (!wl_list_empty(&display->deferred_list))
+ fprintf(stderr, "toytoolkit warning: deferred tasks exist.\n");
+
+ cairo_surface_destroy(display->dummy_surface);
+ free(display->dummy_surface_data);
+
+ display_destroy_outputs(display);
+ display_destroy_inputs(display);
+
+ xkb_context_unref(display->xkb_context);
+
+ theme_destroy(display->theme);
+ destroy_cursors(display);
+
+#ifdef HAVE_CAIRO_EGL
+ if (display->argb_device)
+ fini_egl(display);
+#endif
+
+ if (display->subcompositor)
+ wl_subcompositor_destroy(display->subcompositor);
+
+ if (display->shell)
+ wl_shell_destroy(display->shell);
+
+ if (display->shm)
+ wl_shm_destroy(display->shm);
+
+ if (display->data_device_manager)
+ wl_data_device_manager_destroy(display->data_device_manager);
+
+ wl_compositor_destroy(display->compositor);
+ wl_registry_destroy(display->registry);
+
+ close(display->epoll_fd);
+
+ if (!(display->display_fd_events & EPOLLERR) &&
+ !(display->display_fd_events & EPOLLHUP))
+ wl_display_flush(display->display);
+
+ wl_display_disconnect(display->display);
+ free(display);
+}
+
+void
+display_set_user_data(struct display *display, void *data)
+{
+ display->user_data = data;
+}
+
+void *
+display_get_user_data(struct display *display)
+{
+ return display->user_data;
+}
+
+struct wl_display *
+display_get_display(struct display *display)
+{
+ return display->display;
+}
+
+int
+display_has_subcompositor(struct display *display)
+{
+ if (display->subcompositor)
+ return 1;
+
+ wl_display_roundtrip(display->display);
+
+ return display->subcompositor != NULL;
+}
+
+cairo_device_t *
+display_get_cairo_device(struct display *display)
+{
+ return display->argb_device;
+}
+
+struct output *
+display_get_output(struct display *display)
+{
+ return container_of(display->output_list.next, struct output, link);
+}
+
+struct wl_compositor *
+display_get_compositor(struct display *display)
+{
+ return display->compositor;
+}
+
+uint32_t
+display_get_serial(struct display *display)
+{
+ return display->serial;
+}
+
+EGLDisplay
+display_get_egl_display(struct display *d)
+{
+ return d->dpy;
+}
+
+struct wl_data_source *
+display_create_data_source(struct display *display)
+{
+ return wl_data_device_manager_create_data_source(display->data_device_manager);
+}
+
+EGLConfig
+display_get_argb_egl_config(struct display *d)
+{
+ return d->argb_config;
+}
+
+struct wl_shell *
+display_get_shell(struct display *display)
+{
+ return display->shell;
+}
+
+int
+display_acquire_window_surface(struct display *display,
+ struct window *window,
+ EGLContext ctx)
+{
+ struct surface *surface = window->main_surface;
+
+ if (surface->buffer_type != WINDOW_BUFFER_TYPE_EGL_WINDOW)
+ return -1;
+
+ widget_get_cairo_surface(window->main_surface->widget);
+ return surface->toysurface->acquire(surface->toysurface, ctx);
+}
+
+void
+display_release_window_surface(struct display *display,
+ struct window *window)
+{
+ struct surface *surface = window->main_surface;
+
+ if (surface->buffer_type != WINDOW_BUFFER_TYPE_EGL_WINDOW)
+ return;
+
+ surface->toysurface->release(surface->toysurface);
+}
+
+void
+display_defer(struct display *display, struct task *task)
+{
+ wl_list_insert(&display->deferred_list, &task->link);
+}
+
+void
+display_watch_fd(struct display *display,
+ int fd, uint32_t events, struct task *task)
+{
+ struct epoll_event ep;
+
+ ep.events = events;
+ ep.data.ptr = task;
+ epoll_ctl(display->epoll_fd, EPOLL_CTL_ADD, fd, &ep);
+}
+
+void
+display_unwatch_fd(struct display *display, int fd)
+{
+ epoll_ctl(display->epoll_fd, EPOLL_CTL_DEL, fd, NULL);
+}
+
+void
+display_run(struct display *display)
+{
+ struct task *task;
+ struct epoll_event ep[16];
+ int i, count, ret;
+
+ display->running = 1;
+ while (1) {
+ while (!wl_list_empty(&display->deferred_list)) {
+ task = container_of(display->deferred_list.prev,
+ struct task, link);
+ wl_list_remove(&task->link);
+ task->run(task, 0);
+ }
+
+ wl_display_dispatch_pending(display->display);
+
+ if (!display->running)
+ break;
+
+ ret = wl_display_flush(display->display);
+ if (ret < 0 && errno == EAGAIN) {
+ ep[0].events =
+ EPOLLIN | EPOLLOUT | EPOLLERR | EPOLLHUP;
+ ep[0].data.ptr = &display->display_task;
+
+ epoll_ctl(display->epoll_fd, EPOLL_CTL_MOD,
+ display->display_fd, &ep[0]);
+ } else if (ret < 0) {
+ break;
+ }
+
+ count = epoll_wait(display->epoll_fd,
+ ep, ARRAY_LENGTH(ep), -1);
+ for (i = 0; i < count; i++) {
+ task = ep[i].data.ptr;
+ task->run(task, ep[i].events);
+ }
+ }
+}
+
+void
+display_exit(struct display *display)
+{
+ display->running = 0;
+}
+
+void
+keysym_modifiers_add(struct wl_array *modifiers_map,
+ const char *name)
+{
+ size_t len = strlen(name) + 1;
+ char *p;
+
+ p = wl_array_add(modifiers_map, len);
+
+ if (p == NULL)
+ return;
+
+ strncpy(p, name, len);
+}
+
+static xkb_mod_index_t
+keysym_modifiers_get_index(struct wl_array *modifiers_map,
+ const char *name)
+{
+ xkb_mod_index_t index = 0;
+ char *p = modifiers_map->data;
+
+ while ((const char *)p < (const char *)(modifiers_map->data + modifiers_map->size)) {
+ if (strcmp(p, name) == 0)
+ return index;
+
+ index++;
+ p += strlen(p) + 1;
+ }
+
+ return XKB_MOD_INVALID;
+}
+
+xkb_mod_mask_t
+keysym_modifiers_get_mask(struct wl_array *modifiers_map,
+ const char *name)
+{
+ xkb_mod_index_t index = keysym_modifiers_get_index(modifiers_map, name);
+
+ if (index == XKB_MOD_INVALID)
+ return XKB_MOD_INVALID;
+
+ return 1 << index;
+}
+
+void *
+fail_on_null(void *p)
+{
+ if (p == NULL) {
+ fprintf(stderr, "%s: out of memory\n", program_invocation_short_name);
+ exit(EXIT_FAILURE);
+ }
+
+ return p;
+}
+
+void *
+xmalloc(size_t s)
+{
+ return fail_on_null(malloc(s));
+}
+
+void *
+xzalloc(size_t s)
+{
+ return fail_on_null(zalloc(s));
+}
+
+char *
+xstrdup(const char *s)
+{
+ return fail_on_null(strdup(s));
+}
diff --git a/clients/window.h b/clients/window.h
new file mode 100644
index 00000000..4427ab5a
--- /dev/null
+++ b/clients/window.h
@@ -0,0 +1,599 @@
+/*
+ * Copyright © 2008 Kristian Høgsberg
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * 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.
+ */
+
+#ifndef _WINDOW_H_
+#define _WINDOW_H_
+
+#include <xkbcommon/xkbcommon.h>
+#include <wayland-client.h>
+#include <cairo.h>
+#include "../shared/config-parser.h"
+#include "../shared/zalloc.h"
+#include "subsurface-client-protocol.h"
+
+#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0])
+
+#define container_of(ptr, type, member) ({ \
+ const __typeof__( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - offsetof(type,member) );})
+
+struct window;
+struct widget;
+struct display;
+struct input;
+struct output;
+
+struct task {
+ void (*run)(struct task *task, uint32_t events);
+ struct wl_list link;
+};
+
+struct rectangle {
+ int32_t x;
+ int32_t y;
+ int32_t width;
+ int32_t height;
+};
+
+void *
+fail_on_null(void *p);
+void *
+xmalloc(size_t s);
+void *
+xzalloc(size_t s);
+char *
+xstrdup(const char *s);
+
+struct display *
+display_create(int *argc, char *argv[]);
+
+void
+display_destroy(struct display *display);
+
+void
+display_set_user_data(struct display *display, void *data);
+
+void *
+display_get_user_data(struct display *display);
+
+struct wl_display *
+display_get_display(struct display *display);
+
+int
+display_has_subcompositor(struct display *display);
+
+cairo_device_t *
+display_get_cairo_device(struct display *display);
+
+struct wl_compositor *
+display_get_compositor(struct display *display);
+
+struct wl_shell *
+display_get_shell(struct display *display);
+
+struct output *
+display_get_output(struct display *display);
+
+uint32_t
+display_get_serial(struct display *display);
+
+typedef void (*display_global_handler_t)(struct display *display,
+ uint32_t name,
+ const char *interface,
+ uint32_t version, void *data);
+
+void
+display_set_global_handler(struct display *display,
+ display_global_handler_t handler);
+void *
+display_bind(struct display *display, uint32_t name,
+ const struct wl_interface *interface, uint32_t version);
+
+typedef void (*display_output_handler_t)(struct output *output, void *data);
+
+/*
+ * The output configure handler is called, when a new output is connected
+ * and we know its current mode, or when the current mode changes.
+ * Test and set the output user data in your handler to know, if the
+ * output is new. Note: 'data' in the configure handler is the display
+ * user data.
+ */
+void
+display_set_output_configure_handler(struct display *display,
+ display_output_handler_t handler);
+
+struct wl_data_source *
+display_create_data_source(struct display *display);
+
+#ifdef EGL_NO_DISPLAY
+EGLDisplay
+display_get_egl_display(struct display *d);
+
+EGLConfig
+display_get_argb_egl_config(struct display *d);
+
+int
+display_acquire_window_surface(struct display *display,
+ struct window *window,
+ EGLContext ctx);
+void
+display_release_window_surface(struct display *display,
+ struct window *window);
+#endif
+
+#define SURFACE_OPAQUE 0x01
+#define SURFACE_SHM 0x02
+
+#define SURFACE_HINT_RESIZE 0x10
+
+#define SURFACE_HINT_RGB565 0x100
+
+cairo_surface_t *
+display_create_surface(struct display *display,
+ struct wl_surface *surface,
+ struct rectangle *rectangle,
+ uint32_t flags);
+
+struct wl_buffer *
+display_get_buffer_for_surface(struct display *display,
+ cairo_surface_t *surface);
+
+struct wl_cursor_image *
+display_get_pointer_image(struct display *display, int pointer);
+
+void
+display_defer(struct display *display, struct task *task);
+
+void
+display_watch_fd(struct display *display,
+ int fd, uint32_t events, struct task *task);
+
+void
+display_unwatch_fd(struct display *display, int fd);
+
+void
+display_run(struct display *d);
+
+void
+display_exit(struct display *d);
+
+enum cursor_type {
+ CURSOR_BOTTOM_LEFT,
+ CURSOR_BOTTOM_RIGHT,
+ CURSOR_BOTTOM,
+ CURSOR_DRAGGING,
+ CURSOR_LEFT_PTR,
+ CURSOR_LEFT,
+ CURSOR_RIGHT,
+ CURSOR_TOP_LEFT,
+ CURSOR_TOP_RIGHT,
+ CURSOR_TOP,
+ CURSOR_IBEAM,
+ CURSOR_HAND1,
+ CURSOR_WATCH,
+
+ CURSOR_BLANK
+};
+
+typedef void (*window_key_handler_t)(struct window *window, struct input *input,
+ uint32_t time, uint32_t key, uint32_t unicode,
+ enum wl_keyboard_key_state state, void *data);
+
+typedef void (*window_keyboard_focus_handler_t)(struct window *window,
+ struct input *device, void *data);
+
+typedef void (*window_data_handler_t)(struct window *window,
+ struct input *input,
+ float x, float y,
+ const char **types,
+ void *data);
+
+typedef void (*window_drop_handler_t)(struct window *window,
+ struct input *input,
+ int32_t x, int32_t y, void *data);
+
+typedef void (*window_close_handler_t)(struct window *window, void *data);
+typedef void (*window_fullscreen_handler_t)(struct window *window, void *data);
+
+typedef void (*window_output_handler_t)(struct window *window, struct output *output,
+ int enter, void *data);
+
+typedef void (*widget_resize_handler_t)(struct widget *widget,
+ int32_t width, int32_t height,
+ void *data);
+typedef void (*widget_redraw_handler_t)(struct widget *widget, void *data);
+
+typedef int (*widget_enter_handler_t)(struct widget *widget,
+ struct input *input,
+ float x, float y, void *data);
+typedef void (*widget_leave_handler_t)(struct widget *widget,
+ struct input *input, void *data);
+typedef int (*widget_motion_handler_t)(struct widget *widget,
+ struct input *input, uint32_t time,
+ float x, float y, void *data);
+typedef void (*widget_button_handler_t)(struct widget *widget,
+ struct input *input, uint32_t time,
+ uint32_t button,
+ enum wl_pointer_button_state state,
+ void *data);
+typedef void (*widget_touch_down_handler_t)(struct widget *widget,
+ struct input *input,
+ uint32_t serial,
+ uint32_t time,
+ int32_t id,
+ float x,
+ float y,
+ void *data);
+typedef void (*widget_touch_up_handler_t)(struct widget *widget,
+ struct input *input,
+ uint32_t serial,
+ uint32_t time,
+ int32_t id,
+ void *data);
+typedef void (*widget_touch_motion_handler_t)(struct widget *widget,
+ struct input *input,
+ uint32_t time,
+ int32_t id,
+ float x,
+ float y,
+ void *data);
+typedef void (*widget_touch_frame_handler_t)(struct widget *widget,
+ struct input *input, void *data);
+typedef void (*widget_touch_cancel_handler_t)(struct widget *widget,
+ struct input *input, void *data);
+typedef void (*widget_axis_handler_t)(struct widget *widget,
+ struct input *input, uint32_t time,
+ uint32_t axis,
+ wl_fixed_t value,
+ void *data);
+
+struct window *
+window_create(struct display *display);
+struct window *
+window_create_transient(struct display *display, struct window *parent,
+ int32_t x, int32_t y, uint32_t flags);
+struct window *
+window_create_custom(struct display *display);
+
+int
+window_has_focus(struct window *window);
+
+typedef void (*menu_func_t)(struct window *window, int index, void *data);
+
+void
+window_show_menu(struct display *display,
+ struct input *input, uint32_t time, struct window *parent,
+ int32_t x, int32_t y,
+ menu_func_t func, const char **entries, int count);
+
+void
+window_show_frame_menu(struct window *window,
+ struct input *input, uint32_t time);
+
+int
+window_get_buffer_transform(struct window *window);
+
+void
+window_set_buffer_transform(struct window *window,
+ enum wl_output_transform transform);
+
+uint32_t
+window_get_buffer_scale(struct window *window);
+
+void
+window_set_buffer_scale(struct window *window,
+ int32_t scale);
+
+uint32_t
+window_get_output_scale(struct window *window);
+
+void
+window_destroy(struct window *window);
+
+struct widget *
+window_add_widget(struct window *window, void *data);
+
+enum subsurface_mode {
+ SUBSURFACE_SYNCHRONIZED,
+ SUBSURFACE_DESYNCHRONIZED
+};
+
+struct widget *
+window_add_subsurface(struct window *window, void *data,
+ enum subsurface_mode default_mode);
+
+typedef void (*data_func_t)(void *data, size_t len,
+ int32_t x, int32_t y, void *user_data);
+
+struct display *
+window_get_display(struct window *window);
+void
+window_move(struct window *window, struct input *input, uint32_t time);
+void
+window_touch_move(struct window *window, struct input *input, uint32_t time);
+void
+window_get_allocation(struct window *window, struct rectangle *allocation);
+void
+window_schedule_redraw(struct window *window);
+void
+window_schedule_resize(struct window *window, int width, int height);
+
+void
+window_damage(struct window *window, int32_t x, int32_t y,
+ int32_t width, int32_t height);
+
+cairo_surface_t *
+window_get_surface(struct window *window);
+
+struct wl_surface *
+window_get_wl_surface(struct window *window);
+
+struct wl_shell_surface *
+window_get_wl_shell_surface(struct window *window);
+
+enum window_buffer_type {
+ WINDOW_BUFFER_TYPE_EGL_WINDOW,
+ WINDOW_BUFFER_TYPE_SHM,
+};
+
+void
+display_surface_damage(struct display *display, cairo_surface_t *cairo_surface,
+ int32_t x, int32_t y, int32_t width, int32_t height);
+
+void
+window_set_buffer_type(struct window *window, enum window_buffer_type type);
+
+int
+window_is_fullscreen(struct window *window);
+
+void
+window_set_fullscreen(struct window *window, int fullscreen);
+
+void
+window_set_fullscreen_method(struct window *window,
+ enum wl_shell_surface_fullscreen_method method);
+int
+window_is_maximized(struct window *window);
+
+void
+window_set_maximized(struct window *window, int maximized);
+
+void
+window_set_user_data(struct window *window, void *data);
+
+void *
+window_get_user_data(struct window *window);
+
+void
+window_set_key_handler(struct window *window,
+ window_key_handler_t handler);
+
+void
+window_set_keyboard_focus_handler(struct window *window,
+ window_keyboard_focus_handler_t handler);
+
+void
+window_set_data_handler(struct window *window,
+ window_data_handler_t handler);
+
+void
+window_set_drop_handler(struct window *window,
+ window_drop_handler_t handler);
+
+void
+window_set_close_handler(struct window *window,
+ window_close_handler_t handler);
+void
+window_set_fullscreen_handler(struct window *window,
+ window_fullscreen_handler_t handler);
+void
+window_set_output_handler(struct window *window,
+ window_output_handler_t handler);
+
+void
+window_set_title(struct window *window, const char *title);
+
+const char *
+window_get_title(struct window *window);
+
+void
+window_set_text_cursor_position(struct window *window, int32_t x, int32_t y);
+
+enum preferred_format {
+ WINDOW_PREFERRED_FORMAT_NONE,
+ WINDOW_PREFERRED_FORMAT_RGB565
+};
+
+void
+window_set_preferred_format(struct window *window,
+ enum preferred_format format);
+
+int
+widget_set_tooltip(struct widget *parent, char *entry, float x, float y);
+
+void
+widget_destroy_tooltip(struct widget *parent);
+
+struct widget *
+widget_add_widget(struct widget *parent, void *data);
+
+void
+widget_destroy(struct widget *widget);
+void
+widget_set_default_cursor(struct widget *widget, int cursor);
+void
+widget_get_allocation(struct widget *widget, struct rectangle *allocation);
+
+void
+widget_set_allocation(struct widget *widget,
+ int32_t x, int32_t y, int32_t width, int32_t height);
+void
+widget_set_size(struct widget *widget, int32_t width, int32_t height);
+void
+widget_set_transparent(struct widget *widget, int transparent);
+void
+widget_schedule_resize(struct widget *widget, int32_t width, int32_t height);
+
+void *
+widget_get_user_data(struct widget *widget);
+
+cairo_t *
+widget_cairo_create(struct widget *widget);
+
+struct wl_surface *
+widget_get_wl_surface(struct widget *widget);
+
+uint32_t
+widget_get_last_time(struct widget *widget);
+
+void
+widget_input_region_add(struct widget *widget, const struct rectangle *rect);
+
+void
+widget_set_redraw_handler(struct widget *widget,
+ widget_redraw_handler_t handler);
+void
+widget_set_resize_handler(struct widget *widget,
+ widget_resize_handler_t handler);
+void
+widget_set_enter_handler(struct widget *widget,
+ widget_enter_handler_t handler);
+void
+widget_set_leave_handler(struct widget *widget,
+ widget_leave_handler_t handler);
+void
+widget_set_motion_handler(struct widget *widget,
+ widget_motion_handler_t handler);
+void
+widget_set_button_handler(struct widget *widget,
+ widget_button_handler_t handler);
+void
+widget_set_touch_down_handler(struct widget *widget,
+ widget_touch_down_handler_t handler);
+void
+widget_set_touch_up_handler(struct widget *widget,
+ widget_touch_up_handler_t handler);
+void
+widget_set_touch_motion_handler(struct widget *widget,
+ widget_touch_motion_handler_t handler);
+void
+widget_set_touch_frame_handler(struct widget *widget,
+ widget_touch_frame_handler_t handler);
+void
+widget_set_touch_cancel_handler(struct widget *widget,
+ widget_touch_cancel_handler_t handler);
+void
+widget_set_axis_handler(struct widget *widget,
+ widget_axis_handler_t handler);
+void
+widget_schedule_redraw(struct widget *widget);
+
+struct widget *
+frame_create(struct window *window, void *data);
+
+void
+frame_set_child_size(struct widget *widget, int child_width, int child_height);
+
+void
+input_set_pointer_image(struct input *input, int pointer);
+
+void
+input_get_position(struct input *input, int32_t *x, int32_t *y);
+
+#define MOD_SHIFT_MASK 0x01
+#define MOD_ALT_MASK 0x02
+#define MOD_CONTROL_MASK 0x04
+
+uint32_t
+input_get_modifiers(struct input *input);
+
+void
+input_grab(struct input *input, struct widget *widget, uint32_t button);
+
+void
+input_ungrab(struct input *input);
+
+struct widget *
+input_get_focus_widget(struct input *input);
+
+struct display *
+input_get_display(struct input *input);
+
+struct wl_seat *
+input_get_seat(struct input *input);
+
+struct wl_data_device *
+input_get_data_device(struct input *input);
+
+void
+input_set_selection(struct input *input,
+ struct wl_data_source *source, uint32_t time);
+
+void
+input_accept(struct input *input, const char *type);
+
+
+void
+input_receive_drag_data(struct input *input, const char *mime_type,
+ data_func_t func, void *user_data);
+int
+input_receive_drag_data_to_fd(struct input *input,
+ const char *mime_type, int fd);
+
+int
+input_receive_selection_data(struct input *input, const char *mime_type,
+ data_func_t func, void *data);
+int
+input_receive_selection_data_to_fd(struct input *input,
+ const char *mime_type, int fd);
+
+void
+output_set_user_data(struct output *output, void *data);
+
+void *
+output_get_user_data(struct output *output);
+
+void
+output_set_destroy_handler(struct output *output,
+ display_output_handler_t handler);
+
+void
+output_get_allocation(struct output *output, struct rectangle *allocation);
+
+struct wl_output *
+output_get_wl_output(struct output *output);
+
+enum wl_output_transform
+output_get_transform(struct output *output);
+
+uint32_t
+output_get_scale(struct output *output);
+
+void
+keysym_modifiers_add(struct wl_array *modifiers_map,
+ const char *name);
+
+xkb_mod_mask_t
+keysym_modifiers_get_mask(struct wl_array *modifiers_map,
+ const char *name);
+
+#endif
diff --git a/clients/wscreensaver-glue.c b/clients/wscreensaver-glue.c
new file mode 100644
index 00000000..55d0a8c7
--- /dev/null
+++ b/clients/wscreensaver-glue.c
@@ -0,0 +1,148 @@
+/*
+ * Copyright © 2011 Collabora, Ltd.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * 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.
+ */
+
+#include "wscreensaver-glue.h"
+
+double frand(double f)
+{
+ double r = random();
+ return r * f / (double)RAND_MAX;
+}
+
+void clear_gl_error(void)
+{
+ while (glGetError() != GL_NO_ERROR)
+ ;
+}
+
+void check_gl_error(const char *msg)
+{
+ const char *emsg;
+ int err = glGetError();
+
+ switch (err)
+ {
+ case GL_NO_ERROR:
+ return;
+
+ #define ERR(tok) case tok: emsg = #tok; break;
+ ERR(GL_INVALID_ENUM)
+ ERR(GL_INVALID_VALUE)
+ ERR(GL_INVALID_OPERATION)
+ ERR(GL_STACK_OVERFLOW)
+ ERR(GL_STACK_UNDERFLOW)
+ ERR(GL_OUT_OF_MEMORY)
+ #undef ERR
+
+ default:
+ fprintf(stderr, "%s: %s: unknown GL error 0x%04x\n",
+ progname, msg, err);
+ exit(1);
+ }
+
+ fprintf(stderr, "%s: %s: GL error %s\n", progname, msg, emsg);
+ exit(1);
+}
+
+static void
+read_xpm_color(uint32_t *ctable, const char *line)
+{
+ unsigned char key;
+ char cstr[10];
+ char *end;
+ uint32_t value;
+
+ if (sscanf(line, "%1c c %9s", &key, cstr) < 2) {
+ fprintf(stderr, "%s: error in XPM color definition '%s'\n",
+ progname, line);
+ return;
+ }
+
+ value = strtol(&cstr[1], &end, 16);
+
+ if (strcmp(cstr, "None") == 0)
+ ctable[key] = 0x00000000;
+ else if (cstr[0] != '#' || !(cstr[1] != '\0' && *end == '\0')) {
+ fprintf(stderr, "%s: error interpreting XPM color '%s'\n",
+ progname, cstr);
+ return;
+ } else {
+ ctable[key] = value | 0xff000000;
+ }
+}
+
+static void
+read_xpm_row(char *data, const char *line, uint32_t *ctable, int width)
+{
+ uint32_t *pixel = (uint32_t *)data;
+ uint8_t *p = (uint8_t *)line;
+ int i;
+
+ for (i = 0; i < width; ++i)
+ pixel[i] = ctable[p[i]];
+}
+
+XImage *xpm_to_ximage(char **xpm_data)
+{
+ XImage *xi;
+ int colors;
+ int cpp;
+ int i;
+ uint32_t ctable[256] = { 0 };
+
+ xi = malloc(sizeof *xi);
+ if (!xi)
+ return NULL;
+ xi->data = NULL;
+
+ if (sscanf(xpm_data[0], "%d %d %d %d", &xi->width,
+ &xi->height, &colors, &cpp) < 4)
+ goto errout;
+
+ if (xi->width < 1 || xi->height < 1 || cpp != 1)
+ goto errout;
+
+ xi->bytes_per_line = xi->width * sizeof(uint32_t);
+ xi->data = malloc(xi->height * xi->bytes_per_line);
+ if (!xi->data)
+ goto errout;
+
+ for (i = 0; i < colors; ++i)
+ read_xpm_color(ctable, xpm_data[i + 1]);
+
+ for (i = 0; i < xi->height; ++i)
+ read_xpm_row(xi->data + i * xi->bytes_per_line,
+ xpm_data[i + colors + 1], ctable, xi->width);
+
+ return xi;
+
+errout:
+ fprintf(stderr, "%s: error processing XPM data.\n", progname);
+ XDestroyImage(xi);
+ return NULL;
+}
+
+void XDestroyImage(XImage *xi)
+{
+ free(xi->data);
+ free(xi);
+}
diff --git a/clients/wscreensaver-glue.h b/clients/wscreensaver-glue.h
new file mode 100644
index 00000000..e07915f9
--- /dev/null
+++ b/clients/wscreensaver-glue.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright © 2011 Collabora, Ltd.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * 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.
+ */
+
+#ifndef WSCREENSAVER_GLUE_H
+#define WSCREENSAVER_GLUE_H
+
+/*
+ * This file is glue, that tries to avoid changing glmatrix.c from the
+ * original too much, hopefully easing the porting of other (GL)
+ * xscreensaver "hacks".
+ */
+
+#include "wscreensaver.h"
+
+#include <stdbool.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <time.h>
+#include <math.h>
+#include <assert.h>
+
+#include <GL/gl.h>
+#include <GL/glu.h>
+
+#include "window.h"
+
+#define ENTRYPOINT static
+
+typedef bool Bool;
+#define True true
+#define False false
+
+typedef struct ModeInfo ModeInfo;
+
+#define MI_DISPLAY(mi) NULL
+#define MI_WINDOW(mi) (mi)
+#define MI_SCREEN(mi) ((mi)->instance_number)
+#define MI_WIDTH(mi) ((mi)->width)
+#define MI_HEIGHT(mi) ((mi)->height)
+#define MI_IS_WIREFRAME(mi) 0
+#define MI_NUM_SCREENS(mi) 16
+
+typedef EGLContext GLXContext;
+
+double frand(double f);
+void clear_gl_error(void);
+void check_gl_error(const char *msg);
+
+static inline void
+glXMakeCurrent(void *dummy, ModeInfo *mi, EGLContext ctx)
+{
+ assert(mi->eglctx == ctx);
+}
+
+static inline void
+glXSwapBuffers(void *dummy, ModeInfo *mi)
+{
+ mi->swap_buffers = 1;
+}
+
+static inline void
+do_fps(ModeInfo *mi)
+{
+}
+
+/* just enough XImage to satisfy glmatrix.c */
+
+typedef struct _XImage {
+ int width;
+ int height;
+ char *data;
+ int bytes_per_line;
+} XImage;
+
+XImage *xpm_to_ximage(char **xpm_data);
+void XDestroyImage(XImage *xi);
+
+static inline unsigned long
+XGetPixel(XImage *xi, int x, int y)
+{
+ return *(uint32_t *)(xi->data + xi->bytes_per_line * y + 4 * x);
+}
+
+static inline void
+XPutPixel(XImage *xi, int x, int y, unsigned long pixel)
+{
+ *(uint32_t *)(xi->data + xi->bytes_per_line * y + 4 * x) = pixel;
+}
+
+/*
+ * override glViewport from the plugin, so we can set it up properly
+ * rendering to a regular decorated Wayland window.
+ */
+#ifdef glViewport
+#undef glViewport
+#endif
+#define glViewport(x,y,w,h) do {} while (0)
+
+#endif /* WSCREENSAVER_GLUE_H */
diff --git a/clients/wscreensaver.c b/clients/wscreensaver.c
new file mode 100644
index 00000000..9a2c47ad
--- /dev/null
+++ b/clients/wscreensaver.c
@@ -0,0 +1,344 @@
+/*
+ * Copyright © 2011 Collabora, Ltd.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * 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.
+ */
+
+#include "../config.h"
+
+#include "wscreensaver.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include <GL/gl.h>
+#include <EGL/eglext.h>
+
+#include <wayland-client.h>
+
+#include "desktop-shell-client-protocol.h"
+#include "window.h"
+
+extern struct wscreensaver_plugin glmatrix_screensaver;
+
+static const struct wscreensaver_plugin * const plugins[] = {
+ &glmatrix_screensaver,
+ NULL
+};
+
+const char *progname = NULL;
+
+static int demo_mode;
+
+struct wscreensaver {
+ struct screensaver *interface;
+
+ struct display *display;
+
+ struct ModeInfo *demomode;
+
+ struct {
+ EGLDisplay display;
+ EGLConfig config;
+ } egl;
+
+ const struct wscreensaver_plugin *plugin;
+};
+
+static void
+frame_callback(void *data, struct wl_callback *callback, uint32_t time)
+{
+ struct ModeInfo *mi = data;
+
+ window_schedule_redraw(mi->window);
+ wl_callback_destroy(callback);
+}
+
+static const struct wl_callback_listener listener = {
+ frame_callback
+};
+
+static void
+redraw_handler(struct widget *widget, void *data)
+{
+ struct ModeInfo *mi = data;
+ struct wscreensaver *wscr = mi->priv;
+ struct rectangle drawarea;
+ struct rectangle winarea;
+ struct wl_callback *callback;
+ int bottom;
+
+ mi->swap_buffers = 0;
+
+ widget_get_allocation(mi->widget, &drawarea);
+ window_get_allocation(mi->window, &winarea);
+
+ if (display_acquire_window_surface(wscr->display,
+ mi->window,
+ mi->eglctx) < 0) {
+ fprintf(stderr, "%s: unable to acquire window surface",
+ progname);
+ return;
+ }
+
+ bottom = winarea.height - (drawarea.height + drawarea.y);
+ glViewport(drawarea.x, bottom, drawarea.width, drawarea.height);
+ glScissor(drawarea.x, bottom, drawarea.width, drawarea.height);
+ glEnable(GL_SCISSOR_TEST);
+
+ if (mi->width != drawarea.width || mi->height != drawarea.height) {
+ mi->width = drawarea.width;
+ mi->height = drawarea.height;
+ wscr->plugin->reshape(mi, mi->width, mi->height);
+ }
+
+ wscr->plugin->draw(mi);
+
+ if (mi->swap_buffers == 0)
+ fprintf(stderr, "%s: swapBuffers not called\n", progname);
+
+ display_release_window_surface(wscr->display, mi->window);
+
+ callback = wl_surface_frame(window_get_wl_surface(mi->window));
+ wl_callback_add_listener(callback, &listener, mi);
+}
+
+static void
+init_frand(void)
+{
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ srandom(tv.tv_sec * 100 + tv.tv_usec / 10000);
+}
+
+WL_EXPORT EGLContext *
+init_GL(struct ModeInfo *mi)
+{
+ struct wscreensaver *wscr = mi->priv;
+ EGLContext *pctx;
+
+ pctx = malloc(sizeof *pctx);
+ if (!pctx)
+ return NULL;
+
+ if (mi->eglctx != EGL_NO_CONTEXT) {
+ fprintf(stderr, "%s: multiple GL contexts are not supported",
+ progname);
+ goto errout;
+ }
+
+ mi->eglctx = eglCreateContext(wscr->egl.display, wscr->egl.config,
+ EGL_NO_CONTEXT, NULL);
+ if (mi->eglctx == EGL_NO_CONTEXT) {
+ fprintf(stderr, "%s: init_GL failed to create EGL context\n",
+ progname);
+ goto errout;
+ }
+
+ if (!eglMakeCurrent(wscr->egl.display, NULL, NULL, mi->eglctx)) {
+ fprintf(stderr, "%s: init_GL failed on eglMakeCurrent\n",
+ progname);
+ goto errout;
+ }
+
+ glClearColor(0.0, 0.0, 0.0, 1.0);
+
+ *pctx = mi->eglctx;
+ return pctx;
+
+errout:
+ free(pctx);
+ return NULL;
+}
+
+static struct ModeInfo *
+create_wscreensaver_instance(struct wscreensaver *screensaver,
+ struct wl_output *output, int width, int height)
+{
+ static int instance;
+ struct ModeInfo *mi;
+ struct rectangle drawarea;
+
+ mi = calloc(1, sizeof *mi);
+ if (!mi)
+ return NULL;
+
+ if (demo_mode)
+ mi->window = window_create(screensaver->display);
+ else
+ mi->window = window_create_custom(screensaver->display);
+
+ if (!mi->window) {
+ fprintf(stderr, "%s: creating a window failed.\n", progname);
+ free(mi);
+ return NULL;
+ }
+
+ window_set_title(mi->window, progname);
+
+ if (screensaver->interface && !demo_mode) {
+ mi->widget = window_add_widget(mi->window, mi);
+ screensaver_set_surface(screensaver->interface,
+ window_get_wl_surface(mi->window),
+ output);
+ } else {
+ mi->widget = frame_create(mi->window, mi);
+ }
+ widget_set_redraw_handler(mi->widget, redraw_handler);
+
+ mi->priv = screensaver;
+ mi->eglctx = EGL_NO_CONTEXT;
+ mi->instance_number = instance++; /* XXX */
+
+ widget_get_allocation(mi->widget, &drawarea);
+ mi->width = drawarea.width;
+ mi->height = drawarea.height;
+
+ screensaver->plugin->init(mi);
+
+ window_schedule_resize(mi->window, width, height);
+ return mi;
+}
+
+static void
+handle_output_destroy(struct output *output, void *data)
+{
+ /* struct ModeInfo *mi = data;
+ * TODO */
+}
+
+static void
+handle_output_configure(struct output *output, void *data)
+{
+ struct wscreensaver *screensaver = data;
+ struct ModeInfo *mi;
+ struct rectangle area;
+
+ /* skip existing outputs */
+ if (output_get_user_data(output))
+ return;
+
+ output_get_allocation(output, &area);
+ mi = create_wscreensaver_instance(screensaver,
+ output_get_wl_output(output),
+ area.width, area.height);
+ output_set_user_data(output, mi);
+ output_set_destroy_handler(output, handle_output_destroy);
+}
+
+static int
+init_wscreensaver(struct wscreensaver *wscr, struct display *display)
+{
+ int size;
+ const char prefix[] = "wscreensaver::";
+ char *str;
+
+ display_set_user_data(display, wscr);
+ wscr->display = display;
+ wscr->plugin = plugins[0];
+
+ size = sizeof(prefix) + strlen(wscr->plugin->name);
+ str = malloc(size);
+ if (!str) {
+ fprintf(stderr, "init: out of memory\n");
+ return -1;
+ }
+ snprintf(str, size, "%s%s", prefix, wscr->plugin->name);
+ progname = str;
+
+ wscr->egl.display = display_get_egl_display(wscr->display);
+ if (!wscr->egl.display) {
+ fprintf(stderr, "init: no EGL display\n");
+ return -1;
+ }
+
+ eglBindAPI(EGL_OPENGL_API);
+ wscr->egl.config = display_get_argb_egl_config(wscr->display);
+
+ if (demo_mode) {
+ struct wl_output *o =
+ output_get_wl_output(display_get_output(display));
+ /* only one instance */
+ wscr->demomode =
+ create_wscreensaver_instance(wscr, o, 400, 300);
+ return 0;
+ }
+
+ display_set_output_configure_handler(display, handle_output_configure);
+
+ return 0;
+}
+
+static void
+global_handler(struct display *display, uint32_t name,
+ const char *interface, uint32_t version, void *data)
+{
+ struct wscreensaver *screensaver = data;
+
+ if (!strcmp(interface, "screensaver")) {
+ screensaver->interface =
+ display_bind(display, name, &screensaver_interface, 1);
+ }
+}
+
+static const struct weston_option wscreensaver_options[] = {
+ { WESTON_OPTION_BOOLEAN, "demo", 0, &demo_mode },
+};
+
+int main(int argc, char *argv[])
+{
+ struct display *d;
+ struct wscreensaver screensaver = { 0 };
+
+ init_frand();
+
+ parse_options(wscreensaver_options,
+ ARRAY_LENGTH(wscreensaver_options), &argc, argv);
+
+ d = display_create(&argc, argv);
+ if (d == NULL) {
+ fprintf(stderr, "failed to create display: %m\n");
+ return EXIT_FAILURE;
+ }
+
+ if (!demo_mode) {
+ /* iterates already known globals immediately */
+ display_set_user_data(d, &screensaver);
+ display_set_global_handler(d, global_handler);
+ if (!screensaver.interface) {
+ fprintf(stderr,
+ "Server did not offer screensaver interface,"
+ " exiting.\n");
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (init_wscreensaver(&screensaver, d) < 0) {
+ fprintf(stderr, "wscreensaver init failed.\n");
+ return EXIT_FAILURE;
+ }
+
+ display_run(d);
+
+ free((void *)progname);
+
+ return EXIT_SUCCESS;
+}
diff --git a/clients/wscreensaver.h b/clients/wscreensaver.h
new file mode 100644
index 00000000..e2749d97
--- /dev/null
+++ b/clients/wscreensaver.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright © 2011 Collabora, Ltd.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * 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.
+ */
+
+#ifndef WSCREENSAVER_H
+#define WSCREENSAVER_H
+
+#define MESA_EGL_NO_X11_HEADERS
+#include <EGL/egl.h>
+
+extern const char *progname;
+
+struct wscreensaver;
+
+struct ModeInfo {
+ struct wscreensaver *priv;
+ EGLContext eglctx;
+ int swap_buffers;
+
+ struct window *window;
+ struct widget *widget;
+
+ int instance_number;
+ int width;
+ int height;
+
+ unsigned long polygon_count;
+ int fps_p;
+};
+
+struct wscreensaver_plugin {
+ const char *name;
+ void (*init)(struct ModeInfo *mi);
+ void (*draw)(struct ModeInfo *mi);
+ void (*reshape)(struct ModeInfo *mi, int w, int h);
+/* void (*refresh)(struct ModeInfo *mi);
+ void (*finish)(struct ModeInfo *mi);*/
+};
+
+EGLContext *
+init_GL(struct ModeInfo *mi);
+
+#endif /* WSCREENSAVER_H */
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 00000000..50bce23a
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,498 @@
+m4_define([weston_major_version], [1])
+m4_define([weston_minor_version], [3])
+m4_define([weston_micro_version], [1])
+m4_define([weston_version],
+ [weston_major_version.weston_minor_version.weston_micro_version])
+
+AC_PREREQ([2.64])
+AC_INIT([weston],
+ [weston_version],
+ [https://bugs.freedesktop.org/enter_bug.cgi?product=Wayland&component=weston&version=weston_version],
+ [weston],
+ [http://wayland.freedesktop.org/])
+
+AC_SUBST([WESTON_VERSION_MAJOR], [weston_major_version])
+AC_SUBST([WESTON_VERSION_MINOR], [weston_minor_version])
+AC_SUBST([WESTON_VERSION_MICRO], [weston_micro_version])
+AC_SUBST([WESTON_VERSION], [weston_version])
+
+AC_CONFIG_HEADERS([config.h])
+
+AC_USE_SYSTEM_EXTENSIONS
+AC_SYS_LARGEFILE
+
+AM_INIT_AUTOMAKE([1.11 parallel-tests foreign no-dist-gzip dist-xz color-tests])
+
+AM_SILENT_RULES([yes])
+
+# Check for programs
+AC_PROG_CC
+AC_PROG_SED
+
+# Initialize libtool
+LT_PREREQ([2.2])
+LT_INIT([disable-static])
+
+AC_ARG_VAR([WESTON_NATIVE_BACKEND],
+ [Set the native backend to use, if Weston is not running under Wayland nor X11. @<:@default=drm-backend.so@:>@])
+
+PKG_PROG_PKG_CONFIG()
+
+AC_CHECK_FUNC([dlopen], [],
+ AC_CHECK_LIB([dl], [dlopen], DLOPEN_LIBS="-ldl"))
+AC_SUBST(DLOPEN_LIBS)
+
+AC_CHECK_DECL(SFD_CLOEXEC,[],
+ [AC_MSG_ERROR("SFD_CLOEXEC is needed to compile weston")],
+ [[#include <sys/signalfd.h>]])
+AC_CHECK_DECL(TFD_CLOEXEC,[],
+ [AC_MSG_ERROR("TFD_CLOEXEC is needed to compile weston")],
+ [[#include <sys/timerfd.h>]])
+AC_CHECK_DECL(CLOCK_MONOTONIC,[],
+ [AC_MSG_ERROR("CLOCK_MONOTONIC is needed to compile weston")],
+ [[#include <time.h>]])
+AC_CHECK_HEADERS([execinfo.h])
+
+AC_CHECK_FUNCS([mkostemp strchrnul initgroups])
+
+COMPOSITOR_MODULES="wayland-server >= 1.2.91 pixman-1"
+
+AC_ARG_ENABLE(egl, [ --disable-egl],,
+ enable_egl=yes)
+AM_CONDITIONAL(ENABLE_EGL, test x$enable_egl = xyes)
+if test x$enable_egl = xyes; then
+ AC_DEFINE([ENABLE_EGL], [1], [Build Weston with EGL support])
+ COMPOSITOR_MODULES="$COMPOSITOR_MODULES egl >= 7.10 glesv2"
+fi
+
+AC_ARG_ENABLE(xkbcommon,
+ AS_HELP_STRING([--disable-xkbcommon], [Disable libxkbcommon
+ support: This is only useful in environments
+ where you do not have a hardware keyboard. If
+ libxkbcommon support is disabled clients will not
+ be sent a keymap and and must know how to
+ interpret the keycode sent for any key event.]),,
+ enable_xkbcommon=yes)
+if test x$enable_xkbcommon = xyes; then
+ AC_DEFINE(ENABLE_XKBCOMMON, [1], [Build Weston with libxkbcommon support])
+ COMPOSITOR_MODULES="$COMPOSITOR_MODULES xkbcommon >= 0.3.0"
+fi
+
+PKG_CHECK_MODULES(COMPOSITOR, [$COMPOSITOR_MODULES])
+
+AC_ARG_ENABLE(setuid-install, [ --enable-setuid-install],,
+ enable_setuid_install=yes)
+AM_CONDITIONAL(ENABLE_SETUID_INSTALL, test x$enable_setuid_install = xyes)
+
+
+AC_ARG_ENABLE(xwayland, [ --enable-xwayland],,
+ enable_xwayland=yes)
+AC_ARG_ENABLE(xwayland-test, [ --enable-xwayland-test],,
+ enable_xwayland_test=yes)
+AM_CONDITIONAL(ENABLE_XWAYLAND, test x$enable_xwayland = xyes)
+AM_CONDITIONAL(ENABLE_XWAYLAND_TEST, test x$enable_xwayland = xyes -a x$enable_xwayland_test = xyes)
+if test x$enable_xwayland = xyes; then
+ PKG_CHECK_MODULES([XWAYLAND], xcb xcb-xfixes xcb-composite xcursor cairo-xcb)
+ AC_DEFINE([BUILD_XWAYLAND], [1], [Build the X server launcher])
+
+ AC_ARG_WITH(xserver-path, AS_HELP_STRING([--with-xserver-path=PATH],
+ [Path to X server]), [XSERVER_PATH="$withval"],
+ [XSERVER_PATH="$bindir/Xorg"])
+ AC_SUBST([XSERVER_PATH])
+ if test x$enable_xwayland_test = xyes; then
+ PKG_CHECK_MODULES([XWAYLAND_TEST], xcb xcb-dri2 libdrm)
+ fi
+fi
+
+
+AC_ARG_ENABLE(x11-compositor, [ --enable-x11-compositor],,
+ enable_x11_compositor=yes)
+AM_CONDITIONAL(ENABLE_X11_COMPOSITOR, test x$enable_x11_compositor = xyes)
+if test x$enable_x11_compositor = xyes; then
+ PKG_CHECK_MODULES([XCB], xcb)
+ xcb_save_LIBS=$LIBS
+ xcb_save_CFLAGS=$CFLAGS
+ CFLAGS=$XCB_CFLAGS
+ LIBS=$XCB_LIBS
+ AC_CHECK_FUNCS([xcb_poll_for_queued_event])
+ LIBS=$xcb_save_LIBS
+ CFLAGS=$xcb_save_CFLAGS
+
+ X11_COMPOSITOR_MODULES="x11 x11-xcb xcb-shm"
+
+ PKG_CHECK_MODULES(X11_COMPOSITOR_XKB, [xcb-xkb],
+ [have_xcb_xkb="yes"], [have_xcb_xkb="no"])
+ if test "x$have_xcb_xkb" = xyes; then
+ # Most versions of XCB have totally broken XKB bindings, where the
+ # events don't work. Make sure we can actually use them.
+ xcb_xkb_save_CFLAGS=$CFLAGS
+ CFLAGS=$X11_COMPOSITOR_XKB_CFLAGS
+ AC_CHECK_MEMBER([struct xcb_xkb_state_notify_event_t.xkbType],
+ [], [have_xcb_xkb=no], [[#include <xcb/xkb.h>]])
+ CFLAGS=$xcb_xkb_save_CFLAGS
+ fi
+ if test "x$have_xcb_xkb" = xyes; then
+ X11_COMPOSITOR_MODULES="$X11_COMPOSITOR_MODULES xcb-xkb"
+ AC_DEFINE([HAVE_XCB_XKB], [1], [libxcb supports XKB protocol])
+ fi
+
+ PKG_CHECK_MODULES(X11_COMPOSITOR, [$X11_COMPOSITOR_MODULES])
+ AC_DEFINE([BUILD_X11_COMPOSITOR], [1], [Build the X11 compositor])
+fi
+
+
+AC_ARG_ENABLE(drm-compositor, [ --enable-drm-compositor],,
+ enable_drm_compositor=yes)
+AM_CONDITIONAL(ENABLE_DRM_COMPOSITOR, test x$enable_drm_compositor = xyes -a x$enable_egl = xyes)
+if test x$enable_drm_compositor = xyes -a x$enable_egl = xyes; then
+ AC_DEFINE([BUILD_DRM_COMPOSITOR], [1], [Build the DRM compositor])
+ PKG_CHECK_MODULES(DRM_COMPOSITOR, [libudev >= 136 libdrm >= 2.4.30 gbm mtdev >= 1.1.0])
+fi
+
+
+AC_ARG_ENABLE(wayland-compositor, [ --enable-wayland-compositor],,
+ enable_wayland_compositor=yes)
+AM_CONDITIONAL(ENABLE_WAYLAND_COMPOSITOR,
+ test x$enable_wayland_compositor = xyes -a x$enable_egl = xyes)
+if test x$enable_wayland_compositor = xyes -a x$enable_egl = xyes; then
+ AC_DEFINE([BUILD_WAYLAND_COMPOSITOR], [1],
+ [Build the Wayland (nested) compositor])
+ PKG_CHECK_MODULES(WAYLAND_COMPOSITOR, [wayland-client wayland-egl])
+fi
+
+
+AC_ARG_ENABLE(headless-compositor, [ --enable-headless-compositor],,
+ enable_headless_compositor=yes)
+AM_CONDITIONAL(ENABLE_HEADLESS_COMPOSITOR,
+ test x$enable_headless_compositor = xyes)
+
+
+AC_ARG_ENABLE(rpi-compositor,
+ AS_HELP_STRING([--disable-rpi-compositor],
+ [do not build the Raspberry Pi backend]),,
+ enable_rpi_compositor=yes)
+AM_CONDITIONAL(ENABLE_RPI_COMPOSITOR, test "x$enable_rpi_compositor" = "xyes")
+have_bcm_host="no"
+if test "x$enable_rpi_compositor" = "xyes"; then
+ AC_DEFINE([BUILD_RPI_COMPOSITOR], [1], [Build the compositor for Raspberry Pi])
+ PKG_CHECK_MODULES(RPI_COMPOSITOR, [libudev >= 136 mtdev >= 1.1.0])
+ PKG_CHECK_MODULES(RPI_BCM_HOST, [bcm_host],
+ [have_bcm_host="yes"
+ AC_DEFINE([HAVE_BCM_HOST], [1], [have Raspberry Pi BCM headers])],
+ [AC_MSG_WARN([Raspberry Pi BCM host libraries not found, will use stubs instead.])])
+fi
+AM_CONDITIONAL(INSTALL_RPI_COMPOSITOR, test "x$have_bcm_host" = "xyes")
+
+
+AC_ARG_ENABLE([fbdev-compositor], [ --enable-fbdev-compositor],,
+ enable_fbdev_compositor=yes)
+AM_CONDITIONAL([ENABLE_FBDEV_COMPOSITOR],
+ [test x$enable_fbdev_compositor = xyes])
+AS_IF([test x$enable_fbdev_compositor = xyes], [
+ AC_DEFINE([BUILD_FBDEV_COMPOSITOR], [1], [Build the fbdev compositor])
+ PKG_CHECK_MODULES([FBDEV_COMPOSITOR], [libudev >= 136 mtdev >= 1.1.0])
+])
+
+AC_ARG_ENABLE([rdp-compositor], [ --enable-rdp-compositor],,
+ enable_rdp_compositor=no)
+AM_CONDITIONAL([ENABLE_RDP_COMPOSITOR],
+ [test x$enable_rdp_compositor = xyes])
+if test x$enable_rdp_compositor = xyes; then
+ AC_DEFINE([BUILD_RDP_COMPOSITOR], [1], [Build the RDP compositor])
+ PKG_CHECK_MODULES(RDP_COMPOSITOR, [freerdp >= 1.1.0])
+fi
+
+AC_ARG_WITH(cairo,
+ AS_HELP_STRING([--with-cairo=@<:@image|gl|glesv2@:>@]
+ [Which Cairo renderer to use for the clients]),
+ [],[with_cairo="image"])
+
+if test "x$with_cairo" = "ximage"; then
+ cairo_modules="cairo"
+else
+if test "x$with_cairo" = "xgl"; then
+ cairo_modules="cairo-gl"
+else
+if test "x$with_cairo" = "xglesv2"; then
+ cairo_modules="cairo-glesv2"
+else
+ AC_ERROR([Unknown cairo renderer requested])
+fi
+fi
+fi
+
+# Included for legacy compat
+AC_ARG_WITH(cairo-glesv2,
+ AS_HELP_STRING([--with-cairo-glesv2],
+ [Use GLESv2 cairo]))
+if test "x$with_cairo_glesv2" = "xyes"; then
+ cairo_modules="cairo-glesv2"
+ with_cairo="glesv2"
+fi
+
+if test "x$cairo_modules" = "xcairo-glesv2"; then
+AC_DEFINE([USE_CAIRO_GLESV2], [1], [Use the GLESv2 GL cairo backend])
+fi
+
+PKG_CHECK_MODULES(PIXMAN, [pixman-1])
+PKG_CHECK_MODULES(PNG, [libpng])
+PKG_CHECK_MODULES(WEBP, [libwebp], [have_webp=yes], [have_webp=no])
+AS_IF([test "x$have_webp" = "xyes"],
+ [AC_DEFINE([HAVE_WEBP], [1], [Have webp])])
+
+AC_ARG_ENABLE(vaapi-recorder, [ --enable-vaapi-recorder],,
+ enable_vaapi_recorder=auto)
+if test x$enable_vaapi_recorder != xno; then
+ PKG_CHECK_MODULES(LIBVA, [libva >= 0.34.0 libva-drm >= 0.34.0],
+ [have_libva=yes], [have_libva=no])
+ if test "x$have_libva" = "xno" -a "x$enable_vaapi_recorder" = "xyes"; then
+ AC_MSG_ERROR([vaapi-recorder explicitly enabled, but libva couldn't be found])
+ fi
+ AS_IF([test "x$have_libva" = "xyes"],
+ [AC_DEFINE([BUILD_VAAPI_RECORDER], [1], [Build the vaapi recorder])])
+fi
+AM_CONDITIONAL(ENABLE_VAAPI_RECORDER, test "x$have_libva" = xyes)
+
+
+AC_CHECK_LIB([jpeg], [jpeg_CreateDecompress], have_jpeglib=yes)
+if test x$have_jpeglib = xyes; then
+ JPEG_LIBS="-ljpeg"
+else
+ AC_ERROR([libjpeg not found])
+fi
+AC_SUBST(JPEG_LIBS)
+
+PKG_CHECK_MODULES(CAIRO, [cairo])
+
+AC_ARG_ENABLE(simple-clients,
+ AS_HELP_STRING([--disable-simple-clients],
+ [do not build the simple wl_shm clients]),,
+ enable_simple_clients=yes)
+AM_CONDITIONAL(BUILD_SIMPLE_CLIENTS, test "x$enable_simple_clients" = "xyes")
+if test x$enable_simple_clients = xyes; then
+ PKG_CHECK_MODULES(SIMPLE_CLIENT, [wayland-client])
+fi
+
+AC_ARG_ENABLE(simple-egl-clients,
+ AS_HELP_STRING([--disable-simple-egl-clients],
+ [do not build the simple EGL clients]),,
+ enable_simple_egl_clients="$enable_egl")
+AM_CONDITIONAL(BUILD_SIMPLE_EGL_CLIENTS, test "x$enable_simple_egl_clients" = "xyes")
+if test x$enable_simple_egl_clients = xyes; then
+ PKG_CHECK_MODULES(SIMPLE_EGL_CLIENT,
+ [egl >= 7.10 glesv2 wayland-client wayland-egl wayland-cursor])
+fi
+
+AC_ARG_ENABLE(clients, [ --enable-clients],, enable_clients=yes)
+AM_CONDITIONAL(BUILD_CLIENTS, test x$enable_clients = xyes)
+if test x$enable_clients = xyes; then
+ AC_DEFINE([BUILD_CLIENTS], [1], [Build the Wayland clients])
+
+ PKG_CHECK_MODULES(CLIENT, [wayland-client cairo >= 1.10.0 xkbcommon wayland-cursor])
+ PKG_CHECK_MODULES(SERVER, [wayland-server])
+ PKG_CHECK_MODULES(WESTON_INFO, [wayland-client])
+
+ PKG_CHECK_MODULES(POPPLER, [poppler-glib glib-2.0 gobject-2.0 gio-2.0 ],
+ [have_poppler=yes], [have_poppler=no])
+
+ # Only check for cairo-egl if a GL or GLES renderer requested
+ AS_IF([test "x$cairo_modules" = "xcairo-gl" -o "x$cairo_modules" = "xcairo-glesv2"], [
+ PKG_CHECK_MODULES(CAIRO_EGL, [wayland-egl egl >= 7.10 cairo-egl >= 1.11.3 $cairo_modules],
+ [have_cairo_egl=yes], [have_cairo_egl=no])
+ AS_IF([test "x$have_cairo_egl" = "xyes"],
+ [AC_DEFINE([HAVE_CAIRO_EGL], [1], [Have cairo-egl])],
+ [AC_ERROR([cairo-egl not used because $CAIRO_EGL_PKG_ERRORS])])],
+ [have_cairo_egl=no])
+
+ PKG_CHECK_MODULES(PANGO, [pangocairo], [have_pango=yes], [have_pango=no])
+fi
+
+AC_ARG_ENABLE(resize-optimization,
+ AS_HELP_STRING([--disable-resize-optimization],
+ [disable resize optimization allocating a big buffer in toytoolkit]),,
+ enable_resize_optimization=yes)
+AS_IF([test "x$enable_resize_optimization" = "xyes"],
+ [AC_DEFINE([USE_RESIZE_POOL], [1], [Use resize memory pool as a performance optimization])])
+
+AC_ARG_ENABLE(weston-launch, [ --enable-weston-launch],, enable_weston_launch=yes)
+AM_CONDITIONAL(BUILD_WESTON_LAUNCH, test x$enable_weston_launch == xyes)
+if test x$enable_weston_launch == xyes; then
+ PKG_CHECK_MODULES(WESTON_LAUNCH, [libdrm])
+ PKG_CHECK_MODULES(SYSTEMD_LOGIN, [libsystemd-login],
+ [have_systemd_login=yes], [have_systemd_login=no])
+ AS_IF([test "x$have_systemd_login" = "xyes"],
+ [AC_DEFINE([HAVE_SYSTEMD_LOGIN], [1], [Have systemd-login])])
+
+ AC_CHECK_LIB([pam], [pam_open_session], [have_pam=yes], [have_pam=no])
+ if test x$have_pam == xno; then
+ AC_ERROR([weston-launch requires pam])
+ fi
+ WESTON_LAUNCH_LIBS="$WESTON_LAUNCH_LIBS -lpam"
+fi
+
+if test x$enable_egl = xyes; then
+ PKG_CHECK_MODULES(GLU, [glu], [have_glu=yes], [have_glu=no])
+fi
+AM_CONDITIONAL(HAVE_GLU, test "x$have_glu" = "xyes")
+
+
+AM_CONDITIONAL(HAVE_POPPLER, test "x$have_poppler" = "xyes")
+
+AM_CONDITIONAL(HAVE_PANGO, test "x$have_pango" = "xyes")
+
+AM_CONDITIONAL(HAVE_CAIRO_GLESV2,
+ [test "x$have_cairo_egl" = "xyes" -a "x$cairo_modules" = "xcairo-glesv2" -a "x$enable_egl" = "xyes"])
+
+AM_CONDITIONAL(BUILD_FULL_GL_CLIENTS,
+ test x$cairo_modules = "xcairo-gl" -a "x$have_cairo_egl" = "xyes" -a "x$enable_egl" = "xyes")
+
+AM_CONDITIONAL(BUILD_SUBSURFACES_CLIENT,
+ [test '(' "x$have_cairo_egl" != "xyes" -o "x$cairo_modules" = "xcairo-glesv2" ')' -a "x$enable_simple_egl_clients" = "xyes"])
+
+AM_CONDITIONAL(ENABLE_DESKTOP_SHELL, true)
+
+AC_ARG_ENABLE(tablet-shell,
+ AS_HELP_STRING([--disable-tablet-shell],
+ [do not build tablet-shell server plugin and client]),,
+ enable_tablet_shell=yes)
+AM_CONDITIONAL(ENABLE_TABLET_SHELL,
+ test "x$enable_tablet_shell" = "xyes")
+
+# CMS modules
+AC_ARG_ENABLE(colord,
+ AS_HELP_STRING([--disable-colord],
+ [do not build colord CMS support]),,
+ enable_colord=auto)
+if test "x$enable_colord" != "xno"; then
+ PKG_CHECK_MODULES(COLORD,
+ colord >= 0.1.27,
+ have_colord=yes,
+ have_colord=no)
+ if test "x$have_colord" = "xno" -a "x$enable_colord" = "xyes"; then
+ AC_MSG_ERROR([colord support explicitly requested, but colord couldn't be found])
+ fi
+ if test "x$have_colord" = "xyes"; then
+ enable_colord=yes
+ fi
+fi
+AM_CONDITIONAL(ENABLE_COLORD, test "x$enable_colord" = "xyes")
+
+AC_ARG_ENABLE(wcap-tools, [ --disable-wcap-tools],, enable_wcap_tools=yes)
+AM_CONDITIONAL(BUILD_WCAP_TOOLS, test x$enable_wcap_tools = xyes)
+if test x$enable_wcap_tools = xyes; then
+ AC_DEFINE([BUILD_WCAP_TOOLS], [1], [Build the wcap tools])
+ PKG_CHECK_MODULES(WCAP, [cairo])
+ WCAP_LIBS="$WCAP_LIBS -lm"
+fi
+
+AC_CHECK_PROG(RSVG_CONVERT, rsvg-convert, rsvg-convert)
+AM_CONDITIONAL(HAVE_RSVG_CONVERT, test -n "$RSVG_CONVERT")
+
+PKG_CHECK_MODULES(SETBACKLIGHT, [libudev libdrm], enable_setbacklight=yes, enable_setbacklight=no)
+AM_CONDITIONAL(BUILD_SETBACKLIGHT, test "x$enable_setbacklight" = "xyes")
+
+if test "x$GCC" = "xyes"; then
+ GCC_CFLAGS="-Wall -Wextra -Wno-unused-parameter \
+ -Wno-missing-field-initializers -g -fvisibility=hidden \
+ -Wstrict-prototypes -Wmissing-prototypes"
+fi
+AC_SUBST(GCC_CFLAGS)
+
+AC_ARG_ENABLE(libunwind,
+ AS_HELP_STRING([--disable-libunwind],
+ [Disable libunwind usage for backtraces]),,
+ enable_libunwind=auto)
+AM_CONDITIONAL(HAVE_LIBUNWIND, [test "x$enable_libunwind" = xyes])
+if test "x$enable_libunwind" != "xno"; then
+ PKG_CHECK_MODULES(LIBUNWIND,
+ libunwind,
+ have_libunwind=yes,
+ have_libunwind=no)
+ if test "x$have_libunwind" = "xno" -a "x$enable_libunwind" = "xyes"; then
+ AC_MSG_ERROR([libunwind support explicitly requested, but libunwind couldn't be found])
+ fi
+ if test "x$have_libunwind" = "xyes"; then
+ enable_libunwind=yes
+ AC_DEFINE(HAVE_LIBUNWIND, 1, [Have libunwind support])
+ fi
+fi
+
+
+if test "x$WESTON_NATIVE_BACKEND" = "x"; then
+ WESTON_NATIVE_BACKEND="drm-backend.so"
+fi
+AC_MSG_NOTICE([Weston's native backend: $WESTON_NATIVE_BACKEND])
+AC_DEFINE_UNQUOTED([WESTON_NATIVE_BACKEND], ["$WESTON_NATIVE_BACKEND"],
+ [The default backend to load, if not wayland nor x11.])
+
+AC_ARG_ENABLE(demo-clients,
+ AS_HELP_STRING([--enable-demo-clients],
+ [install demo clients built with weston]),,
+ enable_demo_clients=no)
+AM_CONDITIONAL(ENABLE_DEMO_CLIENTS, [test "x$enable_demo_clients" = "xyes"])
+
+PKG_CHECK_MODULES(LCMS, lcms2,
+ [have_lcms=yes], [have_lcms=no])
+if test "x$have_lcms" = xyes; then
+ AC_DEFINE(HAVE_LCMS, 1, [Have lcms support])
+fi
+AM_CONDITIONAL(HAVE_LCMS, [test "x$have_lcms" = xyes])
+
+AC_PATH_PROG([wayland_scanner], [wayland-scanner])
+if test x$wayland_scanner = x; then
+ AC_MSG_ERROR([wayland-scanner is needed to compile weston])
+fi
+
+AC_CONFIG_FILES([Makefile
+ shared/Makefile
+ src/Makefile
+ src/xwayland/Makefile
+ src/version.h
+ src/weston.pc
+ clients/Makefile
+ wcap/Makefile
+ data/Makefile
+ protocol/Makefile
+ man/Makefile
+ tests/Makefile])
+AC_OUTPUT
+
+AC_MSG_RESULT([
+ Native Backend ${WESTON_NATIVE_BACKEND}
+ setuid Install ${enable_setuid_install}
+
+ Cairo Renderer ${with_cairo}
+ EGL ${enable_egl}
+ libxkbcommon ${enable_xkbcommon}
+ XWayland ${enable_xwayland}
+
+ Build wcap utility ${enable_wcap_tools}
+ Build Tablet Shell ${enable_tablet_shell}
+
+ weston-launch utility ${enable_weston_launch}
+ weston-launch systemd support ${have_systemd_login}
+
+ DRM Compositor ${enable_drm_compositor}
+ X11 Compositor ${enable_x11_compositor}
+ Wayland Compositor ${enable_wayland_compositor}
+ Headless Compositor ${enable_headless_compositor}
+ RPI Compositor ${enable_rpi_compositor}
+ FBDEV Compositor ${enable_fbdev_compositor}
+ RDP Compositor ${enable_rdp_compositor}
+
+ Raspberry Pi BCM headers ${have_bcm_host}
+
+ Build Clients ${enable_clients}
+ Build EGL Clients ${have_cairo_egl}
+ Build Simple Clients ${enable_simple_clients}
+ Build Simple EGL Clients ${enable_simple_egl_clients}
+
+ Install Demo Clients ${enable_demo_clients}
+
+ Colord Support ${have_colord}
+ GLU Support ${have_glu}
+ LCMS2 Support ${have_lcms}
+ libwebp Support ${have_webp}
+ libunwind Support ${have_libunwind}
+ VA H.264 encoding Support ${have_libva}
+])
diff --git a/data/.gitignore b/data/.gitignore
new file mode 100644
index 00000000..8ea50a66
--- /dev/null
+++ b/data/.gitignore
@@ -0,0 +1 @@
+wayland.png
diff --git a/data/COPYING b/data/COPYING
new file mode 100644
index 00000000..430a864d
--- /dev/null
+++ b/data/COPYING
@@ -0,0 +1,11 @@
+For the DMZ cursors:
+
+(c) 2007-2010 Novell, Inc.
+
+This work is licenced under the Creative Commons Attribution-Share Alike 3.0
+United States License. To view a copy of this licence, visit
+http://creativecommons.org/licenses/by-sa/3.0/ or send a letter to Creative
+Commons, 171 Second Street, Suite 300, San Francisco, California 94105, USA.
+
+The terminal icon is taken from the gnome-icon-theme collection which
+is also distributed under the Creative Commons BY-SA 3.0 license. \ No newline at end of file
diff --git a/data/Makefile.am b/data/Makefile.am
new file mode 100644
index 00000000..a7cc9440
--- /dev/null
+++ b/data/Makefile.am
@@ -0,0 +1,19 @@
+westondatadir = $(datadir)/weston
+
+dist_westondata_DATA = \
+ wayland.svg \
+ $(wayland_icon_png) \
+ pattern.png \
+ terminal.png \
+ border.png \
+ icon_window.png \
+ sign_close.png \
+ sign_maximize.png \
+ sign_minimize.png
+
+if HAVE_RSVG_CONVERT
+wayland_icon_png = wayland.png
+
+wayland.png : $(top_srcdir)/data/wayland.svg
+ $(RSVG_CONVERT) -w 128 -h 128 $< -o $@
+endif
diff --git a/data/border.png b/data/border.png
new file mode 100644
index 00000000..6a3a8f9b
--- /dev/null
+++ b/data/border.png
Binary files differ
diff --git a/data/icon_window.png b/data/icon_window.png
new file mode 100644
index 00000000..2587ca34
--- /dev/null
+++ b/data/icon_window.png
Binary files differ
diff --git a/data/pattern.png b/data/pattern.png
new file mode 100644
index 00000000..5ac8986d
--- /dev/null
+++ b/data/pattern.png
Binary files differ
diff --git a/data/sign_close.png b/data/sign_close.png
new file mode 100644
index 00000000..741ea0b0
--- /dev/null
+++ b/data/sign_close.png
Binary files differ
diff --git a/data/sign_maximize.png b/data/sign_maximize.png
new file mode 100644
index 00000000..2443a4c0
--- /dev/null
+++ b/data/sign_maximize.png
Binary files differ
diff --git a/data/sign_minimize.png b/data/sign_minimize.png
new file mode 100644
index 00000000..f4f3d9d2
--- /dev/null
+++ b/data/sign_minimize.png
Binary files differ
diff --git a/data/terminal.png b/data/terminal.png
new file mode 100644
index 00000000..3c02dd21
--- /dev/null
+++ b/data/terminal.png
Binary files differ
diff --git a/data/wayland.svg b/data/wayland.svg
new file mode 100644
index 00000000..29cb8f06
--- /dev/null
+++ b/data/wayland.svg
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Copyright © 2010 Kristian Høgsberg
+
+ Permission to use, copy, modify, distribute, and sell this
+ software and its documentation for any purpose is hereby granted
+ without fee, provided that 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. -->
+
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="128"
+ height="128"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.0 r9654"
+ sodipodi:docname="wayland.svg"
+ inkscape:export-filename="/home/krh/wayland.png"
+ inkscape:export-xdpi="41.302303"
+ inkscape:export-ydpi="41.302303">
+ <defs
+ id="defs4" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.1"
+ inkscape:cx="246.80131"
+ inkscape:cy="58.818182"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1278"
+ inkscape:window-height="740"
+ inkscape:window-x="0"
+ inkscape:window-y="26"
+ inkscape:window-maximized="0" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ style="display:inline"
+ transform="translate(0,-924.36218)">
+ <path
+ sodipodi:type="arc"
+ style="fill:#ffbc00;fill-opacity:1;stroke:none"
+ id="path2985"
+ sodipodi:cx="341.42856"
+ sodipodi:cy="465.21933"
+ sodipodi:rx="122.85714"
+ sodipodi:ry="122.85714"
+ d="m 464.2857,465.21933 a 122.85714,122.85714 0 1 1 -245.71428,0 122.85714,122.85714 0 1 1 245.71428,0 z"
+ transform="matrix(0.49134614,0,0,0.49134614,-102.99669,759.57895)" />
+ <g
+ transform="matrix(0.44987141,0,0,0.53664452,-102.99669,759.57895)"
+ style="font-size:437.47387695px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none"
+ id="text2987">
+ <path
+ d="m 443.91049,314.59549 2.34971,0 -1.49527,1.49528 -0.85444,0 0,-1.49528 m -173.02434,3.63138 0,1.70888 -5.55387,6.83553 -0.85444,0 -0.85444,-5.34026 c 3.27538,-2.13591 5.69629,-3.20396 7.26275,-3.20415 m 202.50256,2.34971 c 4.84164,1.9e-4 7.54736,3.06194 8.11719,9.18524 l -4.0586,28.83739 1.49528,0 3.20415,-2.1361 0.85444,0 0,0.64083 c -4.41481,2.70589 -8.68701,10.11103 -12.81662,22.21547 -1.56666,10.39583 -4.20118,20.00828 -7.90358,28.83739 -3.27553,9.11414 -4.91321,17.51613 -4.91303,25.20602 l -6.1947,15.16633 c -1.7e-4,1.28172 2.34954,2.56338 7.04914,3.84498 -0.85462,2.9906 -2.13628,4.48587 -3.84499,4.48582 l 0,0.85444 1.49527,0 0,0.64083 c -1.7e-4,1.56653 -2.42108,2.34977 -7.26275,2.34971 l 0.85444,0.85444 0,2.99055 -1.49527,0 c 0.99668,2.99059 3.13279,4.48586 6.40831,4.48581 l 0,1.49528 -6.40831,0 0,0.85444 c 2.13594,4e-5 3.20399,1.2817 3.20416,3.84498 l -8.97163,2.99055 0.85444,0.85444 0,0.64083 c -1.6e-4,0.99688 -1.06821,1.4953 -3.20416,1.49527 l 0,0.85444 c 2.70557,3e-5 4.05844,0.49846 4.0586,1.49527 -3.84515,1.28169 -8.90058,7.61879 -15.16633,19.01132 -6.40845,10.11089 -11.96231,15.16633 -16.66161,15.16633 l -1.70888,0 c -3.56029,-2.27851 -6.47963,-9.82607 -8.75802,-22.64269 -9.11415,-19.5097 -13.67116,-30.97344 -13.67106,-34.39126 -1e-4,-8.82916 -2.91944,-18.228 -8.75802,-28.19656 -1.56657,-4.41452 -2.63462,-9.96839 -3.20416,-16.6616 l -3.84498,-0.85444 c -9.68375,14.66799 -14.73919,26.13173 -15.16633,34.39126 -2.7058,4.84189 -4.8419,10.68057 -6.40831,17.51604 5.83861,1.56652 8.75795,3.06179 8.75802,4.48582 l 0,0.85444 c -4.55709,-0.56959 -7.76124,-1.63764 -9.61246,-3.20416 -1.99376,3.84503 -4.62829,14.52553 -7.90358,32.04155 -2.56338,8.11719 -5.76753,12.17578 -9.61247,12.17578 -10.82295,-3.56016 -19.86578,-9.39884 -27.1285,-17.51604 -1.13928,2e-5 -2.99057,-4.05857 -5.55387,-12.17579 -1.56649,3e-5 -2.63454,-4.05856 -3.20415,-12.17578 -5.41147,-11.5349 -9.11405,-24.70753 -11.10774,-39.51791 l 0,-22.21547 -0.85444,0 -3.84499,0.85444 0,-0.85444 c 1e-5,-0.99674 1.28167,-1.49516 3.84499,-1.49527 l -0.85444,-12.81662 0,-10.03968 -6.1947,-32.04154 0,-13.67106 c 2.70573,-8.54423 7.40515,-12.81643 14.09828,-12.81662 l 44.85816,0 0,1.49527 c -3.56022,0.14259 -5.98114,0.92583 -7.26275,2.34972 -2.70577,-1.5663 -5.3403,-2.34953 -7.90358,-2.34972 l -8.75802,0 -4.91304,0.64083 -0.85444,-0.64083 -0.64083,0 -6.40831,0.64083 -3.20415,-0.64083 -2.34972,0.64083 -2.56332,-0.64083 c 10.39568,11.39272 16.51917,21.00518 18.37048,28.83739 l 7.26275,0 0,1.49527 -5.55386,0 0,1.70889 c 2.13607,6.55086 3.70254,9.82621 4.69942,9.82607 l 1.70889,0 3.84498,-0.85444 0.85444,0.85444 0.85444,-0.85444 0.85444,0.85444 0,0.64083 c -2.84817,1.70902 -4.77066,4.05873 -5.76747,7.04914 l -0.64083,0 0,1.49527 1.49527,0 c 1.99366,23.78206 5.69623,41.79651 11.10773,54.04341 l 3.20416,2.1361 1.70888,0 c 2.13605,8e-5 6.62186,-8.33072 13.45745,-24.99241 4.41454,-7.54745 7.61869,-14.16936 9.61246,-19.86575 l -0.85444,-2.34972 c 4.41453,-5.55373 8.97155,-8.33066 13.67106,-8.3308 l 1.49527,0 c 2.99045,1.4e-4 5.12655,1.56661 6.40831,4.69943 3.98729,4.69955 7.19144,11.74868 9.61246,21.14742 l 11.96218,22.21547 c 1.56635,1.56656 2.34959,3.34664 2.34971,5.34026 4.41449,8.40207 8.40188,13.95593 11.96218,16.6616 l 2.56332,0 c 2.99041,6e-5 6.9778,-10.39563 11.96218,-31.1871 0.85429,9e-5 3.204,-5.55377 7.04914,-16.66161 l 0,-0.85444 -13.45745,-1.49527 -3.20416,0.85444 -4.05859,0 0,-1.70888 8.11719,-0.64083 15.80716,0 c 6.12333,-18.37035 11.7484,-32.61103 16.87521,-42.72206 1.70871,-20.79122 4.05842,-31.18691 7.04914,-31.1871 l 9.61247,-3.84499 m -45.49899,2.34971 c -0.85459,1.99389 -2.70588,2.99074 -5.55387,2.99055 l 0,-1.49527 5.55387,-1.49528 m -80.53108,3.84499 21.57464,0 0,1.49527 -4.91304,0.64083 -4.69943,-0.64083 -4.05859,1.49527 -1.49528,-1.49527 -6.4083,0 0,-1.49527 m -112.35902,2.99054 2.34972,0 1.49527,1.49528 0,0.85444 -4.69943,4.48581 0,3.84499 -1.70888,0 0,-2.34971 0.85444,-0.64084 0,-0.85444 -4.0586,-3.84498 c 0.99691,-1.99352 2.9194,-2.99036 5.76748,-2.99055 m 157.00357,9.18525 2.34971,0 0,0.64083 c -1.1e-4,0.99702 -0.78335,1.49544 -2.34971,1.49527 l -0.85444,0.85444 0,0.85444 -2.34972,-0.85444 0,-1.49527 3.20416,-1.49527 m -11.96218,8.3308 0,0.85444 c -1e-4,1.13942 -3.41786,2.34988 -10.25329,3.63137 l 0,-1.49527 4.69942,-2.1361 0.85444,0.64083 0.64083,0 4.0586,-1.49527 m -139.48752,7.68997 0,0.64083 -1.70888,1.49527 -0.64083,-0.64083 0,-0.85444 0.64083,-0.64083 1.70888,0 m 116.41761,0 0,0.64083 c -2.99062,2.27866 -7.83244,4.34356 -14.5255,6.1947 l 0,-1.49527 c 1.13919,1.5e-4 1.70882,-0.78309 1.70888,-2.34972 l 12.81662,-2.99054 m 40.58595,7.47636 0.85445,0.85444 0,0.64083 -1.49528,1.70888 -0.85444,0 -1.70888,-1.70888 0,-0.64083 3.20415,-0.85444 m 29.47822,0.85444 0.85445,0 0,0.64083 -1.49528,1.70888 -0.85444,0 0,-0.85444 1.49527,-1.49527 m -13.45744,10.68051 3.20415,2.13611 c -0.56976,1.56661 -2.13623,2.34985 -4.69943,2.34971 l -1.70888,-2.34971 c -1.3e-4,-0.7119 1.06792,-1.42393 3.20416,-2.13611 m 19.22492,9.82608 0.64083,0.85444 0,0.64083 -0.64083,0.85444 -1.70888,0 0,-0.85444 1.70888,-1.49527 m -30.33266,7.68997 0,0.64083 -4.05859,3.20415 -2.34972,0 0,-0.85444 c -1.1e-4,-1.28154 2.13599,-2.27839 6.40831,-2.99054 m -132.43838,29.69183 0.85444,0 0,0.64083 c -2.13609,4.69951 -3.98737,7.54765 -5.55387,8.54441 l -1.70888,0 0,-3.20415 6.40831,-5.98109 m 104.45543,35.67292 0.85444,0 0,1.49527 -6.4083,3.20415 -0.85445,-0.85444 0,-1.49527 6.40831,-2.34971 m -87.79383,10.03968 1.49527,0 2.56333,8.3308 -1.70888,0 -2.34972,-7.68997 0,-0.64083 m 85.44412,9.82607 0.64083,0 2.56332,2.34972 -0.85444,0.64083 -0.85444,0 -1.49527,-2.1361 0,-0.85445"
+ id="path2986"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccccccccccccccccccccccccccccccccccccccccsccccccccccccccccccccsccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc" />
+ </g>
+ </g>
+</svg>
diff --git a/man/.gitignore b/man/.gitignore
new file mode 100644
index 00000000..6138c7d5
--- /dev/null
+++ b/man/.gitignore
@@ -0,0 +1,4 @@
+weston.1
+weston-drm.7
+weston.ini.5
+
diff --git a/man/Makefile.am b/man/Makefile.am
new file mode 100644
index 00000000..e4abd8c4
--- /dev/null
+++ b/man/Makefile.am
@@ -0,0 +1,25 @@
+man_MANS = weston.1 weston.ini.5
+
+if ENABLE_DRM_COMPOSITOR
+man_MANS += weston-drm.7
+endif
+
+MAN_SUBSTS = \
+ -e 's|__weston_native_backend__|$(WESTON_NATIVE_BACKEND)|g' \
+ -e 's|__weston_modules_dir__|$(pkglibdir)|g' \
+ -e 's|__version__|$(PACKAGE_VERSION)|g'
+
+SUFFIXES = .1 .5 .7 .man
+
+.man.1:
+ $(AM_V_GEN)$(SED) $(MAN_SUBSTS) < $< > $@
+
+.man.5:
+ $(AM_V_GEN)$(SED) $(MAN_SUBSTS) < $< > $@
+
+.man.7:
+ $(AM_V_GEN)$(SED) $(MAN_SUBSTS) < $< > $@
+
+EXTRA_DIST = weston.man weston-drm.man weston.ini.man
+
+CLEANFILES = $(man_MANS)
diff --git a/man/weston-drm.man b/man/weston-drm.man
new file mode 100644
index 00000000..35d62ae6
--- /dev/null
+++ b/man/weston-drm.man
@@ -0,0 +1,130 @@
+.TH WESTON-DRM 7 "2012-11-27" "Weston __version__"
+.SH NAME
+weston-drm \- the DRM backend for Weston
+.SH SYNOPSIS
+.B weston-launch
+.LP
+.B weston --backend=drm-backend.so
+.
+.\" ***************************************************************
+.SH DESCRIPTION
+The DRM backend is the native Weston backend for systems that support
+the Linux kernel DRM, kernel mode setting (KMS), and evdev input devices.
+It is the recommended backend for desktop PCs, and aims to provide
+the full Wayland experience with the "every frame is perfect" concept.
+It also relies on the Mesa GBM interface.
+
+With the DRM backend,
+.B weston
+runs without any underlying windowing system. The backend uses the
+Linux KMS API to detect connected monitors. Monitor hot-plugging is
+supported. Input devices are found automatically by
+.BR udev (7).
+Compositing happens mainly in GL\ ES\ 2, initialized through EGL. It
+is also possible to take advantage of hardware cursors and overlays,
+when they exist and are functional. Full-screen surfaces will be
+scanned out directly without compositing, when possible.
+Hardware accelerated clients are supported via EGL.
+
+The backend chooses the DRM graphics device first based on seat id.
+If seat identifiers are not set, it looks for the graphics device
+that was used in boot. If that is not found, it finally chooses
+the first DRM device returned by
+.BR udev (7).
+Combining multiple graphics devices are not supported yet.
+
+The DRM backend relies on
+.B weston-launch
+for managing input device access and DRM master status, so that
+.B weston
+can be run without root privileges. On switching away from the
+virtual terminal (VT) hosting Weston, all input devices are closed and
+the DRM master capability is dropped, so that other servers,
+including
+.BR Xorg (1),
+can run on other VTs. On switching back to Weston's VT, input devices
+and DRM master are re-acquired through the parent process
+.BR weston-launch .
+.
+.\" ***************************************************************
+.SH CONFIGURATION
+.
+The DRM backend uses the following entries from
+.BR weston.ini .
+.SS Section output
+.TP
+\fBname\fR=\fIconnector\fR
+The KMS connector name identifying the output, for instance
+.IR LVDS1 .
+.TP
+\fBmode\fR=\fImode\fR
+Specify the video mode for the output. The argument
+.I mode
+can be one of the words
+.BR off " to turn the output off, "
+.BR preferred " to use the monitor's preferred video mode, or "
+.BR current " to use the current video mode and avoid a mode switch."
+It can also be a resolution as
+\fIwidth\fBx\fIheight\fR, or a detailed mode line as below.
+.TP
+\fBmode\fR=\fIdotclock hdisp hsyncstart hsyncend htotal \
+vdisp vsyncstart vsyncend vtotal hflag vflag\fR
+Use the given detailed mode line as the video mode for this output.
+The definition is the same as in
+.BR xorg.conf "(5), and " cvt (1)
+can generate detailed mode lines.
+.TP
+\fBtransform\fR=\fItransform\fR
+Transform for the output, which can be rotated in 90-degree steps
+and possibly flipped. Possible values are
+.BR normal ", " 90 ", " 180 ", " 270 ", "
+.BR flipped ", " flipped-90 ", " flipped-180 ", and " flipped-270 .
+.
+.\" ***************************************************************
+.SH OPTIONS
+.
+When the DRM backend is loaded,
+.B weston
+will understand the following additional command line options.
+.TP
+\fB\-\-connector\fR=\fIconnectorid\fR
+Use the connector with id number
+.I connectorid
+as the only initial output.
+.TP
+.B \-\-current\-mode
+By default, use the current video mode of all outputs, instead of
+switching to the monitor preferred mode.
+.TP
+\fB\-\-seat\fR=\fIseatid\fR
+Use graphics and input devices designated for seat
+.I seatid
+instead of the default seat
+.BR seat0 .
+.TP
+\fB\-\-tty\fR=\fIx\fR
+Launch Weston on tty
+.I x
+instead of using the current tty.
+.
+.\" ***************************************************************
+.SH ENVIRONMENT
+.
+.TP
+.B WESTON_TTY_FD
+The file descriptor (integer) of the opened tty where
+.B weston
+will run. Set by
+.BR weston-launch .
+.TP
+.B WESTON_LAUNCHER_SOCK
+The file descriptor (integer) where
+.B weston-launch
+is listening. Automatically set by
+.BR weston-launch .
+.
+.\" ***************************************************************
+.SH "SEE ALSO"
+.BR weston (1)
+.\".BR weston-launch (1),
+.\".BR weston.ini (5)
diff --git a/man/weston.ini.man b/man/weston.ini.man
new file mode 100644
index 00000000..c5ec3218
--- /dev/null
+++ b/man/weston.ini.man
@@ -0,0 +1,378 @@
+.\" shorthand for double quote that works everywhere.
+.ds q \N'34'
+.TH weston.ini 5 "2013-01-17" "Weston __version__"
+.SH NAME
+weston.ini \- configuration file for
+.B Weston
+\- the reference Wayland
+compositor
+.SH INTRODUCTION
+.B Weston
+obtains configuration from its command line parameters and the configuration
+file described here.
+.SH DESCRIPTION
+.B Weston
+uses a configuration file called
+.I weston.ini
+for its setup.
+The
+.I weston.ini
+configuration file is searched for in one of the following places when the
+server is started:
+.PP
+.RS 4
+.nf
+.BR "$XDG_CONFIG_HOME/weston.ini " "(if $XDG_CONFIG_HOME is set)"
+.BR "$HOME/.config/weston.ini " "(if $HOME is set)"
+.B "weston/weston.ini in each"
+.BR "\ \ \ \ $XDG_CONFIG_DIR " "(if $XDG_CONFIG_DIRS is set)"
+.BR "/etc/xdg/weston/weston.ini " "(if $XDG_CONFIG_DIRS is not set)"
+.BR "<current dir>/weston.ini " "(if no variables were set)"
+.fi
+.RE
+.PP
+where environment variable
+.B $HOME
+is the user's home directory, and
+.B $XDG_CONFIG_HOME
+is the user specific configuration directory, and
+.B $XDG_CONFIG_DIRS
+is a colon
+.B ':'
+delimited listed of configuration base directories, such as
+.BR /etc/xdg-foo:/etc/xdg .
+.PP
+The
+.I weston.ini
+file is composed of a number of sections which may be present in any order, or
+omitted to use default configuration values. Each section has the form:
+.PP
+.RS 4
+.nf
+.BI [ SectionHeader ]
+.RI Key1=Value1
+.RI Key2=Value2
+ ...
+.fi
+.RE
+.PP
+The spaces are significant.
+Comment lines are ignored:
+.PP
+.RS 4
+.nf
+.IR "#comment"
+.fi
+.RE
+.PP
+The section headers are:
+.PP
+.RS 4
+.nf
+.BR "core " "The core modules"
+.BR "shell " "Desktop customization"
+.BR "launcher " "Add launcher to the panel"
+.BR "screensaver " "Screensaver selection"
+.BR "output " "Output configuration"
+.BR "input-method " "Onscreen keyboard input"
+.BR "keyboard " "Keyboard layouts"
+.BR "terminal " "Terminal application options"
+.BR "xwayland " "XWayland options"
+.fi
+.RE
+.PP
+Possible value types are string, signed and unsigned 32-bit
+integer, and boolean. Strings must not be quoted, do not support any
+escape sequences, and run till the end of the line. Integers can
+be given in decimal (e.g. 123), octal (e.g. 0173), and hexadecimal
+(e.g. 0x7b) form. Boolean values can be only 'true' or 'false'.
+.RE
+.SH "CORE SECTION"
+The
+.B core
+section is used to select the startup compositor modules.
+.TP 7
+.BI "modules=" desktop-shell.so,xwayland.so
+specifies the modules to load (string). Available modules in the
+.IR "__weston_modules_dir__"
+directory are:
+.PP
+.RS 10
+.nf
+.BR desktop-shell.so
+.BR tablet-shell.so
+.BR xwayland.so
+.fi
+.RE
+.RS
+.PP
+
+.SH "SHELL SECTION"
+The
+.B shell
+section is used to customize the compositor. Some keys may not be handled by
+different shell plugins.
+.PP
+The entries that can appear in this section are:
+.TP 7
+.BI "background-image=" file
+sets the path for the background image file (string).
+.TP 7
+.BI "background-type=" tile
+determines how the background image is drawn (string). Can be
+.BR scale ", " scale-crop " or " tile " (default)."
+Scale means scaled to fit the output precisely, not preserving aspect ratio.
+Scale-crop preserves aspect ratio, scales the background image just big
+enough to cover the output, and centers it. The image ends up cropped from
+left and right, or top and bottom, if the aspect ratio does not match the
+output. Tile repeats the background image to fill the output.
+.TP 7
+.BI "background-color=" 0xAARRGGBB
+sets the color of the background (unsigned integer). The hexadecimal
+digit pairs are in order alpha, red, green, and blue.
+.TP 7
+.BI "panel-color=" 0xAARRGGBB
+sets the color of the panel (unsigned integer). The hexadecimal
+digit pairs are in order transparency, red, green, and blue. Examples:
+.PP
+.RS 10
+.nf
+.BR "0xffff0000 " "Red"
+.BR "0xff00ff00 " "Green"
+.BR "0xff0000ff " "Blue"
+.BR "0x00ffffff " "Fully transparent"
+.fi
+.RE
+.TP 7
+.BI "locking=" true
+enables screen locking (boolean).
+.TP 7
+.BI "animation=" zoom
+sets the effect used for opening new windows (string). Can be
+.B zoom,
+.B fade,
+.B none.
+By default, no animation is used.
+.TP 7
+.BI "startup-animation=" fade
+sets the effect used for opening new windows (string). Can be
+.B fade,
+.B none.
+By default, the fade animation is used.
+.TP 7
+.BI "binding-modifier=" ctrl
+sets the modifier key used for common bindings (string), such as moving
+surfaces, resizing, rotating, switching, closing and setting the transparency
+for windows, controlling the backlight and zooming the desktop. Possible values:
+ctrl, alt, super (default)
+.TP 7
+.BI "num-workspaces=" 6
+defines the number of workspaces (unsigned integer). The user can switch
+workspaces by using the
+binding+F1, F2 keys. If this key is not set, fall back to one workspace.
+.TP 7
+.BI "cursor-theme=" theme
+sets the cursor theme (string).
+.TP 7
+.BI "cursor-size=" 24
+sets the cursor size (unsigned integer).
+.TP 7
+.BI "lockscreen-icon=" path
+sets the path to lock screen icon image (string). (tablet shell only)
+.TP 7
+.BI "lockscreen=" path
+sets the path to lock screen background image (string). (tablet shell only)
+.TP 7
+.BI "homescreen=" path
+sets the path to home screen background image (string). (tablet shell only)
+.RE
+.SH "LAUNCHER SECTION"
+There can be multiple launcher sections, one for each launcher.
+.TP 7
+.BI "icon=" icon
+sets the path to icon image (string). Svg images are not currently supported.
+.TP 7
+.BI "path=" program
+sets the path to the program that is run by clicking on this launcher (string).
+It is possible to pass arguments and environment variables to the program. For
+example:
+.nf
+.in +4n
+
+path=GDK_BACKEND=wayland gnome-terminal --full-screen
+.in
+.fi
+.PP
+.RE
+.SH "SCREENSAVER SECTION"
+The
+.B screensaver
+section is used to select and schedule a screensaver.
+The
+.B screensaver
+section is optional, as are all of the entries that may be specified in
+it.
+.TP 7
+.BI "path=" /usr/libexec/weston-screensaver
+This instructs the compositor to use the selected screensaver client on a given
+path (string). If this line is missing or commented out, the screensaver in
+.B "weston(1)"
+is disabled.
+.RE
+.TP 7
+.BI "duration=" 600
+The idle time in seconds until the screensaver disappears in order to save power
+(unsigned integer).
+.SH "OUTPUT SECTION"
+There can be multiple output sections, each corresponding to one output. It is
+currently only recognized by the drm and x11 backends.
+.TP 7
+.BI "name=" name
+sets a name for the output (string). The backend uses the name to
+identify the output. All X11 output names start with a letter X. The available
+output names for DRM backend are listed in the
+.B "weston-launch(1)"
+output.
+Examples of usage:
+.PP
+.RS 10
+.nf
+.BR "LVDS1 " "DRM backend, Laptop internal panel no.1"
+.BR "VGA1 " "DRM backend, VGA connector no.1"
+.BR "X1 " "X11 backend, X window no.1"
+.fi
+.RE
+.RS
+.PP
+See
+.B "weston-drm(7)"
+for more details.
+.RE
+.TP 7
+.BI "mode=" mode
+sets the output mode (string). The mode parameter is handled differently
+depending on the backend. On the X11 backend, it just sets the WIDTHxHEIGHT of
+the weston window.
+The DRM backend accepts different modes:
+.PP
+.RS 10
+.nf
+.BR "WIDTHxHEIGHT " "Resolution size width and height in pixels"
+.BR "preferred " "Uses the preferred mode"
+.BR "current " "Uses the current crt controller mode"
+.BR "off " "Disables the output"
+.fi
+.RE
+.RS
+.PP
+Optionally, an user may specify a modeline, such as:
+.PP
+.nf
+.in +4n
+.nf
+173.00 1920 2048 2248 2576 1080 1083 1088 1120 -hsync +vsync
+.fi
+.in
+.PP
+It consists of the refresh rate in Hz, horizontal and vertical resolution,
+options for horizontal and vertical synchronisation. The program
+.B "cvt(1)"
+can provide suitable modeline string.
+.RE
+.TP 7
+.BI "transform=" normal
+The transformation applied to screen output (string). The transform key can
+be one of the following 8 strings:
+.PP
+.RS 10
+.nf
+.BR "normal " "Normal output."
+.BR "90 " "90 degrees clockwise."
+.BR "180 " "Upside down."
+.BR "270 " "90 degrees counter clockwise."
+.BR "flipped " "Horizontally flipped"
+.BR "flipped-90 " "Flipped and 90 degrees clockwise"
+.BR "flipped-180 " "Flipped upside down"
+.BR "flipped-270 " "Flipped and 90 degrees counter clockwise"
+.fi
+.RE
+.TP 7
+.BI "seat=" name
+The logical seat name that that this output should be associated with. If this
+is set then the seat's input will be confined to the output that has the seat
+set on it. The expectation is that this functionality will be used in a
+multiheaded environment with a single compositor for multiple output and input
+configurations. The default seat is called "default" and will always be
+present. This seat can be constrained like any other.
+.RE
+.SH "INPUT-METHOD SECTION"
+.TP 7
+.BI "path=" "/usr/libexec/weston-keyboard"
+sets the path of the on screen keyboard input method (string).
+.RE
+.RE
+.SH "KEYBOARD SECTION"
+This section contains the following keys:
+.TP 7
+.BI "keymap_rules=" "evdev"
+sets the keymap rules file (string). Used to map layout and model to input
+device.
+.RE
+.RE
+.TP 7
+.BI "keymap_model=" "pc105"
+sets the keymap model (string). See the Models section in
+.B "xkeyboard-config(7)."
+.RE
+.RE
+.TP 7
+.BI "keymap_layout=" "us,de,gb"
+sets the comma separated list of keyboard layout codes (string). See the
+Layouts section in
+.B "xkeyboard-config(7)."
+.RE
+.RE
+.TP 7
+.BI "keymap_variant=" "euro,,intl"
+sets the comma separated list of keyboard layout variants (string). The number
+of variants must be the same as the number of layouts above. See the Layouts
+section in
+.B "xkeyboard-config(7)."
+.RE
+.RE
+.TP 7
+.BI "keymap_options=" "grp:alt_shift_toggle,grp_led:scroll"
+sets the keymap options (string). See the Options section in
+.B "xkeyboard-config(7)."
+.RE
+.RE
+.SH "TERMINAL SECTION"
+Contains settings for the weston terminal application (weston-terminal). It
+allows to customize the font and shell of the command line interface.
+.TP 7
+.BI "font=" "DejaVu Sans Mono"
+sets the font of the terminal (string). For a good experience it is recommend
+to use monospace fonts. In case the font is not found, the default one is used.
+.RE
+.RE
+.TP 7
+.BI "font-size=" "14"
+sets the size of the terminal font (unsigned integer).
+.RE
+.RE
+.TP 7
+.BI "term=" "xterm-256color"
+The terminal shell (string). Sets the $TERM variable.
+.RE
+.RE
+.SH "XWAYLAND SECTION"
+.TP 7
+.BI "path=" "/usr/bin/Xorg"
+sets the path to the xserver to run (string).
+.RE
+.RE
+.SH "SEE ALSO"
+.BR weston (1),
+.BR weston-launch (1),
+.BR weston-drm (7),
+.BR xkeyboard-config (7)
diff --git a/man/weston.man b/man/weston.man
new file mode 100644
index 00000000..39d854be
--- /dev/null
+++ b/man/weston.man
@@ -0,0 +1,283 @@
+.TH WESTON 1 "2012-11-27" "Weston __version__"
+.SH NAME
+weston \- the reference Wayland server
+.SH SYNOPSIS
+.B weston
+.
+.\" ***************************************************************
+.SH DESCRIPTION
+.B weston
+is the reference implementation of a Wayland server. A Wayland server is a
+display server, a window manager, and a compositor all in one. Weston has
+several backends as loadable modules: it can run on Linux KMS (kernel
+modesetting via DRM), as an X client, or inside another Wayland server
+instance.
+
+Weston supports fundamentally different graphical user interface paradigms via
+shell plugins. Two plugins are provided: the desktop shell, and the tablet
+shell.
+
+When weston is started as the first windowing system (i.e. not under X nor
+under another Wayland server), it should be done with the command
+.B weston-launch
+to set up proper privileged access to devices.
+
+Weston also supports X clients via
+.BR XWayland ", see below."
+.
+.\" ***************************************************************
+.SH BACKENDS
+.TP
+.I drm-backend.so
+The DRM backend uses Linux KMS for output and evdev devices for input.
+It supports multiple monitors in a unified desktop with DPMS. See
+.BR weston-drm (7),
+if installed.
+.TP
+.I wayland-backend.so
+The Wayland backend runs on another Wayland server, a different Weston
+instance, for example. Weston shows up as a single desktop window on
+the parent server.
+.TP
+.I x11-backend.so
+The X11 backend runs on an X server. Each Weston output becomes an
+X window. This is a cheap way to test multi-monitor support of a
+Wayland shell, desktop, or applications.
+.
+.\" ***************************************************************
+.SH SHELLS
+.TP
+Desktop shell
+Desktop shell is like a modern X desktop environment, concentrating
+on traditional keyboard and mouse user interfaces and the familiar
+desktop-like window management. Desktop shell consists of the
+shell plugin
+.I desktop-shell.so
+and the special client
+.B weston-desktop-shell
+which provides the wallpaper, panel, and screen locking dialog.
+.TP
+Tablet shell
+Tablet shell is a graphical user interface aimed for tablet-like
+devices, where usually the only input method is a touch screen.
+It does not support freely floating windows or many other desktop
+features, but intends to provide a natural interface on tablets.
+Tablet shell consists of the shell plugin
+.I tablet-shell.so
+and the special client
+.B weston-tablet-shell
+which provides the basic user interface.
+.
+.\" ***************************************************************
+.SH XWAYLAND
+XWayland requires a special X.org server to be installed. This X server will
+connect to a Wayland server as a Wayland client, and X clients will connect to
+the X server. XWayland provides backwards compatibility to X applications in a
+Wayland stack.
+
+XWayland is activated by instructing
+.BR weston " to load " xwayland.so " module, see " EXAMPLES .
+Weston starts listening on a new X display socket, and exports it in the
+environment variable
+.BR DISPLAY .
+When the first X client connects, Weston launches a special X server as a
+Wayland client to handle the X client and all future X clients.
+
+It has also its own X window manager where cursor themes and sizes can be
+chosen using
+.BR XCURSOR_PATH
+and
+.BR XCURSOR_SIZE " environment variables. See " ENVIRONMENT .
+.
+.\" ***************************************************************
+.SH OPTIONS
+.
+.SS Weston core options:
+.TP
+\fB\-\^B\fR\fIbackend.so\fR, \fB\-\-backend\fR=\fIbackend.so\fR
+Load
+.I backend.so
+instead of the default backend. The file is searched for in
+.IR "__weston_modules_dir__" ,
+or you can pass an absolute path. The default backend is
+.I __weston_native_backend__
+unless the environment suggests otherwise, see
+.IR DISPLAY " and " WAYLAND_DISPLAY .
+.TP
+.BR \-\-version
+Print the program version.
+.TP
+.BR \-\^h ", " \-\-help
+Print a summary of command line options, and quit.
+.TP
+\fB\-\^i\fR\fIN\fR, \fB\-\-idle\-time\fR=\fIN\fR
+Set the idle timeout to
+.I N
+seconds. The default timeout is 300 seconds. When there has not been any
+user input for the idle timeout, Weston enters an inactive mode. The
+screen fades to black, and depending on the shell in use, a screensaver
+may activate, monitors may switch off, and the shell may lock the session.
+A value of 0 effectively disables the timeout.
+.TP
+\fB\-\-log\fR=\fIfile.log\fR
+Append log messages to the file
+.I file.log
+instead of writing them to stderr.
+.TP
+\fB\-\-modules\fR=\fImodule1.so,module2.so\fR
+Load the comma-separated list of modules. Only used by the test
+suite. The file is searched for in
+.IR "__weston_modules_dir__" ,
+or you can pass an absolute path.
+.TP
+\fB\-\^S\fR\fIname\fR, \fB\-\-socket\fR=\fIname\fR
+Weston will listen in the Wayland socket called
+.IR name .
+Weston will export
+.B WAYLAND_DISPLAY
+with this value in the environment for all child processes to allow them to
+connect to the right server automatically.
+.SS DRM backend options:
+See
+.BR weston-drm (7).
+.
+.SS Wayland backend options:
+.TP
+\fB\-\-display\fR=\fIdisplay\fR
+Name of the Wayland display to connect to, see also
+.I WAYLAND_DISPLAY
+of the environment.
+.TP
+\fB\-\-width\fR=\fIW\fR, \fB\-\-height\fR=\fIH\fR
+Make the desktop size
+.IR W x H " pixels."
+.
+.SS X11 backend options:
+.TP
+.B \-\-fullscreen
+.TP
+.B \-\-no\-input
+Do not provide any input devices. Used for testing input-less Weston.
+.TP
+\fB\-\-output\-count\fR=\fIN\fR
+Create
+.I N
+X windows to emulate the same number of outputs.
+.TP
+\fB\-\-width\fR=\fIW\fR, \fB\-\-height\fR=\fIH\fR
+Make the default size of each X window
+.IR W x H " pixels."
+.TP
+.B \-\-use\-pixman
+Use the pixman renderer. By default weston will try to use EGL and
+GLES2 for rendering. Passing this option will make weston use the
+pixman library for software compsiting.
+.
+.\" ***************************************************************
+.SH FILES
+.
+If the environment variable is set, the configuration file is read
+from the respective path, or the current directory if neither is set.
+.PP
+.BI $XDG_CONFIG_HOME /weston.ini
+.br
+.BI $HOME /.config/weston.ini
+.br
+.I ./weston.ini
+.br
+.
+.\" ***************************************************************
+.SH ENVIRONMENT
+.
+.TP
+.B DISPLAY
+The X display. If
+.B DISPLAY
+is set, and
+.B WAYLAND_DISPLAY
+is not set, the default backend becomes
+.IR x11-backend.so .
+.TP
+.B WAYLAND_DEBUG
+If set to any value, causes libwayland to print the live protocol
+to stderr.
+.TP
+.B WAYLAND_DISPLAY
+The name of the display (socket) of an already running Wayland server, without
+the path. The directory path is always taken from
+.BR XDG_RUNTIME_DIR .
+If
+.B WAYLAND_DISPLAY
+is not set, the socket name is "wayland-0".
+
+If
+.B WAYLAND_DISPLAY
+is already set, the default backend becomes
+.IR wayland-backend.so .
+This allows launching Weston as a nested server.
+.TP
+.B WAYLAND_SOCKET
+For Wayland clients, holds the file descriptor of an open local socket
+to a Wayland server.
+.TP
+.B XCURSOR_PATH
+Set the list of paths to look for cursors in. It changes both
+libwayland-cursor and libXcursor, so it affects both Wayland and X11 based
+clients. See
+.B xcursor
+(3).
+.TP
+.B XCURSOR_SIZE
+This variable can be set for choosing an specific size of cursor. Affect
+Wayland and X11 clients. See
+.B xcursor
+(3).
+.TP
+.B XDG_CONFIG_HOME
+If set, specifies the directory where to look for
+.BR weston.ini .
+.TP
+.B XDG_RUNTIME_DIR
+The directory for Weston's socket and lock files.
+Wayland clients will automatically use this.
+.
+.\" ***************************************************************
+.SH DIAGNOSTICS
+Weston has a segmentation fault handler, that attempts to restore
+the virtual console or ungrab X before raising
+.BR SIGTRAP .
+If you run
+.BR weston " under " gdb (1)
+from an X11 terminal or a different virtual terminal, and tell gdb
+.IP
+handle SIGSEGV nostop
+.PP
+This will allow weston to switch back to gdb on crash and then
+gdb will catch the crash with SIGTRAP.
+.
+.\" ***************************************************************
+.SH BUGS
+Bugs should be reported to the freedesktop.org bugzilla at
+https://bugs.freedesktop.org with product "Wayland" and
+component "weston".
+.
+.\" ***************************************************************
+.SH WWW
+http://wayland.freedesktop.org/
+.
+.\" ***************************************************************
+.SH EXAMPLES
+.IP "Launch Weston with the DRM backend on a VT"
+weston-launch
+.IP "Launch Weston with the DRM backend and XWayland support"
+weston-launch -- --modules=xwayland.so
+.IP "Launch Weston (wayland-1) nested in another Weston instance (wayland-0)"
+WAYLAND_DISPLAY=wayland-0 weston -Swayland-1
+.IP "From an X terminal, launch Weston with the x11 backend"
+weston
+.
+.\" ***************************************************************
+.SH "SEE ALSO"
+.BR weston-drm (7)
+.\".BR weston-launch (1),
+.\".BR weston.ini (5)
diff --git a/notes.txt b/notes.txt
new file mode 100644
index 00000000..e49052b8
--- /dev/null
+++ b/notes.txt
@@ -0,0 +1,77 @@
+This file is a collection of informal notes, with references to where
+they were originally written. Each note should have a source and date
+mentioned. Let's keep these in date order, newest first.
+
+
+
+-----------------------------------------------------------------------
+2012-10-23; Pekka Paalanen <ppaalanen@gmail.com>
+http://lists.freedesktop.org/archives/wayland-devel/2012-October/005969.html
+
+For anyone wanting to port or write their own window manager to Wayland:
+
+Most likely you have a desktop window manager. A quick way to get
+started, is to fork Weston's desktop-shell plugin and start hacking it.
+Qt could be another good choice, but I am not familiar with it.
+
+You also need to understand some concepts. I'm repeating things I wrote
+to the wayland-devel list earlier, a little rephrased.
+
+We need to distinguish three different things here (towards Wayland
+clients):
+
+- compositors (servers)
+ All Wayland compositors are indistinguishable by definition,
+ since they are Wayland compositors. They only differ in the
+ global interfaces they advertise, and for general purpose
+ compositors, we should aim to support the same minimum set of
+ globals everywhere. For instance, all desktop compositors
+ should implement wl_shell. In X, this component corresponds to
+ the X server with a built-in compositing manager.
+
+- shells
+ This is a new concept compared to an X stack. A shell defines
+ how a user and applications interact. The most familiar is a
+ desktop (environment). If KDE, Gnome, and XFCE are desktop
+ environments, they all fall under the *same* shell: the desktop
+ shell. You can have applications in windows, several visible at
+ the same time, you have keyboards and mice, etc.
+
+ An example of something that is not a desktop shell
+ could be a TV user interface. TV is profoundly different:
+ usually no mouse, no keyboard, but you have a remote control
+ with some buttons. Freely floating windows probably do not make
+ sense. You may have picture-in-picture, but usually not several
+ applications showing at once. Most importantly, trying to run
+ desktop applications here does not work due to the
+ incompatible application and user interface paradigms.
+
+ On protocol level, a shell is the public shell interface(s),
+ currently for desktop it is the wl_shell.
+
+- "window managers"
+ The X Window Managers correspond to different wl_shell
+ implementations, not different shells, since they pratically
+ all deal with a desktop environment. You also want all desktop
+ applications to work with all window managers, so you need to
+ implement wl_shell anyway.
+
+I understand there could be special purpose X Window Managers, that
+would better correspond to their own shells. These window managers
+might not implement e.g. EWMH by the spec.
+
+When you implement your own window manager, you want to keep the public
+desktop shell interface (wl_shell). You can offer new public
+interfaces, too, but keep in mind, that someone needs to make
+applications use them.
+
+In Weston, a shell implementation has two parts: a weston plugin, and a
+special client. For desktop shell (wl_shell) these are src/shell.c and
+clients/desktop-shell.c. The is also a private protocol extension that
+these two can explicitly communicate with.
+
+The plugin does window management, and the client does most of user
+interaction: draw backgrounds, panels, buttons, lock screen dialog,
+basically everything that is on screen.
+
+-----------------------------------------------------------------------
diff --git a/protocol/Makefile.am b/protocol/Makefile.am
new file mode 100644
index 00000000..924e48f3
--- /dev/null
+++ b/protocol/Makefile.am
@@ -0,0 +1,11 @@
+EXTRA_DIST = \
+ desktop-shell.xml \
+ screenshooter.xml \
+ tablet-shell.xml \
+ xserver.xml \
+ text.xml \
+ input-method.xml \
+ workspaces.xml \
+ subsurface.xml \
+ text-cursor-position.xml \
+ wayland-test.xml
diff --git a/protocol/desktop-shell.xml b/protocol/desktop-shell.xml
new file mode 100644
index 00000000..65e44a73
--- /dev/null
+++ b/protocol/desktop-shell.xml
@@ -0,0 +1,112 @@
+<protocol name="desktop">
+
+ <interface name="desktop_shell" version="2">
+ <description summary="create desktop widgets and helpers">
+ Traditional user interfaces can rely on this interface to define the
+ foundations of typical desktops. Currently it's possible to set up
+ background, panels and locking surfaces.
+ </description>
+
+ <request name="set_background">
+ <arg name="output" type="object" interface="wl_output"/>
+ <arg name="surface" type="object" interface="wl_surface"/>
+ </request>
+
+ <request name="set_panel">
+ <arg name="output" type="object" interface="wl_output"/>
+ <arg name="surface" type="object" interface="wl_surface"/>
+ </request>
+
+ <request name="set_lock_surface">
+ <arg name="surface" type="object" interface="wl_surface"/>
+ </request>
+
+ <request name="unlock"/>
+
+ <request name="set_grab_surface">
+ <description summary="set grab surface">
+ The surface set by this request will receive a fake
+ pointer.enter event during grabs at position 0, 0 and is
+ expected to set an appropriate cursor image as described by
+ the grab_cursor event sent just before the enter event.
+ </description>
+ <arg name="surface" type="object" interface="wl_surface"/>
+ </request>
+
+ <request name="desktop_ready" since="2">
+ <description summary="desktop is ready to be shown">
+ Tell the server, that enough desktop elements have been drawn
+ to make the desktop look ready for use. During start-up, the
+ server can wait for this request with a black screen before
+ starting to fade in the desktop, for instance. If the client
+ parts of a desktop take a long time to initialize, we avoid
+ showing temporary garbage.
+ </description>
+ </request>
+
+ <!-- We'll fold most of wl_shell into this interface and then
+ they'll share the configure event. -->
+ <event name="configure">
+ <arg name="edges" type="uint"/>
+ <arg name="surface" type="object" interface="wl_surface"/>
+ <arg name="width" type="int"/>
+ <arg name="height" type="int"/>
+ </event>
+
+ <event name="prepare_lock_surface">
+ <description summary="tell the client to create, set the lock surface">
+ Tell the shell we want it to create and set the lock surface, which is
+ a GUI asking the user to unlock the screen. The lock surface is
+ announced with 'set_lock_surface'. Whether or not the shell actually
+ implements locking, it MUST send 'unlock' request to let the normal
+ desktop resume.
+ </description>
+ </event>
+
+ <event name="grab_cursor">
+ <description summary="tell client what cursor to show during a grab">
+ This event will be sent immediately before a fake enter event on the
+ grab surface.
+ </description>
+ <arg name="cursor" type="uint"/>
+ </event>
+
+ <enum name="cursor">
+ <entry name="none" value="0"/>
+
+ <entry name="resize_top" value="1"/>
+ <entry name="resize_bottom" value="2"/>
+
+ <entry name="arrow" value="3"/>
+
+ <entry name="resize_left" value="4"/>
+ <entry name="resize_top_left" value="5"/>
+ <entry name="resize_bottom_left" value="6"/>
+
+ <entry name="move" value="7"/>
+
+ <entry name="resize_right" value="8"/>
+ <entry name="resize_top_right" value="9"/>
+ <entry name="resize_bottom_right" value="10"/>
+
+ <entry name="busy" value="11"/>
+ </enum>
+ </interface>
+
+ <interface name="screensaver" version="1">
+ <description summary="interface for implementing screensavers">
+ Only one client can bind this interface at a time.
+ </description>
+
+ <request name="set_surface">
+ <description summary="set the surface type as a screensaver">
+ A screensaver surface is normally hidden, and only visible after an
+ idle timeout.
+ </description>
+
+ <arg name="surface" type="object" interface="wl_surface"/>
+ <arg name="output" type="object" interface="wl_output"/>
+ </request>
+
+ </interface>
+</protocol>
diff --git a/protocol/input-method.xml b/protocol/input-method.xml
new file mode 100644
index 00000000..70afdcb1
--- /dev/null
+++ b/protocol/input-method.xml
@@ -0,0 +1,273 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="input_method">
+ <copyright>
+ Copyright © 2012, 2013 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 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>
+
+
+ <interface name="wl_input_method_context" version="1">
+ <description summary="input method context">
+ Corresponds to a text model on input method side. An input method context
+ is created on text mode activation on the input method side. It allows to
+ receive information about the text model from the application via events.
+ Input method contexts do not keep state after deactivation and should be
+ destroyed after deactivation is handled.
+
+ 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).
+ </description>
+ <request name="destroy" type="destructor"/>
+ <request name="commit_string">
+ <description summary="commit string">
+ Send the commit string text for insertion to the application.
+
+ 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 be also 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 will be removed.
+ </description>
+ <arg name="serial" type="uint" summary="serial of the latest known text input state"/>
+ <arg name="text" type="string"/>
+ </request>
+ <request name="preedit_string">
+ <description summary="pre-edit string">
+ Send the pre-edit string text to the application text input.
+
+ The commit text can be used to replace the preedit text on reset (for
+ example on unfocus).
+
+ Also previously sent preedit_style and preedit_cursor requests are
+ processed bt the text_input also.
+ </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"/>
+ </request>
+ <request name="preedit_styling">
+ <description summary="pre-edit styling">
+ Sets styling information on composing text. The style is applied for
+ length in bytes from index relative to the beginning of
+ the composing text (as byte offset). Multiple styles can
+ be applied to a composing text.
+
+ This request should be sent before sending preedit_string request.
+ </description>
+ <arg name="index" type="uint"/>
+ <arg name="length" type="uint"/>
+ <arg name="style" type="uint"/>
+ </request>
+ <request 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 negative no cursor should be displayed.
+
+ This request should be sent before sending preedit_string request.
+ </description>
+ <arg name="index" type="int"/>
+ </request>
+ <request name="delete_surrounding_text">
+ <description summary="delete text">
+
+
+ This request will be handled on text_input side as part of a directly
+ following commit_string request.
+ </description>
+ <arg name="index" type="int"/>
+ <arg name="length" type="uint"/>
+ </request>
+ <request name="cursor_position">
+ <description summary="set cursor to a new position">
+ Sets the cursor and anchor to a new position. Index is the new cursor
+ position in bytess (when >= 0 relative to the end of inserted text
+ else relative to beginning of inserted text). Anchor is the new anchor
+ position in bytes (when >= 0 relative to the end of inserted text, else
+ relative to beginning of inserted text). When there should be no
+ selected text anchor should be the same as index.
+
+ This request will be handled on text_input side as part of a directly
+ following commit_string request.
+ </description>
+ <arg name="index" type="int"/>
+ <arg name="anchor" type="int"/>
+ </request>
+ <request name="modifiers_map">
+ <arg name="map" type="array"/>
+ </request>
+ <request 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_surrounfing_text, etc. The key event follows the wl_keyboard key
+ event convention. Sym is a XKB keysym, state a wl_keyboard key_state.
+ </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"/>
+ </request>
+ <request name="grab_keyboard">
+ <description summary="grab hardware keyboard">
+ Allows an input method to receive hardware keyboard input and process
+ key events to generate text events (with pre-edit) over the wire. This
+ allows input methods which compose multiple key events for inputting
+ text like it is done for CJK languages.
+ </description>
+ <arg name="keyboard" type="new_id" interface="wl_keyboard"/>
+ </request>
+ <request name="key">
+ <description summary="forward key event">
+ Should be used when filtering key events with grab_keyboard.
+
+ When the wl_keyboard::key event is not processed by the input
+ method itself and should be sent to the client instead, forward it
+ with this request. The arguments should be the ones from the
+ wl_keyboard::key event.
+
+ For generating custom key events use the keysym request instead.
+ </description>
+ <arg name="serial" type="uint" summary="serial from wl_keyboard::key"/>
+ <arg name="time" type="uint" summary="time from wl_keyboard::key"/>
+ <arg name="key" type="uint" summary="key from wl_keyboard::key"/>
+ <arg name="state" type="uint" summary="state from wl_keyboard::key"/>
+ </request>
+ <request name="modifiers">
+ <description summary="forward modifiers event">
+ Should be used when filtering key events with grab_keyboard.
+
+ When the wl_keyboard::modifiers event should be also send to the
+ client, forward it with this request. The arguments should be the ones
+ from the wl_keyboard::modifiers event.
+ </description>
+ <arg name="serial" type="uint" summary="serial from wl_keyboard::modifiers"/>
+ <arg name="mods_depressed" type="uint" summary="mods_depressed from wl_keyboard::modifiers"/>
+ <arg name="mods_latched" type="uint" summary="mods_latched from wl_keyboard::modifiers"/>
+ <arg name="mods_locked" type="uint" summary="mods_locked from wl_keyboard::modifiers"/>
+ <arg name="group" type="uint" summary="group from wl_keyboard::modifiers"/>
+ </request>
+ <request name="language">
+ <arg name="serial" type="uint" summary="serial of the latest known text input state"/>
+ <arg name="language" type="string"/>
+ </request>
+ <request name="text_direction">
+ <arg name="serial" type="uint" summary="serial of the latest known text input state"/>
+ <arg name="direction" type="uint"/>
+ </request>
+ <event name="surrounding_text">
+ <description summary="surrounding text event">
+ The plain surrounding text around the input position. Cursor is the
+ position in bytes within the surrounding text relative to the beginning
+ of the text. Anchor is the position in bytes of the selection anchor
+ within the surrounding text relative to the beginning of the text. If
+ there is no selected text anchor is the same as cursor.
+ </description>
+ <arg name="text" type="string"/>
+ <arg name="cursor" type="uint"/>
+ <arg name="anchor" type="uint"/>
+ </event>
+ <event name="reset">
+ </event>
+ <event name="content_type">
+ <arg name="hint" type="uint"/>
+ <arg name="purpose" type="uint"/>
+ </event>
+ <event name="invoke_action">
+ <arg name="button" type="uint"/>
+ <arg name="index" type="uint"/>
+ </event>
+ <event name="commit_state">
+ <arg name="serial" type="uint" summary="serial of text input state"/>
+ </event>
+ <event name="preferred_language">
+ <arg name="language" type="string"/>
+ </event>
+ </interface>
+
+ <interface name="wl_input_method" version="1">
+ <description summary="input method">
+ An input method object is responsible to compose text in response to
+ input from hardware or virtual keyboards. There is one input method
+ object per seat. On activate there is a new input method context object
+ created which allows the input method to communicate with the text model.
+ </description>
+ <event name="activate">
+ <description summary="activate event">
+ A text model was activated. Creates an input method context object
+ which allows communication with the text model.
+ </description>
+ <arg name="id" type="new_id" interface="wl_input_method_context"/>
+ </event>
+ <event name="deactivate">
+ <description summary="activate event">
+ The text model corresponding to the context argument was deactivated.
+ The input method context should be destroyed after deactivation is
+ handled.
+ </description>
+ <arg name="context" type="object" interface="wl_input_method_context"/>
+ </event>
+ </interface>
+
+ <interface name="wl_input_panel" version="1">
+ <description summary="interface for implementing keyboards">
+ Only one client can bind this interface at a time.
+ </description>
+
+ <request name="get_input_panel_surface">
+ <arg name="id" type="new_id" interface="wl_input_panel_surface"/>
+ <arg name="surface" type="object" interface="wl_surface"/>
+ </request>
+ </interface>
+
+ <interface name="wl_input_panel_surface" version="1">
+ <enum name="position">
+ <entry name="center_bottom" value="0"/>
+ </enum>
+
+ <request name="set_toplevel">
+ <description summary="set the surface type as a keyboard">
+ A keybaord surface is only shown, when a text model is active
+ </description>
+ <arg name="output" type="object" interface="wl_output"/>
+ <arg name="position" type="uint"/>
+ </request>
+
+ <request name="set_overlay_panel">
+ <description summary="set the surface type as an overlay panel">
+ An overlay panel is shown near the input cursor above the application
+ window when a text model is active.
+ </description>
+ </request>
+ </interface>
+</protocol>
diff --git a/protocol/screenshooter.xml b/protocol/screenshooter.xml
new file mode 100644
index 00000000..76e3c85a
--- /dev/null
+++ b/protocol/screenshooter.xml
@@ -0,0 +1,12 @@
+<protocol name="screenshooter">
+
+ <interface name="screenshooter" version="1">
+ <request name="shoot">
+ <arg name="output" type="object" interface="wl_output"/>
+ <arg name="buffer" type="object" interface="wl_buffer"/>
+ </request>
+ <event name="done">
+ </event>
+ </interface>
+
+</protocol>
diff --git a/protocol/subsurface.xml b/protocol/subsurface.xml
new file mode 100644
index 00000000..9e4a658d
--- /dev/null
+++ b/protocol/subsurface.xml
@@ -0,0 +1,244 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="subsurface">
+
+ <copyright>
+ Copyright © 2012-2013 Collabora, Ltd.
+
+ Permission to use, copy, modify, distribute, and sell this
+ software and its documentation for any purpose is hereby granted
+ without fee, provided that 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>
+
+ <interface name="wl_subcompositor" version="1">
+ <description summary="sub-surface compositing">
+ The global interface exposing sub-surface compositing capabilities.
+ A wl_surface, that has sub-surfaces associated, is called the
+ parent surface. Sub-surfaces can be arbitrarily nested and create
+ a tree of sub-surfaces.
+
+ The root surface in a tree of sub-surfaces is the main
+ surface. The main surface cannot be a sub-surface, because
+ sub-surfaces must always have a parent.
+
+ A main surface with its sub-surfaces forms a (compound) window.
+ For window management purposes, this set of wl_surface objects is
+ to be considered as a single window, and it should also behave as
+ such.
+
+ The aim of sub-surfaces is to offload some of the compositing work
+ within a window from clients to the compositor. A prime example is
+ a video player with decorations and video in separate wl_surface
+ objects. This should allow the compositor to pass YUV video buffer
+ processing to dedicated overlay hardware when possible.
+ </description>
+
+ <request name="destroy" type="destructor">
+ <description summary="unbind from the subcompositor interface">
+ Informs the server that the client will not be using this
+ protocol object anymore. This does not affect any other
+ objects, wl_subsurface objects included.
+ </description>
+ </request>
+
+ <enum name="error">
+ <entry name="bad_surface" value="0"
+ summary="the to-be sub-surface is invalid"/>
+ </enum>
+
+ <request name="get_subsurface">
+ <description summary="give a surface the role sub-surface">
+ Create a sub-surface interface for the given surface, and
+ associate it with the given parent surface. This turns a
+ plain wl_surface into a sub-surface.
+
+ The to-be sub-surface must not already have a dedicated
+ purpose, like any shell surface type, cursor image, drag icon,
+ or sub-surface. Otherwise a protocol error is raised.
+ </description>
+
+ <arg name="id" type="new_id" interface="wl_subsurface"
+ summary="the new subsurface object id"/>
+ <arg name="surface" type="object" interface="wl_surface"
+ summary="the surface to be turned into a sub-surface"/>
+ <arg name="parent" type="object" interface="wl_surface"
+ summary="the parent surface"/>
+ </request>
+ </interface>
+
+ <interface name="wl_subsurface" version="1">
+ <description summary="sub-surface interface to a wl_surface">
+ An additional interface to a wl_surface object, which has been
+ made a sub-surface. A sub-surface has one parent surface. A
+ sub-surface's size and position are not limited to that of the parent.
+ Particularly, a sub-surface is not automatically clipped to its
+ parent's area.
+
+ A sub-surface becomes mapped, when a non-NULL wl_buffer is applied
+ and the parent surface is mapped. The order of which one happens
+ first is irrelevant. A sub-surface is hidden if the parent becomes
+ hidden, or if a NULL wl_buffer is applied. These rules apply
+ recursively through the tree of surfaces.
+
+ The behaviour of wl_surface.commit request on a sub-surface
+ depends on the sub-surface's mode. The possible modes are
+ synchronized and desynchronized, see methods
+ wl_subsurface.set_sync and wl_subsurface.set_desync. Synchronized
+ mode caches the wl_surface state to be applied when the parent's
+ state gets applied, and desynchronized mode applies the pending
+ wl_surface state directly. A sub-surface is initially in the
+ synchronized mode.
+
+ Sub-surfaces have also other kind of state, which is managed by
+ wl_subsurface requests, as opposed to wl_surface requests. This
+ state includes the sub-surface position relative to the parent
+ surface (wl_subsurface.set_position), and the stacking order of
+ the parent and its sub-surfaces (wl_subsurface.place_above and
+ .place_below). This state is applied when the parent surface's
+ wl_surface state is applied, regardless of the sub-surface's mode.
+ As the exception, set_sync and set_desync are effective immediately.
+
+ The main surface can be thought to be always in desynchronized mode,
+ since it does not have a parent in the sub-surfaces sense.
+
+ Even if a sub-surface is in desynchronized mode, it will behave as
+ in synchronized mode, if its parent surface behaves as in
+ synchronized mode. This rule is applied recursively throughout the
+ tree of surfaces. This means, that one can set a sub-surface into
+ synchronized mode, and then assume that all its child and grand-child
+ sub-surfaces are synchronized, too, without explicitly setting them.
+
+ If the wl_surface associated with the wl_subsurface is destroyed, the
+ wl_subsurface object becomes inert. Note, that destroying either object
+ takes effect immediately. If you need to synchronize the removal
+ of a sub-surface to the parent surface update, unmap the sub-surface
+ first by attaching a NULL wl_buffer, update parent, and then destroy
+ the sub-surface.
+
+ If the parent wl_surface object is destroyed, the sub-surface is
+ unmapped.
+ </description>
+
+ <request name="destroy" type="destructor">
+ <description summary="remove sub-surface interface">
+ The sub-surface interface is removed from the wl_surface object
+ that was turned into a sub-surface with
+ wl_subcompositor.get_subsurface request. The wl_surface's association
+ to the parent is deleted, and the wl_surface loses its role as
+ a sub-surface. The wl_surface is unmapped.
+ </description>
+ </request>
+
+ <enum name="error">
+ <entry name="bad_surface" value="0"
+ summary="wl_surface is not a sibling or the parent"/>
+ </enum>
+
+ <request name="set_position">
+ <description summary="reposition the sub-surface">
+ This schedules a sub-surface position change.
+ The sub-surface will be moved so, that its origin (top-left
+ corner pixel) will be at the location x, y of the parent surface
+ coordinate system. The coordinates are not restricted to the parent
+ surface area. Negative values are allowed.
+
+ The next wl_surface.commit on the parent surface will reset
+ the sub-surface's position to the scheduled coordinates.
+
+ The initial position is 0, 0.
+ </description>
+
+ <arg name="x" type="int" summary="coordinate in the parent surface"/>
+ <arg name="y" type="int" summary="coordinate in the parent surface"/>
+ </request>
+
+ <request name="place_above">
+ <description summary="restack the sub-surface">
+ This sub-surface is taken from the stack, and put back just
+ above the reference surface, changing the z-order of the sub-surfaces.
+ The reference surface must be one of the sibling surfaces, or the
+ parent surface. Using any other surface, including this sub-surface,
+ will cause a protocol error.
+
+ The z-order is double-buffered state, and will be applied on the
+ next commit of the parent surface.
+ See wl_surface.commit and wl_subcompositor.get_subsurface.
+
+ A new sub-surface is initially added as the top-most in the stack
+ of its siblings and parent.
+ </description>
+
+ <arg name="sibling" type="object" interface="wl_surface"
+ summary="the reference surface"/>
+ </request>
+
+ <request name="place_below">
+ <description summary="restack the sub-surface">
+ The sub-surface is placed just below of the reference surface.
+ See wl_subsurface.place_above.
+ </description>
+
+ <arg name="sibling" type="object" interface="wl_surface"
+ summary="the reference surface"/>
+ </request>
+
+ <request name="set_sync">
+ <description summary="set sub-surface to synchronized mode">
+ Change the commit behaviour of the sub-surface to synchronized
+ mode, also described as the parent dependant mode.
+
+ In synchronized mode, wl_surface.commit on a sub-surface will
+ accumulate the committed state in a cache, but the state will
+ not be applied and hence will not change the compositor output.
+ The cached state is applied to the sub-surface immediately after
+ the parent surface's state is applied. This ensures atomic
+ updates of the parent and all its synchronized sub-surfaces.
+ Applying the cached state will invalidate the cache, so further
+ parent surface commits do not (re-)apply old state.
+
+ See wl_subsurface for the recursive effect of this mode.
+ </description>
+ </request>
+
+ <request name="set_desync">
+ <description summary="set sub-surface to desynchronized mode">
+ Change the commit behaviour of the sub-surface to desynchronized
+ mode, also described as independent or freely running mode.
+
+ In desynchronized mode, wl_surface.commit on a sub-surface will
+ apply the pending state directly, without caching, as happens
+ normally with a wl_surface. Calling wl_surface.commit on the
+ parent surface has no effect on the sub-surface's wl_surface
+ state. This mode allows a sub-surface to be updated on its own.
+
+ If cached state exists when wl_surface.commit is called in
+ desynchronized mode, the pending state is added to the cached
+ state, and applied as whole. This invalidates the cache.
+
+ Note: even if a sub-surface is set to desynchronized, a parent
+ sub-surface may override it to behave as synchronized. For details,
+ see wl_subsurface.
+
+ If a surface's parent surface behaves as desynchronized, then
+ the cached state is applied on set_desync.
+ </description>
+ </request>
+
+ </interface>
+</protocol>
diff --git a/protocol/tablet-shell.xml b/protocol/tablet-shell.xml
new file mode 100644
index 00000000..10f17568
--- /dev/null
+++ b/protocol/tablet-shell.xml
@@ -0,0 +1,40 @@
+<protocol name="tablet">
+
+ <interface name="tablet_shell" version="1">
+ <request name="set_lockscreen">
+ <arg name="surface" type="object" interface="wl_surface"/>
+ </request>
+
+ <request name="set_switcher">
+ <arg name="surface" type="object" interface="wl_surface"/>
+ </request>
+
+ <request name="set_homescreen">
+ <arg name="surface" type="object" interface="wl_surface"/>
+ </request>
+
+ <request name="show_grid">
+ <arg name="surface" type="object" interface="wl_surface"/>
+ </request>
+
+ <request name="show_panels">
+ <arg name="surface" type="object" interface="wl_surface"/>
+ </request>
+
+ <request name="create_client">
+ <arg name="id" type="new_id" interface="tablet_client"/>
+ <arg name="name" type="string"/>
+ <arg name="fd" type="fd"/>
+ </request>
+
+ <event name="show_lockscreen"/>
+ <event name="show_switcher"/>
+ <event name="hide_switcher"/>
+ </interface>
+
+ <interface name="tablet_client" version="1">
+ <request name="destroy" type="destructor"/>
+ <request name="activate"/>
+ </interface>
+
+</protocol>
diff --git a/protocol/text-cursor-position.xml b/protocol/text-cursor-position.xml
new file mode 100644
index 00000000..0fbc54e7
--- /dev/null
+++ b/protocol/text-cursor-position.xml
@@ -0,0 +1,11 @@
+<protocol name="text_cursor_position">
+
+ <interface name="text_cursor_position" version="1">
+ <request name="notify">
+ <arg name="surface" type="object" interface="wl_surface"/>
+ <arg name="x" type="fixed"/>
+ <arg name="y" type="fixed"/>
+ </request>
+ </interface>
+
+</protocol>
diff --git a/protocol/text.xml b/protocol/text.xml
new file mode 100644
index 00000000..1b5284dc
--- /dev/null
+++ b/protocol/text.xml
@@ -0,0 +1,346 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="text">
+
+ <copyright>
+ Copyright © 2012, 2013 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 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>
+
+ <interface name="wl_text_input" 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).
+ </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 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 a RFC-3066
+ format language tag.
+
+ It could be used for example in a word processor to indicate language of
+ 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 modifiers 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 occuring 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 be also 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 a 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 a 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 input cursor on correct side of the
+ editor when there is no input yet done 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="wl_text_input_manager" 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="wl_text_input"/>
+ </request>
+ </interface>
+</protocol>
diff --git a/protocol/wayland-test.xml b/protocol/wayland-test.xml
new file mode 100644
index 00000000..2993f087
--- /dev/null
+++ b/protocol/wayland-test.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="wl_test">
+
+ <copyright>
+ Copyright © 2012 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 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>
+
+ <interface name="wl_test" version="1">
+ <request name="move_surface">
+ <arg name="surface" type="object" interface="wl_surface"/>
+ <arg name="x" type="int"/>
+ <arg name="y" type="int"/>
+ </request>
+ <request name="move_pointer">
+ <arg name="x" type="int"/>
+ <arg name="y" type="int"/>
+ </request>
+ <request name="send_button">
+ <arg name="button" type="int"/>
+ <arg name="state" type="uint"/>
+ </request>
+ <request name="activate_surface">
+ <arg name="surface" type="object" interface="wl_surface" allow-null="true"/>
+ </request>
+ <request name="send_key">
+ <arg name="key" type="uint"/>
+ <arg name="state" type="uint"/>
+ </request>
+ <event name="pointer_position">
+ <arg name="x" type="fixed"/>
+ <arg name="y" type="fixed"/>
+ </event>
+ </interface>
+</protocol>
diff --git a/protocol/workspaces.xml b/protocol/workspaces.xml
new file mode 100644
index 00000000..22f4802b
--- /dev/null
+++ b/protocol/workspaces.xml
@@ -0,0 +1,27 @@
+<protocol name="workspaces">
+
+ <interface name="workspace_manager" version="1">
+ <description summary="workspaces manager">
+ An interface for managing surfaces in workspaces.
+ </description>
+
+ <request name="move_surface">
+ <description summary="move surface to workspace">
+ Move the given surface to the specified workspace.
+ </description>
+ <arg name="surface" type="object" interface="wl_surface"/>
+ <arg name="workspace" type="uint"/>
+ </request>
+
+ <event name="state">
+ <description summary="workspace state">
+ The current workspace state, such as current workspace and workspace
+ count, has changed.
+ </description>
+ <arg name="current" type="uint"/>
+ <arg name="count" type="uint"/>
+ </event>
+
+ </interface>
+
+</protocol>
diff --git a/protocol/xserver.xml b/protocol/xserver.xml
new file mode 100644
index 00000000..9e25f5c0
--- /dev/null
+++ b/protocol/xserver.xml
@@ -0,0 +1,18 @@
+<protocol name="xserver">
+
+ <interface name="xserver" version="1">
+ <request name="set_window_id">
+ <arg name="surface" type="object" interface="wl_surface"/>
+ <arg name="id" type="uint"/>
+ </request>
+
+ <event name="client">
+ <arg name="fd" type="fd"/>
+ </event>
+
+ <event name="listen_socket">
+ <arg name="fd" type="fd"/>
+ </event>
+ </interface>
+
+</protocol>
diff --git a/shared/Makefile.am b/shared/Makefile.am
new file mode 100644
index 00000000..2fcff7bb
--- /dev/null
+++ b/shared/Makefile.am
@@ -0,0 +1,32 @@
+noinst_LTLIBRARIES = libshared.la libshared-cairo.la
+
+libshared_la_CFLAGS = $(GCC_CFLAGS) $(COMPOSITOR_CFLAGS)
+
+libshared_la_SOURCES = \
+ config-parser.c \
+ option-parser.c \
+ config-parser.h \
+ os-compatibility.c \
+ os-compatibility.h
+
+libshared_cairo_la_CFLAGS = \
+ $(GCC_CFLAGS) \
+ $(COMPOSITOR_CFLAGS) \
+ $(PIXMAN_CFLAGS) \
+ $(CAIRO_CFLAGS) \
+ $(PNG_CFLAGS) \
+ $(WEBP_CFLAGS)
+
+libshared_cairo_la_LIBADD = \
+ $(PIXMAN_LIBS) \
+ $(CAIRO_LIBS) \
+ $(PNG_LIBS) \
+ $(WEBP_LIBS) \
+ $(JPEG_LIBS)
+
+libshared_cairo_la_SOURCES = \
+ $(libshared_la_SOURCES) \
+ image-loader.c \
+ image-loader.h \
+ cairo-util.c \
+ cairo-util.h
diff --git a/shared/cairo-util.c b/shared/cairo-util.c
new file mode 100644
index 00000000..4305ba69
--- /dev/null
+++ b/shared/cairo-util.c
@@ -0,0 +1,511 @@
+/*
+ * Copyright © 2008 Kristian Høgsberg
+ * Copyright © 2012 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
+ * 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.
+ */
+
+#include "config.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+#include <cairo.h>
+#include "cairo-util.h"
+
+#include "image-loader.h"
+#include "config-parser.h"
+
+#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0])
+
+void
+surface_flush_device(cairo_surface_t *surface)
+{
+ cairo_device_t *device;
+
+ device = cairo_surface_get_device(surface);
+ if (device)
+ cairo_device_flush(device);
+}
+
+static int
+blur_surface(cairo_surface_t *surface, int margin)
+{
+ int32_t width, height, stride, x, y, z, w;
+ uint8_t *src, *dst;
+ uint32_t *s, *d, a, p;
+ int i, j, k, size, half;
+ uint32_t kernel[71];
+ double f;
+
+ size = ARRAY_LENGTH(kernel);
+ width = cairo_image_surface_get_width(surface);
+ height = cairo_image_surface_get_height(surface);
+ stride = cairo_image_surface_get_stride(surface);
+ src = cairo_image_surface_get_data(surface);
+
+ dst = malloc(height * stride);
+ if (dst == NULL)
+ return -1;
+
+ half = size / 2;
+ a = 0;
+ for (i = 0; i < size; i++) {
+ f = (i - half);
+ kernel[i] = exp(- f * f / ARRAY_LENGTH(kernel)) * 10000;
+ a += kernel[i];
+ }
+
+ for (i = 0; i < height; i++) {
+ s = (uint32_t *) (src + i * stride);
+ d = (uint32_t *) (dst + i * stride);
+ for (j = 0; j < width; j++) {
+ if (margin < j && j < width - margin) {
+ d[j] = s[j];
+ continue;
+ }
+
+ x = 0;
+ y = 0;
+ z = 0;
+ w = 0;
+ for (k = 0; k < size; k++) {
+ if (j - half + k < 0 || j - half + k >= width)
+ continue;
+ p = s[j - half + k];
+
+ x += (p >> 24) * kernel[k];
+ y += ((p >> 16) & 0xff) * kernel[k];
+ z += ((p >> 8) & 0xff) * kernel[k];
+ w += (p & 0xff) * kernel[k];
+ }
+ d[j] = (x / a << 24) | (y / a << 16) | (z / a << 8) | w / a;
+ }
+ }
+
+ for (i = 0; i < height; i++) {
+ s = (uint32_t *) (dst + i * stride);
+ d = (uint32_t *) (src + i * stride);
+ for (j = 0; j < width; j++) {
+ if (margin <= i && i < height - margin) {
+ d[j] = s[j];
+ continue;
+ }
+
+ x = 0;
+ y = 0;
+ z = 0;
+ w = 0;
+ for (k = 0; k < size; k++) {
+ if (i - half + k < 0 || i - half + k >= height)
+ continue;
+ s = (uint32_t *) (dst + (i - half + k) * stride);
+ p = s[j];
+
+ x += (p >> 24) * kernel[k];
+ y += ((p >> 16) & 0xff) * kernel[k];
+ z += ((p >> 8) & 0xff) * kernel[k];
+ w += (p & 0xff) * kernel[k];
+ }
+ d[j] = (x / a << 24) | (y / a << 16) | (z / a << 8) | w / a;
+ }
+ }
+
+ free(dst);
+ cairo_surface_mark_dirty(surface);
+
+ return 0;
+}
+
+void
+tile_mask(cairo_t *cr, cairo_surface_t *surface,
+ int x, int y, int width, int height, int margin, int top_margin)
+{
+ cairo_pattern_t *pattern;
+ cairo_matrix_t matrix;
+ int i, fx, fy, vmargin;
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+ pattern = cairo_pattern_create_for_surface (surface);
+ cairo_pattern_set_filter(pattern, CAIRO_FILTER_NEAREST);
+
+ for (i = 0; i < 4; i++) {
+ fx = i & 1;
+ fy = i >> 1;
+
+ cairo_matrix_init_translate(&matrix,
+ -x + fx * (128 - width),
+ -y + fy * (128 - height));
+ cairo_pattern_set_matrix(pattern, &matrix);
+
+ if (fy)
+ vmargin = margin;
+ else
+ vmargin = top_margin;
+
+ cairo_reset_clip(cr);
+ cairo_rectangle(cr,
+ x + fx * (width - margin),
+ y + fy * (height - vmargin),
+ margin, vmargin);
+ cairo_clip (cr);
+ cairo_mask(cr, pattern);
+ }
+
+ /* Top stretch */
+ cairo_matrix_init_translate(&matrix, 60, 0);
+ cairo_matrix_scale(&matrix, 8.0 / width, 1);
+ cairo_matrix_translate(&matrix, -x - width / 2, -y);
+ cairo_pattern_set_matrix(pattern, &matrix);
+ cairo_rectangle(cr, x + margin, y, width - 2 * margin, margin);
+
+ cairo_reset_clip(cr);
+ cairo_rectangle(cr,
+ x + margin,
+ y,
+ width - 2 * margin, margin);
+ cairo_clip (cr);
+ cairo_mask(cr, pattern);
+
+ /* Bottom stretch */
+ cairo_matrix_translate(&matrix, 0, -height + 128);
+ cairo_pattern_set_matrix(pattern, &matrix);
+
+ cairo_reset_clip(cr);
+ cairo_rectangle(cr, x + margin, y + height - margin,
+ width - 2 * margin, margin);
+ cairo_clip (cr);
+ cairo_mask(cr, pattern);
+
+ /* Left stretch */
+ cairo_matrix_init_translate(&matrix, 0, 60);
+ cairo_matrix_scale(&matrix, 1, 8.0 / height);
+ cairo_matrix_translate(&matrix, -x, -y - height / 2);
+ cairo_pattern_set_matrix(pattern, &matrix);
+ cairo_reset_clip(cr);
+ cairo_rectangle(cr, x, y + margin, margin, height - 2 * margin);
+ cairo_clip (cr);
+ cairo_mask(cr, pattern);
+
+ /* Right stretch */
+ cairo_matrix_translate(&matrix, -width + 128, 0);
+ cairo_pattern_set_matrix(pattern, &matrix);
+ cairo_rectangle(cr, x + width - margin, y + margin,
+ margin, height - 2 * margin);
+ cairo_reset_clip(cr);
+ cairo_clip (cr);
+ cairo_mask(cr, pattern);
+
+ cairo_pattern_destroy(pattern);
+ cairo_reset_clip(cr);
+}
+
+void
+tile_source(cairo_t *cr, cairo_surface_t *surface,
+ int x, int y, int width, int height, int margin, int top_margin)
+{
+ cairo_pattern_t *pattern;
+ cairo_matrix_t matrix;
+ int i, fx, fy, vmargin;
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+ pattern = cairo_pattern_create_for_surface (surface);
+ cairo_pattern_set_filter(pattern, CAIRO_FILTER_NEAREST);
+ cairo_set_source(cr, pattern);
+ cairo_pattern_destroy(pattern);
+
+ for (i = 0; i < 4; i++) {
+ fx = i & 1;
+ fy = i >> 1;
+
+ cairo_matrix_init_translate(&matrix,
+ -x + fx * (128 - width),
+ -y + fy * (128 - height));
+ cairo_pattern_set_matrix(pattern, &matrix);
+
+ if (fy)
+ vmargin = margin;
+ else
+ vmargin = top_margin;
+
+ cairo_rectangle(cr,
+ x + fx * (width - margin),
+ y + fy * (height - vmargin),
+ margin, vmargin);
+ cairo_fill(cr);
+ }
+
+ /* Top stretch */
+ cairo_matrix_init_translate(&matrix, 60, 0);
+ cairo_matrix_scale(&matrix, 8.0 / (width - 2 * margin), 1);
+ cairo_matrix_translate(&matrix, -x - width / 2, -y);
+ cairo_pattern_set_matrix(pattern, &matrix);
+ cairo_rectangle(cr, x + margin, y, width - 2 * margin, top_margin);
+ cairo_fill(cr);
+
+ /* Bottom stretch */
+ cairo_matrix_translate(&matrix, 0, -height + 128);
+ cairo_pattern_set_matrix(pattern, &matrix);
+ cairo_rectangle(cr, x + margin, y + height - margin,
+ width - 2 * margin, margin);
+ cairo_fill(cr);
+
+ /* Left stretch */
+ cairo_matrix_init_translate(&matrix, 0, 60);
+ cairo_matrix_scale(&matrix, 1, 8.0 / (height - margin - top_margin));
+ cairo_matrix_translate(&matrix, -x, -y - height / 2);
+ cairo_pattern_set_matrix(pattern, &matrix);
+ cairo_rectangle(cr, x, y + top_margin,
+ margin, height - margin - top_margin);
+ cairo_fill(cr);
+
+ /* Right stretch */
+ cairo_matrix_translate(&matrix, -width + 128, 0);
+ cairo_pattern_set_matrix(pattern, &matrix);
+ cairo_rectangle(cr, x + width - margin, y + top_margin,
+ margin, height - margin - top_margin);
+ cairo_fill(cr);
+}
+
+void
+rounded_rect(cairo_t *cr, int x0, int y0, int x1, int y1, int radius)
+{
+ cairo_move_to(cr, x0, y0 + radius);
+ cairo_arc(cr, x0 + radius, y0 + radius, radius, M_PI, 3 * M_PI / 2);
+ cairo_line_to(cr, x1 - radius, y0);
+ cairo_arc(cr, x1 - radius, y0 + radius, radius, 3 * M_PI / 2, 2 * M_PI);
+ cairo_line_to(cr, x1, y1 - radius);
+ cairo_arc(cr, x1 - radius, y1 - radius, radius, 0, M_PI / 2);
+ cairo_line_to(cr, x0 + radius, y1);
+ cairo_arc(cr, x0 + radius, y1 - radius, radius, M_PI / 2, M_PI);
+ cairo_close_path(cr);
+}
+
+cairo_surface_t *
+load_cairo_surface(const char *filename)
+{
+ pixman_image_t *image;
+ int width, height, stride;
+ void *data;
+
+ image = load_image(filename);
+ if (image == NULL) {
+ return NULL;
+ }
+
+ data = pixman_image_get_data(image);
+ width = pixman_image_get_width(image);
+ height = pixman_image_get_height(image);
+ stride = pixman_image_get_stride(image);
+
+ return cairo_image_surface_create_for_data(data, CAIRO_FORMAT_ARGB32,
+ width, height, stride);
+}
+
+struct theme *
+theme_create(void)
+{
+ struct theme *t;
+ cairo_t *cr;
+ cairo_pattern_t *pattern;
+
+ t = malloc(sizeof *t);
+ if (t == NULL)
+ return NULL;
+
+ t->margin = 32;
+ t->width = 6;
+ t->titlebar_height = 27;
+ t->frame_radius = 3;
+ t->shadow = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 128, 128);
+ cr = cairo_create(t->shadow);
+ cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+ cairo_set_source_rgba(cr, 0, 0, 0, 1);
+ rounded_rect(cr, 32, 32, 96, 96, t->frame_radius);
+ cairo_fill(cr);
+ if (cairo_status (cr) != CAIRO_STATUS_SUCCESS)
+ goto err_shadow;
+ cairo_destroy(cr);
+ if (blur_surface(t->shadow, 64) == -1)
+ goto err_shadow;
+
+ t->active_frame =
+ cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 128, 128);
+ cr = cairo_create(t->active_frame);
+ cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+
+ pattern = cairo_pattern_create_linear(16, 16, 16, 112);
+ cairo_pattern_add_color_stop_rgb(pattern, 0.0, 1.0, 1.0, 1.0);
+ cairo_pattern_add_color_stop_rgb(pattern, 0.2, 0.8, 0.8, 0.8);
+ cairo_set_source(cr, pattern);
+ cairo_pattern_destroy(pattern);
+
+ rounded_rect(cr, 0, 0, 128, 128, t->frame_radius);
+ cairo_fill(cr);
+
+ if (cairo_status(cr) != CAIRO_STATUS_SUCCESS)
+ goto err_active_frame;
+
+ cairo_destroy(cr);
+
+ t->inactive_frame =
+ cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 128, 128);
+ cr = cairo_create(t->inactive_frame);
+ cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+ cairo_set_source_rgba(cr, 0.75, 0.75, 0.75, 1);
+ rounded_rect(cr, 0, 0, 128, 128, t->frame_radius);
+ cairo_fill(cr);
+
+ if (cairo_status (cr) != CAIRO_STATUS_SUCCESS)
+ goto err_inactive_frame;
+
+ cairo_destroy(cr);
+
+ return t;
+
+ err_inactive_frame:
+ cairo_surface_destroy(t->inactive_frame);
+ err_active_frame:
+ cairo_surface_destroy(t->active_frame);
+ err_shadow:
+ cairo_surface_destroy(t->shadow);
+ free(t);
+ return NULL;
+}
+
+void
+theme_destroy(struct theme *t)
+{
+ cairo_surface_destroy(t->active_frame);
+ cairo_surface_destroy(t->inactive_frame);
+ cairo_surface_destroy(t->shadow);
+ free(t);
+}
+
+void
+theme_render_frame(struct theme *t,
+ cairo_t *cr, int width, int height,
+ const char *title, uint32_t flags)
+{
+ cairo_text_extents_t extents;
+ cairo_font_extents_t font_extents;
+ cairo_surface_t *source;
+ int x, y, margin;
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ cairo_set_source_rgba(cr, 0, 0, 0, 0);
+ cairo_paint(cr);
+
+ if (flags & THEME_FRAME_MAXIMIZED)
+ margin = 0;
+ else {
+ cairo_set_source_rgba(cr, 0, 0, 0, 0.45);
+ tile_mask(cr, t->shadow,
+ 2, 2, width + 8, height + 8,
+ 64, 64);
+ margin = t->margin;
+ }
+
+ if (flags & THEME_FRAME_ACTIVE)
+ source = t->active_frame;
+ else
+ source = t->inactive_frame;
+
+ tile_source(cr, source,
+ margin, margin,
+ width - margin * 2, height - margin * 2,
+ t->width, t->titlebar_height);
+
+ cairo_rectangle (cr, margin + t->width, margin,
+ width - (margin + t->width) * 2,
+ t->titlebar_height - t->width);
+ cairo_clip(cr);
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+ cairo_select_font_face(cr, "sans",
+ CAIRO_FONT_SLANT_NORMAL,
+ CAIRO_FONT_WEIGHT_BOLD);
+ cairo_set_font_size(cr, 14);
+ cairo_text_extents(cr, title, &extents);
+ cairo_font_extents (cr, &font_extents);
+ x = (width - extents.width) / 2;
+ y = margin +
+ (t->titlebar_height -
+ font_extents.ascent - font_extents.descent) / 2 +
+ font_extents.ascent;
+
+ if (flags & THEME_FRAME_ACTIVE) {
+ cairo_move_to(cr, x + 1, y + 1);
+ cairo_set_source_rgb(cr, 1, 1, 1);
+ cairo_show_text(cr, title);
+ cairo_move_to(cr, x, y);
+ cairo_set_source_rgb(cr, 0, 0, 0);
+ cairo_show_text(cr, title);
+ } else {
+ cairo_move_to(cr, x, y);
+ cairo_set_source_rgb(cr, 0.4, 0.4, 0.4);
+ cairo_show_text(cr, title);
+ }
+}
+
+enum theme_location
+theme_get_location(struct theme *t, int x, int y,
+ int width, int height, int flags)
+{
+ int vlocation, hlocation, location;
+ const int grip_size = 8;
+ int margin;
+
+ margin = (flags & THEME_FRAME_MAXIMIZED) ? 0 : t->margin;
+
+ if (x < margin)
+ hlocation = THEME_LOCATION_EXTERIOR;
+ else if (margin <= x && x < margin + grip_size)
+ hlocation = THEME_LOCATION_RESIZING_LEFT;
+ else if (x < width - margin - grip_size)
+ hlocation = THEME_LOCATION_INTERIOR;
+ else if (x < width - margin)
+ hlocation = THEME_LOCATION_RESIZING_RIGHT;
+ else
+ hlocation = THEME_LOCATION_EXTERIOR;
+
+ if (y < margin)
+ vlocation = THEME_LOCATION_EXTERIOR;
+ else if (margin <= y && y < margin + grip_size)
+ vlocation = THEME_LOCATION_RESIZING_TOP;
+ else if (y < height - margin - grip_size)
+ vlocation = THEME_LOCATION_INTERIOR;
+ else if (y < height - margin)
+ vlocation = THEME_LOCATION_RESIZING_BOTTOM;
+ else
+ vlocation = THEME_LOCATION_EXTERIOR;
+
+ location = vlocation | hlocation;
+ if (location & THEME_LOCATION_EXTERIOR)
+ location = THEME_LOCATION_EXTERIOR;
+ if (location == THEME_LOCATION_INTERIOR &&
+ y < margin + t->titlebar_height)
+ location = THEME_LOCATION_TITLEBAR;
+ else if (location == THEME_LOCATION_INTERIOR)
+ location = THEME_LOCATION_CLIENT_AREA;
+
+ return location;
+}
diff --git a/shared/cairo-util.h b/shared/cairo-util.h
new file mode 100644
index 00000000..7b403944
--- /dev/null
+++ b/shared/cairo-util.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright © 2008 Kristian Høgsberg
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * 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.
+ */
+
+#ifndef _CAIRO_UTIL_H
+#define _CAIRO_UTIL_H
+
+#include <cairo.h>
+
+void
+surface_flush_device(cairo_surface_t *surface);
+
+void
+tile_mask(cairo_t *cr, cairo_surface_t *surface,
+ int x, int y, int width, int height, int margin, int top_margin);
+
+void
+tile_source(cairo_t *cr, cairo_surface_t *surface,
+ int x, int y, int width, int height, int margin, int top_margin);
+
+void
+rounded_rect(cairo_t *cr, int x0, int y0, int x1, int y1, int radius);
+
+cairo_surface_t *
+load_cairo_surface(const char *filename);
+
+struct theme {
+ cairo_surface_t *active_frame;
+ cairo_surface_t *inactive_frame;
+ cairo_surface_t *shadow;
+ int frame_radius;
+ int margin;
+ int width;
+ int titlebar_height;
+};
+
+struct theme *
+theme_create(void);
+void
+theme_destroy(struct theme *t);
+
+enum {
+ THEME_FRAME_ACTIVE = 1,
+ THEME_FRAME_MAXIMIZED,
+};
+
+void
+theme_render_frame(struct theme *t,
+ cairo_t *cr, int width, int height,
+ const char *title, uint32_t flags);
+
+enum theme_location {
+ THEME_LOCATION_INTERIOR = 0,
+ THEME_LOCATION_RESIZING_TOP = 1,
+ THEME_LOCATION_RESIZING_BOTTOM = 2,
+ THEME_LOCATION_RESIZING_LEFT = 4,
+ THEME_LOCATION_RESIZING_TOP_LEFT = 5,
+ THEME_LOCATION_RESIZING_BOTTOM_LEFT = 6,
+ THEME_LOCATION_RESIZING_RIGHT = 8,
+ THEME_LOCATION_RESIZING_TOP_RIGHT = 9,
+ THEME_LOCATION_RESIZING_BOTTOM_RIGHT = 10,
+ THEME_LOCATION_RESIZING_MASK = 15,
+ THEME_LOCATION_EXTERIOR = 16,
+ THEME_LOCATION_TITLEBAR = 17,
+ THEME_LOCATION_CLIENT_AREA = 18,
+};
+
+enum theme_location
+theme_get_location(struct theme *t, int x, int y, int width, int height, int flags);
+
+#endif
diff --git a/shared/config-parser.c b/shared/config-parser.c
new file mode 100644
index 00000000..8defbbb4
--- /dev/null
+++ b/shared/config-parser.c
@@ -0,0 +1,434 @@
+/*
+ * Copyright © 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
+ * 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.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <ctype.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <wayland-util.h>
+#include "config-parser.h"
+
+#define container_of(ptr, type, member) ({ \
+ const __typeof__( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - offsetof(type,member) );})
+
+struct weston_config_entry {
+ char *key;
+ char *value;
+ struct wl_list link;
+};
+
+struct weston_config_section {
+ char *name;
+ struct wl_list entry_list;
+ struct wl_list link;
+};
+
+struct weston_config {
+ struct wl_list section_list;
+ char path[PATH_MAX];
+};
+
+static int
+open_config_file(struct weston_config *c, const char *name)
+{
+ const char *config_dir = getenv("XDG_CONFIG_HOME");
+ const char *home_dir = getenv("HOME");
+ const char *config_dirs = getenv("XDG_CONFIG_DIRS");
+ const char *p, *next;
+ int fd;
+
+ if (name[0] == '/') {
+ snprintf(c->path, sizeof c->path, "%s", name);
+ return open(name, O_RDONLY | O_CLOEXEC);
+ }
+
+ /* Precedence is given to config files in the home directory,
+ * and then to directories listed in XDG_CONFIG_DIRS and
+ * finally to the current working directory. */
+
+ /* $XDG_CONFIG_HOME */
+ if (config_dir) {
+ snprintf(c->path, sizeof c->path, "%s/%s", config_dir, name);
+ fd = open(c->path, O_RDONLY | O_CLOEXEC);
+ if (fd >= 0)
+ return fd;
+ }
+
+ /* $HOME/.config */
+ if (home_dir) {
+ snprintf(c->path, sizeof c->path,
+ "%s/.config/%s", home_dir, name);
+ fd = open(c->path, O_RDONLY | O_CLOEXEC);
+ if (fd >= 0)
+ return fd;
+ }
+
+ /* For each $XDG_CONFIG_DIRS: weston/<config_file> */
+ if (!config_dirs)
+ config_dirs = "/etc/xdg"; /* See XDG base dir spec. */
+
+ for (p = config_dirs; *p != '\0'; p = next) {
+ next = strchrnul(p, ':');
+ snprintf(c->path, sizeof c->path,
+ "%.*s/weston/%s", (int)(next - p), p, name);
+ fd = open(c->path, O_RDONLY | O_CLOEXEC);
+ if (fd >= 0)
+ return fd;
+
+ if (*next == ':')
+ next++;
+ }
+
+ /* Current working directory. */
+ snprintf(c->path, sizeof c->path, "./%s", name);
+
+ return open(c->path, O_RDONLY | O_CLOEXEC);
+}
+
+static struct weston_config_entry *
+config_section_get_entry(struct weston_config_section *section,
+ const char *key)
+{
+ struct weston_config_entry *e;
+
+ if (section == NULL)
+ return NULL;
+ wl_list_for_each(e, &section->entry_list, link)
+ if (strcmp(e->key, key) == 0)
+ return e;
+
+ return NULL;
+}
+
+WL_EXPORT
+struct weston_config_section *
+weston_config_get_section(struct weston_config *config, const char *section,
+ const char *key, const char *value)
+{
+ struct weston_config_section *s;
+ struct weston_config_entry *e;
+
+ if (config == NULL)
+ return NULL;
+ wl_list_for_each(s, &config->section_list, link) {
+ if (strcmp(s->name, section) != 0)
+ continue;
+ if (key == NULL)
+ return s;
+ e = config_section_get_entry(s, key);
+ if (e && strcmp(e->value, value) == 0)
+ return s;
+ }
+
+ return NULL;
+}
+
+WL_EXPORT
+int
+weston_config_section_get_int(struct weston_config_section *section,
+ const char *key,
+ int32_t *value, int32_t default_value)
+{
+ struct weston_config_entry *entry;
+ char *end;
+
+ entry = config_section_get_entry(section, key);
+ if (entry == NULL) {
+ *value = default_value;
+ errno = ENOENT;
+ return -1;
+ }
+
+ *value = strtol(entry->value, &end, 0);
+ if (*end != '\0') {
+ *value = default_value;
+ errno = EINVAL;
+ return -1;
+ }
+
+ return 0;
+}
+
+WL_EXPORT
+int
+weston_config_section_get_uint(struct weston_config_section *section,
+ const char *key,
+ uint32_t *value, uint32_t default_value)
+{
+ struct weston_config_entry *entry;
+ char *end;
+
+ entry = config_section_get_entry(section, key);
+ if (entry == NULL) {
+ *value = default_value;
+ errno = ENOENT;
+ return -1;
+ }
+
+ *value = strtoul(entry->value, &end, 0);
+ if (*end != '\0') {
+ *value = default_value;
+ errno = EINVAL;
+ return -1;
+ }
+
+ return 0;
+}
+
+WL_EXPORT
+int
+weston_config_section_get_double(struct weston_config_section *section,
+ const char *key,
+ double *value, double default_value)
+{
+ struct weston_config_entry *entry;
+ char *end;
+
+ entry = config_section_get_entry(section, key);
+ if (entry == NULL) {
+ *value = default_value;
+ errno = ENOENT;
+ return -1;
+ }
+
+ *value = strtod(entry->value, &end);
+ if (*end != '\0') {
+ *value = default_value;
+ errno = EINVAL;
+ return -1;
+ }
+
+ return 0;
+}
+
+WL_EXPORT
+int
+weston_config_section_get_string(struct weston_config_section *section,
+ const char *key,
+ char **value, const char *default_value)
+{
+ struct weston_config_entry *entry;
+
+ entry = config_section_get_entry(section, key);
+ if (entry == NULL) {
+ if (default_value)
+ *value = strdup(default_value);
+ else
+ *value = NULL;
+ errno = ENOENT;
+ return -1;
+ }
+
+ *value = strdup(entry->value);
+
+ return 0;
+}
+
+WL_EXPORT
+int
+weston_config_section_get_bool(struct weston_config_section *section,
+ const char *key,
+ int *value, int default_value)
+{
+ struct weston_config_entry *entry;
+
+ entry = config_section_get_entry(section, key);
+ if (entry == NULL) {
+ *value = default_value;
+ errno = ENOENT;
+ return -1;
+ }
+
+ if (strcmp(entry->value, "false") == 0)
+ *value = 0;
+ else if (strcmp(entry->value, "true") == 0)
+ *value = 1;
+ else {
+ *value = default_value;
+ errno = EINVAL;
+ return -1;
+ }
+
+ return 0;
+}
+
+static struct weston_config_section *
+config_add_section(struct weston_config *config, const char *name)
+{
+ struct weston_config_section *section;
+
+ section = malloc(sizeof *section);
+ section->name = strdup(name);
+ wl_list_init(&section->entry_list);
+ wl_list_insert(config->section_list.prev, &section->link);
+
+ return section;
+}
+
+static struct weston_config_entry *
+section_add_entry(struct weston_config_section *section,
+ const char *key, const char *value)
+{
+ struct weston_config_entry *entry;
+
+ entry = malloc(sizeof *entry);
+ entry->key = strdup(key);
+ entry->value = strdup(value);
+ wl_list_insert(section->entry_list.prev, &entry->link);
+
+ return entry;
+}
+
+struct weston_config *
+weston_config_parse(const char *name)
+{
+ FILE *fp;
+ char line[512], *p;
+ struct weston_config *config;
+ struct weston_config_section *section = NULL;
+ int i, fd;
+
+ config = malloc(sizeof *config);
+ if (config == NULL)
+ return NULL;
+
+ wl_list_init(&config->section_list);
+
+ fd = open_config_file(config, name);
+ if (fd == -1) {
+ free(config);
+ return NULL;
+ }
+
+ fp = fdopen(fd, "r");
+ if (fp == NULL) {
+ free(config);
+ return NULL;
+ }
+
+ while (fgets(line, sizeof line, fp)) {
+ switch (line[0]) {
+ case '#':
+ case '\n':
+ continue;
+ case '[':
+ p = strchr(&line[1], ']');
+ if (!p || p[1] != '\n') {
+ fprintf(stderr, "malformed "
+ "section header: %s\n", line);
+ fclose(fp);
+ weston_config_destroy(config);
+ return NULL;
+ }
+ p[0] = '\0';
+ section = config_add_section(config, &line[1]);
+ continue;
+ default:
+ p = strchr(line, '=');
+ if (!p || p == line || !section) {
+ fprintf(stderr, "malformed "
+ "config line: %s\n", line);
+ fclose(fp);
+ weston_config_destroy(config);
+ return NULL;
+ }
+
+ p[0] = '\0';
+ p++;
+ while (isspace(*p))
+ p++;
+ i = strlen(p);
+ while (i > 0 && isspace(p[i - 1])) {
+ p[i - 1] = '\0';
+ i--;
+ }
+ section_add_entry(section, line, p);
+ continue;
+ }
+ }
+
+ fclose(fp);
+
+ return config;
+}
+
+const char *
+weston_config_get_full_path(struct weston_config *config)
+{
+ return config == NULL ? NULL : config->path;
+}
+
+int
+weston_config_next_section(struct weston_config *config,
+ struct weston_config_section **section,
+ const char **name)
+{
+ if (config == NULL)
+ return 0;
+
+ if (*section == NULL)
+ *section = container_of(config->section_list.next,
+ struct weston_config_section, link);
+ else
+ *section = container_of((*section)->link.next,
+ struct weston_config_section, link);
+
+ if (&(*section)->link == &config->section_list)
+ return 0;
+
+ *name = (*section)->name;
+
+ return 1;
+}
+
+void
+weston_config_destroy(struct weston_config *config)
+{
+ struct weston_config_section *s, *next_s;
+ struct weston_config_entry *e, *next_e;
+
+ if (config == NULL)
+ return;
+
+ wl_list_for_each_safe(s, next_s, &config->section_list, link) {
+ wl_list_for_each_safe(e, next_e, &s->entry_list, link) {
+ free(e->key);
+ free(e->value);
+ free(e);
+ }
+ free(s->name);
+ free(s);
+ }
+
+ free(config);
+}
diff --git a/shared/config-parser.h b/shared/config-parser.h
new file mode 100644
index 00000000..745562bc
--- /dev/null
+++ b/shared/config-parser.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright © 2008 Kristian Høgsberg
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * 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.
+ */
+
+#ifndef CONFIGPARSER_H
+#define CONFIGPARSER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum config_key_type {
+ CONFIG_KEY_INTEGER, /* typeof data = int */
+ CONFIG_KEY_UNSIGNED_INTEGER, /* typeof data = unsigned int */
+ CONFIG_KEY_STRING, /* typeof data = char* */
+ CONFIG_KEY_BOOLEAN /* typeof data = int */
+};
+
+struct config_key {
+ const char *name;
+ enum config_key_type type;
+ void *data;
+};
+
+struct config_section {
+ const char *name;
+ const struct config_key *keys;
+ int num_keys;
+ void (*done)(void *data);
+};
+
+enum weston_option_type {
+ WESTON_OPTION_INTEGER,
+ WESTON_OPTION_UNSIGNED_INTEGER,
+ WESTON_OPTION_STRING,
+ WESTON_OPTION_BOOLEAN
+};
+
+struct weston_option {
+ enum weston_option_type type;
+ const char *name;
+ int short_name;
+ void *data;
+};
+
+int
+parse_options(const struct weston_option *options,
+ int count, int *argc, char *argv[]);
+
+struct weston_config_section;
+struct weston_config;
+
+struct weston_config_section *
+weston_config_get_section(struct weston_config *config, const char *section,
+ const char *key, const char *value);
+int
+weston_config_section_get_int(struct weston_config_section *section,
+ const char *key,
+ int32_t *value, int32_t default_value);
+int
+weston_config_section_get_uint(struct weston_config_section *section,
+ const char *key,
+ uint32_t *value, uint32_t default_value);
+int
+weston_config_section_get_double(struct weston_config_section *section,
+ const char *key,
+ double *value, double default_value);
+int
+weston_config_section_get_string(struct weston_config_section *section,
+ const char *key,
+ char **value,
+ const char *default_value);
+int
+weston_config_section_get_bool(struct weston_config_section *section,
+ const char *key,
+ int *value, int default_value);
+struct weston_config *
+weston_config_parse(const char *name);
+
+const char *
+weston_config_get_full_path(struct weston_config *config);
+
+void
+weston_config_destroy(struct weston_config *config);
+
+int weston_config_next_section(struct weston_config *config,
+ struct weston_config_section **section,
+ const char **name);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CONFIGPARSER_H */
+
diff --git a/shared/image-loader.c b/shared/image-loader.c
new file mode 100644
index 00000000..35dadd3d
--- /dev/null
+++ b/shared/image-loader.c
@@ -0,0 +1,405 @@
+/*
+ * Copyright © 2008-2012 Kristian Høgsberg
+ * Copyright © 2012 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
+ * 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.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <jpeglib.h>
+#include <png.h>
+#include <pixman.h>
+
+#include "image-loader.h"
+
+#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0])
+
+#ifdef HAVE_WEBP
+#include <webp/decode.h>
+#endif
+
+static int
+stride_for_width(int width)
+{
+ return width * 4;
+}
+
+static void
+swizzle_row(JSAMPLE *row, JDIMENSION width)
+{
+ JSAMPLE *s;
+ uint32_t *d;
+
+ s = row + (width - 1) * 3;
+ d = (uint32_t *) (row + (width - 1) * 4);
+ while (s >= row) {
+ *d = 0xff000000 | (s[0] << 16) | (s[1] << 8) | (s[2] << 0);
+ s -= 3;
+ d--;
+ }
+}
+
+static void
+error_exit(j_common_ptr cinfo)
+{
+ longjmp(cinfo->client_data, 1);
+}
+
+static void
+pixman_image_destroy_func(pixman_image_t *image, void *data)
+{
+ free(data);
+}
+
+static pixman_image_t *
+load_jpeg(FILE *fp)
+{
+ struct jpeg_decompress_struct cinfo;
+ struct jpeg_error_mgr jerr;
+ pixman_image_t *pixman_image = NULL;
+ unsigned int i;
+ int stride, first;
+ JSAMPLE *data, *rows[4];
+ jmp_buf env;
+
+ cinfo.err = jpeg_std_error(&jerr);
+ jerr.error_exit = error_exit;
+ cinfo.client_data = env;
+ if (setjmp(env))
+ return NULL;
+
+ jpeg_create_decompress(&cinfo);
+
+ jpeg_stdio_src(&cinfo, fp);
+
+ jpeg_read_header(&cinfo, TRUE);
+
+ cinfo.out_color_space = JCS_RGB;
+ jpeg_start_decompress(&cinfo);
+
+ stride = cinfo.output_width * 4;
+ data = malloc(stride * cinfo.output_height);
+ if (data == NULL) {
+ fprintf(stderr, "couldn't allocate image data\n");
+ return NULL;
+ }
+
+ while (cinfo.output_scanline < cinfo.output_height) {
+ first = cinfo.output_scanline;
+ for (i = 0; i < ARRAY_LENGTH(rows); i++)
+ rows[i] = data + (first + i) * stride;
+
+ jpeg_read_scanlines(&cinfo, rows, ARRAY_LENGTH(rows));
+ for (i = 0; first + i < cinfo.output_scanline; i++)
+ swizzle_row(rows[i], cinfo.output_width);
+ }
+
+ jpeg_finish_decompress(&cinfo);
+
+ jpeg_destroy_decompress(&cinfo);
+
+ pixman_image = pixman_image_create_bits(PIXMAN_a8r8g8b8,
+ cinfo.output_width,
+ cinfo.output_height,
+ (uint32_t *) data, stride);
+
+ pixman_image_set_destroy_function(pixman_image,
+ pixman_image_destroy_func, data);
+
+ return pixman_image;
+}
+
+static inline int
+multiply_alpha(int alpha, int color)
+{
+ int temp = (alpha * color) + 0x80;
+
+ return ((temp + (temp >> 8)) >> 8);
+}
+
+static void
+premultiply_data(png_structp png,
+ png_row_infop row_info,
+ png_bytep data)
+{
+ unsigned int i;
+ png_bytep p;
+
+ for (i = 0, p = data; i < row_info->rowbytes; i += 4, p += 4) {
+ png_byte alpha = p[3];
+ uint32_t w;
+
+ if (alpha == 0) {
+ w = 0;
+ } else {
+ png_byte red = p[0];
+ png_byte green = p[1];
+ png_byte blue = p[2];
+
+ if (alpha != 0xff) {
+ red = multiply_alpha(alpha, red);
+ green = multiply_alpha(alpha, green);
+ blue = multiply_alpha(alpha, blue);
+ }
+ w = (alpha << 24) | (red << 16) | (green << 8) | (blue << 0);
+ }
+
+ * (uint32_t *) p = w;
+ }
+}
+
+static void
+read_func(png_structp png, png_bytep data, png_size_t size)
+{
+ FILE *fp = png_get_io_ptr(png);
+
+ if (fread(data, 1, size, fp) != size)
+ png_error(png, NULL);
+}
+
+static void
+png_error_callback(png_structp png, png_const_charp error_msg)
+{
+ longjmp (png_jmpbuf (png), 1);
+}
+
+static pixman_image_t *
+load_png(FILE *fp)
+{
+ png_struct *png;
+ png_info *info;
+ png_byte *data = NULL;
+ png_byte **row_pointers = NULL;
+ png_uint_32 width, height;
+ int depth, color_type, interlace, stride;
+ unsigned int i;
+ pixman_image_t *pixman_image = NULL;
+
+ png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,
+ png_error_callback, NULL);
+ if (!png)
+ return NULL;
+
+ info = png_create_info_struct(png);
+ if (!info) {
+ png_destroy_read_struct(&png, &info, NULL);
+ return NULL;
+ }
+
+ if (setjmp(png_jmpbuf(png))) {
+ if (data)
+ free(data);
+ if (row_pointers)
+ free(row_pointers);
+ png_destroy_read_struct(&png, &info, NULL);
+ return NULL;
+ }
+
+ png_set_read_fn(png, fp, read_func);
+ png_read_info(png, info);
+ png_get_IHDR(png, info,
+ &width, &height, &depth,
+ &color_type, &interlace, NULL, NULL);
+
+ if (color_type == PNG_COLOR_TYPE_PALETTE)
+ png_set_palette_to_rgb(png);
+
+ if (color_type == PNG_COLOR_TYPE_GRAY)
+ png_set_expand_gray_1_2_4_to_8(png);
+
+ if (png_get_valid(png, info, PNG_INFO_tRNS))
+ png_set_tRNS_to_alpha(png);
+
+ if (depth == 16)
+ png_set_strip_16(png);
+
+ if (depth < 8)
+ png_set_packing(png);
+
+ if (color_type == PNG_COLOR_TYPE_GRAY ||
+ color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+ png_set_gray_to_rgb(png);
+
+ if (interlace != PNG_INTERLACE_NONE)
+ png_set_interlace_handling(png);
+
+ png_set_filler(png, 0xff, PNG_FILLER_AFTER);
+ png_set_read_user_transform_fn(png, premultiply_data);
+ png_read_update_info(png, info);
+ png_get_IHDR(png, info,
+ &width, &height, &depth,
+ &color_type, &interlace, NULL, NULL);
+
+
+ stride = stride_for_width(width);
+ data = malloc(stride * height);
+ if (!data) {
+ png_destroy_read_struct(&png, &info, NULL);
+ return NULL;
+ }
+
+ row_pointers = malloc(height * sizeof row_pointers[0]);
+ if (row_pointers == NULL) {
+ free(data);
+ png_destroy_read_struct(&png, &info, NULL);
+ return NULL;
+ }
+
+ for (i = 0; i < height; i++)
+ row_pointers[i] = &data[i * stride];
+
+ png_read_image(png, row_pointers);
+ png_read_end(png, info);
+
+ free(row_pointers);
+ png_destroy_read_struct(&png, &info, NULL);
+
+ pixman_image = pixman_image_create_bits(PIXMAN_a8r8g8b8,
+ width, height, (uint32_t *) data, stride);
+
+ pixman_image_set_destroy_function(pixman_image,
+ pixman_image_destroy_func, data);
+
+ return pixman_image;
+}
+
+#ifdef HAVE_WEBP
+
+static pixman_image_t *
+load_webp(FILE *fp)
+{
+ WebPDecoderConfig config;
+ uint8_t buffer[16 * 1024];
+ int len;
+ VP8StatusCode status;
+ WebPIDecoder *idec;
+
+ if (!WebPInitDecoderConfig(&config)) {
+ fprintf(stderr, "Library version mismatch!\n");
+ return NULL;
+ }
+
+ /* webp decoding api doesn't seem to specify a min size that's
+ usable for GetFeatures, but 256 works... */
+ len = fread(buffer, 1, 256, fp);
+ status = WebPGetFeatures(buffer, len, &config.input);
+ if (status != VP8_STATUS_OK) {
+ fprintf(stderr, "failed to parse webp header\n");
+ WebPFreeDecBuffer(&config.output);
+ return NULL;
+ }
+
+ config.output.colorspace = MODE_BGRA;
+ config.output.u.RGBA.stride = stride_for_width(config.input.width);
+ config.output.u.RGBA.size =
+ config.output.u.RGBA.stride * config.input.height;
+ config.output.u.RGBA.rgba =
+ malloc(config.output.u.RGBA.stride * config.input.height);
+ config.output.is_external_memory = 1;
+ if (!config.output.u.RGBA.rgba) {
+ WebPFreeDecBuffer(&config.output);
+ return NULL;
+ }
+
+ rewind(fp);
+ idec = WebPINewDecoder(&config.output);
+ if (!idec) {
+ WebPFreeDecBuffer(&config.output);
+ return NULL;
+ }
+
+ while (!feof(fp)) {
+ len = fread(buffer, 1, sizeof buffer, fp);
+ status = WebPIAppend(idec, buffer, len);
+ if (status != VP8_STATUS_OK) {
+ fprintf(stderr, "webp decode status %d\n", status);
+ WebPIDelete(idec);
+ WebPFreeDecBuffer(&config.output);
+ return NULL;
+ }
+ }
+
+ WebPIDelete(idec);
+ WebPFreeDecBuffer(&config.output);
+
+ return pixman_image_create_bits(PIXMAN_a8r8g8b8,
+ config.input.width,
+ config.input.height,
+ (uint32_t *) config.output.u.RGBA.rgba,
+ config.output.u.RGBA.stride);
+}
+
+#endif
+
+
+struct image_loader {
+ unsigned char header[4];
+ int header_size;
+ pixman_image_t *(*load)(FILE *fp);
+};
+
+static const struct image_loader loaders[] = {
+ { { 0x89, 'P', 'N', 'G' }, 4, load_png },
+ { { 0xff, 0xd8 }, 2, load_jpeg },
+#ifdef HAVE_WEBP
+ { { 'R', 'I', 'F', 'F' }, 4, load_webp }
+#endif
+};
+
+pixman_image_t *
+load_image(const char *filename)
+{
+ pixman_image_t *image;
+ unsigned char header[4];
+ FILE *fp;
+ unsigned int i;
+
+ fp = fopen(filename, "rb");
+ if (fp == NULL)
+ return NULL;
+
+ if (fread(header, sizeof header, 1, fp) != 1) {
+ fclose(fp);
+ return NULL;
+ }
+
+ rewind(fp);
+ for (i = 0; i < ARRAY_LENGTH(loaders); i++) {
+ if (memcmp(header, loaders[i].header,
+ loaders[i].header_size) == 0) {
+ image = loaders[i].load(fp);
+ break;
+ }
+ }
+
+ fclose(fp);
+
+ if (i == ARRAY_LENGTH(loaders)) {
+ fprintf(stderr, "unrecognized file header for %s: "
+ "0x%02x 0x%02x 0x%02x 0x%02x\n",
+ filename, header[0], header[1], header[2], header[3]);
+ image = NULL;
+ }
+
+ return image;
+}
diff --git a/shared/image-loader.h b/shared/image-loader.h
new file mode 100644
index 00000000..445e651e
--- /dev/null
+++ b/shared/image-loader.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright © 2013 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
+ * 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.
+ */
+
+#ifndef _IMAGE_LOADER_H
+#define _IMAGE_LOADER_H
+
+#include <pixman.h>
+
+pixman_image_t *
+load_image(const char *filename);
+
+#endif
diff --git a/shared/matrix.c b/shared/matrix.c
new file mode 100644
index 00000000..4f0b6b79
--- /dev/null
+++ b/shared/matrix.c
@@ -0,0 +1,273 @@
+/*
+ * Copyright © 2011 Intel Corporation
+ * Copyright © 2012 Collabora, Ltd.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that 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.
+ */
+
+#include "config.h"
+
+#include <float.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+
+#ifdef IN_WESTON
+#include <wayland-server.h>
+#else
+#define WL_EXPORT
+#endif
+
+#include "matrix.h"
+
+
+/*
+ * Matrices are stored in column-major order, that is the array indices are:
+ * 0 4 8 12
+ * 1 5 9 13
+ * 2 6 10 14
+ * 3 7 11 15
+ */
+
+WL_EXPORT void
+weston_matrix_init(struct weston_matrix *matrix)
+{
+ static const struct weston_matrix identity = {
+ .d = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 },
+ .type = 0,
+ };
+
+ memcpy(matrix, &identity, sizeof identity);
+}
+
+/* m <- n * m, that is, m is multiplied on the LEFT. */
+WL_EXPORT void
+weston_matrix_multiply(struct weston_matrix *m, const struct weston_matrix *n)
+{
+ struct weston_matrix tmp;
+ const float *row, *column;
+ div_t d;
+ int i, j;
+
+ for (i = 0; i < 16; i++) {
+ tmp.d[i] = 0;
+ d = div(i, 4);
+ row = m->d + d.quot * 4;
+ column = n->d + d.rem;
+ for (j = 0; j < 4; j++)
+ tmp.d[i] += row[j] * column[j * 4];
+ }
+ tmp.type = m->type | n->type;
+ memcpy(m, &tmp, sizeof tmp);
+}
+
+WL_EXPORT void
+weston_matrix_translate(struct weston_matrix *matrix, float x, float y, float z)
+{
+ struct weston_matrix translate = {
+ .d = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x, y, z, 1 },
+ .type = WESTON_MATRIX_TRANSFORM_TRANSLATE,
+ };
+
+ weston_matrix_multiply(matrix, &translate);
+}
+
+WL_EXPORT void
+weston_matrix_scale(struct weston_matrix *matrix, float x, float y,float z)
+{
+ struct weston_matrix scale = {
+ .d = { x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1 },
+ .type = WESTON_MATRIX_TRANSFORM_SCALE,
+ };
+
+ weston_matrix_multiply(matrix, &scale);
+}
+
+WL_EXPORT void
+weston_matrix_rotate_xy(struct weston_matrix *matrix, float cos, float sin)
+{
+ struct weston_matrix translate = {
+ .d = { cos, sin, 0, 0, -sin, cos, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 },
+ .type = WESTON_MATRIX_TRANSFORM_ROTATE,
+ };
+
+ weston_matrix_multiply(matrix, &translate);
+}
+
+/* v <- m * v */
+WL_EXPORT void
+weston_matrix_transform(struct weston_matrix *matrix, struct weston_vector *v)
+{
+ int i, j;
+ struct weston_vector t;
+
+ for (i = 0; i < 4; i++) {
+ t.f[i] = 0;
+ for (j = 0; j < 4; j++)
+ t.f[i] += v->f[j] * matrix->d[i + j * 4];
+ }
+
+ *v = t;
+}
+
+static inline void
+swap_rows(double *a, double *b)
+{
+ unsigned k;
+ double tmp;
+
+ for (k = 0; k < 13; k += 4) {
+ tmp = a[k];
+ a[k] = b[k];
+ b[k] = tmp;
+ }
+}
+
+static inline void
+swap_unsigned(unsigned *a, unsigned *b)
+{
+ unsigned tmp;
+
+ tmp = *a;
+ *a = *b;
+ *b = tmp;
+}
+
+static inline unsigned
+find_pivot(double *column, unsigned k)
+{
+ unsigned p = k;
+ for (++k; k < 4; ++k)
+ if (fabs(column[p]) < fabs(column[k]))
+ p = k;
+
+ return p;
+}
+
+/*
+ * reference: Gene H. Golub and Charles F. van Loan. Matrix computations.
+ * 3rd ed. The Johns Hopkins University Press. 1996.
+ * LU decomposition, forward and back substitution: Chapter 3.
+ */
+
+MATRIX_TEST_EXPORT inline int
+matrix_invert(double *A, unsigned *p, const struct weston_matrix *matrix)
+{
+ unsigned i, j, k;
+ unsigned pivot;
+ double pv;
+
+ for (i = 0; i < 4; ++i)
+ p[i] = i;
+ for (i = 16; i--; )
+ A[i] = matrix->d[i];
+
+ /* LU decomposition with partial pivoting */
+ for (k = 0; k < 4; ++k) {
+ pivot = find_pivot(&A[k * 4], k);
+ if (pivot != k) {
+ swap_unsigned(&p[k], &p[pivot]);
+ swap_rows(&A[k], &A[pivot]);
+ }
+
+ pv = A[k * 4 + k];
+ if (fabs(pv) < 1e-9)
+ return -1; /* zero pivot, not invertible */
+
+ for (i = k + 1; i < 4; ++i) {
+ A[i + k * 4] /= pv;
+
+ for (j = k + 1; j < 4; ++j)
+ A[i + j * 4] -= A[i + k * 4] * A[k + j * 4];
+ }
+ }
+
+ return 0;
+}
+
+MATRIX_TEST_EXPORT inline void
+inverse_transform(const double *LU, const unsigned *p, float *v)
+{
+ /* Solve A * x = v, when we have P * A = L * U.
+ * P * A * x = P * v => L * U * x = P * v
+ * Let U * x = b, then L * b = P * v.
+ */
+ double b[4];
+ unsigned j;
+
+ /* Forward substitution, column version, solves L * b = P * v */
+ /* The diagonal of L is all ones, and not explicitly stored. */
+ b[0] = v[p[0]];
+ b[1] = (double)v[p[1]] - b[0] * LU[1 + 0 * 4];
+ b[2] = (double)v[p[2]] - b[0] * LU[2 + 0 * 4];
+ b[3] = (double)v[p[3]] - b[0] * LU[3 + 0 * 4];
+ b[2] -= b[1] * LU[2 + 1 * 4];
+ b[3] -= b[1] * LU[3 + 1 * 4];
+ b[3] -= b[2] * LU[3 + 2 * 4];
+
+ /* backward substitution, column version, solves U * y = b */
+#if 1
+ /* hand-unrolled, 25% faster for whole function */
+ b[3] /= LU[3 + 3 * 4];
+ b[0] -= b[3] * LU[0 + 3 * 4];
+ b[1] -= b[3] * LU[1 + 3 * 4];
+ b[2] -= b[3] * LU[2 + 3 * 4];
+
+ b[2] /= LU[2 + 2 * 4];
+ b[0] -= b[2] * LU[0 + 2 * 4];
+ b[1] -= b[2] * LU[1 + 2 * 4];
+
+ b[1] /= LU[1 + 1 * 4];
+ b[0] -= b[1] * LU[0 + 1 * 4];
+
+ b[0] /= LU[0 + 0 * 4];
+#else
+ for (j = 3; j > 0; --j) {
+ unsigned k;
+ b[j] /= LU[j + j * 4];
+ for (k = 0; k < j; ++k)
+ b[k] -= b[j] * LU[k + j * 4];
+ }
+
+ b[0] /= LU[0 + 0 * 4];
+#endif
+
+ /* the result */
+ for (j = 0; j < 4; ++j)
+ v[j] = b[j];
+}
+
+WL_EXPORT int
+weston_matrix_invert(struct weston_matrix *inverse,
+ const struct weston_matrix *matrix)
+{
+ double LU[16]; /* column-major */
+ unsigned perm[4]; /* permutation */
+ unsigned c;
+
+ if (matrix_invert(LU, perm, matrix) < 0)
+ return -1;
+
+ weston_matrix_init(inverse);
+ for (c = 0; c < 4; ++c)
+ inverse_transform(LU, perm, &inverse->d[c * 4]);
+ inverse->type = matrix->type;
+
+ return 0;
+}
diff --git a/shared/matrix.h b/shared/matrix.h
new file mode 100644
index 00000000..e5cf636b
--- /dev/null
+++ b/shared/matrix.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright © 2008-2011 Kristian Høgsberg
+ * Copyright © 2012 Collabora, Ltd.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that 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.
+ */
+
+#ifndef WESTON_MATRIX_H
+#define WESTON_MATRIX_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum weston_matrix_transform_type {
+ WESTON_MATRIX_TRANSFORM_TRANSLATE = (1 << 0),
+ WESTON_MATRIX_TRANSFORM_SCALE = (1 << 1),
+ WESTON_MATRIX_TRANSFORM_ROTATE = (1 << 2),
+ WESTON_MATRIX_TRANSFORM_OTHER = (1 << 3),
+};
+
+struct weston_matrix {
+ float d[16];
+ unsigned int type;
+};
+
+struct weston_vector {
+ float f[4];
+};
+
+void
+weston_matrix_init(struct weston_matrix *matrix);
+void
+weston_matrix_multiply(struct weston_matrix *m, const struct weston_matrix *n);
+void
+weston_matrix_scale(struct weston_matrix *matrix, float x, float y, float z);
+void
+weston_matrix_translate(struct weston_matrix *matrix,
+ float x, float y, float z);
+void
+weston_matrix_rotate_xy(struct weston_matrix *matrix, float cos, float sin);
+void
+weston_matrix_transform(struct weston_matrix *matrix, struct weston_vector *v);
+
+int
+weston_matrix_invert(struct weston_matrix *inverse,
+ const struct weston_matrix *matrix);
+
+#ifdef UNIT_TEST
+# define MATRIX_TEST_EXPORT WL_EXPORT
+
+int
+matrix_invert(double *A, unsigned *p, const struct weston_matrix *matrix);
+
+void
+inverse_transform(const double *LU, const unsigned *p, float *v);
+
+#else
+# define MATRIX_TEST_EXPORT static
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* WESTON_MATRIX_H */
diff --git a/shared/option-parser.c b/shared/option-parser.c
new file mode 100644
index 00000000..c00349a8
--- /dev/null
+++ b/shared/option-parser.c
@@ -0,0 +1,85 @@
+/*
+ * Copyright © 2012 Kristian Høgsberg
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * 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.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include "config-parser.h"
+
+static void
+handle_option(const struct weston_option *option, char *value)
+{
+ switch (option->type) {
+ case WESTON_OPTION_INTEGER:
+ * (int32_t *) option->data = strtol(value, NULL, 0);
+ return;
+ case WESTON_OPTION_UNSIGNED_INTEGER:
+ * (uint32_t *) option->data = strtoul(value, NULL, 0);
+ return;
+ case WESTON_OPTION_STRING:
+ * (char **) option->data = strdup(value);
+ return;
+ case WESTON_OPTION_BOOLEAN:
+ * (int32_t *) option->data = 1;
+ return;
+ default:
+ assert(0);
+ }
+}
+
+int
+parse_options(const struct weston_option *options,
+ int count, int *argc, char *argv[])
+{
+ int i, j, k, len = 0;
+
+ for (i = 1, j = 1; i < *argc; i++) {
+ for (k = 0; k < count; k++) {
+ if (options[k].name)
+ len = strlen(options[k].name);
+ if (options[k].name &&
+ argv[i][0] == '-' &&
+ argv[i][1] == '-' &&
+ strncmp(options[k].name, &argv[i][2], len) == 0 &&
+ (argv[i][len + 2] == '=' || argv[i][len + 2] == '\0')) {
+ handle_option(&options[k], &argv[i][len + 3]);
+ break;
+ } else if (options[k].short_name &&
+ argv[i][0] == '-' &&
+ options[k].short_name == argv[i][1]) {
+ handle_option(&options[k], &argv[i][2]);
+ break;
+ }
+ }
+ if (k == count)
+ argv[j++] = argv[i];
+ }
+ argv[j] = NULL;
+ *argc = j;
+
+ return j;
+}
diff --git a/shared/os-compatibility.c b/shared/os-compatibility.c
new file mode 100644
index 00000000..4f96dd4c
--- /dev/null
+++ b/shared/os-compatibility.c
@@ -0,0 +1,180 @@
+/*
+ * Copyright © 2012 Collabora, Ltd.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * 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.
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/epoll.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "os-compatibility.h"
+
+static int
+set_cloexec_or_close(int fd)
+{
+ long flags;
+
+ if (fd == -1)
+ return -1;
+
+ flags = fcntl(fd, F_GETFD);
+ if (flags == -1)
+ goto err;
+
+ if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
+ goto err;
+
+ return fd;
+
+err:
+ close(fd);
+ return -1;
+}
+
+int
+os_socketpair_cloexec(int domain, int type, int protocol, int *sv)
+{
+ int ret;
+
+#ifdef SOCK_CLOEXEC
+ ret = socketpair(domain, type | SOCK_CLOEXEC, protocol, sv);
+ if (ret == 0 || errno != EINVAL)
+ return ret;
+#endif
+
+ ret = socketpair(domain, type, protocol, sv);
+ if (ret < 0)
+ return ret;
+
+ sv[0] = set_cloexec_or_close(sv[0]);
+ sv[1] = set_cloexec_or_close(sv[1]);
+
+ if (sv[0] != -1 && sv[1] != -1)
+ return 0;
+
+ close(sv[0]);
+ close(sv[1]);
+ return -1;
+}
+
+int
+os_epoll_create_cloexec(void)
+{
+ int fd;
+
+#ifdef EPOLL_CLOEXEC
+ fd = epoll_create1(EPOLL_CLOEXEC);
+ if (fd >= 0)
+ return fd;
+ if (errno != EINVAL)
+ return -1;
+#endif
+
+ fd = epoll_create(1);
+ return set_cloexec_or_close(fd);
+}
+
+static int
+create_tmpfile_cloexec(char *tmpname)
+{
+ int fd;
+
+#ifdef HAVE_MKOSTEMP
+ fd = mkostemp(tmpname, O_CLOEXEC);
+ if (fd >= 0)
+ unlink(tmpname);
+#else
+ fd = mkstemp(tmpname);
+ if (fd >= 0) {
+ fd = set_cloexec_or_close(fd);
+ unlink(tmpname);
+ }
+#endif
+
+ return fd;
+}
+
+/*
+ * Create a new, unique, anonymous file of the given size, and
+ * return the file descriptor for it. The file descriptor is set
+ * CLOEXEC. The file is immediately suitable for mmap()'ing
+ * the given size at offset zero.
+ *
+ * The file should not have a permanent backing store like a disk,
+ * but may have if XDG_RUNTIME_DIR is not properly implemented in OS.
+ *
+ * The file name is deleted from the file system.
+ *
+ * The file is suitable for buffer sharing between processes by
+ * transmitting the file descriptor over Unix sockets using the
+ * SCM_RIGHTS methods.
+ */
+int
+os_create_anonymous_file(off_t size)
+{
+ static const char template[] = "/weston-shared-XXXXXX";
+ const char *path;
+ char *name;
+ int fd;
+
+ path = getenv("XDG_RUNTIME_DIR");
+ if (!path) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ name = malloc(strlen(path) + sizeof(template));
+ if (!name)
+ return -1;
+
+ strcpy(name, path);
+ strcat(name, template);
+
+ fd = create_tmpfile_cloexec(name);
+
+ free(name);
+
+ if (fd < 0)
+ return -1;
+
+ if (ftruncate(fd, size) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+#ifndef HAVE_STRCHRNUL
+char *
+strchrnul(const char *s, int c)
+{
+ while (*s && *s != c)
+ s++;
+ return (char *)s;
+}
+#endif
diff --git a/shared/os-compatibility.h b/shared/os-compatibility.h
new file mode 100644
index 00000000..c1edcfbd
--- /dev/null
+++ b/shared/os-compatibility.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright © 2012 Collabora, Ltd.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * 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.
+ */
+
+#ifndef OS_COMPATIBILITY_H
+#define OS_COMPATIBILITY_H
+
+#include <sys/types.h>
+
+#include "../config.h"
+
+#ifdef HAVE_EXECINFO_H
+#include <execinfo.h>
+#else
+static inline int
+backtrace(void **buffer, int size)
+{
+ return 0;
+}
+#endif
+
+int
+os_socketpair_cloexec(int domain, int type, int protocol, int *sv);
+
+int
+os_epoll_create_cloexec(void);
+
+int
+os_create_anonymous_file(off_t size);
+
+#ifndef HAVE_STRCHRNUL
+char *
+strchrnul(const char *s, int c);
+#endif
+
+#endif /* OS_COMPATIBILITY_H */
diff --git a/shared/zalloc.h b/shared/zalloc.h
new file mode 100644
index 00000000..29da1190
--- /dev/null
+++ b/shared/zalloc.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright © 2013 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that 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.
+ */
+
+#ifndef WESTON_ZALLOC_H
+#define WESTON_ZALLOC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdlib.h>
+
+static inline void *
+zalloc(size_t size)
+{
+ return calloc(1, size);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* WESTON_ZALLOC_H */
diff --git a/src/.gitignore b/src/.gitignore
new file mode 100644
index 00000000..539150d4
--- /dev/null
+++ b/src/.gitignore
@@ -0,0 +1,24 @@
+git-version.h
+version.h
+weston
+weston-launch
+screenshooter-protocol.c
+screenshooter-server-protocol.h
+spring-tool
+text-cursor-position-protocol.c
+text-cursor-position-server-protocol.h
+tablet-shell-protocol.c
+tablet-shell-server-protocol.h
+xserver-protocol.c
+xserver-server-protocol.h
+desktop-shell-protocol.c
+desktop-shell-server-protocol.h
+text-protocol.c
+text-server-protocol.h
+workspaces-protocol.c
+workspaces-server-protocol.h
+input-method-protocol.c
+input-method-server-protocol.h
+subsurface-server-protocol.h
+subsurface-protocol.c
+
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 00000000..749c074a
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,326 @@
+bin_PROGRAMS = weston \
+ $(weston_launch)
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/shared \
+ -DDATADIR='"$(datadir)"' \
+ -DMODULEDIR='"$(moduledir)"' \
+ -DLIBEXECDIR='"$(libexecdir)"' \
+ -DIN_WESTON
+
+weston_LDFLAGS = -export-dynamic
+weston_CFLAGS = $(GCC_CFLAGS) $(COMPOSITOR_CFLAGS) $(LIBUNWIND_CFLAGS)
+weston_LDADD = $(COMPOSITOR_LIBS) $(LIBUNWIND_LIBS) \
+ $(DLOPEN_LIBS) -lm ../shared/libshared.la
+
+weston_SOURCES = \
+ git-version.h \
+ log.c \
+ compositor.c \
+ compositor.h \
+ input.c \
+ data-device.c \
+ filter.c \
+ filter.h \
+ screenshooter.c \
+ screenshooter-protocol.c \
+ screenshooter-server-protocol.h \
+ clipboard.c \
+ text-cursor-position-protocol.c \
+ text-cursor-position-server-protocol.h \
+ zoom.c \
+ text-backend.c \
+ text-protocol.c \
+ text-server-protocol.h \
+ input-method-protocol.c \
+ input-method-server-protocol.h \
+ workspaces-protocol.c \
+ workspaces-server-protocol.h \
+ subsurface-protocol.c \
+ subsurface-server-protocol.h \
+ bindings.c \
+ animation.c \
+ gl-renderer.h \
+ noop-renderer.c \
+ pixman-renderer.c \
+ pixman-renderer.h \
+ ../shared/matrix.c \
+ ../shared/matrix.h \
+ ../shared/zalloc.h \
+ weston-launch.h \
+ weston-egl-ext.h
+
+if ENABLE_EGL
+weston_SOURCES += \
+ gl-renderer.c \
+ vertex-clipping.c \
+ vertex-clipping.h
+endif
+
+git-version.h : .FORCE
+ $(AM_V_GEN)(echo "#define BUILD_ID \"$(shell git --git-dir=$(top_srcdir)/.git describe --always --dirty) $(shell git --git-dir=$(top_srcdir)/.git log -1 --format='%s (%ci)')\"" > $@-new; \
+ cmp -s $@ $@-new || cp $@-new $@; \
+ rm $@-new)
+
+.FORCE :
+
+if ENABLE_XWAYLAND
+SUBDIRS = xwayland
+endif
+
+DIST_SUBDIRS = xwayland
+
+
+if BUILD_WESTON_LAUNCH
+weston_launch = weston-launch
+weston_launch_SOURCES = weston-launch.c weston-launch.h
+weston_launch_CFLAGS= $(GCC_CFLAGS)
+weston_launch_CPPFLAGS = $(WESTON_LAUNCH_CFLAGS) $(SYSTEMD_LOGIN_CFLAGS) \
+ -DBINDIR='"$(bindir)"'
+weston_launch_LDADD = $(WESTON_LAUNCH_LIBS) $(SYSTEMD_LOGIN_LIBS)
+
+if ENABLE_SETUID_INSTALL
+install-exec-hook:
+ chown root $(DESTDIR)$(bindir)/weston-launch
+ chmod u+s $(DESTDIR)$(bindir)/weston-launch
+endif
+
+endif # BUILD_WESTON_LAUNCH
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = weston.pc
+
+westonincludedir = $(includedir)/weston
+westoninclude_HEADERS = \
+ version.h \
+ compositor.h \
+ ../shared/matrix.h \
+ ../shared/config-parser.h \
+ ../shared/zalloc.h
+
+moduledir = $(libdir)/weston
+module_LTLIBRARIES = \
+ $(desktop_shell) \
+ $(tablet_shell) \
+ $(cms_static) \
+ $(cms_colord) \
+ $(x11_backend) \
+ $(drm_backend) \
+ $(wayland_backend) \
+ $(headless_backend) \
+ $(fbdev_backend) \
+ $(rdp_backend)
+
+noinst_LTLIBRARIES =
+
+if INSTALL_RPI_COMPOSITOR
+module_LTLIBRARIES += $(rpi_backend)
+else
+noinst_LTLIBRARIES += $(rpi_backend)
+endif
+
+if ENABLE_X11_COMPOSITOR
+x11_backend = x11-backend.la
+x11_backend_la_LDFLAGS = -module -avoid-version
+x11_backend_la_LIBADD = $(COMPOSITOR_LIBS) $(X11_COMPOSITOR_LIBS) \
+ ../shared/libshared-cairo.la
+x11_backend_la_CFLAGS = \
+ $(COMPOSITOR_CFLAGS) \
+ $(PIXMAN_CFLAGS) \
+ $(CAIRO_CFLAGS) \
+ $(X11_COMPOSITOR_CFLAGS) \
+ $(GCC_CFLAGS)
+x11_backend_la_SOURCES = compositor-x11.c
+endif
+
+if ENABLE_DRM_COMPOSITOR
+drm_backend = drm-backend.la
+drm_backend_la_LDFLAGS = -module -avoid-version
+drm_backend_la_LIBADD = $(COMPOSITOR_LIBS) $(DRM_COMPOSITOR_LIBS) \
+ ../shared/libshared.la -lrt
+drm_backend_la_CFLAGS = \
+ $(COMPOSITOR_CFLAGS) \
+ $(DRM_COMPOSITOR_CFLAGS) \
+ $(GCC_CFLAGS)
+drm_backend_la_SOURCES = \
+ compositor-drm.c \
+ udev-seat.c \
+ udev-seat.h \
+ evdev.c \
+ evdev.h \
+ evdev-touchpad.c \
+ launcher-util.c \
+ launcher-util.h \
+ libbacklight.c \
+ libbacklight.h
+
+if ENABLE_VAAPI_RECORDER
+drm_backend_la_SOURCES += vaapi-recorder.c vaapi-recorder.h
+drm_backend_la_LIBADD += $(LIBVA_LIBS)
+drm_backend_la_CFLAGS += $(LIBVA_CFLAGS)
+endif
+endif
+
+if ENABLE_WAYLAND_COMPOSITOR
+wayland_backend = wayland-backend.la
+wayland_backend_la_LDFLAGS = -module -avoid-version
+wayland_backend_la_LIBADD = $(COMPOSITOR_LIBS) $(WAYLAND_COMPOSITOR_LIBS) \
+ ../shared/libshared-cairo.la
+wayland_backend_la_CFLAGS = \
+ $(COMPOSITOR_CFLAGS) \
+ $(PIXMAN_CFLAGS) \
+ $(CAIRO_CFLAGS) \
+ $(WAYLAND_COMPOSITOR_CFLAGS) \
+ $(GCC_CFLAGS)
+wayland_backend_la_SOURCES = compositor-wayland.c
+endif
+
+if ENABLE_RPI_COMPOSITOR
+rpi_backend = rpi-backend.la
+rpi_backend_la_LDFLAGS = -module -avoid-version
+rpi_backend_la_LIBADD = $(COMPOSITOR_LIBS) \
+ $(RPI_COMPOSITOR_LIBS) \
+ $(RPI_BCM_HOST_LIBS) \
+ ../shared/libshared.la
+rpi_backend_la_CFLAGS = \
+ $(GCC_CFLAGS) \
+ $(COMPOSITOR_CFLAGS) \
+ $(RPI_COMPOSITOR_CFLAGS) \
+ $(RPI_BCM_HOST_CFLAGS)
+rpi_backend_la_SOURCES = \
+ compositor-rpi.c \
+ rpi-renderer.c \
+ rpi-renderer.h \
+ rpi-bcm-stubs.h \
+ launcher-util.c \
+ launcher-util.h \
+ evdev.c \
+ evdev.h \
+ evdev-touchpad.c
+endif
+
+if ENABLE_HEADLESS_COMPOSITOR
+headless_backend = headless-backend.la
+headless_backend_la_LDFLAGS = -module -avoid-version
+headless_backend_la_LIBADD = $(COMPOSITOR_LIBS) \
+ ../shared/libshared.la
+headless_backend_la_CFLAGS = \
+ $(COMPOSITOR_CFLAGS) \
+ $(GCC_CFLAGS)
+headless_backend_la_SOURCES = compositor-headless.c
+endif
+
+if ENABLE_FBDEV_COMPOSITOR
+fbdev_backend = fbdev-backend.la
+fbdev_backend_la_LDFLAGS = -module -avoid-version
+fbdev_backend_la_LIBADD = \
+ $(COMPOSITOR_LIBS) \
+ $(FBDEV_COMPOSITOR_LIBS) \
+ ../shared/libshared.la
+fbdev_backend_la_CFLAGS = \
+ $(COMPOSITOR_CFLAGS) \
+ $(FBDEV_COMPOSITOR_CFLAGS) \
+ $(PIXMAN_CFLAGS) \
+ $(GCC_CFLAGS)
+fbdev_backend_la_SOURCES = \
+ compositor-fbdev.c \
+ udev-seat.c \
+ udev-seat.h \
+ evdev.c \
+ evdev.h \
+ evdev-touchpad.c \
+ launcher-util.c \
+ launcher-util.h
+endif
+
+if ENABLE_RDP_COMPOSITOR
+rdp_backend = rdp-backend.la
+rdp_backend_la_LDFLAGS = -module -avoid-version
+rdp_backend_la_LIBADD = $(COMPOSITOR_LIBS) \
+ $(RDP_COMPOSITOR_LIBS) \
+ ../shared/libshared.la
+rdp_backend_la_CFLAGS = \
+ $(COMPOSITOR_CFLAGS) \
+ $(RDP_COMPOSITOR_CFLAGS) \
+ $(GCC_CFLAGS)
+rdp_backend_la_SOURCES = compositor-rdp.c
+endif
+
+if ENABLE_DESKTOP_SHELL
+desktop_shell = desktop-shell.la
+desktop_shell_la_LDFLAGS = -module -avoid-version
+desktop_shell_la_LIBADD = $(COMPOSITOR_LIBS) \
+ ../shared/libshared.la
+desktop_shell_la_CFLAGS = $(GCC_CFLAGS) $(COMPOSITOR_CFLAGS)
+desktop_shell_la_SOURCES = \
+ shell.c \
+ desktop-shell-protocol.c \
+ desktop-shell-server-protocol.h
+endif
+
+if ENABLE_TABLET_SHELL
+tablet_shell = tablet-shell.la
+tablet_shell_la_LDFLAGS = -module -avoid-version
+tablet_shell_la_LIBADD = $(COMPOSITOR_LIBS)
+tablet_shell_la_CFLAGS = $(GCC_CFLAGS) $(COMPOSITOR_CFLAGS)
+tablet_shell_la_SOURCES = \
+ tablet-shell.c \
+ tablet-shell-protocol.c \
+ tablet-shell-server-protocol.h
+endif
+
+if HAVE_LCMS
+cms_static = cms-static.la
+cms_static_la_LDFLAGS = -module -avoid-version
+cms_static_la_LIBADD = $(COMPOSITOR_LIBS) $(LCMS_LIBS) ../shared/libshared.la
+cms_static_la_CFLAGS = $(GCC_CFLAGS) $(COMPOSITOR_CFLAGS) $(LCMS_CFLAGS)
+cms_static_la_SOURCES = \
+ cms-static.c \
+ cms-helper.c \
+ cms-helper.h
+if ENABLE_COLORD
+cms_colord = cms-colord.la
+cms_colord_la_LDFLAGS = -module -avoid-version
+cms_colord_la_LIBADD = $(COMPOSITOR_LIBS) $(COLORD_LIBS)
+cms_colord_la_CFLAGS = $(GCC_CFLAGS) $(COMPOSITOR_CFLAGS) $(COLORD_CFLAGS)
+cms_colord_la_SOURCES = \
+ cms-colord.c \
+ cms-helper.c \
+ cms-helper.h
+endif
+endif
+
+noinst_PROGRAMS = spring-tool
+
+spring_tool_CFLAGS = $(GCC_CFLAGS) $(COMPOSITOR_CFLAGS)
+spring_tool_LDADD = $(COMPOSITOR_LIBS) -lm
+spring_tool_SOURCES = \
+ spring-tool.c \
+ animation.c \
+ ../shared/matrix.c \
+ ../shared/matrix.h \
+ compositor.h
+
+BUILT_SOURCES = \
+ screenshooter-server-protocol.h \
+ screenshooter-protocol.c \
+ text-cursor-position-server-protocol.h \
+ text-cursor-position-protocol.c \
+ tablet-shell-protocol.c \
+ tablet-shell-server-protocol.h \
+ desktop-shell-protocol.c \
+ desktop-shell-server-protocol.h \
+ text-protocol.c \
+ text-server-protocol.h \
+ input-method-protocol.c \
+ input-method-server-protocol.h \
+ workspaces-server-protocol.h \
+ workspaces-protocol.c \
+ subsurface-server-protocol.h \
+ subsurface-protocol.c \
+ git-version.h
+
+CLEANFILES = $(BUILT_SOURCES)
+
+wayland_protocoldir = $(top_srcdir)/protocol
+include $(top_srcdir)/wayland-scanner.mk
diff --git a/src/animation.c b/src/animation.c
new file mode 100644
index 00000000..2c47ae7e
--- /dev/null
+++ b/src/animation.c
@@ -0,0 +1,336 @@
+/*
+ * Copyright © 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 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.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "compositor.h"
+
+WL_EXPORT void
+weston_spring_init(struct weston_spring *spring,
+ double k, double current, double target)
+{
+ spring->k = k;
+ spring->friction = 400.0;
+ spring->current = current;
+ spring->previous = current;
+ spring->target = target;
+ spring->clip = WESTON_SPRING_OVERSHOOT;
+ spring->min = 0.0;
+ spring->max = 1.0;
+}
+
+WL_EXPORT void
+weston_spring_update(struct weston_spring *spring, uint32_t msec)
+{
+ double force, v, current, step;
+
+ /* Limit the number of executions of the loop below by ensuring that
+ * the timestamp for last update of the spring is no more than 1s ago.
+ * This handles the case where time moves backwards or forwards in
+ * large jumps.
+ */
+ if (msec - spring->timestamp > 1000) {
+ weston_log("unexpectedly large timestamp jump (from %u to %u)\n",
+ spring->timestamp, msec);
+ spring->timestamp = msec - 1000;
+ }
+
+ step = 0.01;
+ while (4 < msec - spring->timestamp) {
+ current = spring->current;
+ v = current - spring->previous;
+ force = spring->k * (spring->target - current) / 10.0 +
+ (spring->previous - current) - v * spring->friction;
+
+ spring->current =
+ current + (current - spring->previous) +
+ force * step * step;
+ spring->previous = current;
+
+ switch (spring->clip) {
+ case WESTON_SPRING_OVERSHOOT:
+ break;
+
+ case WESTON_SPRING_CLAMP:
+ if (spring->current > spring->max) {
+ spring->current = spring->max;
+ spring->previous = spring->max;
+ } else if (spring->current < 0.0) {
+ spring->current = spring->min;
+ spring->previous = spring->min;
+ }
+ break;
+
+ case WESTON_SPRING_BOUNCE:
+ if (spring->current > spring->max) {
+ spring->current =
+ 2 * spring->max - spring->current;
+ spring->previous =
+ 2 * spring->max - spring->previous;
+ } else if (spring->current < spring->min) {
+ spring->current =
+ 2 * spring->min - spring->current;
+ spring->previous =
+ 2 * spring->min - spring->previous;
+ }
+ break;
+ }
+
+ spring->timestamp += 4;
+ }
+}
+
+WL_EXPORT int
+weston_spring_done(struct weston_spring *spring)
+{
+ return fabs(spring->previous - spring->target) < 0.002 &&
+ fabs(spring->current - spring->target) < 0.002;
+}
+
+typedef void (*weston_surface_animation_frame_func_t)(struct weston_surface_animation *animation);
+
+struct weston_surface_animation {
+ struct weston_surface *surface;
+ struct weston_animation animation;
+ struct weston_spring spring;
+ struct weston_transform transform;
+ struct wl_listener listener;
+ float start, stop;
+ weston_surface_animation_frame_func_t frame;
+ weston_surface_animation_frame_func_t reset;
+ weston_surface_animation_done_func_t done;
+ void *data;
+};
+
+static void
+weston_surface_animation_destroy(struct weston_surface_animation *animation)
+{
+ wl_list_remove(&animation->animation.link);
+ wl_list_remove(&animation->listener.link);
+ wl_list_remove(&animation->transform.link);
+ if (animation->reset)
+ animation->reset(animation);
+ weston_surface_geometry_dirty(animation->surface);
+ if (animation->done)
+ animation->done(animation, animation->data);
+ free(animation);
+}
+
+static void
+handle_animation_surface_destroy(struct wl_listener *listener, void *data)
+{
+ struct weston_surface_animation *animation =
+ container_of(listener,
+ struct weston_surface_animation, listener);
+
+ weston_surface_animation_destroy(animation);
+}
+
+static void
+weston_surface_animation_frame(struct weston_animation *base,
+ struct weston_output *output, uint32_t msecs)
+{
+ struct weston_surface_animation *animation =
+ container_of(base,
+ struct weston_surface_animation, animation);
+
+ if (base->frame_counter <= 1)
+ animation->spring.timestamp = msecs;
+
+ weston_spring_update(&animation->spring, msecs);
+
+ if (weston_spring_done(&animation->spring)) {
+ weston_compositor_schedule_repaint(animation->surface->compositor);
+ weston_surface_animation_destroy(animation);
+ return;
+ }
+
+ if (animation->frame)
+ animation->frame(animation);
+
+ weston_surface_geometry_dirty(animation->surface);
+ weston_compositor_schedule_repaint(animation->surface->compositor);
+}
+
+static struct weston_surface_animation *
+weston_surface_animation_run(struct weston_surface *surface,
+ float start, float stop,
+ weston_surface_animation_frame_func_t frame,
+ weston_surface_animation_frame_func_t reset,
+ weston_surface_animation_done_func_t done,
+ void *data)
+{
+ struct weston_surface_animation *animation;
+
+ animation = malloc(sizeof *animation);
+ if (!animation)
+ return NULL;
+
+ animation->surface = surface;
+ animation->frame = frame;
+ animation->reset = reset;
+ animation->done = done;
+ animation->data = data;
+ animation->start = start;
+ animation->stop = stop;
+ weston_matrix_init(&animation->transform.matrix);
+ wl_list_insert(&surface->geometry.transformation_list,
+ &animation->transform.link);
+ weston_spring_init(&animation->spring, 200.0, 0.0, 1.0);
+ animation->spring.friction = 700;
+ animation->animation.frame_counter = 0;
+ animation->animation.frame = weston_surface_animation_frame;
+ weston_surface_animation_frame(&animation->animation, NULL, 0);
+
+ animation->listener.notify = handle_animation_surface_destroy;
+ wl_signal_add(&surface->destroy_signal, &animation->listener);
+
+ wl_list_insert(&surface->output->animation_list,
+ &animation->animation.link);
+
+ return animation;
+}
+
+static void
+reset_alpha(struct weston_surface_animation *animation)
+{
+ struct weston_surface *surface = animation->surface;
+
+ surface->alpha = animation->stop;
+}
+
+static void
+zoom_frame(struct weston_surface_animation *animation)
+{
+ struct weston_surface *es = animation->surface;
+ float scale;
+
+ scale = animation->start +
+ (animation->stop - animation->start) *
+ animation->spring.current;
+ weston_matrix_init(&animation->transform.matrix);
+ weston_matrix_translate(&animation->transform.matrix,
+ -0.5f * es->geometry.width,
+ -0.5f * es->geometry.height, 0);
+ weston_matrix_scale(&animation->transform.matrix, scale, scale, scale);
+ weston_matrix_translate(&animation->transform.matrix,
+ 0.5f * es->geometry.width,
+ 0.5f * es->geometry.height, 0);
+
+ es->alpha = animation->spring.current;
+ if (es->alpha > 1.0)
+ es->alpha = 1.0;
+}
+
+WL_EXPORT struct weston_surface_animation *
+weston_zoom_run(struct weston_surface *surface, float start, float stop,
+ weston_surface_animation_done_func_t done, void *data)
+{
+ struct weston_surface_animation *zoom;
+
+ zoom = weston_surface_animation_run(surface, start, stop,
+ zoom_frame, reset_alpha,
+ done, data);
+
+ weston_spring_init(&zoom->spring, 300.0, start, stop);
+ zoom->spring.friction = 1400;
+ zoom->spring.previous = start - (stop - start) * 0.03;
+
+ return zoom;
+}
+
+static void
+fade_frame(struct weston_surface_animation *animation)
+{
+ if (animation->spring.current > 0.999)
+ animation->surface->alpha = 1;
+ else if (animation->spring.current < 0.001 )
+ animation->surface->alpha = 0;
+ else
+ animation->surface->alpha = animation->spring.current;
+}
+
+WL_EXPORT struct weston_surface_animation *
+weston_fade_run(struct weston_surface *surface,
+ float start, float end, float k,
+ weston_surface_animation_done_func_t done, void *data)
+{
+ struct weston_surface_animation *fade;
+
+ fade = weston_surface_animation_run(surface, 0, end,
+ fade_frame, reset_alpha,
+ done, data);
+
+ weston_spring_init(&fade->spring, k, start, end);
+
+ fade->spring.friction = 1400;
+ fade->spring.previous = -(end - start) * 0.03;
+
+ surface->alpha = start;
+
+ return fade;
+}
+
+WL_EXPORT void
+weston_fade_update(struct weston_surface_animation *fade, float target)
+{
+ fade->spring.target = target;
+}
+
+static void
+slide_frame(struct weston_surface_animation *animation)
+{
+ float scale;
+
+ scale = animation->start +
+ (animation->stop - animation->start) *
+ animation->spring.current;
+ weston_matrix_init(&animation->transform.matrix);
+ weston_matrix_translate(&animation->transform.matrix, 0, scale, 0);
+}
+
+WL_EXPORT struct weston_surface_animation *
+weston_slide_run(struct weston_surface *surface, float start, float stop,
+ weston_surface_animation_done_func_t done, void *data)
+{
+ struct weston_surface_animation *animation;
+
+ animation = weston_surface_animation_run(surface, start, stop,
+ slide_frame, NULL, done,
+ data);
+ if (!animation)
+ return NULL;
+
+ animation->spring.friction = 600;
+ animation->spring.k = 400;
+ animation->spring.clip = WESTON_SPRING_BOUNCE;
+
+ return animation;
+}
diff --git a/src/bindings.c b/src/bindings.c
new file mode 100644
index 00000000..7cbded92
--- /dev/null
+++ b/src/bindings.c
@@ -0,0 +1,342 @@
+/*
+ * Copyright © 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 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.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include "compositor.h"
+
+struct weston_binding {
+ uint32_t key;
+ uint32_t button;
+ uint32_t axis;
+ uint32_t modifier;
+ void *handler;
+ void *data;
+ struct wl_list link;
+};
+
+static struct weston_binding *
+weston_compositor_add_binding(struct weston_compositor *compositor,
+ uint32_t key, uint32_t button, uint32_t axis,
+ uint32_t modifier, void *handler, void *data)
+{
+ struct weston_binding *binding;
+
+ binding = malloc(sizeof *binding);
+ if (binding == NULL)
+ return NULL;
+
+ binding->key = key;
+ binding->button = button;
+ binding->axis = axis;
+ binding->modifier = modifier;
+ binding->handler = handler;
+ binding->data = data;
+
+ return binding;
+}
+
+WL_EXPORT struct weston_binding *
+weston_compositor_add_key_binding(struct weston_compositor *compositor,
+ uint32_t key, uint32_t modifier,
+ weston_key_binding_handler_t handler,
+ void *data)
+{
+ struct weston_binding *binding;
+
+ binding = weston_compositor_add_binding(compositor, key, 0, 0,
+ modifier, handler, data);
+ if (binding == NULL)
+ return NULL;
+
+ wl_list_insert(compositor->key_binding_list.prev, &binding->link);
+
+ return binding;
+}
+
+WL_EXPORT struct weston_binding *
+weston_compositor_add_button_binding(struct weston_compositor *compositor,
+ uint32_t button, uint32_t modifier,
+ weston_button_binding_handler_t handler,
+ void *data)
+{
+ struct weston_binding *binding;
+
+ binding = weston_compositor_add_binding(compositor, 0, button, 0,
+ modifier, handler, data);
+ if (binding == NULL)
+ return NULL;
+
+ wl_list_insert(compositor->button_binding_list.prev, &binding->link);
+
+ return binding;
+}
+
+WL_EXPORT struct weston_binding *
+weston_compositor_add_touch_binding(struct weston_compositor *compositor,
+ uint32_t modifier,
+ weston_touch_binding_handler_t handler,
+ void *data)
+{
+ struct weston_binding *binding;
+
+ binding = weston_compositor_add_binding(compositor, 0, 0, 0,
+ modifier, handler, data);
+ if (binding == NULL)
+ return NULL;
+
+ wl_list_insert(compositor->touch_binding_list.prev, &binding->link);
+
+ return binding;
+}
+
+WL_EXPORT struct weston_binding *
+weston_compositor_add_axis_binding(struct weston_compositor *compositor,
+ uint32_t axis, uint32_t modifier,
+ weston_axis_binding_handler_t handler,
+ void *data)
+{
+ struct weston_binding *binding;
+
+ binding = weston_compositor_add_binding(compositor, 0, 0, axis,
+ modifier, handler, data);
+ if (binding == NULL)
+ return NULL;
+
+ wl_list_insert(compositor->axis_binding_list.prev, &binding->link);
+
+ return binding;
+}
+
+WL_EXPORT struct weston_binding *
+weston_compositor_add_debug_binding(struct weston_compositor *compositor,
+ uint32_t key,
+ weston_key_binding_handler_t handler,
+ void *data)
+{
+ struct weston_binding *binding;
+
+ binding = weston_compositor_add_binding(compositor, key, 0, 0, 0,
+ handler, data);
+
+ wl_list_insert(compositor->debug_binding_list.prev, &binding->link);
+
+ return binding;
+}
+
+WL_EXPORT void
+weston_binding_destroy(struct weston_binding *binding)
+{
+ wl_list_remove(&binding->link);
+ free(binding);
+}
+
+WL_EXPORT void
+weston_binding_list_destroy_all(struct wl_list *list)
+{
+ struct weston_binding *binding, *tmp;
+
+ wl_list_for_each_safe(binding, tmp, list, link)
+ weston_binding_destroy(binding);
+}
+
+struct binding_keyboard_grab {
+ uint32_t key;
+ struct weston_keyboard_grab grab;
+};
+
+static void
+binding_key(struct weston_keyboard_grab *grab,
+ uint32_t time, uint32_t key, uint32_t state_w)
+{
+ struct binding_keyboard_grab *b =
+ container_of(grab, struct binding_keyboard_grab, grab);
+ struct wl_resource *resource;
+ enum wl_keyboard_key_state state = state_w;
+ uint32_t serial;
+ struct weston_keyboard *keyboard = grab->keyboard;
+ struct wl_display *display = keyboard->seat->compositor->wl_display;
+
+ if (key == b->key) {
+ if (state == WL_KEYBOARD_KEY_STATE_RELEASED) {
+ weston_keyboard_end_grab(grab->keyboard);
+ if (keyboard->input_method_resource)
+ keyboard->grab = &keyboard->input_method_grab;
+ free(b);
+ }
+ } else if (!wl_list_empty(&keyboard->focus_resource_list)) {
+ serial = wl_display_next_serial(display);
+ wl_resource_for_each(resource, &keyboard->focus_resource_list) {
+ wl_keyboard_send_key(resource,
+ serial,
+ time,
+ key,
+ state);
+ }
+ }
+}
+
+static void
+binding_modifiers(struct weston_keyboard_grab *grab, uint32_t serial,
+ uint32_t mods_depressed, uint32_t mods_latched,
+ uint32_t mods_locked, uint32_t group)
+{
+ struct wl_resource *resource;
+
+ wl_resource_for_each(resource, &grab->keyboard->focus_resource_list) {
+ wl_keyboard_send_modifiers(resource, serial, mods_depressed,
+ mods_latched, mods_locked, group);
+ }
+}
+
+static void
+binding_cancel(struct weston_keyboard_grab *grab)
+{
+ struct binding_keyboard_grab *binding_grab =
+ container_of(grab, struct binding_keyboard_grab, grab);
+
+ weston_keyboard_end_grab(grab->keyboard);
+ free(binding_grab);
+}
+
+static const struct weston_keyboard_grab_interface binding_grab = {
+ binding_key,
+ binding_modifiers,
+ binding_cancel,
+};
+
+static void
+install_binding_grab(struct weston_seat *seat, uint32_t time, uint32_t key)
+{
+ struct binding_keyboard_grab *grab;
+
+ grab = malloc(sizeof *grab);
+ grab->key = key;
+ grab->grab.interface = &binding_grab;
+ weston_keyboard_start_grab(seat->keyboard, &grab->grab);
+}
+
+WL_EXPORT void
+weston_compositor_run_key_binding(struct weston_compositor *compositor,
+ struct weston_seat *seat,
+ uint32_t time, uint32_t key,
+ enum wl_keyboard_key_state state)
+{
+ struct weston_binding *b;
+
+ if (state == WL_KEYBOARD_KEY_STATE_RELEASED)
+ return;
+
+ wl_list_for_each(b, &compositor->key_binding_list, link) {
+ if (b->key == key && b->modifier == seat->modifier_state) {
+ weston_key_binding_handler_t handler = b->handler;
+ handler(seat, time, key, b->data);
+
+ /* If this was a key binding and it didn't
+ * install a keyboard grab, install one now to
+ * swallow the key release. */
+ if (seat->keyboard->grab ==
+ &seat->keyboard->default_grab)
+ install_binding_grab(seat, time, key);
+ }
+ }
+}
+
+WL_EXPORT void
+weston_compositor_run_button_binding(struct weston_compositor *compositor,
+ struct weston_seat *seat,
+ uint32_t time, uint32_t button,
+ enum wl_pointer_button_state state)
+{
+ struct weston_binding *b;
+
+ if (state == WL_POINTER_BUTTON_STATE_RELEASED)
+ return;
+
+ wl_list_for_each(b, &compositor->button_binding_list, link) {
+ if (b->button == button && b->modifier == seat->modifier_state) {
+ weston_button_binding_handler_t handler = b->handler;
+ handler(seat, time, button, b->data);
+ }
+ }
+}
+
+WL_EXPORT void
+weston_compositor_run_touch_binding(struct weston_compositor *compositor,
+ struct weston_seat *seat, uint32_t time,
+ int touch_type)
+{
+ struct weston_binding *b;
+
+ if (seat->num_tp != 1 || touch_type != WL_TOUCH_DOWN)
+ return;
+
+ wl_list_for_each(b, &compositor->touch_binding_list, link) {
+ if (b->modifier == seat->modifier_state) {
+ weston_touch_binding_handler_t handler = b->handler;
+ handler(seat, time, b->data);
+ }
+ }
+}
+
+WL_EXPORT int
+weston_compositor_run_axis_binding(struct weston_compositor *compositor,
+ struct weston_seat *seat,
+ uint32_t time, uint32_t axis,
+ wl_fixed_t value)
+{
+ struct weston_binding *b;
+
+ wl_list_for_each(b, &compositor->axis_binding_list, link) {
+ if (b->axis == axis && b->modifier == seat->modifier_state) {
+ weston_axis_binding_handler_t handler = b->handler;
+ handler(seat, time, axis, value, b->data);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+WL_EXPORT int
+weston_compositor_run_debug_binding(struct weston_compositor *compositor,
+ struct weston_seat *seat,
+ uint32_t time, uint32_t key,
+ enum wl_keyboard_key_state state)
+{
+ weston_key_binding_handler_t handler;
+ struct weston_binding *binding;
+ int count = 0;
+
+ wl_list_for_each(binding, &compositor->debug_binding_list, link) {
+ if (key != binding->key)
+ continue;
+
+ count++;
+ handler = binding->handler;
+ handler(seat, time, key, binding->data);
+ }
+
+ return count;
+}
diff --git a/src/clipboard.c b/src/clipboard.c
new file mode 100644
index 00000000..5a3a02d2
--- /dev/null
+++ b/src/clipboard.c
@@ -0,0 +1,300 @@
+/*
+ * Copyright © 2012 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 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.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <linux/input.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/uio.h>
+
+#include "compositor.h"
+
+struct clipboard_source {
+ struct weston_data_source base;
+ struct wl_array contents;
+ struct clipboard *clipboard;
+ struct wl_event_source *event_source;
+ uint32_t serial;
+ int refcount;
+ int fd;
+};
+
+struct clipboard {
+ struct weston_seat *seat;
+ struct wl_listener selection_listener;
+ struct wl_listener destroy_listener;
+ struct clipboard_source *source;
+};
+
+static void clipboard_client_create(struct clipboard_source *source, int fd);
+
+static void
+clipboard_source_unref(struct clipboard_source *source)
+{
+ char **s;
+
+ source->refcount--;
+ if (source->refcount > 0)
+ return;
+
+ if (source->event_source) {
+ wl_event_source_remove(source->event_source);
+ close(source->fd);
+ }
+ wl_signal_emit(&source->base.destroy_signal,
+ &source->base);
+ s = source->base.mime_types.data;
+ free(*s);
+ wl_array_release(&source->base.mime_types);
+ wl_array_release(&source->contents);
+ free(source);
+}
+
+static int
+clipboard_source_data(int fd, uint32_t mask, void *data)
+{
+ struct clipboard_source *source = data;
+ struct clipboard *clipboard = source->clipboard;
+ char *p;
+ int len, size;
+
+ if (source->contents.alloc - source->contents.size < 1024) {
+ wl_array_add(&source->contents, 1024);
+ source->contents.size -= 1024;
+ }
+
+ p = source->contents.data + source->contents.size;
+ size = source->contents.alloc - source->contents.size;
+ len = read(fd, p, size);
+ if (len == 0) {
+ wl_event_source_remove(source->event_source);
+ close(fd);
+ source->event_source = NULL;
+ } else if (len < 0) {
+ clipboard_source_unref(source);
+ clipboard->source = NULL;
+ } else {
+ source->contents.size += len;
+ }
+
+ return 1;
+}
+
+static void
+clipboard_source_accept(struct weston_data_source *source,
+ uint32_t time, const char *mime_type)
+{
+}
+
+static void
+clipboard_source_send(struct weston_data_source *base,
+ const char *mime_type, int32_t fd)
+{
+ struct clipboard_source *source =
+ container_of(base, struct clipboard_source, base);
+ char **s;
+
+ s = source->base.mime_types.data;
+ if (strcmp(mime_type, s[0]) == 0)
+ clipboard_client_create(source, fd);
+ else
+ close(fd);
+}
+
+static void
+clipboard_source_cancel(struct weston_data_source *source)
+{
+}
+
+static struct clipboard_source *
+clipboard_source_create(struct clipboard *clipboard,
+ const char *mime_type, uint32_t serial, int fd)
+{
+ struct wl_display *display = clipboard->seat->compositor->wl_display;
+ struct wl_event_loop *loop = wl_display_get_event_loop(display);
+ struct clipboard_source *source;
+ char **s;
+
+ source = malloc(sizeof *source);
+ if (source == NULL)
+ return NULL;
+
+ wl_array_init(&source->contents);
+ wl_array_init(&source->base.mime_types);
+ source->base.resource = NULL;
+ source->base.accept = clipboard_source_accept;
+ source->base.send = clipboard_source_send;
+ source->base.cancel = clipboard_source_cancel;
+ wl_signal_init(&source->base.destroy_signal);
+ source->refcount = 1;
+ source->clipboard = clipboard;
+ source->serial = serial;
+
+ s = wl_array_add(&source->base.mime_types, sizeof *s);
+ if (s == NULL)
+ goto err_add;
+ *s = strdup(mime_type);
+ if (*s == NULL)
+ goto err_strdup;
+ source->event_source =
+ wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE,
+ clipboard_source_data, source);
+ if (source->event_source == NULL)
+ goto err_source;
+
+ return source;
+
+ err_source:
+ free(*s);
+ err_strdup:
+ wl_array_release(&source->base.mime_types);
+ err_add:
+ free(source);
+
+ return NULL;
+}
+
+struct clipboard_client {
+ struct wl_event_source *event_source;
+ size_t offset;
+ struct clipboard_source *source;
+};
+
+static int
+clipboard_client_data(int fd, uint32_t mask, void *data)
+{
+ struct clipboard_client *client = data;
+ char *p;
+ size_t size;
+ int len;
+
+ size = client->source->contents.size;
+ p = client->source->contents.data;
+ len = write(fd, p + client->offset, size - client->offset);
+ if (len > 0)
+ client->offset += len;
+
+ if (client->offset == size || len <= 0) {
+ close(fd);
+ wl_event_source_remove(client->event_source);
+ clipboard_source_unref(client->source);
+ free(client);
+ }
+
+ return 1;
+}
+
+static void
+clipboard_client_create(struct clipboard_source *source, int fd)
+{
+ struct weston_seat *seat = source->clipboard->seat;
+ struct clipboard_client *client;
+ struct wl_event_loop *loop =
+ wl_display_get_event_loop(seat->compositor->wl_display);
+
+ client = malloc(sizeof *client);
+
+ client->offset = 0;
+ client->source = source;
+ source->refcount++;
+ client->event_source =
+ wl_event_loop_add_fd(loop, fd, WL_EVENT_WRITABLE,
+ clipboard_client_data, client);
+}
+
+static void
+clipboard_set_selection(struct wl_listener *listener, void *data)
+{
+ struct clipboard *clipboard =
+ container_of(listener, struct clipboard, selection_listener);
+ struct weston_seat *seat = data;
+ struct weston_data_source *source = seat->selection_data_source;
+ const char **mime_types;
+ int p[2];
+
+ if (source == NULL) {
+ if (clipboard->source)
+ weston_seat_set_selection(seat,
+ &clipboard->source->base,
+ clipboard->source->serial);
+ return;
+ } else if (source->accept == clipboard_source_accept) {
+ /* Callback for our data source. */
+ return;
+ }
+
+ if (clipboard->source)
+ clipboard_source_unref(clipboard->source);
+
+ clipboard->source = NULL;
+
+ mime_types = source->mime_types.data;
+
+ if (pipe2(p, O_CLOEXEC) == -1)
+ return;
+
+ source->send(source, mime_types[0], p[1]);
+
+ clipboard->source =
+ clipboard_source_create(clipboard, mime_types[0],
+ seat->selection_serial, p[0]);
+ if (clipboard->source == NULL) {
+ close(p[0]);
+ return;
+ }
+}
+
+static void
+clipboard_destroy(struct wl_listener *listener, void *data)
+{
+ struct clipboard *clipboard =
+ container_of(listener, struct clipboard, destroy_listener);
+
+ wl_list_remove(&clipboard->selection_listener.link);
+ wl_list_remove(&clipboard->destroy_listener.link);
+
+ free(clipboard);
+}
+
+struct clipboard *
+clipboard_create(struct weston_seat *seat)
+{
+ struct clipboard *clipboard;
+
+ clipboard = zalloc(sizeof *clipboard);
+ if (clipboard == NULL)
+ return NULL;
+
+ clipboard->seat = seat;
+ clipboard->selection_listener.notify = clipboard_set_selection;
+ clipboard->destroy_listener.notify = clipboard_destroy;
+
+ wl_signal_add(&seat->selection_signal,
+ &clipboard->selection_listener);
+ wl_signal_add(&seat->destroy_signal,
+ &clipboard->destroy_listener);
+
+ return clipboard;
+}
diff --git a/src/cms-colord.c b/src/cms-colord.c
new file mode 100644
index 00000000..4ff3aaca
--- /dev/null
+++ b/src/cms-colord.c
@@ -0,0 +1,554 @@
+/*
+ * Copyright © 2013 Richard Hughes
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <colord.h>
+
+#include "compositor.h"
+#include "cms-helper.h"
+
+struct cms_colord {
+ struct weston_compositor *ec;
+ CdClient *client;
+ GHashTable *devices; /* key = device-id, value = cms_output */
+ GHashTable *pnp_ids; /* key = pnp-id, value = vendor */
+ gchar *pnp_ids_data;
+ GMainLoop *loop;
+ GThread *thread;
+ GList *pending;
+ GMutex pending_mutex;
+ struct wl_event_source *source;
+ int readfd;
+ int writefd;
+ struct wl_listener destroy_listener;
+ struct wl_listener output_created_listener;
+};
+
+struct cms_output {
+ CdDevice *device;
+ guint32 backlight_value;
+ struct cms_colord *cms;
+ struct weston_color_profile *p;
+ struct weston_output *o;
+ struct wl_listener destroy_listener;
+};
+
+static gint
+colord_idle_find_output_cb(gconstpointer a, gconstpointer b)
+{
+ struct cms_output *ocms = (struct cms_output *) a;
+ struct weston_output *o = (struct weston_output *) b;
+ return ocms->o == o ? 0 : -1;
+}
+
+static void
+colord_idle_cancel_for_output(struct cms_colord *cms, struct weston_output *o)
+{
+ GList *l;
+
+ /* cancel and remove any helpers that match the output */
+ g_mutex_lock(&cms->pending_mutex);
+ l = g_list_find_custom (cms->pending, o, colord_idle_find_output_cb);
+ if (l) {
+ struct cms_output *ocms = l->data;
+ cms->pending = g_list_remove (cms->pending, ocms);
+ }
+ g_mutex_unlock(&cms->pending_mutex);
+}
+
+static int
+edid_value_valid(const char *str)
+{
+ if (str == NULL)
+ return 0;
+ if (str[0] == '\0')
+ return 0;
+ if (strcmp(str, "unknown") == 0)
+ return 0;
+ return 1;
+}
+
+static gchar *
+get_output_id(struct cms_colord *cms, struct weston_output *o)
+{
+ const gchar *tmp;
+ GString *device_id;
+
+ /* see https://github.com/hughsie/colord/blob/master/doc/device-and-profile-naming-spec.txt
+ * for format and allowed values */
+ device_id = g_string_new("xrandr");
+ if (edid_value_valid(o->make)) {
+ tmp = g_hash_table_lookup(cms->pnp_ids, o->make);
+ if (tmp == NULL)
+ tmp = o->make;
+ g_string_append_printf(device_id, "-%s", tmp);
+ }
+ if (edid_value_valid(o->model))
+ g_string_append_printf(device_id, "-%s", o->model);
+ if (edid_value_valid(o->serial_number))
+ g_string_append_printf(device_id, "-%s", o->serial_number);
+
+ /* no EDID data, so use fallback */
+ if (strcmp(device_id->str, "xrandr") == 0)
+ g_string_append_printf(device_id, "-drm-%i", o->id);
+
+ return g_string_free(device_id, FALSE);
+}
+
+static void
+update_device_with_profile_in_idle(struct cms_output *ocms)
+{
+ gboolean signal_write = FALSE;
+ ssize_t rc;
+ struct cms_colord *cms = ocms->cms;
+
+ colord_idle_cancel_for_output(cms, ocms->o);
+ g_mutex_lock(&cms->pending_mutex);
+ if (cms->pending == NULL)
+ signal_write = TRUE;
+ cms->pending = g_list_prepend(cms->pending, ocms);
+ g_mutex_unlock(&cms->pending_mutex);
+
+ /* signal we've got updates to do */
+ if (signal_write) {
+ gchar tmp = '\0';
+ rc = write(cms->writefd, &tmp, 1);
+ if (rc == 0)
+ weston_log("colord: failed to write to pending fd");
+ }
+}
+
+static void
+colord_update_output_from_device (struct cms_output *ocms)
+{
+ CdProfile *profile;
+ const gchar *tmp;
+ gboolean ret;
+ GError *error = NULL;
+ gint percentage;
+
+ /* old profile is no longer valid */
+ weston_cms_destroy_profile(ocms->p);
+ ocms->p = NULL;
+
+ ret = cd_device_connect_sync(ocms->device, NULL, &error);
+ if (!ret) {
+ weston_log("colord: failed to connect to device %s: %s\n",
+ cd_device_get_object_path (ocms->device),
+ error->message);
+ g_error_free(error);
+ goto out;
+ }
+ profile = cd_device_get_default_profile(ocms->device);
+ if (!profile) {
+ weston_log("colord: no assigned color profile for %s\n",
+ cd_device_get_id (ocms->device));
+ goto out;
+ }
+ ret = cd_profile_connect_sync(profile, NULL, &error);
+ if (!ret) {
+ weston_log("colord: failed to connect to profile %s: %s\n",
+ cd_profile_get_object_path (profile),
+ error->message);
+ g_error_free(error);
+ goto out;
+ }
+
+ /* get the calibration brightness level (only set for some profiles) */
+ tmp = cd_profile_get_metadata_item(profile, CD_PROFILE_METADATA_SCREEN_BRIGHTNESS);
+ if (tmp != NULL) {
+ percentage = atoi(tmp);
+ if (percentage > 0 && percentage <= 100)
+ ocms->backlight_value = percentage * 255 / 100;
+ }
+
+ ocms->p = weston_cms_load_profile(cd_profile_get_filename(profile));
+ if (ocms->p == NULL) {
+ weston_log("colord: warning failed to load profile %s: %s\n",
+ cd_profile_get_object_path (profile),
+ error->message);
+ g_error_free(error);
+ goto out;
+ }
+out:
+ update_device_with_profile_in_idle(ocms);
+}
+
+static void
+colord_device_changed_cb(CdDevice *device, struct cms_output *ocms)
+{
+ weston_log("colord: device %s changed, update output\n",
+ cd_device_get_object_path (ocms->device));
+ colord_update_output_from_device(ocms);
+}
+
+static void
+colord_notifier_output_destroy(struct wl_listener *listener, void *data)
+{
+ struct cms_colord *cms =
+ container_of(listener, struct cms_colord, destroy_listener);
+ struct weston_output *o = (struct weston_output *) data;
+ struct cms_output *ocms;
+ gboolean ret;
+ gchar *device_id;
+ GError *error = NULL;
+
+ colord_idle_cancel_for_output(cms, o);
+ device_id = get_output_id(cms, o);
+ weston_log("colord: output removed %s\n", device_id);
+ ocms = g_hash_table_lookup(cms->devices, device_id);
+ if (!ocms) {
+ weston_log("colord: failed to delete device\n");
+ goto out;
+ }
+ g_signal_handlers_disconnect_by_data(ocms->device, ocms);
+ ret = cd_client_delete_device_sync (cms->client,
+ ocms->device,
+ NULL,
+ &error);
+
+ if (!ret) {
+ weston_log("colord: failed to delete device: %s\n", error->message);
+ g_error_free(error);
+ goto out;
+ }
+out:
+ g_hash_table_remove (cms->devices, device_id);
+ g_free (device_id);
+}
+
+static void
+colord_output_created(struct cms_colord *cms, struct weston_output *o)
+{
+ CdDevice *device;
+ const gchar *tmp;
+ gchar *device_id;
+ GError *error = NULL;
+ GHashTable *device_props;
+ struct cms_output *ocms;
+
+ /* create device */
+ device_id = get_output_id(cms, o);
+ weston_log("colord: output added %s\n", device_id);
+ device_props = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, g_free);
+ g_hash_table_insert (device_props,
+ g_strdup(CD_DEVICE_PROPERTY_KIND),
+ g_strdup(cd_device_kind_to_string (CD_DEVICE_KIND_DISPLAY)));
+ g_hash_table_insert (device_props,
+ g_strdup(CD_DEVICE_PROPERTY_FORMAT),
+ g_strdup("ColorModel.OutputMode.OutputResolution"));
+ g_hash_table_insert (device_props,
+ g_strdup(CD_DEVICE_PROPERTY_COLORSPACE),
+ g_strdup(cd_colorspace_to_string(CD_COLORSPACE_RGB)));
+ if (edid_value_valid(o->make)) {
+ tmp = g_hash_table_lookup(cms->pnp_ids, o->make);
+ if (tmp == NULL)
+ tmp = o->make;
+ g_hash_table_insert (device_props,
+ g_strdup(CD_DEVICE_PROPERTY_VENDOR),
+ g_strdup(tmp));
+ }
+ if (edid_value_valid(o->model)) {
+ g_hash_table_insert (device_props,
+ g_strdup(CD_DEVICE_PROPERTY_MODEL),
+ g_strdup(o->model));
+ }
+ if (edid_value_valid(o->serial_number)) {
+ g_hash_table_insert (device_props,
+ g_strdup(CD_DEVICE_PROPERTY_SERIAL),
+ g_strdup(o->serial_number));
+ }
+ if (o->connection_internal) {
+ g_hash_table_insert (device_props,
+ g_strdup (CD_DEVICE_PROPERTY_EMBEDDED),
+ NULL);
+ }
+ device = cd_client_create_device_sync(cms->client,
+ device_id,
+ CD_OBJECT_SCOPE_TEMP,
+ device_props,
+ NULL,
+ &error);
+ if (g_error_matches (error,
+ CD_CLIENT_ERROR,
+ CD_CLIENT_ERROR_ALREADY_EXISTS)) {
+ g_clear_error(&error);
+ device = cd_client_find_device_sync (cms->client,
+ device_id,
+ NULL,
+ &error);
+ }
+ if (!device) {
+ weston_log("colord: failed to create new or "
+ "find existing device: %s\n",
+ error->message);
+ g_error_free(error);
+ goto out;
+ }
+
+ /* create object and watch for the output to be destroyed */
+ ocms = g_slice_new0(struct cms_output);
+ ocms->cms = cms;
+ ocms->o = o;
+ ocms->device = g_object_ref(device);
+ ocms->destroy_listener.notify = colord_notifier_output_destroy;
+ wl_signal_add(&o->destroy_signal, &ocms->destroy_listener);
+
+ /* add to local cache */
+ g_hash_table_insert (cms->devices, g_strdup(device_id), ocms);
+ g_signal_connect (ocms->device, "changed",
+ G_CALLBACK (colord_device_changed_cb), ocms);
+
+ /* get profiles */
+ colord_update_output_from_device (ocms);
+out:
+ g_hash_table_unref (device_props);
+ if (device)
+ g_object_unref (device);
+ g_free (device_id);
+}
+
+static void
+colord_notifier_output_created(struct wl_listener *listener, void *data)
+{
+ struct weston_output *o = (struct weston_output *) data;
+ struct cms_colord *cms =
+ container_of(listener, struct cms_colord, destroy_listener);
+ weston_log("colord: output %s created\n", o->name);
+ colord_output_created(cms, o);
+}
+
+static gpointer
+colord_run_loop_thread(gpointer data)
+{
+ struct cms_colord *cms = (struct cms_colord *) data;
+ struct weston_output *o;
+
+ /* coldplug outputs */
+ wl_list_for_each(o, &cms->ec->output_list, link) {
+ weston_log("colord: output %s coldplugged\n", o->name);
+ colord_output_created(cms, o);
+ }
+
+ g_main_loop_run(cms->loop);
+ return NULL;
+}
+
+static int
+colord_dispatch_all_pending(int fd, uint32_t mask, void *data)
+{
+ gchar tmp;
+ GList *l;
+ ssize_t rc;
+ struct cms_colord *cms = data;
+ struct cms_output *ocms;
+
+ weston_log("colord: dispatching events\n");
+ g_mutex_lock(&cms->pending_mutex);
+ for (l = cms->pending; l != NULL; l = l->next) {
+ ocms = l->data;
+
+ /* optionally set backlight to calibration value */
+ if (ocms->o->set_backlight && ocms->backlight_value != 0) {
+ weston_log("colord: profile calibration backlight to %i/255\n",
+ ocms->backlight_value);
+ ocms->o->set_backlight(ocms->o, ocms->backlight_value);
+ }
+
+ weston_cms_set_color_profile(ocms->o, ocms->p);
+ }
+ g_list_free (cms->pending);
+ cms->pending = NULL;
+ g_mutex_unlock(&cms->pending_mutex);
+
+ /* done */
+ rc = read(cms->readfd, &tmp, 1);
+ if (rc == 0)
+ weston_log("colord: failed to read from pending fd");
+ return 1;
+}
+
+static void
+colord_load_pnp_ids(struct cms_colord *cms)
+{
+ gboolean ret = FALSE;
+ gchar *tmp;
+ GError *error = NULL;
+ guint i;
+ const gchar *pnp_ids_fn[] = { "/usr/share/hwdata/pnp.ids",
+ "/usr/share/misc/pnp.ids",
+ NULL };
+
+ /* find and load file */
+ for (i = 0; pnp_ids_fn[i] != NULL; i++) {
+ if (!g_file_test(pnp_ids_fn[i], G_FILE_TEST_EXISTS))
+ continue;
+ ret = g_file_get_contents(pnp_ids_fn[i],
+ &cms->pnp_ids_data,
+ NULL,
+ &error);
+ if (!ret) {
+ weston_log("colord: failed to load %s: %s\n",
+ pnp_ids_fn[i], error->message);
+ g_error_free(error);
+ return;
+ }
+ break;
+ }
+ if (!ret) {
+ weston_log("colord: no pnp.ids found\n");
+ return;
+ }
+
+ /* parse fixed offsets into lines */
+ tmp = cms->pnp_ids_data;
+ for (i = 0; cms->pnp_ids_data[i] != '\0'; i++) {
+ if (cms->pnp_ids_data[i] != '\n')
+ continue;
+ cms->pnp_ids_data[i] = '\0';
+ if (tmp[0] && tmp[1] && tmp[2] && tmp[3] == '\t' && tmp[4]) {
+ tmp[3] = '\0';
+ g_hash_table_insert(cms->pnp_ids, tmp, tmp+4);
+ tmp = &cms->pnp_ids_data[i+1];
+ }
+ }
+}
+
+static void
+colord_module_destroy(struct cms_colord *cms)
+{
+ g_free(cms->pnp_ids_data);
+ g_hash_table_unref(cms->pnp_ids);
+
+ if (cms->loop) {
+ g_main_loop_quit(cms->loop);
+ g_main_loop_unref(cms->loop);
+ }
+ if (cms->thread)
+ g_thread_join(cms->thread);
+ if (cms->devices)
+ g_hash_table_unref(cms->devices);
+ if (cms->client)
+ g_object_unref(cms->client);
+ if (cms->readfd)
+ close(cms->readfd);
+ if (cms->writefd)
+ close(cms->writefd);
+ free(cms);
+}
+
+static void
+colord_notifier_destroy(struct wl_listener *listener, void *data)
+{
+ struct cms_colord *cms =
+ container_of(listener, struct cms_colord, destroy_listener);
+ colord_module_destroy(cms);
+}
+
+static void
+colord_cms_output_destroy(gpointer data)
+{
+ struct cms_output *ocms = (struct cms_output *) data;
+ g_object_unref(ocms->device);
+ g_slice_free(struct cms_output, ocms);
+}
+
+WL_EXPORT int
+module_init(struct weston_compositor *ec,
+ int *argc, char *argv[])
+{
+ gboolean ret;
+ GError *error = NULL;
+ int fd[2];
+ struct cms_colord *cms;
+ struct wl_event_loop *loop;
+
+ weston_log("colord: initialized\n");
+
+ /* create local state object */
+ cms = zalloc(sizeof *cms);
+ if (cms == NULL)
+ return -1;
+ cms->ec = ec;
+#if !GLIB_CHECK_VERSION(2,36,0)
+ g_type_init();
+#endif
+ cms->client = cd_client_new();
+ ret = cd_client_connect_sync(cms->client, NULL, &error);
+ if (!ret) {
+ weston_log("colord: failed to contact daemon: %s\n", error->message);
+ g_error_free(error);
+ colord_module_destroy(cms);
+ return -1;
+ }
+ g_mutex_init(&cms->pending_mutex);
+ cms->devices = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, colord_cms_output_destroy);
+
+ /* destroy */
+ cms->destroy_listener.notify = colord_notifier_destroy;
+ wl_signal_add(&ec->destroy_signal, &cms->destroy_listener);
+
+ /* devices added */
+ cms->output_created_listener.notify = colord_notifier_output_created;
+ wl_signal_add(&ec->output_created_signal, &cms->output_created_listener);
+
+ /* add all the PNP IDs */
+ cms->pnp_ids = g_hash_table_new_full(g_str_hash,
+ g_str_equal,
+ NULL,
+ NULL);
+ colord_load_pnp_ids(cms);
+
+ /* setup a thread for the GLib callbacks */
+ cms->loop = g_main_loop_new(NULL, FALSE);
+ cms->thread = g_thread_new("colord CMS main loop",
+ colord_run_loop_thread, cms);
+
+ /* batch device<->profile updates */
+ if (pipe2(fd, O_CLOEXEC) == -1) {
+ colord_module_destroy(cms);
+ return -1;
+ }
+ cms->readfd = fd[0];
+ cms->writefd = fd[1];
+ loop = wl_display_get_event_loop(ec->wl_display);
+ cms->source = wl_event_loop_add_fd (loop,
+ cms->readfd,
+ WL_EVENT_READABLE,
+ colord_dispatch_all_pending,
+ cms);
+ if (!cms->source) {
+ colord_module_destroy(cms);
+ return -1;
+ }
+ return 0;
+}
diff --git a/src/cms-helper.c b/src/cms-helper.c
new file mode 100644
index 00000000..c063c773
--- /dev/null
+++ b/src/cms-helper.c
@@ -0,0 +1,132 @@
+/*
+ * Copyright © 2013 Richard Hughes
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that 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.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#ifdef HAVE_LCMS
+#include <lcms2.h>
+#endif
+
+#include "compositor.h"
+#include "cms-helper.h"
+
+#ifdef HAVE_LCMS
+static void
+weston_cms_gamma_clear(struct weston_output *o)
+{
+ int i;
+ uint16_t *red;
+
+ if (!o->set_gamma)
+ return;
+
+ red = calloc(o->gamma_size, sizeof(uint16_t));
+ for (i = 0; i < o->gamma_size; i++)
+ red[i] = (uint32_t) 0xffff * (uint32_t) i / (uint32_t) (o->gamma_size - 1);
+ o->set_gamma(o, o->gamma_size, red, red, red);
+ free(red);
+}
+#endif
+
+void
+weston_cms_set_color_profile(struct weston_output *o,
+ struct weston_color_profile *p)
+{
+#ifdef HAVE_LCMS
+ cmsFloat32Number in;
+ const cmsToneCurve **vcgt;
+ int i;
+ int size;
+ uint16_t *red = NULL;
+ uint16_t *green = NULL;
+ uint16_t *blue = NULL;
+
+ if (!o->set_gamma)
+ return;
+ if (!p) {
+ weston_cms_gamma_clear(o);
+ return;
+ }
+
+ weston_log("Using ICC profile %s\n", p->filename);
+ vcgt = cmsReadTag (p->lcms_handle, cmsSigVcgtTag);
+ if (vcgt == NULL || vcgt[0] == NULL) {
+ weston_cms_gamma_clear(o);
+ return;
+ }
+
+ size = o->gamma_size;
+ red = calloc(size, sizeof(uint16_t));
+ green = calloc(size, sizeof(uint16_t));
+ blue = calloc(size, sizeof(uint16_t));
+ for (i = 0; i < size; i++) {
+ in = (cmsFloat32Number) i / (cmsFloat32Number) (size - 1);
+ red[i] = cmsEvalToneCurveFloat(vcgt[0], in) * (double) 0xffff;
+ green[i] = cmsEvalToneCurveFloat(vcgt[1], in) * (double) 0xffff;
+ blue[i] = cmsEvalToneCurveFloat(vcgt[2], in) * (double) 0xffff;
+ }
+ o->set_gamma(o, size, red, green, blue);
+ free(red);
+ free(green);
+ free(blue);
+#endif
+}
+
+void
+weston_cms_destroy_profile(struct weston_color_profile *p)
+{
+ if (!p)
+ return;
+#ifdef HAVE_LCMS
+ cmsCloseProfile(p->lcms_handle);
+#endif
+ free(p->filename);
+ free(p);
+}
+
+struct weston_color_profile *
+weston_cms_create_profile(const char *filename,
+ void *lcms_profile)
+{
+ struct weston_color_profile *p;
+ p = calloc(1, sizeof(struct weston_color_profile));
+ p->filename = strdup(filename);
+ p->lcms_handle = lcms_profile;
+ return p;
+}
+
+struct weston_color_profile *
+weston_cms_load_profile(const char *filename)
+{
+ struct weston_color_profile *p = NULL;
+#ifdef HAVE_LCMS
+ cmsHPROFILE lcms_profile;
+ lcms_profile = cmsOpenProfileFromFile(filename, "r");
+ if (lcms_profile)
+ p = weston_cms_create_profile(filename, lcms_profile);
+#endif
+ return p;
+}
diff --git a/src/cms-helper.h b/src/cms-helper.h
new file mode 100644
index 00000000..6e5594df
--- /dev/null
+++ b/src/cms-helper.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright © 2013 Richard Hughes
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that 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.
+ */
+
+#ifndef _WESTON_CMS_H_
+#define _WESTON_CMS_H_
+
+#include "config.h"
+
+#include "compositor.h"
+
+/* General overview on how to be a CMS plugin:
+ *
+ * First, some nomenclature:
+ *
+ * CMF: Color management framework, i.e. "Use foo.icc for device $bar"
+ * CMM: Color management module that converts pixel colors, which is
+ * usually lcms2 on any modern OS.
+ * CMS: Color management system that encompasses both a CMF and CMM.
+ * ICC: International Color Consortium, the people that define the
+ * binary encoding of a .icc file.
+ * VCGT: Video Card Gamma Tag. An Apple extension to the ICC specification
+ * that allows the calibration state to be stored in the ICC profile
+ * Output: Physical port with a display attached, e.g. LVDS1
+ *
+ * As a CMF is probably something you don't want or need on an embeded install
+ * these functions will not be called if the icc_profile key is set for a
+ * specific [output] section in weston.ini
+ *
+ * Most desktop environments want the CMF to decide what profile to use in
+ * different situations, so that displays can be profiled and also so that
+ * the ICC profiles can be changed at runtime depending on the task or ambient
+ * environment.
+ *
+ * The CMF can be selected using the 'modules' key in the [core] section.
+ */
+
+struct weston_color_profile {
+ char *filename;
+ void *lcms_handle;
+};
+
+void
+weston_cms_set_color_profile(struct weston_output *o,
+ struct weston_color_profile *p);
+struct weston_color_profile *
+weston_cms_create_profile(const char *filename,
+ void *lcms_profile);
+struct weston_color_profile *
+weston_cms_load_profile(const char *filename);
+void
+weston_cms_destroy_profile(struct weston_color_profile *p);
+
+#endif
diff --git a/src/cms-static.c b/src/cms-static.c
new file mode 100644
index 00000000..ad54fd15
--- /dev/null
+++ b/src/cms-static.c
@@ -0,0 +1,113 @@
+/*
+ * Copyright © 2013 Richard Hughes
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that 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.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "compositor.h"
+#include "cms-helper.h"
+
+struct cms_static {
+ struct weston_compositor *ec;
+ struct wl_listener destroy_listener;
+ struct wl_listener output_created_listener;
+};
+
+static void
+cms_output_created(struct cms_static *cms, struct weston_output *o)
+{
+ struct weston_color_profile *p;
+ struct weston_config_section *s;
+ char *profile;
+
+ weston_log("cms-static: output %i [%s] created\n", o->id, o->name);
+
+ if (o->name == NULL)
+ return;
+ s = weston_config_get_section(cms->ec->config,
+ "output", "name", o->name);
+ if (s == NULL)
+ return;
+ if (weston_config_section_get_string(s, "icc_profile", &profile, NULL) < 0)
+ return;
+ p = weston_cms_load_profile(profile);
+ if (p == NULL) {
+ weston_log("cms-static: failed to load %s\n", profile);
+ } else {
+ weston_log("cms-static: loading %s for %s\n",
+ profile, o->name);
+ weston_cms_set_color_profile(o, p);
+ }
+}
+
+static void
+cms_notifier_output_created(struct wl_listener *listener, void *data)
+{
+ struct weston_output *o = (struct weston_output *) data;
+ struct cms_static *cms =
+ container_of(listener, struct cms_static, output_created_listener);
+ cms_output_created(cms, o);
+}
+
+static void
+cms_module_destroy(struct cms_static *cms)
+{
+ free(cms);
+}
+
+static void
+cms_notifier_destroy(struct wl_listener *listener, void *data)
+{
+ struct cms_static *cms = container_of(listener, struct cms_static, destroy_listener);
+ cms_module_destroy(cms);
+}
+
+
+WL_EXPORT int
+module_init(struct weston_compositor *ec,
+ int *argc, char *argv[])
+{
+ struct cms_static *cms;
+ struct weston_output *output;
+
+ weston_log("cms-static: initialized\n");
+
+ /* create local state object */
+ cms = zalloc(sizeof *cms);
+ if (cms == NULL)
+ return -1;
+
+ cms->ec = ec;
+ cms->destroy_listener.notify = cms_notifier_destroy;
+ wl_signal_add(&ec->destroy_signal, &cms->destroy_listener);
+
+ cms->output_created_listener.notify = cms_notifier_output_created;
+ wl_signal_add(&ec->output_created_signal, &cms->output_created_listener);
+
+ /* discover outputs */
+ wl_list_for_each(output, &ec->output_list, link)
+ cms_output_created(cms, output);
+
+ return 0;
+}
diff --git a/src/compositor-drm.c b/src/compositor-drm.c
new file mode 100644
index 00000000..4f015d11
--- /dev/null
+++ b/src/compositor-drm.c
@@ -0,0 +1,2736 @@
+/*
+ * Copyright © 2008-2011 Kristian Høgsberg
+ * Copyright © 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 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.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <linux/input.h>
+#include <linux/vt.h>
+#include <assert.h>
+#include <sys/mman.h>
+#include <time.h>
+
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+#include <drm_fourcc.h>
+
+#include <gbm.h>
+#include <libbacklight.h>
+#include <libudev.h>
+
+#include "compositor.h"
+#include "gl-renderer.h"
+#include "pixman-renderer.h"
+#include "udev-seat.h"
+#include "launcher-util.h"
+#include "vaapi-recorder.h"
+
+#ifndef DRM_CAP_TIMESTAMP_MONOTONIC
+#define DRM_CAP_TIMESTAMP_MONOTONIC 0x6
+#endif
+
+static int option_current_mode = 0;
+
+enum output_config {
+ OUTPUT_CONFIG_INVALID = 0,
+ OUTPUT_CONFIG_OFF,
+ OUTPUT_CONFIG_PREFERRED,
+ OUTPUT_CONFIG_CURRENT,
+ OUTPUT_CONFIG_MODE,
+ OUTPUT_CONFIG_MODELINE
+};
+
+struct drm_compositor {
+ struct weston_compositor base;
+
+ struct udev *udev;
+ struct wl_event_source *drm_source;
+
+ struct udev_monitor *udev_monitor;
+ struct wl_event_source *udev_drm_source;
+
+ struct {
+ int id;
+ int fd;
+ char *filename;
+ } drm;
+ struct gbm_device *gbm;
+ uint32_t *crtcs;
+ int num_crtcs;
+ uint32_t crtc_allocator;
+ uint32_t connector_allocator;
+ struct wl_listener session_listener;
+ uint32_t format;
+
+ /* we need these parameters in order to not fail drmModeAddFB2()
+ * due to out of bounds dimensions, and then mistakenly set
+ * sprites_are_broken:
+ */
+ uint32_t min_width, max_width;
+ uint32_t min_height, max_height;
+ int no_addfb2;
+
+ struct wl_list sprite_list;
+ int sprites_are_broken;
+ int sprites_hidden;
+
+ int cursors_are_broken;
+
+ int use_pixman;
+
+ uint32_t prev_state;
+
+ clockid_t clock;
+ struct udev_input input;
+};
+
+struct drm_mode {
+ struct weston_mode base;
+ drmModeModeInfo mode_info;
+};
+
+struct drm_output;
+
+struct drm_fb {
+ struct drm_output *output;
+ uint32_t fb_id, stride, handle, size;
+ int fd;
+ int is_client_buffer;
+ struct weston_buffer_reference buffer_ref;
+
+ /* Used by gbm fbs */
+ struct gbm_bo *bo;
+
+ /* Used by dumb fbs */
+ void *map;
+};
+
+struct drm_edid {
+ char eisa_id[13];
+ char monitor_name[13];
+ char pnp_id[5];
+ char serial_number[13];
+};
+
+struct drm_output {
+ struct weston_output base;
+
+ uint32_t crtc_id;
+ int pipe;
+ uint32_t connector_id;
+ drmModeCrtcPtr original_crtc;
+ struct drm_edid edid;
+ drmModePropertyPtr dpms_prop;
+
+ int vblank_pending;
+ int page_flip_pending;
+ int destroy_pending;
+
+ struct gbm_surface *surface;
+ struct gbm_bo *cursor_bo[2];
+ struct weston_plane cursor_plane;
+ struct weston_plane fb_plane;
+ struct weston_surface *cursor_surface;
+ int current_cursor;
+ struct drm_fb *current, *next;
+ struct backlight *backlight;
+
+ struct drm_fb *dumb[2];
+ pixman_image_t *image[2];
+ int current_image;
+ pixman_region32_t previous_damage;
+
+ struct vaapi_recorder *recorder;
+ struct wl_listener recorder_frame_listener;
+};
+
+/*
+ * An output has a primary display plane plus zero or more sprites for
+ * blending display contents.
+ */
+struct drm_sprite {
+ struct wl_list link;
+
+ struct weston_plane plane;
+
+ struct drm_fb *current, *next;
+ struct drm_output *output;
+ struct drm_compositor *compositor;
+
+ uint32_t possible_crtcs;
+ uint32_t plane_id;
+ uint32_t count_formats;
+
+ int32_t src_x, src_y;
+ uint32_t src_w, src_h;
+ uint32_t dest_x, dest_y;
+ uint32_t dest_w, dest_h;
+
+ uint32_t formats[];
+};
+
+static const char default_seat[] = "seat0";
+
+static void
+drm_output_set_cursor(struct drm_output *output);
+
+static int
+drm_sprite_crtc_supported(struct weston_output *output_base, uint32_t supported)
+{
+ struct weston_compositor *ec = output_base->compositor;
+ struct drm_compositor *c =(struct drm_compositor *) ec;
+ struct drm_output *output = (struct drm_output *) output_base;
+ int crtc;
+
+ for (crtc = 0; crtc < c->num_crtcs; crtc++) {
+ if (c->crtcs[crtc] != output->crtc_id)
+ continue;
+
+ if (supported & (1 << crtc))
+ return -1;
+ }
+
+ return 0;
+}
+
+static void
+drm_fb_destroy_callback(struct gbm_bo *bo, void *data)
+{
+ struct drm_fb *fb = data;
+ struct gbm_device *gbm = gbm_bo_get_device(bo);
+
+ if (fb->fb_id)
+ drmModeRmFB(gbm_device_get_fd(gbm), fb->fb_id);
+
+ weston_buffer_reference(&fb->buffer_ref, NULL);
+
+ free(data);
+}
+
+static struct drm_fb *
+drm_fb_create_dumb(struct drm_compositor *ec, unsigned width, unsigned height)
+{
+ struct drm_fb *fb;
+ int ret;
+
+ struct drm_mode_create_dumb create_arg;
+ struct drm_mode_destroy_dumb destroy_arg;
+ struct drm_mode_map_dumb map_arg;
+
+ fb = zalloc(sizeof *fb);
+ if (!fb)
+ return NULL;
+
+ memset(&create_arg, 0, sizeof create_arg);
+ create_arg.bpp = 32;
+ create_arg.width = width;
+ create_arg.height = height;
+
+ ret = drmIoctl(ec->drm.fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_arg);
+ if (ret)
+ goto err_fb;
+
+ fb->handle = create_arg.handle;
+ fb->stride = create_arg.pitch;
+ fb->size = create_arg.size;
+ fb->fd = ec->drm.fd;
+
+ ret = drmModeAddFB(ec->drm.fd, width, height, 24, 32,
+ fb->stride, fb->handle, &fb->fb_id);
+ if (ret)
+ goto err_bo;
+
+ memset(&map_arg, 0, sizeof map_arg);
+ map_arg.handle = fb->handle;
+ ret = drmIoctl(fb->fd, DRM_IOCTL_MODE_MAP_DUMB, &map_arg);
+ if (ret)
+ goto err_add_fb;
+
+ fb->map = mmap(0, fb->size, PROT_WRITE,
+ MAP_SHARED, ec->drm.fd, map_arg.offset);
+ if (fb->map == MAP_FAILED)
+ goto err_add_fb;
+
+ return fb;
+
+err_add_fb:
+ drmModeRmFB(ec->drm.fd, fb->fb_id);
+err_bo:
+ memset(&destroy_arg, 0, sizeof(destroy_arg));
+ destroy_arg.handle = create_arg.handle;
+ drmIoctl(ec->drm.fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_arg);
+err_fb:
+ free(fb);
+ return NULL;
+}
+
+static void
+drm_fb_destroy_dumb(struct drm_fb *fb)
+{
+ struct drm_mode_destroy_dumb destroy_arg;
+
+ if (!fb->map)
+ return;
+
+ if (fb->fb_id)
+ drmModeRmFB(fb->fd, fb->fb_id);
+
+ weston_buffer_reference(&fb->buffer_ref, NULL);
+
+ munmap(fb->map, fb->size);
+
+ memset(&destroy_arg, 0, sizeof(destroy_arg));
+ destroy_arg.handle = fb->handle;
+ drmIoctl(fb->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_arg);
+
+ free(fb);
+}
+
+static struct drm_fb *
+drm_fb_get_from_bo(struct gbm_bo *bo,
+ struct drm_compositor *compositor, uint32_t format)
+{
+ struct drm_fb *fb = gbm_bo_get_user_data(bo);
+ uint32_t width, height;
+ uint32_t handles[4], pitches[4], offsets[4];
+ int ret;
+
+ if (fb)
+ return fb;
+
+ fb = calloc(1, sizeof *fb);
+ if (!fb)
+ return NULL;
+
+ fb->bo = bo;
+
+ width = gbm_bo_get_width(bo);
+ height = gbm_bo_get_height(bo);
+ fb->stride = gbm_bo_get_stride(bo);
+ fb->handle = gbm_bo_get_handle(bo).u32;
+ fb->size = fb->stride * height;
+ fb->fd = compositor->drm.fd;
+
+ if (compositor->min_width > width || width > compositor->max_width ||
+ compositor->min_height > height ||
+ height > compositor->max_height) {
+ weston_log("bo geometry out of bounds\n");
+ goto err_free;
+ }
+
+ ret = -1;
+
+ if (format && !compositor->no_addfb2) {
+ handles[0] = fb->handle;
+ pitches[0] = fb->stride;
+ offsets[0] = 0;
+
+ ret = drmModeAddFB2(compositor->drm.fd, width, height,
+ format, handles, pitches, offsets,
+ &fb->fb_id, 0);
+ if (ret) {
+ weston_log("addfb2 failed: %m\n");
+ compositor->no_addfb2 = 1;
+ compositor->sprites_are_broken = 1;
+ }
+ }
+
+ if (ret)
+ ret = drmModeAddFB(compositor->drm.fd, width, height, 24, 32,
+ fb->stride, fb->handle, &fb->fb_id);
+
+ if (ret) {
+ weston_log("failed to create kms fb: %m\n");
+ goto err_free;
+ }
+
+ gbm_bo_set_user_data(bo, fb, drm_fb_destroy_callback);
+
+ return fb;
+
+err_free:
+ free(fb);
+ return NULL;
+}
+
+static void
+drm_fb_set_buffer(struct drm_fb *fb, struct weston_buffer *buffer)
+{
+ assert(fb->buffer_ref.buffer == NULL);
+
+ fb->is_client_buffer = 1;
+
+ weston_buffer_reference(&fb->buffer_ref, buffer);
+}
+
+static void
+drm_output_release_fb(struct drm_output *output, struct drm_fb *fb)
+{
+ if (!fb)
+ return;
+
+ if (fb->map &&
+ (fb != output->dumb[0] && fb != output->dumb[1])) {
+ drm_fb_destroy_dumb(fb);
+ } else if (fb->bo) {
+ if (fb->is_client_buffer)
+ gbm_bo_destroy(fb->bo);
+ else
+ gbm_surface_release_buffer(output->surface,
+ output->current->bo);
+ }
+}
+
+static uint32_t
+drm_output_check_scanout_format(struct drm_output *output,
+ struct weston_surface *es, struct gbm_bo *bo)
+{
+ uint32_t format;
+ pixman_region32_t r;
+
+ format = gbm_bo_get_format(bo);
+
+ switch (format) {
+ case GBM_FORMAT_XRGB8888:
+ return format;
+ case GBM_FORMAT_ARGB8888:
+ /* We can only scanout an ARGB buffer if the surface's
+ * opaque region covers the whole output */
+ pixman_region32_init(&r);
+ pixman_region32_subtract(&r, &output->base.region,
+ &es->opaque);
+
+ if (!pixman_region32_not_empty(&r))
+ format = GBM_FORMAT_XRGB8888;
+ else
+ format = 0;
+
+ pixman_region32_fini(&r);
+
+ return format;
+ default:
+ return 0;
+ }
+}
+
+static struct weston_plane *
+drm_output_prepare_scanout_surface(struct weston_output *_output,
+ struct weston_surface *es)
+{
+ struct drm_output *output = (struct drm_output *) _output;
+ struct drm_compositor *c =
+ (struct drm_compositor *) output->base.compositor;
+ struct weston_buffer *buffer = es->buffer_ref.buffer;
+ struct gbm_bo *bo;
+ uint32_t format;
+
+ if (es->geometry.x != output->base.x ||
+ es->geometry.y != output->base.y ||
+ buffer == NULL || c->gbm == NULL ||
+ buffer->width != output->base.current_mode->width ||
+ buffer->height != output->base.current_mode->height ||
+ output->base.transform != es->buffer_transform ||
+ es->transform.enabled)
+ return NULL;
+
+ bo = gbm_bo_import(c->gbm, GBM_BO_IMPORT_WL_BUFFER,
+ buffer->resource, GBM_BO_USE_SCANOUT);
+
+ /* Unable to use the buffer for scanout */
+ if (!bo)
+ return NULL;
+
+ format = drm_output_check_scanout_format(output, es, bo);
+ if (format == 0) {
+ gbm_bo_destroy(bo);
+ return NULL;
+ }
+
+ output->next = drm_fb_get_from_bo(bo, c, format);
+ if (!output->next) {
+ gbm_bo_destroy(bo);
+ return NULL;
+ }
+
+ drm_fb_set_buffer(output->next, buffer);
+
+ return &output->fb_plane;
+}
+
+static void
+drm_output_render_gl(struct drm_output *output, pixman_region32_t *damage)
+{
+ struct drm_compositor *c =
+ (struct drm_compositor *) output->base.compositor;
+ struct gbm_bo *bo;
+
+ c->base.renderer->repaint_output(&output->base, damage);
+
+ bo = gbm_surface_lock_front_buffer(output->surface);
+ if (!bo) {
+ weston_log("failed to lock front buffer: %m\n");
+ return;
+ }
+
+ output->next = drm_fb_get_from_bo(bo, c, c->format);
+ if (!output->next) {
+ weston_log("failed to get drm_fb for bo\n");
+ gbm_surface_release_buffer(output->surface, bo);
+ return;
+ }
+}
+
+static void
+drm_output_render_pixman(struct drm_output *output, pixman_region32_t *damage)
+{
+ struct weston_compositor *ec = output->base.compositor;
+ pixman_region32_t total_damage, previous_damage;
+
+ pixman_region32_init(&total_damage);
+ pixman_region32_init(&previous_damage);
+
+ pixman_region32_copy(&previous_damage, damage);
+
+ pixman_region32_union(&total_damage, damage, &output->previous_damage);
+ pixman_region32_copy(&output->previous_damage, &previous_damage);
+
+ output->current_image ^= 1;
+
+ output->next = output->dumb[output->current_image];
+ pixman_renderer_output_set_buffer(&output->base,
+ output->image[output->current_image]);
+
+ ec->renderer->repaint_output(&output->base, &total_damage);
+
+ pixman_region32_fini(&total_damage);
+ pixman_region32_fini(&previous_damage);
+}
+
+static void
+drm_output_render(struct drm_output *output, pixman_region32_t *damage)
+{
+ struct drm_compositor *c =
+ (struct drm_compositor *) output->base.compositor;
+
+ if (c->use_pixman)
+ drm_output_render_pixman(output, damage);
+ else
+ drm_output_render_gl(output, damage);
+
+ pixman_region32_subtract(&c->base.primary_plane.damage,
+ &c->base.primary_plane.damage, damage);
+}
+
+static void
+drm_output_set_gamma(struct weston_output *output_base,
+ uint16_t size, uint16_t *r, uint16_t *g, uint16_t *b)
+{
+ int rc;
+ struct drm_output *output = (struct drm_output *) output_base;
+ struct drm_compositor *compositor = (struct drm_compositor *) output->base.compositor;
+
+ /* check */
+ if (output_base->gamma_size != size)
+ return;
+ if (!output->original_crtc)
+ return;
+
+ rc = drmModeCrtcSetGamma(compositor->drm.fd,
+ output->crtc_id,
+ size, r, g, b);
+ if (rc)
+ weston_log("set gamma failed: %m\n");
+}
+
+static int
+drm_output_repaint(struct weston_output *output_base,
+ pixman_region32_t *damage)
+{
+ struct drm_output *output = (struct drm_output *) output_base;
+ struct drm_compositor *compositor =
+ (struct drm_compositor *) output->base.compositor;
+ struct drm_sprite *s;
+ struct drm_mode *mode;
+ int ret = 0;
+
+ if (output->destroy_pending)
+ return -1;
+
+ if (!output->next)
+ drm_output_render(output, damage);
+ if (!output->next)
+ return -1;
+
+ mode = container_of(output->base.current_mode, struct drm_mode, base);
+ if (!output->current) {
+ ret = drmModeSetCrtc(compositor->drm.fd, output->crtc_id,
+ output->next->fb_id, 0, 0,
+ &output->connector_id, 1,
+ &mode->mode_info);
+ if (ret) {
+ weston_log("set mode failed: %m\n");
+ goto err_pageflip;
+ }
+ output_base->set_dpms(output_base, WESTON_DPMS_ON);
+ }
+
+ if (drmModePageFlip(compositor->drm.fd, output->crtc_id,
+ output->next->fb_id,
+ DRM_MODE_PAGE_FLIP_EVENT, output) < 0) {
+ weston_log("queueing pageflip failed: %m\n");
+ goto err_pageflip;
+ }
+
+ output->page_flip_pending = 1;
+
+ drm_output_set_cursor(output);
+
+ /*
+ * Now, update all the sprite surfaces
+ */
+ wl_list_for_each(s, &compositor->sprite_list, link) {
+ uint32_t flags = 0, fb_id = 0;
+ drmVBlank vbl = {
+ .request.type = DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT,
+ .request.sequence = 1,
+ };
+
+ if ((!s->current && !s->next) ||
+ !drm_sprite_crtc_supported(output_base, s->possible_crtcs))
+ continue;
+
+ if (s->next && !compositor->sprites_hidden)
+ fb_id = s->next->fb_id;
+
+ ret = drmModeSetPlane(compositor->drm.fd, s->plane_id,
+ output->crtc_id, fb_id, flags,
+ s->dest_x, s->dest_y,
+ s->dest_w, s->dest_h,
+ s->src_x, s->src_y,
+ s->src_w, s->src_h);
+ if (ret)
+ weston_log("setplane failed: %d: %s\n",
+ ret, strerror(errno));
+
+ if (output->pipe > 0)
+ vbl.request.type |= DRM_VBLANK_SECONDARY;
+
+ /*
+ * Queue a vblank signal so we know when the surface
+ * becomes active on the display or has been replaced.
+ */
+ vbl.request.signal = (unsigned long)s;
+ ret = drmWaitVBlank(compositor->drm.fd, &vbl);
+ if (ret) {
+ weston_log("vblank event request failed: %d: %s\n",
+ ret, strerror(errno));
+ }
+
+ s->output = output;
+ output->vblank_pending = 1;
+ }
+
+ return 0;
+
+err_pageflip:
+ if (output->next) {
+ drm_output_release_fb(output, output->next);
+ output->next = NULL;
+ }
+
+ return -1;
+}
+
+static void
+drm_output_start_repaint_loop(struct weston_output *output_base)
+{
+ struct drm_output *output = (struct drm_output *) output_base;
+ struct drm_compositor *compositor = (struct drm_compositor *)
+ output_base->compositor;
+ uint32_t fb_id;
+ uint32_t msec;
+ struct timespec ts;
+
+ if (output->destroy_pending)
+ return;
+
+ if (!output->current) {
+ /* We can't page flip if there's no mode set */
+ goto finish_frame;
+ }
+
+ fb_id = output->current->fb_id;
+
+ if (drmModePageFlip(compositor->drm.fd, output->crtc_id, fb_id,
+ DRM_MODE_PAGE_FLIP_EVENT, output) < 0) {
+ weston_log("queueing pageflip failed: %m\n");
+ goto finish_frame;
+ }
+
+ return;
+
+finish_frame:
+ /* if we cannot page-flip, immediately finish frame */
+ clock_gettime(compositor->clock, &ts);
+ msec = ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
+ weston_output_finish_frame(output_base, msec);
+}
+
+static void
+vblank_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec,
+ void *data)
+{
+ struct drm_sprite *s = (struct drm_sprite *)data;
+ struct drm_output *output = s->output;
+ uint32_t msecs;
+
+ output->vblank_pending = 0;
+
+ drm_output_release_fb(output, s->current);
+ s->current = s->next;
+ s->next = NULL;
+
+ if (!output->page_flip_pending) {
+ msecs = sec * 1000 + usec / 1000;
+ weston_output_finish_frame(&output->base, msecs);
+ }
+}
+
+static void
+drm_output_destroy(struct weston_output *output_base);
+
+static void
+page_flip_handler(int fd, unsigned int frame,
+ unsigned int sec, unsigned int usec, void *data)
+{
+ struct drm_output *output = (struct drm_output *) data;
+ uint32_t msecs;
+
+ /* We don't set page_flip_pending on start_repaint_loop, in that case
+ * we just want to page flip to the current buffer to get an accurate
+ * timestamp */
+ if (output->page_flip_pending) {
+ drm_output_release_fb(output, output->current);
+ output->current = output->next;
+ output->next = NULL;
+ }
+
+ output->page_flip_pending = 0;
+
+ if (output->destroy_pending)
+ drm_output_destroy(&output->base);
+ else if (!output->vblank_pending) {
+ msecs = sec * 1000 + usec / 1000;
+ weston_output_finish_frame(&output->base, msecs);
+
+ /* We can't call this from frame_notify, because the output's
+ * repaint needed flag is cleared just after that */
+ if (output->recorder)
+ weston_output_schedule_repaint(&output->base);
+ }
+}
+
+static uint32_t
+drm_output_check_sprite_format(struct drm_sprite *s,
+ struct weston_surface *es, struct gbm_bo *bo)
+{
+ uint32_t i, format;
+
+ format = gbm_bo_get_format(bo);
+
+ if (format == GBM_FORMAT_ARGB8888) {
+ pixman_region32_t r;
+
+ pixman_region32_init_rect(&r, 0, 0,
+ es->geometry.width,
+ es->geometry.height);
+ pixman_region32_subtract(&r, &r, &es->opaque);
+
+ if (!pixman_region32_not_empty(&r))
+ format = GBM_FORMAT_XRGB8888;
+
+ pixman_region32_fini(&r);
+ }
+
+ for (i = 0; i < s->count_formats; i++)
+ if (s->formats[i] == format)
+ return format;
+
+ return 0;
+}
+
+static int
+drm_surface_transform_supported(struct weston_surface *es)
+{
+ return !es->transform.enabled ||
+ (es->transform.matrix.type < WESTON_MATRIX_TRANSFORM_ROTATE);
+}
+
+static struct weston_plane *
+drm_output_prepare_overlay_surface(struct weston_output *output_base,
+ struct weston_surface *es)
+{
+ struct weston_compositor *ec = output_base->compositor;
+ struct drm_compositor *c =(struct drm_compositor *) ec;
+ struct drm_sprite *s;
+ int found = 0;
+ struct gbm_bo *bo;
+ pixman_region32_t dest_rect, src_rect;
+ pixman_box32_t *box, tbox;
+ uint32_t format;
+ wl_fixed_t sx1, sy1, sx2, sy2;
+
+ if (c->gbm == NULL)
+ return NULL;
+
+ if (es->buffer_transform != output_base->transform)
+ return NULL;
+
+ if (es->buffer_scale != output_base->current_scale)
+ return NULL;
+
+ if (c->sprites_are_broken)
+ return NULL;
+
+ if (es->output_mask != (1u << output_base->id))
+ return NULL;
+
+ if (es->buffer_ref.buffer == NULL)
+ return NULL;
+
+ if (es->alpha != 1.0f)
+ return NULL;
+
+ if (wl_shm_buffer_get(es->buffer_ref.buffer->resource))
+ return NULL;
+
+ if (!drm_surface_transform_supported(es))
+ return NULL;
+
+ wl_list_for_each(s, &c->sprite_list, link) {
+ if (!drm_sprite_crtc_supported(output_base, s->possible_crtcs))
+ continue;
+
+ if (!s->next) {
+ found = 1;
+ break;
+ }
+ }
+
+ /* No sprites available */
+ if (!found)
+ return NULL;
+
+ bo = gbm_bo_import(c->gbm, GBM_BO_IMPORT_WL_BUFFER,
+ es->buffer_ref.buffer->resource,
+ GBM_BO_USE_SCANOUT);
+ if (!bo)
+ return NULL;
+
+ format = drm_output_check_sprite_format(s, es, bo);
+ if (format == 0) {
+ gbm_bo_destroy(bo);
+ return NULL;
+ }
+
+ s->next = drm_fb_get_from_bo(bo, c, format);
+ if (!s->next) {
+ gbm_bo_destroy(bo);
+ return NULL;
+ }
+
+ drm_fb_set_buffer(s->next, es->buffer_ref.buffer);
+
+ box = pixman_region32_extents(&es->transform.boundingbox);
+ s->plane.x = box->x1;
+ s->plane.y = box->y1;
+
+ /*
+ * Calculate the source & dest rects properly based on actual
+ * position (note the caller has called weston_surface_update_transform()
+ * for us already).
+ */
+ pixman_region32_init(&dest_rect);
+ pixman_region32_intersect(&dest_rect, &es->transform.boundingbox,
+ &output_base->region);
+ pixman_region32_translate(&dest_rect, -output_base->x, -output_base->y);
+ box = pixman_region32_extents(&dest_rect);
+ tbox = weston_transformed_rect(output_base->width,
+ output_base->height,
+ output_base->transform,
+ output_base->current_scale,
+ *box);
+ s->dest_x = tbox.x1;
+ s->dest_y = tbox.y1;
+ s->dest_w = tbox.x2 - tbox.x1;
+ s->dest_h = tbox.y2 - tbox.y1;
+ pixman_region32_fini(&dest_rect);
+
+ pixman_region32_init(&src_rect);
+ pixman_region32_intersect(&src_rect, &es->transform.boundingbox,
+ &output_base->region);
+ box = pixman_region32_extents(&src_rect);
+
+ weston_surface_from_global_fixed(es,
+ wl_fixed_from_int(box->x1),
+ wl_fixed_from_int(box->y1),
+ &sx1, &sy1);
+ weston_surface_from_global_fixed(es,
+ wl_fixed_from_int(box->x2),
+ wl_fixed_from_int(box->y2),
+ &sx2, &sy2);
+
+ if (sx1 < 0)
+ sx1 = 0;
+ if (sy1 < 0)
+ sy1 = 0;
+ if (sx2 > wl_fixed_from_int(es->geometry.width))
+ sx2 = wl_fixed_from_int(es->geometry.width);
+ if (sy2 > wl_fixed_from_int(es->geometry.height))
+ sy2 = wl_fixed_from_int(es->geometry.height);
+
+ tbox.x1 = sx1;
+ tbox.y1 = sy1;
+ tbox.x2 = sx2;
+ tbox.y2 = sy2;
+
+ tbox = weston_transformed_rect(wl_fixed_from_int(es->geometry.width),
+ wl_fixed_from_int(es->geometry.height),
+ es->buffer_transform, es->buffer_scale, tbox);
+
+ s->src_x = tbox.x1 << 8;
+ s->src_y = tbox.y1 << 8;
+ s->src_w = (tbox.x2 - tbox.x1) << 8;
+ s->src_h = (tbox.y2 - tbox.y1) << 8;
+ pixman_region32_fini(&src_rect);
+
+ return &s->plane;
+}
+
+static struct weston_plane *
+drm_output_prepare_cursor_surface(struct weston_output *output_base,
+ struct weston_surface *es)
+{
+ struct drm_compositor *c =
+ (struct drm_compositor *) output_base->compositor;
+ struct drm_output *output = (struct drm_output *) output_base;
+
+ if (c->gbm == NULL)
+ return NULL;
+ if (output->base.transform != WL_OUTPUT_TRANSFORM_NORMAL)
+ return NULL;
+ if (output->cursor_surface)
+ return NULL;
+ if (es->output_mask != (1u << output_base->id))
+ return NULL;
+ if (c->cursors_are_broken)
+ return NULL;
+ if (es->buffer_ref.buffer == NULL ||
+ !wl_shm_buffer_get(es->buffer_ref.buffer->resource) ||
+ es->geometry.width > 64 || es->geometry.height > 64)
+ return NULL;
+
+ output->cursor_surface = es;
+
+ return &output->cursor_plane;
+}
+
+static void
+drm_output_set_cursor(struct drm_output *output)
+{
+ struct weston_surface *es = output->cursor_surface;
+ struct drm_compositor *c =
+ (struct drm_compositor *) output->base.compositor;
+ EGLint handle, stride;
+ struct gbm_bo *bo;
+ uint32_t buf[64 * 64];
+ unsigned char *s;
+ int i, x, y;
+
+ output->cursor_surface = NULL;
+ if (es == NULL) {
+ drmModeSetCursor(c->drm.fd, output->crtc_id, 0, 0, 0);
+ return;
+ }
+
+ if (es->buffer_ref.buffer &&
+ pixman_region32_not_empty(&output->cursor_plane.damage)) {
+ pixman_region32_fini(&output->cursor_plane.damage);
+ pixman_region32_init(&output->cursor_plane.damage);
+ output->current_cursor ^= 1;
+ bo = output->cursor_bo[output->current_cursor];
+ memset(buf, 0, sizeof buf);
+ stride = wl_shm_buffer_get_stride(es->buffer_ref.buffer->shm_buffer);
+ s = wl_shm_buffer_get_data(es->buffer_ref.buffer->shm_buffer);
+ for (i = 0; i < es->geometry.height; i++)
+ memcpy(buf + i * 64, s + i * stride,
+ es->geometry.width * 4);
+
+ if (gbm_bo_write(bo, buf, sizeof buf) < 0)
+ weston_log("failed update cursor: %m\n");
+
+ handle = gbm_bo_get_handle(bo).s32;
+ if (drmModeSetCursor(c->drm.fd,
+ output->crtc_id, handle, 64, 64)) {
+ weston_log("failed to set cursor: %m\n");
+ c->cursors_are_broken = 1;
+ }
+ }
+
+ x = (es->geometry.x - output->base.x) * output->base.current_scale;
+ y = (es->geometry.y - output->base.y) * output->base.current_scale;
+ if (output->cursor_plane.x != x || output->cursor_plane.y != y) {
+ if (drmModeMoveCursor(c->drm.fd, output->crtc_id, x, y)) {
+ weston_log("failed to move cursor: %m\n");
+ c->cursors_are_broken = 1;
+ }
+
+ output->cursor_plane.x = x;
+ output->cursor_plane.y = y;
+ }
+}
+
+static void
+drm_assign_planes(struct weston_output *output)
+{
+ struct drm_compositor *c =
+ (struct drm_compositor *) output->compositor;
+ struct weston_surface *es, *next;
+ pixman_region32_t overlap, surface_overlap;
+ struct weston_plane *primary, *next_plane;
+
+ /*
+ * Find a surface for each sprite in the output using some heuristics:
+ * 1) size
+ * 2) frequency of update
+ * 3) opacity (though some hw might support alpha blending)
+ * 4) clipping (this can be fixed with color keys)
+ *
+ * The idea is to save on blitting since this should save power.
+ * If we can get a large video surface on the sprite for example,
+ * the main display surface may not need to update at all, and
+ * the client buffer can be used directly for the sprite surface
+ * as we do for flipping full screen surfaces.
+ */
+ pixman_region32_init(&overlap);
+ primary = &c->base.primary_plane;
+ wl_list_for_each_safe(es, next, &c->base.surface_list, link) {
+ /* test whether this buffer can ever go into a plane:
+ * non-shm, or small enough to be a cursor
+ */
+ if ((es->buffer_ref.buffer &&
+ !wl_shm_buffer_get(es->buffer_ref.buffer->resource)) ||
+ (es->geometry.width <= 64 && es->geometry.height <= 64))
+ es->keep_buffer = 1;
+ else
+ es->keep_buffer = 0;
+
+ pixman_region32_init(&surface_overlap);
+ pixman_region32_intersect(&surface_overlap, &overlap,
+ &es->transform.boundingbox);
+
+ next_plane = NULL;
+ if (pixman_region32_not_empty(&surface_overlap))
+ next_plane = primary;
+ if (next_plane == NULL)
+ next_plane = drm_output_prepare_cursor_surface(output, es);
+ if (next_plane == NULL)
+ next_plane = drm_output_prepare_scanout_surface(output, es);
+ if (next_plane == NULL)
+ next_plane = drm_output_prepare_overlay_surface(output, es);
+ if (next_plane == NULL)
+ next_plane = primary;
+ weston_surface_move_to_plane(es, next_plane);
+ if (next_plane == primary)
+ pixman_region32_union(&overlap, &overlap,
+ &es->transform.boundingbox);
+
+ pixman_region32_fini(&surface_overlap);
+ }
+ pixman_region32_fini(&overlap);
+}
+
+static void
+drm_output_fini_pixman(struct drm_output *output);
+
+static void
+drm_output_destroy(struct weston_output *output_base)
+{
+ struct drm_output *output = (struct drm_output *) output_base;
+ struct drm_compositor *c =
+ (struct drm_compositor *) output->base.compositor;
+ drmModeCrtcPtr origcrtc = output->original_crtc;
+
+ if (output->page_flip_pending) {
+ output->destroy_pending = 1;
+ weston_log("destroy output while page flip pending\n");
+ return;
+ }
+
+ if (output->backlight)
+ backlight_destroy(output->backlight);
+
+ drmModeFreeProperty(output->dpms_prop);
+
+ /* Turn off hardware cursor */
+ drmModeSetCursor(c->drm.fd, output->crtc_id, 0, 0, 0);
+
+ /* Restore original CRTC state */
+ drmModeSetCrtc(c->drm.fd, origcrtc->crtc_id, origcrtc->buffer_id,
+ origcrtc->x, origcrtc->y,
+ &output->connector_id, 1, &origcrtc->mode);
+ drmModeFreeCrtc(origcrtc);
+
+ c->crtc_allocator &= ~(1 << output->crtc_id);
+ c->connector_allocator &= ~(1 << output->connector_id);
+
+ if (c->use_pixman) {
+ drm_output_fini_pixman(output);
+ } else {
+ gl_renderer_output_destroy(output_base);
+ gbm_surface_destroy(output->surface);
+ }
+
+ weston_plane_release(&output->fb_plane);
+ weston_plane_release(&output->cursor_plane);
+
+ weston_output_destroy(&output->base);
+ wl_list_remove(&output->base.link);
+
+ free(output);
+}
+
+static struct drm_mode *
+choose_mode (struct drm_output *output, struct weston_mode *target_mode)
+{
+ struct drm_mode *tmp_mode = NULL, *mode;
+
+ if (output->base.current_mode->width == target_mode->width &&
+ output->base.current_mode->height == target_mode->height &&
+ (output->base.current_mode->refresh == target_mode->refresh ||
+ target_mode->refresh == 0))
+ return (struct drm_mode *)output->base.current_mode;
+
+ wl_list_for_each(mode, &output->base.mode_list, base.link) {
+ if (mode->mode_info.hdisplay == target_mode->width &&
+ mode->mode_info.vdisplay == target_mode->height) {
+ if (mode->mode_info.vrefresh == target_mode->refresh ||
+ target_mode->refresh == 0) {
+ return mode;
+ } else if (!tmp_mode)
+ tmp_mode = mode;
+ }
+ }
+
+ return tmp_mode;
+}
+
+static int
+drm_output_init_egl(struct drm_output *output, struct drm_compositor *ec);
+static int
+drm_output_init_pixman(struct drm_output *output, struct drm_compositor *c);
+
+static int
+drm_output_switch_mode(struct weston_output *output_base, struct weston_mode *mode)
+{
+ struct drm_output *output;
+ struct drm_mode *drm_mode;
+ struct drm_compositor *ec;
+
+ if (output_base == NULL) {
+ weston_log("output is NULL.\n");
+ return -1;
+ }
+
+ if (mode == NULL) {
+ weston_log("mode is NULL.\n");
+ return -1;
+ }
+
+ ec = (struct drm_compositor *)output_base->compositor;
+ output = (struct drm_output *)output_base;
+ drm_mode = choose_mode (output, mode);
+
+ if (!drm_mode) {
+ weston_log("%s, invalid resolution:%dx%d\n", __func__, mode->width, mode->height);
+ return -1;
+ }
+
+ if (&drm_mode->base == output->base.current_mode)
+ return 0;
+
+ output->base.current_mode->flags = 0;
+
+ output->base.current_mode = &drm_mode->base;
+ output->base.current_mode->flags =
+ WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
+
+ /* reset rendering stuff. */
+ drm_output_release_fb(output, output->current);
+ drm_output_release_fb(output, output->next);
+ output->current = output->next = NULL;
+
+ if (ec->use_pixman) {
+ drm_output_fini_pixman(output);
+ if (drm_output_init_pixman(output, ec) < 0) {
+ weston_log("failed to init output pixman state with "
+ "new mode\n");
+ return -1;
+ }
+ } else {
+ gl_renderer_output_destroy(&output->base);
+ gbm_surface_destroy(output->surface);
+
+ if (drm_output_init_egl(output, ec) < 0) {
+ weston_log("failed to init output egl state with "
+ "new mode");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int
+on_drm_input(int fd, uint32_t mask, void *data)
+{
+ drmEventContext evctx;
+
+ memset(&evctx, 0, sizeof evctx);
+ evctx.version = DRM_EVENT_CONTEXT_VERSION;
+ evctx.page_flip_handler = page_flip_handler;
+ evctx.vblank_handler = vblank_handler;
+ drmHandleEvent(fd, &evctx);
+
+ return 1;
+}
+
+static int
+init_drm(struct drm_compositor *ec, struct udev_device *device)
+{
+ const char *filename, *sysnum;
+ uint64_t cap;
+ int fd, ret;
+
+ sysnum = udev_device_get_sysnum(device);
+ if (sysnum)
+ ec->drm.id = atoi(sysnum);
+ if (!sysnum || ec->drm.id < 0) {
+ weston_log("cannot get device sysnum\n");
+ return -1;
+ }
+
+ filename = udev_device_get_devnode(device);
+ fd = weston_launcher_open(ec->base.launcher, filename, O_RDWR);
+ if (fd < 0) {
+ /* Probably permissions error */
+ weston_log("couldn't open %s, skipping\n",
+ udev_device_get_devnode(device));
+ return -1;
+ }
+
+ weston_log("using %s\n", filename);
+
+ ec->drm.fd = fd;
+ ec->drm.filename = strdup(filename);
+
+ ret = drmGetCap(fd, DRM_CAP_TIMESTAMP_MONOTONIC, &cap);
+ if (ret == 0 && cap == 1)
+ ec->clock = CLOCK_MONOTONIC;
+ else
+ ec->clock = CLOCK_REALTIME;
+
+ return 0;
+}
+
+static int
+init_egl(struct drm_compositor *ec)
+{
+ EGLint format;
+
+ ec->gbm = gbm_create_device(ec->drm.fd);
+
+ if (!ec->gbm)
+ return -1;
+
+ format = ec->format;
+ if (gl_renderer_create(&ec->base, ec->gbm,
+ gl_renderer_opaque_attribs, &format) < 0) {
+ gbm_device_destroy(ec->gbm);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+init_pixman(struct drm_compositor *ec)
+{
+ return pixman_renderer_init(&ec->base);
+}
+
+static struct drm_mode *
+drm_output_add_mode(struct drm_output *output, drmModeModeInfo *info)
+{
+ struct drm_mode *mode;
+ uint64_t refresh;
+
+ mode = malloc(sizeof *mode);
+ if (mode == NULL)
+ return NULL;
+
+ mode->base.flags = 0;
+ mode->base.width = info->hdisplay;
+ mode->base.height = info->vdisplay;
+
+ /* Calculate higher precision (mHz) refresh rate */
+ refresh = (info->clock * 1000000LL / info->htotal +
+ info->vtotal / 2) / info->vtotal;
+
+ if (info->flags & DRM_MODE_FLAG_INTERLACE)
+ refresh *= 2;
+ if (info->flags & DRM_MODE_FLAG_DBLSCAN)
+ refresh /= 2;
+ if (info->vscan > 1)
+ refresh /= info->vscan;
+
+ mode->base.refresh = refresh;
+ mode->mode_info = *info;
+
+ if (info->type & DRM_MODE_TYPE_PREFERRED)
+ mode->base.flags |= WL_OUTPUT_MODE_PREFERRED;
+
+ wl_list_insert(output->base.mode_list.prev, &mode->base.link);
+
+ return mode;
+}
+
+static int
+drm_subpixel_to_wayland(int drm_value)
+{
+ switch (drm_value) {
+ default:
+ case DRM_MODE_SUBPIXEL_UNKNOWN:
+ return WL_OUTPUT_SUBPIXEL_UNKNOWN;
+ case DRM_MODE_SUBPIXEL_NONE:
+ return WL_OUTPUT_SUBPIXEL_NONE;
+ case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB:
+ return WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB;
+ case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR:
+ return WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR;
+ case DRM_MODE_SUBPIXEL_VERTICAL_RGB:
+ return WL_OUTPUT_SUBPIXEL_VERTICAL_RGB;
+ case DRM_MODE_SUBPIXEL_VERTICAL_BGR:
+ return WL_OUTPUT_SUBPIXEL_VERTICAL_BGR;
+ }
+}
+
+/* returns a value between 0-255 range, where higher is brighter */
+static uint32_t
+drm_get_backlight(struct drm_output *output)
+{
+ long brightness, max_brightness, norm;
+
+ brightness = backlight_get_brightness(output->backlight);
+ max_brightness = backlight_get_max_brightness(output->backlight);
+
+ /* convert it on a scale of 0 to 255 */
+ norm = (brightness * 255)/(max_brightness);
+
+ return (uint32_t) norm;
+}
+
+/* values accepted are between 0-255 range */
+static void
+drm_set_backlight(struct weston_output *output_base, uint32_t value)
+{
+ struct drm_output *output = (struct drm_output *) output_base;
+ long max_brightness, new_brightness;
+
+ if (!output->backlight)
+ return;
+
+ if (value > 255)
+ return;
+
+ max_brightness = backlight_get_max_brightness(output->backlight);
+
+ /* get denormalized value */
+ new_brightness = (value * max_brightness) / 255;
+
+ backlight_set_brightness(output->backlight, new_brightness);
+}
+
+static drmModePropertyPtr
+drm_get_prop(int fd, drmModeConnectorPtr connector, const char *name)
+{
+ drmModePropertyPtr props;
+ int i;
+
+ for (i = 0; i < connector->count_props; i++) {
+ props = drmModeGetProperty(fd, connector->props[i]);
+ if (!props)
+ continue;
+
+ if (!strcmp(props->name, name))
+ return props;
+
+ drmModeFreeProperty(props);
+ }
+
+ return NULL;
+}
+
+static void
+drm_set_dpms(struct weston_output *output_base, enum dpms_enum level)
+{
+ struct drm_output *output = (struct drm_output *) output_base;
+ struct weston_compositor *ec = output_base->compositor;
+ struct drm_compositor *c = (struct drm_compositor *) ec;
+
+ if (!output->dpms_prop)
+ return;
+
+ drmModeConnectorSetProperty(c->drm.fd, output->connector_id,
+ output->dpms_prop->prop_id, level);
+}
+
+static const char *connector_type_names[] = {
+ "None",
+ "VGA",
+ "DVI",
+ "DVI",
+ "DVI",
+ "Composite",
+ "TV",
+ "LVDS",
+ "CTV",
+ "DIN",
+ "DP",
+ "HDMI",
+ "HDMI",
+ "TV",
+ "eDP",
+};
+
+static int
+find_crtc_for_connector(struct drm_compositor *ec,
+ drmModeRes *resources, drmModeConnector *connector)
+{
+ drmModeEncoder *encoder;
+ uint32_t possible_crtcs;
+ int i, j;
+
+ for (j = 0; j < connector->count_encoders; j++) {
+ encoder = drmModeGetEncoder(ec->drm.fd, connector->encoders[j]);
+ if (encoder == NULL) {
+ weston_log("Failed to get encoder.\n");
+ return -1;
+ }
+ possible_crtcs = encoder->possible_crtcs;
+ drmModeFreeEncoder(encoder);
+
+ for (i = 0; i < resources->count_crtcs; i++) {
+ if (possible_crtcs & (1 << i) &&
+ !(ec->crtc_allocator & (1 << resources->crtcs[i])))
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+/* Init output state that depends on gl or gbm */
+static int
+drm_output_init_egl(struct drm_output *output, struct drm_compositor *ec)
+{
+ int i, flags;
+
+ output->surface = gbm_surface_create(ec->gbm,
+ output->base.current_mode->width,
+ output->base.current_mode->height,
+ ec->format,
+ GBM_BO_USE_SCANOUT |
+ GBM_BO_USE_RENDERING);
+ if (!output->surface) {
+ weston_log("failed to create gbm surface\n");
+ return -1;
+ }
+
+ if (gl_renderer_output_create(&output->base, output->surface) < 0) {
+ weston_log("failed to create gl renderer output state\n");
+ gbm_surface_destroy(output->surface);
+ return -1;
+ }
+
+ flags = GBM_BO_USE_CURSOR_64X64 | GBM_BO_USE_WRITE;
+
+ for (i = 0; i < 2; i++) {
+ if (output->cursor_bo[i])
+ continue;
+
+ output->cursor_bo[i] =
+ gbm_bo_create(ec->gbm, 64, 64, GBM_FORMAT_ARGB8888,
+ flags);
+ }
+
+ if (output->cursor_bo[0] == NULL || output->cursor_bo[1] == NULL) {
+ weston_log("cursor buffers unavailable, using gl cursors\n");
+ ec->cursors_are_broken = 1;
+ }
+
+ return 0;
+}
+
+static int
+drm_output_init_pixman(struct drm_output *output, struct drm_compositor *c)
+{
+ int w = output->base.current_mode->width;
+ int h = output->base.current_mode->height;
+ unsigned int i;
+
+ /* FIXME error checking */
+
+ for (i = 0; i < ARRAY_LENGTH(output->dumb); i++) {
+ output->dumb[i] = drm_fb_create_dumb(c, w, h);
+ if (!output->dumb[i])
+ goto err;
+
+ output->image[i] =
+ pixman_image_create_bits(PIXMAN_x8r8g8b8, w, h,
+ output->dumb[i]->map,
+ output->dumb[i]->stride);
+ if (!output->image[i])
+ goto err;
+ }
+
+ if (pixman_renderer_output_create(&output->base) < 0)
+ goto err;
+
+ pixman_region32_init_rect(&output->previous_damage,
+ output->base.x, output->base.y, output->base.width, output->base.height);
+
+ return 0;
+
+err:
+ for (i = 0; i < ARRAY_LENGTH(output->dumb); i++) {
+ if (output->dumb[i])
+ drm_fb_destroy_dumb(output->dumb[i]);
+ if (output->image[i])
+ pixman_image_unref(output->image[i]);
+
+ output->dumb[i] = NULL;
+ output->image[i] = NULL;
+ }
+
+ return -1;
+}
+
+static void
+drm_output_fini_pixman(struct drm_output *output)
+{
+ unsigned int i;
+
+ pixman_renderer_output_destroy(&output->base);
+ pixman_region32_fini(&output->previous_damage);
+
+ for (i = 0; i < ARRAY_LENGTH(output->dumb); i++) {
+ drm_fb_destroy_dumb(output->dumb[i]);
+ pixman_image_unref(output->image[i]);
+ output->dumb[i] = NULL;
+ output->image[i] = NULL;
+ }
+}
+
+static void
+edid_parse_string(const uint8_t *data, char text[])
+{
+ int i;
+ int replaced = 0;
+
+ /* this is always 12 bytes, but we can't guarantee it's null
+ * terminated or not junk. */
+ strncpy(text, (const char *) data, 12);
+
+ /* remove insane chars */
+ for (i = 0; text[i] != '\0'; i++) {
+ if (text[i] == '\n' ||
+ text[i] == '\r') {
+ text[i] = '\0';
+ break;
+ }
+ }
+
+ /* ensure string is printable */
+ for (i = 0; text[i] != '\0'; i++) {
+ if (!isprint(text[i])) {
+ text[i] = '-';
+ replaced++;
+ }
+ }
+
+ /* if the string is random junk, ignore the string */
+ if (replaced > 4)
+ text[0] = '\0';
+}
+
+#define EDID_DESCRIPTOR_ALPHANUMERIC_DATA_STRING 0xfe
+#define EDID_DESCRIPTOR_DISPLAY_PRODUCT_NAME 0xfc
+#define EDID_DESCRIPTOR_DISPLAY_PRODUCT_SERIAL_NUMBER 0xff
+#define EDID_OFFSET_DATA_BLOCKS 0x36
+#define EDID_OFFSET_LAST_BLOCK 0x6c
+#define EDID_OFFSET_PNPID 0x08
+#define EDID_OFFSET_SERIAL 0x0c
+
+static int
+edid_parse(struct drm_edid *edid, const uint8_t *data, size_t length)
+{
+ int i;
+ uint32_t serial_number;
+
+ /* check header */
+ if (length < 128)
+ return -1;
+ if (data[0] != 0x00 || data[1] != 0xff)
+ return -1;
+
+ /* decode the PNP ID from three 5 bit words packed into 2 bytes
+ * /--08--\/--09--\
+ * 7654321076543210
+ * |\---/\---/\---/
+ * R C1 C2 C3 */
+ edid->pnp_id[0] = 'A' + ((data[EDID_OFFSET_PNPID + 0] & 0x7c) / 4) - 1;
+ edid->pnp_id[1] = 'A' + ((data[EDID_OFFSET_PNPID + 0] & 0x3) * 8) + ((data[EDID_OFFSET_PNPID + 1] & 0xe0) / 32) - 1;
+ edid->pnp_id[2] = 'A' + (data[EDID_OFFSET_PNPID + 1] & 0x1f) - 1;
+ edid->pnp_id[3] = '\0';
+
+ /* maybe there isn't a ASCII serial number descriptor, so use this instead */
+ serial_number = (uint32_t) data[EDID_OFFSET_SERIAL + 0];
+ serial_number += (uint32_t) data[EDID_OFFSET_SERIAL + 1] * 0x100;
+ serial_number += (uint32_t) data[EDID_OFFSET_SERIAL + 2] * 0x10000;
+ serial_number += (uint32_t) data[EDID_OFFSET_SERIAL + 3] * 0x1000000;
+ if (serial_number > 0)
+ sprintf(edid->serial_number, "%lu", (unsigned long) serial_number);
+
+ /* parse EDID data */
+ for (i = EDID_OFFSET_DATA_BLOCKS;
+ i <= EDID_OFFSET_LAST_BLOCK;
+ i += 18) {
+ /* ignore pixel clock data */
+ if (data[i] != 0)
+ continue;
+ if (data[i+2] != 0)
+ continue;
+
+ /* any useful blocks? */
+ if (data[i+3] == EDID_DESCRIPTOR_DISPLAY_PRODUCT_NAME) {
+ edid_parse_string(&data[i+5],
+ edid->monitor_name);
+ } else if (data[i+3] == EDID_DESCRIPTOR_DISPLAY_PRODUCT_SERIAL_NUMBER) {
+ edid_parse_string(&data[i+5],
+ edid->serial_number);
+ } else if (data[i+3] == EDID_DESCRIPTOR_ALPHANUMERIC_DATA_STRING) {
+ edid_parse_string(&data[i+5],
+ edid->eisa_id);
+ }
+ }
+ return 0;
+}
+
+static void
+find_and_parse_output_edid(struct drm_compositor *ec,
+ struct drm_output *output,
+ drmModeConnector *connector)
+{
+ drmModePropertyBlobPtr edid_blob = NULL;
+ drmModePropertyPtr property;
+ int i;
+ int rc;
+
+ for (i = 0; i < connector->count_props && !edid_blob; i++) {
+ property = drmModeGetProperty(ec->drm.fd, connector->props[i]);
+ if (!property)
+ continue;
+ if ((property->flags & DRM_MODE_PROP_BLOB) &&
+ !strcmp(property->name, "EDID")) {
+ edid_blob = drmModeGetPropertyBlob(ec->drm.fd,
+ connector->prop_values[i]);
+ }
+ drmModeFreeProperty(property);
+ }
+ if (!edid_blob)
+ return;
+
+ rc = edid_parse(&output->edid,
+ edid_blob->data,
+ edid_blob->length);
+ if (!rc) {
+ weston_log("EDID data '%s', '%s', '%s'\n",
+ output->edid.pnp_id,
+ output->edid.monitor_name,
+ output->edid.serial_number);
+ if (output->edid.pnp_id[0] != '\0')
+ output->base.make = output->edid.pnp_id;
+ if (output->edid.monitor_name[0] != '\0')
+ output->base.model = output->edid.monitor_name;
+ if (output->edid.serial_number[0] != '\0')
+ output->base.serial_number = output->edid.serial_number;
+ }
+ drmModeFreePropertyBlob(edid_blob);
+}
+
+
+
+static int
+parse_modeline(const char *s, drmModeModeInfo *mode)
+{
+ char hsync[16];
+ char vsync[16];
+ float fclock;
+
+ mode->type = DRM_MODE_TYPE_USERDEF;
+ mode->hskew = 0;
+ mode->vscan = 0;
+ mode->vrefresh = 0;
+ mode->flags = 0;
+
+ if (sscanf(s, "%f %hd %hd %hd %hd %hd %hd %hd %hd %15s %15s",
+ &fclock,
+ &mode->hdisplay,
+ &mode->hsync_start,
+ &mode->hsync_end,
+ &mode->htotal,
+ &mode->vdisplay,
+ &mode->vsync_start,
+ &mode->vsync_end,
+ &mode->vtotal, hsync, vsync) != 11)
+ return -1;
+
+ mode->clock = fclock * 1000;
+ if (strcmp(hsync, "+hsync") == 0)
+ mode->flags |= DRM_MODE_FLAG_PHSYNC;
+ else if (strcmp(hsync, "-hsync") == 0)
+ mode->flags |= DRM_MODE_FLAG_NHSYNC;
+ else
+ return -1;
+
+ if (strcmp(vsync, "+vsync") == 0)
+ mode->flags |= DRM_MODE_FLAG_PVSYNC;
+ else if (strcmp(vsync, "-vsync") == 0)
+ mode->flags |= DRM_MODE_FLAG_NVSYNC;
+ else
+ return -1;
+
+ return 0;
+}
+
+static uint32_t
+parse_transform(const char *transform, const char *output_name)
+{
+ static const struct { const char *name; uint32_t token; } names[] = {
+ { "normal", WL_OUTPUT_TRANSFORM_NORMAL },
+ { "90", WL_OUTPUT_TRANSFORM_90 },
+ { "180", WL_OUTPUT_TRANSFORM_180 },
+ { "270", WL_OUTPUT_TRANSFORM_270 },
+ { "flipped", WL_OUTPUT_TRANSFORM_FLIPPED },
+ { "flipped-90", WL_OUTPUT_TRANSFORM_FLIPPED_90 },
+ { "flipped-180", WL_OUTPUT_TRANSFORM_FLIPPED_180 },
+ { "flipped-270", WL_OUTPUT_TRANSFORM_FLIPPED_270 },
+ };
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_LENGTH(names); i++)
+ if (strcmp(names[i].name, transform) == 0)
+ return names[i].token;
+
+ weston_log("Invalid transform \"%s\" for output %s\n",
+ transform, output_name);
+
+ return WL_OUTPUT_TRANSFORM_NORMAL;
+}
+
+static void
+setup_output_seat_constraint(struct drm_compositor *ec,
+ struct weston_output *output,
+ const char *s)
+{
+ if (strcmp(s, "") != 0) {
+ struct udev_seat *seat;
+
+ seat = udev_seat_get_named(&ec->base, s);
+ if (seat)
+ seat->base.output = output;
+
+ if (seat && seat->base.pointer)
+ weston_pointer_clamp(seat->base.pointer,
+ &seat->base.pointer->x,
+ &seat->base.pointer->y);
+ }
+}
+
+static int
+create_output_for_connector(struct drm_compositor *ec,
+ drmModeRes *resources,
+ drmModeConnector *connector,
+ int x, int y, struct udev_device *drm_device)
+{
+ struct drm_output *output;
+ struct drm_mode *drm_mode, *next, *preferred, *current, *configured;
+ struct weston_mode *m;
+ struct weston_config_section *section;
+ drmModeEncoder *encoder;
+ drmModeModeInfo crtc_mode, modeline;
+ drmModeCrtc *crtc;
+ int i, width, height, scale;
+ char name[32], *s;
+ const char *type_name;
+ enum output_config config;
+ uint32_t transform;
+
+ i = find_crtc_for_connector(ec, resources, connector);
+ if (i < 0) {
+ weston_log("No usable crtc/encoder pair for connector.\n");
+ return -1;
+ }
+
+ output = zalloc(sizeof *output);
+ if (output == NULL)
+ return -1;
+
+ output->base.subpixel = drm_subpixel_to_wayland(connector->subpixel);
+ output->base.make = "unknown";
+ output->base.model = "unknown";
+ output->base.serial_number = "unknown";
+ wl_list_init(&output->base.mode_list);
+
+ if (connector->connector_type < ARRAY_LENGTH(connector_type_names))
+ type_name = connector_type_names[connector->connector_type];
+ else
+ type_name = "UNKNOWN";
+ snprintf(name, 32, "%s%d", type_name, connector->connector_type_id);
+ output->base.name = strdup(name);
+
+ section = weston_config_get_section(ec->base.config, "output", "name",
+ output->base.name);
+ weston_config_section_get_string(section, "mode", &s, "preferred");
+ if (strcmp(s, "off") == 0)
+ config = OUTPUT_CONFIG_OFF;
+ else if (strcmp(s, "preferred") == 0)
+ config = OUTPUT_CONFIG_PREFERRED;
+ else if (strcmp(s, "current") == 0)
+ config = OUTPUT_CONFIG_CURRENT;
+ else if (sscanf(s, "%dx%d", &width, &height) == 2)
+ config = OUTPUT_CONFIG_MODE;
+ else if (parse_modeline(s, &modeline) == 0)
+ config = OUTPUT_CONFIG_MODELINE;
+ else {
+ weston_log("Invalid mode \"%s\" for output %s\n",
+ s, output->base.name);
+ config = OUTPUT_CONFIG_PREFERRED;
+ }
+ free(s);
+
+ weston_config_section_get_int(section, "scale", &scale, 1);
+ weston_config_section_get_string(section, "transform", &s, "normal");
+ transform = parse_transform(s, output->base.name);
+ free(s);
+
+ weston_config_section_get_string(section, "seat", &s, "");
+ setup_output_seat_constraint(ec, &output->base, s);
+ free(s);
+
+ output->crtc_id = resources->crtcs[i];
+ output->pipe = i;
+ ec->crtc_allocator |= (1 << output->crtc_id);
+ output->connector_id = connector->connector_id;
+ ec->connector_allocator |= (1 << output->connector_id);
+
+ output->original_crtc = drmModeGetCrtc(ec->drm.fd, output->crtc_id);
+ output->dpms_prop = drm_get_prop(ec->drm.fd, connector, "DPMS");
+
+ /* Get the current mode on the crtc that's currently driving
+ * this connector. */
+ encoder = drmModeGetEncoder(ec->drm.fd, connector->encoder_id);
+ memset(&crtc_mode, 0, sizeof crtc_mode);
+ if (encoder != NULL) {
+ crtc = drmModeGetCrtc(ec->drm.fd, encoder->crtc_id);
+ drmModeFreeEncoder(encoder);
+ if (crtc == NULL)
+ goto err_free;
+ if (crtc->mode_valid)
+ crtc_mode = crtc->mode;
+ drmModeFreeCrtc(crtc);
+ }
+
+ for (i = 0; i < connector->count_modes; i++) {
+ drm_mode = drm_output_add_mode(output, &connector->modes[i]);
+ if (!drm_mode)
+ goto err_free;
+ }
+
+ if (config == OUTPUT_CONFIG_OFF) {
+ weston_log("Disabling output %s\n", output->base.name);
+ drmModeSetCrtc(ec->drm.fd, output->crtc_id,
+ 0, 0, 0, 0, 0, NULL);
+ goto err_free;
+ }
+
+ preferred = NULL;
+ current = NULL;
+ configured = NULL;
+
+ wl_list_for_each_reverse(drm_mode, &output->base.mode_list, base.link) {
+ if (config == OUTPUT_CONFIG_MODE &&
+ width == drm_mode->base.width &&
+ height == drm_mode->base.height)
+ configured = drm_mode;
+ if (!memcmp(&crtc_mode, &drm_mode->mode_info, sizeof crtc_mode))
+ current = drm_mode;
+ if (drm_mode->base.flags & WL_OUTPUT_MODE_PREFERRED)
+ preferred = drm_mode;
+ }
+
+ if (config == OUTPUT_CONFIG_MODELINE) {
+ configured = drm_output_add_mode(output, &modeline);
+ if (!configured)
+ goto err_free;
+ }
+
+ if (current == NULL && crtc_mode.clock != 0) {
+ current = drm_output_add_mode(output, &crtc_mode);
+ if (!current)
+ goto err_free;
+ }
+
+ if (config == OUTPUT_CONFIG_CURRENT)
+ configured = current;
+
+ if (option_current_mode && current)
+ output->base.current_mode = &current->base;
+ else if (configured)
+ output->base.current_mode = &configured->base;
+ else if (preferred)
+ output->base.current_mode = &preferred->base;
+ else if (current)
+ output->base.current_mode = &current->base;
+
+ if (output->base.current_mode == NULL) {
+ weston_log("no available modes for %s\n", output->base.name);
+ goto err_free;
+ }
+
+ output->base.current_mode->flags |= WL_OUTPUT_MODE_CURRENT;
+
+ weston_output_init(&output->base, &ec->base, x, y,
+ connector->mmWidth, connector->mmHeight,
+ transform, scale);
+
+ if (ec->use_pixman) {
+ if (drm_output_init_pixman(output, ec) < 0) {
+ weston_log("Failed to init output pixman state\n");
+ goto err_output;
+ }
+ } else if (drm_output_init_egl(output, ec) < 0) {
+ weston_log("Failed to init output gl state\n");
+ goto err_output;
+ }
+
+ output->backlight = backlight_init(drm_device,
+ connector->connector_type);
+ if (output->backlight) {
+ weston_log("Initialized backlight, device %s\n",
+ output->backlight->path);
+ output->base.set_backlight = drm_set_backlight;
+ output->base.backlight_current = drm_get_backlight(output);
+ } else {
+ weston_log("Failed to initialize backlight\n");
+ }
+
+ wl_list_insert(ec->base.output_list.prev, &output->base.link);
+
+ find_and_parse_output_edid(ec, output, connector);
+ if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)
+ output->base.connection_internal = 1;
+
+ output->base.start_repaint_loop = drm_output_start_repaint_loop;
+ output->base.repaint = drm_output_repaint;
+ output->base.destroy = drm_output_destroy;
+ output->base.assign_planes = drm_assign_planes;
+ output->base.set_dpms = drm_set_dpms;
+ output->base.switch_mode = drm_output_switch_mode;
+
+ output->base.gamma_size = output->original_crtc->gamma_size;
+ output->base.set_gamma = drm_output_set_gamma;
+
+ weston_plane_init(&output->cursor_plane, &ec->base, 0, 0);
+ weston_plane_init(&output->fb_plane, &ec->base, 0, 0);
+
+ weston_compositor_stack_plane(&ec->base, &output->cursor_plane, NULL);
+ weston_compositor_stack_plane(&ec->base, &output->fb_plane,
+ &ec->base.primary_plane);
+
+ weston_log("Output %s, (connector %d, crtc %d)\n",
+ output->base.name, output->connector_id, output->crtc_id);
+ wl_list_for_each(m, &output->base.mode_list, link)
+ weston_log_continue(" mode %dx%d@%.1f%s%s%s\n",
+ m->width, m->height, m->refresh / 1000.0,
+ m->flags & WL_OUTPUT_MODE_PREFERRED ?
+ ", preferred" : "",
+ m->flags & WL_OUTPUT_MODE_CURRENT ?
+ ", current" : "",
+ connector->count_modes == 0 ?
+ ", built-in" : "");
+
+ return 0;
+
+err_output:
+ weston_output_destroy(&output->base);
+err_free:
+ wl_list_for_each_safe(drm_mode, next, &output->base.mode_list,
+ base.link) {
+ wl_list_remove(&drm_mode->base.link);
+ free(drm_mode);
+ }
+
+ drmModeFreeCrtc(output->original_crtc);
+ ec->crtc_allocator &= ~(1 << output->crtc_id);
+ ec->connector_allocator &= ~(1 << output->connector_id);
+ free(output);
+
+ return -1;
+}
+
+static void
+create_sprites(struct drm_compositor *ec)
+{
+ struct drm_sprite *sprite;
+ drmModePlaneRes *plane_res;
+ drmModePlane *plane;
+ uint32_t i;
+
+ plane_res = drmModeGetPlaneResources(ec->drm.fd);
+ if (!plane_res) {
+ weston_log("failed to get plane resources: %s\n",
+ strerror(errno));
+ return;
+ }
+
+ for (i = 0; i < plane_res->count_planes; i++) {
+ plane = drmModeGetPlane(ec->drm.fd, plane_res->planes[i]);
+ if (!plane)
+ continue;
+
+ sprite = zalloc(sizeof(*sprite) + ((sizeof(uint32_t)) *
+ plane->count_formats));
+ if (!sprite) {
+ weston_log("%s: out of memory\n",
+ __func__);
+ free(plane);
+ continue;
+ }
+
+ sprite->possible_crtcs = plane->possible_crtcs;
+ sprite->plane_id = plane->plane_id;
+ sprite->current = NULL;
+ sprite->next = NULL;
+ sprite->compositor = ec;
+ sprite->count_formats = plane->count_formats;
+ memcpy(sprite->formats, plane->formats,
+ plane->count_formats * sizeof(plane->formats[0]));
+ drmModeFreePlane(plane);
+ weston_plane_init(&sprite->plane, &ec->base, 0, 0);
+ weston_compositor_stack_plane(&ec->base, &sprite->plane,
+ &ec->base.primary_plane);
+
+ wl_list_insert(&ec->sprite_list, &sprite->link);
+ }
+
+ drmModeFreePlaneResources(plane_res);
+}
+
+static void
+destroy_sprites(struct drm_compositor *compositor)
+{
+ struct drm_sprite *sprite, *next;
+ struct drm_output *output;
+
+ output = container_of(compositor->base.output_list.next,
+ struct drm_output, base.link);
+
+ wl_list_for_each_safe(sprite, next, &compositor->sprite_list, link) {
+ drmModeSetPlane(compositor->drm.fd,
+ sprite->plane_id,
+ output->crtc_id, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0);
+ drm_output_release_fb(output, sprite->current);
+ drm_output_release_fb(output, sprite->next);
+ weston_plane_release(&sprite->plane);
+ free(sprite);
+ }
+}
+
+static int
+create_outputs(struct drm_compositor *ec, uint32_t option_connector,
+ struct udev_device *drm_device)
+{
+ drmModeConnector *connector;
+ drmModeRes *resources;
+ int i;
+ int x = 0, y = 0;
+
+ resources = drmModeGetResources(ec->drm.fd);
+ if (!resources) {
+ weston_log("drmModeGetResources failed\n");
+ return -1;
+ }
+
+ ec->crtcs = calloc(resources->count_crtcs, sizeof(uint32_t));
+ if (!ec->crtcs) {
+ drmModeFreeResources(resources);
+ return -1;
+ }
+
+ ec->min_width = resources->min_width;
+ ec->max_width = resources->max_width;
+ ec->min_height = resources->min_height;
+ ec->max_height = resources->max_height;
+
+ ec->num_crtcs = resources->count_crtcs;
+ memcpy(ec->crtcs, resources->crtcs, sizeof(uint32_t) * ec->num_crtcs);
+
+ for (i = 0; i < resources->count_connectors; i++) {
+ connector = drmModeGetConnector(ec->drm.fd,
+ resources->connectors[i]);
+ if (connector == NULL)
+ continue;
+
+ if (connector->connection == DRM_MODE_CONNECTED &&
+ (option_connector == 0 ||
+ connector->connector_id == option_connector)) {
+ if (create_output_for_connector(ec, resources,
+ connector, x, y,
+ drm_device) < 0) {
+ drmModeFreeConnector(connector);
+ continue;
+ }
+
+ x += container_of(ec->base.output_list.prev,
+ struct weston_output,
+ link)->width;
+ }
+
+ drmModeFreeConnector(connector);
+ }
+
+ if (wl_list_empty(&ec->base.output_list)) {
+ weston_log("No currently active connector found.\n");
+ drmModeFreeResources(resources);
+ return -1;
+ }
+
+ drmModeFreeResources(resources);
+
+ return 0;
+}
+
+static void
+update_outputs(struct drm_compositor *ec, struct udev_device *drm_device)
+{
+ drmModeConnector *connector;
+ drmModeRes *resources;
+ struct drm_output *output, *next;
+ int x = 0, y = 0;
+ int x_offset = 0, y_offset = 0;
+ uint32_t connected = 0, disconnects = 0;
+ int i;
+
+ resources = drmModeGetResources(ec->drm.fd);
+ if (!resources) {
+ weston_log("drmModeGetResources failed\n");
+ return;
+ }
+
+ /* collect new connects */
+ for (i = 0; i < resources->count_connectors; i++) {
+ int connector_id = resources->connectors[i];
+
+ connector = drmModeGetConnector(ec->drm.fd, connector_id);
+ if (connector == NULL)
+ continue;
+
+ if (connector->connection != DRM_MODE_CONNECTED) {
+ drmModeFreeConnector(connector);
+ continue;
+ }
+
+ connected |= (1 << connector_id);
+
+ if (!(ec->connector_allocator & (1 << connector_id))) {
+ struct weston_output *last =
+ container_of(ec->base.output_list.prev,
+ struct weston_output, link);
+
+ /* XXX: not yet needed, we die with 0 outputs */
+ if (!wl_list_empty(&ec->base.output_list))
+ x = last->x + last->width;
+ else
+ x = 0;
+ y = 0;
+ create_output_for_connector(ec, resources,
+ connector, x, y,
+ drm_device);
+ weston_log("connector %d connected\n", connector_id);
+
+ }
+ drmModeFreeConnector(connector);
+ }
+ drmModeFreeResources(resources);
+
+ disconnects = ec->connector_allocator & ~connected;
+ if (disconnects) {
+ wl_list_for_each_safe(output, next, &ec->base.output_list,
+ base.link) {
+ if (x_offset != 0 || y_offset != 0) {
+ weston_output_move(&output->base,
+ output->base.x - x_offset,
+ output->base.y - y_offset);
+ }
+
+ if (disconnects & (1 << output->connector_id)) {
+ disconnects &= ~(1 << output->connector_id);
+ weston_log("connector %d disconnected\n",
+ output->connector_id);
+ x_offset += output->base.width;
+ drm_output_destroy(&output->base);
+ }
+ }
+ }
+
+ /* FIXME: handle zero outputs, without terminating */
+ if (ec->connector_allocator == 0)
+ wl_display_terminate(ec->base.wl_display);
+}
+
+static int
+udev_event_is_hotplug(struct drm_compositor *ec, struct udev_device *device)
+{
+ const char *sysnum;
+ const char *val;
+
+ sysnum = udev_device_get_sysnum(device);
+ if (!sysnum || atoi(sysnum) != ec->drm.id)
+ return 0;
+
+ val = udev_device_get_property_value(device, "HOTPLUG");
+ if (!val)
+ return 0;
+
+ return strcmp(val, "1") == 0;
+}
+
+static int
+udev_drm_event(int fd, uint32_t mask, void *data)
+{
+ struct drm_compositor *ec = data;
+ struct udev_device *event;
+
+ event = udev_monitor_receive_device(ec->udev_monitor);
+
+ if (udev_event_is_hotplug(ec, event))
+ update_outputs(ec, event);
+
+ udev_device_unref(event);
+
+ return 1;
+}
+
+static void
+drm_restore(struct weston_compositor *ec)
+{
+ weston_launcher_restore(ec->launcher);
+}
+
+static void
+drm_destroy(struct weston_compositor *ec)
+{
+ struct drm_compositor *d = (struct drm_compositor *) ec;
+
+ udev_input_destroy(&d->input);
+
+ wl_event_source_remove(d->udev_drm_source);
+ wl_event_source_remove(d->drm_source);
+
+ destroy_sprites(d);
+
+ weston_compositor_shutdown(ec);
+
+ ec->renderer->destroy(ec);
+
+ if (d->gbm)
+ gbm_device_destroy(d->gbm);
+
+ weston_launcher_destroy(d->base.launcher);
+
+ close(d->drm.fd);
+
+ free(d);
+}
+
+static void
+drm_compositor_set_modes(struct drm_compositor *compositor)
+{
+ struct drm_output *output;
+ struct drm_mode *drm_mode;
+ int ret;
+
+ wl_list_for_each(output, &compositor->base.output_list, base.link) {
+ if (!output->current) {
+ /* If something that would cause the output to
+ * switch mode happened while in another vt, we
+ * might not have a current drm_fb. In that case,
+ * schedule a repaint and let drm_output_repaint
+ * handle setting the mode. */
+ weston_output_schedule_repaint(&output->base);
+ continue;
+ }
+
+ drm_mode = (struct drm_mode *) output->base.current_mode;
+ ret = drmModeSetCrtc(compositor->drm.fd, output->crtc_id,
+ output->current->fb_id, 0, 0,
+ &output->connector_id, 1,
+ &drm_mode->mode_info);
+ if (ret < 0) {
+ weston_log(
+ "failed to set mode %dx%d for output at %d,%d: %m\n",
+ drm_mode->base.width, drm_mode->base.height,
+ output->base.x, output->base.y);
+ }
+ }
+}
+
+static void
+session_notify(struct wl_listener *listener, void *data)
+{
+ struct weston_compositor *compositor = data;
+ struct drm_compositor *ec = data;
+ struct drm_sprite *sprite;
+ struct drm_output *output;
+
+ if (ec->base.session_active) {
+ weston_log("activating session\n");
+ compositor->focus = 1;
+ compositor->state = ec->prev_state;
+ drm_compositor_set_modes(ec);
+ weston_compositor_damage_all(compositor);
+ udev_input_enable(&ec->input, ec->udev);
+ } else {
+ weston_log("deactivating session\n");
+ udev_input_disable(&ec->input);
+
+ compositor->focus = 0;
+ ec->prev_state = compositor->state;
+ weston_compositor_offscreen(compositor);
+
+ /* If we have a repaint scheduled (either from a
+ * pending pageflip or the idle handler), make sure we
+ * cancel that so we don't try to pageflip when we're
+ * vt switched away. The OFFSCREEN state will prevent
+ * further attemps at repainting. When we switch
+ * back, we schedule a repaint, which will process
+ * pending frame callbacks. */
+
+ wl_list_for_each(output, &ec->base.output_list, base.link) {
+ output->base.repaint_needed = 0;
+ drmModeSetCursor(ec->drm.fd, output->crtc_id, 0, 0, 0);
+ }
+
+ output = container_of(ec->base.output_list.next,
+ struct drm_output, base.link);
+
+ wl_list_for_each(sprite, &ec->sprite_list, link)
+ drmModeSetPlane(ec->drm.fd,
+ sprite->plane_id,
+ output->crtc_id, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0);
+ };
+}
+
+static void
+switch_vt_binding(struct weston_seat *seat, uint32_t time, uint32_t key, void *data)
+{
+ struct weston_compositor *compositor = data;
+
+ weston_launcher_activate_vt(compositor->launcher, key - KEY_F1 + 1);
+}
+
+/*
+ * Find primary GPU
+ * Some systems may have multiple DRM devices attached to a single seat. This
+ * function loops over all devices and tries to find a PCI device with the
+ * boot_vga sysfs attribute set to 1.
+ * If no such device is found, the first DRM device reported by udev is used.
+ */
+static struct udev_device*
+find_primary_gpu(struct drm_compositor *ec, const char *seat)
+{
+ struct udev_enumerate *e;
+ struct udev_list_entry *entry;
+ const char *path, *device_seat, *id;
+ struct udev_device *device, *drm_device, *pci;
+
+ e = udev_enumerate_new(ec->udev);
+ udev_enumerate_add_match_subsystem(e, "drm");
+ udev_enumerate_add_match_sysname(e, "card[0-9]*");
+
+ udev_enumerate_scan_devices(e);
+ drm_device = NULL;
+ udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) {
+ path = udev_list_entry_get_name(entry);
+ device = udev_device_new_from_syspath(ec->udev, path);
+ if (!device)
+ continue;
+ device_seat = udev_device_get_property_value(device, "ID_SEAT");
+ if (!device_seat)
+ device_seat = default_seat;
+ if (strcmp(device_seat, seat)) {
+ udev_device_unref(device);
+ continue;
+ }
+
+ pci = udev_device_get_parent_with_subsystem_devtype(device,
+ "pci", NULL);
+ if (pci) {
+ id = udev_device_get_sysattr_value(pci, "boot_vga");
+ if (id && !strcmp(id, "1")) {
+ if (drm_device)
+ udev_device_unref(drm_device);
+ drm_device = device;
+ break;
+ }
+ }
+
+ if (!drm_device)
+ drm_device = device;
+ else
+ udev_device_unref(device);
+ }
+
+ udev_enumerate_unref(e);
+ return drm_device;
+}
+
+static void
+planes_binding(struct weston_seat *seat, uint32_t time, uint32_t key, void *data)
+{
+ struct drm_compositor *c = data;
+
+ switch (key) {
+ case KEY_C:
+ c->cursors_are_broken ^= 1;
+ break;
+ case KEY_V:
+ c->sprites_are_broken ^= 1;
+ break;
+ case KEY_O:
+ c->sprites_hidden ^= 1;
+ break;
+ default:
+ break;
+ }
+}
+
+#ifdef BUILD_VAAPI_RECORDER
+static void
+recorder_frame_notify(struct wl_listener *listener, void *data)
+{
+ struct drm_output *output;
+ struct drm_compositor *c;
+ int fd, ret;
+
+ output = container_of(listener, struct drm_output,
+ recorder_frame_listener);
+ c = (struct drm_compositor *) output->base.compositor;
+
+ if (!output->recorder)
+ return;
+
+ ret = drmPrimeHandleToFD(c->drm.fd, output->current->handle,
+ DRM_CLOEXEC, &fd);
+ if (ret) {
+ weston_log("[libva recorder] "
+ "failed to create prime fd for front buffer\n");
+ return;
+ }
+
+ vaapi_recorder_frame(output->recorder, fd, output->current->stride / 4);
+}
+
+static void *
+create_recorder(struct drm_compositor *c, int width, int height,
+ const char *filename)
+{
+ int fd;
+ drm_magic_t magic;
+
+ fd = open(c->drm.filename, O_RDWR | O_CLOEXEC);
+ if (fd < 0)
+ return NULL;
+
+ drmGetMagic(fd, &magic);
+ drmAuthMagic(c->drm.fd, magic);
+
+ return vaapi_recorder_create(fd, width, height, filename);
+}
+
+static void
+recorder_binding(struct weston_seat *seat, uint32_t time, uint32_t key,
+ void *data)
+{
+ struct drm_compositor *c = data;
+ struct drm_output *output;
+ int width, height;
+
+ output = container_of(c->base.output_list.next,
+ struct drm_output, base.link);
+
+ if (!output->recorder) {
+ width = output->base.current_mode->width;
+ height = output->base.current_mode->height;
+
+ output->recorder =
+ create_recorder(c, width, height, "capture.h264");
+ if (!output->recorder) {
+ weston_log("failed to create vaapi recorder\n");
+ return;
+ }
+
+ output->base.disable_planes++;
+
+ output->recorder_frame_listener.notify = recorder_frame_notify;
+ wl_signal_add(&output->base.frame_signal,
+ &output->recorder_frame_listener);
+
+ weston_output_schedule_repaint(&output->base);
+
+ weston_log("[libva recorder] initialized\n");
+ } else {
+ vaapi_recorder_destroy(output->recorder);
+ output->recorder = NULL;
+
+ output->base.disable_planes--;
+
+ wl_list_remove(&output->recorder_frame_listener.link);
+ weston_log("[libva recorder] done\n");
+ }
+}
+#else
+static void
+recorder_binding(struct weston_seat *seat, uint32_t time, uint32_t key,
+ void *data)
+{
+ weston_log("Compiled without libva support\n");
+}
+#endif
+
+static struct weston_compositor *
+drm_compositor_create(struct wl_display *display,
+ int connector, const char *seat_id, int tty, int pixman,
+ int *argc, char *argv[],
+ struct weston_config *config)
+{
+ struct drm_compositor *ec;
+ struct udev_device *drm_device;
+ struct wl_event_loop *loop;
+ const char *path;
+ uint32_t key;
+
+ weston_log("initializing drm backend\n");
+
+ ec = zalloc(sizeof *ec);
+ if (ec == NULL)
+ return NULL;
+
+ /* KMS support for sprites is not complete yet, so disable the
+ * functionality for now. */
+ ec->sprites_are_broken = 1;
+ ec->format = GBM_FORMAT_XRGB8888;
+ ec->use_pixman = pixman;
+
+ if (weston_compositor_init(&ec->base, display, argc, argv,
+ config) < 0) {
+ weston_log("%s failed\n", __func__);
+ goto err_base;
+ }
+
+ /* Check if we run drm-backend using weston-launch */
+ ec->base.launcher = weston_launcher_connect(&ec->base, tty);
+ if (ec->base.launcher == NULL) {
+ weston_log("fatal: drm backend should be run "
+ "using weston-launch binary or as root\n");
+ goto err_compositor;
+ }
+
+ ec->udev = udev_new();
+ if (ec->udev == NULL) {
+ weston_log("failed to initialize udev context\n");
+ goto err_launcher;
+ }
+
+ ec->base.wl_display = display;
+ ec->session_listener.notify = session_notify;
+ wl_signal_add(&ec->base.session_signal, &ec->session_listener);
+
+ drm_device = find_primary_gpu(ec, seat_id);
+ if (drm_device == NULL) {
+ weston_log("no drm device found\n");
+ goto err_udev;
+ }
+ path = udev_device_get_syspath(drm_device);
+
+ if (init_drm(ec, drm_device) < 0) {
+ weston_log("failed to initialize kms\n");
+ goto err_udev_dev;
+ }
+
+ if (ec->use_pixman) {
+ if (init_pixman(ec) < 0) {
+ weston_log("failed to initialize pixman renderer\n");
+ goto err_udev_dev;
+ }
+ } else {
+ if (init_egl(ec) < 0) {
+ weston_log("failed to initialize egl\n");
+ goto err_udev_dev;
+ }
+ }
+
+ ec->base.destroy = drm_destroy;
+ ec->base.restore = drm_restore;
+
+ ec->base.focus = 1;
+
+ ec->prev_state = WESTON_COMPOSITOR_ACTIVE;
+
+ for (key = KEY_F1; key < KEY_F9; key++)
+ weston_compositor_add_key_binding(&ec->base, key,
+ MODIFIER_CTRL | MODIFIER_ALT,
+ switch_vt_binding, ec);
+
+ wl_list_init(&ec->sprite_list);
+ create_sprites(ec);
+
+ if (create_outputs(ec, connector, drm_device) < 0) {
+ weston_log("failed to create output for %s\n", path);
+ goto err_sprite;
+ }
+
+ path = NULL;
+
+ if (udev_input_init(&ec->input, &ec->base, ec->udev, seat_id) < 0) {
+ weston_log("failed to create input devices\n");
+ goto err_sprite;
+ }
+
+ loop = wl_display_get_event_loop(ec->base.wl_display);
+ ec->drm_source =
+ wl_event_loop_add_fd(loop, ec->drm.fd,
+ WL_EVENT_READABLE, on_drm_input, ec);
+
+ ec->udev_monitor = udev_monitor_new_from_netlink(ec->udev, "udev");
+ if (ec->udev_monitor == NULL) {
+ weston_log("failed to intialize udev monitor\n");
+ goto err_drm_source;
+ }
+ udev_monitor_filter_add_match_subsystem_devtype(ec->udev_monitor,
+ "drm", NULL);
+ ec->udev_drm_source =
+ wl_event_loop_add_fd(loop,
+ udev_monitor_get_fd(ec->udev_monitor),
+ WL_EVENT_READABLE, udev_drm_event, ec);
+
+ if (udev_monitor_enable_receiving(ec->udev_monitor) < 0) {
+ weston_log("failed to enable udev-monitor receiving\n");
+ goto err_udev_monitor;
+ }
+
+ udev_device_unref(drm_device);
+
+ weston_compositor_add_debug_binding(&ec->base, KEY_O,
+ planes_binding, ec);
+ weston_compositor_add_debug_binding(&ec->base, KEY_C,
+ planes_binding, ec);
+ weston_compositor_add_debug_binding(&ec->base, KEY_V,
+ planes_binding, ec);
+ weston_compositor_add_debug_binding(&ec->base, KEY_Q,
+ recorder_binding, ec);
+
+ return &ec->base;
+
+err_udev_monitor:
+ wl_event_source_remove(ec->udev_drm_source);
+ udev_monitor_unref(ec->udev_monitor);
+err_drm_source:
+ wl_event_source_remove(ec->drm_source);
+ udev_input_destroy(&ec->input);
+err_sprite:
+ ec->base.renderer->destroy(&ec->base);
+ gbm_device_destroy(ec->gbm);
+ destroy_sprites(ec);
+err_udev_dev:
+ udev_device_unref(drm_device);
+err_launcher:
+ weston_launcher_destroy(ec->base.launcher);
+err_udev:
+ udev_unref(ec->udev);
+err_compositor:
+ weston_compositor_shutdown(&ec->base);
+err_base:
+ free(ec);
+ return NULL;
+}
+
+WL_EXPORT struct weston_compositor *
+backend_init(struct wl_display *display, int *argc, char *argv[],
+ struct weston_config *config)
+{
+ int connector = 0, tty = 0, use_pixman = 0;
+ const char *seat_id = default_seat;
+
+ const struct weston_option drm_options[] = {
+ { WESTON_OPTION_INTEGER, "connector", 0, &connector },
+ { WESTON_OPTION_STRING, "seat", 0, &seat_id },
+ { WESTON_OPTION_INTEGER, "tty", 0, &tty },
+ { WESTON_OPTION_BOOLEAN, "current-mode", 0, &option_current_mode },
+ { WESTON_OPTION_BOOLEAN, "use-pixman", 0, &use_pixman },
+ };
+
+ parse_options(drm_options, ARRAY_LENGTH(drm_options), argc, argv);
+
+ return drm_compositor_create(display, connector, seat_id, tty, use_pixman,
+ argc, argv, config);
+}
diff --git a/src/compositor-fbdev.c b/src/compositor-fbdev.c
new file mode 100644
index 00000000..24140eff
--- /dev/null
+++ b/src/compositor-fbdev.c
@@ -0,0 +1,973 @@
+/*
+ * Copyright © 2008-2011 Kristian Høgsberg
+ * Copyright © 2011 Intel Corporation
+ * Copyright © 2012 Raspberry Pi Foundation
+ * Copyright © 2013 Philip Withnall
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that 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.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <linux/fb.h>
+#include <linux/input.h>
+
+#include <libudev.h>
+
+#include "compositor.h"
+#include "launcher-util.h"
+#include "pixman-renderer.h"
+#include "udev-seat.h"
+#include "gl-renderer.h"
+
+struct fbdev_compositor {
+ struct weston_compositor base;
+ uint32_t prev_state;
+
+ struct udev *udev;
+ struct udev_input input;
+ int use_pixman;
+ struct wl_listener session_listener;
+};
+
+struct fbdev_screeninfo {
+ unsigned int x_resolution; /* pixels, visible area */
+ unsigned int y_resolution; /* pixels, visible area */
+ unsigned int width_mm; /* visible screen width in mm */
+ unsigned int height_mm; /* visible screen height in mm */
+ unsigned int bits_per_pixel;
+
+ size_t buffer_length; /* length of frame buffer memory in bytes */
+ size_t line_length; /* length of a line in bytes */
+ char id[16]; /* screen identifier */
+
+ pixman_format_code_t pixel_format; /* frame buffer pixel format */
+ unsigned int refresh_rate; /* Hertz */
+};
+
+struct fbdev_output {
+ struct fbdev_compositor *compositor;
+ struct weston_output base;
+
+ struct weston_mode mode;
+ struct wl_event_source *finish_frame_timer;
+
+ /* Frame buffer details. */
+ const char *device; /* ownership shared with fbdev_parameters */
+ struct fbdev_screeninfo fb_info;
+ void *fb; /* length is fb_info.buffer_length */
+
+ /* pixman details. */
+ pixman_image_t *hw_surface;
+ pixman_image_t *shadow_surface;
+ void *shadow_buf;
+ uint8_t depth;
+};
+
+struct fbdev_parameters {
+ int tty;
+ char *device;
+ int use_gl;
+};
+
+static const char default_seat[] = "seat0";
+
+static inline struct fbdev_output *
+to_fbdev_output(struct weston_output *base)
+{
+ return container_of(base, struct fbdev_output, base);
+}
+
+static inline struct fbdev_compositor *
+to_fbdev_compositor(struct weston_compositor *base)
+{
+ return container_of(base, struct fbdev_compositor, base);
+}
+
+static void
+fbdev_output_start_repaint_loop(struct weston_output *output)
+{
+ uint32_t msec;
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+ msec = tv.tv_sec * 1000 + tv.tv_usec / 1000;
+ weston_output_finish_frame(output, msec);
+}
+
+static void
+fbdev_output_repaint_pixman(struct weston_output *base, pixman_region32_t *damage)
+{
+ struct fbdev_output *output = to_fbdev_output(base);
+ struct weston_compositor *ec = output->base.compositor;
+ pixman_box32_t *rects;
+ int nrects, i, src_x, src_y, x1, y1, x2, y2, width, height;
+
+ /* Repaint the damaged region onto the back buffer. */
+ pixman_renderer_output_set_buffer(base, output->shadow_surface);
+ ec->renderer->repaint_output(base, damage);
+
+ /* Transform and composite onto the frame buffer. */
+ width = pixman_image_get_width(output->shadow_surface);
+ height = pixman_image_get_height(output->shadow_surface);
+ rects = pixman_region32_rectangles(damage, &nrects);
+
+ for (i = 0; i < nrects; i++) {
+ switch (base->transform) {
+ default:
+ case WL_OUTPUT_TRANSFORM_NORMAL:
+ x1 = rects[i].x1;
+ x2 = rects[i].x2;
+ y1 = rects[i].y1;
+ y2 = rects[i].y2;
+ break;
+ case WL_OUTPUT_TRANSFORM_180:
+ x1 = width - rects[i].x2;
+ x2 = width - rects[i].x1;
+ y1 = height - rects[i].y2;
+ y2 = height - rects[i].y1;
+ break;
+ case WL_OUTPUT_TRANSFORM_90:
+ x1 = height - rects[i].y2;
+ x2 = height - rects[i].y1;
+ y1 = rects[i].x1;
+ y2 = rects[i].x2;
+ break;
+ case WL_OUTPUT_TRANSFORM_270:
+ x1 = rects[i].y1;
+ x2 = rects[i].y2;
+ y1 = width - rects[i].x2;
+ y2 = width - rects[i].x1;
+ break;
+ }
+ src_x = x1;
+ src_y = y1;
+
+ pixman_image_composite32(PIXMAN_OP_SRC,
+ output->shadow_surface, /* src */
+ NULL /* mask */,
+ output->hw_surface, /* dest */
+ src_x, src_y, /* src_x, src_y */
+ 0, 0, /* mask_x, mask_y */
+ x1, y1, /* dest_x, dest_y */
+ x2 - x1, /* width */
+ y2 - y1 /* height */);
+ }
+
+ /* Update the damage region. */
+ pixman_region32_subtract(&ec->primary_plane.damage,
+ &ec->primary_plane.damage, damage);
+
+ /* Schedule the end of the frame. We do not sync this to the frame
+ * buffer clock because users who want that should be using the DRM
+ * compositor. FBIO_WAITFORVSYNC blocks and FB_ACTIVATE_VBL requires
+ * panning, which is broken in most kernel drivers.
+ *
+ * Finish the frame synchronised to the specified refresh rate. The
+ * refresh rate is given in mHz and the interval in ms. */
+ wl_event_source_timer_update(output->finish_frame_timer,
+ 1000000 / output->mode.refresh);
+}
+
+static int
+fbdev_output_repaint(struct weston_output *base, pixman_region32_t *damage)
+{
+ struct fbdev_output *output = to_fbdev_output(base);
+ struct fbdev_compositor *fbc = output->compositor;
+ struct weston_compositor *ec = & fbc->base;
+
+ if (fbc->use_pixman) {
+ fbdev_output_repaint_pixman(base,damage);
+ } else {
+ ec->renderer->repaint_output(base, damage);
+ /* Update the damage region. */
+ pixman_region32_subtract(&ec->primary_plane.damage,
+ &ec->primary_plane.damage, damage);
+
+ wl_event_source_timer_update(output->finish_frame_timer,
+ 1000000 / output->mode.refresh);
+ }
+
+ return 0;
+}
+
+static int
+finish_frame_handler(void *data)
+{
+ struct fbdev_output *output = data;
+
+ fbdev_output_start_repaint_loop(&output->base);
+
+ return 1;
+}
+
+static pixman_format_code_t
+calculate_pixman_format(struct fb_var_screeninfo *vinfo,
+ struct fb_fix_screeninfo *finfo)
+{
+ /* Calculate the pixman format supported by the frame buffer from the
+ * buffer's metadata. Return 0 if no known pixman format is supported
+ * (since this has depth 0 it's guaranteed to not conflict with any
+ * actual pixman format).
+ *
+ * Documentation on the vinfo and finfo structures:
+ * http://www.mjmwired.net/kernel/Documentation/fb/api.txt
+ *
+ * TODO: Try a bit harder to support other formats, including setting
+ * the preferred format in the hardware. */
+ int type;
+
+ weston_log("Calculating pixman format from:\n"
+ STAMP_SPACE " - type: %i (aux: %i)\n"
+ STAMP_SPACE " - visual: %i\n"
+ STAMP_SPACE " - bpp: %i (grayscale: %i)\n"
+ STAMP_SPACE " - red: offset: %i, length: %i, MSB: %i\n"
+ STAMP_SPACE " - green: offset: %i, length: %i, MSB: %i\n"
+ STAMP_SPACE " - blue: offset: %i, length: %i, MSB: %i\n"
+ STAMP_SPACE " - transp: offset: %i, length: %i, MSB: %i\n",
+ finfo->type, finfo->type_aux, finfo->visual,
+ vinfo->bits_per_pixel, vinfo->grayscale,
+ vinfo->red.offset, vinfo->red.length, vinfo->red.msb_right,
+ vinfo->green.offset, vinfo->green.length,
+ vinfo->green.msb_right,
+ vinfo->blue.offset, vinfo->blue.length,
+ vinfo->blue.msb_right,
+ vinfo->transp.offset, vinfo->transp.length,
+ vinfo->transp.msb_right);
+
+ /* We only handle packed formats at the moment. */
+ if (finfo->type != FB_TYPE_PACKED_PIXELS)
+ return 0;
+
+ /* We only handle true-colour frame buffers at the moment. */
+ switch(finfo->visual) {
+ case FB_VISUAL_TRUECOLOR:
+ case FB_VISUAL_DIRECTCOLOR:
+ if (vinfo->grayscale != 0)
+ return 0;
+ break;
+ default:
+ return 0;
+ }
+
+ /* We only support formats with MSBs on the left. */
+ if (vinfo->red.msb_right != 0 || vinfo->green.msb_right != 0 ||
+ vinfo->blue.msb_right != 0)
+ return 0;
+
+ /* Work out the format type from the offsets. We only support RGBA and
+ * ARGB at the moment. */
+ type = PIXMAN_TYPE_OTHER;
+
+ if ((vinfo->transp.offset >= vinfo->red.offset ||
+ vinfo->transp.length == 0) &&
+ vinfo->red.offset >= vinfo->green.offset &&
+ vinfo->green.offset >= vinfo->blue.offset)
+ type = PIXMAN_TYPE_ARGB;
+ else if (vinfo->red.offset >= vinfo->green.offset &&
+ vinfo->green.offset >= vinfo->blue.offset &&
+ vinfo->blue.offset >= vinfo->transp.offset)
+ type = PIXMAN_TYPE_RGBA;
+
+ if (type == PIXMAN_TYPE_OTHER)
+ return 0;
+
+ /* Build the format. */
+ return PIXMAN_FORMAT(vinfo->bits_per_pixel, type,
+ vinfo->transp.length,
+ vinfo->red.length,
+ vinfo->green.length,
+ vinfo->blue.length);
+}
+
+static int
+calculate_refresh_rate(struct fb_var_screeninfo *vinfo)
+{
+ uint64_t quot;
+
+ /* Calculate monitor refresh rate. Default is 60 Hz. Units are mHz. */
+ quot = (vinfo->upper_margin + vinfo->lower_margin + vinfo->yres);
+ quot *= (vinfo->left_margin + vinfo->right_margin + vinfo->xres);
+ quot *= vinfo->pixclock;
+
+ if (quot > 0) {
+ uint64_t refresh_rate;
+
+ refresh_rate = 1000000000000000LLU / quot;
+ if (refresh_rate > 200000)
+ refresh_rate = 200000; /* cap at 200 Hz */
+
+ return refresh_rate;
+ }
+
+ return 60 * 1000; /* default to 60 Hz */
+}
+
+static int
+fbdev_query_screen_info(struct fbdev_output *output, int fd,
+ struct fbdev_screeninfo *info)
+{
+ struct fb_var_screeninfo varinfo;
+ struct fb_fix_screeninfo fixinfo;
+
+ /* Probe the device for screen information. */
+ if (ioctl(fd, FBIOGET_FSCREENINFO, &fixinfo) < 0 ||
+ ioctl(fd, FBIOGET_VSCREENINFO, &varinfo) < 0) {
+ return -1;
+ }
+
+ /* Store the pertinent data. */
+ info->x_resolution = varinfo.xres;
+ info->y_resolution = varinfo.yres;
+ info->width_mm = varinfo.width;
+ info->height_mm = varinfo.height;
+ info->bits_per_pixel = varinfo.bits_per_pixel;
+
+ info->buffer_length = fixinfo.smem_len;
+ info->line_length = fixinfo.line_length;
+ strncpy(info->id, fixinfo.id, sizeof(info->id) / sizeof(*info->id));
+
+ info->pixel_format = calculate_pixman_format(&varinfo, &fixinfo);
+ info->refresh_rate = calculate_refresh_rate(&varinfo);
+
+ if (info->pixel_format == 0) {
+ weston_log("Frame buffer uses an unsupported format.\n");
+ return -1;
+ }
+
+ return 1;
+}
+
+static int
+fbdev_set_screen_info(struct fbdev_output *output, int fd,
+ struct fbdev_screeninfo *info)
+{
+ struct fb_var_screeninfo varinfo;
+
+ /* Grab the current screen information. */
+ if (ioctl(fd, FBIOGET_VSCREENINFO, &varinfo) < 0) {
+ return -1;
+ }
+
+ /* Update the information. */
+ varinfo.xres = info->x_resolution;
+ varinfo.yres = info->y_resolution;
+ varinfo.width = info->width_mm;
+ varinfo.height = info->height_mm;
+ varinfo.bits_per_pixel = info->bits_per_pixel;
+
+ /* Try to set up an ARGB (x8r8g8b8) pixel format. */
+ varinfo.grayscale = 0;
+ varinfo.transp.offset = 24;
+ varinfo.transp.length = 0;
+ varinfo.transp.msb_right = 0;
+ varinfo.red.offset = 16;
+ varinfo.red.length = 8;
+ varinfo.red.msb_right = 0;
+ varinfo.green.offset = 8;
+ varinfo.green.length = 8;
+ varinfo.green.msb_right = 0;
+ varinfo.blue.offset = 0;
+ varinfo.blue.length = 8;
+ varinfo.blue.msb_right = 0;
+
+ /* Set the device's screen information. */
+ if (ioctl(fd, FBIOPUT_VSCREENINFO, &varinfo) < 0) {
+ return -1;
+ }
+
+ return 1;
+}
+
+static void fbdev_frame_buffer_destroy(struct fbdev_output *output);
+
+/* Returns an FD for the frame buffer device. */
+static int
+fbdev_frame_buffer_open(struct fbdev_output *output, const char *fb_dev,
+ struct fbdev_screeninfo *screen_info)
+{
+ int fd = -1;
+
+ weston_log("Opening fbdev frame buffer.\n");
+
+ /* Open the frame buffer device. */
+ fd = open(fb_dev, O_RDWR | O_CLOEXEC);
+ if (fd < 0) {
+ weston_log("Failed to open frame buffer device ‘%s’: %s\n",
+ fb_dev, strerror(errno));
+ return -1;
+ }
+
+ /* Grab the screen info. */
+ if (fbdev_query_screen_info(output, fd, screen_info) < 0) {
+ weston_log("Failed to get frame buffer info: %s\n",
+ strerror(errno));
+
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+/* Closes the FD on success or failure. */
+static int
+fbdev_frame_buffer_map(struct fbdev_output *output, int fd)
+{
+ int retval = -1;
+
+ weston_log("Mapping fbdev frame buffer.\n");
+
+ /* Map the frame buffer. Write-only mode, since we don't want to read
+ * anything back (because it's slow). */
+ output->fb = mmap(NULL, output->fb_info.buffer_length,
+ PROT_WRITE, MAP_SHARED, fd, 0);
+ if (output->fb == MAP_FAILED) {
+ weston_log("Failed to mmap frame buffer: %s\n",
+ strerror(errno));
+ goto out_close;
+ }
+
+ /* Create a pixman image to wrap the memory mapped frame buffer. */
+ output->hw_surface =
+ pixman_image_create_bits(output->fb_info.pixel_format,
+ output->fb_info.x_resolution,
+ output->fb_info.y_resolution,
+ output->fb,
+ output->fb_info.line_length);
+ if (output->hw_surface == NULL) {
+ weston_log("Failed to create surface for frame buffer.\n");
+ goto out_unmap;
+ }
+
+ /* Success! */
+ retval = 0;
+
+out_unmap:
+ if (retval != 0 && output->fb != NULL)
+ fbdev_frame_buffer_destroy(output);
+
+out_close:
+ if (fd >= 0)
+ close(fd);
+
+ return retval;
+}
+
+static void
+fbdev_frame_buffer_destroy(struct fbdev_output *output)
+{
+ weston_log("Destroying fbdev frame buffer.\n");
+
+ if (munmap(output->fb, output->fb_info.buffer_length) < 0)
+ weston_log("Failed to munmap frame buffer: %s\n",
+ strerror(errno));
+
+ output->fb = NULL;
+}
+
+static void fbdev_output_destroy(struct weston_output *base);
+static void fbdev_output_disable(struct weston_output *base);
+
+static int
+fbdev_output_create(struct fbdev_compositor *compositor,
+ const char *device)
+{
+ struct fbdev_output *output;
+ pixman_transform_t transform;
+ int fb_fd;
+ int shadow_width, shadow_height;
+ int width, height;
+ unsigned int bytes_per_pixel;
+ struct wl_event_loop *loop;
+
+ weston_log("Creating fbdev output.\n");
+
+ output = calloc(1, sizeof *output);
+ if (!output)
+ return -1;
+
+ output->compositor = compositor;
+ output->device = device;
+
+ /* Create the frame buffer. */
+ fb_fd = fbdev_frame_buffer_open(output, device, &output->fb_info);
+ if (fb_fd < 0) {
+ weston_log("Creating frame buffer failed.\n");
+ goto out_free;
+ }
+ if (compositor->use_pixman) {
+ if (fbdev_frame_buffer_map(output, fb_fd) < 0) {
+ weston_log("Mapping frame buffer failed.\n");
+ goto out_free;
+ }
+ } else {
+ close(fb_fd);
+ }
+
+ output->base.start_repaint_loop = fbdev_output_start_repaint_loop;
+ output->base.repaint = fbdev_output_repaint;
+ output->base.destroy = fbdev_output_destroy;
+ output->base.assign_planes = NULL;
+ output->base.set_backlight = NULL;
+ output->base.set_dpms = NULL;
+ output->base.switch_mode = NULL;
+
+ /* only one static mode in list */
+ output->mode.flags =
+ WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
+ output->mode.width = output->fb_info.x_resolution;
+ output->mode.height = output->fb_info.y_resolution;
+ output->mode.refresh = output->fb_info.refresh_rate;
+ wl_list_init(&output->base.mode_list);
+ wl_list_insert(&output->base.mode_list, &output->mode.link);
+
+ output->base.current_mode = &output->mode;
+ output->base.subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN;
+ output->base.make = "unknown";
+ output->base.model = output->fb_info.id;
+
+ weston_output_init(&output->base, &compositor->base,
+ 0, 0, output->fb_info.width_mm,
+ output->fb_info.height_mm,
+ WL_OUTPUT_TRANSFORM_NORMAL,
+ 1);
+
+ width = output->fb_info.x_resolution;
+ height = output->fb_info.y_resolution;
+
+ pixman_transform_init_identity(&transform);
+ switch (output->base.transform) {
+ default:
+ case WL_OUTPUT_TRANSFORM_NORMAL:
+ shadow_width = width;
+ shadow_height = height;
+ pixman_transform_rotate(&transform,
+ NULL, 0, 0);
+ pixman_transform_translate(&transform, NULL,
+ 0, 0);
+ break;
+ case WL_OUTPUT_TRANSFORM_180:
+ shadow_width = width;
+ shadow_height = height;
+ pixman_transform_rotate(&transform,
+ NULL, -pixman_fixed_1, 0);
+ pixman_transform_translate(NULL, &transform,
+ pixman_int_to_fixed(shadow_width),
+ pixman_int_to_fixed(shadow_height));
+ break;
+ case WL_OUTPUT_TRANSFORM_270:
+ shadow_width = height;
+ shadow_height = width;
+ pixman_transform_rotate(&transform,
+ NULL, 0, pixman_fixed_1);
+ pixman_transform_translate(&transform,
+ NULL,
+ pixman_int_to_fixed(shadow_width),
+ 0);
+ break;
+ case WL_OUTPUT_TRANSFORM_90:
+ shadow_width = height;
+ shadow_height = width;
+ pixman_transform_rotate(&transform,
+ NULL, 0, -pixman_fixed_1);
+ pixman_transform_translate(&transform,
+ NULL,
+ 0,
+ pixman_int_to_fixed(shadow_height));
+ break;
+ }
+
+ bytes_per_pixel = output->fb_info.bits_per_pixel / 8;
+
+ output->shadow_buf = malloc(width * height * bytes_per_pixel);
+ output->shadow_surface =
+ pixman_image_create_bits(output->fb_info.pixel_format,
+ shadow_width, shadow_height,
+ output->shadow_buf,
+ shadow_width * bytes_per_pixel);
+ if (output->shadow_buf == NULL || output->shadow_surface == NULL) {
+ weston_log("Failed to create surface for frame buffer.\n");
+ goto out_hw_surface;
+ }
+
+ /* No need in transform for normal output */
+ if (output->base.transform != WL_OUTPUT_TRANSFORM_NORMAL)
+ pixman_image_set_transform(output->shadow_surface, &transform);
+
+ if (compositor->use_pixman) {
+ if (pixman_renderer_output_create(&output->base) < 0)
+ goto out_shadow_surface;
+ } else {
+ setenv("HYBRIS_EGLPLATFORM", "wayland", 1);
+ if (gl_renderer_output_create(&output->base,
+ (EGLNativeWindowType)NULL) < 0) {
+ weston_log("gl_renderer_output_create failed.\n");
+ goto out_shadow_surface;
+ }
+ }
+
+
+ loop = wl_display_get_event_loop(compositor->base.wl_display);
+ output->finish_frame_timer =
+ wl_event_loop_add_timer(loop, finish_frame_handler, output);
+
+ wl_list_insert(compositor->base.output_list.prev, &output->base.link);
+
+ weston_log("fbdev output %d×%d px\n",
+ output->mode.width, output->mode.height);
+ weston_log_continue(STAMP_SPACE "guessing %d Hz and 96 dpi\n",
+ output->mode.refresh / 1000);
+
+ return 0;
+
+out_shadow_surface:
+ pixman_image_unref(output->shadow_surface);
+ output->shadow_surface = NULL;
+out_hw_surface:
+ free(output->shadow_buf);
+ pixman_image_unref(output->hw_surface);
+ output->hw_surface = NULL;
+ weston_output_destroy(&output->base);
+ fbdev_frame_buffer_destroy(output);
+out_free:
+ free(output);
+
+ return -1;
+}
+
+static void
+fbdev_output_destroy(struct weston_output *base)
+{
+ struct fbdev_output *output = to_fbdev_output(base);
+ struct fbdev_compositor *compositor = output->compositor;
+
+ weston_log("Destroying fbdev output.\n");
+
+ /* Close the frame buffer. */
+ fbdev_output_disable(base);
+
+ if (compositor->use_pixman) {
+ if (base->renderer_state != NULL)
+ pixman_renderer_output_destroy(base);
+
+ if (output->shadow_surface != NULL) {
+ pixman_image_unref(output->shadow_surface);
+ output->shadow_surface = NULL;
+ }
+
+ if (output->shadow_buf != NULL) {
+ free(output->shadow_buf);
+ output->shadow_buf = NULL;
+ }
+ } else {
+ gl_renderer_output_destroy(base);
+ }
+
+ /* Remove the output. */
+ wl_list_remove(&output->base.link);
+ weston_output_destroy(&output->base);
+
+ free(output);
+}
+
+/* strcmp()-style return values. */
+static int
+compare_screen_info (const struct fbdev_screeninfo *a,
+ const struct fbdev_screeninfo *b)
+{
+ if (a->x_resolution == b->x_resolution &&
+ a->y_resolution == b->y_resolution &&
+ a->width_mm == b->width_mm &&
+ a->height_mm == b->height_mm &&
+ a->bits_per_pixel == b->bits_per_pixel &&
+ a->pixel_format == b->pixel_format &&
+ a->refresh_rate == b->refresh_rate)
+ return 0;
+
+ return 1;
+}
+
+static int
+fbdev_output_reenable(struct fbdev_compositor *compositor,
+ struct weston_output *base)
+{
+ struct fbdev_output *output = to_fbdev_output(base);
+ struct fbdev_screeninfo new_screen_info;
+ int fb_fd;
+ const char *device;
+
+ weston_log("Re-enabling fbdev output.\n");
+
+ /* Create the frame buffer. */
+ fb_fd = fbdev_frame_buffer_open(output, output->device,
+ &new_screen_info);
+ if (fb_fd < 0) {
+ weston_log("Creating frame buffer failed.\n");
+ goto err;
+ }
+
+ /* Check whether the frame buffer details have changed since we were
+ * disabled. */
+ if (compare_screen_info (&output->fb_info, &new_screen_info) != 0) {
+ /* Perform a mode-set to restore the old mode. */
+ if (fbdev_set_screen_info(output, fb_fd,
+ &output->fb_info) < 0) {
+ weston_log("Failed to restore mode settings. "
+ "Attempting to re-open output anyway.\n");
+ }
+
+ close(fb_fd);
+
+ /* Remove and re-add the output so that resources depending on
+ * the frame buffer X/Y resolution (such as the shadow buffer)
+ * are re-initialised. */
+ device = output->device;
+ fbdev_output_destroy(base);
+ fbdev_output_create(compositor, device);
+
+ return 0;
+ }
+
+ /* Map the device if it has the same details as before. */
+ if (compositor->use_pixman) {
+ if (fbdev_frame_buffer_map(output, fb_fd) < 0) {
+ weston_log("Mapping frame buffer failed.\n");
+ goto err;
+ }
+ }
+
+ return 0;
+
+err:
+ return -1;
+}
+
+/* NOTE: This leaves output->fb_info populated, caching data so that if
+ * fbdev_output_reenable() is called again, it can determine whether a mode-set
+ * is needed. */
+static void
+fbdev_output_disable(struct weston_output *base)
+{
+ struct fbdev_output *output = to_fbdev_output(base);
+ struct fbdev_compositor *compositor = output->compositor;
+
+ weston_log("Disabling fbdev output.\n");
+
+ if ( ! compositor->use_pixman) return;
+
+ if (output->hw_surface != NULL) {
+ pixman_image_unref(output->hw_surface);
+ output->hw_surface = NULL;
+ }
+
+ fbdev_frame_buffer_destroy(output);
+}
+
+static void
+fbdev_compositor_destroy(struct weston_compositor *base)
+{
+ struct fbdev_compositor *compositor = to_fbdev_compositor(base);
+
+ udev_input_destroy(&compositor->input);
+
+ /* Destroy the output. */
+ weston_compositor_shutdown(&compositor->base);
+
+ /* Chain up. */
+ compositor->base.renderer->destroy(&compositor->base);
+ weston_launcher_destroy(compositor->base.launcher);
+
+ free(compositor);
+}
+
+static void
+session_notify(struct wl_listener *listener, void *data)
+{
+ struct fbdev_compositor *compositor = data;
+ struct weston_output *output;
+
+ if (compositor->base.session_active) {
+ weston_log("entering VT\n");
+ compositor->base.focus = 1;
+ compositor->base.state = compositor->prev_state;
+
+ wl_list_for_each(output, &compositor->base.output_list, link) {
+ fbdev_output_reenable(compositor, output);
+ }
+
+ weston_compositor_damage_all(&compositor->base);
+
+ udev_input_enable(&compositor->input, compositor->udev);
+ } else {
+ weston_log("leaving VT\n");
+ udev_input_disable(&compositor->input);
+
+ wl_list_for_each(output, &compositor->base.output_list, link) {
+ fbdev_output_disable(output);
+ }
+
+ compositor->base.focus = 0;
+ compositor->prev_state = compositor->base.state;
+ weston_compositor_offscreen(&compositor->base);
+
+ /* If we have a repaint scheduled (from the idle handler), make
+ * sure we cancel that so we don't try to pageflip when we're
+ * vt switched away. The OFFSCREEN state will prevent
+ * further attemps at repainting. When we switch
+ * back, we schedule a repaint, which will process
+ * pending frame callbacks. */
+
+ wl_list_for_each(output,
+ &compositor->base.output_list, link) {
+ output->repaint_needed = 0;
+ }
+ };
+}
+
+static void
+fbdev_restore(struct weston_compositor *compositor)
+{
+ weston_launcher_restore(compositor->launcher);
+}
+
+static void
+switch_vt_binding(struct weston_seat *seat, uint32_t time, uint32_t key, void *data)
+{
+ struct weston_compositor *compositor = data;
+
+ weston_launcher_activate_vt(compositor->launcher, key - KEY_F1 + 1);
+}
+
+static struct weston_compositor *
+fbdev_compositor_create(struct wl_display *display, int *argc, char *argv[],
+ struct weston_config *config,
+ struct fbdev_parameters *param)
+{
+ struct fbdev_compositor *compositor;
+ const char *seat_id = default_seat;
+ uint32_t key;
+
+ weston_log("initializing fbdev backend\n");
+
+ compositor = calloc(1, sizeof *compositor);
+ if (compositor == NULL)
+ return NULL;
+
+ if (weston_compositor_init(&compositor->base, display, argc, argv,
+ config) < 0)
+ goto out_free;
+
+ compositor->udev = udev_new();
+ if (compositor->udev == NULL) {
+ weston_log("Failed to initialize udev context.\n");
+ goto out_compositor;
+ }
+
+ /* Set up the TTY. */
+ compositor->session_listener.notify = session_notify;
+ wl_signal_add(&compositor->base.session_signal,
+ &compositor->session_listener);
+ compositor->base.launcher =
+ weston_launcher_connect(&compositor->base, param->tty);
+ if (!compositor->base.launcher) {
+ weston_log("fatal: fbdev backend should be run "
+ "using weston-launch binary or as root\n");
+ goto out_udev;
+ }
+
+ compositor->base.destroy = fbdev_compositor_destroy;
+ compositor->base.restore = fbdev_restore;
+
+ compositor->base.focus = 1;
+ compositor->prev_state = WESTON_COMPOSITOR_ACTIVE;
+ compositor->use_pixman = !param->use_gl;
+
+ for (key = KEY_F1; key < KEY_F9; key++)
+ weston_compositor_add_key_binding(&compositor->base, key,
+ MODIFIER_CTRL | MODIFIER_ALT,
+ switch_vt_binding,
+ compositor);
+ if (compositor->use_pixman) {
+ if (pixman_renderer_init(&compositor->base) < 0)
+ goto out_launcher;
+ } else {
+ if (gl_renderer_create(&compositor->base, EGL_DEFAULT_DISPLAY,
+ gl_renderer_opaque_attribs, NULL) < 0) {
+ weston_log("gl_renderer_create failed.\n");
+ goto out_launcher;
+ }
+ }
+
+ if (fbdev_output_create(compositor, param->device) < 0)
+ goto out_pixman;
+
+ udev_input_init(&compositor->input, &compositor->base, compositor->udev, seat_id);
+
+ return &compositor->base;
+
+out_pixman:
+ compositor->base.renderer->destroy(&compositor->base);
+
+out_launcher:
+ weston_launcher_destroy(compositor->base.launcher);
+
+out_udev:
+ udev_unref(compositor->udev);
+
+out_compositor:
+ weston_compositor_shutdown(&compositor->base);
+
+out_free:
+ free(compositor);
+
+ return NULL;
+}
+
+WL_EXPORT struct weston_compositor *
+backend_init(struct wl_display *display, int *argc, char *argv[],
+ struct weston_config *config)
+{
+ /* TODO: Ideally, available frame buffers should be enumerated using
+ * udev, rather than passing a device node in as a parameter. */
+ struct fbdev_parameters param = {
+ .tty = 0, /* default to current tty */
+ .device = "/dev/fb0", /* default frame buffer */
+ .use_gl = 0,
+ };
+
+ const struct weston_option fbdev_options[] = {
+ { WESTON_OPTION_INTEGER, "tty", 0, &param.tty },
+ { WESTON_OPTION_STRING, "device", 0, &param.device },
+ { WESTON_OPTION_BOOLEAN, "use-gl", 0, &param.use_gl },
+ };
+
+ parse_options(fbdev_options, ARRAY_LENGTH(fbdev_options), argc, argv);
+
+ return fbdev_compositor_create(display, argc, argv, config, &param);
+}
diff --git a/src/compositor-headless.c b/src/compositor-headless.c
new file mode 100644
index 00000000..9d9f6dd3
--- /dev/null
+++ b/src/compositor-headless.c
@@ -0,0 +1,206 @@
+/*
+ * Copyright © 2010-2011 Benjamin Franzke
+ * Copyright © 2012 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 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.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include "compositor.h"
+
+struct headless_compositor {
+ struct weston_compositor base;
+ struct weston_seat fake_seat;
+};
+
+struct headless_output {
+ struct weston_output base;
+ struct weston_mode mode;
+ struct wl_event_source *finish_frame_timer;
+};
+
+
+static void
+headless_output_start_repaint_loop(struct weston_output *output)
+{
+ uint32_t msec;
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+ msec = tv.tv_sec * 1000 + tv.tv_usec / 1000;
+ weston_output_finish_frame(output, msec);
+}
+
+static int
+finish_frame_handler(void *data)
+{
+ headless_output_start_repaint_loop(data);
+
+ return 1;
+}
+
+static int
+headless_output_repaint(struct weston_output *output_base,
+ pixman_region32_t *damage)
+{
+ struct headless_output *output = (struct headless_output *) output_base;
+ struct weston_compositor *ec = output->base.compositor;
+
+ ec->renderer->repaint_output(&output->base, damage);
+
+ pixman_region32_subtract(&ec->primary_plane.damage,
+ &ec->primary_plane.damage, damage);
+
+ wl_event_source_timer_update(output->finish_frame_timer, 16);
+
+ return 0;
+}
+
+static void
+headless_output_destroy(struct weston_output *output_base)
+{
+ struct headless_output *output = (struct headless_output *) output_base;
+
+ wl_event_source_remove(output->finish_frame_timer);
+ free(output);
+
+ return;
+}
+
+static int
+headless_compositor_create_output(struct headless_compositor *c,
+ int width, int height)
+{
+ struct headless_output *output;
+ struct wl_event_loop *loop;
+
+ output = zalloc(sizeof *output);
+ if (output == NULL)
+ return -1;
+
+ output->mode.flags =
+ WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
+ output->mode.width = width;
+ output->mode.height = height;
+ output->mode.refresh = 60;
+ wl_list_init(&output->base.mode_list);
+ wl_list_insert(&output->base.mode_list, &output->mode.link);
+
+ output->base.current_mode = &output->mode;
+ weston_output_init(&output->base, &c->base, 0, 0, width, height,
+ WL_OUTPUT_TRANSFORM_NORMAL, 1);
+
+ output->base.make = "weston";
+ output->base.model = "headless";
+
+ weston_output_move(&output->base, 0, 0);
+
+ loop = wl_display_get_event_loop(c->base.wl_display);
+ output->finish_frame_timer =
+ wl_event_loop_add_timer(loop, finish_frame_handler, output);
+
+ output->base.start_repaint_loop = headless_output_start_repaint_loop;
+ output->base.repaint = headless_output_repaint;
+ output->base.destroy = headless_output_destroy;
+ output->base.assign_planes = NULL;
+ output->base.set_backlight = NULL;
+ output->base.set_dpms = NULL;
+ output->base.switch_mode = NULL;
+
+ wl_list_insert(c->base.output_list.prev, &output->base.link);
+
+ return 0;
+}
+
+static void
+headless_restore(struct weston_compositor *ec)
+{
+}
+
+static void
+headless_destroy(struct weston_compositor *ec)
+{
+ struct headless_compositor *c = (struct headless_compositor *) ec;
+
+ ec->renderer->destroy(ec);
+
+ weston_seat_release(&c->fake_seat);
+ weston_compositor_shutdown(ec);
+
+ free(ec);
+}
+
+static struct weston_compositor *
+headless_compositor_create(struct wl_display *display,
+ int width, int height, const char *display_name,
+ int *argc, char *argv[],
+ struct weston_config *config)
+{
+ struct headless_compositor *c;
+
+ c = zalloc(sizeof *c);
+ if (c == NULL)
+ return NULL;
+
+ if (weston_compositor_init(&c->base, display, argc, argv, config) < 0)
+ goto err_free;
+
+ weston_seat_init(&c->fake_seat, &c->base, "default");
+
+ c->base.destroy = headless_destroy;
+ c->base.restore = headless_restore;
+
+ if (headless_compositor_create_output(c, width, height) < 0)
+ goto err_compositor;
+
+ if (noop_renderer_init(&c->base) < 0)
+ goto err_compositor;
+
+ return &c->base;
+
+err_compositor:
+ weston_compositor_shutdown(&c->base);
+err_free:
+ free(c);
+ return NULL;
+}
+
+WL_EXPORT struct weston_compositor *
+backend_init(struct wl_display *display, int *argc, char *argv[],
+ struct weston_config *config)
+{
+ int width = 1024, height = 640;
+ char *display_name = NULL;
+
+ const struct weston_option headless_options[] = {
+ { WESTON_OPTION_INTEGER, "width", 0, &width },
+ { WESTON_OPTION_INTEGER, "height", 0, &height },
+ };
+
+ parse_options(headless_options,
+ ARRAY_LENGTH(headless_options), argc, argv);
+
+ return headless_compositor_create(display, width, height, display_name,
+ argc, argv, config);
+}
diff --git a/src/compositor-rdp.c b/src/compositor-rdp.c
new file mode 100644
index 00000000..8a302f85
--- /dev/null
+++ b/src/compositor-rdp.c
@@ -0,0 +1,1094 @@
+/*
+ * Copyright © 2013 Hardening <rdp.effort@gmail.com>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that 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.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <linux/input.h>
+
+#include <freerdp/freerdp.h>
+#include <freerdp/listener.h>
+#include <freerdp/update.h>
+#include <freerdp/input.h>
+#include <freerdp/codec/color.h>
+#include <freerdp/codec/rfx.h>
+#include <freerdp/codec/nsc.h>
+#include <winpr/input.h>
+
+#include "compositor.h"
+#include "pixman-renderer.h"
+
+#define MAX_FREERDP_FDS 32
+#define DEFAULT_AXIS_STEP_DISTANCE wl_fixed_from_int(10)
+
+struct rdp_compositor_config {
+ int width;
+ int height;
+ char *bind_address;
+ int port;
+ char *rdp_key;
+ char *server_cert;
+ char *server_key;
+ char *extra_modes;
+ int env_socket;
+};
+
+struct rdp_output;
+
+struct rdp_compositor {
+ struct weston_compositor base;
+
+ freerdp_listener *listener;
+ struct wl_event_source *listener_events[MAX_FREERDP_FDS];
+ struct rdp_output *output;
+
+ char *server_cert;
+ char *server_key;
+ char *rdp_key;
+ int tls_enabled;
+};
+
+enum peer_item_flags {
+ RDP_PEER_ACTIVATED = (1 << 0),
+ RDP_PEER_OUTPUT_ENABLED = (1 << 1),
+};
+
+struct rdp_peers_item {
+ int flags;
+ freerdp_peer *peer;
+ struct weston_seat seat;
+
+ struct wl_list link;
+};
+
+struct rdp_output {
+ struct weston_output base;
+ struct wl_event_source *finish_frame_timer;
+ pixman_image_t *shadow_surface;
+
+ struct wl_list peers;
+};
+
+struct rdp_peer_context {
+ rdpContext _p;
+
+ struct rdp_compositor *rdpCompositor;
+ struct wl_event_source *events[MAX_FREERDP_FDS];
+ RFX_CONTEXT *rfx_context;
+ wStream *encode_stream;
+ RFX_RECT *rfx_rects;
+ NSC_CONTEXT *nsc_context;
+
+ struct rdp_peers_item item;
+};
+typedef struct rdp_peer_context RdpPeerContext;
+
+static void
+rdp_compositor_config_init(struct rdp_compositor_config *config) {
+ config->width = 640;
+ config->height = 480;
+ config->bind_address = NULL;
+ config->port = 3389;
+ config->rdp_key = NULL;
+ config->server_cert = NULL;
+ config->server_key = NULL;
+ config->extra_modes = NULL;
+ config->env_socket = 0;
+}
+
+static void
+rdp_peer_refresh_rfx(pixman_region32_t *damage, pixman_image_t *image, freerdp_peer *peer)
+{
+ int width, height, nrects, i;
+ pixman_box32_t *region, *rects;
+ uint32_t *ptr;
+ RFX_RECT *rfxRect;
+ rdpUpdate *update = peer->update;
+ SURFACE_BITS_COMMAND *cmd = &update->surface_bits_command;
+ RdpPeerContext *context = (RdpPeerContext *)peer->context;
+
+ Stream_Clear(context->encode_stream);
+ Stream_SetPosition(context->encode_stream, 0);
+
+ width = (damage->extents.x2 - damage->extents.x1);
+ height = (damage->extents.y2 - damage->extents.y1);
+
+ cmd->destLeft = damage->extents.x1;
+ cmd->destTop = damage->extents.y1;
+ cmd->destRight = damage->extents.x2;
+ cmd->destBottom = damage->extents.y2;
+ cmd->bpp = 32;
+ cmd->codecID = peer->settings->RemoteFxCodecId;
+ cmd->width = width;
+ cmd->height = height;
+
+ ptr = pixman_image_get_data(image) + damage->extents.x1 +
+ damage->extents.y1 * (pixman_image_get_stride(image) / sizeof(uint32_t));
+
+ rects = pixman_region32_rectangles(damage, &nrects);
+ context->rfx_rects = realloc(context->rfx_rects, nrects * sizeof *rfxRect);
+
+ for (i = 0; i < nrects; i++) {
+ region = &rects[i];
+ rfxRect = &context->rfx_rects[i];
+
+ rfxRect->x = (region->x1 - damage->extents.x1);
+ rfxRect->y = (region->y1 - damage->extents.y1);
+ rfxRect->width = (region->x2 - region->x1);
+ rfxRect->height = (region->y2 - region->y1);
+ }
+
+ rfx_compose_message(context->rfx_context, context->encode_stream, context->rfx_rects, nrects,
+ (BYTE *)ptr, width, height,
+ pixman_image_get_stride(image)
+ );
+
+ cmd->bitmapDataLength = Stream_GetPosition(context->encode_stream);
+ cmd->bitmapData = Stream_Buffer(context->encode_stream);
+
+ update->SurfaceBits(update->context, cmd);
+}
+
+
+static void
+rdp_peer_refresh_nsc(pixman_region32_t *damage, pixman_image_t *image, freerdp_peer *peer)
+{
+ int width, height;
+ uint32_t *ptr;
+ rdpUpdate *update = peer->update;
+ SURFACE_BITS_COMMAND *cmd = &update->surface_bits_command;
+ RdpPeerContext *context = (RdpPeerContext *)peer->context;
+
+ Stream_Clear(context->encode_stream);
+ Stream_SetPosition(context->encode_stream, 0);
+
+ width = (damage->extents.x2 - damage->extents.x1);
+ height = (damage->extents.y2 - damage->extents.y1);
+
+ cmd->destLeft = damage->extents.x1;
+ cmd->destTop = damage->extents.y1;
+ cmd->destRight = damage->extents.x2;
+ cmd->destBottom = damage->extents.y2;
+ cmd->bpp = 32;
+ cmd->codecID = peer->settings->NSCodecId;
+ cmd->width = width;
+ cmd->height = height;
+
+ ptr = pixman_image_get_data(image) + damage->extents.x1 +
+ damage->extents.y1 * (pixman_image_get_stride(image) / sizeof(uint32_t));
+
+ nsc_compose_message(context->nsc_context, context->encode_stream, (BYTE *)ptr,
+ cmd->width, cmd->height,
+ pixman_image_get_stride(image));
+ cmd->bitmapDataLength = Stream_GetPosition(context->encode_stream);
+ cmd->bitmapData = Stream_Buffer(context->encode_stream);
+ update->SurfaceBits(update->context, cmd);
+}
+
+static void
+pixman_image_flipped_subrect(const pixman_box32_t *rect, pixman_image_t *img, BYTE *dest) {
+ int stride = pixman_image_get_stride(img);
+ int h;
+ int toCopy = (rect->x2 - rect->x1) * 4;
+ int height = (rect->y2 - rect->y1);
+ const BYTE *src = (const BYTE *)pixman_image_get_data(img);
+ src += ((rect->y2-1) * stride) + (rect->x1 * 4);
+
+ for (h = 0; h < height; h++, src -= stride, dest += toCopy)
+ memcpy(dest, src, toCopy);
+}
+
+static void
+rdp_peer_refresh_raw(pixman_region32_t *region, pixman_image_t *image, freerdp_peer *peer)
+{
+ rdpUpdate *update = peer->update;
+ SURFACE_BITS_COMMAND *cmd = &update->surface_bits_command;
+ SURFACE_FRAME_MARKER *marker = &update->surface_frame_marker;
+ pixman_box32_t *rect, subrect;
+ int nrects, i;
+ int heightIncrement, remainingHeight, top;
+
+ rect = pixman_region32_rectangles(region, &nrects);
+ if (!nrects)
+ return;
+
+ marker->frameId++;
+ marker->frameAction = SURFACECMD_FRAMEACTION_BEGIN;
+ update->SurfaceFrameMarker(peer->context, marker);
+
+ cmd->bpp = 32;
+ cmd->codecID = 0;
+
+ for (i = 0; i < nrects; i++, rect++) {
+ /*weston_log("rect(%d,%d, %d,%d)\n", rect->x1, rect->y1, rect->x2, rect->y2);*/
+ cmd->destLeft = rect->x1;
+ cmd->destRight = rect->x2;
+ cmd->width = rect->x2 - rect->x1;
+
+ heightIncrement = peer->settings->MultifragMaxRequestSize / (16 + cmd->width * 4);
+ remainingHeight = rect->y2 - rect->y1;
+ top = rect->y1;
+
+ subrect.x1 = rect->x1;
+ subrect.x2 = rect->x2;
+
+ while (remainingHeight) {
+ cmd->height = (remainingHeight > heightIncrement) ? heightIncrement : remainingHeight;
+ cmd->destTop = top;
+ cmd->destBottom = top + cmd->height;
+ cmd->bitmapDataLength = cmd->width * cmd->height * 4;
+ cmd->bitmapData = (BYTE *)realloc(cmd->bitmapData, cmd->bitmapDataLength);
+
+ subrect.y1 = top;
+ subrect.y2 = top + cmd->height;
+ pixman_image_flipped_subrect(&subrect, image, cmd->bitmapData);
+
+ /*weston_log("* sending (%d,%d, %d,%d)\n", subrect.x1, subrect.y1, subrect.x2, subrect.y2); */
+ update->SurfaceBits(peer->context, cmd);
+
+ remainingHeight -= cmd->height;
+ top += cmd->height;
+ }
+ }
+
+ marker->frameAction = SURFACECMD_FRAMEACTION_END;
+ update->SurfaceFrameMarker(peer->context, marker);
+}
+
+static void
+rdp_peer_refresh_region(pixman_region32_t *region, freerdp_peer *peer)
+{
+ RdpPeerContext *context = (RdpPeerContext *)peer->context;
+ struct rdp_output *output = context->rdpCompositor->output;
+ rdpSettings *settings = peer->settings;
+
+ if (settings->RemoteFxCodec)
+ rdp_peer_refresh_rfx(region, output->shadow_surface, peer);
+ else if (settings->NSCodec)
+ rdp_peer_refresh_nsc(region, output->shadow_surface, peer);
+ else
+ rdp_peer_refresh_raw(region, output->shadow_surface, peer);
+}
+
+static void
+rdp_output_start_repaint_loop(struct weston_output *output)
+{
+ uint32_t msec;
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+ msec = tv.tv_sec * 1000 + tv.tv_usec / 1000;
+ weston_output_finish_frame(output, msec);
+}
+
+static int
+rdp_output_repaint(struct weston_output *output_base, pixman_region32_t *damage)
+{
+ struct rdp_output *output = container_of(output_base, struct rdp_output, base);
+ struct weston_compositor *ec = output->base.compositor;
+ struct rdp_peers_item *outputPeer;
+
+ pixman_renderer_output_set_buffer(output_base, output->shadow_surface);
+ ec->renderer->repaint_output(&output->base, damage);
+
+ wl_list_for_each(outputPeer, &output->peers, link) {
+ if ((outputPeer->flags & RDP_PEER_ACTIVATED) &&
+ (outputPeer->flags & RDP_PEER_OUTPUT_ENABLED))
+ {
+ rdp_peer_refresh_region(damage, outputPeer->peer);
+ }
+ }
+
+ pixman_region32_subtract(&ec->primary_plane.damage,
+ &ec->primary_plane.damage, damage);
+
+ wl_event_source_timer_update(output->finish_frame_timer, 16);
+ return 0;
+}
+
+static void
+rdp_output_destroy(struct weston_output *output_base)
+{
+ struct rdp_output *output = (struct rdp_output *)output_base;
+
+ wl_event_source_remove(output->finish_frame_timer);
+ free(output);
+}
+
+static int
+finish_frame_handler(void *data)
+{
+ rdp_output_start_repaint_loop(data);
+
+ return 1;
+}
+
+
+static struct weston_mode *
+find_matching_mode(struct weston_output *output, struct weston_mode *target) {
+ struct weston_mode *local;
+
+ wl_list_for_each(local, &output->mode_list, link) {
+ if((local->width == target->width) && (local->height == target->height))
+ return local;
+ }
+ return 0;
+}
+
+static int
+rdp_switch_mode(struct weston_output *output, struct weston_mode *target_mode) {
+ struct rdp_output *rdpOutput = container_of(output, struct rdp_output, base);
+ struct rdp_peers_item *rdpPeer;
+ rdpSettings *settings;
+ pixman_image_t *new_shadow_buffer;
+ struct weston_mode *local_mode;
+
+ local_mode = find_matching_mode(output, target_mode);
+ if(!local_mode) {
+ weston_log("mode %dx%d not available\n", target_mode->width, target_mode->height);
+ return -ENOENT;
+ }
+
+ if(local_mode == output->current_mode)
+ return 0;
+
+ output->current_mode->flags &= ~WL_OUTPUT_MODE_CURRENT;
+
+ output->current_mode = local_mode;
+ output->current_mode->flags |= WL_OUTPUT_MODE_CURRENT;
+
+ pixman_renderer_output_destroy(output);
+ pixman_renderer_output_create(output);
+
+ new_shadow_buffer = pixman_image_create_bits(PIXMAN_x8r8g8b8, target_mode->width,
+ target_mode->height, 0, target_mode->width * 4);
+ pixman_image_composite32(PIXMAN_OP_SRC, rdpOutput->shadow_surface, 0, new_shadow_buffer,
+ 0, 0, 0, 0, 0, 0, target_mode->width, target_mode->height);
+ pixman_image_unref(rdpOutput->shadow_surface);
+ rdpOutput->shadow_surface = new_shadow_buffer;
+
+ wl_list_for_each(rdpPeer, &rdpOutput->peers, link) {
+ settings = rdpPeer->peer->settings;
+ if(!settings->DesktopResize) {
+ /* too bad this peer does not support desktop resize */
+ rdpPeer->peer->Close(rdpPeer->peer);
+ } else {
+ settings->DesktopWidth = target_mode->width;
+ settings->DesktopHeight = target_mode->height;
+ rdpPeer->peer->update->DesktopResize(rdpPeer->peer->context);
+ }
+ }
+ return 0;
+}
+
+static int
+parse_extra_modes(const char *modes_str, struct rdp_output *output) {
+ const char *startAt = modes_str;
+ const char *nextPos;
+ int w, h;
+ struct weston_mode *mode;
+
+ while(startAt && *startAt) {
+ nextPos = strchr(startAt, 'x');
+ if(!nextPos)
+ return -1;
+
+ w = strtoul(startAt, NULL, 0);
+ startAt = nextPos + 1;
+ if(!*startAt)
+ return -1;
+
+ h = strtoul(startAt, NULL, 0);
+
+ if(!w || (w > 3000) || !h || (h > 3000))
+ return -1;
+ mode = malloc(sizeof *mode);
+ if(!mode)
+ return -1;
+
+ mode->width = w;
+ mode->height = h;
+ mode->refresh = 5;
+ mode->flags = 0;
+ wl_list_insert(&output->base.mode_list, &mode->link);
+
+ startAt = strchr(startAt, ',');
+ if(startAt && *startAt == ',')
+ startAt++;
+ }
+ return 0;
+}
+static int
+rdp_compositor_create_output(struct rdp_compositor *c, int width, int height,
+ const char *extraModes)
+{
+ struct rdp_output *output;
+ struct wl_event_loop *loop;
+ struct weston_mode *currentMode, *next;
+
+ output = zalloc(sizeof *output);
+ if (output == NULL)
+ return -1;
+
+ wl_list_init(&output->peers);
+ wl_list_init(&output->base.mode_list);
+
+ currentMode = malloc(sizeof *currentMode);
+ if(!currentMode)
+ goto out_free_output;
+ currentMode->flags = WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
+ currentMode->width = width;
+ currentMode->height = height;
+ currentMode->refresh = 5;
+ wl_list_insert(&output->base.mode_list, &currentMode->link);
+
+ if(parse_extra_modes(extraModes, output) < 0) {
+ weston_log("invalid extra modes\n");
+ goto out_free_output_and_modes;
+ }
+
+ output->base.current_mode = output->base.native_mode = currentMode;
+ weston_output_init(&output->base, &c->base, 0, 0, width, height,
+ WL_OUTPUT_TRANSFORM_NORMAL, 1);
+
+ output->base.make = "weston";
+ output->base.model = "rdp";
+ output->shadow_surface = pixman_image_create_bits(PIXMAN_x8r8g8b8,
+ width, height,
+ NULL,
+ width * 4);
+ if (output->shadow_surface == NULL) {
+ weston_log("Failed to create surface for frame buffer.\n");
+ goto out_output;
+ }
+
+ if (pixman_renderer_output_create(&output->base) < 0)
+ goto out_shadow_surface;
+
+ weston_output_move(&output->base, 0, 0);
+
+ loop = wl_display_get_event_loop(c->base.wl_display);
+ output->finish_frame_timer = wl_event_loop_add_timer(loop, finish_frame_handler, output);
+
+ output->base.start_repaint_loop = rdp_output_start_repaint_loop;
+ output->base.repaint = rdp_output_repaint;
+ output->base.destroy = rdp_output_destroy;
+ output->base.assign_planes = NULL;
+ output->base.set_backlight = NULL;
+ output->base.set_dpms = NULL;
+ output->base.switch_mode = rdp_switch_mode;
+ c->output = output;
+
+ wl_list_insert(c->base.output_list.prev, &output->base.link);
+ return 0;
+
+out_shadow_surface:
+ pixman_image_unref(output->shadow_surface);
+out_output:
+ weston_output_destroy(&output->base);
+out_free_output_and_modes:
+ wl_list_for_each_safe(currentMode, next, &output->base.mode_list, link)
+ free(currentMode);
+out_free_output:
+ free(output);
+ return -1;
+}
+
+static void
+rdp_restore(struct weston_compositor *ec)
+{
+}
+
+static void
+rdp_destroy(struct weston_compositor *ec)
+{
+ ec->renderer->destroy(ec);
+ weston_compositor_shutdown(ec);
+
+ free(ec);
+}
+
+static
+int rdp_listener_activity(int fd, uint32_t mask, void *data) {
+ freerdp_listener* instance = (freerdp_listener *)data;
+
+ if (!(mask & WL_EVENT_READABLE))
+ return 0;
+ if (!instance->CheckFileDescriptor(instance))
+ {
+ weston_log("failed to check FreeRDP file descriptor\n");
+ return -1;
+ }
+ return 0;
+}
+
+static
+int rdp_implant_listener(struct rdp_compositor *c, freerdp_listener* instance) {
+ int i, fd;
+ int rcount = 0;
+ void* rfds[MAX_FREERDP_FDS];
+ struct wl_event_loop *loop;
+
+ if (!instance->GetFileDescriptor(instance, rfds, &rcount)) {
+ weston_log("Failed to get FreeRDP file descriptor\n");
+ return -1;
+ }
+
+ loop = wl_display_get_event_loop(c->base.wl_display);
+ for (i = 0; i < rcount; i++) {
+ fd = (int)(long)(rfds[i]);
+ c->listener_events[i] = wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE,
+ rdp_listener_activity, instance);
+ }
+
+ for( ; i < MAX_FREERDP_FDS; i++)
+ c->listener_events[i] = 0;
+ return 0;
+}
+
+
+static void
+rdp_peer_context_new(freerdp_peer* client, RdpPeerContext* context)
+{
+ context->item.peer = client;
+ context->item.flags = RDP_PEER_OUTPUT_ENABLED;
+
+ context->rfx_context = rfx_context_new();
+ context->rfx_context->mode = RLGR3;
+ context->rfx_context->width = client->settings->DesktopWidth;
+ context->rfx_context->height = client->settings->DesktopHeight;
+ rfx_context_set_pixel_format(context->rfx_context, RDP_PIXEL_FORMAT_B8G8R8A8);
+
+ context->nsc_context = nsc_context_new();
+ nsc_context_set_pixel_format(context->nsc_context, RDP_PIXEL_FORMAT_B8G8R8A8);
+
+ context->encode_stream = Stream_New(NULL, 65536);
+}
+
+static void
+rdp_peer_context_free(freerdp_peer* client, RdpPeerContext* context)
+{
+ int i;
+ if(!context)
+ return;
+
+ wl_list_remove(&context->item.link);
+ for(i = 0; i < MAX_FREERDP_FDS; i++) {
+ if (context->events[i])
+ wl_event_source_remove(context->events[i]);
+ }
+
+ if(context->item.flags & RDP_PEER_ACTIVATED)
+ weston_seat_release(&context->item.seat);
+ Stream_Free(context->encode_stream, TRUE);
+ nsc_context_free(context->nsc_context);
+ rfx_context_free(context->rfx_context);
+ free(context->rfx_rects);
+}
+
+
+static int
+rdp_client_activity(int fd, uint32_t mask, void *data) {
+ freerdp_peer* client = (freerdp_peer *)data;
+
+ if (!client->CheckFileDescriptor(client)) {
+ weston_log("unable to checkDescriptor for %p\n", client);
+ goto out_clean;
+ }
+ return 0;
+
+out_clean:
+ freerdp_peer_context_free(client);
+ freerdp_peer_free(client);
+ return 0;
+}
+
+static BOOL
+xf_peer_capabilities(freerdp_peer* client)
+{
+ return TRUE;
+}
+
+
+struct rdp_to_xkb_keyboard_layout {
+ UINT32 rdpLayoutCode;
+ char *xkbLayout;
+};
+
+/* picked from http://technet.microsoft.com/en-us/library/cc766503(WS.10).aspx */
+static struct rdp_to_xkb_keyboard_layout rdp_keyboards[] = {
+ {0x00000406, "dk"},
+ {0x00000407, "de"},
+ {0x00000409, "us"},
+ {0x0000040c, "fr"},
+ {0x00000410, "it"},
+ {0x00000813, "be"},
+ {0x00000000, 0},
+};
+
+/* taken from 2.2.7.1.6 Input Capability Set (TS_INPUT_CAPABILITYSET) */
+static char *rdp_keyboard_types[] = {
+ "", /* 0: unused */
+ "", /* 1: IBM PC/XT or compatible (83-key) keyboard */
+ "", /* 2: Olivetti "ICO" (102-key) keyboard */
+ "", /* 3: IBM PC/AT (84-key) or similar keyboard */
+ "pc102",/* 4: IBM enhanced (101- or 102-key) keyboard */
+ "", /* 5: Nokia 1050 and similar keyboards */
+ "", /* 6: Nokia 9140 and similar keyboards */
+ "" /* 7: Japanese keyboard */
+};
+
+static BOOL
+xf_peer_post_connect(freerdp_peer* client)
+{
+ RdpPeerContext *peerCtx;
+ struct rdp_compositor *c;
+ struct rdp_output *output;
+ rdpSettings *settings;
+ rdpPointerUpdate *pointer;
+ struct xkb_context *xkbContext;
+ struct xkb_rule_names xkbRuleNames;
+ struct xkb_keymap *keymap;
+ int i;
+ pixman_box32_t box;
+ pixman_region32_t damage;
+
+
+ peerCtx = (RdpPeerContext *)client->context;
+ c = peerCtx->rdpCompositor;
+ output = c->output;
+ settings = client->settings;
+
+ if (!settings->SurfaceCommandsEnabled) {
+ weston_log("client doesn't support required SurfaceCommands\n");
+ return FALSE;
+ }
+
+ if (output->base.width != (int)settings->DesktopWidth ||
+ output->base.height != (int)settings->DesktopHeight)
+ {
+ struct weston_mode new_mode;
+ struct weston_mode *target_mode;
+ new_mode.width = (int)settings->DesktopWidth;
+ new_mode.height = (int)settings->DesktopHeight;
+ target_mode = find_matching_mode(&output->base, &new_mode);
+ if (!target_mode) {
+ weston_log("client mode not found\n");
+ return FALSE;
+ }
+ weston_output_switch_mode(&output->base, target_mode, 1, WESTON_MODE_SWITCH_SET_NATIVE);
+ output->base.width = new_mode.width;
+ output->base.height = new_mode.height;
+ }
+
+ weston_log("kbd_layout:%x kbd_type:%x kbd_subType:%x kbd_functionKeys:%x\n",
+ settings->KeyboardLayout, settings->KeyboardType, settings->KeyboardSubType,
+ settings->KeyboardFunctionKey);
+
+ memset(&xkbRuleNames, 0, sizeof(xkbRuleNames));
+ if(settings->KeyboardType <= 7)
+ xkbRuleNames.model = rdp_keyboard_types[settings->KeyboardType];
+ for(i = 0; rdp_keyboards[i].xkbLayout; i++) {
+ if(rdp_keyboards[i].rdpLayoutCode == settings->KeyboardLayout) {
+ xkbRuleNames.layout = rdp_keyboards[i].xkbLayout;
+ break;
+ }
+ }
+
+ keymap = NULL;
+ if(xkbRuleNames.layout) {
+ xkbContext = xkb_context_new(0);
+ if(!xkbContext) {
+ weston_log("unable to create a xkb_context\n");
+ return FALSE;
+ }
+
+ keymap = xkb_keymap_new_from_names(xkbContext, &xkbRuleNames, 0);
+ }
+ weston_seat_init_keyboard(&peerCtx->item.seat, keymap);
+ weston_seat_init_pointer(&peerCtx->item.seat);
+
+ peerCtx->item.flags |= RDP_PEER_ACTIVATED;
+
+ /* disable pointer on the client side */
+ pointer = client->update->pointer;
+ pointer->pointer_system.type = SYSPTR_NULL;
+ pointer->PointerSystem(client->context, &pointer->pointer_system);
+
+ /* sends a full refresh */
+ box.x1 = 0;
+ box.y1 = 0;
+ box.x2 = output->base.width;
+ box.y2 = output->base.height;
+ pixman_region32_init_with_extents(&damage, &box);
+
+ rdp_peer_refresh_region(&damage, client);
+
+ pixman_region32_fini(&damage);
+
+ return TRUE;
+}
+
+static BOOL
+xf_peer_activate(freerdp_peer *client)
+{
+ RdpPeerContext *context = (RdpPeerContext *)client->context;
+ rfx_context_reset(context->rfx_context);
+ return TRUE;
+}
+
+static void
+xf_mouseEvent(rdpInput *input, UINT16 flags, UINT16 x, UINT16 y) {
+ wl_fixed_t wl_x, wl_y, axis;
+ RdpPeerContext *peerContext = (RdpPeerContext *)input->context;
+ struct rdp_output *output;
+ uint32_t button = 0;
+
+ if (flags & PTR_FLAGS_MOVE) {
+ output = peerContext->rdpCompositor->output;
+ if(x < output->base.width && y < output->base.height) {
+ wl_x = wl_fixed_from_int((int)x);
+ wl_y = wl_fixed_from_int((int)y);
+ notify_motion_absolute(&peerContext->item.seat, weston_compositor_get_time(),
+ wl_x, wl_y);
+ }
+ }
+
+ if (flags & PTR_FLAGS_BUTTON1)
+ button = BTN_LEFT;
+ else if (flags & PTR_FLAGS_BUTTON2)
+ button = BTN_RIGHT;
+ else if (flags & PTR_FLAGS_BUTTON3)
+ button = BTN_MIDDLE;
+
+ if(button) {
+ notify_button(&peerContext->item.seat, weston_compositor_get_time(), button,
+ (flags & PTR_FLAGS_DOWN) ? WL_POINTER_BUTTON_STATE_PRESSED : WL_POINTER_BUTTON_STATE_RELEASED
+ );
+ }
+
+ if (flags & PTR_FLAGS_WHEEL) {
+ /* DEFAULT_AXIS_STEP_DISTANCE is stolen from compositor-x11.c
+ * The RDP specs says the lower bits of flags contains the "the number of rotation
+ * units the mouse wheel was rotated".
+ *
+ * http://blogs.msdn.com/b/oldnewthing/archive/2013/01/23/10387366.aspx explains the 120 value
+ */
+ axis = (DEFAULT_AXIS_STEP_DISTANCE * (flags & 0xff)) / 120;
+ if (flags & PTR_FLAGS_WHEEL_NEGATIVE)
+ axis = -axis;
+
+ notify_axis(&peerContext->item.seat, weston_compositor_get_time(),
+ WL_POINTER_AXIS_VERTICAL_SCROLL,
+ axis);
+ }
+}
+
+static void
+xf_extendedMouseEvent(rdpInput *input, UINT16 flags, UINT16 x, UINT16 y) {
+ wl_fixed_t wl_x, wl_y;
+ RdpPeerContext *peerContext = (RdpPeerContext *)input->context;
+ struct rdp_output *output;
+
+ output = peerContext->rdpCompositor->output;
+ if(x < output->base.width && y < output->base.height) {
+ wl_x = wl_fixed_from_int((int)x);
+ wl_y = wl_fixed_from_int((int)y);
+ notify_motion_absolute(&peerContext->item.seat, weston_compositor_get_time(),
+ wl_x, wl_y);
+ }
+}
+
+
+static void
+xf_input_synchronize_event(rdpInput *input, UINT32 flags)
+{
+ freerdp_peer *client = input->context->peer;
+ RdpPeerContext *peerCtx = (RdpPeerContext *)input->context;
+ struct rdp_output *output = peerCtx->rdpCompositor->output;
+ pixman_box32_t box;
+ pixman_region32_t damage;
+
+ /* sends a full refresh */
+ box.x1 = 0;
+ box.y1 = 0;
+ box.x2 = output->base.width;
+ box.y2 = output->base.height;
+ pixman_region32_init_with_extents(&damage, &box);
+
+ rdp_peer_refresh_region(&damage, client);
+
+ pixman_region32_fini(&damage);
+}
+
+extern DWORD KEYCODE_TO_VKCODE_EVDEV[];
+static uint32_t vk_to_keycode[256];
+static void
+init_vk_translator(void)
+{
+ int i;
+
+ memset(vk_to_keycode, 0, sizeof(vk_to_keycode));
+ for(i = 0; i < 256; i++)
+ vk_to_keycode[KEYCODE_TO_VKCODE_EVDEV[i] & 0xff] = i-8;
+}
+
+static void
+xf_input_keyboard_event(rdpInput *input, UINT16 flags, UINT16 code)
+{
+ uint32_t scan_code, vk_code, full_code;
+ enum wl_keyboard_key_state keyState;
+ RdpPeerContext *peerContext = (RdpPeerContext *)input->context;
+ int notify = 0;
+
+ if (flags & KBD_FLAGS_DOWN) {
+ keyState = WL_KEYBOARD_KEY_STATE_PRESSED;
+ notify = 1;
+ } else if (flags & KBD_FLAGS_RELEASE) {
+ keyState = WL_KEYBOARD_KEY_STATE_RELEASED;
+ notify = 1;
+ }
+
+ if(notify) {
+ full_code = code;
+ if(flags & KBD_FLAGS_EXTENDED)
+ full_code |= KBD_FLAGS_EXTENDED;
+
+ vk_code = GetVirtualKeyCodeFromVirtualScanCode(full_code, 4);
+ if(vk_code > 0xff) {
+ weston_log("invalid vk_code %x", vk_code);
+ return;
+ }
+ scan_code = vk_to_keycode[vk_code];
+
+
+ /*weston_log("code=%x ext=%d vk_code=%x scan_code=%x\n", code, (flags & KBD_FLAGS_EXTENDED) ? 1 : 0,
+ vk_code, scan_code);*/
+ notify_key(&peerContext->item.seat, weston_compositor_get_time(),
+ scan_code, keyState, STATE_UPDATE_AUTOMATIC);
+ }
+}
+
+static void
+xf_input_unicode_keyboard_event(rdpInput *input, UINT16 flags, UINT16 code)
+{
+ weston_log("Client sent a unicode keyboard event (flags:0x%X code:0x%X)\n", flags, code);
+}
+
+
+static void
+xf_suppress_output(rdpContext *context, BYTE allow, RECTANGLE_16 *area) {
+ RdpPeerContext *peerContext = (RdpPeerContext *)context;
+ if(allow)
+ peerContext->item.flags |= RDP_PEER_OUTPUT_ENABLED;
+ else
+ peerContext->item.flags &= (~RDP_PEER_OUTPUT_ENABLED);
+}
+
+static int
+rdp_peer_init(freerdp_peer *client, struct rdp_compositor *c)
+{
+ int rcount = 0;
+ void *rfds[MAX_FREERDP_FDS];
+ int i, fd;
+ struct wl_event_loop *loop;
+ rdpSettings *settings;
+ rdpInput *input;
+ RdpPeerContext *peerCtx;
+ char seat_name[32];
+
+ client->ContextSize = sizeof(RdpPeerContext);
+ client->ContextNew = (psPeerContextNew)rdp_peer_context_new;
+ client->ContextFree = (psPeerContextFree)rdp_peer_context_free;
+ freerdp_peer_context_new(client);
+
+ peerCtx = (RdpPeerContext *) client->context;
+ peerCtx->rdpCompositor = c;
+
+ settings = client->settings;
+ settings->RdpKeyFile = c->rdp_key;
+ if(c->tls_enabled) {
+ settings->CertificateFile = c->server_cert;
+ settings->PrivateKeyFile = c->server_key;
+ } else {
+ settings->TlsSecurity = FALSE;
+ }
+
+ settings->NlaSecurity = FALSE;
+
+ client->Capabilities = xf_peer_capabilities;
+ client->PostConnect = xf_peer_post_connect;
+ client->Activate = xf_peer_activate;
+
+ client->update->SuppressOutput = xf_suppress_output;
+
+ input = client->input;
+ input->SynchronizeEvent = xf_input_synchronize_event;
+ input->MouseEvent = xf_mouseEvent;
+ input->ExtendedMouseEvent = xf_extendedMouseEvent;
+ input->KeyboardEvent = xf_input_keyboard_event;
+ input->UnicodeKeyboardEvent = xf_input_unicode_keyboard_event;
+
+ if (snprintf(seat_name, 32, "rdp:%d:%s", client->sockfd, client->hostname) >= 32)
+ seat_name[31] = '\0';
+
+ weston_seat_init(&peerCtx->item.seat, &c->base, seat_name);
+
+ client->Initialize(client);
+
+ if (!client->GetFileDescriptor(client, rfds, &rcount)) {
+ weston_log("unable to retrieve client fds\n");
+ return -1;
+ }
+
+ loop = wl_display_get_event_loop(c->base.wl_display);
+ for(i = 0; i < rcount; i++) {
+ fd = (int)(long)(rfds[i]);
+
+ peerCtx->events[i] = wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE,
+ rdp_client_activity, client);
+ }
+ for ( ; i < MAX_FREERDP_FDS; i++)
+ peerCtx->events[i] = 0;
+
+ wl_list_insert(&c->output->peers, &peerCtx->item.link);
+ return 0;
+}
+
+
+static void
+rdp_incoming_peer(freerdp_listener *instance, freerdp_peer *client)
+{
+ struct rdp_compositor *c = (struct rdp_compositor *)instance->param4;
+ if (rdp_peer_init(client, c) < 0)
+ return;
+}
+
+static struct weston_compositor *
+rdp_compositor_create(struct wl_display *display,
+ struct rdp_compositor_config *config,
+ int *argc, char *argv[], struct weston_config *wconfig)
+{
+ struct rdp_compositor *c;
+ char *fd_str;
+ int fd;
+
+ c = zalloc(sizeof *c);
+ if (c == NULL)
+ return NULL;
+
+ if (weston_compositor_init(&c->base, display, argc, argv, wconfig) < 0)
+ goto err_free;
+
+ c->base.destroy = rdp_destroy;
+ c->base.restore = rdp_restore;
+ c->rdp_key = config->rdp_key ? strdup(config->rdp_key) : NULL;
+
+ /* activate TLS only if certificate/key are available */
+ if(config->server_cert && config->server_key) {
+ weston_log("TLS support activated\n");
+ c->server_cert = strdup(config->server_cert);
+ c->server_key = strdup(config->server_key);
+ if(!c->server_cert || !c->server_key)
+ goto err_free_strings;
+ c->tls_enabled = 1;
+ }
+
+ if (pixman_renderer_init(&c->base) < 0)
+ goto err_compositor;
+
+ if (rdp_compositor_create_output(c, config->width, config->height, config->extra_modes) < 0)
+ goto err_compositor;
+
+ if(!config->env_socket) {
+ c->listener = freerdp_listener_new();
+ c->listener->PeerAccepted = rdp_incoming_peer;
+ c->listener->param4 = c;
+ if(!c->listener->Open(c->listener, config->bind_address, config->port)) {
+ weston_log("unable to bind rdp socket\n");
+ goto err_listener;
+ }
+
+ if (rdp_implant_listener(c, c->listener) < 0)
+ goto err_compositor;
+ } else {
+ /* get the socket from RDP_FD var */
+ fd_str = getenv("RDP_FD");
+ if(!fd_str) {
+ weston_log("RDP_FD env variable not set");
+ goto err_output;
+ }
+
+ fd = strtoul(fd_str, NULL, 10);
+ if(rdp_peer_init(freerdp_peer_new(fd), c))
+ goto err_output;
+ }
+
+ return &c->base;
+
+err_listener:
+ freerdp_listener_free(c->listener);
+err_output:
+ weston_output_destroy(&c->output->base);
+err_compositor:
+ weston_compositor_shutdown(&c->base);
+err_free_strings:
+ if(c->rdp_key)
+ free(c->rdp_key);
+ if(c->server_cert)
+ free(c->server_cert);
+ if(c->server_key)
+ free(c->server_key);
+err_free:
+ free(c);
+ return NULL;
+}
+
+WL_EXPORT struct weston_compositor *
+backend_init(struct wl_display *display, int *argc, char *argv[],
+ struct weston_config *wconfig)
+{
+ struct rdp_compositor_config config;
+ rdp_compositor_config_init(&config);
+ int major, minor, revision;
+
+ freerdp_get_version(&major, &minor, &revision);
+ weston_log("using FreeRDP version %d.%d.%d\n", major, minor, revision);
+ init_vk_translator();
+
+ const struct weston_option rdp_options[] = {
+ { WESTON_OPTION_BOOLEAN, "env-socket", 0, &config.env_socket },
+ { WESTON_OPTION_INTEGER, "width", 0, &config.width },
+ { WESTON_OPTION_INTEGER, "height", 0, &config.height },
+ { WESTON_OPTION_STRING, "extra-modes", 0, &config.extra_modes },
+ { WESTON_OPTION_STRING, "address", 0, &config.bind_address },
+ { WESTON_OPTION_INTEGER, "port", 0, &config.port },
+ { WESTON_OPTION_STRING, "rdp4-key", 0, &config.rdp_key },
+ { WESTON_OPTION_STRING, "rdp-tls-cert", 0, &config.server_cert },
+ { WESTON_OPTION_STRING, "rdp-tls-key", 0, &config.server_key }
+ };
+
+ parse_options(rdp_options, ARRAY_LENGTH(rdp_options), argc, argv);
+ return rdp_compositor_create(display, &config, argc, argv, wconfig);
+}
diff --git a/src/compositor-rpi.c b/src/compositor-rpi.c
new file mode 100644
index 00000000..60926325
--- /dev/null
+++ b/src/compositor-rpi.c
@@ -0,0 +1,844 @@
+/*
+ * Copyright © 2008-2011 Kristian Høgsberg
+ * Copyright © 2011 Intel Corporation
+ * Copyright © 2012-2013 Raspberry Pi Foundation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that 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.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <libudev.h>
+
+#ifdef HAVE_BCM_HOST
+# include <bcm_host.h>
+#else
+# include "rpi-bcm-stubs.h"
+#endif
+
+#include "compositor.h"
+#include "rpi-renderer.h"
+#include "evdev.h"
+#include "launcher-util.h"
+
+#if 0
+#define DBG(...) \
+ weston_log(__VA_ARGS__)
+#else
+#define DBG(...) do {} while (0)
+#endif
+
+struct rpi_compositor;
+struct rpi_output;
+
+struct rpi_flippipe {
+ int readfd;
+ int writefd;
+ struct wl_event_source *source;
+};
+
+struct rpi_output {
+ struct rpi_compositor *compositor;
+ struct weston_output base;
+ int single_buffer;
+
+ struct weston_mode mode;
+ struct rpi_flippipe flippipe;
+
+ DISPMANX_DISPLAY_HANDLE_T display;
+};
+
+struct rpi_seat {
+ struct weston_seat base;
+ struct wl_list devices_list;
+
+ struct udev_monitor *udev_monitor;
+ struct wl_event_source *udev_monitor_source;
+ char *seat_id;
+};
+
+struct rpi_compositor {
+ struct weston_compositor base;
+ uint32_t prev_state;
+
+ struct udev *udev;
+ struct wl_listener session_listener;
+
+ int single_buffer;
+};
+
+static inline struct rpi_output *
+to_rpi_output(struct weston_output *base)
+{
+ return container_of(base, struct rpi_output, base);
+}
+
+static inline struct rpi_seat *
+to_rpi_seat(struct weston_seat *base)
+{
+ return container_of(base, struct rpi_seat, base);
+}
+
+static inline struct rpi_compositor *
+to_rpi_compositor(struct weston_compositor *base)
+{
+ return container_of(base, struct rpi_compositor, base);
+}
+
+static uint64_t
+rpi_get_current_time(void)
+{
+ struct timeval tv;
+
+ /* XXX: use CLOCK_MONOTONIC instead? */
+ gettimeofday(&tv, NULL);
+ return (uint64_t)tv.tv_sec * 1000 + tv.tv_usec / 1000;
+}
+
+static void
+rpi_flippipe_update_complete(DISPMANX_UPDATE_HANDLE_T update, void *data)
+{
+ /* This function runs in a different thread. */
+ struct rpi_flippipe *flippipe = data;
+ uint64_t time;
+ ssize_t ret;
+
+ /* manufacture flip completion timestamp */
+ time = rpi_get_current_time();
+
+ ret = write(flippipe->writefd, &time, sizeof time);
+ if (ret != sizeof time)
+ weston_log("ERROR: %s failed to write, ret %zd, errno %d\n",
+ __func__, ret, errno);
+}
+
+static int
+rpi_dispmanx_update_submit(DISPMANX_UPDATE_HANDLE_T update,
+ struct rpi_output *output)
+{
+ /*
+ * The callback registered here will eventually be called
+ * in a different thread context. Therefore we cannot call
+ * the usual functions from rpi_flippipe_update_complete().
+ * Instead, we have a pipe for passing the message from the
+ * thread, waking up the Weston main event loop, calling
+ * rpi_flippipe_handler(), and then ending up in
+ * rpi_output_update_complete() in the main thread context,
+ * where we can do the frame finishing work.
+ */
+ return vc_dispmanx_update_submit(update, rpi_flippipe_update_complete,
+ &output->flippipe);
+}
+
+static void
+rpi_output_update_complete(struct rpi_output *output, uint64_t time);
+
+static int
+rpi_flippipe_handler(int fd, uint32_t mask, void *data)
+{
+ struct rpi_output *output = data;
+ ssize_t ret;
+ uint64_t time;
+
+ if (mask != WL_EVENT_READABLE)
+ weston_log("ERROR: unexpected mask 0x%x in %s\n",
+ mask, __func__);
+
+ ret = read(fd, &time, sizeof time);
+ if (ret != sizeof time) {
+ weston_log("ERROR: %s failed to read, ret %zd, errno %d\n",
+ __func__, ret, errno);
+ }
+
+ rpi_output_update_complete(output, time);
+
+ return 1;
+}
+
+static int
+rpi_flippipe_init(struct rpi_flippipe *flippipe, struct rpi_output *output)
+{
+ struct wl_event_loop *loop;
+ int fd[2];
+
+ if (pipe2(fd, O_CLOEXEC) == -1)
+ return -1;
+
+ flippipe->readfd = fd[0];
+ flippipe->writefd = fd[1];
+
+ loop = wl_display_get_event_loop(output->compositor->base.wl_display);
+ flippipe->source = wl_event_loop_add_fd(loop, flippipe->readfd,
+ WL_EVENT_READABLE,
+ rpi_flippipe_handler, output);
+
+ if (!flippipe->source) {
+ close(flippipe->readfd);
+ close(flippipe->writefd);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void
+rpi_flippipe_release(struct rpi_flippipe *flippipe)
+{
+ wl_event_source_remove(flippipe->source);
+ close(flippipe->readfd);
+ close(flippipe->writefd);
+}
+
+static void
+rpi_output_start_repaint_loop(struct weston_output *output)
+{
+ uint64_t time;
+
+ time = rpi_get_current_time();
+ weston_output_finish_frame(output, time);
+}
+
+static int
+rpi_output_repaint(struct weston_output *base, pixman_region32_t *damage)
+{
+ struct rpi_output *output = to_rpi_output(base);
+ struct rpi_compositor *compositor = output->compositor;
+ struct weston_plane *primary_plane = &compositor->base.primary_plane;
+ DISPMANX_UPDATE_HANDLE_T update;
+
+ DBG("frame update start\n");
+
+ /* Update priority higher than in rpi-renderer's
+ * output destroy function, see rpi_output_destroy().
+ */
+ update = vc_dispmanx_update_start(1);
+
+ rpi_renderer_set_update_handle(&output->base, update);
+ compositor->base.renderer->repaint_output(&output->base, damage);
+
+ pixman_region32_subtract(&primary_plane->damage,
+ &primary_plane->damage, damage);
+
+ /* schedule callback to rpi_output_update_complete() */
+ rpi_dispmanx_update_submit(update, output);
+ DBG("frame update submitted\n");
+ return 0;
+}
+
+static void
+rpi_output_update_complete(struct rpi_output *output, uint64_t time)
+{
+ DBG("frame update complete(%" PRIu64 ")\n", time);
+ rpi_renderer_finish_frame(&output->base);
+ weston_output_finish_frame(&output->base, time);
+}
+
+static void
+rpi_output_destroy(struct weston_output *base)
+{
+ struct rpi_output *output = to_rpi_output(base);
+
+ DBG("%s\n", __func__);
+
+ rpi_renderer_output_destroy(base);
+
+ /* rpi_renderer_output_destroy() will schedule a removal of
+ * all Dispmanx Elements, and wait for the update to complete.
+ * Assuming updates are sequential, the wait should guarantee,
+ * that any pending rpi_flippipe_update_complete() callbacks
+ * have happened already. Therefore we can destroy the flippipe
+ * now.
+ */
+ rpi_flippipe_release(&output->flippipe);
+
+ wl_list_remove(&output->base.link);
+ weston_output_destroy(&output->base);
+
+ vc_dispmanx_display_close(output->display);
+
+ free(output);
+}
+
+static const char *transform_names[] = {
+ [WL_OUTPUT_TRANSFORM_NORMAL] = "normal",
+ [WL_OUTPUT_TRANSFORM_90] = "90",
+ [WL_OUTPUT_TRANSFORM_180] = "180",
+ [WL_OUTPUT_TRANSFORM_270] = "270",
+ [WL_OUTPUT_TRANSFORM_FLIPPED] = "flipped",
+ [WL_OUTPUT_TRANSFORM_FLIPPED_90] = "flipped-90",
+ [WL_OUTPUT_TRANSFORM_FLIPPED_180] = "flipped-180",
+ [WL_OUTPUT_TRANSFORM_FLIPPED_270] = "flipped-270",
+};
+
+static int
+str2transform(const char *name)
+{
+ unsigned i;
+
+ for (i = 0; i < ARRAY_LENGTH(transform_names); i++)
+ if (strcmp(name, transform_names[i]) == 0)
+ return i;
+
+ return -1;
+}
+
+static const char *
+transform2str(uint32_t output_transform)
+{
+ if (output_transform >= ARRAY_LENGTH(transform_names))
+ return "<illegal value>";
+
+ return transform_names[output_transform];
+}
+
+static int
+rpi_output_create(struct rpi_compositor *compositor, uint32_t transform)
+{
+ struct rpi_output *output;
+ DISPMANX_MODEINFO_T modeinfo;
+ int ret;
+ float mm_width, mm_height;
+
+ output = calloc(1, sizeof *output);
+ if (!output)
+ return -1;
+
+ output->compositor = compositor;
+ output->single_buffer = compositor->single_buffer;
+
+ if (rpi_flippipe_init(&output->flippipe, output) < 0) {
+ weston_log("Creating message pipe failed.\n");
+ goto out_free;
+ }
+
+ output->display = vc_dispmanx_display_open(DISPMANX_ID_HDMI);
+ if (!output->display) {
+ weston_log("Failed to open dispmanx HDMI display.\n");
+ goto out_pipe;
+ }
+
+ ret = vc_dispmanx_display_get_info(output->display, &modeinfo);
+ if (ret < 0) {
+ weston_log("Failed to get display mode information.\n");
+ goto out_dmx_close;
+ }
+
+ output->base.start_repaint_loop = rpi_output_start_repaint_loop;
+ output->base.repaint = rpi_output_repaint;
+ output->base.destroy = rpi_output_destroy;
+ output->base.assign_planes = NULL;
+ output->base.set_backlight = NULL;
+ output->base.set_dpms = NULL;
+ output->base.switch_mode = NULL;
+
+ /* XXX: use tvservice to get information from and control the
+ * HDMI and SDTV outputs. See:
+ * /opt/vc/include/interface/vmcs_host/vc_tvservice.h
+ */
+
+ /* only one static mode in list */
+ output->mode.flags =
+ WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
+ output->mode.width = modeinfo.width;
+ output->mode.height = modeinfo.height;
+ output->mode.refresh = 60000;
+ wl_list_init(&output->base.mode_list);
+ wl_list_insert(&output->base.mode_list, &output->mode.link);
+
+ output->base.current_mode = &output->mode;
+ output->base.subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN;
+ output->base.make = "unknown";
+ output->base.model = "unknown";
+
+ /* guess 96 dpi */
+ mm_width = modeinfo.width * (25.4f / 96.0f);
+ mm_height = modeinfo.height * (25.4f / 96.0f);
+
+ weston_output_init(&output->base, &compositor->base,
+ 0, 0, round(mm_width), round(mm_height),
+ transform, 1);
+
+ if (rpi_renderer_output_create(&output->base, output->display) < 0)
+ goto out_output;
+
+ wl_list_insert(compositor->base.output_list.prev, &output->base.link);
+
+ weston_log("Raspberry Pi HDMI output %dx%d px\n",
+ output->mode.width, output->mode.height);
+ weston_log_continue(STAMP_SPACE "guessing %d Hz and 96 dpi\n",
+ output->mode.refresh / 1000);
+ weston_log_continue(STAMP_SPACE "orientation: %s\n",
+ transform2str(output->base.transform));
+
+ if (!strncmp(transform2str(output->base.transform), "flipped", 7))
+ weston_log("warning: flipped output transforms may not work\n");
+
+ return 0;
+
+out_output:
+ weston_output_destroy(&output->base);
+
+out_dmx_close:
+ vc_dispmanx_display_close(output->display);
+
+out_pipe:
+ rpi_flippipe_release(&output->flippipe);
+
+out_free:
+ free(output);
+ return -1;
+}
+
+static void
+rpi_led_update(struct weston_seat *seat_base, enum weston_led leds)
+{
+ struct rpi_seat *seat = to_rpi_seat(seat_base);
+ struct evdev_device *device;
+
+ wl_list_for_each(device, &seat->devices_list, link)
+ evdev_led_update(device, leds);
+}
+
+static const char default_seat[] = "seat0";
+
+static void
+device_added(struct udev_device *udev_device, struct rpi_seat *master)
+{
+ struct evdev_device *device;
+ const char *devnode;
+ const char *device_seat;
+ int fd;
+
+ device_seat = udev_device_get_property_value(udev_device, "ID_SEAT");
+ if (!device_seat)
+ device_seat = default_seat;
+
+ if (strcmp(device_seat, master->seat_id))
+ return;
+
+ devnode = udev_device_get_devnode(udev_device);
+
+ /* Use non-blocking mode so that we can loop on read on
+ * evdev_device_data() until all events on the fd are
+ * read. mtdev_get() also expects this. */
+ fd = open(devnode, O_RDWR | O_NONBLOCK | O_CLOEXEC);
+ if (fd < 0) {
+ weston_log("opening input device '%s' failed.\n", devnode);
+ return;
+ }
+
+ device = evdev_device_create(&master->base, devnode, fd);
+ if (!device) {
+ close(fd);
+ weston_log("not using input device '%s'.\n", devnode);
+ return;
+ }
+
+ wl_list_insert(master->devices_list.prev, &device->link);
+}
+
+static void
+evdev_add_devices(struct udev *udev, struct weston_seat *seat_base)
+{
+ struct rpi_seat *seat = to_rpi_seat(seat_base);
+ struct udev_enumerate *e;
+ struct udev_list_entry *entry;
+ struct udev_device *device;
+ const char *path, *sysname;
+
+ e = udev_enumerate_new(udev);
+ udev_enumerate_add_match_subsystem(e, "input");
+ udev_enumerate_scan_devices(e);
+ udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) {
+ path = udev_list_entry_get_name(entry);
+ device = udev_device_new_from_syspath(udev, path);
+
+ sysname = udev_device_get_sysname(device);
+ if (strncmp("event", sysname, 5) != 0) {
+ udev_device_unref(device);
+ continue;
+ }
+
+ device_added(device, seat);
+
+ udev_device_unref(device);
+ }
+ udev_enumerate_unref(e);
+
+ evdev_notify_keyboard_focus(&seat->base, &seat->devices_list);
+
+ if (wl_list_empty(&seat->devices_list)) {
+ weston_log(
+ "warning: no input devices on entering Weston. "
+ "Possible causes:\n"
+ "\t- no permissions to read /dev/input/event*\n"
+ "\t- seats misconfigured "
+ "(Weston backend option 'seat', "
+ "udev device property ID_SEAT)\n");
+ }
+}
+
+static int
+evdev_udev_handler(int fd, uint32_t mask, void *data)
+{
+ struct rpi_seat *seat = data;
+ struct udev_device *udev_device;
+ struct evdev_device *device, *next;
+ const char *action;
+ const char *devnode;
+
+ udev_device = udev_monitor_receive_device(seat->udev_monitor);
+ if (!udev_device)
+ return 1;
+
+ action = udev_device_get_action(udev_device);
+ if (!action)
+ goto out;
+
+ if (strncmp("event", udev_device_get_sysname(udev_device), 5) != 0)
+ goto out;
+
+ if (!strcmp(action, "add")) {
+ device_added(udev_device, seat);
+ }
+ else if (!strcmp(action, "remove")) {
+ devnode = udev_device_get_devnode(udev_device);
+ wl_list_for_each_safe(device, next, &seat->devices_list, link)
+ if (!strcmp(device->devnode, devnode)) {
+ weston_log("input device %s, %s removed\n",
+ device->devname, device->devnode);
+ evdev_device_destroy(device);
+ break;
+ }
+ }
+
+out:
+ udev_device_unref(udev_device);
+
+ return 0;
+}
+
+static int
+evdev_enable_udev_monitor(struct udev *udev, struct weston_seat *seat_base)
+{
+ struct rpi_seat *master = to_rpi_seat(seat_base);
+ struct wl_event_loop *loop;
+ struct weston_compositor *c = master->base.compositor;
+ int fd;
+
+ master->udev_monitor = udev_monitor_new_from_netlink(udev, "udev");
+ if (!master->udev_monitor) {
+ weston_log("udev: failed to create the udev monitor\n");
+ return 0;
+ }
+
+ udev_monitor_filter_add_match_subsystem_devtype(master->udev_monitor,
+ "input", NULL);
+
+ if (udev_monitor_enable_receiving(master->udev_monitor)) {
+ weston_log("udev: failed to bind the udev monitor\n");
+ udev_monitor_unref(master->udev_monitor);
+ return 0;
+ }
+
+ loop = wl_display_get_event_loop(c->wl_display);
+ fd = udev_monitor_get_fd(master->udev_monitor);
+ master->udev_monitor_source =
+ wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE,
+ evdev_udev_handler, master);
+ if (!master->udev_monitor_source) {
+ udev_monitor_unref(master->udev_monitor);
+ return 0;
+ }
+
+ return 1;
+}
+
+static void
+evdev_disable_udev_monitor(struct weston_seat *seat_base)
+{
+ struct rpi_seat *seat = to_rpi_seat(seat_base);
+
+ if (!seat->udev_monitor)
+ return;
+
+ udev_monitor_unref(seat->udev_monitor);
+ seat->udev_monitor = NULL;
+ wl_event_source_remove(seat->udev_monitor_source);
+ seat->udev_monitor_source = NULL;
+}
+
+static void
+evdev_input_create(struct weston_compositor *c, struct udev *udev,
+ const char *seat_id)
+{
+ struct rpi_seat *seat;
+
+ seat = zalloc(sizeof *seat);
+ if (seat == NULL)
+ return;
+
+ weston_seat_init(&seat->base, c, "default");
+ seat->base.led_update = rpi_led_update;
+
+ wl_list_init(&seat->devices_list);
+ seat->seat_id = strdup(seat_id);
+ if (!evdev_enable_udev_monitor(udev, &seat->base)) {
+ free(seat->seat_id);
+ free(seat);
+ return;
+ }
+
+ evdev_add_devices(udev, &seat->base);
+}
+
+static void
+evdev_remove_devices(struct weston_seat *seat_base)
+{
+ struct rpi_seat *seat = to_rpi_seat(seat_base);
+ struct evdev_device *device, *next;
+
+ wl_list_for_each_safe(device, next, &seat->devices_list, link)
+ evdev_device_destroy(device);
+
+ if (seat->base.keyboard)
+ notify_keyboard_focus_out(&seat->base);
+}
+
+static void
+evdev_input_destroy(struct weston_seat *seat_base)
+{
+ struct rpi_seat *seat = to_rpi_seat(seat_base);
+
+ evdev_remove_devices(seat_base);
+ evdev_disable_udev_monitor(&seat->base);
+
+ weston_seat_release(seat_base);
+ free(seat->seat_id);
+ free(seat);
+}
+
+static void
+rpi_compositor_destroy(struct weston_compositor *base)
+{
+ struct rpi_compositor *compositor = to_rpi_compositor(base);
+ struct weston_seat *seat, *next;
+
+ wl_list_for_each_safe(seat, next, &compositor->base.seat_list, link)
+ evdev_input_destroy(seat);
+
+ /* destroys outputs, too */
+ weston_compositor_shutdown(&compositor->base);
+
+ compositor->base.renderer->destroy(&compositor->base);
+ weston_launcher_destroy(compositor->base.launcher);
+
+ bcm_host_deinit();
+ free(compositor);
+}
+
+static void
+session_notify(struct wl_listener *listener, void *data)
+{
+ struct rpi_compositor *compositor = data;
+ struct weston_seat *seat;
+ struct weston_output *output;
+
+ if (compositor->base.session_active) {
+ weston_log("activating session\n");
+ compositor->base.focus = 1;
+ compositor->base.state = compositor->prev_state;
+ weston_compositor_damage_all(&compositor->base);
+ wl_list_for_each(seat, &compositor->base.seat_list, link) {
+ evdev_add_devices(compositor->udev, seat);
+ evdev_enable_udev_monitor(compositor->udev, seat);
+ }
+ } else {
+ weston_log("deactivating session\n");
+ wl_list_for_each(seat, &compositor->base.seat_list, link) {
+ evdev_disable_udev_monitor(seat);
+ evdev_remove_devices(seat);
+ }
+
+ compositor->base.focus = 0;
+ compositor->prev_state = compositor->base.state;
+ weston_compositor_offscreen(&compositor->base);
+
+ /* If we have a repaint scheduled (either from a
+ * pending pageflip or the idle handler), make sure we
+ * cancel that so we don't try to pageflip when we're
+ * vt switched away. The OFFSCREEN state will prevent
+ * further attemps at repainting. When we switch
+ * back, we schedule a repaint, which will process
+ * pending frame callbacks. */
+
+ wl_list_for_each(output,
+ &compositor->base.output_list, link) {
+ output->repaint_needed = 0;
+ }
+ };
+}
+
+static void
+rpi_restore(struct weston_compositor *compositor)
+{
+ weston_launcher_restore(compositor->launcher);
+}
+
+static void
+switch_vt_binding(struct weston_seat *seat, uint32_t time, uint32_t key, void *data)
+{
+ struct weston_compositor *compositor = data;
+
+ weston_launcher_activate_vt(compositor->launcher, key - KEY_F1 + 1);
+}
+
+struct rpi_parameters {
+ int tty;
+ struct rpi_renderer_parameters renderer;
+ uint32_t output_transform;
+};
+
+static struct weston_compositor *
+rpi_compositor_create(struct wl_display *display, int *argc, char *argv[],
+ struct weston_config *config,
+ struct rpi_parameters *param)
+{
+ struct rpi_compositor *compositor;
+ const char *seat = default_seat;
+ uint32_t key;
+
+ weston_log("initializing Raspberry Pi backend\n");
+
+ compositor = calloc(1, sizeof *compositor);
+ if (compositor == NULL)
+ return NULL;
+
+ if (weston_compositor_init(&compositor->base, display, argc, argv,
+ config) < 0)
+ goto out_free;
+
+ compositor->udev = udev_new();
+ if (compositor->udev == NULL) {
+ weston_log("Failed to initialize udev context.\n");
+ goto out_compositor;
+ }
+
+ compositor->session_listener.notify = session_notify;
+ wl_signal_add(&compositor->base.session_signal,
+ &compositor ->session_listener);
+ compositor->base.launcher =
+ weston_launcher_connect(&compositor->base, param->tty);
+ if (!compositor->base.launcher) {
+ weston_log("Failed to initialize tty.\n");
+ goto out_udev;
+ }
+
+ compositor->base.destroy = rpi_compositor_destroy;
+ compositor->base.restore = rpi_restore;
+
+ compositor->base.focus = 1;
+ compositor->prev_state = WESTON_COMPOSITOR_ACTIVE;
+ compositor->single_buffer = param->renderer.single_buffer;
+
+ weston_log("Dispmanx planes are %s buffered.\n",
+ compositor->single_buffer ? "single" : "double");
+
+ for (key = KEY_F1; key < KEY_F9; key++)
+ weston_compositor_add_key_binding(&compositor->base, key,
+ MODIFIER_CTRL | MODIFIER_ALT,
+ switch_vt_binding, compositor);
+
+ /*
+ * bcm_host_init() creates threads.
+ * Therefore we must have all signal handlers set and signals blocked
+ * before calling it. Otherwise the signals may end in the bcm
+ * threads and cause the default behaviour there. For instance,
+ * SIGUSR1 used for VT switching caused Weston to terminate there.
+ */
+ bcm_host_init();
+
+ if (rpi_renderer_create(&compositor->base, &param->renderer) < 0)
+ goto out_launcher;
+
+ if (rpi_output_create(compositor, param->output_transform) < 0)
+ goto out_renderer;
+
+ evdev_input_create(&compositor->base, compositor->udev, seat);
+
+ return &compositor->base;
+
+out_renderer:
+ compositor->base.renderer->destroy(&compositor->base);
+
+out_launcher:
+ weston_launcher_destroy(compositor->base.launcher);
+
+out_udev:
+ udev_unref(compositor->udev);
+
+out_compositor:
+ weston_compositor_shutdown(&compositor->base);
+
+out_free:
+ bcm_host_deinit();
+ free(compositor);
+
+ return NULL;
+}
+
+WL_EXPORT struct weston_compositor *
+backend_init(struct wl_display *display, int *argc, char *argv[],
+ struct weston_config *config)
+{
+ const char *transform = "normal";
+ int ret;
+
+ struct rpi_parameters param = {
+ .tty = 0, /* default to current tty */
+ .renderer.single_buffer = 0,
+ .output_transform = WL_OUTPUT_TRANSFORM_NORMAL,
+ };
+
+ const struct weston_option rpi_options[] = {
+ { WESTON_OPTION_INTEGER, "tty", 0, &param.tty },
+ { WESTON_OPTION_BOOLEAN, "single-buffer", 0,
+ &param.renderer.single_buffer },
+ { WESTON_OPTION_STRING, "transform", 0, &transform },
+ };
+
+ parse_options(rpi_options, ARRAY_LENGTH(rpi_options), argc, argv);
+
+ ret = str2transform(transform);
+ if (ret < 0)
+ weston_log("invalid transform \"%s\"\n", transform);
+ else
+ param.output_transform = ret;
+
+ return rpi_compositor_create(display, argc, argv, config, &param);
+}
diff --git a/src/compositor-wayland.c b/src/compositor-wayland.c
new file mode 100644
index 00000000..f3b03619
--- /dev/null
+++ b/src/compositor-wayland.c
@@ -0,0 +1,808 @@
+/*
+ * Copyright © 2010-2011 Benjamin Franzke
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that 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.
+ */
+
+#include "config.h"
+
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+#include <wayland-client.h>
+#include <wayland-egl.h>
+
+#include "compositor.h"
+#include "gl-renderer.h"
+#include "../shared/image-loader.h"
+#include "../shared/os-compatibility.h"
+
+struct wayland_compositor {
+ struct weston_compositor base;
+
+ struct {
+ struct wl_display *wl_display;
+ struct wl_registry *registry;
+ struct wl_compositor *compositor;
+ struct wl_shell *shell;
+ struct wl_output *output;
+ struct wl_shm *shm;
+
+ struct {
+ int32_t x, y, width, height;
+ } screen_allocation;
+
+ struct wl_event_source *wl_source;
+ uint32_t event_mask;
+ } parent;
+
+ struct {
+ int32_t top, bottom, left, right;
+ } border;
+
+ struct wl_list input_list;
+};
+
+struct wayland_output {
+ struct weston_output base;
+ struct {
+ int draw_initial_frame;
+ struct wl_surface *surface;
+ struct wl_shell_surface *shell_surface;
+ struct wl_egl_window *egl_window;
+ } parent;
+ struct weston_mode mode;
+};
+
+struct wayland_input {
+ struct weston_seat base;
+ struct wayland_compositor *compositor;
+ struct wl_seat *seat;
+ struct wl_pointer *pointer;
+ struct wl_keyboard *keyboard;
+ struct wl_touch *touch;
+ struct wl_list link;
+ uint32_t key_serial;
+ uint32_t enter_serial;
+ int focus;
+ struct wayland_output *output;
+};
+
+static void
+create_border(struct wayland_compositor *c)
+{
+ pixman_image_t *image;
+ int32_t edges[4];
+
+ image = load_image(DATADIR "/weston/border.png");
+ if (!image) {
+ weston_log("couldn't load border image\n");
+ return;
+ }
+
+ edges[0] = c->border.left;
+ edges[1] = c->border.right;
+ edges[2] = c->border.top;
+ edges[3] = c->border.bottom;
+
+ gl_renderer_set_border(&c->base, pixman_image_get_width(image),
+ pixman_image_get_height(image),
+ pixman_image_get_data(image), edges);
+
+ pixman_image_unref(image);
+}
+
+static void
+frame_done(void *data, struct wl_callback *callback, uint32_t time)
+{
+ struct weston_output *output = data;
+
+ wl_callback_destroy(callback);
+ weston_output_finish_frame(output, time);
+}
+
+static const struct wl_callback_listener frame_listener = {
+ frame_done
+};
+
+static void
+buffer_release(void *data, struct wl_buffer *buffer)
+{
+ wl_buffer_destroy(buffer);
+}
+
+static const struct wl_buffer_listener buffer_listener = {
+ buffer_release
+};
+
+static void
+draw_initial_frame(struct wayland_output *output)
+{
+ struct wayland_compositor *c =
+ (struct wayland_compositor *) output->base.compositor;
+ struct wl_shm *shm = c->parent.shm;
+ struct wl_surface *surface = output->parent.surface;
+ struct wl_shm_pool *pool;
+ struct wl_buffer *buffer;
+
+ int width, height, stride;
+ int size;
+ int fd;
+ void *data;
+
+ width = output->mode.width + c->border.left + c->border.right;
+ height = output->mode.height + c->border.top + c->border.bottom;
+ stride = width * 4;
+ size = height * stride;
+
+ fd = os_create_anonymous_file(size);
+ if (fd < 0) {
+ perror("os_create_anonymous_file");
+ return;
+ }
+
+ data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (data == MAP_FAILED) {
+ perror("mmap");
+ close(fd);
+ return;
+ }
+
+ pool = wl_shm_create_pool(shm, fd, size);
+
+ buffer = wl_shm_pool_create_buffer(pool, 0,
+ width, height,
+ stride,
+ WL_SHM_FORMAT_ARGB8888);
+ wl_buffer_add_listener(buffer, &buffer_listener, buffer);
+ wl_shm_pool_destroy(pool);
+ close(fd);
+
+ memset(data, 0, size);
+
+ wl_surface_attach(surface, buffer, 0, 0);
+
+ /* We only need to damage some part, as its only transparant
+ * pixels anyway. */
+ wl_surface_damage(surface, 0, 0, 1, 1);
+}
+
+static void
+wayland_output_start_repaint_loop(struct weston_output *output_base)
+{
+ struct wayland_output *output = (struct wayland_output *) output_base;
+ struct wl_callback *callback;
+
+ /* If this is the initial frame, we need to attach a buffer so that
+ * the compositor can map the surface and include it in its render
+ * loop. If the surface doesn't end up in the render loop, the frame
+ * callback won't be invoked. The buffer is transparent and of the
+ * same size as the future real output buffer. */
+ if (output->parent.draw_initial_frame) {
+ output->parent.draw_initial_frame = 0;
+
+ draw_initial_frame(output);
+ }
+
+ callback = wl_surface_frame(output->parent.surface);
+ wl_callback_add_listener(callback, &frame_listener, output);
+ wl_surface_commit(output->parent.surface);
+}
+
+static int
+wayland_output_repaint(struct weston_output *output_base,
+ pixman_region32_t *damage)
+{
+ struct wayland_output *output = (struct wayland_output *) output_base;
+ struct weston_compositor *ec = output->base.compositor;
+ struct wl_callback *callback;
+
+ callback = wl_surface_frame(output->parent.surface);
+ wl_callback_add_listener(callback, &frame_listener, output);
+
+ ec->renderer->repaint_output(&output->base, damage);
+
+ pixman_region32_subtract(&ec->primary_plane.damage,
+ &ec->primary_plane.damage, damage);
+ return 0;
+}
+
+static void
+wayland_output_destroy(struct weston_output *output_base)
+{
+ struct wayland_output *output = (struct wayland_output *) output_base;
+
+ gl_renderer_output_destroy(output_base);
+
+ wl_egl_window_destroy(output->parent.egl_window);
+ free(output);
+
+ return;
+}
+
+static const struct wl_shell_surface_listener shell_surface_listener;
+
+static int
+wayland_compositor_create_output(struct wayland_compositor *c,
+ int width, int height)
+{
+ struct wayland_output *output;
+
+ output = zalloc(sizeof *output);
+ if (output == NULL)
+ return -1;
+
+ output->mode.flags =
+ WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
+ output->mode.width = width;
+ output->mode.height = height;
+ output->mode.refresh = 60;
+ wl_list_init(&output->base.mode_list);
+ wl_list_insert(&output->base.mode_list, &output->mode.link);
+
+ output->base.current_mode = &output->mode;
+ weston_output_init(&output->base, &c->base, 0, 0, width, height,
+ WL_OUTPUT_TRANSFORM_NORMAL, 1);
+
+ output->base.make = "waywayland";
+ output->base.model = "none";
+
+ weston_output_move(&output->base, 0, 0);
+
+ output->parent.surface =
+ wl_compositor_create_surface(c->parent.compositor);
+ wl_surface_set_user_data(output->parent.surface, output);
+
+ output->parent.egl_window =
+ wl_egl_window_create(output->parent.surface,
+ width + c->border.left + c->border.right,
+ height + c->border.top + c->border.bottom);
+ if (!output->parent.egl_window) {
+ weston_log("failure to create wl_egl_window\n");
+ goto cleanup_output;
+ }
+
+ if (gl_renderer_output_create(&output->base,
+ output->parent.egl_window) < 0)
+ goto cleanup_window;
+
+ output->parent.draw_initial_frame = 1;
+ output->parent.shell_surface =
+ wl_shell_get_shell_surface(c->parent.shell,
+ output->parent.surface);
+ wl_shell_surface_add_listener(output->parent.shell_surface,
+ &shell_surface_listener, output);
+ wl_shell_surface_set_toplevel(output->parent.shell_surface);
+
+ output->base.start_repaint_loop = wayland_output_start_repaint_loop;
+ output->base.repaint = wayland_output_repaint;
+ output->base.destroy = wayland_output_destroy;
+ output->base.assign_planes = NULL;
+ output->base.set_backlight = NULL;
+ output->base.set_dpms = NULL;
+ output->base.switch_mode = NULL;
+
+ wl_list_insert(c->base.output_list.prev, &output->base.link);
+
+ return 0;
+
+cleanup_window:
+ wl_egl_window_destroy(output->parent.egl_window);
+cleanup_output:
+ /* FIXME: cleanup weston_output */
+ free(output);
+
+ return -1;
+}
+
+static void
+shell_surface_ping(void *data, struct wl_shell_surface *shell_surface,
+ uint32_t serial)
+{
+ wl_shell_surface_pong(shell_surface, serial);
+}
+
+static void
+shell_surface_configure(void *data, struct wl_shell_surface *shell_surface,
+ uint32_t edges, int32_t width, int32_t height)
+{
+ /* FIXME: implement resizing */
+}
+
+static void
+shell_surface_popup_done(void *data, struct wl_shell_surface *shell_surface)
+{
+}
+
+static const struct wl_shell_surface_listener shell_surface_listener = {
+ shell_surface_ping,
+ shell_surface_configure,
+ shell_surface_popup_done
+};
+
+/* Events received from the wayland-server this compositor is client of: */
+
+/* parent output interface */
+static void
+display_handle_geometry(void *data,
+ struct wl_output *wl_output,
+ int x,
+ int y,
+ int physical_width,
+ int physical_height,
+ int subpixel,
+ const char *make,
+ const char *model,
+ int transform)
+{
+ struct wayland_compositor *c = data;
+
+ c->parent.screen_allocation.x = x;
+ c->parent.screen_allocation.y = y;
+}
+
+static void
+display_handle_mode(void *data,
+ struct wl_output *wl_output,
+ uint32_t flags,
+ int width,
+ int height,
+ int refresh)
+{
+ struct wayland_compositor *c = data;
+
+ c->parent.screen_allocation.width = width;
+ c->parent.screen_allocation.height = height;
+}
+
+static const struct wl_output_listener output_listener = {
+ display_handle_geometry,
+ display_handle_mode
+};
+
+static void
+check_focus(struct wayland_input *input, wl_fixed_t x, wl_fixed_t y)
+{
+ struct wayland_compositor *c = input->compositor;
+ int width, height, inside;
+
+ width = input->output->mode.width;
+ height = input->output->mode.height;
+
+ inside = c->border.left <= wl_fixed_to_int(x) &&
+ wl_fixed_to_int(x) < width + c->border.left &&
+ c->border.top <= wl_fixed_to_int(y) &&
+ wl_fixed_to_int(y) < height + c->border.top;
+
+ if (!input->focus && inside) {
+ notify_pointer_focus(&input->base, &input->output->base,
+ x - wl_fixed_from_int(c->border.left),
+ y = wl_fixed_from_int(c->border.top));
+ wl_pointer_set_cursor(input->pointer,
+ input->enter_serial, NULL, 0, 0);
+ } else if (input->focus && !inside) {
+ notify_pointer_focus(&input->base, NULL, 0, 0);
+ /* FIXME: Should set default cursor here. */
+ }
+
+ input->focus = inside;
+}
+
+/* parent input interface */
+static void
+input_handle_pointer_enter(void *data, struct wl_pointer *pointer,
+ uint32_t serial, struct wl_surface *surface,
+ wl_fixed_t x, wl_fixed_t y)
+{
+ struct wayland_input *input = data;
+
+ /* XXX: If we get a modifier event immediately before the focus,
+ * we should try to keep the same serial. */
+ input->enter_serial = serial;
+ input->output = wl_surface_get_user_data(surface);
+ check_focus(input, x, y);
+}
+
+static void
+input_handle_pointer_leave(void *data, struct wl_pointer *pointer,
+ uint32_t serial, struct wl_surface *surface)
+{
+ struct wayland_input *input = data;
+
+ notify_pointer_focus(&input->base, NULL, 0, 0);
+ input->output = NULL;
+ input->focus = 0;
+}
+
+static void
+input_handle_motion(void *data, struct wl_pointer *pointer,
+ uint32_t time, wl_fixed_t x, wl_fixed_t y)
+{
+ struct wayland_input *input = data;
+ struct wayland_compositor *c = input->compositor;
+
+ check_focus(input, x, y);
+ if (input->focus)
+ notify_motion(&input->base, time,
+ x - wl_fixed_from_int(c->border.left) -
+ input->base.pointer->x,
+ y - wl_fixed_from_int(c->border.top) -
+ input->base.pointer->y);
+}
+
+static void
+input_handle_button(void *data, struct wl_pointer *pointer,
+ uint32_t serial, uint32_t time, uint32_t button,
+ uint32_t state_w)
+{
+ struct wayland_input *input = data;
+ enum wl_pointer_button_state state = state_w;
+
+ notify_button(&input->base, time, button, state);
+}
+
+static void
+input_handle_axis(void *data, struct wl_pointer *pointer,
+ uint32_t time, uint32_t axis, wl_fixed_t value)
+{
+ struct wayland_input *input = data;
+
+ notify_axis(&input->base, time, axis, value);
+}
+
+static const struct wl_pointer_listener pointer_listener = {
+ input_handle_pointer_enter,
+ input_handle_pointer_leave,
+ input_handle_motion,
+ input_handle_button,
+ input_handle_axis,
+};
+
+static void
+input_handle_keymap(void *data, struct wl_keyboard *keyboard, uint32_t format,
+ int fd, uint32_t size)
+{
+ struct wayland_input *input = data;
+ struct xkb_keymap *keymap;
+ char *map_str;
+
+ if (!data) {
+ close(fd);
+ return;
+ }
+
+ if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
+ close(fd);
+ return;
+ }
+
+ map_str = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
+ if (map_str == MAP_FAILED) {
+ close(fd);
+ return;
+ }
+
+ keymap = xkb_map_new_from_string(input->compositor->base.xkb_context,
+ map_str,
+ XKB_KEYMAP_FORMAT_TEXT_V1,
+ 0);
+ munmap(map_str, size);
+ close(fd);
+
+ if (!keymap) {
+ weston_log("failed to compile keymap\n");
+ return;
+ }
+
+ weston_seat_init_keyboard(&input->base, keymap);
+ xkb_map_unref(keymap);
+}
+
+static void
+input_handle_keyboard_enter(void *data,
+ struct wl_keyboard *keyboard,
+ uint32_t serial,
+ struct wl_surface *surface,
+ struct wl_array *keys)
+{
+ struct wayland_input *input = data;
+
+ /* XXX: If we get a modifier event immediately before the focus,
+ * we should try to keep the same serial. */
+ notify_keyboard_focus_in(&input->base, keys,
+ STATE_UPDATE_AUTOMATIC);
+}
+
+static void
+input_handle_keyboard_leave(void *data,
+ struct wl_keyboard *keyboard,
+ uint32_t serial,
+ struct wl_surface *surface)
+{
+ struct wayland_input *input = data;
+
+ notify_keyboard_focus_out(&input->base);
+}
+
+static void
+input_handle_key(void *data, struct wl_keyboard *keyboard,
+ uint32_t serial, uint32_t time, uint32_t key, uint32_t state)
+{
+ struct wayland_input *input = data;
+
+ input->key_serial = serial;
+ notify_key(&input->base, time, key,
+ state ? WL_KEYBOARD_KEY_STATE_PRESSED :
+ WL_KEYBOARD_KEY_STATE_RELEASED,
+ STATE_UPDATE_NONE);
+}
+
+static void
+input_handle_modifiers(void *data, struct wl_keyboard *keyboard,
+ uint32_t serial_in, uint32_t mods_depressed,
+ uint32_t mods_latched, uint32_t mods_locked,
+ uint32_t group)
+{
+ struct wayland_input *input = data;
+ struct wayland_compositor *c = input->compositor;
+ uint32_t serial_out;
+
+ /* If we get a key event followed by a modifier event with the
+ * same serial number, then we try to preserve those semantics by
+ * reusing the same serial number on the way out too. */
+ if (serial_in == input->key_serial)
+ serial_out = wl_display_get_serial(c->base.wl_display);
+ else
+ serial_out = wl_display_next_serial(c->base.wl_display);
+
+ xkb_state_update_mask(input->base.xkb_state.state,
+ mods_depressed, mods_latched,
+ mods_locked, 0, 0, group);
+ notify_modifiers(&input->base, serial_out);
+}
+
+static const struct wl_keyboard_listener keyboard_listener = {
+ input_handle_keymap,
+ input_handle_keyboard_enter,
+ input_handle_keyboard_leave,
+ input_handle_key,
+ input_handle_modifiers,
+};
+
+static void
+input_handle_capabilities(void *data, struct wl_seat *seat,
+ enum wl_seat_capability caps)
+{
+ struct wayland_input *input = data;
+
+ if ((caps & WL_SEAT_CAPABILITY_POINTER) && !input->pointer) {
+ input->pointer = wl_seat_get_pointer(seat);
+ wl_pointer_set_user_data(input->pointer, input);
+ wl_pointer_add_listener(input->pointer, &pointer_listener,
+ input);
+ weston_seat_init_pointer(&input->base);
+ } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && input->pointer) {
+ wl_pointer_destroy(input->pointer);
+ input->pointer = NULL;
+ }
+
+ if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !input->keyboard) {
+ input->keyboard = wl_seat_get_keyboard(seat);
+ wl_keyboard_set_user_data(input->keyboard, input);
+ wl_keyboard_add_listener(input->keyboard, &keyboard_listener,
+ input);
+ } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && input->keyboard) {
+ wl_keyboard_destroy(input->keyboard);
+ input->keyboard = NULL;
+ }
+}
+
+static const struct wl_seat_listener seat_listener = {
+ input_handle_capabilities,
+};
+
+static void
+display_add_seat(struct wayland_compositor *c, uint32_t id)
+{
+ struct wayland_input *input;
+
+ input = zalloc(sizeof *input);
+ if (input == NULL)
+ return;
+
+ weston_seat_init(&input->base, &c->base, "default");
+ input->compositor = c;
+ input->seat = wl_registry_bind(c->parent.registry, id,
+ &wl_seat_interface, 1);
+ wl_list_insert(c->input_list.prev, &input->link);
+
+ wl_seat_add_listener(input->seat, &seat_listener, input);
+ wl_seat_set_user_data(input->seat, input);
+}
+
+static void
+registry_handle_global(void *data, struct wl_registry *registry, uint32_t name,
+ const char *interface, uint32_t version)
+{
+ struct wayland_compositor *c = data;
+
+ if (strcmp(interface, "wl_compositor") == 0) {
+ c->parent.compositor =
+ wl_registry_bind(registry, name,
+ &wl_compositor_interface, 1);
+ } else if (strcmp(interface, "wl_output") == 0) {
+ c->parent.output =
+ wl_registry_bind(registry, name,
+ &wl_output_interface, 1);
+ wl_output_add_listener(c->parent.output, &output_listener, c);
+ } else if (strcmp(interface, "wl_shell") == 0) {
+ c->parent.shell =
+ wl_registry_bind(registry, name,
+ &wl_shell_interface, 1);
+ } else if (strcmp(interface, "wl_seat") == 0) {
+ display_add_seat(c, name);
+ } else if (strcmp(interface, "wl_shm") == 0) {
+ c->parent.shm =
+ wl_registry_bind(registry, name, &wl_shm_interface, 1);
+ }
+}
+
+static const struct wl_registry_listener registry_listener = {
+ registry_handle_global
+};
+
+static int
+wayland_compositor_handle_event(int fd, uint32_t mask, void *data)
+{
+ struct wayland_compositor *c = data;
+ int count = 0;
+
+ if (mask & WL_EVENT_READABLE)
+ count = wl_display_dispatch(c->parent.wl_display);
+ if (mask & WL_EVENT_WRITABLE)
+ wl_display_flush(c->parent.wl_display);
+
+ if (mask == 0) {
+ count = wl_display_dispatch_pending(c->parent.wl_display);
+ wl_display_flush(c->parent.wl_display);
+ }
+
+ return count;
+}
+
+static void
+wayland_restore(struct weston_compositor *ec)
+{
+}
+
+static void
+wayland_destroy(struct weston_compositor *ec)
+{
+ struct wayland_compositor *c = (struct wayland_compositor *) ec;
+
+ ec->renderer->destroy(ec);
+
+ weston_compositor_shutdown(ec);
+
+ if (c->parent.shm)
+ wl_shm_destroy(c->parent.shm);
+
+ free(ec);
+}
+
+static struct weston_compositor *
+wayland_compositor_create(struct wl_display *display,
+ int width, int height, const char *display_name,
+ int *argc, char *argv[],
+ struct weston_config *config)
+{
+ struct wayland_compositor *c;
+ struct wl_event_loop *loop;
+ int fd;
+
+ c = zalloc(sizeof *c);
+ if (c == NULL)
+ return NULL;
+
+ if (weston_compositor_init(&c->base, display, argc, argv,
+ config) < 0)
+ goto err_free;
+
+ c->parent.wl_display = wl_display_connect(display_name);
+
+ if (c->parent.wl_display == NULL) {
+ weston_log("failed to create display: %m\n");
+ goto err_compositor;
+ }
+
+ wl_list_init(&c->input_list);
+ c->parent.registry = wl_display_get_registry(c->parent.wl_display);
+ wl_registry_add_listener(c->parent.registry, &registry_listener, c);
+ wl_display_dispatch(c->parent.wl_display);
+
+ c->base.wl_display = display;
+ if (gl_renderer_create(&c->base, c->parent.wl_display,
+ gl_renderer_alpha_attribs,
+ NULL) < 0)
+ goto err_display;
+
+ c->base.destroy = wayland_destroy;
+ c->base.restore = wayland_restore;
+
+ c->border.top = 30;
+ c->border.bottom = 24;
+ c->border.left = 25;
+ c->border.right = 26;
+
+ /* requires border fields */
+ if (wayland_compositor_create_output(c, width, height) < 0)
+ goto err_gl;
+
+ /* requires gl_renderer_output_state_create called
+ * by wayland_compositor_create_output */
+ create_border(c);
+
+ loop = wl_display_get_event_loop(c->base.wl_display);
+
+ fd = wl_display_get_fd(c->parent.wl_display);
+ c->parent.wl_source =
+ wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE,
+ wayland_compositor_handle_event, c);
+ if (c->parent.wl_source == NULL)
+ goto err_gl;
+
+ wl_event_source_check(c->parent.wl_source);
+
+ return &c->base;
+
+err_gl:
+ c->base.renderer->destroy(&c->base);
+err_display:
+ wl_display_disconnect(c->parent.wl_display);
+err_compositor:
+ weston_compositor_shutdown(&c->base);
+err_free:
+ free(c);
+ return NULL;
+}
+
+WL_EXPORT struct weston_compositor *
+backend_init(struct wl_display *display, int *argc, char *argv[],
+ struct weston_config *config)
+{
+ int width = 1024, height = 640;
+ char *display_name = NULL;
+
+ const struct weston_option wayland_options[] = {
+ { WESTON_OPTION_INTEGER, "width", 0, &width },
+ { WESTON_OPTION_INTEGER, "height", 0, &height },
+ { WESTON_OPTION_STRING, "display", 0, &display_name },
+ };
+
+ parse_options(wayland_options,
+ ARRAY_LENGTH(wayland_options), argc, argv);
+
+ return wayland_compositor_create(display, width, height, display_name,
+ argc, argv, config);
+}
diff --git a/src/compositor-x11.c b/src/compositor-x11.c
new file mode 100644
index 00000000..e6022f77
--- /dev/null
+++ b/src/compositor-x11.c
@@ -0,0 +1,1621 @@
+/*
+ * Copyright © 2008-2011 Kristian Høgsberg
+ * Copyright © 2010-2011 Intel Corporation
+ * Copyright © 2013 Vasily Khoruzhick <anarsoul@gmail.com>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that 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.
+ */
+
+#include "config.h"
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/shm.h>
+#include <linux/input.h>
+
+#include <xcb/xcb.h>
+#include <xcb/shm.h>
+#ifdef HAVE_XCB_XKB
+#include <xcb/xkb.h>
+#endif
+
+#include <X11/Xlib.h>
+#include <X11/Xlib-xcb.h>
+
+#include <xkbcommon/xkbcommon.h>
+
+#include "compositor.h"
+#include "gl-renderer.h"
+#include "pixman-renderer.h"
+#include "../shared/config-parser.h"
+#include "../shared/image-loader.h"
+
+#define DEFAULT_AXIS_STEP_DISTANCE wl_fixed_from_int(10)
+
+static int option_width;
+static int option_height;
+static int option_count;
+
+struct x11_compositor {
+ struct weston_compositor base;
+
+ Display *dpy;
+ xcb_connection_t *conn;
+ xcb_screen_t *screen;
+ xcb_cursor_t null_cursor;
+ struct wl_array keys;
+ struct wl_event_source *xcb_source;
+ struct xkb_keymap *xkb_keymap;
+ unsigned int has_xkb;
+ uint8_t xkb_event_base;
+ int use_pixman;
+
+ int has_net_wm_state_fullscreen;
+
+ /* We could map multi-pointer X to multiple wayland seats, but
+ * for now we only support core X input. */
+ struct weston_seat core_seat;
+ wl_fixed_t prev_x;
+ wl_fixed_t prev_y;
+
+ struct {
+ xcb_atom_t wm_protocols;
+ xcb_atom_t wm_normal_hints;
+ xcb_atom_t wm_size_hints;
+ xcb_atom_t wm_delete_window;
+ xcb_atom_t wm_class;
+ xcb_atom_t net_wm_name;
+ xcb_atom_t net_supporting_wm_check;
+ xcb_atom_t net_supported;
+ xcb_atom_t net_wm_icon;
+ xcb_atom_t net_wm_state;
+ xcb_atom_t net_wm_state_fullscreen;
+ xcb_atom_t string;
+ xcb_atom_t utf8_string;
+ xcb_atom_t cardinal;
+ xcb_atom_t xkb_names;
+ } atom;
+};
+
+struct x11_output {
+ struct weston_output base;
+
+ xcb_window_t window;
+ struct weston_mode mode;
+ struct wl_event_source *finish_frame_timer;
+
+ xcb_gc_t gc;
+ xcb_shm_seg_t segment;
+ pixman_image_t *hw_surface;
+ int shm_id;
+ void *buf;
+ uint8_t depth;
+ int32_t scale;
+};
+
+static struct xkb_keymap *
+x11_compositor_get_keymap(struct x11_compositor *c)
+{
+ xcb_get_property_cookie_t cookie;
+ xcb_get_property_reply_t *reply;
+ struct xkb_rule_names names;
+ struct xkb_keymap *ret;
+ const char *value_all, *value_part;
+ int length_all, length_part;
+
+ memset(&names, 0, sizeof(names));
+
+ cookie = xcb_get_property(c->conn, 0, c->screen->root,
+ c->atom.xkb_names, c->atom.string, 0, 1024);
+ reply = xcb_get_property_reply(c->conn, cookie, NULL);
+ if (reply == NULL)
+ return NULL;
+
+ value_all = xcb_get_property_value(reply);
+ length_all = xcb_get_property_value_length(reply);
+ value_part = value_all;
+
+#define copy_prop_value(to) \
+ length_part = strlen(value_part); \
+ if (value_part + length_part < (value_all + length_all) && \
+ length_part > 0) \
+ names.to = value_part; \
+ value_part += length_part + 1;
+
+ copy_prop_value(rules);
+ copy_prop_value(model);
+ copy_prop_value(layout);
+ copy_prop_value(variant);
+ copy_prop_value(options);
+#undef copy_prop_value
+
+ ret = xkb_map_new_from_names(c->base.xkb_context, &names, 0);
+
+ free(reply);
+ return ret;
+}
+
+static uint32_t
+get_xkb_mod_mask(struct x11_compositor *c, uint32_t in)
+{
+ struct weston_xkb_info *info = c->core_seat.xkb_info;
+ uint32_t ret = 0;
+
+ if ((in & ShiftMask) && info->shift_mod != XKB_MOD_INVALID)
+ ret |= (1 << info->shift_mod);
+ if ((in & LockMask) && info->caps_mod != XKB_MOD_INVALID)
+ ret |= (1 << info->caps_mod);
+ if ((in & ControlMask) && info->ctrl_mod != XKB_MOD_INVALID)
+ ret |= (1 << info->ctrl_mod);
+ if ((in & Mod1Mask) && info->alt_mod != XKB_MOD_INVALID)
+ ret |= (1 << info->alt_mod);
+ if ((in & Mod2Mask) && info->mod2_mod != XKB_MOD_INVALID)
+ ret |= (1 << info->mod2_mod);
+ if ((in & Mod3Mask) && info->mod3_mod != XKB_MOD_INVALID)
+ ret |= (1 << info->mod3_mod);
+ if ((in & Mod4Mask) && info->super_mod != XKB_MOD_INVALID)
+ ret |= (1 << info->super_mod);
+ if ((in & Mod5Mask) && info->mod5_mod != XKB_MOD_INVALID)
+ ret |= (1 << info->mod5_mod);
+
+ return ret;
+}
+
+static void
+x11_compositor_setup_xkb(struct x11_compositor *c)
+{
+#ifndef HAVE_XCB_XKB
+ weston_log("XCB-XKB not available during build\n");
+ c->has_xkb = 0;
+ c->xkb_event_base = 0;
+ return;
+#else
+ const xcb_query_extension_reply_t *ext;
+ xcb_generic_error_t *error;
+ xcb_void_cookie_t select;
+ xcb_xkb_use_extension_cookie_t use_ext;
+ xcb_xkb_use_extension_reply_t *use_ext_reply;
+ xcb_xkb_per_client_flags_cookie_t pcf;
+ xcb_xkb_per_client_flags_reply_t *pcf_reply;
+ xcb_xkb_get_state_cookie_t state;
+ xcb_xkb_get_state_reply_t *state_reply;
+
+ c->has_xkb = 0;
+ c->xkb_event_base = 0;
+
+ ext = xcb_get_extension_data(c->conn, &xcb_xkb_id);
+ if (!ext) {
+ weston_log("XKB extension not available on host X11 server\n");
+ return;
+ }
+ c->xkb_event_base = ext->first_event;
+
+ select = xcb_xkb_select_events_checked(c->conn,
+ XCB_XKB_ID_USE_CORE_KBD,
+ XCB_XKB_EVENT_TYPE_STATE_NOTIFY,
+ 0,
+ XCB_XKB_EVENT_TYPE_STATE_NOTIFY,
+ 0,
+ 0,
+ NULL);
+ error = xcb_request_check(c->conn, select);
+ if (error) {
+ weston_log("error: failed to select for XKB state events\n");
+ free(error);
+ return;
+ }
+
+ use_ext = xcb_xkb_use_extension(c->conn,
+ XCB_XKB_MAJOR_VERSION,
+ XCB_XKB_MINOR_VERSION);
+ use_ext_reply = xcb_xkb_use_extension_reply(c->conn, use_ext, NULL);
+ if (!use_ext_reply) {
+ weston_log("couldn't start using XKB extension\n");
+ return;
+ }
+
+ if (!use_ext_reply->supported) {
+ weston_log("XKB extension version on the server is too old "
+ "(want %d.%d, has %d.%d)\n",
+ XCB_XKB_MAJOR_VERSION, XCB_XKB_MINOR_VERSION,
+ use_ext_reply->serverMajor, use_ext_reply->serverMinor);
+ free(use_ext_reply);
+ return;
+ }
+ free(use_ext_reply);
+
+ pcf = xcb_xkb_per_client_flags(c->conn,
+ XCB_XKB_ID_USE_CORE_KBD,
+ XCB_XKB_PER_CLIENT_FLAG_DETECTABLE_AUTO_REPEAT,
+ XCB_XKB_PER_CLIENT_FLAG_DETECTABLE_AUTO_REPEAT,
+ 0,
+ 0,
+ 0);
+ pcf_reply = xcb_xkb_per_client_flags_reply(c->conn, pcf, NULL);
+ if (!pcf_reply ||
+ !(pcf_reply->value & XCB_XKB_PER_CLIENT_FLAG_DETECTABLE_AUTO_REPEAT)) {
+ weston_log("failed to set XKB per-client flags, not using "
+ "detectable repeat\n");
+ free(pcf_reply);
+ return;
+ }
+ free(pcf_reply);
+
+ state = xcb_xkb_get_state(c->conn, XCB_XKB_ID_USE_CORE_KBD);
+ state_reply = xcb_xkb_get_state_reply(c->conn, state, NULL);
+ if (!state_reply) {
+ weston_log("failed to get initial XKB state\n");
+ return;
+ }
+
+ xkb_state_update_mask(c->core_seat.xkb_state.state,
+ get_xkb_mod_mask(c, state_reply->baseMods),
+ get_xkb_mod_mask(c, state_reply->latchedMods),
+ get_xkb_mod_mask(c, state_reply->lockedMods),
+ 0,
+ 0,
+ state_reply->group);
+
+ free(state_reply);
+
+ c->has_xkb = 1;
+#endif
+}
+
+static int
+x11_input_create(struct x11_compositor *c, int no_input)
+{
+ struct xkb_keymap *keymap;
+
+ weston_seat_init(&c->core_seat, &c->base, "default");
+
+ if (no_input)
+ return 0;
+
+ weston_seat_init_pointer(&c->core_seat);
+
+ keymap = x11_compositor_get_keymap(c);
+ if (weston_seat_init_keyboard(&c->core_seat, keymap) < 0)
+ return -1;
+ if (keymap)
+ xkb_map_unref(keymap);
+
+ x11_compositor_setup_xkb(c);
+
+ return 0;
+}
+
+static void
+x11_input_destroy(struct x11_compositor *compositor)
+{
+ weston_seat_release(&compositor->core_seat);
+}
+
+static void
+x11_output_start_repaint_loop(struct weston_output *output)
+{
+ uint32_t msec;
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+ msec = tv.tv_sec * 1000 + tv.tv_usec / 1000;
+ weston_output_finish_frame(output, msec);
+}
+
+static int
+x11_output_repaint_gl(struct weston_output *output_base,
+ pixman_region32_t *damage)
+{
+ struct x11_output *output = (struct x11_output *)output_base;
+ struct weston_compositor *ec = output->base.compositor;
+
+ ec->renderer->repaint_output(output_base, damage);
+
+ pixman_region32_subtract(&ec->primary_plane.damage,
+ &ec->primary_plane.damage, damage);
+
+ wl_event_source_timer_update(output->finish_frame_timer, 10);
+ return 0;
+}
+
+static void
+set_clip_for_output(struct weston_output *output_base, pixman_region32_t *region)
+{
+ struct x11_output *output = (struct x11_output *)output_base;
+ struct weston_compositor *ec = output->base.compositor;
+ struct x11_compositor *c = (struct x11_compositor *)ec;
+ pixman_box32_t *rects;
+ xcb_rectangle_t *output_rects;
+ pixman_box32_t rect, transformed_rect;
+ xcb_void_cookie_t cookie;
+ int width, height, nrects, i;
+ xcb_generic_error_t *err;
+
+ rects = pixman_region32_rectangles(region, &nrects);
+ output_rects = calloc(nrects, sizeof(xcb_rectangle_t));
+
+ if (output_rects == NULL)
+ return;
+
+ width = output_base->width;
+ height = output_base->height;
+
+ for (i = 0; i < nrects; i++) {
+ rect = rects[i];
+ rect.x1 -= output_base->x;
+ rect.y1 -= output_base->y;
+ rect.x2 -= output_base->x;
+ rect.y2 -= output_base->y;
+
+ switch (output_base->transform) {
+ default:
+ case WL_OUTPUT_TRANSFORM_NORMAL:
+ transformed_rect.x1 = rect.x1;
+ transformed_rect.y1 = rect.y1;
+ transformed_rect.x2 = rect.x2;
+ transformed_rect.y2 = rect.y2;
+ break;
+ case WL_OUTPUT_TRANSFORM_90:
+ transformed_rect.x1 = height - rect.y2;
+ transformed_rect.y1 = rect.x1;
+ transformed_rect.x2 = height - rect.y1;
+ transformed_rect.y2 = rect.x2;
+ break;
+ case WL_OUTPUT_TRANSFORM_180:
+ transformed_rect.x1 = width - rect.x2;
+ transformed_rect.y1 = height - rect.y2;
+ transformed_rect.x2 = width - rect.x1;
+ transformed_rect.y2 = height - rect.y1;
+ break;
+ case WL_OUTPUT_TRANSFORM_270:
+ transformed_rect.x1 = rect.y1;
+ transformed_rect.y1 = width - rect.x2;
+ transformed_rect.x2 = rect.y2;
+ transformed_rect.y2 = width - rect.x1;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED:
+ transformed_rect.x1 = width - rect.x2;
+ transformed_rect.y1 = rect.y1;
+ transformed_rect.x2 = width - rect.x1;
+ transformed_rect.y2 = rect.y2;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+ transformed_rect.x1 = height - rect.y2;
+ transformed_rect.y1 = width - rect.x2;
+ transformed_rect.x2 = height - rect.y1;
+ transformed_rect.y2 = width - rect.x1;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+ transformed_rect.x1 = rect.x1;
+ transformed_rect.y1 = height - rect.y2;
+ transformed_rect.x2 = rect.x2;
+ transformed_rect.y2 = height - rect.y1;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+ transformed_rect.x1 = rect.y1;
+ transformed_rect.y1 = rect.x1;
+ transformed_rect.x2 = rect.y2;
+ transformed_rect.y2 = rect.x2;
+ break;
+ }
+
+ transformed_rect.x1 *= output_base->current_scale;
+ transformed_rect.y1 *= output_base->current_scale;
+ transformed_rect.x2 *= output_base->current_scale;
+ transformed_rect.y2 *= output_base->current_scale;
+
+ output_rects[i].x = transformed_rect.x1;
+ output_rects[i].y = transformed_rect.y1;
+ output_rects[i].width = transformed_rect.x2 - transformed_rect.x1;
+ output_rects[i].height = transformed_rect.y2 - transformed_rect.y1;
+ }
+
+ cookie = xcb_set_clip_rectangles_checked(c->conn, XCB_CLIP_ORDERING_UNSORTED,
+ output->gc,
+ 0, 0, nrects,
+ output_rects);
+ err = xcb_request_check(c->conn, cookie);
+ if (err != NULL) {
+ weston_log("Failed to set clip rects, err: %d\n", err->error_code);
+ free(err);
+ }
+ free(output_rects);
+}
+
+
+static int
+x11_output_repaint_shm(struct weston_output *output_base,
+ pixman_region32_t *damage)
+{
+ struct x11_output *output = (struct x11_output *)output_base;
+ struct weston_compositor *ec = output->base.compositor;
+ struct x11_compositor *c = (struct x11_compositor *)ec;
+ xcb_void_cookie_t cookie;
+ xcb_generic_error_t *err;
+
+ pixman_renderer_output_set_buffer(output_base, output->hw_surface);
+ ec->renderer->repaint_output(output_base, damage);
+
+ pixman_region32_subtract(&ec->primary_plane.damage,
+ &ec->primary_plane.damage, damage);
+ set_clip_for_output(output_base, damage);
+ cookie = xcb_shm_put_image_checked(c->conn, output->window, output->gc,
+ pixman_image_get_width(output->hw_surface),
+ pixman_image_get_height(output->hw_surface),
+ 0, 0,
+ pixman_image_get_width(output->hw_surface),
+ pixman_image_get_height(output->hw_surface),
+ 0, 0, output->depth, XCB_IMAGE_FORMAT_Z_PIXMAP,
+ 0, output->segment, 0);
+ err = xcb_request_check(c->conn, cookie);
+ if (err != NULL) {
+ weston_log("Failed to put shm image, err: %d\n", err->error_code);
+ free(err);
+ }
+
+ wl_event_source_timer_update(output->finish_frame_timer, 10);
+ return 0;
+}
+
+static int
+finish_frame_handler(void *data)
+{
+ struct x11_output *output = data;
+
+ x11_output_start_repaint_loop(&output->base);
+
+ return 1;
+}
+
+static void
+x11_output_deinit_shm(struct x11_compositor *c, struct x11_output *output)
+{
+ xcb_void_cookie_t cookie;
+ xcb_generic_error_t *err;
+ xcb_free_gc(c->conn, output->gc);
+
+ pixman_image_unref(output->hw_surface);
+ output->hw_surface = NULL;
+ cookie = xcb_shm_detach_checked(c->conn, output->segment);
+ err = xcb_request_check(c->conn, cookie);
+ if (err) {
+ weston_log("xcb_shm_detach failed, error %d\n", err->error_code);
+ free(err);
+ }
+ shmdt(output->buf);
+}
+
+static void
+x11_output_destroy(struct weston_output *output_base)
+{
+ struct x11_output *output = (struct x11_output *)output_base;
+ struct x11_compositor *compositor =
+ (struct x11_compositor *)output->base.compositor;
+
+ wl_list_remove(&output->base.link);
+ wl_event_source_remove(output->finish_frame_timer);
+
+ if (compositor->use_pixman) {
+ pixman_renderer_output_destroy(output_base);
+ x11_output_deinit_shm(compositor, output);
+ } else
+ gl_renderer_output_destroy(output_base);
+
+ xcb_destroy_window(compositor->conn, output->window);
+
+ weston_output_destroy(&output->base);
+
+ free(output);
+}
+
+static void
+x11_output_set_wm_protocols(struct x11_compositor *c,
+ struct x11_output *output)
+{
+ xcb_atom_t list[1];
+
+ list[0] = c->atom.wm_delete_window;
+ xcb_change_property (c->conn,
+ XCB_PROP_MODE_REPLACE,
+ output->window,
+ c->atom.wm_protocols,
+ XCB_ATOM_ATOM,
+ 32,
+ ARRAY_LENGTH(list),
+ list);
+}
+
+struct wm_normal_hints {
+ uint32_t flags;
+ uint32_t pad[4];
+ int32_t min_width, min_height;
+ int32_t max_width, max_height;
+ int32_t width_inc, height_inc;
+ int32_t min_aspect_x, min_aspect_y;
+ int32_t max_aspect_x, max_aspect_y;
+ int32_t base_width, base_height;
+ int32_t win_gravity;
+};
+
+#define WM_NORMAL_HINTS_MIN_SIZE 16
+#define WM_NORMAL_HINTS_MAX_SIZE 32
+
+static void
+x11_output_set_icon(struct x11_compositor *c,
+ struct x11_output *output, const char *filename)
+{
+ uint32_t *icon;
+ int32_t width, height;
+ pixman_image_t *image;
+
+ image = load_image(filename);
+ if (!image)
+ return;
+ width = pixman_image_get_width(image);
+ height = pixman_image_get_height(image);
+ icon = malloc(width * height * 4 + 8);
+ if (!icon) {
+ pixman_image_unref(image);
+ return;
+ }
+
+ icon[0] = width;
+ icon[1] = height;
+ memcpy(icon + 2, pixman_image_get_data(image), width * height * 4);
+ xcb_change_property(c->conn, XCB_PROP_MODE_REPLACE, output->window,
+ c->atom.net_wm_icon, c->atom.cardinal, 32,
+ width * height + 2, icon);
+ free(icon);
+ pixman_image_unref(image);
+}
+
+static void
+x11_output_wait_for_map(struct x11_compositor *c, struct x11_output *output)
+{
+ xcb_map_notify_event_t *map_notify;
+ xcb_configure_notify_event_t *configure_notify;
+ xcb_generic_event_t *event;
+ int mapped = 0, configured = 0;
+ uint8_t response_type;
+
+ /* This isn't the nicest way to do this. Ideally, we could
+ * just go back to the main loop and once we get the configure
+ * notify, we add the output to the compositor. While we do
+ * support output hotplug, we can't start up with no outputs.
+ * We could add the output and then resize once we get the
+ * configure notify, but we don't want to start up and
+ * immediately resize the output.
+ *
+ * Also, some window managers don't give us our final
+ * fullscreen size before map_notify, so if we don't get a
+ * configure_notify before map_notify, we just wait for the
+ * first one and hope that's our size. */
+
+ xcb_flush(c->conn);
+
+ while (!mapped || !configured) {
+ event = xcb_wait_for_event(c->conn);
+ response_type = event->response_type & ~0x80;
+
+ switch (response_type) {
+ case XCB_MAP_NOTIFY:
+ map_notify = (xcb_map_notify_event_t *) event;
+ if (map_notify->window == output->window)
+ mapped = 1;
+ break;
+
+ case XCB_CONFIGURE_NOTIFY:
+ configure_notify =
+ (xcb_configure_notify_event_t *) event;
+
+
+ if (configure_notify->width % output->scale != 0 ||
+ configure_notify->height % output->scale != 0)
+ weston_log("Resolution is not a multiple of screen size, rounding\n");
+ output->mode.width = configure_notify->width / output->scale;
+ output->mode.height = configure_notify->height / output->scale;
+ configured = 1;
+ break;
+ }
+ }
+}
+
+static xcb_visualtype_t *
+find_visual_by_id(xcb_screen_t *screen,
+ xcb_visualid_t id)
+{
+ xcb_depth_iterator_t i;
+ xcb_visualtype_iterator_t j;
+ for (i = xcb_screen_allowed_depths_iterator(screen);
+ i.rem;
+ xcb_depth_next(&i)) {
+ for (j = xcb_depth_visuals_iterator(i.data);
+ j.rem;
+ xcb_visualtype_next(&j)) {
+ if (j.data->visual_id == id)
+ return j.data;
+ }
+ }
+ return 0;
+}
+
+static uint8_t
+get_depth_of_visual(xcb_screen_t *screen,
+ xcb_visualid_t id)
+{
+ xcb_depth_iterator_t i;
+ xcb_visualtype_iterator_t j;
+ for (i = xcb_screen_allowed_depths_iterator(screen);
+ i.rem;
+ xcb_depth_next(&i)) {
+ for (j = xcb_depth_visuals_iterator(i.data);
+ j.rem;
+ xcb_visualtype_next(&j)) {
+ if (j.data->visual_id == id)
+ return i.data->depth;
+ }
+ }
+ return 0;
+}
+
+static int
+x11_output_init_shm(struct x11_compositor *c, struct x11_output *output,
+ int width, int height)
+{
+ xcb_screen_iterator_t iter;
+ xcb_visualtype_t *visual_type;
+ xcb_format_iterator_t fmt;
+ xcb_void_cookie_t cookie;
+ xcb_generic_error_t *err;
+ const xcb_query_extension_reply_t *ext;
+ int bitsperpixel = 0;
+ pixman_format_code_t pixman_format;
+
+ /* Check if SHM is available */
+ ext = xcb_get_extension_data(c->conn, &xcb_shm_id);
+ if (ext == NULL || !ext->present) {
+ /* SHM is missing */
+ weston_log("SHM extension is not available\n");
+ errno = ENOENT;
+ return -1;
+ }
+
+ iter = xcb_setup_roots_iterator(xcb_get_setup(c->conn));
+ visual_type = find_visual_by_id(iter.data, iter.data->root_visual);
+ if (!visual_type) {
+ weston_log("Failed to lookup visual for root window\n");
+ errno = ENOENT;
+ return -1;
+ }
+ weston_log("Found visual, bits per value: %d, red_mask: %.8x, green_mask: %.8x, blue_mask: %.8x\n",
+ visual_type->bits_per_rgb_value,
+ visual_type->red_mask,
+ visual_type->green_mask,
+ visual_type->blue_mask);
+ output->depth = get_depth_of_visual(iter.data, iter.data->root_visual);
+ weston_log("Visual depth is %d\n", output->depth);
+
+ for (fmt = xcb_setup_pixmap_formats_iterator(xcb_get_setup(c->conn));
+ fmt.rem;
+ xcb_format_next(&fmt)) {
+ if (fmt.data->depth == output->depth) {
+ bitsperpixel = fmt.data->bits_per_pixel;
+ break;
+ }
+ }
+ weston_log("Found format for depth %d, bpp: %d\n",
+ output->depth, bitsperpixel);
+
+ if (bitsperpixel == 32 &&
+ visual_type->red_mask == 0xff0000 &&
+ visual_type->green_mask == 0x00ff00 &&
+ visual_type->blue_mask == 0x0000ff) {
+ weston_log("Will use x8r8g8b8 format for SHM surfaces\n");
+ pixman_format = PIXMAN_x8r8g8b8;
+ } else {
+ weston_log("Can't find appropriate format for SHM pixmap\n");
+ errno = ENOTSUP;
+ return -1;
+ }
+
+
+ /* Create SHM segment and attach it */
+ output->shm_id = shmget(IPC_PRIVATE, width * height * (bitsperpixel / 8), IPC_CREAT | S_IRWXU);
+ if (output->shm_id == -1) {
+ weston_log("x11shm: failed to allocate SHM segment\n");
+ return -1;
+ }
+ output->buf = shmat(output->shm_id, NULL, 0 /* read/write */);
+ if (-1 == (long)output->buf) {
+ weston_log("x11shm: failed to attach SHM segment\n");
+ return -1;
+ }
+ output->segment = xcb_generate_id(c->conn);
+ cookie = xcb_shm_attach_checked(c->conn, output->segment, output->shm_id, 1);
+ err = xcb_request_check(c->conn, cookie);
+ if (err) {
+ weston_log("x11shm: xcb_shm_attach error %d\n", err->error_code);
+ free(err);
+ return -1;
+ }
+
+ shmctl(output->shm_id, IPC_RMID, NULL);
+
+ /* Now create pixman image */
+ output->hw_surface = pixman_image_create_bits(pixman_format, width, height, output->buf,
+ width * (bitsperpixel / 8));
+
+ output->gc = xcb_generate_id(c->conn);
+ xcb_create_gc(c->conn, output->gc, output->window, 0, NULL);
+
+ return 0;
+}
+
+static struct x11_output *
+x11_compositor_create_output(struct x11_compositor *c, int x, int y,
+ int width, int height, int fullscreen,
+ int no_input, char *configured_name,
+ uint32_t transform, int32_t scale)
+{
+ static const char name[] = "Weston Compositor";
+ static const char class[] = "weston-1\0Weston Compositor";
+ char title[32];
+ struct x11_output *output;
+ xcb_screen_iterator_t iter;
+ struct wm_normal_hints normal_hints;
+ struct wl_event_loop *loop;
+ int output_width, output_height;
+ uint32_t mask = XCB_CW_EVENT_MASK | XCB_CW_CURSOR;
+ xcb_atom_t atom_list[1];
+ uint32_t values[2] = {
+ XCB_EVENT_MASK_EXPOSURE |
+ XCB_EVENT_MASK_STRUCTURE_NOTIFY,
+ 0
+ };
+
+ output_width = width * scale;
+ output_height = height * scale;
+
+ if (configured_name)
+ sprintf(title, "%s - %s", name, configured_name);
+ else
+ strcpy(title, name);
+
+ if (!no_input)
+ values[0] |=
+ XCB_EVENT_MASK_KEY_PRESS |
+ XCB_EVENT_MASK_KEY_RELEASE |
+ XCB_EVENT_MASK_BUTTON_PRESS |
+ XCB_EVENT_MASK_BUTTON_RELEASE |
+ XCB_EVENT_MASK_POINTER_MOTION |
+ XCB_EVENT_MASK_ENTER_WINDOW |
+ XCB_EVENT_MASK_LEAVE_WINDOW |
+ XCB_EVENT_MASK_KEYMAP_STATE |
+ XCB_EVENT_MASK_FOCUS_CHANGE;
+
+ output = zalloc(sizeof *output);
+ if (output == NULL)
+ return NULL;
+
+ output->mode.flags =
+ WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
+
+ output->mode.width = output_width;
+ output->mode.height = output_height;
+ output->mode.refresh = 60000;
+ output->scale = scale;
+ wl_list_init(&output->base.mode_list);
+ wl_list_insert(&output->base.mode_list, &output->mode.link);
+
+ values[1] = c->null_cursor;
+ output->window = xcb_generate_id(c->conn);
+ iter = xcb_setup_roots_iterator(xcb_get_setup(c->conn));
+ xcb_create_window(c->conn,
+ XCB_COPY_FROM_PARENT,
+ output->window,
+ iter.data->root,
+ 0, 0,
+ output_width, output_height,
+ 0,
+ XCB_WINDOW_CLASS_INPUT_OUTPUT,
+ iter.data->root_visual,
+ mask, values);
+
+ if (fullscreen) {
+ atom_list[0] = c->atom.net_wm_state_fullscreen;
+ xcb_change_property(c->conn, XCB_PROP_MODE_REPLACE,
+ output->window,
+ c->atom.net_wm_state,
+ XCB_ATOM_ATOM, 32,
+ ARRAY_LENGTH(atom_list), atom_list);
+ } else {
+ /* Don't resize me. */
+ memset(&normal_hints, 0, sizeof normal_hints);
+ normal_hints.flags =
+ WM_NORMAL_HINTS_MAX_SIZE | WM_NORMAL_HINTS_MIN_SIZE;
+ normal_hints.min_width = output_width;
+ normal_hints.min_height = output_height;
+ normal_hints.max_width = output_width;
+ normal_hints.max_height = output_height;
+ xcb_change_property(c->conn, XCB_PROP_MODE_REPLACE, output->window,
+ c->atom.wm_normal_hints,
+ c->atom.wm_size_hints, 32,
+ sizeof normal_hints / 4,
+ (uint8_t *) &normal_hints);
+ }
+
+ /* Set window name. Don't bother with non-EWMH WMs. */
+ xcb_change_property(c->conn, XCB_PROP_MODE_REPLACE, output->window,
+ c->atom.net_wm_name, c->atom.utf8_string, 8,
+ strlen(title), title);
+ xcb_change_property(c->conn, XCB_PROP_MODE_REPLACE, output->window,
+ c->atom.wm_class, c->atom.string, 8,
+ sizeof class, class);
+
+ x11_output_set_icon(c, output, DATADIR "/weston/wayland.png");
+
+ x11_output_set_wm_protocols(c, output);
+
+ xcb_map_window(c->conn, output->window);
+
+ if (fullscreen)
+ x11_output_wait_for_map(c, output);
+
+ output->base.start_repaint_loop = x11_output_start_repaint_loop;
+ if (c->use_pixman)
+ output->base.repaint = x11_output_repaint_shm;
+ else
+ output->base.repaint = x11_output_repaint_gl;
+ output->base.destroy = x11_output_destroy;
+ output->base.assign_planes = NULL;
+ output->base.set_backlight = NULL;
+ output->base.set_dpms = NULL;
+ output->base.switch_mode = NULL;
+ output->base.current_mode = &output->mode;
+ output->base.make = "xwayland";
+ output->base.model = "none";
+ weston_output_init(&output->base, &c->base,
+ x, y, width, height, transform, scale);
+
+ if (c->use_pixman) {
+ if (x11_output_init_shm(c, output,
+ output->mode.width,
+ output->mode.height) < 0)
+ return NULL;
+ if (pixman_renderer_output_create(&output->base) < 0) {
+ x11_output_deinit_shm(c, output);
+ return NULL;
+ }
+ } else {
+ if (gl_renderer_output_create(&output->base, (EGLNativeWindowType)output->window) < 0)
+ return NULL;
+ }
+
+ loop = wl_display_get_event_loop(c->base.wl_display);
+ output->finish_frame_timer =
+ wl_event_loop_add_timer(loop, finish_frame_handler, output);
+
+ wl_list_insert(c->base.output_list.prev, &output->base.link);
+
+ weston_log("x11 output %dx%d, window id %d\n",
+ width, height, output->window);
+
+ return output;
+}
+
+static struct x11_output *
+x11_compositor_find_output(struct x11_compositor *c, xcb_window_t window)
+{
+ struct x11_output *output;
+
+ wl_list_for_each(output, &c->base.output_list, base.link) {
+ if (output->window == window)
+ return output;
+ }
+
+ assert(0);
+}
+
+#ifdef HAVE_XCB_XKB
+static void
+update_xkb_state(struct x11_compositor *c, xcb_xkb_state_notify_event_t *state)
+{
+ xkb_state_update_mask(c->core_seat.xkb_state.state,
+ get_xkb_mod_mask(c, state->baseMods),
+ get_xkb_mod_mask(c, state->latchedMods),
+ get_xkb_mod_mask(c, state->lockedMods),
+ 0,
+ 0,
+ state->group);
+
+ notify_modifiers(&c->core_seat,
+ wl_display_next_serial(c->base.wl_display));
+}
+#endif
+
+/**
+ * This is monumentally unpleasant. If we don't have XCB-XKB bindings,
+ * the best we can do (given that XCB also lacks XI2 support), is to take
+ * the state from the core key events. Unfortunately that only gives us
+ * the effective (i.e. union of depressed/latched/locked) state, and we
+ * need the granularity.
+ *
+ * So we still update the state with every key event we see, but also use
+ * the state field from X11 events as a mask so we don't get any stuck
+ * modifiers.
+ */
+static void
+update_xkb_state_from_core(struct x11_compositor *c, uint16_t x11_mask)
+{
+ uint32_t mask = get_xkb_mod_mask(c, x11_mask);
+ struct weston_keyboard *keyboard = c->core_seat.keyboard;
+
+ xkb_state_update_mask(c->core_seat.xkb_state.state,
+ keyboard->modifiers.mods_depressed & mask,
+ keyboard->modifiers.mods_latched & mask,
+ keyboard->modifiers.mods_locked & mask,
+ 0,
+ 0,
+ (x11_mask >> 13) & 3);
+ notify_modifiers(&c->core_seat,
+ wl_display_next_serial(c->base.wl_display));
+}
+
+static void
+x11_compositor_deliver_button_event(struct x11_compositor *c,
+ xcb_generic_event_t *event, int state)
+{
+ xcb_button_press_event_t *button_event =
+ (xcb_button_press_event_t *) event;
+ uint32_t button;
+ struct x11_output *output;
+
+ output = x11_compositor_find_output(c, button_event->event);
+
+ if (state)
+ xcb_grab_pointer(c->conn, 0, output->window,
+ XCB_EVENT_MASK_BUTTON_PRESS |
+ XCB_EVENT_MASK_BUTTON_RELEASE |
+ XCB_EVENT_MASK_POINTER_MOTION |
+ XCB_EVENT_MASK_ENTER_WINDOW |
+ XCB_EVENT_MASK_LEAVE_WINDOW,
+ XCB_GRAB_MODE_ASYNC,
+ XCB_GRAB_MODE_ASYNC,
+ output->window, XCB_CURSOR_NONE,
+ button_event->time);
+ else
+ xcb_ungrab_pointer(c->conn, button_event->time);
+
+ if (!c->has_xkb)
+ update_xkb_state_from_core(c, button_event->state);
+
+ switch (button_event->detail) {
+ default:
+ button = button_event->detail + BTN_LEFT - 1;
+ break;
+ case 2:
+ button = BTN_MIDDLE;
+ break;
+ case 3:
+ button = BTN_RIGHT;
+ break;
+ case 4:
+ /* Axis are measured in pixels, but the xcb events are discrete
+ * steps. Therefore move the axis by some pixels every step. */
+ if (state)
+ notify_axis(&c->core_seat,
+ weston_compositor_get_time(),
+ WL_POINTER_AXIS_VERTICAL_SCROLL,
+ -DEFAULT_AXIS_STEP_DISTANCE);
+ return;
+ case 5:
+ if (state)
+ notify_axis(&c->core_seat,
+ weston_compositor_get_time(),
+ WL_POINTER_AXIS_VERTICAL_SCROLL,
+ DEFAULT_AXIS_STEP_DISTANCE);
+ return;
+ case 6:
+ if (state)
+ notify_axis(&c->core_seat,
+ weston_compositor_get_time(),
+ WL_POINTER_AXIS_HORIZONTAL_SCROLL,
+ -DEFAULT_AXIS_STEP_DISTANCE);
+ return;
+ case 7:
+ if (state)
+ notify_axis(&c->core_seat,
+ weston_compositor_get_time(),
+ WL_POINTER_AXIS_HORIZONTAL_SCROLL,
+ DEFAULT_AXIS_STEP_DISTANCE);
+ return;
+ }
+
+ notify_button(&c->core_seat,
+ weston_compositor_get_time(), button,
+ state ? WL_POINTER_BUTTON_STATE_PRESSED :
+ WL_POINTER_BUTTON_STATE_RELEASED);
+}
+
+static void
+x11_compositor_deliver_motion_event(struct x11_compositor *c,
+ xcb_generic_event_t *event)
+{
+ struct x11_output *output;
+ wl_fixed_t x, y;
+ xcb_motion_notify_event_t *motion_notify =
+ (xcb_motion_notify_event_t *) event;
+
+ if (!c->has_xkb)
+ update_xkb_state_from_core(c, motion_notify->state);
+ output = x11_compositor_find_output(c, motion_notify->event);
+ weston_output_transform_coordinate(&output->base,
+ motion_notify->event_x,
+ motion_notify->event_y, &x, &y);
+
+ notify_motion(&c->core_seat, weston_compositor_get_time(),
+ x - c->prev_x, y - c->prev_y);
+
+ c->prev_x = x;
+ c->prev_y = y;
+}
+
+static void
+x11_compositor_deliver_enter_event(struct x11_compositor *c,
+ xcb_generic_event_t *event)
+{
+ struct x11_output *output;
+ wl_fixed_t x, y;
+
+ xcb_enter_notify_event_t *enter_notify =
+ (xcb_enter_notify_event_t *) event;
+ if (enter_notify->state >= Button1Mask)
+ return;
+ if (!c->has_xkb)
+ update_xkb_state_from_core(c, enter_notify->state);
+ output = x11_compositor_find_output(c, enter_notify->event);
+ weston_output_transform_coordinate(&output->base,
+ enter_notify->event_x,
+ enter_notify->event_y, &x, &y);
+
+ notify_pointer_focus(&c->core_seat, &output->base, x, y);
+
+ c->prev_x = x;
+ c->prev_y = y;
+}
+
+static int
+x11_compositor_next_event(struct x11_compositor *c,
+ xcb_generic_event_t **event, uint32_t mask)
+{
+ if (mask & WL_EVENT_READABLE) {
+ *event = xcb_poll_for_event(c->conn);
+ } else {
+#ifdef HAVE_XCB_POLL_FOR_QUEUED_EVENT
+ *event = xcb_poll_for_queued_event(c->conn);
+#else
+ *event = xcb_poll_for_event(c->conn);
+#endif
+ }
+
+ return *event != NULL;
+}
+
+static int
+x11_compositor_handle_event(int fd, uint32_t mask, void *data)
+{
+ struct x11_compositor *c = data;
+ struct x11_output *output;
+ xcb_generic_event_t *event, *prev;
+ xcb_client_message_event_t *client_message;
+ xcb_enter_notify_event_t *enter_notify;
+ xcb_key_press_event_t *key_press, *key_release;
+ xcb_keymap_notify_event_t *keymap_notify;
+ xcb_focus_in_event_t *focus_in;
+ xcb_expose_event_t *expose;
+ xcb_atom_t atom;
+ uint32_t *k;
+ uint32_t i, set;
+ uint8_t response_type;
+ int count;
+
+ prev = NULL;
+ count = 0;
+ while (x11_compositor_next_event(c, &event, mask)) {
+ response_type = event->response_type & ~0x80;
+
+ switch (prev ? prev->response_type & ~0x80 : 0x80) {
+ case XCB_KEY_RELEASE:
+ /* Suppress key repeat events; this is only used if we
+ * don't have XCB XKB support. */
+ key_release = (xcb_key_press_event_t *) prev;
+ key_press = (xcb_key_press_event_t *) event;
+ if (response_type == XCB_KEY_PRESS &&
+ key_release->time == key_press->time &&
+ key_release->detail == key_press->detail) {
+ /* Don't deliver the held key release
+ * event or the new key press event. */
+ free(event);
+ free(prev);
+ prev = NULL;
+ continue;
+ } else {
+ /* Deliver the held key release now
+ * and fall through and handle the new
+ * event below. */
+ update_xkb_state_from_core(c, key_release->state);
+ notify_key(&c->core_seat,
+ weston_compositor_get_time(),
+ key_release->detail - 8,
+ WL_KEYBOARD_KEY_STATE_RELEASED,
+ STATE_UPDATE_AUTOMATIC);
+ free(prev);
+ prev = NULL;
+ break;
+ }
+
+ case XCB_FOCUS_IN:
+ assert(response_type == XCB_KEYMAP_NOTIFY);
+ keymap_notify = (xcb_keymap_notify_event_t *) event;
+ c->keys.size = 0;
+ for (i = 0; i < ARRAY_LENGTH(keymap_notify->keys) * 8; i++) {
+ set = keymap_notify->keys[i >> 3] &
+ (1 << (i & 7));
+ if (set) {
+ k = wl_array_add(&c->keys, sizeof *k);
+ *k = i;
+ }
+ }
+
+ /* Unfortunately the state only comes with the enter
+ * event, rather than with the focus event. I'm not
+ * sure of the exact semantics around it and whether
+ * we can ensure that we get both? */
+ notify_keyboard_focus_in(&c->core_seat, &c->keys,
+ STATE_UPDATE_AUTOMATIC);
+
+ free(prev);
+ prev = NULL;
+ break;
+
+ default:
+ /* No previous event held */
+ break;
+ }
+
+ switch (response_type) {
+ case XCB_KEY_PRESS:
+ key_press = (xcb_key_press_event_t *) event;
+ if (!c->has_xkb)
+ update_xkb_state_from_core(c, key_press->state);
+ notify_key(&c->core_seat,
+ weston_compositor_get_time(),
+ key_press->detail - 8,
+ WL_KEYBOARD_KEY_STATE_PRESSED,
+ c->has_xkb ? STATE_UPDATE_NONE :
+ STATE_UPDATE_AUTOMATIC);
+ break;
+ case XCB_KEY_RELEASE:
+ /* If we don't have XKB, we need to use the lame
+ * autorepeat detection above. */
+ if (!c->has_xkb) {
+ prev = event;
+ break;
+ }
+ key_release = (xcb_key_press_event_t *) event;
+ notify_key(&c->core_seat,
+ weston_compositor_get_time(),
+ key_release->detail - 8,
+ WL_KEYBOARD_KEY_STATE_RELEASED,
+ STATE_UPDATE_NONE);
+ break;
+ case XCB_BUTTON_PRESS:
+ x11_compositor_deliver_button_event(c, event, 1);
+ break;
+ case XCB_BUTTON_RELEASE:
+ x11_compositor_deliver_button_event(c, event, 0);
+ break;
+ case XCB_MOTION_NOTIFY:
+ x11_compositor_deliver_motion_event(c, event);
+ break;
+
+ case XCB_EXPOSE:
+ expose = (xcb_expose_event_t *) event;
+ output = x11_compositor_find_output(c, expose->window);
+ weston_output_schedule_repaint(&output->base);
+ break;
+
+ case XCB_ENTER_NOTIFY:
+ x11_compositor_deliver_enter_event(c, event);
+ break;
+
+ case XCB_LEAVE_NOTIFY:
+ enter_notify = (xcb_enter_notify_event_t *) event;
+ if (enter_notify->state >= Button1Mask)
+ break;
+ if (!c->has_xkb)
+ update_xkb_state_from_core(c, enter_notify->state);
+ notify_pointer_focus(&c->core_seat, NULL, 0, 0);
+ break;
+
+ case XCB_CLIENT_MESSAGE:
+ client_message = (xcb_client_message_event_t *) event;
+ atom = client_message->data.data32[0];
+ if (atom == c->atom.wm_delete_window)
+ wl_display_terminate(c->base.wl_display);
+ break;
+
+ case XCB_FOCUS_IN:
+ focus_in = (xcb_focus_in_event_t *) event;
+ if (focus_in->mode == XCB_NOTIFY_MODE_WHILE_GRABBED)
+ break;
+
+ prev = event;
+ break;
+
+ case XCB_FOCUS_OUT:
+ focus_in = (xcb_focus_in_event_t *) event;
+ if (focus_in->mode == XCB_NOTIFY_MODE_WHILE_GRABBED ||
+ focus_in->mode == XCB_NOTIFY_MODE_UNGRAB)
+ break;
+ notify_keyboard_focus_out(&c->core_seat);
+ break;
+
+ default:
+ break;
+ }
+
+#ifdef HAVE_XCB_XKB
+ if (c->has_xkb &&
+ response_type == c->xkb_event_base) {
+ xcb_xkb_state_notify_event_t *state =
+ (xcb_xkb_state_notify_event_t *) event;
+ if (state->xkbType == XCB_XKB_STATE_NOTIFY)
+ update_xkb_state(c, state);
+ }
+#endif
+
+ count++;
+ if (prev != event)
+ free (event);
+ }
+
+ switch (prev ? prev->response_type & ~0x80 : 0x80) {
+ case XCB_KEY_RELEASE:
+ key_release = (xcb_key_press_event_t *) prev;
+ update_xkb_state_from_core(c, key_release->state);
+ notify_key(&c->core_seat,
+ weston_compositor_get_time(),
+ key_release->detail - 8,
+ WL_KEYBOARD_KEY_STATE_RELEASED,
+ STATE_UPDATE_AUTOMATIC);
+ free(prev);
+ prev = NULL;
+ break;
+ default:
+ break;
+ }
+
+ return count;
+}
+
+#define F(field) offsetof(struct x11_compositor, field)
+
+static void
+x11_compositor_get_resources(struct x11_compositor *c)
+{
+ static const struct { const char *name; int offset; } atoms[] = {
+ { "WM_PROTOCOLS", F(atom.wm_protocols) },
+ { "WM_NORMAL_HINTS", F(atom.wm_normal_hints) },
+ { "WM_SIZE_HINTS", F(atom.wm_size_hints) },
+ { "WM_DELETE_WINDOW", F(atom.wm_delete_window) },
+ { "WM_CLASS", F(atom.wm_class) },
+ { "_NET_WM_NAME", F(atom.net_wm_name) },
+ { "_NET_WM_ICON", F(atom.net_wm_icon) },
+ { "_NET_WM_STATE", F(atom.net_wm_state) },
+ { "_NET_WM_STATE_FULLSCREEN", F(atom.net_wm_state_fullscreen) },
+ { "_NET_SUPPORTING_WM_CHECK",
+ F(atom.net_supporting_wm_check) },
+ { "_NET_SUPPORTED", F(atom.net_supported) },
+ { "STRING", F(atom.string) },
+ { "UTF8_STRING", F(atom.utf8_string) },
+ { "CARDINAL", F(atom.cardinal) },
+ { "_XKB_RULES_NAMES", F(atom.xkb_names) },
+ };
+
+ xcb_intern_atom_cookie_t cookies[ARRAY_LENGTH(atoms)];
+ xcb_intern_atom_reply_t *reply;
+ xcb_pixmap_t pixmap;
+ xcb_gc_t gc;
+ unsigned int i;
+ uint8_t data[] = { 0, 0, 0, 0 };
+
+ for (i = 0; i < ARRAY_LENGTH(atoms); i++)
+ cookies[i] = xcb_intern_atom (c->conn, 0,
+ strlen(atoms[i].name),
+ atoms[i].name);
+
+ for (i = 0; i < ARRAY_LENGTH(atoms); i++) {
+ reply = xcb_intern_atom_reply (c->conn, cookies[i], NULL);
+ *(xcb_atom_t *) ((char *) c + atoms[i].offset) = reply->atom;
+ free(reply);
+ }
+
+ pixmap = xcb_generate_id(c->conn);
+ gc = xcb_generate_id(c->conn);
+ xcb_create_pixmap(c->conn, 1, pixmap, c->screen->root, 1, 1);
+ xcb_create_gc(c->conn, gc, pixmap, 0, NULL);
+ xcb_put_image(c->conn, XCB_IMAGE_FORMAT_XY_PIXMAP,
+ pixmap, gc, 1, 1, 0, 0, 0, 32, sizeof data, data);
+ c->null_cursor = xcb_generate_id(c->conn);
+ xcb_create_cursor (c->conn, c->null_cursor,
+ pixmap, pixmap, 0, 0, 0, 0, 0, 0, 1, 1);
+ xcb_free_gc(c->conn, gc);
+ xcb_free_pixmap(c->conn, pixmap);
+}
+
+static void
+x11_compositor_get_wm_info(struct x11_compositor *c)
+{
+ xcb_get_property_cookie_t cookie;
+ xcb_get_property_reply_t *reply;
+ xcb_atom_t *atom;
+ unsigned int i;
+
+ cookie = xcb_get_property(c->conn, 0, c->screen->root,
+ c->atom.net_supported,
+ XCB_ATOM_ATOM, 0, 1024);
+ reply = xcb_get_property_reply(c->conn, cookie, NULL);
+ if (reply == NULL)
+ return;
+
+ atom = (xcb_atom_t *) xcb_get_property_value(reply);
+
+ for (i = 0; i < reply->value_len; i++) {
+ if (atom[i] == c->atom.net_wm_state_fullscreen)
+ c->has_net_wm_state_fullscreen = 1;
+ }
+}
+
+static void
+x11_restore(struct weston_compositor *ec)
+{
+}
+
+static void
+x11_destroy(struct weston_compositor *ec)
+{
+ struct x11_compositor *compositor = (struct x11_compositor *)ec;
+
+ wl_event_source_remove(compositor->xcb_source);
+ x11_input_destroy(compositor);
+
+ weston_compositor_shutdown(ec); /* destroys outputs, too */
+
+ ec->renderer->destroy(ec);
+
+ XCloseDisplay(compositor->dpy);
+ free(ec);
+}
+
+static uint32_t
+parse_transform(const char *transform, const char *output_name)
+{
+ static const struct { const char *name; uint32_t token; } names[] = {
+ { "normal", WL_OUTPUT_TRANSFORM_NORMAL },
+ { "90", WL_OUTPUT_TRANSFORM_90 },
+ { "180", WL_OUTPUT_TRANSFORM_180 },
+ { "270", WL_OUTPUT_TRANSFORM_270 },
+ { "flipped", WL_OUTPUT_TRANSFORM_FLIPPED },
+ { "flipped-90", WL_OUTPUT_TRANSFORM_FLIPPED_90 },
+ { "flipped-180", WL_OUTPUT_TRANSFORM_FLIPPED_180 },
+ { "flipped-270", WL_OUTPUT_TRANSFORM_FLIPPED_270 },
+ };
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_LENGTH(names); i++)
+ if (strcmp(names[i].name, transform) == 0)
+ return names[i].token;
+
+ weston_log("Invalid transform \"%s\" for output %s\n",
+ transform, output_name);
+
+ return WL_OUTPUT_TRANSFORM_NORMAL;
+}
+
+static struct weston_compositor *
+x11_compositor_create(struct wl_display *display,
+ int fullscreen,
+ int no_input,
+ int use_pixman,
+ int *argc, char *argv[],
+ struct weston_config *config)
+{
+ struct x11_compositor *c;
+ struct x11_output *output;
+ struct weston_config_section *section;
+ xcb_screen_iterator_t s;
+ int i, x = 0, output_count = 0;
+ int width, height, count, scale;
+ const char *section_name;
+ char *name, *t, *mode;
+ uint32_t transform;
+
+ weston_log("initializing x11 backend\n");
+
+ c = zalloc(sizeof *c);
+ if (c == NULL)
+ return NULL;
+
+ if (weston_compositor_init(&c->base, display, argc, argv, config) < 0)
+ goto err_free;
+
+ c->dpy = XOpenDisplay(NULL);
+ if (c->dpy == NULL)
+ goto err_free;
+
+ c->conn = XGetXCBConnection(c->dpy);
+ XSetEventQueueOwner(c->dpy, XCBOwnsEventQueue);
+
+ if (xcb_connection_has_error(c->conn))
+ goto err_xdisplay;
+
+ s = xcb_setup_roots_iterator(xcb_get_setup(c->conn));
+ c->screen = s.data;
+ wl_array_init(&c->keys);
+
+ x11_compositor_get_resources(c);
+ x11_compositor_get_wm_info(c);
+
+ if (!c->has_net_wm_state_fullscreen && fullscreen) {
+ weston_log("Can not fullscreen without window manager support"
+ "(need _NET_WM_STATE_FULLSCREEN)\n");
+ fullscreen = 0;
+ }
+
+ c->base.wl_display = display;
+ c->use_pixman = use_pixman;
+ if (c->use_pixman) {
+ if (pixman_renderer_init(&c->base) < 0)
+ goto err_xdisplay;
+ }
+ else {
+ if (gl_renderer_create(&c->base, (EGLNativeDisplayType)c->dpy, gl_renderer_opaque_attribs,
+ NULL) < 0)
+ goto err_xdisplay;
+ }
+ weston_log("Using %s renderer\n", use_pixman ? "pixman" : "gl");
+
+ c->base.destroy = x11_destroy;
+ c->base.restore = x11_restore;
+
+ if (x11_input_create(c, no_input) < 0)
+ goto err_renderer;
+
+ width = option_width ? option_width : 1024;
+ height = option_height ? option_height : 640;
+ count = option_count ? option_count : 1;
+
+ section = NULL;
+ while (weston_config_next_section(c->base.config,
+ &section, &section_name)) {
+ if (strcmp(section_name, "output") != 0)
+ continue;
+ weston_config_section_get_string(section, "name", &name, NULL);
+ if (name == NULL || name[0] != 'X') {
+ free(name);
+ continue;
+ }
+
+ weston_config_section_get_string(section,
+ "mode", &mode, "1024x600");
+ if (sscanf(mode, "%dx%d", &width, &height) != 2) {
+ weston_log("Invalid mode \"%s\" for output %s\n",
+ mode, name);
+ width = 1024;
+ height = 600;
+ }
+ free(mode);
+
+ if (option_width)
+ width = option_width;
+ if (option_height)
+ height = option_height;
+
+ weston_config_section_get_int(section, "scale", &scale, 1);
+ weston_config_section_get_string(section,
+ "transform", &t, "normal");
+ transform = parse_transform(t, name);
+ free(t);
+
+ output = x11_compositor_create_output(c, x, 0,
+ width, height,
+ fullscreen, no_input,
+ name, transform, scale);
+ free(name);
+ if (output == NULL)
+ goto err_x11_input;
+
+ x = pixman_region32_extents(&output->base.region)->x2;
+
+ output_count++;
+ if (option_count && output_count >= option_count)
+ break;
+ }
+
+ for (i = output_count; i < count; i++) {
+ output = x11_compositor_create_output(c, x, 0, width, height,
+ fullscreen, no_input, NULL,
+ WL_OUTPUT_TRANSFORM_NORMAL, 1);
+ if (output == NULL)
+ goto err_x11_input;
+ x = pixman_region32_extents(&output->base.region)->x2;
+ }
+
+ c->xcb_source =
+ wl_event_loop_add_fd(c->base.input_loop,
+ xcb_get_file_descriptor(c->conn),
+ WL_EVENT_READABLE,
+ x11_compositor_handle_event, c);
+ wl_event_source_check(c->xcb_source);
+
+ return &c->base;
+
+err_x11_input:
+ x11_input_destroy(c);
+err_renderer:
+ c->base.renderer->destroy(&c->base);
+err_xdisplay:
+ XCloseDisplay(c->dpy);
+err_free:
+ free(c);
+ return NULL;
+}
+
+WL_EXPORT struct weston_compositor *
+backend_init(struct wl_display *display, int *argc, char *argv[],
+ struct weston_config *config)
+{
+ int fullscreen = 0;
+ int no_input = 0;
+ int use_pixman = 0;
+
+ const struct weston_option x11_options[] = {
+ { WESTON_OPTION_INTEGER, "width", 0, &option_width },
+ { WESTON_OPTION_INTEGER, "height", 0, &option_height },
+ { WESTON_OPTION_BOOLEAN, "fullscreen", 'f', &fullscreen },
+ { WESTON_OPTION_INTEGER, "output-count", 0, &option_count },
+ { WESTON_OPTION_BOOLEAN, "no-input", 0, &no_input },
+ { WESTON_OPTION_BOOLEAN, "use-pixman", 0, &use_pixman },
+ };
+
+ parse_options(x11_options, ARRAY_LENGTH(x11_options), argc, argv);
+
+ return x11_compositor_create(display,
+ fullscreen,
+ no_input,
+ use_pixman,
+ argc, argv, config);
+}
diff --git a/src/compositor.c b/src/compositor.c
new file mode 100644
index 00000000..36b54b5b
--- /dev/null
+++ b/src/compositor.c
@@ -0,0 +1,3595 @@
+/*
+ * Copyright © 2010-2011 Intel Corporation
+ * Copyright © 2008-2011 Kristian Høgsberg
+ * Copyright © 2012 Collabora, Ltd.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that 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.
+ */
+
+#include "config.h"
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/utsname.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <math.h>
+#include <linux/input.h>
+#include <dlfcn.h>
+#include <signal.h>
+#include <setjmp.h>
+#include <sys/time.h>
+#include <time.h>
+
+#ifdef HAVE_LIBUNWIND
+#define UNW_LOCAL_ONLY
+#include <libunwind.h>
+#endif
+
+#include "compositor.h"
+#include "subsurface-server-protocol.h"
+#include "../shared/os-compatibility.h"
+#include "git-version.h"
+#include "version.h"
+
+static struct wl_list child_process_list;
+static struct weston_compositor *segv_compositor;
+
+static int
+sigchld_handler(int signal_number, void *data)
+{
+ struct weston_process *p;
+ int status;
+ pid_t pid;
+
+ pid = waitpid(-1, &status, WNOHANG);
+ if (!pid)
+ return 1;
+
+ wl_list_for_each(p, &child_process_list, link) {
+ if (p->pid == pid)
+ break;
+ }
+
+ if (&p->link == &child_process_list) {
+ weston_log("unknown child process exited\n");
+ return 1;
+ }
+
+ wl_list_remove(&p->link);
+ p->cleanup(p, status);
+
+ return 1;
+}
+
+static void
+weston_output_transform_scale_init(struct weston_output *output,
+ uint32_t transform, uint32_t scale);
+
+static void
+weston_compositor_build_surface_list(struct weston_compositor *compositor);
+
+WL_EXPORT int
+weston_output_switch_mode(struct weston_output *output, struct weston_mode *mode,
+ int32_t scale, enum weston_mode_switch_op op)
+{
+ struct weston_seat *seat;
+ struct wl_resource *resource;
+ pixman_region32_t old_output_region;
+ int ret, notify_mode_changed, notify_scale_changed;
+ int temporary_mode, temporary_scale;
+
+ if (!output->switch_mode)
+ return -1;
+
+ temporary_mode = (output->original_mode != 0);
+ temporary_scale = (output->current_scale != output->original_scale);
+ ret = 0;
+
+ notify_mode_changed = 0;
+ notify_scale_changed = 0;
+ switch(op) {
+ case WESTON_MODE_SWITCH_SET_NATIVE:
+ output->native_mode = mode;
+ if (!temporary_mode) {
+ notify_mode_changed = 1;
+ ret = output->switch_mode(output, mode);
+ if (ret < 0)
+ return ret;
+ }
+
+ output->native_scale = scale;
+ if(!temporary_scale)
+ notify_scale_changed = 1;
+ break;
+ case WESTON_MODE_SWITCH_SET_TEMPORARY:
+ if (!temporary_mode)
+ output->original_mode = output->native_mode;
+ if (!temporary_scale)
+ output->original_scale = output->native_scale;
+
+ ret = output->switch_mode(output, mode);
+ if (ret < 0)
+ return ret;
+
+ output->current_mode = mode;
+ output->current_scale = scale;
+ break;
+ case WESTON_MODE_SWITCH_RESTORE_NATIVE:
+ if (!temporary_mode) {
+ weston_log("already in the native mode\n");
+ return -1;
+ }
+
+ notify_mode_changed = (output->original_mode != output->native_mode);
+
+ ret = output->switch_mode(output, mode);
+ if (ret < 0)
+ return ret;
+
+ if (output->original_scale != output->native_scale) {
+ notify_scale_changed = 1;
+ scale = output->native_scale;
+ output->original_scale = scale;
+ }
+ output->original_mode = 0;
+
+ output->current_scale = output->native_scale;
+ break;
+ default:
+ weston_log("unknown weston_switch_mode_op %d\n", op);
+ break;
+ }
+
+ pixman_region32_init(&old_output_region);
+ pixman_region32_copy(&old_output_region, &output->region);
+
+ /* Update output region and transformation matrix */
+ weston_output_transform_scale_init(output, output->transform, output->current_scale);
+
+ pixman_region32_init(&output->previous_damage);
+ pixman_region32_init_rect(&output->region, output->x, output->y,
+ output->width, output->height);
+
+ weston_output_update_matrix(output);
+
+ /* If a pointer falls outside the outputs new geometry, move it to its
+ * lower-right corner */
+ wl_list_for_each(seat, &output->compositor->seat_list, link) {
+ struct weston_pointer *pointer = seat->pointer;
+ int32_t x, y;
+
+ if (!pointer)
+ continue;
+
+ x = wl_fixed_to_int(pointer->x);
+ y = wl_fixed_to_int(pointer->y);
+
+ if (!pixman_region32_contains_point(&old_output_region,
+ x, y, NULL) ||
+ pixman_region32_contains_point(&output->region,
+ x, y, NULL))
+ continue;
+
+ if (x >= output->x + output->width)
+ x = output->x + output->width - 1;
+ if (y >= output->y + output->height)
+ y = output->y + output->height - 1;
+
+ pointer->x = wl_fixed_from_int(x);
+ pointer->y = wl_fixed_from_int(y);
+ }
+
+ pixman_region32_fini(&old_output_region);
+
+ /* notify clients of the changes */
+ if (notify_mode_changed || notify_scale_changed) {
+ wl_resource_for_each(resource, &output->resource_list) {
+ if(notify_mode_changed) {
+ wl_output_send_mode(resource,
+ mode->flags | WL_OUTPUT_MODE_CURRENT,
+ mode->width,
+ mode->height,
+ mode->refresh);
+ }
+
+ if (notify_scale_changed)
+ wl_output_send_scale(resource, scale);
+
+ if (wl_resource_get_version(resource) >= 2)
+ wl_output_send_done(resource);
+ }
+ }
+
+ return ret;
+}
+
+WL_EXPORT void
+weston_watch_process(struct weston_process *process)
+{
+ wl_list_insert(&child_process_list, &process->link);
+}
+
+static void
+child_client_exec(int sockfd, const char *path)
+{
+ int clientfd;
+ char s[32];
+ sigset_t allsigs;
+
+ /* do not give our signal mask to the new process */
+ sigfillset(&allsigs);
+ sigprocmask(SIG_UNBLOCK, &allsigs, NULL);
+
+ /* Launch clients as the user. Do not lauch clients with wrong euid.*/
+ if (seteuid(getuid()) == -1) {
+ weston_log("compositor: failed seteuid\n");
+ return;
+ }
+
+ /* SOCK_CLOEXEC closes both ends, so we dup the fd to get a
+ * non-CLOEXEC fd to pass through exec. */
+ clientfd = dup(sockfd);
+ if (clientfd == -1) {
+ weston_log("compositor: dup failed: %m\n");
+ return;
+ }
+
+ snprintf(s, sizeof s, "%d", clientfd);
+ setenv("WAYLAND_SOCKET", s, 1);
+
+ if (execl(path, path, NULL) < 0)
+ weston_log("compositor: executing '%s' failed: %m\n",
+ path);
+}
+
+WL_EXPORT struct wl_client *
+weston_client_launch(struct weston_compositor *compositor,
+ struct weston_process *proc,
+ const char *path,
+ weston_process_cleanup_func_t cleanup)
+{
+ int sv[2];
+ pid_t pid;
+ struct wl_client *client;
+
+ weston_log("launching '%s'\n", path);
+
+ if (os_socketpair_cloexec(AF_UNIX, SOCK_STREAM, 0, sv) < 0) {
+ weston_log("weston_client_launch: "
+ "socketpair failed while launching '%s': %m\n",
+ path);
+ return NULL;
+ }
+
+ pid = fork();
+ if (pid == -1) {
+ close(sv[0]);
+ close(sv[1]);
+ weston_log("weston_client_launch: "
+ "fork failed while launching '%s': %m\n", path);
+ return NULL;
+ }
+
+ if (pid == 0) {
+ child_client_exec(sv[1], path);
+ _exit(-1);
+ }
+
+ close(sv[1]);
+
+ client = wl_client_create(compositor->wl_display, sv[0]);
+ if (!client) {
+ close(sv[0]);
+ weston_log("weston_client_launch: "
+ "wl_client_create failed while launching '%s'.\n",
+ path);
+ return NULL;
+ }
+
+ proc->pid = pid;
+ proc->cleanup = cleanup;
+ weston_watch_process(proc);
+
+ return client;
+}
+
+static void
+surface_handle_pending_buffer_destroy(struct wl_listener *listener, void *data)
+{
+ struct weston_surface *surface =
+ container_of(listener, struct weston_surface,
+ pending.buffer_destroy_listener);
+
+ surface->pending.buffer = NULL;
+}
+
+static void
+empty_region(pixman_region32_t *region)
+{
+ pixman_region32_fini(region);
+ pixman_region32_init(region);
+}
+
+static void
+region_init_infinite(pixman_region32_t *region)
+{
+ pixman_region32_init_rect(region, INT32_MIN, INT32_MIN,
+ UINT32_MAX, UINT32_MAX);
+}
+
+static struct weston_subsurface *
+weston_surface_to_subsurface(struct weston_surface *surface);
+
+WL_EXPORT struct weston_surface *
+weston_surface_create(struct weston_compositor *compositor)
+{
+ struct weston_surface *surface;
+
+ surface = calloc(1, sizeof *surface);
+ if (surface == NULL)
+ return NULL;
+
+ wl_signal_init(&surface->destroy_signal);
+
+ surface->resource = NULL;
+
+ wl_list_init(&surface->link);
+ wl_list_init(&surface->layer_link);
+
+ surface->compositor = compositor;
+ surface->alpha = 1.0;
+ surface->ref_count = 1;
+
+ if (compositor->renderer->create_surface(surface) < 0) {
+ free(surface);
+ return NULL;
+ }
+
+ surface->buffer_transform = WL_OUTPUT_TRANSFORM_NORMAL;
+ surface->buffer_scale = 1;
+ surface->pending.buffer_transform = surface->buffer_transform;
+ surface->pending.buffer_scale = surface->buffer_scale;
+ surface->output = NULL;
+ surface->plane = NULL;
+ surface->pending.newly_attached = 0;
+
+ pixman_region32_init(&surface->damage);
+ pixman_region32_init(&surface->opaque);
+ pixman_region32_init(&surface->clip);
+ region_init_infinite(&surface->input);
+ pixman_region32_init(&surface->transform.opaque);
+ wl_list_init(&surface->frame_callback_list);
+
+ wl_list_init(&surface->geometry.transformation_list);
+ wl_list_insert(&surface->geometry.transformation_list,
+ &surface->transform.position.link);
+ weston_matrix_init(&surface->transform.position.matrix);
+ wl_list_init(&surface->geometry.child_list);
+ pixman_region32_init(&surface->transform.boundingbox);
+ surface->transform.dirty = 1;
+
+ surface->pending.buffer_destroy_listener.notify =
+ surface_handle_pending_buffer_destroy;
+ pixman_region32_init(&surface->pending.damage);
+ pixman_region32_init(&surface->pending.opaque);
+ region_init_infinite(&surface->pending.input);
+ wl_list_init(&surface->pending.frame_callback_list);
+
+ wl_list_init(&surface->subsurface_list);
+ wl_list_init(&surface->subsurface_list_pending);
+
+ return surface;
+}
+
+WL_EXPORT void
+weston_surface_set_color(struct weston_surface *surface,
+ float red, float green, float blue, float alpha)
+{
+ surface->compositor->renderer->surface_set_color(surface, red, green, blue, alpha);
+}
+
+WL_EXPORT void
+weston_surface_to_global_float(struct weston_surface *surface,
+ float sx, float sy, float *x, float *y)
+{
+ if (surface->transform.enabled) {
+ struct weston_vector v = { { sx, sy, 0.0f, 1.0f } };
+
+ weston_matrix_transform(&surface->transform.matrix, &v);
+
+ if (fabsf(v.f[3]) < 1e-6) {
+ weston_log("warning: numerical instability in "
+ "%s(), divisor = %g\n", __func__,
+ v.f[3]);
+ *x = 0;
+ *y = 0;
+ return;
+ }
+
+ *x = v.f[0] / v.f[3];
+ *y = v.f[1] / v.f[3];
+ } else {
+ *x = sx + surface->geometry.x;
+ *y = sy + surface->geometry.y;
+ }
+}
+
+WL_EXPORT void
+weston_transformed_coord(int width, int height,
+ enum wl_output_transform transform,
+ int32_t scale,
+ float sx, float sy, float *bx, float *by)
+{
+ switch (transform) {
+ case WL_OUTPUT_TRANSFORM_NORMAL:
+ default:
+ *bx = sx;
+ *by = sy;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED:
+ *bx = width - sx;
+ *by = sy;
+ break;
+ case WL_OUTPUT_TRANSFORM_90:
+ *bx = height - sy;
+ *by = sx;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+ *bx = height - sy;
+ *by = width - sx;
+ break;
+ case WL_OUTPUT_TRANSFORM_180:
+ *bx = width - sx;
+ *by = height - sy;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+ *bx = sx;
+ *by = height - sy;
+ break;
+ case WL_OUTPUT_TRANSFORM_270:
+ *bx = sy;
+ *by = width - sx;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+ *bx = sy;
+ *by = sx;
+ break;
+ }
+
+ *bx *= scale;
+ *by *= scale;
+}
+
+WL_EXPORT pixman_box32_t
+weston_transformed_rect(int width, int height,
+ enum wl_output_transform transform,
+ int32_t scale,
+ pixman_box32_t rect)
+{
+ float x1, x2, y1, y2;
+
+ pixman_box32_t ret;
+
+ weston_transformed_coord(width, height, transform, scale,
+ rect.x1, rect.y1, &x1, &y1);
+ weston_transformed_coord(width, height, transform, scale,
+ rect.x2, rect.y2, &x2, &y2);
+
+ if (x1 <= x2) {
+ ret.x1 = x1;
+ ret.x2 = x2;
+ } else {
+ ret.x1 = x2;
+ ret.x2 = x1;
+ }
+
+ if (y1 <= y2) {
+ ret.y1 = y1;
+ ret.y2 = y2;
+ } else {
+ ret.y1 = y2;
+ ret.y2 = y1;
+ }
+
+ return ret;
+}
+
+WL_EXPORT void
+weston_surface_to_buffer_float(struct weston_surface *surface,
+ float sx, float sy, float *bx, float *by)
+{
+ weston_transformed_coord(surface->geometry.width,
+ surface->geometry.height,
+ surface->buffer_transform,
+ surface->buffer_scale,
+ sx, sy, bx, by);
+}
+
+WL_EXPORT void
+weston_surface_to_buffer(struct weston_surface *surface,
+ int sx, int sy, int *bx, int *by)
+{
+ float bxf, byf;
+
+ weston_transformed_coord(surface->geometry.width,
+ surface->geometry.height,
+ surface->buffer_transform,
+ surface->buffer_scale,
+ sx, sy, &bxf, &byf);
+ *bx = floorf(bxf);
+ *by = floorf(byf);
+}
+
+WL_EXPORT pixman_box32_t
+weston_surface_to_buffer_rect(struct weston_surface *surface,
+ pixman_box32_t rect)
+{
+ return weston_transformed_rect(surface->geometry.width,
+ surface->geometry.height,
+ surface->buffer_transform,
+ surface->buffer_scale,
+ rect);
+}
+
+WL_EXPORT void
+weston_surface_move_to_plane(struct weston_surface *surface,
+ struct weston_plane *plane)
+{
+ if (surface->plane == plane)
+ return;
+
+ weston_surface_damage_below(surface);
+ surface->plane = plane;
+ weston_surface_damage(surface);
+}
+
+WL_EXPORT void
+weston_surface_damage_below(struct weston_surface *surface)
+{
+ pixman_region32_t damage;
+
+ pixman_region32_init(&damage);
+ pixman_region32_subtract(&damage, &surface->transform.boundingbox,
+ &surface->clip);
+ if (surface->plane)
+ pixman_region32_union(&surface->plane->damage,
+ &surface->plane->damage, &damage);
+ pixman_region32_fini(&damage);
+}
+
+static void
+weston_surface_update_output_mask(struct weston_surface *es, uint32_t mask)
+{
+ uint32_t different = es->output_mask ^ mask;
+ uint32_t entered = mask & different;
+ uint32_t left = es->output_mask & different;
+ struct weston_output *output;
+ struct wl_resource *resource = NULL;
+ struct wl_client *client;
+
+ es->output_mask = mask;
+ if (es->resource == NULL)
+ return;
+ if (different == 0)
+ return;
+
+ client = wl_resource_get_client(es->resource);
+
+ wl_list_for_each(output, &es->compositor->output_list, link) {
+ if (1 << output->id & different)
+ resource =
+ wl_resource_find_for_client(&output->resource_list,
+ client);
+ if (resource == NULL)
+ continue;
+ if (1 << output->id & entered)
+ wl_surface_send_enter(es->resource, resource);
+ if (1 << output->id & left)
+ wl_surface_send_leave(es->resource, resource);
+ }
+}
+
+static void
+weston_surface_assign_output(struct weston_surface *es)
+{
+ struct weston_compositor *ec = es->compositor;
+ struct weston_output *output, *new_output;
+ pixman_region32_t region;
+ uint32_t max, area, mask;
+ pixman_box32_t *e;
+
+ new_output = NULL;
+ max = 0;
+ mask = 0;
+ pixman_region32_init(&region);
+ wl_list_for_each(output, &ec->output_list, link) {
+ pixman_region32_intersect(&region, &es->transform.boundingbox,
+ &output->region);
+
+ e = pixman_region32_extents(&region);
+ area = (e->x2 - e->x1) * (e->y2 - e->y1);
+
+ if (area > 0)
+ mask |= 1 << output->id;
+
+ if (area >= max) {
+ new_output = output;
+ max = area;
+ }
+ }
+ pixman_region32_fini(&region);
+
+ es->output = new_output;
+ weston_surface_update_output_mask(es, mask);
+}
+
+static void
+surface_compute_bbox(struct weston_surface *surface, int32_t sx, int32_t sy,
+ int32_t width, int32_t height,
+ pixman_region32_t *bbox)
+{
+ float min_x = HUGE_VALF, min_y = HUGE_VALF;
+ float max_x = -HUGE_VALF, max_y = -HUGE_VALF;
+ int32_t s[4][2] = {
+ { sx, sy },
+ { sx, sy + height },
+ { sx + width, sy },
+ { sx + width, sy + height }
+ };
+ float int_x, int_y;
+ int i;
+
+ if (width == 0 || height == 0) {
+ /* avoid rounding empty bbox to 1x1 */
+ pixman_region32_init(bbox);
+ return;
+ }
+
+ for (i = 0; i < 4; ++i) {
+ float x, y;
+ weston_surface_to_global_float(surface,
+ s[i][0], s[i][1], &x, &y);
+ if (x < min_x)
+ min_x = x;
+ if (x > max_x)
+ max_x = x;
+ if (y < min_y)
+ min_y = y;
+ if (y > max_y)
+ max_y = y;
+ }
+
+ int_x = floorf(min_x);
+ int_y = floorf(min_y);
+ pixman_region32_init_rect(bbox, int_x, int_y,
+ ceilf(max_x) - int_x, ceilf(max_y) - int_y);
+}
+
+static void
+weston_surface_update_transform_disable(struct weston_surface *surface)
+{
+ surface->transform.enabled = 0;
+
+ /* round off fractions when not transformed */
+ surface->geometry.x = roundf(surface->geometry.x);
+ surface->geometry.y = roundf(surface->geometry.y);
+
+ /* Otherwise identity matrix, but with x and y translation. */
+ surface->transform.position.matrix.type = WESTON_MATRIX_TRANSFORM_TRANSLATE;
+ surface->transform.position.matrix.d[12] = surface->geometry.x;
+ surface->transform.position.matrix.d[13] = surface->geometry.y;
+
+ surface->transform.matrix = surface->transform.position.matrix;
+
+ surface->transform.inverse = surface->transform.position.matrix;
+ surface->transform.inverse.d[12] = -surface->geometry.x;
+ surface->transform.inverse.d[13] = -surface->geometry.y;
+
+ pixman_region32_init_rect(&surface->transform.boundingbox,
+ surface->geometry.x,
+ surface->geometry.y,
+ surface->geometry.width,
+ surface->geometry.height);
+
+ if (surface->alpha == 1.0) {
+ pixman_region32_copy(&surface->transform.opaque,
+ &surface->opaque);
+ pixman_region32_translate(&surface->transform.opaque,
+ surface->geometry.x,
+ surface->geometry.y);
+ }
+}
+
+static int
+weston_surface_update_transform_enable(struct weston_surface *surface)
+{
+ struct weston_surface *parent = surface->geometry.parent;
+ struct weston_matrix *matrix = &surface->transform.matrix;
+ struct weston_matrix *inverse = &surface->transform.inverse;
+ struct weston_transform *tform;
+
+ surface->transform.enabled = 1;
+
+ /* Otherwise identity matrix, but with x and y translation. */
+ surface->transform.position.matrix.type = WESTON_MATRIX_TRANSFORM_TRANSLATE;
+ surface->transform.position.matrix.d[12] = surface->geometry.x;
+ surface->transform.position.matrix.d[13] = surface->geometry.y;
+
+ weston_matrix_init(matrix);
+ wl_list_for_each(tform, &surface->geometry.transformation_list, link)
+ weston_matrix_multiply(matrix, &tform->matrix);
+
+ if (parent)
+ weston_matrix_multiply(matrix, &parent->transform.matrix);
+
+ if (weston_matrix_invert(inverse, matrix) < 0) {
+ /* Oops, bad total transformation, not invertible */
+ weston_log("error: weston_surface %p"
+ " transformation not invertible.\n", surface);
+ return -1;
+ }
+
+ surface_compute_bbox(surface, 0, 0, surface->geometry.width,
+ surface->geometry.height,
+ &surface->transform.boundingbox);
+
+ return 0;
+}
+
+WL_EXPORT void
+weston_surface_update_transform(struct weston_surface *surface)
+{
+ struct weston_surface *parent = surface->geometry.parent;
+
+ if (!surface->transform.dirty)
+ return;
+
+ if (parent)
+ weston_surface_update_transform(parent);
+
+ surface->transform.dirty = 0;
+
+ weston_surface_damage_below(surface);
+
+ pixman_region32_fini(&surface->transform.boundingbox);
+ pixman_region32_fini(&surface->transform.opaque);
+ pixman_region32_init(&surface->transform.opaque);
+
+ /* transform.position is always in transformation_list */
+ if (surface->geometry.transformation_list.next ==
+ &surface->transform.position.link &&
+ surface->geometry.transformation_list.prev ==
+ &surface->transform.position.link &&
+ !parent) {
+ weston_surface_update_transform_disable(surface);
+ } else {
+ if (weston_surface_update_transform_enable(surface) < 0)
+ weston_surface_update_transform_disable(surface);
+ }
+
+ weston_surface_damage_below(surface);
+
+ weston_surface_assign_output(surface);
+
+ wl_signal_emit(&surface->compositor->transform_signal, surface);
+}
+
+WL_EXPORT void
+weston_surface_geometry_dirty(struct weston_surface *surface)
+{
+ struct weston_surface *child;
+
+ /*
+ * The invariant: if surface->geometry.dirty, then all surfaces
+ * in surface->geometry.child_list have geometry.dirty too.
+ * Corollary: if not parent->geometry.dirty, then all ancestors
+ * are not dirty.
+ */
+
+ if (surface->transform.dirty)
+ return;
+
+ surface->transform.dirty = 1;
+
+ wl_list_for_each(child, &surface->geometry.child_list,
+ geometry.parent_link)
+ weston_surface_geometry_dirty(child);
+}
+
+WL_EXPORT void
+weston_surface_to_global_fixed(struct weston_surface *surface,
+ wl_fixed_t sx, wl_fixed_t sy,
+ wl_fixed_t *x, wl_fixed_t *y)
+{
+ float xf, yf;
+
+ weston_surface_to_global_float(surface,
+ wl_fixed_to_double(sx),
+ wl_fixed_to_double(sy),
+ &xf, &yf);
+ *x = wl_fixed_from_double(xf);
+ *y = wl_fixed_from_double(yf);
+}
+
+WL_EXPORT void
+weston_surface_from_global_float(struct weston_surface *surface,
+ float x, float y, float *sx, float *sy)
+{
+ if (surface->transform.enabled) {
+ struct weston_vector v = { { x, y, 0.0f, 1.0f } };
+
+ weston_matrix_transform(&surface->transform.inverse, &v);
+
+ if (fabsf(v.f[3]) < 1e-6) {
+ weston_log("warning: numerical instability in "
+ "weston_surface_from_global(), divisor = %g\n",
+ v.f[3]);
+ *sx = 0;
+ *sy = 0;
+ return;
+ }
+
+ *sx = v.f[0] / v.f[3];
+ *sy = v.f[1] / v.f[3];
+ } else {
+ *sx = x - surface->geometry.x;
+ *sy = y - surface->geometry.y;
+ }
+}
+
+WL_EXPORT void
+weston_surface_from_global_fixed(struct weston_surface *surface,
+ wl_fixed_t x, wl_fixed_t y,
+ wl_fixed_t *sx, wl_fixed_t *sy)
+{
+ float sxf, syf;
+
+ weston_surface_from_global_float(surface,
+ wl_fixed_to_double(x),
+ wl_fixed_to_double(y),
+ &sxf, &syf);
+ *sx = wl_fixed_from_double(sxf);
+ *sy = wl_fixed_from_double(syf);
+}
+
+WL_EXPORT void
+weston_surface_from_global(struct weston_surface *surface,
+ int32_t x, int32_t y, int32_t *sx, int32_t *sy)
+{
+ float sxf, syf;
+
+ weston_surface_from_global_float(surface, x, y, &sxf, &syf);
+ *sx = floorf(sxf);
+ *sy = floorf(syf);
+}
+
+WL_EXPORT void
+weston_surface_schedule_repaint(struct weston_surface *surface)
+{
+ struct weston_output *output;
+
+ wl_list_for_each(output, &surface->compositor->output_list, link)
+ if (surface->output_mask & (1 << output->id))
+ weston_output_schedule_repaint(output);
+}
+
+WL_EXPORT void
+weston_surface_damage(struct weston_surface *surface)
+{
+ pixman_region32_union_rect(&surface->damage, &surface->damage,
+ 0, 0, surface->geometry.width,
+ surface->geometry.height);
+
+ weston_surface_schedule_repaint(surface);
+}
+
+WL_EXPORT void
+weston_surface_configure(struct weston_surface *surface,
+ float x, float y, int width, int height)
+{
+ surface->geometry.x = x;
+ surface->geometry.y = y;
+ surface->geometry.width = width;
+ surface->geometry.height = height;
+ weston_surface_geometry_dirty(surface);
+}
+
+WL_EXPORT void
+weston_surface_set_position(struct weston_surface *surface,
+ float x, float y)
+{
+ surface->geometry.x = x;
+ surface->geometry.y = y;
+ weston_surface_geometry_dirty(surface);
+}
+
+static void
+transform_parent_handle_parent_destroy(struct wl_listener *listener,
+ void *data)
+{
+ struct weston_surface *surface =
+ container_of(listener, struct weston_surface,
+ geometry.parent_destroy_listener);
+
+ weston_surface_set_transform_parent(surface, NULL);
+}
+
+WL_EXPORT void
+weston_surface_set_transform_parent(struct weston_surface *surface,
+ struct weston_surface *parent)
+{
+ if (surface->geometry.parent) {
+ wl_list_remove(&surface->geometry.parent_destroy_listener.link);
+ wl_list_remove(&surface->geometry.parent_link);
+ }
+
+ surface->geometry.parent = parent;
+
+ surface->geometry.parent_destroy_listener.notify =
+ transform_parent_handle_parent_destroy;
+ if (parent) {
+ wl_signal_add(&parent->destroy_signal,
+ &surface->geometry.parent_destroy_listener);
+ wl_list_insert(&parent->geometry.child_list,
+ &surface->geometry.parent_link);
+ }
+
+ weston_surface_geometry_dirty(surface);
+}
+
+WL_EXPORT int
+weston_surface_is_mapped(struct weston_surface *surface)
+{
+ if (surface->output)
+ return 1;
+ else
+ return 0;
+}
+
+WL_EXPORT int32_t
+weston_surface_buffer_width(struct weston_surface *surface)
+{
+ int32_t width;
+ switch (surface->buffer_transform) {
+ case WL_OUTPUT_TRANSFORM_90:
+ case WL_OUTPUT_TRANSFORM_270:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+ width = surface->buffer_ref.buffer->height;
+ break;
+ default:
+ width = surface->buffer_ref.buffer->width;
+ break;
+ }
+ return width / surface->buffer_scale;
+}
+
+WL_EXPORT int32_t
+weston_surface_buffer_height(struct weston_surface *surface)
+{
+ int32_t height;
+ switch (surface->buffer_transform) {
+ case WL_OUTPUT_TRANSFORM_90:
+ case WL_OUTPUT_TRANSFORM_270:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+ height = surface->buffer_ref.buffer->width;
+ break;
+ default:
+ height = surface->buffer_ref.buffer->height;
+ break;
+ }
+ return height / surface->buffer_scale;
+}
+
+WL_EXPORT uint32_t
+weston_compositor_get_time(void)
+{
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+
+ return tv.tv_sec * 1000 + tv.tv_usec / 1000;
+}
+
+WL_EXPORT struct weston_surface *
+weston_compositor_pick_surface(struct weston_compositor *compositor,
+ wl_fixed_t x, wl_fixed_t y,
+ wl_fixed_t *sx, wl_fixed_t *sy)
+{
+ struct weston_surface *surface;
+
+ wl_list_for_each(surface, &compositor->surface_list, link) {
+ weston_surface_from_global_fixed(surface, x, y, sx, sy);
+ if (pixman_region32_contains_point(&surface->input,
+ wl_fixed_to_int(*sx),
+ wl_fixed_to_int(*sy),
+ NULL))
+ return surface;
+ }
+
+ return NULL;
+}
+
+static void
+weston_compositor_repick(struct weston_compositor *compositor)
+{
+ struct weston_seat *seat;
+
+ if (!compositor->focus)
+ return;
+
+ wl_list_for_each(seat, &compositor->seat_list, link)
+ weston_seat_repick(seat);
+}
+
+WL_EXPORT void
+weston_surface_unmap(struct weston_surface *surface)
+{
+ struct weston_seat *seat;
+
+ weston_surface_damage_below(surface);
+ surface->output = NULL;
+ surface->plane = NULL;
+ wl_list_remove(&surface->layer_link);
+ wl_list_remove(&surface->link);
+ wl_list_init(&surface->link);
+
+ wl_list_for_each(seat, &surface->compositor->seat_list, link) {
+ if (seat->keyboard && seat->keyboard->focus == surface)
+ weston_keyboard_set_focus(seat->keyboard, NULL);
+ if (seat->pointer && seat->pointer->focus == surface)
+ weston_pointer_set_focus(seat->pointer,
+ NULL,
+ wl_fixed_from_int(0),
+ wl_fixed_from_int(0));
+ if (seat->touch && seat->touch->focus == surface)
+ weston_touch_set_focus(seat, NULL);
+ }
+
+ weston_surface_schedule_repaint(surface);
+}
+
+struct weston_frame_callback {
+ struct wl_resource *resource;
+ struct wl_list link;
+};
+
+
+WL_EXPORT void
+weston_surface_destroy(struct weston_surface *surface)
+{
+ struct weston_compositor *compositor = surface->compositor;
+ struct weston_frame_callback *cb, *next;
+
+ if (--surface->ref_count > 0)
+ return;
+
+ wl_signal_emit(&surface->destroy_signal, &surface->resource);
+
+ assert(wl_list_empty(&surface->geometry.child_list));
+ assert(wl_list_empty(&surface->subsurface_list_pending));
+ assert(wl_list_empty(&surface->subsurface_list));
+
+ if (weston_surface_is_mapped(surface))
+ weston_surface_unmap(surface);
+
+ wl_list_for_each_safe(cb, next,
+ &surface->pending.frame_callback_list, link)
+ wl_resource_destroy(cb->resource);
+
+ pixman_region32_fini(&surface->pending.input);
+ pixman_region32_fini(&surface->pending.opaque);
+ pixman_region32_fini(&surface->pending.damage);
+
+ if (surface->pending.buffer)
+ wl_list_remove(&surface->pending.buffer_destroy_listener.link);
+
+ weston_buffer_reference(&surface->buffer_ref, NULL);
+
+ compositor->renderer->destroy_surface(surface);
+
+ pixman_region32_fini(&surface->transform.boundingbox);
+ pixman_region32_fini(&surface->damage);
+ pixman_region32_fini(&surface->opaque);
+ pixman_region32_fini(&surface->clip);
+ pixman_region32_fini(&surface->input);
+
+ wl_list_for_each_safe(cb, next, &surface->frame_callback_list, link)
+ wl_resource_destroy(cb->resource);
+
+ weston_surface_set_transform_parent(surface, NULL);
+
+ free(surface);
+}
+
+static void
+destroy_surface(struct wl_resource *resource)
+{
+ struct weston_surface *surface = wl_resource_get_user_data(resource);
+
+ weston_surface_destroy(surface);
+}
+
+static void
+weston_buffer_destroy_handler(struct wl_listener *listener, void *data)
+{
+ struct weston_buffer *buffer =
+ container_of(listener, struct weston_buffer, destroy_listener);
+
+ wl_signal_emit(&buffer->destroy_signal, buffer);
+ free(buffer);
+}
+
+struct weston_buffer *
+weston_buffer_from_resource(struct wl_resource *resource)
+{
+ struct weston_buffer *buffer;
+ struct wl_listener *listener;
+
+ listener = wl_resource_get_destroy_listener(resource,
+ weston_buffer_destroy_handler);
+
+ if (listener)
+ return container_of(listener, struct weston_buffer,
+ destroy_listener);
+
+ buffer = zalloc(sizeof *buffer);
+ if (buffer == NULL)
+ return NULL;
+
+ buffer->resource = resource;
+ wl_signal_init(&buffer->destroy_signal);
+ buffer->destroy_listener.notify = weston_buffer_destroy_handler;
+ buffer->y_inverted = 1;
+ wl_resource_add_destroy_listener(resource, &buffer->destroy_listener);
+
+ return buffer;
+}
+
+static void
+weston_buffer_reference_handle_destroy(struct wl_listener *listener,
+ void *data)
+{
+ struct weston_buffer_reference *ref =
+ container_of(listener, struct weston_buffer_reference,
+ destroy_listener);
+
+ assert((struct weston_buffer *)data == ref->buffer);
+ ref->buffer = NULL;
+}
+
+WL_EXPORT void
+weston_buffer_reference(struct weston_buffer_reference *ref,
+ struct weston_buffer *buffer)
+{
+ if (ref->buffer && buffer != ref->buffer) {
+ ref->buffer->busy_count--;
+ if (ref->buffer->busy_count == 0) {
+ assert(wl_resource_get_client(ref->buffer->resource));
+ wl_resource_queue_event(ref->buffer->resource,
+ WL_BUFFER_RELEASE);
+ }
+ wl_list_remove(&ref->destroy_listener.link);
+ }
+
+ if (buffer && buffer != ref->buffer) {
+ buffer->busy_count++;
+ wl_signal_add(&buffer->destroy_signal,
+ &ref->destroy_listener);
+ }
+
+ ref->buffer = buffer;
+ ref->destroy_listener.notify = weston_buffer_reference_handle_destroy;
+}
+
+static void
+weston_surface_attach(struct weston_surface *surface,
+ struct weston_buffer *buffer)
+{
+ weston_buffer_reference(&surface->buffer_ref, buffer);
+
+ if (!buffer) {
+ if (weston_surface_is_mapped(surface))
+ weston_surface_unmap(surface);
+ }
+
+ surface->compositor->renderer->attach(surface, buffer);
+}
+
+WL_EXPORT void
+weston_surface_restack(struct weston_surface *surface, struct wl_list *below)
+{
+ wl_list_remove(&surface->layer_link);
+ wl_list_insert(below, &surface->layer_link);
+ weston_surface_damage_below(surface);
+ weston_surface_damage(surface);
+}
+
+WL_EXPORT void
+weston_compositor_damage_all(struct weston_compositor *compositor)
+{
+ struct weston_output *output;
+
+ wl_list_for_each(output, &compositor->output_list, link)
+ weston_output_damage(output);
+}
+
+WL_EXPORT void
+weston_output_damage(struct weston_output *output)
+{
+ struct weston_compositor *compositor = output->compositor;
+
+ pixman_region32_union(&compositor->primary_plane.damage,
+ &compositor->primary_plane.damage,
+ &output->region);
+ weston_output_schedule_repaint(output);
+}
+
+static void
+surface_accumulate_damage(struct weston_surface *surface,
+ pixman_region32_t *opaque)
+{
+ if (surface->buffer_ref.buffer &&
+ wl_shm_buffer_get(surface->buffer_ref.buffer->resource))
+ surface->compositor->renderer->flush_damage(surface);
+
+ if (surface->transform.enabled) {
+ pixman_box32_t *extents;
+
+ extents = pixman_region32_extents(&surface->damage);
+ surface_compute_bbox(surface, extents->x1, extents->y1,
+ extents->x2 - extents->x1,
+ extents->y2 - extents->y1,
+ &surface->damage);
+ pixman_region32_translate(&surface->damage,
+ -surface->plane->x,
+ -surface->plane->y);
+ } else {
+ pixman_region32_translate(&surface->damage,
+ surface->geometry.x - surface->plane->x,
+ surface->geometry.y - surface->plane->y);
+ }
+
+ pixman_region32_subtract(&surface->damage, &surface->damage, opaque);
+ pixman_region32_union(&surface->plane->damage,
+ &surface->plane->damage, &surface->damage);
+ empty_region(&surface->damage);
+ pixman_region32_copy(&surface->clip, opaque);
+ pixman_region32_union(opaque, opaque, &surface->transform.opaque);
+}
+
+static void
+compositor_accumulate_damage(struct weston_compositor *ec)
+{
+ struct weston_plane *plane;
+ struct weston_surface *es;
+ pixman_region32_t opaque, clip;
+
+ pixman_region32_init(&clip);
+
+ wl_list_for_each(plane, &ec->plane_list, link) {
+ pixman_region32_copy(&plane->clip, &clip);
+
+ pixman_region32_init(&opaque);
+
+ wl_list_for_each(es, &ec->surface_list, link) {
+ if (es->plane != plane)
+ continue;
+
+ surface_accumulate_damage(es, &opaque);
+ }
+
+ pixman_region32_union(&clip, &clip, &opaque);
+ pixman_region32_fini(&opaque);
+ }
+
+ pixman_region32_fini(&clip);
+
+ wl_list_for_each(es, &ec->surface_list, link) {
+ /* Both the renderer and the backend have seen the buffer
+ * by now. If renderer needs the buffer, it has its own
+ * reference set. If the backend wants to keep the buffer
+ * around for migrating the surface into a non-primary plane
+ * later, keep_buffer is true. Otherwise, drop the core
+ * reference now, and allow early buffer release. This enables
+ * clients to use single-buffering.
+ */
+ if (!es->keep_buffer)
+ weston_buffer_reference(&es->buffer_ref, NULL);
+ }
+}
+
+static void
+surface_list_add(struct weston_compositor *compositor,
+ struct weston_surface *surface)
+{
+ struct weston_subsurface *sub;
+
+ if (wl_list_empty(&surface->subsurface_list)) {
+ weston_surface_update_transform(surface);
+ wl_list_insert(compositor->surface_list.prev, &surface->link);
+ return;
+ }
+
+ wl_list_for_each(sub, &surface->subsurface_list, parent_link) {
+ if (!weston_surface_is_mapped(sub->surface))
+ continue;
+
+ if (sub->surface == surface) {
+ weston_surface_update_transform(sub->surface);
+ wl_list_insert(compositor->surface_list.prev,
+ &sub->surface->link);
+ } else {
+ surface_list_add(compositor, sub->surface);
+ }
+ }
+}
+
+static void
+weston_compositor_build_surface_list(struct weston_compositor *compositor)
+{
+ struct weston_surface *surface;
+ struct weston_layer *layer;
+
+ wl_list_init(&compositor->surface_list);
+ wl_list_for_each(layer, &compositor->layer_list, link) {
+ wl_list_for_each(surface, &layer->surface_list, layer_link) {
+ surface_list_add(compositor, surface);
+ }
+ }
+}
+
+static int
+weston_output_repaint(struct weston_output *output, uint32_t msecs)
+{
+ struct weston_compositor *ec = output->compositor;
+ struct weston_surface *es;
+ struct weston_animation *animation, *next;
+ struct weston_frame_callback *cb, *cnext;
+ struct wl_list frame_callback_list;
+ pixman_region32_t output_damage;
+ int r;
+
+ /* Rebuild the surface list and update surface transforms up front. */
+ weston_compositor_build_surface_list(ec);
+
+ if (output->assign_planes && !output->disable_planes)
+ output->assign_planes(output);
+ else
+ wl_list_for_each(es, &ec->surface_list, link)
+ weston_surface_move_to_plane(es, &ec->primary_plane);
+
+ wl_list_init(&frame_callback_list);
+ wl_list_for_each(es, &ec->surface_list, link) {
+ if (es->output == output) {
+ wl_list_insert_list(&frame_callback_list,
+ &es->frame_callback_list);
+ wl_list_init(&es->frame_callback_list);
+ }
+ }
+
+ compositor_accumulate_damage(ec);
+
+ pixman_region32_init(&output_damage);
+ pixman_region32_intersect(&output_damage,
+ &ec->primary_plane.damage, &output->region);
+ pixman_region32_subtract(&output_damage,
+ &output_damage, &ec->primary_plane.clip);
+
+ if (output->dirty)
+ weston_output_update_matrix(output);
+
+ r = output->repaint(output, &output_damage);
+
+ pixman_region32_fini(&output_damage);
+
+ output->repaint_needed = 0;
+
+ weston_compositor_repick(ec);
+ wl_event_loop_dispatch(ec->input_loop, 0);
+
+ wl_list_for_each_safe(cb, cnext, &frame_callback_list, link) {
+ wl_callback_send_done(cb->resource, msecs);
+ wl_resource_destroy(cb->resource);
+ }
+
+ wl_list_for_each_safe(animation, next, &output->animation_list, link) {
+ animation->frame_counter++;
+ animation->frame(animation, output, msecs);
+ }
+
+ return r;
+}
+
+static int
+weston_compositor_read_input(int fd, uint32_t mask, void *data)
+{
+ struct weston_compositor *compositor = data;
+
+ wl_event_loop_dispatch(compositor->input_loop, 0);
+
+ return 1;
+}
+
+WL_EXPORT void
+weston_output_finish_frame(struct weston_output *output, uint32_t msecs)
+{
+ struct weston_compositor *compositor = output->compositor;
+ struct wl_event_loop *loop =
+ wl_display_get_event_loop(compositor->wl_display);
+ int fd, r;
+
+ output->frame_time = msecs;
+
+ if (output->repaint_needed &&
+ compositor->state != WESTON_COMPOSITOR_SLEEPING &&
+ compositor->state != WESTON_COMPOSITOR_OFFSCREEN) {
+ r = weston_output_repaint(output, msecs);
+ if (!r)
+ return;
+ }
+
+ output->repaint_scheduled = 0;
+ if (compositor->input_loop_source)
+ return;
+
+ fd = wl_event_loop_get_fd(compositor->input_loop);
+ compositor->input_loop_source =
+ wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE,
+ weston_compositor_read_input, compositor);
+}
+
+static void
+idle_repaint(void *data)
+{
+ struct weston_output *output = data;
+
+ output->start_repaint_loop(output);
+}
+
+WL_EXPORT void
+weston_layer_init(struct weston_layer *layer, struct wl_list *below)
+{
+ wl_list_init(&layer->surface_list);
+ if (below != NULL)
+ wl_list_insert(below, &layer->link);
+}
+
+WL_EXPORT void
+weston_output_schedule_repaint(struct weston_output *output)
+{
+ struct weston_compositor *compositor = output->compositor;
+ struct wl_event_loop *loop;
+
+ if (compositor->state == WESTON_COMPOSITOR_SLEEPING ||
+ compositor->state == WESTON_COMPOSITOR_OFFSCREEN)
+ return;
+
+ loop = wl_display_get_event_loop(compositor->wl_display);
+ output->repaint_needed = 1;
+ if (output->repaint_scheduled)
+ return;
+
+ wl_event_loop_add_idle(loop, idle_repaint, output);
+ output->repaint_scheduled = 1;
+
+ if (compositor->input_loop_source) {
+ wl_event_source_remove(compositor->input_loop_source);
+ compositor->input_loop_source = NULL;
+ }
+}
+
+WL_EXPORT void
+weston_compositor_schedule_repaint(struct weston_compositor *compositor)
+{
+ struct weston_output *output;
+
+ wl_list_for_each(output, &compositor->output_list, link)
+ weston_output_schedule_repaint(output);
+}
+
+static void
+surface_destroy(struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static void
+surface_attach(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *buffer_resource, int32_t sx, int32_t sy)
+{
+ struct weston_surface *surface = wl_resource_get_user_data(resource);
+ struct weston_buffer *buffer = NULL;
+
+ if (buffer_resource) {
+ buffer = weston_buffer_from_resource(buffer_resource);
+ if (buffer == NULL) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+ }
+
+ /* Attach, attach, without commit in between does not send
+ * wl_buffer.release. */
+ if (surface->pending.buffer)
+ wl_list_remove(&surface->pending.buffer_destroy_listener.link);
+
+ surface->pending.sx = sx;
+ surface->pending.sy = sy;
+ surface->pending.buffer = buffer;
+ surface->pending.newly_attached = 1;
+ if (buffer) {
+ wl_signal_add(&buffer->destroy_signal,
+ &surface->pending.buffer_destroy_listener);
+ }
+}
+
+static void
+surface_damage(struct wl_client *client,
+ struct wl_resource *resource,
+ int32_t x, int32_t y, int32_t width, int32_t height)
+{
+ struct weston_surface *surface = wl_resource_get_user_data(resource);
+
+ pixman_region32_union_rect(&surface->pending.damage,
+ &surface->pending.damage,
+ x, y, width, height);
+}
+
+static void
+destroy_frame_callback(struct wl_resource *resource)
+{
+ struct weston_frame_callback *cb = wl_resource_get_user_data(resource);
+
+ wl_list_remove(&cb->link);
+ free(cb);
+}
+
+static void
+surface_frame(struct wl_client *client,
+ struct wl_resource *resource, uint32_t callback)
+{
+ struct weston_frame_callback *cb;
+ struct weston_surface *surface = wl_resource_get_user_data(resource);
+
+ cb = malloc(sizeof *cb);
+ if (cb == NULL) {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ cb->resource = wl_resource_create(client, &wl_callback_interface, 1,
+ callback);
+ if (cb->resource == NULL) {
+ free(cb);
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ wl_resource_set_implementation(cb->resource, NULL, cb,
+ destroy_frame_callback);
+
+ wl_list_insert(surface->pending.frame_callback_list.prev, &cb->link);
+}
+
+static void
+surface_set_opaque_region(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *region_resource)
+{
+ struct weston_surface *surface = wl_resource_get_user_data(resource);
+ struct weston_region *region;
+
+ if (region_resource) {
+ region = wl_resource_get_user_data(region_resource);
+ pixman_region32_copy(&surface->pending.opaque,
+ &region->region);
+ } else {
+ empty_region(&surface->pending.opaque);
+ }
+}
+
+static void
+surface_set_input_region(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *region_resource)
+{
+ struct weston_surface *surface = wl_resource_get_user_data(resource);
+ struct weston_region *region;
+
+ if (region_resource) {
+ region = wl_resource_get_user_data(region_resource);
+ pixman_region32_copy(&surface->pending.input,
+ &region->region);
+ } else {
+ pixman_region32_fini(&surface->pending.input);
+ region_init_infinite(&surface->pending.input);
+ }
+}
+
+static void
+weston_surface_commit_subsurface_order(struct weston_surface *surface)
+{
+ struct weston_subsurface *sub;
+
+ wl_list_for_each_reverse(sub, &surface->subsurface_list_pending,
+ parent_link_pending) {
+ wl_list_remove(&sub->parent_link);
+ wl_list_insert(&surface->subsurface_list, &sub->parent_link);
+ }
+}
+
+static void
+weston_surface_commit(struct weston_surface *surface)
+{
+ pixman_region32_t opaque;
+ int surface_width = 0;
+ int surface_height = 0;
+
+ /* wl_surface.set_buffer_transform */
+ surface->buffer_transform = surface->pending.buffer_transform;
+
+ /* wl_surface.set_buffer_scale */
+ surface->buffer_scale = surface->pending.buffer_scale;
+
+ /* wl_surface.attach */
+ if (surface->pending.buffer || surface->pending.newly_attached)
+ weston_surface_attach(surface, surface->pending.buffer);
+
+ if (surface->buffer_ref.buffer) {
+ surface_width = weston_surface_buffer_width(surface);
+ surface_height = weston_surface_buffer_height(surface);
+ }
+
+ if (surface->configure && surface->pending.newly_attached)
+ surface->configure(surface,
+ surface->pending.sx, surface->pending.sy,
+ surface_width, surface_height);
+
+ if (surface->pending.buffer)
+ wl_list_remove(&surface->pending.buffer_destroy_listener.link);
+ surface->pending.buffer = NULL;
+ surface->pending.sx = 0;
+ surface->pending.sy = 0;
+ surface->pending.newly_attached = 0;
+
+ /* wl_surface.damage */
+ pixman_region32_union(&surface->damage, &surface->damage,
+ &surface->pending.damage);
+ pixman_region32_intersect_rect(&surface->damage, &surface->damage,
+ 0, 0,
+ surface->geometry.width,
+ surface->geometry.height);
+ empty_region(&surface->pending.damage);
+
+ /* wl_surface.set_opaque_region */
+ pixman_region32_init_rect(&opaque, 0, 0,
+ surface->geometry.width,
+ surface->geometry.height);
+ pixman_region32_intersect(&opaque,
+ &opaque, &surface->pending.opaque);
+
+ if (!pixman_region32_equal(&opaque, &surface->opaque)) {
+ pixman_region32_copy(&surface->opaque, &opaque);
+ weston_surface_geometry_dirty(surface);
+ }
+
+ pixman_region32_fini(&opaque);
+
+ /* wl_surface.set_input_region */
+ pixman_region32_fini(&surface->input);
+ pixman_region32_init_rect(&surface->input, 0, 0,
+ surface->geometry.width,
+ surface->geometry.height);
+ pixman_region32_intersect(&surface->input,
+ &surface->input, &surface->pending.input);
+
+ /* wl_surface.frame */
+ wl_list_insert_list(&surface->frame_callback_list,
+ &surface->pending.frame_callback_list);
+ wl_list_init(&surface->pending.frame_callback_list);
+
+ weston_surface_commit_subsurface_order(surface);
+
+ weston_surface_schedule_repaint(surface);
+}
+
+static void
+weston_subsurface_commit(struct weston_subsurface *sub);
+
+static void
+weston_subsurface_parent_commit(struct weston_subsurface *sub,
+ int parent_is_synchronized);
+
+static void
+surface_commit(struct wl_client *client, struct wl_resource *resource)
+{
+ struct weston_surface *surface = wl_resource_get_user_data(resource);
+ struct weston_subsurface *sub = weston_surface_to_subsurface(surface);
+
+ if (sub) {
+ weston_subsurface_commit(sub);
+ return;
+ }
+
+ weston_surface_commit(surface);
+
+ wl_list_for_each(sub, &surface->subsurface_list, parent_link) {
+ if (sub->surface != surface)
+ weston_subsurface_parent_commit(sub, 0);
+ }
+}
+
+static void
+surface_set_buffer_transform(struct wl_client *client,
+ struct wl_resource *resource, int transform)
+{
+ struct weston_surface *surface = wl_resource_get_user_data(resource);
+
+ surface->pending.buffer_transform = transform;
+}
+
+static void
+surface_set_buffer_scale(struct wl_client *client,
+ struct wl_resource *resource,
+ int32_t scale)
+{
+ struct weston_surface *surface = wl_resource_get_user_data(resource);
+
+ surface->pending.buffer_scale = scale;
+}
+
+static const struct wl_surface_interface surface_interface = {
+ surface_destroy,
+ surface_attach,
+ surface_damage,
+ surface_frame,
+ surface_set_opaque_region,
+ surface_set_input_region,
+ surface_commit,
+ surface_set_buffer_transform,
+ surface_set_buffer_scale
+};
+
+static void
+compositor_create_surface(struct wl_client *client,
+ struct wl_resource *resource, uint32_t id)
+{
+ struct weston_compositor *ec = wl_resource_get_user_data(resource);
+ struct weston_surface *surface;
+
+ surface = weston_surface_create(ec);
+ if (surface == NULL) {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ surface->resource =
+ wl_resource_create(client, &wl_surface_interface,
+ wl_resource_get_version(resource), id);
+ if (surface->resource == NULL) {
+ weston_surface_destroy(surface);
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+ wl_resource_set_implementation(surface->resource, &surface_interface,
+ surface, destroy_surface);
+}
+
+static void
+destroy_region(struct wl_resource *resource)
+{
+ struct weston_region *region = wl_resource_get_user_data(resource);
+
+ pixman_region32_fini(&region->region);
+ free(region);
+}
+
+static void
+region_destroy(struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static void
+region_add(struct wl_client *client, struct wl_resource *resource,
+ int32_t x, int32_t y, int32_t width, int32_t height)
+{
+ struct weston_region *region = wl_resource_get_user_data(resource);
+
+ pixman_region32_union_rect(&region->region, &region->region,
+ x, y, width, height);
+}
+
+static void
+region_subtract(struct wl_client *client, struct wl_resource *resource,
+ int32_t x, int32_t y, int32_t width, int32_t height)
+{
+ struct weston_region *region = wl_resource_get_user_data(resource);
+ pixman_region32_t rect;
+
+ pixman_region32_init_rect(&rect, x, y, width, height);
+ pixman_region32_subtract(&region->region, &region->region, &rect);
+ pixman_region32_fini(&rect);
+}
+
+static const struct wl_region_interface region_interface = {
+ region_destroy,
+ region_add,
+ region_subtract
+};
+
+static void
+compositor_create_region(struct wl_client *client,
+ struct wl_resource *resource, uint32_t id)
+{
+ struct weston_region *region;
+
+ region = malloc(sizeof *region);
+ if (region == NULL) {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ pixman_region32_init(&region->region);
+
+ region->resource =
+ wl_resource_create(client, &wl_region_interface, 1, id);
+ if (region->resource == NULL) {
+ free(region);
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+ wl_resource_set_implementation(region->resource, &region_interface,
+ region, destroy_region);
+}
+
+static const struct wl_compositor_interface compositor_interface = {
+ compositor_create_surface,
+ compositor_create_region
+};
+
+static void
+weston_subsurface_commit_from_cache(struct weston_subsurface *sub)
+{
+ struct weston_surface *surface = sub->surface;
+ pixman_region32_t opaque;
+ int surface_width = 0;
+ int surface_height = 0;
+
+ /* wl_surface.set_buffer_transform */
+ surface->buffer_transform = sub->cached.buffer_transform;
+
+ /* wl_surface.set_buffer_scale */
+ surface->buffer_scale = sub->cached.buffer_scale;
+
+ /* wl_surface.attach */
+ if (sub->cached.buffer_ref.buffer || sub->cached.newly_attached)
+ weston_surface_attach(surface, sub->cached.buffer_ref.buffer);
+ weston_buffer_reference(&sub->cached.buffer_ref, NULL);
+
+ if (surface->buffer_ref.buffer) {
+ surface_width = weston_surface_buffer_width(surface);
+ surface_height = weston_surface_buffer_height(surface);
+ }
+
+ if (surface->configure && sub->cached.newly_attached)
+ surface->configure(surface, sub->cached.sx, sub->cached.sy,
+ surface_width, surface_height);
+ sub->cached.sx = 0;
+ sub->cached.sy = 0;
+ sub->cached.newly_attached = 0;
+
+ /* wl_surface.damage */
+ pixman_region32_union(&surface->damage, &surface->damage,
+ &sub->cached.damage);
+ pixman_region32_intersect_rect(&surface->damage, &surface->damage,
+ 0, 0,
+ surface->geometry.width,
+ surface->geometry.height);
+ empty_region(&sub->cached.damage);
+
+ /* wl_surface.set_opaque_region */
+ pixman_region32_init_rect(&opaque, 0, 0,
+ surface->geometry.width,
+ surface->geometry.height);
+ pixman_region32_intersect(&opaque,
+ &opaque, &sub->cached.opaque);
+
+ if (!pixman_region32_equal(&opaque, &surface->opaque)) {
+ pixman_region32_copy(&surface->opaque, &opaque);
+ weston_surface_geometry_dirty(surface);
+ }
+
+ pixman_region32_fini(&opaque);
+
+ /* wl_surface.set_input_region */
+ pixman_region32_fini(&surface->input);
+ pixman_region32_init_rect(&surface->input, 0, 0,
+ surface->geometry.width,
+ surface->geometry.height);
+ pixman_region32_intersect(&surface->input,
+ &surface->input, &sub->cached.input);
+
+ /* wl_surface.frame */
+ wl_list_insert_list(&surface->frame_callback_list,
+ &sub->cached.frame_callback_list);
+ wl_list_init(&sub->cached.frame_callback_list);
+
+ weston_surface_commit_subsurface_order(surface);
+
+ weston_surface_schedule_repaint(surface);
+
+ sub->cached.has_data = 0;
+}
+
+static void
+weston_subsurface_commit_to_cache(struct weston_subsurface *sub)
+{
+ struct weston_surface *surface = sub->surface;
+
+ /*
+ * If this commit would cause the surface to move by the
+ * attach(dx, dy) parameters, the old damage region must be
+ * translated to correspond to the new surface coordinate system
+ * original_mode.
+ */
+ pixman_region32_translate(&sub->cached.damage,
+ -surface->pending.sx, -surface->pending.sy);
+ pixman_region32_union(&sub->cached.damage, &sub->cached.damage,
+ &surface->pending.damage);
+ empty_region(&surface->pending.damage);
+
+ if (surface->pending.newly_attached) {
+ sub->cached.newly_attached = 1;
+ weston_buffer_reference(&sub->cached.buffer_ref,
+ surface->pending.buffer);
+ }
+ sub->cached.sx += surface->pending.sx;
+ sub->cached.sy += surface->pending.sy;
+ surface->pending.sx = 0;
+ surface->pending.sy = 0;
+ surface->pending.newly_attached = 0;
+
+ sub->cached.buffer_transform = surface->pending.buffer_transform;
+ sub->cached.buffer_scale = surface->pending.buffer_scale;
+
+ pixman_region32_copy(&sub->cached.opaque, &surface->pending.opaque);
+
+ pixman_region32_copy(&sub->cached.input, &surface->pending.input);
+
+ wl_list_insert_list(&sub->cached.frame_callback_list,
+ &surface->pending.frame_callback_list);
+ wl_list_init(&surface->pending.frame_callback_list);
+
+ sub->cached.has_data = 1;
+}
+
+static int
+weston_subsurface_is_synchronized(struct weston_subsurface *sub)
+{
+ while (sub) {
+ if (sub->synchronized)
+ return 1;
+
+ if (!sub->parent)
+ return 0;
+
+ sub = weston_surface_to_subsurface(sub->parent);
+ }
+
+ return 0;
+}
+
+static void
+weston_subsurface_commit(struct weston_subsurface *sub)
+{
+ struct weston_surface *surface = sub->surface;
+ struct weston_subsurface *tmp;
+
+ /* Recursive check for effectively synchronized. */
+ if (weston_subsurface_is_synchronized(sub)) {
+ weston_subsurface_commit_to_cache(sub);
+ } else {
+ if (sub->cached.has_data) {
+ /* flush accumulated state from cache */
+ weston_subsurface_commit_to_cache(sub);
+ weston_subsurface_commit_from_cache(sub);
+ } else {
+ weston_surface_commit(surface);
+ }
+
+ wl_list_for_each(tmp, &surface->subsurface_list, parent_link) {
+ if (tmp->surface != surface)
+ weston_subsurface_parent_commit(tmp, 0);
+ }
+ }
+}
+
+static void
+weston_subsurface_synchronized_commit(struct weston_subsurface *sub)
+{
+ struct weston_surface *surface = sub->surface;
+ struct weston_subsurface *tmp;
+
+ /* From now on, commit_from_cache the whole sub-tree, regardless of
+ * the synchronized mode of each child. This sub-surface or some
+ * of its ancestors were synchronized, so we are synchronized
+ * all the way down.
+ */
+
+ if (sub->cached.has_data)
+ weston_subsurface_commit_from_cache(sub);
+
+ wl_list_for_each(tmp, &surface->subsurface_list, parent_link) {
+ if (tmp->surface != surface)
+ weston_subsurface_parent_commit(tmp, 1);
+ }
+}
+
+static void
+weston_subsurface_parent_commit(struct weston_subsurface *sub,
+ int parent_is_synchronized)
+{
+ if (sub->position.set) {
+ weston_surface_set_position(sub->surface,
+ sub->position.x, sub->position.y);
+ sub->position.set = 0;
+ }
+
+ if (parent_is_synchronized || sub->synchronized)
+ weston_subsurface_synchronized_commit(sub);
+}
+
+static void
+subsurface_configure(struct weston_surface *surface, int32_t dx, int32_t dy,
+ int32_t width, int32_t height)
+{
+ struct weston_compositor *compositor = surface->compositor;
+
+ weston_surface_configure(surface,
+ surface->geometry.x + dx,
+ surface->geometry.y + dy,
+ width, height);
+
+ /* No need to check parent mappedness, because if parent is not
+ * mapped, parent is not in a visible layer, so this sub-surface
+ * will not be drawn either.
+ */
+ if (!weston_surface_is_mapped(surface)) {
+ wl_list_init(&surface->layer_link);
+
+ /* Cannot call weston_surface_update_transform(),
+ * because that would call it also for the parent surface,
+ * which might not be mapped yet. That would lead to
+ * inconsistent state, where the window could never be
+ * mapped.
+ *
+ * Instead just assing any output, to make
+ * weston_surface_is_mapped() return true, so that when the
+ * parent surface does get mapped, this one will get
+ * included, too. See surface_list_add().
+ */
+ assert(!wl_list_empty(&compositor->output_list));
+ surface->output = container_of(compositor->output_list.next,
+ struct weston_output, link);
+ }
+}
+
+static struct weston_subsurface *
+weston_surface_to_subsurface(struct weston_surface *surface)
+{
+ if (surface->configure == subsurface_configure)
+ return surface->configure_private;
+
+ return NULL;
+}
+
+WL_EXPORT struct weston_surface *
+weston_surface_get_main_surface(struct weston_surface *surface)
+{
+ struct weston_subsurface *sub;
+
+ while (surface && (sub = weston_surface_to_subsurface(surface)))
+ surface = sub->parent;
+
+ return surface;
+}
+
+static void
+subsurface_set_position(struct wl_client *client,
+ struct wl_resource *resource, int32_t x, int32_t y)
+{
+ struct weston_subsurface *sub = wl_resource_get_user_data(resource);
+
+ if (!sub)
+ return;
+
+ sub->position.x = x;
+ sub->position.y = y;
+ sub->position.set = 1;
+}
+
+static struct weston_subsurface *
+subsurface_from_surface(struct weston_surface *surface)
+{
+ struct weston_subsurface *sub;
+
+ sub = weston_surface_to_subsurface(surface);
+ if (sub)
+ return sub;
+
+ wl_list_for_each(sub, &surface->subsurface_list, parent_link)
+ if (sub->surface == surface)
+ return sub;
+
+ return NULL;
+}
+
+static struct weston_subsurface *
+subsurface_sibling_check(struct weston_subsurface *sub,
+ struct weston_surface *surface,
+ const char *request)
+{
+ struct weston_subsurface *sibling;
+
+ sibling = subsurface_from_surface(surface);
+
+ if (!sibling) {
+ wl_resource_post_error(sub->resource,
+ WL_SUBSURFACE_ERROR_BAD_SURFACE,
+ "%s: wl_surface@%d is not a parent or sibling",
+ request, wl_resource_get_id(surface->resource));
+ return NULL;
+ }
+
+ if (sibling->parent != sub->parent) {
+ wl_resource_post_error(sub->resource,
+ WL_SUBSURFACE_ERROR_BAD_SURFACE,
+ "%s: wl_surface@%d has a different parent",
+ request, wl_resource_get_id(surface->resource));
+ return NULL;
+ }
+
+ return sibling;
+}
+
+static void
+subsurface_place_above(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *sibling_resource)
+{
+ struct weston_subsurface *sub = wl_resource_get_user_data(resource);
+ struct weston_surface *surface =
+ wl_resource_get_user_data(sibling_resource);
+ struct weston_subsurface *sibling;
+
+ if (!sub)
+ return;
+
+ sibling = subsurface_sibling_check(sub, surface, "place_above");
+ if (!sibling)
+ return;
+
+ wl_list_remove(&sub->parent_link_pending);
+ wl_list_insert(sibling->parent_link_pending.prev,
+ &sub->parent_link_pending);
+}
+
+static void
+subsurface_place_below(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *sibling_resource)
+{
+ struct weston_subsurface *sub = wl_resource_get_user_data(resource);
+ struct weston_surface *surface =
+ wl_resource_get_user_data(sibling_resource);
+ struct weston_subsurface *sibling;
+
+ if (!sub)
+ return;
+
+ sibling = subsurface_sibling_check(sub, surface, "place_below");
+ if (!sibling)
+ return;
+
+ wl_list_remove(&sub->parent_link_pending);
+ wl_list_insert(&sibling->parent_link_pending,
+ &sub->parent_link_pending);
+}
+
+static void
+subsurface_set_sync(struct wl_client *client, struct wl_resource *resource)
+{
+ struct weston_subsurface *sub = wl_resource_get_user_data(resource);
+
+ if (sub)
+ sub->synchronized = 1;
+}
+
+static void
+subsurface_set_desync(struct wl_client *client, struct wl_resource *resource)
+{
+ struct weston_subsurface *sub = wl_resource_get_user_data(resource);
+
+ if (sub && sub->synchronized) {
+ sub->synchronized = 0;
+
+ /* If sub became effectively desynchronized, flush. */
+ if (!weston_subsurface_is_synchronized(sub))
+ weston_subsurface_synchronized_commit(sub);
+ }
+}
+
+static void
+weston_subsurface_cache_init(struct weston_subsurface *sub)
+{
+ pixman_region32_init(&sub->cached.damage);
+ pixman_region32_init(&sub->cached.opaque);
+ pixman_region32_init(&sub->cached.input);
+ wl_list_init(&sub->cached.frame_callback_list);
+ sub->cached.buffer_ref.buffer = NULL;
+}
+
+static void
+weston_subsurface_cache_fini(struct weston_subsurface *sub)
+{
+ struct weston_frame_callback *cb, *tmp;
+
+ wl_list_for_each_safe(cb, tmp, &sub->cached.frame_callback_list, link)
+ wl_resource_destroy(cb->resource);
+
+ weston_buffer_reference(&sub->cached.buffer_ref, NULL);
+ pixman_region32_fini(&sub->cached.damage);
+ pixman_region32_fini(&sub->cached.opaque);
+ pixman_region32_fini(&sub->cached.input);
+}
+
+static void
+weston_subsurface_unlink_parent(struct weston_subsurface *sub)
+{
+ wl_list_remove(&sub->parent_link);
+ wl_list_remove(&sub->parent_link_pending);
+ wl_list_remove(&sub->parent_destroy_listener.link);
+ sub->parent = NULL;
+}
+
+static void
+weston_subsurface_destroy(struct weston_subsurface *sub);
+
+static void
+subsurface_handle_surface_destroy(struct wl_listener *listener, void *data)
+{
+ struct weston_subsurface *sub =
+ container_of(listener, struct weston_subsurface,
+ surface_destroy_listener);
+ assert(data == &sub->surface->resource);
+
+ /* The protocol object (wl_resource) is left inert. */
+ if (sub->resource)
+ wl_resource_set_user_data(sub->resource, NULL);
+
+ weston_subsurface_destroy(sub);
+}
+
+static void
+subsurface_handle_parent_destroy(struct wl_listener *listener, void *data)
+{
+ struct weston_subsurface *sub =
+ container_of(listener, struct weston_subsurface,
+ parent_destroy_listener);
+ assert(data == &sub->parent->resource);
+ assert(sub->surface != sub->parent);
+
+ if (weston_surface_is_mapped(sub->surface))
+ weston_surface_unmap(sub->surface);
+
+ weston_subsurface_unlink_parent(sub);
+}
+
+static void
+subsurface_resource_destroy(struct wl_resource *resource)
+{
+ struct weston_subsurface *sub = wl_resource_get_user_data(resource);
+
+ if (sub)
+ weston_subsurface_destroy(sub);
+}
+
+static void
+subsurface_destroy(struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static void
+weston_subsurface_link_parent(struct weston_subsurface *sub,
+ struct weston_surface *parent)
+{
+ sub->parent = parent;
+ sub->parent_destroy_listener.notify = subsurface_handle_parent_destroy;
+ wl_signal_add(&parent->destroy_signal,
+ &sub->parent_destroy_listener);
+
+ wl_list_insert(&parent->subsurface_list, &sub->parent_link);
+ wl_list_insert(&parent->subsurface_list_pending,
+ &sub->parent_link_pending);
+}
+
+static void
+weston_subsurface_link_surface(struct weston_subsurface *sub,
+ struct weston_surface *surface)
+{
+ sub->surface = surface;
+ sub->surface_destroy_listener.notify =
+ subsurface_handle_surface_destroy;
+ wl_signal_add(&surface->destroy_signal,
+ &sub->surface_destroy_listener);
+}
+
+static void
+weston_subsurface_destroy(struct weston_subsurface *sub)
+{
+ assert(sub->surface);
+
+ if (sub->resource) {
+ assert(weston_surface_to_subsurface(sub->surface) == sub);
+ assert(sub->parent_destroy_listener.notify ==
+ subsurface_handle_parent_destroy);
+
+ weston_surface_set_transform_parent(sub->surface, NULL);
+ if (sub->parent)
+ weston_subsurface_unlink_parent(sub);
+
+ weston_subsurface_cache_fini(sub);
+
+ sub->surface->configure = NULL;
+ sub->surface->configure_private = NULL;
+ } else {
+ /* the dummy weston_subsurface for the parent itself */
+ assert(sub->parent_destroy_listener.notify == NULL);
+ wl_list_remove(&sub->parent_link);
+ wl_list_remove(&sub->parent_link_pending);
+ }
+
+ wl_list_remove(&sub->surface_destroy_listener.link);
+ free(sub);
+}
+
+static const struct wl_subsurface_interface subsurface_implementation = {
+ subsurface_destroy,
+ subsurface_set_position,
+ subsurface_place_above,
+ subsurface_place_below,
+ subsurface_set_sync,
+ subsurface_set_desync
+};
+
+static struct weston_subsurface *
+weston_subsurface_create(uint32_t id, struct weston_surface *surface,
+ struct weston_surface *parent)
+{
+ struct weston_subsurface *sub;
+ struct wl_client *client = wl_resource_get_client(surface->resource);
+
+ sub = calloc(1, sizeof *sub);
+ if (!sub)
+ return NULL;
+
+ sub->resource =
+ wl_resource_create(client, &wl_subsurface_interface, 1, id);
+ if (!sub->resource) {
+ free(sub);
+ return NULL;
+ }
+
+ wl_resource_set_implementation(sub->resource,
+ &subsurface_implementation,
+ sub, subsurface_resource_destroy);
+ weston_subsurface_link_surface(sub, surface);
+ weston_subsurface_link_parent(sub, parent);
+ weston_subsurface_cache_init(sub);
+ sub->synchronized = 1;
+ weston_surface_set_transform_parent(surface, parent);
+
+ return sub;
+}
+
+/* Create a dummy subsurface for having the parent itself in its
+ * sub-surface lists. Makes stacking order manipulation easy.
+ */
+static struct weston_subsurface *
+weston_subsurface_create_for_parent(struct weston_surface *parent)
+{
+ struct weston_subsurface *sub;
+
+ sub = calloc(1, sizeof *sub);
+ if (!sub)
+ return NULL;
+
+ weston_subsurface_link_surface(sub, parent);
+ sub->parent = parent;
+ wl_list_insert(&parent->subsurface_list, &sub->parent_link);
+ wl_list_insert(&parent->subsurface_list_pending,
+ &sub->parent_link_pending);
+
+ return sub;
+}
+
+static void
+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 weston_surface *surface =
+ wl_resource_get_user_data(surface_resource);
+ struct weston_surface *parent =
+ wl_resource_get_user_data(parent_resource);
+ struct weston_subsurface *sub;
+ static const char where[] = "get_subsurface: wl_subsurface@";
+
+ if (surface == parent) {
+ wl_resource_post_error(resource,
+ WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE,
+ "%s%d: wl_surface@%d cannot be its own parent",
+ where, id, wl_resource_get_id(surface_resource));
+ return;
+ }
+
+ if (weston_surface_to_subsurface(surface)) {
+ wl_resource_post_error(resource,
+ WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE,
+ "%s%d: wl_surface@%d is already a sub-surface",
+ where, id, wl_resource_get_id(surface_resource));
+ return;
+ }
+
+ if (surface->configure) {
+ wl_resource_post_error(resource,
+ WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE,
+ "%s%d: wl_surface@%d already has a role",
+ where, id, wl_resource_get_id(surface_resource));
+ return;
+ }
+
+ if (weston_surface_get_main_surface(parent) == surface) {
+ wl_resource_post_error(resource,
+ WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE,
+ "%s%d: wl_surface@%d is an ancestor of parent",
+ where, id, wl_resource_get_id(surface_resource));
+ return;
+ }
+
+ /* make sure the parent is in its own list */
+ if (wl_list_empty(&parent->subsurface_list)) {
+ if (!weston_subsurface_create_for_parent(parent)) {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+ }
+
+ sub = weston_subsurface_create(id, surface, parent);
+ if (!sub) {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ surface->configure = subsurface_configure;
+ surface->configure_private = sub;
+}
+
+static void
+subcompositor_destroy(struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static const struct wl_subcompositor_interface subcompositor_interface = {
+ subcompositor_destroy,
+ subcompositor_get_subsurface
+};
+
+static void
+bind_subcompositor(struct wl_client *client,
+ void *data, uint32_t version, uint32_t id)
+{
+ struct weston_compositor *compositor = data;
+ struct wl_resource *resource;
+
+ resource =
+ wl_resource_create(client, &wl_subcompositor_interface, 1, id);
+ if (resource == NULL) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+ wl_resource_set_implementation(resource, &subcompositor_interface,
+ compositor, NULL);
+}
+
+static void
+weston_compositor_dpms(struct weston_compositor *compositor,
+ enum dpms_enum state)
+{
+ struct weston_output *output;
+
+ wl_list_for_each(output, &compositor->output_list, link)
+ if (output->set_dpms)
+ output->set_dpms(output, state);
+}
+
+WL_EXPORT void
+weston_compositor_wake(struct weston_compositor *compositor)
+{
+ uint32_t old_state = compositor->state;
+
+ /* The state needs to be changed before emitting the wake
+ * signal because that may try to schedule a repaint which
+ * will not work if the compositor is still sleeping */
+ compositor->state = WESTON_COMPOSITOR_ACTIVE;
+
+ switch (old_state) {
+ case WESTON_COMPOSITOR_SLEEPING:
+ weston_compositor_dpms(compositor, WESTON_DPMS_ON);
+ /* fall through */
+ case WESTON_COMPOSITOR_IDLE:
+ case WESTON_COMPOSITOR_OFFSCREEN:
+ wl_signal_emit(&compositor->wake_signal, compositor);
+ /* fall through */
+ default:
+ wl_event_source_timer_update(compositor->idle_source,
+ compositor->idle_time * 1000);
+ }
+}
+
+WL_EXPORT void
+weston_compositor_offscreen(struct weston_compositor *compositor)
+{
+ switch (compositor->state) {
+ case WESTON_COMPOSITOR_OFFSCREEN:
+ return;
+ case WESTON_COMPOSITOR_SLEEPING:
+ weston_compositor_dpms(compositor, WESTON_DPMS_ON);
+ /* fall through */
+ default:
+ compositor->state = WESTON_COMPOSITOR_OFFSCREEN;
+ wl_event_source_timer_update(compositor->idle_source, 0);
+ }
+}
+
+WL_EXPORT void
+weston_compositor_sleep(struct weston_compositor *compositor)
+{
+ if (compositor->state == WESTON_COMPOSITOR_SLEEPING)
+ return;
+
+ wl_event_source_timer_update(compositor->idle_source, 0);
+ compositor->state = WESTON_COMPOSITOR_SLEEPING;
+ weston_compositor_dpms(compositor, WESTON_DPMS_OFF);
+}
+
+static int
+idle_handler(void *data)
+{
+ struct weston_compositor *compositor = data;
+
+ if (compositor->idle_inhibit)
+ return 1;
+
+ compositor->state = WESTON_COMPOSITOR_IDLE;
+ wl_signal_emit(&compositor->idle_signal, compositor);
+
+ return 1;
+}
+
+WL_EXPORT void
+weston_plane_init(struct weston_plane *plane,
+ struct weston_compositor *ec,
+ int32_t x, int32_t y)
+{
+ pixman_region32_init(&plane->damage);
+ pixman_region32_init(&plane->clip);
+ plane->x = x;
+ plane->y = y;
+ plane->compositor = ec;
+
+ /* Init the link so that the call to wl_list_remove() when releasing
+ * the plane without ever stacking doesn't lead to a crash */
+ wl_list_init(&plane->link);
+}
+
+WL_EXPORT void
+weston_plane_release(struct weston_plane *plane)
+{
+ struct weston_surface *surface;
+
+ pixman_region32_fini(&plane->damage);
+ pixman_region32_fini(&plane->clip);
+
+ wl_list_for_each(surface, &plane->compositor->surface_list, link) {
+ if (surface->plane == plane)
+ surface->plane = NULL;
+ }
+
+ wl_list_remove(&plane->link);
+}
+
+WL_EXPORT void
+weston_compositor_stack_plane(struct weston_compositor *ec,
+ struct weston_plane *plane,
+ struct weston_plane *above)
+{
+ if (above)
+ wl_list_insert(above->link.prev, &plane->link);
+ else
+ wl_list_insert(&ec->plane_list, &plane->link);
+}
+
+static void unbind_resource(struct wl_resource *resource)
+{
+ wl_list_remove(wl_resource_get_link(resource));
+}
+
+static void
+bind_output(struct wl_client *client,
+ void *data, uint32_t version, uint32_t id)
+{
+ struct weston_output *output = data;
+ struct weston_mode *mode;
+ struct wl_resource *resource;
+
+ resource = wl_resource_create(client, &wl_output_interface,
+ MIN(version, 2), id);
+ if (resource == NULL) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+
+ wl_list_insert(&output->resource_list, wl_resource_get_link(resource));
+ wl_resource_set_implementation(resource, NULL, data, unbind_resource);
+
+ wl_output_send_geometry(resource,
+ output->x,
+ output->y,
+ output->mm_width,
+ output->mm_height,
+ output->subpixel,
+ output->make, output->model,
+ output->transform);
+ if (version >= 2)
+ wl_output_send_scale(resource,
+ output->current_scale);
+
+ wl_list_for_each (mode, &output->mode_list, link) {
+ wl_output_send_mode(resource,
+ mode->flags,
+ mode->width,
+ mode->height,
+ mode->refresh);
+ }
+
+ if (version >= 2)
+ wl_output_send_done(resource);
+}
+
+WL_EXPORT void
+weston_output_destroy(struct weston_output *output)
+{
+ wl_signal_emit(&output->destroy_signal, output);
+
+ free(output->name);
+ pixman_region32_fini(&output->region);
+ pixman_region32_fini(&output->previous_damage);
+ output->compositor->output_id_pool &= ~(1 << output->id);
+
+ wl_global_destroy(output->global);
+}
+
+static void
+weston_output_compute_transform(struct weston_output *output)
+{
+ struct weston_matrix transform;
+ int flip;
+
+ weston_matrix_init(&transform);
+ transform.type = WESTON_MATRIX_TRANSFORM_ROTATE;
+
+ switch(output->transform) {
+ case WL_OUTPUT_TRANSFORM_FLIPPED:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+ transform.type |= WESTON_MATRIX_TRANSFORM_OTHER;
+ flip = -1;
+ break;
+ default:
+ flip = 1;
+ break;
+ }
+
+ switch(output->transform) {
+ case WL_OUTPUT_TRANSFORM_NORMAL:
+ case WL_OUTPUT_TRANSFORM_FLIPPED:
+ transform.d[0] = flip;
+ transform.d[1] = 0;
+ transform.d[4] = 0;
+ transform.d[5] = 1;
+ break;
+ case WL_OUTPUT_TRANSFORM_90:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+ transform.d[0] = 0;
+ transform.d[1] = -flip;
+ transform.d[4] = 1;
+ transform.d[5] = 0;
+ break;
+ case WL_OUTPUT_TRANSFORM_180:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+ transform.d[0] = -flip;
+ transform.d[1] = 0;
+ transform.d[4] = 0;
+ transform.d[5] = -1;
+ break;
+ case WL_OUTPUT_TRANSFORM_270:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+ transform.d[0] = 0;
+ transform.d[1] = flip;
+ transform.d[4] = -1;
+ transform.d[5] = 0;
+ break;
+ default:
+ break;
+ }
+
+ weston_matrix_multiply(&output->matrix, &transform);
+}
+
+WL_EXPORT void
+weston_output_update_matrix(struct weston_output *output)
+{
+ float magnification;
+ struct weston_matrix camera;
+ struct weston_matrix modelview;
+
+ weston_matrix_init(&output->matrix);
+ weston_matrix_translate(&output->matrix,
+ -(output->x + (output->border.right + output->width - output->border.left) / 2.0),
+ -(output->y + (output->border.bottom + output->height - output->border.top) / 2.0), 0);
+
+ weston_matrix_scale(&output->matrix,
+ 2.0 / (output->width + output->border.left + output->border.right),
+ -2.0 / (output->height + output->border.top + output->border.bottom), 1);
+
+ weston_output_compute_transform(output);
+
+ if (output->zoom.active) {
+ magnification = 1 / (1 - output->zoom.spring_z.current);
+ weston_matrix_init(&camera);
+ weston_matrix_init(&modelview);
+ weston_output_update_zoom(output, output->zoom.type);
+ weston_matrix_translate(&camera, output->zoom.trans_x,
+ -output->zoom.trans_y, 0);
+ weston_matrix_invert(&modelview, &camera);
+ weston_matrix_scale(&modelview, magnification, magnification, 1.0);
+ weston_matrix_multiply(&output->matrix, &modelview);
+ }
+
+ output->dirty = 0;
+}
+
+static void
+weston_output_transform_scale_init(struct weston_output *output, uint32_t transform, uint32_t scale)
+{
+ output->transform = transform;
+
+ switch (transform) {
+ case WL_OUTPUT_TRANSFORM_90:
+ case WL_OUTPUT_TRANSFORM_270:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+ /* Swap width and height */
+ output->width = output->current_mode->height;
+ output->height = output->current_mode->width;
+ break;
+ case WL_OUTPUT_TRANSFORM_NORMAL:
+ case WL_OUTPUT_TRANSFORM_180:
+ case WL_OUTPUT_TRANSFORM_FLIPPED:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+ output->width = output->current_mode->width;
+ output->height = output->current_mode->height;
+ break;
+ default:
+ break;
+ }
+
+ output->native_scale = output->current_scale = scale;
+ output->width /= scale;
+ output->height /= scale;
+}
+
+WL_EXPORT void
+weston_output_move(struct weston_output *output, int x, int y)
+{
+ output->x = x;
+ output->y = y;
+
+ pixman_region32_init(&output->previous_damage);
+ pixman_region32_init_rect(&output->region, x, y,
+ output->width,
+ output->height);
+}
+
+WL_EXPORT void
+weston_output_init(struct weston_output *output, struct weston_compositor *c,
+ int x, int y, int mm_width, int mm_height, uint32_t transform,
+ int32_t scale)
+{
+ output->compositor = c;
+ output->x = x;
+ output->y = y;
+ output->border.top = 0;
+ output->border.bottom = 0;
+ output->border.left = 0;
+ output->border.right = 0;
+ output->mm_width = mm_width;
+ output->mm_height = mm_height;
+ output->dirty = 1;
+ output->original_scale = scale;
+
+ weston_output_transform_scale_init(output, transform, scale);
+ weston_output_init_zoom(output);
+
+ weston_output_move(output, x, y);
+ weston_output_damage(output);
+
+ wl_signal_init(&output->frame_signal);
+ wl_signal_init(&output->destroy_signal);
+ wl_list_init(&output->animation_list);
+ wl_list_init(&output->resource_list);
+
+ output->id = ffs(~output->compositor->output_id_pool) - 1;
+ output->compositor->output_id_pool |= 1 << output->id;
+
+ output->global =
+ wl_global_create(c->wl_display, &wl_output_interface, 2,
+ output, bind_output);
+ wl_signal_emit(&c->output_created_signal, output);
+}
+
+WL_EXPORT void
+weston_output_transform_coordinate(struct weston_output *output,
+ int device_x, int device_y,
+ wl_fixed_t *x, wl_fixed_t *y)
+{
+ wl_fixed_t tx, ty;
+ wl_fixed_t width, height;
+
+ width = wl_fixed_from_int(output->width * output->current_scale - 1);
+ height = wl_fixed_from_int(output->height * output->current_scale - 1);
+
+ switch(output->transform) {
+ case WL_OUTPUT_TRANSFORM_NORMAL:
+ default:
+ tx = wl_fixed_from_int(device_x);
+ ty = wl_fixed_from_int(device_y);
+ break;
+ case WL_OUTPUT_TRANSFORM_90:
+ tx = wl_fixed_from_int(device_y);
+ ty = height - wl_fixed_from_int(device_x);
+ break;
+ case WL_OUTPUT_TRANSFORM_180:
+ tx = width - wl_fixed_from_int(device_x);
+ ty = height - wl_fixed_from_int(device_y);
+ break;
+ case WL_OUTPUT_TRANSFORM_270:
+ tx = width - wl_fixed_from_int(device_y);
+ ty = wl_fixed_from_int(device_x);
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED:
+ tx = width - wl_fixed_from_int(device_x);
+ ty = wl_fixed_from_int(device_y);
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+ tx = width - wl_fixed_from_int(device_y);
+ ty = height - wl_fixed_from_int(device_x);
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+ tx = wl_fixed_from_int(device_x);
+ ty = height - wl_fixed_from_int(device_y);
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+ tx = wl_fixed_from_int(device_y);
+ ty = wl_fixed_from_int(device_x);
+ break;
+ }
+
+ *x = tx / output->current_scale + wl_fixed_from_int(output->x);
+ *y = ty / output->current_scale + wl_fixed_from_int(output->y);
+}
+
+static void
+compositor_bind(struct wl_client *client,
+ void *data, uint32_t version, uint32_t id)
+{
+ struct weston_compositor *compositor = data;
+ struct wl_resource *resource;
+
+ resource = wl_resource_create(client, &wl_compositor_interface,
+ MIN(version, 3), id);
+ if (resource == NULL) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+
+ wl_resource_set_implementation(resource, &compositor_interface,
+ compositor, NULL);
+}
+
+static void
+log_uname(void)
+{
+ struct utsname usys;
+
+ uname(&usys);
+
+ weston_log("OS: %s, %s, %s, %s\n", usys.sysname, usys.release,
+ usys.version, usys.machine);
+}
+
+WL_EXPORT int
+weston_environment_get_fd(const char *env)
+{
+ char *e, *end;
+ int fd, flags;
+
+ e = getenv(env);
+ if (!e)
+ return -1;
+ fd = strtol(e, &end, 0);
+ if (*end != '\0')
+ return -1;
+
+ flags = fcntl(fd, F_GETFD);
+ if (flags == -1)
+ return -1;
+
+ fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
+ unsetenv(env);
+
+ return fd;
+}
+
+WL_EXPORT int
+weston_compositor_init(struct weston_compositor *ec,
+ struct wl_display *display,
+ int *argc, char *argv[],
+ struct weston_config *config)
+{
+ struct wl_event_loop *loop;
+ struct xkb_rule_names xkb_names;
+ struct weston_config_section *s;
+
+ ec->config = config;
+ ec->wl_display = display;
+ wl_signal_init(&ec->destroy_signal);
+ wl_signal_init(&ec->activate_signal);
+ wl_signal_init(&ec->transform_signal);
+ wl_signal_init(&ec->kill_signal);
+ wl_signal_init(&ec->idle_signal);
+ wl_signal_init(&ec->wake_signal);
+ wl_signal_init(&ec->show_input_panel_signal);
+ wl_signal_init(&ec->hide_input_panel_signal);
+ wl_signal_init(&ec->update_input_panel_signal);
+ wl_signal_init(&ec->seat_created_signal);
+ wl_signal_init(&ec->output_created_signal);
+ wl_signal_init(&ec->session_signal);
+ ec->session_active = 1;
+
+ ec->output_id_pool = 0;
+
+ if (!wl_global_create(display, &wl_compositor_interface, 3,
+ ec, compositor_bind))
+ return -1;
+
+ if (!wl_global_create(display, &wl_subcompositor_interface, 1,
+ ec, bind_subcompositor))
+ return -1;
+
+ wl_list_init(&ec->surface_list);
+ wl_list_init(&ec->plane_list);
+ wl_list_init(&ec->layer_list);
+ wl_list_init(&ec->seat_list);
+ wl_list_init(&ec->output_list);
+ wl_list_init(&ec->key_binding_list);
+ wl_list_init(&ec->button_binding_list);
+ wl_list_init(&ec->touch_binding_list);
+ wl_list_init(&ec->axis_binding_list);
+ wl_list_init(&ec->debug_binding_list);
+
+ weston_plane_init(&ec->primary_plane, ec, 0, 0);
+ weston_compositor_stack_plane(ec, &ec->primary_plane, NULL);
+
+ s = weston_config_get_section(ec->config, "keyboard", NULL, NULL);
+ weston_config_section_get_string(s, "keymap_rules",
+ (char **) &xkb_names.rules, NULL);
+ weston_config_section_get_string(s, "keymap_model",
+ (char **) &xkb_names.model, NULL);
+ weston_config_section_get_string(s, "keymap_layout",
+ (char **) &xkb_names.layout, NULL);
+ weston_config_section_get_string(s, "keymap_variant",
+ (char **) &xkb_names.variant, NULL);
+ weston_config_section_get_string(s, "keymap_options",
+ (char **) &xkb_names.options, NULL);
+
+ if (weston_compositor_xkb_init(ec, &xkb_names) < 0)
+ return -1;
+
+ ec->ping_handler = NULL;
+
+ screenshooter_create(ec);
+ text_cursor_position_notifier_create(ec);
+ text_backend_init(ec);
+
+ wl_data_device_manager_init(ec->wl_display);
+
+ wl_display_init_shm(display);
+
+ loop = wl_display_get_event_loop(ec->wl_display);
+ ec->idle_source = wl_event_loop_add_timer(loop, idle_handler, ec);
+ wl_event_source_timer_update(ec->idle_source, ec->idle_time * 1000);
+
+ ec->input_loop = wl_event_loop_create();
+
+ weston_layer_init(&ec->fade_layer, &ec->layer_list);
+ weston_layer_init(&ec->cursor_layer, &ec->fade_layer.link);
+
+ weston_compositor_schedule_repaint(ec);
+
+ return 0;
+}
+
+WL_EXPORT void
+weston_compositor_shutdown(struct weston_compositor *ec)
+{
+ struct weston_output *output, *next;
+
+ wl_event_source_remove(ec->idle_source);
+ if (ec->input_loop_source)
+ wl_event_source_remove(ec->input_loop_source);
+
+ /* Destroy all outputs associated with this compositor */
+ wl_list_for_each_safe(output, next, &ec->output_list, link)
+ output->destroy(output);
+
+ weston_binding_list_destroy_all(&ec->key_binding_list);
+ weston_binding_list_destroy_all(&ec->button_binding_list);
+ weston_binding_list_destroy_all(&ec->touch_binding_list);
+ weston_binding_list_destroy_all(&ec->axis_binding_list);
+ weston_binding_list_destroy_all(&ec->debug_binding_list);
+
+ weston_plane_release(&ec->primary_plane);
+
+ wl_event_loop_destroy(ec->input_loop);
+
+ weston_config_destroy(ec->config);
+}
+
+WL_EXPORT void
+weston_version(int *major, int *minor, int *micro)
+{
+ *major = WESTON_VERSION_MAJOR;
+ *minor = WESTON_VERSION_MINOR;
+ *micro = WESTON_VERSION_MICRO;
+}
+
+static const struct {
+ uint32_t bit; /* enum weston_capability */
+ const char *desc;
+} capability_strings[] = {
+ { WESTON_CAP_ROTATION_ANY, "arbitrary surface rotation:" },
+ { WESTON_CAP_CAPTURE_YFLIP, "screen capture uses y-flip:" },
+};
+
+static void
+weston_compositor_log_capabilities(struct weston_compositor *compositor)
+{
+ unsigned i;
+ int yes;
+
+ weston_log("Compositor capabilities:\n");
+ for (i = 0; i < ARRAY_LENGTH(capability_strings); i++) {
+ yes = compositor->capabilities & capability_strings[i].bit;
+ weston_log_continue(STAMP_SPACE "%s %s\n",
+ capability_strings[i].desc,
+ yes ? "yes" : "no");
+ }
+}
+
+static int on_term_signal(int signal_number, void *data)
+{
+ struct wl_display *display = data;
+
+ weston_log("caught signal %d\n", signal_number);
+ wl_display_terminate(display);
+
+ return 1;
+}
+
+#ifdef HAVE_LIBUNWIND
+
+static void
+print_backtrace(void)
+{
+ unw_cursor_t cursor;
+ unw_context_t context;
+ unw_word_t off;
+ unw_proc_info_t pip;
+ int ret, i = 0;
+ char procname[256];
+ const char *filename;
+ Dl_info dlinfo;
+
+ pip.unwind_info = NULL;
+ ret = unw_getcontext(&context);
+ if (ret) {
+ weston_log("unw_getcontext: %d\n", ret);
+ return;
+ }
+
+ ret = unw_init_local(&cursor, &context);
+ if (ret) {
+ weston_log("unw_init_local: %d\n", ret);
+ return;
+ }
+
+ ret = unw_step(&cursor);
+ while (ret > 0) {
+ ret = unw_get_proc_info(&cursor, &pip);
+ if (ret) {
+ weston_log("unw_get_proc_info: %d\n", ret);
+ break;
+ }
+
+ ret = unw_get_proc_name(&cursor, procname, 256, &off);
+ if (ret && ret != -UNW_ENOMEM) {
+ if (ret != -UNW_EUNSPEC)
+ weston_log("unw_get_proc_name: %d\n", ret);
+ procname[0] = '?';
+ procname[1] = 0;
+ }
+
+ if (dladdr((void *)(pip.start_ip + off), &dlinfo) && dlinfo.dli_fname &&
+ *dlinfo.dli_fname)
+ filename = dlinfo.dli_fname;
+ else
+ filename = "?";
+
+ weston_log("%u: %s (%s%s+0x%x) [%p]\n", i++, filename, procname,
+ ret == -UNW_ENOMEM ? "..." : "", (int)off, (void *)(pip.start_ip + off));
+
+ ret = unw_step(&cursor);
+ if (ret < 0)
+ weston_log("unw_step: %d\n", ret);
+ }
+}
+
+#else
+
+static void
+print_backtrace(void)
+{
+ void *buffer[32];
+ int i, count;
+ Dl_info info;
+
+ count = backtrace(buffer, ARRAY_LENGTH(buffer));
+ for (i = 0; i < count; i++) {
+ dladdr(buffer[i], &info);
+ weston_log(" [%016lx] %s (%s)\n",
+ (long) buffer[i],
+ info.dli_sname ? info.dli_sname : "--",
+ info.dli_fname);
+ }
+}
+
+#endif
+
+static void
+on_caught_signal(int s, siginfo_t *siginfo, void *context)
+{
+ /* This signal handler will do a best-effort backtrace, and
+ * then call the backend restore function, which will switch
+ * back to the vt we launched from or ungrab X etc and then
+ * raise SIGTRAP. If we run weston under gdb from X or a
+ * different vt, and tell gdb "handle *s* nostop", this
+ * will allow weston to switch back to gdb on crash and then
+ * gdb will catch the crash with SIGTRAP.*/
+
+ weston_log("caught signal: %d\n", s);
+
+ print_backtrace();
+
+ segv_compositor->restore(segv_compositor);
+
+ raise(SIGTRAP);
+}
+
+static void *
+load_module(const char *name, const char *entrypoint)
+{
+ char path[PATH_MAX];
+ void *module, *init;
+
+ if (name[0] != '/')
+ snprintf(path, sizeof path, "%s/%s", MODULEDIR, name);
+ else
+ snprintf(path, sizeof path, "%s", name);
+
+ module = dlopen(path, RTLD_NOW | RTLD_NOLOAD);
+ if (module) {
+ weston_log("Module '%s' already loaded\n", path);
+ dlclose(module);
+ return NULL;
+ }
+
+ weston_log("Loading module '%s'\n", path);
+ module = dlopen(path, RTLD_NOW);
+ if (!module) {
+ weston_log("Failed to load module: %s\n", dlerror());
+ return NULL;
+ }
+
+ init = dlsym(module, entrypoint);
+ if (!init) {
+ weston_log("Failed to lookup init function: %s\n", dlerror());
+ dlclose(module);
+ return NULL;
+ }
+
+ return init;
+}
+
+static int
+load_modules(struct weston_compositor *ec, const char *modules,
+ int *argc, char *argv[])
+{
+ const char *p, *end;
+ char buffer[256];
+ int (*module_init)(struct weston_compositor *ec,
+ int *argc, char *argv[]);
+
+ if (modules == NULL)
+ return 0;
+
+ p = modules;
+ while (*p) {
+ end = strchrnul(p, ',');
+ snprintf(buffer, sizeof buffer, "%.*s", (int) (end - p), p);
+ module_init = load_module(buffer, "module_init");
+ if (module_init)
+ module_init(ec, argc, argv);
+ p = end;
+ while (*p == ',')
+ p++;
+
+ }
+
+ return 0;
+}
+
+static const char xdg_error_message[] =
+ "fatal: environment variable XDG_RUNTIME_DIR is not set.\n";
+
+static const char xdg_wrong_message[] =
+ "fatal: environment variable XDG_RUNTIME_DIR\n"
+ "is set to \"%s\", which is not a directory.\n";
+
+static const char xdg_wrong_mode_message[] =
+ "warning: XDG_RUNTIME_DIR \"%s\" is not configured\n"
+ "correctly. Unix access mode must be 0700 but is %o,\n"
+ "and XDG_RUNTIME_DIR must be owned by the user, but is\n"
+ "owned by UID %d.\n";
+
+static const char xdg_detail_message[] =
+ "Refer to your distribution on how to get it, or\n"
+ "http://www.freedesktop.org/wiki/Specifications/basedir-spec\n"
+ "on how to implement it.\n";
+
+static void
+verify_xdg_runtime_dir(void)
+{
+ char *dir = getenv("XDG_RUNTIME_DIR");
+ struct stat s;
+
+ if (!dir) {
+ weston_log(xdg_error_message);
+ weston_log_continue(xdg_detail_message);
+ exit(EXIT_FAILURE);
+ }
+
+ if (stat(dir, &s) || !S_ISDIR(s.st_mode)) {
+ weston_log(xdg_wrong_message, dir);
+ weston_log_continue(xdg_detail_message);
+ exit(EXIT_FAILURE);
+ }
+
+ if ((s.st_mode & 0777) != 0700 || s.st_uid != getuid()) {
+ weston_log(xdg_wrong_mode_message,
+ dir, s.st_mode & 0777, s.st_uid);
+ weston_log_continue(xdg_detail_message);
+ }
+}
+
+static int
+usage(int error_code)
+{
+ fprintf(stderr,
+ "Usage: weston [OPTIONS]\n\n"
+ "This is weston version " VERSION ", the Wayland reference compositor.\n"
+ "Weston supports multiple backends, and depending on which backend is in use\n"
+ "different options will be accepted.\n\n"
+
+
+ "Core options:\n\n"
+ " --version\t\tPrint weston version\n"
+ " -B, --backend=MODULE\tBackend module, one of drm-backend.so,\n"
+ "\t\t\t\tfbdev-backend.so, x11-backend.so or\n"
+ "\t\t\t\twayland-backend.so\n"
+ " --shell=MODULE\tShell module, defaults to desktop-shell.so\n"
+ " -S, --socket=NAME\tName of socket to listen on\n"
+ " -i, --idle-time=SECS\tIdle time in seconds\n"
+ " --modules\t\tLoad the comma-separated list of modules\n"
+ " --log==FILE\t\tLog to the given file\n"
+ " -h, --help\t\tThis help message\n\n");
+
+ fprintf(stderr,
+ "Options for drm-backend.so:\n\n"
+ " --connector=ID\tBring up only this connector\n"
+ " --seat=SEAT\t\tThe seat that weston should run on\n"
+ " --tty=TTY\t\tThe tty to use\n"
+ " --use-pixman\t\tUse the pixman (CPU) renderer\n"
+ " --current-mode\tPrefer current KMS mode over EDID preferred mode\n\n");
+
+ fprintf(stderr,
+ "Options for fbdev-backend.so:\n\n"
+ " --tty=TTY\t\tThe tty to use\n"
+ " --device=DEVICE\tThe framebuffer device to use\n\n");
+
+ fprintf(stderr,
+ "Options for x11-backend.so:\n\n"
+ " --width=WIDTH\t\tWidth of X window\n"
+ " --height=HEIGHT\tHeight of X window\n"
+ " --fullscreen\t\tRun in fullscreen mode\n"
+ " --use-pixman\t\tUse the pixman (CPU) renderer\n"
+ " --output-count=COUNT\tCreate multiple outputs\n"
+ " --no-input\t\tDont create input devices\n\n");
+
+ fprintf(stderr,
+ "Options for wayland-backend.so:\n\n"
+ " --width=WIDTH\t\tWidth of Wayland surface\n"
+ " --height=HEIGHT\tHeight of Wayland surface\n"
+ " --display=DISPLAY\tWayland display to connect to\n\n");
+
+#if defined(BUILD_RPI_COMPOSITOR) && defined(HAVE_BCM_HOST)
+ fprintf(stderr,
+ "Options for rpi-backend.so:\n\n"
+ " --tty=TTY\t\tThe tty to use\n"
+ " --single-buffer\tUse single-buffered Dispmanx elements.\n"
+ " --transform=TR\tThe output transformation, TR is one of:\n"
+ "\tnormal 90 180 270 flipped flipped-90 flipped-180 flipped-270\n"
+ "\n");
+#endif
+
+#if defined(BUILD_RDP_COMPOSITOR)
+ fprintf(stderr,
+ "Options for rdp-backend.so:\n\n"
+ " --width=WIDTH\t\tWidth of desktop\n"
+ " --height=HEIGHT\tHeight of desktop\n"
+ " --extra-modes=MODES\t\n"
+ " --env-socket=SOCKET\tUse that socket as peer connection\n"
+ " --address=ADDR\tThe address to bind\n"
+ " --port=PORT\tThe port to listen on\n"
+ " --rdp4-key=FILE\tThe file containing the key for RDP4 encryption\n"
+ " --rdp-tls-cert=FILE\tThe file containing the certificate for TLS encryption\n"
+ " --rdp-tls-key=FILE\tThe file containing the private key for TLS encryption\n"
+ "\n");
+#endif
+
+ exit(error_code);
+}
+
+static void
+catch_signals(void)
+{
+ struct sigaction action;
+
+ action.sa_flags = SA_SIGINFO | SA_RESETHAND;
+ action.sa_sigaction = on_caught_signal;
+ sigemptyset(&action.sa_mask);
+ sigaction(SIGSEGV, &action, NULL);
+ sigaction(SIGABRT, &action, NULL);
+}
+
+int main(int argc, char *argv[])
+{
+ int ret = EXIT_SUCCESS;
+ struct wl_display *display;
+ struct weston_compositor *ec;
+ struct wl_event_source *signals[4];
+ struct wl_event_loop *loop;
+ struct weston_compositor
+ *(*backend_init)(struct wl_display *display,
+ int *argc, char *argv[],
+ struct weston_config *config);
+ int i;
+ char *backend = NULL;
+ char *shell = NULL;
+ char *modules, *option_modules = NULL;
+ char *log = NULL;
+ int32_t idle_time = 300;
+ int32_t help = 0;
+ char *socket_name = "wayland-0";
+ int32_t version = 0;
+ struct weston_config *config;
+ struct weston_config_section *section;
+
+ const struct weston_option core_options[] = {
+ { WESTON_OPTION_STRING, "backend", 'B', &backend },
+ { WESTON_OPTION_STRING, "shell", 0, &shell },
+ { WESTON_OPTION_STRING, "socket", 'S', &socket_name },
+ { WESTON_OPTION_INTEGER, "idle-time", 'i', &idle_time },
+ { WESTON_OPTION_STRING, "modules", 0, &option_modules },
+ { WESTON_OPTION_STRING, "log", 0, &log },
+ { WESTON_OPTION_BOOLEAN, "help", 'h', &help },
+ { WESTON_OPTION_BOOLEAN, "version", 0, &version },
+ };
+
+ parse_options(core_options, ARRAY_LENGTH(core_options), &argc, argv);
+
+ if (help)
+ usage(EXIT_SUCCESS);
+
+ if (version) {
+ printf(PACKAGE_STRING "\n");
+ return EXIT_SUCCESS;
+ }
+
+ weston_log_file_open(log);
+
+ weston_log("%s\n"
+ STAMP_SPACE "%s\n"
+ STAMP_SPACE "Bug reports to: %s\n"
+ STAMP_SPACE "Build: %s\n",
+ PACKAGE_STRING, PACKAGE_URL, PACKAGE_BUGREPORT,
+ BUILD_ID);
+ log_uname();
+
+ verify_xdg_runtime_dir();
+
+ display = wl_display_create();
+
+ loop = wl_display_get_event_loop(display);
+ signals[0] = wl_event_loop_add_signal(loop, SIGTERM, on_term_signal,
+ display);
+ signals[1] = wl_event_loop_add_signal(loop, SIGINT, on_term_signal,
+ display);
+ signals[2] = wl_event_loop_add_signal(loop, SIGQUIT, on_term_signal,
+ display);
+
+ wl_list_init(&child_process_list);
+ signals[3] = wl_event_loop_add_signal(loop, SIGCHLD, sigchld_handler,
+ NULL);
+
+ if (!backend) {
+ if (getenv("WAYLAND_DISPLAY"))
+ backend = "wayland-backend.so";
+ else if (getenv("DISPLAY"))
+ backend = "x11-backend.so";
+ else
+ backend = WESTON_NATIVE_BACKEND;
+ }
+
+ config = weston_config_parse("weston.ini");
+ if (config != NULL) {
+ weston_log("Using config file '%s'\n",
+ weston_config_get_full_path(config));
+ } else {
+ weston_log("Starting with no config file.\n");
+ }
+ section = weston_config_get_section(config, "core", NULL, NULL);
+ weston_config_section_get_string(section, "modules", &modules, "");
+
+ backend_init = load_module(backend, "backend_init");
+ if (!backend_init)
+ exit(EXIT_FAILURE);
+
+ ec = backend_init(display, &argc, argv, config);
+ if (ec == NULL) {
+ weston_log("fatal: failed to create compositor\n");
+ exit(EXIT_FAILURE);
+ }
+
+ catch_signals();
+ segv_compositor = ec;
+
+ ec->idle_time = idle_time;
+
+ setenv("WAYLAND_DISPLAY", socket_name, 1);
+
+ if (!shell)
+ weston_config_section_get_string(section, "shell", &shell,
+ "desktop-shell.so");
+ if (load_modules(ec, shell, &argc, argv) < 0)
+ goto out;
+
+ if (load_modules(ec, modules, &argc, argv) < 0)
+ goto out;
+ if (load_modules(ec, option_modules, &argc, argv) < 0)
+ goto out;
+
+ for (i = 1; i < argc; i++)
+ weston_log("fatal: unhandled option: %s\n", argv[i]);
+ if (argc > 1) {
+ ret = EXIT_FAILURE;
+ goto out;
+ }
+
+ weston_compositor_log_capabilities(ec);
+
+ if (wl_display_add_socket(display, socket_name)) {
+ weston_log("fatal: failed to add socket: %m\n");
+ ret = EXIT_FAILURE;
+ goto out;
+ }
+
+ weston_compositor_wake(ec);
+
+ wl_display_run(display);
+
+ out:
+ /* prevent further rendering while shutting down */
+ ec->state = WESTON_COMPOSITOR_OFFSCREEN;
+
+ wl_signal_emit(&ec->destroy_signal, ec);
+
+ for (i = ARRAY_LENGTH(signals); i;)
+ wl_event_source_remove(signals[--i]);
+
+ weston_compositor_xkb_destroy(ec);
+
+ ec->destroy(ec);
+ wl_display_destroy(display);
+
+ weston_log_file_close();
+
+ return ret;
+}
diff --git a/src/compositor.h b/src/compositor.h
new file mode 100644
index 00000000..0dcb604e
--- /dev/null
+++ b/src/compositor.h
@@ -0,0 +1,1278 @@
+/*
+ * Copyright © 2008-2011 Kristian Høgsberg
+ * Copyright © 2012 Collabora, Ltd.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that 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.
+ */
+
+#ifndef _WAYLAND_SYSTEM_COMPOSITOR_H_
+#define _WAYLAND_SYSTEM_COMPOSITOR_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <pixman.h>
+#include <xkbcommon/xkbcommon.h>
+
+#define WL_HIDE_DEPRECATED
+#include <wayland-server.h>
+
+#include "version.h"
+#include "matrix.h"
+#include "config-parser.h"
+#include "zalloc.h"
+
+#ifndef MIN
+#define MIN(x,y) (((x) < (y)) ? (x) : (y))
+#endif
+
+#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0])
+
+#define container_of(ptr, type, member) ({ \
+ const __typeof__( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - offsetof(type,member) );})
+
+struct weston_transform {
+ struct weston_matrix matrix;
+ struct wl_list link;
+};
+
+struct weston_surface;
+struct weston_buffer;
+struct shell_surface;
+struct weston_seat;
+struct weston_output;
+struct input_method;
+
+enum weston_keyboard_modifier {
+ MODIFIER_CTRL = (1 << 0),
+ MODIFIER_ALT = (1 << 1),
+ MODIFIER_SUPER = (1 << 2),
+ MODIFIER_SHIFT = (1 << 3),
+};
+
+enum weston_led {
+ LED_NUM_LOCK = (1 << 0),
+ LED_CAPS_LOCK = (1 << 1),
+ LED_SCROLL_LOCK = (1 << 2),
+};
+
+struct weston_mode {
+ uint32_t flags;
+ int32_t width, height;
+ uint32_t refresh;
+ struct wl_list link;
+};
+
+struct weston_shell_client {
+ void (*send_configure)(struct weston_surface *surface,
+ uint32_t edges, int32_t width, int32_t height);
+};
+
+struct weston_shell_interface {
+ void *shell; /* either desktop or tablet */
+
+ struct shell_surface *(*create_shell_surface)(void *shell,
+ struct weston_surface *surface,
+ const struct weston_shell_client *client);
+
+ void (*set_toplevel)(struct shell_surface *shsurf);
+
+ void (*set_transient)(struct shell_surface *shsurf,
+ struct weston_surface *parent,
+ int x, int y, uint32_t flags);
+ void (*set_fullscreen)(struct shell_surface *shsurf,
+ uint32_t method,
+ uint32_t framerate,
+ struct weston_output *output);
+ void (*set_xwayland)(struct shell_surface *shsurf,
+ int x, int y, uint32_t flags);
+ int (*move)(struct shell_surface *shsurf, struct weston_seat *ws);
+ int (*resize)(struct shell_surface *shsurf,
+ struct weston_seat *ws, uint32_t edges);
+ void (*set_title)(struct shell_surface *shsurf,
+ const char *title);
+
+};
+
+struct weston_border {
+ int32_t left, right, top, bottom;
+};
+
+struct weston_animation {
+ void (*frame)(struct weston_animation *animation,
+ struct weston_output *output, uint32_t msecs);
+ int frame_counter;
+ struct wl_list link;
+};
+
+enum {
+ WESTON_SPRING_OVERSHOOT,
+ WESTON_SPRING_CLAMP,
+ WESTON_SPRING_BOUNCE
+};
+
+struct weston_spring {
+ double k;
+ double friction;
+ double current;
+ double target;
+ double previous;
+ double min, max;
+ uint32_t timestamp;
+ uint32_t clip;
+};
+
+enum {
+ ZOOM_FOCUS_POINTER,
+ ZOOM_FOCUS_TEXT
+};
+
+struct weston_fixed_point {
+ wl_fixed_t x, y;
+};
+
+struct weston_output_zoom {
+ int active;
+ uint32_t type;
+ float increment;
+ float level;
+ float max_level;
+ float trans_x, trans_y;
+ struct weston_animation animation_z;
+ struct weston_spring spring_z;
+ struct weston_animation animation_xy;
+ struct weston_spring spring_xy;
+ struct weston_fixed_point from;
+ struct weston_fixed_point to;
+ struct weston_fixed_point current;
+ struct weston_fixed_point text_cursor;
+};
+
+/* bit compatible with drm definitions. */
+enum dpms_enum {
+ WESTON_DPMS_ON,
+ WESTON_DPMS_STANDBY,
+ WESTON_DPMS_SUSPEND,
+ WESTON_DPMS_OFF
+};
+
+enum weston_mode_switch_op {
+ WESTON_MODE_SWITCH_SET_NATIVE,
+ WESTON_MODE_SWITCH_SET_TEMPORARY,
+ WESTON_MODE_SWITCH_RESTORE_NATIVE
+};
+
+struct weston_output {
+ uint32_t id;
+ char *name;
+
+ void *renderer_state;
+
+ struct wl_list link;
+ struct wl_list resource_list;
+ struct wl_global *global;
+ struct weston_compositor *compositor;
+ struct weston_matrix matrix;
+ struct wl_list animation_list;
+ int32_t x, y, width, height;
+ int32_t mm_width, mm_height;
+ struct weston_border border;
+ pixman_region32_t region;
+ pixman_region32_t previous_damage;
+ int repaint_needed;
+ int repaint_scheduled;
+ struct weston_output_zoom zoom;
+ int dirty;
+ struct wl_signal frame_signal;
+ struct wl_signal destroy_signal;
+ uint32_t frame_time;
+ int disable_planes;
+
+ char *make, *model, *serial_number;
+ uint32_t subpixel;
+ uint32_t transform;
+ int32_t native_scale;
+ int32_t current_scale;
+ int32_t original_scale;
+
+ struct weston_mode *native_mode;
+ struct weston_mode *current_mode;
+ struct weston_mode *original_mode;
+ struct wl_list mode_list;
+
+ void (*start_repaint_loop)(struct weston_output *output);
+ int (*repaint)(struct weston_output *output,
+ pixman_region32_t *damage);
+ void (*destroy)(struct weston_output *output);
+ void (*assign_planes)(struct weston_output *output);
+ int (*switch_mode)(struct weston_output *output, struct weston_mode *mode);
+
+ /* backlight values are on 0-255 range, where higher is brighter */
+ int32_t backlight_current;
+ void (*set_backlight)(struct weston_output *output, uint32_t value);
+ void (*set_dpms)(struct weston_output *output, enum dpms_enum level);
+
+ int connection_internal;
+ uint16_t gamma_size;
+ void (*set_gamma)(struct weston_output *output,
+ uint16_t size,
+ uint16_t *r,
+ uint16_t *g,
+ uint16_t *b);
+};
+
+struct weston_pointer_grab;
+struct weston_pointer_grab_interface {
+ void (*focus)(struct weston_pointer_grab *grab);
+ void (*motion)(struct weston_pointer_grab *grab, uint32_t time);
+ void (*button)(struct weston_pointer_grab *grab,
+ uint32_t time, uint32_t button, uint32_t state);
+ void (*cancel)(struct weston_pointer_grab *grab);
+};
+
+struct weston_pointer_grab {
+ const struct weston_pointer_grab_interface *interface;
+ struct weston_pointer *pointer;
+};
+
+struct weston_keyboard_grab;
+struct weston_keyboard_grab_interface {
+ void (*key)(struct weston_keyboard_grab *grab, uint32_t time,
+ uint32_t key, uint32_t state);
+ void (*modifiers)(struct weston_keyboard_grab *grab, uint32_t serial,
+ uint32_t mods_depressed, uint32_t mods_latched,
+ uint32_t mods_locked, uint32_t group);
+ void (*cancel)(struct weston_keyboard_grab *grab);
+};
+
+struct weston_keyboard_grab {
+ const struct weston_keyboard_grab_interface *interface;
+ struct weston_keyboard *keyboard;
+};
+
+struct weston_touch_grab;
+struct weston_touch_grab_interface {
+ void (*down)(struct weston_touch_grab *grab,
+ uint32_t time,
+ int touch_id,
+ wl_fixed_t sx,
+ wl_fixed_t sy);
+ void (*up)(struct weston_touch_grab *grab,
+ uint32_t time,
+ int touch_id);
+ void (*motion)(struct weston_touch_grab *grab,
+ uint32_t time,
+ int touch_id,
+ wl_fixed_t sx,
+ wl_fixed_t sy);
+ void (*cancel)(struct weston_touch_grab *grab);
+};
+
+struct weston_touch_grab {
+ const struct weston_touch_grab_interface *interface;
+ struct weston_touch *touch;
+};
+
+struct weston_data_offer {
+ struct wl_resource *resource;
+ struct weston_data_source *source;
+ struct wl_listener source_destroy_listener;
+};
+
+struct weston_data_source {
+ struct wl_resource *resource;
+ struct wl_signal destroy_signal;
+ struct wl_array mime_types;
+
+ void (*accept)(struct weston_data_source *source,
+ uint32_t serial, const char *mime_type);
+ void (*send)(struct weston_data_source *source,
+ const char *mime_type, int32_t fd);
+ void (*cancel)(struct weston_data_source *source);
+};
+
+struct weston_pointer {
+ struct weston_seat *seat;
+
+ struct wl_list resource_list;
+ struct wl_list focus_resource_list;
+ struct weston_surface *focus;
+ uint32_t focus_serial;
+ struct wl_signal focus_signal;
+
+ struct weston_surface *sprite;
+ struct wl_listener sprite_destroy_listener;
+ int32_t hotspot_x, hotspot_y;
+
+ struct weston_pointer_grab *grab;
+ struct weston_pointer_grab default_grab;
+ wl_fixed_t grab_x, grab_y;
+ uint32_t grab_button;
+ uint32_t grab_serial;
+ uint32_t grab_time;
+
+ wl_fixed_t x, y;
+ uint32_t button_count;
+};
+
+
+struct weston_touch {
+ struct weston_seat *seat;
+
+ struct wl_list resource_list;
+ struct wl_list focus_resource_list;
+ struct weston_surface *focus;
+ uint32_t focus_serial;
+ struct wl_signal focus_signal;
+
+ struct weston_touch_grab *grab;
+ struct weston_touch_grab default_grab;
+ int grab_touch_id;
+ wl_fixed_t grab_x, grab_y;
+ uint32_t grab_serial;
+ uint32_t grab_time;
+};
+
+struct weston_pointer *
+weston_pointer_create(void);
+void
+weston_pointer_destroy(struct weston_pointer *pointer);
+void
+weston_pointer_set_focus(struct weston_pointer *pointer,
+ struct weston_surface *surface,
+ wl_fixed_t sx, wl_fixed_t sy);
+void
+weston_pointer_start_grab(struct weston_pointer *pointer,
+ struct weston_pointer_grab *grab);
+void
+weston_pointer_end_grab(struct weston_pointer *pointer);
+void
+weston_pointer_clamp(struct weston_pointer *pointer,
+ wl_fixed_t *fx, wl_fixed_t *fy);
+
+struct weston_keyboard *
+weston_keyboard_create(void);
+void
+weston_keyboard_destroy(struct weston_keyboard *keyboard);
+void
+weston_keyboard_set_focus(struct weston_keyboard *keyboard,
+ struct weston_surface *surface);
+void
+weston_keyboard_start_grab(struct weston_keyboard *device,
+ struct weston_keyboard_grab *grab);
+void
+weston_keyboard_end_grab(struct weston_keyboard *keyboard);
+
+struct weston_touch *
+weston_touch_create(void);
+void
+weston_touch_destroy(struct weston_touch *touch);
+void
+weston_touch_set_focus(struct weston_seat *seat,
+ struct weston_surface *surface);
+void
+weston_touch_start_grab(struct weston_touch *device,
+ struct weston_touch_grab *grab);
+void
+weston_touch_end_grab(struct weston_touch *touch);
+
+void
+wl_data_device_set_keyboard_focus(struct weston_seat *seat);
+
+int
+wl_data_device_manager_init(struct wl_display *display);
+
+
+void
+weston_seat_set_selection(struct weston_seat *seat,
+ struct weston_data_source *source, uint32_t serial);
+int
+weston_seat_start_drag(struct weston_seat *seat,
+ struct weston_data_source *source,
+ struct weston_surface *icon,
+ struct wl_client *client);
+
+struct weston_xkb_info {
+ struct xkb_keymap *keymap;
+ int keymap_fd;
+ size_t keymap_size;
+ char *keymap_area;
+ int32_t ref_count;
+ xkb_mod_index_t shift_mod;
+ xkb_mod_index_t caps_mod;
+ xkb_mod_index_t ctrl_mod;
+ xkb_mod_index_t alt_mod;
+ xkb_mod_index_t mod2_mod;
+ xkb_mod_index_t mod3_mod;
+ xkb_mod_index_t super_mod;
+ xkb_mod_index_t mod5_mod;
+ xkb_led_index_t num_led;
+ xkb_led_index_t caps_led;
+ xkb_led_index_t scroll_led;
+};
+
+struct weston_keyboard {
+ struct weston_seat *seat;
+
+ struct wl_list resource_list;
+ struct wl_list focus_resource_list;
+ struct weston_surface *focus;
+ uint32_t focus_serial;
+ struct wl_signal focus_signal;
+
+ struct weston_keyboard_grab *grab;
+ struct weston_keyboard_grab default_grab;
+ uint32_t grab_key;
+ uint32_t grab_serial;
+ uint32_t grab_time;
+
+ struct wl_array keys;
+
+ struct {
+ uint32_t mods_depressed;
+ uint32_t mods_latched;
+ uint32_t mods_locked;
+ uint32_t group;
+ } modifiers;
+
+ struct weston_keyboard_grab input_method_grab;
+ struct wl_resource *input_method_resource;
+};
+
+struct weston_seat {
+ struct wl_list base_resource_list;
+
+ struct wl_global *global;
+ struct weston_pointer *pointer;
+ struct weston_keyboard *keyboard;
+ struct weston_touch *touch;
+ int pointer_device_count;
+ int keyboard_device_count;
+ int touch_device_count;
+
+ struct weston_output *output; /* constraint */
+
+ struct wl_signal destroy_signal;
+
+ struct weston_compositor *compositor;
+ struct wl_list link;
+ enum weston_keyboard_modifier modifier_state;
+ struct weston_surface *saved_kbd_focus;
+ struct wl_listener saved_kbd_focus_listener;
+ struct wl_list drag_resource_list;
+
+ uint32_t selection_serial;
+ struct weston_data_source *selection_data_source;
+ struct wl_listener selection_data_source_listener;
+ struct wl_signal selection_signal;
+
+ uint32_t num_tp;
+
+ void (*led_update)(struct weston_seat *ws, enum weston_led leds);
+
+ struct weston_xkb_info *xkb_info;
+ struct {
+ struct xkb_state *state;
+ enum weston_led leds;
+ } xkb_state;
+
+ struct input_method *input_method;
+ char *seat_name;
+};
+
+enum {
+ WESTON_COMPOSITOR_ACTIVE,
+ WESTON_COMPOSITOR_IDLE, /* shell->unlock called on activity */
+ WESTON_COMPOSITOR_OFFSCREEN, /* no rendering, no frame events */
+ WESTON_COMPOSITOR_SLEEPING /* same as offscreen, but also set dmps
+ * to off */
+};
+
+struct weston_layer {
+ struct wl_list surface_list;
+ struct wl_list link;
+};
+
+struct weston_plane {
+ struct weston_compositor *compositor;
+ pixman_region32_t damage;
+ pixman_region32_t clip;
+ int32_t x, y;
+ struct wl_list link;
+};
+
+struct weston_renderer {
+ int (*read_pixels)(struct weston_output *output,
+ pixman_format_code_t format, void *pixels,
+ uint32_t x, uint32_t y,
+ uint32_t width, uint32_t height);
+ void (*repaint_output)(struct weston_output *output,
+ pixman_region32_t *output_damage);
+ void (*flush_damage)(struct weston_surface *surface);
+ void (*attach)(struct weston_surface *es, struct weston_buffer *buffer);
+ int (*create_surface)(struct weston_surface *surface);
+ void (*surface_set_color)(struct weston_surface *surface,
+ float red, float green,
+ float blue, float alpha);
+ void (*destroy_surface)(struct weston_surface *surface);
+ void (*destroy)(struct weston_compositor *ec);
+};
+
+enum weston_capability {
+ /* backend/renderer supports arbitrary rotation */
+ WESTON_CAP_ROTATION_ANY = 0x0001,
+
+ /* screencaptures need to be y-flipped */
+ WESTON_CAP_CAPTURE_YFLIP = 0x0002,
+};
+
+struct weston_compositor {
+ struct wl_signal destroy_signal;
+
+ struct wl_display *wl_display;
+ struct weston_shell_interface shell_interface;
+ struct weston_config *config;
+
+ /* surface signals */
+ struct wl_signal activate_signal;
+ struct wl_signal transform_signal;
+
+ struct wl_signal kill_signal;
+ struct wl_signal idle_signal;
+ struct wl_signal wake_signal;
+
+ struct wl_signal show_input_panel_signal;
+ struct wl_signal hide_input_panel_signal;
+ struct wl_signal update_input_panel_signal;
+
+ struct wl_signal seat_created_signal;
+ struct wl_signal output_created_signal;
+
+ struct wl_event_loop *input_loop;
+ struct wl_event_source *input_loop_source;
+
+ struct wl_signal session_signal;
+ int session_active;
+
+ struct weston_layer fade_layer;
+ struct weston_layer cursor_layer;
+
+ struct wl_list output_list;
+ struct wl_list seat_list;
+ struct wl_list layer_list;
+ struct wl_list surface_list;
+ struct wl_list plane_list;
+ struct wl_list key_binding_list;
+ struct wl_list button_binding_list;
+ struct wl_list touch_binding_list;
+ struct wl_list axis_binding_list;
+ struct wl_list debug_binding_list;
+
+ uint32_t state;
+ struct wl_event_source *idle_source;
+ uint32_t idle_inhibit;
+ int idle_time; /* timeout, s */
+
+ /* Repaint state. */
+ struct weston_plane primary_plane;
+ uint32_t capabilities; /* combination of enum weston_capability */
+
+ uint32_t focus;
+
+ struct weston_renderer *renderer;
+
+ pixman_format_code_t read_format;
+
+ void (*destroy)(struct weston_compositor *ec);
+ void (*restore)(struct weston_compositor *ec);
+ int (*authenticate)(struct weston_compositor *c, uint32_t id);
+
+ void (*ping_handler)(struct weston_surface *surface, uint32_t serial);
+
+ struct weston_launcher *launcher;
+
+ uint32_t output_id_pool;
+
+ struct xkb_rule_names xkb_names;
+ struct xkb_context *xkb_context;
+ struct weston_xkb_info *xkb_info;
+
+ /* Raw keyboard processing (no libxkbcommon initialization or handling) */
+ int use_xkbcommon;
+};
+
+struct weston_buffer {
+ struct wl_resource *resource;
+ struct wl_signal destroy_signal;
+ struct wl_listener destroy_listener;
+
+ union {
+ struct wl_shm_buffer *shm_buffer;
+ void *legacy_buffer;
+ };
+ int32_t width, height;
+ uint32_t busy_count;
+ int y_inverted;
+};
+
+struct weston_buffer_reference {
+ struct weston_buffer *buffer;
+ struct wl_listener destroy_listener;
+};
+
+struct weston_region {
+ struct wl_resource *resource;
+ pixman_region32_t region;
+};
+
+struct weston_subsurface {
+ struct wl_resource *resource;
+
+ /* guaranteed to be valid and non-NULL */
+ struct weston_surface *surface;
+ struct wl_listener surface_destroy_listener;
+
+ /* can be NULL */
+ struct weston_surface *parent;
+ struct wl_listener parent_destroy_listener;
+ struct wl_list parent_link;
+ struct wl_list parent_link_pending;
+
+ struct {
+ int32_t x;
+ int32_t y;
+ int set;
+ } position;
+
+ struct {
+ int has_data;
+
+ /* wl_surface.attach */
+ int newly_attached;
+ struct weston_buffer_reference buffer_ref;
+ int32_t sx;
+ int32_t sy;
+
+ /* wl_surface.damage */
+ pixman_region32_t damage;
+
+ /* wl_surface.set_opaque_region */
+ pixman_region32_t opaque;
+
+ /* wl_surface.set_input_region */
+ pixman_region32_t input;
+
+ /* wl_surface.frame */
+ struct wl_list frame_callback_list;
+
+ /* wl_surface.set_buffer_transform */
+ uint32_t buffer_transform;
+
+ /* wl_surface.set_buffer_scale */
+ int32_t buffer_scale;
+ } cached;
+
+ int synchronized;
+};
+
+/* Using weston_surface transformations
+ *
+ * To add a transformation to a surface, create a struct weston_transform, and
+ * add it to the list surface->geometry.transformation_list. Whenever you
+ * change the list, anything under surface->geometry, or anything in the
+ * weston_transforms linked into the list, you must call
+ * weston_surface_geometry_dirty().
+ *
+ * The order in the list defines the order of transformations. Let the list
+ * contain the transformation matrices M1, ..., Mn as head to tail. The
+ * transformation is applied to surface-local coordinate vector p as
+ * P = Mn * ... * M2 * M1 * p
+ * to produce the global coordinate vector P. The total transform
+ * Mn * ... * M2 * M1
+ * is cached in surface->transform.matrix, and the inverse of it in
+ * surface->transform.inverse.
+ *
+ * The list always contains surface->transform.position transformation, which
+ * is the translation by surface->geometry.x and y.
+ *
+ * If you want to apply a transformation in local coordinates, add your
+ * weston_transform to the head of the list. If you want to apply a
+ * transformation in global coordinates, add it to the tail of the list.
+ *
+ * If surface->geometry.parent is set, the total transformation of this
+ * surface will be the parent's total transformation and this transformation
+ * combined:
+ * Mparent * Mn * ... * M2 * M1
+ */
+
+struct weston_surface {
+ struct wl_resource *resource;
+ struct wl_signal destroy_signal;
+ struct weston_compositor *compositor;
+ pixman_region32_t clip;
+ pixman_region32_t damage;
+ pixman_region32_t opaque; /* part of geometry, see below */
+ pixman_region32_t input;
+ struct wl_list link;
+ struct wl_list layer_link;
+ float alpha; /* part of geometry, see below */
+ struct weston_plane *plane;
+ int32_t ref_count;
+
+ void *renderer_state;
+
+ /* Surface geometry state, mutable.
+ * If you change anything, call weston_surface_geometry_dirty().
+ * That includes the transformations referenced from the list.
+ */
+ struct {
+ float x, y; /* surface translation on display */
+ int32_t width, height;
+
+ /* struct weston_transform */
+ struct wl_list transformation_list;
+
+ /* managed by weston_surface_set_transform_parent() */
+ struct weston_surface *parent;
+ struct wl_listener parent_destroy_listener;
+ struct wl_list child_list; /* geometry.parent_link */
+ struct wl_list parent_link;
+ } geometry;
+
+ /* State derived from geometry state, read-only.
+ * This is updated by weston_surface_update_transform().
+ */
+ struct {
+ int dirty;
+
+ pixman_region32_t boundingbox;
+ pixman_region32_t opaque;
+
+ /* matrix and inverse are used only if enabled = 1.
+ * If enabled = 0, use x, y, width, height directly.
+ */
+ int enabled;
+ struct weston_matrix matrix;
+ struct weston_matrix inverse;
+
+ struct weston_transform position; /* matrix from x, y */
+ } transform;
+
+ /*
+ * Which output to vsync this surface to.
+ * Used to determine, whether to send or queue frame events.
+ * Must be NULL, if 'link' is not in weston_compositor::surface_list.
+ */
+ struct weston_output *output;
+
+ /*
+ * A more complete representation of all outputs this surface is
+ * displayed on.
+ */
+ uint32_t output_mask;
+
+ struct wl_list frame_callback_list;
+
+ struct weston_buffer_reference buffer_ref;
+ uint32_t buffer_transform;
+ int32_t buffer_scale;
+ int keep_buffer; /* bool for backends to prevent early release */
+
+ /* All the pending state, that wl_surface.commit will apply. */
+ struct {
+ /* wl_surface.attach */
+ int newly_attached;
+ struct weston_buffer *buffer;
+ struct wl_listener buffer_destroy_listener;
+ int32_t sx;
+ int32_t sy;
+
+ /* wl_surface.damage */
+ pixman_region32_t damage;
+
+ /* wl_surface.set_opaque_region */
+ pixman_region32_t opaque;
+
+ /* wl_surface.set_input_region */
+ pixman_region32_t input;
+
+ /* wl_surface.frame */
+ struct wl_list frame_callback_list;
+
+ /* wl_surface.set_buffer_transform */
+ uint32_t buffer_transform;
+
+ /* wl_surface.set_scaling_factor */
+ int32_t buffer_scale;
+ } pending;
+
+ /*
+ * If non-NULL, this function will be called on surface::attach after
+ * a new buffer has been set up for this surface. The integer params
+ * are the sx and sy paramerters supplied to surface::attach .
+ */
+ void (*configure)(struct weston_surface *es, int32_t sx, int32_t sy, int32_t width, int32_t height);
+ void *configure_private;
+
+ /* Parent's list of its sub-surfaces, weston_subsurface:parent_link.
+ * Contains also the parent itself as a dummy weston_subsurface,
+ * if the list is not empty.
+ */
+ struct wl_list subsurface_list; /* weston_subsurface::parent_link */
+ struct wl_list subsurface_list_pending; /* ...::parent_link_pending */
+};
+
+enum weston_key_state_update {
+ STATE_UPDATE_AUTOMATIC,
+ STATE_UPDATE_NONE,
+};
+
+void
+weston_version(int *major, int *minor, int *micro);
+
+void
+weston_surface_update_transform(struct weston_surface *surface);
+
+void
+weston_surface_geometry_dirty(struct weston_surface *surface);
+
+void
+weston_surface_to_global_fixed(struct weston_surface *surface,
+ wl_fixed_t sx, wl_fixed_t sy,
+ wl_fixed_t *x, wl_fixed_t *y);
+void
+weston_surface_to_global_float(struct weston_surface *surface,
+ float sx, float sy, float *x, float *y);
+
+void
+weston_surface_from_global_float(struct weston_surface *surface,
+ float x, float y, float *sx, float *sy);
+void
+weston_surface_from_global(struct weston_surface *surface,
+ int32_t x, int32_t y, int32_t *sx, int32_t *sy);
+void
+weston_surface_from_global_fixed(struct weston_surface *surface,
+ wl_fixed_t x, wl_fixed_t y,
+ wl_fixed_t *sx, wl_fixed_t *sy);
+int32_t
+weston_surface_buffer_width(struct weston_surface *surface);
+int32_t
+weston_surface_buffer_height(struct weston_surface *surface);
+
+WL_EXPORT void
+weston_surface_to_buffer_float(struct weston_surface *surface,
+ float x, float y, float *bx, float *by);
+WL_EXPORT void
+weston_surface_to_buffer(struct weston_surface *surface,
+ int sx, int sy, int *bx, int *by);
+
+pixman_box32_t
+weston_surface_to_buffer_rect(struct weston_surface *surface,
+ pixman_box32_t rect);
+
+void
+weston_spring_init(struct weston_spring *spring,
+ double k, double current, double target);
+void
+weston_spring_update(struct weston_spring *spring, uint32_t msec);
+int
+weston_spring_done(struct weston_spring *spring);
+
+void
+weston_surface_activate(struct weston_surface *surface,
+ struct weston_seat *seat);
+void
+notify_motion(struct weston_seat *seat, uint32_t time,
+ wl_fixed_t dx, wl_fixed_t dy);
+void
+notify_motion_absolute(struct weston_seat *seat, uint32_t time,
+ wl_fixed_t x, wl_fixed_t y);
+void
+notify_button(struct weston_seat *seat, uint32_t time, int32_t button,
+ enum wl_pointer_button_state state);
+void
+notify_axis(struct weston_seat *seat, uint32_t time, uint32_t axis,
+ wl_fixed_t value);
+void
+notify_key(struct weston_seat *seat, uint32_t time, uint32_t key,
+ enum wl_keyboard_key_state state,
+ enum weston_key_state_update update_state);
+void
+notify_modifiers(struct weston_seat *seat, uint32_t serial);
+
+void
+notify_pointer_focus(struct weston_seat *seat, struct weston_output *output,
+ wl_fixed_t x, wl_fixed_t y);
+
+void
+notify_keyboard_focus_in(struct weston_seat *seat, struct wl_array *keys,
+ enum weston_key_state_update update_state);
+void
+notify_keyboard_focus_out(struct weston_seat *seat);
+
+void
+notify_touch(struct weston_seat *seat, uint32_t time, int touch_id,
+ wl_fixed_t x, wl_fixed_t y, int touch_type);
+
+void
+weston_layer_init(struct weston_layer *layer, struct wl_list *below);
+
+void
+weston_plane_init(struct weston_plane *plane,
+ struct weston_compositor *ec,
+ int32_t x, int32_t y);
+void
+weston_plane_release(struct weston_plane *plane);
+
+void
+weston_compositor_stack_plane(struct weston_compositor *ec,
+ struct weston_plane *plane,
+ struct weston_plane *above);
+
+void
+weston_output_finish_frame(struct weston_output *output, uint32_t msecs);
+void
+weston_output_schedule_repaint(struct weston_output *output);
+void
+weston_output_damage(struct weston_output *output);
+void
+weston_compositor_schedule_repaint(struct weston_compositor *compositor);
+void
+weston_compositor_fade(struct weston_compositor *compositor, float tint);
+void
+weston_compositor_damage_all(struct weston_compositor *compositor);
+void
+weston_compositor_unlock(struct weston_compositor *compositor);
+void
+weston_compositor_wake(struct weston_compositor *compositor);
+void
+weston_compositor_offscreen(struct weston_compositor *compositor);
+void
+weston_compositor_sleep(struct weston_compositor *compositor);
+struct weston_surface *
+weston_compositor_pick_surface(struct weston_compositor *compositor,
+ wl_fixed_t x, wl_fixed_t y,
+ wl_fixed_t *sx, wl_fixed_t *sy);
+
+
+struct weston_binding;
+typedef void (*weston_key_binding_handler_t)(struct weston_seat *seat,
+ uint32_t time, uint32_t key,
+ void *data);
+struct weston_binding *
+weston_compositor_add_key_binding(struct weston_compositor *compositor,
+ uint32_t key,
+ enum weston_keyboard_modifier modifier,
+ weston_key_binding_handler_t binding,
+ void *data);
+
+typedef void (*weston_button_binding_handler_t)(struct weston_seat *seat,
+ uint32_t time, uint32_t button,
+ void *data);
+struct weston_binding *
+weston_compositor_add_button_binding(struct weston_compositor *compositor,
+ uint32_t button,
+ enum weston_keyboard_modifier modifier,
+ weston_button_binding_handler_t binding,
+ void *data);
+
+typedef void (*weston_touch_binding_handler_t)(struct weston_seat *seat,
+ uint32_t time,
+ void *data);
+struct weston_binding *
+weston_compositor_add_touch_binding(struct weston_compositor *compositor,
+ enum weston_keyboard_modifier modifier,
+ weston_touch_binding_handler_t binding,
+ void *data);
+
+typedef void (*weston_axis_binding_handler_t)(struct weston_seat *seat,
+ uint32_t time, uint32_t axis,
+ wl_fixed_t value, void *data);
+struct weston_binding *
+weston_compositor_add_axis_binding(struct weston_compositor *compositor,
+ uint32_t axis,
+ enum weston_keyboard_modifier modifier,
+ weston_axis_binding_handler_t binding,
+ void *data);
+struct weston_binding *
+weston_compositor_add_debug_binding(struct weston_compositor *compositor,
+ uint32_t key,
+ weston_key_binding_handler_t binding,
+ void *data);
+void
+weston_binding_destroy(struct weston_binding *binding);
+
+void
+weston_binding_list_destroy_all(struct wl_list *list);
+
+void
+weston_compositor_run_key_binding(struct weston_compositor *compositor,
+ struct weston_seat *seat, uint32_t time,
+ uint32_t key,
+ enum wl_keyboard_key_state state);
+void
+weston_compositor_run_button_binding(struct weston_compositor *compositor,
+ struct weston_seat *seat, uint32_t time,
+ uint32_t button,
+ enum wl_pointer_button_state value);
+void
+weston_compositor_run_touch_binding(struct weston_compositor *compositor,
+ struct weston_seat *seat, uint32_t time,
+ int touch_type);
+int
+weston_compositor_run_axis_binding(struct weston_compositor *compositor,
+ struct weston_seat *seat, uint32_t time,
+ uint32_t axis, int32_t value);
+int
+weston_compositor_run_debug_binding(struct weston_compositor *compositor,
+ struct weston_seat *seat, uint32_t time,
+ uint32_t key,
+ enum wl_keyboard_key_state state);
+
+int
+weston_environment_get_fd(const char *env);
+
+struct wl_list *
+weston_compositor_top(struct weston_compositor *compositor);
+
+struct weston_surface *
+weston_surface_create(struct weston_compositor *compositor);
+
+void
+weston_surface_configure(struct weston_surface *surface,
+ float x, float y, int width, int height);
+
+void
+weston_surface_restack(struct weston_surface *surface, struct wl_list *below);
+
+void
+weston_surface_set_position(struct weston_surface *surface,
+ float x, float y);
+
+void
+weston_surface_set_transform_parent(struct weston_surface *surface,
+ struct weston_surface *parent);
+
+int
+weston_surface_is_mapped(struct weston_surface *surface);
+
+void
+weston_surface_schedule_repaint(struct weston_surface *surface);
+
+void
+weston_surface_damage(struct weston_surface *surface);
+
+void
+weston_surface_damage_below(struct weston_surface *surface);
+
+void
+weston_surface_move_to_plane(struct weston_surface *surface,
+ struct weston_plane *plane);
+void
+weston_surface_unmap(struct weston_surface *surface);
+
+struct weston_surface *
+weston_surface_get_main_surface(struct weston_surface *surface);
+
+struct weston_buffer *
+weston_buffer_from_resource(struct wl_resource *resource);
+
+void
+weston_buffer_reference(struct weston_buffer_reference *ref,
+ struct weston_buffer *buffer);
+
+uint32_t
+weston_compositor_get_time(void);
+
+int
+weston_compositor_init(struct weston_compositor *ec, struct wl_display *display,
+ int *argc, char *argv[], struct weston_config *config);
+void
+weston_compositor_shutdown(struct weston_compositor *ec);
+void
+weston_text_cursor_position_notify(struct weston_surface *surface,
+ wl_fixed_t x, wl_fixed_t y);
+void
+weston_output_init_zoom(struct weston_output *output);
+void
+weston_output_update_zoom(struct weston_output *output, uint32_t type);
+void
+weston_output_update_matrix(struct weston_output *output);
+void
+weston_output_move(struct weston_output *output, int x, int y);
+void
+weston_output_init(struct weston_output *output, struct weston_compositor *c,
+ int x, int y, int width, int height, uint32_t transform, int32_t scale);
+void
+weston_output_destroy(struct weston_output *output);
+void
+weston_output_transform_coordinate(struct weston_output *x11_output,
+ int device_x, int device_y,
+ wl_fixed_t *x, wl_fixed_t *y);
+
+void
+weston_seat_init(struct weston_seat *seat, struct weston_compositor *ec,
+ const char *seat_name);
+void
+weston_seat_init_pointer(struct weston_seat *seat);
+void
+weston_seat_release_pointer(struct weston_seat *seat);
+int
+weston_seat_init_keyboard(struct weston_seat *seat, struct xkb_keymap *keymap);
+void
+weston_seat_release_keyboard(struct weston_seat *seat);
+void
+weston_seat_init_touch(struct weston_seat *seat);
+void
+weston_seat_release_touch(struct weston_seat *seat);
+void
+weston_seat_repick(struct weston_seat *seat);
+
+void
+weston_seat_release(struct weston_seat *seat);
+int
+weston_compositor_xkb_init(struct weston_compositor *ec,
+ struct xkb_rule_names *names);
+void
+weston_compositor_xkb_destroy(struct weston_compositor *ec);
+
+/* String literal of spaces, the same width as the timestamp. */
+#define STAMP_SPACE " "
+
+void
+weston_log_file_open(const char *filename);
+void
+weston_log_file_close(void);
+int
+weston_vlog(const char *fmt, va_list ap);
+int
+weston_vlog_continue(const char *fmt, va_list ap);
+int
+weston_log(const char *fmt, ...)
+ __attribute__ ((format (printf, 1, 2)));
+int
+weston_log_continue(const char *fmt, ...)
+ __attribute__ ((format (printf, 1, 2)));
+
+enum {
+ TTY_ENTER_VT,
+ TTY_LEAVE_VT
+};
+
+struct tty *
+tty_create(struct weston_compositor *compositor, int tty_nr);
+
+void
+tty_destroy(struct tty *tty);
+
+void
+tty_reset(struct tty *tty);
+
+int
+tty_activate_vt(struct tty *tty, int vt);
+
+void
+screenshooter_create(struct weston_compositor *ec);
+
+struct clipboard *
+clipboard_create(struct weston_seat *seat);
+
+void
+text_cursor_position_notifier_create(struct weston_compositor *ec);
+
+int
+text_backend_init(struct weston_compositor *ec);
+
+struct weston_process;
+typedef void (*weston_process_cleanup_func_t)(struct weston_process *process,
+ int status);
+
+struct weston_process {
+ pid_t pid;
+ weston_process_cleanup_func_t cleanup;
+ struct wl_list link;
+};
+
+struct wl_client *
+weston_client_launch(struct weston_compositor *compositor,
+ struct weston_process *proc,
+ const char *path,
+ weston_process_cleanup_func_t cleanup);
+
+void
+weston_watch_process(struct weston_process *process);
+
+struct weston_surface_animation;
+typedef void (*weston_surface_animation_done_func_t)(struct weston_surface_animation *animation, void *data);
+
+struct weston_surface_animation *
+weston_zoom_run(struct weston_surface *surface, float start, float stop,
+ weston_surface_animation_done_func_t done, void *data);
+
+struct weston_surface_animation *
+weston_fade_run(struct weston_surface *surface,
+ float start, float end, float k,
+ weston_surface_animation_done_func_t done, void *data);
+void
+weston_fade_update(struct weston_surface_animation *fade, float target);
+
+struct weston_surface_animation *
+weston_slide_run(struct weston_surface *surface, float start, float stop,
+ weston_surface_animation_done_func_t done, void *data);
+
+void
+weston_surface_set_color(struct weston_surface *surface,
+ float red, float green, float blue, float alpha);
+
+void
+weston_surface_destroy(struct weston_surface *surface);
+
+int
+weston_output_switch_mode(struct weston_output *output, struct weston_mode *mode,
+ int32_t scale, enum weston_mode_switch_op op);
+
+int
+noop_renderer_init(struct weston_compositor *ec);
+
+struct weston_compositor *
+backend_init(struct wl_display *display, int *argc, char *argv[],
+ struct weston_config *config);
+
+int
+module_init(struct weston_compositor *compositor,
+ int *argc, char *argv[]);
+
+void
+weston_transformed_coord(int width, int height,
+ enum wl_output_transform transform,
+ int32_t scale,
+ float sx, float sy, float *bx, float *by);
+pixman_box32_t
+weston_transformed_rect(int width, int height,
+ enum wl_output_transform transform,
+ int32_t scale,
+ pixman_box32_t rect);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/data-device.c b/src/data-device.c
new file mode 100644
index 00000000..c3dc0bda
--- /dev/null
+++ b/src/data-device.c
@@ -0,0 +1,677 @@
+/*
+ * Copyright © 2011 Kristian Høgsberg
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * 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.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#include "compositor.h"
+
+struct weston_drag {
+ struct wl_client *client;
+ struct weston_data_source *data_source;
+ struct wl_listener data_source_listener;
+ struct weston_surface *focus;
+ struct wl_resource *focus_resource;
+ struct wl_listener focus_listener;
+ struct weston_pointer_grab grab;
+ struct weston_surface *icon;
+ struct wl_listener icon_destroy_listener;
+ int32_t dx, dy;
+};
+
+static void
+empty_region(pixman_region32_t *region)
+{
+ pixman_region32_fini(region);
+ pixman_region32_init(region);
+}
+
+static void
+data_offer_accept(struct wl_client *client, struct wl_resource *resource,
+ uint32_t serial, const char *mime_type)
+{
+ struct weston_data_offer *offer = wl_resource_get_user_data(resource);
+
+ /* FIXME: Check that client is currently focused by the input
+ * device that is currently dragging this data source. Should
+ * this be a wl_data_device request? */
+
+ if (offer->source)
+ offer->source->accept(offer->source, serial, mime_type);
+}
+
+static void
+data_offer_receive(struct wl_client *client, struct wl_resource *resource,
+ const char *mime_type, int32_t fd)
+{
+ struct weston_data_offer *offer = wl_resource_get_user_data(resource);
+
+ if (offer->source)
+ offer->source->send(offer->source, mime_type, fd);
+ else
+ close(fd);
+}
+
+static void
+data_offer_destroy(struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static const struct wl_data_offer_interface data_offer_interface = {
+ data_offer_accept,
+ data_offer_receive,
+ data_offer_destroy,
+};
+
+static void
+destroy_data_offer(struct wl_resource *resource)
+{
+ struct weston_data_offer *offer = wl_resource_get_user_data(resource);
+
+ if (offer->source)
+ wl_list_remove(&offer->source_destroy_listener.link);
+ free(offer);
+}
+
+static void
+destroy_offer_data_source(struct wl_listener *listener, void *data)
+{
+ struct weston_data_offer *offer;
+
+ offer = container_of(listener, struct weston_data_offer,
+ source_destroy_listener);
+
+ offer->source = NULL;
+}
+
+static struct wl_resource *
+weston_data_source_send_offer(struct weston_data_source *source,
+ struct wl_resource *target)
+{
+ struct weston_data_offer *offer;
+ char **p;
+
+ offer = malloc(sizeof *offer);
+ if (offer == NULL)
+ return NULL;
+
+ offer->resource =
+ wl_resource_create(wl_resource_get_client(target),
+ &wl_data_offer_interface, 1, 0);
+ if (offer->resource == NULL) {
+ free(offer);
+ return NULL;
+ }
+
+ wl_resource_set_implementation(offer->resource, &data_offer_interface,
+ offer, destroy_data_offer);
+
+ offer->source = source;
+ offer->source_destroy_listener.notify = destroy_offer_data_source;
+ wl_signal_add(&source->destroy_signal,
+ &offer->source_destroy_listener);
+
+ wl_data_device_send_data_offer(target, offer->resource);
+
+ wl_array_for_each(p, &source->mime_types)
+ wl_data_offer_send_offer(offer->resource, *p);
+
+ return offer->resource;
+}
+
+static void
+data_source_offer(struct wl_client *client,
+ struct wl_resource *resource,
+ const char *type)
+{
+ struct weston_data_source *source =
+ wl_resource_get_user_data(resource);
+ char **p;
+
+ p = wl_array_add(&source->mime_types, sizeof *p);
+ if (p)
+ *p = strdup(type);
+ if (!p || !*p)
+ wl_resource_post_no_memory(resource);
+}
+
+static void
+data_source_destroy(struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static struct wl_data_source_interface data_source_interface = {
+ data_source_offer,
+ data_source_destroy
+};
+
+static void
+drag_surface_configure(struct weston_surface *es, int32_t sx, int32_t sy, int32_t width, int32_t height)
+{
+ struct weston_drag *drag = es->configure_private;
+ struct weston_pointer *pointer = drag->grab.pointer;
+ struct wl_list *list;
+ float fx, fy;
+
+ if (!weston_surface_is_mapped(es) && es->buffer_ref.buffer) {
+ if (pointer->sprite && weston_surface_is_mapped(pointer->sprite))
+ list = &pointer->sprite->layer_link;
+ else
+ list = &es->compositor->cursor_layer.surface_list;
+
+ wl_list_insert(list, &es->layer_link);
+ weston_surface_update_transform(es);
+ empty_region(&es->pending.input);
+ }
+
+ drag->dx += sx;
+ drag->dy += sy;
+
+ fx = wl_fixed_to_double(pointer->x) + drag->dx;
+ fy = wl_fixed_to_double(pointer->y) + drag->dy;
+ weston_surface_configure(es, fx, fy, width, height);
+}
+
+static void
+destroy_drag_focus(struct wl_listener *listener, void *data)
+{
+ struct weston_drag *drag =
+ container_of(listener, struct weston_drag, focus_listener);
+
+ drag->focus_resource = NULL;
+}
+
+static void
+weston_drag_set_focus(struct weston_drag *drag, struct weston_surface *surface,
+ wl_fixed_t sx, wl_fixed_t sy)
+{
+ struct weston_pointer *pointer = drag->grab.pointer;
+ struct wl_resource *resource, *offer = NULL;
+ struct wl_display *display = pointer->seat->compositor->wl_display;
+ uint32_t serial;
+
+ if (drag->focus_resource) {
+ wl_data_device_send_leave(drag->focus_resource);
+ wl_list_remove(&drag->focus_listener.link);
+ drag->focus_resource = NULL;
+ drag->focus = NULL;
+ }
+
+ if (!surface)
+ return;
+
+ if (!drag->data_source &&
+ wl_resource_get_client(surface->resource) != drag->client)
+ return;
+
+ resource = wl_resource_find_for_client(&pointer->seat->drag_resource_list,
+ wl_resource_get_client(surface->resource));
+ if (!resource)
+ return;
+
+ serial = wl_display_next_serial(display);
+
+ if (drag->data_source) {
+ offer = weston_data_source_send_offer(drag->data_source,
+ resource);
+ if (offer == NULL)
+ return;
+ }
+
+ wl_data_device_send_enter(resource, serial, surface->resource,
+ sx, sy, offer);
+
+ drag->focus = surface;
+ drag->focus_listener.notify = destroy_drag_focus;
+ wl_resource_add_destroy_listener(resource, &drag->focus_listener);
+ drag->focus_resource = resource;
+}
+
+static void
+drag_grab_focus(struct weston_pointer_grab *grab)
+{
+ struct weston_drag *drag =
+ container_of(grab, struct weston_drag, grab);
+ struct weston_pointer *pointer = grab->pointer;
+ struct weston_surface *surface;
+ wl_fixed_t sx, sy;
+
+ surface = weston_compositor_pick_surface(pointer->seat->compositor,
+ pointer->x, pointer->y,
+ &sx, &sy);
+ if (drag->focus != surface)
+ weston_drag_set_focus(drag, surface, sx, sy);
+}
+
+static void
+drag_grab_motion(struct weston_pointer_grab *grab, uint32_t time)
+{
+ struct weston_drag *drag =
+ container_of(grab, struct weston_drag, grab);
+ struct weston_pointer *pointer = drag->grab.pointer;
+ float fx, fy;
+ wl_fixed_t sx, sy;
+
+ if (drag->icon) {
+ fx = wl_fixed_to_double(pointer->x) + drag->dx;
+ fy = wl_fixed_to_double(pointer->y) + drag->dy;
+ weston_surface_set_position(drag->icon, fx, fy);
+ weston_surface_schedule_repaint(drag->icon);
+ }
+
+ if (drag->focus_resource) {
+ weston_surface_from_global_fixed(drag->focus,
+ pointer->x, pointer->y,
+ &sx, &sy);
+
+ wl_data_device_send_motion(drag->focus_resource, time, sx, sy);
+ }
+}
+
+static void
+data_device_end_drag_grab(struct weston_drag *drag)
+{
+ if (drag->icon) {
+ if (weston_surface_is_mapped(drag->icon))
+ weston_surface_unmap(drag->icon);
+
+ drag->icon->configure = NULL;
+ empty_region(&drag->icon->pending.input);
+ wl_list_remove(&drag->icon_destroy_listener.link);
+ }
+
+ weston_drag_set_focus(drag, NULL, 0, 0);
+
+ weston_pointer_end_grab(drag->grab.pointer);
+
+ free(drag);
+}
+
+static void
+drag_grab_button(struct weston_pointer_grab *grab,
+ uint32_t time, uint32_t button, uint32_t state_w)
+{
+ struct weston_drag *drag =
+ container_of(grab, struct weston_drag, grab);
+ struct weston_pointer *pointer = drag->grab.pointer;
+ enum wl_pointer_button_state state = state_w;
+
+ if (drag->focus_resource &&
+ pointer->grab_button == button &&
+ state == WL_POINTER_BUTTON_STATE_RELEASED)
+ wl_data_device_send_drop(drag->focus_resource);
+
+ if (pointer->button_count == 0 &&
+ state == WL_POINTER_BUTTON_STATE_RELEASED) {
+ if (drag->data_source)
+ wl_list_remove(&drag->data_source_listener.link);
+ data_device_end_drag_grab(drag);
+ }
+}
+
+static void
+drag_grab_cancel(struct weston_pointer_grab *grab)
+{
+ struct weston_drag *drag =
+ container_of(grab, struct weston_drag, grab);
+
+ if (drag->data_source)
+ wl_list_remove(&drag->data_source_listener.link);
+
+ data_device_end_drag_grab(drag);
+}
+
+static const struct weston_pointer_grab_interface drag_grab_interface = {
+ drag_grab_focus,
+ drag_grab_motion,
+ drag_grab_button,
+ drag_grab_cancel,
+};
+
+static void
+destroy_data_device_source(struct wl_listener *listener, void *data)
+{
+ struct weston_drag *drag = container_of(listener, struct weston_drag,
+ data_source_listener);
+
+ data_device_end_drag_grab(drag);
+}
+
+static void
+handle_drag_icon_destroy(struct wl_listener *listener, void *data)
+{
+ struct weston_drag *drag = container_of(listener, struct weston_drag,
+ icon_destroy_listener);
+
+ drag->icon = NULL;
+}
+
+WL_EXPORT int
+weston_seat_start_drag(struct weston_seat *seat,
+ struct weston_data_source *source,
+ struct weston_surface *icon,
+ struct wl_client *client)
+{
+ struct weston_drag *drag;
+
+ drag = zalloc(sizeof *drag);
+ if (drag == NULL)
+ return -1;
+
+ drag->grab.interface = &drag_grab_interface;
+ drag->client = client;
+ drag->data_source = source;
+ drag->icon = icon;
+
+ if (source) {
+ drag->data_source_listener.notify = destroy_data_device_source;
+ wl_signal_add(&source->destroy_signal,
+ &drag->data_source_listener);
+ }
+
+ if (icon) {
+ drag->icon_destroy_listener.notify = handle_drag_icon_destroy;
+ wl_signal_add(&icon->destroy_signal,
+ &drag->icon_destroy_listener);
+
+ icon->configure = drag_surface_configure;
+ icon->configure_private = drag;
+ }
+
+ weston_pointer_set_focus(seat->pointer, NULL,
+ wl_fixed_from_int(0), wl_fixed_from_int(0));
+ weston_pointer_start_grab(seat->pointer, &drag->grab);
+
+ return 0;
+}
+
+static void
+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 weston_seat *seat = wl_resource_get_user_data(resource);
+ struct weston_data_source *source = NULL;
+ struct weston_surface *icon = NULL;
+
+ if (seat->pointer->button_count == 0 ||
+ seat->pointer->grab_serial != serial ||
+ seat->pointer->focus != wl_resource_get_user_data(origin_resource))
+ return;
+
+ /* FIXME: Check that the data source type array isn't empty. */
+
+ if (source_resource)
+ source = wl_resource_get_user_data(source_resource);
+ if (icon_resource)
+ icon = wl_resource_get_user_data(icon_resource);
+ if (icon && icon->configure) {
+ wl_resource_post_error(icon_resource,
+ WL_DISPLAY_ERROR_INVALID_OBJECT,
+ "surface->configure already set");
+ return;
+ }
+
+ if (weston_seat_start_drag(seat, source, icon, client) < 0)
+ wl_resource_post_no_memory(resource);
+}
+
+static void
+destroy_selection_data_source(struct wl_listener *listener, void *data)
+{
+ struct weston_seat *seat = container_of(listener, struct weston_seat,
+ selection_data_source_listener);
+ struct wl_resource *data_device;
+ struct weston_surface *focus = NULL;
+
+ seat->selection_data_source = NULL;
+
+ if (seat->keyboard)
+ focus = seat->keyboard->focus;
+ if (focus && focus->resource) {
+ data_device = wl_resource_find_for_client(&seat->drag_resource_list,
+ wl_resource_get_client(focus->resource));
+ if (data_device)
+ wl_data_device_send_selection(data_device, NULL);
+ }
+
+ wl_signal_emit(&seat->selection_signal, seat);
+}
+
+WL_EXPORT void
+weston_seat_set_selection(struct weston_seat *seat,
+ struct weston_data_source *source, uint32_t serial)
+{
+ struct wl_resource *data_device, *offer;
+ struct weston_surface *focus = NULL;
+
+ if (seat->selection_data_source &&
+ seat->selection_serial - serial < UINT32_MAX / 2)
+ return;
+
+ if (seat->selection_data_source) {
+ seat->selection_data_source->cancel(seat->selection_data_source);
+ wl_list_remove(&seat->selection_data_source_listener.link);
+ seat->selection_data_source = NULL;
+ }
+
+ seat->selection_data_source = source;
+ seat->selection_serial = serial;
+
+ if (seat->keyboard)
+ focus = seat->keyboard->focus;
+ if (focus && focus->resource) {
+ data_device = wl_resource_find_for_client(&seat->drag_resource_list,
+ wl_resource_get_client(focus->resource));
+ if (data_device && source) {
+ offer = weston_data_source_send_offer(seat->selection_data_source,
+ data_device);
+ wl_data_device_send_selection(data_device, offer);
+ } else if (data_device) {
+ wl_data_device_send_selection(data_device, NULL);
+ }
+ }
+
+ wl_signal_emit(&seat->selection_signal, seat);
+
+ if (source) {
+ seat->selection_data_source_listener.notify =
+ destroy_selection_data_source;
+ wl_signal_add(&source->destroy_signal,
+ &seat->selection_data_source_listener);
+ }
+}
+
+static void
+data_device_set_selection(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *source_resource, uint32_t serial)
+{
+ if (!source_resource)
+ return;
+
+ /* FIXME: Store serial and check against incoming serial here. */
+ weston_seat_set_selection(wl_resource_get_user_data(resource),
+ wl_resource_get_user_data(source_resource),
+ serial);
+}
+
+static const struct wl_data_device_interface data_device_interface = {
+ data_device_start_drag,
+ data_device_set_selection,
+};
+
+static void
+destroy_data_source(struct wl_resource *resource)
+{
+ struct weston_data_source *source =
+ wl_resource_get_user_data(resource);
+ char **p;
+
+ wl_signal_emit(&source->destroy_signal, source);
+
+ wl_array_for_each(p, &source->mime_types)
+ free(*p);
+
+ wl_array_release(&source->mime_types);
+
+ free(source);
+}
+
+static void
+client_source_accept(struct weston_data_source *source,
+ uint32_t time, const char *mime_type)
+{
+ wl_data_source_send_target(source->resource, mime_type);
+}
+
+static void
+client_source_send(struct weston_data_source *source,
+ const char *mime_type, int32_t fd)
+{
+ wl_data_source_send_send(source->resource, mime_type, fd);
+ close(fd);
+}
+
+static void
+client_source_cancel(struct weston_data_source *source)
+{
+ wl_data_source_send_cancelled(source->resource);
+}
+
+static void
+create_data_source(struct wl_client *client,
+ struct wl_resource *resource, uint32_t id)
+{
+ struct weston_data_source *source;
+
+ source = malloc(sizeof *source);
+ if (source == NULL) {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ wl_signal_init(&source->destroy_signal);
+ source->accept = client_source_accept;
+ source->send = client_source_send;
+ source->cancel = client_source_cancel;
+
+ wl_array_init(&source->mime_types);
+
+ source->resource =
+ wl_resource_create(client, &wl_data_source_interface, 1, id);
+ wl_resource_set_implementation(source->resource, &data_source_interface,
+ source, destroy_data_source);
+}
+
+static void unbind_data_device(struct wl_resource *resource)
+{
+ wl_list_remove(wl_resource_get_link(resource));
+}
+
+static void
+get_data_device(struct wl_client *client,
+ struct wl_resource *manager_resource,
+ uint32_t id, struct wl_resource *seat_resource)
+{
+ struct weston_seat *seat = wl_resource_get_user_data(seat_resource);
+ struct wl_resource *resource;
+
+ resource = wl_resource_create(client,
+ &wl_data_device_interface, 1, id);
+ if (resource == NULL) {
+ wl_resource_post_no_memory(manager_resource);
+ return;
+ }
+
+ wl_list_insert(&seat->drag_resource_list,
+ wl_resource_get_link(resource));
+ wl_resource_set_implementation(resource, &data_device_interface,
+ seat, unbind_data_device);
+}
+
+static const struct wl_data_device_manager_interface manager_interface = {
+ create_data_source,
+ get_data_device
+};
+
+static void
+bind_manager(struct wl_client *client,
+ void *data, uint32_t version, uint32_t id)
+{
+ struct wl_resource *resource;
+
+ resource =
+ wl_resource_create(client,
+ &wl_data_device_manager_interface, 1, id);
+ if (resource == NULL) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+
+ wl_resource_set_implementation(resource, &manager_interface,
+ NULL, NULL);
+}
+
+WL_EXPORT void
+wl_data_device_set_keyboard_focus(struct weston_seat *seat)
+{
+ struct wl_resource *data_device, *offer;
+ struct weston_data_source *source;
+ struct weston_surface *focus;
+
+ if (!seat->keyboard)
+ return;
+
+ focus = seat->keyboard->focus;
+ if (!focus || !focus->resource)
+ return;
+
+ data_device = wl_resource_find_for_client(&seat->drag_resource_list,
+ wl_resource_get_client(focus->resource));
+ if (!data_device)
+ return;
+
+ source = seat->selection_data_source;
+ if (source) {
+ offer = weston_data_source_send_offer(source, data_device);
+ wl_data_device_send_selection(data_device, offer);
+ }
+}
+
+WL_EXPORT int
+wl_data_device_manager_init(struct wl_display *display)
+{
+ if (wl_global_create(display,
+ &wl_data_device_manager_interface, 1,
+ NULL, bind_manager) == NULL)
+ return -1;
+
+ return 0;
+}
diff --git a/src/evdev-touchpad.c b/src/evdev-touchpad.c
new file mode 100644
index 00000000..69f913ac
--- /dev/null
+++ b/src/evdev-touchpad.c
@@ -0,0 +1,800 @@
+/*
+ * Copyright © 2012 Jonas Ådahl
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that 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.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <linux/input.h>
+
+#include "filter.h"
+#include "evdev.h"
+#include "../shared/config-parser.h"
+
+/* Default values */
+#define DEFAULT_CONSTANT_ACCEL_NUMERATOR 50
+#define DEFAULT_MIN_ACCEL_FACTOR 0.16
+#define DEFAULT_MAX_ACCEL_FACTOR 1.0
+#define DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR 700.0
+
+#define DEFAULT_TOUCHPAD_SINGLE_TAP_BUTTON BTN_LEFT
+#define DEFAULT_TOUCHPAD_SINGLE_TAP_TIMEOUT 100
+
+enum touchpad_model {
+ TOUCHPAD_MODEL_UNKNOWN = 0,
+ TOUCHPAD_MODEL_SYNAPTICS,
+ TOUCHPAD_MODEL_ALPS,
+ TOUCHPAD_MODEL_APPLETOUCH,
+ TOUCHPAD_MODEL_ELANTECH
+};
+
+enum touchpad_event {
+ TOUCHPAD_EVENT_NONE = 0,
+ TOUCHPAD_EVENT_ABSOLUTE_ANY = (1 << 0),
+ TOUCHPAD_EVENT_ABSOLUTE_X = (1 << 1),
+ TOUCHPAD_EVENT_ABSOLUTE_Y = (1 << 2),
+ TOUCHPAD_EVENT_REPORT = (1 << 3)
+};
+
+struct touchpad_model_spec {
+ short vendor;
+ short product;
+ enum touchpad_model model;
+};
+
+static struct touchpad_model_spec touchpad_spec_table[] = {
+ {0x0002, 0x0007, TOUCHPAD_MODEL_SYNAPTICS},
+ {0x0002, 0x0008, TOUCHPAD_MODEL_ALPS},
+ {0x05ac, 0x0000, TOUCHPAD_MODEL_APPLETOUCH},
+ {0x0002, 0x000e, TOUCHPAD_MODEL_ELANTECH},
+ {0x0000, 0x0000, TOUCHPAD_MODEL_UNKNOWN}
+};
+
+enum touchpad_state {
+ TOUCHPAD_STATE_NONE = 0,
+ TOUCHPAD_STATE_TOUCH = (1 << 0),
+ TOUCHPAD_STATE_MOVE = (1 << 1)
+};
+
+#define TOUCHPAD_HISTORY_LENGTH 4
+
+struct touchpad_motion {
+ int32_t x;
+ int32_t y;
+};
+
+enum touchpad_fingers_state {
+ TOUCHPAD_FINGERS_ONE = (1 << 0),
+ TOUCHPAD_FINGERS_TWO = (1 << 1),
+ TOUCHPAD_FINGERS_THREE = (1 << 2)
+};
+
+enum fsm_event {
+ FSM_EVENT_TOUCH,
+ FSM_EVENT_RELEASE,
+ FSM_EVENT_MOTION,
+ FSM_EVENT_TIMEOUT
+};
+
+enum fsm_state {
+ FSM_IDLE,
+ FSM_TOUCH,
+ FSM_TAP,
+ FSM_TAP_2,
+ FSM_DRAG
+};
+
+struct touchpad_dispatch {
+ struct evdev_dispatch base;
+ struct evdev_device *device;
+
+ enum touchpad_model model;
+ unsigned int state;
+ int finger_state;
+ int last_finger_state;
+
+ double constant_accel_factor;
+ double min_accel_factor;
+ double max_accel_factor;
+
+ unsigned int event_mask;
+ unsigned int event_mask_filter;
+
+ int reset;
+
+ struct {
+ bool enable;
+
+ struct wl_array events;
+ enum fsm_state state;
+ struct wl_event_source *timer_source;
+ } fsm;
+
+ struct {
+ int32_t x;
+ int32_t y;
+ } hw_abs;
+
+ int has_pressure;
+ struct {
+ int32_t touch_low;
+ int32_t touch_high;
+ } pressure;
+
+ struct {
+ int32_t margin_x;
+ int32_t margin_y;
+ int32_t center_x;
+ int32_t center_y;
+ } hysteresis;
+
+ struct touchpad_motion motion_history[TOUCHPAD_HISTORY_LENGTH];
+ int motion_index;
+ unsigned int motion_count;
+
+ struct weston_motion_filter *filter;
+};
+
+static enum touchpad_model
+get_touchpad_model(struct evdev_device *device)
+{
+ struct input_id id;
+ unsigned int i;
+
+ if (ioctl(device->fd, EVIOCGID, &id) < 0)
+ return TOUCHPAD_MODEL_UNKNOWN;
+
+ for (i = 0; i < ARRAY_LENGTH(touchpad_spec_table); i++)
+ if (touchpad_spec_table[i].vendor == id.vendor &&
+ (!touchpad_spec_table[i].product ||
+ touchpad_spec_table[i].product == id.product))
+ return touchpad_spec_table[i].model;
+
+ return TOUCHPAD_MODEL_UNKNOWN;
+}
+
+static void
+configure_touchpad_pressure(struct touchpad_dispatch *touchpad,
+ int32_t pressure_min, int32_t pressure_max)
+{
+ int32_t range = pressure_max - pressure_min + 1;
+
+ touchpad->has_pressure = 1;
+
+ /* Magic numbers from xf86-input-synaptics */
+ switch (touchpad->model) {
+ case TOUCHPAD_MODEL_ELANTECH:
+ touchpad->pressure.touch_low = pressure_min + 1;
+ touchpad->pressure.touch_high = pressure_min + 1;
+ break;
+ default:
+ touchpad->pressure.touch_low =
+ pressure_min + range * (25.0/256.0);
+ touchpad->pressure.touch_high =
+ pressure_min + range * (30.0/256.0);
+ }
+}
+
+static double
+touchpad_profile(struct weston_motion_filter *filter,
+ void *data,
+ double velocity,
+ uint32_t time)
+{
+ struct touchpad_dispatch *touchpad =
+ (struct touchpad_dispatch *) data;
+
+ double accel_factor;
+
+ accel_factor = velocity * touchpad->constant_accel_factor;
+
+ if (accel_factor > touchpad->max_accel_factor)
+ accel_factor = touchpad->max_accel_factor;
+ else if (accel_factor < touchpad->min_accel_factor)
+ accel_factor = touchpad->min_accel_factor;
+
+ return accel_factor;
+}
+
+static inline struct touchpad_motion *
+motion_history_offset(struct touchpad_dispatch *touchpad, int offset)
+{
+ int offset_index =
+ (touchpad->motion_index - offset + TOUCHPAD_HISTORY_LENGTH) %
+ TOUCHPAD_HISTORY_LENGTH;
+
+ return &touchpad->motion_history[offset_index];
+}
+
+static double
+estimate_delta(int x0, int x1, int x2, int x3)
+{
+ return (x0 + x1 - x2 - x3) / 4;
+}
+
+static int
+hysteresis(int in, int center, int margin)
+{
+ int diff = in - center;
+ if (abs(diff) <= margin)
+ return center;
+
+ if (diff > margin)
+ return center + diff - margin;
+ else if (diff < -margin)
+ return center + diff + margin;
+ return center + diff;
+}
+
+static void
+touchpad_get_delta(struct touchpad_dispatch *touchpad, double *dx, double *dy)
+{
+ *dx = estimate_delta(motion_history_offset(touchpad, 0)->x,
+ motion_history_offset(touchpad, 1)->x,
+ motion_history_offset(touchpad, 2)->x,
+ motion_history_offset(touchpad, 3)->x);
+ *dy = estimate_delta(motion_history_offset(touchpad, 0)->y,
+ motion_history_offset(touchpad, 1)->y,
+ motion_history_offset(touchpad, 2)->y,
+ motion_history_offset(touchpad, 3)->y);
+}
+
+static void
+filter_motion(struct touchpad_dispatch *touchpad,
+ double *dx, double *dy, uint32_t time)
+{
+ struct weston_motion_params motion;
+
+ motion.dx = *dx;
+ motion.dy = *dy;
+
+ weston_filter_dispatch(touchpad->filter, &motion, touchpad, time);
+
+ *dx = motion.dx;
+ *dy = motion.dy;
+}
+
+static void
+notify_button_pressed(struct touchpad_dispatch *touchpad, uint32_t time)
+{
+ notify_button(touchpad->device->seat, time,
+ DEFAULT_TOUCHPAD_SINGLE_TAP_BUTTON,
+ WL_POINTER_BUTTON_STATE_PRESSED);
+}
+
+static void
+notify_button_released(struct touchpad_dispatch *touchpad, uint32_t time)
+{
+ notify_button(touchpad->device->seat, time,
+ DEFAULT_TOUCHPAD_SINGLE_TAP_BUTTON,
+ WL_POINTER_BUTTON_STATE_RELEASED);
+}
+
+static void
+notify_tap(struct touchpad_dispatch *touchpad, uint32_t time)
+{
+ notify_button_pressed(touchpad, time);
+ notify_button_released(touchpad, time);
+}
+
+static void
+process_fsm_events(struct touchpad_dispatch *touchpad, uint32_t time)
+{
+ uint32_t timeout = UINT32_MAX;
+ enum fsm_event *pevent;
+ enum fsm_event event;
+
+ if (!touchpad->fsm.enable)
+ return;
+
+ if (touchpad->fsm.events.size == 0)
+ return;
+
+ wl_array_for_each(pevent, &touchpad->fsm.events) {
+ event = *pevent;
+ timeout = 0;
+
+ switch (touchpad->fsm.state) {
+ case FSM_IDLE:
+ switch (event) {
+ case FSM_EVENT_TOUCH:
+ touchpad->fsm.state = FSM_TOUCH;
+ break;
+ default:
+ break;
+ }
+ break;
+ case FSM_TOUCH:
+ switch (event) {
+ case FSM_EVENT_RELEASE:
+ timeout = DEFAULT_TOUCHPAD_SINGLE_TAP_TIMEOUT;
+ touchpad->fsm.state = FSM_TAP;
+ break;
+ default:
+ touchpad->fsm.state = FSM_IDLE;
+ break;
+ }
+ break;
+ case FSM_TAP:
+ switch (event) {
+ case FSM_EVENT_TIMEOUT:
+ notify_tap(touchpad, time);
+ touchpad->fsm.state = FSM_IDLE;
+ break;
+ case FSM_EVENT_TOUCH:
+ notify_button_pressed(touchpad, time);
+ touchpad->fsm.state = FSM_TAP_2;
+ break;
+ default:
+ touchpad->fsm.state = FSM_IDLE;
+ break;
+ }
+ break;
+ case FSM_TAP_2:
+ switch (event) {
+ case FSM_EVENT_MOTION:
+ touchpad->fsm.state = FSM_DRAG;
+ break;
+ case FSM_EVENT_RELEASE:
+ notify_button_released(touchpad, time);
+ notify_tap(touchpad, time);
+ touchpad->fsm.state = FSM_IDLE;
+ break;
+ default:
+ touchpad->fsm.state = FSM_IDLE;
+ break;
+ }
+ break;
+ case FSM_DRAG:
+ switch (event) {
+ case FSM_EVENT_RELEASE:
+ notify_button_released(touchpad, time);
+ touchpad->fsm.state = FSM_IDLE;
+ break;
+ default:
+ touchpad->fsm.state = FSM_IDLE;
+ break;
+ }
+ break;
+ default:
+ weston_log("evdev-touchpad: Unknown state %d",
+ touchpad->fsm.state);
+ touchpad->fsm.state = FSM_IDLE;
+ break;
+ }
+ }
+
+ if (timeout != UINT32_MAX)
+ wl_event_source_timer_update(touchpad->fsm.timer_source,
+ timeout);
+
+ wl_array_release(&touchpad->fsm.events);
+ wl_array_init(&touchpad->fsm.events);
+}
+
+static void
+push_fsm_event(struct touchpad_dispatch *touchpad,
+ enum fsm_event event)
+{
+ enum fsm_event *pevent;
+
+ if (!touchpad->fsm.enable)
+ return;
+
+ pevent = wl_array_add(&touchpad->fsm.events, sizeof event);
+ if (pevent)
+ *pevent = event;
+ else
+ touchpad->fsm.state = FSM_IDLE;
+}
+
+static int
+fsm_timout_handler(void *data)
+{
+ struct touchpad_dispatch *touchpad = data;
+
+ if (touchpad->fsm.events.size == 0) {
+ push_fsm_event(touchpad, FSM_EVENT_TIMEOUT);
+ process_fsm_events(touchpad, weston_compositor_get_time());
+ }
+
+ return 1;
+}
+
+static void
+touchpad_update_state(struct touchpad_dispatch *touchpad, uint32_t time)
+{
+ int motion_index;
+ int center_x, center_y;
+ double dx = 0.0, dy = 0.0;
+
+ if (touchpad->reset ||
+ touchpad->last_finger_state != touchpad->finger_state) {
+ touchpad->reset = 0;
+ touchpad->motion_count = 0;
+ touchpad->event_mask = TOUCHPAD_EVENT_NONE;
+ touchpad->event_mask_filter =
+ TOUCHPAD_EVENT_ABSOLUTE_X | TOUCHPAD_EVENT_ABSOLUTE_Y;
+
+ touchpad->last_finger_state = touchpad->finger_state;
+
+ process_fsm_events(touchpad, time);
+
+ return;
+ }
+ touchpad->last_finger_state = touchpad->finger_state;
+
+ if (!(touchpad->event_mask & TOUCHPAD_EVENT_REPORT))
+ return;
+ else
+ touchpad->event_mask &= ~TOUCHPAD_EVENT_REPORT;
+
+ if ((touchpad->event_mask & touchpad->event_mask_filter) !=
+ touchpad->event_mask_filter)
+ return;
+
+ touchpad->event_mask_filter = TOUCHPAD_EVENT_ABSOLUTE_ANY;
+ touchpad->event_mask = 0;
+
+ /* Avoid noice by moving center only when delta reaches a threshold
+ * distance from the old center. */
+ if (touchpad->motion_count > 0) {
+ center_x = hysteresis(touchpad->hw_abs.x,
+ touchpad->hysteresis.center_x,
+ touchpad->hysteresis.margin_x);
+ center_y = hysteresis(touchpad->hw_abs.y,
+ touchpad->hysteresis.center_y,
+ touchpad->hysteresis.margin_y);
+ } else {
+ center_x = touchpad->hw_abs.x;
+ center_y = touchpad->hw_abs.y;
+ }
+ touchpad->hysteresis.center_x = center_x;
+ touchpad->hysteresis.center_y = center_y;
+ touchpad->hw_abs.x = center_x;
+ touchpad->hw_abs.y = center_y;
+
+ /* Update motion history tracker */
+ motion_index = (touchpad->motion_index + 1) % TOUCHPAD_HISTORY_LENGTH;
+ touchpad->motion_index = motion_index;
+ touchpad->motion_history[motion_index].x = touchpad->hw_abs.x;
+ touchpad->motion_history[motion_index].y = touchpad->hw_abs.y;
+ if (touchpad->motion_count < 4)
+ touchpad->motion_count++;
+
+ if (touchpad->motion_count >= 4) {
+ touchpad_get_delta(touchpad, &dx, &dy);
+
+ filter_motion(touchpad, &dx, &dy, time);
+
+ if (touchpad->finger_state == TOUCHPAD_FINGERS_ONE) {
+ notify_motion(touchpad->device->seat, time,
+ wl_fixed_from_double(dx),
+ wl_fixed_from_double(dy));
+ } else if (touchpad->finger_state == TOUCHPAD_FINGERS_TWO) {
+ if (dx != 0.0)
+ notify_axis(touchpad->device->seat,
+ time,
+ WL_POINTER_AXIS_HORIZONTAL_SCROLL,
+ wl_fixed_from_double(dx));
+ if (dy != 0.0)
+ notify_axis(touchpad->device->seat,
+ time,
+ WL_POINTER_AXIS_VERTICAL_SCROLL,
+ wl_fixed_from_double(dy));
+ }
+ }
+
+ if (!(touchpad->state & TOUCHPAD_STATE_MOVE) &&
+ ((int)dx || (int)dy)) {
+ touchpad->state |= TOUCHPAD_STATE_MOVE;
+ push_fsm_event(touchpad, FSM_EVENT_MOTION);
+ }
+
+ process_fsm_events(touchpad, time);
+}
+
+static void
+on_touch(struct touchpad_dispatch *touchpad)
+{
+ touchpad->state |= TOUCHPAD_STATE_TOUCH;
+
+ push_fsm_event(touchpad, FSM_EVENT_TOUCH);
+}
+
+static void
+on_release(struct touchpad_dispatch *touchpad)
+{
+
+ touchpad->reset = 1;
+ touchpad->state &= ~(TOUCHPAD_STATE_MOVE | TOUCHPAD_STATE_TOUCH);
+
+ push_fsm_event(touchpad, FSM_EVENT_RELEASE);
+}
+
+static inline void
+process_absolute(struct touchpad_dispatch *touchpad,
+ struct evdev_device *device,
+ struct input_event *e)
+{
+ switch (e->code) {
+ case ABS_PRESSURE:
+ if (e->value > touchpad->pressure.touch_high &&
+ !(touchpad->state & TOUCHPAD_STATE_TOUCH))
+ on_touch(touchpad);
+ else if (e->value < touchpad->pressure.touch_low &&
+ touchpad->state & TOUCHPAD_STATE_TOUCH)
+ on_release(touchpad);
+
+ break;
+ case ABS_X:
+ if (touchpad->state & TOUCHPAD_STATE_TOUCH) {
+ touchpad->hw_abs.x = e->value;
+ touchpad->event_mask |= TOUCHPAD_EVENT_ABSOLUTE_ANY;
+ touchpad->event_mask |= TOUCHPAD_EVENT_ABSOLUTE_X;
+ }
+ break;
+ case ABS_Y:
+ if (touchpad->state & TOUCHPAD_STATE_TOUCH) {
+ touchpad->hw_abs.y = e->value;
+ touchpad->event_mask |= TOUCHPAD_EVENT_ABSOLUTE_ANY;
+ touchpad->event_mask |= TOUCHPAD_EVENT_ABSOLUTE_Y;
+ }
+ break;
+ }
+}
+
+static inline void
+process_key(struct touchpad_dispatch *touchpad,
+ struct evdev_device *device,
+ struct input_event *e,
+ uint32_t time)
+{
+ uint32_t code;
+
+ switch (e->code) {
+ case BTN_TOUCH:
+ if (!touchpad->has_pressure) {
+ if (e->value && !(touchpad->state & TOUCHPAD_STATE_TOUCH))
+ on_touch(touchpad);
+ else if (!e->value)
+ on_release(touchpad);
+ }
+ break;
+ case BTN_LEFT:
+ case BTN_RIGHT:
+ case BTN_MIDDLE:
+ case BTN_SIDE:
+ case BTN_EXTRA:
+ case BTN_FORWARD:
+ case BTN_BACK:
+ case BTN_TASK:
+ if (!touchpad->fsm.enable && e->code == BTN_LEFT &&
+ touchpad->finger_state == TOUCHPAD_FINGERS_TWO)
+ code = BTN_RIGHT;
+ else
+ code = e->code;
+ notify_button(device->seat, time, code,
+ e->value ? WL_POINTER_BUTTON_STATE_PRESSED :
+ WL_POINTER_BUTTON_STATE_RELEASED);
+ break;
+ case BTN_TOOL_PEN:
+ case BTN_TOOL_RUBBER:
+ case BTN_TOOL_BRUSH:
+ case BTN_TOOL_PENCIL:
+ case BTN_TOOL_AIRBRUSH:
+ case BTN_TOOL_MOUSE:
+ case BTN_TOOL_LENS:
+ touchpad->reset = 1;
+ break;
+ case BTN_TOOL_FINGER:
+ if (e->value)
+ touchpad->finger_state |= TOUCHPAD_FINGERS_ONE;
+ else
+ touchpad->finger_state &= ~TOUCHPAD_FINGERS_ONE;
+ break;
+ case BTN_TOOL_DOUBLETAP:
+ if (e->value)
+ touchpad->finger_state |= TOUCHPAD_FINGERS_TWO;
+ else
+ touchpad->finger_state &= ~TOUCHPAD_FINGERS_TWO;
+ break;
+ case BTN_TOOL_TRIPLETAP:
+ if (e->value)
+ touchpad->finger_state |= TOUCHPAD_FINGERS_THREE;
+ else
+ touchpad->finger_state &= ~TOUCHPAD_FINGERS_THREE;
+ break;
+ }
+}
+
+static void
+touchpad_process(struct evdev_dispatch *dispatch,
+ struct evdev_device *device,
+ struct input_event *e,
+ uint32_t time)
+{
+ struct touchpad_dispatch *touchpad =
+ (struct touchpad_dispatch *) dispatch;
+
+ switch (e->type) {
+ case EV_SYN:
+ if (e->code == SYN_REPORT)
+ touchpad->event_mask |= TOUCHPAD_EVENT_REPORT;
+ break;
+ case EV_ABS:
+ process_absolute(touchpad, device, e);
+ break;
+ case EV_KEY:
+ process_key(touchpad, device, e, time);
+ break;
+ }
+
+ touchpad_update_state(touchpad, time);
+}
+
+static void
+touchpad_destroy(struct evdev_dispatch *dispatch)
+{
+ struct touchpad_dispatch *touchpad =
+ (struct touchpad_dispatch *) dispatch;
+
+ touchpad->filter->interface->destroy(touchpad->filter);
+ wl_event_source_remove(touchpad->fsm.timer_source);
+ free(dispatch);
+}
+
+struct evdev_dispatch_interface touchpad_interface = {
+ touchpad_process,
+ touchpad_destroy
+};
+
+static void
+touchpad_parse_config(struct touchpad_dispatch *touchpad, double diagonal)
+{
+ struct weston_compositor *compositor =
+ touchpad->device->seat->compositor;
+ struct weston_config_section *s;
+ double constant_accel_factor;
+ double min_accel_factor;
+ double max_accel_factor;
+
+ s = weston_config_get_section(compositor->config,
+ "touchpad", NULL, NULL);
+ weston_config_section_get_double(s, "constant_accel_factor",
+ &constant_accel_factor,
+ DEFAULT_CONSTANT_ACCEL_NUMERATOR);
+ weston_config_section_get_double(s, "min_accel_factor",
+ &min_accel_factor,
+ DEFAULT_MIN_ACCEL_FACTOR);
+ weston_config_section_get_double(s, "max_accel_factor",
+ &max_accel_factor,
+ DEFAULT_MAX_ACCEL_FACTOR);
+
+ touchpad->constant_accel_factor =
+ constant_accel_factor / diagonal;
+ touchpad->min_accel_factor = min_accel_factor;
+ touchpad->max_accel_factor = max_accel_factor;
+}
+
+static int
+touchpad_init(struct touchpad_dispatch *touchpad,
+ struct evdev_device *device)
+{
+ struct weston_motion_filter *accel;
+ struct wl_event_loop *loop;
+
+ unsigned long prop_bits[INPUT_PROP_MAX];
+ struct input_absinfo absinfo;
+ unsigned long abs_bits[NBITS(ABS_MAX)];
+
+ bool has_buttonpad;
+
+ double width;
+ double height;
+ double diagonal;
+
+ touchpad->base.interface = &touchpad_interface;
+ touchpad->device = device;
+
+ /* Detect model */
+ touchpad->model = get_touchpad_model(device);
+
+ ioctl(device->fd, EVIOCGPROP(sizeof(prop_bits)), prop_bits);
+ has_buttonpad = TEST_BIT(prop_bits, INPUT_PROP_BUTTONPAD);
+
+ /* Configure pressure */
+ ioctl(device->fd, EVIOCGBIT(EV_ABS, sizeof(abs_bits)), abs_bits);
+ if (TEST_BIT(abs_bits, ABS_PRESSURE)) {
+ ioctl(device->fd, EVIOCGABS(ABS_PRESSURE), &absinfo);
+ configure_touchpad_pressure(touchpad,
+ absinfo.minimum,
+ absinfo.maximum);
+ }
+
+ /* Configure acceleration factor */
+ width = abs(device->abs.max_x - device->abs.min_x);
+ height = abs(device->abs.max_y - device->abs.min_y);
+ diagonal = sqrt(width*width + height*height);
+
+ touchpad_parse_config(touchpad, diagonal);
+
+ touchpad->hysteresis.margin_x =
+ diagonal / DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR;
+ touchpad->hysteresis.margin_y =
+ diagonal / DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR;
+ touchpad->hysteresis.center_x = 0;
+ touchpad->hysteresis.center_y = 0;
+
+ /* Configure acceleration profile */
+ accel = create_pointer_accelator_filter(touchpad_profile);
+ if (accel == NULL)
+ return -1;
+ touchpad->filter = accel;
+
+ /* Setup initial state */
+ touchpad->reset = 1;
+
+ memset(touchpad->motion_history, 0, sizeof touchpad->motion_history);
+ touchpad->motion_index = 0;
+ touchpad->motion_count = 0;
+
+ touchpad->state = TOUCHPAD_STATE_NONE;
+ touchpad->last_finger_state = 0;
+ touchpad->finger_state = 0;
+
+ wl_array_init(&touchpad->fsm.events);
+ touchpad->fsm.state = FSM_IDLE;
+
+ loop = wl_display_get_event_loop(device->seat->compositor->wl_display);
+ touchpad->fsm.timer_source =
+ wl_event_loop_add_timer(loop, fsm_timout_handler, touchpad);
+ if (touchpad->fsm.timer_source == NULL) {
+ accel->interface->destroy(accel);
+ return -1;
+ }
+
+ /* Configure */
+ touchpad->fsm.enable = !has_buttonpad;
+
+ return 0;
+}
+
+struct evdev_dispatch *
+evdev_touchpad_create(struct evdev_device *device)
+{
+ struct touchpad_dispatch *touchpad;
+
+ touchpad = malloc(sizeof *touchpad);
+ if (touchpad == NULL)
+ return NULL;
+
+ if (touchpad_init(touchpad, device) != 0) {
+ free(touchpad);
+ return NULL;
+ }
+
+ return &touchpad->base;
+}
diff --git a/src/evdev.c b/src/evdev.c
new file mode 100644
index 00000000..eb7631a7
--- /dev/null
+++ b/src/evdev.c
@@ -0,0 +1,719 @@
+/*
+ * Copyright © 2010 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 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.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <linux/input.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <mtdev.h>
+#include <assert.h>
+
+#include "compositor.h"
+#include "evdev.h"
+
+#define DEFAULT_AXIS_STEP_DISTANCE wl_fixed_from_int(10)
+
+void
+evdev_led_update(struct evdev_device *device, enum weston_led leds)
+{
+ static const struct {
+ enum weston_led weston;
+ int evdev;
+ } map[] = {
+ { LED_NUM_LOCK, LED_NUML },
+ { LED_CAPS_LOCK, LED_CAPSL },
+ { LED_SCROLL_LOCK, LED_SCROLLL },
+ };
+ struct input_event ev[ARRAY_LENGTH(map) + 1];
+ unsigned int i;
+
+ if (!device->caps & EVDEV_KEYBOARD)
+ return;
+
+ memset(ev, 0, sizeof(ev));
+ for (i = 0; i < ARRAY_LENGTH(map); i++) {
+ ev[i].type = EV_LED;
+ ev[i].code = map[i].evdev;
+ ev[i].value = !!(leds & map[i].weston);
+ }
+ ev[i].type = EV_SYN;
+ ev[i].code = SYN_REPORT;
+
+ i = write(device->fd, ev, sizeof ev);
+ (void)i; /* no, we really don't care about the return value */
+}
+
+static void
+transform_absolute(struct evdev_device *device, int32_t *x, int32_t *y)
+{
+ if (!device->abs.apply_calibration) {
+ *x = device->abs.x;
+ *y = device->abs.y;
+ return;
+ } else {
+ *x = device->abs.x * device->abs.calibration[0] +
+ device->abs.y * device->abs.calibration[1] +
+ device->abs.calibration[2];
+
+ *y = device->abs.x * device->abs.calibration[3] +
+ device->abs.y * device->abs.calibration[4] +
+ device->abs.calibration[5];
+ }
+}
+
+static void
+evdev_flush_pending_event(struct evdev_device *device, uint32_t time)
+{
+ struct weston_seat *master = device->seat;
+ wl_fixed_t x, y;
+ int32_t cx, cy;
+ int slot;
+
+ slot = device->mt.slot;
+
+ switch (device->pending_event) {
+ case EVDEV_NONE:
+ return;
+ case EVDEV_RELATIVE_MOTION:
+ notify_motion(master, time, device->rel.dx, device->rel.dy);
+ device->rel.dx = 0;
+ device->rel.dy = 0;
+ goto handled;
+ case EVDEV_ABSOLUTE_MT_DOWN:
+ weston_output_transform_coordinate(device->output,
+ device->mt.slots[slot].x,
+ device->mt.slots[slot].y,
+ &x, &y);
+ notify_touch(master, time,
+ slot, x, y, WL_TOUCH_DOWN);
+ goto handled;
+ case EVDEV_ABSOLUTE_MT_MOTION:
+ weston_output_transform_coordinate(device->output,
+ device->mt.slots[slot].x,
+ device->mt.slots[slot].y,
+ &x, &y);
+ notify_touch(master, time,
+ slot, x, y, WL_TOUCH_MOTION);
+ goto handled;
+ case EVDEV_ABSOLUTE_MT_UP:
+ notify_touch(master, time, slot, 0, 0,
+ WL_TOUCH_UP);
+ goto handled;
+ case EVDEV_ABSOLUTE_TOUCH_DOWN:
+ transform_absolute(device, &cx, &cy);
+ weston_output_transform_coordinate(device->output,
+ cx, cy, &x, &y);
+ notify_touch(master, time, 0, x, y, WL_TOUCH_DOWN);
+ goto handled;
+ case EVDEV_ABSOLUTE_MOTION:
+ transform_absolute(device, &cx, &cy);
+ weston_output_transform_coordinate(device->output,
+ cx, cy, &x, &y);
+
+ if (device->caps & EVDEV_TOUCH)
+ notify_touch(master, time, 0, x, y, WL_TOUCH_MOTION);
+ else
+ notify_motion_absolute(master, time, x, y);
+ goto handled;
+ case EVDEV_ABSOLUTE_TOUCH_UP:
+ notify_touch(master, time, 0, 0, 0, WL_TOUCH_UP);
+ goto handled;
+ }
+
+ assert(0 && "Unknown pending event type");
+
+handled:
+ device->pending_event = EVDEV_NONE;
+}
+
+static void
+evdev_process_touch_button(struct evdev_device *device, int time, int value)
+{
+ if (device->pending_event != EVDEV_NONE &&
+ device->pending_event != EVDEV_ABSOLUTE_MOTION)
+ evdev_flush_pending_event(device, time);
+
+ device->pending_event = (value ?
+ EVDEV_ABSOLUTE_TOUCH_DOWN :
+ EVDEV_ABSOLUTE_TOUCH_UP);
+}
+
+static inline void
+evdev_process_key(struct evdev_device *device, struct input_event *e, int time)
+{
+ /* ignore kernel key repeat */
+ if (e->value == 2)
+ return;
+
+ if (e->code == BTN_TOUCH) {
+ if (!device->is_mt)
+ evdev_process_touch_button(device, time, e->value);
+ return;
+ }
+
+ evdev_flush_pending_event(device, time);
+
+ switch (e->code) {
+ case BTN_LEFT:
+ case BTN_RIGHT:
+ case BTN_MIDDLE:
+ case BTN_SIDE:
+ case BTN_EXTRA:
+ case BTN_FORWARD:
+ case BTN_BACK:
+ case BTN_TASK:
+ notify_button(device->seat,
+ time, e->code,
+ e->value ? WL_POINTER_BUTTON_STATE_PRESSED :
+ WL_POINTER_BUTTON_STATE_RELEASED);
+ break;
+
+ default:
+ notify_key(device->seat,
+ time, e->code,
+ e->value ? WL_KEYBOARD_KEY_STATE_PRESSED :
+ WL_KEYBOARD_KEY_STATE_RELEASED,
+ STATE_UPDATE_AUTOMATIC);
+ break;
+ }
+}
+
+static void
+evdev_process_touch(struct evdev_device *device,
+ struct input_event *e,
+ uint32_t time)
+{
+ const int screen_width = device->output->current_mode->width;
+ const int screen_height = device->output->current_mode->height;
+
+ switch (e->code) {
+ case ABS_MT_SLOT:
+ evdev_flush_pending_event(device, time);
+ device->mt.slot = e->value;
+ break;
+ case ABS_MT_TRACKING_ID:
+ if (device->pending_event != EVDEV_NONE &&
+ device->pending_event != EVDEV_ABSOLUTE_MT_MOTION)
+ evdev_flush_pending_event(device, time);
+ if (e->value >= 0)
+ device->pending_event = EVDEV_ABSOLUTE_MT_DOWN;
+ else
+ device->pending_event = EVDEV_ABSOLUTE_MT_UP;
+ break;
+ case ABS_MT_POSITION_X:
+ device->mt.slots[device->mt.slot].x =
+ (e->value - device->abs.min_x) * screen_width /
+ (device->abs.max_x - device->abs.min_x);
+ if (device->pending_event == EVDEV_NONE)
+ device->pending_event = EVDEV_ABSOLUTE_MT_MOTION;
+ break;
+ case ABS_MT_POSITION_Y:
+ device->mt.slots[device->mt.slot].y =
+ (e->value - device->abs.min_y) * screen_height /
+ (device->abs.max_y - device->abs.min_y);
+ if (device->pending_event == EVDEV_NONE)
+ device->pending_event = EVDEV_ABSOLUTE_MT_MOTION;
+ break;
+ }
+}
+
+static inline void
+evdev_process_absolute_motion(struct evdev_device *device,
+ struct input_event *e)
+{
+ const int screen_width = device->output->current_mode->width;
+ const int screen_height = device->output->current_mode->height;
+
+ switch (e->code) {
+ case ABS_X:
+ device->abs.x =
+ (e->value - device->abs.min_x) * screen_width /
+ (device->abs.max_x - device->abs.min_x);
+ if (device->pending_event == EVDEV_NONE)
+ device->pending_event = EVDEV_ABSOLUTE_MOTION;
+ break;
+ case ABS_Y:
+ device->abs.y =
+ (e->value - device->abs.min_y) * screen_height /
+ (device->abs.max_y - device->abs.min_y);
+ if (device->pending_event == EVDEV_NONE)
+ device->pending_event = EVDEV_ABSOLUTE_MOTION;
+ break;
+ }
+}
+
+static inline void
+evdev_process_relative(struct evdev_device *device,
+ struct input_event *e, uint32_t time)
+{
+ switch (e->code) {
+ case REL_X:
+ if (device->pending_event != EVDEV_RELATIVE_MOTION)
+ evdev_flush_pending_event(device, time);
+ device->rel.dx += wl_fixed_from_int(e->value);
+ device->pending_event = EVDEV_RELATIVE_MOTION;
+ break;
+ case REL_Y:
+ if (device->pending_event != EVDEV_RELATIVE_MOTION)
+ evdev_flush_pending_event(device, time);
+ device->rel.dy += wl_fixed_from_int(e->value);
+ device->pending_event = EVDEV_RELATIVE_MOTION;
+ break;
+ case REL_WHEEL:
+ evdev_flush_pending_event(device, time);
+ switch (e->value) {
+ case -1:
+ /* Scroll down */
+ case 1:
+ /* Scroll up */
+ notify_axis(device->seat,
+ time,
+ WL_POINTER_AXIS_VERTICAL_SCROLL,
+ -1 * e->value * DEFAULT_AXIS_STEP_DISTANCE);
+ break;
+ default:
+ break;
+ }
+ break;
+ case REL_HWHEEL:
+ evdev_flush_pending_event(device, time);
+ switch (e->value) {
+ case -1:
+ /* Scroll left */
+ case 1:
+ /* Scroll right */
+ notify_axis(device->seat,
+ time,
+ WL_POINTER_AXIS_HORIZONTAL_SCROLL,
+ e->value * DEFAULT_AXIS_STEP_DISTANCE);
+ break;
+ default:
+ break;
+
+ }
+ }
+}
+
+static inline void
+evdev_process_absolute(struct evdev_device *device,
+ struct input_event *e,
+ uint32_t time)
+{
+ if (device->is_mt) {
+ evdev_process_touch(device, e, time);
+ } else {
+ evdev_process_absolute_motion(device, e);
+ }
+}
+
+static void
+fallback_process(struct evdev_dispatch *dispatch,
+ struct evdev_device *device,
+ struct input_event *event,
+ uint32_t time)
+{
+ switch (event->type) {
+ case EV_REL:
+ evdev_process_relative(device, event, time);
+ break;
+ case EV_ABS:
+ evdev_process_absolute(device, event, time);
+ break;
+ case EV_KEY:
+ evdev_process_key(device, event, time);
+ break;
+ case EV_SYN:
+ evdev_flush_pending_event(device, time);
+ break;
+ }
+}
+
+static void
+fallback_destroy(struct evdev_dispatch *dispatch)
+{
+ free(dispatch);
+}
+
+struct evdev_dispatch_interface fallback_interface = {
+ fallback_process,
+ fallback_destroy
+};
+
+static struct evdev_dispatch *
+fallback_dispatch_create(void)
+{
+ struct evdev_dispatch *dispatch = malloc(sizeof *dispatch);
+ if (dispatch == NULL)
+ return NULL;
+
+ dispatch->interface = &fallback_interface;
+
+ return dispatch;
+}
+
+static void
+evdev_process_events(struct evdev_device *device,
+ struct input_event *ev, int count)
+{
+ struct evdev_dispatch *dispatch = device->dispatch;
+ struct input_event *e, *end;
+ uint32_t time = 0;
+
+ e = ev;
+ end = e + count;
+ for (e = ev; e < end; e++) {
+ time = e->time.tv_sec * 1000 + e->time.tv_usec / 1000;
+
+ dispatch->interface->process(dispatch, device, e, time);
+ }
+}
+
+static int
+evdev_device_data(int fd, uint32_t mask, void *data)
+{
+ struct weston_compositor *ec;
+ struct evdev_device *device = data;
+ struct input_event ev[32];
+ int len;
+
+ ec = device->seat->compositor;
+ if (!ec->focus)
+ return 1;
+
+ /* If the compositor is repainting, this function is called only once
+ * per frame and we have to process all the events available on the
+ * fd, otherwise there will be input lag. */
+ do {
+ if (device->mtdev)
+ len = mtdev_get(device->mtdev, fd, ev,
+ ARRAY_LENGTH(ev)) *
+ sizeof (struct input_event);
+ else
+ len = read(fd, &ev, sizeof ev);
+
+ if (len < 0 || len % sizeof ev[0] != 0) {
+ if (len < 0 && errno != EAGAIN && errno != EINTR) {
+ weston_log("device %s died\n",
+ device->devnode);
+ wl_event_source_remove(device->source);
+ device->source = NULL;
+ }
+
+ return 1;
+ }
+
+ evdev_process_events(device, ev, len / sizeof ev[0]);
+
+ } while (len > 0);
+
+ return 1;
+}
+
+static int
+evdev_handle_device(struct evdev_device *device)
+{
+ struct input_absinfo absinfo;
+ unsigned long ev_bits[NBITS(EV_MAX)];
+ unsigned long abs_bits[NBITS(ABS_MAX)];
+ unsigned long rel_bits[NBITS(REL_MAX)];
+ unsigned long key_bits[NBITS(KEY_MAX)];
+ int has_key, has_abs;
+ unsigned int i;
+
+ has_key = 0;
+ has_abs = 0;
+ device->caps = 0;
+
+ ioctl(device->fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits);
+ if (TEST_BIT(ev_bits, EV_ABS)) {
+ has_abs = 1;
+
+ ioctl(device->fd, EVIOCGBIT(EV_ABS, sizeof(abs_bits)),
+ abs_bits);
+
+ if (TEST_BIT(abs_bits, ABS_WHEEL) ||
+ TEST_BIT(abs_bits, ABS_GAS) ||
+ TEST_BIT(abs_bits, ABS_BRAKE) ||
+ TEST_BIT(abs_bits, ABS_HAT0X)) {
+ weston_log("device %s is a joystick, ignoring\n",
+ device->devnode);
+ return 0;
+ }
+
+ if (TEST_BIT(abs_bits, ABS_X)) {
+ ioctl(device->fd, EVIOCGABS(ABS_X), &absinfo);
+ device->abs.min_x = absinfo.minimum;
+ device->abs.max_x = absinfo.maximum;
+ device->caps |= EVDEV_MOTION_ABS;
+ }
+ if (TEST_BIT(abs_bits, ABS_Y)) {
+ ioctl(device->fd, EVIOCGABS(ABS_Y), &absinfo);
+ device->abs.min_y = absinfo.minimum;
+ device->abs.max_y = absinfo.maximum;
+ device->caps |= EVDEV_MOTION_ABS;
+ }
+ /* We only handle the slotted Protocol B in weston.
+ Devices with ABS_MT_POSITION_* but not ABS_MT_SLOT
+ require mtdev for conversion. */
+ if (TEST_BIT(abs_bits, ABS_MT_POSITION_X) &&
+ TEST_BIT(abs_bits, ABS_MT_POSITION_Y)) {
+ ioctl(device->fd, EVIOCGABS(ABS_MT_POSITION_X),
+ &absinfo);
+ device->abs.min_x = absinfo.minimum;
+ device->abs.max_x = absinfo.maximum;
+ ioctl(device->fd, EVIOCGABS(ABS_MT_POSITION_Y),
+ &absinfo);
+ device->abs.min_y = absinfo.minimum;
+ device->abs.max_y = absinfo.maximum;
+ device->is_mt = 1;
+ device->caps |= EVDEV_TOUCH;
+
+ if (!TEST_BIT(abs_bits, ABS_MT_SLOT)) {
+ device->mtdev = mtdev_new_open(device->fd);
+ if (!device->mtdev) {
+ weston_log("mtdev required but failed to open for %s\n",
+ device->devnode);
+ return 0;
+ }
+ device->mt.slot = device->mtdev->caps.slot.value;
+ } else {
+ ioctl(device->fd, EVIOCGABS(ABS_MT_SLOT),
+ &absinfo);
+ device->mt.slot = absinfo.value;
+ }
+ }
+ }
+ if (TEST_BIT(ev_bits, EV_REL)) {
+ ioctl(device->fd, EVIOCGBIT(EV_REL, sizeof(rel_bits)),
+ rel_bits);
+ if (TEST_BIT(rel_bits, REL_X) || TEST_BIT(rel_bits, REL_Y))
+ device->caps |= EVDEV_MOTION_REL;
+ }
+ if (TEST_BIT(ev_bits, EV_KEY)) {
+ has_key = 1;
+ ioctl(device->fd, EVIOCGBIT(EV_KEY, sizeof(key_bits)),
+ key_bits);
+ if (TEST_BIT(key_bits, BTN_TOOL_FINGER) &&
+ !TEST_BIT(key_bits, BTN_TOOL_PEN) &&
+ has_abs) {
+ device->dispatch = evdev_touchpad_create(device);
+ weston_log("input device %s, %s is a touchpad\n",
+ device->devname, device->devnode);
+ }
+ for (i = KEY_ESC; i < KEY_MAX; i++) {
+ if (i >= BTN_MISC && i < KEY_OK)
+ continue;
+ if (TEST_BIT(key_bits, i)) {
+ device->caps |= EVDEV_KEYBOARD;
+ break;
+ }
+ }
+ if (TEST_BIT(key_bits, BTN_TOUCH)) {
+ device->caps |= EVDEV_TOUCH;
+ }
+ for (i = BTN_MISC; i < BTN_JOYSTICK; i++) {
+ if (TEST_BIT(key_bits, i)) {
+ device->caps |= EVDEV_BUTTON;
+ device->caps &= ~EVDEV_TOUCH;
+ break;
+ }
+ }
+ }
+ if (TEST_BIT(ev_bits, EV_LED)) {
+ device->caps |= EVDEV_KEYBOARD;
+ }
+
+ /* This rule tries to catch accelerometer devices and opt out. We may
+ * want to adjust the protocol later adding a proper event for dealing
+ * with accelerometers and implement here accordingly */
+ if (has_abs && !has_key && !device->is_mt) {
+ weston_log("input device %s, %s "
+ "ignored: unsupported device type\n",
+ device->devname, device->devnode);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+evdev_configure_device(struct evdev_device *device)
+{
+ if ((device->caps & (EVDEV_MOTION_ABS | EVDEV_MOTION_REL)) &&
+ (device->caps & EVDEV_BUTTON)) {
+ weston_seat_init_pointer(device->seat);
+ device->seat_caps |= EVDEV_SEAT_POINTER;
+ weston_log("input device %s, %s is a pointer caps =%s%s%s\n",
+ device->devname, device->devnode,
+ device->caps & EVDEV_MOTION_ABS ? " absolute-motion" : "",
+ device->caps & EVDEV_MOTION_REL ? " relative-motion": "",
+ device->caps & EVDEV_BUTTON ? " button" : "");
+ }
+ if ((device->caps & EVDEV_KEYBOARD)) {
+ if (weston_seat_init_keyboard(device->seat, NULL) < 0)
+ return -1;
+ device->seat_caps |= EVDEV_SEAT_KEYBOARD;
+ weston_log("input device %s, %s is a keyboard\n",
+ device->devname, device->devnode);
+ }
+ if ((device->caps & EVDEV_TOUCH)) {
+ weston_seat_init_touch(device->seat);
+ device->seat_caps |= EVDEV_SEAT_TOUCH;
+ weston_log("input device %s, %s is a touch device\n",
+ device->devname, device->devnode);
+ }
+
+ return 0;
+}
+
+struct evdev_device *
+evdev_device_create(struct weston_seat *seat, const char *path, int device_fd)
+{
+ struct evdev_device *device;
+ struct weston_compositor *ec;
+ char devname[256] = "unknown";
+
+ device = zalloc(sizeof *device);
+ if (device == NULL)
+ return NULL;
+
+ ec = seat->compositor;
+ device->output =
+ container_of(ec->output_list.next, struct weston_output, link);
+
+ device->seat = seat;
+ device->seat_caps = 0;
+ device->is_mt = 0;
+ device->mtdev = NULL;
+ device->devnode = strdup(path);
+ device->mt.slot = -1;
+ device->rel.dx = 0;
+ device->rel.dy = 0;
+ device->dispatch = NULL;
+ device->fd = device_fd;
+ device->pending_event = EVDEV_NONE;
+ wl_list_init(&device->link);
+
+ ioctl(device->fd, EVIOCGNAME(sizeof(devname)), devname);
+ devname[sizeof(devname) - 1] = '\0';
+ device->devname = strdup(devname);
+
+ if (!evdev_handle_device(device)) {
+ evdev_device_destroy(device);
+ return EVDEV_UNHANDLED_DEVICE;
+ }
+
+ if (evdev_configure_device(device) == -1)
+ goto err;
+
+ /* If the dispatch was not set up use the fallback. */
+ if (device->dispatch == NULL)
+ device->dispatch = fallback_dispatch_create();
+ if (device->dispatch == NULL)
+ goto err;
+
+ device->source = wl_event_loop_add_fd(ec->input_loop, device->fd,
+ WL_EVENT_READABLE,
+ evdev_device_data, device);
+ if (device->source == NULL)
+ goto err;
+
+ return device;
+
+err:
+ evdev_device_destroy(device);
+ return NULL;
+}
+
+void
+evdev_device_destroy(struct evdev_device *device)
+{
+ struct evdev_dispatch *dispatch;
+
+ if (device->seat_caps & EVDEV_SEAT_POINTER)
+ weston_seat_release_pointer(device->seat);
+ if (device->seat_caps & EVDEV_SEAT_KEYBOARD)
+ weston_seat_release_keyboard(device->seat);
+ if (device->seat_caps & EVDEV_SEAT_TOUCH)
+ weston_seat_release_touch(device->seat);
+
+ dispatch = device->dispatch;
+ if (dispatch)
+ dispatch->interface->destroy(dispatch);
+
+ if (device->source)
+ wl_event_source_remove(device->source);
+ wl_list_remove(&device->link);
+ if (device->mtdev)
+ mtdev_close_delete(device->mtdev);
+ close(device->fd);
+ free(device->devname);
+ free(device->devnode);
+ free(device);
+}
+
+void
+evdev_notify_keyboard_focus(struct weston_seat *seat,
+ struct wl_list *evdev_devices)
+{
+ struct evdev_device *device;
+ struct wl_array keys;
+ unsigned int i, set;
+ char evdev_keys[(KEY_CNT + 7) / 8];
+ char all_keys[(KEY_CNT + 7) / 8];
+ uint32_t *k;
+ int ret;
+
+ if (!seat->keyboard)
+ return;
+
+ memset(all_keys, 0, sizeof all_keys);
+ wl_list_for_each(device, evdev_devices, link) {
+ memset(evdev_keys, 0, sizeof evdev_keys);
+ ret = ioctl(device->fd,
+ EVIOCGKEY(sizeof evdev_keys), evdev_keys);
+ if (ret < 0) {
+ weston_log("failed to get keys for device %s\n",
+ device->devnode);
+ continue;
+ }
+ for (i = 0; i < ARRAY_LENGTH(evdev_keys); i++)
+ all_keys[i] |= evdev_keys[i];
+ }
+
+ wl_array_init(&keys);
+ for (i = 0; i < KEY_CNT; i++) {
+ set = all_keys[i >> 3] & (1 << (i & 7));
+ if (set) {
+ k = wl_array_add(&keys, sizeof *k);
+ *k = i;
+ }
+ }
+
+ notify_keyboard_focus_in(seat, &keys, STATE_UPDATE_AUTOMATIC);
+
+ wl_array_release(&keys);
+}
diff --git a/src/evdev.h b/src/evdev.h
new file mode 100644
index 00000000..e146d1a4
--- /dev/null
+++ b/src/evdev.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright © 2011, 2012 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 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.
+ */
+
+#ifndef EVDEV_H
+#define EVDEV_H
+
+#include "config.h"
+
+#include <linux/input.h>
+#include <wayland-util.h>
+
+#define MAX_SLOTS 16
+
+enum evdev_event_type {
+ EVDEV_NONE,
+ EVDEV_ABSOLUTE_TOUCH_DOWN,
+ EVDEV_ABSOLUTE_MOTION,
+ EVDEV_ABSOLUTE_TOUCH_UP,
+ EVDEV_ABSOLUTE_MT_DOWN,
+ EVDEV_ABSOLUTE_MT_MOTION,
+ EVDEV_ABSOLUTE_MT_UP,
+ EVDEV_RELATIVE_MOTION,
+};
+
+enum evdev_device_capability {
+ EVDEV_KEYBOARD = (1 << 0),
+ EVDEV_BUTTON = (1 << 1),
+ EVDEV_MOTION_ABS = (1 << 2),
+ EVDEV_MOTION_REL = (1 << 3),
+ EVDEV_TOUCH = (1 << 4),
+};
+
+enum evdev_device_seat_capability {
+ EVDEV_SEAT_POINTER = (1 << 0),
+ EVDEV_SEAT_KEYBOARD = (1 << 1),
+ EVDEV_SEAT_TOUCH = (1 << 2)
+};
+
+struct evdev_device {
+ struct weston_seat *seat;
+ struct wl_list link;
+ struct wl_event_source *source;
+ struct weston_output *output;
+ struct evdev_dispatch *dispatch;
+ char *devnode;
+ char *devname;
+ int fd;
+ struct {
+ int min_x, max_x, min_y, max_y;
+ int32_t x, y;
+
+ int apply_calibration;
+ float calibration[6];
+ } abs;
+
+ struct {
+ int slot;
+ struct {
+ int32_t x, y;
+ } slots[MAX_SLOTS];
+ } mt;
+ struct mtdev *mtdev;
+
+ struct {
+ wl_fixed_t dx, dy;
+ } rel;
+
+ enum evdev_event_type pending_event;
+ enum evdev_device_capability caps;
+ enum evdev_device_seat_capability seat_caps;
+
+ int is_mt;
+};
+
+/* copied from udev/extras/input_id/input_id.c */
+/* we must use this kernel-compatible implementation */
+#define BITS_PER_LONG (sizeof(unsigned long) * 8)
+#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1)
+#define OFF(x) ((x)%BITS_PER_LONG)
+#define BIT(x) (1UL<<OFF(x))
+#define LONG(x) ((x)/BITS_PER_LONG)
+#define TEST_BIT(array, bit) ((array[LONG(bit)] >> OFF(bit)) & 1)
+/* end copied */
+
+#define EVDEV_UNHANDLED_DEVICE ((struct evdev_device *) 1)
+
+struct evdev_dispatch;
+
+struct evdev_dispatch_interface {
+ /* Process an evdev input event. */
+ void (*process)(struct evdev_dispatch *dispatch,
+ struct evdev_device *device,
+ struct input_event *event,
+ uint32_t time);
+
+ /* Destroy an event dispatch handler and free all its resources. */
+ void (*destroy)(struct evdev_dispatch *dispatch);
+};
+
+struct evdev_dispatch {
+ struct evdev_dispatch_interface *interface;
+};
+
+struct evdev_dispatch *
+evdev_touchpad_create(struct evdev_device *device);
+
+void
+evdev_led_update(struct evdev_device *device, enum weston_led leds);
+
+struct evdev_device *
+evdev_device_create(struct weston_seat *seat, const char *path, int device_fd);
+
+void
+evdev_device_destroy(struct evdev_device *device);
+
+void
+evdev_notify_keyboard_focus(struct weston_seat *seat,
+ struct wl_list *evdev_devices);
+
+#endif /* EVDEV_H */
diff --git a/src/filter.c b/src/filter.c
new file mode 100644
index 00000000..a55ebf27
--- /dev/null
+++ b/src/filter.c
@@ -0,0 +1,338 @@
+/*
+ * Copyright © 2012 Jonas Ådahl
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that 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.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <limits.h>
+#include <math.h>
+
+#include <wayland-util.h>
+
+#include "compositor.h"
+#include "filter.h"
+
+void
+weston_filter_dispatch(struct weston_motion_filter *filter,
+ struct weston_motion_params *motion,
+ void *data, uint32_t time)
+{
+ filter->interface->filter(filter, motion, data, time);
+}
+
+/*
+ * Pointer acceleration filter
+ */
+
+#define MAX_VELOCITY_DIFF 1.0
+#define MOTION_TIMEOUT 300 /* (ms) */
+#define NUM_POINTER_TRACKERS 16
+
+struct pointer_tracker {
+ double dx;
+ double dy;
+ uint32_t time;
+ int dir;
+};
+
+struct pointer_accelerator;
+struct pointer_accelerator {
+ struct weston_motion_filter base;
+
+ accel_profile_func_t profile;
+
+ double velocity;
+ double last_velocity;
+ int last_dx;
+ int last_dy;
+
+ struct pointer_tracker *trackers;
+ int cur_tracker;
+};
+
+enum directions {
+ N = 1 << 0,
+ NE = 1 << 1,
+ E = 1 << 2,
+ SE = 1 << 3,
+ S = 1 << 4,
+ SW = 1 << 5,
+ W = 1 << 6,
+ NW = 1 << 7,
+ UNDEFINED_DIRECTION = 0xff
+};
+
+static int
+get_direction(int dx, int dy)
+{
+ int dir = UNDEFINED_DIRECTION;
+ int d1, d2;
+ double r;
+
+ if (abs(dx) < 2 && abs(dy) < 2) {
+ if (dx > 0 && dy > 0)
+ dir = S | SE | E;
+ else if (dx > 0 && dy < 0)
+ dir = N | NE | E;
+ else if (dx < 0 && dy > 0)
+ dir = S | SW | W;
+ else if (dx < 0 && dy < 0)
+ dir = N | NW | W;
+ else if (dx > 0)
+ dir = NW | W | SW;
+ else if (dx < 0)
+ dir = NE | E | SE;
+ else if (dy > 0)
+ dir = SE | S | SW;
+ else if (dy < 0)
+ dir = NE | N | NW;
+ }
+ else {
+ /* Calculate r within the interval [0 to 8)
+ *
+ * r = [0 .. 2π] where 0 is North
+ * d_f = r / 2π ([0 .. 1))
+ * d_8 = 8 * d_f
+ */
+ r = atan2(dy, dx);
+ r = fmod(r + 2.5*M_PI, 2*M_PI);
+ r *= 4*M_1_PI;
+
+ /* Mark one or two close enough octants */
+ d1 = (int)(r + 0.9) % 8;
+ d2 = (int)(r + 0.1) % 8;
+
+ dir = (1 << d1) | (1 << d2);
+ }
+
+ return dir;
+}
+
+static void
+feed_trackers(struct pointer_accelerator *accel,
+ double dx, double dy,
+ uint32_t time)
+{
+ int i, current;
+ struct pointer_tracker *trackers = accel->trackers;
+
+ for (i = 0; i < NUM_POINTER_TRACKERS; i++) {
+ trackers[i].dx += dx;
+ trackers[i].dy += dy;
+ }
+
+ current = (accel->cur_tracker + 1) % NUM_POINTER_TRACKERS;
+ accel->cur_tracker = current;
+
+ trackers[current].dx = 0.0;
+ trackers[current].dy = 0.0;
+ trackers[current].time = time;
+ trackers[current].dir = get_direction(dx, dy);
+}
+
+static struct pointer_tracker *
+tracker_by_offset(struct pointer_accelerator *accel, unsigned int offset)
+{
+ unsigned int index =
+ (accel->cur_tracker + NUM_POINTER_TRACKERS - offset)
+ % NUM_POINTER_TRACKERS;
+ return &accel->trackers[index];
+}
+
+static double
+calculate_tracker_velocity(struct pointer_tracker *tracker, uint32_t time)
+{
+ int dx;
+ int dy;
+ double distance;
+
+ dx = tracker->dx;
+ dy = tracker->dy;
+ distance = sqrt(dx*dx + dy*dy);
+ return distance / (double)(time - tracker->time);
+}
+
+static double
+calculate_velocity(struct pointer_accelerator *accel, uint32_t time)
+{
+ struct pointer_tracker *tracker;
+ double velocity;
+ double result = 0.0;
+ double initial_velocity;
+ double velocity_diff;
+ unsigned int offset;
+
+ unsigned int dir = tracker_by_offset(accel, 0)->dir;
+
+ /* Find first velocity */
+ for (offset = 1; offset < NUM_POINTER_TRACKERS; offset++) {
+ tracker = tracker_by_offset(accel, offset);
+
+ if (time <= tracker->time)
+ continue;
+
+ result = initial_velocity =
+ calculate_tracker_velocity(tracker, time);
+ if (initial_velocity > 0.0)
+ break;
+ }
+
+ /* Find least recent vector within a timelimit, maximum velocity diff
+ * and direction threshold. */
+ for (; offset < NUM_POINTER_TRACKERS; offset++) {
+ tracker = tracker_by_offset(accel, offset);
+
+ /* Stop if too far away in time */
+ if (time - tracker->time > MOTION_TIMEOUT ||
+ tracker->time > time)
+ break;
+
+ /* Stop if direction changed */
+ dir &= tracker->dir;
+ if (dir == 0)
+ break;
+
+ velocity = calculate_tracker_velocity(tracker, time);
+
+ /* Stop if velocity differs too much from initial */
+ velocity_diff = fabs(initial_velocity - velocity);
+ if (velocity_diff > MAX_VELOCITY_DIFF)
+ break;
+
+ result = velocity;
+ }
+
+ return result;
+}
+
+static double
+acceleration_profile(struct pointer_accelerator *accel,
+ void *data, double velocity, uint32_t time)
+{
+ return accel->profile(&accel->base, data, velocity, time);
+}
+
+static double
+calculate_acceleration(struct pointer_accelerator *accel,
+ void *data, double velocity, uint32_t time)
+{
+ double factor;
+
+ /* Use Simpson's rule to calculate the avarage acceleration between
+ * the previous motion and the most recent. */
+ factor = acceleration_profile(accel, data, velocity, time);
+ factor += acceleration_profile(accel, data, accel->last_velocity, time);
+ factor += 4.0 *
+ acceleration_profile(accel, data,
+ (accel->last_velocity + velocity) / 2,
+ time);
+
+ factor = factor / 6.0;
+
+ return factor;
+}
+
+static double
+soften_delta(double last_delta, double delta)
+{
+ if (delta < -1.0 || delta > 1.0) {
+ if (delta > last_delta)
+ return delta - 0.5;
+ else if (delta < last_delta)
+ return delta + 0.5;
+ }
+
+ return delta;
+}
+
+static void
+apply_softening(struct pointer_accelerator *accel,
+ struct weston_motion_params *motion)
+{
+ motion->dx = soften_delta(accel->last_dx, motion->dx);
+ motion->dy = soften_delta(accel->last_dy, motion->dy);
+}
+
+static void
+accelerator_filter(struct weston_motion_filter *filter,
+ struct weston_motion_params *motion,
+ void *data, uint32_t time)
+{
+ struct pointer_accelerator *accel =
+ (struct pointer_accelerator *) filter;
+ double velocity;
+ double accel_value;
+
+ feed_trackers(accel, motion->dx, motion->dy, time);
+ velocity = calculate_velocity(accel, time);
+ accel_value = calculate_acceleration(accel, data, velocity, time);
+
+ motion->dx = accel_value * motion->dx;
+ motion->dy = accel_value * motion->dy;
+
+ apply_softening(accel, motion);
+
+ accel->last_dx = motion->dx;
+ accel->last_dy = motion->dy;
+
+ accel->last_velocity = velocity;
+}
+
+static void
+accelerator_destroy(struct weston_motion_filter *filter)
+{
+ struct pointer_accelerator *accel =
+ (struct pointer_accelerator *) filter;
+
+ free(accel->trackers);
+ free(accel);
+}
+
+struct weston_motion_filter_interface accelerator_interface = {
+ accelerator_filter,
+ accelerator_destroy
+};
+
+struct weston_motion_filter *
+create_pointer_accelator_filter(accel_profile_func_t profile)
+{
+ struct pointer_accelerator *filter;
+
+ filter = malloc(sizeof *filter);
+ if (filter == NULL)
+ return NULL;
+
+ filter->base.interface = &accelerator_interface;
+ wl_list_init(&filter->base.link);
+
+ filter->profile = profile;
+ filter->last_velocity = 0.0;
+ filter->last_dx = 0;
+ filter->last_dy = 0;
+
+ filter->trackers =
+ calloc(NUM_POINTER_TRACKERS, sizeof *filter->trackers);
+ filter->cur_tracker = 0;
+
+ return &filter->base;
+}
diff --git a/src/filter.h b/src/filter.h
new file mode 100644
index 00000000..850ce8df
--- /dev/null
+++ b/src/filter.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright © 2012 Jonas Ådahl
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that 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.
+ */
+
+#ifndef _FILTER_H_
+#define _FILTER_H_
+
+#include "config.h"
+
+#include <wayland-util.h>
+
+#include "compositor.h"
+
+struct weston_motion_params {
+ double dx, dy;
+};
+
+struct weston_motion_filter;
+
+WL_EXPORT void
+weston_filter_dispatch(struct weston_motion_filter *filter,
+ struct weston_motion_params *motion,
+ void *data, uint32_t time);
+
+
+struct weston_motion_filter_interface {
+ void (*filter)(struct weston_motion_filter *filter,
+ struct weston_motion_params *motion,
+ void *data, uint32_t time);
+ void (*destroy)(struct weston_motion_filter *filter);
+};
+
+struct weston_motion_filter {
+ struct weston_motion_filter_interface *interface;
+ struct wl_list link;
+};
+
+WL_EXPORT struct weston_motion_filter *
+create_linear_acceleration_filter(double speed);
+
+typedef double (*accel_profile_func_t)(struct weston_motion_filter *filter,
+ void *data,
+ double velocity,
+ uint32_t time);
+
+WL_EXPORT struct weston_motion_filter *
+create_pointer_accelator_filter(accel_profile_func_t filter);
+
+#endif // _FILTER_H_
diff --git a/src/gl-renderer.c b/src/gl-renderer.c
new file mode 100644
index 00000000..ae69f220
--- /dev/null
+++ b/src/gl-renderer.c
@@ -0,0 +1,1865 @@
+/*
+ * Copyright © 2012 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 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.
+ */
+
+#include "config.h"
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <float.h>
+#include <assert.h>
+#include <linux/input.h>
+
+#include "gl-renderer.h"
+#include "vertex-clipping.h"
+
+#include <EGL/eglext.h>
+#include "weston-egl-ext.h"
+
+struct gl_shader {
+ GLuint program;
+ GLuint vertex_shader, fragment_shader;
+ GLint proj_uniform;
+ GLint tex_uniforms[3];
+ GLint alpha_uniform;
+ GLint color_uniform;
+ const char *vertex_source, *fragment_source;
+};
+
+#define BUFFER_DAMAGE_COUNT 2
+
+struct gl_output_state {
+ EGLSurface egl_surface;
+ pixman_region32_t buffer_damage[BUFFER_DAMAGE_COUNT];
+};
+
+enum buffer_type {
+ BUFFER_TYPE_NULL,
+ BUFFER_TYPE_SHM,
+ BUFFER_TYPE_EGL
+};
+
+struct gl_surface_state {
+ GLfloat color[4];
+ struct gl_shader *shader;
+
+ GLuint textures[3];
+ int num_textures;
+ int needs_full_upload;
+ pixman_region32_t texture_damage;
+
+ EGLImageKHR images[3];
+ GLenum target;
+ int num_images;
+
+ struct weston_buffer_reference buffer_ref;
+ enum buffer_type buffer_type;
+ int pitch; /* in pixels */
+ int height; /* in pixels */
+ int y_inverted;
+};
+
+struct gl_renderer {
+ struct weston_renderer base;
+ int fragment_shader_debug;
+ int fan_debug;
+
+ EGLDisplay egl_display;
+ EGLContext egl_context;
+ EGLConfig egl_config;
+
+ struct {
+ int32_t top, bottom, left, right;
+ GLuint texture;
+ int32_t width, height;
+ } border;
+
+ struct wl_array vertices;
+ struct wl_array indices; /* only used in compositor-wayland */
+ struct wl_array vtxcnt;
+
+ PFNGLEGLIMAGETARGETTEXTURE2DOESPROC image_target_texture_2d;
+ PFNEGLCREATEIMAGEKHRPROC create_image;
+ PFNEGLDESTROYIMAGEKHRPROC destroy_image;
+
+ int has_unpack_subimage;
+
+ PFNEGLBINDWAYLANDDISPLAYWL bind_display;
+ PFNEGLUNBINDWAYLANDDISPLAYWL unbind_display;
+ PFNEGLQUERYWAYLANDBUFFERWL query_buffer;
+ int has_bind_display;
+
+ int has_egl_image_external;
+
+ int has_egl_buffer_age;
+
+ struct gl_shader texture_shader_rgba;
+ struct gl_shader texture_shader_rgbx;
+ struct gl_shader texture_shader_egl_external;
+ struct gl_shader texture_shader_y_uv;
+ struct gl_shader texture_shader_y_u_v;
+ struct gl_shader texture_shader_y_xuxv;
+ struct gl_shader invert_color_shader;
+ struct gl_shader solid_shader;
+ struct gl_shader *current_shader;
+};
+
+static inline struct gl_output_state *
+get_output_state(struct weston_output *output)
+{
+ return (struct gl_output_state *)output->renderer_state;
+}
+
+static inline struct gl_surface_state *
+get_surface_state(struct weston_surface *surface)
+{
+ return (struct gl_surface_state *)surface->renderer_state;
+}
+
+static inline struct gl_renderer *
+get_renderer(struct weston_compositor *ec)
+{
+ return (struct gl_renderer *)ec->renderer;
+}
+
+static const char *
+egl_error_string(EGLint code)
+{
+#define MYERRCODE(x) case x: return #x;
+ switch (code) {
+ MYERRCODE(EGL_SUCCESS)
+ MYERRCODE(EGL_NOT_INITIALIZED)
+ MYERRCODE(EGL_BAD_ACCESS)
+ MYERRCODE(EGL_BAD_ALLOC)
+ MYERRCODE(EGL_BAD_ATTRIBUTE)
+ MYERRCODE(EGL_BAD_CONTEXT)
+ MYERRCODE(EGL_BAD_CONFIG)
+ MYERRCODE(EGL_BAD_CURRENT_SURFACE)
+ MYERRCODE(EGL_BAD_DISPLAY)
+ MYERRCODE(EGL_BAD_SURFACE)
+ MYERRCODE(EGL_BAD_MATCH)
+ MYERRCODE(EGL_BAD_PARAMETER)
+ MYERRCODE(EGL_BAD_NATIVE_PIXMAP)
+ MYERRCODE(EGL_BAD_NATIVE_WINDOW)
+ MYERRCODE(EGL_CONTEXT_LOST)
+ default:
+ return "unknown";
+ }
+#undef MYERRCODE
+}
+
+WL_EXPORT void
+gl_renderer_print_egl_error_state(void)
+{
+ EGLint code;
+
+ code = eglGetError();
+ weston_log("EGL error state: %s (0x%04lx)\n",
+ egl_error_string(code), (long)code);
+}
+
+#define max(a, b) (((a) > (b)) ? (a) : (b))
+#define min(a, b) (((a) > (b)) ? (b) : (a))
+
+/*
+ * Compute the boundary vertices of the intersection of the global coordinate
+ * aligned rectangle 'rect', and an arbitrary quadrilateral produced from
+ * 'surf_rect' when transformed from surface coordinates into global coordinates.
+ * The vertices are written to 'ex' and 'ey', and the return value is the
+ * number of vertices. Vertices are produced in clockwise winding order.
+ * Guarantees to produce either zero vertices, or 3-8 vertices with non-zero
+ * polygon area.
+ */
+static int
+calculate_edges(struct weston_surface *es, pixman_box32_t *rect,
+ pixman_box32_t *surf_rect, GLfloat *ex, GLfloat *ey)
+{
+
+ struct clip_context ctx;
+ int i, n;
+ GLfloat min_x, max_x, min_y, max_y;
+ struct polygon8 surf = {
+ { surf_rect->x1, surf_rect->x2, surf_rect->x2, surf_rect->x1 },
+ { surf_rect->y1, surf_rect->y1, surf_rect->y2, surf_rect->y2 },
+ 4
+ };
+
+ ctx.clip.x1 = rect->x1;
+ ctx.clip.y1 = rect->y1;
+ ctx.clip.x2 = rect->x2;
+ ctx.clip.y2 = rect->y2;
+
+ /* transform surface to screen space: */
+ for (i = 0; i < surf.n; i++)
+ weston_surface_to_global_float(es, surf.x[i], surf.y[i],
+ &surf.x[i], &surf.y[i]);
+
+ /* find bounding box: */
+ min_x = max_x = surf.x[0];
+ min_y = max_y = surf.y[0];
+
+ for (i = 1; i < surf.n; i++) {
+ min_x = min(min_x, surf.x[i]);
+ max_x = max(max_x, surf.x[i]);
+ min_y = min(min_y, surf.y[i]);
+ max_y = max(max_y, surf.y[i]);
+ }
+
+ /* First, simple bounding box check to discard early transformed
+ * surface rects that do not intersect with the clip region:
+ */
+ if ((min_x >= ctx.clip.x2) || (max_x <= ctx.clip.x1) ||
+ (min_y >= ctx.clip.y2) || (max_y <= ctx.clip.y1))
+ return 0;
+
+ /* Simple case, bounding box edges are parallel to surface edges,
+ * there will be only four edges. We just need to clip the surface
+ * vertices to the clip rect bounds:
+ */
+ if (!es->transform.enabled) {
+ return clip_simple(&ctx, &surf, ex, ey);
+ }
+
+ /* Transformed case: use a general polygon clipping algorithm to
+ * clip the surface rectangle with each side of 'rect'.
+ * The algorithm is Sutherland-Hodgman, as explained in
+ * http://www.codeguru.com/cpp/misc/misc/graphics/article.php/c8965/Polygon-Clipping.htm
+ * but without looking at any of that code.
+ */
+ n = clip_transformed(&ctx, &surf, ex, ey);
+
+ if (n < 3)
+ return 0;
+
+ return n;
+}
+
+static int
+texture_region(struct weston_surface *es, pixman_region32_t *region,
+ pixman_region32_t *surf_region)
+{
+ struct gl_surface_state *gs = get_surface_state(es);
+ struct weston_compositor *ec = es->compositor;
+ struct gl_renderer *gr = get_renderer(ec);
+ GLfloat *v, inv_width, inv_height;
+ unsigned int *vtxcnt, nvtx = 0;
+ pixman_box32_t *rects, *surf_rects;
+ int i, j, k, nrects, nsurf;
+
+ rects = pixman_region32_rectangles(region, &nrects);
+ surf_rects = pixman_region32_rectangles(surf_region, &nsurf);
+
+ /* worst case we can have 8 vertices per rect (ie. clipped into
+ * an octagon):
+ */
+ v = wl_array_add(&gr->vertices, nrects * nsurf * 8 * 4 * sizeof *v);
+ vtxcnt = wl_array_add(&gr->vtxcnt, nrects * nsurf * sizeof *vtxcnt);
+
+ inv_width = 1.0 / gs->pitch;
+ inv_height = 1.0 / gs->height;
+
+ for (i = 0; i < nrects; i++) {
+ pixman_box32_t *rect = &rects[i];
+ for (j = 0; j < nsurf; j++) {
+ pixman_box32_t *surf_rect = &surf_rects[j];
+ GLfloat sx, sy, bx, by;
+ GLfloat ex[8], ey[8]; /* edge points in screen space */
+ int n;
+
+ /* The transformed surface, after clipping to the clip region,
+ * can have as many as eight sides, emitted as a triangle-fan.
+ * The first vertex in the triangle fan can be chosen arbitrarily,
+ * since the area is guaranteed to be convex.
+ *
+ * If a corner of the transformed surface falls outside of the
+ * clip region, instead of emitting one vertex for the corner
+ * of the surface, up to two are emitted for two corresponding
+ * intersection point(s) between the surface and the clip region.
+ *
+ * To do this, we first calculate the (up to eight) points that
+ * form the intersection of the clip rect and the transformed
+ * surface.
+ */
+ n = calculate_edges(es, rect, surf_rect, ex, ey);
+ if (n < 3)
+ continue;
+
+ /* emit edge points: */
+ for (k = 0; k < n; k++) {
+ weston_surface_from_global_float(es, ex[k], ey[k], &sx, &sy);
+ /* position: */
+ *(v++) = ex[k];
+ *(v++) = ey[k];
+ /* texcoord: */
+ weston_surface_to_buffer_float(es, sx, sy,
+ &bx, &by);
+ *(v++) = bx * inv_width;
+ if (gs->y_inverted) {
+ *(v++) = by * inv_height;
+ } else {
+ *(v++) = (gs->height - by) * inv_height;
+ }
+ }
+
+ vtxcnt[nvtx++] = n;
+ }
+ }
+
+ return nvtx;
+}
+
+static void
+triangle_fan_debug(struct weston_surface *surface, int first, int count)
+{
+ struct weston_compositor *compositor = surface->compositor;
+ struct gl_renderer *gr = get_renderer(compositor);
+ int i;
+ GLushort *buffer;
+ GLushort *index;
+ int nelems;
+ static int color_idx = 0;
+ static const GLfloat color[][4] = {
+ { 1.0, 0.0, 0.0, 1.0 },
+ { 0.0, 1.0, 0.0, 1.0 },
+ { 0.0, 0.0, 1.0, 1.0 },
+ { 1.0, 1.0, 1.0, 1.0 },
+ };
+
+ nelems = (count - 1 + count - 2) * 2;
+
+ buffer = malloc(sizeof(GLushort) * nelems);
+ index = buffer;
+
+ for (i = 1; i < count; i++) {
+ *index++ = first;
+ *index++ = first + i;
+ }
+
+ for (i = 2; i < count; i++) {
+ *index++ = first + i - 1;
+ *index++ = first + i;
+ }
+
+ glUseProgram(gr->solid_shader.program);
+ glUniform4fv(gr->solid_shader.color_uniform, 1,
+ color[color_idx++ % ARRAY_LENGTH(color)]);
+ glDrawElements(GL_LINES, nelems, GL_UNSIGNED_SHORT, buffer);
+ glUseProgram(gr->current_shader->program);
+ free(buffer);
+}
+
+static void
+repaint_region(struct weston_surface *es, pixman_region32_t *region,
+ pixman_region32_t *surf_region)
+{
+ struct weston_compositor *ec = es->compositor;
+ struct gl_renderer *gr = get_renderer(ec);
+ GLfloat *v;
+ unsigned int *vtxcnt;
+ int i, first, nfans;
+
+ /* The final region to be painted is the intersection of
+ * 'region' and 'surf_region'. However, 'region' is in the global
+ * coordinates, and 'surf_region' is in the surface-local
+ * coordinates. texture_region() will iterate over all pairs of
+ * rectangles from both regions, compute the intersection
+ * polygon for each pair, and store it as a triangle fan if
+ * it has a non-zero area (at least 3 vertices1, actually).
+ */
+ nfans = texture_region(es, region, surf_region);
+
+ v = gr->vertices.data;
+ vtxcnt = gr->vtxcnt.data;
+
+ /* position: */
+ glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof *v, &v[0]);
+ glEnableVertexAttribArray(0);
+
+ /* texcoord: */
+ glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof *v, &v[2]);
+ glEnableVertexAttribArray(1);
+
+ for (i = 0, first = 0; i < nfans; i++) {
+ glDrawArrays(GL_TRIANGLE_FAN, first, vtxcnt[i]);
+ if (gr->fan_debug)
+ triangle_fan_debug(es, first, vtxcnt[i]);
+ first += vtxcnt[i];
+ }
+
+ glDisableVertexAttribArray(1);
+ glDisableVertexAttribArray(0);
+
+ gr->vertices.size = 0;
+ gr->vtxcnt.size = 0;
+}
+
+static int
+use_output(struct weston_output *output)
+{
+ static int errored;
+ struct gl_output_state *go = get_output_state(output);
+ struct gl_renderer *gr = get_renderer(output->compositor);
+ EGLBoolean ret;
+
+ ret = eglMakeCurrent(gr->egl_display, go->egl_surface,
+ go->egl_surface, gr->egl_context);
+
+ if (ret == EGL_FALSE) {
+ if (errored)
+ return -1;
+ errored = 1;
+ weston_log("Failed to make EGL context current.\n");
+ gl_renderer_print_egl_error_state();
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+shader_init(struct gl_shader *shader, struct gl_renderer *gr,
+ const char *vertex_source, const char *fragment_source);
+
+static void
+use_shader(struct gl_renderer *gr, struct gl_shader *shader)
+{
+ if (!shader->program) {
+ int ret;
+
+ ret = shader_init(shader, gr,
+ shader->vertex_source,
+ shader->fragment_source);
+
+ if (ret < 0)
+ weston_log("warning: failed to compile shader\n");
+ }
+
+ if (gr->current_shader == shader)
+ return;
+ glUseProgram(shader->program);
+ gr->current_shader = shader;
+}
+
+static void
+shader_uniforms(struct gl_shader *shader,
+ struct weston_surface *surface,
+ struct weston_output *output)
+{
+ int i;
+ struct gl_surface_state *gs = get_surface_state(surface);
+
+ glUniformMatrix4fv(shader->proj_uniform,
+ 1, GL_FALSE, output->matrix.d);
+ glUniform4fv(shader->color_uniform, 1, gs->color);
+ glUniform1f(shader->alpha_uniform, surface->alpha);
+
+ for (i = 0; i < gs->num_textures; i++)
+ glUniform1i(shader->tex_uniforms[i], i);
+}
+
+static void
+draw_surface(struct weston_surface *es, struct weston_output *output,
+ pixman_region32_t *damage) /* in global coordinates */
+{
+ struct weston_compositor *ec = es->compositor;
+ struct gl_renderer *gr = get_renderer(ec);
+ struct gl_surface_state *gs = get_surface_state(es);
+ /* repaint bounding region in global coordinates: */
+ pixman_region32_t repaint;
+ /* non-opaque region in surface coordinates: */
+ pixman_region32_t surface_blend;
+ GLint filter;
+ int i;
+
+ pixman_region32_init(&repaint);
+ pixman_region32_intersect(&repaint,
+ &es->transform.boundingbox, damage);
+ pixman_region32_subtract(&repaint, &repaint, &es->clip);
+
+ if (!pixman_region32_not_empty(&repaint))
+ goto out;
+
+ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+
+ if (gr->fan_debug) {
+ use_shader(gr, &gr->solid_shader);
+ shader_uniforms(&gr->solid_shader, es, output);
+ }
+
+ use_shader(gr, gs->shader);
+ shader_uniforms(gs->shader, es, output);
+
+ if (es->transform.enabled || output->zoom.active || output->current_scale != es->buffer_scale)
+ filter = GL_LINEAR;
+ else
+ filter = GL_NEAREST;
+
+ for (i = 0; i < gs->num_textures; i++) {
+ glActiveTexture(GL_TEXTURE0 + i);
+ glBindTexture(gs->target, gs->textures[i]);
+ glTexParameteri(gs->target, GL_TEXTURE_MIN_FILTER, filter);
+ glTexParameteri(gs->target, GL_TEXTURE_MAG_FILTER, filter);
+ }
+
+ /* blended region is whole surface minus opaque region: */
+ pixman_region32_init_rect(&surface_blend, 0, 0,
+ es->geometry.width, es->geometry.height);
+ pixman_region32_subtract(&surface_blend, &surface_blend, &es->opaque);
+
+ if (pixman_region32_not_empty(&es->opaque)) {
+ if (gs->shader == &gr->texture_shader_rgba) {
+ /* Special case for RGBA textures with possibly
+ * bad data in alpha channel: use the shader
+ * that forces texture alpha = 1.0.
+ * Xwayland surfaces need this.
+ */
+ use_shader(gr, &gr->texture_shader_rgbx);
+ shader_uniforms(&gr->texture_shader_rgbx, es, output);
+ }
+
+ if (es->alpha < 1.0)
+ glEnable(GL_BLEND);
+ else
+ glDisable(GL_BLEND);
+
+ repaint_region(es, &repaint, &es->opaque);
+ }
+
+ if (pixman_region32_not_empty(&surface_blend)) {
+ use_shader(gr, gs->shader);
+ glEnable(GL_BLEND);
+ repaint_region(es, &repaint, &surface_blend);
+ }
+
+ pixman_region32_fini(&surface_blend);
+
+out:
+ pixman_region32_fini(&repaint);
+}
+
+static void
+repaint_surfaces(struct weston_output *output, pixman_region32_t *damage)
+{
+ struct weston_compositor *compositor = output->compositor;
+ struct weston_surface *surface;
+
+ wl_list_for_each_reverse(surface, &compositor->surface_list, link)
+ if (surface->plane == &compositor->primary_plane)
+ draw_surface(surface, output, damage);
+}
+
+
+static int
+texture_border(struct weston_output *output)
+{
+ struct weston_compositor *ec = output->compositor;
+ struct gl_renderer *gr = get_renderer(ec);
+ GLfloat *d;
+ unsigned short *p;
+ int i, j, k, n;
+ GLfloat x[4], y[4], u[4], v[4];
+
+ x[0] = -gr->border.left;
+ x[1] = 0;
+ x[2] = output->current_mode->width;
+ x[3] = output->current_mode->width + gr->border.right;
+
+ y[0] = -gr->border.top;
+ y[1] = 0;
+ y[2] = output->current_mode->height;
+ y[3] = output->current_mode->height + gr->border.bottom;
+
+ u[0] = 0.0;
+ u[1] = (GLfloat) gr->border.left / gr->border.width;
+ u[2] = (GLfloat) (gr->border.width - gr->border.right) / gr->border.width;
+ u[3] = 1.0;
+
+ v[0] = 0.0;
+ v[1] = (GLfloat) gr->border.top / gr->border.height;
+ v[2] = (GLfloat) (gr->border.height - gr->border.bottom) / gr->border.height;
+ v[3] = 1.0;
+
+ n = 8;
+ d = wl_array_add(&gr->vertices, n * 16 * sizeof *d);
+ p = wl_array_add(&gr->indices, n * 6 * sizeof *p);
+
+ k = 0;
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 3; j++) {
+
+ if (i == 1 && j == 1)
+ continue;
+
+ d[ 0] = x[i];
+ d[ 1] = y[j];
+ d[ 2] = u[i];
+ d[ 3] = v[j];
+
+ d[ 4] = x[i];
+ d[ 5] = y[j + 1];
+ d[ 6] = u[i];
+ d[ 7] = v[j + 1];
+
+ d[ 8] = x[i + 1];
+ d[ 9] = y[j];
+ d[10] = u[i + 1];
+ d[11] = v[j];
+
+ d[12] = x[i + 1];
+ d[13] = y[j + 1];
+ d[14] = u[i + 1];
+ d[15] = v[j + 1];
+
+ p[0] = k + 0;
+ p[1] = k + 1;
+ p[2] = k + 2;
+ p[3] = k + 2;
+ p[4] = k + 1;
+ p[5] = k + 3;
+
+ d += 16;
+ p += 6;
+ k += 4;
+ }
+
+ return k / 4;
+}
+
+static void
+draw_border(struct weston_output *output)
+{
+ struct weston_compositor *ec = output->compositor;
+ struct gl_renderer *gr = get_renderer(ec);
+ struct gl_shader *shader = &gr->texture_shader_rgba;
+ GLfloat *v;
+ int n;
+
+ glDisable(GL_BLEND);
+ use_shader(gr, shader);
+
+ glUniformMatrix4fv(shader->proj_uniform,
+ 1, GL_FALSE, output->matrix.d);
+
+ glUniform1i(shader->tex_uniforms[0], 0);
+ glUniform1f(shader->alpha_uniform, 1);
+
+ n = texture_border(output);
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, gr->border.texture);
+
+ v = gr->vertices.data;
+ glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof *v, &v[0]);
+ glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof *v, &v[2]);
+ glEnableVertexAttribArray(0);
+ glEnableVertexAttribArray(1);
+
+ glDrawElements(GL_TRIANGLES, n * 6,
+ GL_UNSIGNED_SHORT, gr->indices.data);
+
+ glDisableVertexAttribArray(1);
+ glDisableVertexAttribArray(0);
+
+ gr->vertices.size = 0;
+ gr->indices.size = 0;
+}
+
+static void
+output_get_buffer_damage(struct weston_output *output,
+ pixman_region32_t *buffer_damage)
+{
+ struct gl_output_state *go = get_output_state(output);
+ struct gl_renderer *gr = get_renderer(output->compositor);
+ EGLint buffer_age = 0;
+ EGLBoolean ret;
+ int i;
+
+ if (gr->has_egl_buffer_age) {
+ ret = eglQuerySurface(gr->egl_display, go->egl_surface,
+ EGL_BUFFER_AGE_EXT, &buffer_age);
+ if (ret == EGL_FALSE) {
+ weston_log("buffer age query failed.\n");
+ gl_renderer_print_egl_error_state();
+ }
+ }
+
+ if (buffer_age == 0 || buffer_age - 1 > BUFFER_DAMAGE_COUNT)
+ pixman_region32_copy(buffer_damage, &output->region);
+ else
+ for (i = 0; i < buffer_age - 1; i++)
+ pixman_region32_union(buffer_damage, buffer_damage,
+ &go->buffer_damage[i]);
+}
+
+static void
+output_rotate_damage(struct weston_output *output,
+ pixman_region32_t *output_damage)
+{
+ struct gl_output_state *go = get_output_state(output);
+ struct gl_renderer *gr = get_renderer(output->compositor);
+ int i;
+
+ if (!gr->has_egl_buffer_age)
+ return;
+
+ for (i = BUFFER_DAMAGE_COUNT - 1; i >= 1; i--)
+ pixman_region32_copy(&go->buffer_damage[i],
+ &go->buffer_damage[i - 1]);
+
+ pixman_region32_copy(&go->buffer_damage[0], output_damage);
+}
+
+static void
+gl_renderer_repaint_output(struct weston_output *output,
+ pixman_region32_t *output_damage)
+{
+ struct gl_output_state *go = get_output_state(output);
+ struct weston_compositor *compositor = output->compositor;
+ struct gl_renderer *gr = get_renderer(compositor);
+ EGLBoolean ret;
+ static int errored;
+ int32_t width, height;
+ pixman_region32_t buffer_damage, total_damage;
+
+ width = output->current_mode->width +
+ output->border.left + output->border.right;
+ height = output->current_mode->height +
+ output->border.top + output->border.bottom;
+
+ glViewport(0, 0, width, height);
+
+ if (use_output(output) < 0)
+ return;
+
+ /* if debugging, redraw everything outside the damage to clean up
+ * debug lines from the previous draw on this buffer:
+ */
+ if (gr->fan_debug) {
+ pixman_region32_t undamaged;
+ pixman_region32_init(&undamaged);
+ pixman_region32_subtract(&undamaged, &output->region,
+ output_damage);
+ gr->fan_debug = 0;
+ repaint_surfaces(output, &undamaged);
+ gr->fan_debug = 1;
+ pixman_region32_fini(&undamaged);
+ }
+
+ pixman_region32_init(&total_damage);
+ pixman_region32_init(&buffer_damage);
+
+ output_get_buffer_damage(output, &buffer_damage);
+ output_rotate_damage(output, output_damage);
+
+ pixman_region32_union(&total_damage, &buffer_damage, output_damage);
+
+ repaint_surfaces(output, &total_damage);
+
+ pixman_region32_fini(&total_damage);
+ pixman_region32_fini(&buffer_damage);
+
+ if (gr->border.texture)
+ draw_border(output);
+
+ pixman_region32_copy(&output->previous_damage, output_damage);
+ wl_signal_emit(&output->frame_signal, output);
+
+ ret = eglSwapBuffers(gr->egl_display, go->egl_surface);
+ if (ret == EGL_FALSE && !errored) {
+ errored = 1;
+ weston_log("Failed in eglSwapBuffers.\n");
+ gl_renderer_print_egl_error_state();
+ }
+
+}
+
+static int
+gl_renderer_read_pixels(struct weston_output *output,
+ pixman_format_code_t format, void *pixels,
+ uint32_t x, uint32_t y,
+ uint32_t width, uint32_t height)
+{
+ GLenum gl_format;
+
+ switch (format) {
+ case PIXMAN_a8r8g8b8:
+ gl_format = GL_BGRA_EXT;
+ break;
+ case PIXMAN_a8b8g8r8:
+ gl_format = GL_RGBA;
+ break;
+ default:
+ return -1;
+ }
+
+ if (use_output(output) < 0)
+ return -1;
+
+ glPixelStorei(GL_PACK_ALIGNMENT, 1);
+ glReadPixels(x, y, width, height, gl_format,
+ GL_UNSIGNED_BYTE, pixels);
+
+ return 0;
+}
+
+static void
+gl_renderer_flush_damage(struct weston_surface *surface)
+{
+ struct gl_renderer *gr = get_renderer(surface->compositor);
+ struct gl_surface_state *gs = get_surface_state(surface);
+ struct weston_buffer *buffer = gs->buffer_ref.buffer;
+ GLenum format;
+ int pixel_type;
+
+#ifdef GL_EXT_unpack_subimage
+ pixman_box32_t *rectangles;
+ void *data;
+ int i, n;
+#endif
+
+ pixman_region32_union(&gs->texture_damage,
+ &gs->texture_damage, &surface->damage);
+
+ if (!buffer)
+ return;
+
+ /* Avoid upload, if the texture won't be used this time.
+ * We still accumulate the damage in texture_damage, and
+ * hold the reference to the buffer, in case the surface
+ * migrates back to the primary plane.
+ */
+ if (surface->plane != &surface->compositor->primary_plane)
+ return;
+
+ if (!pixman_region32_not_empty(&gs->texture_damage))
+ goto done;
+
+ switch (wl_shm_buffer_get_format(buffer->shm_buffer)) {
+ case WL_SHM_FORMAT_XRGB8888:
+ case WL_SHM_FORMAT_ARGB8888:
+ format = GL_BGRA_EXT;
+ pixel_type = GL_UNSIGNED_BYTE;
+ break;
+ case WL_SHM_FORMAT_RGB565:
+ format = GL_RGB;
+ pixel_type = GL_UNSIGNED_SHORT_5_6_5;
+ break;
+ default:
+ weston_log("warning: unknown shm buffer format\n");
+ format = GL_BGRA_EXT;
+ pixel_type = GL_UNSIGNED_BYTE;
+ }
+
+ glBindTexture(GL_TEXTURE_2D, gs->textures[0]);
+
+ if (!gr->has_unpack_subimage) {
+ glTexImage2D(GL_TEXTURE_2D, 0, format,
+ gs->pitch, buffer->height, 0,
+ format, pixel_type,
+ wl_shm_buffer_get_data(buffer->shm_buffer));
+
+ goto done;
+ }
+
+#ifdef GL_EXT_unpack_subimage
+ glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, gs->pitch);
+ data = wl_shm_buffer_get_data(buffer->shm_buffer);
+
+ if (gs->needs_full_upload) {
+ glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, 0);
+ glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, 0);
+ glTexSubImage2D(GL_TEXTURE_2D, 0,
+ 0, 0, gs->pitch, buffer->height,
+ format, pixel_type, data);
+ goto done;
+ }
+
+ rectangles = pixman_region32_rectangles(&gs->texture_damage, &n);
+ for (i = 0; i < n; i++) {
+ pixman_box32_t r;
+
+ r = weston_surface_to_buffer_rect(surface, rectangles[i]);
+
+ glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, r.x1);
+ glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, r.y1);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, r.x1, r.y1,
+ r.x2 - r.x1, r.y2 - r.y1,
+ format, pixel_type, data);
+ }
+#endif
+
+done:
+ pixman_region32_fini(&gs->texture_damage);
+ pixman_region32_init(&gs->texture_damage);
+ gs->needs_full_upload = 0;
+
+ weston_buffer_reference(&gs->buffer_ref, NULL);
+}
+
+static void
+ensure_textures(struct gl_surface_state *gs, int num_textures)
+{
+ int i;
+
+ if (num_textures <= gs->num_textures)
+ return;
+
+ for (i = gs->num_textures; i < num_textures; i++) {
+ glGenTextures(1, &gs->textures[i]);
+ glBindTexture(gs->target, gs->textures[i]);
+ glTexParameteri(gs->target,
+ GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(gs->target,
+ GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ }
+ gs->num_textures = num_textures;
+ glBindTexture(gs->target, 0);
+}
+
+static void
+gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer,
+ struct wl_shm_buffer *shm_buffer)
+{
+ struct weston_compositor *ec = es->compositor;
+ struct gl_renderer *gr = get_renderer(ec);
+ struct gl_surface_state *gs = get_surface_state(es);
+ int pitch;
+
+ buffer->shm_buffer = shm_buffer;
+ buffer->width = wl_shm_buffer_get_width(shm_buffer);
+ buffer->height = wl_shm_buffer_get_height(shm_buffer);
+
+ switch (wl_shm_buffer_get_format(shm_buffer)) {
+ case WL_SHM_FORMAT_XRGB8888:
+ gs->shader = &gr->texture_shader_rgbx;
+ pitch = wl_shm_buffer_get_stride(shm_buffer) / 4;
+ break;
+ case WL_SHM_FORMAT_ARGB8888:
+ gs->shader = &gr->texture_shader_rgba;
+ pitch = wl_shm_buffer_get_stride(shm_buffer) / 4;
+ break;
+ case WL_SHM_FORMAT_RGB565:
+ gs->shader = &gr->texture_shader_rgbx;
+ pitch = wl_shm_buffer_get_stride(shm_buffer) / 2;
+ break;
+ default:
+ weston_log("warning: unknown shm buffer format\n");
+ gs->shader = &gr->texture_shader_rgba;
+ pitch = wl_shm_buffer_get_stride(shm_buffer) / 4;
+ }
+
+ /* Only allocate a texture if it doesn't match existing one.
+ * If a switch from DRM allocated buffer to a SHM buffer is
+ * happening, we need to allocate a new texture buffer. */
+ if (pitch != gs->pitch ||
+ buffer->height != gs->height ||
+ gs->buffer_type != BUFFER_TYPE_SHM) {
+ gs->pitch = pitch;
+ gs->height = buffer->height;
+ gs->target = GL_TEXTURE_2D;
+ gs->buffer_type = BUFFER_TYPE_SHM;
+ gs->needs_full_upload = 1;
+ gs->y_inverted = 1;
+
+ ensure_textures(gs, 1);
+ glBindTexture(GL_TEXTURE_2D, gs->textures[0]);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA_EXT,
+ gs->pitch, buffer->height, 0,
+ GL_BGRA_EXT, GL_UNSIGNED_BYTE, NULL);
+ }
+}
+
+static void
+gl_renderer_attach_egl(struct weston_surface *es, struct weston_buffer *buffer,
+ uint32_t format)
+{
+ struct weston_compositor *ec = es->compositor;
+ struct gl_renderer *gr = get_renderer(ec);
+ struct gl_surface_state *gs = get_surface_state(es);
+ EGLint attribs[3];
+ int i, num_planes;
+
+ buffer->legacy_buffer = (struct wl_buffer *)buffer->resource;
+ gr->query_buffer(gr->egl_display, buffer->legacy_buffer,
+ EGL_WIDTH, &buffer->width);
+ gr->query_buffer(gr->egl_display, buffer->legacy_buffer,
+ EGL_HEIGHT, &buffer->height);
+ gr->query_buffer(gr->egl_display, buffer->legacy_buffer,
+ EGL_WAYLAND_Y_INVERTED_WL, &buffer->y_inverted);
+
+ for (i = 0; i < gs->num_images; i++)
+ gr->destroy_image(gr->egl_display, gs->images[i]);
+ gs->num_images = 0;
+ gs->target = GL_TEXTURE_2D;
+ switch (format) {
+ case EGL_TEXTURE_RGB:
+ case EGL_TEXTURE_RGBA:
+ default:
+ num_planes = 1;
+ gs->shader = &gr->texture_shader_rgba;
+ break;
+ case EGL_TEXTURE_EXTERNAL_WL:
+ num_planes = 1;
+ gs->target = GL_TEXTURE_EXTERNAL_OES;
+ gs->shader = &gr->texture_shader_egl_external;
+ break;
+ case EGL_TEXTURE_Y_UV_WL:
+ num_planes = 2;
+ gs->shader = &gr->texture_shader_y_uv;
+ break;
+ case EGL_TEXTURE_Y_U_V_WL:
+ num_planes = 3;
+ gs->shader = &gr->texture_shader_y_u_v;
+ break;
+ case EGL_TEXTURE_Y_XUXV_WL:
+ num_planes = 2;
+ gs->shader = &gr->texture_shader_y_xuxv;
+ break;
+ }
+
+ ensure_textures(gs, num_planes);
+ for (i = 0; i < num_planes; i++) {
+ attribs[0] = EGL_WAYLAND_PLANE_WL;
+ attribs[1] = i;
+ attribs[2] = EGL_NONE;
+ gs->images[i] = gr->create_image(gr->egl_display,
+ NULL,
+ EGL_WAYLAND_BUFFER_WL,
+ buffer->legacy_buffer,
+ attribs);
+ if (!gs->images[i]) {
+ weston_log("failed to create img for plane %d\n", i);
+ continue;
+ }
+ gs->num_images++;
+
+ glActiveTexture(GL_TEXTURE0 + i);
+ glBindTexture(gs->target, gs->textures[i]);
+ gr->image_target_texture_2d(gs->target,
+ gs->images[i]);
+ }
+
+ gs->pitch = buffer->width;
+ gs->height = buffer->height;
+ gs->buffer_type = BUFFER_TYPE_EGL;
+ gs->y_inverted = buffer->y_inverted;
+}
+
+static void
+gl_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer)
+{
+ struct weston_compositor *ec = es->compositor;
+ struct gl_renderer *gr = get_renderer(ec);
+ struct gl_surface_state *gs = get_surface_state(es);
+ struct wl_shm_buffer *shm_buffer;
+ EGLint format;
+ int i;
+
+ weston_buffer_reference(&gs->buffer_ref, buffer);
+
+ if (!buffer) {
+ for (i = 0; i < gs->num_images; i++) {
+ gr->destroy_image(gr->egl_display, gs->images[i]);
+ gs->images[i] = NULL;
+ }
+ gs->num_images = 0;
+ glDeleteTextures(gs->num_textures, gs->textures);
+ gs->num_textures = 0;
+ gs->buffer_type = BUFFER_TYPE_NULL;
+ gs->y_inverted = 1;
+ return;
+ }
+
+ shm_buffer = wl_shm_buffer_get(buffer->resource);
+
+ if (shm_buffer)
+ gl_renderer_attach_shm(es, buffer, shm_buffer);
+ else if (gr->query_buffer(gr->egl_display, (void *) buffer->resource,
+ EGL_TEXTURE_FORMAT, &format))
+ gl_renderer_attach_egl(es, buffer, format);
+ else {
+ weston_log("unhandled buffer type!\n");
+ weston_buffer_reference(&gs->buffer_ref, NULL);
+ gs->buffer_type = BUFFER_TYPE_NULL;
+ gs->y_inverted = 1;
+ }
+}
+
+static void
+gl_renderer_surface_set_color(struct weston_surface *surface,
+ float red, float green, float blue, float alpha)
+{
+ struct gl_surface_state *gs = get_surface_state(surface);
+ struct gl_renderer *gr = get_renderer(surface->compositor);
+
+ gs->color[0] = red;
+ gs->color[1] = green;
+ gs->color[2] = blue;
+ gs->color[3] = alpha;
+
+ gs->shader = &gr->solid_shader;
+}
+
+static int
+gl_renderer_create_surface(struct weston_surface *surface)
+{
+ struct gl_surface_state *gs;
+
+ gs = calloc(1, sizeof *gs);
+ if (!gs)
+ return -1;
+
+ /* A buffer is never attached to solid color surfaces, yet
+ * they still go through texcoord computations. Do not divide
+ * by zero there.
+ */
+ gs->pitch = 1;
+ gs->y_inverted = 1;
+
+ pixman_region32_init(&gs->texture_damage);
+ surface->renderer_state = gs;
+
+ return 0;
+}
+
+static void
+gl_renderer_destroy_surface(struct weston_surface *surface)
+{
+ struct gl_surface_state *gs = get_surface_state(surface);
+ struct gl_renderer *gr = get_renderer(surface->compositor);
+ int i;
+
+ glDeleteTextures(gs->num_textures, gs->textures);
+
+ for (i = 0; i < gs->num_images; i++)
+ gr->destroy_image(gr->egl_display, gs->images[i]);
+
+ weston_buffer_reference(&gs->buffer_ref, NULL);
+ pixman_region32_fini(&gs->texture_damage);
+ free(gs);
+}
+
+static const char vertex_shader[] =
+ "uniform mat4 proj;\n"
+ "attribute vec2 position;\n"
+ "attribute vec2 texcoord;\n"
+ "varying vec2 v_texcoord;\n"
+ "void main()\n"
+ "{\n"
+ " gl_Position = proj * vec4(position, 0.0, 1.0);\n"
+ " v_texcoord = texcoord;\n"
+ "}\n";
+
+/* Declare common fragment shader uniforms */
+#define FRAGMENT_CONVERT_YUV \
+ " y *= alpha;\n" \
+ " u *= alpha;\n" \
+ " v *= alpha;\n" \
+ " gl_FragColor.r = y + 1.59602678 * v;\n" \
+ " gl_FragColor.g = y - 0.39176229 * u - 0.81296764 * v;\n" \
+ " gl_FragColor.b = y + 2.01723214 * u;\n" \
+ " gl_FragColor.a = alpha;\n"
+
+static const char fragment_debug[] =
+ " gl_FragColor = vec4(0.0, 0.3, 0.0, 0.2) + gl_FragColor * 0.8;\n";
+
+static const char fragment_brace[] =
+ "}\n";
+
+static const char texture_fragment_shader_rgba[] =
+ "precision mediump float;\n"
+ "varying vec2 v_texcoord;\n"
+ "uniform sampler2D tex;\n"
+ "uniform float alpha;\n"
+ "void main()\n"
+ "{\n"
+ " gl_FragColor = alpha * texture2D(tex, v_texcoord)\n;"
+ ;
+
+static const char texture_fragment_shader_rgbx[] =
+ "precision mediump float;\n"
+ "varying vec2 v_texcoord;\n"
+ "uniform sampler2D tex;\n"
+ "uniform float alpha;\n"
+ "void main()\n"
+ "{\n"
+ " gl_FragColor.rgb = alpha * texture2D(tex, v_texcoord).rgb\n;"
+ " gl_FragColor.a = alpha;\n"
+ ;
+
+static const char texture_fragment_shader_egl_external[] =
+ "#extension GL_OES_EGL_image_external : require\n"
+ "precision mediump float;\n"
+ "varying vec2 v_texcoord;\n"
+ "uniform samplerExternalOES tex;\n"
+ "uniform float alpha;\n"
+ "void main()\n"
+ "{\n"
+ " gl_FragColor = alpha * texture2D(tex, v_texcoord)\n;"
+ ;
+
+static const char texture_fragment_shader_y_uv[] =
+ "precision mediump float;\n"
+ "uniform sampler2D tex;\n"
+ "uniform sampler2D tex1;\n"
+ "varying vec2 v_texcoord;\n"
+ "uniform float alpha;\n"
+ "void main() {\n"
+ " float y = 1.16438356 * (texture2D(tex, v_texcoord).x - 0.0625);\n"
+ " float u = texture2D(tex1, v_texcoord).r - 0.5;\n"
+ " float v = texture2D(tex1, v_texcoord).g - 0.5;\n"
+ FRAGMENT_CONVERT_YUV
+ ;
+
+static const char texture_fragment_shader_y_u_v[] =
+ "precision mediump float;\n"
+ "uniform sampler2D tex;\n"
+ "uniform sampler2D tex1;\n"
+ "uniform sampler2D tex2;\n"
+ "varying vec2 v_texcoord;\n"
+ "uniform float alpha;\n"
+ "void main() {\n"
+ " float y = 1.16438356 * (texture2D(tex, v_texcoord).x - 0.0625);\n"
+ " float u = texture2D(tex1, v_texcoord).x - 0.5;\n"
+ " float v = texture2D(tex2, v_texcoord).x - 0.5;\n"
+ FRAGMENT_CONVERT_YUV
+ ;
+
+static const char texture_fragment_shader_y_xuxv[] =
+ "precision mediump float;\n"
+ "uniform sampler2D tex;\n"
+ "uniform sampler2D tex1;\n"
+ "varying vec2 v_texcoord;\n"
+ "uniform float alpha;\n"
+ "void main() {\n"
+ " float y = 1.16438356 * (texture2D(tex, v_texcoord).x - 0.0625);\n"
+ " float u = texture2D(tex1, v_texcoord).g - 0.5;\n"
+ " float v = texture2D(tex1, v_texcoord).a - 0.5;\n"
+ FRAGMENT_CONVERT_YUV
+ ;
+
+static const char solid_fragment_shader[] =
+ "precision mediump float;\n"
+ "uniform vec4 color;\n"
+ "uniform float alpha;\n"
+ "void main()\n"
+ "{\n"
+ " gl_FragColor = alpha * color\n;"
+ ;
+
+static int
+compile_shader(GLenum type, int count, const char **sources)
+{
+ GLuint s;
+ char msg[512];
+ GLint status;
+
+ s = glCreateShader(type);
+ glShaderSource(s, count, sources, NULL);
+ glCompileShader(s);
+ glGetShaderiv(s, GL_COMPILE_STATUS, &status);
+ if (!status) {
+ glGetShaderInfoLog(s, sizeof msg, NULL, msg);
+ weston_log("shader info: %s\n", msg);
+ return GL_NONE;
+ }
+
+ return s;
+}
+
+static int
+shader_init(struct gl_shader *shader, struct gl_renderer *renderer,
+ const char *vertex_source, const char *fragment_source)
+{
+ char msg[512];
+ GLint status;
+ int count;
+ const char *sources[3];
+
+ shader->vertex_shader =
+ compile_shader(GL_VERTEX_SHADER, 1, &vertex_source);
+
+ if (renderer->fragment_shader_debug) {
+ sources[0] = fragment_source;
+ sources[1] = fragment_debug;
+ sources[2] = fragment_brace;
+ count = 3;
+ } else {
+ sources[0] = fragment_source;
+ sources[1] = fragment_brace;
+ count = 2;
+ }
+
+ shader->fragment_shader =
+ compile_shader(GL_FRAGMENT_SHADER, count, sources);
+
+ shader->program = glCreateProgram();
+ glAttachShader(shader->program, shader->vertex_shader);
+ glAttachShader(shader->program, shader->fragment_shader);
+ glBindAttribLocation(shader->program, 0, "position");
+ glBindAttribLocation(shader->program, 1, "texcoord");
+
+ glLinkProgram(shader->program);
+ glGetProgramiv(shader->program, GL_LINK_STATUS, &status);
+ if (!status) {
+ glGetProgramInfoLog(shader->program, sizeof msg, NULL, msg);
+ weston_log("link info: %s\n", msg);
+ return -1;
+ }
+
+ shader->proj_uniform = glGetUniformLocation(shader->program, "proj");
+ shader->tex_uniforms[0] = glGetUniformLocation(shader->program, "tex");
+ shader->tex_uniforms[1] = glGetUniformLocation(shader->program, "tex1");
+ shader->tex_uniforms[2] = glGetUniformLocation(shader->program, "tex2");
+ shader->alpha_uniform = glGetUniformLocation(shader->program, "alpha");
+ shader->color_uniform = glGetUniformLocation(shader->program, "color");
+
+ return 0;
+}
+
+static void
+shader_release(struct gl_shader *shader)
+{
+ glDeleteShader(shader->vertex_shader);
+ glDeleteShader(shader->fragment_shader);
+ glDeleteProgram(shader->program);
+
+ shader->vertex_shader = 0;
+ shader->fragment_shader = 0;
+ shader->program = 0;
+}
+
+static void
+log_extensions(const char *name, const char *extensions)
+{
+ const char *p, *end;
+ int l;
+ int len;
+
+ l = weston_log("%s:", name);
+ p = extensions;
+ while (*p) {
+ end = strchrnul(p, ' ');
+ len = end - p;
+ if (l + len > 78)
+ l = weston_log_continue("\n" STAMP_SPACE "%.*s",
+ len, p);
+ else
+ l += weston_log_continue(" %.*s", len, p);
+ for (p = end; isspace(*p); p++)
+ ;
+ }
+ weston_log_continue("\n");
+}
+
+static void
+log_egl_gl_info(EGLDisplay egldpy)
+{
+ const char *str;
+
+ str = eglQueryString(egldpy, EGL_VERSION);
+ weston_log("EGL version: %s\n", str ? str : "(null)");
+
+ str = eglQueryString(egldpy, EGL_VENDOR);
+ weston_log("EGL vendor: %s\n", str ? str : "(null)");
+
+ str = eglQueryString(egldpy, EGL_CLIENT_APIS);
+ weston_log("EGL client APIs: %s\n", str ? str : "(null)");
+
+ str = eglQueryString(egldpy, EGL_EXTENSIONS);
+ log_extensions("EGL extensions", str ? str : "(null)");
+
+ str = (char *)glGetString(GL_VERSION);
+ weston_log("GL version: %s\n", str ? str : "(null)");
+
+ str = (char *)glGetString(GL_SHADING_LANGUAGE_VERSION);
+ weston_log("GLSL version: %s\n", str ? str : "(null)");
+
+ str = (char *)glGetString(GL_VENDOR);
+ weston_log("GL vendor: %s\n", str ? str : "(null)");
+
+ str = (char *)glGetString(GL_RENDERER);
+ weston_log("GL renderer: %s\n", str ? str : "(null)");
+
+ str = (char *)glGetString(GL_EXTENSIONS);
+ log_extensions("GL extensions", str ? str : "(null)");
+}
+
+static void
+log_egl_config_info(EGLDisplay egldpy, EGLConfig eglconfig)
+{
+ EGLint r, g, b, a;
+
+ weston_log("Chosen EGL config details:\n");
+
+ weston_log_continue(STAMP_SPACE "RGBA bits");
+ if (eglGetConfigAttrib(egldpy, eglconfig, EGL_RED_SIZE, &r) &&
+ eglGetConfigAttrib(egldpy, eglconfig, EGL_GREEN_SIZE, &g) &&
+ eglGetConfigAttrib(egldpy, eglconfig, EGL_BLUE_SIZE, &b) &&
+ eglGetConfigAttrib(egldpy, eglconfig, EGL_ALPHA_SIZE, &a))
+ weston_log_continue(": %d %d %d %d\n", r, g, b, a);
+ else
+ weston_log_continue(" unknown\n");
+
+ weston_log_continue(STAMP_SPACE "swap interval range");
+ if (eglGetConfigAttrib(egldpy, eglconfig, EGL_MIN_SWAP_INTERVAL, &a) &&
+ eglGetConfigAttrib(egldpy, eglconfig, EGL_MAX_SWAP_INTERVAL, &b))
+ weston_log_continue(": %d - %d\n", a, b);
+ else
+ weston_log_continue(" unknown\n");
+}
+
+static void
+output_apply_border(struct weston_output *output, struct gl_renderer *gr)
+{
+ output->border.top = gr->border.top;
+ output->border.bottom = gr->border.bottom;
+ output->border.left = gr->border.left;
+ output->border.right = gr->border.right;
+}
+
+WL_EXPORT void
+gl_renderer_set_border(struct weston_compositor *ec, int32_t width, int32_t height, void *data,
+ int32_t *edges)
+{
+ struct gl_renderer *gr = get_renderer(ec);
+ struct weston_output *output;
+
+ gr->border.left = edges[0];
+ gr->border.right = edges[1];
+ gr->border.top = edges[2];
+ gr->border.bottom = edges[3];
+
+ gr->border.width = width;
+ gr->border.height = height;
+
+ glGenTextures(1, &gr->border.texture);
+ glBindTexture(GL_TEXTURE_2D, gr->border.texture);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA_EXT,
+ width,
+ height,
+ 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE,
+ data);
+
+ wl_list_for_each(output, &ec->output_list, link)
+ output_apply_border(output, gr);
+}
+
+static int
+gl_renderer_setup(struct weston_compositor *ec, EGLSurface egl_surface);
+
+WL_EXPORT int
+gl_renderer_output_create(struct weston_output *output,
+ EGLNativeWindowType window)
+{
+ struct weston_compositor *ec = output->compositor;
+ struct gl_renderer *gr = get_renderer(ec);
+ struct gl_output_state *go = calloc(1, sizeof *go);
+ int i;
+
+ if (!go)
+ return -1;
+
+ go->egl_surface =
+ eglCreateWindowSurface(gr->egl_display,
+ gr->egl_config,
+ window, NULL);
+
+ if (go->egl_surface == EGL_NO_SURFACE) {
+ weston_log("failed to create egl surface\n");
+ free(go);
+ return -1;
+ }
+
+ if (gr->egl_context == NULL)
+ if (gl_renderer_setup(ec, go->egl_surface) < 0) {
+ free(go);
+ return -1;
+ }
+
+ for (i = 0; i < BUFFER_DAMAGE_COUNT; i++)
+ pixman_region32_init(&go->buffer_damage[i]);
+
+ output->renderer_state = go;
+
+ output_apply_border(output, gr);
+
+ return 0;
+}
+
+WL_EXPORT void
+gl_renderer_output_destroy(struct weston_output *output)
+{
+ struct gl_renderer *gr = get_renderer(output->compositor);
+ struct gl_output_state *go = get_output_state(output);
+ int i;
+
+ for (i = 0; i < 2; i++)
+ pixman_region32_fini(&go->buffer_damage[i]);
+
+ eglDestroySurface(gr->egl_display, go->egl_surface);
+
+ free(go);
+}
+
+WL_EXPORT EGLSurface
+gl_renderer_output_surface(struct weston_output *output)
+{
+ return get_output_state(output)->egl_surface;
+}
+
+static void
+gl_renderer_destroy(struct weston_compositor *ec)
+{
+ struct gl_renderer *gr = get_renderer(ec);
+
+ if (gr->has_bind_display)
+ gr->unbind_display(gr->egl_display, ec->wl_display);
+
+ /* Work around crash in egl_dri2.c's dri2_make_current() - when does this apply? */
+ eglMakeCurrent(gr->egl_display,
+ EGL_NO_SURFACE, EGL_NO_SURFACE,
+ EGL_NO_CONTEXT);
+
+ eglTerminate(gr->egl_display);
+ eglReleaseThread();
+
+ wl_array_release(&gr->vertices);
+ wl_array_release(&gr->indices);
+ wl_array_release(&gr->vtxcnt);
+
+ free(gr);
+}
+
+static int
+egl_choose_config(struct gl_renderer *gr, const EGLint *attribs,
+ const EGLint *visual_id)
+{
+ EGLint count = 0;
+ EGLint matched = 0;
+ EGLConfig *configs;
+ int i;
+
+ if (!eglGetConfigs(gr->egl_display, NULL, 0, &count) || count < 1)
+ return -1;
+
+ configs = calloc(count, sizeof *configs);
+ if (!configs)
+ return -1;
+
+ if (!eglChooseConfig(gr->egl_display, attribs, configs,
+ count, &matched))
+ goto out;
+
+ for (i = 0; i < matched; ++i) {
+ EGLint id;
+
+ if (visual_id) {
+ if (!eglGetConfigAttrib(gr->egl_display,
+ configs[i], EGL_NATIVE_VISUAL_ID,
+ &id))
+ continue;
+
+ if (id != 0 && id != *visual_id)
+ continue;
+ }
+
+ gr->egl_config = configs[i];
+
+ free(configs);
+ return 0;
+ }
+
+out:
+ free(configs);
+ return -1;
+}
+
+WL_EXPORT const EGLint gl_renderer_opaque_attribs[] = {
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_RED_SIZE, 1,
+ EGL_GREEN_SIZE, 1,
+ EGL_BLUE_SIZE, 1,
+ EGL_ALPHA_SIZE, 0,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_NONE
+};
+
+WL_EXPORT const EGLint gl_renderer_alpha_attribs[] = {
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_RED_SIZE, 1,
+ EGL_GREEN_SIZE, 1,
+ EGL_BLUE_SIZE, 1,
+ EGL_ALPHA_SIZE, 1,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_NONE
+};
+
+WL_EXPORT int
+gl_renderer_create(struct weston_compositor *ec, EGLNativeDisplayType display,
+ const EGLint *attribs, const EGLint *visual_id)
+{
+ struct gl_renderer *gr;
+ EGLint major, minor;
+
+ gr = calloc(1, sizeof *gr);
+
+ if (gr == NULL)
+ return -1;
+
+ gr->base.read_pixels = gl_renderer_read_pixels;
+ gr->base.repaint_output = gl_renderer_repaint_output;
+ gr->base.flush_damage = gl_renderer_flush_damage;
+ gr->base.attach = gl_renderer_attach;
+ gr->base.create_surface = gl_renderer_create_surface;
+ gr->base.surface_set_color = gl_renderer_surface_set_color;
+ gr->base.destroy_surface = gl_renderer_destroy_surface;
+ gr->base.destroy = gl_renderer_destroy;
+
+ gr->egl_display = eglGetDisplay(display);
+ if (gr->egl_display == EGL_NO_DISPLAY) {
+ weston_log("failed to create display\n");
+ goto err_egl;
+ }
+
+ if (!eglInitialize(gr->egl_display, &major, &minor)) {
+ weston_log("failed to initialize display\n");
+ goto err_egl;
+ }
+
+ if (egl_choose_config(gr, attribs, visual_id) < 0) {
+ weston_log("failed to choose EGL config\n");
+ goto err_egl;
+ }
+
+ ec->renderer = &gr->base;
+ ec->capabilities |= WESTON_CAP_ROTATION_ANY;
+ ec->capabilities |= WESTON_CAP_CAPTURE_YFLIP;
+
+ wl_display_add_shm_format(ec->wl_display, WL_SHM_FORMAT_RGB565);
+
+ return 0;
+
+err_egl:
+ gl_renderer_print_egl_error_state();
+ free(gr);
+ return -1;
+}
+
+WL_EXPORT EGLDisplay
+gl_renderer_display(struct weston_compositor *ec)
+{
+ return get_renderer(ec)->egl_display;
+}
+
+static int
+compile_shaders(struct weston_compositor *ec)
+{
+ struct gl_renderer *gr = get_renderer(ec);
+
+ gr->texture_shader_rgba.vertex_source = vertex_shader;
+ gr->texture_shader_rgba.fragment_source = texture_fragment_shader_rgba;
+
+ gr->texture_shader_rgbx.vertex_source = vertex_shader;
+ gr->texture_shader_rgbx.fragment_source = texture_fragment_shader_rgbx;
+
+ gr->texture_shader_egl_external.vertex_source = vertex_shader;
+ gr->texture_shader_egl_external.fragment_source =
+ texture_fragment_shader_egl_external;
+
+ gr->texture_shader_y_uv.vertex_source = vertex_shader;
+ gr->texture_shader_y_uv.fragment_source = texture_fragment_shader_y_uv;
+
+ gr->texture_shader_y_u_v.vertex_source = vertex_shader;
+ gr->texture_shader_y_u_v.fragment_source =
+ texture_fragment_shader_y_u_v;
+
+ gr->texture_shader_y_u_v.vertex_source = vertex_shader;
+ gr->texture_shader_y_xuxv.fragment_source =
+ texture_fragment_shader_y_xuxv;
+
+ gr->solid_shader.vertex_source = vertex_shader;
+ gr->solid_shader.fragment_source = solid_fragment_shader;
+
+ return 0;
+}
+
+static void
+fragment_debug_binding(struct weston_seat *seat, uint32_t time, uint32_t key,
+ void *data)
+{
+ struct weston_compositor *ec = data;
+ struct gl_renderer *gr = get_renderer(ec);
+ struct weston_output *output;
+
+ gr->fragment_shader_debug ^= 1;
+
+ shader_release(&gr->texture_shader_rgba);
+ shader_release(&gr->texture_shader_rgbx);
+ shader_release(&gr->texture_shader_egl_external);
+ shader_release(&gr->texture_shader_y_uv);
+ shader_release(&gr->texture_shader_y_u_v);
+ shader_release(&gr->texture_shader_y_xuxv);
+ shader_release(&gr->solid_shader);
+
+ /* Force use_shader() to call glUseProgram(), since we need to use
+ * the recompiled version of the shader. */
+ gr->current_shader = NULL;
+
+ wl_list_for_each(output, &ec->output_list, link)
+ weston_output_damage(output);
+}
+
+static void
+fan_debug_repaint_binding(struct weston_seat *seat, uint32_t time, uint32_t key,
+ void *data)
+{
+ struct weston_compositor *compositor = data;
+ struct gl_renderer *gr = get_renderer(compositor);
+
+ gr->fan_debug = !gr->fan_debug;
+ weston_compositor_damage_all(compositor);
+}
+
+static int
+gl_renderer_setup(struct weston_compositor *ec, EGLSurface egl_surface)
+{
+ struct gl_renderer *gr = get_renderer(ec);
+ const char *extensions;
+ EGLBoolean ret;
+
+ static const EGLint context_attribs[] = {
+ EGL_CONTEXT_CLIENT_VERSION, 2,
+ EGL_NONE
+ };
+
+ if (!eglBindAPI(EGL_OPENGL_ES_API)) {
+ weston_log("failed to bind EGL_OPENGL_ES_API\n");
+ gl_renderer_print_egl_error_state();
+ return -1;
+ }
+
+ log_egl_config_info(gr->egl_display, gr->egl_config);
+
+ gr->egl_context = eglCreateContext(gr->egl_display, gr->egl_config,
+ EGL_NO_CONTEXT, context_attribs);
+ if (gr->egl_context == NULL) {
+ weston_log("failed to create context\n");
+ gl_renderer_print_egl_error_state();
+ return -1;
+ }
+
+ ret = eglMakeCurrent(gr->egl_display, egl_surface,
+ egl_surface, gr->egl_context);
+ if (ret == EGL_FALSE) {
+ weston_log("Failed to make EGL context current.\n");
+ gl_renderer_print_egl_error_state();
+ return -1;
+ }
+
+ log_egl_gl_info(gr->egl_display);
+
+ gr->image_target_texture_2d =
+ (void *) eglGetProcAddress("glEGLImageTargetTexture2DOES");
+ gr->create_image = (void *) eglGetProcAddress("eglCreateImageKHR");
+ gr->destroy_image = (void *) eglGetProcAddress("eglDestroyImageKHR");
+ gr->bind_display =
+ (void *) eglGetProcAddress("eglBindWaylandDisplayWL");
+ gr->unbind_display =
+ (void *) eglGetProcAddress("eglUnbindWaylandDisplayWL");
+ gr->query_buffer =
+ (void *) eglGetProcAddress("eglQueryWaylandBufferWL");
+
+ extensions = (const char *) glGetString(GL_EXTENSIONS);
+ if (!extensions) {
+ weston_log("Retrieving GL extension string failed.\n");
+ return -1;
+ }
+
+ if (!strstr(extensions, "GL_EXT_texture_format_BGRA8888")) {
+ weston_log("GL_EXT_texture_format_BGRA8888 not available\n");
+ return -1;
+ }
+
+ if (strstr(extensions, "GL_EXT_read_format_bgra"))
+ ec->read_format = PIXMAN_a8r8g8b8;
+ else
+ ec->read_format = PIXMAN_a8b8g8r8;
+
+#ifdef GL_EXT_unpack_subimage
+ if (strstr(extensions, "GL_EXT_unpack_subimage"))
+ gr->has_unpack_subimage = 1;
+#endif
+
+ if (strstr(extensions, "GL_OES_EGL_image_external"))
+ gr->has_egl_image_external = 1;
+
+ extensions =
+ (const char *) eglQueryString(gr->egl_display, EGL_EXTENSIONS);
+ if (!extensions) {
+ weston_log("Retrieving EGL extension string failed.\n");
+ return -1;
+ }
+
+ if (strstr(extensions, "EGL_WL_bind_wayland_display"))
+ gr->has_bind_display = 1;
+ if (gr->has_bind_display) {
+ ret = gr->bind_display(gr->egl_display, ec->wl_display);
+ if (!ret)
+ gr->has_bind_display = 0;
+ }
+
+ if (strstr(extensions, "EGL_EXT_buffer_age"))
+ gr->has_egl_buffer_age = 1;
+ else
+ weston_log("warning: EGL_EXT_buffer_age not supported. "
+ "Performance could be affected.\n");
+
+ glActiveTexture(GL_TEXTURE0);
+
+ if (compile_shaders(ec))
+ return -1;
+
+ weston_compositor_add_debug_binding(ec, KEY_S,
+ fragment_debug_binding, ec);
+ weston_compositor_add_debug_binding(ec, KEY_F,
+ fan_debug_repaint_binding, ec);
+
+ weston_log("GL ES 2 renderer features:\n");
+ weston_log_continue(STAMP_SPACE "read-back format: %s\n",
+ ec->read_format == PIXMAN_a8r8g8b8 ? "BGRA" : "RGBA");
+ weston_log_continue(STAMP_SPACE "wl_shm sub-image to texture: %s\n",
+ gr->has_unpack_subimage ? "yes" : "no");
+ weston_log_continue(STAMP_SPACE "EGL Wayland extension: %s\n",
+ gr->has_bind_display ? "yes" : "no");
+
+
+ return 0;
+}
diff --git a/src/gl-renderer.h b/src/gl-renderer.h
new file mode 100644
index 00000000..d16ade29
--- /dev/null
+++ b/src/gl-renderer.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright © 2012 John Kåre Alsaker
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that 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.
+ */
+
+#include "config.h"
+
+#include "compositor.h"
+
+#ifdef ENABLE_EGL
+
+#include <EGL/egl.h>
+
+extern const EGLint gl_renderer_opaque_attribs[];
+extern const EGLint gl_renderer_alpha_attribs[];
+
+int
+gl_renderer_create(struct weston_compositor *ec, EGLNativeDisplayType display,
+ const EGLint *attribs, const EGLint *visual_id);
+EGLDisplay
+gl_renderer_display(struct weston_compositor *ec);
+int
+gl_renderer_output_create(struct weston_output *output,
+ EGLNativeWindowType window);
+void
+gl_renderer_output_destroy(struct weston_output *output);
+EGLSurface
+gl_renderer_output_surface(struct weston_output *output);
+void
+gl_renderer_set_border(struct weston_compositor *ec, int32_t width, int32_t height, void *data,
+ int32_t *edges);
+
+void
+gl_renderer_print_egl_error_state(void);
+#else
+
+typedef int EGLint;
+typedef void *EGLDisplay;
+typedef void *EGLSurface;
+typedef intptr_t EGLNativeDisplayType;
+typedef intptr_t EGLNativeWindowType;
+#define EGL_DEFAULT_DISPLAY NULL
+
+static const EGLint gl_renderer_opaque_attribs[];
+static const EGLint gl_renderer_alpha_attribs[];
+
+inline static int
+gl_renderer_create(struct weston_compositor *ec, EGLNativeDisplayType display,
+ const EGLint *attribs, const EGLint *visual_id)
+{
+ return -1;
+}
+
+inline static EGLDisplay
+gl_renderer_display(struct weston_compositor *ec)
+{
+ return 0;
+}
+
+inline static int
+gl_renderer_output_create(struct weston_output *output,
+ EGLNativeWindowType window)
+{
+ return -1;
+}
+
+inline static void
+gl_renderer_output_destroy(struct weston_output *output)
+{
+}
+
+inline static EGLSurface
+gl_renderer_output_surface(struct weston_output *output)
+{
+ return 0;
+}
+
+inline static void
+gl_renderer_set_border(struct weston_compositor *ec, int32_t width, int32_t height, void *data,
+ int32_t *edges)
+{
+}
+
+inline static void
+gl_renderer_print_egl_error_state(void)
+{
+}
+
+#endif
diff --git a/src/input.c b/src/input.c
new file mode 100644
index 00000000..dd8e4973
--- /dev/null
+++ b/src/input.c
@@ -0,0 +1,1851 @@
+/*
+ * Copyright © 2013 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 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.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <assert.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "../shared/os-compatibility.h"
+#include "compositor.h"
+
+static void
+empty_region(pixman_region32_t *region)
+{
+ pixman_region32_fini(region);
+ pixman_region32_init(region);
+}
+
+static void unbind_resource(struct wl_resource *resource)
+{
+ wl_list_remove(wl_resource_get_link(resource));
+}
+
+WL_EXPORT void
+weston_seat_repick(struct weston_seat *seat)
+{
+ const struct weston_pointer *pointer = seat->pointer;
+
+ if (pointer == NULL)
+ return;
+
+ pointer->grab->interface->focus(seat->pointer->grab);
+}
+
+static void
+weston_compositor_idle_inhibit(struct weston_compositor *compositor)
+{
+ weston_compositor_wake(compositor);
+ compositor->idle_inhibit++;
+}
+
+static void
+weston_compositor_idle_release(struct weston_compositor *compositor)
+{
+ compositor->idle_inhibit--;
+ weston_compositor_wake(compositor);
+}
+
+static void
+move_resources(struct wl_list *destination, struct wl_list *source)
+{
+ wl_list_insert_list(destination, source);
+ wl_list_init(source);
+}
+
+static void
+move_resources_for_client(struct wl_list *destination,
+ struct wl_list *source,
+ struct wl_client *client)
+{
+ struct wl_resource *resource, *tmp;
+ wl_resource_for_each_safe(resource, tmp, source) {
+ if (wl_resource_get_client(resource) == client) {
+ wl_list_remove(wl_resource_get_link(resource));
+ wl_list_insert(destination,
+ wl_resource_get_link(resource));
+ }
+ }
+}
+
+static void
+default_grab_focus(struct weston_pointer_grab *grab)
+{
+ struct weston_pointer *pointer = grab->pointer;
+ struct weston_surface *surface;
+ wl_fixed_t sx, sy;
+
+ if (pointer->button_count > 0)
+ return;
+
+ surface = weston_compositor_pick_surface(pointer->seat->compositor,
+ pointer->x, pointer->y,
+ &sx, &sy);
+
+ if (pointer->focus != surface)
+ weston_pointer_set_focus(pointer, surface, sx, sy);
+}
+
+static void
+default_grab_motion(struct weston_pointer_grab *grab, uint32_t time)
+{
+ struct weston_pointer *pointer = grab->pointer;
+ wl_fixed_t sx, sy;
+ struct wl_list *resource_list;
+ struct wl_resource *resource;
+
+ resource_list = &pointer->focus_resource_list;
+ wl_resource_for_each(resource, resource_list) {
+ weston_surface_from_global_fixed(pointer->focus,
+ pointer->x, pointer->y,
+ &sx, &sy);
+ wl_pointer_send_motion(resource, time, sx, sy);
+ }
+}
+
+static void
+default_grab_button(struct weston_pointer_grab *grab,
+ uint32_t time, uint32_t button, uint32_t state_w)
+{
+ struct weston_pointer *pointer = grab->pointer;
+ struct weston_compositor *compositor = pointer->seat->compositor;
+ struct weston_surface *surface;
+ struct wl_resource *resource;
+ uint32_t serial;
+ enum wl_pointer_button_state state = state_w;
+ struct wl_display *display = compositor->wl_display;
+ wl_fixed_t sx, sy;
+ struct wl_list *resource_list;
+
+ resource_list = &pointer->focus_resource_list;
+ if (!wl_list_empty(resource_list)) {
+ serial = wl_display_next_serial(display);
+ wl_resource_for_each(resource, resource_list)
+ wl_pointer_send_button(resource,
+ serial,
+ time,
+ button,
+ state_w);
+ }
+
+ if (pointer->button_count == 0 &&
+ state == WL_POINTER_BUTTON_STATE_RELEASED) {
+ surface = weston_compositor_pick_surface(compositor,
+ pointer->x,
+ pointer->y,
+ &sx, &sy);
+
+ weston_pointer_set_focus(pointer, surface, sx, sy);
+ }
+}
+
+static void
+default_grab_pointer_cancel(struct weston_pointer_grab *grab)
+{
+}
+
+static const struct weston_pointer_grab_interface
+ default_pointer_grab_interface = {
+ default_grab_focus,
+ default_grab_motion,
+ default_grab_button,
+ default_grab_pointer_cancel,
+};
+
+static void
+default_grab_touch_down(struct weston_touch_grab *grab, uint32_t time,
+ int touch_id, wl_fixed_t sx, wl_fixed_t sy)
+{
+ struct weston_touch *touch = grab->touch;
+ struct wl_display *display = touch->seat->compositor->wl_display;
+ uint32_t serial;
+ struct wl_resource *resource;
+ struct wl_list *resource_list;
+
+ resource_list = &touch->focus_resource_list;
+
+ if (!wl_list_empty(resource_list) && touch->focus) {
+ serial = wl_display_next_serial(display);
+ wl_resource_for_each(resource, resource_list)
+ wl_touch_send_down(resource, serial, time,
+ touch->focus->resource,
+ touch_id, sx, sy);
+ }
+}
+
+static void
+default_grab_touch_up(struct weston_touch_grab *grab,
+ uint32_t time, int touch_id)
+{
+ struct weston_touch *touch = grab->touch;
+ struct wl_display *display = touch->seat->compositor->wl_display;
+ uint32_t serial;
+ struct wl_resource *resource;
+ struct wl_list *resource_list;
+
+ resource_list = &touch->focus_resource_list;
+
+ if (!wl_list_empty(resource_list)) {
+ serial = wl_display_next_serial(display);
+ wl_resource_for_each(resource, resource_list)
+ wl_touch_send_up(resource, serial, time, touch_id);
+ }
+}
+
+static void
+default_grab_touch_motion(struct weston_touch_grab *grab, uint32_t time,
+ int touch_id, wl_fixed_t sx, wl_fixed_t sy)
+{
+ struct weston_touch *touch = grab->touch;
+ struct wl_resource *resource;
+ struct wl_list *resource_list;
+
+ resource_list = &touch->focus_resource_list;
+
+ wl_resource_for_each(resource, resource_list) {
+ wl_touch_send_motion(resource, time,
+ touch_id, sx, sy);
+ }
+}
+
+static void
+default_grab_touch_cancel(struct weston_touch_grab *grab)
+{
+}
+
+static const struct weston_touch_grab_interface default_touch_grab_interface = {
+ default_grab_touch_down,
+ default_grab_touch_up,
+ default_grab_touch_motion,
+ default_grab_touch_cancel,
+};
+
+static void
+default_grab_key(struct weston_keyboard_grab *grab,
+ uint32_t time, uint32_t key, uint32_t state)
+{
+ struct weston_keyboard *keyboard = grab->keyboard;
+ struct wl_resource *resource;
+ struct wl_display *display = keyboard->seat->compositor->wl_display;
+ uint32_t serial;
+ struct wl_list *resource_list;
+
+ resource_list = &keyboard->focus_resource_list;
+ if (!wl_list_empty(resource_list)) {
+ serial = wl_display_next_serial(display);
+ wl_resource_for_each(resource, resource_list)
+ wl_keyboard_send_key(resource,
+ serial,
+ time,
+ key,
+ state);
+ }
+}
+
+static void
+send_modifiers_to_resource(struct weston_keyboard *keyboard,
+ struct wl_resource *resource,
+ uint32_t serial)
+{
+ wl_keyboard_send_modifiers(resource,
+ serial,
+ keyboard->modifiers.mods_depressed,
+ keyboard->modifiers.mods_latched,
+ keyboard->modifiers.mods_locked,
+ keyboard->modifiers.group);
+}
+
+static void
+send_modifiers_to_client_in_list(struct wl_client *client,
+ struct wl_list *list,
+ uint32_t serial,
+ struct weston_keyboard *keyboard)
+{
+ struct wl_resource *resource;
+
+ wl_resource_for_each(resource, list) {
+ if (wl_resource_get_client(resource) == client)
+ send_modifiers_to_resource(keyboard,
+ resource,
+ serial);
+ }
+}
+
+static struct wl_resource *
+find_resource_for_surface(struct wl_list *list, struct weston_surface *surface)
+{
+ if (!surface)
+ return NULL;
+
+ if (!surface->resource)
+ return NULL;
+
+ return wl_resource_find_for_client(list, wl_resource_get_client(surface->resource));
+}
+
+static void
+default_grab_modifiers(struct weston_keyboard_grab *grab, uint32_t serial,
+ uint32_t mods_depressed, uint32_t mods_latched,
+ uint32_t mods_locked, uint32_t group)
+{
+ struct weston_keyboard *keyboard = grab->keyboard;
+ struct weston_pointer *pointer = grab->keyboard->seat->pointer;
+ struct wl_resource *resource;
+ struct wl_list *resource_list;
+
+ resource_list = &keyboard->focus_resource_list;
+
+ wl_resource_for_each(resource, resource_list) {
+ wl_keyboard_send_modifiers(resource, serial, mods_depressed,
+ mods_latched, mods_locked, group);
+ }
+ if (pointer && pointer->focus && pointer->focus != keyboard->focus) {
+ struct wl_client *pointer_client =
+ wl_resource_get_client(pointer->focus->resource);
+ send_modifiers_to_client_in_list(pointer_client,
+ &keyboard->resource_list,
+ serial,
+ keyboard);
+ }
+}
+
+static void
+default_grab_keyboard_cancel(struct weston_keyboard_grab *grab)
+{
+}
+
+static const struct weston_keyboard_grab_interface
+ default_keyboard_grab_interface = {
+ default_grab_key,
+ default_grab_modifiers,
+ default_grab_keyboard_cancel,
+};
+
+static void
+pointer_unmap_sprite(struct weston_pointer *pointer)
+{
+ if (weston_surface_is_mapped(pointer->sprite))
+ weston_surface_unmap(pointer->sprite);
+
+ wl_list_remove(&pointer->sprite_destroy_listener.link);
+ pointer->sprite->configure = NULL;
+ pointer->sprite->configure_private = NULL;
+ pointer->sprite = NULL;
+}
+
+static void
+pointer_handle_sprite_destroy(struct wl_listener *listener, void *data)
+{
+ struct weston_pointer *pointer =
+ container_of(listener, struct weston_pointer,
+ sprite_destroy_listener);
+
+ pointer->sprite = NULL;
+}
+
+WL_EXPORT struct weston_pointer *
+weston_pointer_create(void)
+{
+ struct weston_pointer *pointer;
+
+ pointer = zalloc(sizeof *pointer);
+ if (pointer == NULL)
+ return NULL;
+
+ wl_list_init(&pointer->resource_list);
+ wl_list_init(&pointer->focus_resource_list);
+ pointer->default_grab.interface = &default_pointer_grab_interface;
+ pointer->default_grab.pointer = pointer;
+ pointer->grab = &pointer->default_grab;
+ wl_signal_init(&pointer->focus_signal);
+
+ pointer->sprite_destroy_listener.notify = pointer_handle_sprite_destroy;
+
+ /* FIXME: Pick better co-ords. */
+ pointer->x = wl_fixed_from_int(100);
+ pointer->y = wl_fixed_from_int(100);
+
+ return pointer;
+}
+
+WL_EXPORT void
+weston_pointer_destroy(struct weston_pointer *pointer)
+{
+ if (pointer->sprite)
+ pointer_unmap_sprite(pointer);
+
+ /* XXX: What about pointer->resource_list? */
+
+ free(pointer);
+}
+
+WL_EXPORT struct weston_keyboard *
+weston_keyboard_create(void)
+{
+ struct weston_keyboard *keyboard;
+
+ keyboard = zalloc(sizeof *keyboard);
+ if (keyboard == NULL)
+ return NULL;
+
+ wl_list_init(&keyboard->resource_list);
+ wl_list_init(&keyboard->focus_resource_list);
+ wl_array_init(&keyboard->keys);
+ keyboard->default_grab.interface = &default_keyboard_grab_interface;
+ keyboard->default_grab.keyboard = keyboard;
+ keyboard->grab = &keyboard->default_grab;
+ wl_signal_init(&keyboard->focus_signal);
+
+ return keyboard;
+}
+
+WL_EXPORT void
+weston_keyboard_destroy(struct weston_keyboard *keyboard)
+{
+ /* XXX: What about keyboard->resource_list? */
+
+ wl_array_release(&keyboard->keys);
+ free(keyboard);
+}
+
+WL_EXPORT struct weston_touch *
+weston_touch_create(void)
+{
+ struct weston_touch *touch;
+
+ touch = zalloc(sizeof *touch);
+ if (touch == NULL)
+ return NULL;
+
+ wl_list_init(&touch->resource_list);
+ wl_list_init(&touch->focus_resource_list);
+ touch->default_grab.interface = &default_touch_grab_interface;
+ touch->default_grab.touch = touch;
+ touch->grab = &touch->default_grab;
+ wl_signal_init(&touch->focus_signal);
+
+ return touch;
+}
+
+WL_EXPORT void
+weston_touch_destroy(struct weston_touch *touch)
+{
+ /* XXX: What about touch->resource_list? */
+
+ free(touch);
+}
+
+static void
+seat_send_updated_caps(struct weston_seat *seat)
+{
+ enum wl_seat_capability caps = 0;
+ struct wl_resource *resource;
+
+ if (seat->pointer_device_count > 0)
+ caps |= WL_SEAT_CAPABILITY_POINTER;
+ if (seat->keyboard_device_count > 0)
+ caps |= WL_SEAT_CAPABILITY_KEYBOARD;
+ if (seat->touch_device_count > 0)
+ caps |= WL_SEAT_CAPABILITY_TOUCH;
+
+ wl_resource_for_each(resource, &seat->base_resource_list) {
+ wl_seat_send_capabilities(resource, caps);
+ }
+}
+
+WL_EXPORT void
+weston_pointer_set_focus(struct weston_pointer *pointer,
+ struct weston_surface *surface,
+ wl_fixed_t sx, wl_fixed_t sy)
+{
+ struct weston_keyboard *kbd = pointer->seat->keyboard;
+ struct wl_resource *resource;
+ struct wl_display *display = pointer->seat->compositor->wl_display;
+ uint32_t serial;
+ struct wl_list *focus_resource_list;
+
+ focus_resource_list = &pointer->focus_resource_list;
+
+ if (!wl_list_empty(focus_resource_list) && pointer->focus != surface) {
+ serial = wl_display_next_serial(display);
+ wl_resource_for_each(resource, focus_resource_list) {
+ wl_pointer_send_leave(resource, serial,
+ pointer->focus->resource);
+ }
+
+ move_resources(&pointer->resource_list, focus_resource_list);
+ }
+
+ if (find_resource_for_surface(&pointer->resource_list, surface) &&
+ pointer->focus != surface) {
+ struct wl_client *surface_client =
+ wl_resource_get_client(surface->resource);
+
+ serial = wl_display_next_serial(display);
+
+ if (kbd && kbd->focus != pointer->focus)
+ send_modifiers_to_client_in_list(surface_client,
+ &kbd->resource_list,
+ serial,
+ kbd);
+
+ move_resources_for_client(focus_resource_list,
+ &pointer->resource_list,
+ surface_client);
+
+ wl_resource_for_each(resource, focus_resource_list) {
+ wl_pointer_send_enter(resource,
+ serial,
+ surface->resource,
+ sx, sy);
+ }
+
+ pointer->focus_serial = serial;
+ }
+
+ pointer->focus = surface;
+ wl_signal_emit(&pointer->focus_signal, pointer);
+}
+
+static void
+send_enter_to_resource_list(struct wl_list *list,
+ struct weston_keyboard *keyboard,
+ struct weston_surface *surface,
+ uint32_t serial)
+{
+ struct wl_resource *resource;
+
+ wl_resource_for_each(resource, list) {
+ send_modifiers_to_resource(keyboard, resource, serial);
+ wl_keyboard_send_enter(resource, serial,
+ surface->resource,
+ &keyboard->keys);
+ }
+}
+
+WL_EXPORT void
+weston_keyboard_set_focus(struct weston_keyboard *keyboard,
+ struct weston_surface *surface)
+{
+ struct wl_resource *resource;
+ struct wl_display *display = keyboard->seat->compositor->wl_display;
+ uint32_t serial;
+ struct wl_list *focus_resource_list;
+
+ focus_resource_list = &keyboard->focus_resource_list;
+
+ if (!wl_list_empty(focus_resource_list) && keyboard->focus != surface) {
+ serial = wl_display_next_serial(display);
+ wl_resource_for_each(resource, focus_resource_list) {
+ wl_keyboard_send_leave(resource, serial,
+ keyboard->focus->resource);
+ }
+ move_resources(&keyboard->resource_list, focus_resource_list);
+ }
+
+ if (find_resource_for_surface(&keyboard->resource_list, surface) &&
+ keyboard->focus != surface) {
+ struct wl_client *surface_client =
+ wl_resource_get_client(surface->resource);
+
+ serial = wl_display_next_serial(display);
+
+ move_resources_for_client(focus_resource_list,
+ &keyboard->resource_list,
+ surface_client);
+ send_enter_to_resource_list(focus_resource_list,
+ keyboard,
+ surface,
+ serial);
+ keyboard->focus_serial = serial;
+ }
+
+ keyboard->focus = surface;
+ wl_signal_emit(&keyboard->focus_signal, keyboard);
+}
+
+WL_EXPORT void
+weston_keyboard_start_grab(struct weston_keyboard *keyboard,
+ struct weston_keyboard_grab *grab)
+{
+ keyboard->grab = grab;
+ grab->keyboard = keyboard;
+
+ /* XXX focus? */
+}
+
+WL_EXPORT void
+weston_keyboard_end_grab(struct weston_keyboard *keyboard)
+{
+ keyboard->grab = &keyboard->default_grab;
+}
+
+static void
+weston_keyboard_cancel_grab(struct weston_keyboard *keyboard)
+{
+ keyboard->grab->interface->cancel(keyboard->grab);
+}
+
+WL_EXPORT void
+weston_pointer_start_grab(struct weston_pointer *pointer,
+ struct weston_pointer_grab *grab)
+{
+ pointer->grab = grab;
+ grab->pointer = pointer;
+ pointer->grab->interface->focus(pointer->grab);
+}
+
+WL_EXPORT void
+weston_pointer_end_grab(struct weston_pointer *pointer)
+{
+ pointer->grab = &pointer->default_grab;
+ pointer->grab->interface->focus(pointer->grab);
+}
+
+static void
+weston_pointer_cancel_grab(struct weston_pointer *pointer)
+{
+ pointer->grab->interface->cancel(pointer->grab);
+}
+
+WL_EXPORT void
+weston_touch_start_grab(struct weston_touch *touch, struct weston_touch_grab *grab)
+{
+ touch->grab = grab;
+ grab->touch = touch;
+}
+
+WL_EXPORT void
+weston_touch_end_grab(struct weston_touch *touch)
+{
+ touch->grab = &touch->default_grab;
+}
+
+static void
+weston_touch_cancel_grab(struct weston_touch *touch)
+{
+ touch->grab->interface->cancel(touch->grab);
+}
+
+WL_EXPORT void
+weston_pointer_clamp(struct weston_pointer *pointer, wl_fixed_t *fx, wl_fixed_t *fy)
+{
+ struct weston_compositor *ec = pointer->seat->compositor;
+ struct weston_output *output, *prev = NULL;
+ int x, y, old_x, old_y, valid = 0;
+
+ x = wl_fixed_to_int(*fx);
+ y = wl_fixed_to_int(*fy);
+ old_x = wl_fixed_to_int(pointer->x);
+ old_y = wl_fixed_to_int(pointer->y);
+
+ wl_list_for_each(output, &ec->output_list, link) {
+ if (pointer->seat->output && pointer->seat->output != output)
+ continue;
+ if (pixman_region32_contains_point(&output->region,
+ x, y, NULL))
+ valid = 1;
+ if (pixman_region32_contains_point(&output->region,
+ old_x, old_y, NULL))
+ prev = output;
+ }
+
+ if (!prev)
+ prev = pointer->seat->output;
+
+ if (prev && !valid) {
+ if (x < prev->x)
+ *fx = wl_fixed_from_int(prev->x);
+ else if (x >= prev->x + prev->width)
+ *fx = wl_fixed_from_int(prev->x +
+ prev->width - 1);
+ if (y < prev->y)
+ *fy = wl_fixed_from_int(prev->y);
+ else if (y >= prev->y + prev->height)
+ *fy = wl_fixed_from_int(prev->y +
+ prev->height - 1);
+ }
+}
+
+/* Takes absolute values */
+static void
+move_pointer(struct weston_seat *seat, wl_fixed_t x, wl_fixed_t y)
+{
+ struct weston_compositor *ec = seat->compositor;
+ struct weston_pointer *pointer = seat->pointer;
+ struct weston_output *output;
+ int32_t ix, iy;
+
+ weston_pointer_clamp (pointer, &x, &y);
+
+ pointer->x = x;
+ pointer->y = y;
+
+ ix = wl_fixed_to_int(x);
+ iy = wl_fixed_to_int(y);
+
+ wl_list_for_each(output, &ec->output_list, link)
+ if (output->zoom.active &&
+ pixman_region32_contains_point(&output->region,
+ ix, iy, NULL))
+ weston_output_update_zoom(output, ZOOM_FOCUS_POINTER);
+
+ if (pointer->sprite) {
+ weston_surface_set_position(pointer->sprite,
+ ix - pointer->hotspot_x,
+ iy - pointer->hotspot_y);
+ weston_surface_schedule_repaint(pointer->sprite);
+ }
+}
+
+WL_EXPORT void
+notify_motion(struct weston_seat *seat,
+ uint32_t time, wl_fixed_t dx, wl_fixed_t dy)
+{
+ struct weston_compositor *ec = seat->compositor;
+ struct weston_pointer *pointer = seat->pointer;
+
+ weston_compositor_wake(ec);
+
+ move_pointer(seat, pointer->x + dx, pointer->y + dy);
+
+ pointer->grab->interface->focus(pointer->grab);
+ pointer->grab->interface->motion(pointer->grab, time);
+}
+
+WL_EXPORT void
+notify_motion_absolute(struct weston_seat *seat,
+ uint32_t time, wl_fixed_t x, wl_fixed_t y)
+{
+ struct weston_compositor *ec = seat->compositor;
+ struct weston_pointer *pointer = seat->pointer;
+
+ weston_compositor_wake(ec);
+
+ move_pointer(seat, x, y);
+
+ pointer->grab->interface->focus(pointer->grab);
+ pointer->grab->interface->motion(pointer->grab, time);
+}
+
+WL_EXPORT void
+weston_surface_activate(struct weston_surface *surface,
+ struct weston_seat *seat)
+{
+ struct weston_compositor *compositor = seat->compositor;
+
+ if (seat->keyboard) {
+ weston_keyboard_set_focus(seat->keyboard, surface);
+ wl_data_device_set_keyboard_focus(seat);
+ }
+
+ wl_signal_emit(&compositor->activate_signal, surface);
+}
+
+WL_EXPORT void
+notify_button(struct weston_seat *seat, uint32_t time, int32_t button,
+ enum wl_pointer_button_state state)
+{
+ struct weston_compositor *compositor = seat->compositor;
+ struct weston_pointer *pointer = seat->pointer;
+ struct weston_surface *focus =
+ (struct weston_surface *) pointer->focus;
+ uint32_t serial = wl_display_next_serial(compositor->wl_display);
+
+ if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
+ if (compositor->ping_handler && focus)
+ compositor->ping_handler(focus, serial);
+ weston_compositor_idle_inhibit(compositor);
+ if (pointer->button_count == 0) {
+ pointer->grab_button = button;
+ pointer->grab_time = time;
+ pointer->grab_x = pointer->x;
+ pointer->grab_y = pointer->y;
+ }
+ pointer->button_count++;
+ } else {
+ weston_compositor_idle_release(compositor);
+ pointer->button_count--;
+ }
+
+ weston_compositor_run_button_binding(compositor, seat, time, button,
+ state);
+
+ pointer->grab->interface->button(pointer->grab, time, button, state);
+
+ if (pointer->button_count == 1)
+ pointer->grab_serial =
+ wl_display_get_serial(compositor->wl_display);
+}
+
+WL_EXPORT void
+notify_axis(struct weston_seat *seat, uint32_t time, uint32_t axis,
+ wl_fixed_t value)
+{
+ struct weston_compositor *compositor = seat->compositor;
+ struct weston_pointer *pointer = seat->pointer;
+ struct weston_surface *focus =
+ (struct weston_surface *) pointer->focus;
+ uint32_t serial = wl_display_next_serial(compositor->wl_display);
+ struct wl_resource *resource;
+ struct wl_list *resource_list;
+
+ if (compositor->ping_handler && focus)
+ compositor->ping_handler(focus, serial);
+
+ weston_compositor_wake(compositor);
+
+ if (!value)
+ return;
+
+ if (weston_compositor_run_axis_binding(compositor, seat,
+ time, axis, value))
+ return;
+
+ resource_list = &pointer->focus_resource_list;
+ wl_resource_for_each(resource, resource_list)
+ wl_pointer_send_axis(resource, time, axis,
+ value);
+}
+
+#ifdef ENABLE_XKBCOMMON
+WL_EXPORT void
+notify_modifiers(struct weston_seat *seat, uint32_t serial)
+{
+ struct weston_keyboard *keyboard = seat->keyboard;
+ struct weston_keyboard_grab *grab = keyboard->grab;
+ uint32_t mods_depressed, mods_latched, mods_locked, group;
+ uint32_t mods_lookup;
+ enum weston_led leds = 0;
+ int changed = 0;
+
+ /* Serialize and update our internal state, checking to see if it's
+ * different to the previous state. */
+ mods_depressed = xkb_state_serialize_mods(seat->xkb_state.state,
+ XKB_STATE_DEPRESSED);
+ mods_latched = xkb_state_serialize_mods(seat->xkb_state.state,
+ XKB_STATE_LATCHED);
+ mods_locked = xkb_state_serialize_mods(seat->xkb_state.state,
+ XKB_STATE_LOCKED);
+ group = xkb_state_serialize_group(seat->xkb_state.state,
+ XKB_STATE_EFFECTIVE);
+
+ if (mods_depressed != seat->keyboard->modifiers.mods_depressed ||
+ mods_latched != seat->keyboard->modifiers.mods_latched ||
+ mods_locked != seat->keyboard->modifiers.mods_locked ||
+ group != seat->keyboard->modifiers.group)
+ changed = 1;
+
+ seat->keyboard->modifiers.mods_depressed = mods_depressed;
+ seat->keyboard->modifiers.mods_latched = mods_latched;
+ seat->keyboard->modifiers.mods_locked = mods_locked;
+ seat->keyboard->modifiers.group = group;
+
+ /* And update the modifier_state for bindings. */
+ mods_lookup = mods_depressed | mods_latched;
+ seat->modifier_state = 0;
+ if (mods_lookup & (1 << seat->xkb_info->ctrl_mod))
+ seat->modifier_state |= MODIFIER_CTRL;
+ if (mods_lookup & (1 << seat->xkb_info->alt_mod))
+ seat->modifier_state |= MODIFIER_ALT;
+ if (mods_lookup & (1 << seat->xkb_info->super_mod))
+ seat->modifier_state |= MODIFIER_SUPER;
+ if (mods_lookup & (1 << seat->xkb_info->shift_mod))
+ seat->modifier_state |= MODIFIER_SHIFT;
+
+ /* Finally, notify the compositor that LEDs have changed. */
+ if (xkb_state_led_index_is_active(seat->xkb_state.state,
+ seat->xkb_info->num_led))
+ leds |= LED_NUM_LOCK;
+ if (xkb_state_led_index_is_active(seat->xkb_state.state,
+ seat->xkb_info->caps_led))
+ leds |= LED_CAPS_LOCK;
+ if (xkb_state_led_index_is_active(seat->xkb_state.state,
+ seat->xkb_info->scroll_led))
+ leds |= LED_SCROLL_LOCK;
+ if (leds != seat->xkb_state.leds && seat->led_update)
+ seat->led_update(seat, leds);
+ seat->xkb_state.leds = leds;
+
+ if (changed) {
+ grab->interface->modifiers(grab,
+ serial,
+ keyboard->modifiers.mods_depressed,
+ keyboard->modifiers.mods_latched,
+ keyboard->modifiers.mods_locked,
+ keyboard->modifiers.group);
+ }
+}
+
+static void
+update_modifier_state(struct weston_seat *seat, uint32_t serial, uint32_t key,
+ enum wl_keyboard_key_state state)
+{
+ enum xkb_key_direction direction;
+
+ /* Keyboard modifiers don't exist in raw keyboard mode */
+ if (!seat->compositor->use_xkbcommon)
+ return;
+
+ if (state == WL_KEYBOARD_KEY_STATE_PRESSED)
+ direction = XKB_KEY_DOWN;
+ else
+ direction = XKB_KEY_UP;
+
+ /* Offset the keycode by 8, as the evdev XKB rules reflect X's
+ * broken keycode system, which starts at 8. */
+ xkb_state_update_key(seat->xkb_state.state, key + 8, direction);
+
+ notify_modifiers(seat, serial);
+}
+#else
+WL_EXPORT void
+notify_modifiers(struct weston_seat *seat, uint32_t serial)
+{
+}
+
+static void
+update_modifier_state(struct weston_seat *seat, uint32_t serial, uint32_t key,
+ enum wl_keyboard_key_state state)
+{
+}
+#endif
+
+WL_EXPORT void
+notify_key(struct weston_seat *seat, uint32_t time, uint32_t key,
+ enum wl_keyboard_key_state state,
+ enum weston_key_state_update update_state)
+{
+ struct weston_compositor *compositor = seat->compositor;
+ struct weston_keyboard *keyboard = seat->keyboard;
+ struct weston_surface *focus =
+ (struct weston_surface *) keyboard->focus;
+ struct weston_keyboard_grab *grab = keyboard->grab;
+ uint32_t serial = wl_display_next_serial(compositor->wl_display);
+ uint32_t *k, *end;
+
+ if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
+ if (compositor->ping_handler && focus)
+ compositor->ping_handler(focus, serial);
+
+ weston_compositor_idle_inhibit(compositor);
+ keyboard->grab_key = key;
+ keyboard->grab_time = time;
+ } else {
+ weston_compositor_idle_release(compositor);
+ }
+
+ end = keyboard->keys.data + keyboard->keys.size;
+ for (k = keyboard->keys.data; k < end; k++) {
+ if (*k == key) {
+ /* Ignore server-generated repeats. */
+ if (state == WL_KEYBOARD_KEY_STATE_PRESSED)
+ return;
+ *k = *--end;
+ }
+ }
+ keyboard->keys.size = (void *) end - keyboard->keys.data;
+ if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
+ k = wl_array_add(&keyboard->keys, sizeof *k);
+ *k = key;
+ }
+
+ if (grab == &keyboard->default_grab ||
+ grab == &keyboard->input_method_grab) {
+ weston_compositor_run_key_binding(compositor, seat, time, key,
+ state);
+ grab = keyboard->grab;
+ }
+
+ grab->interface->key(grab, time, key, state);
+
+ if (update_state == STATE_UPDATE_AUTOMATIC) {
+ update_modifier_state(seat,
+ wl_display_get_serial(compositor->wl_display),
+ key,
+ state);
+ }
+}
+
+WL_EXPORT void
+notify_pointer_focus(struct weston_seat *seat, struct weston_output *output,
+ wl_fixed_t x, wl_fixed_t y)
+{
+ struct weston_compositor *compositor = seat->compositor;
+
+ if (output) {
+ move_pointer(seat, x, y);
+ compositor->focus = 1;
+ } else {
+ compositor->focus = 0;
+ /* FIXME: We should call weston_pointer_set_focus(seat,
+ * NULL) here, but somehow that breaks re-entry... */
+ }
+}
+
+static void
+destroy_device_saved_kbd_focus(struct wl_listener *listener, void *data)
+{
+ struct weston_seat *ws;
+
+ ws = container_of(listener, struct weston_seat,
+ saved_kbd_focus_listener);
+
+ ws->saved_kbd_focus = NULL;
+}
+
+WL_EXPORT void
+notify_keyboard_focus_in(struct weston_seat *seat, struct wl_array *keys,
+ enum weston_key_state_update update_state)
+{
+ struct weston_compositor *compositor = seat->compositor;
+ struct weston_keyboard *keyboard = seat->keyboard;
+ struct weston_surface *surface;
+ uint32_t *k, serial;
+
+ serial = wl_display_next_serial(compositor->wl_display);
+ wl_array_copy(&keyboard->keys, keys);
+ wl_array_for_each(k, &keyboard->keys) {
+ weston_compositor_idle_inhibit(compositor);
+ if (update_state == STATE_UPDATE_AUTOMATIC)
+ update_modifier_state(seat, serial, *k,
+ WL_KEYBOARD_KEY_STATE_PRESSED);
+ }
+
+ /* Run key bindings after we've updated the state. */
+ wl_array_for_each(k, &keyboard->keys) {
+ weston_compositor_run_key_binding(compositor, seat, 0, *k,
+ WL_KEYBOARD_KEY_STATE_PRESSED);
+ }
+
+ surface = seat->saved_kbd_focus;
+
+ if (surface) {
+ wl_list_remove(&seat->saved_kbd_focus_listener.link);
+ weston_keyboard_set_focus(keyboard, surface);
+ seat->saved_kbd_focus = NULL;
+ }
+}
+
+WL_EXPORT void
+notify_keyboard_focus_out(struct weston_seat *seat)
+{
+ struct weston_compositor *compositor = seat->compositor;
+ struct weston_keyboard *keyboard = seat->keyboard;
+ uint32_t *k, serial;
+
+ serial = wl_display_next_serial(compositor->wl_display);
+ wl_array_for_each(k, &keyboard->keys) {
+ weston_compositor_idle_release(compositor);
+ update_modifier_state(seat, serial, *k,
+ WL_KEYBOARD_KEY_STATE_RELEASED);
+ }
+
+ seat->modifier_state = 0;
+
+ if (keyboard->focus) {
+ seat->saved_kbd_focus = keyboard->focus;
+ seat->saved_kbd_focus_listener.notify =
+ destroy_device_saved_kbd_focus;
+ wl_signal_add(&keyboard->focus->destroy_signal,
+ &seat->saved_kbd_focus_listener);
+ }
+
+ weston_keyboard_set_focus(keyboard, NULL);
+ weston_keyboard_cancel_grab(keyboard);
+}
+
+WL_EXPORT void
+weston_touch_set_focus(struct weston_seat *seat, struct weston_surface *surface)
+{
+ struct wl_list *focus_resource_list;
+
+ focus_resource_list = &seat->touch->focus_resource_list;
+
+ if (seat->touch->focus == surface)
+ return;
+
+ if (!wl_list_empty(focus_resource_list)) {
+ move_resources(&seat->touch->resource_list,
+ focus_resource_list);
+ }
+
+ if (surface) {
+ struct wl_client *surface_client =
+ wl_resource_get_client(surface->resource);
+ move_resources_for_client(focus_resource_list,
+ &seat->touch->resource_list,
+ surface_client);
+ }
+ seat->touch->focus = surface;
+}
+
+/**
+ * notify_touch - emulates button touches and notifies surfaces accordingly.
+ *
+ * It assumes always the correct cycle sequence until it gets here: touch_down
+ * → touch_update → ... → touch_update → touch_end. The driver is responsible
+ * for sending along such order.
+ *
+ */
+WL_EXPORT void
+notify_touch(struct weston_seat *seat, uint32_t time, int touch_id,
+ wl_fixed_t x, wl_fixed_t y, int touch_type)
+{
+ struct weston_compositor *ec = seat->compositor;
+ struct weston_touch *touch = seat->touch;
+ struct weston_touch_grab *grab = touch->grab;
+ struct weston_surface *es;
+ wl_fixed_t sx, sy;
+
+ /* Update grab's global coordinates. */
+ if (touch_id == touch->grab_touch_id && touch_type != WL_TOUCH_UP) {
+ touch->grab_x = x;
+ touch->grab_y = y;
+ }
+
+ switch (touch_type) {
+ case WL_TOUCH_DOWN:
+ weston_compositor_idle_inhibit(ec);
+
+ seat->num_tp++;
+
+ /* the first finger down picks the surface, and all further go
+ * to that surface for the remainder of the touch session i.e.
+ * until all touch points are up again. */
+ if (seat->num_tp == 1) {
+ es = weston_compositor_pick_surface(ec, x, y, &sx, &sy);
+ weston_touch_set_focus(seat, es);
+ } else if (touch->focus) {
+ es = (struct weston_surface *) touch->focus;
+ weston_surface_from_global_fixed(es, x, y, &sx, &sy);
+ } else {
+ /* Unexpected condition: We have non-initial touch but
+ * there is no focused surface.
+ */
+ weston_log("touch event received with %d points down"
+ "but no surface focused\n", seat->num_tp);
+ return;
+ }
+
+ grab->interface->down(grab, time, touch_id, sx, sy);
+ if (seat->num_tp == 1) {
+ touch->grab_serial =
+ wl_display_get_serial(ec->wl_display);
+ touch->grab_touch_id = touch_id;
+ touch->grab_time = time;
+ touch->grab_x = x;
+ touch->grab_y = y;
+ }
+
+ break;
+ case WL_TOUCH_MOTION:
+ es = (struct weston_surface *) touch->focus;
+ if (!es)
+ break;
+
+ weston_surface_from_global_fixed(es, x, y, &sx, &sy);
+ grab->interface->motion(grab, time, touch_id, sx, sy);
+ break;
+ case WL_TOUCH_UP:
+ weston_compositor_idle_release(ec);
+ seat->num_tp--;
+
+ grab->interface->up(grab, time, touch_id);
+ if (seat->num_tp == 0)
+ weston_touch_set_focus(seat, NULL);
+ break;
+ }
+
+ weston_compositor_run_touch_binding(ec, seat, time, touch_type);
+}
+
+static void
+pointer_cursor_surface_configure(struct weston_surface *es,
+ int32_t dx, int32_t dy, int32_t width, int32_t height)
+{
+ struct weston_pointer *pointer = es->configure_private;
+ int x, y;
+
+ if (width == 0)
+ return;
+
+ assert(es == pointer->sprite);
+
+ pointer->hotspot_x -= dx;
+ pointer->hotspot_y -= dy;
+
+ x = wl_fixed_to_int(pointer->x) - pointer->hotspot_x;
+ y = wl_fixed_to_int(pointer->y) - pointer->hotspot_y;
+
+ weston_surface_configure(pointer->sprite, x, y, width, height);
+
+ empty_region(&es->pending.input);
+
+ if (!weston_surface_is_mapped(es)) {
+ wl_list_insert(&es->compositor->cursor_layer.surface_list,
+ &es->layer_link);
+ weston_surface_update_transform(es);
+ }
+}
+
+static void
+pointer_set_cursor(struct wl_client *client, struct wl_resource *resource,
+ uint32_t serial, struct wl_resource *surface_resource,
+ int32_t x, int32_t y)
+{
+ struct weston_pointer *pointer = wl_resource_get_user_data(resource);
+ struct weston_surface *surface = NULL;
+
+ if (surface_resource)
+ surface = wl_resource_get_user_data(surface_resource);
+
+ if (pointer->focus == NULL)
+ return;
+ /* pointer->focus->resource can be NULL. Surfaces like the
+ black_surface used in shell.c for fullscreen don't have
+ a resource, but can still have focus */
+ if (pointer->focus->resource == NULL)
+ return;
+ if (wl_resource_get_client(pointer->focus->resource) != client)
+ return;
+ if (pointer->focus_serial - serial > UINT32_MAX / 2)
+ return;
+
+ if (surface && surface != pointer->sprite) {
+ if (surface->configure) {
+ wl_resource_post_error(surface->resource,
+ WL_DISPLAY_ERROR_INVALID_OBJECT,
+ "surface->configure already "
+ "set");
+ return;
+ }
+ }
+
+ if (pointer->sprite)
+ pointer_unmap_sprite(pointer);
+
+ if (!surface)
+ return;
+
+ wl_signal_add(&surface->destroy_signal,
+ &pointer->sprite_destroy_listener);
+
+ surface->configure = pointer_cursor_surface_configure;
+ surface->configure_private = pointer;
+ pointer->sprite = surface;
+ pointer->hotspot_x = x;
+ pointer->hotspot_y = y;
+
+ if (surface->buffer_ref.buffer)
+ pointer_cursor_surface_configure(surface, 0, 0, weston_surface_buffer_width(surface),
+ weston_surface_buffer_height(surface));
+}
+
+static void
+pointer_release(struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static const struct wl_pointer_interface pointer_interface = {
+ pointer_set_cursor,
+ pointer_release
+};
+
+static void
+seat_get_pointer(struct wl_client *client, struct wl_resource *resource,
+ uint32_t id)
+{
+ struct weston_seat *seat = wl_resource_get_user_data(resource);
+ struct wl_resource *cr;
+
+ if (!seat->pointer)
+ return;
+
+ cr = wl_resource_create(client, &wl_pointer_interface,
+ wl_resource_get_version(resource), id);
+ if (cr == NULL) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+
+ /* May be moved to focused list later by either
+ * weston_pointer_set_focus or directly if this client is already
+ * focused */
+ wl_list_insert(&seat->pointer->resource_list, wl_resource_get_link(cr));
+ wl_resource_set_implementation(cr, &pointer_interface, seat->pointer,
+ unbind_resource);
+
+ if (seat->pointer->focus && seat->pointer->focus->resource &&
+ wl_resource_get_client(seat->pointer->focus->resource) == client) {
+ struct weston_surface *surface;
+ wl_fixed_t sx, sy;
+
+ surface = (struct weston_surface *) seat->pointer->focus;
+ weston_surface_from_global_fixed(surface,
+ seat->pointer->x,
+ seat->pointer->y,
+ &sx,
+ &sy);
+
+ wl_list_remove(wl_resource_get_link(cr));
+ wl_list_insert(&seat->pointer->focus_resource_list,
+ wl_resource_get_link(cr));
+ wl_pointer_send_enter(cr,
+ seat->pointer->focus_serial,
+ surface->resource,
+ sx, sy);
+ }
+}
+
+static void
+keyboard_release(struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static const struct wl_keyboard_interface keyboard_interface = {
+ keyboard_release
+};
+
+static int
+should_send_modifiers_to_client(struct weston_seat *seat,
+ struct wl_client *client)
+{
+ if (seat->keyboard &&
+ seat->keyboard->focus &&
+ wl_resource_get_client(seat->keyboard->focus->resource) == client)
+ return 1;
+
+ if (seat->pointer &&
+ seat->pointer->focus &&
+ wl_resource_get_client(seat->pointer->focus->resource) == client)
+ return 1;
+
+ return 0;
+}
+
+static void
+seat_get_keyboard(struct wl_client *client, struct wl_resource *resource,
+ uint32_t id)
+{
+ struct weston_seat *seat = wl_resource_get_user_data(resource);
+ struct wl_resource *cr;
+
+ if (!seat->keyboard)
+ return;
+
+ cr = wl_resource_create(client, &wl_keyboard_interface,
+ wl_resource_get_version(resource), id);
+ if (cr == NULL) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+
+ /* May be moved to focused list later by either
+ * weston_keyboard_set_focus or directly if this client is already
+ * focused */
+ wl_list_insert(&seat->keyboard->resource_list, wl_resource_get_link(cr));
+ wl_resource_set_implementation(cr, &keyboard_interface,
+ seat, unbind_resource);
+
+ if (seat->compositor->use_xkbcommon) {
+ wl_keyboard_send_keymap(cr, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1,
+ seat->xkb_info->keymap_fd,
+ seat->xkb_info->keymap_size);
+ } else {
+ int null_fd = open("/dev/null", O_RDONLY);
+ wl_keyboard_send_keymap(cr, WL_KEYBOARD_KEYMAP_FORMAT_NO_KEYMAP,
+ null_fd,
+ 0);
+ close(null_fd);
+ }
+
+ if (should_send_modifiers_to_client(seat, client)) {
+ send_modifiers_to_resource(seat->keyboard,
+ cr,
+ seat->keyboard->focus_serial);
+ }
+
+ if (seat->keyboard->focus &&
+ wl_resource_get_client(seat->keyboard->focus->resource) == client) {
+ struct weston_surface *surface =
+ (struct weston_surface *) seat->keyboard->focus;
+
+ wl_list_remove(wl_resource_get_link(cr));
+ wl_list_insert(&seat->keyboard->focus_resource_list,
+ wl_resource_get_link(cr));
+ wl_keyboard_send_enter(cr,
+ seat->keyboard->focus_serial,
+ surface->resource,
+ &seat->keyboard->keys);
+
+ /* If this is the first keyboard resource for this
+ * client... */
+ if (seat->keyboard->focus_resource_list.prev ==
+ wl_resource_get_link(cr))
+ wl_data_device_set_keyboard_focus(seat);
+ }
+}
+
+static void
+touch_release(struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static const struct wl_touch_interface touch_interface = {
+ touch_release
+};
+
+static void
+seat_get_touch(struct wl_client *client, struct wl_resource *resource,
+ uint32_t id)
+{
+ struct weston_seat *seat = wl_resource_get_user_data(resource);
+ struct wl_resource *cr;
+
+ if (!seat->touch)
+ return;
+
+ cr = wl_resource_create(client, &wl_touch_interface,
+ wl_resource_get_version(resource), id);
+ if (cr == NULL) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+
+ if (seat->touch->focus &&
+ wl_resource_get_client(seat->touch->focus->resource) == client) {
+ wl_list_insert(&seat->touch->resource_list,
+ wl_resource_get_link(cr));
+ } else {
+ wl_list_insert(&seat->touch->focus_resource_list,
+ wl_resource_get_link(cr));
+ }
+ wl_resource_set_implementation(cr, &touch_interface,
+ seat, unbind_resource);
+}
+
+static const struct wl_seat_interface seat_interface = {
+ seat_get_pointer,
+ seat_get_keyboard,
+ seat_get_touch,
+};
+
+static void
+bind_seat(struct wl_client *client, void *data, uint32_t version, uint32_t id)
+{
+ struct weston_seat *seat = data;
+ struct wl_resource *resource;
+ enum wl_seat_capability caps = 0;
+
+ resource = wl_resource_create(client,
+ &wl_seat_interface, MIN(version, 3), id);
+ wl_list_insert(&seat->base_resource_list, wl_resource_get_link(resource));
+ wl_resource_set_implementation(resource, &seat_interface, data,
+ unbind_resource);
+
+ if (seat->pointer)
+ caps |= WL_SEAT_CAPABILITY_POINTER;
+ if (seat->keyboard)
+ caps |= WL_SEAT_CAPABILITY_KEYBOARD;
+ if (seat->touch)
+ caps |= WL_SEAT_CAPABILITY_TOUCH;
+
+ wl_seat_send_capabilities(resource, caps);
+ if (version >= 2)
+ wl_seat_send_name(resource, seat->seat_name);
+}
+
+#ifdef ENABLE_XKBCOMMON
+int
+weston_compositor_xkb_init(struct weston_compositor *ec,
+ struct xkb_rule_names *names)
+{
+ ec->use_xkbcommon = 1;
+
+ if (ec->xkb_context == NULL) {
+ ec->xkb_context = xkb_context_new(0);
+ if (ec->xkb_context == NULL) {
+ weston_log("failed to create XKB context\n");
+ return -1;
+ }
+ }
+
+ if (names)
+ ec->xkb_names = *names;
+ if (!ec->xkb_names.rules)
+ ec->xkb_names.rules = strdup("evdev");
+ if (!ec->xkb_names.model)
+ ec->xkb_names.model = strdup("pc105");
+ if (!ec->xkb_names.layout)
+ ec->xkb_names.layout = strdup("us");
+
+ return 0;
+}
+
+static void
+weston_xkb_info_destroy(struct weston_xkb_info *xkb_info)
+{
+ if (--xkb_info->ref_count > 0)
+ return;
+
+ if (xkb_info->keymap)
+ xkb_map_unref(xkb_info->keymap);
+
+ if (xkb_info->keymap_area)
+ munmap(xkb_info->keymap_area, xkb_info->keymap_size);
+ if (xkb_info->keymap_fd >= 0)
+ close(xkb_info->keymap_fd);
+ free(xkb_info);
+}
+
+void
+weston_compositor_xkb_destroy(struct weston_compositor *ec)
+{
+ /*
+ * If we're operating in raw keyboard mode, we never initialized
+ * libxkbcommon so there's no cleanup to do either.
+ */
+ if (!ec->use_xkbcommon)
+ return;
+
+ free((char *) ec->xkb_names.rules);
+ free((char *) ec->xkb_names.model);
+ free((char *) ec->xkb_names.layout);
+ free((char *) ec->xkb_names.variant);
+ free((char *) ec->xkb_names.options);
+
+ if (ec->xkb_info)
+ weston_xkb_info_destroy(ec->xkb_info);
+ xkb_context_unref(ec->xkb_context);
+}
+
+static struct weston_xkb_info *
+weston_xkb_info_create(struct xkb_keymap *keymap)
+{
+ struct weston_xkb_info *xkb_info = zalloc(sizeof *xkb_info);
+ if (xkb_info == NULL)
+ return NULL;
+
+ xkb_info->keymap = xkb_map_ref(keymap);
+ xkb_info->ref_count = 1;
+
+ char *keymap_str;
+
+ xkb_info->shift_mod = xkb_map_mod_get_index(xkb_info->keymap,
+ XKB_MOD_NAME_SHIFT);
+ xkb_info->caps_mod = xkb_map_mod_get_index(xkb_info->keymap,
+ XKB_MOD_NAME_CAPS);
+ xkb_info->ctrl_mod = xkb_map_mod_get_index(xkb_info->keymap,
+ XKB_MOD_NAME_CTRL);
+ xkb_info->alt_mod = xkb_map_mod_get_index(xkb_info->keymap,
+ XKB_MOD_NAME_ALT);
+ xkb_info->mod2_mod = xkb_map_mod_get_index(xkb_info->keymap, "Mod2");
+ xkb_info->mod3_mod = xkb_map_mod_get_index(xkb_info->keymap, "Mod3");
+ xkb_info->super_mod = xkb_map_mod_get_index(xkb_info->keymap,
+ XKB_MOD_NAME_LOGO);
+ xkb_info->mod5_mod = xkb_map_mod_get_index(xkb_info->keymap, "Mod5");
+
+ xkb_info->num_led = xkb_map_led_get_index(xkb_info->keymap,
+ XKB_LED_NAME_NUM);
+ xkb_info->caps_led = xkb_map_led_get_index(xkb_info->keymap,
+ XKB_LED_NAME_CAPS);
+ xkb_info->scroll_led = xkb_map_led_get_index(xkb_info->keymap,
+ XKB_LED_NAME_SCROLL);
+
+ keymap_str = xkb_map_get_as_string(xkb_info->keymap);
+ if (keymap_str == NULL) {
+ weston_log("failed to get string version of keymap\n");
+ goto err_keymap;
+ }
+ xkb_info->keymap_size = strlen(keymap_str) + 1;
+
+ xkb_info->keymap_fd = os_create_anonymous_file(xkb_info->keymap_size);
+ if (xkb_info->keymap_fd < 0) {
+ weston_log("creating a keymap file for %lu bytes failed: %m\n",
+ (unsigned long) xkb_info->keymap_size);
+ goto err_keymap_str;
+ }
+
+ xkb_info->keymap_area = mmap(NULL, xkb_info->keymap_size,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED, xkb_info->keymap_fd, 0);
+ if (xkb_info->keymap_area == MAP_FAILED) {
+ weston_log("failed to mmap() %lu bytes\n",
+ (unsigned long) xkb_info->keymap_size);
+ goto err_dev_zero;
+ }
+ strcpy(xkb_info->keymap_area, keymap_str);
+ free(keymap_str);
+
+ return xkb_info;
+
+err_dev_zero:
+ close(xkb_info->keymap_fd);
+err_keymap_str:
+ free(keymap_str);
+err_keymap:
+ xkb_map_unref(xkb_info->keymap);
+ free(xkb_info);
+ return NULL;
+}
+
+static int
+weston_compositor_build_global_keymap(struct weston_compositor *ec)
+{
+ struct xkb_keymap *keymap;
+
+ if (ec->xkb_info != NULL)
+ return 0;
+
+ keymap = xkb_map_new_from_names(ec->xkb_context,
+ &ec->xkb_names,
+ 0);
+ if (keymap == NULL) {
+ weston_log("failed to compile global XKB keymap\n");
+ weston_log(" tried rules %s, model %s, layout %s, variant %s, "
+ "options %s\n",
+ ec->xkb_names.rules, ec->xkb_names.model,
+ ec->xkb_names.layout, ec->xkb_names.variant,
+ ec->xkb_names.options);
+ return -1;
+ }
+
+ ec->xkb_info = weston_xkb_info_create(keymap);
+ if (ec->xkb_info == NULL)
+ return -1;
+
+ return 0;
+}
+#else
+int
+weston_compositor_xkb_init(struct weston_compositor *ec,
+ struct xkb_rule_names *names)
+{
+ return 0;
+}
+
+void
+weston_compositor_xkb_destroy(struct weston_compositor *ec)
+{
+}
+#endif
+
+WL_EXPORT int
+weston_seat_init_keyboard(struct weston_seat *seat, struct xkb_keymap *keymap)
+{
+ struct weston_keyboard *keyboard;
+
+ if (seat->keyboard) {
+ seat->keyboard_device_count += 1;
+ if (seat->keyboard_device_count == 1)
+ seat_send_updated_caps(seat);
+ return 0;
+ }
+
+#ifdef ENABLE_XKBCOMMON
+ if (seat->compositor->use_xkbcommon) {
+ if (keymap != NULL) {
+ seat->xkb_info = weston_xkb_info_create(keymap);
+ if (seat->xkb_info == NULL)
+ return -1;
+ } else {
+ if (weston_compositor_build_global_keymap(seat->compositor) < 0)
+ return -1;
+ seat->xkb_info = seat->compositor->xkb_info;
+ seat->xkb_info->ref_count++;
+ }
+
+ seat->xkb_state.state = xkb_state_new(seat->xkb_info->keymap);
+ if (seat->xkb_state.state == NULL) {
+ weston_log("failed to initialise XKB state\n");
+ return -1;
+ }
+
+ seat->xkb_state.leds = 0;
+ }
+#endif
+
+ keyboard = weston_keyboard_create();
+ if (keyboard == NULL) {
+ weston_log("failed to allocate weston keyboard struct\n");
+ return -1;
+ }
+
+ seat->keyboard = keyboard;
+ seat->keyboard_device_count = 1;
+ keyboard->seat = seat;
+
+ seat_send_updated_caps(seat);
+
+ return 0;
+}
+
+WL_EXPORT void
+weston_seat_release_keyboard(struct weston_seat *seat)
+{
+ seat->keyboard_device_count--;
+ if (seat->keyboard_device_count == 0) {
+ weston_keyboard_set_focus(seat->keyboard, NULL);
+ weston_keyboard_cancel_grab(seat->keyboard);
+ seat_send_updated_caps(seat);
+ }
+}
+
+WL_EXPORT void
+weston_seat_init_pointer(struct weston_seat *seat)
+{
+ struct weston_pointer *pointer;
+
+ if (seat->pointer) {
+ seat->pointer_device_count += 1;
+ if (seat->pointer_device_count == 1)
+ seat_send_updated_caps(seat);
+ return;
+ }
+
+ pointer = weston_pointer_create();
+ if (pointer == NULL)
+ return;
+
+ seat->pointer = pointer;
+ seat->pointer_device_count = 1;
+ pointer->seat = seat;
+
+ seat_send_updated_caps(seat);
+}
+
+WL_EXPORT void
+weston_seat_release_pointer(struct weston_seat *seat)
+{
+ struct weston_pointer *pointer = seat->pointer;
+
+ seat->pointer_device_count--;
+ if (seat->pointer_device_count == 0) {
+ weston_pointer_set_focus(pointer, NULL,
+ wl_fixed_from_int(0),
+ wl_fixed_from_int(0));
+ weston_pointer_cancel_grab(pointer);
+
+ if (pointer->sprite)
+ pointer_unmap_sprite(pointer);
+
+ seat_send_updated_caps(seat);
+ }
+}
+
+WL_EXPORT void
+weston_seat_init_touch(struct weston_seat *seat)
+{
+ struct weston_touch *touch;
+
+ if (seat->touch) {
+ seat->touch_device_count += 1;
+ if (seat->touch_device_count == 1)
+ seat_send_updated_caps(seat);
+ return;
+ }
+
+ touch = weston_touch_create();
+ if (touch == NULL)
+ return;
+
+ seat->touch = touch;
+ seat->touch_device_count = 1;
+ touch->seat = seat;
+
+ seat_send_updated_caps(seat);
+}
+
+WL_EXPORT void
+weston_seat_release_touch(struct weston_seat *seat)
+{
+ seat->touch_device_count--;
+ if (seat->touch_device_count == 0) {
+ weston_touch_set_focus(seat, NULL);
+ weston_touch_cancel_grab(seat->touch);
+ seat_send_updated_caps(seat);
+ }
+}
+
+WL_EXPORT void
+weston_seat_init(struct weston_seat *seat, struct weston_compositor *ec,
+ const char *seat_name)
+{
+ memset(seat, 0, sizeof *seat);
+
+ seat->selection_data_source = NULL;
+ wl_list_init(&seat->base_resource_list);
+ wl_signal_init(&seat->selection_signal);
+ wl_list_init(&seat->drag_resource_list);
+ wl_signal_init(&seat->destroy_signal);
+
+ seat->global = wl_global_create(ec->wl_display, &wl_seat_interface, 3,
+ seat, bind_seat);
+
+ seat->compositor = ec;
+ seat->modifier_state = 0;
+ seat->num_tp = 0;
+ seat->seat_name = strdup(seat_name);
+
+ wl_list_insert(ec->seat_list.prev, &seat->link);
+
+ clipboard_create(seat);
+
+ wl_signal_emit(&ec->seat_created_signal, seat);
+}
+
+WL_EXPORT void
+weston_seat_release(struct weston_seat *seat)
+{
+ wl_list_remove(&seat->link);
+
+#ifdef ENABLE_XKBCOMMON
+ if (seat->compositor->use_xkbcommon) {
+ if (seat->xkb_state.state != NULL)
+ xkb_state_unref(seat->xkb_state.state);
+ if (seat->xkb_info)
+ weston_xkb_info_destroy(seat->xkb_info);
+ }
+#endif
+
+ if (seat->pointer)
+ weston_pointer_destroy(seat->pointer);
+ if (seat->keyboard)
+ weston_keyboard_destroy(seat->keyboard);
+ if (seat->touch)
+ weston_touch_destroy(seat->touch);
+
+ free (seat->seat_name);
+
+ wl_global_destroy(seat->global);
+
+ wl_signal_emit(&seat->destroy_signal, seat);
+}
diff --git a/src/launcher-util.c b/src/launcher-util.c
new file mode 100644
index 00000000..4ce06c4f
--- /dev/null
+++ b/src/launcher-util.c
@@ -0,0 +1,397 @@
+/*
+ * Copyright © 2012 Benjamin Franzke
+ * Copyright © 2013 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 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.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <errno.h>
+#include <signal.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <linux/vt.h>
+#include <linux/kd.h>
+#include <linux/major.h>
+
+#ifdef BUILD_DRM_COMPOSITOR
+#include <xf86drm.h>
+#endif
+
+#include "compositor.h"
+#include "launcher-util.h"
+#include "weston-launch.h"
+
+#define DRM_MAJOR 226
+
+#ifndef KDSKBMUTE
+#define KDSKBMUTE 0x4B51
+#endif
+
+union cmsg_data { unsigned char b[4]; int fd; };
+
+struct weston_launcher {
+ struct weston_compositor *compositor;
+ int fd;
+ struct wl_event_source *source;
+
+ int kb_mode, tty, drm_fd;
+ struct wl_event_source *vt_source;
+};
+
+#ifdef BUILD_DRM_COMPOSITOR
+static int
+drm_drop_master(int drm_fd)
+{
+ return drmDropMaster(drm_fd);
+}
+static int
+drm_set_master(int drm_fd)
+{
+ return drmSetMaster(drm_fd);
+}
+static int
+drm_is_master(int drm_fd)
+{
+ drm_magic_t magic;
+
+ return drmGetMagic(drm_fd, &magic) == 0 &&
+ drmAuthMagic(drm_fd, magic) == 0;
+}
+#else
+static int drm_drop_master(int drm_fd) {return 0;}
+static int drm_set_master(int drm_fd) {return 0;}
+static int drm_is_master(int drm_fd) {return 1;}
+#endif
+
+int
+weston_launcher_open(struct weston_launcher *launcher,
+ const char *path, int flags)
+{
+ int n, fd, ret = -1;
+ struct msghdr msg;
+ struct cmsghdr *cmsg;
+ struct iovec iov;
+ union cmsg_data *data;
+ char control[CMSG_SPACE(sizeof data->fd)];
+ ssize_t len;
+ struct weston_launcher_open *message;
+ struct stat s;
+
+ if (launcher->fd == -1) {
+ fd = open(path, flags | O_CLOEXEC);
+ if (fd == -1)
+ return -1;
+
+ if (fstat(fd, &s) == -1) {
+ close(fd);
+ return -1;
+ }
+
+ if (major(s.st_rdev) == DRM_MAJOR) {
+ launcher->drm_fd = fd;
+ if (!drm_is_master(fd)) {
+ weston_log("drm fd not master\n");
+ close(fd);
+ return -1;
+ }
+ }
+
+ return fd;
+ }
+
+ n = sizeof(*message) + strlen(path) + 1;
+ message = malloc(n);
+ if (!message)
+ return -1;
+
+ message->header.opcode = WESTON_LAUNCHER_OPEN;
+ message->flags = flags;
+ strcpy(message->path, path);
+
+ do {
+ len = send(launcher->fd, message, n, 0);
+ } while (len < 0 && errno == EINTR);
+ free(message);
+
+ memset(&msg, 0, sizeof msg);
+ iov.iov_base = &ret;
+ iov.iov_len = sizeof ret;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = control;
+ msg.msg_controllen = sizeof control;
+
+ do {
+ len = recvmsg(launcher->fd, &msg, MSG_CMSG_CLOEXEC);
+ } while (len < 0 && errno == EINTR);
+
+ if (len != sizeof ret ||
+ ret < 0)
+ return -1;
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+ if (!cmsg ||
+ cmsg->cmsg_level != SOL_SOCKET ||
+ cmsg->cmsg_type != SCM_RIGHTS) {
+ fprintf(stderr, "invalid control message\n");
+ return -1;
+ }
+
+ data = (union cmsg_data *) CMSG_DATA(cmsg);
+ if (data->fd == -1) {
+ fprintf(stderr, "missing drm fd in socket request\n");
+ return -1;
+ }
+
+ return data->fd;
+}
+
+void
+weston_launcher_restore(struct weston_launcher *launcher)
+{
+ struct vt_mode mode = { 0 };
+
+ if (ioctl(launcher->tty, KDSKBMUTE, 0) &&
+ ioctl(launcher->tty, KDSKBMODE, launcher->kb_mode))
+ weston_log("failed to restore kb mode: %m\n");
+
+ if (ioctl(launcher->tty, KDSETMODE, KD_TEXT))
+ weston_log("failed to set KD_TEXT mode on tty: %m\n");
+
+ /* We have to drop master before we switch the VT back in
+ * VT_AUTO, so we don't risk switching to a VT with another
+ * display server, that will then fail to set drm master. */
+ drm_drop_master(launcher->drm_fd);
+
+ mode.mode = VT_AUTO;
+ if (ioctl(launcher->tty, VT_SETMODE, &mode) < 0)
+ weston_log("could not reset vt handling\n");
+}
+
+static int
+weston_launcher_data(int fd, uint32_t mask, void *data)
+{
+ struct weston_launcher *launcher = data;
+ int len, ret;
+
+ if (mask & (WL_EVENT_HANGUP | WL_EVENT_ERROR)) {
+ weston_log("launcher socket closed, exiting\n");
+ /* Normally the weston-launch will reset the tty, but
+ * in this case it died or something, so do it here so
+ * we don't end up with a stuck vt. */
+ weston_launcher_restore(launcher);
+ exit(-1);
+ }
+
+ do {
+ len = recv(launcher->fd, &ret, sizeof ret, 0);
+ } while (len < 0 && errno == EINTR);
+
+ switch (ret) {
+ case WESTON_LAUNCHER_ACTIVATE:
+ launcher->compositor->session_active = 1;
+ wl_signal_emit(&launcher->compositor->session_signal,
+ launcher->compositor);
+ break;
+ case WESTON_LAUNCHER_DEACTIVATE:
+ launcher->compositor->session_active = 0;
+ wl_signal_emit(&launcher->compositor->session_signal,
+ launcher->compositor);
+ break;
+ default:
+ weston_log("unexpected event from weston-launch\n");
+ break;
+ }
+
+ return 1;
+}
+
+static int
+vt_handler(int signal_number, void *data)
+{
+ struct weston_launcher *launcher = data;
+ struct weston_compositor *compositor = launcher->compositor;
+
+ if (compositor->session_active) {
+ compositor->session_active = 0;
+ wl_signal_emit(&compositor->session_signal, compositor);
+ drm_drop_master(launcher->drm_fd);
+ ioctl(launcher->tty, VT_RELDISP, 1);
+ } else {
+ ioctl(launcher->tty, VT_RELDISP, VT_ACKACQ);
+ drm_set_master(launcher->drm_fd);
+ compositor->session_active = 1;
+ wl_signal_emit(&compositor->session_signal, compositor);
+ }
+
+ return 1;
+}
+
+static int
+setup_tty(struct weston_launcher *launcher, int tty)
+{
+ struct wl_event_loop *loop;
+ struct vt_mode mode = { 0 };
+ struct stat buf;
+ char tty_device[32] ="<stdin>";
+ int ret, kd_mode;
+
+ if (tty == 0) {
+ launcher->tty = dup(tty);
+ if (launcher->tty == -1) {
+ weston_log("couldn't dup stdin: %m\n");
+ return -1;
+ }
+ } else {
+ snprintf(tty_device, sizeof tty_device, "/dev/tty%d", tty);
+ launcher->tty = open(tty_device, O_RDWR | O_CLOEXEC);
+ if (launcher->tty == -1) {
+ weston_log("couldn't open tty %s: %m\n", tty_device);
+ return -1;
+ }
+ }
+
+ if (fstat(launcher->tty, &buf) == -1 ||
+ major(buf.st_rdev) != TTY_MAJOR || minor(buf.st_rdev) == 0) {
+ weston_log("%s not a vt\n", tty_device);
+ weston_log("if running weston from ssh, "
+ "use --tty to specify a tty\n");
+ goto err_close;
+ }
+
+ ret = ioctl(launcher->tty, KDGETMODE, &kd_mode);
+ if (ret) {
+ weston_log("failed to get VT mode: %m\n");
+ return -1;
+ }
+ if (kd_mode != KD_TEXT) {
+ weston_log("%s is already in graphics mode, "
+ "is another display server running?\n", tty_device);
+ goto err_close;
+ }
+
+ ioctl(launcher->tty, VT_ACTIVATE, minor(buf.st_rdev));
+ ioctl(launcher->tty, VT_WAITACTIVE, minor(buf.st_rdev));
+
+ if (ioctl(launcher->tty, KDGKBMODE, &launcher->kb_mode)) {
+ weston_log("failed to read keyboard mode: %m\n");
+ goto err_close;
+ }
+
+ if (ioctl(launcher->tty, KDSKBMUTE, 1) &&
+ ioctl(launcher->tty, KDSKBMODE, K_OFF)) {
+ weston_log("failed to set K_OFF keyboard mode: %m\n");
+ goto err_close;
+ }
+
+ ret = ioctl(launcher->tty, KDSETMODE, KD_GRAPHICS);
+ if (ret) {
+ weston_log("failed to set KD_GRAPHICS mode on tty: %m\n");
+ goto err_close;
+ }
+
+ mode.mode = VT_PROCESS;
+ mode.relsig = SIGUSR1;
+ mode.acqsig = SIGUSR1;
+ if (ioctl(launcher->tty, VT_SETMODE, &mode) < 0) {
+ weston_log("failed to take control of vt handling\n");
+ goto err_close;
+ }
+
+ loop = wl_display_get_event_loop(launcher->compositor->wl_display);
+ launcher->vt_source =
+ wl_event_loop_add_signal(loop, SIGUSR1, vt_handler, launcher);
+ if (!launcher->vt_source)
+ goto err_close;
+
+ return 0;
+
+ err_close:
+ close(launcher->tty);
+ return -1;
+}
+
+int
+weston_launcher_activate_vt(struct weston_launcher *launcher, int vt)
+{
+ return ioctl(launcher->tty, VT_ACTIVATE, vt);
+}
+
+struct weston_launcher *
+weston_launcher_connect(struct weston_compositor *compositor, int tty)
+{
+ struct weston_launcher *launcher;
+ struct wl_event_loop *loop;
+
+ launcher = malloc(sizeof *launcher);
+ if (launcher == NULL)
+ return NULL;
+
+ launcher->compositor = compositor;
+ launcher->drm_fd = -1;
+ launcher->fd = weston_environment_get_fd("WESTON_LAUNCHER_SOCK");
+ if (launcher->fd != -1) {
+ launcher->tty = weston_environment_get_fd("WESTON_TTY_FD");
+ loop = wl_display_get_event_loop(compositor->wl_display);
+ launcher->source = wl_event_loop_add_fd(loop, launcher->fd,
+ WL_EVENT_READABLE,
+ weston_launcher_data,
+ launcher);
+ if (launcher->source == NULL) {
+ free(launcher);
+ return NULL;
+ }
+ } else if (geteuid() == 0) {
+ if (setup_tty(launcher, tty) == -1) {
+ free(launcher);
+ return NULL;
+ }
+ } else {
+ free(launcher);
+ return NULL;
+ }
+
+ return launcher;
+}
+
+void
+weston_launcher_destroy(struct weston_launcher *launcher)
+{
+ if (launcher->fd != -1) {
+ close(launcher->fd);
+ wl_event_source_remove(launcher->source);
+ } else {
+ weston_launcher_restore(launcher);
+ wl_event_source_remove(launcher->vt_source);
+ }
+
+ close(launcher->tty);
+ free(launcher);
+}
diff --git a/src/launcher-util.h b/src/launcher-util.h
new file mode 100644
index 00000000..3e7ceb59
--- /dev/null
+++ b/src/launcher-util.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright © 2012 Benjamin Franzke
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that 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.
+ */
+
+#ifndef _WESTON_LAUNCHER_UTIL_H_
+#define _WESTON_LAUNCHER_UTIL_H_
+
+#include "config.h"
+
+#include "compositor.h"
+
+struct weston_launcher;
+
+struct weston_launcher *
+weston_launcher_connect(struct weston_compositor *compositor, int tty);
+
+void
+weston_launcher_destroy(struct weston_launcher *launcher);
+
+int
+weston_launcher_open(struct weston_launcher *launcher,
+ const char *path, int flags);
+
+int
+weston_launcher_activate_vt(struct weston_launcher *launcher, int vt);
+
+void
+weston_launcher_restore(struct weston_launcher *launcher);
+
+#endif
diff --git a/src/libbacklight.c b/src/libbacklight.c
new file mode 100644
index 00000000..b3acc63f
--- /dev/null
+++ b/src/libbacklight.c
@@ -0,0 +1,309 @@
+/*
+ * libbacklight - userspace interface to Linux backlight control
+ *
+ * Copyright © 2012 Intel Corporation
+ * Copyright 2010 Red Hat <mjg@redhat.com>
+ *
+ * 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.
+ *
+ * Authors:
+ * Matthew Garrett <mjg@redhat.com>
+ * Tiago Vignatti <vignatti@freedesktop.org>
+ */
+
+#include "config.h"
+
+#include "libbacklight.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <linux/types.h>
+#include <dirent.h>
+#include <drm.h>
+#include <fcntl.h>
+#include <malloc.h>
+#include <string.h>
+#include <errno.h>
+
+static long backlight_get(struct backlight *backlight, char *node)
+{
+ char buffer[100];
+ char *path;
+ int fd;
+ long value, ret;
+
+ if (asprintf(&path, "%s/%s", backlight->path, node) < 0)
+ return -ENOMEM
+;
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ ret = -1;
+ goto out;
+ }
+
+ ret = read(fd, &buffer, sizeof(buffer));
+ if (ret < 1) {
+ ret = -1;
+ goto out;
+ }
+
+ value = strtol(buffer, NULL, 10);
+ ret = value;
+out:
+ if (fd >= 0)
+ close(fd);
+ free(path);
+ return ret;
+}
+
+long backlight_get_brightness(struct backlight *backlight)
+{
+ return backlight_get(backlight, "brightness");
+}
+
+long backlight_get_max_brightness(struct backlight *backlight)
+{
+ return backlight_get(backlight, "max_brightness");
+}
+
+long backlight_get_actual_brightness(struct backlight *backlight)
+{
+ return backlight_get(backlight, "actual_brightness");
+}
+
+long backlight_set_brightness(struct backlight *backlight, long brightness)
+{
+ char *path;
+ char *buffer = NULL;
+ int fd;
+ long ret;
+
+ if (asprintf(&path, "%s/%s", backlight->path, "brightness") < 0)
+ return -ENOMEM;
+
+ fd = open(path, O_RDWR);
+ if (fd < 0) {
+ ret = -1;
+ goto out;
+ }
+
+ ret = read(fd, &buffer, sizeof(buffer));
+ if (ret < 1) {
+ ret = -1;
+ goto out;
+ }
+
+ if (asprintf(&buffer, "%ld", brightness) < 0) {
+ ret = -1;
+ goto out;
+ }
+
+ ret = write(fd, buffer, strlen(buffer));
+ if (ret < 0) {
+ ret = -1;
+ goto out;
+ }
+
+ ret = backlight_get_brightness(backlight);
+ backlight->brightness = ret;
+out:
+ free(buffer);
+ free(path);
+ if (fd >= 0)
+ close(fd);
+ return ret;
+}
+
+void backlight_destroy(struct backlight *backlight)
+{
+ if (!backlight)
+ return;
+
+ if (backlight->path)
+ free(backlight->path);
+
+ free(backlight);
+}
+
+struct backlight *backlight_init(struct udev_device *drm_device,
+ uint32_t connector_type)
+{
+ const char *syspath = NULL;
+ char *pci_name = NULL;
+ char *chosen_path = NULL;
+ char *path = NULL;
+ DIR *backlights = NULL;
+ struct dirent *entry;
+ enum backlight_type type = 0;
+ char buffer[100];
+ struct backlight *backlight = NULL;
+ int ret;
+
+ if (!drm_device)
+ return NULL;
+
+ syspath = udev_device_get_syspath(drm_device);
+ if (!syspath)
+ return NULL;
+
+ if (asprintf(&path, "%s/%s", syspath, "device") < 0)
+ return NULL;
+
+ ret = readlink(path, buffer, sizeof(buffer) - 1);
+ free(path);
+ if (ret < 0)
+ return NULL;
+
+ buffer[ret] = '\0';
+ pci_name = basename(buffer);
+
+ if (connector_type <= 0)
+ return NULL;
+
+ backlights = opendir("/sys/class/backlight");
+ if (!backlights)
+ return NULL;
+
+ /* Find the "best" backlight for the device. Firmware
+ interfaces are preferred over platform interfaces are
+ preferred over raw interfaces. For raw interfaces we'll
+ check if the device ID in the form of pci match, while
+ for firmware interfaces we require the pci ID to
+ match. It's assumed that platform interfaces always match,
+ since we can't actually associate them with IDs.
+
+ A further awkwardness is that, while it's theoretically
+ possible for an ACPI interface to include support for
+ changing the backlight of external devices, it's unlikely
+ to ever be done. It's effectively impossible for a platform
+ interface to do so. So if we get asked about anything that
+ isn't LVDS or eDP, we pretty much have to require that the
+ control be supplied via a raw interface */
+
+ while ((entry = readdir(backlights))) {
+ char *backlight_path;
+ char *parent;
+ enum backlight_type entry_type;
+ int fd;
+
+ if (entry->d_name[0] == '.')
+ continue;
+
+ if (asprintf(&backlight_path, "%s/%s", "/sys/class/backlight",
+ entry->d_name) < 0)
+ goto err;
+
+ if (asprintf(&path, "%s/%s", backlight_path, "type") < 0)
+ goto err;
+
+ fd = open(path, O_RDONLY);
+
+ if (fd < 0)
+ goto out;
+
+ ret = read (fd, &buffer, sizeof(buffer));
+ close (fd);
+
+ if (ret < 1)
+ goto out;
+
+ buffer[ret] = '\0';
+
+ if (!strncmp(buffer, "raw\n", sizeof(buffer)))
+ entry_type = BACKLIGHT_RAW;
+ else if (!strncmp(buffer, "platform\n", sizeof(buffer)))
+ entry_type = BACKLIGHT_PLATFORM;
+ else if (!strncmp(buffer, "firmware\n", sizeof(buffer)))
+ entry_type = BACKLIGHT_FIRMWARE;
+ else
+ goto out;
+
+ if (connector_type != DRM_MODE_CONNECTOR_LVDS &&
+ connector_type != DRM_MODE_CONNECTOR_eDP) {
+ /* External displays are assumed to require
+ gpu control at the moment */
+ if (entry_type != BACKLIGHT_RAW)
+ goto out;
+ }
+
+ free (path);
+
+ if (asprintf(&path, "%s/%s", backlight_path, "device") < 0)
+ goto err;
+
+ ret = readlink(path, buffer, sizeof(buffer) - 1);
+
+ if (ret < 0)
+ goto out;
+
+ buffer[ret] = '\0';
+
+ parent = basename(buffer);
+
+ /* Perform matching for raw and firmware backlights -
+ platform backlights have to be assumed to match */
+ if (entry_type == BACKLIGHT_RAW ||
+ entry_type == BACKLIGHT_FIRMWARE) {
+ if (!(pci_name && !strcmp(pci_name, parent)))
+ goto out;
+ }
+
+ if (entry_type < type)
+ goto out;
+
+ type = entry_type;
+
+ if (chosen_path)
+ free(chosen_path);
+ chosen_path = strdup(backlight_path);
+
+ out:
+ free(backlight_path);
+ free(path);
+ }
+
+ if (!chosen_path)
+ goto err;
+
+ backlight = malloc(sizeof(struct backlight));
+
+ if (!backlight)
+ goto err;
+
+ backlight->path = chosen_path;
+ backlight->type = type;
+
+ backlight->max_brightness = backlight_get_max_brightness(backlight);
+ if (backlight->max_brightness < 0)
+ goto err;
+
+ backlight->brightness = backlight_get_actual_brightness(backlight);
+ if (backlight->brightness < 0)
+ goto err;
+
+ closedir(backlights);
+ return backlight;
+err:
+ closedir(backlights);
+ free (chosen_path);
+ free (backlight);
+ return NULL;
+}
diff --git a/src/libbacklight.h b/src/libbacklight.h
new file mode 100644
index 00000000..0c326711
--- /dev/null
+++ b/src/libbacklight.h
@@ -0,0 +1,49 @@
+#ifndef LIBBACKLIGHT_H
+#define LIBBACKLIGHT_H
+#include <libudev.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum backlight_type {
+ BACKLIGHT_RAW,
+ BACKLIGHT_PLATFORM,
+ BACKLIGHT_FIRMWARE
+};
+
+struct backlight {
+ char *path;
+ int max_brightness;
+ int brightness;
+ enum backlight_type type;
+};
+
+/*
+ * Find and set up a backlight for a valid udev connector device, i.e. one
+ * matching drm subsytem and with status of connected.
+ */
+struct backlight *backlight_init(struct udev_device *drm_device,
+ uint32_t connector_type);
+
+/* Free backlight resources */
+void backlight_destroy(struct backlight *backlight);
+
+/* Provide the maximum backlight value */
+long backlight_get_max_brightness(struct backlight *backlight);
+
+/* Provide the cached backlight value */
+long backlight_get_brightness(struct backlight *backlight);
+
+/* Provide the hardware backlight value */
+long backlight_get_actual_brightness(struct backlight *backlight);
+
+/* Set the backlight to a value between 0 and max */
+long backlight_set_brightness(struct backlight *backlight, long brightness);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBBACKLIGHT_H */
diff --git a/src/log.c b/src/log.c
new file mode 100644
index 00000000..911b0c63
--- /dev/null
+++ b/src/log.c
@@ -0,0 +1,132 @@
+/*
+ * Copyright © 2012 Martin Minarik
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that 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.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include <wayland-util.h>
+
+#include "compositor.h"
+
+static FILE *weston_logfile = NULL;
+
+static int cached_tm_mday = -1;
+
+static int weston_log_timestamp(void)
+{
+ struct timeval tv;
+ struct tm *brokendown_time;
+ char string[128];
+
+ gettimeofday(&tv, NULL);
+
+ brokendown_time = localtime(&tv.tv_sec);
+ if (brokendown_time->tm_mday != cached_tm_mday) {
+ strftime(string, sizeof string, "%Y-%m-%d %Z", brokendown_time);
+ fprintf(weston_logfile, "Date: %s\n", string);
+
+ cached_tm_mday = brokendown_time->tm_mday;
+ }
+
+ strftime(string, sizeof string, "%H:%M:%S", brokendown_time);
+
+ return fprintf(weston_logfile, "[%s.%03li] ", string, tv.tv_usec/1000);
+}
+
+static void
+custom_handler(const char *fmt, va_list arg)
+{
+ weston_log_timestamp();
+ fprintf(weston_logfile, "libwayland: ");
+ vfprintf(weston_logfile, fmt, arg);
+}
+
+void
+weston_log_file_open(const char *filename)
+{
+ wl_log_set_handler_server(custom_handler);
+
+ if (filename != NULL)
+ weston_logfile = fopen(filename, "a");
+
+ if (weston_logfile == NULL)
+ weston_logfile = stderr;
+ else
+ setvbuf(weston_logfile, NULL, _IOLBF, 256);
+}
+
+void
+weston_log_file_close()
+{
+ if ((weston_logfile != stderr) && (weston_logfile != NULL))
+ fclose(weston_logfile);
+ weston_logfile = stderr;
+}
+
+WL_EXPORT int
+weston_vlog(const char *fmt, va_list ap)
+{
+ int l;
+
+ l = weston_log_timestamp();
+ l += vfprintf(weston_logfile, fmt, ap);
+
+ return l;
+}
+
+WL_EXPORT int
+weston_log(const char *fmt, ...)
+{
+ int l;
+ va_list argp;
+
+ va_start(argp, fmt);
+ l = weston_vlog(fmt, argp);
+ va_end(argp);
+
+ return l;
+}
+
+WL_EXPORT int
+weston_vlog_continue(const char *fmt, va_list argp)
+{
+ return vfprintf(weston_logfile, fmt, argp);
+}
+
+WL_EXPORT int
+weston_log_continue(const char *fmt, ...)
+{
+ int l;
+ va_list argp;
+
+ va_start(argp, fmt);
+ l = weston_vlog_continue(fmt, argp);
+ va_end(argp);
+
+ return l;
+}
diff --git a/src/noop-renderer.c b/src/noop-renderer.c
new file mode 100644
index 00000000..91659f58
--- /dev/null
+++ b/src/noop-renderer.c
@@ -0,0 +1,98 @@
+/*
+ * Copyright © 2012 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 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.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include "compositor.h"
+
+static int
+noop_renderer_read_pixels(struct weston_output *output,
+ pixman_format_code_t format, void *pixels,
+ uint32_t x, uint32_t y,
+ uint32_t width, uint32_t height)
+{
+ return 0;
+}
+
+static void
+noop_renderer_repaint_output(struct weston_output *output,
+ pixman_region32_t *output_damage)
+{
+}
+
+static void
+noop_renderer_flush_damage(struct weston_surface *surface)
+{
+}
+
+static void
+noop_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer)
+{
+}
+
+static int
+noop_renderer_create_surface(struct weston_surface *surface)
+{
+ return 0;
+}
+
+static void
+noop_renderer_surface_set_color(struct weston_surface *surface,
+ float red, float green, float blue, float alpha)
+{
+}
+
+static void
+noop_renderer_destroy_surface(struct weston_surface *surface)
+{
+}
+
+static void
+noop_renderer_destroy(struct weston_compositor *ec)
+{
+ free(ec->renderer);
+ ec->renderer = NULL;
+}
+
+WL_EXPORT int
+noop_renderer_init(struct weston_compositor *ec)
+{
+ struct weston_renderer *renderer;
+
+ renderer = malloc(sizeof *renderer);
+ if (renderer == NULL)
+ return -1;
+
+ renderer->read_pixels = noop_renderer_read_pixels;
+ renderer->repaint_output = noop_renderer_repaint_output;
+ renderer->flush_damage = noop_renderer_flush_damage;
+ renderer->attach = noop_renderer_attach;
+ renderer->create_surface = noop_renderer_create_surface;
+ renderer->surface_set_color = noop_renderer_surface_set_color;
+ renderer->destroy_surface = noop_renderer_destroy_surface;
+ renderer->destroy = noop_renderer_destroy;
+ ec->renderer = renderer;
+
+ return 0;
+}
diff --git a/src/pixman-renderer.c b/src/pixman-renderer.c
new file mode 100644
index 00000000..987c5393
--- /dev/null
+++ b/src/pixman-renderer.c
@@ -0,0 +1,753 @@
+/*
+ * Copyright © 2012 Intel Corporation
+ * Copyright © 2013 Vasily Khoruzhick <anarsoul@gmail.com>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that 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.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdlib.h>
+
+#include "pixman-renderer.h"
+
+#include <linux/input.h>
+
+struct pixman_output_state {
+ void *shadow_buffer;
+ pixman_image_t *shadow_image;
+ pixman_image_t *hw_buffer;
+};
+
+struct pixman_surface_state {
+ pixman_image_t *image;
+ struct weston_buffer_reference buffer_ref;
+};
+
+struct pixman_renderer {
+ struct weston_renderer base;
+ int repaint_debug;
+ pixman_image_t *debug_color;
+};
+
+static inline struct pixman_output_state *
+get_output_state(struct weston_output *output)
+{
+ return (struct pixman_output_state *)output->renderer_state;
+}
+
+static inline struct pixman_surface_state *
+get_surface_state(struct weston_surface *surface)
+{
+ return (struct pixman_surface_state *)surface->renderer_state;
+}
+
+static inline struct pixman_renderer *
+get_renderer(struct weston_compositor *ec)
+{
+ return (struct pixman_renderer *)ec->renderer;
+}
+
+static int
+pixman_renderer_read_pixels(struct weston_output *output,
+ pixman_format_code_t format, void *pixels,
+ uint32_t x, uint32_t y,
+ uint32_t width, uint32_t height)
+{
+ struct pixman_output_state *po = get_output_state(output);
+ pixman_transform_t transform;
+ pixman_image_t *out_buf;
+
+ if (!po->hw_buffer) {
+ errno = ENODEV;
+ return -1;
+ }
+
+ out_buf = pixman_image_create_bits(format,
+ width,
+ height,
+ pixels,
+ (PIXMAN_FORMAT_BPP(format) / 8) * width);
+
+ /* Caller expects vflipped source image */
+ pixman_transform_init_translate(&transform,
+ pixman_int_to_fixed (x),
+ pixman_int_to_fixed (y - pixman_image_get_height (po->hw_buffer)));
+ pixman_transform_scale(&transform, NULL,
+ pixman_fixed_1,
+ pixman_fixed_minus_1);
+ pixman_image_set_transform(po->hw_buffer, &transform);
+
+ pixman_image_composite32(PIXMAN_OP_SRC,
+ po->hw_buffer, /* src */
+ NULL /* mask */,
+ out_buf, /* dest */
+ 0, 0, /* src_x, src_y */
+ 0, 0, /* mask_x, mask_y */
+ 0, 0, /* dest_x, dest_y */
+ pixman_image_get_width (po->hw_buffer), /* width */
+ pixman_image_get_height (po->hw_buffer) /* height */);
+ pixman_image_set_transform(po->hw_buffer, NULL);
+
+ pixman_image_unref(out_buf);
+
+ return 0;
+}
+
+static void
+box_scale(pixman_box32_t *dst, int scale)
+{
+ dst->x1 *= scale;
+ dst->x2 *= scale;
+ dst->y1 *= scale;
+ dst->y2 *= scale;
+}
+
+static void
+scale_region (pixman_region32_t *region, int scale)
+{
+ pixman_box32_t *rects, *scaled_rects;
+ int nrects, i;
+
+ if (scale != 1) {
+ rects = pixman_region32_rectangles(region, &nrects);
+ scaled_rects = calloc(nrects, sizeof(pixman_box32_t));
+
+ for (i = 0; i < nrects; i++) {
+ scaled_rects[i] = rects[i];
+ box_scale(&scaled_rects[i], scale);
+ }
+ pixman_region32_clear(region);
+
+ pixman_region32_init_rects (region, scaled_rects, nrects);
+ free (scaled_rects);
+ }
+}
+
+static void
+transform_region (pixman_region32_t *region, int width, int height, enum wl_output_transform transform)
+{
+ pixman_box32_t *rects, *transformed_rects;
+ int nrects, i;
+
+ if (transform == WL_OUTPUT_TRANSFORM_NORMAL)
+ return;
+
+ rects = pixman_region32_rectangles(region, &nrects);
+ transformed_rects = calloc(nrects, sizeof(pixman_box32_t));
+
+ for (i = 0; i < nrects; i++) {
+ switch (transform) {
+ default:
+ case WL_OUTPUT_TRANSFORM_NORMAL:
+ transformed_rects[i].x1 = rects[i].x1;
+ transformed_rects[i].y1 = rects[i].y1;
+ transformed_rects[i].x2 = rects[i].x2;
+ transformed_rects[i].y2 = rects[i].y2;
+ break;
+ case WL_OUTPUT_TRANSFORM_90:
+ transformed_rects[i].x1 = height - rects[i].y2;
+ transformed_rects[i].y1 = rects[i].x1;
+ transformed_rects[i].x2 = height - rects[i].y1;
+ transformed_rects[i].y2 = rects[i].x2;
+ break;
+ case WL_OUTPUT_TRANSFORM_180:
+ transformed_rects[i].x1 = width - rects[i].x2;
+ transformed_rects[i].y1 = height - rects[i].y2;
+ transformed_rects[i].x2 = width - rects[i].x1;
+ transformed_rects[i].y2 = height - rects[i].y1;
+ break;
+ case WL_OUTPUT_TRANSFORM_270:
+ transformed_rects[i].x1 = rects[i].y1;
+ transformed_rects[i].y1 = width - rects[i].x2;
+ transformed_rects[i].x2 = rects[i].y2;
+ transformed_rects[i].y2 = width - rects[i].x1;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED:
+ transformed_rects[i].x1 = width - rects[i].x2;
+ transformed_rects[i].y1 = rects[i].y1;
+ transformed_rects[i].x2 = width - rects[i].x1;
+ transformed_rects[i].y2 = rects[i].y2;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+ transformed_rects[i].x1 = height - rects[i].y2;
+ transformed_rects[i].y1 = width - rects[i].x2;
+ transformed_rects[i].x2 = height - rects[i].y1;
+ transformed_rects[i].y2 = width - rects[i].x1;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+ transformed_rects[i].x1 = rects[i].x1;
+ transformed_rects[i].y1 = height - rects[i].y2;
+ transformed_rects[i].x2 = rects[i].x2;
+ transformed_rects[i].y2 = height - rects[i].y1;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+ transformed_rects[i].x1 = rects[i].y1;
+ transformed_rects[i].y1 = rects[i].x1;
+ transformed_rects[i].x2 = rects[i].y2;
+ transformed_rects[i].y2 = rects[i].x2;
+ break;
+ }
+ }
+ pixman_region32_clear(region);
+
+ pixman_region32_init_rects (region, transformed_rects, nrects);
+ free (transformed_rects);
+}
+
+static void
+region_global_to_output(struct weston_output *output, pixman_region32_t *region)
+{
+ pixman_region32_translate(region, -output->x, -output->y);
+ transform_region (region, output->width, output->height, output->transform);
+ scale_region (region, output->current_scale);
+}
+
+#define D2F(v) pixman_double_to_fixed((double)v)
+
+static void
+repaint_region(struct weston_surface *es, struct weston_output *output,
+ pixman_region32_t *region, pixman_region32_t *surf_region,
+ pixman_op_t pixman_op)
+{
+ struct pixman_renderer *pr =
+ (struct pixman_renderer *) output->compositor->renderer;
+ struct pixman_surface_state *ps = get_surface_state(es);
+ struct pixman_output_state *po = get_output_state(output);
+ pixman_region32_t final_region;
+ float surface_x, surface_y;
+ pixman_transform_t transform;
+ pixman_fixed_t fw, fh;
+
+ /* The final region to be painted is the intersection of
+ * 'region' and 'surf_region'. However, 'region' is in the global
+ * coordinates, and 'surf_region' is in the surface-local
+ * coordinates
+ */
+ pixman_region32_init(&final_region);
+ if (surf_region) {
+ pixman_region32_copy(&final_region, surf_region);
+
+ /* Convert from surface to global coordinates */
+ if (!es->transform.enabled) {
+ pixman_region32_translate(&final_region, es->geometry.x, es->geometry.y);
+ } else {
+ weston_surface_to_global_float(es, 0, 0, &surface_x, &surface_y);
+ pixman_region32_translate(&final_region, (int)surface_x, (int)surface_y);
+ }
+
+ /* We need to paint the intersection */
+ pixman_region32_intersect(&final_region, &final_region, region);
+ } else {
+ /* If there is no surface region, just use the global region */
+ pixman_region32_copy(&final_region, region);
+ }
+
+ /* Convert from global to output coord */
+ region_global_to_output(output, &final_region);
+
+ /* And clip to it */
+ pixman_image_set_clip_region32 (po->shadow_image, &final_region);
+
+ /* Set up the source transformation based on the surface
+ position, the output position/transform/scale and the client
+ specified buffer transform/scale */
+ pixman_transform_init_identity(&transform);
+ pixman_transform_scale(&transform, NULL,
+ pixman_double_to_fixed ((double)1.0/output->current_scale),
+ pixman_double_to_fixed ((double)1.0/output->current_scale));
+
+ fw = pixman_int_to_fixed(output->width);
+ fh = pixman_int_to_fixed(output->height);
+ switch (output->transform) {
+ default:
+ case WL_OUTPUT_TRANSFORM_NORMAL:
+ case WL_OUTPUT_TRANSFORM_FLIPPED:
+ break;
+ case WL_OUTPUT_TRANSFORM_90:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+ pixman_transform_rotate(&transform, NULL, 0, -pixman_fixed_1);
+ pixman_transform_translate(&transform, NULL, 0, fh);
+ break;
+ case WL_OUTPUT_TRANSFORM_180:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+ pixman_transform_rotate(&transform, NULL, -pixman_fixed_1, 0);
+ pixman_transform_translate(&transform, NULL, fw, fh);
+ break;
+ case WL_OUTPUT_TRANSFORM_270:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+ pixman_transform_rotate(&transform, NULL, 0, pixman_fixed_1);
+ pixman_transform_translate(&transform, NULL, fw, 0);
+ break;
+ }
+
+ switch (output->transform) {
+ case WL_OUTPUT_TRANSFORM_FLIPPED:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+ pixman_transform_scale(&transform, NULL,
+ pixman_int_to_fixed (-1),
+ pixman_int_to_fixed (1));
+ pixman_transform_translate(&transform, NULL, fw, 0);
+ break;
+ }
+
+ pixman_transform_translate(&transform, NULL,
+ pixman_double_to_fixed (output->x),
+ pixman_double_to_fixed (output->y));
+
+ if (es->transform.enabled) {
+ /* Pixman supports only 2D transform matrix, but Weston uses 3D,
+ * so we're omitting Z coordinate here
+ */
+ pixman_transform_t surface_transform = {{
+ { D2F(es->transform.matrix.d[0]),
+ D2F(es->transform.matrix.d[4]),
+ D2F(es->transform.matrix.d[12]),
+ },
+ { D2F(es->transform.matrix.d[1]),
+ D2F(es->transform.matrix.d[5]),
+ D2F(es->transform.matrix.d[13]),
+ },
+ { D2F(es->transform.matrix.d[3]),
+ D2F(es->transform.matrix.d[7]),
+ D2F(es->transform.matrix.d[15]),
+ }
+ }};
+
+ pixman_transform_invert(&surface_transform, &surface_transform);
+ pixman_transform_multiply (&transform, &surface_transform, &transform);
+ } else {
+ pixman_transform_translate(&transform, NULL,
+ pixman_double_to_fixed ((double)-es->geometry.x),
+ pixman_double_to_fixed ((double)-es->geometry.y));
+ }
+
+
+ fw = pixman_int_to_fixed(es->geometry.width);
+ fh = pixman_int_to_fixed(es->geometry.height);
+
+ switch (es->buffer_transform) {
+ case WL_OUTPUT_TRANSFORM_FLIPPED:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+ pixman_transform_scale(&transform, NULL,
+ pixman_int_to_fixed (-1),
+ pixman_int_to_fixed (1));
+ pixman_transform_translate(&transform, NULL, fw, 0);
+ break;
+ }
+
+ switch (es->buffer_transform) {
+ default:
+ case WL_OUTPUT_TRANSFORM_NORMAL:
+ case WL_OUTPUT_TRANSFORM_FLIPPED:
+ break;
+ case WL_OUTPUT_TRANSFORM_90:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+ pixman_transform_rotate(&transform, NULL, 0, pixman_fixed_1);
+ pixman_transform_translate(&transform, NULL, fh, 0);
+ break;
+ case WL_OUTPUT_TRANSFORM_180:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+ pixman_transform_rotate(&transform, NULL, -pixman_fixed_1, 0);
+ pixman_transform_translate(&transform, NULL, fw, fh);
+ break;
+ case WL_OUTPUT_TRANSFORM_270:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+ pixman_transform_rotate(&transform, NULL, 0, -pixman_fixed_1);
+ pixman_transform_translate(&transform, NULL, 0, fw);
+ break;
+ }
+
+ pixman_transform_scale(&transform, NULL,
+ pixman_double_to_fixed ((double)es->buffer_scale),
+ pixman_double_to_fixed ((double)es->buffer_scale));
+
+ pixman_image_set_transform(ps->image, &transform);
+
+ if (es->transform.enabled || output->current_scale != es->buffer_scale)
+ pixman_image_set_filter(ps->image, PIXMAN_FILTER_BILINEAR, NULL, 0);
+ else
+ pixman_image_set_filter(ps->image, PIXMAN_FILTER_NEAREST, NULL, 0);
+
+ pixman_image_composite32(pixman_op,
+ ps->image, /* src */
+ NULL /* mask */,
+ po->shadow_image, /* dest */
+ 0, 0, /* src_x, src_y */
+ 0, 0, /* mask_x, mask_y */
+ 0, 0, /* dest_x, dest_y */
+ pixman_image_get_width (po->shadow_image), /* width */
+ pixman_image_get_height (po->shadow_image) /* height */);
+
+ if (pr->repaint_debug)
+ pixman_image_composite32(PIXMAN_OP_OVER,
+ pr->debug_color, /* src */
+ NULL /* mask */,
+ po->shadow_image, /* dest */
+ 0, 0, /* src_x, src_y */
+ 0, 0, /* mask_x, mask_y */
+ 0, 0, /* dest_x, dest_y */
+ pixman_image_get_width (po->shadow_image), /* width */
+ pixman_image_get_height (po->shadow_image) /* height */);
+
+ pixman_image_set_clip_region32 (po->shadow_image, NULL);
+
+ pixman_region32_fini(&final_region);
+}
+
+static void
+draw_surface(struct weston_surface *es, struct weston_output *output,
+ pixman_region32_t *damage) /* in global coordinates */
+{
+ struct pixman_surface_state *ps = get_surface_state(es);
+ /* repaint bounding region in global coordinates: */
+ pixman_region32_t repaint;
+ /* non-opaque region in surface coordinates: */
+ pixman_region32_t surface_blend;
+
+ /* No buffer attached */
+ if (!ps->image)
+ return;
+
+ pixman_region32_init(&repaint);
+ pixman_region32_intersect(&repaint,
+ &es->transform.boundingbox, damage);
+ pixman_region32_subtract(&repaint, &repaint, &es->clip);
+
+ if (!pixman_region32_not_empty(&repaint))
+ goto out;
+
+ if (output->zoom.active) {
+ weston_log("pixman renderer does not support zoom\n");
+ goto out;
+ }
+
+ /* TODO: Implement repaint_region_complex() using pixman_composite_trapezoids() */
+ if (es->transform.enabled &&
+ es->transform.matrix.type != WESTON_MATRIX_TRANSFORM_TRANSLATE) {
+ repaint_region(es, output, &repaint, NULL, PIXMAN_OP_OVER);
+ } else {
+ /* blended region is whole surface minus opaque region: */
+ pixman_region32_init_rect(&surface_blend, 0, 0,
+ es->geometry.width, es->geometry.height);
+ pixman_region32_subtract(&surface_blend, &surface_blend, &es->opaque);
+
+ if (pixman_region32_not_empty(&es->opaque)) {
+ repaint_region(es, output, &repaint, &es->opaque, PIXMAN_OP_SRC);
+ }
+
+ if (pixman_region32_not_empty(&surface_blend)) {
+ repaint_region(es, output, &repaint, &surface_blend, PIXMAN_OP_OVER);
+ }
+ pixman_region32_fini(&surface_blend);
+ }
+
+
+out:
+ pixman_region32_fini(&repaint);
+}
+static void
+repaint_surfaces(struct weston_output *output, pixman_region32_t *damage)
+{
+ struct weston_compositor *compositor = output->compositor;
+ struct weston_surface *surface;
+
+ wl_list_for_each_reverse(surface, &compositor->surface_list, link)
+ if (surface->plane == &compositor->primary_plane)
+ draw_surface(surface, output, damage);
+}
+
+static void
+copy_to_hw_buffer(struct weston_output *output, pixman_region32_t *region)
+{
+ struct pixman_output_state *po = get_output_state(output);
+ pixman_region32_t output_region;
+
+ pixman_region32_init(&output_region);
+ pixman_region32_copy(&output_region, region);
+
+ region_global_to_output(output, &output_region);
+
+ pixman_image_set_clip_region32 (po->hw_buffer, &output_region);
+
+ pixman_image_composite32(PIXMAN_OP_SRC,
+ po->shadow_image, /* src */
+ NULL /* mask */,
+ po->hw_buffer, /* dest */
+ 0, 0, /* src_x, src_y */
+ 0, 0, /* mask_x, mask_y */
+ 0, 0, /* dest_x, dest_y */
+ pixman_image_get_width (po->hw_buffer), /* width */
+ pixman_image_get_height (po->hw_buffer) /* height */);
+
+ pixman_image_set_clip_region32 (po->hw_buffer, NULL);
+}
+
+static void
+pixman_renderer_repaint_output(struct weston_output *output,
+ pixman_region32_t *output_damage)
+{
+ struct pixman_output_state *po = get_output_state(output);
+
+ if (!po->hw_buffer)
+ return;
+
+ repaint_surfaces(output, output_damage);
+ copy_to_hw_buffer(output, output_damage);
+
+ pixman_region32_copy(&output->previous_damage, output_damage);
+ wl_signal_emit(&output->frame_signal, output);
+
+ /* Actual flip should be done by caller */
+}
+
+static void
+pixman_renderer_flush_damage(struct weston_surface *surface)
+{
+ /* No-op for pixman renderer */
+}
+
+static void
+pixman_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer)
+{
+ struct pixman_surface_state *ps = get_surface_state(es);
+ struct wl_shm_buffer *shm_buffer;
+ pixman_format_code_t pixman_format;
+
+ weston_buffer_reference(&ps->buffer_ref, buffer);
+
+ if (ps->image) {
+ pixman_image_unref(ps->image);
+ ps->image = NULL;
+ }
+
+ if (!buffer)
+ return;
+
+ shm_buffer = wl_shm_buffer_get(buffer->resource);
+
+ if (! shm_buffer) {
+ weston_log("Pixman renderer supports only SHM buffers\n");
+ weston_buffer_reference(&ps->buffer_ref, NULL);
+ return;
+ }
+
+ switch (wl_shm_buffer_get_format(shm_buffer)) {
+ case WL_SHM_FORMAT_XRGB8888:
+ pixman_format = PIXMAN_x8r8g8b8;
+ break;
+ case WL_SHM_FORMAT_ARGB8888:
+ pixman_format = PIXMAN_a8r8g8b8;
+ break;
+ case WL_SHM_FORMAT_RGB565:
+ pixman_format = PIXMAN_r5g6b5;
+ break;
+ default:
+ weston_log("Unsupported SHM buffer format\n");
+ weston_buffer_reference(&ps->buffer_ref, NULL);
+ return;
+ break;
+ }
+
+ buffer->shm_buffer = shm_buffer;
+ buffer->width = wl_shm_buffer_get_width(shm_buffer);
+ buffer->height = wl_shm_buffer_get_height(shm_buffer);
+
+ ps->image = pixman_image_create_bits(pixman_format,
+ buffer->width, buffer->height,
+ wl_shm_buffer_get_data(shm_buffer),
+ wl_shm_buffer_get_stride(shm_buffer));
+}
+
+static int
+pixman_renderer_create_surface(struct weston_surface *surface)
+{
+ struct pixman_surface_state *ps;
+
+ ps = calloc(1, sizeof *ps);
+ if (!ps)
+ return -1;
+
+ surface->renderer_state = ps;
+
+ return 0;
+}
+
+static void
+pixman_renderer_surface_set_color(struct weston_surface *es,
+ float red, float green, float blue, float alpha)
+{
+ struct pixman_surface_state *ps = get_surface_state(es);
+ pixman_color_t color;
+
+ color.red = red * 0xffff;
+ color.green = green * 0xffff;
+ color.blue = blue * 0xffff;
+ color.alpha = alpha * 0xffff;
+
+ if (ps->image) {
+ pixman_image_unref(ps->image);
+ ps->image = NULL;
+ }
+
+ ps->image = pixman_image_create_solid_fill(&color);
+}
+
+static void
+pixman_renderer_destroy_surface(struct weston_surface *surface)
+{
+ struct pixman_surface_state *ps = get_surface_state(surface);
+
+ if (ps->image) {
+ pixman_image_unref(ps->image);
+ ps->image = NULL;
+ }
+ weston_buffer_reference(&ps->buffer_ref, NULL);
+ free(ps);
+}
+
+static void
+pixman_renderer_destroy(struct weston_compositor *ec)
+{
+ free(ec->renderer);
+ ec->renderer = NULL;
+}
+
+static void
+debug_binding(struct weston_seat *seat, uint32_t time, uint32_t key,
+ void *data)
+{
+ struct weston_compositor *ec = data;
+ struct pixman_renderer *pr = (struct pixman_renderer *) ec->renderer;
+
+ pr->repaint_debug ^= 1;
+
+ if (pr->repaint_debug) {
+ pixman_color_t red = {
+ 0x3fff, 0x0000, 0x0000, 0x3fff
+ };
+
+ pr->debug_color = pixman_image_create_solid_fill(&red);
+ } else {
+ pixman_image_unref(pr->debug_color);
+ weston_compositor_damage_all(ec);
+ }
+}
+
+WL_EXPORT int
+pixman_renderer_init(struct weston_compositor *ec)
+{
+ struct pixman_renderer *renderer;
+
+ renderer = malloc(sizeof *renderer);
+ if (renderer == NULL)
+ return -1;
+
+ renderer->repaint_debug = 0;
+ renderer->debug_color = NULL;
+ renderer->base.read_pixels = pixman_renderer_read_pixels;
+ renderer->base.repaint_output = pixman_renderer_repaint_output;
+ renderer->base.flush_damage = pixman_renderer_flush_damage;
+ renderer->base.attach = pixman_renderer_attach;
+ renderer->base.create_surface = pixman_renderer_create_surface;
+ renderer->base.surface_set_color = pixman_renderer_surface_set_color;
+ renderer->base.destroy_surface = pixman_renderer_destroy_surface;
+ renderer->base.destroy = pixman_renderer_destroy;
+ ec->renderer = &renderer->base;
+ ec->capabilities |= WESTON_CAP_ROTATION_ANY;
+ ec->capabilities |= WESTON_CAP_CAPTURE_YFLIP;
+
+ weston_compositor_add_debug_binding(ec, KEY_R,
+ debug_binding, ec);
+
+ wl_display_add_shm_format(ec->wl_display, WL_SHM_FORMAT_RGB565);
+
+ return 0;
+}
+
+WL_EXPORT void
+pixman_renderer_output_set_buffer(struct weston_output *output, pixman_image_t *buffer)
+{
+ struct pixman_output_state *po = get_output_state(output);
+
+ if (po->hw_buffer)
+ pixman_image_unref(po->hw_buffer);
+ po->hw_buffer = buffer;
+
+ if (po->hw_buffer) {
+ output->compositor->read_format = pixman_image_get_format(po->hw_buffer);
+ pixman_image_ref(po->hw_buffer);
+ }
+}
+
+WL_EXPORT int
+pixman_renderer_output_create(struct weston_output *output)
+{
+ struct pixman_output_state *po = calloc(1, sizeof *po);
+ int w, h;
+
+ if (!po)
+ return -1;
+
+ /* set shadow image transformation */
+ w = output->current_mode->width;
+ h = output->current_mode->height;
+
+ po->shadow_buffer = malloc(w * h * 4);
+
+ if (!po->shadow_buffer) {
+ free(po);
+ return -1;
+ }
+
+ po->shadow_image =
+ pixman_image_create_bits(PIXMAN_x8r8g8b8, w, h,
+ po->shadow_buffer, w * 4);
+
+ if (!po->shadow_image) {
+ free(po->shadow_buffer);
+ free(po);
+ return -1;
+ }
+
+ output->renderer_state = po;
+
+ return 0;
+}
+
+WL_EXPORT void
+pixman_renderer_output_destroy(struct weston_output *output)
+{
+ struct pixman_output_state *po = get_output_state(output);
+
+ pixman_image_unref(po->shadow_image);
+
+ if (po->hw_buffer)
+ pixman_image_unref(po->hw_buffer);
+
+ po->shadow_image = NULL;
+ po->hw_buffer = NULL;
+
+ free(po);
+}
diff --git a/src/pixman-renderer.h b/src/pixman-renderer.h
new file mode 100644
index 00000000..2532299e
--- /dev/null
+++ b/src/pixman-renderer.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright © 2013 Vasily Khoruzhick <anarsoul@gmail.com>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that 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.
+ */
+
+#include "config.h"
+
+#include "compositor.h"
+
+int
+pixman_renderer_init(struct weston_compositor *ec);
+
+int
+pixman_renderer_output_create(struct weston_output *output);
+
+void
+pixman_renderer_output_set_buffer(struct weston_output *output, pixman_image_t *buffer);
+
+void
+pixman_renderer_output_destroy(struct weston_output *output);
diff --git a/src/rpi-bcm-stubs.h b/src/rpi-bcm-stubs.h
new file mode 100644
index 00000000..703cd771
--- /dev/null
+++ b/src/rpi-bcm-stubs.h
@@ -0,0 +1,314 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/*
+ * This file provides just enough types and stubs, so that the rpi-backend
+ * can be built without the real headers and libraries of the Raspberry Pi.
+ *
+ * This file CANNOT be used to build a working rpi-backend, it is intended
+ * only for build-testing, when the proper headers are not available.
+ */
+
+#ifndef RPI_BCM_STUBS
+#define RPI_BCM_STUBS
+
+#include <stdint.h>
+
+/* from /opt/vc/include/bcm_host.h */
+
+static inline void bcm_host_init(void) {}
+static inline void bcm_host_deinit(void) {}
+
+
+/* from /opt/vc/include/interface/vmcs_host/vc_dispservice_defs.h */
+
+#define TRANSFORM_HFLIP (1<<0)
+#define TRANSFORM_VFLIP (1<<1)
+#define TRANSFORM_TRANSPOSE (1<<2)
+
+
+/* from /opt/vc/include/interface/vctypes/vc_display_types.h */
+
+typedef enum
+{
+ VCOS_DISPLAY_INPUT_FORMAT_INVALID = 0,
+} DISPLAY_INPUT_FORMAT_T;
+
+/* from /opt/vc/include/interface/vctypes/vc_image_types.h */
+
+typedef struct tag_VC_RECT_T {
+ int32_t x;
+ int32_t y;
+ int32_t width;
+ int32_t height;
+} VC_RECT_T;
+
+typedef enum {
+ VC_IMAGE_ROT0,
+ /* these are not the right values: */
+ VC_IMAGE_ROT90,
+ VC_IMAGE_ROT180,
+ VC_IMAGE_ROT270,
+ VC_IMAGE_MIRROR_ROT0,
+ VC_IMAGE_MIRROR_ROT90,
+ VC_IMAGE_MIRROR_ROT180,
+ VC_IMAGE_MIRROR_ROT270,
+} VC_IMAGE_TRANSFORM_T;
+
+typedef enum
+{
+ VC_IMAGE_MIN = 0,
+ /* these are not the right values: */
+ VC_IMAGE_ARGB8888,
+ VC_IMAGE_XRGB8888,
+ VC_IMAGE_RGB565,
+} VC_IMAGE_TYPE_T;
+
+/* from /opt/vc/include/interface/vmcs_host/vc_dispmanx_types.h */
+
+typedef uint32_t DISPMANX_DISPLAY_HANDLE_T;
+typedef uint32_t DISPMANX_UPDATE_HANDLE_T;
+typedef uint32_t DISPMANX_ELEMENT_HANDLE_T;
+typedef uint32_t DISPMANX_RESOURCE_HANDLE_T;
+typedef uint32_t DISPMANX_PROTECTION_T;
+
+#define DISPMANX_NO_HANDLE 0
+#define DISPMANX_PROTECTION_NONE 0
+#define DISPMANX_ID_HDMI 2
+
+typedef enum {
+ /* Bottom 2 bits sets the alpha mode */
+ DISPMANX_FLAGS_ALPHA_FROM_SOURCE = 0,
+ DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS = 1,
+ DISPMANX_FLAGS_ALPHA_FIXED_NON_ZERO = 2,
+ DISPMANX_FLAGS_ALPHA_FIXED_EXCEED_0X07 = 3,
+
+ DISPMANX_FLAGS_ALPHA_PREMULT = 1 << 16,
+ DISPMANX_FLAGS_ALPHA_MIX = 1 << 17
+} DISPMANX_FLAGS_ALPHA_T;
+
+typedef struct {
+ DISPMANX_FLAGS_ALPHA_T flags;
+ uint32_t opacity;
+ DISPMANX_RESOURCE_HANDLE_T mask;
+} VC_DISPMANX_ALPHA_T;
+
+typedef struct {
+ int32_t width;
+ int32_t height;
+ VC_IMAGE_TRANSFORM_T transform;
+ DISPLAY_INPUT_FORMAT_T input_format;
+} DISPMANX_MODEINFO_T;
+
+typedef enum {
+ DISPMANX_NO_ROTATE = 0,
+ DISPMANX_ROTATE_90 = 1,
+ DISPMANX_ROTATE_180 = 2,
+ DISPMANX_ROTATE_270 = 3,
+
+ DISPMANX_FLIP_HRIZ = 1 << 16,
+ DISPMANX_FLIP_VERT = 1 << 17
+} DISPMANX_TRANSFORM_T;
+
+typedef struct {
+ uint32_t dummy;
+} DISPMANX_CLAMP_T;
+
+typedef void (*DISPMANX_CALLBACK_FUNC_T)(DISPMANX_UPDATE_HANDLE_T u,
+ void *arg);
+
+/* from /opt/vc/include/interface/vmcs_host/vc_dispmanx.h */
+
+static inline int
+vc_dispmanx_rect_set(VC_RECT_T *rect, uint32_t x_offset, uint32_t y_offset,
+ uint32_t width, uint32_t height)
+{
+ rect->x = x_offset;
+ rect->y = y_offset;
+ rect->width = width;
+ rect->height = height;
+ return 0;
+}
+
+static inline DISPMANX_RESOURCE_HANDLE_T
+vc_dispmanx_resource_create(VC_IMAGE_TYPE_T type, uint32_t width,
+ uint32_t height, uint32_t *native_image_handle)
+{
+ return DISPMANX_NO_HANDLE;
+}
+
+static inline int
+vc_dispmanx_resource_write_data(DISPMANX_RESOURCE_HANDLE_T res,
+ VC_IMAGE_TYPE_T src_type,
+ int src_pitch,
+ void *src_address,
+ const VC_RECT_T *rect)
+{
+ return -1;
+}
+
+static inline int
+vc_dispmanx_resource_write_data_rect(DISPMANX_RESOURCE_HANDLE_T handle,
+ VC_IMAGE_TYPE_T src_type,
+ int src_pitch,
+ void *src_address,
+ const VC_RECT_T *src_rect,
+ uint32_t dst_x,
+ uint32_t dst_y)
+{
+ return -1;
+}
+
+static inline int
+vc_dispmanx_resource_read_data(DISPMANX_RESOURCE_HANDLE_T handle,
+ const VC_RECT_T *p_rect,
+ void *dst_address,
+ uint32_t dst_pitch)
+{
+ return -1;
+}
+
+static inline int
+vc_dispmanx_resource_delete(DISPMANX_RESOURCE_HANDLE_T res)
+{
+ return -1;
+}
+
+static inline DISPMANX_DISPLAY_HANDLE_T
+vc_dispmanx_display_open(uint32_t device)
+{
+ return DISPMANX_NO_HANDLE;
+}
+
+static inline int
+vc_dispmanx_display_close(DISPMANX_DISPLAY_HANDLE_T display)
+{
+ return -1;
+}
+
+static inline int
+vc_dispmanx_display_get_info(DISPMANX_DISPLAY_HANDLE_T display,
+ DISPMANX_MODEINFO_T *pinfo)
+{
+ return -1;
+}
+
+static inline DISPMANX_UPDATE_HANDLE_T
+vc_dispmanx_update_start(int32_t priority)
+{
+ return DISPMANX_NO_HANDLE;
+}
+
+static inline DISPMANX_ELEMENT_HANDLE_T
+vc_dispmanx_element_add(DISPMANX_UPDATE_HANDLE_T update,
+ DISPMANX_DISPLAY_HANDLE_T display,
+ int32_t layer,
+ const VC_RECT_T *dest_rect,
+ DISPMANX_RESOURCE_HANDLE_T src,
+ const VC_RECT_T *src_rect,
+ DISPMANX_PROTECTION_T protection,
+ VC_DISPMANX_ALPHA_T *alpha,
+ DISPMANX_CLAMP_T *clamp,
+ DISPMANX_TRANSFORM_T transform)
+{
+ return DISPMANX_NO_HANDLE;
+}
+
+static inline int
+vc_dispmanx_element_change_source(DISPMANX_UPDATE_HANDLE_T update,
+ DISPMANX_ELEMENT_HANDLE_T element,
+ DISPMANX_RESOURCE_HANDLE_T src)
+{
+ return -1;
+}
+
+static inline int
+vc_dispmanx_element_modified(DISPMANX_UPDATE_HANDLE_T update,
+ DISPMANX_ELEMENT_HANDLE_T element,
+ const VC_RECT_T *rect)
+{
+ return -1;
+}
+
+static inline int
+vc_dispmanx_element_remove(DISPMANX_UPDATE_HANDLE_T update,
+ DISPMANX_ELEMENT_HANDLE_T element)
+{
+ return -1;
+}
+
+static inline int
+vc_dispmanx_update_submit(DISPMANX_UPDATE_HANDLE_T update,
+ DISPMANX_CALLBACK_FUNC_T cb_func, void *cb_arg)
+{
+ return -1;
+}
+
+static inline int
+vc_dispmanx_update_submit_sync(DISPMANX_UPDATE_HANDLE_T update)
+{
+ return -1;
+}
+
+static inline int
+vc_dispmanx_element_change_attributes(DISPMANX_UPDATE_HANDLE_T update,
+ DISPMANX_ELEMENT_HANDLE_T element,
+ uint32_t change_flags,
+ int32_t layer,
+ uint8_t opacity,
+ const VC_RECT_T *dest_rect,
+ const VC_RECT_T *src_rect,
+ DISPMANX_RESOURCE_HANDLE_T mask,
+ VC_IMAGE_TRANSFORM_T transform)
+{
+ return -1;
+}
+
+static inline int
+vc_dispmanx_snapshot(DISPMANX_DISPLAY_HANDLE_T display,
+ DISPMANX_RESOURCE_HANDLE_T snapshot_resource,
+ VC_IMAGE_TRANSFORM_T transform)
+{
+ return -1;
+}
+
+struct wl_resource;
+static inline DISPMANX_RESOURCE_HANDLE_T
+vc_dispmanx_get_handle_from_wl_buffer(struct wl_resource *_buffer)
+{
+ return DISPMANX_NO_HANDLE;
+}
+
+/* from /opt/vc/include/EGL/eglplatform.h */
+
+typedef struct {
+ DISPMANX_ELEMENT_HANDLE_T element;
+ int width;
+ int height;
+} EGL_DISPMANX_WINDOW_T;
+
+#endif /* RPI_BCM_STUBS */
diff --git a/src/rpi-renderer.c b/src/rpi-renderer.c
new file mode 100644
index 00000000..a95cc604
--- /dev/null
+++ b/src/rpi-renderer.c
@@ -0,0 +1,1594 @@
+/*
+ * Copyright © 2012-2013 Raspberry Pi Foundation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that 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.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+
+#ifdef HAVE_BCM_HOST
+# include <bcm_host.h>
+#else
+# include "rpi-bcm-stubs.h"
+#endif
+
+#include "compositor.h"
+#include "rpi-renderer.h"
+
+#ifdef ENABLE_EGL
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include "weston-egl-ext.h"
+#endif
+
+/*
+ * Dispmanx API offers alpha-blended overlays for hardware compositing.
+ * The final composite consists of dispmanx elements, and their contents:
+ * the dispmanx resource assigned to the element. The elements may be
+ * scanned out directly, or composited to a temporary surface, depending on
+ * how the firmware decides to handle the scene. Updates to multiple elements
+ * may be queued in a single dispmanx update object, resulting in atomic and
+ * vblank synchronized display updates.
+ *
+ * To avoid tearing and display artifacts, the current dispmanx resource in a
+ * dispmanx element must not be touched. Therefore each element must be
+ * double-buffered, using two resources, the front and the back. While a
+ * dispmanx update is running, the both resources must be considered in use.
+ *
+ * A resource may be destroyed only, when the update removing the element has
+ * completed. Otherwise you risk showing an incomplete composition.
+ */
+
+#ifndef ELEMENT_CHANGE_LAYER
+/* copied from interface/vmcs_host/vc_vchi_dispmanx.h of userland.git */
+#define ELEMENT_CHANGE_LAYER (1<<0)
+#define ELEMENT_CHANGE_OPACITY (1<<1)
+#define ELEMENT_CHANGE_DEST_RECT (1<<2)
+#define ELEMENT_CHANGE_SRC_RECT (1<<3)
+#define ELEMENT_CHANGE_MASK_RESOURCE (1<<4)
+#define ELEMENT_CHANGE_TRANSFORM (1<<5)
+#endif
+
+#if 0
+#define DBG(...) \
+ weston_log(__VA_ARGS__)
+#else
+#define DBG(...) do {} while (0)
+#endif
+
+/* If we had a fully featured vc_dispmanx_resource_write_data()... */
+/*#define HAVE_RESOURCE_WRITE_DATA_RECT 1*/
+
+struct rpi_resource {
+ DISPMANX_RESOURCE_HANDLE_T handle;
+ int width;
+ int height; /* height of the image (valid pixel data) */
+ int stride; /* bytes */
+ int buffer_height; /* height of the buffer */
+ VC_IMAGE_TYPE_T ifmt;
+};
+
+struct rpir_output;
+
+struct rpir_egl_buffer {
+ struct weston_buffer_reference buffer_ref;
+ DISPMANX_RESOURCE_HANDLE_T resource_handle;
+};
+
+enum buffer_type {
+ BUFFER_TYPE_NULL,
+ BUFFER_TYPE_SHM,
+ BUFFER_TYPE_EGL
+};
+
+struct rpir_surface {
+ struct weston_surface *surface;
+
+ /* If link is empty, the surface is guaranteed to not be on screen,
+ * i.e. updates removing Elements have completed.
+ */
+ struct wl_list link;
+
+ DISPMANX_ELEMENT_HANDLE_T handle;
+ int layer;
+ int need_swap;
+ int single_buffer;
+
+ struct rpi_resource resources[2];
+ struct rpi_resource *front;
+ struct rpi_resource *back;
+ pixman_region32_t prev_damage;
+
+ struct rpir_egl_buffer *egl_front;
+ struct rpir_egl_buffer *egl_back;
+ struct rpir_egl_buffer *egl_old_front;
+
+ struct weston_buffer_reference buffer_ref;
+ enum buffer_type buffer_type;
+};
+
+struct rpir_output {
+ DISPMANX_DISPLAY_HANDLE_T display;
+
+ DISPMANX_UPDATE_HANDLE_T update;
+ struct weston_matrix matrix;
+
+ /* all Elements currently on screen */
+ struct wl_list surface_list; /* struct rpir_surface::link */
+
+ /* Elements just removed, waiting for update completion */
+ struct wl_list surface_cleanup_list; /* struct rpir_surface::link */
+
+ struct rpi_resource capture_buffer;
+ uint8_t *capture_data;
+};
+
+struct rpi_renderer {
+ struct weston_renderer base;
+
+ int single_buffer;
+
+#ifdef ENABLE_EGL
+ EGLDisplay egl_display;
+
+ PFNEGLBINDWAYLANDDISPLAYWL bind_display;
+ PFNEGLUNBINDWAYLANDDISPLAYWL unbind_display;
+ PFNEGLQUERYWAYLANDBUFFERWL query_buffer;
+#endif
+ int has_bind_display;
+};
+
+static inline struct rpir_surface *
+to_rpir_surface(struct weston_surface *surface)
+{
+ return surface->renderer_state;
+}
+
+static inline struct rpir_output *
+to_rpir_output(struct weston_output *output)
+{
+ return output->renderer_state;
+}
+
+static inline struct rpi_renderer *
+to_rpi_renderer(struct weston_compositor *compositor)
+{
+ return container_of(compositor->renderer, struct rpi_renderer, base);
+}
+
+static inline int
+int_max(int a, int b)
+{
+ return a > b ? a : b;
+}
+
+static inline void
+int_swap(int *a, int *b)
+{
+ int tmp = *a;
+ *a = *b;
+ *b = tmp;
+}
+
+static uint8_t
+float2uint8(float f)
+{
+ int v = roundf(f * 255.0f);
+
+ return v < 0 ? 0 : (v > 255 ? 255 : v);
+}
+
+static void
+rpi_resource_init(struct rpi_resource *resource)
+{
+ resource->handle = DISPMANX_NO_HANDLE;
+}
+
+static void
+rpi_resource_release(struct rpi_resource *resource)
+{
+ if (resource->handle == DISPMANX_NO_HANDLE)
+ return;
+
+ vc_dispmanx_resource_delete(resource->handle);
+ DBG("resource %p release\n", resource);
+ resource->handle = DISPMANX_NO_HANDLE;
+}
+
+static int
+rpi_resource_realloc(struct rpi_resource *resource, VC_IMAGE_TYPE_T ifmt,
+ int width, int height, int stride, int buffer_height)
+{
+ uint32_t dummy;
+
+ if (resource->handle != DISPMANX_NO_HANDLE &&
+ resource->width == width &&
+ resource->height == height &&
+ resource->stride == stride &&
+ resource->buffer_height == buffer_height &&
+ resource->ifmt == ifmt)
+ return 0;
+
+ rpi_resource_release(resource);
+
+ /* NOTE: if stride is not a multiple of 16 pixels in bytes,
+ * the vc_image_* functions may break. Dispmanx elements
+ * should be fine, though. Buffer_height probably has similar
+ * constraints, too.
+ */
+ resource->handle =
+ vc_dispmanx_resource_create(ifmt,
+ width | (stride << 16),
+ height | (buffer_height << 16),
+ &dummy);
+ if (resource->handle == DISPMANX_NO_HANDLE)
+ return -1;
+
+ resource->width = width;
+ resource->height = height;
+ resource->stride = stride;
+ resource->buffer_height = buffer_height;
+ resource->ifmt = ifmt;
+ DBG("resource %p alloc\n", resource);
+ return 1;
+}
+
+/* A firmware workaround for broken ALPHA_PREMULT + ALPHA_MIX hardware. */
+#define PREMULT_ALPHA_FLAG (1 << 31)
+
+static VC_IMAGE_TYPE_T
+shm_buffer_get_vc_format(struct wl_shm_buffer *buffer)
+{
+ switch (wl_shm_buffer_get_format(buffer)) {
+ case WL_SHM_FORMAT_XRGB8888:
+ return VC_IMAGE_XRGB8888;
+ case WL_SHM_FORMAT_ARGB8888:
+ return VC_IMAGE_ARGB8888 | PREMULT_ALPHA_FLAG;
+ case WL_SHM_FORMAT_RGB565:
+ return VC_IMAGE_RGB565;
+ default:
+ /* invalid format */
+ return VC_IMAGE_MIN;
+ }
+}
+
+static int
+rpi_resource_update(struct rpi_resource *resource, struct weston_buffer *buffer,
+ pixman_region32_t *region)
+{
+ pixman_region32_t write_region;
+ pixman_box32_t *r;
+ VC_RECT_T rect;
+ VC_IMAGE_TYPE_T ifmt;
+ uint32_t *pixels;
+ int width;
+ int height;
+ int stride;
+ int ret;
+#ifdef HAVE_RESOURCE_WRITE_DATA_RECT
+ int n;
+#endif
+
+ if (!buffer)
+ return -1;
+
+ ifmt = shm_buffer_get_vc_format(buffer->shm_buffer);
+ width = wl_shm_buffer_get_width(buffer->shm_buffer);
+ height = wl_shm_buffer_get_height(buffer->shm_buffer);
+ stride = wl_shm_buffer_get_stride(buffer->shm_buffer);
+ pixels = wl_shm_buffer_get_data(buffer->shm_buffer);
+
+ ret = rpi_resource_realloc(resource, ifmt & ~PREMULT_ALPHA_FLAG,
+ width, height, stride, height);
+ if (ret < 0)
+ return -1;
+
+ pixman_region32_init_rect(&write_region, 0, 0, width, height);
+ if (ret == 0)
+ pixman_region32_intersect(&write_region,
+ &write_region, region);
+
+#ifdef HAVE_RESOURCE_WRITE_DATA_RECT
+ /* XXX: Can this do a format conversion, so that scanout does not have to? */
+ r = pixman_region32_rectangles(&write_region, &n);
+ while (n--) {
+ vc_dispmanx_rect_set(&rect, r[n].x1, r[n].y1,
+ r[n].x2 - r[n].x1, r[n].y2 - r[n].y1);
+
+ ret = vc_dispmanx_resource_write_data_rect(resource->handle,
+ ifmt, stride,
+ pixels, &rect,
+ rect.x, rect.y);
+ DBG("%s: %p %ux%u@%u,%u, ret %d\n", __func__, resource,
+ rect.width, rect.height, rect.x, rect.y, ret);
+ if (ret)
+ break;
+ }
+#else
+ /* vc_dispmanx_resource_write_data() ignores ifmt,
+ * rect.x, rect.width, and uses stride only for computing
+ * the size of the transfer as rect.height * stride.
+ * Therefore we can only write rows starting at x=0.
+ * To be able to write more than one scanline at a time,
+ * the resource must have been created with the same stride
+ * as used here, and we must write full scanlines.
+ */
+
+ r = pixman_region32_extents(&write_region);
+ vc_dispmanx_rect_set(&rect, 0, r->y1, width, r->y2 - r->y1);
+ ret = vc_dispmanx_resource_write_data(resource->handle,
+ ifmt, stride, pixels, &rect);
+ DBG("%s: %p %ux%u@%u,%u, ret %d\n", __func__, resource,
+ width, r->y2 - r->y1, 0, r->y1, ret);
+#endif
+
+ pixman_region32_fini(&write_region);
+
+ return ret ? -1 : 0;
+}
+
+static struct rpir_surface *
+rpir_surface_create(struct rpi_renderer *renderer)
+{
+ struct rpir_surface *surface;
+
+ surface = calloc(1, sizeof *surface);
+ if (!surface)
+ return NULL;
+
+ wl_list_init(&surface->link);
+ surface->single_buffer = renderer->single_buffer;
+ surface->handle = DISPMANX_NO_HANDLE;
+ rpi_resource_init(&surface->resources[0]);
+ rpi_resource_init(&surface->resources[1]);
+ surface->front = &surface->resources[0];
+ if (surface->single_buffer)
+ surface->back = &surface->resources[0];
+ else
+ surface->back = &surface->resources[1];
+ surface->buffer_type = BUFFER_TYPE_NULL;
+
+ pixman_region32_init(&surface->prev_damage);
+
+ return surface;
+}
+
+static void
+rpir_surface_destroy(struct rpir_surface *surface)
+{
+ wl_list_remove(&surface->link);
+
+ if (surface->handle != DISPMANX_NO_HANDLE)
+ weston_log("ERROR rpi: destroying on-screen element\n");
+
+ pixman_region32_fini(&surface->prev_damage);
+ rpi_resource_release(&surface->resources[0]);
+ rpi_resource_release(&surface->resources[1]);
+ DBG("rpir_surface %p destroyed (%u)\n", surface, surface->handle);
+
+ if (surface->egl_back != NULL) {
+ weston_buffer_reference(&surface->egl_back->buffer_ref, NULL);
+ free(surface->egl_back);
+ surface->egl_back = NULL;
+ }
+
+ if (surface->egl_front != NULL) {
+ weston_buffer_reference(&surface->egl_front->buffer_ref, NULL);
+ free(surface->egl_front);
+ surface->egl_front = NULL;
+ }
+
+ if (surface->egl_old_front != NULL) {
+ weston_buffer_reference(&surface->egl_old_front->buffer_ref, NULL);
+ free(surface->egl_old_front);
+ surface->egl_old_front = NULL;
+ }
+
+ free(surface);
+}
+
+static int
+rpir_surface_damage(struct rpir_surface *surface, struct weston_buffer *buffer,
+ pixman_region32_t *damage)
+{
+ pixman_region32_t upload;
+ int ret;
+
+ if (!pixman_region32_not_empty(damage))
+ return 0;
+
+ DBG("rpir_surface %p update resource %p\n", surface, surface->back);
+
+ /* XXX: todo: if no surface->handle, update front buffer directly
+ * to avoid creating a new back buffer */
+ if (surface->single_buffer) {
+ ret = rpi_resource_update(surface->front, buffer, damage);
+ } else {
+ pixman_region32_init(&upload);
+ pixman_region32_union(&upload, &surface->prev_damage, damage);
+ ret = rpi_resource_update(surface->back, buffer, &upload);
+ pixman_region32_fini(&upload);
+ }
+
+ pixman_region32_copy(&surface->prev_damage, damage);
+ surface->need_swap = 1;
+
+ return ret;
+}
+
+static void
+matrix_type_str(struct weston_matrix *matrix, char *buf, int len)
+{
+ static const char types[33] = "TSRO";
+ unsigned mask = matrix->type;
+ int i = 0;
+
+ while (mask && i < len - 1) {
+ if (mask & (1u << i))
+ *buf++ = types[i];
+ mask &= ~(1u << i);
+ i++;
+ }
+ *buf = '\0';
+}
+
+static void
+log_print_matrix(struct weston_matrix *matrix)
+{
+ char typestr[6];
+ float *d = matrix->d;
+
+ matrix_type_str(matrix, typestr, sizeof typestr);
+ weston_log_continue("%14.6e %14.6e %14.6e %14.6e\n",
+ d[0], d[4], d[8], d[12]);
+ weston_log_continue("%14.6e %14.6e %14.6e %14.6e\n",
+ d[1], d[5], d[9], d[13]);
+ weston_log_continue("%14.6e %14.6e %14.6e %14.6e\n",
+ d[2], d[6], d[10], d[14]);
+ weston_log_continue("%14.6e %14.6e %14.6e %14.6e type: %s\n",
+ d[3], d[7], d[11], d[15], typestr);
+}
+
+static void
+warn_bad_matrix(struct weston_matrix *total, struct weston_matrix *output,
+ struct weston_matrix *surface)
+{
+ static int n_warn;
+ char typestr[6];
+
+ if (n_warn++ == 10)
+ weston_log("%s: not showing more warnings\n", __func__);
+
+ if (n_warn > 10)
+ return;
+
+ weston_log("%s: warning: total transformation is not renderable:\n",
+ __func__);
+ log_print_matrix(total);
+
+ matrix_type_str(surface, typestr, sizeof typestr);
+ weston_log_continue("surface matrix type: %s\n", typestr);
+ matrix_type_str(output, typestr, sizeof typestr);
+ weston_log_continue("output matrix type: %s\n", typestr);
+}
+
+/*#define SURFACE_TRANSFORM */
+
+static int
+rpir_surface_compute_rects(struct rpir_surface *surface,
+ VC_RECT_T *src_rect, VC_RECT_T *dst_rect,
+ VC_IMAGE_TRANSFORM_T *flipmask)
+{
+ struct weston_output *output_base = surface->surface->output;
+ struct rpir_output *output = to_rpir_output(output_base);
+ struct weston_matrix matrix = surface->surface->transform.matrix;
+ VC_IMAGE_TRANSFORM_T flipt = 0;
+ int src_x, src_y;
+ int dst_x, dst_y;
+ int src_width, src_height;
+ int dst_width, dst_height;
+ struct weston_vector p1 = {{ 0.0f, 0.0f, 0.0f, 1.0f }};
+ struct weston_vector p2 = {{ 0.0f, 0.0f, 0.0f, 1.0f }};
+ int t;
+ int over;
+
+ /* XXX: take buffer transform into account */
+
+ /* src is in 16.16, dst is in 32.0 fixed point.
+ * Negative values are not allowed in VC_RECT_T.
+ * Clip size to output boundaries, firmware ignores
+ * huge elements like 8192x8192.
+ */
+
+ src_x = 0 << 16;
+ src_y = 0 << 16;
+
+ if (surface->buffer_type == BUFFER_TYPE_EGL) {
+ struct weston_buffer *buffer = surface->egl_front->buffer_ref.buffer;
+
+ src_width = buffer->width << 16;
+ src_height = buffer->height << 16;
+ } else {
+ src_width = surface->front->width << 16;
+ src_height = surface->front->height << 16;
+ }
+
+ weston_matrix_multiply(&matrix, &output->matrix);
+
+#ifdef SURFACE_TRANSFORM
+ if (matrix.type >= WESTON_MATRIX_TRANSFORM_OTHER) {
+#else
+ if (matrix.type >= WESTON_MATRIX_TRANSFORM_ROTATE) {
+#endif
+ warn_bad_matrix(&matrix, &output->matrix,
+ &surface->surface->transform.matrix);
+ } else {
+ if (matrix.type & WESTON_MATRIX_TRANSFORM_ROTATE) {
+ if (fabsf(matrix.d[0]) < 1e-4f &&
+ fabsf(matrix.d[5]) < 1e-4f) {
+ flipt |= TRANSFORM_TRANSPOSE;
+ } else if (fabsf(matrix.d[1]) < 1e-4 &&
+ fabsf(matrix.d[4]) < 1e-4) {
+ /* no transpose */
+ } else {
+ warn_bad_matrix(&matrix, &output->matrix,
+ &surface->surface->transform.matrix);
+ }
+ }
+ }
+
+ p2.f[0] = surface->surface->geometry.width;
+ p2.f[1] = surface->surface->geometry.height;
+
+ /* transform top-left and bot-right corner into screen coordinates */
+ weston_matrix_transform(&matrix, &p1);
+ weston_matrix_transform(&matrix, &p2);
+
+ /* Compute the destination rectangle on screen, converting
+ * negative dimensions to flips.
+ */
+
+ dst_width = round(p2.f[0] - p1.f[0]);
+ if (dst_width < 0) {
+ dst_x = round(p2.f[0]);
+ dst_width = -dst_width;
+
+ if (!(flipt & TRANSFORM_TRANSPOSE))
+ flipt |= TRANSFORM_HFLIP;
+ else
+ flipt |= TRANSFORM_VFLIP;
+ } else {
+ dst_x = round(p1.f[0]);
+ }
+
+ dst_height = round(p2.f[1] - p1.f[1]);
+ if (dst_height < 0) {
+ dst_y = round(p2.f[1]);
+ dst_height = -dst_height;
+
+ if (!(flipt & TRANSFORM_TRANSPOSE))
+ flipt |= TRANSFORM_VFLIP;
+ else
+ flipt |= TRANSFORM_HFLIP;
+ } else {
+ dst_y = round(p1.f[1]);
+ }
+
+ if (dst_width == 0 || dst_height == 0) {
+ DBG("ignored, zero surface area before clipping\n");
+ return -1;
+ }
+
+#ifdef SURFACE_TRANSFORM
+ /* Dispmanx works as if you flipped the whole screen, when
+ * you flip an element. But, we want to flip an element in place.
+ * XXX: fixme
+ */
+ if (flipt & TRANSFORM_HFLIP)
+ dst_x = output_base->width - dst_x;
+ if (flipt & TRANSFORM_VFLIP)
+ dst_y = output_base->height - dst_y;
+ if (flipt & TRANSFORM_TRANSPOSE) {
+ int_swap(&dst_x, &dst_y);
+ int_swap(&dst_width, &dst_height);
+ }
+#else
+ switch (output_base->transform) {
+ case WL_OUTPUT_TRANSFORM_FLIPPED:
+ flipt = TRANSFORM_HFLIP;
+ break;
+ case WL_OUTPUT_TRANSFORM_NORMAL:
+ flipt = 0;
+ break;
+
+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+ flipt = TRANSFORM_HFLIP | TRANSFORM_VFLIP | TRANSFORM_TRANSPOSE;
+ break;
+ case WL_OUTPUT_TRANSFORM_90:
+ flipt = TRANSFORM_VFLIP | TRANSFORM_TRANSPOSE;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+ flipt = TRANSFORM_VFLIP;
+ break;
+ case WL_OUTPUT_TRANSFORM_180:
+ flipt = TRANSFORM_HFLIP | TRANSFORM_VFLIP;
+ break;
+
+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+ flipt = TRANSFORM_TRANSPOSE;
+ break;
+ case WL_OUTPUT_TRANSFORM_270:
+ flipt = TRANSFORM_HFLIP | TRANSFORM_TRANSPOSE;
+ break;
+ default:
+ break;
+ }
+#endif
+
+ /* clip destination rectangle to screen dimensions */
+
+ if (dst_x < 0) {
+ t = (int64_t)dst_x * src_width / dst_width;
+ src_width += t;
+ dst_width += dst_x;
+ src_x -= t;
+ dst_x = 0;
+ }
+
+ if (dst_y < 0) {
+ t = (int64_t)dst_y * src_height / dst_height;
+ src_height += t;
+ dst_height += dst_y;
+ src_y -= t;
+ dst_y = 0;
+ }
+
+ over = dst_x + dst_width - output_base->width;
+ if (over > 0) {
+ t = (int64_t)over * src_width / dst_width;
+ src_width -= t;
+ dst_width -= over;
+ }
+
+ over = dst_y + dst_height - output_base->height;
+ if (over > 0) {
+ t = (int64_t)over * src_height / dst_height;
+ src_height -= t;
+ dst_height -= over;
+ }
+
+ src_width = int_max(src_width, 0);
+ src_height = int_max(src_height, 0);
+
+ DBG("rpir_surface %p %dx%d: p1 %f, %f; p2 %f, %f\n", surface,
+ surface->surface->geometry.width,
+ surface->surface->geometry.height,
+ p1.f[0], p1.f[1], p2.f[0], p2.f[1]);
+ DBG("src rect %d;%d, %d;%d, %d;%dx%d;%d\n",
+ src_x >> 16, src_x & 0xffff,
+ src_y >> 16, src_y & 0xffff,
+ src_width >> 16, src_width & 0xffff,
+ src_height >> 16, src_height & 0xffff);
+ DBG("dest rect %d, %d, %dx%d%s%s%s\n",
+ dst_x, dst_y, dst_width, dst_height,
+ (flipt & TRANSFORM_HFLIP) ? " hflip" : "",
+ (flipt & TRANSFORM_VFLIP) ? " vflip" : "",
+ (flipt & TRANSFORM_TRANSPOSE) ? " transp" : "");
+
+ assert(src_x >= 0);
+ assert(src_y >= 0);
+ assert(dst_x >= 0);
+ assert(dst_y >= 0);
+
+ if (dst_width < 1 || dst_height < 1) {
+ DBG("ignored, zero surface area after clipping\n");
+ return -1;
+ }
+
+ /* EGL buffers will be upside-down related to what DispmanX expects */
+ if (surface->buffer_type == BUFFER_TYPE_EGL)
+ flipt ^= TRANSFORM_VFLIP;
+
+ vc_dispmanx_rect_set(src_rect, src_x, src_y, src_width, src_height);
+ vc_dispmanx_rect_set(dst_rect, dst_x, dst_y, dst_width, dst_height);
+ *flipmask = flipt;
+
+ return 0;
+}
+
+static DISPMANX_TRANSFORM_T
+vc_image2dispmanx_transform(VC_IMAGE_TRANSFORM_T t)
+{
+ /* XXX: uhh, are these right? */
+ switch (t) {
+ case VC_IMAGE_ROT0:
+ return DISPMANX_NO_ROTATE;
+ case VC_IMAGE_MIRROR_ROT0:
+ return DISPMANX_FLIP_HRIZ;
+ case VC_IMAGE_MIRROR_ROT180:
+ return DISPMANX_FLIP_VERT;
+ case VC_IMAGE_ROT180:
+ return DISPMANX_ROTATE_180;
+ case VC_IMAGE_MIRROR_ROT90:
+ return DISPMANX_ROTATE_90 | DISPMANX_FLIP_HRIZ;
+ case VC_IMAGE_ROT270:
+ return DISPMANX_ROTATE_270;
+ case VC_IMAGE_ROT90:
+ return DISPMANX_ROTATE_90;
+ case VC_IMAGE_MIRROR_ROT270:
+ return DISPMANX_ROTATE_270 | DISPMANX_FLIP_VERT;
+ default:
+ assert(0 && "bad VC_IMAGE_TRANSFORM_T");
+ return DISPMANX_NO_ROTATE;
+ }
+}
+
+
+static DISPMANX_RESOURCE_HANDLE_T
+rpir_surface_get_resource(struct rpir_surface *surface)
+{
+ switch (surface->buffer_type) {
+ case BUFFER_TYPE_SHM:
+ case BUFFER_TYPE_NULL:
+ return surface->front->handle;
+ case BUFFER_TYPE_EGL:
+ if (surface->egl_front != NULL)
+ return surface->egl_front->resource_handle;
+ default:
+ return DISPMANX_NO_HANDLE;
+ }
+}
+
+static int
+rpir_surface_dmx_add(struct rpir_surface *surface, struct rpir_output *output,
+ DISPMANX_UPDATE_HANDLE_T update, int layer)
+{
+ /* Do not use DISPMANX_FLAGS_ALPHA_PREMULT here.
+ * If you define PREMULT and ALPHA_MIX, the hardware will not
+ * multiply the source color with the element alpha, leading to
+ * bad colors. Instead, we define PREMULT during pixel data upload.
+ */
+ VC_DISPMANX_ALPHA_T alphasetup = {
+ DISPMANX_FLAGS_ALPHA_FROM_SOURCE |
+ DISPMANX_FLAGS_ALPHA_MIX,
+ float2uint8(surface->surface->alpha), /* opacity 0-255 */
+ 0 /* mask resource handle */
+ };
+ VC_RECT_T dst_rect;
+ VC_RECT_T src_rect;
+ VC_IMAGE_TRANSFORM_T flipmask;
+ int ret;
+ DISPMANX_RESOURCE_HANDLE_T resource_handle;
+
+ resource_handle = rpir_surface_get_resource(surface);
+ if (resource_handle == DISPMANX_NO_HANDLE) {
+ weston_log("%s: no buffer yet, aborting\n", __func__);
+ return 0;
+ }
+
+ ret = rpir_surface_compute_rects(surface, &src_rect, &dst_rect,
+ &flipmask);
+ if (ret < 0)
+ return 0;
+
+ surface->handle = vc_dispmanx_element_add(
+ update,
+ output->display,
+ layer,
+ &dst_rect,
+ resource_handle,
+ &src_rect,
+ DISPMANX_PROTECTION_NONE,
+ &alphasetup,
+ NULL /* clamp */,
+ vc_image2dispmanx_transform(flipmask));
+ DBG("rpir_surface %p add %u, alpha %f resource %d\n", surface,
+ surface->handle, surface->surface->alpha, resource_handle);
+
+ return 1;
+}
+
+static void
+rpir_surface_dmx_swap(struct rpir_surface *surface,
+ DISPMANX_UPDATE_HANDLE_T update)
+{
+ VC_RECT_T rect;
+ pixman_box32_t *r;
+
+ /* XXX: skip, iff resource was not reallocated, and single-buffering */
+ vc_dispmanx_element_change_source(update, surface->handle,
+ surface->front->handle);
+
+ /* This is current damage now, after rpir_surface_damage() */
+ r = pixman_region32_extents(&surface->prev_damage);
+
+ vc_dispmanx_rect_set(&rect, r->x1, r->y1,
+ r->x2 - r->x1, r->y2 - r->y1);
+ vc_dispmanx_element_modified(update, surface->handle, &rect);
+ DBG("rpir_surface %p swap\n", surface);
+}
+
+static int
+rpir_surface_dmx_move(struct rpir_surface *surface,
+ DISPMANX_UPDATE_HANDLE_T update, int layer)
+{
+ uint8_t alpha = float2uint8(surface->surface->alpha);
+ VC_RECT_T dst_rect;
+ VC_RECT_T src_rect;
+ VC_IMAGE_TRANSFORM_T flipmask;
+ int ret;
+
+ /* XXX: return early, if all attributes stay the same */
+
+ if (surface->buffer_type == BUFFER_TYPE_EGL) {
+ DISPMANX_RESOURCE_HANDLE_T resource_handle;
+
+ resource_handle = rpir_surface_get_resource(surface);
+ if (resource_handle == DISPMANX_NO_HANDLE) {
+ weston_log("%s: no buffer yet, aborting\n", __func__);
+ return 0;
+ }
+
+ vc_dispmanx_element_change_source(update,
+ surface->handle,
+ resource_handle);
+ }
+
+ ret = rpir_surface_compute_rects(surface, &src_rect, &dst_rect,
+ &flipmask);
+ if (ret < 0)
+ return 0;
+
+ ret = vc_dispmanx_element_change_attributes(
+ update,
+ surface->handle,
+ ELEMENT_CHANGE_LAYER |
+ ELEMENT_CHANGE_OPACITY |
+ ELEMENT_CHANGE_TRANSFORM |
+ ELEMENT_CHANGE_DEST_RECT |
+ ELEMENT_CHANGE_SRC_RECT,
+ layer,
+ alpha,
+ &dst_rect,
+ &src_rect,
+ DISPMANX_NO_HANDLE,
+ /* This really is DISPMANX_TRANSFORM_T, no matter
+ * what the header says. */
+ vc_image2dispmanx_transform(flipmask));
+ DBG("rpir_surface %p move\n", surface);
+
+ if (ret)
+ return -1;
+
+ return 1;
+}
+
+static void
+rpir_surface_dmx_remove(struct rpir_surface *surface,
+ DISPMANX_UPDATE_HANDLE_T update)
+{
+ if (surface->handle == DISPMANX_NO_HANDLE)
+ return;
+
+ vc_dispmanx_element_remove(update, surface->handle);
+ DBG("rpir_surface %p remove %u\n", surface, surface->handle);
+ surface->handle = DISPMANX_NO_HANDLE;
+}
+
+static void
+rpir_surface_swap_pointers(struct rpir_surface *surface)
+{
+ struct rpi_resource *tmp;
+
+ tmp = surface->front;
+ surface->front = surface->back;
+ surface->back = tmp;
+ surface->need_swap = 0;
+ DBG("new back %p, new front %p\n", surface->back, surface->front);
+}
+
+static int
+is_surface_not_visible(struct weston_surface *surface)
+{
+ /* Return true, if surface is guaranteed to be totally obscured. */
+ int ret;
+ pixman_region32_t unocc;
+
+ pixman_region32_init(&unocc);
+ pixman_region32_subtract(&unocc, &surface->transform.boundingbox,
+ &surface->clip);
+ ret = !pixman_region32_not_empty(&unocc);
+ pixman_region32_fini(&unocc);
+
+ return ret;
+}
+
+static void
+rpir_surface_update(struct rpir_surface *surface, struct rpir_output *output,
+ DISPMANX_UPDATE_HANDLE_T update, int layer)
+{
+ int need_swap = surface->need_swap;
+ int ret;
+ int obscured;
+
+ if (surface->buffer_type == BUFFER_TYPE_EGL) {
+ if (surface->egl_back != NULL) {
+ assert(surface->egl_old_front == NULL);
+ surface->egl_old_front = surface->egl_front;
+ surface->egl_front = surface->egl_back;
+ surface->egl_back = NULL;
+ }
+ if (surface->egl_front->buffer_ref.buffer == NULL) {
+ weston_log("warning: client unreffed current front buffer\n");
+
+ wl_list_remove(&surface->link);
+ if (surface->handle == DISPMANX_NO_HANDLE) {
+ wl_list_init(&surface->link);
+ } else {
+ rpir_surface_dmx_remove(surface, update);
+ wl_list_insert(&output->surface_cleanup_list,
+ &surface->link);
+ }
+
+ goto out;
+ }
+ } else {
+ if (need_swap)
+ rpir_surface_swap_pointers(surface);
+ }
+
+ obscured = is_surface_not_visible(surface->surface);
+ if (obscured) {
+ DBG("rpir_surface %p totally obscured.\n", surface);
+
+ wl_list_remove(&surface->link);
+ if (surface->handle == DISPMANX_NO_HANDLE) {
+ wl_list_init(&surface->link);
+ } else {
+ rpir_surface_dmx_remove(surface, update);
+ wl_list_insert(&output->surface_cleanup_list,
+ &surface->link);
+ }
+
+ goto out;
+ }
+
+ if (surface->handle == DISPMANX_NO_HANDLE) {
+ ret = rpir_surface_dmx_add(surface, output, update, layer);
+ if (ret == 0) {
+ wl_list_remove(&surface->link);
+ wl_list_init(&surface->link);
+ } else if (ret < 0) {
+ weston_log("ERROR rpir_surface_dmx_add() failed.\n");
+ }
+ } else {
+ if (need_swap)
+ rpir_surface_dmx_swap(surface, update);
+
+ ret = rpir_surface_dmx_move(surface, update, layer);
+ if (ret == 0) {
+ rpir_surface_dmx_remove(surface, update);
+
+ wl_list_remove(&surface->link);
+ wl_list_insert(&output->surface_cleanup_list,
+ &surface->link);
+ } else if (ret < 0) {
+ weston_log("ERROR rpir_surface_dmx_move() failed.\n");
+ }
+ }
+
+out:
+ surface->layer = layer;
+}
+
+static int
+rpi_renderer_read_pixels(struct weston_output *base,
+ pixman_format_code_t format, void *pixels,
+ uint32_t x, uint32_t y,
+ uint32_t width, uint32_t height)
+{
+ struct rpir_output *output = to_rpir_output(base);
+ struct rpi_resource *buffer = &output->capture_buffer;
+ VC_RECT_T rect;
+ uint32_t fb_width, fb_height;
+ uint32_t dst_pitch;
+ uint32_t i;
+ int ret;
+
+ fb_width = base->current_mode->width;
+ fb_height = base->current_mode->height;
+
+ DBG("%s(%u, %u, %u, %u), resource %p\n", __func__,
+ x, y, width, height, buffer);
+
+ if (format != PIXMAN_a8r8g8b8) {
+ weston_log("rpi-renderer error: bad read_format\n");
+ return -1;
+ }
+
+ dst_pitch = fb_width * 4;
+
+ if (buffer->handle == DISPMANX_NO_HANDLE) {
+ free(output->capture_data);
+ output->capture_data = NULL;
+
+ ret = rpi_resource_realloc(buffer, VC_IMAGE_ARGB8888,
+ fb_width, fb_height,
+ dst_pitch, fb_height);
+ if (ret < 0) {
+ weston_log("rpi-renderer error: "
+ "allocating read buffer failed\n");
+ return -1;
+ }
+
+ ret = vc_dispmanx_snapshot(output->display, buffer->handle,
+ VC_IMAGE_ROT0);
+ if (ret) {
+ weston_log("rpi-renderer error: "
+ "vc_dispmanx_snapshot returned %d\n", ret);
+ return -1;
+ }
+ DBG("%s: snapshot done.\n", __func__);
+ }
+
+ /*
+ * If vc_dispmanx_resource_read_data was able to read sub-rectangles,
+ * we could read directly into 'pixels'. But it cannot, it does not
+ * use rect.x or rect.width, and does this:
+ * host_start = (uint8_t *)dst_address + (dst_pitch * p_rect->y);
+ * In other words, it is only good for reading the full buffer in
+ * one go.
+ */
+ vc_dispmanx_rect_set(&rect, 0, 0, fb_width, fb_height);
+
+ if (x == 0 && y == 0 && width == fb_width && height == fb_height) {
+ ret = vc_dispmanx_resource_read_data(buffer->handle, &rect,
+ pixels, dst_pitch);
+ if (ret) {
+ weston_log("rpi-renderer error: "
+ "resource_read_data returned %d\n", ret);
+ return -1;
+ }
+ DBG("%s: full frame done.\n", __func__);
+ return 0;
+ }
+
+ if (!output->capture_data) {
+ output->capture_data = malloc(fb_height * dst_pitch);
+ if (!output->capture_data) {
+ weston_log("rpi-renderer error: "
+ "out of memory\n");
+ return -1;
+ }
+
+ ret = vc_dispmanx_resource_read_data(buffer->handle, &rect,
+ output->capture_data,
+ dst_pitch);
+ if (ret) {
+ weston_log("rpi-renderer error: "
+ "resource_read_data returned %d\n", ret);
+ return -1;
+ }
+ }
+
+ for (i = 0; i < height; i++) {
+ uint8_t *src = output->capture_data +
+ (y + i) * dst_pitch + x * 4;
+ uint8_t *dst = (uint8_t *)pixels + i * width * 4;
+ memcpy(dst, src, width * 4);
+ }
+
+ return 0;
+}
+
+static void
+rpir_output_dmx_remove_all(struct rpir_output *output,
+ DISPMANX_UPDATE_HANDLE_T update)
+{
+ struct rpir_surface *surface;
+
+ while (!wl_list_empty(&output->surface_list)) {
+ surface = container_of(output->surface_list.next,
+ struct rpir_surface, link);
+ rpir_surface_dmx_remove(surface, update);
+
+ wl_list_remove(&surface->link);
+ wl_list_insert(&output->surface_cleanup_list, &surface->link);
+ }
+}
+
+static void
+output_compute_matrix(struct weston_output *base)
+{
+ struct rpir_output *output = to_rpir_output(base);
+ struct weston_matrix *matrix = &output->matrix;
+ const float half_w = 0.5f * base->width;
+ const float half_h = 0.5f * base->height;
+ float mag;
+ float dx, dy;
+
+ weston_matrix_init(matrix);
+ weston_matrix_translate(matrix, -base->x, -base->y, 0.0f);
+
+#ifdef SURFACE_TRANSFORM
+ weston_matrix_translate(matrix, -half_w, -half_h, 0.0f);
+ switch (base->transform) {
+ case WL_OUTPUT_TRANSFORM_FLIPPED:
+ weston_matrix_scale(matrix, -1.0f, 1.0f, 1.0f);
+ case WL_OUTPUT_TRANSFORM_NORMAL:
+ /* weston_matrix_rotate_xy(matrix, 1.0f, 0.0f); no-op */
+ weston_matrix_translate(matrix, half_w, half_h, 0.0f);
+ break;
+
+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+ weston_matrix_scale(matrix, -1.0f, 1.0f, 1.0f);
+ case WL_OUTPUT_TRANSFORM_90:
+ weston_matrix_rotate_xy(matrix, 0.0f, 1.0f);
+ weston_matrix_translate(matrix, half_h, half_w, 0.0f);
+ break;
+
+ case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+ weston_matrix_scale(matrix, -1.0f, 1.0f, 1.0f);
+ case WL_OUTPUT_TRANSFORM_180:
+ weston_matrix_rotate_xy(matrix, -1.0f, 0.0f);
+ weston_matrix_translate(matrix, half_w, half_h, 0.0f);
+ break;
+
+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+ weston_matrix_scale(matrix, -1.0f, 1.0f, 1.0f);
+ case WL_OUTPUT_TRANSFORM_270:
+ weston_matrix_rotate_xy(matrix, 0.0f, -1.0f);
+ weston_matrix_translate(matrix, half_h, half_w, 0.0f);
+ break;
+
+ default:
+ break;
+ }
+#endif
+
+ if (base->zoom.active) {
+ /* The base->zoom stuff is in GL coordinate system */
+ mag = 1.0f / (1.0f - base->zoom.spring_z.current);
+ dx = -(base->zoom.trans_x + 1.0f) * half_w;
+ dy = -(base->zoom.trans_y + 1.0f) * half_h;
+ weston_matrix_translate(matrix, dx, dy, 0.0f);
+ weston_matrix_scale(matrix, mag, mag, 1.0f);
+ weston_matrix_translate(matrix, half_w, half_h, 0.0f);
+ }
+}
+
+/* Note: this won't work right for multiple outputs. A DispmanX Element
+ * is tied to one DispmanX Display, i.e. output.
+ */
+static void
+rpi_renderer_repaint_output(struct weston_output *base,
+ pixman_region32_t *output_damage)
+{
+ struct weston_compositor *compositor = base->compositor;
+ struct rpir_output *output = to_rpir_output(base);
+ struct weston_surface *ws;
+ struct rpir_surface *surface;
+ struct wl_list done_list;
+ int layer = 1;
+
+ assert(output->update != DISPMANX_NO_HANDLE);
+
+ output_compute_matrix(base);
+
+ rpi_resource_release(&output->capture_buffer);
+ free(output->capture_data);
+ output->capture_data = NULL;
+
+ /* update all renderable surfaces */
+ wl_list_init(&done_list);
+ wl_list_for_each_reverse(ws, &compositor->surface_list, link) {
+ if (ws->plane != &compositor->primary_plane)
+ continue;
+
+ surface = to_rpir_surface(ws);
+ assert(!wl_list_empty(&surface->link) ||
+ surface->handle == DISPMANX_NO_HANDLE);
+
+ wl_list_remove(&surface->link);
+ wl_list_insert(&done_list, &surface->link);
+ rpir_surface_update(surface, output, output->update, layer++);
+ }
+
+ /* Remove all surfaces that are still on screen, but were
+ * not rendered this time.
+ */
+ rpir_output_dmx_remove_all(output, output->update);
+
+ wl_list_insert_list(&output->surface_list, &done_list);
+ output->update = DISPMANX_NO_HANDLE;
+
+ /* The frame_signal is emitted in rpi_renderer_finish_frame(),
+ * so that the firmware can capture the up-to-date contents.
+ */
+}
+
+static void
+rpi_renderer_flush_damage(struct weston_surface *base)
+{
+ /* Called for every surface just before repainting it, if
+ * having an shm buffer.
+ */
+ struct rpir_surface *surface = to_rpir_surface(base);
+ struct weston_buffer *buffer = surface->buffer_ref.buffer;
+ int ret;
+
+ assert(buffer);
+ assert(wl_shm_buffer_get(buffer->resource));
+
+ ret = rpir_surface_damage(surface, buffer, &base->damage);
+ if (ret)
+ weston_log("%s error: updating Dispmanx resource failed.\n",
+ __func__);
+
+ weston_buffer_reference(&surface->buffer_ref, NULL);
+}
+
+static void
+rpi_renderer_attach(struct weston_surface *base, struct weston_buffer *buffer)
+{
+ /* Called every time a client commits an attach. */
+ struct rpir_surface *surface = to_rpir_surface(base);
+
+ assert(surface);
+ if (!surface)
+ return;
+
+ if (surface->buffer_type == BUFFER_TYPE_SHM) {
+ if (!surface->single_buffer)
+ /* XXX: need to check if in middle of update */
+ rpi_resource_release(surface->back);
+
+ if (surface->handle == DISPMANX_NO_HANDLE)
+ /* XXX: cannot do this, if middle of an update */
+ rpi_resource_release(surface->front);
+
+ weston_buffer_reference(&surface->buffer_ref, NULL);
+ }
+
+ /* If buffer is NULL, Weston core unmaps the surface, the surface
+ * will not appear in repaint list, and so rpi_renderer_repaint_output
+ * will remove the DispmanX element. Later, for SHM, also the front
+ * buffer will be released in the cleanup_list processing.
+ */
+ if (!buffer)
+ return;
+
+ if (wl_shm_buffer_get(buffer->resource)) {
+ surface->buffer_type = BUFFER_TYPE_SHM;
+ buffer->shm_buffer = wl_shm_buffer_get(buffer->resource);
+ buffer->width = wl_shm_buffer_get_width(buffer->shm_buffer);
+ buffer->height = wl_shm_buffer_get_height(buffer->shm_buffer);
+
+ weston_buffer_reference(&surface->buffer_ref, buffer);
+ } else {
+#if ENABLE_EGL
+ struct rpi_renderer *renderer = to_rpi_renderer(base->compositor);
+ struct wl_resource *wl_resource = buffer->resource;
+
+ if (!renderer->has_bind_display ||
+ !renderer->query_buffer(renderer->egl_display,
+ wl_resource,
+ EGL_WIDTH, &buffer->width)) {
+ weston_log("unhandled buffer type!\n");
+ weston_buffer_reference(&surface->buffer_ref, NULL);
+ surface->buffer_type = BUFFER_TYPE_NULL;
+ }
+
+ renderer->query_buffer(renderer->egl_display,
+ wl_resource,
+ EGL_HEIGHT, &buffer->height);
+
+ surface->buffer_type = BUFFER_TYPE_EGL;
+
+ if(surface->egl_back == NULL)
+ surface->egl_back = calloc(1, sizeof *surface->egl_back);
+
+ weston_buffer_reference(&surface->egl_back->buffer_ref, buffer);
+ surface->egl_back->resource_handle =
+ vc_dispmanx_get_handle_from_wl_buffer(wl_resource);
+#else
+ weston_log("unhandled buffer type!\n");
+ weston_buffer_reference(&surface->buffer_ref, NULL);
+ surface->buffer_type = BUFFER_TYPE_NULL;
+#endif
+ }
+}
+
+static int
+rpi_renderer_create_surface(struct weston_surface *base)
+{
+ struct rpi_renderer *renderer = to_rpi_renderer(base->compositor);
+ struct rpir_surface *surface;
+
+ assert(base->renderer_state == NULL);
+
+ surface = rpir_surface_create(renderer);
+ if (!surface)
+ return -1;
+
+ surface->surface = base;
+ base->renderer_state = surface;
+ return 0;
+}
+
+static void
+rpi_renderer_surface_set_color(struct weston_surface *base,
+ float red, float green, float blue, float alpha)
+{
+ struct rpir_surface *surface = to_rpir_surface(base);
+ uint8_t color[4];
+ VC_RECT_T rect;
+ int ret;
+
+ assert(surface);
+
+ ret = rpi_resource_realloc(surface->back, VC_IMAGE_ARGB8888,
+ 1, 1, 4, 1);
+ if (ret < 0) {
+ weston_log("Error: %s: rpi_resource_realloc failed.\n",
+ __func__);
+ return;
+ }
+
+ color[0] = float2uint8(blue);
+ color[1] = float2uint8(green);
+ color[2] = float2uint8(red);
+ color[3] = float2uint8(alpha);
+
+ vc_dispmanx_rect_set(&rect, 0, 0, 1, 1);
+ ret = vc_dispmanx_resource_write_data(surface->back->handle,
+ VC_IMAGE_ARGB8888,
+ 4, color, &rect);
+ if (ret) {
+ weston_log("Error: %s: resource_write_data failed.\n",
+ __func__);
+ return;
+ }
+
+ DBG("%s: resource %p solid color BGRA %u,%u,%u,%u\n", __func__,
+ surface->back, color[0], color[1], color[2], color[3]);
+
+ /*pixman_region32_copy(&surface->prev_damage, damage);*/
+ surface->need_swap = 1;
+}
+
+static void
+rpi_renderer_destroy_surface(struct weston_surface *base)
+{
+ struct rpir_surface *surface = to_rpir_surface(base);
+
+ assert(surface);
+ assert(surface->surface == base);
+ if (!surface)
+ return;
+
+ surface->surface = NULL;
+ base->renderer_state = NULL;
+
+ /* If guaranteed to not be on screen, just detroy it. */
+ if (wl_list_empty(&surface->link))
+ rpir_surface_destroy(surface);
+
+ /* Otherwise, the surface is either on screen and needs
+ * to be removed by a repaint update, or it is in the
+ * surface_cleanup_list, and will be destroyed by
+ * rpi_renderer_finish_frame().
+ */
+}
+
+static void
+rpi_renderer_destroy(struct weston_compositor *compositor)
+{
+ struct rpi_renderer *renderer = to_rpi_renderer(compositor);
+
+#if ENABLE_EGL
+ if (renderer->has_bind_display)
+ renderer->unbind_display(renderer->egl_display,
+ compositor->wl_display);
+#endif
+
+ free(renderer);
+ compositor->renderer = NULL;
+}
+
+WL_EXPORT int
+rpi_renderer_create(struct weston_compositor *compositor,
+ const struct rpi_renderer_parameters *params)
+{
+ struct rpi_renderer *renderer;
+#if ENABLE_EGL
+ const char *extensions;
+ EGLBoolean ret;
+ EGLint major, minor;
+#endif
+
+ weston_log("Initializing the DispmanX compositing renderer\n");
+
+ renderer = calloc(1, sizeof *renderer);
+ if (renderer == NULL)
+ return -1;
+
+ renderer->single_buffer = params->single_buffer;
+
+ renderer->base.read_pixels = rpi_renderer_read_pixels;
+ renderer->base.repaint_output = rpi_renderer_repaint_output;
+ renderer->base.flush_damage = rpi_renderer_flush_damage;
+ renderer->base.attach = rpi_renderer_attach;
+ renderer->base.create_surface = rpi_renderer_create_surface;
+ renderer->base.surface_set_color = rpi_renderer_surface_set_color;
+ renderer->base.destroy_surface = rpi_renderer_destroy_surface;
+ renderer->base.destroy = rpi_renderer_destroy;
+
+#ifdef ENABLE_EGL
+ renderer->egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ if (renderer->egl_display == EGL_NO_DISPLAY) {
+ weston_log("failed to create EGL display\n");
+ return -1;
+ }
+
+ if (!eglInitialize(renderer->egl_display, &major, &minor)) {
+ weston_log("failed to initialize EGL display\n");
+ return -1;
+ }
+
+ renderer->bind_display =
+ (void *) eglGetProcAddress("eglBindWaylandDisplayWL");
+ renderer->unbind_display =
+ (void *) eglGetProcAddress("eglUnbindWaylandDisplayWL");
+ renderer->query_buffer =
+ (void *) eglGetProcAddress("eglQueryWaylandBufferWL");
+
+ extensions = (const char *) eglQueryString(renderer->egl_display,
+ EGL_EXTENSIONS);
+ if (!extensions) {
+ weston_log("Retrieving EGL extension string failed.\n");
+ return -1;
+ }
+
+ if (strstr(extensions, "EGL_WL_bind_wayland_display"))
+ renderer->has_bind_display = 1;
+
+ if (renderer->has_bind_display) {
+ ret = renderer->bind_display(renderer->egl_display,
+ compositor->wl_display);
+ if (!ret)
+ renderer->has_bind_display = 0;
+ }
+#endif
+
+ compositor->renderer = &renderer->base;
+ compositor->read_format = PIXMAN_a8r8g8b8;
+ /* WESTON_CAP_ROTATION_ANY not supported */
+
+ wl_display_add_shm_format(compositor->wl_display, WL_SHM_FORMAT_RGB565);
+
+ return 0;
+}
+
+WL_EXPORT int
+rpi_renderer_output_create(struct weston_output *base,
+ DISPMANX_DISPLAY_HANDLE_T display)
+{
+ struct rpir_output *output;
+
+ assert(base->renderer_state == NULL);
+
+ output = calloc(1, sizeof *output);
+ if (!output)
+ return -1;
+
+ output->display = display;
+ output->update = DISPMANX_NO_HANDLE;
+ wl_list_init(&output->surface_list);
+ wl_list_init(&output->surface_cleanup_list);
+ rpi_resource_init(&output->capture_buffer);
+ base->renderer_state = output;
+
+ return 0;
+}
+
+WL_EXPORT void
+rpi_renderer_output_destroy(struct weston_output *base)
+{
+ struct rpir_output *output = to_rpir_output(base);
+ struct rpir_surface *surface;
+ DISPMANX_UPDATE_HANDLE_T update;
+
+ rpi_resource_release(&output->capture_buffer);
+ free(output->capture_data);
+ output->capture_data = NULL;
+
+ update = vc_dispmanx_update_start(0);
+ rpir_output_dmx_remove_all(output, update);
+ vc_dispmanx_update_submit_sync(update);
+
+ while (!wl_list_empty(&output->surface_cleanup_list)) {
+ surface = container_of(output->surface_cleanup_list.next,
+ struct rpir_surface, link);
+ if (surface->surface)
+ surface->surface->renderer_state = NULL;
+ rpir_surface_destroy(surface);
+ }
+
+ free(output);
+ base->renderer_state = NULL;
+}
+
+WL_EXPORT void
+rpi_renderer_set_update_handle(struct weston_output *base,
+ DISPMANX_UPDATE_HANDLE_T handle)
+{
+ struct rpir_output *output = to_rpir_output(base);
+
+ output->update = handle;
+}
+
+WL_EXPORT void
+rpi_renderer_finish_frame(struct weston_output *base)
+{
+ struct rpir_output *output = to_rpir_output(base);
+ struct weston_compositor *compositor = base->compositor;
+ struct weston_surface *ws;
+ struct rpir_surface *surface;
+
+ while (!wl_list_empty(&output->surface_cleanup_list)) {
+ surface = container_of(output->surface_cleanup_list.next,
+ struct rpir_surface, link);
+
+ if (surface->surface) {
+ /* The weston_surface still exists, but is
+ * temporarily not visible, and hence its Element
+ * was removed. The current front buffer contents
+ * must be preserved.
+ */
+ if (!surface->single_buffer)
+ rpi_resource_release(surface->back);
+
+ wl_list_remove(&surface->link);
+ wl_list_init(&surface->link);
+ } else {
+ rpir_surface_destroy(surface);
+ }
+ }
+
+ wl_list_for_each(ws, &compositor->surface_list, link) {
+ surface = to_rpir_surface(ws);
+
+ if (surface->buffer_type != BUFFER_TYPE_EGL)
+ continue;
+
+ if(surface->egl_old_front == NULL)
+ continue;
+
+ weston_buffer_reference(&surface->egl_old_front->buffer_ref, NULL);
+ free(surface->egl_old_front);
+ surface->egl_old_front = NULL;
+ }
+
+ wl_signal_emit(&base->frame_signal, base);
+}
diff --git a/src/rpi-renderer.h b/src/rpi-renderer.h
new file mode 100644
index 00000000..28ae3039
--- /dev/null
+++ b/src/rpi-renderer.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright © 2013 Raspberry Pi Foundation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that 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.
+ */
+
+#ifndef RPI_RENDERER_H
+#define RPI_RENDERER_H
+
+struct rpi_renderer_parameters {
+ int single_buffer;
+};
+
+int
+rpi_renderer_create(struct weston_compositor *compositor,
+ const struct rpi_renderer_parameters *params);
+
+int
+rpi_renderer_output_create(struct weston_output *base,
+ DISPMANX_DISPLAY_HANDLE_T display);
+
+void
+rpi_renderer_output_destroy(struct weston_output *base);
+
+void
+rpi_renderer_set_update_handle(struct weston_output *base,
+ DISPMANX_UPDATE_HANDLE_T handle);
+
+void
+rpi_renderer_finish_frame(struct weston_output *base);
+
+#endif /* RPI_RENDERER_H */
diff --git a/src/screenshooter.c b/src/screenshooter.c
new file mode 100644
index 00000000..645114d0
--- /dev/null
+++ b/src/screenshooter.c
@@ -0,0 +1,591 @@
+/*
+ * Copyright © 2008-2011 Kristian Høgsberg
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that 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.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <linux/input.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/uio.h>
+
+#include "compositor.h"
+#include "screenshooter-server-protocol.h"
+
+#include "../wcap/wcap-decode.h"
+
+struct screenshooter {
+ struct weston_compositor *ec;
+ struct wl_global *global;
+ struct wl_client *client;
+ struct weston_process process;
+ struct wl_listener destroy_listener;
+};
+
+struct screenshooter_frame_listener {
+ struct wl_listener listener;
+ struct weston_buffer *buffer;
+ struct wl_resource *resource;
+};
+
+static void
+copy_bgra_yflip(uint8_t *dst, uint8_t *src, int height, int stride)
+{
+ uint8_t *end;
+
+ end = dst + height * stride;
+ while (dst < end) {
+ memcpy(dst, src, stride);
+ dst += stride;
+ src -= stride;
+ }
+}
+
+static void
+copy_bgra(uint8_t *dst, uint8_t *src, int height, int stride)
+{
+ /* TODO: optimize this out */
+ memcpy(dst, src, height * stride);
+}
+
+static void
+copy_row_swap_RB(void *vdst, void *vsrc, int bytes)
+{
+ uint32_t *dst = vdst;
+ uint32_t *src = vsrc;
+ uint32_t *end = dst + bytes / 4;
+
+ while (dst < end) {
+ uint32_t v = *src++;
+ /* A R G B */
+ uint32_t tmp = v & 0xff00ff00;
+ tmp |= (v >> 16) & 0x000000ff;
+ tmp |= (v << 16) & 0x00ff0000;
+ *dst++ = tmp;
+ }
+}
+
+static void
+copy_rgba_yflip(uint8_t *dst, uint8_t *src, int height, int stride)
+{
+ uint8_t *end;
+
+ end = dst + height * stride;
+ while (dst < end) {
+ copy_row_swap_RB(dst, src, stride);
+ dst += stride;
+ src -= stride;
+ }
+}
+
+static void
+copy_rgba(uint8_t *dst, uint8_t *src, int height, int stride)
+{
+ uint8_t *end;
+
+ end = dst + height * stride;
+ while (dst < end) {
+ copy_row_swap_RB(dst, src, stride);
+ dst += stride;
+ src += stride;
+ }
+}
+
+static void
+screenshooter_frame_notify(struct wl_listener *listener, void *data)
+{
+ struct screenshooter_frame_listener *l =
+ container_of(listener,
+ struct screenshooter_frame_listener, listener);
+ struct weston_output *output = data;
+ struct weston_compositor *compositor = output->compositor;
+ int32_t stride;
+ uint8_t *pixels, *d, *s;
+
+ output->disable_planes--;
+ wl_list_remove(&listener->link);
+ stride = l->buffer->width * (PIXMAN_FORMAT_BPP(compositor->read_format) / 8);
+ pixels = malloc(stride * l->buffer->height);
+
+ if (pixels == NULL) {
+ wl_resource_post_no_memory(l->resource);
+ free(l);
+ return;
+ }
+
+ compositor->renderer->read_pixels(output,
+ compositor->read_format, pixels,
+ 0, 0, output->current_mode->width,
+ output->current_mode->height);
+
+ stride = wl_shm_buffer_get_stride(l->buffer->shm_buffer);
+
+ d = wl_shm_buffer_get_data(l->buffer->shm_buffer);
+ s = pixels + stride * (l->buffer->height - 1);
+
+ switch (compositor->read_format) {
+ case PIXMAN_a8r8g8b8:
+ case PIXMAN_x8r8g8b8:
+ if (compositor->capabilities & WESTON_CAP_CAPTURE_YFLIP)
+ copy_bgra_yflip(d, s, output->current_mode->height, stride);
+ else
+ copy_bgra(d, pixels, output->current_mode->height, stride);
+ break;
+ case PIXMAN_x8b8g8r8:
+ case PIXMAN_a8b8g8r8:
+ if (compositor->capabilities & WESTON_CAP_CAPTURE_YFLIP)
+ copy_rgba_yflip(d, s, output->current_mode->height, stride);
+ else
+ copy_rgba(d, pixels, output->current_mode->height, stride);
+ break;
+ default:
+ break;
+ }
+
+ screenshooter_send_done(l->resource);
+ free(pixels);
+ free(l);
+}
+
+static void
+screenshooter_shoot(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *output_resource,
+ struct wl_resource *buffer_resource)
+{
+ struct weston_output *output =
+ wl_resource_get_user_data(output_resource);
+ struct screenshooter_frame_listener *l;
+ struct weston_buffer *buffer =
+ weston_buffer_from_resource(buffer_resource);
+
+ if (buffer == NULL) {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+ if (!wl_shm_buffer_get(buffer->resource))
+ return;
+
+ buffer->shm_buffer = wl_shm_buffer_get(buffer->resource);
+ buffer->width = wl_shm_buffer_get_width(buffer->shm_buffer);
+ buffer->height = wl_shm_buffer_get_height(buffer->shm_buffer);
+
+ if (buffer->width < output->current_mode->width ||
+ buffer->height < output->current_mode->height)
+ return;
+
+ l = malloc(sizeof *l);
+ if (l == NULL) {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ l->buffer = buffer;
+ l->resource = resource;
+
+ l->listener.notify = screenshooter_frame_notify;
+ wl_signal_add(&output->frame_signal, &l->listener);
+ output->disable_planes++;
+ weston_output_schedule_repaint(output);
+}
+
+struct screenshooter_interface screenshooter_implementation = {
+ screenshooter_shoot
+};
+
+static void
+bind_shooter(struct wl_client *client,
+ void *data, uint32_t version, uint32_t id)
+{
+ struct screenshooter *shooter = data;
+ struct wl_resource *resource;
+
+ resource = wl_resource_create(client,
+ &screenshooter_interface, 1, id);
+
+ if (client != shooter->client) {
+ wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
+ "screenshooter failed: permission denied");
+ wl_resource_destroy(resource);
+ }
+
+ wl_resource_set_implementation(resource, &screenshooter_implementation,
+ data, NULL);
+}
+
+static void
+screenshooter_sigchld(struct weston_process *process, int status)
+{
+ struct screenshooter *shooter =
+ container_of(process, struct screenshooter, process);
+
+ shooter->client = NULL;
+}
+
+static void
+screenshooter_binding(struct weston_seat *seat, uint32_t time, uint32_t key,
+ void *data)
+{
+ struct screenshooter *shooter = data;
+ const char *screenshooter_exe = LIBEXECDIR "/weston-screenshooter";
+
+ if (!shooter->client)
+ shooter->client = weston_client_launch(shooter->ec,
+ &shooter->process,
+ screenshooter_exe, screenshooter_sigchld);
+}
+
+struct weston_recorder {
+ struct weston_output *output;
+ uint32_t *frame, *rect;
+ uint32_t *tmpbuf;
+ uint32_t total;
+ int fd;
+ struct wl_listener frame_listener;
+ int count;
+};
+
+static uint32_t *
+output_run(uint32_t *p, uint32_t delta, int run)
+{
+ int i;
+
+ while (run > 0) {
+ if (run <= 0xe0) {
+ *p++ = delta | ((run - 1) << 24);
+ break;
+ }
+
+ i = 24 - __builtin_clz(run);
+ *p++ = delta | ((i + 0xe0) << 24);
+ run -= 1 << (7 + i);
+ }
+
+ return p;
+}
+
+static uint32_t
+component_delta(uint32_t next, uint32_t prev)
+{
+ unsigned char dr, dg, db;
+
+ dr = (next >> 16) - (prev >> 16);
+ dg = (next >> 8) - (prev >> 8);
+ db = (next >> 0) - (prev >> 0);
+
+ return (dr << 16) | (dg << 8) | (db << 0);
+}
+
+static void
+transform_rect(struct weston_output *output, pixman_box32_t *r)
+{
+ pixman_box32_t s = *r;
+
+ switch (output->transform) {
+ case WL_OUTPUT_TRANSFORM_FLIPPED:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+ s.x1 = output->width - r->x2;
+ s.x2 = output->width - r->x1;
+ break;
+ default:
+ break;
+ }
+
+ switch (output->transform) {
+ case WL_OUTPUT_TRANSFORM_NORMAL:
+ case WL_OUTPUT_TRANSFORM_FLIPPED:
+ r->x1 = s.x1;
+ r->x2 = s.x2;
+ break;
+ case WL_OUTPUT_TRANSFORM_90:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+ r->x1 = output->current_mode->width - s.y2;
+ r->y1 = s.x1;
+ r->x2 = output->current_mode->width - s.y1;
+ r->y2 = s.x2;
+ break;
+ case WL_OUTPUT_TRANSFORM_180:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+ r->x1 = output->current_mode->width - s.x2;
+ r->y1 = output->current_mode->height - s.y2;
+ r->x2 = output->current_mode->width - s.x1;
+ r->y2 = output->current_mode->height - s.y1;
+ break;
+ case WL_OUTPUT_TRANSFORM_270:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+ r->x1 = s.y1;
+ r->y1 = output->current_mode->height - s.x2;
+ r->x2 = s.y2;
+ r->y2 = output->current_mode->height - s.x1;
+ break;
+ default:
+ break;
+ }
+
+ r->x1 *= output->current_scale;
+ r->y1 *= output->current_scale;
+ r->x2 *= output->current_scale;
+ r->y2 *= output->current_scale;
+}
+
+static void
+weston_recorder_frame_notify(struct wl_listener *listener, void *data)
+{
+ struct weston_recorder *recorder =
+ container_of(listener, struct weston_recorder, frame_listener);
+ struct weston_output *output = data;
+ struct weston_compositor *compositor = output->compositor;
+ uint32_t msecs = output->frame_time;
+ pixman_box32_t *r;
+ pixman_region32_t damage;
+ int i, j, k, n, width, height, run, stride;
+ uint32_t delta, prev, *d, *s, *p, next;
+ struct {
+ uint32_t msecs;
+ uint32_t nrects;
+ } header;
+ struct iovec v[2];
+ int do_yflip;
+ int y_orig;
+ uint32_t *outbuf;
+
+ do_yflip = !!(compositor->capabilities & WESTON_CAP_CAPTURE_YFLIP);
+ if (do_yflip)
+ outbuf = recorder->rect;
+ else
+ outbuf = recorder->tmpbuf;
+
+ pixman_region32_init(&damage);
+ pixman_region32_intersect(&damage, &output->region,
+ &output->previous_damage);
+
+ r = pixman_region32_rectangles(&damage, &n);
+ if (n == 0)
+ return;
+
+ for (i = 0; i < n; i++)
+ transform_rect(output, &r[i]);
+
+ header.msecs = msecs;
+ header.nrects = n;
+ v[0].iov_base = &header;
+ v[0].iov_len = sizeof header;
+ v[1].iov_base = r;
+ v[1].iov_len = n * sizeof *r;
+ recorder->total += writev(recorder->fd, v, 2);
+ stride = output->current_mode->width;
+
+ for (i = 0; i < n; i++) {
+ width = r[i].x2 - r[i].x1;
+ height = r[i].y2 - r[i].y1;
+
+ if (do_yflip)
+ y_orig = output->current_mode->height - r[i].y2;
+ else
+ y_orig = r[i].y1;
+
+ compositor->renderer->read_pixels(output,
+ compositor->read_format, recorder->rect,
+ r[i].x1, y_orig, width, height);
+
+ s = recorder->rect;
+ p = outbuf;
+ run = prev = 0; /* quiet gcc */
+ for (j = 0; j < height; j++) {
+ if (do_yflip)
+ y_orig = r[i].y2 - j - 1;
+ else
+ y_orig = r[i].y1 + j;
+ d = recorder->frame + stride * y_orig + r[i].x1;
+
+ for (k = 0; k < width; k++) {
+ next = *s++;
+ delta = component_delta(next, *d);
+ *d++ = next;
+ if (run == 0 || delta == prev) {
+ run++;
+ } else {
+ p = output_run(p, prev, run);
+ run = 1;
+ }
+ prev = delta;
+ }
+ }
+
+ p = output_run(p, prev, run);
+
+ recorder->total += write(recorder->fd,
+ outbuf, (p - outbuf) * 4);
+
+#if 0
+ fprintf(stderr,
+ "%dx%d at %d,%d rle from %d to %d bytes (%f) total %dM\n",
+ width, height, r[i].x1, r[i].y1,
+ width * height * 4, (int) (p - outbuf) * 4,
+ (float) (p - outbuf) / (width * height),
+ recorder->total / 1024 / 1024);
+#endif
+ }
+
+ pixman_region32_fini(&damage);
+ recorder->count++;
+}
+
+static void
+weston_recorder_create(struct weston_output *output, const char *filename)
+{
+ struct weston_compositor *compositor = output->compositor;
+ struct weston_recorder *recorder;
+ int stride, size;
+ struct { uint32_t magic, format, width, height; } header;
+ int do_yflip;
+
+ do_yflip = !!(compositor->capabilities & WESTON_CAP_CAPTURE_YFLIP);
+
+ recorder = malloc(sizeof *recorder);
+
+ stride = output->current_mode->width;
+ size = stride * 4 * output->current_mode->height;
+ recorder->frame = zalloc(size);
+ recorder->rect = malloc(size);
+ recorder->total = 0;
+ recorder->count = 0;
+ recorder->output = output;
+
+ if (do_yflip)
+ recorder->tmpbuf = NULL;
+ else
+ recorder->tmpbuf = malloc(size);
+
+ header.magic = WCAP_HEADER_MAGIC;
+
+ switch (compositor->read_format) {
+ case PIXMAN_x8r8g8b8:
+ case PIXMAN_a8r8g8b8:
+ header.format = WCAP_FORMAT_XRGB8888;
+ break;
+ case PIXMAN_a8b8g8r8:
+ header.format = WCAP_FORMAT_XBGR8888;
+ break;
+ default:
+ weston_log("unknown recorder format\n");
+ free(recorder);
+ return;
+ }
+
+ recorder->fd = open(filename,
+ O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644);
+
+ if (recorder->fd < 0) {
+ weston_log("problem opening output file %s: %m\n", filename);
+ free(recorder);
+ return;
+ }
+
+ header.width = output->current_mode->width;
+ header.height = output->current_mode->height;
+ recorder->total += write(recorder->fd, &header, sizeof header);
+
+ recorder->frame_listener.notify = weston_recorder_frame_notify;
+ wl_signal_add(&output->frame_signal, &recorder->frame_listener);
+ output->disable_planes++;
+ weston_output_damage(output);
+}
+
+static void
+weston_recorder_destroy(struct weston_recorder *recorder)
+{
+ wl_list_remove(&recorder->frame_listener.link);
+ close(recorder->fd);
+ free(recorder->tmpbuf);
+ free(recorder->frame);
+ free(recorder->rect);
+ recorder->output->disable_planes--;
+ free(recorder);
+}
+
+static void
+recorder_binding(struct weston_seat *seat, uint32_t time, uint32_t key, void *data)
+{
+ struct weston_seat *ws = (struct weston_seat *) seat;
+ struct weston_compositor *ec = ws->compositor;
+ struct weston_output *output =
+ container_of(ec->output_list.next,
+ struct weston_output, link);
+ struct wl_listener *listener;
+ struct weston_recorder *recorder;
+ static const char filename[] = "capture.wcap";
+
+ listener = wl_signal_get(&output->frame_signal,
+ weston_recorder_frame_notify);
+ if (listener) {
+ recorder = container_of(listener, struct weston_recorder,
+ frame_listener);
+
+ weston_log(
+ "stopping recorder, total file size %dM, %d frames\n",
+ recorder->total / (1024 * 1024), recorder->count);
+
+ weston_recorder_destroy(recorder);
+ } else {
+ weston_log("starting recorder, file %s\n", filename);
+ weston_recorder_create(output, filename);
+ }
+}
+
+static void
+screenshooter_destroy(struct wl_listener *listener, void *data)
+{
+ struct screenshooter *shooter =
+ container_of(listener, struct screenshooter, destroy_listener);
+
+ wl_global_destroy(shooter->global);
+ free(shooter);
+}
+
+void
+screenshooter_create(struct weston_compositor *ec)
+{
+ struct screenshooter *shooter;
+
+ shooter = malloc(sizeof *shooter);
+ if (shooter == NULL)
+ return;
+
+ shooter->ec = ec;
+ shooter->client = NULL;
+
+ shooter->global = wl_global_create(ec->wl_display,
+ &screenshooter_interface, 1,
+ shooter, bind_shooter);
+ weston_compositor_add_key_binding(ec, KEY_S, MODIFIER_SUPER,
+ screenshooter_binding, shooter);
+ weston_compositor_add_key_binding(ec, KEY_R, MODIFIER_SUPER,
+ recorder_binding, shooter);
+
+ shooter->destroy_listener.notify = screenshooter_destroy;
+ wl_signal_add(&ec->destroy_signal, &shooter->destroy_listener);
+}
diff --git a/src/shell.c b/src/shell.c
new file mode 100644
index 00000000..fa9ff544
--- /dev/null
+++ b/src/shell.c
@@ -0,0 +1,4799 @@
+/*
+ * Copyright © 2010-2012 Intel Corporation
+ * Copyright © 2011-2012 Collabora, Ltd.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that 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.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+#include <linux/input.h>
+#include <assert.h>
+#include <signal.h>
+#include <math.h>
+#include <sys/types.h>
+
+#include "compositor.h"
+#include "desktop-shell-server-protocol.h"
+#include "input-method-server-protocol.h"
+#include "workspaces-server-protocol.h"
+#include "../shared/config-parser.h"
+
+#define DEFAULT_NUM_WORKSPACES 1
+#define DEFAULT_WORKSPACE_CHANGE_ANIMATION_LENGTH 200
+
+enum animation_type {
+ ANIMATION_NONE,
+
+ ANIMATION_ZOOM,
+ ANIMATION_FADE
+};
+
+enum fade_type {
+ FADE_IN,
+ FADE_OUT
+};
+
+struct focus_state {
+ struct weston_seat *seat;
+ struct workspace *ws;
+ struct weston_surface *keyboard_focus;
+ struct wl_list link;
+ struct wl_listener seat_destroy_listener;
+ struct wl_listener surface_destroy_listener;
+};
+
+struct workspace {
+ struct weston_layer layer;
+
+ struct wl_list focus_list;
+ struct wl_listener seat_destroyed_listener;
+};
+
+struct input_panel_surface {
+ struct wl_resource *resource;
+ struct wl_signal destroy_signal;
+
+ struct desktop_shell *shell;
+
+ struct wl_list link;
+ struct weston_surface *surface;
+ struct wl_listener surface_destroy_listener;
+
+ struct weston_output *output;
+ uint32_t panel;
+};
+
+struct desktop_shell {
+ struct weston_compositor *compositor;
+
+ struct wl_listener idle_listener;
+ struct wl_listener wake_listener;
+ struct wl_listener destroy_listener;
+ struct wl_listener show_input_panel_listener;
+ struct wl_listener hide_input_panel_listener;
+ struct wl_listener update_input_panel_listener;
+
+ struct weston_layer fullscreen_layer;
+ struct weston_layer panel_layer;
+ struct weston_layer background_layer;
+ struct weston_layer lock_layer;
+ struct weston_layer input_panel_layer;
+
+ struct wl_listener pointer_focus_listener;
+ struct weston_surface *grab_surface;
+
+ struct {
+ struct weston_process process;
+ struct wl_client *client;
+ struct wl_resource *desktop_shell;
+
+ unsigned deathcount;
+ uint32_t deathstamp;
+ } child;
+
+ bool locked;
+ bool showing_input_panels;
+ bool prepare_event_sent;
+
+ struct {
+ struct weston_surface *surface;
+ pixman_box32_t cursor_rectangle;
+ } text_input;
+
+ struct weston_surface *lock_surface;
+ struct wl_listener lock_surface_listener;
+
+ struct {
+ struct wl_array array;
+ unsigned int current;
+ unsigned int num;
+
+ struct wl_list client_list;
+
+ struct weston_animation animation;
+ struct wl_list anim_sticky_list;
+ int anim_dir;
+ uint32_t anim_timestamp;
+ double anim_current;
+ struct workspace *anim_from;
+ struct workspace *anim_to;
+ } workspaces;
+
+ struct {
+ char *path;
+ int duration;
+ struct wl_resource *binding;
+ struct weston_process process;
+ struct wl_event_source *timer;
+ } screensaver;
+
+ struct {
+ struct wl_resource *binding;
+ struct wl_list surfaces;
+ } input_panel;
+
+ struct {
+ struct weston_surface *surface;
+ struct weston_surface_animation *animation;
+ enum fade_type type;
+ struct wl_event_source *startup_timer;
+ } fade;
+
+ uint32_t binding_modifier;
+ enum animation_type win_animation_type;
+ enum animation_type startup_animation_type;
+};
+
+enum shell_surface_type {
+ SHELL_SURFACE_NONE,
+ SHELL_SURFACE_TOPLEVEL,
+ SHELL_SURFACE_TRANSIENT,
+ SHELL_SURFACE_FULLSCREEN,
+ SHELL_SURFACE_MAXIMIZED,
+ SHELL_SURFACE_POPUP,
+ SHELL_SURFACE_XWAYLAND
+};
+
+struct ping_timer {
+ struct wl_event_source *source;
+ uint32_t serial;
+};
+
+struct shell_surface {
+ struct wl_resource *resource;
+ struct wl_signal destroy_signal;
+
+ struct weston_surface *surface;
+ struct wl_listener surface_destroy_listener;
+ struct weston_surface *parent;
+ struct desktop_shell *shell;
+
+ enum shell_surface_type type, next_type;
+ char *title, *class;
+ int32_t saved_x, saved_y;
+ bool saved_position_valid;
+ bool saved_rotation_valid;
+ int unresponsive;
+
+ struct {
+ struct weston_transform transform;
+ struct weston_matrix rotation;
+ } rotation;
+
+ struct {
+ struct wl_list grab_link;
+ int32_t x, y;
+ struct shell_seat *shseat;
+ uint32_t serial;
+ } popup;
+
+ struct {
+ int32_t x, y;
+ uint32_t flags;
+ } transient;
+
+ struct {
+ enum wl_shell_surface_fullscreen_method type;
+ struct weston_transform transform; /* matrix from x, y */
+ uint32_t framerate;
+ struct weston_surface *black_surface;
+ } fullscreen;
+
+ struct ping_timer *ping_timer;
+
+ struct weston_transform workspace_transform;
+
+ struct weston_output *fullscreen_output;
+ struct weston_output *output;
+ struct wl_list link;
+
+ const struct weston_shell_client *client;
+};
+
+struct shell_grab {
+ struct weston_pointer_grab grab;
+ struct shell_surface *shsurf;
+ struct wl_listener shsurf_destroy_listener;
+};
+
+struct shell_touch_grab {
+ struct weston_touch_grab grab;
+ struct shell_surface *shsurf;
+ struct wl_listener shsurf_destroy_listener;
+ struct weston_touch *touch;
+};
+
+struct weston_move_grab {
+ struct shell_grab base;
+ wl_fixed_t dx, dy;
+};
+
+struct weston_touch_move_grab {
+ struct shell_touch_grab base;
+ wl_fixed_t dx, dy;
+};
+
+struct rotate_grab {
+ struct shell_grab base;
+ struct weston_matrix rotation;
+ struct {
+ float x;
+ float y;
+ } center;
+};
+
+struct shell_seat {
+ struct weston_seat *seat;
+ struct wl_listener seat_destroy_listener;
+
+ struct {
+ struct weston_pointer_grab grab;
+ struct wl_list surfaces_list;
+ struct wl_client *client;
+ int32_t initial_up;
+ } popup_grab;
+};
+
+static void
+activate(struct desktop_shell *shell, struct weston_surface *es,
+ struct weston_seat *seat);
+
+static struct workspace *
+get_current_workspace(struct desktop_shell *shell);
+
+static struct shell_surface *
+get_shell_surface(struct weston_surface *surface);
+
+static struct desktop_shell *
+shell_surface_get_shell(struct shell_surface *shsurf);
+
+static void
+surface_rotate(struct shell_surface *surface, struct weston_seat *seat);
+
+static void
+shell_fade_startup(struct desktop_shell *shell);
+
+static bool
+shell_surface_is_top_fullscreen(struct shell_surface *shsurf)
+{
+ struct desktop_shell *shell;
+ struct weston_surface *top_fs_es;
+
+ shell = shell_surface_get_shell(shsurf);
+
+ if (wl_list_empty(&shell->fullscreen_layer.surface_list))
+ return false;
+
+ top_fs_es = container_of(shell->fullscreen_layer.surface_list.next,
+ struct weston_surface,
+ layer_link);
+ return (shsurf == get_shell_surface(top_fs_es));
+}
+
+static void
+destroy_shell_grab_shsurf(struct wl_listener *listener, void *data)
+{
+ struct shell_grab *grab;
+
+ grab = container_of(listener, struct shell_grab,
+ shsurf_destroy_listener);
+
+ grab->shsurf = NULL;
+}
+
+static void
+popup_grab_end(struct weston_pointer *pointer);
+
+static void
+shell_grab_start(struct shell_grab *grab,
+ const struct weston_pointer_grab_interface *interface,
+ struct shell_surface *shsurf,
+ struct weston_pointer *pointer,
+ enum desktop_shell_cursor cursor)
+{
+ struct desktop_shell *shell = shsurf->shell;
+
+ popup_grab_end(pointer);
+
+ grab->grab.interface = interface;
+ grab->shsurf = shsurf;
+ grab->shsurf_destroy_listener.notify = destroy_shell_grab_shsurf;
+ wl_signal_add(&shsurf->destroy_signal,
+ &grab->shsurf_destroy_listener);
+
+ weston_pointer_start_grab(pointer, &grab->grab);
+ if (shell->child.desktop_shell) {
+ desktop_shell_send_grab_cursor(shell->child.desktop_shell,
+ cursor);
+ weston_pointer_set_focus(pointer, shell->grab_surface,
+ wl_fixed_from_int(0),
+ wl_fixed_from_int(0));
+ }
+}
+
+static void
+shell_grab_end(struct shell_grab *grab)
+{
+ if (grab->shsurf)
+ wl_list_remove(&grab->shsurf_destroy_listener.link);
+
+ weston_pointer_end_grab(grab->grab.pointer);
+}
+
+static void
+shell_touch_grab_start(struct shell_touch_grab *grab,
+ const struct weston_touch_grab_interface *interface,
+ struct shell_surface *shsurf,
+ struct weston_touch *touch)
+{
+ struct desktop_shell *shell = shsurf->shell;
+
+ grab->grab.interface = interface;
+ grab->shsurf = shsurf;
+ grab->shsurf_destroy_listener.notify = destroy_shell_grab_shsurf;
+ wl_signal_add(&shsurf->destroy_signal,
+ &grab->shsurf_destroy_listener);
+
+ grab->touch = touch;
+
+ weston_touch_start_grab(touch, &grab->grab);
+ if (shell->child.desktop_shell)
+ weston_touch_set_focus(touch->seat, shell->grab_surface);
+}
+
+static void
+shell_touch_grab_end(struct shell_touch_grab *grab)
+{
+ if (grab->shsurf)
+ wl_list_remove(&grab->shsurf_destroy_listener.link);
+
+ weston_touch_end_grab(grab->touch);
+}
+
+static void
+center_on_output(struct weston_surface *surface,
+ struct weston_output *output);
+
+static enum weston_keyboard_modifier
+get_modifier(char *modifier)
+{
+ if (!modifier)
+ return MODIFIER_SUPER;
+
+ if (!strcmp("ctrl", modifier))
+ return MODIFIER_CTRL;
+ else if (!strcmp("alt", modifier))
+ return MODIFIER_ALT;
+ else if (!strcmp("super", modifier))
+ return MODIFIER_SUPER;
+ else
+ return MODIFIER_SUPER;
+}
+
+static enum animation_type
+get_animation_type(char *animation)
+{
+ if (!strcmp("zoom", animation))
+ return ANIMATION_ZOOM;
+ else if (!strcmp("fade", animation))
+ return ANIMATION_FADE;
+ else
+ return ANIMATION_NONE;
+}
+
+static void
+shell_configuration(struct desktop_shell *shell)
+{
+ struct weston_config_section *section;
+ int duration;
+ char *s;
+
+ section = weston_config_get_section(shell->compositor->config,
+ "screensaver", NULL, NULL);
+ weston_config_section_get_string(section,
+ "path", &shell->screensaver.path, NULL);
+ weston_config_section_get_int(section, "duration", &duration, 60);
+ shell->screensaver.duration = duration * 1000;
+
+ section = weston_config_get_section(shell->compositor->config,
+ "shell", NULL, NULL);
+ weston_config_section_get_string(section,
+ "binding-modifier", &s, "super");
+ shell->binding_modifier = get_modifier(s);
+ free(s);
+ weston_config_section_get_string(section, "animation", &s, "none");
+ shell->win_animation_type = get_animation_type(s);
+ free(s);
+ weston_config_section_get_string(section,
+ "startup-animation", &s, "fade");
+ shell->startup_animation_type = get_animation_type(s);
+ free(s);
+ if (shell->startup_animation_type == ANIMATION_ZOOM)
+ shell->startup_animation_type = ANIMATION_NONE;
+
+ weston_config_section_get_uint(section, "num-workspaces",
+ &shell->workspaces.num,
+ DEFAULT_NUM_WORKSPACES);
+}
+
+static void
+focus_state_destroy(struct focus_state *state)
+{
+ wl_list_remove(&state->seat_destroy_listener.link);
+ wl_list_remove(&state->surface_destroy_listener.link);
+ free(state);
+}
+
+static void
+focus_state_seat_destroy(struct wl_listener *listener, void *data)
+{
+ struct focus_state *state = container_of(listener,
+ struct focus_state,
+ seat_destroy_listener);
+
+ wl_list_remove(&state->link);
+ focus_state_destroy(state);
+}
+
+static void
+focus_state_surface_destroy(struct wl_listener *listener, void *data)
+{
+ struct focus_state *state = container_of(listener,
+ struct focus_state,
+ surface_destroy_listener);
+ struct desktop_shell *shell;
+ struct weston_surface *main_surface;
+ struct weston_surface *surface, *next;
+
+ main_surface = weston_surface_get_main_surface(state->keyboard_focus);
+
+ next = NULL;
+ wl_list_for_each(surface, &state->ws->layer.surface_list, layer_link) {
+ if (surface == main_surface)
+ continue;
+
+ next = surface;
+ break;
+ }
+
+ /* if the focus was a sub-surface, activate its main surface */
+ if (main_surface != state->keyboard_focus)
+ next = main_surface;
+
+ if (next) {
+ shell = state->seat->compositor->shell_interface.shell;
+ activate(shell, next, state->seat);
+ } else {
+ wl_list_remove(&state->link);
+ focus_state_destroy(state);
+ }
+}
+
+static struct focus_state *
+focus_state_create(struct weston_seat *seat, struct workspace *ws)
+{
+ struct focus_state *state;
+
+ state = malloc(sizeof *state);
+ if (state == NULL)
+ return NULL;
+
+ state->ws = ws;
+ state->seat = seat;
+ wl_list_insert(&ws->focus_list, &state->link);
+
+ state->seat_destroy_listener.notify = focus_state_seat_destroy;
+ state->surface_destroy_listener.notify = focus_state_surface_destroy;
+ wl_signal_add(&seat->destroy_signal,
+ &state->seat_destroy_listener);
+ wl_list_init(&state->surface_destroy_listener.link);
+
+ return state;
+}
+
+static struct focus_state *
+ensure_focus_state(struct desktop_shell *shell, struct weston_seat *seat)
+{
+ struct workspace *ws = get_current_workspace(shell);
+ struct focus_state *state;
+
+ wl_list_for_each(state, &ws->focus_list, link)
+ if (state->seat == seat)
+ break;
+
+ if (&state->link == &ws->focus_list)
+ state = focus_state_create(seat, ws);
+
+ return state;
+}
+
+static void
+restore_focus_state(struct desktop_shell *shell, struct workspace *ws)
+{
+ struct focus_state *state, *next;
+ struct weston_surface *surface;
+
+ wl_list_for_each_safe(state, next, &ws->focus_list, link) {
+ surface = state->keyboard_focus;
+
+ weston_keyboard_set_focus(state->seat->keyboard, surface);
+ }
+}
+
+static void
+replace_focus_state(struct desktop_shell *shell, struct workspace *ws,
+ struct weston_seat *seat)
+{
+ struct focus_state *state;
+ struct weston_surface *surface;
+
+ wl_list_for_each(state, &ws->focus_list, link) {
+ if (state->seat == seat) {
+ surface = seat->keyboard->focus;
+ state->keyboard_focus = surface;
+ return;
+ }
+ }
+}
+
+static void
+drop_focus_state(struct desktop_shell *shell, struct workspace *ws,
+ struct weston_surface *surface)
+{
+ struct focus_state *state;
+
+ wl_list_for_each(state, &ws->focus_list, link)
+ if (state->keyboard_focus == surface)
+ state->keyboard_focus = NULL;
+}
+
+static void
+workspace_destroy(struct workspace *ws)
+{
+ struct focus_state *state, *next;
+
+ wl_list_for_each_safe(state, next, &ws->focus_list, link)
+ focus_state_destroy(state);
+
+ free(ws);
+}
+
+static void
+seat_destroyed(struct wl_listener *listener, void *data)
+{
+ struct weston_seat *seat = data;
+ struct focus_state *state, *next;
+ struct workspace *ws = container_of(listener,
+ struct workspace,
+ seat_destroyed_listener);
+
+ wl_list_for_each_safe(state, next, &ws->focus_list, link)
+ if (state->seat == seat)
+ wl_list_remove(&state->link);
+}
+
+static struct workspace *
+workspace_create(void)
+{
+ struct workspace *ws = malloc(sizeof *ws);
+ if (ws == NULL)
+ return NULL;
+
+ weston_layer_init(&ws->layer, NULL);
+
+ wl_list_init(&ws->focus_list);
+ wl_list_init(&ws->seat_destroyed_listener.link);
+ ws->seat_destroyed_listener.notify = seat_destroyed;
+
+ return ws;
+}
+
+static int
+workspace_is_empty(struct workspace *ws)
+{
+ return wl_list_empty(&ws->layer.surface_list);
+}
+
+static struct workspace *
+get_workspace(struct desktop_shell *shell, unsigned int index)
+{
+ struct workspace **pws = shell->workspaces.array.data;
+ assert(index < shell->workspaces.num);
+ pws += index;
+ return *pws;
+}
+
+static struct workspace *
+get_current_workspace(struct desktop_shell *shell)
+{
+ return get_workspace(shell, shell->workspaces.current);
+}
+
+static void
+activate_workspace(struct desktop_shell *shell, unsigned int index)
+{
+ struct workspace *ws;
+
+ ws = get_workspace(shell, index);
+ wl_list_insert(&shell->panel_layer.link, &ws->layer.link);
+
+ shell->workspaces.current = index;
+}
+
+static unsigned int
+get_output_height(struct weston_output *output)
+{
+ return abs(output->region.extents.y1 - output->region.extents.y2);
+}
+
+static void
+surface_translate(struct weston_surface *surface, double d)
+{
+ struct shell_surface *shsurf = get_shell_surface(surface);
+ struct weston_transform *transform;
+
+ transform = &shsurf->workspace_transform;
+ if (wl_list_empty(&transform->link))
+ wl_list_insert(surface->geometry.transformation_list.prev,
+ &shsurf->workspace_transform.link);
+
+ weston_matrix_init(&shsurf->workspace_transform.matrix);
+ weston_matrix_translate(&shsurf->workspace_transform.matrix,
+ 0.0, d, 0.0);
+ weston_surface_geometry_dirty(surface);
+}
+
+static void
+workspace_translate_out(struct workspace *ws, double fraction)
+{
+ struct weston_surface *surface;
+ unsigned int height;
+ double d;
+
+ wl_list_for_each(surface, &ws->layer.surface_list, layer_link) {
+ height = get_output_height(surface->output);
+ d = height * fraction;
+
+ surface_translate(surface, d);
+ }
+}
+
+static void
+workspace_translate_in(struct workspace *ws, double fraction)
+{
+ struct weston_surface *surface;
+ unsigned int height;
+ double d;
+
+ wl_list_for_each(surface, &ws->layer.surface_list, layer_link) {
+ height = get_output_height(surface->output);
+
+ if (fraction > 0)
+ d = -(height - height * fraction);
+ else
+ d = height + height * fraction;
+
+ surface_translate(surface, d);
+ }
+}
+
+static void
+broadcast_current_workspace_state(struct desktop_shell *shell)
+{
+ struct wl_resource *resource;
+
+ wl_resource_for_each(resource, &shell->workspaces.client_list)
+ workspace_manager_send_state(resource,
+ shell->workspaces.current,
+ shell->workspaces.num);
+}
+
+static void
+reverse_workspace_change_animation(struct desktop_shell *shell,
+ unsigned int index,
+ struct workspace *from,
+ struct workspace *to)
+{
+ shell->workspaces.current = index;
+
+ shell->workspaces.anim_to = to;
+ shell->workspaces.anim_from = from;
+ shell->workspaces.anim_dir = -1 * shell->workspaces.anim_dir;
+ shell->workspaces.anim_timestamp = 0;
+
+ weston_compositor_schedule_repaint(shell->compositor);
+}
+
+static void
+workspace_deactivate_transforms(struct workspace *ws)
+{
+ struct weston_surface *surface;
+ struct shell_surface *shsurf;
+
+ wl_list_for_each(surface, &ws->layer.surface_list, layer_link) {
+ shsurf = get_shell_surface(surface);
+ if (!wl_list_empty(&shsurf->workspace_transform.link)) {
+ wl_list_remove(&shsurf->workspace_transform.link);
+ wl_list_init(&shsurf->workspace_transform.link);
+ }
+ weston_surface_geometry_dirty(surface);
+ }
+}
+
+static void
+finish_workspace_change_animation(struct desktop_shell *shell,
+ struct workspace *from,
+ struct workspace *to)
+{
+ weston_compositor_schedule_repaint(shell->compositor);
+
+ wl_list_remove(&shell->workspaces.animation.link);
+ workspace_deactivate_transforms(from);
+ workspace_deactivate_transforms(to);
+ shell->workspaces.anim_to = NULL;
+
+ wl_list_remove(&shell->workspaces.anim_from->layer.link);
+}
+
+static void
+animate_workspace_change_frame(struct weston_animation *animation,
+ struct weston_output *output, uint32_t msecs)
+{
+ struct desktop_shell *shell =
+ container_of(animation, struct desktop_shell,
+ workspaces.animation);
+ struct workspace *from = shell->workspaces.anim_from;
+ struct workspace *to = shell->workspaces.anim_to;
+ uint32_t t;
+ double x, y;
+
+ if (workspace_is_empty(from) && workspace_is_empty(to)) {
+ finish_workspace_change_animation(shell, from, to);
+ return;
+ }
+
+ if (shell->workspaces.anim_timestamp == 0) {
+ if (shell->workspaces.anim_current == 0.0)
+ shell->workspaces.anim_timestamp = msecs;
+ else
+ shell->workspaces.anim_timestamp =
+ msecs -
+ /* Invers of movement function 'y' below. */
+ (asin(1.0 - shell->workspaces.anim_current) *
+ DEFAULT_WORKSPACE_CHANGE_ANIMATION_LENGTH *
+ M_2_PI);
+ }
+
+ t = msecs - shell->workspaces.anim_timestamp;
+
+ /*
+ * x = [0, π/2]
+ * y(x) = sin(x)
+ */
+ x = t * (1.0/DEFAULT_WORKSPACE_CHANGE_ANIMATION_LENGTH) * M_PI_2;
+ y = sin(x);
+
+ if (t < DEFAULT_WORKSPACE_CHANGE_ANIMATION_LENGTH) {
+ weston_compositor_schedule_repaint(shell->compositor);
+
+ workspace_translate_out(from, shell->workspaces.anim_dir * y);
+ workspace_translate_in(to, shell->workspaces.anim_dir * y);
+ shell->workspaces.anim_current = y;
+
+ weston_compositor_schedule_repaint(shell->compositor);
+ }
+ else
+ finish_workspace_change_animation(shell, from, to);
+}
+
+static void
+animate_workspace_change(struct desktop_shell *shell,
+ unsigned int index,
+ struct workspace *from,
+ struct workspace *to)
+{
+ struct weston_output *output;
+
+ int dir;
+
+ if (index > shell->workspaces.current)
+ dir = -1;
+ else
+ dir = 1;
+
+ shell->workspaces.current = index;
+
+ shell->workspaces.anim_dir = dir;
+ shell->workspaces.anim_from = from;
+ shell->workspaces.anim_to = to;
+ shell->workspaces.anim_current = 0.0;
+ shell->workspaces.anim_timestamp = 0;
+
+ output = container_of(shell->compositor->output_list.next,
+ struct weston_output, link);
+ wl_list_insert(&output->animation_list,
+ &shell->workspaces.animation.link);
+
+ wl_list_insert(from->layer.link.prev, &to->layer.link);
+
+ workspace_translate_in(to, 0);
+
+ restore_focus_state(shell, to);
+
+ weston_compositor_schedule_repaint(shell->compositor);
+}
+
+static void
+update_workspace(struct desktop_shell *shell, unsigned int index,
+ struct workspace *from, struct workspace *to)
+{
+ shell->workspaces.current = index;
+ wl_list_insert(&from->layer.link, &to->layer.link);
+ wl_list_remove(&from->layer.link);
+}
+
+static void
+change_workspace(struct desktop_shell *shell, unsigned int index)
+{
+ struct workspace *from;
+ struct workspace *to;
+
+ if (index == shell->workspaces.current)
+ return;
+
+ /* Don't change workspace when there is any fullscreen surfaces. */
+ if (!wl_list_empty(&shell->fullscreen_layer.surface_list))
+ return;
+
+ from = get_current_workspace(shell);
+ to = get_workspace(shell, index);
+
+ if (shell->workspaces.anim_from == to &&
+ shell->workspaces.anim_to == from) {
+ restore_focus_state(shell, to);
+ reverse_workspace_change_animation(shell, index, from, to);
+ broadcast_current_workspace_state(shell);
+ return;
+ }
+
+ if (shell->workspaces.anim_to != NULL)
+ finish_workspace_change_animation(shell,
+ shell->workspaces.anim_from,
+ shell->workspaces.anim_to);
+
+ restore_focus_state(shell, to);
+
+ if (workspace_is_empty(to) && workspace_is_empty(from))
+ update_workspace(shell, index, from, to);
+ else
+ animate_workspace_change(shell, index, from, to);
+
+ broadcast_current_workspace_state(shell);
+}
+
+static bool
+workspace_has_only(struct workspace *ws, struct weston_surface *surface)
+{
+ struct wl_list *list = &ws->layer.surface_list;
+ struct wl_list *e;
+
+ if (wl_list_empty(list))
+ return false;
+
+ e = list->next;
+
+ if (e->next != list)
+ return false;
+
+ return container_of(e, struct weston_surface, layer_link) == surface;
+}
+
+static void
+move_surface_to_workspace(struct desktop_shell *shell,
+ struct weston_surface *surface,
+ uint32_t workspace)
+{
+ struct workspace *from;
+ struct workspace *to;
+ struct weston_seat *seat;
+ struct weston_surface *focus;
+
+ assert(weston_surface_get_main_surface(surface) == surface);
+
+ if (workspace == shell->workspaces.current)
+ return;
+
+ if (workspace >= shell->workspaces.num)
+ workspace = shell->workspaces.num - 1;
+
+ from = get_current_workspace(shell);
+ to = get_workspace(shell, workspace);
+
+ wl_list_remove(&surface->layer_link);
+ wl_list_insert(&to->layer.surface_list, &surface->layer_link);
+
+ drop_focus_state(shell, from, surface);
+ wl_list_for_each(seat, &shell->compositor->seat_list, link) {
+ if (!seat->keyboard)
+ continue;
+
+ focus = weston_surface_get_main_surface(seat->keyboard->focus);
+ if (focus == surface)
+ weston_keyboard_set_focus(seat->keyboard, NULL);
+ }
+
+ weston_surface_damage_below(surface);
+}
+
+static void
+take_surface_to_workspace_by_seat(struct desktop_shell *shell,
+ struct weston_seat *seat,
+ unsigned int index)
+{
+ struct weston_surface *surface;
+ struct shell_surface *shsurf;
+ struct workspace *from;
+ struct workspace *to;
+ struct focus_state *state;
+
+ surface = weston_surface_get_main_surface(seat->keyboard->focus);
+ if (surface == NULL ||
+ index == shell->workspaces.current)
+ return;
+
+ from = get_current_workspace(shell);
+ to = get_workspace(shell, index);
+
+ wl_list_remove(&surface->layer_link);
+ wl_list_insert(&to->layer.surface_list, &surface->layer_link);
+
+ replace_focus_state(shell, to, seat);
+ drop_focus_state(shell, from, surface);
+
+ if (shell->workspaces.anim_from == to &&
+ shell->workspaces.anim_to == from) {
+ wl_list_remove(&to->layer.link);
+ wl_list_insert(from->layer.link.prev, &to->layer.link);
+
+ reverse_workspace_change_animation(shell, index, from, to);
+ broadcast_current_workspace_state(shell);
+
+ return;
+ }
+
+ if (shell->workspaces.anim_to != NULL)
+ finish_workspace_change_animation(shell,
+ shell->workspaces.anim_from,
+ shell->workspaces.anim_to);
+
+ if (workspace_is_empty(from) &&
+ workspace_has_only(to, surface))
+ update_workspace(shell, index, from, to);
+ else {
+ shsurf = get_shell_surface(surface);
+ if (wl_list_empty(&shsurf->workspace_transform.link))
+ wl_list_insert(&shell->workspaces.anim_sticky_list,
+ &shsurf->workspace_transform.link);
+
+ animate_workspace_change(shell, index, from, to);
+ }
+
+ broadcast_current_workspace_state(shell);
+
+ state = ensure_focus_state(shell, seat);
+ if (state != NULL)
+ state->keyboard_focus = surface;
+}
+
+static void
+workspace_manager_move_surface(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *surface_resource,
+ uint32_t workspace)
+{
+ struct desktop_shell *shell = wl_resource_get_user_data(resource);
+ struct weston_surface *surface =
+ wl_resource_get_user_data(surface_resource);
+ struct weston_surface *main_surface;
+
+ main_surface = weston_surface_get_main_surface(surface);
+ move_surface_to_workspace(shell, main_surface, workspace);
+}
+
+static const struct workspace_manager_interface workspace_manager_implementation = {
+ workspace_manager_move_surface,
+};
+
+static void
+unbind_resource(struct wl_resource *resource)
+{
+ wl_list_remove(wl_resource_get_link(resource));
+}
+
+static void
+bind_workspace_manager(struct wl_client *client,
+ void *data, uint32_t version, uint32_t id)
+{
+ struct desktop_shell *shell = data;
+ struct wl_resource *resource;
+
+ resource = wl_resource_create(client,
+ &workspace_manager_interface, 1, id);
+
+ if (resource == NULL) {
+ weston_log("couldn't add workspace manager object");
+ return;
+ }
+
+ wl_resource_set_implementation(resource,
+ &workspace_manager_implementation,
+ shell, unbind_resource);
+ wl_list_insert(&shell->workspaces.client_list,
+ wl_resource_get_link(resource));
+
+ workspace_manager_send_state(resource,
+ shell->workspaces.current,
+ shell->workspaces.num);
+}
+
+static void
+touch_move_grab_down(struct weston_touch_grab *grab, uint32_t time,
+ int touch_id, wl_fixed_t sx, wl_fixed_t sy)
+{
+}
+
+static void
+touch_move_grab_up(struct weston_touch_grab *grab, uint32_t time, int touch_id)
+{
+ struct weston_touch_move_grab *move =
+ (struct weston_touch_move_grab *) container_of(
+ grab, struct shell_touch_grab, grab);
+
+ if (grab->touch->seat->num_tp == 0) {
+ shell_touch_grab_end(&move->base);
+ free(move);
+ }
+}
+
+static void
+touch_move_grab_motion(struct weston_touch_grab *grab, uint32_t time,
+ int touch_id, wl_fixed_t sx, wl_fixed_t sy)
+{
+ struct weston_touch_move_grab *move = (struct weston_touch_move_grab *) grab;
+ struct shell_surface *shsurf = move->base.shsurf;
+ struct weston_surface *es;
+ int dx = wl_fixed_to_int(grab->touch->grab_x + move->dx);
+ int dy = wl_fixed_to_int(grab->touch->grab_y + move->dy);
+
+ if (!shsurf)
+ return;
+
+ es = shsurf->surface;
+
+ weston_surface_configure(es, dx, dy,
+ es->geometry.width, es->geometry.height);
+
+ weston_compositor_schedule_repaint(es->compositor);
+}
+
+static void
+touch_move_grab_cancel(struct weston_touch_grab *grab)
+{
+ struct weston_touch_move_grab *move =
+ (struct weston_touch_move_grab *) container_of(
+ grab, struct shell_touch_grab, grab);
+
+ shell_touch_grab_end(&move->base);
+ free(move);
+}
+
+static const struct weston_touch_grab_interface touch_move_grab_interface = {
+ touch_move_grab_down,
+ touch_move_grab_up,
+ touch_move_grab_motion,
+ touch_move_grab_cancel,
+};
+
+static int
+surface_touch_move(struct shell_surface *shsurf, struct weston_seat *seat)
+{
+ struct weston_touch_move_grab *move;
+
+ if (!shsurf)
+ return -1;
+
+ if (shsurf->type == SHELL_SURFACE_FULLSCREEN)
+ return 0;
+
+ move = malloc(sizeof *move);
+ if (!move)
+ return -1;
+
+ move->dx = wl_fixed_from_double(shsurf->surface->geometry.x) -
+ seat->touch->grab_x;
+ move->dy = wl_fixed_from_double(shsurf->surface->geometry.y) -
+ seat->touch->grab_y;
+
+ shell_touch_grab_start(&move->base, &touch_move_grab_interface, shsurf,
+ seat->touch);
+
+ return 0;
+}
+
+static void
+noop_grab_focus(struct weston_pointer_grab *grab)
+{
+}
+
+static void
+move_grab_motion(struct weston_pointer_grab *grab, uint32_t time)
+{
+ struct weston_move_grab *move = (struct weston_move_grab *) grab;
+ struct weston_pointer *pointer = grab->pointer;
+ struct shell_surface *shsurf = move->base.shsurf;
+ struct weston_surface *es;
+ int dx = wl_fixed_to_int(pointer->x + move->dx);
+ int dy = wl_fixed_to_int(pointer->y + move->dy);
+
+ if (!shsurf)
+ return;
+
+ es = shsurf->surface;
+
+ weston_surface_configure(es, dx, dy,
+ es->geometry.width, es->geometry.height);
+
+ weston_compositor_schedule_repaint(es->compositor);
+}
+
+static void
+move_grab_button(struct weston_pointer_grab *grab,
+ uint32_t time, uint32_t button, uint32_t state_w)
+{
+ struct shell_grab *shell_grab = container_of(grab, struct shell_grab,
+ grab);
+ struct weston_pointer *pointer = grab->pointer;
+ enum wl_pointer_button_state state = state_w;
+
+ if (pointer->button_count == 0 &&
+ state == WL_POINTER_BUTTON_STATE_RELEASED) {
+ shell_grab_end(shell_grab);
+ free(grab);
+ }
+}
+
+static void
+move_grab_cancel(struct weston_pointer_grab *grab)
+{
+ struct shell_grab *shell_grab =
+ container_of(grab, struct shell_grab, grab);
+
+ shell_grab_end(shell_grab);
+ free(grab);
+}
+
+static const struct weston_pointer_grab_interface move_grab_interface = {
+ noop_grab_focus,
+ move_grab_motion,
+ move_grab_button,
+ move_grab_cancel,
+};
+
+static int
+surface_move(struct shell_surface *shsurf, struct weston_seat *seat)
+{
+ struct weston_move_grab *move;
+
+ if (!shsurf)
+ return -1;
+
+ if (shsurf->type == SHELL_SURFACE_FULLSCREEN)
+ return 0;
+
+ move = malloc(sizeof *move);
+ if (!move)
+ return -1;
+
+ move->dx = wl_fixed_from_double(shsurf->surface->geometry.x) -
+ seat->pointer->grab_x;
+ move->dy = wl_fixed_from_double(shsurf->surface->geometry.y) -
+ seat->pointer->grab_y;
+
+ shell_grab_start(&move->base, &move_grab_interface, shsurf,
+ seat->pointer, DESKTOP_SHELL_CURSOR_MOVE);
+
+ return 0;
+}
+
+static void
+shell_surface_move(struct wl_client *client, struct wl_resource *resource,
+ struct wl_resource *seat_resource, uint32_t serial)
+{
+ struct weston_seat *seat = wl_resource_get_user_data(seat_resource);
+ struct shell_surface *shsurf = wl_resource_get_user_data(resource);
+ struct weston_surface *surface;
+
+ if (seat->pointer &&
+ seat->pointer->button_count > 0 &&
+ seat->pointer->grab_serial == serial) {
+ surface = weston_surface_get_main_surface(seat->pointer->focus);
+ if ((surface == shsurf->surface) &&
+ (surface_move(shsurf, seat) < 0))
+ wl_resource_post_no_memory(resource);
+ } else if (seat->touch &&
+ seat->touch->grab_serial == serial) {
+ surface = weston_surface_get_main_surface(seat->touch->focus);
+ if ((surface == shsurf->surface) &&
+ (surface_touch_move(shsurf, seat) < 0))
+ wl_resource_post_no_memory(resource);
+ }
+}
+
+struct weston_resize_grab {
+ struct shell_grab base;
+ uint32_t edges;
+ int32_t width, height;
+};
+
+static void
+resize_grab_motion(struct weston_pointer_grab *grab, uint32_t time)
+{
+ struct weston_resize_grab *resize = (struct weston_resize_grab *) grab;
+ struct weston_pointer *pointer = grab->pointer;
+ struct shell_surface *shsurf = resize->base.shsurf;
+ int32_t width, height;
+ wl_fixed_t from_x, from_y;
+ wl_fixed_t to_x, to_y;
+
+ if (!shsurf)
+ return;
+
+ weston_surface_from_global_fixed(shsurf->surface,
+ pointer->grab_x, pointer->grab_y,
+ &from_x, &from_y);
+ weston_surface_from_global_fixed(shsurf->surface,
+ pointer->x, pointer->y, &to_x, &to_y);
+
+ width = resize->width;
+ if (resize->edges & WL_SHELL_SURFACE_RESIZE_LEFT) {
+ width += wl_fixed_to_int(from_x - to_x);
+ } else if (resize->edges & WL_SHELL_SURFACE_RESIZE_RIGHT) {
+ width += wl_fixed_to_int(to_x - from_x);
+ }
+
+ height = resize->height;
+ if (resize->edges & WL_SHELL_SURFACE_RESIZE_TOP) {
+ height += wl_fixed_to_int(from_y - to_y);
+ } else if (resize->edges & WL_SHELL_SURFACE_RESIZE_BOTTOM) {
+ height += wl_fixed_to_int(to_y - from_y);
+ }
+
+ shsurf->client->send_configure(shsurf->surface,
+ resize->edges, width, height);
+}
+
+static void
+send_configure(struct weston_surface *surface,
+ uint32_t edges, int32_t width, int32_t height)
+{
+ struct shell_surface *shsurf = get_shell_surface(surface);
+
+ wl_shell_surface_send_configure(shsurf->resource,
+ edges, width, height);
+}
+
+static const struct weston_shell_client shell_client = {
+ send_configure
+};
+
+static void
+resize_grab_button(struct weston_pointer_grab *grab,
+ uint32_t time, uint32_t button, uint32_t state_w)
+{
+ struct weston_resize_grab *resize = (struct weston_resize_grab *) grab;
+ struct weston_pointer *pointer = grab->pointer;
+ enum wl_pointer_button_state state = state_w;
+
+ if (pointer->button_count == 0 &&
+ state == WL_POINTER_BUTTON_STATE_RELEASED) {
+ shell_grab_end(&resize->base);
+ free(grab);
+ }
+}
+
+static void
+resize_grab_cancel(struct weston_pointer_grab *grab)
+{
+ struct weston_resize_grab *resize = (struct weston_resize_grab *) grab;
+
+ shell_grab_end(&resize->base);
+ free(grab);
+}
+
+static const struct weston_pointer_grab_interface resize_grab_interface = {
+ noop_grab_focus,
+ resize_grab_motion,
+ resize_grab_button,
+ resize_grab_cancel,
+};
+
+/*
+ * Returns the bounding box of a surface and all its sub-surfaces,
+ * in the surface coordinates system. */
+static void
+surface_subsurfaces_boundingbox(struct weston_surface *surface, int32_t *x,
+ int32_t *y, int32_t *w, int32_t *h) {
+ pixman_region32_t region;
+ pixman_box32_t *box;
+ struct weston_subsurface *subsurface;
+
+ pixman_region32_init_rect(&region, 0, 0,
+ surface->geometry.width,
+ surface->geometry.height);
+
+ wl_list_for_each(subsurface, &surface->subsurface_list, parent_link) {
+ pixman_region32_union_rect(&region, &region,
+ subsurface->position.x,
+ subsurface->position.y,
+ subsurface->surface->geometry.width,
+ subsurface->surface->geometry.height);
+ }
+
+ box = pixman_region32_extents(&region);
+ if (x)
+ *x = box->x1;
+ if (y)
+ *y = box->y1;
+ if (w)
+ *w = box->x2 - box->x1;
+ if (h)
+ *h = box->y2 - box->y1;
+
+ pixman_region32_fini(&region);
+}
+
+static int
+surface_resize(struct shell_surface *shsurf,
+ struct weston_seat *seat, uint32_t edges)
+{
+ struct weston_resize_grab *resize;
+
+ if (shsurf->type == SHELL_SURFACE_FULLSCREEN ||
+ shsurf->type == SHELL_SURFACE_MAXIMIZED)
+ return 0;
+
+ if (edges == 0 || edges > 15 ||
+ (edges & 3) == 3 || (edges & 12) == 12)
+ return 0;
+
+ resize = malloc(sizeof *resize);
+ if (!resize)
+ return -1;
+
+ resize->edges = edges;
+ surface_subsurfaces_boundingbox(shsurf->surface, NULL, NULL,
+ &resize->width, &resize->height);
+
+ shell_grab_start(&resize->base, &resize_grab_interface, shsurf,
+ seat->pointer, edges);
+
+ return 0;
+}
+
+static void
+shell_surface_resize(struct wl_client *client, struct wl_resource *resource,
+ struct wl_resource *seat_resource, uint32_t serial,
+ uint32_t edges)
+{
+ struct weston_seat *seat = wl_resource_get_user_data(seat_resource);
+ struct shell_surface *shsurf = wl_resource_get_user_data(resource);
+ struct weston_surface *surface;
+
+ if (shsurf->type == SHELL_SURFACE_FULLSCREEN)
+ return;
+
+ surface = weston_surface_get_main_surface(seat->pointer->focus);
+ if (seat->pointer->button_count == 0 ||
+ seat->pointer->grab_serial != serial ||
+ surface != shsurf->surface)
+ return;
+
+ if (surface_resize(shsurf, seat, edges) < 0)
+ wl_resource_post_no_memory(resource);
+}
+
+static void
+end_busy_cursor(struct shell_surface *shsurf, struct weston_pointer *pointer);
+
+static void
+busy_cursor_grab_focus(struct weston_pointer_grab *base)
+{
+ struct shell_grab *grab = (struct shell_grab *) base;
+ struct weston_pointer *pointer = base->pointer;
+ struct weston_surface *surface;
+ wl_fixed_t sx, sy;
+
+ surface = weston_compositor_pick_surface(pointer->seat->compositor,
+ pointer->x, pointer->y,
+ &sx, &sy);
+
+ if (!grab->shsurf || grab->shsurf->surface != surface)
+ end_busy_cursor(grab->shsurf, pointer);
+}
+
+static void
+busy_cursor_grab_motion(struct weston_pointer_grab *grab, uint32_t time)
+{
+}
+
+static void
+busy_cursor_grab_button(struct weston_pointer_grab *base,
+ uint32_t time, uint32_t button, uint32_t state)
+{
+ struct shell_grab *grab = (struct shell_grab *) base;
+ struct shell_surface *shsurf = grab->shsurf;
+ struct weston_seat *seat = grab->grab.pointer->seat;
+
+ if (shsurf && button == BTN_LEFT && state) {
+ activate(shsurf->shell, shsurf->surface, seat);
+ surface_move(shsurf, seat);
+ } else if (shsurf && button == BTN_RIGHT && state) {
+ activate(shsurf->shell, shsurf->surface, seat);
+ surface_rotate(shsurf, seat);
+ }
+}
+
+static void
+busy_cursor_grab_cancel(struct weston_pointer_grab *base)
+{
+ struct shell_grab *grab = (struct shell_grab *) base;
+
+ shell_grab_end(grab);
+ free(grab);
+}
+
+static const struct weston_pointer_grab_interface busy_cursor_grab_interface = {
+ busy_cursor_grab_focus,
+ busy_cursor_grab_motion,
+ busy_cursor_grab_button,
+ busy_cursor_grab_cancel,
+};
+
+static void
+set_busy_cursor(struct shell_surface *shsurf, struct weston_pointer *pointer)
+{
+ struct shell_grab *grab;
+
+ grab = malloc(sizeof *grab);
+ if (!grab)
+ return;
+
+ shell_grab_start(grab, &busy_cursor_grab_interface, shsurf, pointer,
+ DESKTOP_SHELL_CURSOR_BUSY);
+}
+
+static void
+end_busy_cursor(struct shell_surface *shsurf, struct weston_pointer *pointer)
+{
+ struct shell_grab *grab = (struct shell_grab *) pointer->grab;
+
+ if (grab->grab.interface == &busy_cursor_grab_interface &&
+ grab->shsurf == shsurf) {
+ shell_grab_end(grab);
+ free(grab);
+ }
+}
+
+static void
+ping_timer_destroy(struct shell_surface *shsurf)
+{
+ if (!shsurf || !shsurf->ping_timer)
+ return;
+
+ if (shsurf->ping_timer->source)
+ wl_event_source_remove(shsurf->ping_timer->source);
+
+ free(shsurf->ping_timer);
+ shsurf->ping_timer = NULL;
+}
+
+static int
+ping_timeout_handler(void *data)
+{
+ struct shell_surface *shsurf = data;
+ struct weston_seat *seat;
+
+ /* Client is not responding */
+ shsurf->unresponsive = 1;
+
+ wl_list_for_each(seat, &shsurf->surface->compositor->seat_list, link)
+ if (seat->pointer->focus == shsurf->surface)
+ set_busy_cursor(shsurf, seat->pointer);
+
+ return 1;
+}
+
+static void
+ping_handler(struct weston_surface *surface, uint32_t serial)
+{
+ struct shell_surface *shsurf = get_shell_surface(surface);
+ struct wl_event_loop *loop;
+ int ping_timeout = 200;
+
+ if (!shsurf)
+ return;
+ if (!shsurf->resource)
+ return;
+
+ if (shsurf->surface == shsurf->shell->grab_surface)
+ return;
+
+ if (!shsurf->ping_timer) {
+ shsurf->ping_timer = malloc(sizeof *shsurf->ping_timer);
+ if (!shsurf->ping_timer)
+ return;
+
+ shsurf->ping_timer->serial = serial;
+ loop = wl_display_get_event_loop(surface->compositor->wl_display);
+ shsurf->ping_timer->source =
+ wl_event_loop_add_timer(loop, ping_timeout_handler, shsurf);
+ wl_event_source_timer_update(shsurf->ping_timer->source, ping_timeout);
+
+ wl_shell_surface_send_ping(shsurf->resource, serial);
+ }
+}
+
+static void
+handle_pointer_focus(struct wl_listener *listener, void *data)
+{
+ struct weston_pointer *pointer = data;
+ struct weston_surface *surface = pointer->focus;
+ struct weston_compositor *compositor;
+ struct shell_surface *shsurf;
+ uint32_t serial;
+
+ if (!surface)
+ return;
+
+ compositor = surface->compositor;
+ shsurf = get_shell_surface(surface);
+
+ if (shsurf && shsurf->unresponsive) {
+ set_busy_cursor(shsurf, pointer);
+ } else {
+ serial = wl_display_next_serial(compositor->wl_display);
+ ping_handler(surface, serial);
+ }
+}
+
+static void
+create_pointer_focus_listener(struct weston_seat *seat)
+{
+ struct wl_listener *listener;
+
+ if (!seat->pointer)
+ return;
+
+ listener = malloc(sizeof *listener);
+ listener->notify = handle_pointer_focus;
+ wl_signal_add(&seat->pointer->focus_signal, listener);
+}
+
+static void
+shell_surface_pong(struct wl_client *client, struct wl_resource *resource,
+ uint32_t serial)
+{
+ struct shell_surface *shsurf = wl_resource_get_user_data(resource);
+ struct weston_seat *seat;
+ struct weston_compositor *ec = shsurf->surface->compositor;
+
+ if (shsurf->ping_timer == NULL)
+ /* Just ignore unsolicited pong. */
+ return;
+
+ if (shsurf->ping_timer->serial == serial) {
+ shsurf->unresponsive = 0;
+ wl_list_for_each(seat, &ec->seat_list, link) {
+ if(seat->pointer)
+ end_busy_cursor(shsurf, seat->pointer);
+ }
+ ping_timer_destroy(shsurf);
+ }
+}
+
+static void
+set_title(struct shell_surface *shsurf, const char *title)
+{
+ free(shsurf->title);
+ shsurf->title = strdup(title);
+}
+
+static void
+shell_surface_set_title(struct wl_client *client,
+ struct wl_resource *resource, const char *title)
+{
+ struct shell_surface *shsurf = wl_resource_get_user_data(resource);
+
+ set_title(shsurf, title);
+}
+
+static void
+shell_surface_set_class(struct wl_client *client,
+ struct wl_resource *resource, const char *class)
+{
+ struct shell_surface *shsurf = wl_resource_get_user_data(resource);
+
+ free(shsurf->class);
+ shsurf->class = strdup(class);
+}
+
+static struct weston_output *
+get_default_output(struct weston_compositor *compositor)
+{
+ return container_of(compositor->output_list.next,
+ struct weston_output, link);
+}
+
+static void
+restore_output_mode(struct weston_output *output)
+{
+ if (output->current_mode != output->original_mode ||
+ (int32_t)output->current_scale != output->original_scale)
+ weston_output_switch_mode(output,
+ output->original_mode,
+ output->original_scale,
+ WESTON_MODE_SWITCH_RESTORE_NATIVE);
+}
+
+static void
+restore_all_output_modes(struct weston_compositor *compositor)
+{
+ struct weston_output *output;
+
+ wl_list_for_each(output, &compositor->output_list, link)
+ restore_output_mode(output);
+}
+
+static void
+shell_unset_fullscreen(struct shell_surface *shsurf)
+{
+ struct workspace *ws;
+ /* undo all fullscreen things here */
+ if (shsurf->fullscreen.type == WL_SHELL_SURFACE_FULLSCREEN_METHOD_DRIVER &&
+ shell_surface_is_top_fullscreen(shsurf)) {
+ restore_output_mode(shsurf->fullscreen_output);
+ }
+ shsurf->fullscreen.type = WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT;
+ shsurf->fullscreen.framerate = 0;
+ wl_list_remove(&shsurf->fullscreen.transform.link);
+ wl_list_init(&shsurf->fullscreen.transform.link);
+ if (shsurf->fullscreen.black_surface)
+ weston_surface_destroy(shsurf->fullscreen.black_surface);
+ shsurf->fullscreen.black_surface = NULL;
+ shsurf->fullscreen_output = NULL;
+ weston_surface_set_position(shsurf->surface,
+ shsurf->saved_x, shsurf->saved_y);
+ if (shsurf->saved_rotation_valid) {
+ wl_list_insert(&shsurf->surface->geometry.transformation_list,
+ &shsurf->rotation.transform.link);
+ shsurf->saved_rotation_valid = false;
+ }
+
+ ws = get_current_workspace(shsurf->shell);
+ wl_list_remove(&shsurf->surface->layer_link);
+ wl_list_insert(&ws->layer.surface_list, &shsurf->surface->layer_link);
+}
+
+static void
+shell_unset_maximized(struct shell_surface *shsurf)
+{
+ struct workspace *ws;
+ /* undo all maximized things here */
+ shsurf->output = get_default_output(shsurf->surface->compositor);
+ weston_surface_set_position(shsurf->surface,
+ shsurf->saved_x,
+ shsurf->saved_y);
+
+ if (shsurf->saved_rotation_valid) {
+ wl_list_insert(&shsurf->surface->geometry.transformation_list,
+ &shsurf->rotation.transform.link);
+ shsurf->saved_rotation_valid = false;
+ }
+
+ ws = get_current_workspace(shsurf->shell);
+ wl_list_remove(&shsurf->surface->layer_link);
+ wl_list_insert(&ws->layer.surface_list, &shsurf->surface->layer_link);
+}
+
+static int
+reset_shell_surface_type(struct shell_surface *surface)
+{
+ switch (surface->type) {
+ case SHELL_SURFACE_FULLSCREEN:
+ shell_unset_fullscreen(surface);
+ break;
+ case SHELL_SURFACE_MAXIMIZED:
+ shell_unset_maximized(surface);
+ break;
+ case SHELL_SURFACE_NONE:
+ case SHELL_SURFACE_TOPLEVEL:
+ case SHELL_SURFACE_TRANSIENT:
+ case SHELL_SURFACE_POPUP:
+ case SHELL_SURFACE_XWAYLAND:
+ break;
+ }
+
+ surface->type = SHELL_SURFACE_NONE;
+ return 0;
+}
+
+static void
+set_surface_type(struct shell_surface *shsurf)
+{
+ struct weston_surface *surface = shsurf->surface;
+ struct weston_surface *pes = shsurf->parent;
+
+ reset_shell_surface_type(shsurf);
+
+ shsurf->type = shsurf->next_type;
+ shsurf->next_type = SHELL_SURFACE_NONE;
+
+ switch (shsurf->type) {
+ case SHELL_SURFACE_TOPLEVEL:
+ break;
+ case SHELL_SURFACE_TRANSIENT:
+ weston_surface_set_position(surface,
+ pes->geometry.x + shsurf->transient.x,
+ pes->geometry.y + shsurf->transient.y);
+ break;
+
+ case SHELL_SURFACE_MAXIMIZED:
+ case SHELL_SURFACE_FULLSCREEN:
+ shsurf->saved_x = surface->geometry.x;
+ shsurf->saved_y = surface->geometry.y;
+ shsurf->saved_position_valid = true;
+
+ if (!wl_list_empty(&shsurf->rotation.transform.link)) {
+ wl_list_remove(&shsurf->rotation.transform.link);
+ wl_list_init(&shsurf->rotation.transform.link);
+ weston_surface_geometry_dirty(shsurf->surface);
+ shsurf->saved_rotation_valid = true;
+ }
+ break;
+
+ case SHELL_SURFACE_XWAYLAND:
+ weston_surface_set_position(surface, shsurf->transient.x,
+ shsurf->transient.y);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void
+set_toplevel(struct shell_surface *shsurf)
+{
+ shsurf->next_type = SHELL_SURFACE_TOPLEVEL;
+}
+
+static void
+shell_surface_set_toplevel(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ struct shell_surface *surface = wl_resource_get_user_data(resource);
+
+ set_toplevel(surface);
+}
+
+static void
+set_transient(struct shell_surface *shsurf,
+ struct weston_surface *parent, int x, int y, uint32_t flags)
+{
+ /* assign to parents output */
+ shsurf->parent = parent;
+ shsurf->transient.x = x;
+ shsurf->transient.y = y;
+ shsurf->transient.flags = flags;
+ shsurf->next_type = SHELL_SURFACE_TRANSIENT;
+}
+
+static void
+shell_surface_set_transient(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *parent_resource,
+ int x, int y, uint32_t flags)
+{
+ struct shell_surface *shsurf = wl_resource_get_user_data(resource);
+ struct weston_surface *parent =
+ wl_resource_get_user_data(parent_resource);
+
+ set_transient(shsurf, parent, x, y, flags);
+}
+
+static struct desktop_shell *
+shell_surface_get_shell(struct shell_surface *shsurf)
+{
+ return shsurf->shell;
+}
+
+static int
+get_output_panel_height(struct desktop_shell *shell,
+ struct weston_output *output)
+{
+ struct weston_surface *surface;
+ int panel_height = 0;
+
+ if (!output)
+ return 0;
+
+ wl_list_for_each(surface, &shell->panel_layer.surface_list, layer_link) {
+ if (surface->output == output) {
+ panel_height = surface->geometry.height;
+ break;
+ }
+ }
+
+ return panel_height;
+}
+
+static void
+shell_surface_set_maximized(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *output_resource )
+{
+ struct shell_surface *shsurf = wl_resource_get_user_data(resource);
+ struct weston_surface *es = shsurf->surface;
+ struct desktop_shell *shell = NULL;
+ uint32_t edges = 0, panel_height = 0;
+
+ /* get the default output, if the client set it as NULL
+ check whether the ouput is available */
+ if (output_resource)
+ shsurf->output = wl_resource_get_user_data(output_resource);
+ else if (es->output)
+ shsurf->output = es->output;
+ else
+ shsurf->output = get_default_output(es->compositor);
+
+ shell = shell_surface_get_shell(shsurf);
+ panel_height = get_output_panel_height(shell, shsurf->output);
+ edges = WL_SHELL_SURFACE_RESIZE_TOP|WL_SHELL_SURFACE_RESIZE_LEFT;
+
+ shsurf->client->send_configure(shsurf->surface, edges,
+ shsurf->output->width,
+ shsurf->output->height - panel_height);
+
+ shsurf->next_type = SHELL_SURFACE_MAXIMIZED;
+}
+
+static void
+black_surface_configure(struct weston_surface *es, int32_t sx, int32_t sy, int32_t width, int32_t height);
+
+static struct weston_surface *
+create_black_surface(struct weston_compositor *ec,
+ struct weston_surface *fs_surface,
+ float x, float y, int w, int h)
+{
+ struct weston_surface *surface = NULL;
+
+ surface = weston_surface_create(ec);
+ if (surface == NULL) {
+ weston_log("no memory\n");
+ return NULL;
+ }
+
+ surface->configure = black_surface_configure;
+ surface->configure_private = fs_surface;
+ weston_surface_configure(surface, x, y, w, h);
+ weston_surface_set_color(surface, 0.0, 0.0, 0.0, 1);
+ pixman_region32_fini(&surface->opaque);
+ pixman_region32_init_rect(&surface->opaque, 0, 0, w, h);
+ pixman_region32_fini(&surface->input);
+ pixman_region32_init_rect(&surface->input, 0, 0, w, h);
+
+ return surface;
+}
+
+/* Create black surface and append it to the associated fullscreen surface.
+ * Handle size dismatch and positioning according to the method. */
+static void
+shell_configure_fullscreen(struct shell_surface *shsurf)
+{
+ struct weston_output *output = shsurf->fullscreen_output;
+ struct weston_surface *surface = shsurf->surface;
+ struct weston_matrix *matrix;
+ float scale, output_aspect, surface_aspect, x, y;
+ int32_t surf_x, surf_y, surf_width, surf_height;
+
+ if (shsurf->fullscreen.type != WL_SHELL_SURFACE_FULLSCREEN_METHOD_DRIVER)
+ restore_output_mode(output);
+
+ if (!shsurf->fullscreen.black_surface)
+ shsurf->fullscreen.black_surface =
+ create_black_surface(surface->compositor,
+ surface,
+ output->x, output->y,
+ output->width,
+ output->height);
+
+ wl_list_remove(&shsurf->fullscreen.black_surface->layer_link);
+ wl_list_insert(&surface->layer_link,
+ &shsurf->fullscreen.black_surface->layer_link);
+ shsurf->fullscreen.black_surface->output = output;
+
+ surface_subsurfaces_boundingbox(surface, &surf_x, &surf_y,
+ &surf_width, &surf_height);
+
+ switch (shsurf->fullscreen.type) {
+ case WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT:
+ if (surface->buffer_ref.buffer)
+ center_on_output(surface, shsurf->fullscreen_output);
+ break;
+ case WL_SHELL_SURFACE_FULLSCREEN_METHOD_SCALE:
+ /* 1:1 mapping between surface and output dimensions */
+ if (output->width == surf_width &&
+ output->height == surf_height) {
+ weston_surface_set_position(surface, output->x - surf_x,
+ output->y - surf_y);
+ break;
+ }
+
+ matrix = &shsurf->fullscreen.transform.matrix;
+ weston_matrix_init(matrix);
+
+ output_aspect = (float) output->width /
+ (float) output->height;
+ surface_aspect = (float) surface->geometry.width /
+ (float) surface->geometry.height;
+ if (output_aspect < surface_aspect)
+ scale = (float) output->width /
+ (float) surf_width;
+ else
+ scale = (float) output->height /
+ (float) surf_height;
+
+ weston_matrix_scale(matrix, scale, scale, 1);
+ wl_list_remove(&shsurf->fullscreen.transform.link);
+ wl_list_insert(&surface->geometry.transformation_list,
+ &shsurf->fullscreen.transform.link);
+ x = output->x + (output->width - surf_width * scale) / 2 - surf_x;
+ y = output->y + (output->height - surf_height * scale) / 2 - surf_y;
+ weston_surface_set_position(surface, x, y);
+
+ break;
+ case WL_SHELL_SURFACE_FULLSCREEN_METHOD_DRIVER:
+ if (shell_surface_is_top_fullscreen(shsurf)) {
+ struct weston_mode mode = {0,
+ surf_width * surface->buffer_scale,
+ surf_height * surface->buffer_scale,
+ shsurf->fullscreen.framerate};
+
+ if (weston_output_switch_mode(output, &mode, surface->buffer_scale,
+ WESTON_MODE_SWITCH_SET_TEMPORARY) == 0) {
+ weston_surface_set_position(surface,
+ output->x - surf_x,
+ output->y - surf_y);
+ weston_surface_configure(shsurf->fullscreen.black_surface,
+ output->x - surf_x,
+ output->y - surf_y,
+ output->width,
+ output->height);
+ break;
+ } else {
+ restore_output_mode(output);
+ center_on_output(surface, output);
+ }
+ }
+ break;
+ case WL_SHELL_SURFACE_FULLSCREEN_METHOD_FILL:
+ center_on_output(surface, output);
+ break;
+ default:
+ break;
+ }
+}
+
+/* make the fullscreen and black surface at the top */
+static void
+shell_stack_fullscreen(struct shell_surface *shsurf)
+{
+ struct weston_output *output = shsurf->fullscreen_output;
+ struct weston_surface *surface = shsurf->surface;
+ struct desktop_shell *shell = shell_surface_get_shell(shsurf);
+
+ wl_list_remove(&surface->layer_link);
+ wl_list_insert(&shell->fullscreen_layer.surface_list,
+ &surface->layer_link);
+ weston_surface_damage(surface);
+
+ if (!shsurf->fullscreen.black_surface)
+ shsurf->fullscreen.black_surface =
+ create_black_surface(surface->compositor,
+ surface,
+ output->x, output->y,
+ output->width,
+ output->height);
+
+ wl_list_remove(&shsurf->fullscreen.black_surface->layer_link);
+ wl_list_insert(&surface->layer_link,
+ &shsurf->fullscreen.black_surface->layer_link);
+ weston_surface_damage(shsurf->fullscreen.black_surface);
+}
+
+static void
+shell_map_fullscreen(struct shell_surface *shsurf)
+{
+ shell_stack_fullscreen(shsurf);
+ shell_configure_fullscreen(shsurf);
+}
+
+static void
+set_fullscreen(struct shell_surface *shsurf,
+ uint32_t method,
+ uint32_t framerate,
+ struct weston_output *output)
+{
+ struct weston_surface *es = shsurf->surface;
+
+ if (output)
+ shsurf->output = output;
+ else if (es->output)
+ shsurf->output = es->output;
+ else
+ shsurf->output = get_default_output(es->compositor);
+
+ shsurf->fullscreen_output = shsurf->output;
+ shsurf->fullscreen.type = method;
+ shsurf->fullscreen.framerate = framerate;
+ shsurf->next_type = SHELL_SURFACE_FULLSCREEN;
+
+ shsurf->client->send_configure(shsurf->surface, 0,
+ shsurf->output->width,
+ shsurf->output->height);
+}
+
+static void
+shell_surface_set_fullscreen(struct wl_client *client,
+ struct wl_resource *resource,
+ uint32_t method,
+ uint32_t framerate,
+ struct wl_resource *output_resource)
+{
+ struct shell_surface *shsurf = wl_resource_get_user_data(resource);
+ struct weston_output *output;
+
+ if (output_resource)
+ output = wl_resource_get_user_data(output_resource);
+ else
+ output = NULL;
+
+ set_fullscreen(shsurf, method, framerate, output);
+}
+
+static void
+set_xwayland(struct shell_surface *shsurf, int x, int y, uint32_t flags)
+{
+ /* XXX: using the same fields for transient type */
+ shsurf->transient.x = x;
+ shsurf->transient.y = y;
+ shsurf->transient.flags = flags;
+ shsurf->next_type = SHELL_SURFACE_XWAYLAND;
+}
+
+static const struct weston_pointer_grab_interface popup_grab_interface;
+
+static void
+destroy_shell_seat(struct wl_listener *listener, void *data)
+{
+ struct shell_seat *shseat =
+ container_of(listener,
+ struct shell_seat, seat_destroy_listener);
+ struct shell_surface *shsurf, *prev = NULL;
+
+ if (shseat->popup_grab.grab.interface == &popup_grab_interface) {
+ weston_pointer_end_grab(shseat->popup_grab.grab.pointer);
+ shseat->popup_grab.client = NULL;
+
+ wl_list_for_each(shsurf, &shseat->popup_grab.surfaces_list, popup.grab_link) {
+ shsurf->popup.shseat = NULL;
+ if (prev) {
+ wl_list_init(&prev->popup.grab_link);
+ }
+ prev = shsurf;
+ }
+ wl_list_init(&prev->popup.grab_link);
+ }
+
+ wl_list_remove(&shseat->seat_destroy_listener.link);
+ free(shseat);
+}
+
+static struct shell_seat *
+create_shell_seat(struct weston_seat *seat)
+{
+ struct shell_seat *shseat;
+
+ shseat = calloc(1, sizeof *shseat);
+ if (!shseat) {
+ weston_log("no memory to allocate shell seat\n");
+ return NULL;
+ }
+
+ shseat->seat = seat;
+ wl_list_init(&shseat->popup_grab.surfaces_list);
+
+ shseat->seat_destroy_listener.notify = destroy_shell_seat;
+ wl_signal_add(&seat->destroy_signal,
+ &shseat->seat_destroy_listener);
+
+ return shseat;
+}
+
+static struct shell_seat *
+get_shell_seat(struct weston_seat *seat)
+{
+ struct wl_listener *listener;
+
+ listener = wl_signal_get(&seat->destroy_signal, destroy_shell_seat);
+ if (listener == NULL)
+ return create_shell_seat(seat);
+
+ return container_of(listener,
+ struct shell_seat, seat_destroy_listener);
+}
+
+static void
+popup_grab_focus(struct weston_pointer_grab *grab)
+{
+ struct weston_pointer *pointer = grab->pointer;
+ struct weston_surface *surface;
+ struct shell_seat *shseat =
+ container_of(grab, struct shell_seat, popup_grab.grab);
+ struct wl_client *client = shseat->popup_grab.client;
+ wl_fixed_t sx, sy;
+
+ surface = weston_compositor_pick_surface(pointer->seat->compositor,
+ pointer->x, pointer->y,
+ &sx, &sy);
+
+ if (surface && wl_resource_get_client(surface->resource) == client) {
+ weston_pointer_set_focus(pointer, surface, sx, sy);
+ } else {
+ weston_pointer_set_focus(pointer, NULL,
+ wl_fixed_from_int(0),
+ wl_fixed_from_int(0));
+ }
+}
+
+static void
+popup_grab_motion(struct weston_pointer_grab *grab, uint32_t time)
+{
+ struct weston_pointer *pointer = grab->pointer;
+ struct wl_resource *resource;
+ wl_fixed_t sx, sy;
+
+ wl_resource_for_each(resource, &pointer->focus_resource_list) {
+ weston_surface_from_global_fixed(pointer->focus,
+ pointer->x, pointer->y,
+ &sx, &sy);
+ wl_pointer_send_motion(resource, time, sx, sy);
+ }
+}
+
+static void
+popup_grab_button(struct weston_pointer_grab *grab,
+ uint32_t time, uint32_t button, uint32_t state_w)
+{
+ struct wl_resource *resource;
+ struct shell_seat *shseat =
+ container_of(grab, struct shell_seat, popup_grab.grab);
+ struct wl_display *display = shseat->seat->compositor->wl_display;
+ enum wl_pointer_button_state state = state_w;
+ uint32_t serial;
+ struct wl_list *resource_list;
+
+ resource_list = &grab->pointer->focus_resource_list;
+ if (!wl_list_empty(resource_list)) {
+ serial = wl_display_get_serial(display);
+ wl_resource_for_each(resource, resource_list) {
+ wl_pointer_send_button(resource, serial,
+ time, button, state);
+ }
+ } else if (state == WL_POINTER_BUTTON_STATE_RELEASED &&
+ (shseat->popup_grab.initial_up ||
+ time - shseat->seat->pointer->grab_time > 500)) {
+ popup_grab_end(grab->pointer);
+ }
+
+ if (state == WL_POINTER_BUTTON_STATE_RELEASED)
+ shseat->popup_grab.initial_up = 1;
+}
+
+static void
+popup_grab_cancel(struct weston_pointer_grab *grab)
+{
+ popup_grab_end(grab->pointer);
+}
+
+static const struct weston_pointer_grab_interface popup_grab_interface = {
+ popup_grab_focus,
+ popup_grab_motion,
+ popup_grab_button,
+ popup_grab_cancel,
+};
+
+static void
+popup_grab_end(struct weston_pointer *pointer)
+{
+ struct weston_pointer_grab *grab = pointer->grab;
+ struct shell_seat *shseat =
+ container_of(grab, struct shell_seat, popup_grab.grab);
+ struct shell_surface *shsurf;
+ struct shell_surface *prev = NULL;
+
+ if (pointer->grab->interface == &popup_grab_interface) {
+ weston_pointer_end_grab(grab->pointer);
+ shseat->popup_grab.client = NULL;
+ shseat->popup_grab.grab.interface = NULL;
+ assert(!wl_list_empty(&shseat->popup_grab.surfaces_list));
+ /* Send the popup_done event to all the popups open */
+ wl_list_for_each(shsurf, &shseat->popup_grab.surfaces_list, popup.grab_link) {
+ wl_shell_surface_send_popup_done(shsurf->resource);
+ shsurf->popup.shseat = NULL;
+ if (prev) {
+ wl_list_init(&prev->popup.grab_link);
+ }
+ prev = shsurf;
+ }
+ wl_list_init(&prev->popup.grab_link);
+ wl_list_init(&shseat->popup_grab.surfaces_list);
+ }
+}
+
+static void
+add_popup_grab(struct shell_surface *shsurf, struct shell_seat *shseat)
+{
+ struct weston_seat *seat = shseat->seat;
+
+ if (wl_list_empty(&shseat->popup_grab.surfaces_list)) {
+ shseat->popup_grab.client = wl_resource_get_client(shsurf->resource);
+ shseat->popup_grab.grab.interface = &popup_grab_interface;
+ /* We must make sure here that this popup was opened after
+ * a mouse press, and not just by moving around with other
+ * popups already open. */
+ if (shseat->seat->pointer->button_count > 0)
+ shseat->popup_grab.initial_up = 0;
+
+ wl_list_insert(&shseat->popup_grab.surfaces_list, &shsurf->popup.grab_link);
+ weston_pointer_start_grab(seat->pointer, &shseat->popup_grab.grab);
+ } else {
+ wl_list_insert(&shseat->popup_grab.surfaces_list, &shsurf->popup.grab_link);
+ }
+}
+
+static void
+remove_popup_grab(struct shell_surface *shsurf)
+{
+ struct shell_seat *shseat = shsurf->popup.shseat;
+
+ wl_list_remove(&shsurf->popup.grab_link);
+ wl_list_init(&shsurf->popup.grab_link);
+ if (wl_list_empty(&shseat->popup_grab.surfaces_list)) {
+ weston_pointer_end_grab(shseat->popup_grab.grab.pointer);
+ shseat->popup_grab.grab.interface = NULL;
+ }
+}
+
+static void
+shell_map_popup(struct shell_surface *shsurf)
+{
+ struct shell_seat *shseat = shsurf->popup.shseat;
+ struct weston_surface *es = shsurf->surface;
+ struct weston_surface *parent = shsurf->parent;
+
+ es->output = parent->output;
+
+ weston_surface_set_transform_parent(es, parent);
+ weston_surface_set_position(es, shsurf->popup.x, shsurf->popup.y);
+ weston_surface_update_transform(es);
+
+ if (shseat->seat->pointer->grab_serial == shsurf->popup.serial) {
+ add_popup_grab(shsurf, shseat);
+ } else {
+ wl_shell_surface_send_popup_done(shsurf->resource);
+ shseat->popup_grab.client = NULL;
+ }
+}
+
+static void
+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 shell_surface *shsurf = wl_resource_get_user_data(resource);
+
+ shsurf->type = SHELL_SURFACE_POPUP;
+ shsurf->parent = wl_resource_get_user_data(parent_resource);
+ shsurf->popup.shseat = get_shell_seat(wl_resource_get_user_data(seat_resource));
+ shsurf->popup.serial = serial;
+ shsurf->popup.x = x;
+ shsurf->popup.y = y;
+}
+
+static const struct wl_shell_surface_interface shell_surface_implementation = {
+ shell_surface_pong,
+ shell_surface_move,
+ shell_surface_resize,
+ shell_surface_set_toplevel,
+ shell_surface_set_transient,
+ shell_surface_set_fullscreen,
+ shell_surface_set_popup,
+ shell_surface_set_maximized,
+ shell_surface_set_title,
+ shell_surface_set_class
+};
+
+static void
+destroy_shell_surface(struct shell_surface *shsurf)
+{
+ wl_signal_emit(&shsurf->destroy_signal, shsurf);
+
+ if (!wl_list_empty(&shsurf->popup.grab_link)) {
+ remove_popup_grab(shsurf);
+ }
+
+ if (shsurf->fullscreen.type == WL_SHELL_SURFACE_FULLSCREEN_METHOD_DRIVER &&
+ shell_surface_is_top_fullscreen(shsurf))
+ restore_output_mode (shsurf->fullscreen_output);
+
+ if (shsurf->fullscreen.black_surface)
+ weston_surface_destroy(shsurf->fullscreen.black_surface);
+
+ /* As destroy_resource() use wl_list_for_each_safe(),
+ * we can always remove the listener.
+ */
+ wl_list_remove(&shsurf->surface_destroy_listener.link);
+ shsurf->surface->configure = NULL;
+ ping_timer_destroy(shsurf);
+ free(shsurf->title);
+
+ wl_list_remove(&shsurf->link);
+ free(shsurf);
+}
+
+static void
+shell_destroy_shell_surface(struct wl_resource *resource)
+{
+ struct shell_surface *shsurf = wl_resource_get_user_data(resource);
+
+ destroy_shell_surface(shsurf);
+}
+
+static void
+shell_handle_surface_destroy(struct wl_listener *listener, void *data)
+{
+ struct shell_surface *shsurf = container_of(listener,
+ struct shell_surface,
+ surface_destroy_listener);
+
+ if (shsurf->resource)
+ wl_resource_destroy(shsurf->resource);
+ else
+ destroy_shell_surface(shsurf);
+}
+
+static void
+shell_surface_configure(struct weston_surface *, int32_t, int32_t, int32_t, int32_t);
+
+static struct shell_surface *
+get_shell_surface(struct weston_surface *surface)
+{
+ if (surface->configure == shell_surface_configure)
+ return surface->configure_private;
+ else
+ return NULL;
+}
+
+static struct shell_surface *
+create_shell_surface(void *shell, struct weston_surface *surface,
+ const struct weston_shell_client *client)
+{
+ struct shell_surface *shsurf;
+
+ if (surface->configure) {
+ weston_log("surface->configure already set\n");
+ return NULL;
+ }
+
+ shsurf = calloc(1, sizeof *shsurf);
+ if (!shsurf) {
+ weston_log("no memory to allocate shell surface\n");
+ return NULL;
+ }
+
+ surface->configure = shell_surface_configure;
+ surface->configure_private = shsurf;
+
+ shsurf->shell = (struct desktop_shell *) shell;
+ shsurf->unresponsive = 0;
+ shsurf->saved_position_valid = false;
+ shsurf->saved_rotation_valid = false;
+ shsurf->surface = surface;
+ shsurf->fullscreen.type = WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT;
+ shsurf->fullscreen.framerate = 0;
+ shsurf->fullscreen.black_surface = NULL;
+ shsurf->ping_timer = NULL;
+ wl_list_init(&shsurf->fullscreen.transform.link);
+
+ wl_signal_init(&shsurf->destroy_signal);
+ shsurf->surface_destroy_listener.notify = shell_handle_surface_destroy;
+ wl_signal_add(&surface->destroy_signal,
+ &shsurf->surface_destroy_listener);
+
+ /* init link so its safe to always remove it in destroy_shell_surface */
+ wl_list_init(&shsurf->link);
+ wl_list_init(&shsurf->popup.grab_link);
+
+ /* empty when not in use */
+ wl_list_init(&shsurf->rotation.transform.link);
+ weston_matrix_init(&shsurf->rotation.rotation);
+
+ wl_list_init(&shsurf->workspace_transform.link);
+
+ shsurf->type = SHELL_SURFACE_NONE;
+ shsurf->next_type = SHELL_SURFACE_NONE;
+
+ shsurf->client = client;
+
+ return shsurf;
+}
+
+static void
+shell_get_shell_surface(struct wl_client *client,
+ struct wl_resource *resource,
+ uint32_t id,
+ struct wl_resource *surface_resource)
+{
+ struct weston_surface *surface =
+ wl_resource_get_user_data(surface_resource);
+ struct desktop_shell *shell = wl_resource_get_user_data(resource);
+ struct shell_surface *shsurf;
+
+ if (get_shell_surface(surface)) {
+ wl_resource_post_error(surface_resource,
+ WL_DISPLAY_ERROR_INVALID_OBJECT,
+ "desktop_shell::get_shell_surface already requested");
+ return;
+ }
+
+ shsurf = create_shell_surface(shell, surface, &shell_client);
+ if (!shsurf) {
+ wl_resource_post_error(surface_resource,
+ WL_DISPLAY_ERROR_INVALID_OBJECT,
+ "surface->configure already set");
+ return;
+ }
+
+ shsurf->resource =
+ wl_resource_create(client,
+ &wl_shell_surface_interface, 1, id);
+ wl_resource_set_implementation(shsurf->resource,
+ &shell_surface_implementation,
+ shsurf, shell_destroy_shell_surface);
+}
+
+static const struct wl_shell_interface shell_implementation = {
+ shell_get_shell_surface
+};
+
+static void
+shell_fade(struct desktop_shell *shell, enum fade_type type);
+
+static int
+screensaver_timeout(void *data)
+{
+ struct desktop_shell *shell = data;
+
+ shell_fade(shell, FADE_OUT);
+
+ return 1;
+}
+
+static void
+handle_screensaver_sigchld(struct weston_process *proc, int status)
+{
+ struct desktop_shell *shell =
+ container_of(proc, struct desktop_shell, screensaver.process);
+
+ proc->pid = 0;
+
+ if (shell->locked)
+ weston_compositor_sleep(shell->compositor);
+}
+
+static void
+launch_screensaver(struct desktop_shell *shell)
+{
+ if (shell->screensaver.binding)
+ return;
+
+ if (!shell->screensaver.path) {
+ weston_compositor_sleep(shell->compositor);
+ return;
+ }
+
+ if (shell->screensaver.process.pid != 0) {
+ weston_log("old screensaver still running\n");
+ return;
+ }
+
+ weston_client_launch(shell->compositor,
+ &shell->screensaver.process,
+ shell->screensaver.path,
+ handle_screensaver_sigchld);
+}
+
+static void
+terminate_screensaver(struct desktop_shell *shell)
+{
+ if (shell->screensaver.process.pid == 0)
+ return;
+
+ kill(shell->screensaver.process.pid, SIGTERM);
+}
+
+static void
+configure_static_surface(struct weston_surface *es, struct weston_layer *layer, int32_t width, int32_t height)
+{
+ struct weston_surface *s, *next;
+
+ if (width == 0)
+ return;
+
+ wl_list_for_each_safe(s, next, &layer->surface_list, layer_link) {
+ if (s->output == es->output && s != es) {
+ weston_surface_unmap(s);
+ s->configure = NULL;
+ }
+ }
+
+ weston_surface_configure(es, es->output->x, es->output->y, width, height);
+
+ if (wl_list_empty(&es->layer_link)) {
+ wl_list_insert(&layer->surface_list, &es->layer_link);
+ weston_compositor_schedule_repaint(es->compositor);
+ }
+}
+
+static void
+background_configure(struct weston_surface *es, int32_t sx, int32_t sy, int32_t width, int32_t height)
+{
+ struct desktop_shell *shell = es->configure_private;
+
+ configure_static_surface(es, &shell->background_layer, width, height);
+}
+
+static void
+desktop_shell_set_background(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *output_resource,
+ struct wl_resource *surface_resource)
+{
+ struct desktop_shell *shell = wl_resource_get_user_data(resource);
+ struct weston_surface *surface =
+ wl_resource_get_user_data(surface_resource);
+
+ if (surface->configure) {
+ wl_resource_post_error(surface_resource,
+ WL_DISPLAY_ERROR_INVALID_OBJECT,
+ "surface role already assigned");
+ return;
+ }
+
+ surface->configure = background_configure;
+ surface->configure_private = shell;
+ surface->output = wl_resource_get_user_data(output_resource);
+ desktop_shell_send_configure(resource, 0,
+ surface_resource,
+ surface->output->width,
+ surface->output->height);
+}
+
+static void
+panel_configure(struct weston_surface *es, int32_t sx, int32_t sy, int32_t width, int32_t height)
+{
+ struct desktop_shell *shell = es->configure_private;
+
+ configure_static_surface(es, &shell->panel_layer, width, height);
+}
+
+static void
+desktop_shell_set_panel(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *output_resource,
+ struct wl_resource *surface_resource)
+{
+ struct desktop_shell *shell = wl_resource_get_user_data(resource);
+ struct weston_surface *surface =
+ wl_resource_get_user_data(surface_resource);
+
+ if (surface->configure) {
+ wl_resource_post_error(surface_resource,
+ WL_DISPLAY_ERROR_INVALID_OBJECT,
+ "surface role already assigned");
+ return;
+ }
+
+ surface->configure = panel_configure;
+ surface->configure_private = shell;
+ surface->output = wl_resource_get_user_data(output_resource);
+ desktop_shell_send_configure(resource, 0,
+ surface_resource,
+ surface->output->width,
+ surface->output->height);
+}
+
+static void
+lock_surface_configure(struct weston_surface *surface, int32_t sx, int32_t sy, int32_t width, int32_t height)
+{
+ struct desktop_shell *shell = surface->configure_private;
+
+ if (width == 0)
+ return;
+
+ surface->geometry.width = width;
+ surface->geometry.height = height;
+ center_on_output(surface, get_default_output(shell->compositor));
+
+ if (!weston_surface_is_mapped(surface)) {
+ wl_list_insert(&shell->lock_layer.surface_list,
+ &surface->layer_link);
+ weston_surface_update_transform(surface);
+ shell_fade(shell, FADE_IN);
+ }
+}
+
+static void
+handle_lock_surface_destroy(struct wl_listener *listener, void *data)
+{
+ struct desktop_shell *shell =
+ container_of(listener, struct desktop_shell, lock_surface_listener);
+
+ weston_log("lock surface gone\n");
+ shell->lock_surface = NULL;
+}
+
+static void
+desktop_shell_set_lock_surface(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *surface_resource)
+{
+ struct desktop_shell *shell = wl_resource_get_user_data(resource);
+ struct weston_surface *surface =
+ wl_resource_get_user_data(surface_resource);
+
+ shell->prepare_event_sent = false;
+
+ if (!shell->locked)
+ return;
+
+ shell->lock_surface = surface;
+
+ shell->lock_surface_listener.notify = handle_lock_surface_destroy;
+ wl_signal_add(&surface->destroy_signal,
+ &shell->lock_surface_listener);
+
+ surface->configure = lock_surface_configure;
+ surface->configure_private = shell;
+}
+
+static void
+resume_desktop(struct desktop_shell *shell)
+{
+ struct workspace *ws = get_current_workspace(shell);
+
+ terminate_screensaver(shell);
+
+ wl_list_remove(&shell->lock_layer.link);
+ wl_list_insert(&shell->compositor->cursor_layer.link,
+ &shell->fullscreen_layer.link);
+ wl_list_insert(&shell->fullscreen_layer.link,
+ &shell->panel_layer.link);
+ if (shell->showing_input_panels) {
+ wl_list_insert(&shell->panel_layer.link,
+ &shell->input_panel_layer.link);
+ wl_list_insert(&shell->input_panel_layer.link,
+ &ws->layer.link);
+ } else {
+ wl_list_insert(&shell->panel_layer.link, &ws->layer.link);
+ }
+
+ restore_focus_state(shell, get_current_workspace(shell));
+
+ shell->locked = false;
+ shell_fade(shell, FADE_IN);
+ weston_compositor_damage_all(shell->compositor);
+}
+
+static void
+desktop_shell_unlock(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ struct desktop_shell *shell = wl_resource_get_user_data(resource);
+
+ shell->prepare_event_sent = false;
+
+ if (shell->locked)
+ resume_desktop(shell);
+}
+
+static void
+desktop_shell_set_grab_surface(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *surface_resource)
+{
+ struct desktop_shell *shell = wl_resource_get_user_data(resource);
+
+ shell->grab_surface = wl_resource_get_user_data(surface_resource);
+}
+
+static void
+desktop_shell_desktop_ready(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ struct desktop_shell *shell = wl_resource_get_user_data(resource);
+
+ shell_fade_startup(shell);
+}
+
+static const struct desktop_shell_interface desktop_shell_implementation = {
+ desktop_shell_set_background,
+ desktop_shell_set_panel,
+ desktop_shell_set_lock_surface,
+ desktop_shell_unlock,
+ desktop_shell_set_grab_surface,
+ desktop_shell_desktop_ready
+};
+
+static enum shell_surface_type
+get_shell_surface_type(struct weston_surface *surface)
+{
+ struct shell_surface *shsurf;
+
+ shsurf = get_shell_surface(surface);
+ if (!shsurf)
+ return SHELL_SURFACE_NONE;
+ return shsurf->type;
+}
+
+static void
+move_binding(struct weston_seat *seat, uint32_t time, uint32_t button, void *data)
+{
+ struct weston_surface *focus =
+ (struct weston_surface *) seat->pointer->focus;
+ struct weston_surface *surface;
+ struct shell_surface *shsurf;
+
+ surface = weston_surface_get_main_surface(focus);
+ if (surface == NULL)
+ return;
+
+ shsurf = get_shell_surface(surface);
+ if (shsurf == NULL || shsurf->type == SHELL_SURFACE_FULLSCREEN ||
+ shsurf->type == SHELL_SURFACE_MAXIMIZED)
+ return;
+
+ surface_move(shsurf, (struct weston_seat *) seat);
+}
+
+static void
+touch_move_binding(struct weston_seat *seat, uint32_t time, void *data)
+{
+ struct weston_surface *focus =
+ (struct weston_surface *) seat->touch->focus;
+ struct weston_surface *surface;
+ struct shell_surface *shsurf;
+
+ surface = weston_surface_get_main_surface(focus);
+ if (surface == NULL)
+ return;
+
+ shsurf = get_shell_surface(surface);
+ if (shsurf == NULL || shsurf->type == SHELL_SURFACE_FULLSCREEN ||
+ shsurf->type == SHELL_SURFACE_MAXIMIZED)
+ return;
+
+ surface_touch_move(shsurf, (struct weston_seat *) seat);
+}
+
+static void
+resize_binding(struct weston_seat *seat, uint32_t time, uint32_t button, void *data)
+{
+ struct weston_surface *focus =
+ (struct weston_surface *) seat->pointer->focus;
+ struct weston_surface *surface;
+ uint32_t edges = 0;
+ int32_t x, y;
+ struct shell_surface *shsurf;
+
+ surface = weston_surface_get_main_surface(focus);
+ if (surface == NULL)
+ return;
+
+ shsurf = get_shell_surface(surface);
+ if (!shsurf || shsurf->type == SHELL_SURFACE_FULLSCREEN ||
+ shsurf->type == SHELL_SURFACE_MAXIMIZED)
+ return;
+
+ weston_surface_from_global(surface,
+ wl_fixed_to_int(seat->pointer->grab_x),
+ wl_fixed_to_int(seat->pointer->grab_y),
+ &x, &y);
+
+ if (x < surface->geometry.width / 3)
+ edges |= WL_SHELL_SURFACE_RESIZE_LEFT;
+ else if (x < 2 * surface->geometry.width / 3)
+ edges |= 0;
+ else
+ edges |= WL_SHELL_SURFACE_RESIZE_RIGHT;
+
+ if (y < surface->geometry.height / 3)
+ edges |= WL_SHELL_SURFACE_RESIZE_TOP;
+ else if (y < 2 * surface->geometry.height / 3)
+ edges |= 0;
+ else
+ edges |= WL_SHELL_SURFACE_RESIZE_BOTTOM;
+
+ surface_resize(shsurf, (struct weston_seat *) seat, edges);
+}
+
+static void
+surface_opacity_binding(struct weston_seat *seat, uint32_t time, uint32_t axis,
+ wl_fixed_t value, void *data)
+{
+ float step = 0.005;
+ struct shell_surface *shsurf;
+ struct weston_surface *focus =
+ (struct weston_surface *) seat->pointer->focus;
+ struct weston_surface *surface;
+
+ /* XXX: broken for windows containing sub-surfaces */
+ surface = weston_surface_get_main_surface(focus);
+ if (surface == NULL)
+ return;
+
+ shsurf = get_shell_surface(surface);
+ if (!shsurf)
+ return;
+
+ surface->alpha -= wl_fixed_to_double(value) * step;
+
+ if (surface->alpha > 1.0)
+ surface->alpha = 1.0;
+ if (surface->alpha < step)
+ surface->alpha = step;
+
+ weston_surface_geometry_dirty(surface);
+ weston_surface_damage(surface);
+}
+
+static void
+do_zoom(struct weston_seat *seat, uint32_t time, uint32_t key, uint32_t axis,
+ wl_fixed_t value)
+{
+ struct weston_seat *ws = (struct weston_seat *) seat;
+ struct weston_compositor *compositor = ws->compositor;
+ struct weston_output *output;
+ float increment;
+
+ wl_list_for_each(output, &compositor->output_list, link) {
+ if (pixman_region32_contains_point(&output->region,
+ wl_fixed_to_double(seat->pointer->x),
+ wl_fixed_to_double(seat->pointer->y),
+ NULL)) {
+ if (key == KEY_PAGEUP)
+ increment = output->zoom.increment;
+ else if (key == KEY_PAGEDOWN)
+ increment = -output->zoom.increment;
+ else if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL)
+ /* For every pixel zoom 20th of a step */
+ increment = output->zoom.increment *
+ -wl_fixed_to_double(value) / 20.0;
+ else
+ increment = 0;
+
+ output->zoom.level += increment;
+
+ if (output->zoom.level < 0.0)
+ output->zoom.level = 0.0;
+ else if (output->zoom.level > output->zoom.max_level)
+ output->zoom.level = output->zoom.max_level;
+ else if (!output->zoom.active) {
+ output->zoom.active = 1;
+ output->disable_planes++;
+ }
+
+ output->zoom.spring_z.target = output->zoom.level;
+
+ weston_output_update_zoom(output, output->zoom.type);
+ }
+ }
+}
+
+static void
+zoom_axis_binding(struct weston_seat *seat, uint32_t time, uint32_t axis,
+ wl_fixed_t value, void *data)
+{
+ do_zoom(seat, time, 0, axis, value);
+}
+
+static void
+zoom_key_binding(struct weston_seat *seat, uint32_t time, uint32_t key,
+ void *data)
+{
+ do_zoom(seat, time, key, 0, 0);
+}
+
+static void
+terminate_binding(struct weston_seat *seat, uint32_t time, uint32_t key,
+ void *data)
+{
+ struct weston_compositor *compositor = data;
+
+ wl_display_terminate(compositor->wl_display);
+}
+
+static void
+rotate_grab_motion(struct weston_pointer_grab *grab, uint32_t time)
+{
+ struct rotate_grab *rotate =
+ container_of(grab, struct rotate_grab, base.grab);
+ struct weston_pointer *pointer = grab->pointer;
+ struct shell_surface *shsurf = rotate->base.shsurf;
+ struct weston_surface *surface;
+ float cx, cy, dx, dy, cposx, cposy, dposx, dposy, r;
+
+ if (!shsurf)
+ return;
+
+ surface = shsurf->surface;
+
+ cx = 0.5f * surface->geometry.width;
+ cy = 0.5f * surface->geometry.height;
+
+ dx = wl_fixed_to_double(pointer->x) - rotate->center.x;
+ dy = wl_fixed_to_double(pointer->y) - rotate->center.y;
+ r = sqrtf(dx * dx + dy * dy);
+
+ wl_list_remove(&shsurf->rotation.transform.link);
+ weston_surface_geometry_dirty(shsurf->surface);
+
+ if (r > 20.0f) {
+ struct weston_matrix *matrix =
+ &shsurf->rotation.transform.matrix;
+
+ weston_matrix_init(&rotate->rotation);
+ weston_matrix_rotate_xy(&rotate->rotation, dx / r, dy / r);
+
+ weston_matrix_init(matrix);
+ weston_matrix_translate(matrix, -cx, -cy, 0.0f);
+ weston_matrix_multiply(matrix, &shsurf->rotation.rotation);
+ weston_matrix_multiply(matrix, &rotate->rotation);
+ weston_matrix_translate(matrix, cx, cy, 0.0f);
+
+ wl_list_insert(
+ &shsurf->surface->geometry.transformation_list,
+ &shsurf->rotation.transform.link);
+ } else {
+ wl_list_init(&shsurf->rotation.transform.link);
+ weston_matrix_init(&shsurf->rotation.rotation);
+ weston_matrix_init(&rotate->rotation);
+ }
+
+ /* We need to adjust the position of the surface
+ * in case it was resized in a rotated state before */
+ cposx = surface->geometry.x + cx;
+ cposy = surface->geometry.y + cy;
+ dposx = rotate->center.x - cposx;
+ dposy = rotate->center.y - cposy;
+ if (dposx != 0.0f || dposy != 0.0f) {
+ weston_surface_set_position(surface,
+ surface->geometry.x + dposx,
+ surface->geometry.y + dposy);
+ }
+
+ /* Repaint implies weston_surface_update_transform(), which
+ * lazily applies the damage due to rotation update.
+ */
+ weston_compositor_schedule_repaint(shsurf->surface->compositor);
+}
+
+static void
+rotate_grab_button(struct weston_pointer_grab *grab,
+ uint32_t time, uint32_t button, uint32_t state_w)
+{
+ struct rotate_grab *rotate =
+ container_of(grab, struct rotate_grab, base.grab);
+ struct weston_pointer *pointer = grab->pointer;
+ struct shell_surface *shsurf = rotate->base.shsurf;
+ enum wl_pointer_button_state state = state_w;
+
+ if (pointer->button_count == 0 &&
+ state == WL_POINTER_BUTTON_STATE_RELEASED) {
+ if (shsurf)
+ weston_matrix_multiply(&shsurf->rotation.rotation,
+ &rotate->rotation);
+ shell_grab_end(&rotate->base);
+ free(rotate);
+ }
+}
+
+static void
+rotate_grab_cancel(struct weston_pointer_grab *grab)
+{
+ struct rotate_grab *rotate =
+ container_of(grab, struct rotate_grab, base.grab);
+
+ shell_grab_end(&rotate->base);
+ free(rotate);
+}
+
+static const struct weston_pointer_grab_interface rotate_grab_interface = {
+ noop_grab_focus,
+ rotate_grab_motion,
+ rotate_grab_button,
+ rotate_grab_cancel,
+};
+
+static void
+surface_rotate(struct shell_surface *surface, struct weston_seat *seat)
+{
+ struct rotate_grab *rotate;
+ float dx, dy;
+ float r;
+
+ rotate = malloc(sizeof *rotate);
+ if (!rotate)
+ return;
+
+ weston_surface_to_global_float(surface->surface,
+ surface->surface->geometry.width * 0.5f,
+ surface->surface->geometry.height * 0.5f,
+ &rotate->center.x, &rotate->center.y);
+
+ dx = wl_fixed_to_double(seat->pointer->x) - rotate->center.x;
+ dy = wl_fixed_to_double(seat->pointer->y) - rotate->center.y;
+ r = sqrtf(dx * dx + dy * dy);
+ if (r > 20.0f) {
+ struct weston_matrix inverse;
+
+ weston_matrix_init(&inverse);
+ weston_matrix_rotate_xy(&inverse, dx / r, -dy / r);
+ weston_matrix_multiply(&surface->rotation.rotation, &inverse);
+
+ weston_matrix_init(&rotate->rotation);
+ weston_matrix_rotate_xy(&rotate->rotation, dx / r, dy / r);
+ } else {
+ weston_matrix_init(&surface->rotation.rotation);
+ weston_matrix_init(&rotate->rotation);
+ }
+
+ shell_grab_start(&rotate->base, &rotate_grab_interface, surface,
+ seat->pointer, DESKTOP_SHELL_CURSOR_ARROW);
+}
+
+static void
+rotate_binding(struct weston_seat *seat, uint32_t time, uint32_t button,
+ void *data)
+{
+ struct weston_surface *focus =
+ (struct weston_surface *) seat->pointer->focus;
+ struct weston_surface *base_surface;
+ struct shell_surface *surface;
+
+ base_surface = weston_surface_get_main_surface(focus);
+ if (base_surface == NULL)
+ return;
+
+ surface = get_shell_surface(base_surface);
+ if (!surface || surface->type == SHELL_SURFACE_FULLSCREEN ||
+ surface->type == SHELL_SURFACE_MAXIMIZED)
+ return;
+
+ surface_rotate(surface, seat);
+}
+
+static void
+lower_fullscreen_layer(struct desktop_shell *shell)
+{
+ struct workspace *ws;
+ struct weston_surface *surface, *prev;
+
+ ws = get_current_workspace(shell);
+ wl_list_for_each_reverse_safe(surface, prev,
+ &shell->fullscreen_layer.surface_list,
+ layer_link)
+ weston_surface_restack(surface, &ws->layer.surface_list);
+}
+
+static void
+activate(struct desktop_shell *shell, struct weston_surface *es,
+ struct weston_seat *seat)
+{
+ struct weston_surface *main_surface;
+ struct focus_state *state;
+ struct workspace *ws;
+
+ main_surface = weston_surface_get_main_surface(es);
+
+ weston_surface_activate(es, seat);
+
+ state = ensure_focus_state(shell, seat);
+ if (state == NULL)
+ return;
+
+ state->keyboard_focus = es;
+ wl_list_remove(&state->surface_destroy_listener.link);
+ wl_signal_add(&es->destroy_signal, &state->surface_destroy_listener);
+
+ switch (get_shell_surface_type(main_surface)) {
+ case SHELL_SURFACE_FULLSCREEN:
+ /* should on top of panels */
+ shell_stack_fullscreen(get_shell_surface(main_surface));
+ shell_configure_fullscreen(get_shell_surface(main_surface));
+ break;
+ default:
+ restore_all_output_modes(shell->compositor);
+ ws = get_current_workspace(shell);
+ weston_surface_restack(main_surface, &ws->layer.surface_list);
+ break;
+ }
+}
+
+/* no-op func for checking black surface */
+static void
+black_surface_configure(struct weston_surface *es, int32_t sx, int32_t sy, int32_t width, int32_t height)
+{
+}
+
+static bool
+is_black_surface (struct weston_surface *es, struct weston_surface **fs_surface)
+{
+ if (es->configure == black_surface_configure) {
+ if (fs_surface)
+ *fs_surface = (struct weston_surface *)es->configure_private;
+ return true;
+ }
+ return false;
+}
+
+static void
+activate_binding(struct weston_seat *seat,
+ struct desktop_shell *shell,
+ struct weston_surface *focus)
+{
+ struct weston_surface *main_surface;
+
+ if (!focus)
+ return;
+
+ if (is_black_surface(focus, &main_surface))
+ focus = main_surface;
+
+ main_surface = weston_surface_get_main_surface(focus);
+ if (get_shell_surface_type(main_surface) == SHELL_SURFACE_NONE)
+ return;
+
+ activate(shell, focus, seat);
+}
+
+static void
+click_to_activate_binding(struct weston_seat *seat, uint32_t time, uint32_t button,
+ void *data)
+{
+ if (seat->pointer->grab != &seat->pointer->default_grab)
+ return;
+
+ activate_binding(seat, data,
+ (struct weston_surface *) seat->pointer->focus);
+}
+
+static void
+touch_to_activate_binding(struct weston_seat *seat, uint32_t time, void *data)
+{
+ if (seat->touch->grab != &seat->touch->default_grab)
+ return;
+
+ activate_binding(seat, data,
+ (struct weston_surface *) seat->touch->focus);
+}
+
+static void
+lock(struct desktop_shell *shell)
+{
+ struct workspace *ws = get_current_workspace(shell);
+
+ if (shell->locked) {
+ weston_compositor_sleep(shell->compositor);
+ return;
+ }
+
+ shell->locked = true;
+
+ /* Hide all surfaces by removing the fullscreen, panel and
+ * toplevel layers. This way nothing else can show or receive
+ * input events while we are locked. */
+
+ wl_list_remove(&shell->panel_layer.link);
+ wl_list_remove(&shell->fullscreen_layer.link);
+ if (shell->showing_input_panels)
+ wl_list_remove(&shell->input_panel_layer.link);
+ wl_list_remove(&ws->layer.link);
+ wl_list_insert(&shell->compositor->cursor_layer.link,
+ &shell->lock_layer.link);
+
+ launch_screensaver(shell);
+
+ /* TODO: disable bindings that should not work while locked. */
+
+ /* All this must be undone in resume_desktop(). */
+}
+
+static void
+unlock(struct desktop_shell *shell)
+{
+ if (!shell->locked || shell->lock_surface) {
+ shell_fade(shell, FADE_IN);
+ return;
+ }
+
+ /* If desktop-shell client has gone away, unlock immediately. */
+ if (!shell->child.desktop_shell) {
+ resume_desktop(shell);
+ return;
+ }
+
+ if (shell->prepare_event_sent)
+ return;
+
+ desktop_shell_send_prepare_lock_surface(shell->child.desktop_shell);
+ shell->prepare_event_sent = true;
+}
+
+static void
+shell_fade_done(struct weston_surface_animation *animation, void *data)
+{
+ struct desktop_shell *shell = data;
+
+ shell->fade.animation = NULL;
+
+ switch (shell->fade.type) {
+ case FADE_IN:
+ weston_surface_destroy(shell->fade.surface);
+ shell->fade.surface = NULL;
+ break;
+ case FADE_OUT:
+ lock(shell);
+ break;
+ }
+}
+
+static struct weston_surface *
+shell_fade_create_surface(struct desktop_shell *shell)
+{
+ struct weston_compositor *compositor = shell->compositor;
+ struct weston_surface *surface;
+
+ surface = weston_surface_create(compositor);
+ if (!surface)
+ return NULL;
+
+ weston_surface_configure(surface, 0, 0, 8192, 8192);
+ weston_surface_set_color(surface, 0.0, 0.0, 0.0, 1.0);
+ wl_list_insert(&compositor->fade_layer.surface_list,
+ &surface->layer_link);
+ pixman_region32_init(&surface->input);
+
+ return surface;
+}
+
+static void
+shell_fade(struct desktop_shell *shell, enum fade_type type)
+{
+ float tint;
+
+ switch (type) {
+ case FADE_IN:
+ tint = 0.0;
+ break;
+ case FADE_OUT:
+ tint = 1.0;
+ break;
+ default:
+ weston_log("shell: invalid fade type\n");
+ return;
+ }
+
+ shell->fade.type = type;
+
+ if (shell->fade.surface == NULL) {
+ shell->fade.surface = shell_fade_create_surface(shell);
+ if (!shell->fade.surface)
+ return;
+
+ shell->fade.surface->alpha = 1.0 - tint;
+ weston_surface_update_transform(shell->fade.surface);
+ }
+
+ if (shell->fade.animation)
+ weston_fade_update(shell->fade.animation, tint);
+ else
+ shell->fade.animation =
+ weston_fade_run(shell->fade.surface,
+ 1.0 - tint, tint, 300.0,
+ shell_fade_done, shell);
+}
+
+static void
+do_shell_fade_startup(void *data)
+{
+ struct desktop_shell *shell = data;
+
+ if (shell->startup_animation_type == ANIMATION_FADE)
+ shell_fade(shell, FADE_IN);
+ else if (shell->startup_animation_type == ANIMATION_NONE) {
+ weston_surface_destroy(shell->fade.surface);
+ shell->fade.surface = NULL;
+ }
+}
+
+static void
+shell_fade_startup(struct desktop_shell *shell)
+{
+ struct wl_event_loop *loop;
+
+ if (!shell->fade.startup_timer)
+ return;
+
+ wl_event_source_remove(shell->fade.startup_timer);
+ shell->fade.startup_timer = NULL;
+
+ loop = wl_display_get_event_loop(shell->compositor->wl_display);
+ wl_event_loop_add_idle(loop, do_shell_fade_startup, shell);
+}
+
+static int
+fade_startup_timeout(void *data)
+{
+ struct desktop_shell *shell = data;
+
+ shell_fade_startup(shell);
+ return 0;
+}
+
+static void
+shell_fade_init(struct desktop_shell *shell)
+{
+ /* Make compositor output all black, and wait for the desktop-shell
+ * client to signal it is ready, then fade in. The timer triggers a
+ * fade-in, in case the desktop-shell client takes too long.
+ */
+
+ struct wl_event_loop *loop;
+
+ if (shell->fade.surface != NULL) {
+ weston_log("%s: warning: fade surface already exists\n",
+ __func__);
+ return;
+ }
+
+ shell->fade.surface = shell_fade_create_surface(shell);
+ if (!shell->fade.surface)
+ return;
+
+ weston_surface_update_transform(shell->fade.surface);
+ weston_surface_damage(shell->fade.surface);
+
+ loop = wl_display_get_event_loop(shell->compositor->wl_display);
+ shell->fade.startup_timer =
+ wl_event_loop_add_timer(loop, fade_startup_timeout, shell);
+ wl_event_source_timer_update(shell->fade.startup_timer, 15000);
+}
+
+static void
+idle_handler(struct wl_listener *listener, void *data)
+{
+ struct desktop_shell *shell =
+ container_of(listener, struct desktop_shell, idle_listener);
+
+ shell_fade(shell, FADE_OUT);
+ /* lock() is called from shell_fade_done() */
+}
+
+static void
+wake_handler(struct wl_listener *listener, void *data)
+{
+ struct desktop_shell *shell =
+ container_of(listener, struct desktop_shell, wake_listener);
+
+ unlock(shell);
+}
+
+static void
+show_input_panels(struct wl_listener *listener, void *data)
+{
+ struct desktop_shell *shell =
+ container_of(listener, struct desktop_shell,
+ show_input_panel_listener);
+ struct input_panel_surface *surface, *next;
+ struct weston_surface *ws;
+
+ shell->text_input.surface = (struct weston_surface*)data;
+
+ if (shell->showing_input_panels)
+ return;
+
+ shell->showing_input_panels = true;
+
+ if (!shell->locked)
+ wl_list_insert(&shell->panel_layer.link,
+ &shell->input_panel_layer.link);
+
+ wl_list_for_each_safe(surface, next,
+ &shell->input_panel.surfaces, link) {
+ ws = surface->surface;
+ if (!ws->buffer_ref.buffer)
+ continue;
+ wl_list_insert(&shell->input_panel_layer.surface_list,
+ &ws->layer_link);
+ weston_surface_geometry_dirty(ws);
+ weston_surface_update_transform(ws);
+ weston_surface_damage(ws);
+ weston_slide_run(ws, ws->geometry.height, 0, NULL, NULL);
+ }
+}
+
+static void
+hide_input_panels(struct wl_listener *listener, void *data)
+{
+ struct desktop_shell *shell =
+ container_of(listener, struct desktop_shell,
+ hide_input_panel_listener);
+ struct weston_surface *surface, *next;
+
+ if (!shell->showing_input_panels)
+ return;
+
+ shell->showing_input_panels = false;
+
+ if (!shell->locked)
+ wl_list_remove(&shell->input_panel_layer.link);
+
+ wl_list_for_each_safe(surface, next,
+ &shell->input_panel_layer.surface_list, layer_link)
+ weston_surface_unmap(surface);
+}
+
+static void
+update_input_panels(struct wl_listener *listener, void *data)
+{
+ struct desktop_shell *shell =
+ container_of(listener, struct desktop_shell,
+ update_input_panel_listener);
+
+ memcpy(&shell->text_input.cursor_rectangle, data, sizeof(pixman_box32_t));
+}
+
+static void
+center_on_output(struct weston_surface *surface, struct weston_output *output)
+{
+ int32_t surf_x, surf_y, width, height;
+ float x, y;
+
+ surface_subsurfaces_boundingbox(surface, &surf_x, &surf_y, &width, &height);
+
+ x = output->x + (output->width - width) / 2 - surf_x / 2;
+ y = output->y + (output->height - height) / 2 - surf_y / 2;
+
+ weston_surface_configure(surface, x, y, width, height);
+}
+
+static void
+weston_surface_set_initial_position (struct weston_surface *surface,
+ struct desktop_shell *shell)
+{
+ struct weston_compositor *compositor = shell->compositor;
+ int ix = 0, iy = 0;
+ int range_x, range_y;
+ int dx, dy, x, y, panel_height;
+ struct weston_output *output, *target_output = NULL;
+ struct weston_seat *seat;
+
+ /* As a heuristic place the new window on the same output as the
+ * pointer. Falling back to the output containing 0, 0.
+ *
+ * TODO: Do something clever for touch too?
+ */
+ wl_list_for_each(seat, &compositor->seat_list, link) {
+ if (seat->pointer) {
+ ix = wl_fixed_to_int(seat->pointer->x);
+ iy = wl_fixed_to_int(seat->pointer->y);
+ break;
+ }
+ }
+
+ wl_list_for_each(output, &compositor->output_list, link) {
+ if (pixman_region32_contains_point(&output->region, ix, iy, NULL)) {
+ target_output = output;
+ break;
+ }
+ }
+
+ if (!target_output) {
+ weston_surface_set_position(surface, 10 + random() % 400,
+ 10 + random() % 400);
+ return;
+ }
+
+ /* Valid range within output where the surface will still be onscreen.
+ * If this is negative it means that the surface is bigger than
+ * output.
+ */
+ panel_height = get_output_panel_height(shell, target_output);
+ range_x = target_output->width - surface->geometry.width;
+ range_y = (target_output->height - panel_height) -
+ surface->geometry.height;
+
+ if (range_x > 0)
+ dx = random() % range_x;
+ else
+ dx = 0;
+
+ if (range_y > 0)
+ dy = panel_height + random() % range_y;
+ else
+ dy = panel_height;
+
+ x = target_output->x + dx;
+ y = target_output->y + dy;
+
+ weston_surface_set_position (surface, x, y);
+}
+
+static void
+map(struct desktop_shell *shell, struct weston_surface *surface,
+ int32_t width, int32_t height, int32_t sx, int32_t sy)
+{
+ struct weston_compositor *compositor = shell->compositor;
+ struct shell_surface *shsurf = get_shell_surface(surface);
+ enum shell_surface_type surface_type = shsurf->type;
+ struct weston_surface *parent;
+ struct weston_seat *seat;
+ struct workspace *ws;
+ int panel_height = 0;
+ int32_t surf_x, surf_y;
+
+ surface->geometry.width = width;
+ surface->geometry.height = height;
+ weston_surface_geometry_dirty(surface);
+
+ /* initial positioning, see also configure() */
+ switch (surface_type) {
+ case SHELL_SURFACE_TOPLEVEL:
+ weston_surface_set_initial_position(surface, shell);
+ break;
+ case SHELL_SURFACE_FULLSCREEN:
+ center_on_output(surface, shsurf->fullscreen_output);
+ shell_map_fullscreen(shsurf);
+ break;
+ case SHELL_SURFACE_MAXIMIZED:
+ /* use surface configure to set the geometry */
+ panel_height = get_output_panel_height(shell,surface->output);
+ surface_subsurfaces_boundingbox(shsurf->surface, &surf_x, &surf_y,
+ NULL, NULL);
+ weston_surface_set_position(surface, shsurf->output->x - surf_x,
+ shsurf->output->y + panel_height - surf_y);
+ break;
+ case SHELL_SURFACE_POPUP:
+ shell_map_popup(shsurf);
+ break;
+ case SHELL_SURFACE_NONE:
+ weston_surface_set_position(surface,
+ surface->geometry.x + sx,
+ surface->geometry.y + sy);
+ break;
+ default:
+ ;
+ }
+
+ /* surface stacking order, see also activate() */
+ switch (surface_type) {
+ case SHELL_SURFACE_POPUP:
+ case SHELL_SURFACE_TRANSIENT:
+ parent = shsurf->parent;
+ wl_list_insert(parent->layer_link.prev, &surface->layer_link);
+ break;
+ case SHELL_SURFACE_FULLSCREEN:
+ case SHELL_SURFACE_NONE:
+ break;
+ case SHELL_SURFACE_XWAYLAND:
+ default:
+ ws = get_current_workspace(shell);
+ wl_list_insert(&ws->layer.surface_list, &surface->layer_link);
+ break;
+ }
+
+ if (surface_type != SHELL_SURFACE_NONE) {
+ weston_surface_update_transform(surface);
+ if (surface_type == SHELL_SURFACE_MAXIMIZED)
+ surface->output = shsurf->output;
+ }
+
+ switch (surface_type) {
+ /* XXX: xwayland's using the same fields for transient type */
+ case SHELL_SURFACE_XWAYLAND:
+ case SHELL_SURFACE_TRANSIENT:
+ if (shsurf->transient.flags ==
+ WL_SHELL_SURFACE_TRANSIENT_INACTIVE)
+ break;
+ case SHELL_SURFACE_TOPLEVEL:
+ case SHELL_SURFACE_FULLSCREEN:
+ case SHELL_SURFACE_MAXIMIZED:
+ if (!shell->locked) {
+ wl_list_for_each(seat, &compositor->seat_list, link)
+ activate(shell, surface, seat);
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (surface_type == SHELL_SURFACE_TOPLEVEL)
+ {
+ switch (shell->win_animation_type) {
+ case ANIMATION_FADE:
+ weston_fade_run(surface, 0.0, 1.0, 300.0, NULL, NULL);
+ break;
+ case ANIMATION_ZOOM:
+ weston_zoom_run(surface, 0.5, 1.0, NULL, NULL);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void
+configure(struct desktop_shell *shell, struct weston_surface *surface,
+ float x, float y, int32_t width, int32_t height)
+{
+ enum shell_surface_type surface_type = SHELL_SURFACE_NONE;
+ struct shell_surface *shsurf;
+ int32_t surf_x, surf_y;
+
+ shsurf = get_shell_surface(surface);
+ if (shsurf)
+ surface_type = shsurf->type;
+
+ weston_surface_configure(surface, x, y, width, height);
+
+ switch (surface_type) {
+ case SHELL_SURFACE_FULLSCREEN:
+ shell_stack_fullscreen(shsurf);
+ shell_configure_fullscreen(shsurf);
+ break;
+ case SHELL_SURFACE_MAXIMIZED:
+ /* setting x, y and using configure to change that geometry */
+ surface_subsurfaces_boundingbox(shsurf->surface, &surf_x, &surf_y,
+ NULL, NULL);
+ surface->geometry.x = surface->output->x - surf_x;
+ surface->geometry.y = surface->output->y +
+ get_output_panel_height(shell,surface->output) - surf_y;
+ break;
+ case SHELL_SURFACE_TOPLEVEL:
+ break;
+ default:
+ break;
+ }
+
+ /* XXX: would a fullscreen surface need the same handling? */
+ if (surface->output) {
+ weston_surface_update_transform(surface);
+
+ if (surface_type == SHELL_SURFACE_MAXIMIZED)
+ surface->output = shsurf->output;
+ }
+}
+
+static void
+shell_surface_configure(struct weston_surface *es, int32_t sx, int32_t sy, int32_t width, int32_t height)
+{
+ struct shell_surface *shsurf = get_shell_surface(es);
+ struct desktop_shell *shell = shsurf->shell;
+
+ int type_changed = 0;
+
+ if (!weston_surface_is_mapped(es) &&
+ !wl_list_empty(&shsurf->popup.grab_link)) {
+ remove_popup_grab(shsurf);
+ }
+
+ if (width == 0)
+ return;
+
+ if (shsurf->next_type != SHELL_SURFACE_NONE &&
+ shsurf->type != shsurf->next_type) {
+ set_surface_type(shsurf);
+ type_changed = 1;
+ }
+
+ if (!weston_surface_is_mapped(es)) {
+ map(shell, es, width, height, sx, sy);
+ } else if (type_changed || sx != 0 || sy != 0 ||
+ es->geometry.width != width ||
+ es->geometry.height != height) {
+ float from_x, from_y;
+ float to_x, to_y;
+
+ weston_surface_to_global_float(es, 0, 0, &from_x, &from_y);
+ weston_surface_to_global_float(es, sx, sy, &to_x, &to_y);
+ configure(shell, es,
+ es->geometry.x + to_x - from_x,
+ es->geometry.y + to_y - from_y,
+ width, height);
+ }
+}
+
+static void launch_desktop_shell_process(void *data);
+
+static void
+desktop_shell_sigchld(struct weston_process *process, int status)
+{
+ uint32_t time;
+ struct desktop_shell *shell =
+ container_of(process, struct desktop_shell, child.process);
+
+ shell->child.process.pid = 0;
+ shell->child.client = NULL; /* already destroyed by wayland */
+
+ /* if desktop-shell dies more than 5 times in 30 seconds, give up */
+ time = weston_compositor_get_time();
+ if (time - shell->child.deathstamp > 30000) {
+ shell->child.deathstamp = time;
+ shell->child.deathcount = 0;
+ }
+
+ shell->child.deathcount++;
+ if (shell->child.deathcount > 5) {
+ weston_log("weston-desktop-shell died, giving up.\n");
+ return;
+ }
+
+ weston_log("weston-desktop-shell died, respawning...\n");
+ launch_desktop_shell_process(shell);
+ shell_fade_startup(shell);
+}
+
+static void
+launch_desktop_shell_process(void *data)
+{
+ struct desktop_shell *shell = data;
+ const char *shell_exe = LIBEXECDIR "/weston-desktop-shell";
+
+ shell->child.client = weston_client_launch(shell->compositor,
+ &shell->child.process,
+ shell_exe,
+ desktop_shell_sigchld);
+
+ if (!shell->child.client)
+ weston_log("not able to start %s\n", shell_exe);
+}
+
+static void
+bind_shell(struct wl_client *client, void *data, uint32_t version, uint32_t id)
+{
+ struct desktop_shell *shell = data;
+ struct wl_resource *resource;
+
+ resource = wl_resource_create(client, &wl_shell_interface, 1, id);
+ if (resource)
+ wl_resource_set_implementation(resource, &shell_implementation,
+ shell, NULL);
+}
+
+static void
+unbind_desktop_shell(struct wl_resource *resource)
+{
+ struct desktop_shell *shell = wl_resource_get_user_data(resource);
+
+ if (shell->locked)
+ resume_desktop(shell);
+
+ shell->child.desktop_shell = NULL;
+ shell->prepare_event_sent = false;
+}
+
+static void
+bind_desktop_shell(struct wl_client *client,
+ void *data, uint32_t version, uint32_t id)
+{
+ struct desktop_shell *shell = data;
+ struct wl_resource *resource;
+
+ resource = wl_resource_create(client, &desktop_shell_interface,
+ MIN(version, 2), id);
+
+ if (client == shell->child.client) {
+ wl_resource_set_implementation(resource,
+ &desktop_shell_implementation,
+ shell, unbind_desktop_shell);
+ shell->child.desktop_shell = resource;
+
+ if (version < 2)
+ shell_fade_startup(shell);
+
+ return;
+ }
+
+ wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
+ "permission to bind desktop_shell denied");
+ wl_resource_destroy(resource);
+}
+
+static void
+screensaver_configure(struct weston_surface *surface, int32_t sx, int32_t sy, int32_t width, int32_t height)
+{
+ struct desktop_shell *shell = surface->configure_private;
+
+ if (width == 0)
+ return;
+
+ /* XXX: starting weston-screensaver beforehand does not work */
+ if (!shell->locked)
+ return;
+
+ center_on_output(surface, surface->output);
+
+ if (wl_list_empty(&surface->layer_link)) {
+ wl_list_insert(shell->lock_layer.surface_list.prev,
+ &surface->layer_link);
+ weston_surface_update_transform(surface);
+ wl_event_source_timer_update(shell->screensaver.timer,
+ shell->screensaver.duration);
+ shell_fade(shell, FADE_IN);
+ }
+}
+
+static void
+screensaver_set_surface(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *surface_resource,
+ struct wl_resource *output_resource)
+{
+ struct desktop_shell *shell = wl_resource_get_user_data(resource);
+ struct weston_surface *surface =
+ wl_resource_get_user_data(surface_resource);
+ struct weston_output *output = wl_resource_get_user_data(output_resource);
+
+ surface->configure = screensaver_configure;
+ surface->configure_private = shell;
+ surface->output = output;
+}
+
+static const struct screensaver_interface screensaver_implementation = {
+ screensaver_set_surface
+};
+
+static void
+unbind_screensaver(struct wl_resource *resource)
+{
+ struct desktop_shell *shell = wl_resource_get_user_data(resource);
+
+ shell->screensaver.binding = NULL;
+}
+
+static void
+bind_screensaver(struct wl_client *client,
+ void *data, uint32_t version, uint32_t id)
+{
+ struct desktop_shell *shell = data;
+ struct wl_resource *resource;
+
+ resource = wl_resource_create(client, &screensaver_interface, 1, id);
+
+ if (shell->screensaver.binding == NULL) {
+ wl_resource_set_implementation(resource,
+ &screensaver_implementation,
+ shell, unbind_screensaver);
+ shell->screensaver.binding = resource;
+ return;
+ }
+
+ wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
+ "interface object already bound");
+ wl_resource_destroy(resource);
+}
+
+static void
+input_panel_configure(struct weston_surface *surface, int32_t sx, int32_t sy, int32_t width, int32_t height)
+{
+ struct input_panel_surface *ip_surface = surface->configure_private;
+ struct desktop_shell *shell = ip_surface->shell;
+ float x, y;
+ uint32_t show_surface = 0;
+
+ if (width == 0)
+ return;
+
+ if (!weston_surface_is_mapped(surface)) {
+ if (!shell->showing_input_panels)
+ return;
+
+ show_surface = 1;
+ }
+
+ fprintf(stderr, "%s panel: %d, output: %p\n", __FUNCTION__, ip_surface->panel, ip_surface->output);
+
+ if (ip_surface->panel) {
+ x = shell->text_input.surface->geometry.x + shell->text_input.cursor_rectangle.x2;
+ y = shell->text_input.surface->geometry.y + shell->text_input.cursor_rectangle.y2;
+ } else {
+ x = ip_surface->output->x + (ip_surface->output->width - width) / 2;
+ y = ip_surface->output->y + ip_surface->output->height - height;
+ }
+
+ weston_surface_configure(surface,
+ x, y,
+ width, height);
+
+ if (show_surface) {
+ wl_list_insert(&shell->input_panel_layer.surface_list,
+ &surface->layer_link);
+ weston_surface_update_transform(surface);
+ weston_surface_damage(surface);
+ weston_slide_run(surface, surface->geometry.height, 0, NULL, NULL);
+ }
+}
+
+static void
+destroy_input_panel_surface(struct input_panel_surface *input_panel_surface)
+{
+ wl_signal_emit(&input_panel_surface->destroy_signal, input_panel_surface);
+
+ wl_list_remove(&input_panel_surface->surface_destroy_listener.link);
+ wl_list_remove(&input_panel_surface->link);
+
+ input_panel_surface->surface->configure = NULL;
+
+ free(input_panel_surface);
+}
+
+static struct input_panel_surface *
+get_input_panel_surface(struct weston_surface *surface)
+{
+ if (surface->configure == input_panel_configure) {
+ return surface->configure_private;
+ } else {
+ return NULL;
+ }
+}
+
+static void
+input_panel_handle_surface_destroy(struct wl_listener *listener, void *data)
+{
+ struct input_panel_surface *ipsurface = container_of(listener,
+ struct input_panel_surface,
+ surface_destroy_listener);
+
+ if (ipsurface->resource) {
+ wl_resource_destroy(ipsurface->resource);
+ } else {
+ destroy_input_panel_surface(ipsurface);
+ }
+}
+
+static struct input_panel_surface *
+create_input_panel_surface(struct desktop_shell *shell,
+ struct weston_surface *surface)
+{
+ struct input_panel_surface *input_panel_surface;
+
+ input_panel_surface = calloc(1, sizeof *input_panel_surface);
+ if (!input_panel_surface)
+ return NULL;
+
+ surface->configure = input_panel_configure;
+ surface->configure_private = input_panel_surface;
+
+ input_panel_surface->shell = shell;
+
+ input_panel_surface->surface = surface;
+
+ wl_signal_init(&input_panel_surface->destroy_signal);
+ input_panel_surface->surface_destroy_listener.notify = input_panel_handle_surface_destroy;
+ wl_signal_add(&surface->destroy_signal,
+ &input_panel_surface->surface_destroy_listener);
+
+ wl_list_init(&input_panel_surface->link);
+
+ return input_panel_surface;
+}
+
+static void
+input_panel_surface_set_toplevel(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *output_resource,
+ uint32_t position)
+{
+ struct input_panel_surface *input_panel_surface =
+ wl_resource_get_user_data(resource);
+ struct desktop_shell *shell = input_panel_surface->shell;
+
+ wl_list_insert(&shell->input_panel.surfaces,
+ &input_panel_surface->link);
+
+ input_panel_surface->output = wl_resource_get_user_data(output_resource);
+ input_panel_surface->panel = 0;
+}
+
+static void
+input_panel_surface_set_overlay_panel(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ struct input_panel_surface *input_panel_surface =
+ wl_resource_get_user_data(resource);
+ struct desktop_shell *shell = input_panel_surface->shell;
+
+ wl_list_insert(&shell->input_panel.surfaces,
+ &input_panel_surface->link);
+
+ input_panel_surface->panel = 1;
+}
+
+static const struct wl_input_panel_surface_interface input_panel_surface_implementation = {
+ input_panel_surface_set_toplevel,
+ input_panel_surface_set_overlay_panel
+};
+
+static void
+destroy_input_panel_surface_resource(struct wl_resource *resource)
+{
+ struct input_panel_surface *ipsurf =
+ wl_resource_get_user_data(resource);
+
+ destroy_input_panel_surface(ipsurf);
+}
+
+static void
+input_panel_get_input_panel_surface(struct wl_client *client,
+ struct wl_resource *resource,
+ uint32_t id,
+ struct wl_resource *surface_resource)
+{
+ struct weston_surface *surface =
+ wl_resource_get_user_data(surface_resource);
+ struct desktop_shell *shell = wl_resource_get_user_data(resource);
+ struct input_panel_surface *ipsurf;
+
+ if (get_input_panel_surface(surface)) {
+ wl_resource_post_error(surface_resource,
+ WL_DISPLAY_ERROR_INVALID_OBJECT,
+ "wl_input_panel::get_input_panel_surface already requested");
+ return;
+ }
+
+ ipsurf = create_input_panel_surface(shell, surface);
+ if (!ipsurf) {
+ wl_resource_post_error(surface_resource,
+ WL_DISPLAY_ERROR_INVALID_OBJECT,
+ "surface->configure already set");
+ return;
+ }
+
+ ipsurf->resource =
+ wl_resource_create(client,
+ &wl_input_panel_surface_interface, 1, id);
+ wl_resource_set_implementation(ipsurf->resource,
+ &input_panel_surface_implementation,
+ ipsurf,
+ destroy_input_panel_surface_resource);
+}
+
+static const struct wl_input_panel_interface input_panel_implementation = {
+ input_panel_get_input_panel_surface
+};
+
+static void
+unbind_input_panel(struct wl_resource *resource)
+{
+ struct desktop_shell *shell = wl_resource_get_user_data(resource);
+
+ shell->input_panel.binding = NULL;
+}
+
+static void
+bind_input_panel(struct wl_client *client,
+ void *data, uint32_t version, uint32_t id)
+{
+ struct desktop_shell *shell = data;
+ struct wl_resource *resource;
+
+ resource = wl_resource_create(client,
+ &wl_input_panel_interface, 1, id);
+
+ if (shell->input_panel.binding == NULL) {
+ wl_resource_set_implementation(resource,
+ &input_panel_implementation,
+ shell, unbind_input_panel);
+ shell->input_panel.binding = resource;
+ return;
+ }
+
+ wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
+ "interface object already bound");
+ wl_resource_destroy(resource);
+}
+
+struct switcher {
+ struct desktop_shell *shell;
+ struct weston_surface *current;
+ struct wl_listener listener;
+ struct weston_keyboard_grab grab;
+};
+
+static void
+switcher_next(struct switcher *switcher)
+{
+ struct weston_surface *surface;
+ struct weston_surface *first = NULL, *prev = NULL, *next = NULL;
+ struct shell_surface *shsurf;
+ struct workspace *ws = get_current_workspace(switcher->shell);
+
+ wl_list_for_each(surface, &ws->layer.surface_list, layer_link) {
+ switch (get_shell_surface_type(surface)) {
+ case SHELL_SURFACE_TOPLEVEL:
+ case SHELL_SURFACE_FULLSCREEN:
+ case SHELL_SURFACE_MAXIMIZED:
+ if (first == NULL)
+ first = surface;
+ if (prev == switcher->current)
+ next = surface;
+ prev = surface;
+ surface->alpha = 0.25;
+ weston_surface_geometry_dirty(surface);
+ weston_surface_damage(surface);
+ break;
+ default:
+ break;
+ }
+
+ if (is_black_surface(surface, NULL)) {
+ surface->alpha = 0.25;
+ weston_surface_geometry_dirty(surface);
+ weston_surface_damage(surface);
+ }
+ }
+
+ if (next == NULL)
+ next = first;
+
+ if (next == NULL)
+ return;
+
+ wl_list_remove(&switcher->listener.link);
+ wl_signal_add(&next->destroy_signal, &switcher->listener);
+
+ switcher->current = next;
+ next->alpha = 1.0;
+
+ shsurf = get_shell_surface(switcher->current);
+ if (shsurf && shsurf->type ==SHELL_SURFACE_FULLSCREEN)
+ shsurf->fullscreen.black_surface->alpha = 1.0;
+}
+
+static void
+switcher_handle_surface_destroy(struct wl_listener *listener, void *data)
+{
+ struct switcher *switcher =
+ container_of(listener, struct switcher, listener);
+
+ switcher_next(switcher);
+}
+
+static void
+switcher_destroy(struct switcher *switcher)
+{
+ struct weston_surface *surface;
+ struct weston_keyboard *keyboard = switcher->grab.keyboard;
+ struct workspace *ws = get_current_workspace(switcher->shell);
+
+ wl_list_for_each(surface, &ws->layer.surface_list, layer_link) {
+ surface->alpha = 1.0;
+ weston_surface_damage(surface);
+ }
+
+ if (switcher->current)
+ activate(switcher->shell, switcher->current,
+ (struct weston_seat *) keyboard->seat);
+ wl_list_remove(&switcher->listener.link);
+ weston_keyboard_end_grab(keyboard);
+ if (keyboard->input_method_resource)
+ keyboard->grab = &keyboard->input_method_grab;
+ free(switcher);
+}
+
+static void
+switcher_key(struct weston_keyboard_grab *grab,
+ uint32_t time, uint32_t key, uint32_t state_w)
+{
+ struct switcher *switcher = container_of(grab, struct switcher, grab);
+ enum wl_keyboard_key_state state = state_w;
+
+ if (key == KEY_TAB && state == WL_KEYBOARD_KEY_STATE_PRESSED)
+ switcher_next(switcher);
+}
+
+static void
+switcher_modifier(struct weston_keyboard_grab *grab, uint32_t serial,
+ uint32_t mods_depressed, uint32_t mods_latched,
+ uint32_t mods_locked, uint32_t group)
+{
+ struct switcher *switcher = container_of(grab, struct switcher, grab);
+ struct weston_seat *seat = (struct weston_seat *) grab->keyboard->seat;
+
+ if ((seat->modifier_state & switcher->shell->binding_modifier) == 0)
+ switcher_destroy(switcher);
+}
+
+static void
+switcher_cancel(struct weston_keyboard_grab *grab)
+{
+ struct switcher *switcher = container_of(grab, struct switcher, grab);
+
+ switcher_destroy(switcher);
+}
+
+static const struct weston_keyboard_grab_interface switcher_grab = {
+ switcher_key,
+ switcher_modifier,
+ switcher_cancel,
+};
+
+static void
+switcher_binding(struct weston_seat *seat, uint32_t time, uint32_t key,
+ void *data)
+{
+ struct desktop_shell *shell = data;
+ struct switcher *switcher;
+
+ switcher = malloc(sizeof *switcher);
+ switcher->shell = shell;
+ switcher->current = NULL;
+ switcher->listener.notify = switcher_handle_surface_destroy;
+ wl_list_init(&switcher->listener.link);
+
+ restore_all_output_modes(shell->compositor);
+ lower_fullscreen_layer(switcher->shell);
+ switcher->grab.interface = &switcher_grab;
+ weston_keyboard_start_grab(seat->keyboard, &switcher->grab);
+ weston_keyboard_set_focus(seat->keyboard, NULL);
+ switcher_next(switcher);
+}
+
+static void
+backlight_binding(struct weston_seat *seat, uint32_t time, uint32_t key,
+ void *data)
+{
+ struct weston_compositor *compositor = data;
+ struct weston_output *output;
+ long backlight_new = 0;
+
+ /* TODO: we're limiting to simple use cases, where we assume just
+ * control on the primary display. We'd have to extend later if we
+ * ever get support for setting backlights on random desktop LCD
+ * panels though */
+ output = get_default_output(compositor);
+ if (!output)
+ return;
+
+ if (!output->set_backlight)
+ return;
+
+ if (key == KEY_F9 || key == KEY_BRIGHTNESSDOWN)
+ backlight_new = output->backlight_current - 25;
+ else if (key == KEY_F10 || key == KEY_BRIGHTNESSUP)
+ backlight_new = output->backlight_current + 25;
+
+ if (backlight_new < 5)
+ backlight_new = 5;
+ if (backlight_new > 255)
+ backlight_new = 255;
+
+ output->backlight_current = backlight_new;
+ output->set_backlight(output, output->backlight_current);
+}
+
+struct debug_binding_grab {
+ struct weston_keyboard_grab grab;
+ struct weston_seat *seat;
+ uint32_t key[2];
+ int key_released[2];
+};
+
+static void
+debug_binding_key(struct weston_keyboard_grab *grab, uint32_t time,
+ uint32_t key, uint32_t state)
+{
+ struct debug_binding_grab *db = (struct debug_binding_grab *) grab;
+ struct weston_compositor *ec = db->seat->compositor;
+ struct wl_display *display = ec->wl_display;
+ struct wl_resource *resource;
+ uint32_t serial;
+ int send = 0, terminate = 0;
+ int check_binding = 1;
+ int i;
+ struct wl_list *resource_list;
+
+ if (state == WL_KEYBOARD_KEY_STATE_RELEASED) {
+ /* Do not run bindings on key releases */
+ check_binding = 0;
+
+ for (i = 0; i < 2; i++)
+ if (key == db->key[i])
+ db->key_released[i] = 1;
+
+ if (db->key_released[0] && db->key_released[1]) {
+ /* All key releases been swalled so end the grab */
+ terminate = 1;
+ } else if (key != db->key[0] && key != db->key[1]) {
+ /* Should not swallow release of other keys */
+ send = 1;
+ }
+ } else if (key == db->key[0] && !db->key_released[0]) {
+ /* Do not check bindings for the first press of the binding
+ * key. This allows it to be used as a debug shortcut.
+ * We still need to swallow this event. */
+ check_binding = 0;
+ } else if (db->key[1]) {
+ /* If we already ran a binding don't process another one since
+ * we can't keep track of all the binding keys that were
+ * pressed in order to swallow the release events. */
+ send = 1;
+ check_binding = 0;
+ }
+
+ if (check_binding) {
+ if (weston_compositor_run_debug_binding(ec, db->seat, time,
+ key, state)) {
+ /* We ran a binding so swallow the press and keep the
+ * grab to swallow the released too. */
+ send = 0;
+ terminate = 0;
+ db->key[1] = key;
+ } else {
+ /* Terminate the grab since the key pressed is not a
+ * debug binding key. */
+ send = 1;
+ terminate = 1;
+ }
+ }
+
+ if (send) {
+ serial = wl_display_next_serial(display);
+ resource_list = &grab->keyboard->focus_resource_list;
+ wl_resource_for_each(resource, resource_list) {
+ wl_keyboard_send_key(resource, serial, time, key, state);
+ }
+ }
+
+ if (terminate) {
+ weston_keyboard_end_grab(grab->keyboard);
+ if (grab->keyboard->input_method_resource)
+ grab->keyboard->grab = &grab->keyboard->input_method_grab;
+ free(db);
+ }
+}
+
+static void
+debug_binding_modifiers(struct weston_keyboard_grab *grab, uint32_t serial,
+ uint32_t mods_depressed, uint32_t mods_latched,
+ uint32_t mods_locked, uint32_t group)
+{
+ struct wl_resource *resource;
+ struct wl_list *resource_list;
+
+ resource_list = &grab->keyboard->focus_resource_list;
+
+ wl_resource_for_each(resource, resource_list) {
+ wl_keyboard_send_modifiers(resource, serial, mods_depressed,
+ mods_latched, mods_locked, group);
+ }
+}
+
+static void
+debug_binding_cancel(struct weston_keyboard_grab *grab)
+{
+ struct debug_binding_grab *db = (struct debug_binding_grab *) grab;
+
+ weston_keyboard_end_grab(grab->keyboard);
+ free(db);
+}
+
+struct weston_keyboard_grab_interface debug_binding_keyboard_grab = {
+ debug_binding_key,
+ debug_binding_modifiers,
+ debug_binding_cancel,
+};
+
+static void
+debug_binding(struct weston_seat *seat, uint32_t time, uint32_t key, void *data)
+{
+ struct debug_binding_grab *grab;
+
+ grab = calloc(1, sizeof *grab);
+ if (!grab)
+ return;
+
+ grab->seat = (struct weston_seat *) seat;
+ grab->key[0] = key;
+ grab->grab.interface = &debug_binding_keyboard_grab;
+ weston_keyboard_start_grab(seat->keyboard, &grab->grab);
+}
+
+static void
+force_kill_binding(struct weston_seat *seat, uint32_t time, uint32_t key,
+ void *data)
+{
+ struct weston_surface *focus_surface;
+ struct wl_client *client;
+ struct desktop_shell *shell = data;
+ struct weston_compositor *compositor = shell->compositor;
+ pid_t pid;
+
+ focus_surface = seat->keyboard->focus;
+ if (!focus_surface)
+ return;
+
+ wl_signal_emit(&compositor->kill_signal, focus_surface);
+
+ client = wl_resource_get_client(focus_surface->resource);
+ wl_client_get_credentials(client, &pid, NULL, NULL);
+
+ /* Skip clients that we launched ourselves (the credentials of
+ * the socketpair is ours) */
+ if (pid == getpid())
+ return;
+
+ kill(pid, SIGKILL);
+}
+
+static void
+workspace_up_binding(struct weston_seat *seat, uint32_t time,
+ uint32_t key, void *data)
+{
+ struct desktop_shell *shell = data;
+ unsigned int new_index = shell->workspaces.current;
+
+ if (shell->locked)
+ return;
+ if (new_index != 0)
+ new_index--;
+
+ change_workspace(shell, new_index);
+}
+
+static void
+workspace_down_binding(struct weston_seat *seat, uint32_t time,
+ uint32_t key, void *data)
+{
+ struct desktop_shell *shell = data;
+ unsigned int new_index = shell->workspaces.current;
+
+ if (shell->locked)
+ return;
+ if (new_index < shell->workspaces.num - 1)
+ new_index++;
+
+ change_workspace(shell, new_index);
+}
+
+static void
+workspace_f_binding(struct weston_seat *seat, uint32_t time,
+ uint32_t key, void *data)
+{
+ struct desktop_shell *shell = data;
+ unsigned int new_index;
+
+ if (shell->locked)
+ return;
+ new_index = key - KEY_F1;
+ if (new_index >= shell->workspaces.num)
+ new_index = shell->workspaces.num - 1;
+
+ change_workspace(shell, new_index);
+}
+
+static void
+workspace_move_surface_up_binding(struct weston_seat *seat, uint32_t time,
+ uint32_t key, void *data)
+{
+ struct desktop_shell *shell = data;
+ unsigned int new_index = shell->workspaces.current;
+
+ if (shell->locked)
+ return;
+
+ if (new_index != 0)
+ new_index--;
+
+ take_surface_to_workspace_by_seat(shell, seat, new_index);
+}
+
+static void
+workspace_move_surface_down_binding(struct weston_seat *seat, uint32_t time,
+ uint32_t key, void *data)
+{
+ struct desktop_shell *shell = data;
+ unsigned int new_index = shell->workspaces.current;
+
+ if (shell->locked)
+ return;
+
+ if (new_index < shell->workspaces.num - 1)
+ new_index++;
+
+ take_surface_to_workspace_by_seat(shell, seat, new_index);
+}
+
+static void
+shell_destroy(struct wl_listener *listener, void *data)
+{
+ struct desktop_shell *shell =
+ container_of(listener, struct desktop_shell, destroy_listener);
+ struct workspace **ws;
+
+ if (shell->child.client)
+ wl_client_destroy(shell->child.client);
+
+ wl_list_remove(&shell->idle_listener.link);
+ wl_list_remove(&shell->wake_listener.link);
+ wl_list_remove(&shell->show_input_panel_listener.link);
+ wl_list_remove(&shell->hide_input_panel_listener.link);
+
+ wl_array_for_each(ws, &shell->workspaces.array)
+ workspace_destroy(*ws);
+ wl_array_release(&shell->workspaces.array);
+
+ free(shell->screensaver.path);
+ free(shell);
+}
+
+static void
+shell_add_bindings(struct weston_compositor *ec, struct desktop_shell *shell)
+{
+ uint32_t mod;
+ int i, num_workspace_bindings;
+
+ /* fixed bindings */
+ weston_compositor_add_key_binding(ec, KEY_BACKSPACE,
+ MODIFIER_CTRL | MODIFIER_ALT,
+ terminate_binding, ec);
+ weston_compositor_add_button_binding(ec, BTN_LEFT, 0,
+ click_to_activate_binding,
+ shell);
+ weston_compositor_add_touch_binding(ec, 0,
+ touch_to_activate_binding,
+ shell);
+ weston_compositor_add_axis_binding(ec, WL_POINTER_AXIS_VERTICAL_SCROLL,
+ MODIFIER_SUPER | MODIFIER_ALT,
+ surface_opacity_binding, NULL);
+ weston_compositor_add_axis_binding(ec, WL_POINTER_AXIS_VERTICAL_SCROLL,
+ MODIFIER_SUPER, zoom_axis_binding,
+ NULL);
+
+ /* configurable bindings */
+ mod = shell->binding_modifier;
+ weston_compositor_add_key_binding(ec, KEY_PAGEUP, mod,
+ zoom_key_binding, NULL);
+ weston_compositor_add_key_binding(ec, KEY_PAGEDOWN, mod,
+ zoom_key_binding, NULL);
+ weston_compositor_add_button_binding(ec, BTN_LEFT, mod, move_binding,
+ shell);
+ weston_compositor_add_touch_binding(ec, mod, touch_move_binding, shell);
+ weston_compositor_add_button_binding(ec, BTN_MIDDLE, mod,
+ resize_binding, shell);
+
+ if (ec->capabilities & WESTON_CAP_ROTATION_ANY)
+ weston_compositor_add_button_binding(ec, BTN_RIGHT, mod,
+ rotate_binding, NULL);
+
+ weston_compositor_add_key_binding(ec, KEY_TAB, mod, switcher_binding,
+ shell);
+ weston_compositor_add_key_binding(ec, KEY_F9, mod, backlight_binding,
+ ec);
+ weston_compositor_add_key_binding(ec, KEY_BRIGHTNESSDOWN, 0,
+ backlight_binding, ec);
+ weston_compositor_add_key_binding(ec, KEY_F10, mod, backlight_binding,
+ ec);
+ weston_compositor_add_key_binding(ec, KEY_BRIGHTNESSUP, 0,
+ backlight_binding, ec);
+ weston_compositor_add_key_binding(ec, KEY_K, mod,
+ force_kill_binding, shell);
+ weston_compositor_add_key_binding(ec, KEY_UP, mod,
+ workspace_up_binding, shell);
+ weston_compositor_add_key_binding(ec, KEY_DOWN, mod,
+ workspace_down_binding, shell);
+ weston_compositor_add_key_binding(ec, KEY_UP, mod | MODIFIER_SHIFT,
+ workspace_move_surface_up_binding,
+ shell);
+ weston_compositor_add_key_binding(ec, KEY_DOWN, mod | MODIFIER_SHIFT,
+ workspace_move_surface_down_binding,
+ shell);
+
+ /* Add bindings for mod+F[1-6] for workspace 1 to 6. */
+ if (shell->workspaces.num > 1) {
+ num_workspace_bindings = shell->workspaces.num;
+ if (num_workspace_bindings > 6)
+ num_workspace_bindings = 6;
+ for (i = 0; i < num_workspace_bindings; i++)
+ weston_compositor_add_key_binding(ec, KEY_F1 + i, mod,
+ workspace_f_binding,
+ shell);
+ }
+
+ /* Debug bindings */
+ weston_compositor_add_key_binding(ec, KEY_SPACE, mod | MODIFIER_SHIFT,
+ debug_binding, shell);
+}
+
+WL_EXPORT int
+module_init(struct weston_compositor *ec,
+ int *argc, char *argv[])
+{
+ struct weston_seat *seat;
+ struct desktop_shell *shell;
+ struct workspace **pws;
+ unsigned int i;
+ struct wl_event_loop *loop;
+
+ shell = zalloc(sizeof *shell);
+ if (shell == NULL)
+ return -1;
+
+ shell->compositor = ec;
+
+ shell->destroy_listener.notify = shell_destroy;
+ wl_signal_add(&ec->destroy_signal, &shell->destroy_listener);
+ shell->idle_listener.notify = idle_handler;
+ wl_signal_add(&ec->idle_signal, &shell->idle_listener);
+ shell->wake_listener.notify = wake_handler;
+ wl_signal_add(&ec->wake_signal, &shell->wake_listener);
+ shell->show_input_panel_listener.notify = show_input_panels;
+ wl_signal_add(&ec->show_input_panel_signal, &shell->show_input_panel_listener);
+ shell->hide_input_panel_listener.notify = hide_input_panels;
+ wl_signal_add(&ec->hide_input_panel_signal, &shell->hide_input_panel_listener);
+ shell->update_input_panel_listener.notify = update_input_panels;
+ wl_signal_add(&ec->update_input_panel_signal, &shell->update_input_panel_listener);
+ ec->ping_handler = ping_handler;
+ ec->shell_interface.shell = shell;
+ ec->shell_interface.create_shell_surface = create_shell_surface;
+ ec->shell_interface.set_toplevel = set_toplevel;
+ ec->shell_interface.set_transient = set_transient;
+ ec->shell_interface.set_fullscreen = set_fullscreen;
+ ec->shell_interface.set_xwayland = set_xwayland;
+ ec->shell_interface.move = surface_move;
+ ec->shell_interface.resize = surface_resize;
+ ec->shell_interface.set_title = set_title;
+
+ wl_list_init(&shell->input_panel.surfaces);
+
+ weston_layer_init(&shell->fullscreen_layer, &ec->cursor_layer.link);
+ weston_layer_init(&shell->panel_layer, &shell->fullscreen_layer.link);
+ weston_layer_init(&shell->background_layer, &shell->panel_layer.link);
+ weston_layer_init(&shell->lock_layer, NULL);
+ weston_layer_init(&shell->input_panel_layer, NULL);
+
+ wl_array_init(&shell->workspaces.array);
+ wl_list_init(&shell->workspaces.client_list);
+
+ shell_configuration(shell);
+
+ for (i = 0; i < shell->workspaces.num; i++) {
+ pws = wl_array_add(&shell->workspaces.array, sizeof *pws);
+ if (pws == NULL)
+ return -1;
+
+ *pws = workspace_create();
+ if (*pws == NULL)
+ return -1;
+ }
+ activate_workspace(shell, 0);
+
+ wl_list_init(&shell->workspaces.anim_sticky_list);
+ wl_list_init(&shell->workspaces.animation.link);
+ shell->workspaces.animation.frame = animate_workspace_change_frame;
+
+ if (wl_global_create(ec->wl_display, &wl_shell_interface, 1,
+ shell, bind_shell) == NULL)
+ return -1;
+
+ if (wl_global_create(ec->wl_display,
+ &desktop_shell_interface, 2,
+ shell, bind_desktop_shell) == NULL)
+ return -1;
+
+ if (wl_global_create(ec->wl_display, &screensaver_interface, 1,
+ shell, bind_screensaver) == NULL)
+ return -1;
+
+ if (wl_global_create(ec->wl_display, &wl_input_panel_interface, 1,
+ shell, bind_input_panel) == NULL)
+ return -1;
+
+ if (wl_global_create(ec->wl_display, &workspace_manager_interface, 1,
+ shell, bind_workspace_manager) == NULL)
+ return -1;
+
+ shell->child.deathstamp = weston_compositor_get_time();
+
+ loop = wl_display_get_event_loop(ec->wl_display);
+ wl_event_loop_add_idle(loop, launch_desktop_shell_process, shell);
+
+ shell->screensaver.timer =
+ wl_event_loop_add_timer(loop, screensaver_timeout, shell);
+
+ wl_list_for_each(seat, &ec->seat_list, link)
+ create_pointer_focus_listener(seat);
+
+ shell_add_bindings(ec, shell);
+
+ shell_fade_init(shell);
+
+ return 0;
+}
diff --git a/src/spring-tool.c b/src/spring-tool.c
new file mode 100644
index 00000000..935acc4b
--- /dev/null
+++ b/src/spring-tool.c
@@ -0,0 +1,66 @@
+/*
+ * Copyright © 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 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.
+ */
+
+#include "config.h"
+
+#include "compositor.h"
+
+WL_EXPORT void
+weston_surface_geometry_dirty(struct weston_surface *surface)
+{
+}
+
+WL_EXPORT int
+weston_log(const char *fmt, ...)
+{
+ return 0;
+}
+
+WL_EXPORT void
+weston_compositor_schedule_repaint(struct weston_compositor *compositor)
+{
+}
+
+int
+main(int argc, char *argv[])
+{
+ const double k = 300.0;
+ const double current = 0.5;
+ const double target = 1.0;
+ const double friction = 1400;
+
+ struct weston_spring spring;
+ uint32_t time = 0;
+
+ weston_spring_init(&spring, k, current, target);
+ spring.friction = friction;
+ spring.previous = 0.48;
+ spring.timestamp = 0;
+
+ while (!weston_spring_done(&spring)) {
+ printf("\t%d\t%f\n", time, spring.current);
+ weston_spring_update(&spring, time);
+ time += 16;
+ }
+
+ return 0;
+}
diff --git a/src/tablet-shell.c b/src/tablet-shell.c
new file mode 100644
index 00000000..b055ab23
--- /dev/null
+++ b/src/tablet-shell.c
@@ -0,0 +1,575 @@
+/*
+ * Copyright © 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 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.
+ */
+
+#include "config.h"
+
+#include <sys/wait.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <linux/input.h>
+
+#include "compositor.h"
+#include "tablet-shell-server-protocol.h"
+
+/*
+ * TODO: Don't fade back from black until we've received a lockscreen
+ * attachment.
+ */
+
+enum {
+ STATE_STARTING,
+ STATE_LOCKED,
+ STATE_HOME,
+ STATE_SWITCHER,
+ STATE_TASK
+};
+
+struct tablet_shell {
+ struct wl_resource *resource;
+
+ struct wl_listener lock_listener;
+ struct wl_listener unlock_listener;
+ struct wl_listener destroy_listener;
+
+ struct weston_compositor *compositor;
+ struct weston_process process;
+ struct wl_client *client;
+
+ struct weston_surface *surface;
+
+ struct weston_surface *lockscreen_surface;
+ struct wl_listener lockscreen_listener;
+ struct weston_layer lockscreen_layer;
+
+ struct weston_layer application_layer;
+
+ struct weston_surface *home_surface;
+ struct weston_layer homescreen_layer;
+
+ struct weston_surface *switcher_surface;
+ struct wl_listener switcher_listener;
+
+ struct tablet_client *current_client;
+
+ int state, previous_state;
+ int long_press_active;
+ struct wl_event_source *long_press_source;
+};
+
+struct tablet_client {
+ struct wl_resource *resource;
+ struct tablet_shell *shell;
+ struct wl_client *client;
+ struct weston_surface *surface;
+ char *name;
+};
+
+static void
+tablet_shell_destroy(struct wl_listener *listener, void *data);
+
+static struct tablet_shell *
+get_shell(struct weston_compositor *compositor)
+{
+ struct wl_listener *l;
+
+ l = wl_signal_get(&compositor->destroy_signal, tablet_shell_destroy);
+ if (l)
+ return container_of(l, struct tablet_shell, destroy_listener);
+
+ return NULL;
+}
+
+static void
+tablet_shell_sigchld(struct weston_process *process, int status)
+{
+ struct tablet_shell *shell =
+ container_of(process, struct tablet_shell, process);
+
+ shell->process.pid = 0;
+
+ weston_log("weston-tablet-shell crashed, exit code %d\n", status);
+}
+
+static void
+tablet_shell_set_state(struct tablet_shell *shell, int state)
+{
+ static const char *states[] = {
+ "STARTING", "LOCKED", "HOME", "SWITCHER", "TASK"
+ };
+
+ weston_log("switching to state %s (from %s)\n",
+ states[state], states[shell->state]);
+ shell->previous_state = shell->state;
+ shell->state = state;
+}
+
+static void
+tablet_shell_surface_configure(struct weston_surface *surface,
+ int32_t sx, int32_t sy, int32_t width, int32_t height)
+{
+ struct tablet_shell *shell = get_shell(surface->compositor);
+
+ if (weston_surface_is_mapped(surface) || width == 0)
+ return;
+
+ weston_surface_configure(surface, 0, 0, width, height);
+
+ if (surface == shell->lockscreen_surface) {
+ wl_list_insert(&shell->lockscreen_layer.surface_list,
+ &surface->layer_link);
+ } else if (surface == shell->switcher_surface) {
+ /* */
+ } else if (surface == shell->home_surface) {
+ if (shell->state == STATE_STARTING) {
+ /* homescreen always visible, at the bottom */
+ wl_list_insert(&shell->homescreen_layer.surface_list,
+ &surface->layer_link);
+
+ tablet_shell_set_state(shell, STATE_LOCKED);
+ shell->previous_state = STATE_HOME;
+ tablet_shell_send_show_lockscreen(shell->resource);
+ }
+ } else if (shell->current_client &&
+ shell->current_client->surface != surface &&
+ shell->current_client->client == wl_resource_get_client(surface->resource)) {
+ tablet_shell_set_state(shell, STATE_TASK);
+ shell->current_client->surface = surface;
+ weston_zoom_run(surface, 0.3, 1.0, NULL, NULL);
+ wl_list_insert(&shell->application_layer.surface_list,
+ &surface->layer_link);
+ }
+
+ weston_surface_update_transform(surface);
+}
+
+static void
+handle_lockscreen_surface_destroy(struct wl_listener *listener, void *data)
+{
+ struct tablet_shell *shell =
+ container_of(listener,
+ struct tablet_shell, lockscreen_listener);
+
+ shell->lockscreen_surface = NULL;
+ tablet_shell_set_state(shell, shell->previous_state);
+}
+
+static void
+tablet_shell_set_lockscreen(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *surface_resource)
+{
+ struct tablet_shell *shell = wl_resource_get_user_data(resource);
+ struct weston_surface *es = wl_resource_get_user_data(surface_resource);
+
+ weston_surface_set_position(es, 0, 0);
+ shell->lockscreen_surface = es;
+ shell->lockscreen_surface->configure = tablet_shell_surface_configure;
+ shell->lockscreen_listener.notify = handle_lockscreen_surface_destroy;
+ wl_signal_add(&es->destroy_signal, &shell->lockscreen_listener);
+}
+
+static void
+handle_switcher_surface_destroy(struct wl_listener *listener, void *data)
+{
+ struct tablet_shell *shell =
+ container_of(listener,
+ struct tablet_shell, switcher_listener);
+
+ shell->switcher_surface = NULL;
+ if (shell->state != STATE_LOCKED)
+ tablet_shell_set_state(shell, shell->previous_state);
+}
+
+static void
+tablet_shell_set_switcher(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *surface_resource)
+{
+ struct tablet_shell *shell = wl_resource_get_user_data(resource);
+ struct weston_surface *es = wl_resource_get_user_data(surface_resource);
+
+ /* FIXME: Switcher should be centered and the compositor
+ * should do the tinting of the background. With the cache
+ * layer idea, we should be able to hit the framerate on the
+ * fade/zoom in. */
+ shell->switcher_surface = es;
+ weston_surface_set_position(shell->switcher_surface, 0, 0);
+
+ shell->switcher_listener.notify = handle_switcher_surface_destroy;
+ wl_signal_add(&es->destroy_signal, &shell->switcher_listener);
+}
+
+static void
+tablet_shell_set_homescreen(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *surface_resource)
+{
+ struct tablet_shell *shell = wl_resource_get_user_data(resource);
+
+ shell->home_surface = wl_resource_get_user_data(surface_resource);
+ shell->home_surface->configure = tablet_shell_surface_configure;
+
+ weston_surface_set_position(shell->home_surface, 0, 0);
+}
+
+static void
+minimize_zoom_done(struct weston_surface_animation *zoom, void *data)
+{
+ struct tablet_shell *shell = data;
+ struct weston_compositor *compositor = shell->compositor;
+ struct weston_seat *seat;
+
+ wl_list_for_each(seat, &compositor->seat_list, link)
+ weston_surface_activate(shell->home_surface, seat);
+}
+
+static void
+tablet_shell_switch_to(struct tablet_shell *shell,
+ struct weston_surface *surface)
+{
+ struct weston_compositor *compositor = shell->compositor;
+ struct weston_seat *seat;
+ struct weston_surface *current;
+
+ if (shell->state == STATE_SWITCHER) {
+ wl_list_remove(&shell->switcher_listener.link);
+ shell->switcher_surface = NULL;
+ };
+
+ if (surface == shell->home_surface) {
+ tablet_shell_set_state(shell, STATE_HOME);
+
+ if (shell->current_client && shell->current_client->surface) {
+ current = shell->current_client->surface;
+ weston_zoom_run(current, 1.0, 0.3,
+ minimize_zoom_done, shell);
+ }
+ } else {
+ fprintf(stderr, "switch to %p\n", surface);
+ wl_list_for_each(seat, &compositor->seat_list, link)
+ weston_surface_activate(surface, seat);
+ tablet_shell_set_state(shell, STATE_TASK);
+ weston_zoom_run(surface, 0.3, 1.0, NULL, NULL);
+ }
+}
+
+static void
+tablet_shell_show_grid(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *surface_resource)
+{
+ struct tablet_shell *shell = wl_resource_get_user_data(resource);
+ struct weston_surface *es = wl_resource_get_user_data(surface_resource);
+
+ tablet_shell_switch_to(shell, es);
+}
+
+static void
+tablet_shell_show_panels(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *surface_resource)
+{
+ struct tablet_shell *shell = wl_resource_get_user_data(resource);
+ struct weston_surface *es = wl_resource_get_user_data(surface_resource);
+
+ tablet_shell_switch_to(shell, es);
+}
+
+static void
+destroy_tablet_client(struct wl_resource *resource)
+{
+ struct tablet_client *tablet_client =
+ wl_resource_get_user_data(resource);
+
+ free(tablet_client->name);
+ free(tablet_client);
+}
+
+static void
+tablet_client_destroy(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static void
+tablet_client_activate(struct wl_client *client, struct wl_resource *resource)
+{
+ struct tablet_client *tablet_client = wl_resource_get_user_data(resource);
+ struct tablet_shell *shell = tablet_client->shell;
+
+ shell->current_client = tablet_client;
+ if (!tablet_client->surface)
+ return;
+
+ tablet_shell_switch_to(shell, tablet_client->surface);
+}
+
+static const struct tablet_client_interface tablet_client_implementation = {
+ tablet_client_destroy,
+ tablet_client_activate
+};
+
+static void
+tablet_shell_create_client(struct wl_client *client,
+ struct wl_resource *resource,
+ uint32_t id, const char *name, int fd)
+{
+ struct tablet_shell *shell = wl_resource_get_user_data(resource);
+ struct weston_compositor *compositor = shell->compositor;
+ struct tablet_client *tablet_client;
+
+ tablet_client = malloc(sizeof *tablet_client);
+ if (tablet_client == NULL) {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ tablet_client->client = wl_client_create(compositor->wl_display, fd);
+ tablet_client->shell = shell;
+ tablet_client->name = strdup(name);
+
+ tablet_client->resource =
+ wl_resource_create(client, &tablet_client_interface, 1, id);
+ wl_resource_set_implementation(tablet_client->resource,
+ &tablet_client_implementation,
+ tablet_client, destroy_tablet_client);
+
+ tablet_client->surface = NULL;
+ shell->current_client = tablet_client;
+
+ weston_log("created client %p, id %d, name %s, fd %d\n",
+ tablet_client->client, id, name, fd);
+}
+
+static const struct tablet_shell_interface tablet_shell_implementation = {
+ tablet_shell_set_lockscreen,
+ tablet_shell_set_switcher,
+ tablet_shell_set_homescreen,
+ tablet_shell_show_grid,
+ tablet_shell_show_panels,
+ tablet_shell_create_client
+};
+
+static void
+launch_ux_daemon(struct tablet_shell *shell)
+{
+ const char *shell_exe = LIBEXECDIR "/weston-tablet-shell";
+
+ shell->client = weston_client_launch(shell->compositor,
+ &shell->process,
+ shell_exe, tablet_shell_sigchld);
+}
+
+static void
+toggle_switcher(struct tablet_shell *shell)
+{
+ switch (shell->state) {
+ case STATE_SWITCHER:
+ tablet_shell_send_hide_switcher(shell->resource);
+ break;
+ default:
+ tablet_shell_send_show_switcher(shell->resource);
+ tablet_shell_set_state(shell, STATE_SWITCHER);
+ break;
+ }
+}
+
+static void
+tablet_shell_lock(struct wl_listener *listener, void *data)
+{
+ struct tablet_shell *shell =
+ container_of(listener, struct tablet_shell, lock_listener);
+
+ if (shell->state == STATE_LOCKED)
+ return;
+ if (shell->state == STATE_SWITCHER)
+ tablet_shell_send_hide_switcher(shell->resource);
+
+ tablet_shell_send_show_lockscreen(shell->resource);
+ tablet_shell_set_state(shell, STATE_LOCKED);
+}
+
+static void
+tablet_shell_unlock(struct wl_listener *listener, void *data)
+{
+ struct tablet_shell *shell =
+ container_of(listener, struct tablet_shell, unlock_listener);
+
+ tablet_shell_set_state(shell, STATE_HOME);
+}
+
+static void
+go_home(struct tablet_shell *shell, struct weston_seat *seat)
+{
+ if (shell->state == STATE_SWITCHER)
+ tablet_shell_send_hide_switcher(shell->resource);
+
+ weston_surface_activate(shell->home_surface, seat);
+
+ tablet_shell_set_state(shell, STATE_HOME);
+}
+
+static int
+long_press_handler(void *data)
+{
+ struct tablet_shell *shell = data;
+
+ shell->long_press_active = 0;
+ toggle_switcher(shell);
+
+ return 1;
+}
+
+static void
+menu_key_binding(struct weston_seat *seat, uint32_t time, uint32_t key, void *data)
+{
+ struct tablet_shell *shell = data;
+
+ if (shell->state == STATE_LOCKED)
+ return;
+
+ toggle_switcher(shell);
+}
+
+static void
+home_key_binding(struct weston_seat *seat, uint32_t time, uint32_t key, void *data)
+{
+ struct tablet_shell *shell = data;
+
+ if (shell->state == STATE_LOCKED)
+ return;
+
+ if (1) {
+ wl_event_source_timer_update(shell->long_press_source, 500);
+ shell->long_press_active = 1;
+ } else if (shell->long_press_active) {
+ /* This code has never been run ... */
+ wl_event_source_timer_update(shell->long_press_source, 0);
+ shell->long_press_active = 0;
+
+ switch (shell->state) {
+ case STATE_HOME:
+ case STATE_SWITCHER:
+ toggle_switcher(shell);
+ break;
+ default:
+ go_home(shell, (struct weston_seat *) seat);
+ break;
+ }
+ }
+}
+
+static void
+destroy_tablet_shell(struct wl_resource *resource)
+{
+}
+
+static void
+bind_tablet_shell(struct wl_client *client, void *data, uint32_t version,
+ uint32_t id)
+{
+ struct tablet_shell *shell = data;
+
+ if (shell->client != client)
+ /* Throw an error or just let the client fail when it
+ * tries to access the object?. */
+ return;
+
+ shell->resource =
+ wl_resource_create(client, &tablet_shell_interface, 1, id);
+ wl_resource_set_implementation(shell->resource,
+ &tablet_shell_implementation,
+ shell, destroy_tablet_shell);
+}
+
+static void
+tablet_shell_destroy(struct wl_listener *listener, void *data)
+{
+ struct tablet_shell *shell =
+ container_of(listener, struct tablet_shell, destroy_listener);
+
+ if (shell->home_surface)
+ shell->home_surface->configure = NULL;
+
+ if (shell->lockscreen_surface)
+ shell->lockscreen_surface->configure = NULL;
+
+ wl_event_source_remove(shell->long_press_source);
+ free(shell);
+}
+
+WL_EXPORT int
+module_init(struct weston_compositor *compositor,
+ int *argc, char *argv[])
+{
+ struct tablet_shell *shell;
+ struct wl_event_loop *loop;
+
+ shell = zalloc(sizeof *shell);
+ if (shell == NULL)
+ return -1;
+
+ shell->compositor = compositor;
+
+ shell->destroy_listener.notify = tablet_shell_destroy;
+ wl_signal_add(&compositor->destroy_signal, &shell->destroy_listener);
+ shell->lock_listener.notify = tablet_shell_lock;
+ wl_signal_add(&compositor->idle_signal, &shell->lock_listener);
+ shell->unlock_listener.notify = tablet_shell_unlock;
+ wl_signal_add(&compositor->wake_signal, &shell->unlock_listener);
+
+ /* FIXME: This will make the object available to all clients. */
+ wl_global_create(compositor->wl_display, &tablet_shell_interface, 1,
+ shell, bind_tablet_shell);
+
+ loop = wl_display_get_event_loop(compositor->wl_display);
+ shell->long_press_source =
+ wl_event_loop_add_timer(loop, long_press_handler, shell);
+
+ weston_compositor_add_key_binding(compositor, KEY_LEFTMETA, 0,
+ home_key_binding, shell);
+ weston_compositor_add_key_binding(compositor, KEY_RIGHTMETA, 0,
+ home_key_binding, shell);
+ weston_compositor_add_key_binding(compositor, KEY_LEFTMETA,
+ MODIFIER_SUPER, home_key_binding,
+ shell);
+ weston_compositor_add_key_binding(compositor, KEY_RIGHTMETA,
+ MODIFIER_SUPER, home_key_binding,
+ shell);
+ weston_compositor_add_key_binding(compositor, KEY_COMPOSE, 0,
+ menu_key_binding, shell);
+
+ weston_layer_init(&shell->homescreen_layer,
+ &compositor->cursor_layer.link);
+ weston_layer_init(&shell->application_layer,
+ &compositor->cursor_layer.link);
+ weston_layer_init(&shell->lockscreen_layer,
+ &compositor->cursor_layer.link);
+ launch_ux_daemon(shell);
+
+ tablet_shell_set_state(shell, STATE_STARTING);
+
+ return 0;
+}
diff --git a/src/text-backend.c b/src/text-backend.c
new file mode 100644
index 00000000..107ccd63
--- /dev/null
+++ b/src/text-backend.c
@@ -0,0 +1,967 @@
+/*
+ * Copyright © 2012 Openismus GmbH
+ * Copyright © 2012 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 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.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+
+#include "compositor.h"
+#include "text-server-protocol.h"
+#include "input-method-server-protocol.h"
+
+struct input_method;
+struct input_method_context;
+struct text_backend;
+
+struct text_input {
+ struct wl_resource *resource;
+
+ struct weston_compositor *ec;
+
+ struct wl_list input_methods;
+
+ struct weston_surface *surface;
+
+ pixman_box32_t cursor_rectangle;
+
+ uint32_t input_panel_visible;
+};
+
+struct text_input_manager {
+ struct wl_global *text_input_manager_global;
+ struct wl_listener destroy_listener;
+
+ struct weston_compositor *ec;
+};
+
+struct input_method {
+ struct wl_resource *input_method_binding;
+ struct wl_global *input_method_global;
+ struct wl_listener destroy_listener;
+
+ struct weston_seat *seat;
+ struct text_input *model;
+
+ struct wl_list link;
+
+ struct wl_listener keyboard_focus_listener;
+
+ int focus_listener_initialized;
+
+ struct input_method_context *context;
+
+ struct text_backend *text_backend;
+};
+
+struct input_method_context {
+ struct wl_resource *resource;
+
+ struct text_input *model;
+ struct input_method *input_method;
+
+ struct wl_list link;
+
+ struct wl_resource *keyboard;
+};
+
+struct text_backend {
+ struct weston_compositor *compositor;
+
+ struct {
+ char *path;
+ struct wl_resource *binding;
+ struct weston_process process;
+ struct wl_client *client;
+
+ unsigned deathcount;
+ uint32_t deathstamp;
+ } input_method;
+
+ struct wl_listener seat_created_listener;
+ struct wl_listener destroy_listener;
+};
+
+static void input_method_context_create(struct text_input *model,
+ struct input_method *input_method);
+static void input_method_context_end_keyboard_grab(struct input_method_context *context);
+
+static void input_method_init_seat(struct weston_seat *seat);
+
+static void
+deactivate_text_input(struct text_input *text_input,
+ struct input_method *input_method)
+{
+ struct weston_compositor *ec = text_input->ec;
+
+ if (input_method->model == text_input) {
+ if (input_method->context && input_method->input_method_binding) {
+ input_method_context_end_keyboard_grab(input_method->context);
+ wl_input_method_send_deactivate(input_method->input_method_binding,
+ input_method->context->resource);
+ }
+
+ wl_list_remove(&input_method->link);
+ input_method->model = NULL;
+ input_method->context = NULL;
+ wl_signal_emit(&ec->hide_input_panel_signal, ec);
+ wl_text_input_send_leave(text_input->resource);
+ }
+}
+
+static void
+destroy_text_input(struct wl_resource *resource)
+{
+ struct text_input *text_input = wl_resource_get_user_data(resource);
+ struct input_method *input_method, *next;
+
+ wl_list_for_each_safe(input_method, next, &text_input->input_methods, link)
+ deactivate_text_input(text_input, input_method);
+
+ free(text_input);
+}
+
+static void
+text_input_set_surrounding_text(struct wl_client *client,
+ struct wl_resource *resource,
+ const char *text,
+ uint32_t cursor,
+ uint32_t anchor)
+{
+ struct text_input *text_input = wl_resource_get_user_data(resource);
+ struct input_method *input_method, *next;
+
+ wl_list_for_each_safe(input_method, next, &text_input->input_methods, link) {
+ if (!input_method->context)
+ continue;
+ wl_input_method_context_send_surrounding_text(input_method->context->resource,
+ text,
+ cursor,
+ anchor);
+ }
+}
+
+static void
+text_input_activate(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *seat,
+ struct wl_resource *surface)
+{
+ struct text_input *text_input = wl_resource_get_user_data(resource);
+ struct weston_seat *weston_seat = wl_resource_get_user_data(seat);
+ struct input_method *input_method = weston_seat->input_method;
+ struct text_input *old = weston_seat->input_method->model;
+ struct weston_compositor *ec = text_input->ec;
+
+ if (old == text_input)
+ return;
+
+ if (old) {
+ deactivate_text_input(old,
+ weston_seat->input_method);
+ }
+
+ input_method->model = text_input;
+ wl_list_insert(&text_input->input_methods, &input_method->link);
+ input_method_init_seat(weston_seat);
+
+ text_input->surface = wl_resource_get_user_data(surface);
+
+ input_method_context_create(text_input, input_method);
+
+ if (text_input->input_panel_visible) {
+ wl_signal_emit(&ec->show_input_panel_signal, text_input->surface);
+ wl_signal_emit(&ec->update_input_panel_signal, &text_input->cursor_rectangle);
+ }
+
+ wl_text_input_send_enter(text_input->resource, text_input->surface->resource);
+}
+
+static void
+text_input_deactivate(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *seat)
+{
+ struct text_input *text_input = wl_resource_get_user_data(resource);
+ struct weston_seat *weston_seat = wl_resource_get_user_data(seat);
+
+ deactivate_text_input(text_input,
+ weston_seat->input_method);
+}
+
+static void
+text_input_reset(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ struct text_input *text_input = wl_resource_get_user_data(resource);
+ struct input_method *input_method, *next;
+
+ wl_list_for_each_safe(input_method, next, &text_input->input_methods, link) {
+ if (!input_method->context)
+ continue;
+ wl_input_method_context_send_reset(input_method->context->resource);
+ }
+}
+
+static void
+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 text_input *text_input = wl_resource_get_user_data(resource);
+ struct weston_compositor *ec = text_input->ec;
+
+ text_input->cursor_rectangle.x1 = x;
+ text_input->cursor_rectangle.y1 = y;
+ text_input->cursor_rectangle.x2 = x + width;
+ text_input->cursor_rectangle.y2 = y + height;
+
+ wl_signal_emit(&ec->update_input_panel_signal, &text_input->cursor_rectangle);
+}
+
+static void
+text_input_set_content_type(struct wl_client *client,
+ struct wl_resource *resource,
+ uint32_t hint,
+ uint32_t purpose)
+{
+ struct text_input *text_input = wl_resource_get_user_data(resource);
+ struct input_method *input_method, *next;
+
+ wl_list_for_each_safe(input_method, next, &text_input->input_methods, link) {
+ if (!input_method->context)
+ continue;
+ wl_input_method_context_send_content_type(input_method->context->resource, hint, purpose);
+ }
+}
+
+static void
+text_input_invoke_action(struct wl_client *client,
+ struct wl_resource *resource,
+ uint32_t button,
+ uint32_t index)
+{
+ struct text_input *text_input = wl_resource_get_user_data(resource);
+ struct input_method *input_method, *next;
+
+ wl_list_for_each_safe(input_method, next, &text_input->input_methods, link) {
+ if (!input_method->context)
+ continue;
+ wl_input_method_context_send_invoke_action(input_method->context->resource, button, index);
+ }
+}
+
+static void
+text_input_commit_state(struct wl_client *client,
+ struct wl_resource *resource,
+ uint32_t serial)
+{
+ struct text_input *text_input = wl_resource_get_user_data(resource);
+ struct input_method *input_method, *next;
+
+ wl_list_for_each_safe(input_method, next, &text_input->input_methods, link) {
+ if (!input_method->context)
+ continue;
+ wl_input_method_context_send_commit_state(input_method->context->resource, serial);
+ }
+}
+
+static void
+text_input_show_input_panel(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ struct text_input *text_input = wl_resource_get_user_data(resource);
+ struct weston_compositor *ec = text_input->ec;
+
+ text_input->input_panel_visible = 1;
+
+ if (!wl_list_empty(&text_input->input_methods)) {
+ wl_signal_emit(&ec->show_input_panel_signal, text_input->surface);
+ wl_signal_emit(&ec->update_input_panel_signal, &text_input->cursor_rectangle);
+ }
+}
+
+static void
+text_input_hide_input_panel(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ struct text_input *text_input = wl_resource_get_user_data(resource);
+ struct weston_compositor *ec = text_input->ec;
+
+ text_input->input_panel_visible = 0;
+
+ if (!wl_list_empty(&text_input->input_methods))
+ wl_signal_emit(&ec->hide_input_panel_signal, ec);
+}
+
+static void
+text_input_set_preferred_language(struct wl_client *client,
+ struct wl_resource *resource,
+ const char *language)
+{
+ struct text_input *text_input = wl_resource_get_user_data(resource);
+ struct input_method *input_method, *next;
+
+ wl_list_for_each_safe(input_method, next, &text_input->input_methods, link) {
+ if (!input_method->context)
+ continue;
+ wl_input_method_context_send_preferred_language(input_method->context->resource,
+ language);
+ }
+}
+
+static const struct wl_text_input_interface text_input_implementation = {
+ text_input_activate,
+ text_input_deactivate,
+ text_input_show_input_panel,
+ text_input_hide_input_panel,
+ text_input_reset,
+ text_input_set_surrounding_text,
+ text_input_set_content_type,
+ text_input_set_cursor_rectangle,
+ text_input_set_preferred_language,
+ text_input_commit_state,
+ text_input_invoke_action
+};
+
+static void text_input_manager_create_text_input(struct wl_client *client,
+ struct wl_resource *resource,
+ uint32_t id)
+{
+ struct text_input_manager *text_input_manager = wl_resource_get_user_data(resource);
+ struct text_input *text_input;
+
+ text_input = calloc(1, sizeof *text_input);
+
+ text_input->resource =
+ wl_resource_create(client, &wl_text_input_interface, 1, id);
+ wl_resource_set_implementation(text_input->resource,
+ &text_input_implementation,
+ text_input, destroy_text_input);
+
+ text_input->ec = text_input_manager->ec;
+
+ wl_list_init(&text_input->input_methods);
+};
+
+static const struct wl_text_input_manager_interface text_input_manager_implementation = {
+ text_input_manager_create_text_input
+};
+
+static void
+bind_text_input_manager(struct wl_client *client,
+ void *data,
+ uint32_t version,
+ uint32_t id)
+{
+ struct text_input_manager *text_input_manager = data;
+ struct wl_resource *resource;
+
+ /* No checking for duplicate binding necessary. */
+ resource =
+ wl_resource_create(client,
+ &wl_text_input_manager_interface, 1, id);
+ if (resource)
+ wl_resource_set_implementation(resource,
+ &text_input_manager_implementation,
+ text_input_manager, NULL);
+}
+
+static void
+text_input_manager_notifier_destroy(struct wl_listener *listener, void *data)
+{
+ struct text_input_manager *text_input_manager =
+ container_of(listener, struct text_input_manager, destroy_listener);
+
+ wl_global_destroy(text_input_manager->text_input_manager_global);
+
+ free(text_input_manager);
+}
+
+static void
+text_input_manager_create(struct weston_compositor *ec)
+{
+ struct text_input_manager *text_input_manager;
+
+ text_input_manager = calloc(1, sizeof *text_input_manager);
+
+ text_input_manager->ec = ec;
+
+ text_input_manager->text_input_manager_global =
+ wl_global_create(ec->wl_display,
+ &wl_text_input_manager_interface, 1,
+ text_input_manager, bind_text_input_manager);
+
+ text_input_manager->destroy_listener.notify = text_input_manager_notifier_destroy;
+ wl_signal_add(&ec->destroy_signal, &text_input_manager->destroy_listener);
+}
+
+static void
+input_method_context_destroy(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static void
+input_method_context_commit_string(struct wl_client *client,
+ struct wl_resource *resource,
+ uint32_t serial,
+ const char *text)
+{
+ struct input_method_context *context =
+ wl_resource_get_user_data(resource);
+
+ wl_text_input_send_commit_string(context->model->resource, serial, text);
+}
+
+static void
+input_method_context_preedit_string(struct wl_client *client,
+ struct wl_resource *resource,
+ uint32_t serial,
+ const char *text,
+ const char *commit)
+{
+ struct input_method_context *context =
+ wl_resource_get_user_data(resource);
+
+ wl_text_input_send_preedit_string(context->model->resource, serial, text, commit);
+}
+
+static void
+input_method_context_preedit_styling(struct wl_client *client,
+ struct wl_resource *resource,
+ uint32_t index,
+ uint32_t length,
+ uint32_t style)
+{
+ struct input_method_context *context =
+ wl_resource_get_user_data(resource);
+
+ wl_text_input_send_preedit_styling(context->model->resource, index, length, style);
+}
+
+static void
+input_method_context_preedit_cursor(struct wl_client *client,
+ struct wl_resource *resource,
+ int32_t cursor)
+{
+ struct input_method_context *context =
+ wl_resource_get_user_data(resource);
+
+ wl_text_input_send_preedit_cursor(context->model->resource, cursor);
+}
+
+static void
+input_method_context_delete_surrounding_text(struct wl_client *client,
+ struct wl_resource *resource,
+ int32_t index,
+ uint32_t length)
+{
+ struct input_method_context *context =
+ wl_resource_get_user_data(resource);
+
+ wl_text_input_send_delete_surrounding_text(context->model->resource, index, length);
+}
+
+static void
+input_method_context_cursor_position(struct wl_client *client,
+ struct wl_resource *resource,
+ int32_t index,
+ int32_t anchor)
+{
+ struct input_method_context *context =
+ wl_resource_get_user_data(resource);
+
+ wl_text_input_send_cursor_position(context->model->resource, index, anchor);
+}
+
+static void
+input_method_context_modifiers_map(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_array *map)
+{
+ struct input_method_context *context =
+ wl_resource_get_user_data(resource);
+
+ wl_text_input_send_modifiers_map(context->model->resource, map);
+}
+
+static void
+input_method_context_keysym(struct wl_client *client,
+ struct wl_resource *resource,
+ uint32_t serial,
+ uint32_t time,
+ uint32_t sym,
+ uint32_t state,
+ uint32_t modifiers)
+{
+ struct input_method_context *context =
+ wl_resource_get_user_data(resource);
+
+ wl_text_input_send_keysym(context->model->resource, serial, time,
+ sym, state, modifiers);
+}
+
+static void
+unbind_keyboard(struct wl_resource *resource)
+{
+ struct input_method_context *context =
+ wl_resource_get_user_data(resource);
+
+ input_method_context_end_keyboard_grab(context);
+ context->keyboard = NULL;
+}
+
+static void
+input_method_context_grab_key(struct weston_keyboard_grab *grab,
+ uint32_t time, uint32_t key, uint32_t state_w)
+{
+ struct weston_keyboard *keyboard = grab->keyboard;
+ struct wl_display *display;
+ uint32_t serial;
+
+ if (!keyboard->input_method_resource)
+ return;
+
+ display = wl_client_get_display(wl_resource_get_client(keyboard->input_method_resource));
+ serial = wl_display_next_serial(display);
+ wl_keyboard_send_key(keyboard->input_method_resource,
+ serial, time, key, state_w);
+}
+
+static void
+input_method_context_grab_modifier(struct weston_keyboard_grab *grab, uint32_t serial,
+ uint32_t mods_depressed, uint32_t mods_latched,
+ uint32_t mods_locked, uint32_t group)
+{
+ struct weston_keyboard *keyboard = grab->keyboard;
+
+ if (!keyboard->input_method_resource)
+ return;
+
+ wl_keyboard_send_modifiers(keyboard->input_method_resource,
+ serial, mods_depressed, mods_latched,
+ mods_locked, group);
+}
+
+static void
+input_method_context_grab_cancel(struct weston_keyboard_grab *grab)
+{
+ weston_keyboard_end_grab(grab->keyboard);
+}
+
+static const struct weston_keyboard_grab_interface input_method_context_grab = {
+ input_method_context_grab_key,
+ input_method_context_grab_modifier,
+ input_method_context_grab_cancel,
+};
+
+static void
+input_method_context_grab_keyboard(struct wl_client *client,
+ struct wl_resource *resource,
+ uint32_t id)
+{
+ struct input_method_context *context = wl_resource_get_user_data(resource);
+ struct wl_resource *cr;
+ struct weston_seat *seat = context->input_method->seat;
+ struct weston_keyboard *keyboard = seat->keyboard;
+
+ cr = wl_resource_create(client, &wl_keyboard_interface, 1, id);
+ wl_resource_set_implementation(cr, NULL, context, unbind_keyboard);
+
+ context->keyboard = cr;
+
+ wl_keyboard_send_keymap(cr, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1,
+ seat->xkb_info->keymap_fd,
+ seat->xkb_info->keymap_size);
+
+ if (keyboard->grab != &keyboard->default_grab) {
+ weston_keyboard_end_grab(keyboard);
+ }
+ weston_keyboard_start_grab(keyboard, &keyboard->input_method_grab);
+ keyboard->input_method_resource = cr;
+}
+
+static void
+input_method_context_key(struct wl_client *client,
+ struct wl_resource *resource,
+ uint32_t serial,
+ uint32_t time,
+ uint32_t key,
+ uint32_t state_w)
+{
+ struct input_method_context *context = wl_resource_get_user_data(resource);
+ struct weston_seat *seat = context->input_method->seat;
+ struct weston_keyboard *keyboard = seat->keyboard;
+ struct weston_keyboard_grab *default_grab = &keyboard->default_grab;
+
+ default_grab->interface->key(default_grab, time, key, state_w);
+}
+
+static void
+input_method_context_modifiers(struct wl_client *client,
+ struct wl_resource *resource,
+ uint32_t serial,
+ uint32_t mods_depressed,
+ uint32_t mods_latched,
+ uint32_t mods_locked,
+ uint32_t group)
+{
+ struct input_method_context *context = wl_resource_get_user_data(resource);
+
+ struct weston_seat *seat = context->input_method->seat;
+ struct weston_keyboard *keyboard = seat->keyboard;
+ struct weston_keyboard_grab *default_grab = &keyboard->default_grab;
+
+ default_grab->interface->modifiers(default_grab,
+ serial, mods_depressed,
+ mods_latched, mods_locked,
+ group);
+}
+
+static void
+input_method_context_language(struct wl_client *client,
+ struct wl_resource *resource,
+ uint32_t serial,
+ const char *language)
+{
+ struct input_method_context *context = wl_resource_get_user_data(resource);
+
+ wl_text_input_send_language(context->model->resource, serial, language);
+}
+
+static void
+input_method_context_text_direction(struct wl_client *client,
+ struct wl_resource *resource,
+ uint32_t serial,
+ uint32_t direction)
+{
+ struct input_method_context *context = wl_resource_get_user_data(resource);
+
+ wl_text_input_send_text_direction(context->model->resource, serial, direction);
+}
+
+
+static const struct wl_input_method_context_interface input_method_context_implementation = {
+ input_method_context_destroy,
+ input_method_context_commit_string,
+ input_method_context_preedit_string,
+ input_method_context_preedit_styling,
+ input_method_context_preedit_cursor,
+ input_method_context_delete_surrounding_text,
+ input_method_context_cursor_position,
+ input_method_context_modifiers_map,
+ input_method_context_keysym,
+ input_method_context_grab_keyboard,
+ input_method_context_key,
+ input_method_context_modifiers,
+ input_method_context_language,
+ input_method_context_text_direction
+};
+
+static void
+destroy_input_method_context(struct wl_resource *resource)
+{
+ struct input_method_context *context = wl_resource_get_user_data(resource);
+
+ if (context->keyboard) {
+ wl_resource_destroy(context->keyboard);
+ }
+
+ free(context);
+}
+
+static void
+input_method_context_create(struct text_input *model,
+ struct input_method *input_method)
+{
+ struct input_method_context *context;
+ struct wl_resource *binding;
+
+ if (!input_method->input_method_binding)
+ return;
+
+ context = calloc(1, sizeof *context);
+ if (context == NULL)
+ return;
+
+ binding = input_method->input_method_binding;
+ context->resource =
+ wl_resource_create(wl_resource_get_client(binding),
+ &wl_input_method_context_interface, 1, 0);
+ wl_resource_set_implementation(context->resource,
+ &input_method_context_implementation,
+ context, destroy_input_method_context);
+
+ context->model = model;
+ context->input_method = input_method;
+ input_method->context = context;
+
+
+ wl_input_method_send_activate(binding, context->resource);
+}
+
+static void
+input_method_context_end_keyboard_grab(struct input_method_context *context)
+{
+ struct weston_keyboard_grab *grab =
+ &context->input_method->seat->keyboard->input_method_grab;
+ struct weston_keyboard *keyboard = grab->keyboard;
+
+ if (!grab->keyboard)
+ return;
+
+ if (grab->keyboard->grab == grab)
+ weston_keyboard_end_grab(grab->keyboard);
+
+ keyboard->input_method_resource = NULL;
+}
+
+static void
+unbind_input_method(struct wl_resource *resource)
+{
+ struct input_method *input_method = wl_resource_get_user_data(resource);
+ struct text_backend *text_backend = input_method->text_backend;
+
+ input_method->input_method_binding = NULL;
+ input_method->context = NULL;
+
+ text_backend->input_method.binding = NULL;
+}
+
+static void
+bind_input_method(struct wl_client *client,
+ void *data,
+ uint32_t version,
+ uint32_t id)
+{
+ struct input_method *input_method = data;
+ struct text_backend *text_backend = input_method->text_backend;
+ struct wl_resource *resource;
+
+ resource =
+ wl_resource_create(client, &wl_input_method_interface, 1, id);
+
+ if (input_method->input_method_binding != NULL) {
+ wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
+ "interface object already bound");
+ wl_resource_destroy(resource);
+ return;
+ }
+
+ if (text_backend->input_method.client != client) {
+ wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
+ "permission to bind input_method denied");
+ wl_resource_destroy(resource);
+ return;
+ }
+
+ wl_resource_set_implementation(resource, NULL, input_method,
+ unbind_input_method);
+ input_method->input_method_binding = resource;
+
+ text_backend->input_method.binding = resource;
+}
+
+static void
+input_method_notifier_destroy(struct wl_listener *listener, void *data)
+{
+ struct input_method *input_method =
+ container_of(listener, struct input_method, destroy_listener);
+
+ if (input_method->model)
+ deactivate_text_input(input_method->model, input_method);
+
+ wl_global_destroy(input_method->input_method_global);
+ wl_list_remove(&input_method->destroy_listener.link);
+
+ free(input_method);
+}
+
+static void
+handle_keyboard_focus(struct wl_listener *listener, void *data)
+{
+ struct weston_keyboard *keyboard = data;
+ struct input_method *input_method =
+ container_of(listener, struct input_method, keyboard_focus_listener);
+ struct weston_surface *surface = keyboard->focus;
+
+ if (!input_method->model)
+ return;
+
+ if (!surface || input_method->model->surface != surface)
+ deactivate_text_input(input_method->model,
+ input_method);
+}
+
+static void
+input_method_init_seat(struct weston_seat *seat)
+{
+ if (seat->input_method->focus_listener_initialized)
+ return;
+
+ if (seat->keyboard) {
+ seat->input_method->keyboard_focus_listener.notify = handle_keyboard_focus;
+ wl_signal_add(&seat->keyboard->focus_signal, &seat->input_method->keyboard_focus_listener);
+ seat->keyboard->input_method_grab.interface = &input_method_context_grab;
+ }
+
+ seat->input_method->focus_listener_initialized = 1;
+}
+
+static void launch_input_method(struct text_backend *text_backend);
+
+static void
+handle_input_method_sigchld(struct weston_process *process, int status)
+{
+ uint32_t time;
+ struct text_backend *text_backend =
+ container_of(process, struct text_backend, input_method.process);
+
+ text_backend->input_method.process.pid = 0;
+ text_backend->input_method.client = NULL;
+
+ /* if input_method dies more than 5 times in 10 seconds, give up */
+ time = weston_compositor_get_time();
+ if (time - text_backend->input_method.deathstamp > 10000) {
+ text_backend->input_method.deathstamp = time;
+ text_backend->input_method.deathcount = 0;
+ }
+
+ text_backend->input_method.deathcount++;
+ if (text_backend->input_method.deathcount > 5) {
+ weston_log("input_method died, giving up.\n");
+ return;
+ }
+
+ weston_log("input_method died, respawning...\n");
+ launch_input_method(text_backend);
+}
+
+static void
+launch_input_method(struct text_backend *text_backend)
+{
+ if (text_backend->input_method.binding)
+ return;
+
+ if (!text_backend->input_method.path)
+ return;
+
+ if (text_backend->input_method.process.pid != 0)
+ return;
+
+ text_backend->input_method.client = weston_client_launch(text_backend->compositor,
+ &text_backend->input_method.process,
+ text_backend->input_method.path,
+ handle_input_method_sigchld);
+
+ if (!text_backend->input_method.client)
+ weston_log("not able to start %s\n", text_backend->input_method.path);
+}
+
+static void
+handle_seat_created(struct wl_listener *listener,
+ void *data)
+{
+ struct weston_seat *seat = data;
+ struct text_backend *text_backend =
+ container_of(listener, struct text_backend,
+ seat_created_listener);
+ struct input_method *input_method;
+ struct weston_compositor *ec = seat->compositor;
+
+ input_method = calloc(1, sizeof *input_method);
+
+ input_method->seat = seat;
+ input_method->model = NULL;
+ input_method->focus_listener_initialized = 0;
+ input_method->context = NULL;
+ input_method->text_backend = text_backend;
+
+ input_method->input_method_global =
+ wl_global_create(ec->wl_display, &wl_input_method_interface, 1,
+ input_method, bind_input_method);
+
+ input_method->destroy_listener.notify = input_method_notifier_destroy;
+ wl_signal_add(&seat->destroy_signal, &input_method->destroy_listener);
+
+ seat->input_method = input_method;
+
+ launch_input_method(text_backend);
+}
+
+static void
+text_backend_configuration(struct text_backend *text_backend)
+{
+ struct weston_config_section *section;
+
+ section = weston_config_get_section(text_backend->compositor->config,
+ "input-method", NULL, NULL);
+ weston_config_section_get_string(section, "path",
+ &text_backend->input_method.path,
+ LIBEXECDIR "/weston-keyboard");
+}
+
+static void
+text_backend_notifier_destroy(struct wl_listener *listener, void *data)
+{
+ struct text_backend *text_backend =
+ container_of(listener, struct text_backend, destroy_listener);
+
+ if (text_backend->input_method.client)
+ wl_client_destroy(text_backend->input_method.client);
+
+ free(text_backend->input_method.path);
+
+ free(text_backend);
+}
+
+
+WL_EXPORT int
+text_backend_init(struct weston_compositor *ec)
+{
+ struct text_backend *text_backend;
+
+ text_backend = calloc(1, sizeof(*text_backend));
+
+ text_backend->compositor = ec;
+
+ text_backend->seat_created_listener.notify = handle_seat_created;
+ wl_signal_add(&ec->seat_created_signal,
+ &text_backend->seat_created_listener);
+
+ text_backend->destroy_listener.notify = text_backend_notifier_destroy;
+ wl_signal_add(&ec->destroy_signal, &text_backend->destroy_listener);
+
+ text_backend_configuration(text_backend);
+
+ text_input_manager_create(ec);
+
+ return 0;
+}
diff --git a/src/udev-seat.c b/src/udev-seat.c
new file mode 100644
index 00000000..ffaf08aa
--- /dev/null
+++ b/src/udev-seat.c
@@ -0,0 +1,380 @@
+/*
+ * Copyright © 2013 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 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.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "compositor.h"
+#include "launcher-util.h"
+#include "evdev.h"
+#include "udev-seat.h"
+
+static const char default_seat[] = "seat0";
+static const char default_seat_name[] = "default";
+
+static struct udev_seat *
+udev_seat_create(struct weston_compositor *c, const char *seat_name);
+static void
+udev_seat_destroy(struct udev_seat *seat);
+
+static int
+device_added(struct udev_device *udev_device, struct udev_input *input)
+{
+ struct weston_compositor *c;
+ struct evdev_device *device;
+ struct weston_output *output;
+ const char *devnode;
+ const char *device_seat, *seat_name, *output_name;
+ const char *calibration_values;
+ int fd;
+ struct udev_seat *seat;
+
+ device_seat = udev_device_get_property_value(udev_device, "ID_SEAT");
+ if (!device_seat)
+ device_seat = default_seat;
+
+ if (strcmp(device_seat, input->seat_id))
+ return 0;
+
+ c = input->compositor;
+ devnode = udev_device_get_devnode(udev_device);
+
+ /* Search for matching logical seat */
+ seat_name = udev_device_get_property_value(udev_device, "WL_SEAT");
+ if (!seat_name)
+ seat_name = default_seat_name;
+
+ seat = udev_seat_get_named(c, seat_name);
+
+ if (seat == NULL)
+ return -1;
+
+ /* Use non-blocking mode so that we can loop on read on
+ * evdev_device_data() until all events on the fd are
+ * read. mtdev_get() also expects this. */
+ fd = weston_launcher_open(c->launcher, devnode, O_RDWR | O_NONBLOCK);
+ if (fd < 0) {
+ weston_log("opening input device '%s' failed.\n", devnode);
+ return 0;
+ }
+
+ device = evdev_device_create(&seat->base, devnode, fd);
+ if (device == EVDEV_UNHANDLED_DEVICE) {
+ close(fd);
+ weston_log("not using input device '%s'.\n", devnode);
+ return 0;
+ } else if (device == NULL) {
+ close(fd);
+ weston_log("failed to create input device '%s'.\n", devnode);
+ return 0;
+ }
+
+ calibration_values =
+ udev_device_get_property_value(udev_device,
+ "WL_CALIBRATION");
+
+ if (calibration_values && sscanf(calibration_values,
+ "%f %f %f %f %f %f",
+ &device->abs.calibration[0],
+ &device->abs.calibration[1],
+ &device->abs.calibration[2],
+ &device->abs.calibration[3],
+ &device->abs.calibration[4],
+ &device->abs.calibration[5]) == 6) {
+ device->abs.apply_calibration = 1;
+ weston_log ("Applying calibration: %f %f %f %f %f %f\n",
+ device->abs.calibration[0],
+ device->abs.calibration[1],
+ device->abs.calibration[2],
+ device->abs.calibration[3],
+ device->abs.calibration[4],
+ device->abs.calibration[5]);
+ }
+
+ wl_list_insert(seat->devices_list.prev, &device->link);
+
+ if (seat->base.output && seat->base.pointer)
+ weston_pointer_clamp(seat->base.pointer,
+ &seat->base.pointer->x,
+ &seat->base.pointer->y);
+
+ output_name = udev_device_get_property_value(udev_device, "WL_OUTPUT");
+ if (output_name) {
+ wl_list_for_each(output, &c->output_list, link)
+ if (strcmp(output->name, output_name) == 0)
+ device->output = output;
+ }
+
+ if (input->enabled == 1)
+ weston_seat_repick(&seat->base);
+
+ return 0;
+}
+
+static int
+udev_input_add_devices(struct udev_input *input, struct udev *udev)
+{
+ struct udev_enumerate *e;
+ struct udev_list_entry *entry;
+ struct udev_device *device;
+ const char *path, *sysname;
+ struct udev_seat *seat;
+ int devices_found = 0;
+
+ e = udev_enumerate_new(udev);
+ udev_enumerate_add_match_subsystem(e, "input");
+ udev_enumerate_scan_devices(e);
+ udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) {
+ path = udev_list_entry_get_name(entry);
+ device = udev_device_new_from_syspath(udev, path);
+
+ sysname = udev_device_get_sysname(device);
+ if (strncmp("event", sysname, 5) != 0) {
+ udev_device_unref(device);
+ continue;
+ }
+
+ if (device_added(device, input) < 0) {
+ udev_device_unref(device);
+ udev_enumerate_unref(e);
+ return -1;
+ }
+
+ udev_device_unref(device);
+ }
+ udev_enumerate_unref(e);
+
+ wl_list_for_each(seat, &input->compositor->seat_list, base.link) {
+ evdev_notify_keyboard_focus(&seat->base, &seat->devices_list);
+
+ if (!wl_list_empty(&seat->devices_list))
+ devices_found = 1;
+ }
+
+ if (devices_found == 0) {
+ weston_log(
+ "warning: no input devices on entering Weston. "
+ "Possible causes:\n"
+ "\t- no permissions to read /dev/input/event*\n"
+ "\t- seats misconfigured "
+ "(Weston backend option 'seat', "
+ "udev device property ID_SEAT)\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+evdev_udev_handler(int fd, uint32_t mask, void *data)
+{
+ struct udev_input *input = data;
+ struct udev_device *udev_device;
+ struct evdev_device *device, *next;
+ const char *action;
+ const char *devnode;
+ struct udev_seat *seat;
+
+ udev_device = udev_monitor_receive_device(input->udev_monitor);
+ if (!udev_device)
+ return 1;
+
+ action = udev_device_get_action(udev_device);
+ if (!action)
+ goto out;
+
+ if (strncmp("event", udev_device_get_sysname(udev_device), 5) != 0)
+ goto out;
+
+ if (!strcmp(action, "add")) {
+ device_added(udev_device, input);
+ }
+ else if (!strcmp(action, "remove")) {
+ devnode = udev_device_get_devnode(udev_device);
+ wl_list_for_each(seat, &input->compositor->seat_list, base.link) {
+ wl_list_for_each_safe(device, next, &seat->devices_list, link)
+ if (!strcmp(device->devnode, devnode)) {
+ weston_log("input device %s, %s removed\n",
+ device->devname, device->devnode);
+ evdev_device_destroy(device);
+ break;
+ }
+ }
+ }
+
+out:
+ udev_device_unref(udev_device);
+
+ return 0;
+}
+
+int
+udev_input_enable(struct udev_input *input, struct udev *udev)
+{
+ struct wl_event_loop *loop;
+ struct weston_compositor *c = input->compositor;
+ int fd;
+
+ input->udev_monitor = udev_monitor_new_from_netlink(udev, "udev");
+ if (!input->udev_monitor) {
+ weston_log("udev: failed to create the udev monitor\n");
+ return -1;
+ }
+
+ udev_monitor_filter_add_match_subsystem_devtype(input->udev_monitor,
+ "input", NULL);
+
+ if (udev_monitor_enable_receiving(input->udev_monitor)) {
+ weston_log("udev: failed to bind the udev monitor\n");
+ udev_monitor_unref(input->udev_monitor);
+ return -1;
+ }
+
+ loop = wl_display_get_event_loop(c->wl_display);
+ fd = udev_monitor_get_fd(input->udev_monitor);
+ input->udev_monitor_source =
+ wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE,
+ evdev_udev_handler, input);
+ if (!input->udev_monitor_source) {
+ udev_monitor_unref(input->udev_monitor);
+ return -1;
+ }
+
+ if (udev_input_add_devices(input, udev) < 0)
+ return -1;
+
+ input->enabled = 1;
+
+ return 0;
+}
+
+static void
+udev_input_remove_devices(struct udev_input *input)
+{
+ struct evdev_device *device, *next;
+ struct udev_seat *seat;
+
+ wl_list_for_each(seat, &input->compositor->seat_list, base.link) {
+ wl_list_for_each_safe(device, next, &seat->devices_list, link)
+ evdev_device_destroy(device);
+
+ if (seat->base.keyboard)
+ notify_keyboard_focus_out(&seat->base);
+ }
+}
+
+void
+udev_input_disable(struct udev_input *input)
+{
+ if (!input->udev_monitor)
+ return;
+
+ udev_monitor_unref(input->udev_monitor);
+ input->udev_monitor = NULL;
+ wl_event_source_remove(input->udev_monitor_source);
+ input->udev_monitor_source = NULL;
+
+ udev_input_remove_devices(input);
+}
+
+
+int
+udev_input_init(struct udev_input *input, struct weston_compositor *c, struct udev *udev,
+ const char *seat_id)
+{
+ memset(input, 0, sizeof *input);
+ input->seat_id = strdup(seat_id);
+ input->compositor = c;
+ if (udev_input_enable(input, udev) < 0)
+ goto err;
+
+ return 0;
+
+ err:
+ free(input->seat_id);
+ return -1;
+}
+
+void
+udev_input_destroy(struct udev_input *input)
+{
+ struct udev_seat *seat, *next;
+ udev_input_disable(input);
+ wl_list_for_each_safe(seat, next, &input->compositor->seat_list, base.link)
+ udev_seat_destroy(seat);
+ free(input->seat_id);
+}
+
+static void
+drm_led_update(struct weston_seat *seat_base, enum weston_led leds)
+{
+ struct udev_seat *seat = (struct udev_seat *) seat_base;
+ struct evdev_device *device;
+
+ wl_list_for_each(device, &seat->devices_list, link)
+ evdev_led_update(device, leds);
+}
+
+static struct udev_seat *
+udev_seat_create(struct weston_compositor *c, const char *seat_name)
+{
+ struct udev_seat *seat;
+
+ seat = zalloc(sizeof *seat);
+
+ if (!seat)
+ return NULL;
+ weston_seat_init(&seat->base, c, seat_name);
+ seat->base.led_update = drm_led_update;
+
+ wl_list_init(&seat->devices_list);
+ return seat;
+}
+
+static void
+udev_seat_destroy(struct udev_seat *seat)
+{
+ weston_seat_release(&seat->base);
+ free(seat);
+}
+
+struct udev_seat *
+udev_seat_get_named(struct weston_compositor *c, const char *seat_name)
+{
+ struct udev_seat *seat;
+
+ wl_list_for_each(seat, &c->seat_list, base.link) {
+ if (strcmp(seat->base.seat_name, seat_name) == 0)
+ return seat;
+ }
+
+ seat = udev_seat_create(c, seat_name);
+
+ if (!seat)
+ return NULL;
+
+ return seat;
+}
diff --git a/src/udev-seat.h b/src/udev-seat.h
new file mode 100644
index 00000000..4cb6f071
--- /dev/null
+++ b/src/udev-seat.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright © 2013 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 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.
+ */
+
+#ifndef _UDEV_SEAT_H_
+#define _UDEV_SEAT_H_
+
+#include "config.h"
+
+#include <libudev.h>
+
+#include "compositor.h"
+
+struct udev_seat {
+ struct weston_seat base;
+ struct wl_list devices_list;
+};
+
+struct udev_input {
+ struct udev_monitor *udev_monitor;
+ struct wl_event_source *udev_monitor_source;
+ char *seat_id;
+ struct weston_compositor *compositor;
+ int enabled;
+};
+
+
+int udev_input_enable(struct udev_input *input, struct udev *udev);
+void udev_input_disable(struct udev_input *input);
+int udev_input_init(struct udev_input *input,
+ struct weston_compositor *c,
+ struct udev *udev,
+ const char *seat_id);
+void udev_input_destroy(struct udev_input *input);
+
+struct udev_seat *udev_seat_get_named(struct weston_compositor *c,
+ const char *seat_name);
+#endif
diff --git a/src/vaapi-recorder.c b/src/vaapi-recorder.c
new file mode 100644
index 00000000..41314071
--- /dev/null
+++ b/src/vaapi-recorder.c
@@ -0,0 +1,1154 @@
+/*
+ * Copyright © 2013 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 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 (c) 2012 Intel Corporation. All Rights Reserved.
+ *
+ * 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, sub license, 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 NON-INFRINGEMENT.
+ * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <pthread.h>
+
+#include <va/va.h>
+#include <va/va_drm.h>
+#include <va/va_drmcommon.h>
+#include <va/va_enc_h264.h>
+#include <va/va_vpp.h>
+
+#include "compositor.h"
+#include "vaapi-recorder.h"
+
+#define NAL_REF_IDC_NONE 0
+#define NAL_REF_IDC_LOW 1
+#define NAL_REF_IDC_MEDIUM 2
+#define NAL_REF_IDC_HIGH 3
+
+#define NAL_NON_IDR 1
+#define NAL_IDR 5
+#define NAL_SPS 7
+#define NAL_PPS 8
+#define NAL_SEI 6
+
+#define SLICE_TYPE_P 0
+#define SLICE_TYPE_B 1
+#define SLICE_TYPE_I 2
+
+#define ENTROPY_MODE_CAVLC 0
+#define ENTROPY_MODE_CABAC 1
+
+#define PROFILE_IDC_BASELINE 66
+#define PROFILE_IDC_MAIN 77
+#define PROFILE_IDC_HIGH 100
+
+struct vaapi_recorder {
+ int drm_fd, output_fd;
+ int width, height;
+ int frame_count;
+
+ int destroying;
+ pthread_t worker_thread;
+ pthread_mutex_t mutex;
+ pthread_cond_t input_cond;
+
+ struct {
+ int valid;
+ int prime_fd, stride;
+ } input;
+
+ VADisplay va_dpy;
+
+ /* video post processing is used for colorspace conversion */
+ struct {
+ VAConfigID cfg;
+ VAContextID ctx;
+ VABufferID pipeline_buf;
+ VASurfaceID output;
+ } vpp;
+
+ struct {
+ VAConfigID cfg;
+ VAContextID ctx;
+ VASurfaceID reference_picture[3];
+
+ int intra_period;
+ int output_size;
+ int constraint_set_flag;
+
+ struct {
+ VAEncSequenceParameterBufferH264 seq;
+ VAEncPictureParameterBufferH264 pic;
+ VAEncSliceParameterBufferH264 slice;
+ } param;
+ } encoder;
+};
+
+static void *
+worker_thread_function(void *);
+
+/* bistream code used for writing the packed headers */
+
+#define BITSTREAM_ALLOCATE_STEPPING 4096
+
+struct bitstream {
+ unsigned int *buffer;
+ int bit_offset;
+ int max_size_in_dword;
+};
+
+static unsigned int
+va_swap32(unsigned int val)
+{
+ unsigned char *pval = (unsigned char *)&val;
+
+ return ((pval[0] << 24) |
+ (pval[1] << 16) |
+ (pval[2] << 8) |
+ (pval[3] << 0));
+}
+
+static void
+bitstream_start(struct bitstream *bs)
+{
+ bs->max_size_in_dword = BITSTREAM_ALLOCATE_STEPPING;
+ bs->buffer = calloc(bs->max_size_in_dword * sizeof(int), 1);
+ bs->bit_offset = 0;
+}
+
+static void
+bitstream_end(struct bitstream *bs)
+{
+ int pos = (bs->bit_offset >> 5);
+ int bit_offset = (bs->bit_offset & 0x1f);
+ int bit_left = 32 - bit_offset;
+
+ if (bit_offset) {
+ bs->buffer[pos] = va_swap32((bs->buffer[pos] << bit_left));
+ }
+}
+
+static void
+bitstream_put_ui(struct bitstream *bs, unsigned int val, int size_in_bits)
+{
+ int pos = (bs->bit_offset >> 5);
+ int bit_offset = (bs->bit_offset & 0x1f);
+ int bit_left = 32 - bit_offset;
+
+ if (!size_in_bits)
+ return;
+
+ bs->bit_offset += size_in_bits;
+
+ if (bit_left > size_in_bits) {
+ bs->buffer[pos] = (bs->buffer[pos] << size_in_bits | val);
+ return;
+ }
+
+ size_in_bits -= bit_left;
+ bs->buffer[pos] =
+ (bs->buffer[pos] << bit_left) | (val >> size_in_bits);
+ bs->buffer[pos] = va_swap32(bs->buffer[pos]);
+
+ if (pos + 1 == bs->max_size_in_dword) {
+ bs->max_size_in_dword += BITSTREAM_ALLOCATE_STEPPING;
+ bs->buffer =
+ realloc(bs->buffer,
+ bs->max_size_in_dword * sizeof(unsigned int));
+ }
+
+ bs->buffer[pos + 1] = val;
+}
+
+static void
+bitstream_put_ue(struct bitstream *bs, unsigned int val)
+{
+ int size_in_bits = 0;
+ int tmp_val = ++val;
+
+ while (tmp_val) {
+ tmp_val >>= 1;
+ size_in_bits++;
+ }
+
+ bitstream_put_ui(bs, 0, size_in_bits - 1); // leading zero
+ bitstream_put_ui(bs, val, size_in_bits);
+}
+
+static void
+bitstream_put_se(struct bitstream *bs, int val)
+{
+ unsigned int new_val;
+
+ if (val <= 0)
+ new_val = -2 * val;
+ else
+ new_val = 2 * val - 1;
+
+ bitstream_put_ue(bs, new_val);
+}
+
+static void
+bitstream_byte_aligning(struct bitstream *bs, int bit)
+{
+ int bit_offset = (bs->bit_offset & 0x7);
+ int bit_left = 8 - bit_offset;
+ int new_val;
+
+ if (!bit_offset)
+ return;
+
+ if (bit)
+ new_val = (1 << bit_left) - 1;
+ else
+ new_val = 0;
+
+ bitstream_put_ui(bs, new_val, bit_left);
+}
+
+static VAStatus
+encoder_create_config(struct vaapi_recorder *r)
+{
+ VAConfigAttrib attrib[2];
+ VAStatus status;
+
+ /* FIXME: should check if VAEntrypointEncSlice is supported */
+
+ /* FIXME: should check if specified attributes are supported */
+
+ attrib[0].type = VAConfigAttribRTFormat;
+ attrib[0].value = VA_RT_FORMAT_YUV420;
+
+ attrib[1].type = VAConfigAttribRateControl;
+ attrib[1].value = VA_RC_CQP;
+
+ status = vaCreateConfig(r->va_dpy, VAProfileH264Main,
+ VAEntrypointEncSlice, attrib, 2,
+ &r->encoder.cfg);
+ if (status != VA_STATUS_SUCCESS)
+ return status;
+
+ status = vaCreateContext(r->va_dpy, r->encoder.cfg,
+ r->width, r->height, VA_PROGRESSIVE, 0, 0,
+ &r->encoder.ctx);
+ if (status != VA_STATUS_SUCCESS) {
+ vaDestroyConfig(r->va_dpy, r->encoder.cfg);
+ return status;
+ }
+
+ return VA_STATUS_SUCCESS;
+}
+
+static void
+encoder_destroy_config(struct vaapi_recorder *r)
+{
+ vaDestroyContext(r->va_dpy, r->encoder.ctx);
+ vaDestroyConfig(r->va_dpy, r->encoder.cfg);
+}
+
+static void
+encoder_init_seq_parameters(struct vaapi_recorder *r)
+{
+ int width_in_mbs, height_in_mbs;
+ int frame_cropping_flag = 0;
+ int frame_crop_bottom_offset = 0;
+
+ width_in_mbs = (r->width + 15) / 16;
+ height_in_mbs = (r->height + 15) / 16;
+
+ r->encoder.param.seq.level_idc = 41;
+ r->encoder.param.seq.intra_period = r->encoder.intra_period;
+ r->encoder.param.seq.max_num_ref_frames = 4;
+ r->encoder.param.seq.picture_width_in_mbs = width_in_mbs;
+ r->encoder.param.seq.picture_height_in_mbs = height_in_mbs;
+ r->encoder.param.seq.seq_fields.bits.frame_mbs_only_flag = 1;
+
+ /* Tc = num_units_in_tick / time_scale */
+ r->encoder.param.seq.time_scale = 1800;
+ r->encoder.param.seq.num_units_in_tick = 15;
+
+ if (height_in_mbs * 16 - r->height > 0) {
+ frame_cropping_flag = 1;
+ frame_crop_bottom_offset = (height_in_mbs * 16 - r->height) / 2;
+ }
+
+ r->encoder.param.seq.frame_cropping_flag = frame_cropping_flag;
+ r->encoder.param.seq.frame_crop_bottom_offset = frame_crop_bottom_offset;
+
+ r->encoder.param.seq.seq_fields.bits.log2_max_pic_order_cnt_lsb_minus4 = 2;
+}
+
+static VABufferID
+encoder_update_seq_parameters(struct vaapi_recorder *r)
+{
+ VABufferID seq_buf;
+ VAStatus status;
+
+ status = vaCreateBuffer(r->va_dpy, r->encoder.ctx,
+ VAEncSequenceParameterBufferType,
+ sizeof(r->encoder.param.seq),
+ 1, &r->encoder.param.seq,
+ &seq_buf);
+
+ if (status == VA_STATUS_SUCCESS)
+ return seq_buf;
+ else
+ return VA_INVALID_ID;
+}
+
+static void
+encoder_init_pic_parameters(struct vaapi_recorder *r)
+{
+ VAEncPictureParameterBufferH264 *pic = &r->encoder.param.pic;
+
+ pic->pic_init_qp = 0;
+
+ /* ENTROPY_MODE_CABAC */
+ pic->pic_fields.bits.entropy_coding_mode_flag = 1;
+
+ pic->pic_fields.bits.deblocking_filter_control_present_flag = 1;
+}
+
+static VABufferID
+encoder_update_pic_parameters(struct vaapi_recorder *r,
+ VABufferID output_buf)
+{
+ VAEncPictureParameterBufferH264 *pic = &r->encoder.param.pic;
+ VAStatus status;
+ VABufferID pic_param_buf;
+ VASurfaceID curr_pic, pic0;
+
+ curr_pic = r->encoder.reference_picture[r->frame_count % 2];
+ pic0 = r->encoder.reference_picture[(r->frame_count + 1) % 2];
+
+ pic->CurrPic.picture_id = curr_pic;
+ pic->CurrPic.TopFieldOrderCnt = r->frame_count * 2;
+ pic->ReferenceFrames[0].picture_id = pic0;
+ pic->ReferenceFrames[1].picture_id = r->encoder.reference_picture[2];
+ pic->ReferenceFrames[2].picture_id = VA_INVALID_ID;
+
+ pic->coded_buf = output_buf;
+ pic->frame_num = r->frame_count;
+
+ pic->pic_fields.bits.idr_pic_flag = (r->frame_count == 0);
+ pic->pic_fields.bits.reference_pic_flag = 1;
+
+ status = vaCreateBuffer(r->va_dpy, r->encoder.ctx,
+ VAEncPictureParameterBufferType,
+ sizeof(VAEncPictureParameterBufferH264), 1,
+ pic, &pic_param_buf);
+
+ if (status == VA_STATUS_SUCCESS)
+ return pic_param_buf;
+ else
+ return VA_INVALID_ID;
+}
+
+static VABufferID
+encoder_update_slice_parameter(struct vaapi_recorder *r, int slice_type)
+{
+ VABufferID slice_param_buf;
+ VAStatus status;
+
+ int width_in_mbs = (r->width + 15) / 16;
+ int height_in_mbs = (r->height + 15) / 16;
+
+ memset(&r->encoder.param.slice, 0, sizeof r->encoder.param.slice);
+
+ r->encoder.param.slice.num_macroblocks = width_in_mbs * height_in_mbs;
+ r->encoder.param.slice.slice_type = slice_type;
+
+ r->encoder.param.slice.slice_alpha_c0_offset_div2 = 2;
+ r->encoder.param.slice.slice_beta_offset_div2 = 2;
+
+ status = vaCreateBuffer(r->va_dpy, r->encoder.ctx,
+ VAEncSliceParameterBufferType,
+ sizeof(r->encoder.param.slice), 1,
+ &r->encoder.param.slice,
+ &slice_param_buf);
+
+ if (status == VA_STATUS_SUCCESS)
+ return slice_param_buf;
+ else
+ return VA_INVALID_ID;
+}
+
+static VABufferID
+encoder_update_misc_hdr_parameter(struct vaapi_recorder *r)
+{
+ VAEncMiscParameterBuffer *misc_param;
+ VAEncMiscParameterHRD *hrd;
+ VABufferID buffer;
+ VAStatus status;
+
+ int total_size =
+ sizeof(VAEncMiscParameterBuffer) +
+ sizeof(VAEncMiscParameterRateControl);
+
+ status = vaCreateBuffer(r->va_dpy, r->encoder.ctx,
+ VAEncMiscParameterBufferType, total_size,
+ 1, NULL, &buffer);
+ if (status != VA_STATUS_SUCCESS)
+ return VA_INVALID_ID;
+
+ status = vaMapBuffer(r->va_dpy, buffer, (void **) &misc_param);
+ if (status != VA_STATUS_SUCCESS) {
+ vaDestroyBuffer(r->va_dpy, buffer);
+ return VA_INVALID_ID;
+ }
+
+ misc_param->type = VAEncMiscParameterTypeHRD;
+ hrd = (VAEncMiscParameterHRD *) misc_param->data;
+
+ hrd->initial_buffer_fullness = 0;
+ hrd->buffer_size = 0;
+
+ vaUnmapBuffer(r->va_dpy, buffer);
+
+ return buffer;
+}
+
+static int
+setup_encoder(struct vaapi_recorder *r)
+{
+ VAStatus status;
+
+ status = encoder_create_config(r);
+ if (status != VA_STATUS_SUCCESS) {
+ return -1;
+ }
+
+ status = vaCreateSurfaces(r->va_dpy, VA_RT_FORMAT_YUV420,
+ r->width, r->height,
+ r->encoder.reference_picture, 3,
+ NULL, 0);
+ if (status != VA_STATUS_SUCCESS) {
+ encoder_destroy_config(r);
+ return -1;
+ }
+
+ /* VAProfileH264Main */
+ r->encoder.constraint_set_flag |= (1 << 1); /* Annex A.2.2 */
+
+ r->encoder.output_size = r->width * r->height;
+
+ r->encoder.intra_period = 30;
+
+ encoder_init_seq_parameters(r);
+ encoder_init_pic_parameters(r);
+
+ return 0;
+}
+
+static void
+encoder_destroy(struct vaapi_recorder *r)
+{
+ vaDestroySurfaces(r->va_dpy, r->encoder.reference_picture, 3);
+
+ encoder_destroy_config(r);
+}
+
+static void
+nal_start_code_prefix(struct bitstream *bs)
+{
+ bitstream_put_ui(bs, 0x00000001, 32);
+}
+
+static void
+nal_header(struct bitstream *bs, int nal_ref_idc, int nal_unit_type)
+{
+ /* forbidden_zero_bit: 0 */
+ bitstream_put_ui(bs, 0, 1);
+
+ bitstream_put_ui(bs, nal_ref_idc, 2);
+ bitstream_put_ui(bs, nal_unit_type, 5);
+}
+
+static void
+rbsp_trailing_bits(struct bitstream *bs)
+{
+ bitstream_put_ui(bs, 1, 1);
+ bitstream_byte_aligning(bs, 0);
+}
+
+static void sps_rbsp(struct bitstream *bs,
+ VAEncSequenceParameterBufferH264 *seq,
+ int constraint_set_flag)
+{
+ int i;
+
+ bitstream_put_ui(bs, PROFILE_IDC_MAIN, 8);
+
+ /* constraint_set[0-3] flag */
+ for (i = 0; i < 4; i++) {
+ int set = (constraint_set_flag & (1 << i)) ? 1 : 0;
+ bitstream_put_ui(bs, set, 1);
+ }
+
+ /* reserved_zero_4bits */
+ bitstream_put_ui(bs, 0, 4);
+ bitstream_put_ui(bs, seq->level_idc, 8);
+ bitstream_put_ue(bs, seq->seq_parameter_set_id);
+
+ bitstream_put_ue(bs, seq->seq_fields.bits.log2_max_frame_num_minus4);
+ bitstream_put_ue(bs, seq->seq_fields.bits.pic_order_cnt_type);
+ bitstream_put_ue(bs,
+ seq->seq_fields.bits.log2_max_pic_order_cnt_lsb_minus4);
+
+ bitstream_put_ue(bs, seq->max_num_ref_frames);
+
+ /* gaps_in_frame_num_value_allowed_flag */
+ bitstream_put_ui(bs, 0, 1);
+
+ /* pic_width_in_mbs_minus1, pic_height_in_map_units_minus1 */
+ bitstream_put_ue(bs, seq->picture_width_in_mbs - 1);
+ bitstream_put_ue(bs, seq->picture_height_in_mbs - 1);
+
+ bitstream_put_ui(bs, seq->seq_fields.bits.frame_mbs_only_flag, 1);
+ bitstream_put_ui(bs, seq->seq_fields.bits.direct_8x8_inference_flag, 1);
+
+ bitstream_put_ui(bs, seq->frame_cropping_flag, 1);
+
+ if (seq->frame_cropping_flag) {
+ bitstream_put_ue(bs, seq->frame_crop_left_offset);
+ bitstream_put_ue(bs, seq->frame_crop_right_offset);
+ bitstream_put_ue(bs, seq->frame_crop_top_offset);
+ bitstream_put_ue(bs, seq->frame_crop_bottom_offset);
+ }
+
+ /* vui_parameters_present_flag */
+ bitstream_put_ui(bs, 1, 1);
+
+ /* aspect_ratio_info_present_flag */
+ bitstream_put_ui(bs, 0, 1);
+ /* overscan_info_present_flag */
+ bitstream_put_ui(bs, 0, 1);
+
+ /* video_signal_type_present_flag */
+ bitstream_put_ui(bs, 0, 1);
+ /* chroma_loc_info_present_flag */
+ bitstream_put_ui(bs, 0, 1);
+
+ /* timing_info_present_flag */
+ bitstream_put_ui(bs, 1, 1);
+ bitstream_put_ui(bs, seq->num_units_in_tick, 32);
+ bitstream_put_ui(bs, seq->time_scale, 32);
+ /* fixed_frame_rate_flag */
+ bitstream_put_ui(bs, 1, 1);
+
+ /* nal_hrd_parameters_present_flag */
+ bitstream_put_ui(bs, 0, 1);
+
+ /* vcl_hrd_parameters_present_flag */
+ bitstream_put_ui(bs, 0, 1);
+
+ /* low_delay_hrd_flag */
+ bitstream_put_ui(bs, 0, 1);
+
+ /* pic_struct_present_flag */
+ bitstream_put_ui(bs, 0, 1);
+ /* bitstream_restriction_flag */
+ bitstream_put_ui(bs, 0, 1);
+
+ rbsp_trailing_bits(bs);
+}
+
+static void pps_rbsp(struct bitstream *bs,
+ VAEncPictureParameterBufferH264 *pic)
+{
+ /* pic_parameter_set_id, seq_parameter_set_id */
+ bitstream_put_ue(bs, pic->pic_parameter_set_id);
+ bitstream_put_ue(bs, pic->seq_parameter_set_id);
+
+ bitstream_put_ui(bs, pic->pic_fields.bits.entropy_coding_mode_flag, 1);
+
+ /* pic_order_present_flag: 0 */
+ bitstream_put_ui(bs, 0, 1);
+
+ /* num_slice_groups_minus1 */
+ bitstream_put_ue(bs, 0);
+
+ bitstream_put_ue(bs, pic->num_ref_idx_l0_active_minus1);
+ bitstream_put_ue(bs, pic->num_ref_idx_l1_active_minus1);
+
+ bitstream_put_ui(bs, pic->pic_fields.bits.weighted_pred_flag, 1);
+ bitstream_put_ui(bs, pic->pic_fields.bits.weighted_bipred_idc, 2);
+
+ /* pic_init_qp_minus26, pic_init_qs_minus26, chroma_qp_index_offset */
+ bitstream_put_se(bs, pic->pic_init_qp - 26);
+ bitstream_put_se(bs, 0);
+ bitstream_put_se(bs, 0);
+
+ bitstream_put_ui(bs, pic->pic_fields.bits.deblocking_filter_control_present_flag, 1);
+
+ /* constrained_intra_pred_flag, redundant_pic_cnt_present_flag */
+ bitstream_put_ui(bs, 0, 1);
+ bitstream_put_ui(bs, 0, 1);
+
+ bitstream_put_ui(bs, pic->pic_fields.bits.transform_8x8_mode_flag, 1);
+
+ /* pic_scaling_matrix_present_flag */
+ bitstream_put_ui(bs, 0, 1);
+ bitstream_put_se(bs, pic->second_chroma_qp_index_offset );
+
+ rbsp_trailing_bits(bs);
+}
+
+static int
+build_packed_pic_buffer(struct vaapi_recorder *r,
+ void **header_buffer)
+{
+ struct bitstream bs;
+
+ bitstream_start(&bs);
+ nal_start_code_prefix(&bs);
+ nal_header(&bs, NAL_REF_IDC_HIGH, NAL_PPS);
+ pps_rbsp(&bs, &r->encoder.param.pic);
+ bitstream_end(&bs);
+
+ *header_buffer = bs.buffer;
+ return bs.bit_offset;
+}
+
+static int
+build_packed_seq_buffer(struct vaapi_recorder *r,
+ void **header_buffer)
+{
+ struct bitstream bs;
+
+ bitstream_start(&bs);
+ nal_start_code_prefix(&bs);
+ nal_header(&bs, NAL_REF_IDC_HIGH, NAL_SPS);
+ sps_rbsp(&bs, &r->encoder.param.seq, r->encoder.constraint_set_flag);
+ bitstream_end(&bs);
+
+ *header_buffer = bs.buffer;
+ return bs.bit_offset;
+}
+
+static int
+create_packed_header_buffers(struct vaapi_recorder *r, VABufferID *buffers,
+ VAEncPackedHeaderType type,
+ void *data, int bit_length)
+{
+ VAEncPackedHeaderParameterBuffer packed_header;
+ VAStatus status;
+
+ packed_header.type = type;
+ packed_header.bit_length = bit_length;
+ packed_header.has_emulation_bytes = 0;
+
+ status = vaCreateBuffer(r->va_dpy, r->encoder.ctx,
+ VAEncPackedHeaderParameterBufferType,
+ sizeof packed_header, 1, &packed_header,
+ &buffers[0]);
+ if (status != VA_STATUS_SUCCESS)
+ return 0;
+
+ status = vaCreateBuffer(r->va_dpy, r->encoder.ctx,
+ VAEncPackedHeaderDataBufferType,
+ (bit_length + 7) / 8, 1, data, &buffers[1]);
+ if (status != VA_STATUS_SUCCESS) {
+ vaDestroyBuffer(r->va_dpy, buffers[0]);
+ return 0;
+ }
+
+ return 2;
+}
+
+static int
+encoder_prepare_headers(struct vaapi_recorder *r, VABufferID *buffers)
+{
+ VABufferID *p;
+
+ int bit_length;
+ void *data;
+
+ p = buffers;
+
+ bit_length = build_packed_seq_buffer(r, &data);
+ p += create_packed_header_buffers(r, p, VAEncPackedHeaderSequence,
+ data, bit_length);
+ free(data);
+
+ bit_length = build_packed_pic_buffer(r, &data);
+ p += create_packed_header_buffers(r, p, VAEncPackedHeaderPicture,
+ data, bit_length);
+ free(data);
+
+ return p - buffers;
+}
+
+static VAStatus
+encoder_render_picture(struct vaapi_recorder *r, VASurfaceID input,
+ VABufferID *buffers, int count)
+{
+ VAStatus status;
+
+ status = vaBeginPicture(r->va_dpy, r->encoder.ctx, input);
+ if (status != VA_STATUS_SUCCESS)
+ return status;
+
+ status = vaRenderPicture(r->va_dpy, r->encoder.ctx, buffers, count);
+ if (status != VA_STATUS_SUCCESS)
+ return status;
+
+ status = vaEndPicture(r->va_dpy, r->encoder.ctx);
+ if (status != VA_STATUS_SUCCESS)
+ return status;
+
+ return vaSyncSurface(r->va_dpy, input);
+}
+
+static VABufferID
+encoder_create_output_buffer(struct vaapi_recorder *r)
+{
+ VABufferID output_buf;
+ VAStatus status;
+
+ status = vaCreateBuffer(r->va_dpy, r->encoder.ctx,
+ VAEncCodedBufferType, r->encoder.output_size,
+ 1, NULL, &output_buf);
+ if (status == VA_STATUS_SUCCESS)
+ return output_buf;
+ else
+ return VA_INVALID_ID;
+}
+
+static int
+encoder_write_output(struct vaapi_recorder *r, VABufferID output_buf)
+{
+ VACodedBufferSegment *segment;
+ VAStatus status;
+ int count;
+
+ status = vaMapBuffer(r->va_dpy, output_buf, (void **) &segment);
+ if (status != VA_STATUS_SUCCESS)
+ return -1;
+
+ if (segment->status & VA_CODED_BUF_STATUS_SLICE_OVERFLOW_MASK) {
+ r->encoder.output_size *= 2;
+ vaUnmapBuffer(r->va_dpy, output_buf);
+ return -1;
+ }
+
+ count = write(r->output_fd, segment->buf, segment->size);
+
+ vaUnmapBuffer(r->va_dpy, output_buf);
+
+ return count;
+}
+
+static void
+encoder_encode(struct vaapi_recorder *r, VASurfaceID input)
+{
+ VABufferID output_buf = VA_INVALID_ID;
+
+ VABufferID buffers[8];
+ int count = 0;
+
+ int slice_type;
+ int ret, i;
+
+ if ((r->frame_count % r->encoder.intra_period) == 0)
+ slice_type = SLICE_TYPE_I;
+ else
+ slice_type = SLICE_TYPE_P;
+
+ buffers[count++] = encoder_update_seq_parameters(r);
+ buffers[count++] = encoder_update_misc_hdr_parameter(r);
+ buffers[count++] = encoder_update_slice_parameter(r, slice_type);
+
+ for (i = 0; i < count; i++)
+ if (buffers[i] == VA_INVALID_ID)
+ goto bail;
+
+ if (r->frame_count == 0)
+ count += encoder_prepare_headers(r, buffers + count);
+
+ do {
+ output_buf = encoder_create_output_buffer(r);
+ if (output_buf == VA_INVALID_ID)
+ goto bail;
+
+ buffers[count++] =
+ encoder_update_pic_parameters(r, output_buf);
+ if (buffers[count - 1] == VA_INVALID_ID)
+ goto bail;
+
+ encoder_render_picture(r, input, buffers, count);
+ ret = encoder_write_output(r, output_buf);
+
+ vaDestroyBuffer(r->va_dpy, output_buf);
+ output_buf = VA_INVALID_ID;
+
+ vaDestroyBuffer(r->va_dpy, buffers[--count]);
+ } while (ret < 0);
+
+ for (i = 0; i < count; i++)
+ vaDestroyBuffer(r->va_dpy, buffers[i]);
+
+ r->frame_count++;
+ return;
+
+bail:
+ for (i = 0; i < count; i++)
+ vaDestroyBuffer(r->va_dpy, buffers[i]);
+ if (output_buf != VA_INVALID_ID)
+ vaDestroyBuffer(r->va_dpy, output_buf);
+}
+
+
+static int
+setup_vpp(struct vaapi_recorder *r)
+{
+ VAStatus status;
+
+ status = vaCreateConfig(r->va_dpy, VAProfileNone,
+ VAEntrypointVideoProc, NULL, 0,
+ &r->vpp.cfg);
+ if (status != VA_STATUS_SUCCESS) {
+ weston_log("vaapi: failed to create VPP config\n");
+ return -1;
+ }
+
+ status = vaCreateContext(r->va_dpy, r->vpp.cfg, r->width, r->height,
+ 0, NULL, 0, &r->vpp.ctx);
+ if (status != VA_STATUS_SUCCESS) {
+ weston_log("vaapi: failed to create VPP context\n");
+ goto err_cfg;
+ }
+
+ status = vaCreateBuffer(r->va_dpy, r->vpp.ctx,
+ VAProcPipelineParameterBufferType,
+ sizeof(VAProcPipelineParameterBuffer),
+ 1, NULL, &r->vpp.pipeline_buf);
+ if (status != VA_STATUS_SUCCESS) {
+ weston_log("vaapi: failed to create VPP pipeline buffer\n");
+ goto err_ctx;
+ }
+
+ status = vaCreateSurfaces(r->va_dpy, VA_RT_FORMAT_YUV420,
+ r->width, r->height, &r->vpp.output, 1,
+ NULL, 0);
+ if (status != VA_STATUS_SUCCESS) {
+ weston_log("vaapi: failed to create YUV surface\n");
+ goto err_buf;
+ }
+
+ return 0;
+
+err_buf:
+ vaDestroyBuffer(r->va_dpy, r->vpp.pipeline_buf);
+err_ctx:
+ vaDestroyConfig(r->va_dpy, r->vpp.ctx);
+err_cfg:
+ vaDestroyConfig(r->va_dpy, r->vpp.cfg);
+
+ return -1;
+}
+
+static void
+vpp_destroy(struct vaapi_recorder *r)
+{
+ vaDestroySurfaces(r->va_dpy, &r->vpp.output, 1);
+ vaDestroyBuffer(r->va_dpy, r->vpp.pipeline_buf);
+ vaDestroyConfig(r->va_dpy, r->vpp.ctx);
+ vaDestroyConfig(r->va_dpy, r->vpp.cfg);
+}
+
+static int
+setup_worker_thread(struct vaapi_recorder *r)
+{
+ pthread_mutex_init(&r->mutex, NULL);
+ pthread_cond_init(&r->input_cond, NULL);
+ pthread_create(&r->worker_thread, NULL, worker_thread_function, r);
+
+ return 1;
+}
+
+static void
+destroy_worker_thread(struct vaapi_recorder *r)
+{
+ pthread_mutex_lock(&r->mutex);
+
+ /* Make sure the worker thread finishes */
+ r->destroying = 1;
+ pthread_cond_signal(&r->input_cond);
+
+ pthread_mutex_unlock(&r->mutex);
+
+ pthread_join(r->worker_thread, NULL);
+
+ pthread_mutex_destroy(&r->mutex);
+ pthread_cond_destroy(&r->input_cond);
+}
+
+struct vaapi_recorder *
+vaapi_recorder_create(int drm_fd, int width, int height, const char *filename)
+{
+ struct vaapi_recorder *r;
+ VAStatus status;
+ int major, minor;
+ int flags;
+
+ r = calloc(1, sizeof *r);
+ if (!r)
+ return NULL;
+
+ r->width = width;
+ r->height = height;
+ r->drm_fd = drm_fd;
+
+ if (setup_worker_thread(r) < 0)
+ goto err_free;
+
+ flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC;
+ r->output_fd = open(filename, flags, 0644);
+ if (r->output_fd < 0)
+ goto err_thread;
+
+ r->va_dpy = vaGetDisplayDRM(drm_fd);
+ if (!r->va_dpy) {
+ weston_log("failed to create VA display\n");
+ goto err_fd;
+ }
+
+ status = vaInitialize(r->va_dpy, &major, &minor);
+ if (status != VA_STATUS_SUCCESS) {
+ weston_log("vaapi: failed to initialize display\n");
+ goto err_fd;
+ }
+
+ if (setup_vpp(r) < 0) {
+ weston_log("vaapi: failed to initialize VPP pipeline\n");
+ goto err_va_dpy;
+ }
+
+ if (setup_encoder(r) < 0) {
+ goto err_vpp;
+ }
+
+ return r;
+
+err_vpp:
+ vpp_destroy(r);
+err_va_dpy:
+ vaTerminate(r->va_dpy);
+err_fd:
+ close(r->output_fd);
+err_thread:
+ destroy_worker_thread(r);
+err_free:
+ free(r);
+
+ return NULL;
+}
+
+void
+vaapi_recorder_destroy(struct vaapi_recorder *r)
+{
+ destroy_worker_thread(r);
+
+ encoder_destroy(r);
+ vpp_destroy(r);
+
+ vaTerminate(r->va_dpy);
+
+ close(r->output_fd);
+ close(r->drm_fd);
+
+ free(r);
+}
+
+static VAStatus
+create_surface_from_fd(struct vaapi_recorder *r, int prime_fd,
+ int stride, VASurfaceID *surface)
+{
+ VASurfaceAttrib va_attribs[2];
+ VASurfaceAttribExternalBuffers va_attrib_extbuf;
+ VAStatus status;
+
+ unsigned long buffer_fd = prime_fd;
+
+ va_attrib_extbuf.pixel_format = VA_FOURCC_BGRX;
+ va_attrib_extbuf.width = r->width;
+ va_attrib_extbuf.height = r->height;
+ va_attrib_extbuf.data_size = r->height * stride;
+ va_attrib_extbuf.num_planes = 1;
+ va_attrib_extbuf.pitches[0] = stride;
+ va_attrib_extbuf.offsets[0] = 0;
+ va_attrib_extbuf.buffers = &buffer_fd;
+ va_attrib_extbuf.num_buffers = 1;
+ va_attrib_extbuf.flags = 0;
+ va_attrib_extbuf.private_data = NULL;
+
+ va_attribs[0].type = VASurfaceAttribMemoryType;
+ va_attribs[0].flags = VA_SURFACE_ATTRIB_SETTABLE;
+ va_attribs[0].value.type = VAGenericValueTypeInteger;
+ va_attribs[0].value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME;
+
+ va_attribs[1].type = VASurfaceAttribExternalBufferDescriptor;
+ va_attribs[1].flags = VA_SURFACE_ATTRIB_SETTABLE;
+ va_attribs[1].value.type = VAGenericValueTypePointer;
+ va_attribs[1].value.value.p = &va_attrib_extbuf;
+
+ status = vaCreateSurfaces(r->va_dpy, VA_RT_FORMAT_RGB32,
+ r->width, r->height, surface, 1,
+ va_attribs, 2);
+
+ return status;
+}
+
+static VAStatus
+convert_rgb_to_yuv(struct vaapi_recorder *r, VASurfaceID rgb_surface)
+{
+ VAProcPipelineParameterBuffer *pipeline_param;
+ VAStatus status;
+
+ status = vaMapBuffer(r->va_dpy, r->vpp.pipeline_buf,
+ (void **) &pipeline_param);
+ if (status != VA_STATUS_SUCCESS)
+ return status;
+
+ memset(pipeline_param, 0, sizeof *pipeline_param);
+
+ pipeline_param->surface = rgb_surface;
+ pipeline_param->surface_color_standard = VAProcColorStandardNone;
+
+ pipeline_param->output_background_color = 0xff000000;
+ pipeline_param->output_color_standard = VAProcColorStandardNone;
+
+ status = vaUnmapBuffer(r->va_dpy, r->vpp.pipeline_buf);
+ if (status != VA_STATUS_SUCCESS)
+ return status;
+
+ status = vaBeginPicture(r->va_dpy, r->vpp.ctx, r->vpp.output);
+ if (status != VA_STATUS_SUCCESS)
+ return status;
+
+ status = vaRenderPicture(r->va_dpy, r->vpp.ctx,
+ &r->vpp.pipeline_buf, 1);
+ if (status != VA_STATUS_SUCCESS)
+ return status;
+
+ status = vaEndPicture(r->va_dpy, r->vpp.ctx);
+ if (status != VA_STATUS_SUCCESS)
+ return status;
+
+ return status;
+}
+
+static void
+recorder_frame(struct vaapi_recorder *r)
+{
+ VASurfaceID rgb_surface;
+ VAStatus status;
+
+ status = create_surface_from_fd(r, r->input.prime_fd,
+ r->input.stride, &rgb_surface);
+ if (status != VA_STATUS_SUCCESS) {
+ weston_log("[libva recorder] "
+ "failed to create surface from bo\n");
+ return;
+ }
+
+ close(r->input.prime_fd);
+
+ status = convert_rgb_to_yuv(r, rgb_surface);
+ if (status != VA_STATUS_SUCCESS) {
+ weston_log("[libva recorder] "
+ "color space conversion failed\n");
+ return;
+ }
+
+ encoder_encode(r, r->vpp.output);
+
+ vaDestroySurfaces(r->va_dpy, &rgb_surface, 1);
+}
+
+static void *
+worker_thread_function(void *data)
+{
+ struct vaapi_recorder *r = data;
+
+ pthread_mutex_lock(&r->mutex);
+
+ while (!r->destroying) {
+ if (!r->input.valid)
+ pthread_cond_wait(&r->input_cond, &r->mutex);
+
+ /* If the thread is awaken by destroy_worker_thread(),
+ * there might not be valid input */
+ if (!r->input.valid)
+ continue;
+
+ recorder_frame(r);
+ r->input.valid = 0;
+ }
+
+ pthread_mutex_unlock(&r->mutex);
+
+ return NULL;
+}
+
+void
+vaapi_recorder_frame(struct vaapi_recorder *r, int prime_fd, int stride)
+{
+ pthread_mutex_lock(&r->mutex);
+
+ /* The mutex is never released while encoding, so this point should
+ * never be reached if input.valid is true. */
+ assert(!r->input.valid);
+
+ r->input.prime_fd = prime_fd;
+ r->input.stride = stride;
+ r->input.valid = 1;
+ pthread_cond_signal(&r->input_cond);
+
+ pthread_mutex_unlock(&r->mutex);
+}
diff --git a/src/vaapi-recorder.h b/src/vaapi-recorder.h
new file mode 100644
index 00000000..664b1f96
--- /dev/null
+++ b/src/vaapi-recorder.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright © 2013 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 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.
+ */
+
+#ifndef _VAAPI_RECORDER_H_
+#define _VAAPI_RECORDER_H_
+
+struct vaapi_recorder;
+
+struct vaapi_recorder *
+vaapi_recorder_create(int drm_fd, int width, int height, const char *filename);
+void
+vaapi_recorder_destroy(struct vaapi_recorder *r);
+void
+vaapi_recorder_frame(struct vaapi_recorder *r, int fd, int stride);
+
+#endif /* _VAAPI_RECORDER_H_ */
diff --git a/src/version.h.in b/src/version.h.in
new file mode 100644
index 00000000..79dba45c
--- /dev/null
+++ b/src/version.h.in
@@ -0,0 +1,37 @@
+/*
+ * Copyright © 2013 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
+ * 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.
+ */
+
+#ifndef WESTON_VERSION_H
+#define WESTON_VERSION_H
+
+#define WESTON_VERSION_MAJOR @WESTON_VERSION_MAJOR@
+#define WESTON_VERSION_MINOR @WESTON_VERSION_MINOR@
+#define WESTON_VERSION_MICRO @WESTON_VERSION_MICRO@
+#define WESTON_VERSION "@WESTON_VERSION@"
+
+/* Can be used like #if WESTON_VERSION_AT_LEAST(1, 2, 0) */
+#define WESTON_VERSION_AT_LEAST(major, minor, micro) \
+ (WESTON_VERSION_MAJOR == (major) && \
+ WESTON_VERSION_MINOR == (minor) && \
+ WESTON_VERSION_MICRO >= (micro))
+
+#endif
diff --git a/src/vertex-clipping.c b/src/vertex-clipping.c
new file mode 100644
index 00000000..603ce6f4
--- /dev/null
+++ b/src/vertex-clipping.c
@@ -0,0 +1,317 @@
+/*
+ * Copyright © 2012 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 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.
+ */
+#include <assert.h>
+#include <float.h>
+#include <math.h>
+
+#include <GLES2/gl2.h>
+
+#include "vertex-clipping.h"
+
+GLfloat
+float_difference(GLfloat a, GLfloat b)
+{
+ /* http://www.altdevblogaday.com/2012/02/22/comparing-floating-point-numbers-2012-edition/ */
+ static const GLfloat max_diff = 4.0f * FLT_MIN;
+ static const GLfloat max_rel_diff = 4.0e-5;
+ GLfloat diff = a - b;
+ GLfloat adiff = fabsf(diff);
+
+ if (adiff <= max_diff)
+ return 0.0f;
+
+ a = fabsf(a);
+ b = fabsf(b);
+ if (adiff <= (a > b ? a : b) * max_rel_diff)
+ return 0.0f;
+
+ return diff;
+}
+
+/* A line segment (p1x, p1y)-(p2x, p2y) intersects the line x = x_arg.
+ * Compute the y coordinate of the intersection.
+ */
+static GLfloat
+clip_intersect_y(GLfloat p1x, GLfloat p1y, GLfloat p2x, GLfloat p2y,
+ GLfloat x_arg)
+{
+ GLfloat a;
+ GLfloat diff = float_difference(p1x, p2x);
+
+ /* Practically vertical line segment, yet the end points have already
+ * been determined to be on different sides of the line. Therefore
+ * the line segment is part of the line and intersects everywhere.
+ * Return the end point, so we use the whole line segment.
+ */
+ if (diff == 0.0f)
+ return p2y;
+
+ a = (x_arg - p2x) / diff;
+ return p2y + (p1y - p2y) * a;
+}
+
+/* A line segment (p1x, p1y)-(p2x, p2y) intersects the line y = y_arg.
+ * Compute the x coordinate of the intersection.
+ */
+static GLfloat
+clip_intersect_x(GLfloat p1x, GLfloat p1y, GLfloat p2x, GLfloat p2y,
+ GLfloat y_arg)
+{
+ GLfloat a;
+ GLfloat diff = float_difference(p1y, p2y);
+
+ /* Practically horizontal line segment, yet the end points have already
+ * been determined to be on different sides of the line. Therefore
+ * the line segment is part of the line and intersects everywhere.
+ * Return the end point, so we use the whole line segment.
+ */
+ if (diff == 0.0f)
+ return p2x;
+
+ a = (y_arg - p2y) / diff;
+ return p2x + (p1x - p2x) * a;
+}
+
+enum path_transition {
+ PATH_TRANSITION_OUT_TO_OUT = 0,
+ PATH_TRANSITION_OUT_TO_IN = 1,
+ PATH_TRANSITION_IN_TO_OUT = 2,
+ PATH_TRANSITION_IN_TO_IN = 3,
+};
+
+static void
+clip_append_vertex(struct clip_context *ctx, GLfloat x, GLfloat y)
+{
+ *ctx->vertices.x++ = x;
+ *ctx->vertices.y++ = y;
+}
+
+static enum path_transition
+path_transition_left_edge(struct clip_context *ctx, GLfloat x, GLfloat y)
+{
+ return ((ctx->prev.x >= ctx->clip.x1) << 1) | (x >= ctx->clip.x1);
+}
+
+static enum path_transition
+path_transition_right_edge(struct clip_context *ctx, GLfloat x, GLfloat y)
+{
+ return ((ctx->prev.x < ctx->clip.x2) << 1) | (x < ctx->clip.x2);
+}
+
+static enum path_transition
+path_transition_top_edge(struct clip_context *ctx, GLfloat x, GLfloat y)
+{
+ return ((ctx->prev.y >= ctx->clip.y1) << 1) | (y >= ctx->clip.y1);
+}
+
+static enum path_transition
+path_transition_bottom_edge(struct clip_context *ctx, GLfloat x, GLfloat y)
+{
+ return ((ctx->prev.y < ctx->clip.y2) << 1) | (y < ctx->clip.y2);
+}
+
+static void
+clip_polygon_leftright(struct clip_context *ctx,
+ enum path_transition transition,
+ GLfloat x, GLfloat y, GLfloat clip_x)
+{
+ GLfloat yi;
+
+ switch (transition) {
+ case PATH_TRANSITION_IN_TO_IN:
+ clip_append_vertex(ctx, x, y);
+ break;
+ case PATH_TRANSITION_IN_TO_OUT:
+ yi = clip_intersect_y(ctx->prev.x, ctx->prev.y, x, y, clip_x);
+ clip_append_vertex(ctx, clip_x, yi);
+ break;
+ case PATH_TRANSITION_OUT_TO_IN:
+ yi = clip_intersect_y(ctx->prev.x, ctx->prev.y, x, y, clip_x);
+ clip_append_vertex(ctx, clip_x, yi);
+ clip_append_vertex(ctx, x, y);
+ break;
+ case PATH_TRANSITION_OUT_TO_OUT:
+ /* nothing */
+ break;
+ default:
+ assert(0 && "bad enum path_transition");
+ }
+
+ ctx->prev.x = x;
+ ctx->prev.y = y;
+}
+
+static void
+clip_polygon_topbottom(struct clip_context *ctx,
+ enum path_transition transition,
+ GLfloat x, GLfloat y, GLfloat clip_y)
+{
+ GLfloat xi;
+
+ switch (transition) {
+ case PATH_TRANSITION_IN_TO_IN:
+ clip_append_vertex(ctx, x, y);
+ break;
+ case PATH_TRANSITION_IN_TO_OUT:
+ xi = clip_intersect_x(ctx->prev.x, ctx->prev.y, x, y, clip_y);
+ clip_append_vertex(ctx, xi, clip_y);
+ break;
+ case PATH_TRANSITION_OUT_TO_IN:
+ xi = clip_intersect_x(ctx->prev.x, ctx->prev.y, x, y, clip_y);
+ clip_append_vertex(ctx, xi, clip_y);
+ clip_append_vertex(ctx, x, y);
+ break;
+ case PATH_TRANSITION_OUT_TO_OUT:
+ /* nothing */
+ break;
+ default:
+ assert(0 && "bad enum path_transition");
+ }
+
+ ctx->prev.x = x;
+ ctx->prev.y = y;
+}
+
+static void
+clip_context_prepare(struct clip_context *ctx, const struct polygon8 *src,
+ GLfloat *dst_x, GLfloat *dst_y)
+{
+ ctx->prev.x = src->x[src->n - 1];
+ ctx->prev.y = src->y[src->n - 1];
+ ctx->vertices.x = dst_x;
+ ctx->vertices.y = dst_y;
+}
+
+static int
+clip_polygon_left(struct clip_context *ctx, const struct polygon8 *src,
+ GLfloat *dst_x, GLfloat *dst_y)
+{
+ enum path_transition trans;
+ int i;
+
+ clip_context_prepare(ctx, src, dst_x, dst_y);
+ for (i = 0; i < src->n; i++) {
+ trans = path_transition_left_edge(ctx, src->x[i], src->y[i]);
+ clip_polygon_leftright(ctx, trans, src->x[i], src->y[i],
+ ctx->clip.x1);
+ }
+ return ctx->vertices.x - dst_x;
+}
+
+static int
+clip_polygon_right(struct clip_context *ctx, const struct polygon8 *src,
+ GLfloat *dst_x, GLfloat *dst_y)
+{
+ enum path_transition trans;
+ int i;
+
+ clip_context_prepare(ctx, src, dst_x, dst_y);
+ for (i = 0; i < src->n; i++) {
+ trans = path_transition_right_edge(ctx, src->x[i], src->y[i]);
+ clip_polygon_leftright(ctx, trans, src->x[i], src->y[i],
+ ctx->clip.x2);
+ }
+ return ctx->vertices.x - dst_x;
+}
+
+static int
+clip_polygon_top(struct clip_context *ctx, const struct polygon8 *src,
+ GLfloat *dst_x, GLfloat *dst_y)
+{
+ enum path_transition trans;
+ int i;
+
+ clip_context_prepare(ctx, src, dst_x, dst_y);
+ for (i = 0; i < src->n; i++) {
+ trans = path_transition_top_edge(ctx, src->x[i], src->y[i]);
+ clip_polygon_topbottom(ctx, trans, src->x[i], src->y[i],
+ ctx->clip.y1);
+ }
+ return ctx->vertices.x - dst_x;
+}
+
+static int
+clip_polygon_bottom(struct clip_context *ctx, const struct polygon8 *src,
+ GLfloat *dst_x, GLfloat *dst_y)
+{
+ enum path_transition trans;
+ int i;
+
+ clip_context_prepare(ctx, src, dst_x, dst_y);
+ for (i = 0; i < src->n; i++) {
+ trans = path_transition_bottom_edge(ctx, src->x[i], src->y[i]);
+ clip_polygon_topbottom(ctx, trans, src->x[i], src->y[i],
+ ctx->clip.y2);
+ }
+ return ctx->vertices.x - dst_x;
+}
+
+#define max(a, b) (((a) > (b)) ? (a) : (b))
+#define min(a, b) (((a) > (b)) ? (b) : (a))
+#define clip(x, a, b) min(max(x, a), b)
+
+int
+clip_simple(struct clip_context *ctx,
+ struct polygon8 *surf,
+ GLfloat *ex,
+ GLfloat *ey)
+{
+ int i;
+ for (i = 0; i < surf->n; i++) {
+ ex[i] = clip(surf->x[i], ctx->clip.x1, ctx->clip.x2);
+ ey[i] = clip(surf->y[i], ctx->clip.y1, ctx->clip.y2);
+ }
+ return surf->n;
+}
+
+int
+clip_transformed(struct clip_context *ctx,
+ struct polygon8 *surf,
+ GLfloat *ex,
+ GLfloat *ey)
+{
+ struct polygon8 polygon;
+ int i, n;
+
+ polygon.n = clip_polygon_left(ctx, surf, polygon.x, polygon.y);
+ surf->n = clip_polygon_right(ctx, &polygon, surf->x, surf->y);
+ polygon.n = clip_polygon_top(ctx, surf, polygon.x, polygon.y);
+ surf->n = clip_polygon_bottom(ctx, &polygon, surf->x, surf->y);
+
+ /* Get rid of duplicate vertices */
+ ex[0] = surf->x[0];
+ ey[0] = surf->y[0];
+ n = 1;
+ for (i = 1; i < surf->n; i++) {
+ if (float_difference(ex[n - 1], surf->x[i]) == 0.0f &&
+ float_difference(ey[n - 1], surf->y[i]) == 0.0f)
+ continue;
+ ex[n] = surf->x[i];
+ ey[n] = surf->y[i];
+ n++;
+ }
+ if (float_difference(ex[n - 1], surf->x[0]) == 0.0f &&
+ float_difference(ey[n - 1], surf->y[0]) == 0.0f)
+ n--;
+
+ return n;
+}
diff --git a/src/vertex-clipping.h b/src/vertex-clipping.h
new file mode 100644
index 00000000..a16b1dfc
--- /dev/null
+++ b/src/vertex-clipping.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright © 2012 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 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.
+ */
+#ifndef _WESTON_VERTEX_CLIPPING_H
+#define _WESTON_VERTEX_CLIPPING_H
+
+#include <GLES2/gl2.h>
+
+struct polygon8 {
+ GLfloat x[8];
+ GLfloat y[8];
+ int n;
+};
+
+struct clip_context {
+ struct {
+ GLfloat x;
+ GLfloat y;
+ } prev;
+
+ struct {
+ GLfloat x1, y1;
+ GLfloat x2, y2;
+ } clip;
+
+ struct {
+ GLfloat *x;
+ GLfloat *y;
+ } vertices;
+};
+
+GLfloat
+float_difference(GLfloat a, GLfloat b);
+
+int
+clip_simple(struct clip_context *ctx,
+ struct polygon8 *surf,
+ GLfloat *ex,
+ GLfloat *ey);
+
+int
+clip_transformed(struct clip_context *ctx,
+ struct polygon8 *surf,
+ GLfloat *ex,
+ GLfloat *ey);\
+
+#endif
diff --git a/src/weston-egl-ext.h b/src/weston-egl-ext.h
new file mode 100644
index 00000000..8ddf97f0
--- /dev/null
+++ b/src/weston-egl-ext.h
@@ -0,0 +1,80 @@
+/**************************************************************************
+ *
+ * Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas.
+ * All Rights Reserved.
+ *
+ * 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, sub license, 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.
+ *
+ **************************************************************************/
+
+/* Extensions used by Weston, copied from Mesa's eglmesaext.h, */
+
+#ifndef WESTON_EGL_EXT_H
+#define WESTON_EGL_EXT_H
+
+#ifndef EGL_WL_bind_wayland_display
+#define EGL_WL_bind_wayland_display 1
+
+#define EGL_WAYLAND_BUFFER_WL 0x31D5 /* eglCreateImageKHR target */
+#define EGL_WAYLAND_PLANE_WL 0x31D6 /* eglCreateImageKHR target */
+
+#define EGL_TEXTURE_Y_U_V_WL 0x31D7
+#define EGL_TEXTURE_Y_UV_WL 0x31D8
+#define EGL_TEXTURE_Y_XUXV_WL 0x31D9
+#define EGL_TEXTURE_EXTERNAL_WL 0x31DA
+
+struct wl_display;
+struct wl_resource;
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglBindWaylandDisplayWL(EGLDisplay dpy, struct wl_display *display);
+EGLAPI EGLBoolean EGLAPIENTRY eglUnbindWaylandDisplayWL(EGLDisplay dpy, struct wl_display *display);
+EGLAPI EGLBoolean EGLAPIENTRY eglQueryWaylandBufferWL(EGLDisplay dpy, struct wl_resource *buffer, EGLint attribute, EGLint *value);
+#endif
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLBINDWAYLANDDISPLAYWL) (EGLDisplay dpy, struct wl_display *display);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLUNBINDWAYLANDDISPLAYWL) (EGLDisplay dpy, struct wl_display *display);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYWAYLANDBUFFERWL) (EGLDisplay dpy, struct wl_resource *buffer, EGLint attribute, EGLint *value);
+
+#endif
+
+#ifndef EGL_TEXTURE_EXTERNAL_WL
+#define EGL_TEXTURE_EXTERNAL_WL 0x31DA
+#endif
+
+#ifndef EGL_BUFFER_AGE_EXT
+#define EGL_BUFFER_AGE_EXT 0x313D
+#endif
+
+#ifndef EGL_WAYLAND_Y_INVERTED_WL
+#define EGL_WAYLAND_Y_INVERTED_WL 0x31DB /* eglQueryWaylandBufferWL attribute */
+#endif
+
+/* Mesas gl2ext.h and probably Khronos upstream defined
+ * GL_EXT_unpack_subimage with non _EXT suffixed GL_UNPACK_* tokens.
+ * In case we're using that mess, manually define the _EXT versions
+ * of the tokens here.*/
+#if defined(GL_EXT_unpack_subimage) && !defined(GL_UNPACK_ROW_LENGTH_EXT)
+#define GL_UNPACK_ROW_LENGTH_EXT 0x0CF2
+#define GL_UNPACK_SKIP_ROWS_EXT 0x0CF3
+#define GL_UNPACK_SKIP_PIXELS_EXT 0x0CF4
+#endif
+
+
+#endif
diff --git a/src/weston-launch.c b/src/weston-launch.c
new file mode 100644
index 00000000..d8364c85
--- /dev/null
+++ b/src/weston-launch.c
@@ -0,0 +1,750 @@
+/*
+ * Copyright © 2012 Benjamin Franzke
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that 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.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <poll.h>
+#include <errno.h>
+
+#include <error.h>
+#include <getopt.h>
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/signalfd.h>
+#include <signal.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <linux/vt.h>
+#include <linux/major.h>
+#include <linux/kd.h>
+
+#include <pwd.h>
+#include <grp.h>
+#include <security/pam_appl.h>
+
+#include <xf86drm.h>
+
+#ifdef HAVE_SYSTEMD_LOGIN
+#include <systemd/sd-login.h>
+#endif
+
+#include "weston-launch.h"
+
+#define DRM_MAJOR 226
+
+#ifndef KDSKBMUTE
+#define KDSKBMUTE 0x4B51
+#endif
+
+#ifndef EVIOCREVOKE
+#define EVIOCREVOKE _IOW('E', 0x91, int)
+#endif
+
+#define MAX_ARGV_SIZE 256
+
+struct weston_launch {
+ struct pam_conv pc;
+ pam_handle_t *ph;
+ int tty;
+ int ttynr;
+ int sock[2];
+ int drm_fd;
+ int last_input_fd;
+ int kb_mode;
+ struct passwd *pw;
+
+ int signalfd;
+
+ pid_t child;
+ int verbose;
+ char *new_user;
+};
+
+union cmsg_data { unsigned char b[4]; int fd; };
+
+static gid_t *
+read_groups(void)
+{
+ int n;
+ gid_t *groups;
+
+ n = getgroups(0, NULL);
+
+ if (n < 0) {
+ fprintf(stderr, "Unable to retrieve groups: %m\n");
+ return NULL;
+ }
+
+ groups = malloc(n * sizeof(gid_t));
+ if (!groups)
+ return NULL;
+
+ if (getgroups(n, groups) < 0) {
+ fprintf(stderr, "Unable to retrieve groups: %m\n");
+ free(groups);
+ return NULL;
+ }
+ return groups;
+}
+
+static int
+weston_launch_allowed(struct weston_launch *wl)
+{
+ struct group *gr;
+ gid_t *groups;
+ int i;
+#ifdef HAVE_SYSTEMD_LOGIN
+ char *session, *seat;
+ int err;
+#endif
+
+ if (getuid() == 0)
+ return 1;
+
+ gr = getgrnam("weston-launch");
+ if (gr) {
+ groups = read_groups();
+ if (groups) {
+ for (i = 0; groups[i]; ++i) {
+ if (groups[i] == gr->gr_gid) {
+ free(groups);
+ return 1;
+ }
+ }
+ free(groups);
+ }
+ }
+
+#ifdef HAVE_SYSTEMD_LOGIN
+ err = sd_pid_get_session(getpid(), &session);
+ if (err == 0 && session) {
+ if (sd_session_is_active(session) &&
+ sd_session_get_seat(session, &seat) == 0) {
+ free(seat);
+ free(session);
+ return 1;
+ }
+ free(session);
+ }
+#endif
+
+ return 0;
+}
+
+static int
+pam_conversation_fn(int msg_count,
+ const struct pam_message **messages,
+ struct pam_response **responses,
+ void *user_data)
+{
+ return PAM_SUCCESS;
+}
+
+static int
+setup_pam(struct weston_launch *wl)
+{
+ int err;
+
+ wl->pc.conv = pam_conversation_fn;
+ wl->pc.appdata_ptr = wl;
+
+ err = pam_start("login", wl->pw->pw_name, &wl->pc, &wl->ph);
+ if (err != PAM_SUCCESS) {
+ fprintf(stderr, "failed to start pam transaction: %d: %s\n",
+ err, pam_strerror(wl->ph, err));
+ return -1;
+ }
+
+ err = pam_set_item(wl->ph, PAM_TTY, ttyname(wl->tty));
+ if (err != PAM_SUCCESS) {
+ fprintf(stderr, "failed to set PAM_TTY item: %d: %s\n",
+ err, pam_strerror(wl->ph, err));
+ return -1;
+ }
+
+ err = pam_open_session(wl->ph, 0);
+ if (err != PAM_SUCCESS) {
+ fprintf(stderr, "failed to open pam session: %d: %s\n",
+ err, pam_strerror(wl->ph, err));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+setup_launcher_socket(struct weston_launch *wl)
+{
+ if (socketpair(AF_LOCAL, SOCK_SEQPACKET, 0, wl->sock) < 0)
+ error(1, errno, "socketpair failed");
+
+ if (fcntl(wl->sock[0], F_SETFD, FD_CLOEXEC) < 0)
+ error(1, errno, "fcntl failed");
+
+ return 0;
+}
+
+static int
+setup_signals(struct weston_launch *wl)
+{
+ int ret;
+ sigset_t mask;
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof sa);
+ sa.sa_handler = SIG_DFL;
+ sa.sa_flags = SA_NOCLDSTOP | SA_RESTART;
+ ret = sigaction(SIGCHLD, &sa, NULL);
+ assert(ret == 0);
+
+ sa.sa_handler = SIG_IGN;
+ sa.sa_flags = 0;
+ sigaction(SIGHUP, &sa, NULL);
+
+ ret = sigemptyset(&mask);
+ assert(ret == 0);
+ sigaddset(&mask, SIGCHLD);
+ sigaddset(&mask, SIGINT);
+ sigaddset(&mask, SIGTERM);
+ sigaddset(&mask, SIGUSR1);
+ sigaddset(&mask, SIGUSR2);
+ ret = sigprocmask(SIG_BLOCK, &mask, NULL);
+ assert(ret == 0);
+
+ wl->signalfd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC);
+ if (wl->signalfd < 0)
+ return -errno;
+
+ return 0;
+}
+
+static void
+setenv_fd(const char *env, int fd)
+{
+ char buf[32];
+
+ snprintf(buf, sizeof buf, "%d", fd);
+ setenv(env, buf, 1);
+}
+
+static int
+send_reply(struct weston_launch *wl, int reply)
+{
+ int len;
+
+ do {
+ len = send(wl->sock[0], &reply, sizeof reply, 0);
+ } while (len < 0 && errno == EINTR);
+
+ return len;
+}
+
+static int
+handle_open(struct weston_launch *wl, struct msghdr *msg, ssize_t len)
+{
+ int fd = -1, ret = -1;
+ char control[CMSG_SPACE(sizeof(fd))];
+ struct cmsghdr *cmsg;
+ struct stat s;
+ struct msghdr nmsg;
+ struct iovec iov;
+ struct weston_launcher_open *message;
+ union cmsg_data *data;
+
+ message = msg->msg_iov->iov_base;
+ if ((size_t)len < sizeof(*message))
+ goto err0;
+
+ /* Ensure path is null-terminated */
+ ((char *) message)[len-1] = '\0';
+
+ fd = open(message->path, message->flags);
+ if (fd < 0) {
+ fprintf(stderr, "Error opening device %s: %m\n",
+ message->path);
+ goto err0;
+ }
+
+ if (fstat(fd, &s) < 0) {
+ close(fd);
+ fd = -1;
+ fprintf(stderr, "Failed to stat %s\n", message->path);
+ goto err0;
+ }
+
+ if (major(s.st_rdev) != INPUT_MAJOR &&
+ major(s.st_rdev) != DRM_MAJOR) {
+ close(fd);
+ fd = -1;
+ fprintf(stderr, "Device %s is not an input or drm device\n",
+ message->path);
+ goto err0;
+ }
+
+err0:
+ memset(&nmsg, 0, sizeof nmsg);
+ nmsg.msg_iov = &iov;
+ nmsg.msg_iovlen = 1;
+ if (fd != -1) {
+ nmsg.msg_control = control;
+ nmsg.msg_controllen = sizeof control;
+ cmsg = CMSG_FIRSTHDR(&nmsg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
+ data = (union cmsg_data *) CMSG_DATA(cmsg);
+ data->fd = fd;
+ nmsg.msg_controllen = cmsg->cmsg_len;
+ ret = 0;
+ }
+ iov.iov_base = &ret;
+ iov.iov_len = sizeof ret;
+
+ if (wl->verbose)
+ fprintf(stderr, "weston-launch: opened %s: ret: %d, fd: %d\n",
+ message->path, ret, fd);
+ do {
+ len = sendmsg(wl->sock[0], &nmsg, 0);
+ } while (len < 0 && errno == EINTR);
+
+ if (len < 0)
+ return -1;
+
+ if (fd != -1 && major(s.st_rdev) == DRM_MAJOR)
+ wl->drm_fd = fd;
+ if (fd != -1 && major(s.st_rdev) == INPUT_MAJOR &&
+ wl->last_input_fd < fd)
+ wl->last_input_fd = fd;
+
+ return 0;
+}
+
+static int
+handle_socket_msg(struct weston_launch *wl)
+{
+ char control[CMSG_SPACE(sizeof(int))];
+ char buf[BUFSIZ];
+ struct msghdr msg;
+ struct iovec iov;
+ int ret = -1;
+ ssize_t len;
+ struct weston_launcher_message *message;
+
+ memset(&msg, 0, sizeof(msg));
+ iov.iov_base = buf;
+ iov.iov_len = sizeof buf;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = control;
+ msg.msg_controllen = sizeof control;
+
+ do {
+ len = recvmsg(wl->sock[0], &msg, 0);
+ } while (len < 0 && errno == EINTR);
+
+ if (len < 1)
+ return -1;
+
+ message = (void *) buf;
+ switch (message->opcode) {
+ case WESTON_LAUNCHER_OPEN:
+ ret = handle_open(wl, &msg, len);
+ break;
+ }
+
+ return ret;
+}
+
+static void
+quit(struct weston_launch *wl, int status)
+{
+ struct vt_mode mode = { 0 };
+ int err;
+
+ close(wl->signalfd);
+ close(wl->sock[0]);
+
+ if (wl->new_user) {
+ err = pam_close_session(wl->ph, 0);
+ if (err)
+ fprintf(stderr, "pam_close_session failed: %d: %s\n",
+ err, pam_strerror(wl->ph, err));
+ pam_end(wl->ph, err);
+ }
+
+ if (ioctl(wl->tty, KDSKBMUTE, 0) &&
+ ioctl(wl->tty, KDSKBMODE, wl->kb_mode))
+ fprintf(stderr, "failed to restore keyboard mode: %m\n");
+
+ if (ioctl(wl->tty, KDSETMODE, KD_TEXT))
+ fprintf(stderr, "failed to set KD_TEXT mode on tty: %m\n");
+
+ /* We have to drop master before we switch the VT back in
+ * VT_AUTO, so we don't risk switching to a VT with another
+ * display server, that will then fail to set drm master. */
+ drmDropMaster(wl->drm_fd);
+
+ mode.mode = VT_AUTO;
+ if (ioctl(wl->tty, VT_SETMODE, &mode) < 0)
+ fprintf(stderr, "could not reset vt handling\n");
+
+ exit(status);
+}
+
+static void
+close_input_fds(struct weston_launch *wl)
+{
+ struct stat s;
+ int fd;
+
+ for (fd = 3; fd <= wl->last_input_fd; fd++) {
+ if (fstat(fd, &s) == 0 && major(s.st_rdev) == INPUT_MAJOR) {
+ /* EVIOCREVOKE may fail if the kernel doesn't
+ * support it, but all we can do is ignore it. */
+ ioctl(fd, EVIOCREVOKE, 0);
+ close(fd);
+ }
+ }
+}
+
+static int
+handle_signal(struct weston_launch *wl)
+{
+ struct signalfd_siginfo sig;
+ int pid, status, ret;
+
+ if (read(wl->signalfd, &sig, sizeof sig) != sizeof sig) {
+ error(0, errno, "reading signalfd failed");
+ return -1;
+ }
+
+ switch (sig.ssi_signo) {
+ case SIGCHLD:
+ pid = waitpid(-1, &status, 0);
+ if (pid == wl->child) {
+ wl->child = 0;
+ if (WIFEXITED(status))
+ ret = WEXITSTATUS(status);
+ else if (WIFSIGNALED(status))
+ /*
+ * If weston dies because of signal N, we
+ * return 10+N. This is distinct from
+ * weston-launch dying because of a signal
+ * (128+N).
+ */
+ ret = 10 + WTERMSIG(status);
+ else
+ ret = 0;
+ quit(wl, ret);
+ }
+ break;
+ case SIGTERM:
+ case SIGINT:
+ if (wl->child)
+ kill(wl->child, sig.ssi_signo);
+ break;
+ case SIGUSR1:
+ send_reply(wl, WESTON_LAUNCHER_DEACTIVATE);
+ close_input_fds(wl);
+ drmDropMaster(wl->drm_fd);
+ ioctl(wl->tty, VT_RELDISP, 1);
+ break;
+ case SIGUSR2:
+ ioctl(wl->tty, VT_RELDISP, VT_ACKACQ);
+ drmSetMaster(wl->drm_fd);
+ send_reply(wl, WESTON_LAUNCHER_ACTIVATE);
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+setup_tty(struct weston_launch *wl, const char *tty)
+{
+ struct stat buf;
+ struct vt_mode mode = { 0 };
+ char *t;
+
+ if (!wl->new_user) {
+ wl->tty = STDIN_FILENO;
+ } else if (tty) {
+ t = ttyname(STDIN_FILENO);
+ if (t && strcmp(t, tty) == 0)
+ wl->tty = STDIN_FILENO;
+ else
+ wl->tty = open(tty, O_RDWR | O_NOCTTY);
+ } else {
+ int tty0 = open("/dev/tty0", O_WRONLY | O_CLOEXEC);
+ char filename[16];
+
+ if (tty0 < 0)
+ error(1, errno, "could not open tty0");
+
+ if (ioctl(tty0, VT_OPENQRY, &wl->ttynr) < 0 || wl->ttynr == -1)
+ error(1, errno, "failed to find non-opened console");
+
+ snprintf(filename, sizeof filename, "/dev/tty%d", wl->ttynr);
+ wl->tty = open(filename, O_RDWR | O_NOCTTY);
+ close(tty0);
+ }
+
+ if (wl->tty < 0)
+ error(1, errno, "failed to open tty");
+
+ if (fstat(wl->tty, &buf) == -1 ||
+ major(buf.st_rdev) != TTY_MAJOR || minor(buf.st_rdev) == 0)
+ error(1, 0, "weston-launch must be run from a virtual terminal");
+
+ if (tty) {
+ if (fstat(wl->tty, &buf) < 0)
+ error(1, errno, "stat %s failed", tty);
+
+ if (major(buf.st_rdev) != TTY_MAJOR)
+ error(1, 0, "invalid tty device: %s", tty);
+
+ wl->ttynr = minor(buf.st_rdev);
+ }
+
+ if (ioctl(wl->tty, KDGKBMODE, &wl->kb_mode))
+ error(1, errno, "failed to get current keyboard mode: %m\n");
+
+ if (ioctl(wl->tty, KDSKBMUTE, 1) &&
+ ioctl(wl->tty, KDSKBMODE, K_OFF))
+ error(1, errno, "failed to set K_OFF keyboard mode: %m\n");
+
+ if (ioctl(wl->tty, KDSETMODE, KD_GRAPHICS))
+ error(1, errno, "failed to set KD_GRAPHICS mode on tty: %m\n");
+
+ mode.mode = VT_PROCESS;
+ mode.relsig = SIGUSR1;
+ mode.acqsig = SIGUSR2;
+ if (ioctl(wl->tty, VT_SETMODE, &mode) < 0)
+ error(1, errno, "failed to take control of vt handling\n");
+
+ return 0;
+}
+
+static void
+setup_session(struct weston_launch *wl)
+{
+ char **env;
+ char *term;
+ int i;
+
+ if (wl->tty != STDIN_FILENO) {
+ if (setsid() < 0)
+ error(1, errno, "setsid failed");
+ if (ioctl(wl->tty, TIOCSCTTY, 0) < 0)
+ error(1, errno, "TIOCSCTTY failed - tty is in use");
+ }
+
+ term = getenv("TERM");
+ clearenv();
+ if (term)
+ setenv("TERM", term, 1);
+ setenv("USER", wl->pw->pw_name, 1);
+ setenv("LOGNAME", wl->pw->pw_name, 1);
+ setenv("HOME", wl->pw->pw_dir, 1);
+ setenv("SHELL", wl->pw->pw_shell, 1);
+
+ env = pam_getenvlist(wl->ph);
+ if (env) {
+ for (i = 0; env[i]; ++i) {
+ if (putenv(env[i]) < 0)
+ error(0, 0, "putenv %s failed", env[i]);
+ }
+ free(env);
+ }
+}
+
+static void
+drop_privileges(struct weston_launch *wl)
+{
+ if (setgid(wl->pw->pw_gid) < 0 ||
+#ifdef HAVE_INITGROUPS
+ initgroups(wl->pw->pw_name, wl->pw->pw_gid) < 0 ||
+#endif
+ setuid(wl->pw->pw_uid) < 0)
+ error(1, errno, "dropping privileges failed");
+}
+
+static void
+launch_compositor(struct weston_launch *wl, int argc, char *argv[])
+{
+ char *child_argv[MAX_ARGV_SIZE];
+ sigset_t mask;
+ int i;
+
+ if (wl->verbose)
+ printf("weston-launch: spawned weston with pid: %d\n", getpid());
+ if (wl->new_user)
+ setup_session(wl);
+
+ if (geteuid() == 0)
+ drop_privileges(wl);
+
+ setenv_fd("WESTON_TTY_FD", wl->tty);
+ setenv_fd("WESTON_LAUNCHER_SOCK", wl->sock[1]);
+
+ unsetenv("DISPLAY");
+
+ /* Do not give our signal mask to the new process. */
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGTERM);
+ sigaddset(&mask, SIGCHLD);
+ sigaddset(&mask, SIGINT);
+ sigprocmask(SIG_UNBLOCK, &mask, NULL);
+
+ child_argv[0] = "/bin/sh";
+ child_argv[1] = "-l";
+ child_argv[2] = "-c";
+ child_argv[3] = BINDIR "/weston \"$@\"";
+ child_argv[4] = "weston";
+ for (i = 0; i < argc; ++i)
+ child_argv[5 + i] = argv[i];
+ child_argv[5 + i] = NULL;
+
+ execv(child_argv[0], child_argv);
+ error(1, errno, "exec failed");
+}
+
+static void
+help(const char *name)
+{
+ fprintf(stderr, "Usage: %s [args...] [-- [weston args..]]\n", name);
+ fprintf(stderr, " -u, --user Start session as specified username\n");
+ fprintf(stderr, " -t, --tty Start session on alternative tty\n");
+ fprintf(stderr, " -v, --verbose Be verbose\n");
+ fprintf(stderr, " -h, --help Display this help message\n");
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct weston_launch wl;
+ int i, c;
+ char *tty = NULL;
+ struct option opts[] = {
+ { "user", required_argument, NULL, 'u' },
+ { "tty", required_argument, NULL, 't' },
+ { "verbose", no_argument, NULL, 'v' },
+ { "help", no_argument, NULL, 'h' },
+ { 0, 0, NULL, 0 }
+ };
+
+ memset(&wl, 0, sizeof wl);
+
+ while ((c = getopt_long(argc, argv, "u:t::vh", opts, &i)) != -1) {
+ switch (c) {
+ case 'u':
+ wl.new_user = optarg;
+ if (getuid() != 0)
+ error(1, 0, "Permission denied. -u allowed for root only");
+ break;
+ case 't':
+ tty = optarg;
+ break;
+ case 'v':
+ wl.verbose = 1;
+ break;
+ case 'h':
+ help("weston-launch");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if ((argc - optind) > (MAX_ARGV_SIZE - 6))
+ error(1, E2BIG, "Too many arguments to pass to weston");
+
+ if (wl.new_user)
+ wl.pw = getpwnam(wl.new_user);
+ else
+ wl.pw = getpwuid(getuid());
+ if (wl.pw == NULL)
+ error(1, errno, "failed to get username");
+
+ if (!weston_launch_allowed(&wl))
+ error(1, 0, "Permission denied. You should either:\n"
+#ifdef HAVE_SYSTEMD_LOGIN
+ " - run from an active and local (systemd) session.\n"
+#else
+ " - enable systemd session support for weston-launch.\n"
+#endif
+ " - or add yourself to the 'weston-launch' group.");
+
+ if (setup_tty(&wl, tty) < 0)
+ exit(EXIT_FAILURE);
+
+ if (wl.new_user && setup_pam(&wl) < 0)
+ exit(EXIT_FAILURE);
+
+ if (setup_launcher_socket(&wl) < 0)
+ exit(EXIT_FAILURE);
+
+ if (setup_signals(&wl) < 0)
+ exit(EXIT_FAILURE);
+
+ wl.child = fork();
+ if (wl.child == -1) {
+ error(1, errno, "fork failed");
+ exit(EXIT_FAILURE);
+ }
+
+ if (wl.child == 0)
+ launch_compositor(&wl, argc - optind, argv + optind);
+
+ close(wl.sock[1]);
+ if (wl.tty != STDIN_FILENO)
+ close(wl.tty);
+
+ while (1) {
+ struct pollfd fds[2];
+ int n;
+
+ fds[0].fd = wl.sock[0];
+ fds[0].events = POLLIN;
+ fds[1].fd = wl.signalfd;
+ fds[1].events = POLLIN;
+
+ n = poll(fds, 2, -1);
+ if (n < 0)
+ error(0, errno, "poll failed");
+ if (fds[0].revents & POLLIN)
+ handle_socket_msg(&wl);
+ if (fds[1].revents)
+ handle_signal(&wl);
+ }
+
+ return 0;
+}
diff --git a/src/weston-launch.h b/src/weston-launch.h
new file mode 100644
index 00000000..e20c4c71
--- /dev/null
+++ b/src/weston-launch.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright © 2012 Benjamin Franzke
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that 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.
+ */
+
+#ifndef _WESTON_LAUNCH_H_
+#define _WESTON_LAUNCH_H_
+
+enum weston_launcher_opcode {
+ WESTON_LAUNCHER_OPEN,
+};
+
+enum weston_launcher_event {
+ WESTON_LAUNCHER_SUCCESS,
+ WESTON_LAUNCHER_ACTIVATE,
+ WESTON_LAUNCHER_DEACTIVATE
+};
+
+struct weston_launcher_message {
+ int opcode;
+};
+
+struct weston_launcher_open {
+ struct weston_launcher_message header;
+ int flags;
+ char path[0];
+};
+
+#endif
diff --git a/src/weston.pc.in b/src/weston.pc.in
new file mode 100644
index 00000000..828cb1f0
--- /dev/null
+++ b/src/weston.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+libexecdir=@libexecdir@
+pkglibexecdir=${libexecdir}/@PACKAGE@
+
+Name: Weston Plugin API
+Description: Header files for Weston plugin development
+Version: @WESTON_VERSION@
+Cflags: -I${includedir}
diff --git a/src/xwayland/Makefile.am b/src/xwayland/Makefile.am
new file mode 100644
index 00000000..cab59c70
--- /dev/null
+++ b/src/xwayland/Makefile.am
@@ -0,0 +1,40 @@
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/shared \
+ -I$(top_builddir)/src \
+ -DDATADIR='"$(datadir)"' \
+ -DMODULEDIR='"$(moduledir)"' \
+ -DLIBEXECDIR='"$(libexecdir)"' \
+ -DXSERVER_PATH='"@XSERVER_PATH@"'
+
+moduledir = @libdir@/weston
+module_LTLIBRARIES = xwayland.la
+
+xwayland = xwayland.la
+xwayland_la_LDFLAGS = -module -avoid-version
+xwayland_la_LIBADD = \
+ $(XWAYLAND_LIBS) \
+ $(top_builddir)/shared/libshared-cairo.la
+xwayland_la_CFLAGS = \
+ $(GCC_CFLAGS) \
+ $(COMPOSITOR_CFLAGS) \
+ $(PIXMAN_CFLAGS) \
+ $(CAIRO_CFLAGS)
+xwayland_la_SOURCES = \
+ xwayland.h \
+ window-manager.c \
+ selection.c \
+ dnd.c \
+ launcher.c \
+ xserver-protocol.c \
+ xserver-server-protocol.h \
+ hash.c \
+ hash.h
+
+BUILT_SOURCES = \
+ xserver-protocol.c \
+ xserver-server-protocol.h
+
+CLEANFILES = $(BUILT_SOURCES)
+
+wayland_protocoldir = $(top_srcdir)/protocol
+include $(top_srcdir)/wayland-scanner.mk
diff --git a/src/xwayland/dnd.c b/src/xwayland/dnd.c
new file mode 100644
index 00000000..b146e123
--- /dev/null
+++ b/src/xwayland/dnd.c
@@ -0,0 +1,274 @@
+/*
+ * Copyright © 2013 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 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.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#include <X11/Xcursor/Xcursor.h>
+
+#include "xwayland.h"
+
+#include "../../shared/cairo-util.h"
+#include "../compositor.h"
+#include "xserver-server-protocol.h"
+#include "hash.h"
+
+static void
+weston_dnd_start(struct weston_wm *wm, xcb_window_t owner)
+{
+ uint32_t values[1], version = 4;
+
+ wm->dnd_window = xcb_generate_id(wm->conn);
+ values[0] =
+ XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
+ XCB_EVENT_MASK_PROPERTY_CHANGE;
+
+ xcb_create_window(wm->conn,
+ XCB_COPY_FROM_PARENT,
+ wm->dnd_window,
+ wm->screen->root,
+ 0, 0,
+ 8192, 8192,
+ 0,
+ XCB_WINDOW_CLASS_INPUT_ONLY,
+ wm->screen->root_visual,
+ XCB_CW_EVENT_MASK, values);
+ xcb_change_property(wm->conn,
+ XCB_PROP_MODE_REPLACE,
+ wm->dnd_window,
+ wm->atom.xdnd_aware,
+ XCB_ATOM_ATOM,
+ 32, /* format */
+ 1, &version);
+
+ xcb_map_window(wm->conn, wm->dnd_window);
+ wm->dnd_owner = owner;
+}
+
+static void
+weston_dnd_stop(struct weston_wm *wm)
+{
+ xcb_destroy_window(wm->conn, wm->dnd_window);
+ wm->dnd_window = XCB_WINDOW_NONE;
+}
+
+struct dnd_data_source {
+ struct weston_data_source base;
+ struct weston_wm *wm;
+ int version;
+ uint32_t window;
+};
+
+static void
+data_source_accept(struct weston_data_source *base,
+ uint32_t time, const char *mime_type)
+{
+ struct dnd_data_source *source = (struct dnd_data_source *) base;
+ xcb_client_message_event_t client_message;
+ struct weston_wm *wm = source->wm;
+
+ weston_log("got accept, mime-type %s\n", mime_type);
+
+ /* FIXME: If we rewrote UTF8_STRING to
+ * text/plain;charset=utf-8 and the source doesn't support the
+ * mime-type, we'll have to rewrite the mime-type back to
+ * UTF8_STRING here. */
+
+ client_message.response_type = XCB_CLIENT_MESSAGE;
+ client_message.format = 32;
+ client_message.window = wm->dnd_window;
+ client_message.type = wm->atom.xdnd_status;
+ client_message.data.data32[0] = wm->dnd_window;
+ client_message.data.data32[1] = 2;
+ if (mime_type)
+ client_message.data.data32[1] |= 1;
+ client_message.data.data32[2] = 0;
+ client_message.data.data32[3] = 0;
+ client_message.data.data32[4] = wm->atom.xdnd_action_copy;
+
+ xcb_send_event(wm->conn, 0, wm->dnd_owner,
+ XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
+ (char *) &client_message);
+}
+
+static void
+data_source_send(struct weston_data_source *base,
+ const char *mime_type, int32_t fd)
+{
+ struct dnd_data_source *source = (struct dnd_data_source *) base;
+ struct weston_wm *wm = source->wm;
+
+ weston_log("got send, %s\n", mime_type);
+
+ /* Get data for the utf8_string target */
+ xcb_convert_selection(wm->conn,
+ wm->selection_window,
+ wm->atom.xdnd_selection,
+ wm->atom.utf8_string,
+ wm->atom.wl_selection,
+ XCB_TIME_CURRENT_TIME);
+
+ xcb_flush(wm->conn);
+
+ fcntl(fd, F_SETFL, O_WRONLY | O_NONBLOCK);
+ wm->data_source_fd = fd;
+}
+
+static void
+data_source_cancel(struct weston_data_source *source)
+{
+ weston_log("got cancel\n");
+}
+
+static void
+handle_enter(struct weston_wm *wm, xcb_client_message_event_t *client_message)
+{
+ struct dnd_data_source *source;
+ struct weston_seat *seat = weston_wm_pick_seat(wm);
+ char **p;
+ const char *name;
+ uint32_t *types;
+ int i, length, has_text;
+ xcb_get_property_cookie_t cookie;
+ xcb_get_property_reply_t *reply;
+
+ source = malloc(sizeof *source);
+ if (source == NULL)
+ return;
+
+ wl_signal_init(&source->base.destroy_signal);
+ source->base.accept = data_source_accept;
+ source->base.send = data_source_send;
+ source->base.cancel = data_source_cancel;
+ source->wm = wm;
+ source->window = client_message->data.data32[0];
+ source->version = client_message->data.data32[1] >> 24;
+
+ if (client_message->data.data32[1] & 1) {
+ cookie = xcb_get_property(wm->conn,
+ 0, /* delete */
+ source->window,
+ wm->atom.xdnd_type_list,
+ XCB_ATOM_ANY, 0, 2048);
+ reply = xcb_get_property_reply(wm->conn, cookie, NULL);
+ types = xcb_get_property_value(reply);
+ length = reply->value_len;
+ } else {
+ reply = NULL;
+ types = &client_message->data.data32[2];
+ length = 3;
+ }
+
+ wl_array_init(&source->base.mime_types);
+ has_text = 0;
+ for (i = 0; i < length; i++) {
+ if (types[i] == XCB_ATOM_NONE)
+ continue;
+
+ name = get_atom_name(wm->conn, types[i]);
+ if (types[i] == wm->atom.utf8_string ||
+ types[i] == wm->atom.text_plain_utf8 ||
+ types[i] == wm->atom.text_plain) {
+ if (has_text)
+ continue;
+
+ has_text = 1;
+ p = wl_array_add(&source->base.mime_types, sizeof *p);
+ if (p)
+ *p = strdup("text/plain;charset=utf-8");
+ } else if (strchr(name, '/')) {
+ p = wl_array_add(&source->base.mime_types, sizeof *p);
+ if (p)
+ *p = strdup(name);
+ }
+ }
+
+ free(reply);
+ weston_seat_start_drag(seat, &source->base, NULL, NULL);
+}
+
+int
+weston_wm_handle_dnd_event(struct weston_wm *wm,
+ xcb_generic_event_t *event)
+{
+ xcb_xfixes_selection_notify_event_t *xfixes_selection_notify =
+ (xcb_xfixes_selection_notify_event_t *) event;
+ xcb_client_message_event_t *client_message =
+ (xcb_client_message_event_t *) event;
+
+ switch (event->response_type - wm->xfixes->first_event) {
+ case XCB_XFIXES_SELECTION_NOTIFY:
+ if (xfixes_selection_notify->selection != wm->atom.xdnd_selection)
+ return 0;
+
+ weston_log("XdndSelection owner: %d!\n",
+ xfixes_selection_notify->owner);
+
+ if (xfixes_selection_notify->owner != XCB_WINDOW_NONE)
+ weston_dnd_start(wm, xfixes_selection_notify->owner);
+ else
+ weston_dnd_stop(wm);
+
+ return 1;
+ }
+
+ switch (EVENT_TYPE(event)) {
+ case XCB_CLIENT_MESSAGE:
+ if (client_message->type == wm->atom.xdnd_enter) {
+ handle_enter(wm, client_message);
+ return 1;
+ } else if (client_message->type == wm->atom.xdnd_leave) {
+ weston_log("got leave!\n");
+ return 1;
+ } else if (client_message->type == wm->atom.xdnd_drop) {
+ weston_log("got drop!\n");
+ return 1;
+ } else if (client_message->type == wm->atom.xdnd_drop) {
+ weston_log("got enter!\n");
+ return 1;
+ }
+ return 0;
+ }
+
+ return 0;
+}
+
+void
+weston_wm_dnd_init(struct weston_wm *wm)
+{
+ uint32_t mask;
+
+ mask =
+ XCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER |
+ XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_WINDOW_DESTROY |
+ XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_CLIENT_CLOSE;
+ xcb_xfixes_select_selection_input(wm->conn, wm->selection_window,
+ wm->atom.xdnd_selection, mask);
+}
diff --git a/src/xwayland/hash.c b/src/xwayland/hash.c
new file mode 100644
index 00000000..54f3de93
--- /dev/null
+++ b/src/xwayland/hash.c
@@ -0,0 +1,309 @@
+/*
+ * Copyright © 2009 Intel Corporation
+ * Copyright © 1988-2004 Keith Packard and Bart Massey.
+ *
+ * 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.
+ *
+ * Except as contained in this notice, the names of the authors
+ * or their institutions shall not be used in advertising or
+ * otherwise to promote the sale, use or other dealings in this
+ * Software without prior written authorization from the
+ * authors.
+ *
+ * Authors:
+ * Eric Anholt <eric@anholt.net>
+ * Keith Packard <keithp@keithp.com>
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "hash.h"
+
+struct hash_entry {
+ uint32_t hash;
+ void *data;
+};
+
+struct hash_table {
+ struct hash_entry *table;
+ uint32_t size;
+ uint32_t rehash;
+ uint32_t max_entries;
+ uint32_t size_index;
+ uint32_t entries;
+ uint32_t deleted_entries;
+};
+
+#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
+
+/*
+ * From Knuth -- a good choice for hash/rehash values is p, p-2 where
+ * p and p-2 are both prime. These tables are sized to have an extra 10%
+ * free to avoid exponential performance degradation as the hash table fills
+ */
+
+static const uint32_t deleted_data;
+
+static const struct {
+ uint32_t max_entries, size, rehash;
+} hash_sizes[] = {
+ { 2, 5, 3 },
+ { 4, 7, 5 },
+ { 8, 13, 11 },
+ { 16, 19, 17 },
+ { 32, 43, 41 },
+ { 64, 73, 71 },
+ { 128, 151, 149 },
+ { 256, 283, 281 },
+ { 512, 571, 569 },
+ { 1024, 1153, 1151 },
+ { 2048, 2269, 2267 },
+ { 4096, 4519, 4517 },
+ { 8192, 9013, 9011 },
+ { 16384, 18043, 18041 },
+ { 32768, 36109, 36107 },
+ { 65536, 72091, 72089 },
+ { 131072, 144409, 144407 },
+ { 262144, 288361, 288359 },
+ { 524288, 576883, 576881 },
+ { 1048576, 1153459, 1153457 },
+ { 2097152, 2307163, 2307161 },
+ { 4194304, 4613893, 4613891 },
+ { 8388608, 9227641, 9227639 },
+ { 16777216, 18455029, 18455027 },
+ { 33554432, 36911011, 36911009 },
+ { 67108864, 73819861, 73819859 },
+ { 134217728, 147639589, 147639587 },
+ { 268435456, 295279081, 295279079 },
+ { 536870912, 590559793, 590559791 },
+ { 1073741824, 1181116273, 1181116271},
+ { 2147483648ul, 2362232233ul, 2362232231ul}
+};
+
+static int
+entry_is_free(struct hash_entry *entry)
+{
+ return entry->data == NULL;
+}
+
+static int
+entry_is_deleted(struct hash_entry *entry)
+{
+ return entry->data == &deleted_data;
+}
+
+static int
+entry_is_present(struct hash_entry *entry)
+{
+ return entry->data != NULL && entry->data != &deleted_data;
+}
+
+struct hash_table *
+hash_table_create(void)
+{
+ struct hash_table *ht;
+
+ ht = malloc(sizeof(*ht));
+ if (ht == NULL)
+ return NULL;
+
+ ht->size_index = 0;
+ ht->size = hash_sizes[ht->size_index].size;
+ ht->rehash = hash_sizes[ht->size_index].rehash;
+ ht->max_entries = hash_sizes[ht->size_index].max_entries;
+ ht->table = calloc(ht->size, sizeof(*ht->table));
+ ht->entries = 0;
+ ht->deleted_entries = 0;
+
+ if (ht->table == NULL) {
+ free(ht);
+ return NULL;
+ }
+
+ return ht;
+}
+
+/**
+ * Frees the given hash table.
+ */
+void
+hash_table_destroy(struct hash_table *ht)
+{
+ if (!ht)
+ return;
+
+ free(ht->table);
+ free(ht);
+}
+
+/**
+ * Finds a hash table entry with the given key and hash of that key.
+ *
+ * Returns NULL if no entry is found. Note that the data pointer may be
+ * modified by the user.
+ */
+static void *
+hash_table_search(struct hash_table *ht, uint32_t hash)
+{
+ uint32_t hash_address;
+
+ hash_address = hash % ht->size;
+ do {
+ uint32_t double_hash;
+
+ struct hash_entry *entry = ht->table + hash_address;
+
+ if (entry_is_free(entry)) {
+ return NULL;
+ } else if (entry_is_present(entry) && entry->hash == hash) {
+ return entry;
+ }
+
+ double_hash = 1 + hash % ht->rehash;
+
+ hash_address = (hash_address + double_hash) % ht->size;
+ } while (hash_address != hash % ht->size);
+
+ return NULL;
+}
+
+void
+hash_table_for_each(struct hash_table *ht,
+ hash_table_iterator_func_t func, void *data)
+{
+ struct hash_entry *entry;
+ uint32_t i;
+
+ for (i = 0; i < ht->size; i++) {
+ entry = ht->table + i;
+ if (entry_is_present(entry))
+ func(entry->data, data);
+ }
+}
+
+void *
+hash_table_lookup(struct hash_table *ht, uint32_t hash)
+{
+ struct hash_entry *entry;
+
+ entry = hash_table_search(ht, hash);
+ if (entry != NULL)
+ return entry->data;
+
+ return NULL;
+}
+
+static void
+hash_table_rehash(struct hash_table *ht, unsigned int new_size_index)
+{
+ struct hash_table old_ht;
+ struct hash_entry *table, *entry;
+
+ if (new_size_index >= ARRAY_SIZE(hash_sizes))
+ return;
+
+ table = calloc(hash_sizes[new_size_index].size, sizeof(*ht->table));
+ if (table == NULL)
+ return;
+
+ old_ht = *ht;
+
+ ht->table = table;
+ ht->size_index = new_size_index;
+ ht->size = hash_sizes[ht->size_index].size;
+ ht->rehash = hash_sizes[ht->size_index].rehash;
+ ht->max_entries = hash_sizes[ht->size_index].max_entries;
+ ht->entries = 0;
+ ht->deleted_entries = 0;
+
+ for (entry = old_ht.table;
+ entry != old_ht.table + old_ht.size;
+ entry++) {
+ if (entry_is_present(entry)) {
+ hash_table_insert(ht, entry->hash, entry->data);
+ }
+ }
+
+ free(old_ht.table);
+}
+
+/**
+ * Inserts the data with the given hash into the table.
+ *
+ * Note that insertion may rearrange the table on a resize or rehash,
+ * so previously found hash_entries are no longer valid after this function.
+ */
+int
+hash_table_insert(struct hash_table *ht, uint32_t hash, void *data)
+{
+ uint32_t hash_address;
+
+ if (ht->entries >= ht->max_entries) {
+ hash_table_rehash(ht, ht->size_index + 1);
+ } else if (ht->deleted_entries + ht->entries >= ht->max_entries) {
+ hash_table_rehash(ht, ht->size_index);
+ }
+
+ hash_address = hash % ht->size;
+ do {
+ struct hash_entry *entry = ht->table + hash_address;
+ uint32_t double_hash;
+
+ if (!entry_is_present(entry)) {
+ if (entry_is_deleted(entry))
+ ht->deleted_entries--;
+ entry->hash = hash;
+ entry->data = data;
+ ht->entries++;
+ return 0;
+ }
+
+ double_hash = 1 + hash % ht->rehash;
+
+ hash_address = (hash_address + double_hash) % ht->size;
+ } while (hash_address != hash % ht->size);
+
+ /* We could hit here if a required resize failed. An unchecked-malloc
+ * application could ignore this result.
+ */
+ return -1;
+}
+
+/**
+ * This function deletes the given hash table entry.
+ *
+ * Note that deletion doesn't otherwise modify the table, so an iteration over
+ * the table deleting entries is safe.
+ */
+void
+hash_table_remove(struct hash_table *ht, uint32_t hash)
+{
+ struct hash_entry *entry;
+
+ entry = hash_table_search(ht, hash);
+ if (entry != NULL) {
+ entry->data = (void *) &deleted_data;
+ ht->entries--;
+ ht->deleted_entries++;
+ }
+}
diff --git a/src/xwayland/hash.h b/src/xwayland/hash.h
new file mode 100644
index 00000000..6e1674e1
--- /dev/null
+++ b/src/xwayland/hash.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright © 2009 Intel Corporation
+ * Copyright © 1988-2004 Keith Packard and Bart Massey.
+ *
+ * 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.
+ *
+ * Except as contained in this notice, the names of the authors
+ * or their institutions shall not be used in advertising or
+ * otherwise to promote the sale, use or other dealings in this
+ * Software without prior written authorization from the
+ * authors.
+ *
+ * Authors:
+ * Eric Anholt <eric@anholt.net>
+ * Keith Packard <keithp@keithp.com>
+ */
+
+#ifndef HASH_H
+#define HASH_H
+
+struct hash_table;
+struct hash_table *hash_table_create(void);
+typedef void (*hash_table_iterator_func_t)(void *element, void *data);
+
+void hash_table_destroy(struct hash_table *ht);
+void *hash_table_lookup(struct hash_table *ht, uint32_t hash);
+int hash_table_insert(struct hash_table *ht, uint32_t hash, void *data);
+void hash_table_remove(struct hash_table *ht, uint32_t hash);
+void hash_table_for_each(struct hash_table *ht,
+ hash_table_iterator_func_t func, void *data);
+
+#endif
diff --git a/src/xwayland/launcher.c b/src/xwayland/launcher.c
new file mode 100644
index 00000000..8d8e060a
--- /dev/null
+++ b/src/xwayland/launcher.c
@@ -0,0 +1,389 @@
+/*
+ * Copyright © 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 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.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+
+#include "xwayland.h"
+#include "xserver-server-protocol.h"
+
+
+static int
+weston_xserver_handle_event(int listen_fd, uint32_t mask, void *data)
+{
+ struct weston_xserver *wxs = data;
+ char display[8], s[8];
+ int sv[2], client_fd;
+ char *xserver = NULL;
+ struct weston_config_section *section;
+
+ if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) {
+ weston_log("socketpair failed\n");
+ return 1;
+ }
+
+ wxs->process.pid = fork();
+ switch (wxs->process.pid) {
+ case 0:
+ /* SOCK_CLOEXEC closes both ends, so we need to unset
+ * the flag on the client fd. */
+ client_fd = dup(sv[1]);
+ if (client_fd < 0)
+ return 1;
+
+ snprintf(s, sizeof s, "%d", client_fd);
+ setenv("WAYLAND_SOCKET", s, 1);
+
+ snprintf(display, sizeof display, ":%d", wxs->display);
+
+ section = weston_config_get_section(wxs->compositor->config, "xwayland", NULL, NULL);
+ weston_config_section_get_string(section, "path", &xserver, XSERVER_PATH);
+
+ if (execl(xserver,
+ xserver,
+ display,
+ "-wayland",
+ "-rootless",
+ "-retro",
+ "-nolisten", "all",
+ "-terminate",
+ NULL) < 0)
+ weston_log("exec failed: %m\n");
+ free(xserver);
+ _exit(EXIT_FAILURE);
+
+ default:
+ weston_log("forked X server, pid %d\n", wxs->process.pid);
+
+ close(sv[1]);
+ wxs->client = wl_client_create(wxs->wl_display, sv[0]);
+
+ weston_watch_process(&wxs->process);
+
+ wl_event_source_remove(wxs->abstract_source);
+ wl_event_source_remove(wxs->unix_source);
+ break;
+
+ case -1:
+ weston_log( "failed to fork\n");
+ break;
+ }
+
+ return 1;
+}
+
+static void
+weston_xserver_shutdown(struct weston_xserver *wxs)
+{
+ char path[256];
+
+ snprintf(path, sizeof path, "/tmp/.X%d-lock", wxs->display);
+ unlink(path);
+ snprintf(path, sizeof path, "/tmp/.X11-unix/X%d", wxs->display);
+ unlink(path);
+ if (wxs->process.pid == 0) {
+ wl_event_source_remove(wxs->abstract_source);
+ wl_event_source_remove(wxs->unix_source);
+ }
+ close(wxs->abstract_fd);
+ close(wxs->unix_fd);
+ if (wxs->wm)
+ weston_wm_destroy(wxs->wm);
+ wxs->loop = NULL;
+}
+
+static void
+weston_xserver_cleanup(struct weston_process *process, int status)
+{
+ struct weston_xserver *wxs =
+ container_of(process, struct weston_xserver, process);
+
+ wxs->process.pid = 0;
+ wxs->client = NULL;
+ wxs->resource = NULL;
+
+ wxs->abstract_source =
+ wl_event_loop_add_fd(wxs->loop, wxs->abstract_fd,
+ WL_EVENT_READABLE,
+ weston_xserver_handle_event, wxs);
+
+ wxs->unix_source =
+ wl_event_loop_add_fd(wxs->loop, wxs->unix_fd,
+ WL_EVENT_READABLE,
+ weston_xserver_handle_event, wxs);
+
+ if (wxs->wm) {
+ weston_log("xserver exited, code %d\n", status);
+ weston_wm_destroy(wxs->wm);
+ wxs->wm = NULL;
+ } else {
+ /* If the X server crashes before it binds to the
+ * xserver interface, shut down and don't try
+ * again. */
+ weston_log("xserver crashing too fast: %d\n", status);
+ weston_xserver_shutdown(wxs);
+ }
+}
+
+static void
+bind_xserver(struct wl_client *client,
+ void *data, uint32_t version, uint32_t id)
+{
+ struct weston_xserver *wxs = data;
+
+ /* If it's a different client than the xserver we launched,
+ * don't start the wm. */
+ if (client != wxs->client)
+ return;
+
+ wxs->resource =
+ wl_resource_create(client, &xserver_interface,
+ 1, id);
+ wl_resource_set_implementation(wxs->resource, &xserver_implementation,
+ wxs, NULL);
+
+ wxs->wm = weston_wm_create(wxs);
+ if (wxs->wm == NULL) {
+ weston_log("failed to create wm\n");
+ }
+
+ xserver_send_listen_socket(wxs->resource, wxs->abstract_fd);
+ xserver_send_listen_socket(wxs->resource, wxs->unix_fd);
+}
+
+static int
+bind_to_abstract_socket(int display)
+{
+ struct sockaddr_un addr;
+ socklen_t size, name_size;
+ int fd;
+
+ fd = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
+ if (fd < 0)
+ return -1;
+
+ addr.sun_family = AF_LOCAL;
+ name_size = snprintf(addr.sun_path, sizeof addr.sun_path,
+ "%c/tmp/.X11-unix/X%d", 0, display);
+ size = offsetof(struct sockaddr_un, sun_path) + name_size;
+ if (bind(fd, (struct sockaddr *) &addr, size) < 0) {
+ weston_log("failed to bind to @%s: %s\n",
+ addr.sun_path + 1, strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ if (listen(fd, 1) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+static int
+bind_to_unix_socket(int display)
+{
+ struct sockaddr_un addr;
+ socklen_t size, name_size;
+ int fd;
+
+ fd = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
+ if (fd < 0)
+ return -1;
+
+ addr.sun_family = AF_LOCAL;
+ name_size = snprintf(addr.sun_path, sizeof addr.sun_path,
+ "/tmp/.X11-unix/X%d", display) + 1;
+ size = offsetof(struct sockaddr_un, sun_path) + name_size;
+ unlink(addr.sun_path);
+ if (bind(fd, (struct sockaddr *) &addr, size) < 0) {
+ weston_log("failed to bind to %s (%s)\n",
+ addr.sun_path, strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ if (listen(fd, 1) < 0) {
+ unlink(addr.sun_path);
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+static int
+create_lockfile(int display, char *lockfile, size_t lsize)
+{
+ char pid[16], *end;
+ int fd, size;
+ pid_t other;
+
+ snprintf(lockfile, lsize, "/tmp/.X%d-lock", display);
+ fd = open(lockfile, O_WRONLY | O_CLOEXEC | O_CREAT | O_EXCL, 0444);
+ if (fd < 0 && errno == EEXIST) {
+ fd = open(lockfile, O_CLOEXEC, O_RDONLY);
+ if (fd < 0 || read(fd, pid, 11) != 11) {
+ weston_log("can't read lock file %s: %s\n",
+ lockfile, strerror(errno));
+ if (fd >= 0)
+ close (fd);
+
+ errno = EEXIST;
+ return -1;
+ }
+
+ other = strtol(pid, &end, 0);
+ if (end != pid + 10) {
+ weston_log("can't parse lock file %s\n",
+ lockfile);
+ close(fd);
+ errno = EEXIST;
+ return -1;
+ }
+
+ if (kill(other, 0) < 0 && errno == ESRCH) {
+ /* stale lock file; unlink and try again */
+ weston_log("unlinking stale lock file %s\n", lockfile);
+ close(fd);
+ if (unlink(lockfile))
+ /* If we fail to unlink, return EEXIST
+ so we try the next display number.*/
+ errno = EEXIST;
+ else
+ errno = EAGAIN;
+ return -1;
+ }
+
+ close(fd);
+ errno = EEXIST;
+ return -1;
+ } else if (fd < 0) {
+ weston_log("failed to create lock file %s: %s\n",
+ lockfile, strerror(errno));
+ return -1;
+ }
+
+ /* Subtle detail: we use the pid of the wayland
+ * compositor, not the xserver in the lock file. */
+ size = snprintf(pid, sizeof pid, "%10d\n", getpid());
+ if (write(fd, pid, size) != size) {
+ unlink(lockfile);
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+
+ return 0;
+}
+
+static void
+weston_xserver_destroy(struct wl_listener *l, void *data)
+{
+ struct weston_xserver *wxs =
+ container_of(l, struct weston_xserver, destroy_listener);
+
+ if (!wxs)
+ return;
+
+ if (wxs->loop)
+ weston_xserver_shutdown(wxs);
+
+ free(wxs);
+}
+
+WL_EXPORT int
+module_init(struct weston_compositor *compositor,
+ int *argc, char *argv[])
+
+{
+ struct wl_display *display = compositor->wl_display;
+ struct weston_xserver *wxs;
+ char lockfile[256], display_name[8];
+
+ wxs = zalloc(sizeof *wxs);
+ wxs->process.cleanup = weston_xserver_cleanup;
+ wxs->wl_display = display;
+ wxs->compositor = compositor;
+
+ wxs->display = 0;
+
+ retry:
+ if (create_lockfile(wxs->display, lockfile, sizeof lockfile) < 0) {
+ if (errno == EAGAIN) {
+ goto retry;
+ } else if (errno == EEXIST) {
+ wxs->display++;
+ goto retry;
+ } else {
+ free(wxs);
+ return -1;
+ }
+ }
+
+ wxs->abstract_fd = bind_to_abstract_socket(wxs->display);
+ if (wxs->abstract_fd < 0 && errno == EADDRINUSE) {
+ wxs->display++;
+ unlink(lockfile);
+ goto retry;
+ }
+
+ wxs->unix_fd = bind_to_unix_socket(wxs->display);
+ if (wxs->unix_fd < 0) {
+ unlink(lockfile);
+ close(wxs->abstract_fd);
+ free(wxs);
+ return -1;
+ }
+
+ snprintf(display_name, sizeof display_name, ":%d", wxs->display);
+ weston_log("xserver listening on display %s\n", display_name);
+ setenv("DISPLAY", display_name, 1);
+
+ wxs->loop = wl_display_get_event_loop(display);
+ wxs->abstract_source =
+ wl_event_loop_add_fd(wxs->loop, wxs->abstract_fd,
+ WL_EVENT_READABLE,
+ weston_xserver_handle_event, wxs);
+ wxs->unix_source =
+ wl_event_loop_add_fd(wxs->loop, wxs->unix_fd,
+ WL_EVENT_READABLE,
+ weston_xserver_handle_event, wxs);
+
+ wl_global_create(display, &xserver_interface, 1, wxs, bind_xserver);
+
+ wxs->destroy_listener.notify = weston_xserver_destroy;
+ wl_signal_add(&compositor->destroy_signal, &wxs->destroy_listener);
+
+ return 0;
+}
diff --git a/src/xwayland/selection.c b/src/xwayland/selection.c
new file mode 100644
index 00000000..b694477e
--- /dev/null
+++ b/src/xwayland/selection.c
@@ -0,0 +1,712 @@
+/*
+ * Copyright © 2012 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 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.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "xwayland.h"
+
+static int
+writable_callback(int fd, uint32_t mask, void *data)
+{
+ struct weston_wm *wm = data;
+ unsigned char *property;
+ int len, remainder;
+
+ property = xcb_get_property_value(wm->property_reply);
+ remainder = xcb_get_property_value_length(wm->property_reply) -
+ wm->property_start;
+
+ len = write(fd, property + wm->property_start, remainder);
+ if (len == -1) {
+ free(wm->property_reply);
+ wm->property_reply = NULL;
+ if (wm->property_source)
+ wl_event_source_remove(wm->property_source);
+ close(fd);
+ weston_log("write error to target fd: %m\n");
+ return 1;
+ }
+
+ weston_log("wrote %d (chunk size %d) of %d bytes\n",
+ wm->property_start + len,
+ len, xcb_get_property_value_length(wm->property_reply));
+
+ wm->property_start += len;
+ if (len == remainder) {
+ free(wm->property_reply);
+ wm->property_reply = NULL;
+ if (wm->property_source)
+ wl_event_source_remove(wm->property_source);
+
+ if (wm->incr) {
+ xcb_delete_property(wm->conn,
+ wm->selection_window,
+ wm->atom.wl_selection);
+ } else {
+ weston_log("transfer complete\n");
+ close(fd);
+ }
+ }
+
+ return 1;
+}
+
+static void
+weston_wm_write_property(struct weston_wm *wm, xcb_get_property_reply_t *reply)
+{
+ wm->property_start = 0;
+ wm->property_reply = reply;
+ writable_callback(wm->data_source_fd, WL_EVENT_WRITABLE, wm);
+
+ if (wm->property_reply)
+ wm->property_source =
+ wl_event_loop_add_fd(wm->server->loop,
+ wm->data_source_fd,
+ WL_EVENT_WRITABLE,
+ writable_callback, wm);
+}
+
+static void
+weston_wm_get_incr_chunk(struct weston_wm *wm)
+{
+ xcb_get_property_cookie_t cookie;
+ xcb_get_property_reply_t *reply;
+
+ cookie = xcb_get_property(wm->conn,
+ 0, /* delete */
+ wm->selection_window,
+ wm->atom.wl_selection,
+ XCB_GET_PROPERTY_TYPE_ANY,
+ 0, /* offset */
+ 0x1fffffff /* length */);
+
+ reply = xcb_get_property_reply(wm->conn, cookie, NULL);
+
+ dump_property(wm, wm->atom.wl_selection, reply);
+
+ if (xcb_get_property_value_length(reply) > 0) {
+ weston_wm_write_property(wm, reply);
+ } else {
+ weston_log("transfer complete\n");
+ close(wm->data_source_fd);
+ free(reply);
+ }
+}
+
+struct x11_data_source {
+ struct weston_data_source base;
+ struct weston_wm *wm;
+};
+
+static void
+data_source_accept(struct weston_data_source *source,
+ uint32_t time, const char *mime_type)
+{
+}
+
+static void
+data_source_send(struct weston_data_source *base,
+ const char *mime_type, int32_t fd)
+{
+ struct x11_data_source *source = (struct x11_data_source *) base;
+ struct weston_wm *wm = source->wm;
+
+ if (strcmp(mime_type, "text/plain;charset=utf-8") == 0) {
+ /* Get data for the utf8_string target */
+ xcb_convert_selection(wm->conn,
+ wm->selection_window,
+ wm->atom.clipboard,
+ wm->atom.utf8_string,
+ wm->atom.wl_selection,
+ XCB_TIME_CURRENT_TIME);
+
+ xcb_flush(wm->conn);
+
+ fcntl(fd, F_SETFL, O_WRONLY | O_NONBLOCK);
+ wm->data_source_fd = fd;
+ }
+}
+
+static void
+data_source_cancel(struct weston_data_source *source)
+{
+}
+
+static void
+weston_wm_get_selection_targets(struct weston_wm *wm)
+{
+ struct x11_data_source *source;
+ struct weston_compositor *compositor;
+ struct weston_seat *seat = weston_wm_pick_seat(wm);
+ xcb_get_property_cookie_t cookie;
+ xcb_get_property_reply_t *reply;
+ xcb_atom_t *value;
+ char **p;
+ uint32_t i;
+
+ cookie = xcb_get_property(wm->conn,
+ 1, /* delete */
+ wm->selection_window,
+ wm->atom.wl_selection,
+ XCB_GET_PROPERTY_TYPE_ANY,
+ 0, /* offset */
+ 4096 /* length */);
+
+ reply = xcb_get_property_reply(wm->conn, cookie, NULL);
+
+ dump_property(wm, wm->atom.wl_selection, reply);
+
+ if (reply->type != XCB_ATOM_ATOM) {
+ free(reply);
+ return;
+ }
+
+ source = malloc(sizeof *source);
+ if (source == NULL)
+ return;
+
+ wl_signal_init(&source->base.destroy_signal);
+ source->base.accept = data_source_accept;
+ source->base.send = data_source_send;
+ source->base.cancel = data_source_cancel;
+ source->wm = wm;
+
+ wl_array_init(&source->base.mime_types);
+ value = xcb_get_property_value(reply);
+ for (i = 0; i < reply->value_len; i++) {
+ if (value[i] == wm->atom.utf8_string) {
+ p = wl_array_add(&source->base.mime_types, sizeof *p);
+ if (p)
+ *p = strdup("text/plain;charset=utf-8");
+ }
+ }
+
+ compositor = wm->server->compositor;
+ weston_seat_set_selection(seat, &source->base,
+ wl_display_next_serial(compositor->wl_display));
+
+ free(reply);
+}
+
+static void
+weston_wm_get_selection_data(struct weston_wm *wm)
+{
+ xcb_get_property_cookie_t cookie;
+ xcb_get_property_reply_t *reply;
+
+ cookie = xcb_get_property(wm->conn,
+ 1, /* delete */
+ wm->selection_window,
+ wm->atom.wl_selection,
+ XCB_GET_PROPERTY_TYPE_ANY,
+ 0, /* offset */
+ 0x1fffffff /* length */);
+
+ reply = xcb_get_property_reply(wm->conn, cookie, NULL);
+
+ if (reply->type == wm->atom.incr) {
+ dump_property(wm, wm->atom.wl_selection, reply);
+ wm->incr = 1;
+ free(reply);
+ } else {
+ dump_property(wm, wm->atom.wl_selection, reply);
+ wm->incr = 0;
+ weston_wm_write_property(wm, reply);
+ }
+}
+
+static void
+weston_wm_handle_selection_notify(struct weston_wm *wm,
+ xcb_generic_event_t *event)
+{
+ xcb_selection_notify_event_t *selection_notify =
+ (xcb_selection_notify_event_t *) event;
+
+ if (selection_notify->property == XCB_ATOM_NONE) {
+ /* convert selection failed */
+ } else if (selection_notify->target == wm->atom.targets) {
+ weston_wm_get_selection_targets(wm);
+ } else {
+ weston_wm_get_selection_data(wm);
+ }
+}
+
+static const size_t incr_chunk_size = 64 * 1024;
+
+static void
+weston_wm_send_selection_notify(struct weston_wm *wm, xcb_atom_t property)
+{
+ xcb_selection_notify_event_t selection_notify;
+
+ memset(&selection_notify, 0, sizeof selection_notify);
+ selection_notify.response_type = XCB_SELECTION_NOTIFY;
+ selection_notify.sequence = 0;
+ selection_notify.time = wm->selection_request.time;
+ selection_notify.requestor = wm->selection_request.requestor;
+ selection_notify.selection = wm->selection_request.selection;
+ selection_notify.target = wm->selection_request.target;
+ selection_notify.property = property;
+
+ xcb_send_event(wm->conn, 0, /* propagate */
+ wm->selection_request.requestor,
+ XCB_EVENT_MASK_NO_EVENT, (char *) &selection_notify);
+}
+
+static void
+weston_wm_send_targets(struct weston_wm *wm)
+{
+ xcb_atom_t targets[] = {
+ wm->atom.timestamp,
+ wm->atom.targets,
+ wm->atom.utf8_string,
+ /* wm->atom.compound_text, */
+ wm->atom.text,
+ /* wm->atom.string */
+ };
+
+ xcb_change_property(wm->conn,
+ XCB_PROP_MODE_REPLACE,
+ wm->selection_request.requestor,
+ wm->selection_request.property,
+ XCB_ATOM_ATOM,
+ 32, /* format */
+ ARRAY_LENGTH(targets), targets);
+
+ weston_wm_send_selection_notify(wm, wm->selection_request.property);
+}
+
+static void
+weston_wm_send_timestamp(struct weston_wm *wm)
+{
+ xcb_change_property(wm->conn,
+ XCB_PROP_MODE_REPLACE,
+ wm->selection_request.requestor,
+ wm->selection_request.property,
+ XCB_ATOM_INTEGER,
+ 32, /* format */
+ 1, &wm->selection_timestamp);
+
+ weston_wm_send_selection_notify(wm, wm->selection_request.property);
+}
+
+static int
+weston_wm_flush_source_data(struct weston_wm *wm)
+{
+ int length;
+
+ xcb_change_property(wm->conn,
+ XCB_PROP_MODE_REPLACE,
+ wm->selection_request.requestor,
+ wm->selection_request.property,
+ wm->selection_target,
+ 8, /* format */
+ wm->source_data.size,
+ wm->source_data.data);
+ wm->selection_property_set = 1;
+ length = wm->source_data.size;
+ wm->source_data.size = 0;
+
+ return length;
+}
+
+static int
+weston_wm_read_data_source(int fd, uint32_t mask, void *data)
+{
+ struct weston_wm *wm = data;
+ int len, current, available;
+ void *p;
+
+ current = wm->source_data.size;
+ if (wm->source_data.size < incr_chunk_size)
+ p = wl_array_add(&wm->source_data, incr_chunk_size);
+ else
+ p = (char *) wm->source_data.data + wm->source_data.size;
+ available = wm->source_data.alloc - current;
+
+ len = read(fd, p, available);
+ if (len == -1) {
+ weston_log("read error from data source: %m\n");
+ weston_wm_send_selection_notify(wm, XCB_ATOM_NONE);
+ wl_event_source_remove(wm->property_source);
+ close(fd);
+ wl_array_release(&wm->source_data);
+ }
+
+ weston_log("read %d (available %d, mask 0x%x) bytes: \"%.*s\"\n",
+ len, available, mask, len, (char *) p);
+
+ wm->source_data.size = current + len;
+ if (wm->source_data.size >= incr_chunk_size) {
+ if (!wm->incr) {
+ weston_log("got %zu bytes, starting incr\n",
+ wm->source_data.size);
+ wm->incr = 1;
+ xcb_change_property(wm->conn,
+ XCB_PROP_MODE_REPLACE,
+ wm->selection_request.requestor,
+ wm->selection_request.property,
+ wm->atom.incr,
+ 32, /* format */
+ 1, &incr_chunk_size);
+ wm->selection_property_set = 1;
+ wm->flush_property_on_delete = 1;
+ wl_event_source_remove(wm->property_source);
+ weston_wm_send_selection_notify(wm, wm->selection_request.property);
+ } else if (wm->selection_property_set) {
+ weston_log("got %zu bytes, waiting for "
+ "property delete\n", wm->source_data.size);
+
+ wm->flush_property_on_delete = 1;
+ wl_event_source_remove(wm->property_source);
+ } else {
+ weston_log("got %zu bytes, "
+ "property deleted, seting new property\n",
+ wm->source_data.size);
+ weston_wm_flush_source_data(wm);
+ }
+ } else if (len == 0 && !wm->incr) {
+ weston_log("non-incr transfer complete\n");
+ /* Non-incr transfer all done. */
+ weston_wm_flush_source_data(wm);
+ weston_wm_send_selection_notify(wm, wm->selection_request.property);
+ xcb_flush(wm->conn);
+ wl_event_source_remove(wm->property_source);
+ close(fd);
+ wl_array_release(&wm->source_data);
+ wm->selection_request.requestor = XCB_NONE;
+ } else if (len == 0 && wm->incr) {
+ weston_log("incr transfer complete\n");
+
+ wm->flush_property_on_delete = 1;
+ if (wm->selection_property_set) {
+ weston_log("got %zu bytes, waiting for "
+ "property delete\n", wm->source_data.size);
+ } else {
+ weston_log("got %zu bytes, "
+ "property deleted, seting new property\n",
+ wm->source_data.size);
+ weston_wm_flush_source_data(wm);
+ }
+ xcb_flush(wm->conn);
+ wl_event_source_remove(wm->property_source);
+ close(wm->data_source_fd);
+ wm->data_source_fd = -1;
+ close(fd);
+ } else {
+ weston_log("nothing happened, buffered the bytes\n");
+ }
+
+ return 1;
+}
+
+static void
+weston_wm_send_data(struct weston_wm *wm, xcb_atom_t target, const char *mime_type)
+{
+ struct weston_data_source *source;
+ struct weston_seat *seat = weston_wm_pick_seat(wm);
+ int p[2];
+
+ if (pipe2(p, O_CLOEXEC | O_NONBLOCK) == -1) {
+ weston_log("pipe2 failed: %m\n");
+ weston_wm_send_selection_notify(wm, XCB_ATOM_NONE);
+ return;
+ }
+
+ wl_array_init(&wm->source_data);
+ wm->selection_target = target;
+ wm->data_source_fd = p[0];
+ wm->property_source = wl_event_loop_add_fd(wm->server->loop,
+ wm->data_source_fd,
+ WL_EVENT_READABLE,
+ weston_wm_read_data_source,
+ wm);
+
+ source = seat->selection_data_source;
+ source->send(source, mime_type, p[1]);
+ close(p[1]);
+}
+
+static void
+weston_wm_send_incr_chunk(struct weston_wm *wm)
+{
+ int length;
+
+ weston_log("property deleted\n");
+
+ wm->selection_property_set = 0;
+ if (wm->flush_property_on_delete) {
+ weston_log("setting new property, %zu bytes\n",
+ wm->source_data.size);
+ wm->flush_property_on_delete = 0;
+ length = weston_wm_flush_source_data(wm);
+
+ if (wm->data_source_fd >= 0) {
+ wm->property_source =
+ wl_event_loop_add_fd(wm->server->loop,
+ wm->data_source_fd,
+ WL_EVENT_READABLE,
+ weston_wm_read_data_source,
+ wm);
+ } else if (length > 0) {
+ /* Transfer is all done, but queue a flush for
+ * the delete of the last chunk so we can set
+ * the 0 sized propert to signal the end of
+ * the transfer. */
+ wm->flush_property_on_delete = 1;
+ wl_array_release(&wm->source_data);
+ } else {
+ wm->selection_request.requestor = XCB_NONE;
+ }
+ }
+}
+
+static int
+weston_wm_handle_selection_property_notify(struct weston_wm *wm,
+ xcb_generic_event_t *event)
+{
+ xcb_property_notify_event_t *property_notify =
+ (xcb_property_notify_event_t *) event;
+
+ if (property_notify->window == wm->selection_window) {
+ if (property_notify->state == XCB_PROPERTY_NEW_VALUE &&
+ property_notify->atom == wm->atom.wl_selection &&
+ wm->incr)
+ weston_wm_get_incr_chunk(wm);
+ return 1;
+ } else if (property_notify->window == wm->selection_request.requestor) {
+ if (property_notify->state == XCB_PROPERTY_DELETE &&
+ property_notify->atom == wm->selection_request.property &&
+ wm->incr)
+ weston_wm_send_incr_chunk(wm);
+ return 1;
+ }
+
+ return 0;
+}
+
+static void
+weston_wm_handle_selection_request(struct weston_wm *wm,
+ xcb_generic_event_t *event)
+{
+ xcb_selection_request_event_t *selection_request =
+ (xcb_selection_request_event_t *) event;
+
+ weston_log("selection request, %s, ",
+ get_atom_name(wm->conn, selection_request->selection));
+ weston_log_continue("target %s, ",
+ get_atom_name(wm->conn, selection_request->target));
+ weston_log_continue("property %s\n",
+ get_atom_name(wm->conn, selection_request->property));
+
+ wm->selection_request = *selection_request;
+ wm->incr = 0;
+ wm->flush_property_on_delete = 0;
+
+ if (selection_request->selection == wm->atom.clipboard_manager) {
+ /* The weston clipboard should already have grabbed
+ * the first target, so just send selection notify
+ * now. This isn't synchronized with the clipboard
+ * finishing getting the data, so there's a race here. */
+ weston_wm_send_selection_notify(wm, wm->selection_request.property);
+ return;
+ }
+
+ if (selection_request->target == wm->atom.targets) {
+ weston_wm_send_targets(wm);
+ } else if (selection_request->target == wm->atom.timestamp) {
+ weston_wm_send_timestamp(wm);
+ } else if (selection_request->target == wm->atom.utf8_string ||
+ selection_request->target == wm->atom.text) {
+ weston_wm_send_data(wm, wm->atom.utf8_string,
+ "text/plain;charset=utf-8");
+ } else {
+ weston_log("can only handle UTF8_STRING targets...\n");
+ weston_wm_send_selection_notify(wm, XCB_ATOM_NONE);
+ }
+}
+
+static int
+weston_wm_handle_xfixes_selection_notify(struct weston_wm *wm,
+ xcb_generic_event_t *event)
+{
+ xcb_xfixes_selection_notify_event_t *xfixes_selection_notify =
+ (xcb_xfixes_selection_notify_event_t *) event;
+ struct weston_compositor *compositor;
+ struct weston_seat *seat = weston_wm_pick_seat(wm);
+ uint32_t serial;
+
+ if (xfixes_selection_notify->selection != wm->atom.clipboard)
+ return 0;
+
+ weston_log("xfixes selection notify event: owner %d\n",
+ xfixes_selection_notify->owner);
+
+ if (xfixes_selection_notify->owner == XCB_WINDOW_NONE) {
+ if (wm->selection_owner != wm->selection_window) {
+ /* A real X client selection went away, not our
+ * proxy selection. Clear the wayland selection. */
+ compositor = wm->server->compositor;
+ serial = wl_display_next_serial(compositor->wl_display);
+ weston_seat_set_selection(seat, NULL, serial);
+ }
+
+ wm->selection_owner = XCB_WINDOW_NONE;
+
+ return 1;
+ }
+
+ wm->selection_owner = xfixes_selection_notify->owner;
+
+ /* We have to use XCB_TIME_CURRENT_TIME when we claim the
+ * selection, so grab the actual timestamp here so we can
+ * answer TIMESTAMP conversion requests correctly. */
+ if (xfixes_selection_notify->owner == wm->selection_window) {
+ wm->selection_timestamp = xfixes_selection_notify->timestamp;
+ weston_log("our window, skipping\n");
+ return 1;
+ }
+
+ wm->incr = 0;
+ xcb_convert_selection(wm->conn, wm->selection_window,
+ wm->atom.clipboard,
+ wm->atom.targets,
+ wm->atom.wl_selection,
+ xfixes_selection_notify->timestamp);
+
+ xcb_flush(wm->conn);
+
+ return 1;
+}
+
+int
+weston_wm_handle_selection_event(struct weston_wm *wm,
+ xcb_generic_event_t *event)
+{
+ switch (event->response_type & ~0x80) {
+ case XCB_SELECTION_NOTIFY:
+ weston_wm_handle_selection_notify(wm, event);
+ return 1;
+ case XCB_PROPERTY_NOTIFY:
+ return weston_wm_handle_selection_property_notify(wm, event);
+ case XCB_SELECTION_REQUEST:
+ weston_wm_handle_selection_request(wm, event);
+ return 1;
+ }
+
+ switch (event->response_type - wm->xfixes->first_event) {
+ case XCB_XFIXES_SELECTION_NOTIFY:
+ return weston_wm_handle_xfixes_selection_notify(wm, event);
+ }
+
+ return 0;
+}
+
+static void
+weston_wm_set_selection(struct wl_listener *listener, void *data)
+{
+ struct weston_seat *seat = data;
+ struct weston_wm *wm =
+ container_of(listener, struct weston_wm, selection_listener);
+ struct weston_data_source *source = seat->selection_data_source;
+ const char **p, **end;
+ int has_text_plain = 0;
+
+ if (source == NULL) {
+ if (wm->selection_owner == wm->selection_window)
+ xcb_set_selection_owner(wm->conn,
+ XCB_ATOM_NONE,
+ wm->atom.clipboard,
+ wm->selection_timestamp);
+ return;
+ }
+
+ if (source->send == data_source_send)
+ return;
+
+ p = source->mime_types.data;
+ end = (const char **)
+ ((char *) source->mime_types.data + source->mime_types.size);
+ while (p < end) {
+ weston_log(" %s\n", *p);
+ if (strcmp(*p, "text/plain") == 0 ||
+ strcmp(*p, "text/plain;charset=utf-8") == 0)
+ has_text_plain = 1;
+ p++;
+ }
+
+ if (has_text_plain) {
+ xcb_set_selection_owner(wm->conn,
+ wm->selection_window,
+ wm->atom.clipboard,
+ XCB_TIME_CURRENT_TIME);
+ } else {
+ xcb_set_selection_owner(wm->conn,
+ XCB_ATOM_NONE,
+ wm->atom.clipboard,
+ XCB_TIME_CURRENT_TIME);
+ }
+}
+
+void
+weston_wm_selection_init(struct weston_wm *wm)
+{
+ struct weston_seat *seat;
+ uint32_t values[1], mask;
+
+ wm->selection_request.requestor = XCB_NONE;
+
+ values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE;
+ wm->selection_window = xcb_generate_id(wm->conn);
+ xcb_create_window(wm->conn,
+ XCB_COPY_FROM_PARENT,
+ wm->selection_window,
+ wm->screen->root,
+ 0, 0,
+ 10, 10,
+ 0,
+ XCB_WINDOW_CLASS_INPUT_OUTPUT,
+ wm->screen->root_visual,
+ XCB_CW_EVENT_MASK, values);
+
+ xcb_set_selection_owner(wm->conn,
+ wm->selection_window,
+ wm->atom.clipboard_manager,
+ XCB_TIME_CURRENT_TIME);
+
+ mask =
+ XCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER |
+ XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_WINDOW_DESTROY |
+ XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_CLIENT_CLOSE;
+ xcb_xfixes_select_selection_input(wm->conn, wm->selection_window,
+ wm->atom.clipboard, mask);
+
+ seat = weston_wm_pick_seat(wm);
+ wm->selection_listener.notify = weston_wm_set_selection;
+ wl_signal_add(&seat->selection_signal, &wm->selection_listener);
+
+ weston_wm_set_selection(&wm->selection_listener, seat);
+}
diff --git a/src/xwayland/window-manager.c b/src/xwayland/window-manager.c
new file mode 100644
index 00000000..f1b58c9b
--- /dev/null
+++ b/src/xwayland/window-manager.c
@@ -0,0 +1,2186 @@
+/*
+ * Copyright © 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 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.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#include <X11/Xcursor/Xcursor.h>
+
+#include "xwayland.h"
+
+#include "../../shared/cairo-util.h"
+#include "../compositor.h"
+#include "xserver-server-protocol.h"
+#include "hash.h"
+
+struct wm_size_hints {
+ uint32_t flags;
+ int32_t x, y;
+ int32_t width, height; /* should set so old wm's don't mess up */
+ int32_t min_width, min_height;
+ int32_t max_width, max_height;
+ int32_t width_inc, height_inc;
+ struct {
+ int32_t x;
+ int32_t y;
+ } min_aspect, max_aspect;
+ int32_t base_width, base_height;
+ int32_t win_gravity;
+};
+
+#define USPosition (1L << 0)
+#define USSize (1L << 1)
+#define PPosition (1L << 2)
+#define PSize (1L << 3)
+#define PMinSize (1L << 4)
+#define PMaxSize (1L << 5)
+#define PResizeInc (1L << 6)
+#define PAspect (1L << 7)
+#define PBaseSize (1L << 8)
+#define PWinGravity (1L << 9)
+
+struct motif_wm_hints {
+ uint32_t flags;
+ uint32_t functions;
+ uint32_t decorations;
+ int32_t input_mode;
+ uint32_t status;
+};
+
+#define MWM_HINTS_FUNCTIONS (1L << 0)
+#define MWM_HINTS_DECORATIONS (1L << 1)
+#define MWM_HINTS_INPUT_MODE (1L << 2)
+#define MWM_HINTS_STATUS (1L << 3)
+
+#define MWM_FUNC_ALL (1L << 0)
+#define MWM_FUNC_RESIZE (1L << 1)
+#define MWM_FUNC_MOVE (1L << 2)
+#define MWM_FUNC_MINIMIZE (1L << 3)
+#define MWM_FUNC_MAXIMIZE (1L << 4)
+#define MWM_FUNC_CLOSE (1L << 5)
+
+#define MWM_DECOR_ALL (1L << 0)
+#define MWM_DECOR_BORDER (1L << 1)
+#define MWM_DECOR_RESIZEH (1L << 2)
+#define MWM_DECOR_TITLE (1L << 3)
+#define MWM_DECOR_MENU (1L << 4)
+#define MWM_DECOR_MINIMIZE (1L << 5)
+#define MWM_DECOR_MAXIMIZE (1L << 6)
+
+#define MWM_INPUT_MODELESS 0
+#define MWM_INPUT_PRIMARY_APPLICATION_MODAL 1
+#define MWM_INPUT_SYSTEM_MODAL 2
+#define MWM_INPUT_FULL_APPLICATION_MODAL 3
+#define MWM_INPUT_APPLICATION_MODAL MWM_INPUT_PRIMARY_APPLICATION_MODAL
+
+#define MWM_TEAROFF_WINDOW (1L<<0)
+
+#define _NET_WM_MOVERESIZE_SIZE_TOPLEFT 0
+#define _NET_WM_MOVERESIZE_SIZE_TOP 1
+#define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT 2
+#define _NET_WM_MOVERESIZE_SIZE_RIGHT 3
+#define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT 4
+#define _NET_WM_MOVERESIZE_SIZE_BOTTOM 5
+#define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT 6
+#define _NET_WM_MOVERESIZE_SIZE_LEFT 7
+#define _NET_WM_MOVERESIZE_MOVE 8 /* movement only */
+#define _NET_WM_MOVERESIZE_SIZE_KEYBOARD 9 /* size via keyboard */
+#define _NET_WM_MOVERESIZE_MOVE_KEYBOARD 10 /* move via keyboard */
+#define _NET_WM_MOVERESIZE_CANCEL 11 /* cancel operation */
+
+struct weston_wm_window {
+ struct weston_wm *wm;
+ xcb_window_t id;
+ xcb_window_t frame_id;
+ cairo_surface_t *cairo_surface;
+ struct weston_surface *surface;
+ struct shell_surface *shsurf;
+ struct wl_listener surface_destroy_listener;
+ struct wl_event_source *repaint_source;
+ struct wl_event_source *configure_source;
+ int properties_dirty;
+ int pid;
+ char *machine;
+ char *class;
+ char *name;
+ struct weston_wm_window *transient_for;
+ uint32_t protocols;
+ xcb_atom_t type;
+ int width, height;
+ int x, y;
+ int saved_width, saved_height;
+ int decorate;
+ int override_redirect;
+ int fullscreen;
+ int has_alpha;
+ struct wm_size_hints size_hints;
+ struct motif_wm_hints motif_hints;
+};
+
+static struct weston_wm_window *
+get_wm_window(struct weston_surface *surface);
+
+static void
+weston_wm_window_schedule_repaint(struct weston_wm_window *window);
+
+static int __attribute__ ((format (printf, 1, 2)))
+wm_log(const char *fmt, ...)
+{
+#ifdef WM_DEBUG
+ int l;
+ va_list argp;
+
+ va_start(argp, fmt);
+ l = weston_vlog(fmt, argp);
+ va_end(argp);
+
+ return l;
+#else
+ return 0;
+#endif
+}
+
+static int __attribute__ ((format (printf, 1, 2)))
+wm_log_continue(const char *fmt, ...)
+{
+#ifdef WM_DEBUG
+ int l;
+ va_list argp;
+
+ va_start(argp, fmt);
+ l = weston_vlog_continue(fmt, argp);
+ va_end(argp);
+
+ return l;
+#else
+ return 0;
+#endif
+}
+
+
+const char *
+get_atom_name(xcb_connection_t *c, xcb_atom_t atom)
+{
+ xcb_get_atom_name_cookie_t cookie;
+ xcb_get_atom_name_reply_t *reply;
+ xcb_generic_error_t *e;
+ static char buffer[64];
+
+ if (atom == XCB_ATOM_NONE)
+ return "None";
+
+ cookie = xcb_get_atom_name (c, atom);
+ reply = xcb_get_atom_name_reply (c, cookie, &e);
+
+ if(reply) {
+ snprintf(buffer, sizeof buffer, "%.*s",
+ xcb_get_atom_name_name_length (reply),
+ xcb_get_atom_name_name (reply));
+ } else {
+ snprintf(buffer, sizeof buffer, "(atom %u)", atom);
+ }
+
+ free(reply);
+
+ return buffer;
+}
+
+static xcb_cursor_t
+xcb_cursor_image_load_cursor(struct weston_wm *wm, const XcursorImage *img)
+{
+ xcb_connection_t *c = wm->conn;
+ xcb_screen_iterator_t s = xcb_setup_roots_iterator(xcb_get_setup(c));
+ xcb_screen_t *screen = s.data;
+ xcb_gcontext_t gc;
+ xcb_pixmap_t pix;
+ xcb_render_picture_t pic;
+ xcb_cursor_t cursor;
+ int stride = img->width * 4;
+
+ pix = xcb_generate_id(c);
+ xcb_create_pixmap(c, 32, pix, screen->root, img->width, img->height);
+
+ pic = xcb_generate_id(c);
+ xcb_render_create_picture(c, pic, pix, wm->format_rgba.id, 0, 0);
+
+ gc = xcb_generate_id(c);
+ xcb_create_gc(c, gc, pix, 0, 0);
+
+ xcb_put_image(c, XCB_IMAGE_FORMAT_Z_PIXMAP, pix, gc,
+ img->width, img->height, 0, 0, 0, 32,
+ stride * img->height, (uint8_t *) img->pixels);
+ xcb_free_gc(c, gc);
+
+ cursor = xcb_generate_id(c);
+ xcb_render_create_cursor(c, cursor, pic, img->xhot, img->yhot);
+
+ xcb_render_free_picture(c, pic);
+ xcb_free_pixmap(c, pix);
+
+ return cursor;
+}
+
+static xcb_cursor_t
+xcb_cursor_images_load_cursor(struct weston_wm *wm, const XcursorImages *images)
+{
+ /* TODO: treat animated cursors as well */
+ if (images->nimage != 1)
+ return -1;
+
+ return xcb_cursor_image_load_cursor(wm, images->images[0]);
+}
+
+static xcb_cursor_t
+xcb_cursor_library_load_cursor(struct weston_wm *wm, const char *file)
+{
+ xcb_cursor_t cursor;
+ XcursorImages *images;
+ char *v = NULL;
+ int size = 0;
+
+ if (!file)
+ return 0;
+
+ v = getenv ("XCURSOR_SIZE");
+ if (v)
+ size = atoi(v);
+
+ if (!size)
+ size = 32;
+
+ images = XcursorLibraryLoadImages (file, NULL, size);
+ if (!images)
+ return -1;
+
+ cursor = xcb_cursor_images_load_cursor (wm, images);
+ XcursorImagesDestroy (images);
+
+ return cursor;
+}
+
+void
+dump_property(struct weston_wm *wm,
+ xcb_atom_t property, xcb_get_property_reply_t *reply)
+{
+ int32_t *incr_value;
+ const char *text_value, *name;
+ xcb_atom_t *atom_value;
+ int width, len;
+ uint32_t i;
+
+ width = wm_log_continue("%s: ", get_atom_name(wm->conn, property));
+ if (reply == NULL) {
+ wm_log_continue("(no reply)\n");
+ return;
+ }
+
+ width += wm_log_continue("%s/%d, length %d (value_len %d): ",
+ get_atom_name(wm->conn, reply->type),
+ reply->format,
+ xcb_get_property_value_length(reply),
+ reply->value_len);
+
+ if (reply->type == wm->atom.incr) {
+ incr_value = xcb_get_property_value(reply);
+ wm_log_continue("%d\n", *incr_value);
+ } else if (reply->type == wm->atom.utf8_string ||
+ reply->type == wm->atom.string) {
+ text_value = xcb_get_property_value(reply);
+ if (reply->value_len > 40)
+ len = 40;
+ else
+ len = reply->value_len;
+ wm_log_continue("\"%.*s\"\n", len, text_value);
+ } else if (reply->type == XCB_ATOM_ATOM) {
+ atom_value = xcb_get_property_value(reply);
+ for (i = 0; i < reply->value_len; i++) {
+ name = get_atom_name(wm->conn, atom_value[i]);
+ if (width + strlen(name) + 2 > 78) {
+ wm_log_continue("\n ");
+ width = 4;
+ } else if (i > 0) {
+ width += wm_log_continue(", ");
+ }
+
+ width += wm_log_continue("%s", name);
+ }
+ wm_log_continue("\n");
+ } else {
+ wm_log_continue("huh?\n");
+ }
+}
+
+static void
+read_and_dump_property(struct weston_wm *wm,
+ xcb_window_t window, xcb_atom_t property)
+{
+ xcb_get_property_reply_t *reply;
+ xcb_get_property_cookie_t cookie;
+
+ cookie = xcb_get_property(wm->conn, 0, window,
+ property, XCB_ATOM_ANY, 0, 2048);
+ reply = xcb_get_property_reply(wm->conn, cookie, NULL);
+
+ dump_property(wm, property, reply);
+
+ free(reply);
+}
+
+/* We reuse some predefined, but otherwise useles atoms */
+#define TYPE_WM_PROTOCOLS XCB_ATOM_CUT_BUFFER0
+#define TYPE_MOTIF_WM_HINTS XCB_ATOM_CUT_BUFFER1
+#define TYPE_NET_WM_STATE XCB_ATOM_CUT_BUFFER2
+#define TYPE_WM_NORMAL_HINTS XCB_ATOM_CUT_BUFFER3
+
+static void
+weston_wm_window_read_properties(struct weston_wm_window *window)
+{
+ struct weston_wm *wm = window->wm;
+ struct weston_shell_interface *shell_interface =
+ &wm->server->compositor->shell_interface;
+
+#define F(field) offsetof(struct weston_wm_window, field)
+ const struct {
+ xcb_atom_t atom;
+ xcb_atom_t type;
+ int offset;
+ } props[] = {
+ { XCB_ATOM_WM_CLASS, XCB_ATOM_STRING, F(class) },
+ { XCB_ATOM_WM_NAME, XCB_ATOM_STRING, F(name) },
+ { XCB_ATOM_WM_TRANSIENT_FOR, XCB_ATOM_WINDOW, F(transient_for) },
+ { wm->atom.wm_protocols, TYPE_WM_PROTOCOLS, F(protocols) },
+ { wm->atom.wm_normal_hints, TYPE_WM_NORMAL_HINTS, F(protocols) },
+ { wm->atom.net_wm_state, TYPE_NET_WM_STATE },
+ { wm->atom.net_wm_window_type, XCB_ATOM_ATOM, F(type) },
+ { wm->atom.net_wm_name, XCB_ATOM_STRING, F(name) },
+ { wm->atom.net_wm_pid, XCB_ATOM_CARDINAL, F(pid) },
+ { wm->atom.motif_wm_hints, TYPE_MOTIF_WM_HINTS, 0 },
+ { wm->atom.wm_client_machine, XCB_ATOM_WM_CLIENT_MACHINE, F(machine) },
+ };
+#undef F
+
+ xcb_get_property_cookie_t cookie[ARRAY_LENGTH(props)];
+ xcb_get_property_reply_t *reply;
+ void *p;
+ uint32_t *xid;
+ xcb_atom_t *atom;
+ uint32_t i;
+
+ if (!window->properties_dirty)
+ return;
+ window->properties_dirty = 0;
+
+ for (i = 0; i < ARRAY_LENGTH(props); i++)
+ cookie[i] = xcb_get_property(wm->conn,
+ 0, /* delete */
+ window->id,
+ props[i].atom,
+ XCB_ATOM_ANY, 0, 2048);
+
+ window->decorate = !window->override_redirect;
+ window->size_hints.flags = 0;
+ window->motif_hints.flags = 0;
+
+ for (i = 0; i < ARRAY_LENGTH(props); i++) {
+ reply = xcb_get_property_reply(wm->conn, cookie[i], NULL);
+ if (!reply)
+ /* Bad window, typically */
+ continue;
+ if (reply->type == XCB_ATOM_NONE) {
+ /* No such property */
+ free(reply);
+ continue;
+ }
+
+ p = ((char *) window + props[i].offset);
+
+ switch (props[i].type) {
+ case XCB_ATOM_WM_CLIENT_MACHINE:
+ case XCB_ATOM_STRING:
+ /* FIXME: We're using this for both string and
+ utf8_string */
+ if (*(char **) p)
+ free(*(char **) p);
+
+ *(char **) p =
+ strndup(xcb_get_property_value(reply),
+ xcb_get_property_value_length(reply));
+ break;
+ case XCB_ATOM_WINDOW:
+ xid = xcb_get_property_value(reply);
+ *(struct weston_wm_window **) p =
+ hash_table_lookup(wm->window_hash, *xid);
+ break;
+ case XCB_ATOM_CARDINAL:
+ case XCB_ATOM_ATOM:
+ atom = xcb_get_property_value(reply);
+ *(xcb_atom_t *) p = *atom;
+ break;
+ case TYPE_WM_PROTOCOLS:
+ break;
+ case TYPE_WM_NORMAL_HINTS:
+ memcpy(&window->size_hints,
+ xcb_get_property_value(reply),
+ sizeof window->size_hints);
+ break;
+ case TYPE_NET_WM_STATE:
+ window->fullscreen = 0;
+ atom = xcb_get_property_value(reply);
+ for (i = 0; i < reply->value_len; i++)
+ if (atom[i] == wm->atom.net_wm_state_fullscreen)
+ window->fullscreen = 1;
+ break;
+ case TYPE_MOTIF_WM_HINTS:
+ memcpy(&window->motif_hints,
+ xcb_get_property_value(reply),
+ sizeof window->motif_hints);
+ if (window->motif_hints.flags & MWM_HINTS_DECORATIONS)
+ window->decorate =
+ window->motif_hints.decorations > 0;
+ break;
+ default:
+ break;
+ }
+ free(reply);
+ }
+
+ if (window->shsurf && window->name)
+ shell_interface->set_title(window->shsurf, window->name);
+}
+
+static void
+weston_wm_window_get_frame_size(struct weston_wm_window *window,
+ int *width, int *height)
+{
+ struct theme *t = window->wm->theme;
+
+ if (window->fullscreen) {
+ *width = window->width;
+ *height = window->height;
+ } else if (window->decorate) {
+ *width = window->width + (t->margin + t->width) * 2;
+ *height = window->height +
+ t->margin * 2 + t->width + t->titlebar_height;
+ } else {
+ *width = window->width + t->margin * 2;
+ *height = window->height + t->margin * 2;
+ }
+}
+
+static void
+weston_wm_window_get_child_position(struct weston_wm_window *window,
+ int *x, int *y)
+{
+ struct theme *t = window->wm->theme;
+
+ if (window->fullscreen) {
+ *x = 0;
+ *y = 0;
+ } else if (window->decorate) {
+ *x = t->margin + t->width;
+ *y = t->margin + t->titlebar_height;
+ } else {
+ *x = t->margin;
+ *y = t->margin;
+ }
+}
+
+static void
+weston_wm_window_send_configure_notify(struct weston_wm_window *window)
+{
+ xcb_configure_notify_event_t configure_notify;
+ struct weston_wm *wm = window->wm;
+ int x, y;
+
+ weston_wm_window_get_child_position(window, &x, &y);
+ configure_notify.response_type = XCB_CONFIGURE_NOTIFY;
+ configure_notify.pad0 = 0;
+ configure_notify.event = window->id;
+ configure_notify.window = window->id;
+ configure_notify.above_sibling = XCB_WINDOW_NONE;
+ configure_notify.x = x;
+ configure_notify.y = y;
+ configure_notify.width = window->width;
+ configure_notify.height = window->height;
+ configure_notify.border_width = 0;
+ configure_notify.override_redirect = 0;
+ configure_notify.pad1 = 0;
+
+ xcb_send_event(wm->conn, 0, window->id,
+ XCB_EVENT_MASK_STRUCTURE_NOTIFY,
+ (char *) &configure_notify);
+}
+
+static void
+weston_wm_handle_configure_request(struct weston_wm *wm, xcb_generic_event_t *event)
+{
+ xcb_configure_request_event_t *configure_request =
+ (xcb_configure_request_event_t *) event;
+ struct weston_wm_window *window;
+ uint32_t mask, values[16];
+ int x, y, width, height, i = 0;
+
+ wm_log("XCB_CONFIGURE_REQUEST (window %d) %d,%d @ %dx%d\n",
+ configure_request->window,
+ configure_request->x, configure_request->y,
+ configure_request->width, configure_request->height);
+
+ window = hash_table_lookup(wm->window_hash, configure_request->window);
+
+ if (window->fullscreen) {
+ weston_wm_window_send_configure_notify(window);
+ return;
+ }
+
+ if (configure_request->value_mask & XCB_CONFIG_WINDOW_WIDTH)
+ window->width = configure_request->width;
+ if (configure_request->value_mask & XCB_CONFIG_WINDOW_HEIGHT)
+ window->height = configure_request->height;
+
+ weston_wm_window_get_child_position(window, &x, &y);
+ values[i++] = x;
+ values[i++] = y;
+ values[i++] = window->width;
+ values[i++] = window->height;
+ values[i++] = 0;
+ mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y |
+ XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT |
+ XCB_CONFIG_WINDOW_BORDER_WIDTH;
+ if (configure_request->value_mask & XCB_CONFIG_WINDOW_SIBLING) {
+ values[i++] = configure_request->sibling;
+ mask |= XCB_CONFIG_WINDOW_SIBLING;
+ }
+ if (configure_request->value_mask & XCB_CONFIG_WINDOW_STACK_MODE) {
+ values[i++] = configure_request->stack_mode;
+ mask |= XCB_CONFIG_WINDOW_STACK_MODE;
+ }
+
+ xcb_configure_window(wm->conn, window->id, mask, values);
+
+ weston_wm_window_get_frame_size(window, &width, &height);
+ values[0] = width;
+ values[1] = height;
+ mask = XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;
+ xcb_configure_window(wm->conn, window->frame_id, mask, values);
+
+ weston_wm_window_schedule_repaint(window);
+}
+
+static int
+our_resource(struct weston_wm *wm, uint32_t id)
+{
+ const xcb_setup_t *setup;
+
+ setup = xcb_get_setup(wm->conn);
+
+ return (id & ~setup->resource_id_mask) == setup->resource_id_base;
+}
+
+static void
+weston_wm_handle_configure_notify(struct weston_wm *wm, xcb_generic_event_t *event)
+{
+ xcb_configure_notify_event_t *configure_notify =
+ (xcb_configure_notify_event_t *) event;
+ struct weston_wm_window *window;
+
+ wm_log("XCB_CONFIGURE_NOTIFY (window %d) %d,%d @ %dx%d\n",
+ configure_notify->window,
+ configure_notify->x, configure_notify->y,
+ configure_notify->width, configure_notify->height);
+
+ window = hash_table_lookup(wm->window_hash, configure_notify->window);
+ window->x = configure_notify->x;
+ window->y = configure_notify->y;
+ if (window->override_redirect) {
+ window->width = configure_notify->width;
+ window->height = configure_notify->height;
+ }
+}
+
+static void
+weston_wm_kill_client(struct wl_listener *listener, void *data)
+{
+ struct weston_surface *surface = data;
+ struct weston_wm_window *window = get_wm_window(surface);
+ char name[1024];
+
+ if (!window)
+ return;
+
+ gethostname(name, 1024);
+
+ /* this is only one heuristic to guess the PID of a client is valid,
+ * assuming it's compliant with icccm and ewmh. Non-compliants and
+ * remote applications of course fail. */
+ if (!strcmp(window->machine, name) && window->pid != 0)
+ kill(window->pid, SIGKILL);
+}
+
+static void
+weston_wm_window_activate(struct wl_listener *listener, void *data)
+{
+ struct weston_surface *surface = data;
+ struct weston_wm_window *window = NULL;
+ struct weston_wm *wm =
+ container_of(listener, struct weston_wm, activate_listener);
+ xcb_client_message_event_t client_message;
+
+ if (surface) {
+ window = get_wm_window(surface);
+ }
+
+ if (window) {
+ client_message.response_type = XCB_CLIENT_MESSAGE;
+ client_message.format = 32;
+ client_message.window = window->id;
+ client_message.type = wm->atom.wm_protocols;
+ client_message.data.data32[0] = wm->atom.wm_take_focus;
+ client_message.data.data32[1] = XCB_TIME_CURRENT_TIME;
+
+ xcb_send_event(wm->conn, 0, window->id,
+ XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
+ (char *) &client_message);
+
+ xcb_set_input_focus (wm->conn, XCB_INPUT_FOCUS_POINTER_ROOT,
+ window->id, XCB_TIME_CURRENT_TIME);
+ } else {
+ xcb_set_input_focus (wm->conn,
+ XCB_INPUT_FOCUS_POINTER_ROOT,
+ XCB_NONE,
+ XCB_TIME_CURRENT_TIME);
+ }
+
+ if (wm->focus_window)
+ weston_wm_window_schedule_repaint(wm->focus_window);
+ wm->focus_window = window;
+ if (wm->focus_window)
+ weston_wm_window_schedule_repaint(wm->focus_window);
+}
+
+static void
+weston_wm_window_transform(struct wl_listener *listener, void *data)
+{
+ struct weston_surface *surface = data;
+ struct weston_wm_window *window = get_wm_window(surface);
+ struct weston_wm *wm =
+ container_of(listener, struct weston_wm, transform_listener);
+ uint32_t mask, values[2];
+
+ if (!window || !wm)
+ return;
+
+ if (!weston_surface_is_mapped(surface))
+ return;
+
+ if (window->x != surface->geometry.x ||
+ window->y != surface->geometry.y) {
+ values[0] = surface->geometry.x;
+ values[1] = surface->geometry.y;
+ mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y;
+
+ xcb_configure_window(wm->conn, window->frame_id, mask, values);
+ xcb_flush(wm->conn);
+ }
+}
+
+#define ICCCM_WITHDRAWN_STATE 0
+#define ICCCM_NORMAL_STATE 1
+#define ICCCM_ICONIC_STATE 3
+
+static void
+weston_wm_window_set_wm_state(struct weston_wm_window *window, int32_t state)
+{
+ struct weston_wm *wm = window->wm;
+ uint32_t property[2];
+
+ property[0] = state;
+ property[1] = XCB_WINDOW_NONE;
+
+ xcb_change_property(wm->conn,
+ XCB_PROP_MODE_REPLACE,
+ window->id,
+ wm->atom.wm_state,
+ wm->atom.wm_state,
+ 32, /* format */
+ 2, property);
+}
+
+static void
+weston_wm_window_set_net_wm_state(struct weston_wm_window *window)
+{
+ struct weston_wm *wm = window->wm;
+ uint32_t property[1];
+ int i;
+
+ i = 0;
+ if (window->fullscreen)
+ property[i++] = wm->atom.net_wm_state_fullscreen;
+
+ xcb_change_property(wm->conn,
+ XCB_PROP_MODE_REPLACE,
+ window->id,
+ wm->atom.net_wm_state,
+ XCB_ATOM_ATOM,
+ 32, /* format */
+ i, property);
+}
+
+static void
+weston_wm_window_create_frame(struct weston_wm_window *window)
+{
+ struct weston_wm *wm = window->wm;
+ uint32_t values[3];
+ int x, y, width, height;
+
+ weston_wm_window_get_frame_size(window, &width, &height);
+ weston_wm_window_get_child_position(window, &x, &y);
+
+ values[0] = wm->screen->black_pixel;
+ values[1] =
+ XCB_EVENT_MASK_KEY_PRESS |
+ XCB_EVENT_MASK_KEY_RELEASE |
+ XCB_EVENT_MASK_BUTTON_PRESS |
+ XCB_EVENT_MASK_BUTTON_RELEASE |
+ XCB_EVENT_MASK_POINTER_MOTION |
+ XCB_EVENT_MASK_ENTER_WINDOW |
+ XCB_EVENT_MASK_LEAVE_WINDOW |
+ XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
+ XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT;
+ values[2] = wm->colormap;
+
+ window->frame_id = xcb_generate_id(wm->conn);
+ xcb_create_window(wm->conn,
+ 32,
+ window->frame_id,
+ wm->screen->root,
+ 0, 0,
+ width, height,
+ 0,
+ XCB_WINDOW_CLASS_INPUT_OUTPUT,
+ wm->visual_id,
+ XCB_CW_BORDER_PIXEL |
+ XCB_CW_EVENT_MASK |
+ XCB_CW_COLORMAP, values);
+
+ xcb_reparent_window(wm->conn, window->id, window->frame_id, x, y);
+
+ values[0] = 0;
+ xcb_configure_window(wm->conn, window->id,
+ XCB_CONFIG_WINDOW_BORDER_WIDTH, values);
+
+ window->cairo_surface =
+ cairo_xcb_surface_create_with_xrender_format(wm->conn,
+ wm->screen,
+ window->frame_id,
+ &wm->format_rgba,
+ width, height);
+
+ hash_table_insert(wm->window_hash, window->frame_id, window);
+}
+
+static void
+weston_wm_handle_map_request(struct weston_wm *wm, xcb_generic_event_t *event)
+{
+ xcb_map_request_event_t *map_request =
+ (xcb_map_request_event_t *) event;
+ struct weston_wm_window *window;
+
+ if (our_resource(wm, map_request->window)) {
+ wm_log("XCB_MAP_REQUEST (window %d, ours)\n",
+ map_request->window);
+ return;
+ }
+
+ window = hash_table_lookup(wm->window_hash, map_request->window);
+
+ weston_wm_window_read_properties(window);
+
+ if (window->frame_id == XCB_WINDOW_NONE)
+ weston_wm_window_create_frame(window);
+
+ wm_log("XCB_MAP_REQUEST (window %d, %p, frame %d)\n",
+ window->id, window, window->frame_id);
+
+ weston_wm_window_set_wm_state(window, ICCCM_NORMAL_STATE);
+ weston_wm_window_set_net_wm_state(window);
+
+ xcb_map_window(wm->conn, map_request->window);
+ xcb_map_window(wm->conn, window->frame_id);
+}
+
+static void
+weston_wm_handle_map_notify(struct weston_wm *wm, xcb_generic_event_t *event)
+{
+ xcb_map_notify_event_t *map_notify = (xcb_map_notify_event_t *) event;
+
+ if (our_resource(wm, map_notify->window)) {
+ wm_log("XCB_MAP_NOTIFY (window %d, ours)\n",
+ map_notify->window);
+ return;
+ }
+
+ wm_log("XCB_MAP_NOTIFY (window %d)\n", map_notify->window);
+}
+
+static void
+weston_wm_handle_unmap_notify(struct weston_wm *wm, xcb_generic_event_t *event)
+{
+ xcb_unmap_notify_event_t *unmap_notify =
+ (xcb_unmap_notify_event_t *) event;
+ struct weston_wm_window *window;
+
+ wm_log("XCB_UNMAP_NOTIFY (window %d, event %d%s)\n",
+ unmap_notify->window,
+ unmap_notify->event,
+ our_resource(wm, unmap_notify->window) ? ", ours" : "");
+
+ if (our_resource(wm, unmap_notify->window))
+ return;
+
+ if (unmap_notify->response_type & SEND_EVENT_MASK)
+ /* We just ignore the ICCCM 4.1.4 synthetic unmap notify
+ * as it may come in after we've destroyed the window. */
+ return;
+
+ window = hash_table_lookup(wm->window_hash, unmap_notify->window);
+ if (wm->focus_window == window)
+ wm->focus_window = NULL;
+ if (window->surface)
+ wl_list_remove(&window->surface_destroy_listener.link);
+ window->surface = NULL;
+ window->shsurf = NULL;
+ xcb_unmap_window(wm->conn, window->frame_id);
+}
+
+static void
+weston_wm_window_draw_decoration(void *data)
+{
+ struct weston_wm_window *window = data;
+ struct weston_wm *wm = window->wm;
+ struct theme *t = wm->theme;
+ cairo_t *cr;
+ int x, y, width, height;
+ const char *title;
+ uint32_t flags = 0;
+
+ weston_wm_window_read_properties(window);
+
+ window->repaint_source = NULL;
+
+ weston_wm_window_get_frame_size(window, &width, &height);
+ weston_wm_window_get_child_position(window, &x, &y);
+
+ cairo_xcb_surface_set_size(window->cairo_surface, width, height);
+ cr = cairo_create(window->cairo_surface);
+
+ if (window->fullscreen) {
+ /* nothing */
+ } else if (window->decorate) {
+ if (wm->focus_window == window)
+ flags |= THEME_FRAME_ACTIVE;
+
+ if (window->name)
+ title = window->name;
+ else
+ title = "untitled";
+
+ theme_render_frame(t, cr, width, height, title, flags);
+ } else {
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ cairo_set_source_rgba(cr, 0, 0, 0, 0);
+ cairo_paint(cr);
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+ cairo_set_source_rgba(cr, 0, 0, 0, 0.45);
+ tile_mask(cr, t->shadow, 2, 2, width + 8, height + 8, 64, 64);
+ }
+
+ cairo_destroy(cr);
+
+ if (window->surface) {
+ pixman_region32_fini(&window->surface->pending.opaque);
+ if(window->has_alpha) {
+ pixman_region32_init(&window->surface->pending.opaque);
+ } else {
+ /* We leave an extra pixel around the X window area to
+ * make sure we don't sample from the undefined alpha
+ * channel when filtering. */
+ pixman_region32_init_rect(&window->surface->pending.opaque,
+ x - 1, y - 1,
+ window->width + 2,
+ window->height + 2);
+ }
+ weston_surface_geometry_dirty(window->surface);
+
+ pixman_region32_fini(&window->surface->pending.input);
+
+ if (window->fullscreen) {
+ pixman_region32_init_rect(&window->surface->pending.input,
+ 0, 0, window->width, window->height);
+ } else if (window->decorate) {
+ pixman_region32_init_rect(&window->surface->pending.input,
+ t->margin, t->margin,
+ width - 2 * t->margin,
+ height - 2 * t->margin);
+ }
+ }
+}
+
+static void
+weston_wm_window_schedule_repaint(struct weston_wm_window *window)
+{
+ struct weston_wm *wm = window->wm;
+ int width, height;
+
+ if (window->frame_id == XCB_WINDOW_NONE) {
+ if (window->surface != NULL) {
+ weston_wm_window_get_frame_size(window, &width, &height);
+ pixman_region32_fini(&window->surface->pending.opaque);
+ if(window->has_alpha) {
+ pixman_region32_init(&window->surface->pending.opaque);
+ } else {
+ pixman_region32_init_rect(&window->surface->pending.opaque, 0, 0,
+ width, height);
+ }
+ weston_surface_geometry_dirty(window->surface);
+ }
+ return;
+ }
+
+ if (window->repaint_source)
+ return;
+
+ window->repaint_source =
+ wl_event_loop_add_idle(wm->server->loop,
+ weston_wm_window_draw_decoration,
+ window);
+}
+
+static void
+weston_wm_handle_property_notify(struct weston_wm *wm, xcb_generic_event_t *event)
+{
+ xcb_property_notify_event_t *property_notify =
+ (xcb_property_notify_event_t *) event;
+ struct weston_wm_window *window;
+
+ window = hash_table_lookup(wm->window_hash, property_notify->window);
+ if (!window)
+ return;
+
+ window->properties_dirty = 1;
+
+ wm_log("XCB_PROPERTY_NOTIFY: window %d, ", property_notify->window);
+ if (property_notify->state == XCB_PROPERTY_DELETE)
+ wm_log("deleted\n");
+ else
+ read_and_dump_property(wm, property_notify->window,
+ property_notify->atom);
+
+ if (property_notify->atom == wm->atom.net_wm_name ||
+ property_notify->atom == XCB_ATOM_WM_NAME)
+ weston_wm_window_schedule_repaint(window);
+}
+
+static void
+weston_wm_window_create(struct weston_wm *wm,
+ xcb_window_t id, int width, int height, int x, int y, int override)
+{
+ struct weston_wm_window *window;
+ uint32_t values[1];
+ xcb_get_geometry_cookie_t geometry_cookie;
+ xcb_get_geometry_reply_t *geometry_reply;
+
+ window = zalloc(sizeof *window);
+ if (window == NULL) {
+ wm_log("failed to allocate window\n");
+ return;
+ }
+
+ geometry_cookie = xcb_get_geometry(wm->conn, id);
+
+ values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE;
+ xcb_change_window_attributes(wm->conn, id, XCB_CW_EVENT_MASK, values);
+
+ window->wm = wm;
+ window->id = id;
+ window->properties_dirty = 1;
+ window->override_redirect = override;
+ window->width = width;
+ window->height = height;
+ window->x = x;
+ window->y = y;
+
+ geometry_reply = xcb_get_geometry_reply(wm->conn, geometry_cookie, NULL);
+ /* technically we should use XRender and check the visual format's
+ alpha_mask, but checking depth is simpler and works in all known cases */
+ if(geometry_reply != NULL)
+ window->has_alpha = geometry_reply->depth == 32;
+ free(geometry_reply);
+
+ hash_table_insert(wm->window_hash, id, window);
+}
+
+static void
+weston_wm_window_destroy(struct weston_wm_window *window)
+{
+ struct weston_wm *wm = window->wm;
+
+ if (window->repaint_source)
+ wl_event_source_remove(window->repaint_source);
+ if (window->cairo_surface)
+ cairo_surface_destroy(window->cairo_surface);
+
+ if (window->frame_id) {
+ xcb_reparent_window(wm->conn, window->id, wm->wm_window, 0, 0);
+ xcb_destroy_window(wm->conn, window->frame_id);
+ weston_wm_window_set_wm_state(window, ICCCM_WITHDRAWN_STATE);
+ hash_table_remove(wm->window_hash, window->frame_id);
+ window->frame_id = XCB_WINDOW_NONE;
+ }
+
+ hash_table_remove(window->wm->window_hash, window->id);
+ free(window);
+}
+
+static void
+weston_wm_handle_create_notify(struct weston_wm *wm, xcb_generic_event_t *event)
+{
+ xcb_create_notify_event_t *create_notify =
+ (xcb_create_notify_event_t *) event;
+
+ wm_log("XCB_CREATE_NOTIFY (window %d, width %d, height %d%s%s)\n",
+ create_notify->window,
+ create_notify->width, create_notify->height,
+ create_notify->override_redirect ? ", override" : "",
+ our_resource(wm, create_notify->window) ? ", ours" : "");
+
+ if (our_resource(wm, create_notify->window))
+ return;
+
+ weston_wm_window_create(wm, create_notify->window,
+ create_notify->width, create_notify->height,
+ create_notify->x, create_notify->y,
+ create_notify->override_redirect);
+}
+
+static void
+weston_wm_handle_destroy_notify(struct weston_wm *wm, xcb_generic_event_t *event)
+{
+ xcb_destroy_notify_event_t *destroy_notify =
+ (xcb_destroy_notify_event_t *) event;
+ struct weston_wm_window *window;
+
+ wm_log("XCB_DESTROY_NOTIFY, win %d, event %d%s\n",
+ destroy_notify->window,
+ destroy_notify->event,
+ our_resource(wm, destroy_notify->window) ? ", ours" : "");
+
+ if (our_resource(wm, destroy_notify->window))
+ return;
+
+ window = hash_table_lookup(wm->window_hash, destroy_notify->window);
+ weston_wm_window_destroy(window);
+}
+
+static void
+weston_wm_handle_reparent_notify(struct weston_wm *wm, xcb_generic_event_t *event)
+{
+ xcb_reparent_notify_event_t *reparent_notify =
+ (xcb_reparent_notify_event_t *) event;
+ struct weston_wm_window *window;
+
+ wm_log("XCB_REPARENT_NOTIFY (window %d, parent %d, event %d)\n",
+ reparent_notify->window,
+ reparent_notify->parent,
+ reparent_notify->event);
+
+ if (reparent_notify->parent == wm->screen->root) {
+ weston_wm_window_create(wm, reparent_notify->window, 10, 10,
+ reparent_notify->x, reparent_notify->y,
+ reparent_notify->override_redirect);
+ } else if (!our_resource(wm, reparent_notify->parent)) {
+ window = hash_table_lookup(wm->window_hash,
+ reparent_notify->window);
+ weston_wm_window_destroy(window);
+ }
+}
+
+struct weston_seat *
+weston_wm_pick_seat(struct weston_wm *wm)
+{
+ return container_of(wm->server->compositor->seat_list.next,
+ struct weston_seat, link);
+}
+
+static void
+weston_wm_window_handle_moveresize(struct weston_wm_window *window,
+ xcb_client_message_event_t *client_message)
+{
+ static const int map[] = {
+ THEME_LOCATION_RESIZING_TOP_LEFT,
+ THEME_LOCATION_RESIZING_TOP,
+ THEME_LOCATION_RESIZING_TOP_RIGHT,
+ THEME_LOCATION_RESIZING_RIGHT,
+ THEME_LOCATION_RESIZING_BOTTOM_RIGHT,
+ THEME_LOCATION_RESIZING_BOTTOM,
+ THEME_LOCATION_RESIZING_BOTTOM_LEFT,
+ THEME_LOCATION_RESIZING_LEFT
+ };
+
+ struct weston_wm *wm = window->wm;
+ struct weston_seat *seat = weston_wm_pick_seat(wm);
+ int detail;
+ struct weston_shell_interface *shell_interface =
+ &wm->server->compositor->shell_interface;
+
+ if (seat->pointer->button_count != 1 ||
+ seat->pointer->focus != window->surface)
+ return;
+
+ detail = client_message->data.data32[2];
+ switch (detail) {
+ case _NET_WM_MOVERESIZE_MOVE:
+ shell_interface->move(window->shsurf, seat);
+ break;
+ case _NET_WM_MOVERESIZE_SIZE_TOPLEFT:
+ case _NET_WM_MOVERESIZE_SIZE_TOP:
+ case _NET_WM_MOVERESIZE_SIZE_TOPRIGHT:
+ case _NET_WM_MOVERESIZE_SIZE_RIGHT:
+ case _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT:
+ case _NET_WM_MOVERESIZE_SIZE_BOTTOM:
+ case _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT:
+ case _NET_WM_MOVERESIZE_SIZE_LEFT:
+ shell_interface->resize(window->shsurf, seat, map[detail]);
+ break;
+ case _NET_WM_MOVERESIZE_CANCEL:
+ break;
+ }
+}
+
+#define _NET_WM_STATE_REMOVE 0
+#define _NET_WM_STATE_ADD 1
+#define _NET_WM_STATE_TOGGLE 2
+
+static int
+update_state(int action, int *state)
+{
+ int new_state, changed;
+
+ switch (action) {
+ case _NET_WM_STATE_REMOVE:
+ new_state = 0;
+ break;
+ case _NET_WM_STATE_ADD:
+ new_state = 1;
+ break;
+ case _NET_WM_STATE_TOGGLE:
+ new_state = !*state;
+ break;
+ default:
+ return 0;
+ }
+
+ changed = (*state != new_state);
+ *state = new_state;
+
+ return changed;
+}
+
+static void
+weston_wm_window_configure(void *data);
+
+static void
+weston_wm_window_handle_state(struct weston_wm_window *window,
+ xcb_client_message_event_t *client_message)
+{
+ struct weston_wm *wm = window->wm;
+ struct weston_shell_interface *shell_interface =
+ &wm->server->compositor->shell_interface;
+ uint32_t action, property;
+
+ action = client_message->data.data32[0];
+ property = client_message->data.data32[1];
+
+ if (property == wm->atom.net_wm_state_fullscreen &&
+ update_state(action, &window->fullscreen)) {
+ weston_wm_window_set_net_wm_state(window);
+ if (window->fullscreen) {
+ window->saved_width = window->width;
+ window->saved_height = window->height;
+
+ if (window->shsurf)
+ shell_interface->set_fullscreen(window->shsurf,
+ WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT,
+ 0, NULL);
+ } else {
+ shell_interface->set_toplevel(window->shsurf);
+ window->width = window->saved_width;
+ window->height = window->saved_height;
+ weston_wm_window_configure(window);
+ }
+ }
+}
+
+static void
+weston_wm_handle_client_message(struct weston_wm *wm,
+ xcb_generic_event_t *event)
+{
+ xcb_client_message_event_t *client_message =
+ (xcb_client_message_event_t *) event;
+ struct weston_wm_window *window;
+
+ window = hash_table_lookup(wm->window_hash, client_message->window);
+
+ wm_log("XCB_CLIENT_MESSAGE (%s %d %d %d %d %d win %d)\n",
+ get_atom_name(wm->conn, client_message->type),
+ client_message->data.data32[0],
+ client_message->data.data32[1],
+ client_message->data.data32[2],
+ client_message->data.data32[3],
+ client_message->data.data32[4],
+ client_message->window);
+
+ if (client_message->type == wm->atom.net_wm_moveresize)
+ weston_wm_window_handle_moveresize(window, client_message);
+ else if (client_message->type == wm->atom.net_wm_state)
+ weston_wm_window_handle_state(window, client_message);
+}
+
+enum cursor_type {
+ XWM_CURSOR_TOP,
+ XWM_CURSOR_BOTTOM,
+ XWM_CURSOR_LEFT,
+ XWM_CURSOR_RIGHT,
+ XWM_CURSOR_TOP_LEFT,
+ XWM_CURSOR_TOP_RIGHT,
+ XWM_CURSOR_BOTTOM_LEFT,
+ XWM_CURSOR_BOTTOM_RIGHT,
+ XWM_CURSOR_LEFT_PTR,
+};
+
+/*
+ * The following correspondences between file names and cursors was copied
+ * from: https://bugs.kde.org/attachment.cgi?id=67313
+ */
+
+static const char *bottom_left_corners[] = {
+ "bottom_left_corner",
+ "sw-resize",
+ "size_bdiag"
+};
+
+static const char *bottom_right_corners[] = {
+ "bottom_right_corner",
+ "se-resize",
+ "size_fdiag"
+};
+
+static const char *bottom_sides[] = {
+ "bottom_side",
+ "s-resize",
+ "size_ver"
+};
+
+static const char *left_ptrs[] = {
+ "left_ptr",
+ "default",
+ "top_left_arrow",
+ "left-arrow"
+};
+
+static const char *left_sides[] = {
+ "left_side",
+ "w-resize",
+ "size_hor"
+};
+
+static const char *right_sides[] = {
+ "right_side",
+ "e-resize",
+ "size_hor"
+};
+
+static const char *top_left_corners[] = {
+ "top_left_corner",
+ "nw-resize",
+ "size_fdiag"
+};
+
+static const char *top_right_corners[] = {
+ "top_right_corner",
+ "ne-resize",
+ "size_bdiag"
+};
+
+static const char *top_sides[] = {
+ "top_side",
+ "n-resize",
+ "size_ver"
+};
+
+struct cursor_alternatives {
+ const char **names;
+ size_t count;
+};
+
+static const struct cursor_alternatives cursors[] = {
+ {top_sides, ARRAY_LENGTH(top_sides)},
+ {bottom_sides, ARRAY_LENGTH(bottom_sides)},
+ {left_sides, ARRAY_LENGTH(left_sides)},
+ {right_sides, ARRAY_LENGTH(right_sides)},
+ {top_left_corners, ARRAY_LENGTH(top_left_corners)},
+ {top_right_corners, ARRAY_LENGTH(top_right_corners)},
+ {bottom_left_corners, ARRAY_LENGTH(bottom_left_corners)},
+ {bottom_right_corners, ARRAY_LENGTH(bottom_right_corners)},
+ {left_ptrs, ARRAY_LENGTH(left_ptrs)},
+};
+
+static void
+weston_wm_create_cursors(struct weston_wm *wm)
+{
+ const char *name;
+ int i, count = ARRAY_LENGTH(cursors);
+ size_t j;
+
+ wm->cursors = malloc(count * sizeof(xcb_cursor_t));
+ for (i = 0; i < count; i++) {
+ for (j = 0; j < cursors[i].count; j++) {
+ name = cursors[i].names[j];
+ wm->cursors[i] =
+ xcb_cursor_library_load_cursor(wm, name);
+ if (wm->cursors[i] != (xcb_cursor_t)-1)
+ break;
+ }
+ }
+
+ wm->last_cursor = -1;
+}
+
+static void
+weston_wm_destroy_cursors(struct weston_wm *wm)
+{
+ uint8_t i;
+
+ for (i = 0; i < ARRAY_LENGTH(cursors); i++)
+ xcb_free_cursor(wm->conn, wm->cursors[i]);
+
+ free(wm->cursors);
+}
+
+static int
+get_cursor_for_location(struct theme *t, int width, int height, int x, int y)
+{
+ int location = theme_get_location(t, x, y, width, height, 0);
+
+ switch (location) {
+ case THEME_LOCATION_RESIZING_TOP:
+ return XWM_CURSOR_TOP;
+ case THEME_LOCATION_RESIZING_BOTTOM:
+ return XWM_CURSOR_BOTTOM;
+ case THEME_LOCATION_RESIZING_LEFT:
+ return XWM_CURSOR_LEFT;
+ case THEME_LOCATION_RESIZING_RIGHT:
+ return XWM_CURSOR_RIGHT;
+ case THEME_LOCATION_RESIZING_TOP_LEFT:
+ return XWM_CURSOR_TOP_LEFT;
+ case THEME_LOCATION_RESIZING_TOP_RIGHT:
+ return XWM_CURSOR_TOP_RIGHT;
+ case THEME_LOCATION_RESIZING_BOTTOM_LEFT:
+ return XWM_CURSOR_BOTTOM_LEFT;
+ case THEME_LOCATION_RESIZING_BOTTOM_RIGHT:
+ return XWM_CURSOR_BOTTOM_RIGHT;
+ case THEME_LOCATION_EXTERIOR:
+ case THEME_LOCATION_TITLEBAR:
+ default:
+ return XWM_CURSOR_LEFT_PTR;
+ }
+}
+
+static void
+weston_wm_window_set_cursor(struct weston_wm *wm, xcb_window_t window_id,
+ int cursor)
+{
+ uint32_t cursor_value_list;
+
+ if (wm->last_cursor == cursor)
+ return;
+
+ wm->last_cursor = cursor;
+
+ cursor_value_list = wm->cursors[cursor];
+ xcb_change_window_attributes (wm->conn, window_id,
+ XCB_CW_CURSOR, &cursor_value_list);
+ xcb_flush(wm->conn);
+}
+
+static void
+weston_wm_handle_button(struct weston_wm *wm, xcb_generic_event_t *event)
+{
+ xcb_button_press_event_t *button = (xcb_button_press_event_t *) event;
+ struct weston_shell_interface *shell_interface =
+ &wm->server->compositor->shell_interface;
+ struct weston_seat *seat = weston_wm_pick_seat(wm);
+ struct weston_wm_window *window;
+ enum theme_location location;
+ struct theme *t = wm->theme;
+ int width, height;
+
+ wm_log("XCB_BUTTON_%s (detail %d)\n",
+ button->response_type == XCB_BUTTON_PRESS ?
+ "PRESS" : "RELEASE", button->detail);
+
+ window = hash_table_lookup(wm->window_hash, button->event);
+ weston_wm_window_get_frame_size(window, &width, &height);
+
+ if (button->response_type == XCB_BUTTON_PRESS &&
+ button->detail == 1) {
+ location = theme_get_location(t,
+ button->event_x,
+ button->event_y,
+ width, height, 0);
+
+ switch (location) {
+ case THEME_LOCATION_TITLEBAR:
+ shell_interface->move(window->shsurf, seat);
+ break;
+ case THEME_LOCATION_RESIZING_TOP:
+ case THEME_LOCATION_RESIZING_BOTTOM:
+ case THEME_LOCATION_RESIZING_LEFT:
+ case THEME_LOCATION_RESIZING_RIGHT:
+ case THEME_LOCATION_RESIZING_TOP_LEFT:
+ case THEME_LOCATION_RESIZING_TOP_RIGHT:
+ case THEME_LOCATION_RESIZING_BOTTOM_LEFT:
+ case THEME_LOCATION_RESIZING_BOTTOM_RIGHT:
+ shell_interface->resize(window->shsurf,
+ seat, location);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void
+weston_wm_handle_motion(struct weston_wm *wm, xcb_generic_event_t *event)
+{
+ xcb_motion_notify_event_t *motion = (xcb_motion_notify_event_t *) event;
+ struct weston_wm_window *window;
+ int cursor, width, height;
+
+ window = hash_table_lookup(wm->window_hash, motion->event);
+ if (!window || !window->decorate)
+ return;
+
+ weston_wm_window_get_frame_size(window, &width, &height);
+ cursor = get_cursor_for_location(wm->theme, width, height,
+ motion->event_x, motion->event_y);
+
+ weston_wm_window_set_cursor(wm, window->frame_id, cursor);
+}
+
+static void
+weston_wm_handle_enter(struct weston_wm *wm, xcb_generic_event_t *event)
+{
+ xcb_enter_notify_event_t *enter = (xcb_enter_notify_event_t *) event;
+ struct weston_wm_window *window;
+ int cursor, width, height;
+
+ window = hash_table_lookup(wm->window_hash, enter->event);
+ if (!window || !window->decorate)
+ return;
+
+ weston_wm_window_get_frame_size(window, &width, &height);
+ cursor = get_cursor_for_location(wm->theme, width, height,
+ enter->event_x, enter->event_y);
+
+ weston_wm_window_set_cursor(wm, window->frame_id, cursor);
+}
+
+static void
+weston_wm_handle_leave(struct weston_wm *wm, xcb_generic_event_t *event)
+{
+ xcb_leave_notify_event_t *leave = (xcb_leave_notify_event_t *) event;
+ struct weston_wm_window *window;
+
+ window = hash_table_lookup(wm->window_hash, leave->event);
+ if (!window || !window->decorate)
+ return;
+
+ weston_wm_window_set_cursor(wm, window->frame_id, XWM_CURSOR_LEFT_PTR);
+}
+
+static int
+weston_wm_handle_event(int fd, uint32_t mask, void *data)
+{
+ struct weston_wm *wm = data;
+ xcb_generic_event_t *event;
+ int count = 0;
+
+ while (event = xcb_poll_for_event(wm->conn), event != NULL) {
+ if (weston_wm_handle_selection_event(wm, event)) {
+ free(event);
+ count++;
+ continue;
+ }
+
+ if (weston_wm_handle_dnd_event(wm, event)) {
+ free(event);
+ count++;
+ continue;
+ }
+
+ switch (EVENT_TYPE(event)) {
+ case XCB_BUTTON_PRESS:
+ case XCB_BUTTON_RELEASE:
+ weston_wm_handle_button(wm, event);
+ break;
+ case XCB_ENTER_NOTIFY:
+ weston_wm_handle_enter(wm, event);
+ break;
+ case XCB_LEAVE_NOTIFY:
+ weston_wm_handle_leave(wm, event);
+ break;
+ case XCB_MOTION_NOTIFY:
+ weston_wm_handle_motion(wm, event);
+ break;
+ case XCB_CREATE_NOTIFY:
+ weston_wm_handle_create_notify(wm, event);
+ break;
+ case XCB_MAP_REQUEST:
+ weston_wm_handle_map_request(wm, event);
+ break;
+ case XCB_MAP_NOTIFY:
+ weston_wm_handle_map_notify(wm, event);
+ break;
+ case XCB_UNMAP_NOTIFY:
+ weston_wm_handle_unmap_notify(wm, event);
+ break;
+ case XCB_REPARENT_NOTIFY:
+ weston_wm_handle_reparent_notify(wm, event);
+ break;
+ case XCB_CONFIGURE_REQUEST:
+ weston_wm_handle_configure_request(wm, event);
+ break;
+ case XCB_CONFIGURE_NOTIFY:
+ weston_wm_handle_configure_notify(wm, event);
+ break;
+ case XCB_DESTROY_NOTIFY:
+ weston_wm_handle_destroy_notify(wm, event);
+ break;
+ case XCB_MAPPING_NOTIFY:
+ wm_log("XCB_MAPPING_NOTIFY\n");
+ break;
+ case XCB_PROPERTY_NOTIFY:
+ weston_wm_handle_property_notify(wm, event);
+ break;
+ case XCB_CLIENT_MESSAGE:
+ weston_wm_handle_client_message(wm, event);
+ break;
+ }
+
+ free(event);
+ count++;
+ }
+
+ xcb_flush(wm->conn);
+
+ return count;
+}
+
+static void
+weston_wm_get_visual_and_colormap(struct weston_wm *wm)
+{
+ xcb_depth_iterator_t d_iter;
+ xcb_visualtype_iterator_t vt_iter;
+ xcb_visualtype_t *visualtype;
+
+ d_iter = xcb_screen_allowed_depths_iterator(wm->screen);
+ visualtype = NULL;
+ while (d_iter.rem > 0) {
+ if (d_iter.data->depth == 32) {
+ vt_iter = xcb_depth_visuals_iterator(d_iter.data);
+ visualtype = vt_iter.data;
+ break;
+ }
+
+ xcb_depth_next(&d_iter);
+ }
+
+ if (visualtype == NULL) {
+ weston_log("no 32 bit visualtype\n");
+ return;
+ }
+
+ wm->visual_id = visualtype->visual_id;
+ wm->colormap = xcb_generate_id(wm->conn);
+ xcb_create_colormap(wm->conn, XCB_COLORMAP_ALLOC_NONE,
+ wm->colormap, wm->screen->root, wm->visual_id);
+}
+
+static void
+weston_wm_get_resources(struct weston_wm *wm)
+{
+
+#define F(field) offsetof(struct weston_wm, field)
+
+ static const struct { const char *name; int offset; } atoms[] = {
+ { "WM_PROTOCOLS", F(atom.wm_protocols) },
+ { "WM_NORMAL_HINTS", F(atom.wm_normal_hints) },
+ { "WM_TAKE_FOCUS", F(atom.wm_take_focus) },
+ { "WM_DELETE_WINDOW", F(atom.wm_delete_window) },
+ { "WM_STATE", F(atom.wm_state) },
+ { "WM_S0", F(atom.wm_s0) },
+ { "WM_CLIENT_MACHINE", F(atom.wm_client_machine) },
+ { "_NET_WM_CM_S0", F(atom.net_wm_cm_s0) },
+ { "_NET_WM_NAME", F(atom.net_wm_name) },
+ { "_NET_WM_PID", F(atom.net_wm_pid) },
+ { "_NET_WM_ICON", F(atom.net_wm_icon) },
+ { "_NET_WM_STATE", F(atom.net_wm_state) },
+ { "_NET_WM_STATE_FULLSCREEN", F(atom.net_wm_state_fullscreen) },
+ { "_NET_WM_USER_TIME", F(atom.net_wm_user_time) },
+ { "_NET_WM_ICON_NAME", F(atom.net_wm_icon_name) },
+ { "_NET_WM_WINDOW_TYPE", F(atom.net_wm_window_type) },
+
+ { "_NET_WM_WINDOW_TYPE_DESKTOP", F(atom.net_wm_window_type_desktop) },
+ { "_NET_WM_WINDOW_TYPE_DOCK", F(atom.net_wm_window_type_dock) },
+ { "_NET_WM_WINDOW_TYPE_TOOLBAR", F(atom.net_wm_window_type_toolbar) },
+ { "_NET_WM_WINDOW_TYPE_MENU", F(atom.net_wm_window_type_menu) },
+ { "_NET_WM_WINDOW_TYPE_UTILITY", F(atom.net_wm_window_type_utility) },
+ { "_NET_WM_WINDOW_TYPE_SPLASH", F(atom.net_wm_window_type_splash) },
+ { "_NET_WM_WINDOW_TYPE_DIALOG", F(atom.net_wm_window_type_dialog) },
+ { "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU", F(atom.net_wm_window_type_dropdown) },
+ { "_NET_WM_WINDOW_TYPE_POPUP_MENU", F(atom.net_wm_window_type_popup) },
+ { "_NET_WM_WINDOW_TYPE_TOOLTIP", F(atom.net_wm_window_type_tooltip) },
+ { "_NET_WM_WINDOW_TYPE_NOTIFICATION", F(atom.net_wm_window_type_notification) },
+ { "_NET_WM_WINDOW_TYPE_COMBO", F(atom.net_wm_window_type_combo) },
+ { "_NET_WM_WINDOW_TYPE_DND", F(atom.net_wm_window_type_dnd) },
+ { "_NET_WM_WINDOW_TYPE_NORMAL", F(atom.net_wm_window_type_normal) },
+
+ { "_NET_WM_MOVERESIZE", F(atom.net_wm_moveresize) },
+ { "_NET_SUPPORTING_WM_CHECK",
+ F(atom.net_supporting_wm_check) },
+ { "_NET_SUPPORTED", F(atom.net_supported) },
+ { "_MOTIF_WM_HINTS", F(atom.motif_wm_hints) },
+ { "CLIPBOARD", F(atom.clipboard) },
+ { "CLIPBOARD_MANAGER", F(atom.clipboard_manager) },
+ { "TARGETS", F(atom.targets) },
+ { "UTF8_STRING", F(atom.utf8_string) },
+ { "_WL_SELECTION", F(atom.wl_selection) },
+ { "INCR", F(atom.incr) },
+ { "TIMESTAMP", F(atom.timestamp) },
+ { "MULTIPLE", F(atom.multiple) },
+ { "UTF8_STRING" , F(atom.utf8_string) },
+ { "COMPOUND_TEXT", F(atom.compound_text) },
+ { "TEXT", F(atom.text) },
+ { "STRING", F(atom.string) },
+ { "text/plain;charset=utf-8", F(atom.text_plain_utf8) },
+ { "text/plain", F(atom.text_plain) },
+ { "XdndSelection", F(atom.xdnd_selection) },
+ { "XdndAware", F(atom.xdnd_aware) },
+ { "XdndEnter", F(atom.xdnd_enter) },
+ { "XdndLeave", F(atom.xdnd_leave) },
+ { "XdndDrop", F(atom.xdnd_drop) },
+ { "XdndStatus", F(atom.xdnd_status) },
+ { "XdndFinished", F(atom.xdnd_finished) },
+ { "XdndTypeList", F(atom.xdnd_type_list) },
+ { "XdndActionCopy", F(atom.xdnd_action_copy) }
+ };
+#undef F
+
+ xcb_xfixes_query_version_cookie_t xfixes_cookie;
+ xcb_xfixes_query_version_reply_t *xfixes_reply;
+ xcb_intern_atom_cookie_t cookies[ARRAY_LENGTH(atoms)];
+ xcb_intern_atom_reply_t *reply;
+ xcb_render_query_pict_formats_reply_t *formats_reply;
+ xcb_render_query_pict_formats_cookie_t formats_cookie;
+ xcb_render_pictforminfo_t *formats;
+ uint32_t i;
+
+ xcb_prefetch_extension_data (wm->conn, &xcb_xfixes_id);
+ xcb_prefetch_extension_data (wm->conn, &xcb_composite_id);
+
+ formats_cookie = xcb_render_query_pict_formats(wm->conn);
+
+ for (i = 0; i < ARRAY_LENGTH(atoms); i++)
+ cookies[i] = xcb_intern_atom (wm->conn, 0,
+ strlen(atoms[i].name),
+ atoms[i].name);
+
+ for (i = 0; i < ARRAY_LENGTH(atoms); i++) {
+ reply = xcb_intern_atom_reply (wm->conn, cookies[i], NULL);
+ *(xcb_atom_t *) ((char *) wm + atoms[i].offset) = reply->atom;
+ free(reply);
+ }
+
+ wm->xfixes = xcb_get_extension_data(wm->conn, &xcb_xfixes_id);
+ if (!wm->xfixes || !wm->xfixes->present)
+ weston_log("xfixes not available\n");
+
+ xfixes_cookie = xcb_xfixes_query_version(wm->conn,
+ XCB_XFIXES_MAJOR_VERSION,
+ XCB_XFIXES_MINOR_VERSION);
+ xfixes_reply = xcb_xfixes_query_version_reply(wm->conn,
+ xfixes_cookie, NULL);
+
+ weston_log("xfixes version: %d.%d\n",
+ xfixes_reply->major_version, xfixes_reply->minor_version);
+
+ free(xfixes_reply);
+
+ formats_reply = xcb_render_query_pict_formats_reply(wm->conn,
+ formats_cookie, 0);
+ if (formats_reply == NULL)
+ return;
+
+ formats = xcb_render_query_pict_formats_formats(formats_reply);
+ for (i = 0; i < formats_reply->num_formats; i++) {
+ if (formats[i].direct.red_mask != 0xff &&
+ formats[i].direct.red_shift != 16)
+ continue;
+ if (formats[i].type == XCB_RENDER_PICT_TYPE_DIRECT &&
+ formats[i].depth == 24)
+ wm->format_rgb = formats[i];
+ if (formats[i].type == XCB_RENDER_PICT_TYPE_DIRECT &&
+ formats[i].depth == 32 &&
+ formats[i].direct.alpha_mask == 0xff &&
+ formats[i].direct.alpha_shift == 24)
+ wm->format_rgba = formats[i];
+ }
+
+ free(formats_reply);
+}
+
+static void
+weston_wm_create_wm_window(struct weston_wm *wm)
+{
+ static const char name[] = "Weston WM";
+
+ wm->wm_window = xcb_generate_id(wm->conn);
+ xcb_create_window(wm->conn,
+ XCB_COPY_FROM_PARENT,
+ wm->wm_window,
+ wm->screen->root,
+ 0, 0,
+ 10, 10,
+ 0,
+ XCB_WINDOW_CLASS_INPUT_OUTPUT,
+ wm->screen->root_visual,
+ 0, NULL);
+
+ xcb_change_property(wm->conn,
+ XCB_PROP_MODE_REPLACE,
+ wm->wm_window,
+ wm->atom.net_supporting_wm_check,
+ XCB_ATOM_WINDOW,
+ 32, /* format */
+ 1, &wm->wm_window);
+
+ xcb_change_property(wm->conn,
+ XCB_PROP_MODE_REPLACE,
+ wm->wm_window,
+ wm->atom.net_wm_name,
+ wm->atom.utf8_string,
+ 8, /* format */
+ strlen(name), name);
+
+ xcb_change_property(wm->conn,
+ XCB_PROP_MODE_REPLACE,
+ wm->screen->root,
+ wm->atom.net_supporting_wm_check,
+ XCB_ATOM_WINDOW,
+ 32, /* format */
+ 1, &wm->wm_window);
+
+ /* Claim the WM_S0 selection even though we don't suport
+ * the --replace functionality. */
+ xcb_set_selection_owner(wm->conn,
+ wm->wm_window,
+ wm->atom.wm_s0,
+ XCB_TIME_CURRENT_TIME);
+
+ xcb_set_selection_owner(wm->conn,
+ wm->wm_window,
+ wm->atom.net_wm_cm_s0,
+ XCB_TIME_CURRENT_TIME);
+}
+
+struct weston_wm *
+weston_wm_create(struct weston_xserver *wxs)
+{
+ struct weston_wm *wm;
+ struct wl_event_loop *loop;
+ xcb_screen_iterator_t s;
+ uint32_t values[1];
+ int sv[2];
+ xcb_atom_t supported[3];
+
+ wm = zalloc(sizeof *wm);
+ if (wm == NULL)
+ return NULL;
+
+ wm->server = wxs;
+ wm->window_hash = hash_table_create();
+ if (wm->window_hash == NULL) {
+ free(wm);
+ return NULL;
+ }
+
+ if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) {
+ weston_log("socketpair failed\n");
+ hash_table_destroy(wm->window_hash);
+ free(wm);
+ return NULL;
+ }
+
+ xserver_send_client(wxs->resource, sv[1]);
+ wl_client_flush(wl_resource_get_client(wxs->resource));
+ close(sv[1]);
+
+ /* xcb_connect_to_fd takes ownership of the fd. */
+ wm->conn = xcb_connect_to_fd(sv[0], NULL);
+ if (xcb_connection_has_error(wm->conn)) {
+ weston_log("xcb_connect_to_fd failed\n");
+ close(sv[0]);
+ hash_table_destroy(wm->window_hash);
+ free(wm);
+ return NULL;
+ }
+
+ s = xcb_setup_roots_iterator(xcb_get_setup(wm->conn));
+ wm->screen = s.data;
+
+ loop = wl_display_get_event_loop(wxs->wl_display);
+ wm->source =
+ wl_event_loop_add_fd(loop, sv[0],
+ WL_EVENT_READABLE,
+ weston_wm_handle_event, wm);
+ wl_event_source_check(wm->source);
+
+ weston_wm_get_resources(wm);
+ weston_wm_get_visual_and_colormap(wm);
+
+ values[0] =
+ XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
+ XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT |
+ XCB_EVENT_MASK_PROPERTY_CHANGE;
+ xcb_change_window_attributes(wm->conn, wm->screen->root,
+ XCB_CW_EVENT_MASK, values);
+
+ xcb_composite_redirect_subwindows(wm->conn, wm->screen->root,
+ XCB_COMPOSITE_REDIRECT_MANUAL);
+
+ wm->theme = theme_create();
+
+ weston_wm_create_wm_window(wm);
+
+ supported[0] = wm->atom.net_wm_moveresize;
+ supported[1] = wm->atom.net_wm_state;
+ supported[2] = wm->atom.net_wm_state_fullscreen;
+ xcb_change_property(wm->conn,
+ XCB_PROP_MODE_REPLACE,
+ wm->screen->root,
+ wm->atom.net_supported,
+ XCB_ATOM_ATOM,
+ 32, /* format */
+ ARRAY_LENGTH(supported), supported);
+
+ weston_wm_selection_init(wm);
+
+ weston_wm_dnd_init(wm);
+
+ xcb_flush(wm->conn);
+
+ wm->activate_listener.notify = weston_wm_window_activate;
+ wl_signal_add(&wxs->compositor->activate_signal,
+ &wm->activate_listener);
+ wm->transform_listener.notify = weston_wm_window_transform;
+ wl_signal_add(&wxs->compositor->transform_signal,
+ &wm->transform_listener);
+ wm->kill_listener.notify = weston_wm_kill_client;
+ wl_signal_add(&wxs->compositor->kill_signal,
+ &wm->kill_listener);
+
+ weston_wm_create_cursors(wm);
+ weston_wm_window_set_cursor(wm, wm->screen->root, XWM_CURSOR_LEFT_PTR);
+
+ weston_log("created wm\n");
+
+ return wm;
+}
+
+void
+weston_wm_destroy(struct weston_wm *wm)
+{
+ /* FIXME: Free windows in hash. */
+ hash_table_destroy(wm->window_hash);
+ weston_wm_destroy_cursors(wm);
+ xcb_disconnect(wm->conn);
+ wl_event_source_remove(wm->source);
+ wl_list_remove(&wm->selection_listener.link);
+ wl_list_remove(&wm->activate_listener.link);
+ wl_list_remove(&wm->kill_listener.link);
+ wl_list_remove(&wm->transform_listener.link);
+
+ free(wm);
+}
+
+static void
+surface_destroy(struct wl_listener *listener, void *data)
+{
+ struct weston_wm_window *window =
+ container_of(listener,
+ struct weston_wm_window, surface_destroy_listener);
+
+ wm_log("surface for xid %d destroyed\n", window->id);
+
+ /* This should have been freed by the shell.
+ Don't try to use it later. */
+ window->shsurf = NULL;
+ window->surface = NULL;
+}
+
+static struct weston_wm_window *
+get_wm_window(struct weston_surface *surface)
+{
+ struct wl_listener *listener;
+
+ listener = wl_signal_get(&surface->destroy_signal, surface_destroy);
+ if (listener)
+ return container_of(listener, struct weston_wm_window,
+ surface_destroy_listener);
+
+ return NULL;
+}
+
+static void
+weston_wm_window_configure(void *data)
+{
+ struct weston_wm_window *window = data;
+ struct weston_wm *wm = window->wm;
+ uint32_t values[4];
+ int x, y, width, height;
+
+ weston_wm_window_get_child_position(window, &x, &y);
+ values[0] = x;
+ values[1] = y;
+ values[2] = window->width;
+ values[3] = window->height;
+ xcb_configure_window(wm->conn,
+ window->id,
+ XCB_CONFIG_WINDOW_X |
+ XCB_CONFIG_WINDOW_Y |
+ XCB_CONFIG_WINDOW_WIDTH |
+ XCB_CONFIG_WINDOW_HEIGHT,
+ values);
+
+ weston_wm_window_get_frame_size(window, &width, &height);
+ values[0] = width;
+ values[1] = height;
+ xcb_configure_window(wm->conn,
+ window->frame_id,
+ XCB_CONFIG_WINDOW_WIDTH |
+ XCB_CONFIG_WINDOW_HEIGHT,
+ values);
+
+ window->configure_source = NULL;
+
+ weston_wm_window_schedule_repaint(window);
+}
+
+static void
+send_configure(struct weston_surface *surface,
+ uint32_t edges, int32_t width, int32_t height)
+{
+ struct weston_wm_window *window = get_wm_window(surface);
+ struct weston_wm *wm = window->wm;
+ struct theme *t = window->wm->theme;
+ int vborder, hborder;
+
+ if (window->fullscreen) {
+ hborder = 0;
+ vborder = 0;
+ } else if (window->decorate) {
+ hborder = 2 * (t->margin + t->width);
+ vborder = 2 * t->margin + t->titlebar_height + t->width;
+ } else {
+ hborder = 2 * t->margin;
+ vborder = 2 * t->margin;
+ }
+
+ if (width > hborder)
+ window->width = width - hborder;
+ else
+ window->width = 1;
+
+ if (height > vborder)
+ window->height = height - vborder;
+ else
+ window->height = 1;
+
+ if (window->configure_source)
+ return;
+
+ window->configure_source =
+ wl_event_loop_add_idle(wm->server->loop,
+ weston_wm_window_configure, window);
+}
+
+static const struct weston_shell_client shell_client = {
+ send_configure
+};
+
+static int
+legacy_fullscreen(struct weston_wm *wm,
+ struct weston_wm_window *window,
+ struct weston_output **output_ret)
+{
+ struct weston_compositor *compositor = wm->server->compositor;
+ struct weston_output *output;
+ uint32_t minmax = PMinSize | PMaxSize;
+ int matching_size;
+
+ /* Heuristics for detecting legacy fullscreen windows... */
+
+ wl_list_for_each(output, &compositor->output_list, link) {
+ if (output->x == window->x &&
+ output->y == window->y &&
+ output->width == window->width &&
+ output->height == window->height &&
+ window->override_redirect) {
+ *output_ret = output;
+ return 1;
+ }
+
+ matching_size = 0;
+ if ((window->size_hints.flags & (USSize |PSize)) &&
+ window->size_hints.width == output->width &&
+ window->size_hints.height == output->height)
+ matching_size = 1;
+ if ((window->size_hints.flags & minmax) == minmax &&
+ window->size_hints.min_width == output->width &&
+ window->size_hints.min_height == output->height &&
+ window->size_hints.max_width == output->width &&
+ window->size_hints.max_height == output->height)
+ matching_size = 1;
+
+ if (matching_size && !window->decorate &&
+ (window->size_hints.flags & (USPosition | PPosition)) &&
+ window->size_hints.x == output->x &&
+ window->size_hints.y == output->y) {
+ *output_ret = output;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+static void
+xserver_map_shell_surface(struct weston_wm *wm,
+ struct weston_wm_window *window)
+{
+ struct weston_shell_interface *shell_interface =
+ &wm->server->compositor->shell_interface;
+ struct weston_output *output;
+
+ if (!shell_interface->create_shell_surface)
+ return;
+
+ window->shsurf =
+ shell_interface->create_shell_surface(shell_interface->shell,
+ window->surface,
+ &shell_client);
+
+ if (window->name)
+ shell_interface->set_title(window->shsurf, window->name);
+
+ if (window->fullscreen) {
+ window->saved_width = window->width;
+ window->saved_height = window->height;
+ shell_interface->set_fullscreen(window->shsurf,
+ WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT,
+ 0, NULL);
+ return;
+ } else if (legacy_fullscreen(wm, window, &output)) {
+ window->fullscreen = 1;
+ shell_interface->set_fullscreen(window->shsurf,
+ WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT,
+ 0, output);
+ } else if (!window->override_redirect && !window->transient_for) {
+ shell_interface->set_toplevel(window->shsurf);
+ return;
+ } else {
+ shell_interface->set_xwayland(window->shsurf,
+ window->x,
+ window->y,
+ WL_SHELL_SURFACE_TRANSIENT_INACTIVE);
+ }
+}
+
+static void
+xserver_set_window_id(struct wl_client *client, struct wl_resource *resource,
+ struct wl_resource *surface_resource, uint32_t id)
+{
+ struct weston_xserver *wxs = wl_resource_get_user_data(resource);
+ struct weston_wm *wm = wxs->wm;
+ struct weston_surface *surface =
+ wl_resource_get_user_data(surface_resource);
+ struct weston_wm_window *window;
+
+ if (client != wxs->client)
+ return;
+
+ window = hash_table_lookup(wm->window_hash, id);
+ if (window == NULL) {
+ weston_log("set_window_id for unknown window %d\n", id);
+ return;
+ }
+
+ wm_log("set_window_id %d for surface %p\n", id, surface);
+
+ weston_wm_window_read_properties(window);
+
+ /* A weston_wm_window may have many different surfaces assigned
+ * throughout its life, so we must make sure to remove the listener
+ * from the old surface signal list. */
+ if (window->surface)
+ wl_list_remove(&window->surface_destroy_listener.link);
+
+ window->surface = (struct weston_surface *) surface;
+ window->surface_destroy_listener.notify = surface_destroy;
+ wl_signal_add(&surface->destroy_signal,
+ &window->surface_destroy_listener);
+
+ weston_wm_window_schedule_repaint(window);
+ xserver_map_shell_surface(wm, window);
+}
+
+const struct xserver_interface xserver_implementation = {
+ xserver_set_window_id
+};
diff --git a/src/xwayland/xwayland.h b/src/xwayland/xwayland.h
new file mode 100644
index 00000000..77262e8f
--- /dev/null
+++ b/src/xwayland/xwayland.h
@@ -0,0 +1,175 @@
+/*
+ * Copyright © 2012 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 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.
+ */
+
+#include <wayland-server.h>
+#include <xcb/xcb.h>
+#include <xcb/xfixes.h>
+#include <xcb/composite.h>
+#include <cairo/cairo-xcb.h>
+
+#include "../compositor.h"
+
+#define SEND_EVENT_MASK (0x80)
+#define EVENT_TYPE(event) ((event)->response_type & ~SEND_EVENT_MASK)
+
+struct weston_xserver {
+ struct wl_display *wl_display;
+ struct wl_event_loop *loop;
+ struct wl_event_source *sigchld_source;
+ int abstract_fd;
+ struct wl_event_source *abstract_source;
+ int unix_fd;
+ struct wl_event_source *unix_source;
+ int display;
+ struct weston_process process;
+ struct wl_resource *resource;
+ struct wl_client *client;
+ struct weston_compositor *compositor;
+ struct weston_wm *wm;
+ struct wl_listener destroy_listener;
+};
+
+struct weston_wm {
+ xcb_connection_t *conn;
+ const xcb_query_extension_reply_t *xfixes;
+ struct wl_event_source *source;
+ xcb_screen_t *screen;
+ struct hash_table *window_hash;
+ struct weston_xserver *server;
+ xcb_window_t wm_window;
+ struct weston_wm_window *focus_window;
+ struct theme *theme;
+ xcb_cursor_t *cursors;
+ int last_cursor;
+ xcb_render_pictforminfo_t format_rgb, format_rgba;
+ xcb_visualid_t visual_id;
+ xcb_colormap_t colormap;
+ struct wl_listener activate_listener;
+ struct wl_listener transform_listener;
+ struct wl_listener kill_listener;
+
+ xcb_window_t selection_window;
+ xcb_window_t selection_owner;
+ int incr;
+ int data_source_fd;
+ struct wl_event_source *property_source;
+ xcb_get_property_reply_t *property_reply;
+ int property_start;
+ struct wl_array source_data;
+ xcb_selection_request_event_t selection_request;
+ xcb_atom_t selection_target;
+ xcb_timestamp_t selection_timestamp;
+ int selection_property_set;
+ int flush_property_on_delete;
+ struct wl_listener selection_listener;
+
+ xcb_window_t dnd_window;
+ xcb_window_t dnd_owner;
+
+ struct {
+ xcb_atom_t wm_protocols;
+ xcb_atom_t wm_normal_hints;
+ xcb_atom_t wm_take_focus;
+ xcb_atom_t wm_delete_window;
+ xcb_atom_t wm_state;
+ xcb_atom_t wm_s0;
+ xcb_atom_t wm_client_machine;
+ xcb_atom_t net_wm_cm_s0;
+ xcb_atom_t net_wm_name;
+ xcb_atom_t net_wm_pid;
+ xcb_atom_t net_wm_icon;
+ xcb_atom_t net_wm_state;
+ xcb_atom_t net_wm_state_fullscreen;
+ xcb_atom_t net_wm_user_time;
+ xcb_atom_t net_wm_icon_name;
+ xcb_atom_t net_wm_window_type;
+ xcb_atom_t net_wm_window_type_desktop;
+ xcb_atom_t net_wm_window_type_dock;
+ xcb_atom_t net_wm_window_type_toolbar;
+ xcb_atom_t net_wm_window_type_menu;
+ xcb_atom_t net_wm_window_type_utility;
+ xcb_atom_t net_wm_window_type_splash;
+ xcb_atom_t net_wm_window_type_dialog;
+ xcb_atom_t net_wm_window_type_dropdown;
+ xcb_atom_t net_wm_window_type_popup;
+ xcb_atom_t net_wm_window_type_tooltip;
+ xcb_atom_t net_wm_window_type_notification;
+ xcb_atom_t net_wm_window_type_combo;
+ xcb_atom_t net_wm_window_type_dnd;
+ xcb_atom_t net_wm_window_type_normal;
+ xcb_atom_t net_wm_moveresize;
+ xcb_atom_t net_supporting_wm_check;
+ xcb_atom_t net_supported;
+ xcb_atom_t motif_wm_hints;
+ xcb_atom_t clipboard;
+ xcb_atom_t clipboard_manager;
+ xcb_atom_t targets;
+ xcb_atom_t utf8_string;
+ xcb_atom_t wl_selection;
+ xcb_atom_t incr;
+ xcb_atom_t timestamp;
+ xcb_atom_t multiple;
+ xcb_atom_t compound_text;
+ xcb_atom_t text;
+ xcb_atom_t string;
+ xcb_atom_t text_plain_utf8;
+ xcb_atom_t text_plain;
+ xcb_atom_t xdnd_selection;
+ xcb_atom_t xdnd_aware;
+ xcb_atom_t xdnd_enter;
+ xcb_atom_t xdnd_leave;
+ xcb_atom_t xdnd_drop;
+ xcb_atom_t xdnd_status;
+ xcb_atom_t xdnd_finished;
+ xcb_atom_t xdnd_type_list;
+ xcb_atom_t xdnd_action_copy;
+ } atom;
+};
+
+void
+dump_property(struct weston_wm *wm, xcb_atom_t property,
+ xcb_get_property_reply_t *reply);
+
+const char *
+get_atom_name(xcb_connection_t *c, xcb_atom_t atom);
+
+void
+weston_wm_selection_init(struct weston_wm *wm);
+int
+weston_wm_handle_selection_event(struct weston_wm *wm,
+ xcb_generic_event_t *event);
+
+extern const struct xserver_interface xserver_implementation;
+
+struct weston_wm *
+weston_wm_create(struct weston_xserver *wxs);
+void
+weston_wm_destroy(struct weston_wm *wm);
+
+struct weston_seat *
+weston_wm_pick_seat(struct weston_wm *wm);
+
+int
+weston_wm_handle_dnd_event(struct weston_wm *wm,
+ xcb_generic_event_t *event);
+void
+weston_wm_dnd_init(struct weston_wm *wm);
diff --git a/src/zoom.c b/src/zoom.c
new file mode 100644
index 00000000..220b2b6e
--- /dev/null
+++ b/src/zoom.c
@@ -0,0 +1,397 @@
+/*
+ * Copyright © 2012 Scott Moreau
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that 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.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include "compositor.h"
+#include "text-cursor-position-server-protocol.h"
+
+struct text_cursor_position {
+ struct weston_compositor *ec;
+ struct wl_global *global;
+ struct wl_listener destroy_listener;
+};
+
+static void
+text_cursor_position_notify(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *surface_resource,
+ wl_fixed_t x, wl_fixed_t y)
+{
+ struct weston_surface *surface =
+ wl_resource_get_user_data(surface_resource);
+
+ weston_text_cursor_position_notify(surface, x, y);
+}
+
+struct text_cursor_position_interface text_cursor_position_implementation = {
+ text_cursor_position_notify
+};
+
+static void
+bind_text_cursor_position(struct wl_client *client,
+ void *data, uint32_t version, uint32_t id)
+{
+ struct wl_resource *resource;
+
+ resource = wl_resource_create(client,
+ &text_cursor_position_interface, 1, id);
+ if (resource)
+ wl_resource_set_implementation(resource,
+ &text_cursor_position_implementation,
+ data, NULL);
+}
+
+static void
+text_cursor_position_notifier_destroy(struct wl_listener *listener, void *data)
+{
+ struct text_cursor_position *text_cursor_position =
+ container_of(listener, struct text_cursor_position, destroy_listener);
+
+ wl_global_destroy(text_cursor_position->global);
+ free(text_cursor_position);
+}
+
+void
+text_cursor_position_notifier_create(struct weston_compositor *ec)
+{
+ struct text_cursor_position *text_cursor_position;
+
+ text_cursor_position = malloc(sizeof *text_cursor_position);
+ if (text_cursor_position == NULL)
+ return;
+
+ text_cursor_position->ec = ec;
+
+ text_cursor_position->global =
+ wl_global_create(ec->wl_display,
+ &text_cursor_position_interface, 1,
+ text_cursor_position,
+ bind_text_cursor_position);
+
+ text_cursor_position->destroy_listener.notify =
+ text_cursor_position_notifier_destroy;
+ wl_signal_add(&ec->destroy_signal, &text_cursor_position->destroy_listener);
+}
+
+WL_EXPORT void
+weston_text_cursor_position_notify(struct weston_surface *surface,
+ wl_fixed_t cur_pos_x,
+ wl_fixed_t cur_pos_y)
+{
+ struct weston_output *output;
+ wl_fixed_t global_x, global_y;
+
+ weston_surface_to_global_fixed(surface, cur_pos_x, cur_pos_y,
+ &global_x, &global_y);
+
+ wl_list_for_each(output, &surface->compositor->output_list, link)
+ if (output->zoom.active &&
+ pixman_region32_contains_point(&output->region,
+ wl_fixed_to_int(global_x),
+ wl_fixed_to_int(global_y),
+ NULL)) {
+ output->zoom.text_cursor.x = global_x;
+ output->zoom.text_cursor.y = global_y;
+ weston_output_update_zoom(output, ZOOM_FOCUS_TEXT);
+ }
+}
+
+static void
+weston_zoom_frame_z(struct weston_animation *animation,
+ struct weston_output *output, uint32_t msecs)
+{
+ if (animation->frame_counter <= 1)
+ output->zoom.spring_z.timestamp = msecs;
+
+ weston_spring_update(&output->zoom.spring_z, msecs);
+
+ if (output->zoom.spring_z.current > output->zoom.max_level)
+ output->zoom.spring_z.current = output->zoom.max_level;
+ else if (output->zoom.spring_z.current < 0.0)
+ output->zoom.spring_z.current = 0.0;
+
+ if (weston_spring_done(&output->zoom.spring_z)) {
+ if (output->zoom.active && output->zoom.level <= 0.0) {
+ output->zoom.active = 0;
+ output->disable_planes--;
+ }
+ output->zoom.spring_z.current = output->zoom.level;
+ wl_list_remove(&animation->link);
+ wl_list_init(&animation->link);
+ }
+
+ output->dirty = 1;
+ weston_output_damage(output);
+}
+
+static struct weston_seat *
+weston_zoom_pick_seat(struct weston_compositor *compositor)
+{
+ return container_of(compositor->seat_list.next,
+ struct weston_seat, link);
+}
+
+
+static void
+weston_zoom_frame_xy(struct weston_animation *animation,
+ struct weston_output *output, uint32_t msecs)
+{
+ struct weston_seat *seat = weston_zoom_pick_seat(output->compositor);
+ wl_fixed_t x, y;
+
+ if (animation->frame_counter <= 1)
+ output->zoom.spring_xy.timestamp = msecs;
+
+ weston_spring_update(&output->zoom.spring_xy, msecs);
+
+ x = output->zoom.from.x - ((output->zoom.from.x - output->zoom.to.x) *
+ output->zoom.spring_xy.current);
+ y = output->zoom.from.y - ((output->zoom.from.y - output->zoom.to.y) *
+ output->zoom.spring_xy.current);
+
+ output->zoom.current.x = x;
+ output->zoom.current.y = y;
+
+ if (weston_spring_done(&output->zoom.spring_xy)) {
+ output->zoom.spring_xy.current = output->zoom.spring_xy.target;
+ output->zoom.current.x =
+ output->zoom.type == ZOOM_FOCUS_POINTER ?
+ seat->pointer->x : output->zoom.text_cursor.x;
+ output->zoom.current.y =
+ output->zoom.type == ZOOM_FOCUS_POINTER ?
+ seat->pointer->y : output->zoom.text_cursor.y;
+ wl_list_remove(&animation->link);
+ wl_list_init(&animation->link);
+ }
+
+ output->dirty = 1;
+ weston_output_damage(output);
+}
+
+static void
+zoom_area_center_from_pointer(struct weston_output *output,
+ wl_fixed_t *x, wl_fixed_t *y)
+{
+ float level = output->zoom.spring_z.current;
+ wl_fixed_t offset_x = wl_fixed_from_int(output->x);
+ wl_fixed_t offset_y = wl_fixed_from_int(output->y);
+ wl_fixed_t w = wl_fixed_from_int(output->width);
+ wl_fixed_t h = wl_fixed_from_int(output->height);
+
+ *x -= ((((*x - offset_x) / (float) w) - 0.5) * (w * (1.0 - level)));
+ *y -= ((((*y - offset_y) / (float) h) - 0.5) * (h * (1.0 - level)));
+}
+
+static void
+weston_zoom_apply_output_transform(struct weston_output *output,
+ float *x, float *y)
+{
+ float tx, ty;
+
+ switch(output->transform) {
+ case WL_OUTPUT_TRANSFORM_NORMAL:
+ default:
+ return;
+ case WL_OUTPUT_TRANSFORM_90:
+ tx = -*y;
+ ty = *x;
+ break;
+ case WL_OUTPUT_TRANSFORM_180:
+ tx = -*x;
+ ty = -*y;
+ break;
+ case WL_OUTPUT_TRANSFORM_270:
+ tx = *y;
+ ty = -*x;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED:
+ tx = -*x;
+ ty = *y;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+ tx = -*y;
+ ty = -*x;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+ tx = *x;
+ ty = -*y;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+ tx = *y;
+ ty = *x;
+ break;
+ }
+
+ *x = tx;
+ *y = ty;
+}
+
+static void
+weston_output_update_zoom_transform(struct weston_output *output)
+{
+ uint32_t type = output->zoom.type;
+ float global_x, global_y;
+ wl_fixed_t x = output->zoom.current.x;
+ wl_fixed_t y = output->zoom.current.y;
+ float trans_min, trans_max;
+ float ratio, level;
+
+ level = output->zoom.spring_z.current;
+ ratio = 1 / level;
+
+ if (!output->zoom.active || level > output->zoom.max_level ||
+ level == 0.0f)
+ return;
+
+ if (type == ZOOM_FOCUS_POINTER &&
+ wl_list_empty(&output->zoom.animation_xy.link))
+ zoom_area_center_from_pointer(output, &x, &y);
+
+ global_x = wl_fixed_to_double(x);
+ global_y = wl_fixed_to_double(y);
+
+ output->zoom.trans_x =
+ ((((global_x - output->x) / output->width) *
+ (level * 2)) - level) * ratio;
+ output->zoom.trans_y =
+ ((((global_y - output->y) / output->height) *
+ (level * 2)) - level) * ratio;
+
+ weston_zoom_apply_output_transform(output, &output->zoom.trans_x,
+ &output->zoom.trans_y);
+
+ trans_max = level * 2 - level;
+ trans_min = -trans_max;
+
+ /* Clip zoom area to output */
+ if (output->zoom.trans_x > trans_max)
+ output->zoom.trans_x = trans_max;
+ else if (output->zoom.trans_x < trans_min)
+ output->zoom.trans_x = trans_min;
+ if (output->zoom.trans_y > trans_max)
+ output->zoom.trans_y = trans_max;
+ else if (output->zoom.trans_y < trans_min)
+ output->zoom.trans_y = trans_min;
+}
+
+static void
+weston_zoom_transition(struct weston_output *output, uint32_t type,
+ wl_fixed_t x, wl_fixed_t y)
+{
+ if (output->zoom.type != type) {
+ /* Set from/to points and start animation */
+ output->zoom.spring_xy.current = 0.0;
+ output->zoom.spring_xy.previous = 0.0;
+ output->zoom.spring_xy.target = 1.0;
+
+ if (wl_list_empty(&output->zoom.animation_xy.link)) {
+ output->zoom.animation_xy.frame_counter = 0;
+ wl_list_insert(output->animation_list.prev,
+ &output->zoom.animation_xy.link);
+
+ output->zoom.from.x = (type == ZOOM_FOCUS_TEXT) ?
+ x : output->zoom.text_cursor.x;
+ output->zoom.from.y = (type == ZOOM_FOCUS_TEXT) ?
+ y : output->zoom.text_cursor.y;
+ } else {
+ output->zoom.from.x = output->zoom.current.x;
+ output->zoom.from.y = output->zoom.current.y;
+ }
+
+ output->zoom.to.x = (type == ZOOM_FOCUS_POINTER) ?
+ x : output->zoom.text_cursor.x;
+ output->zoom.to.y = (type == ZOOM_FOCUS_POINTER) ?
+ y : output->zoom.text_cursor.y;
+ output->zoom.current.x = output->zoom.from.x;
+ output->zoom.current.y = output->zoom.from.y;
+
+ output->zoom.type = type;
+ }
+
+ if (output->zoom.level != output->zoom.spring_z.current) {
+ output->zoom.spring_z.target = output->zoom.level;
+ if (wl_list_empty(&output->zoom.animation_z.link)) {
+ output->zoom.animation_z.frame_counter = 0;
+ wl_list_insert(output->animation_list.prev,
+ &output->zoom.animation_z.link);
+ }
+ }
+
+ output->dirty = 1;
+ weston_output_damage(output);
+}
+
+WL_EXPORT void
+weston_output_update_zoom(struct weston_output *output, uint32_t type)
+{
+ struct weston_seat *seat = weston_zoom_pick_seat(output->compositor);
+ wl_fixed_t x = seat->pointer->x;
+ wl_fixed_t y = seat->pointer->y;
+
+ zoom_area_center_from_pointer(output, &x, &y);
+
+ if (type == ZOOM_FOCUS_POINTER) {
+ if (wl_list_empty(&output->zoom.animation_xy.link)) {
+ output->zoom.current.x = seat->pointer->x;
+ output->zoom.current.y = seat->pointer->y;
+ } else {
+ output->zoom.to.x = x;
+ output->zoom.to.y = y;
+ }
+ }
+
+ if (type == ZOOM_FOCUS_TEXT) {
+ if (wl_list_empty(&output->zoom.animation_xy.link)) {
+ output->zoom.current.x = output->zoom.text_cursor.x;
+ output->zoom.current.y = output->zoom.text_cursor.y;
+ } else {
+ output->zoom.to.x = output->zoom.text_cursor.x;
+ output->zoom.to.y = output->zoom.text_cursor.y;
+ }
+ }
+
+ weston_zoom_transition(output, type, x, y);
+ weston_output_update_zoom_transform(output);
+}
+
+WL_EXPORT void
+weston_output_init_zoom(struct weston_output *output)
+{
+ output->zoom.active = 0;
+ output->zoom.increment = 0.07;
+ output->zoom.max_level = 0.95;
+ output->zoom.level = 0.0;
+ output->zoom.trans_x = 0.0;
+ output->zoom.trans_y = 0.0;
+ output->zoom.type = ZOOM_FOCUS_POINTER;
+ weston_spring_init(&output->zoom.spring_z, 250.0, 0.0, 0.0);
+ output->zoom.spring_z.friction = 1000;
+ output->zoom.animation_z.frame = weston_zoom_frame_z;
+ wl_list_init(&output->zoom.animation_z.link);
+ weston_spring_init(&output->zoom.spring_xy, 250.0, 0.0, 0.0);
+ output->zoom.spring_xy.friction = 1000;
+ output->zoom.animation_xy.frame = weston_zoom_frame_xy;
+ wl_list_init(&output->zoom.animation_xy.link);
+}
diff --git a/tests/.gitignore b/tests/.gitignore
new file mode 100644
index 00000000..aba378c3
--- /dev/null
+++ b/tests/.gitignore
@@ -0,0 +1,13 @@
+*.test
+*.trs
+*.weston
+logs
+matrix-test
+setbacklight
+test-client
+test-text-client
+wayland-test-client-protocol.h
+wayland-test-protocol.c
+wayland-test-server-protocol.h
+subsurface-client-protocol.h
+subsurface-protocol.c
diff --git a/tests/Makefile.am b/tests/Makefile.am
new file mode 100644
index 00000000..5be52c6e
--- /dev/null
+++ b/tests/Makefile.am
@@ -0,0 +1,158 @@
+TESTS = $(shared_tests) $(module_tests) $(weston_tests)
+
+shared_tests = \
+ config-parser.test \
+ vertex-clip.test
+
+module_tests = \
+ surface-test.la \
+ surface-global-test.la
+
+weston_test = weston-test.la
+
+weston_tests = \
+ keyboard.weston \
+ event.weston \
+ button.weston \
+ text.weston \
+ subsurface.weston \
+ $(xwayland_test)
+
+AM_TESTS_ENVIRONMENT = \
+ abs_builddir='$(abs_builddir)'; export abs_builddir;
+
+TEST_EXTENSIONS = .la .weston
+LA_LOG_COMPILER = $(srcdir)/weston-tests-env
+WESTON_LOG_COMPILER = $(srcdir)/weston-tests-env
+
+clean-local:
+ -rm -rf logs
+
+# To remove when automake 1.11 support is dropped
+export abs_builddir
+
+noinst_LTLIBRARIES = \
+ $(weston_test) \
+ $(module_tests)
+
+noinst_PROGRAMS = \
+ $(setbacklight) \
+ $(shared_tests) \
+ $(weston_tests) \
+ matrix-test
+
+AM_CFLAGS = $(GCC_CFLAGS)
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/src \
+ -I$(top_srcdir)/shared \
+ -I$(top_builddir)/src \
+ -DUNIT_TEST \
+ $(COMPOSITOR_CFLAGS)
+
+surface_global_test_la_SOURCES = surface-global-test.c
+surface_global_test_la_LDFLAGS = -module -avoid-version -rpath $(libdir)
+surface_test_la_SOURCES = surface-test.c
+surface_test_la_LDFLAGS = -module -avoid-version -rpath $(libdir)
+
+weston_test_la_LIBADD = $(COMPOSITOR_LIBS) \
+ ../shared/libshared.la
+weston_test_la_LDFLAGS = -module -avoid-version -rpath $(libdir)
+weston_test_la_CFLAGS = $(GCC_CFLAGS) $(COMPOSITOR_CFLAGS)
+weston_test_la_SOURCES = \
+ weston-test.c \
+ wayland-test-protocol.c \
+ wayland-test-server-protocol.h
+
+weston_test_runner_src = \
+ weston-test-runner.c \
+ weston-test-runner.h
+
+check_LTLIBRARIES = libshared-test.la
+
+libshared_test_la_SOURCES = \
+ $(weston_test_runner_src)
+libshared_test_la_CFLAGS = $(GCC_CFLAGS) $(COMPOSITOR_CFLAGS)
+
+config_parser_test_LDADD = \
+ ../shared/libshared.la \
+ libshared-test.la \
+ $(COMPOSITOR_LIBS)
+config_parser_test_SOURCES = \
+ config-parser-test.c
+vertex_clip_test_SOURCES = \
+ vertex-clip-test.c \
+ ../src/vertex-clipping.c \
+ ../src/vertex-clipping.h
+vertex_clip_test_LDADD = \
+ libshared-test.la \
+ -lm -lrt
+
+weston_test_client_src = \
+ weston-test-client-helper.c \
+ weston-test-client-helper.h \
+ wayland-test-protocol.c \
+ wayland-test-client-protocol.h \
+ subsurface-protocol.c \
+ subsurface-client-protocol.h
+weston_test_client_libs = \
+ $(SIMPLE_CLIENT_LIBS) \
+ ../shared/libshared.la \
+ libshared-test.la
+
+keyboard_weston_SOURCES = keyboard-test.c $(weston_test_client_src)
+keyboard_weston_LDADD = $(weston_test_client_libs)
+
+event_weston_SOURCES = event-test.c $(weston_test_client_src)
+event_weston_LDADD = $(weston_test_client_libs)
+
+button_weston_SOURCES = button-test.c $(weston_test_client_src)
+button_weston_LDADD = $(weston_test_client_libs)
+
+text_weston_SOURCES = \
+ text-test.c \
+ ../clients/text-protocol.c \
+ $(weston_test_client_src)
+text_weston_LDADD = $(weston_test_client_libs)
+
+subsurface_weston_SOURCES = subsurface-test.c $(weston_test_client_src)
+subsurface_weston_LDADD = $(weston_test_client_libs)
+
+xwayland_weston_SOURCES = xwayland-test.c $(weston_test_client_src)
+
+xwayland_weston_LDADD = $(weston_test_client_libs) $(XWAYLAND_TEST_LIBS)
+
+if ENABLE_XWAYLAND_TEST
+xwayland_test = xwayland.weston
+endif
+
+matrix_test_SOURCES = \
+ matrix-test.c \
+ $(top_srcdir)/shared/matrix.c \
+ $(top_srcdir)/shared/matrix.h
+matrix_test_LDADD = -lm -lrt
+
+setbacklight_SOURCES = \
+ setbacklight.c \
+ $(top_srcdir)/src/libbacklight.c \
+ $(top_srcdir)/src/libbacklight.h
+
+setbacklight_CFLAGS = $(AM_CFLAGS) $(SETBACKLIGHT_CFLAGS)
+setbacklight_LDADD = $(SETBACKLIGHT_LIBS)
+
+if BUILD_SETBACKLIGHT
+setbacklight = setbacklight
+endif
+
+EXTRA_DIST = weston-tests-env
+
+BUILT_SOURCES = \
+ subsurface-protocol.c \
+ subsurface-client-protocol.h \
+ wayland-test-protocol.c \
+ wayland-test-server-protocol.h \
+ wayland-test-client-protocol.h
+
+CLEANFILES = $(BUILT_SOURCES)
+
+wayland_protocoldir = $(top_srcdir)/protocol
+include $(top_srcdir)/wayland-scanner.mk
diff --git a/tests/button-test.c b/tests/button-test.c
new file mode 100644
index 00000000..dc02fd44
--- /dev/null
+++ b/tests/button-test.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright © 2012 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 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.
+ */
+
+#include <linux/input.h>
+#include "weston-test-client-helper.h"
+
+TEST(simple_button_test)
+{
+ struct client *client;
+ struct pointer *pointer;
+
+ client = client_create(100, 100, 100, 100);
+ assert(client);
+
+ pointer = client->input->pointer;
+
+ assert(pointer->button == 0);
+ assert(pointer->state == 0);
+
+ wl_test_move_pointer(client->test->wl_test, 150, 150);
+ client_roundtrip(client);
+ assert(pointer->x == 50);
+ assert(pointer->y == 50);
+
+ wl_test_send_button(client->test->wl_test, BTN_LEFT,
+ WL_POINTER_BUTTON_STATE_PRESSED);
+ client_roundtrip(client);
+ assert(pointer->button == BTN_LEFT);
+ assert(pointer->state == WL_POINTER_BUTTON_STATE_PRESSED);
+
+ wl_test_send_button(client->test->wl_test, BTN_LEFT,
+ WL_POINTER_BUTTON_STATE_RELEASED);
+ client_roundtrip(client);
+ assert(pointer->button == BTN_LEFT);
+ assert(pointer->state == WL_POINTER_BUTTON_STATE_RELEASED);
+}
diff --git a/tests/config-parser-test.c b/tests/config-parser-test.c
new file mode 100644
index 00000000..4b8fc7e7
--- /dev/null
+++ b/tests/config-parser-test.c
@@ -0,0 +1,203 @@
+/*
+ * Copyright © 2013 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
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "config-parser.h"
+
+static struct weston_config *
+run_test(const char *text)
+{
+ struct weston_config *config;
+ char file[] = "/tmp/weston-config-parser-test-XXXXXX";
+ int fd, len;
+
+ fd = mkstemp(file);
+ len = write(fd, text, strlen(text));
+ assert(len == (int) strlen(text));
+
+ config = weston_config_parse(file);
+ close(fd);
+ unlink(file);
+
+ return config;
+}
+
+static const char t0[] =
+ "# nothing in this file...\n";
+
+static const char t1[] =
+ "# comment line here...\n"
+ "\n"
+ "[foo]\n"
+ "a=b\n"
+ "name= Roy Batty \n"
+ "\n"
+ "\n"
+ "[bar]\n"
+ "# more comments\n"
+ "number=5252\n"
+ "flag=false\n"
+ "\n"
+ "[stuff]\n"
+ "flag= true \n"
+ "\n"
+ "[bucket]\n"
+ "color=blue \n"
+ "contents=live crabs\n"
+ "pinchy=true\n"
+ "\n"
+ "[bucket]\n"
+ "material=plastic \n"
+ "color=red\n"
+ "contents=sand\n";
+
+static const char *section_names[] = {
+ "foo", "bar", "stuff", "bucket", "bucket"
+};
+
+static const char t2[] =
+ "# invalid section...\n"
+ "[this bracket isn't closed\n";
+
+static const char t3[] =
+ "# line without = ...\n"
+ "[bambam]\n"
+ "this line isn't any kind of valid\n";
+
+static const char t4[] =
+ "# starting with = ...\n"
+ "[bambam]\n"
+ "=not valid at all\n";
+
+int main(int argc, char *argv[])
+{
+ struct weston_config *config;
+ struct weston_config_section *section;
+ const char *name;
+ char *s;
+ int r, b, i;
+ int32_t n;
+ uint32_t u;
+
+ config = run_test(t0);
+ assert(config);
+ weston_config_destroy(config);
+
+ config = run_test(t1);
+ assert(config);
+ section = weston_config_get_section(config, "mollusc", NULL, NULL);
+ assert(section == NULL);
+
+ section = weston_config_get_section(config, "foo", NULL, NULL);
+ r = weston_config_section_get_string(section, "a", &s, NULL);
+ assert(r == 0 && strcmp(s, "b") == 0);
+ free(s);
+
+ section = weston_config_get_section(config, "foo", NULL, NULL);
+ r = weston_config_section_get_string(section, "b", &s, NULL);
+ assert(r == -1 && errno == ENOENT && s == NULL);
+
+ section = weston_config_get_section(config, "foo", NULL, NULL);
+ r = weston_config_section_get_string(section, "name", &s, NULL);
+ assert(r == 0 && strcmp(s, "Roy Batty") == 0);
+ free(s);
+
+ section = weston_config_get_section(config, "bar", NULL, NULL);
+ r = weston_config_section_get_string(section, "a", &s, "boo");
+ assert(r == -1 && errno == ENOENT && strcmp(s, "boo") == 0);
+ free(s);
+
+ section = weston_config_get_section(config, "bar", NULL, NULL);
+ r = weston_config_section_get_int(section, "number", &n, 600);
+ assert(r == 0 && n == 5252);
+
+ section = weston_config_get_section(config, "bar", NULL, NULL);
+ r = weston_config_section_get_int(section, "+++", &n, 700);
+ assert(r == -1 && errno == ENOENT && n == 700);
+
+ section = weston_config_get_section(config, "bar", NULL, NULL);
+ r = weston_config_section_get_uint(section, "number", &u, 600);
+ assert(r == 0 && u == 5252);
+
+ section = weston_config_get_section(config, "bar", NULL, NULL);
+ r = weston_config_section_get_uint(section, "+++", &u, 600);
+ assert(r == -1 && errno == ENOENT && u == 600);
+
+ section = weston_config_get_section(config, "bar", NULL, NULL);
+ r = weston_config_section_get_bool(section, "flag", &b, 600);
+ assert(r == 0 && b == 0);
+
+ section = weston_config_get_section(config, "stuff", NULL, NULL);
+ r = weston_config_section_get_bool(section, "flag", &b, -1);
+ assert(r == 0 && b == 1);
+
+ section = weston_config_get_section(config, "stuff", NULL, NULL);
+ r = weston_config_section_get_bool(section, "bonk", &b, -1);
+ assert(r == -1 && errno == ENOENT && b == -1);
+
+ section = weston_config_get_section(config, "bucket", "color", "blue");
+ r = weston_config_section_get_string(section, "contents", &s, NULL);
+ assert(r == 0 && strcmp(s, "live crabs") == 0);
+ free(s);
+
+ section = weston_config_get_section(config, "bucket", "color", "red");
+ r = weston_config_section_get_string(section, "contents", &s, NULL);
+ assert(r == 0 && strcmp(s, "sand") == 0);
+ free(s);
+
+ section = weston_config_get_section(config, "bucket", "color", "pink");
+ assert(section == NULL);
+ r = weston_config_section_get_string(section, "contents", &s, "eels");
+ assert(r == -1 && errno == ENOENT && strcmp(s, "eels") == 0);
+ free(s);
+
+ section = NULL;
+ i = 0;
+ while (weston_config_next_section(config, &section, &name))
+ assert(strcmp(section_names[i++], name) == 0);
+ assert(i == 5);
+
+ weston_config_destroy(config);
+
+ config = run_test(t2);
+ assert(config == NULL);
+
+ config = run_test(t3);
+ assert(config == NULL);
+
+ config = run_test(t4);
+ assert(config == NULL);
+
+ weston_config_destroy(NULL);
+ assert(weston_config_next_section(NULL, NULL, NULL) == 0);
+
+ section = weston_config_get_section(NULL, "bucket", NULL, NULL);
+ assert(section == NULL);
+
+ return 0;
+}
diff --git a/tests/event-test.c b/tests/event-test.c
new file mode 100644
index 00000000..980dfaad
--- /dev/null
+++ b/tests/event-test.c
@@ -0,0 +1,418 @@
+/*
+ * Copyright © 2012 Intel Corporation
+ * Copyright © 2013 Collabora, Ltd.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that 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.
+ */
+
+#include "weston-test-client-helper.h"
+
+static void
+check_pointer(struct client *client, int x, int y)
+{
+ int sx, sy;
+
+ /* check that the client got the global pointer update */
+ assert(client->test->pointer_x == x);
+ assert(client->test->pointer_y == y);
+
+ /* Does global pointer map onto the surface? */
+ if (surface_contains(client->surface, x, y)) {
+ /* check that the surface has the pointer focus */
+ assert(client->input->pointer->focus == client->surface);
+
+ /*
+ * check that the local surface pointer maps
+ * to the global pointer.
+ */
+ sx = client->input->pointer->x + client->surface->x;
+ sy = client->input->pointer->y + client->surface->y;
+ assert(sx == x);
+ assert(sy == y);
+ } else {
+ /*
+ * The global pointer does not map onto surface. So
+ * check that it doesn't have the pointer focus.
+ */
+ assert(client->input->pointer->focus == NULL);
+ }
+}
+
+static void
+check_pointer_move(struct client *client, int x, int y)
+{
+ wl_test_move_pointer(client->test->wl_test, x, y);
+ client_roundtrip(client);
+ check_pointer(client, x, y);
+}
+
+TEST(test_pointer_top_left)
+{
+ struct client *client;
+ int x, y;
+
+ client = client_create(46, 76, 111, 134);
+ assert(client);
+
+ /* move pointer outside top left */
+ x = client->surface->x - 1;
+ y = client->surface->y - 1;
+ assert(!surface_contains(client->surface, x, y));
+ check_pointer_move(client, x, y);
+
+ /* move pointer on top left */
+ x += 1; y += 1;
+ assert(surface_contains(client->surface, x, y));
+ check_pointer_move(client, x, y);
+
+ /* move pointer outside top left */
+ x -= 1; y -= 1;
+ assert(!surface_contains(client->surface, x, y));
+ check_pointer_move(client, x, y);
+}
+
+TEST(test_pointer_bottom_left)
+{
+ struct client *client;
+ int x, y;
+
+ client = client_create(99, 100, 100, 98);
+ assert(client);
+
+ /* move pointer outside bottom left */
+ x = client->surface->x - 1;
+ y = client->surface->y + client->surface->height;
+ assert(!surface_contains(client->surface, x, y));
+ check_pointer_move(client, x, y);
+
+ /* move pointer on bottom left */
+ x += 1; y -= 1;
+ assert(surface_contains(client->surface, x, y));
+ check_pointer_move(client, x, y);
+
+ /* move pointer outside bottom left */
+ x -= 1; y += 1;
+ assert(!surface_contains(client->surface, x, y));
+ check_pointer_move(client, x, y);
+}
+
+TEST(test_pointer_top_right)
+{
+ struct client *client;
+ int x, y;
+
+ client = client_create(48, 100, 67, 100);
+ assert(client);
+
+ /* move pointer outside top right */
+ x = client->surface->x + client->surface->width;
+ y = client->surface->y - 1;
+ assert(!surface_contains(client->surface, x, y));
+ check_pointer_move(client, x, y);
+
+ /* move pointer on top right */
+ x -= 1; y += 1;
+ assert(surface_contains(client->surface, x, y));
+ check_pointer_move(client, x, y);
+
+ /* move pointer outside top right */
+ x += 1; y -= 1;
+ assert(!surface_contains(client->surface, x, y));
+ check_pointer_move(client, x, y);
+}
+
+TEST(test_pointer_bottom_right)
+{
+ struct client *client;
+ int x, y;
+
+ client = client_create(100, 123, 100, 69);
+ assert(client);
+
+ /* move pointer outside bottom right */
+ x = client->surface->x + client->surface->width;
+ y = client->surface->y + client->surface->height;
+ assert(!surface_contains(client->surface, x, y));
+ check_pointer_move(client, x, y);
+
+ /* move pointer on bottom right */
+ x -= 1; y -= 1;
+ assert(surface_contains(client->surface, x, y));
+ check_pointer_move(client, x, y);
+
+ /* move pointer outside bottom right */
+ x += 1; y += 1;
+ assert(!surface_contains(client->surface, x, y));
+ check_pointer_move(client, x, y);
+}
+
+TEST(test_pointer_top_center)
+{
+ struct client *client;
+ int x, y;
+
+ client = client_create(100, 201, 100, 50);
+ assert(client);
+
+ /* move pointer outside top center */
+ x = client->surface->x + client->surface->width/2;
+ y = client->surface->y - 1;
+ assert(!surface_contains(client->surface, x, y));
+ check_pointer_move(client, x, y);
+
+ /* move pointer on top center */
+ y += 1;
+ assert(surface_contains(client->surface, x, y));
+ check_pointer_move(client, x, y);
+
+ /* move pointer outside top center */
+ y -= 1;
+ assert(!surface_contains(client->surface, x, y));
+ check_pointer_move(client, x, y);
+}
+
+TEST(test_pointer_bottom_center)
+{
+ struct client *client;
+ int x, y;
+
+ client = client_create(100, 45, 67, 100);
+ assert(client);
+
+ /* move pointer outside bottom center */
+ x = client->surface->x + client->surface->width/2;
+ y = client->surface->y + client->surface->height;
+ assert(!surface_contains(client->surface, x, y));
+ check_pointer_move(client, x, y);
+
+ /* move pointer on bottom center */
+ y -= 1;
+ assert(surface_contains(client->surface, x, y));
+ check_pointer_move(client, x, y);
+
+ /* move pointer outside bottom center */
+ y += 1;
+ assert(!surface_contains(client->surface, x, y));
+ check_pointer_move(client, x, y);
+}
+
+TEST(test_pointer_left_center)
+{
+ struct client *client;
+ int x, y;
+
+ client = client_create(167, 45, 78, 100);
+ assert(client);
+
+ /* move pointer outside left center */
+ x = client->surface->x - 1;
+ y = client->surface->y + client->surface->height/2;
+ assert(!surface_contains(client->surface, x, y));
+ check_pointer_move(client, x, y);
+
+ /* move pointer on left center */
+ x += 1;
+ assert(surface_contains(client->surface, x, y));
+ check_pointer_move(client, x, y);
+
+ /* move pointer outside left center */
+ x -= 1;
+ assert(!surface_contains(client->surface, x, y));
+ check_pointer_move(client, x, y);
+}
+
+TEST(test_pointer_right_center)
+{
+ struct client *client;
+ int x, y;
+
+ client = client_create(110, 37, 100, 46);
+ assert(client);
+
+ /* move pointer outside right center */
+ x = client->surface->x + client->surface->width;
+ y = client->surface->y + client->surface->height/2;
+ assert(!surface_contains(client->surface, x, y));
+ check_pointer_move(client, x, y);
+
+ /* move pointer on right center */
+ x -= 1;
+ assert(surface_contains(client->surface, x, y));
+ check_pointer_move(client, x, y);
+
+ /* move pointer outside right center */
+ x += 1;
+ assert(!surface_contains(client->surface, x, y));
+ check_pointer_move(client, x, y);
+}
+
+TEST(test_pointer_surface_move)
+{
+ struct client *client;
+
+ client = client_create(100, 100, 100, 100);
+ assert(client);
+
+ /* move pointer outside of client */
+ assert(!surface_contains(client->surface, 50, 50));
+ check_pointer_move(client, 50, 50);
+
+ /* move client center to pointer */
+ move_client(client, 0, 0);
+ assert(surface_contains(client->surface, 50, 50));
+ check_pointer(client, 50, 50);
+}
+
+static int
+output_contains_client(struct client *client)
+{
+ struct output *output = client->output;
+ struct surface *surface = client->surface;
+
+ return !(output->x >= surface->x + surface->width
+ || output->x + output->width <= surface->x
+ || output->y >= surface->y + surface->height
+ || output->y + output->height <= surface->y);
+}
+
+static void
+check_client_move(struct client *client, int x, int y)
+{
+ move_client(client, x, y);
+
+ if (output_contains_client(client)) {
+ assert(client->surface->output == client->output);
+ } else {
+ assert(client->surface->output == NULL);
+ }
+}
+
+TEST(test_surface_output)
+{
+ struct client *client;
+ int x, y;
+
+ client = client_create(100, 100, 100, 100);
+ assert(client);
+
+ assert(output_contains_client(client));
+
+ /* not visible */
+ x = 0;
+ y = -client->surface->height;
+ check_client_move(client, x, y);
+
+ /* visible */
+ check_client_move(client, x, ++y);
+
+ /* not visible */
+ x = -client->surface->width;
+ y = 0;
+ check_client_move(client, x, y);
+
+ /* visible */
+ check_client_move(client, ++x, y);
+
+ /* not visible */
+ x = client->output->width;
+ y = 0;
+ check_client_move(client, x, y);
+
+ /* visible */
+ check_client_move(client, --x, y);
+ assert(output_contains_client(client));
+
+ /* not visible */
+ x = 0;
+ y = client->output->height;
+ check_client_move(client, x, y);
+ assert(!output_contains_client(client));
+
+ /* visible */
+ check_client_move(client, x, --y);
+ assert(output_contains_client(client));
+}
+
+static void
+buffer_release_handler(void *data, struct wl_buffer *buffer)
+{
+ int *released = data;
+
+ *released = 1;
+}
+
+static struct wl_buffer_listener buffer_listener = {
+ buffer_release_handler
+};
+
+TEST(buffer_release)
+{
+ struct client *client;
+ struct wl_surface *surface;
+ struct wl_buffer *buf1;
+ struct wl_buffer *buf2;
+ struct wl_buffer *buf3;
+ int buf1_released = 0;
+ int buf2_released = 0;
+ int buf3_released = 0;
+ int frame;
+
+ client = client_create(100, 100, 100, 100);
+ assert(client);
+ surface = client->surface->wl_surface;
+
+ buf1 = create_shm_buffer(client, 100, 100, NULL);
+ wl_buffer_add_listener(buf1, &buffer_listener, &buf1_released);
+
+ buf2 = create_shm_buffer(client, 100, 100, NULL);
+ wl_buffer_add_listener(buf2, &buffer_listener, &buf2_released);
+
+ buf3 = create_shm_buffer(client, 100, 100, NULL);
+ wl_buffer_add_listener(buf3, &buffer_listener, &buf3_released);
+
+ /*
+ * buf1 must never be released, since it is replaced before
+ * it is committed, therefore it never becomes busy.
+ */
+
+ wl_surface_attach(surface, buf1, 0, 0);
+ wl_surface_attach(surface, buf2, 0, 0);
+ frame_callback_set(surface, &frame);
+ wl_surface_commit(surface);
+ frame_callback_wait(client, &frame);
+ assert(buf1_released == 0);
+ /* buf2 may or may not be released */
+ assert(buf3_released == 0);
+
+ wl_surface_attach(surface, buf3, 0, 0);
+ frame_callback_set(surface, &frame);
+ wl_surface_commit(surface);
+ frame_callback_wait(client, &frame);
+ assert(buf1_released == 0);
+ assert(buf2_released == 1);
+ /* buf3 may or may not be released */
+
+ wl_surface_attach(surface, client->surface->wl_buffer, 0, 0);
+ frame_callback_set(surface, &frame);
+ wl_surface_commit(surface);
+ frame_callback_wait(client, &frame);
+ assert(buf1_released == 0);
+ assert(buf2_released == 1);
+ assert(buf3_released == 1);
+}
diff --git a/tests/keyboard-test.c b/tests/keyboard-test.c
new file mode 100644
index 00000000..542bf4eb
--- /dev/null
+++ b/tests/keyboard-test.c
@@ -0,0 +1,65 @@
+/*
+ * Copyright © 2012 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 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.
+ */
+
+#include "weston-test-client-helper.h"
+
+TEST(simple_keyboard_test)
+{
+ struct client *client;
+ struct surface *expect_focus = NULL;
+ struct keyboard *keyboard;
+ uint32_t expect_key = 0;
+ uint32_t expect_state = 0;
+
+ client = client_create(10, 10, 1, 1);
+ assert(client);
+
+ keyboard = client->input->keyboard;
+
+ while(1) {
+ assert(keyboard->key == expect_key);
+ assert(keyboard->state == expect_state);
+ assert(keyboard->focus == expect_focus);
+
+ if (keyboard->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
+ expect_state = WL_KEYBOARD_KEY_STATE_RELEASED;
+ wl_test_send_key(client->test->wl_test, expect_key,
+ expect_state);
+ } else if (keyboard->focus) {
+ expect_focus = NULL;
+ wl_test_activate_surface(client->test->wl_test,
+ NULL);
+ } else if (expect_key < 10) {
+ expect_key++;
+ expect_focus = client->surface;
+ expect_state = WL_KEYBOARD_KEY_STATE_PRESSED;
+ wl_test_activate_surface(client->test->wl_test,
+ expect_focus->wl_surface);
+ wl_test_send_key(client->test->wl_test, expect_key,
+ expect_state);
+ } else {
+ break;
+ }
+
+ client_roundtrip(client);
+ }
+}
diff --git a/tests/matrix-test.c b/tests/matrix-test.c
new file mode 100644
index 00000000..5b0513f3
--- /dev/null
+++ b/tests/matrix-test.c
@@ -0,0 +1,419 @@
+/*
+ * Copyright © 2012 Collabora, Ltd.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <unistd.h>
+#include <signal.h>
+#include <time.h>
+
+#include "../shared/matrix.h"
+
+struct inverse_matrix {
+ double LU[16]; /* column-major */
+ unsigned perm[4]; /* permutation */
+};
+
+static struct timespec begin_time;
+
+static void
+reset_timer(void)
+{
+ clock_gettime(CLOCK_MONOTONIC, &begin_time);
+}
+
+static double
+read_timer(void)
+{
+ struct timespec t;
+
+ clock_gettime(CLOCK_MONOTONIC, &t);
+ return (double)(t.tv_sec - begin_time.tv_sec) +
+ 1e-9 * (t.tv_nsec - begin_time.tv_nsec);
+}
+
+static double
+det3x3(const float *c0, const float *c1, const float *c2)
+{
+ return (double)
+ c0[0] * c1[1] * c2[2] +
+ c1[0] * c2[1] * c0[2] +
+ c2[0] * c0[1] * c1[2] -
+ c0[2] * c1[1] * c2[0] -
+ c1[2] * c2[1] * c0[0] -
+ c2[2] * c0[1] * c1[0];
+}
+
+static double
+determinant(const struct weston_matrix *m)
+{
+ double det = 0;
+#if 1
+ /* develop on last row */
+ det -= m->d[3 + 0 * 4] * det3x3(&m->d[4], &m->d[8], &m->d[12]);
+ det += m->d[3 + 1 * 4] * det3x3(&m->d[0], &m->d[8], &m->d[12]);
+ det -= m->d[3 + 2 * 4] * det3x3(&m->d[0], &m->d[4], &m->d[12]);
+ det += m->d[3 + 3 * 4] * det3x3(&m->d[0], &m->d[4], &m->d[8]);
+#else
+ /* develop on first row */
+ det += m->d[0 + 0 * 4] * det3x3(&m->d[5], &m->d[9], &m->d[13]);
+ det -= m->d[0 + 1 * 4] * det3x3(&m->d[1], &m->d[9], &m->d[13]);
+ det += m->d[0 + 2 * 4] * det3x3(&m->d[1], &m->d[5], &m->d[13]);
+ det -= m->d[0 + 3 * 4] * det3x3(&m->d[1], &m->d[5], &m->d[9]);
+#endif
+ return det;
+}
+
+static void
+print_permutation_matrix(const struct inverse_matrix *m)
+{
+ const unsigned *p = m->perm;
+ const char *row[4] = {
+ "1 0 0 0\n",
+ "0 1 0 0\n",
+ "0 0 1 0\n",
+ "0 0 0 1\n"
+ };
+
+ printf(" P =\n%s%s%s%s", row[p[0]], row[p[1]], row[p[2]], row[p[3]]);
+}
+
+static void
+print_LU_decomposition(const struct inverse_matrix *m)
+{
+ unsigned r, c;
+
+ printf(" L "
+ " U\n");
+ for (r = 0; r < 4; ++r) {
+ double v;
+
+ for (c = 0; c < 4; ++c) {
+ if (c < r)
+ v = m->LU[r + c * 4];
+ else if (c == r)
+ v = 1.0;
+ else
+ v = 0.0;
+ printf(" %12.6f", v);
+ }
+
+ printf(" | ");
+
+ for (c = 0; c < 4; ++c) {
+ if (c >= r)
+ v = m->LU[r + c * 4];
+ else
+ v = 0.0;
+ printf(" %12.6f", v);
+ }
+ printf("\n");
+ }
+}
+
+static void
+print_inverse_data_matrix(const struct inverse_matrix *m)
+{
+ unsigned r, c;
+
+ for (r = 0; r < 4; ++r) {
+ for (c = 0; c < 4; ++c)
+ printf(" %12.6f", m->LU[r + c * 4]);
+ printf("\n");
+ }
+
+ printf("permutation: ");
+ for (r = 0; r < 4; ++r)
+ printf(" %u", m->perm[r]);
+ printf("\n");
+}
+
+static void
+print_matrix(const struct weston_matrix *m)
+{
+ unsigned r, c;
+
+ for (r = 0; r < 4; ++r) {
+ for (c = 0; c < 4; ++c)
+ printf(" %14.6e", m->d[r + c * 4]);
+ printf("\n");
+ }
+}
+
+static double
+frand(void)
+{
+ double r = random();
+ return r / (double)(RAND_MAX / 2) - 1.0f;
+}
+
+static void
+randomize_matrix(struct weston_matrix *m)
+{
+ unsigned i;
+ for (i = 0; i < 16; ++i)
+#if 1
+ m->d[i] = frand() * exp(10.0 * frand());
+#else
+ m->d[i] = frand();
+#endif
+}
+
+/* Take a matrix, compute inverse, multiply together
+ * and subtract the identity matrix to get the error matrix.
+ * Return the largest absolute value from the error matrix.
+ */
+static double
+test_inverse(struct weston_matrix *m)
+{
+ unsigned i;
+ struct inverse_matrix q;
+ double errsup = 0.0;
+
+ if (matrix_invert(q.LU, q.perm, m) != 0)
+ return INFINITY;
+
+ for (i = 0; i < 4; ++i)
+ inverse_transform(q.LU, q.perm, &m->d[i * 4]);
+
+ m->d[0] -= 1.0f;
+ m->d[5] -= 1.0f;
+ m->d[10] -= 1.0f;
+ m->d[15] -= 1.0f;
+
+ for (i = 0; i < 16; ++i) {
+ double err = fabs(m->d[i]);
+ if (err > errsup)
+ errsup = err;
+ }
+
+ return errsup;
+}
+
+enum {
+ TEST_OK,
+ TEST_NOT_INVERTIBLE_OK,
+ TEST_FAIL,
+ TEST_COUNT
+};
+
+static int
+test(void)
+{
+ struct weston_matrix m;
+ double det, errsup;
+
+ randomize_matrix(&m);
+ det = determinant(&m);
+
+ errsup = test_inverse(&m);
+ if (errsup < 1e-6)
+ return TEST_OK;
+
+ if (fabs(det) < 1e-5 && isinf(errsup))
+ return TEST_NOT_INVERTIBLE_OK;
+
+ printf("test fail, det: %g, error sup: %g\n", det, errsup);
+
+ return TEST_FAIL;
+}
+
+static int running;
+static void
+stopme(int n)
+{
+ running = 0;
+}
+
+static void
+test_loop_precision(void)
+{
+ int counts[TEST_COUNT] = { 0 };
+
+ printf("\nRunning a test loop for 10 seconds...\n");
+ running = 1;
+ alarm(10);
+ while (running) {
+ counts[test()]++;
+ }
+
+ printf("tests: %d ok, %d not invertible but ok, %d failed.\n"
+ "Total: %d iterations.\n",
+ counts[TEST_OK], counts[TEST_NOT_INVERTIBLE_OK],
+ counts[TEST_FAIL],
+ counts[TEST_OK] + counts[TEST_NOT_INVERTIBLE_OK] +
+ counts[TEST_FAIL]);
+}
+
+static void __attribute__((noinline))
+test_loop_speed_matrixvector(void)
+{
+ struct weston_matrix m;
+ struct weston_vector v = { { 0.5, 0.5, 0.5, 1.0 } };
+ unsigned long count = 0;
+ double t;
+
+ printf("\nRunning 3 s test on weston_matrix_transform()...\n");
+
+ weston_matrix_init(&m);
+
+ running = 1;
+ alarm(3);
+ reset_timer();
+ while (running) {
+ weston_matrix_transform(&m, &v);
+ count++;
+ }
+ t = read_timer();
+
+ printf("%lu iterations in %f seconds, avg. %.1f us/iter.\n",
+ count, t, 1e9 * t / count);
+}
+
+static void __attribute__((noinline))
+test_loop_speed_inversetransform(void)
+{
+ struct weston_matrix m;
+ struct inverse_matrix inv;
+ struct weston_vector v = { { 0.5, 0.5, 0.5, 1.0 } };
+ unsigned long count = 0;
+ double t;
+
+ printf("\nRunning 3 s test on inverse_transform()...\n");
+
+ weston_matrix_init(&m);
+ matrix_invert(inv.LU, inv.perm, &m);
+
+ running = 1;
+ alarm(3);
+ reset_timer();
+ while (running) {
+ inverse_transform(inv.LU, inv.perm, v.f);
+ count++;
+ }
+ t = read_timer();
+
+ printf("%lu iterations in %f seconds, avg. %.1f us/iter.\n",
+ count, t, 1e9 * t / count);
+}
+
+static void __attribute__((noinline))
+test_loop_speed_invert(void)
+{
+ struct weston_matrix m;
+ struct inverse_matrix inv;
+ unsigned long count = 0;
+ double t;
+
+ printf("\nRunning 3 s test on matrix_invert()...\n");
+
+ weston_matrix_init(&m);
+
+ running = 1;
+ alarm(3);
+ reset_timer();
+ while (running) {
+ matrix_invert(inv.LU, inv.perm, &m);
+ count++;
+ }
+ t = read_timer();
+
+ printf("%lu iterations in %f seconds, avg. %.1f ns/iter.\n",
+ count, t, 1e9 * t / count);
+}
+
+static void __attribute__((noinline))
+test_loop_speed_invert_explicit(void)
+{
+ struct weston_matrix m;
+ unsigned long count = 0;
+ double t;
+
+ printf("\nRunning 3 s test on weston_matrix_invert()...\n");
+
+ weston_matrix_init(&m);
+
+ running = 1;
+ alarm(3);
+ reset_timer();
+ while (running) {
+ weston_matrix_invert(&m, &m);
+ count++;
+ }
+ t = read_timer();
+
+ printf("%lu iterations in %f seconds, avg. %.1f ns/iter.\n",
+ count, t, 1e9 * t / count);
+}
+
+int main(void)
+{
+ struct sigaction ding;
+ struct weston_matrix M;
+ struct inverse_matrix Q;
+ int ret;
+ double errsup;
+ double det;
+
+ ding.sa_handler = stopme;
+ sigemptyset(&ding.sa_mask);
+ ding.sa_flags = 0;
+ sigaction(SIGALRM, &ding, NULL);
+
+ srandom(13);
+
+ M.d[0] = 3.0; M.d[4] = 17.0; M.d[8] = 10.0; M.d[12] = 0.0;
+ M.d[1] = 2.0; M.d[5] = 4.0; M.d[9] = -2.0; M.d[13] = 0.0;
+ M.d[2] = 6.0; M.d[6] = 18.0; M.d[10] = -12; M.d[14] = 0.0;
+ M.d[3] = 0.0; M.d[7] = 0.0; M.d[11] = 0.0; M.d[15] = 1.0;
+
+ ret = matrix_invert(Q.LU, Q.perm, &M);
+ printf("ret = %d\n", ret);
+ printf("det = %g\n\n", determinant(&M));
+
+ if (ret != 0)
+ return 1;
+
+ print_inverse_data_matrix(&Q);
+ printf("P * A = L * U\n");
+ print_permutation_matrix(&Q);
+ print_LU_decomposition(&Q);
+
+
+ printf("a random matrix:\n");
+ randomize_matrix(&M);
+ det = determinant(&M);
+ print_matrix(&M);
+ errsup = test_inverse(&M);
+ printf("\nThe matrix multiplied by its inverse, error:\n");
+ print_matrix(&M);
+ printf("max abs error: %g, original determinant %g\n", errsup, det);
+
+ test_loop_precision();
+ test_loop_speed_matrixvector();
+ test_loop_speed_inversetransform();
+ test_loop_speed_invert();
+ test_loop_speed_invert_explicit();
+
+ return 0;
+}
diff --git a/tests/setbacklight.c b/tests/setbacklight.c
new file mode 100644
index 00000000..92bd4bf3
--- /dev/null
+++ b/tests/setbacklight.c
@@ -0,0 +1,186 @@
+/*
+ * Copyright © 2012 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 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.
+ *
+ * Author: Tiago Vignatti
+ */
+/*
+ * \file setbacklight.c
+ * Test program to get a backlight connector and set its brightness value.
+ * Queries for the connectors id can be performed using drm/tests/modeprint
+ * program.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+#include "libbacklight.h"
+
+static uint32_t
+get_drm_connector_type(struct udev_device *drm_device, uint32_t connector_id)
+{
+ const char *filename;
+ int fd, i, connector_type;
+ drmModeResPtr res;
+ drmModeConnectorPtr connector;
+
+ filename = udev_device_get_devnode(drm_device);
+ fd = open(filename, O_RDWR | O_CLOEXEC);
+ if (fd < 0) {
+ printf("couldn't open drm_device\n");
+ return -1;
+ }
+
+ res = drmModeGetResources(fd);
+ if (res == 0) {
+ printf("Failed to get resources from card\n");
+ close(fd);
+ return -1;
+ }
+
+ for (i = 0; i < res->count_connectors; i++) {
+ connector = drmModeGetConnector(fd, res->connectors[i]);
+ if (!connector)
+ continue;
+
+ if ((connector->connection == DRM_MODE_DISCONNECTED) ||
+ (connector->connector_id != connector_id)) {
+ drmModeFreeConnector(connector);
+ continue;
+ }
+
+ connector_type = connector->connector_type;
+ drmModeFreeConnector(connector);
+ drmModeFreeResources(res);
+
+ close(fd);
+ return connector_type;
+ }
+
+ close(fd);
+ drmModeFreeResources(res);
+ return -1;
+}
+
+/* returns a value between 0-255 range, where higher is brighter */
+static uint32_t
+get_normalized_backlight(struct backlight *backlight)
+{
+ long brightness, max_brightness;
+ long norm;
+
+ brightness = backlight_get_brightness(backlight);
+ max_brightness = backlight_get_max_brightness(backlight);
+
+ /* convert it to a scale of 0 to 255 */
+ norm = (brightness * 255)/(max_brightness);
+
+ return (int) norm;
+}
+
+static void
+set_backlight(struct udev_device *drm_device, int connector_id, int blight)
+{
+ int connector_type;
+ long max_brightness, brightness, actual_brightness;
+ struct backlight *backlight;
+ long new_blight;
+
+ connector_type = get_drm_connector_type(drm_device, connector_id);
+ if (connector_type < 0)
+ return;
+
+ backlight = backlight_init(drm_device, connector_type);
+ if (!backlight) {
+ printf("backlight adjust failed\n");
+ return;
+ }
+
+ max_brightness = backlight_get_max_brightness(backlight);
+ printf("Max backlight: %ld\n", max_brightness);
+
+ brightness = backlight_get_brightness(backlight);
+ printf("Cached backlight: %ld\n", brightness);
+
+ actual_brightness = backlight_get_actual_brightness(backlight);
+ printf("Hardware backlight: %ld\n", actual_brightness);
+
+ printf("normalized current brightness: %d\n",
+ get_normalized_backlight(backlight));
+
+ /* denormalized value */
+ new_blight = (blight * max_brightness) / 255;
+
+ backlight_set_brightness(backlight, new_blight);
+ printf("Setting brightness to: %ld (norm: %d)\n", new_blight, blight);
+
+ backlight_destroy(backlight);
+}
+
+int
+main(int argc, char **argv)
+{
+ int blight, connector_id;
+ const char *path;
+ struct udev *udev;
+ struct udev_enumerate *e;
+ struct udev_list_entry *entry;
+ struct udev_device *drm_device;
+
+ if (argc < 3) {
+ printf("Please add connector_id and brightness values from 0-255\n");
+ return 1;
+ }
+
+ connector_id = atoi(argv[1]);
+ blight = atoi(argv[2]);
+
+ udev = udev_new();
+ if (udev == NULL) {
+ printf("failed to initialize udev context\n");
+ return 1;
+ }
+
+ e = udev_enumerate_new(udev);
+ udev_enumerate_add_match_subsystem(e, "drm");
+ udev_enumerate_add_match_sysname(e, "card[0-9]*");
+
+ udev_enumerate_scan_devices(e);
+ drm_device = NULL;
+ udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) {
+ path = udev_list_entry_get_name(entry);
+ drm_device = udev_device_new_from_syspath(udev, path);
+ break;
+ }
+
+ if (drm_device == NULL) {
+ printf("no drm device found\n");
+ return 1;
+ }
+
+ set_backlight(drm_device, connector_id, blight);
+
+ udev_device_unref(drm_device);
+ return 0;
+}
diff --git a/tests/subsurface-test.c b/tests/subsurface-test.c
new file mode 100644
index 00000000..98e00fea
--- /dev/null
+++ b/tests/subsurface-test.c
@@ -0,0 +1,549 @@
+/*
+ * Copyright © 2012 Collabora, Ltd.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that 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.
+ */
+
+#include <string.h>
+
+#include "weston-test-client-helper.h"
+#include "subsurface-client-protocol.h"
+#include <stdio.h>
+
+#define NUM_SUBSURFACES 3
+
+struct compound_surface {
+ struct wl_subcompositor *subco;
+ struct wl_surface *parent;
+ struct wl_surface *child[NUM_SUBSURFACES];
+ struct wl_subsurface *sub[NUM_SUBSURFACES];
+};
+
+static struct wl_subcompositor *
+get_subcompositor(struct client *client)
+{
+ struct global *g;
+ struct global *global_sub = NULL;
+ struct wl_subcompositor *sub;
+
+ wl_list_for_each(g, &client->global_list, link) {
+ if (strcmp(g->interface, "wl_subcompositor"))
+ continue;
+
+ if (global_sub)
+ assert(0 && "multiple wl_subcompositor objects");
+
+ global_sub = g;
+ }
+
+ assert(global_sub && "no wl_subcompositor found");
+
+ assert(global_sub->version == 1);
+
+ sub = wl_registry_bind(client->wl_registry, global_sub->name,
+ &wl_subcompositor_interface, 1);
+ assert(sub);
+
+ return sub;
+}
+
+static void
+populate_compound_surface(struct compound_surface *com, struct client *client)
+{
+ int i;
+
+ com->subco = get_subcompositor(client);
+
+ com->parent = wl_compositor_create_surface(client->wl_compositor);
+
+ for (i = 0; i < NUM_SUBSURFACES; i++) {
+ com->child[i] =
+ wl_compositor_create_surface(client->wl_compositor);
+ com->sub[i] =
+ wl_subcompositor_get_subsurface(com->subco,
+ com->child[i],
+ com->parent);
+ }
+}
+
+TEST(test_subsurface_basic_protocol)
+{
+ struct client *client;
+ struct compound_surface com1;
+ struct compound_surface com2;
+
+ client = client_create(100, 50, 123, 77);
+ assert(client);
+
+ populate_compound_surface(&com1, client);
+ populate_compound_surface(&com2, client);
+
+ client_roundtrip(client);
+}
+
+TEST(test_subsurface_position_protocol)
+{
+ struct client *client;
+ struct compound_surface com;
+ int i;
+
+ client = client_create(100, 50, 123, 77);
+ assert(client);
+
+ populate_compound_surface(&com, client);
+ for (i = 0; i < NUM_SUBSURFACES; i++)
+ wl_subsurface_set_position(com.sub[i],
+ (i + 2) * 20, (i + 2) * 10);
+
+ client_roundtrip(client);
+}
+
+TEST(test_subsurface_placement_protocol)
+{
+ struct client *client;
+ struct compound_surface com;
+
+ client = client_create(100, 50, 123, 77);
+ assert(client);
+
+ populate_compound_surface(&com, client);
+
+ wl_subsurface_place_above(com.sub[0], com.child[1]);
+ wl_subsurface_place_above(com.sub[1], com.parent);
+ wl_subsurface_place_below(com.sub[2], com.child[0]);
+ wl_subsurface_place_below(com.sub[1], com.parent);
+
+ client_roundtrip(client);
+}
+
+FAIL_TEST(test_subsurface_paradox)
+{
+ struct client *client;
+ struct wl_surface *parent;
+ struct wl_subcompositor *subco;
+
+ client = client_create(100, 50, 123, 77);
+ assert(client);
+
+ subco = get_subcompositor(client);
+ parent = wl_compositor_create_surface(client->wl_compositor);
+
+ /* surface is its own parent */
+ wl_subcompositor_get_subsurface(subco, parent, parent);
+
+ client_roundtrip(client);
+}
+
+FAIL_TEST(test_subsurface_identical_link)
+{
+ struct client *client;
+ struct compound_surface com;
+
+ client = client_create(100, 50, 123, 77);
+ assert(client);
+
+ populate_compound_surface(&com, client);
+
+ /* surface is already a subsurface */
+ wl_subcompositor_get_subsurface(com.subco, com.child[0], com.parent);
+
+ client_roundtrip(client);
+}
+
+FAIL_TEST(test_subsurface_change_link)
+{
+ struct client *client;
+ struct compound_surface com;
+ struct wl_surface *stranger;
+
+ client = client_create(100, 50, 123, 77);
+ assert(client);
+
+ stranger = wl_compositor_create_surface(client->wl_compositor);
+ populate_compound_surface(&com, client);
+
+ /* surface is already a subsurface */
+ wl_subcompositor_get_subsurface(com.subco, com.child[0], stranger);
+
+ client_roundtrip(client);
+}
+
+TEST(test_subsurface_nesting)
+{
+ struct client *client;
+ struct compound_surface com;
+ struct wl_surface *stranger;
+
+ client = client_create(100, 50, 123, 77);
+ assert(client);
+
+ stranger = wl_compositor_create_surface(client->wl_compositor);
+ populate_compound_surface(&com, client);
+
+ /* parent is a sub-surface */
+ wl_subcompositor_get_subsurface(com.subco, stranger, com.child[0]);
+
+ client_roundtrip(client);
+}
+
+TEST(test_subsurface_nesting_parent)
+{
+ struct client *client;
+ struct compound_surface com;
+ struct wl_surface *stranger;
+
+ client = client_create(100, 50, 123, 77);
+ assert(client);
+
+ stranger = wl_compositor_create_surface(client->wl_compositor);
+ populate_compound_surface(&com, client);
+
+ /* surface is already a parent */
+ wl_subcompositor_get_subsurface(com.subco, com.parent, stranger);
+
+ client_roundtrip(client);
+}
+
+FAIL_TEST(test_subsurface_loop_paradox)
+{
+ struct client *client;
+ struct wl_surface *surface[3];
+ struct wl_subcompositor *subco;
+
+ client = client_create(100, 50, 123, 77);
+ assert(client);
+
+ subco = get_subcompositor(client);
+ surface[0] = wl_compositor_create_surface(client->wl_compositor);
+ surface[1] = wl_compositor_create_surface(client->wl_compositor);
+ surface[2] = wl_compositor_create_surface(client->wl_compositor);
+
+ /* create a nesting loop */
+ wl_subcompositor_get_subsurface(subco, surface[1], surface[0]);
+ wl_subcompositor_get_subsurface(subco, surface[2], surface[1]);
+ wl_subcompositor_get_subsurface(subco, surface[0], surface[2]);
+
+ client_roundtrip(client);
+}
+
+FAIL_TEST(test_subsurface_place_above_stranger)
+{
+ struct client *client;
+ struct compound_surface com;
+ struct wl_surface *stranger;
+
+ client = client_create(100, 50, 123, 77);
+ assert(client);
+
+ stranger = wl_compositor_create_surface(client->wl_compositor);
+ populate_compound_surface(&com, client);
+
+ /* bad sibling */
+ wl_subsurface_place_above(com.sub[0], stranger);
+
+ client_roundtrip(client);
+}
+
+FAIL_TEST(test_subsurface_place_below_stranger)
+{
+ struct client *client;
+ struct compound_surface com;
+ struct wl_surface *stranger;
+
+ client = client_create(100, 50, 123, 77);
+ assert(client);
+
+ stranger = wl_compositor_create_surface(client->wl_compositor);
+ populate_compound_surface(&com, client);
+
+ /* bad sibling */
+ wl_subsurface_place_below(com.sub[0], stranger);
+
+ client_roundtrip(client);
+}
+
+FAIL_TEST(test_subsurface_place_above_foreign)
+{
+ struct client *client;
+ struct compound_surface com1;
+ struct compound_surface com2;
+
+ client = client_create(100, 50, 123, 77);
+ assert(client);
+
+ populate_compound_surface(&com1, client);
+ populate_compound_surface(&com2, client);
+
+ /* bad sibling */
+ wl_subsurface_place_above(com1.sub[0], com2.child[0]);
+
+ client_roundtrip(client);
+}
+
+FAIL_TEST(test_subsurface_place_below_foreign)
+{
+ struct client *client;
+ struct compound_surface com1;
+ struct compound_surface com2;
+
+ client = client_create(100, 50, 123, 77);
+ assert(client);
+
+ populate_compound_surface(&com1, client);
+ populate_compound_surface(&com2, client);
+
+ /* bad sibling */
+ wl_subsurface_place_below(com1.sub[0], com2.child[0]);
+
+ client_roundtrip(client);
+}
+
+TEST(test_subsurface_destroy_protocol)
+{
+ struct client *client;
+ struct compound_surface com;
+
+ client = client_create(100, 50, 123, 77);
+ assert(client);
+
+ populate_compound_surface(&com, client);
+
+ /* not needed anymore */
+ wl_subcompositor_destroy(com.subco);
+
+ /* detach child from parent */
+ wl_subsurface_destroy(com.sub[0]);
+
+ /* destroy: child, parent */
+ wl_surface_destroy(com.child[1]);
+ wl_surface_destroy(com.parent);
+
+ /* destroy: parent, child */
+ wl_surface_destroy(com.child[2]);
+
+ /* destroy: sub, child */
+ wl_surface_destroy(com.child[0]);
+
+ /* 2x destroy: child, sub */
+ wl_subsurface_destroy(com.sub[2]);
+ wl_subsurface_destroy(com.sub[1]);
+
+ client_roundtrip(client);
+}
+
+static void
+create_subsurface_tree(struct client *client, struct wl_surface **surfs,
+ struct wl_subsurface **subs, int n)
+{
+ struct wl_subcompositor *subco;
+ int i;
+
+ subco = get_subcompositor(client);
+
+ for (i = 0; i < n; i++)
+ surfs[i] = wl_compositor_create_surface(client->wl_compositor);
+
+ /*
+ * The tree of sub-surfaces:
+ * 0
+ * / \
+ * 1 2 - 10
+ * / \ |\
+ * 3 5 9 6
+ * / / \
+ * 4 7 8
+ * Surface 0 has no wl_subsurface, others do.
+ */
+
+ switch (n) {
+ default:
+ assert(0);
+ break;
+
+#define SUB_LINK(s,p) \
+ subs[s] = wl_subcompositor_get_subsurface(subco, surfs[s], surfs[p])
+
+ case 11:
+ SUB_LINK(10, 2);
+ case 10:
+ SUB_LINK(9, 2);
+ case 9:
+ SUB_LINK(8, 6);
+ case 8:
+ SUB_LINK(7, 6);
+ case 7:
+ SUB_LINK(6, 2);
+ case 6:
+ SUB_LINK(5, 1);
+ case 5:
+ SUB_LINK(4, 3);
+ case 4:
+ SUB_LINK(3, 1);
+ case 3:
+ SUB_LINK(2, 0);
+ case 2:
+ SUB_LINK(1, 0);
+
+#undef SUB_LINK
+ };
+}
+
+static void
+destroy_subsurface_tree(struct wl_surface **surfs,
+ struct wl_subsurface **subs, int n)
+{
+ int i;
+
+ for (i = n; i-- > 0; ) {
+ if (surfs[i])
+ wl_surface_destroy(surfs[i]);
+
+ if (subs[i])
+ wl_subsurface_destroy(subs[i]);
+ }
+}
+
+static int
+has_dupe(int *cnt, int n)
+{
+ int i;
+
+ for (i = 0; i < n; i++)
+ if (cnt[i] == cnt[n])
+ return 1;
+
+ return 0;
+}
+
+/* Note: number of permutations to test is: set_size! / (set_size - NSTEPS)!
+ */
+#define NSTEPS 3
+
+struct permu_state {
+ int set_size;
+ int cnt[NSTEPS];
+};
+
+static void
+permu_init(struct permu_state *s, int set_size)
+{
+ int i;
+
+ s->set_size = set_size;
+ for (i = 0; i < NSTEPS; i++)
+ s->cnt[i] = 0;
+}
+
+static int
+permu_next(struct permu_state *s)
+{
+ int k;
+
+ s->cnt[NSTEPS - 1]++;
+
+ while (1) {
+ if (s->cnt[0] >= s->set_size) {
+ return -1;
+ }
+
+ for (k = 1; k < NSTEPS; k++) {
+ if (s->cnt[k] >= s->set_size) {
+ s->cnt[k - 1]++;
+ s->cnt[k] = 0;
+ break;
+ }
+
+ if (has_dupe(s->cnt, k)) {
+ s->cnt[k]++;
+ break;
+ }
+ }
+
+ if (k == NSTEPS)
+ return 0;
+ }
+}
+
+static void
+destroy_permu_object(struct wl_surface **surfs,
+ struct wl_subsurface **subs, int i)
+{
+ int h = (i + 1) / 2;
+
+ if (i & 1) {
+ fprintf(stderr, " [sub %2d]", h);
+ wl_subsurface_destroy(subs[h]);
+ subs[h] = NULL;
+ } else {
+ fprintf(stderr, " [surf %2d]", h);
+ wl_surface_destroy(surfs[h]);
+ surfs[h] = NULL;
+ }
+}
+
+TEST(test_subsurface_destroy_permutations)
+{
+ /*
+ * Test wl_surface and wl_subsurface destruction orders in a
+ * complex tree of sub-surfaces.
+ *
+ * In the tree of sub-surfaces, go through every possible
+ * permutation of destroying all wl_surface and wl_subsurface
+ * objects. Execpt, to limit running time to a reasonable level,
+ * execute only the first NSTEPS destructions from each
+ * permutation, and ignore identical cases.
+ */
+
+ const int test_size = 11;
+ struct client *client;
+ struct wl_surface *surfs[test_size];
+ struct wl_subsurface *subs[test_size];
+ struct permu_state per;
+ int counter = 0;
+ int i;
+
+ client = client_create(100, 50, 123, 77);
+ assert(client);
+
+ permu_init(&per, test_size * 2 - 1);
+ while (permu_next(&per) != -1) {
+ /* for each permutation of NSTEPS out of test_size */
+ memset(surfs, 0, sizeof surfs);
+ memset(subs, 0, sizeof subs);
+
+ create_subsurface_tree(client, surfs, subs, test_size);
+
+ fprintf(stderr, "permu");
+
+ for (i = 0; i < NSTEPS; i++)
+ fprintf(stderr, " %2d", per.cnt[i]);
+
+ for (i = 0; i < NSTEPS; i++)
+ destroy_permu_object(surfs, subs, per.cnt[i]);
+
+ fprintf(stderr, "\n");
+ client_roundtrip(client);
+
+ destroy_subsurface_tree(surfs, subs, test_size);
+ counter++;
+ }
+
+ client_roundtrip(client);
+ fprintf(stderr, "tried %d destroy permutations\n", counter);
+}
diff --git a/tests/surface-global-test.c b/tests/surface-global-test.c
new file mode 100644
index 00000000..04b64d6a
--- /dev/null
+++ b/tests/surface-global-test.c
@@ -0,0 +1,80 @@
+/*
+ * Copyright © 2012 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 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.
+ */
+
+#include <assert.h>
+
+#include "../src/compositor.h"
+
+static void
+surface_to_from_global(void *data)
+{
+ struct weston_compositor *compositor = data;
+ struct weston_surface *surface;
+ float x, y;
+ wl_fixed_t fx, fy;
+ int32_t ix, iy;
+
+ surface = weston_surface_create(compositor);
+ assert(surface);
+ weston_surface_configure(surface, 5, 10, 50, 50);
+ weston_surface_update_transform(surface);
+
+ weston_surface_to_global_float(surface, 33, 22, &x, &y);
+ assert(x == 38 && y == 32);
+
+ weston_surface_to_global_float(surface, -8, -2, &x, &y);
+ assert(x == -3 && y == 8);
+
+ weston_surface_to_global_fixed(surface, wl_fixed_from_int(12),
+ wl_fixed_from_int(5), &fx, &fy);
+ assert(fx == wl_fixed_from_int(17) && fy == wl_fixed_from_int(15));
+
+ weston_surface_from_global_float(surface, 38, 32, &x, &y);
+ assert(x == 33 && y == 22);
+
+ weston_surface_from_global_float(surface, 42, 5, &x, &y);
+ assert(x == 37 && y == -5);
+
+ weston_surface_from_global_fixed(surface, wl_fixed_from_int(21),
+ wl_fixed_from_int(100), &fx, &fy);
+ assert(fx == wl_fixed_from_int(16) && fy == wl_fixed_from_int(90));
+
+ weston_surface_from_global(surface, 0, 0, &ix, &iy);
+ assert(ix == -5 && iy == -10);
+
+ weston_surface_from_global(surface, 5, 10, &ix, &iy);
+ assert(ix == 0 && iy == 0);
+
+ wl_display_terminate(compositor->wl_display);
+}
+
+WL_EXPORT int
+module_init(struct weston_compositor *compositor, int *argc, char *argv[])
+{
+ struct wl_event_loop *loop;
+
+ loop = wl_display_get_event_loop(compositor->wl_display);
+
+ wl_event_loop_add_idle(loop, surface_to_from_global, compositor);
+
+ return 0;
+}
diff --git a/tests/surface-test.c b/tests/surface-test.c
new file mode 100644
index 00000000..e8af2ed7
--- /dev/null
+++ b/tests/surface-test.c
@@ -0,0 +1,63 @@
+/*
+ * Copyright © 2012 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 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.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include "../src/compositor.h"
+
+static void
+surface_transform(void *data)
+{
+ struct weston_compositor *compositor = data;
+ struct weston_surface *surface;
+ float x, y;
+
+ surface = weston_surface_create(compositor);
+ assert(surface);
+ weston_surface_configure(surface, 100, 100, 200, 200);
+ weston_surface_update_transform(surface);
+ weston_surface_to_global_float(surface, 20, 20, &x, &y);
+
+ fprintf(stderr, "20,20 maps to %f, %f\n", x, y);
+ assert(x == 120 && y == 120);
+
+ weston_surface_set_position(surface, 150, 300);
+ weston_surface_update_transform(surface);
+ weston_surface_to_global_float(surface, 50, 40, &x, &y);
+ assert(x == 200 && y == 340);
+
+ wl_display_terminate(compositor->wl_display);
+}
+
+WL_EXPORT int
+module_init(struct weston_compositor *compositor, int *argc, char *argv[])
+{
+ struct wl_event_loop *loop;
+
+ loop = wl_display_get_event_loop(compositor->wl_display);
+
+ wl_event_loop_add_idle(loop, surface_transform, compositor);
+
+ return 0;
+}
diff --git a/tests/text-test.c b/tests/text-test.c
new file mode 100644
index 00000000..48f2b5a6
--- /dev/null
+++ b/tests/text-test.c
@@ -0,0 +1,214 @@
+/*
+ * Copyright © 2012 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 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.
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <linux/input.h>
+#include "weston-test-client-helper.h"
+#include "../clients/text-client-protocol.h"
+
+struct text_input_state {
+ int activated;
+ int deactivated;
+};
+
+static void
+text_input_commit_string(void *data,
+ struct wl_text_input *text_input,
+ uint32_t serial,
+ const char *text)
+{
+}
+
+static void
+text_input_preedit_string(void *data,
+ struct wl_text_input *text_input,
+ uint32_t serial,
+ const char *text,
+ const char *commit)
+{
+}
+
+static void
+text_input_delete_surrounding_text(void *data,
+ struct wl_text_input *text_input,
+ int32_t index,
+ uint32_t length)
+{
+}
+
+static void
+text_input_cursor_position(void *data,
+ struct wl_text_input *text_input,
+ int32_t index,
+ int32_t anchor)
+{
+}
+
+static void
+text_input_preedit_styling(void *data,
+ struct wl_text_input *text_input,
+ uint32_t index,
+ uint32_t length,
+ uint32_t style)
+{
+}
+
+static void
+text_input_preedit_cursor(void *data,
+ struct wl_text_input *text_input,
+ int32_t index)
+{
+}
+
+static void
+text_input_modifiers_map(void *data,
+ struct wl_text_input *text_input,
+ struct wl_array *map)
+{
+}
+
+static void
+text_input_keysym(void *data,
+ struct wl_text_input *text_input,
+ uint32_t serial,
+ uint32_t time,
+ uint32_t sym,
+ uint32_t state,
+ uint32_t modifiers)
+{
+}
+
+static void
+text_input_enter(void *data,
+ struct wl_text_input *text_input,
+ struct wl_surface *surface)
+
+{
+ struct text_input_state *state = data;
+
+ fprintf(stderr, "%s\n", __FUNCTION__);
+
+ state->activated += 1;
+}
+
+static void
+text_input_leave(void *data,
+ struct wl_text_input *text_input)
+{
+ struct text_input_state *state = data;
+
+ state->deactivated += 1;
+}
+
+static void
+text_input_input_panel_state(void *data,
+ struct wl_text_input *text_input,
+ uint32_t state)
+{
+}
+
+static void
+text_input_language(void *data,
+ struct wl_text_input *text_input,
+ uint32_t serial,
+ const char *language)
+{
+}
+
+static void
+text_input_text_direction(void *data,
+ struct wl_text_input *text_input,
+ uint32_t serial,
+ uint32_t direction)
+{
+}
+
+static const struct wl_text_input_listener text_input_listener = {
+ text_input_enter,
+ text_input_leave,
+ text_input_modifiers_map,
+ text_input_input_panel_state,
+ text_input_preedit_string,
+ text_input_preedit_styling,
+ text_input_preedit_cursor,
+ text_input_commit_string,
+ text_input_cursor_position,
+ text_input_delete_surrounding_text,
+ text_input_keysym,
+ text_input_language,
+ text_input_text_direction
+};
+
+TEST(text_test)
+{
+ struct client *client;
+ struct global *global;
+ struct wl_text_input_manager *factory;
+ struct wl_text_input *text_input;
+ struct text_input_state state;
+
+ client = client_create(100, 100, 100, 100);
+ assert(client);
+
+ factory = NULL;
+ wl_list_for_each(global, &client->global_list, link) {
+ if (strcmp(global->interface, "wl_text_input_manager") == 0)
+ factory = wl_registry_bind(client->wl_registry,
+ global->name,
+ &wl_text_input_manager_interface, 1);
+ }
+
+ assert(factory);
+
+ memset(&state, 0, sizeof state);
+ text_input = wl_text_input_manager_create_text_input(factory);
+ wl_text_input_add_listener(text_input, &text_input_listener, &state);
+
+ /* Make sure our test surface has keyboard focus. */
+ wl_test_activate_surface(client->test->wl_test,
+ client->surface->wl_surface);
+ client_roundtrip(client);
+ assert(client->input->keyboard->focus == client->surface);
+
+ /* Activate test model and make sure we get enter event. */
+ wl_text_input_activate(text_input, client->input->wl_seat,
+ client->surface->wl_surface);
+ client_roundtrip(client);
+ assert(state.activated == 1 && state.deactivated == 0);
+
+ /* Deactivate test model and make sure we get leave event. */
+ wl_text_input_deactivate(text_input, client->input->wl_seat);
+ client_roundtrip(client);
+ assert(state.activated == 1 && state.deactivated == 1);
+
+ /* Activate test model again. */
+ wl_text_input_activate(text_input, client->input->wl_seat,
+ client->surface->wl_surface);
+ client_roundtrip(client);
+ assert(state.activated == 2 && state.deactivated == 1);
+
+ /* Take keyboard focus away and verify we get leave event. */
+ wl_test_activate_surface(client->test->wl_test, NULL);
+ client_roundtrip(client);
+ assert(state.activated == 2 && state.deactivated == 2);
+}
diff --git a/tests/vertex-clip-test.c b/tests/vertex-clip-test.c
new file mode 100644
index 00000000..5b2e08c5
--- /dev/null
+++ b/tests/vertex-clip-test.c
@@ -0,0 +1,219 @@
+/*
+ * Copyright © 2013 Sam Spilsbury <smspillaz@gmail.com>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that 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.
+ */
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "weston-test-runner.h"
+
+#include "../src/vertex-clipping.h"
+
+#define BOUNDING_BOX_TOP_Y 100.0f
+#define BOUNDING_BOX_LEFT_X 50.0f
+#define BOUNDING_BOX_RIGHT_X 100.0f
+#define BOUNDING_BOX_BOTTOM_Y 50.0f
+
+#define INSIDE_X1 (BOUNDING_BOX_LEFT_X + 1.0f)
+#define INSIDE_X2 (BOUNDING_BOX_RIGHT_X - 1.0f)
+#define INSIDE_Y1 (BOUNDING_BOX_BOTTOM_Y + 1.0f)
+#define INSIDE_Y2 (BOUNDING_BOX_TOP_Y - 1.0f)
+
+#define OUTSIDE_X1 (BOUNDING_BOX_LEFT_X - 1.0f)
+#define OUTSIDE_X2 (BOUNDING_BOX_RIGHT_X + 1.0f)
+#define OUTSIDE_Y1 (BOUNDING_BOX_BOTTOM_Y - 1.0f)
+#define OUTSIDE_Y2 (BOUNDING_BOX_TOP_Y + 1.0f)
+
+static void
+populate_clip_context (struct clip_context *ctx)
+{
+ ctx->clip.x1 = BOUNDING_BOX_LEFT_X;
+ ctx->clip.y1 = BOUNDING_BOX_BOTTOM_Y;
+ ctx->clip.x2 = BOUNDING_BOX_RIGHT_X;
+ ctx->clip.y2 = BOUNDING_BOX_TOP_Y;
+}
+
+static int
+clip_polygon (struct clip_context *ctx,
+ struct polygon8 *polygon,
+ GLfloat *vertices_x,
+ GLfloat *vertices_y)
+{
+ populate_clip_context(ctx);
+ return clip_transformed(ctx, polygon, vertices_x, vertices_y);
+}
+
+struct vertex_clip_test_data
+{
+ struct polygon8 surface;
+ struct polygon8 expected;
+};
+
+const struct vertex_clip_test_data test_data[] =
+{
+ /* All inside */
+ {
+ {
+ { INSIDE_X1, INSIDE_X2, INSIDE_X2, INSIDE_X1 },
+ { INSIDE_Y1, INSIDE_Y1, INSIDE_Y2, INSIDE_Y2 },
+ 4
+ },
+ {
+ { INSIDE_X1, INSIDE_X2, INSIDE_X2, INSIDE_X1 },
+ { INSIDE_Y1, INSIDE_Y1, INSIDE_Y2, INSIDE_Y2 },
+ 4
+ }
+ },
+ /* Top outside */
+ {
+ {
+ { INSIDE_X1, INSIDE_X2, INSIDE_X2, INSIDE_X1 },
+ { INSIDE_Y1, INSIDE_Y1, OUTSIDE_Y2, OUTSIDE_Y2 },
+ 4
+ },
+ {
+ { INSIDE_X1, INSIDE_X1, INSIDE_X2, INSIDE_X2 },
+ { BOUNDING_BOX_TOP_Y, INSIDE_Y1, INSIDE_Y1, BOUNDING_BOX_TOP_Y },
+ 4
+ }
+ },
+ /* Bottom outside */
+ {
+ {
+ { INSIDE_X1, INSIDE_X2, INSIDE_X2, INSIDE_X1 },
+ { OUTSIDE_Y1, OUTSIDE_Y1, INSIDE_Y2, INSIDE_Y2 },
+ 4
+ },
+ {
+ { INSIDE_X1, INSIDE_X2, INSIDE_X2, INSIDE_X1 },
+ { BOUNDING_BOX_BOTTOM_Y, BOUNDING_BOX_BOTTOM_Y, INSIDE_Y2, INSIDE_Y2 },
+ 4
+ }
+ },
+ /* Left outside */
+ {
+ {
+ { OUTSIDE_X1, INSIDE_X2, INSIDE_X2, OUTSIDE_X1 },
+ { INSIDE_Y1, INSIDE_Y1, INSIDE_Y2, INSIDE_Y2 },
+ 4
+ },
+ {
+ { BOUNDING_BOX_LEFT_X, INSIDE_X2, INSIDE_X2, BOUNDING_BOX_LEFT_X },
+ { INSIDE_Y1, INSIDE_Y1, INSIDE_Y2, INSIDE_Y2 },
+ 4
+ }
+ },
+ /* Right outside */
+ {
+ {
+ { INSIDE_X1, OUTSIDE_X2, OUTSIDE_X2, INSIDE_X1 },
+ { INSIDE_Y1, INSIDE_Y1, INSIDE_Y2, INSIDE_Y2 },
+ 4
+ },
+ {
+ { INSIDE_X1, BOUNDING_BOX_RIGHT_X, BOUNDING_BOX_RIGHT_X, INSIDE_X1 },
+ { INSIDE_Y1, INSIDE_Y1, INSIDE_Y2, INSIDE_Y2 },
+ 4
+ }
+ },
+ /* Diamond extending from bounding box edges, clip to bounding box */
+ {
+ {
+ { BOUNDING_BOX_LEFT_X - 25, BOUNDING_BOX_LEFT_X + 25, BOUNDING_BOX_RIGHT_X + 25, BOUNDING_BOX_RIGHT_X - 25 },
+ { BOUNDING_BOX_BOTTOM_Y + 25, BOUNDING_BOX_TOP_Y + 25, BOUNDING_BOX_TOP_Y - 25, BOUNDING_BOX_BOTTOM_Y - 25 },
+ 4
+ },
+ {
+ { BOUNDING_BOX_LEFT_X, BOUNDING_BOX_LEFT_X, BOUNDING_BOX_RIGHT_X, BOUNDING_BOX_RIGHT_X },
+ { BOUNDING_BOX_BOTTOM_Y, BOUNDING_BOX_TOP_Y, BOUNDING_BOX_TOP_Y, BOUNDING_BOX_BOTTOM_Y },
+ 4
+ }
+ },
+ /* Diamond inside of bounding box edges, clip t bounding box, 8 resulting vertices */
+ {
+ {
+ { BOUNDING_BOX_LEFT_X - 12.5, BOUNDING_BOX_LEFT_X + 25, BOUNDING_BOX_RIGHT_X + 12.5, BOUNDING_BOX_RIGHT_X - 25 },
+ { BOUNDING_BOX_BOTTOM_Y + 25, BOUNDING_BOX_TOP_Y + 12.5, BOUNDING_BOX_TOP_Y - 25, BOUNDING_BOX_BOTTOM_Y - 12.5 },
+ 4
+ },
+ {
+ { BOUNDING_BOX_LEFT_X + 12.5, BOUNDING_BOX_LEFT_X, BOUNDING_BOX_LEFT_X, BOUNDING_BOX_LEFT_X + 12.5,
+ BOUNDING_BOX_RIGHT_X - 12.5, BOUNDING_BOX_RIGHT_X, BOUNDING_BOX_RIGHT_X, BOUNDING_BOX_RIGHT_X - 12.5 },
+ { BOUNDING_BOX_BOTTOM_Y, BOUNDING_BOX_BOTTOM_Y + 12.5, BOUNDING_BOX_TOP_Y - 12.5, BOUNDING_BOX_TOP_Y,
+ BOUNDING_BOX_TOP_Y, BOUNDING_BOX_TOP_Y - 12.5, BOUNDING_BOX_BOTTOM_Y + 12.5, BOUNDING_BOX_BOTTOM_Y },
+ 8
+ }
+ }
+};
+
+/* clip_polygon modifies the source operand and the test data must
+ * be const, so we need to deep copy it */
+static void
+deep_copy_polygon8(const struct polygon8 *src, struct polygon8 *dst)
+{
+ dst->n = src->n;
+ memcpy((void *) dst->x, src->x, sizeof (src->x));
+ memcpy((void *) dst->y, src->y, sizeof (src->y));
+}
+
+TEST_P(clip_polygon_n_vertices_emitted, test_data)
+{
+ struct vertex_clip_test_data *tdata = data;
+ struct clip_context ctx;
+ struct polygon8 polygon;
+ GLfloat vertices_x[8];
+ GLfloat vertices_y[8];
+ deep_copy_polygon8(&tdata->surface, &polygon);
+ int emitted = clip_polygon(&ctx, &polygon, vertices_x, vertices_y);
+
+ assert(emitted == tdata->expected.n);
+}
+
+TEST_P(clip_polygon_expected_vertices, test_data)
+{
+ struct vertex_clip_test_data *tdata = data;
+ struct clip_context ctx;
+ struct polygon8 polygon;
+ GLfloat vertices_x[8];
+ GLfloat vertices_y[8];
+ deep_copy_polygon8(&tdata->surface, &polygon);
+ int emitted = clip_polygon(&ctx, &polygon, vertices_x, vertices_y);
+ int i = 0;
+
+ for (; i < emitted; ++i)
+ {
+ assert(vertices_x[i] == tdata->expected.x[i]);
+ assert(vertices_y[i] == tdata->expected.y[i]);
+ }
+}
+
+TEST(float_difference_different)
+{
+ assert(float_difference(1.0f, 0.0f) == 1.0f);
+}
+
+TEST(float_difference_same)
+{
+ assert(float_difference(1.0f, 1.0f) == 0.0f);
+}
+
diff --git a/tests/weston-test-client-helper.c b/tests/weston-test-client-helper.c
new file mode 100644
index 00000000..b19be400
--- /dev/null
+++ b/tests/weston-test-client-helper.c
@@ -0,0 +1,539 @@
+/*
+ * Copyright © 2012 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
+ * 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.
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+#include "../shared/os-compatibility.h"
+#include "weston-test-client-helper.h"
+
+static inline void *
+xzalloc(size_t size)
+{
+ void *p;
+
+ p = calloc(1, size);
+ assert(p);
+
+ return p;
+}
+
+int
+surface_contains(struct surface *surface, int x, int y)
+{
+ /* test whether a global x,y point is contained in the surface */
+ int sx = surface->x;
+ int sy = surface->y;
+ int sw = surface->width;
+ int sh = surface->height;
+ return x >= sx && y >= sy && x < sx + sw && y < sy + sh;
+}
+
+static void
+frame_callback_handler(void *data, struct wl_callback *callback, uint32_t time)
+{
+ int *done = data;
+
+ *done = 1;
+
+ wl_callback_destroy(callback);
+}
+
+static const struct wl_callback_listener frame_listener = {
+ frame_callback_handler
+};
+
+struct wl_callback *
+frame_callback_set(struct wl_surface *surface, int *done)
+{
+ struct wl_callback *callback;
+
+ *done = 0;
+ callback = wl_surface_frame(surface);
+ wl_callback_add_listener(callback, &frame_listener, done);
+
+ return callback;
+}
+
+void
+frame_callback_wait(struct client *client, int *done)
+{
+ while (!*done) {
+ assert(wl_display_dispatch(client->wl_display) >= 0);
+ }
+}
+
+void
+move_client(struct client *client, int x, int y)
+{
+ struct surface *surface = client->surface;
+ int done;
+
+ client->surface->x = x;
+ client->surface->y = y;
+ wl_test_move_surface(client->test->wl_test, surface->wl_surface,
+ surface->x, surface->y);
+ /* The attach here is necessary because commit() will call congfigure
+ * only on surfaces newly attached, and the one that sets the surface
+ * position is the configure. */
+ wl_surface_attach(surface->wl_surface, surface->wl_buffer, 0, 0);
+ wl_surface_damage(surface->wl_surface, 0, 0, surface->width,
+ surface->height);
+
+ frame_callback_set(surface->wl_surface, &done);
+
+ wl_surface_commit(surface->wl_surface);
+
+ frame_callback_wait(client, &done);
+}
+
+static void
+pointer_handle_enter(void *data, struct wl_pointer *wl_pointer,
+ uint32_t serial, struct wl_surface *wl_surface,
+ wl_fixed_t x, wl_fixed_t y)
+{
+ struct pointer *pointer = data;
+
+ pointer->focus = wl_surface_get_user_data(wl_surface);
+ pointer->x = wl_fixed_to_int(x);
+ pointer->y = wl_fixed_to_int(y);
+
+ fprintf(stderr, "test-client: got pointer enter %d %d, surface %p\n",
+ pointer->x, pointer->y, pointer->focus);
+}
+
+static void
+pointer_handle_leave(void *data, struct wl_pointer *wl_pointer,
+ uint32_t serial, struct wl_surface *wl_surface)
+{
+ struct pointer *pointer = data;
+
+ pointer->focus = NULL;
+
+ fprintf(stderr, "test-client: got pointer leave, surface %p\n",
+ wl_surface_get_user_data(wl_surface));
+}
+
+static void
+pointer_handle_motion(void *data, struct wl_pointer *wl_pointer,
+ uint32_t time, wl_fixed_t x, wl_fixed_t y)
+{
+ struct pointer *pointer = data;
+
+ pointer->x = wl_fixed_to_int(x);
+ pointer->y = wl_fixed_to_int(y);
+
+ fprintf(stderr, "test-client: got pointer motion %d %d\n",
+ pointer->x, pointer->y);
+}
+
+static void
+pointer_handle_button(void *data, struct wl_pointer *wl_pointer,
+ uint32_t serial, uint32_t time, uint32_t button,
+ uint32_t state)
+{
+ struct pointer *pointer = data;
+
+ pointer->button = button;
+ pointer->state = state;
+
+ fprintf(stderr, "test-client: got pointer button %u %u\n",
+ button, state);
+}
+
+static void
+pointer_handle_axis(void *data, struct wl_pointer *wl_pointer,
+ uint32_t time, uint32_t axis, wl_fixed_t value)
+{
+ fprintf(stderr, "test-client: got pointer axis %u %f\n",
+ axis, wl_fixed_to_double(value));
+}
+
+static const struct wl_pointer_listener pointer_listener = {
+ pointer_handle_enter,
+ pointer_handle_leave,
+ pointer_handle_motion,
+ pointer_handle_button,
+ pointer_handle_axis,
+};
+
+static void
+keyboard_handle_keymap(void *data, struct wl_keyboard *wl_keyboard,
+ uint32_t format, int fd, uint32_t size)
+{
+ close(fd);
+
+ fprintf(stderr, "test-client: got keyboard keymap\n");
+}
+
+static void
+keyboard_handle_enter(void *data, struct wl_keyboard *wl_keyboard,
+ uint32_t serial, struct wl_surface *wl_surface,
+ struct wl_array *keys)
+{
+ struct keyboard *keyboard = data;
+
+ keyboard->focus = wl_surface_get_user_data(wl_surface);
+
+ fprintf(stderr, "test-client: got keyboard enter, surface %p\n",
+ keyboard->focus);
+}
+
+static void
+keyboard_handle_leave(void *data, struct wl_keyboard *wl_keyboard,
+ uint32_t serial, struct wl_surface *wl_surface)
+{
+ struct keyboard *keyboard = data;
+
+ keyboard->focus = NULL;
+
+ fprintf(stderr, "test-client: got keyboard leave, surface %p\n",
+ wl_surface_get_user_data(wl_surface));
+}
+
+static void
+keyboard_handle_key(void *data, struct wl_keyboard *wl_keyboard,
+ uint32_t serial, uint32_t time, uint32_t key,
+ uint32_t state)
+{
+ struct keyboard *keyboard = data;
+
+ keyboard->key = key;
+ keyboard->state = state;
+
+ fprintf(stderr, "test-client: got keyboard key %u %u\n", key, state);
+}
+
+static void
+keyboard_handle_modifiers(void *data, struct wl_keyboard *wl_keyboard,
+ uint32_t serial, uint32_t mods_depressed,
+ uint32_t mods_latched, uint32_t mods_locked,
+ uint32_t group)
+{
+ struct keyboard *keyboard = data;
+
+ keyboard->mods_depressed = mods_depressed;
+ keyboard->mods_latched = mods_latched;
+ keyboard->mods_locked = mods_locked;
+ keyboard->group = group;
+
+ fprintf(stderr, "test-client: got keyboard modifiers %u %u %u %u\n",
+ mods_depressed, mods_latched, mods_locked, group);
+}
+
+static const struct wl_keyboard_listener keyboard_listener = {
+ keyboard_handle_keymap,
+ keyboard_handle_enter,
+ keyboard_handle_leave,
+ keyboard_handle_key,
+ keyboard_handle_modifiers,
+};
+
+static void
+surface_enter(void *data,
+ struct wl_surface *wl_surface, struct wl_output *output)
+{
+ struct surface *surface = data;
+
+ surface->output = wl_output_get_user_data(output);
+
+ fprintf(stderr, "test-client: got surface enter output %p\n",
+ surface->output);
+}
+
+static void
+surface_leave(void *data,
+ struct wl_surface *wl_surface, struct wl_output *output)
+{
+ struct surface *surface = data;
+
+ surface->output = NULL;
+
+ fprintf(stderr, "test-client: got surface leave output %p\n",
+ wl_output_get_user_data(output));
+}
+
+static const struct wl_surface_listener surface_listener = {
+ surface_enter,
+ surface_leave
+};
+
+struct wl_buffer *
+create_shm_buffer(struct client *client, int width, int height, void **pixels)
+{
+ struct wl_shm *shm = client->wl_shm;
+ int stride = width * 4;
+ int size = stride * height;
+ struct wl_shm_pool *pool;
+ struct wl_buffer *buffer;
+ int fd;
+ void *data;
+
+ fd = os_create_anonymous_file(size);
+ assert(fd >= 0);
+
+ data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (data == MAP_FAILED) {
+ close(fd);
+ assert(data != MAP_FAILED);
+ }
+
+ pool = wl_shm_create_pool(shm, fd, size);
+ buffer = wl_shm_pool_create_buffer(pool, 0, width, height, stride,
+ WL_SHM_FORMAT_ARGB8888);
+ wl_shm_pool_destroy(pool);
+
+ close(fd);
+
+ if (pixels)
+ *pixels = data;
+
+ return buffer;
+}
+
+static void
+shm_format(void *data, struct wl_shm *wl_shm, uint32_t format)
+{
+ struct client *client = data;
+
+ if (format == WL_SHM_FORMAT_ARGB8888)
+ client->has_argb = 1;
+}
+
+struct wl_shm_listener shm_listener = {
+ shm_format
+};
+
+static void
+test_handle_pointer_position(void *data, struct wl_test *wl_test,
+ wl_fixed_t x, wl_fixed_t y)
+{
+ struct test *test = data;
+ test->pointer_x = wl_fixed_to_int(x);
+ test->pointer_y = wl_fixed_to_int(y);
+
+ fprintf(stderr, "test-client: got global pointer %d %d\n",
+ test->pointer_x, test->pointer_y);
+}
+
+static const struct wl_test_listener test_listener = {
+ test_handle_pointer_position
+};
+
+static void
+seat_handle_capabilities(void *data, struct wl_seat *seat,
+ enum wl_seat_capability caps)
+{
+ struct input *input = data;
+ struct pointer *pointer;
+ struct keyboard *keyboard;
+
+ if ((caps & WL_SEAT_CAPABILITY_POINTER) && !input->pointer) {
+ pointer = xzalloc(sizeof *pointer);
+ pointer->wl_pointer = wl_seat_get_pointer(seat);
+ wl_pointer_set_user_data(pointer->wl_pointer, pointer);
+ wl_pointer_add_listener(pointer->wl_pointer, &pointer_listener,
+ pointer);
+ input->pointer = pointer;
+ } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && input->pointer) {
+ wl_pointer_destroy(input->pointer->wl_pointer);
+ free(input->pointer);
+ input->pointer = NULL;
+ }
+
+ if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !input->keyboard) {
+ keyboard = xzalloc(sizeof *keyboard);
+ keyboard->wl_keyboard = wl_seat_get_keyboard(seat);
+ wl_keyboard_set_user_data(keyboard->wl_keyboard, keyboard);
+ wl_keyboard_add_listener(keyboard->wl_keyboard, &keyboard_listener,
+ keyboard);
+ input->keyboard = keyboard;
+ } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && input->keyboard) {
+ wl_keyboard_destroy(input->keyboard->wl_keyboard);
+ free(input->keyboard);
+ input->keyboard = NULL;
+ }
+}
+
+static const struct wl_seat_listener seat_listener = {
+ seat_handle_capabilities,
+};
+
+static void
+output_handle_geometry(void *data,
+ struct wl_output *wl_output,
+ int x, int y,
+ int physical_width,
+ int physical_height,
+ int subpixel,
+ const char *make,
+ const char *model,
+ int32_t transform)
+{
+ struct output *output = data;
+
+ output->x = x;
+ output->y = y;
+}
+
+static void
+output_handle_mode(void *data,
+ struct wl_output *wl_output,
+ uint32_t flags,
+ int width,
+ int height,
+ int refresh)
+{
+ struct output *output = data;
+
+ if (flags & WL_OUTPUT_MODE_CURRENT) {
+ output->width = width;
+ output->height = height;
+ }
+}
+
+static const struct wl_output_listener output_listener = {
+ output_handle_geometry,
+ output_handle_mode
+};
+
+static void
+handle_global(void *data, struct wl_registry *registry,
+ uint32_t id, const char *interface, uint32_t version)
+{
+ struct client *client = data;
+ struct input *input;
+ struct output *output;
+ struct test *test;
+ struct global *global;
+
+ global = xzalloc(sizeof *global);
+ global->name = id;
+ global->interface = strdup(interface);
+ assert(interface);
+ global->version = version;
+ wl_list_insert(client->global_list.prev, &global->link);
+
+ if (strcmp(interface, "wl_compositor") == 0) {
+ client->wl_compositor =
+ wl_registry_bind(registry, id,
+ &wl_compositor_interface, 1);
+ } else if (strcmp(interface, "wl_seat") == 0) {
+ input = xzalloc(sizeof *input);
+ input->wl_seat =
+ wl_registry_bind(registry, id,
+ &wl_seat_interface, 1);
+ wl_seat_add_listener(input->wl_seat, &seat_listener, input);
+ client->input = input;
+ } else if (strcmp(interface, "wl_shm") == 0) {
+ client->wl_shm =
+ wl_registry_bind(registry, id,
+ &wl_shm_interface, 1);
+ wl_shm_add_listener(client->wl_shm, &shm_listener, client);
+ } else if (strcmp(interface, "wl_output") == 0) {
+ output = xzalloc(sizeof *output);
+ output->wl_output =
+ wl_registry_bind(registry, id,
+ &wl_output_interface, 1);
+ wl_output_add_listener(output->wl_output,
+ &output_listener, output);
+ client->output = output;
+ } else if (strcmp(interface, "wl_test") == 0) {
+ test = xzalloc(sizeof *test);
+ test->wl_test =
+ wl_registry_bind(registry, id,
+ &wl_test_interface, 1);
+ wl_test_add_listener(test->wl_test, &test_listener, test);
+ client->test = test;
+ }
+}
+
+static const struct wl_registry_listener registry_listener = {
+ handle_global
+};
+
+static void
+log_handler(const char *fmt, va_list args)
+{
+ fprintf(stderr, "libwayland: ");
+ vfprintf(stderr, fmt, args);
+}
+
+struct client *
+client_create(int x, int y, int width, int height)
+{
+ struct client *client;
+ struct surface *surface;
+
+ wl_log_set_handler_client(log_handler);
+
+ /* connect to display */
+ client = xzalloc(sizeof *client);
+ client->wl_display = wl_display_connect(NULL);
+ assert(client->wl_display);
+ wl_list_init(&client->global_list);
+
+ /* setup registry so we can bind to interfaces */
+ client->wl_registry = wl_display_get_registry(client->wl_display);
+ wl_registry_add_listener(client->wl_registry, &registry_listener, client);
+
+ /* trigger global listener */
+ wl_display_dispatch(client->wl_display);
+ wl_display_roundtrip(client->wl_display);
+
+ /* must have WL_SHM_FORMAT_ARGB32 */
+ assert(client->has_argb);
+
+ /* must have wl_test interface */
+ assert(client->test);
+
+ /* must have an output */
+ assert(client->output);
+
+ /* initialize the client surface */
+ surface = xzalloc(sizeof *surface);
+ surface->wl_surface =
+ wl_compositor_create_surface(client->wl_compositor);
+ assert(surface->wl_surface);
+
+ wl_surface_add_listener(surface->wl_surface, &surface_listener,
+ surface);
+
+ client->surface = surface;
+ wl_surface_set_user_data(surface->wl_surface, surface);
+
+ surface->width = width;
+ surface->height = height;
+ surface->wl_buffer = create_shm_buffer(client, width, height,
+ &surface->data);
+
+ memset(surface->data, 64, width * height * 4);
+
+ move_client(client, x, y);
+
+ return client;
+}
diff --git a/tests/weston-test-client-helper.h b/tests/weston-test-client-helper.h
new file mode 100644
index 00000000..a5edca90
--- /dev/null
+++ b/tests/weston-test-client-helper.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright © 2012 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 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.
+ */
+
+#ifndef _WESTON_TEST_CLIENT_HELPER_H_
+#define _WESTON_TEST_CLIENT_HELPER_H_
+
+#include <assert.h>
+#include "weston-test-runner.h"
+#include "wayland-test-client-protocol.h"
+
+struct client {
+ struct wl_display *wl_display;
+ struct wl_registry *wl_registry;
+ struct wl_compositor *wl_compositor;
+ struct wl_shm *wl_shm;
+ struct test *test;
+ struct input *input;
+ struct output *output;
+ struct surface *surface;
+ int has_argb;
+ struct wl_list global_list;
+};
+
+struct global {
+ uint32_t name;
+ char *interface;
+ uint32_t version;
+ struct wl_list link;
+};
+
+struct test {
+ struct wl_test *wl_test;
+ int pointer_x;
+ int pointer_y;
+};
+
+struct input {
+ struct wl_seat *wl_seat;
+ struct pointer *pointer;
+ struct keyboard *keyboard;
+};
+
+struct pointer {
+ struct wl_pointer *wl_pointer;
+ struct surface *focus;
+ int x;
+ int y;
+ uint32_t button;
+ uint32_t state;
+};
+
+struct keyboard {
+ struct wl_keyboard *wl_keyboard;
+ struct surface *focus;
+ uint32_t key;
+ uint32_t state;
+ uint32_t mods_depressed;
+ uint32_t mods_latched;
+ uint32_t mods_locked;
+ uint32_t group;
+};
+
+struct output {
+ struct wl_output *wl_output;
+ int x;
+ int y;
+ int width;
+ int height;
+};
+
+struct surface {
+ struct wl_surface *wl_surface;
+ struct wl_buffer *wl_buffer;
+ struct output *output;
+ int x;
+ int y;
+ int width;
+ int height;
+ void *data;
+};
+
+struct client *
+client_create(int x, int y, int width, int height);
+
+struct wl_buffer *
+create_shm_buffer(struct client *client, int width, int height, void **pixels);
+
+int
+surface_contains(struct surface *surface, int x, int y);
+
+void
+move_client(struct client *client, int x, int y);
+
+#define client_roundtrip(c) do { \
+ assert(wl_display_roundtrip((c)->wl_display) >= 0); \
+} while (0)
+
+struct wl_callback *
+frame_callback_set(struct wl_surface *surface, int *done);
+
+void
+frame_callback_wait(struct client *client, int *done);
+
+#endif
diff --git a/tests/weston-test-runner.c b/tests/weston-test-runner.c
new file mode 100644
index 00000000..4274b393
--- /dev/null
+++ b/tests/weston-test-runner.c
@@ -0,0 +1,167 @@
+/*
+ * Copyright © 2012 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
+ * 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.
+ */
+
+#include "config.h"
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <signal.h>
+#include "weston-test-runner.h"
+
+extern const struct weston_test __start_test_section, __stop_test_section;
+
+static const struct weston_test *
+find_test(const char *name)
+{
+ const struct weston_test *t;
+
+ for (t = &__start_test_section; t < &__stop_test_section; t++)
+ if (strcmp(t->name, name) == 0)
+ return t;
+
+ return NULL;
+}
+
+static void
+run_test(const struct weston_test *t, void *data)
+{
+ t->run(data);
+ exit(EXIT_SUCCESS);
+}
+
+static void
+list_tests(void)
+{
+ const struct weston_test *t;
+
+ fprintf(stderr, "Available test names:\n");
+ for (t = &__start_test_section; t < &__stop_test_section; t++)
+ fprintf(stderr, " %s\n", t->name);
+}
+
+static int
+exec_and_report_test(const struct weston_test *t, void *test_data, int iteration)
+{
+ int success = 0;
+ int hardfail = 0;
+ siginfo_t info;
+
+ pid_t pid = fork();
+ assert(pid >= 0);
+
+ if (pid == 0)
+ run_test(t, test_data); /* never returns */
+
+ if (waitid(P_ALL, 0, &info, WEXITED)) {
+ fprintf(stderr, "waitid failed: %m\n");
+ abort();
+ }
+
+ if (test_data)
+ fprintf(stderr, "test \"%s/%i\":\t", t->name, iteration);
+ else
+ fprintf(stderr, "test \"%s\":\t", t->name);
+
+ switch (info.si_code) {
+ case CLD_EXITED:
+ fprintf(stderr, "exit status %d", info.si_status);
+ if (info.si_status == EXIT_SUCCESS)
+ success = 1;
+ break;
+ case CLD_KILLED:
+ case CLD_DUMPED:
+ fprintf(stderr, "signal %d", info.si_status);
+ if (info.si_status != SIGABRT)
+ hardfail = 1;
+ break;
+ }
+
+ if (t->must_fail)
+ success = !success;
+
+ if (success && !hardfail) {
+ fprintf(stderr, ", pass.\n");
+ return 1;
+ } else {
+ fprintf(stderr, ", fail.\n");
+ return 0;
+ }
+}
+
+/* Returns number of tests and number of pass / fail in param args */
+static int
+iterate_test(const struct weston_test *t, int *passed)
+{
+ int i;
+ void *current_test_data = (void *) t->table_data;
+ for (i = 0; i < t->n_elements; ++i, current_test_data += t->element_size)
+ {
+ if (exec_and_report_test(t, current_test_data, i))
+ ++(*passed);
+ }
+
+ return t->n_elements;
+}
+
+int main(int argc, char *argv[])
+{
+ const struct weston_test *t;
+ int total = 0;
+ int pass = 0;
+
+ if (argc == 2) {
+ const char *testname = argv[1];
+ if (strcmp(testname, "--help") == 0 ||
+ strcmp(testname, "-h") == 0) {
+ fprintf(stderr, "Usage: %s [test-name]\n", program_invocation_short_name);
+ list_tests();
+ exit(EXIT_SUCCESS);
+ }
+
+ t = find_test(argv[1]);
+ if (t == NULL) {
+ fprintf(stderr, "unknown test: \"%s\"\n", argv[1]);
+ list_tests();
+ exit(EXIT_FAILURE);
+ }
+
+ int number_passed_in_test = 0;
+ total += iterate_test(t, &number_passed_in_test);
+ pass += number_passed_in_test;
+ } else {
+ for (t = &__start_test_section; t < &__stop_test_section; t++) {
+ int number_passed_in_test = 0;
+ total += iterate_test(t, &number_passed_in_test);
+ pass += number_passed_in_test;
+ }
+ }
+
+ fprintf(stderr, "%d tests, %d pass, %d fail\n",
+ total, pass, total - pass);
+
+ return pass == total ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/tests/weston-test-runner.h b/tests/weston-test-runner.h
new file mode 100644
index 00000000..457cf31c
--- /dev/null
+++ b/tests/weston-test-runner.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright © 2012 Intel Corporation
+ * Copyright © 2013 Sam Spilsbury <smspillaz@gmail.com>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that 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.
+ */
+
+#ifndef _WESTON_TEST_RUNNER_H_
+#define _WESTON_TEST_RUNNER_H_
+
+#include <stdlib.h>
+
+#ifdef NDEBUG
+#error "Tests must not be built with NDEBUG defined, they rely on assert()."
+#endif
+
+struct weston_test {
+ const char *name;
+ void (*run)(void *);
+ const void *table_data;
+ size_t element_size;
+ int n_elements;
+ int must_fail;
+} __attribute__ ((aligned (32)));
+
+#define TEST_BEGIN(name, arg) \
+ static void name(arg)
+
+#define TEST_COMMON(func, name, ret, data, size, n_elem) \
+ static void func(void *); \
+ \
+ const struct weston_test test##name \
+ __attribute__ ((section ("test_section"))) = \
+ { \
+ #name, func, data, size, n_elem, ret \
+ };
+
+#define NO_ARG_TEST(name, ret) \
+ TEST_COMMON(wrap##name, name, ret, NULL, 0, 1) \
+ static void name(void); \
+ static void wrap##name(void *data) \
+ { \
+ (void) data; \
+ name(); \
+ } \
+ \
+ TEST_BEGIN(name, void)
+
+#define ARG_TEST(name, ret, test_data) \
+ TEST_COMMON(name, name, ret, test_data, \
+ sizeof(test_data[0]), \
+ sizeof(test_data) / sizeof (test_data[0])) \
+ TEST_BEGIN(name, void *data) \
+
+#define TEST(name) NO_ARG_TEST(name, 0)
+#define FAIL_TEST(name) NO_ARG_TEST(name, 1)
+#define TEST_P(name, data) ARG_TEST(name, 0, data)
+#define FAIL_TEST_P(name, data) ARG_TEST(name, 1, data)
+
+#endif
diff --git a/tests/weston-test.c b/tests/weston-test.c
new file mode 100644
index 00000000..bc5b6e9d
--- /dev/null
+++ b/tests/weston-test.c
@@ -0,0 +1,250 @@
+/*
+ * Copyright © 2012 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 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <signal.h>
+#include <unistd.h>
+#include "../src/compositor.h"
+#include "wayland-test-server-protocol.h"
+
+struct weston_test {
+ struct weston_compositor *compositor;
+ struct weston_layer layer;
+ struct weston_process process;
+};
+
+struct weston_test_surface {
+ struct weston_surface *surface;
+ int32_t x, y;
+ struct weston_test *test;
+};
+
+static void
+test_client_sigchld(struct weston_process *process, int status)
+{
+ struct weston_test *test =
+ container_of(process, struct weston_test, process);
+
+ assert(status == 0);
+
+ wl_display_terminate(test->compositor->wl_display);
+}
+
+static struct weston_seat *
+get_seat(struct weston_test *test)
+{
+ struct wl_list *seat_list;
+ struct weston_seat *seat;
+
+ seat_list = &test->compositor->seat_list;
+ assert(wl_list_length(seat_list) == 1);
+ seat = container_of(seat_list->next, struct weston_seat, link);
+
+ return seat;
+}
+
+static void
+notify_pointer_position(struct weston_test *test, struct wl_resource *resource)
+{
+ struct weston_seat *seat = get_seat(test);
+ struct weston_pointer *pointer = seat->pointer;
+
+ wl_test_send_pointer_position(resource, pointer->x, pointer->y);
+}
+
+static void
+test_surface_configure(struct weston_surface *surface, int32_t sx, int32_t sy, int32_t width, int32_t height)
+{
+ struct weston_test_surface *test_surface = surface->configure_private;
+ struct weston_test *test = test_surface->test;
+
+ if (wl_list_empty(&surface->layer_link))
+ wl_list_insert(&test->layer.surface_list,
+ &surface->layer_link);
+
+ weston_surface_configure(surface, test_surface->x, test_surface->y,
+ width, height);
+
+ if (!weston_surface_is_mapped(surface))
+ weston_surface_update_transform(surface);
+}
+
+static void
+move_surface(struct wl_client *client, struct wl_resource *resource,
+ struct wl_resource *surface_resource,
+ int32_t x, int32_t y)
+{
+ struct weston_surface *surface =
+ wl_resource_get_user_data(surface_resource);
+ struct weston_test_surface *test_surface;
+
+ surface->configure = test_surface_configure;
+ if (surface->configure_private == NULL)
+ surface->configure_private = malloc(sizeof *test_surface);
+ test_surface = surface->configure_private;
+ if (test_surface == NULL) {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ test_surface->surface = surface;
+ test_surface->test = wl_resource_get_user_data(resource);
+ test_surface->x = x;
+ test_surface->y = y;
+}
+
+static void
+move_pointer(struct wl_client *client, struct wl_resource *resource,
+ int32_t x, int32_t y)
+{
+ struct weston_test *test = wl_resource_get_user_data(resource);
+ struct weston_seat *seat = get_seat(test);
+ struct weston_pointer *pointer = seat->pointer;
+
+ test->compositor->focus = 1;
+
+ notify_motion(seat, 100,
+ wl_fixed_from_int(x) - pointer->x,
+ wl_fixed_from_int(y) - pointer->y);
+
+ notify_pointer_position(test, resource);
+}
+
+static void
+send_button(struct wl_client *client, struct wl_resource *resource,
+ int32_t button, uint32_t state)
+{
+ struct weston_test *test = wl_resource_get_user_data(resource);
+ struct weston_seat *seat = get_seat(test);
+
+ test->compositor->focus = 1;
+
+ notify_button(seat, 100, button, state);
+}
+
+static void
+activate_surface(struct wl_client *client, struct wl_resource *resource,
+ struct wl_resource *surface_resource)
+{
+ struct weston_surface *surface = surface_resource ?
+ wl_resource_get_user_data(surface_resource) : NULL;
+ struct weston_test *test = wl_resource_get_user_data(resource);
+ struct weston_seat *seat;
+
+ seat = get_seat(test);
+
+ if (surface) {
+ weston_surface_activate(surface, seat);
+ notify_keyboard_focus_in(seat, &seat->keyboard->keys,
+ STATE_UPDATE_AUTOMATIC);
+ }
+ else {
+ notify_keyboard_focus_out(seat);
+ weston_surface_activate(surface, seat);
+ }
+}
+
+static void
+send_key(struct wl_client *client, struct wl_resource *resource,
+ uint32_t key, enum wl_keyboard_key_state state)
+{
+ struct weston_test *test = wl_resource_get_user_data(resource);
+ struct weston_seat *seat = get_seat(test);
+
+ test->compositor->focus = 1;
+
+ notify_key(seat, 100, key, state, STATE_UPDATE_AUTOMATIC);
+}
+
+static const struct wl_test_interface test_implementation = {
+ move_surface,
+ move_pointer,
+ send_button,
+ activate_surface,
+ send_key
+};
+
+static void
+bind_test(struct wl_client *client, void *data, uint32_t version, uint32_t id)
+{
+ struct weston_test *test = data;
+ struct wl_resource *resource;
+
+ resource = wl_resource_create(client, &wl_test_interface, 1, id);
+ wl_resource_set_implementation(resource,
+ &test_implementation, test, NULL);
+
+ notify_pointer_position(test, resource);
+}
+
+static void
+idle_launch_client(void *data)
+{
+ struct weston_test *test = data;
+ pid_t pid;
+ sigset_t allsigs;
+ char *path;
+
+ path = getenv("WESTON_TEST_CLIENT_PATH");
+ if (path == NULL)
+ exit(EXIT_FAILURE);
+ pid = fork();
+ if (pid == -1)
+ exit(EXIT_FAILURE);
+ if (pid == 0) {
+ sigfillset(&allsigs);
+ sigprocmask(SIG_UNBLOCK, &allsigs, NULL);
+ execl(path, path, NULL);
+ weston_log("compositor: executing '%s' failed: %m\n", path);
+ exit(EXIT_FAILURE);
+ }
+
+ test->process.pid = pid;
+ test->process.cleanup = test_client_sigchld;
+ weston_watch_process(&test->process);
+}
+
+WL_EXPORT int
+module_init(struct weston_compositor *ec,
+ int *argc, char *argv[])
+{
+ struct weston_test *test;
+ struct wl_event_loop *loop;
+
+ test = zalloc(sizeof *test);
+ if (test == NULL)
+ return -1;
+
+ test->compositor = ec;
+ weston_layer_init(&test->layer, &ec->cursor_layer.link);
+
+ if (wl_global_create(ec->wl_display, &wl_test_interface, 1,
+ test, bind_test) == NULL)
+ return -1;
+
+ loop = wl_display_get_event_loop(ec->wl_display);
+ wl_event_loop_add_idle(loop, idle_launch_client, test);
+
+ return 0;
+}
diff --git a/tests/weston-tests-env b/tests/weston-tests-env
new file mode 100755
index 00000000..b7322507
--- /dev/null
+++ b/tests/weston-tests-env
@@ -0,0 +1,43 @@
+#!/bin/bash
+
+TESTNAME=$1
+
+if test -z "$TESTNAME"; then
+ echo "usage: $(basename $0) <test name>"
+ exit 1;
+fi
+
+WESTON=$abs_builddir/../src/weston
+LOGDIR=$abs_builddir/logs
+
+mkdir -p "$LOGDIR"
+
+SERVERLOG="$LOGDIR/$1-serverlog.txt"
+OUTLOG="$LOGDIR/$1-log.txt"
+
+rm -f "$SERVERLOG"
+
+if test x$WAYLAND_DISPLAY != x; then
+ BACKEND=$abs_builddir/../src/.libs/wayland-backend.so
+elif test x$DISPLAY != x; then
+ BACKEND=$abs_builddir/../src/.libs/x11-backend.so
+else
+ BACKEND=$abs_builddir/../src/.libs/wayland-backend.so
+fi
+
+case $TESTNAME in
+ *.la|*.so)
+ $WESTON --backend=$BACKEND \
+ --socket=test-$(basename $TESTNAME) \
+ --modules=$abs_builddir/.libs/${TESTNAME/.la/.so},xwayland.so \
+ --log="$SERVERLOG" \
+ &> "$OUTLOG"
+ ;;
+ *)
+ WESTON_TEST_CLIENT_PATH=$abs_builddir/$TESTNAME $WESTON \
+ --socket=test-$(basename $TESTNAME) \
+ --backend=$BACKEND \
+ --log="$SERVERLOG" \
+ --modules=$abs_builddir/.libs/weston-test.so,xwayland.so \
+ &> "$OUTLOG"
+esac
diff --git a/tests/xwayland-test.c b/tests/xwayland-test.c
new file mode 100644
index 00000000..658453f3
--- /dev/null
+++ b/tests/xwayland-test.c
@@ -0,0 +1,142 @@
+/*
+ * Copyright © 2013 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 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.
+ *
+ * Author: Tiago Vignatti
+ *
+ * xwayland-test: the idea is to guarantee that XWayland infrastructure in
+ * general works with Weston.
+ */
+
+#include "weston-test-runner.h"
+
+#include <assert.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <xcb/xcb.h>
+#include <xcb/dri2.h>
+#include <xf86drm.h>
+
+
+static int
+dri2_open(xcb_connection_t *c, xcb_screen_t *screen)
+{
+ xcb_dri2_connect_cookie_t cookie;
+ xcb_dri2_connect_reply_t *reply;
+ xcb_dri2_authenticate_cookie_t cookie_auth;
+ xcb_dri2_authenticate_reply_t *reply_auth;
+ char *driver, *device;
+ int fd;
+ drm_magic_t magic;
+
+ cookie = xcb_dri2_connect(c, screen->root, XCB_DRI2_DRIVER_TYPE_DRI);
+ reply = xcb_dri2_connect_reply(c, cookie, 0);
+ assert(reply);
+
+ driver = strndup(xcb_dri2_connect_driver_name (reply),
+ xcb_dri2_connect_driver_name_length (reply));
+ device = strndup(xcb_dri2_connect_device_name (reply),
+ xcb_dri2_connect_device_name_length (reply));
+
+ fd = open(device, O_RDWR);
+ printf ("Trying connect to %s driver on %s\n", driver, device);
+ free(driver);
+ free(device);
+
+ if (fd < 0)
+ return -1;
+
+ drmGetMagic(fd, &magic);
+
+ cookie_auth = xcb_dri2_authenticate(c, screen->root, magic);
+ reply_auth = xcb_dri2_authenticate_reply(c, cookie_auth, 0);
+ assert(reply_auth);
+
+ return fd;
+}
+
+static int
+create_window(void)
+{
+ xcb_connection_t *c;
+ xcb_screen_t *screen;
+ xcb_window_t win;
+ int fd;
+
+ c = xcb_connect (NULL, NULL);
+ if (c == NULL) {
+ printf("failed to get X11 connection\n");
+ return -1;
+ }
+
+ screen = xcb_setup_roots_iterator(xcb_get_setup(c)).data;
+
+ win = xcb_generate_id(c);
+ xcb_create_window(c, XCB_COPY_FROM_PARENT, win, screen->root,
+ 0, 0, 150, 150, 1, XCB_WINDOW_CLASS_INPUT_OUTPUT,
+ screen->root_visual, 0, NULL);
+
+ xcb_change_property (c, XCB_PROP_MODE_REPLACE, win,
+ XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8,
+ 5, "title");
+ xcb_map_window(c, win);
+ xcb_flush(c);
+
+ fd = dri2_open(c, screen);
+ if (fd < 0)
+ return -1;
+
+ xcb_destroy_window(c, win);
+ xcb_disconnect(c);
+ return 0;
+}
+
+/*
+ * Ideally, the X Window Manager (XWM) and Weston Wayland compositor shouldn't
+ * be in the same process because they are using two different protocol
+ * streams in which one does not interface with the other. Probably the
+ * biggest problem with such architecture are the potentials dead locks that
+ * it may occur. So hypothetically, an X client might issue an X11 blocking
+ * request via X (DRI2Authenticate) which in turn sends a Wayland blocking
+ * request for Weston process it. X is blocked. At the same time, XWM might be
+ * trying to process an XChangeProperty, so it requests a blocking X11 call to
+ * the X server (xcb_get_property_reply -> xcb_wait_for_reply) which therefore
+ * will blocks there. It's a deadlock situation and this test is trying to
+ * catch that.
+ */
+static void
+check_dri2_authenticate(void)
+{
+ int i, num_tests;
+
+ /* TODO: explain why num_tests times */
+ num_tests = 10;
+ for (i = 0; i < num_tests; i++)
+ assert(create_window() == 0);
+}
+
+TEST(xwayland_client_test)
+{
+ check_dri2_authenticate();
+ exit(EXIT_SUCCESS);
+}
diff --git a/wayland-scanner.mk b/wayland-scanner.mk
new file mode 100644
index 00000000..0a72062b
--- /dev/null
+++ b/wayland-scanner.mk
@@ -0,0 +1,8 @@
+%-protocol.c : $(wayland_protocoldir)/%.xml
+ $(AM_V_GEN)$(wayland_scanner) code < $< > $@
+
+%-server-protocol.h : $(wayland_protocoldir)/%.xml
+ $(AM_V_GEN)$(wayland_scanner) server-header < $< > $@
+
+%-client-protocol.h : $(wayland_protocoldir)/%.xml
+ $(AM_V_GEN)$(wayland_scanner) client-header < $< > $@
diff --git a/wcap/.gitignore b/wcap/.gitignore
new file mode 100644
index 00000000..67b722d1
--- /dev/null
+++ b/wcap/.gitignore
@@ -0,0 +1,3 @@
+wcap-decode
+wcap-snapshot
+
diff --git a/wcap/Makefile.am b/wcap/Makefile.am
new file mode 100644
index 00000000..338208e3
--- /dev/null
+++ b/wcap/Makefile.am
@@ -0,0 +1,9 @@
+bin_PROGRAMS = wcap-decode
+
+wcap_decode_SOURCES = \
+ main.c \
+ wcap-decode.c \
+ wcap-decode.h
+
+wcap_decode_CFLAGS = $(GCC_CFLAGS) $(WCAP_CFLAGS)
+wcap_decode_LDADD = $(WCAP_LIBS)
diff --git a/wcap/README b/wcap/README
new file mode 100644
index 00000000..0994a1bb
--- /dev/null
+++ b/wcap/README
@@ -0,0 +1,99 @@
+WCAP Tools
+
+WCAP is the video capture format used by Weston (Weston CAPture).
+It's a simple, lossless format, that encodes the difference between
+frames as run-length encoded rectangles. It's a variable framerate
+format, that only records new frames along with a timestamp when
+something actually changes.
+
+Recording in Weston is started by pressing MOD+R and stopped by
+pressing MOD+R again. Currently this leaves a capture.wcap file in
+the cwd of the weston process. The file format is documented below
+and Weston comes with the wcap-decode tool to convert the wcap file
+into something more usable:
+
+ - Extract single or all frames as individual png files. This will
+ produce a lossless screenshot, which is useful if you're trying to
+ screenshot a brief glitch or something like that that's hard to
+ capture with the screenshot tool.
+
+ wcap-decode takes a number of options and a wcap file as its
+ arguments. Without anything else, it will show the screen size and
+ number of frames in the file. Pass --frame=<frame> to extract a
+ single frame or pass --all to extract all frames as png files:
+
+ [krh@minato weston]$ wcap-snapshot capture.wcap
+ wcap file: size 1024x640, 176 frames
+ [krh@minato weston]$ wcap-snapshot capture.wcap 20
+ wrote wcap-frame-20.png
+ wcap file: size 1024x640, 176 frames
+
+ - Decode and the wcap file and dump it as a YUV4MPEG2 stream on
+ stdout. This format is compatible with most video encoders and can
+ be piped directly into a command line encoder such as vpxenc (part
+ of libvpx, encodes to a webm file) or theora_encode (part of
+ libtheora, encodes to a ogg theora file).
+
+ Using vpxenc to encode a webm file would look something like this:
+
+ [krh@minato weston]$ wcap-decode --yuv4mpeg2 ../capture.wcap |
+ vpxenc --target-bitrate=1024 --best -t 4 -o foo.webm -
+
+ where we select target bitrate, pass -t 4 to let vpxenc use
+ multiple threads. To encode to Ogg Theora a command line like this
+ works:
+
+ [krh@minato weston]$ wcap-decode ../capture.wcap --yuv4mpeg2 |
+ theora_encode - -o cap.ogv
+
+
+WCAP File format
+
+The file format has a small header and then just consists of the
+indivial frames. The header is
+
+ uint32_t magic
+ uint32_t format
+ uint32_t width
+ uint32_t height
+
+all CPU endian 32 bit words. The magic number is
+
+ #define WCAP_HEADER_MAGIC 0x57434150
+
+and makes it easy to recognize a wcap file and verify that it's the
+right endian. There are four supported pixel formats:
+
+ #define WCAP_FORMAT_XRGB8888 0x34325258
+ #define WCAP_FORMAT_XBGR8888 0x34324258
+ #define WCAP_FORMAT_RGBX8888 0x34325852
+ #define WCAP_FORMAT_BGRX8888 0x34325842
+
+Each frame has a header:
+
+ uint32_t msecs
+ uint32_t nrects
+
+which specifies a timestamp in ms and the number of rectangles that
+changed since previous frame. The timestamps are typically just a raw
+system timestamp and the first frame doesn't start from 0ms.
+
+A frame consists of a list of rectangles, each of which represents the
+component-wise difference between the previous frame and the current
+using a run-length encoding. The initial frame is decoded against a
+previous frame of all 0x00000000 pixels. Each rectangle starts out
+with
+
+ int32_t x1
+ int32_t y1
+ int32_t x2
+ int32_t y2
+
+followed by (x2 - x1) * (y2 - y1) pixels, run-length encoded. The
+run-length encoding uses the 'X' channel in the pixel format to encode
+the length of the run. That is for WCAP_FORMAT_XRGB8888, for example,
+the length of the run is in the upper 8 bits. For X values 0-0xdf,
+the length is X + 1, for X above or equal to 0xe0, the run length is 1
+<< (X - 0xe0 + 7). That is, a pixel value of 0xe3000100, means that
+the next 1024 pixels differ by RGB(0x00, 0x01, 0x00) from the previous
+pixels.
diff --git a/wcap/main.c b/wcap/main.c
new file mode 100644
index 00000000..1e4605ae
--- /dev/null
+++ b/wcap/main.c
@@ -0,0 +1,304 @@
+/*
+ * Copyright © 2012 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 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.
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <sys/mman.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <assert.h>
+
+#include <cairo.h>
+
+#include "wcap-decode.h"
+
+static void
+write_png(struct wcap_decoder *decoder, const char *filename)
+{
+ cairo_surface_t *surface;
+
+ surface = cairo_image_surface_create_for_data((unsigned char *) decoder->frame,
+ CAIRO_FORMAT_ARGB32,
+ decoder->width,
+ decoder->height,
+ decoder->width * 4);
+ cairo_surface_write_to_png(surface, filename);
+ cairo_surface_destroy(surface);
+}
+
+static inline int
+rgb_to_yuv(uint32_t format, uint32_t p, int *u, int *v)
+{
+ int r, g, b, y;
+
+ switch (format) {
+ case WCAP_FORMAT_XRGB8888:
+ r = (p >> 16) & 0xff;
+ g = (p >> 8) & 0xff;
+ b = (p >> 0) & 0xff;
+ break;
+ case WCAP_FORMAT_XBGR8888:
+ r = (p >> 0) & 0xff;
+ g = (p >> 8) & 0xff;
+ b = (p >> 16) & 0xff;
+ break;
+ default:
+ assert(0);
+ }
+
+ y = (19595 * r + 38469 * g + 7472 * b) >> 16;
+ if (y > 255)
+ y = 255;
+
+ *u += 46727 * (r - y);
+ *v += 36962 * (b - y);
+
+ return y;
+}
+
+static inline
+int clamp_uv(int u)
+{
+ int clamp = (u >> 18) + 128;
+
+ if (clamp < 0)
+ return 0;
+ else if (clamp > 255)
+ return 255;
+ else
+ return clamp;
+}
+
+static void
+convert_to_yv12(struct wcap_decoder *decoder, unsigned char *out)
+{
+ unsigned char *y1, *y2, *u, *v;
+ uint32_t *p1, *p2, *end;
+ int i, u_accum, v_accum, stride0, stride1;
+ uint32_t format = decoder->format;
+
+ stride0 = decoder->width;
+ stride1 = decoder->width / 2;
+ for (i = 0; i < decoder->height; i += 2) {
+ y1 = out + stride0 * i;
+ y2 = y1 + stride0;
+ v = out + stride0 * decoder->height + stride1 * i / 2;
+ u = v + stride1 * decoder->height / 2;
+ p1 = decoder->frame + decoder->width * i;
+ p2 = p1 + decoder->width;
+ end = p1 + decoder->width;
+
+ while (p1 < end) {
+ u_accum = 0;
+ v_accum = 0;
+ y1[0] = rgb_to_yuv(format, p1[0], &u_accum, &v_accum);
+ y1[1] = rgb_to_yuv(format, p1[1], &u_accum, &v_accum);
+ y2[0] = rgb_to_yuv(format, p2[0], &u_accum, &v_accum);
+ y2[1] = rgb_to_yuv(format, p2[1], &u_accum, &v_accum);
+ u[0] = clamp_uv(u_accum);
+ v[0] = clamp_uv(v_accum);
+
+ y1 += 2;
+ p1 += 2;
+ y2 += 2;
+ p2 += 2;
+ u++;
+ v++;
+ }
+ }
+}
+
+static void
+convert_to_yuv444(struct wcap_decoder *decoder, unsigned char *out)
+{
+
+ unsigned char *yp, *up, *vp;
+ uint32_t *rp, *end;
+ int u, v;
+ int i, stride, psize;
+ uint32_t format = decoder->format;
+
+ stride = decoder->width;
+ psize = stride * decoder->height;
+ for (i = 0; i < decoder->height; i++) {
+ yp = out + stride * i;
+ up = yp + (psize * 2);
+ vp = yp + (psize * 1);
+ rp = decoder->frame + decoder->width * i;
+ end = rp + decoder->width;
+ while (rp < end) {
+ u = 0;
+ v = 0;
+ yp[0] = rgb_to_yuv(format, rp[0], &u, &v);
+ up[0] = clamp_uv(u/.3);
+ vp[0] = clamp_uv(v/.3);
+ up++;
+ vp++;
+ yp++;
+ rp++;
+ }
+ }
+}
+
+static void
+output_yuv_frame(struct wcap_decoder *decoder, int depth)
+{
+ static unsigned char *out;
+ int size;
+
+ if (depth == 444) {
+ size = decoder->width * decoder->height * 3;
+ } else {
+ size = decoder->width * decoder->height * 3 / 2;
+ }
+ if (out == NULL)
+ out = malloc(size);
+
+ if (depth == 444) {
+ convert_to_yuv444(decoder, out);
+ } else {
+ convert_to_yv12(decoder, out);
+ }
+
+ printf("FRAME\n");
+ fwrite(out, 1, size, stdout);
+}
+
+static void
+usage(int exit_code)
+{
+ fprintf(stderr, "usage: wcap-decode "
+ "[--help] [--yuv4mpeg2] [--frame=<frame>] [--all] \n"
+ "\t[--rate=<num:denom>] <wcap file>\n\n"
+ "\t--help\t\t\tthis help text\n"
+ "\t--yuv4mpeg2\t\tdump wcap file to stdout in yuv4mpeg2 format\n"
+ "\t--yuv4mpeg2-444\t\tdump wcap file to stdout in yuv4mpeg2 444 format\n"
+ "\t--frame=<frame>\t\twrite out the given frame number as png\n"
+ "\t--all\t\t\twrite all frames as pngs\n"
+ "\t--rate=<num:denom>\treplay frame rate for yuv4mpeg2,\n"
+ "\t\t\t\tspecified as an integer fraction\n\n");
+
+ exit(exit_code);
+}
+
+int main(int argc, char *argv[])
+{
+ struct wcap_decoder *decoder;
+ int i, j, output_frame = -1, yuv4mpeg2 = 0, all = 0, has_frame;
+ int num = 30, denom = 1;
+ char filename[200];
+ char *mode;
+ uint32_t msecs, frame_time, *frame, frame_size;
+
+ for (i = 1, j = 1; i < argc; i++) {
+ if (strcmp(argv[i], "--yuv4mpeg2-444") == 0) {
+ yuv4mpeg2 = 444;
+ } else if (strcmp(argv[i], "--yuv4mpeg2") == 0) {
+ yuv4mpeg2 = 420;
+ } else if (strcmp(argv[i], "--help") == 0) {
+ usage(EXIT_SUCCESS);
+ } else if (strcmp(argv[i], "--all") == 0) {
+ all = 1;
+ } else if (sscanf(argv[i], "--frame=%d", &output_frame) == 1) {
+ ;
+ } else if (sscanf(argv[i], "--rate=%d", &num) == 1) {
+ ;
+ } else if (sscanf(argv[i], "--rate=%d:%d", &num, &denom) == 2) {
+ ;
+ } else if (strcmp(argv[i], "--") == 0) {
+ break;
+ } else if (argv[i][0] == '-') {
+ fprintf(stderr,
+ "unknown option or invalid argument: %s\n", argv[i]);
+ usage(EXIT_FAILURE);
+ } else {
+ argv[j++] = argv[i];
+ }
+ }
+ argc = j;
+
+ if (argc != 2)
+ usage(EXIT_FAILURE);
+ if (denom == 0) {
+ fprintf(stderr, "invalid rate, denom can not be 0\n");
+ exit(EXIT_FAILURE);
+ }
+
+ decoder = wcap_decoder_create(argv[1]);
+
+ if (yuv4mpeg2 && isatty(1)) {
+ fprintf(stderr, "Not dumping yuv4mpeg2 data to terminal. Pipe output to a file or a process.\n");
+ fprintf(stderr, "For example, to encode to webm, use something like\n\n");
+ fprintf(stderr, "\t$ wcap-decode --yuv4mpeg2 ../capture.wcap |\n"
+ "\t\tvpxenc --target-bitrate=1024 --best -t 4 -o foo.webm -\n\n");
+
+ exit(EXIT_FAILURE);
+ }
+
+ if (yuv4mpeg2) {
+ if (yuv4mpeg2 == 444) {
+ mode = "C444";
+ } else {
+ mode = "C420jpeg";
+ }
+ printf("YUV4MPEG2 %s W%d H%d F%d:%d Ip A0:0\n",
+ mode, decoder->width, decoder->height, num, denom);
+ fflush(stdout);
+ }
+
+ i = 0;
+ has_frame = wcap_decoder_get_frame(decoder);
+ msecs = decoder->msecs;
+ frame_time = 1000 * denom / num;
+ frame_size = decoder->width * decoder->height * 4;
+ frame = malloc(frame_size);
+ while (has_frame) {
+ if (decoder->msecs >= msecs)
+ memcpy(frame, decoder->frame, frame_size);
+ if (all || i == output_frame) {
+ snprintf(filename, sizeof filename,
+ "wcap-frame-%d.png", i);
+ write_png(decoder, filename);
+ fprintf(stderr, "wrote %s\n", filename);
+ }
+ if (yuv4mpeg2)
+ output_yuv_frame(decoder, yuv4mpeg2);
+ i++;
+ msecs += frame_time;
+ while (decoder->msecs < msecs && has_frame)
+ has_frame = wcap_decoder_get_frame(decoder);
+ }
+
+ fprintf(stderr, "wcap file: size %dx%d, %d frames\n",
+ decoder->width, decoder->height, i);
+
+ wcap_decoder_destroy(decoder);
+
+ return EXIT_SUCCESS;
+}
diff --git a/wcap/wcap-decode.c b/wcap/wcap-decode.c
new file mode 100644
index 00000000..87d93379
--- /dev/null
+++ b/wcap/wcap-decode.c
@@ -0,0 +1,152 @@
+/*
+ * Copyright © 2012 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 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.
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <sys/mman.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include <cairo.h>
+
+#include "wcap-decode.h"
+
+static void
+wcap_decoder_decode_rectangle(struct wcap_decoder *decoder,
+ struct wcap_rectangle *rect)
+{
+ uint32_t v, *p = decoder->p, *d;
+ int width = rect->x2 - rect->x1, height = rect->y2 - rect->y1;
+ int x, i, j, k, l, count = width * height;
+ unsigned char r, g, b, dr, dg, db;
+
+ d = decoder->frame + (rect->y2 - 1) * decoder->width;
+ x = rect->x1;
+ i = 0;
+ while (i < count) {
+ v = *p++;
+ l = v >> 24;
+ if (l < 0xe0) {
+ j = l + 1;
+ } else {
+ j = 1 << (l - 0xe0 + 7);
+ }
+
+ dr = (v >> 16);
+ dg = (v >> 8);
+ db = (v >> 0);
+ for (k = 0; k < j; k++) {
+ r = (d[x] >> 16) + dr;
+ g = (d[x] >> 8) + dg;
+ b = (d[x] >> 0) + db;
+ d[x] = 0xff000000 | (r << 16) | (g << 8) | b;
+ x++;
+ if (x == rect->x2) {
+ x = rect->x1;
+ d -= decoder->width;
+ }
+ }
+ i += j;
+ }
+
+ if (i != count)
+ printf("rle encoding longer than expected (%d expected %d)\n",
+ i, count);
+
+ decoder->p = p;
+}
+
+int
+wcap_decoder_get_frame(struct wcap_decoder *decoder)
+{
+ struct wcap_rectangle *rects;
+ struct wcap_frame_header *header;
+ uint32_t i;
+
+ if (decoder->p == decoder->end)
+ return 0;
+
+ header = decoder->p;
+ decoder->msecs = header->msecs;
+ decoder->count++;
+
+ rects = (void *) (header + 1);
+ decoder->p = (uint32_t *) (rects + header->nrects);
+ for (i = 0; i < header->nrects; i++)
+ wcap_decoder_decode_rectangle(decoder, &rects[i]);
+
+ return 1;
+}
+
+struct wcap_decoder *
+wcap_decoder_create(const char *filename)
+{
+ struct wcap_decoder *decoder;
+ struct wcap_header *header;
+ int frame_size;
+ struct stat buf;
+
+ decoder = malloc(sizeof *decoder);
+ if (decoder == NULL)
+ return NULL;
+
+ decoder->fd = open(filename, O_RDONLY);
+ if (decoder->fd == -1) {
+ free(decoder);
+ return NULL;
+ }
+
+ fstat(decoder->fd, &buf);
+ decoder->size = buf.st_size;
+ decoder->map = mmap(NULL, decoder->size,
+ PROT_READ, MAP_PRIVATE, decoder->fd, 0);
+
+ header = decoder->map;
+ decoder->format = header->format;
+ decoder->count = 0;
+ decoder->width = header->width;
+ decoder->height = header->height;
+ decoder->p = header + 1;
+ decoder->end = decoder->map + decoder->size;
+
+ frame_size = header->width * header->height * 4;
+ decoder->frame = malloc(frame_size);
+ memset(decoder->frame, 0, frame_size);
+
+ return decoder;
+}
+
+void
+wcap_decoder_destroy(struct wcap_decoder *decoder)
+{
+ munmap(decoder->map, decoder->size);
+ close(decoder->fd);
+ free(decoder->frame);
+ free(decoder);
+}
diff --git a/wcap/wcap-decode.h b/wcap/wcap-decode.h
new file mode 100644
index 00000000..d6304154
--- /dev/null
+++ b/wcap/wcap-decode.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright © 2012 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 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.
+ */
+
+#ifndef _WCAP_DECODE_
+#define _WCAP_DECODE_
+
+#define WCAP_HEADER_MAGIC 0x57434150
+
+#define WCAP_FORMAT_XRGB8888 0x34325258
+#define WCAP_FORMAT_XBGR8888 0x34324258
+#define WCAP_FORMAT_RGBX8888 0x34325852
+#define WCAP_FORMAT_BGRX8888 0x34325842
+
+struct wcap_header {
+ uint32_t magic;
+ uint32_t format;
+ uint32_t width, height;
+};
+
+struct wcap_frame_header {
+ uint32_t msecs;
+ uint32_t nrects;
+};
+
+struct wcap_rectangle {
+ int32_t x1, y1, x2, y2;
+};
+
+struct wcap_decoder {
+ int fd;
+ size_t size;
+ void *map, *p, *end;
+ uint32_t *frame;
+ uint32_t format;
+ uint32_t msecs;
+ uint32_t count;
+ int width, height;
+};
+
+int wcap_decoder_get_frame(struct wcap_decoder *decoder);
+struct wcap_decoder *wcap_decoder_create(const char *filename);
+void wcap_decoder_destroy(struct wcap_decoder *decoder);
+
+#endif
diff --git a/weston.ini b/weston.ini
new file mode 100644
index 00000000..70069316
--- /dev/null
+++ b/weston.ini
@@ -0,0 +1,66 @@
+[core]
+#modules=xwayland.so,cms-colord.so
+#shell=desktop-shell.so
+
+[shell]
+background-image=/usr/share/backgrounds/gnome/Aqua.jpg
+background-color=0xff002244
+background-type=tile
+panel-color=0x90ff0000
+locking=true
+animation=zoom
+startup-animation=fade
+#binding-modifier=ctrl
+#num-workspaces=6
+#cursor-theme=whiteglass
+#cursor-size=24
+
+#lockscreen-icon=/usr/share/icons/gnome/256x256/actions/lock.png
+#lockscreen=/usr/share/backgrounds/gnome/Garden.jpg
+#homescreen=/usr/share/backgrounds/gnome/Blinds.jpg
+#animation=fade
+
+[launcher]
+icon=/usr/share/icons/gnome/24x24/apps/utilities-terminal.png
+path=/usr/bin/gnome-terminal
+
+[launcher]
+icon=/usr/share/icons/gnome/24x24/apps/utilities-terminal.png
+path=/usr/bin/weston-terminal
+
+[launcher]
+icon=/usr/share/icons/hicolor/24x24/apps/google-chrome.png
+path=/usr/bin/google-chrome
+
+[launcher]
+icon=/usr/share/icons/gnome/24x24/apps/arts.png
+path=./clients/weston-flower
+
+[screensaver]
+# Uncomment path to disable screensaver
+path=/usr/libexec/weston-screensaver
+duration=600
+
+[input-method]
+path=/usr/libexec/weston-keyboard
+
+#[output]
+#name=LVDS1
+#mode=1680x1050
+#transform=90
+#icc_profile=/usr/share/color/icc/colord/Bluish.icc
+
+#[output]
+#name=VGA1
+#mode=173.00 1920 2048 2248 2576 1080 1083 1088 1120 -hsync +vsync
+#transform=flipped
+
+#[output]
+#name=X1
+#mode=1024x768
+#transform=flipped-270
+
+#[touchpad]
+#constant_accel_factor = 50
+#min_accel_factor = 0.16
+#max_accel_factor = 1.0