/xbmc/guilib/Profile
/xbmc/guilib/Profile_FastCap
+# /xbmc/input
+/xbmc/input/linux/Makefile
+
# /xbmc/interfaces/
/xbmc/interfaces/Makefile
/xbmc/interfaces/python/Makefile
AC_MSG_ERROR($missing_library))
PKG_CHECK_MODULES([XKBCOMMON], [xkbcommon],
[INCLUDES="$INCLUDES $XKBCOMMON_CFLAGS";
- XKBCOMMON_LIBRARY_LINE=`LIBRARY=\`${PKG_CONFIG} --libs-only-l xkbcommon\`; echo ${LIBRARY:2}`
+ XKBCOMMON_LIBRARY_LINE=`LIBRARY=\`${PKG_CONFIG} --libs-only-l xkbcommon\`; echo ${LIBRARY:2}`;
XB_FIND_SONAME([XKBCOMMON_LIBRARY], $XKBCOMMON_LIBRARY_LINE)],
AC_MSG_ERROR($missing_library))
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.])
# Disable SDL and X11 builds
use_sdl=no
if test "$use_wayland" = "yes"; then
final_message="$final_message\n Wayland:\tYes"
USE_WAYLAND=1
+ USE_XKBCOMMON=1
else
final_message="$final_message\n Wayland:\tNo"
fi
xbmc/music/karaoke/Makefile \
xbmc/osx/Makefile \
xbmc/guilib/Makefile \
+ xbmc/input/linux/Makefile \
xbmc/interfaces/Makefile \
xbmc/network/Makefile \
xbmc/network/upnp/Makefile \
AC_SUBST(USE_WAYLAND)
AC_SUBST(USE_WEB_SERVER)
AC_SUBST(USE_UPNP)
+AC_SUBST(USE_XKBCOMMON)
AC_SUBST(USE_OMXLIB)
AC_SUBST(USE_ANDROID)
AC_SUBST(GTEST_CONFIGURED)
--- /dev/null
+#pragma once
+
+/*
+ * Copyright (C) 2011-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"
+
+class ILinuxKeymap
+{
+public:
+
+ virtual ~ILinuxKeymap() {};
+
+ virtual uint32_t KeysymForKeycode(uint32_t code) const = 0;
+ virtual void UpdateMask(uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group) = 0;
+ virtual uint32_t CurrentModifiers() const = 0;
+
+ virtual uint32_t XBMCKeysymForKeycode(uint32_t code) const = 0;
+ virtual uint32_t ActiveXBMCModifiers() const = 0;
+};
+++ /dev/null
-SRCS=LIRC.cpp \
- LinuxInputDevices.cpp
-
-LIB=input_linux.a
-
-include ../../../Makefile.include
--include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS)))
--- /dev/null
+SRCS=LIRC.cpp \
+ LinuxInputDevices.cpp
+
+# xkbcommon detail
+ifeq (@USE_XKBCOMMON@,1)
+SRCS += XKBCommonKeymap.cpp
+endif
+
+LIB=input_linux.a
+
+include ../../../Makefile.include
+-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS)))
--- /dev/null
+/*
+ * Copyright (C) 2011-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 <sstream>
+#include <iostream>
+#include <stdexcept>
+
+#include <sys/mman.h>
+
+#include <xkbcommon/xkbcommon.h>
+
+#include "Application.h"
+
+#include "windowing/DllXKBCommon.h"
+#include "XKBCommonKeymap.h"
+
+/* Opens a shared memory region and parses the data in it to an
+ * xkbcommon keymap.
+ *
+ * Generally a keymap is determined by the compositor by evaluating
+ * the currently available hardware and compiling a keymap
+ * most appropriate for that hardware. The compositor simply
+ * sends keycodes and modifier bits in the wire protocol. It is the
+ * client's responsibility to handle transformation of those hardware
+ * specific keycodes and modifier bits into a common keyboard
+ * representation. The compositor provides a serialized keymap
+ * in shared memory to allow clients to perform these transformations.
+ *
+ * This function does not own the file descriptor. The fact that it
+ * is sent as a const & is a reminder of that. It must not be closed
+ * from this function.
+ */
+struct xkb_keymap *
+CXKBKeymap::ReceiveXKBKeymapFromSharedMemory(IDllXKBCommon &xkbCommonLibrary, struct xkb_context *context, const int &fd, uint32_t size)
+{
+ const char *keymapString = static_cast<const char *>(mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0));
+ if (keymapString == MAP_FAILED)
+ {
+ std::stringstream ss;
+ ss << "mmap: " << strerror(errno);
+ throw std::runtime_error(ss.str());
+ }
+
+ /* In every exit path, the keymap string memory region must be
+ * unmapped */
+ BOOST_SCOPE_EXIT((keymapString)(size))
+ {
+ munmap(const_cast<void *>(static_cast<const void *>(keymapString)),
+ size);
+ } BOOST_SCOPE_EXIT_END
+
+ enum xkb_keymap_compile_flags flags =
+ static_cast<enum xkb_keymap_compile_flags>(0);
+ struct xkb_keymap *keymap =
+ xkbCommonLibrary.xkb_keymap_new_from_string(context, keymapString, XKB_KEYMAP_FORMAT_TEXT_V1, flags);
+
+ /* Failure to compile a keymap is a runtime error and the caller
+ * should handle it */
+ if (!keymap)
+ throw std::runtime_error("Failed to compile keymap");
+
+ return keymap;
+}
+
+struct xkb_state *
+CXKBKeymap::CreateXKBStateFromKeymap(IDllXKBCommon &xkbCommonLibrary, struct xkb_keymap *keymap)
+{
+ struct xkb_state *state = xkbCommonLibrary.xkb_state_new(keymap);
+
+ if (!state)
+ throw std::runtime_error("Failed to create keyboard state");
+
+ return state;
+}
+
+/* A wrapper class around an xkbcommon keymap and state tracker.
+ *
+ * This class knows about some common modifier combinations and keeps
+ * track of the currently pressed keys and modifiers. It also has
+ * some utility functions to transform hardware keycodes into
+ * a common representation.
+ *
+ * Since this class is keeping track of all the pressed and depressed
+ * modifiers, IT MUST ALWAYS BE KEPT UP TO DATE WITH ANY NEWLY
+ * PRESSED MODIFIERS. Undefined behaviour will result if it is not
+ * kept up to date.
+ */
+CXKBKeymap::CXKBKeymap(IDllXKBCommon &xkbCommonLibrary,
+ struct xkb_keymap *keymap,
+ struct xkb_state *state) :
+ m_xkbCommonLibrary(xkbCommonLibrary),
+ m_keymap(keymap),
+ m_state(state),
+ m_internalLeftControlIndex(m_xkbCommonLibrary.xkb_keymap_mod_get_index(m_keymap,
+ XKB_MOD_NAME_CTRL)),
+ m_internalLeftShiftIndex(m_xkbCommonLibrary.xkb_keymap_mod_get_index(m_keymap,
+ XKB_MOD_NAME_SHIFT)),
+ m_internalLeftSuperIndex(m_xkbCommonLibrary.xkb_keymap_mod_get_index(m_keymap,
+ XKB_MOD_NAME_LOGO)),
+ m_internalLeftAltIndex(m_xkbCommonLibrary.xkb_keymap_mod_get_index(m_keymap,
+ XKB_MOD_NAME_ALT)),
+ m_internalLeftMetaIndex(m_xkbCommonLibrary.xkb_keymap_mod_get_index(m_keymap,
+ "Meta")),
+ m_internalRightControlIndex(m_xkbCommonLibrary.xkb_keymap_mod_get_index(m_keymap,
+ "RControl")),
+ m_internalRightShiftIndex(m_xkbCommonLibrary.xkb_keymap_mod_get_index(m_keymap,
+ "RShift")),
+ m_internalRightSuperIndex(m_xkbCommonLibrary.xkb_keymap_mod_get_index(m_keymap,
+ "Hyper")),
+ m_internalRightAltIndex(m_xkbCommonLibrary.xkb_keymap_mod_get_index(m_keymap,
+ "AltGr")),
+ m_internalRightMetaIndex(m_xkbCommonLibrary.xkb_keymap_mod_get_index(m_keymap,
+ "Meta")),
+ m_internalCapsLockIndex(m_xkbCommonLibrary.xkb_keymap_mod_get_index(m_keymap,
+ XKB_LED_NAME_CAPS)),
+ m_internalNumLockIndex(m_xkbCommonLibrary.xkb_keymap_mod_get_index(m_keymap,
+ XKB_LED_NAME_NUM)),
+ m_internalModeIndex(m_xkbCommonLibrary.xkb_keymap_mod_get_index(m_keymap,
+ XKB_LED_NAME_SCROLL))
+{
+}
+
+CXKBKeymap::~CXKBKeymap()
+{
+ m_xkbCommonLibrary.xkb_state_unref(m_state);
+ m_xkbCommonLibrary.xkb_keymap_unref(m_keymap);
+}
+
+uint32_t
+CXKBKeymap::KeysymForKeycode(uint32_t code) const
+{
+ const xkb_keysym_t *syms;
+ uint32_t numSyms;
+
+ /* Key the keysyms for a particular keycode. Generally each
+ * keycode should only have one symbol, but if it has more than
+ * one then we're unable to just get one symbol for it, so that's
+ * a runtime_error which the client needs to handle.
+ *
+ * Codes sent generally have an offset of 8 */
+ numSyms = m_xkbCommonLibrary.xkb_state_key_get_syms(m_state, code + 8, &syms);
+
+ if (numSyms == 1)
+ return static_cast<uint32_t>(syms[0]);
+
+ std::stringstream ss;
+ ss << "Pressed key "
+ << std::hex
+ << code
+ << std::dec
+ << " is unspported";
+
+ throw std::runtime_error(ss.str());
+}
+
+/* Gets the currently depressed, latched and locked modifiers
+ * for the keyboard */
+uint32_t CXKBKeymap::CurrentModifiers() const
+{
+ enum xkb_state_component components =
+ static_cast <xkb_state_component>(XKB_STATE_DEPRESSED |
+ XKB_STATE_LATCHED |
+ XKB_STATE_LOCKED);
+ xkb_mod_mask_t mask = m_xkbCommonLibrary.xkb_state_serialize_mods(m_state,
+ components);
+ return mask;
+}
+
+/* Updates the currently depressed, latched, locked and group
+ * modifiers for a keyboard being tracked.
+ *
+ * THIS FUNCTION MUST BE CALLED WHENEVER MODIFIERS CHANGE */
+void CXKBKeymap::UpdateMask(uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group)
+{
+ m_xkbCommonLibrary.xkb_state_update_mask(m_state, depressed, latched, locked, 0, 0, group);
+}
+
+uint32_t CXKBKeymap::ActiveXBMCModifiers() const
+{
+ xkb_mod_mask_t mask(CurrentModifiers());
+ XBMCMod xbmcModifiers = XBMCKMOD_NONE;
+
+ struct ModTable
+ {
+ xkb_mod_index_t xkbMod;
+ XBMCMod xbmcMod;
+ } modTable[] =
+ {
+ { m_internalLeftShiftIndex, XBMCKMOD_LSHIFT },
+ { m_internalRightShiftIndex, XBMCKMOD_RSHIFT },
+ { m_internalLeftShiftIndex, XBMCKMOD_LSUPER },
+ { m_internalRightSuperIndex, XBMCKMOD_RSUPER },
+ { m_internalLeftControlIndex, XBMCKMOD_LCTRL },
+ { m_internalRightControlIndex, XBMCKMOD_RCTRL },
+ { m_internalLeftAltIndex, XBMCKMOD_LALT },
+ { m_internalRightAltIndex, XBMCKMOD_RALT },
+ { m_internalLeftMetaIndex, XBMCKMOD_LMETA },
+ { m_internalRightMetaIndex, XBMCKMOD_RMETA },
+ { m_internalNumLockIndex, XBMCKMOD_NUM },
+ { m_internalCapsLockIndex, XBMCKMOD_CAPS },
+ { m_internalModeIndex, XBMCKMOD_MODE }
+ };
+
+ size_t modTableSize = sizeof(modTable) / sizeof(modTable[0]);
+
+ for (size_t i = 0; i < modTableSize; ++i)
+ {
+ if (mask & (1 << modTable[i].xkbMod))
+ xbmcModifiers = static_cast<XBMCMod>(xbmcModifiers | modTable[i].xbmcMod);
+ }
+
+ return static_cast<uint32_t>(xbmcModifiers);
+}
+
+uint32_t CXKBKeymap::XBMCKeysymForKeycode(uint32_t code) const
+{
+ uint32_t sym = KeysymForKeycode(code);
+
+ /* Strip high bits from functional keys */
+ if ((sym & ~(0xff00)) <= 0x1b)
+ sym = sym & ~(0xff00);
+ else if ((sym & ~(0xff00)) == 0xff)
+ sym = static_cast<uint32_t>(XBMCK_DELETE);
+
+ /* We only support keys within certain ranges */
+ const bool isNavigationKey = (sym >= 0xff50 && sym <= 0xff58);
+ const bool isModifierKey = (sym >= 0xffe1 && sym <= 0xffee);
+ const bool isKeyPadKey = (sym >= 0xffb1 && sym <= 0xffb9);
+ const bool isFKey = (sym >= 0xffbe && sym <= 0xffcc);
+ const bool isMediaKey = (sym >= 0x1008ff26 && sym <= 0x1008ffa2);
+
+ if (isNavigationKey ||
+ isModifierKey ||
+ isKeyPadKey ||
+ isFKey ||
+ isMediaKey)
+ {
+ /* Navigation keys are not in line, so we need to
+ * look them up */
+ static const struct NavigationKeySyms
+ {
+ uint32_t xkb;
+ XBMCKey xbmc;
+ } navigationKeySyms[] =
+ {
+ { XKB_KEY_Home, XBMCK_HOME },
+ { XKB_KEY_Left, XBMCK_LEFT },
+ { XKB_KEY_Right, XBMCK_RIGHT },
+ { XKB_KEY_Down, XBMCK_DOWN },
+ { XKB_KEY_Up, XBMCK_UP },
+ { XKB_KEY_Page_Up, XBMCK_PAGEUP },
+ { XKB_KEY_Page_Down, XBMCK_PAGEDOWN },
+ { XKB_KEY_End, XBMCK_END },
+ { XKB_KEY_Insert, XBMCK_INSERT },
+ { XKB_KEY_KP_0, XBMCK_KP0 },
+ { XKB_KEY_KP_1, XBMCK_KP1 },
+ { XKB_KEY_KP_2, XBMCK_KP2 },
+ { XKB_KEY_KP_3, XBMCK_KP3 },
+ { XKB_KEY_KP_4, XBMCK_KP4 },
+ { XKB_KEY_KP_5, XBMCK_KP5 },
+ { XKB_KEY_KP_6, XBMCK_KP6 },
+ { XKB_KEY_KP_7, XBMCK_KP7 },
+ { XKB_KEY_KP_8, XBMCK_KP8 },
+ { XKB_KEY_KP_9, XBMCK_KP9 },
+ { XKB_KEY_KP_Decimal, XBMCK_KP_PERIOD },
+ { XKB_KEY_KP_Divide, XBMCK_KP_DIVIDE },
+ { XKB_KEY_KP_Multiply, XBMCK_KP_MULTIPLY },
+ { XKB_KEY_KP_Add, XBMCK_KP_PLUS },
+ { XKB_KEY_KP_Separator, XBMCK_KP_MINUS },
+ { XKB_KEY_KP_Equal, XBMCK_KP_EQUALS },
+ { XKB_KEY_F1, XBMCK_F1 },
+ { XKB_KEY_F2, XBMCK_F2 },
+ { XKB_KEY_F3, XBMCK_F3 },
+ { XKB_KEY_F4, XBMCK_F4 },
+ { XKB_KEY_F5, XBMCK_F5 },
+ { XKB_KEY_F6, XBMCK_F6 },
+ { XKB_KEY_F7, XBMCK_F7 },
+ { XKB_KEY_F8, XBMCK_F8 },
+ { XKB_KEY_F9, XBMCK_F9 },
+ { XKB_KEY_F10, XBMCK_F10 },
+ { XKB_KEY_F11, XBMCK_F11 },
+ { XKB_KEY_F12, XBMCK_F12 },
+ { XKB_KEY_F13, XBMCK_F13 },
+ { XKB_KEY_F14, XBMCK_F14 },
+ { XKB_KEY_F15, XBMCK_F15 },
+ { XKB_KEY_Caps_Lock, XBMCK_CAPSLOCK },
+ { XKB_KEY_Shift_Lock, XBMCK_SCROLLOCK },
+ { XKB_KEY_Shift_R, XBMCK_RSHIFT },
+ { XKB_KEY_Shift_L, XBMCK_LSHIFT },
+ { XKB_KEY_Alt_R, XBMCK_RALT },
+ { XKB_KEY_Alt_L, XBMCK_LALT },
+ { XKB_KEY_Control_R, XBMCK_RCTRL },
+ { XKB_KEY_Control_L, XBMCK_LCTRL },
+ { XKB_KEY_Meta_R, XBMCK_RMETA },
+ { XKB_KEY_Meta_L, XBMCK_LMETA },
+ { XKB_KEY_Super_R, XBMCK_RSUPER },
+ { XKB_KEY_Super_L, XBMCK_LSUPER },
+ { XKB_KEY_XF86Eject, XBMCK_EJECT },
+ { XKB_KEY_XF86AudioStop, XBMCK_STOP },
+ { XKB_KEY_XF86AudioRecord, XBMCK_RECORD },
+ { XKB_KEY_XF86AudioRewind, XBMCK_REWIND },
+ { XKB_KEY_XF86AudioPlay, XBMCK_PLAY },
+ { XKB_KEY_XF86AudioRandomPlay, XBMCK_SHUFFLE },
+ { XKB_KEY_XF86AudioForward, XBMCK_FASTFORWARD }
+ };
+
+ static const size_t navigationKeySymsSize = sizeof(navigationKeySyms) /
+ sizeof(navigationKeySyms[0]);
+
+ for (size_t i = 0; i < navigationKeySymsSize; ++i)
+ {
+ if (navigationKeySyms[i].xkb == sym)
+ {
+ sym = navigationKeySyms[i].xbmc;
+ break;
+ }
+ }
+ }
+
+ return sym;
+}
--- /dev/null
+#pragma once
+
+/*
+ * Copyright (C) 2011-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 <boost/noncopyable.hpp>
+#include <boost/scope_exit.hpp>
+
+#include "input/linux/Keymap.h"
+
+struct xkb_keymap;
+struct xkb_state;
+
+typedef uint32_t xkb_mod_index_t;
+typedef uint32_t xkb_mask_index_t;
+
+class IDllXKBCommon;
+
+class CXKBKeymap : public ILinuxKeymap
+{
+public:
+
+ CXKBKeymap(IDllXKBCommon &m_xkbCommonLibrary,
+ struct xkb_keymap *keymap,
+ struct xkb_state *state);
+ ~CXKBKeymap();
+
+ /* ReceiveXKBKeymapFromSharedMemory does not own the file descriptor, as such it takes a const reference to it */
+ static struct xkb_keymap * ReceiveXKBKeymapFromSharedMemory(IDllXKBCommon &xkbCommonLibrary, struct xkb_context *, const int &fd, uint32_t size);
+ static struct xkb_state * CreateXKBStateFromKeymap(IDllXKBCommon &xkbCommonLibrary, struct xkb_keymap *keymap);
+private:
+
+ uint32_t KeysymForKeycode(uint32_t code) const;
+ void UpdateMask(uint32_t depressed,
+ uint32_t latched,
+ uint32_t locked,
+ uint32_t group);
+ uint32_t CurrentModifiers() const;
+ uint32_t XBMCKeysymForKeycode(uint32_t code) const;
+ uint32_t ActiveXBMCModifiers() const;
+
+ IDllXKBCommon &m_xkbCommonLibrary;
+
+ struct xkb_keymap *m_keymap;
+ struct xkb_state *m_state;
+
+ xkb_mod_index_t m_internalLeftControlIndex;
+ xkb_mod_index_t m_internalLeftShiftIndex;
+ xkb_mod_index_t m_internalLeftSuperIndex;
+ xkb_mod_index_t m_internalLeftAltIndex;
+ xkb_mod_index_t m_internalLeftMetaIndex;
+
+ xkb_mod_index_t m_internalRightControlIndex;
+ xkb_mod_index_t m_internalRightShiftIndex;
+ xkb_mod_index_t m_internalRightSuperIndex;
+ xkb_mod_index_t m_internalRightAltIndex;
+ xkb_mod_index_t m_internalRightMetaIndex;
+
+ xkb_mod_index_t m_internalCapsLockIndex;
+ xkb_mod_index_t m_internalNumLockIndex;
+ xkb_mod_index_t m_internalModeIndex;
+};
{
public:
virtual ~IDllXKBCommon() {}
+
+ virtual struct xkb_context * xkb_context_new(enum xkb_context_flags) = 0;
+ virtual void xkb_context_unref(struct xkb_context *) = 0;
+ virtual struct xkb_keymap * xkb_keymap_new_from_string(struct xkb_context *,
+ const char *,
+ enum xkb_keymap_format,
+ enum xkb_keymap_compile_flags) = 0;
+ virtual xkb_mod_index_t xkb_keymap_mod_get_index(struct xkb_keymap *,
+ const char *) = 0;
+ virtual void xkb_keymap_unref(struct xkb_keymap *) = 0;
+ virtual struct xkb_state * xkb_state_new(struct xkb_keymap *) = 0;
+ virtual xkb_mod_mask_t xkb_state_serialize_mods(struct xkb_state *,
+ enum xkb_state_component) = 0;
+ virtual enum xkb_state_component xkb_state_update_mask (struct xkb_state *,
+ xkb_mod_mask_t,
+ xkb_mod_mask_t,
+ xkb_mod_mask_t,
+ xkb_layout_index_t,
+ xkb_layout_index_t,
+ xkb_layout_index_t) = 0;
+ virtual uint32_t xkb_state_key_get_syms(struct xkb_state *,
+ uint32_t,
+ const xkb_keysym_t **) = 0;
+ virtual void xkb_state_unref(struct xkb_state *) = 0;
};
class DllXKBCommon : public DllDynamic, public IDllXKBCommon
{
DECLARE_DLL_WRAPPER(DllXKBCommon, DLL_PATH_XKBCOMMON)
+
+ DEFINE_METHOD1(struct xkb_context *, xkb_context_new, (enum xkb_context_flags p1));
+ DEFINE_METHOD1(void, xkb_context_unref, (struct xkb_context *p1));
+ DEFINE_METHOD4(struct xkb_keymap *, xkb_keymap_new_from_string, (struct xkb_context *p1, const char *p2, enum xkb_keymap_format p3, enum xkb_keymap_compile_flags p4));
+ DEFINE_METHOD2(xkb_mod_index_t, xkb_keymap_mod_get_index, (struct xkb_keymap *p1, const char *p2));
+ DEFINE_METHOD1(void, xkb_keymap_unref, (struct xkb_keymap *p1));
+ DEFINE_METHOD1(struct xkb_state *, xkb_state_new, (struct xkb_keymap *p1));
+ DEFINE_METHOD2(xkb_mod_mask_t, xkb_state_serialize_mods, (struct xkb_state *p1, enum xkb_state_component p2));
+ DEFINE_METHOD7(enum xkb_state_component, xkb_state_update_mask, (struct xkb_state *p1, xkb_mod_mask_t p2, xkb_mod_mask_t p3, xkb_mod_mask_t p4, xkb_layout_index_t p5, xkb_layout_index_t p6, xkb_layout_index_t p7));
+ DEFINE_METHOD3(uint32_t, xkb_state_key_get_syms, (struct xkb_state *p1, uint32_t p2, const xkb_keysym_t **p3));
+ DEFINE_METHOD1(void, xkb_state_unref, (struct xkb_state *p1));
+
BEGIN_METHOD_RESOLVE()
+ RESOLVE_METHOD(xkb_context_new)
+ RESOLVE_METHOD(xkb_context_unref)
+ RESOLVE_METHOD(xkb_keymap_new_from_string)
+ RESOLVE_METHOD(xkb_keymap_mod_get_index)
+ RESOLVE_METHOD(xkb_keymap_unref)
+ RESOLVE_METHOD(xkb_state_new)
+ RESOLVE_METHOD(xkb_state_serialize_mods)
+ RESOLVE_METHOD(xkb_state_update_mask)
+ RESOLVE_METHOD(xkb_state_key_get_syms)
+ RESOLVE_METHOD(xkb_state_unref)
END_METHOD_RESOLVE()
};
# Wayland implementation detail
ifeq (@USE_WAYLAND@,1)
SRCS += wayland/Seat.cpp \
- wayland/Pointer.cpp
+ wayland/Pointer.cpp \
+ wayland/Keyboard.cpp
endif
LIB=windowing.a
#include <boost/weak_ptr.hpp>
#include <sys/poll.h>
-#include <sys/mman.h>
#include <wayland-client.h>
#include <xkbcommon/xkbcommon.h>
#include "WinEvents.h"
#include "WinEventsWayland.h"
#include "utils/Stopwatch.h"
+#include "utils/log.h"
#include "DllWaylandClient.h"
#include "DllXKBCommon.h"
#include "wayland/Seat.h"
#include "wayland/Pointer.h"
+#include "wayland/Keyboard.h"
-namespace
-{
-IDllWaylandClient *g_clientLibrary = NULL;
-struct wl_display *g_display = NULL;
-}
+#include "input/linux/XKBCommonKeymap.h"
+#include "input/linux/Keymap.h"
namespace xbmc
{
+/* ITimeoutManager defines an interface for arbitary classes
+ * to register full closures to be called initially on a timeout
+ * specified by initial and then subsequently on a timeout specified
+ * by timeout. The interface is more or less artificial and exists to
+ * break the dependency between keyboard processing code and
+ * actual system timers, whcih is useful for testing purposes */
+class ITimeoutManager
+{
+public:
+
+ typedef boost::function<void()> Callback;
+ typedef boost::shared_ptr <Callback> CallbackPtr;
+
+ virtual ~ITimeoutManager() {}
+ virtual CallbackPtr RepeatAfterMs (const Callback &callback,
+ uint32_t initial,
+ uint32_t timeout) = 0;
+};
+
/* IEventListener defines an interface for WaylandInput to deliver
* simple events to. This interface is more or less artificial and used
* to break the dependency between the event transformation code
};
+/* KeyboardProcessor implements IKeyboardReceiver and transforms
+ * keyboard events into XBMC events for further processing.
+ *
+ * It needs to know whether or not a surface is in focus, so as soon
+ * as a surface is available, SetXBMCSurface should be called.
+ *
+ * KeyboardProcessor also performs key-repeat and registers a callback
+ * function to repeat the currently depressed key if it has not been
+ * released within a certain period. As such it depends on
+ * ITimeoutManager */
+class KeyboardProcessor :
+ public wayland::IKeyboardReceiver
+{
+public:
+
+ KeyboardProcessor(IDllXKBCommon &m_xkbCommonLibrary,
+ IEventListener &listener,
+ ITimeoutManager &timeouts);
+ ~KeyboardProcessor();
+
+ void SetXBMCSurface(struct wl_surface *xbmcWindow);
+
+private:
+
+ void UpdateKeymap(uint32_t format,
+ int fd,
+ uint32_t size);
+ void Enter(uint32_t serial,
+ struct wl_surface *surface,
+ struct wl_array *keys);
+ void Leave(uint32_t serial,
+ struct wl_surface *surface);
+ void Key(uint32_t serial,
+ uint32_t time,
+ uint32_t key,
+ enum wl_keyboard_key_state state);
+ void Modifier(uint32_t serial,
+ uint32_t depressed,
+ uint32_t latched,
+ uint32_t locked,
+ uint32_t group);
+
+ void SendKeyToXBMC(uint32_t key,
+ uint32_t sym,
+ uint32_t type);
+ void RepeatCallback(uint32_t key,
+ uint32_t sym);
+
+ IDllXKBCommon &m_xkbCommonLibrary;
+
+ /* KeyboardProcessor owns a keymap and does parts of its processing
+ * by delegating to the keymap the job of looking up generic keysyms
+ * for keycodes */
+ boost::scoped_ptr<ILinuxKeymap> m_keymap;
+ IEventListener &m_listener;
+ ITimeoutManager &m_timeouts;
+ struct wl_surface *m_xbmcWindow;
+
+ ITimeoutManager::CallbackPtr m_repeatCallback;
+ uint32_t m_repeatSym;
+
+ struct xkb_context *m_context;
+};
+
class EventDispatch :
public IEventListener
{
namespace
{
+/* WaylandEventLoop encapsulates the entire process of dispatching
+ * wayland events and timers that might be in place for duplicate
+ * processing. Calling its Dispatch() method will cause any pending
+ * timers and events to be dispatched. It implements ITimeoutManager
+ * and timeouts can be added directly to it */
+class WaylandEventLoop :
+ public xbmc::ITimeoutManager
+{
+public:
+
+ WaylandEventLoop(IDllWaylandClient &clientLibrary,
+ struct wl_display *display);
+
+ void Dispatch();
+
+ struct CallbackTracker
+ {
+ typedef boost::weak_ptr <Callback> CallbackObserver;
+
+ CallbackTracker(uint32_t time,
+ uint32_t initial,
+ const CallbackPtr &callback);
+
+ uint32_t time;
+ uint32_t remaining;
+ CallbackObserver callback;
+ };
+
+private:
+
+ CallbackPtr RepeatAfterMs(const Callback &callback,
+ uint32_t initial,
+ uint32_t timeout);
+ void DispatchTimers();
+
+ IDllWaylandClient &m_clientLibrary;
+
+ struct wl_display *m_display;
+ std::vector<CallbackTracker> m_callbackQueue;
+ CStopWatch m_stopWatch;
+};
+
/* WaylandInput is effectively just a manager class that encapsulates
* all input related information and ties together a wayland seat with
* the rest of the XBMC input handling subsystem. It is an internal
WaylandInput(IDllWaylandClient &clientLibrary,
IDllXKBCommon &xkbCommonLibrary,
struct wl_seat *seat,
- xbmc::EventDispatch &dispatch);
+ xbmc::EventDispatch &dispatch,
+ xbmc::ITimeoutManager &timeouts);
void SetXBMCSurface(struct wl_surface *s);
double surfaceY);
bool InsertPointer(struct wl_pointer *);
+ bool InsertKeyboard(struct wl_keyboard *);
void RemovePointer();
+ void RemoveKeyboard();
bool OnEvent(XBMC_Event &);
IDllXKBCommon &m_xkbCommonLibrary;
xbmc::PointerProcessor m_pointerProcessor;
+ xbmc::KeyboardProcessor m_keyboardProcessor;
boost::scoped_ptr<xw::Seat> m_seat;
boost::scoped_ptr<xw::Pointer> m_pointer;
+ boost::scoped_ptr<xw::Keyboard> m_keyboard;
};
xbmc::EventDispatch g_dispatch;
boost::scoped_ptr <WaylandInput> g_inputInstance;
+boost::scoped_ptr <WaylandEventLoop> g_eventLoop;
}
xbmc::PointerProcessor::PointerProcessor(IEventListener &listener,
m_cursorManager.SetCursor(0, NULL, 0, 0);
}
+xbmc::KeyboardProcessor::KeyboardProcessor(IDllXKBCommon &xkbCommonLibrary,
+ IEventListener &listener,
+ ITimeoutManager &timeouts) :
+ m_xkbCommonLibrary(xkbCommonLibrary),
+ m_listener(listener),
+ m_timeouts(timeouts),
+ m_xbmcWindow(NULL),
+ m_repeatSym(0),
+ m_context(NULL)
+{
+ enum xkb_context_flags flags =
+ static_cast<enum xkb_context_flags>(0);
+
+ /* KeyboardProcessor and not XKBKeymap owns the xkb_context. The
+ * xkb_context is merely just a detail for construction of the
+ * more interesting xkb_state and xkb_keymap objects.
+ *
+ * Failure to create the context effectively means that we will
+ * be unable to create a keymap or serve any useful purpose in
+ * processing key events. As such, it makes this an incomplete
+ * object and a runtime_error will be thrown */
+ m_context = m_xkbCommonLibrary.xkb_context_new(flags);
+
+ if (!m_context)
+ throw std::runtime_error("Failed to create xkb context");
+}
+
+xbmc::KeyboardProcessor::~KeyboardProcessor()
+{
+ m_xkbCommonLibrary.xkb_context_unref(m_context);
+}
+
+void
+xbmc::KeyboardProcessor::SetXBMCSurface(struct wl_surface *s)
+{
+ m_xbmcWindow = s;
+}
+
+/* Creates a new internal keymap representation for a serialized
+ * keymap as represented in shared memory as referred to by fd.
+ *
+ * Since the fd is sent to us via sendmsg(), the currently running
+ * process has ownership over it. As such, it MUST close the file
+ * descriptor after it has decided what to do with it in order to
+ * avoid a leak.
+ */
+void
+xbmc::KeyboardProcessor::UpdateKeymap(uint32_t format,
+ int fd,
+ uint32_t size)
+{
+ /* The file descriptor must always be closed */
+ BOOST_SCOPE_EXIT((fd))
+ {
+ close(fd);
+ } BOOST_SCOPE_EXIT_END
+
+ /* We don't understand anything other than xkbv1. If we get some
+ * other keyboard, then we can't process keyboard events reliably
+ * and that's a runtime error. */
+ if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1)
+ throw std::runtime_error("Server gave us a keymap we don't understand");
+
+ bool successfullyCreatedKeyboard = false;
+
+ /* Either throws or returns a valid struct xkb_keymap * */
+ struct xkb_keymap *keymap =
+ CXKBKeymap::ReceiveXKBKeymapFromSharedMemory(m_xkbCommonLibrary, m_context, fd, size);
+
+ BOOST_SCOPE_EXIT((&m_xkbCommonLibrary)(&successfullyCreatedKeyboard)(keymap))
+ {
+ if (!successfullyCreatedKeyboard)
+ m_xkbCommonLibrary.xkb_keymap_unref(keymap);
+ } BOOST_SCOPE_EXIT_END
+
+ struct xkb_state *state =
+ CXKBKeymap::CreateXKBStateFromKeymap(m_xkbCommonLibrary, keymap);
+
+ m_keymap.reset(new CXKBKeymap(m_xkbCommonLibrary,
+ keymap,
+ state));
+
+ successfullyCreatedKeyboard = true;
+}
+
+void
+xbmc::KeyboardProcessor::Enter(uint32_t serial,
+ struct wl_surface *surface,
+ struct wl_array *keys)
+{
+ if (surface == m_xbmcWindow)
+ {
+ m_listener.OnFocused();
+ }
+}
+
+void
+xbmc::KeyboardProcessor::Leave(uint32_t serial,
+ struct wl_surface *surface)
+{
+ if (surface == m_xbmcWindow)
+ {
+ m_listener.OnUnfocused();
+ }
+}
+
+void
+xbmc::KeyboardProcessor::SendKeyToXBMC(uint32_t key,
+ uint32_t sym,
+ uint32_t eventType)
+{
+ XBMC_Event event;
+ event.type = eventType;
+ event.key.keysym.scancode = key;
+ event.key.keysym.sym = static_cast<XBMCKey>(sym);
+ event.key.keysym.unicode = static_cast<XBMCKey>(sym);
+ event.key.keysym.mod =
+ static_cast<XBMCMod>(m_keymap->ActiveXBMCModifiers());
+ event.key.state = 0;
+ event.key.type = event.type;
+ event.key.which = '0';
+
+ m_listener.OnEvent(event);
+}
+
+void
+xbmc::KeyboardProcessor::RepeatCallback(uint32_t key,
+ uint32_t sym)
+{
+ /* Release and press the key again */
+ SendKeyToXBMC(key, sym, XBMC_KEYUP);
+ SendKeyToXBMC(key, sym, XBMC_KEYDOWN);
+}
+
+/* If this function is called before a keymap is set, then that
+ * is a precondition violation and a logic_error results */
+void
+xbmc::KeyboardProcessor::Key(uint32_t serial,
+ uint32_t time,
+ uint32_t key,
+ enum wl_keyboard_key_state state)
+{
+ if (!m_keymap.get())
+ throw std::logic_error("a keymap must be set before processing key events");
+
+ uint32_t sym = XKB_KEY_NoSymbol;
+
+ /* If we're unable to process a single key, then catch the error
+ * and report it, but don't allow it to be fatal */
+ try
+ {
+ sym = m_keymap->XBMCKeysymForKeycode(key);
+ }
+ catch (const std::runtime_error &err)
+ {
+ CLog::Log(LOGERROR, "%s: Failed to process keycode %i: %s",
+ __FUNCTION__, key, err.what());
+ return;
+ }
+
+ uint32_t keyEventType = 0;
+
+ switch (state)
+ {
+ case WL_KEYBOARD_KEY_STATE_PRESSED:
+ keyEventType = XBMC_KEYDOWN;
+ break;
+ case WL_KEYBOARD_KEY_STATE_RELEASED:
+ keyEventType = XBMC_KEYUP;
+ break;
+ default:
+ CLog::Log(LOGERROR, "%s: Unrecognized key state", __FUNCTION__);
+ return;
+ }
+
+ /* Key-repeat is handled on the client side so we need to add a new
+ * timeout here to repeat this symbol if it is still being held down
+ */
+ if (keyEventType == XBMC_KEYDOWN)
+ {
+ m_repeatCallback =
+ m_timeouts.RepeatAfterMs(boost::bind (
+ &KeyboardProcessor::RepeatCallback,
+ this,
+ key,
+ sym),
+ 1000,
+ 250);
+ m_repeatSym = sym;
+ }
+ else if (keyEventType == XBMC_KEYUP &&
+ sym == m_repeatSym)
+ m_repeatCallback.reset();
+
+ SendKeyToXBMC(key, sym, keyEventType);
+}
+
+/* We MUST update the keymap mask whenever we receive a new modifier
+ * event */
+void
+xbmc::KeyboardProcessor::Modifier(uint32_t serial,
+ uint32_t depressed,
+ uint32_t latched,
+ uint32_t locked,
+ uint32_t group)
+{
+ m_keymap->UpdateMask(depressed, latched, locked, group);
+}
+
/* Once EventDispatch recieves some information it just forwards
* it all on to XBMC */
bool xbmc::EventDispatch::OnEvent(XBMC_Event &e)
WaylandInput::WaylandInput(IDllWaylandClient &clientLibrary,
IDllXKBCommon &xkbCommonLibrary,
struct wl_seat *seat,
- xbmc::EventDispatch &dispatch) :
+ xbmc::EventDispatch &dispatch,
+ xbmc::ITimeoutManager &timeouts) :
m_clientLibrary(clientLibrary),
m_xkbCommonLibrary(xkbCommonLibrary),
m_pointerProcessor(dispatch, *this),
+ m_keyboardProcessor(m_xkbCommonLibrary, dispatch, timeouts),
m_seat(new xw::Seat(clientLibrary, seat, *this))
{
}
-void
-WaylandInput::SetXBMCSurface(struct wl_surface *s)
+void WaylandInput::SetXBMCSurface(struct wl_surface *s)
{
+ m_keyboardProcessor.SetXBMCSurface(s);
}
void WaylandInput::SetCursor(uint32_t serial,
return true;
}
+bool WaylandInput::InsertKeyboard(struct wl_keyboard *k)
+{
+ if (m_keyboard.get())
+ return false;
+
+ m_keyboard.reset(new xw::Keyboard(m_clientLibrary,
+ k,
+ m_keyboardProcessor));
+ return true;
+}
+
void WaylandInput::RemovePointer()
{
m_pointer.reset();
}
-CWinEventsWayland::CWinEventsWayland()
+void WaylandInput::RemoveKeyboard()
{
+ m_keyboard.reset();
}
-void CWinEventsWayland::RefreshDevices()
+WaylandEventLoop::WaylandEventLoop(IDllWaylandClient &clientLibrary,
+ struct wl_display *display) :
+ m_clientLibrary(clientLibrary),
+ m_display(display)
{
+ m_stopWatch.StartZero();
}
-bool CWinEventsWayland::IsRemoteLowBattery()
+namespace
{
- return false;
+bool TimeoutInactive(const WaylandEventLoop::CallbackTracker &tracker)
+{
+ return tracker.callback.expired();
}
-/* This function reads the display connection and dispatches
- * any events through the specified object listeners */
-bool CWinEventsWayland::MessagePump()
+void SubtractTimeoutAndTrigger(WaylandEventLoop::CallbackTracker &tracker,
+ int time)
{
- if (!g_display)
- return false;
+ int value = std::max(0, static_cast <int> (tracker.remaining - time));
+ if (value == 0)
+ {
+ tracker.remaining = time;
+ xbmc::ITimeoutManager::CallbackPtr callback (tracker.callback.lock());
+
+ (*callback) ();
+ }
+ else
+ tracker.remaining = value;
+}
+
+bool ByRemaining(const WaylandEventLoop::CallbackTracker &a,
+ const WaylandEventLoop::CallbackTracker &b)
+{
+ return a.remaining < b.remaining;
+}
+}
+WaylandEventLoop::CallbackTracker::CallbackTracker(uint32_t time,
+ uint32_t initial,
+ const xbmc::ITimeoutManager::CallbackPtr &cb) :
+ time(time),
+ remaining(time > initial ? time : initial),
+ callback(cb)
+{
+}
+
+void WaylandEventLoop::DispatchTimers()
+{
+ float elapsedMs = m_stopWatch.GetElapsedMilliseconds();
+ m_stopWatch.Stop();
+ /* We must subtract the elapsed time from each tracked timeout and
+ * trigger any remaining ones. If a timeout is triggered, then its
+ * remaining time will return to the original timeout value */
+ std::for_each(m_callbackQueue.begin(), m_callbackQueue.end (),
+ boost::bind(SubtractTimeoutAndTrigger,
+ _1,
+ static_cast<int>(elapsedMs)));
+ /* Timeout times may have changed so that the timeouts are no longer
+ * in order. Sort them so that they are. If they are unsorted,
+ * the ordering of two timeouts, one which was added just before
+ * the other which both reach a zero value at the same time,
+ * will be undefined. */
+ std::sort(m_callbackQueue.begin(), m_callbackQueue.end(),
+ ByRemaining);
+ m_stopWatch.StartZero();
+}
+
+void WaylandEventLoop::Dispatch()
+{
/* It is very important that these functions occurr in this order.
* Deadlocks might occurr otherwise.
*
* are a particular culprit here), or where events that we need to
* dispatch in order to keep going are never read.
*/
- g_clientLibrary->wl_display_dispatch_pending(g_display);
- g_clientLibrary->wl_display_flush(g_display);
- g_clientLibrary->wl_display_dispatch(g_display);
+ m_clientLibrary.wl_display_dispatch_pending(m_display);
+ m_clientLibrary.wl_display_flush(m_display);
+
+ /* Remove any timers which are no longer active */
+ m_callbackQueue.erase (std::remove_if(m_callbackQueue.begin(),
+ m_callbackQueue.end(),
+ TimeoutInactive),
+ m_callbackQueue.end());
+
+ DispatchTimers();
+
+ /* Calculate the poll timeout based on any current
+ * timers on the main loop. */
+ uint32_t minTimeout = 0;
+ for (std::vector<CallbackTracker>::iterator it = m_callbackQueue.begin();
+ it != m_callbackQueue.end();
+ ++it)
+ {
+ if (minTimeout < it->remaining)
+ minTimeout = it->remaining;
+ }
+
+ struct pollfd pfd;
+ pfd.events = POLLIN | POLLHUP | POLLERR;
+ pfd.revents = 0;
+ pfd.fd = m_clientLibrary.wl_display_get_fd(m_display);
+
+ int pollTimeout = minTimeout == 0 ?
+ -1 : minTimeout;
+
+ if (poll(&pfd, 1, pollTimeout) == -1)
+ throw std::runtime_error(strerror(errno));
+
+ DispatchTimers();
+ m_clientLibrary.wl_display_dispatch(m_display);
+}
+
+xbmc::ITimeoutManager::CallbackPtr
+WaylandEventLoop::RepeatAfterMs(const xbmc::ITimeoutManager::Callback &cb,
+ uint32_t initial,
+ uint32_t time)
+{
+ CallbackPtr ptr(new Callback(cb));
+
+ bool inserted = false;
+
+ for (std::vector<CallbackTracker>::iterator it = m_callbackQueue.begin();
+ it != m_callbackQueue.end();
+ ++it)
+ {
+ /* The appropriate place to insert is just before an existing
+ * timer which has a greater remaining time than ours */
+ if (it->remaining > time)
+ {
+ m_callbackQueue.insert(it, CallbackTracker(time, initial, ptr));
+ inserted = true;
+ break;
+ }
+ }
+
+ /* Insert at the back */
+ if (!inserted)
+ m_callbackQueue.push_back(CallbackTracker(time, initial, ptr));
+
+ return ptr;
+}
+
+CWinEventsWayland::CWinEventsWayland()
+{
+}
+
+void CWinEventsWayland::RefreshDevices()
+{
+}
+
+bool CWinEventsWayland::IsRemoteLowBattery()
+{
+ return false;
+}
+
+/* This function reads the display connection and dispatches
+ * any events through the specified object listeners */
+bool CWinEventsWayland::MessagePump()
+{
+ if (!g_eventLoop.get())
+ return false;
+
+ g_eventLoop->Dispatch();
return true;
}
return 0;
}
-void CWinEventsWayland::SetWaylandDisplay(IDllWaylandClient *clientLibrary,
+void CWinEventsWayland::SetWaylandDisplay(IDllWaylandClient &clientLibrary,
struct wl_display *d)
{
- g_clientLibrary = clientLibrary;
- g_display = d;
+ g_eventLoop.reset(new WaylandEventLoop(clientLibrary, d));
}
void CWinEventsWayland::DestroyWaylandDisplay()
* destroying the display */
MessagePump();
- g_display = NULL;
+ g_eventLoop.reset();
}
/* Once we know about a wayland seat, we can just create our manager
IDllXKBCommon &xkbCommonLibrary,
struct wl_seat *s)
{
- if (!g_display)
+ if (!g_eventLoop.get())
throw std::logic_error("Must have a wl_display set before setting "
"the wl_seat in CWinEventsWayland ");
-
+
g_inputInstance.reset(new WaylandInput(clientLibrary,
xkbCommonLibrary,
s,
- g_dispatch));
+ g_dispatch,
+ *g_eventLoop));
}
void CWinEventsWayland::DestroyWaylandSeat()
* a seat has been registered */
void CWinEventsWayland::SetXBMCSurface(struct wl_surface *s)
{
+ if (!g_inputInstance.get())
+ throw std::logic_error("Must have a wl_seat set before setting "
+ "the wl_surface in CWinEventsWayland");
+
+ g_inputInstance->SetXBMCSurface(s);
}
#endif
static void RefreshDevices();
static bool IsRemoteLowBattery();
- static void SetWaylandDisplay(IDllWaylandClient *clientLibrary,
+ static void SetWaylandDisplay(IDllWaylandClient &clientLibrary,
struct wl_display *d);
static void DestroyWaylandDisplay();
/* Tell CWinEvents what our display is. That way
* CWinEvents::MessagePump is now able to dispatch events from
* the display whenever it is called */
- (*m_eventInjector.setDisplay)(&clientLibrary,
+ (*m_eventInjector.setDisplay)(clientLibrary,
m_display->GetWlDisplay());
WaitForSynchronize();
struct EventInjector
{
- typedef void (*SetWaylandDisplay)(IDllWaylandClient *clientLibrary,
+ typedef void (*SetWaylandDisplay)(IDllWaylandClient &clientLibrary,
struct wl_display *display);
typedef void (*DestroyWaylandDisplay)();
typedef void (*SetWaylandSeat)(IDllWaylandClient &clientLibrary,
--- /dev/null
+/*
+ * Copyright (C) 2011-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 <sstream>
+#include <iostream>
+#include <stdexcept>
+
+#include <wayland-client.h>
+
+#include "windowing/DllWaylandClient.h"
+#include "windowing/WaylandProtocol.h"
+#include "Keyboard.h"
+
+namespace xw = xbmc::wayland;
+
+const struct wl_keyboard_listener xw::Keyboard::m_listener =
+{
+ Keyboard::HandleKeymapCallback,
+ Keyboard::HandleEnterCallback,
+ Keyboard::HandleLeaveCallback,
+ Keyboard::HandleKeyCallback,
+ Keyboard::HandleModifiersCallback
+};
+
+xw::Keyboard::Keyboard(IDllWaylandClient &clientLibrary,
+ struct wl_keyboard *keyboard,
+ IKeyboardReceiver &receiver) :
+ m_clientLibrary(clientLibrary),
+ m_keyboard(keyboard),
+ m_reciever(receiver)
+{
+ protocol::AddListenerOnWaylandObject(m_clientLibrary,
+ m_keyboard,
+ &m_listener,
+ this);
+}
+
+xw::Keyboard::~Keyboard()
+{
+ protocol::DestroyWaylandObject(m_clientLibrary,
+ m_keyboard);
+}
+
+void xw::Keyboard::HandleKeymapCallback(void *data,
+ struct wl_keyboard *keyboard,
+ uint32_t format,
+ int fd,
+ uint32_t size)
+{
+ static_cast <Keyboard *>(data)->HandleKeymap(format,
+ fd,
+ size);
+}
+
+void xw::Keyboard::HandleEnterCallback(void *data,
+ struct wl_keyboard *keyboard,
+ uint32_t serial,
+ struct wl_surface *surface,
+ struct wl_array *keys)
+{
+ static_cast<Keyboard *>(data)->HandleEnter(serial,
+ surface,
+ keys);
+}
+
+void xw::Keyboard::HandleLeaveCallback(void *data,
+ struct wl_keyboard *keyboard,
+ uint32_t serial,
+ struct wl_surface *surface)
+{
+ static_cast<Keyboard *>(data)->HandleLeave(serial,
+ surface);
+}
+
+void xw::Keyboard::HandleKeyCallback(void *data,
+ struct wl_keyboard *keyboard,
+ uint32_t serial,
+ uint32_t time,
+ uint32_t key,
+ uint32_t state)
+{
+ static_cast<Keyboard *>(data)->HandleKey(serial,
+ time,
+ key,
+ state);
+}
+
+void xw::Keyboard::HandleModifiersCallback(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_cast<Keyboard *>(data)->HandleModifiers(serial,
+ mods_depressed,
+ mods_latched,
+ mods_locked,
+ group);
+}
+
+void xw::Keyboard::HandleKeymap(uint32_t format,
+ int fd,
+ uint32_t size)
+{
+ m_reciever.UpdateKeymap(format, fd, size);
+}
+
+void xw::Keyboard::HandleEnter(uint32_t serial,
+ struct wl_surface *surface,
+ struct wl_array *keys)
+{
+ m_reciever.Enter(serial, surface, keys);
+}
+
+void xw::Keyboard::HandleLeave(uint32_t serial,
+ struct wl_surface *surface)
+{
+ m_reciever.Leave(serial, surface);
+}
+
+void xw::Keyboard::HandleKey(uint32_t serial,
+ uint32_t time,
+ uint32_t key,
+ uint32_t state)
+{
+ m_reciever.Key(serial,
+ time,
+ key,
+ static_cast<enum wl_keyboard_key_state>(state));
+}
+
+void xw::Keyboard::HandleModifiers(uint32_t serial,
+ uint32_t mods_depressed,
+ uint32_t mods_latched,
+ uint32_t mods_locked,
+ uint32_t group)
+{
+ m_reciever.Modifier(serial,
+ mods_depressed,
+ mods_latched,
+ mods_locked,
+ group);
+}
--- /dev/null
+#pragma once
+
+/*
+ * Copyright (C) 2011-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 <boost/noncopyable.hpp>
+
+#include <wayland-client.h>
+
+class IDllWaylandClient;
+
+namespace xbmc
+{
+namespace wayland
+{
+class IKeyboardReceiver
+{
+public:
+
+ virtual ~IKeyboardReceiver() {}
+
+ virtual void UpdateKeymap(uint32_t format,
+ int fd,
+ uint32_t size) = 0;
+ virtual void Enter(uint32_t serial,
+ struct wl_surface *surface,
+ struct wl_array *keys) = 0;
+ virtual void Leave(uint32_t serial,
+ struct wl_surface *surface) = 0;
+ virtual void Key(uint32_t serial,
+ uint32_t time,
+ uint32_t key,
+ enum wl_keyboard_key_state state) = 0;
+ virtual void Modifier(uint32_t serial,
+ uint32_t depressed,
+ uint32_t latched,
+ uint32_t locked,
+ uint32_t group) = 0;
+};
+
+/* Wrapper class for a keyboard object. Generally there is one keyboard
+ * per seat.
+ *
+ * Keyboard events are translated into a more readable form and
+ * forwarded on to the injected IKeyboardReceiver for further
+ * processing.
+ *
+ * Many of these events require some shared agreement between the
+ * compositor and the client as to the keymap in use. A file descriptor
+ * for a shared memory region to a serialized keymap parsable
+ * with libxkbcommon is provided in HandleKeymap and to the
+ * registered IKeyboardReceiever through UpdateKeymap. The delegate for
+ * that interface should ascertain the intended keymap before processing
+ * any other events.
+ */
+class Keyboard :
+ public boost::noncopyable
+{
+public:
+
+ Keyboard(IDllWaylandClient &,
+ struct wl_keyboard *,
+ IKeyboardReceiver &);
+ ~Keyboard();
+
+ struct wl_keyboard * GetWlKeyboard();
+
+ static void HandleKeymapCallback(void *,
+ struct wl_keyboard *,
+ uint32_t,
+ int,
+ uint32_t);
+ static void HandleEnterCallback(void *,
+ struct wl_keyboard *,
+ uint32_t,
+ struct wl_surface *,
+ struct wl_array *);
+ static void HandleLeaveCallback(void *,
+ struct wl_keyboard *,
+ uint32_t,
+ struct wl_surface *);
+ static void HandleKeyCallback(void *,
+ struct wl_keyboard *,
+ uint32_t,
+ uint32_t,
+ uint32_t,
+ uint32_t);
+ static void HandleModifiersCallback(void *,
+ struct wl_keyboard *,
+ uint32_t,
+ uint32_t,
+ uint32_t,
+ uint32_t,
+ uint32_t);
+
+private:
+
+ void HandleKeymap(uint32_t format,
+ int fd,
+ uint32_t size);
+ void HandleEnter(uint32_t serial,
+ struct wl_surface *surface,
+ struct wl_array *keys);
+ void HandleLeave(uint32_t serial,
+ struct wl_surface *surface);
+ void HandleKey(uint32_t serial,
+ uint32_t time,
+ uint32_t key,
+ uint32_t state);
+ void HandleModifiers(uint32_t serial,
+ uint32_t mods_depressed,
+ uint32_t mods_latched,
+ uint32_t mods_locked,
+ uint32_t group);
+
+ static const struct wl_keyboard_listener m_listener;
+
+ IDllWaylandClient &m_clientLibrary;
+ struct wl_keyboard *m_keyboard;
+ IKeyboardReceiver &m_reciever;
+};
+}
+}
m_input.InsertPointer(pointer);
}
+ if (newCaps & WL_SEAT_CAPABILITY_KEYBOARD)
+ {
+ struct wl_keyboard *keyboard =
+ protocol::CreateWaylandObject<struct wl_keyboard *,
+ struct wl_seat *>(m_clientLibrary,
+ m_seat,
+ m_clientLibrary.Get_wl_keyboard_interface());
+ protocol::CallMethodOnWaylandObject(m_clientLibrary,
+ m_seat,
+ WL_SEAT_GET_KEYBOARD,
+ keyboard);
+ m_input.InsertKeyboard(keyboard);
+ }
+
if (lostCaps & WL_SEAT_CAPABILITY_POINTER)
m_input.RemovePointer();
+ if (lostCaps & WL_SEAT_CAPABILITY_KEYBOARD)
+ m_input.RemoveKeyboard();
+
m_currentCapabilities = cap;
}
virtual ~IInputReceiver() {}
virtual bool InsertPointer(struct wl_pointer *pointer) = 0;
+ virtual bool InsertKeyboard(struct wl_keyboard *keyboard) = 0;
virtual void RemovePointer() = 0;
+ virtual void RemoveKeyboard() = 0;
};
class Seat :