init
Some checks failed
Docker. / Ubuntu (push) Has been cancelled
User-agent updater. / User-agent (push) Failing after 15s
Lock Threads / lock (push) Failing after 10s
Waiting for answer. / waiting-for-answer (push) Failing after 22s
Needs user action. / needs-user-action (push) Failing after 8s
Can't reproduce. / cant-reproduce (push) Failing after 8s
Close stale issues and PRs / stale (push) Has been cancelled

This commit is contained in:
allhaileris
2026-02-16 15:50:16 +03:00
commit afb81b8278
13816 changed files with 3689732 additions and 0 deletions

View File

@@ -0,0 +1,57 @@
set(FCITX5_QT_EXTRA_PLUGIN_NAME "")
if (WITH_FCITX_PLUGIN_NAME)
set(FCITX5_QT_EXTRA_PLUGIN_NAME "\"fcitx\",")
endif()
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/fcitx5.json.in" "${CMAKE_CURRENT_BINARY_DIR}/fcitx5.json")
set(plugin_SRCS
fcitx4watcher.cpp
fcitx4inputcontextproxy.cpp
fcitx4inputcontextproxyimpl.cpp
fcitx4inputmethodproxy.cpp
hybridinputcontext.cpp
qfcitxplatforminputcontext.cpp
fcitxcandidatewindow.cpp
fcitxtheme.cpp
font.cpp
qtkey.cpp
main.cpp
)
if (BUILD_ONLY_PLUGIN AND BUILD_STATIC_PLUGIN)
set(plugin_SRCS ${plugin_SRCS} $<TARGET_OBJECTS:Fcitx5Qt5DBusAddons>)
endif()
add_library(fcitx5platforminputcontextplugin ${PLUGIN_LIBRARY_TYPE} ${plugin_SRCS})
set_target_properties(fcitx5platforminputcontextplugin PROPERTIES
AUTOMOC TRUE
)
target_include_directories(fcitx5platforminputcontextplugin PRIVATE ${Qt5Gui_PRIVATE_INCLUDE_DIRS} "${PROJECT_SOURCE_DIR}/common")
if (BUILD_ONLY_PLUGIN AND BUILD_STATIC_PLUGIN)
target_compile_definitions(fcitx5platforminputcontextplugin PRIVATE "-DQT_STATICPLUGIN")
endif()
target_compile_definitions(fcitx5platforminputcontextplugin PRIVATE "-DFCITX_PLUGIN_DATA_FILE_PATH=\"${CMAKE_CURRENT_BINARY_DIR}/fcitx5.json\"")
if (WITH_FCITX_PLUGIN_NAME)
# This is not really necessary, but can trigger a cmake rebuild.
target_compile_definitions(fcitx5platforminputcontextplugin PRIVATE "-DFCITX5_QT_WITH_FCITX_NAME")
endif()
target_link_libraries(fcitx5platforminputcontextplugin
Qt5::Core
Qt5::Gui
Qt5::DBus
Qt5::Widgets
Fcitx5Qt5::DBusAddons
XKBCommon::XKBCommon
)
if (ENABLE_X11)
target_link_libraries(fcitx5platforminputcontextplugin XCB::XCB)
target_compile_definitions(fcitx5platforminputcontextplugin PRIVATE "-DENABLE_X11")
endif()
include(ECMQueryQmake)
query_qmake(_QT5PLUGINDIR QT_INSTALL_PLUGINS)
set(CMAKE_INSTALL_QT5PLUGINDIR ${_QT5PLUGINDIR} CACHE PATH "Qt5 plugin dir")
install(TARGETS fcitx5platforminputcontextplugin DESTINATION ${CMAKE_INSTALL_QT5PLUGINDIR}/platforminputcontexts)

View File

@@ -0,0 +1,86 @@
/*
* SPDX-FileCopyrightText: 2012~2023 CSSlayer <wengxt@gmail.com>
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#include "fcitx4inputcontextproxy_p.h"
#include <QCoreApplication>
#include <QDBusConnectionInterface>
#include <QDBusInterface>
#include <QDBusMetaType>
#include <QFileInfo>
#include <QTimer>
namespace fcitx {
Fcitx4InputContextProxy::Fcitx4InputContextProxy(Fcitx4Watcher *watcher,
QObject *parent)
: QObject(parent),
d_ptr(new Fcitx4InputContextProxyPrivate(watcher, this)) {}
Fcitx4InputContextProxy::~Fcitx4InputContextProxy() { delete d_ptr; }
void Fcitx4InputContextProxy::setDisplay(const QString &display) {
Q_D(Fcitx4InputContextProxy);
d->display_ = display;
}
const QString &Fcitx4InputContextProxy::display() const {
Q_D(const Fcitx4InputContextProxy);
return d->display_;
}
bool Fcitx4InputContextProxy::isValid() const {
Q_D(const Fcitx4InputContextProxy);
return d->isValid();
}
QDBusPendingReply<> Fcitx4InputContextProxy::focusIn() {
Q_D(Fcitx4InputContextProxy);
return d->icproxy_->FocusIn();
}
QDBusPendingReply<> Fcitx4InputContextProxy::focusOut() {
Q_D(Fcitx4InputContextProxy);
return d->icproxy_->FocusOut();
}
QDBusPendingReply<int> Fcitx4InputContextProxy::processKeyEvent(
unsigned int keyval, unsigned int keycode, unsigned int state, int type,
unsigned int time) {
Q_D(Fcitx4InputContextProxy);
return d->icproxy_->ProcessKeyEvent(keyval, keycode, state, type, time);
}
QDBusPendingReply<> Fcitx4InputContextProxy::reset() {
Q_D(Fcitx4InputContextProxy);
return d->icproxy_->Reset();
}
QDBusPendingReply<> Fcitx4InputContextProxy::setCapability(unsigned int caps) {
Q_D(Fcitx4InputContextProxy);
return d->icproxy_->SetCapacity(caps);
}
QDBusPendingReply<> Fcitx4InputContextProxy::setCursorRect(int x, int y, int w,
int h) {
Q_D(Fcitx4InputContextProxy);
return d->icproxy_->SetCursorRect(x, y, w, h);
}
QDBusPendingReply<> Fcitx4InputContextProxy::setSurroundingText(
const QString &text, unsigned int cursor, unsigned int anchor) {
Q_D(Fcitx4InputContextProxy);
return d->icproxy_->SetSurroundingText(text, cursor, anchor);
}
QDBusPendingReply<>
Fcitx4InputContextProxy::setSurroundingTextPosition(unsigned int cursor,
unsigned int anchor) {
Q_D(Fcitx4InputContextProxy);
return d->icproxy_->SetSurroundingTextPosition(cursor, anchor);
}
} // namespace fcitx

View File

@@ -0,0 +1,67 @@
/*
* SPDX-FileCopyrightText: 2012~2023 CSSlayer <wengxt@gmail.com>
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#ifndef _FCITX4INPUTCONTEXTPROXY_H_
#define _FCITX4INPUTCONTEXTPROXY_H_
#include "fcitxqtdbustypes.h"
#include <QDBusConnection>
#include <QDBusPendingReply>
#include <QDBusServiceWatcher>
#include <QObject>
class QDBusPendingCallWatcher;
namespace fcitx {
class Fcitx4Watcher;
class Fcitx4InputContextProxyPrivate;
class Fcitx4InputContextProxy : public QObject {
Q_OBJECT
public:
Fcitx4InputContextProxy(Fcitx4Watcher *watcher, QObject *parent);
~Fcitx4InputContextProxy();
bool isValid() const;
void setDisplay(const QString &display);
const QString &display() const;
public Q_SLOTS:
QDBusPendingReply<> focusIn();
QDBusPendingReply<> focusOut();
QDBusPendingReply<int> processKeyEvent(unsigned int keyval,
unsigned int keycode,
unsigned int state, int type,
unsigned int time);
QDBusPendingReply<> reset();
QDBusPendingReply<> setCapability(unsigned int caps);
QDBusPendingReply<> setCursorRect(int x, int y, int w, int h);
QDBusPendingReply<> setSurroundingText(const QString &text,
unsigned int cursor,
unsigned int anchor);
QDBusPendingReply<> setSurroundingTextPosition(unsigned int cursor,
unsigned int anchor);
Q_SIGNALS:
void commitString(const QString &str);
void currentIM(const QString &name, const QString &uniqueName,
const QString &langCode);
void deleteSurroundingText(int offset, unsigned int nchar);
void forwardKey(unsigned int keyval, unsigned int state, bool isRelease);
void updateFormattedPreedit(const FcitxQtFormattedPreeditList &str,
int cursorpos);
void inputContextCreated();
private:
Fcitx4InputContextProxyPrivate *const d_ptr;
Q_DECLARE_PRIVATE(Fcitx4InputContextProxy);
};
} // namespace fcitx
#endif // _FCITX4INPUTCONTEXTPROXY_H_

View File

@@ -0,0 +1,152 @@
/*
* SPDX-FileCopyrightText: 2012~2023 CSSlayer <wengxt@gmail.com>
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#ifndef _FCITX4INPUTCONTEXTPROXY_P_H_
#define _FCITX4INPUTCONTEXTPROXY_P_H_
#include "fcitx4inputcontextproxy.h"
#include "fcitx4inputcontextproxyimpl.h"
#include "fcitx4inputmethodproxy.h"
#include "fcitx4watcher.h"
#include <QDBusServiceWatcher>
#include <cstddef>
#include <unistd.h>
namespace fcitx {
class Fcitx4InputContextProxyPrivate {
public:
Fcitx4InputContextProxyPrivate(Fcitx4Watcher *watcher,
Fcitx4InputContextProxy *q)
: q_ptr(q), fcitxWatcher_(watcher), watcher_(q) {
registerFcitxQtDBusTypes();
QObject::connect(fcitxWatcher_, &Fcitx4Watcher::availabilityChanged, q,
[this]() { availabilityChanged(); });
watcher_.setWatchMode(QDBusServiceWatcher::WatchForUnregistration);
QObject::connect(&watcher_, &QDBusServiceWatcher::serviceUnregistered,
q, [this]() {
cleanUp();
availabilityChanged();
});
availabilityChanged();
}
~Fcitx4InputContextProxyPrivate() {
if (isValid()) {
icproxy_->DestroyIC();
}
cleanUp();
}
bool isValid() const { return (icproxy_ && icproxy_->isValid()); }
void availabilityChanged() {
QTimer::singleShot(100, q_ptr, [this]() { recheck(); });
}
void recheck() {
if (!isValid() && fcitxWatcher_->availability()) {
createInputContext();
}
if (!fcitxWatcher_->availability()) {
cleanUp();
}
}
void cleanUp() {
auto services = watcher_.watchedServices();
for (const auto &service : services) {
watcher_.removeWatchedService(service);
}
delete improxy_;
improxy_ = nullptr;
delete icproxy_;
icproxy_ = nullptr;
delete createInputContextWatcher_;
createInputContextWatcher_ = nullptr;
}
void createInputContext() {
Q_Q(Fcitx4InputContextProxy);
if (!fcitxWatcher_->availability()) {
return;
}
cleanUp();
auto service = fcitxWatcher_->service();
auto connection = fcitxWatcher_->connection();
auto owner = connection.interface()->serviceOwner(service);
if (!owner.isValid()) {
return;
}
watcher_.setConnection(connection);
watcher_.setWatchedServices(QStringList() << owner);
// Avoid race, query again.
if (!connection.interface()->isServiceRegistered(owner)) {
cleanUp();
return;
}
QFileInfo info(QCoreApplication::applicationFilePath());
improxy_ =
new Fcitx4InputMethodProxy(owner, "/inputmethod", connection, q);
auto result = improxy_->CreateICv3(info.fileName(), getpid());
createInputContextWatcher_ = new QDBusPendingCallWatcher(result);
QObject::connect(createInputContextWatcher_,
&QDBusPendingCallWatcher::finished, q,
[this]() { createInputContextFinished(); });
}
void createInputContextFinished() {
Q_Q(Fcitx4InputContextProxy);
if (createInputContextWatcher_->isError()) {
cleanUp();
return;
}
QDBusPendingReply<int, bool, unsigned int, unsigned int, unsigned int,
unsigned int>
reply(*createInputContextWatcher_);
QString path = QString("/inputcontext_%1").arg(reply.value());
icproxy_ = new Fcitx4InputContextProxyImpl(improxy_->service(), path,
improxy_->connection(), q);
QObject::connect(icproxy_, &Fcitx4InputContextProxyImpl::CommitString,
q, &Fcitx4InputContextProxy::commitString);
QObject::connect(icproxy_, &Fcitx4InputContextProxyImpl::CurrentIM, q,
&Fcitx4InputContextProxy::currentIM);
QObject::connect(icproxy_,
&Fcitx4InputContextProxyImpl::DeleteSurroundingText, q,
&Fcitx4InputContextProxy::deleteSurroundingText);
QObject::connect(icproxy_, &Fcitx4InputContextProxyImpl::ForwardKey, q,
&Fcitx4InputContextProxy::forwardKey);
QObject::connect(icproxy_,
&Fcitx4InputContextProxyImpl::UpdateFormattedPreedit,
q, &Fcitx4InputContextProxy::updateFormattedPreedit);
delete createInputContextWatcher_;
createInputContextWatcher_ = nullptr;
Q_EMIT q->inputContextCreated();
}
Fcitx4InputContextProxy *q_ptr;
Q_DECLARE_PUBLIC(Fcitx4InputContextProxy);
Fcitx4Watcher *fcitxWatcher_;
QDBusServiceWatcher watcher_;
Fcitx4InputMethodProxy *improxy_ = nullptr;
Fcitx4InputContextProxyImpl *icproxy_ = nullptr;
QDBusPendingCallWatcher *createInputContextWatcher_ = nullptr;
QString display_;
};
} // namespace fcitx
#endif // _FCITX4INPUTCONTEXTPROXY_P_H_

View File

@@ -0,0 +1,30 @@
/*
* This file was generated by qdbusxml2cpp version 0.8
* Command line was: qdbusxml2cpp -N -p fcitx4inputcontextproxyimpl -c
* Fcitx4InputContextProxy org.fcitx.Fcitx.InputContext.xml
* org.fcitx.Fcitx.InputContext
*
* qdbusxml2cpp is Copyright (C) 2022 The Qt Company Ltd.
*
* This is an auto-generated file.
* This file may have been hand-edited. Look for HAND-EDIT comments
* before re-generating it.
*/
#include "fcitx4inputcontextproxyimpl.h"
namespace fcitx {
/*
* Implementation of interface class Fcitx4InputContextProxy
*/
Fcitx4InputContextProxyImpl::Fcitx4InputContextProxyImpl(
const QString &service, const QString &path,
const QDBusConnection &connection, QObject *parent)
: QDBusAbstractInterface(service, path, staticInterfaceName(), connection,
parent) {}
Fcitx4InputContextProxyImpl::~Fcitx4InputContextProxyImpl() {}
} // namespace fcitx

View File

