/* * Graphics driver management functions * * Copyright 1994 Bob Amstadt * Copyright 1996, 2001 Alexandre Julliard * * 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 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include "config.h" #include "wine/port.h" #include <stdarg.h> #include <string.h> #include <stdio.h> #include "windef.h" #include "winbase.h" #include "winreg.h" #include "gdi_private.h" #include "wine/unicode.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(driver); struct graphics_driver { struct graphics_driver *next; struct graphics_driver *prev; HMODULE module; /* module handle */ unsigned int count; /* reference count */ DC_FUNCTIONS funcs; }; static struct graphics_driver *first_driver; static struct graphics_driver *display_driver; static CRITICAL_SECTION driver_section; static CRITICAL_SECTION_DEBUG critsect_debug = { 0, 0, &driver_section, { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList }, 0, 0, { (DWORD_PTR)(__FILE__ ": driver_section") } }; static CRITICAL_SECTION driver_section = { &critsect_debug, -1, 0, 0, 0, 0 }; /********************************************************************** * create_driver * * Allocate and fill the driver structure for a given module. */ static struct graphics_driver *create_driver( HMODULE module ) { struct graphics_driver *driver; if (!(driver = HeapAlloc( GetProcessHeap(), 0, sizeof(*driver)))) return NULL; driver->next = NULL; driver->prev = NULL; driver->module = module; driver->count = 1; /* fill the function table */ if (module) { #define GET_FUNC(name) driver->funcs.p##name = (void*)GetProcAddress( module, #name ) GET_FUNC(AbortDoc); GET_FUNC(AbortPath); GET_FUNC(AlphaBlend); GET_FUNC(AngleArc); GET_FUNC(Arc); GET_FUNC(ArcTo); GET_FUNC(BeginPath); GET_FUNC(BitBlt); GET_FUNC(ChoosePixelFormat); GET_FUNC(Chord); GET_FUNC(CloseFigure); GET_FUNC(CreateBitmap); GET_FUNC(CreateDC); GET_FUNC(CreateDIBSection); GET_FUNC(DeleteBitmap); GET_FUNC(DeleteDC); GET_FUNC(DescribePixelFormat); GET_FUNC(DeviceCapabilities); GET_FUNC(Ellipse); GET_FUNC(EndDoc); GET_FUNC(EndPage); GET_FUNC(EndPath); GET_FUNC(EnumDeviceFonts); GET_FUNC(ExcludeClipRect); GET_FUNC(ExtDeviceMode); GET_FUNC(ExtEscape); GET_FUNC(ExtFloodFill); GET_FUNC(ExtSelectClipRgn); GET_FUNC(ExtTextOut); GET_FUNC(FillPath); GET_FUNC(FillRgn); GET_FUNC(FlattenPath); GET_FUNC(FrameRgn); GET_FUNC(GdiComment); GET_FUNC(GetBitmapBits); GET_FUNC(GetCharWidth); GET_FUNC(GetDCOrgEx); GET_FUNC(GetDIBColorTable); GET_FUNC(GetDIBits); GET_FUNC(GetDeviceCaps); GET_FUNC(GetDeviceGammaRamp); GET_FUNC(GetICMProfile); GET_FUNC(GetNearestColor); GET_FUNC(GetPixel); GET_FUNC(GetPixelFormat); GET_FUNC(GetSystemPaletteEntries); GET_FUNC(GetTextExtentExPoint); GET_FUNC(GetTextMetrics); GET_FUNC(IntersectClipRect); GET_FUNC(InvertRgn); GET_FUNC(LineTo); GET_FUNC(MoveTo); GET_FUNC(ModifyWorldTransform); GET_FUNC(OffsetClipRgn); GET_FUNC(OffsetViewportOrg); GET_FUNC(OffsetWindowOrg); GET_FUNC(PaintRgn); GET_FUNC(PatBlt); GET_FUNC(Pie); GET_FUNC(PolyBezier); GET_FUNC(PolyBezierTo); GET_FUNC(PolyDraw); GET_FUNC(PolyPolygon); GET_FUNC(PolyPolyline); GET_FUNC(Polygon); GET_FUNC(Polyline); GET_FUNC(PolylineTo); GET_FUNC(RealizeDefaultPalette); GET_FUNC(RealizePalette); GET_FUNC(Rectangle); GET_FUNC(ResetDC); GET_FUNC(RestoreDC); GET_FUNC(RoundRect); GET_FUNC(SaveDC); GET_FUNC(ScaleViewportExt); GET_FUNC(ScaleWindowExt); GET_FUNC(SelectBitmap); GET_FUNC(SelectBrush); GET_FUNC(SelectClipPath); GET_FUNC(SelectFont); GET_FUNC(SelectPalette); GET_FUNC(SelectPen); GET_FUNC(SetArcDirection); GET_FUNC(SetBitmapBits); GET_FUNC(SetBkColor); GET_FUNC(SetBkMode); GET_FUNC(SetDCBrushColor); GET_FUNC(SetDCOrg); GET_FUNC(SetDCPenColor); GET_FUNC(SetDIBColorTable); GET_FUNC(SetDIBits); GET_FUNC(SetDIBitsToDevice); GET_FUNC(SetDeviceClipping); GET_FUNC(SetDeviceGammaRamp); GET_FUNC(SetMapMode); GET_FUNC(SetMapperFlags); GET_FUNC(SetPixel); GET_FUNC(SetPixelFormat); GET_FUNC(SetPolyFillMode); GET_FUNC(SetROP2); GET_FUNC(SetRelAbs); GET_FUNC(SetStretchBltMode); GET_FUNC(SetTextAlign); GET_FUNC(SetTextCharacterExtra); GET_FUNC(SetTextColor); GET_FUNC(SetTextJustification); GET_FUNC(SetViewportExt); GET_FUNC(SetViewportOrg); GET_FUNC(SetWindowExt); GET_FUNC(SetWindowOrg); GET_FUNC(SetWorldTransform); GET_FUNC(StartDoc); GET_FUNC(StartPage); GET_FUNC(StretchBlt); GET_FUNC(StretchDIBits); GET_FUNC(StrokeAndFillPath); GET_FUNC(StrokePath); GET_FUNC(SwapBuffers); GET_FUNC(UnrealizePalette); GET_FUNC(WidenPath); /* OpenGL32 */ GET_FUNC(wglCreateContext); GET_FUNC(wglDeleteContext); GET_FUNC(wglGetProcAddress); GET_FUNC(wglGetPbufferDCARB); GET_FUNC(wglMakeContextCurrentARB); GET_FUNC(wglMakeCurrent); GET_FUNC(wglSetPixelFormatWINE); GET_FUNC(wglShareLists); GET_FUNC(wglUseFontBitmapsA); GET_FUNC(wglUseFontBitmapsW); #undef GET_FUNC } else memset( &driver->funcs, 0, sizeof(driver->funcs) ); /* add it to the list */ driver->prev = NULL; if ((driver->next = first_driver)) driver->next->prev = driver; first_driver = driver; return driver; } /********************************************************************** * load_display_driver * * Special case for loading the display driver: get the name from the config file */ static struct graphics_driver *load_display_driver(void) { char buffer[MAX_PATH], libname[32], *name, *next; HMODULE module = 0; HKEY hkey; if (display_driver) /* already loaded */ { display_driver->count++; return display_driver; } strcpy( buffer, "x11" ); /* default value */ /* @@ Wine registry key: HKCU\Software\Wine\Drivers */ if (!RegOpenKeyA( HKEY_CURRENT_USER, "Software\\Wine\\Drivers", &hkey )) { DWORD type, count = sizeof(buffer); RegQueryValueExA( hkey, "Graphics", 0, &type, (LPBYTE) buffer, &count ); RegCloseKey( hkey ); } name = buffer; while (name) { next = strchr( name, ',' ); if (next) *next++ = 0; snprintf( libname, sizeof(libname), "wine%s.drv", name ); if ((module = LoadLibraryA( libname )) != 0) break; name = next; } if (!(display_driver = create_driver( module ))) { MESSAGE( "Could not create graphics driver '%s'\n", buffer ); FreeLibrary( module ); ExitProcess(1); } display_driver->count++; /* we don't want to free it */ return display_driver; } /********************************************************************** * DRIVER_load_driver */ const DC_FUNCTIONS *DRIVER_load_driver( LPCWSTR name ) { HMODULE module; struct graphics_driver *driver; static const WCHAR displayW[] = { 'd','i','s','p','l','a','y',0 }; static const WCHAR display1W[] = {'\\','\\','.','\\','D','I','S','P','L','A','Y','1',0}; EnterCriticalSection( &driver_section ); /* display driver is a special case */ if (!strcmpiW( name, displayW ) || !strcmpiW( name, display1W )) { driver = load_display_driver(); LeaveCriticalSection( &driver_section ); return &driver->funcs; } if ((module = GetModuleHandleW( name ))) { for (driver = first_driver; driver; driver = driver->next) { if (driver->module == module) { driver->count++; LeaveCriticalSection( &driver_section ); return &driver->funcs; } } } if (!(module = LoadLibraryW( name ))) { LeaveCriticalSection( &driver_section ); return NULL; } if (!(driver = create_driver( module ))) { FreeLibrary( module ); LeaveCriticalSection( &driver_section ); return NULL; } TRACE( "loaded driver %p for %s\n", driver, debugstr_w(name) ); LeaveCriticalSection( &driver_section ); return &driver->funcs; } /********************************************************************** * DRIVER_get_driver * * Get a new copy of an existing driver. */ const DC_FUNCTIONS *DRIVER_get_driver( const DC_FUNCTIONS *funcs ) { struct graphics_driver *driver; EnterCriticalSection( &driver_section ); for (driver = first_driver; driver; driver = driver->next) if (&driver->funcs == funcs) break; if (!driver) ERR( "driver not found, trouble ahead\n" ); driver->count++; LeaveCriticalSection( &driver_section ); return funcs; } /********************************************************************** * DRIVER_release_driver * * Release a driver by decrementing ref count and freeing it if needed. */ void DRIVER_release_driver( const DC_FUNCTIONS *funcs ) { struct graphics_driver *driver; EnterCriticalSection( &driver_section ); for (driver = first_driver; driver; driver = driver->next) if (&driver->funcs == funcs) break; if (!driver) goto done; if (--driver->count) goto done; /* removed last reference, free it */ if (driver->next) driver->next->prev = driver->prev; if (driver->prev) driver->prev->next = driver->next; else first_driver = driver->next; if (driver == display_driver) display_driver = NULL; FreeLibrary( driver->module ); HeapFree( GetProcessHeap(), 0, driver ); done: LeaveCriticalSection( &driver_section ); } /***************************************************************************** * DRIVER_GetDriverName * */ BOOL DRIVER_GetDriverName( LPCWSTR device, LPWSTR driver, DWORD size ) { static const WCHAR displayW[] = { 'd','i','s','p','l','a','y',0 }; static const WCHAR devicesW[] = { 'd','e','v','i','c','e','s',0 }; static const WCHAR display1W[] = {'\\','\\','.','\\','D','I','S','P','L','A','Y','1',0}; static const WCHAR empty_strW[] = { 0 }; WCHAR *p; /* display is a special case */ if (!strcmpiW( device, displayW ) || !strcmpiW( device, display1W )) { lstrcpynW( driver, displayW, size ); return TRUE; } size = GetProfileStringW(devicesW, device, empty_strW, driver, size); if(!size) { WARN("Unable to find %s in [devices] section of win.ini\n", debugstr_w(device)); return FALSE; } p = strchrW(driver, ','); if(!p) { WARN("%s entry in [devices] section of win.ini is malformed.\n", debugstr_w(device)); return FALSE; } *p = 0; TRACE("Found %s for %s\n", debugstr_w(driver), debugstr_w(device)); return TRUE; } /*********************************************************************** * GdiConvertToDevmodeW (GDI32.@) */ DEVMODEW * WINAPI GdiConvertToDevmodeW(const DEVMODEA *dmA) { DEVMODEW *dmW; WORD dmW_size, dmA_size; dmA_size = dmA->dmSize; /* this is the minimal dmSize that XP accepts */ if (dmA_size < FIELD_OFFSET(DEVMODEA, dmFields)) return NULL; if (dmA_size > sizeof(DEVMODEA)) dmA_size = sizeof(DEVMODEA); dmW_size = dmA_size + CCHDEVICENAME; if (dmA_size >= FIELD_OFFSET(DEVMODEA, dmFormName) + CCHFORMNAME) dmW_size += CCHFORMNAME; dmW = HeapAlloc(GetProcessHeap(), 0, dmW_size + dmA->dmDriverExtra); if (!dmW) return NULL; MultiByteToWideChar(CP_ACP, 0, (const char*) dmA->dmDeviceName, -1, dmW->dmDeviceName, CCHDEVICENAME); /* copy slightly more, to avoid long computations */ memcpy(&dmW->dmSpecVersion, &dmA->dmSpecVersion, dmA_size - CCHDEVICENAME); if (dmA_size >= FIELD_OFFSET(DEVMODEA, dmFormName) + CCHFORMNAME) { if (dmA->dmFields & DM_FORMNAME) MultiByteToWideChar(CP_ACP, 0, (const char*) dmA->dmFormName, -1, dmW->dmFormName, CCHFORMNAME); else dmW->dmFormName[0] = 0; if (dmA_size > FIELD_OFFSET(DEVMODEA, dmLogPixels)) memcpy(&dmW->dmLogPixels, &dmA->dmLogPixels, dmA_size - FIELD_OFFSET(DEVMODEA, dmLogPixels)); } if (dmA->dmDriverExtra) memcpy((char *)dmW + dmW_size, (const char *)dmA + dmA_size, dmA->dmDriverExtra); dmW->dmSize = dmW_size; return dmW; } /***************************************************************************** * @ [GDI32.100] * * This should thunk to 16-bit and simply call the proc with the given args. */ INT WINAPI GDI_CallDevInstall16( FARPROC16 lpfnDevInstallProc, HWND hWnd, LPSTR lpModelName, LPSTR OldPort, LPSTR NewPort ) { FIXME("(%p, %p, %s, %s, %s)\n", lpfnDevInstallProc, hWnd, lpModelName, OldPort, NewPort ); return -1; } /***************************************************************************** * @ [GDI32.101] * * This should load the correct driver for lpszDevice and calls this driver's * ExtDeviceModePropSheet proc. * * Note: The driver calls a callback routine for each property sheet page; these * pages are supposed to be filled into the structure pointed to by lpPropSheet. * The layout of this structure is: * * struct * { * DWORD nPages; * DWORD unknown; * HPROPSHEETPAGE pages[10]; * }; */ INT WINAPI GDI_CallExtDeviceModePropSheet16( HWND hWnd, LPCSTR lpszDevice, LPCSTR lpszPort, LPVOID lpPropSheet ) { FIXME("(%p, %s, %s, %p)\n", hWnd, lpszDevice, lpszPort, lpPropSheet ); return -1; } /***************************************************************************** * @ [GDI32.102] * * This should load the correct driver for lpszDevice and call this driver's * ExtDeviceMode proc. * * FIXME: convert ExtDeviceMode to unicode in the driver interface */ INT WINAPI GDI_CallExtDeviceMode16( HWND hwnd, LPDEVMODEA lpdmOutput, LPSTR lpszDevice, LPSTR lpszPort, LPDEVMODEA lpdmInput, LPSTR lpszProfile, DWORD fwMode ) { WCHAR deviceW[300]; WCHAR bufW[300]; char buf[300]; HDC hdc; DC *dc; INT ret = -1; TRACE("(%p, %p, %s, %s, %p, %s, %d)\n", hwnd, lpdmOutput, lpszDevice, lpszPort, lpdmInput, lpszProfile, fwMode ); if (!lpszDevice) return -1; if (!MultiByteToWideChar(CP_ACP, 0, lpszDevice, -1, deviceW, 300)) return -1; if(!DRIVER_GetDriverName( deviceW, bufW, 300 )) return -1; if (!WideCharToMultiByte(CP_ACP, 0, bufW, -1, buf, 300, NULL, NULL)) return -1; if (!(hdc = CreateICA( buf, lpszDevice, lpszPort, NULL ))) return -1; if ((dc = get_dc_ptr( hdc ))) { if (dc->funcs->pExtDeviceMode) ret = dc->funcs->pExtDeviceMode( buf, hwnd, lpdmOutput, lpszDevice, lpszPort, lpdmInput, lpszProfile, fwMode ); release_dc_ptr( dc ); } DeleteDC( hdc ); return ret; } /**************************************************************************** * @ [GDI32.103] * * This should load the correct driver for lpszDevice and calls this driver's * AdvancedSetupDialog proc. */ INT WINAPI GDI_CallAdvancedSetupDialog16( HWND hwnd, LPSTR lpszDevice, LPDEVMODEA devin, LPDEVMODEA devout ) { TRACE("(%p, %s, %p, %p)\n", hwnd, lpszDevice, devin, devout ); return -1; } /***************************************************************************** * @ [GDI32.104] * * This should load the correct driver for lpszDevice and calls this driver's * DeviceCapabilities proc. * * FIXME: convert DeviceCapabilities to unicode in the driver interface */ DWORD WINAPI GDI_CallDeviceCapabilities16( LPCSTR lpszDevice, LPCSTR lpszPort, WORD fwCapability, LPSTR lpszOutput, LPDEVMODEA lpdm ) { WCHAR deviceW[300]; WCHAR bufW[300]; char buf[300]; HDC hdc; DC *dc; INT ret = -1; TRACE("(%s, %s, %d, %p, %p)\n", lpszDevice, lpszPort, fwCapability, lpszOutput, lpdm ); if (!lpszDevice) return -1; if (!MultiByteToWideChar(CP_ACP, 0, lpszDevice, -1, deviceW, 300)) return -1; if(!DRIVER_GetDriverName( deviceW, bufW, 300 )) return -1; if (!WideCharToMultiByte(CP_ACP, 0, bufW, -1, buf, 300, NULL, NULL)) return -1; if (!(hdc = CreateICA( buf, lpszDevice, lpszPort, NULL ))) return -1; if ((dc = get_dc_ptr( hdc ))) { if (dc->funcs->pDeviceCapabilities) ret = dc->funcs->pDeviceCapabilities( buf, lpszDevice, lpszPort, fwCapability, lpszOutput, lpdm ); release_dc_ptr( dc ); } DeleteDC( hdc ); return ret; } /************************************************************************ * Escape [GDI32.@] */ INT WINAPI Escape( HDC hdc, INT escape, INT in_count, LPCSTR in_data, LPVOID out_data ) { INT ret; POINT *pt; switch (escape) { case ABORTDOC: return AbortDoc( hdc ); case ENDDOC: return EndDoc( hdc ); case GETPHYSPAGESIZE: pt = out_data; pt->x = GetDeviceCaps( hdc, PHYSICALWIDTH ); pt->y = GetDeviceCaps( hdc, PHYSICALHEIGHT ); return 1; case GETPRINTINGOFFSET: pt = out_data; pt->x = GetDeviceCaps( hdc, PHYSICALOFFSETX ); pt->y = GetDeviceCaps( hdc, PHYSICALOFFSETY ); return 1; case GETSCALINGFACTOR: pt = out_data; pt->x = GetDeviceCaps( hdc, SCALINGFACTORX ); pt->y = GetDeviceCaps( hdc, SCALINGFACTORY ); return 1; case NEWFRAME: return EndPage( hdc ); case SETABORTPROC: return SetAbortProc( hdc, (ABORTPROC)in_data ); case STARTDOC: { DOCINFOA doc; char *name = NULL; /* in_data may not be 0 terminated so we must copy it */ if (in_data) { name = HeapAlloc( GetProcessHeap(), 0, in_count+1 ); memcpy( name, in_data, in_count ); name[in_count] = 0; } /* out_data is actually a pointer to the DocInfo structure and used as * a second input parameter */ if (out_data) doc = *(DOCINFOA *)out_data; else { doc.cbSize = sizeof(doc); doc.lpszOutput = NULL; doc.lpszDatatype = NULL; doc.fwType = 0; } doc.lpszDocName = name; ret = StartDocA( hdc, &doc ); HeapFree( GetProcessHeap(), 0, name ); if (ret > 0) ret = StartPage( hdc ); return ret; } case QUERYESCSUPPORT: { const INT *ptr = (const INT *)in_data; if (in_count < sizeof(INT)) return 0; switch(*ptr) { case ABORTDOC: case ENDDOC: case GETPHYSPAGESIZE: case GETPRINTINGOFFSET: case GETSCALINGFACTOR: case NEWFRAME: case QUERYESCSUPPORT: case SETABORTPROC: case STARTDOC: return TRUE; } break; } } /* if not handled internally, pass it to the driver */ return ExtEscape( hdc, escape, in_count, in_data, 0, out_data ); } /****************************************************************************** * ExtEscape [GDI32.@] * * Access capabilities of a particular device that are not available through GDI. * * PARAMS * hdc [I] Handle to device context * nEscape [I] Escape function * cbInput [I] Number of bytes in input structure * lpszInData [I] Pointer to input structure * cbOutput [I] Number of bytes in output structure * lpszOutData [O] Pointer to output structure * * RETURNS * Success: >0 * Not implemented: 0 * Failure: <0 */ INT WINAPI ExtEscape( HDC hdc, INT nEscape, INT cbInput, LPCSTR lpszInData, INT cbOutput, LPSTR lpszOutData ) { INT ret = 0; DC * dc = get_dc_ptr( hdc ); if (dc) { if (dc->funcs->pExtEscape) ret = dc->funcs->pExtEscape( dc->physDev, nEscape, cbInput, lpszInData, cbOutput, lpszOutData ); release_dc_ptr( dc ); } return ret; } /******************************************************************* * DrawEscape [GDI32.@] * * */ INT WINAPI DrawEscape(HDC hdc, INT nEscape, INT cbInput, LPCSTR lpszInData) { FIXME("DrawEscape, stub\n"); return 0; } /******************************************************************* * NamedEscape [GDI32.@] */ INT WINAPI NamedEscape( HDC hdc, LPCWSTR pDriver, INT nEscape, INT cbInput, LPCSTR lpszInData, INT cbOutput, LPSTR lpszOutData ) { FIXME("(%p, %s, %d, %d, %p, %d, %p)\n", hdc, wine_dbgstr_w(pDriver), nEscape, cbInput, lpszInData, cbOutput, lpszOutData); return 0; }