InputRedirection for keyboard events

Major new functionality is xkbcommon support. InputRedirection holds an
instance to a small wrapper class which has the xkb context, keymap and
state. The keymap is initialied from the file descriptor we get from the
Wayland backend.

InputRedirection uses this to translate the keycodes into keysymbols and
to QString and to track the modifiers as provided by the
Qt::KeybordModifiers flags.

This provides us enough information for internal usage (e.g. pass through
effects if they have "grabbed" the keyboard).

If KWin doesn't filter out the key events, it passes them on to the
currently active Client respectively an unmanaged on top of the stack.
This needs still some improvement (not each unmanaged should get the
event). The Client/Unmnaged still uses xtest extension to send the key
events to the window. So keylogging is still possible.
icc-effect-5.14.5
Martin Gräßlin 2013-07-02 11:44:18 +02:00
parent 104e89aff2
commit 6baf794f88
12 changed files with 286 additions and 21 deletions

View File

@ -61,6 +61,11 @@ configure_file(libkwineffects/kwinconfig.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/lib
# for kwin internal things
set(HAVE_WAYLAND ${Wayland_Client_FOUND})
set(HAVE_WAYLAND_EGL ${Wayland_Egl_FOUND} AND ${Wayland_Client_FOUND})
if(${HAVE_WAYLAND})
set(HAVE_XKB ${XKB_FOUND})
else()
set(HAVE_XKB FALSE)
endif()
configure_file(config-kwin.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kwin.h )

View File

@ -2586,6 +2586,16 @@ void Client::sendPointerAxisEvent(InputRedirection::PointerAxis axis, qreal delt
}
}
void Client::sendKeybordKeyEvent(uint32_t key, InputRedirection::KeyboardKeyState state)
{
// TODO: don't use xtest
uint8_t type = XCB_KEY_PRESS;
if (state == InputRedirection::KeyboardKeyReleased) {
type = XCB_KEY_RELEASE;
}
xcb_test_fake_input(connection(), type, key + 8, XCB_TIME_CURRENT_TIME, frameId(), 0, 0, 0);
}
} // namespace
#include "client.moc"

View File

@ -656,6 +656,7 @@ public:
void sendPointerButtonEvent(uint32_t button, InputRedirection::PointerButtonState state) override;
void sendPointerAxisEvent(InputRedirection::PointerAxis axis, qreal delta) override;
void sendKeybordKeyEvent(uint32_t key, InputRedirection::KeyboardKeyState state) override;
public Q_SLOTS:
void closeWindow();

View File

@ -10,3 +10,4 @@
#define KWIN_VERSION_STRING "${KDE4WORKSPACE_VERSION}"
#cmakedefine01 HAVE_WAYLAND
#cmakedefine01 HAVE_WAYLAND_EGL
#cmakedefine01 HAVE_XKB

View File

@ -741,9 +741,11 @@ bool EffectsHandlerImpl::grabKeyboard(Effect* effect)
{
if (keyboard_grab_effect != NULL)
return false;
bool ret = grabXKeyboard();
if (!ret)
return false;
if (kwinApp()->operationMode() == Application::OperationModeX11) {
bool ret = grabXKeyboard();
if (!ret)
return false;
}
keyboard_grab_effect = effect;
return true;
}
@ -751,7 +753,9 @@ bool EffectsHandlerImpl::grabKeyboard(Effect* effect)
void EffectsHandlerImpl::ungrabKeyboard()
{
assert(keyboard_grab_effect != NULL);
ungrabXKeyboard();
if (kwinApp()->operationMode() == Application::OperationModeX11) {
ungrabXKeyboard();
}
keyboard_grab_effect = NULL;
}

183
input.cpp
View File

