3182 lines
90 KiB
C
3182 lines
90 KiB
C
/*-----------------------------------------------------------------------------+
|
|
| MCI.C |
|
|
| |
|
|
| This file contains the routines which the media player uses to interact with |
|
|
| the Media Control Interface (MCI). |
|
|
| |
|
|
| (C) Copyright Microsoft Corporation 1991. All rights reserved. |
|
|
| |
|
|
| Revision History |
|
|
| Oct-1992 MikeTri Ported to WIN32 / WIN16 common code |
|
|
| |
|
|
+-----------------------------------------------------------------------------*/
|
|
|
|
#undef NOGDICAPMASKS // CC_*, LC_*, PC_*, CP_*, TC_*, RC_
|
|
#undef NOSCROLL
|
|
#undef NOWINOFFSETS
|
|
#undef NODRAWTEXT
|
|
|
|
#include <windows.h>
|
|
#include <mmsystem.h>
|
|
#include <mmddk.h>
|
|
#include <stdlib.h>
|
|
#include <shellapi.h>
|
|
#include "digitalv.h"
|
|
#include "mpole.h"
|
|
#include "mplayer.h"
|
|
#include "ctrls.h"
|
|
#include "errprop.h"
|
|
#include "utils.h"
|
|
|
|
#ifndef MCI_MCIAVI_PLAY_WINDOW
|
|
// force MCIAVI to play windowed in play in place
|
|
#define MCI_MCIAVI_PLAY_WINDOW 0x01000000L
|
|
#endif
|
|
|
|
// gets the name of the current device
|
|
STATICDT SZCODE aszInfoProduct[] = TEXT("info zyzsmag product");
|
|
STATICDT SZCODE aszMMPName[] = TEXT("Microsoft Multimedia Movie Player");
|
|
|
|
//#ifdef CHICAGO_PRODUCT
|
|
#define NEW_MCI_DIALOG
|
|
//#endif
|
|
|
|
#ifdef NEW_MCI_DIALOG
|
|
|
|
STATICDT SZCODE aszMCIAVIOpt[] = TEXT("Software\\Microsoft\\Multimedia\\Video For Windows\\MCIAVI");
|
|
STATICDT SZCODE aszDefVideoOpt[] = TEXT("DefaultOptions");
|
|
|
|
//
|
|
// !!! Caution. These are stolen from \MCIAVI\graphic.h and are registry values
|
|
// for MCIAVI.
|
|
//
|
|
#define MCIAVIO_ZOOMBY2 0x00000100L
|
|
#define MCIAVIO_1QSCREENSIZE 0x00010000L
|
|
#define MCIAVIO_2QSCREENSIZE 0x00020000L
|
|
#define MCIAVIO_3QSCREENSIZE 0x00040000L
|
|
#define MCIAVIO_MAXWINDOWSIZE 0x00080000L
|
|
#define MCIAVIO_DEFWINDOWSIZE 0x00000000L
|
|
#define MCIAVIO_WINDOWSIZEMASK 0x000f0000L
|
|
|
|
#endif /* NEW_MCI_DIALOG */
|
|
|
|
extern HMENU ghMenu;
|
|
|
|
/*
|
|
* global variables
|
|
*
|
|
* <gwDeviceID> is the MCI device ID of the currently-open device, or NULL
|
|
* if no device is open. <gdwMediaLength> is the length of the entire medium
|
|
* in milliseconds. If <gwDeviceID> is not NULL, then:
|
|
* -- <gwNumTracks> is the number of tracks on the medium, or 0 if the
|
|
* medium doesn't support tracks
|
|
* -- <gwFirstTrack> is the number of the first track, currently constrained
|
|
* to be 0 or 1.
|
|
* -- <gadwTrackStart> is an array; the i-th element specifies the position
|
|
* of track i (starting from track 0), in milliseconds from the beginning
|
|
* of the medium
|
|
* -- <gfCanEject> is TRUE if the medium can be ejected
|
|
*
|
|
*/
|
|
|
|
UINT gwDeviceID; /* MCI device ID of the current device */
|
|
UINT gwDeviceType; /* DTMCI_ flags of current device */
|
|
DWORD gdwMediaLength; /* length in msec of the entire medium */
|
|
DWORD gdwMediaStart; /* start time in msec of medium */
|
|
UINT gwNumTracks; /* # of tracks in the medium */
|
|
UINT gwFirstTrack; /* # of first track */
|
|
DWORD NEAR * gadwTrackStart; /* array of track start positions */
|
|
DWORD gdwLastSeekToPosition; /* Last requested seek position */
|
|
int extHeight;
|
|
int extWidth;
|
|
|
|
STATICDT SZCODE aszMPlayerAlias[] = TEXT("zyzsmag");
|
|
STATICDT SZCODE aszStatusCommand[] = TEXT("status zyzsmag mode");
|
|
STATICDT SZCODE aszStatusWindow[] = TEXT("status zyzsmag window handle");
|
|
STATICDT SZCODE aszWindowShow[] = TEXT("window zyzsmag state show");
|
|
STATICDT SZCODE aszWindowHide[] = TEXT("window zyzsmag state hide");
|
|
STATICDT SZCODE aszSeekExactOn[] = TEXT("set zyzsmag seek exactly on");
|
|
STATICDT SZCODE aszSeekExactOff[] = TEXT("set zyzsmag seek exactly off");
|
|
STATICDT SZCODE aszSeekExact[] = TEXT("status zyzsmag seek exactly");
|
|
|
|
STATICDT SZCODE aszMCI[] = MCI_SECTION;
|
|
|
|
extern UINT gwCurScale; // either ID_FRAMES, ID_TIME, ID_TRACKS
|
|
|
|
//#define MCI_CONFIG 0x900 // these are not found in MMSYSTEM.H
|
|
//#define MCI_TEST 0x00000020L
|
|
|
|
HWND ghwndMCI = NULL; /* current window for window objects */
|
|
#ifdef NEW_MCI_DIALOG
|
|
RECT grcInitSize = { 0, 0, 0, 0 };
|
|
#endif
|
|
RECT grcSize; /* size of MCI object */
|
|
BOOL gfInPlayMCI = FALSE;
|
|
extern WNDPROC gfnMCIWndProc;
|
|
extern HWND ghwndSubclass;
|
|
|
|
|
|
|
|
/* Status mapping stuff:
|
|
*/
|
|
typedef struct _MCI_STATUS_MAPPING
|
|
{
|
|
WORD Mode;
|
|
WORD ResourceID;
|
|
LPTSTR pString;
|
|
}
|
|
MCI_STATUS_MAPPING, *PMCI_STATUS_MAPPING;
|
|
|
|
MCI_STATUS_MAPPING MCIStatusMapping[] =
|
|
{
|
|
{ MCI_MODE_NOT_READY, IDS_SSNOTREADY, NULL },
|
|
{ MCI_MODE_STOP, IDS_SSSTOPPED, NULL },
|
|
{ MCI_MODE_PLAY, IDS_SSPLAYING, NULL },
|
|
{ MCI_MODE_RECORD, IDS_SSRECORDING, NULL },
|
|
{ MCI_MODE_SEEK, IDS_SSSEEKING, NULL },
|
|
{ MCI_MODE_PAUSE, IDS_SSPAUSED, NULL },
|
|
{ MCI_MODE_OPEN, IDS_SSOPEN, NULL },
|
|
{ MCI_VD_MODE_PARK, IDS_SSPARKED, NULL },
|
|
{ 0, IDS_SSUNKNOWN, NULL }
|
|
};
|
|
|
|
static TCHAR szNULL[] = TEXT("");
|
|
|
|
/* Devices we know about, as they appear in system.ini, or the registry:
|
|
*/
|
|
SZCODE szCDAudio[] = TEXT("cdaudio");
|
|
SZCODE szVideoDisc[] = TEXT("videodisc");
|
|
SZCODE szSequencer[] = TEXT("sequencer");
|
|
SZCODE szVCR[] = TEXT("vcr");
|
|
SZCODE szWaveAudio[] = TEXT("waveaudio");
|
|
SZCODE szAVIVideo[] = TEXT("avivideo");
|
|
|
|
|
|
STRING_TO_ID_MAP DevToDevIDMap[] =
|
|
{
|
|
{ szCDAudio, DTMCI_CDAUDIO },
|
|
{ szVideoDisc, DTMCI_VIDEODISC },
|
|
{ szSequencer, DTMCI_SEQUENCER },
|
|
{ szVCR, DTMCI_VCR },
|
|
{ szWaveAudio, DTMCI_WAVEAUDIO },
|
|
{ szAVIVideo, DTMCI_AVIVIDEO }
|
|
};
|
|
|
|
|
|
void LoadStatusStrings(void);
|
|
STATICFN BOOL NEAR PASCAL CheckErrorMCI(DWORD dwRet);
|
|
extern LPTSTR FAR FileName(LPCTSTR szPath);
|
|
|
|
HPALETTE CopyPalette(HPALETTE hpal);
|
|
HANDLE PictureFromBitmap(HBITMAP hbm, HPALETTE hpal);
|
|
HANDLE FAR PASCAL PictureFromDib(HANDLE hdib, HPALETTE hpal);
|
|
HANDLE FAR PASCAL DibFromBitmap(HBITMAP hbm, HPALETTE hpal);
|
|
|
|
|
|
|
|
LONG_PTR FAR PASCAL _EXPORT MCIWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
|
|
|
//
|
|
// we will either send every command with a MCI_NOTIFY, or we will
|
|
// not.
|
|
//
|
|
|
|
#define F_NOTIFY MCI_NOTIFY
|
|
//#define F_NOTIFY 0
|
|
|
|
BOOL FAR PASCAL InitMCI(HANDLE hPrev, HANDLE hInst)
|
|
{
|
|
if (!hPrev)
|
|
{
|
|
WNDCLASS cls;
|
|
|
|
cls.lpszClassName = MCI_WINDOW_CLASS;
|
|
cls.lpfnWndProc = (WNDPROC)MCIWndProc;
|
|
cls.style = CS_HREDRAW | CS_VREDRAW | CS_SAVEBITS |
|
|
CS_DBLCLKS;
|
|
cls.hCursor = LoadCursor(NULL,IDC_ARROW);
|
|
cls.hIcon = NULL;
|
|
cls.lpszMenuName = NULL;
|
|
cls.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
|
|
cls.hInstance = hInst;
|
|
cls.cbClsExtra = 0;
|
|
cls.cbWndExtra = 0;
|
|
|
|
if (!RegisterClass(&cls))
|
|
return FALSE;
|
|
}
|
|
|
|
LoadStatusStrings();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/* LoadStatusStrings
|
|
*
|
|
* Fixes up the status-mapping table with pointers to strings loaded
|
|
* from resources. This need be called only on initialisation.
|
|
*
|
|
* 2 February 1994, andrewbe, hardly at all based on the original.
|
|
*/
|
|
void LoadStatusStrings(void)
|
|
{
|
|
int i;
|
|
TCHAR Buffer[80];
|
|
|
|
for( i = 0; i < sizeof(MCIStatusMapping) / sizeof(*MCIStatusMapping); i++ )
|
|
{
|
|
if( LOADSTRING( MCIStatusMapping[i].ResourceID, Buffer ) )
|
|
{
|
|
MCIStatusMapping[i].pString = AllocStr( Buffer );
|
|
|
|
if( MCIStatusMapping[i].pString == NULL )
|
|
{
|
|
MCIStatusMapping[i].pString = szNULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DPF0( "LoadStatusStrings failed to load string ID %d\n", MCIStatusMapping[i].ResourceID );
|
|
|
|
MCIStatusMapping[i].pString = szNULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* MapModeToStatusString
|
|
*
|
|
* Given an MCI mode, scans the mapping table to find the corresponding string.
|
|
* In the event that an unknown mode is passed in (which shouldn't really happen),
|
|
* the last string in the mapping table is returned.
|
|
*
|
|
* 2 February 1994, andrewbe
|
|
*/
|
|
LPTSTR MapModeToStatusString( WORD Mode )
|
|
{
|
|
int i;
|
|
|
|
for( i = 0; i < sizeof(MCIStatusMapping) / sizeof(*MCIStatusMapping); i++ )
|
|
{
|
|
if( MCIStatusMapping[i].Mode == Mode )
|
|
{
|
|
return MCIStatusMapping[i].pString;
|
|
}
|
|
}
|
|
|
|
/* The following assumes that the last in the array of status mappings
|
|
* contains a pointer to the "(unknown)" string:
|
|
*/
|
|
return MCIStatusMapping[sizeof(MCIStatusMapping) / sizeof(*MCIStatusMapping) - 1].pString;
|
|
}
|
|
|
|
|
|
/******************************Public*Routine******************************\
|
|
* IsCdromTrackAudio
|
|
*
|
|
* Filched from CD Player
|
|
*
|
|
\**************************************************************************/
|
|
BOOL IsCdromTrackAudio(
|
|
MCIDEVICEID DevHandle,
|
|
int iTrackNumber)
|
|
{
|
|
MCI_STATUS_PARMS mciStatus;
|
|
|
|
ZeroMemory( &mciStatus, sizeof(mciStatus) );
|
|
mciStatus.dwItem = MCI_CDA_STATUS_TYPE_TRACK;
|
|
mciStatus.dwTrack = iTrackNumber + 1;
|
|
|
|
mciSendCommand( DevHandle, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK,
|
|
(DWORD_PTR)(LPVOID)&mciStatus);
|
|
|
|
return mciStatus.dwReturn == MCI_CDA_TRACK_AUDIO;
|
|
}
|
|
|
|
|
|
/* IsCdromDataOnly
|
|
*
|
|
* It seems that MCICDA can handle CDs with some audio tracks, so just check
|
|
* whether there is at least one audio track.
|
|
*
|
|
*/
|
|
BOOL IsCdromDataOnly()
|
|
{
|
|
MCI_STATUS_PARMS mciStatus;
|
|
DWORD dw;
|
|
DWORD iTrack;
|
|
DWORD_PTR NumTracks;
|
|
|
|
/* gwNumTracks is set in UpdateMCI, but it hasn't been called
|
|
* at this stage in the proceedings, and I'm not about to start
|
|
* changing the order that things are done and bring this whole
|
|
* flimsy edifice tumbling down.
|
|
*/
|
|
ZeroMemory( &mciStatus, sizeof(mciStatus) );
|
|
mciStatus.dwItem = MCI_STATUS_NUMBER_OF_TRACKS;
|
|
dw = mciSendCommand(gwDeviceID, MCI_STATUS, MCI_STATUS_ITEM,
|
|
(DWORD_PTR)&mciStatus);
|
|
|
|
/* Do NOT set gwNumtracks here, because this will result in an
|
|
* access violation in CalcTicsOfDoom. What a nightmare!
|
|
*/
|
|
NumTracks = mciStatus.dwReturn;
|
|
|
|
/* If there was an error or there are no tracks, let's hope MCICDA
|
|
* will throw a wobbly.
|
|
*/
|
|
if (dw != 0 || NumTracks == 0)
|
|
return FALSE;
|
|
|
|
/* Now run through the tracks until we find an audio one:
|
|
*/
|
|
for (iTrack = 0; iTrack < NumTracks - 1; iTrack++)
|
|
{
|
|
if (IsCdromTrackAudio(gwDeviceID, iTrack))
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
#ifdef NEW_MCI_DIALOG
|
|
//
|
|
// Read the MCIAVI playback options from the registry
|
|
//
|
|
DWORD ReadOptionsFromReg(void)
|
|
{
|
|
HKEY hkVideoOpt;
|
|
DWORD dwType;
|
|
DWORD dwOpt;
|
|
DWORD cbSize;
|
|
|
|
if(RegCreateKey(HKEY_CURRENT_USER, (LPTSTR)aszMCIAVIOpt, &hkVideoOpt))
|
|
return 0 ;
|
|
|
|
cbSize = sizeof(DWORD);
|
|
if (RegQueryValueEx(hkVideoOpt, (LPTSTR)aszDefVideoOpt, NULL, &dwType,
|
|
(LPBYTE)&dwOpt,&cbSize ))
|
|
{
|
|
dwOpt = 0;
|
|
RegSetValueEx(hkVideoOpt, (LPTSTR)aszDefVideoOpt, 0, REG_DWORD,
|
|
(LPBYTE)&dwOpt, sizeof(DWORD));
|
|
}
|
|
RegCloseKey(hkVideoOpt);
|
|
return dwOpt;
|
|
}
|
|
|
|
//
|
|
// Obey the registry default sizing of Zoom by 2 and Fixed screen %. Takes the
|
|
// registry values for MCIAVI and a rect, and either zooms it by 2 or replaces
|
|
// it with a constant size or leaves it alone.
|
|
//
|
|
void FAR PASCAL AlterRectUsingDefaults(LPRECT lprc)
|
|
{
|
|
DWORD dwOptions;
|
|
|
|
// This is only an MCIAVI hack.
|
|
if ((gwDeviceType & DTMCI_DEVICE) != DTMCI_AVIVIDEO)
|
|
return;
|
|
|
|
dwOptions = ReadOptionsFromReg();
|
|
|
|
if (dwOptions & MCIAVIO_ZOOMBY2)
|
|
SetRect(lprc, 0, 0, lprc->right*2, lprc->bottom*2);
|
|
|
|
else if (dwOptions & MCIAVIO_WINDOWSIZEMASK) {
|
|
lprc->right = GetSystemMetrics (SM_CXSCREEN);
|
|
lprc->bottom = GetSystemMetrics (SM_CYSCREEN);
|
|
switch(dwOptions & MCIAVIO_WINDOWSIZEMASK)
|
|
{
|
|
case MCIAVIO_1QSCREENSIZE:
|
|
SetRect(lprc, 0, 0, lprc->right/4, lprc->bottom/4);
|
|
break;
|
|
case MCIAVIO_2QSCREENSIZE:
|
|
SetRect(lprc, 0, 0, lprc->right/2, lprc->bottom/2);
|
|
break;
|
|
case MCIAVIO_3QSCREENSIZE:
|
|
SetRect(lprc, 0, 0, lprc->right*3/4, lprc->bottom*3/4);
|
|
break;
|
|
case MCIAVIO_MAXWINDOWSIZE:
|
|
SetRect(lprc, 0, 0, lprc->right, lprc->bottom);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif /* NEW_MCI_DIALOG */
|
|
|
|
/*
|
|
* fOK = OpenMCI(szFile, szDevice)
|
|
*
|
|
* Open the file/device combination of <szFile> and <szDevice>.
|
|
* <szFile> may be "" if a "pure device" (e.g. "CDAudio") is to be opened.
|
|
* <szDevice> may be "" if a file is to be opened with an implicit type.
|
|
* However, <szFile> and <szDevice> may not both be "".
|
|
*
|
|
* On success, return TRUE. On failure, display an error message and
|
|
* return FALSE.
|
|
*
|
|
*/
|
|
|
|
BOOL FAR PASCAL OpenMCI(
|
|
LPCTSTR szFile, /* name of the media file to be loaded (or "") */
|
|
LPCTSTR szDevice) /* name of the device to be opened (or "") */
|
|
{
|
|
MCI_OPEN_PARMS mciOpen; /* Structure for MCI_OPEN command */
|
|
DWORD dwFlags;
|
|
DWORD dw;
|
|
HCURSOR hcurPrev;
|
|
HDRVR hdrv;
|
|
SHFILEINFO sfi;
|
|
HFILE hFile;
|
|
|
|
/*
|
|
* This application is designed to handle only one device at a time,
|
|
* so before opening a new device we should close the device that is
|
|
* currently open (if there is one).
|
|
*
|
|
* in case the user is opening a file of the same device again, do
|
|
* an OpenDriver to hold the DLL in memory.
|
|
*
|
|
*/
|
|
if (gwDeviceID && gwCurDevice > 0) {
|
|
|
|
#ifdef UNICODE
|
|
hdrv = OpenDriver(garMciDevices[gwCurDevice].szDevice, aszMCI, 0);
|
|
#else
|
|
//
|
|
// There is only a UNICODE version of OpenDriver. Unfortunately
|
|
// the majority of this code is Ascii. Convert the ASCII strings
|
|
// to UNICODE, then call OpenDriver
|
|
//
|
|
WCHAR waszMCI[sizeof(aszMCI)];
|
|
WCHAR wszDevice[40];
|
|
AnsiToUnicodeString(aszMCI, waszMCI, UNKNOWN_LENGTH);
|
|
AnsiToUnicodeString(garMciDevices[gwCurDevice].szDevice, wszDevice, UNKNOWN_LENGTH);
|
|
hdrv = OpenDriver((LPCWSTR)garMciDevices[gwCurDevice].szDevice,
|
|
(LPCWSTR)aszMCI,
|
|
0);
|
|
#endif
|
|
}
|
|
else
|
|
hdrv = NULL;
|
|
|
|
CloseMCI(TRUE);
|
|
|
|
//
|
|
// Store the displayable file/device name in <gachFileDevice>
|
|
//
|
|
if (szFile == NULL || szFile[0] == 0) {
|
|
/* It's a device -- display the device name */
|
|
lstrcpy(gachFileDevice, szDevice);
|
|
|
|
if (gwCurDevice > 0)
|
|
lstrcpy(gachWindowTitle,garMciDevices[gwCurDevice].szDeviceName);
|
|
else
|
|
lstrcpy(gachWindowTitle,gachFileDevice);
|
|
} else {
|
|
/* It's a file -- display the filename */
|
|
lstrcpy(gachFileDevice, szFile); //!!!
|
|
|
|
// Makes the window title be the name of the file being played
|
|
lstrcpy(gachWindowTitle, FileName(gachFileDevice));
|
|
}
|
|
|
|
/* Get the display name for this file:
|
|
*/
|
|
|
|
if (SHGetFileInfo(gachFileDevice, 0 /* No file attributes specified */,
|
|
&sfi, sizeof sfi, SHGFI_DISPLAYNAME))
|
|
lstrcpy(gachWindowTitle, sfi.szDisplayName);
|
|
|
|
//
|
|
// Set caption to the WindowTitle
|
|
//
|
|
lstrcpy(gachCaption, gachWindowTitle);
|
|
|
|
|
|
/*
|
|
* because *most* MCI devices yield during the open call, we *must*
|
|
* register our document *before* doing the open. OLE does not expect
|
|
* the server app to yield when exec'ed with a link request.
|
|
*
|
|
* if the open fails then we revoke our document right away.
|
|
*/
|
|
|
|
// if (!gfEmbeddedObject)
|
|
// RegisterDocument(0L,0L);
|
|
|
|
/*
|
|
* Show the hourglass cursor -- who knows how long this stuff
|
|
* will take
|
|
*/
|
|
|
|
hcurPrev = SetCursor(LoadCursor(NULL, IDC_WAIT));
|
|
|
|
DPF("OpenMCI: Device = %"DTS", File = %"DTS"\n", szDevice ? szDevice : TEXT("(null)"),szFile ? szFile : TEXT("(null)"));
|
|
|
|
mciOpen.lpstrAlias = aszMPlayerAlias;
|
|
dwFlags = MCI_OPEN_ALIAS;
|
|
|
|
if (szFile == NULL || szFile[0] == 0) {
|
|
|
|
/* Open a fileless (simple) device (e.g. "CDAudio") */
|
|
|
|
mciOpen.lpstrDeviceType = szDevice;
|
|
dwFlags |= MCI_WAIT | MCI_OPEN_TYPE | MCI_OPEN_SHAREABLE;
|
|
} else if (szDevice == NULL || szDevice[0] == 0) {
|
|
/*
|
|
* Open a file; the correct device is determined implicitly by the
|
|
* filename extension.
|
|
*
|
|
*/
|
|
mciOpen.lpstrElementName = szFile;
|
|
mciOpen.lpstrDeviceType = NULL;
|
|
dwFlags |= MCI_WAIT | MCI_OPEN_ELEMENT;
|
|
} else {
|
|
|
|
/* Open a file with an explicitly specified device */
|
|
|
|
mciOpen.lpstrDeviceType = szDevice;
|
|
mciOpen.lpstrElementName = szFile;
|
|
dwFlags |= MCI_WAIT | MCI_OPEN_ELEMENT | MCI_OPEN_TYPE;
|
|
}
|
|
|
|
/*
|
|
* Now that we have filled the parameter structure appropriately and
|
|
* supplied the correct flags, send the actual MCI_OPEN message.
|
|
*
|
|
*/
|
|
|
|
//
|
|
// What if the MCI device brings up an error box! We don't want MPlayer
|
|
// to be allowed to exit.
|
|
//
|
|
gfErrorBox++;
|
|
|
|
dw = mciSendCommand((MCIDEVICEID)0, MCI_OPEN, dwFlags,(DWORD_PTR)(LPVOID)&mciOpen);
|
|
|
|
if (dw != 0 && (dwFlags & MCI_OPEN_SHAREABLE))
|
|
dw = mciSendCommand((MCIDEVICEID)0, MCI_OPEN, (dwFlags & ~MCI_OPEN_SHAREABLE),
|
|
(DWORD_PTR)(LPVOID)&mciOpen);
|
|
DPF("MCI_OPEN returned %lu, wDeviceID=%u\n", dw, mciOpen.wDeviceID);
|
|
gfErrorBox--;
|
|
|
|
/*
|
|
* now free the driver instance we opened above.
|
|
*/
|
|
if (hdrv)
|
|
CloseDriver(hdrv, 0, 0);
|
|
|
|
if (hcurPrev)
|
|
SetCursor(hcurPrev);
|
|
|
|
if (dw != 0 && !gfEmbeddedObject) {
|
|
// UnblockServer(); // we may have blocked before and the error
|
|
// recovery code will loop infinitely if we're
|
|
// blocked!
|
|
InitDoc(TRUE);
|
|
}
|
|
|
|
/* If the open was unsuccessful, display an error message and return */
|
|
|
|
if (dw == MCIERR_DEVICE_OPEN || /* nonshareable device already open */
|
|
dw == MCIERR_MUST_USE_SHAREABLE) {
|
|
Error(ghwndApp, IDS_DEVICEINUSE);
|
|
return FALSE;
|
|
}
|
|
|
|
if (dw == MCIERR_FILE_NOT_FOUND) {
|
|
//Need to give an appropriate error message.
|
|
//The following could be the causes:
|
|
//1. File does not exist
|
|
//This is already handled by the file open dialog box.
|
|
//2. Access to the file is denied. (bug #53492)
|
|
//3. The file is opened exclusively by another app.
|
|
//The file already exists. so if it cannot be opened for reading
|
|
//either access is denied or it is opened by another app.
|
|
if ((hFile = (HFILE)HandleToUlong(CreateFile (szFile, GENERIC_READ,
|
|
FILE_SHARE_READ, NULL,
|
|
OPEN_EXISTING, 0, NULL))) == HFILE_ERROR)
|
|
{
|
|
Error(ghwndApp, IDS_CANTACCESSFILE);
|
|
}
|
|
//4. File was not in a recognized format
|
|
else
|
|
{
|
|
_lclose(hFile);
|
|
Error(ghwndApp, IDS_CANTOPENFILE);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/* If the MCI device that plays the given file does not exist then */
|
|
/* bring up a dialog box and close mplayer. */
|
|
if (dw == MCIERR_INVALID_DEVICE_NAME) {
|
|
Error(ghwndApp, IDS_DEVICENOTINSTALLED);
|
|
return FALSE;
|
|
}
|
|
|
|
if (dw != 0) { /* some other error */
|
|
//
|
|
// try again, if we can't open the file with a particular device.
|
|
// this lets the MCI core try to figure out the device type from
|
|
// the file extension, or some other method.
|
|
//
|
|
if ((dw != MCIERR_DRIVER_INTERNAL) && szDevice != NULL &&
|
|
szDevice[0] != 0) {
|
|
if (szFile && szFile[0] != 0) {
|
|
return OpenMCI(szFile, TEXT(""));
|
|
}
|
|
}
|
|
|
|
CheckErrorMCI(dw);
|
|
return FALSE;
|
|
}
|
|
|
|
/* The open was successful, so retain the MCI device ID for later use */
|
|
gwDeviceID = (UINT)mciOpen.wDeviceID;
|
|
|
|
//
|
|
// now query the device and see what it can do
|
|
//
|
|
FindDeviceMCI();
|
|
gwDeviceType = QueryDeviceTypeMCI(gwDeviceID);
|
|
|
|
if (!(gwDeviceType & DTMCI_CANPLAY)) {
|
|
Error(ghwndApp, IDS_DEVICECANNOTPLAY);
|
|
CloseMCI(TRUE);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!(gwDeviceType & (DTMCI_TIMEMS|DTMCI_TIMEFRAMES))) {
|
|
Error(ghwndApp, IDS_NOGOODTIMEFORMATS);
|
|
CloseMCI(TRUE);
|
|
return FALSE;
|
|
}
|
|
|
|
if (gwDeviceType & DTMCI_CANWINDOW) {
|
|
GetDestRectMCI(&grcSize);
|
|
#ifdef NEW_MCI_DIALOG
|
|
grcInitSize = grcSize;
|
|
// HACK!! We want to pay attention to some MCIAVI registry default
|
|
// sizes, so we'll read the registry and adjust the size of the movie
|
|
// accordingly.
|
|
AlterRectUsingDefaults(&grcSize);
|
|
#endif /* NEW_MCI_DIALOG */
|
|
} else
|
|
SetRectEmpty(&grcSize);
|
|
|
|
if (IsRectEmpty(&grcSize)) {
|
|
DPF("NULL rectange in GetDestRect() assuming device cant window!\n");
|
|
gwDeviceType &= ~DTMCI_CANWINDOW;
|
|
}
|
|
|
|
/* Turn on the update-display timer so the display is updated regularly */
|
|
|
|
EnableTimer(TRUE);
|
|
|
|
/*
|
|
** for devices that do windows, show the window right away.
|
|
**
|
|
** !!! note when we support a built in window it will go here.
|
|
*/
|
|
if (gfPlayOnly) {
|
|
CreateWindowMCI();
|
|
if (!IsIconic(ghwndApp))
|
|
SetMPlayerSize(&grcSize);
|
|
}
|
|
else if (GetWindowMCI() && IsWindowVisible(ghwndApp)) {
|
|
|
|
MCI_SEEK_PARMS mciSeek; /* parameter structure for MCI_SEEK */
|
|
TCHAR achReturn[40];
|
|
|
|
PostMessage(ghwndApp, WM_QUERYNEWPALETTE, 0, 0);
|
|
|
|
//
|
|
// make sure the default window is the right size.
|
|
//
|
|
PutWindowMCI(NULL);
|
|
|
|
//
|
|
// center the default window above or below our window
|
|
//
|
|
SmartWindowPosition(GetWindowMCI(), ghwndApp, TRUE);
|
|
|
|
//
|
|
// make sure the default window is showing
|
|
//
|
|
ShowWindowMCI(TRUE);
|
|
|
|
/* hack for MMP, do a seek to the start, it does not paint
|
|
correctly for some reason if we just show the window!
|
|
NOTE: 0 may not be the start of the media so this may
|
|
fail, but OH WELL! We can't call UpdateMCI yet to set
|
|
gdwMediaStart because we don't know the scale (time/frames)
|
|
yet so UpdateMCI won't set gdwMediaLength properly, and
|
|
I don't feel like calling UpdateMCI twice, so tough!!
|
|
And we can't just SeekMCI(0) because UpdateDisplay will get
|
|
called too soon so we hack everything! */
|
|
|
|
mciSendString(aszInfoProduct, achReturn,
|
|
CHAR_COUNT(achReturn), NULL);
|
|
if (!lstrcmpi(achReturn, aszMMPName)) {
|
|
mciSeek.dwTo = 0;
|
|
dw = mciSendCommand(gwDeviceID, MCI_SEEK, MCI_TO,
|
|
(DWORD_PTR)&mciSeek);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Remember to update the media information and the caption when
|
|
* UpdateDisplay() is next called. We don't set them until now
|
|
* because we want the ReadDefaults() to be called which will set
|
|
* gwCurScale to happen before UpdateDisplay calls UpdateMCI.
|
|
*/
|
|
gfValidMediaInfo = FALSE;
|
|
gfValidCaption = FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// GetDeviceNameMCI()
|
|
//
|
|
// wLen is the size IN BYTES of szDevice buffer
|
|
void FAR PASCAL GetDeviceNameMCI(LPTSTR szDevice, UINT wLen)
|
|
{
|
|
MCI_SYSINFO_PARMS mciSysInfo;
|
|
DWORD dw;
|
|
|
|
//
|
|
// assume failure.
|
|
//
|
|
szDevice[0] = 0;
|
|
|
|
mciSysInfo.dwCallback = 0L;
|
|
mciSysInfo.lpstrReturn = szDevice;
|
|
mciSysInfo.dwRetSize = wLen;
|
|
mciSysInfo.dwNumber = 0;
|
|
mciSysInfo.wDeviceType = 0;
|
|
|
|
if (gwDeviceID) {
|
|
dw = mciSendCommand(gwDeviceID, MCI_SYSINFO,
|
|
MCI_SYSINFO_INSTALLNAME,
|
|
(DWORD_PTR)(LPVOID)&mciSysInfo);
|
|
}
|
|
}
|
|
|
|
//
|
|
// QueryDevicesMCI
|
|
//
|
|
// wLen is the size IN BYTES of szDevice buffer
|
|
//
|
|
// Returns a list of devices in form "<device1>\0<device2>\0 ... <deviceN>\0\0"
|
|
void FAR PASCAL QueryDevicesMCI(LPTSTR szDevices, UINT wLen)
|
|
{
|
|
MCI_SYSINFO_PARMS mciSysInfo;
|
|
DWORD dw;
|
|
DWORD i;
|
|
DWORD_PTR cDevices; /* Total number of devices to enumerate */
|
|
DWORD BufferPos; /* Index to end of buffer */
|
|
|
|
//
|
|
// assume failure.
|
|
//
|
|
szDevices[0] = 0;
|
|
szDevices[1] = 0;
|
|
|
|
mciSysInfo.dwCallback = 0L;
|
|
mciSysInfo.lpstrReturn = szDevices;
|
|
mciSysInfo.dwRetSize = wLen;
|
|
mciSysInfo.dwNumber = 0;
|
|
mciSysInfo.wDeviceType = MCI_ALL_DEVICE_ID;
|
|
|
|
/* How many devices does mmsystem know about?
|
|
*/
|
|
dw = mciSendCommand(MCI_ALL_DEVICE_ID,
|
|
MCI_SYSINFO,
|
|
MCI_SYSINFO_QUANTITY,
|
|
(DWORD_PTR)(LPVOID)&mciSysInfo);
|
|
|
|
if (dw == 0) {
|
|
|
|
/* Device count is returned in lpstrReturn!
|
|
*/
|
|
cDevices = (DWORD_PTR)(LPVOID)*mciSysInfo.lpstrReturn;
|
|
BufferPos = 0;
|
|
|
|
/* Get the name of each device in turn. N.B. Not zero-based!
|
|
* Ensure there's room for the final (double) null terminator.
|
|
*/
|
|
for (i = 1; i < (cDevices + 1) && BufferPos < (wLen - 1); i++) {
|
|
|
|
mciSysInfo.lpstrReturn = &(szDevices[BufferPos]);
|
|
mciSysInfo.dwRetSize = wLen - BufferPos; /* How much space left */
|
|
mciSysInfo.dwNumber = i;
|
|
|
|
dw = mciSendCommand(MCI_ALL_DEVICE_ID,
|
|
MCI_SYSINFO,
|
|
MCI_SYSINFO_NAME,
|
|
(DWORD_PTR)(LPVOID)&mciSysInfo);
|
|
|
|
if (dw == 0) {
|
|
DPF1("Found device: %"DTS"\n", &(szDevices[BufferPos]));
|
|
BufferPos += (lstrlen(&(szDevices[BufferPos])) + 1);
|
|
}
|
|
}
|
|
|
|
/* Not strictly required, since our buffer was allocated LMEM_ZEROINIT:
|
|
*/
|
|
szDevices[BufferPos] = '\0';
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// FindDeviceMCI()
|
|
//
|
|
// Find the device the user just opened. We normally should know what
|
|
// was opened, but in the auto-open case MCI will pick a device for us.
|
|
//
|
|
// Determines what device is associated with <gwDeviceID> and
|
|
// sets the <gwCurDevice> global.
|
|
//
|
|
// Called by OpenMCI() whenever a new device is opened successfully.
|
|
//
|
|
void FAR PASCAL FindDeviceMCI(void)
|
|
{
|
|
UINT w;
|
|
TCHAR achDevice[80];
|
|
|
|
//
|
|
// assume failure.
|
|
//
|
|
gwCurDevice = 0;
|
|
|
|
GetDeviceNameMCI(achDevice, BYTE_COUNT(achDevice));
|
|
|
|
for (w=1; w<=gwNumDevices; w++)
|
|
{
|
|
if (lstrcmpi(achDevice, garMciDevices[w].szDevice) == 0) {
|
|
gwCurDevice = w;
|
|
}
|
|
|
|
if (ghMenu)
|
|
CheckMenuItem(ghMenu, IDM_DEVICE0+w, MF_BYCOMMAND |
|
|
((gwCurDevice == w) ? MF_CHECKED : MF_UNCHECKED));
|
|
}
|
|
|
|
if (gwCurDevice == 0)
|
|
{
|
|
DPF("FindDevice: Unable to find device\n");
|
|
}
|
|
}
|
|
|
|
void FAR PASCAL CreateWindowMCI()
|
|
{
|
|
RECT rc;
|
|
HWND hwnd;
|
|
|
|
if (IsWindow(ghwndMCI) || !gwDeviceID || !(gwDeviceType & DTMCI_CANWINDOW))
|
|
return;
|
|
|
|
/* Figure out how big the Playback window is, and make our MCI window */
|
|
/* the same size. */
|
|
|
|
hwnd = GetWindowMCI();
|
|
|
|
if (hwnd != NULL)
|
|
GetClientRect(hwnd, &rc);
|
|
else
|
|
rc = grcSize; // use original size if error
|
|
|
|
CreateWindowEx(gfdwFlagsEx,
|
|
MCI_WINDOW_CLASS,
|
|
TEXT(""),
|
|
WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
|
|
rc.left,
|
|
rc.top,
|
|
rc.right - rc.left,
|
|
rc.bottom - rc.top,
|
|
ghwndApp,
|
|
(HMENU)NULL,
|
|
ghInst,
|
|
NULL);
|
|
}
|
|
|
|
/*
|
|
* SendStringMCI() - send a MCI string command to the device.
|
|
*
|
|
* the string is of the form "verb params" our device name is inserted
|
|
* after the verb and sent to the device.
|
|
*
|
|
*/
|
|
DWORD PASCAL SendStringMCI(PTSTR szCmd, PTSTR szReturn, UINT wLen /* Characters */)
|
|
{
|
|
TCHAR ach[MCI_STRING_LENGTH + CHAR_COUNT(aszMPlayerAlias) + 1];
|
|
TCHAR *pch;
|
|
|
|
pch = ach;
|
|
while (*szCmd && *szCmd != TEXT(' '))
|
|
*pch++ = *szCmd++;
|
|
|
|
*pch++ = TEXT(' ');
|
|
lstrcpy(pch,aszMPlayerAlias);
|
|
lstrcat(pch,szCmd);
|
|
|
|
return mciSendString(ach, szReturn, wLen, ghwndApp);
|
|
}
|
|
|
|
/*
|
|
* UpdateMCI()
|
|
*
|
|
* Update <gfCanEject>, <gdwMediaLength>, <gwNumTracks>, and <gadwTrackStart>
|
|
* to agree with what MCI knows them to be.
|
|
*/
|
|
void FAR PASCAL UpdateMCI(void)
|
|
{
|
|
MCI_STATUS_PARMS mciStatus; /* Structure for MCI_STATUS command */
|
|
DWORD dw;
|
|
HCURSOR hcurPrev;
|
|
|
|
if (gfValidMediaInfo)
|
|
return;
|
|
|
|
/* If no device is currently open, then there's nothing to update */
|
|
|
|
if (gwDeviceID == (UINT)0) {
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Show the hourglass cursor -- who knows how long this stuff will take
|
|
*/
|
|
|
|
hcurPrev = SetCursor(LoadCursor(NULL, IDC_WAIT));
|
|
|
|
/*
|
|
* This function may fail (due to I/O error etc.), but we might as
|
|
* well say that the media information is valid now, because otherwise
|
|
* we'll just get into an endless loop.
|
|
*
|
|
*/
|
|
|
|
gfValidMediaInfo = TRUE;
|
|
|
|
gdwMediaStart = 0L;
|
|
gdwMediaLength = 0L;
|
|
gwNumTracks = 0;
|
|
|
|
/* If things aren't valid anyway, give up. */
|
|
if (gwStatus == MCI_MODE_OPEN || gwStatus == MCI_MODE_NOT_READY)
|
|
goto exit;
|
|
|
|
/* Find out how many tracks are present in the medium */
|
|
|
|
mciStatus.dwItem = MCI_STATUS_NUMBER_OF_TRACKS;
|
|
dw = mciSendCommand(gwDeviceID, MCI_STATUS, MCI_STATUS_ITEM,
|
|
(DWORD_PTR)&mciStatus);
|
|
|
|
#ifdef DEBUG
|
|
DPF("MCI_STATUS (MCI_STATUS_NUMBER_OF_TRACKS) returned %lu,"
|
|
" %d tracks\n", dw, mciStatus.dwReturn);
|
|
#endif
|
|
|
|
/*
|
|
* If the command retuned a value of zero, then the medium contains tracks,
|
|
* so use the number of tracks returned in the parameter structure.
|
|
* Otherwise, the medium does not contain tracks, so use a value of 0.
|
|
*
|
|
*/
|
|
|
|
if (dw == 0L)
|
|
gwNumTracks = (UINT) mciStatus.dwReturn;
|
|
|
|
/* Set the correct time format either frames or milliseconds */
|
|
|
|
if (gwCurScale == ID_FRAMES && !(gwDeviceType & DTMCI_TIMEFRAMES))
|
|
gwCurScale = ID_TIME;
|
|
|
|
if (gwCurScale == ID_TRACKS && gwNumTracks <= 1)
|
|
gwCurScale = ID_TIME;
|
|
|
|
if (gwCurScale == ID_TIME && !(gwDeviceType & DTMCI_TIMEMS))
|
|
gwCurScale = ID_FRAMES;
|
|
|
|
/* set the time format, If this does not work, punt. */
|
|
if (!SetTimeFormatMCI(gwCurScale == ID_FRAMES ? MCI_FORMAT_FRAMES : MCI_FORMAT_MILLISECONDS))
|
|
goto exit;
|
|
|
|
mciStatus.dwItem = MCI_STATUS_LENGTH;
|
|
dw = mciSendCommand(gwDeviceID, MCI_STATUS, MCI_STATUS_ITEM,
|
|
(DWORD_PTR)&mciStatus);
|
|
|
|
DPF("MCI_STATUS (MCI_STATUS_LENGTH) returned %lu, media length %ld\n", dw, mciStatus.dwReturn);
|
|
|
|
/*
|
|
* If the MCI command returned a nonzero value, then an error has
|
|
* occurred, so alert the user, close the offending device, and return.
|
|
*
|
|
*/
|
|
|
|
if (dw != 0L)
|
|
goto exit;
|
|
|
|
/* Everything is OK, so retain the media length for later use */
|
|
|
|
gdwMediaLength = (DWORD)mciStatus.dwReturn;
|
|
|
|
mciStatus.dwItem = MCI_STATUS_POSITION;
|
|
dw = mciSendCommand(gwDeviceID, MCI_STATUS,
|
|
MCI_STATUS_START | MCI_STATUS_ITEM, (DWORD_PTR)&mciStatus);
|
|
|
|
#ifdef DEBUG
|
|
DPF2("MCI_STATUS (MCI_STATUS_START) returned %lu, start %ld\n",dw, mciStatus.dwReturn);
|
|
#endif
|
|
|
|
gdwMediaStart = (DWORD)mciStatus.dwReturn;
|
|
|
|
if (dw != 0) {
|
|
/* Error: forget about track display */
|
|
gwNumTracks = 0;
|
|
}
|
|
|
|
if (gwNumTracks > 0) {
|
|
|
|
UINT wTrack;
|
|
|
|
/* Free the track map if it already exists */
|
|
|
|
if (gadwTrackStart != NULL)
|
|
FreeMem(gadwTrackStart, sizeof(DWORD) * gwNumTracks);
|
|
|
|
/* Allocate memory for the track map */
|
|
|
|
gadwTrackStart = AllocMem(sizeof(DWORD) * gwNumTracks);
|
|
if (gadwTrackStart == NULL) {
|
|
|
|
/* AllocMem() failed - alert the user, close the device, return */
|
|
|
|
Error(ghwndApp, IDS_OUTOFMEMORY);
|
|
gwNumTracks = 0;
|
|
goto exit;
|
|
}
|
|
|
|
/* See if there is a track zero */
|
|
mciStatus.dwItem = MCI_STATUS_POSITION;
|
|
mciStatus.dwTrack = (DWORD) 0;
|
|
dw = mciSendCommand(gwDeviceID, MCI_STATUS,
|
|
MCI_TRACK | MCI_STATUS_ITEM, (DWORD_PTR)&mciStatus);
|
|
|
|
#ifdef DEBUG
|
|
DPF2("MCI_STATUS (MCI_STATUS_START for track %lu) returned %lu, start %ld\n", mciStatus.dwTrack, dw, mciStatus.dwReturn);
|
|
#endif
|
|
|
|
if (dw == 0)
|
|
gwFirstTrack = 0;
|
|
else
|
|
gwFirstTrack = 1;
|
|
|
|
/* Get the track map from MCI */
|
|
|
|
for (wTrack = 0; wTrack < gwNumTracks; wTrack++) {
|
|
|
|
mciStatus.dwItem = MCI_STATUS_POSITION;
|
|
mciStatus.dwTrack = (DWORD) wTrack + gwFirstTrack;
|
|
dw = mciSendCommand(gwDeviceID, MCI_STATUS,
|
|
MCI_TRACK | MCI_STATUS_ITEM, (DWORD_PTR)&mciStatus);
|
|
|
|
#ifdef DEBUG
|
|
DPF2("MCI_STATUS (MCI_STATUS_START for track %lu) returned %lu, start %ld\n", mciStatus.dwTrack, dw,mciStatus.dwReturn);
|
|
#endif
|
|
|
|
if (dw != 0) {
|
|
#if 1
|
|
/* Error: forget about track display */
|
|
gwNumTracks = 0;
|
|
goto exit;
|
|
#else
|
|
/* An error occurred - do the usual stuff */
|
|
|
|
Error(ghwndApp, IDS_CANTACCESSFILEDEV);
|
|
goto exit;
|
|
#endif
|
|
}
|
|
|
|
/* Add the start of this track to the track list */
|
|
|
|
gadwTrackStart[wTrack] = (DWORD)mciStatus.dwReturn;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Invalidate the track map window so it will be redrawn with the
|
|
* correct positions, etc.
|
|
*
|
|
*/
|
|
exit:
|
|
#ifdef DEBUG
|
|
DPF("Finished updating status: # tracks = %u, length = %lu\n", gwNumTracks, gdwMediaLength);
|
|
#endif
|
|
|
|
SendMessage(ghwndTrackbar, TBM_SETRANGEMIN, (WPARAM)FALSE, gdwMediaStart);
|
|
SendMessage(ghwndTrackbar, TBM_SETRANGEMAX, (WPARAM)FALSE, gdwMediaStart + gdwMediaLength);
|
|
|
|
/* We must set the range before calling TBM_SETTIC (which is sent by
|
|
* CalcTicsOfDoom()), since the common trackbar now tests the range
|
|
* before accepting a new tic.
|
|
* It would probably be better to set the range in CalcTicsOfDoom().
|
|
*/
|
|
if (!gfCurrentCDNotAudio)
|
|
CalcTicsOfDoom();
|
|
|
|
SendMessage(ghwndTrackbar, TBM_SETSELSTART, (WPARAM)FALSE, -1); // invalid selection
|
|
SendMessage(ghwndTrackbar, TBM_SETSELEND, (WPARAM)TRUE, -1);
|
|
|
|
if (hcurPrev)
|
|
SetCursor(hcurPrev);
|
|
}
|
|
|
|
/*
|
|
* CloseMCI(fUpdateDisplay)
|
|
*
|
|
* Close the currently-open MCI device (if any). If <fUpdateDisplay>
|
|
* is TRUE, then update the display as well.
|
|
*
|
|
* Closing the device merely relinquishes control of it so that the device
|
|
* may be used by someone else. The device does not necessarily stop playing
|
|
* or return to the start of the medium when this message is received - the
|
|
* behaviour is device-dependent.
|
|
*
|
|
*/
|
|
|
|
void FAR PASCAL CloseMCI(BOOL fUpdateDisplay)
|
|
{
|
|
DWORD dw;
|
|
UINT w;
|
|
HWND hwnd;
|
|
|
|
if (!gfEmbeddedObject)
|
|
gachCaption[0] = 0; // nuke the caption
|
|
|
|
/* If no device is currently open, then there's nothing to close */
|
|
if (gwDeviceID == (UINT)0)
|
|
return;
|
|
|
|
/*
|
|
* Disable the display-update timer, as there's no longer any reason to
|
|
* periodically update the display.
|
|
*/
|
|
EnableTimer(FALSE);
|
|
|
|
////StopMCI();
|
|
|
|
//
|
|
// set either the owner or the WS_CHILD bit so it will
|
|
// not act up because we have the palette bit set and cause the
|
|
// desktop to steal the palette.
|
|
//
|
|
// because we are being run from client apps that dont deal
|
|
// with palettes we dont want the desktop to hose the palette.
|
|
//
|
|
hwnd = GetWindowMCI();
|
|
|
|
if ((hwnd != NULL) && gfRunWithEmbeddingFlag)
|
|
SetParent(hwnd, ghwndApp);
|
|
|
|
/* Send the MCI CLOSE message, and set the current device to NULL */
|
|
dw = mciSendCommand(gwDeviceID, MCI_CLOSE, 0L, (DWORD_PTR)0);
|
|
gwDeviceID = (UINT)0;
|
|
gwDeviceType = 0;
|
|
gwCurScale = ID_NONE;
|
|
SetRectEmpty(&grcSize);
|
|
|
|
/* Now close the MCI window AFTER we close the MCIDevice, so that the */
|
|
/* SetMCIWindow(NULL) this does won't flash up a default playback win.*/
|
|
if (ghwndMCI) {
|
|
/*
|
|
** Don't pass the WM_CLOSE to the subclass window proc or it will
|
|
** spuriously issue and IDM_CLOSE again!
|
|
*/
|
|
if (gfnMCIWndProc != NULL && ghwndSubclass == ghwndMCI) {
|
|
SetWindowLongPtr(ghwndMCI, GWLP_WNDPROC, (LONG_PTR)gfnMCIWndProc);
|
|
gfnMCIWndProc = NULL;
|
|
}
|
|
SendMessage(ghwndMCI, WM_CLOSE, 0, 0L);
|
|
}
|
|
|
|
/* Don't set gwCurDevice = 0 because if we were called by Open MCI, then */
|
|
/* we won't remember what device we were opening. So instead, we'll set */
|
|
/* gwCurDevice = 0 after returning from CloseMCI if we so desire. I know*/
|
|
/* it sounds hacky, but Todd told me to do it this way. End disclamer. */
|
|
|
|
/* Uncheck the device menus */
|
|
if (ghMenu) {
|
|
for (w = 1; w <= gwNumDevices; w++)
|
|
CheckMenuItem(ghMenu, IDM_DEVICE0 + w, MF_BYCOMMAND | MF_UNCHECKED);
|
|
}
|
|
|
|
DPF("MCI_CLOSE returned %lu\n", dw);
|
|
|
|
/* Free up the resources used by the track map */
|
|
|
|
if (gadwTrackStart != NULL)
|
|
{
|
|
FreeMem(gadwTrackStart, sizeof(DWORD) * gwNumTracks);
|
|
gadwTrackStart = NULL;
|
|
}
|
|
|
|
/* If you have auto-repeat on and you load a new file in between the */
|
|
/* repeating, the new file may come up with no buttons or no scrollbar */
|
|
/* because our JustPlayed code sets the old status to PLAY which avoids*/
|
|
/* updating. */
|
|
gfJustPlayed = FALSE;
|
|
|
|
/*
|
|
* If the display update flag was set, then update the display, taking
|
|
* into account that the media information and caption are now inaccurate.
|
|
*/
|
|
if (fUpdateDisplay) {
|
|
gfValidCaption = FALSE;
|
|
gfValidMediaInfo = FALSE;
|
|
UpdateDisplay();
|
|
}
|
|
}
|
|
|
|
/* Helper function to check return code from MCI functions. */
|
|
STATICFN BOOL NEAR PASCAL CheckErrorMCI(DWORD dwRet)
|
|
{
|
|
TCHAR ach[200];
|
|
if (dwRet != 0 && dwRet != MCIERR_NONAPPLICABLE_FUNCTION) {
|
|
mciGetErrorString(dwRet, ach, CHAR_COUNT(ach));
|
|
Error1(ghwndApp, IDS_DEVICEERROR, ach);
|
|
// CloseMCI(TRUE);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* PlayMCI()
|
|
*
|
|
* Start the current device playing. If the device is in a paused state,
|
|
* un-pause it.
|
|
* Maybe play the selection.
|
|
*
|
|
#ifdef NEW_MCI_DIALOG
|
|
* NOTE: MCIAVI will automatically play fullscreen if that option is selected
|
|
* in the registry. We don't have to do anything.
|
|
#endif NEW_MCI_DIALOG
|
|
*
|
|
*/
|
|
|
|
BOOL FAR PASCAL PlayMCI(DWORD_PTR dwFrom, DWORD_PTR dwTo)
|
|
|
|
{
|
|
MCI_PLAY_PARMS mciPlay; /* structure used to pass parameters along
|
|
with the MCI_PLAY command */
|
|
DWORD dw; /* variable holding the return value of
|
|
the various MCI commands */
|
|
DWORD dwflags = 0L; /* play flags */
|
|
|
|
/* If no device is currently open, then there's nothing to play */
|
|
|
|
DPF("mciPlay: From=%d To=%d\n", dwFrom, dwTo);
|
|
|
|
if (gwDeviceID == (UINT)0)
|
|
return TRUE;
|
|
|
|
if (gfInPlayMCI) {
|
|
return(TRUE);
|
|
}
|
|
|
|
gfInPlayMCI = TRUE;
|
|
|
|
/*
|
|
* Send the MCI_PLAY message. This will start the device playing from
|
|
* wherever the current position is within the medium. This message will
|
|
* un-pause the player if it is currently in the paused state.
|
|
*
|
|
*/
|
|
|
|
mciPlay.dwCallback = (DWORD_PTR)(HWND) ghwndApp;
|
|
if (dwFrom != dwTo) {
|
|
mciPlay.dwFrom = (DWORD)dwFrom;
|
|
mciPlay.dwTo = (DWORD)dwTo;
|
|
dwflags = MCI_FROM | MCI_TO;
|
|
}
|
|
|
|
/* don't allow MCIAVI full screen mode --- force Windowing */
|
|
if (gfPlayingInPlace && ((gwDeviceType & DTMCI_DEVICE) == DTMCI_AVIVIDEO))
|
|
dwflags |= MCI_MCIAVI_PLAY_WINDOW;
|
|
|
|
/* If auto-repeat is on, MCIAVI will bring the playback window to the */
|
|
/* front every time it repeats, because that's what it does when you */
|
|
/* issue a play. To avoid that, we'll just do a play repeat once. */
|
|
if (((gwDeviceType & DTMCI_DEVICE) == DTMCI_AVIVIDEO) &&
|
|
(gwOptions & OPT_AUTOREP))
|
|
dwflags |= MCI_DGV_PLAY_REPEAT;
|
|
|
|
//
|
|
// what if the MCI device brings up a error box? We don't want MPlayer
|
|
// to be allowed to exit.
|
|
//
|
|
gfErrorBox++;
|
|
dw = mciSendCommand(gwDeviceID, MCI_PLAY, F_NOTIFY | dwflags, (DWORD_PTR)&mciPlay);
|
|
DPF("MCI_PLAY returned %lu\n", dw);
|
|
gfErrorBox--;
|
|
|
|
/* In case it stops so soon we wouldn't notice this play command. */
|
|
if (dw == 0)
|
|
gfJustPlayed = TRUE;
|
|
|
|
gfInPlayMCI = FALSE;
|
|
|
|
return CheckErrorMCI(dw);
|
|
}
|
|
|
|
|
|
/*
|
|
* SetTimeFormatMCI()
|
|
*
|
|
* sets the current time format
|
|
*
|
|
*/
|
|
|
|
BOOL FAR PASCAL SetTimeFormatMCI(UINT wTimeFormat)
|
|
{
|
|
MCI_SET_PARMS mciSet; /* Structure for MCI_SET command */
|
|
DWORD dw;
|
|
|
|
mciSet.dwTimeFormat = wTimeFormat;
|
|
|
|
dw = mciSendCommand(gwDeviceID, MCI_SET, MCI_SET_TIME_FORMAT,
|
|
(DWORD_PTR) (LPVOID) &mciSet);
|
|
|
|
if (dw != 0) {
|
|
mciSet.dwTimeFormat = MCI_FORMAT_MILLISECONDS;
|
|
|
|
mciSendCommand(gwDeviceID, MCI_SET, MCI_SET_TIME_FORMAT,
|
|
(DWORD_PTR)(LPVOID)&mciSet);
|
|
}
|
|
|
|
return (dw == 0);
|
|
}
|
|
|
|
/*
|
|
* PauseMCI()
|
|
*
|
|
* Pause the current MCI device.
|
|
*
|
|
*/
|
|
|
|
BOOL FAR PASCAL PauseMCI(void)
|
|
|
|
{
|
|
MCI_GENERIC_PARMS mciGeneric; /* General-purpose structure used to pass
|
|
parameters along with various MCI
|
|
commands */
|
|
DWORD dw; /* variable holding the return value of
|
|
the various MCI commands */
|
|
|
|
/* If no device is currently open, then there's nothing to pause */
|
|
|
|
if (gwDeviceID == (UINT)0)
|
|
return TRUE;
|
|
|
|
/* Send the MCI_PAUSE message */
|
|
|
|
mciGeneric.dwCallback = (DWORD_PTR)(HWND) ghwndApp;
|
|
|
|
dw = mciSendCommand(gwDeviceID, MCI_PAUSE, F_NOTIFY, (DWORD_PTR)&mciGeneric);
|
|
|
|
DPF("MCI_PAUSE returned %lu\n", dw);
|
|
|
|
if (dw == MCIERR_UNSUPPORTED_FUNCTION) {
|
|
/* Pause isn't supported. Don't allow it any more. */
|
|
gwDeviceType &= ~DTMCI_CANPAUSE;
|
|
}
|
|
|
|
return CheckErrorMCI(dw);
|
|
}
|
|
|
|
/*
|
|
* SeekExactMCI()
|
|
*
|
|
* Set set exactly on or off
|
|
*
|
|
*/
|
|
|
|
BOOL FAR PASCAL SeekExactMCI(BOOL fExact)
|
|
{
|
|
DWORD dw;
|
|
BOOL fWasExact;
|
|
MCI_STATUS_PARMS mciStatus;
|
|
|
|
if (gwDeviceID == (UINT)0 || !(gwDeviceType & DTMCI_CANSEEKEXACT))
|
|
return FALSE;
|
|
|
|
//
|
|
// see if the device can seek exactly
|
|
//
|
|
dw = mciSendString(aszSeekExact, NULL, 0, NULL);
|
|
|
|
if (dw != 0)
|
|
{
|
|
gwDeviceType &= ~DTMCI_CANSEEKEXACT;
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// get current value.
|
|
//
|
|
mciStatus.dwItem = MCI_DGV_STATUS_SEEK_EXACTLY;
|
|
dw = mciSendCommand(gwDeviceID, MCI_STATUS, MCI_STATUS_ITEM,
|
|
(DWORD_PTR) (LPVOID) &mciStatus);
|
|
fWasExact = (dw == 0 && mciStatus.dwReturn != MCI_OFF) ? TRUE : FALSE;
|
|
|
|
if (fExact)
|
|
dw = mciSendString(aszSeekExactOn, NULL, 0, NULL);
|
|
else
|
|
dw = mciSendString(aszSeekExactOff, NULL, 0, NULL);
|
|
|
|
return fWasExact;
|
|
}
|
|
|
|
/*
|
|
* SetAudioMCI()
|
|
*
|
|
* Set audio for the current MCI device on/off.
|
|
*
|
|
*/
|
|
|
|
BOOL FAR PASCAL SetAudioMCI(BOOL fAudioOn)
|
|
|
|
{
|
|
MCI_SET_PARMS mciSet;
|
|
DWORD dw;
|
|
|
|
/* If no device is currently open, then there's nothing to do. */
|
|
|
|
if (gwDeviceID == (UINT)0)
|
|
return TRUE;
|
|
|
|
/* Send the MCI_SET message */
|
|
mciSet.dwAudio = MCI_SET_AUDIO_ALL;
|
|
|
|
dw = mciSendCommand(gwDeviceID, MCI_SET,
|
|
MCI_SET_AUDIO | (fAudioOn ? MCI_SET_ON : MCI_SET_OFF),
|
|
(DWORD_PTR)&mciSet);
|
|
|
|
DPF("MCI_SET returned %lu\n", dw);
|
|
|
|
return CheckErrorMCI(dw);
|
|
}
|
|
|
|
/*
|
|
* StopMCI()
|
|
*
|
|
* Stop the current MCI device.
|
|
*
|
|
*/
|
|
|
|
BOOL FAR PASCAL StopMCI(void)
|
|
|
|
{
|
|
MCI_GENERIC_PARMS mciGeneric; /* General-purpose structure used to pass
|
|
parameters along with various MCI
|
|
commands */
|
|
DWORD dw; /* variable holding the return value of
|
|
the various MCI commands */
|
|
|
|
/* If no device is currently open, then there's nothing to stop */
|
|
|
|
if (gwDeviceID == (UINT)0)
|
|
return TRUE;
|
|
|
|
/* Send the MCI_STOP message */
|
|
|
|
mciGeneric.dwCallback = (DWORD_PTR)(HWND) ghwndApp;
|
|
|
|
dw = mciSendCommand(gwDeviceID, MCI_STOP, F_NOTIFY,
|
|
(DWORD_PTR)&mciGeneric);
|
|
|
|
DPF("MCI_STOP returned %lu\n", dw);
|
|
|
|
return CheckErrorMCI(dw);
|
|
}
|
|
|
|
|
|
/*
|
|
* EjectMCI(fOpen)
|
|
*
|
|
* Open the device door if <fOpen> is TRUE, otherwise close it.
|
|
*
|
|
* To do: When un-ejected, update track map, media length, etc.
|
|
*
|
|
*/
|
|
|
|
BOOL FAR PASCAL EjectMCI(BOOL fOpen)
|
|
|
|
{
|
|
MCI_GENERIC_PARMS mciGeneric; /* General-purpose structure used to pass
|
|
parameters along with various MCI
|
|
commands */
|
|
DWORD dw; /* variable holding the return value of
|
|
the various MCI commands */
|
|
|
|
/* If no device is currently open, then there's nothing to eject */
|
|
|
|
if (gwDeviceID == (UINT)0)
|
|
return TRUE;
|
|
|
|
/*
|
|
* Send a message opening or closing the door depending on the state of
|
|
* <fOpen>.
|
|
*
|
|
*/
|
|
|
|
mciGeneric.dwCallback = (DWORD_PTR)(HWND) ghwndApp;
|
|
|
|
dw = mciSendCommand(gwDeviceID, MCI_SET,
|
|
(fOpen ? MCI_SET_DOOR_OPEN : MCI_SET_DOOR_CLOSED) | F_NOTIFY,
|
|
(DWORD_PTR)&mciGeneric);
|
|
|
|
DPF("MCI_SET (MCI_SET_DOOR_%s) returned %lu\n",(LPSTR)(fOpen ? "OPEN" : "CLOSED"), dw);
|
|
|
|
return CheckErrorMCI(dw);
|
|
}
|
|
|
|
|
|
/*
|
|
* SeekMCI(dwPosition)
|
|
*
|
|
* Seek to position <dwPosition> (measured in milliseconds from 0L to
|
|
* <gdwMediaLength> inclusive).
|
|
*
|
|
*/
|
|
STATICDT BOOL sfSeeking = FALSE;
|
|
|
|
BOOL FAR PASCAL SeekMCI(DWORD_PTR dwPosition)
|
|
{
|
|
DWORD dw; /* variable holding the return value of
|
|
the various MCI commands */
|
|
static int wStatus = -1;
|
|
|
|
/*
|
|
* If no device is currently open, then there's not much bloody point
|
|
* in trying to seek to a new position, is there?
|
|
*
|
|
*/
|
|
|
|
if (gwDeviceID == (UINT)0)
|
|
return TRUE;
|
|
|
|
/*
|
|
** If we're seeking, decide whether to play from or seek to based on
|
|
** the status at the last time we seeked. Otherwise, use the current
|
|
** status.
|
|
*/
|
|
|
|
if (!sfSeeking)
|
|
wStatus = gwStatus;
|
|
|
|
/* Playing from end of media is broken in CD, so don't let it happen. */
|
|
if (dwPosition >= gdwMediaStart + gdwMediaLength) {
|
|
if (!StopMCI())
|
|
return FALSE;
|
|
wStatus = MCI_MODE_STOP;
|
|
}
|
|
|
|
if (wStatus == MCI_MODE_PLAY) {
|
|
|
|
MCI_PLAY_PARMS mciPlay; /* parameter structure for MCI_PLAY */
|
|
DWORD dwflags = 0L;
|
|
|
|
/*
|
|
* If the player in in 'Play' mode, then we want to jump to the new
|
|
* position and keep playing. This can be done by sending an MCI_PLAY
|
|
* message and specifying the position which we wish to play from.
|
|
*
|
|
*/
|
|
|
|
mciPlay.dwFrom = (DWORD)dwPosition;
|
|
mciPlay.dwCallback = (DWORD_PTR)(HWND) ghwndApp;
|
|
|
|
/* don't allow MCIAVI full screen mode --- force Windowing */
|
|
if (gfPlayingInPlace && ((gwDeviceType & DTMCI_DEVICE) == DTMCI_AVIVIDEO))
|
|
dwflags |= MCI_MCIAVI_PLAY_WINDOW;
|
|
|
|
dw = mciSendCommand(gwDeviceID, MCI_PLAY, MCI_FROM | F_NOTIFY | dwflags,
|
|
(DWORD_PTR)&mciPlay);
|
|
DPF("MCI_PLAY (from %lu) returned %lu\n", mciPlay.dwFrom, dw);
|
|
|
|
/* In case it stops so soon we wouldn't notice this play command. */
|
|
if (dw == 0)
|
|
gfJustPlayed = TRUE;
|
|
|
|
}
|
|
else {
|
|
|
|
MCI_SEEK_PARMS mciSeek; /* parameter structure for MCI_SEEK */
|
|
|
|
/*
|
|
* In any other state but 'Play', we want the player to go to the new
|
|
* position and remain stopped. This is accomplished by sending an
|
|
* MCI_SEEK message and specifying the position we want to seek to.
|
|
*
|
|
*/
|
|
|
|
mciSeek.dwTo = (DWORD)dwPosition;
|
|
mciSeek.dwCallback = (DWORD_PTR)(HWND) ghwndApp;
|
|
|
|
dw = mciSendCommand(gwDeviceID, MCI_SEEK, MCI_TO | F_NOTIFY,
|
|
(DWORD_PTR)&mciSeek);
|
|
DPF2("MCI_SEEK (to %lu) returned %lu\n", mciSeek.dwTo, dw);
|
|
|
|
}
|
|
|
|
/*
|
|
* If no error occurred, save the position that is to be seeked to in
|
|
* order to use that position in UpdateDisplay() if the device is in
|
|
* seek mode.
|
|
*
|
|
*/
|
|
if (!dw)
|
|
gdwLastSeekToPosition = (DWORD)dwPosition;
|
|
|
|
/*
|
|
* Because we've moved to a new position in the medium, the scrollbar
|
|
* thumb is no longer positioned accurately. Call UpdateDisplay()
|
|
* immediately to rectify this. (We could just wait for the next
|
|
* automatic update, but this is friendlier).
|
|
*
|
|
*/
|
|
|
|
UpdateDisplay();
|
|
|
|
return CheckErrorMCI(dw);
|
|
}
|
|
|
|
|
|
/* SeekToStartMCI( )
|
|
*
|
|
* Better than SeekMCI(gdwMediaStart) for CDAudio (like, it works).
|
|
*
|
|
*/
|
|
BOOL FAR PASCAL SeekToStartMCI( )
|
|
{
|
|
MCI_SEEK_PARMS mciSeek; /* parameter structure for MCI_SEEK */
|
|
DWORD dw;
|
|
|
|
mciSeek.dwTo = 0;
|
|
mciSeek.dwCallback = (DWORD_PTR)(HWND) ghwndApp;
|
|
|
|
dw = mciSendCommand(gwDeviceID, MCI_SEEK, MCI_SEEK_TO_START,
|
|
(DWORD_PTR)&mciSeek);
|
|
|
|
DPF2("MCI_SEEK_TO_START returned %lu\n", dw);
|
|
|
|
return CheckErrorMCI(dw);
|
|
}
|
|
|
|
|
|
/*
|
|
* SkipTrackMCI(iSkip)
|
|
*
|
|
* Skip to the beginning of track <iCur> + <iSkip>, where <iCur>
|
|
* is the current track.
|
|
*
|
|
*/
|
|
|
|
void FAR PASCAL SkipTrackMCI(int iSkip)
|
|
{
|
|
MCI_STATUS_PARMS mciStatus; /* Structure used to pass parameters
|
|
along with an MCI_STATUS command */
|
|
DWORD dw; /* variable holding the return value
|
|
of the various MCI commands */
|
|
int iTrack; /* index of the track to skip to */
|
|
static int iLastTrack = -1;
|
|
|
|
/* If no device is currently open, then return */
|
|
|
|
if (gwDeviceID == (UINT)0)
|
|
return;
|
|
|
|
/* Determine the track # of the current track */
|
|
|
|
if (gfScrollTrack && gdwSeekPosition != 0) {
|
|
iTrack = iLastTrack + iSkip;
|
|
} else {
|
|
mciStatus.dwItem = MCI_STATUS_CURRENT_TRACK;
|
|
dw = mciSendCommand(gwDeviceID, MCI_STATUS, MCI_STATUS_ITEM,
|
|
(DWORD_PTR)&mciStatus);
|
|
|
|
DPF("MCI_STATUS (MCI_STATUS_CURRENT_TRACK) returned %lu, current track %ld\n", dw, mciStatus.dwReturn);
|
|
|
|
if (dw != 0L) {
|
|
|
|
/* Something went wrong, but it isn't catastrophic... */
|
|
|
|
MessageBeep(0);
|
|
return;
|
|
}
|
|
|
|
/* Compute the track # to which we wish to skip */
|
|
|
|
iTrack = ((int) mciStatus.dwReturn) + iSkip;
|
|
}
|
|
|
|
/* Handle special case of seeking backward from middle first track */
|
|
if (iTrack < (int)gwFirstTrack)
|
|
iTrack = (int)gwFirstTrack;
|
|
|
|
/* Don't do anything if <iTrack> is out of range */
|
|
|
|
if ((iTrack < (int)gwFirstTrack) || (iTrack >= (int)gwNumTracks +
|
|
(int)gwFirstTrack))
|
|
return;
|
|
|
|
/* Everything seems to be OK, so skip to the requested track */
|
|
|
|
gdwSeekPosition = gadwTrackStart[iTrack - gwFirstTrack];
|
|
iLastTrack = iTrack;
|
|
|
|
/* Hack: Update global scroll position */
|
|
SendMessage(ghwndTrackbar, TBM_SETPOS, TRUE, gadwTrackStart[iTrack-gwFirstTrack]);
|
|
}
|
|
|
|
STATICFN DWORD GetMode(MCI_STATUS_PARMS *pmciStatus)
|
|
{
|
|
pmciStatus->dwItem = MCI_STATUS_MODE;
|
|
if (0 != mciSendCommand(gwDeviceID, MCI_STATUS, MCI_STATUS_ITEM,
|
|
(DWORD_PTR)pmciStatus)) {
|
|
/* If the command returned a nonzero value, the mode is unknown */
|
|
return MCI_MODE_NOT_READY;
|
|
} else {
|
|
return (UINT)pmciStatus->dwReturn;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* wStatus = StatusMCI(pdwPosition)
|
|
*
|
|
* Query the status of the current device and return it.
|
|
*
|
|
* If <pdwPosition> is not NULL, then <*pdwPosition> is filled in with
|
|
* the current position of the device within the medium (in milliseconds,
|
|
* from 0 to <gdwMediaLength> *inclusive*). <*pdwPosition> is not
|
|
* necessarily filled in if MCI_MODE_NOT_READY is returned.
|
|
*
|
|
*/
|
|
|
|
UINT FAR PASCAL StatusMCI(DWORD_PTR* pdwPosition)
|
|
{
|
|
static UINT swModeLast = MCI_MODE_NOT_READY;
|
|
MCI_STATUS_PARMS mciStatus;
|
|
DWORD dw;
|
|
UINT wMode;
|
|
DWORD dwPosition;
|
|
|
|
/* If no device is currently open, return error. */
|
|
|
|
if (gwDeviceID == (UINT)0)
|
|
return MCI_MODE_NOT_READY;
|
|
|
|
/* Determine what the current mode (status) of the device is */
|
|
wMode = GetMode(&mciStatus);
|
|
|
|
if ((gwDeviceType & DTMCI_CANPLAY) &&
|
|
wMode != MCI_MODE_OPEN && wMode != MCI_MODE_NOT_READY) {
|
|
/* Determine the current position within the medium */
|
|
|
|
mciStatus.dwItem = MCI_STATUS_POSITION;
|
|
dw = mciSendCommand(gwDeviceID, MCI_STATUS, MCI_STATUS_ITEM,
|
|
(DWORD_PTR)&mciStatus);
|
|
|
|
DPF4("position = %lu (%lu)\n", mciStatus.dwReturn, dw);
|
|
|
|
/* If an error occurred, set the current position to zero */
|
|
|
|
if (dw == 0)
|
|
dwPosition = (DWORD)mciStatus.dwReturn;
|
|
else
|
|
dwPosition = 0L;
|
|
} else
|
|
dwPosition = 0L;
|
|
|
|
/*
|
|
* If the current position is past the end of the medium, set it to be
|
|
* equal to the end of the medium.
|
|
*
|
|
*/
|
|
|
|
if (dwPosition > gdwMediaLength + gdwMediaStart) {
|
|
DPF("Position beyond end of media: truncating value\n");
|
|
dwPosition = gdwMediaLength + gdwMediaStart;
|
|
}
|
|
|
|
if (dwPosition < gdwMediaStart) {
|
|
DPF2("Position before beginning of media: adjusting value\n");
|
|
dwPosition = gdwMediaStart;
|
|
}
|
|
|
|
sfSeeking = (wMode == MCI_MODE_SEEK);
|
|
|
|
/*
|
|
* If we were passed a valid position pointer, then return the current
|
|
* position.
|
|
*
|
|
*/
|
|
|
|
if (pdwPosition != NULL)
|
|
*pdwPosition = dwPosition;
|
|
|
|
/* Return the status of the device */
|
|
|
|
return wMode;
|
|
}
|
|
|
|
/*
|
|
* wRet = QueryDeviceTypeMCI(wDeviceID)
|
|
*
|
|
* This routine determines whether or not the device given in <szDevice> uses
|
|
* files and whether or not it can play anything at all.
|
|
* It does so by opening the device in question and then querying its
|
|
* capabilities.
|
|
*
|
|
* It returns a combination of DTMCI_ flags or DTMCI_ERROR
|
|
*
|
|
*/
|
|
UINT FAR PASCAL QueryDeviceTypeMCI(UINT wDeviceID)
|
|
{
|
|
MCI_GETDEVCAPS_PARMS mciDevCaps; /* for the MCI_GETDEVCAPS command */
|
|
MCI_SET_PARMS mciSet; /* for the MCI_SET command */
|
|
MCI_ANIM_WINDOW_PARMS mciWindow; /* for the MCI_WINDOW command */
|
|
DWORD dw;
|
|
UINT wRet=0;
|
|
TCHAR achDevice[40];
|
|
DWORD i;
|
|
|
|
//
|
|
// determine if the device is simple or compound
|
|
//
|
|
mciDevCaps.dwItem = MCI_GETDEVCAPS_COMPOUND_DEVICE;
|
|
dw = mciSendCommand(wDeviceID, MCI_GETDEVCAPS,
|
|
MCI_GETDEVCAPS_ITEM, (DWORD_PTR)&mciDevCaps);
|
|
|
|
DPF("MCI_GETDEVCAPS_COMPOUND_DEVICE: %lu (ret=%lu)\n", dw, mciDevCaps.dwReturn);
|
|
|
|
if (dw == 0 && mciDevCaps.dwReturn != 0)
|
|
wRet |= DTMCI_COMPOUNDDEV;
|
|
else
|
|
wRet |= DTMCI_SIMPLEDEV;
|
|
|
|
//
|
|
// determine if the device handles files
|
|
//
|
|
if (wRet & DTMCI_COMPOUNDDEV) {
|
|
mciDevCaps.dwItem = MCI_GETDEVCAPS_USES_FILES;
|
|
dw = mciSendCommand(wDeviceID, MCI_GETDEVCAPS,
|
|
MCI_GETDEVCAPS_ITEM, (DWORD_PTR)&mciDevCaps);
|
|
|
|
DPF("MCI_GETDEVCAPS_USES_FILES: %lu (ret=%lu)\n", dw, mciDevCaps.dwReturn);
|
|
|
|
if (dw == 0 && mciDevCaps.dwReturn != 0)
|
|
wRet |= DTMCI_FILEDEV;
|
|
}
|
|
|
|
//
|
|
// determine if the device can play
|
|
//
|
|
mciDevCaps.dwItem = MCI_GETDEVCAPS_CAN_PLAY;
|
|
dw = mciSendCommand(wDeviceID, MCI_GETDEVCAPS,
|
|
MCI_GETDEVCAPS_ITEM, (DWORD_PTR)&mciDevCaps);
|
|
|
|
if (dw == 0 && mciDevCaps.dwReturn != 0)
|
|
wRet |= DTMCI_CANPLAY;
|
|
|
|
//
|
|
// determine if the device can pause
|
|
//
|
|
if (wRet & DTMCI_CANPLAY)
|
|
wRet |= DTMCI_CANPAUSE; // assume it can pause!!!
|
|
|
|
//
|
|
// determine if the device does frames
|
|
//
|
|
mciSet.dwTimeFormat = MCI_FORMAT_FRAMES;
|
|
dw = mciSendCommand(wDeviceID, MCI_SET, MCI_SET_TIME_FORMAT, (DWORD_PTR)&mciSet);
|
|
|
|
DPF("MCI_SET TIME FORMAT (frames) returned %lu\n", dw);
|
|
|
|
if (dw == 0)
|
|
wRet |= DTMCI_TIMEFRAMES;
|
|
|
|
//
|
|
// determine if the device does milliseconds
|
|
//
|
|
mciSet.dwTimeFormat = MCI_FORMAT_MILLISECONDS;
|
|
dw = mciSendCommand(wDeviceID, MCI_SET, MCI_SET_TIME_FORMAT, (DWORD_PTR)&mciSet);
|
|
|
|
DPF("MCI_SET TIME FORMAT (milliseconds) returned %lu\n", dw);
|
|
|
|
if (dw == 0)
|
|
wRet |= DTMCI_TIMEMS;
|
|
|
|
//
|
|
// determine if the device can eject.
|
|
//
|
|
mciDevCaps.dwItem = MCI_GETDEVCAPS_CAN_EJECT;
|
|
dw = mciSendCommand(wDeviceID, MCI_GETDEVCAPS, MCI_GETDEVCAPS_ITEM, (DWORD_PTR)(LPVOID)&mciDevCaps);
|
|
|
|
DPF("MCI_GETDEVCAPS (MCI_GETDEVCAPS_CAN_EJECT) returned %lu, can eject: %ld\n", dw, mciDevCaps.dwReturn);
|
|
|
|
if (dw == 0 && mciDevCaps.dwReturn)
|
|
wRet |= DTMCI_CANEJECT;
|
|
|
|
//
|
|
// determine if the device supports configuration
|
|
//
|
|
dw = mciSendCommand(wDeviceID, MCI_CONFIGURE, MCI_TEST, (DWORD_PTR)NULL);
|
|
|
|
DPF("MCI_CONFIGURE (MCI_TEST) returned %lu\n", dw);
|
|
|
|
if (dw == 0)
|
|
wRet |= DTMCI_CANCONFIG;
|
|
|
|
//
|
|
// test the device driver and see if it can config.
|
|
//
|
|
if (!(wRet & DTMCI_CANCONFIG)) {
|
|
|
|
//!!! is this safe?
|
|
|
|
dw = mciSendCommand(wDeviceID, DRV_QUERYCONFIGURE, 0, 0);
|
|
|
|
if (dw == 1L)
|
|
wRet |= DTMCI_CANCONFIG;
|
|
}
|
|
|
|
//
|
|
// determine if the device supports the "set audio" command
|
|
//
|
|
mciSet.dwAudio = MCI_SET_AUDIO_ALL;
|
|
dw = mciSendCommand(wDeviceID, MCI_SET, MCI_SET_AUDIO | MCI_SET_ON,(DWORD_PTR)(LPVOID)&mciSet);
|
|
|
|
DPF("MCI_SET (audio all) returned %lu\n", dw);
|
|
|
|
if (dw == 0)
|
|
wRet |= DTMCI_CANMUTE;
|
|
|
|
//
|
|
// determine if the device supports the "window" command, by sending a
|
|
// "window handle default" command
|
|
//
|
|
|
|
#ifdef NEWSTUFF
|
|
/* Uh oh, we don't want to do this, because it causes our MCIWnd to be
|
|
* overridden by the default window:
|
|
*/
|
|
|
|
if (MCIWndCanWindow(ghwndMCI) == TRUE);
|
|
wRet |= DTMCI_CANWINDOW;
|
|
#else
|
|
mciWindow.hWnd = MCI_ANIM_WINDOW_DEFAULT;
|
|
dw = mciSendCommand(wDeviceID, MCI_WINDOW,MCI_ANIM_WINDOW_HWND|MCI_WAIT,
|
|
(DWORD_PTR)(LPVOID)&mciWindow);
|
|
|
|
DPF("MCI_WINDOW: (set default) dw=0x%08lx\n", dw);
|
|
|
|
if (dw == 0)
|
|
wRet |= DTMCI_CANWINDOW;
|
|
|
|
//
|
|
// determine if the device supports the "window" command, by sending a
|
|
// "window state hide" command
|
|
//
|
|
if (!(wRet & DTMCI_CANWINDOW)) {
|
|
mciWindow.nCmdShow = SW_HIDE;
|
|
dw = mciSendCommand(wDeviceID, MCI_WINDOW,MCI_ANIM_WINDOW_STATE|MCI_WAIT,
|
|
(DWORD_PTR)(LPVOID)&mciWindow);
|
|
|
|
DPF("MCI_WINDOW: (hide) dw=0x%08lx\n", dw);
|
|
|
|
if (dw == 0)
|
|
wRet |= DTMCI_CANWINDOW;
|
|
}
|
|
#endif /* NEWSTUFF */
|
|
|
|
//
|
|
// assume the device can seek exact.
|
|
//
|
|
wRet |= DTMCI_CANSEEKEXACT; // assume it can seek exact
|
|
|
|
//
|
|
// Are we the MCIAVI device?
|
|
//
|
|
GetDeviceNameMCI(achDevice, BYTE_COUNT(achDevice));
|
|
|
|
if (*achDevice)
|
|
{
|
|
for (i = 0; i < sizeof DevToDevIDMap / sizeof *DevToDevIDMap; i++)
|
|
{
|
|
if (!lstrcmpi(achDevice, DevToDevIDMap[i].pString))
|
|
{
|
|
wRet |= DevToDevIDMap[i].ID;
|
|
DPF("Found device %"DTS"\n", DevToDevIDMap[i].pString);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
mciDevCaps.dwItem = MCI_GETDEVCAPS_DEVICE_TYPE;
|
|
dw = mciSendCommand(gwDeviceID, MCI_GETDEVCAPS,
|
|
MCI_GETDEVCAPS_ITEM, (DWORD_PTR)&mciDevCaps);
|
|
if ((dw == 0)
|
|
&&(mciDevCaps.dwReturn == MCI_DEVTYPE_CD_AUDIO))
|
|
wRet |= DTMCI_CDAUDIO;
|
|
|
|
return wRet;
|
|
}
|
|
|
|
BOOL FAR PASCAL SetWindowMCI(HWND hwnd)
|
|
{
|
|
MCI_ANIM_WINDOW_PARMS mciWindow; /* for the MCI_WINDOW command */
|
|
DWORD dw;
|
|
|
|
if (gwDeviceID == (UINT)0 || !(gwDeviceType & DTMCI_CANWINDOW))
|
|
return FALSE;
|
|
|
|
mciWindow.hWnd = hwnd;
|
|
|
|
dw = mciSendCommand(gwDeviceID, MCI_WINDOW,MCI_ANIM_WINDOW_HWND|MCI_WAIT,
|
|
(DWORD_PTR)(LPVOID)&mciWindow);
|
|
|
|
if (dw != 0)
|
|
gwDeviceType &= ~DTMCI_CANWINDOW;
|
|
|
|
return (dw == 0);
|
|
}
|
|
|
|
BOOL FAR PASCAL ShowWindowMCI(BOOL fShow)
|
|
{
|
|
DWORD dw;
|
|
|
|
if (fShow)
|
|
dw = mciSendString(aszWindowShow, NULL, 0, NULL);
|
|
else
|
|
dw = mciSendString(aszWindowHide, NULL, 0, NULL);
|
|
|
|
return dw == 0;
|
|
}
|
|
|
|
BOOL FAR PASCAL PutWindowMCI(LPRECT prc)
|
|
{
|
|
RECT rc;
|
|
HWND hwnd;
|
|
UINT w;
|
|
|
|
//
|
|
// note we could use the "put window at x y dx dy client" command but it
|
|
// may not work on all devices.
|
|
//
|
|
|
|
if (gwDeviceID == (UINT)0 || !(gwDeviceType & DTMCI_CANWINDOW))
|
|
return FALSE;
|
|
|
|
if (!(hwnd = GetWindowMCI()))
|
|
return FALSE;
|
|
|
|
//
|
|
// either snap to the default size or use the given size *and* position.
|
|
//
|
|
if (prc == NULL || IsRectEmpty(prc))
|
|
rc = grcSize;
|
|
else
|
|
rc = *prc;
|
|
|
|
if (rc.left == 0 && rc.top == 0)
|
|
w = SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE;
|
|
else
|
|
w = SWP_NOZORDER | SWP_NOACTIVATE;
|
|
|
|
AdjustWindowRect(&rc, (DWORD)GetWindowLongPtr(hwnd, GWL_STYLE), GetMenu(hwnd) != NULL);
|
|
SetWindowPos(hwnd, NULL, rc.left, rc.top, rc.right-rc.left,
|
|
rc.bottom-rc.top,w);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
HWND FAR PASCAL GetWindowMCI(void)
|
|
{
|
|
DWORD dw;
|
|
TCHAR ach[40];
|
|
|
|
if (gwDeviceID == (UINT)0 || !(gwDeviceType & DTMCI_CANWINDOW))
|
|
return NULL;
|
|
|
|
dw = mciSendString(aszStatusWindow, ach, CHAR_COUNT(ach), NULL);
|
|
|
|
if (dw != 0)
|
|
gwDeviceType &= ~DTMCI_CANWINDOW;
|
|
|
|
if (dw == 0)
|
|
return (HWND)IntToPtr(ATOI(ach));
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
BOOL FAR PASCAL SetPaletteMCI(HPALETTE hpal)
|
|
{
|
|
MCI_DGV_SETVIDEO_PARMS mciVideo;
|
|
DWORD dw;
|
|
|
|
if (gwDeviceID == (UINT)0 || !(gwDeviceType & DTMCI_CANWINDOW))
|
|
return FALSE;
|
|
|
|
//!!! bug should not send this.
|
|
|
|
mciVideo.dwItem = MCI_DGV_SETVIDEO_PALHANDLE;
|
|
mciVideo.dwValue = (DWORD)(DWORD_PTR)(UINT_PTR)hpal;
|
|
|
|
dw = mciSendCommand(gwDeviceID, MCI_SETVIDEO,
|
|
MCI_DGV_SETVIDEO_ITEM|MCI_DGV_SETVIDEO_VALUE|MCI_WAIT,
|
|
(DWORD_PTR)(LPVOID)&mciVideo);
|
|
|
|
return (dw == 0);
|
|
}
|
|
|
|
/*
|
|
* wRet = DeviceTypeMCI(szDevice)
|
|
*
|
|
* This routine determines whether or not the device given in <szDevice> uses
|
|
* files and whether or not it can play anything at all.
|
|
* It does so by opening the device in question and then querying its
|
|
* capabilities. It returns either DTMCI_FILEDEV, DTMCI_SIMPLEDEV,
|
|
* DTMCI_CANTPLAY, or DTMCI_ERROR.
|
|
*
|
|
*/
|
|
|
|
UINT FAR PASCAL DeviceTypeMCI(
|
|
LPTSTR szDevice, /* name of the device to be opened (or "") */
|
|
LPTSTR szDeviceName, /* place to put device full-name */
|
|
int nBuf) /* size of buffer IN CHARACTERS */
|
|
|
|
{
|
|
MCI_OPEN_PARMS mciOpen; /* Structure used for MCI_OPEN */
|
|
MCI_INFO_PARMS mciInfo; /* Structure used for MCI_INFO */
|
|
DWORD dw;
|
|
UINT wRet;
|
|
|
|
if (szDeviceName && nBuf > 0)
|
|
szDeviceName[0] = 0;
|
|
|
|
/*
|
|
* Open the device as a simple device. If the device is actually compound,
|
|
* then the open should still succeed, but the only thing we'll be able to
|
|
* go is query the device capabilities.
|
|
*/
|
|
|
|
mciOpen.lpstrDeviceType = szDevice;
|
|
dw = mciSendCommand((MCIDEVICEID)0, MCI_OPEN, MCI_OPEN_TYPE,(DWORD_PTR)&mciOpen);
|
|
|
|
if (dw == MCIERR_MUST_USE_SHAREABLE)
|
|
dw = mciSendCommand((MCIDEVICEID)0, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_SHAREABLE,
|
|
(DWORD_PTR)(LPVOID)&mciOpen);
|
|
|
|
DPF("MCI_OPEN(%"DTS") returned %lu, wDeviceID=%u\n", szDevice, dw, mciOpen.wDeviceID);
|
|
|
|
/* If the open was unsuccessful, return */
|
|
|
|
switch (dw)
|
|
{
|
|
case MCIERR_MUST_USE_SHAREABLE:
|
|
case MCIERR_DEVICE_OPEN:
|
|
return DTMCI_IGNOREDEVICE;
|
|
|
|
case 0: // no error
|
|
break;
|
|
|
|
default:
|
|
DPF("Unable to open device (%"DTS")\n", szDevice);
|
|
return DTMCI_ERROR;
|
|
}
|
|
|
|
wRet = QueryDeviceTypeMCI(mciOpen.wDeviceID);
|
|
|
|
//
|
|
// get the "name" of the device if the caller wants it
|
|
//
|
|
if (szDeviceName && nBuf > 0)
|
|
{
|
|
mciInfo.dwCallback = 0;
|
|
mciInfo.lpstrReturn = szDeviceName;
|
|
mciInfo.dwRetSize = nBuf;
|
|
|
|
//
|
|
// default the product name to the device name
|
|
//
|
|
lstrcpy(szDeviceName, szDevice);
|
|
|
|
dw = mciSendCommand(mciOpen.wDeviceID, MCI_INFO,
|
|
MCI_INFO_PRODUCT, (DWORD_PTR)(LPVOID)&mciInfo);
|
|
|
|
if (dw != 0)
|
|
lstrcpy(szDeviceName, szDevice);
|
|
}
|
|
|
|
/* Close the device, and exit */
|
|
|
|
dw = mciSendCommand(mciOpen.wDeviceID, MCI_CLOSE, 0L, (DWORD_PTR)0);
|
|
|
|
return wRet;
|
|
}
|
|
|
|
BOOL FAR PASCAL ConfigMCI(HWND hwnd)
|
|
{
|
|
DWORD dw;
|
|
DRVCONFIGINFO drvc;
|
|
RECT rc1,rc2;
|
|
#ifndef UNICODE
|
|
WCHAR waszMCI[sizeof(aszMCI)];
|
|
WCHAR wszDevice[40];
|
|
#endif
|
|
|
|
if (gwDeviceID == (UINT)0)
|
|
return TRUE;
|
|
|
|
dw = mciSendCommand(gwDeviceID, MCI_CONFIGURE, MCI_TEST, (DWORD_PTR)0);
|
|
|
|
if (dw == 0) {
|
|
GetDestRectMCI(&rc1);
|
|
|
|
dw = mciSendCommand(gwDeviceID, MCI_CONFIGURE, 0L, (DWORD_PTR)0);
|
|
|
|
GetDestRectMCI(&rc2);
|
|
|
|
//
|
|
// get the new size from MCIAVI, because the user may have
|
|
// chosen ZoomBy2 as default.
|
|
//
|
|
|
|
//
|
|
// This won't happen anymore... it was fixed by an MCIAVI fix.
|
|
//
|
|
#ifdef NEW_MCI_DIALOG
|
|
if (IsRectEmpty(&rc2))
|
|
{
|
|
/* On Windows 95, GetDestRectMCI() returns an empty rectangle
|
|
* if you make a change in the configure dialog.
|
|
* I don't know if this is a bug.
|
|
*/
|
|
grcSize = grcInitSize;
|
|
|
|
AlterRectUsingDefaults(&grcSize);
|
|
|
|
SetDestRectMCI(&grcSize);
|
|
SetMPlayerSize(&grcSize);
|
|
//HACK: It doesn't always repaint properly.
|
|
InvalidateRect(GetWindowMCI(), NULL, TRUE);
|
|
}
|
|
else
|
|
#endif
|
|
if (!EqualRect(&rc1, &rc2) && !IsRectEmpty(&rc2))
|
|
grcSize = rc2;
|
|
|
|
} else if (dw != MCIERR_DEVICE_NOT_READY) {
|
|
drvc.dwDCISize = sizeof(drvc);
|
|
#ifdef UNICODE
|
|
drvc.lpszDCISectionName = aszMCI;
|
|
drvc.lpszDCIAliasName = garMciDevices[gwCurDevice].szDevice;
|
|
dw = mciSendCommand(gwDeviceID, DRV_CONFIGURE, (LONG_PTR) (UINT_PTR) hwnd,
|
|
(DWORD_PTR) (DRVCONFIGINFO FAR *) &drvc);
|
|
#else
|
|
// No ASCII->Unicode thunking exists for DRV_CONFIGURE. We have
|
|
// to pass unicode strings on the configure command.
|
|
|
|
AnsiToUnicodeString(aszMCI, waszMCI, UNKNOWN_LENGTH);
|
|
AnsiToUnicodeString(garMciDevices[gwCurDevice].szDevice, wszDevice, UNKNOWN_LENGTH);
|
|
|
|
drvc.lpszDCISectionName = waszMCI;
|
|
drvc.lpszDCIAliasName = wszDevice;
|
|
#ifdef CHICAGO_PRODUCT
|
|
dw = mciSendCommand(gwDeviceID, DRV_CONFIGURE, (LONG) (UINT) hwnd,
|
|
(DWORD_PTR) (DRVCONFIGINFO FAR *) &drvc);
|
|
#else
|
|
dw = mciSendCommandW(gwDeviceID, DRV_CONFIGURE, (LONG) (UINT) hwnd,
|
|
(DWORD_PTR) (DRVCONFIGINFO FAR *) &drvc);
|
|
#endif
|
|
#endif
|
|
|
|
}
|
|
|
|
return dw == 0;
|
|
}
|
|
|
|
BOOL FAR PASCAL GetDestRectMCI(LPRECT lprc)
|
|
{
|
|
MCI_ANIM_RECT_PARMS mciRect;
|
|
DWORD dw;
|
|
|
|
/* get the size (rectangle) of the element */
|
|
if (gwDeviceID != (UINT)0)
|
|
dw = mciSendCommand(gwDeviceID, MCI_WHERE,
|
|
MCI_ANIM_WHERE_DESTINATION | MCI_WAIT,
|
|
(DWORD_PTR)(LPVOID)&mciRect);
|
|
else
|
|
dw = 1;
|
|
|
|
DPF("MCI_WHERE (dest): dw0x%08lx [%d,%d,%d,%d]\n", dw, mciRect.rc);
|
|
|
|
if (dw != 0) {
|
|
SetRectEmpty(lprc);
|
|
return FALSE;
|
|
}
|
|
else {
|
|
*lprc = mciRect.rc;
|
|
lprc->right += lprc->left;
|
|
lprc->bottom += lprc->top;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
#if 0 /* This is never called */
|
|
BOOL FAR PASCAL GetSourceRectMCI(LPRECT lprc)
|
|
{
|
|
MCI_ANIM_RECT_PARMS mciRect;
|
|
DWORD dw;
|
|
|
|
/* get the size (rectangle) of the element */
|
|
if (gwDeviceID != (UINT)0)
|
|
dw = mciSendCommand(gwDeviceID, MCI_WHERE,
|
|
MCI_ANIM_WHERE_SOURCE | MCI_WAIT,
|
|
(DWORD_PTR)(LPVOID)&mciRect);
|
|
else
|
|
dw = 1;
|
|
|
|
DPF("MCI_WHERE (source): dw0x%08lx [%d,%d,%d,%d]\n", dw, mciRect.rc);
|
|
|
|
if (dw != 0) {
|
|
SetRectEmpty(lprc);
|
|
return FALSE;
|
|
}
|
|
else {
|
|
*lprc = mciRect.rc;
|
|
lprc->right += lprc->left;
|
|
lprc->bottom += lprc->top;
|
|
return TRUE;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
BOOL FAR PASCAL SetDestRectMCI(LPRECT lprc)
|
|
{
|
|
MCI_ANIM_RECT_PARMS mciRect;
|
|
DWORD dw;
|
|
|
|
mciRect.rc = *lprc;
|
|
|
|
/* get the size (rectangle) of the element */
|
|
|
|
mciRect.rc.right = mciRect.rc.right - mciRect.rc.left;
|
|
mciRect.rc.bottom = mciRect.rc.bottom - mciRect.rc.top;
|
|
|
|
dw = mciSendCommand(gwDeviceID, MCI_PUT,
|
|
MCI_ANIM_RECT | MCI_ANIM_PUT_DESTINATION | MCI_WAIT,
|
|
(DWORD_PTR)(LPVOID)&mciRect);
|
|
|
|
if (dw != 0)
|
|
{
|
|
DPF0("mciSendCommand( MCI_PUT ) failed with error x%08x\n", dw);
|
|
}
|
|
|
|
DPF("MCI_PUT (dest): [%d,%d,%d,%d]\n", mciRect.rc);
|
|
|
|
return (dw == 0);
|
|
}
|
|
|
|
#if 0
|
|
BOOL FAR PASCAL SetSourceRectMCI(LPRECT lprc)
|
|
{
|
|
MCI_ANIM_RECT_PARMS mciRect;
|
|
DWORD dw;
|
|
|
|
mciRect.rc = *lprc;
|
|
|
|
mciRect.rc.right = mciRect.rc.right - mciRect.rc.left;
|
|
mciRect.rc.bottom = mciRect.rc.bottom - mciRect.rc.top;
|
|
|
|
dw = mciSendCommand(gwDeviceID, MCI_PUT,
|
|
MCI_ANIM_RECT | MCI_ANIM_PUT_SOURCE | MCI_WAIT,
|
|
(DWORD_PTR)(LPVOID)&mciRect);
|
|
|
|
DPF("MCI_PUT (source): [%d,%d,%d,%d]\n", mciRect.rc);
|
|
|
|
return (dw == 0);
|
|
}
|
|
#endif
|
|
|
|
HPALETTE FAR PASCAL PaletteMCI(void)
|
|
{
|
|
MCI_STATUS_PARMS mciStatus;
|
|
DWORD dw;
|
|
|
|
if (gwDeviceID == (UINT)0 || !(gwDeviceType & DTMCI_CANWINDOW))
|
|
return NULL;
|
|
|
|
mciStatus.dwItem = MCI_ANIM_STATUS_HPAL;
|
|
dw = mciSendCommand(gwDeviceID, MCI_STATUS, MCI_STATUS_ITEM,
|
|
(DWORD_PTR)(LPVOID)&mciStatus);
|
|
|
|
if (dw == 0 && mciStatus.dwReturn)
|
|
return (HPALETTE)mciStatus.dwReturn;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
HBITMAP FAR PASCAL BitmapMCI(void)
|
|
{
|
|
MCI_ANIM_UPDATE_PARMS mciUpdate;
|
|
HDC hdc, hdcMem;
|
|
HBITMAP hbm, hbmT;
|
|
HBRUSH hbrOld;
|
|
HANDLE hfontOld;
|
|
DWORD dw;
|
|
RECT rc;
|
|
int xExt, yExt; // size of text area
|
|
int xOff = 0, yOff = 0; // offset of text string
|
|
int xSize, ySize; // size of whole picture
|
|
int xIconOffset; // x Offset if drawing Icon.
|
|
TCHAR ach[20];
|
|
RECT rcSave;
|
|
RECT rcs;
|
|
SIZE TempSize;
|
|
|
|
/* Minimum size of bitmap is icon size */
|
|
int ICON_MINX = GetSystemMetrics(SM_CXICON);
|
|
int ICON_MINY = GetSystemMetrics(SM_CYICON);
|
|
|
|
/* Get size of a frame or an icon that we'll be drawing */
|
|
rcs = grcSize;
|
|
GetDestRectMCI(&grcSize);
|
|
rc = grcSize;
|
|
|
|
if (IsRectEmpty(&rc))
|
|
SetRect(&rc, 0, 0, 3*ICON_MINX, ICON_MINY);
|
|
|
|
/* Offset to title bar */
|
|
yOff = rc.bottom;
|
|
|
|
hdc = GetDC(NULL);
|
|
if (hdc == NULL)
|
|
return NULL;
|
|
hdcMem = CreateCompatibleDC(NULL);
|
|
if (hdcMem == NULL) {
|
|
ReleaseDC(NULL, hdc);
|
|
return NULL;
|
|
}
|
|
|
|
if (gwOptions & OPT_TITLE) {
|
|
if (ghfontMap)
|
|
hfontOld = SelectObject(hdcMem, ghfontMap);
|
|
|
|
GetTextExtentPoint32(hdcMem, gachCaption, STRLEN(gachCaption), &TempSize);
|
|
xExt = max(TempSize.cx + 4, ICON_MINX);
|
|
yExt = TempSize.cy;
|
|
|
|
if (yExt > TITLE_HEIGHT) // don't let text be higher than bar
|
|
yExt = TITLE_HEIGHT;
|
|
if (xExt > rc.right) {
|
|
rc.left = (xExt - rc.right) / 2;
|
|
rc.right += rc.left;
|
|
} else {
|
|
xOff = (rc.right - xExt) /2;
|
|
xExt = rc.right;
|
|
}
|
|
if (rc.bottom < ICON_MINY) {
|
|
yOff = ICON_MINY;
|
|
rc.top = (ICON_MINY - rc.bottom) / 2;
|
|
rc.bottom += rc.top;
|
|
}
|
|
xSize = xExt; ySize = yOff + TITLE_HEIGHT;
|
|
} else {
|
|
if (rc.right < ICON_MINX) {
|
|
rc.left = (ICON_MINX - rc.right) / 2;
|
|
rc.right += rc.left;
|
|
}
|
|
if (rc.bottom < ICON_MINY) {
|
|
rc.top = (ICON_MINY - rc.bottom) / 2;
|
|
rc.bottom += rc.top;
|
|
}
|
|
xSize = max(rc.right, ICON_MINX);
|
|
ySize = max(rc.bottom, ICON_MINY);
|
|
}
|
|
|
|
/* Big enough to hold text caption too, if necessary */
|
|
hbm = CreateCompatibleBitmap(hdc, xSize, ySize);
|
|
|
|
ReleaseDC(NULL, hdc);
|
|
if (hbm == NULL) {
|
|
DeleteDC(hdcMem);
|
|
return NULL;
|
|
}
|
|
|
|
hbmT = SelectObject(hdcMem, hbm);
|
|
|
|
hbrOld = SelectObject(hdcMem, hbrWindowColour);
|
|
PatBlt(hdcMem, 0,0, xSize, ySize, PATCOPY);
|
|
SelectObject(hdcMem, hbrOld);
|
|
|
|
if (gwOptions & OPT_TITLE) {
|
|
hbrOld = SelectObject(hdcMem, hbrButtonFace);
|
|
PatBlt(hdcMem, 0, rc.bottom, xExt, TITLE_HEIGHT, PATCOPY);
|
|
SetBkMode(hdcMem, TRANSPARENT);
|
|
SetTextColor(hdcMem, rgbButtonText);
|
|
/* Centre text vertically in title bar */
|
|
TextOut(hdcMem, xOff + 2, yOff + (TITLE_HEIGHT - yExt) / 2,
|
|
gachCaption, STRLEN(gachCaption));
|
|
if (hbrOld)
|
|
SelectObject(hdcMem, hbrOld);
|
|
if (ghfontMap)
|
|
SelectObject(hdcMem, hfontOld);
|
|
}
|
|
|
|
/* Use our ICON as the picture */
|
|
if (gwDeviceID == (UINT)0 || !(gwDeviceType & DTMCI_CANWINDOW)) {
|
|
xIconOffset = rc.left + (rc.right-rc.left-ICON_MINX)/2;
|
|
xIconOffset = xIconOffset < 0 ? 0: xIconOffset;
|
|
DrawIcon(hdcMem, xIconOffset, rc.top,
|
|
GetIconForCurrentDevice(GI_LARGE, IDI_DDEFAULT));
|
|
|
|
/* Use a frame of our file */
|
|
} else {
|
|
LOADSTRING(IDS_NOPICTURE, ach);
|
|
DrawText(hdcMem, ach, STRLEN(ach), &rc, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
|
|
|
|
mciUpdate.hDC = hdcMem;
|
|
|
|
mciUpdate.dwCallback = 0;
|
|
SetRectEmpty(&mciUpdate.rc);
|
|
|
|
/* NO matter what size our playback window is, we want to use the */
|
|
/* original size of the window as the picture we put on the clipbrd */
|
|
SetViewportOrgEx(hdcMem, rc.left, rc.top, NULL);
|
|
GetDestRectMCI(&rcSave);
|
|
SetDestRectMCI(&grcSize);
|
|
dw = mciSendCommand(gwDeviceID, MCI_UPDATE,
|
|
MCI_ANIM_UPDATE_HDC | MCI_WAIT, (DWORD_PTR)(LPVOID)&mciUpdate);
|
|
SetDestRectMCI(&rcSave);
|
|
SetViewportOrgEx(hdcMem, 0, 0, NULL);
|
|
}
|
|
|
|
if (gwOptions & OPT_BORDER) {
|
|
SetRect(&rc, 0, 0, xSize, ySize);
|
|
FrameRect(hdcMem, &rc, GetStockObject(BLACK_BRUSH));
|
|
|
|
if (gwOptions & OPT_TITLE) {
|
|
SetRect(&rc, 0, ySize - TITLE_HEIGHT, xSize, ySize-TITLE_HEIGHT+1);
|
|
FrameRect(hdcMem, &rc, GetStockObject(BLACK_BRUSH));
|
|
}
|
|
}
|
|
|
|
if (hbmT)
|
|
SelectObject(hdcMem, hbmT);
|
|
DeleteDC(hdcMem);
|
|
grcSize=rcs;
|
|
|
|
return hbm;
|
|
}
|
|
|
|
//
|
|
// if we are on a palette device, dither to the VGA colors
|
|
// for apps that dont deal with palettes!
|
|
//
|
|
void FAR PASCAL DitherMCI(HANDLE hdib, HPALETTE hpal)
|
|
{
|
|
LPBYTE lpBits;
|
|
int i;
|
|
LPBITMAPINFOHEADER lpbi;
|
|
|
|
DPF2("DitherMCI\n");
|
|
|
|
lpbi = (LPVOID)GLOBALLOCK(hdib);
|
|
|
|
if (lpbi == NULL)
|
|
return;
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// HACK!!! patch the fake gamma-corrected colors to match the VGA's
|
|
//
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
lpBits = (LPBYTE)(lpbi+1);
|
|
|
|
for (i=0; i<8*4; i++)
|
|
{
|
|
if (lpBits[i] == 191)
|
|
lpBits[i] = 128;
|
|
}
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
lpBits = (LPBYTE)(lpbi+1) + 256 * sizeof(RGBQUAD);
|
|
|
|
BltProp(lpbi,lpBits,0,0,(int)lpbi->biWidth,(int)lpbi->biHeight,
|
|
lpbi,lpBits,0,0);
|
|
|
|
GLOBALUNLOCK(hdib);
|
|
}
|
|
|
|
|
|
void FAR PASCAL CopyMCI(HWND hwnd)
|
|
{
|
|
HBITMAP hbm;
|
|
HPALETTE hpal;
|
|
HANDLE hdib;
|
|
HANDLE hmfp;
|
|
HDC hdc;
|
|
|
|
DPF2("CopyMCI\n");
|
|
|
|
if (gwDeviceID == (UINT)0)
|
|
return;
|
|
|
|
if (hwnd) {
|
|
if (!OpenClipboard(ghwndApp))
|
|
return;
|
|
|
|
EmptyClipboard();
|
|
}
|
|
|
|
hpal = PaletteMCI();
|
|
hbm = BitmapMCI();
|
|
hdib = DibFromBitmap(hbm, hpal);
|
|
hpal = CopyPalette(hpal);
|
|
|
|
//
|
|
// if we are on a palette device. possibly dither to the VGA colors
|
|
// for apps that dont deal with palettes!
|
|
//
|
|
hdc = GetDC(NULL);
|
|
if ((GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) &&
|
|
(gwOptions & OPT_DITHER) && (gwDeviceType & DTMCI_CANWINDOW)) {
|
|
DitherMCI(hdib, hpal);
|
|
hpal = NULL;
|
|
}
|
|
ReleaseDC(NULL, hdc);
|
|
|
|
hmfp = PictureFromDib(hdib, hpal);
|
|
|
|
if (hmfp)
|
|
SetClipboardData(CF_METAFILEPICT, hmfp);
|
|
|
|
if (hdib)
|
|
SetClipboardData(CF_DIB, hdib);
|
|
|
|
if (hpal)
|
|
SetClipboardData(CF_PALETTE, hpal);
|
|
|
|
//// we want people to pick the meta file always.
|
|
////if (hbm)
|
|
//// SetClipboardData(CF_BITMAP, hbm);
|
|
if (hbm)
|
|
DeleteObject(hbm);
|
|
|
|
/* If not everything can be copied to the clipboard, error out and */
|
|
/* don't put anything up there. */
|
|
if (!hmfp || !hdib) {
|
|
EmptyClipboard();
|
|
Error(ghwndApp, IDS_CANTCOPY);
|
|
}
|
|
|
|
if (hwnd)
|
|
CloseClipboard();
|
|
}
|
|
|
|
|
|
/* MCIWndProc()
|
|
*
|
|
* Window procedure for MCI element window.
|
|
* This also initiates the the OLE2 drag-drop data transfer if required.
|
|
*/
|
|
LONG_PTR FAR PASCAL _EXPORT
|
|
MCIWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
PAINTSTRUCT ps; // information from BeginPaint()
|
|
HDC hdc;
|
|
DWORD dw; // function return status
|
|
MCI_ANIM_UPDATE_PARMS mciUpdate;
|
|
RECT rc;
|
|
static BOOL fDragCapture = FALSE;
|
|
static RECT rcWin;
|
|
POINT pt;
|
|
|
|
switch (msg)
|
|
{
|
|
// case WM_NCHITTEST:
|
|
// return HTTRANSPARENT;
|
|
|
|
case WM_CREATE:
|
|
ghwndMCI = hwnd;
|
|
SetWindowMCI(hwnd);
|
|
break;
|
|
|
|
case WM_SIZE:
|
|
GetClientRect(hwnd, &rc);
|
|
SetDestRectMCI(&rc);
|
|
break;
|
|
|
|
case WM_CLOSE:
|
|
SetWindowMCI(NULL);
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
SetWindowMCI(NULL);
|
|
ghwndMCI = NULL;
|
|
CleanUpDrag();
|
|
break;
|
|
|
|
case WM_RBUTTONDOWN:
|
|
PostMessage(ghwndApp, WM_COMMAND, (WPARAM)ID_STOP, 0);
|
|
break;
|
|
|
|
case WM_LBUTTONDOWN:
|
|
switch(gwStatus) {
|
|
|
|
case MCI_MODE_PAUSE:
|
|
PostMessage(ghwndApp, WM_COMMAND, (WPARAM)ID_PLAY, 0);
|
|
break;
|
|
|
|
case MCI_MODE_PLAY:
|
|
case MCI_MODE_SEEK:
|
|
PostMessage(ghwndApp, WM_COMMAND, (WPARAM)ID_PAUSE, 0);
|
|
break;
|
|
|
|
default:
|
|
//Capture to initiate the drag drop operation.
|
|
if (!gfOle2IPEditing) {
|
|
fDragCapture = TRUE;
|
|
SetCapture(hwnd);
|
|
GetClientRect(hwnd, (LPRECT)&rcWin);
|
|
MapWindowPoints(hwnd, NULL, (LPPOINT)&rcWin, 2);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_LBUTTONDBLCLK:
|
|
SeekMCI(gdwMediaStart);
|
|
PostMessage(ghwndApp, WM_COMMAND, (WPARAM)ID_PLAY, 0);
|
|
break;
|
|
|
|
case WM_LBUTTONUP:
|
|
if (!fDragCapture)
|
|
break;
|
|
fDragCapture = FALSE;
|
|
ReleaseCapture();
|
|
break;
|
|
|
|
case WM_MOUSEMOVE:
|
|
//Initiate drag drop if outside the window.
|
|
if (!fDragCapture)
|
|
break;
|
|
LONG2POINT(lParam, pt);
|
|
MapWindowPoints(hwnd, NULL, &pt, 1);
|
|
|
|
if (!PtInRect((LPRECT)&rcWin, pt)) {
|
|
|
|
ReleaseCapture();
|
|
DoDrag();
|
|
fDragCapture = FALSE;
|
|
|
|
} else {
|
|
|
|
SetCursor(LoadCursor(ghInst,MAKEINTRESOURCE(IDC_DRAG)));
|
|
}
|
|
break;
|
|
|
|
case WM_PALETTECHANGED:
|
|
InvalidateRect(hwnd, NULL, TRUE);
|
|
break;
|
|
|
|
case WM_QUERYNEWPALETTE:
|
|
if (gwDeviceID && (gwDeviceType & DTMCI_CANWINDOW)) {
|
|
mciSendCommand(gwDeviceID, MCI_REALIZE,
|
|
MCI_ANIM_REALIZE_NORM, 0L);
|
|
}
|
|
break;
|
|
|
|
case WM_ERASEBKGND:
|
|
/* Don't erase the part we'll paint into cuz we'd flicker */
|
|
/* and flicker is bad. */
|
|
if (gwDeviceID && (gwDeviceType & DTMCI_CANWINDOW)) {
|
|
GetDestRectMCI(&rc);
|
|
SaveDC((HDC)wParam);
|
|
ExcludeClipRect((HDC)wParam, rc.left, rc.top, rc.right,
|
|
rc.bottom);
|
|
DefWindowProc(hwnd, msg, wParam, lParam);
|
|
RestoreDC((HDC)wParam, -1);
|
|
}
|
|
return 0;
|
|
|
|
case WM_PAINT:
|
|
hdc = BeginPaint(hwnd, &ps);
|
|
|
|
if (gwDeviceID)
|
|
{
|
|
GetClientRect(hwnd, &rc);
|
|
|
|
if (gwDeviceType & DTMCI_CANWINDOW) {
|
|
mciUpdate.hDC = hdc;
|
|
|
|
/*!!! should we send MCI_DGV_UPDATE_PAINT? to non dgv devices? */
|
|
|
|
dw = mciSendCommand(gwDeviceID, MCI_UPDATE,
|
|
MCI_ANIM_UPDATE_HDC | MCI_WAIT |
|
|
MCI_DGV_UPDATE_PAINT,
|
|
(DWORD_PTR)(LPVOID)&mciUpdate);
|
|
|
|
//
|
|
// if the update fails then erase
|
|
//
|
|
if (dw != 0)
|
|
DefWindowProc(hwnd, WM_ERASEBKGND, (WPARAM)hdc, 0);
|
|
|
|
}
|
|
}
|
|
EndPaint(hwnd, &ps);
|
|
return 0;
|
|
}
|
|
|
|
return DefWindowProc(hwnd, msg, wParam, lParam);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
HPALETTE CopyPalette(HPALETTE hpal)
|
|
{
|
|
PLOGPALETTE ppal;
|
|
int nNumEntries = 0; // MUST initialise. GetObject stores TWO bytes
|
|
int i;
|
|
|
|
if (!hpal)
|
|
return NULL;
|
|
|
|
GetObject(hpal,sizeof(int),&nNumEntries);
|
|
|
|
if (nNumEntries == 0)
|
|
return NULL;
|
|
|
|
ppal = AllocMem(sizeof(LOGPALETTE) + nNumEntries * sizeof(PALETTEENTRY));
|
|
|
|
if (!ppal)
|
|
return NULL;
|
|
|
|
ppal->palVersion = 0x300;
|
|
ppal->palNumEntries = (USHORT)nNumEntries;
|
|
|
|
GetPaletteEntries(hpal,0,nNumEntries,ppal->palPalEntry);
|
|
|
|
for (i=0; i<nNumEntries; i++)
|
|
ppal->palPalEntry[i].peFlags = 0;
|
|
|
|
hpal = CreatePalette(ppal);
|
|
|
|
FreeMem(ppal, sizeof(LOGPALETTE) + nNumEntries * sizeof(PALETTEENTRY));
|
|
|
|
return hpal;
|
|
}
|
|
|
|
|
|
#ifdef UNUSED
|
|
HANDLE PictureFromBitmap(HBITMAP hbm, HPALETTE hpal)
|
|
{
|
|
LPMETAFILEPICT pmfp;
|
|
HANDLE hmfp;
|
|
HANDLE hmf;
|
|
HANDLE hdc;
|
|
HDC hdcMem;
|
|
BITMAP bm;
|
|
HBITMAP hbmT;
|
|
|
|
if (!hbm)
|
|
return NULL;
|
|
|
|
GetObject(hbm, sizeof(bm), (LPVOID)&bm);
|
|
|
|
hdcMem = CreateCompatibleDC(NULL);
|
|
if (!hdcMem)
|
|
return NULL;
|
|
hbmT = SelectObject(hdcMem, hbm);
|
|
|
|
hdc = CreateMetaFile(NULL);
|
|
if (!hdc) {
|
|
DeleteDC(hdcMem);
|
|
return NULL;
|
|
}
|
|
|
|
SetWindowOrgEx (hdc, 0, 0, NULL);
|
|
SetWindowExtEx (hdc, bm.bmWidth, bm.bmHeight, NULL);
|
|
|
|
if (hpal)
|
|
{
|
|
SelectPalette(hdcMem,hpal,FALSE);
|
|
RealizePalette(hdcMem);
|
|
SelectPalette(hdc,hpal,FALSE);
|
|
RealizePalette(hdc);
|
|
}
|
|
|
|
SetStretchBltMode(hdc, COLORONCOLOR);
|
|
BitBlt(hdc,0,0,bm.bmWidth,bm.bmHeight,hdcMem,0,0,SRCCOPY);
|
|
|
|
hmf = CloseMetaFile(hdc);
|
|
|
|
SelectObject(hdcMem, hbmT);
|
|
DeleteDC(hdcMem);
|
|
|
|
if (hmfp = GlobalAlloc(GMEM_DDESHARE|GMEM_MOVEABLE, sizeof(METAFILEPICT)))
|
|
{
|
|
pmfp = (LPMETAFILEPICT)GLOBALLOCK(hmfp);
|
|
|
|
hdc = GetDC(NULL);
|
|
#if 1
|
|
pmfp->mm = MM_ANISOTROPIC;
|
|
pmfp->hMF = hmf;
|
|
pmfp->xExt = MulDiv(bm.bmWidth ,2540,GetDeviceCaps(hdc, LOGPIXELSX));
|
|
pmfp->yExt = MulDiv(bm.bmHeight,2540,GetDeviceCaps(hdc, LOGPIXELSX));
|
|
#else
|
|
pmfp->mm = MM_TEXT;
|
|
pmfp->hMF = hmf;
|
|
pmfp->xExt = bm.bmWidth;
|
|
pmfp->yExt = bm.bmHeight;
|
|
#endif
|
|
ReleaseDC(NULL, hdc);
|
|
}
|
|
else
|
|
{
|
|
DeleteMetaFile(hmf);
|
|
}
|
|
|
|
return hmfp;
|
|
}
|
|
#endif /* UNUSED */
|
|
|
|
HANDLE FAR PASCAL PictureFromDib(HANDLE hdib, HPALETTE hpal)
|
|
{
|
|
LPMETAFILEPICT pmfp;
|
|
HANDLE hmfp;
|
|
HANDLE hmf;
|
|
HANDLE hdc;
|
|
LPBITMAPINFOHEADER lpbi;
|
|
|
|
if (!hdib)
|
|
return NULL;
|
|
|
|
lpbi = (LPVOID)GLOBALLOCK(hdib);
|
|
if (lpbi->biClrUsed == 0 && lpbi->biBitCount <= 8)
|
|
lpbi->biClrUsed = 1 << lpbi->biBitCount;
|
|
|
|
hdc = CreateMetaFile(NULL);
|
|
if (!hdc)
|
|
return NULL;
|
|
|
|
SetWindowOrgEx(hdc, 0, 0, NULL);
|
|
SetWindowExtEx(hdc, (int)lpbi->biWidth, (int)lpbi->biHeight, NULL);
|
|
|
|
if (hpal)
|
|
{
|
|
SelectPalette(hdc,hpal,FALSE);
|
|
RealizePalette(hdc);
|
|
}
|
|
|
|
SetStretchBltMode(hdc, COLORONCOLOR);
|
|
|
|
StretchDIBits(hdc,
|
|
0,0,(int)lpbi->biWidth, (int)lpbi->biHeight,
|
|
0,0,(int)lpbi->biWidth, (int)lpbi->biHeight,
|
|
(LPBYTE)lpbi + (int)lpbi->biSize + (int)lpbi->biClrUsed * sizeof(RGBQUAD),
|
|
(LPBITMAPINFO)lpbi,
|
|
DIB_RGB_COLORS,
|
|
SRCCOPY);
|
|
|
|
if (hpal)
|
|
SelectPalette(hdc, GetStockObject(DEFAULT_PALETTE), FALSE);
|
|
|
|
hmf = CloseMetaFile(hdc);
|
|
|
|
hmfp = GlobalAlloc(GMEM_DDESHARE|GMEM_MOVEABLE, sizeof(METAFILEPICT));
|
|
|
|
if (hmfp)
|
|
{
|
|
pmfp = (LPMETAFILEPICT)GLOBALLOCK(hmfp);
|
|
|
|
hdc = GetDC(NULL);
|
|
#if 1
|
|
pmfp->mm = MM_ANISOTROPIC;
|
|
pmfp->hMF = hmf;
|
|
pmfp->xExt = MulDiv((int)lpbi->biWidth ,2540,GetDeviceCaps(hdc, LOGPIXELSX));
|
|
pmfp->yExt = MulDiv((int)lpbi->biHeight,2540,GetDeviceCaps(hdc, LOGPIXELSY));
|
|
extWidth = pmfp->xExt;
|
|
extHeight = pmfp->yExt;
|
|
DPF1("PictureFromDib: Bitmap %d x %d; metafile %d x %d\n", lpbi->biWidth, lpbi->biHeight, extWidth, extHeight);
|
|
#else
|
|
pmfp->mm = MM_TEXT;
|
|
pmfp->hMF = hmf;
|
|
pmfp->xExt = (int)lpbi->biWidth;
|
|
pmfp->yExt = (int)lpbi->biHeight;
|
|
#endif
|
|
|
|
ReleaseDC(NULL, hdc);
|
|
}
|
|
else
|
|
{
|
|
DeleteMetaFile(hmf);
|
|
}
|
|
|
|
GLOBALUNLOCK(hdib);
|
|
GLOBALUNLOCK(hmfp);
|
|
|
|
return hmfp;
|
|
}
|
|
|
|
#define WIDTHBYTES(i) ((unsigned)((i+31)&(~31))/8) /* ULONG aligned ! */
|
|
|
|
/*
|
|
* DibFromBitmap()
|
|
*
|
|
* Will create a global memory block in DIB format that represents the DDB
|
|
* passed in
|
|
*
|
|
*/
|
|
HANDLE FAR PASCAL DibFromBitmap(HBITMAP hbm, HPALETTE hpal)
|
|
{
|
|
BITMAP bm;
|
|
BITMAPINFOHEADER bi;
|
|
BITMAPINFOHEADER FAR *lpbi;
|
|
DWORD dw;
|
|
HANDLE hdib;
|
|
HDC hdc;
|
|
HPALETTE hpalT;
|
|
|
|
if (!hbm)
|
|
return NULL;
|
|
|
|
GetObject(hbm,sizeof(bm),&bm);
|
|
|
|
bi.biSize = sizeof(BITMAPINFOHEADER);
|
|
bi.biWidth = bm.bmWidth;
|
|
bi.biHeight = bm.bmHeight;
|
|
bi.biPlanes = 1;
|
|
bi.biBitCount = (bm.bmPlanes * bm.bmBitsPixel) > 8 ? 24 : 8;
|
|
bi.biCompression = BI_RGB;
|
|
bi.biSizeImage = (DWORD)WIDTHBYTES(bi.biWidth * bi.biBitCount) * bi.biHeight;
|
|
bi.biXPelsPerMeter = 0;
|
|
bi.biYPelsPerMeter = 0;
|
|
bi.biClrUsed = bi.biBitCount == 8 ? 256 : 0;
|
|
bi.biClrImportant = 0;
|
|
|
|
dw = bi.biSize + bi.biClrUsed * sizeof(RGBQUAD) + bi.biSizeImage;
|
|
|
|
hdib = GlobalAlloc(GHND | GMEM_DDESHARE, dw);
|
|
|
|
if (!hdib)
|
|
return NULL;
|
|
|
|
lpbi = (LPBITMAPINFOHEADER)GLOBALLOCK(hdib);
|
|
*lpbi = bi;
|
|
|
|
hdc = CreateCompatibleDC(NULL);
|
|
|
|
if (hpal && hdc)
|
|
{
|
|
hpalT = SelectPalette(hdc,hpal,FALSE);
|
|
RealizePalette(hdc);
|
|
}
|
|
|
|
GetDIBits(hdc, hbm, 0, (UINT)bi.biHeight,
|
|
(LPBYTE)lpbi + (int)lpbi->biSize + (int)lpbi->biClrUsed * sizeof(RGBQUAD),
|
|
(LPBITMAPINFO)lpbi, DIB_RGB_COLORS);
|
|
|
|
if (hpal)
|
|
SelectPalette(hdc,hpalT,FALSE);
|
|
|
|
if (hdc)
|
|
DeleteDC(hdc);
|
|
|
|
GLOBALUNLOCK(hdib);
|
|
|
|
return hdib;
|
|
}
|
|
|
|
/* CreateSystemPalette()
|
|
*
|
|
* Return a palette which represents the system (physical) palette.
|
|
* By selecting this palette into a screen DC and realizing the palette,
|
|
* the exact physical mapping will be restored
|
|
*
|
|
* one use for this is when "snapping" the screen as a bitmap
|
|
*
|
|
* On error (e.g. out of memory), NULL is returned.
|
|
*/
|
|
HPALETTE FAR PASCAL CreateSystemPalette()
|
|
{
|
|
HDC hdc; // DC onto the screen
|
|
int iSizePalette; // size of entire palette
|
|
int iFixedPalette; // number of reserved colors
|
|
int i;
|
|
|
|
struct {
|
|
WORD palVersion;
|
|
WORD palNumEntries;
|
|
PALETTEENTRY palPalEntry[256];
|
|
} pal;
|
|
|
|
hdc = GetDC(NULL);
|
|
|
|
if (!(GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE))
|
|
{
|
|
ReleaseDC(NULL,hdc);
|
|
return NULL;
|
|
}
|
|
|
|
iSizePalette = GetDeviceCaps(hdc, SIZEPALETTE);
|
|
|
|
//
|
|
// determine the number of 'static' system colors that
|
|
// are currently reserved
|
|
//
|
|
if (GetSystemPaletteUse(hdc) == SYSPAL_STATIC)
|
|
iFixedPalette = GetDeviceCaps(hdc, NUMCOLORS);
|
|
else
|
|
iFixedPalette = 2;
|
|
|
|
//
|
|
// create a logical palette containing the system colors;
|
|
// this palette has all entries except fixed (system) colors
|
|
// flagged as PC_NOCOLLAPSE
|
|
//
|
|
pal.palVersion = 0x300;
|
|
pal.palNumEntries = (USHORT)iSizePalette;
|
|
|
|
GetSystemPaletteEntries(hdc, 0, iSizePalette, pal.palPalEntry);
|
|
|
|
ReleaseDC(NULL,hdc);
|
|
|
|
for (i = iFixedPalette/2; i < iSizePalette-iFixedPalette/2; i++)
|
|
pal.palPalEntry[i].peFlags = PC_NOCOLLAPSE;
|
|
|
|
return CreatePalette((LPLOGPALETTE)&pal);
|
|
}
|