libraries.c 17.6 KB
Newer Older
1 2 3 4
/*
 * WineCfg libraries tabsheet
 *
 * Copyright 2004 Robert van Herk
5
 * Copyright 2004 Mike Hearn
6 7 8 9 10 11 12 13 14 15 16 17 18
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * 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 24
#include "config.h"

25
#define NONAMELESSUNION
26
#define WIN32_LEAN_AND_MEAN
27 28
#include <windows.h>
#include <commdlg.h>
29
#include <wine/library.h>
30 31
#include <wine/debug.h>
#include <stdio.h>
32
#include <dirent.h>
33
#include <assert.h>
34
#include <stdlib.h>
35 36 37
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
38 39 40 41
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

42 43 44 45 46
#include "winecfg.h"
#include "resource.h"

WINE_DEFAULT_DEBUG_CHANNEL(winecfg);

47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
/* dlls that shouldn't be configured anything other than builtin; list must be sorted*/
static const char * const builtin_only[] =
{
    "advapi32",
    "capi2032",
    "dbghelp",
    "ddraw",
    "gdi32",
    "glu32",
    "icmp",
    "iphlpapi",
    "joystick.drv",
    "kernel32",
    "mswsock",
    "ntdll",
    "opengl32",
    "stdole2.tlb",
    "stdole32.tlb",
    "twain_32",
    "unicows",
    "user32",
    "vdmdbg",
    "w32skrnl",
    "winealsa.drv",
    "winearts.drv",
    "wineaudioio.drv",
    "wined3d",
    "winedos",
    "wineesd.drv",
    "winejack.drv",
    "winemp3.acm",
    "winenas.drv",
    "wineoss.drv",
    "wineps",
    "wineps.drv",
    "winex11.drv",
    "winmm",
    "wintab32",
    "wnaspi32",
    "wow32",
    "ws2_32",
    "wsock32",
};

91
enum dllmode
92
{
93 94
	BUILTIN_NATIVE,
	NATIVE_BUILTIN,
95
	BUILTIN,
96 97
	NATIVE,
	DISABLE,
98 99 100 101 102 103 104 105
	UNKNOWN /* Special value indicating an erronous DLL override mode */
};

struct dll
{
        char *name;
        enum dllmode mode;
};
106

107 108
/* Convert a registry string to a dllmode */
static enum dllmode string_to_mode(char *in)
109
{
110
    int i, j, len;
111
    char *out;
112
    enum dllmode res;
113

114 115
    len = strlen(in);
    out = HeapAlloc(GetProcessHeap(), 0, len);
116 117

    /* remove the spaces */
118 119 120
    for (i = j = 0; i <= len; ++i) {
        if (in[i] != ' ') {
            out[j++] = in[i];
121
        }
122
    }
123 124

    /* parse the string */
125 126 127 128 129 130 131 132 133 134
    res = UNKNOWN;
    if (strcmp(out, "builtin,native") == 0) res = BUILTIN_NATIVE;
    if (strcmp(out, "native,builtin") == 0) res = NATIVE_BUILTIN;
    if (strcmp(out, "builtin") == 0) res = BUILTIN;
    if (strcmp(out, "native") == 0) res = NATIVE;
    if (strcmp(out, "") == 0) res = DISABLE;

    HeapFree(GetProcessHeap(), 0, out);
    return res;
}
135

136
/* Convert a dllmode to a registry string. */
137
static const char* mode_to_string(enum dllmode mode)
138
{
139 140 141 142 143 144 145 146 147
    switch( mode )
    {
        case NATIVE: return "native";
        case BUILTIN: return "builtin";
        case NATIVE_BUILTIN: return "native,builtin";
        case BUILTIN_NATIVE: return "builtin,native";
        case DISABLE: return "";
        default: assert(FALSE); return "";
    }
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 154 155 156 157 158 159 160 161 162 163 164 165 166
    static char buffer[256];
    UINT id = 0;

    switch( mode )
    {
    case NATIVE: id = IDS_DLL_NATIVE; break;
    case BUILTIN: id = IDS_DLL_BUILTIN; break;
    case NATIVE_BUILTIN: id = IDS_DLL_NATIVE_BUILTIN; break;
    case BUILTIN_NATIVE: id = IDS_DLL_BUILTIN_NATIVE; break;
    case DISABLE: id = IDS_DLL_DISABLED; break;
    default: assert(FALSE);
    }
    if (!LoadStringA( GetModuleHandleA(NULL), id, buffer, sizeof(buffer) )) buffer[0] = 0;
    return buffer;
167
}
168

