blob: e2d02007b87e26162dca96a98781594ff3eaccfe [file] [log] [blame]
Constantin Kaplinskyfbfbb922004-11-14 18:28:51 +00001/* Copyright (C) 2004 TightVNC Team. All Rights Reserved.
2 *
3 * This is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This software is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this software; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
16 * USA.
17 */
18
19// -=- RFB Player for Win32
20
21#include <conio.h>
22
23#include <rfb/LogWriter.h>
24#include <rfb/Exception.h>
25#include <rfb/Threading.h>
26
27#include <rfb_win32/Win32Util.h>
28#include <rfb_win32/WMShatter.h>
29
30#include <rfbplayer/rfbplayer.h>
31#include <rfbplayer/utils.h>
32#include <rfbplayer/resource.h>
33
34using namespace rfb;
35using namespace rfb::win32;
36
37// -=- Variables & consts
38
39static LogWriter vlog("RfbPlayer");
40
41TStr rfb::win32::AppName("RfbPlayer");
42extern const char* buildTime;
43
44// -=- RfbPlayer's defines
45
46#define strcasecmp _stricmp
47
Constantin Kaplinskyfbfbb922004-11-14 18:28:51 +000048//
49// -=- RfbPlayerClass
50
51//
52// Window class used as the basis for RfbPlayer instance
53//
54
55class RfbPlayerClass {
56public:
57 RfbPlayerClass();
58 ~RfbPlayerClass();
59 ATOM classAtom;
60 HINSTANCE instance;
61};
62
63LRESULT CALLBACK RfbPlayerProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
64 LRESULT result;
65
66 if (msg == WM_CREATE)
67 SetWindowLong(hwnd, GWL_USERDATA, (long)((CREATESTRUCT*)lParam)->lpCreateParams);
68 else if (msg == WM_DESTROY) {
69 RfbPlayer* _this = (RfbPlayer*) GetWindowLong(hwnd, GWL_USERDATA);
george8267cbcd02005-01-16 15:39:56 +000070 _this->fRun = false;
Constantin Kaplinskyfbfbb922004-11-14 18:28:51 +000071
72 // Resume playback (It's need to quit from FbsInputStream::waitWhilePaused())
73 _this->setPaused(false);
74 SetWindowLong(hwnd, GWL_USERDATA, 0);
75 }
76 RfbPlayer* _this = (RfbPlayer*) GetWindowLong(hwnd, GWL_USERDATA);
77 if (!_this) {
78 vlog.info("null _this in %x, message %u", hwnd, msg);
79 return DefWindowProc(hwnd, msg, wParam, lParam);
80 }
81
82 try {
83 result = _this->processMainMessage(hwnd, msg, wParam, lParam);
84 } catch (rdr::Exception& e) {
85 vlog.error("untrapped: %s", e.str());
86 }
87
88 return result;
89};
90
91RfbPlayerClass::RfbPlayerClass() : classAtom(0) {
92 WNDCLASS wndClass;
93 wndClass.style = 0;
94 wndClass.lpfnWndProc = RfbPlayerProc;
95 wndClass.cbClsExtra = 0;
96 wndClass.cbWndExtra = 0;
97 wndClass.hInstance = instance = GetModuleHandle(0);
98 wndClass.hIcon = (HICON)LoadImage(GetModuleHandle(0),
george827214b822004-12-12 07:02:51 +000099 MAKEINTRESOURCE(IDI_ICON), IMAGE_ICON, 0, 0, LR_SHARED);
Constantin Kaplinskyfbfbb922004-11-14 18:28:51 +0000100 if (!wndClass.hIcon)
101 printf("unable to load icon:%ld", GetLastError());
102 wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
103 wndClass.hbrBackground = HBRUSH(COLOR_WINDOW);
george82c2c691f2004-12-08 18:04:14 +0000104 wndClass.lpszMenuName = MAKEINTRESOURCE(IDR_MENU);
Constantin Kaplinskyfbfbb922004-11-14 18:28:51 +0000105 wndClass.lpszClassName = _T("RfbPlayerClass");
106 classAtom = RegisterClass(&wndClass);
107 if (!classAtom) {
108 throw rdr::SystemException("unable to register RfbPlayer window class",
109 GetLastError());
110 }
111}
112
113RfbPlayerClass::~RfbPlayerClass() {
114 if (classAtom) {
115 UnregisterClass((const TCHAR*)classAtom, instance);
116 }
117}
118
119RfbPlayerClass baseClass;
120
121//
122// -=- RfbFrameClass
123
124//
125// Window class used to displaying the rfb data
126//
127
128class RfbFrameClass {
129public:
130 RfbFrameClass();
131 ~RfbFrameClass();
132 ATOM classAtom;
133 HINSTANCE instance;
134};
135
136LRESULT CALLBACK FrameProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
137 LRESULT result;
138
139 if (msg == WM_CREATE)
140 SetWindowLong(hwnd, GWL_USERDATA, (long)((CREATESTRUCT*)lParam)->lpCreateParams);
141 else if (msg == WM_DESTROY)
142 SetWindowLong(hwnd, GWL_USERDATA, 0);
143 RfbPlayer* _this = (RfbPlayer*) GetWindowLong(hwnd, GWL_USERDATA);
144 if (!_this) {
145 vlog.info("null _this in %x, message %u", hwnd, msg);
146 return DefWindowProc(hwnd, msg, wParam, lParam);
147 }
148
149 try {
150 result = _this->processFrameMessage(hwnd, msg, wParam, lParam);
151 } catch (rdr::Exception& e) {
152 vlog.error("untrapped: %s", e.str());
153 }
154
155 return result;
156}
157
158RfbFrameClass::RfbFrameClass() : classAtom(0) {
159 WNDCLASS wndClass;
160 wndClass.style = 0;
161 wndClass.lpfnWndProc = FrameProc;
162 wndClass.cbClsExtra = 0;
163 wndClass.cbWndExtra = 0;
164 wndClass.hInstance = instance = GetModuleHandle(0);
165 wndClass.hIcon = 0;
166 wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
167 wndClass.hbrBackground = 0;
168 wndClass.lpszMenuName = 0;
169 wndClass.lpszClassName = _T("RfbPlayerClass1");
170 classAtom = RegisterClass(&wndClass);
171 if (!classAtom) {
172 throw rdr::SystemException("unable to register RfbPlayer window class",
173 GetLastError());
174 }
175}
176
177RfbFrameClass::~RfbFrameClass() {
178 if (classAtom) {
179 UnregisterClass((const TCHAR*)classAtom, instance);
180 }
181}
182
183RfbFrameClass frameClass;
184
185//
186// -=- RfbPlayer instance implementation
187//
188
189RfbPlayer::RfbPlayer(char *_fileName, long _initTime = 0, double _playbackSpeed = 1.0,
190 bool _autoplay = false, bool _showControls = true,
191 bool _acceptBell = false)
192: RfbProto(_fileName), initTime(_initTime), playbackSpeed(_playbackSpeed),
193 autoplay(_autoplay), showControls(_showControls), buffer(0), client_size(0, 0, 32, 32),
george8267cbcd02005-01-16 15:39:56 +0000194 window_size(0, 0, 32, 32), cutText(0), seekMode(false), fileName(_fileName), fRun(true),
Constantin Kaplinskyfbfbb922004-11-14 18:28:51 +0000195 serverInitTime(0), btnStart(0), txtPos(0), editPos(0), txtSpeed(0), editSpeed(0),
196 lastPos(0), acceptBell(_acceptBell) {
197
198 if (showControls)
199 CTRL_BAR_HEIGHT = 30;
200 else
201 CTRL_BAR_HEIGHT = 0;
202
203 // Create the main window
204 const TCHAR* name = _T("RfbPlayer");
205 mainHwnd = CreateWindow((const TCHAR*)baseClass.classAtom, name, WS_OVERLAPPEDWINDOW,
206 0, 0, 10, 10, 0, 0, baseClass.instance, this);
207 if (!mainHwnd) {
208 throw rdr::SystemException("unable to create WMNotifier window instance", GetLastError());
209 }
210 vlog.debug("created window \"%s\" (%x)", (const char*)CStr(name), getMainHandle());
211
212 // Create the backing buffer
213 buffer = new win32::DIBSectionBuffer(getFrameHandle());
214}
215
216RfbPlayer::~RfbPlayer() {
217 vlog.debug("~RfbPlayer");
218 if (mainHwnd) {
219 setVisible(false);
220 DestroyWindow(mainHwnd);
221 mainHwnd = 0;
222 }
223 delete buffer;
224 delete cutText;
225 vlog.debug("~RfbPlayer done");
226}
227
Constantin Kaplinskyfbfbb922004-11-14 18:28:51 +0000228LRESULT
229RfbPlayer::processMainMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
230 switch (msg) {
231
232 // -=- Process standard window messages
Constantin Kaplinskyfbfbb922004-11-14 18:28:51 +0000233
234 case WM_CREATE:
235 {
Constantin Kaplinskyfbfbb922004-11-14 18:28:51 +0000236 // Create the frame window
237 frameHwnd = CreateWindowEx(WS_EX_CLIENTEDGE, (const TCHAR*)frameClass.classAtom,
238 0, WS_CHILD | WS_VISIBLE, 0, CTRL_BAR_HEIGHT, 10, CTRL_BAR_HEIGHT + 10,
239 hwnd, 0, frameClass.instance, this);
240
241 return 0;
242 }
243
george827214b822004-12-12 07:02:51 +0000244 // Process the main menu and toolbar's messages
Constantin Kaplinskyfbfbb922004-11-14 18:28:51 +0000245
246 case WM_COMMAND:
247 {
Constantin Kaplinskyfbfbb922004-11-14 18:28:51 +0000248
Constantin Kaplinskyfbfbb922004-11-14 18:28:51 +0000249 }
250 break;
251
252 // Update frame's window size and add scrollbars if required
253
254 case WM_SIZE:
255 {
256 Point old_offset = bufferToClient(Point(0, 0));
257
258 // Update the cached sizing information
259 RECT r;
260 GetClientRect(getMainHandle(), &r);
261 MoveWindow(getFrameHandle(), 0, CTRL_BAR_HEIGHT, r.right - r.left,
262 r.bottom - r.top - CTRL_BAR_HEIGHT, TRUE);
263
264 GetWindowRect(getFrameHandle(), &r);
265 window_size = Rect(r.left, r.top, r.right, r.bottom);
266 GetClientRect(getFrameHandle(), &r);
267 client_size = Rect(r.left, r.top, r.right, r.bottom);
268
269 // Determine whether scrollbars are required
270 calculateScrollBars();
271
272 // Redraw if required
273 if (!old_offset.equals(bufferToClient(Point(0, 0))))
274 InvalidateRect(getFrameHandle(), 0, TRUE);
275 }
276 break;
277
278 case WM_CLOSE:
279 vlog.debug("WM_CLOSE %x", getMainHandle());
280 PostQuitMessage(0);
281 break;
282 }
283
284 return rfb::win32::SafeDefWindowProc(getMainHandle(), msg, wParam, lParam);
285}
286
287LRESULT RfbPlayer::processFrameMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
288 switch (msg) {
289
290 case WM_PAINT:
291 {
292 if (is->isSeeking()) {
293 seekMode = true;
294 return 0;
295 } else {
296 if (seekMode) {
297 seekMode = false;
298 InvalidateRect(getFrameHandle(), 0, true);
299 UpdateWindow(getFrameHandle());
300 return 0;
301 }
302 }
303
304 PAINTSTRUCT ps;
305 HDC paintDC = BeginPaint(getFrameHandle(), &ps);
306 if (!paintDC)
307 throw SystemException("unable to BeginPaint", GetLastError());
308 Rect pr = Rect(ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right, ps.rcPaint.bottom);
309
310 if (!pr.is_empty()) {
311
312 if (buffer->bitmap) {
313
314 // Get device context
315 BitmapDC bitmapDC(paintDC, buffer->bitmap);
316
317 // Blit the border if required
318 Rect bufpos = bufferToClient(buffer->getRect());
319 if (!pr.enclosed_by(bufpos)) {
320 vlog.debug("draw border");
321 HBRUSH black = (HBRUSH) GetStockObject(BLACK_BRUSH);
322 RECT r;
323 SetRect(&r, 0, 0, bufpos.tl.x, client_size.height()); FillRect(paintDC, &r, black);
324 SetRect(&r, bufpos.tl.x, 0, bufpos.br.x, bufpos.tl.y); FillRect(paintDC, &r, black);
325 SetRect(&r, bufpos.br.x, 0, client_size.width(), client_size.height()); FillRect(paintDC, &r, black);
326 SetRect(&r, bufpos.tl.x, bufpos.br.y, bufpos.br.x, client_size.height()); FillRect(paintDC, &r, black);
327 }
328
329 // Do the blit
330 Point buf_pos = clientToBuffer(pr.tl);
331 if (!BitBlt(paintDC, pr.tl.x, pr.tl.y, pr.width(), pr.height(),
332 bitmapDC, buf_pos.x, buf_pos.y, SRCCOPY))
333 throw SystemException("unable to BitBlt to window", GetLastError());
334
335 } else {
336 // Blit a load of black
337 if (!BitBlt(paintDC, pr.tl.x, pr.tl.y, pr.width(), pr.height(),
338 0, 0, 0, BLACKNESS))
339 throw SystemException("unable to BitBlt to blank window", GetLastError());
340 }
341 }
342 EndPaint(getFrameHandle(), &ps);
343 }
344 return 0;
345
346 case WM_VSCROLL:
347 case WM_HSCROLL:
348 {
349 Point delta;
350 int newpos = (msg == WM_VSCROLL) ? scrolloffset.y : scrolloffset.x;
351
352 switch (LOWORD(wParam)) {
353 case SB_PAGEUP: newpos -= 50; break;
354 case SB_PAGEDOWN: newpos += 50; break;
355 case SB_LINEUP: newpos -= 5; break;
356 case SB_LINEDOWN: newpos += 5; break;
357 case SB_THUMBTRACK:
358 case SB_THUMBPOSITION: newpos = HIWORD(wParam); break;
359 default: vlog.info("received unknown scroll message");
360 };
361
362 if (msg == WM_HSCROLL)
363 setViewportOffset(Point(newpos, scrolloffset.y));
364 else
365 setViewportOffset(Point(scrolloffset.x, newpos));
366
367 SCROLLINFO si;
368 si.cbSize = sizeof(si);
369 si.fMask = SIF_POS;
370 si.nPos = newpos;
371 SetScrollInfo(getFrameHandle(), (msg == WM_VSCROLL) ? SB_VERT : SB_HORZ, &si, TRUE);
372 }
373 break;
374 }
375
376 return DefWindowProc(hwnd, msg, wParam, lParam);
377}
378
george8267cbcd02005-01-16 15:39:56 +0000379void RfbPlayer::run() {
380 long initTime = -1;
381
382 // Process the rfb messages
383 while (fRun) {
384 try {
385 if (initTime >= 0) {
386 setPos(initTime);
387 initTime = -1;
388 }
389 if (!isSeeking())
390 updatePos();
391 processMsg();
392 } catch (rdr::Exception e) {
393 if (strcmp(e.str(), "[End Of File]") == 0) {
394 rewind();
395 setPaused(true);
396 continue;
397 }
398 // It's a special exception to perform backward seeking.
399 // We only rewind the stream and seek the offset
400 if (strcmp(e.str(), "[REWIND]") == 0) {
401 initTime = getSeekOffset();
402 double speed = getSpeed();
403 bool play = !isPaused();
404 rewind();
405 setSpeed(speed);
406 setPaused(!play);
407 } else {
408 MessageBox(getMainHandle(), e.str(), e.type(), MB_OK | MB_ICONERROR);
409 return;
410 }
411 }
412 }
413}
414
Constantin Kaplinskyfbfbb922004-11-14 18:28:51 +0000415void RfbPlayer::setOptions(long _initTime = 0, double _playbackSpeed = 1.0,
416 bool _autoplay = false, bool _showControls = true) {
417 showControls = _showControls;
418 autoplay = _autoplay;
419 playbackSpeed = _playbackSpeed;
420 initTime = _initTime;
421}
422
423void RfbPlayer::applyOptions() {
424 if (initTime >= 0)
425 setPos(initTime);
426 setSpeed(playbackSpeed);
427 setPaused(!autoplay);
428
429 // Update the position
430 SetWindowText(editPos, LongToStr(initTime / 1000));
431}
432
433void RfbPlayer::setVisible(bool visible) {
434 ShowWindow(getMainHandle(), visible ? SW_SHOW : SW_HIDE);
435 if (visible) {
436 // When the window becomes visible, make it active
437 SetForegroundWindow(getMainHandle());
438 SetActiveWindow(getMainHandle());
439 }
440}
441
442void RfbPlayer::setTitle(const char *title) {
443 char _title[256];
444 strcpy(_title, AppName);
445 strcat(_title, " - ");
446 strcat(_title, title);
447 SetWindowText(getMainHandle(), _title);
448}
449
450void RfbPlayer::setFrameSize(int width, int height) {
451 // Calculate and set required size for main window
452 RECT r = {0, 0, width, height};
453 AdjustWindowRectEx(&r, GetWindowLong(getFrameHandle(), GWL_STYLE), FALSE,
454 GetWindowLong(getFrameHandle(), GWL_EXSTYLE));
455 r.bottom += CTRL_BAR_HEIGHT; // Include RfbPlayr's controls area
456 AdjustWindowRect(&r, GetWindowLong(getMainHandle(), GWL_STYLE), FALSE);
457 SetWindowPos(getMainHandle(), 0, 0, 0, r.right-r.left, r.bottom-r.top,
458 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOOWNERZORDER);
459
460 // Enable/disable scrollbars as appropriate
461 calculateScrollBars();
462}
463
464void RfbPlayer::calculateScrollBars() {
465 // Calculate the required size of window
466 DWORD current_style = GetWindowLong(getFrameHandle(), GWL_STYLE);
467 DWORD style = current_style & ~(WS_VSCROLL | WS_HSCROLL);
468 DWORD old_style;
469 RECT r;
470 SetRect(&r, 0, 0, buffer->width(), buffer->height());
471 AdjustWindowRectEx(&r, style, FALSE, GetWindowLong(getFrameHandle(), GWL_EXSTYLE));
472 Rect reqd_size = Rect(r.left, r.top, r.right, r.bottom);
473
474 // Work out whether scroll bars are required
475 do {
476 old_style = style;
477
478 if (!(style & WS_HSCROLL) && (reqd_size.width() > window_size.width())) {
479 style |= WS_HSCROLL;
480 reqd_size.br.y += GetSystemMetrics(SM_CXHSCROLL);
481 }
482 if (!(style & WS_VSCROLL) && (reqd_size.height() > window_size.height())) {
483 style |= WS_VSCROLL;
484 reqd_size.br.x += GetSystemMetrics(SM_CXVSCROLL);
485 }
486 } while (style != old_style);
487
488 // Tell Windows to update the window style & cached settings
489 if (style != current_style) {
490 SetWindowLong(getFrameHandle(), GWL_STYLE, style);
491 SetWindowPos(getFrameHandle(), NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
492 }
493
494 // Update the scroll settings
495 SCROLLINFO si;
496 if (style & WS_VSCROLL) {
497 si.cbSize = sizeof(si);
498 si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
499 si.nMin = 0;
500 si.nMax = buffer->height();
501 si.nPage = buffer->height() - (reqd_size.height() - window_size.height());
502 maxscrolloffset.y = max(0, si.nMax-si.nPage);
503 scrolloffset.y = min(maxscrolloffset.y, scrolloffset.y);
504 si.nPos = scrolloffset.y;
505 SetScrollInfo(getFrameHandle(), SB_VERT, &si, TRUE);
506 }
507 if (style & WS_HSCROLL) {
508 si.cbSize = sizeof(si);
509 si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
510 si.nMin = 0;
511 si.nMax = buffer->width();
512 si.nPage = buffer->width() - (reqd_size.width() - window_size.width());
513 maxscrolloffset.x = max(0, si.nMax-si.nPage);
514 scrolloffset.x = min(maxscrolloffset.x, scrolloffset.x);
515 si.nPos = scrolloffset.x;
516 SetScrollInfo(getFrameHandle(), SB_HORZ, &si, TRUE);
517 }
518}
519
520bool RfbPlayer::setViewportOffset(const Point& tl) {
521/* ***
522 Point np = Point(max(0, min(maxscrolloffset.x, tl.x)),
523 max(0, min(maxscrolloffset.y, tl.y)));
524 */
525 Point np = Point(max(0, min(tl.x, buffer->width()-client_size.width())),
526 max(0, min(tl.y, buffer->height()-client_size.height())));
527 Point delta = np.translate(scrolloffset.negate());
528 if (!np.equals(scrolloffset)) {
529 scrolloffset = np;
530 ScrollWindowEx(getFrameHandle(), -delta.x, -delta.y, 0, 0, 0, 0, SW_INVALIDATE);
531 UpdateWindow(getFrameHandle());
532 return true;
533 }
534 return false;
535}
536
537void RfbPlayer::close(const char* reason) {
538 setVisible(false);
539 if (reason) {
540 vlog.info("closing - %s", reason);
541 MessageBox(NULL, TStr(reason), "RfbPlayer", MB_ICONINFORMATION | MB_OK);
542 }
543 SendMessage(getFrameHandle(), WM_CLOSE, 0, 0);
544}
545
546void RfbPlayer::blankBuffer() {
547 fillRect(buffer->getRect(), 0);
548}
549
550void RfbPlayer::rewind() {
551 blankBuffer();
552 newSession(fileName);
553 skipHandshaking();
554}
555
556void RfbPlayer::serverInit() {
557 RfbProto::serverInit();
558
559 // Save the server init time for using in setPos()
560 serverInitTime = getTimeOffset() / getSpeed();
561
562 // Resize the backing buffer
563 buffer->setSize(cp.width, cp.height);
564
565 // Check on the true colour mode
566 if (!(cp.pf()).trueColour)
Peter Ã…strandc81a6522004-12-30 11:32:08 +0000567 throw rdr::Exception("This version plays only true color session!");
Constantin Kaplinskyfbfbb922004-11-14 18:28:51 +0000568
569 // Set the session pixel format
570 buffer->setPF(cp.pf());
571
572 // If the window is not maximised then resize it
573 if (!(GetWindowLong(getMainHandle(), GWL_STYLE) & WS_MAXIMIZE))
574 setFrameSize(cp.width, cp.height);
575
576 // Set the window title and show it
577 setTitle(cp.name());
578 setVisible(true);
579
580 // Set the player's param
581 applyOptions();
582}
583
584void RfbPlayer::setColourMapEntries(int first, int count, U16* rgbs) {
585 vlog.debug("setColourMapEntries: first=%d, count=%d", first, count);
586 throw rdr::Exception("Can't handle SetColourMapEntries message", "RfbPlayer");
587/* int i;
588 for (i=0;i<count;i++) {
589 buffer->setColour(i+first, rgbs[i*3], rgbs[i*3+1], rgbs[i*3+2]);
590 }
591 // *** change to 0, 256?
592 refreshWindowPalette(first, count);
593 palette_changed = true;
594 InvalidateRect(getFrameHandle(), 0, FALSE);*/
595}
596
597void RfbPlayer::bell() {
598 if (acceptBell)
599 MessageBeep(-1);
600}
601
602void RfbPlayer::serverCutText(const char* str, int len) {
603 if (cutText != NULL)
604 delete [] cutText;
605 cutText = new char[len + 1];
606 memcpy(cutText, str, len);
607 cutText[len] = '\0';
608}
609
610void RfbPlayer::frameBufferUpdateEnd() {
611};
612
613void RfbPlayer::beginRect(const Rect& r, unsigned int encoding) {
614}
615
616void RfbPlayer::endRect(const Rect& r, unsigned int encoding) {
617}
618
619
620void RfbPlayer::fillRect(const Rect& r, Pixel pix) {
621 buffer->fillRect(r, pix);
622 invalidateBufferRect(r);
623}
624
625void RfbPlayer::imageRect(const Rect& r, void* pixels) {
626 buffer->imageRect(r, pixels);
627 invalidateBufferRect(r);
628}
629
630void RfbPlayer::copyRect(const Rect& r, int srcX, int srcY) {
631 buffer->copyRect(r, Point(r.tl.x-srcX, r.tl.y-srcY));
632 invalidateBufferRect(r);
633}
634
635bool RfbPlayer::invalidateBufferRect(const Rect& crect) {
636 Rect rect = bufferToClient(crect);
637 if (rect.intersect(client_size).is_empty()) return false;
638 RECT invalid = {rect.tl.x, rect.tl.y, rect.br.x, rect.br.y};
639 InvalidateRect(getFrameHandle(), &invalid, FALSE);
640 return true;
641}
642
643void RfbPlayer::setPaused(bool paused) {
644 if (paused) {
645 if (btnStart) SetWindowText(btnStart, "Start");
646 is->pausePlayback();
647 } else {
648 if (btnStart) SetWindowText(btnStart, "Stop");
649 is->resumePlayback();
650 }
651}
652
653void RfbPlayer::setSpeed(double speed) {
654 serverInitTime = serverInitTime * getSpeed() / speed;
655 is->setSpeed(speed);
656 if (editSpeed)
657 SetWindowText(editSpeed, DoubleToStr(speed, 1));
658}
659
660double RfbPlayer::getSpeed() {
661 return is->getSpeed();
662}
663
664void RfbPlayer::setPos(long pos) {
665 is->setTimeOffset(max(pos, serverInitTime));
666}
667
668long RfbPlayer::getSeekOffset() {
669 return is->getSeekOffset();
670}
671
672bool RfbPlayer::isSeeking() {
673 return is->isSeeking();
674}
675
676bool RfbPlayer::isSeekMode() {
677 return seekMode;
678}
679
680bool RfbPlayer::isPaused() {
681 return is->isPaused();
682}
683
684long RfbPlayer::getTimeOffset() {
685 return is->getTimeOffset();
686}
687
688void RfbPlayer::updatePos() {
689 long newPos = is->getTimeOffset() / 1000;
690 if (editPos && lastPos != newPos) {
691 lastPos = newPos;
692 SetWindowText(editPos, LongToStr(lastPos));
693 }
694}
695
696void RfbPlayer::skipHandshaking() {
697 int skipBytes = 12 + 4 + 24 + strlen(cp.name());
698 is->skip(skipBytes);
699 state_ = RFBSTATE_NORMAL;
700}
701
702void programInfo() {
703 win32::FileVersionInfo inf;
704 _tprintf(_T("%s - %s, Version %s\n"),
705 inf.getVerString(_T("ProductName")),
706 inf.getVerString(_T("FileDescription")),
707 inf.getVerString(_T("FileVersion")));
708 printf("%s\n", buildTime);
709 _tprintf(_T("%s\n\n"), inf.getVerString(_T("LegalCopyright")));
710}
711
712void programUsage() {
713 printf("usage: rfbplayer <options> <filename>\n");
714 printf("Command-line options:\n");
715 printf(" -help - Provide usage information.\n");
716 printf(" -speed <value> - Sets playback speed, where 1 is normal speed,\n");
717 printf(" 2 is double speed, 0.5 is half speed. Default: 1.0.\n");
718 printf(" -pos <ms> - Sets initial time position in the session file,\n");
719 printf(" in milliseconds. Default: 0.\n");
720 printf(" -autoplay <yes|no> - Runs the player in the playback mode. Default: \"no\".\n");
721 printf(" -controls <yes|no> - Shows the control panel at the top. Default: \"yes\".\n");
722 printf(" -bell <yes|no> - Accepts the bell. Default: \"no\".\n");
723}
724
725double playbackSpeed = 1.0;
726long initTime = -1;
727bool autoplay = false;
728bool showControls = true;
729char *fileName;
730bool console = false;
731bool wrong_param = false;
732bool print_usage = false;
733bool acceptBell = false;
734
735bool processParams(int argc, char* argv[]) {
736 for (int i = 1; i < argc; i++) {
737 if ((strcasecmp(argv[i], "-help") == 0) ||
738 (strcasecmp(argv[i], "--help") == 0) ||
739 (strcasecmp(argv[i], "/help") == 0) ||
740 (strcasecmp(argv[i], "-h") == 0) ||
741 (strcasecmp(argv[i], "/h") == 0) ||
742 (strcasecmp(argv[i], "/?") == 0)) {
743 print_usage = true;
744 return true;
745 }
746
747 if ((strcasecmp(argv[i], "-speed") == 0) ||
748 (strcasecmp(argv[i], "/speed") == 0) && (i < argc-1)) {
749 playbackSpeed = atof(argv[++i]);
750 if (playbackSpeed <= 0) {
751 return false;
752 }
753 continue;
754 }
755
756 if ((strcasecmp(argv[i], "-pos") == 0) ||
757 (strcasecmp(argv[i], "/pos") == 0) && (i < argc-1)) {
758 initTime = atol(argv[++i]);
759 if (initTime <= 0)
760 return false;
761 continue;
762 }
763
764 if ((strcasecmp(argv[i], "-autoplay") == 0) ||
765 (strcasecmp(argv[i], "/autoplay") == 0) && (i < argc-1)) {
766 i++;
767 if (strcasecmp(argv[i], "yes") == 0) {
768 autoplay = true;
769 continue;
770 }
771 if (strcasecmp(argv[i], "no") == 0) {
772 autoplay = false;
773 continue;
774 }
775 return false;
776 }
777
778 if ((strcasecmp(argv[i], "-controls") == 0) ||
779 (strcasecmp(argv[i], "/controls") == 0) && (i < argc-1)) {
780 i++;
781 if (strcasecmp(argv[i], "yes") == 0) {
782 showControls = true;
783 continue;
784 }
785 if (strcasecmp(argv[i], "no") == 0) {
786 showControls = false;
787 continue;
788 }
789 return false;
790 }
791
792 if ((strcasecmp(argv[i], "-bell") == 0) ||
793 (strcasecmp(argv[i], "/bell") == 0) && (i < argc-1)) {
794 i++;
795 if (strcasecmp(argv[i], "yes") == 0) {
796 acceptBell = true;
797 continue;
798 }
799 if (strcasecmp(argv[i], "no") == 0) {
800 acceptBell = false;
801 continue;
802 }
803 return false;
804 }
805
806 if (i != argc - 1)
807 return false;
808 }
809
810 fileName = strDup(argv[argc-1]);
811 return true;
812}
813
814//
815// -=- WinMain
816//
817
818int WINAPI WinMain(HINSTANCE inst, HINSTANCE prevInst, char* cmdLine, int cmdShow) {
819
820 // - Process the command-line
821
822 int argc = __argc;
823 char** argv = __argv;
824 if (argc > 1) {
825 wrong_param = !processParams(argc, argv);
826 console = print_usage | wrong_param;
827 } else {
828 console = true;
829 }
830
831 if (console) {
832 AllocConsole();
833 freopen("CONOUT$","wb",stdout);
834
835 programInfo();
836 if (wrong_param)
837 printf("Wrong a command line.\n");
838 else
839 programUsage();
840
841 printf("\nPress Enter/Return key to continue\n");
842 char c = getch();
843 FreeConsole();
844
845 return 0;
george8267cbcd02005-01-16 15:39:56 +0000846 }
Constantin Kaplinskyfbfbb922004-11-14 18:28:51 +0000847
848 // Create the player and the thread which reading the rfb data
849 RfbPlayer *player = NULL;
Constantin Kaplinskyfbfbb922004-11-14 18:28:51 +0000850 try {
851 player = new RfbPlayer(fileName, initTime, playbackSpeed, autoplay,
852 showControls, acceptBell);
george8267cbcd02005-01-16 15:39:56 +0000853 player->start();
Constantin Kaplinskyfbfbb922004-11-14 18:28:51 +0000854 } catch (rdr::Exception e) {
855 MessageBox(NULL, e.str(), e.type(), MB_OK | MB_ICONERROR);
856 delete player;
857 return 0;
858 }
859
860 // Run the player
george825bbd61b2004-12-09 17:47:37 +0000861 HACCEL hAccel = LoadAccelerators(inst, MAKEINTRESOURCE(IDR_ACCELERATOR));
Constantin Kaplinskyfbfbb922004-11-14 18:28:51 +0000862 MSG msg;
863 while (GetMessage(&msg, NULL, 0, 0) > 0) {
george825bbd61b2004-12-09 17:47:37 +0000864 if(!TranslateAccelerator(player->getMainHandle(), hAccel, &msg)) {
865 TranslateMessage(&msg);
866 DispatchMessage(&msg);
867 }
Constantin Kaplinskyfbfbb922004-11-14 18:28:51 +0000868 }
869
870 // Wait while the thread destroying and then destroy the player
871 try{
george8267cbcd02005-01-16 15:39:56 +0000872 while (player->getState() == ThreadStarted) {}
Constantin Kaplinskyfbfbb922004-11-14 18:28:51 +0000873 if (player) delete player;
874 } catch (rdr::Exception e) {
875 MessageBox(NULL, e.str(), e.type(), MB_OK | MB_ICONERROR);
876 }
877
878 return 0;
879};