443 lines
11 KiB
C++
443 lines
11 KiB
C++
/*
|
|
* Copyright 2008-2009 Haiku Inc. All rights reserved.
|
|
* Distributed under the terms of the MIT license.
|
|
*
|
|
* Authors:
|
|
* Pieter Panman
|
|
*/
|
|
|
|
|
|
#include <Application.h>
|
|
#include <Catalog.h>
|
|
#include <LayoutBuilder.h>
|
|
#include <MenuBar.h>
|
|
#include <ScrollView.h>
|
|
#include <String.h>
|
|
|
|
#include <iostream>
|
|
|
|
#include "DevicesView.h"
|
|
|
|
#undef B_TRANSLATION_CONTEXT
|
|
#define B_TRANSLATION_CONTEXT "DevicesView"
|
|
|
|
DevicesView::DevicesView()
|
|
:
|
|
BView("DevicesView", B_WILL_DRAW | B_FRAME_EVENTS)
|
|
{
|
|
CreateLayout();
|
|
RescanDevices();
|
|
RebuildDevicesOutline();
|
|
}
|
|
|
|
|
|
void
|
|
DevicesView::CreateLayout()
|
|
{
|
|
BMenuBar* menuBar = new BMenuBar("menu");
|
|
BMenu* menu = new BMenu(B_TRANSLATE("Devices"));
|
|
BMenuItem* item;
|
|
menu->AddItem(new BMenuItem(B_TRANSLATE("Refresh devices"),
|
|
new BMessage(kMsgRefresh), 'R'));
|
|
menu->AddItem(item = new BMenuItem(B_TRANSLATE("Report compatibility"),
|
|
new BMessage(kMsgReportCompatibility)));
|
|
item->SetEnabled(false);
|
|
menu->AddItem(item = new BMenuItem(B_TRANSLATE(
|
|
"Generate system information"), new BMessage(kMsgGenerateSysInfo)));
|
|
item->SetEnabled(false);
|
|
menu->AddSeparatorItem();
|
|
menu->AddItem(new BMenuItem(B_TRANSLATE("Quit"),
|
|
new BMessage(B_QUIT_REQUESTED), 'Q'));
|
|
menu->SetTargetForItems(this);
|
|
item->SetTarget(be_app);
|
|
menuBar->AddItem(menu);
|
|
|
|
fDevicesOutline = new BOutlineListView("devices_list");
|
|
fDevicesOutline->SetTarget(this);
|
|
fDevicesOutline->SetSelectionMessage(new BMessage(kMsgSelectionChanged));
|
|
|
|
BScrollView *scrollView = new BScrollView("devicesScrollView",
|
|
fDevicesOutline, B_WILL_DRAW | B_FRAME_EVENTS, true, true);
|
|
// Horizontal scrollbar doesn't behave properly like the vertical
|
|
// scrollbar... If you make the view bigger (exposing a larger percentage
|
|
// of the view), it does not adjust the width of the scroll 'dragger'
|
|
// why? Bug? In scrollview or in outlinelistview?
|
|
|
|
BPopUpMenu* orderByPopupMenu = new BPopUpMenu("orderByMenu");
|
|
BMenuItem* byCategory = new BMenuItem(B_TRANSLATE("Category"),
|
|
new BMessage(kMsgOrderCategory));
|
|
BMenuItem* byConnection = new BMenuItem(B_TRANSLATE("Connection"),
|
|
new BMessage(kMsgOrderConnection));
|
|
byCategory->SetMarked(true);
|
|
fOrderBy = byCategory->IsMarked() ? ORDER_BY_CATEGORY :
|
|
ORDER_BY_CONNECTION;
|
|
orderByPopupMenu->AddItem(byCategory);
|
|
orderByPopupMenu->AddItem(byConnection);
|
|
fOrderByMenu = new BMenuField(B_TRANSLATE("Order by:"), orderByPopupMenu);
|
|
|
|
fTabView = new BTabView("fTabView", B_WIDTH_FROM_LABEL);
|
|
|
|
fBasicTab = new BTab();
|
|
fBasicView = new PropertyListPlain("basicView");
|
|
fTabView->AddTab(fBasicView, fBasicTab);
|
|
fBasicTab->SetLabel(B_TRANSLATE("Basic information"));
|
|
|
|
fDeviceTypeTab = new BTab();
|
|
fBusView = new PropertyListPlain("busView");
|
|
fTabView->AddTab(fBusView, fDeviceTypeTab);
|
|
fDeviceTypeTab->SetLabel(B_TRANSLATE("Bus"));
|
|
|
|
fDetailedTab = new BTab();
|
|
fAttributesView = new PropertyList("attributesView");
|
|
fTabView->AddTab(fAttributesView, fDetailedTab);
|
|
fDetailedTab->SetLabel(B_TRANSLATE("Detailed"));
|
|
|
|
BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
|
|
.Add(menuBar)
|
|
.AddSplit(B_HORIZONTAL)
|
|
.SetInsets(B_USE_WINDOW_SPACING)
|
|
.AddGroup(B_VERTICAL)
|
|
.Add(fOrderByMenu, 1)
|
|
.Add(scrollView, 2)
|
|
.End()
|
|
.Add(fTabView, 2);
|
|
}
|
|
|
|
|
|
void
|
|
DevicesView::RescanDevices()
|
|
{
|
|
// Empty the outline and delete the devices in the list, incl. categories
|
|
fDevicesOutline->MakeEmpty();
|
|
DeleteDevices();
|
|
DeleteCategoryMap();
|
|
|
|
// Fill the devices list
|
|
status_t error;
|
|
device_node_cookie rootCookie;
|
|
if ((error = init_dm_wrapper()) < 0) {
|
|
std::cerr << "Error initializing device manager: " << strerror(error)
|
|
<< std::endl;
|
|
return;
|
|
}
|
|
|
|
get_root(&rootCookie);
|
|
AddDeviceAndChildren(&rootCookie, NULL);
|
|
|
|
uninit_dm_wrapper();
|
|
|
|
CreateCategoryMap();
|
|
}
|
|
|
|
|
|
void
|
|
DevicesView::DeleteDevices()
|
|
{
|
|
while (fDevices.size() > 0) {
|
|
delete fDevices.back();
|
|
fDevices.pop_back();
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
DevicesView::CreateCategoryMap()
|
|
{
|
|
CategoryMapIterator iter;
|
|
for (unsigned int i = 0; i < fDevices.size(); i++) {
|
|
Category category = fDevices[i]->GetCategory();
|
|
if (category < 0 || category >= kCategoryStringLength) {
|
|
std::cerr << "CreateCategoryMap: device " << fDevices[i]->GetName()
|
|
<< " returned an unknown category index (" << category << "). "
|
|
<< "Skipping device." << std::endl;
|
|
continue;
|
|
}
|
|
|
|
const char* categoryName = kCategoryString[category];
|
|
|
|
iter = fCategoryMap.find(category);
|
|
if (iter == fCategoryMap.end()) {
|
|
// This category has not yet been added, add it.
|
|
fCategoryMap[category] = new Device(NULL, BUS_NONE, CAT_NONE,
|
|
categoryName);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
DevicesView::DeleteCategoryMap()
|
|
{
|
|
CategoryMapIterator iter;
|
|
for (iter = fCategoryMap.begin(); iter != fCategoryMap.end(); iter++) {
|
|
delete iter->second;
|
|
}
|
|
fCategoryMap.clear();
|
|
}
|
|
|
|
|
|
int
|
|
DevicesView::SortItemsCompare(const BListItem *item1,
|
|
const BListItem *item2)
|
|
{
|
|
const BStringItem* stringItem1 = dynamic_cast<const BStringItem*>(item1);
|
|
const BStringItem* stringItem2 = dynamic_cast<const BStringItem*>(item2);
|
|
if (!(stringItem1 && stringItem2)) {
|
|
// is this check necessary?
|
|
std::cerr << "Could not cast BListItem to BStringItem, file a bug\n";
|
|
return 0;
|
|
}
|
|
return Compare(stringItem1->Text(), stringItem2->Text());
|
|
}
|
|
|
|
|
|
void
|
|
DevicesView::RebuildDevicesOutline()
|
|
{
|
|
// Rearranges existing Devices into the proper hierarchy
|
|
fDevicesOutline->MakeEmpty();
|
|
|
|
if (fOrderBy == ORDER_BY_CONNECTION) {
|
|
for (unsigned int i = 0; i < fDevices.size(); i++) {
|
|
if (fDevices[i]->GetPhysicalParent() == NULL) {
|
|
// process each parent device and its children
|
|
fDevicesOutline->AddItem(fDevices[i]);
|
|
AddChildrenToOutlineByConnection(fDevices[i]);
|
|
}
|
|
}
|
|
} else if (fOrderBy == ORDER_BY_CATEGORY) {
|
|
// Add all categories to the outline
|
|
CategoryMapIterator iter;
|
|
for (iter = fCategoryMap.begin(); iter != fCategoryMap.end(); iter++) {
|
|
fDevicesOutline->AddItem(iter->second);
|
|
}
|
|
|
|
// Add all devices under the categories
|
|
for (unsigned int i = 0; i < fDevices.size(); i++) {
|
|
Category category = fDevices[i]->GetCategory();
|
|
|
|
iter = fCategoryMap.find(category);
|
|
if (iter == fCategoryMap.end()) {
|
|
std::cerr
|
|
<< "Tried to add device without category, file a bug\n";
|
|
continue;
|
|
} else {
|
|
fDevicesOutline->AddUnder(fDevices[i], iter->second);
|
|
}
|
|
}
|
|
fDevicesOutline->SortItemsUnder(NULL, true, SortItemsCompare);
|
|
}
|
|
// TODO: Implement BY_BUS
|
|
}
|
|
|
|
|
|
void
|
|
DevicesView::AddChildrenToOutlineByConnection(Device* parent)
|
|
{
|
|
for (unsigned int i = 0; i < fDevices.size(); i++) {
|
|
if (fDevices[i]->GetPhysicalParent() == parent) {
|
|
fDevicesOutline->AddUnder(fDevices[i], parent);
|
|
AddChildrenToOutlineByConnection(fDevices[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
DevicesView::AddDeviceAndChildren(device_node_cookie *node, Device* parent)
|
|
{
|
|
Attributes attributes;
|
|
Device* newDevice = NULL;
|
|
|
|
// Copy all its attributes,
|
|
// necessary because we can only request them once from the device manager
|
|
char data[256];
|
|
struct device_attr_info attr;
|
|
attr.cookie = 0;
|
|
attr.node_cookie = *node;
|
|
attr.value.raw.data = data;
|
|
attr.value.raw.length = sizeof(data);
|
|
|
|
while (dm_get_next_attr(&attr) == B_OK) {
|
|
BString attrString;
|
|
switch (attr.type) {
|
|
case B_STRING_TYPE:
|
|
attrString << attr.value.string;
|
|
break;
|
|
case B_UINT8_TYPE:
|
|
attrString << attr.value.ui8;
|
|
break;
|
|
case B_UINT16_TYPE:
|
|
attrString << attr.value.ui16;
|
|
break;
|
|
case B_UINT32_TYPE:
|
|
attrString << attr.value.ui32;
|
|
break;
|
|
case B_UINT64_TYPE:
|
|
attrString << attr.value.ui64;
|
|
break;
|
|
default:
|
|
attrString << "Raw data";
|
|
}
|
|
attributes.push_back(Attribute(attr.name, attrString));
|
|
}
|
|
|
|
// Determine what type of device it is and create it
|
|
for (unsigned int i = 0; i < attributes.size(); i++) {
|
|
// Devices Root
|
|
if (attributes[i].fName == B_DEVICE_PRETTY_NAME
|
|
&& attributes[i].fValue == "Devices Root") {
|
|
newDevice = new Device(parent, BUS_NONE,
|
|
CAT_COMPUTER, B_TRANSLATE("Computer"));
|
|
break;
|
|
}
|
|
|
|
// ACPI Controller
|
|
if (attributes[i].fName == B_DEVICE_PRETTY_NAME
|
|
&& attributes[i].fValue == "ACPI") {
|
|
newDevice = new Device(parent, BUS_ACPI,
|
|
CAT_BUS, B_TRANSLATE("ACPI bus"));
|
|
break;
|
|
}
|
|
|
|
// PCI bus
|
|
if (attributes[i].fName == B_DEVICE_PRETTY_NAME
|
|
&& attributes[i].fValue == "PCI") {
|
|
newDevice = new Device(parent, BUS_PCI,
|
|
CAT_BUS, B_TRANSLATE("PCI bus"));
|
|
break;
|
|
}
|
|
|
|
// ISA bus
|
|
if (attributes[i].fName == B_DEVICE_BUS
|
|
&& attributes[i].fValue == "isa") {
|
|
newDevice = new Device(parent, BUS_ISA,
|
|
CAT_BUS, B_TRANSLATE("ISA bus"));
|
|
break;
|
|
}
|
|
|
|
// PCI device
|
|
if (attributes[i].fName == B_DEVICE_BUS
|
|
&& attributes[i].fValue == "pci") {
|
|
newDevice = new DevicePCI(parent);
|
|
break;
|
|
}
|
|
|
|
// ACPI device
|
|
if (attributes[i].fName == B_DEVICE_BUS
|
|
&& attributes[i].fValue == "acpi") {
|
|
newDevice = new DeviceACPI(parent);
|
|
break;
|
|
}
|
|
|
|
// ATA / SCSI / IDE controller
|
|
if (attributes[i].fName == "controller_name") {
|
|
newDevice = new Device(parent, BUS_PCI,
|
|
CAT_MASS, attributes[i].fValue);
|
|
}
|
|
|
|
// SCSI device node
|
|
if (attributes[i].fName == B_DEVICE_BUS
|
|
&& attributes[i].fValue == "scsi") {
|
|
newDevice = new DeviceSCSI(parent);
|
|
break;
|
|
}
|
|
|
|
// Last resort, lets look for a pretty name
|
|
if (attributes[i].fName == B_DEVICE_PRETTY_NAME) {
|
|
newDevice = new Device(parent, BUS_NONE,
|
|
CAT_NONE, attributes[i].fValue);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// A completely unknown device
|
|
if (newDevice == NULL) {
|
|
newDevice = new Device(parent, BUS_NONE,
|
|
CAT_NONE, B_TRANSLATE("Unknown device"));
|
|
}
|
|
|
|
// Add its attributes to the device, initialize it and add to the list.
|
|
for (unsigned int i = 0; i < attributes.size(); i++) {
|
|
newDevice->SetAttribute(attributes[i].fName, attributes[i].fValue);
|
|
}
|
|
newDevice->InitFromAttributes();
|
|
fDevices.push_back(newDevice);
|
|
|
|
// Process children
|
|
status_t err;
|
|
device_node_cookie child = *node;
|
|
|
|
if (get_child(&child) != B_OK)
|
|
return;
|
|
|
|
do {
|
|
AddDeviceAndChildren(&child, newDevice);
|
|
} while ((err = get_next_child(&child)) == B_OK);
|
|
}
|
|
|
|
|
|
DevicesView::~DevicesView()
|
|
{
|
|
DeleteDevices();
|
|
}
|
|
|
|
|
|
void
|
|
DevicesView::MessageReceived(BMessage *msg)
|
|
{
|
|
switch (msg->what) {
|
|
case kMsgSelectionChanged:
|
|
{
|
|
int32 selected = fDevicesOutline->CurrentSelection(0);
|
|
if (selected >= 0) {
|
|
Device* device = (Device*)fDevicesOutline->ItemAt(selected);
|
|
fBasicView->AddAttributes(device->GetBasicAttributes());
|
|
fBusView->AddAttributes(device->GetBusAttributes());
|
|
fAttributesView->AddAttributes(device->GetAllAttributes());
|
|
fDeviceTypeTab->SetLabel(device->GetBusTabName());
|
|
// hmm the label doesn't automatically refresh
|
|
fTabView->Invalidate();
|
|
}
|
|
break;
|
|
}
|
|
|
|
case kMsgOrderCategory:
|
|
{
|
|
fOrderBy = ORDER_BY_CATEGORY;
|
|
RescanDevices();
|
|
RebuildDevicesOutline();
|
|
break;
|
|
}
|
|
|
|
case kMsgOrderConnection:
|
|
{
|
|
fOrderBy = ORDER_BY_CONNECTION;
|
|
RescanDevices();
|
|
RebuildDevicesOutline();
|
|
break;
|
|
}
|
|
|
|
case kMsgRefresh:
|
|
{
|
|
RescanDevices();
|
|
RebuildDevicesOutline();
|
|
break;
|
|
}
|
|
|
|
case kMsgReportCompatibility:
|
|
{
|
|
break;
|
|
}
|
|
|
|
case kMsgGenerateSysInfo:
|
|
{
|
|
break;
|
|
}
|
|
|
|
default:
|
|
BView::MessageReceived(msg);
|
|
break;
|
|
}
|
|
}
|