From d493e695274d0c429af354899cfdef575d23569a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Sun, 29 Oct 2006 17:28:52 +0000 Subject: [PATCH] Wrote a small application to show the power status in a window, replicant, or the Deskbar tray. Currently only works under BeOS, and has rather ugly graphics; Haiku doesn't have an APM kernel interface yet, and I was too lazy to make any nice icons/drawings :) git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@19145 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/apps/Jamfile | 1 + src/apps/powerstatus/Jamfile | 13 + src/apps/powerstatus/PowerStatus.cpp | 135 ++++++ src/apps/powerstatus/PowerStatus.h | 15 + src/apps/powerstatus/PowerStatus.rdef | 17 + src/apps/powerstatus/PowerStatusView.cpp | 475 +++++++++++++++++++++ src/apps/powerstatus/PowerStatusView.h | 56 +++ src/apps/powerstatus/PowerStatusWindow.cpp | 38 ++ src/apps/powerstatus/PowerStatusWindow.h | 23 + 9 files changed, 773 insertions(+) create mode 100644 src/apps/powerstatus/Jamfile create mode 100644 src/apps/powerstatus/PowerStatus.cpp create mode 100644 src/apps/powerstatus/PowerStatus.h create mode 100644 src/apps/powerstatus/PowerStatus.rdef create mode 100644 src/apps/powerstatus/PowerStatusView.cpp create mode 100644 src/apps/powerstatus/PowerStatusView.h create mode 100644 src/apps/powerstatus/PowerStatusWindow.cpp create mode 100644 src/apps/powerstatus/PowerStatusWindow.h diff --git a/src/apps/Jamfile b/src/apps/Jamfile index 5c6401e101..e4b303afaa 100644 --- a/src/apps/Jamfile +++ b/src/apps/Jamfile @@ -19,6 +19,7 @@ SubInclude HAIKU_TOP src apps mediaplayer ; SubInclude HAIKU_TOP src apps midiplayer ; SubInclude HAIKU_TOP src apps people ; SubInclude HAIKU_TOP src apps poorman ; +SubInclude HAIKU_TOP src apps powerstatus ; SubInclude HAIKU_TOP src apps processcontroller ; SubInclude HAIKU_TOP src apps pulse ; SubInclude HAIKU_TOP src apps showimage ; diff --git a/src/apps/powerstatus/Jamfile b/src/apps/powerstatus/Jamfile new file mode 100644 index 0000000000..dc62de8c59 --- /dev/null +++ b/src/apps/powerstatus/Jamfile @@ -0,0 +1,13 @@ +SubDir HAIKU_TOP src apps powerstatus ; + +SetSubDirSupportedPlatformsBeOSCompatible ; + +UsePrivateHeaders shared ; + +Application PowerStatus : + PowerStatusWindow.cpp + PowerStatusView.cpp + PowerStatus.cpp + : be + : PowerStatus.rdef + ; diff --git a/src/apps/powerstatus/PowerStatus.cpp b/src/apps/powerstatus/PowerStatus.cpp new file mode 100644 index 0000000000..46d40fd4ed --- /dev/null +++ b/src/apps/powerstatus/PowerStatus.cpp @@ -0,0 +1,135 @@ +/* + * Copyright 2006, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Axel Dörfler, axeld@pinc-software.de + */ + + +#include "PowerStatus.h" +#include "PowerStatusWindow.h" + +#include +#include +#include +#include + +#include +#include +#include + + +class PowerStatus : public BApplication { + public: + PowerStatus(); + virtual ~PowerStatus(); + + virtual void ReadyToRun(); + virtual void AboutRequested(); +}; + + +const char* kSignature = "application/x-vnd.Haiku-PowerStatus"; +const char* kDeskbarSignature = "application/x-vnd.Be-TSKB"; +const char* kDeskbarItemName = "PowerStatus"; + + +status_t +our_image(image_info& image) +{ + int32 cookie = 0; + while (get_next_image_info(B_CURRENT_TEAM, &cookie, &image) == B_OK) { + if ((char *)our_image >= (char *)image.text + && (char *)our_image <= (char *)image.text + image.text_size) + return B_OK; + } + + return B_ERROR; +} + + +// #pragma mark - + + +PowerStatus::PowerStatus() + : BApplication(kSignature) +{ +} + + +PowerStatus::~PowerStatus() +{ +} + + +void +PowerStatus::ReadyToRun() +{ + bool isInstalled = false; + bool isDeskbarRunning = true; + + { + // if the Deskbar is not alive at this point, it might be after having + // acknowledged the requester below + BDeskbar deskbar; +#ifdef HAIKU_TARGET_PLATFORM_HAIKU + isDeskbarRunning = deskbar.IsRunning()) +#endif + isInstalled = deskbar.HasItem(kDeskbarItemName); + } + + if (isDeskbarRunning && !isInstalled) { + BAlert* alert = new BAlert("", "Do you want PowerStatus to live in the Deskbar?", + "Don't", "Install", NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT); + alert->SetShortcut(0, B_ESCAPE); + + if (alert->Go()) { + image_info info; + entry_ref ref; + + if (our_image(info) == B_OK + && get_ref_for_path(info.name, &ref) == B_OK) { + BDeskbar deskbar; + deskbar.AddItem(&ref); + } + + Quit(); + return; + } + } + + BWindow* window = new PowerStatusWindow(); + window->Show(); +} + + +void +PowerStatus::AboutRequested() +{ + BWindow* window = WindowAt(0); + if (window == NULL) + return; + + BView* view = window->FindView(kDeskbarItemName); + if (view == NULL) + return; + + BMessenger target((BHandler*)view); + BMessage about(B_ABOUT_REQUESTED); + target.SendMessage(&about); +} + + +// #pragma mark - + + +int +main(int, char**) +{ + PowerStatus app; + app.Run(); + + return 0; +} + diff --git a/src/apps/powerstatus/PowerStatus.h b/src/apps/powerstatus/PowerStatus.h new file mode 100644 index 0000000000..6c4527dda7 --- /dev/null +++ b/src/apps/powerstatus/PowerStatus.h @@ -0,0 +1,15 @@ +/* + * Copyright 2006, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Axel Dörfler, axeld@pinc-software.de + */ +#ifndef POWER_STATUS_H +#define POWER_STATUS_H + + +extern const char* kSignature; +extern const char* kDeskbarItemName; + +#endif // POWER_STATUS_H diff --git a/src/apps/powerstatus/PowerStatus.rdef b/src/apps/powerstatus/PowerStatus.rdef new file mode 100644 index 0000000000..b28b171595 --- /dev/null +++ b/src/apps/powerstatus/PowerStatus.rdef @@ -0,0 +1,17 @@ + +resource(1, "BEOS:APP_SIG") #'MIMS' "application/x-vnd.Haiku-PowerStatus"; + +resource app_flags B_SINGLE_LAUNCH; + +resource app_version { + major = 1, + middle = 0, + minor = 0, + + variety = B_APPV_DEVELOPMENT, + internal = 0, + + short_info = "PowerStatus", + long_info = "PowerStatus ©2006 Haiku" +}; + diff --git a/src/apps/powerstatus/PowerStatusView.cpp b/src/apps/powerstatus/PowerStatusView.cpp new file mode 100644 index 0000000000..c7337f18a2 --- /dev/null +++ b/src/apps/powerstatus/PowerStatusView.cpp @@ -0,0 +1,475 @@ +/* + * Copyright 2006, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Axel Dörfler, axeld@pinc-software.de + */ + + +#include "PowerStatusView.h" +#include "PowerStatus.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + +extern "C" _EXPORT BView *instantiate_deskbar_item(void); + + +const uint32 kMsgUpdate = 'updt'; +const uint32 kMsgToggleLabel = 'tglb'; +const uint32 kMsgToggleTime = 'tgtm'; +const uint32 kMsgToggleStatusIcon = 'tgsi'; + +const uint32 kMinIconWidth = 16; +const uint32 kMinIconHeight = 16; + +const bigtime_t kUpdateInterval = 1000000; + // every second + + +#ifndef HAIKU_TARGET_PLATFORM_HAIKU +// definitions for the APM driver available for BeOS +enum { + APM_CONTROL = B_DEVICE_OP_CODES_END + 1, + APM_DUMP_POWER_STATUS, + APM_BIOS_CALL, + APM_SET_SAFETY +}; + +#define BIOS_APM_GET_POWER_STATUS 0x530a +#endif + + +PowerStatusView::PowerStatusView(BRect frame, int32 resizingMode, bool inDeskbar) + : BView(frame, kDeskbarItemName, resizingMode, + B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE), + fInDeskbar(inDeskbar) +{ + _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(); +} + + +PowerStatusView::PowerStatusView(BMessage* archive) + : BView(archive) +{ + _Init(); + + bool value; + if (archive->FindBool("show label", &value) == B_OK) + fShowLabel = value; + if (archive->FindBool("show icon", &value) == B_OK) + fShowStatusIcon = value; + if (archive->FindBool("show time", &value) == B_OK) + fShowTime = value; +} + + +PowerStatusView::~PowerStatusView() +{ +#ifndef HAIKU_TARGET_PLATFORM_HAIKU + close(fDevice); +#endif +} + + +void +PowerStatusView::_Init() +{ + fShowLabel = true; + fShowTime = false; + fShowStatusIcon = true; + + fMessageRunner = NULL; + fPercent = -1; + fOnline = true; + fTimeLeft = 0; + +#ifdef HAIKU_TARGET_PLATFORM_HAIKU + // TODO: implement me +#else + fDevice = open("/dev/misc/apm", O_RDONLY); + if (fDevice < 0) { + fprintf(stderr, "No power interface found.\n"); + _Quit(); + } +#endif +} + + +void +PowerStatusView::_Quit() +{ + if (fInDeskbar) { + BDeskbar deskbar; + deskbar.RemoveItem(kDeskbarItemName); + } else + be_app->PostMessage(B_QUIT_REQUESTED); +} + + +PowerStatusView * +PowerStatusView::Instantiate(BMessage* archive) +{ + if (!validate_instantiation(archive, "PowerStatusView")) + return NULL; + + return new PowerStatusView(archive); +} + + +status_t +PowerStatusView::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", "PowerStatusView"); + if (status == B_OK) + status = archive->AddBool("show label", fShowLabel); + if (status == B_OK) + status = archive->AddBool("show icon", fShowStatusIcon); + if (status == B_OK) + status = archive->AddBool("show time", fShowTime); + + return status; +} + + +void +PowerStatusView::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); + + _Update(); +} + + +void +PowerStatusView::DetachedFromWindow() +{ + delete fMessageRunner; +} + + +void +PowerStatusView::MessageReceived(BMessage *message) +{ + switch (message->what) { + case kMsgUpdate: + _Update(); + break; + + case kMsgToggleLabel: + fShowLabel = !fShowLabel; + _Update(true); + break; + + case kMsgToggleTime: + fShowTime = !fShowTime; + _Update(true); + break; + + case kMsgToggleStatusIcon: + fShowStatusIcon = !fShowStatusIcon; + _Update(true); + break; + + case B_ABOUT_REQUESTED: + _AboutRequested(); + break; + + case B_QUIT_REQUESTED: + _Quit(); + break; + + default: + BView::MessageReceived(message); + } +} + + +void +PowerStatusView::_DrawBattery(BRect rect) +{ + float quarter = floorf((rect.Height() + 1) / 4); + rect.top += quarter; + rect.bottom -= quarter; + + rect.InsetBy(2, 0); + + float left = rect.left; + rect.left += rect.Width() / 11; + + float gap = 1; + if (rect.Height() > 8) { + rect.InsetBy(1, 1); + SetPenSize(2); + gap = 2; + } + + if (fOnline) + SetHighColor(92, 92, 92); + + StrokeRect(rect); + + SetPenSize(1); + FillRect(BRect(left, floorf(rect.top + rect.Height() / 4) + 1, + rect.left, floorf(rect.bottom - rect.Height() / 4))); + + int32 percent = fPercent; + if (percent > 100 || percent < 0) + percent = 100; + + if (percent > 0) { + if (percent < 16) + SetHighColor(180, 0, 0); + + rect.InsetBy(gap + 1, gap + 1); + if (gap > 1) { + rect.right++; + rect.bottom++; + } + + rect.right = rect.left + rect.Width() * min_c(percent, 100) / 100.0; + FillRect(rect); + } + + SetHighColor(0, 0, 0); +} + + +void +PowerStatusView::Draw(BRect updateRect) +{ + float aspect = Bounds().Width() / Bounds().Height(); + bool below = aspect <= 1.0f; + + font_height fontHeight; + GetFontHeight(&fontHeight); + float baseLine = ceilf(fontHeight.ascent); + + char text[64]; + _SetLabel(text, sizeof(text)); + + float textHeight = ceilf(fontHeight.descent + fontHeight.ascent); + float textWidth = StringWidth(text); + bool showLabel = fShowLabel && text[0]; + + BRect iconRect; + + if (fShowStatusIcon) { + iconRect = Bounds(); + if (showLabel) { + if (below) + iconRect.bottom -= textHeight + 4; + else + iconRect.right -= textWidth + 4; + } + + // make a square + iconRect.bottom = min_c(iconRect.bottom, iconRect.right); + iconRect.right = iconRect.bottom; + + if (iconRect.Width() + 1 >= kMinIconWidth + && iconRect.Height() + 1 >= kMinIconHeight) { + // TODO: have real icons + //if (!fOnline) + _DrawBattery(iconRect); + //else + // FillRect(iconRect); + } else { + // there is not enough space for the icon + iconRect.Set(0, 0, -1, -1); + } + } + + if (showLabel) { + BPoint point(0, baseLine); + + if (iconRect.IsValid()) { + if (below) { + point.x = (iconRect.Width() - textWidth) / 2; + point.y += iconRect.Height() + 2; + } else { + point.x = iconRect.Width() + 2; + point.y += (iconRect.Height() - textHeight) / 2; + } + } else { + point.x = (Bounds().Width() - textWidth) / 2; + point.y += (Bounds().Height() - textHeight) / 2; + } + + DrawString(text, point); + } +} + + +void +PowerStatusView::MouseDown(BPoint point) +{ + BPopUpMenu *menu = new BPopUpMenu(B_EMPTY_STRING, false, false); + menu->SetFont(be_plain_font); + + BMenuItem* item; + menu->AddItem(item = new BMenuItem("Show Text Label", new BMessage(kMsgToggleLabel))); + if (fShowLabel) + item->SetMarked(true); + menu->AddItem(item = new BMenuItem("Show Status Icon", + new BMessage(kMsgToggleStatusIcon))); + if (fShowStatusIcon) + item->SetMarked(true); + menu->AddItem(new BMenuItem(!fShowTime ? "Show Time" : "Show Percent", + new BMessage(kMsgToggleTime))); + + menu->AddSeparatorItem(); + menu->AddItem(new BMenuItem("About" B_UTF8_ELLIPSIS, new BMessage(B_ABOUT_REQUESTED))); + menu->AddItem(new BMenuItem("Quit", new BMessage(B_QUIT_REQUESTED))); + menu->SetTargetForItems(this); + + ConvertToScreen(&point); + menu->Go(point, true, false, true); +} + + +void +PowerStatusView::_AboutRequested() +{ + BAlert *alert = new BAlert("about", "PowerStatus\n" + "\twritten by Axel Dörfler\n" + "\tCopyright 2006, 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, 11, &font); + + alert->Go(); +} + + +void +PowerStatusView::_SetLabel(char* buffer, size_t bufferLength) +{ + if (bufferLength < 1) + return; + + buffer[0] = '\0'; + + if (!fShowLabel) + return; + + const char* open = ""; + const char* close = ""; + if (fOnline) { + open = "("; + close = ")"; + } + + if (!fShowTime && fPercent >= 0) + snprintf(buffer, bufferLength, "%s%ld%%%s", open, fPercent, close); + else if (fShowTime && fTimeLeft >= 0) { + snprintf(buffer, bufferLength, "%s%ld:%ld%s", + open, fTimeLeft / 3600, fTimeLeft / 60, close); + } +} + + +void +PowerStatusView::_Update(bool force) +{ +#ifdef HAIKU_TARGET_PLATFORM_HAIKU + // TODO: retrieve data from APM/ACPI kernel interface + fPercent = 42; + fTimeLeft = 1500; + fOnline = true; +#else + if (fDevice < 0) + return; + + uint16 regs[6] = {0, 0, 0, 0, 0, 0}; + regs[0] = BIOS_APM_GET_POWER_STATUS; + regs[1] = 0x1; + if (ioctl(fDevice, APM_BIOS_CALL, regs) == 0) { + fOnline = (regs[1] >> 8) != 0 && (regs[1] >> 8) != 2; + fPercent = regs[2] & 255; + if (fPercent > 100) + fPercent = -1; + fTimeLeft = fPercent >= 0 ? regs[3] : -1; + if (fTimeLeft > 0xffff) + fTimeLeft = -1; + } +#endif + + if (fInDeskbar) { + // make sure the tray icon is large enough + float width = fShowStatusIcon ? kMinIconWidth + 2 : 0; + + if (fShowLabel) { + char text[64]; + _SetLabel(text, sizeof(text)); + + if (text[0]) + width += ceilf(StringWidth(text)) + 4; + } + if (width == 0) { + // make sure we're not going away completely + width = 8; + } + + if (width != Bounds().Width()) + ResizeTo(width, Bounds().Height()); + } + + if (force) + Invalidate(); +} + + +// #pragma mark - + + +extern "C" _EXPORT BView * +instantiate_deskbar_item(void) +{ + return new PowerStatusView(BRect(0, 0, 15, 15), B_FOLLOW_NONE, true); +} + diff --git a/src/apps/powerstatus/PowerStatusView.h b/src/apps/powerstatus/PowerStatusView.h new file mode 100644 index 0000000000..3c6e5491ed --- /dev/null +++ b/src/apps/powerstatus/PowerStatusView.h @@ -0,0 +1,56 @@ +/* + * Copyright 2006, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Axel Dörfler, axeld@pinc-software.de + */ +#ifndef POWER_STATUS_VIEW_H +#define POWER_STATUS_VIEW_H + + +#include + +class BMessageRunner; + + +class PowerStatusView : public BView { + public: + PowerStatusView(BRect frame, int32 resizingMode, bool inDeskbar = false); + PowerStatusView(BMessage* archive); + virtual ~PowerStatusView(); + + static PowerStatusView* Instantiate(BMessage* archive); + virtual status_t Archive(BMessage* archive, bool deep = true) const; + + virtual void AttachedToWindow(); + virtual void DetachedFromWindow(); + + virtual void MessageReceived(BMessage* message); + virtual void MouseDown(BPoint where); + virtual void Draw(BRect updateRect); + + private: + void _AboutRequested(); + void _Quit(); + void _Init(); + void _SetLabel(char* buffer, size_t bufferLength); + void _Update(bool force = false); + void _DrawBattery(BRect rect); + + BMessageRunner* fMessageRunner; + bool fInDeskbar; + bool fShowLabel; + bool fShowTime; + bool fShowStatusIcon; + + int32 fPercent; + time_t fTimeLeft; + bool fOnline; + +#ifndef HAIKU_TARGET_PLATFORM_HAIKU + int fDevice; +#endif +}; + +#endif // POWER_STATUS_VIEW_H diff --git a/src/apps/powerstatus/PowerStatusWindow.cpp b/src/apps/powerstatus/PowerStatusWindow.cpp new file mode 100644 index 0000000000..fc97156dcd --- /dev/null +++ b/src/apps/powerstatus/PowerStatusWindow.cpp @@ -0,0 +1,38 @@ +/* + * Copyright 2006, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Axel Dörfler, axeld@pinc-software.de + */ + + +#include "PowerStatusWindow.h" +#include "PowerStatusView.h" + +#include + + +PowerStatusWindow::PowerStatusWindow() + : BWindow(BRect(100, 150, 147, 197), "PowerStatus", B_TITLED_WINDOW, + B_NOT_ZOOMABLE | B_ASYNCHRONOUS_CONTROLS) +{ + BView* topView = new BView(Bounds(), NULL, B_FOLLOW_ALL, B_WILL_DRAW); + topView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); + AddChild(topView); + + topView->AddChild(new PowerStatusView(Bounds(), B_FOLLOW_ALL)); +} + + +PowerStatusWindow::~PowerStatusWindow() +{ +} + + +bool +PowerStatusWindow::QuitRequested() +{ + be_app->PostMessage(B_QUIT_REQUESTED); + return true; +} diff --git a/src/apps/powerstatus/PowerStatusWindow.h b/src/apps/powerstatus/PowerStatusWindow.h new file mode 100644 index 0000000000..f3ab170c71 --- /dev/null +++ b/src/apps/powerstatus/PowerStatusWindow.h @@ -0,0 +1,23 @@ +/* + * Copyright 2006, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Axel Dörfler, axeld@pinc-software.de + */ +#ifndef POWER_STATUS_WINDOW_H +#define POWER_STATUS_WINDOW_H + + +#include + + +class PowerStatusWindow : public BWindow { + public: + PowerStatusWindow(); + virtual ~PowerStatusWindow(); + + virtual bool QuitRequested(); +}; + +#endif // POWER_STATUS_WINDOW_H