1 /*****************************************************************************
2  *   Copyright 2013 - 2015 Yichao Yu <yyc1992@gmail.com>                     *
3  *                                                                           *
4  *   This program is free software; you can redistribute it and/or modify    *
5  *   it under the terms of the GNU Lesser General Public License as          *
6  *   published by the Free Software Foundation; either version 2.1 of the    *
7  *   License, or (at your option) version 3, or any later version accepted   *
8  *   by the membership of KDE e.V. (or its successor approved by the         *
9  *   membership of KDE e.V.), which shall act as a proxy defined in          *
10  *   Section 6 of version 3 of the license.                                  *
11  *                                                                           *
12  *   This program is distributed in the hope that it will be useful,         *
13  *   but WITHOUT ANY WARRANTY; without even the implied warranty of          *
14  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU       *
15  *   Lesser General Public License for more details.                         *
16  *                                                                           *
17  *   You should have received a copy of the GNU Lesser General Public        *
18  *   License along with this library. If not,                                *
19  *   see <http://www.gnu.org/licenses/>.                                     *
20  *****************************************************************************/
21 
22 #include "qtcurve_plugin.h"
23 #include "qtcurve.h"
24 
25 #include "config-qt5.h"
26 
27 #include <qtcurve-utils/qtprops.h>
28 #include <qtcurve-utils/x11shadow.h>
29 #include <qtcurve-utils/x11blur.h>
30 
31 #include <QApplication>
32 
33 #ifdef Qt5X11Extras_FOUND
34 #  include <qtcurve-utils/x11base.h>
35 #  include <QX11Info>
36 #endif
37 
38 #ifdef QTC_QT5_ENABLE_QTQUICK2
39 #  include <QQuickWindow>
40 #  include <QQuickItem>
41 #endif
42 #include <QDebug>
43 
44 #include <qtcurve-utils/log.h>
45 
46 namespace QtCurve {
47 
48 __attribute__((hot)) static void
polishQuickControl(QObject * obj)49 polishQuickControl(QObject *obj)
50 {
51 #ifdef QTC_QT5_ENABLE_QTQUICK2
52     if (QQuickWindow *window = qobject_cast<QQuickWindow*>(obj)) {
53         // QtQuickControl support
54         // This is still VERY experimental.
55         // Need a lot more testing and refactoring.
56         if (Style *style = getStyle(qApp)) {
57             if (window->inherits("QQuickPopupWindow")) {
58                 if (window->inherits("QQuickMenuPopupWindow")) {
59                     window->setColor(QColor(0, 0, 0, 0));
60                 }
61                 qtcX11ShadowInstall(window->winId());
62             } else {
63                 QColor color = window->color();
64                 int opacity = style->options().bgndOpacity;
65                 if (color.alpha() == 255 && opacity != 100) {
66                     qreal opacityF = opacity / 100.0;
67                     window->setColor(QColor::fromRgbF(color.redF() * opacityF,
68                                                       color.greenF() * opacityF,
69                                                       color.blueF() * opacityF,
70                                                       opacityF));
71                     qtcX11BlurTrigger(window->winId(), true, 0, nullptr);
72                 }
73             }
74         }
75     } else if (QQuickItem *item = qobject_cast<QQuickItem*>(obj)) {
76         if (QQuickWindow *window = item->window()) {
77             if (getStyle(qApp)) {
78                 window->setColor(QColor(0, 0, 0, 0));
79                 qtcX11BlurTrigger(window->winId(), true, 0, nullptr);
80             }
81         }
82     }
83 #else
84     QTC_UNUSED(obj);
85 #endif
86 }
87 
88 __attribute__((hot)) static bool
qtcEventCallback(void ** cbdata)89 qtcEventCallback(void **cbdata)
90 {
91     QObject *receiver = (QObject*)cbdata[0];
92     QTC_RET_IF_FAIL(receiver, false);
93     QEvent *event = (QEvent*)cbdata[1];
94     if (qtcUnlikely(event->type() == QEvent::DynamicPropertyChange)) {
95         QDynamicPropertyChangeEvent *prop_event =
96             static_cast<QDynamicPropertyChangeEvent*>(event);
97         // eat the property change events from ourselves
98         if (prop_event->propertyName() == QTC_PROP_NAME) {
99             return true;
100         }
101     }
102     QWidget *widget = qtcToWidget(receiver);
103     if (qtcUnlikely(widget && !qtcGetWid(widget))) {
104         if (Style *style = getStyle(widget)) {
105             style->prePolish(widget);
106         }
107     } else if (widget && event->type() == QEvent::UpdateRequest) {
108         QtcQWidgetProps props(widget);
109         props->opacity = 100;
110     } else {
111         polishQuickControl(receiver);
112     }
113     return false;
114 }
115 
116 static StylePlugin *firstPlInstance = nullptr;
117 static QList<Style*> *styleInstances = nullptr;
118 
119 QStyle*
create(const QString & key)120 StylePlugin::create(const QString &key)
121 {
122     if (!firstPlInstance) {
123         firstPlInstance = this;
124         styleInstances = &m_styleInstances;
125     }
126 
127     init();
128     Style *qtc;
129     if (key.toLower() == "qtcurve") {
130         qtc = new Style;
131         qtc->m_plugin = this;
132         m_styleInstances << qtc;
133     } else {
134         qtc = nullptr;
135     }
136     return qtc;
137 }
138 
unregisterCallback()139 void StylePlugin::unregisterCallback()
140 {
141     if (m_eventNotifyCallbackInstalled) {
142         qtcInfo("Unregistering the event notify callback (for plugin %p)\n", this);
143         QInternal::unregisterCallback(QInternal::EventNotifyCallback,
144                                     qtcEventCallback);
145         m_eventNotifyCallbackInstalled = false;
146     }
147 }
148 
~StylePlugin()149 StylePlugin::~StylePlugin()
150 {
151     qtcInfo("Deleting QtCurve plugin (%p)\n", this);
152     if (!m_styleInstances.isEmpty()) {
153         qtcWarn("there remain(s) %d Style instance(s)\n", m_styleInstances.count());
154         QList<Style*>::Iterator it = m_styleInstances.begin();
155         while (it != m_styleInstances.end()) {
156             Style *that = *it;
157             it = m_styleInstances.erase(it);
158             delete that;
159         }
160     }
161     if (firstPlInstance == this) {
162         firstPlInstance = nullptr;
163         styleInstances = nullptr;
164     }
165 }
166 
167 void
init()168 StylePlugin::init()
169 {
170     std::call_once(m_ref_flag, [this] {
171             QInternal::registerCallback(QInternal::EventNotifyCallback,
172                                         qtcEventCallback);
173             m_eventNotifyCallbackInstalled = true;
174             if (QCoreApplication::instance()) {
175                 connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, &StylePlugin::unregisterCallback);
176             }
177 #ifdef QTC_QT5_ENABLE_QTQUICK2
178             QQuickWindow::setDefaultAlphaBuffer(true);
179 #endif
180 #ifdef Qt5X11Extras_FOUND
181             if (qApp->platformName() == "xcb") {
182                 qtcX11InitXcb(QX11Info::connection(), QX11Info::appScreen());
183             }
184 #endif
185         });
186 }
187 
atLibOpen()188 __attribute__((constructor)) int atLibOpen()
189 {
190     qtcDebug("Opening QtCurve\n");
191     return 0;
192 }
193 
atLibClose()194 __attribute__((destructor)) int atLibClose()
195 {
196     qtcInfo("Closing QtCurve\n");
197     if (firstPlInstance) {
198         qtcInfo("Plugin instance %p still open with %d open Style instance(s)\n",
199             firstPlInstance, styleInstances->count());
200     }
201     return 0;
202 }
203 
204 }
205