winecfg: Move controls on "Desktop Integration" tab to make more room for the next...
[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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  */
21
22 #define WIN32_LEAN_AND_MEAN
23 #define NONAMELESSSTRUCT
24 #define NONAMELESSUNION
25
26 #include "config.h"
27 #include "wine/port.h"
28
29 #include <assert.h>
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <string.h>
33
34 #include <windows.h>
35 #include <wine/debug.h>
36 #include <shellapi.h>
37 #include <objbase.h>
38 #include <shlguid.h>
39 #include <shlwapi.h>
40 #include <shlobj.h>
41 #include <mmsystem.h>
42 #include <mmreg.h>
43 #include <mmddk.h>
44
45 #include "winecfg.h"
46 #include "resource.h"
47
48 WINE_DEFAULT_DEBUG_CHANNEL(winecfg);
49
50 #define DRIVER_MASK 0x80000000
51 #define DEVICE_MASK 0x40000000
52
53 typedef DWORD (WINAPI * MessagePtr)(UINT, UINT, DWORD, DWORD, DWORD);
54
55 static struct DSOUNDACCEL
56 {
57   UINT displayID;
58   const char* settingStr;
59 } const DSound_HW_Accels[] = {
60   {IDS_ACCEL_FULL,      "Full"},
61   {IDS_ACCEL_STANDARD,  "Standard"},
62   {IDS_ACCEL_BASIC,     "Basic"},
63   {IDS_ACCEL_EMULATION, "Emulation"},
64   {0, 0}
65 };
66
67 static const char* DSound_Rates[] = {
68   "48000",
69   "44100",
70   "22050",
71   "16000",
72   "11025",
73   "8000",
74   NULL
75 };
76
77 static const char* DSound_Bits[] = {
78   "8",
79   "16",
80   NULL
81 };
82
83 static const AUDIO_DRIVER sAudioDrivers[] = {
84   {IDS_DRIVER_ALSA,      "alsa"},
85   {IDS_DRIVER_ARTS,      "arts"},
86   {IDS_DRIVER_ESOUND,    "esd"},
87   {IDS_DRIVER_OSS,       "oss"},
88   {IDS_DRIVER_JACK,      "jack"},
89   {IDS_DRIVER_NAS,       "nas"},
90   {IDS_DRIVER_AUDIOIO,   "audioio"},
91   {IDS_DRIVER_COREAUDIO, "coreaudio"},
92   {0, ""}
93 };
94
95 /* list of available drivers */
96 static AUDIO_DRIVER * loadedAudioDrv;
97
98 /* local copy of registry setting */
99 static char curAudioDriver[1024];
100
101 /* driver index to configure */
102 static int toConfigure;
103
104 /* display a driver specific configuration dialog */
105 static void configureAudioDriver(HWND hDlg)
106 {
107     const AUDIO_DRIVER *pAudioDrv = &loadedAudioDrv[toConfigure];
108
109     if (strlen(pAudioDrv->szDriver) != 0)
110     {
111         HDRVR hdrvr;
112         char wine_driver[MAX_NAME_LENGTH + 9];
113         sprintf(wine_driver, "wine%s.drv", pAudioDrv->szDriver);
114         hdrvr = OpenDriverA(wine_driver, 0, 0);
115         if (hdrvr != 0)
116         {
117             if (SendDriverMessage(hdrvr, DRV_QUERYCONFIGURE, 0, 0) != 0)
118             {
119                 DRVCONFIGINFO dci;
120                 LONG lRes;
121                 dci.dwDCISize = sizeof (dci);
122                 dci.lpszDCISectionName = NULL;
123                 dci.lpszDCIAliasName = NULL;
124                 lRes = SendDriverMessage(hdrvr, DRV_CONFIGURE, 0, (LONG_PTR)&dci);
125             }
126             CloseDriver(hdrvr, 0, 0);
127         }
128         else
129         {
130             WCHAR wine_driverW[MAX_NAME_LENGTH+9];
131             WCHAR messageStr[256];
132             WCHAR str[1024];
133             
134             MultiByteToWideChar (CP_ACP, 0, wine_driver, -1, wine_driverW, 
135                 sizeof (wine_driverW)/sizeof(wine_driverW[0])); 
136             
137             LoadStringW (GetModuleHandle (NULL), IDS_OPEN_DRIVER_ERROR, messageStr,
138                 sizeof(messageStr)/sizeof(messageStr[0]));
139             wsprintfW (str, messageStr, wine_driverW);
140             MessageBoxW (hDlg, str, NULL, MB_OK | MB_ICONERROR);
141         }
142     }
143 }
144
145 /* is driver in local copy of driver registry string */
146 static BOOL isDriverSet(const char * driver)
147 {
148     WINE_TRACE("driver = %s, curAudioDriver = %s\n", driver, curAudioDriver);
149
150     if (strstr(curAudioDriver, driver))
151         return TRUE;
152
153     return FALSE;
154 }
155
156 /* add driver to local copy of driver registry string */
157 static void addDriver(const char * driver)
158 {
159     if (!isDriverSet(driver))
160     {
161         if (strlen(curAudioDriver))
162             strcat(curAudioDriver, ",");
163         strcat(curAudioDriver, driver);
164     }
165 }
166
167 /* remove driver from local copy of driver registry string */
168 static void removeDriver(const char * driver)
169 {
170     char before[32], after[32], * start;
171
172     strcpy(before, ",");
173     strcat(before, driver);
174     strcpy(after, driver);
175     strcat(after, ",");
176
177     if ((start = strstr(curAudioDriver, after)))
178     {
179         int len = strlen(after);
180         char * end = curAudioDriver + strlen(curAudioDriver);
181         int i, count = end - start + len;
182         for (i = 0; i < count; i++)
183         {
184             if (start + len >= end)
185                 *start = 0;
186             else
187                 *start = start[len];
188             start++;
189         }
190     }
191     else if ((start = strstr(curAudioDriver, before)))
192     {
193         int len = strlen(before);
194         char * end = curAudioDriver + strlen(curAudioDriver);
195         int i, count = end - start + len;
196         for (i = 0; i < count; i++)
197         {
198             if (start + len >= end)
199                 *start = 0;
200             else
201                 *start = start[len];
202             start++;
203         }
204     }
205     else if (strcmp(curAudioDriver, driver) == 0)
206     {
207         strcpy(curAudioDriver, "");
208     }
209 }
210
211 static void initAudioDeviceTree(HWND hDlg)
212 {
213     const AUDIO_DRIVER *pAudioDrv = NULL;
214     int i, j;
215     TVINSERTSTRUCTW insert;
216     HTREEITEM root, driver[10];
217     HWND tree = NULL;
218     HIMAGELIST hImageList;
219     HBITMAP hBitMap;
220     HCURSOR old_cursor;
221     WCHAR driver_type[64], dev_type[64];
222
223     tree = GetDlgItem(hDlg, IDC_AUDIO_TREE);
224
225     if (!tree)
226         return;
227
228     /* set tree view style */
229     SetWindowLong(tree, GWL_STYLE, GetWindowLong(tree, GWL_STYLE) | TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT);
230
231     /* state checkbox */
232     hImageList = ImageList_Create(16, 16, FALSE, 3, 0);
233     hBitMap = LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(IDB_CHECKBOX));
234     ImageList_Add(hImageList, hBitMap, NULL);
235     DeleteObject(hBitMap);
236     SendMessageW( tree, TVM_SETIMAGELIST, TVSIL_STATE, (LPARAM)hImageList );
237
238     /* root item */
239     LoadStringW (GetModuleHandle (NULL), IDS_SOUNDDRIVERS, driver_type,
240         sizeof(driver_type)/sizeof(driver_type[0]));
241     insert.hParent = TVI_ROOT;
242     insert.hInsertAfter = TVI_LAST;
243     insert.u.item.mask = TVIF_TEXT | TVIF_CHILDREN;
244     insert.u.item.pszText = driver_type;
245     insert.u.item.cChildren = 1;
246     root = (HTREEITEM)SendDlgItemMessageW (hDlg, IDC_AUDIO_TREE, TVM_INSERTITEMW, 0, (LPARAM)&insert);
247
248     /* change to the wait cursor because this can take a while if there is a
249      * misbehaving driver that takes a long time to open
250      */
251     old_cursor = SetCursor(LoadCursor(0, IDC_WAIT));
252
253     /* iterate over list of loaded drivers */
254     for (pAudioDrv = loadedAudioDrv, i = 0; pAudioDrv->nameID; i++, pAudioDrv++) {
255         HDRVR hdrv;
256         char name[MAX_PATH];
257         WCHAR text[MAX_PATH];
258
259         sprintf(name, "wine%s.drv", pAudioDrv->szDriver);
260         LoadStringW (GetModuleHandle (NULL), pAudioDrv->nameID, text,
261             sizeof(text)/sizeof(text[0]));
262
263         if ((hdrv = OpenDriverA(name, 0, 0)))
264         {
265             HMODULE lib;
266             if ((lib = GetDriverModuleHandle(hdrv)))
267             {
268                 int num_wod = 0, num_wid = 0, num_mod = 0, num_mid = 0, num_aux = 0, num_mxd = 0;
269                 MessagePtr wodMessagePtr = (MessagePtr)GetProcAddress(lib, "wodMessage");
270                 MessagePtr widMessagePtr = (MessagePtr)GetProcAddress(lib, "widMessage");
271                 MessagePtr modMessagePtr = (MessagePtr)GetProcAddress(lib, "modMessage");
272                 MessagePtr midMessagePtr = (MessagePtr)GetProcAddress(lib, "midMessage");
273                 MessagePtr auxMessagePtr = (MessagePtr)GetProcAddress(lib, "auxMessage");
274                 MessagePtr mxdMessagePtr = (MessagePtr)GetProcAddress(lib, "mxdMessage");
275
276                 if (wodMessagePtr)
277                     num_wod = wodMessagePtr(0, WODM_GETNUMDEVS, 0, 0, 0);
278
279                 if (widMessagePtr)
280                     num_wid = widMessagePtr(0, WIDM_GETNUMDEVS, 0, 0, 0);
281
282                 if (modMessagePtr)
283                     num_mod = modMessagePtr(0, MODM_GETNUMDEVS, 0, 0, 0);
284
285                 if (midMessagePtr)
286                     num_mid = midMessagePtr(0, MIDM_GETNUMDEVS, 0, 0, 0);
287
288                 if (auxMessagePtr)
289                     num_aux = auxMessagePtr(0, AUXDM_GETNUMDEVS, 0, 0, 0);
290
291                 if (mxdMessagePtr)
292                     num_mxd = mxdMessagePtr(0, MXDM_GETNUMDEVS, 0, 0, 0);
293
294                 if (num_wod == 0 && num_wid == 0 && num_mod == 0 && num_mid == 0 && num_aux == 0 && num_mxd == 0)
295                 {
296                     insert.hParent = root;
297                     insert.u.item.mask = TVIF_TEXT | TVIF_STATE | TVIF_PARAM;
298                     insert.u.item.pszText = text;
299                     insert.u.item.stateMask = TVIS_STATEIMAGEMASK;
300                     insert.u.item.lParam =  i + DRIVER_MASK;
301
302                     driver[i] = (HTREEITEM)SendDlgItemMessageW (hDlg, IDC_AUDIO_TREE, TVM_INSERTITEMW, 0, (LPARAM)&insert);
303                 }
304                 else
305                 {
306                     HTREEITEM type;
307
308                     insert.hParent = root;
309                     insert.u.item.mask = TVIF_TEXT | TVIF_CHILDREN | TVIF_STATE | TVIF_PARAM;
310                     insert.u.item.pszText = text;
311                     insert.u.item.cChildren = 1;
312                     insert.u.item.stateMask = TVIS_STATEIMAGEMASK;
313                     insert.u.item.lParam =  i + DRIVER_MASK;
314
315                     if (isDriverSet(pAudioDrv->szDriver))
316                         insert.u.item.state = INDEXTOSTATEIMAGEMASK(2);
317                     else
318                         insert.u.item.state = INDEXTOSTATEIMAGEMASK(1);
319
320                     driver[i] = (HTREEITEM)SendDlgItemMessageW (hDlg, IDC_AUDIO_TREE, TVM_INSERTITEMW, 0, (LPARAM)&insert);
321
322                     if (num_wod)
323                     {
324                         LoadStringW (GetModuleHandle (NULL), IDS_DEVICES_WAVEOUT, dev_type,
325                             sizeof(dev_type)/sizeof(dev_type[0]));
326
327                         insert.hParent = driver[i];
328                         insert.u.item.mask = TVIF_TEXT | TVIF_CHILDREN;
329                         insert.u.item.pszText = dev_type;
330                         insert.u.item.cChildren = 1;
331
332                         type = (HTREEITEM)SendDlgItemMessageW (hDlg, IDC_AUDIO_TREE, TVM_INSERTITEMW, 0, (LPARAM)&insert);
333
334                         for (j = 0; j < num_wod; j++)
335                         {
336                             WAVEOUTCAPSW caps;
337
338                             wodMessagePtr(j, WODM_GETDEVCAPS, 0, (DWORD_PTR)&caps, sizeof(caps));
339
340                             insert.hParent = type;
341                             insert.u.item.mask = TVIF_TEXT | TVIF_PARAM;
342                             insert.u.item.pszText = caps.szPname;
343                             insert.u.item.lParam = j + DEVICE_MASK;
344
345                             SendDlgItemMessageW (hDlg, IDC_AUDIO_TREE, TVM_INSERTITEMW, 0, (LPARAM)&insert);
346                         }
347                     }
348
349                     if (num_wid)
350                     {
351                         LoadStringW (GetModuleHandle (NULL), IDS_DEVICES_WAVEIN, dev_type,
352                             sizeof(dev_type)/sizeof(dev_type[0]));
353
354                         insert.hParent = driver[i];
355                         insert.u.item.mask = TVIF_TEXT | TVIF_CHILDREN;
356                         insert.u.item.pszText = dev_type;
357                         insert.u.item.cChildren = 1;
358
359                         type = (HTREEITEM)SendDlgItemMessageW (hDlg, IDC_AUDIO_TREE, TVM_INSERTITEMW, 0, (LPARAM)&insert);
360
361                         for (j = 0; j < num_wid; j++)
362                         {
363                             WAVEINCAPSW caps;
364
365                             widMessagePtr(j, WIDM_GETDEVCAPS, 0, (DWORD_PTR)&caps, sizeof(caps));
366
367                             insert.hParent = type;
368                             insert.u.item.mask = TVIF_TEXT | TVIF_PARAM;
369                             insert.u.item.pszText = caps.szPname;
370                             insert.u.item.lParam = j + DEVICE_MASK;
371
372                             SendDlgItemMessageW (hDlg, IDC_AUDIO_TREE, TVM_INSERTITEMW, 0, (LPARAM)&insert);
373                         }
374                     }
375
376                     if (num_mod)
377                     {
378                         LoadStringW (GetModuleHandle (NULL), IDS_DEVICES_MIDIOUT, dev_type,
379                             sizeof(dev_type)/sizeof(dev_type[0]));
380
381                         insert.hParent = driver[i];
382                         insert.u.item.mask = TVIF_TEXT | TVIF_CHILDREN;
383                         insert.u.item.pszText = dev_type;
384                         insert.u.item.cChildren = 1;
385
386                         type = (HTREEITEM)SendDlgItemMessageW (hDlg, IDC_AUDIO_TREE, TVM_INSERTITEMW, 0, (LPARAM)&insert);
387
388                         for (j = 0; j < num_mod; j++)
389                         {
390                             MIDIOUTCAPSW caps;
391
392                             modMessagePtr(j, MODM_GETDEVCAPS, 0, (DWORD_PTR)&caps, sizeof(caps));
393
394                             insert.hParent = type;
395                             insert.u.item.mask = TVIF_TEXT | TVIF_PARAM;
396                             insert.u.item.pszText = caps.szPname;
397                             insert.u.item.lParam = j + DEVICE_MASK;
398
399                             SendDlgItemMessageW (hDlg, IDC_AUDIO_TREE, TVM_INSERTITEMW, 0, (LPARAM)&insert);
400                         }
401                     }
402
403                     if (num_mid)
404                     {
405                         LoadStringW (GetModuleHandle (NULL), IDS_DEVICES_MIDIIN, dev_type,
406                             sizeof(dev_type)/sizeof(dev_type[0]));
407
408                         insert.hParent = driver[i];
409                         insert.u.item.mask = TVIF_TEXT | TVIF_CHILDREN;
410                         insert.u.item.pszText = dev_type;
411                         insert.u.item.cChildren = 1;
412
413                         type = (HTREEITEM)SendDlgItemMessageW (hDlg, IDC_AUDIO_TREE, TVM_INSERTITEMW, 0, (LPARAM)&insert);
414
415                         for (j = 0; j < num_mid; j++)
416                         {
417                             MIDIINCAPSW caps;
418
419                             midMessagePtr(j, MIDM_GETDEVCAPS, 0, (DWORD_PTR)&caps, sizeof(caps));
420
421                             insert.hParent = type;
422                             insert.u.item.mask = TVIF_TEXT | TVIF_PARAM;
423                             insert.u.item.pszText = caps.szPname;
424                             insert.u.item.lParam = j + DEVICE_MASK;
425
426                             SendDlgItemMessageW (hDlg, IDC_AUDIO_TREE, TVM_INSERTITEMW, 0, (LPARAM)&insert);
427                         }
428                     }
429
430                     if (num_aux)
431                     {
432                         LoadStringW (GetModuleHandle (NULL), IDS_DEVICES_AUX, dev_type,
433                             sizeof(dev_type)/sizeof(dev_type[0]));
434
435                         insert.hParent = driver[i];
436                         insert.u.item.mask = TVIF_TEXT | TVIF_CHILDREN;
437                         insert.u.item.pszText = dev_type;
438                         insert.u.item.cChildren = 1;
439
440                         type = (HTREEITEM)SendDlgItemMessageW (hDlg, IDC_AUDIO_TREE, TVM_INSERTITEMW, 0, (LPARAM)&insert);
441
442                         for (j = 0; j < num_aux; j++)
443                         {
444                             AUXCAPSW caps;
445
446                             auxMessagePtr(j, AUXDM_GETDEVCAPS, 0, (DWORD_PTR)&caps, sizeof(caps));
447
448                             insert.hParent = type;
449                             insert.u.item.mask = TVIF_TEXT | TVIF_PARAM;
450                             insert.u.item.pszText = caps.szPname;
451                             insert.u.item.lParam = j + DEVICE_MASK;
452
453                             SendDlgItemMessageW (hDlg, IDC_AUDIO_TREE, TVM_INSERTITEMW, 0, (LPARAM)&insert);
454                         }
455                     }
456
457                     if (num_mxd)
458                     {
459                         LoadStringW (GetModuleHandle (NULL), IDS_DEVICES_MIXER, dev_type,
460                             sizeof(dev_type)/sizeof(dev_type[0]));
461
462                         insert.hParent = driver[i];
463                         insert.u.item.mask = TVIF_TEXT | TVIF_CHILDREN;
464                         insert.u.item.pszText = dev_type;
465                         insert.u.item.cChildren = 1;
466
467                         type = (HTREEITEM)SendDlgItemMessageW (hDlg, IDC_AUDIO_TREE, TVM_INSERTITEMW, 0, (LPARAM)&insert);
468
469                         for (j = 0; j < num_mxd; j++)
470                         {
471                             MIXERCAPSW caps;
472
473                             mxdMessagePtr(j, MXDM_GETDEVCAPS, 0, (DWORD_PTR)&caps, sizeof(caps));
474
475                             insert.hParent = type;
476                             insert.u.item.mask = TVIF_TEXT | TVIF_PARAM;
477                             insert.u.item.pszText = caps.szPname;
478                             insert.u.item.lParam = j + DEVICE_MASK;
479
480                             SendDlgItemMessageW (hDlg, IDC_AUDIO_TREE, TVM_INSERTITEMW, 0, (LPARAM)&insert);
481                         }
482                     }
483                 }
484             }
485         }
486     }
487
488     /* restore the original cursor */
489     SetCursor(old_cursor);
490
491     SendDlgItemMessage(hDlg, IDC_AUDIO_TREE, TVM_SELECTITEM, 0, 0);
492     SendDlgItemMessage(hDlg, IDC_AUDIO_TREE, TVM_EXPAND, TVE_EXPAND, (LPARAM)root);
493     for (j = 0; j < i; j++)
494         SendDlgItemMessage(hDlg, IDC_AUDIO_TREE, TVM_EXPAND, TVE_EXPAND, (LPARAM)driver[j]);
495 }
496
497 /* find all drivers that can be loaded */
498 static void findAudioDrivers(void)
499 {
500     int numFound = 0;
501     const AUDIO_DRIVER *pAudioDrv = NULL;
502     HCURSOR old_cursor;
503
504     /* delete an existing list */
505     HeapFree(GetProcessHeap(), 0, loadedAudioDrv);
506     loadedAudioDrv = 0;
507
508     /* change to the wait cursor because this can take a while if there is a
509      * misbehaving driver that takes a long time to open
510      */
511     old_cursor = SetCursor(LoadCursor(0, IDC_WAIT));
512
513     for (pAudioDrv = sAudioDrivers; pAudioDrv->nameID; pAudioDrv++)
514     {
515         if (strlen(pAudioDrv->szDriver))
516         {
517             HDRVR hdrv;
518             char driver[MAX_PATH];
519
520             sprintf(driver, "wine%s.drv", pAudioDrv->szDriver);
521
522             if ((hdrv = OpenDriverA(driver, 0, 0)))
523             {
524                 CloseDriver(hdrv, 0, 0);
525
526                 if (loadedAudioDrv)
527                     loadedAudioDrv = HeapReAlloc(GetProcessHeap(), 0, loadedAudioDrv, (numFound + 1) * sizeof(AUDIO_DRIVER));
528                 else
529                     loadedAudioDrv = HeapAlloc(GetProcessHeap(), 0, sizeof(AUDIO_DRIVER));
530
531                 CopyMemory(&loadedAudioDrv[numFound], pAudioDrv, sizeof(AUDIO_DRIVER));
532                 numFound++;
533             }
534         }
535     }
536
537     /* restore the original cursor */
538     SetCursor(old_cursor);
539
540     /* terminate list with empty driver */
541     if (numFound) {
542         loadedAudioDrv = HeapReAlloc(GetProcessHeap(), 0, loadedAudioDrv, (numFound + 1) * sizeof(AUDIO_DRIVER));
543         CopyMemory(&loadedAudioDrv[numFound], pAudioDrv, sizeof(AUDIO_DRIVER));
544     } else {
545         loadedAudioDrv = HeapAlloc(GetProcessHeap(), 0, sizeof(AUDIO_DRIVER));
546         ZeroMemory(&loadedAudioDrv[0], sizeof(AUDIO_DRIVER));
547     }
548 }
549
550 /* check local copy of registry string for unloadable drivers */
551 static void checkRegistrySetting(HWND hDlg)
552 {
553     const AUDIO_DRIVER *pAudioDrv;
554     char * token, * tokens = strdup(curAudioDriver);
555
556 start_over:
557     token = strtok(tokens, ",");
558     while (token != NULL)
559     {
560         BOOL found = FALSE;
561         for (pAudioDrv = loadedAudioDrv; pAudioDrv->nameID; pAudioDrv++)
562         {
563             if (strcmp(token, pAudioDrv->szDriver) == 0)
564             {
565                 found = TRUE;
566                 break;
567             }
568         }
569         if (found == FALSE)
570         {
571             WCHAR tokenW[MAX_NAME_LENGTH+1];
572             WCHAR messageStr[256];
573             WCHAR str[1024];
574             WCHAR caption[64];
575             
576             MultiByteToWideChar (CP_ACP, 0, token, -1, tokenW, sizeof(tokenW)/sizeof(tokenW[0]));
577             
578             LoadStringW (GetModuleHandle (NULL), IDS_UNAVAILABLE_DRIVER, messageStr,
579                 sizeof(messageStr)/sizeof(messageStr[0]));
580             wsprintfW (str, messageStr, tokenW);
581             LoadStringW (GetModuleHandle (NULL), IDS_WARNING, caption,
582                 sizeof(caption)/sizeof(caption[0]));
583             if (MessageBoxW (hDlg, str, caption, MB_ICONWARNING | MB_YESNOCANCEL) == IDYES)
584             {
585                 removeDriver(token);
586                 strcpy(tokens, curAudioDriver);
587                 goto start_over;
588             }
589         }
590         token = strtok(NULL, ",");
591     }
592     free(tokens);
593 }
594
595 static void selectDriver(HWND hDlg, const char * driver)
596 {
597     WCHAR text[1024];
598     WCHAR caption[64];
599
600     strcpy(curAudioDriver, driver);
601     set_reg_key(config_key, "Drivers", "Audio", curAudioDriver);
602
603     if (LoadStringW(GetModuleHandle(NULL), IDS_AUDIO_MISSING, text, sizeof(text)/sizeof(text[0])))
604     {
605         if (LoadStringW(GetModuleHandle(NULL), IDS_WINECFG_TITLE, caption, sizeof(caption)/sizeof(caption[0])))
606             MessageBoxW(hDlg, text, caption, MB_OK | MB_ICONINFORMATION);
607     }
608    
609     SendMessage(GetParent(hDlg), PSM_CHANGED, (WPARAM) hDlg, 0); /* enable apply button */
610 }
611
612 static void initAudioDlg (HWND hDlg)
613 {
614     int i;
615     char* buf = NULL;
616
617     WINE_TRACE("\n");
618
619     /* make a list of all drivers that can be loaded */
620     findAudioDrivers();
621
622     /* get current registry setting if available */
623     buf = get_reg_key(config_key, "Drivers", "Audio", NULL);
624
625     /* check for first time install and set a default driver
626      * select in this order: oss, alsa, first available driver, none
627      */
628     if (buf == NULL)
629     {
630         const AUDIO_DRIVER *pAudioDrv = NULL;
631
632         /* select oss if available */
633         for (pAudioDrv = loadedAudioDrv; pAudioDrv->nameID; pAudioDrv++)
634         {
635             if (strcmp(pAudioDrv->szDriver, "oss") == 0)
636             {
637                 selectDriver(hDlg, "oss");
638                 break;
639             }
640         }
641
642         if (strlen(curAudioDriver) == 0)
643         {
644             /* select alsa if available */
645             for (pAudioDrv = loadedAudioDrv; pAudioDrv->nameID; pAudioDrv++)
646             {
647                 if (strcmp(pAudioDrv->szDriver, "alsa") == 0)
648                 {
649                     selectDriver(hDlg, "alsa");
650                     break;
651                 }
652             }
653         }
654
655         if (strlen(curAudioDriver) == 0)
656         {
657             /* select first available driver */
658             if (*loadedAudioDrv->szDriver)
659                 selectDriver(hDlg, loadedAudioDrv->szDriver);
660         }
661     }
662     else /* make a local copy of the current registry setting */
663         strcpy(curAudioDriver, buf);
664
665     WINE_TRACE("curAudioDriver = %s\n", curAudioDriver);
666
667     /* check for drivers that can't be loaded */
668     checkRegistrySetting(hDlg);
669
670     initAudioDeviceTree(hDlg);
671
672     SendDlgItemMessage(hDlg, IDC_DSOUND_HW_ACCEL, CB_RESETCONTENT, 0, 0);
673     for (i = 0; 0 != DSound_HW_Accels[i].displayID; ++i) {
674       WCHAR accelStr[64];
675       LoadStringW (GetModuleHandle (NULL), DSound_HW_Accels[i].displayID, accelStr,
676           sizeof(accelStr)/sizeof(accelStr[0]));
677       SendDlgItemMessageW (hDlg, IDC_DSOUND_HW_ACCEL, CB_ADDSTRING, 0, (LPARAM)accelStr);
678     }
679     buf = get_reg_key(config_key, keypath("DirectSound"), "HardwareAcceleration", "Full");
680     for (i = 0; NULL != DSound_HW_Accels[i].settingStr; ++i) {
681       if (strcmp(buf, DSound_HW_Accels[i].settingStr) == 0) {
682         SendDlgItemMessage(hDlg, IDC_DSOUND_HW_ACCEL, CB_SETCURSEL, i, 0);
683         break ;
684       }
685     }
686     if (NULL == DSound_HW_Accels[i].settingStr) {
687       WINE_ERR("Invalid Direct Sound HW Accel read from registry (%s)\n", buf);
688     }
689     HeapFree(GetProcessHeap(), 0, buf);
690
691     SendDlgItemMessage(hDlg, IDC_DSOUND_RATES, CB_RESETCONTENT, 0, 0);
692     for (i = 0; NULL != DSound_Rates[i]; ++i) {
693       SendDlgItemMessage(hDlg, IDC_DSOUND_RATES, CB_ADDSTRING, 0, (LPARAM) DSound_Rates[i]);
694     }
695     buf = get_reg_key(config_key, keypath("DirectSound"), "DefaultSampleRate", "22050");
696     for (i = 0; NULL != DSound_Rates[i]; ++i) {
697       if (strcmp(buf, DSound_Rates[i]) == 0) {
698         SendDlgItemMessage(hDlg, IDC_DSOUND_RATES, CB_SETCURSEL, i, 0);
699         break ;
700       }
701     }
702
703     SendDlgItemMessage(hDlg, IDC_DSOUND_BITS, CB_RESETCONTENT, 0, 0);
704     for (i = 0; NULL != DSound_Bits[i]; ++i) {
705       SendDlgItemMessage(hDlg, IDC_DSOUND_BITS, CB_ADDSTRING, 0, (LPARAM) DSound_Bits[i]);
706     }
707     buf = get_reg_key(config_key, keypath("DirectSound"), "DefaultBitsPerSample", "8");
708     for (i = 0; NULL != DSound_Bits[i]; ++i) {
709       if (strcmp(buf, DSound_Bits[i]) == 0) {
710         SendDlgItemMessage(hDlg, IDC_DSOUND_BITS, CB_SETCURSEL, i, 0);
711         break ;
712       }
713     }
714
715     buf = get_reg_key(config_key, keypath("DirectSound"), "EmulDriver", "N");
716     if (IS_OPTION_TRUE(*buf))
717       CheckDlgButton(hDlg, IDC_DSOUND_DRV_EMUL, BST_CHECKED);
718     else
719       CheckDlgButton(hDlg, IDC_DSOUND_DRV_EMUL, BST_UNCHECKED);
720     HeapFree(GetProcessHeap(), 0, buf);
721 }
722
723 INT_PTR CALLBACK
724 AudioDlgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
725 {
726   switch (uMsg) {
727       case WM_COMMAND:
728         switch (LOWORD(wParam)) {
729           case IDC_AUDIO_CONFIGURE:
730              configureAudioDriver(hDlg);
731              break;
732           case IDC_AUDIO_CONTROL_PANEL:
733              MessageBox(NULL, "Launching audio control panel not implemented yet!", "Fixme", MB_OK | MB_ICONERROR);
734              break;
735           case IDC_DSOUND_HW_ACCEL:
736             if (HIWORD(wParam) == CBN_SELCHANGE) {
737               int selected_dsound_accel;
738               SendMessage(GetParent(hDlg), PSM_CHANGED, 0, 0);
739               selected_dsound_accel = SendDlgItemMessage(hDlg, IDC_DSOUND_HW_ACCEL, CB_GETCURSEL, 0, 0);
740               set_reg_key(config_key, keypath("DirectSound"), "HardwareAcceleration", 
741                  DSound_HW_Accels[selected_dsound_accel].settingStr);
742             }
743             break;
744           case IDC_DSOUND_RATES:
745             if (HIWORD(wParam) == CBN_SELCHANGE) {
746               int selected_dsound_rate;
747               SendMessage(GetParent(hDlg), PSM_CHANGED, 0, 0);
748               selected_dsound_rate = SendDlgItemMessage(hDlg, IDC_DSOUND_RATES, CB_GETCURSEL, 0, 0);
749               set_reg_key(config_key, keypath("DirectSound"), "DefaultSampleRate", DSound_Rates[selected_dsound_rate]);
750             }
751             break;
752           case IDC_DSOUND_BITS:
753             if (HIWORD(wParam) == CBN_SELCHANGE) {
754               int selected_dsound_bits;
755               SendMessage(GetParent(hDlg), PSM_CHANGED, 0, 0);
756               selected_dsound_bits = SendDlgItemMessage(hDlg, IDC_DSOUND_BITS, CB_GETCURSEL, 0, 0);
757               set_reg_key(config_key, keypath("DirectSound"), "DefaultBitsPerSample", DSound_Bits[selected_dsound_bits]);
758             }
759             break;
760           case IDC_DSOUND_DRV_EMUL:
761             if (HIWORD(wParam) == BN_CLICKED) {
762               SendMessage(GetParent(hDlg), PSM_CHANGED, 0, 0);
763               if (IsDlgButtonChecked(hDlg, IDC_DSOUND_DRV_EMUL) == BST_CHECKED)
764                 set_reg_key(config_key, keypath("DirectSound"), "EmulDriver", "Y");
765               else
766                 set_reg_key(config_key, keypath("DirectSound"), "EmulDriver", "N");
767             }
768             break;
769         }
770         break;
771
772       case WM_SHOWWINDOW:
773         set_window_title(hDlg);
774         break;
775
776       case WM_NOTIFY:
777         switch(((LPNMHDR)lParam)->code) {
778             case PSN_KILLACTIVE:
779               SetWindowLongPtr(hDlg, DWLP_MSGRESULT, FALSE);
780               break;
781             case PSN_APPLY:
782               set_reg_key(config_key, "Drivers", "Audio", curAudioDriver);
783               apply();
784               SetWindowLongPtr(hDlg, DWLP_MSGRESULT, PSNRET_NOERROR);
785               break;
786             case PSN_SETACTIVE:
787               break;
788             case NM_CLICK:
789               if (((LPNMHDR)lParam)->idFrom == IDC_AUDIO_TREE)
790               {
791                   TVHITTESTINFO ht;
792                   DWORD dwPos = GetMessagePos();
793                   HWND tree = ((LPNMHDR)lParam)->hwndFrom;
794                   ZeroMemory(&ht, sizeof(ht));
795                   ht.pt.x = (short)LOWORD(dwPos);
796                   ht.pt.y = (short)HIWORD(dwPos);
797                   MapWindowPoints(HWND_DESKTOP, tree, &ht.pt, 1);
798                   SendMessageW( tree, TVM_HITTEST, 0, (LPARAM)&ht );
799                   if (TVHT_ONITEMSTATEICON & ht.flags)
800                   {
801                       TVITEM tvItem;
802                       int index;
803                       ZeroMemory(&tvItem, sizeof(tvItem));
804                       tvItem.hItem = ht.hItem;
805                       SendMessageW( tree, TVM_GETITEMW, 0, (LPARAM) &tvItem );
806
807                       index = TreeView_GetItemState(tree, ht.hItem, TVIS_STATEIMAGEMASK);
808                       if (index == INDEXTOSTATEIMAGEMASK(1))
809                       {
810                           TreeView_SetItemState(tree, ht.hItem, INDEXTOSTATEIMAGEMASK(2), TVIS_STATEIMAGEMASK);
811                           addDriver(loadedAudioDrv[tvItem.lParam & 0xff].szDriver);
812                           SendMessage(GetParent(hDlg), PSM_CHANGED, (WPARAM) hDlg, 0); /* enable apply button */
813                       }
814                       else if (index == INDEXTOSTATEIMAGEMASK(2))
815                       {
816                           TreeView_SetItemState(tree, ht.hItem, INDEXTOSTATEIMAGEMASK(1), TVIS_STATEIMAGEMASK);
817                           removeDriver(loadedAudioDrv[tvItem.lParam & 0xff].szDriver);
818                           SendMessage(GetParent(hDlg), PSM_CHANGED, (WPARAM) hDlg, 0); /* enable apply button */
819                       }
820                   }
821               }
822               break;
823             case NM_RCLICK:
824               if (((LPNMHDR)lParam)->idFrom == IDC_AUDIO_TREE)
825               {
826                   TVHITTESTINFO ht;
827                   DWORD dwPos = GetMessagePos();
828                   HWND tree = ((LPNMHDR)lParam)->hwndFrom;
829                   POINT pt;
830                   ZeroMemory(&ht, sizeof(ht));
831                   pt.x = (short)LOWORD(dwPos);
832                   pt.y = (short)HIWORD(dwPos);
833                   ht.pt = pt;
834                   MapWindowPoints(HWND_DESKTOP, tree, &ht.pt, 1);
835                   SendMessageW( tree, TVM_HITTEST, 0, (LPARAM)&ht );
836                   if (TVHT_ONITEMLABEL & ht.flags)
837                   {
838                       TVITEM tvItem;
839                       ZeroMemory(&tvItem, sizeof(tvItem));
840                       tvItem.hItem = ht.hItem;
841                       tvItem.mask = TVIF_PARAM;
842                       tvItem.lParam = -1;
843                       if (TreeView_GetItem(tree, &tvItem))
844                       {
845                           if (tvItem.lParam & DRIVER_MASK)
846                           {
847                               if (hPopupMenus)
848                               {
849                                   TrackPopupMenu(GetSubMenu(hPopupMenus, 0), TPM_RIGHTBUTTON, pt.x, pt.y, 0, tree, NULL);
850                                   toConfigure = tvItem.lParam & ~DRIVER_MASK;
851                               }
852                           }
853                           else if (tvItem.lParam & DEVICE_MASK)
854                           {
855                               /* FIXME TBD */
856                           }
857
858                       }
859                   }
860               }
861         }
862         break;
863
864   case WM_INITDIALOG:
865     initAudioDlg(hDlg);
866     break;
867   }
868
869   return FALSE;
870 }