Unity 8
 All Classes Functions
dbusinterface.cpp
1 /*
2  * Copyright 2014 Canonical Ltd.
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 published by
6  * the Free Software Foundation; version 3.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this program. If not, see <http://www.gnu.org/licenses/>.
15  *
16  * Authors:
17  * Michael Zanetti <michael.zanetti@canonical.com>
18  */
19 
20 #include "dbusinterface.h"
21 #include "launchermodel.h"
22 #include "launcheritem.h"
23 
24 #include <QDBusArgument>
25 #include <QDBusConnection>
26 #include <QDBusMessage>
27 #include <QDebug>
28 
29 DBusInterface::DBusInterface(LauncherModel *parent):
30  QDBusVirtualObject(parent),
31  m_launcherModel(parent)
32 {
33  /* Set up ourselves on DBus */
34  QDBusConnection con = QDBusConnection::sessionBus();
35  if (!con.registerService("com.canonical.Unity.Launcher")) {
36  qWarning() << "Unable to register launcher name";
37  }
38  if (!con.registerVirtualObject("/com/canonical/Unity/Launcher", this, QDBusConnection::VirtualObjectRegisterOption::SubPath)) {
39  qWarning() << "Unable to register launcher object";
40  }
41 }
42 
43 DBusInterface::~DBusInterface()
44 {
45  /* Remove oursevles from DBus */
46  QDBusConnection con = QDBusConnection::sessionBus();
47  con.unregisterService("com.canonical.Unity.Launcher");
48  con.unregisterObject("/com/canonical/Unity/Launcher");
49 }
50 
51 QString DBusInterface::introspect(const QString &path) const
52 {
53  /* This case we should just list the nodes */
54  if (path == "/com/canonical/Unity/Launcher/" || path == "/com/canonical/Unity/Launcher") {
55  QString nodes;
56 
57  // Add Refresh to introspect
58  nodes = "<interface name=\"com.canonical.Unity.Launcher\">"
59  "<method name=\"Refresh\"/>"
60  "</interface>";
61 
62  // Add dynamic properties for launcher emblems
63  for (int i = 0; i < m_launcherModel->rowCount(); i++) {
64  nodes.append("<node name=\"");
65  nodes.append(encodeAppId(m_launcherModel->get(i)->appId()));
66  nodes.append("\"/>\n");
67  }
68  return nodes;
69  }
70 
71  /* Should not happen, but let's handle it */
72  if (!path.startsWith("/com/canonical/Unity/Launcher")) {
73  return "";
74  }
75 
76  /* Now we should be looking at a node */
77  QString nodeiface =
78  "<interface name=\"com.canonical.Unity.Launcher.Item\">"
79  "<property name=\"count\" type=\"i\" access=\"readwrite\" />"
80  "<property name=\"countVisible\" type=\"b\" access=\"readwrite\" />"
81  "</interface>";
82  return nodeiface;
83 }
84 
85 
86 QString DBusInterface::decodeAppId(const QString& path)
87 {
88  QByteArray bytes = path.toUtf8();
89  QByteArray decoded;
90 
91  for (int i = 0; i < bytes.size(); ++i) {
92  char chr = bytes.at(i);
93 
94  if (chr == '_') {
95  QString number;
96  number.append(bytes.at(i+1));
97  number.append(bytes.at(i+2));
98 
99  bool okay;
100  char newchar = number.toUInt(&okay, 16);
101  if (okay)
102  decoded.append(newchar);
103 
104  i += 2;
105  } else {
106  decoded.append(chr);
107  }
108  }
109 
110  return QString::fromUtf8(decoded);
111 }
112 
113 QString DBusInterface::encodeAppId(const QString& appId)
114 {
115  QByteArray bytes = appId.toUtf8();
116  QString encoded;
117 
118  for (int i = 0; i < bytes.size(); ++i) {
119  uchar chr = bytes.at(i);
120 
121  if ((chr >= 'a' && chr <= 'z') ||
122  (chr >= 'A' && chr <= 'Z') ||
123  (chr >= '0' && chr <= '9'&& i != 0)) {
124  encoded.append(chr);
125  } else {
126  QString hexval = QString("_%1").arg(chr, 2, 16, QChar('0'));
127  encoded.append(hexval.toUpper());
128  }
129  }
130 
131  return encoded;
132 }
133 
134 bool DBusInterface::handleMessage(const QDBusMessage& message, const QDBusConnection& connection)
135 {
136  /* Check to make sure we're getting properties on our interface */
137  if (message.type() != QDBusMessage::MessageType::MethodCallMessage) {
138  return false;
139  }
140 
141  // First handle methods of the Launcher interface
142  if (message.interface() == "com.canonical.Unity.Launcher") {
143  if (message.member() == "Refresh") {
144  QDBusMessage reply = message.createReply();
145  Q_EMIT refreshCalled();
146  return connection.send(reply);
147  }
148  }
149 
150  // Now handle dynamic properties (for launcher emblems)
151  if (message.interface() != "org.freedesktop.DBus.Properties") {
152  return false;
153  }
154 
155  if (message.member() != "GetAll" && message.arguments()[0].toString() != "com.canonical.Unity.Launcher.Item") {
156  return false;
157  }
158 
159  /* Break down the path to just the app id */
160  QString pathtemp = message.path();
161  if (!pathtemp.startsWith("/com/canonical/Unity/Launcher/")) {
162  return false;
163  }
164  pathtemp.remove("/com/canonical/Unity/Launcher/");
165  if (pathtemp.indexOf('/') >= 0) {
166  return false;
167  }
168 
169  /* Find ourselves an appid */
170  QString appid = decodeAppId(pathtemp);
171  int index = m_launcherModel->findApplication(appid);
172  LauncherItem *item = static_cast<LauncherItem*>(m_launcherModel->get(index));
173 
174  QVariantList retval;
175  if (message.member() == "Get") {
176  if (!item) {
177  return false;
178  }
179  if (message.arguments()[1].toString() == "count") {
180  retval.append(QVariant::fromValue(QDBusVariant(item->count())));
181  } else if (message.arguments()[1].toString() == "countVisible") {
182  retval.append(QVariant::fromValue(QDBusVariant(item->countVisible())));
183  }
184  } else if (message.member() == "Set") {
185  if (message.arguments()[1].toString() == "count") {
186  int newCount = message.arguments()[2].toInt();
187  if (!item || newCount != item->count()) {
188  Q_EMIT countChanged(appid, newCount);
189  emitPropChangedDbus(appid, "count", QVariant(newCount));
190  }
191  } else if (message.arguments()[1].toString() == "countVisible") {
192  bool newVisible = message.arguments()[2].toBool();
193  if (!item || newVisible != item->countVisible()) {
194  Q_EMIT countVisibleChanged(appid, newVisible);
195  emitPropChangedDbus(appid, "countVisible", newVisible);
196  }
197  }
198  } else if (message.member() == "GetAll") {
199  if (item) {
200  QVariantMap all;
201  all.insert("count", item->count());
202  all.insert("countVisible", item->countVisible());
203  retval.append(all);
204  }
205  } else {
206  return false;
207  }
208 
209  QDBusMessage reply = message.createReply(retval);
210  return connection.send(reply);
211 }
212 
213 void DBusInterface::emitPropChangedDbus(const QString& appId, const QString& property, const QVariant &value)
214 {
215  QString path("/com/canonical/Unity/Launcher/");
216  path.append(encodeAppId(appId));
217 
218  QDBusMessage message = QDBusMessage::createSignal(path, "org.freedesktop.DBus.Properties", "PropertiesChanged");
219 
220  QList<QVariant> arguments;
221  QVariantHash changedprops;
222  changedprops[property] = QVariant::fromValue(QDBusVariant(value));
223  QVariantList deletedprops;
224 
225  arguments.append(changedprops);
226  arguments.append(deletedprops);
227 
228  message.setArguments(arguments);
229 
230  QDBusConnection con = QDBusConnection::sessionBus();
231  con.send(message);
232 }