aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--Makefile.in18
-rw-r--r--configure.in28
-rw-r--r--xbmc/windowing/tests/wayland/Makefile.in43
-rw-r--r--xbmc/windowing/tests/wayland/TestEGLNativeTypeWayland.cpp1053
-rw-r--r--xbmc/windowing/tests/wayland/XBMCWaylandTestExtension.cpp792
-rw-r--r--xbmc/windowing/tests/wayland/protocol.xml63
7 files changed, 1996 insertions, 5 deletions
diff --git a/.gitignore b/.gitignore
index 7f59570149..40a084444a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -723,6 +723,10 @@ lib/cmyth/Makefile
# /xbmc/windowing/
/xbmc/windowing/Makefile
/xbmc/windowing/egl/Makefile
+/xbmc/windowing/tests/wayland/xbmc_wayland_test_client_protocol.h
+/xbmc/windowing/tests/wayland/xbmc_wayland_test_protocol.c
+/xbmc/windowing/tests/wayland/xbmc_wayland_test_server_protocol.h
+/xbmc/windowing/tests/wayland/Makefile
# /lib/ffmpeg/
/lib/ffmpeg/config.h
diff --git a/Makefile.in b/Makefile.in
index 3b21d50a72..77cf789abb 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -307,10 +307,20 @@ CHECK_LIBS = xbmc/filesystem/test/filesystemTest.a \
xbmc/utils/test/utilsTest.a \
xbmc/threads/test/threadTest.a \
xbmc/interfaces/python/test/pythonSwigTest.a \
+ xbmc/windowing/tests/wayland/test_wayland.a \
xbmc/test/xbmc-test.a
+
+ifeq (@USE_WAYLAND_TEST_EXTENSION@,1)
+WAYLAND_TEST_MODULE = xbmc/windowing/tests/wayland/xbmc-wayland-test-extension.so
+$(WAYLAND_TEST_MODULE): force
+ $(MAKE) -C $(@D) $(@F)
+CHECK_EXTENSIONS = $(WAYLAND_TEST_MODULE)
+CHECK_LIBADD=@WAYLAND_TEST_LIBS@
+endif
+
CHECK_PROGRAMS = xbmc-test
-CLEAN_FILES += $(CHECK_PROGRAMS)
+CLEAN_FILES += $(CHECK_PROGRAMS) $(CHECK_EXTENSIONS)
all : $(FINAL_TARGETS)
@echo '-----------------------'
@@ -674,7 +684,7 @@ ifeq (1,@GTEST_CONFIGURED@)
check: testsuite
for check_program in $(CHECK_PROGRAMS); do $(CURDIR)/$$check_program; done
-testsuite: $(CHECK_PROGRAMS)
+testsuite: $(CHECK_EXTENSIONS) $(CHECK_PROGRAMS)
testframework: $(GTEST_LIBS)
@@ -688,9 +698,9 @@ $(CHECK_LIBS): force
xbmc-test: $(CHECK_LIBS) $(OBJSXBMC) $(DYNOBJSXBMC) $(NWAOBJSXBMC) $(GTEST_LIBS)
ifeq ($(findstring osx,@ARCH@), osx)
- $(SILENT_LD) $(CXX) $(LDFLAGS) $(GTEST_INCLUDES) -o $@ -Wl,-all_load,-ObjC $(CHECK_LIBS) $(DYNOBJSXBMC) $(NWAOBJSXBMC) $(OBJSXBMC) $(GTEST_LIBS) $(LIBS) -rdynamic
+ $(SILENT_LD) $(CXX) $(LDFLAGS) $(GTEST_INCLUDES) -o $@ -Wl,-all_load,-ObjC $(DYNOBJSXBMC) $(NWAOBJSXBMC) $(OBJSXBMC) $(GTEST_LIBS) $(CHECK_LIBS) $(LIBS) $(CHECK_LIBADD) -rdynamic
else
- $(SILENT_LD) $(CXX) $(CXXFLAGS) $(LDFLAGS) $(GTEST_INCLUDES) -o $@ -Wl,--whole-archive $(CHECK_LIBS) $(DYNOBJSXBMC) $(OBJSXBMC) -Wl,--no-whole-archive $(NWAOBJSXBMC) $(GTEST_LIBS) $(LIBS) -rdynamic
+ $(SILENT_LD) $(CXX) $(CXXFLAGS) $(LDFLAGS) $(GTEST_INCLUDES) -o $@ -Wl,--whole-archive $(DYNOBJSXBMC) $(OBJSXBMC) $(GTEST_LIBS) $(CHECK_LIBS) -Wl,--no-whole-archive $(NWAOBJSXBMC) $(LIBS) $(CHECK_LIBADD) -rdynamic
endif
else
# Give a message that the framework is not configured, but don't fail.
diff --git a/configure.in b/configure.in
index 99319d7e6d..521bd3807a 100644
--- a/configure.in
+++ b/configure.in
@@ -985,12 +985,37 @@ if test "$use_wayland" = "yes" && test "$host_vendor" != "apple"; then
AC_DEFINE([HAVE_WAYLAND], [1], [Define to 1 if you have Wayland libs installed.])
AC_DEFINE([HAVE_XKBCOMMON], [1], [Define to 1 if you have libxkbcommon installed.])
+ # If we are also building with tests then we want to build
+ # wayland tests as well
+ if test "$configure_gtest" = "yes"; then
+ have_weston_sdk=no;
+ PKG_CHECK_MODULES([PIXMAN],
+ [pixman-1],have_pixman=yes,
+ [AC_MSG_WARN($missing_library); have_pixman=no])
+ PKG_CHECK_MODULES([WESTON],
+ [weston >= 1.1.90],[have_weston_sdk=yes],
+ [have_weston_sdk=no; AC_MSG_WARN($missing_library)])
+
+ AC_CHECK_PROG(WAYLAND_SCANNER, wayland-scanner, "wayland-scanner", "no")
+ if test "x$WAYLAND_SCANNER" == "xno"; then
+ AC_MSG_WARN($missing_program)
+ else
+ if test "x$have_weston_sdk" == "xyes" && test "x$have_pixman" = "xyes"; then
+ AC_SUBST(WAYLAND_TEST_INCLUDES,"$WAYLAND_CLIENT_CFLAGS $XKBCOMMON_CFLAGS $PIXMAN_CFLAGS $WESTON_CFLAGS")
+ AC_SUBST(WAYLAND_TEST_LIBS,"$WAYLAND_CLIENT_LIBS $XKBCOMMON_LIBS $PIXMAN_LIBS $WESTON_LIBS")
+ AC_DEFINE([HAVE_WESTON_SDK], [1], [Define to 1 if Weston SDK is installed.])
+ AC_SUBST(USE_WAYLAND_TEST_EXTENSION, 1)
+ fi
+ AC_SUBST(WAYLAND_SCANNER)
+ AC_DEFINE([HAVE_WAYLAND_XBMC_PROTO],[1],["Define to 1 if the wayland test-protocol will be built"])
+ fi
+ fi
+
# Disable SDL and X11 builds
use_sdl=no
use_joystick=no
use_x11=no
-
# Wayland requires the EGL "window system" which in turn only supports
# the OpenGL ES API, so enable gles support
use_gles=yes
@@ -2530,6 +2555,7 @@ OUTPUT_FILES="Makefile \
xbmc/android/jni/Makefile \
xbmc/utils/Makefile \
xbmc/main/Makefile \
+ xbmc/windowing/tests/wayland/Makefile \
project/cmake/xbmc-config.cmake"
if test "$use_skin_touched" = "yes"; then
diff --git a/xbmc/windowing/tests/wayland/Makefile.in b/xbmc/windowing/tests/wayland/Makefile.in
new file mode 100644
index 0000000000..1aadd7b300
--- /dev/null
+++ b/xbmc/windowing/tests/wayland/Makefile.in
@@ -0,0 +1,43 @@
+ifeq (@USE_WAYLAND@,1)
+SRCS = TestEGLNativeTypeWayland.cpp
+
+WAYLAND_TEST_MODULE_PROTOCOL = protocol.xml
+WAYLAND_TEST_MODULE_PROTOCOL_SRCS = xbmc_wayland_test_protocol.c
+WAYLAND_TEST_MODULE_PROTOCOL_GENERATED_SRCS = $(WAYLAND_TEST_MODULE_PROTOCOL_SRCS)
+WAYLAND_TEST_MODULE_PROTOCOL_GENERATED_SRCS += xbmc_wayland_test_server_protocol.h
+WAYLAND_TEST_MODULE_PROTOCOL_GENERATED_SRCS += xbmc_wayland_test_client_protocol.h
+WAYLAND_TEST_MODULE_SRCS = XBMCWaylandTestExtension.cpp
+
+INCLUDES += -I@abs_top_srcdir@/lib/gtest/include -I@WAYLAND_TEST_INCLUDES@
+LIB = test_wayland.a
+
+ifneq (,@WAYLAND_SCANNER@)
+SRCS += $(WAYLAND_TEST_MODULE_PROTOCOL_SRCS)
+endif
+
+CLEAN_FILES += $(WAYLAND_TEST_MODULE_PROTOCOL_GENERATED_SRCS) xbmc-wayland-test-extension.so
+
+include @abs_top_srcdir@/Makefile.include
+-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(patsubst %.h,%.P,$(SRCS))))
+
+ifneq (,@WAYLAND_SCANNER@)
+TestEGLNativeTypeWayland.cpp : xbmc_wayland_test_client_protocol.h
+
+$(WAYLAND_TEST_MODULE_SRCS) : $(WAYLAND_TEST_MODULE_PROTOCOL_GENERATED_SRCS)
+
+xbmc_wayland_test_protocol.c: $(WAYLAND_TEST_MODULE_PROTOCOL)
+ @WAYLAND_SCANNER@ code < $< > $@
+
+xbmc_wayland_test_server_protocol.h: $(WAYLAND_TEST_MODULE_PROTOCOL)
+ @WAYLAND_SCANNER@ server-header < $< > $@
+
+xbmc_wayland_test_client_protocol.h: $(WAYLAND_TEST_MODULE_PROTOCOL)
+ @WAYLAND_SCANNER@ client-header < $< > $@
+
+ifeq (@USE_WAYLAND_TEST_EXTENSION@,1)
+xbmc-wayland-test-extension.so: $(WAYLAND_TEST_MODULE_PROTOCOL_SRCS:.c=.o) $(WAYLAND_TEST_MODULE_SRCS:.cpp=.o)
+ $(SILENT_LD) $(CXX) $(CXXFLAGS) $(LDFLAGS) $(WAYLAND_TEST_LIBS) -shared -o $@ $+ -rdynamic
+endif
+
+endif
+endif
diff --git a/xbmc/windowing/tests/wayland/TestEGLNativeTypeWayland.cpp b/xbmc/windowing/tests/wayland/TestEGLNativeTypeWayland.cpp
new file mode 100644
index 0000000000..9bc4162b66
--- /dev/null
+++ b/xbmc/windowing/tests/wayland/TestEGLNativeTypeWayland.cpp
@@ -0,0 +1,1053 @@
+/*
+* Copyright (C) 2005-2013 Team XBMC
+* http://www.xbmc.org
+*
+* This Program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2, or (at your option)
+* any later version.
+*
+* This Program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with XBMC; see the file COPYING. If not, see
+* <http://www.gnu.org/licenses/>.
+*
+*/
+#define WL_EGL_PLATFORM
+
+#include "system.h"
+
+/* HAVE_WAYLAND is implicit in HAVE_WAYLAND_XBMC_PROTO */
+#if defined(HAVE_WAYLAND_XBMC_PROTO)
+
+#include <sstream>
+#include <stdexcept>
+
+#include <iostream>
+
+#include <boost/array.hpp>
+#include <boost/bind.hpp>
+#include <boost/tokenizer.hpp>
+
+#include <gtest/gtest.h>
+
+#include <unistd.h>
+#include <signal.h>
+
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <wayland-client.h>
+#include <wayland-client-protocol.h>
+#include "xbmc_wayland_test_client_protocol.h"
+
+#include "windowing/egl/wayland/Callback.h"
+#include "windowing/egl/wayland/Display.h"
+#include "windowing/egl/wayland/Registry.h"
+#include "windowing/egl/wayland/Surface.h"
+#include "windowing/egl/EGLNativeTypeWayland.h"
+#include "windowing/WinEvents.h"
+
+#include "windowing/DllWaylandClient.h"
+
+#include "utils/StringUtils.h"
+#include "test/TestUtils.h"
+
+namespace
+{
+static const int DefaultProcessWaitTimeout = 3000; // 3000ms
+}
+
+namespace xbmc
+{
+namespace test
+{
+namespace wayland
+{
+class XBMCWayland :
+ boost::noncopyable
+{
+public:
+
+ XBMCWayland(struct xbmc_wayland *xbmcWayland);
+ ~XBMCWayland();
+
+ struct wl_surface * MostRecentSurface();
+
+ void AddMode(int width,
+ int height,
+ uint32_t refresh,
+ enum wl_output_mode mode);
+ void MovePointerTo(struct wl_surface *surface,
+ wl_fixed_t x,
+ wl_fixed_t y);
+ void SendButtonTo(struct wl_surface *surface,
+ uint32_t button,
+ uint32_t state);
+ void SendAxisTo(struct wl_surface *,
+ uint32_t axis,
+ wl_fixed_t value);
+ void SendKeyToKeyboard(struct wl_surface *surface,
+ uint32_t key,
+ enum wl_keyboard_key_state state);
+ void SendModifiersToKeyboard(struct wl_surface *surface,
+ uint32_t depressed,
+ uint32_t latched,
+ uint32_t locked,
+ uint32_t group);
+ void GiveSurfaceKeyboardFocus(struct wl_surface *surface);
+ void PingSurface (struct wl_surface *surface,
+ uint32_t serial);
+
+private:
+
+ struct xbmc_wayland *m_xbmcWayland;
+};
+}
+
+class Process :
+ boost::noncopyable
+{
+public:
+
+ Process(const CStdString &base,
+ const CStdString &socket);
+ ~Process();
+
+ void WaitForSignal(int signal, int timeout);
+ void WaitForStatus(int status, int timeout);
+
+ void Interrupt();
+ void Terminate();
+ void Kill();
+
+ pid_t Pid();
+
+private:
+
+ void SendSignal(int signal);
+
+ void Child(const char *program,
+ char * const *options);
+ void ForkError();
+ void Parent();
+
+ pid_t m_pid;
+};
+}
+}
+
+namespace xt = xbmc::test;
+namespace xw = xbmc::wayland;
+namespace xtw = xbmc::test::wayland;
+namespace xwe = xbmc::wayland::events;
+
+xtw::XBMCWayland::XBMCWayland(struct xbmc_wayland *xbmcWayland) :
+ m_xbmcWayland(xbmcWayland)
+{
+}
+
+xtw::XBMCWayland::~XBMCWayland()
+{
+ xbmc_wayland_destroy(m_xbmcWayland);
+}
+
+void
+xtw::XBMCWayland::AddMode(int width,
+ int height,
+ uint32_t refresh,
+ enum wl_output_mode flags)
+{
+ xbmc_wayland_add_mode(m_xbmcWayland,
+ width,
+ height,
+ refresh,
+ static_cast<uint32_t>(flags));
+}
+
+void
+xtw::XBMCWayland::MovePointerTo(struct wl_surface *surface,
+ wl_fixed_t x,
+ wl_fixed_t y)
+{
+ xbmc_wayland_move_pointer_to_on_surface(m_xbmcWayland,
+ surface,
+ x,
+ y);
+}
+
+void
+xtw::XBMCWayland::SendButtonTo(struct wl_surface *surface,
+ uint32_t button,
+ uint32_t state)
+{
+ xbmc_wayland_send_button_to_surface(m_xbmcWayland,
+ surface,
+ button,
+ state);
+}
+
+void
+xtw::XBMCWayland::SendAxisTo(struct wl_surface *surface,
+ uint32_t axis,
+ wl_fixed_t value)
+{
+ xbmc_wayland_send_axis_to_surface(m_xbmcWayland,
+ surface,
+ axis,
+ value);
+}
+
+void
+xtw::XBMCWayland::SendKeyToKeyboard(struct wl_surface *surface,
+ uint32_t key,
+ enum wl_keyboard_key_state state)
+{
+ xbmc_wayland_send_key_to_keyboard(m_xbmcWayland,
+ surface,
+ key,
+ state);
+}
+
+void
+xtw::XBMCWayland::SendModifiersToKeyboard(struct wl_surface *surface,
+ uint32_t depressed,
+ uint32_t latched,
+ uint32_t locked,
+ uint32_t group)
+{
+ xbmc_wayland_send_modifiers_to_keyboard(m_xbmcWayland,
+ surface,
+ depressed,
+ latched,
+ locked,
+ group);
+}
+
+void
+xtw::XBMCWayland::GiveSurfaceKeyboardFocus(struct wl_surface *surface)
+{
+ xbmc_wayland_give_surface_keyboard_focus(m_xbmcWayland,
+ surface);
+}
+
+void
+xtw::XBMCWayland::PingSurface(struct wl_surface *surface,
+ uint32_t serial)
+{
+ xbmc_wayland_ping_surface(m_xbmcWayland, surface, serial);
+}
+
+namespace
+{
+class TempFileWrapper :
+ boost::noncopyable
+{
+public:
+
+ TempFileWrapper(const CStdString &suffix);
+ ~TempFileWrapper();
+
+ void FetchDirectory(CStdString &directory);
+ void FetchFilename(CStdString &name);
+private:
+
+ XFILE::CFile *m_file;
+};
+
+TempFileWrapper::TempFileWrapper(const CStdString &suffix) :
+ m_file(CXBMCTestUtils::Instance().CreateTempFile(suffix))
+{
+}
+
+TempFileWrapper::~TempFileWrapper()
+{
+ CXBMCTestUtils::Instance().DeleteTempFile(m_file);
+}
+
+void TempFileWrapper::FetchDirectory(CStdString &directory)
+{
+ directory = CXBMCTestUtils::Instance().TempFileDirectory(m_file);
+ /* Strip trailing "/" */
+ directory.resize(directory.size() - 1);
+}
+
+void TempFileWrapper::FetchFilename(CStdString &name)
+{
+ CStdString path(CXBMCTestUtils::Instance().TempFilePath(m_file));
+ CStdString directory(CXBMCTestUtils::Instance().TempFileDirectory(m_file));
+
+ name = path.substr(directory.size());
+}
+
+class SavedTempSocket :
+ boost::noncopyable
+{
+public:
+
+ SavedTempSocket();
+
+ const CStdString & FetchFilename();
+ const CStdString & FetchDirectory();
+
+private:
+
+ CStdString m_filename;
+ CStdString m_directory;
+};
+
+SavedTempSocket::SavedTempSocket()
+{
+ TempFileWrapper wrapper("");
+ wrapper.FetchDirectory(m_directory);
+ wrapper.FetchFilename(m_filename);
+}
+
+const CStdString &
+SavedTempSocket::FetchFilename()
+{
+ return m_filename;
+}
+
+const CStdString &
+SavedTempSocket::FetchDirectory()
+{
+ return m_directory;
+}
+
+class TmpEnv :
+ boost::noncopyable
+{
+public:
+
+ TmpEnv(const char *env, const char *val);
+ ~TmpEnv();
+
+private:
+
+ const char *m_env;
+ const char *m_previous;
+};
+
+TmpEnv::TmpEnv(const char *env,
+ const char *val) :
+ m_env(env),
+ m_previous(getenv(env))
+{
+ setenv(env, val, 1);
+}
+
+TmpEnv::~TmpEnv()
+{
+ if (m_previous)
+ setenv(m_env, m_previous, 1);
+ else
+ unsetenv(m_env);
+}
+
+std::string
+FindBinaryFromPATH(const std::string &binary)
+{
+ const char *pathEnvironmentCArray = getenv("PATH");
+ if (!pathEnvironmentCArray)
+ throw std::runtime_error("PATH is not set");
+
+ std::string pathEnvironment(pathEnvironmentCArray);
+
+ typedef boost::char_separator<char> CharSeparator;
+ typedef boost::tokenizer<CharSeparator> CharTokenizer;
+
+ CharTokenizer paths(pathEnvironment,
+ CharSeparator(":"));
+
+ for (CharTokenizer::iterator it = paths.begin();
+ it != paths.end();
+ ++it)
+ {
+ std::stringstream possibleBinaryLocationStream;
+ possibleBinaryLocationStream << *it
+ << "/"
+ << binary;
+ std::string possibleBinaryLocation(possibleBinaryLocationStream.str());
+ int ok = access(possibleBinaryLocation.c_str(), X_OK);
+
+ if (ok == -1)
+ {
+ switch (errno)
+ {
+ case EACCES:
+ case ENOENT:
+ continue;
+ default:
+ throw std::runtime_error(strerror(errno));
+ }
+ }
+
+ return possibleBinaryLocation.c_str();
+ }
+
+ std::stringstream ss;
+ ss << "Unable to find "
+ << binary
+ << " in PATH as it does not exist or is not executable";
+
+ throw std::runtime_error(ss.str());
+}
+}
+
+xt::Process::Process(const CStdString &xbmcTestBase,
+ const CStdString &tempFileName) :
+ m_pid(0)
+{
+ std::stringstream socketOptionStream;
+ socketOptionStream << "--socket=";
+ socketOptionStream << tempFileName.c_str();
+
+ std::string socketOption(socketOptionStream.str());
+
+ std::stringstream modulesOptionStream;
+ modulesOptionStream << "--modules=";
+ modulesOptionStream << xbmcTestBase.c_str();
+ modulesOptionStream << "xbmc/windowing/tests/wayland/xbmc-wayland-test-extension.so";
+
+ std::string modulesOption(modulesOptionStream.str());
+
+ std::string program(FindBinaryFromPATH("weston"));
+ const char *options[] =
+ {
+ program.c_str(),
+ "--backend=headless-backend.so",
+ modulesOption.c_str(),
+ socketOption.c_str(),
+ NULL
+ };
+
+ m_pid = fork();
+
+ switch (m_pid)
+ {
+ case 0:
+ Child(program.c_str(),
+ const_cast <char * const *>(options));
+ case -1:
+ ForkError();
+ default:
+ Parent();
+ }
+}
+
+pid_t
+xt::Process::Pid()
+{
+ return m_pid;
+}
+
+void
+xt::Process::Child(const char *program,
+ char * const *options)
+{
+ signal(SIGUSR2, SIG_IGN);
+
+ /* Unblock SIGUSR2 */
+ sigset_t signalMask;
+ sigemptyset(&signalMask);
+ sigaddset(&signalMask, SIGUSR2);
+ if (sigprocmask(SIG_UNBLOCK, &signalMask, NULL))
+ {
+ std::stringstream ss;
+ ss << "sigprocmask: " << strerror(errno);
+ throw std::runtime_error(ss.str());
+ }
+
+ if (!getenv("XBMC_WESTON_GTEST_CHILD_STDOUT"))
+ {
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+ }
+
+ if (execvpe(program, options, environ) == -1)
+ {
+ std::stringstream ss;
+ ss << "execvpe: " << strerror(errno);
+ throw std::runtime_error(ss.str());
+ }
+}
+
+void
+xt::Process::Parent()
+{
+}
+
+void
+xt::Process::ForkError()
+{
+ std::stringstream ss;
+ ss << "fork: "
+ << strerror(errno);
+ throw std::runtime_error(ss.str());
+}
+
+void
+xt::Process::WaitForSignal(int signal, int timeout)
+{
+ sigset_t signalMask;
+
+ if (timeout >= 0)
+ {
+ static const uint32_t MsecToNsec = 1000000;
+ static const uint32_t SecToMsec = 1000;
+ int seconds = timeout / SecToMsec;
+
+ /* Remove seconds from timeout */
+ timeout -= seconds * SecToMsec;
+ struct timespec ts = { seconds, timeout * MsecToNsec };
+
+ sigemptyset(&signalMask);
+ sigaddset(&signalMask, signal);
+ int received = 0;
+
+ do
+ {
+ errno = 0;
+ received = sigtimedwait(&signalMask,
+ NULL,
+ &ts);
+ if (received == -1)
+ {
+ /* Just retry if we got signalled */
+ if (errno != EINTR)
+ {
+ std::stringstream ss;
+ ss << "sigtimedwait: "
+ << strerror(errno);
+
+ throw std::runtime_error(ss.str());
+ }
+ }
+ } while (errno != 0);
+
+ return;
+ }
+ else
+ {
+ sigemptyset(&signalMask);
+ sigaddset(&signalMask, signal);
+ errno = 0;
+ int received = sigwaitinfo(&signalMask, NULL);
+
+ if (received != signal)
+ {
+ std::stringstream ss;
+ ss << "sigwaitinfo: "
+ << strerror(errno);
+ }
+ }
+}
+
+namespace
+{
+void
+WestonMisbehaviourMessage(std::stringstream &ss)
+{
+ ss << std::endl;
+ ss << "It is possible that Weston is just shutting down uncleanly "
+ << " - you should check the stacktrace and run with "
+ << " ALLOW_WESTON_MISBEHAVIOUR set to suppress this";
+}
+
+bool
+NoMisbehaviour()
+{
+ return !getenv("ALLOW_WESTON_MISBEHAVIOUR");
+}
+
+class StatusWaitTimeoutError :
+ public std::exception
+{
+public:
+
+ StatusWaitTimeoutError(int expected, int timeout);
+ ~StatusWaitTimeoutError() throw() {}
+
+private:
+
+ const char * what() const throw();
+
+ int m_expected;
+ int m_timeout;
+
+ mutable std::string m_what;
+};
+
+class TerminatedBySignalError :
+ public std::exception
+{
+public:
+
+ TerminatedBySignalError(int expected, int signal);
+ ~TerminatedBySignalError() throw() {}
+
+private:
+
+ const char * what() const throw();
+
+ int m_expected;
+ int m_signal;
+
+ mutable std::string m_what;
+};
+
+class AbnormalExitStatusError :
+ public std::exception
+{
+public:
+
+ AbnormalExitStatusError(int expected, int status);
+ ~AbnormalExitStatusError() throw() {}
+
+private:
+
+ const char * what() const throw();
+
+ int m_expected;
+ int m_status;
+
+ mutable std::string m_what;
+};
+
+StatusWaitTimeoutError::StatusWaitTimeoutError(int expected,
+ int timeout) :
+ m_expected(expected),
+ m_timeout(timeout)
+{
+}
+
+const char *
+StatusWaitTimeoutError::what() const throw()
+{
+ std::stringstream ss;
+ ss << "Expected exit status "
+ << m_expected
+ << " within "
+ << m_timeout
+ << " ms";
+ m_what = ss.str();
+ return m_what.c_str();
+}
+
+TerminatedBySignalError::TerminatedBySignalError(int expected,
+ int signal) :
+ m_expected(expected),
+ m_signal(signal)
+{
+}
+
+const char *
+TerminatedBySignalError::what() const throw()
+{
+ std::stringstream ss;
+ ss << "Expected exit status "
+ << m_expected
+ << " but was instead terminated by signal "
+ << m_signal
+ << " - "
+ << strsignal(m_signal);
+ WestonMisbehaviourMessage(ss);
+ m_what = ss.str();
+ return m_what.c_str();
+}
+
+AbnormalExitStatusError::AbnormalExitStatusError(int expected,
+ int status) :
+ m_expected(expected),
+ m_status(status)
+{
+}
+
+const char *
+AbnormalExitStatusError::what() const throw()
+{
+ std::stringstream ss;
+ ss << "Expected exit status "
+ << m_expected
+ << " but instead exited with "
+ << m_status
+ << " - "
+ << strsignal(m_status);
+ WestonMisbehaviourMessage(ss);
+ m_what = ss.str();
+ return m_what.c_str();
+}
+}
+
+void
+xt::Process::WaitForStatus(int code, int timeout)
+{
+ struct timespec startTime;
+ struct timespec currentTime;
+ clock_gettime(CLOCK_MONOTONIC, &startTime);
+ clock_gettime(CLOCK_MONOTONIC, &currentTime);
+
+ const uint32_t SecToMsec = 1000;
+ const uint32_t MsecToNsec = 1000000;
+
+ int32_t startTimestamp = startTime.tv_sec * SecToMsec +
+ startTime.tv_nsec / MsecToNsec;
+ int32_t currentTimestamp = currentTime.tv_sec * SecToMsec +
+ currentTime.tv_nsec / MsecToNsec;
+
+ int options = WUNTRACED;
+
+ std::stringstream statusMessage;
+
+ if (timeout >= 0)
+ options |= WNOHANG;
+
+ do
+ {
+ clock_gettime(CLOCK_MONOTONIC, &currentTime);
+
+ currentTimestamp = currentTime.tv_sec * SecToMsec +
+ currentTime.tv_nsec / MsecToNsec;
+
+ int returnedStatus;
+ pid_t pid = waitpid(m_pid, &returnedStatus, options);
+
+ if (pid == m_pid)
+ {
+ /* At least one child has exited */
+ if (WIFEXITED(returnedStatus))
+ {
+ int returnedExitCode = WEXITSTATUS(returnedStatus);
+ if (returnedExitCode == code)
+ return;
+
+ /* Abnormal exit status */
+ throw AbnormalExitStatusError(code, returnedExitCode);
+ }
+ else if (WIFSIGNALED(returnedStatus))
+ {
+ int returnedSignal = WTERMSIG(returnedStatus);
+
+ /* Signaled and died */
+ throw TerminatedBySignalError(code, returnedSignal);
+ }
+ }
+ else if (pid == -1)
+ {
+ std::stringstream ss;
+ ss << "waitpid failed: "
+ << strerror(errno);
+ throw std::runtime_error(ss.str());
+ }
+ else if (!pid)
+ {
+ struct timespec ts;
+ ts.tv_sec = 0;
+
+ /* Don't sleep the whole time, we might have just missed
+ * the signal */
+ ts.tv_nsec = timeout * MsecToNsec / 10;
+
+ nanosleep(&ts, NULL);
+ }
+ }
+ while (timeout == -1 ||
+ (timeout > currentTimestamp - startTimestamp));
+
+ /* If we didn't get out early, it means we timed out */
+ throw StatusWaitTimeoutError(code, timeout);
+}
+
+void
+xt::Process::SendSignal(int signal)
+{
+ if (kill(m_pid, signal) == -1)
+ {
+ /* Already dead ... lets see if it exited normally */
+ if (errno == ESRCH)
+ {
+ try
+ {
+ WaitForStatus(0, 0);
+ }
+ catch (std::runtime_error &err)
+ {
+ std::stringstream ss;
+ ss << err.what()
+ << " - process was already dead"
+ << std::endl;
+ throw std::runtime_error(ss.str());
+ }
+ }
+ else
+ {
+ std::stringstream ss;
+ ss << "failed to send signal "
+ << signal
+ << " to process "
+ << m_pid
+ << ": " << strerror(errno);
+ throw std::runtime_error(ss.str());
+ }
+ }
+}
+
+void
+xt::Process::Interrupt()
+{
+ SendSignal(SIGINT);
+}
+
+void
+xt::Process::Terminate()
+{
+ SendSignal(SIGTERM);
+}
+
+void
+xt::Process::Kill()
+{
+ SendSignal(SIGKILL);
+}
+
+xt::Process::~Process()
+{
+ typedef void (Process::*SignalAction)(void);
+ SignalAction deathActions[] =
+ {
+ &Process::Interrupt,
+ &Process::Terminate,
+ &Process::Kill
+ };
+
+ static const size_t deathActionsSize = sizeof(deathActions) /
+ sizeof(deathActions[0]);
+
+ size_t i = 0;
+
+ std::stringstream processStatusMessages;
+
+ for (; i < deathActionsSize; ++i)
+ {
+ try
+ {
+ SignalAction func(deathActions[i]);
+ ((*this).*(func))();
+ WaitForStatus(0, DefaultProcessWaitTimeout);
+ break;
+ }
+ catch (const TerminatedBySignalError &err)
+ {
+ if (NoMisbehaviour())
+ throw;
+ else
+ break;
+ }
+ catch (const AbnormalExitStatusError &err)
+ {
+ if (NoMisbehaviour())
+ throw;
+ else
+ break;
+ }
+ catch (const StatusWaitTimeoutError &err)
+ {
+ processStatusMessages << "[TIMEOUT] "
+ << static_cast<const std::exception &>(err).what()
+ << std::endl;
+ }
+ }
+
+ if (i == deathActionsSize)
+ {
+ std::stringstream ss;
+ ss << "Failed to terminate "
+ << m_pid
+ << std::endl;
+ ss << processStatusMessages;
+ throw std::runtime_error(ss.str());
+ }
+}
+
+namespace
+{
+template <typename Iterator>
+class SignalGuard :
+ boost::noncopyable
+{
+public:
+
+ SignalGuard(const Iterator &begin,
+ const Iterator &end);
+ ~SignalGuard();
+private:
+
+ sigset_t mask;
+};
+
+template <typename Iterator>
+SignalGuard<Iterator>::SignalGuard(const Iterator &begin,
+ const Iterator &end)
+{
+ sigemptyset(&mask);
+ for (Iterator it = begin;
+ it != end;
+ ++it)
+ sigaddset(&mask, *it);
+
+ if (sigprocmask(SIG_BLOCK, &mask, NULL))
+ {
+ std::stringstream ss;
+ ss << "sigprogmask: "
+ << strerror(errno);
+ throw std::runtime_error(ss.str());
+ }
+}
+
+template <typename Iterator>
+SignalGuard<Iterator>::~SignalGuard()
+{
+ if (sigprocmask(SIG_UNBLOCK, &mask, NULL))
+ CLog::Log(LOGERROR, "Failed to unblock signals");
+}
+
+typedef boost::array<int, 4> SigArray;
+SigArray BlockedSignals =
+{
+ {
+ SIGUSR2,
+ SIGCHLD
+ }
+};
+}
+
+class WestonTest :
+ public ::testing::Test
+{
+public:
+
+ WestonTest();
+ pid_t Pid();
+
+ virtual void SetUp();
+
+protected:
+
+ CEGLNativeTypeWayland m_nativeType;
+ xw::Display *m_display;
+ boost::scoped_ptr<xtw::XBMCWayland> m_xbmcWayland;
+ struct wl_surface *m_mostRecentSurface;
+
+ CStdString m_xbmcTestBase;
+ SavedTempSocket m_tempSocketName;
+ TmpEnv m_xdgRuntimeDir;
+
+private:
+
+ void Global(struct wl_registry *, uint32_t, const char *, uint32_t);
+ void DisplayAvailable(xw::Display &display);
+ void SurfaceCreated(xw::Surface &surface);
+
+ SignalGuard<SigArray::iterator> m_signalGuard;
+ xt::Process m_process;
+};
+
+WestonTest::WestonTest() :
+ m_display(NULL),
+ m_mostRecentSurface(NULL),
+ m_xbmcTestBase(CXBMCTestUtils::Instance().ReferenceFilePath("")),
+ /* We want wayland (client and server) to look in our
+ * temp file directory for the socket */
+ m_xdgRuntimeDir("XDG_RUNTIME_DIR", m_tempSocketName.FetchDirectory().c_str()),
+ /* Block emission of SIGUSR2 so that we can wait on it */
+ m_signalGuard(BlockedSignals.begin(), BlockedSignals.end()),
+ m_process(m_xbmcTestBase,
+ m_tempSocketName.FetchFilename())
+{
+}
+
+pid_t
+WestonTest::Pid()
+{
+ return m_process.Pid();
+}
+
+void
+WestonTest::SetUp()
+{
+ m_process.WaitForSignal(SIGUSR2, DefaultProcessWaitTimeout);
+}
+
+void
+WestonTest::Global(struct wl_registry *registry,
+ uint32_t name,
+ const char *interface,
+ uint32_t version)
+{
+ if (std::string(interface) == "xbmc_wayland")
+ m_xbmcWayland.reset(new xtw::XBMCWayland(static_cast<xbmc_wayland *>(wl_registry_bind(registry,
+ name,
+ &xbmc_wayland_interface,
+ version))));
+}
+
+void
+WestonTest::DisplayAvailable(xw::Display &display)
+{
+ m_display = &display;
+}
+
+void
+WestonTest::SurfaceCreated(xw::Surface &surface)
+{
+ m_mostRecentSurface = surface.GetWlSurface();
+}
+
+TEST_F(WestonTest, TestCheckCompatibilityWithEnvSet)
+{
+ TmpEnv env("WAYLAND_DISPLAY", m_tempSocketName.FetchFilename().c_str());
+ EXPECT_TRUE(m_nativeType.CheckCompatibility());
+}
+
+TEST_F(WestonTest, TestCheckCompatibilityWithEnvNotSet)
+{
+ EXPECT_FALSE(m_nativeType.CheckCompatibility());
+}
+
+class CompatibleWestonTest :
+ public WestonTest
+{
+public:
+
+ CompatibleWestonTest();
+ virtual void SetUp();
+
+private:
+
+ TmpEnv m_waylandDisplayEnv;
+};
+
+CompatibleWestonTest::CompatibleWestonTest() :
+ m_waylandDisplayEnv("WAYLAND_DISPLAY",
+ m_tempSocketName.FetchFilename().c_str())
+{
+}
+
+void
+CompatibleWestonTest::SetUp()
+{
+ WestonTest::SetUp();
+ ASSERT_TRUE(m_nativeType.CheckCompatibility());
+}
+
+TEST_F(CompatibleWestonTest, TestConnection)
+{
+ EXPECT_TRUE(m_nativeType.CreateNativeDisplay());
+}
+
+#endif
diff --git a/xbmc/windowing/tests/wayland/XBMCWaylandTestExtension.cpp b/xbmc/windowing/tests/wayland/XBMCWaylandTestExtension.cpp
new file mode 100644
index 0000000000..1520765b66
--- /dev/null
+++ b/xbmc/windowing/tests/wayland/XBMCWaylandTestExtension.cpp
@@ -0,0 +1,792 @@
+/*
+* Copyright (C) 2005-2013 Team XBMC
+* http://www.xbmc.org
+*
+* This Program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2, or (at your option)
+* any later version.
+*
+* This Program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with XBMC; see the file COPYING. If not, see
+* <http://www.gnu.org/licenses/>.
+*
+*/
+#include "system.h"
+
+#include <sstream>
+#include <vector>
+
+#include <boost/scoped_ptr.hpp>
+#include <boost/noncopyable.hpp>
+#include <boost/function.hpp>
+#include <boost/bind.hpp>
+
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+
+extern "C"
+{
+/* Work around usage of a reserved keyword
+ * https://bugs.freedesktop.org/show_bug.cgi?id=63485 */
+#define private cprivate
+#include <weston/compositor.h>
+#undef private
+#include <wayland-server.h>
+#include "xbmc_wayland_test_server_protocol.h"
+}
+
+namespace xbmc
+{
+namespace test
+{
+namespace wayland
+{
+class Listener :
+ boost::noncopyable
+{
+public:
+
+ typedef boost::function<void()> Delegate;
+
+ Listener(const Delegate &);
+ void BindTo(struct wl_signal *);
+
+private:
+
+ static void MainCallback(struct wl_listener *listener, void *data);
+
+ void Callback();
+
+ struct wl_listener m_listener;
+ Delegate m_delegate;
+};
+}
+
+namespace weston
+{
+class Compositor :
+ boost::noncopyable
+{
+public:
+
+ Compositor(struct weston_compositor *);
+ ~Compositor();
+
+ struct wl_display * Display();
+
+ struct weston_surface * TopSurface();
+ struct weston_mode * LastMode();
+ void OnEachMode(const boost::function<void(struct weston_mode *)> &);
+ struct wl_resource * PointerResource(struct wl_client *client);
+ struct wl_resource * KeyboardResource(struct wl_client *client);
+ struct weston_surface * Surface(struct wl_resource *client);
+ struct weston_keyboard * Keyboard();
+
+private:
+
+ static void Unload(Compositor *compositor);
+ static int Ready(void *);
+
+ struct weston_compositor *m_compositor;
+ struct wl_event_source *m_readySource;
+ wayland::Listener m_destroyListener;
+ struct weston_seat * Seat();
+ struct weston_output * FirstOutput();
+};
+}
+
+namespace wayland
+{
+class XBMCWayland :
+ boost::noncopyable
+{
+public:
+
+ ~XBMCWayland();
+
+ struct wl_resource * GetResource();
+
+ /* Effectively a factory function for XBMCWayland. Creates an
+ * instantiation for a client */
+ static void BindToClient(struct wl_client *client,
+ void *data,
+ uint32_t version,
+ uint32_t id);
+
+private:
+
+ /* Constructor is private as this object may only be constructed
+ * by a client binding to its interface */
+ XBMCWayland(struct wl_client *client,
+ uint32_t id,
+ weston::Compositor &compositor);
+
+ static void UnbindFromClientCallback(struct wl_resource *);
+
+ static const struct xbmc_wayland_interface m_listener;
+
+ static void AddModeCallback(struct wl_client *,
+ struct wl_resource *,
+ int32_t,
+ int32_t,
+ uint32_t,
+ uint32_t);
+ static void MovePointerToOnSurfaceCallback(struct wl_client *,
+ struct wl_resource *,
+ struct wl_resource *,
+ wl_fixed_t x,
+ wl_fixed_t y);
+ static void SendButtonToSurfaceCallback(struct wl_client *,
+ struct wl_resource *,
+ struct wl_resource *,
+ uint32_t,
+ uint32_t);
+ static void SendAxisToSurfaceCallback(struct wl_client *,
+ struct wl_resource *,
+ struct wl_resource *,
+ uint32_t,
+ wl_fixed_t);
+ static void SendKeyToKeyboardCallback(struct wl_client *,
+ struct wl_resource *,
+ struct wl_resource *,
+ uint32_t,
+ uint32_t);
+ static void SendModifiersToKeyboardCallback(struct wl_client *,
+ struct wl_resource *,
+ struct wl_resource *,
+ uint32_t,
+ uint32_t,
+ uint32_t,
+ uint32_t);
+ static void GiveSurfaceKeyboardFocusCallback(struct wl_client *,
+ struct wl_resource *,
+ struct wl_resource *);
+ static void PingSurfaceCallback(struct wl_client *,
+ struct wl_resource *,
+ struct wl_resource *,
+ uint32_t timestamp);
+
+ void AddMode(struct wl_client *client,
+ struct wl_resource *resource,
+ int32_t width,
+ int32_t height,
+ uint32_t refresh,
+ uint32_t flags);
+ void MovePointerToOnSurface(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *surface,
+ wl_fixed_t x,
+ wl_fixed_t y);
+ void SendButtonToSurface(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *surface,
+ uint32_t button,
+ uint32_t state);
+ void SendAxisToSurface(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *surface,
+ uint32_t axis,
+ wl_fixed_t value);
+ void SendKeyToKeyboard(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *surfaceResource,
+ uint32_t key,
+ uint32_t state);
+ void SendModifiersToKeyboard(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *surfaceResource,
+ uint32_t depressed,
+ uint32_t latched,
+ uint32_t locked,
+ uint32_t group);
+ void GiveSurfaceKeyboardFocus(struct wl_client *clent,
+ struct wl_resource *resource,
+ struct wl_resource *surfaceResource);
+ void PingSurface(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *surfaceResource,
+ uint32_t timestamp);
+
+ weston::Compositor &m_compositor;
+ struct wl_resource *m_clientXBMCWaylandResource;
+
+ std::vector<struct weston_mode> m_additionalModes;
+};
+
+const struct xbmc_wayland_interface XBMCWayland::m_listener =
+{
+ XBMCWayland::AddModeCallback,
+ XBMCWayland::MovePointerToOnSurfaceCallback,
+ XBMCWayland::SendButtonToSurfaceCallback,
+ XBMCWayland::SendAxisToSurfaceCallback,
+ XBMCWayland::SendKeyToKeyboardCallback,
+ XBMCWayland::SendModifiersToKeyboardCallback,
+ XBMCWayland::GiveSurfaceKeyboardFocusCallback,
+ XBMCWayland::PingSurfaceCallback
+};
+}
+}
+}
+
+namespace xtw = xbmc::test::wayland;
+namespace xtwc = xbmc::test::weston;
+
+xtw::XBMCWayland::XBMCWayland(struct wl_client *client,
+ uint32_t id,
+ xtwc::Compositor &compositor) :
+ m_compositor(compositor)
+{
+ m_clientXBMCWaylandResource =
+ static_cast<struct wl_resource *>(wl_resource_create(client,
+ &xbmc_wayland_interface,
+ 1,
+ id));
+ wl_resource_set_implementation (m_clientXBMCWaylandResource,
+ &m_listener,
+ this,
+ XBMCWayland::UnbindFromClientCallback);
+}
+
+xtw::XBMCWayland::~XBMCWayland()
+{
+ /* Remove all but the first output if we added any */
+ for (std::vector<struct weston_mode>::iterator it = m_additionalModes.begin();
+ it != m_additionalModes.end();
+ ++it)
+ {
+ wl_list_remove(&it->link);
+ }
+}
+
+void
+xtw::XBMCWayland::UnbindFromClientCallback(struct wl_resource *r)
+{
+ delete static_cast<XBMCWayland *>(wl_resource_get_user_data(r));
+}
+
+void
+xtw::XBMCWayland::BindToClient(struct wl_client *client,
+ void *data,
+ uint32_t version,
+ uint32_t id)
+{
+ xtwc::Compositor *compositor = static_cast<xtwc::Compositor *>(data);
+
+ /* This looks funky - however the constructor will handle registering
+ * the destructor function with wl_registry so that it gets destroyed
+ * at the right time */
+ new XBMCWayland(client, id, *compositor);
+}
+
+void
+xtw::XBMCWayland::AddModeCallback(struct wl_client *c,
+ struct wl_resource *r,
+ int32_t w,
+ int32_t h,
+ uint32_t re,
+ uint32_t f)
+{
+ static_cast<XBMCWayland *>(wl_resource_get_user_data(r))->AddMode(c, r, w, h, re, f);
+}
+
+void
+xtw::XBMCWayland::MovePointerToOnSurfaceCallback(struct wl_client *c,
+ struct wl_resource *r,
+ struct wl_resource *s,
+ wl_fixed_t x,
+ wl_fixed_t y)
+{
+ static_cast<XBMCWayland *>(wl_resource_get_user_data(r))->MovePointerToOnSurface(c, r, s, x, y);
+}
+
+void
+xtw::XBMCWayland::SendButtonToSurfaceCallback(struct wl_client *c,
+ struct wl_resource *r,
+ struct wl_resource *s,
+ uint32_t b,
+ uint32_t st)
+{
+ static_cast<XBMCWayland *>(wl_resource_get_user_data(r))->SendButtonToSurface(c, r, s, b, st);
+}
+
+void
+xtw::XBMCWayland::SendAxisToSurfaceCallback(struct wl_client *c,
+ struct wl_resource *r,
+ struct wl_resource *s,
+ uint32_t b,
+ wl_fixed_t v)
+{
+ static_cast<XBMCWayland *>(wl_resource_get_user_data(r))->SendAxisToSurface(c, r, s, b, v);
+}
+
+void
+xtw::XBMCWayland::SendModifiersToKeyboardCallback(struct wl_client *c,
+ struct wl_resource *r,
+ struct wl_resource *s,
+ uint32_t d,
+ uint32_t la,
+ uint32_t lo,
+ uint32_t g)
+{
+ static_cast<XBMCWayland *>(wl_resource_get_user_data(r))->SendModifiersToKeyboard(c, r, s, d, la, lo, g);
+}
+
+void
+xtw::XBMCWayland::SendKeyToKeyboardCallback(struct wl_client *c,
+ struct wl_resource *r,
+ struct wl_resource *s,
+ uint32_t k,
+ uint32_t st)
+{
+ static_cast<XBMCWayland *>(wl_resource_get_user_data(r))->SendKeyToKeyboard(c, r, s, k, st);
+}
+
+void
+xtw::XBMCWayland::GiveSurfaceKeyboardFocusCallback(struct wl_client *c,
+ struct wl_resource *r,
+ struct wl_resource *s)
+{
+ static_cast<XBMCWayland *>(wl_resource_get_user_data(r))->GiveSurfaceKeyboardFocus(c, r, s);
+}
+
+void
+xtw::XBMCWayland::PingSurfaceCallback(struct wl_client *c,
+ struct wl_resource *r,
+ struct wl_resource *s,
+ uint32_t t)
+{
+ static_cast<XBMCWayland *>(wl_resource_get_user_data(r))->PingSurface(c, r, s, t);
+}
+
+namespace
+{
+void ClearFlagsOnOtherModes(struct weston_mode *mode,
+ uint32_t flags,
+ struct weston_mode *skip)
+{
+ if (mode == skip)
+ return;
+
+ mode->flags &= ~flags;
+}
+}
+
+void
+xtw::XBMCWayland::AddMode(struct wl_client *client,
+ struct wl_resource *resource,
+ int32_t width,
+ int32_t height,
+ uint32_t refresh,
+ uint32_t flags)
+{
+ const struct weston_mode mode =
+ {
+ flags,
+ width,
+ height,
+ refresh
+ };
+
+ m_additionalModes.push_back(mode);
+ struct weston_mode *lastMode = m_compositor.LastMode();
+ wl_list_insert(&lastMode->link, &m_additionalModes.back().link);
+
+ /* Clear flags from all other outputs that may have the same flags
+ * as this one */
+ m_compositor.OnEachMode(boost::bind(ClearFlagsOnOtherModes,
+ _1,
+ flags,
+ &m_additionalModes.back()));
+}
+
+namespace
+{
+void GetSerialAndTime(struct wl_display *display,
+ uint32_t &serial,
+ uint32_t &time)
+{
+ serial = wl_display_next_serial(display);
+
+ struct timespec ts;
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+
+ time = ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
+}
+}
+
+void
+xtw::XBMCWayland::MovePointerToOnSurface(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *surface,
+ wl_fixed_t x,
+ wl_fixed_t y)
+{
+ struct wl_client *surfaceClient = wl_resource_get_client(surface);
+ struct wl_resource *pointer = m_compositor.PointerResource(surfaceClient);
+ struct wl_display *display = wl_client_get_display(surfaceClient);
+ uint32_t serial, time;
+
+ GetSerialAndTime(display, serial, time);
+
+ wl_pointer_send_motion(pointer, time, x, y);
+}
+
+void
+xtw::XBMCWayland::SendButtonToSurface(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *surface,
+ uint32_t button,
+ uint32_t state)
+{
+ struct wl_client *surfaceClient = wl_resource_get_client(surface);
+ struct wl_resource *pointer = m_compositor.PointerResource(surfaceClient);
+ struct wl_display *display = wl_client_get_display(surfaceClient);
+ uint32_t serial, time;
+
+ GetSerialAndTime(display, serial, time);
+
+ wl_pointer_send_button(pointer, serial, time, button, state);
+}
+
+void
+xtw::XBMCWayland::SendAxisToSurface(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *surface,
+ uint32_t axis,
+ wl_fixed_t value)
+{
+ struct wl_client *surfaceClient = wl_resource_get_client(surface);
+ struct wl_resource *pointer = m_compositor.PointerResource(surfaceClient);
+ struct wl_display *display = wl_client_get_display(surfaceClient);
+ uint32_t serial, time;
+
+ GetSerialAndTime(display, serial, time);
+
+ wl_pointer_send_axis(pointer, time, axis, value);
+}
+
+void
+xtw::XBMCWayland::SendKeyToKeyboard(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *surface,
+ uint32_t key,
+ uint32_t state)
+{
+ struct wl_client *surfaceClient = wl_resource_get_client(surface);
+ struct wl_resource *keyboard = m_compositor.KeyboardResource(surfaceClient);
+ struct wl_display *display = wl_client_get_display(surfaceClient);
+ uint32_t serial, time;
+
+ GetSerialAndTime(display, serial, time);
+
+ wl_keyboard_send_key(keyboard, serial, time, key, state);
+}
+
+void
+xtw::XBMCWayland::SendModifiersToKeyboard(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *surface,
+ uint32_t depressed,
+ uint32_t latched,
+ uint32_t locked,
+ uint32_t group)
+{
+ struct wl_client *surfaceClient = wl_resource_get_client(surface);
+ struct wl_resource *keyboard = m_compositor.KeyboardResource(surfaceClient);
+ struct wl_display *display = wl_client_get_display(surfaceClient);
+ uint32_t serial = wl_display_next_serial(display);
+
+ wl_keyboard_send_modifiers(keyboard,
+ serial,
+ depressed,
+ latched,
+ locked,
+ group);
+}
+
+void
+xtw::XBMCWayland::GiveSurfaceKeyboardFocus(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *surface)
+{
+ struct weston_surface *westonSurface = m_compositor.Surface(surface);
+ struct weston_keyboard *keyboard = m_compositor.Keyboard();
+ weston_keyboard_set_focus(keyboard, westonSurface);
+}
+
+void
+xtw::XBMCWayland::PingSurface(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *surface,
+ uint32_t timestamp)
+{
+}
+
+xtw::Listener::Listener(const Delegate &delegate) :
+ m_delegate(delegate)
+{
+ m_listener.notify = Listener::MainCallback;
+}
+
+void
+xtw::Listener::MainCallback(struct wl_listener *listener, void *data)
+{
+ static_cast<Listener *>(data)->Callback();
+}
+
+void
+xtw::Listener::Callback()
+{
+ m_delegate();
+}
+
+void
+xtw::Listener::BindTo(struct wl_signal *s)
+{
+ wl_signal_add(s, &m_listener);
+}
+
+xtwc::Compositor::Compositor(struct weston_compositor *c) :
+ m_compositor(c),
+ /* This is a workaround for a race condition where the registry
+ * might not be ready if we send SIGUSR2 right away - so we
+ * put it on the event loop to happen after the first poll() */
+ m_readySource(wl_event_loop_add_timer(wl_display_get_event_loop(Display()),
+ Compositor::Ready,
+ this)),
+ m_destroyListener(boost::bind(Compositor::Unload, this))
+{
+ /* Dispatch ASAP */
+ wl_event_source_timer_update(m_readySource, 1);
+ m_destroyListener.BindTo(&c->destroy_signal);
+
+ /* The parent process should have set the SIGUSR2 handler to
+ * SIG_IGN, throw if it hasn't */
+ if (signal(SIGUSR2, SIG_IGN) != SIG_IGN)
+ {
+ std::stringstream ss;
+ throw std::runtime_error("Parent process is not handling SIGUSR2");
+ }
+}
+
+int
+xtwc::Compositor::Ready(void *data)
+{
+ xtwc::Compositor *compositor = static_cast<xtwc::Compositor *>(data);
+
+ if (kill(getppid(), SIGUSR2) == -1)
+ {
+ std::stringstream ss;
+ ss << "kill: "
+ << strerror(errno);
+ throw std::runtime_error(ss.str());
+ }
+
+ wl_event_source_remove(compositor->m_readySource);
+ compositor->m_readySource = NULL;
+
+ /* Initialize the fake keyboard and pointer on our seat. This is
+ * effectively manipulating the backend into doing something it
+ * shouldn't, but we're in control here */
+ struct weston_seat *seat = compositor->Seat();
+ weston_seat_init_pointer(seat);
+
+ struct xkb_keymap *keymap =
+ xkb_keymap_new_from_names(compositor->m_compositor->xkb_context,
+ &compositor->m_compositor->xkb_names,
+ static_cast<enum xkb_keymap_compile_flags>(0));
+ if (!keymap)
+ throw std::runtime_error("Failed to compile keymap\n");
+
+ weston_seat_init_keyboard(seat, keymap);
+ xkb_keymap_unref(keymap);
+ return 1;
+}
+
+struct weston_output *
+xtwc::Compositor::FirstOutput()
+{
+ struct weston_output *output;
+
+ output = wl_container_of(m_compositor->output_list.prev,
+ output,
+ link);
+
+ return output;
+}
+
+struct wl_display *
+xtwc::Compositor::Display()
+{
+ return m_compositor->wl_display;
+}
+
+struct weston_seat *
+xtwc::Compositor::Seat()
+{
+ /* Since it is impossible to get a weston_seat from a weston_compositor
+ * and we will need that in order to get access to the weston_pointer
+ * and weston_keyboard, we need to use this hack to get access
+ * to the seat by casting the weston_compositor to this, which is
+ * copied from compositor-headless.c . Since weston_compositor is
+ * the the first member of headless_compositor, it is safe to cast
+ * from the second to the first */
+ struct headless_compositor {
+ struct weston_compositor compositor;
+ struct weston_seat seat;
+ };
+
+ /* Before we cast, we should check if we are actually running
+ * on the headless compositor. If not, throw an exception so
+ * that the user might know what's going on */
+ struct weston_output *output = FirstOutput();
+
+ if (!output)
+ throw std::runtime_error("Compositor does not have an output");
+
+ if (std::string(output->model) != "headless")
+ {
+ std::stringstream ss;
+ ss << "Only the compositor-headless.so backend "
+ << "is supported by this extension. "
+ << std::endl
+ << "The current output model detected was "
+ << output->model;
+ throw std::logic_error(ss.str());
+ }
+
+ struct headless_compositor *hc =
+ reinterpret_cast<struct headless_compositor *>(m_compositor);
+
+ return &hc->seat;
+}
+
+struct weston_surface *
+xtwc::Compositor::TopSurface()
+{
+ struct weston_surface *surface;
+
+ /* The strange semantics of wl_container_of means that we can't
+ * return its result directly because it needs to have an
+ * instantiation of the type */
+ surface = wl_container_of(m_compositor->surface_list.prev,
+ surface,
+ link);
+ return surface;
+}
+
+void
+xtwc::Compositor::OnEachMode(const boost::function<void(struct weston_mode *)> &action)
+{
+ struct weston_output *output = FirstOutput();
+ struct weston_mode *mode;
+
+ wl_list_for_each(mode, &output->mode_list, link)
+ {
+ action(mode);
+ }
+}
+
+struct weston_mode *
+xtwc::Compositor::LastMode()
+{
+ struct weston_mode *mode;
+ struct weston_output *output = FirstOutput();
+ mode = wl_container_of(output->mode_list.prev,
+ mode,
+ link);
+
+ return mode;
+}
+
+struct wl_resource *
+xtwc::Compositor::PointerResource(struct wl_client *client)
+{
+ struct weston_seat *seat = Seat();
+ struct wl_resource *r =
+ wl_resource_find_for_client(&seat->pointer->focus_resource_list,
+ client);
+ if (!r)
+ r = wl_resource_find_for_client(&seat->pointer->resource_list,
+ client);
+
+ if (!r)
+ throw std::logic_error ("No pointer resource available for this "
+ "client ");
+ return r;
+}
+
+struct wl_resource *
+xtwc::Compositor::KeyboardResource(struct wl_client *client)
+{
+ struct weston_seat *seat = Seat();
+ struct wl_resource *r =
+ wl_resource_find_for_client(&seat->keyboard->focus_resource_list,
+ client);
+ if (!r)
+ r = wl_resource_find_for_client(&seat->keyboard->resource_list,
+ client);
+
+ if (!r)
+ throw std::logic_error ("No keyboard resource available for this "
+ "client ");
+ return r;
+}
+
+struct weston_surface *
+xtwc::Compositor::Surface(struct wl_resource *surface)
+{
+ struct weston_surface *ws =
+ reinterpret_cast<struct weston_surface *>(wl_resource_get_user_data(surface));
+
+ return ws;
+}
+
+void
+xtwc::Compositor::Unload(xtwc::Compositor *compositor)
+{
+ delete compositor;
+}
+
+struct weston_keyboard *
+xtwc::Compositor::Keyboard()
+{
+ struct weston_seat *seat = Seat();
+ return seat->keyboard;
+}
+
+xtwc::Compositor::~Compositor()
+{
+}
+
+extern "C"
+{
+WL_EXPORT int
+module_init(struct weston_compositor *c,
+ int *argc,
+ char *argv[])
+{
+ /* Using heap allocated memory directly here is awkward, however
+ * weston knows when we need to destroy our resources
+ * so we will let it handle it */
+ xtwc::Compositor *compositor(new xtwc::Compositor(c));
+ /* Register our factory for xbmc_wayland and pass
+ * xtwc::Compositor to it when it gets created */
+ if (wl_global_create(compositor->Display(),
+ &xbmc_wayland_interface,
+ 1,
+ compositor,
+ xtw::XBMCWayland::BindToClient) == NULL)
+ return -1;
+
+ return 0;
+}
+}
+
diff --git a/xbmc/windowing/tests/wayland/protocol.xml b/xbmc/windowing/tests/wayland/protocol.xml
new file mode 100644
index 0000000000..69f5202eba
--- /dev/null
+++ b/xbmc/windowing/tests/wayland/protocol.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="xbmc_wayland_test">
+ <copyright>
+ Copyright (C) 2005-2013 Team XBMC
+ http://www.xbmc.org
+
+ This Program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This Program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with XBMC; see the file COPYING. If not, see
+ &lt;http://www.gnu.org/licenses/&gt;
+
+ </copyright>
+ <interface name="xbmc_wayland" version="1"/>
+ <request name="add_mode">
+ <arg name="width" type="int"/>
+ <arg name="height" type="int"/>
+ <arg name="refresh" type="uint"/>
+ <arg name="flags" type="uint"/>
+ </request>
+ <request name="move_pointer_to_on_surface">
+ <arg name="surface" type="object" interface="wl_surface"/>
+ <arg name="x" type="fixed"/>
+ <arg name="y" type="fixed"/>
+ </request>
+ <request name="send_button_to_surface">
+ <arg name="surface" type="object" interface="wl_surface"/>
+ <arg name="button" type="uint"/>
+ <arg name="state" type="uint"/>
+ </request>
+ <request name="send_axis_to_surface">
+ <arg name="surface" type="object" interface="wl_surface"/>
+ <arg name="axis" type="uint"/>
+ <arg name="value" type="fixed"/>
+ </request>
+ <request name="send_key_to_keyboard">
+ <arg name="surface" type="object" interface="wl_surface"/>
+ <arg name="key" type="uint"/>
+ <arg name="state" type="uint"/>
+ </request>
+ <request name="send_modifiers_to_keyboard">
+ <arg name="surface" type="object" interface="wl_surface"/>
+ <arg name="depressed" type="uint"/>
+ <arg name="latched" type="uint"/>
+ <arg name="locked" type="uint"/>
+ <arg name="group" type="uint"/>
+ </request>
+ <request name="give_surface_keyboard_focus">
+ <arg name="surface" type="object" interface="wl_surface"/>
+ </request>
+ <request name="ping_surface">
+ <arg name="surface" type="object" interface="wl_surface"/>
+ <arg name="timestamp" type="uint"/>
+ </request>
+</protocol>