Handle WM_CHARs and pass them to TREEVIEW_ProcessLetterKeys. See also
[wine] / dlls / dplayx / dplayx_messages.c
1 /* DirectPlay & DirectPlayLobby messaging implementation
2  *
3  * Copyright 2000 - Peter Hunnisett
4  *
5  * <presently under construction - contact hunnise@nortelnetworks.com>
6  *
7  */
8
9 #include <string.h>
10 #include "winbase.h"
11 #include "debugtools.h"
12
13 #include "wingdi.h"
14 #include "winuser.h"
15 #include "winerror.h"
16
17 #include "dplayx_messages.h"
18 #include "dplay_global.h"
19 #include "dplayx_global.h"
20
21 DEFAULT_DEBUG_CHANNEL(dplay);
22
23 typedef struct tagMSGTHREADINFO
24 {
25   HANDLE hStart;
26   HANDLE hDeath; 
27   HANDLE hSettingRead;
28   HANDLE hNotifyEvent; 
29 } MSGTHREADINFO, *LPMSGTHREADINFO;
30
31 static DWORD CALLBACK DPL_MSG_ThreadMain( LPVOID lpContext );
32 static LPVOID DP_MSG_ExpectReply( IDirectPlay2AImpl* This, LPDPSP_SENDDATA data, 
33                                   DWORD dwWaitTime, WORD wReplyCommandId,
34                                   LPVOID* lplpReplyMsg, LPDWORD lpdwMsgBodySize );
35
36
37 /* Create the message reception thread to allow the application to receive
38  * asynchronous message reception
39  */
40 DWORD CreateLobbyMessageReceptionThread( HANDLE hNotifyEvent, HANDLE hStart, 
41                                          HANDLE hDeath, HANDLE hConnRead )
42 {
43   DWORD           dwMsgThreadId;
44   LPMSGTHREADINFO lpThreadInfo;
45
46   lpThreadInfo = HeapAlloc( GetProcessHeap(), 0, sizeof( *lpThreadInfo ) );
47   if( lpThreadInfo == NULL )
48   {
49     return 0;
50   }
51
52   /* The notify event may or may not exist. Depends if async comm or not */
53   if( hNotifyEvent &&
54       !DuplicateHandle( GetCurrentProcess(), hNotifyEvent, 
55                         GetCurrentProcess(), &lpThreadInfo->hNotifyEvent, 
56                         0, FALSE, DUPLICATE_SAME_ACCESS ) )
57   {
58     ERR( "Unable to duplicate event handle\n" );
59     goto error;
60   }
61
62   /* These 3 handles don't need to be duplicated because we don't keep a
63    * reference to them where they're created. They're created specifically
64    * for the message thread 
65    */
66   lpThreadInfo->hStart       = hStart;
67   lpThreadInfo->hDeath       = hDeath;
68   lpThreadInfo->hSettingRead = hConnRead;
69
70   if( !CreateThread( NULL,                  /* Security attribs */
71                      0,                     /* Stack */ 
72                      DPL_MSG_ThreadMain,    /* Msg reception function */
73                      lpThreadInfo,          /* Msg reception func parameter */
74                      0,                     /* Flags */
75                      &dwMsgThreadId         /* Updated with thread id */
76                    )
77     )
78   {
79     ERR( "Unable to create msg thread\n" );
80     goto error;
81   }
82
83   /* FIXME: Should I be closing the handle to the thread or does that
84             terminate the thread? */
85  
86   return dwMsgThreadId;
87
88 error:
89
90   HeapFree( GetProcessHeap(), 0, lpThreadInfo );
91
92   return 0;
93 }
94
95 static DWORD CALLBACK DPL_MSG_ThreadMain( LPVOID lpContext )
96 {
97   LPMSGTHREADINFO lpThreadInfo = (LPMSGTHREADINFO)lpContext;
98   DWORD dwWaitResult;
99  
100   TRACE( "Msg thread created. Waiting on app startup\n" );
101
102   /* Wait to ensure that the lobby application is started w/ 1 min timeout */
103   dwWaitResult = WaitForSingleObject( lpThreadInfo->hStart, 10000 /* 10 sec */ );
104   if( dwWaitResult == WAIT_TIMEOUT )
105   {
106     FIXME( "Should signal app/wait creation failure (0x%08lx)\n", dwWaitResult );
107     goto end_of_thread;
108   }
109
110   /* Close this handle as it's not needed anymore */
111   CloseHandle( lpThreadInfo->hStart );
112   lpThreadInfo->hStart = 0;
113
114   /* Wait until the lobby knows what it is */
115   dwWaitResult = WaitForSingleObject( lpThreadInfo->hSettingRead, INFINITE );
116   if( dwWaitResult == WAIT_TIMEOUT )
117   {
118     ERR( "App Read connection setting timeout fail (0x%08lx)\n", dwWaitResult );
119   }
120
121   /* Close this handle as it's not needed anymore */
122   CloseHandle( lpThreadInfo->hSettingRead );
123   lpThreadInfo->hSettingRead = 0;
124
125   TRACE( "App created && intialized starting main message reception loop\n" );
126
127   for ( ;; )
128   {
129     MSG lobbyMsg;
130     GetMessageW( &lobbyMsg, 0, 0, 0 );
131   }
132
133 end_of_thread:
134   TRACE( "Msg thread exiting!\n" );
135   HeapFree( GetProcessHeap(), 0, lpThreadInfo );
136
137   return 0;
138 }
139
140 /* DP messageing stuff */
141 static HANDLE DP_MSG_BuildAndLinkReplyStruct( IDirectPlay2Impl* This,
142                                               LPDP_MSG_REPLY_STRUCT_LIST lpReplyStructList, 
143                                               WORD wReplyCommandId );
144 static LPVOID DP_MSG_CleanReplyStruct( LPDP_MSG_REPLY_STRUCT_LIST lpReplyStructList,
145                                        LPVOID* lplpReplyMsg, LPDWORD lpdwMsgBodySize );
146
147
148 static
149 HANDLE DP_MSG_BuildAndLinkReplyStruct( IDirectPlay2Impl* This,
150                                        LPDP_MSG_REPLY_STRUCT_LIST lpReplyStructList, WORD wReplyCommandId )
151 {
152   lpReplyStructList->replyExpected.hReceipt       = CreateEventA( NULL, FALSE, FALSE, NULL );
153   lpReplyStructList->replyExpected.wExpectedReply = wReplyCommandId;
154   lpReplyStructList->replyExpected.lpReplyMsg     = NULL;
155   lpReplyStructList->replyExpected.dwMsgBodySize  = 0;
156
157   /* Insert into the message queue while locked */
158   EnterCriticalSection( &This->unk->DP_lock );
159     DPQ_INSERT( This->dp2->replysExpected, lpReplyStructList, replysExpected );
160   LeaveCriticalSection( &This->unk->DP_lock );
161
162   return lpReplyStructList->replyExpected.hReceipt;
163 }
164
165 static
166 LPVOID DP_MSG_CleanReplyStruct( LPDP_MSG_REPLY_STRUCT_LIST lpReplyStructList,
167                                 LPVOID* lplpReplyMsg, LPDWORD lpdwMsgBodySize  )
168 {
169   CloseHandle( lpReplyStructList->replyExpected.hReceipt );
170
171   *lplpReplyMsg    = lpReplyStructList->replyExpected.lpReplyMsg;
172   *lpdwMsgBodySize = lpReplyStructList->replyExpected.dwMsgBodySize;
173
174   return lpReplyStructList->replyExpected.lpReplyMsg;
175 }
176
177 HRESULT DP_MSG_SendRequestPlayerId( IDirectPlay2AImpl* This, DWORD dwFlags,
178                                     LPDPID lpdpidAllocatedId )
179 {
180   LPVOID                     lpMsg;
181   LPDPMSG_REQUESTNEWPLAYERID lpMsgBody;
182   DWORD                      dwMsgSize;
183   HRESULT                    hr = DP_OK;
184
185   dwMsgSize = This->dp2->spData.dwSPHeaderSize + sizeof( *lpMsgBody );
186
187   lpMsg = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwMsgSize );
188
189   lpMsgBody = (LPDPMSG_REQUESTNEWPLAYERID)( (BYTE*)lpMsg + 
190                                              This->dp2->spData.dwSPHeaderSize );
191
192   /* Compose dplay message envelope */
193   lpMsgBody->envelope.dwMagic    = DPMSGMAGIC_DPLAYMSG;
194   lpMsgBody->envelope.wCommandId = DPMSGCMD_REQUESTNEWPLAYERID;
195   lpMsgBody->envelope.wVersion   = DPMSGVER_DP6;
196
197   /* Compose the body of the message */
198   lpMsgBody->dwFlags = dwFlags;
199
200   /* Send the message */
201   {
202     DPSP_SENDDATA data;
203
204     data.dwFlags        = DPSEND_GUARANTEED;
205     data.idPlayerTo     = 0; /* Name server */
206     data.idPlayerFrom   = 0; /* Sending from DP */
207     data.lpMessage      = lpMsg;
208     data.dwMessageSize  = dwMsgSize;
209     data.bSystemMessage = TRUE; /* Allow reply to be sent */
210     data.lpISP          = This->dp2->spData.lpISP;
211
212     TRACE( "Asking for player id w/ dwFlags 0x%08lx\n", 
213            lpMsgBody->dwFlags );
214
215
216     DP_MSG_ExpectReply( This, &data, DPMSG_DEFAULT_WAIT_TIME, DPMSGCMD_NEWPLAYERIDREPLY, 
217                         &lpMsg, &dwMsgSize );
218   }
219
220   /* Need to examine the data and extract the new player id */
221   if( !FAILED(hr) )
222   {
223     LPCDPMSG_NEWPLAYERIDREPLY lpcReply; 
224
225     lpcReply = (LPCDPMSG_NEWPLAYERIDREPLY)lpMsg;
226
227     *lpdpidAllocatedId = lpcReply->dpidNewPlayerId;   
228
229     TRACE( "Received reply for id = 0x%08lx\n", lpcReply->dpidNewPlayerId );
230
231     /* FIXME: I think that the rest of the message has something to do
232      *        with remote data for the player that perhaps I need to setup.
233      *        However, with the information that is passed, all that it could
234      *        be used for is a standardized intialization value, which I'm 
235      *        guessing we can do without. Unless the message content is the same
236      *        for several different messages?
237      */
238
239     HeapFree( GetProcessHeap(), 0, lpMsg ); 
240   }
241
242   return hr;
243 }
244
245 HRESULT DP_MSG_ForwardPlayerCreation( IDirectPlay2AImpl* This, DPID dpidServer )
246 {
247   LPVOID                   lpMsg;
248   LPDPMSG_FORWARDADDPLAYER lpMsgBody;
249   DWORD                    dwMsgSize;
250   HRESULT                  hr = DP_OK;
251
252   dwMsgSize = This->dp2->spData.dwSPHeaderSize + sizeof( *lpMsgBody );
253
254   lpMsg = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwMsgSize );
255
256   lpMsgBody = (LPDPMSG_FORWARDADDPLAYER)( (BYTE*)lpMsg +
257                                           This->dp2->spData.dwSPHeaderSize );
258
259   /* Compose dplay message envelope */
260   lpMsgBody->envelope.dwMagic    = DPMSGMAGIC_DPLAYMSG;
261   lpMsgBody->envelope.wCommandId = DPMSGCMD_FORWARDADDPLAYER;
262   lpMsgBody->envelope.wVersion   = DPMSGVER_DP6;
263
264 #if 0
265   {
266     LPBYTE lpPData;
267     DWORD  dwDataSize;
268     
269     /* SP Player remote data needs to be propagated at some point - is this the point? */
270     IDirectPlaySP_GetSPPlayerData( This->dp2->spData.lpISP, dpidServer, (LPVOID*)&lpPData, &dwDataSize, DPSET_REMOTE );
271     
272     ERR( "Player Data size is 0x%08lx\n"
273          "[%02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x]\n"
274          "[%02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x]\n", 
275  
276          dwDataSize,
277          lpPData[0], lpPData[1], lpPData[2], lpPData[3], lpPData[4],
278          lpPData[5], lpPData[6], lpPData[7], lpPData[8], lpPData[9],
279          lpPData[10], lpPData[11], lpPData[12], lpPData[13], lpPData[14],
280          lpPData[15], lpPData[16], lpPData[17], lpPData[18], lpPData[19],
281          lpPData[20], lpPData[21], lpPData[22], lpPData[23], lpPData[24],
282          lpPData[25], lpPData[26], lpPData[27], lpPData[28], lpPData[29],
283          lpPData[30], lpPData[31]                
284         );
285     DebugBreak();
286   }
287 #endif
288
289   /* Compose body of message */
290   lpMsgBody->dpidAppServer = dpidServer;
291   lpMsgBody->unknown2[0] = 0x0;
292   lpMsgBody->unknown2[1] = 0x1c;
293   lpMsgBody->unknown2[2] = 0x6c;
294   lpMsgBody->unknown2[3] = 0x50;
295   lpMsgBody->unknown2[4] = 0x9;
296
297   lpMsgBody->dpidAppServer2 = dpidServer;
298   lpMsgBody->unknown3[0] = 0x0;
299   lpMsgBody->unknown3[0] = 0x0;
300   lpMsgBody->unknown3[0] = 0x20;
301   lpMsgBody->unknown3[0] = 0x0;
302   lpMsgBody->unknown3[0] = 0x0;
303   
304   lpMsgBody->dpidAppServer3 = dpidServer;
305   lpMsgBody->unknown4[0] =  0x30; 
306   lpMsgBody->unknown4[1] =  0xb;
307   lpMsgBody->unknown4[2] =  0x0;
308   lpMsgBody->unknown4[3] =  0x1e090002;
309   lpMsgBody->unknown4[4] =  0x0;
310   lpMsgBody->unknown4[5] =  0x0;
311   lpMsgBody->unknown4[6] =  0x0;
312   lpMsgBody->unknown4[7] =  0x32090002;
313   lpMsgBody->unknown4[8] =  0x0;
314   lpMsgBody->unknown4[9] =  0x0;
315   lpMsgBody->unknown4[10] = 0x0;
316   lpMsgBody->unknown4[11] = 0x0;
317   lpMsgBody->unknown4[12] = 0x0;
318
319   lpMsgBody->unknown5[0] = 0x0;
320   lpMsgBody->unknown5[1] = 0x0;
321
322   /* Send the message */
323   {
324     DPSP_SENDDATA data;
325
326     data.dwFlags        = DPSEND_GUARANTEED;
327     data.idPlayerTo     = 0; /* Name server */
328     data.idPlayerFrom   = dpidServer; /* Sending from session server */
329     data.lpMessage      = lpMsg;
330     data.dwMessageSize  = dwMsgSize;
331     data.bSystemMessage = TRUE; /* Allow reply to be sent */
332     data.lpISP          = This->dp2->spData.lpISP;
333
334     lpMsg = DP_MSG_ExpectReply( This, &data, 
335                                 DPMSG_WAIT_60_SECS, 
336                                 DPMSGCMD_GETNAMETABLEREPLY,
337                                 &lpMsg, &dwMsgSize );
338   }
339
340   /* Need to examine the data and extract the new player id */
341   if( lpMsg != NULL )
342   {
343     FIXME( "Name Table reply received: stub\n" );
344   }
345
346   return hr;
347 }
348
349 /* Queue up a structure indicating that we want a reply of type wReplyCommandId. DPlay does
350  * not seem to offer any way of uniquely differentiating between replies of the same type
351  * relative to the request sent. There is an implicit assumption that there will be no
352  * ordering issues on sends and receives from the opposite machine. No wonder MS is not
353  * a networking company.
354  */
355 static
356 LPVOID DP_MSG_ExpectReply( IDirectPlay2AImpl* This, LPDPSP_SENDDATA lpData, 
357                            DWORD dwWaitTime, WORD wReplyCommandId,
358                            LPVOID* lplpReplyMsg, LPDWORD lpdwMsgBodySize )
359 {
360   HRESULT                  hr;
361   HANDLE                   hMsgReceipt;
362   DP_MSG_REPLY_STRUCT_LIST replyStructList;
363   DWORD                    dwWaitReturn;
364  
365   /* Setup for receipt */
366   hMsgReceipt = DP_MSG_BuildAndLinkReplyStruct( This, &replyStructList, 
367                                                 wReplyCommandId );
368
369   TRACE( "Sending msg and expecting cmd %u in reply within %lu ticks\n", 
370          wReplyCommandId, dwWaitTime );
371   hr = (*This->dp2->spData.lpCB->Send)( lpData );
372
373   if( FAILED(hr) )
374   {
375     ERR( "Request for new playerID send failed: %s\n",
376          DPLAYX_HresultToString( hr ) );
377     return NULL;
378   }
379
380   dwWaitReturn = WaitForSingleObject( hMsgReceipt, dwWaitTime );
381   if( dwWaitReturn != WAIT_OBJECT_0 )
382   {
383     ERR( "Wait failed 0x%08lx\n", dwWaitReturn );
384     return NULL;
385   }
386
387   /* Clean Up */
388   return DP_MSG_CleanReplyStruct( &replyStructList, lplpReplyMsg, lpdwMsgBodySize );
389 }
390
391 /* Determine if there is a matching request for this incomming message and then copy
392  * all important data. It is quite silly to have to copy the message, but the documents
393  * indicate that a copy is taken. Silly really.
394  */
395 void DP_MSG_ReplyReceived( IDirectPlay2AImpl* This, WORD wCommandId, 
396                            LPCVOID lpcMsgBody, DWORD dwMsgBodySize )
397 {
398   LPDP_MSG_REPLY_STRUCT_LIST lpReplyList;
399
400 #if 0
401   if( wCommandId == DPMSGCMD_FORWARDADDPLAYER )
402   {
403     DebugBreak();
404   }
405 #endif
406
407   /* Find, and immediately remove (to avoid double triggering), the appropriate entry. Call locked to 
408    * avoid problems.
409    */
410   EnterCriticalSection( &This->unk->DP_lock );
411     DPQ_REMOVE_ENTRY( This->dp2->replysExpected, replysExpected, replyExpected.wExpectedReply,\
412                      ==, wCommandId, lpReplyList );
413   LeaveCriticalSection( &This->unk->DP_lock );  
414
415   if( lpReplyList != NULL )
416   {
417     lpReplyList->replyExpected.dwMsgBodySize = dwMsgBodySize; 
418     lpReplyList->replyExpected.lpReplyMsg = HeapAlloc( GetProcessHeap(),
419                                                        HEAP_ZERO_MEMORY,
420                                                        dwMsgBodySize );
421     CopyMemory( lpReplyList->replyExpected.lpReplyMsg, 
422                 lpcMsgBody, dwMsgBodySize );
423
424     /* Signal the thread which sent the message that it has a reply */
425     SetEvent( lpReplyList->replyExpected.hReceipt );
426   }
427   else
428   {
429     ERR( "No receipt event set - only expecting in reply mode\n" );
430     DebugBreak();
431   }
432  
433 }
434
435 void DP_MSG_ErrorReceived( IDirectPlay2AImpl* This, WORD wCommandId,
436                            LPCVOID lpMsgBody, DWORD dwMsgBodySize )
437 {
438   LPCDPMSG_FORWARDADDPLAYERNACK lpcErrorMsg;
439
440   lpcErrorMsg = (LPCDPMSG_FORWARDADDPLAYERNACK)lpMsgBody;
441
442   ERR( "Received error message %u. Error is %s\n", 
443        wCommandId, DPLAYX_HresultToString( lpcErrorMsg->errorCode) );
444   DebugBreak();
445 }
446