root/src/bin/screen_blanker/ScreenBlanker.cpp
/*
 * Copyright 2003-2014 Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *              Jérôme Duval, jerome.duval@free.fr
 *              Axel Dörfler, axeld@pinc-software.de
 *              Ryan Leavengood, leavengood@gmail.com
 *              Michael Phipps
 *              John Scipione, jscipione@gmail.com
 *              Puck Meerburg, puck@puckipedia.nl
 */


#include "ScreenBlanker.h"

#include <Beep.h>
#include <Debug.h>
#include <File.h>
#include <FindDirectory.h>
#include <Path.h>
#include <Screen.h>
#include <StorageDefs.h>
#include <SupportDefs.h>
#include <image.h>

#include <stdio.h>
#include <string.h>
#include <syslog.h>

#include "ScreenSaverShared.h"

const static uint32 kMsgTurnOffScreen = 'tofs';
const static uint32 kMsgSuspendScreen = 'suss';
const static uint32 kMsgStandByScreen = 'stbs';


//      #pragma mark - ScreenBlanker


ScreenBlanker::ScreenBlanker()
        :
        BApplication(SCREEN_BLANKER_SIG),
        fWindow(NULL),
        fSaverRunner(NULL),
        fPasswordWindow(NULL),
        fImmediateLock(false),
        fTestSaver(false),
        fResumeRunner(NULL),
        fStandByScreenRunner(NULL),
        fSuspendScreenRunner(NULL),
        fTurnOffScreenRunner(NULL)
{
        fBlankTime = system_time();
}


ScreenBlanker::~ScreenBlanker()
{
        delete fResumeRunner;
        _TurnOnScreen();
}


void
ScreenBlanker::ReadyToRun()
{
        if (!fSettings.Load())
                fprintf(stderr, "could not load settings, using defaults\n");

        // create a BDirectWindow and start the render thread.
        // TODO: we need a window per screen...
        BScreen screen(B_MAIN_SCREEN_ID);
        fWindow = new ScreenSaverWindow(screen.Frame(), fTestSaver);
        fPasswordWindow = new PasswordWindow();

        BView* view = fWindow->ChildAt(0);
        fSaverRunner = new ScreenSaverRunner(fWindow, view, fSettings);
        fWindow->SetSaverRunner(fSaverRunner);

        BScreenSaver* saver = fSaverRunner->ScreenSaver();
        if (saver != NULL && saver->StartSaver(view, false) == B_OK)
                fSaverRunner->Run();
        else {
                fprintf(stderr, "could not load the screensaver addon\n");
                view->SetViewColor(0, 0, 0);
                        // needed for Blackness saver
        }

        fWindow->SetFullScreen(true);
        fWindow->Show();
        HideCursor();

        _QueueTurnOffScreen();
}


void
ScreenBlanker::_TurnOnScreen()
{
        delete fStandByScreenRunner;
        delete fSuspendScreenRunner;
        delete fTurnOffScreenRunner;

        fStandByScreenRunner = fSuspendScreenRunner = fTurnOffScreenRunner = NULL;

        BScreen screen;
        screen.SetDPMS(B_DPMS_ON);
}


void
ScreenBlanker::_SetDPMSMode(uint32 mode)
{
        BScreen screen;
        screen.SetDPMS(mode);

        if (fWindow->Lock()) {
                fSaverRunner->Suspend();
                fWindow->Unlock();
        }
}


void
ScreenBlanker::_ShowPasswordWindow()
{
        _TurnOnScreen();

        if (fWindow->Lock()) {
                fSaverRunner->Suspend();

                fWindow->Sync();
                        // TODO: is that needed?
                ShowCursor();
                if (fPasswordWindow->IsHidden())
                        fPasswordWindow->Show();

                fWindow->Unlock();
        }

        _QueueResumeScreenSaver();
}


void
ScreenBlanker::_QueueResumeScreenSaver()
{
        delete fResumeRunner;

        BMessage resume(kMsgResumeSaver);
        fResumeRunner = new BMessageRunner(BMessenger(this), &resume,
                fSettings.BlankTime(), 1);
        if (fResumeRunner->InitCheck() != B_OK)
                syslog(LOG_ERR, "resume screen saver runner failed\n");
}


