Listen for a new wl_keyboard from wl_seat_listener and create an
authorSam Spilsbury <smspillaz@gmail.com>
Thu, 25 Jul 2013 05:59:25 +0000 (13:59 +0800)
committerSam Spilsbury <smspillaz@gmail.com>
Fri, 11 Oct 2013 16:34:37 +0000 (00:34 +0800)
XKBKeymap implementing wayland::Keymap. This class keeps track
of the keyboard state and MUST be updated every time a modifier or
a key is pressed (eg, every time we get an event from the protocol).

KeyboardProcessor implements the logic of converting XKB keysyms to
XBMC keysyms. It also changes the focus state of the window and
delivers key events to the XBMC event subsystem.

TimeoutManager and WaylandEventLoop are just a simple abstraction for
adding timeouts to the poll () / wl_display_dispatch () mainloop, in
place so that we can implement key repeat

17 files changed:
.gitignore
configure.in
xbmc/input/linux/Keymap.h [new file with mode: 0644]
xbmc/input/linux/Makefile [deleted file]
xbmc/input/linux/Makefile.in [new file with mode: 0644]
xbmc/input/linux/XKBCommonKeymap.cpp [new file with mode: 0644]
xbmc/input/linux/XKBCommonKeymap.h [new file with mode: 0644]
xbmc/windowing/DllXKBCommon.h
xbmc/windowing/Makefile.in
xbmc/windowing/WinEventsWayland.cpp
xbmc/windowing/WinEventsWayland.h
xbmc/windowing/egl/wayland/XBMCConnection.cpp
xbmc/windowing/egl/wayland/XBMCConnection.h
xbmc/windowing/wayland/Keyboard.cpp [new file with mode: 0644]
xbmc/windowing/wayland/Keyboard.h [new file with mode: 0644]
xbmc/windowing/wayland/Seat.cpp
xbmc/windowing/wayland/Seat.h

index 187fab7..7f59570 100644 (file)
@@ -591,6 +591,9 @@ lib/cmyth/Makefile
 /xbmc/guilib/Profile
 /xbmc/guilib/Profile_FastCap
 
+# /xbmc/input
+/xbmc/input/linux/Makefile
+
 # /xbmc/interfaces/
 /xbmc/interfaces/Makefile
 /xbmc/interfaces/python/Makefile
index 4b09ddd..99319d7 100644 (file)
@@ -979,10 +979,11 @@ if test "$use_wayland" = "yes" && test "$host_vendor" != "apple"; then
     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
@@ -2163,6 +2164,7 @@ fi
 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
@@ -2487,6 +2489,7 @@ OUTPUT_FILES="Makefile \
     xbmc/music/karaoke/Makefile \
     xbmc/osx/Makefile \
     xbmc/guilib/Makefile \
+    xbmc/input/linux/Makefile \
     xbmc/interfaces/Makefile \
     xbmc/network/Makefile \
     xbmc/network/upnp/Makefile \
@@ -2601,6 +2604,7 @@ AC_SUBST(USE_MYSQL)
 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)