@@ -0,0 +1,126 @@
/*
* This file was generated by qdbusxml2cpp version 0.8
* Command line was: qdbusxml2cpp -N -p fcitx4inputcontextproxyimpl -c
* Fcitx4InputContextProxyImpl org.fcitx.Fcitx.InputContext.xml
* org.fcitx.Fcitx.InputContext
*
* qdbusxml2cpp is Copyright (C) 2022 The Qt Company Ltd.
*
* This is an auto-generated file.
* Do not edit! All changes made to it will be lost.
*/
#ifndef FCITX4INPUTCONTEXTPROXYIMPL_H
#define FCITX4INPUTCONTEXTPROXYIMPL_H
#include "fcitxqtdbustypes.h"
#include <QtCore/QByteArray>
#include <QtCore/QList>
#include <QtCore/QMap>
#include <QtCore/QObject>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QVariant>
#include <QtDBus/QtDBus>
namespace fcitx {
/*
* Proxy class for interface org.fcitx.Fcitx.InputContext
*/
class Fcitx4InputContextProxyImpl : public QDBusAbstractInterface {
Q_OBJECT
public:
static inline const char *staticInterfaceName() {
return "org.fcitx.Fcitx.InputContext";
}
public:
Fcitx4InputContextProxyImpl(const QString &service, const QString &path,
const QDBusConnection &connection,
QObject *parent = nullptr);
~Fcitx4InputContextProxyImpl();
public Q_SLOTS: // METHODS
inline QDBusPendingReply<> DestroyIC() {
QList<QVariant> argumentList;
return asyncCallWithArgumentList(QStringLiteral("DestroyIC"),
argumentList);
}
inline QDBusPendingReply<> FocusIn() {
QList<QVariant> argumentList;
return asyncCallWithArgumentList(QStringLiteral("FocusIn"),
argumentList);
}
inline QDBusPendingReply<> FocusOut() {
QList<QVariant> argumentList;
return asyncCallWithArgumentList(QStringLiteral("FocusOut"),
argumentList);
}
inline QDBusPendingReply<int> ProcessKeyEvent(unsigned int keyval,
unsigned int keycode,
unsigned int state, int type,
unsigned int time) {
QList<QVariant> argumentList;
argumentList << QVariant::fromValue(keyval)
<< QVariant::fromValue(keycode)
<< QVariant::fromValue(state) << QVariant::fromValue(type)
<< QVariant::fromValue(time);
return asyncCallWithArgumentList(QStringLiteral("ProcessKeyEvent"),
argumentList);
}
inline QDBusPendingReply<> Reset() {
QList<QVariant> argumentList;
return asyncCallWithArgumentList(QStringLiteral("Reset"), argumentList);
}
inline QDBusPendingReply<> SetCapacity(unsigned int caps) {
QList<QVariant> argumentList;
argumentList << QVariant::fromValue(caps);
return asyncCallWithArgumentList(QStringLiteral("SetCapacity"),
argumentList);
}
inline QDBusPendingReply<> SetCursorRect(int x, int y, int w, int h) {
QList<QVariant> argumentList;
argumentList << QVariant::fromValue(x) << QVariant::fromValue(y)
<< QVariant::fromValue(w) << QVariant::fromValue(h);
return asyncCallWithArgumentList(QStringLiteral("SetCursorRect"),
argumentList);
}
inline QDBusPendingReply<> SetSurroundingText(const QString &text,
unsigned int cursor,
unsigned int anchor) {
QList<QVariant> argumentList;
argumentList << QVariant::fromValue(text) << QVariant::fromValue(cursor)
<< QVariant::fromValue(anchor);
return asyncCallWithArgumentList(QStringLiteral("SetSurroundingText"),
argumentList);
}
inline QDBusPendingReply<> SetSurroundingTextPosition(unsigned int cursor,
unsigned int anchor) {
QList<QVariant> argumentList;
argumentList << QVariant::fromValue(cursor)
<< QVariant::fromValue(anchor);
return asyncCallWithArgumentList(
QStringLiteral("SetSurroundingTextPosition"), argumentList);
}
Q_SIGNALS: // SIGNALS
void CommitString(const QString &str);
void CurrentIM(const QString &name, const QString &uniqueName,
const QString &langCode);
void DeleteSurroundingText(int offset, unsigned int nchar);
void ForwardKey(unsigned int keyval, unsigned int state, int type);
void UpdateFormattedPreedit(FcitxQtFormattedPreeditList str, int cursorpos);
};
} // namespace fcitx
#endif

View File

@@ -0,0 +1,29 @@
/*
* This file was generated by qdbusxml2cpp version 0.8
* Command line was: qdbusxml2cpp -N -p fcitx4inputmethodproxy -c
* Fcitx4InputMethodProxy org.fcitx.Fcitx.InputMethod.xml
* org.fcitx.Fcitx.InputMethod
*
* qdbusxml2cpp is Copyright (C) 2022 The Qt Company Ltd.
*
* This is an auto-generated file.
* This file may have been hand-edited. Look for HAND-EDIT comments
* before re-generating it.
*/
#include "fcitx4inputmethodproxy.h"
namespace fcitx {
/*
* Implementation of interface class Fcitx4InputMethodProxy
*/
Fcitx4InputMethodProxy::Fcitx4InputMethodProxy(
const QString &service, const QString &path,
const QDBusConnection &connection, QObject *parent)
: QDBusAbstractInterface(service, path, staticInterfaceName(), connection,
parent) {}
Fcitx4InputMethodProxy::~Fcitx4InputMethodProxy() {}
} // namespace fcitx

View File

@@ -0,0 +1,80 @@
/*
* This file was generated by qdbusxml2cpp version 0.8
* Command line was: qdbusxml2cpp -N -p fcitx4inputmethodproxy -c
* Fcitx4InputMethodProxy org.fcitx.Fcitx.InputMethod.xml
* org.fcitx.Fcitx.InputMethod
*
* qdbusxml2cpp is Copyright (C) 2022 The Qt Company Ltd.
*
* This is an auto-generated file.
* Do not edit! All changes made to it will be lost.
*/
#ifndef FCITX4INPUTMETHODPROXY_H
#define FCITX4INPUTMETHODPROXY_H
#include <QtCore/QByteArray>
#include <QtCore/QList>
#include <QtCore/QMap>
#include <QtCore/QObject>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QVariant>
#include <QtDBus/QtDBus>
namespace fcitx {
/*
* Proxy class for interface org.fcitx.Fcitx.InputMethod
*/
class Fcitx4InputMethodProxy : public QDBusAbstractInterface {
Q_OBJECT
public:
static inline const char *staticInterfaceName() {
return "org.fcitx.Fcitx.InputMethod";
}
public:
Fcitx4InputMethodProxy(const QString &service, const QString &path,
const QDBusConnection &connection,
QObject *parent = nullptr);
~Fcitx4InputMethodProxy();
public Q_SLOTS: // METHODS
inline QDBusPendingReply<int, bool, unsigned int, unsigned int,
unsigned int, unsigned int>
CreateICv3(const QString &appname, int pid) {
QList<QVariant> argumentList;
argumentList << QVariant::fromValue(appname)
<< QVariant::fromValue(pid);
return asyncCallWithArgumentList(QStringLiteral("CreateICv3"),
argumentList);
}
inline QDBusReply<int> CreateICv3(const QString &appname, int pid,
bool &enable, unsigned int &keyval1,
unsigned int &state1,
unsigned int &keyval2,
unsigned int &state2) {
QList<QVariant> argumentList;
argumentList << QVariant::fromValue(appname)
<< QVariant::fromValue(pid);
QDBusMessage reply = callWithArgumentList(
QDBus::Block, QStringLiteral("CreateICv3"), argumentList);
if (reply.type() == QDBusMessage::ReplyMessage &&
reply.arguments().count() == 6) {
enable = qdbus_cast<bool>(reply.arguments().at(1));
keyval1 = qdbus_cast<unsigned int>(reply.arguments().at(2));
state1 = qdbus_cast<unsigned int>(reply.arguments().at(3));
keyval2 = qdbus_cast<unsigned int>(reply.arguments().at(4));
state2 = qdbus_cast<unsigned int>(reply.arguments().at(5));
}
return reply;
}
Q_SIGNALS: // SIGNALS
};
} // namespace fcitx
#endif

View File

@@ -0,0 +1,267 @@
/*
* SPDX-FileCopyrightText: 2011~2023 CSSlayer <wengxt@gmail.com>
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#include "fcitx4watcher.h"
#include <QDBusConnection>
#include <QDBusConnectionInterface>
#include <QDBusServiceWatcher>
#include <QDebug>
#include <QDir>
#include <QFileSystemWatcher>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
// utils function in fcitx-utils and fcitx-config
bool _pid_exists(pid_t pid) {
if (pid <= 0)
return 0;
return !(kill(pid, 0) && (errno == ESRCH));
}
int displayNumber() {
QByteArray display(qgetenv("DISPLAY"));
QByteArray displayNumber("0");
int pos = display.indexOf(':');
if (pos >= 0) {
++pos;
int pos2 = display.indexOf('.', pos);
if (pos2 > 0) {
displayNumber = display.mid(pos, pos2 - pos);
} else {
displayNumber = display.mid(pos);
}
}
bool ok;
int d = displayNumber.toInt(&ok);
if (ok) {
return d;
}
return 0;
}
QString socketFile() {
QString filename =
QString("%1-%2")
.arg(QString::fromLatin1(QDBusConnection::localMachineId()))
.arg(displayNumber());
QString home = QString::fromLocal8Bit(qgetenv("XDG_CONFIG_HOME"));
if (home.isEmpty()) {
home = QDir::homePath().append(QLatin1String("/.config"));
}
return QString("%1/fcitx/dbus/%2").arg(home).arg(filename);
}
namespace fcitx {
QString newUniqueConnectionName() {
static int idx = 0;
const auto newIdx = idx++;
return QString("_fcitx4_%1").arg(newIdx);
}
Fcitx4Watcher::Fcitx4Watcher(QDBusConnection sessionBus, QObject *parent)
: QObject(parent), connection_(nullptr), sessionBus_(sessionBus),
socketFile_(socketFile()),
serviceName_(QString("org.fcitx.Fcitx-%1").arg(displayNumber())),
availability_(false), uniqueConnectionName_(newUniqueConnectionName()) {}
Fcitx4Watcher::~Fcitx4Watcher() {
cleanUpConnection();
unwatchSocketFile();
}
bool Fcitx4Watcher::availability() const { return availability_; }
QDBusConnection Fcitx4Watcher::connection() const {
if (connection_) {
return *connection_;
}
return sessionBus_;
}
QString Fcitx4Watcher::service() const {
if (connection_) {
return serviceName_;
}
if (mainPresent_) {
return serviceName_;
}
return QString();
}
void Fcitx4Watcher::setAvailability(bool availability) {
if (availability_ != availability) {
availability_ = availability;
Q_EMIT availabilityChanged(availability_);
}
}
void Fcitx4Watcher::watch() {
if (watched_) {
return;
}
serviceWatcher_ = new QDBusServiceWatcher(this);
connect(serviceWatcher_, &QDBusServiceWatcher::serviceOwnerChanged, this,
&Fcitx4Watcher::imChanged);
serviceWatcher_->setConnection(sessionBus_);
serviceWatcher_->addWatchedService(serviceName_);
if (sessionBus_.interface()->isServiceRegistered(serviceName_)) {
mainPresent_ = true;
}
watchSocketFile();
createConnection();
updateAvailability();
watched_ = true;
}
void Fcitx4Watcher::unwatch() {
if (!watched_) {
return;
}
delete serviceWatcher_;
serviceWatcher_ = nullptr;
unwatchSocketFile();
cleanUpConnection();
mainPresent_ = false;
watched_ = false;
updateAvailability();
}
QString Fcitx4Watcher::address() {
QString addr;
QByteArray addrVar = qgetenv("FCITX_DBUS_ADDRESS");
if (!addrVar.isNull())
return QString::fromLocal8Bit(addrVar);
QFile file(socketFile_);
if (!file.open(QIODevice::ReadOnly))
return QString();
const int BUFSIZE = 1024;
char buffer[BUFSIZE];
size_t sz = file.read(buffer, BUFSIZE);
file.close();
if (sz == 0)
return QString();
char *p = buffer;
while (*p)
p++;
size_t addrlen = p - buffer;
if (sz != addrlen + 2 * sizeof(pid_t) + 1)
return QString();
/* skip '\0' */
p++;
pid_t *ppid = (pid_t *)p;
pid_t daemonpid = ppid[0];
pid_t fcitxpid = ppid[1];
if (!_pid_exists(daemonpid) || !_pid_exists(fcitxpid))
return QString();
addr = QLatin1String(buffer);
return addr;
}
void Fcitx4Watcher::cleanUpConnection() {
QDBusConnection::disconnectFromBus(uniqueConnectionName_);
delete connection_;
connection_ = nullptr;
}
void Fcitx4Watcher::socketFileChanged() {
cleanUpConnection();
createConnection();
}
void Fcitx4Watcher::createConnection() {
QString addr = address();
if (!addr.isNull()) {
QDBusConnection connection(
QDBusConnection::connectToBus(addr, uniqueConnectionName_));
if (connection.isConnected()) {
connection_ = new QDBusConnection(connection);
} else {
QDBusConnection::disconnectFromBus(uniqueConnectionName_);
}
}
if (connection_) {
connection_->connect("org.freedesktop.DBus.Local",
"/org/freedesktop/DBus/Local",
"org.freedesktop.DBus.Local", "Disconnected", this,
SLOT(dbusDisconnected()));
unwatchSocketFile();
}
updateAvailability();
}
void Fcitx4Watcher::dbusDisconnected() {
cleanUpConnection();
watchSocketFile();
// Try recreation immediately to avoid race.
createConnection();
}
void Fcitx4Watcher::watchSocketFile() {
if (socketFile_.isEmpty()) {
return;
}
QFileInfo info(socketFile_);
QDir dir(info.path());
if (!dir.exists()) {
QDir rt(QDir::root());
rt.mkpath(info.path());
}
fsWatcher_ = new QFileSystemWatcher(this);
fsWatcher_->addPath(info.path());
if (info.exists()) {
fsWatcher_->addPath(info.filePath());
}
connect(fsWatcher_, &QFileSystemWatcher::fileChanged, this,
&Fcitx4Watcher::socketFileChanged);
connect(fsWatcher_, &QFileSystemWatcher::directoryChanged, this,
&Fcitx4Watcher::socketFileChanged);
}
void Fcitx4Watcher::unwatchSocketFile() {
if (fsWatcher_) {
fsWatcher_->disconnect(this);
fsWatcher_->deleteLater();
fsWatcher_ = nullptr;
}
}
void Fcitx4Watcher::imChanged(const QString &service, const QString &,
const QString &newOwner) {
if (service == serviceName_) {
if (!newOwner.isEmpty()) {
mainPresent_ = true;
} else {
mainPresent_ = false;
}
}
updateAvailability();
}
void Fcitx4Watcher::updateAvailability() {
setAvailability(mainPresent_ || connection_);
}
} // namespace fcitx

View File

