Commit 69786a1f authored by Konrad Materka's avatar Konrad Materka
Browse files

[XembedSNIProxy] Hide container window when something shows it

Summary:
For each tray icon XEmbedSNIProxy is creating container window 32x32 in size. It is black with opaque set to 0 (fully transparent when compositor is enabled). All of these container windows are stacked below all windows, so normally you can't see them. On creation all container windows are created in top-left corner. When user clicks on the tray icon, container window is moved to the click location (to handle events correctly).
On KWin restart all windows are shuffled, usually KWin is able to restore ordering correctly, but for some reason not it this case. As a result black/transparent container windows are stacked above all other windows and panels.
To solve that, when container window is visible, XembedSNIProxy needs hide it again by stacking the container window below again.

BUG: 357443
FIXED-IN: 5.18.0

Test Plan:
1. Run any application with XEmbed system tray icon, do not click on the icon
2. Restart KWin
3. [Optional] Disable compositor - with disable container window is black and easier to spot
4. Expected:
a) before fix: black/transparent rectangle in the top-left corner, reacts to mouse click
b) after fix: no rectangle, mouse clicks work as expected.

Reviewers: #plasma_workspaces, #plasma, davidedmundson

Reviewed By: #plasma_workspaces, #plasma, davidedmundson

Subscribers: plasma-devel

Tags: #plasma

