Game client codebase including: - CharacterActionControl: Character and creature management - GlobalScript: Network, items, skills, quests, utilities - RYLClient: Main client application with GUI and event handlers - Engine: 3D rendering engine (RYLGL) - MemoryManager: Custom memory allocation - Library: Third-party dependencies (DirectX, boost, etc.) - Tools: Development utilities 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1100 lines
29 KiB
C++
1100 lines
29 KiB
C++
//------------------------------------------------------------------------------
|
|
// File: StillViewDlg.cpp
|
|
//
|
|
// Desc: DirectShow sample code - implementation of CStillViewDlg class.
|
|
//
|
|
// Copyright (c) 1998-2001 Microsoft Corporation. All rights reserved.
|
|
//------------------------------------------------------------------------------
|
|
|
|
#include "stdafx.h"
|
|
#include <atlbase.h>
|
|
#include "StillView.h"
|
|
#include "StillViewDlg.h"
|
|
#include "mediatypes.h"
|
|
|
|
#ifdef _DEBUG
|
|
#define new DEBUG_NEW
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CAboutDlg dialog used for App About
|
|
|
|
class CAboutDlg : public CDialog
|
|
{
|
|
public:
|
|
CAboutDlg();
|
|
|
|
// Dialog Data
|
|
//{{AFX_DATA(CAboutDlg)
|
|
enum { IDD = IDD_ABOUTBOX };
|
|
//}}AFX_DATA
|
|
|
|
// ClassWizard generated virtual function overrides
|
|
//{{AFX_VIRTUAL(CAboutDlg)
|
|
protected:
|
|
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
|
|
//}}AFX_VIRTUAL
|
|
|
|
// Implementation
|
|
protected:
|
|
//{{AFX_MSG(CAboutDlg)
|
|
//}}AFX_MSG
|
|
DECLARE_MESSAGE_MAP()
|
|
};
|
|
|
|
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
|
|
{
|
|
//{{AFX_DATA_INIT(CAboutDlg)
|
|
//}}AFX_DATA_INIT
|
|
}
|
|
|
|
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
|
|
{
|
|
CDialog::DoDataExchange(pDX);
|
|
//{{AFX_DATA_MAP(CAboutDlg)
|
|
//}}AFX_DATA_MAP
|
|
}
|
|
|
|
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
|
|
//{{AFX_MSG_MAP(CAboutDlg)
|
|
// No message handlers
|
|
//}}AFX_MSG_MAP
|
|
END_MESSAGE_MAP()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CStillViewDlg dialog
|
|
|
|
CStillViewDlg::CStillViewDlg(CWnd* pParent /*=NULL*/)
|
|
: CDialog(CStillViewDlg::IDD, pParent), m_pGB(0), m_pMC(0), m_pBV(0), m_pVW(0), m_pME(0)
|
|
{
|
|
//{{AFX_DATA_INIT(CStillViewDlg)
|
|
//}}AFX_DATA_INIT
|
|
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
|
|
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
|
|
}
|
|
|
|
void CStillViewDlg::DoDataExchange(CDataExchange* pDX)
|
|
{
|
|
CDialog::DoDataExchange(pDX);
|
|
//{{AFX_DATA_MAP(CStillViewDlg)
|
|
DDX_Control(pDX, IDC_STATIC_FILETIME, m_StrFileTime);
|
|
DDX_Control(pDX, IDC_STATIC_IMAGESIZE, m_StrImageSize);
|
|
DDX_Control(pDX, IDC_EDIT_MEDIADIR, m_EditMediaDir);
|
|
DDX_Control(pDX, IDC_SPIN_FILES, m_SpinFiles);
|
|
DDX_Control(pDX, IDC_BUTTON_PROPPAGE, m_ButtonProperties);
|
|
DDX_Control(pDX, IDC_STATUS_DIRECTORY, m_StrMediaPath);
|
|
DDX_Control(pDX, IDC_STATIC_FILEDATE, m_StrFileDate);
|
|
DDX_Control(pDX, IDC_STATIC_FILESIZE, m_StrFileSize);
|
|
DDX_Control(pDX, IDC_LIST_PINS_OUTPUT, m_ListPinsOutput);
|
|
DDX_Control(pDX, IDC_LIST_PINS_INPUT, m_ListPinsInput);
|
|
DDX_Control(pDX, IDC_STATIC_FILELIST, m_StrFileList);
|
|
DDX_Control(pDX, IDC_MOVIE_SCREEN, m_Screen);
|
|
DDX_Control(pDX, IDC_LIST_FILTERS, m_ListFilters);
|
|
DDX_Control(pDX, IDC_LIST_FILES, m_ListFiles);
|
|
//}}AFX_DATA_MAP
|
|
}
|
|
|
|
BEGIN_MESSAGE_MAP(CStillViewDlg, CDialog)
|
|
//{{AFX_MSG_MAP(CStillViewDlg)
|
|
ON_WM_ERASEBKGND()
|
|
ON_WM_SYSCOMMAND()
|
|
ON_WM_PAINT()
|
|
ON_WM_QUERYDRAGICON()
|
|
ON_WM_CLOSE()
|
|
ON_WM_DESTROY()
|
|
ON_LBN_SELCHANGE(IDC_LIST_FILES, OnSelectFile)
|
|
ON_LBN_SELCHANGE(IDC_LIST_FILTERS, OnSelchangeListFilters)
|
|
ON_LBN_DBLCLK(IDC_LIST_FILTERS, OnDblclkListFilters)
|
|
ON_BN_CLICKED(IDC_BUTTON_PROPPAGE, OnButtonProppage)
|
|
ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_FILES, OnDeltaposSpinFiles)
|
|
ON_BN_CLICKED(IDC_BUTTON_SET_MEDIADIR, OnButtonSetMediadir)
|
|
ON_BN_CLICKED(IDC_BUTTON_GRAPHEDIT, OnButtonGraphedit)
|
|
//}}AFX_MSG_MAP
|
|
END_MESSAGE_MAP()
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CStillViewDlg message handlers
|
|
|
|
void CStillViewDlg::OnSysCommand(UINT nID, LPARAM lParam)
|
|
{
|
|
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
|
|
{
|
|
CAboutDlg dlgAbout;
|
|
dlgAbout.DoModal();
|
|
}
|
|
else
|
|
{
|
|
CDialog::OnSysCommand(nID, lParam);
|
|
}
|
|
}
|
|
|
|
// If you add a minimize button to your dialog, you will need the code below
|
|
// to draw the icon. For MFC applications using the document/view model,
|
|
// this is automatically done for you by the framework.
|
|
|
|
void CStillViewDlg::OnPaint()
|
|
{
|
|
if (IsIconic())
|
|
{
|
|
CPaintDC dc(this); // device context for painting
|
|
|
|
SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
|
|
|
|
// Center icon in client rectangle
|
|
int cxIcon = GetSystemMetrics(SM_CXICON);
|
|
int cyIcon = GetSystemMetrics(SM_CYICON);
|
|
CRect rect;
|
|
GetClientRect(&rect);
|
|
int x = (rect.Width() - cxIcon + 1) / 2;
|
|
int y = (rect.Height() - cyIcon + 1) / 2;
|
|
|
|
// Draw the icon
|
|
dc.DrawIcon(x, y, m_hIcon);
|
|
}
|
|
else
|
|
{
|
|
CDialog::OnPaint();
|
|
}
|
|
}
|
|
|
|
// The system calls this to obtain the cursor to display while the user drags
|
|
// the minimized window.
|
|
HCURSOR CStillViewDlg::OnQueryDragIcon()
|
|
{
|
|
return (HCURSOR) m_hIcon;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CStillViewDlg DirectShow code and message handlers
|
|
|
|
|
|
BOOL CStillViewDlg::OnInitDialog()
|
|
{
|
|
CDialog::OnInitDialog();
|
|
|
|
// Add "About..." menu item to system menu.
|
|
|
|
// IDM_ABOUTBOX must be in the system command range.
|
|
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
|
|
ASSERT(IDM_ABOUTBOX < 0xF000);
|
|
|
|
CMenu* pSysMenu = GetSystemMenu(FALSE);
|
|
if (pSysMenu != NULL)
|
|
{
|
|
CString strAboutMenu;
|
|
strAboutMenu.LoadString(IDS_ABOUTBOX);
|
|
if (!strAboutMenu.IsEmpty())
|
|
{
|
|
pSysMenu->AppendMenu(MF_SEPARATOR);
|
|
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
|
|
}
|
|
}
|
|
|
|
// Set the icon for this dialog. The framework does this automatically
|
|
// when the application's main window is not a dialog
|
|
SetIcon(m_hIcon, TRUE); // Set big icon
|
|
SetIcon(m_hIcon, FALSE); // Set small icon
|
|
|
|
// Initialize COM
|
|
CoInitialize(NULL);
|
|
|
|
// Initialize DirectShow and query for needed interfaces
|
|
HRESULT hr = InitDirectShow();
|
|
if(FAILED(hr))
|
|
{
|
|
RetailOutput(TEXT("Failed to initialize DirectShow! hr=0x%x\r\n"), hr);
|
|
return FALSE;
|
|
}
|
|
|
|
// Since we're embedding video in a child window of a dialog,
|
|
// we must set the WS_CLIPCHILDREN style to prevent the bounding
|
|
// rectangle from drawing over our video frames.
|
|
m_Screen.ModifyStyle(0, WS_CLIPCHILDREN);
|
|
|
|
// Propagate the files list and select the first item
|
|
InitMediaDirectory();
|
|
|
|
return TRUE; // return TRUE unless you set the focus to a control
|
|
}
|
|
|
|
|
|
void CStillViewDlg::InitMediaDirectory(void)
|
|
{
|
|
// Fill the media file list, starting with the directory passed
|
|
// on the command line. If no directory is passed, then read the
|
|
// default media path for the DirectX SDK.
|
|
TCHAR szDir[MAX_PATH];
|
|
LONG lResult=0;
|
|
|
|
if (theApp.m_lpCmdLine[0] == L'\0')
|
|
{
|
|
lResult = GetDXMediaPath(szDir);
|
|
|
|
// If the DirectX SDK is not installed, use the Windows
|
|
// directory instead.
|
|
if (lResult != 0)
|
|
{
|
|
GetWindowsDirectory(szDir, MAX_PATH);
|
|
_tcscat(szDir, _T("\\") );
|
|
}
|
|
}
|
|
else
|
|
_tcscpy(szDir, theApp.m_lpCmdLine);
|
|
|
|
TCHAR szPathMsg[MAX_PATH];
|
|
wsprintf(szPathMsg, TEXT("Media directory: %s\0"), szDir);
|
|
m_StrMediaPath.SetWindowText(szPathMsg);
|
|
|
|
// Save current directory name
|
|
wsprintf(m_szCurrentDir, TEXT("%s\0"), szDir);
|
|
|
|
m_EditMediaDir.SetLimitText(MAX_PATH);
|
|
m_EditMediaDir.SetWindowText(szDir);
|
|
|
|
FillFileList(szDir);
|
|
}
|
|
|
|
|
|
LONG CStillViewDlg::GetDXMediaPath(TCHAR *szPath)
|
|
{
|
|
HKEY hKey;
|
|
DWORD dwType, dwSize = MAX_PATH;
|
|
|
|
// Open the appropriate registry key
|
|
LONG lResult = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
|
_T("Software\\Microsoft\\DirectX SDK"),
|
|
0, KEY_READ, &hKey );
|
|
if( ERROR_SUCCESS != lResult )
|
|
return -1;
|
|
|
|
lResult = RegQueryValueEx( hKey, _T("DX81SDK Samples Path"), NULL,
|
|
&dwType, (BYTE*)szPath, &dwSize );
|
|
RegCloseKey( hKey );
|
|
|
|
if( ERROR_SUCCESS != lResult )
|
|
return -1;
|
|
|
|
_tcscat( szPath, _T("\\Media\\") );
|
|
return 0;
|
|
}
|
|
|
|
|
|
void CStillViewDlg::OnButtonSetMediadir()
|
|
{
|
|
TCHAR szEditPath[MAX_PATH];
|
|
DWORD dwAttr;
|
|
|
|
// Read the string in the media directory edit box.
|
|
m_EditMediaDir.GetWindowText(szEditPath, MAX_PATH);
|
|
|
|
// Is this a valid directory name?
|
|
dwAttr = GetFileAttributes(szEditPath);
|
|
if ((dwAttr == (DWORD) -1) || (! (dwAttr & FILE_ATTRIBUTE_DIRECTORY)))
|
|
{
|
|
MessageBox(TEXT("Please enter a valid directory name."), TEXT("Media error"));
|
|
return;
|
|
}
|
|
|
|
// User has specified a valid media directory.
|
|
// Update the current path string.
|
|
TCHAR szPathMsg[MAX_PATH];
|
|
wsprintf(szPathMsg, TEXT("Media directory: %s\0"), szEditPath);
|
|
m_StrMediaPath.SetWindowText(szPathMsg);
|
|
|
|
// Save current directory name. Append the trailing '\' to match
|
|
// the string created by GetDXSDKMediaPath() (if not present)
|
|
int nLength = _tcslen(szEditPath);
|
|
if (szEditPath[nLength - 1] != TEXT('\\'))
|
|
wsprintf(m_szCurrentDir, TEXT("%s\\\0"), szEditPath);
|
|
|
|
// Propagate the files list and select the first item
|
|
FillFileList(szEditPath);
|
|
}
|
|
|
|
|
|
void CStillViewDlg::FillFileList(LPTSTR pszRootDir)
|
|
{
|
|
UINT attr = 0;
|
|
|
|
// Clear current file list
|
|
m_ListFiles.ResetContent();
|
|
|
|
// Clear filter/pin/event information listboxes
|
|
m_ListFilters.ResetContent();
|
|
m_ListPinsInput.ResetContent();
|
|
m_ListPinsOutput.ResetContent();
|
|
|
|
::SetCurrentDirectory(pszRootDir);
|
|
|
|
// Add all of our known supported media types to the file list.
|
|
// Add files of each type in order.
|
|
for (int i=0; i < NUM_MEDIA_TYPES; i++)
|
|
{
|
|
m_ListFiles.Dir(attr, TypeInfo[i].pszType);
|
|
}
|
|
|
|
// Update list box title with number of items added
|
|
int nItems = m_ListFiles.GetCount();
|
|
TCHAR szTitle[64];
|
|
wsprintf(szTitle, TEXT("Media files (%d found)"), nItems);
|
|
m_StrFileList.SetWindowText(szTitle);
|
|
|
|
// If there are no messages, inform the user
|
|
if (nItems == 0)
|
|
MessageBox(TEXT("This is a valid directory, but it does not contain any ")
|
|
TEXT("media files expected by this application (BMP, GIF, JPG, TGA)."),
|
|
TEXT("No still image files!"));
|
|
|
|
// Automatically select the first file in the list once
|
|
// the dialog is displayed.
|
|
PostMessage(WM_FIRSTFILE, 0, 0L);
|
|
m_nCurrentFileSelection = -1; // No selection yet
|
|
}
|
|
|
|
|
|
void CStillViewDlg::OnClose()
|
|
{
|
|
// Release DirectShow interfaces
|
|
FreeDirectShow();
|
|
|
|
// Release COM
|
|
CoUninitialize();
|
|
|
|
CDialog::OnClose();
|
|
}
|
|
|
|
void CStillViewDlg::OnDestroy()
|
|
{
|
|
FreeDirectShow();
|
|
|
|
CDialog::OnDestroy();
|
|
}
|
|
|
|
void CStillViewDlg::OnSelectFile()
|
|
{
|
|
HRESULT hr;
|
|
TCHAR szFilename[MAX_PATH];
|
|
|
|
// If this is the currently selected file, do nothing
|
|
int nItem = m_ListFiles.GetCurSel();
|
|
if (nItem == m_nCurrentFileSelection)
|
|
return;
|
|
|
|
// Remember the current selection to speed double-click processing
|
|
m_nCurrentFileSelection = nItem;
|
|
|
|
// Read file name from list box
|
|
m_ListFiles.GetText(nItem, szFilename);
|
|
|
|
// First release any existing interfaces
|
|
ResetDirectShow();
|
|
|
|
// Clear filter/pin/event information listboxes
|
|
m_ListFilters.ResetContent();
|
|
m_ListPinsInput.ResetContent();
|
|
m_ListPinsOutput.ResetContent();
|
|
|
|
// Load the selected media file
|
|
hr = PrepareMedia(szFilename);
|
|
if (FAILED(hr))
|
|
{
|
|
MessageBeep(0);
|
|
return;
|
|
}
|
|
|
|
// Display useful information about this file
|
|
DisplayFileInfo(szFilename);
|
|
DisplayImageInfo();
|
|
|
|
// Enumerate and display filters in graph
|
|
hr = EnumFilters();
|
|
|
|
// Select the first filter in the list to display pin info
|
|
m_ListFilters.SetCurSel(0);
|
|
OnSelchangeListFilters();
|
|
}
|
|
|
|
|
|
HRESULT CStillViewDlg::PrepareMedia(LPTSTR lpszMovie)
|
|
{
|
|
USES_CONVERSION;
|
|
HRESULT hr = S_OK;
|
|
|
|
// Allow DirectShow to create the FilterGraph for this media file
|
|
hr = m_pGB->RenderFile(T2W(lpszMovie), NULL);
|
|
if (FAILED(hr)) {
|
|
RetailOutput(TEXT("*** Failed(%08lx) in RenderFile(%s)!\r\n"),
|
|
hr, lpszMovie);
|
|
return hr;
|
|
}
|
|
|
|
// Set the message drain of the video window to point to our main
|
|
// application window.
|
|
hr = m_pVW->put_MessageDrain((OAHWND) m_hWnd);
|
|
|
|
// Have the graph signal event via window callbacks
|
|
hr = m_pME->SetNotifyWindow((OAHWND)m_hWnd, WM_GRAPHNOTIFY, 0);
|
|
|
|
// Place video window within the bounding rectangle
|
|
CenterVideo();
|
|
|
|
// Finish configuring video window
|
|
hr = m_pVW->put_WindowStyle(WS_CHILD);
|
|
hr = m_pVW->put_Owner((OAHWND) m_Screen.GetSafeHwnd());
|
|
hr = m_pVW->SetWindowForeground(-1);
|
|
|
|
// Display the media file's first frame, which is the only frame
|
|
// for a still image.
|
|
hr = m_pMC->Pause();
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CStillViewDlg::InitDirectShow(void)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
// Zero interfaces (sanity check)
|
|
m_pVW = NULL;
|
|
m_pBV = NULL;
|
|
|
|
JIF(CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC, IID_IGraphBuilder, (void **)&m_pGB));
|
|
JIF(m_pGB->QueryInterface(IID_IMediaControl, (void **)&m_pMC));
|
|
JIF(m_pGB->QueryInterface(IID_IBasicVideo, (void **)&m_pBV));
|
|
JIF(m_pGB->QueryInterface(IID_IVideoWindow, (void **)&m_pVW));
|
|
JIF(m_pGB->QueryInterface(IID_IMediaEventEx, (void **)&m_pME));
|
|
|
|
return S_OK;
|
|
|
|
CLEANUP:
|
|
FreeDirectShow();
|
|
return(hr);
|
|
}
|
|
|
|
HRESULT CStillViewDlg::FreeDirectShow(void)
|
|
{
|
|
HRESULT hr=S_OK;
|
|
|
|
// Hide video window and remove owner. This is not necessary here,
|
|
// since we are about to destroy the filter graph, but it is included
|
|
// for demonstration purposes. Remember to hide the video window and
|
|
// clear its owner when destroying a window that plays video.
|
|
if(m_pVW)
|
|
{
|
|
hr = m_pVW->put_Visible(OAFALSE);
|
|
hr = m_pVW->put_Owner(NULL);
|
|
}
|
|
|
|
SAFE_RELEASE(m_pMC);
|
|
SAFE_RELEASE(m_pVW);
|
|
SAFE_RELEASE(m_pBV);
|
|
SAFE_RELEASE(m_pME);
|
|
SAFE_RELEASE(m_pGB);
|
|
|
|
return hr;
|
|
}
|
|
|
|
void CStillViewDlg::ResetDirectShow(void)
|
|
{
|
|
// Destroy the current filter graph its filters.
|
|
FreeDirectShow();
|
|
|
|
// Reinitialize graph builder and query for interfaces
|
|
InitDirectShow();
|
|
}
|
|
|
|
void CStillViewDlg::CenterVideo(void)
|
|
{
|
|
LONG width, height;
|
|
HRESULT hr;
|
|
|
|
if (!m_pVW)
|
|
return;
|
|
|
|
// Read coordinates of video container window
|
|
RECT rc;
|
|
m_Screen.GetClientRect(&rc);
|
|
width = rc.right - rc.left;
|
|
height = rc.bottom - rc.top;
|
|
|
|
// Ignore the video's original size and stretch to fit bounding rectangle
|
|
hr = m_pVW->SetWindowPosition(rc.left, rc.top, width, height);
|
|
if (FAILED(hr))
|
|
{
|
|
RetailOutput(TEXT("Failed to set window position! hr=0x%x\r\n"), hr);
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
LRESULT CStillViewDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
// Field notifications from the DirectShow filter graph manager
|
|
// and those posted by the application
|
|
switch (message)
|
|
{
|
|
case WM_GRAPHNOTIFY:
|
|
HandleGraphEvent();
|
|
break;
|
|
|
|
case WM_PLAYFILE:
|
|
PlaySelectedFile();
|
|
break;
|
|
|
|
case WM_NEXTFILE:
|
|
PlayNextFile();
|
|
break;
|
|
|
|
case WM_PREVIOUSFILE:
|
|
PlayPreviousFile();
|
|
break;
|
|
|
|
case WM_FIRSTFILE:
|
|
// Select the first item in the list
|
|
m_ListFiles.SetCurSel(0);
|
|
OnSelectFile();
|
|
break;
|
|
}
|
|
|
|
// Pass along this message to the video window, which exists as a child
|
|
// of the m_Screen window. This method should be used by windows that
|
|
// make a renderer window a child window. It forwards significant messages
|
|
// to the child window that the child window would not otherwise receive.
|
|
if (m_pVW)
|
|
{
|
|
m_pVW->NotifyOwnerMessage((LONG_PTR) m_hWnd, message, wParam, lParam);
|
|
}
|
|
|
|
return CDialog::WindowProc(message, wParam, lParam);
|
|
}
|
|
|
|
|
|
HRESULT CStillViewDlg::HandleGraphEvent(void)
|
|
{
|
|
LONG evCode, evParam1, evParam2;
|
|
HRESULT hr=S_OK;
|
|
|
|
while(SUCCEEDED(m_pME->GetEvent(&evCode, (LONG_PTR *) &evParam1,
|
|
(LONG_PTR *) &evParam2, 0)))
|
|
{
|
|
// Spin through the events
|
|
hr = m_pME->FreeEventParams(evCode, evParam1, evParam2);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
void CStillViewDlg::PlaySelectedFile()
|
|
{
|
|
OnSelectFile();
|
|
}
|
|
|
|
void CStillViewDlg::OnDeltaposSpinFiles(NMHDR* pNMHDR, LRESULT* pResult)
|
|
{
|
|
NM_UPDOWN* pNMUpDown = (NM_UPDOWN*)pNMHDR;
|
|
|
|
if (pNMUpDown->iDelta > 0)
|
|
PostMessage(WM_NEXTFILE, 0, 0L);
|
|
else
|
|
PostMessage(WM_PREVIOUSFILE, 0, 0L);
|
|
|
|
*pResult = 0;
|
|
}
|
|
|
|
void CStillViewDlg::PlayNextFile(void)
|
|
{
|
|
int nItems = m_ListFiles.GetCount();
|
|
|
|
// Return if the list is empty
|
|
if (!nItems)
|
|
return;
|
|
|
|
int nCurSel = m_ListFiles.GetCurSel();
|
|
int nNewSel = (nCurSel + 1) % nItems;
|
|
|
|
// Select the next item in the list, wrapping to top if needed
|
|
m_ListFiles.SetCurSel(nNewSel);
|
|
OnSelectFile();
|
|
}
|
|
|
|
|
|
void CStillViewDlg::PlayPreviousFile(void)
|
|
{
|
|
int nItems = m_ListFiles.GetCount();
|
|
|
|
// Return if the list is empty
|
|
if (!nItems)
|
|
return;
|
|
|
|
int nCurSel = m_ListFiles.GetCurSel();
|
|
int nNewSel = nCurSel - 1;
|
|
|
|
// If moved off top of list, select last item in list
|
|
if (nNewSel < 0)
|
|
nNewSel = nItems - 1;
|
|
|
|
// Select the next item in the list, wrapping to top if needed
|
|
m_ListFiles.SetCurSel(nNewSel);
|
|
OnSelectFile();
|
|
}
|
|
|
|
|
|
HRESULT CStillViewDlg::EnumFilters (void)
|
|
{
|
|
USES_CONVERSION;
|
|
|
|
HRESULT hr;
|
|
IEnumFilters *pEnum = NULL;
|
|
IBaseFilter *pFilter = NULL;
|
|
ULONG cFetched;
|
|
|
|
// Clear filters list box
|
|
m_ListFilters.ResetContent();
|
|
|
|
// Get filter enumerator
|
|
hr = m_pGB->EnumFilters(&pEnum);
|
|
if (FAILED(hr))
|
|
{
|
|
m_ListFilters.AddString(TEXT("<ERROR>"));
|
|
return hr;
|
|
}
|
|
|
|
// Enumerate all filters in the graph
|
|
while(pEnum->Next(1, &pFilter, &cFetched) == S_OK)
|
|
{
|
|
FILTER_INFO FilterInfo;
|
|
TCHAR szName[256];
|
|
|
|
hr = pFilter->QueryFilterInfo(&FilterInfo);
|
|
if (FAILED(hr))
|
|
{
|
|
m_ListFilters.AddString(TEXT("<ERROR>"));
|
|
}
|
|
else
|
|
{
|
|
// Add the filter name to the filters listbox
|
|
lstrcpy(szName, W2T(FilterInfo.achName));
|
|
m_ListFilters.AddString(szName);
|
|
|
|
FilterInfo.pGraph->Release();
|
|
}
|
|
pFilter->Release();
|
|
}
|
|
pEnum->Release();
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//
|
|
// The GraphBuilder interface provides a FindFilterByName() method,
|
|
// which provides similar functionality to the method below.
|
|
// This local method is provided for educational purposes.
|
|
//
|
|
IBaseFilter *CStillViewDlg::FindFilterFromName(LPTSTR szNameToFind)
|
|
{
|
|
USES_CONVERSION;
|
|
|
|
HRESULT hr;
|
|
IEnumFilters *pEnum = NULL;
|
|
IBaseFilter *pFilter = NULL;
|
|
ULONG cFetched;
|
|
BOOL bFound = FALSE;
|
|
|
|
// Get filter enumerator
|
|
hr = m_pGB->EnumFilters(&pEnum);
|
|
if (FAILED(hr))
|
|
return NULL;
|
|
|
|
// Enumerate all filters in the graph
|
|
while((pEnum->Next(1, &pFilter, &cFetched) == S_OK) && (!bFound))
|
|
{
|
|
FILTER_INFO FilterInfo;
|
|
TCHAR szName[256];
|
|
|
|
hr = pFilter->QueryFilterInfo(&FilterInfo);
|
|
if (FAILED(hr))
|
|
{
|
|
pFilter->Release();
|
|
pEnum->Release();
|
|
return NULL;
|
|
}
|
|
|
|
// Compare this filter's name with the one we want
|
|
lstrcpy(szName, W2T(FilterInfo.achName));
|
|
if (! lstrcmp(szName, szNameToFind))
|
|
{
|
|
bFound = TRUE;
|
|
}
|
|
|
|
FilterInfo.pGraph->Release();
|
|
|
|
// If we found the right filter, don't release its interface.
|
|
// The caller will use it and release it later.
|
|
if (!bFound)
|
|
pFilter->Release();
|
|
else
|
|
break;
|
|
}
|
|
pEnum->Release();
|
|
|
|
return (bFound ? pFilter : NULL);
|
|
}
|
|
|
|
|
|
HRESULT CStillViewDlg::EnumPins(IBaseFilter *pFilter, PIN_DIRECTION PinDir,
|
|
CListBox& Listbox)
|
|
{
|
|
HRESULT hr;
|
|
IEnumPins *pEnum = NULL;
|
|
IPin *pPin = NULL;
|
|
|
|
// Clear the specified listbox (input or output)
|
|
Listbox.ResetContent();
|
|
|
|
// Get pin enumerator
|
|
hr = pFilter->EnumPins(&pEnum);
|
|
if (FAILED(hr))
|
|
{
|
|
Listbox.AddString(TEXT("<ERROR>"));
|
|
return hr;
|
|
}
|
|
|
|
// Enumerate all pins on this filter
|
|
while(pEnum->Next(1, &pPin, 0) == S_OK)
|
|
{
|
|
PIN_DIRECTION PinDirThis;
|
|
|
|
hr = pPin->QueryDirection(&PinDirThis);
|
|
if (FAILED(hr))
|
|
{
|
|
Listbox.AddString(TEXT("<ERROR>"));
|
|
pPin->Release();
|
|
continue;
|
|
}
|
|
|
|
// Does the pin's direction match the requested direction?
|
|
if (PinDir == PinDirThis)
|
|
{
|
|
PIN_INFO pininfo={0};
|
|
|
|
// Direction matches, so add pin name to listbox
|
|
hr = pPin->QueryPinInfo(&pininfo);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
CString str(pininfo.achName);
|
|
Listbox.AddString(str);
|
|
}
|
|
|
|
// The pininfo structure contains a reference to an IBaseFilter,
|
|
// so you must release its reference to prevent resource a leak.
|
|
pininfo.pFilter->Release();
|
|
}
|
|
pPin->Release();
|
|
}
|
|
pEnum->Release();
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
void CStillViewDlg::OnSelchangeListFilters()
|
|
{
|
|
HRESULT hr;
|
|
IBaseFilter *pFilter = NULL;
|
|
TCHAR szNameToFind[128];
|
|
|
|
// Read the current filter name from the list box
|
|
int nCurSel = m_ListFilters.GetCurSel();
|
|
m_ListFilters.GetText(nCurSel, szNameToFind);
|
|
|
|
// Read the current list box name and find it in the graph
|
|
pFilter = FindFilterFromName(szNameToFind);
|
|
if (!pFilter)
|
|
return;
|
|
|
|
// Now that we have filter information, enumerate pins by direction
|
|
// and add their names to the appropriate listboxes
|
|
hr = EnumPins(pFilter, PINDIR_INPUT, m_ListPinsInput);
|
|
hr = EnumPins(pFilter, PINDIR_OUTPUT, m_ListPinsOutput);
|
|
|
|
// Find out if this filter supports a property page
|
|
if (SupportsPropertyPage(pFilter))
|
|
m_ButtonProperties.EnableWindow(TRUE);
|
|
else
|
|
m_ButtonProperties.EnableWindow(FALSE);
|
|
|
|
// Must release the filter interface returned from FindFilterByName()
|
|
pFilter->Release();
|
|
}
|
|
|
|
|
|
BOOL CStillViewDlg::DisplayImageInfo(void)
|
|
{
|
|
HRESULT hr;
|
|
long lWidth, lHeight;
|
|
|
|
if (!m_pBV)
|
|
return FALSE;
|
|
|
|
hr = m_pBV->GetVideoSize(&lWidth, &lHeight);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
TCHAR szSize[64];
|
|
wsprintf(szSize, TEXT("Image size: %d x %d\0"), lWidth, lHeight);
|
|
m_StrImageSize.SetWindowText(szSize);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CStillViewDlg::DisplayFileInfo(LPTSTR szFile)
|
|
{
|
|
HANDLE hFile;
|
|
LONGLONG llSize=0;
|
|
DWORD dwSizeLow=0, dwSizeHigh=0;
|
|
TCHAR szScrap[64];
|
|
|
|
// Open the specified file to read size and date information
|
|
hFile = CreateFile(szFile,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,
|
|
(DWORD) 0, NULL);
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
RetailOutput(TEXT("*** Failed(0x%x) to open file (to read size)!\r\n"),
|
|
GetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
dwSizeLow = GetFileSize(hFile, &dwSizeHigh);
|
|
if ((dwSizeLow == 0xFFFFFFFF) && (GetLastError() != NO_ERROR))
|
|
{
|
|
RetailOutput(TEXT("*** Error(0x%x) reading file size!\r\n"),
|
|
GetLastError());
|
|
CloseHandle(hFile);
|
|
return FALSE;
|
|
}
|
|
|
|
// Large files will also use the upper DWORD to report size.
|
|
// Add them together for the true size if necessary.
|
|
if (dwSizeHigh)
|
|
llSize = (dwSizeHigh << 16) + dwSizeLow;
|
|
else
|
|
llSize = dwSizeLow;
|
|
|
|
// Read date information
|
|
BY_HANDLE_FILE_INFORMATION fi;
|
|
if (GetFileInformationByHandle(hFile, &fi))
|
|
{
|
|
CTime time(fi.ftLastWriteTime);
|
|
|
|
wsprintf(szScrap, TEXT("Date: %02d/%02d/%d\0"),
|
|
time.GetMonth(), time.GetDay(), time.GetYear());
|
|
m_StrFileDate.SetWindowText(szScrap);
|
|
|
|
// Set time. Note that 12am-12:59am will show as "00" instead of 12.
|
|
wsprintf(szScrap, TEXT("Time: %02d:%02d %s\0"),
|
|
(time.GetHour() % 12), time.GetMinute(),
|
|
((time.GetHour() > 12) ? TEXT("pm\0") : TEXT("am\0")));
|
|
m_StrFileTime.SetWindowText(szScrap);
|
|
}
|
|
|
|
CloseHandle(hFile);
|
|
|
|
// Update size/date windows
|
|
wsprintf(szScrap, TEXT("Size: %d bytes\0"), dwSizeLow);
|
|
m_StrFileSize.SetWindowText(szScrap);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL CStillViewDlg::SupportsPropertyPage(IBaseFilter *pFilter)
|
|
{
|
|
HRESULT hr;
|
|
TCHAR szNameToFind[128];
|
|
ISpecifyPropertyPages *pSpecify;
|
|
|
|
// Read the current filter name from the list box
|
|
int nCurSel = m_ListFilters.GetCurSel();
|
|
m_ListFilters.GetText(nCurSel, szNameToFind);
|
|
|
|
// Discover if this filter contains a property page
|
|
hr = pFilter->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pSpecify);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
pSpecify->Release();
|
|
return TRUE;
|
|
}
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
void CStillViewDlg::OnButtonProppage()
|
|
{
|
|
HRESULT hr;
|
|
IBaseFilter *pFilter = NULL;
|
|
TCHAR szNameToFind[128];
|
|
ISpecifyPropertyPages *pSpecify;
|
|
|
|
// Read the current filter name from the list box
|
|
int nCurSel = m_ListFilters.GetCurSel();
|
|
m_ListFilters.GetText(nCurSel, szNameToFind);
|
|
|
|
// Read the current list box name and find it in the graph
|
|
pFilter = FindFilterFromName(szNameToFind);
|
|
if (!pFilter)
|
|
return;
|
|
|
|
// Discover if this filter contains a property page
|
|
hr = pFilter->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pSpecify);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
do
|
|
{
|
|
FILTER_INFO FilterInfo;
|
|
hr = pFilter->QueryFilterInfo(&FilterInfo);
|
|
if (FAILED(hr))
|
|
break;
|
|
|
|
CAUUID caGUID;
|
|
hr = pSpecify->GetPages(&caGUID);
|
|
if (FAILED(hr))
|
|
break;
|
|
|
|
pSpecify->Release();
|
|
|
|
// Display the filter's property page
|
|
OleCreatePropertyFrame(
|
|
m_hWnd, // Parent window
|
|
0, // x (Reserved)
|
|
0, // y (Reserved)
|
|
FilterInfo.achName, // Caption for the dialog box
|
|
1, // Number of filters
|
|
(IUnknown **)&pFilter, // Pointer to the filter
|
|
caGUID.cElems, // Number of property pages
|
|
caGUID.pElems, // Pointer to property page CLSIDs
|
|
0, // Locale identifier
|
|
0, // Reserved
|
|
NULL // Reserved
|
|
);
|
|
CoTaskMemFree(caGUID.pElems);
|
|
FilterInfo.pGraph->Release();
|
|
|
|
} while(0);
|
|
}
|
|
|
|
pFilter->Release();
|
|
}
|
|
|
|
|
|
void CStillViewDlg::OnDblclkListFilters()
|
|
{
|
|
OnButtonProppage();
|
|
}
|
|
|
|
|
|
void RetailOutput(TCHAR *tszErr, ...)
|
|
{
|
|
TCHAR tszErrOut[MAX_PATH + 256];
|
|
|
|
va_list valist;
|
|
|
|
va_start(valist,tszErr);
|
|
wvsprintf(tszErrOut, tszErr, valist);
|
|
OutputDebugString(tszErrOut);
|
|
va_end (valist);
|
|
}
|
|
|
|
|
|
LONG CStillViewDlg::GetGraphEditPath(TCHAR *szPath)
|
|
{
|
|
HKEY hKey;
|
|
DWORD dwType, dwSize = MAX_PATH;
|
|
|
|
// Open the appropriate registry key
|
|
LONG lResult = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
|
_T("Software\\Microsoft\\Shared Tools\\Graphedit"),
|
|
0, KEY_READ, &hKey );
|
|
if( ERROR_SUCCESS != lResult )
|
|
return -1;
|
|
|
|
// Read the full path (including .exe name) for the GraphEdit tool
|
|
lResult = RegQueryValueEx( hKey, _T("Path"), NULL,
|
|
&dwType, (BYTE*)szPath, &dwSize );
|
|
RegCloseKey( hKey );
|
|
|
|
if( ERROR_SUCCESS != lResult )
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
void CStillViewDlg::OnButtonGraphedit()
|
|
{
|
|
TCHAR szFilename[128], szFile[MAX_PATH];
|
|
|
|
// Read file name from list box
|
|
int nItem = m_ListFiles.GetCurSel();
|
|
m_ListFiles.GetText(nItem, szFilename);
|
|
|
|
// Build the full file name with path. The path will already have
|
|
// a '\' appended to the end.
|
|
wsprintf(szFile, TEXT("%s%s\0"), m_szCurrentDir, szFilename);
|
|
|
|
// Launch GraphEdit for the selected file
|
|
//
|
|
// First look for a registry key containing its full path
|
|
TCHAR szPath[MAX_PATH];
|
|
|
|
LONG lResult = GetGraphEditPath(szPath);
|
|
|
|
// If the DirectX SDK is not installed, just look for GraphEdit
|
|
// anywhere in the system path.
|
|
if (lResult != 0)
|
|
{
|
|
wsprintf(szPath, TEXT("%s\0"), TEXT("graphedt\0"));
|
|
}
|
|
|
|
// Lauch GraphEdit using either the full tool path or just its name
|
|
HINSTANCE rc = ShellExecute(m_hWnd, TEXT("open\0"), szPath,
|
|
szFile, NULL, SW_SHOWNORMAL);
|
|
|
|
if (rc < (HINSTANCE) 32)
|
|
{
|
|
// Failed to start the app
|
|
if ((rc == (HINSTANCE) ERROR_FILE_NOT_FOUND) ||
|
|
(rc == (HINSTANCE) ERROR_PATH_NOT_FOUND))
|
|
{
|
|
MessageBox(TEXT("Couldn't find the GraphEdit application.\r\n\r\n")
|
|
TEXT("Please copy graphedt.exe to a directory on your path."),
|
|
TEXT("Can't find GraphEdit"));
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOL CStillViewDlg::OnEraseBkgnd(CDC *pDC)
|
|
{
|
|
// Intercept background erasing for the movie window, since the
|
|
// video renderer will keep the screen painted. Without this code,
|
|
// your video window might get painted over with gray (the default
|
|
// background brush) when it is obscured by another window and redrawn.
|
|
CRect rc;
|
|
|
|
// Get the bounding rectangle for the movie screen
|
|
m_Screen.GetWindowRect(&rc);
|
|
ScreenToClient(&rc);
|
|
|
|
// Exclude the clipping region occupied by our movie screen
|
|
pDC->ExcludeClipRect(&rc);
|
|
|
|
// Erase the remainder of the dialog as usual
|
|
return CDialog::OnEraseBkgnd(pDC);
|
|
}
|