1 /* 2 * Copyright 2006-2015, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Axel Dörfler, axeld@pinc-software.de 7 * Clemens Zeidler, haiku@Clemens-Zeidler.de 8 * Alexander von Gluck, kallisti5@unixzen.com 9 */ 10 11 12 #include "PowerStatusView.h" 13 14 #include <stdio.h> 15 #include <stdlib.h> 16 #include <string.h> 17 #include <unistd.h> 18 19 #include <AboutWindow.h> 20 #include <Application.h> 21 #include <Bitmap.h> 22 #include <Catalog.h> 23 #include <ControlLook.h> 24 #include <DataIO.h> 25 #include <Deskbar.h> 26 #include <Dragger.h> 27 #include <Drivers.h> 28 #include <File.h> 29 #include <FindDirectory.h> 30 #include <MenuItem.h> 31 #include <MessageRunner.h> 32 #include <Notification.h> 33 #include <Path.h> 34 #include <PopUpMenu.h> 35 #include <Resources.h> 36 #include <TextView.h> 37 #include <TranslationUtils.h> 38 39 #include "ACPIDriverInterface.h" 40 #include "APMDriverInterface.h" 41 #include "ExtendedInfoWindow.h" 42 #include "PowerStatus.h" 43 44 45 #undef B_TRANSLATION_CONTEXT 46 #define B_TRANSLATION_CONTEXT "PowerStatus" 47 48 49 extern "C" _EXPORT BView *instantiate_deskbar_item(void); 50 extern const char* kDeskbarItemName; 51 52 const uint32 kMsgToggleLabel = 'tglb'; 53 const uint32 kMsgToggleTime = 'tgtm'; 54 const uint32 kMsgToggleStatusIcon = 'tgsi'; 55 const uint32 kMsgToggleExtInfo = 'texi'; 56 57 const uint32 kMinIconWidth = 16; 58 const uint32 kMinIconHeight = 16; 59 60 const int32 kLowBatteryPercentage = 15; 61 const int32 kNoteBatteryPercentage = 30; 62 63 64 PowerStatusView::PowerStatusView(PowerStatusDriverInterface* interface, 65 BRect frame, int32 resizingMode, int batteryID, bool inDeskbar) 66 : 67 BView(frame, kDeskbarItemName, resizingMode, 68 B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE), 69 fDriverInterface(interface), 70 fBatteryID(batteryID), 71 fInDeskbar(inDeskbar) 72 { 73 _Init(); 74 } 75 76 77 PowerStatusView::PowerStatusView(BMessage* archive) 78 : BView(archive) 79 { 80 _Init(); 81 FromMessage(archive); 82 } 83 84 85 PowerStatusView::~PowerStatusView() 86 { 87 } 88 89 90 status_t 91 PowerStatusView::Archive(BMessage* archive, bool deep) const 92 { 93 status_t status = BView::Archive(archive, deep); 94 if (status == B_OK) 95 status = ToMessage(archive); 96 97 return status; 98 } 99 100 101 void 102 PowerStatusView::_Init() 103 { 104 SetViewColor(B_TRANSPARENT_COLOR); 105 106 fShowLabel = true; 107 fShowTime = false; 108 fShowStatusIcon = true; 109 110 fPercent = 100; 111 fOnline = true; 112 fTimeLeft = 0; 113 } 114 115 116 void 117 PowerStatusView::AttachedToWindow() 118 { 119 BView::AttachedToWindow(); 120 if (Parent()) 121 SetLowColor(Parent()->ViewColor()); 122 else 123 SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 124 125 Update(); 126 } 127 128 129 void 130 PowerStatusView::DetachedFromWindow() 131 { 132 } 133 134 135 void 136 PowerStatusView::MessageReceived(BMessage *message) 137 { 138 switch (message->what) { 139 case kMsgUpdate: 140 Update(); 141 break; 142 143 default: 144 BView::MessageReceived(message); 145 } 146 } 147 148 149 void 150 PowerStatusView::_DrawBattery(BRect rect) 151 { 152 float quarter = floorf((rect.Height() + 1) / 4); 153 rect.top += quarter; 154 rect.bottom -= quarter; 155 156 rect.InsetBy(2, 0); 157 158 float left = rect.left; 159 rect.left += rect.Width() / 11; 160 161 SetHighColor(0, 0, 0); 162 163 float gap = 1; 164 if (rect.Height() > 8) { 165 gap = ceilf((rect.left - left) / 2); 166 167 // left 168 FillRect(BRect(rect.left, rect.top, rect.left + gap - 1, rect.bottom)); 169 // right 170 FillRect(BRect(rect.right - gap + 1, rect.top, rect.right, 171 rect.bottom)); 172 // top 173 FillRect(BRect(rect.left + gap, rect.top, rect.right - gap, 174 rect.top + gap - 1)); 175 // bottom 176 FillRect(BRect(rect.left + gap, rect.bottom + 1 - gap, 177 rect.right - gap, rect.bottom)); 178 } else 179 StrokeRect(rect); 180 181 FillRect(BRect(left, floorf(rect.top + rect.Height() / 4) + 1, 182 rect.left - 1, floorf(rect.bottom - rect.Height() / 4))); 183 184 int32 percent = fPercent; 185 if (percent > 100) 186 percent = 100; 187 else if (percent < 0 || !fHasBattery) 188 percent = 0; 189 190 if (percent > 0) { 191 rect.InsetBy(gap, gap); 192 rgb_color base = {84, 84, 84, 255}; 193 if (be_control_look != NULL) { 194 BRect empty = rect; 195 if (fHasBattery && percent > 0) 196 empty.left += empty.Width() * percent / 100.0; 197 198 be_control_look->DrawButtonBackground(this, empty, empty, base, 199 fHasBattery 200 ? BControlLook::B_ACTIVATED : BControlLook::B_DISABLED, 201 fHasBattery && percent > 0 202 ? (BControlLook::B_ALL_BORDERS 203 & ~BControlLook::B_LEFT_BORDER) 204 : BControlLook::B_ALL_BORDERS); 205 } 206 207 if (fHasBattery) { 208 if (percent <= kLowBatteryPercentage) 209 base.set_to(180, 0, 0); 210 else if (percent <= kNoteBatteryPercentage) 211 base.set_to(200, 140, 0); 212 else 213 base.set_to(20, 180, 0); 214 215 rect.right = rect.left + rect.Width() * percent / 100.0; 216 217 if (be_control_look != NULL) { 218 be_control_look->DrawButtonBackground(this, rect, rect, base, 219 fHasBattery ? 0 : BControlLook::B_DISABLED); 220 } else 221 FillRect(rect); 222 } 223 } 224 225 SetHighColor(0, 0, 0); 226 } 227 228 229 void 230 PowerStatusView::Draw(BRect updateRect) 231 { 232 bool drawBackground = Parent() == NULL 233 || (Parent()->Flags() & B_DRAW_ON_CHILDREN) == 0; 234 if (drawBackground) 235 FillRect(updateRect, B_SOLID_LOW); 236 237 float aspect = Bounds().Width() / Bounds().Height(); 238 bool below = aspect <= 1.0f; 239 240 font_height fontHeight; 241 GetFontHeight(&fontHeight); 242 float baseLine = ceilf(fontHeight.ascent); 243 244 char text[64]; 245 _SetLabel(text, sizeof(text)); 246 247 float textHeight = ceilf(fontHeight.descent + fontHeight.ascent); 248 float textWidth = StringWidth(text); 249 bool showLabel = fShowLabel && text[0]; 250 251 BRect iconRect; 252 253 if (fShowStatusIcon) { 254 iconRect = Bounds(); 255 if (showLabel) { 256 if (below) 257 iconRect.bottom -= textHeight + 4; 258 else 259 iconRect.right -= textWidth + 4; 260 } 261 262 // make a square 263 iconRect.bottom = min_c(iconRect.bottom, iconRect.right); 264 iconRect.right = iconRect.bottom; 265 266 if (iconRect.Width() + 1 >= kMinIconWidth 267 && iconRect.Height() + 1 >= kMinIconHeight) { 268 _DrawBattery(iconRect); 269 } else { 270 // there is not enough space for the icon 271 iconRect.Set(0, 0, -1, -1); 272 } 273 } 274 275 if (showLabel) { 276 BPoint point(0, baseLine); 277 278 if (iconRect.IsValid()) { 279 if (below) { 280 point.x = (iconRect.Width() - textWidth) / 2; 281 point.y += iconRect.Height() + 2; 282 } else { 283 point.x = iconRect.Width() + 2; 284 point.y += (iconRect.Height() - textHeight) / 2; 285 } 286 } else { 287 point.x = (Bounds().Width() - textWidth) / 2; 288 point.y += (Bounds().Height() - textHeight) / 2; 289 } 290 291 if (drawBackground) 292 SetHighColor(ui_color(B_CONTROL_TEXT_COLOR)); 293 else { 294 SetDrawingMode(B_OP_OVER); 295 rgb_color c = Parent()->LowColor(); 296 if (c.red + c.green + c.blue > 128 * 3) 297 SetHighColor(0, 0, 0); 298 else 299 SetHighColor(255, 255, 255); 300 } 301 302 DrawString(text, point); 303 } 304 } 305 306 307 void 308 PowerStatusView::_SetLabel(char* buffer, size_t bufferLength) 309 { 310 if (bufferLength < 1) 311 return; 312 313 buffer[0] = '\0'; 314 315 if (!fShowLabel) 316 return; 317 318 const char* open = ""; 319 const char* close = ""; 320 if (fOnline) { 321 open = "("; 322 close = ")"; 323 } 324 325 if (!fShowTime && fPercent >= 0) { 326 snprintf(buffer, bufferLength, "%s%" B_PRId32 "%%%s", open, fPercent, 327 close); 328 } else if (fShowTime && fTimeLeft >= 0) { 329 snprintf(buffer, bufferLength, "%s%" B_PRId32 ":%02" B_PRId32 "%s", 330 open, fTimeLeft / 3600, (fTimeLeft / 60) % 60, close); 331 } 332 } 333 334 335 336 void 337 PowerStatusView::Update(bool force) 338 { 339 int32 previousPercent = fPercent; 340 time_t previousTimeLeft = fTimeLeft; 341 bool wasOnline = fOnline; 342 bool hadBattery = fHasBattery; 343 344 _GetBatteryInfo(fBatteryID, &fBatteryInfo); 345 fHasBattery = fBatteryInfo.full_capacity > 0; 346 347 if (fBatteryInfo.full_capacity > 0 && fHasBattery) { 348 fPercent = (100 * fBatteryInfo.capacity) / fBatteryInfo.full_capacity; 349 fOnline = (fBatteryInfo.state & BATTERY_DISCHARGING) == 0; 350 fTimeLeft = fBatteryInfo.time_left; 351 } else { 352 fPercent = 0; 353 fOnline = false; 354 fTimeLeft = -1; 355 } 356 357 if (fHasBattery && (fPercent <= 0 || fPercent > 100)) { 358 // Just ignore this probe -- it obviously returned invalid values 359 fPercent = previousPercent; 360 fTimeLeft = previousTimeLeft; 361 fOnline = wasOnline; 362 fHasBattery = hadBattery; 363 return; 364 } 365 366 if (fInDeskbar) { 367 // make sure the tray icon is large enough 368 float width = fShowStatusIcon ? kMinIconWidth + 2 : 0; 369 370 if (fShowLabel) { 371 char text[64]; 372 _SetLabel(text, sizeof(text)); 373 374 if (text[0]) 375 width += ceilf(StringWidth(text)) + 4; 376 } else { 377 char text[256]; 378 const char* open = ""; 379 const char* close = ""; 380 if (fOnline) { 381 open = "("; 382 close = ")"; 383 } 384 if (fHasBattery) { 385 size_t length = snprintf(text, sizeof(text), "%s%" B_PRId32 386 "%%%s", open, fPercent, close); 387 if (fTimeLeft >= 0) { 388 length += snprintf(text + length, sizeof(text) - length, 389 "\n%" B_PRId32 ":%02" B_PRId32, fTimeLeft / 3600, 390 (fTimeLeft / 60) % 60); 391 } 392 393 const char* state = NULL; 394 if ((fBatteryInfo.state & BATTERY_CHARGING) != 0) 395 state = B_TRANSLATE("charging"); 396 else if ((fBatteryInfo.state & BATTERY_DISCHARGING) != 0) 397 state = B_TRANSLATE("discharging"); 398 399 if (state != NULL) { 400 snprintf(text + length, sizeof(text) - length, "\n%s", 401 state); 402 } 403 } else 404 strcpy(text, B_TRANSLATE("no battery")); 405 SetToolTip(text); 406 } 407 if (width == 0) { 408 // make sure we're not going away completely 409 width = 8; 410 } 411 412 if (width != Bounds().Width()) 413 ResizeTo(width, Bounds().Height()); 414 } 415 416 if (force || wasOnline != fOnline 417 || (fShowTime && fTimeLeft != previousTimeLeft) 418 || (!fShowTime && fPercent != previousPercent)) { 419 Invalidate(); 420 } 421 422 if (!fOnline && fHasBattery && previousPercent > kLowBatteryPercentage 423 && fPercent <= kLowBatteryPercentage) { 424 _NotifyLowBattery(); 425 } 426 } 427 428 429 void 430 PowerStatusView::FromMessage(const BMessage* archive) 431 { 432 bool value; 433 if (archive->FindBool("show label", &value) == B_OK) 434 fShowLabel = value; 435 if (archive->FindBool("show icon", &value) == B_OK) 436 fShowStatusIcon = value; 437 if (archive->FindBool("show time", &value) == B_OK) 438 fShowTime = value; 439 440 //Incase we have a bad saving and none are showed.. 441 if (!fShowLabel && !fShowStatusIcon) 442 fShowLabel = true; 443 444 int32 intValue; 445 if (archive->FindInt32("battery id", &intValue) == B_OK) 446 fBatteryID = intValue; 447 } 448 449 450 status_t 451 PowerStatusView::ToMessage(BMessage* archive) const 452 { 453 status_t status = archive->AddBool("show label", fShowLabel); 454 if (status == B_OK) 455 status = archive->AddBool("show icon", fShowStatusIcon); 456 if (status == B_OK) 457 status = archive->AddBool("show time", fShowTime); 458 if (status == B_OK) 459 status = archive->AddInt32("battery id", fBatteryID); 460 461 return status; 462 } 463 464 465 void 466 PowerStatusView::_GetBatteryInfo(int batteryID, battery_info* batteryInfo) 467 { 468 if (batteryID >= 0) { 469 fDriverInterface->GetBatteryInfo(batteryID, batteryInfo); 470 } else { 471 bool first = true; 472 memset(batteryInfo, 0, sizeof(battery_info)); 473 474 for (int i = 0; i < fDriverInterface->GetBatteryCount(); i++) { 475 battery_info info; 476 fDriverInterface->GetBatteryInfo(i, &info); 477 478 if (info.full_capacity <= 0) 479 continue; 480 481 if (first) { 482 *batteryInfo = info; 483 first = false; 484 } else { 485 batteryInfo->state |= info.state; 486 batteryInfo->capacity += info.capacity; 487 batteryInfo->full_capacity += info.full_capacity; 488 batteryInfo->time_left += info.time_left; 489 } 490 } 491 } 492 } 493 494 495 void 496 PowerStatusView::_NotifyLowBattery() 497 { 498 BBitmap* bitmap = NULL; 499 BResources resources; 500 resources.SetToImage((void*)&instantiate_deskbar_item); 501 502 if (resources.InitCheck() == B_OK) { 503 size_t resourceSize = 0; 504 const void* resourceData = resources.LoadResource( 505 B_VECTOR_ICON_TYPE, fHasBattery 506 ? "battery_low" : "battery_critical", &resourceSize); 507 if (resourceData != NULL) { 508 BMemoryIO memoryIO(resourceData, resourceSize); 509 bitmap = BTranslationUtils::GetBitmap(&memoryIO); 510 } 511 } 512 513 BNotification notification( 514 fHasBattery ? B_INFORMATION_NOTIFICATION : B_ERROR_NOTIFICATION); 515 516 if (fHasBattery) { 517 notification.SetTitle(B_TRANSLATE("Battery low")); 518 notification.SetContent(B_TRANSLATE( 519 "The battery level is getting low, please plug in the device.")); 520 } else { 521 notification.SetTitle(B_TRANSLATE("Battery critical")); 522 notification.SetContent(B_TRANSLATE( 523 "The battery level is critical, please plug in the device " 524 "immediately.")); 525 } 526 527 notification.SetIcon(bitmap); 528 notification.Send(); 529 delete bitmap; 530 } 531 532 533 // #pragma mark - Replicant view 534 535 536 PowerStatusReplicant::PowerStatusReplicant(BRect frame, int32 resizingMode, 537 bool inDeskbar) 538 : 539 PowerStatusView(NULL, frame, resizingMode, -1, inDeskbar) 540 { 541 _Init(); 542 _LoadSettings(); 543 544 if (!inDeskbar) { 545 // we were obviously added to a standard window - let's add a dragger 546 frame.OffsetTo(B_ORIGIN); 547 frame.top = frame.bottom - 7; 548 frame.left = frame.right - 7; 549 BDragger* dragger = new BDragger(frame, this, 550 B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM); 551 AddChild(dragger); 552 } else 553 Update(); 554 } 555 556 557 PowerStatusReplicant::PowerStatusReplicant(BMessage* archive) 558 : 559 PowerStatusView(archive) 560 { 561 _Init(); 562 _LoadSettings(); 563 } 564 565 566 PowerStatusReplicant::~PowerStatusReplicant() 567 { 568 if (fMessengerExist) 569 delete fExtWindowMessenger; 570 571 fDriverInterface->StopWatching(this); 572 fDriverInterface->Disconnect(); 573 fDriverInterface->ReleaseReference(); 574 575 _SaveSettings(); 576 } 577 578 579 PowerStatusReplicant* 580 PowerStatusReplicant::Instantiate(BMessage* archive) 581 { 582 if (!validate_instantiation(archive, "PowerStatusReplicant")) 583 return NULL; 584 585 return new PowerStatusReplicant(archive); 586 } 587 588 589 status_t 590 PowerStatusReplicant::Archive(BMessage* archive, bool deep) const 591 { 592 status_t status = PowerStatusView::Archive(archive, deep); 593 if (status == B_OK) 594 status = archive->AddString("add_on", kSignature); 595 if (status == B_OK) 596 status = archive->AddString("class", "PowerStatusReplicant"); 597 598 return status; 599 } 600 601 602 void 603 PowerStatusReplicant::MessageReceived(BMessage *message) 604 { 605 switch (message->what) { 606 case kMsgToggleLabel: 607 if (fShowStatusIcon) 608 fShowLabel = !fShowLabel; 609 else 610 fShowLabel = true; 611 612 Update(true); 613 break; 614 615 case kMsgToggleTime: 616 fShowTime = !fShowTime; 617 Update(true); 618 break; 619 620 case kMsgToggleStatusIcon: 621 if (fShowLabel) 622 fShowStatusIcon = !fShowStatusIcon; 623 else 624 fShowStatusIcon = true; 625 626 Update(true); 627 break; 628 629 case kMsgToggleExtInfo: 630 _OpenExtendedWindow(); 631 break; 632 633 case B_ABOUT_REQUESTED: 634 _AboutRequested(); 635 break; 636 637 case B_QUIT_REQUESTED: 638 _Quit(); 639 break; 640 641 default: 642 PowerStatusView::MessageReceived(message); 643 } 644 } 645 646 647 void 648 PowerStatusReplicant::MouseDown(BPoint point) 649 { 650 BPopUpMenu *menu = new BPopUpMenu(B_EMPTY_STRING, false, false); 651 menu->SetFont(be_plain_font); 652 653 BMenuItem* item; 654 menu->AddItem(item = new BMenuItem(B_TRANSLATE("Show text label"), 655 new BMessage(kMsgToggleLabel))); 656 if (fShowLabel) 657 item->SetMarked(true); 658 menu->AddItem(item = new BMenuItem(B_TRANSLATE("Show status icon"), 659 new BMessage(kMsgToggleStatusIcon))); 660 if (fShowStatusIcon) 661 item->SetMarked(true); 662 menu->AddItem(new BMenuItem(!fShowTime ? B_TRANSLATE("Show time") : 663 B_TRANSLATE("Show percent"), 664 new BMessage(kMsgToggleTime))); 665 666 menu->AddSeparatorItem(); 667 menu->AddItem(new BMenuItem(B_TRANSLATE("Battery info" B_UTF8_ELLIPSIS), 668 new BMessage(kMsgToggleExtInfo))); 669 670 menu->AddSeparatorItem(); 671 menu->AddItem(new BMenuItem(B_TRANSLATE("About" B_UTF8_ELLIPSIS), 672 new BMessage(B_ABOUT_REQUESTED))); 673 menu->AddItem(new BMenuItem(B_TRANSLATE("Quit"), 674 new BMessage(B_QUIT_REQUESTED))); 675 menu->SetTargetForItems(this); 676 677 ConvertToScreen(&point); 678 menu->Go(point, true, false, true); 679 } 680 681 682 void 683 PowerStatusReplicant::_AboutRequested() 684 { 685 BAboutWindow* window = new BAboutWindow( 686 B_TRANSLATE_SYSTEM_NAME("PowerStatus"), kSignature); 687 688 const char* authors[] = { 689 "Axel Dörfler", 690 "Alexander von Gluck", 691 "Clemens Zeidler", 692 NULL 693 }; 694 695 window->AddCopyright(2006, "Haiku, Inc."); 696 window->AddAuthors(authors); 697 698 window->Show(); 699 } 700 701 702 void 703 PowerStatusReplicant::_Init() 704 { 705 fDriverInterface = new ACPIDriverInterface; 706 if (fDriverInterface->Connect() != B_OK) { 707 delete fDriverInterface; 708 fDriverInterface = new APMDriverInterface; 709 if (fDriverInterface->Connect() != B_OK) { 710 fprintf(stderr, "No power interface found.\n"); 711 _Quit(); 712 } 713 } 714 715 fExtendedWindow = NULL; 716 fMessengerExist = false; 717 fExtWindowMessenger = NULL; 718 719 fDriverInterface->StartWatching(this); 720 } 721 722 723 void 724 PowerStatusReplicant::_Quit() 725 { 726 if (fInDeskbar) { 727 BDeskbar deskbar; 728 deskbar.RemoveItem(kDeskbarItemName); 729 } else 730 be_app->PostMessage(B_QUIT_REQUESTED); 731 } 732 733 734 status_t 735 PowerStatusReplicant::_GetSettings(BFile& file, int mode) 736 { 737 BPath path; 738 status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path, 739 (mode & O_ACCMODE) != O_RDONLY); 740 if (status != B_OK) 741 return status; 742 743 path.Append("PowerStatus settings"); 744 745 return file.SetTo(path.Path(), mode); 746 } 747 748 749 void 750 PowerStatusReplicant::_LoadSettings() 751 { 752 fShowLabel = false; 753 754 BFile file; 755 if (_GetSettings(file, B_READ_ONLY) != B_OK) 756 return; 757 758 BMessage settings; 759 if (settings.Unflatten(&file) < B_OK) 760 return; 761 762 FromMessage(&settings); 763 } 764 765 766 void 767 PowerStatusReplicant::_SaveSettings() 768 { 769 BFile file; 770 if (_GetSettings(file, B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE) != B_OK) 771 return; 772 773 BMessage settings('pwst'); 774 ToMessage(&settings); 775 776 ssize_t size = 0; 777 settings.Flatten(&file, &size); 778 } 779 780 781 void 782 PowerStatusReplicant::_OpenExtendedWindow() 783 { 784 if (!fExtendedWindow) { 785 fExtendedWindow = new ExtendedInfoWindow(fDriverInterface); 786 fExtWindowMessenger = new BMessenger(NULL, fExtendedWindow); 787 fExtendedWindow->Show(); 788 return; 789 } 790 791 BMessage msg(B_SET_PROPERTY); 792 msg.AddSpecifier("Hidden", int32(0)); 793 if (fExtWindowMessenger->SendMessage(&msg) == B_BAD_PORT_ID) { 794 fExtendedWindow = new ExtendedInfoWindow(fDriverInterface); 795 if (fMessengerExist) 796 delete fExtWindowMessenger; 797 fExtWindowMessenger = new BMessenger(NULL, fExtendedWindow); 798 fMessengerExist = true; 799 fExtendedWindow->Show(); 800 } else 801 fExtendedWindow->Activate(); 802 803 } 804 805 806 // #pragma mark - 807 808 809 extern "C" _EXPORT BView* 810 instantiate_deskbar_item(void) 811 { 812 return new PowerStatusReplicant(BRect(0, 0, 15, 15), B_FOLLOW_NONE, true); 813 } 814