169 170 171
/* Convert a control id (IDC_ constant) to a dllmode */
static enum dllmode id_to_mode(DWORD id)
{
172 173 174 175 176 177 178 179 180
    switch( id )
    {
        case IDC_RAD_BUILTIN: return BUILTIN;
        case IDC_RAD_NATIVE: return NATIVE;
        case IDC_RAD_NATIVE_BUILTIN: return NATIVE_BUILTIN;
        case IDC_RAD_BUILTIN_NATIVE: return BUILTIN_NATIVE;
        case IDC_RAD_DISABLE: return DISABLE;
        default: assert( FALSE ); return 0; /* should not be reached  */
    }
181 182 183 184 185
}

/* Convert a dllmode to a control id (IDC_ constant) */
static DWORD mode_to_id(enum dllmode mode)
{
186 187 188 189 190 191 192 193 194
    switch( mode )
    {
        case BUILTIN: return IDC_RAD_BUILTIN;
        case NATIVE: return IDC_RAD_NATIVE;
        case NATIVE_BUILTIN: return IDC_RAD_NATIVE_BUILTIN;
        case BUILTIN_NATIVE: return IDC_RAD_BUILTIN_NATIVE;
        case DISABLE: return IDC_RAD_DISABLE;
        default: assert( FALSE ); return 0; /* should not be reached  */
    }
195 196
}

197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
/* helper for is_builtin_only */
static int compare_dll( const void *ptr1, const void *ptr2 )
{
    const char * const *name1 = ptr1;
    const char * const *name2 = ptr2;
    return strcmp( *name1, *name2 );
}

/* check if dll is recommended as builtin only */
static inline int is_builtin_only( const char *name )
{
    return bsearch( &name, builtin_only, sizeof(builtin_only)/sizeof(builtin_only[0]),
                    sizeof(builtin_only[0]), compare_dll ) != NULL;
}

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 220
    int count = SendDlgItemMessage(dialog, IDC_DLLS_LIST, LB_GETCOUNT, 0, 0);
    int i;
221

222 223 224 225 226 227 228 229 230 231 232
    WINE_TRACE("count=%d\n", count);
    
    for (i = 0; i < count; i++)
    {
        struct dll *dll = (struct dll *) SendDlgItemMessage(dialog, IDC_DLLS_LIST, LB_GETITEMDATA, 0, 0);
        
        SendDlgItemMessage(dialog, IDC_DLLS_LIST, LB_DELETESTRING, 0, 0);
        
        HeapFree(GetProcessHeap(), 0, dll->name);
        HeapFree(GetProcessHeap(), 0, dll);
    }
233 234
}

235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
/* check if a given dll is 16-bit */
static int is_16bit_dll( const char *dir, const char *name )
{
    char buffer[64];
    int res;
    size_t len = strlen(dir) + strlen(name) + 2;
    char *path = HeapAlloc( GetProcessHeap(), 0, len );

    strcpy( path, dir );
    strcat( path, "/" );
    strcat( path, name );
    res = readlink( path, buffer, sizeof(buffer) );
    HeapFree( GetProcessHeap(), 0, path );

    if (res == -1) return 0;  /* not a symlink */
    if (res < 4 || res >= sizeof(buffer)) return 0;
    buffer[res] = 0;
    if (strchr( buffer, '/' )) return 0;  /* contains a path, not valid */
    if (strcmp( buffer + res - 3, ".so" )) return 0;  /* does not end in .so, not valid */
    return 1;
}

/* 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 262 263 264 265
    struct dirent *de;
    DIR *dir = opendir( dir_path );

    if (!dir) return;

266 267 268
    if (check_subdirs)
        buffer = HeapAlloc( GetProcessHeap(), 0, strlen(dir_path) + 2 * sizeof(name) + 10 );

269 270 271
    while ((de = readdir( dir )))
    {
        size_t len = strlen(de->d_name);
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
        if (len > sizeof(name)) continue;
        if (len > 7 && !strcmp( de->d_name + len - 7, ".dll.so"))
        {
            if (is_16bit_dll( dir_path, de->d_name )) continue;  /* 16-bit dlls can't be configured */
            len -= 7;
            memcpy( name, de->d_name, len );
            name[len] = 0;
            /* skip dlls that should always be builtin */
            if (is_builtin_only( name )) continue;
            SendDlgItemMessageA( dialog, IDC_DLLCOMBO, CB_ADDSTRING, 0, (LPARAM)name );
        }
        else if (check_subdirs)
        {
            struct stat st;
            if (is_builtin_only( de->d_name )) continue;
            sprintf( buffer, "%s/%s/%s.dll.so", dir_path, de->d_name, de->d_name );
            if (!stat( buffer, &st ))
                SendDlgItemMessageA( dialog, IDC_DLLCOMBO, CB_ADDSTRING, 0, (LPARAM)de->d_name );
        }
