|
|
/*-----------------------------------------------------------------------------+
| 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 = 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); }
|