@@ -0,0 +1,68 @@
/*
* SPDX-FileCopyrightText: 2011~2023 CSSlayer <wengxt@gmail.com>
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#ifndef FCITXWATCHER_H_
#define FCITXWATCHER_H_
#include <QDBusConnection>
#include <QObject>
class QFileSystemWatcher;
class QDBusServiceWatcher;
namespace fcitx {
// A FcitxQtConnection replacement, to implement compatibility with fcitx 5.
// Since we have three thing to monitor, the situation becomes much more
// complexer.
class Fcitx4Watcher : public QObject {
Q_OBJECT
public:
explicit Fcitx4Watcher(QDBusConnection sessionBus,
QObject *parent = nullptr);
~Fcitx4Watcher();
void watch();
void unwatch();
bool availability() const;
QDBusConnection connection() const;
QString service() const;
Q_SIGNALS:
void availabilityChanged(bool);
private Q_SLOTS:
void dbusDisconnected();
void socketFileChanged();
void imChanged(const QString &service, const QString &oldOwner,
const QString &newOwner);
private:
QString address();
void watchSocketFile();
void unwatchSocketFile();
void createConnection();
void cleanUpConnection();
void setAvailability(bool availability);
void updateAvailability();
QFileSystemWatcher *fsWatcher_ = nullptr;
QDBusServiceWatcher *serviceWatcher_ = nullptr;
QDBusConnection *connection_;
QDBusConnection sessionBus_;
QString socketFile_;
QString serviceName_;
bool availability_ = false;
bool mainPresent_ = false;
bool watched_ = false;
QString uniqueConnectionName_;
};
} // namespace fcitx
#endif // FCITXWATCHER_H_

View File

@@ -0,0 +1,6 @@
{
"Keys": [
@FCITX5_QT_EXTRA_PLUGIN_NAME@
"fcitx5"
]
}

View File

@@ -0,0 +1,791 @@
/*
* SPDX-FileCopyrightText: 2021~2021 CSSlayer <wengxt@gmail.com>
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#include "fcitxcandidatewindow.h"
#include "fcitxflags.h"
#include "fcitxtheme.h"
#include "qfcitxplatforminputcontext.h"
#include <QDebug>
#include <QExposeEvent>
#include <QFont>
#include <QGuiApplication>
#include <QMouseEvent>
#include <QPalette>
#include <QResizeEvent>
#include <QScreen>
#include <QTextLayout>
#include <QVariant>
#include <QtMath>
#include <utility>
#if defined(FCITX_ENABLE_QT6_WAYLAND_WORKAROUND) && \
QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
#include <QtGui/private/qhighdpiscaling_p.h>
#include <QtWaylandClient/private/qwayland-xdg-shell.h>
#include <QtWaylandClient/private/qwaylanddisplay_p.h>
#include <QtWaylandClient/private/qwaylandintegration_p.h>
#include <QtWaylandClient/private/qwaylandwindow_p.h>
#include <QtWaylandClient/private/wayland-xdg-shell-client-protocol.h>
#include <qpa/qplatformnativeinterface.h>
#endif
namespace fcitx {
namespace {
#if defined(FCITX_ENABLE_QT6_WAYLAND_WORKAROUND) && \
QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
class XdgWmBase : public QtWayland::xdg_wm_base {
public:
using xdg_wm_base::xdg_wm_base;
protected:
// This is required for all xdg_wm_base bind.
void xdg_wm_base_ping(uint32_t serial) override { pong(serial); }
};
#endif
void doLayout(QTextLayout &layout) {
QFontMetrics fontMetrics(layout.font());
auto minH = fontMetrics.ascent() + fontMetrics.descent();
layout.setCacheEnabled(true);
layout.beginLayout();
int height = 0;
while (1) {
QTextLine line = layout.createLine();
if (!line.isValid())
break;
line.setLineWidth(INT_MAX);
line.setLeadingIncluded(true);
line.setPosition(
QPoint(0, height - line.ascent() + fontMetrics.ascent()));
height += minH;
}
layout.endLayout();
}
} // namespace
class MultilineText {
public:
MultilineText(const QFont &font, const QString &text) {
QStringList lines = text.split("\n");
int width = 0;
QFontMetrics fontMetrics(font);
fontHeight_ = fontMetrics.ascent() + fontMetrics.descent();
for (const auto &line : lines) {
layouts_.emplace_back(std::make_unique<QTextLayout>(line));
layouts_.back()->setFont(font);
doLayout(*layouts_.back());
width = std::max(width,
layouts_.back()->boundingRect().toRect().width());
}
boundingRect_.setTopLeft(QPoint(0, 0));
boundingRect_.setSize(QSize(width, lines.size() * fontHeight_));
}
bool isEmpty() const { return layouts_.empty(); }
void draw(QPainter *painter, QColor color, QPoint position) {
painter->save();
painter->setPen(color);
int currentY = 0;
for (const auto &layout : layouts_) {
layout->draw(painter, position + QPoint(0, currentY));
currentY += fontHeight_;
}
painter->restore();
}
QRect boundingRect() { return boundingRect_; }
private:
std::vector<std::unique_ptr<QTextLayout>> layouts_;
int fontHeight_;
QRect boundingRect_;
};
FcitxCandidateWindow::FcitxCandidateWindow(QWindow *window,
QFcitxPlatformInputContext *context)
: QRasterWindow(), context_(context), theme_(context->theme()),
parent_(window) {
constexpr Qt::WindowFlags commonFlags = Qt::FramelessWindowHint |
Qt::WindowDoesNotAcceptFocus |
Qt::NoDropShadowWindowHint;
if (isWayland_) {
// Qt::ToolTip ensures wayland doesn't grab focus.
// Not using Qt::BypassWindowManagerHint ensures wayland handle
// fractional scale.
setFlags(Qt::ToolTip | commonFlags);
#if defined(FCITX_ENABLE_QT6_WAYLAND_WORKAROUND) && \
QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
if (auto instance = QtWaylandClient::QWaylandIntegration::instance()) {
for (QtWaylandClient::QWaylandDisplay::RegistryGlobal global :
instance->display()->globals()) {
if (global.interface == QLatin1String("xdg_wm_base")) {
xdgWmBase_.reset(
new XdgWmBase(instance->display()->wl_registry(),
global.id, global.version));
break;
}
}
}
setProperty("_q_waylandPopupAnchor",
QVariant::fromValue(Qt::BottomEdge | Qt::LeftEdge));
setProperty("_q_waylandPopupGravity",
QVariant::fromValue(Qt::BottomEdge | Qt::RightEdge));
setProperty(
"_q_waylandPopupConstraintAdjustment",
static_cast<unsigned int>(
QtWayland::xdg_positioner::constraint_adjustment_slide_x |
QtWayland::xdg_positioner::constraint_adjustment_flip_y));
#endif
} else {
// Qt::Popup ensures X11 doesn't apply tooltip animation under kwin.
setFlags(Qt::Popup | Qt::BypassWindowManagerHint | commonFlags);
}
if (isWayland_) {
setTransientParent(parent_);
}
QSurfaceFormat surfaceFormat = format();
surfaceFormat.setAlphaBufferSize(8);
setFormat(surfaceFormat);
connect(this, &QWindow::visibleChanged, this, [this] { hoverIndex_ = -1; });
}
FcitxCandidateWindow::~FcitxCandidateWindow() {}
bool FcitxCandidateWindow::event(QEvent *event) {
if (event->type() == QEvent::Leave) {
auto oldHighlight = highlight();
hoverIndex_ = -1;
if (highlight() != oldHighlight) {
update();
}
}
return QRasterWindow::event(event);
}
void FcitxCandidateWindow::render(QPainter *painter) {
theme_->paint(painter, theme_->background(),
QRect(QPoint(0, 0), actualSize_));
auto contentMargin = theme_->contentMargin();
const QPoint topLeft(contentMargin.left(), contentMargin.top());
painter->setPen(theme_->normalColor());
auto minH =
theme_->fontMetrics().ascent() + theme_->fontMetrics().descent();
auto textMargin = theme_->textMargin();
auto extraW = textMargin.left() + textMargin.right();
auto extraH = textMargin.top() + textMargin.bottom();
size_t currentHeight = 0;
if (!upperLayout_.text().isEmpty()) {
upperLayout_.draw(
painter, topLeft + QPoint(textMargin.left(), textMargin.top()));
// Draw cursor
currentHeight += minH + extraH;
if (cursor_ >= 0) {
auto line = upperLayout_.lineForTextPosition(cursor_);
if (line.isValid()) {
int cursorX = line.cursorToX(cursor_);
line.lineNumber();
painter->save();
QPen pen = painter->pen();
pen.setWidth(2);
painter->setPen(pen);
QPoint start = topLeft + QPoint(textMargin.left() + cursorX + 1,
textMargin.top() +
line.lineNumber() * minH);
painter->drawLine(start, start + QPoint(0, minH));
painter->restore();
}
}
}
if (!lowerLayout_.text().isEmpty()) {
lowerLayout_.draw(painter,
topLeft + QPoint(textMargin.left(),
textMargin.top() + currentHeight));
currentHeight += minH + extraH;
}
bool vertical = theme_->vertical();
if (layoutHint_ == FcitxCandidateLayoutHint::Vertical) {
vertical = true;
} else if (layoutHint_ == FcitxCandidateLayoutHint::Horizontal) {
vertical = false;
}
candidateRegions_.clear();
candidateRegions_.reserve(labelLayouts_.size());
size_t wholeW = 0, wholeH = 0;
// size of text = textMargin + actual text size.
// HighLight = HighLight margin + TEXT.
// Click region = HighLight - click
for (size_t i = 0; i < labelLayouts_.size(); i++) {
int x, y;
if (vertical) {
x = 0;
y = currentHeight + wholeH;
} else {
x = wholeW;
y = currentHeight;
}
x += textMargin.left();
y += textMargin.top();
int labelW = 0, labelH = 0, candidateW = 0, candidateH = 0;
if (!labelLayouts_[i]->isEmpty()) {
auto size = labelLayouts_[i]->boundingRect();
labelW = size.width();
labelH = size.height();
}
if (!candidateLayouts_[i]->isEmpty()) {
auto size = candidateLayouts_[i]->boundingRect();
candidateW = size.width();
candidateH = size.height();
}
int vheight;
if (vertical) {
vheight = std::max({minH, labelH, candidateH});
wholeH += vheight + extraH;
} else {
vheight = candidatesHeight_ - extraH;
wholeW += candidateW + labelW + extraW;
}
QMargins highlightMargin = theme_->highlightMargin();
QMargins clickMargin = theme_->highlightClickMargin();
auto highlightWidth = labelW + candidateW;
bool fullWidthHighlight = true;
if (fullWidthHighlight && vertical) {
// Last candidate, fill.
highlightWidth = actualSize_.width() - contentMargin.left() -
contentMargin.right() - textMargin.left() -
textMargin.right();
}
const int highlightIndex = highlight();
QColor color = theme_->normalColor();
if (highlightIndex >= 0 && i == static_cast<size_t>(highlightIndex)) {
// Paint highlight
theme_->paint(
painter, theme_->highlight(),
QRect(topLeft + QPoint(x, y) -
QPoint(highlightMargin.left(), highlightMargin.top()),
QSize(highlightWidth + highlightMargin.left() +
highlightMargin.right(),
vheight + highlightMargin.top() +
highlightMargin.bottom())));
color = theme_->highlightCandidateColor();
}
QRect candidateRegion(
topLeft + QPoint(x, y) -
QPoint(highlightMargin.left(), highlightMargin.right()) +
QPoint(clickMargin.left(), clickMargin.right()),
QSize(highlightWidth + highlightMargin.left() +
highlightMargin.right() - clickMargin.left() -
clickMargin.right(),
vheight + highlightMargin.top() + highlightMargin.bottom() -
clickMargin.top() - clickMargin.bottom()));
candidateRegions_.push_back(candidateRegion);
if (!labelLayouts_[i]->isEmpty()) {
labelLayouts_[i]->draw(painter, color, topLeft + QPoint(x, y));
}
if (!candidateLayouts_[i]->isEmpty()) {
candidateLayouts_[i]->draw(painter, color,
topLeft + QPoint(x + labelW, y));
}
}
prevRegion_ = QRect();
nextRegion_ = QRect();
if (labelLayouts_.size() && (hasPrev_ || hasNext_)) {
if (theme_->prev().valid() && theme_->next().valid()) {
int prevY = 0, nextY = 0;
if (theme_->buttonAlignment() == "Top") {
prevY = contentMargin.top();
nextY = contentMargin.top();
} else if (theme_->buttonAlignment() == "First Candidate") {
prevY = candidateRegions_.front().top() +
(candidateRegions_.front().height() -
theme_->prev().height()) /
2.0;
nextY = candidateRegions_.front().top() +
(candidateRegions_.front().height() -
theme_->next().height()) /
2.0;
} else if (theme_->buttonAlignment() == "Center") {
prevY = contentMargin.top() +
(actualSize_.height() - contentMargin.top() -
contentMargin.bottom() - theme_->prev().height()) /
2.0;
nextY = contentMargin.top() +
(actualSize_.height() - contentMargin.top() -
contentMargin.bottom() - theme_->next().height()) /
2.0;
} else if (theme_->buttonAlignment() == "Last Candidate") {
prevY = candidateRegions_.back().top() +
(candidateRegions_.back().height() -
theme_->prev().height()) /
2.0;
nextY = candidateRegions_.back().top() +
(candidateRegions_.back().height() -
theme_->next().height()) /
2.0;
} else {
prevY = actualSize_.height() - contentMargin.bottom() -
theme_->prev().height();
nextY = actualSize_.height() - contentMargin.bottom() -
theme_->next().height();
}
nextRegion_ =
QRect(QPoint(actualSize_.width() - contentMargin.right() -
theme_->prev().width(),
nextY),
theme_->next().size());
double alpha = 1.0;
if (!hasNext_) {
alpha = 0.3;
} else if (nextHovered_) {
alpha = 0.7;
}
theme_->paint(painter, theme_->next(), nextRegion_.topLeft(),
alpha);
nextRegion_ = nextRegion_.marginsRemoved(theme_->next().margin());
prevRegion_ = QRect(
QPoint(actualSize_.width() - contentMargin.right() -
theme_->next().width() - theme_->prev().width(),
prevY),
theme_->prev().size());
alpha = 1.0;
if (!hasPrev_) {
alpha = 0.3;
} else if (prevHovered_) {
alpha = 0.7;
}
theme_->paint(painter, theme_->prev(), prevRegion_.topLeft(),
alpha);
prevRegion_ = prevRegion_.marginsRemoved(theme_->prev().margin());
}
}
}
void UpdateLayout(QTextLayout &layout, const FcitxTheme &theme,
std::initializer_list<
std::reference_wrapper<const FcitxQtFormattedPreeditList>>
texts) {
#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)
layout.clearFormats();
#endif
layout.setFont(theme.font());
QVector<QTextLayout::FormatRange> formats;
QString str;
int pos = 0;
for (const auto &text : texts) {
for (const auto &preedit : text.get()) {
str += preedit.string();
QTextCharFormat format;
if (preedit.format() & FcitxTextFormatFlag_Underline) {
format.setUnderlineStyle(QTextCharFormat::DashUnderline);
}
if (preedit.format() & FcitxTextFormatFlag_Strike) {
format.setFontStrikeOut(true);
}
if (preedit.format() & FcitxTextFormatFlag_Bold) {
format.setFontWeight(QFont::Bold);
}
if (preedit.format() & FcitxTextFormatFlag_Italic) {
format.setFontItalic(true);
}
if (preedit.format() & FcitxTextFormatFlag_HighLight) {
format.setBackground(theme.highlightBackgroundColor());
format.setForeground(theme.highlightColor());
}
formats.append(QTextLayout::FormatRange{
pos, static_cast<int>(preedit.string().length()), format});
pos += preedit.string().length();
}
}
layout.setText(str);
#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)
layout.setFormats(formats);
#endif
}
void FcitxCandidateWindow::updateClientSideUI(
const FcitxQtFormattedPreeditList &preedit, int cursorpos,
const FcitxQtFormattedPreeditList &auxUp,
const FcitxQtFormattedPreeditList &auxDown,
const FcitxQtStringKeyValueList &candidates, int candidateIndex,
int layoutHint, bool hasPrev, bool hasNext) {
bool preeditVisible = !preedit.isEmpty();
bool auxUpVisbile = !auxUp.isEmpty();
bool auxDownVisible = !auxDown.isEmpty();
bool candidatesVisible = !candidates.isEmpty();
bool visible =
preeditVisible || auxUpVisbile || auxDownVisible || candidatesVisible;
auto window = context_->focusWindowWrapper();
if (!theme_ || !visible || !window || window != parent_) {
hide();
return;
}
UpdateLayout(upperLayout_, *theme_, {auxUp, preedit});
if (cursorpos >= 0) {
int auxUpLength = 0;
for (const auto &auxUpText : auxUp) {
auxUpLength += auxUpText.string().length();
}
// Get the preedit part
auto preeditString = upperLayout_.text().mid(auxUpLength).toUtf8();
preeditString = preeditString.mid(0, cursorpos);
cursor_ = auxUpLength + QString::fromUtf8(preeditString).length();
} else {
cursor_ = -1;
}
doLayout(upperLayout_);
UpdateLayout(lowerLayout_, *theme_, {auxDown});
doLayout(lowerLayout_);
labelLayouts_.clear();
candidateLayouts_.clear();
for (int i = 0; i < candidates.size(); i++) {
labelLayouts_.emplace_back(std::make_unique<MultilineText>(
theme_->font(), candidates[i].key()));
candidateLayouts_.emplace_back(std::make_unique<MultilineText>(
theme_->font(), candidates[i].value()));
}
highlight_ = candidateIndex;
hasPrev_ = hasPrev;
hasNext_ = hasNext;
layoutHint_ = static_cast<FcitxCandidateLayoutHint>(layoutHint);
actualSize_ = sizeHint();
if (actualSize_.width() <= 0 || actualSize_.height() <= 0) {
hide();
return;
}
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
QSize sizeWithoutShadow = actualSize_.shrunkBy(theme_->shadowMargin());
#else
QSize sizeWithoutShadow =
actualSize_ -
QSize(theme_->shadowMargin().left() + theme_->shadowMargin().right(),
theme_->shadowMargin().top() + theme_->shadowMargin().bottom());
#endif
if (sizeWithoutShadow.width() < 0) {
sizeWithoutShadow.setWidth(0);
}
if (sizeWithoutShadow.height() < 0) {
sizeWithoutShadow.setHeight(0);
}
if (size() != actualSize_) {
resize(actualSize_);
}
update();
QRect cursorRect = context_->cursorRectangleWrapper();
QRect screenGeometry;
#if defined(FCITX_ENABLE_QT6_WAYLAND_WORKAROUND) && \
QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
if (isWayland_) {
auto *waylandWindow =
static_cast<QtWaylandClient::QWaylandWindow *>(window->handle());
const auto windowMargins = waylandWindow->windowContentMargins() -
waylandWindow->clientSideMargins();
auto windowGeometry = waylandWindow->windowContentGeometry();
if (!cursorRect.isValid()) {
if (cursorRect.width() <= 0) {
cursorRect.setWidth(1);
}
if (cursorRect.height() <= 0) {
cursorRect.setHeight(1);
}
}
QRect nativeCursorRect = QHighDpi::toNativePixels(cursorRect, this);
// valid the anchor rect.
if (!nativeCursorRect.intersects(windowGeometry)) {
if (nativeCursorRect.right() < windowGeometry.left()) {
nativeCursorRect.setLeft(windowGeometry.left());
nativeCursorRect.setWidth(1);
}
if (nativeCursorRect.left() > windowGeometry.right()) {
nativeCursorRect.setLeft(windowGeometry.right());
nativeCursorRect.setWidth(1);
}
if (nativeCursorRect.bottom() < windowGeometry.top()) {
nativeCursorRect.setTop(windowGeometry.top());
nativeCursorRect.setHeight(1);
}
if (nativeCursorRect.top() > windowGeometry.bottom()) {
nativeCursorRect.setTop(windowGeometry.bottom());
nativeCursorRect.setHeight(1);
}
}
bool wasVisible = isVisible();
bool cursorRectChanged = false;
if (property("_q_waylandPopupAnchorRect") != nativeCursorRect) {
cursorRectChanged = true;
setProperty("_q_waylandPopupAnchorRect", nativeCursorRect);
}
// This try to ensure xdg_popup is available.
show();
xdg_popup *xdgPopup = static_cast<xdg_popup *>(
QGuiApplication::platformNativeInterface()->nativeResourceForWindow(
"xdg_popup", this));
if (xdgWmBase_ && xdgPopup &&
xdg_popup_get_version(xdgPopup) >=
XDG_POPUP_REPOSITION_SINCE_VERSION) {
nativeCursorRect.translate(-windowMargins.left(),
-windowMargins.top());
auto *positioner =
new QtWayland::xdg_positioner(xdgWmBase_->create_positioner());
positioner->set_anchor_rect(
nativeCursorRect.x(), nativeCursorRect.y(),
nativeCursorRect.width(), nativeCursorRect.height());
positioner->set_anchor(
QtWayland::xdg_positioner::anchor_bottom_left);
positioner->set_gravity(
QtWayland::xdg_positioner::gravity_bottom_right);
auto *waylandCandidateWindow =
static_cast<QtWaylandClient::QWaylandWindow *>(handle());
QRect nativeGeometry =
waylandCandidateWindow->windowContentGeometry();
positioner->set_size(nativeGeometry.width(),
nativeGeometry.height());
positioner->set_reactive();
positioner->set_constraint_adjustment(
QtWayland::xdg_positioner::constraint_adjustment_slide_x |
QtWayland::xdg_positioner::constraint_adjustment_flip_y);
xdg_popup_reposition(xdgPopup, positioner->object(),
repositionToken_++);
positioner->destroy();
return;
}
// Check if we need remap.
// If it was invisible, nothing need to be done.
// If cursor rect changed, the window must be remapped.
// If adjustment is already happening (flip/slide), then remap.
// If we predict adjustment may be happening, then remap.
const auto predictGeometry =
QRect(QPoint(cursorRect.x(), cursorRect.y() + cursorRect.height()),
actualSize_);
if (wasVisible &&
(cursorRectChanged || position() != predictGeometry.topLeft() ||
!windowGeometry.contains(predictGeometry))) {
hide();
show();
}
return;
}
#endif
// Try to apply the screen edge detection over the window, because if we
// intent to use this with wayland. It we have no information above screen
// edge.
if (isWayland_) {
screenGeometry = window->frameGeometry();
cursorRect.translate(window->framePosition());
auto margins = window->frameMargins();
cursorRect.translate(margins.left(), margins.top());
} else {
screenGeometry = window->screen()->geometry();
auto pos = window->mapToGlobal(cursorRect.topLeft());
cursorRect.moveTo(pos);
}
int x = cursorRect.left();
int y = cursorRect.bottom();
if (cursorRect.left() + sizeWithoutShadow.width() >
screenGeometry.right()) {
x = screenGeometry.right() - sizeWithoutShadow.width() + 1;
}
if (x < screenGeometry.left()) {
x = screenGeometry.left();
}
if (y + sizeWithoutShadow.height() > screenGeometry.bottom()) {
if (y > screenGeometry.bottom()) {
y = screenGeometry.bottom() - sizeWithoutShadow.height() - 40;
} else { /* better position the window */
y = y - sizeWithoutShadow.height() -
((cursorRect.height() == 0) ? 40 : cursorRect.height());
}
}
if (y < screenGeometry.top()) {
y = screenGeometry.top();
}
QPoint newPosition(x, y);
newPosition -=
QPoint(theme_->shadowMargin().left(), theme_->shadowMargin().top());
if (newPosition != position()) {
if (isWayland_ && isVisible()) {
hide();
}
setPosition(newPosition);
}
show();
}
void FcitxCandidateWindow::mouseMoveEvent(QMouseEvent *event) {
bool needRepaint = false;
bool prevHovered = false;
bool nextHovered = false;
auto oldHighlight = highlight();
hoverIndex_ = -1;
prevHovered = prevRegion_.contains(event->pos());
if (!prevHovered) {
nextHovered = nextRegion_.contains(event->pos());
if (!nextHovered) {
for (int idx = 0, e = candidateRegions_.size(); idx < e; idx++) {
if (candidateRegions_[idx].contains(event->pos())) {
hoverIndex_ = idx;
break;
}
}
}
}
needRepaint = needRepaint || prevHovered_ != prevHovered;
prevHovered_ = prevHovered;
needRepaint = needRepaint || nextHovered_ != nextHovered;
nextHovered_ = nextHovered;
needRepaint = needRepaint || oldHighlight != highlight();
if (needRepaint) {
update();
}
}
void FcitxCandidateWindow::mouseReleaseEvent(QMouseEvent *event) {
if (event->button() != Qt::LeftButton) {
return;
}
if (prevRegion_.contains(event->pos())) {
Q_EMIT prevClicked();
return;
}
if (nextRegion_.contains(event->pos())) {
Q_EMIT nextClicked();
return;
}
for (int idx = 0, e = candidateRegions_.size(); idx < e; idx++) {
if (candidateRegions_[idx].contains(event->pos())) {
Q_EMIT candidateSelected(idx);
return;
}
}
}
QSize FcitxCandidateWindow::sizeHint() {
auto minH =
theme_->fontMetrics().ascent() + theme_->fontMetrics().descent();
size_t width = 0;
size_t height = 0;
auto updateIfLarger = [](size_t &m, size_t n) {
if (n > m) {
m = n;
}
};
auto textMargin = theme_->textMargin();
auto extraW = textMargin.left() + textMargin.right();
auto extraH = textMargin.top() + textMargin.bottom();
if (!upperLayout_.text().isEmpty()) {
auto size = upperLayout_.boundingRect();
height += minH + extraH;
updateIfLarger(width, size.width() + extraW);
}
if (!lowerLayout_.text().isEmpty()) {
auto size = lowerLayout_.boundingRect();
height += minH + extraH;
updateIfLarger(width, size.width() + extraW);
}
bool vertical = theme_->vertical();
if (layoutHint_ == FcitxCandidateLayoutHint::Vertical) {
vertical = true;
} else if (layoutHint_ == FcitxCandidateLayoutHint::Horizontal) {
vertical = false;
}
size_t wholeH = 0, wholeW = 0;
for (size_t i = 0; i < labelLayouts_.size(); i++) {
size_t candidateW = 0, candidateH = 0;
if (!labelLayouts_[i]->isEmpty()) {
auto size = labelLayouts_[i]->boundingRect();
candidateW += size.width();
updateIfLarger(candidateH,
std::max(minH, qCeil(size.height())) + extraH);
}
if (!candidateLayouts_[i]->isEmpty()) {
auto size = candidateLayouts_[i]->boundingRect();
candidateW += size.width();
updateIfLarger(candidateH,
std::max(minH, qCeil(size.height())) + extraH);
}
candidateW += extraW;
if (vertical) {
wholeH += candidateH;
updateIfLarger(wholeW, candidateW);
} else {
wholeW += candidateW;
updateIfLarger(wholeH, candidateH);
}
}
updateIfLarger(width, wholeW);
candidatesHeight_ = wholeH;
height += wholeH;
auto contentMargin = theme_->contentMargin();
width += contentMargin.left() + contentMargin.right();
height += contentMargin.top() + contentMargin.bottom();
if (!labelLayouts_.empty() && (hasPrev_ || hasNext_)) {
if (theme_->prev().valid() && theme_->next().valid()) {
width += theme_->prev().width() + theme_->next().width();
}
}
return {static_cast<int>(width), static_cast<int>(height)};
}
void FcitxCandidateWindow::wheelEvent(QWheelEvent *event) {
if (!theme_ || !theme_->wheelForPaging()) {
return;
}
accAngle_ += event->angleDelta().y();
auto angleForClick = 120;
while (accAngle_ >= angleForClick) {
accAngle_ -= angleForClick;
Q_EMIT prevClicked();
}
while (accAngle_ <= -angleForClick) {
accAngle_ += angleForClick;
Q_EMIT nextClicked();
}
}
void FcitxCandidateWindow::paintEvent(QPaintEvent *) {
QPainter p(this);
render(&p);
}
} // namespace fcitx

