winamp/Src/nsv/nsvplay/video.cpp
2024-09-24 14:54:57 +02:00

965 lines
28 KiB
C++

#include <windows.h>
#include <ddraw.h>
#include "main.h"
#include "video.h"
#include "subtitles.h"
#include "resource.h"
#undef GetSystemMetrics
#define OSD_ENABLED 1
#define INIT_DIRECTDRAW_STRUCT(x) (ZeroMemory(&x, sizeof(x)), x.dwSize=sizeof(x))
#define OV_COL_R 16
#define OV_COL_G 0
#define OV_COL_B 16
#define OSD_TEXT_SIZE 28
#define OSD_TEXT_R 192
#define OSD_TEXT_G 192
#define OSD_TEXT_B 192
#define OSD_TEXT_R_HILITE 255
#define OSD_TEXT_G_HILITE 255
#define OSD_TEXT_B_HILITE 255
#define OSD_VOL_COL_R 0
#define OSD_VOL_COL_G 0
#define OSD_VOL_COL_B 192
#define OSD_VOL_BKCOL_R 0
#define OSD_VOL_BKCOL_G 0
#define OSD_VOL_BKCOL_B 64
#define TIMER_OSD_ID 1234
#define CTRLTYPE_SYMBOL 0
#define CTRLTYPE_TEXT 1
#define CTRLTYPE_PROGRESS 2
#define CTRLTYPE_SPACER 3
#define CTRL_PROGRESSTEXT 0
#define CTRL_PROGRESS 1
#define CTRL_PROGRESSSPACER 2
#define CTRL_REW 3
#define CTRL_PLAY 4
#define CTRL_PAUSE 5
#define CTRL_STOP 6
#define CTRL_FFWD 7
#define CTRL_VOLSPACER 8
#define CTRL_VOLTEXT 9
#define CTRL_VOL 10
int g_ctrl_type[NUM_WIDGETS] = {
CTRLTYPE_TEXT,
CTRLTYPE_PROGRESS,
CTRLTYPE_SPACER,
CTRLTYPE_SYMBOL,
CTRLTYPE_SYMBOL,
CTRLTYPE_SYMBOL,
CTRLTYPE_SYMBOL,
CTRLTYPE_SYMBOL,
CTRLTYPE_SPACER,
CTRLTYPE_TEXT,
CTRLTYPE_PROGRESS
};
const char *g_ctrl_text[NUM_WIDGETS] = {
"Progress ",
"",
"",
"7", // rew
"4", // play
";", // pause
"<", // stop
"8", // ffwd
"",
"Volume ",
""
};
int g_ctrl_force_width[NUM_WIDGETS] = {
0,
96, // progress bar width
32, // spacer width
0, // rew
0, // play
0, // pause
0, // stop
0, // ffwd
32, // spacer width
0,
64 // volume bar width
};
extern HINSTANCE g_hInstance;
extern int g_bitmap_id;
static BOOL WINAPI DDEnumCallbackEx(GUID FAR *lpGUID, LPSTR lpDriverDescription, LPSTR lpDriverName, LPVOID lpContext, HMONITOR hm) {
VideoOutputChild *ovo=(VideoOutputChild *)lpContext;
if(ovo->m_found_devguid) return 1;
if(hm==ovo->m_monitor_to_find) {
ovo->m_devguid=*lpGUID;
ovo->m_found_devguid=1;
}
return 1;
}
void VideoOutputChild::update_monitor_coords(VideoOutput *parent)
{
//find the correct monitor if multiple monitor support is present
HWND hwnd=parent->getHwnd();
m_found_devguid=0;
m_mon_x=0;
m_mon_y=0;
HINSTANCE h=LoadLibrary("user32.dll");
if (h) {
HMONITOR (WINAPI *Mfp)(POINT pt, DWORD dwFlags) = (HMONITOR (WINAPI *)(POINT,DWORD)) GetProcAddress(h,"MonitorFromPoint");
HMONITOR (WINAPI *Mfr)(LPCRECT lpcr, DWORD dwFlags) = (HMONITOR (WINAPI *)(LPCRECT, DWORD)) GetProcAddress(h, "MonitorFromRect");
HMONITOR (WINAPI *Mfw)(HWND wnd, DWORD dwFlags)=(HMONITOR (WINAPI *)(HWND, DWORD)) GetProcAddress(h, "MonitorFromWindow");
BOOL (WINAPI *Gmi)(HMONITOR mon, LPMONITORINFO lpmi) = (BOOL (WINAPI *)(HMONITOR,LPMONITORINFO)) GetProcAddress(h,"GetMonitorInfoA");
if (Mfp && Mfr && Mfw && Gmi) {
RECT r;
GetWindowRect(hwnd,&r);
HMONITOR hm=Mfr(&r,NULL);
if(hm) {
HINSTANCE hdd = LoadLibrary("ddraw.dll");
if(hdd) {
typedef BOOL (FAR PASCAL * LPDDENUMCALLBACKEXA)(GUID FAR *, LPSTR, LPSTR, LPVOID, HMONITOR);
typedef HRESULT (WINAPI * LPDIRECTDRAWENUMERATEEX)( LPDDENUMCALLBACKEXA lpCallback, LPVOID lpContext, DWORD dwFlags);
LPDIRECTDRAWENUMERATEEX lpDDEnumEx;
lpDDEnumEx = (LPDIRECTDRAWENUMERATEEX) GetProcAddress(hdd,"DirectDrawEnumerateExA");
if (lpDDEnumEx) {
m_monitor_to_find=hm;
lpDDEnumEx(&DDEnumCallbackEx, this, DDENUM_ATTACHEDSECONDARYDEVICES|DDENUM_NONDISPLAYDEVICES);
if(m_found_devguid) {
MONITORINFOEX mi;
memset(&mi,0,sizeof(mi));
mi.cbSize=sizeof(mi);
if (Gmi(hm,&mi)) {
m_mon_x=mi.rcMonitor.left;
m_mon_y=mi.rcMonitor.top;
}
}
}
FreeLibrary(hdd);
}
}
}
FreeLibrary(h);
}
}
int VideoOutput::get_latency()
{
return vid_vsync?15:0;
}
#undef GetSystemMetrics
int VideoOutput::class_refcnt=0;
void VideoOutput::adjustAspect(RECT &rd)
{
if (vid_aspectadj)
{
int outh=rd.bottom-rd.top;
int outw=rd.right-rd.left;
int newh=(int)((aspect*height*outw)/(double)width);
int neww=(int)((width*outh)/(height*aspect));
if (outh > newh) // black bars on top and bottom
{
int d=outh - newh;
rd.top+=d/2;
rd.bottom-=d-d/2;
}
else if (outw > neww) // black bars on left and right
{
int d=outw - neww;
rd.left+=d/2;
rd.right-=d-d/2;
}
}
}
LRESULT CALLBACK VideoOutput::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (uMsg == WM_CREATE)
{
SetWindowLong(hwnd,GWL_USERDATA,(long)((CREATESTRUCT *)lParam)->lpCreateParams);
ShowWindow(hwnd,SW_SHOW);
if (GetParent(hwnd))
{
RECT r;
GetClientRect(GetParent(hwnd),&r);
SetWindowPos(hwnd,NULL,0,0,
r.right,
r.bottom,
SWP_NOACTIVATE|SWP_NOZORDER);
}
return 0;
}
VideoOutput *_This=(VideoOutput*)GetWindowLong(hwnd,GWL_USERDATA);
if (_This) return _This->WindowProc(hwnd,uMsg,wParam,lParam);
else return DefWindowProc(hwnd,uMsg,wParam,lParam);
}
void VideoOutput::notifyBufferState(int bufferstate) /* 0-255*/
{
m_bufferstate=bufferstate;
#ifdef ACTIVEX_CONTROL
PostMessage( video_hwnd, STATUS_MSG, STATUS_PREBUFFER, bufferstate );
#endif
if (!m_video_output) {
if(GetTickCount()-m_lastbufinvalid>500) {
InvalidateRect(video_hwnd,NULL,FALSE);
m_lastbufinvalid=GetTickCount();
}
}
}
LRESULT VideoOutput::WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_TIMER:
case WM_WINDOWPOSCHANGING:
case WM_WINDOWPOSCHANGED:
case WM_SIZE:
case WM_MOVE:
case WM_MOVING:
if (uMsg == WM_TIMER && wParam == TIMER_OSD_ID) {
hideOSD();
return 0;
}
EnterCriticalSection(&m_cs);
if(m_video_output) m_video_output->timerCallback();
LeaveCriticalSection(&m_cs);
if (uMsg == WM_TIMER) return 0;
break;
case WM_LBUTTONDOWN:
if(is_fs)
osdHitTest(LOWORD(lParam),HIWORD(lParam),0);
#ifdef ACTIVEX_CONTROL
SendMessage( video_hwnd, STATUS_MSG, STATUS_MOUSEPRESS, 1 );
#endif
break;
case WM_PAINT:
{
if (m_video_output && m_video_output->onPaint(hwnd,(HDC)wParam)) return 0;
if (m_logo && !m_video_output)
{
PAINTSTRUCT p;
BeginPaint(hwnd,&p);
RECT r;
GetClientRect(hwnd,&r);
HDC out=p.hdc;
HDC dc=CreateCompatibleDC(NULL);
SelectObject(dc,m_logo);
int xp=(r.right-r.left-m_logo_w)/2;
int yp=(r.bottom-r.top-m_logo_h)/2;
BitBlt(out,xp,yp,m_logo_w,m_logo_h,dc,0,0,SRCCOPY);
int bs=m_bufferstate;
if (bs < 16) bs=16;
HGDIOBJ oldobj1=SelectObject(out,CreateSolidBrush(RGB(0,0,0)));
HGDIOBJ oldobj2=SelectObject(out,CreatePen(PS_SOLID,0,RGB(0,0,0)));
Rectangle(out,r.left,r.top,r.right,yp);
if (m_statusmsg)
Rectangle(out,r.left,yp+m_logo_h,r.right,r.bottom);
else
{
Rectangle(out,r.left,yp+m_logo_h+2+9,r.right,r.bottom);
Rectangle(out,xp + ((bs * (m_logo_w+2))>>8),yp+m_logo_h+2,r.right, yp+9+m_logo_h+2);
}
Rectangle(out,r.left,yp,xp-1,yp+m_logo_h+9+2);
Rectangle(out,xp+m_logo_w+1,yp,r.right,yp+m_logo_h+2);
DeleteObject(SelectObject(out,oldobj2));
DeleteObject(SelectObject(out,oldobj1));
if (m_statusmsg)
{
RECT subr={0,yp+m_logo_h+2,r.right,r.bottom};
SetTextColor(out,RGB(255,255,255));
SetBkMode(out,TRANSPARENT);
DrawText(out,m_statusmsg,-1,&subr,DT_TOP|DT_CENTER|DT_NOCLIP|DT_NOPREFIX);
}
else
{
yp+=m_logo_h+2;
if (bs)
{
HGDIOBJ oldobj1=SelectObject(out,CreateSolidBrush(RGB(128,128,128)));
HGDIOBJ oldobj2=SelectObject(out,CreatePen(PS_SOLID,0,RGB(255,255,255)));
Rectangle(out,xp-1,yp,xp + ((bs * (m_logo_w+2))>>8), yp+9);
DeleteObject(SelectObject(out,oldobj2));
DeleteObject(SelectObject(out,oldobj1));
}
}
DeleteDC(dc);
EndPaint(hwnd,&p);
return 0;
}
}
break;
case WM_USER+0x1:
m_need_change=1;
break;
#ifdef ACTIVEX_CONTROL
case STATUS_MSG:
SendStatus( wParam, lParam );
break;
#endif
case WM_KEYDOWN:
if(wParam==27 && is_fs) remove_fullscreen();
break;
case WM_MOUSEMOVE:
if(is_fs) {
if (ignore_mousemove_count>0) {
ignore_mousemove_count--;
}
else if (abs(osdLastMouseX - LOWORD(lParam)) + abs(osdLastMouseY - HIWORD(lParam)) > 1) {
KillTimer(hwnd, TIMER_OSD_ID);
showOSD();
SetTimer(hwnd, TIMER_OSD_ID, 2000, NULL);
if (wParam & MK_LBUTTON)
osdHitTest(LOWORD(lParam),HIWORD(lParam),1);
else
osdHitTest(LOWORD(lParam),HIWORD(lParam),-1);
}
osdLastMouseX = LOWORD(lParam);
osdLastMouseY = HIWORD(lParam);
}
break;
}
if (m_msgcallback)
{
return m_msgcallback(m_msgcallback_tok,hwnd, uMsg, wParam, lParam);
}
return (DefWindowProc(hwnd, uMsg, wParam, lParam));
}
VideoOutput::VideoOutput(HWND parent_hwnd, int initxpos, int initypos)
{
curSubtitle=NULL;
m_statusmsg=0;
m_bufferstate=0;
m_msgcallback=0;
m_msgcallback_tok=0;
video_hwnd=video_parent_hwnd=0;
decoder=0;
vid_aspectadj=true;
vid_overlays=true;
vid_ddraw=true;
vid_vsync=true;
aspect=1.0;
m_need_change=false;
width=height=flip=uyvy_output=yuy2_output=is_fs=ignore_mousemove_count=show_osd=0;
oldfsparent=0;
memset(&oldfsrect,0,sizeof(oldfsrect));
memset(&lastfsrect,0,sizeof(lastfsrect));
oldfsstyle=0;
m_video_output=NULL;
osdFontText=NULL;
osdFontSymbol=NULL;
osdProgressBrushBg=NULL;
osdProgressBrushFg=NULL;
osdProgressPenBg=NULL;
osdProgressPenFg=NULL;
osdProgressPenBgHilite=NULL;
osdBlackBrush=NULL;
osdMemDC=NULL;
osdMemBM=NULL;
osdOldBM=NULL;
osdMemBMW=0;
osdMemBMH=0;
osdLastMouseX=-1;
osdLastMouseY=-1;
for (int i=0; i<NUM_WIDGETS; i++)
SetRect(&ctrlrect[i], 0, 0, 0, 0);
ctrlrects_ready = 0;
resetSubtitle();
WNDCLASS wc={0,};
wc.hCursor=LoadCursor(NULL,IDC_ARROW);
wc.lpfnWndProc = WndProc;
wc.hInstance = GetModuleHandle(NULL);
wc.lpszClassName = "NSVplay";
LOGBRUSH lb={BS_SOLID,RGB(OV_COL_R,OV_COL_G,OV_COL_B),};
wc.hbrBackground=CreateBrushIndirect(&lb);
if (!class_refcnt) RegisterClass(&wc);
class_refcnt++;
m_logo=(HBITMAP)LoadImage(g_hInstance,MAKEINTRESOURCE(g_bitmap_id),IMAGE_BITMAP,0,0,LR_CREATEDIBSECTION);
BITMAP bm;
GetObject(m_logo, sizeof(BITMAP), &bm);
m_logo_w=bm.bmWidth;
m_logo_h=bm.bmHeight;
if(m_logo_h<0) m_logo_h=-m_logo_h;
InitializeCriticalSection(&m_cs);
video_hwnd=CreateWindowEx(0,wc.lpszClassName, "NSV Player",parent_hwnd?WS_CHILD:(WS_OVERLAPPEDWINDOW&(~WS_MAXIMIZEBOX)),
initxpos,initypos,320,200,
parent_hwnd, NULL,wc.hInstance,(void*)this);
video_parent_hwnd=parent_hwnd;
m_lastbufinvalid=0;
#ifdef ACTIVEX_CONTROL
m_firstframe = 1;
#endif
}
VideoOutputChild *VideoOutput::createVideoOutput(int n) {
if(!vid_overlays && !vid_ddraw) vid_overlays=true;
if(!vid_overlays) n++;
if(n==0) return new OverlayVideoOutput();
if(!vid_ddraw) n++;
if(n==1) return new DDrawVideoOutput();
return 0;
}
int VideoOutput::open(int w, int h, int vflip, double aspectratio, unsigned int fmt)
{
EnterCriticalSection(&m_cs);
delete(m_video_output);
m_video_output=NULL;
if (!w) w=320;
if (!h) h=240;
width=w;
height=h;
flip=vflip;
type=fmt;
is_fs=0;
ignore_mousemove_count=0;
show_osd=0;
aspect=aspectratio;
for(int i=0;m_video_output=createVideoOutput(i);i++) {
if(m_video_output->create(this,w,h,fmt,vflip,aspectratio)) {
LeaveCriticalSection(&m_cs);
if (!GetParent(video_hwnd)) {
RECT r,r2;
int ow=width,oh=height;
if (aspect > 0.001)
{
if (aspect < 1.0) ow=(int)(ow/aspect);
else oh=(int)(oh*aspect);
}
GetWindowRect(video_hwnd,&r);
GetClientRect(video_hwnd,&r2);
SetWindowPos(video_hwnd,NULL,0,0,
ow+(r.right-r.left)-(r2.right-r2.left),
oh+(r.bottom-r.top)-(r2.bottom-r2.top),
SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER);
}
return 0;
}
delete(m_video_output);
}
LeaveCriticalSection(&m_cs);
return 1;
}
void VideoOutput::draw(void *frame)
{
if (!m_video_output || !frame) return;
if ((m_video_output && m_video_output->needChange()) || m_need_change) {
open(width,height,flip,aspect,type);
m_need_change=0;
}
#ifdef ACTIVEX_CONTROL
if ( m_firstframe ) {
m_firstframe = 0;
PostMessage( video_hwnd, STATUS_MSG, STATUS_FIRSTFRAME, 1 );
}
#endif
if (m_video_output) m_video_output->displayFrame((const char *)frame,0,0);
}
VideoOutput::~VideoOutput()
{
free(m_statusmsg);
delete(m_video_output);
DestroyWindow(video_hwnd);
if (!--class_refcnt) UnregisterClass("NSVplay",GetModuleHandle(NULL));
if(osdFontText) DeleteObject(osdFontText);
if(osdFontSymbol) DeleteObject(osdFontSymbol);
if(osdProgressBrushBg) DeleteObject(osdProgressBrushBg);
if(osdProgressBrushFg) DeleteObject(osdProgressBrushFg);
if(osdBlackBrush ) DeleteObject(osdBlackBrush );
if(osdProgressPenBg ) DeleteObject(osdProgressPenBg );
if(osdProgressPenFg ) DeleteObject(osdProgressPenFg );
if(osdProgressPenBgHilite) DeleteObject(osdProgressPenBgHilite);
if(osdMemDC) {
SelectObject(osdMemDC,osdOldBM); // delete our doublebuffer
DeleteDC(osdMemDC);
}
if(osdMemBM) DeleteObject(osdMemBM);
DeleteCriticalSection(&m_cs);
}
void VideoOutput::close()
{
delete(m_video_output);
m_video_output=NULL;
}
void VideoOutput::getViewport(RECT *r, HWND wnd, int full) {
POINT *p=NULL;
RECT *sr=NULL;
if (p || sr || wnd) {
HINSTANCE h=LoadLibrary("user32.dll");
if (h) {
HMONITOR (WINAPI *Mfp)(POINT pt, DWORD dwFlags) = (HMONITOR (WINAPI *)(POINT,DWORD)) GetProcAddress(h,"MonitorFromPoint");
HMONITOR (WINAPI *Mfr)(LPCRECT lpcr, DWORD dwFlags) = (HMONITOR (WINAPI *)(LPCRECT, DWORD)) GetProcAddress(h, "MonitorFromRect");
HMONITOR (WINAPI *Mfw)(HWND wnd, DWORD dwFlags)=(HMONITOR (WINAPI *)(HWND, DWORD)) GetProcAddress(h, "MonitorFromWindow");
BOOL (WINAPI *Gmi)(HMONITOR mon, LPMONITORINFO lpmi) = (BOOL (WINAPI *)(HMONITOR,LPMONITORINFO)) GetProcAddress(h,"GetMonitorInfoA");
if (Mfp && Mfr && Mfw && Gmi) {
HMONITOR hm = NULL;
if (p)
hm=Mfp(*p,MONITOR_DEFAULTTONULL);
else if (sr)
hm=Mfr(sr,MONITOR_DEFAULTTONULL);
else if (wnd)
hm=Mfw(wnd,MONITOR_DEFAULTTONULL);
if (hm) {
MONITORINFOEX mi;
memset(&mi,0,sizeof(mi));
mi.cbSize=sizeof(mi);
if (Gmi(hm,&mi)) {
if(!full) *r=mi.rcWork;
else *r=mi.rcMonitor;
FreeLibrary(h);
return;
}
}
}
FreeLibrary(h);
}
}
if (full)
{ // this might be borked =)
r->top=r->left=0;
r->right=::GetSystemMetrics(SM_CXSCREEN);
r->bottom=::GetSystemMetrics(SM_CYSCREEN);
}
else
{
SystemParametersInfo(SPI_GETWORKAREA,0,r,0);
}
}
void VideoOutput::fullscreen()
{
if (is_fs) return;
if(!m_video_output) return;
is_fs=1;
ignore_mousemove_count=2;
oldfsparent=GetParent(video_hwnd);
oldfsstyle=GetWindowLong(video_hwnd,GWL_STYLE);
if (!oldfsparent) GetWindowRect(video_hwnd,&oldfsrect);
else GetClientRect(video_hwnd,&oldfsrect);
getViewport(&lastfsrect,video_hwnd,1);
SetParent(video_hwnd,NULL);
SetWindowLong(video_hwnd,GWL_STYLE,WS_POPUP|WS_VISIBLE);
SetWindowPos(video_hwnd, HWND_TOPMOST, lastfsrect.left, lastfsrect.top, lastfsrect.right-lastfsrect.left, lastfsrect.bottom-lastfsrect.top, SWP_DRAWFRAME);
SetFocus(video_hwnd);
resetSubtitle();
//showOSD();
//SetCursor(NULL);
}
void VideoOutput::getOutputSize(int *w, int *h)
{
RECT r2;
GetClientRect(video_hwnd,&r2);
*w=r2.right-r2.left;
*h=r2.bottom-r2.top;
}
void VideoOutput::setOutputSize(int w, int h)
{
RECT r,r2;
GetWindowRect(video_hwnd,&r);
GetClientRect(video_hwnd,&r2);
SetWindowPos(video_hwnd, 0, 0,0,
w+(r.right-r.left)-(r2.right-r2.left),
h+(r.bottom-r.top)-(r2.bottom-r2.top),
SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE);
}
void VideoOutput::remove_fullscreen()
{
if(!is_fs) return;
SetParent(video_hwnd,oldfsparent);
SetWindowLong(video_hwnd,GWL_STYLE,oldfsstyle);
// note: when returning from fullscreen *on a secondary monitor*,
// be careful how you set the new window Z order.
// nsvplay.exe: only HWND_NOTOPMOST works
// nsvplayX.exe: only HWND_TOP works
SetWindowPos(video_hwnd, oldfsparent ? HWND_TOP : HWND_NOTOPMOST, oldfsrect.left, oldfsrect.top, oldfsrect.right-oldfsrect.left, oldfsrect.bottom-oldfsrect.top, SWP_FRAMECHANGED);
SetFocus(oldfsparent ? oldfsparent : video_hwnd);
is_fs=0;
show_osd=0;
ctrlrects_ready=0;
resetSubtitle();
hideOSD();
}
int VideoOutput::is_fullscreen()
{
return is_fs;
}
void VideoOutput::showStatusMsg(const char *text)
{
m_statusmsg=_strdup(text);
InvalidateRect(video_hwnd,NULL,TRUE);
}
void VideoOutput::drawSubtitle(SubsItem *item)
{
if(!item) {
if(curSubtitle) {
m_video_output->drawSubtitle(NULL);
curSubtitle=NULL;
}
return;
}
if(curSubtitle==item) return;
curSubtitle=item;
m_video_output->drawSubtitle(curSubtitle);
}
void VideoOutput::resetSubtitle()
{
curSubtitle=NULL;
if(m_video_output) m_video_output->resetSubtitle();
}
void VideoOutput::showOSD() {
if(OSD_ENABLED && m_video_output) {
KillTimer(video_hwnd, TIMER_OSD_ID);
if (!show_osd)
m_video_output->showOSD();
SetTimer(video_hwnd, TIMER_OSD_ID, 2000, NULL);
show_osd = 1;
SetCursor(LoadCursor(NULL, IDC_ARROW));
}
}
void VideoOutput::hideOSD() {
if(OSD_ENABLED && m_video_output) {
KillTimer(video_hwnd, TIMER_OSD_ID);
m_video_output->hideOSD();
show_osd = 0;
SetCursor(NULL);
}
}
void VideoOutput::drawOSD(HDC hdc, RECT *rg) {
if(m_video_output && show_osd) {
if (!osdMemDC ) osdMemDC = CreateCompatibleDC(hdc);
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");
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");
if (!osdProgressBrushBg) osdProgressBrushBg = CreateSolidBrush(RGB(OSD_VOL_BKCOL_R,OSD_VOL_BKCOL_G,OSD_VOL_BKCOL_B));
if (!osdProgressBrushFg) osdProgressBrushFg = CreateSolidBrush(RGB(OSD_VOL_COL_R,OSD_VOL_COL_G,OSD_VOL_COL_B));
if (!osdBlackBrush ) osdBlackBrush = CreateSolidBrush(RGB(0,0,0));//OV_COL_R,OV_COL_G,OV_COL_B));
if (!osdProgressPenBg ) osdProgressPenBg = CreatePen(PS_SOLID,0,RGB(OSD_TEXT_R,OSD_TEXT_G,OSD_TEXT_B));
if (!osdProgressPenFg ) osdProgressPenFg = CreatePen(PS_NULL,0,RGB(0,0,0));
if (!osdProgressPenBgHilite) osdProgressPenBgHilite = CreatePen(PS_SOLID,0,RGB(OSD_TEXT_R_HILITE,OSD_TEXT_G_HILITE,OSD_TEXT_B_HILITE));
COLORREF fg = GetTextColor(osdMemDC);
COLORREF bg = GetBkColor(osdMemDC);
SetTextColor(osdMemDC, RGB(OSD_TEXT_R,OSD_TEXT_G,OSD_TEXT_B));
SetBkColor(osdMemDC, RGB(0,0,0));//OV_COL_R,OV_COL_G,OV_COL_B));
HGDIOBJ oldfont = SelectObject(osdMemDC, osdFontText);
HGDIOBJ oldbrush = SelectObject(osdMemDC, osdProgressBrushBg);
HGDIOBJ oldpen = SelectObject(osdMemDC, osdProgressPenBg);
RECT fullr;
GetClientRect(video_hwnd,&fullr);
ClientToScreen(video_hwnd,(LPPOINT)&fullr);
ClientToScreen(video_hwnd,((LPPOINT)&fullr) + 1);
// transform coords from windows desktop coords (where 0,0==upper-left corner of the primary monitor)
// to the coords for the monitor we're displaying on:
fullr.top -= m_video_output->m_mon_y;
fullr.left -= m_video_output->m_mon_x;
fullr.right -= m_video_output->m_mon_x;
fullr.bottom -= m_video_output->m_mon_y;
if (!ctrlrects_ready) {
ctrlrects_ready = 1;
int net_width = 0;
int max_height = 0;
int streaming = (decoder && decoder->getlen()==-1) ? 1 : 0;
for (int i=0; i<NUM_WIDGETS; i++) {
if (streaming && (i==CTRL_PROGRESS || i==CTRL_PROGRESSTEXT || i==CTRL_PROGRESSSPACER || i==CTRL_FFWD || i==CTRL_REW)) {
// disable progress bar + seek arrows when the NSV is a stream
ctrlrect[i].right = -1;
continue;
}
else if (g_ctrl_force_width[i] != 0) {
SetRect(&ctrlrect[i], 0, 0, g_ctrl_force_width[i], 0);
}
else {
SelectObject(osdMemDC, (g_ctrl_type[i] == CTRLTYPE_SYMBOL) ? osdFontSymbol : osdFontText);
SetRect(&ctrlrect[i], 0, 0, 256, 256);
ctrlrect[i].bottom = DrawText(osdMemDC, g_ctrl_text[i], -1, &ctrlrect[i], DT_SINGLELINE|DT_CALCRECT);
}
net_width += ctrlrect[i].right - ctrlrect[i].left;
max_height = max(max_height, ctrlrect[i].bottom - ctrlrect[i].top);
}
// now we know the size of all the controls; now place them.
int x = (fullr.right + fullr.left)/2 - net_width/2;
SetRect(&ctrlrect_all, 0, 0, 0, 0);
for (i=0; i<NUM_WIDGETS; i++)
{
if (ctrlrect[i].right >= 0) // if control is not disabled...
{
int this_width = ctrlrect[i].right - ctrlrect[i].left;
int this_height = ctrlrect[i].bottom - ctrlrect[i].top ;
if (this_height==0) this_height = max_height*2/3;// progress bars
ctrlrect[i].top = max_height/2 - this_height/2;
ctrlrect[i].bottom = max_height/2 + this_height/2;
ctrlrect[i].left = x;
ctrlrect[i].right = x + this_width;
if (ctrlrect_all.bottom==0) {
ctrlrect_all.top = ctrlrect[i].top ;
ctrlrect_all.bottom = ctrlrect[i].bottom;
}
else {
ctrlrect_all.top = min(ctrlrect_all.top , ctrlrect[i].top );
ctrlrect_all.bottom = max(ctrlrect_all.bottom, ctrlrect[i].bottom);
}
x += this_width;
}
}
}
int w = fullr.right - fullr.left;
int h = ctrlrect_all.bottom - ctrlrect_all.top;
if (!osdMemBM || osdMemBMW != w || osdMemBMH != h) {
if (osdMemBM) {
SelectObject(osdMemDC,osdOldBM);
DeleteObject(osdMemBM);
}
osdMemBM = CreateCompatibleBitmap(hdc,w,h);
osdOldBM = (HBITMAP)SelectObject(osdMemDC, osdMemBM);
osdMemBMW = w;
osdMemBMH = h;
}
RECT temp;
SetRect(&temp, 0, 0, w, h);
FillRect(osdMemDC, &temp, (HBRUSH)osdBlackBrush);
for (int i=0; i<NUM_WIDGETS; i++) {
if (g_ctrl_type[i] == CTRLTYPE_PROGRESS)
{
int progress = 0;
int max_progress = ctrlrect[i].right - ctrlrect[i].left;
switch(i)
{
case CTRL_VOL:
if (decoder)
progress = decoder->getvolume()*max_progress/255;
break;
case CTRL_PROGRESS:
if (decoder)
{
int len = decoder->getlen();
if (len>0)
progress = decoder->getpos()*max_progress/len;
}
break;
}
SelectObject(osdMemDC, osdProgressBrushBg);
SelectObject(osdMemDC, (i==osdLastClickItem) ? osdProgressPenBgHilite : osdProgressPenBg);
RoundRect(osdMemDC, ctrlrect[i].left, ctrlrect[i].top, ctrlrect[i].right, ctrlrect[i].bottom, 3, 3);
SelectObject(osdMemDC, osdProgressBrushFg);
SelectObject(osdMemDC, osdProgressPenFg);
Rectangle(osdMemDC, ctrlrect[i].left+1, ctrlrect[i].top+1, ctrlrect[i].left + progress, ctrlrect[i].bottom);
}
else if (g_ctrl_type[i] == CTRLTYPE_SYMBOL ||
g_ctrl_type[i] == CTRLTYPE_TEXT)
{
SelectObject(osdMemDC, (g_ctrl_type[i] == CTRLTYPE_SYMBOL) ? osdFontSymbol : osdFontText);
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));
DrawText(osdMemDC, g_ctrl_text[i], -1, &ctrlrect[i], DT_SINGLELINE);
}
}
int x0 = fullr.left;
int y0 = fullr.bottom - (ctrlrect_all.bottom - ctrlrect_all.top);
BitBlt(hdc,x0,y0,w,h,osdMemDC,0,0,SRCCOPY);
// display stream title @ the top:
#if (SHOW_STREAM_TITLE_AT_TOP)
if (decoder)
{
RECT temp;
SetRect(&temp, 0, 0, w, h);
FillRect(osdMemDC, &temp, (HBRUSH)osdBlackBrush);
SelectObject(osdMemDC, osdFontText);
SetTextColor(osdMemDC, RGB(OSD_TEXT_R,OSD_TEXT_G,OSD_TEXT_B));
char *t=decoder->getTitle();
char *buf=(char*)malloc(32+(t?strlen(t):0));
wsprintf(buf, "%s (%d kbps)", t?t:"", decoder->getBitrate()/1000);
char *p=buf;
while (*p)
{
if (*p == '_') *p=' ';
p++;
}
DrawText(osdMemDC, buf, -1, &temp, DT_SINGLELINE|DT_CENTER);
free(buf);
SelectObject(osdMemDC, osdFontSymbol);
DrawText(osdMemDC, "2r", -1, &temp, DT_SINGLELINE|DT_RIGHT);
int x0 = fullr.left;
int y0 = fullr.top;
BitBlt(hdc,x0,y0,w,h,osdMemDC,0,0,SRCCOPY);
}
SelectObject(osdMemDC, oldpen);
SelectObject(osdMemDC, oldbrush);
SelectObject(osdMemDC, oldfont);
SetTextColor(osdMemDC, fg);
SetBkColor(osdMemDC, bg);
}
#endif
}
void VideoOutput::osdHitTest(int x, int y, int dragging)
{
// dragging == -1: just a mousemove (no clicking)
// dragging == 0: user clicked
// dragging == 1: user clicked before, and is now dragging/moving mouse
if (dragging<1)
osdLastClickItem = -1;
// transform (x,y) from screen coords into coords relative to the memDC
y = y - ((lastfsrect.bottom - lastfsrect.top) - (ctrlrect_all.bottom - ctrlrect_all.top));
int i0 = 0;
int i1 = NUM_WIDGETS;
if (dragging==1) {
i0 = osdLastClickItem;
i1 = osdLastClickItem+1;
}
for (int i=i0; i<i1; i++)
{
if (dragging==1 || (x >= ctrlrect[i].left && x <= ctrlrect[i].right && y >= ctrlrect[i].top && y <= ctrlrect[i].bottom))
{
float t = (x - ctrlrect[i].left) / (float)(ctrlrect[i].right - ctrlrect[i].left);
if (t<0) t=0;
if (t>1) t=1;
if (dragging<1)
osdLastClickItem = i;
switch(i)
{
case CTRL_VOL:
if (decoder && dragging>=0) decoder->setvolume((int)(t*255));
return;
case CTRL_PROGRESS:
if (decoder && dragging>=0)
{
int len = decoder->getlen();
if (len > 0)
decoder->seek((int)(t*len));
}
return;
case CTRL_PAUSE:
if (decoder && dragging>=0) decoder->pause(1);
return;
case CTRL_PLAY:
if (decoder && dragging>=0) decoder->pause(0);
return;
case CTRL_STOP:
if (decoder && dragging>=0) {
decoder->pause(1);
remove_fullscreen();
}
return;
case CTRL_REW:
case CTRL_FFWD:
if (decoder && dragging>=0)
{
int pos = decoder->getpos();
int len = decoder->getlen();
if (len > 0)
{
if (i==CTRL_REW)
pos = max(0, pos-15000); // milliseconds to rewind
else
pos = min(len, pos+30000); // milliseconds to skip ahead
decoder->seek(pos);
}
}
return;
default:
if (dragging<1)
osdLastClickItem = -1;
break;
}
}
}
if (dragging==0)
remove_fullscreen();
}