diff options
Diffstat (limited to 'src/gallium/state_trackers/d3d1x/winedlls')
16 files changed, 436 insertions, 0 deletions
diff --git a/src/gallium/state_trackers/d3d1x/winedlls/Makefile b/src/gallium/state_trackers/d3d1x/winedlls/Makefile new file mode 100644 index 0000000000..c7e51b2fdd --- /dev/null +++ b/src/gallium/state_trackers/d3d1x/winedlls/Makefile @@ -0,0 +1,11 @@ +SUBDIRS=dxgi d3d10 d3d10_1 d3d11 + +all: + @for dir in $(SUBDIRS) ; do $(MAKE) -C "$$dir" || exit $?; done + +clean: + rm -f `find . -name \*.[oa]` + rm -f `find . -name depend` + +install: + sudo install */*.dll.so /usr/lib/wine diff --git a/src/gallium/state_trackers/d3d1x/winedlls/Makefile.wine b/src/gallium/state_trackers/d3d1x/winedlls/Makefile.wine new file mode 100644 index 0000000000..c9a06876c4 --- /dev/null +++ b/src/gallium/state_trackers/d3d1x/winedlls/Makefile.wine @@ -0,0 +1,23 @@ +TOP=../../../../../.. +D3D1X=../.. +include $(TOP)/configs/current +CFLAGS=$(CXXFLAGS) + +all: lib$(LIBNAME).def lib$(LIBNAME).cross.a $(LIBNAME).dll.so + +%.dll.fake: %.spec $(OBJECTS) version.res + wineg++ -m32 -fasynchronous-unwind-tables -shared $^ -o $@ $(LDADD) + +%.dll.so: %.spec $(OBJECTS) version.res + wineg++ -m32 -fasynchronous-unwind-tables -shared $^ -o $@ $(LDADD) + +lib%.def: %.spec + winebuild -w --def -o $@ --export $< + +lib%.cross.a: %.spec + winebuild -m32 -b i586-mingw32msvc -w --implib -o $@ --export $< + +version.res: version.rc + wrc --nostdinc -I. -I. -I../../include -I../../include -D__WINESRC__ -fo$@ $^ + +include ../../../../Makefile.template diff --git a/src/gallium/state_trackers/d3d1x/winedlls/d3d10/Makefile b/src/gallium/state_trackers/d3d1x/winedlls/d3d10/Makefile new file mode 100644 index 0000000000..0ea5ffea0d --- /dev/null +++ b/src/gallium/state_trackers/d3d1x/winedlls/d3d10/Makefile @@ -0,0 +1,6 @@ +LIBNAME=d3d10 +LIBRARY_INCLUDES=-I$(D3D1X)/gd3dapi -I$(D3D1X)/d3dapi -I$(D3D1X)/w32api +OBJECTS=../../dxgid3d10/libdxgid3d10.a ../../gd3d10/libgd3d10.a ../../gd3d1x/libgd3d1x.a ../../d3d1xshader/libd3d1xshader.a ../../d3d1xstutil/libd3d1xstutil.a ../../../../auxiliary/libgallium.a +LDADD=-L../dxgi -ldxgi -ldl + +include ../Makefile.wine diff --git a/src/gallium/state_trackers/d3d1x/winedlls/d3d10/d3d10.spec b/src/gallium/state_trackers/d3d1x/winedlls/d3d10/d3d10.spec new file mode 100644 index 0000000000..4a68ab58db --- /dev/null +++ b/src/gallium/state_trackers/d3d1x/winedlls/d3d10/d3d10.spec @@ -0,0 +1,33 @@ +@ stub D3D10CompileEffectFromMemory +@ stub D3D10CompileShader +@ stdcall D3D10CreateBlob(long ptr) +@ stdcall D3D10CreateDevice(ptr long ptr long long ptr) +@ stdcall D3D10CreateDeviceAndSwapChain(ptr long ptr long long ptr ptr ptr) +@ stub D3D10CreateEffectFromMemory +@ stub D3D10CreateEffectPoolFromMemory +@ stub D3D10CreateStateBlock +@ stub D3D10DisassembleEffect +@ stub D3D10DisassembleShader +@ stdcall D3D10GetGeometryShaderProfile(ptr) +@ stdcall D3D10GetInputAndOutputSignatureBlob(ptr long ptr) +@ stdcall D3D10GetInputSignatureBlob(ptr long ptr) +@ stdcall D3D10GetOutputSignatureBlob(ptr long ptr) +@ stdcall D3D10GetPixelShaderProfile(ptr) +@ stub D3D10GetShaderDebugInfo +@ stub D3D10GetVersion +@ stdcall D3D10GetVertexShaderProfile(ptr) +@ stub D3D10PreprocessShader +@ stub D3D10ReflectShader +@ stub D3D10RegisterLayers +@ stub D3D10StateBlockMaskDifference +@ stub D3D10StateBlockMaskDisableAll +@ stub D3D10StateBlockMaskDisableCapture +@ stub D3D10StateBlockMaskEnableAll +@ stub D3D10StateBlockMaskEnableCapture +@ stub D3D10StateBlockMaskGetSetting +@ stub D3D10StateBlockMaskIntersect +@ stub D3D10StateBlockMaskUnion + +@ stdcall D3D10CreateDevice1(ptr long ptr long long long ptr) +@ stdcall D3D10CreateDeviceAndSwapChain1(ptr long ptr long long long ptr ptr ptr) + diff --git a/src/gallium/state_trackers/d3d1x/winedlls/d3d10/version.rc b/src/gallium/state_trackers/d3d1x/winedlls/d3d10/version.rc new file mode 100644 index 0000000000..0575ab8b57 --- /dev/null +++ b/src/gallium/state_trackers/d3d1x/winedlls/d3d10/version.rc @@ -0,0 +1,3 @@ +#define FILENAME "d3d10" +#define NAME "D3D10" +#include "../version.rc.h" diff --git a/src/gallium/state_trackers/d3d1x/winedlls/d3d10_1/Makefile b/src/gallium/state_trackers/d3d1x/winedlls/d3d10_1/Makefile new file mode 100644 index 0000000000..60cdca1af9 --- /dev/null +++ b/src/gallium/state_trackers/d3d1x/winedlls/d3d10_1/Makefile @@ -0,0 +1,6 @@ +LIBNAME=d3d10_1 +LIBRARY_INCLUDES= +OBJECTS= +LDADD=-L../d3d10 -ld3d10 + +include ../Makefile.wine diff --git a/src/gallium/state_trackers/d3d1x/winedlls/d3d10_1/d3d10_1.spec b/src/gallium/state_trackers/d3d1x/winedlls/d3d10_1/d3d10_1.spec new file mode 100644 index 0000000000..993e4bbe01 --- /dev/null +++ b/src/gallium/state_trackers/d3d1x/winedlls/d3d10_1/d3d10_1.spec @@ -0,0 +1,29 @@ +@ stub D3D10CompileShader +@ stdcall D3D10CreateBlob(long ptr) d3d10.D3D10CreateBlob +@ stdcall D3D10CreateDevice1(ptr long ptr long long long ptr) d3d10.D3D10CreateDevice1 +@ stdcall D3D10CreateDeviceAndSwapChain1(ptr long ptr long long long ptr ptr ptr) d3d10.D3D10CreateDeviceAndSwapChain1 +@ stub D3D10CreateEffectFromMemory +@ stub D3D10CreateEffectPoolFromMemory +@ stub D3D10CreateStateBlock +@ stub D3D10DisassembleEffect +@ stub D3D10DisassembleShader +@ stdcall D3D10GetGeometryShaderProfile(ptr) d3d10.D3D10GetGeometryShaderProfile +@ stdcall D3D10GetInputAndOutputSignatureBlob(ptr long ptr) d3d10.D3D10GetInputAndOutputSignatureBlob +@ stdcall D3D10GetInputSignatureBlob(ptr long ptr) d3d10.D3D10GetInputSignatureBlob +@ stdcall D3D10GetOutputSignatureBlob(ptr long ptr) d3d10.D3D10GetOutputSignatureBlob +@ stdcall D3D10GetPixelShaderProfile(ptr) d3d10.D3D10GetPixelShaderProfile +@ stub D3D10GetShaderDebugInfo +@ stub D3D10GetVersion +@ stdcall D3D10GetVertexShaderProfile(ptr) d3d10.D3D10GetVertexShaderProfile +@ stub D3D10PreprocessShader +@ stub D3D10ReflectShader +@ stub D3D10RegisterLayers +@ stub D3D10StateBlockMaskDifference +@ stub D3D10StateBlockMaskDisableAll +@ stub D3D10StateBlockMaskDisableCapture +@ stub D3D10StateBlockMaskEnableAll +@ stub D3D10StateBlockMaskEnableCapture +@ stub D3D10StateBlockMaskGetSetting +@ stub D3D10StateBlockMaskIntersect +@ stub D3D10StateBlockMaskUnion + diff --git a/src/gallium/state_trackers/d3d1x/winedlls/d3d10_1/version.rc b/src/gallium/state_trackers/d3d1x/winedlls/d3d10_1/version.rc new file mode 100644 index 0000000000..0575ab8b57 --- /dev/null +++ b/src/gallium/state_trackers/d3d1x/winedlls/d3d10_1/version.rc @@ -0,0 +1,3 @@ +#define FILENAME "d3d10" +#define NAME "D3D10" +#include "../version.rc.h" diff --git a/src/gallium/state_trackers/d3d1x/winedlls/d3d11/Makefile b/src/gallium/state_trackers/d3d1x/winedlls/d3d11/Makefile new file mode 100644 index 0000000000..b8d992e243 --- /dev/null +++ b/src/gallium/state_trackers/d3d1x/winedlls/d3d11/Makefile @@ -0,0 +1,6 @@ +LIBNAME=d3d11 +LIBRARY_INCLUDES=-I$(D3D1X)/gd3dapi -I$(D3D1X)/d3dapi -I$(D3D1X)/w32api +OBJECTS=../../dxgid3d11/libdxgid3d11.a ../../gd3d11/libgd3d11.a ../../gd3d1x/libgd3d1x.a ../../d3d1xshader/libd3d1xshader.a ../../d3d1xstutil/libd3d1xstutil.a ../../../../auxiliary/libgallium.a +LDADD=-L../dxgi -ldxgi -ldl + +include ../Makefile.wine diff --git a/src/gallium/state_trackers/d3d1x/winedlls/d3d11/d3d11.spec b/src/gallium/state_trackers/d3d1x/winedlls/d3d11/d3d11.spec new file mode 100644 index 0000000000..1d2e0c5b93 --- /dev/null +++ b/src/gallium/state_trackers/d3d1x/winedlls/d3d11/d3d11.spec @@ -0,0 +1,6 @@ +@ stub D3D11CoreCreateDevice +@ stub D3D11CoreCreateLayeredDevice +@ stub D3D11CoreGetLayeredDeviceSize +@ stub D3D11CoreRegisterLayers +@ stdcall D3D11CreateDevice(ptr long ptr long ptr long long ptr ptr ptr) +@ stdcall D3D11CreateDeviceAndSwapChain(ptr long ptr long ptr long long ptr ptr ptr ptr ptr) diff --git a/src/gallium/state_trackers/d3d1x/winedlls/d3d11/version.rc b/src/gallium/state_trackers/d3d1x/winedlls/d3d11/version.rc new file mode 100644 index 0000000000..a398678333 --- /dev/null +++ b/src/gallium/state_trackers/d3d1x/winedlls/d3d11/version.rc @@ -0,0 +1,3 @@ +#define FILENAME "d3d11" +#define NAME "D3D11" +#include "../version.rc.h" diff --git a/src/gallium/state_trackers/d3d1x/winedlls/dxgi/Makefile b/src/gallium/state_trackers/d3d1x/winedlls/dxgi/Makefile new file mode 100644 index 0000000000..650bdc84d5 --- /dev/null +++ b/src/gallium/state_trackers/d3d1x/winedlls/dxgi/Makefile @@ -0,0 +1,6 @@ +LIBNAME=dxgi +LIBRARY_INCLUDES=-I$(D3D1X)/gd3dapi -I$(D3D1X)/d3dapi -I$(D3D1X)/w32api +OBJECTS=dxgi_dll.o ../../dxgi/libdxgi.a ../../d3d1xstutil/libd3d1xstutil.a ../../../egl/libegl.a ../../../../auxiliary/libgallium.a ../../../../winsys/sw/xlib/libws_xlib.a +LDADD=-lgdi32 -lEGL -lXfixes -lX11 -ldrm -ldl -lXext + +include ../Makefile.wine diff --git a/src/gallium/state_trackers/d3d1x/winedlls/dxgi/dxgi.spec b/src/gallium/state_trackers/d3d1x/winedlls/dxgi/dxgi.spec new file mode 100644 index 0000000000..65a91a4583 --- /dev/null +++ b/src/gallium/state_trackers/d3d1x/winedlls/dxgi/dxgi.spec @@ -0,0 +1,4 @@ +@ stdcall CreateDXGIFactory(ptr ptr) +@ stdcall CreateDXGIFactory1(ptr ptr) +@ stub DXGID3D10CreateDevice +@ stub DXGID3D10RegisterLayers diff --git a/src/gallium/state_trackers/d3d1x/winedlls/dxgi/dxgi_dll.c b/src/gallium/state_trackers/d3d1x/winedlls/dxgi/dxgi_dll.c new file mode 100644 index 0000000000..43e2980afd --- /dev/null +++ b/src/gallium/state_trackers/d3d1x/winedlls/dxgi/dxgi_dll.c @@ -0,0 +1,264 @@ +/************************************************************************** + * + * Copyright 2010 Luca Barbieri + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#include <windows.h> +#include <winnt.h> +#include <X11/Xlib.h> +#include <galliumdxgi.h> + +#define DLL_WINE_PREATTACH 8 + +#define X11DRV_ESCAPE 6789 +#define X11DRV_GET_DISPLAY 0 +#define X11DRV_GET_DRAWABLE 1 + +/* Wine works in this way: wineserver stores the all window positions + * in (somewhat fictitious) "screen coordinates", and does not itself + * interact with X11. + * + * Instead, it is the responsibliity of the owner of the X window to + * handle ConfigureNotify and inform wineserver that the window + * moved. + * + * This means that we can freely look at window positions non-atomically, + * since they won't get updated until we return and the application + * processes the Win32 message queue. + * + * Of course, if this thread doesn't own the window, we are screwed. + * + * It might be a good idea to integrate this code in winex11.drv. + */ + +struct WineDXGIBackend +{ + const IGalliumDXGIBackendVtbl *vtbl_IGalliumDXGIBackend; + LONG ref; +}; + +static HRESULT STDMETHODCALLTYPE WineDXGIBackend_BeginPresent( + IGalliumDXGIBackend* This, + HWND hwnd, + void** ppresent_cookie, + void** pwindow, + RECT* prect, + RGNDATA** prgndata, + BOOL* ppreserve_aspect_ratio) +{ + /* this is the parent HWND which actually has an X11 window associated */ + HWND x11_hwnd; + HDC hdc; + RECT client_rect; + POINT x11_hwnd_origin_from_screen; + Drawable drawable; + POINT hwnd_origin_from_screen; + HRGN hrgn; + unsigned code = X11DRV_GET_DRAWABLE; + unsigned rgndata_size; + RGNDATA* rgndata; + RECT rgn_box; + int rgn_box_type; + + hdc = GetDC(hwnd); + GetDCOrgEx(hdc, &hwnd_origin_from_screen); + hrgn = CreateRectRgn(0, 0, 0, 0); + GetRandomRgn(hdc, hrgn, SYSRGN); + rgn_box_type = GetRgnBox(hrgn, &rgn_box); + + /* the coordinate system differs depending on whether Wine is + * pretending to be Win9x or WinNT, so match that behavior. + */ + if (!(GetVersion() & 0x80000000)) + OffsetRgn(hrgn, -hwnd_origin_from_screen.x, -hwnd_origin_from_screen.y); + ReleaseDC(hwnd, hdc); + + if(rgn_box_type == NULLREGION) + { + DeleteObject(hrgn); + return DXGI_STATUS_OCCLUDED; + } + + rgndata_size = GetRegionData(hrgn, 0, NULL); + rgndata = HeapAlloc(GetProcessHeap(), 0, rgndata_size); + GetRegionData(hrgn, rgndata_size, rgndata); + DeleteObject(hrgn); + *prgndata = rgndata; + + x11_hwnd = GetAncestor(hwnd, GA_ROOT); + hdc = GetDC(x11_hwnd); + ExtEscape(hdc, X11DRV_ESCAPE, sizeof(code), (LPSTR)&code, sizeof(drawable), (LPTSTR)&drawable); + + GetDCOrgEx(hdc, &x11_hwnd_origin_from_screen); + ReleaseDC(x11_hwnd, hdc); + + *pwindow = (void*)drawable; + GetClientRect(hwnd, &client_rect); + + prect->left = hwnd_origin_from_screen.x - x11_hwnd_origin_from_screen.x; + prect->top = hwnd_origin_from_screen.y - x11_hwnd_origin_from_screen.y; + + prect->right = prect->left + client_rect.right; + prect->bottom = prect->top + client_rect.bottom; + + // Windows doesn't preserve the aspect ratio + // TODO: maybe let the user turn this on somehow + *ppreserve_aspect_ratio = FALSE; + + *ppresent_cookie = rgndata; + + // TODO: check for errors and return them + return S_OK; +} + +static void STDMETHODCALLTYPE WineDXGIBackend_EndPresent( + IGalliumDXGIBackend* This, + HWND hwnd, + void *present_cookie) +{ + HeapFree(GetProcessHeap(), 0, present_cookie); +} + +static HRESULT STDMETHODCALLTYPE WineDXGIBackend_TestPresent( + IGalliumDXGIBackend* This, + HWND hwnd) +{ + HDC hdc; + HRGN hrgn; + RECT rgn_box; + int rgn_box_type; + + // TODO: is there a simpler way to check this? + hdc = GetDC(hwnd); + hrgn = CreateRectRgn(0, 0, 0, 0); + GetRandomRgn(hdc, hrgn, SYSRGN); + rgn_box_type = GetRgnBox(hrgn, &rgn_box); + DeleteObject(hrgn); + ReleaseDC(hwnd, hdc); + + return rgn_box_type == NULLREGION ? DXGI_STATUS_OCCLUDED : S_OK; +} + +static HRESULT STDMETHODCALLTYPE WineDXGIBackend_GetPresentSize( + IGalliumDXGIBackend* This, + HWND hwnd, + unsigned* width, + unsigned* height) +{ + RECT client_rect; + GetClientRect(hwnd, &client_rect); + *width = client_rect.right - client_rect.left; + *height = client_rect.bottom - client_rect.top; + + // TODO: check for errors and return them + return S_OK; +} + +/* Wine should switch to C++ at least to be able to implement COM interfaces in a sensible way, + * instead of this ridiculous amount of clumsy duplicated code everywhere + * C++ exists exactly to avoid having to write the following code */ +static ULONG STDMETHODCALLTYPE WineDXGIBackend_AddRef(IGalliumDXGIBackend* This) +{ + return InterlockedIncrement(&((struct WineDXGIBackend*)&This)->ref); +} + +static ULONG STDMETHODCALLTYPE WineDXGIBackend_Release(IGalliumDXGIBackend* This) +{ + ULONG v = InterlockedDecrement(&((struct WineDXGIBackend*)&This)->ref); + if(!v) + HeapFree(GetProcessHeap(), 0, This); + return v; +} + +static HRESULT WINAPI WineDXGIBackend_QueryInterface( + IGalliumDXGIBackend* iface, + REFIID riid, + void** ppvObject) +{ + if (IsEqualGUID(riid, &IID_IUnknown) + || IsEqualGUID(riid, &IID_IGalliumDXGIBackend)) + { + WineDXGIBackend_AddRef(iface); + *ppvObject = iface; + return S_OK; + } + + return E_NOINTERFACE; +} + +static IGalliumDXGIBackendVtbl WineDXGIBackend_vtbl = +{ + WineDXGIBackend_QueryInterface, + WineDXGIBackend_AddRef, + WineDXGIBackend_Release, + WineDXGIBackend_BeginPresent, + WineDXGIBackend_EndPresent, + WineDXGIBackend_TestPresent, + WineDXGIBackend_GetPresentSize +}; + +IGalliumDXGIBackend* new_WineDXGIBackend() +{ + struct WineDXGIBackend* backend = HeapAlloc(GetProcessHeap(), 0, sizeof(struct WineDXGIBackend)); + backend->ref = 1; + backend->vtbl_IGalliumDXGIBackend = &WineDXGIBackend_vtbl; + return (IGalliumDXGIBackend*)backend; +} + +static void install_wine_dxgi_backend() +{ + IGalliumDXGIBackend* backend = new_WineDXGIBackend(); + HWND root = GetDesktopWindow(); + unsigned code = X11DRV_GET_DISPLAY; + Display* dpy; + HDC hdc; + + hdc = GetDC(root); + ExtEscape(hdc, X11DRV_ESCAPE, sizeof(code), (LPSTR)&code, sizeof(dpy), (LPTSTR)&dpy); + ReleaseDC(root, hdc); + + GalliumDXGIUseX11Display(dpy, backend); + GalliumDXGIMakeDefault(); + GalliumDXGIUseNothing(); + backend->lpVtbl->Release(backend); +} + +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + switch (fdwReason) + { + case DLL_WINE_PREATTACH: + return TRUE; + case DLL_PROCESS_ATTACH: + DisableThreadLibraryCalls(hinstDLL); + install_wine_dxgi_backend(); + break; + case DLL_PROCESS_DETACH: + break; + default: + break; + } + + return TRUE; +} diff --git a/src/gallium/state_trackers/d3d1x/winedlls/dxgi/version.rc b/src/gallium/state_trackers/d3d1x/winedlls/dxgi/version.rc new file mode 100644 index 0000000000..3653281fbc --- /dev/null +++ b/src/gallium/state_trackers/d3d1x/winedlls/dxgi/version.rc @@ -0,0 +1,3 @@ +#define FILENAME "dxgi" +#define NAME "DXGI" +#include "../version.rc.h" diff --git a/src/gallium/state_trackers/d3d1x/winedlls/version.rc.h b/src/gallium/state_trackers/d3d1x/winedlls/version.rc.h new file mode 100644 index 0000000000..096d119fa3 --- /dev/null +++ b/src/gallium/state_trackers/d3d1x/winedlls/version.rc.h @@ -0,0 +1,30 @@ +1 VERSIONINFO +FILEVERSION 6,0,6000,16386 +PRODUCTVERSION 6,0,6000,16386 +FILEFLAGSMASK 63 +FILEFLAGS 0 +FILEOS 0x00000000L +FILETYPE 0x00000002L +FILESUBTYPE 0x00000000L +{ + BLOCK "StringFileInfo" + { + BLOCK "040904E4" + { + // all Wine DLLs claim to be from Microsoft, maybe it's needed for compatibility + VALUE "CompanyName", "Microsoft Corporation" + VALUE "FileDescription", "GalliumD3D1x " NAME " runtime" + VALUE "FileVersion", "6.0.6000.16386" + VALUE "InternalName", "" + VALUE "LegalCopyright", "Copyright (c) 2010 Luca Barbieri and other contributors" + VALUE "OriginalFilename", FILENAME ".dll" + VALUE "ProductName", "GalliumD3D1x" + VALUE "ProductVersion", "6.0.6000.16386" + } + } + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0409, 0x04E4 + } +} + |