Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qibusplatforminputcontext.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
6
7#include <QDebug>
8#include <QTextCharFormat>
9#include <QGuiApplication>
10#include <QWindow>
11#include <QEvent>
12#include <QFile>
13#include <QFileInfo>
14#include <QStandardPaths>
15#include <QDBusVariant>
16#include <QDBusPendingReply>
17#include <QDBusReply>
18#include <QDBusServiceWatcher>
19
20#include "qibusproxy.h"
23#include "qibustypes.h"
24
25#include <qpa/qplatformcursor.h>
26#include <qpa/qplatformscreen.h>
27#include <qpa/qwindowsysteminterface_p.h>
28
29#include <private/qguiapplication_p.h>
30#include <private/qxkbcommon_p.h>
31
32#include <memory>
33
34#include <sys/types.h>
35#include <signal.h>
36
37
38#ifndef IBUS_RELEASE_MASK
39#define IBUS_RELEASE_MASK (1 << 30)
40#define IBUS_SHIFT_MASK (1 << 0)
41#define IBUS_CONTROL_MASK (1 << 2)
42#define IBUS_MOD1_MASK (1 << 3)
43#define IBUS_META_MASK (1 << 28)
44#endif
45
46QT_BEGIN_NAMESPACE
47
48using namespace Qt::StringLiterals;
49
50Q_LOGGING_CATEGORY(lcQpaInputMethods, "qt.qpa.input.methods");
51
53{
55public:
56 // This enum might be synced with IBusPreeditFocusMode
57 // in ibustypes.h of IBUS project
62
65 {
66 // dereference QDBusConnection to actually disconnect
67 serviceWatcher.setConnection(QDBusConnection(QString()));
68 context = nullptr;
69 portalBus = nullptr;
70 bus = nullptr;
71 QDBusConnection::disconnectFromBus("QIBusProxy"_L1);
72 }
73
75
77 void initBus();
79
81 std::unique_ptr<QIBusProxyPortal> portalBus; // bus and portalBus are alternative.
84
85 bool usePortal; // return value of shouldConnectIbusPortal
86 bool valid;
92 PreeditFocusMode preeditFocusMode = PREEDIT_COMMIT; // for backward compatibility
93};
94
95
96QIBusPlatformInputContext::QIBusPlatformInputContext ()
98{
99 if (!d->usePortal) {
100 QString socketPath = QIBusPlatformInputContextPrivate::getSocketPath();
101 QFile file(socketPath);
102 if (file.open(QFile::ReadOnly)) {
103#if QT_CONFIG(filesystemwatcher)
104 qCDebug(qtQpaInputMethods) << "socketWatcher.addPath" << socketPath;
105 // If KDE session save is used or restart ibus-daemon,
106 // the applications could run before ibus-daemon runs.
107 // We watch the getSocketPath() to get the launching ibus-daemon.
108 m_socketWatcher.addPath(socketPath);
109 connect(&m_socketWatcher, SIGNAL(fileChanged(QString)), this, SLOT(socketChanged(QString)));
110#endif
111 }
112 m_timer.setSingleShot(true);
113 connect(&m_timer, SIGNAL(timeout()), this, SLOT(connectToBus()));
114 }
115
116 QObject::connect(&d->serviceWatcher, SIGNAL(serviceRegistered(QString)), this, SLOT(busRegistered(QString)));
117 QObject::connect(&d->serviceWatcher, SIGNAL(serviceUnregistered(QString)), this, SLOT(busUnregistered(QString)));
118
119 connectToContextSignals();
120
121 QInputMethod *p = qApp->inputMethod();
122 connect(p, SIGNAL(cursorRectangleChanged()), this, SLOT(cursorRectChanged()));
123 m_eventFilterUseSynchronousMode = false;
124 if (qEnvironmentVariableIsSet("IBUS_ENABLE_SYNC_MODE")) {
125 bool ok;
126 int enableSync = qEnvironmentVariableIntValue("IBUS_ENABLE_SYNC_MODE", &ok);
127 if (ok && enableSync == 1)
128 m_eventFilterUseSynchronousMode = true;
129 }
130}
131
136
138{
139 return d->valid && d->busConnected;
140}
141
142bool QIBusPlatformInputContext::hasCapability(Capability capability) const
143{
144 switch (capability) {
145 case QPlatformInputContext::HiddenTextCapability:
146 return false; // QTBUG-40691, do not show IME on desktop for password entry fields.
147 default:
148 break;
149 }
150 return true;
151}
152
153void QIBusPlatformInputContext::invokeAction(QInputMethod::Action a, int)
154{
155 if (!d->busConnected)
156 return;
157
158 if (a == QInputMethod::Click)
159 commit();
160}
161
163{
164 if (!d->busConnected)
165 return;
166
167 d->context->Reset();
168 d->predit = QString();
169 d->attributes.clear();
170}
171
173{
174 if (!d->busConnected)
175 return;
176
177 QObject *input = qApp->focusObject();
178 if (!input) {
179 d->predit = QString();
180 d->attributes.clear();
181 return;
182 }
183
184 if (d->preeditFocusMode == QIBusPlatformInputContextPrivate::PREEDIT_COMMIT) {
185 if (!d->predit.isEmpty()) {
186 QInputMethodEvent event;
187 event.setCommitString(d->predit);
188 QCoreApplication::sendEvent(input, &event);
189 }
190 } else {
191 if (!d->predit.isEmpty()) {
192 // Clear the existing preedit
193 QInputMethodEvent event;
194 QCoreApplication::sendEvent(input, &event);
195 }
196 }
197
198 d->context->Reset();
199 d->predit = QString();
200 d->attributes.clear();
201}
202
203
204void QIBusPlatformInputContext::update(Qt::InputMethodQueries q)
205{
206 QObject *input = qApp->focusObject();
207
208 if (d->needsSurroundingText && input
209 && (q.testFlag(Qt::ImSurroundingText)
210 || q.testFlag(Qt::ImCursorPosition)
211 || q.testFlag(Qt::ImAnchorPosition))) {
212
213 QInputMethodQueryEvent query(Qt::ImSurroundingText | Qt::ImCursorPosition | Qt::ImAnchorPosition);
214
215 QCoreApplication::sendEvent(input, &query);
216
217 QString surroundingText = query.value(Qt::ImSurroundingText).toString();
218 uint cursorPosition = query.value(Qt::ImCursorPosition).toUInt();
219 uint anchorPosition = query.value(Qt::ImAnchorPosition).toUInt();
220
221 QIBusText text;
222 text.text = surroundingText;
223
224 QVariant variant;
225 variant.setValue(text);
226 QDBusVariant dbusText(variant);
227
228 d->context->SetSurroundingText(dbusText, cursorPosition, anchorPosition);
229 }
230}
231
233{
234 if (!d->busConnected)
235 return;
236
237 QRect r = qApp->inputMethod()->cursorRectangle().toRect();
238 if (!r.isValid())
239 return;
240
241 QWindow *inputWindow = qApp->focusWindow();
242 if (!inputWindow)
243 return;
244 if (!inputWindow->screen())
245 return;
246
247 if (QGuiApplication::platformName().startsWith("wayland"_L1)) {
248 auto margins = inputWindow->frameMargins();
249 r.translate(margins.left(), margins.top());
250 qreal scale = inputWindow->devicePixelRatio();
251 QRect newRect = QRect(r.x() * scale, r.y() * scale, r.width() * scale, r.height() * scale);
252 qCDebug(lcQpaInputMethods) << "microFocus" << newRect;
253 d->context->SetCursorLocationRelative(newRect.x(), newRect.y(),
254 newRect.width(), newRect.height());
255 return;
256 }
257
258 // x11/xcb
259 auto screenGeometry = inputWindow->screen()->geometry();
260 auto point = inputWindow->mapToGlobal(r.topLeft());
261 qreal scale = inputWindow->devicePixelRatio();
262 auto native = (point - screenGeometry.topLeft()) * scale + screenGeometry.topLeft();
263 QRect newRect(native, r.size() * scale);
264 qCDebug(lcQpaInputMethods) << "microFocus" << newRect;
265 d->context->SetCursorLocation(newRect.x(), newRect.y(),
266 newRect.width(), newRect.height());
267}
268
270{
271 if (!d->busConnected)
272 return;
273
274 // It would seem natural here to call FocusOut() on the input method if we
275 // transition from an IME accepted focus object to one that does not accept it.
276 // Mysteriously however that is not sufficient to fix bug QTBUG-63066.
277 if (object && !inputMethodAccepted())
278 return;
279
280 qCDebug(lcQpaInputMethods) << "setFocusObject" << object;
281 if (object)
282 d->context->FocusIn();
283 else
284 d->context->FocusOut();
285}
286
287void QIBusPlatformInputContext::commitText(const QDBusVariant &text)
288{
289 QObject *input = qApp->focusObject();
290 if (!input)
291 return;
292
293 const QDBusArgument arg = qvariant_cast<QDBusArgument>(text.variant());
294
295 QIBusText t;
296 qCDebug(lcQpaInputMethods) << arg.currentSignature();
297 arg >> t;
298 qCDebug(lcQpaInputMethods) << "commit text:" << t.text;
299
300 QInputMethodEvent event;
301 event.setCommitString(t.text);
302 QCoreApplication::sendEvent(input, &event);
303
304 d->predit = QString();
305 d->attributes.clear();
306}
307
308void QIBusPlatformInputContext::updatePreeditText(const QDBusVariant &text, uint cursorPos, bool visible)
309{
310 if (!qApp)
311 return;
312
313 QObject *input = qApp->focusObject();
314 if (!input)
315 return;
316
317 const QDBusArgument arg = qvariant_cast<QDBusArgument>(text.variant());
318
319 QIBusText t;
320 arg >> t;
321 qCDebug(lcQpaInputMethods) << "preedit text:" << t.text;
322
323 d->attributes = t.attributes.imAttributes();
324 if (!t.text.isEmpty())
325 d->attributes += QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, cursorPos, visible ? 1 : 0, QVariant());
326
327 QInputMethodEvent event(t.text, d->attributes);
328 QCoreApplication::sendEvent(input, &event);
329
330 d->predit = t.text;
331}
332
333void QIBusPlatformInputContext::updatePreeditTextWithMode(const QDBusVariant &text, uint cursorPos, bool visible, uint mode)
334{
335 updatePreeditText(text, cursorPos, visible);
336 if (mode > 0)
337 d->preeditFocusMode = QIBusPlatformInputContextPrivate::PreeditFocusMode::PREEDIT_COMMIT;
338 else
339 d->preeditFocusMode = QIBusPlatformInputContextPrivate::PreeditFocusMode::PREEDIT_CLEAR;
340}
341
342void QIBusPlatformInputContext::forwardKeyEvent(uint keyval, uint keycode, uint state)
343{
344 if (!qApp)
345 return;
346
347 QObject *input = qApp->focusObject();
348 if (!input)
349 return;
350
351 QEvent::Type type = QEvent::KeyPress;
352 if (state & IBUS_RELEASE_MASK)
353 type = QEvent::KeyRelease;
354
355 state &= ~IBUS_RELEASE_MASK;
356 keycode += 8;
357
358 Qt::KeyboardModifiers modifiers = Qt::NoModifier;
359 if (state & IBUS_SHIFT_MASK)
360 modifiers |= Qt::ShiftModifier;
361 if (state & IBUS_CONTROL_MASK)
362 modifiers |= Qt::ControlModifier;
363 if (state & IBUS_MOD1_MASK)
364 modifiers |= Qt::AltModifier;
365 if (state & IBUS_META_MASK)
366 modifiers |= Qt::MetaModifier;
367
368 int qtcode = QXkbCommon::keysymToQtKey(keyval, modifiers);
369 QString text = QXkbCommon::lookupStringNoKeysymTransformations(keyval);
370
371 qCDebug(lcQpaInputMethods) << "forwardKeyEvent" << keyval << keycode << state << modifiers << qtcode << text;
372
373 QKeyEvent event(type, qtcode, modifiers, keycode, keyval, state, text);
374 QCoreApplication::sendEvent(input, &event);
375}
376
378{
379 qCDebug(lcQpaInputMethods) << "surroundingTextRequired";
381 update(Qt::ImSurroundingText);
382}
383
384void QIBusPlatformInputContext::deleteSurroundingText(int offset, uint n_chars)
385{
386 QObject *input = qApp->focusObject();
387 if (!input)
388 return;
389
390 qCDebug(lcQpaInputMethods) << "deleteSurroundingText" << offset << n_chars;
391
392 QInputMethodEvent event;
393 event.setCommitString("", offset, n_chars);
394 QCoreApplication::sendEvent(input, &event);
395}
396
398{
399 QObject *input = QGuiApplication::focusObject();
400 if (!input)
401 return;
402
403 QList<QInputMethodEvent::Attribute> attributes;
404 QInputMethodEvent event(QString(), attributes);
405 QCoreApplication::sendEvent(input, &event);
406}
407
409{
410 QObject *input = QGuiApplication::focusObject();
411 if (!input)
412 return;
413
414 QInputMethodEvent event(d->predit, d->attributes);
415 QCoreApplication::sendEvent(input, &event);
416}
417
418bool QIBusPlatformInputContext::filterEvent(const QEvent *event)
419{
420 if (!d->busConnected)
421 return false;
422
423 if (!inputMethodAccepted())
424 return false;
425
426 const QKeyEvent *keyEvent = static_cast<const QKeyEvent *>(event);
427 quint32 sym = keyEvent->nativeVirtualKey();
428 quint32 code = keyEvent->nativeScanCode();
429 quint32 state = keyEvent->nativeModifiers();
430 quint32 ibusState = state;
431
432 if (keyEvent->type() != QEvent::KeyPress)
433 ibusState |= IBUS_RELEASE_MASK;
434
435 QDBusPendingReply<bool> reply = d->context->ProcessKeyEvent(sym, code - 8, ibusState);
436
437 if (m_eventFilterUseSynchronousMode || reply.isFinished()) {
438 bool filtered = reply.value();
439 qCDebug(qtQpaInputMethods) << "filterEvent return" << code << sym << state << filtered;
440 return filtered;
441 }
442
443 Qt::KeyboardModifiers modifiers = keyEvent->modifiers();
444 const int qtcode = keyEvent->key();
445
446 // From QKeyEvent::modifiers()
447 switch (qtcode) {
448 case Qt::Key_Shift:
449 modifiers ^= Qt::ShiftModifier;
450 break;
451 case Qt::Key_Control:
452 modifiers ^= Qt::ControlModifier;
453 break;
454 case Qt::Key_Alt:
455 modifiers ^= Qt::AltModifier;
456 break;
457 case Qt::Key_Meta:
458 modifiers ^= Qt::MetaModifier;
459 break;
460 case Qt::Key_AltGr:
461 modifiers ^= Qt::GroupSwitchModifier;
462 break;
463 }
464
465 QVariantList args;
466 args << QVariant::fromValue(keyEvent->timestamp());
467 args << QVariant::fromValue(static_cast<uint>(keyEvent->type()));
468 args << QVariant::fromValue(qtcode);
469 args << QVariant::fromValue(code) << QVariant::fromValue(sym) << QVariant::fromValue(state);
470 args << QVariant::fromValue(keyEvent->text());
471 args << QVariant::fromValue(keyEvent->isAutoRepeat());
472
473 QIBusFilterEventWatcher *watcher = new QIBusFilterEventWatcher(reply, this, QGuiApplication::focusWindow(), modifiers, args);
474 QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, &QIBusPlatformInputContext::filterEventFinished);
475
476 return true;
477}
478
479void QIBusPlatformInputContext::filterEventFinished(QDBusPendingCallWatcher *call)
480{
482 QDBusPendingReply<bool> reply = *call;
483
484 if (reply.isError()) {
485 call->deleteLater();
486 return;
487 }
488
489 // Use watcher's window instead of the current focused window
490 // since there is a time lag until filterEventFinished() returns.
491 QWindow *window = watcher->window();
492
493 if (!window) {
494 call->deleteLater();
495 return;
496 }
497
498 Qt::KeyboardModifiers modifiers = watcher->modifiers();
499 QVariantList args = watcher->arguments();
500 const ulong time = static_cast<ulong>(args.at(0).toUInt());
501 const QEvent::Type type = static_cast<QEvent::Type>(args.at(1).toUInt());
502 const int qtcode = args.at(2).toInt();
503 const quint32 code = args.at(3).toUInt();
504 const quint32 sym = args.at(4).toUInt();
505 const quint32 state = args.at(5).toUInt();
506 const QString string = args.at(6).toString();
507 const bool isAutoRepeat = args.at(7).toBool();
508
509 // copied from QXcbKeyboard::handleKeyEvent()
510 bool filtered = reply.value();
511 qCDebug(qtQpaInputMethods) << "filterEventFinished return" << code << sym << state << filtered;
512 if (!filtered) {
513#ifndef QT_NO_CONTEXTMENU
514 if (type == QEvent::KeyPress && qtcode == Qt::Key_Menu
515 && window != nullptr) {
516 const QPoint globalPos = window->screen()->handle()->cursor()->pos();
517 const QPoint pos = window->mapFromGlobal(globalPos);
518 QWindowSystemInterfacePrivate::ContextMenuEvent contextMenuEvent(window, false, pos,
519 globalPos, modifiers);
520 QGuiApplicationPrivate::processWindowSystemEvent(&contextMenuEvent);
521 }
522#endif
523 QWindowSystemInterfacePrivate::KeyEvent keyEvent(window, time, type, qtcode, modifiers,
524 code, sym, state, string, isAutoRepeat);
525 QGuiApplicationPrivate::processWindowSystemEvent(&keyEvent);
526 }
527 call->deleteLater();
528}
529
531{
532 // d->locale is not updated when IBus portal is used
533 if (d->usePortal)
534 return QPlatformInputContext::locale();
535 return d->locale;
536}
537
539{
540 qCDebug(qtQpaInputMethods) << "socketChanged";
541 Q_UNUSED (str);
542
543 m_timer.stop();
544
545 // dereference QDBusConnection to actually disconnect
546 d->serviceWatcher.setConnection(QDBusConnection(QString()));
547 d->context = nullptr;
548 d->bus = nullptr;
549 d->busConnected = false;
550 QDBusConnection::disconnectFromBus("QIBusProxy"_L1);
551
552 m_timer.start(100);
553}
554
556{
557 qCDebug(qtQpaInputMethods) << "busRegistered";
558 Q_UNUSED (str);
559 if (d->usePortal) {
561 }
562}
563
565{
566 qCDebug(qtQpaInputMethods) << "busUnregistered";
567 Q_UNUSED (str);
568 d->busConnected = false;
569}
570
571// When getSocketPath() is modified, the bus is not established yet
572// so use m_timer.
574{
575 qCDebug(qtQpaInputMethods) << "QIBusPlatformInputContext::connectToBus";
577 connectToContextSignals();
578
579#if QT_CONFIG(filesystemwatcher)
580 if (!d->usePortal && m_socketWatcher.files().size() == 0)
581 m_socketWatcher.addPath(QIBusPlatformInputContextPrivate::getSocketPath());
582#endif
583}
584
585void QIBusPlatformInputContext::globalEngineChanged(const QString &engine_name)
586{
587 if (!d->bus || !d->bus->isValid())
588 return;
589
590 QIBusEngineDesc desc = d->bus->getGlobalEngine();
591 Q_ASSERT(engine_name == desc.engine_name);
592 QLocale locale(desc.language);
593 if (d->locale != locale) {
594 d->locale = locale;
595 emitLocaleChanged();
596 }
597}
598
599void QIBusPlatformInputContext::connectToContextSignals()
600{
601 if (d->bus && d->bus->isValid()) {
602 connect(d->bus.get(), SIGNAL(GlobalEngineChanged(QString)), this, SLOT(globalEngineChanged(QString)));
603 }
604
605 if (d->context) {
606 connect(d->context.get(), SIGNAL(CommitText(QDBusVariant)), SLOT(commitText(QDBusVariant)));
607 connect(d->context.get(), SIGNAL(UpdatePreeditText(QDBusVariant,uint,bool)), this, SLOT(updatePreeditText(QDBusVariant,uint,bool)));
608 connect(d->context.get(), SIGNAL(UpdatePreeditTextWithMode(QDBusVariant,uint,bool,uint)), this, SLOT(updatePreeditTextWithMode(QDBusVariant,uint,bool,uint)));
609 connect(d->context.get(), SIGNAL(ForwardKeyEvent(uint,uint,uint)), this, SLOT(forwardKeyEvent(uint,uint,uint)));
610 connect(d->context.get(), SIGNAL(DeleteSurroundingText(int,uint)), this, SLOT(deleteSurroundingText(int,uint)));
611 connect(d->context.get(), SIGNAL(RequireSurroundingText()), this, SLOT(surroundingTextRequired()));
612 connect(d->context.get(), SIGNAL(HidePreeditText()), this, SLOT(hidePreeditText()));
613 connect(d->context.get(), SIGNAL(ShowPreeditText()), this, SLOT(showPreeditText()));
614 }
615}
616
617static inline bool checkNeedPortalSupport()
618{
619 return QFileInfo::exists("/.flatpak-info"_L1) || qEnvironmentVariableIsSet("SNAP");
620}
621
623{
624 // honor the same env as ibus-gtk
625 return (checkNeedPortalSupport() || qEnvironmentVariableIsSet("IBUS_USE_PORTAL"));
626}
627
630 valid(false),
631 busConnected(false),
633{
634 if (usePortal) {
635 valid = true;
636 qCDebug(lcQpaInputMethods) << "use IBus portal";
637 } else {
638 valid = !QStandardPaths::findExecutable(QString::fromLocal8Bit("ibus-daemon"), QStringList()).isEmpty();
639 }
640 if (!valid)
641 return;
642 initBus();
643
644 if (bus && bus->isValid()) {
645 QIBusEngineDesc desc = bus->getGlobalEngine();
646 locale = QLocale(desc.language);
647 }
648}
649
656
658{
659 QDBusConnection connection("QIBusProxy"_L1);
660 if (!connection.isConnected())
661 return;
662
663 const char* ibusService = usePortal ? "org.freedesktop.portal.IBus" : "org.freedesktop.IBus";
664 QDBusReply<QDBusObjectPath> ic;
665 if (usePortal) {
666 portalBus = std::make_unique<QIBusProxyPortal>(QLatin1StringView(ibusService),
667 "/org/freedesktop/IBus"_L1,
668 connection);
669 if (!portalBus->isValid()) {
670 qWarning("QIBusPlatformInputContext: invalid portal bus.");
671 return;
672 }
673
674 ic = portalBus->CreateInputContext("QIBusInputContext"_L1);
675 } else {
676 bus = std::make_unique<QIBusProxy>(QLatin1StringView(ibusService),
677 "/org/freedesktop/IBus"_L1,
678 connection);
679 if (!bus->isValid()) {
680 qWarning("QIBusPlatformInputContext: invalid bus.");
681 return;
682 }
683
684 ic = bus->CreateInputContext("QIBusInputContext"_L1);
685 }
686
687 serviceWatcher.removeWatchedService(ibusService);
688 serviceWatcher.setConnection(connection);
689 serviceWatcher.addWatchedService(ibusService);
690
691 if (!ic.isValid()) {
692 qWarning("QIBusPlatformInputContext: CreateInputContext failed.");
693 return;
694 }
695
696 context = std::make_unique<QIBusInputContextProxy>(QLatin1StringView(ibusService), ic.value().path(), connection);
697
698 if (!context->isValid()) {
699 qWarning("QIBusPlatformInputContext: invalid input context.");
700 return;
701 }
702
703 enum Capabilities {
704 IBUS_CAP_PREEDIT_TEXT = 1 << 0,
705 IBUS_CAP_AUXILIARY_TEXT = 1 << 1,
706 IBUS_CAP_LOOKUP_TABLE = 1 << 2,
707 IBUS_CAP_FOCUS = 1 << 3,
708 IBUS_CAP_PROPERTY = 1 << 4,
709 IBUS_CAP_SURROUNDING_TEXT = 1 << 5
710 };
711 context->SetCapabilities(IBUS_CAP_PREEDIT_TEXT|IBUS_CAP_FOCUS|IBUS_CAP_SURROUNDING_TEXT);
712
713 context->setClientCommitPreedit(QIBusPropTypeClientCommitPreedit(true));
714
715 qCDebug(lcQpaInputMethods) << ">>>> bus connected!";
716 busConnected = true;
717}
718
719QString QIBusPlatformInputContextPrivate::getSocketPath()
720{
721 QByteArray display;
722 QByteArray displayNumber = "0";
723 bool isWayland = false;
724
725 if (QString path = qEnvironmentVariable("IBUS_ADDRESS_FILE"); !path.isNull()) {
726 return path;
727 } else if (display = qgetenv("WAYLAND_DISPLAY"); !display.isEmpty()) {
728 isWayland = true;
729 } else {
730 display = qgetenv("DISPLAY");
731 }
732 QByteArray host = "unix";
733
734 if (isWayland) {
735 displayNumber = display;
736 } else {
737 int pos = display.indexOf(':');
738 if (pos > 0)
739 host = display.left(pos);
740 ++pos;
741 int pos2 = display.indexOf('.', pos);
742 if (pos2 > 0)
743 displayNumber = display.mid(pos, pos2 - pos);
744 else
745 displayNumber = display.mid(pos);
746 }
747
748 qCDebug(lcQpaInputMethods) << "host=" << host << "displayNumber" << displayNumber;
749
750 return QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) +
751 "/ibus/bus/"_L1 +
752 QLatin1StringView(QDBusConnection::localMachineId()) +
753 u'-' + QString::fromLocal8Bit(host) + u'-' + QString::fromLocal8Bit(displayNumber);
754}
755
757{
758 if (usePortal) {
759 QDBusConnection::connectToBus(QDBusConnection::SessionBus, "QIBusProxy"_L1);
760 return;
761 }
762
763 QFile file(getSocketPath());
764 if (!file.open(QFile::ReadOnly))
765 return;
766
767 QByteArray address;
768 int pid = -1;
769 QByteArray lineArray;
770
771 while (file.readLineInto(&lineArray)) {
772 QByteArrayView line = QByteArrayView(lineArray).trimmed();
773 if (line.startsWith('#'))
774 continue;
775
776 if (line.startsWith("IBUS_ADDRESS="))
777 address = line.mid(sizeof("IBUS_ADDRESS=") - 1).toByteArray();
778 if (line.startsWith("IBUS_DAEMON_PID="))
779 pid = line.mid(sizeof("IBUS_DAEMON_PID=") - 1).toInt();
780 }
781
782 qCDebug(lcQpaInputMethods) << "IBUS_ADDRESS=" << address << "PID=" << pid;
783 if (address.isEmpty() || pid < 0 || kill(pid, 0) != 0)
784 return;
785
786 QDBusConnection::connectToBus(QString::fromLatin1(address), "QIBusProxy"_L1);
787}
788
789QT_END_NAMESPACE
790
791#include "moc_qibusplatforminputcontext.cpp"
\inmodule QtDBus
std::unique_ptr< QIBusInputContextProxy > context
QList< QInputMethodEvent::Attribute > attributes
std::unique_ptr< QIBusProxyPortal > portalBus
void invokeAction(QInputMethod::Action a, int x) override
Called when the word currently being composed in the input item is tapped by the user.
void busUnregistered(const QString &str)
bool filterEvent(const QEvent *event) override
This function can be reimplemented to filter input events.
void globalEngineChanged(const QString &engine_name)
void filterEventFinished(QDBusPendingCallWatcher *call)
void update(Qt::InputMethodQueries) override
Notification on editor updates.
bool hasCapability(Capability capability) const override
Returns whether the implementation supports capability.
void socketChanged(const QString &str)
bool isValid() const override
Returns input context validity.
void setFocusObject(QObject *object) override
This virtual method gets called to notify updated focus to object.
void reset() override
Method to be called when input method needs to be reset.
void busRegistered(const QString &str)
The QInputMethodEvent class provides parameters for input method events.
Definition qevent.h:628
The QInputMethodQueryEvent class provides an event sent by the input context to input objects.
Definition qevent.h:683
The QKeyEvent class describes a key event.
Definition qevent.h:425
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")
#define IBUS_MOD1_MASK
#define IBUS_SHIFT_MASK
#define IBUS_META_MASK
static bool shouldConnectIbusPortal()
static bool checkNeedPortalSupport()
#define IBUS_CONTROL_MASK
#define IBUS_RELEASE_MASK