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);
85 ssidMenu->setVisible(false);
86 ssidMenu->deleteLater();
101 DHCPCD_CONNECTION *DhcpcdQt::getConnection()
107 QList<DhcpcdWi *> *DhcpcdQt::getWis()
113 const char * DhcpcdQt::signalStrengthIcon(DHCPCD_WI_SCAN *scan)
116 if (scan->strength.value > 80)
117 return "network-wireless-connected-100";
118 else if (scan->strength.value > 55)
119 return "network-wireless-connected-75";
120 else if (scan->strength.value > 30)
121 return "network-wireless-connected-50";
122 else if (scan->strength.value > 5)
123 return "network-wireless-connected-25";
125 return "network-wireless-connected-00";
128 DHCPCD_WI_SCAN * DhcpcdQt::getStrongestSignal()
130 DHCPCD_WI_SCAN *scan, *scans, *s;
135 for (auto &wi : *wis) {
137 i = dhcpcd_wpa_if(wpa);
138 scans = wi->getScans();
139 for (s = scans; s; s = s->next) {
140 if (dhcpcd_wi_associated(i, s) &&
142 s->strength.value > scan->strength.value))
149 void DhcpcdQt::animate()
152 DHCPCD_WI_SCAN *scan;
154 scan = getStrongestSignal();
157 if (aniCounter++ > 6) {
163 if (aniCounter % 2 == 0)
164 icon = scan ? "network-wireless-connected-00" :
167 icon = scan ? DhcpcdQt::signalStrengthIcon(scan) :
168 "network-transmit-receive";
171 switch(aniCounter++) {
173 icon = "network-wireless-connected-00";
176 icon = "network-wireless-connected-25";
179 icon = "network-wireless-connected-50";
182 icon = "network-wireless-connected-75";
185 icon = "network-wireless-connected-100";
189 switch(aniCounter++) {
191 icon = "network-transmit";
194 icon = "network-receive";
197 icon = "network-idle";
203 setIcon("status", icon);
206 void DhcpcdQt::updateOnline(bool showIf)
208 bool isOn, isCarrier;
213 isOn = isCarrier = false;
214 ifs = dhcpcd_interfaces(con);
215 for (i = ifs; i; i = i->next) {
216 if (strcmp(i->type, "link") == 0) {
223 msg = dhcpcd_if_message(i, NULL);
228 msgs = QString::fromAscii(msg);
230 msgs += '\n' + QString::fromAscii(msg);
233 qDebug() << i->ifname << i->reason;
236 if (onLine != isOn || carrier != isCarrier) {
243 aniTimer->start(300);
244 } else if (isCarrier) {
246 aniTimer->start(500);
248 setIcon("status", "network-offline");
251 trayIcon->setToolTip(msgs);
254 void DhcpcdQt::statusCallback(const char *status)
257 qDebug("Status changed to %s", status);
258 if (strcmp(status, "down") == 0) {
261 onLine = carrier = false;
262 setIcon("status", "network-offline");
263 trayIcon->setToolTip(tr("Not connected to dhcpcd"));
264 /* Close down everything */
266 notifier->setEnabled(false);
267 notifier->deleteLater();
271 ssidMenu->deleteLater();
274 preferencesAction->setEnabled(false);
276 preferences->deleteLater();
282 if (lastStatus == NULL || strcmp(lastStatus, "down") == 0) {
283 qDebug("Connected to dhcpcd-%s", dhcpcd_version(con));
286 refresh = strcmp(lastStatus, "opened") ? false : true;
287 updateOnline(refresh);
291 lastStatus = strdup(status);
293 if (strcmp(status, "down") == 0) {
294 if (retryOpenTimer == NULL) {
295 retryOpenTimer = new QTimer(this);
296 connect(retryOpenTimer, SIGNAL(timeout()),
297 this, SLOT(tryOpen()));
298 retryOpenTimer->start(DHCPCD_RETRYOPEN);
303 void DhcpcdQt::dhcpcd_status_cb(_unused DHCPCD_CONNECTION *con,
304 const char *status, void *d)
306 DhcpcdQt *dhcpcdQt = (DhcpcdQt *)d;
308 dhcpcdQt->statusCallback(status);
311 void DhcpcdQt::ifCallback(DHCPCD_IF *i)
316 if (strcmp(i->reason, "RENEW") &&
317 strcmp(i->reason, "STOP") &&
318 strcmp(i->reason, "STOPPED"))
320 msg = dhcpcd_if_message(i, &new_msg);
324 QSystemTrayIcon::MessageIcon icon =
325 i->up ? QSystemTrayIcon::Information :
326 QSystemTrayIcon::Warning;
327 QString t = tr("Network Event");
338 for (auto &wi : *wis) {
339 DHCPCD_WPA *wpa = wi->getWpa();
340 if (dhcpcd_wpa_if(wpa) == i) {
341 DHCPCD_WI_SCAN *scans;
343 scans = dhcpcd_wi_scans(i);
344 processScans(wi, scans);
350 void DhcpcdQt::dhcpcd_if_cb(DHCPCD_IF *i, void *d)
352 DhcpcdQt *dhcpcdQt = (DhcpcdQt *)d;
354 dhcpcdQt->ifCallback(i);
357 DhcpcdWi *DhcpcdQt::findWi(DHCPCD_WPA *wpa)
360 for (auto &wi : *wis) {
361 if (wi->getWpa() == wpa)
367 void DhcpcdQt::processScans(DhcpcdWi *wi, DHCPCD_WI_SCAN *scans)
369 DHCPCD_WI_SCAN *s1, *s2;
371 QString title = tr("New Access Point");
373 for (s1 = scans; s1; s1 = s1->next) {
374 for (s2 = wi->getScans(); s2; s2 = s2->next) {
375 if (strcmp(s1->ssid, s2->ssid) == 0)
379 if (!txt.isEmpty()) {
380 title = tr("New Access Points");
386 if (!txt.isEmpty() &&
387 (ssidMenu == NULL || !ssidMenu->isVisible()))
391 if (ssidMenu && ssidMenu->isVisible())
392 ssidMenu->popup(ssidMenuPos);
395 void DhcpcdQt::scanCallback(DHCPCD_WPA *wpa)
397 DHCPCD_WI_SCAN *scans;
398 int fd = dhcpcd_wpa_get_fd(wpa);
403 qCritical("No fd for WPA");
411 DHCPCD_IF *i = dhcpcd_wpa_if(wpa);
413 qCritical("No interface for WPA");
421 qDebug("%s: Received scan results", i->ifname);
422 scans = dhcpcd_wi_scans(i);
424 wi = new DhcpcdWi(this, wpa);
431 processScans(wi, scans);
433 if (!aniTimer->isActive()) {
434 DHCPCD_WI_SCAN *scan;
436 scan = getStrongestSignal();
438 setIcon("status", DhcpcdQt::signalStrengthIcon(scan));
442 void DhcpcdQt::dhcpcd_wpa_scan_cb(DHCPCD_WPA *wpa, void *d)
444 DhcpcdQt *dhcpcdQt = (DhcpcdQt *)d;
446 dhcpcdQt->scanCallback(wpa);
449 void DhcpcdQt::wpaStatusCallback(DHCPCD_WPA *wpa, const char *status)
453 i = dhcpcd_wpa_if(wpa);
454 qDebug("%s: WPA status %s", i->ifname, status);
455 if (strcmp(status, "down") == 0) {
456 DhcpcdWi *wi = findWi(wpa);
464 void DhcpcdQt::dhcpcd_wpa_status_cb(DHCPCD_WPA *wpa, const char *status,
467 DhcpcdQt *dhcpcdQt = (DhcpcdQt *)d;
469 dhcpcdQt->wpaStatusCallback(wpa, status);
472 void DhcpcdQt::tryOpen() {
473 int fd = dhcpcd_open(con, true);
474 static int last_error;
477 if (errno == EACCES || errno == EPERM) {
478 if ((fd = dhcpcd_open(con, false)) != -1)
481 if (errno != last_error) {
483 const char *errt = strerror(errno);
484 qCritical("dhcpcd_open: %s", errt);
485 trayIcon->setToolTip(
486 tr("Error connecting to dhcpcd: %1").arg(errt));
488 if (retryOpenTimer == NULL) {
489 retryOpenTimer = new QTimer(this);
490 connect(retryOpenTimer, SIGNAL(timeout()),
491 this, SLOT(tryOpen()));
492 retryOpenTimer->start(DHCPCD_RETRYOPEN);
498 /* Start listening to WPA events */
499 dhcpcd_wpa_start(con);
501 if (retryOpenTimer) {
502 retryOpenTimer->stop();
503 retryOpenTimer->deleteLater();
504 retryOpenTimer = NULL;
507 notifier = new QSocketNotifier(fd, QSocketNotifier::Read);
508 connect(notifier, SIGNAL(activated(int)), this, SLOT(dispatch()));
510 preferencesAction->setEnabled(dhcpcd_privileged(con));
513 void DhcpcdQt::dispatch()
516 dhcpcd_dispatch(con);
519 void DhcpcdQt::notify(QString &title, QString &msg,
521 QSystemTrayIcon::MessageIcon
523 QSystemTrayIcon::MessageIcon icon
529 KNotification *n = new KNotification("event", this);
534 //trayIcon->showMessage(title, msg, icon);
538 void DhcpcdQt::closeEvent(QCloseEvent *event)
541 if (trayIcon->isVisible()) {
547 QIcon DhcpcdQt::getIcon(QString category, QString name)
551 if (QIcon::hasThemeIcon(name))
552 icon = QIcon::fromTheme(name);
554 icon = QIcon(ICONDIR "/hicolor/scalable/" + category + "/" + name + ".svg");
559 void DhcpcdQt::setIcon(QString category, QString name)
561 QIcon icon = getIcon(category, name);
563 trayIcon->setIcon(icon);
566 QIcon DhcpcdQt::icon()
569 return getIcon("status", "network-transmit-receive");
572 void DhcpcdQt::menuDeleted(QMenu *menu)
575 if (ssidMenu == menu)
579 void DhcpcdQt::createSsidMenu()
583 ssidMenu->deleteLater();
586 if (wis->size() == 0)
589 ssidMenu = new QMenu(this);
590 if (wis->size() == 1)
591 wis->first()->createMenu(ssidMenu);
593 for (auto &wi : *wis)
594 ssidMenu->addMenu(wi->createIfMenu(ssidMenu));
596 ssidMenuPos = QCursor::pos();
597 ssidMenu->popup(ssidMenuPos);
600 void DhcpcdQt::iconActivated(QSystemTrayIcon::ActivationReason reason)
603 if (reason == QSystemTrayIcon::Trigger)
607 void DhcpcdQt::dialogClosed(QDialog *dialog)
612 else if (dialog == preferences)
616 void DhcpcdQt::showPreferences()
619 if (preferences == NULL) {
620 preferences = new DhcpcdPreferences(this);
623 preferences->activateWindow();
626 void DhcpcdQt::showAbout()
630 about = new DhcpcdAbout(this);
633 about->activateWindow();
636 void DhcpcdQt::createActions()
639 preferencesAction = new QAction(tr("&Preferences"), this);
640 preferencesAction->setIcon(QIcon::fromTheme("preferences-system-network"));
641 preferencesAction->setEnabled(false);
642 connect(preferencesAction, SIGNAL(triggered()),
643 this, SLOT(showPreferences()));
645 aboutAction = new QAction(tr("&About"), this);
646 aboutAction->setIcon(QIcon::fromTheme("help-about"));
647 connect(aboutAction, SIGNAL(triggered()), this, SLOT(showAbout()));
649 quitAction = new QAction(tr("&Quit"), this);
650 quitAction->setIcon(QIcon::fromTheme("application-exit"));
651 connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
655 void DhcpcdQt::createTrayIcon()
658 trayIconMenu = new QMenu(this);
659 trayIconMenu->addAction(preferencesAction);
660 trayIconMenu->addSeparator();
661 trayIconMenu->addAction(aboutAction);
662 trayIconMenu->addAction(quitAction);
664 trayIcon = new QSystemTrayIcon(this);
665 setIcon("status", "network-offline");
666 trayIcon->setContextMenu(trayIconMenu);
668 connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
669 this, SLOT(iconActivated(QSystemTrayIcon::ActivationReason)));