/* * Copyright (C) 2003-2006 Gabest * http://www.gabest.org * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * http://www.gnu.org/copyleft/gpl.html * */ #include "stdafx.h" #include "resource.h" #include "DirectVobSubFilter.h" #include "DSUtil/DSUtil.h" // hWnd == INVALID_HANDLE_VALUE - get name, hWnd != INVALID_HANDLE_VALUE - show ppage static TCHAR* CallPPage(IFilterGraph* pGraph, int idx, HWND hWnd); static HHOOK g_hHook = (HHOOK)INVALID_HANDLE_VALUE; static UINT WM_DVSPREVSUB = RegisterWindowMessage(TEXT("WM_DVSPREVSUB")); static UINT WM_DVSNEXTSUB = RegisterWindowMessage(TEXT("WM_DVSNEXTSUB")); static UINT WM_DVSHIDESUB = RegisterWindowMessage(TEXT("WM_DVSHIDESUB")); static UINT WM_DVSSHOWSUB = RegisterWindowMessage(TEXT("WM_DVSSHOWSUB")); static UINT WM_DVSSHOWHIDESUB = RegisterWindowMessage(TEXT("WM_DVSSHOWHIDESUB")); static UINT s_uTaskbarRestart = RegisterWindowMessage(TEXT("TaskbarCreated")); static UINT WM_NOTIFYICON = RegisterWindowMessage(TEXT("MYWM_NOTIFYICON")); LRESULT CALLBACK HookProc(UINT code, WPARAM wParam, LPARAM lParam) { MSG* msg = (MSG*)lParam; if(msg->message == WM_KEYDOWN) { switch(msg->wParam) { case VK_F13: PostMessage(HWND_BROADCAST, WM_DVSPREVSUB, 0, 0); break; case VK_F14: PostMessage(HWND_BROADCAST, WM_DVSNEXTSUB, 0, 0); break; case VK_F15: PostMessage(HWND_BROADCAST, WM_DVSHIDESUB, 0, 0); break; case VK_F16: PostMessage(HWND_BROADCAST, WM_DVSSHOWSUB, 0, 0); break; case VK_F17: PostMessage(HWND_BROADCAST, WM_DVSSHOWHIDESUB, 0, 0); break; default: break; } } // Always call next hook in chain return CallNextHookEx(g_hHook, code, wParam, lParam); } class CSystrayWindow : public CWnd { SystrayIconData* m_tbid; void StepSub(int dir) { int iSelected, nLangs; if(FAILED(m_tbid->dvs->get_LanguageCount(&nLangs))) return; if(FAILED(m_tbid->dvs->get_SelectedLanguage(&iSelected))) return; if(nLangs > 0) m_tbid->dvs->put_SelectedLanguage((iSelected+dir+nLangs)%nLangs); } void ShowSub(bool fShow) { m_tbid->dvs->put_HideSubtitles(!fShow); } void ToggleSub() { bool fShow; if(FAILED(m_tbid->dvs->get_HideSubtitles(&fShow))) return; m_tbid->dvs->put_HideSubtitles(!fShow); } public: CSystrayWindow(SystrayIconData* tbid) : m_tbid(tbid) {} protected: DECLARE_MESSAGE_MAP() public: afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); afx_msg void OnClose(); afx_msg void OnDestroy(); afx_msg void OnTimer(UINT nIDEvent); afx_msg LRESULT OnDVSPrevSub(WPARAM, LPARAM); afx_msg LRESULT OnDVSNextSub(WPARAM, LPARAM); afx_msg LRESULT OnDVSHideSub(WPARAM, LPARAM); afx_msg LRESULT OnDVSShowSub(WPARAM, LPARAM); afx_msg LRESULT OnDVSShowHideSub(WPARAM, LPARAM); afx_msg LRESULT OnTaskBarRestart(WPARAM, LPARAM); afx_msg LRESULT OnNotifyIcon(WPARAM, LPARAM); }; BEGIN_MESSAGE_MAP(CSystrayWindow, CWnd) ON_WM_CREATE() ON_WM_CLOSE() ON_WM_DESTROY() ON_WM_TIMER() ON_REGISTERED_MESSAGE(WM_DVSPREVSUB, OnDVSPrevSub) ON_REGISTERED_MESSAGE(WM_DVSNEXTSUB, OnDVSNextSub) ON_REGISTERED_MESSAGE(WM_DVSHIDESUB, OnDVSHideSub) ON_REGISTERED_MESSAGE(WM_DVSSHOWSUB, OnDVSShowSub) ON_REGISTERED_MESSAGE(WM_DVSSHOWHIDESUB, OnDVSShowHideSub) ON_REGISTERED_MESSAGE(s_uTaskbarRestart, OnTaskBarRestart) ON_REGISTERED_MESSAGE(WM_NOTIFYICON, OnNotifyIcon) END_MESSAGE_MAP() int CSystrayWindow::OnCreate(LPCREATESTRUCT lpCreateStruct) { if(CWnd::OnCreate(lpCreateStruct) == -1) return -1; if(g_hHook == INVALID_HANDLE_VALUE) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); // g_hHook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)HookProc, AfxGetInstanceHandle(), 0); } SetTimer(1, 5000, NULL); PostMessage(s_uTaskbarRestart); return 0; } void CSystrayWindow::OnClose() { DestroyWindow(); } void CSystrayWindow::OnDestroy() { NOTIFYICONDATA tnid; tnid.cbSize = sizeof(NOTIFYICONDATA); tnid.hWnd = m_hWnd; tnid.uID = IDI_ICON1; Shell_NotifyIcon(NIM_DELETE, &tnid); if(g_hHook != INVALID_HANDLE_VALUE) { UnhookWindowsHookEx(g_hHook); g_hHook = (HHOOK)INVALID_HANDLE_VALUE; } PostQuitMessage(0); } void CSystrayWindow::OnTimer(UINT nIDEvent) { if(nIDEvent == 1) { UINT fScreenSaver = 0; if(SystemParametersInfo(SPI_GETSCREENSAVEACTIVE, 0, (PVOID)&fScreenSaver, 0)) { SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, 0, 0, SPIF_SENDWININICHANGE); // this might not be needed at all... SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, fScreenSaver, 0, SPIF_SENDWININICHANGE); } } CWnd::OnTimer(nIDEvent); } LRESULT CSystrayWindow::OnDVSPrevSub(WPARAM, LPARAM) {StepSub(-1); return 0;} LRESULT CSystrayWindow::OnDVSNextSub(WPARAM, LPARAM) {StepSub(+1); return 0;} LRESULT CSystrayWindow::OnDVSHideSub(WPARAM, LPARAM) {ShowSub(false); return 0;} LRESULT CSystrayWindow::OnDVSShowSub(WPARAM, LPARAM) {ShowSub(true); return 0;} LRESULT CSystrayWindow::OnDVSShowHideSub(WPARAM, LPARAM) {ToggleSub(); return 0;} LRESULT CSystrayWindow::OnTaskBarRestart(WPARAM, LPARAM) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); if(m_tbid->fShowIcon) { NOTIFYICONDATA tnid; tnid.cbSize = sizeof(NOTIFYICONDATA); tnid.hWnd = m_hWnd; tnid.uID = IDI_ICON1; tnid.hIcon = (HICON)LoadIcon(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_ICON1)); // tnid.hIcon = (HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_ICON1), IMAGE_ICON, 0, 0, LR_LOADTRANSPARENT); tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP; tnid.uCallbackMessage = WM_NOTIFYICON; lstrcpyn(tnid.szTip, TEXT("DirectVobSub"), sizeof(tnid.szTip)); BOOL res = Shell_NotifyIcon(NIM_ADD, &tnid); if(tnid.hIcon) DestroyIcon(tnid.hIcon); return res?0:-1; } return 0; } LRESULT CSystrayWindow::OnNotifyIcon(WPARAM wParam, LPARAM lParam) { if((UINT)wParam != IDI_ICON1) return -1; HWND hWnd = m_hWnd; switch((UINT)lParam) { case WM_LBUTTONDBLCLK: { // IMPORTANT: we must not hold the graph at the same time as showing the property page // or else when closing the app the graph doesn't get released and dvobsub's JoinFilterGraph // is never called to close us down. CComPtr pBF2; BeginEnumFilters(m_tbid->graph, pEF, pBF) { if(!CComQIPtr(pBF)) continue; if(CComQIPtr pVW = m_tbid->graph) { HWND hwnd; if(SUCCEEDED(pVW->get_Owner((OAHWND*)&hwnd)) || SUCCEEDED(pVW->get_MessageDrain((OAHWND*)&hwnd))) hWnd = hwnd; } pBF2 = pBF; break; } EndEnumFilters if(pBF2) ShowPPage(pBF2, hWnd); } break; case WM_RBUTTONDOWN: { POINT p; GetCursorPos(&p); CInterfaceArray pStreams; CStringArray names; BeginEnumFilters(m_tbid->graph, pEF, pBF) { CString name = GetFilterName(pBF); if(name.IsEmpty()) continue; if(CComQIPtr pSS = pBF) { pStreams.Add(pSS); names.Add(name); } } EndEnumFilters CMenu popup; popup.CreatePopupMenu(); for(int j = 0; j < pStreams.GetCount(); j++) { bool fMMSwitcher = !names[j].Compare(_T("Morgan Stream Switcher")); DWORD cStreams = 0; pStreams[j]->Count(&cStreams); DWORD flags, group, prevgroup = -1; for(UINT i = 0; i < cStreams; i++) { WCHAR* pName = NULL; if(S_OK == pStreams[j]->Info(i, 0, &flags, 0, &group, &pName, 0, 0)) { if(prevgroup != group && i > 1) { if(fMMSwitcher) {cStreams = i; break;} popup.AppendMenu(MF_SEPARATOR); } prevgroup = group; if(pName) { popup.AppendMenu(MF_ENABLED|MF_STRING|(flags?MF_CHECKED:MF_UNCHECKED), (1<<15)|(j<<8)|(i), CString(pName)); CoTaskMemFree(pName); } } } if(cStreams > 0) popup.AppendMenu(MF_SEPARATOR); } int i; TCHAR* str; for(i = 0; str = CallPPage(m_tbid->graph, i, (HWND)INVALID_HANDLE_VALUE); i++) { if(_tcsncmp(str, _T("DivX MPEG"), 9) || m_tbid->fRunOnce) // divx3's ppage will crash if the graph hasn't been run at least once yet popup.AppendMenu(MF_ENABLED|MF_STRING|MF_UNCHECKED, (1<<14)|(i), str); delete [] str; } SetForegroundWindow(); UINT id = popup.TrackPopupMenu(TPM_LEFTBUTTON|TPM_RETURNCMD, p.x, p.y, CWnd::FromHandle(hWnd), 0); PostMessage(WM_NULL); if(id & (1<<15)) { pStreams[(id>>8)&0x3f]->Enable(id&0xff, AMSTREAMSELECTENABLE_ENABLE); } else if(id & (1<<14)) { if(CComQIPtr pVW = m_tbid->graph) { HWND hwnd; if(SUCCEEDED(pVW->get_Owner((OAHWND*)&hwnd)) || SUCCEEDED(pVW->get_MessageDrain((OAHWND*)&hwnd))) hWnd = hwnd; } CallPPage(m_tbid->graph, id&0xff, hWnd); } } break; default: break; } return 0; } // DWORD CALLBACK SystrayThreadProc(void* pParam) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); CSystrayWindow wnd((SystrayIconData*)pParam); if(!wnd.CreateEx(0, AfxRegisterWndClass(0), _T("DVSWND"), WS_OVERLAPPED, CRect(0, 0, 0, 0), NULL, 0, NULL)) return -1; ((SystrayIconData*)pParam)->hSystrayWnd = wnd.m_hWnd; MSG msg; while(GetMessage(&msg, NULL/*wnd.m_hWnd*/, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return 0; } // TODO: replace this function // hWnd == INVALID_HANDLE_VALUE - get name, hWnd != INVALID_HANDLE_VALUE - show ppage static TCHAR* CallPPage(IFilterGraph* pGraph, int idx, HWND hWnd) { int i = 0; bool fFound = false; WCHAR* wstr = NULL; CComPtr pFilter; CAUUID caGUID; caGUID.pElems = NULL; BeginEnumFilters(pGraph, pEF, pBF) { CComQIPtr pSPS = pBF; if(!pSPS) continue; if(i == idx) { pFilter = pBF; pSPS->GetPages(&caGUID); wstr = _wcsdup(CStringW(GetFilterName(pBF))); // double char-wchar conversion happens in the non-unicode build, but anyway... :) break; } i++; } EndEnumFilters TCHAR* ret = NULL; if(pFilter) { if(hWnd != INVALID_HANDLE_VALUE) { ShowPPage(pFilter, hWnd); } else { if(ret = new TCHAR[wcslen(wstr)+1]) _tcscpy(ret, CString(wstr)); } } if(caGUID.pElems) CoTaskMemFree(caGUID.pElems); if(wstr) free(wstr); return(ret); }