@ -22,18 +22,140 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "effects.h"
#include "unmanaged.h"
#include "workspace.h"
// KDE
#include <kkeyserver.h>
// TODO: remove xtest
#include <xcb/xtest.h>
// system
#include <linux/input.h>
#include <sys/mman.h>
namespace KWin
{
#if HAVE_XKB
Xkb::Xkb()
: m_context(xkb_context_new(static_cast<xkb_context_flags>(0)))
, m_keymap(NULL)
, m_state(NULL)
, m_shiftModifier(0)
, m_controlModifier(0)
, m_altModifier(0)
, m_metaModifier(0)
, m_modifiers(Qt::NoModifier)
{
if (!m_context) {
qDebug() << "Could not create xkb context";
}
}
Xkb::~Xkb()
{
xkb_state_unref(m_state);
xkb_keymap_unref(m_keymap);
xkb_context_unref(m_context);
}
void Xkb::installKeymap(int fd, uint32_t size)
{
if (!m_context) {
return;
}
char *map = reinterpret_cast<char*>(mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0));
if (map == MAP_FAILED) {
return;
}
xkb_keymap *keymap = xkb_keymap_new_from_string(m_context, map, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_MAP_COMPILE_PLACEHOLDER);
munmap(map, size);
if (!keymap) {
qDebug() << "Could not map keymap from file";
return;
}
xkb_state *state = xkb_state_new(keymap);
if (!state) {
qDebug() << "Could not create XKB state";
xkb_keymap_unref(keymap);
return;
}
// now release the old ones
xkb_state_unref(m_state);
xkb_keymap_unref(m_keymap);
m_keymap = keymap;
m_state = state;
m_shiftModifier = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_SHIFT);
m_controlModifier = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_CTRL);
m_altModifier = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_ALT);
m_metaModifier = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_LOGO);
}
void Xkb::updateModifiers(uint32_t modsDepressed, uint32_t modsLatched, uint32_t modsLocked, uint32_t group)
{
if (!m_keymap || !m_state) {
return;
}
xkb_state_update_mask(m_state, modsDepressed, modsLatched, modsLocked, 0, 0, group);
Qt::KeyboardModifiers mods = Qt::NoModifier;
if (xkb_state_mod_index_is_active(m_state, m_shiftModifier, XKB_STATE_MODS_EFFECTIVE) == 1) {
mods |= Qt::ShiftModifier;
}
if (xkb_state_mod_index_is_active(m_state, m_altModifier, XKB_STATE_MODS_EFFECTIVE) == 1) {
mods |= Qt::AltModifier;
}
if (xkb_state_mod_index_is_active(m_state, m_controlModifier, XKB_STATE_MODS_EFFECTIVE) == 1) {
mods |= Qt::ControlModifier;
}
if (xkb_state_mod_index_is_active(m_state, m_metaModifier, XKB_STATE_MODS_EFFECTIVE) == 1) {
mods |= Qt::MetaModifier;
}
m_modifiers = mods;
}
void Xkb::updateKey(uint32_t key, InputRedirection::KeyboardKeyState state)
{
if (!m_keymap || !m_state) {
return;
}
xkb_state_update_key(m_state, key + 8, static_cast<xkb_key_direction>(state));
}
xkb_keysym_t Xkb::toKeysym(uint32_t key)
{
if (!m_state) {
return XKB_KEY_NoSymbol;
}
return xkb_state_key_get_one_sym(m_state, key + 8);
}
QString Xkb::toString(xkb_keysym_t keysym)
{
if (!m_state || keysym == XKB_KEY_NoSymbol) {
return QString();
}
QByteArray byteArray(7, 0);
int ok = xkb_keysym_to_utf8(keysym, byteArray.data(), byteArray.size());
if (ok == -1 || ok == 0) {
return QString();
}
return QString::fromUtf8(byteArray.constData());
}
Qt::Key Xkb::toQtKey(xkb_keysym_t keysym)
{
int key = Qt::Key_unknown;
KKeyServer::symXToKeyQt(keysym, &key);
return static_cast<Qt::Key>(key);
}
#endif
KWIN_SINGLETON_FACTORY(InputRedirection)
InputRedirection::InputRedirection(QObject *parent)
: QObject(parent)
#if HAVE_XKB
, m_xkb(new Xkb())
#endif
, m_pointerWindow()
{
}
@ -71,9 +193,8 @@ void InputRedirection::processPointerMotion(const QPointF &pos, uint32_t time)
emit globalPointerChanged(m_globalPointer);
// TODO: check which part of KWin would like to intercept the event
// TODO: keyboard modifiers
QMouseEvent event(QEvent::MouseMove, m_globalPointer.toPoint(), m_globalPointer.toPoint(),
Qt::NoButton, qtButtonStates(), 0);
Qt::NoButton, qtButtonStates(), keyboardModifiers());
// check whether an effect has a mouse grab
if (effects && static_cast<EffectsHandlerImpl*>(effects)->checkInputWindowEvent(&event)) {
// an effect grabbed the pointer, we do not forward the event to surfaces
@ -97,9 +218,8 @@ void InputRedirection::processPointerButton(uint32_t button, InputRedirection::P
m_pointerButtons[button] = state;
emit pointerButtonStateChanged(button, state);
// TODO: keyboard modifiers
QMouseEvent event(buttonStateToEvent(state), m_globalPointer.toPoint(), m_globalPointer.toPoint(),
buttonToQtMouseButton(button), qtButtonStates(), 0);
buttonToQtMouseButton(button), qtButtonStates(), keyboardModifiers());
// check whether an effect has a mouse grab
if (effects && static_cast<EffectsHandlerImpl*>(effects)->checkInputWindowEvent(&event)) {
// an effect grabbed the pointer, we do not forward the event to surfaces
@ -130,6 +250,51 @@ void InputRedirection::processPointerAxis(InputRedirection::PointerAxis axis, qr
m_pointerWindow.data()->sendPointerAxisEvent(axis, delta);
}
void InputRedirection::processKeyboardKey(uint32_t key, InputRedirection::KeyboardKeyState state, uint32_t time)
{
Q_UNUSED(time)
#if HAVE_XKB
m_xkb->updateKey(key, state);
// TODO: process global shortcuts
// TODO: pass to internal parts of KWin
if (effects && static_cast< EffectsHandlerImpl* >(effects)->hasKeyboardGrab()) {
const xkb_keysym_t keysym = m_xkb->toKeysym(key);
// TODO: start auto-repeat
// TODO: add modifiers to the event
const QEvent::Type type = (state == KeyboardKeyPressed) ? QEvent::KeyPress : QEvent::KeyRelease;
QKeyEvent event(type, m_xkb->toQtKey(keysym), m_xkb->modifiers(), m_xkb->toString(keysym));
static_cast< EffectsHandlerImpl* >(effects)->grabbedKeyboardEvent(&event);
return;
}
#endif
// TODO: handle move resize
// check unmanaged
if (!workspace()->unmanagedList().isEmpty()) {
// TODO: better check whether this unmanaged should get the key event
workspace()->unmanagedList().first()->sendKeybordKeyEvent(key, state);
return;
}
if (Client *client = workspace()->activeClient()) {
client->sendKeybordKeyEvent(key, state);
}
}
void InputRedirection::processKeyboardModifiers(uint32_t modsDepressed, uint32_t modsLatched, uint32_t modsLocked, uint32_t group)
{
// TODO: send to proper Client and also send when active Client changes
#if HAVE_XKB
m_xkb->updateModifiers(modsDepressed, modsLatched, modsLocked, group);
#endif
}
void InputRedirection::processKeymapChange(int fd, uint32_t size)
{
// TODO: should we pass the keymap to our Clients? Or only to the currently active one and update
#if HAVE_XKB
m_xkb->installKeymap(fd, size);
#endif
}
QEvent::Type InputRedirection::buttonStateToEvent(InputRedirection::PointerButtonState state)
{
switch (state) {
@ -241,4 +406,14 @@ uint8_t InputRedirection::toXPointerButton(InputRedirection::PointerAxis axis, q
return XCB_BUTTON_INDEX_ANY;
}
Qt::KeyboardModifiers InputRedirection::keyboardModifiers() const
{
#if HAVE_XKB
return m_xkb->modifiers();
#else
return Qt::NoModifier;
#endif
}
} // namespace

58
input.h
View File

@ -25,10 +25,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <QPoint>
#include <QEvent>
#include <QWeakPointer>
#include <config-kwin.h>
#if HAVE_XKB
#include <xkbcommon/xkbcommon.h>
#endif
namespace KWin
{
class Toplevel;
class Xkb;
/**
* @brief This class is responsible for redirecting incoming input to the surface which currently
@ -51,6 +56,10 @@ public:
PointerAxisVertical,
PointerAxisHorizontal
};
enum KeyboardKeyState {
KeyboardKeyReleased,
KeyboardKeyPressed
};
virtual ~InputRedirection();
/**
@ -66,6 +75,7 @@ public:
*/
PointerButtonState pointerButtonState(uint32_t button) const;
Qt::MouseButtons qtButtonStates() const;
Qt::KeyboardModifiers keyboardModifiers() const;
/**
* @internal
@ -79,6 +89,18 @@ public:
* @internal
*/
void processPointerAxis(PointerAxis axis, qreal delta, uint32_t time);
/**
* @internal
*/
void processKeyboardKey(uint32_t key, KeyboardKeyState state, uint32_t time);
/**
* @internal
*/
void processKeyboardModifiers(uint32_t modsDepressed, uint32_t modsLatched, uint32_t modsLocked, uint32_t group);
/**
* @internal
**/
void processKeymapChange(int fd, uint32_t size);
static uint8_t toXPointerButton(uint32_t button);
static uint8_t toXPointerButton(PointerAxis axis, qreal delta);
@ -114,6 +136,9 @@ private:
Toplevel *findToplevel(const QPoint &pos);
QPointF m_globalPointer;
QHash<uint32_t, PointerButtonState> m_pointerButtons;
#if HAVE_XKB
QScopedPointer<Xkb> m_xkb;
#endif
/**
* @brief The Toplevel which currently receives pointer events
*/
@ -123,6 +148,31 @@ private:
friend InputRedirection *input();
};
#if HAVE_XKB
class Xkb
{
public:
Xkb();
~Xkb();
void installKeymap(int fd, uint32_t size);
void updateModifiers(uint32_t modsDepressed, uint32_t modsLatched, uint32_t modsLocked, uint32_t group);
void updateKey(uint32_t key, InputRedirection::KeyboardKeyState state);
xkb_keysym_t toKeysym(uint32_t key);
QString toString(xkb_keysym_t keysym);
Qt::Key toQtKey(xkb_keysym_t keysym);
Qt::KeyboardModifiers modifiers() const;
private:
xkb_context *m_context;
xkb_keymap *m_keymap;
xkb_state *m_state;
xkb_mod_index_t m_shiftModifier;
xkb_mod_index_t m_controlModifier;
xkb_mod_index_t m_altModifier;
xkb_mod_index_t m_metaModifier;
Qt::KeyboardModifiers m_modifiers;
};
#endif
inline
InputRedirection *input()
{
@ -146,6 +196,14 @@ InputRedirection::PointerButtonState InputRedirection::pointerButtonState(uint32
}
}
#if HAVE_XKB
inline
Qt::KeyboardModifiers Xkb::modifiers() const
{
return m_modifiers;
}
#endif
} // namespace KWin
#endif // KWIN_INPUT_H

View File

@ -521,6 +521,12 @@ void Toplevel::sendPointerAxisEvent(InputRedirection::PointerAxis axis, qreal de
Q_UNUSED(delta)
}
void Toplevel::sendKeybordKeyEvent(uint32_t key, InputRedirection::KeyboardKeyState state)
{
Q_UNUSED(key)
Q_UNUSED(state)
}
} // namespace
#include "toplevel.moc"

View File

@ -325,6 +325,7 @@ public:
virtual void sendPointerLeaveEvent(const QPointF &globalPos);
virtual void sendPointerButtonEvent(uint32_t button, InputRedirection::PointerButtonState state);
virtual void sendPointerAxisEvent(InputRedirection::PointerAxis axis, qreal delta);
virtual void sendKeybordKeyEvent(uint32_t key, InputRedirection::KeyboardKeyState state);
Q_SIGNALS:
void opacityChanged(KWin::Toplevel* toplevel, qreal oldOpacity);

View File

@ -174,6 +174,16 @@ void Unmanaged::sendPointerAxisEvent(InputRedirection::PointerAxis axis, qreal d
}
}
void Unmanaged::sendKeybordKeyEvent(uint32_t key, InputRedirection::KeyboardKeyState state)
{
// TODO: don't use xtest
uint8_t type = XCB_KEY_PRESS;
if (state == InputRedirection::KeyboardKeyReleased) {
type = XCB_KEY_RELEASE;
}
xcb_test_fake_input(connection(), type, key + 8, XCB_TIME_CURRENT_TIME, window(), 0, 0, 0);
}
} // namespace
#include "unmanaged.moc"

