1 /* 2 * Copyright 2003-2016 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 * Jérôme Duval, jerome.duval@free.fr 8 * Filip Maryjański, widelec@morphos.pl 9 * Puck Meerburg, puck@puckipedia.nl 10 * Michael Phipps 11 * John Scipione, jscipione@gmail.com 12 */ 13 14 15 #include "ScreenSaverWindow.h" 16 17 #include <stdio.h> 18 #include <strings.h> 19 20 #include <Alignment.h> 21 #include <Application.h> 22 #include <Box.h> 23 #include <Button.h> 24 #include <Catalog.h> 25 #include <CheckBox.h> 26 #include <ControlLook.h> 27 #include <DefaultSettingsView.h> 28 #include <Directory.h> 29 #include <DurationFormat.h> 30 #include <Entry.h> 31 #include <File.h> 32 #include <FindDirectory.h> 33 #include <Font.h> 34 #include <GroupLayout.h> 35 #include <LayoutBuilder.h> 36 #include <ListItem.h> 37 #include <ListView.h> 38 #include <NodeMonitor.h> 39 #include <Path.h> 40 #include <Rect.h> 41 #include <Roster.h> 42 #include <Screen.h> 43 #include <ScreenSaver.h> 44 #include <ScreenSaverRunner.h> 45 #include <ScrollView.h> 46 #include <Size.h> 47 #include <Slider.h> 48 #include <StringView.h> 49 #include <TabView.h> 50 #include <TextView.h> 51 52 #include <algorithm> 53 // for std::max and std::min 54 55 #include "PreviewView.h" 56 #include "ScreenCornerSelector.h" 57 #include "ScreenSaverItem.h" 58 #include "ScreenSaverShared.h" 59 60 #undef B_TRANSLATION_CONTEXT 61 #define B_TRANSLATION_CONTEXT "ScreenSaver" 62 63 64 const uint32 kPreviewMonitorGap = 16; 65 const uint32 kMinSettingsWidth = 230; 66 const uint32 kMinSettingsHeight = 120; 67 68 const int32 kMsgSaverSelected = 'SSEL'; 69 const int32 kMsgPasswordCheckBox = 'PWCB'; 70 const int32 kMsgRunSliderChanged = 'RSch'; 71 const int32 kMsgRunSliderUpdate = 'RSup'; 72 const int32 kMsgPasswordSliderChanged = 'PWch'; 73 const int32 kMsgPasswordSliderUpdate = 'PWup'; 74 const int32 kMsgChangePassword = 'PWBT'; 75 const int32 kMsgEnableScreenSaverBox = 'ESCH'; 76 77 const int32 kMsgTurnOffCheckBox = 'TUOF'; 78 const int32 kMsgTurnOffSliderChanged = 'TUch'; 79 const int32 kMsgTurnOffSliderUpdate = 'TUup'; 80 81 const int32 kMsgFadeCornerChanged = 'fdcc'; 82 const int32 kMsgNeverFadeCornerChanged = 'nfcc'; 83 84 const float kWindowWidth = 446.0f; 85 const float kWindowHeight = 325.0f; 86 const float kDefaultItemSpacingAt12pt = 12.0f * 0.85; 87 88 89 class TimeSlider : public BSlider { 90 public: 91 TimeSlider(const char* name, 92 uint32 changedMessage, 93 uint32 updateMessage); 94 virtual ~TimeSlider(); 95 96 virtual void SetValue(int32 value); 97 98 void SetTime(bigtime_t useconds); 99 bigtime_t Time() const; 100 101 private: 102 void _TimeToString(bigtime_t useconds, 103 BString& string); 104 }; 105 106 107 class TabView : public BTabView { 108 public: 109 TabView(); 110 111 virtual void MouseDown(BPoint where); 112 }; 113 114 115 class FadeView : public BView { 116 public: 117 FadeView(const char* name, 118 ScreenSaverSettings& settings); 119 120 virtual void AttachedToWindow(); 121 virtual void MessageReceived(BMessage* message); 122 123 void UpdateTurnOffScreen(); 124 void UpdateStatus(); 125 126 private: 127 void _UpdateColors(); 128 ScreenSaverSettings& fSettings; 129 uint32 fTurnOffScreenFlags; 130 131 BCheckBox* fEnableCheckBox; 132 TimeSlider* fRunSlider; 133 134 BTextView* fTurnOffNotSupported; 135 BCheckBox* fTurnOffCheckBox; 136 TimeSlider* fTurnOffSlider; 137 138 BTextView* fFadeNeverText; 139 BTextView* fFadeNowText; 140 141 BCheckBox* fPasswordCheckBox; 142 TimeSlider* fPasswordSlider; 143 BButton* fPasswordButton; 144 145 ScreenCornerSelector* fFadeNow; 146 ScreenCornerSelector* fFadeNever; 147 }; 148 149 150 class ModulesView : public BView { 151 public: 152 ModulesView(const char* name, 153 ScreenSaverSettings& settings); 154 virtual ~ModulesView(); 155 156 virtual void DetachedFromWindow(); 157 virtual void AttachedToWindow(); 158 virtual void AllAttached(); 159 virtual void MessageReceived(BMessage* message); 160 161 void EmptyScreenSaverList(); 162 void PopulateScreenSaverList(); 163 164 void SaveState(); 165 166 BScreenSaver* ScreenSaver(); 167 168 private: 169 friend class TabView; 170 171 static int _CompareScreenSaverItems(const void* left, 172 const void* right); 173 174 void _CloseSaver(); 175 void _OpenSaver(); 176 void _AddNewScreenSaverToList(const char* name, 177 BPath* path); 178 void _RemoveScreenSaverFromList(const char* name); 179 180 private: 181 ScreenSaverSettings& fSettings; 182 183 BListView* fScreenSaversListView; 184 BButton* fTestButton; 185 186 ScreenSaverRunner* fSaverRunner; 187 BString fCurrentName; 188 189 BBox* fSettingsBox; 190 BView* fSettingsView; 191 192 PreviewView* fPreviewView; 193 194 team_id fScreenSaverTestTeam; 195 }; 196 197 198 // #pragma mark - TimeSlider 199 200 201 static const int32 kTimeInUnits[] = { 202 30, 60, 90, 203 120, 150, 180, 204 240, 300, 360, 205 420, 480, 540, 206 600, 900, 1200, 207 1500, 1800, 2400, 208 3000, 3600, 5400, 209 7200, 9000, 10800, 210 14400, 18000 211 }; 212 213 static const int32 kTimeUnitCount 214 = sizeof(kTimeInUnits) / sizeof(kTimeInUnits[0]); 215 216 217 TimeSlider::TimeSlider(const char* name, uint32 changedMessage, 218 uint32 updateMessage) 219 : 220 BSlider(name, B_TRANSLATE("30 seconds"), new BMessage(changedMessage), 221 0, kTimeUnitCount - 1, B_HORIZONTAL, B_TRIANGLE_THUMB) 222 { 223 SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 224 SetModificationMessage(new BMessage(updateMessage)); 225 SetBarThickness(10); 226 } 227 228 229 TimeSlider::~TimeSlider() 230 { 231 } 232 233 234 void 235 TimeSlider::SetValue(int32 value) 236 { 237 int32 oldValue = Value(); 238 BSlider::SetValue(value); 239 240 if (oldValue != Value()) { 241 BString label; 242 _TimeToString(kTimeInUnits[Value()] * 1000000LL, label); 243 SetLabel(label.String()); 244 } 245 } 246 247 248 void 249 TimeSlider::SetTime(bigtime_t useconds) 250 { 251 for (int t = 0; t < kTimeUnitCount; t++) { 252 if (kTimeInUnits[t] * 1000000LL == useconds) { 253 SetValue(t); 254 break; 255 } 256 } 257 } 258 259 260 bigtime_t 261 TimeSlider::Time() const 262 { 263 return 1000000LL * kTimeInUnits[Value()]; 264 } 265 266 267 void 268 TimeSlider::_TimeToString(bigtime_t useconds, BString& string) 269 { 270 BDurationFormat formatter; 271 formatter.Format(string, 0, useconds); 272 } 273 274 275 // #pragma mark - FadeView 276 277 278 FadeView::FadeView(const char* name, ScreenSaverSettings& settings) 279 : 280 BView(name, B_WILL_DRAW), 281 fSettings(settings) 282 { 283 SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 284 285 font_height fontHeight; 286 be_plain_font->GetHeight(&fontHeight); 287 float textHeight = ceilf(fontHeight.ascent + fontHeight.descent); 288 289 fEnableCheckBox = new BCheckBox("EnableCheckBox", 290 B_TRANSLATE("Enable screensaver"), 291 new BMessage(kMsgEnableScreenSaverBox)); 292 293 BBox* box = new BBox("EnableScreenSaverBox"); 294 box->SetLabel(fEnableCheckBox); 295 296 // Start Screensaver 297 BStringView* startScreenSaver = new BStringView("startScreenSaver", 298 B_TRANSLATE("Start screensaver")); 299 startScreenSaver->SetAlignment(B_ALIGN_RIGHT); 300 301 fRunSlider = new TimeSlider("RunSlider", kMsgRunSliderChanged, 302 kMsgRunSliderUpdate); 303 304 // Turn Off 305 rgb_color textColor = disable_color(ui_color(B_PANEL_TEXT_COLOR), 306 ViewColor()); 307 308 fTurnOffNotSupported = new BTextView("not_supported", be_plain_font, 309 &textColor, B_WILL_DRAW); 310 fTurnOffNotSupported->SetExplicitMinSize(BSize(B_SIZE_UNSET, 311 3 + textHeight * 3)); 312 fTurnOffNotSupported->SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 313 fTurnOffNotSupported->MakeEditable(false); 314 fTurnOffNotSupported->MakeSelectable(false); 315 fTurnOffNotSupported->SetText( 316 B_TRANSLATE("Display Power Management Signaling not available")); 317 318 fTurnOffCheckBox = new BCheckBox("TurnOffScreenCheckBox", 319 B_TRANSLATE("Turn off screen"), new BMessage(kMsgTurnOffCheckBox)); 320 fTurnOffCheckBox->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, 321 B_ALIGN_VERTICAL_CENTER)); 322 323 fTurnOffSlider = new TimeSlider("TurnOffSlider", kMsgTurnOffSliderChanged, 324 kMsgTurnOffSliderUpdate); 325 326 // Password 327 fPasswordCheckBox = new BCheckBox("PasswordCheckbox", 328 B_TRANSLATE("Password lock"), new BMessage(kMsgPasswordCheckBox)); 329 fPasswordCheckBox->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, 330 B_ALIGN_VERTICAL_CENTER)); 331 332 fPasswordSlider = new TimeSlider("PasswordSlider", 333 kMsgPasswordSliderChanged, kMsgPasswordSliderUpdate); 334 335 fPasswordButton = new BButton("PasswordButton", 336 B_TRANSLATE("Password" B_UTF8_ELLIPSIS), 337 new BMessage(kMsgChangePassword)); 338 339 // Bottom 340 float monitorHeight = 10 + textHeight * 3; 341 float aspectRatio = 4.0f / 3.0f; 342 float monitorWidth = monitorHeight * aspectRatio; 343 BRect monitorRect = BRect(0, 0, monitorWidth, monitorHeight); 344 345 fFadeNow = new ScreenCornerSelector(monitorRect, "FadeNow", 346 new BMessage(kMsgFadeCornerChanged), B_FOLLOW_NONE); 347 fFadeNowText = new BTextView("FadeNowText", B_WILL_DRAW); 348 fFadeNowText->SetExplicitMinSize(BSize(B_SIZE_UNSET, 349 4 + textHeight * 4)); 350 fFadeNowText->SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 351 fFadeNowText->MakeEditable(false); 352 fFadeNowText->MakeSelectable(false); 353 fFadeNowText->SetText(B_TRANSLATE("Fade now when mouse is here")); 354 355 fFadeNever = new ScreenCornerSelector(monitorRect, "FadeNever", 356 new BMessage(kMsgNeverFadeCornerChanged), B_FOLLOW_NONE); 357 fFadeNeverText = new BTextView("FadeNeverText", B_WILL_DRAW); 358 fFadeNeverText->SetExplicitMinSize(BSize(B_SIZE_UNSET, 359 4 + textHeight * 4)); 360 fFadeNeverText->SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 361 fFadeNeverText->MakeEditable(false); 362 fFadeNeverText->MakeSelectable(false); 363 fFadeNeverText->SetText(B_TRANSLATE("Don't fade when mouse is here")); 364 365 box->AddChild(BLayoutBuilder::Group<>(B_VERTICAL, 0) 366 .SetInsets(B_USE_DEFAULT_SPACING, 0, B_USE_DEFAULT_SPACING, 367 B_USE_DEFAULT_SPACING) 368 .AddGrid(B_USE_DEFAULT_SPACING, B_USE_SMALL_SPACING) 369 .Add(startScreenSaver, 0, 0) 370 .Add(fRunSlider, 1, 0) 371 .Add(fTurnOffCheckBox, 0, 1) 372 .Add(BLayoutBuilder::Group<>(B_VERTICAL) 373 .Add(fTurnOffNotSupported) 374 .Add(fTurnOffSlider) 375 .View(), 1, 1) 376 .Add(fPasswordCheckBox, 0, 2) 377 .Add(fPasswordSlider, 1, 2) 378 .End() 379 .AddGroup(B_HORIZONTAL) 380 .AddGlue() 381 .Add(fPasswordButton) 382 .End() 383 .AddGlue() 384 .AddGroup(B_HORIZONTAL) 385 .Add(fFadeNow) 386 .AddGroup(B_VERTICAL, 0) 387 .Add(fFadeNowText) 388 .AddGlue() 389 .End() 390 .Add(fFadeNever) 391 .AddGroup(B_VERTICAL, 0) 392 .Add(fFadeNeverText) 393 .AddGlue() 394 .End() 395 .End() 396 .AddGlue() 397 .View()); 398 399 BLayoutBuilder::Group<>(this, B_HORIZONTAL) 400 .SetInsets(B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING, 401 B_USE_WINDOW_SPACING, 0) 402 .Add(box) 403 .End(); 404 405 } 406 407 408 void 409 FadeView::AttachedToWindow() 410 { 411 fEnableCheckBox->SetTarget(this); 412 fRunSlider->SetTarget(this); 413 fTurnOffCheckBox->SetTarget(this); 414 fTurnOffSlider->SetTarget(this); 415 fFadeNow->SetTarget(this); 416 fFadeNever->SetTarget(this); 417 fPasswordCheckBox->SetTarget(this); 418 fPasswordSlider->SetTarget(this); 419 420 fEnableCheckBox->SetValue( 421 fSettings.TimeFlags() & ENABLE_SAVER ? B_CONTROL_ON : B_CONTROL_OFF); 422 fRunSlider->SetTime(fSettings.BlankTime()); 423 fTurnOffSlider->SetTime(fSettings.OffTime() + fSettings.BlankTime()); 424 fFadeNow->SetCorner(fSettings.BlankCorner()); 425 fFadeNever->SetCorner(fSettings.NeverBlankCorner()); 426 fPasswordCheckBox->SetValue(fSettings.LockEnable()); 427 fPasswordSlider->SetTime(fSettings.PasswordTime()); 428 429 _UpdateColors(); 430 UpdateTurnOffScreen(); 431 UpdateStatus(); 432 } 433 434 435 void 436 FadeView::MessageReceived(BMessage *message) 437 { 438 switch (message->what) { 439 case B_COLORS_UPDATED: 440 _UpdateColors(); 441 break; 442 case kMsgRunSliderChanged: 443 case kMsgRunSliderUpdate: 444 if (fRunSlider->Value() > fTurnOffSlider->Value()) 445 fTurnOffSlider->SetValue(fRunSlider->Value()); 446 447 if (fRunSlider->Value() > fPasswordSlider->Value()) 448 fPasswordSlider->SetValue(fRunSlider->Value()); 449 break; 450 451 case kMsgTurnOffSliderChanged: 452 case kMsgTurnOffSliderUpdate: 453 if (fRunSlider->Value() > fTurnOffSlider->Value()) 454 fRunSlider->SetValue(fTurnOffSlider->Value()); 455 break; 456 457 case kMsgPasswordSliderChanged: 458 case kMsgPasswordSliderUpdate: 459 if (fPasswordSlider->Value() < fRunSlider->Value()) 460 fRunSlider->SetValue(fPasswordSlider->Value()); 461 break; 462 463 case kMsgTurnOffCheckBox: 464 fTurnOffSlider->SetEnabled( 465 fTurnOffCheckBox->Value() == B_CONTROL_ON); 466 break; 467 } 468 469 switch (message->what) { 470 case kMsgRunSliderChanged: 471 case kMsgTurnOffSliderChanged: 472 case kMsgPasswordSliderChanged: 473 case kMsgPasswordCheckBox: 474 case kMsgEnableScreenSaverBox: 475 case kMsgFadeCornerChanged: 476 case kMsgNeverFadeCornerChanged: 477 UpdateStatus(); 478 fSettings.Save(); 479 break; 480 481 default: 482 BView::MessageReceived(message); 483 } 484 } 485 486 487 void 488 FadeView::UpdateTurnOffScreen() 489 { 490 bool enabled = (fSettings.TimeFlags() & ENABLE_DPMS_MASK) != 0; 491 492 BScreen screen(Window()); 493 uint32 dpmsCapabilities = screen.DPMSCapabilites(); 494 495 fTurnOffScreenFlags = 0; 496 if (dpmsCapabilities & B_DPMS_OFF) 497 fTurnOffScreenFlags |= ENABLE_DPMS_OFF; 498 if (dpmsCapabilities & B_DPMS_STAND_BY) 499 fTurnOffScreenFlags |= ENABLE_DPMS_STAND_BY; 500 if (dpmsCapabilities & B_DPMS_SUSPEND) 501 fTurnOffScreenFlags |= ENABLE_DPMS_SUSPEND; 502 503 fTurnOffCheckBox->SetValue(enabled && fTurnOffScreenFlags != 0 504 ? B_CONTROL_ON : B_CONTROL_OFF); 505 506 enabled = fEnableCheckBox->Value() == B_CONTROL_ON; 507 fTurnOffCheckBox->SetEnabled(enabled && fTurnOffScreenFlags != 0); 508 if (fTurnOffScreenFlags != 0) { 509 fTurnOffNotSupported->Hide(); 510 fTurnOffSlider->Show(); 511 } else { 512 fTurnOffSlider->Hide(); 513 fTurnOffNotSupported->Show(); 514 } 515 } 516 517 518 void 519 FadeView::UpdateStatus() 520 { 521 Window()->DisableUpdates(); 522 523 bool enabled = fEnableCheckBox->Value() == B_CONTROL_ON; 524 fPasswordCheckBox->SetEnabled(enabled); 525 fTurnOffCheckBox->SetEnabled(enabled && fTurnOffScreenFlags != 0); 526 fRunSlider->SetEnabled(enabled); 527 fTurnOffSlider->SetEnabled(enabled && fTurnOffCheckBox->Value()); 528 fPasswordSlider->SetEnabled(enabled && fPasswordCheckBox->Value()); 529 fPasswordButton->SetEnabled(enabled && fPasswordCheckBox->Value()); 530 531 Window()->EnableUpdates(); 532 533 // Update the saved preferences 534 fSettings.SetWindowFrame(Frame()); 535 fSettings.SetTimeFlags((enabled ? ENABLE_SAVER : 0) 536 | (fTurnOffCheckBox->Value() ? fTurnOffScreenFlags : 0)); 537 fSettings.SetBlankTime(fRunSlider->Time()); 538 bigtime_t offTime = fTurnOffSlider->Time() - fSettings.BlankTime(); 539 fSettings.SetOffTime(offTime); 540 fSettings.SetSuspendTime(offTime); 541 fSettings.SetStandByTime(offTime); 542 fSettings.SetBlankCorner(fFadeNow->Corner()); 543 fSettings.SetNeverBlankCorner(fFadeNever->Corner()); 544 fSettings.SetLockEnable(fPasswordCheckBox->Value()); 545 fSettings.SetPasswordTime(fPasswordSlider->Time()); 546 547 // TODO - Tell the password window to update its stuff 548 } 549 550 551 void 552 FadeView::_UpdateColors() 553 { 554 rgb_color color = ui_color(B_PANEL_TEXT_COLOR); 555 fFadeNeverText->SetFontAndColor(be_plain_font, 0, &color); 556 fFadeNowText->SetFontAndColor(be_plain_font, 0, &color); 557 } 558 559 560 // #pragma mark - ModulesView 561 562 563 ModulesView::ModulesView(const char* name, ScreenSaverSettings& settings) 564 : 565 BView(name, B_WILL_DRAW), 566 fSettings(settings), 567 fScreenSaversListView(new BListView("SaversListView")), 568 fTestButton(new BButton("TestButton", B_TRANSLATE("Test"), 569 new BMessage(kMsgTestSaver))), 570 fSaverRunner(NULL), 571 fSettingsBox(new BBox("SettingsBox")), 572 fSettingsView(NULL), 573 fPreviewView(new PreviewView("preview")), 574 fScreenSaverTestTeam(-1) 575 { 576 SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 577 578 fScreenSaversListView->SetSelectionMessage( 579 new BMessage(kMsgSaverSelected)); 580 BScrollView* saversListScrollView = new BScrollView("scroll_list", 581 fScreenSaversListView, 0, false, true); 582 583 fSettingsBox->SetLabel(B_TRANSLATE("Screensaver settings")); 584 fSettingsBox->SetExplicitMinSize(BSize( 585 floorf(be_control_look->DefaultItemSpacing() 586 * ((kWindowWidth - 157.0f) / kDefaultItemSpacingAt12pt)), 587 B_SIZE_UNSET)); 588 589 BLayoutBuilder::Group<>(this, B_HORIZONTAL) 590 .SetInsets(B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING, 591 B_USE_WINDOW_SPACING, 0) 592 .AddGroup(B_VERTICAL) 593 .Add(fPreviewView) 594 .Add(saversListScrollView) 595 .AddGroup(B_HORIZONTAL) 596 .Add(fTestButton) 597 .AddGlue() 598 .End() 599 .End() 600 .Add(fSettingsBox) 601 .End(); 602 } 603 604 605 ModulesView::~ModulesView() 606 { 607 stop_watching(this); 608 609 delete fTestButton; 610 delete fSettingsBox; 611 delete fPreviewView; 612 } 613 614 615 void 616 ModulesView::DetachedFromWindow() 617 { 618 SaveState(); 619 EmptyScreenSaverList(); 620 621 _CloseSaver(); 622 } 623 624 625 void 626 ModulesView::AttachedToWindow() 627 { 628 fScreenSaversListView->SetTarget(this); 629 fTestButton->SetTarget(this); 630 } 631 632 633 void 634 ModulesView::AllAttached() 635 { 636 PopulateScreenSaverList(); 637 fScreenSaversListView->Invoke(); 638 } 639 640 641 void 642 ModulesView::MessageReceived(BMessage* message) 643 { 644 switch (message->what) { 645 case kMsgSaverSelected: 646 { 647 int32 selection = fScreenSaversListView->CurrentSelection(); 648 if (selection < 0) 649 break; 650 651 ScreenSaverItem* item 652 = (ScreenSaverItem*)fScreenSaversListView->ItemAt(selection); 653 if (item == NULL) 654 break; 655 656 if (strcmp(item->Text(), B_TRANSLATE("Blackness")) == 0) 657 fSettings.SetModuleName(""); 658 else 659 fSettings.SetModuleName(item->Text()); 660 661 SaveState(); 662 _CloseSaver(); 663 _OpenSaver(); 664 fSettings.Save(); 665 break; 666 } 667 668 case kMsgTestSaver: 669 { 670 SaveState(); 671 fSettings.Save(); 672 673 _CloseSaver(); 674 675 be_roster->StartWatching(BMessenger(this, Looper()), 676 B_REQUEST_QUIT); 677 BMessage message(kMsgTestSaver); 678 if (be_roster->Launch(SCREEN_BLANKER_SIG, &message, 679 &fScreenSaverTestTeam) == B_OK) { 680 break; 681 } 682 683 // Try really hard to launch it. It's very likely that this fails 684 // when we run from the CD, and there is only an incomplete mime 685 // database for example... 686 BPath path; 687 if (find_directory(B_SYSTEM_BIN_DIRECTORY, &path) != B_OK 688 || path.Append("screen_blanker") != B_OK) { 689 path.SetTo("/bin/screen_blanker"); 690 } 691 692 BEntry entry(path.Path()); 693 entry_ref ref; 694 if (entry.GetRef(&ref) == B_OK) { 695 be_roster->Launch(&ref, &message, 696 &fScreenSaverTestTeam); 697 } 698 break; 699 } 700 701 case B_NODE_MONITOR: 702 { 703 switch (message->GetInt32("opcode", 0)) { 704 case B_ENTRY_CREATED: 705 { 706 const char* name; 707 node_ref nodeRef; 708 709 message->FindString("name", &name); 710 message->FindInt32("device", &nodeRef.device); 711 message->FindInt64("directory", &nodeRef.node); 712 713 BDirectory dir(&nodeRef); 714 715 if (dir.InitCheck() == B_OK) { 716 BPath path(&dir); 717 _AddNewScreenSaverToList(name, &path); 718 } 719 break; 720 } 721 722 723 case B_ENTRY_MOVED: 724 case B_ENTRY_REMOVED: 725 { 726 const char* name; 727 728 message->FindString("name", &name); 729 _RemoveScreenSaverFromList(name); 730 731 break; 732 } 733 734 default: 735 // ignore any other operations 736 break; 737 } 738 break; 739 } 740 741 case B_SOME_APP_QUIT: 742 { 743 team_id team; 744 if (message->FindInt32("be:team", &team) == B_OK 745 && team == fScreenSaverTestTeam) { 746 be_roster->StopWatching(this); 747 _OpenSaver(); 748 } 749 break; 750 } 751 752 default: 753 BView::MessageReceived(message); 754 } 755 } 756 757 758 void 759 ModulesView::SaveState() 760 { 761 BScreenSaver* saver = ScreenSaver(); 762 if (saver == NULL) 763 return; 764 765 BMessage state; 766 if (saver->SaveState(&state) == B_OK) 767 fSettings.SetModuleState(fCurrentName.String(), &state); 768 } 769 770 771 void 772 ModulesView::EmptyScreenSaverList() 773 { 774 fScreenSaversListView->DeselectAll(); 775 while (BListItem* item = fScreenSaversListView->RemoveItem((int32)0)) 776 delete item; 777 } 778 779 780 void 781 ModulesView::PopulateScreenSaverList() 782 { 783 // Blackness is a built-in screen saver 784 ScreenSaverItem* defaultItem 785 = new ScreenSaverItem(B_TRANSLATE("Blackness"), ""); 786 fScreenSaversListView->AddItem(defaultItem); 787 788 // Iterate over add-on directories, and add their files to the list view 789 790 directory_which which[] = { 791 B_USER_NONPACKAGED_ADDONS_DIRECTORY, 792 B_USER_ADDONS_DIRECTORY, 793 B_SYSTEM_NONPACKAGED_ADDONS_DIRECTORY, 794 B_SYSTEM_ADDONS_DIRECTORY, 795 }; 796 ScreenSaverItem* selectedItem = NULL; 797 798 for (uint32 i = 0; i < sizeof(which) / sizeof(which[0]); i++) { 799 BPath basePath; 800 if (find_directory(which[i], &basePath) != B_OK) 801 continue; 802 else if (basePath.Append("Screen Savers", true) != B_OK) 803 continue; 804 805 BDirectory dir(basePath.Path()); 806 BEntry entry; 807 node_ref nodeRef; 808 809 dir.GetNodeRef(&nodeRef); 810 watch_node(&nodeRef, B_WATCH_DIRECTORY, this); 811 812 while (dir.GetNextEntry(&entry, true) == B_OK) { 813 char name[B_FILE_NAME_LENGTH]; 814 if (entry.GetName(name) != B_OK) 815 continue; 816 817 BPath path(basePath); 818 if (path.Append(name) != B_OK) 819 continue; 820 821 ScreenSaverItem* item = new ScreenSaverItem(name, path.Path()); 822 fScreenSaversListView->AddItem(item); 823 824 if (selectedItem != NULL) 825 continue; 826 827 if (strcmp(fSettings.ModuleName(), item->Text()) == 0) 828 selectedItem = item; 829 } 830 } 831 832 fScreenSaversListView->SortItems(_CompareScreenSaverItems); 833 if (selectedItem == NULL) 834 selectedItem = defaultItem; 835 836 fScreenSaversListView->Select(fScreenSaversListView->IndexOf(selectedItem)); 837 fScreenSaversListView->ScrollToSelection(); 838 } 839 840 841 //! Sorting function for ScreenSaverItems 842 int 843 ModulesView::_CompareScreenSaverItems(const void* left, const void* right) 844 { 845 ScreenSaverItem* leftItem = *(ScreenSaverItem **)left; 846 ScreenSaverItem* rightItem = *(ScreenSaverItem **)right; 847 848 return strcasecmp(leftItem->Text(), rightItem->Text()); 849 } 850 851 852 BScreenSaver* 853 ModulesView::ScreenSaver() 854 { 855 if (fSaverRunner != NULL) 856 return fSaverRunner->ScreenSaver(); 857 858 return NULL; 859 } 860 861 862 void 863 ModulesView::_CloseSaver() 864 { 865 // remove old screen saver preview & config 866 867 BScreenSaver* saver = ScreenSaver(); 868 BView* view = fPreviewView->RemovePreview(); 869 if (fSettingsView != NULL) 870 fSettingsBox->RemoveChild(fSettingsView); 871 872 if (fSaverRunner != NULL) 873 fSaverRunner->Quit(); 874 875 if (saver != NULL) 876 saver->StopConfig(); 877 878 delete view; 879 delete fSettingsView; 880 delete fSaverRunner; 881 // the saver runner also unloads the add-on, so it must 882 // be deleted last 883 884 fSettingsView = NULL; 885 fSaverRunner = NULL; 886 } 887 888 889 void 890 ModulesView::_OpenSaver() 891 { 892 // create new screen saver preview & config 893 894 BView* view = fPreviewView->AddPreview(); 895 fCurrentName = fSettings.ModuleName(); 896 fSaverRunner = new ScreenSaverRunner(view->Window(), view, fSettings); 897 898 #ifdef __HAIKU__ 899 BRect rect = fSettingsBox->InnerFrame().InsetByCopy(4, 4); 900 #else 901 BRect rect = fSettingsBox->Bounds().InsetByCopy(4, 4); 902 rect.top += 14; 903 #endif 904 fSettingsView = new BView(rect, "SettingsView", B_FOLLOW_ALL, B_WILL_DRAW); 905 906 fSettingsView->SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 907 fSettingsBox->AddChild(fSettingsView); 908 909 BScreenSaver* saver = ScreenSaver(); 910 if (saver != NULL && fSettingsView != NULL) { 911 saver->StartConfig(fSettingsView); 912 if (saver->StartSaver(view, true) == B_OK) { 913 fPreviewView->HideNoPreview(); 914 fSaverRunner->Run(); 915 } else 916 fPreviewView->ShowNoPreview(); 917 } else { 918 // Failed to load OR this is the "Blackness" screensaver. Show a black 919 // preview (this is what will happen in both cases when screen_blanker 920 // runs). 921 fPreviewView->HideNoPreview(); 922 } 923 924 if (fSettingsView->ChildAt(0) == NULL) { 925 // There are no settings at all, we add the module name here to 926 // let it look a bit better at least. 927 BPrivate::BuildDefaultSettingsView(fSettingsView, 928 fSettings.ModuleName()[0] ? fSettings.ModuleName() 929 : B_TRANSLATE("Blackness"), 930 saver != NULL || !fSettings.ModuleName()[0] 931 ? B_TRANSLATE("No options available") 932 : B_TRANSLATE("Could not load screen saver")); 933 } 934 } 935 936 937 void 938 ModulesView::_AddNewScreenSaverToList(const char* name, BPath* path) 939 { 940 int32 oldSelected = fScreenSaversListView->CurrentSelection(); 941 ScreenSaverItem* selectedItem = (ScreenSaverItem*)fScreenSaversListView->ItemAt( 942 oldSelected); 943 944 path->Append(name); 945 fScreenSaversListView->AddItem(new ScreenSaverItem(name, path->Path())); 946 fScreenSaversListView->SortItems(_CompareScreenSaverItems); 947 948 if (selectedItem != NULL) { 949 fScreenSaversListView->Select(fScreenSaversListView->IndexOf( 950 selectedItem)); 951 fScreenSaversListView->ScrollToSelection(); 952 } 953 } 954 955 956 void 957 ModulesView::_RemoveScreenSaverFromList(const char* name) 958 { 959 int32 oldSelected = fScreenSaversListView->CurrentSelection(); 960 ScreenSaverItem* selectedItem = (ScreenSaverItem*)fScreenSaversListView->ItemAt( 961 oldSelected); 962 963 if (strcasecmp(selectedItem->Text(), name) == 0) { 964 fScreenSaversListView->RemoveItem(selectedItem); 965 fScreenSaversListView->SortItems(_CompareScreenSaverItems); 966 fScreenSaversListView->Select(0); 967 fScreenSaversListView->ScrollToSelection(); 968 return; 969 } 970 971 for (int i = 0, max = fScreenSaversListView->CountItems(); i < max; i++) { 972 ScreenSaverItem* item = (ScreenSaverItem*)fScreenSaversListView->ItemAt( 973 i); 974 975 if (strcasecmp(item->Text(), name) == 0) { 976 fScreenSaversListView->RemoveItem(item); 977 delete item; 978 break; 979 } 980 } 981 982 fScreenSaversListView->SortItems(_CompareScreenSaverItems); 983 984 oldSelected = fScreenSaversListView->IndexOf(selectedItem); 985 fScreenSaversListView->Select(oldSelected); 986 fScreenSaversListView->ScrollToSelection(); 987 } 988 989 990 // #pragma mark - TabView 991 992 993 TabView::TabView() 994 : 995 BTabView("tab_view", B_WIDTH_FROM_LABEL) 996 { 997 } 998 999 1000 void 1001 TabView::MouseDown(BPoint where) 1002 { 1003 BTab* fadeTab = TabAt(0); 1004 BRect fadeTabFrame(TabFrame(0)); 1005 BTab* modulesTab = TabAt(1); 1006 BRect modulesTabFrame(TabFrame(1)); 1007 ModulesView* modulesView = dynamic_cast<ModulesView*>(modulesTab->View()); 1008 1009 if (fadeTab != NULL && Selection() != 0 && fadeTabFrame.Contains(where) 1010 && modulesView != NULL) { 1011 // clicked on the fade tab 1012 modulesView->SaveState(); 1013 modulesView->_CloseSaver(); 1014 } else if (modulesTab != NULL && Selection() != 1 1015 && modulesTabFrame.Contains(where) && modulesView != NULL) { 1016 // clicked on the modules tab 1017 BMessage message(kMsgSaverSelected); 1018 modulesView->MessageReceived(&message); 1019 } 1020 1021 BTabView::MouseDown(where); 1022 } 1023 1024 1025 // #pragma mark - ScreenSaverWindow 1026 1027 1028 ScreenSaverWindow::ScreenSaverWindow() 1029 : 1030 BWindow(BRect(50.0f, 50.0f, 50.0f + kWindowWidth, 50.0f + kWindowHeight), 1031 B_TRANSLATE_SYSTEM_NAME("ScreenSaver"), B_TITLED_WINDOW, 1032 B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS) 1033 { 1034 fSettings.Load(); 1035 1036 fMinWidth = floorf(be_control_look->DefaultItemSpacing() 1037 * (kWindowWidth / kDefaultItemSpacingAt12pt)); 1038 1039 font_height fontHeight; 1040 be_plain_font->GetHeight(&fontHeight); 1041 float textHeight = ceilf(fontHeight.ascent + fontHeight.descent); 1042 1043 fMinHeight = ceilf(std::max(kWindowHeight, textHeight * 28)); 1044 1045 // Create the password editing window 1046 fPasswordWindow = new PasswordWindow(fSettings); 1047 fPasswordWindow->Run(); 1048 1049 // Create the tab view 1050 fTabView = new TabView(); 1051 fTabView->SetBorder(B_NO_BORDER); 1052 1053 // Create the controls inside the tabs 1054 fFadeView = new FadeView(B_TRANSLATE("General"), fSettings); 1055 fModulesView = new ModulesView(B_TRANSLATE("Screensavers"), fSettings); 1056 1057 fTabView->AddTab(fFadeView); 1058 fTabView->AddTab(fModulesView); 1059 1060 // Create the topmost background view 1061 BView* topView = new BView("topView", B_WILL_DRAW); 1062 topView->SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 1063 topView->SetExplicitAlignment(BAlignment(B_ALIGN_USE_FULL_WIDTH, 1064 B_ALIGN_USE_FULL_HEIGHT)); 1065 topView->SetExplicitMinSize(BSize(fMinWidth, fMinHeight)); 1066 BLayoutBuilder::Group<>(topView, B_VERTICAL) 1067 .SetInsets(0, B_USE_DEFAULT_SPACING, 0, B_USE_WINDOW_SPACING) 1068 .Add(fTabView) 1069 .End(); 1070 1071 SetLayout(new BGroupLayout(B_VERTICAL)); 1072 GetLayout()->AddView(topView); 1073 1074 fTabView->Select(fSettings.WindowTab()); 1075 1076 if (fSettings.WindowFrame().left > 0 && fSettings.WindowFrame().top > 0) 1077 MoveTo(fSettings.WindowFrame().left, fSettings.WindowFrame().top); 1078 1079 if (fSettings.WindowFrame().Width() > 0 1080 && fSettings.WindowFrame().Height() > 0) { 1081 ResizeTo(fSettings.WindowFrame().Width(), 1082 fSettings.WindowFrame().Height()); 1083 } 1084 1085 CenterOnScreen(); 1086 } 1087 1088 1089 ScreenSaverWindow::~ScreenSaverWindow() 1090 { 1091 Hide(); 1092 fFadeView->UpdateStatus(); 1093 fSettings.SetWindowTab(fTabView->Selection()); 1094 1095 delete fTabView->RemoveTab(1); 1096 // We delete this here in order to make sure the module view saves its 1097 // state while the window is still intact. 1098 1099 fSettings.Save(); 1100 } 1101 1102 1103 void 1104 ScreenSaverWindow::MessageReceived(BMessage* message) 1105 { 1106 switch (message->what) { 1107 case kMsgChangePassword: 1108 fPasswordWindow->CenterIn(Frame()); 1109 fPasswordWindow->Show(); 1110 break; 1111 1112 case kMsgUpdateList: 1113 fModulesView->EmptyScreenSaverList(); 1114 fModulesView->PopulateScreenSaverList(); 1115 break; 1116 1117 default: 1118 BWindow::MessageReceived(message); 1119 } 1120 } 1121 1122 1123 void 1124 ScreenSaverWindow::ScreenChanged(BRect frame, color_space colorSpace) 1125 { 1126 fFadeView->UpdateTurnOffScreen(); 1127 } 1128 1129 1130 bool 1131 ScreenSaverWindow::QuitRequested() 1132 { 1133 be_app->PostMessage(B_QUIT_REQUESTED); 1134 return true; 1135 } 1136