2 Copyright 2004-2005 Chris Tallon
3 Copyright 2012 Marten Richter
5 This file is part of VOMP.
7 VOMP is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 VOMP is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with VOMP. If not, see <https://www.gnu.org/licenses/>.
27 #define _WIN32_WINNT 0x501
31 #include "vompreswin.h"
41 #include "windowsosd.h"
43 #include "osdwinpixel.h"
45 #include "osdwinvector.h"
48 #include "messagequeue.h"
50 void sighandler(int signalReceived);
51 void shutdown(int code);
53 // Global variables --------------------------------------------------------------------------------------------------
64 std::string commandLineServer; // NCONFIG
66 bool wnd_fullscreen=false;
67 bool wnd_topmost=false;
68 RECT wnd_fs_rect={20,20,768+20,576+20};
69 RECT wnd_fs_rect_client={0,0,768,576};
70 //OSVERSIONINFO windows_ver; //attempt to distigsh windows versions
76 typedef UINT (WINAPI *GETRAWINPUTDATAFNC) (HRAWINPUT,UINT,LPVOID,PUINT,UINT);
77 typedef UINT (WINAPI *REGISTERRAWINPUTDEVICEFNC) (PCRAWINPUTDEVICE,UINT,UINT);
79 GETRAWINPUTDATAFNC dynGetRawInputData=NULL;
80 REGISTERRAWINPUTDEVICEFNC dynRegisterRawInputDevices=NULL;
84 MessageQueue* messageQueue;
87 void MILLISLEEP(ULONG a)
92 int getClockRealTime(struct timespec *tp){
96 GetSystemTime(&systime);
97 SystemTimeToFileTime(&systime, (FILETIME*)&filetime);
98 tp->tv_sec = (filetime - WINDOWS_TIME_BASE_OFFSET) / (10 * 1000 * 1000);
99 tp->tv_nsec = ((filetime - WINDOWS_TIME_BASE_OFFSET) % (10 * 1000 * 1000)) * 100;
103 DWORD WINAPI controlthreadStart(void *arg)
110 void LoadRemoteFunctions() {
111 user32dll=LoadLibrary("user32.dll");
112 if (user32dll!=NULL) {
113 dynGetRawInputData=(GETRAWINPUTDATAFNC)GetProcAddress(user32dll,"GetRawInputData");
114 if (dynGetRawInputData!=NULL) {
115 dynRegisterRawInputDevices=(REGISTERRAWINPUTDEVICEFNC)GetProcAddress(user32dll,"RegisterRawInputDevices");
116 if (dynRegisterRawInputDevices!=NULL) {
123 bool InitApp(HINSTANCE hinst,int cmdshow);
125 HWND win_main;//global window handle
126 HWND win;//global child window handle
129 #define ERROR_MSG(str) MessageBox(win_main,str,"Error!",MB_OK|MB_ICONWARNING)
130 INT WINAPI WinMain( HINSTANCE hinst , HINSTANCE previnst, LPSTR cmdline, int cmdshow)
133 //On Windows we have to init a window, we use DXUT
134 LoadRemoteFunctions();
135 if (!InitApp(hinst,cmdshow)) return false;
136 //Starting Network support
138 int result = WSAStartup(MAKEWORD(2,2),&wsadat);
139 if (result!=NO_ERROR) {
140 ERROR_MSG("Initialising WinSocked: Error at WSAStartup()\n");
144 result= CoInitializeEx(NULL,COINIT_MULTITHREADED );//Initialize COM for DirectShow
146 ERROR_MSG("Initialising COM: Error at Coinitialize()\n");
153 // Init global vars ------------------------------------------------------------------------------------------------
156 remote = new InputMan();
158 timers = new Timers();
159 osd = new Osd_TYPE();
160 video = new VideoWin();
161 audio = new AudioWin();
162 control = new Control();
164 if (!logger || !remote || !led || !osd || !video || !audio || !control)
166 ERROR_MSG("Could not create objects. Memory problems?\n");
172 messageQueue = control;
174 // Get logging module started --------------------------------------------------------------------------------------
176 if (!logger->init(Log::DEBUG, "vompwin.log", true))
178 ERROR_MSG("Could not initialise log object. Aborting.\n");
184 logger->log("Core", Log::INFO, "Starting up...");
188 // Init modules ----------------------------------------------------------------------------------------------------
191 success = remote->init();
194 logger->log("Core", Log::INFO, "Remote module initialised");
198 logger->log("Core", Log::EMERG, "Remote module failed to initialise");
204 inputWin = remote->getInputWin();
206 success = led->init(0);
209 logger->log("Core", Log::INFO, "LED module initialised");
213 logger->log("Core", Log::EMERG, "LED module failed to initialise");
219 success = timers->init();
222 logger->log("Core", Log::INFO, "Timers module initialised");
226 logger->log("Core", Log::EMERG, "Timers module failed to initialise");
232 UCHAR videoFormat = Video::PAL; // PALNTSC FIXME.
234 success = video->init(videoFormat);
237 logger->log("Core", Log::INFO, "Video module initialised");
241 logger->log("Core", Log::EMERG, "Video module failed to initialise");
247 dynamic_cast<WindowsOsd*>(osd)->setWindow(win);
248 success = osd->init();
251 logger->log("Core", Log::INFO, "OSD module initialised");
255 logger->log("Core", Log::EMERG, "OSD module failed to initialise");
260 video->setDefaultAspect();
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 = control->init();
278 logger->log("Core", Log::INFO, "Control module initialised");
282 logger->log("Core", Log::EMERG, "Control module failed to initialise");
288 // Other init ------------------------------------------------------------------------------------------------------
290 logger->log("Core", Log::NOTICE, "Startup successful");
292 // Run main loop ---------------------------------------------------------------------------------------------------
294 // Ok, all major device components and other bits are loaded and ready
295 lastmousemove=timeGetTime();
297 HANDLE controlthread;
298 controlthread= CreateThread(NULL, 0, controlthreadStart, NULL,0,
301 message.message=WM_NULL;
303 while(run && WaitForSingleObject(controlthread,0)==WAIT_TIMEOUT) {
304 if (PeekMessage(&message, NULL, 0,0,PM_REMOVE)!=0) {
305 if (TranslateAccelerator(win_main,acc,&message)==NULL) {
306 TranslateMessage(&message);
307 DispatchMessage(&message);
308 switch (message.message) {
310 run=false; //TODO post exit to control Messages
314 //Render, moved to OSD !
316 //((OsdWin*)osd)->Render();
319 // When that returns quit ------------------------------------------------------------------------------------------
320 WaitForSingleObject(controlthread,INFINITE);
323 if (user32dll) FreeModule(user32dll);
328 bool TranslateMousePosition(POINT *pos) {
331 ScreenToClient(win,pos);
332 GetClientRect(win,&clientrect);
333 if (!PtInRect(&clientrect,*pos)) return false;//Don't pass it further
334 pos->x=((double)pos->x)/((double) (clientrect.right-clientrect.left))
335 *((double)Video::getInstance()->getScreenWidth());
336 pos->y=((double)pos->y)/((double) (clientrect.bottom-clientrect.top))
337 *((double)Video::getInstance()->getScreenHeight());
344 void CalculateWindowSize(RECT * size,ULONG size_mode) {
347 DWORD adjheight,adjwidth;
348 if (!wnd_fullscreen) {
349 DWORD flags =WS_VISIBLE | WS_POPUP | WS_CAPTION | WS_SYSMENU
350 |WS_MINIMIZEBOX | WS_SIZEBOX |WS_MAXIMIZEBOX;
351 RECT wnted={50,50,150,150};
352 AdjustWindowRect(&wnted,flags ,false);
353 adjwidth=-wnted.left+wnted.right-100;
354 adjheight=-wnted.top+wnted.bottom-100;
356 adjwidth=adjheight=0;
358 width=size->right-size->left-adjwidth;
359 height=size->bottom-size->top-adjheight;
360 UCHAR mode=Video::PAL;
361 UCHAR aspect=Video::ASPECT4X3;
362 UCHAR tvsize=Video::ASPECT4X3;
364 mode=video->getMode();
365 aspect=((VideoWin*)video)->getAspectRatio();
366 tvsize=((VideoWin*)video)->getPseudoTVsize();
369 double aspectrt=4./3.;
370 if (tvsize==Video::ASPECT16X9) {
371 if (aspect==Video::ASPECT16X9) {
372 aspectrt=4./3.; //looks strange, but it is a 16:9 tv
373 } else if (aspect==Video::ASPECT4X3) {
374 aspectrt=4./3./(16./9.)*(4./3.); //I hope this is correct
376 } else if (tvsize==Video::ASPECT4X3) {
377 if (aspect==Video::ASPECT16X9) {
378 if (mode!=Video::NORMAL) {
383 } else if (aspect==Video::ASPECT4X3) {
387 if (!wnd_fullscreen) {
390 case WMSZ_BOTTOMRIGHT:
393 width=(ULONG)(((double)height)*aspectrt);
394 size->right=size->left+width+adjwidth;
396 case WMSZ_BOTTOMLEFT:
398 width=(ULONG)(((double)height)*aspectrt);
399 size->left=size->right-width-adjwidth;
403 height=(ULONG)(((double)width)/aspectrt);
404 size->bottom=size->top+height+adjheight;
407 MoveWindow(win,0,0,width,height,TRUE);
409 RECT newrect={0,0,width,height};
411 if ((ULONG)(((double)height)*aspectrt)>width) {
412 newlength=(ULONG)(((double)width)/aspectrt);
413 newrect.top+=(height-newlength)/2;
414 newrect.bottom-=(height-newlength);
416 newlength=(ULONG)(((double)height)*aspectrt);
417 newrect.left+=(width-newlength)/2;
418 newrect.right-=(width-newlength);
420 MoveWindow(win,newrect.left,newrect.top,newrect.right,newrect.bottom,TRUE);
425 void AdjustWindow() {
426 if (!wnd_fullscreen) {
428 GetWindowRect(win_main,&winrect);
429 CalculateWindowSize(&winrect,WMSZ_BOTTOM);
430 MoveWindow(win_main,winrect.left,
431 winrect.top,winrect.right-winrect.left,winrect.bottom-winrect.top,true);
434 GetWindowRect(win_main,&winrect);
435 CalculateWindowSize(&winrect,WMSZ_BOTTOM);
440 void ToggleFullscreen() {
441 if (wnd_fullscreen) {
442 wnd_fullscreen=false;
443 SetWindowLong(win_main,GWL_STYLE,WS_VISIBLE | WS_POPUP | WS_CAPTION | WS_SYSMENU
444 |WS_MINIMIZEBOX | WS_SIZEBOX |WS_MAXIMIZEBOX);
445 SetWindowPos(win_main,NULL,0,0,0,0,SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
447 SetWindowPos(win_main,wnd_topmost?HWND_TOPMOST:HWND_TOP,wnd_fs_rect.left,wnd_fs_rect.top,
448 wnd_fs_rect.right-wnd_fs_rect.left,
449 wnd_fs_rect.bottom-wnd_fs_rect.top,
450 SWP_DRAWFRAME | SWP_FRAMECHANGED);
451 MoveWindow(win,wnd_fs_rect_client.left,wnd_fs_rect_client.top,
452 wnd_fs_rect_client.right-wnd_fs_rect_client.left,
453 wnd_fs_rect_client.bottom-wnd_fs_rect_client.top,TRUE);
456 GetWindowRect(win_main,&wnd_fs_rect);
457 GetWindowRect(win,&wnd_fs_rect_client);
458 SetWindowLong(win_main,GWL_STYLE,WS_VISIBLE | WS_POPUP );
459 SetWindowPos(win_main,NULL,0,0,0,0,SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
460 HMONITOR monitor=MonitorFromWindow(win_main,MONITOR_DEFAULTTONEAREST);
462 moninfo.cbSize=sizeof(moninfo);
464 if (!GetMonitorInfo(monitor,&moninfo)) return ;
465 SetWindowPos(win_main,wnd_topmost?HWND_TOPMOST:HWND_TOP,moninfo.rcMonitor.left,moninfo.rcMonitor.top,
466 moninfo.rcMonitor.right,moninfo.rcMonitor.bottom,SWP_FRAMECHANGED);
475 void ToggleTopmost() {
476 wnd_topmost=!wnd_topmost;
477 SetWindowPos(win_main,wnd_topmost?HWND_TOPMOST:HWND_NOTOPMOST,0,0,
478 0,0,SWP_NOMOVE | SWP_NOSIZE);
481 void CursorUpdate() {
483 GetCursorPos(&cursorpos);
485 asswind=WindowFromPoint(cursorpos);
486 if (asswind!=win_main && asswind!=win) {
487 return ; //not our responsibility
489 if ((timeGetTime()-lastmousemove)<4000 || cmenu) {
490 SetCursor(LoadCursor(NULL,IDC_ARROW));
496 bool ContextMenu(HWND wind,int x,int y) {
499 ScreenToClient(wind,&p);
500 GetClientRect(wind,&clientrect);
501 if (!PtInRect(&clientrect,p)) return false;
502 ClientToScreen(wind,&p);
505 menu=LoadMenu(hinstance,MAKEINTRESOURCE(VOMPMENU));
506 popup=GetSubMenu(menu,0);
507 if (wnd_fullscreen) {
508 CheckMenuItem(popup,VOMP_FULL_SCREEN,MF_BYCOMMAND|MF_CHECKED);
510 CheckMenuItem(popup,VOMP_FULL_SCREEN,MF_BYCOMMAND|MF_UNCHECKED);
513 CheckMenuItem(popup,VOMP_TOPMOST,MF_BYCOMMAND|MF_CHECKED);
515 CheckMenuItem(popup,VOMP_TOPMOST,MF_BYCOMMAND|MF_UNCHECKED);
518 TrackPopupMenu(popup,TPM_RIGHTBUTTON|TPM_LEFTALIGN,x,y,0,wind, NULL);
526 LONG FAR PASCAL WindowProc(HWND wind, UINT msg, WPARAM wparam, LPARAM lparam)
532 logger->log("Core", Log::NOTICE, "Window closed, shutting down...");
537 CalculateWindowSize((RECT*) lparam,wparam);
541 int width = LOWORD(lparam);
542 int height = HIWORD(lparam);
544 if (wparam == SIZE_MAXIMIZED) {
547 } /*else if (wparam == SIZE_MINIMIZED) {
557 if (GetUpdateRect(wind, &r, FALSE)) {
558 BeginPaint(wind, &ps);
559 //Call Painting Mechanism
564 if (inputWin->ReceiveButtonVK(wparam)) {
565 return 0L; //We process that Key
567 return DefWindowProc(wind, msg, wparam, lparam);
572 if (inputWin->ReceiveButtonCH(wparam)) {
573 return 0L; //We process that Key
575 return DefWindowProc(wind, msg, wparam, lparam);
580 if (inputWin->ReceiveButtonAP(GET_APPCOMMAND_LPARAM(lparam))){
581 return TRUE; //yes we process that message
583 return DefWindowProc(wind, msg, wparam, lparam);
592 dynGetRawInputData((HRAWINPUT)lparam,RID_INPUT,NULL,&risize,sizeof(RAWINPUTHEADER));
593 lpit=(LPRAWINPUT)malloc(risize);
594 dynGetRawInputData((HRAWINPUT)lparam,RID_INPUT,lpit,&risize,sizeof(RAWINPUTHEADER));
596 if (lpit->header.dwType==RIM_TYPEHID && lpit->data.hid.dwSizeHid>=2) {
597 DWORD button=lpit->data.hid.bRawData[1] | (lpit->data.hid.bRawData[0]<< 8);
598 if (inputWin->ReceiveButtonRI(button)){
600 return 0; //yes we process that message
605 return DefWindowProc(wind, msg, wparam, lparam);
610 if (LOWORD(wparam)==VOMP_FULL_SCREEN) {
614 if (LOWORD(wparam)==VOMP_TOPMOST) {
618 if (inputWin->ReceiveButtonAP(LOWORD(wparam))){
619 return 0; //yes we process that message
621 return DefWindowProc(wind, msg, wparam, lparam);
626 if (((HANDLE)wparam)==win) {
630 return DefWindowProc(wind, msg, wparam, lparam);
634 if (wparam==SC_MAXIMIZE) {
637 } else if (wparam==SC_SCREENSAVE || wparam==SC_MONITORPOWER) {
640 return DefWindowProc(wind,msg,wparam, lparam);
645 lastmousemove=timeGetTime();
646 SetCursor(LoadCursor(NULL,IDC_ARROW));
647 SetTimer(wind,VOMP_CURSORUPDATE,4500,NULL);
648 POINT mpos={GET_X_LPARAM(lparam),GET_Y_LPARAM(lparam)};
649 ClientToScreen(wind,&mpos);
650 if (TranslateMousePosition(&mpos)) {
651 Message *mousemes=new Message();
652 mousemes->message=Message::MOUSE_MOVE;
654 mousemes->p_to = Message::MOUSE_RECEIVER;
655 mousemes->parameter=(mpos.x & 0xFFFF)<< 16| (mpos.y & 0xFFFF);
657 messageQueue->postMessage(mousemes);
661 //return DefWindowProc(wind,msg,wparam, lparam);
665 if (wparam==VOMP_CURSORUPDATE) {
669 return DefWindowProc(wind, msg, wparam, lparam);
673 if (!ContextMenu(wind,GET_X_LPARAM(lparam),GET_Y_LPARAM(lparam))) {
674 return DefWindowProc(wind, msg, wparam, lparam);
679 case WM_LBUTTONDOWN:{
680 POINT mpos={GET_X_LPARAM(lparam),GET_Y_LPARAM(lparam)};
681 ClientToScreen(wind,&mpos);
682 if (TranslateMousePosition(&mpos)) {
683 Message *mousemes=new Message();
684 mousemes->message=Message::MOUSE_LBDOWN;
686 mousemes->p_to = Message::MOUSE_RECEIVER;
687 mousemes->parameter=(mpos.x & 0xFFFF)<< 16| (mpos.y & 0xFFFF);
689 messageQueue->postMessage(mousemes);
693 POINT mpos = { GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam) };
694 ClientToScreen(wind, &mpos);
695 if (TranslateMousePosition(&mpos)) {
696 Message *mousemes = new Message();
697 mousemes->message = Message::MOUSE_SCROLL;
698 mousemes->from = NULL;
699 mousemes->p_to = Message::MOUSE_RECEIVER;
700 mousemes->parameter = (0 & 0xFFFF) << 16 | (GET_WHEEL_DELTA_WPARAM(wparam) &0xFFFF);
701 mousemes->tag = (mpos.x & 0xFFFF) << 16 | (mpos.y & 0xFFFF);
702 messageQueue->postMessage(mousemes);
707 return DefWindowProc(wind, msg, wparam, lparam);
713 bool InitApp(HINSTANCE hinst,int cmdshow) {
717 wcs.style = CS_HREDRAW | CS_VREDRAW;
718 wcs.lpfnWndProc = WindowProc;
720 wcs.cbWndExtra = sizeof(DWORD);
721 wcs.hInstance = hinst;
724 wcs.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
725 wcs.lpszMenuName = NULL;
726 wcs.lpszClassName = "vomp";
727 acc=LoadAccelerators(hinst,MAKEINTRESOURCE(VOMPACCELERATOR));
728 if (!RegisterClass(&wcs))
730 flags =WS_VISIBLE | WS_POPUP | WS_CAPTION | WS_SYSMENU
731 |WS_MINIMIZEBOX | WS_SIZEBOX |WS_MAXIMIZEBOX;
732 RECT wnted={50,50,768+50,576+50};
733 AdjustWindowRect(&wnted,flags ,false);
734 win_main=CreateWindow("vomp","VOMP on Windows",flags, CW_USEDEFAULT,CW_USEDEFAULT,
735 wnted.right-wnted.left,wnted.bottom-wnted.top,NULL,NULL,hinst,NULL);
738 ShowWindow(win_main,SW_SHOWNORMAL);
739 UpdateWindow(win_main);
740 /* in order to handle letterboxing we use a child window */
742 child_wcs.style = CS_HREDRAW | CS_VREDRAW;
743 child_wcs.lpfnWndProc = WindowProc;
744 child_wcs.cbClsExtra = 0;
745 child_wcs.cbWndExtra = sizeof(DWORD);
746 child_wcs.hInstance = hinst;
747 child_wcs.hIcon = NULL;
748 child_wcs.hCursor = NULL;
749 child_wcs.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
750 child_wcs.lpszMenuName = NULL;
751 child_wcs.lpszClassName = "vomp_playback";
752 if (!RegisterClass(&child_wcs))
755 win=CreateWindow("vomp_playback","Vomp Playback Window",WS_VISIBLE | WS_CHILD |WS_CLIPCHILDREN,
756 0,0,768,576,win_main,NULL,hinst,NULL);
759 ShowWindow(win,SW_SHOWNORMAL);
761 if (remotefnc) {//at least windows XP
762 /* We want to support MCE Remote controls*/
763 RAWINPUTDEVICE remote_control_data[4];
764 ZeroMemory(remote_control_data,sizeof(remote_control_data));
765 remote_control_data[0].usUsagePage=0xFFBC;
766 remote_control_data[0].usUsage=0x88;
767 remote_control_data[0].dwFlags=0;
768 remote_control_data[1].usUsagePage=0x0C;
769 remote_control_data[1].usUsage=0x80;
770 remote_control_data[1].dwFlags=0;
771 remote_control_data[2].usUsagePage=0x0C;
772 remote_control_data[2].usUsage=0x01;
773 remote_control_data[2].dwFlags=0;
774 remote_control_data[3].usUsagePage=0x01;
775 remote_control_data[3].usUsage=0x80;
776 remote_control_data[3].dwFlags=0;
777 if (dynRegisterRawInputDevices(remote_control_data,4,sizeof(remote_control_data[0]))!=TRUE) {
778 MessageBox(0,"Registering remote control failed!","Aborting!",0);
790 // -------------------------------------------------------------------------------------------------------------------
792 void shutdown(int code)
794 if (control) // shut down control here in case views have posted messages
798 logger->log("Core", Log::NOTICE, "Control module shut down");
805 logger->log("Core", Log::NOTICE, "VDR module shut down");
812 logger->log("Core", Log::NOTICE, "OSD module shut down");
819 logger->log("Core", Log::NOTICE, "Audio module shut down");
826 logger->log("Core", Log::NOTICE, "Video module shut down");
833 logger->log("Core", Log::NOTICE, "Timers module shut down");
840 logger->log("Core", Log::NOTICE, "LED module shut down");
847 logger->log("Core", Log::NOTICE, "Remote module shut down");
852 logger->log("Core", Log::NOTICE, "Log module shutting down... bye!\n\n");
860 // -------------------------------------------------------------------------------------------------------------------
862 ULLONG htonll(ULLONG a)
864 return (((ULLONG)htonl((ULONG)((a<<32)>> 32))<<32)
865 |(ULONG)htonl(((ULONG) (a >> 32))));
868 ULLONG ntohll(ULLONG a)
873 const std::string& getCommandLineServer()
875 return commandLineServer;