void
ScreenBlanker::_QueueTurnOffScreen()
{
        // stop running notifiers

        delete fStandByScreenRunner;
        delete fSuspendScreenRunner;
        delete fTurnOffScreenRunner;

        fStandByScreenRunner = fSuspendScreenRunner = fTurnOffScreenRunner = NULL;

        // figure out which notifiers we need and which of them are supported

        uint32 flags = fSettings.TimeFlags();
        BScreen screen;
        uint32 capabilities = screen.DPMSCapabilites();
        if ((capabilities & B_DPMS_OFF) == 0)
                flags &= ~ENABLE_DPMS_OFF;
        if ((capabilities & B_DPMS_SUSPEND) == 0)
                flags &= ~ENABLE_DPMS_SUSPEND;
        if ((capabilities & B_DPMS_STAND_BY) == 0)
                flags &= ~ENABLE_DPMS_STAND_BY;

        if ((flags & ENABLE_DPMS_MASK) == 0)
                return;

        if (fSettings.OffTime() == fSettings.SuspendTime()
                && (flags & (ENABLE_DPMS_OFF | ENABLE_DPMS_SUSPEND))
                        == (ENABLE_DPMS_OFF | ENABLE_DPMS_SUSPEND))
                flags &= ~ENABLE_DPMS_SUSPEND;
        if (fSettings.SuspendTime() == fSettings.StandByTime()
                && (flags & (ENABLE_DPMS_SUSPEND | ENABLE_DPMS_STAND_BY))
                        == (ENABLE_DPMS_SUSPEND | ENABLE_DPMS_STAND_BY))
                flags &= ~ENABLE_DPMS_STAND_BY;

        // start them off again

        if (flags & ENABLE_DPMS_STAND_BY) {
                BMessage dpms(kMsgStandByScreen);
                fStandByScreenRunner = new BMessageRunner(BMessenger(this), &dpms,
                        fSettings.StandByTime(), 1);
                if (fStandByScreenRunner->InitCheck() != B_OK)
                        syslog(LOG_ERR, "standby screen saver runner failed\n");
        }

        if (flags & ENABLE_DPMS_SUSPEND) {
                BMessage dpms(kMsgSuspendScreen);
                fSuspendScreenRunner = new BMessageRunner(BMessenger(this), &dpms,
                        fSettings.SuspendTime(), 1);
                if (fSuspendScreenRunner->InitCheck() != B_OK)
                        syslog(LOG_ERR, "suspend screen saver runner failed\n");
        }

        if (flags & ENABLE_DPMS_OFF) {
                BMessage dpms(kMsgTurnOffScreen);
                fTurnOffScreenRunner = new BMessageRunner(BMessenger(this), &dpms,
                        fSettings.OffTime(), 1);
                if (fTurnOffScreenRunner->InitCheck() != B_OK)
                        syslog(LOG_ERR, "turn off screen saver runner failed\n");
        }
}


void
ScreenBlanker::ArgvReceived(int argc, char** argv)
{
        if (argc > 1 && strcmp("-l", argv[1]) == 0)
                fImmediateLock = true;
}


void
ScreenBlanker::MessageReceived(BMessage* message)
{
        switch (message->what) {
                case kMsgUnlock:
                {
                        // if the user has no password then just exit on any input
                        if (fSettings.Password()[0] != '\0'
                                && strcmp(fSettings.Password(), crypt(fPasswordWindow->Password(),
                                        fSettings.Password())) != 0) {
                                beep();
                                fPasswordWindow->SetPassword("");
                                _QueueResumeScreenSaver();
                        } else  {
                                PRINT(("Quitting!\n"));
                                _Shutdown();
                                Quit();
                        }
                        break;
                }

                case kMsgResumeSaver:
                {
                        if (fWindow->Lock()) {
                                // ensure that our window and application are active before calling HideCursor()
                                fWindow->Activate();
                                HideCursor();
                                fPasswordWindow->SetPassword("");
                                fPasswordWindow->Hide();

                                fSaverRunner->Resume();
                                fWindow->Unlock();
                        }

                        // Turn on the message filter again
                        BMessage enable(kMsgEnableFilter);
                        fWindow->PostMessage(&enable);

                        _QueueTurnOffScreen();
                        break;
                }

                case kMsgTurnOffScreen:
                        _SetDPMSMode(B_DPMS_OFF);
                        break;

                case kMsgSuspendScreen:
                        _SetDPMSMode(B_DPMS_SUSPEND);
                        break;

                case kMsgStandByScreen:
                        _SetDPMSMode(B_DPMS_STAND_BY);
                        break;

                case kMsgTestSaver:
                        fTestSaver = true;
                        break;

                default:
                        BApplication::MessageReceived(message);
        }
}


bool
ScreenBlanker::QuitRequested()
{
        if (fSettings.LockEnable()) {
                bigtime_t minTime = fSettings.PasswordTime() - fSettings.BlankTime();
                if (minTime == 0)
                        minTime = 5000000;
                if (fImmediateLock || system_time() - fBlankTime > minTime) {
                        _ShowPasswordWindow();
                        return false;
                }
        }

        _Shutdown();
        return true;
}


bool
ScreenBlanker::IsPasswordWindowShown() const
{
        return fPasswordWindow != NULL && !fPasswordWindow->IsHidden();
}


void
ScreenBlanker::_Shutdown()
{
        if (fWindow != NULL) {
                fWindow->Hide();

                if (fWindow->Lock())
                        fWindow->Quit();
        }

        delete fSaverRunner;
        fSaverRunner = NULL;
}


//      #pragma mark - main


int
main(int argc, char** argv)
{
        ScreenBlanker app;
        app.Run();

        return 0;
}