Fixed a race condition on RPC worker thread creation, and a typo.
[wine] / dlls / msvideo / mciwnd.c
1 /*
2  * Copyright 2000 Eric Pouech
3  *
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19
20 #define COM_NO_WINDOWS_H
21 #include <stdio.h>
22 #include <string.h>
23
24 #include "winbase.h"
25 #include "windef.h"
26 #include "winnls.h"
27 #include "wingdi.h"
28 #include "winuser.h"
29 #include "vfw.h"
30 #include "digitalv.h"
31 #include "commctrl.h"
32 #include "wine/debug.h"
33
34 WINE_DEFAULT_DEBUG_CHANNEL(mci);
35
36 typedef struct {
37    DWORD        dwStyle;
38    MCIDEVICEID  mci;
39    LPSTR        lpName;
40    HWND         hWnd;
41    UINT         uTimer;
42 } MCIWndInfo;
43
44 static LRESULT WINAPI   MCIWndProc(HWND hWnd, UINT wMsg, WPARAM lParam1, LPARAM lParam2);
45
46 #define CTL_PLAYSTOP    0x3200
47 #define CTL_MENU        0x3201
48 #define CTL_TRACKBAR    0x3202
49
50 /***********************************************************************
51  *              MCIWndRegisterClass             [MSVFW32.@]
52  */
53 BOOL WINAPI MCIWndRegisterClass(HINSTANCE hInst)
54 {
55    WNDCLASSA            wc;
56
57    /* since window creation will also require some common controls, init them */
58    InitCommonControls();
59
60    wc.style = 0;
61    wc.lpfnWndProc = MCIWndProc;
62    wc.cbClsExtra = 0;
63    wc.cbWndExtra = sizeof(MCIWndInfo*);
64    wc.hInstance = hInst;
65    wc.hIcon = 0;
66    wc.hCursor = 0;
67    wc.hbrBackground = 0;
68    wc.lpszMenuName = NULL;
69    wc.lpszClassName = "MCIWndClass";
70
71    return RegisterClassA(&wc);
72
73 }
74
75 /***********************************************************************
76  *              MCIWndCreate            [MSVFW32.@]
77  *              MCIWndCreateA           [MSVFW32.@]
78  */
79 HWND VFWAPIV MCIWndCreateA(HWND hwndParent, HINSTANCE hInstance,
80                            DWORD dwStyle, LPCSTR szFile)
81 {
82    DWORD        wndStyle;
83    MCIWndInfo*  mwi;
84
85    TRACE("%p %p %lx %s\n", hwndParent, hInstance, dwStyle, szFile);
86
87    MCIWndRegisterClass(hInstance);
88
89    mwi = HeapAlloc(GetProcessHeap(), 0, sizeof(*mwi));
90    if (!mwi) return 0;
91
92    mwi->dwStyle = dwStyle;
93    if (szFile)
94      mwi->lpName = strcpy(HeapAlloc(GetProcessHeap(), 0, strlen(szFile) + 1), szFile);
95    else
96      mwi->lpName = NULL;
97    mwi->uTimer = 0;
98
99    wndStyle = ((hwndParent) ? (WS_CHILD|WS_BORDER) : WS_OVERLAPPEDWINDOW) |
100               WS_VISIBLE | (dwStyle & 0xFFFF0000);
101
102    if (CreateWindowExA(0, "MCIWndClass", NULL, wndStyle,
103                        CW_USEDEFAULT, CW_USEDEFAULT,
104                        CW_USEDEFAULT, CW_USEDEFAULT,
105                        hwndParent, NULL, hInstance, mwi))
106       return mwi->hWnd;
107
108    if(mwi->lpName) HeapFree(GetProcessHeap(), 0, mwi->lpName);
109    HeapFree(GetProcessHeap(), 0, mwi);
110    return 0;
111 }
112
113 /***********************************************************************
114  *              MCIWndCreateW                           [MSVFW32.@]
115  */
116 HWND VFWAPIV MCIWndCreateW(HWND hwndParent, HINSTANCE hInstance,
117                            DWORD dwStyle, LPCWSTR szFile)
118 {
119    FIXME("%p %p %lx %s\n", hwndParent, hInstance, dwStyle, debugstr_w(szFile));
120
121    MCIWndRegisterClass(hInstance);
122
123    return 0;
124 }
125
126 static DWORD MCIWND_GetStatus(MCIWndInfo* mwi)
127 {
128    MCI_DGV_STATUS_PARMSA        mdsp;
129
130    memset(&mdsp, 0, sizeof(mdsp));
131    mdsp.dwItem = MCI_STATUS_MODE;
132    if (mciSendCommandA(mwi->mci, MCI_STATUS, MCI_WAIT|MCI_STATUS_ITEM, (DWORD)&mdsp))
133       return MCI_MODE_NOT_READY;
134    if (mdsp.dwReturn == MCI_MODE_STOP && mwi->uTimer) {
135       TRACE("Killing timer\n");
136       KillTimer(mwi->hWnd, 0);
137       mwi->uTimer = 0;
138    }
139    return mdsp.dwReturn;
140 }
141
142 static DWORD MCIWND_Get(MCIWndInfo* mwi, DWORD what)
143 {
144    MCI_DGV_STATUS_PARMSA        mdsp;
145
146    memset(&mdsp, 0, sizeof(mdsp));
147    mdsp.dwItem = what;
148    if (mciSendCommandA(mwi->mci, MCI_STATUS, MCI_WAIT|MCI_STATUS_ITEM, (DWORD)&mdsp))
149       return 0;
150    return mdsp.dwReturn;
151 }
152
153 static void MCIWND_SetText(MCIWndInfo* mwi)
154 {
155    char buffer[1024];
156
157    if (mwi->dwStyle & MCIWNDF_SHOWNAME) {
158       strcpy(buffer, mwi->lpName);
159    } else {
160       *buffer = 0;
161    }
162
163    if (mwi->dwStyle & (MCIWNDF_SHOWPOS|MCIWNDF_SHOWMODE)) {
164       if (*buffer) strcat(buffer, " ");
165       strcat(buffer, "(");
166    }
167
168    if (mwi->dwStyle & MCIWNDF_SHOWPOS) {
169       sprintf(buffer + strlen(buffer), "%ld", MCIWND_Get(mwi, MCI_STATUS_POSITION));
170    }
171
172    if ((mwi->dwStyle & (MCIWNDF_SHOWPOS|MCIWNDF_SHOWMODE)) == (MCIWNDF_SHOWPOS|MCIWNDF_SHOWMODE)) {
173       strcat(buffer, " - ");
174    }
175
176    if (mwi->dwStyle & MCIWNDF_SHOWMODE) {
177       switch (MCIWND_GetStatus(mwi)) {
178       case MCI_MODE_NOT_READY:  strcat(buffer, "not ready");    break;
179       case MCI_MODE_PAUSE:      strcat(buffer, "paused");       break;
180       case MCI_MODE_PLAY:       strcat(buffer, "playing");      break;
181       case MCI_MODE_STOP:       strcat(buffer, "stopped");      break;
182       case MCI_MODE_OPEN:       strcat(buffer, "open");         break;
183       case MCI_MODE_RECORD:     strcat(buffer, "recording");    break;
184       case MCI_MODE_SEEK:       strcat(buffer, "seeking");      break;
185       default:                  strcat(buffer, "???");          break;
186       }
187    }
188    if (mwi->dwStyle & (MCIWNDF_SHOWPOS|MCIWNDF_SHOWMODE)) {
189       strcat(buffer, " )");
190    }
191    TRACE("=> '%s'\n", buffer);
192    SetWindowTextA(mwi->hWnd, buffer);
193 }
194
195 static void MCIWND_Create(HWND hWnd, LPCREATESTRUCTA cs)
196 {
197    MCI_DGV_OPEN_PARMSA  mdopn;
198    MCI_DGV_RECT_PARMS   mdrct;
199    MMRESULT             mmr;
200    int                  cx, cy;
201    HWND                 hChld;
202    MCIWndInfo*          mwi = (MCIWndInfo*)cs->lpCreateParams;
203
204    SetWindowLongA(hWnd, 0, (LPARAM)mwi);
205    mwi->hWnd = hWnd;
206
207    /* now open MCI player for AVI file */
208    memset(&mdopn, 0, sizeof(mdopn));
209    mdopn.lpstrElementName = mwi->lpName;
210    mdopn.dwStyle = WS_VISIBLE|WS_CHILD;
211    mdopn.hWndParent = hWnd;
212
213    mmr = mciSendCommandA(0,  MCI_OPEN, MCI_OPEN_ELEMENT|MCI_DGV_OPEN_PARENT|MCI_DGV_OPEN_WS, (LPARAM)&mdopn);
214    if (mmr) {
215       MessageBoxA(GetTopWindow(hWnd), "Cannot open file", "MciWnd", MB_OK);
216       return;
217    }
218    mwi->mci = mdopn.wDeviceID;
219
220    /* grab AVI window size */
221    memset(&mdrct, 0, sizeof(mdrct));
222    mmr = mciSendCommandA(mwi->mci,  MCI_WHERE, MCI_DGV_WHERE_DESTINATION, (LPARAM)&mdrct);
223    if (mmr) {
224       WARN("Cannot get window rect\n");
225       return;
226    }
227    cx = mdrct.rc.right - mdrct.rc.left;
228    cy = mdrct.rc.bottom - mdrct.rc.top;
229
230    AdjustWindowRect(&mdrct.rc, GetWindowLongA(hWnd, GWL_STYLE), FALSE);
231    SetWindowPos(hWnd, 0, 0, 0, mdrct.rc.right - mdrct.rc.left,
232                 mdrct.rc.bottom - mdrct.rc.top + 32, SWP_NOMOVE|SWP_NOZORDER);
233
234    /* adding the other elements: play/stop button, menu button, status */
235    hChld = CreateWindowExA(0, "BUTTON", "Play", WS_CHILD|WS_VISIBLE, 0, cy, 32, 32,
236                            hWnd, (HMENU)CTL_PLAYSTOP,
237                            (HINSTANCE)GetWindowLongA(hWnd, GWL_HINSTANCE), 0L);
238    TRACE("Get Button1: %p\n", hChld);
239    hChld = CreateWindowExA(0, "BUTTON", "Menu", WS_CHILD|WS_VISIBLE, 32, cy, 32, 32,
240                            hWnd, (HMENU)CTL_MENU,
241                            (HINSTANCE)GetWindowLongA(hWnd, GWL_HINSTANCE), 0L);
242    TRACE("Get Button2: %p\n", hChld);
243    hChld = CreateWindowExA(0, TRACKBAR_CLASSA, "", WS_CHILD|WS_VISIBLE, 64, cy, cx - 64, 32,
244                            hWnd, (HMENU)CTL_TRACKBAR,
245                            (HINSTANCE)GetWindowLongA(hWnd, GWL_HINSTANCE), 0L);
246    TRACE("Get status: %p\n", hChld);
247    SendMessageA(hChld, TBM_SETRANGEMIN, 0L, 0L);
248    SendMessageA(hChld, TBM_SETRANGEMAX, 1L, MCIWND_Get(mwi, MCI_STATUS_LENGTH));
249
250    /* FIXME: no need to set it if child window */
251    MCIWND_SetText(mwi);
252 }
253
254 static void MCIWND_Paint(MCIWndInfo* mwi, WPARAM wParam)
255 {
256    HDC          hdc;
257    PAINTSTRUCT  ps;
258
259    hdc = (wParam) ? (HDC)wParam : BeginPaint(mwi->hWnd, &ps);
260    /* something to do ? */
261    if (!wParam) EndPaint(mwi->hWnd, &ps);
262 }
263
264 static void MCIWND_ToggleState(MCIWndInfo* mwi)
265 {
266    MCI_GENERIC_PARMS    mgp;
267    MCI_DGV_PLAY_PARMS   mdply;
268
269    memset(&mgp, 0, sizeof(mgp));
270    memset(&mdply, 0, sizeof(mdply));
271
272    switch (MCIWND_GetStatus(mwi)) {
273    case MCI_MODE_NOT_READY:
274    case MCI_MODE_RECORD:
275    case MCI_MODE_SEEK:
276    case MCI_MODE_OPEN:
277       TRACE("Cannot do much...\n");
278       break;
279    case MCI_MODE_PAUSE:
280       mciSendCommandA(mwi->mci, MCI_RESUME, MCI_WAIT, (LPARAM)&mgp);
281       break;
282    case MCI_MODE_PLAY:
283       mciSendCommandA(mwi->mci, MCI_PAUSE, MCI_WAIT, (LPARAM)&mgp);
284       break;
285    case MCI_MODE_STOP:
286       mdply.dwFrom = 0L;
287       mciSendCommandA(mwi->mci, MCI_PLAY, MCI_FROM, (LPARAM)&mdply);
288       mwi->uTimer = SetTimer(mwi->hWnd, 0, 333, 0L);
289       TRACE("Timer=%u\n", mwi->uTimer);
290       break;
291    }
292 }
293
294 static LRESULT MCIWND_Command(MCIWndInfo* mwi, WPARAM wParam, LPARAM lParam)
295 {
296    switch (LOWORD(wParam)) {
297    case CTL_PLAYSTOP:   MCIWND_ToggleState(mwi);        break;
298    case CTL_MENU:
299    case CTL_TRACKBAR:
300    default:
301       MessageBoxA(0, "ooch", "NIY", MB_OK);
302    }
303    return 0L;
304 }
305
306 static void MCIWND_Timer(MCIWndInfo* mwi, WPARAM wParam, LPARAM lParam)
307 {
308    TRACE("%ld\n", MCIWND_Get(mwi, MCI_STATUS_POSITION));
309    SendDlgItemMessageA(mwi->hWnd, CTL_TRACKBAR, TBM_SETPOS, 1, MCIWND_Get(mwi, MCI_STATUS_POSITION));
310    MCIWND_SetText(mwi);
311 }
312
313 static void MCIWND_Close(MCIWndInfo* mwi)
314 {
315    MCI_GENERIC_PARMS    mgp;
316
317    memset(&mgp, 0, sizeof(mgp));
318
319    mciSendCommandA(mwi->mci, MCI_CLOSE, 0, (LPARAM)&mgp);
320 }
321
322 static LRESULT WINAPI   MCIWndProc(HWND hWnd, UINT wMsg, WPARAM lParam1, LPARAM lParam2)
323 {
324    MCIWndInfo*  mwi = (MCIWndInfo*)GetWindowLongA(hWnd, 0);
325
326    if (mwi || wMsg == WM_CREATE) {
327       switch (wMsg) {
328       case WM_CREATE:
329          MCIWND_Create(hWnd, (CREATESTRUCTA*)lParam2);
330          return 0;
331       case WM_DESTROY:
332          MCIWND_Close(mwi);
333          HeapFree(GetProcessHeap(), 0, mwi->lpName);
334          HeapFree(GetProcessHeap(), 0, mwi);
335          break;
336       case WM_PAINT:
337          MCIWND_Paint(mwi, lParam1);
338          break;
339       case WM_COMMAND:
340          return MCIWND_Command(mwi, lParam1, lParam2);
341       case WM_TIMER:
342          MCIWND_Timer(mwi, lParam1, lParam2);
343          return TRUE;
344       }
345    }
346
347    return DefWindowProcA(hWnd, wMsg, lParam1, lParam2);
348 }