winecfg: Use sound tree view for driver selection.
[wine] / programs / winecfg / audio.c
1 /*
2  * Audio management UI code
3  *
4  * Copyright 2004 Chris Morgan
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #define NONAMELESSSTRUCT
23 #define NONAMELESSUNION
24
25 #include "config.h"
26 #include "wine/port.h"
27
28 #include <assert.h>
29 #include <stdarg.h>
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <string.h>
33
34 #include <windef.h>
35 #include <winbase.h>
36 #include <winreg.h>
37 #include <wine/debug.h>
38 #include <shellapi.h>
39 #include <objbase.h>
40 #include <shlguid.h>
41 #include <shlwapi.h>
42 #include <shlobj.h>
43 #include <windowsx.h>
44 #include <mmsystem.h>
45 #include <mmreg.h>
46 #include <mmsystem.h>
47 #include <mmddk.h>
48
49 #include "winecfg.h"
50 #include "resource.h"
51
52 WINE_DEFAULT_DEBUG_CHANNEL(winecfg);
53
54 #define DRIVER_MASK 0x80000000
55 #define DEVICE_MASK 0x40000000
56
57 typedef DWORD (WINAPI * MessagePtr)(UINT, UINT, DWORD, DWORD, DWORD);
58
59 static const char* DSound_HW_Accels[] = {
60   "Full",
61   "Standard",
62   "Basic",
63   "Emulation",
64   NULL
65 };
66
67 /* list of available drivers */
68 static AUDIO_DRIVER * loadedAudioDrv;
69
70 /* local copy of registry setting */
71 static char curAudioDriver[1024];
72
73 /* driver index to configure */
74 static int toConfigure;
75
76 /* display a driver specific configuration dialog */
77 static void configureAudioDriver(HWND hDlg)
78 {
79     const AUDIO_DRIVER *pAudioDrv = &loadedAudioDrv[toConfigure];
80
81     if (strlen(pAudioDrv->szDriver) != 0)
82     {
83         HDRVR hdrvr;
84         char wine_driver[MAX_NAME_LENGTH + 8];
85         sprintf(wine_driver, "wine%s.drv", pAudioDrv->szDriver);
86         hdrvr = OpenDriverA(wine_driver, 0, 0);
87         if (hdrvr != 0)
88         {
89             if (SendDriverMessage(hdrvr, DRV_QUERYCONFIGURE, 0, 0) != 0)
90             {
91                 DRVCONFIGINFO dci;
92                 LONG lRes;
93                 dci.dwDCISize = sizeof (dci);
94                 dci.lpszDCISectionName = NULL;
95                 dci.lpszDCIAliasName = NULL;
96                 lRes = SendDriverMessage(hdrvr, DRV_CONFIGURE, 0, (LONG)&dci);
97             }
98             CloseDriver(hdrvr, 0, 0);
99         }
100         else
101         {
102             char str[1024];
103             sprintf(str, "Couldn't open %s!", wine_driver);
104             MessageBox(hDlg, str, "Fixme", MB_OK | MB_ICONERROR);
105         }
106     }
107 }
108
109 /* is driver in local copy of driver registry string */
110 static BOOL isDriverSet(const char * driver)
111 {
112     WINE_TRACE("driver = %s, curAudioDriver = %s\n", driver, curAudioDriver);
113
114     if (strstr(curAudioDriver, driver))
115         return TRUE;
116
117     return FALSE;
118 }
119
120 /* add driver to local copy of driver registry string */
121 static void addDriver(const char * driver)
122 {
123     if (!isDriverSet(driver))
124     {
125         if (strlen(curAudioDriver))
126             strcat(curAudioDriver, ",");
127         strcat(curAudioDriver, driver);
128     }
129 }
130
131 /* remove driver from local copy of driver registry string */
132 static void removeDriver(const char * driver)
133 {
134     char before[32], after[32], * start;
135
136     strcpy(before, ",");
137     strcat(before, driver);
138     strcpy(after, driver);
139     strcat(after, ",");
140
141     if ((start = strstr(curAudioDriver, after)))
142     {
143         int len = strlen(after);
144         char * end = curAudioDriver + strlen(curAudioDriver);
145         int i, count = end - start + len;
146         for (i = 0; i < count; i++)
147         {
148             if (start + len >= end)
149                 *start = 0;
150             else
151                 *start = start[len];
152             start++;
153         }
154     }
155     else if ((start = strstr(curAudioDriver, before)))
156     {
157         int len = strlen(before);
158         char * end = curAudioDriver + strlen(curAudioDriver);
159         int i, count = end - start + len;
160         for (i = 0; i < count; i++)
161         {
162             if (start + len >= end)
163                 *start = 0;
164             else
165                 *start = start[len];
166             start++;
167         }
168     }
169     else if (strcmp(curAudioDriver, driver) == 0)
170     {
171         strcpy(curAudioDriver, "");
172     }
173 }
174
175 static void initAudioDeviceTree(HWND hDlg)
176 {
177     const AUDIO_DRIVER *pAudioDrv = NULL;
178     int i, j;
179     TVINSERTSTRUCT insert;
180     HTREEITEM root, driver[10];
181     HWND tree = NULL;
182     HIMAGELIST hImageList;
183     HBITMAP hBitMap;
184
185     tree = GetDlgItem(hDlg, IDC_AUDIO_TREE);
186
187     if (!tree)
188         return;
189
190     /* set tree view style */
191     SetWindowLong(tree, GWL_STYLE, GetWindowLong(tree, GWL_STYLE) | TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT);
192
193     /* state checkbox */
194     hImageList = ImageList_Create(16, 16, FALSE, 3, 0);
195     hBitMap = LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(IDB_CHECKBOX));
196     ImageList_Add(hImageList, hBitMap, NULL);
197     DeleteObject(hBitMap);
198     TreeView_SetImageList(tree, hImageList, TVSIL_STATE);
199
200     /* root item */
201     insert.hParent = TVI_ROOT;
202     insert.hInsertAfter = TVI_LAST;
203     insert.u.item.mask = TVIF_TEXT | TVIF_CHILDREN;
204     insert.u.item.pszText = "Sound Drivers";
205     insert.u.item.cChildren = 1;
206     root = (HTREEITEM)SendDlgItemMessage(hDlg, IDC_AUDIO_TREE, TVM_INSERTITEM, 0, (LPARAM)&insert);
207
208     /* iterate over list of loaded drivers */
209     for (pAudioDrv = loadedAudioDrv, i = 0; *pAudioDrv->szName; i++, pAudioDrv++) {
210         HDRVR hdrv;
211         char name[MAX_PATH];
212         char text[MAX_PATH];
213
214         sprintf(name, "wine%s.drv", pAudioDrv->szDriver);
215         sprintf(text, "%s Driver", pAudioDrv->szName);
216
217         if ((hdrv = OpenDriverA(name, 0, 0)))
218         {
219             HMODULE lib;
220             if ((lib = GetDriverModuleHandle(hdrv)))
221             {
222                 int num_wod = 0, num_wid = 0, num_mod = 0, num_mid = 0, num_aux = 0, num_mxd = 0;
223                 MessagePtr wodMessagePtr = (MessagePtr)GetProcAddress(lib, "wodMessage");
224                 MessagePtr widMessagePtr = (MessagePtr)GetProcAddress(lib, "widMessage");
225                 MessagePtr modMessagePtr = (MessagePtr)GetProcAddress(lib, "modMessage");
226                 MessagePtr midMessagePtr = (MessagePtr)GetProcAddress(lib, "midMessage");
227                 MessagePtr auxMessagePtr = (MessagePtr)GetProcAddress(lib, "auxMessage");
228                 MessagePtr mxdMessagePtr = (MessagePtr)GetProcAddress(lib, "mxdMessage");
229
230                 if (wodMessagePtr)
231                     num_wod = wodMessagePtr(0, WODM_GETNUMDEVS, 0, 0, 0);
232
233                 if (widMessagePtr)
234                     num_wid = widMessagePtr(0, WIDM_GETNUMDEVS, 0, 0, 0);
235
236                 if (modMessagePtr)
237                     num_mod = modMessagePtr(0, MODM_GETNUMDEVS, 0, 0, 0);
238
239                 if (midMessagePtr)
240                     num_mid = midMessagePtr(0, MIDM_GETNUMDEVS, 0, 0, 0);
241
242                 if (auxMessagePtr)
243                     num_aux = auxMessagePtr(0, AUXDM_GETNUMDEVS, 0, 0, 0);
244
245                 if (mxdMessagePtr)
246                     num_mxd = mxdMessagePtr(0, MXDM_GETNUMDEVS, 0, 0, 0);
247
248                 if (num_wod == 0 && num_wid == 0 && num_mod == 0 && num_mid == 0 && num_aux == 0 && num_mxd == 0)
249                 {
250                     insert.hParent = root;
251                     insert.u.item.mask = TVIF_TEXT | TVIF_STATE | TVIF_PARAM;
252                     insert.u.item.pszText = text;
253                     insert.u.item.stateMask = TVIS_STATEIMAGEMASK;
254                     insert.u.item.lParam =  i + DRIVER_MASK;
255
256                     driver[i] = (HTREEITEM)SendDlgItemMessage(hDlg, IDC_AUDIO_TREE, TVM_INSERTITEM, 0, (LPARAM)&insert);
257                 }
258                 else
259                 {
260                     HTREEITEM type;
261
262                     insert.hParent = root;
263                     insert.u.item.mask = TVIF_TEXT | TVIF_CHILDREN | TVIF_STATE | TVIF_PARAM;
264                     insert.u.item.pszText = text;
265                     insert.u.item.cChildren = 1;
266                     insert.u.item.stateMask = TVIS_STATEIMAGEMASK;
267                     insert.u.item.lParam =  i + DRIVER_MASK;
268
269                     if (isDriverSet(pAudioDrv->szDriver))
270                         insert.u.item.state = INDEXTOSTATEIMAGEMASK(2);
271                     else
272                         insert.u.item.state = INDEXTOSTATEIMAGEMASK(1);
273
274                     driver[i] = (HTREEITEM)SendDlgItemMessage(hDlg, IDC_AUDIO_TREE, TVM_INSERTITEM, 0, (LPARAM)&insert);
275
276                     if (num_wod)
277                     {
278                         insert.hParent = driver[i];
279                         insert.u.item.mask = TVIF_TEXT | TVIF_CHILDREN;
280                         insert.u.item.pszText = "Wave Out Devices";
281                         insert.u.item.cChildren = 1;
282
283                         type = (HTREEITEM)SendDlgItemMessage(hDlg, IDC_AUDIO_TREE, TVM_INSERTITEM, 0, (LPARAM)&insert);
284
285                         for (j = 0; j < num_wod; j++)
286                         {
287                             WAVEOUTCAPSW caps;
288                             char szPname[MAXPNAMELEN];
289
290                             wodMessagePtr(j, WODM_GETDEVCAPS, 0, (DWORD)&caps, sizeof(caps));
291                             WideCharToMultiByte(CP_ACP, 0, caps.szPname, -1, szPname, MAXPNAMELEN, 0, 0);
292
293                             insert.hParent = type;
294                             insert.u.item.mask = TVIF_TEXT | TVIF_PARAM;
295                             insert.u.item.pszText = szPname;
296                             insert.u.item.lParam = j + DEVICE_MASK;
297
298                             SendDlgItemMessage(hDlg, IDC_AUDIO_TREE, TVM_INSERTITEM, 0, (LPARAM)&insert);
299                         }
300                     }
301
302                     if (num_wid)
303                     {
304                         insert.hParent = driver[i];
305                         insert.u.item.mask = TVIF_TEXT | TVIF_CHILDREN;
306                         insert.u.item.pszText = "Wave In Devices";
307                         insert.u.item.cChildren = 1;
308
309                         type = (HTREEITEM)SendDlgItemMessage(hDlg, IDC_AUDIO_TREE, TVM_INSERTITEM, 0, (LPARAM)&insert);
310
311                         for (j = 0; j < num_wid; j++)
312                         {
313                             WAVEINCAPSW caps;
314                             char szPname[MAXPNAMELEN];
315
316                             widMessagePtr(j, WIDM_GETDEVCAPS, 0, (DWORD)&caps, sizeof(caps));
317                             WideCharToMultiByte(CP_ACP, 0, caps.szPname, -1, szPname, MAXPNAMELEN, 0, 0);
318
319                             insert.hParent = type;
320                             insert.u.item.mask = TVIF_TEXT | TVIF_PARAM;
321                             insert.u.item.pszText = szPname;
322                             insert.u.item.lParam = j + DEVICE_MASK;
323
324                             SendDlgItemMessage(hDlg, IDC_AUDIO_TREE, TVM_INSERTITEM, 0, (LPARAM)&insert);
325                         }
326                     }
327
328                     if (num_mod)
329                     {
330                         insert.hParent = driver[i];
331                         insert.u.item.mask = TVIF_TEXT | TVIF_CHILDREN;
332                         insert.u.item.pszText = "MIDI Out Devices";
333                         insert.u.item.cChildren = 1;
334
335                         type = (HTREEITEM)SendDlgItemMessage(hDlg, IDC_AUDIO_TREE, TVM_INSERTITEM, 0, (LPARAM)&insert);
336
337                         for (j = 0; j < num_mod; j++)
338                         {
339                             MIDIOUTCAPSW caps;
340                             char szPname[MAXPNAMELEN];
341
342                             modMessagePtr(j, MODM_GETDEVCAPS, 0, (DWORD)&caps, sizeof(caps));
343                             WideCharToMultiByte(CP_ACP, 0, caps.szPname, -1, szPname, MAXPNAMELEN, 0, 0);
344
345                             insert.hParent = type;
346                             insert.u.item.mask = TVIF_TEXT | TVIF_PARAM;
347                             insert.u.item.pszText = szPname;
348                             insert.u.item.lParam = j + DEVICE_MASK;
349
350                             SendDlgItemMessage(hDlg, IDC_AUDIO_TREE, TVM_INSERTITEM, 0, (LPARAM)&insert);
351                         }
352                     }
353
354                     if (num_mid)
355                     {
356                         insert.hParent = driver[i];
357                         insert.u.item.mask = TVIF_TEXT | TVIF_CHILDREN;
358                         insert.u.item.pszText = "MIDI In Devices";
359                         insert.u.item.cChildren = 1;
360
361                         type = (HTREEITEM)SendDlgItemMessage(hDlg, IDC_AUDIO_TREE, TVM_INSERTITEM, 0, (LPARAM)&insert);
362
363                         for (j = 0; j < num_mid; j++)
364                         {
365                             MIDIINCAPSW caps;
366                             char szPname[MAXPNAMELEN];
367
368                             midMessagePtr(j, MIDM_GETDEVCAPS, 0, (DWORD)&caps, sizeof(caps));
369                             WideCharToMultiByte(CP_ACP, 0, caps.szPname, -1, szPname, MAXPNAMELEN, 0, 0);
370
371                             insert.hParent = type;
372                             insert.u.item.mask = TVIF_TEXT | TVIF_PARAM;
373                             insert.u.item.pszText = szPname;
374                             insert.u.item.lParam = j + DEVICE_MASK;
375
376                             SendDlgItemMessage(hDlg, IDC_AUDIO_TREE, TVM_INSERTITEM, 0, (LPARAM)&insert);
377                         }
378                     }
379
380                     if (num_aux)
381                     {
382                         insert.hParent = driver[i];
383                         insert.u.item.mask = TVIF_TEXT | TVIF_CHILDREN;
384                         insert.u.item.pszText = "Aux Devices";
385                         insert.u.item.cChildren = 1;
386
387                         type = (HTREEITEM)SendDlgItemMessage(hDlg, IDC_AUDIO_TREE, TVM_INSERTITEM, 0, (LPARAM)&insert);
388
389                         for (j = 0; j < num_aux; j++)
390                         {
391                             AUXCAPSW caps;
392                             char szPname[MAXPNAMELEN];
393
394                             auxMessagePtr(j, AUXDM_GETDEVCAPS, 0, (DWORD)&caps, sizeof(caps));
395                             WideCharToMultiByte(CP_ACP, 0, caps.szPname, -1, szPname, MAXPNAMELEN, 0, 0);
396
397                             insert.hParent = type;
398                             insert.u.item.mask = TVIF_TEXT | TVIF_PARAM;
399                             insert.u.item.pszText = szPname;
400                             insert.u.item.lParam = j + DEVICE_MASK;
401
402                             SendDlgItemMessage(hDlg, IDC_AUDIO_TREE, TVM_INSERTITEM, 0, (LPARAM)&insert);
403                         }
404                     }
405
406                     if (num_mxd)
407                     {
408                         insert.hParent = driver[i];
409                         insert.u.item.mask = TVIF_TEXT | TVIF_CHILDREN;
410                         insert.u.item.pszText = "Mixer Devices";
411                         insert.u.item.cChildren = 1;
412
413                         type = (HTREEITEM)SendDlgItemMessage(hDlg, IDC_AUDIO_TREE, TVM_INSERTITEM, 0, (LPARAM)&insert);
414
415                         for (j = 0; j < num_mxd; j++)
416                         {
417                             MIXERCAPSW caps;
418                             char szPname[MAXPNAMELEN];
419
420                             mxdMessagePtr(j, MXDM_GETDEVCAPS, 0, (DWORD)&caps, sizeof(caps));
421                             WideCharToMultiByte(CP_ACP, 0, caps.szPname, -1, szPname, MAXPNAMELEN, 0, 0);
422
423                             insert.hParent = type;
424                             insert.u.item.mask = TVIF_TEXT | TVIF_PARAM;
425                             insert.u.item.pszText = szPname;
426                             insert.u.item.lParam = j + DEVICE_MASK;
427
428                             SendDlgItemMessage(hDlg, IDC_AUDIO_TREE, TVM_INSERTITEM, 0, (LPARAM)&insert);
429                         }
430                     }
431                 }
432             }
433         }
434     }
435
436     SendDlgItemMessage(hDlg, IDC_AUDIO_TREE, TVM_SELECTITEM, 0, 0);
437     SendDlgItemMessage(hDlg, IDC_AUDIO_TREE, TVM_EXPAND, TVE_EXPAND, (LPARAM)root);
438     for (j = 0; j < i; j++)
439         SendDlgItemMessage(hDlg, IDC_AUDIO_TREE, TVM_EXPAND, TVE_EXPAND, (LPARAM)driver[j]);
440 }
441
442 /* find all drivers that can be loaded */
443 static void findAudioDrivers(void)
444 {
445     int numFound = 0;
446     const AUDIO_DRIVER *pAudioDrv = NULL;
447
448     /* delete an existing list */
449     if (loadedAudioDrv)
450     {
451         HeapFree(GetProcessHeap(), 0, loadedAudioDrv);
452         loadedAudioDrv = 0;
453     }
454
455     for (pAudioDrv = getAudioDrivers(); *pAudioDrv->szName; pAudioDrv++)
456     {
457         if (strlen(pAudioDrv->szDriver))
458         {
459             HDRVR hdrv;
460             char driver[MAX_PATH];
461
462             sprintf(driver, "wine%s.drv", pAudioDrv->szDriver);
463
464             if ((hdrv = OpenDriverA(driver, 0, 0)))
465             {
466                 CloseDriver(hdrv, 0, 0);
467
468                 if (loadedAudioDrv)
469                     loadedAudioDrv = HeapReAlloc(GetProcessHeap(), 0, loadedAudioDrv, (numFound + 1) * sizeof(AUDIO_DRIVER));
470                 else
471                     loadedAudioDrv = HeapAlloc(GetProcessHeap(), 0, sizeof(AUDIO_DRIVER));
472
473                 CopyMemory(&loadedAudioDrv[numFound], pAudioDrv, sizeof(AUDIO_DRIVER));
474                 numFound++;
475             }
476         }
477     }
478
479     /* terminate list with empty driver */
480     loadedAudioDrv = HeapReAlloc(GetProcessHeap(), 0, loadedAudioDrv, (numFound + 1) * sizeof(AUDIO_DRIVER));
481     CopyMemory(&loadedAudioDrv[numFound], pAudioDrv, sizeof(AUDIO_DRIVER));
482 }
483
484 /* check local copy of registry string for unloadable drivers */
485 static void checkRegistrySetting(HWND hDlg)
486 {
487     const AUDIO_DRIVER *pAudioDrv;
488     char * token, * tokens = strdup(curAudioDriver);
489
490 start_over:
491     token = strtok(tokens, ",");
492     while (token != NULL)
493     {
494         BOOL found = FALSE;
495         for (pAudioDrv = loadedAudioDrv; *pAudioDrv->szName; pAudioDrv++)
496         {
497             if (strcmp(token, pAudioDrv->szDriver) == 0)
498             {
499                 found = TRUE;
500                 break;
501             }
502         }
503         if (found == FALSE)
504         {
505             char str[1024];
506             sprintf(str, "Found driver in registry that in not available!\n\nRemove \"%s\" from registry?", token);
507             if (MessageBox(hDlg, str, "WARNING", MB_ICONWARNING | MB_YESNOCANCEL) == IDYES)
508             {
509                 removeDriver(token);
510                 strcpy(tokens, curAudioDriver);
511                 goto start_over;
512             }
513         }
514         token = strtok(NULL, ",");
515     }
516     free(tokens);
517 }
518
519 static void initAudioDlg (HWND hDlg)
520 {
521     int i;
522     char* buf = NULL;
523
524     WINE_TRACE("\n");
525
526     /* make a local copy of the current registry setting */
527     strcpy(curAudioDriver, get_reg_key(config_key, "Drivers", "Audio", ""));
528
529     WINE_TRACE("curAudioDriver = %s\n", curAudioDriver);
530
531     /* make a list of all drivers that can be loaded */
532     findAudioDrivers();
533
534     /* check for drivers that can't be loaded */
535     checkRegistrySetting(hDlg);
536
537     initAudioDeviceTree(hDlg);
538
539     SendDlgItemMessage(hDlg, IDC_DSOUND_HW_ACCEL, CB_RESETCONTENT, 0, 0);
540     for (i = 0; NULL != DSound_HW_Accels[i]; ++i) {
541       SendDlgItemMessage(hDlg, IDC_DSOUND_HW_ACCEL, CB_ADDSTRING, 0, (LPARAM) DSound_HW_Accels[i]);
542     }
543     buf = get_reg_key(config_key, keypath("DirectSound"), "HardwareAcceleration", "Full");
544     for (i = 0; NULL != DSound_HW_Accels[i]; ++i) {
545       if (strcmp(buf, DSound_HW_Accels[i]) == 0) {
546         SendDlgItemMessage(hDlg, IDC_DSOUND_HW_ACCEL, CB_SETCURSEL, i, 0);
547         break ;
548       }
549     }
550     if (NULL == DSound_HW_Accels[i]) {
551       WINE_ERR("Invalid Direct Sound HW Accel read from registry (%s)\n", buf);
552     }
553     HeapFree(GetProcessHeap(), 0, buf);
554
555     buf = get_reg_key(config_key, keypath("DirectSound"), "EmulDriver", "N");
556     if (IS_OPTION_TRUE(*buf))
557       CheckDlgButton(hDlg, IDC_DSOUND_DRV_EMUL, BST_CHECKED);
558     else
559       CheckDlgButton(hDlg, IDC_DSOUND_DRV_EMUL, BST_UNCHECKED);
560     HeapFree(GetProcessHeap(), 0, buf);
561 }
562
563 INT_PTR CALLBACK
564 AudioDlgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
565 {
566   switch (uMsg) {
567       case WM_COMMAND:
568         switch (LOWORD(wParam)) {
569           case IDC_AUDIO_CONFIGURE:
570              configureAudioDriver(hDlg);
571              break;
572           case IDC_AUDIO_CONTROL_PANEL:
573              MessageBox(NULL, "Launching audio control panel not implemented yet!", "Fixme", MB_OK | MB_ICONERROR);
574              break;
575           case IDC_DSOUND_HW_ACCEL:
576             if (HIWORD(wParam) == CBN_SELCHANGE) {
577               int selected_dsound_accel;
578               SendMessage(GetParent(hDlg), PSM_CHANGED, 0, 0);
579               selected_dsound_accel = SendDlgItemMessage(hDlg, IDC_DSOUND_HW_ACCEL, CB_GETCURSEL, 0, 0);
580               set_reg_key(config_key, keypath("DirectSound"), "HardwareAcceleration", DSound_HW_Accels[selected_dsound_accel]);
581             }
582             break;
583           case IDC_DSOUND_DRV_EMUL:
584             if (HIWORD(wParam) == BN_CLICKED) {
585               SendMessage(GetParent(hDlg), PSM_CHANGED, 0, 0);
586               if (IsDlgButtonChecked(hDlg, IDC_DSOUND_DRV_EMUL) == BST_CHECKED)
587                 set_reg_key(config_key, keypath("DirectSound"), "EmulDriver", "Y");
588               else
589                 set_reg_key(config_key, keypath("DirectSound"), "EmulDriver", "N");
590             }
591             break;
592         }
593         break;
594
595       case WM_SHOWWINDOW:
596         set_window_title(hDlg);
597         break;
598
599       case WM_NOTIFY:
600         switch(((LPNMHDR)lParam)->code) {
601             case PSN_KILLACTIVE:
602               SetWindowLongPtr(hDlg, DWLP_MSGRESULT, FALSE);
603               break;
604             case PSN_APPLY:
605               set_reg_key(config_key, "Drivers", "Audio", curAudioDriver);
606               apply();
607               SetWindowLongPtr(hDlg, DWLP_MSGRESULT, PSNRET_NOERROR);
608               break;
609             case PSN_SETACTIVE:
610               break;
611             case NM_CLICK:
612               if (((LPNMHDR)lParam)->idFrom == IDC_AUDIO_TREE)
613               {
614                   TVHITTESTINFO ht;
615                   DWORD dwPos = GetMessagePos();
616                   HWND tree = ((LPNMHDR)lParam)->hwndFrom;
617                   ZeroMemory(&ht, sizeof(ht));
618                   ht.pt.x = GET_X_LPARAM(dwPos);
619                   ht.pt.y = GET_Y_LPARAM(dwPos);
620                   MapWindowPoints(HWND_DESKTOP, tree, &ht.pt, 1);
621                   TreeView_HitTest(tree, &ht);
622                   if (TVHT_ONITEMSTATEICON & ht.flags)
623                   {
624                       TVITEM tvItem;
625                       int index;
626                       ZeroMemory(&tvItem, sizeof(tvItem));
627                       tvItem.hItem = ht.hItem;
628                       TreeView_GetItem(tree, &tvItem);
629
630                       index = TreeView_GetItemState(tree, ht.hItem, TVIS_STATEIMAGEMASK);
631                       if (index == INDEXTOSTATEIMAGEMASK(1))
632                       {
633                           TreeView_SetItemState(tree, ht.hItem, INDEXTOSTATEIMAGEMASK(2), TVIS_STATEIMAGEMASK);
634                           addDriver(loadedAudioDrv[tvItem.lParam & 0xff].szDriver);
635                           SendMessage(GetParent(hDlg), PSM_CHANGED, (WPARAM) hDlg, 0); /* enable apply button */
636                       }
637                       else if (index == INDEXTOSTATEIMAGEMASK(2))
638                       {
639                           TreeView_SetItemState(tree, ht.hItem, INDEXTOSTATEIMAGEMASK(1), TVIS_STATEIMAGEMASK);
640                           removeDriver(loadedAudioDrv[tvItem.lParam & 0xff].szDriver);
641                           SendMessage(GetParent(hDlg), PSM_CHANGED, (WPARAM) hDlg, 0); /* enable apply button */
642                       }
643                   }
644               }
645               break;
646             case NM_RCLICK:
647               if (((LPNMHDR)lParam)->idFrom == IDC_AUDIO_TREE)
648               {
649                   TVHITTESTINFO ht;
650                   DWORD dwPos = GetMessagePos();
651                   HWND tree = ((LPNMHDR)lParam)->hwndFrom;
652                   POINT pt;
653                   ZeroMemory(&ht, sizeof(ht));
654                   pt.x = GET_X_LPARAM(dwPos);
655                   pt.y = GET_Y_LPARAM(dwPos);
656                   ht.pt = pt;
657                   MapWindowPoints(HWND_DESKTOP, tree, &ht.pt, 1);
658                   TreeView_HitTest(tree, &ht);
659                   if (TVHT_ONITEMLABEL & ht.flags)
660                   {
661                       TVITEM tvItem;
662                       ZeroMemory(&tvItem, sizeof(tvItem));
663                       tvItem.hItem = ht.hItem;
664                       tvItem.mask = TVIF_PARAM;
665                       tvItem.lParam = -1;
666                       if (TreeView_GetItem(tree, &tvItem))
667                       {
668                           if (tvItem.lParam & DRIVER_MASK)
669                           {
670                               if (hPopupMenus)
671                               {
672                                   TrackPopupMenu(GetSubMenu(hPopupMenus, 0), TPM_RIGHTBUTTON, pt.x, pt.y, 0, tree, NULL);
673                                   toConfigure = tvItem.lParam & ~DRIVER_MASK;
674                               }
675                           }
676                           else if (tvItem.lParam & DEVICE_MASK)
677                           {
678                               /* FIXME TBD */
679                           }
680
681                       }
682                   }
683               }
684         }
685         break;
686
687   case WM_INITDIALOG:
688     initAudioDlg(hDlg);
689     break;
690   }
691
692   return FALSE;
693 }