291 292
    }
    closedir( dir );
293
    HeapFree( GetProcessHeap(), 0, buffer );
294 295 296 297 298 299
}

/* load the list of available libraries */
static void load_library_list( HWND dialog )
{
    unsigned int i = 0;
300
    const char *path, *build_dir = wine_get_build_dir();
301
    char item1[256], item2[256];
302 303 304 305 306 307 308 309 310 311
    HCURSOR old_cursor = SetCursor( LoadCursor(0, IDC_WAIT) );

    if (build_dir)
    {
        char *dir = HeapAlloc( GetProcessHeap(), 0, strlen(build_dir) + sizeof("/dlls") );
        strcpy( dir, build_dir );
        strcat( dir, "/dlls" );
        load_library_list_from_dir( dialog, dir, TRUE );
        HeapFree( GetProcessHeap(), 0, dir );
    }
312 313

    while ((path = wine_dll_enum_load_path( i++ )))
314
        load_library_list_from_dir( dialog, path, FALSE );
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331

    /* get rid of duplicate entries */

    SendDlgItemMessageA( dialog, IDC_DLLCOMBO, CB_GETLBTEXT, 0, (LPARAM)item1 );
    i = 1;
    while (SendDlgItemMessageA( dialog, IDC_DLLCOMBO, CB_GETLBTEXT, i, (LPARAM)item2 ) >= 0)
    {
        if (!strcmp( item1, item2 ))
        {
            SendDlgItemMessageA( dialog, IDC_DLLCOMBO, CB_DELETESTRING, i, 0 );
        }
        else
        {
            strcpy( item1, item2 );
            i++;
        }
    }
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 339
    char **p;
    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 346 347 348 349
    clear_settings(dialog);
    
    if (!overrides || *overrides == NULL)
    {
        set_controls_from_selection(dialog);
350
        disable(IDC_DLLS_EDITDLL);
351 352 353 354
        disable(IDC_DLLS_REMOVEDLL);
        HeapFree(GetProcessHeap(), 0, overrides);
        return;
    }
355

356
    enable(IDC_DLLS_EDITDLL);
357 358 359 360 361
    enable(IDC_DLLS_REMOVEDLL);
    
    for (p = overrides; *p != NULL; p++)
    {
        int index;
362 363
        char *str, *value;
        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 371 372 373 374
        
        str = HeapAlloc(GetProcessHeap(), 0, strlen(*p) + 2 + strlen(label) + 2);
        strcpy(str, *p);
        strcat(str, " (");
        strcat(str, label);
        strcat(str, ")");
375

376 377
        dll = HeapAlloc(GetProcessHeap(), 0, sizeof(struct dll));
        dll->name = *p;
378
        dll->mode = string_to_mode(value);
379

380 381
        index = SendDlgItemMessage(dialog, IDC_DLLS_LIST, LB_ADDSTRING, (WPARAM) -1, (LPARAM) str);
        SendDlgItemMessage(dialog, IDC_DLLS_LIST, LB_SETITEMDATA, index, (LPARAM) dll);
382

383
        HeapFree(GetProcessHeap(), 0, str);
384

385 386
        count++;
    }
387

388
    HeapFree(GetProcessHeap(), 0, overrides);
389

390 391 392 393 394
    /* restore the previous selection, if possible  */
    if (sel >= count - 1) sel = count - 1;
    else if (sel == -1) sel = 0;
    
    SendDlgItemMessage(dialog, IDC_DLLS_LIST, LB_SETCURSEL, sel, 0);
395

396
    set_controls_from_selection(dialog);
397 398
}