View File

@@ -0,0 +1,105 @@
/*
* SPDX-FileCopyrightText: 2021~2021 CSSlayer <wengxt@gmail.com>
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#ifndef _PLATFORMINPUTCONTEXT_FCITXCANDIDATEWINDOW_H_
#define _PLATFORMINPUTCONTEXT_FCITXCANDIDATEWINDOW_H_
#include "fcitxflags.h"
#include "fcitxqtdbustypes.h"
#include <QGuiApplication>
#include <QPainter>
#include <QPointer>
#include <QRasterWindow>
#include <QTextLayout>
#include <memory>
#include <qscopedpointer.h>
#include <vector>
#if defined(FCITX_ENABLE_QT6_WAYLAND_WORKAROUND) && \
QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
#include <QtWaylandClient/private/qwayland-xdg-shell.h>
#endif
namespace fcitx {
class FcitxTheme;
class MultilineText;
class QFcitxPlatformInputContext;
class FcitxCandidateWindow : public QRasterWindow {
Q_OBJECT
public:
explicit FcitxCandidateWindow(QWindow *window,
QFcitxPlatformInputContext *context);
~FcitxCandidateWindow();
void render(QPainter *painter);
public Q_SLOTS:
void updateClientSideUI(const FcitxQtFormattedPreeditList &preedit,
int cursorpos,
const FcitxQtFormattedPreeditList &auxUp,
const FcitxQtFormattedPreeditList &auxDown,
const FcitxQtStringKeyValueList &candidates,
int candidateIndex, int layoutHint, bool hasPrev,
bool hasNext);
QSize sizeHint();
Q_SIGNALS:
void candidateSelected(int i);
void prevClicked();
void nextClicked();
protected:
bool event(QEvent *event) override;
void paintEvent(QPaintEvent *event) override;
void mouseMoveEvent(QMouseEvent *) override;
void mouseReleaseEvent(QMouseEvent *) override;
void wheelEvent(QWheelEvent *) override;
int highlight() const {
int highlightIndex = (hoverIndex_ >= 0) ? hoverIndex_ : highlight_;
return highlightIndex;
}
private:
const bool isWayland_ =
QGuiApplication::platformName().startsWith("wayland");
uint32_t repositionToken_ = 0;
QSize actualSize_;
QPointer<QFcitxPlatformInputContext> context_;
QPointer<FcitxTheme> theme_;
QTextLayout upperLayout_;
QTextLayout lowerLayout_;
std::vector<std::unique_ptr<MultilineText>> candidateLayouts_;
std::vector<std::unique_ptr<MultilineText>> labelLayouts_;
int cursor_ = -1;
int highlight_ = -1;
int hoverIndex_ = -1;
int accAngle_ = 0;
bool prevHovered_ = false;
bool nextHovered_ = false;
bool hasPrev_ = false;
bool hasNext_ = false;
FcitxCandidateLayoutHint layoutHint_ = FcitxCandidateLayoutHint::NotSet;
int candidatesHeight_ = 0;
QRect prevRegion_;
QRect nextRegion_;
std::vector<QRect> candidateRegions_;
QPointer<QWindow> parent_;
#if defined(FCITX_ENABLE_QT6_WAYLAND_WORKAROUND) && \
QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
QScopedPointer<QtWayland::xdg_wm_base> xdgWmBase_;
#endif
};
} // namespace fcitx
#endif // _PLATFORMINPUTCONTEXT_FCITXCANDIDATEWINDOW_H_

View File

@@ -0,0 +1,451 @@
/*
* SPDX-FileCopyrightText: 2021~2021 CSSlayer <wengxt@gmail.com>
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#include "fcitxtheme.h"
#include "font.h"
#include <QDebug>
#include <QMargins>
#include <QPixmap>
#include <QSettings>
#include <QStandardPaths>
namespace fcitx {
bool readBool(const QSettings &settings, const QString &name,
bool defaultValue) {
return settings.value(name, defaultValue ? "True" : "False").toString() ==
"True";
}
QMargins readMargin(const QSettings &settings) {
settings.allKeys();
return QMargins(settings.value("Left", 0).toInt(),
settings.value("Top", 0).toInt(),
settings.value("Right", 0).toInt(),
settings.value("Bottom", 0).toInt());
}
QColor readColor(const QSettings &settings, const QString &name,
const QString &defaultValue) {
QString colorString = settings.value(name, defaultValue).toString();
QColor color(defaultValue);
if (colorString.startsWith("#")) {
if (colorString.size() == 7) {
// Parse #RRGGBB
color = QColor(colorString.toUpper());
} else if (colorString.size() == 9) {
// Qt accept "#AARRGGBB"
auto newColorString =
QString("#%1%2")
.arg(colorString.mid(7, 2), colorString.mid(1, 6))
.toUpper();
color = QColor(newColorString);
}
}
return color;
}
void BackgroundImage::load(const QString &name, QSettings &settings) {
settings.allKeys();
image_ = QPixmap();
overlay_ = QPixmap();
if (auto image = settings.value("Image").toString(); !image.isEmpty()) {
auto file = QStandardPaths::locate(
QStandardPaths::GenericDataLocation,
QString("fcitx5/themes/%1/%2").arg(name, image));
image_.load(file);
}
if (auto image = settings.value("Overlay").toString(); !image.isEmpty()) {
auto file = QStandardPaths::locate(
QStandardPaths::GenericDataLocation,
QString("fcitx5/themes/%1/%2").arg(name, image));
overlay_.load(file);
}
settings.beginGroup("Margin");
margin_ = readMargin(settings);
settings.endGroup();
if (image_.isNull()) {
QColor color = readColor(settings, "Color", "#ffffff");
QColor borderColor = readColor(settings, "BorderColor", "#00ffffff");
int borderWidth = settings.value("BorderWidth", 0).toInt();
fillBackground(borderColor, color, borderWidth);
}
settings.beginGroup("OverlayClipMargin");
overlayClipMargin_ = readMargin(settings);
settings.endGroup();
hideOverlayIfOversize_ =
settings.value("HideOverlayIfOversize", "False").toString() == "True";
overlayOffsetX_ = settings.value("OverlayOffsetX", 0).toInt();
overlayOffsetY_ = settings.value("OverlayOffsetY", 0).toInt();
gravity_ = settings.value("Gravity", "TopLeft").toString();
}
void BackgroundImage::loadFromValue(const QColor &border,
const QColor &background, QMargins margin,
int borderWidth) {
image_ = QPixmap();
overlay_ = QPixmap();
margin_ = margin;
fillBackground(border, background, borderWidth);
overlayClipMargin_ = QMargins();
hideOverlayIfOversize_ = false;
overlayOffsetX_ = 0;
overlayOffsetY_ = 0;
gravity_ = QString();
}
void BackgroundImage::fillBackground(const QColor &border,
const QColor &background,
int borderWidth) {
image_ = QPixmap(margin_.left() + margin_.right() + 1,
margin_.top() + margin_.bottom() + 1);
borderWidth = std::min({borderWidth, margin_.left(), margin_.right(),
margin_.top(), margin_.bottom()});
borderWidth = std::max(0, borderWidth);
QPainter painter;
painter.begin(&image_);
painter.setCompositionMode(QPainter::CompositionMode_Source);
if (borderWidth) {
painter.fillRect(image_.rect(), border);
}
painter.fillRect(QRect(borderWidth, borderWidth,
image_.width() - borderWidth * 2,
image_.height() - borderWidth * 2),
background);
painter.end();
}
void ActionImage::load(const QString &name, QSettings &settings) {
settings.allKeys();
image_ = QPixmap();
valid_ = false;
if (auto image = settings.value("Image").toString(); !image.isEmpty()) {
auto file = QStandardPaths::locate(
QStandardPaths::GenericDataLocation,
QString("fcitx5/themes/%1/%2").arg(name, image));
image_.load(file);
valid_ = !image_.isNull();
}
settings.beginGroup("ClickMargin");
margin_ = readMargin(settings);
settings.endGroup();
}
void ActionImage::reset() {
image_ = QPixmap();
valid_ = false;
margin_ = QMargins(0, 0, 0, 0);
}
FcitxTheme::FcitxTheme(QObject *parent)
: QObject(parent), configPath_(QStandardPaths::writableLocation(
QStandardPaths::GenericConfigLocation)
.append("/fcitx5/conf/classicui.conf")),
watcher_(new QFileSystemWatcher) {
connect(watcher_, &QFileSystemWatcher::fileChanged, this,
&FcitxTheme::configChanged);
watcher_->addPath(configPath_);
configChanged();
}
FcitxTheme::~FcitxTheme() {}
void FcitxTheme::configChanged() {
// Since fcitx is doing things like delete and move, we need to re-add the
// path.
watcher_->removePath(configPath_);
watcher_->addPath(configPath_);
QSettings settings(configPath_, QSettings::IniFormat);
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
settings.setIniCodec("UTF-8");
#endif
settings.childGroups();
font_ = parseFont(settings.value("Font", "Sans Serif 9").toString());
fontMetrics_ = QFontMetrics(font_);
vertical_ =
settings.value("Vertical Candidate List", "False").toString() == "True";
wheelForPaging_ =
settings.value("WheelForPaging", "True").toString() == "True";
theme_ = settings.value("Theme", "default").toString();
themeChanged();
}
void FcitxTheme::themeChanged() {
if (!themeConfigPath_.isEmpty()) {
watcher_->removePath(themeConfigPath_);
}
auto themeConfig = QString("/fcitx5/themes/%1/theme.conf").arg(theme_);
themeConfigPath_ =
QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)
.append(themeConfig);
auto file = QStandardPaths::locate(QStandardPaths::GenericDataLocation,
themeConfig);
if (file.isEmpty()) {
file = QStandardPaths::locate(QStandardPaths::GenericDataLocation,
"fcitx5/themes/default/theme.conf");
themeConfigPath_ = QStandardPaths::writableLocation(
QStandardPaths::GenericDataLocation)
.append("fcitx5/themes/default/theme.conf");
theme_ = "default";
}
watcher_->addPath(themeConfigPath_);
// We can not locate default theme.
if (file.isEmpty()) {
normalColor_ = QColor("#000000");
highlightCandidateColor_ = QColor("#ffffff");
fullWidthHighlight_ = true;
highlightColor_ = QColor("#ffffff");
highlightBackgroundColor_ = QColor("#a5a5a5");
contentMargin_ = QMargins{2, 2, 2, 2};
textMargin_ = QMargins{5, 5, 5, 5};
highlightClickMargin_ = QMargins{0, 0, 0, 0};
shadowMargin_ = QMargins{0, 0, 0, 0};
background_.loadFromValue(highlightBackgroundColor_, highlightColor_,
contentMargin_, 2);
highlight_.loadFromValue(highlightBackgroundColor_,
highlightBackgroundColor_, textMargin_, 0);
prev_.reset();
next_.reset();
return;
}
QSettings settings(file, QSettings::IniFormat);
settings.childGroups();
settings.beginGroup("InputPanel");
normalColor_ = readColor(settings, "NormalColor", "#000000");
highlightCandidateColor_ =
readColor(settings, "HighlightCandidateColor", "#ffffff");
fullWidthHighlight_ = readBool(settings, "FullWidthHighlight", true);
highlightColor_ = readColor(settings, "HighlightColor", "#ffffff");
highlightBackgroundColor_ =
readColor(settings, "HighlightBackgroundColor", "#a5a5a5");
buttonAlignment_ =
settings.value("PageButtonAlignment", "Bottom").toString();
settings.beginGroup("ContentMargin");
contentMargin_ = readMargin(settings);
settings.endGroup();
settings.beginGroup("TextMargin");
textMargin_ = readMargin(settings);
settings.endGroup();
settings.beginGroup("ShadowMargin");
shadowMargin_ = readMargin(settings);
settings.endGroup();
settings.beginGroup("Background");
background_.load(theme_, settings);
settings.endGroup();
settings.beginGroup("Highlight");
highlight_.load(theme_, settings);
settings.beginGroup("HighlightClickMargin");
highlightClickMargin_ = readMargin(settings);
settings.endGroup();
settings.endGroup();
settings.beginGroup("PrevPage");
prev_.load(theme_, settings);
settings.endGroup();
settings.beginGroup("NextPage");
next_.load(theme_, settings);
settings.endGroup();
}
} // namespace fcitx
void fcitx::FcitxTheme::paint(QPainter *painter,
const fcitx::BackgroundImage &image,
QRect region) {
auto marginTop = image.margin_.top();
auto marginBottom = image.margin_.bottom();
auto marginLeft = image.margin_.left();
auto marginRight = image.margin_.right();
int resizeHeight = image.image_.height() - marginTop - marginBottom;
int resizeWidth = image.image_.width() - marginLeft - marginRight;
if (resizeHeight <= 0) {
resizeHeight = 1;
}
if (resizeWidth <= 0) {
resizeWidth = 1;
}
if (region.height() < 0) {
region.setHeight(resizeHeight);
}
if (region.width() < 0) {
region.setWidth(resizeWidth);
}
/*
* 7 8 9
* 4 5 6
* 1 2 3
*/
if (marginLeft && marginBottom) {
/* part 1 */
painter->drawPixmap(
QRect(0, region.height() - marginBottom, marginLeft, marginBottom)
.translated(region.topLeft()),
image.image_,
QRect(0, marginTop + resizeHeight, marginLeft, marginBottom));
}
if (marginRight && marginBottom) {
/* part 3 */
painter->drawPixmap(
QRect(region.width() - marginRight, region.height() - marginBottom,
marginRight, marginBottom)
.translated(region.topLeft()),
image.image_,
QRect(marginLeft + resizeWidth, marginTop + resizeHeight,
marginRight, marginBottom));
}
if (marginLeft && marginTop) {
/* part 7 */
painter->drawPixmap(
QRect(0, 0, marginLeft, marginTop).translated(region.topLeft()),
image.image_, QRect(0, 0, marginLeft, marginTop));
}
if (marginRight && marginTop) {
/* part 9 */
painter->drawPixmap(
QRect(region.width() - marginRight, 0, marginRight, marginTop)
.translated(region.topLeft()),
image.image_,
QRect(marginLeft + resizeWidth, 0, marginRight, marginTop));
}
/* part 2 & 8 */
if (marginTop) {
painter->drawPixmap(
QRect(marginLeft, 0, region.width() - marginLeft - marginRight,
marginTop)
.translated(region.topLeft()),
image.image_, QRect(marginLeft, 0, resizeWidth, marginTop));
}
if (marginBottom) {
painter->drawPixmap(QRect(marginLeft, region.height() - marginBottom,
region.width() - marginLeft - marginRight,
marginBottom)
.translated(region.topLeft()),
image.image_,
QRect(marginLeft, marginTop + resizeHeight,
resizeWidth, marginBottom));
}
/* part 4 & 6 */
if (marginLeft) {
painter->drawPixmap(QRect(0, marginTop, marginLeft,
region.height() - marginTop - marginBottom)
.translated(region.topLeft()),
image.image_,
QRect(0, marginTop, marginLeft, resizeHeight));
}
if (marginRight) {
painter->drawPixmap(QRect(region.width() - marginRight, marginTop,
marginRight,
region.height() - marginTop - marginBottom)
.translated(region.topLeft()),
image.image_,
QRect(marginLeft + resizeWidth, marginTop,
marginRight, resizeHeight));
}
/* part 5 */
{
painter->drawPixmap(
QRect(marginLeft, marginTop,
region.width() - marginLeft - marginRight,
region.height() - marginTop - marginBottom)
.translated(region.topLeft()),
image.image_,
QRect(marginLeft, marginTop, resizeWidth, resizeHeight));
}
if (image.overlay_.isNull()) {
return;
}
auto clipWidth = region.width() - image.overlayClipMargin_.left() -
image.overlayClipMargin_.right();
auto clipHeight = region.height() - image.overlayClipMargin_.top() -
image.overlayClipMargin_.bottom();
if (clipWidth <= 0 || clipHeight <= 0) {
return;
}
QRect clipRect(region.topLeft() + QPoint(image.overlayClipMargin_.left(),
image.overlayClipMargin_.top()),
QSize(clipWidth, clipHeight));
int x = 0, y = 0;
if (image.gravity_ == "Top Left" || image.gravity_ == "Center Left" ||
image.gravity_ == "Bottom Left") {
x = image.overlayOffsetX_;
} else if (image.gravity_ == "Top Center" || image.gravity_ == "Center" ||
image.gravity_ == "Bottom Center") {
x = (region.width() - image.overlay_.width()) / 2 +
image.overlayOffsetX_;
} else {
x = region.width() - image.overlay_.width() - image.overlayOffsetX_;
}
if (image.gravity_ == "Top Left" || image.gravity_ == "Top Center" ||
image.gravity_ == "Top Right") {
y = image.overlayOffsetY_;
} else if (image.gravity_ == "Center Left" || image.gravity_ == "Center" ||
image.gravity_ == "Center Right") {
y = (region.height() - image.overlay_.height()) / 2 +
image.overlayOffsetY_;
} else {
y = region.height() - image.overlay_.height() - image.overlayOffsetY_;
}
QRect rect(QPoint(x, y) + region.topLeft(), image.overlay_.size());
QRect finalRect = rect.intersected(clipRect);
if (finalRect.isEmpty()) {
return;
}
if (image.hideOverlayIfOversize_ && !clipRect.contains(rect)) {
return;
}
painter->save();
painter->setClipRect(clipRect);
painter->drawPixmap(rect, image.overlay_);
painter->restore();
}
void fcitx::FcitxTheme::paint(QPainter *painter,
const fcitx::ActionImage &image, QPoint position,
float alpha) {
painter->save();
painter->setOpacity(alpha);
painter->drawPixmap(position, image.image_);
painter->restore();
}
QMargins fcitx::FcitxTheme::highlightMargin() const {
return highlight_.margin_;
}

