NT4/private/windows/media/winmm/mmwnd.c
2020-09-30 17:12:29 +02:00

569 lines
15 KiB
C

/******************************************************************************
Copyright (C) Microsoft Corporation 1985-1990. All rights reserved.
Title: mmwnd.c - contains the window procedure for the WINMM 'global'
window
the global window is used by sndPlaySound and MCI for
reciving notification messages.
Version: 1.00
Date: 04-Sep-1990
Author: ToddLa
Changes: SteveDav Jan 92 Ported to NT
*****************************************************************************/
#include "winmmi.h"
#include "mci.h"
// WINMMI.H includes WINDOWS.H which will eventually include WINMM.H
//#ifdef DBG
// #include "netname.h"
//#endif // DBG
#define CLASS_NAME MAKEINTATOM(43) // 42 clashes with 16-bit mmsystem
DWORD mciWindowThreadId;
STATICFN LRESULT mmWndProc(HWND hwnd, MMMESSAGE msg, WPARAM wParam, LPARAM lParam);
STATICFN BOOL WaitForWaitMsg(void);
typedef struct SentMsg {
ULONG Result;
MMMESSAGE msg;
WPARAM wParam;
LPARAM lParam;
UINT SendingThread;
} SENTMSG, * PSENTMSG;
/*
** Client notification stuff
*/
HWND hwndNotify = NULL;
/*
** Server notification stuff
*/
PGLOBALMCI base;
CRITICAL_SECTION mciGlobalCritSec;
HANDLE hEvent;
/***************************************************************************/
STATICDT BOOL classcreated = FALSE;
STATICFN BOOL PASCAL FAR CreateMMClass(
void)
{
WNDCLASS cls;
if (classcreated) {
return(TRUE);
}
ZeroMemory(&cls, sizeof(WNDCLASS));
cls.hCursor = NULL;
cls.hIcon = LoadIcon(ghInst, MAKEINTRESOURCE(IDI_MCIHWND));
cls.lpszMenuName = NULL;
cls.lpszClassName = CLASS_NAME;
cls.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
cls.hInstance = ghInst;
cls.style = CS_GLOBALCLASS;
cls.lpfnWndProc = (WNDPROC)mmWndProc;
cls.cbWndExtra = 0;
cls.cbClsExtra = 0;
classcreated = RegisterClass(&cls);
return classcreated;
}
STATICDT CHAR mciWndName[] = "MCI command handling window";
//
//
//
BOOL mciGlobalInit(
void)
{
return TRUE;
}
//
//
//
STATICFN DWORD mciwnd2(LPVOID lpParams)
{
UINT msg;
LPCWSTR lszSound;
DWORD wFlags;
DWORD n;
WCHAR soundname[MAX_PATH];
while (TRUE) {
LockMCIGlobal;
if (!base->msg) {
#ifdef LATER
This still needs to be tidied up. The intention is to have a
list of sounds that should be played. This will also make it
easier to STOP all sound playing by clearing out the list.
#endif
// We have no work to do; reset the event and wait for
// more work to be posted. By setting the event within
// the lock we are safe from timing windows.
ResetMCIEvent(hEvent);
UnlockMCIGlobal;
dprintf2(("MCIWND2 thread waiting for next event..."));
n = WaitForSingleObject(hEvent, WAIT_FOREVER);
#if DBG
if ((DWORD)-1 == n) {
n = GetLastError();
dprintf2(("Error %d waiting on event in worker thread", n));
}
#endif
LockMCIGlobal;
}
msg = base->msg;
wFlags = base->dwFlags;
lszSound = base->lszSound;
base->msg=0;
if (wFlags & SND_FILENAME) {
// Have to copy the file name
wcscpy(soundname, base->szSound);
lszSound = soundname;
dprintf3(("Copying the soundfile name to a local variable: %ls", lszSound));
} else {
dprintf3(("Playing a system sound"));
}
UnlockMCIGlobal;
PlaySoundW(lszSound, NULL, (wFlags & ~SND_ASYNC)); // Play sync
}
#if DBG
dprintf(("MCIWND2 thread ending...!!"));
#endif
return(0);
}
/***************************************************************************
*
* @doc INTERNAL WINMM
*
* @api void | WndTerminate | called when WINMM is terminating
*
***************************************************************************/
STATICFN void NEAR PASCAL WndTerminate(
void)
{
dprintf1(("hwndNotify terminating"));
if (hwndNotify)
{
dprintf1(("sending Close\n"));
SendMessage(hwndNotify, WM_CLOSE, 0, 0L);
UnregisterClass(CLASS_NAME, ghInst);
}
}
/***************************************************************************
*
* @doc INTERNAL WINMM
*
* @api LRESULT | mmWndProc | The Window procedure for the WINMM window
*
* @comm mmWndProc calls DefWindowProc for all messages except:
*
* MM_MCINOTIFY: calls MciNotify() in MCI.C
* MM_WOM_DONE: calls WaveOutNotify() in PLAYWAV.C
*
* @xref sndPlaySound
*
***************************************************************************/
STATICFN LRESULT mmWndProc(
HWND hwnd,
MMMESSAGE msg,
WPARAM wParam,
LPARAM lParam)
{
#if DBG
dprintf4(("MMWNDPROC: Msg %5x Hwnd=%8x\r\n wParam=%8x lParam=%8x", msg, hwnd, wParam, lParam));
#endif
switch (msg)
{
case WM_CREATE:
hwndNotify = hwnd;
break;
case MM_MCINOTIFY:
MciNotify(wParam, (LONG)lParam);
break;
#define NODELAY
#ifdef NODELAY
case MM_WOM_DONE:
/*
The sound started with sndPlaySound has completed
so we should call the cleanup routine. On NT we do NOT
delay as the wave really has finished playing.
*/
dprintf2(("Received MM_WOM_DONE, calling WaveOutNotify"));
WaveOutNotify(0,0);
break;
#else
/*
SOUND_DELAY is the number of ms to delay before closing the wave device
after the buffer is done.
*/
#define SOUND_DELAY 300
case WM_TIMER:
KillTimer(hwnd, (UINT)wParam);
WaveOutNotify(0,0);
break;
case MM_WOM_DONE:
/*
The sound started with sndPlaySound has completed
so we should call the cleanup routine. We delay
this call for several hundred milliseconds because
some sound drivers have a nasty characteristic - they
will notify before the final DMA transfer is complete
because the app. supplied buffer is no longer required.
This means that they may have to spin inside a close
request until the dma transfer completes. This hangs
the system for hundreds of milliseconds.
*/
dprintf2(("Received MM_WOM_DONE, setting timer delay"));
SetTimer(hwndNotify, 1, SOUND_DELAY, NULL);
break;
#endif
case MM_SND_ABORT: /* Do not need to do anything */
break;
case MM_SND_PLAY:
{
// There is a critical section problem as we have one global, and
// sounds being played on separate threads.
MSG abortmsg;
if (SND_ALIAS_ID == (wParam & SND_ALIAS_ID)) {
return((LRESULT)PlaySound((LPCSTR)lParam, NULL, wParam & ~SND_ASYNC));
}
if (!PeekMessage(&abortmsg, hwnd, MM_SND_ABORT, MM_SND_ABORT, PM_NOREMOVE)) {
// There is no pending synchronous sound
return (LRESULT)(LONG)sndMessage((LPWSTR)lParam, (UINT)wParam);
}
// We must free the sound definition. Note that this does not close
// the critical section as we may be past this check point when the
// synchronous sound causes the abort message to be posted. But it
// will prevent spurious code being run. It is perfectly valid for
// an asynchronous sound to be after the abort message, which is
// why the message is not removed at this point.
dprintf3(("Aborting sound..."));
if (!(wParam & SND_MEMORY)) {
LocalFree((HANDLE)lParam);
}
break;
}
case MM_SND_SEND:
((PSENTMSG)wParam)->Result =
mmWndProc(NULL, ((PSENTMSG)wParam)->msg,
((PSENTMSG)wParam)->wParam,
((PSENTMSG)wParam)->lParam);
PostThreadMessage(((PSENTMSG)wParam)->SendingThread, MCIWAITMSG, 0, 0);
break;
case MM_POLYMSGBUFRDONE:
--(((PMIDIEMU)wParam)->cPostedBuffers);
midiOutNukePMBuffer((PMIDIEMU)wParam, (LPMIDIHDR)lParam);
return (0L);
case MM_MCISYSTEM_STRING:
// In MCI.C
return (LRESULT)mciRelaySystemString ((LPMCI_SYSTEM_MESSAGE)lParam);
default:
return DefWindowProc(hwnd, msg, wParam,lParam);
}
return (LRESULT)0L;
}
void mciwindow(HANDLE hEvent);
/*
** Initialize all the bits for creating sound. For non-server apps this
** means initializing our hwnd. For the server we set up a thread et
*/
BOOL InitAsyncSound(VOID)
{
if (!WinmmRunningInServer) {
return CreatehwndNotify();
} else {
LockMCIGlobal;
if (base == NULL) {
HANDLE hThread;
PGLOBALMCI pBase;
/*
** We need a thread, an event (we already have the crit sec) and
** some memory
*/
pBase = mciAlloc(sizeof(GLOBALMCI));
if (pBase == NULL) {
UnlockMCIGlobal;
return FALSE;
}
hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (hEvent == NULL) {
mciFree((PVOID)pBase);
UnlockMCIGlobal;
return FALSE;
}
/*
** We have to create a thread by a special method inside the
** server and register it with CSR
*/
if (!CreateServerPlayingThread((PVOID)mciwnd2)) {
mciFree((PVOID)pBase);
CloseHandle(hEvent);
hEvent = NULL;
UnlockMCIGlobal;
return FALSE;
}
base = pBase;
}
UnlockMCIGlobal;
return base != NULL;
}
}
BOOL CreatehwndNotify(VOID)
{
HANDLE hWindowThread;
BOOL ReturnCode;
HANDLE hEvent;
mciEnter("CreatehwndNotify");
if (hwndNotify != NULL) {
mciLeave("CreatehwndNotify");
return TRUE;
}
if (!CreateMMClass()) {
dprintf1(("Failed to create the MCI global window class, rc=%d", GetLastError()));
mciLeave("CreatehwndNotify");
return FALSE;
} else {
dprintf4(("Created global window class"));
}
// We create our new thread then suspend ourselves until the new
// thread has called CreateWindow. We are then triggered to run
// and passed the results of the CreateWindow call. NOTE: Any
// messages that arrive for this thread that are not destined for
// a specific window will be DISCARDED until the one message we
// are waiting for arrives. We could create an event and wait
// for that event to be triggered. This was slightly quicker to
// code and involves less creation/destruction of resources.
hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (hEvent != NULL) {
hWindowThread = CreateThread(NULL, // attributes
0, // same stack size as thread 1
(LPTHREAD_START_ROUTINE)mciwindow,
(LPVOID) hEvent,
0, // Thread runs immediately
&mciWindowThreadId
);
CloseHandle(hWindowThread);
if (!hWindowThread) {
dprintf1(("Failed to create window thread. Error: %XH", GetLastError()));
} else {
dprintf3(("Window thread is %x", mciWindowThreadId));
WaitForSingleObject(hEvent, INFINITE);
dprintf3(("hwndNotify now %x", hwndNotify));
}
CloseHandle(hEvent);
}
ReturnCode = hwndNotify != NULL;
mciLeave("CreatehwndNotify");
return ReturnCode;
}
void mciwindow(
HANDLE hEvent)
{
BOOL fResult = TRUE;
//
// Higher priority so we hear the sound at once!
// This seems to work better than calling SetThreadPriority
// on the handle just after creation (?).
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
if (!(hwndNotify = CreateWindowEx(0, CLASS_NAME, mciWndName, WS_OVERLAPPED,
0, 0, 0, 0, NULL, NULL, ghInst, NULL))) {
dprintf1(("Failed to create the MCI global window, rc=%d", GetLastError()));
UnregisterClass(CLASS_NAME, ghInst);
fResult = FALSE;
}
//
// Let our creator thread know we are up and running
//
SetEvent(hEvent);
if (fResult) {
MSG msg;
HWND hwndTemp;
while (GetMessage(&msg, NULL, 0, 0)) {
/*
* If the message is for a window dispatch it
*/
dprintf3(("mciwindow - Msg %5x hwnd %8x (%8x %8x)", msg.message, msg.hwnd, msg.wParam, msg.lParam));
if (msg.hwnd != NULL) {
DispatchMessage(&msg);
}
}
hwndTemp = hwndNotify;
hwndNotify = NULL; // Clear the global before destroying the window
DestroyWindow(hwndTemp);
}
ExitThread(0);
}
#define mciDeleteCrit() (DeleteCriticalSection(&mciCritSec))
VOID mciCleanup(VOID)
{
mciDeleteCrit();
if (WinmmRunningInServer) {
DeleteCriticalSection(&mciGlobalCritSec);
}
}
#if 0 //LATER - not currently used
//
// Routine to SEND (synchronous) a message to another thread. Currently
// the standard API allows you to send a message to a window, or post to
// a thread. There are circumstances when it would be helpful to send
// to a thread.
//
STATICFN LRESULT SendThreadMessage(
UINT tid,
MMMESSAGE msg,
WPARAM wParam,
LPARAM lParam)
{
SENTMSG smsg;
smsg.msg = msg;
smsg.wParam = wParam;
smsg.lParam = lParam;
smsg.SendingThread = GetCurrentThreadId();
PostThreadMessage(tid, MM_SND_SEND, (WPARAM)&smsg, 0);
WaitForWaitMsg();
return(smsg.Result);
}
#endif
/*********************************************************************\
* WaitForWaitMsg: *
* *
* This routine waits until a specific message is returned to this *
* thread. While waiting NO posted messages are processed, but sent *
* messages will be handled within GetMessage. The routine is used *
* to synchronise two threads of execution, and to implement a *
* synchronous PostMessage operation between threads. *
* *
\*********************************************************************/
STATICFN BOOL WaitForWaitMsg() {
for (;;) {
MSG msg;
/*
* Retrieve our particular message
*/
GetMessage(&msg, NULL, MCIWAITMSG, MCIWAITMSG);
/*
* If the message is for a window dispatch it
*/
WinAssert(msg.hwnd == NULL);
#if 0
if (msg.hwnd != NULL) { // This should not be executed.
DispatchMessage(&msg); // MCIWAITMSG is not sent to a window
} else
#endif
/*
* MCIWAITMSG is the signal message
*/
if (msg.message == MCIWAITMSG) {
break;
}
}
return(TRUE);
}