diff options
author | Constantin Kaplinsky <const@tightvnc.com> | 2004-11-14 18:28:51 +0000 |
---|---|---|
committer | Constantin Kaplinsky <const@tightvnc.com> | 2004-11-14 18:28:51 +0000 |
commit | fbfbb9205ddc84193c3001e8bcd38c39fe134cdd (patch) | |
tree | d1e928d4ca623b9053a22563cd9037cba56fd75e | |
parent | a92b0872f13865f404506730a8ebbc3ffa3b6083 (diff) | |
download | tigervnc-fbfbb9205ddc84193c3001e8bcd38c39fe134cdd.tar.gz tigervnc-fbfbb9205ddc84193c3001e8bcd38c39fe134cdd.zip |
Added initial RFB Session Player implementation, by Egor Vegner.
git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@6 3789f03b-4d11-0410-bbf8-ca57d06f2519
-rw-r--r-- | rfbplayer/.cvsignore | 6 | ||||
-rw-r--r-- | rfbplayer/FbsInputStream.cxx | 244 | ||||
-rw-r--r-- | rfbplayer/FbsInputStream.h | 66 | ||||
-rw-r--r-- | rfbplayer/RfbProto.cxx | 142 | ||||
-rw-r--r-- | rfbplayer/RfbProto.h | 67 | ||||
-rw-r--r-- | rfbplayer/buildTime.cxx | 1 | ||||
-rw-r--r-- | rfbplayer/resource.h | 20 | ||||
-rw-r--r-- | rfbplayer/rfbplayer.cxx | 1035 | ||||
-rw-r--r-- | rfbplayer/rfbplayer.dsp | 147 | ||||
-rw-r--r-- | rfbplayer/rfbplayer.h | 165 | ||||
-rw-r--r-- | rfbplayer/rfbplayer.ico | bin | 0 -> 766 bytes | |||
-rw-r--r-- | rfbplayer/rfbplayer.rc | 118 | ||||
-rw-r--r-- | rfbplayer/utils.h | 45 | ||||
-rw-r--r-- | vnc.dsw | 27 |
14 files changed, 2083 insertions, 0 deletions
diff --git a/rfbplayer/.cvsignore b/rfbplayer/.cvsignore new file mode 100644 index 00000000..2c7653ae --- /dev/null +++ b/rfbplayer/.cvsignore @@ -0,0 +1,6 @@ +Debug +Debug_Unicode +Profile +Release +rfbplayer.plg +rfbplayer.aps diff --git a/rfbplayer/FbsInputStream.cxx b/rfbplayer/FbsInputStream.cxx new file mode 100644 index 00000000..7000c145 --- /dev/null +++ b/rfbplayer/FbsInputStream.cxx @@ -0,0 +1,244 @@ +/* Copyright (C) 2004 TightVNC Team. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- FbsInputStream class + +#include <windows.h> + +#include <rfb/Exception.h> + +#include <rfbplayer/FbsInputStream.h> + +FbsInputStream::FbsInputStream(char* FileName) { + bufferSize = 0; + ptr = end = start = NULL; + + timeOffset = 0; + seekOffset = -1; + startTime = GetTickCount(); + + playbackSpeed = 1.0; + seekBackwards = false; + paused = false; + + fbsFile = fopen(FileName, "rb"); + if (fbsFile == NULL) { + char *msg = new char[12 + sizeof(FileName)]; + strcpy(msg, "Can't open "); + strcat(msg, FileName); + throw rfb::Exception(msg,"RfbPlayer Error"); + } + + byte b[12]; + readNByte(b, 12); + + if (b[0] != 'F' || b[1] != 'B' || b[2] != 'S' || b[3] != ' ' || + b[4] != '0' || b[5] != '0' || b[6] != '1' || b[7] != '.' || + b[8] < '0' || b[8] > '9' || b[9] < '0' || b[9] > '9' || + b[10] < '0' || b[10] > '9' || b[11] != '\n') { + throw rfb::Exception("Incorrect protocol version", "RfbPlayer Error"); + } +} + +FbsInputStream::~FbsInputStream() { + if (start != NULL) + delete [] start; + ptr = end = start = NULL; + fclose(fbsFile); +} + +int FbsInputStream::pos() { + return ptr - start; +} + +// +// Close FbsInputStream and free data buffer +// + +void FbsInputStream::close() { + fclose(fbsFile); + + startTime = -1; + timeOffset = 0; + seekOffset = -1; + seekBackwards = false; + paused = false; + playbackSpeed = 1.0; + + if (start != NULL) + delete [] start; + ptr = end = start = NULL; + bufferSize = 0; +} + +// +// Fill data buffer from the session file (InStream::overrun() override) +// + +int FbsInputStream::overrun(int itemSize, int nItems, bool wait=true) { + // Just wait unless we are performing playback OR seeking. + waitWhilePaused(); + + // Perform backwardSeek (throws the special exception) + if (seekBackwards) { + throw rfb::Exception("[REWIND]", "RfbPlayer"); + } + + // Save a tail of data + U8 *tmp; + int n = end - ptr; + if (n) { + tmp = new U8[n]; + memmove(tmp, ptr, n); + } + + bufferSize = (int)readUnsigned32(); + if (bufferSize >= 0) { + if (start != NULL) + delete [] start; + int realSize = (bufferSize + 3) & 0xFFFFFFFC; // padding to multiple of 32-bits + ptr = start = new byte[realSize + n]; + end = ptr + bufferSize + n; + if (n) { + memmove(start, tmp, n); + delete [] tmp; + } + readNByte(start + n, realSize); + timeOffset = (long)(readUnsigned32() / playbackSpeed); + + if (itemSize * nItems > bufferSize) + nItems = bufferSize / itemSize; + } + + if (bufferSize < 0 || timeOffset < 0) { + if (start != NULL) + delete [] start; + ptr = end = start = NULL; + bufferSize = 0; + return 0; + } + + if (seekOffset >= 0) { + if (timeOffset >= seekOffset) { + startTime = GetTickCount() - seekOffset; + seekOffset = -1; + } else { + return nItems; + } + } + + while (true) { + long timeDiff = startTime + timeOffset - GetTickCount(); + if (timeDiff <= 0) { + break; + } + Sleep(timeDiff); + waitWhilePaused(); + } + + return nItems; +} + +int FbsInputStream::readUnsigned32() { + byte buf[4]; + if (!readNByte(buf, 4)) + return -1; + + return ((long)(buf[0] & 0xFF) << 24 | + (buf[1] & 0xFF) << 16 | + (buf[2] & 0xFF) << 8 | + (buf[3] & 0xFF)); +} + +// +// Read n-bytes from the session file +// + +bool FbsInputStream::readNByte(byte b[], int n) { + int off = 0; + + while (off != n) { + int count = fread(b, 1, n - off, fbsFile); + if (count < n) { + if (ferror(fbsFile)) + throw rfb::Exception("Read error from session file", "RfbPlayer Error"); + if (feof(fbsFile)) + throw rfb::Exception("[End Of File]", "RfbPlayer Error"); + } + off += count; + } + return true; +} + +void FbsInputStream::waitWhilePaused() { + while (paused && !isSeeking()) { + // A small delay helps to decrease the cpu usage + Sleep(20); + } +} + +// +// Methods providing additional functionality. +// + +long FbsInputStream::getTimeOffset() { + long off = max(seekOffset, timeOffset); + return (long)(off * playbackSpeed); +} + +void FbsInputStream::setTimeOffset(long pos) { + seekOffset = (long)(pos / playbackSpeed); + if (seekOffset < timeOffset) { + seekBackwards = true; + } +} + +void FbsInputStream::setSpeed(double newSpeed) { + long newOffset = (long)(timeOffset * playbackSpeed / newSpeed); + startTime += timeOffset - newOffset; + timeOffset = newOffset; + if (isSeeking()) { + seekOffset = (long)(seekOffset * playbackSpeed / newSpeed); + } + playbackSpeed = newSpeed; +} + +double FbsInputStream::getSpeed() { + return playbackSpeed; +} + +bool FbsInputStream::isSeeking() { + return (seekOffset >= 0); +} + +long FbsInputStream::getSeekOffset() { + return (long)(seekOffset * playbackSpeed); +} + +bool FbsInputStream::isPaused() { + return paused; +} + +void FbsInputStream::pausePlayback() { + paused = true; +} + +void FbsInputStream::resumePlayback() { + paused = false; + startTime = GetTickCount() - timeOffset; +} diff --git a/rfbplayer/FbsInputStream.h b/rfbplayer/FbsInputStream.h new file mode 100644 index 00000000..a072b7f3 --- /dev/null +++ b/rfbplayer/FbsInputStream.h @@ -0,0 +1,66 @@ +/* Copyright (C) 2004 TightVNC Team. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- FbsInputStream.h + +#include <rdr/InStream.h> + +using namespace rdr; + +class FbsInputStream : public InStream { + public: + FbsInputStream(char *FileName); + ~FbsInputStream(); + + // Methods are used to contol the Rfb Stream + long getTimeOffset(); + void setTimeOffset(long pos); + double getSpeed(); + void setSpeed(double newSpeed); + bool isSeeking(); + long getSeekOffset(); + bool isPaused(); + void pausePlayback(); + void resumePlayback(); + void close(); + int pos(); + + private: + U8 *start; + int bufferSize; + long startTime; + long timeOffset; + long seekOffset; + double playbackSpeed; + bool seekBackwards; + bool paused; + + FILE *fbsFile; + + // overrun() - overrides InStream::overrun(). + // It is implemented to fill the data buffer from the session file. + // It ensures there are at least itemSize bytes of buffer data. Returns + // the number of items in the buffer (up to a maximum of nItems). itemSize + // is supposed to be "small" (a few bytes). + + int overrun(int itemSize, int nItems, bool wait); + + int readUnsigned32(); + bool readNByte(U8 *b, int n); + void waitWhilePaused(); +}; diff --git a/rfbplayer/RfbProto.cxx b/rfbplayer/RfbProto.cxx new file mode 100644 index 00000000..68a0f57e --- /dev/null +++ b/rfbplayer/RfbProto.cxx @@ -0,0 +1,142 @@ +/* Copyright (C) 2004 TightVNC Team. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- RFB Protocol + +#include <rfb/Exception.h> +#include <rfb/LogWriter.h> + +#include <rfbplayer/RfbProto.h> + +using namespace rfb; + +static LogWriter vlog("RfbProto"); + +// +// Constructor +// + +RfbProto::RfbProto(char *fileName) { + is = NULL; + reader = NULL; + newSession(fileName); +} + +// +// Destructor +// + +RfbProto::~RfbProto() { + delete is; + is = NULL; + delete reader; + reader = NULL; +} + +void RfbProto::newSession(char *fileName) { + // Close the previous session + if (is) { + delete is; + is = NULL; + delete reader; + reader = NULL; + } + + // Begin the new session + if (fileName != NULL) { + is = new FbsInputStream(fileName); + reader = new CMsgReaderV3(this, is); + initialiseProtocol(); + } +} + +void RfbProto::initialiseProtocol() { + state_ = RFBSTATE_PROTOCOL_VERSION; +} + +void RfbProto::processMsg() +{ + switch (state_) { + + case RFBSTATE_PROTOCOL_VERSION: processVersionMsg(); break; + case RFBSTATE_SECURITY: processSecurityMsg(); break; + case RFBSTATE_INITIALISATION: processInitMsg(); break; + case RFBSTATE_NORMAL: reader->readMsg(); break; + default: + throw rfb::Exception("RfbProto::processMsg: invalid state"); + } +} + +void RfbProto::processVersionMsg() +{ + vlog.debug("reading protocol version"); + bool done; + if (!cp.readVersion(is, &done)) { + state_ = RFBSTATE_INVALID; + throw rfb::Exception("reading version failed: wrong file format?", "RfbPlayer"); + } + if (!done) return; + + // The only official RFB protocol versions are currently 3.3, 3.7 and 3.8 + if (!cp.isVersion(3,3) && !cp.isVersion(3,7) && !cp.isVersion(3,8)) { + char msg[256]; + sprintf(msg,"File have unsupported RFB protocol version %d.%d", + cp.majorVersion, cp.minorVersion); + state_ = RFBSTATE_INVALID; + throw rfb::Exception(msg, "RfbPlayer Error"); + } + + state_ = RFBSTATE_SECURITY; + + vlog.info("Using RFB protocol version %d.%d", + cp.majorVersion, cp.minorVersion); +} + +void RfbProto::processSecurityMsg() +{ + vlog.debug("processing security types message"); + + int secType = secTypeInvalid; + + // legacy 3.3 server may only offer "vnc authentication" or "none" + secType = is->readU32(); + if (secType == secTypeInvalid) { + int reasonLen = is->readU32(); + char *reason = new char[reasonLen]; + is->readBytes(reason, reasonLen); + throw rfb::Exception(reason, "RfbPlayer"); + } + + if (secType != secTypeNone) { + throw rfb::Exception("Wrong authentication type in the session file", + "RfbPlayer Error"); + } + + state_ = RFBSTATE_INITIALISATION; +} + +void RfbProto::processInitMsg() { + vlog.debug("reading server initialisation"); + reader->readServerInit(); +} + +void RfbProto::serverInit() +{ + state_ = RFBSTATE_NORMAL; + vlog.debug("initialisation done"); +} diff --git a/rfbplayer/RfbProto.h b/rfbplayer/RfbProto.h new file mode 100644 index 00000000..7ad31777 --- /dev/null +++ b/rfbplayer/RfbProto.h @@ -0,0 +1,67 @@ +/* Copyright (C) 2004 TightVNC Team. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- RfbProto.h + +#include <rfb/CMsgReaderV3.h> +#include <rfb/CMsgHandler.h> +#include <rfb/CSecurity.h> +#include <rfb/secTypes.h> + +#include <rfbplayer/FbsInputStream.h> + +using namespace rfb; + +class RfbProto : public CMsgHandler { + public: + + RfbProto(char *fileName); + ~RfbProto(); + + void newSession(char *filename); + void initialiseProtocol(); + + void processMsg(); + + // serverInit() is called when the ServerInit message is received. The + // derived class must call on to CMsgHandler::serverInit(). + void serverInit(); + + enum stateEnum { + RFBSTATE_PROTOCOL_VERSION, + RFBSTATE_SECURITY, + RFBSTATE_INITIALISATION, + RFBSTATE_NORMAL, + RFBSTATE_INVALID + }; + + stateEnum state() { return state_; } + + protected: + void setState(stateEnum s) { state_ = s; } + virtual void framebufferUpdateEnd() {}; + + FbsInputStream* is; + CMsgReader* reader; + stateEnum state_; + + private: + void processVersionMsg(); + void processSecurityMsg(); + void processInitMsg(); +}; diff --git a/rfbplayer/buildTime.cxx b/rfbplayer/buildTime.cxx new file mode 100644 index 00000000..bab2e137 --- /dev/null +++ b/rfbplayer/buildTime.cxx @@ -0,0 +1 @@ +const char* buildTime = "Built on " __DATE__ " at " __TIME__;
\ No newline at end of file diff --git a/rfbplayer/resource.h b/rfbplayer/resource.h new file mode 100644 index 00000000..fb1f1325 --- /dev/null +++ b/rfbplayer/resource.h @@ -0,0 +1,20 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by rfbplayer.rc +// +#define IDD_DIALOGBAR 103 +#define IDI_ICON1 105 +#define IDC_BUTTON1 1000 +#define IDC_EDIT1 1001 +#define IDC_EDIT2 1002 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 128 +#define _APS_NEXT_COMMAND_VALUE 40011 +#define _APS_NEXT_CONTROL_VALUE 1003 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/rfbplayer/rfbplayer.cxx b/rfbplayer/rfbplayer.cxx new file mode 100644 index 00000000..21bd4fd1 --- /dev/null +++ b/rfbplayer/rfbplayer.cxx @@ -0,0 +1,1035 @@ +/* Copyright (C) 2004 TightVNC Team. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- RFB Player for Win32 + +#include <conio.h> + +#include <rfb/LogWriter.h> +#include <rfb/Exception.h> +#include <rfb/Threading.h> + +#include <rfb_win32/Win32Util.h> +#include <rfb_win32/WMShatter.h> + +#include <rfbplayer/rfbplayer.h> +#include <rfbplayer/utils.h> +#include <rfbplayer/resource.h> + +using namespace rfb; +using namespace rfb::win32; + +// -=- Variables & consts + +static LogWriter vlog("RfbPlayer"); + +TStr rfb::win32::AppName("RfbPlayer"); +extern const char* buildTime; + +// -=- RfbPlayer's defines + +#define strcasecmp _stricmp + +#define ID_START_BTN 0 +#define ID_POS_EDT 1 +#define ID_SPEED_EDT 2 +#define ID_POS_TXT 3 +#define ID_SPEED_TXT 4 +#define ID_CLIENT_STC 5 + +// -=- Custom thread class used to reading the rfb data + +class CRfbThread : public Thread { + public: + CRfbThread(RfbPlayer *_player) { + p = _player; + setDeleteAfterRun(); + }; + ~CRfbThread() {}; + + void run() { + long initTime = -1; + + // Process the rfb messages + while (p->run) { + try { + if (initTime >= 0) { + p->setPos(initTime); + initTime = -1; + } + if (!p->isSeeking()) + p->updatePos(); + p->processMsg(); + } catch (rdr::Exception e) { + if (strcmp(e.str(), "[End Of File]") == 0) { + p->rewind(); + p->setPaused(true); + continue; + } + // It's a special exception to perform backward seeking. + // We only rewind the stream and seek the offset + if (strcmp(e.str(), "[REWIND]") == 0) { + initTime = p->getSeekOffset(); + double speed = p->getSpeed(); + bool play = !p->isPaused(); + p->rewind(); + p->setSpeed(speed); + p->setPaused(!play); + } else { + MessageBox(p->getMainHandle(), e.str(), e.type(), MB_OK | MB_ICONERROR); + return; + } + } + } + } + + private: + RfbPlayer *p; +}; + +// +// -=- RfbPlayerClass + +// +// Window class used as the basis for RfbPlayer instance +// + +class RfbPlayerClass { +public: + RfbPlayerClass(); + ~RfbPlayerClass(); + ATOM classAtom; + HINSTANCE instance; +}; + +LRESULT CALLBACK RfbPlayerProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { + LRESULT result; + + if (msg == WM_CREATE) + SetWindowLong(hwnd, GWL_USERDATA, (long)((CREATESTRUCT*)lParam)->lpCreateParams); + else if (msg == WM_DESTROY) { + RfbPlayer* _this = (RfbPlayer*) GetWindowLong(hwnd, GWL_USERDATA); + _this->run = false; + + // Resume playback (It's need to quit from FbsInputStream::waitWhilePaused()) + _this->setPaused(false); + SetWindowLong(hwnd, GWL_USERDATA, 0); + } + RfbPlayer* _this = (RfbPlayer*) GetWindowLong(hwnd, GWL_USERDATA); + if (!_this) { + vlog.info("null _this in %x, message %u", hwnd, msg); + return DefWindowProc(hwnd, msg, wParam, lParam); + } + + try { + result = _this->processMainMessage(hwnd, msg, wParam, lParam); + } catch (rdr::Exception& e) { + vlog.error("untrapped: %s", e.str()); + } + + return result; +}; + +RfbPlayerClass::RfbPlayerClass() : classAtom(0) { + WNDCLASS wndClass; + wndClass.style = 0; + wndClass.lpfnWndProc = RfbPlayerProc; + wndClass.cbClsExtra = 0; + wndClass.cbWndExtra = 0; + wndClass.hInstance = instance = GetModuleHandle(0); + wndClass.hIcon = (HICON)LoadImage(GetModuleHandle(0), + MAKEINTRESOURCE(IDI_ICON1), IMAGE_ICON, 0, 0, LR_SHARED); + if (!wndClass.hIcon) + printf("unable to load icon:%ld", GetLastError()); + wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); + wndClass.hbrBackground = HBRUSH(COLOR_WINDOW); + wndClass.lpszMenuName = 0; + wndClass.lpszClassName = _T("RfbPlayerClass"); + classAtom = RegisterClass(&wndClass); + if (!classAtom) { + throw rdr::SystemException("unable to register RfbPlayer window class", + GetLastError()); + } +} + +RfbPlayerClass::~RfbPlayerClass() { + if (classAtom) { + UnregisterClass((const TCHAR*)classAtom, instance); + } +} + +RfbPlayerClass baseClass; + +// +// -=- RfbFrameClass + +// +// Window class used to displaying the rfb data +// + +class RfbFrameClass { +public: + RfbFrameClass(); + ~RfbFrameClass(); + ATOM classAtom; + HINSTANCE instance; +}; + +LRESULT CALLBACK FrameProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { + LRESULT result; + + if (msg == WM_CREATE) + SetWindowLong(hwnd, GWL_USERDATA, (long)((CREATESTRUCT*)lParam)->lpCreateParams); + else if (msg == WM_DESTROY) + SetWindowLong(hwnd, GWL_USERDATA, 0); + RfbPlayer* _this = (RfbPlayer*) GetWindowLong(hwnd, GWL_USERDATA); + if (!_this) { + vlog.info("null _this in %x, message %u", hwnd, msg); + return DefWindowProc(hwnd, msg, wParam, lParam); + } + + try { + result = _this->processFrameMessage(hwnd, msg, wParam, lParam); + } catch (rdr::Exception& e) { + vlog.error("untrapped: %s", e.str()); + } + + return result; +} + +RfbFrameClass::RfbFrameClass() : classAtom(0) { + WNDCLASS wndClass; + wndClass.style = 0; + wndClass.lpfnWndProc = FrameProc; + wndClass.cbClsExtra = 0; + wndClass.cbWndExtra = 0; + wndClass.hInstance = instance = GetModuleHandle(0); + wndClass.hIcon = 0; + wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); + wndClass.hbrBackground = 0; + wndClass.lpszMenuName = 0; + wndClass.lpszClassName = _T("RfbPlayerClass1"); + classAtom = RegisterClass(&wndClass); + if (!classAtom) { + throw rdr::SystemException("unable to register RfbPlayer window class", + GetLastError()); + } +} + +RfbFrameClass::~RfbFrameClass() { + if (classAtom) { + UnregisterClass((const TCHAR*)classAtom, instance); + } +} + +RfbFrameClass frameClass; + +// +// -=- RfbPlayer instance implementation +// + +RfbPlayer::RfbPlayer(char *_fileName, long _initTime = 0, double _playbackSpeed = 1.0, + bool _autoplay = false, bool _showControls = true, + bool _acceptBell = false) +: RfbProto(_fileName), initTime(_initTime), playbackSpeed(_playbackSpeed), + autoplay(_autoplay), showControls(_showControls), buffer(0), client_size(0, 0, 32, 32), + window_size(0, 0, 32, 32), cutText(0), seekMode(false), fileName(_fileName), run(true), + serverInitTime(0), btnStart(0), txtPos(0), editPos(0), txtSpeed(0), editSpeed(0), + lastPos(0), acceptBell(_acceptBell) { + + if (showControls) + CTRL_BAR_HEIGHT = 30; + else + CTRL_BAR_HEIGHT = 0; + + // Create the main window + const TCHAR* name = _T("RfbPlayer"); + mainHwnd = CreateWindow((const TCHAR*)baseClass.classAtom, name, WS_OVERLAPPEDWINDOW, + 0, 0, 10, 10, 0, 0, baseClass.instance, this); + if (!mainHwnd) { + throw rdr::SystemException("unable to create WMNotifier window instance", GetLastError()); + } + vlog.debug("created window \"%s\" (%x)", (const char*)CStr(name), getMainHandle()); + + // Create the backing buffer + buffer = new win32::DIBSectionBuffer(getFrameHandle()); +} + +RfbPlayer::~RfbPlayer() { + vlog.debug("~RfbPlayer"); + if (mainHwnd) { + setVisible(false); + DestroyWindow(mainHwnd); + mainHwnd = 0; + } + delete buffer; + delete cutText; + vlog.debug("~RfbPlayer done"); +} + +// RfbPlayer control's tabstop processing + +WNDPROC OldProc[3]; +static HWND focusHwnd = 0; + +LRESULT CALLBACK TabProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { + int CONTROL_ID = GetWindowLong(hwnd, GWL_ID); + + if (msg == WM_DESTROY) + SetWindowLong(hwnd, GWL_USERDATA, 0); + + RfbPlayer* _this = (RfbPlayer*) GetWindowLong(hwnd, GWL_USERDATA); + if (!_this) + return CallWindowProc(OldProc[CONTROL_ID], hwnd, msg, wParam, lParam); + + switch (msg) { + case WM_KEYDOWN: + + // Process tab pressing + if (wParam == VK_TAB) { + switch (CONTROL_ID){ + case ID_START_BTN: + focusHwnd = _this->getPosEdit(); + break; + case ID_POS_EDT: + focusHwnd = _this->getSpeedEdit(); + break; + case ID_SPEED_EDT: + focusHwnd = _this->getStartBtn(); + break; + } + SetFocus(focusHwnd); + } + + // Process return/enter pressing (set the speed and the position) + if (wParam == VK_RETURN) { + switch (CONTROL_ID){ + + // Change the position + + case ID_POS_EDT: + { + char posTxt[20]; + long pos; + GetWindowText(_this->getPosEdit(), posTxt, 10); + pos = atol(posTxt); + if (pos != long(_this->getTimeOffset() / 1000) && (pos >= 0)) + _this->setPos(pos * 1000); + else + SetWindowText(_this->getPosEdit(), LongToStr(_this->getTimeOffset() / 1000)); + } + break; + + // Change the playback speed + + case ID_SPEED_EDT: + { + char speedTxt[20]; + double speed; + GetWindowText(_this->getSpeedEdit(), speedTxt, 10); + speed = atof(speedTxt); + if ((speed != _this->getSpeed()) && (speed != 0)) + _this->setSpeed(speed); + else + SetWindowText(_this->getSpeedEdit(), DoubleToStr(_this->getSpeed())); + } + break; + } + } + break; + + case WM_SETFOCUS: + focusHwnd = hwnd; + break; + } + + return CallWindowProc(OldProc[CONTROL_ID], hwnd, msg, wParam, lParam); +} + +LRESULT +RfbPlayer::processMainMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { + switch (msg) { + + // -=- Process standard window messages + + case WM_SETFOCUS: + { + if (focusHwnd) + SetFocus(focusHwnd); + else + SetFocus(btnStart); + } + break; + + case WM_CREATE: + { + // Create the player controls + if (showControls) { + btnStart = CreateWindow("BUTTON", "Stop", WS_CHILD | WS_VISIBLE | + BS_PUSHBUTTON | BS_NOTIFY, 5, 5, 60, 20, hwnd, (HMENU)ID_START_BTN, + baseClass.instance, NULL); + txtPos = CreateWindow("STATIC", "Position:", WS_CHILD | WS_VISIBLE, + 70, 5, 60, 20, hwnd, (HMENU)ID_POS_TXT, baseClass.instance, NULL); + editPos = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", "0", WS_CHILD | + WS_VISIBLE | ES_NUMBER |ES_RIGHT, 135, 5, 60, 20, hwnd, + (HMENU)ID_POS_EDT, baseClass.instance, this); + txtSpeed = CreateWindow("STATIC", "Speed:", WS_CHILD | WS_VISIBLE, + 200, 5, 50, 20, hwnd, (HMENU)ID_SPEED_TXT, baseClass.instance, NULL); + editSpeed = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", "1.0", WS_CHILD | + WS_VISIBLE | ES_RIGHT, 255, 5, 60, 20, hwnd, (HMENU)ID_SPEED_EDT, + baseClass.instance, this); + + // Windows subclassing (It's used to implement "TabStop") + OldProc[0] = (WNDPROC)SetWindowLong(btnStart, GWL_WNDPROC, (LONG)TabProc); + OldProc[1] = (WNDPROC)SetWindowLong(editPos, GWL_WNDPROC, (LONG)TabProc); + OldProc[2] = (WNDPROC)SetWindowLong(editSpeed, GWL_WNDPROC, (LONG)TabProc); + + SetWindowLong(btnStart, GWL_USERDATA, (long)this); + SetWindowLong(editPos, GWL_USERDATA, (long)this); + SetWindowLong(editSpeed, GWL_USERDATA, (long)this); + } + + // Create the frame window + frameHwnd = CreateWindowEx(WS_EX_CLIENTEDGE, (const TCHAR*)frameClass.classAtom, + 0, WS_CHILD | WS_VISIBLE, 0, CTRL_BAR_HEIGHT, 10, CTRL_BAR_HEIGHT + 10, + hwnd, 0, frameClass.instance, this); + + return 0; + } + + // Process the start button messages + + case WM_COMMAND: + { + if ((LOWORD(wParam) == ID_START_BTN) && (HIWORD(wParam) == BN_CLICKED)) { + if (is->isPaused()) { + long pos; + double speed; + char str[20]; + + // Change the playback speed + GetWindowText(editSpeed, str, 10); + speed = atof(str); + if ((speed != getSpeed()) && (speed != 0)) + setSpeed(speed); + + // Change the position + GetWindowText(editPos, str, 10); + pos = atol(str); + if (pos != long(getTimeOffset() / 1000)) + setPos(pos * 1000); + setPaused(false); + } else { + setPaused(true); + updatePos(); + } + } + } + break; + + // Update frame's window size and add scrollbars if required + + case WM_SIZE: + { + Point old_offset = bufferToClient(Point(0, 0)); + + // Update the cached sizing information + RECT r; + GetClientRect(getMainHandle(), &r); + MoveWindow(getFrameHandle(), 0, CTRL_BAR_HEIGHT, r.right - r.left, + r.bottom - r.top - CTRL_BAR_HEIGHT, TRUE); + + GetWindowRect(getFrameHandle(), &r); + window_size = Rect(r.left, r.top, r.right, r.bottom); + GetClientRect(getFrameHandle(), &r); + client_size = Rect(r.left, r.top, r.right, r.bottom); + + // Determine whether scrollbars are required + calculateScrollBars(); + + // Redraw if required + if (!old_offset.equals(bufferToClient(Point(0, 0)))) + InvalidateRect(getFrameHandle(), 0, TRUE); + } + break; + + case WM_CLOSE: + vlog.debug("WM_CLOSE %x", getMainHandle()); + PostQuitMessage(0); + break; + } + + return rfb::win32::SafeDefWindowProc(getMainHandle(), msg, wParam, lParam); +} + +LRESULT RfbPlayer::processFrameMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { + switch (msg) { + + case WM_PAINT: + { + if (is->isSeeking()) { + seekMode = true; + return 0; + } else { + if (seekMode) { + seekMode = false; + InvalidateRect(getFrameHandle(), 0, true); + UpdateWindow(getFrameHandle()); + return 0; + } + } + + PAINTSTRUCT ps; + HDC paintDC = BeginPaint(getFrameHandle(), &ps); + if (!paintDC) + throw SystemException("unable to BeginPaint", GetLastError()); + Rect pr = Rect(ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right, ps.rcPaint.bottom); + + if (!pr.is_empty()) { + + if (buffer->bitmap) { + + // Get device context + BitmapDC bitmapDC(paintDC, buffer->bitmap); + + // Blit the border if required + Rect bufpos = bufferToClient(buffer->getRect()); + if (!pr.enclosed_by(bufpos)) { + vlog.debug("draw border"); + HBRUSH black = (HBRUSH) GetStockObject(BLACK_BRUSH); + RECT r; + SetRect(&r, 0, 0, bufpos.tl.x, client_size.height()); FillRect(paintDC, &r, black); + SetRect(&r, bufpos.tl.x, 0, bufpos.br.x, bufpos.tl.y); FillRect(paintDC, &r, black); + SetRect(&r, bufpos.br.x, 0, client_size.width(), client_size.height()); FillRect(paintDC, &r, black); + SetRect(&r, bufpos.tl.x, bufpos.br.y, bufpos.br.x, client_size.height()); FillRect(paintDC, &r, black); + } + + // Do the blit + Point buf_pos = clientToBuffer(pr.tl); + if (!BitBlt(paintDC, pr.tl.x, pr.tl.y, pr.width(), pr.height(), + bitmapDC, buf_pos.x, buf_pos.y, SRCCOPY)) + throw SystemException("unable to BitBlt to window", GetLastError()); + + } else { + // Blit a load of black + if (!BitBlt(paintDC, pr.tl.x, pr.tl.y, pr.width(), pr.height(), + 0, 0, 0, BLACKNESS)) + throw SystemException("unable to BitBlt to blank window", GetLastError()); + } + } + EndPaint(getFrameHandle(), &ps); + } + return 0; + + case WM_VSCROLL: + case WM_HSCROLL: + { + Point delta; + int newpos = (msg == WM_VSCROLL) ? scrolloffset.y : scrolloffset.x; + + switch (LOWORD(wParam)) { + case SB_PAGEUP: newpos -= 50; break; + case SB_PAGEDOWN: newpos += 50; break; + case SB_LINEUP: newpos -= 5; break; + case SB_LINEDOWN: newpos += 5; break; + case SB_THUMBTRACK: + case SB_THUMBPOSITION: newpos = HIWORD(wParam); break; + default: vlog.info("received unknown scroll message"); + }; + + if (msg == WM_HSCROLL) + setViewportOffset(Point(newpos, scrolloffset.y)); + else + setViewportOffset(Point(scrolloffset.x, newpos)); + + SCROLLINFO si; + si.cbSize = sizeof(si); + si.fMask = SIF_POS; + si.nPos = newpos; + SetScrollInfo(getFrameHandle(), (msg == WM_VSCROLL) ? SB_VERT : SB_HORZ, &si, TRUE); + } + break; + } + + return DefWindowProc(hwnd, msg, wParam, lParam); +} + +void RfbPlayer::setOptions(long _initTime = 0, double _playbackSpeed = 1.0, + bool _autoplay = false, bool _showControls = true) { + showControls = _showControls; + autoplay = _autoplay; + playbackSpeed = _playbackSpeed; + initTime = _initTime; +} + +void RfbPlayer::applyOptions() { + if (initTime >= 0) + setPos(initTime); + setSpeed(playbackSpeed); + setPaused(!autoplay); + + // Update the position + SetWindowText(editPos, LongToStr(initTime / 1000)); +} + +void RfbPlayer::setVisible(bool visible) { + ShowWindow(getMainHandle(), visible ? SW_SHOW : SW_HIDE); + if (visible) { + // When the window becomes visible, make it active + SetForegroundWindow(getMainHandle()); + SetActiveWindow(getMainHandle()); + } +} + +void RfbPlayer::setTitle(const char *title) { + char _title[256]; + strcpy(_title, AppName); + strcat(_title, " - "); + strcat(_title, title); + SetWindowText(getMainHandle(), _title); +} + +void RfbPlayer::setFrameSize(int width, int height) { + // Calculate and set required size for main window + RECT r = {0, 0, width, height}; + AdjustWindowRectEx(&r, GetWindowLong(getFrameHandle(), GWL_STYLE), FALSE, + GetWindowLong(getFrameHandle(), GWL_EXSTYLE)); + r.bottom += CTRL_BAR_HEIGHT; // Include RfbPlayr's controls area + AdjustWindowRect(&r, GetWindowLong(getMainHandle(), GWL_STYLE), FALSE); + SetWindowPos(getMainHandle(), 0, 0, 0, r.right-r.left, r.bottom-r.top, + SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOOWNERZORDER); + + // Enable/disable scrollbars as appropriate + calculateScrollBars(); +} + +void RfbPlayer::calculateScrollBars() { + // Calculate the required size of window + DWORD current_style = GetWindowLong(getFrameHandle(), GWL_STYLE); + DWORD style = current_style & ~(WS_VSCROLL | WS_HSCROLL); + DWORD old_style; + RECT r; + SetRect(&r, 0, 0, buffer->width(), buffer->height()); + AdjustWindowRectEx(&r, style, FALSE, GetWindowLong(getFrameHandle(), GWL_EXSTYLE)); + Rect reqd_size = Rect(r.left, r.top, r.right, r.bottom); + + // Work out whether scroll bars are required + do { + old_style = style; + + if (!(style & WS_HSCROLL) && (reqd_size.width() > window_size.width())) { + style |= WS_HSCROLL; + reqd_size.br.y += GetSystemMetrics(SM_CXHSCROLL); + } + if (!(style & WS_VSCROLL) && (reqd_size.height() > window_size.height())) { + style |= WS_VSCROLL; + reqd_size.br.x += GetSystemMetrics(SM_CXVSCROLL); + } + } while (style != old_style); + + // Tell Windows to update the window style & cached settings + if (style != current_style) { + SetWindowLong(getFrameHandle(), GWL_STYLE, style); + SetWindowPos(getFrameHandle(), NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); + } + + // Update the scroll settings + SCROLLINFO si; + if (style & WS_VSCROLL) { + si.cbSize = sizeof(si); + si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS; + si.nMin = 0; + si.nMax = buffer->height(); + si.nPage = buffer->height() - (reqd_size.height() - window_size.height()); + maxscrolloffset.y = max(0, si.nMax-si.nPage); + scrolloffset.y = min(maxscrolloffset.y, scrolloffset.y); + si.nPos = scrolloffset.y; + SetScrollInfo(getFrameHandle(), SB_VERT, &si, TRUE); + } + if (style & WS_HSCROLL) { + si.cbSize = sizeof(si); + si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS; + si.nMin = 0; + si.nMax = buffer->width(); + si.nPage = buffer->width() - (reqd_size.width() - window_size.width()); + maxscrolloffset.x = max(0, si.nMax-si.nPage); + scrolloffset.x = min(maxscrolloffset.x, scrolloffset.x); + si.nPos = scrolloffset.x; + SetScrollInfo(getFrameHandle(), SB_HORZ, &si, TRUE); + } +} + +bool RfbPlayer::setViewportOffset(const Point& tl) { +/* *** + Point np = Point(max(0, min(maxscrolloffset.x, tl.x)), + max(0, min(maxscrolloffset.y, tl.y))); + */ + Point np = Point(max(0, min(tl.x, buffer->width()-client_size.width())), + max(0, min(tl.y, buffer->height()-client_size.height()))); + Point delta = np.translate(scrolloffset.negate()); + if (!np.equals(scrolloffset)) { + scrolloffset = np; + ScrollWindowEx(getFrameHandle(), -delta.x, -delta.y, 0, 0, 0, 0, SW_INVALIDATE); + UpdateWindow(getFrameHandle()); + return true; + } + return false; +} + +void RfbPlayer::close(const char* reason) { + setVisible(false); + if (reason) { + vlog.info("closing - %s", reason); + MessageBox(NULL, TStr(reason), "RfbPlayer", MB_ICONINFORMATION | MB_OK); + } + SendMessage(getFrameHandle(), WM_CLOSE, 0, 0); +} + +void RfbPlayer::blankBuffer() { + fillRect(buffer->getRect(), 0); +} + +void RfbPlayer::rewind() { + blankBuffer(); + newSession(fileName); + skipHandshaking(); +} + +void RfbPlayer::serverInit() { + RfbProto::serverInit(); + + // Save the server init time for using in setPos() + serverInitTime = getTimeOffset() / getSpeed(); + + // Resize the backing buffer + buffer->setSize(cp.width, cp.height); + + // Check on the true colour mode + if (!(cp.pf()).trueColour) + throw rdr::Exception("This version plays only true colour session!"); + + // Set the session pixel format + buffer->setPF(cp.pf()); + + // If the window is not maximised then resize it + if (!(GetWindowLong(getMainHandle(), GWL_STYLE) & WS_MAXIMIZE)) + setFrameSize(cp.width, cp.height); + + // Set the window title and show it + setTitle(cp.name()); + setVisible(true); + + // Set the player's param + applyOptions(); +} + +void RfbPlayer::setColourMapEntries(int first, int count, U16* rgbs) { + vlog.debug("setColourMapEntries: first=%d, count=%d", first, count); + throw rdr::Exception("Can't handle SetColourMapEntries message", "RfbPlayer"); +/* int i; + for (i=0;i<count;i++) { + buffer->setColour(i+first, rgbs[i*3], rgbs[i*3+1], rgbs[i*3+2]); + } + // *** change to 0, 256? + refreshWindowPalette(first, count); + palette_changed = true; + InvalidateRect(getFrameHandle(), 0, FALSE);*/ +} + +void RfbPlayer::bell() { + if (acceptBell) + MessageBeep(-1); +} + +void RfbPlayer::serverCutText(const char* str, int len) { + if (cutText != NULL) + delete [] cutText; + cutText = new char[len + 1]; + memcpy(cutText, str, len); + cutText[len] = '\0'; +} + +void RfbPlayer::frameBufferUpdateEnd() { +}; + +void RfbPlayer::beginRect(const Rect& r, unsigned int encoding) { +} + +void RfbPlayer::endRect(const Rect& r, unsigned int encoding) { +} + + +void RfbPlayer::fillRect(const Rect& r, Pixel pix) { + buffer->fillRect(r, pix); + invalidateBufferRect(r); +} + +void RfbPlayer::imageRect(const Rect& r, void* pixels) { + buffer->imageRect(r, pixels); + invalidateBufferRect(r); +} + +void RfbPlayer::copyRect(const Rect& r, int srcX, int srcY) { + buffer->copyRect(r, Point(r.tl.x-srcX, r.tl.y-srcY)); + invalidateBufferRect(r); +} + +bool RfbPlayer::invalidateBufferRect(const Rect& crect) { + Rect rect = bufferToClient(crect); + if (rect.intersect(client_size).is_empty()) return false; + RECT invalid = {rect.tl.x, rect.tl.y, rect.br.x, rect.br.y}; + InvalidateRect(getFrameHandle(), &invalid, FALSE); + return true; +} + +void RfbPlayer::setPaused(bool paused) { + if (paused) { + if (btnStart) SetWindowText(btnStart, "Start"); + is->pausePlayback(); + } else { + if (btnStart) SetWindowText(btnStart, "Stop"); + is->resumePlayback(); + } +} + +void RfbPlayer::setSpeed(double speed) { + serverInitTime = serverInitTime * getSpeed() / speed; + is->setSpeed(speed); + if (editSpeed) + SetWindowText(editSpeed, DoubleToStr(speed, 1)); +} + +double RfbPlayer::getSpeed() { + return is->getSpeed(); +} + +void RfbPlayer::setPos(long pos) { + is->setTimeOffset(max(pos, serverInitTime)); +} + +long RfbPlayer::getSeekOffset() { + return is->getSeekOffset(); +} + +bool RfbPlayer::isSeeking() { + return is->isSeeking(); +} + +bool RfbPlayer::isSeekMode() { + return seekMode; +} + +bool RfbPlayer::isPaused() { + return is->isPaused(); +} + +long RfbPlayer::getTimeOffset() { + return is->getTimeOffset(); +} + +void RfbPlayer::updatePos() { + long newPos = is->getTimeOffset() / 1000; + if (editPos && lastPos != newPos) { + lastPos = newPos; + SetWindowText(editPos, LongToStr(lastPos)); + } +} + +void RfbPlayer::skipHandshaking() { + int skipBytes = 12 + 4 + 24 + strlen(cp.name()); + is->skip(skipBytes); + state_ = RFBSTATE_NORMAL; +} + +void programInfo() { + win32::FileVersionInfo inf; + _tprintf(_T("%s - %s, Version %s\n"), + inf.getVerString(_T("ProductName")), + inf.getVerString(_T("FileDescription")), + inf.getVerString(_T("FileVersion"))); + printf("%s\n", buildTime); + _tprintf(_T("%s\n\n"), inf.getVerString(_T("LegalCopyright"))); +} + +void programUsage() { + printf("usage: rfbplayer <options> <filename>\n"); + printf("Command-line options:\n"); + printf(" -help - Provide usage information.\n"); + printf(" -speed <value> - Sets playback speed, where 1 is normal speed,\n"); + printf(" 2 is double speed, 0.5 is half speed. Default: 1.0.\n"); + printf(" -pos <ms> - Sets initial time position in the session file,\n"); + printf(" in milliseconds. Default: 0.\n"); + printf(" -autoplay <yes|no> - Runs the player in the playback mode. Default: \"no\".\n"); + printf(" -controls <yes|no> - Shows the control panel at the top. Default: \"yes\".\n"); + printf(" -bell <yes|no> - Accepts the bell. Default: \"no\".\n"); +} + +double playbackSpeed = 1.0; +long initTime = -1; +bool autoplay = false; +bool showControls = true; +char *fileName; +bool console = false; +bool wrong_param = false; +bool print_usage = false; +bool acceptBell = false; + +bool processParams(int argc, char* argv[]) { + for (int i = 1; i < argc; i++) { + if ((strcasecmp(argv[i], "-help") == 0) || + (strcasecmp(argv[i], "--help") == 0) || + (strcasecmp(argv[i], "/help") == 0) || + (strcasecmp(argv[i], "-h") == 0) || + (strcasecmp(argv[i], "/h") == 0) || + (strcasecmp(argv[i], "/?") == 0)) { + print_usage = true; + return true; + } + + if ((strcasecmp(argv[i], "-speed") == 0) || + (strcasecmp(argv[i], "/speed") == 0) && (i < argc-1)) { + playbackSpeed = atof(argv[++i]); + if (playbackSpeed <= 0) { + return false; + } + continue; + } + + if ((strcasecmp(argv[i], "-pos") == 0) || + (strcasecmp(argv[i], "/pos") == 0) && (i < argc-1)) { + initTime = atol(argv[++i]); + if (initTime <= 0) + return false; + continue; + } + + if ((strcasecmp(argv[i], "-autoplay") == 0) || + (strcasecmp(argv[i], "/autoplay") == 0) && (i < argc-1)) { + i++; + if (strcasecmp(argv[i], "yes") == 0) { + autoplay = true; + continue; + } + if (strcasecmp(argv[i], "no") == 0) { + autoplay = false; + continue; + } + return false; + } + + if ((strcasecmp(argv[i], "-controls") == 0) || + (strcasecmp(argv[i], "/controls") == 0) && (i < argc-1)) { + i++; + if (strcasecmp(argv[i], "yes") == 0) { + showControls = true; + continue; + } + if (strcasecmp(argv[i], "no") == 0) { + showControls = false; + continue; + } + return false; + } + + if ((strcasecmp(argv[i], "-bell") == 0) || + (strcasecmp(argv[i], "/bell") == 0) && (i < argc-1)) { + i++; + if (strcasecmp(argv[i], "yes") == 0) { + acceptBell = true; + continue; + } + if (strcasecmp(argv[i], "no") == 0) { + acceptBell = false; + continue; + } + return false; + } + + if (i != argc - 1) + return false; + } + + fileName = strDup(argv[argc-1]); + return true; +} + +// +// -=- WinMain +// + +int WINAPI WinMain(HINSTANCE inst, HINSTANCE prevInst, char* cmdLine, int cmdShow) { + + // - Process the command-line + + int argc = __argc; + char** argv = __argv; + if (argc > 1) { + wrong_param = !processParams(argc, argv); + console = print_usage | wrong_param; + } else { + console = true; + } + + if (console) { + AllocConsole(); + freopen("CONOUT$","wb",stdout); + + programInfo(); + if (wrong_param) + printf("Wrong a command line.\n"); + else + programUsage(); + + printf("\nPress Enter/Return key to continue\n"); + char c = getch(); + FreeConsole(); + + return 0; + } + + // Create the player and the thread which reading the rfb data + RfbPlayer *player = NULL; + CRfbThread *rfbThread = NULL; + try { + player = new RfbPlayer(fileName, initTime, playbackSpeed, autoplay, + showControls, acceptBell); + rfbThread = new CRfbThread(player); + rfbThread->start(); + } catch (rdr::Exception e) { + MessageBox(NULL, e.str(), e.type(), MB_OK | MB_ICONERROR); + delete player; + return 0; + } + + // Run the player + MSG msg; + while (GetMessage(&msg, NULL, 0, 0) > 0) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + // Wait while the thread destroying and then destroy the player + try{ + while (rfbThread->getState() == ThreadStarted) {} + if (player) delete player; + } catch (rdr::Exception e) { + MessageBox(NULL, e.str(), e.type(), MB_OK | MB_ICONERROR); + } + + return 0; +}; diff --git a/rfbplayer/rfbplayer.dsp b/rfbplayer/rfbplayer.dsp new file mode 100644 index 00000000..14b2faf0 --- /dev/null +++ b/rfbplayer/rfbplayer.dsp @@ -0,0 +1,147 @@ +# Microsoft Developer Studio Project File - Name="rfbplayer" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=rfbplayer - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "rfbplayer.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "rfbplayer.mak" CFG="rfbplayer - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "rfbplayer - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "rfbplayer - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "rfbplayer - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "..\Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I ".." /D "NDEBUG" /D "_WINDOWS" /D "WIN32" /D "_MBCS" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x419 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib advapi32.lib shell32.lib uuid.lib version.lib /nologo /subsystem:windows /machine:I386 + +!ELSEIF "$(CFG)" == "rfbplayer - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "..\Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /D "_DEBUG" /D "_WINDOWS" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x419 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# ADD LINK32 user32.lib gdi32.lib advapi32.lib ws2_32.lib version.lib comctl32.lib shell32.lib comdlg32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# Begin Special Build Tool +SOURCE="$(InputPath)" +PreLink_Cmds=cl /c /nologo /FoDebug\ /FdDebug\ /MTd buildTime.cxx +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "rfbplayer - Win32 Release" +# Name "rfbplayer - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\buildTime.cxx +# End Source File +# Begin Source File + +SOURCE=.\FbsInputStream.cxx +# End Source File +# Begin Source File + +SOURCE=.\rfbplayer.cxx +# End Source File +# Begin Source File + +SOURCE=.\rfbplayer.rc +# End Source File +# Begin Source File + +SOURCE=.\RfbProto.cxx +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\FbsInputStream.h +# End Source File +# Begin Source File + +SOURCE=.\rfbplayer.h +# End Source File +# Begin Source File + +SOURCE=.\RfbProto.h +# End Source File +# Begin Source File + +SOURCE=.\utils.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# Begin Source File + +SOURCE=.\rfbplayer.ico +# End Source File +# End Group +# End Target +# End Project diff --git a/rfbplayer/rfbplayer.h b/rfbplayer/rfbplayer.h new file mode 100644 index 00000000..ecf1632c --- /dev/null +++ b/rfbplayer/rfbplayer.h @@ -0,0 +1,165 @@ +/* Copyright (C) 2004 TightVNC Team. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- RfbPlayer.h + +#include <windows.h> + +#include <rfb_win32/DIBSectionBuffer.h> + +#include <rfbplayer/RfbProto.h> + +using namespace rfb; +using namespace rfb::win32; + +class RfbPlayer : public RfbProto{ + public: + RfbPlayer(char *filename, long _pos, double s_peed, bool autoplay, + bool _showControls, bool _acceptBell); + ~RfbPlayer(); + + // -=- Window Message handling + + LRESULT processMainMessage(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam); + LRESULT processFrameMessage(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam); + + // -=- Window interface + + HWND getMainHandle() const {return mainHwnd;} + HWND getFrameHandle() const {return frameHwnd;} + HWND getStartBtn() const {return btnStart;} + HWND getPosEdit() const {return editPos;} + HWND getSpeedEdit() const {return editSpeed;} + void setFrameSize(int width, int height); + void setVisible(bool visible); + void setTitle(const char *title); + void calculateScrollBars(); + void close(const char* reason=0); + void updatePos(); + + // -=- Coordinate conversions + + inline Point bufferToClient(const Point& p) { + Point pos = p; + if (client_size.width() > buffer->width()) + pos.x += (client_size.width() - buffer->width()) / 2; + else if (client_size.width() < buffer->width()) + pos.x -= scrolloffset.x; + if (client_size.height() > buffer->height()) + pos.y += (client_size.height() - buffer->height()) / 2; + else if (client_size.height() < buffer->height()) + pos.y -= scrolloffset.y; + return pos; + } + inline Rect bufferToClient(const Rect& r) { + return Rect(bufferToClient(r.tl), bufferToClient(r.br)); + } + + inline Point clientToBuffer(const Point& p) { + Point pos = p; + if (client_size.width() > buffer->width()) + pos.x -= (client_size.width() - buffer->width()) / 2; + else if (client_size.width() < buffer->width()) + pos.x += scrolloffset.x; + if (client_size.height() > buffer->height()) + pos.y -= (client_size.height() - buffer->height()) / 2; + else if (client_size.height() < buffer->height()) + pos.y += scrolloffset.y; + return pos; + } + inline Rect clientToBuffer(const Rect& r) { + return Rect(clientToBuffer(r.tl), clientToBuffer(r.br)); + } + + bool setViewportOffset(const Point& tl); + + // -=- RfbProto interface overrides + + virtual void serverInit(); + virtual void frameBufferUpdateEnd(); + virtual void setColourMapEntries(int first, int count, U16* rgbs); + virtual void serverCutText(const char* str, int len); + virtual void bell(); + + virtual void beginRect(const Rect& r, unsigned int encoding); + virtual void endRect(const Rect& r, unsigned int encoding); + virtual void fillRect(const Rect& r, Pixel pix); + virtual void imageRect(const Rect& r, void* pixels); + virtual void copyRect(const Rect& r, int srcX, int srcY); + + // -=- Functions used to manage player's parameters + + void setOptions(long pos, double speed, bool autoPlay, bool showControls); + void applyOptions(); + + // -=- Player functions + + // skipHandshaking() - is implemented to skip the initial handshaking when + // perform backward seeking OR replaying. + void skipHandshaking(); + + void blankBuffer(); + void rewind(); + void setPaused(bool paused); + long getTimeOffset(); + bool isSeekMode(); + bool isSeeking(); + bool isPaused(); + long getSeekOffset(); + void setPos(long pos); + void setSpeed(double speed); + double getSpeed(); + + char *fileName; + + // run is a flag which display the player status (running or destroing). + bool run; + + private: + bool seekMode; + long lastPos; + int CTRL_BAR_HEIGHT; + + protected: + + // Returns true if part of the supplied rect is visible, false otherwise + bool invalidateBufferRect(const Rect& crect); + + // Local window state + HWND mainHwnd; + HWND btnStart; + HWND txtPos; + HWND editPos; + HWND txtSpeed; + HWND editSpeed; + HWND frameHwnd; + Rect window_size; + Rect client_size; + Point scrolloffset; + Point maxscrolloffset; + char *cutText; + win32::DIBSectionBuffer* buffer; + + // The player's parameters + bool showControls; + bool autoplay; + double playbackSpeed; + long initTime; + long serverInitTime; + bool acceptBell; +}; diff --git a/rfbplayer/rfbplayer.ico b/rfbplayer/rfbplayer.ico Binary files differnew file mode 100644 index 00000000..c9136bfe --- /dev/null +++ b/rfbplayer/rfbplayer.ico diff --git a/rfbplayer/rfbplayer.rc b/rfbplayer/rfbplayer.rc new file mode 100644 index 00000000..db4240c5 --- /dev/null +++ b/rfbplayer/rfbplayer.rc @@ -0,0 +1,118 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_ICON1 ICON DISCARDABLE "rfbplayer.ico" + +#ifndef _MAC +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "080904b0" + BEGIN + VALUE "Comments", "\0" + VALUE "CompanyName", "TightVNC Team\0" + VALUE "FileDescription", "RFB Session Player for Win32\0" + VALUE "FileVersion", "1, 0, 0, 1\0" + VALUE "InternalName", "rfbplayer\0" + VALUE "LegalCopyright", "Copyright (C) 2004 TightVNC Team.\0" + VALUE "LegalTrademarks", "\0" + VALUE "OriginalFilename", "rfbplayer.exe\0" + VALUE "PrivateBuild", "\0" + VALUE "ProductName", "RFB Session Player 1.0\0" + VALUE "ProductVersion", "1, 0, 0, 1\0" + VALUE "SpecialBuild", "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x809, 1200 + END +END + +#endif // !_MAC + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/rfbplayer/utils.h b/rfbplayer/utils.h new file mode 100644 index 00000000..abf48606 --- /dev/null +++ b/rfbplayer/utils.h @@ -0,0 +1,45 @@ +/* Copyright (C) 2004 TightVNC Team. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- utils.h + +// Convert a long integer to a string. + +char *LongToStr(long num) { + char str[20]; + _ltoa(num, str, 10); + return strDup(str); +} + +// Convert a double to a string: +// len - number of digits after decimal point. + +char *DoubleToStr(double num, int lenAfterPoint=1) { + int decimal, sign; + char *str; + char dstr[20]; + str = _fcvt(num, lenAfterPoint, &decimal, &sign); + int len = lenAfterPoint + decimal + sign + 2; + dstr[0] = '\0'; + if (sign) + strcat(dstr, "-"); + strncat(dstr, str, decimal); + strcat(dstr, "."); + strncat(dstr, str + decimal, len - int(dstr + decimal)); + return strDup(dstr); +} @@ -84,6 +84,33 @@ Package=<4> ############################################################################### +Project: "rfbplayer"=.\rfbplayer\rfbplayer.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name rdr + End Project Dependency + Begin Project Dependency + Project_Dep_Name rfb + End Project Dependency + Begin Project Dependency + Project_Dep_Name rfb_win32 + End Project Dependency + Begin Project Dependency + Project_Dep_Name Xregion + End Project Dependency + Begin Project Dependency + Project_Dep_Name zlib + End Project Dependency +}}} + +############################################################################### + Project: "vncconfig"=.\vncconfig\vncconfig.dsp - Package Owner=<4> Package=<5> |