diff --git a/xbmc/input/linux/Keymap.h b/xbmc/input/linux/Keymap.h
new file mode 100644 (file)
index 0000000..4fe9952
--- /dev/null
@@ -0,0 +1,36 @@
+#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;
+};
diff --git a/xbmc/input/linux/Makefile b/xbmc/input/linux/Makefile
deleted file mode 100644 (file)
index 33885a5..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-SRCS=LIRC.cpp \
-  LinuxInputDevices.cpp
-
-LIB=input_linux.a
-
-include ../../../Makefile.include
--include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS)))
diff --git a/xbmc/input/linux/Makefile.in b/xbmc/input/linux/Makefile.in
new file mode 100644 (file)
index 0000000..56b428e
--- /dev/null
@@ -0,0 +1,12 @@
+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)))
diff --git a/xbmc/input/linux/XKBCommonKeymap.cpp b/xbmc/input/linux/XKBCommonKeymap.cpp
new file mode 100644 (file)
index 0000000..fe777b4
--- /dev/null
@@ -0,0 +1,337 @@
+/*
+ *      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;
+}
diff --git a/xbmc/input/linux/XKBCommonKeymap.h b/xbmc/input/linux/XKBCommonKeymap.h
new file mode 100644 (file)
index 0000000..9fa3cf6
--- /dev/null
@@ -0,0 +1,78 @@
+#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;
+};
index cdeefea..e3071e0 100644 (file)
@@ -31,11 +31,57 @@ class IDllXKBCommon
 {
 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()
 };
index 353fcca..bee9e24 100644 (file)
@@ -7,7 +7,8 @@ SRCS=WinEventsSDL.cpp \
 # Wayland implementation detail
 ifeq (@USE_WAYLAND@,1)
 SRCS += wayland/Seat.cpp \
-        wayland/Pointer.cpp
+        wayland/Pointer.cpp \
+        wayland/Keyboard.cpp
 endif
 
 LIB=windowing.a
index bc86475..cfdca71 100644 (file)
@@ -34,7 +34,6 @@
 #include <boost/weak_ptr.hpp>
 
 #include <sys/poll.h>
-#include <sys/mman.h>
 
 #include <wayland-client.h>
 #include <xkbcommon/xkbcommon.h>
@@ -44,6 +43,7 @@
 #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
@@ -131,6 +148,70 @@ private:
   
 };
 
+/* 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
 {
@@ -146,6 +227,48 @@ namespace xw = xbmc::wayland;
 
 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
@@ -159,7 +282,8 @@ public:
   WaylandInput(IDllWaylandClient &clientLibrary,
                IDllXKBCommon &xkbCommonLibrary,
                struct wl_seat *seat,
-               xbmc::EventDispatch &dispatch);
+               xbmc::EventDispatch &dispatch,
+               xbmc::ITimeoutManager &timeouts);
 
   void SetXBMCSurface(struct wl_surface *s);
 
@@ -171,8 +295,10 @@ private:
                  double surfaceY);
 
   bool InsertPointer(struct wl_pointer *);
+  bool InsertKeyboard(struct wl_keyboard *);
 
   void RemovePointer();
+  void RemoveKeyboard();
 
   bool OnEvent(XBMC_Event &);
 
@@ -180,13 +306,16 @@ private:
   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,
@@ -311,6 +440,215 @@ xbmc::PointerProcessor::Enter(struct wl_surface *surface,
   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)
@@ -335,17 +673,19 @@ bool xbmc::EventDispatch::OnUnfocused()
 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,
@@ -367,31 +707,96 @@ bool WaylandInput::InsertPointer(struct wl_pointer *p)
   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.
    * 
@@ -412,9 +817,94 @@ bool CWinEventsWayland::MessagePump()
    * 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;
 }
@@ -425,11 +915,10 @@ size_t CWinEventsWayland::GetQueueSize()
   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()
@@ -438,7 +927,7 @@ 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
@@ -449,14 +938,15 @@ void CWinEventsWayland::SetWaylandSeat(IDllWaylandClient &clientLibrary,
                                        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()
@@ -471,6 +961,11 @@ 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
index 7f62fe4..9927775 100644 (file)
@@ -40,7 +40,7 @@ public:
   static void RefreshDevices();
   static bool IsRemoteLowBattery();
 
-  static void SetWaylandDisplay(IDllWaylandClient *clientLibrary,
+  static void SetWaylandDisplay(IDllWaylandClient &clientLibrary,
                                 struct wl_display *d);
   static void DestroyWaylandDisplay();
 
index 41962dd..1cbc5fb 100644 (file)
@@ -132,7 +132,7 @@ xw::XBMCConnection::Private::Private(IDllWaylandClient &clientLibrary,
   /* 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();
index 86120f8..9d3ce69 100644 (file)
@@ -49,7 +49,7 @@ public:
 
   struct EventInjector
   {
-    typedef void (*SetWaylandDisplay)(IDllWaylandClient *clientLibrary,
+    typedef void (*SetWaylandDisplay)(IDllWaylandClient &clientLibrary,
                                       struct wl_display *display);
     typedef void (*DestroyWaylandDisplay)();
     typedef void (*SetWaylandSeat)(IDllWaylandClient &clientLibrary,
diff --git a/xbmc/windowing/wayland/Keyboard.cpp b/xbmc/windowing/wayland/Keyboard.cpp
new file mode 100644 (file)
index 0000000..99e7ced
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ *      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);
+}
diff --git a/xbmc/windowing/wayland/Keyboard.h b/xbmc/windowing/wayland/Keyboard.h
new file mode 100644 (file)
index 0000000..b59e61c
--- /dev/null
@@ -0,0 +1,139 @@
+#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;
+};
+}
+}
index 2d4fd5a..15c5571 100644 (file)
@@ -88,8 +88,25 @@ void xw::Seat::HandleCapabilities(enum wl_seat_capability cap)
     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;
 }
index bf4c434..4729ac4 100644 (file)
@@ -36,8 +36,10 @@ public:
   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 :