// FullScreenClockWnd.cpp : implementation file
//
#include "stdafx.h"
#include "resource.h" // main symbols
#include "ClockSaverWnd.h"
#include "Clock.h"
#include "ClockSettings.h"
#include "Monitors.h"
#include "Monitor.h"
#include "MailNotification.h"
#include "MailNotificationFactory.h"
#include "Reminder.h"
#define RANDOM( lbound, ubound ) (int)((ubound - lbound + 1) * ( (double)rand() / (double)RAND_MAX ) + lbound)
#define QUICKEXIT_THRESHOLD 500 //milliseconds
// CClockSaverWnd
//IMPLEMENT_DYNAMIC(CClockSaverWnd, CWnd)
CClockSaverWnd::CClockSaverWnd() : m_bTerminateOnMouse( TRUE ),
m_pMailNotification( NULL ),
m_hMailOpenIcon( NULL ),
m_hMailClosedIcon( NULL )
{
//seed the random number generator
::srand( (unsigned)::time( NULL ) );
}
CClockSaverWnd::~CClockSaverWnd()
{
}
BEGIN_MESSAGE_MAP(CClockSaverWnd, CSaverWnd)
ON_WM_CREATE()
ON_WM_DESTROY()
ON_WM_MOUSEMOVE()
ON_WM_TIMER()
ON_WM_PAINT()
ON_WM_ERASEBKGND()
ON_WM_SYSCOMMAND()
ON_WM_SETCURSOR()
ON_WM_NCACTIVATE()
ON_WM_ACTIVATE()
ON_WM_ACTIVATEAPP()
ON_WM_MOUSEWHEEL( )
ON_WM_LBUTTONDOWN()
ON_WM_MBUTTONDOWN()
ON_WM_RBUTTONDOWN()
ON_WM_KEYDOWN()
ON_WM_SYSKEYDOWN()
END_MESSAGE_MAP()
BEGIN_TIMER_MAP( CClockSaverWnd )
ON_TIMER( TIMER_QUICKEXIT, OnQuickExitTimer )
ON_TIMER( TIMER_MOUSEEXIT, OnExitOnMouseTimer )
ON_TIMER( TIMER_SHUTDOWN_COMPUTER, OnShutDownComputerTimer )
ON_TIMER( TIMER_IDLE, OnIdleTimer )
ON_TIMER( TIMER_BLINKMAIL, OnBlinkMailTimer )
ON_TIMER( TIMER_WIGGLE_MOUSE, OnWiggleMouse )
ON_TIMER( TIMER_START_WIGGLE_MOUSE, OnStartWiggleMouse )
ON_TIMER( TIMER_END_WIGGLE_MOUSE, OnEndWiggleMouse )
END_TIMER_MAP( CSaverWnd )
BOOL CClockSaverWnd::Create()
{
CRect rect;
CMonitors::GetVirtualDesktopRect( &rect );
return CSaverWnd::Create(WS_EX_TOPMOST | WS_EX_TOOLWINDOW, ::AfxGetAppName(), WS_POPUP, rect, GetDesktopWindow());
}
// CClockSaverWnd message handlers
int CClockSaverWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
int ret = CSaverWnd::OnCreate(lpCreateStruct);
if ( ret == 0 )
{
// if the mouse moves within QUICKEXIT_THRESHOLD of starting the screen saver will exit
SET_TIMER( TIMER_QUICKEXIT, QUICKEXIT_THRESHOLD );
if ( m_ClockSettings.GetShowNewMailNotification() && ConnectToMail() )
{
CMonitor monitor = CMonitors::GetPrimaryMonitor();
CRect rect;
monitor.GetMonitorRect( &rect );
ScreenToClient( &rect );
int ret1 = m_MailLabel.Create( _T("mail"), WS_CHILD|WS_VISIBLE|SS_ICON|SS_CENTERIMAGE, CRect(rect.right - 32, rect.bottom - 32, rect.right, rect.bottom ), this);
if ( ret1 != 0 )
m_MailLabel.SetIcon( ::AfxGetApp()->LoadIcon( IDI_BLACK ) );
if ( m_pMailNotification->HasCalendar() )
m_pMailNotification->GetAllDayEvents( m_AllDayEvents );
}
if ( m_ClockSettings.GetShutDownComputer() )
SET_TIMER( TIMER_SHUTDOWN_COMPUTER, m_ClockSettings.GetShutDownComputerInterval() * 60 * 60 * 1000 );
m_hMailOpenIcon = (HICON)::LoadImage( ::AfxGetInstanceHandle(), MAKEINTRESOURCE(IDI_MAIL_OPEN), IMAGE_ICON, 32, 32, LR_LOADTRANSPARENT );
m_hMailClosedIcon = (HICON)::LoadImage( ::AfxGetInstanceHandle(), MAKEINTRESOURCE(IDI_MAIL_CLOSED), IMAGE_ICON, 32, 32, LR_LOADTRANSPARENT );
}
return ret;
}
void CClockSaverWnd::Draw( CDC* pDC )
{
//then draw our reminders and all day events
CRect rect;
CMonitors::GetPrimaryMonitor().GetMonitorRect( &rect );
CRect reminderRect = rect;
reminderRect.right = rect.Width() / 2;
ScreenToClient( &reminderRect );
COLORREF oldColor = pDC->SetTextColor( RGB(255,0,0) );
for ( int i = 0; i < m_RemindersArray.GetCount(); i++ )
{
CReminder* pReminder = (CReminder*)m_RemindersArray[i];
if ( pReminder->IsPastDue() )
pDC->SetTextColor( RGB(255,0,0) );
else
pDC->SetTextColor( RGB(255,255,255) );
CString text;
pReminder->GetDisplayString( text );
reminderRect.top += pDC->DrawText( text, &reminderRect, DT_LEFT|DT_TOP|DT_WORDBREAK );
}
if ( m_AllDayEvents.GetLength() > 0 )
{
CRect AllDayEventsRect = rect;
AllDayEventsRect.left = rect.Width() / 2;
AllDayEventsRect.right = rect.right;
ScreenToClient( &AllDayEventsRect );
pDC->SetTextColor( RGB(255,255,255) );
pDC->DrawText( m_AllDayEvents, &AllDayEventsRect, DT_RIGHT|DT_TOP|DT_WORDBREAK );
}
pDC->SetTextColor( oldColor );
CSaverWnd::Draw( pDC );
}
bool CClockSaverWnd::ConnectToMail()
{
m_pMailNotification = CMailNotificationFactory::CreateMailNotification( this );
return m_pMailNotification != NULL;
}
BOOL CClockSaverWnd::OnWiggleMouse()
{
CPoint clockPoint = m_pClock->GetPoint();
CPoint point( clockPoint );
point.x += RANDOM( -10, 10 );
point.y += RANDOM( -10, 10 );
m_pClock->SetPoint( point );
return FALSE;
}
BOOL CClockSaverWnd::OnStartWiggleMouse()
{
SET_TIMER( TIMER_WIGGLE_MOUSE, 500 );
SET_TIMER( TIMER_END_WIGGLE_MOUSE, 5000 );
return TRUE;
}
BOOL CClockSaverWnd::OnEndWiggleMouse()
{
KILL_TIMER( TIMER_WIGGLE_MOUSE );
SET_TIMER( TIMER_START_WIGGLE_MOUSE, RANDOM( 60000, 120000 ) );
return TRUE;
}
void CClockSaverWnd::OnGotNewMail()
{
SET_TIMER( TIMER_BLINKMAIL, 1000 );
}
void CClockSaverWnd::OnDestroy()
{
CObject* pa = NULL;
for ( int i = m_RemindersArray.GetCount(); i > 0; i-- )
{
if( ( pa = (CObject*)m_RemindersArray.GetAt( 0 ) ) != NULL )
{
m_RemindersArray.RemoveAt( 0 ); // Element 1 moves to 0.
delete pa; // Delete the original element at 0.
}
}
if ( ::IsWindow( m_MailLabel ) )
m_MailLabel.DestroyWindow();
delete m_pMailNotification;
::DestroyIcon( m_hMailOpenIcon );
::DestroyIcon( m_hMailClosedIcon );
CSaverWnd::OnDestroy();
}
BOOL CClockSaverWnd::OnShutDownComputerTimer()
{
ShutDownComputer();
return TRUE;
}
BOOL CClockSaverWnd::OnBlinkMailTimer()
{
static HICON icon = m_hMailOpenIcon;
if ( icon == m_hMailOpenIcon )
icon = m_hMailClosedIcon;
else
icon = m_hMailOpenIcon;
m_MailLabel.SetIcon( icon );
return FALSE;
}
BOOL CClockSaverWnd::OnExitOnMouseTimer()
{
//enough time has elapsed for the user to play around with the mouse movement
//now we exit on mouse
m_bTerminateOnMouse = TRUE;
return TRUE;
}
BOOL CClockSaverWnd::OnQuickExitTimer()
{
//the quick exit timer has elapsed
//moving the mouse no longer exits the screen saver
m_bTerminateOnMouse = FALSE;
if ( m_ClockSettings.GetExitOnMouse() )
SET_TIMER( TIMER_MOUSEEXIT, m_ClockSettings.GetMouseTimeOutInterval() * 60000 );
return TRUE;
}
BOOL CClockSaverWnd::OnIdleTimer()
{
m_pClock->SetPoint( GetRandomPoint() );
return FALSE;
}
CPoint CClockSaverWnd::GetRandomPoint()
{
//generate a random position on the screen such that
//the entire clock is still on the screen
// first pick a random monitor
CMonitors monitors;
CMonitor monitor = monitors.GetMonitor( RANDOM( 0, CMonitors::GetMonitorCount() - 1 ) );
// get the rect of that monitor
CRect rect;
monitor.GetMonitorRect( &rect );
ScreenToClient( &rect );
// shrink the rect enough so that the whole clock will be on screen
int deflate = (int)(m_pClock->GetDiameter() * 2.0);
rect.DeflateRect( deflate, deflate );
// then get a random point in that rect
int x = RANDOM( rect.left, rect.right );
int y = RANDOM( rect.top, rect.bottom );
return CPoint( x, y );
}
void CClockSaverWnd::OnMouseMove(UINT nFlags, CPoint point)
{
if( m_bTerminateOnMouse )
{
CSaverWnd::OnMouseMove( nFlags, point ); // SaverWnd closes the scren saver on mouse movement
return;
}
if ( m_ClockSettings.GetShutDownComputer() )
RESET_TIMER( TIMER_SHUTDOWN_COMPUTER, m_ClockSettings.GetShutDownComputerInterval() * 60 * 60 * 1000 );
if ( m_ClockSettings.GetExitOnMouse() )
RESET_TIMER( TIMER_MOUSEEXIT, m_ClockSettings.GetMouseTimeOutInterval() * 60000 );
RESET_TIMER( TIMER_START_WIGGLE_MOUSE, RANDOM( 60000, 120000 ) );
RESET_TIMER( TIMER_IDLE, RANDOM( 30000, 300000 ) );
// bypass the saverwnd which closes the window on mouse movement
CClockWnd::OnMouseMove( nFlags, point );
}
void CClockSaverWnd::OnGotReminder( CReminder* pReminder )
{
m_RemindersArray.Add( pReminder );
// make sure the new reminder gets painted
CRect rect;
CMonitors::GetPrimaryMonitor().GetMonitorRect( &rect );
ScreenToClient( &rect );
InvalidateRect( &rect );
}
void CClockSaverWnd::CloseSaverWindow()
{
m_bReallyClose = TRUE;
this->PostMessage( WM_CLOSE );
}
bool CClockSaverWnd::ShutDownComputer()
{
HANDLE hToken;
TOKEN_PRIVILEGES tkp;
// Get a token for this process.
if (!::OpenProcessToken(::GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
return false;
// Get the LUID for the shutdown privilege.
::LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid);
tkp.PrivilegeCount = 1; // one privilege to set
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
// Get the shutdown privilege for this process.
::AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES)NULL, 0);
if (::GetLastError() != ERROR_SUCCESS)
return false;
// Shut down the system and power off
if (!::ExitWindowsEx(EWX_POWEROFF, SHTDN_REASON_FLAG_PLANNED))
return false;
return true;
}