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"
39 #include "windowsosd.h"
43 #include "messagequeue.h"
45 void sighandler(int signalReceived);
46 void shutdown(int code);
48 // Global variables --------------------------------------------------------------------------------------------------
53 std::string commandLineServer; // NCONFIG
55 bool wnd_fullscreen=false;
56 bool wnd_topmost=false;
57 RECT wnd_fs_rect={20,20,768+20,576+20};
58 RECT wnd_fs_rect_client={0,0,768,576};
59 //OSVERSIONINFO windows_ver; //attempt to distigsh windows versions
65 typedef UINT (WINAPI *GETRAWINPUTDATAFNC) (HRAWINPUT,UINT,LPVOID,PUINT,UINT);
66 typedef UINT (WINAPI *REGISTERRAWINPUTDEVICEFNC) (PCRAWINPUTDEVICE,UINT,UINT);
68 GETRAWINPUTDATAFNC dynGetRawInputData=NULL;
69 REGISTERRAWINPUTDEVICEFNC dynRegisterRawInputDevices=NULL;
73 MessageQueue* messageQueue;
75 int getClockRealTime(struct timespec *tp){
79 GetSystemTime(&systime);
80 SystemTimeToFileTime(&systime, (FILETIME*)&filetime);
81 tp->tv_sec = (filetime - WINDOWS_TIME_BASE_OFFSET) / (10 * 1000 * 1000);
82 tp->tv_nsec = ((filetime - WINDOWS_TIME_BASE_OFFSET) % (10 * 1000 * 1000)) * 100;
86 DWORD WINAPI controlthreadStart(void *arg)
93 void LoadRemoteFunctions() {
94 user32dll=LoadLibrary("user32.dll");
95 if (user32dll!=NULL) {
96 dynGetRawInputData=(GETRAWINPUTDATAFNC)GetProcAddress(user32dll,"GetRawInputData");
97 if (dynGetRawInputData!=NULL) {
98 dynRegisterRawInputDevices=(REGISTERRAWINPUTDEVICEFNC)GetProcAddress(user32dll,"RegisterRawInputDevices");
99 if (dynRegisterRawInputDevices!=NULL) {
106 bool InitApp(HINSTANCE hinst,int cmdshow);
108 HWND win_main;//global window handle
109 HWND win;//global child window handle
112 #define ERROR_MSG(str) MessageBox(win_main,str,"Error!",MB_OK|MB_ICONWARNING)
113 INT WINAPI WinMain( HINSTANCE hinst , HINSTANCE previnst, LPSTR cmdline, int cmdshow)
116 //On Windows we have to init a window, we use DXUT
117 LoadRemoteFunctions();
118 if (!InitApp(hinst,cmdshow)) return false;
119 //Starting Network support
121 int result = WSAStartup(MAKEWORD(2,2),&wsadat);
122 if (result!=NO_ERROR) {
123 ERROR_MSG("Initialising WinSocked: Error at WSAStartup()\n");
127 result= CoInitializeEx(NULL,COINIT_MULTITHREADED );//Initialize COM for DirectShow
129 ERROR_MSG("Initialising COM: Error at Coinitialize()\n");
136 // Init global vars ------------------------------------------------------------------------------------------------
139 control = new Control();
141 if (!logger || !control)
143 ERROR_MSG("Could not create objects. Memory problems?\n");
149 messageQueue = control;
151 // Get logging module started --------------------------------------------------------------------------------------
153 if (!logger->init(Log::DEBUG, "vompwin.log", true))
155 ERROR_MSG("Could not initialise log object. Aborting.\n");
161 logger->log("Core", Log::INFO, "Starting up...");
164 // Init modules ----------------------------------------------------------------------------------------------------
167 dynamic_cast<WindowsOsd*>(osd)->setWindow(win);
169 success = control->init();
172 logger->log("Core", Log::INFO, "Control module initialised");
176 logger->log("Core", Log::EMERG, "Control module failed to initialise");
184 inputWin = InputMan::getInstance()->getInputWin();
185 Video::getInstance()->setDefaultAspect();
187 // Other init ------------------------------------------------------------------------------------------------------
189 logger->log("Core", Log::NOTICE, "Startup successful");
191 // Run main loop ---------------------------------------------------------------------------------------------------
193 // Ok, all major device components and other bits are loaded and ready
194 lastmousemove=timeGetTime();
196 HANDLE controlthread;
197 controlthread= CreateThread(NULL, 0, controlthreadStart, NULL,0,
200 message.message=WM_NULL;
202 while(run && WaitForSingleObject(controlthread,0)==WAIT_TIMEOUT) {
203 if (PeekMessage(&message, NULL, 0,0,PM_REMOVE)!=0) {
204 if (TranslateAccelerator(win_main,acc,&message)==NULL) {
205 TranslateMessage(&message);
206 DispatchMessage(&message);
207 switch (message.message) {
209 run=false; //TODO post exit to control Messages
213 //Render, moved to OSD !
215 //((OsdWin*)osd)->Render();
218 // When that returns quit ------------------------------------------------------------------------------------------
219 WaitForSingleObject(controlthread,INFINITE);
222 if (user32dll) FreeModule(user32dll);
227 bool TranslateMousePosition(POINT *pos) {
230 ScreenToClient(win,pos);
231 GetClientRect(win,&clientrect);
232 if (!PtInRect(&clientrect,*pos)) return false;//Don't pass it further
233 pos->x=((double)pos->x)/((double) (clientrect.right-clientrect.left))
234 *((double)Video::getInstance()->getScreenWidth());
235 pos->y=((double)pos->y)/((double) (clientrect.bottom-clientrect.top))
236 *((double)Video::getInstance()->getScreenHeight());
243 void CalculateWindowSize(RECT * size,ULONG size_mode) {
246 DWORD adjheight,adjwidth;
247 if (!wnd_fullscreen) {
248 DWORD flags =WS_VISIBLE | WS_POPUP | WS_CAPTION | WS_SYSMENU
249 |WS_MINIMIZEBOX | WS_SIZEBOX |WS_MAXIMIZEBOX;
250 RECT wnted={50,50,150,150};
251 AdjustWindowRect(&wnted,flags ,false);
252 adjwidth=-wnted.left+wnted.right-100;
253 adjheight=-wnted.top+wnted.bottom-100;
255 adjwidth=adjheight=0;
257 width=size->right-size->left-adjwidth;
258 height=size->bottom-size->top-adjheight;
259 UCHAR mode=Video::PAL;
260 UCHAR aspect=Video::ASPECT4X3;
261 UCHAR tvsize=Video::ASPECT4X3;
262 Video* video = Video::getInstance();
264 mode=video->getMode();
265 aspect=((VideoWin*)video)->getAspectRatio();
266 tvsize=((VideoWin*)video)->getPseudoTVsize();
269 double aspectrt=4./3.;
270 if (tvsize==Video::ASPECT16X9) {
271 if (aspect==Video::ASPECT16X9) {
272 aspectrt=4./3.; //looks strange, but it is a 16:9 tv
273 } else if (aspect==Video::ASPECT4X3) {
274 aspectrt=4./3./(16./9.)*(4./3.); //I hope this is correct
276 } else if (tvsize==Video::ASPECT4X3) {
277 if (aspect==Video::ASPECT16X9) {
278 if (mode!=Video::NORMAL) {
283 } else if (aspect==Video::ASPECT4X3) {
287 if (!wnd_fullscreen) {
290 case WMSZ_BOTTOMRIGHT:
293 width=(ULONG)(((double)height)*aspectrt);
294 size->right=size->left+width+adjwidth;
296 case WMSZ_BOTTOMLEFT:
298 width=(ULONG)(((double)height)*aspectrt);
299 size->left=size->right-width-adjwidth;
303 height=(ULONG)(((double)width)/aspectrt);
304 size->bottom=size->top+height+adjheight;
307 MoveWindow(win,0,0,width,height,TRUE);
309 RECT newrect={0,0,width,height};
311 if ((ULONG)(((double)height)*aspectrt)>width) {
312 newlength=(ULONG)(((double)width)/aspectrt);
313 newrect.top+=(height-newlength)/2;
314 newrect.bottom-=(height-newlength);
316 newlength=(ULONG)(((double)height)*aspectrt);
317 newrect.left+=(width-newlength)/2;
318 newrect.right-=(width-newlength);
320 MoveWindow(win,newrect.left,newrect.top,newrect.right,newrect.bottom,TRUE);
325 void AdjustWindow() {
326 if (!wnd_fullscreen) {
328 GetWindowRect(win_main,&winrect);
329 CalculateWindowSize(&winrect,WMSZ_BOTTOM);
330 MoveWindow(win_main,winrect.left,
331 winrect.top,winrect.right-winrect.left,winrect.bottom-winrect.top,true);
334 GetWindowRect(win_main,&winrect);
335 CalculateWindowSize(&winrect,WMSZ_BOTTOM);
340 void ToggleFullscreen() {
341 if (wnd_fullscreen) {
342 wnd_fullscreen=false;
343 SetWindowLong(win_main,GWL_STYLE,WS_VISIBLE | WS_POPUP | WS_CAPTION | WS_SYSMENU
344 |WS_MINIMIZEBOX | WS_SIZEBOX |WS_MAXIMIZEBOX);
345 SetWindowPos(win_main,NULL,0,0,0,0,SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
347 SetWindowPos(win_main,wnd_topmost?HWND_TOPMOST:HWND_TOP,wnd_fs_rect.left,wnd_fs_rect.top,
348 wnd_fs_rect.right-wnd_fs_rect.left,
349 wnd_fs_rect.bottom-wnd_fs_rect.top,
350 SWP_DRAWFRAME | SWP_FRAMECHANGED);
351 MoveWindow(win,wnd_fs_rect_client.left,wnd_fs_rect_client.top,
352 wnd_fs_rect_client.right-wnd_fs_rect_client.left,
353 wnd_fs_rect_client.bottom-wnd_fs_rect_client.top,TRUE);
356 GetWindowRect(win_main,&wnd_fs_rect);
357 GetWindowRect(win,&wnd_fs_rect_client);
358 SetWindowLong(win_main,GWL_STYLE,WS_VISIBLE | WS_POPUP );
359 SetWindowPos(win_main,NULL,0,0,0,0,SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
360 HMONITOR monitor=MonitorFromWindow(win_main,MONITOR_DEFAULTTONEAREST);
362 moninfo.cbSize=sizeof(moninfo);
364 if (!GetMonitorInfo(monitor,&moninfo)) return ;
365 SetWindowPos(win_main,wnd_topmost?HWND_TOPMOST:HWND_TOP,moninfo.rcMonitor.left,moninfo.rcMonitor.top,
366 moninfo.rcMonitor.right,moninfo.rcMonitor.bottom,SWP_FRAMECHANGED);
375 void ToggleTopmost() {
376 wnd_topmost=!wnd_topmost;
377 SetWindowPos(win_main,wnd_topmost?HWND_TOPMOST:HWND_NOTOPMOST,0,0,
378 0,0,SWP_NOMOVE | SWP_NOSIZE);
381 void CursorUpdate() {
383 GetCursorPos(&cursorpos);
385 asswind=WindowFromPoint(cursorpos);
386 if (asswind!=win_main && asswind!=win) {
387 return ; //not our responsibility
389 if ((timeGetTime()-lastmousemove)<4000 || cmenu) {
390 SetCursor(LoadCursor(NULL,IDC_ARROW));
396 bool ContextMenu(HWND wind,int x,int y) {
399 ScreenToClient(wind,&p);
400 GetClientRect(wind,&clientrect);
401 if (!PtInRect(&clientrect,p)) return false;
402 ClientToScreen(wind,&p);
405 menu=LoadMenu(hinstance,MAKEINTRESOURCE(VOMPMENU));
406 popup=GetSubMenu(menu,0);
407 if (wnd_fullscreen) {
408 CheckMenuItem(popup,VOMP_FULL_SCREEN,MF_BYCOMMAND|MF_CHECKED);
410 CheckMenuItem(popup,VOMP_FULL_SCREEN,MF_BYCOMMAND|MF_UNCHECKED);
413 CheckMenuItem(popup,VOMP_TOPMOST,MF_BYCOMMAND|MF_CHECKED);
415 CheckMenuItem(popup,VOMP_TOPMOST,MF_BYCOMMAND|MF_UNCHECKED);
418 TrackPopupMenu(popup,TPM_RIGHTBUTTON|TPM_LEFTALIGN,x,y,0,wind, NULL);
426 LONG FAR PASCAL WindowProc(HWND wind, UINT msg, WPARAM wparam, LPARAM lparam)
432 logger->log("Core", Log::NOTICE, "Window closed, shutting down...");
437 CalculateWindowSize((RECT*) lparam,wparam);
441 int width = LOWORD(lparam);
442 int height = HIWORD(lparam);
444 if (wparam == SIZE_MAXIMIZED) {
447 } /*else if (wparam == SIZE_MINIMIZED) {
457 if (GetUpdateRect(wind, &r, FALSE)) {
458 BeginPaint(wind, &ps);
459 //Call Painting Mechanism
464 if (inputWin->ReceiveButtonVK(wparam)) {
465 return 0L; //We process that Key
467 return DefWindowProc(wind, msg, wparam, lparam);
472 if (inputWin->ReceiveButtonCH(wparam)) {
473 return 0L; //We process that Key
475 return DefWindowProc(wind, msg, wparam, lparam);
480 if (inputWin->ReceiveButtonAP(GET_APPCOMMAND_LPARAM(lparam))){
481 return TRUE; //yes we process that message
483 return DefWindowProc(wind, msg, wparam, lparam);
492 dynGetRawInputData((HRAWINPUT)lparam,RID_INPUT,NULL,&risize,sizeof(RAWINPUTHEADER));
493 lpit=(LPRAWINPUT)malloc(risize);
494 dynGetRawInputData((HRAWINPUT)lparam,RID_INPUT,lpit,&risize,sizeof(RAWINPUTHEADER));
496 if (lpit->header.dwType==RIM_TYPEHID && lpit->data.hid.dwSizeHid>=2) {
497 DWORD button=lpit->data.hid.bRawData[1] | (lpit->data.hid.bRawData[0]<< 8);
498 if (inputWin->ReceiveButtonRI(button)){
500 return 0; //yes we process that message
505 return DefWindowProc(wind, msg, wparam, lparam);
510 if (LOWORD(wparam)==VOMP_FULL_SCREEN) {
514 if (LOWORD(wparam)==VOMP_TOPMOST) {
518 if (inputWin->ReceiveButtonAP(LOWORD(wparam))){
519 return 0; //yes we process that message
521 return DefWindowProc(wind, msg, wparam, lparam);
526 if (((HANDLE)wparam)==win) {
530 return DefWindowProc(wind, msg, wparam, lparam);
534 if (wparam==SC_MAXIMIZE) {
537 } else if (wparam==SC_SCREENSAVE || wparam==SC_MONITORPOWER) {
540 return DefWindowProc(wind,msg,wparam, lparam);
544 if (!ContextMenu(wind,GET_X_LPARAM(lparam),GET_Y_LPARAM(lparam))) {
545 return DefWindowProc(wind, msg, wparam, lparam);
551 if (wparam==VOMP_CURSORUPDATE) {
555 return DefWindowProc(wind, msg, wparam, lparam);
561 // data = wheel delta
565 lastmousemove = timeGetTime();
566 SetCursor(LoadCursor(NULL,IDC_ARROW));
567 SetTimer(wind, VOMP_CURSORUPDATE, 4500, NULL);
568 POINT mpos = { GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam) };
569 ClientToScreen(wind, &mpos);
570 if (TranslateMousePosition(&mpos))
572 Message* mousemes = new Message();
573 mousemes->message = Message::MOUSE_MOVE;
574 mousemes->from = NULL;
575 mousemes->p_to = Message::MOUSE_RECEIVER;
576 mousemes->parameter = mpos.x;
577 mousemes->tag = mpos.y;
579 //mousemes->parameter = (mpos.x & 0xFFFF) << 16 | (mpos.y & 0xFFFF);
581 messageQueue->postMessage(mousemes);
583 //return DefWindowProc(wind,msg,wparam, lparam);
588 POINT mpos = { GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam) };
589 ClientToScreen(wind, &mpos);
590 if (TranslateMousePosition(&mpos))
592 Message* mousemes = new Message();
593 mousemes->message = Message::MOUSE_LBDOWN;
594 mousemes->from = NULL;
595 mousemes->p_to = Message::MOUSE_RECEIVER;
596 mousemes->parameter = mpos.x;
597 mousemes->tag = mpos.y;
599 //mousemes->parameter = (mpos.x & 0xFFFF) << 16 | (mpos.y & 0xFFFF);
601 messageQueue->postMessage(mousemes);
607 POINT mpos = { GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam) };
608 ClientToScreen(wind, &mpos);
609 if (TranslateMousePosition(&mpos))
611 Message* mousemes = new Message();
612 mousemes->message = Message::MOUSE_SCROLL;
613 mousemes->from = NULL;
614 mousemes->p_to = Message::MOUSE_RECEIVER;
615 mousemes->parameter = mpos.x;
616 mousemes->tag = mpos.y;
617 mousemes->data = GET_WHEEL_DELTA_WPARAM(wparam) & 0xFFFF; // filter prob not necessary
619 //mousemes->parameter = (0 & 0xFFFF) << 16 | (GET_WHEEL_DELTA_WPARAM(wparam) & 0xFFFF);
620 //mousemes->tag = (mpos.x & 0xFFFF) << 16 | (mpos.y & 0xFFFF);
621 messageQueue->postMessage(mousemes);
628 return DefWindowProc(wind, msg, wparam, lparam);
635 bool InitApp(HINSTANCE hinst,int cmdshow) {
639 wcs.style = CS_HREDRAW | CS_VREDRAW;
640 wcs.lpfnWndProc = WindowProc;
642 wcs.cbWndExtra = sizeof(DWORD);
643 wcs.hInstance = hinst;
646 wcs.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
647 wcs.lpszMenuName = NULL;
648 wcs.lpszClassName = "vomp";
649 acc=LoadAccelerators(hinst,MAKEINTRESOURCE(VOMPACCELERATOR));
650 if (!RegisterClass(&wcs))
652 flags =WS_VISIBLE | WS_POPUP | WS_CAPTION | WS_SYSMENU
653 |WS_MINIMIZEBOX | WS_SIZEBOX |WS_MAXIMIZEBOX;
654 RECT wnted={50,50,768+50,576+50};
655 AdjustWindowRect(&wnted,flags ,false);
656 win_main=CreateWindow("vomp","VOMP on Windows",flags, CW_USEDEFAULT,CW_USEDEFAULT,
657 wnted.right-wnted.left,wnted.bottom-wnted.top,NULL,NULL,hinst,NULL);
660 ShowWindow(win_main,SW_SHOWNORMAL);
661 UpdateWindow(win_main);
662 /* in order to handle letterboxing we use a child window */
664 child_wcs.style = CS_HREDRAW | CS_VREDRAW;
665 child_wcs.lpfnWndProc = WindowProc;
666 child_wcs.cbClsExtra = 0;
667 child_wcs.cbWndExtra = sizeof(DWORD);
668 child_wcs.hInstance = hinst;
669 child_wcs.hIcon = NULL;
670 child_wcs.hCursor = NULL;
671 child_wcs.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
672 child_wcs.lpszMenuName = NULL;
673 child_wcs.lpszClassName = "vomp_playback";
674 if (!RegisterClass(&child_wcs))
677 win=CreateWindow("vomp_playback","Vomp Playback Window",WS_VISIBLE | WS_CHILD |WS_CLIPCHILDREN,
678 0,0,768,576,win_main,NULL,hinst,NULL);
681 ShowWindow(win,SW_SHOWNORMAL);
683 if (remotefnc) {//at least windows XP
684 /* We want to support MCE Remote controls*/
685 RAWINPUTDEVICE remote_control_data[4];
686 ZeroMemory(remote_control_data,sizeof(remote_control_data));
687 remote_control_data[0].usUsagePage=0xFFBC;
688 remote_control_data[0].usUsage=0x88;
689 remote_control_data[0].dwFlags=0;
690 remote_control_data[1].usUsagePage=0x0C;
691 remote_control_data[1].usUsage=0x80;
692 remote_control_data[1].dwFlags=0;
693 remote_control_data[2].usUsagePage=0x0C;
694 remote_control_data[2].usUsage=0x01;
695 remote_control_data[2].dwFlags=0;
696 remote_control_data[3].usUsagePage=0x01;
697 remote_control_data[3].usUsage=0x80;
698 remote_control_data[3].dwFlags=0;
699 if (dynRegisterRawInputDevices(remote_control_data,4,sizeof(remote_control_data[0]))!=TRUE) {
700 MessageBox(0,"Registering remote control failed!","Aborting!",0);
712 // -------------------------------------------------------------------------------------------------------------------
714 void shutdown(int code)
716 if (control) // shut down control here in case views have posted messages
720 logger->log("Core", Log::NOTICE, "Control module shut down");
725 logger->log("Core", Log::NOTICE, "Log module shutting down... bye!\n\n");
733 // -------------------------------------------------------------------------------------------------------------------
735 const std::string& getCommandLineServer()
737 return commandLineServer;