View File

@ -50,6 +50,7 @@ public:
void sendPointerButtonEvent(uint32_t button, InputRedirection::PointerButtonState state) override;
void sendPointerAxisEvent(InputRedirection::PointerAxis axis, qreal delta) override;
void sendKeybordKeyEvent(uint32_t key, InputRedirection::KeyboardKeyState state) override;
protected:
virtual void debug(QDebug& stream) const;
virtual bool shouldUnredirect() const;

View File

@ -162,9 +162,10 @@ static void keyboardHandleKeymap(void *data, wl_keyboard *keyboard,
{
Q_UNUSED(data)
Q_UNUSED(keyboard)
Q_UNUSED(format)
Q_UNUSED(fd)
Q_UNUSED(size)
if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
return;
}
input()->processKeymapChange(fd, size);
}
static void keyboardHandleEnter(void *data, wl_keyboard *keyboard,
@ -192,12 +193,7 @@ static void keyboardHandleKey(void *data, wl_keyboard *keyboard, uint32_t serial
Q_UNUSED(data)
Q_UNUSED(keyboard)
Q_UNUSED(serial)
Q_UNUSED(time)
uint8_t type = XCB_KEY_PRESS;
if (state == WL_KEYBOARD_KEY_STATE_RELEASED) {
type = XCB_KEY_RELEASE;
}
xcb_test_fake_input(connection(), type, key + 8 /*magic*/, XCB_TIME_CURRENT_TIME, XCB_WINDOW_NONE, 0, 0, 0);
input()->processKeyboardKey(key, static_cast<InputRedirection::KeyboardKeyState>(state), time);
}
static void keyboardHandleModifiers(void *data, wl_keyboard *keyboard, uint32_t serial, uint32_t modsDepressed,
@ -206,10 +202,7 @@ static void keyboardHandleModifiers(void *data, wl_keyboard *keyboard, uint32_t
Q_UNUSED(data)
Q_UNUSED(keyboard)
Q_UNUSED(serial)
Q_UNUSED(modsDepressed)
Q_UNUSED(modsLatched)
Q_UNUSED(modsLocked)
Q_UNUSED(group)
input()->processKeyboardModifiers(modsDepressed, modsLatched, modsLocked, group);
}
static void bufferRelease(void *data, wl_buffer *wl_buffer)