399 400
/* Called when the application is initialized (cannot reinit!)  */
static void init_libsheet(HWND dialog)
401
{
402 403
    /* clear the add dll controls  */
    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 411 412 413 414 415 416 417
    char buffer[1024];

    SendDlgItemMessage(dialog, IDC_DLLCOMBO, WM_GETTEXT, sizeof(buffer), (LPARAM) buffer);

    if (strlen(buffer))
        enable(IDC_DLLS_ADDDLL)
    else
        disable(IDC_DLLS_ADDDLL);
418 419
}

420
static void set_dllmode(HWND dialog, DWORD id)
421
{
422 423 424
    enum dllmode mode;
    struct dll *dll;
    int sel;
425
    const char *str;
426

427
    mode = id_to_mode(id);
428 429 430 431 432 433

    sel = SendDlgItemMessage(dialog, IDC_DLLS_LIST, LB_GETCURSEL, 0, 0);
    if (sel == -1) return;
    
    dll = (struct dll *) SendDlgItemMessage(dialog, IDC_DLLS_LIST, LB_GETITEMDATA, sel, 0);

434
    str = mode_to_string(mode);
435 436
    WINE_TRACE("Setting %s to %s\n", dll->name, str);
    
437
    SendMessage(GetParent(dialog), PSM_CHANGED, 0, 0);
438
    set_reg_key(config_key, keypath("DllOverrides"), dll->name, str);
439 440

    load_library_settings(dialog);  /* ... and refresh  */
441 442
}

443
static void on_add_click(HWND dialog)
444
{
445 446
    static const char dotDll[] = ".dll";
    char buffer[1024], *ptr;
447 448 449 450

    ZeroMemory(buffer, sizeof(buffer));

    SendDlgItemMessage(dialog, IDC_DLLCOMBO, WM_GETTEXT, sizeof(buffer), (LPARAM) buffer);
451 452 453 454 455 456 457 458 459
    if (lstrlenA(buffer) >= sizeof(dotDll))
    {
        ptr = buffer + lstrlenA(buffer) - sizeof(dotDll) + 1;
        if (!lstrcmpiA(ptr, dotDll))
        {
            WINE_TRACE("Stripping dll extension\n");
            *ptr = '\0';
        }
    }
460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483

    /* check if dll is in the builtin-only list */
    if (!(ptr = strrchr( buffer, '\\' )))
    {
        ptr = buffer;
        if (*ptr == '*') ptr++;
    }
    else ptr++;
    if (is_builtin_only( ptr ))
    {
        MSGBOXPARAMSA params;
        params.cbSize = sizeof(params);
        params.hwndOwner = dialog;
        params.hInstance = GetModuleHandleA( NULL );
        params.lpszText = MAKEINTRESOURCEA( IDS_DLL_WARNING );
        params.lpszCaption = MAKEINTRESOURCEA( IDS_DLL_WARNING_CAPTION );
        params.dwStyle = MB_ICONWARNING | MB_YESNO;
        params.lpszIcon = NULL;
        params.dwContextHelpId = 0;
        params.lpfnMsgBoxCallback = NULL;
        params.dwLanguageId = 0;
        if (MessageBoxIndirectA( &params ) != IDYES) return;
    }

484 485 486 487 488
    SendDlgItemMessage(dialog, IDC_DLLCOMBO, WM_SETTEXT, 0, (LPARAM) "");
    disable(IDC_DLLS_ADDDLL);
    
    WINE_TRACE("Adding %s as native, builtin", buffer);
    
489
    SendMessage(GetParent(dialog), PSM_CHANGED, 0, 0);
490
    set_reg_key(config_key, keypath("DllOverrides"), buffer, "native,builtin");
491 492 493 494 495 496

    load_library_settings(dialog);

    SendDlgItemMessage(dialog, IDC_DLLS_LIST, LB_SELECTSTRING, (WPARAM) 0, (LPARAM) buffer);

    set_controls_from_selection(dialog);
497 498
}

499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550
static INT_PTR CALLBACK loadorder_dlgproc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    static WORD sel;

    switch(uMsg) 
    {
    case WM_INITDIALOG:
        CheckRadioButton(hwndDlg, IDC_RAD_BUILTIN, IDC_RAD_DISABLE, lParam);
        sel = lParam;
        return TRUE;

    case WM_COMMAND:
        if(HIWORD(wParam) != BN_CLICKED) break;
        switch (LOWORD(wParam))
        {
        case IDC_RAD_BUILTIN:
        case IDC_RAD_NATIVE:
        case IDC_RAD_BUILTIN_NATIVE:
        case IDC_RAD_NATIVE_BUILTIN:
        case IDC_RAD_DISABLE:
            sel = LOWORD(wParam);
            return TRUE;
        case IDOK:
            EndDialog(hwndDlg, sel);
            return TRUE;
        case IDCANCEL:
            EndDialog(hwndDlg, wParam);
            return TRUE;
        }
    }
    return FALSE;
}

