You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

282 lines
7.2 KiB

/*****************************************************************************
* Copyright (C) 2023 the VideoLAN team
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#include <QWindow>
#include <QGuiApplication>
#include <QQuickItem>
#if !defined(QT_NO_ACCESSIBILITY) && defined(QT5_DECLARATIVE_PRIVATE)
#include <QAccessibleObject>
#include <private/qquickitem_p.h>
#include "compositor_accessibility.hpp"
#include "compositor.hpp"
#ifdef QT5_HAS_X11_COMPOSITOR
# include "compositor_x11_renderwindow.hpp"
#endif
#ifdef HAVE_DCOMP_H
# include "compositor_dcomp_uisurface.hpp"
#endif
namespace vlc {
/*
* we could have use QAccessibleQuickWindow like in QAccessibleQuickWidget
* but QAccessibleQuickWindow is not publicly exposed in the library, so we
* mimic the behavior of QAccessibleQuickWindow directly
*/
static void unignoredChildren(QQuickItem *item, QList<QQuickItem *> *items)
{
const QList<QQuickItem*> childItems = item->childItems();
for (QQuickItem *child : childItems)
{
if (QQuickItemPrivate::get(child)->isAccessible)
items->append(child);
else
unignoredChildren(child, items);
}
}
static QList<QQuickItem *> accessibleUnignoredChildren(QQuickItem *item)
{
QList<QQuickItem *> items;
unignoredChildren(item, &items);
return items;
}
class QAccessibleRenderWindow: public QAccessibleObject
{
public:
QAccessibleRenderWindow(QWindow* window, AccessibleRenderWindow* renderWindow)
: QAccessibleObject(window)
, m_window(renderWindow->getOffscreenWindow())
{
}
QAccessibleInterface* parent() const override
{
// we assume to be a top level window...
return QAccessible::queryAccessibleInterface(qApp);
}
QList<QQuickItem *> rootItems() const
{
if (QQuickItem *ci = m_window->contentItem())
return accessibleUnignoredChildren(ci);
return QList<QQuickItem *>();
}
QAccessibleInterface* child(int index) const override
{
const QList<QQuickItem*> &kids = rootItems();
if (index >= 0 && index < kids.count())
return QAccessible::queryAccessibleInterface(kids.at(index));
return nullptr;
}
int childCount() const override
{
return rootItems().count();
}
int indexOfChild(const QAccessibleInterface *iface) const override
{
int i = -1;
if (iface)
{
const QList<QQuickItem *> &roots = rootItems();
i = roots.count() - 1;
while (i >= 0)
{
if (iface->object() == roots.at(i))
break;
--i;
}
}
return i;
}
QAccessibleInterface *childAt(int x, int y) const override
{
for (int i = childCount() - 1; i >= 0; --i)
{
QAccessibleInterface *childIface = child(i);
if (childIface && !childIface->state().invisible)
{
if (QAccessibleInterface *iface = childIface->childAt(x, y))
return iface;
if (childIface->rect().contains(x, y))
return childIface;
}
}
return nullptr;
}
QString text(QAccessible::Text) const override
{
return window()->title();
}
QRect rect() const override
{
return QRect(window()->x(), window()->y(), window()->width(), window()->height());
}
QAccessible::Role role() const override
{
return QAccessible::Window;
}
QAccessible::State state() const override
{
QAccessible::State st;
if (window() == QGuiApplication::focusWindow())
st.active = true;
if (!window()->isVisible())
st.invisible = true;
return st;
}
QWindow* window() const override
{
return static_cast<QWindow*>(object());
}
private:
QQuickWindow* m_window;
};
/*
* DCompOffscreenWindow is a top window, mark it as unacessible so it won't
* be introspected by a11y, the QML scene is actually accessible though QAccessibleRenderWindow
*/
class QAccessibleOffscreenWindow: public QAccessibleObject
{
public:
QAccessibleOffscreenWindow(QQuickWindow *window)
: QAccessibleObject(window)
{
}
QAccessibleInterface* parent() const override
{
// we assume to be a top level window...
return QAccessible::queryAccessibleInterface(qApp);
}
QAccessibleInterface* child(int) const override
{
return nullptr;
}
int childCount() const override
{
return 0;
}
int indexOfChild(const QAccessibleInterface *) const override
{
return -1;
}
QAccessibleInterface *childAt(int, int) const override
{
return nullptr;
}
QAccessibleInterface *focusChild() const override
{
return nullptr;
}
QString text(QAccessible::Text) const override
{
return window()->title();
}
QRect rect() const override
{
return QRect(window()->x(), window()->y(), window()->width(), window()->height());
}
QAccessible::Role role() const override
{
return QAccessible::Window;
}
QAccessible::State state() const override
{
QAccessible::State st;
if (window() == QGuiApplication::focusWindow())
st.active = true;
if (!window()->isVisible())
st.invisible = true;
return st;
}
private:
QWindow* window() const override
{
return static_cast<QWindow*>(object());
}
};
QAccessibleInterface* compositionAccessibleFactory(const QString &classname, QObject *object)
{
#ifdef QT5_HAS_X11_COMPOSITOR
if (classname == QLatin1String("vlc::CompositorX11RenderWindow"))
{
CompositorX11RenderWindow* renderWindow = qobject_cast<CompositorX11RenderWindow *>(object);
assert(renderWindow);
return new QAccessibleRenderWindow(renderWindow, renderWindow);
}
#endif
#ifdef HAVE_DCOMP_H
if (classname == QLatin1String("vlc::DCompRenderWindow"))
{
DCompRenderWindow* renderWindow = qobject_cast<DCompRenderWindow *>(object);
assert(renderWindow);
return new QAccessibleRenderWindow(renderWindow, renderWindow);
}
#endif
if (classname == QLatin1String("vlc::CompositorOffscreenWindow")
|| (classname == QLatin1String("vlc::DCompOffscreenWindow")) )
{
return new QAccessibleOffscreenWindow(qobject_cast<QQuickWindow *>(object));
}
return nullptr;
}
}
#endif