이 글은 KDAB에 소개된 글과 예제소스코드를 기반으로 작성되었다.
가상키보드는 실제 물리 키보드가 없는 환경에서 입력장치로 유용하게 사용될 수 있다.
Qt는 이미 훌륭한 Virtual keyboard를 제공하고 있지만 이 글에서는 dbus, QPA기반에서 키입력 인터페이스를 사용하는 방법과 가상키보드 구현 기본 개념을 살펴본다.
가상키보드를 구현하는 방법에는 크게 두가지 방법이 있다. in-process로 구현하거나 또는 하나의 독립 프로세스로(out-of-process) 동작하도록 구현하는 것이다.
in-process로 구현하는 접근 방식의 장점은 키보드와 응용프로그램간 즉, 프로세스 간 통신이 필요 없기 때문에 구현이 매우 쉽다는 것이다. 단점은 모든 응용프로그램이 가상키보드의 인스턴스를 가지고 있어야 한다는 것이다. 이 문제는 모든 인스턴스 (가시성, 활동 등)의 상태를 응용프로그램간 동기화를 어렵게 한다는 것을 의미한다.
반면 out-of-process 방식은 다른 모든 응용프로그램간에 가상 키보드의 단일 인스턴스(singleton)를 공유 할 수 있으므로 여러 가상키보드 인스턴스간에 동기화 문제가 발생하지 않는다는 것이다. 다만 이를 위해서는 전역 가상키보드 인스턴스를 사용하려는 모든 응용 프로그램 사이의 프로세스 간 통신 메커니즘이 필요하다.
이 글에서는 기존 리눅스 Input Method 시스템인 IBus에서 이미 작동하는 것으로 입증되었기 때문에 아웃 프로세스 (out-of-process) 방식을 사용하고 dbus를 IPC메커니즘으로 사용한다.
dbus에 관해서 더 자세한 내용은 다음 글을 참고하길 바란다. https://makersweb.net/linux/2027
#include <QApplication>
#include <QDBusConnection>
#include "keyboard.h"
int main(int argc, char **argv)
{
QApplication app(argc, argv);
if (!QDBusConnection::sessionBus().registerService("com.kdab.inputmethod")) {
qFatal("Unable to register at DBus");
return 1;
}
Keyboard keyboard;
if (!QDBusConnection::sessionBus().registerObject("/VirtualKeyboard", &keyboard, QDBusConnection::ExportAllSignals | QDBusConnection::ExportAllSlots)) {
qFatal("Unable to register object at DBus");
return 1;
}
return app.exec();
}
#ifndef KEYBOARD_H
#define KEYBOARD_H
#include <QWidget>
class Keyboard : public QWidget
{
Q_OBJECT
public:
explicit Keyboard(QWidget *parent = Q_NULLPTR);
public slots:
void showKeyboard(int globalX, int globalY);
void hideKeyboard();
bool keyboardVisible() const;
signals:
void specialKeyClicked(int key);
void keyClicked(const QString &text);
private slots:
void buttonClicked(int key);
};
#endif
Keyboard::Keyboard(QWidget *parent)
: QWidget(parent)
{
setWindowFlags(Qt::WindowDoesNotAcceptFocus | Qt::Tool | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::BypassWindowManagerHint);
QGridLayout *gridLayout = new QGridLayout(this);
QSignalMapper *mapper = new QSignalMapper(this);
connect(mapper, SIGNAL(mapped(int)), SLOT(buttonClicked(int)));
int row = 0;
int column = 0;
for (int i = 0; i < layoutSize; ++i) {
if (keyboardLayout[i].key == NEXT_ROW_MARKER) {
row++;
column = 0;
continue;
}
QPushButton *button = new QPushButton;
button->setFixedWidth(40);
button->setText(QString::fromLatin1(keyboardLayout[i].label));
mapper->setMapping(button, keyboardLayout[i].key);
connect(button, SIGNAL(clicked()), mapper, SLOT(map()));
gridLayout->addWidget(button, row, column);
column++;
}
}
void Keyboard::buttonClicked(int key)
{
if ((key == Qt::Key_Enter) || (key == Qt::Key_Backspace))
emit specialKeyClicked(key);
else
emit keyClicked(keyToCharacter(key));
}
static QString keyToCharacter(int key)
{
for (int i = 0; i < layoutSize; ++i) {
if (keyboardLayout[i].key == key)
return QString::fromLatin1(keyboardLayout[i].label);
}
return QString();
}
class QVkImPlatformInputContextPlugin : public QPlatformInputContextPlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID QPlatformInputContextFactoryInterface_iid FILE "vkim.json")
public:
QVkImPlatformInputContext *create(const QString&, const QStringList&) Q_DECL_OVERRIDE;
};
QVkImPlatformInputContext *QVkImPlatformInputContextPlugin::create(const QString& system, const QStringList& paramList)
{
Q_UNUSED(paramList);
if (system == QLatin1String("vkim")) {
return new QVkImPlatformInputContext;
}
return 0;
}
{
"Keys": [ "vkim" ]
}
QVkImPlatformInputContext::QVkImPlatformInputContext()
: m_focusObject(0)
{
m_keyboardInterface = new QDBusInterface("com.kdab.inputmethod", "/VirtualKeyboard", "local.server.Keyboard", QDBusConnection::sessionBus(), this);
connect(m_keyboardInterface, SIGNAL(keyClicked(QString)), SLOT(keyboardKeyClicked(QString)));
connect(m_keyboardInterface, SIGNAL(specialKeyClicked(int)), SLOT(keyboardSpecialKeyClicked(int)));
}
void QVkImPlatformInputContext::keyboardKeyClicked(const QString &characters)
{
if (!m_focusObject)
return;
QInputMethodEvent event;
event.setCommitString(characters);
QGuiApplication::sendEvent(m_focusObject, &event);
}
이제 입력 필드를 가진 간단한 응용프로그램을 만든다.
응용프로그램을 실행하고 입력 필드를 클릭하면 키보드가 나타나고 키를 클릭하면 글자가 입력된다.
아래는 예제소스코드 저장소이다.
https://github.com/KDAB/virtual-keyboard-demo