View File

@@ -0,0 +1,124 @@
/*
* SPDX-FileCopyrightText: 2021~2021 CSSlayer <wengxt@gmail.com>
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#ifndef _PLATFORMINPUTCONTEXT_FCITXTHEME_H_
#define _PLATFORMINPUTCONTEXT_FCITXTHEME_H_
#include <QColor>
#include <QFileSystemWatcher>
#include <QFont>
#include <QFontMetrics>
#include <QMargins>
#include <QObject>
#include <QPainter>
#include <QPixmap>
#include <QSettings>
namespace fcitx {
class BackgroundImage {
friend class FcitxTheme;
public:
void load(const QString &name, QSettings &settings);
void loadFromValue(const QColor &border, const QColor &background,
QMargins margin, int borderWidth);
private:
void fillBackground(const QColor &border, const QColor &background,
int borderWidth);
QPixmap image_, overlay_;
QMargins margin_, overlayClipMargin_;
bool hideOverlayIfOversize_ = false;
QString gravity_;
int overlayOffsetX_ = 0;
int overlayOffsetY_ = 0;
};
class ActionImage {
friend class FcitxTheme;
public:
void load(const QString &name, QSettings &settings);
void reset();
bool valid() const { return valid_; }
int width() const { return image_.width(); }
int height() const { return image_.height(); }
QSize size() const { return image_.size(); }
const QMargins &margin() const { return margin_; }
private:
bool valid_ = false;
QPixmap image_;
QMargins margin_;
};
class FcitxTheme : public QObject {
Q_OBJECT
public:
FcitxTheme(QObject *parent = nullptr);
~FcitxTheme();
void paint(QPainter *painter, const BackgroundImage &image, QRect region);
void paint(QPainter *painter, const ActionImage &image, QPoint position,
float alpha);
const auto &background() const { return background_; }
const auto &highlight() const { return highlight_; }
const auto &prev() const { return prev_; }
const auto &next() const { return next_; }
const auto &font() const { return font_; }
const auto &fontMetrics() const { return fontMetrics_; }
const auto &highlightBackgroundColor() const {
return highlightBackgroundColor_;
}
const auto &highlightColor() const { return highlightColor_; }
const auto &buttonAlignment() const { return buttonAlignment_; }
auto contentMargin() const { return contentMargin_; }
auto textMargin() const { return textMargin_; }
auto highlightClickMargin() const { return highlightClickMargin_; }
QMargins highlightMargin() const;
auto shadowMargin() const { return shadowMargin_; }
auto normalColor() const { return normalColor_; }
auto highlightCandidateColor() const { return highlightCandidateColor_; }
auto vertical() const { return vertical_; }
auto wheelForPaging() const { return wheelForPaging_; }
private Q_SLOTS:
void configChanged();
void themeChanged();
private:
QString configPath_;
QString themeConfigPath_;
QFileSystemWatcher *watcher_;
QFont font_;
QFontMetrics fontMetrics_{font_};
bool vertical_ = false;
bool wheelForPaging_ = true;
QString theme_ = "default";
BackgroundImage background_;
BackgroundImage highlight_;
ActionImage prev_;
ActionImage next_;
QColor normalColor_{Qt::black};
QColor highlightCandidateColor_{Qt::white};
bool fullWidthHighlight_ = true;
QColor highlightColor_{Qt::white};
QColor highlightBackgroundColor_{0xa5, 0xa5, 0xa5};
QString buttonAlignment_;
QMargins highlightClickMargin_;
QMargins contentMargin_;
QMargins textMargin_;
QMargins shadowMargin_;
};
} // namespace fcitx
#endif // _PLATFORMINPUTCONTEXT_FCITXTHEME_H_

View File

@@ -0,0 +1,69 @@
/*
* SPDX-FileCopyrightText: 2020~2020 CSSlayer <wengxt@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
*/
#include "font.h"
#include <QMap>
QFont fcitx::parseFont(const QString &string) {
#if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0))
auto list = string.split(" ", QString::SkipEmptyParts);
#else
auto list = string.split(" ", Qt::SkipEmptyParts);
#endif
int size = 9; // Default size.
if (!list.empty()) {
bool ok = false;
auto fontSize = list.back().toInt(&ok);
if (ok) {
if (fontSize > 0) {
size = fontSize;
}
list.pop_back();
}
}
QFont::Style style = QFont::StyleNormal;
QFont::Weight weight = QFont::Normal;
const QMap<QString, QFont::Weight> strToWeight = {
{"Thin", QFont::Thin},
{"Ultra-Light", QFont::Thin},
{"Extra-Light", QFont::ExtraLight},
{"Light", QFont::Light},
{"Semi-Light", QFont::Light},
{"Demi-Light", QFont::Light},
{"Book", QFont::Light},
{"Regular", QFont::Normal},
{"Medium", QFont::Medium},
{"Semi-Bold", QFont::Medium},
{"Demi-Bold", QFont::DemiBold},
{"Bold", QFont::Bold},
{"Ultra-Bold", QFont::Bold},
{"Extra-Bold", QFont::ExtraBold},
{"Black", QFont::Black},
{"Ultra-Black", QFont::Black},
{"Extra-Black", QFont::Black},
};
const QMap<QString, QFont::Style> strToStyle = {
{"Italic", QFont::StyleItalic}, {"Oblique", QFont::StyleOblique}};
while (!list.empty()) {
if (strToWeight.contains(list.back())) {
weight = strToWeight.value(list.back(), QFont::Normal);
list.pop_back();
} else if (strToStyle.contains(list.back())) {
style = strToStyle.value(list.back(), QFont::StyleNormal);
list.pop_back();
} else {
break;
}
}
QString family = list.join(" ");
QFont font;
font.setFamily(family);
font.setWeight(weight);
font.setStyle(style);
font.setPointSize(size);
return font;
}

