3 * Copyright 2014 Roy Marples <roy@marples.name>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 #include <QSocketNotifier>
36 #include "dhcpcd-qt.h"
37 #include "dhcpcd-about.h"
38 #include "dhcpcd-preferences.h"
39 #include "dhcpcd-wi.h"
40 #include "dhcpcd-ifmenu.h"
41 #include "dhcpcd-ssidmenu.h"
44 #include <knotification.h>
53 onLine = carrier = false;
55 aniTimer = new QTimer(this);
56 connect(aniTimer, SIGNAL(timeout()), this, SLOT(animate()));
58 retryOpenTimer = NULL;
63 wis = new QList<DhcpcdWi *>();
66 qDebug("%s", "Connecting ...");
69 qCritical("libdhcpcd: %s", strerror(errno));
73 dhcpcd_set_progname(con, "dhcpcd-qt");
74 dhcpcd_set_status_callback(con, dhcpcd_status_cb, this);
75 dhcpcd_set_if_callback(con, dhcpcd_if_cb, this);
76 dhcpcd_wpa_set_scan_callback(con, dhcpcd_wpa_scan_cb, this);
77 dhcpcd_wpa_set_status_callback(con, dhcpcd_wpa_status_cb, this);
84 /* This will have already been destroyed,
85 * but the reference may not be. */
100 DHCPCD_CONNECTION *DhcpcdQt::getConnection()
106 QList<DhcpcdWi *> *DhcpcdQt::getWis()
112 void DhcpcdQt::animate()
117 if (aniCounter++ > 6) {
123 if (aniCounter % 2 == 0)
124 icon = "network-idle";
126 icon = "network-transmit-receive";
128 switch(aniCounter++) {
130 icon = "network-transmit";
133 icon = "network-receive";
136 icon = "network-idle";
141 setIcon("status", icon);
144 void DhcpcdQt::updateOnline(bool showIf)
146 bool isOn, isCarrier;
151 isOn = isCarrier = false;
152 ifs = dhcpcd_interfaces(con);
153 for (i = ifs; i; i = i->next) {
154 if (strcmp(i->type, "link") == 0) {
161 msg = dhcpcd_if_message(i, NULL);
166 msgs = QString::fromAscii(msg);
168 msgs += '\n' + QString::fromAscii(msg);
171 qDebug() << i->ifname << i->reason;
174 if (onLine != isOn || carrier != isCarrier) {
181 aniTimer->start(300);
182 } else if (isCarrier) {
184 aniTimer->start(500);
186 setIcon("status", "network-offline");
189 trayIcon->setToolTip(msgs);
192 void DhcpcdQt::statusCallback(const char *status)
195 qDebug("Status changed to %s", status);
196 if (strcmp(status, "down") == 0) {
199 onLine = carrier = false;
200 setIcon("status", "network-offline");
201 trayIcon->setToolTip(tr("Not connected to dhcpcd"));
202 /* Close down everything */
215 preferencesAction->setEnabled(false);
219 if (lastStatus == NULL || strcmp(lastStatus, "down") == 0) {
220 qDebug("Connected to dhcpcd-%s", dhcpcd_version(con));
223 refresh = strcmp(lastStatus, "opened") ? false : true;
224 updateOnline(refresh);
228 lastStatus = strdup(status);
230 if (strcmp(status, "down") == 0) {
231 if (retryOpenTimer == NULL) {
232 retryOpenTimer = new QTimer(this);
233 connect(retryOpenTimer, SIGNAL(timeout()),
234 this, SLOT(tryOpen()));
235 retryOpenTimer->start(DHCPCD_RETRYOPEN);
240 void DhcpcdQt::dhcpcd_status_cb(_unused DHCPCD_CONNECTION *con,
241 const char *status, void *d)
243 DhcpcdQt *dhcpcdQt = (DhcpcdQt *)d;
245 dhcpcdQt->statusCallback(status);
248 void DhcpcdQt::ifCallback(DHCPCD_IF *i)
253 if (strcmp(i->reason, "RENEW") &&
254 strcmp(i->reason, "STOP") &&
255 strcmp(i->reason, "STOPPED"))
257 msg = dhcpcd_if_message(i, &new_msg);
261 QSystemTrayIcon::MessageIcon icon =
262 i->up ? QSystemTrayIcon::Information :
263 QSystemTrayIcon::Warning;
264 QString t = tr("Network Event");
275 void DhcpcdQt::dhcpcd_if_cb(DHCPCD_IF *i, void *d)
277 DhcpcdQt *dhcpcdQt = (DhcpcdQt *)d;
279 dhcpcdQt->ifCallback(i);
282 DhcpcdWi *DhcpcdQt::findWi(DHCPCD_WPA *wpa)
285 for (auto &wi : *wis) {
286 if (wi->getWpa() == wpa)
292 void DhcpcdQt::scanCallback(DHCPCD_WPA *wpa)
294 DHCPCD_WI_SCAN *scans, *s1, *s2;
295 int fd = dhcpcd_wpa_get_fd(wpa);
300 qCritical("No fd for WPA");
308 DHCPCD_IF *i = dhcpcd_wpa_if(wpa);
310 qCritical("No interface for WPA");
318 qDebug("%s: Received scan results", i->ifname);
319 scans = dhcpcd_wi_scans(i);
321 wi = new DhcpcdWi(this, wpa);
324 QString title = tr("New Access Point");
326 for (s1 = scans; s1; s1 = s1->next) {
327 for (s2 = wi->getScans(); s2; s2 = s2->next) {
328 if (strcmp(s1->ssid, s2->ssid) == 0)
332 if (!txt.isEmpty()) {
333 title = tr("New Access Points");
339 if (!txt.isEmpty() &&
340 (ssidMenu == NULL || !ssidMenu->isVisible()))
342 qDebug("%s", qPrintable(txt));
347 if (wi->setScans(scans) && ssidMenu->isVisible())
348 ssidMenu->popup(ssidMenuPos);
351 void DhcpcdQt::dhcpcd_wpa_scan_cb(DHCPCD_WPA *wpa, void *d)
353 DhcpcdQt *dhcpcdQt = (DhcpcdQt *)d;
355 dhcpcdQt->scanCallback(wpa);
358 void DhcpcdQt::wpaStatusCallback(DHCPCD_WPA *wpa, const char *status)
362 i = dhcpcd_wpa_if(wpa);
363 qDebug("%s: WPA status %s", i->ifname, status);
364 if (strcmp(status, "down") == 0) {
365 DhcpcdWi *wi = findWi(wpa);
373 void DhcpcdQt::dhcpcd_wpa_status_cb(DHCPCD_WPA *wpa, const char *status,
376 DhcpcdQt *dhcpcdQt = (DhcpcdQt *)d;
378 dhcpcdQt->wpaStatusCallback(wpa, status);
381 void DhcpcdQt::tryOpen() {
382 int fd = dhcpcd_open(con, true);
383 static int last_error;
386 if (errno == EACCES || errno == EPERM) {
387 if ((fd = dhcpcd_open(con, false)) != -1)
390 if (errno != last_error) {
392 const char *errt = strerror(errno);
393 qCritical("dhcpcd_open: %s", errt);
394 trayIcon->setToolTip(
395 tr("Error connecting to dhcpcd: %1").arg(errt));
397 if (retryOpenTimer == NULL) {
398 retryOpenTimer = new QTimer(this);
399 connect(retryOpenTimer, SIGNAL(timeout()),
400 this, SLOT(tryOpen()));
401 retryOpenTimer->start(DHCPCD_RETRYOPEN);
407 /* Start listening to WPA events */
408 dhcpcd_wpa_start(con);
410 if (retryOpenTimer) {
411 delete retryOpenTimer;
412 retryOpenTimer = NULL;
415 notifier = new QSocketNotifier(fd, QSocketNotifier::Read);
416 connect(notifier, SIGNAL(activated(int)), this, SLOT(dispatch()));
418 preferencesAction->setEnabled(dhcpcd_privileged(con));
421 void DhcpcdQt::dispatch() {
423 if (dhcpcd_get_fd(con) == -1) {
424 qWarning("dhcpcd connection lost");
428 dhcpcd_dispatch(con);
431 void DhcpcdQt::notify(QString &title, QString &msg,
433 QSystemTrayIcon::MessageIcon
435 QSystemTrayIcon::MessageIcon icon
441 KNotification *n = new KNotification("event", this);
446 trayIcon->showMessage(title, msg, icon);
451 void DhcpcdQt::closeEvent(QCloseEvent *event)
454 if (trayIcon->isVisible()) {
460 QIcon DhcpcdQt::getIcon(QString category, QString name)
464 if (QIcon::hasThemeIcon(name))
465 icon = QIcon::fromTheme(name);
467 icon = QIcon(ICONDIR "/hicolor/scalable/" + category + "/" + name + ".svg");
471 void DhcpcdQt::setIcon(QString category, QString name)
473 QIcon icon = getIcon(category, name);
475 trayIcon->setIcon(icon);
478 QIcon DhcpcdQt::icon()
481 return getIcon("status", "network-transmit-receive");
484 void DhcpcdQt::createSsidMenu()
491 if (wis->size() == 0)
494 ssidMenu = new QMenu(this);
495 if (wis->size() == 1)
496 wis->first()->createMenu(ssidMenu);
498 for (auto &wi : *wis)
499 ssidMenu->addMenu(wi->createIfMenu(ssidMenu));
501 ssidMenuPos = QCursor::pos();
502 ssidMenu->popup(ssidMenuPos);
505 void DhcpcdQt::iconActivated(QSystemTrayIcon::ActivationReason reason)
508 if (reason == QSystemTrayIcon::Trigger)
512 void DhcpcdQt::dialogClosed(QDialog *dialog)
517 else if (dialog == preferences)
521 void DhcpcdQt::showPreferences()
524 if (preferences == NULL) {
525 preferences = new DhcpcdPreferences(this);
528 preferences->activateWindow();
531 void DhcpcdQt::showAbout()
535 about = new DhcpcdAbout(this);
538 about->activateWindow();
541 void DhcpcdQt::createActions()
544 preferencesAction = new QAction(tr("&Preferences"), this);
545 preferencesAction->setIcon(QIcon::fromTheme("preferences-system-network"));
546 preferencesAction->setEnabled(false);
547 connect(preferencesAction, SIGNAL(triggered()),
548 this, SLOT(showPreferences()));
550 aboutAction = new QAction(tr("&About"), this);
551 aboutAction->setIcon(QIcon::fromTheme("help-about"));
552 connect(aboutAction, SIGNAL(triggered()), this, SLOT(showAbout()));
554 quitAction = new QAction(tr("&Quit"), this);
555 quitAction->setIcon(QIcon::fromTheme("application-exit"));
556 connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
560 void DhcpcdQt::createTrayIcon()
563 trayIconMenu = new QMenu(this);
564 trayIconMenu->addAction(preferencesAction);
565 trayIconMenu->addSeparator();
566 trayIconMenu->addAction(aboutAction);
567 trayIconMenu->addAction(quitAction);
569 trayIcon = new QSystemTrayIcon(this);
570 setIcon("status", "network-offline");
571 trayIcon->setContextMenu(trayIconMenu);
573 connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
574 this, SLOT(iconActivated(QSystemTrayIcon::ActivationReason)));