static void on_edit_click(HWND hwnd)
{
    INT_PTR ret; 
    int index = SendDlgItemMessage(hwnd, IDC_DLLS_LIST, LB_GETCURSEL, 0, 0);
    struct dll *dll;
    DWORD id;

    /* if no override is selected the edit button should be disabled... */
    assert(index != -1);

    dll = (struct dll *) SendDlgItemMessage(hwnd, IDC_DLLS_LIST, LB_GETITEMDATA, index, 0);
    id = mode_to_id(dll->mode);
    
    ret = DialogBoxParam(0, MAKEINTRESOURCE(IDD_LOADORDER), hwnd, loadorder_dlgproc, id);
    
    if(ret != IDCANCEL)
        set_dllmode(hwnd, ret);
}

551
static void on_remove_click(HWND dialog)
552
{
553 554 555 556 557 558 559 560 561
    int sel = SendDlgItemMessage(dialog, IDC_DLLS_LIST, LB_GETCURSEL, 0, 0);
    struct dll *dll;

    if (sel == LB_ERR) return;
    
    dll = (struct dll *) SendDlgItemMessage(dialog, IDC_DLLS_LIST, LB_GETITEMDATA, sel, 0);
    
    SendDlgItemMessage(dialog, IDC_DLLS_LIST, LB_DELETESTRING, sel, 0);

562
    SendMessage(GetParent(dialog), PSM_CHANGED, 0, 0);
563
    set_reg_key(config_key, keypath("DllOverrides"), dll->name, NULL);
564 565 566 567 568 569 570

    HeapFree(GetProcessHeap(), 0, dll->name);
    HeapFree(GetProcessHeap(), 0, dll);

    if (SendDlgItemMessage(dialog, IDC_DLLS_LIST, LB_GETCOUNT, 0, 0) > 0)
        SendDlgItemMessage(dialog, IDC_DLLS_LIST, LB_SETCURSEL, max(sel - 1, 0), 0);
    else
571 572
    {
        disable(IDC_DLLS_EDITDLL);
573
        disable(IDC_DLLS_REMOVEDLL);
574
    }
575 576

    set_controls_from_selection(dialog);
577 578 579 580 581 582 583 584
}

INT_PTR CALLBACK
LibrariesDlgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{
	case WM_INITDIALOG:
585 586 587 588 589
		init_libsheet(hDlg);
		break;
        case WM_SHOWWINDOW:
                set_window_title(hDlg);
                break;
590 591
	case WM_NOTIFY:
		switch (((LPNMHDR)lParam)->code) {
592 593 594
                case PSN_SETACTIVE:
                    load_library_settings(hDlg);
                    break;
595 596 597 598
		}
		break;
	case WM_COMMAND:
		switch(HIWORD(wParam)) {
599 600 601 602 603 604 605 606 607 608 609

                    /* FIXME: when the user hits enter in the DLL combo box we should invoke the add
                     * add button, rather than the propsheet OK button. But I don't know how to do that!
                     */
                    
                case CBN_EDITCHANGE:
                        if(LOWORD(wParam) == IDC_DLLCOMBO)
                        {
                            on_add_combo_change(hDlg);
                            break;
                        }
610

611 612 613
		case BN_CLICKED:
			switch(LOWORD(wParam)) {
			case IDC_DLLS_ADDDLL:
614 615
                            on_add_click(hDlg);
                            break;
616 617 618
			case IDC_DLLS_EDITDLL:
			    on_edit_click(hDlg);
			    break;
619
			case IDC_DLLS_REMOVEDLL:
620 621
                            on_remove_click(hDlg);
                            break;
622 623
			}
			break;
624
                case LBN_SELCHANGE:
625 626 627 628
                        if(LOWORD(wParam) == IDC_DLLCOMBO)
                            on_add_combo_change(hDlg);
                        else
                            set_controls_from_selection(hDlg);
629
                        break;
630 631 632 633 634 635
		}
		break;
	}

	return 0;
}