1 /****************************************************************************
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the examples of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file. Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights. These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
40 ****************************************************************************/
42 #include "qmaemo5homescreenadaptor.h"
44 #include <QtCore/qsocketnotifier.h>
45 #include <QtCore/qpointer.h>
47 #include <QtGui/qapplication.h>
48 #include <QtGui/qx11info_x11.h>
49 #include <QtGui/qwidget.h>
50 #include <QtGui/qevent.h>
53 #include <X11/Xatom.h>
54 #include <X11/Xutil.h>
56 static QCoreApplication::EventFilter oldEventFilter;
57 static QList<QMaemo5HomescreenAdaptor *> allDesktopItems;
59 static Atom atomByName(const char *name)
61 Atom atom = XInternAtom(QX11Info::display(), name, False);
63 qWarning("Unable to obtain %s atom. This class requires a running Hildon session.", name);
73 HildonTypeHomeApplet = 3,
74 HildonAppletSettings = 4,
75 HildonAppletShowSettings = 5,
76 HildonAppletOnCurrentDesktop = 6,
80 static Atom hsAtoms[EnumCount] = { 0, 0, 0, 0, 0, 0, 0 };
82 static void initAtoms()
84 hsAtoms[HildonAppletId] = atomByName("_HILDON_APPLET_ID");
85 hsAtoms[NetWmWindowType] = atomByName("_NET_WM_WINDOW_TYPE");
86 hsAtoms[Utf8String] = atomByName("UTF8_STRING");
87 hsAtoms[HildonTypeHomeApplet] = atomByName("_HILDON_WM_WINDOW_TYPE_HOME_APPLET");
88 hsAtoms[HildonAppletSettings] = atomByName("_HILDON_APPLET_SETTINGS");
89 hsAtoms[HildonAppletShowSettings] = atomByName("_HILDON_APPLET_SHOW_SETTINGS");
90 hsAtoms[HildonAppletOnCurrentDesktop] = atomByName("_HILDON_APPLET_ON_CURRENT_DESKTOP");
93 /*! \class QMaemo5HomescreenAdaptor
95 \brief The QMaemo5HomescreenAdaptor flags a top-level QWidget as homescreen widget
97 QMaemo5HomescreenAdaptor is used in conjunction with the Qt for Maemo homescreen
98 loader. It evaluates the two command line arguments "-plugin-id" and "-write-pipe"
99 to set up a Qt top-level widget as Maemo 5 homescreen widget.
101 Note: By default, the widget will have a black background. In order to make the
102 widget transparent, set the Qt::WA_TranslucentBackground widget attribute.
107 QLabel *label = new QLabel("Hello Homescreen");
108 new QMaemo5HomescreenAdaptor(label);
112 Maemo 5 supports homescreen widgets with settings dialogs. To use it, set
113 the settingsAvailable() property and show a settings dialog when the
114 settingsRequested() signal is emitted.
116 Maemo 5 supports more than one homescreen. In order to determine whether
117 the homescreen widget is on the currently visible homescreen, connect to
118 the homescreenChanged() signal.
121 /*! \property QMaemo5HomescreenAdaptor::settingsAvailable
123 Set this property to true if the widget can make use of a settings dialog,
124 otherwise to false. When this property is set to true, the Maemo 5 homescreen
125 renders a small settings icon on top of the homescreen widget when the
126 user enters the desktop menu. When the user clicks that settings icon, the
127 settingsRequested() signal is emitted.
129 The default is false.
131 \sa settingsRequested()
134 /*! \fn void settingsRequested()
136 This signal is emitted every time the homescreen widget's settings icon is
137 invoked by the user. Note that this icon is only visible when the settingsAvailable()
140 \sa settingsAvailable()
143 /*! \fn void homescreenChanged(bool isOnCurrentHomescreen)
145 This is signal is emitted when current homescreen changes and the homescreen
146 widget becomes visible or invisible. The \a isOnCurrentHomescreen argument
147 indicates whether the homescreen widget is on the current homescreen or not.
149 This signal can be used to start/stop background processing in order to save
154 Constructs a new QMaemo5HomescreenAdaptor for the given \a widget.
156 Note: The widget must be a top-level widget, and must not be reparented
157 during the lifetime of this adaptor.
159 Note: \a widget is also the parent of this class, if the widget is destroyed,
162 QMaemo5HomescreenAdaptor::QMaemo5HomescreenAdaptor(QWidget *widget)
166 Q_ASSERT(widget->isWindow());
171 Display *display = QX11Info::display();
173 const QStringList args = QApplication::arguments();
175 // parse the command line arguments.
177 if ((idx = args.indexOf(QLatin1String("-plugin-id"))) != -1) {
178 appletId = args.value(idx + 1);
179 const QByteArray pluginId = appletId.toUtf8();
180 if (!pluginId.isEmpty()) {
181 XChangeProperty(display,
183 hsAtoms[HildonAppletId],
184 hsAtoms[Utf8String], 8, PropModeReplace,
185 reinterpret_cast<const unsigned char *>(pluginId.constData()),
189 if ((idx = args.indexOf(QLatin1String("-write-pipe"))) != -1) {
191 int sockId = args.value(idx + 1).toInt(&ok);
193 socketNotifier = new QSocketNotifier(sockId, QSocketNotifier::Exception, this);
194 connect(socketNotifier, SIGNAL(activated(int)), this, SLOT(socketException()));
198 // set the X11 atoms to flag our widget as homescreen widget
199 if (!appletId.isEmpty()) {
200 XChangeProperty(display,
202 hsAtoms[NetWmWindowType],
203 XA_ATOM, 32, PropModeReplace,
204 reinterpret_cast<const unsigned char *>(&hsAtoms[HildonTypeHomeApplet]),
209 // --- make this window a child of root
210 XSetTransientForHint(display, widget->winId(),
211 RootWindow(display, widget->x11Info().screen()));
213 // --- add an x11 event filter
215 oldEventFilter = QCoreApplication::instance()->setEventFilter(applicationEventFilter);
217 allDesktopItems.append(this);
219 // --- set WM input hints indicating that we don't want focus events
220 XWMHints *h = XGetWMHints(display, widget->winId());
223 memset(&wm_hints, 0, sizeof(wm_hints)); // make valgrind happy
226 h->flags |= InputHint;
229 XSetWMHints(display, widget->winId(), h);
233 widget->setMouseTracking(true);
237 QMaemo5HomescreenAdaptor::~QMaemo5HomescreenAdaptor()
239 allDesktopItems.removeOne(this);
243 void QMaemo5HomescreenAdaptor::updateStatus()
245 if (appletId.isEmpty())
248 Display *display = QX11Info::display();
250 // Set or remove settings property
252 XChangeProperty(display,
253 appletWidget()->winId(),
254 hsAtoms[HildonAppletSettings],
255 XA_CARDINAL, 32, PropModeReplace,
256 (const unsigned char *) &(hasSettings), 1);
258 XDeleteProperty(display,
259 appletWidget()->winId(),
260 hsAtoms[HildonAppletSettings]);
264 void QMaemo5HomescreenAdaptor::socketException()
266 socketNotifier->setEnabled(false);
267 appletWidget()->close();
270 bool qt_sendSpontaneousEvent(QObject *receiver, QEvent *event)
272 return QCoreApplication::sendSpontaneousEvent(receiver, event);
278 bool QMaemo5HomescreenAdaptor::applicationEventFilter(void *message, long *result)
280 static QPointer<QWidget> lastMouseWidget;
284 retval = oldEventFilter(message, result);
286 if (allDesktopItems.isEmpty())
289 XEvent *ev = reinterpret_cast<XEvent *>(message);
291 // Generate a mouse release for a leave Notify (as we don't get the mouse release from X11)
292 if (ev->type == ButtonPress) {
293 QPoint globalPos( ev->xbutton.x_root, ev->xbutton.y_root);
294 QWidget *widget = QWidget::find((WId)ev->xany.window);
296 lastMouseWidget = widget->childAt(widget->mapFromGlobal(globalPos));
297 if (!lastMouseWidget)
298 lastMouseWidget = widget;
301 } else if (ev->type == ButtonRelease) {
304 } else if (ev->type == LeaveNotify) {
305 if (lastMouseWidget) {
306 // create a mouse up event that lies in Nirvana.
307 QPoint pos(-1000, -1000);
308 QMouseEvent e(QEvent::MouseButtonRelease, pos, pos, Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
309 qt_sendSpontaneousEvent(lastMouseWidget, &e);
313 } else if (ev->type == ClientMessage) {
314 XClientMessageEvent *cm = (XClientMessageEvent *)message;
315 if (cm->message_type == hsAtoms[HildonAppletShowSettings]) {
316 for (int i = 0; i < allDesktopItems.count(); ++i) {
317 if (allDesktopItems.at(i)->appletWidget()->winId() == ev->xproperty.window) {
318 emit allDesktopItems.at(i)->settingsRequested();
323 } else if (ev->type == PropertyNotify) {
324 if (ev->xproperty.atom == hsAtoms[HildonAppletOnCurrentDesktop]) {
325 for (int i = 0; i < allDesktopItems.count(); ++i) {
326 if (allDesktopItems.at(i)->appletWidget()->winId() == ev->xproperty.window) {
327 emit allDesktopItems.at(i)->homescreenChanged(ev->xproperty.state == 0);