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-ssidmenu.h"
48 onLine = carrier = false;
50 aniTimer = new QTimer(this);
51 connect(aniTimer, SIGNAL(timeout()), this, SLOT(animate()));
53 retryOpenTimer = NULL;
58 wis = new QList<DhcpcdWi *>();
61 qDebug("%s", "Connecting ...");
64 qCritical("libdhcpcd: %s", strerror(errno));
68 dhcpcd_set_status_callback(con, dhcpcd_status_cb, this);
69 dhcpcd_set_if_callback(con, dhcpcd_if_cb, this);
70 dhcpcd_wpa_set_scan_callback(con, dhcpcd_wpa_scan_cb, this);
89 void DhcpcdQt::animate()
94 if (aniCounter++ > 6) {
100 if (aniCounter % 2 == 0)
101 icon = "network-idle";
103 icon = "network-transmit-receive";
105 switch(aniCounter++) {
107 icon = "network-transmit";
110 icon = "network-receive";
113 icon = "network-idle";
118 setIcon("status", icon);
121 void DhcpcdQt::updateOnline(bool showIf)
123 bool isOn, isCarrier;
128 isOn = isCarrier = false;
129 ifs = dhcpcd_interfaces(con);
130 for (i = ifs; i; i = i->next) {
131 if (strcmp(i->type, "link") == 0) {
138 msg = dhcpcd_if_message(i, NULL);
143 msgs = QString::fromAscii(msg);
145 msgs += '\n' + QString::fromAscii(msg);
148 qDebug() << i->ifname << i->reason;
151 if (onLine != isOn || carrier != isCarrier) {
157 aniTimer->start(300);
158 } else if (isCarrier) {
160 aniTimer->start(500);
162 setIcon("status", "network-offline");
165 trayIcon->setToolTip(msgs);
168 void DhcpcdQt::statusCallback(const char *status)
171 qDebug("Status changed to %s", status);
172 if (strcmp(status, "down") == 0) {
175 setIcon("status", "network-offline");
180 trayIcon->setToolTip(tr("Not connected to dhcpcd"));
184 if (lastStatus == NULL || strcmp(lastStatus, "down") == 0) {
185 qDebug("Connected to dhcpcd-%s", dhcpcd_version(con));
188 refresh = strcmp(lastStatus, "opened") ? false : true;
189 printf ("refresh %d\n", refresh);
190 updateOnline(refresh);
191 printf ("updated\n");
195 lastStatus = strdup(status);
197 if (strcmp(status, "down") == 0) {
198 if (retryOpenTimer == NULL) {
199 retryOpenTimer = new QTimer(this);
200 connect(retryOpenTimer, SIGNAL(timeout()),
201 this, SLOT(tryOpen()));
202 retryOpenTimer->start(DHCPCD_RETRYOPEN);
207 void DhcpcdQt::dhcpcd_status_cb(_unused DHCPCD_CONNECTION *con,
208 const char *status, void *d)
210 DhcpcdQt *dhcpcdQt = (DhcpcdQt *)d;
212 dhcpcdQt->statusCallback(status);
215 void DhcpcdQt::ifCallback(DHCPCD_IF *i)
222 if (strcmp(i->reason, "RENEW") == 0 ||
223 strcmp(i->reason, "STOP") == 0 ||
224 strcmp(i->reason, "STOPPED") == 0)
227 msg = dhcpcd_if_message(i, &new_msg);
231 QSystemTrayIcon::MessageIcon icon =
232 i->up ? QSystemTrayIcon::Information :
233 QSystemTrayIcon::Warning;
234 trayIcon->showMessage(tr("Network Event"), msg, icon);
240 void DhcpcdQt::dhcpcd_if_cb(DHCPCD_IF *i, void *d)
242 DhcpcdQt *dhcpcdQt = (DhcpcdQt *)d;
244 dhcpcdQt->ifCallback(i);
247 DhcpcdWi *DhcpcdQt::findWi(DHCPCD_WPA *wpa)
250 for (auto &wi : *wis) {
251 if (wi->getWpa() == wpa)
257 void DhcpcdQt::scanCallback(DHCPCD_WPA *wpa)
259 DHCPCD_WI_SCAN *scans, *s1, *s2;
260 int fd = dhcpcd_wpa_get_fd(wpa);
265 qCritical("No fd for WPA");
273 DHCPCD_IF *i = dhcpcd_wpa_if(wpa);
275 qCritical("No interface for WPA");
283 qDebug("%s: Received scan results", i->ifname);
284 scans = dhcpcd_wi_scans(i);
286 wi = new DhcpcdWi(this, wpa);
289 QString title = tr("New Access Point");
291 for (s1 = scans; s1; s1 = s1->next) {
292 for (s2 = wi->getScans(); s2; s2 = s2->next) {
293 if (strcmp(s1->ssid, s2->ssid) == 0)
296 if (!txt.isEmpty()) {
297 title = tr("New Access Points");
310 void DhcpcdQt::dhcpcd_wpa_scan_cb(DHCPCD_WPA *wpa, void *d)
312 DhcpcdQt *dhcpcdQt = (DhcpcdQt *)d;
314 dhcpcdQt->scanCallback(wpa);
317 void DhcpcdQt::tryOpen() {
318 int fd = dhcpcd_open(con);
319 static int last_error;
322 if (errno != last_error) {
324 qCritical("dhcpcd_open: %s", strerror(errno));
326 if (retryOpenTimer == NULL) {
327 retryOpenTimer = new QTimer(this);
328 connect(retryOpenTimer, SIGNAL(timeout()),
329 this, SLOT(tryOpen()));
330 retryOpenTimer->start(DHCPCD_RETRYOPEN);
335 /* Start listening to WPA events */
336 dhcpcd_wpa_start(con);
338 if (retryOpenTimer) {
339 delete retryOpenTimer;
340 retryOpenTimer = NULL;
343 notifier = new QSocketNotifier(fd, QSocketNotifier::Read);
344 connect(notifier, SIGNAL(activated(int)), this, SLOT(dispatch()));
347 void DhcpcdQt::dispatch() {
349 if (dhcpcd_get_fd(con) == -1) {
350 qWarning("dhcpcd connection lost");
354 dhcpcd_dispatch(con);
357 void DhcpcdQt::notify(QString &title, QString &msg,
358 QSystemTrayIcon::MessageIcon icon)
361 qDebug("%s", qPrintable(msg));
362 trayIcon->showMessage(title, msg, icon);
366 void DhcpcdQt::closeEvent(QCloseEvent *event)
369 if (trayIcon->isVisible()) {
375 QIcon DhcpcdQt::getIcon(QString category, QString name)
379 if (QIcon::hasThemeIcon(name))
380 icon = QIcon::fromTheme(name);
382 icon = QIcon(ICONDIR "/hicolor/scalable/" + category + "/" + name + ".svg");
386 void DhcpcdQt::setIcon(QString category, QString name)
388 QIcon icon = getIcon(category, name);
390 trayIcon->setIcon(icon);
393 QIcon DhcpcdQt::icon()
396 return getIcon("status", "network-transmit-receive");
399 void DhcpcdQt::addSsidMenu(QMenu *&menu, DHCPCD_IF *ifp, DhcpcdWi *&wi)
401 DHCPCD_WI_SCAN *scan;
403 for (scan = wi->getScans(); scan; scan = scan->next) {
404 QWidgetAction *wa = new QWidgetAction(menu);
405 DhcpcdSsidMenu *ssidMenu = new DhcpcdSsidMenu(menu, ifp, scan);
406 wa->setDefaultWidget(ssidMenu);
408 connect(ssidMenu, SIGNAL(selected(DHCPCD_IF *, DHCPCD_WI_SCAN *)),
409 this, SLOT(connectSsid(DHCPCD_IF *, DHCPCD_WI_SCAN *)));
413 void DhcpcdQt::connectSsid(DHCPCD_IF *, DHCPCD_WI_SCAN *)
416 QMessageBox::information(this, "Not implemented",
417 "SSID selection is not yet implemented");
420 void DhcpcdQt::createSsidMenu()
429 if (wis->size() == 0)
432 ssidMenu = new QMenu(this);
433 if (wis->size() == 1) {
434 DhcpcdWi *wi = wis->first();
436 ifp = dhcpcd_wpa_if(wpa);
437 addSsidMenu(ssidMenu, ifp, wi);
439 for (auto &wi : *wis) {
441 ifp = dhcpcd_wpa_if(wpa);
443 QMenu *ifmenu = ssidMenu->addMenu(ifp->ifname);
444 addSsidMenu(ifmenu, ifp, wi);
448 ssidMenu->popup(QCursor::pos());
451 void DhcpcdQt::iconActivated(QSystemTrayIcon::ActivationReason reason)
454 if (reason == QSystemTrayIcon::Trigger)
458 void DhcpcdQt::dialogClosed(QDialog *dialog)
463 else if (dialog == preferences)
467 void DhcpcdQt::showPreferences()
470 if (preferences == NULL) {
471 preferences = new DhcpcdPreferences(this);
474 preferences->activateWindow();
477 void DhcpcdQt::showAbout()
481 about = new DhcpcdAbout(this);
484 about->activateWindow();
487 void DhcpcdQt::createActions()
490 preferencesAction = new QAction(tr("&Preferences"), this);
491 preferencesAction->setIcon(QIcon::fromTheme("preferences-system-network"));
492 connect(preferencesAction, SIGNAL(triggered()),
493 this, SLOT(showPreferences()));
495 aboutAction = new QAction(tr("&About"), this);
496 aboutAction->setIcon(QIcon::fromTheme("help-about"));
497 connect(aboutAction, SIGNAL(triggered()), this, SLOT(showAbout()));
499 quitAction = new QAction(tr("&Quit"), this);
500 quitAction->setIcon(QIcon::fromTheme("application-exit"));
501 connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
505 void DhcpcdQt::createTrayIcon()
508 trayIconMenu = new QMenu(this);
509 trayIconMenu->addAction(preferencesAction);
510 trayIconMenu->addSeparator();
511 trayIconMenu->addAction(aboutAction);
512 trayIconMenu->addAction(quitAction);
514 trayIcon = new QSystemTrayIcon(this);
515 setIcon("status", "network-offline");
516 trayIcon->setContextMenu(trayIconMenu);
518 connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
519 this, SLOT(iconActivated(QSystemTrayIcon::ActivationReason)));