Files
haiku-beta6/src/apps/networkstatus/NetworkStatusView.cpp
T
Axel Dörfler f01106c3cf * Moved the network status replicant into its own application, similar to what
ProcessController and PowerStatus are doing.
* Currently polls for info only - later, the stack should support listeners for
  interface related changes.
* Also works under BONE (although it doesn't make much sense to use it there).


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@20655 a95241bf-73f2-0310-859d-f6bbb57e9c96
2007-04-11 16:01:02 +00:00

470 lines
9.2 KiB
C++

/*
* Copyright 2006-2007, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Axel Dörfler, [email protected]
* Hugo Santos, [email protected]
*/
#include "NetworkStatusView.h"
#include "NetworkStatus.h"
#include "NetworkStatusIcons.h"
#include <Alert.h>
#include <Application.h>
#include <Bitmap.h>
#include <Deskbar.h>
#include <Dragger.h>
#include <Drivers.h>
#include <IconUtils.h>
#include <MenuItem.h>
#include <MessageRunner.h>
#include <PopUpMenu.h>
#include <Resources.h>
#include <String.h>
#include <TextView.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <unistd.h>
#ifndef HAIKU_TARGET_PLATFORM_HAIKU
// BONE compatibility
# define IF_NAMESIZE IFNAMSIZ
# define IFF_LINK 0
# define IFF_CONFIGURING 0
# define ifc_value ifc_val
#endif
static const char *kStatusDescriptions[] = {
"Unknown",
"No Link",
"No stateful configuration",
"Configuring",
"Ready"
};
extern "C" _EXPORT BView *instantiate_deskbar_item(void);
const uint32 kMsgUpdate = 'updt';
const uint32 kMsgShowConfiguration = 'shcf';
const uint32 kMinIconWidth = 16;
const uint32 kMinIconHeight = 16;
const bigtime_t kUpdateInterval = 1000000;
// every second
NetworkStatusView::NetworkStatusView(BRect frame, int32 resizingMode,
bool inDeskbar)
: BView(frame, kDeskbarItemName, resizingMode,
B_WILL_DRAW | B_FRAME_EVENTS | B_FULL_UPDATE_ON_RESIZE),
fInDeskbar(inDeskbar),
fStatus(kStatusUnknown)
{
_Init();
if (!inDeskbar) {
// we were obviously added to a standard window - let's add a dragger
frame.OffsetTo(B_ORIGIN);
frame.top = frame.bottom - 7;
frame.left = frame.right - 7;
BDragger* dragger = new BDragger(frame, this,
B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM);
AddChild(dragger);
} else
_Update();
}
NetworkStatusView::NetworkStatusView(BMessage* archive)
: BView(archive)
{
_Init();
}
NetworkStatusView::~NetworkStatusView()
{
}
void
NetworkStatusView::_Init()
{
fMessageRunner = NULL;
for (int i = 0; i < kStatusCount; i++) {
fBitmaps[i] = NULL;
}
_UpdateBitmaps();
}
void
NetworkStatusView::_UpdateBitmaps()
{
for (int i = 0; i < kStatusCount; i++) {
delete fBitmaps[i];
fBitmaps[i] = NULL;
}
image_info info;
if (our_image(info) != B_OK)
return;
BFile file(info.name, B_READ_ONLY);
if (file.InitCheck() < B_OK)
return;
BResources resources(&file);
#ifdef HAIKU_TARGET_PLATFORM_HAIKU
if (resources.InitCheck() < B_OK)
return;
#endif
for (int i = 0; i < kStatusCount; i++) {
const void* data = NULL;
size_t size;
data = resources.LoadResource(B_VECTOR_ICON_TYPE,
kNetworkStatusNoDevice + i, &size);
if (data != NULL) {
BBitmap* icon = new BBitmap(Bounds(), B_RGB32);
if (icon->InitCheck() == B_OK
&& BIconUtils::GetVectorIcon((const uint8 *)data,
size, icon) == B_OK) {
fBitmaps[i] = icon;
} else
delete icon;
}
}
}
void
NetworkStatusView::_Quit()
{
if (fInDeskbar) {
BDeskbar deskbar;
deskbar.RemoveItem(kDeskbarItemName);
} else
be_app->PostMessage(B_QUIT_REQUESTED);
}
NetworkStatusView *
NetworkStatusView::Instantiate(BMessage* archive)
{
if (!validate_instantiation(archive, "NetworkStatusView"))
return NULL;
return new NetworkStatusView(archive);
}
status_t
NetworkStatusView::Archive(BMessage* archive, bool deep) const
{
status_t status = BView::Archive(archive, deep);
if (status == B_OK)
status = archive->AddString("add_on", kSignature);
if (status == B_OK)
status = archive->AddString("class", "NetworkStatusView");
return status;
}
void
NetworkStatusView::AttachedToWindow()
{
BView::AttachedToWindow();
if (Parent())
SetViewColor(Parent()->ViewColor());
else
SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
SetLowColor(ViewColor());
BMessage update(kMsgUpdate);
fMessageRunner = new BMessageRunner(this, &update, kUpdateInterval);
fSocket = socket(AF_INET, SOCK_DGRAM, 0);
_Update();
}
void
NetworkStatusView::DetachedFromWindow()
{
delete fMessageRunner;
close(fSocket);
}
void
NetworkStatusView::MessageReceived(BMessage* message)
{
switch (message->what) {
case kMsgUpdate:
_Update();
break;
case kMsgShowConfiguration:
_ShowConfiguration(message);
break;
case B_ABOUT_REQUESTED:
_AboutRequested();
break;
case B_QUIT_REQUESTED:
_Quit();
break;
default:
BView::MessageReceived(message);
}
}
void
NetworkStatusView::FrameResized(float width, float height)
{
_UpdateBitmaps();
}
void
NetworkStatusView::Draw(BRect updateRect)
{
if (fBitmaps[fStatus] == NULL)
return;
SetDrawingMode(B_OP_ALPHA);
DrawBitmap(fBitmaps[fStatus]);
SetDrawingMode(B_OP_COPY);
}
void
NetworkStatusView::_ShowConfiguration(BMessage* message)
{
static const struct information_entry {
const char *label;
int32 control;
} kInformationEntries[] = {
{ "Address", SIOCGIFADDR },
{ "Broadcast", SIOCGIFBRDADDR },
{ "Netmask", SIOCGIFNETMASK },
{ NULL }
};
const char *name;
if (message->FindString("interface", &name) != B_OK)
return;
ifreq request;
if (!_PrepareRequest(request, name))
return;
BString text = name;
text += " information:\n";
size_t boldLength = text.Length();
for (int i = 0; kInformationEntries[i].label; i++) {
if (ioctl(fSocket, kInformationEntries[i].control, &request,
sizeof(request)) < 0) {
continue;
}
char address[32];
sockaddr_in* inetAddress = NULL;
switch (kInformationEntries[i].control) {
case SIOCGIFNETMASK:
inetAddress = (sockaddr_in*)&request.ifr_mask;
break;
default:
inetAddress = (sockaddr_in*)&request.ifr_addr;
break;
}
if (inet_ntop(AF_INET, &inetAddress->sin_addr, address,
sizeof(address)) == NULL) {
return;
}
text += "\n";
text += kInformationEntries[i].label;
text += ": ";
text += address;
}
BAlert* alert = new BAlert(name, text.String(), "Ok");
BTextView* view = alert->TextView();
BFont font;
view->SetStylable(true);
view->GetFont(&font);
font.SetFace(B_BOLD_FACE);
view->SetFontAndColor(0, boldLength, &font);
alert->Go(NULL);
}
void
NetworkStatusView::MouseDown(BPoint point)
{
BPopUpMenu *menu = new BPopUpMenu(B_EMPTY_STRING, false, false);
menu->SetFont(be_plain_font);
for (int32 i = 0; i < fInterfaces.CountItems(); i++) {
BString& name = *fInterfaces.ItemAt(i);
BString label = name;
label += ": ";
label += kStatusDescriptions[
_DetermineInterfaceStatus(name.String())];
BMessage* info = new BMessage(kMsgShowConfiguration);
info->AddString("interface", name.String());
menu->AddItem(new BMenuItem(label.String(), info));
}
menu->AddSeparatorItem();
menu->AddItem(new BMenuItem("About" B_UTF8_ELLIPSIS,
new BMessage(B_ABOUT_REQUESTED)));
if (fInDeskbar)
menu->AddItem(new BMenuItem("Quit", new BMessage(B_QUIT_REQUESTED)));
menu->SetTargetForItems(this);
ConvertToScreen(&point);
menu->Go(point, true, false, true);
}
void
NetworkStatusView::_AboutRequested()
{
BAlert *alert = new BAlert("about", "NetworkStatus\n"
"\twritten by Axel Dörfler and Hugo Santos\n"
"\tCopyright 2007, Haiku, Inc.\n", "Ok");
BTextView *view = alert->TextView();
BFont font;
view->SetStylable(true);
view->GetFont(&font);
font.SetSize(18);
font.SetFace(B_BOLD_FACE);
view->SetFontAndColor(0, 13, &font);
alert->Go();
}
bool
NetworkStatusView::_PrepareRequest(struct ifreq& request, const char* name)
{
if (strlen(name) > IF_NAMESIZE)
return false;
strcpy(request.ifr_name, name);
return true;
}
int32
NetworkStatusView::_DetermineInterfaceStatus(const char* name)
{
ifreq request;
if (!_PrepareRequest(request, name))
return kStatusUnknown;
uint32 flags = 0;
if (ioctl(fSocket, SIOCGIFFLAGS, &request, sizeof(struct ifreq)) == 0)
flags = request.ifr_flags;
int32 status = kStatusNoLink;
// TODO: no kStatusLinkNoConfig yet
if (flags & IFF_CONFIGURING)
status = kStatusConnecting;
else if (flags & (IFF_UP | IFF_LINK) == IFF_UP | IFF_LINK)
status = kStatusReady;
return status;
}
void
NetworkStatusView::_Update(bool force)
{
// iterate over all interfaces and retrieve minimal status
ifconf config;
config.ifc_len = sizeof(config.ifc_value);
if (ioctl(fSocket, SIOCGIFCOUNT, &config, sizeof(struct ifconf)) < 0)
return;
uint32 count = (uint32)config.ifc_value;
if (count == 0)
return;
void *buffer = malloc(count * sizeof(struct ifreq));
if (buffer == NULL)
return;
config.ifc_len = count * sizeof(struct ifreq);
config.ifc_buf = buffer;
if (ioctl(fSocket, SIOCGIFCONF, &config, sizeof(struct ifconf)) < 0)
return;
ifreq *interface = (ifreq *)buffer;
int32 oldStatus = fStatus;
fStatus = kStatusUnknown;
fInterfaces.MakeEmpty();
for (uint32 i = 0; i < count; i++) {
if (strncmp(interface->ifr_name, "loop", 4) && interface->ifr_name[0]) {
fInterfaces.AddItem(new BString(interface->ifr_name));
int32 status = _DetermineInterfaceStatus(interface->ifr_name);
if (status > fStatus)
fStatus = status;
}
interface = (ifreq *)((addr_t)interface + IF_NAMESIZE
+ interface->ifr_addr.sa_len);
}
free(buffer);
if (fStatus != oldStatus)
Invalidate();
}
// #pragma mark -
extern "C" _EXPORT BView *
instantiate_deskbar_item(void)
{
return new NetworkStatusView(BRect(0, 0, 15, 15), B_FOLLOW_NONE, true);
}