View File

@@ -0,0 +1,19 @@
/*
* SPDX-FileCopyrightText: 2020~2020 CSSlayer <wengxt@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
*/
#ifndef _CONFIGLIB_FONT_H_
#define _CONFIGLIB_FONT_H_
#include <QFont>
#include <QString>
namespace fcitx {
QFont parseFont(const QString &str);
} // namespace fcitx
#endif // _CONFIGLIB_FONT_H_

View File

@@ -0,0 +1,280 @@
/*
* SPDX-FileCopyrightText: 2023~2023 CSSlayer <wengxt@gmail.com>
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#include "hybridinputcontext.h"
#include "fcitx4inputcontextproxy.h"
#include "fcitxqtinputcontextproxy.h"
#include "fcitxqtwatcher.h"
#include <cstddef>
namespace fcitx {
HybridInputContext::HybridInputContext(FcitxQtWatcher *watcher,
Fcitx4Watcher *fcitx4Watcher,
QObject *parent)
: QObject(parent), watcher_(watcher), fcitx4Watcher_(fcitx4Watcher) {
// We use a timer here to avoid recheck being triggered multiple times.
// For fcitx5, the fcitx4frontend and dbusfrontend will be initialized
// around the same time. 50ms is specifically selected to be smaller than
// the 100ms timer within input context.
timer_.setInterval(50);
timer_.setSingleShot(true);
connect(&timer_, &QTimer::timeout, this,
[this]() { doRecheck(/*skipFcitx5=*/false); });
connect(watcher_, &FcitxQtWatcher::availabilityChanged, this,
&HybridInputContext::recheck);
connect(fcitx4Watcher_, &Fcitx4Watcher::availabilityChanged, this,
&HybridInputContext::recheck);
recheck();
}
void HybridInputContext::recheck() { timer_.start(); }
void HybridInputContext::doRecheck(bool skipFcitx5) {
const bool fcitx5Available = !skipFcitx5 && watcher_->availability();
if (!fcitx5Available) {
delete proxy_;
proxy_ = nullptr;
}
if (!fcitx4Watcher_->availability()) {
delete proxy4_;
proxy4_ = nullptr;
}
if (fcitx5Available) {
delete proxy4_;
proxy4_ = nullptr;
if (!proxy_) {
proxy_ = new FcitxQtInputContextProxy(watcher_, this);
proxy_->setDisplay(display_);
connect(proxy_, &FcitxQtInputContextProxy::commitString, this,
&HybridInputContext::commitString);
connect(proxy_, &FcitxQtInputContextProxy::currentIM, this,
&HybridInputContext::currentIM);
connect(proxy_, &FcitxQtInputContextProxy::deleteSurroundingText,
this, &HybridInputContext::deleteSurroundingText);
connect(proxy_, &FcitxQtInputContextProxy::forwardKey, this,
&HybridInputContext::forwardKey);
connect(proxy_, &FcitxQtInputContextProxy::updateFormattedPreedit,
this, &HybridInputContext::updateFormattedPreedit);
connect(proxy_, &FcitxQtInputContextProxy::updateClientSideUI, this,
&HybridInputContext::updateClientSideUI);
connect(proxy_, &FcitxQtInputContextProxy::inputContextCreated,
this, &HybridInputContext::inputContextCreated);
connect(proxy_,
&FcitxQtInputContextProxy::inputContextCreationFailed, this,
&HybridInputContext::inputContextCreationFailed);
connect(proxy_, &FcitxQtInputContextProxy::notifyFocusOut, this,
&HybridInputContext::notifyFocusOut);
connect(proxy_,
&FcitxQtInputContextProxy::virtualKeyboardVisibilityChanged,
this,
&HybridInputContext::virtualKeyboardVisibilityChanged);
}
} else if (fcitx4Watcher_->availability()) {
if (!proxy4_) {
proxy4_ = new Fcitx4InputContextProxy(fcitx4Watcher_, this);
connect(proxy4_, &Fcitx4InputContextProxy::commitString, this,
&HybridInputContext::commitString);
connect(proxy4_, &Fcitx4InputContextProxy::currentIM, this,
&HybridInputContext::currentIM);
connect(proxy4_, &Fcitx4InputContextProxy::deleteSurroundingText,
this, &HybridInputContext::deleteSurroundingText);
connect(proxy4_, &Fcitx4InputContextProxy::forwardKey, this,
&HybridInputContext::forwardKey);
connect(
proxy4_, &Fcitx4InputContextProxy::updateFormattedPreedit, this,
[this](const FcitxQtFormattedPreeditList &list, int cursorpos) {
auto newList = list;
for (auto item : newList) {
const qint32 underlineBit = (1 << 3);
// revert non underline and "underline"
item.setFormat(item.format() ^ underlineBit);
}
Q_EMIT updateFormattedPreedit(newList, cursorpos);
});
connect(proxy4_, &Fcitx4InputContextProxy::inputContextCreated,
this,
[this]() { Q_EMIT inputContextCreated(QByteArray()); });
}
}
}
void HybridInputContext::inputContextCreationFailed() {
doRecheck(/*skipFcitx5=*/true);
}
bool HybridInputContext::isValid() const {
if (proxy_) {
return proxy_->isValid();
} else if (proxy4_) {
return proxy4_->isValid();
}
return false;
}
void HybridInputContext::reset() {
if (proxy_) {
proxy_->reset();
} else if (proxy4_) {
proxy4_->reset();
}
}
void HybridInputContext::selectCandidate(int i) {
if (proxy_) {
proxy_->selectCandidate(i);
}
}
void HybridInputContext::prevPage() {
if (proxy_) {
proxy_->prevPage();
}
}
void HybridInputContext::nextPage() {
if (proxy_) {
proxy_->nextPage();
}
}
bool HybridInputContext::supportInvokeAction() const {
if (proxy_) {
return proxy_->supportInvokeAction();
}
return false;
}
void HybridInputContext::invokeAction(unsigned int action, int cursor) {
if (proxy_) {
proxy_->invokeAction(action, cursor);
}
}
void HybridInputContext::setSurroundingText(const QString &text,
unsigned int cursor,
unsigned int anchor) {
if (proxy_) {
proxy_->setSurroundingText(text, cursor, anchor);
} else if (proxy4_) {
proxy4_->setSurroundingText(text, cursor, anchor);
}
}
void HybridInputContext::setSurroundingTextPosition(unsigned int cursor,
unsigned int anchor) {
if (proxy_) {
proxy_->setSurroundingTextPosition(cursor, anchor);
} else if (proxy4_) {
proxy4_->setSurroundingTextPosition(cursor, anchor);
}
}
void HybridInputContext::focusIn() {
if (proxy_) {
proxy_->focusIn();
} else if (proxy4_) {
proxy4_->focusIn();
}
}
void HybridInputContext::focusOut() {
if (proxy_) {
proxy_->focusOut();
} else if (proxy4_) {
proxy4_->focusOut();
}
}
void HybridInputContext::setCursorRectV2(int x, int y, int w, int h,
double scale) {
if (proxy_) {
proxy_->setCursorRectV2(x, y, w, h, scale);
} else if (proxy4_) {
proxy4_->setCursorRect(x, y, w, h);
}
}
void HybridInputContext::setCursorRect(int x, int y, int w, int h) {
if (proxy_) {
proxy_->setCursorRect(x, y, w, h);
} else if (proxy4_) {
proxy4_->setCursorRect(x, y, w, h);
}
}
void HybridInputContext::setSupportedCapability(quint64 supported) {
if (proxy_) {
proxy_->setSupportedCapability(supported);
}
}
void HybridInputContext::setCapability(quint64 capability) {
if (proxy_) {
proxy_->setCapability(capability);
} else if (proxy4_) {
proxy4_->setCapability(capability & 0xfffffffffULL);
}
}
void HybridInputContext::showVirtualKeyboard() {
if (proxy_) {
proxy_->showVirtualKeyboard();
}
}
void HybridInputContext::hideVirtualKeyboard() {
if (proxy_) {
proxy_->hideVirtualKeyboard();
}
}
bool HybridInputContext::isVirtualKeyboardVisible() const {
if (proxy_) {
return proxy_->isVirtualKeyboardVisible();
}
return false;
}
void HybridInputContext::setDisplay(const QString &display) {
if (proxy_) {
proxy_->setDisplay(display);
}
display_ = display;
}
QDBusPendingCall HybridInputContext::processKeyEvent(unsigned int keyval,
unsigned int keycode,
unsigned int state,
bool type,
unsigned int time) {
if (proxy_) {
return proxy_->processKeyEvent(keyval, keycode, state, type, time);
}
return proxy4_->processKeyEvent(keyval, keycode, state, type ? 1 : 0, time);
}
bool HybridInputContext::processKeyEventResult(const QDBusPendingCall &call) {
if (call.isError()) {
return false;
}
if (call.reply().signature() == "b") {
QDBusPendingReply<bool> reply = call;
return reply.value();
} else if (call.reply().signature() == "i") {
QDBusPendingReply<int> reply = call;
return reply.value() > 0;
}
return false;
}
} // namespace fcitx

View File