Differential Revision: https://phabricator.kde.org/D26395
parent 34216709
/*
* Registers as a embed container
* Copyright (C) 2015 <davidedmundson@kde.org> David Edmundson
* Copyright (C) 2019 <materka@gmail.com> Konrad Materka
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
......@@ -22,11 +23,7 @@
#include "debug.h"
#include <QCoreApplication>
#include <QDBusConnection>
#include <QDBusServiceWatcher>
#include <QHash>
#include <QTimer>
#include <QTextDocument>
#include <QX11Info>
#include <KSelectionOwner>
......@@ -163,6 +160,15 @@ bool FdoSelectionManager::nativeEventFilter(const QByteArray &eventType, void *m
sniProxy->resizeWindow(event->width, event->height);
}
}
} else if (responseType == XCB_VISIBILITY_NOTIFY) {
const auto event = reinterpret_cast<xcb_visibility_notify_event_t *>(ev);
// it's possible that something showed our container window, we have to hide it
// workaround for BUG 357443: when KWin is restarted, container window is shown on top
if (event->state == XCB_VISIBILITY_UNOBSCURED) {
for (auto sniProxy : m_proxies.values()) {
sniProxy->hideContainerWindow(event->window);
}
}
}
return false;
......@@ -197,17 +203,6 @@ void FdoSelectionManager::onClaimedOwnership()
qCDebug(SNIPROXY) << "Manager selection claimed";
setSystemTrayVisual();
// send all container windows to background on KWin restart
QDBusServiceWatcher *watcher = new QDBusServiceWatcher(QStringLiteral("org.kde.KWin"), QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForRegistration, this);
connect(watcher, &QDBusServiceWatcher::serviceRegistered, this, [=](const QString &) {
// some delay is necesary
QTimer::singleShot(100, this, [=]() {
for (auto sniproxy : m_proxies) {
sniproxy->stackContainerWindow(XCB_STACK_MODE_BELOW);
}
});
});
}
void FdoSelectionManager::onFailedToClaimOwnership()
......
/*
* Holds one embedded window, registers as DBus entry
* Copyright (C) 2015 <davidedmundson@kde.org> David Edmundson
* Copyright (C) 2019 <materka@gmail.com> Konrad Materka
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
......@@ -79,6 +80,7 @@ SNIProxy::SNIProxy(xcb_window_t wid, QObject* parent):
//instead lets use one DBus connection per SNI
m_dbus(QDBusConnection::connectToBus(QDBusConnection::SessionBus, QStringLiteral("XembedSniProxy%1").arg(s_serviceCount++))),
m_windowId(wid),
sendingClickEvent(false),
m_injectMode(Direct)
{
//create new SNI
......@@ -101,8 +103,9 @@ SNIProxy::SNIProxy(xcb_window_t wid, QObject* parent):
uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK;
values[0] = screen->black_pixel; //draw a solid background so the embedded icon doesn't get garbage in it
values[1] = true; //bypass wM
// Redirect and handle structure (size, position) requests on the embedded window.
values[2] = XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT;
values[2] = XCB_EVENT_MASK_VISIBILITY_CHANGE | // receive visibility change, to handle KWin restart #357443
// Redirect and handle structure (size, position) requests from the embedded window.
XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT;
xcb_create_window (c, /* connection */
XCB_COPY_FROM_PARENT, /* depth */
m_containerWid, /* window Id */
......@@ -217,13 +220,6 @@ void SNIProxy::update()
emit NewToolTip();
}
void SNIProxy::stackContainerWindow(const uint32_t stackMode) const
{
auto c = QX11Info::connection();
const uint32_t stackData[] = {stackMode};
xcb_configure_window(c, m_containerWid, XCB_CONFIG_WINDOW_STACK_MODE, stackData);
}
void SNIProxy::resizeWindow(const uint16_t width, const uint16_t height) const
{
auto connection = QX11Info::connection();
......@@ -239,6 +235,14 @@ void SNIProxy::resizeWindow(const uint16_t width, const uint16_t height) const
xcb_flush(connection);
}
void SNIProxy::hideContainerWindow(xcb_window_t windowId) const
{
if (m_containerWid == windowId && !sendingClickEvent) {
qDebug() << "Container window visible, stack below";
stackContainerWindow(XCB_STACK_MODE_BELOW);
}
}
QSize SNIProxy::calculateClientWindowSize() const
{
auto c = QX11Info::connection();
......@@ -432,6 +436,13 @@ QPoint SNIProxy::calculateClickPoint() const
return clickPoint;
}
void SNIProxy::stackContainerWindow(const uint32_t stackMode) const
{
auto c = QX11Info::connection();
const uint32_t stackData[] = {stackMode};
xcb_configure_window(c, m_containerWid, XCB_CONFIG_WINDOW_STACK_MODE, stackData);
}
//____________properties__________
QString SNIProxy::Category() const
......@@ -513,6 +524,7 @@ void SNIProxy::sendClick(uint8_t mouseButton, int x, int y)
//ideally we should make this match the plasmoid hit area
qCDebug(SNIPROXY) << "Received click" << mouseButton << "with passed x*y" << x << y;
sendingClickEvent = true;
auto c = QX11Info::connection();
......@@ -603,4 +615,6 @@ void SNIProxy::sendClick(uint8_t mouseButton, int x, int y)
#ifndef VISUAL_DEBUG
stackContainerWindow(XCB_STACK_MODE_BELOW);
#endif
sendingClickEvent = false;
}
/*
* Holds one embedded window, registers as DBus entry
* Copyright (C) 2015 <davidedmundson@kde.org> David Edmundson
* Copyright (C) 2019 <materka@gmail.com> Konrad Materka
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
......@@ -49,8 +50,8 @@ public:
~SNIProxy() override;
void update();
void stackContainerWindow(const uint32_t stackMode) const;
void resizeWindow(const uint16_t width, const uint16_t height) const;
void hideContainerWindow(xcb_window_t windowId) const;
/**
* @return the category of the application associated to this item
......@@ -154,13 +155,14 @@ private:
bool isTransparentImage(const QImage &image) const;
QImage convertFromNative(xcb_image_t *xcbImage) const;
QPoint calculateClickPoint() const;
void stackContainerWindow(const uint32_t stackMode) const;
QDBusConnection m_dbus;
xcb_window_t m_windowId;
xcb_window_t m_containerWid;
static int s_serviceCount;
QPixmap m_pixmap;
bool sendingClickEvent;
InjectMode m_injectMode;
};
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment