[effects/zoom] Implement focus tracking with QAccessibilityClient

Currently, the focus tracking functionality in the zoom effect does not
work because it relies on kaccessibleapp, which is dead. Luckily for us,
there is a library called libqaccessibilityclient that provides a way
to monitor focus changes.

BUG: 421234
master
Vlad Zahorodnii 4 years ago
parent 7226e75bc8
commit c1ea0412a4

@ -300,6 +300,15 @@ set_package_properties(hwdata PROPERTIES
URL "https://github.com/vcrhonek/hwdata"
)
find_package(QAccessibilityClient CONFIG)
set_package_properties(QAccessibilityClient PROPERTIES
URL "https://www.kde.org"
DESCRIPTION "KDE client-side accessibility library"
TYPE OPTIONAL
PURPOSE "Required to enable accessibility features"
)
set(HAVE_ACCESSIBILITY ${QAccessibilityClient_FOUND})
include(ECMQMLModules)
ecm_find_qmlmodule(QtQuick 2.3)
ecm_find_qmlmodule(QtQuick.Controls 1.2)

@ -25,6 +25,7 @@
#cmakedefine01 HAVE_BREEZE_DECO
#cmakedefine01 HAVE_LIBCAP
#cmakedefine01 HAVE_SCHED_RESET_ON_FORK
#cmakedefine01 HAVE_ACCESSIBILITY
#if HAVE_BREEZE_DECO
#define BREEZE_KDECORATION_PLUGIN_ID "${BREEZE_KDECORATION_PLUGIN_ID}"
#endif

