video.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964
  1. #include <windows.h>
  2. #include <ddraw.h>
  3. #include "main.h"
  4. #include "video.h"
  5. #include "subtitles.h"
  6. #include "resource.h"
  7. #undef GetSystemMetrics
  8. #define OSD_ENABLED 1
  9. #define INIT_DIRECTDRAW_STRUCT(x) (ZeroMemory(&x, sizeof(x)), x.dwSize=sizeof(x))
  10. #define OV_COL_R 16
  11. #define OV_COL_G 0
  12. #define OV_COL_B 16
  13. #define OSD_TEXT_SIZE 28
  14. #define OSD_TEXT_R 192
  15. #define OSD_TEXT_G 192
  16. #define OSD_TEXT_B 192
  17. #define OSD_TEXT_R_HILITE 255
  18. #define OSD_TEXT_G_HILITE 255
  19. #define OSD_TEXT_B_HILITE 255
  20. #define OSD_VOL_COL_R 0
  21. #define OSD_VOL_COL_G 0
  22. #define OSD_VOL_COL_B 192
  23. #define OSD_VOL_BKCOL_R 0
  24. #define OSD_VOL_BKCOL_G 0
  25. #define OSD_VOL_BKCOL_B 64
  26. #define TIMER_OSD_ID 1234
  27. #define CTRLTYPE_SYMBOL 0
  28. #define CTRLTYPE_TEXT 1
  29. #define CTRLTYPE_PROGRESS 2
  30. #define CTRLTYPE_SPACER 3
  31. #define CTRL_PROGRESSTEXT 0
  32. #define CTRL_PROGRESS 1
  33. #define CTRL_PROGRESSSPACER 2
  34. #define CTRL_REW 3
  35. #define CTRL_PLAY 4
  36. #define CTRL_PAUSE 5
  37. #define CTRL_STOP 6
  38. #define CTRL_FFWD 7
  39. #define CTRL_VOLSPACER 8
  40. #define CTRL_VOLTEXT 9
  41. #define CTRL_VOL 10
  42. int g_ctrl_type[NUM_WIDGETS] = {
  43. CTRLTYPE_TEXT,
  44. CTRLTYPE_PROGRESS,
  45. CTRLTYPE_SPACER,
  46. CTRLTYPE_SYMBOL,
  47. CTRLTYPE_SYMBOL,
  48. CTRLTYPE_SYMBOL,
  49. CTRLTYPE_SYMBOL,
  50. CTRLTYPE_SYMBOL,
  51. CTRLTYPE_SPACER,
  52. CTRLTYPE_TEXT,
  53. CTRLTYPE_PROGRESS
  54. };
  55. const char *g_ctrl_text[NUM_WIDGETS] = {
  56. "Progress ",
  57. "",
  58. "",
  59. "7", // rew
  60. "4", // play
  61. ";", // pause
  62. "<", // stop
  63. "8", // ffwd
  64. "",
  65. "Volume ",
  66. ""
  67. };
  68. int g_ctrl_force_width[NUM_WIDGETS] = {
  69. 0,
  70. 96, // progress bar width
  71. 32, // spacer width
  72. 0, // rew
  73. 0, // play
  74. 0, // pause
  75. 0, // stop
  76. 0, // ffwd
  77. 32, // spacer width
  78. 0,
  79. 64 // volume bar width
  80. };
  81. extern HINSTANCE g_hInstance;
  82. extern int g_bitmap_id;
  83. static BOOL WINAPI DDEnumCallbackEx(GUID FAR *lpGUID, LPSTR lpDriverDescription, LPSTR lpDriverName, LPVOID lpContext, HMONITOR hm) {
  84. VideoOutputChild *ovo=(VideoOutputChild *)lpContext;
  85. if(ovo->m_found_devguid) return 1;
  86. if(hm==ovo->m_monitor_to_find) {
  87. ovo->m_devguid=*lpGUID;
  88. ovo->m_found_devguid=1;
  89. }
  90. return 1;
  91. }
  92. void VideoOutputChild::update_monitor_coords(VideoOutput *parent)
  93. {
  94. //find the correct monitor if multiple monitor support is present
  95. HWND hwnd=parent->getHwnd();
  96. m_found_devguid=0;
  97. m_mon_x=0;
  98. m_mon_y=0;
  99. HINSTANCE h=LoadLibrary("user32.dll");
  100. if (h) {
  101. HMONITOR (WINAPI *Mfp)(POINT pt, DWORD dwFlags) = (HMONITOR (WINAPI *)(POINT,DWORD)) GetProcAddress(h,"MonitorFromPoint");
  102. HMONITOR (WINAPI *Mfr)(LPCRECT lpcr, DWORD dwFlags) = (HMONITOR (WINAPI *)(LPCRECT, DWORD)) GetProcAddress(h, "MonitorFromRect");
  103. HMONITOR (WINAPI *Mfw)(HWND wnd, DWORD dwFlags)=(HMONITOR (WINAPI *)(HWND, DWORD)) GetProcAddress(h, "MonitorFromWindow");
  104. BOOL (WINAPI *Gmi)(HMONITOR mon, LPMONITORINFO lpmi) = (BOOL (WINAPI *)(HMONITOR,LPMONITORINFO)) GetProcAddress(h,"GetMonitorInfoA");
  105. if (Mfp && Mfr && Mfw && Gmi) {
  106. RECT r;
  107. GetWindowRect(hwnd,&r);
  108. HMONITOR hm=Mfr(&r,NULL);
  109. if(hm) {
  110. HINSTANCE hdd = LoadLibrary("ddraw.dll");
  111. if(hdd) {
  112. typedef BOOL (FAR PASCAL * LPDDENUMCALLBACKEXA)(GUID FAR *, LPSTR, LPSTR, LPVOID, HMONITOR);
  113. typedef HRESULT (WINAPI * LPDIRECTDRAWENUMERATEEX)( LPDDENUMCALLBACKEXA lpCallback, LPVOID lpContext, DWORD dwFlags);
  114. LPDIRECTDRAWENUMERATEEX lpDDEnumEx;
  115. lpDDEnumEx = (LPDIRECTDRAWENUMERATEEX) GetProcAddress(hdd,"DirectDrawEnumerateExA");
  116. if (lpDDEnumEx) {
  117. m_monitor_to_find=hm;
  118. lpDDEnumEx(&DDEnumCallbackEx, this, DDENUM_ATTACHEDSECONDARYDEVICES|DDENUM_NONDISPLAYDEVICES);
  119. if(m_found_devguid) {
  120. MONITORINFOEX mi;
  121. memset(&mi,0,sizeof(mi));
  122. mi.cbSize=sizeof(mi);
  123. if (Gmi(hm,&mi)) {
  124. m_mon_x=mi.rcMonitor.left;
  125. m_mon_y=mi.rcMonitor.top;
  126. }
  127. }
  128. }
  129. FreeLibrary(hdd);
  130. }
  131. }
  132. }
  133. FreeLibrary(h);
  134. }
  135. }
  136. int VideoOutput::get_latency()
  137. {
  138. return vid_vsync?15:0;
  139. }
  140. #undef GetSystemMetrics
  141. int VideoOutput::class_refcnt=0;
  142. void VideoOutput::adjustAspect(RECT &rd)
  143. {
  144. if (vid_aspectadj)
  145. {
  146. int outh=rd.bottom-rd.top;
  147. int outw=rd.right-rd.left;
  148. int newh=(int)((aspect*height*outw)/(double)width);
  149. int neww=(int)((width*outh)/(height*aspect));
  150. if (outh > newh) // black bars on top and bottom
  151. {
  152. int d=outh - newh;
  153. rd.top+=d/2;
  154. rd.bottom-=d-d/2;
  155. }
  156. else if (outw > neww) // black bars on left and right
  157. {
  158. int d=outw - neww;
  159. rd.left+=d/2;
  160. rd.right-=d-d/2;
  161. }
  162. }
  163. }
  164. LRESULT CALLBACK VideoOutput::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  165. {
  166. if (uMsg == WM_CREATE)
  167. {
  168. SetWindowLong(hwnd,GWL_USERDATA,(long)((CREATESTRUCT *)lParam)->lpCreateParams);
  169. ShowWindow(hwnd,SW_SHOW);
  170. if (GetParent(hwnd))
  171. {
  172. RECT r;
  173. GetClientRect(GetParent(hwnd),&r);
  174. SetWindowPos(hwnd,NULL,0,0,
  175. r.right,
  176. r.bottom,
  177. SWP_NOACTIVATE|SWP_NOZORDER);
  178. }
  179. return 0;
  180. }
  181. VideoOutput *_This=(VideoOutput*)GetWindowLong(hwnd,GWL_USERDATA);
  182. if (_This) return _This->WindowProc(hwnd,uMsg,wParam,lParam);
  183. else return DefWindowProc(hwnd,uMsg,wParam,lParam);
  184. }
  185. void VideoOutput::notifyBufferState(int bufferstate) /* 0-255*/
  186. {
  187. m_bufferstate=bufferstate;
  188. #ifdef ACTIVEX_CONTROL
  189. PostMessage( video_hwnd, STATUS_MSG, STATUS_PREBUFFER, bufferstate );
  190. #endif
  191. if (!m_video_output) {
  192. if(GetTickCount()-m_lastbufinvalid>500) {
  193. InvalidateRect(video_hwnd,NULL,FALSE);
  194. m_lastbufinvalid=GetTickCount();
  195. }
  196. }
  197. }
  198. LRESULT VideoOutput::WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  199. {
  200. switch (uMsg)
  201. {
  202. case WM_TIMER:
  203. case WM_WINDOWPOSCHANGING:
  204. case WM_WINDOWPOSCHANGED:
  205. case WM_SIZE:
  206. case WM_MOVE:
  207. case WM_MOVING:
  208. if (uMsg == WM_TIMER && wParam == TIMER_OSD_ID) {
  209. hideOSD();
  210. return 0;
  211. }
  212. EnterCriticalSection(&m_cs);
  213. if(m_video_output) m_video_output->timerCallback();
  214. LeaveCriticalSection(&m_cs);
  215. if (uMsg == WM_TIMER) return 0;
  216. break;
  217. case WM_LBUTTONDOWN:
  218. if(is_fs)
  219. osdHitTest(LOWORD(lParam),HIWORD(lParam),0);
  220. #ifdef ACTIVEX_CONTROL
  221. SendMessage( video_hwnd, STATUS_MSG, STATUS_MOUSEPRESS, 1 );
  222. #endif
  223. break;
  224. case WM_PAINT:
  225. {
  226. if (m_video_output && m_video_output->onPaint(hwnd,(HDC)wParam)) return 0;
  227. if (m_logo && !m_video_output)
  228. {
  229. PAINTSTRUCT p;
  230. BeginPaint(hwnd,&p);
  231. RECT r;
  232. GetClientRect(hwnd,&r);
  233. HDC out=p.hdc;
  234. HDC dc=CreateCompatibleDC(NULL);
  235. SelectObject(dc,m_logo);
  236. int xp=(r.right-r.left-m_logo_w)/2;
  237. int yp=(r.bottom-r.top-m_logo_h)/2;
  238. BitBlt(out,xp,yp,m_logo_w,m_logo_h,dc,0,0,SRCCOPY);
  239. int bs=m_bufferstate;
  240. if (bs < 16) bs=16;
  241. HGDIOBJ oldobj1=SelectObject(out,CreateSolidBrush(RGB(0,0,0)));
  242. HGDIOBJ oldobj2=SelectObject(out,CreatePen(PS_SOLID,0,RGB(0,0,0)));
  243. Rectangle(out,r.left,r.top,r.right,yp);
  244. if (m_statusmsg)
  245. Rectangle(out,r.left,yp+m_logo_h,r.right,r.bottom);
  246. else
  247. {
  248. Rectangle(out,r.left,yp+m_logo_h+2+9,r.right,r.bottom);
  249. Rectangle(out,xp + ((bs * (m_logo_w+2))>>8),yp+m_logo_h+2,r.right, yp+9+m_logo_h+2);
  250. }
  251. Rectangle(out,r.left,yp,xp-1,yp+m_logo_h+9+2);
  252. Rectangle(out,xp+m_logo_w+1,yp,r.right,yp+m_logo_h+2);
  253. DeleteObject(SelectObject(out,oldobj2));
  254. DeleteObject(SelectObject(out,oldobj1));
  255. if (m_statusmsg)
  256. {
  257. RECT subr={0,yp+m_logo_h+2,r.right,r.bottom};
  258. SetTextColor(out,RGB(255,255,255));
  259. SetBkMode(out,TRANSPARENT);
  260. DrawText(out,m_statusmsg,-1,&subr,DT_TOP|DT_CENTER|DT_NOCLIP|DT_NOPREFIX);
  261. }
  262. else
  263. {
  264. yp+=m_logo_h+2;
  265. if (bs)
  266. {
  267. HGDIOBJ oldobj1=SelectObject(out,CreateSolidBrush(RGB(128,128,128)));
  268. HGDIOBJ oldobj2=SelectObject(out,CreatePen(PS_SOLID,0,RGB(255,255,255)));
  269. Rectangle(out,xp-1,yp,xp + ((bs * (m_logo_w+2))>>8), yp+9);
  270. DeleteObject(SelectObject(out,oldobj2));
  271. DeleteObject(SelectObject(out,oldobj1));
  272. }
  273. }
  274. DeleteDC(dc);
  275. EndPaint(hwnd,&p);
  276. return 0;
  277. }
  278. }
  279. break;
  280. case WM_USER+0x1:
  281. m_need_change=1;
  282. break;
  283. #ifdef ACTIVEX_CONTROL
  284. case STATUS_MSG:
  285. SendStatus( wParam, lParam );
  286. break;
  287. #endif
  288. case WM_KEYDOWN:
  289. if(wParam==27 && is_fs) remove_fullscreen();
  290. break;
  291. case WM_MOUSEMOVE:
  292. if(is_fs) {
  293. if (ignore_mousemove_count>0) {
  294. ignore_mousemove_count--;
  295. }
  296. else if (abs(osdLastMouseX - LOWORD(lParam)) + abs(osdLastMouseY - HIWORD(lParam)) > 1) {
  297. KillTimer(hwnd, TIMER_OSD_ID);
  298. showOSD();
  299. SetTimer(hwnd, TIMER_OSD_ID, 2000, NULL);
  300. if (wParam & MK_LBUTTON)
  301. osdHitTest(LOWORD(lParam),HIWORD(lParam),1);
  302. else
  303. osdHitTest(LOWORD(lParam),HIWORD(lParam),-1);
  304. }
  305. osdLastMouseX = LOWORD(lParam);
  306. osdLastMouseY = HIWORD(lParam);
  307. }
  308. break;
  309. }
  310. if (m_msgcallback)
  311. {
  312. return m_msgcallback(m_msgcallback_tok,hwnd, uMsg, wParam, lParam);
  313. }
  314. return (DefWindowProc(hwnd, uMsg, wParam, lParam));
  315. }
  316. VideoOutput::VideoOutput(HWND parent_hwnd, int initxpos, int initypos)
  317. {
  318. curSubtitle=NULL;
  319. m_statusmsg=0;
  320. m_bufferstate=0;
  321. m_msgcallback=0;
  322. m_msgcallback_tok=0;
  323. video_hwnd=video_parent_hwnd=0;
  324. decoder=0;
  325. vid_aspectadj=true;
  326. vid_overlays=true;
  327. vid_ddraw=true;
  328. vid_vsync=true;
  329. aspect=1.0;
  330. m_need_change=false;
  331. width=height=flip=uyvy_output=yuy2_output=is_fs=ignore_mousemove_count=show_osd=0;
  332. oldfsparent=0;
  333. memset(&oldfsrect,0,sizeof(oldfsrect));
  334. memset(&lastfsrect,0,sizeof(lastfsrect));
  335. oldfsstyle=0;
  336. m_video_output=NULL;
  337. osdFontText=NULL;
  338. osdFontSymbol=NULL;
  339. osdProgressBrushBg=NULL;
  340. osdProgressBrushFg=NULL;
  341. osdProgressPenBg=NULL;
  342. osdProgressPenFg=NULL;
  343. osdProgressPenBgHilite=NULL;
  344. osdBlackBrush=NULL;
  345. osdMemDC=NULL;
  346. osdMemBM=NULL;
  347. osdOldBM=NULL;
  348. osdMemBMW=0;
  349. osdMemBMH=0;
  350. osdLastMouseX=-1;
  351. osdLastMouseY=-1;
  352. for (int i=0; i<NUM_WIDGETS; i++)
  353. SetRect(&ctrlrect[i], 0, 0, 0, 0);
  354. ctrlrects_ready = 0;
  355. resetSubtitle();
  356. WNDCLASS wc={0,};
  357. wc.hCursor=LoadCursor(NULL,IDC_ARROW);
  358. wc.lpfnWndProc = WndProc;
  359. wc.hInstance = GetModuleHandle(NULL);
  360. wc.lpszClassName = "NSVplay";
  361. LOGBRUSH lb={BS_SOLID,RGB(OV_COL_R,OV_COL_G,OV_COL_B),};
  362. wc.hbrBackground=CreateBrushIndirect(&lb);
  363. if (!class_refcnt) RegisterClass(&wc);
  364. class_refcnt++;
  365. m_logo=(HBITMAP)LoadImage(g_hInstance,MAKEINTRESOURCE(g_bitmap_id),IMAGE_BITMAP,0,0,LR_CREATEDIBSECTION);
  366. BITMAP bm;
  367. GetObject(m_logo, sizeof(BITMAP), &bm);
  368. m_logo_w=bm.bmWidth;
  369. m_logo_h=bm.bmHeight;
  370. if(m_logo_h<0) m_logo_h=-m_logo_h;
  371. InitializeCriticalSection(&m_cs);
  372. video_hwnd=CreateWindowEx(0,wc.lpszClassName, "NSV Player",parent_hwnd?WS_CHILD:(WS_OVERLAPPEDWINDOW&(~WS_MAXIMIZEBOX)),
  373. initxpos,initypos,320,200,
  374. parent_hwnd, NULL,wc.hInstance,(void*)this);
  375. video_parent_hwnd=parent_hwnd;
  376. m_lastbufinvalid=0;
  377. #ifdef ACTIVEX_CONTROL
  378. m_firstframe = 1;
  379. #endif
  380. }
  381. VideoOutputChild *VideoOutput::createVideoOutput(int n) {
  382. if(!vid_overlays && !vid_ddraw) vid_overlays=true;
  383. if(!vid_overlays) n++;
  384. if(n==0) return new OverlayVideoOutput();
  385. if(!vid_ddraw) n++;
  386. if(n==1) return new DDrawVideoOutput();
  387. return 0;
  388. }
  389. int VideoOutput::open(int w, int h, int vflip, double aspectratio, unsigned int fmt)
  390. {
  391. EnterCriticalSection(&m_cs);
  392. delete(m_video_output);
  393. m_video_output=NULL;
  394. if (!w) w=320;
  395. if (!h) h=240;
  396. width=w;
  397. height=h;
  398. flip=vflip;
  399. type=fmt;
  400. is_fs=0;
  401. ignore_mousemove_count=0;
  402. show_osd=0;
  403. aspect=aspectratio;
  404. for(int i=0;m_video_output=createVideoOutput(i);i++) {
  405. if(m_video_output->create(this,w,h,fmt,vflip,aspectratio)) {
  406. LeaveCriticalSection(&m_cs);
  407. if (!GetParent(video_hwnd)) {
  408. RECT r,r2;
  409. int ow=width,oh=height;
  410. if (aspect > 0.001)
  411. {
  412. if (aspect < 1.0) ow=(int)(ow/aspect);
  413. else oh=(int)(oh*aspect);
  414. }
  415. GetWindowRect(video_hwnd,&r);
  416. GetClientRect(video_hwnd,&r2);
  417. SetWindowPos(video_hwnd,NULL,0,0,
  418. ow+(r.right-r.left)-(r2.right-r2.left),
  419. oh+(r.bottom-r.top)-(r2.bottom-r2.top),
  420. SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER);
  421. }
  422. return 0;
  423. }
  424. delete(m_video_output);
  425. }
  426. LeaveCriticalSection(&m_cs);
  427. return 1;
  428. }
  429. void VideoOutput::draw(void *frame)
  430. {
  431. if (!m_video_output || !frame) return;
  432. if ((m_video_output && m_video_output->needChange()) || m_need_change) {
  433. open(width,height,flip,aspect,type);
  434. m_need_change=0;
  435. }
  436. #ifdef ACTIVEX_CONTROL
  437. if ( m_firstframe ) {
  438. m_firstframe = 0;
  439. PostMessage( video_hwnd, STATUS_MSG, STATUS_FIRSTFRAME, 1 );
  440. }
  441. #endif
  442. if (m_video_output) m_video_output->displayFrame((const char *)frame,0,0);
  443. }
  444. VideoOutput::~VideoOutput()
  445. {
  446. free(m_statusmsg);
  447. delete(m_video_output);
  448. DestroyWindow(video_hwnd);
  449. if (!--class_refcnt) UnregisterClass("NSVplay",GetModuleHandle(NULL));
  450. if(osdFontText) DeleteObject(osdFontText);
  451. if(osdFontSymbol) DeleteObject(osdFontSymbol);
  452. if(osdProgressBrushBg) DeleteObject(osdProgressBrushBg);
  453. if(osdProgressBrushFg) DeleteObject(osdProgressBrushFg);
  454. if(osdBlackBrush ) DeleteObject(osdBlackBrush );
  455. if(osdProgressPenBg ) DeleteObject(osdProgressPenBg );
  456. if(osdProgressPenFg ) DeleteObject(osdProgressPenFg );
  457. if(osdProgressPenBgHilite) DeleteObject(osdProgressPenBgHilite);
  458. if(osdMemDC) {
  459. SelectObject(osdMemDC,osdOldBM); // delete our doublebuffer
  460. DeleteDC(osdMemDC);
  461. }
  462. if(osdMemBM) DeleteObject(osdMemBM);
  463. DeleteCriticalSection(&m_cs);
  464. }
  465. void VideoOutput::close()
  466. {
  467. delete(m_video_output);
  468. m_video_output=NULL;
  469. }
  470. void VideoOutput::getViewport(RECT *r, HWND wnd, int full) {
  471. POINT *p=NULL;
  472. RECT *sr=NULL;
  473. if (p || sr || wnd) {
  474. HINSTANCE h=LoadLibrary("user32.dll");
  475. if (h) {
  476. HMONITOR (WINAPI *Mfp)(POINT pt, DWORD dwFlags) = (HMONITOR (WINAPI *)(POINT,DWORD)) GetProcAddress(h,"MonitorFromPoint");
  477. HMONITOR (WINAPI *Mfr)(LPCRECT lpcr, DWORD dwFlags) = (HMONITOR (WINAPI *)(LPCRECT, DWORD)) GetProcAddress(h, "MonitorFromRect");
  478. HMONITOR (WINAPI *Mfw)(HWND wnd, DWORD dwFlags)=(HMONITOR (WINAPI *)(HWND, DWORD)) GetProcAddress(h, "MonitorFromWindow");
  479. BOOL (WINAPI *Gmi)(HMONITOR mon, LPMONITORINFO lpmi) = (BOOL (WINAPI *)(HMONITOR,LPMONITORINFO)) GetProcAddress(h,"GetMonitorInfoA");
  480. if (Mfp && Mfr && Mfw && Gmi) {
  481. HMONITOR hm = NULL;
  482. if (p)
  483. hm=Mfp(*p,MONITOR_DEFAULTTONULL);
  484. else if (sr)
  485. hm=Mfr(sr,MONITOR_DEFAULTTONULL);
  486. else if (wnd)
  487. hm=Mfw(wnd,MONITOR_DEFAULTTONULL);
  488. if (hm) {
  489. MONITORINFOEX mi;
  490. memset(&mi,0,sizeof(mi));
  491. mi.cbSize=sizeof(mi);
  492. if (Gmi(hm,&mi)) {
  493. if(!full) *r=mi.rcWork;
  494. else *r=mi.rcMonitor;
  495. FreeLibrary(h);
  496. return;
  497. }
  498. }
  499. }
  500. FreeLibrary(h);
  501. }
  502. }
  503. if (full)
  504. { // this might be borked =)
  505. r->top=r->left=0;
  506. r->right=::GetSystemMetrics(SM_CXSCREEN);
  507. r->bottom=::GetSystemMetrics(SM_CYSCREEN);
  508. }
  509. else
  510. {
  511. SystemParametersInfo(SPI_GETWORKAREA,0,r,0);
  512. }
  513. }
  514. void VideoOutput::fullscreen()
  515. {
  516. if (is_fs) return;
  517. if(!m_video_output) return;
  518. is_fs=1;
  519. ignore_mousemove_count=2;
  520. oldfsparent=GetParent(video_hwnd);
  521. oldfsstyle=GetWindowLong(video_hwnd,GWL_STYLE);
  522. if (!oldfsparent) GetWindowRect(video_hwnd,&oldfsrect);
  523. else GetClientRect(video_hwnd,&oldfsrect);
  524. getViewport(&lastfsrect,video_hwnd,1);
  525. SetParent(video_hwnd,NULL);
  526. SetWindowLong(video_hwnd,GWL_STYLE,WS_POPUP|WS_VISIBLE);
  527. SetWindowPos(video_hwnd, HWND_TOPMOST, lastfsrect.left, lastfsrect.top, lastfsrect.right-lastfsrect.left, lastfsrect.bottom-lastfsrect.top, SWP_DRAWFRAME);
  528. SetFocus(video_hwnd);
  529. resetSubtitle();
  530. //showOSD();
  531. //SetCursor(NULL);
  532. }
  533. void VideoOutput::getOutputSize(int *w, int *h)
  534. {
  535. RECT r2;
  536. GetClientRect(video_hwnd,&r2);
  537. *w=r2.right-r2.left;
  538. *h=r2.bottom-r2.top;
  539. }
  540. void VideoOutput::setOutputSize(int w, int h)
  541. {
  542. RECT r,r2;
  543. GetWindowRect(video_hwnd,&r);
  544. GetClientRect(video_hwnd,&r2);
  545. SetWindowPos(video_hwnd, 0, 0,0,
  546. w+(r.right-r.left)-(r2.right-r2.left),
  547. h+(r.bottom-r.top)-(r2.bottom-r2.top),
  548. SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE);
  549. }
  550. void VideoOutput::remove_fullscreen()
  551. {
  552. if(!is_fs) return;
  553. SetParent(video_hwnd,oldfsparent);
  554. SetWindowLong(video_hwnd,GWL_STYLE,oldfsstyle);
  555. // note: when returning from fullscreen *on a secondary monitor*,
  556. // be careful how you set the new window Z order.
  557. // nsvplay.exe: only HWND_NOTOPMOST works
  558. // nsvplayX.exe: only HWND_TOP works
  559. SetWindowPos(video_hwnd, oldfsparent ? HWND_TOP : HWND_NOTOPMOST, oldfsrect.left, oldfsrect.top, oldfsrect.right-oldfsrect.left, oldfsrect.bottom-oldfsrect.top, SWP_FRAMECHANGED);
  560. SetFocus(oldfsparent ? oldfsparent : video_hwnd);
  561. is_fs=0;
  562. show_osd=0;
  563. ctrlrects_ready=0;
  564. resetSubtitle();
  565. hideOSD();
  566. }
  567. int VideoOutput::is_fullscreen()
  568. {
  569. return is_fs;
  570. }
  571. void VideoOutput::showStatusMsg(const char *text)
  572. {
  573. m_statusmsg=_strdup(text);
  574. InvalidateRect(video_hwnd,NULL,TRUE);
  575. }
  576. void VideoOutput::drawSubtitle(SubsItem *item)
  577. {
  578. if(!item) {
  579. if(curSubtitle) {
  580. m_video_output->drawSubtitle(NULL);
  581. curSubtitle=NULL;
  582. }
  583. return;
  584. }
  585. if(curSubtitle==item) return;
  586. curSubtitle=item;
  587. m_video_output->drawSubtitle(curSubtitle);
  588. }
  589. void VideoOutput::resetSubtitle()
  590. {
  591. curSubtitle=NULL;
  592. if(m_video_output) m_video_output->resetSubtitle();
  593. }
  594. void VideoOutput::showOSD() {
  595. if(OSD_ENABLED && m_video_output) {
  596. KillTimer(video_hwnd, TIMER_OSD_ID);
  597. if (!show_osd)
  598. m_video_output->showOSD();
  599. SetTimer(video_hwnd, TIMER_OSD_ID, 2000, NULL);
  600. show_osd = 1;
  601. SetCursor(LoadCursor(NULL, IDC_ARROW));
  602. }
  603. }
  604. void VideoOutput::hideOSD() {
  605. if(OSD_ENABLED && m_video_output) {
  606. KillTimer(video_hwnd, TIMER_OSD_ID);
  607. m_video_output->hideOSD();
  608. show_osd = 0;
  609. SetCursor(NULL);
  610. }
  611. }
  612. void VideoOutput::drawOSD(HDC hdc, RECT *rg) {
  613. if(m_video_output && show_osd) {
  614. if (!osdMemDC ) osdMemDC = CreateCompatibleDC(hdc);
  615. if (!osdFontText) osdFontText=CreateFont(OSD_TEXT_SIZE,0,0,0,FW_SEMIBOLD,FALSE,FALSE,FALSE,ANSI_CHARSET,OUT_OUTLINE_PRECIS,CLIP_DEFAULT_PRECIS,ANTIALIASED_QUALITY,DEFAULT_PITCH|FF_DONTCARE,"Arial");
  616. if (!osdFontSymbol) osdFontSymbol=CreateFont(OSD_TEXT_SIZE,0,0,0,FW_NORMAL,FALSE,FALSE,FALSE,SYMBOL_CHARSET,OUT_OUTLINE_PRECIS,CLIP_DEFAULT_PRECIS,ANTIALIASED_QUALITY,DEFAULT_PITCH,"Webdings");
  617. if (!osdProgressBrushBg) osdProgressBrushBg = CreateSolidBrush(RGB(OSD_VOL_BKCOL_R,OSD_VOL_BKCOL_G,OSD_VOL_BKCOL_B));
  618. if (!osdProgressBrushFg) osdProgressBrushFg = CreateSolidBrush(RGB(OSD_VOL_COL_R,OSD_VOL_COL_G,OSD_VOL_COL_B));
  619. if (!osdBlackBrush ) osdBlackBrush = CreateSolidBrush(RGB(0,0,0));//OV_COL_R,OV_COL_G,OV_COL_B));
  620. if (!osdProgressPenBg ) osdProgressPenBg = CreatePen(PS_SOLID,0,RGB(OSD_TEXT_R,OSD_TEXT_G,OSD_TEXT_B));
  621. if (!osdProgressPenFg ) osdProgressPenFg = CreatePen(PS_NULL,0,RGB(0,0,0));
  622. if (!osdProgressPenBgHilite) osdProgressPenBgHilite = CreatePen(PS_SOLID,0,RGB(OSD_TEXT_R_HILITE,OSD_TEXT_G_HILITE,OSD_TEXT_B_HILITE));
  623. COLORREF fg = GetTextColor(osdMemDC);
  624. COLORREF bg = GetBkColor(osdMemDC);
  625. SetTextColor(osdMemDC, RGB(OSD_TEXT_R,OSD_TEXT_G,OSD_TEXT_B));
  626. SetBkColor(osdMemDC, RGB(0,0,0));//OV_COL_R,OV_COL_G,OV_COL_B));
  627. HGDIOBJ oldfont = SelectObject(osdMemDC, osdFontText);
  628. HGDIOBJ oldbrush = SelectObject(osdMemDC, osdProgressBrushBg);
  629. HGDIOBJ oldpen = SelectObject(osdMemDC, osdProgressPenBg);
  630. RECT fullr;
  631. GetClientRect(video_hwnd,&fullr);
  632. ClientToScreen(video_hwnd,(LPPOINT)&fullr);
  633. ClientToScreen(video_hwnd,((LPPOINT)&fullr) + 1);
  634. // transform coords from windows desktop coords (where 0,0==upper-left corner of the primary monitor)
  635. // to the coords for the monitor we're displaying on:
  636. fullr.top -= m_video_output->m_mon_y;
  637. fullr.left -= m_video_output->m_mon_x;
  638. fullr.right -= m_video_output->m_mon_x;
  639. fullr.bottom -= m_video_output->m_mon_y;
  640. if (!ctrlrects_ready) {
  641. ctrlrects_ready = 1;
  642. int net_width = 0;
  643. int max_height = 0;
  644. int streaming = (decoder && decoder->getlen()==-1) ? 1 : 0;
  645. for (int i=0; i<NUM_WIDGETS; i++) {
  646. if (streaming && (i==CTRL_PROGRESS || i==CTRL_PROGRESSTEXT || i==CTRL_PROGRESSSPACER || i==CTRL_FFWD || i==CTRL_REW)) {
  647. // disable progress bar + seek arrows when the NSV is a stream
  648. ctrlrect[i].right = -1;
  649. continue;
  650. }
  651. else if (g_ctrl_force_width[i] != 0) {
  652. SetRect(&ctrlrect[i], 0, 0, g_ctrl_force_width[i], 0);
  653. }
  654. else {
  655. SelectObject(osdMemDC, (g_ctrl_type[i] == CTRLTYPE_SYMBOL) ? osdFontSymbol : osdFontText);
  656. SetRect(&ctrlrect[i], 0, 0, 256, 256);
  657. ctrlrect[i].bottom = DrawText(osdMemDC, g_ctrl_text[i], -1, &ctrlrect[i], DT_SINGLELINE|DT_CALCRECT);
  658. }
  659. net_width += ctrlrect[i].right - ctrlrect[i].left;
  660. max_height = max(max_height, ctrlrect[i].bottom - ctrlrect[i].top);
  661. }
  662. // now we know the size of all the controls; now place them.
  663. int x = (fullr.right + fullr.left)/2 - net_width/2;
  664. SetRect(&ctrlrect_all, 0, 0, 0, 0);
  665. for (i=0; i<NUM_WIDGETS; i++)
  666. {
  667. if (ctrlrect[i].right >= 0) // if control is not disabled...
  668. {
  669. int this_width = ctrlrect[i].right - ctrlrect[i].left;
  670. int this_height = ctrlrect[i].bottom - ctrlrect[i].top ;
  671. if (this_height==0) this_height = max_height*2/3;// progress bars
  672. ctrlrect[i].top = max_height/2 - this_height/2;
  673. ctrlrect[i].bottom = max_height/2 + this_height/2;
  674. ctrlrect[i].left = x;
  675. ctrlrect[i].right = x + this_width;
  676. if (ctrlrect_all.bottom==0) {
  677. ctrlrect_all.top = ctrlrect[i].top ;
  678. ctrlrect_all.bottom = ctrlrect[i].bottom;
  679. }
  680. else {
  681. ctrlrect_all.top = min(ctrlrect_all.top , ctrlrect[i].top );
  682. ctrlrect_all.bottom = max(ctrlrect_all.bottom, ctrlrect[i].bottom);
  683. }
  684. x += this_width;
  685. }
  686. }
  687. }
  688. int w = fullr.right - fullr.left;
  689. int h = ctrlrect_all.bottom - ctrlrect_all.top;
  690. if (!osdMemBM || osdMemBMW != w || osdMemBMH != h) {
  691. if (osdMemBM) {
  692. SelectObject(osdMemDC,osdOldBM);
  693. DeleteObject(osdMemBM);
  694. }
  695. osdMemBM = CreateCompatibleBitmap(hdc,w,h);
  696. osdOldBM = (HBITMAP)SelectObject(osdMemDC, osdMemBM);
  697. osdMemBMW = w;
  698. osdMemBMH = h;
  699. }
  700. RECT temp;
  701. SetRect(&temp, 0, 0, w, h);
  702. FillRect(osdMemDC, &temp, (HBRUSH)osdBlackBrush);
  703. for (int i=0; i<NUM_WIDGETS; i++) {
  704. if (g_ctrl_type[i] == CTRLTYPE_PROGRESS)
  705. {
  706. int progress = 0;
  707. int max_progress = ctrlrect[i].right - ctrlrect[i].left;
  708. switch(i)
  709. {
  710. case CTRL_VOL:
  711. if (decoder)
  712. progress = decoder->getvolume()*max_progress/255;
  713. break;
  714. case CTRL_PROGRESS:
  715. if (decoder)
  716. {
  717. int len = decoder->getlen();
  718. if (len>0)
  719. progress = decoder->getpos()*max_progress/len;
  720. }
  721. break;
  722. }
  723. SelectObject(osdMemDC, osdProgressBrushBg);
  724. SelectObject(osdMemDC, (i==osdLastClickItem) ? osdProgressPenBgHilite : osdProgressPenBg);
  725. RoundRect(osdMemDC, ctrlrect[i].left, ctrlrect[i].top, ctrlrect[i].right, ctrlrect[i].bottom, 3, 3);
  726. SelectObject(osdMemDC, osdProgressBrushFg);
  727. SelectObject(osdMemDC, osdProgressPenFg);
  728. Rectangle(osdMemDC, ctrlrect[i].left+1, ctrlrect[i].top+1, ctrlrect[i].left + progress, ctrlrect[i].bottom);
  729. }
  730. else if (g_ctrl_type[i] == CTRLTYPE_SYMBOL ||
  731. g_ctrl_type[i] == CTRLTYPE_TEXT)
  732. {
  733. SelectObject(osdMemDC, (g_ctrl_type[i] == CTRLTYPE_SYMBOL) ? osdFontSymbol : osdFontText);
  734. SetTextColor(osdMemDC, (i==osdLastClickItem) ? RGB(OSD_TEXT_R_HILITE,OSD_TEXT_G_HILITE,OSD_TEXT_B_HILITE) : RGB(OSD_TEXT_R,OSD_TEXT_G,OSD_TEXT_B));
  735. DrawText(osdMemDC, g_ctrl_text[i], -1, &ctrlrect[i], DT_SINGLELINE);
  736. }
  737. }
  738. int x0 = fullr.left;
  739. int y0 = fullr.bottom - (ctrlrect_all.bottom - ctrlrect_all.top);
  740. BitBlt(hdc,x0,y0,w,h,osdMemDC,0,0,SRCCOPY);
  741. // display stream title @ the top:
  742. #if (SHOW_STREAM_TITLE_AT_TOP)
  743. if (decoder)
  744. {
  745. RECT temp;
  746. SetRect(&temp, 0, 0, w, h);
  747. FillRect(osdMemDC, &temp, (HBRUSH)osdBlackBrush);
  748. SelectObject(osdMemDC, osdFontText);
  749. SetTextColor(osdMemDC, RGB(OSD_TEXT_R,OSD_TEXT_G,OSD_TEXT_B));
  750. char *t=decoder->getTitle();
  751. char *buf=(char*)malloc(32+(t?strlen(t):0));
  752. wsprintf(buf, "%s (%d kbps)", t?t:"", decoder->getBitrate()/1000);
  753. char *p=buf;
  754. while (*p)
  755. {
  756. if (*p == '_') *p=' ';
  757. p++;
  758. }
  759. DrawText(osdMemDC, buf, -1, &temp, DT_SINGLELINE|DT_CENTER);
  760. free(buf);
  761. SelectObject(osdMemDC, osdFontSymbol);
  762. DrawText(osdMemDC, "2r", -1, &temp, DT_SINGLELINE|DT_RIGHT);
  763. int x0 = fullr.left;
  764. int y0 = fullr.top;
  765. BitBlt(hdc,x0,y0,w,h,osdMemDC,0,0,SRCCOPY);
  766. }
  767. SelectObject(osdMemDC, oldpen);
  768. SelectObject(osdMemDC, oldbrush);
  769. SelectObject(osdMemDC, oldfont);
  770. SetTextColor(osdMemDC, fg);
  771. SetBkColor(osdMemDC, bg);
  772. }
  773. #endif
  774. }
  775. void VideoOutput::osdHitTest(int x, int y, int dragging)
  776. {
  777. // dragging == -1: just a mousemove (no clicking)
  778. // dragging == 0: user clicked
  779. // dragging == 1: user clicked before, and is now dragging/moving mouse
  780. if (dragging<1)
  781. osdLastClickItem = -1;
  782. // transform (x,y) from screen coords into coords relative to the memDC
  783. y = y - ((lastfsrect.bottom - lastfsrect.top) - (ctrlrect_all.bottom - ctrlrect_all.top));
  784. int i0 = 0;
  785. int i1 = NUM_WIDGETS;
  786. if (dragging==1) {
  787. i0 = osdLastClickItem;
  788. i1 = osdLastClickItem+1;
  789. }
  790. for (int i=i0; i<i1; i++)
  791. {
  792. if (dragging==1 || (x >= ctrlrect[i].left && x <= ctrlrect[i].right && y >= ctrlrect[i].top && y <= ctrlrect[i].bottom))
  793. {
  794. float t = (x - ctrlrect[i].left) / (float)(ctrlrect[i].right - ctrlrect[i].left);
  795. if (t<0) t=0;
  796. if (t>1) t=1;
  797. if (dragging<1)
  798. osdLastClickItem = i;
  799. switch(i)
  800. {
  801. case CTRL_VOL:
  802. if (decoder && dragging>=0) decoder->setvolume((int)(t*255));
  803. return;
  804. case CTRL_PROGRESS:
  805. if (decoder && dragging>=0)
  806. {
  807. int len = decoder->getlen();
  808. if (len > 0)
  809. decoder->seek((int)(t*len));
  810. }
  811. return;
  812. case CTRL_PAUSE:
  813. if (decoder && dragging>=0) decoder->pause(1);
  814. return;
  815. case CTRL_PLAY:
  816. if (decoder && dragging>=0) decoder->pause(0);
  817. return;
  818. case CTRL_STOP:
  819. if (decoder && dragging>=0) {
  820. decoder->pause(1);
  821. remove_fullscreen();
  822. }
  823. return;
  824. case CTRL_REW:
  825. case CTRL_FFWD:
  826. if (decoder && dragging>=0)
  827. {
  828. int pos = decoder->getpos();
  829. int len = decoder->getlen();
  830. if (len > 0)
  831. {
  832. if (i==CTRL_REW)
  833. pos = max(0, pos-15000); // milliseconds to rewind
  834. else
  835. pos = min(len, pos+30000); // milliseconds to skip ahead
  836. decoder->seek(pos);
  837. }
  838. }
  839. return;
  840. default:
  841. if (dragging<1)
  842. osdLastClickItem = -1;
  843. break;
  844. }
  845. }
  846. }
  847. if (dragging==0)
  848. remove_fullscreen();
  849. }