2 Copyright 2004-2005 Chris Tallon
4 This file is part of VOMP.
6 VOMP is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 VOMP is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with VOMP; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26 #define _WIN32_WINNT 0x501
30 #include "vompreswin.h"
34 #include "remotewin.h"
46 void sighandler(int signalReceived);
47 void shutdown(int code);
49 // Global variables --------------------------------------------------------------------------------------------------
64 bool wnd_fullscreen=false;
65 bool wnd_topmost=false;
66 RECT wnd_fs_rect={20,20,768+20,576+20};
67 RECT wnd_fs_rect_client={0,0,768,576};
68 //OSVERSIONINFO windows_ver; //attempt to distigsh windows versions
74 typedef UINT (WINAPI *GETRAWINPUTDATAFNC) (HRAWINPUT,UINT,LPVOID,PUINT,UINT);
75 typedef UINT (WINAPI *REGISTERRAWINPUTDEVICEFNC) (PCRAWINPUTDEVICE,UINT,UINT);
77 GETRAWINPUTDATAFNC dynGetRawInputData=NULL;
78 REGISTERRAWINPUTDEVICEFNC dynRegisterRawInputDevices=NULL;
83 void MILLISLEEP(ULONG a)
90 DWORD WINAPI commandthreadStart(void *arg)
96 void LoadRemoteFunctions() {
97 user32dll=LoadLibrary("user32.dll");
98 if (user32dll!=NULL) {
99 dynGetRawInputData=(GETRAWINPUTDATAFNC)GetProcAddress(user32dll,"GetRawInputData");
100 if (dynGetRawInputData!=NULL) {
101 dynRegisterRawInputDevices=(REGISTERRAWINPUTDEVICEFNC)GetProcAddress(user32dll,"RegisterRawInputDevices");
102 if (dynRegisterRawInputDevices!=NULL) {
109 bool InitApp(HINSTANCE hinst,int cmdshow);
111 HWND win_main;//global window handle
112 HWND win;//global child window handle
115 #define ERROR_MSG(str) MessageBox(win_main,str,"Error!",MB_OK|MB_ICONWARNING)
116 INT WINAPI WinMain( HINSTANCE hinst , HINSTANCE previnst, LPSTR cmdline, int cmdshow)
119 //On Windows we have to init a window, we use DXUT
120 LoadRemoteFunctions();
121 if (!InitApp(hinst,cmdshow)) return false;
122 //Starting Network support
124 int result = WSAStartup(MAKEWORD(2,2),&wsadat);
125 if (result!=NO_ERROR) {
126 ERROR_MSG("Initialising WinSocked: Error at WSAStartup()\n");
130 result= CoInitializeEx(NULL,COINIT_MULTITHREADED );//Initialize COM for DirectShow
132 ERROR_MSG("Initialising COM: Error at Coinitialize()\n");
139 // Init global vars ------------------------------------------------------------------------------------------------
142 remote = new RemoteWin();
145 timers = new Timers();
148 video = new VideoWin();
149 audio = new AudioWin();
150 boxstack = new BoxStack();
151 command = new Command();
154 if (!logger || !remote || !mtd || !led || !osd || !video || !audio || !boxstack || !command)
156 ERROR_MSG("Could not create objects. Memory problems?\n");
162 // Get logging module started --------------------------------------------------------------------------------------
164 if (!logger->init(Log::DEBUG, "vompwin.log", true))
166 ERROR_MSG("Could not initialise log object. Aborting.\n");
172 logger->log("Core", Log::INFO, "Starting up...");
176 // Init modules ----------------------------------------------------------------------------------------------------
179 success = remote->init("/dev/rawir");
182 logger->log("Core", Log::INFO, "Remote module initialised");
186 logger->log("Core", Log::EMERG, "Remote module failed to initialise");
192 success = led->init(0);
195 logger->log("Core", Log::INFO, "LED module initialised");
199 logger->log("Core", Log::EMERG, "LED module failed to initialise");
205 success = mtd->init();
208 logger->log("Core", Log::INFO, "Mtd module initialised");
212 logger->log("Core", Log::EMERG, "Mtd module failed to initialise");
218 success = timers->init();
221 logger->log("Core", Log::INFO, "Timers module initialised");
225 logger->log("Core", Log::EMERG, "Timers module failed to initialise");
231 UCHAR videoFormat = (UCHAR)mtd->getPALorNTSC();
232 if (videoFormat == Video::PAL) logger->log("Core", Log::INFO, "Read from MTD: PAL 720x576");
233 else if (videoFormat == Video::NTSC) logger->log("Core", Log::INFO, "Read from MTD: NTSC 720x480");
234 else logger->log("Core", Log::INFO, "No help from MTD. Assuming NTSC 720x480");
236 success = video->init(videoFormat);
239 logger->log("Core", Log::INFO, "Video module initialised");
243 logger->log("Core", Log::EMERG, "Video module failed to initialise");
249 success = osd->init((void*)&win);
252 logger->log("Core", Log::INFO, "OSD module initialised");
256 logger->log("Core", Log::EMERG, "OSD module failed to initialise");
262 success = audio->init(Audio::MPEG2_PES);
265 logger->log("Core", Log::INFO, "Audio module initialised");
269 logger->log("Core", Log::EMERG, "Audio module failed to initialise");
275 success = vdr->init(3024);
278 logger->log("Core", Log::INFO, "VDR module initialised");
282 logger->log("Core", Log::EMERG, "VDR module failed to initialise");
288 success = boxstack->init();
291 logger->log("Core", Log::INFO, "BoxStack module initialised");
295 logger->log("Core", Log::EMERG, "BoxStack module failed to initialise");
301 success = command->init();
304 logger->log("Core", Log::INFO, "Command module initialised");
308 logger->log("Core", Log::EMERG, "Command module failed to initialise");
314 // Other init ------------------------------------------------------------------------------------------------------
316 logger->log("Core", Log::NOTICE, "Startup successful");
318 // Run main loop ---------------------------------------------------------------------------------------------------
320 // Ok, all major device components and other bits are loaded and ready
321 lastmousemove=timeGetTime();
323 HANDLE commandthread;
324 commandthread= CreateThread(NULL, 0, commandthreadStart, NULL,0,
327 message.message=WM_NULL;
329 while(run && WaitForSingleObject(commandthread,0)==WAIT_TIMEOUT) {
330 if (PeekMessage(&message, NULL, 0,0,PM_REMOVE)!=0) {
331 if (TranslateAccelerator(win_main,acc,&message)==NULL) {
332 TranslateMessage(&message);
333 DispatchMessage(&message);
334 switch (message.message) {
336 run=false; //TODO post exit to command Messages
341 ((OsdWin*)osd)->Render();
344 // When that returns quit ------------------------------------------------------------------------------------------
345 WaitForSingleObject(commandthread,INFINITE);
348 if (user32dll) FreeModule(user32dll);
353 bool TranslateMousePosition(POINT *pos) {
356 ScreenToClient(win,pos);
357 GetClientRect(win,&clientrect);
358 if (!PtInRect(&clientrect,*pos)) return false;//Don't pass it further
359 pos->x=((double)pos->x)/((double) (clientrect.right-clientrect.left))
360 *((double)Video::getInstance()->getScreenWidth());
361 pos->y=((double)pos->y)/((double) (clientrect.bottom-clientrect.top))
362 *((double)Video::getInstance()->getScreenHeight());
369 void CalculateWindowSize(RECT * size,ULONG size_mode) {
372 DWORD adjheight,adjwidth;
373 if (!wnd_fullscreen) {
374 DWORD flags =WS_VISIBLE | WS_POPUP | WS_CAPTION | WS_SYSMENU
375 |WS_MINIMIZEBOX | WS_SIZEBOX |WS_MAXIMIZEBOX;
376 RECT wnted={50,50,150,150};
377 AdjustWindowRect(&wnted,flags ,false);
378 adjwidth=-wnted.left+wnted.right-100;
379 adjheight=-wnted.top+wnted.bottom-100;
381 adjwidth=adjheight=0;
383 width=size->right-size->left-adjwidth;
384 height=size->bottom-size->top-adjheight;
385 UCHAR mode=video->getMode();
386 UCHAR aspect=((VideoWin*)video)->getAspectRatio();
387 UCHAR tvsize=((VideoWin*)video)->getPseudoTVsize();
388 double aspectrt=4./3.;
389 if (tvsize==Video::ASPECT16X9) {
390 if (aspect==Video::ASPECT16X9) {
391 aspectrt=4./3.; //looks strange, but it is a 16:9 tv
392 } else if (aspect==Video::ASPECT4X3) {
393 aspectrt=4./3./(16./9.)*(4./3.); //I hope this is correct
395 } else if (tvsize==Video::ASPECT4X3) {
396 if (aspect==Video::ASPECT16X9) {
397 if (mode!=Video::NORMAL) {
402 } else if (aspect==Video::ASPECT4X3) {
406 if (!wnd_fullscreen) {
409 case WMSZ_BOTTOMRIGHT:
412 width=(ULONG)(((double)height)*aspectrt);
413 size->right=size->left+width+adjwidth;
415 case WMSZ_BOTTOMLEFT:
417 width=(ULONG)(((double)height)*aspectrt);
418 size->left=size->right-width-adjwidth;
422 height=(ULONG)(((double)width)/aspectrt);
423 size->bottom=size->top+height+adjheight;
426 MoveWindow(win,0,0,width,height,TRUE);
428 RECT newrect={0,0,width,height};
430 if ((ULONG)(((double)height)*aspectrt)>width) {
431 newlength=(ULONG)(((double)width)/aspectrt);
432 newrect.top+=(height-newlength)/2;
433 newrect.bottom-=(height-newlength);
435 newlength=(ULONG)(((double)height)*aspectrt);
436 newrect.left+=(width-newlength)/2;
437 newrect.right-=(width-newlength);
439 MoveWindow(win,newrect.left,newrect.top,newrect.right,newrect.bottom,TRUE);
444 void AdjustWindow() {
445 if (!wnd_fullscreen) {
447 GetWindowRect(win_main,&winrect);
448 CalculateWindowSize(&winrect,WMSZ_BOTTOM);
449 MoveWindow(win_main,winrect.left,
450 winrect.top,winrect.right-winrect.left,winrect.bottom-winrect.top,true);
453 GetWindowRect(win_main,&winrect);
454 CalculateWindowSize(&winrect,WMSZ_BOTTOM);
459 void ToggleFullscreen() {
460 if (wnd_fullscreen) {
461 wnd_fullscreen=false;
462 SetWindowLong(win_main,GWL_STYLE,WS_VISIBLE | WS_POPUP | WS_CAPTION | WS_SYSMENU
463 |WS_MINIMIZEBOX | WS_SIZEBOX |WS_MAXIMIZEBOX);
464 SetWindowPos(win_main,NULL,0,0,0,0,SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
466 SetWindowPos(win_main,wnd_topmost?HWND_TOPMOST:HWND_TOP,wnd_fs_rect.left,wnd_fs_rect.top,
467 wnd_fs_rect.right-wnd_fs_rect.left,
468 wnd_fs_rect.bottom-wnd_fs_rect.top,
469 SWP_DRAWFRAME | SWP_FRAMECHANGED);
470 MoveWindow(win,wnd_fs_rect_client.left,wnd_fs_rect_client.top,
471 wnd_fs_rect_client.right-wnd_fs_rect_client.left,
472 wnd_fs_rect_client.bottom-wnd_fs_rect_client.top,TRUE);
475 GetWindowRect(win_main,&wnd_fs_rect);
476 GetWindowRect(win,&wnd_fs_rect_client);
477 SetWindowLong(win_main,GWL_STYLE,WS_VISIBLE | WS_POPUP );
478 SetWindowPos(win_main,NULL,0,0,0,0,SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
479 HMONITOR monitor=MonitorFromWindow(win_main,MONITOR_DEFAULTTONEAREST);
481 moninfo.cbSize=sizeof(moninfo);
483 if (!GetMonitorInfo(monitor,&moninfo)) return ;
484 SetWindowPos(win_main,wnd_topmost?HWND_TOPMOST:HWND_TOP,moninfo.rcMonitor.left,moninfo.rcMonitor.top,
485 moninfo.rcMonitor.right,moninfo.rcMonitor.bottom,SWP_FRAMECHANGED);
494 void ToggleTopmost() {
495 wnd_topmost=!wnd_topmost;
496 SetWindowPos(win_main,wnd_topmost?HWND_TOPMOST:HWND_NOTOPMOST,0,0,
497 0,0,SWP_NOMOVE | SWP_NOSIZE);
500 void CursorUpdate() {
502 GetCursorPos(&cursorpos);
504 asswind=WindowFromPoint(cursorpos);
505 if (asswind!=win_main && asswind!=win) {
506 return ; //not our responsibility
508 if ((timeGetTime()-lastmousemove)<4000 || cmenu) {
509 SetCursor(LoadCursor(NULL,IDC_ARROW));
515 bool ContextMenu(HWND wind,int x,int y) {
518 ScreenToClient(wind,&p);
519 GetClientRect(wind,&clientrect);
520 if (!PtInRect(&clientrect,p)) return false;
521 ClientToScreen(wind,&p);
524 menu=LoadMenu(hinstance,MAKEINTRESOURCE(VOMPMENU));
525 popup=GetSubMenu(menu,0);
526 if (wnd_fullscreen) {
527 CheckMenuItem(popup,VOMP_FULL_SCREEN,MF_BYCOMMAND|MF_CHECKED);
529 CheckMenuItem(popup,VOMP_FULL_SCREEN,MF_BYCOMMAND|MF_UNCHECKED);
532 CheckMenuItem(popup,VOMP_TOPMOST,MF_BYCOMMAND|MF_CHECKED);
534 CheckMenuItem(popup,VOMP_TOPMOST,MF_BYCOMMAND|MF_UNCHECKED);
537 TrackPopupMenu(popup,TPM_RIGHTBUTTON|TPM_LEFTALIGN,x,y,0,wind, NULL);
545 LONG FAR PASCAL WindowProc(HWND wind, UINT msg, WPARAM wparam, LPARAM lparam)
551 logger->log("Core", Log::NOTICE, "Window closed, shutting down...");
553 ((RemoteWin*)Remote::getInstance())->SendPower();
557 CalculateWindowSize((RECT*) lparam,wparam);
561 int width = LOWORD(lparam);
562 int height = HIWORD(lparam);
564 if (wparam == SIZE_MAXIMIZED) {
567 } else if (wparam == SIZE_MINIMIZED) {
576 if (GetUpdateRect(wind, &r, FALSE)) {
577 BeginPaint(wind, &ps);
578 //Call Painting Mechanism
583 if (((RemoteWin*)remote)->ReceiveButtonVK(wparam)) {
584 return 0L; //We process that Key
586 return DefWindowProc(wind, msg, wparam, lparam);
591 if (((RemoteWin*)remote)->ReceiveButtonCH(wparam)) {
592 return 0L; //We process that Key
594 return DefWindowProc(wind, msg, wparam, lparam);
599 if (((RemoteWin*)remote)->ReceiveButtonAP(GET_APPCOMMAND_LPARAM(lparam))){
600 return TRUE; //yes we process that message
602 return DefWindowProc(wind, msg, wparam, lparam);
611 dynGetRawInputData((HRAWINPUT)lparam,RID_INPUT,NULL,&risize,sizeof(RAWINPUTHEADER));
612 lpit=(LPRAWINPUT)malloc(risize);
613 dynGetRawInputData((HRAWINPUT)lparam,RID_INPUT,lpit,&risize,sizeof(RAWINPUTHEADER));
615 if (lpit->header.dwType==RIM_TYPEHID && lpit->data.hid.dwSizeHid>=2) {
616 DWORD button=lpit->data.hid.bRawData[1] | (lpit->data.hid.bRawData[0]<< 8);
617 if (((RemoteWin*)remote)->ReceiveButtonRI(button)){
619 return 0; //yes we process that message
624 return DefWindowProc(wind, msg, wparam, lparam);
629 if (LOWORD(wparam)==VOMP_FULL_SCREEN) {
633 if (LOWORD(wparam)==VOMP_TOPMOST) {
637 if (((RemoteWin*)remote)->ReceiveButtonAP(LOWORD(wparam))){
638 return 0; //yes we process that message
640 return DefWindowProc(wind, msg, wparam, lparam);
645 if (((HANDLE)wparam)==win) {
649 return DefWindowProc(wind, msg, wparam, lparam);
653 if (wparam==SC_MAXIMIZE) {
656 } else if (wparam==SC_SCREENSAVE || wparam==SC_MONITORPOWER) {
659 return DefWindowProc(wind,msg,wparam, lparam);
664 lastmousemove=timeGetTime();
665 SetCursor(LoadCursor(NULL,IDC_ARROW));
666 SetTimer(wind,VOMP_CURSORUPDATE,4500,NULL);
667 POINT mpos={GET_X_LPARAM(lparam),GET_Y_LPARAM(lparam)};
668 ClientToScreen(wind,&mpos);
669 if (TranslateMousePosition(&mpos)) {
670 Message *mousemes=new Message();
671 mousemes->message=Message::MOUSE_MOVE;
673 mousemes->to=BoxStack::getInstance();
674 mousemes->parameter=(mpos.x & 0xFFFF)<< 16| (mpos.y & 0xFFFF);
676 //command->postMessageFromOuterSpace(mousemes);
677 command->postMessageIfNotBusy(mousemes);
681 //return DefWindowProc(wind,msg,wparam, lparam);
685 if (wparam==VOMP_CURSORUPDATE) {
689 return DefWindowProc(wind, msg, wparam, lparam);
693 if (!ContextMenu(wind,GET_X_LPARAM(lparam),GET_Y_LPARAM(lparam))) {
694 return DefWindowProc(wind, msg, wparam, lparam);
699 case WM_LBUTTONDOWN:{
700 POINT mpos={GET_X_LPARAM(lparam),GET_Y_LPARAM(lparam)};
701 ClientToScreen(wind,&mpos);
702 if (TranslateMousePosition(&mpos)) {
703 Message *mousemes=new Message();
704 mousemes->message=Message::MOUSE_LBDOWN;
706 mousemes->to=BoxStack::getInstance();
707 mousemes->parameter=(mpos.x & 0xFFFF)<< 16| (mpos.y & 0xFFFF);
709 command->postMessageFromOuterSpace(mousemes);
713 return DefWindowProc(wind, msg, wparam, lparam);
719 bool InitApp(HINSTANCE hinst,int cmdshow) {
723 wcs.style = CS_HREDRAW | CS_VREDRAW;
724 wcs.lpfnWndProc = WindowProc;
726 wcs.cbWndExtra = sizeof(DWORD);
727 wcs.hInstance = hinst;
730 wcs.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
731 wcs.lpszMenuName = NULL;
732 wcs.lpszClassName = "vomp";
733 acc=LoadAccelerators(hinst,MAKEINTRESOURCE(VOMPACCELERATOR));
734 if (!RegisterClass(&wcs))
736 flags =WS_VISIBLE | WS_POPUP | WS_CAPTION | WS_SYSMENU
737 |WS_MINIMIZEBOX | WS_SIZEBOX |WS_MAXIMIZEBOX;
738 RECT wnted={50,50,768+50,576+50};
739 AdjustWindowRect(&wnted,flags ,false);
740 win_main=CreateWindow("vomp","VOMP on Windows",flags, CW_USEDEFAULT,CW_USEDEFAULT,
741 wnted.right-wnted.left,wnted.bottom-wnted.top,NULL,NULL,hinst,NULL);
744 ShowWindow(win_main,SW_SHOWNORMAL);
745 UpdateWindow(win_main);
746 /* in order to handle letterboxing we use a child window */
748 child_wcs.style = CS_HREDRAW | CS_VREDRAW;
749 child_wcs.lpfnWndProc = WindowProc;
750 child_wcs.cbClsExtra = 0;
751 child_wcs.cbWndExtra = sizeof(DWORD);
752 child_wcs.hInstance = hinst;
753 child_wcs.hIcon = NULL;
754 child_wcs.hCursor = NULL;
755 child_wcs.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
756 child_wcs.lpszMenuName = NULL;
757 child_wcs.lpszClassName = "vomp_playback";
758 if (!RegisterClass(&child_wcs))
761 win=CreateWindow("vomp_playback","Vomp Playback Window",WS_VISIBLE | WS_CHILD |WS_CLIPCHILDREN,
762 0,0,768,576,win_main,NULL,hinst,NULL);
765 ShowWindow(win,SW_SHOWNORMAL);
767 if (remotefnc) {//at least windows XP
768 /* We want to support MCE Remote controls*/
769 RAWINPUTDEVICE remote_control_data[4];
770 ZeroMemory(remote_control_data,sizeof(remote_control_data));
771 remote_control_data[0].usUsagePage=0xFFBC;
772 remote_control_data[0].usUsage=0x88;
773 remote_control_data[0].dwFlags=0;
774 remote_control_data[1].usUsagePage=0x0C;
775 remote_control_data[1].usUsage=0x80;
776 remote_control_data[1].dwFlags=0;
777 remote_control_data[2].usUsagePage=0x0C;
778 remote_control_data[2].usUsage=0x01;
779 remote_control_data[2].dwFlags=0;
780 remote_control_data[3].usUsagePage=0x01;
781 remote_control_data[3].usUsage=0x80;
782 remote_control_data[3].dwFlags=0;
783 if (dynRegisterRawInputDevices(remote_control_data,4,sizeof(remote_control_data[0]))!=TRUE) {
784 MessageBox(0,"Registering remote control failed!","Aborting!",0);
796 // -------------------------------------------------------------------------------------------------------------------
798 void shutdown(int code)
802 boxstack->shutdown();
804 logger->log("Core", Log::NOTICE, "BoxStack module shut down");
807 if (command) // shut down command here in case views have posted messages
811 logger->log("Core", Log::NOTICE, "Command module shut down");
818 logger->log("Core", Log::NOTICE, "VDR module shut down");
825 logger->log("Core", Log::NOTICE, "OSD module shut down");
832 logger->log("Core", Log::NOTICE, "Audio module shut down");
839 logger->log("Core", Log::NOTICE, "Video module shut down");
846 logger->log("Core", Log::NOTICE, "Timers module shut down");
853 logger->log("Core", Log::NOTICE, "MTD module shut down");
860 logger->log("Core", Log::NOTICE, "LED module shut down");
867 logger->log("Core", Log::NOTICE, "Remote module shut down");
872 logger->log("Core", Log::NOTICE, "WOL module shut down");
877 logger->log("Core", Log::NOTICE, "Log module shutting down... bye!\n\n");
885 // -------------------------------------------------------------------------------------------------------------------
887 ULLONG ntohll(ULLONG a)
892 ULLONG htonll(ULLONG a)
894 return (((ULLONG)htonl((ULONG)((a<<32)>> 32))<<32)
895 |(ULONG)htonl(((ULONG) (a >> 32))));