@ -3,6 +3,10 @@ add_definitions(-DTRANSLATION_DOMAIN=\"kwin_effects\" -DEFFECT_BUILTINS)
include_directories(${KWin_SOURCE_DIR}) # for xcbutils.h
if (HAVE_ACCESSIBILITY)
include_directories(${QACCESSIBILITYCLIENT_INCLUDE_DIR})
endif()
set(kwin_effect_OWN_LIBS
kwineffects
)
@ -21,6 +25,10 @@ set(kwin_effect_KDE_LIBS
KF5::WindowSystem
)
if (HAVE_ACCESSIBILITY)
set(kwin_effect_KDE_LIBS ${kwin_effect_KDE_LIBS} ${QACCESSIBILITYCLIENT_LIBRARY})
endif()
set(kwin_effect_QT_LIBS
Qt5::Concurrent
Qt5::DBus
@ -104,6 +112,13 @@ set(kwin4_effect_builtins_sources
zoom/zoom.cpp
)
if (HAVE_ACCESSIBILITY)
set(kwin4_effect_builtins_sources
zoom/accessibilityintegration.cpp
${kwin4_effect_builtins_sources}
)
endif()
qt5_add_resources(kwin4_effect_builtins_sources shaders.qrc)
kconfig_add_kcfg_files(kwin4_effect_builtins_sources

@ -0,0 +1,91 @@
/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#include "accessibilityintegration.h"
using namespace QAccessibleClient; // Whatever, sue me...
namespace KWin
{
ZoomAccessibilityIntegration::ZoomAccessibilityIntegration(QObject *parent)
: QObject(parent)
{
}
void ZoomAccessibilityIntegration::setFocusTrackingEnabled(bool enabled)
{
if (m_isFocusTrackingEnabled == enabled) {
return;
}
m_isFocusTrackingEnabled = enabled;
updateAccessibilityRegistry();
}
bool ZoomAccessibilityIntegration::isFocusTrackingEnabled() const
{
return m_isFocusTrackingEnabled;
}
void ZoomAccessibilityIntegration::updateAccessibilityRegistry()
{
Registry::EventListeners eventListeners = Registry::NoEventListeners;
if (isFocusTrackingEnabled()) {
eventListeners |= Registry::Focus;
}
if (eventListeners == Registry::NoEventListeners) {
destroyAccessibilityRegistry();
return;
}
if (!m_accessibilityRegistry) {
createAccessibilityRegistry();
}
m_accessibilityRegistry->subscribeEventListeners(eventListeners);
}
void ZoomAccessibilityIntegration::createAccessibilityRegistry()
{
m_accessibilityRegistry = new Registry(this);
connect(m_accessibilityRegistry, &Registry::focusChanged,
this, &ZoomAccessibilityIntegration::slotFocusChanged);
}
void ZoomAccessibilityIntegration::destroyAccessibilityRegistry()
{
if (!m_accessibilityRegistry) {
return;
}
disconnect(m_accessibilityRegistry, nullptr, this, nullptr);
m_accessibilityRegistry->deleteLater();
m_accessibilityRegistry = nullptr;
}
void ZoomAccessibilityIntegration::slotFocusChanged(const AccessibleObject &object)
{
emit focusPointChanged(object.focusPoint());
}
} // namespace KWin

@ -0,0 +1,53 @@
/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#pragma once
#include <qaccessibilityclient/registry.h>
namespace KWin
{
class ZoomAccessibilityIntegration : public QObject
{
Q_OBJECT
public:
explicit ZoomAccessibilityIntegration(QObject *parent = nullptr);
void setFocusTrackingEnabled(bool enabled);
bool isFocusTrackingEnabled() const;
Q_SIGNALS:
void focusPointChanged(const QPoint &point);
private Q_SLOTS:
void slotFocusChanged(const QAccessibleClient::AccessibleObject &object);
private:
void createAccessibilityRegistry();
void destroyAccessibilityRegistry();
void updateAccessibilityRegistry();
QAccessibleClient::Registry *m_accessibilityRegistry = nullptr;
bool m_isFocusTrackingEnabled = false;
};
} // namespace KWin

@ -23,11 +23,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// KConfigSkeleton
#include "zoomconfig.h"
#if HAVE_ACCESSIBILITY
#include "accessibilityintegration.h"
#endif
#include <QAction>
#include <QApplication>
#include <QStyle>
#include <QVector2D>
#include <QDBusConnection>
#include <kstandardaction.h>
#include <KConfigGroup>
#include <KGlobalAccel>
@ -49,8 +52,6 @@ ZoomEffect::ZoomEffect()
, polling(false)
, zoomFactor(1.25)
, mouseTracking(MouseTrackingProportional)
, enableFocusTracking(false)
, followFocus(true)
, mousePointer(MousePointerScale)
, focusDelay(350) // in milliseconds
, imageWidth(0)
@ -133,6 +134,11 @@ ZoomEffect::ZoomEffect()
connect(&timeline, &QTimeLine::frameChanged, this, &ZoomEffect::timelineFrameChanged);
connect(effects, &EffectsHandler::mouseChanged, this, &ZoomEffect::slotMouseChanged);
#if HAVE_ACCESSIBILITY
m_accessibilityIntegration = new ZoomAccessibilityIntegration(this);
connect(m_accessibilityIntegration, &ZoomAccessibilityIntegration::focusPointChanged, this, &ZoomEffect::moveFocus);
#endif
source_zoom = -1; // used to trigger initialZoom reading
reconfigure(ReconfigureAll);
}
@ -146,6 +152,15 @@ ZoomEffect::~ZoomEffect()
ZoomConfig::self()->save();
}
bool ZoomEffect::isFocusTrackingEnabled() const
{
#if HAVE_ACCESSIBILITY
return m_accessibilityIntegration->isFocusTrackingEnabled();
#else
return false;
#endif
}
void ZoomEffect::showCursor()
{
if (isMouseHidden) {
@ -215,27 +230,10 @@ void ZoomEffect::reconfigure(ReconfigureFlags)
mousePointer = MousePointerType(ZoomConfig::mousePointer());
// Track moving of the mouse.
mouseTracking = MouseTrackingType(ZoomConfig::mouseTracking());
#if HAVE_ACCESSIBILITY
// Enable tracking of the focused location.
bool _enableFocusTracking = ZoomConfig::enableFocusTracking();
if (enableFocusTracking != _enableFocusTracking) {
enableFocusTracking = _enableFocusTracking;
if (QDBusConnection::sessionBus().isConnected()) {
if (enableFocusTracking)
QDBusConnection::sessionBus().connect(QStringLiteral("org.kde.kaccessibleapp"),
QStringLiteral("/Adaptor"),
QStringLiteral("org.kde.kaccessibleapp.Adaptor"),
QStringLiteral("focusChanged"),
this, SLOT(focusChanged(int,int,int,int,int,int)));
else
QDBusConnection::sessionBus().disconnect(QStringLiteral("org.kde.kaccessibleapp"),
QStringLiteral("/Adaptor"),
QStringLiteral("org.kde.kaccessibleapp.Adaptor"),
QStringLiteral("focusChanged"),
this, SLOT(focusChanged(int,int,int,int,int,int)));
}
}
// When the focus changes, move the zoom area to the focused location.
followFocus = ZoomConfig::enableFollowFocus();
m_accessibilityIntegration->setFocusTrackingEnabled(ZoomConfig::enableFocusTracking());
#endif
// The time in milliseconds to wait before a focus-event takes away a mouse-move.
focusDelay = qMax(uint(0), ZoomConfig::focusDelay());
// The factor the zoom-area will be moved on touching an edge on push-mode or using the navigation KAction's.
@ -316,7 +314,7 @@ void ZoomEffect::paintScreen(int mask, const QRegion &region, ScreenPaintData& d
}
// use the focusPoint if focus tracking is enabled
if (enableFocusTracking && followFocus) {
if (isFocusTrackingEnabled()) {
bool acceptFocus = true;
if (mouseTracking != MouseTrackingDisabled && focusDelay > 0) {
// Wait some time for the mouse before doing the switch. This serves as threshold
@ -513,16 +511,13 @@ void ZoomEffect::slotMouseChanged(const QPoint& pos, const QPoint& old, Qt::Mous
}
}
void ZoomEffect::focusChanged(int px, int py, int rx, int ry, int rwidth, int rheight)
void ZoomEffect::moveFocus(const QPoint &point)
{
if (zoom == 1.0)
return;
const QSize screenSize = effects->virtualScreenSize();
focusPoint = (px >= 0 && py >= 0) ? QPoint(px, py) : QPoint(rx + qMax(0, (qMin(screenSize.width(), rwidth) / 2) - 60), ry + qMax(0, (qMin(screenSize.height(), rheight) / 2) - 60));
if (enableFocusTracking) {
lastFocusEvent = QTime::currentTime();
effects->addRepaintFull();
}
focusPoint = point;
lastFocusEvent = QTime::currentTime();
effects->addRepaintFull();
}
bool ZoomEffect::isActive() const

@ -22,6 +22,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef KWIN_ZOOM_H
#define KWIN_ZOOM_H
#include <config-kwin.h>
#include <kwineffects.h>
#include <QTime>
#include <QTimeLine>
@ -29,6 +31,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
namespace KWin
{
#if HAVE_ACCESSIBILITY
class ZoomAccessibilityIntegration;
#endif
class GLTexture;
class XRenderPicture;
@ -39,8 +45,7 @@ class ZoomEffect
Q_PROPERTY(qreal zoomFactor READ configuredZoomFactor)
Q_PROPERTY(int mousePointer READ configuredMousePointer)
Q_PROPERTY(int mouseTracking READ configuredMouseTracking)
Q_PROPERTY(bool enableFocusTracking READ isEnableFocusTracking)
Q_PROPERTY(bool followFocus READ isFollowFocus)
Q_PROPERTY(bool focusTrackingEnabled READ isFocusTrackingEnabled)
Q_PROPERTY(int focusDelay READ configuredFocusDelay)
Q_PROPERTY(qreal moveFactor READ configuredMoveFactor)
Q_PROPERTY(qreal targetZoom READ targetZoom)
@ -62,12 +67,7 @@ public:
int configuredMouseTracking() const {
return mouseTracking;
}
bool isEnableFocusTracking() const {
return enableFocusTracking;
}
bool isFollowFocus() const {
return followFocus;
}
bool isFocusTrackingEnabled() const;
int configuredFocusDelay() const {
return focusDelay;
}
@ -89,7 +89,7 @@ private Q_SLOTS:
void moveMouseToFocus();
void moveMouseToCenter();
void timelineFrameChanged(int frame);
void focusChanged(int px, int py, int rx, int ry, int rwidth, int rheight);
void moveFocus(const QPoint &point);
void slotMouseChanged(const QPoint& pos, const QPoint& old,
Qt::MouseButtons buttons, Qt::MouseButtons oldbuttons,
Qt::KeyboardModifiers modifiers, Qt::KeyboardModifiers oldmodifiers);
@ -99,6 +99,9 @@ private:
void hideCursor();
void moveZoom(int x, int y);
private:
#if HAVE_ACCESSIBILITY
ZoomAccessibilityIntegration *m_accessibilityIntegration = nullptr;
#endif
double zoom;
double target_zoom;
double source_zoom;
@ -106,8 +109,6 @@ private:
double zoomFactor;
enum MouseTrackingType { MouseTrackingProportional = 0, MouseTrackingCentred = 1, MouseTrackingPush = 2, MouseTrackingDisabled = 3 };
MouseTrackingType mouseTracking;
bool enableFocusTracking;
bool followFocus;
enum MousePointerType { MousePointerScale = 0, MousePointerKeep = 1, MousePointerHide = 2 };
MousePointerType mousePointer;
int focusDelay;

@ -17,8 +17,6 @@
<entry name="EnableFocusTracking" type="Bool">
<default>false</default>
</entry>
<entry name="EnableFollowFocus" type="Bool">
<default>true</default>
</entry>
<entry name="FocusDelay" type="UInt">
<default>350</default>

@ -60,6 +60,10 @@ ZoomEffectConfig::ZoomEffectConfig(QWidget* parent, const QVariantList& args) :
connect(m_ui->editor, &KShortcutsEditor::keyChange, this, &ZoomEffectConfig::markAsChanged);
#if !HAVE_ACCESSIBILITY
m_ui->kcfg_EnableFocusTracking->setVisible(false);
#endif
// Shortcut config. The shortcut belongs to the component "kwin"!
KActionCollection *actionCollection = new KActionCollection(this, QStringLiteral("kwin"));
actionCollection->setComponentDisplayName(i18n("KWin"));

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>304</width>
<height>212</height>
<height>288</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
@ -63,26 +63,13 @@
<string/>
</property>
<property name="whatsThis">
<string>Enable tracking of the focused location. This needs QAccessible to be enabled per application (&quot;export QT_ACCESSIBILITY=1&quot;).</string>
<string>Enable tracking of the focused location. This needs QAccessible to be enabled per application (&quot;export QT_LINUX_ACCESSIBILITY_ALWAYS_ON=1&quot;).</string>
</property>
<property name="text">
<string>Enable Focus Tracking</string>
</property>
</widget>
</item>
<item row="5" column="0" colspan="2">
<widget class="QCheckBox" name="kcfg_EnableFollowFocus">
<property name="enabled">
<bool>false</bool>
</property>
<property name="whatsThis">
<string>When the focus changes, move the zoom area to the focused location.</string>
</property>
<property name="text">
<string>Follow Focus</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
@ -164,16 +151,13 @@
</widget>
</item>
<item>
<widget class="KShortcutsEditor" name="editor" native="true">
<widget class="KShortcutsEditor" name="editor">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="actionTypes">
<enum>KShortcutsEditor::GlobalAction</enum>
</property>
</widget>
</item>
</layout>
@ -182,7 +166,7 @@
<customwidget>
<class>KShortcutsEditor</class>
<extends>QWidget</extends>
<header location="global">KShortcutsEditor</header>
<header>kshortcutseditor.h</header>
<container>1</container>
</customwidget>
</customwidgets>
@ -191,25 +175,7 @@
<tabstop>kcfg_MousePointer</tabstop>
<tabstop>kcfg_MouseTracking</tabstop>
<tabstop>kcfg_EnableFocusTracking</tabstop>
<tabstop>kcfg_EnableFollowFocus</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>kcfg_EnableFocusTracking</sender>
<signal>toggled(bool)</signal>
<receiver>kcfg_EnableFollowFocus</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>152</x>
<y>73</y>
</hint>
<hint type="destinationlabel">
<x>152</x>
<y>98</y>
</hint>
</hints>
</connection>
</connections>
<connections/>
</ui>

Loading…
Cancel
Save