<commit_before>/* * This file is part of meego-keyboard *
 *
 * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
 * All rights reserved.
 * Contact: Nokia Corporation (<EMAIL>)
 *
 * If you have questions regarding the use of this file, please contact
 * Nokia at <EMAIL>.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1 as published by the Free Software Foundation
 * and appearing in the file LICENSE.LGPL included in the packaging
 * of this file.
 */

#include <QDebug>
#include <QX11Info>
#include <QTextCodec>
#include <algorithm>

#include <minputcontextconnection.h>

#include "mhardwarekeyboard.h"
#include "layoutsmanager.h"

#include <X11/X.h>
#undef KeyPress
#undef KeyRelease
#include <X11/XKBlib.h>
#define XK_MISCELLANY
#define XK_XKB_KEYS
#include <X11/keysymdef.h>

#define ELEMENTS(array) (sizeof(array)/sizeof((array)[0]))

namespace
{
    const Qt::KeyboardModifier FnLevelModifier = Qt::GroupSwitchModifier;
    const Qt::Key FnLevelKey = Qt::Key_AltGr;
    const Qt::Key SymKey = Qt::Key_Multi_key;
    const unsigned int SymModifierMask = Mod4Mask;
    const unsigned int FnModifierMask = Mod5Mask;
    const unsigned int longPressTime = 600; // in milliseconds

    const unsigned short RepeatDelay(600);   // in milliseconds
    // Repeat rate is specified (at least as of this writing) to be slightly different for
    // horizontal and vertical operations (lower) but we can't have key-specific repeat
    // rates if we want to keep using X's autorepeat.  So we use the repeat rate of
    // horizontal operation keys for all keys for now.
    const unsigned short RepeatInterval(100); // in milliseconds

    //! Character set for MTextEdit number content type
    const QString NumberContentCharacterSetRegexp(QString("[-+0-9%1-%2,.%3]")
                                                  .arg(QChar(0x0660)) // Arabic numbers begin
                                                  .arg(QChar(0x0669)) // Arabic numbers end
                                                  .arg(QChar(0x066b))); // Arabic decimal separator

    const int KeyCodeShift = 3;
    const int KeyCodeMask = 7;

    inline void setKeyBit(XkbDescPtr description, KeyCode keyCode)
    {
        description->ctrls->per_key_repeat[keyCode >> KeyCodeShift] |=
            1 << (keyCode & KeyCodeMask);
    }

    inline void clearKeyBit(XkbDescPtr description, KeyCode keyCode)
    {
        description->ctrls->per_key_repeat[keyCode >> KeyCodeShift] &=
            ~(1 << (keyCode & KeyCodeMask));
    }
};

MHardwareKeyboard::MHardwareKeyboard(MInputContextConnection& icConnection, QObject *parent)
    : QObject(parent),
      currentKeyboardType(M::FreeTextContentType),
      autoCaps(false),
      inputContextConnection(icConnection),
      lastEventType(QEvent::KeyRelease),
      currentLatchedMods(0),
      currentLockedMods(0),
      characterLoopIndex(-1),
      stateTransitionsDisabled(false),
      shiftsPressed(0),
      shiftShiftCapsLock(false),
      longPressTimer(this),
      imMode(M::InputMethodModeNormal),
      fnPressed(false),
      numberContentCharacterMatcher(NumberContentCharacterSetRegexp)
{
    longPressTimer.setSingleShot(true);
    longPressTimer.setInterval(longPressTime);
    connect(&longPressTimer, SIGNAL(timeout()), this, SLOT(handleLongPressTimeout()));
    connect(&deadKeyMapper, SIGNAL(stateChanged(const QChar &)),
            this, SIGNAL(deadKeyStateChanged(const QChar &)));
}


MHardwareKeyboard::~MHardwareKeyboard()
{
}


void MHardwareKeyboard::setKeyboardType(M::TextContentType type)
{
    qDebug() << __PRETTY_FUNCTION__ << ":" << type;
    if (currentKeyboardType == type)
        return;
    currentKeyboardType = type;

    // To support dynamic keyboard type changes we need to set correct state
    // for modifiers according current keyboard type.
    latchModifiers(LockMask | FnModifierMask, 0);
    switch (currentKeyboardType) {
    case M::NumberContentType:
    case M::PhoneNumberContentType:
        // With number and phone number content type Fn must be permanently locked
        lockModifiers(FnModifierMask, FnModifierMask);
        stateTransitionsDisabled = true;
        break;
    default:
        stateTransitionsDisabled = false;
        // clear locked modifiers for other keyboard types.
        lockModifiers(LockMask | FnModifierMask, 0);
        break;
    }
}

M::TextContentType MHardwareKeyboard::keyboardType() const
{
    return currentKeyboardType;
}


