windows-nt/Source/XPSP1/NT/multimedia/media/mplayer2/mci.c

3182 lines
90 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*-----------------------------------------------------------------------------+
| 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);
}