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, see <https://www.gnu.org/licenses/>.
25 #define _WIN32_WINNT 0x501
29 #include "vompreswin.h"
33 #include "remotewin.h"
39 #include "windowsosd.h"
41 #include "osdwinpixel.h"
43 #include "osdwinvector.h"
48 #include "vsleeptimer.h"
49 #include "messagequeue.h"
51 void sighandler(int signalReceived);
52 void shutdown(int code);
54 // Global variables --------------------------------------------------------------------------------------------------
67 Sleeptimer* sleeptimer;
70 bool wnd_fullscreen=false;
71 bool wnd_topmost=false;
72 RECT wnd_fs_rect={20,20,768+20,576+20};
73 RECT wnd_fs_rect_client={0,0,768,576};
74 //OSVERSIONINFO windows_ver; //attempt to distigsh windows versions
80 typedef UINT (WINAPI *GETRAWINPUTDATAFNC) (HRAWINPUT,UINT,LPVOID,PUINT,UINT);
81 typedef UINT (WINAPI *REGISTERRAWINPUTDEVICEFNC) (PCRAWINPUTDEVICE,UINT,UINT);
83 GETRAWINPUTDATAFNC dynGetRawInputData=NULL;
84 REGISTERRAWINPUTDEVICEFNC dynRegisterRawInputDevices=NULL;
88 MessageQueue* messageQueue;
91 void MILLISLEEP(ULONG a)
98 int getClockRealTime(struct timespec *tp){
102 GetSystemTime(&systime);
103 SystemTimeToFileTime(&systime, (FILETIME*)&filetime);
104 tp->tv_sec = (filetime - WINDOWS_TIME_BASE_OFFSET) / (10 * 1000 * 1000);
105 tp->tv_nsec = ((filetime - WINDOWS_TIME_BASE_OFFSET) % (10 * 1000 * 1000)) * 100;
109 DWORD WINAPI commandthreadStart(void *arg)
115 void LoadRemoteFunctions() {
116 user32dll=LoadLibrary("user32.dll");
117 if (user32dll!=NULL) {
118 dynGetRawInputData=(GETRAWINPUTDATAFNC)GetProcAddress(user32dll,"GetRawInputData");
119 if (dynGetRawInputData!=NULL) {
120 dynRegisterRawInputDevices=(REGISTERRAWINPUTDEVICEFNC)GetProcAddress(user32dll,"RegisterRawInputDevices");
121 if (dynRegisterRawInputDevices!=NULL) {
128 bool InitApp(HINSTANCE hinst,int cmdshow);
130 HWND win_main;//global window handle
131 HWND win;//global child window handle
134 #define ERROR_MSG(str) MessageBox(win_main,str,"Error!",MB_OK|MB_ICONWARNING)
135 INT WINAPI WinMain( HINSTANCE hinst , HINSTANCE previnst, LPSTR cmdline, int cmdshow)
138 //On Windows we have to init a window, we use DXUT
139 LoadRemoteFunctions();
140 if (!InitApp(hinst,cmdshow)) return false;
141 //Starting Network support
143 int result = WSAStartup(MAKEWORD(2,2),&wsadat);
144 if (result!=NO_ERROR) {
145 ERROR_MSG("Initialising WinSocked: Error at WSAStartup()\n");
149 result= CoInitializeEx(NULL,COINIT_MULTITHREADED );//Initialize COM for DirectShow
151 ERROR_MSG("Initialising COM: Error at Coinitialize()\n");
158 // Init global vars ------------------------------------------------------------------------------------------------
161 remote = new RemoteWin();
163 timers = new Timers();
164 osd = new Osd_TYPE();
166 video = new VideoWin();
167 audio = new AudioWin();
168 boxstack = new BoxStack();
169 command = new Command();
171 sleeptimer = new Sleeptimer();
173 if (!logger || !remote || !led || !osd || !video || !audio || !boxstack || !command || !sleeptimer)
175 ERROR_MSG("Could not create objects. Memory problems?\n");
181 messageQueue = command;
183 // Get logging module started --------------------------------------------------------------------------------------
185 if (!logger->init(Log::DEBUG, "vompwin.log", true))
187 ERROR_MSG("Could not initialise log object. Aborting.\n");
193 logger->log("Core", Log::INFO, "Starting up...");
197 // Init modules ----------------------------------------------------------------------------------------------------
200 success = remote->init("/dev/rawir");
203 logger->log("Core", Log::INFO, "Remote module initialised");
207 logger->log("Core", Log::EMERG, "Remote module failed to initialise");
213 success = led->init(0);
216 logger->log("Core", Log::INFO, "LED module initialised");
220 logger->log("Core", Log::EMERG, "LED module failed to initialise");
226 success = timers->init();
229 logger->log("Core", Log::INFO, "Timers module initialised");
233 logger->log("Core", Log::EMERG, "Timers module failed to initialise");
239 UCHAR videoFormat = Video::PAL; // PALNTSC FIXME.
241 success = video->init(videoFormat);
244 logger->log("Core", Log::INFO, "Video module initialised");
248 logger->log("Core", Log::EMERG, "Video module failed to initialise");
254 dynamic_cast<WindowsOsd*>(osd)->setWindow(win);
255 success = osd->init();
258 logger->log("Core", Log::INFO, "OSD module initialised");
262 logger->log("Core", Log::EMERG, "OSD module failed to initialise");
267 video->setDefaultAspect();
269 success = audio->init(Audio::MPEG2_PES);
272 logger->log("Core", Log::INFO, "Audio module initialised");
276 logger->log("Core", Log::EMERG, "Audio module failed to initialise");
282 success = vdr->init();
285 logger->log("Core", Log::INFO, "VDR module initialised");
289 logger->log("Core", Log::EMERG, "VDR module failed to initialise");
295 success = boxstack->init();
298 logger->log("Core", Log::INFO, "BoxStack module initialised");
302 logger->log("Core", Log::EMERG, "BoxStack module failed to initialise");
308 success = command->init();
311 logger->log("Core", Log::INFO, "Command module initialised");
315 logger->log("Core", Log::EMERG, "Command module failed to initialise");
321 // Other init ------------------------------------------------------------------------------------------------------
323 logger->log("Core", Log::NOTICE, "Startup successful");
325 // Run main loop ---------------------------------------------------------------------------------------------------
327 // Ok, all major device components and other bits are loaded and ready
328 lastmousemove=timeGetTime();
330 HANDLE commandthread;
331 commandthread= CreateThread(NULL, 0, commandthreadStart, NULL,0,
334 message.message=WM_NULL;
336 while(run && WaitForSingleObject(commandthread,0)==WAIT_TIMEOUT) {
337 if (PeekMessage(&message, NULL, 0,0,PM_REMOVE)!=0) {
338 if (TranslateAccelerator(win_main,acc,&message)==NULL) {
339 TranslateMessage(&message);
340 DispatchMessage(&message);
341 switch (message.message) {
343 run=false; //TODO post exit to command Messages
347 //Render, moved to OSD !
349 //((OsdWin*)osd)->Render();
352 // When that returns quit ------------------------------------------------------------------------------------------
353 WaitForSingleObject(commandthread,INFINITE);
356 if (user32dll) FreeModule(user32dll);
361 bool TranslateMousePosition(POINT *pos) {
364 ScreenToClient(win,pos);
365 GetClientRect(win,&clientrect);
366 if (!PtInRect(&clientrect,*pos)) return false;//Don't pass it further
367 pos->x=((double)pos->x)/((double) (clientrect.right-clientrect.left))
368 *((double)Video::getInstance()->getScreenWidth());
369 pos->y=((double)pos->y)/((double) (clientrect.bottom-clientrect.top))
370 *((double)Video::getInstance()->getScreenHeight());
377 void CalculateWindowSize(RECT * size,ULONG size_mode) {
380 DWORD adjheight,adjwidth;
381 if (!wnd_fullscreen) {
382 DWORD flags =WS_VISIBLE | WS_POPUP | WS_CAPTION | WS_SYSMENU
383 |WS_MINIMIZEBOX | WS_SIZEBOX |WS_MAXIMIZEBOX;
384 RECT wnted={50,50,150,150};
385 AdjustWindowRect(&wnted,flags ,false);
386 adjwidth=-wnted.left+wnted.right-100;
387 adjheight=-wnted.top+wnted.bottom-100;
389 adjwidth=adjheight=0;
391 width=size->right-size->left-adjwidth;
392 height=size->bottom-size->top-adjheight;
393 UCHAR mode=Video::PAL;
394 UCHAR aspect=Video::ASPECT4X3;
395 UCHAR tvsize=Video::ASPECT4X3;
397 mode=video->getMode();
398 aspect=((VideoWin*)video)->getAspectRatio();
399 tvsize=((VideoWin*)video)->getPseudoTVsize();
402 double aspectrt=4./3.;
403 if (tvsize==Video::ASPECT16X9) {
404 if (aspect==Video::ASPECT16X9) {
405 aspectrt=4./3.; //looks strange, but it is a 16:9 tv
406 } else if (aspect==Video::ASPECT4X3) {
407 aspectrt=4./3./(16./9.)*(4./3.); //I hope this is correct
409 } else if (tvsize==Video::ASPECT4X3) {
410 if (aspect==Video::ASPECT16X9) {
411 if (mode!=Video::NORMAL) {
416 } else if (aspect==Video::ASPECT4X3) {
420 if (!wnd_fullscreen) {
423 case WMSZ_BOTTOMRIGHT:
426 width=(ULONG)(((double)height)*aspectrt);
427 size->right=size->left+width+adjwidth;
429 case WMSZ_BOTTOMLEFT:
431 width=(ULONG)(((double)height)*aspectrt);
432 size->left=size->right-width-adjwidth;
436 height=(ULONG)(((double)width)/aspectrt);
437 size->bottom=size->top+height+adjheight;
440 MoveWindow(win,0,0,width,height,TRUE);
442 RECT newrect={0,0,width,height};
444 if ((ULONG)(((double)height)*aspectrt)>width) {
445 newlength=(ULONG)(((double)width)/aspectrt);
446 newrect.top+=(height-newlength)/2;
447 newrect.bottom-=(height-newlength);
449 newlength=(ULONG)(((double)height)*aspectrt);
450 newrect.left+=(width-newlength)/2;
451 newrect.right-=(width-newlength);
453 MoveWindow(win,newrect.left,newrect.top,newrect.right,newrect.bottom,TRUE);
458 void AdjustWindow() {
459 if (!wnd_fullscreen) {
461 GetWindowRect(win_main,&winrect);
462 CalculateWindowSize(&winrect,WMSZ_BOTTOM);
463 MoveWindow(win_main,winrect.left,
464 winrect.top,winrect.right-winrect.left,winrect.bottom-winrect.top,true);
467 GetWindowRect(win_main,&winrect);
468 CalculateWindowSize(&winrect,WMSZ_BOTTOM);
473 void ToggleFullscreen() {
474 if (wnd_fullscreen) {
475 wnd_fullscreen=false;
476 SetWindowLong(win_main,GWL_STYLE,WS_VISIBLE | WS_POPUP | WS_CAPTION | WS_SYSMENU
477 |WS_MINIMIZEBOX | WS_SIZEBOX |WS_MAXIMIZEBOX);
478 SetWindowPos(win_main,NULL,0,0,0,0,SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
480 SetWindowPos(win_main,wnd_topmost?HWND_TOPMOST:HWND_TOP,wnd_fs_rect.left,wnd_fs_rect.top,
481 wnd_fs_rect.right-wnd_fs_rect.left,
482 wnd_fs_rect.bottom-wnd_fs_rect.top,
483 SWP_DRAWFRAME | SWP_FRAMECHANGED);
484 MoveWindow(win,wnd_fs_rect_client.left,wnd_fs_rect_client.top,
485 wnd_fs_rect_client.right-wnd_fs_rect_client.left,
486 wnd_fs_rect_client.bottom-wnd_fs_rect_client.top,TRUE);
489 GetWindowRect(win_main,&wnd_fs_rect);
490 GetWindowRect(win,&wnd_fs_rect_client);
491 SetWindowLong(win_main,GWL_STYLE,WS_VISIBLE | WS_POPUP );
492 SetWindowPos(win_main,NULL,0,0,0,0,SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
493 HMONITOR monitor=MonitorFromWindow(win_main,MONITOR_DEFAULTTONEAREST);
495 moninfo.cbSize=sizeof(moninfo);
497 if (!GetMonitorInfo(monitor,&moninfo)) return ;
498 SetWindowPos(win_main,wnd_topmost?HWND_TOPMOST:HWND_TOP,moninfo.rcMonitor.left,moninfo.rcMonitor.top,
499 moninfo.rcMonitor.right,moninfo.rcMonitor.bottom,SWP_FRAMECHANGED);
508 void ToggleTopmost() {
509 wnd_topmost=!wnd_topmost;
510 SetWindowPos(win_main,wnd_topmost?HWND_TOPMOST:HWND_NOTOPMOST,0,0,
511 0,0,SWP_NOMOVE | SWP_NOSIZE);
514 void CursorUpdate() {
516 GetCursorPos(&cursorpos);
518 asswind=WindowFromPoint(cursorpos);
519 if (asswind!=win_main && asswind!=win) {
520 return ; //not our responsibility
522 if ((timeGetTime()-lastmousemove)<4000 || cmenu) {
523 SetCursor(LoadCursor(NULL,IDC_ARROW));
529 bool ContextMenu(HWND wind,int x,int y) {
532 ScreenToClient(wind,&p);
533 GetClientRect(wind,&clientrect);
534 if (!PtInRect(&clientrect,p)) return false;
535 ClientToScreen(wind,&p);
538 menu=LoadMenu(hinstance,MAKEINTRESOURCE(VOMPMENU));
539 popup=GetSubMenu(menu,0);
540 if (wnd_fullscreen) {
541 CheckMenuItem(popup,VOMP_FULL_SCREEN,MF_BYCOMMAND|MF_CHECKED);
543 CheckMenuItem(popup,VOMP_FULL_SCREEN,MF_BYCOMMAND|MF_UNCHECKED);
546 CheckMenuItem(popup,VOMP_TOPMOST,MF_BYCOMMAND|MF_CHECKED);
548 CheckMenuItem(popup,VOMP_TOPMOST,MF_BYCOMMAND|MF_UNCHECKED);
551 TrackPopupMenu(popup,TPM_RIGHTBUTTON|TPM_LEFTALIGN,x,y,0,wind, NULL);
559 LONG FAR PASCAL WindowProc(HWND wind, UINT msg, WPARAM wparam, LPARAM lparam)
565 logger->log("Core", Log::NOTICE, "Window closed, shutting down...");
567 ((RemoteWin*)Input::getInstance())->SendPower();
571 CalculateWindowSize((RECT*) lparam,wparam);
575 int width = LOWORD(lparam);
576 int height = HIWORD(lparam);
578 if (wparam == SIZE_MAXIMIZED) {
581 } /*else if (wparam == SIZE_MINIMIZED) {
591 if (GetUpdateRect(wind, &r, FALSE)) {
592 BeginPaint(wind, &ps);
593 //Call Painting Mechanism
598 if (((RemoteWin*)remote)->ReceiveButtonVK(wparam)) {
599 return 0L; //We process that Key
601 return DefWindowProc(wind, msg, wparam, lparam);
606 if (((RemoteWin*)remote)->ReceiveButtonCH(wparam)) {
607 return 0L; //We process that Key
609 return DefWindowProc(wind, msg, wparam, lparam);
614 if (((RemoteWin*)remote)->ReceiveButtonAP(GET_APPCOMMAND_LPARAM(lparam))){
615 return TRUE; //yes we process that message
617 return DefWindowProc(wind, msg, wparam, lparam);
626 dynGetRawInputData((HRAWINPUT)lparam,RID_INPUT,NULL,&risize,sizeof(RAWINPUTHEADER));
627 lpit=(LPRAWINPUT)malloc(risize);
628 dynGetRawInputData((HRAWINPUT)lparam,RID_INPUT,lpit,&risize,sizeof(RAWINPUTHEADER));
630 if (lpit->header.dwType==RIM_TYPEHID && lpit->data.hid.dwSizeHid>=2) {
631 DWORD button=lpit->data.hid.bRawData[1] | (lpit->data.hid.bRawData[0]<< 8);
632 if (((RemoteWin*)remote)->ReceiveButtonRI(button)){
634 return 0; //yes we process that message
639 return DefWindowProc(wind, msg, wparam, lparam);
644 if (LOWORD(wparam)==VOMP_FULL_SCREEN) {
648 if (LOWORD(wparam)==VOMP_TOPMOST) {
652 if (((RemoteWin*)remote)->ReceiveButtonAP(LOWORD(wparam))){
653 return 0; //yes we process that message
655 return DefWindowProc(wind, msg, wparam, lparam);
660 if (((HANDLE)wparam)==win) {
664 return DefWindowProc(wind, msg, wparam, lparam);
668 if (wparam==SC_MAXIMIZE) {
671 } else if (wparam==SC_SCREENSAVE || wparam==SC_MONITORPOWER) {
674 return DefWindowProc(wind,msg,wparam, lparam);
679 lastmousemove=timeGetTime();
680 SetCursor(LoadCursor(NULL,IDC_ARROW));
681 SetTimer(wind,VOMP_CURSORUPDATE,4500,NULL);
682 POINT mpos={GET_X_LPARAM(lparam),GET_Y_LPARAM(lparam)};
683 ClientToScreen(wind,&mpos);
684 if (TranslateMousePosition(&mpos)) {
685 Message *mousemes=new Message();
686 mousemes->message=Message::MOUSE_MOVE;
688 mousemes->to=BoxStack::getInstance();
689 mousemes->parameter=(mpos.x & 0xFFFF)<< 16| (mpos.y & 0xFFFF);
691 messageQueue->postMessage(mousemes);
695 //return DefWindowProc(wind,msg,wparam, lparam);
699 if (wparam==VOMP_CURSORUPDATE) {
703 return DefWindowProc(wind, msg, wparam, lparam);
707 if (!ContextMenu(wind,GET_X_LPARAM(lparam),GET_Y_LPARAM(lparam))) {
708 return DefWindowProc(wind, msg, wparam, lparam);
713 case WM_LBUTTONDOWN:{
714 POINT mpos={GET_X_LPARAM(lparam),GET_Y_LPARAM(lparam)};
715 ClientToScreen(wind,&mpos);
716 if (TranslateMousePosition(&mpos)) {
717 Message *mousemes=new Message();
718 mousemes->message=Message::MOUSE_LBDOWN;
720 mousemes->to=BoxStack::getInstance();
721 mousemes->parameter=(mpos.x & 0xFFFF)<< 16| (mpos.y & 0xFFFF);
723 messageQueue->postMessage(mousemes);
727 POINT mpos = { GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam) };
728 ClientToScreen(wind, &mpos);
729 if (TranslateMousePosition(&mpos)) {
730 Message *mousemes = new Message();
731 mousemes->message = Message::MOUSE_SCROLL;
732 mousemes->from = NULL;
733 mousemes->to = BoxStack::getInstance();
734 mousemes->parameter = (0 & 0xFFFF) << 16 | (GET_WHEEL_DELTA_WPARAM(wparam) &0xFFFF);
735 mousemes->tag = (mpos.x & 0xFFFF) << 16 | (mpos.y & 0xFFFF);
736 messageQueue->postMessage(mousemes);
741 return DefWindowProc(wind, msg, wparam, lparam);
747 bool InitApp(HINSTANCE hinst,int cmdshow) {
751 wcs.style = CS_HREDRAW | CS_VREDRAW;
752 wcs.lpfnWndProc = WindowProc;
754 wcs.cbWndExtra = sizeof(DWORD);
755 wcs.hInstance = hinst;
758 wcs.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
759 wcs.lpszMenuName = NULL;
760 wcs.lpszClassName = "vomp";
761 acc=LoadAccelerators(hinst,MAKEINTRESOURCE(VOMPACCELERATOR));
762 if (!RegisterClass(&wcs))
764 flags =WS_VISIBLE | WS_POPUP | WS_CAPTION | WS_SYSMENU
765 |WS_MINIMIZEBOX | WS_SIZEBOX |WS_MAXIMIZEBOX;
766 RECT wnted={50,50,768+50,576+50};
767 AdjustWindowRect(&wnted,flags ,false);
768 win_main=CreateWindow("vomp","VOMP on Windows",flags, CW_USEDEFAULT,CW_USEDEFAULT,
769 wnted.right-wnted.left,wnted.bottom-wnted.top,NULL,NULL,hinst,NULL);
772 ShowWindow(win_main,SW_SHOWNORMAL);
773 UpdateWindow(win_main);
774 /* in order to handle letterboxing we use a child window */
776 child_wcs.style = CS_HREDRAW | CS_VREDRAW;
777 child_wcs.lpfnWndProc = WindowProc;
778 child_wcs.cbClsExtra = 0;
779 child_wcs.cbWndExtra = sizeof(DWORD);
780 child_wcs.hInstance = hinst;
781 child_wcs.hIcon = NULL;
782 child_wcs.hCursor = NULL;
783 child_wcs.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
784 child_wcs.lpszMenuName = NULL;
785 child_wcs.lpszClassName = "vomp_playback";
786 if (!RegisterClass(&child_wcs))
789 win=CreateWindow("vomp_playback","Vomp Playback Window",WS_VISIBLE | WS_CHILD |WS_CLIPCHILDREN,
790 0,0,768,576,win_main,NULL,hinst,NULL);
793 ShowWindow(win,SW_SHOWNORMAL);
795 if (remotefnc) {//at least windows XP
796 /* We want to support MCE Remote controls*/
797 RAWINPUTDEVICE remote_control_data[4];
798 ZeroMemory(remote_control_data,sizeof(remote_control_data));
799 remote_control_data[0].usUsagePage=0xFFBC;
800 remote_control_data[0].usUsage=0x88;
801 remote_control_data[0].dwFlags=0;
802 remote_control_data[1].usUsagePage=0x0C;
803 remote_control_data[1].usUsage=0x80;
804 remote_control_data[1].dwFlags=0;
805 remote_control_data[2].usUsagePage=0x0C;
806 remote_control_data[2].usUsage=0x01;
807 remote_control_data[2].dwFlags=0;
808 remote_control_data[3].usUsagePage=0x01;
809 remote_control_data[3].usUsage=0x80;
810 remote_control_data[3].dwFlags=0;
811 if (dynRegisterRawInputDevices(remote_control_data,4,sizeof(remote_control_data[0]))!=TRUE) {
812 MessageBox(0,"Registering remote control failed!","Aborting!",0);
824 // -------------------------------------------------------------------------------------------------------------------
826 void shutdown(int code)
830 boxstack->shutdown();
832 logger->log("Core", Log::NOTICE, "BoxStack module shut down");
835 if (command) // shut down command here in case views have posted messages
839 logger->log("Core", Log::NOTICE, "Command module shut down");
846 logger->log("Core", Log::NOTICE, "VDR module shut down");
853 logger->log("Core", Log::NOTICE, "OSD module shut down");
860 logger->log("Core", Log::NOTICE, "Audio module shut down");
867 logger->log("Core", Log::NOTICE, "Video module shut down");
874 logger->log("Core", Log::NOTICE, "Timers module shut down");
881 logger->log("Core", Log::NOTICE, "LED module shut down");
888 logger->log("Core", Log::NOTICE, "Remote module shut down");
893 logger->log("Core", Log::NOTICE, "WOL module shut down");
898 logger->log("Core", Log::NOTICE, "Log module shutting down... bye!\n\n");
905 logger->log("Core", Log::NOTICE, "Sleeptimer module shut down");
911 // -------------------------------------------------------------------------------------------------------------------
913 ULLONG htonll(ULLONG a)
915 return (((ULLONG)htonl((ULONG)((a<<32)>> 32))<<32)
916 |(ULONG)htonl(((ULONG) (a >> 32))));
919 ULLONG ntohll(ULLONG a)