void MHardwareKeyboard::toggleCustomAutoRepeat(const bool enable)
{
    XkbDescPtr description(XkbAllocKeyboard());
    if (!description) {
        qWarning() << "Unable to allocate Xkb keyboard description for autorepeat setup.";
        return;
    }

    const unsigned int neededControls(XkbRepeatKeysMask | XkbPerKeyRepeatMask);
    if (Success != XkbGetControls(QX11Info::display(), neededControls, description)) {
        qWarning() << "Failure to query Xkb server controls for autorepeat setup.";
        XkbFreeKeyboard(description, 0, True);
        return;
    }

    if (!description->ctrls) {
        qWarning() << "Unable to query Xkb server controls for autorepeat setup.";
        XkbFreeKeyboard(description, 0, True);
        return;
    }

    if (False == XkbChangeEnabledControls(QX11Info::display(), XkbUseCoreKbd,
                                          XkbRepeatKeysMask, XkbRepeatKeysMask)) {
        qWarning() << "Unable to enable controls for autorepeat.";
        XkbFreeKeyboard(description, 0, True);
        return;
    }

    description->ctrls->repeat_delay = RepeatDelay;
    description->ctrls->repeat_interval = RepeatInterval;

    std::fill(description->ctrls->per_key_repeat,
              description->ctrls->per_key_repeat + XkbPerKeyBitArraySize, enable ? 0 : 0xff);

    if (enable) {
        static const KeySym repeatableKeys[] = { XK_BackSpace, XK_Left, XK_Up, XK_Right, XK_Down };

        for (unsigned int i = 0; i < ELEMENTS(repeatableKeys); ++i) {
            const KeyCode repeatableKeyCode(XKeysymToKeycode(QX11Info::display(), repeatableKeys[i]));
            if (repeatableKeyCode) {
                setKeyBit(description, repeatableKeyCode);
            } else {
                qWarning() << "Unable to make keysym" << repeatableKeys[i] << "repeatable: no keycode found.";
            }
        }
    } else {
        static const KeySym nonRepeatableKeys[] = {
            XK_ISO_Level3_Shift, // Fn
            XK_Multi_key, // Sym
            XK_Shift_L,
            XK_Shift_R,
            XK_Control_R<commit_msg>Changes: hwkb: don't disable autorepeat for right shift<commit_after>/* * This file is part of meego-keyboard *
 *
 * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
 * All rights reserved.
 * Contact: Nokia Corporation (<EMAIL>)
 *
 * If you have questions regarding the use of this file, please contact
 * Nokia at <EMAIL>.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1 as published by the Free Software Foundation
 * and appearing in the file LICENSE.LGPL included in the packaging
 * of this file.
 */

#include <QDebug>
#include <QX11Info>
#include <QTextCodec>
#include <algorithm>

#include <minputcontextconnection.h>

#include "mhardwarekeyboard.h"
#include "layoutsmanager.h"

#include <X11/X.h>
#undef KeyPress
#undef KeyRelease
#include <X11/XKBlib.h>
#define XK_MISCELLANY
#define XK_XKB_KEYS
#include <X11/keysymdef.h>

#define ELEMENTS(array) (sizeof(array)/sizeof((array)[0]))

namespace
{
    const Qt::KeyboardModifier FnLevelModifier = Qt::GroupSwitchModifier;
    const Qt::Key FnLevelKey = Qt::Key_AltGr;
    const Qt::Key SymKey = Qt::Key_Multi_key;
    const unsigned int SymModifierMask = Mod4Mask;
    const unsigned int FnModifierMask = Mod5Mask;
    const unsigned int longPressTime = 600; // in milliseconds

    const unsigned short RepeatDelay(600);   // in milliseconds
    // Repeat rate is specified (at least as of this writing) to be slightly different for
    // horizontal and vertical operations (lower) but we can't have key-specific repeat
    // rates if we want to keep using X's autorepeat.  So we use the repeat rate of
    // horizontal operation keys for all keys for now.
    const unsigned short RepeatInterval(100); // in milliseconds

    //! Character set for MTextEdit number content type
    const QString NumberContentCharacterSetRegexp(QString("[-+0-9%1-%2,.%3]")
                                                  .arg(QChar(0x0660)) // Arabic numbers begin
                                                  .arg(QChar(0x0669)) // Arabic numbers end
                                                  .arg(QChar(0x066b))); // Arabic decimal separator

    const int KeyCodeShift = 3;
    const int KeyCodeMask = 7;

    inline void setKeyBit(XkbDescPtr description, KeyCode keyCode)
    {
        description->ctrls->per_key_repeat[keyCode >> KeyCodeShift] |=
            1 << (keyCode & KeyCodeMask);
    }

    inline void clearKeyBit(XkbDescPtr description, KeyCode keyCode)
    {
        description->ctrls->per_key_repeat[keyCode >> KeyCodeShift] &=
            ~(1 << (keyCode & KeyCodeMask));
    }
};

MHardwareKeyboard::MHardwareKeyboard(MInputContextConnection& icConnection, QObject *parent)
    : QObject(parent),
      currentKeyboardType(M::FreeTextContentType),
      autoCaps(false),
      inputContextConnection(icConnection),
      lastEventType(QEvent::KeyRelease),
      currentLatchedMods(0),
      currentLockedMods(0),
      characterLoopIndex(-1),
      stateTransitionsDisabled(false),
      shiftsPressed(0),
      shiftShiftCapsLock(false),
      longPressTimer(this),
      imMode(M::InputMethodModeNormal),
      fnPressed(false),
      numberContentCharacterMatcher(NumberContentCharacterSetRegexp)
{
    longPressTimer.setSingleShot(true);
    longPressTimer.setInterval(longPressTime);
    connect(&longPressTimer, SIGNAL(timeout()), this, SLOT(handleLongPressTimeout()));
    connect(&deadKeyMapper, SIGNAL(stateChanged(const QChar &)),
            this, SIGNAL(deadKeyStateChanged(const QChar &)));
}


MHardwareKeyboard::~MHardwareKeyboard()
{
}


void MHardwareKeyboard::setKeyboardType(M::TextContentType type)
{
    qDebug() << __PRETTY_FUNCTION__ << ":" << type;
    if (currentKeyboardType == type)
        return;
    currentKeyboardType = type;

    // To support dynamic keyboard type changes we need to set correct state
    // for modifiers according current keyboard type.
    latchModifiers(LockMask | FnModifierMask, 0);
    switch (currentKeyboardType) {
    case M::NumberContentType:
    case M::PhoneNumberContentType:
        // With number and phone number content type Fn must be permanently locked
        lockModifiers(FnModifierMask, FnModifierMask);
        stateTransitionsDisabled = true;
        break;
    default:
        stateTransitionsDisabled = false;
        // clear locked modifiers for other keyboard types.
        lockModifiers(LockMask | FnModifierMask, 0);
        break;
    }
}

M::TextContentType MHardwareKeyboard::keyboardType() const
{
    return currentKeyboardType;
}


void MHardwareKeyboard::toggleCustomAutoRepeat(const bool enable)
{
    XkbDescPtr description(XkbAllocKeyboard());
    if (!description) {
        qWarning() << "Unable to allocate Xkb keyboard description for autorepeat setup.";
        return;
    }

    const unsigned int neededControls(XkbRepeatKeysMask | XkbPerKeyRepeatMask);
    if (Success != XkbGetControls(QX11Info::display(), neededControls, description)) {
        qWarning() << "Failure to query Xkb server controls for autorepeat setup.";
        XkbFreeKeyboard(description, 0, True);
        return;
    }

    if (!description->ctrls) {
        qWarning() << "Unable to query Xkb server controls for autorepeat setup.";
        XkbFreeKeyboard(description, 0, True);
        return;
    }

    if (False == XkbChangeEnabledControls(QX11Info::display(), XkbUseCoreKbd,
                                          XkbRepeatKeysMask, XkbRepeatKeysMask)) {
        qWarning() << "Unable to enable controls for autorepeat.";
        XkbFreeKeyboard(description, 0, True);
        return;
    }

    description->ctrls->repeat_delay = RepeatDelay;
    description->ctrls->repeat_interval = RepeatInterval;

    std::fill(description->ctrls->per_key_repeat,
              description->ctrls->per_key_repeat + XkbPerKeyBitArraySize, enable ? 0 : 0xff);

    if (enable) {
        static const KeySym repeatableKeys[] = { XK_BackSpace, XK_Left, XK_Up, XK_Right, XK_Down };

        for (unsigned int i = 0; i < ELEMENTS(repeatableKeys); ++i) {
            const KeyCode repeatableKeyCode(XKeysymToKeycode(QX11Info::display(), repeatableKeys[i]));
            if (repeatableKeyCode) {
                setKeyBit(description, repeatableKeyCode);
            } else {
                qWarning() << "Unable to make keysym" << repeatableKeys[i] << "repeatable: no keycode found.";
            }
        }
    } else {
        static const KeySym nonRepeatableKeys[] = {
            XK_ISO_Level3_Shift, // Fn
            XK_Multi_key, // Sym
            XK_Shift_L,
            XK_Control_R
{'fraction_non_alphanumeric': 0.09735973597359736, 'fraction_numerical': 0.0102010201020102, 'mean_word_length': 3.0736327528261533, 'pattern_counts': {'":': 2, '<': 73, '<?xml version=': 0, '>': 57, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 16, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '236769', 'n_tokens_mistral': 3876, 'n_tokens_neox': 3773, 'n_words': 1003}