// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
//
// This file is part of the VNC system.
//
// The VNC system is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
// USA.
//
// If the source code for the VNC system is not available from the place
// whence you received this file, check http://www.uk.research.att.com/vnc or contact
// the authors on vnc@uk.research.att.com for information on obtaining it.
// Flasher.cpp: implementation of the Flasher class.
#include "stdhdrs.h"
#include "vncviewer.h"
#include "Flasher.h"
#include "Exception.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
#define FLASHER_CLASS_NAME "VNCviewer Flasher"
#define FLASHFONTHEIGHT 80
Flasher::Flasher(int port)
{
// Create a dummy window. We don't use it for anything except
// receiving socket events, so a seperate listening thread would
// probably be easier!
WNDCLASSEX wndclass;
wndclass.cbSize = sizeof(wndclass);
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = Flasher::WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = pApp->m_instance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = (const char *) NULL;
wndclass.lpszClassName = FLASHER_CLASS_NAME;
wndclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
RegisterClassEx(&wndclass);
m_hwnd = CreateWindow(FLASHER_CLASS_NAME,
FLASHER_CLASS_NAME,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
200, 200,
NULL,
NULL,
pApp->m_instance,
NULL);
// record which client created this window
SetWindowLong(m_hwnd, GWL_USERDATA, (LONG) this);
// Select a font for displaying user name
LOGFONT lf;
memset(&lf, 0, sizeof(lf));
lf.lfHeight = FLASHFONTHEIGHT;
lf.lfWeight = FW_BOLD;
lf.lfItalic = 1;
lf.lfPitchAndFamily = VARIABLE_PITCH | FF_SWISS;
lf.lfFaceName[0] = '\0';
m_hfont = CreateFontIndirect(&lf);
if (m_hfont == NULL) {
log.Print(1, _T("FAILED TO SELECT FLASHER FONT!\n"));
}
// Create a listening socket
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
m_sock = socket(AF_INET, SOCK_STREAM, 0);
if (!m_sock) throw WarningException("Error creating Flasher socket");
try {
int one = 1, res = 0;
// If we use the SO_REUSEADDR option then you can run multiple daemons
// because the bind doesn't return an error. Only one gets the accept,
// but when that process dies it hands back to another. This may or may
// not be desirable. We don't use it.
//int res = setsockopt(m_sock, SOL_SOCKET, SO_REUSEADDR, (const char *) &one, sizeof(one));
//if (res == SOCKET_ERROR)
// throw WarningException("Error setting Flasher socket options");
res = bind(m_sock, (struct sockaddr *)&addr, sizeof(addr));
if (res == SOCKET_ERROR)
throw WarningException("Error binding Flasher socket");
res = listen(m_sock, 5);
if (res == SOCKET_ERROR)
throw WarningException("Error when Flasher tries to listen");
} catch (...) {
closesocket(m_sock);
m_sock = 0;
throw;
}
// Send a message to specified window on an incoming connection
WSAAsyncSelect (m_sock, m_hwnd, WM_SOCKEVENT, FD_ACCEPT | FD_CLOSE);
}
// convert a lower-case ASCII char to a value 0-255
inline int scalechar(char c) {
return ( ((c - 'a')+4) & 0x1f ) * 255 / 0x1f;
}
// We use this on each screen saver window running
BOOL CALLBACK KillScreenSaverFunc(HWND hwnd, LPARAM lParam)
{
char buffer[256];
// - ONLY try to close Screen-saver windows!!!
if ((GetClassName(hwnd, buffer, 256) != 0) &&
(strcmp(buffer, "WindowsScreenSaverClass") == 0))
PostMessage(hwnd, WM_CLOSE, 0, 0);
return TRUE;
}
// Process window messages
LRESULT CALLBACK Flasher::WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) {
// This is a static method, so we don't know which instantiation we're
// dealing with. We use Allen Hadden's (ahadden@taratec.com) suggestion
// from a newsgroup to get the pseudo-this.
Flasher *_this = (Flasher *) GetWindowLong(hwnd, GWL_USERDATA);
switch (iMsg) {
case WM_CREATE:
return 0;
case WM_SOCKEVENT:
{
assert(HIWORD(lParam) == 0);
// A new socket created by accept might send messages to
// this procedure. We can ignore them.
if(wParam != _this->m_sock) {
return 0;
}
switch(lParam) {
case FD_ACCEPT:
{
SOCKET hNewSock;
char username[256];
hNewSock = accept(_this->m_sock, NULL, NULL);
// make it blocking
WSAAsyncSelect(hNewSock, hwnd, 0, 0);
u_long blk = 0;
int res = ioctlsocket(hNewSock, FIONBIO, &blk);
assert(res == 0);
CloseScreenSaver();
// Se if the server's sending a user name
int namelen = recv(hNewSock, username, 250, 0);
if (namelen >= 0)
username[namelen] = 0;
log.Print(2, _T("Flash for '%s'\n"), username);
closesocket(hNewSock);
// flash
// Get a DC for the root window
HDC hrootdc = ::GetDC(NULL);
HBRUSH holdbrush = (HBRUSH) SelectObject(hrootdc,
(HBRUSH) GetStockObject(BLACK_BRUSH));
// Find the size.
RECT rect;
GetClipBox(hrootdc, &rect);
int barwidth = (rect.right - rect.left) / 10;
int barheight = max(
(rect.bottom - rect.top) / 10,
FLASHFONTHEIGHT);
HFONT oldfont = (HFONT) SelectObject(hrootdc, _this->m_hfont);
// Flash the screen
::Beep(440,50);
Rectangle(hrootdc, rect.left, rect.top,
rect.right, barheight);
Rectangle(hrootdc, rect.left, rect.bottom-barheight,
rect.right, rect.bottom);
Rectangle(hrootdc, rect.left, rect.top+barheight,
barwidth, rect.bottom-barheight);
Rectangle(hrootdc, rect.right-barwidth, rect.top+barheight,
rect.right, rect.bottom-barheight);
RECT topbar;
SetRect(&topbar, rect.left, rect.top,
rect.right, barheight);
int i = 0;
if (namelen > 0) {
int oldmode = SetBkMode(hrootdc, TRANSPARENT);
COLORREF oldcolor = SetTextColor(hrootdc,
RGB(scalechar(username[0])/(i+1), scalechar(username[1])/(i+1), scalechar(username[2])/(i+1)));
DrawText(hrootdc, username, -1, &topbar, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
SetTextColor(hrootdc, oldcolor);
SetBkMode(hrootdc, oldmode);
}
GdiFlush();
SelectObject(hrootdc, holdbrush);
::Sleep(1000);
SelectObject(hrootdc, oldfont);
InvalidateRect(0, &rect, TRUE);
::ReleaseDC(NULL, hrootdc);
break;
}
case FD_CLOSE:
log.Print(2, _T("Flasher connection closed\n"));
DestroyWindow(hwnd);
break;
}
return 0;
}
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, iMsg, wParam, lParam);
}
void Flasher::CloseScreenSaver() {
// Which OS are we on?
OSVERSIONINFO ovi;
ovi.dwOSVersionInfoSize = sizeof(ovi);
if (!GetVersionEx(&ovi)) return;
switch (ovi.dwPlatformId) {
case VER_PLATFORM_WIN32_WINDOWS:
{
// Windows 95
HWND hsswnd = FindWindow ("WindowsScreenSaverClass", NULL);
if (hsswnd != NULL)
PostMessage(hsswnd, WM_CLOSE, 0, 0);
break;
}
case VER_PLATFORM_WIN32_NT:
{
// Windows NT
HDESK hdesk = OpenDesktop(
TEXT("Screen-saver"),
0, FALSE, DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS);
if (hdesk) {
if (EnumDesktopWindows(hdesk, (WNDENUMPROC) KillScreenSaverFunc, 0)) {
CloseDesktop(hdesk);
}
Sleep(1000);
}
}
}
}
Flasher::~Flasher()
{
shutdown(m_sock, SD_BOTH);
closesocket(m_sock);
if (m_hfont != NULL) DeleteObject(m_hfont);
}