/*-----------------------------------------------------------------------------+ | 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 #include #include #include #include #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 * * is the MCI device ID of the currently-open device, or NULL * if no device is open. is the length of the entire medium * in milliseconds. If is not NULL, then: * -- is the number of tracks on the medium, or 0 if the * medium doesn't support tracks * -- is the number of the first track, currently constrained * to be 0 or 1. * -- 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 * -- 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 and . * may be "" if a "pure device" (e.g. "CDAudio") is to be opened. * may be "" if a file is to be opened with an implicit type. * However, and 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 // 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 "\0\0 ... \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 and // sets the 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 , , , and * 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 * 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 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 * . * */ 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 (measured in milliseconds from 0L to * 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 + , where * 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 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 is not NULL, then <*pdwPosition> is filled in with * the current position of the device within the medium (in milliseconds, * from 0 to *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 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 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; ipalPalEntry[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); }