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"
42 #include "windowsosd.h"
44 #include "osdwinpixel.h"
46 #include "osdwinvector.h"
49 #include "messagequeue.h"
51 void sighandler(int signalReceived);
52 void shutdown(int code);
54 // Global variables --------------------------------------------------------------------------------------------------
66 std::string commandLineServer;
68 bool wnd_fullscreen=false;
69 bool wnd_topmost=false;
70 RECT wnd_fs_rect={20,20,768+20,576+20};
71 RECT wnd_fs_rect_client={0,0,768,576};
72 //OSVERSIONINFO windows_ver; //attempt to distigsh windows versions
78 typedef UINT (WINAPI *GETRAWINPUTDATAFNC) (HRAWINPUT,UINT,LPVOID,PUINT,UINT);
79 typedef UINT (WINAPI *REGISTERRAWINPUTDEVICEFNC) (PCRAWINPUTDEVICE,UINT,UINT);
81 GETRAWINPUTDATAFNC dynGetRawInputData=NULL;
82 REGISTERRAWINPUTDEVICEFNC dynRegisterRawInputDevices=NULL;
86 MessageQueue* messageQueue;
89 void MILLISLEEP(ULONG a)
94 int getClockRealTime(struct timespec *tp){
98 GetSystemTime(&systime);
99 SystemTimeToFileTime(&systime, (FILETIME*)&filetime);
100 tp->tv_sec = (filetime - WINDOWS_TIME_BASE_OFFSET) / (10 * 1000 * 1000);
101 tp->tv_nsec = ((filetime - WINDOWS_TIME_BASE_OFFSET) % (10 * 1000 * 1000)) * 100;
105 DWORD WINAPI controlthreadStart(void *arg)
112 void LoadRemoteFunctions() {
113 user32dll=LoadLibrary("user32.dll");
114 if (user32dll!=NULL) {
115 dynGetRawInputData=(GETRAWINPUTDATAFNC)GetProcAddress(user32dll,"GetRawInputData");
116 if (dynGetRawInputData!=NULL) {
117 dynRegisterRawInputDevices=(REGISTERRAWINPUTDEVICEFNC)GetProcAddress(user32dll,"RegisterRawInputDevices");
118 if (dynRegisterRawInputDevices!=NULL) {
125 bool InitApp(HINSTANCE hinst,int cmdshow);
127 HWND win_main;//global window handle
128 HWND win;//global child window handle
131 #define ERROR_MSG(str) MessageBox(win_main,str,"Error!",MB_OK|MB_ICONWARNING)
132 INT WINAPI WinMain( HINSTANCE hinst , HINSTANCE previnst, LPSTR cmdline, int cmdshow)
135 //On Windows we have to init a window, we use DXUT
136 LoadRemoteFunctions();
137 if (!InitApp(hinst,cmdshow)) return false;
138 //Starting Network support
140 int result = WSAStartup(MAKEWORD(2,2),&wsadat);
141 if (result!=NO_ERROR) {
142 ERROR_MSG("Initialising WinSocked: Error at WSAStartup()\n");
146 result= CoInitializeEx(NULL,COINIT_MULTITHREADED );//Initialize COM for DirectShow
148 ERROR_MSG("Initialising COM: Error at Coinitialize()\n");
155 // Init global vars ------------------------------------------------------------------------------------------------
158 remote = new InputMan();
160 timers = new Timers();
161 osd = new Osd_TYPE();
163 video = new VideoWin();
164 audio = new AudioWin();
165 control = new Control();
167 if (!logger || !remote || !led || !osd || !video || !audio || !control)
169 ERROR_MSG("Could not create objects. Memory problems?\n");
175 messageQueue = control;
177 // Get logging module started --------------------------------------------------------------------------------------
179 if (!logger->init(Log::DEBUG, "vompwin.log", true))
181 ERROR_MSG("Could not initialise log object. Aborting.\n");
187 logger->log("Core", Log::INFO, "Starting up...");
191 // Init modules ----------------------------------------------------------------------------------------------------
194 success = remote->init();
197 logger->log("Core", Log::INFO, "Remote module initialised");
201 logger->log("Core", Log::EMERG, "Remote module failed to initialise");
207 inputWin = remote->getInputWin();
209 success = led->init(0);
212 logger->log("Core", Log::INFO, "LED module initialised");
216 logger->log("Core", Log::EMERG, "LED module failed to initialise");
222 success = timers->init();
225 logger->log("Core", Log::INFO, "Timers module initialised");
229 logger->log("Core", Log::EMERG, "Timers module failed to initialise");
235 UCHAR videoFormat = Video::PAL; // PALNTSC FIXME.
237 success = video->init(videoFormat);
240 logger->log("Core", Log::INFO, "Video module initialised");
244 logger->log("Core", Log::EMERG, "Video module failed to initialise");
250 dynamic_cast<WindowsOsd*>(osd)->setWindow(win);
251 success = osd->init();
254 logger->log("Core", Log::INFO, "OSD module initialised");
258 logger->log("Core", Log::EMERG, "OSD module failed to initialise");
263 video->setDefaultAspect();
265 success = audio->init(Audio::MPEG2_PES);
268 logger->log("Core", Log::INFO, "Audio module initialised");
272 logger->log("Core", Log::EMERG, "Audio module failed to initialise");
278 success = vdr->init();
281 logger->log("Core", Log::INFO, "VDR module initialised");
285 logger->log("Core", Log::EMERG, "VDR module failed to initialise");
291 success = control->init();
294 logger->log("Core", Log::INFO, "Control module initialised");
298 logger->log("Core", Log::EMERG, "Control module failed to initialise");
304 // Other init ------------------------------------------------------------------------------------------------------
306 logger->log("Core", Log::NOTICE, "Startup successful");
308 // Run main loop ---------------------------------------------------------------------------------------------------
310 // Ok, all major device components and other bits are loaded and ready
311 lastmousemove=timeGetTime();
313 HANDLE controlthread;
314 controlthread= CreateThread(NULL, 0, controlthreadStart, NULL,0,
317 message.message=WM_NULL;
319 while(run && WaitForSingleObject(controlthread,0)==WAIT_TIMEOUT) {
320 if (PeekMessage(&message, NULL, 0,0,PM_REMOVE)!=0) {
321 if (TranslateAccelerator(win_main,acc,&message)==NULL) {
322 TranslateMessage(&message);
323 DispatchMessage(&message);
324 switch (message.message) {
326 run=false; //TODO post exit to control Messages
330 //Render, moved to OSD !
332 //((OsdWin*)osd)->Render();
335 // When that returns quit ------------------------------------------------------------------------------------------
336 WaitForSingleObject(controlthread,INFINITE);
339 if (user32dll) FreeModule(user32dll);
344 bool TranslateMousePosition(POINT *pos) {
347 ScreenToClient(win,pos);
348 GetClientRect(win,&clientrect);
349 if (!PtInRect(&clientrect,*pos)) return false;//Don't pass it further
350 pos->x=((double)pos->x)/((double) (clientrect.right-clientrect.left))
351 *((double)Video::getInstance()->getScreenWidth());
352 pos->y=((double)pos->y)/((double) (clientrect.bottom-clientrect.top))
353 *((double)Video::getInstance()->getScreenHeight());
360 void CalculateWindowSize(RECT * size,ULONG size_mode) {
363 DWORD adjheight,adjwidth;
364 if (!wnd_fullscreen) {
365 DWORD flags =WS_VISIBLE | WS_POPUP | WS_CAPTION | WS_SYSMENU
366 |WS_MINIMIZEBOX | WS_SIZEBOX |WS_MAXIMIZEBOX;
367 RECT wnted={50,50,150,150};
368 AdjustWindowRect(&wnted,flags ,false);
369 adjwidth=-wnted.left+wnted.right-100;
370 adjheight=-wnted.top+wnted.bottom-100;
372 adjwidth=adjheight=0;
374 width=size->right-size->left-adjwidth;
375 height=size->bottom-size->top-adjheight;
376 UCHAR mode=Video::PAL;
377 UCHAR aspect=Video::ASPECT4X3;
378 UCHAR tvsize=Video::ASPECT4X3;
380 mode=video->getMode();
381 aspect=((VideoWin*)video)->getAspectRatio();
382 tvsize=((VideoWin*)video)->getPseudoTVsize();
385 double aspectrt=4./3.;
386 if (tvsize==Video::ASPECT16X9) {
387 if (aspect==Video::ASPECT16X9) {
388 aspectrt=4./3.; //looks strange, but it is a 16:9 tv
389 } else if (aspect==Video::ASPECT4X3) {
390 aspectrt=4./3./(16./9.)*(4./3.); //I hope this is correct
392 } else if (tvsize==Video::ASPECT4X3) {
393 if (aspect==Video::ASPECT16X9) {
394 if (mode!=Video::NORMAL) {
399 } else if (aspect==Video::ASPECT4X3) {
403 if (!wnd_fullscreen) {
406 case WMSZ_BOTTOMRIGHT:
409 width=(ULONG)(((double)height)*aspectrt);
410 size->right=size->left+width+adjwidth;
412 case WMSZ_BOTTOMLEFT:
414 width=(ULONG)(((double)height)*aspectrt);
415 size->left=size->right-width-adjwidth;
419 height=(ULONG)(((double)width)/aspectrt);
420 size->bottom=size->top+height+adjheight;
423 MoveWindow(win,0,0,width,height,TRUE);
425 RECT newrect={0,0,width,height};
427 if ((ULONG)(((double)height)*aspectrt)>width) {
428 newlength=(ULONG)(((double)width)/aspectrt);
429 newrect.top+=(height-newlength)/2;
430 newrect.bottom-=(height-newlength);
432 newlength=(ULONG)(((double)height)*aspectrt);
433 newrect.left+=(width-newlength)/2;
434 newrect.right-=(width-newlength);
436 MoveWindow(win,newrect.left,newrect.top,newrect.right,newrect.bottom,TRUE);
441 void AdjustWindow() {
442 if (!wnd_fullscreen) {
444 GetWindowRect(win_main,&winrect);
445 CalculateWindowSize(&winrect,WMSZ_BOTTOM);
446 MoveWindow(win_main,winrect.left,
447 winrect.top,winrect.right-winrect.left,winrect.bottom-winrect.top,true);
450 GetWindowRect(win_main,&winrect);
451 CalculateWindowSize(&winrect,WMSZ_BOTTOM);
456 void ToggleFullscreen() {
457 if (wnd_fullscreen) {
458 wnd_fullscreen=false;
459 SetWindowLong(win_main,GWL_STYLE,WS_VISIBLE | WS_POPUP | WS_CAPTION | WS_SYSMENU
460 |WS_MINIMIZEBOX | WS_SIZEBOX |WS_MAXIMIZEBOX);
461 SetWindowPos(win_main,NULL,0,0,0,0,SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
463 SetWindowPos(win_main,wnd_topmost?HWND_TOPMOST:HWND_TOP,wnd_fs_rect.left,wnd_fs_rect.top,
464 wnd_fs_rect.right-wnd_fs_rect.left,
465 wnd_fs_rect.bottom-wnd_fs_rect.top,
466 SWP_DRAWFRAME | SWP_FRAMECHANGED);
467 MoveWindow(win,wnd_fs_rect_client.left,wnd_fs_rect_client.top,
468 wnd_fs_rect_client.right-wnd_fs_rect_client.left,
469 wnd_fs_rect_client.bottom-wnd_fs_rect_client.top,TRUE);
472 GetWindowRect(win_main,&wnd_fs_rect);
473 GetWindowRect(win,&wnd_fs_rect_client);
474 SetWindowLong(win_main,GWL_STYLE,WS_VISIBLE | WS_POPUP );
475 SetWindowPos(win_main,NULL,0,0,0,0,SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
476 HMONITOR monitor=MonitorFromWindow(win_main,MONITOR_DEFAULTTONEAREST);
478 moninfo.cbSize=sizeof(moninfo);
480 if (!GetMonitorInfo(monitor,&moninfo)) return ;
481 SetWindowPos(win_main,wnd_topmost?HWND_TOPMOST:HWND_TOP,moninfo.rcMonitor.left,moninfo.rcMonitor.top,
482 moninfo.rcMonitor.right,moninfo.rcMonitor.bottom,SWP_FRAMECHANGED);
491 void ToggleTopmost() {
492 wnd_topmost=!wnd_topmost;
493 SetWindowPos(win_main,wnd_topmost?HWND_TOPMOST:HWND_NOTOPMOST,0,0,
494 0,0,SWP_NOMOVE | SWP_NOSIZE);
497 void CursorUpdate() {
499 GetCursorPos(&cursorpos);
501 asswind=WindowFromPoint(cursorpos);
502 if (asswind!=win_main && asswind!=win) {
503 return ; //not our responsibility
505 if ((timeGetTime()-lastmousemove)<4000 || cmenu) {
506 SetCursor(LoadCursor(NULL,IDC_ARROW));
512 bool ContextMenu(HWND wind,int x,int y) {
515 ScreenToClient(wind,&p);
516 GetClientRect(wind,&clientrect);
517 if (!PtInRect(&clientrect,p)) return false;
518 ClientToScreen(wind,&p);
521 menu=LoadMenu(hinstance,MAKEINTRESOURCE(VOMPMENU));
522 popup=GetSubMenu(menu,0);
523 if (wnd_fullscreen) {
524 CheckMenuItem(popup,VOMP_FULL_SCREEN,MF_BYCOMMAND|MF_CHECKED);
526 CheckMenuItem(popup,VOMP_FULL_SCREEN,MF_BYCOMMAND|MF_UNCHECKED);
529 CheckMenuItem(popup,VOMP_TOPMOST,MF_BYCOMMAND|MF_CHECKED);
531 CheckMenuItem(popup,VOMP_TOPMOST,MF_BYCOMMAND|MF_UNCHECKED);
534 TrackPopupMenu(popup,TPM_RIGHTBUTTON|TPM_LEFTALIGN,x,y,0,wind, NULL);
542 LONG FAR PASCAL WindowProc(HWND wind, UINT msg, WPARAM wparam, LPARAM lparam)
548 logger->log("Core", Log::NOTICE, "Window closed, shutting down...");
553 CalculateWindowSize((RECT*) lparam,wparam);
557 int width = LOWORD(lparam);
558 int height = HIWORD(lparam);
560 if (wparam == SIZE_MAXIMIZED) {
563 } /*else if (wparam == SIZE_MINIMIZED) {
573 if (GetUpdateRect(wind, &r, FALSE)) {
574 BeginPaint(wind, &ps);
575 //Call Painting Mechanism
580 if (inputWin->ReceiveButtonVK(wparam)) {
581 return 0L; //We process that Key
583 return DefWindowProc(wind, msg, wparam, lparam);
588 if (inputWin->ReceiveButtonCH(wparam)) {
589 return 0L; //We process that Key
591 return DefWindowProc(wind, msg, wparam, lparam);
596 if (inputWin->ReceiveButtonAP(GET_APPCOMMAND_LPARAM(lparam))){
597 return TRUE; //yes we process that message
599 return DefWindowProc(wind, msg, wparam, lparam);
608 dynGetRawInputData((HRAWINPUT)lparam,RID_INPUT,NULL,&risize,sizeof(RAWINPUTHEADER));
609 lpit=(LPRAWINPUT)malloc(risize);
610 dynGetRawInputData((HRAWINPUT)lparam,RID_INPUT,lpit,&risize,sizeof(RAWINPUTHEADER));
612 if (lpit->header.dwType==RIM_TYPEHID && lpit->data.hid.dwSizeHid>=2) {
613 DWORD button=lpit->data.hid.bRawData[1] | (lpit->data.hid.bRawData[0]<< 8);
614 if (inputWin->ReceiveButtonRI(button)){
616 return 0; //yes we process that message
621 return DefWindowProc(wind, msg, wparam, lparam);
626 if (LOWORD(wparam)==VOMP_FULL_SCREEN) {
630 if (LOWORD(wparam)==VOMP_TOPMOST) {
634 if (inputWin->ReceiveButtonAP(LOWORD(wparam))){
635 return 0; //yes we process that message
637 return DefWindowProc(wind, msg, wparam, lparam);
642 if (((HANDLE)wparam)==win) {
646 return DefWindowProc(wind, msg, wparam, lparam);
650 if (wparam==SC_MAXIMIZE) {
653 } else if (wparam==SC_SCREENSAVE || wparam==SC_MONITORPOWER) {
656 return DefWindowProc(wind,msg,wparam, lparam);
661 lastmousemove=timeGetTime();
662 SetCursor(LoadCursor(NULL,IDC_ARROW));
663 SetTimer(wind,VOMP_CURSORUPDATE,4500,NULL);
664 POINT mpos={GET_X_LPARAM(lparam),GET_Y_LPARAM(lparam)};
665 ClientToScreen(wind,&mpos);
666 if (TranslateMousePosition(&mpos)) {
667 Message *mousemes=new Message();
668 mousemes->message=Message::MOUSE_MOVE;
670 mousemes->p_to = Message::MOUSE_RECEIVER;
671 mousemes->parameter=(mpos.x & 0xFFFF)<< 16| (mpos.y & 0xFFFF);
673 messageQueue->postMessage(mousemes);
677 //return DefWindowProc(wind,msg,wparam, lparam);
681 if (wparam==VOMP_CURSORUPDATE) {
685 return DefWindowProc(wind, msg, wparam, lparam);
689 if (!ContextMenu(wind,GET_X_LPARAM(lparam),GET_Y_LPARAM(lparam))) {
690 return DefWindowProc(wind, msg, wparam, lparam);
695 case WM_LBUTTONDOWN:{
696 POINT mpos={GET_X_LPARAM(lparam),GET_Y_LPARAM(lparam)};
697 ClientToScreen(wind,&mpos);
698 if (TranslateMousePosition(&mpos)) {
699 Message *mousemes=new Message();
700 mousemes->message=Message::MOUSE_LBDOWN;
702 mousemes->p_to = Message::MOUSE_RECEIVER;
703 mousemes->parameter=(mpos.x & 0xFFFF)<< 16| (mpos.y & 0xFFFF);
705 messageQueue->postMessage(mousemes);
709 POINT mpos = { GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam) };
710 ClientToScreen(wind, &mpos);
711 if (TranslateMousePosition(&mpos)) {
712 Message *mousemes = new Message();
713 mousemes->message = Message::MOUSE_SCROLL;
714 mousemes->from = NULL;
715 mousemes->p_to = Message::MOUSE_RECEIVER;
716 mousemes->parameter = (0 & 0xFFFF) << 16 | (GET_WHEEL_DELTA_WPARAM(wparam) &0xFFFF);
717 mousemes->tag = (mpos.x & 0xFFFF) << 16 | (mpos.y & 0xFFFF);
718 messageQueue->postMessage(mousemes);
723 return DefWindowProc(wind, msg, wparam, lparam);
729 bool InitApp(HINSTANCE hinst,int cmdshow) {
733 wcs.style = CS_HREDRAW | CS_VREDRAW;
734 wcs.lpfnWndProc = WindowProc;
736 wcs.cbWndExtra = sizeof(DWORD);
737 wcs.hInstance = hinst;
740 wcs.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
741 wcs.lpszMenuName = NULL;
742 wcs.lpszClassName = "vomp";
743 acc=LoadAccelerators(hinst,MAKEINTRESOURCE(VOMPACCELERATOR));
744 if (!RegisterClass(&wcs))
746 flags =WS_VISIBLE | WS_POPUP | WS_CAPTION | WS_SYSMENU
747 |WS_MINIMIZEBOX | WS_SIZEBOX |WS_MAXIMIZEBOX;
748 RECT wnted={50,50,768+50,576+50};
749 AdjustWindowRect(&wnted,flags ,false);
750 win_main=CreateWindow("vomp","VOMP on Windows",flags, CW_USEDEFAULT,CW_USEDEFAULT,
751 wnted.right-wnted.left,wnted.bottom-wnted.top,NULL,NULL,hinst,NULL);
754 ShowWindow(win_main,SW_SHOWNORMAL);
755 UpdateWindow(win_main);
756 /* in order to handle letterboxing we use a child window */
758 child_wcs.style = CS_HREDRAW | CS_VREDRAW;
759 child_wcs.lpfnWndProc = WindowProc;
760 child_wcs.cbClsExtra = 0;
761 child_wcs.cbWndExtra = sizeof(DWORD);
762 child_wcs.hInstance = hinst;
763 child_wcs.hIcon = NULL;
764 child_wcs.hCursor = NULL;
765 child_wcs.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
766 child_wcs.lpszMenuName = NULL;
767 child_wcs.lpszClassName = "vomp_playback";
768 if (!RegisterClass(&child_wcs))
771 win=CreateWindow("vomp_playback","Vomp Playback Window",WS_VISIBLE | WS_CHILD |WS_CLIPCHILDREN,
772 0,0,768,576,win_main,NULL,hinst,NULL);
775 ShowWindow(win,SW_SHOWNORMAL);
777 if (remotefnc) {//at least windows XP
778 /* We want to support MCE Remote controls*/
779 RAWINPUTDEVICE remote_control_data[4];
780 ZeroMemory(remote_control_data,sizeof(remote_control_data));
781 remote_control_data[0].usUsagePage=0xFFBC;
782 remote_control_data[0].usUsage=0x88;
783 remote_control_data[0].dwFlags=0;
784 remote_control_data[1].usUsagePage=0x0C;
785 remote_control_data[1].usUsage=0x80;
786 remote_control_data[1].dwFlags=0;
787 remote_control_data[2].usUsagePage=0x0C;
788 remote_control_data[2].usUsage=0x01;
789 remote_control_data[2].dwFlags=0;
790 remote_control_data[3].usUsagePage=0x01;
791 remote_control_data[3].usUsage=0x80;
792 remote_control_data[3].dwFlags=0;
793 if (dynRegisterRawInputDevices(remote_control_data,4,sizeof(remote_control_data[0]))!=TRUE) {
794 MessageBox(0,"Registering remote control failed!","Aborting!",0);
806 // -------------------------------------------------------------------------------------------------------------------
808 void shutdown(int code)
810 if (control) // shut down control here in case views have posted messages
814 logger->log("Core", Log::NOTICE, "Control module shut down");
821 logger->log("Core", Log::NOTICE, "VDR module shut down");
828 logger->log("Core", Log::NOTICE, "OSD module shut down");
835 logger->log("Core", Log::NOTICE, "Audio module shut down");
842 logger->log("Core", Log::NOTICE, "Video module shut down");
849 logger->log("Core", Log::NOTICE, "Timers module shut down");
856 logger->log("Core", Log::NOTICE, "LED module shut down");
863 logger->log("Core", Log::NOTICE, "Remote module shut down");
868 logger->log("Core", Log::NOTICE, "Log module shutting down... bye!\n\n");
876 // -------------------------------------------------------------------------------------------------------------------
878 ULLONG htonll(ULLONG a)
880 return (((ULLONG)htonl((ULONG)((a<<32)>> 32))<<32)
881 |(ULONG)htonl(((ULONG) (a >> 32))));
884 ULLONG ntohll(ULLONG a)
889 std::string tp2str(const std::chrono::time_point<std::chrono::system_clock>& tp)
891 auto tms = std::chrono::time_point_cast<std::chrono::milliseconds>(tp);
892 std::chrono::milliseconds e = tms.time_since_epoch();
893 long long c = e.count();
894 time_t tt = c / 1000;
896 auto stm = std::localtime(&tt);
897 std::stringstream ss;
898 ss << std::put_time(stm, "%T") << "." << std::setfill('0') << std::setw(3) << ttm;
902 const std::string& getCommandLineServer()
904 return commandLineServer;