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