user32: Make sure that IsWindowVisible returns FALSE for HWND_MESSAGE windows.
[wine] / programs / winecfg / libraries.c
1 /*
2  * WineCfg libraries tabsheet
3  *
4  * Copyright 2004 Robert van Herk
5  * Copyright 2004 Mike Hearn
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  *
21  */
22
23 #include "config.h"
24 #include "wine/port.h"
25
26 #define NONAMELESSUNION
27 #define WIN32_LEAN_AND_MEAN
28 #include <windows.h>
29 #include <commdlg.h>
30 #include <wine/library.h>
31 #include <wine/debug.h>
32 #include <stdio.h>
33 #include <dirent.h>
34 #include <assert.h>
35 #include <stdlib.h>
36 #ifdef HAVE_SYS_STAT_H
37 #include <sys/stat.h>
38 #endif
39 #ifdef HAVE_UNISTD_H
40 #include <unistd.h>
41 #endif
42
43 #include "winecfg.h"
44 #include "resource.h"
45
46 WINE_DEFAULT_DEBUG_CHANNEL(winecfg);
47
48 /* dlls that shouldn't be configured anything other than builtin; list must be sorted*/
49 static const char * const builtin_only[] =
50 {
51     "advapi32",
52     "capi2032",
53     "dbghelp",
54     "ddraw",
55     "gdi32",
56     "glu32",
57     "icmp",
58     "iphlpapi",
59     "kernel32",
60     "mswsock",
61     "ntdll",
62     "opengl32",
63     "stdole2.tlb",
64     "stdole32.tlb",
65     "twain_32",
66     "unicows",
67     "user32",
68     "vdmdbg",
69     "w32skrnl",
70     "winealsa.drv",
71     "wineaudioio.drv",
72     "wined3d",
73     "winedos",
74     "wineesd.drv",
75     "winejack.drv",
76     "winejoystick.drv",
77     "winemp3.acm",
78     "winenas.drv",
79     "wineoss.drv",
80     "wineps",
81     "wineps.drv",
82     "winex11.drv",
83     "winmm",
84     "wintab32",
85     "wnaspi32",
86     "wow32",
87     "ws2_32",
88     "wsock32",
89 };
90
91 enum dllmode
92 {
93         BUILTIN_NATIVE,
94         NATIVE_BUILTIN,
95         BUILTIN,
96         NATIVE,
97         DISABLE,
98         UNKNOWN /* Special value indicating an erroneous DLL override mode */
99 };
100
101 struct dll
102 {
103         char *name;
104         enum dllmode mode;
105 };
106
107 /* Convert a registry string to a dllmode */
108 static enum dllmode string_to_mode(char *in)
109 {
110     int i, j, len;
111     char *out;
112     enum dllmode res;
113
114     len = strlen(in);
115     out = HeapAlloc(GetProcessHeap(), 0, len);
116
117     /* remove the spaces */
118     for (i = j = 0; i <= len; ++i) {
119         if (in[i] != ' ') {
120             out[j++] = in[i];
121         }
122     }
123
124     /* parse the string */
125     res = UNKNOWN;
126     if (strcmp(out, "builtin,native") == 0) res = BUILTIN_NATIVE;
127     if (strcmp(out, "native,builtin") == 0) res = NATIVE_BUILTIN;
128     if (strcmp(out, "builtin") == 0) res = BUILTIN;
129     if (strcmp(out, "native") == 0) res = NATIVE;
130     if (strcmp(out, "") == 0) res = DISABLE;
131
132     HeapFree(GetProcessHeap(), 0, out);
133     return res;
134 }
135
136 /* Convert a dllmode to a registry string. */
137 static const char* mode_to_string(enum dllmode mode)
138 {
139     switch( mode )
140     {
141         case NATIVE: return "native";
142         case BUILTIN: return "builtin";
143         case NATIVE_BUILTIN: return "native,builtin";
144         case BUILTIN_NATIVE: return "builtin,native";
145         case DISABLE: return "";
146         default: assert(FALSE); return "";
147     }
148 }
149
150 /* Convert a dllmode to a pretty string for display. TODO: use translations. */
151 static const char* mode_to_label(enum dllmode mode)
152 {
153     static char buffer[256];
154     UINT id = 0;
155
156     switch( mode )
157     {
158     case NATIVE: id = IDS_DLL_NATIVE; break;
159     case BUILTIN: id = IDS_DLL_BUILTIN; break;
160     case NATIVE_BUILTIN: id = IDS_DLL_NATIVE_BUILTIN; break;
161     case BUILTIN_NATIVE: id = IDS_DLL_BUILTIN_NATIVE; break;
162     case DISABLE: id = IDS_DLL_DISABLED; break;
163     default: assert(FALSE);
164     }
165     if (!LoadStringA( GetModuleHandleA(NULL), id, buffer, sizeof(buffer) )) buffer[0] = 0;
166     return buffer;
167 }
168
169 /* Convert a control id (IDC_ constant) to a dllmode */
170 static enum dllmode id_to_mode(DWORD id)
171 {
172     switch( id )
173     {
174         case IDC_RAD_BUILTIN: return BUILTIN;
175         case IDC_RAD_NATIVE: return NATIVE;
176         case IDC_RAD_NATIVE_BUILTIN: return NATIVE_BUILTIN;
177         case IDC_RAD_BUILTIN_NATIVE: return BUILTIN_NATIVE;
178         case IDC_RAD_DISABLE: return DISABLE;
179         default: assert( FALSE ); return 0; /* should not be reached  */
180     }
181 }
182
183 /* Convert a dllmode to a control id (IDC_ constant) */
184 static DWORD mode_to_id(enum dllmode mode)
185 {
186     switch( mode )
187     {
188         case BUILTIN: return IDC_RAD_BUILTIN;
189         case NATIVE: return IDC_RAD_NATIVE;
190         case NATIVE_BUILTIN: return IDC_RAD_NATIVE_BUILTIN;
191         case BUILTIN_NATIVE: return IDC_RAD_BUILTIN_NATIVE;
192         case DISABLE: return IDC_RAD_DISABLE;
193         default: assert( FALSE ); return 0; /* should not be reached  */
194     }
195 }
196
197 /* helper for is_builtin_only */
198 static int compare_dll( const void *ptr1, const void *ptr2 )
199 {
200     const char * const *name1 = ptr1;
201     const char * const *name2 = ptr2;
202     return strcmp( *name1, *name2 );
203 }
204
205 /* check if dll is recommended as builtin only */
206 static inline int is_builtin_only( const char *name )
207 {
208     return bsearch( &name, builtin_only, sizeof(builtin_only)/sizeof(builtin_only[0]),
209                     sizeof(builtin_only[0]), compare_dll ) != NULL;
210 }
211
212 static void set_controls_from_selection(HWND dialog)
213 {
214     /* FIXME: display/update some information about the selected dll (purpose, recommended loadorder) maybe? */
215 }
216
217 static void clear_settings(HWND dialog)
218 {
219     int count = SendDlgItemMessage(dialog, IDC_DLLS_LIST, LB_GETCOUNT, 0, 0);
220     int i;
221
222     WINE_TRACE("count=%d\n", count);
223     
224     for (i = 0; i < count; i++)
225     {
226         struct dll *dll = (struct dll *) SendDlgItemMessage(dialog, IDC_DLLS_LIST, LB_GETITEMDATA, 0, 0);
227         
228         SendDlgItemMessage(dialog, IDC_DLLS_LIST, LB_DELETESTRING, 0, 0);
229         
230         HeapFree(GetProcessHeap(), 0, dll->name);
231         HeapFree(GetProcessHeap(), 0, dll);
232     }
233 }
234
235 /* check if a given dll is 16-bit */
236 static int is_16bit_dll( const char *dir, const char *name )
237 {
238     char buffer[64];
239     int res;
240     size_t len = strlen(dir) + strlen(name) + 2;
241     char *path = HeapAlloc( GetProcessHeap(), 0, len );
242
243     strcpy( path, dir );
244     strcat( path, "/" );
245     strcat( path, name );
246     res = readlink( path, buffer, sizeof(buffer) );
247     HeapFree( GetProcessHeap(), 0, path );
248
249     if (res == -1) return 0;  /* not a symlink */
250     if (res < 4 || res >= sizeof(buffer)) return 0;
251     buffer[res] = 0;
252     if (strchr( buffer, '/' )) return 0;  /* contains a path, not valid */
253     if (strcmp( buffer + res - 3, ".so" )) return 0;  /* does not end in .so, not valid */
254     return 1;
255 }
256
257 /* load the list of available libraries from a given dir */
258 static void load_library_list_from_dir( HWND dialog, const char *dir_path, int check_subdirs )
259 {
260     char *buffer = NULL, name[256];
261     struct dirent *de;
262     DIR *dir = opendir( dir_path );
263
264     if (!dir) return;
265
266     if (check_subdirs)
267         buffer = HeapAlloc( GetProcessHeap(), 0, strlen(dir_path) + 2 * sizeof(name) + 10 );
268
269     while ((de = readdir( dir )))
270     {
271         size_t len = strlen(de->d_name);
272         if (len > sizeof(name)) continue;
273         if (len > 7 && !strcmp( de->d_name + len - 7, ".dll.so"))
274         {
275             if (is_16bit_dll( dir_path, de->d_name )) continue;  /* 16-bit dlls can't be configured */
276             len -= 7;
277             memcpy( name, de->d_name, len );
278             name[len] = 0;
279             /* skip dlls that should always be builtin */
280             if (is_builtin_only( name )) continue;
281             SendDlgItemMessageA( dialog, IDC_DLLCOMBO, CB_ADDSTRING, 0, (LPARAM)name );
282         }
283         else if (check_subdirs)
284         {
285             struct stat st;
286             if (is_builtin_only( de->d_name )) continue;
287             sprintf( buffer, "%s/%s/%s.dll.so", dir_path, de->d_name, de->d_name );
288             if (!stat( buffer, &st ))
289                 SendDlgItemMessageA( dialog, IDC_DLLCOMBO, CB_ADDSTRING, 0, (LPARAM)de->d_name );
290         }
291     }
292     closedir( dir );
293     HeapFree( GetProcessHeap(), 0, buffer );
294 }
295
296 /* load the list of available libraries */
297 static void load_library_list( HWND dialog )
298 {
299     unsigned int i = 0;
300     const char *path, *build_dir = wine_get_build_dir();
301     char item1[256], item2[256];
302     HCURSOR old_cursor = SetCursor( LoadCursor(0, IDC_WAIT) );
303
304     if (build_dir)
305     {
306         char *dir = HeapAlloc( GetProcessHeap(), 0, strlen(build_dir) + sizeof("/dlls") );
307         strcpy( dir, build_dir );
308         strcat( dir, "/dlls" );
309         load_library_list_from_dir( dialog, dir, TRUE );
310         HeapFree( GetProcessHeap(), 0, dir );
311     }
312
313     while ((path = wine_dll_enum_load_path( i++ )))
314         load_library_list_from_dir( dialog, path, FALSE );
315
316     /* get rid of duplicate entries */
317
318     SendDlgItemMessageA( dialog, IDC_DLLCOMBO, CB_GETLBTEXT, 0, (LPARAM)item1 );
319     i = 1;
320     while (SendDlgItemMessageA( dialog, IDC_DLLCOMBO, CB_GETLBTEXT, i, (LPARAM)item2 ) >= 0)
321     {
322         if (!strcmp( item1, item2 ))
323         {
324             SendDlgItemMessageA( dialog, IDC_DLLCOMBO, CB_DELETESTRING, i, 0 );
325         }
326         else
327         {
328             strcpy( item1, item2 );
329             i++;
330         }
331     }
332     SetCursor( old_cursor );
333 }
334
335 static void load_library_settings(HWND dialog)
336 {
337     char **overrides = enumerate_values(config_key, keypath("DllOverrides"));
338     char **p;
339     int sel, count = 0;
340
341     sel = SendDlgItemMessage(dialog, IDC_DLLS_LIST, LB_GETCURSEL, 0, 0);
342
343     WINE_TRACE("sel=%d\n", sel);
344
345     clear_settings(dialog);
346     
347     if (!overrides || *overrides == NULL)
348     {
349         set_controls_from_selection(dialog);
350         disable(IDC_DLLS_EDITDLL);
351         disable(IDC_DLLS_REMOVEDLL);
352         HeapFree(GetProcessHeap(), 0, overrides);
353         return;
354     }
355
356     enable(IDC_DLLS_EDITDLL);
357     enable(IDC_DLLS_REMOVEDLL);
358     
359     for (p = overrides; *p != NULL; p++)
360     {
361         int index;
362         char *str, *value;
363         const char *label;
364         struct dll *dll;
365
366         value = get_reg_key(config_key, keypath("DllOverrides"), *p, NULL);
367
368         label = mode_to_label(string_to_mode(value));
369         
370         str = HeapAlloc(GetProcessHeap(), 0, strlen(*p) + 2 + strlen(label) + 2);
371         strcpy(str, *p);
372         strcat(str, " (");
373         strcat(str, label);
374         strcat(str, ")");
375
376         dll = HeapAlloc(GetProcessHeap(), 0, sizeof(struct dll));
377         dll->name = *p;
378         dll->mode = string_to_mode(value);
379
380         index = SendDlgItemMessage(dialog, IDC_DLLS_LIST, LB_ADDSTRING, (WPARAM) -1, (LPARAM) str);
381         SendDlgItemMessage(dialog, IDC_DLLS_LIST, LB_SETITEMDATA, index, (LPARAM) dll);
382
383         HeapFree(GetProcessHeap(), 0, str);
384
385         count++;
386     }
387
388     HeapFree(GetProcessHeap(), 0, overrides);
389
390     /* restore the previous selection, if possible  */
391     if (sel >= count - 1) sel = count - 1;
392     else if (sel == -1) sel = 0;
393     
394     SendDlgItemMessage(dialog, IDC_DLLS_LIST, LB_SETCURSEL, sel, 0);
395
396     set_controls_from_selection(dialog);
397 }
398
399 /* Called when the application is initialized (cannot reinit!)  */
400 static void init_libsheet(HWND dialog)
401 {
402     /* clear the add dll controls  */
403     SendDlgItemMessage(dialog, IDC_DLLCOMBO, WM_SETTEXT, 1, (LPARAM) "");
404     load_library_list( dialog );
405     disable(IDC_DLLS_ADDDLL);
406 }
407
408 static void on_add_combo_change(HWND dialog)
409 {
410     char buffer[1024];
411     int sel, len;
412
413     SendDlgItemMessage(dialog, IDC_DLLCOMBO, WM_GETTEXT, sizeof(buffer), (LPARAM) buffer);
414     /* if lib was chosen from combobox, we receive an empty buffer, check manually */
415     sel=SendDlgItemMessage(dialog, IDC_DLLCOMBO, CB_GETCURSEL, 0, 0);
416     len=SendDlgItemMessage(dialog, IDC_DLLCOMBO, CB_GETLBTEXTLEN, sel, 0);
417
418     if (strlen(buffer)>0 || len>0)
419         enable(IDC_DLLS_ADDDLL)
420     else
421         disable(IDC_DLLS_ADDDLL);
422 }
423
424 static void set_dllmode(HWND dialog, DWORD id)
425 {
426     enum dllmode mode;
427     struct dll *dll;
428     int sel;
429     const char *str;
430
431     mode = id_to_mode(id);
432
433     sel = SendDlgItemMessage(dialog, IDC_DLLS_LIST, LB_GETCURSEL, 0, 0);
434     if (sel == -1) return;
435     
436     dll = (struct dll *) SendDlgItemMessage(dialog, IDC_DLLS_LIST, LB_GETITEMDATA, sel, 0);
437
438     str = mode_to_string(mode);
439     WINE_TRACE("Setting %s to %s\n", dll->name, str);
440     
441     SendMessage(GetParent(dialog), PSM_CHANGED, 0, 0);
442     set_reg_key(config_key, keypath("DllOverrides"), dll->name, str);
443
444     load_library_settings(dialog);  /* ... and refresh  */
445 }
446
447 static void on_add_click(HWND dialog)
448 {
449     static const char dotDll[] = ".dll";
450     char buffer[1024], *ptr;
451
452     ZeroMemory(buffer, sizeof(buffer));
453
454     SendDlgItemMessage(dialog, IDC_DLLCOMBO, WM_GETTEXT, sizeof(buffer), (LPARAM) buffer);
455     if (lstrlenA(buffer) >= sizeof(dotDll))
456     {
457         ptr = buffer + lstrlenA(buffer) - sizeof(dotDll) + 1;
458         if (!lstrcmpiA(ptr, dotDll))
459         {
460             WINE_TRACE("Stripping dll extension\n");
461             *ptr = '\0';
462         }
463     }
464
465     /* check if dll is in the builtin-only list */
466     if (!(ptr = strrchr( buffer, '\\' )))
467     {
468         ptr = buffer;
469         if (*ptr == '*') ptr++;
470     }
471     else ptr++;
472     if (is_builtin_only( ptr ))
473     {
474         MSGBOXPARAMSA params;
475         params.cbSize = sizeof(params);
476         params.hwndOwner = dialog;
477         params.hInstance = GetModuleHandleA( NULL );
478         params.lpszText = MAKEINTRESOURCEA( IDS_DLL_WARNING );
479         params.lpszCaption = MAKEINTRESOURCEA( IDS_DLL_WARNING_CAPTION );
480         params.dwStyle = MB_ICONWARNING | MB_YESNO;
481         params.lpszIcon = NULL;
482         params.dwContextHelpId = 0;
483         params.lpfnMsgBoxCallback = NULL;
484         params.dwLanguageId = 0;
485         if (MessageBoxIndirectA( &params ) != IDYES) return;
486     }
487
488     SendDlgItemMessage(dialog, IDC_DLLCOMBO, WM_SETTEXT, 0, (LPARAM) "");
489     disable(IDC_DLLS_ADDDLL);
490     
491     WINE_TRACE("Adding %s as native, builtin\n", buffer);
492     
493     SendMessage(GetParent(dialog), PSM_CHANGED, 0, 0);
494     set_reg_key(config_key, keypath("DllOverrides"), buffer, "native,builtin");
495
496     load_library_settings(dialog);
497
498     SendDlgItemMessage(dialog, IDC_DLLS_LIST, LB_SELECTSTRING, (WPARAM) 0, (LPARAM) buffer);
499
500     set_controls_from_selection(dialog);
501 }
502
503 static INT_PTR CALLBACK loadorder_dlgproc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
504 {
505     static WORD sel;
506
507     switch(uMsg) 
508     {
509     case WM_INITDIALOG:
510         CheckRadioButton(hwndDlg, IDC_RAD_BUILTIN, IDC_RAD_DISABLE, lParam);
511         sel = lParam;
512         return TRUE;
513
514     case WM_COMMAND:
515         if(HIWORD(wParam) != BN_CLICKED) break;
516         switch (LOWORD(wParam))
517         {
518         case IDC_RAD_BUILTIN:
519         case IDC_RAD_NATIVE:
520         case IDC_RAD_BUILTIN_NATIVE:
521         case IDC_RAD_NATIVE_BUILTIN:
522         case IDC_RAD_DISABLE:
523             sel = LOWORD(wParam);
524             return TRUE;
525         case IDOK:
526             EndDialog(hwndDlg, sel);
527             return TRUE;
528         case IDCANCEL:
529             EndDialog(hwndDlg, wParam);
530             return TRUE;
531         }
532     }
533     return FALSE;
534 }
535
536 static void on_edit_click(HWND hwnd)
537 {
538     INT_PTR ret; 
539     int index = SendDlgItemMessage(hwnd, IDC_DLLS_LIST, LB_GETCURSEL, 0, 0);
540     struct dll *dll;
541     DWORD id;
542
543     /* if no override is selected the edit button should be disabled... */
544     assert(index != -1);
545
546     dll = (struct dll *) SendDlgItemMessage(hwnd, IDC_DLLS_LIST, LB_GETITEMDATA, index, 0);
547     id = mode_to_id(dll->mode);
548     
549     ret = DialogBoxParam(0, MAKEINTRESOURCE(IDD_LOADORDER), hwnd, loadorder_dlgproc, id);
550     
551     if(ret != IDCANCEL)
552         set_dllmode(hwnd, ret);
553 }
554
555 static void on_remove_click(HWND dialog)
556 {
557     int sel = SendDlgItemMessage(dialog, IDC_DLLS_LIST, LB_GETCURSEL, 0, 0);
558     struct dll *dll;
559
560     if (sel == LB_ERR) return;
561     
562     dll = (struct dll *) SendDlgItemMessage(dialog, IDC_DLLS_LIST, LB_GETITEMDATA, sel, 0);
563     
564     SendDlgItemMessage(dialog, IDC_DLLS_LIST, LB_DELETESTRING, sel, 0);
565
566     SendMessage(GetParent(dialog), PSM_CHANGED, 0, 0);
567     set_reg_key(config_key, keypath("DllOverrides"), dll->name, NULL);
568
569     HeapFree(GetProcessHeap(), 0, dll->name);
570     HeapFree(GetProcessHeap(), 0, dll);
571
572     if (SendDlgItemMessage(dialog, IDC_DLLS_LIST, LB_GETCOUNT, 0, 0) > 0)
573         SendDlgItemMessage(dialog, IDC_DLLS_LIST, LB_SETCURSEL, max(sel - 1, 0), 0);
574     else
575     {
576         disable(IDC_DLLS_EDITDLL);
577         disable(IDC_DLLS_REMOVEDLL);
578     }
579
580     set_controls_from_selection(dialog);
581 }
582
583 INT_PTR CALLBACK
584 LibrariesDlgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
585 {
586         switch (uMsg)
587         {
588         case WM_INITDIALOG:
589                 init_libsheet(hDlg);
590                 break;
591         case WM_SHOWWINDOW:
592                 set_window_title(hDlg);
593                 break;
594         case WM_NOTIFY:
595                 switch (((LPNMHDR)lParam)->code) {
596                 case PSN_SETACTIVE:
597                     load_library_settings(hDlg);
598                     break;
599                 }
600                 break;
601         case WM_COMMAND:
602                 switch(HIWORD(wParam)) {
603
604                     /* FIXME: when the user hits enter in the DLL combo box we should invoke the add
605                      * add button, rather than the propsheet OK button. But I don't know how to do that!
606                      */
607                     
608                 case CBN_EDITCHANGE:
609                         if(LOWORD(wParam) == IDC_DLLCOMBO)
610                         {
611                             on_add_combo_change(hDlg);
612                             break;
613                         }
614
615                 case BN_CLICKED:
616                         switch(LOWORD(wParam)) {
617                         case IDC_DLLS_ADDDLL:
618                             on_add_click(hDlg);
619                             break;
620                         case IDC_DLLS_EDITDLL:
621                             on_edit_click(hDlg);
622                             break;
623                         case IDC_DLLS_REMOVEDLL:
624                             on_remove_click(hDlg);
625                             break;
626                         }
627                         break;
628                 case LBN_SELCHANGE:
629                         if(LOWORD(wParam) == IDC_DLLCOMBO)
630                             on_add_combo_change(hDlg);
631                         else
632                             set_controls_from_selection(hDlg);
633                         break;
634                 }
635                 break;
636         }
637
638         return 0;
639 }