#include "DeadKeys.h" #include "layout/KeymapKeys.h" #include #include namespace { QByteArray systemLocale() { for (const char *name : {"LC_ALL", "LC_CTYPE", "LANG"}) { if (const QByteArray value = qgetenv(name); !value.isEmpty()) return value; } return "C"; } } // namespace DeadKeys::DeadKeys(const QString &locale) : m_context(xkb_context_new(XKB_CONTEXT_NO_FLAGS), xkb_context_unref) , m_table(nullptr, xkb_compose_table_unref) , m_state(nullptr, xkb_compose_state_unref) { // The C locale has no compose table; en_US.UTF-8's is the common base of all. const QByteArray name = locale.isEmpty() ? systemLocale() : locale.toUtf8(); for (const QByteArray &candidate : {name, QByteArrayLiteral("en_US.UTF-8")}) { m_table.reset(xkb_compose_table_new_from_locale(m_context.get(), candidate.constData(), XKB_COMPOSE_COMPILE_NO_FLAGS)); if (m_table) break; } if (m_table) m_state.reset(xkb_compose_state_new(m_table.get(), XKB_COMPOSE_STATE_NO_FLAGS)); } DeadKeys::~DeadKeys() = default; QString DeadKeys::press(quint32 deadKeysym) { if (!m_state) { m_pending += deadKeyText(deadKeysym); return {}; } xkb_compose_state_feed(m_state.get(), deadKeysym); switch (xkb_compose_state_get_status(m_state.get())) { case XKB_COMPOSE_COMPOSED: { const QString composed = composedText(); cancel(); return composed; } case XKB_COMPOSE_CANCELLED: { // No compose sequence continues with this key: flush the pending accents // and start a new sequence. const QString accents = m_pending; cancel(); return accents + press(deadKeysym); } default: m_pending += deadKeyText(deadKeysym); return {}; } } QString DeadKeys::composedText() const { char utf8[64]; xkb_compose_state_get_utf8(m_state.get(), utf8, sizeof utf8); return QString::fromUtf8(utf8); } QString DeadKeys::compose(const QString &text) { QString result = m_pending + text; if (m_state && !text.isEmpty()) { const char32_t ucs = text.toUcs4().first(); xkb_compose_state_feed(m_state.get(), xkb_utf32_to_keysym(ucs)); if (xkb_compose_state_get_status(m_state.get()) == XKB_COMPOSE_COMPOSED) result = composedText() + text.mid(QString::fromUcs4(&ucs, 1).size()); } cancel(); return result; } void DeadKeys::cancel() { m_pending.clear(); if (m_state) xkb_compose_state_reset(m_state.get()); }