@@ -0,0 +1,95 @@
/*
* SPDX-FileCopyrightText: 2023~2023 CSSlayer <wengxt@gmail.com>
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#ifndef HYBRIDINPUTCONTEXT_H
#define HYBRIDINPUTCONTEXT_H
#include "fcitx4inputcontextproxy.h"
#include "fcitx4watcher.h"
#include "fcitxqtdbustypes.h"
#include "fcitxqtinputcontextproxy.h"
#include "fcitxqtwatcher.h"
#include <QDBusPendingReply>
#include <QObject>
#include <QString>
#include <QTimer>
namespace fcitx {
class HybridInputContext : public QObject {
Q_OBJECT
public:
HybridInputContext(FcitxQtWatcher *watcher, Fcitx4Watcher *fcitx4Watcher,
QObject *parent);
void focusIn();
void focusOut();
void setSurroundingText(const QString &text, unsigned int cursor,
unsigned int anchor);
void setSurroundingTextPosition(unsigned int cursor, unsigned int anchor);
void prevPage();
void nextPage();
void reset();
void selectCandidate(int index);
bool supportInvokeAction() const;
void invokeAction(unsigned int action, int cursor);
bool isValid() const;
void showVirtualKeyboard();
void hideVirtualKeyboard();
bool isVirtualKeyboardVisible() const;
void setDisplay(const QString &display);
void setCapability(quint64 capability);
void setCursorRect(int x, int y, int w, int h);
void setCursorRectV2(int x, int y, int w, int h, double scale);
void setSupportedCapability(quint64 supported);
QDBusPendingCall processKeyEvent(unsigned int keyval, unsigned int keycode,
unsigned int state, bool type,
unsigned int time);
static bool processKeyEventResult(const QDBusPendingCall &call);
Q_SIGNALS:
void commitString(const QString &str);
void currentIM(const QString &name, const QString &uniqueName,
const QString &langCode);
void deleteSurroundingText(int offset, unsigned int nchar);
void forwardKey(unsigned int keyval, unsigned int state, bool isRelease);
void updateFormattedPreedit(const FcitxQtFormattedPreeditList &str,
int cursorpos);
void updateClientSideUI(const FcitxQtFormattedPreeditList &preedit,
int cursorpos,
const FcitxQtFormattedPreeditList &auxUp,
const FcitxQtFormattedPreeditList &auxDown,
const FcitxQtStringKeyValueList &candidates,
int candidateIndex, int layoutHint, bool hasPrev,
bool hasNext);
void inputContextCreated(const QByteArray &uuid);
void notifyFocusOut();
void virtualKeyboardVisibilityChanged(bool visible);
private Q_SLOTS:
void recheck();
void doRecheck(bool skipFcitx5);
void inputContextCreationFailed();
private:
QTimer timer_;
FcitxQtWatcher *watcher_;
Fcitx4Watcher *fcitx4Watcher_;
FcitxQtInputContextProxy *proxy_ = nullptr;
Fcitx4InputContextProxy *proxy4_ = nullptr;
QString display_;
};
} // namespace fcitx
#endif

View File

@@ -0,0 +1,21 @@
/*
* SPDX-FileCopyrightText: 2012~2017 CSSlayer <wengxt@gmail.com>
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#include "main.h"
fcitx::QFcitxPlatformInputContext *
QFcitx5PlatformInputContextPlugin::create(const QString &system,
const QStringList &paramList) {
Q_UNUSED(paramList);
if (system.compare(system, QStringLiteral("fcitx5"), Qt::CaseInsensitive) ==
0 ||
system.compare(system, QStringLiteral("fcitx"), Qt::CaseInsensitive) ==
0) {
return new fcitx::QFcitxPlatformInputContext;
}
return 0;
}

View File

@@ -0,0 +1,25 @@
/*
* SPDX-FileCopyrightText: 2012~2017 CSSlayer <wengxt@gmail.com>
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#ifndef MAIN_H
#define MAIN_H
#include <QStringList>
#include <qpa/qplatforminputcontextplugin_p.h>
#include "qfcitxplatforminputcontext.h"
class QFcitx5PlatformInputContextPlugin : public QPlatformInputContextPlugin {
Q_OBJECT
Q_PLUGIN_METADATA(IID QPlatformInputContextFactoryInterface_iid FILE
FCITX_PLUGIN_DATA_FILE_PATH)
public:
fcitx::QFcitxPlatformInputContext *
create(const QString &system, const QStringList &paramList) override;
};
#endif // MAIN_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,224 @@
/*
* SPDX-FileCopyrightText: 2012~2017 CSSlayer <wengxt@gmail.com>
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#ifndef QFCITXPLATFORMINPUTCONTEXT_H
#define QFCITXPLATFORMINPUTCONTEXT_H
#include "fcitxcandidatewindow.h"
#include "fcitxqtwatcher.h"
#include "hybridinputcontext.h"
#include <QDBusConnection>
#include <QDBusPendingCallWatcher>
#include <QDBusServiceWatcher>
#include <QGuiApplication>
#include <QKeyEvent>
#include <QPointer>
#include <QRect>
#include <QWindow>
#include <memory>
#include <qpa/qplatforminputcontext.h>
#include <unordered_map>
#include <xkbcommon/xkbcommon-compose.h>
namespace fcitx {
class FcitxQtConnection;
class QFcitxPlatformInputContext;
class FcitxQtICData : public QObject {
public:
FcitxQtICData(QFcitxPlatformInputContext *context, QWindow *window);
FcitxQtICData(const FcitxQtICData &that) = delete;
~FcitxQtICData() override;
FcitxCandidateWindow *candidateWindow();
QWindow *window() { return window_.data(); }
void resetCandidateWindow();
quint64 capability = 0;
HybridInputContext *proxy;
QRect rect;
// Last key event forwarded.
std::unique_ptr<QKeyEvent> event;
QString surroundingText;
int surroundingAnchor = -1;
int surroundingCursor = -1;
bool expectingMicroFocusChange = false;
bool eventFilter(QObject *watched, QEvent *event) override;
private:
QFcitxPlatformInputContext *context_;
QPointer<QWindow> window_;
QPointer<FcitxCandidateWindow> candidateWindow_;
};
class ProcessKeyWatcher : public QDBusPendingCallWatcher {
Q_OBJECT
public:
ProcessKeyWatcher(const QKeyEvent &event, QWindow *window,
const QDBusPendingCall &call, QObject *parent = nullptr)
: QDBusPendingCallWatcher(call, parent),
event_(event.type(), event.key(), event.modifiers(),
event.nativeScanCode(), event.nativeVirtualKey(),
event.nativeModifiers(), event.text(), event.isAutoRepeat(),
event.count()),
window_(window) {}
const QKeyEvent &keyEvent() { return event_; }
QWindow *window() { return window_.data(); }
private:
QKeyEvent event_;
QPointer<QWindow> window_;
};
struct XkbContextDeleter {
static inline void cleanup(struct xkb_context *pointer) {
if (pointer) {
xkb_context_unref(pointer);
}
}
};
struct XkbComposeTableDeleter {
static inline void cleanup(struct xkb_compose_table *pointer) {
if (pointer) {
xkb_compose_table_unref(pointer);
}
}
};
struct XkbComposeStateDeleter {
static inline void cleanup(struct xkb_compose_state *pointer) {
if (pointer) {
xkb_compose_state_unref(pointer);
}
}
};
class QFcitxPlatformInputContext : public QPlatformInputContext {
Q_OBJECT
public:
QFcitxPlatformInputContext();
~QFcitxPlatformInputContext() override;
bool isValid() const override;
void setFocusObject(QObject *object) override;
void invokeAction(QInputMethod::Action imAction,
int cursorPosition) override;
void reset() override;
void commit() override;
void update(Qt::InputMethodQueries queries) override;
bool filterEvent(const QEvent *event) override;
QLocale locale() const override;
bool hasCapability(Capability capability) const override;
void showInputPanel() override;
void hideInputPanel() override;
bool isInputPanelVisible() const override;
auto *watcher() { return watcher_; }
auto *fcitx4Watcher() { return fcitx4Watcher_; }
// Use Wrapper as suffix to avoid upstream add function with same name.
QObject *focusObjectWrapper() const;
QWindow *focusWindowWrapper() const;
QRect cursorRectangleWrapper() const;
// Initialize theme object on demand.
FcitxTheme *theme();
bool hasPreedit() const { return !preeditList_.isEmpty(); }
public Q_SLOTS:
void cursorRectChanged();
void commitString(const QString &str);
void updateFormattedPreedit(const FcitxQtFormattedPreeditList &preeditList,
int cursorPos);
void deleteSurroundingText(int offset, unsigned int nchar);
void forwardKey(unsigned int keyval, unsigned int state, bool type);
void createInputContextFinished(const QByteArray &uuid);
void cleanUp();
void windowDestroyed(QObject *object);
void updateCurrentIM(const QString &name, const QString &uniqueName,
const QString &langCode);
void updateClientSideUI(const FcitxQtFormattedPreeditList &preedit,
int cursorpos,
const FcitxQtFormattedPreeditList &auxUp,
const FcitxQtFormattedPreeditList &auxDown,
const FcitxQtStringKeyValueList &candidates,
int candidateIndex, int layoutHint, bool hasPrev,
bool hasNext);
void serverSideFocusOut();
bool commitPreedit(QPointer<QObject> input = qApp->focusObject());
private Q_SLOTS:
void processKeyEventFinished(QDBusPendingCallWatcher *);
private:
bool processCompose(unsigned int keyval, unsigned int state,
bool isRelease);
QKeyEvent *createKeyEvent(unsigned int keyval, unsigned int state,
bool isRelease, const QKeyEvent *event);
void forwardEvent(QWindow *window, const QKeyEvent &event);
void addCapability(FcitxQtICData &data, quint64 capability,
bool forceUpdate = false) {
auto newcaps = data.capability | capability;
if (data.capability != newcaps || forceUpdate) {
data.capability = newcaps;
updateCapability(data);
}
}
void removeCapability(FcitxQtICData &data, quint64 capability,
bool forceUpdate = false) {
auto newcaps = data.capability & (~capability);
if (data.capability != newcaps || forceUpdate) {
data.capability = newcaps;
updateCapability(data);
}
}
void updateCapability(const FcitxQtICData &data);
void createICData(QWindow *w);
HybridInputContext *validIC() const;
HybridInputContext *validICByWindow(QWindow *window) const;
bool filterEventFallback(unsigned int keyval, unsigned int keycode,
unsigned int state, bool isRelease);
void updateCursorRect();
bool objectAcceptsInputMethod() const;
bool shouldDisableInputMethod() const;
void updateInputPanelVisible();
FcitxQtWatcher *watcher_;
Fcitx4Watcher *fcitx4Watcher_;
QString preedit_;
QString commitPreedit_;
FcitxQtFormattedPreeditList preeditList_;
int cursorPos_;
bool useSurroundingText_;
bool syncMode_;
std::unordered_map<QWindow *, FcitxQtICData> icMap_;
QPointer<QWindow> lastWindow_;
QPointer<QObject> lastObject_;
bool destroy_;
bool virtualKeyboardVisible_;
QScopedPointer<struct xkb_context, XkbContextDeleter> xkbContext_;
QScopedPointer<struct xkb_compose_table, XkbComposeTableDeleter>
xkbComposeTable_;
QScopedPointer<struct xkb_compose_state, XkbComposeStateDeleter>
xkbComposeState_;
QLocale locale_;
FcitxTheme *theme_ = nullptr;
bool inputPanelVisible_ = false;
};
} // namespace fcitx
#endif // QFCITXPLATFORMINPUTCONTEXT_H

View File

@@ -0,0 +1,364 @@
/*
* SPDX-FileCopyrightText: 2017~2017 CSSlayer <wengxt@gmail.com>
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#include "qtkey.h"
#include <QHash>
#include <QString>
#include <ctype.h>
#include <qnamespace.h>
#include <unordered_map>
#include <xkbcommon/xkbcommon.h>
const std::unordered_map<uint32_t, int> &KeyTbl() {
static std::unordered_map<uint32_t, int> keyTbl{
std::make_pair(XKB_KEY_KP_Space, Qt::Key_Space),
std::make_pair(XKB_KEY_KP_Tab, Qt::Key_Tab),
std::make_pair(XKB_KEY_KP_Enter, Qt::Key_Enter),
std::make_pair(XKB_KEY_KP_F1, Qt::Key_F1),
std::make_pair(XKB_KEY_KP_F2, Qt::Key_F2),
std::make_pair(XKB_KEY_KP_F3, Qt::Key_F3),
std::make_pair(XKB_KEY_KP_F4, Qt::Key_F4),
std::make_pair(XKB_KEY_KP_Home, Qt::Key_Home),
std::make_pair(XKB_KEY_KP_Left, Qt::Key_Left),
std::make_pair(XKB_KEY_KP_Up, Qt::Key_Up),
std::make_pair(XKB_KEY_KP_Right, Qt::Key_Right),
std::make_pair(XKB_KEY_KP_Down, Qt::Key_Down),
std::make_pair(XKB_KEY_KP_Page_Up, Qt::Key_PageUp),
std::make_pair(XKB_KEY_KP_Page_Down, Qt::Key_PageDown),
std::make_pair(XKB_KEY_KP_End, Qt::Key_End),
std::make_pair(XKB_KEY_KP_Begin, Qt::Key_Clear),
std::make_pair(XKB_KEY_KP_Insert, Qt::Key_Insert),
std::make_pair(XKB_KEY_KP_Delete, Qt::Key_Delete),
std::make_pair(XKB_KEY_KP_Equal, Qt::Key_Equal),
std::make_pair(XKB_KEY_KP_Multiply, Qt::Key_multiply),
std::make_pair(XKB_KEY_KP_Add, Qt::Key_Plus),
std::make_pair(XKB_KEY_KP_Separator, Qt::Key_Comma),
std::make_pair(XKB_KEY_KP_Subtract, Qt::Key_Minus),
std::make_pair(XKB_KEY_KP_Decimal, Qt::Key_Period),
std::make_pair(XKB_KEY_KP_Divide, Qt::Key_Slash),
std::make_pair(XKB_KEY_KP_0, Qt::Key_0),
std::make_pair(XKB_KEY_KP_1, Qt::Key_1),
std::make_pair(XKB_KEY_KP_2, Qt::Key_2),
std::make_pair(XKB_KEY_KP_3, Qt::Key_3),
std::make_pair(XKB_KEY_KP_4, Qt::Key_4),
std::make_pair(XKB_KEY_KP_5, Qt::Key_5),
std::make_pair(XKB_KEY_KP_6, Qt::Key_6),
std::make_pair(XKB_KEY_KP_7, Qt::Key_7),
std::make_pair(XKB_KEY_KP_8, Qt::Key_8),
std::make_pair(XKB_KEY_KP_9, Qt::Key_9),
std::make_pair(XKB_KEY_Escape, Qt::Key_Escape),
std::make_pair(XKB_KEY_Tab, Qt::Key_Tab),
std::make_pair(XKB_KEY_ISO_Left_Tab, Qt::Key_Backtab),
std::make_pair(XKB_KEY_BackSpace, Qt::Key_Backspace),
std::make_pair(XKB_KEY_Return, Qt::Key_Return),
std::make_pair(XKB_KEY_KP_Enter, Qt::Key_Enter),
std::make_pair(XKB_KEY_Insert, Qt::Key_Insert),
std::make_pair(XKB_KEY_Delete, Qt::Key_Delete),
std::make_pair(XKB_KEY_Clear, Qt::Key_Delete),
std::make_pair(XKB_KEY_Pause, Qt::Key_Pause),
std::make_pair(XKB_KEY_Print, Qt::Key_Print),
std::make_pair(XKB_KEY_Sys_Req, Qt::Key_SysReq),
std::make_pair(0x1005FF60, Qt::Key_SysReq),
std::make_pair(0x1007ff00, Qt::Key_SysReq),
std::make_pair(XKB_KEY_Home, Qt::Key_Home),
std::make_pair(XKB_KEY_End, Qt::Key_End),
std::make_pair(XKB_KEY_Left, Qt::Key_Left),
std::make_pair(XKB_KEY_Up, Qt::Key_Up),
std::make_pair(XKB_KEY_Right, Qt::Key_Right),
std::make_pair(XKB_KEY_Down, Qt::Key_Down),
std::make_pair(XKB_KEY_Page_Up, Qt::Key_PageUp),
std::make_pair(XKB_KEY_Page_Down, Qt::Key_PageDown),
std::make_pair(XKB_KEY_Shift_L, Qt::Key_Shift),
std::make_pair(XKB_KEY_Shift_R, Qt::Key_Shift),
std::make_pair(XKB_KEY_Shift_Lock, Qt::Key_Shift),
std::make_pair(XKB_KEY_Control_L, Qt::Key_Control),
std::make_pair(XKB_KEY_Control_R, Qt::Key_Control),
std::make_pair(XKB_KEY_Meta_L, Qt::Key_Meta),
std::make_pair(XKB_KEY_Meta_R, Qt::Key_Meta),
std::make_pair(XKB_KEY_Alt_L, Qt::Key_Alt),
std::make_pair(XKB_KEY_Alt_R, Qt::Key_Alt),
std::make_pair(XKB_KEY_Caps_Lock, Qt::Key_CapsLock),
std::make_pair(XKB_KEY_Num_Lock, Qt::Key_NumLock),
std::make_pair(XKB_KEY_Scroll_Lock, Qt::Key_ScrollLock),
std::make_pair(XKB_KEY_F1, Qt::Key_F1),
std::make_pair(XKB_KEY_F2, Qt::Key_F2),
std::make_pair(XKB_KEY_F3, Qt::Key_F3),
std::make_pair(XKB_KEY_F4, Qt::Key_F4),
std::make_pair(XKB_KEY_F5, Qt::Key_F5),
std::make_pair(XKB_KEY_F6, Qt::Key_F6),
std::make_pair(XKB_KEY_F7, Qt::Key_F7),
std::make_pair(XKB_KEY_F8, Qt::Key_F8),
std::make_pair(XKB_KEY_F9, Qt::Key_F9),
std::make_pair(XKB_KEY_F10, Qt::Key_F10),
std::make_pair(XKB_KEY_F11, Qt::Key_F11),
std::make_pair(XKB_KEY_F12, Qt::Key_F12),
std::make_pair(XKB_KEY_F13, Qt::Key_F13),
std::make_pair(XKB_KEY_F14, Qt::Key_F14),
std::make_pair(XKB_KEY_F15, Qt::Key_F15),
std::make_pair(XKB_KEY_F16, Qt::Key_F16),
std::make_pair(XKB_KEY_F17, Qt::Key_F17),
std::make_pair(XKB_KEY_F18, Qt::Key_F18),
std::make_pair(XKB_KEY_F19, Qt::Key_F19),
std::make_pair(XKB_KEY_F20, Qt::Key_F20),
std::make_pair(XKB_KEY_F21, Qt::Key_F21),
std::make_pair(XKB_KEY_F22, Qt::Key_F22),
std::make_pair(XKB_KEY_F23, Qt::Key_F23),
std::make_pair(XKB_KEY_F24, Qt::Key_F24),
std::make_pair(XKB_KEY_F25, Qt::Key_F25),
std::make_pair(XKB_KEY_F26, Qt::Key_F26),
std::make_pair(XKB_KEY_F27, Qt::Key_F27),
std::make_pair(XKB_KEY_F28, Qt::Key_F28),
std::make_pair(XKB_KEY_F29, Qt::Key_F29),
std::make_pair(XKB_KEY_F30, Qt::Key_F30),
std::make_pair(XKB_KEY_F31, Qt::Key_F31),
std::make_pair(XKB_KEY_F32, Qt::Key_F32),
std::make_pair(XKB_KEY_F33, Qt::Key_F33),
std::make_pair(XKB_KEY_F34, Qt::Key_F34),
std::make_pair(XKB_KEY_F35, Qt::Key_F35),
std::make_pair(XKB_KEY_Super_L, Qt::Key_Super_L),
std::make_pair(XKB_KEY_Super_R, Qt::Key_Super_R),
std::make_pair(XKB_KEY_Menu, Qt::Key_Menu),
std::make_pair(XKB_KEY_Hyper_L, Qt::Key_Hyper_L),
std::make_pair(XKB_KEY_Hyper_R, Qt::Key_Hyper_R),
std::make_pair(XKB_KEY_Help, Qt::Key_Help),
std::make_pair(XKB_KEY_ISO_Level3_Shift, Qt::Key_AltGr),
std::make_pair(XKB_KEY_Multi_key, Qt::Key_Multi_key),
std::make_pair(XKB_KEY_Codeinput, Qt::Key_Codeinput),
std::make_pair(XKB_KEY_SingleCandidate, Qt::Key_SingleCandidate),
std::make_pair(XKB_KEY_MultipleCandidate, Qt::Key_MultipleCandidate),
std::make_pair(XKB_KEY_PreviousCandidate, Qt::Key_PreviousCandidate),
std::make_pair(XKB_KEY_Mode_switch, Qt::Key_Mode_switch),
std::make_pair(XKB_KEY_script_switch, Qt::Key_Mode_switch),
std::make_pair(XKB_KEY_Kanji, Qt::Key_Kanji),
std::make_pair(XKB_KEY_Muhenkan, Qt::Key_Muhenkan),
std::make_pair(XKB_KEY_Henkan, Qt::Key_Henkan),
std::make_pair(XKB_KEY_Romaji, Qt::Key_Romaji),
std::make_pair(XKB_KEY_Hiragana, Qt::Key_Hiragana),
std::make_pair(XKB_KEY_Katakana, Qt::Key_Katakana),
std::make_pair(XKB_KEY_Hiragana_Katakana, Qt::Key_Hiragana_Katakana),
std::make_pair(XKB_KEY_Zenkaku, Qt::Key_Zenkaku),
std::make_pair(XKB_KEY_Hankaku, Qt::Key_Hankaku),
std::make_pair(XKB_KEY_Zenkaku_Hankaku, Qt::Key_Zenkaku_Hankaku),
std::make_pair(XKB_KEY_Touroku, Qt::Key_Touroku),
std::make_pair(XKB_KEY_Massyo, Qt::Key_Massyo),
std::make_pair(XKB_KEY_Kana_Lock, Qt::Key_Kana_Lock),
std::make_pair(XKB_KEY_Kana_Shift, Qt::Key_Kana_Shift),
std::make_pair(XKB_KEY_Eisu_Shift, Qt::Key_Eisu_Shift),
std::make_pair(XKB_KEY_Eisu_toggle, Qt::Key_Eisu_toggle),
std::make_pair(XKB_KEY_Kanji_Bangou, Qt::Key_Codeinput),
std::make_pair(XKB_KEY_Zen_Koho, Qt::Key_MultipleCandidate),
std::make_pair(XKB_KEY_Mae_Koho, Qt::Key_PreviousCandidate),
std::make_pair(XKB_KEY_Hangul, Qt::Key_Hangul),
std::make_pair(XKB_KEY_Hangul_Start, Qt::Key_Hangul_Start),
std::make_pair(XKB_KEY_Hangul_End, Qt::Key_Hangul_End),
std::make_pair(XKB_KEY_Hangul_Hanja, Qt::Key_Hangul_Hanja),
std::make_pair(XKB_KEY_Hangul_Jamo, Qt::Key_Hangul_Jamo),
std::make_pair(XKB_KEY_Hangul_Romaja, Qt::Key_Hangul_Romaja),
std::make_pair(XKB_KEY_Hangul_Codeinput, Qt::Key_Codeinput),
std::make_pair(XKB_KEY_Hangul_Jeonja, Qt::Key_Hangul_Jeonja),
std::make_pair(XKB_KEY_Hangul_Banja, Qt::Key_Hangul_Banja),
std::make_pair(XKB_KEY_Hangul_PreHanja, Qt::Key_Hangul_PreHanja),
std::make_pair(XKB_KEY_Hangul_PostHanja, Qt::Key_Hangul_PostHanja),
std::make_pair(XKB_KEY_Hangul_SingleCandidate, Qt::Key_SingleCandidate),
std::make_pair(XKB_KEY_Hangul_MultipleCandidate,
Qt::Key_MultipleCandidate),
std::make_pair(XKB_KEY_Hangul_PreviousCandidate,
Qt::Key_PreviousCandidate),
std::make_pair(XKB_KEY_Hangul_Special, Qt::Key_Hangul_Special),
std::make_pair(XKB_KEY_Hangul_switch, Qt::Key_Mode_switch),
std::make_pair(XKB_KEY_dead_grave, Qt::Key_Dead_Grave),
std::make_pair(XKB_KEY_dead_acute, Qt::Key_Dead_Acute),
std::make_pair(XKB_KEY_dead_circumflex, Qt::Key_Dead_Circumflex),
std::make_pair(XKB_KEY_dead_tilde, Qt::Key_Dead_Tilde),
std::make_pair(XKB_KEY_dead_macron, Qt::Key_Dead_Macron),
std::make_pair(XKB_KEY_dead_breve, Qt::Key_Dead_Breve),
std::make_pair(XKB_KEY_dead_abovedot, Qt::Key_Dead_Abovedot),
std::make_pair(XKB_KEY_dead_diaeresis, Qt::Key_Dead_Diaeresis),
std::make_pair(XKB_KEY_dead_abovering, Qt::Key_Dead_Abovering),
std::make_pair(XKB_KEY_dead_doubleacute, Qt::Key_Dead_Doubleacute),
std::make_pair(XKB_KEY_dead_caron, Qt::Key_Dead_Caron),
std::make_pair(XKB_KEY_dead_cedilla, Qt::Key_Dead_Cedilla),
std::make_pair(XKB_KEY_dead_ogonek, Qt::Key_Dead_Ogonek),
std::make_pair(XKB_KEY_dead_iota, Qt::Key_Dead_Iota),
std::make_pair(XKB_KEY_dead_voiced_sound, Qt::Key_Dead_Voiced_Sound),
std::make_pair(XKB_KEY_dead_semivoiced_sound,
Qt::Key_Dead_Semivoiced_Sound),
std::make_pair(XKB_KEY_dead_belowdot, Qt::Key_Dead_Belowdot),
std::make_pair(XKB_KEY_dead_hook, Qt::Key_Dead_Hook),
std::make_pair(XKB_KEY_dead_horn, Qt::Key_Dead_Horn),
std::make_pair(XKB_KEY_XF86Back, Qt::Key_Back),
std::make_pair(XKB_KEY_XF86Forward, Qt::Key_Forward),
std::make_pair(XKB_KEY_XF86Stop, Qt::Key_Stop),
std::make_pair(XKB_KEY_XF86Refresh, Qt::Key_Refresh),
std::make_pair(XKB_KEY_XF86AudioLowerVolume, Qt::Key_VolumeDown),
std::make_pair(XKB_KEY_XF86AudioMute, Qt::Key_VolumeMute),
std::make_pair(XKB_KEY_XF86AudioRaiseVolume, Qt::Key_VolumeUp),
std::make_pair(XKB_KEY_XF86AudioPlay, Qt::Key_MediaPlay),
std::make_pair(XKB_KEY_XF86AudioStop, Qt::Key_MediaStop),
std::make_pair(XKB_KEY_XF86AudioPrev, Qt::Key_MediaPrevious),
std::make_pair(XKB_KEY_XF86AudioNext, Qt::Key_MediaNext),
std::make_pair(XKB_KEY_XF86AudioRecord, Qt::Key_MediaRecord),
std::make_pair(XKB_KEY_XF86AudioPause, Qt::Key_MediaPause),
std::make_pair(XKB_KEY_XF86HomePage, Qt::Key_HomePage),
std::make_pair(XKB_KEY_XF86Favorites, Qt::Key_Favorites),
std::make_pair(XKB_KEY_XF86Search, Qt::Key_Search),
std::make_pair(XKB_KEY_XF86Standby, Qt::Key_Standby),
std::make_pair(XKB_KEY_XF86OpenURL, Qt::Key_OpenUrl),
std::make_pair(XKB_KEY_XF86Mail, Qt::Key_LaunchMail),
std::make_pair(XKB_KEY_XF86AudioMedia, Qt::Key_LaunchMedia),
std::make_pair(XKB_KEY_XF86MyComputer, Qt::Key_Launch0),
std::make_pair(XKB_KEY_XF86Calculator, Qt::Key_Launch1),
std::make_pair(XKB_KEY_XF86Launch0, Qt::Key_Launch2),
std::make_pair(XKB_KEY_XF86Launch1, Qt::Key_Launch3),
std::make_pair(XKB_KEY_XF86Launch2, Qt::Key_Launch4),
std::make_pair(XKB_KEY_XF86Launch3, Qt::Key_Launch5),
std::make_pair(XKB_KEY_XF86Launch4, Qt::Key_Launch6),
std::make_pair(XKB_KEY_XF86Launch5, Qt::Key_Launch7),
std::make_pair(XKB_KEY_XF86Launch6, Qt::Key_Launch8),
std::make_pair(XKB_KEY_XF86Launch7, Qt::Key_Launch9),
std::make_pair(XKB_KEY_XF86Launch8, Qt::Key_LaunchA),
std::make_pair(XKB_KEY_XF86Launch9, Qt::Key_LaunchB),
std::make_pair(XKB_KEY_XF86LaunchA, Qt::Key_LaunchC),
std::make_pair(XKB_KEY_XF86LaunchB, Qt::Key_LaunchD),
std::make_pair(XKB_KEY_XF86LaunchC, Qt::Key_LaunchE),
std::make_pair(XKB_KEY_XF86LaunchD, Qt::Key_LaunchF),
std::make_pair(XKB_KEY_XF86MonBrightnessUp, Qt::Key_MonBrightnessUp),
std::make_pair(XKB_KEY_XF86MonBrightnessDown,
Qt::Key_MonBrightnessDown),
std::make_pair(XKB_KEY_XF86KbdLightOnOff, Qt::Key_KeyboardLightOnOff),
std::make_pair(XKB_KEY_XF86KbdBrightnessUp,
Qt::Key_KeyboardBrightnessUp),
std::make_pair(XKB_KEY_XF86KbdBrightnessDown,
Qt::Key_KeyboardBrightnessDown),
std::make_pair(XKB_KEY_XF86PowerOff, Qt::Key_PowerOff),
std::make_pair(XKB_KEY_XF86WakeUp, Qt::Key_WakeUp),
std::make_pair(XKB_KEY_XF86Eject, Qt::Key_Eject),
std::make_pair(XKB_KEY_XF86ScreenSaver, Qt::Key_ScreenSaver),
std::make_pair(XKB_KEY_XF86WWW, Qt::Key_WWW),
std::make_pair(XKB_KEY_XF86Memo, Qt::Key_Memo),
std::make_pair(XKB_KEY_XF86LightBulb, Qt::Key_LightBulb),
std::make_pair(XKB_KEY_XF86Shop, Qt::Key_Shop),
std::make_pair(XKB_KEY_XF86History, Qt::Key_History),
std::make_pair(XKB_KEY_XF86AddFavorite, Qt::Key_AddFavorite),
std::make_pair(XKB_KEY_XF86HotLinks, Qt::Key_HotLinks),
std::make_pair(XKB_KEY_XF86BrightnessAdjust, Qt::Key_BrightnessAdjust),
std::make_pair(XKB_KEY_XF86Finance, Qt::Key_Finance),
std::make_pair(XKB_KEY_XF86Community, Qt::Key_Community),
std::make_pair(XKB_KEY_XF86AudioRewind, Qt::Key_AudioRewind),
std::make_pair(XKB_KEY_XF86BackForward, Qt::Key_BackForward),
std::make_pair(XKB_KEY_XF86ApplicationLeft, Qt::Key_ApplicationLeft),
std::make_pair(XKB_KEY_XF86ApplicationRight, Qt::Key_ApplicationRight),
std::make_pair(XKB_KEY_XF86Book, Qt::Key_Book),
std::make_pair(XKB_KEY_XF86CD, Qt::Key_CD),
std::make_pair(XKB_KEY_XF86Calculater, Qt::Key_Calculator),
std::make_pair(XKB_KEY_XF86ToDoList, Qt::Key_ToDoList),
std::make_pair(XKB_KEY_XF86ClearGrab, Qt::Key_ClearGrab),
std::make_pair(XKB_KEY_XF86Close, Qt::Key_Close),
std::make_pair(XKB_KEY_XF86Copy, Qt::Key_Copy),
std::make_pair(XKB_KEY_XF86Cut, Qt::Key_Cut),
std::make_pair(XKB_KEY_XF86Display, Qt::Key_Display),
std::make_pair(XKB_KEY_XF86DOS, Qt::Key_DOS),
std::make_pair(XKB_KEY_XF86Documents, Qt::Key_Documents),
std::make_pair(XKB_KEY_XF86Excel, Qt::Key_Excel),
std::make_pair(XKB_KEY_XF86Explorer, Qt::Key_Explorer),
std::make_pair(XKB_KEY_XF86Game, Qt::Key_Game),
std::make_pair(XKB_KEY_XF86Go, Qt::Key_Go),
std::make_pair(XKB_KEY_XF86iTouch, Qt::Key_iTouch),
std::make_pair(XKB_KEY_XF86LogOff, Qt::Key_LogOff),
std::make_pair(XKB_KEY_XF86Market, Qt::Key_Market),
std::make_pair(XKB_KEY_XF86Meeting, Qt::Key_Meeting),
std::make_pair(XKB_KEY_XF86MenuKB, Qt::Key_MenuKB),
std::make_pair(XKB_KEY_XF86MenuPB, Qt::Key_MenuPB),
std::make_pair(XKB_KEY_XF86MySites, Qt::Key_MySites),
std::make_pair(XKB_KEY_XF86News, Qt::Key_News),
std::make_pair(XKB_KEY_XF86OfficeHome, Qt::Key_OfficeHome),
std::make_pair(XKB_KEY_XF86Option, Qt::Key_Option),
std::make_pair(XKB_KEY_XF86Paste, Qt::Key_Paste),
std::make_pair(XKB_KEY_XF86Phone, Qt::Key_Phone),
std::make_pair(XKB_KEY_XF86Calendar, Qt::Key_Calendar),
std::make_pair(XKB_KEY_XF86Reply, Qt::Key_Reply),
std::make_pair(XKB_KEY_XF86Reload, Qt::Key_Reload),
std::make_pair(XKB_KEY_XF86RotateWindows, Qt::Key_RotateWindows),
std::make_pair(XKB_KEY_XF86RotationPB, Qt::Key_RotationPB),
std::make_pair(XKB_KEY_XF86RotationKB, Qt::Key_RotationKB),
std::make_pair(XKB_KEY_XF86Save, Qt::Key_Save),
std::make_pair(XKB_KEY_XF86Send, Qt::Key_Send),
std::make_pair(XKB_KEY_XF86Spell, Qt::Key_Spell),
std::make_pair(XKB_KEY_XF86SplitScreen, Qt::Key_SplitScreen),
std::make_pair(XKB_KEY_XF86Support, Qt::Key_Support),
std::make_pair(XKB_KEY_XF86TaskPane, Qt::Key_TaskPane),
std::make_pair(XKB_KEY_XF86Terminal, Qt::Key_Terminal),
std::make_pair(XKB_KEY_XF86Tools, Qt::Key_Tools),
std::make_pair(XKB_KEY_XF86Travel, Qt::Key_Travel),
std::make_pair(XKB_KEY_XF86Video, Qt::Key_Video),
std::make_pair(XKB_KEY_XF86Word, Qt::Key_Word),
std::make_pair(XKB_KEY_XF86Xfer, Qt::Key_Xfer),
std::make_pair(XKB_KEY_XF86ZoomIn, Qt::Key_ZoomIn),
std::make_pair(XKB_KEY_XF86ZoomOut, Qt::Key_ZoomOut),
std::make_pair(XKB_KEY_XF86Away, Qt::Key_Away),
std::make_pair(XKB_KEY_XF86Messenger, Qt::Key_Messenger),
std::make_pair(XKB_KEY_XF86WebCam, Qt::Key_WebCam),
std::make_pair(XKB_KEY_XF86MailForward, Qt::Key_MailForward),
std::make_pair(XKB_KEY_XF86Pictures, Qt::Key_Pictures),
std::make_pair(XKB_KEY_XF86Music, Qt::Key_Music),
std::make_pair(XKB_KEY_XF86Battery, Qt::Key_Battery),
std::make_pair(XKB_KEY_XF86Bluetooth, Qt::Key_Bluetooth),
std::make_pair(XKB_KEY_XF86WLAN, Qt::Key_WLAN),
std::make_pair(XKB_KEY_XF86UWB, Qt::Key_UWB),
std::make_pair(XKB_KEY_XF86AudioForward, Qt::Key_AudioForward),
std::make_pair(XKB_KEY_XF86AudioRepeat, Qt::Key_AudioRepeat),
std::make_pair(XKB_KEY_XF86AudioRandomPlay, Qt::Key_AudioRandomPlay),
std::make_pair(XKB_KEY_XF86Subtitle, Qt::Key_Subtitle),
std::make_pair(XKB_KEY_XF86AudioCycleTrack, Qt::Key_AudioCycleTrack),
std::make_pair(XKB_KEY_XF86Time, Qt::Key_Time),
std::make_pair(XKB_KEY_XF86Hibernate, Qt::Key_Hibernate),
std::make_pair(XKB_KEY_XF86View, Qt::Key_View),
std::make_pair(XKB_KEY_XF86TopMenu, Qt::Key_TopMenu),
std::make_pair(XKB_KEY_XF86PowerDown, Qt::Key_PowerDown),
std::make_pair(XKB_KEY_XF86Suspend, Qt::Key_Suspend),
std::make_pair(XKB_KEY_XF86ContrastAdjust, Qt::Key_ContrastAdjust),
std::make_pair(XKB_KEY_XF86LaunchE, Qt::Key_LaunchG),
std::make_pair(XKB_KEY_XF86LaunchF, Qt::Key_LaunchH),
std::make_pair(XKB_KEY_XF86Select, Qt::Key_Select),
std::make_pair(XKB_KEY_Cancel, Qt::Key_Cancel),
std::make_pair(XKB_KEY_Execute, Qt::Key_Execute),
std::make_pair(XKB_KEY_XF86Sleep, Qt::Key_Sleep),
};
return keyTbl;
}
int keysymToQtKey(uint32_t key) {
auto iter = KeyTbl().find(key);
if (iter != KeyTbl().end()) {
return iter->second;
}
return 0;
}
int keysymToQtKey(uint32_t keysym, const QString &text) {
int code = 0;
if (keysym < 128) {
// upper-case key, if known
code = isprint((int)keysym) ? toupper((int)keysym) : 0;
} else if (text.length() == 1 && text.unicode()->unicode() > 0x1f &&
text.unicode()->unicode() != 0x7f &&
!(keysym >= XKB_KEY_dead_grave &&
keysym <= XKB_KEY_dead_currency)) {
code = text.unicode()->toUpper().unicode();
} else {
// any other keys
code = keysymToQtKey(keysym);
}
return code;
}

View File

@@ -0,0 +1,16 @@
/*
* SPDX-FileCopyrightText: 2017~2017 CSSlayer <wengxt@gmail.com>
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#ifndef _PLATFORMINPUTCONTEXT_QTKEY_H_
#define _PLATFORMINPUTCONTEXT_QTKEY_H_
#include <QString>
#include <cstdint>
int keysymToQtKey(uint32_t keysym, const QString &text);
#endif // _PLATFORMINPUTCONTEXT_QTKEY_H_