--- /dev/null
- {
+/*
+ *
+ * Copyright 1997 Marcus Meissner
+ * Copyright 1998 Juergen Schmied
+ * Copyright 2005 Mike McCormack
+ *
+ * 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
+ *
+ * NOTES
+ * Nearly complete information about the binary formats
+ * of .lnk files available at http://www.wotsit.org
+ *
+ * You can use winedump to examine the contents of a link file:
+ * winedump lnk sc.lnk
+ *
+ * MSI advertised shortcuts are totally undocumented. They provide an
+ * icon for a program that is not yet installed, and invoke MSI to
+ * install the program when the shortcut is clicked on. They are
+ * created by passing a special string to SetPath, and the information
+ * in that string is parsed an stored.
+ */
+
+#include <precomp.h>
+
+WINE_DEFAULT_DEBUG_CHANNEL(shell);
+
+/* link file formats */
+
+#include "pshpack1.h"
+
+typedef struct _LINK_HEADER
+{
+ DWORD dwSize; /* 0x00 size of the header - 0x4c */
+ GUID MagicGuid; /* 0x04 is CLSID_ShellLink */
+ DWORD dwFlags; /* 0x14 describes elements following */
+ DWORD dwFileAttr; /* 0x18 attributes of the target file */
+ FILETIME Time1; /* 0x1c */
+ FILETIME Time2; /* 0x24 */
+ FILETIME Time3; /* 0x2c */
+ DWORD dwFileLength; /* 0x34 File length */
+ DWORD nIcon; /* 0x38 icon number */
+ DWORD fStartup; /* 0x3c startup type */
+ DWORD wHotKey; /* 0x40 hotkey */
+ DWORD Unknown5; /* 0x44 */
+ DWORD Unknown6; /* 0x48 */
+} LINK_HEADER, * PLINK_HEADER;
+
+#define SHLINK_LOCAL 0
+#define SHLINK_REMOTE 1
+#define MAX_PROPERTY_SHEET_PAGE 32
+
+typedef struct _LOCATION_INFO
+{
+ DWORD dwTotalSize;
+ DWORD dwHeaderSize;
+ DWORD dwFlags;
+ DWORD dwVolTableOfs;
+ DWORD dwLocalPathOfs;
+ DWORD dwNetworkVolTableOfs;
+ DWORD dwFinalPathOfs;
+} LOCATION_INFO;
+
+typedef struct _LOCAL_VOLUME_INFO
+{
+ DWORD dwSize;
+ DWORD dwType;
+ DWORD dwVolSerial;
+ DWORD dwVolLabelOfs;
+} LOCAL_VOLUME_INFO;
+
+typedef struct volume_info_t
+{
+ DWORD type;
+ DWORD serial;
+ WCHAR label[12]; /* assume 8.3 */
+} volume_info;
+
+#include "poppack.h"
+
+static const IShellLinkAVtbl slvt;
+static const IShellLinkWVtbl slvtw;
+static const IPersistFileVtbl pfvt;
+static const IPersistStreamVtbl psvt;
+static const IShellLinkDataListVtbl dlvt;
+static const IShellExtInitVtbl eivt;
+static const IContextMenuVtbl cmvt;
+static const IObjectWithSiteVtbl owsvt;
+static const IShellPropSheetExtVtbl pse;
+
+/* IShellLink Implementation */
+
+typedef struct
+{
+ const IShellLinkAVtbl *lpVtbl;
+ const IShellLinkWVtbl *lpvtblw;
+ const IPersistFileVtbl *lpvtblPersistFile;
+ const IPersistStreamVtbl *lpvtblPersistStream;
+ const IShellLinkDataListVtbl *lpvtblShellLinkDataList;
+ const IShellExtInitVtbl *lpvtblShellExtInit;
+ const IContextMenuVtbl *lpvtblContextMenu;
+ const IObjectWithSiteVtbl *lpvtblObjectWithSite;
+ const IShellPropSheetExtVtbl * lpvtblPropSheetExt;
+
+ LONG ref;
+
+ /* data structures according to the information in the link */
+ LPITEMIDLIST pPidl;
+ WORD wHotKey;
+ SYSTEMTIME time1;
+ SYSTEMTIME time2;
+ SYSTEMTIME time3;
+
+ DWORD iShowCmd;
+ LPWSTR sIcoPath;
+ INT iIcoNdx;
+ LPWSTR sPath;
+ LPWSTR sArgs;
+ LPWSTR sWorkDir;
+ LPWSTR sDescription;
+ LPWSTR sPathRel;
+ LPWSTR sProduct;
+ LPWSTR sComponent;
+ volume_info volume;
+ LPWSTR sLinkPath;
++ LPWSTR sCurFile;
+ BOOL bRunAs;
+ BOOL bDirty;
+ INT iIdOpen; /* id of the "Open" entry in the context menu */
+ IUnknown *site;
+} IShellLinkImpl, *LPIShellLinkImpl;
+
+static LPIShellLinkImpl __inline impl_from_IShellLinkW( IShellLinkW *iface )
+{
+ return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblw));
+}
+
+static LPIShellLinkImpl __inline impl_from_IPersistFile( IPersistFile *iface )
+{
+ return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblPersistFile));
+}
+
+static LPIShellLinkImpl __inline impl_from_IPersistStream( IPersistStream *iface )
+{
+ return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblPersistStream));
+}
+
+static LPIShellLinkImpl __inline impl_from_IShellLinkDataList( IShellLinkDataList *iface )
+{
+ return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblShellLinkDataList));
+}
+
+static LPIShellLinkImpl __inline impl_from_IShellExtInit( IShellExtInit *iface )
+{
+ return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblShellExtInit));
+}
+
+static LPIShellLinkImpl __inline impl_from_IContextMenu( IContextMenu *iface )
+{
+ return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblContextMenu));
+}
+
+static LPIShellLinkImpl __inline impl_from_IObjectWithSite( IObjectWithSite *iface )
+{
+ return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblObjectWithSite));
+}
+
+static LPIShellLinkImpl __inline impl_from_IShellPropSheetExt( IShellPropSheetExt *iface )
+{
+ return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblPropSheetExt));
+}
+
+
+static HRESULT ShellLink_UpdatePath(LPCWSTR sPathRel, LPCWSTR path, LPCWSTR sWorkDir, LPWSTR* psPath);
+
+/* strdup on the process heap */
+static LPWSTR __inline HEAP_strdupAtoW( HANDLE heap, DWORD flags, LPCSTR str)
+{
++ assert(str);
+ INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
+ LPWSTR p = HeapAlloc( heap, flags, len*sizeof (WCHAR) );
+ if( !p )
+ return p;
+ MultiByteToWideChar( CP_ACP, 0, str, -1, p, len );
+ return p;
+}
+
+static LPWSTR __inline strdupW( LPCWSTR src )
+{
+ LPWSTR dest;
+ if (!src) return NULL;
+ dest = HeapAlloc( GetProcessHeap(), 0, (wcslen(src)+1)*sizeof(WCHAR) );
+ if (dest)
+ wcscpy(dest, src);
+ return dest;
+}
+
+/**************************************************************************
+ * ShellLink::QueryInterface implementation
+ */
+static HRESULT ShellLink_QueryInterface( IShellLinkImpl *This, REFIID riid, LPVOID *ppvObj)
+{
+ TRACE("(%p)->(\n\tIID:\t%s)\n",This,debugstr_guid(riid));
+
+ *ppvObj = NULL;
+
+ if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IShellLinkA))
+ {
+ *ppvObj = This;
+ }
+ else if(IsEqualIID(riid, &IID_IShellLinkW))
+ {
+ *ppvObj = &(This->lpvtblw);
+ }
+ else if(IsEqualIID(riid, &IID_IPersistFile))
+ {
+ *ppvObj = &(This->lpvtblPersistFile);
+ }
+ else if(IsEqualIID(riid, &IID_IPersistStream))
+ {
+ *ppvObj = &(This->lpvtblPersistStream);
+ }
+ else if(IsEqualIID(riid, &IID_IShellLinkDataList))
+ {
+ *ppvObj = &(This->lpvtblShellLinkDataList);
+ }
+ else if(IsEqualIID(riid, &IID_IShellExtInit))
+ {
+ *ppvObj = &(This->lpvtblShellExtInit);
+ }
+ else if(IsEqualIID(riid, &IID_IContextMenu))
+ {
+ *ppvObj = &(This->lpvtblContextMenu);
+ }
+ else if(IsEqualIID(riid, &IID_IObjectWithSite))
+ {
+ *ppvObj = &(This->lpvtblObjectWithSite);
+ }
+ else if(IsEqualIID(riid, &IID_IShellPropSheetExt))
+ {
+ *ppvObj = &(This->lpvtblPropSheetExt);
+ }
+
+ if(*ppvObj)
+ {
+ IUnknown_AddRef((IUnknown*)(*ppvObj));
+ TRACE("-- Interface: (%p)->(%p)\n",ppvObj,*ppvObj);
+ return S_OK;
+ }
+ ERR("-- Interface: E_NOINTERFACE\n");
+ return E_NOINTERFACE;
+}
+
+/**************************************************************************
+ * ShellLink::AddRef implementation
+ */
+static ULONG ShellLink_AddRef( IShellLinkImpl *This )
+{
+ ULONG refCount = InterlockedIncrement(&This->ref);
+
+ TRACE("(%p)->(count=%u)\n", This, refCount - 1);
+
+ return refCount;
+}
+
+/**************************************************************************
+ * ShellLink::Release implementation
+ */
+static ULONG ShellLink_Release( IShellLinkImpl *This )
+{
+ ULONG refCount = InterlockedDecrement(&This->ref);
+
+ TRACE("(%p)->(count=%u)\n", This, refCount + 1);
+
+ if (refCount)
+ return refCount;
+
+ TRACE("-- destroying IShellLink(%p)\n",This);
+
+ HeapFree(GetProcessHeap(), 0, This->sIcoPath);
+ HeapFree(GetProcessHeap(), 0, This->sArgs);
+ HeapFree(GetProcessHeap(), 0, This->sWorkDir);
+ HeapFree(GetProcessHeap(), 0, This->sDescription);
+ HeapFree(GetProcessHeap(),0,This->sPath);
+ HeapFree(GetProcessHeap(),0,This->sLinkPath);
+
+ if (This->site)
+ IUnknown_Release( This->site );
+
+ if (This->pPidl)
+ ILFree(This->pPidl);
+
+ LocalFree(This);
+
+ return 0;
+}
+
+static HRESULT ShellLink_GetClassID( IShellLinkImpl *This, CLSID *pclsid )
+{
+ TRACE("%p %p\n", This, pclsid);
+
+ *pclsid = CLSID_ShellLink;
+ return S_OK;
+}
+
+/**************************************************************************
+ * IPersistFile_QueryInterface
+ */
+static HRESULT WINAPI IPersistFile_fnQueryInterface(
+ IPersistFile* iface,
+ REFIID riid,
+ LPVOID *ppvObj)
+{
+ IShellLinkImpl *This = impl_from_IPersistFile(iface);
+ return ShellLink_QueryInterface( This, riid, ppvObj );
+}
+
+/******************************************************************************
+ * IPersistFile_AddRef
+ */
+static ULONG WINAPI IPersistFile_fnAddRef(IPersistFile* iface)
+{
+ IShellLinkImpl *This = impl_from_IPersistFile(iface);
+ return ShellLink_AddRef( This );
+}
+
+/******************************************************************************
+ * IPersistFile_Release
+ */
+static ULONG WINAPI IPersistFile_fnRelease(IPersistFile* iface)
+{
+ IShellLinkImpl *This = impl_from_IPersistFile(iface);
+ return IShellLinkA_Release((IShellLinkA*)This);
+}
+
+static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile* iface, CLSID *pClassID)
+{
+ IShellLinkImpl *This = impl_from_IPersistFile(iface);
+ return ShellLink_GetClassID( This, pClassID );
+}
+
+static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile* iface)
+{
+ IShellLinkImpl *This = impl_from_IPersistFile(iface);
+
+ TRACE("(%p)\n",This);
+
+ if (This->bDirty)
+ return S_OK;
+
+ return S_FALSE;
+}
+
+static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile* iface, LPCOLESTR pszFileName, DWORD dwMode)
+{
+ IShellLinkImpl *This = impl_from_IPersistFile(iface);
+ IPersistStream *StreamThis = (IPersistStream *)&This->lpvtblPersistStream;
+ HRESULT r;
+ IStream *stm;
+
+ TRACE("(%p, %s, %x)\n",This, debugstr_w(pszFileName), dwMode);
+
+ if( dwMode == 0 )
+ dwMode = STGM_READ | STGM_SHARE_DENY_WRITE;
+ r = SHCreateStreamOnFileW(pszFileName, dwMode, &stm);
+ if( SUCCEEDED( r ) )
+ {
+ HeapFree(GetProcessHeap(), 0, This->sLinkPath);
+ This->sLinkPath = strdupW(pszFileName);
+ r = IPersistStream_Load(StreamThis, stm);
+ ShellLink_UpdatePath(This->sPathRel, pszFileName, This->sWorkDir, &This->sPath);
+ IStream_Release( stm );
+ This->bDirty = FALSE;
+ }
+ TRACE("-- returning hr %08x\n", r);
+ return r;
+}
+
+static BOOL StartLinkProcessor( LPCOLESTR szLink )
+{
+ static const WCHAR szFormat[] = {
+ 'w','i','n','e','m','e','n','u','b','u','i','l','d','e','r','.','e','x','e',
+ ' ','-','w',' ','"','%','s','"',0 };
+ LONG len;
+ LPWSTR buffer;
+ STARTUPINFOW si;
+ PROCESS_INFORMATION pi;
+ BOOL ret;
+
+ len = sizeof(szFormat) + wcslen( szLink ) * sizeof(WCHAR);
+ buffer = HeapAlloc( GetProcessHeap(), 0, len );
+ if( !buffer )
+ return FALSE;
+
+ swprintf( buffer, szFormat, szLink );
+
+ TRACE("starting %s\n",debugstr_w(buffer));
+
+ memset(&si, 0, sizeof(si));
+ si.cb = sizeof(si);
+
+ ret = CreateProcessW( NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi );
+
+ HeapFree( GetProcessHeap(), 0, buffer );
+
+ if (ret)
+ {
+ CloseHandle( pi.hProcess );
+ CloseHandle( pi.hThread );
+ }
+
+ return ret;
+}
+
+static HRESULT WINAPI IPersistFile_fnSave(IPersistFile* iface, LPCOLESTR pszFileName, BOOL fRemember)
+{
+ IShellLinkImpl *This = impl_from_IPersistFile(iface);
+ IPersistStream *StreamThis = (IPersistStream *)&This->lpvtblPersistStream;
+ HRESULT r;
+ IStream *stm;
+
+ TRACE("(%p)->(%s)\n",This,debugstr_w(pszFileName));
+
+ if (!pszFileName)
+ return E_FAIL;
+
+ r = SHCreateStreamOnFileW( pszFileName, STGM_READWRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE, &stm );
+ if( SUCCEEDED( r ) )
+ {
+ r = IPersistStream_Save(StreamThis, stm, FALSE);
+ IStream_Release( stm );
+
+ if( SUCCEEDED( r ) )
- else
++ {
++ if ( This->sCurFile )
++ {
++ HeapFree(GetProcessHeap(), 0, This->sCurFile);
++ }
++ This->sCurFile = HeapAlloc(GetProcessHeap(), 0, (wcslen(pszFileName)+1) * sizeof(WCHAR));
++ if ( This->sCurFile )
++ {
++ wcscpy(This->sCurFile, pszFileName);
++ }
++
+ StartLinkProcessor( pszFileName );
+
+ This->bDirty = FALSE;
+ }
- IShellLinkImpl *This = impl_from_IPersistFile(iface);
- FIXME("(%p)\n",This);
- return NOERROR;
++ else
+ {
+ DeleteFileW( pszFileName );
+ WARN("Failed to create shortcut %s\n", debugstr_w(pszFileName) );
+ }
+ }
+
+ return r;
+}
+
+static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile* iface, LPCOLESTR pszFileName)
+{
+ IShellLinkImpl *This = impl_from_IPersistFile(iface);
+ FIXME("(%p)->(%s)\n",This,debugstr_w(pszFileName));
+ return NOERROR;
+}
+
+static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile* iface, LPOLESTR *ppszFileName)
+{
- if (This->sComponent || This->sProduct)
- return S_FALSE;
-
++ IShellLinkImpl *This = impl_from_IPersistFile(iface);
++
++ *ppszFileName = NULL;
++
++ if ( !This->sCurFile)
++ {
++ /* IPersistFile::GetCurFile called before IPersistFile::Save */
++ return S_FALSE;
++ }
++
++ *ppszFileName = CoTaskMemAlloc((wcslen(This->sCurFile)+1) * sizeof(WCHAR));
++ if (!*ppszFileName)
++ {
++ /* out of memory */
++ return E_OUTOFMEMORY;
++ }
++
++ /* copy last saved filename */
++ wcscpy(*ppszFileName, This->sCurFile);
++
++ return NOERROR;
+}
+
+static const IPersistFileVtbl pfvt =
+{
+ IPersistFile_fnQueryInterface,
+ IPersistFile_fnAddRef,
+ IPersistFile_fnRelease,
+ IPersistFile_fnGetClassID,
+ IPersistFile_fnIsDirty,
+ IPersistFile_fnLoad,
+ IPersistFile_fnSave,
+ IPersistFile_fnSaveCompleted,
+ IPersistFile_fnGetCurFile
+};
+
+/************************************************************************
+ * IPersistStream_QueryInterface
+ */
+static HRESULT WINAPI IPersistStream_fnQueryInterface(
+ IPersistStream* iface,
+ REFIID riid,
+ VOID** ppvObj)
+{
+ IShellLinkImpl *This = impl_from_IPersistStream(iface);
+ return ShellLink_QueryInterface( This, riid, ppvObj );
+}
+
+/************************************************************************
+ * IPersistStream_Release
+ */
+static ULONG WINAPI IPersistStream_fnRelease(
+ IPersistStream* iface)
+{
+ IShellLinkImpl *This = impl_from_IPersistStream(iface);
+ return IShellLinkA_Release((IShellLinkA*)This);
+}
+
+/************************************************************************
+ * IPersistStream_AddRef
+ */
+static ULONG WINAPI IPersistStream_fnAddRef(
+ IPersistStream* iface)
+{
+ IShellLinkImpl *This = impl_from_IPersistStream(iface);
+ return ShellLink_AddRef( This );
+}
+
+/************************************************************************
+ * IPersistStream_GetClassID
+ *
+ */
+static HRESULT WINAPI IPersistStream_fnGetClassID(
+ IPersistStream* iface,
+ CLSID* pClassID)
+{
+ IShellLinkImpl *This = impl_from_IPersistStream(iface);
+ return ShellLink_GetClassID( This, pClassID );
+}
+
+/************************************************************************
+ * IPersistStream_IsDirty (IPersistStream)
+ */
+static HRESULT WINAPI IPersistStream_fnIsDirty(
+ IPersistStream* iface)
+{
+ IShellLinkImpl *This = impl_from_IPersistStream(iface);
+
+ TRACE("(%p)\n", This);
+
+ return S_OK;
+}
+
+
+static HRESULT Stream_LoadString( IStream* stm, BOOL unicode, LPWSTR *pstr )
+{
+ DWORD count;
+ USHORT len;
+ LPVOID temp;
+ LPWSTR str;
+ HRESULT r;
+
+ TRACE("%p\n", stm);
+
+ count = 0;
+ r = IStream_Read(stm, &len, sizeof(len), &count);
+ if ( FAILED (r) || ( count != sizeof(len) ) )
+ return E_FAIL;
+
+ if( unicode )
+ len *= sizeof (WCHAR);
+
+ TRACE("reading %d\n", len);
+ temp = HeapAlloc(GetProcessHeap(), 0, len+sizeof(WCHAR));
+ if( !temp )
+ return E_OUTOFMEMORY;
+ count = 0;
+ r = IStream_Read(stm, temp, len, &count);
+ if( FAILED (r) || ( count != len ) )
+ {
+ HeapFree( GetProcessHeap(), 0, temp );
+ return E_FAIL;
+ }
+
+ TRACE("read %s\n", debugstr_an(temp,len));
+
+ /* convert to unicode if necessary */
+ if( !unicode )
+ {
+ count = MultiByteToWideChar( CP_ACP, 0, temp, len, NULL, 0 );
+ str = HeapAlloc( GetProcessHeap(), 0, (count+1)*sizeof (WCHAR) );
+ if( !str )
+ {
+ HeapFree( GetProcessHeap(), 0, temp );
+ return E_OUTOFMEMORY;
+ }
+ MultiByteToWideChar( CP_ACP, 0, temp, len, str, count );
+ HeapFree( GetProcessHeap(), 0, temp );
+ }
+ else
+ {
+ count /= 2;
+ str = temp;
+ }
+ str[count] = 0;
+
+ *pstr = str;
+
+ return S_OK;
+}
+
+static HRESULT Stream_ReadChunk( IStream* stm, LPVOID *data )
+{
+ DWORD size;
+ ULONG count;
+ HRESULT r;
+ struct sized_chunk {
+ DWORD size;
+ unsigned char data[1];
+ } *chunk;
+
+ TRACE("%p\n",stm);
+
+ r = IStream_Read( stm, &size, sizeof(size), &count );
+ if( FAILED( r ) || count != sizeof(size) )
+ return E_FAIL;
+
+ chunk = HeapAlloc( GetProcessHeap(), 0, size );
+ if( !chunk )
+ return E_OUTOFMEMORY;
+
+ chunk->size = size;
+ r = IStream_Read( stm, chunk->data, size - sizeof(size), &count );
+ if( FAILED( r ) || count != (size - sizeof(size)) )
+ {
+ HeapFree( GetProcessHeap(), 0, chunk );
+ return E_FAIL;
+ }
+
+ TRACE("Read %d bytes\n",chunk->size);
+
+ *data = chunk;
+
+ return S_OK;
+}
+
+static BOOL Stream_LoadVolume( LOCAL_VOLUME_INFO *vol, volume_info *volume )
+{
+ const int label_sz = sizeof volume->label/sizeof volume->label[0];
+ LPSTR label;
+ int len;
+
+ volume->serial = vol->dwVolSerial;
+ volume->type = vol->dwType;
+
+ if( !vol->dwVolLabelOfs )
+ return FALSE;
+ if( vol->dwSize <= vol->dwVolLabelOfs )
+ return FALSE;
+ len = vol->dwSize - vol->dwVolLabelOfs;
+
+ label = (LPSTR) vol;
+ label += vol->dwVolLabelOfs;
+ MultiByteToWideChar( CP_ACP, 0, label, len, volume->label, label_sz-1);
+
+ return TRUE;
+}
+
+static LPWSTR Stream_LoadPath( LPCSTR p, DWORD maxlen )
+{
+ int len = 0, wlen;
+ LPWSTR path;
+
+ while( p[len] && (len < maxlen) )
+ len++;
+
+ wlen = MultiByteToWideChar(CP_ACP, 0, p, len, NULL, 0);
+ path = HeapAlloc(GetProcessHeap(), 0, (wlen+1)*sizeof(WCHAR));
+ MultiByteToWideChar(CP_ACP, 0, p, len, path, wlen);
+ path[wlen] = 0;
+
+ return path;
+}
+
+static HRESULT Stream_LoadLocation( IStream *stm,
+ volume_info *volume, LPWSTR *path )
+{
+ char *p = NULL;
+ LOCATION_INFO *loc;
+ HRESULT r;
+ DWORD n;
+
+ r = Stream_ReadChunk( stm, (LPVOID*) &p );
+ if( FAILED(r) )
+ return r;
+
+ loc = (LOCATION_INFO*) p;
+ if (loc->dwTotalSize < sizeof(LOCATION_INFO))
+ {
+ HeapFree( GetProcessHeap(), 0, p );
+ return E_FAIL;
+ }
+
+ /* if there's valid local volume information, load it */
+ if( loc->dwVolTableOfs &&
+ ((loc->dwVolTableOfs + sizeof(LOCAL_VOLUME_INFO)) <= loc->dwTotalSize) )
+ {
+ LOCAL_VOLUME_INFO *volume_info;
+
+ volume_info = (LOCAL_VOLUME_INFO*) &p[loc->dwVolTableOfs];
+ Stream_LoadVolume( volume_info, volume );
+ }
+
+ /* if there's a local path, load it */
+ n = loc->dwLocalPathOfs;
+ if( n && (n < loc->dwTotalSize) )
+ *path = Stream_LoadPath( &p[n], loc->dwTotalSize - n );
+
+ TRACE("type %d serial %08x name %s path %s\n", volume->type,
+ volume->serial, debugstr_w(volume->label), debugstr_w(*path));
+
+ HeapFree( GetProcessHeap(), 0, p );
+ return S_OK;
+}
+
+/*
+ * The format of the advertised shortcut info seems to be:
+ *
+ * Offset Description
+ * ------ -----------
+ *
+ * 0 Length of the block (4 bytes, usually 0x314)
+ * 4 tag (dword)
+ * 8 string data in ASCII
+ * 8+0x104 string data in UNICODE
+ *
+ * In the original Win32 implementation the buffers are not initialized
+ * to zero, so data trailing the string is random garbage.
+ */
+static HRESULT Stream_LoadAdvertiseInfo( IStream* stm, LPWSTR *str )
+{
+ DWORD size;
+ ULONG count;
+ HRESULT r;
+ EXP_DARWIN_LINK buffer;
+
+ TRACE("%p\n",stm);
+
+ r = IStream_Read( stm, &buffer.dbh.cbSize, sizeof (DWORD), &count );
+ if( FAILED( r ) )
+ return r;
+
+ /* make sure that we read the size of the structure even on error */
+ size = sizeof buffer - sizeof (DWORD);
+ if( buffer.dbh.cbSize != sizeof buffer )
+ {
+ ERR("Ooops. This structure is not as expected...\n");
+ return E_FAIL;
+ }
+
+ r = IStream_Read( stm, &buffer.dbh.dwSignature, size, &count );
+ if( FAILED( r ) )
+ return r;
+
+ if( count != size )
+ return E_FAIL;
+
+ TRACE("magic %08x string = %s\n", buffer.dbh.dwSignature, debugstr_w(buffer.szwDarwinID));
+
+ if( (buffer.dbh.dwSignature&0xffff0000) != 0xa0000000 )
+ {
+ ERR("Unknown magic number %08x in advertised shortcut\n", buffer.dbh.dwSignature);
+ return E_FAIL;
+ }
+
+ *str = HeapAlloc( GetProcessHeap(), 0,
+ (wcslen(buffer.szwDarwinID)+1) * sizeof(WCHAR) );
+ wcscpy( *str, buffer.szwDarwinID );
+
+ return S_OK;
+}
+
+/************************************************************************
+ * IPersistStream_Load (IPersistStream)
+ */
+static HRESULT WINAPI IPersistStream_fnLoad(
+ IPersistStream* iface,
+ IStream* stm)
+{
+ LINK_HEADER hdr;
+ ULONG dwBytesRead;
+ BOOL unicode;
+ HRESULT r;
+ DWORD zero;
+
+ IShellLinkImpl *This = impl_from_IPersistStream(iface);
+
+ TRACE("%p %p\n", This, stm);
+
+ if( !stm )
+ return STG_E_INVALIDPOINTER;
+
+ dwBytesRead = 0;
+ r = IStream_Read(stm, &hdr, sizeof(hdr), &dwBytesRead);
+ if( FAILED( r ) )
+ return r;
+
+ if( dwBytesRead != sizeof(hdr))
+ return E_FAIL;
+ if( hdr.dwSize != sizeof(hdr))
+ return E_FAIL;
+ if( !IsEqualIID(&hdr.MagicGuid, &CLSID_ShellLink) )
+ return E_FAIL;
+
+ /* free all the old stuff */
+ ILFree(This->pPidl);
+ This->pPidl = NULL;
+ memset( &This->volume, 0, sizeof This->volume );
+ HeapFree(GetProcessHeap(), 0, This->sPath);
+ This->sPath = NULL;
+ HeapFree(GetProcessHeap(), 0, This->sDescription);
+ This->sDescription = NULL;
+ HeapFree(GetProcessHeap(), 0, This->sPathRel);
+ This->sPathRel = NULL;
+ HeapFree(GetProcessHeap(), 0, This->sWorkDir);
+ This->sWorkDir = NULL;
+ HeapFree(GetProcessHeap(), 0, This->sArgs);
+ This->sArgs = NULL;
+ HeapFree(GetProcessHeap(), 0, This->sIcoPath);
+ This->sIcoPath = NULL;
+ HeapFree(GetProcessHeap(), 0, This->sProduct);
+ This->sProduct = NULL;
+ HeapFree(GetProcessHeap(), 0, This->sComponent);
+ This->sComponent = NULL;
+
+ This->wHotKey = (WORD)hdr.wHotKey;
+ This->iIcoNdx = hdr.nIcon;
+ FileTimeToSystemTime (&hdr.Time1, &This->time1);
+ FileTimeToSystemTime (&hdr.Time2, &This->time2);
+ FileTimeToSystemTime (&hdr.Time3, &This->time3);
+ if (TRACE_ON(shell))
+ {
+ WCHAR sTemp[MAX_PATH];
+ GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE, &This->time1,
+ NULL, sTemp, sizeof(sTemp)/sizeof(*sTemp));
+ TRACE("-- time1: %s\n", debugstr_w(sTemp) );
+ GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE, &This->time2,
+ NULL, sTemp, sizeof(sTemp)/sizeof(*sTemp));
+ TRACE("-- time2: %s\n", debugstr_w(sTemp) );
+ GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE, &This->time3,
+ NULL, sTemp, sizeof(sTemp)/sizeof(*sTemp));
+ TRACE("-- time3: %s\n", debugstr_w(sTemp) );
+ }
+
+ /* load all the new stuff */
+ if( hdr.dwFlags & SLDF_HAS_ID_LIST )
+ {
+ r = ILLoadFromStream( stm, &This->pPidl );
+ if( FAILED( r ) )
+ return r;
+ }
+ pdump(This->pPidl);
+
+ /* load the location information */
+ if( hdr.dwFlags & SLDF_HAS_LINK_INFO )
+ r = Stream_LoadLocation( stm, &This->volume, &This->sPath );
+ if( FAILED( r ) )
+ goto end;
+
+ unicode = hdr.dwFlags & SLDF_UNICODE;
+ if( hdr.dwFlags & SLDF_HAS_NAME )
+ {
+ r = Stream_LoadString( stm, unicode, &This->sDescription );
+ TRACE("Description -> %s\n",debugstr_w(This->sDescription));
+ }
+ if( FAILED( r ) )
+ goto end;
+
+ if( hdr.dwFlags & SLDF_HAS_RELPATH )
+ {
+ r = Stream_LoadString( stm, unicode, &This->sPathRel );
+ TRACE("Relative Path-> %s\n",debugstr_w(This->sPathRel));
+ }
+ if( FAILED( r ) )
+ goto end;
+
+ if( hdr.dwFlags & SLDF_HAS_WORKINGDIR )
+ {
+ r = Stream_LoadString( stm, unicode, &This->sWorkDir );
+ TRACE("Working Dir -> %s\n",debugstr_w(This->sWorkDir));
+ }
+ if( FAILED( r ) )
+ goto end;
+
+ if( hdr.dwFlags & SLDF_HAS_ARGS )
+ {
+ r = Stream_LoadString( stm, unicode, &This->sArgs );
+ TRACE("Working Dir -> %s\n",debugstr_w(This->sArgs));
+ }
+ if( FAILED( r ) )
+ goto end;
+
+ if( hdr.dwFlags & SLDF_HAS_ICONLOCATION )
+ {
+ r = Stream_LoadString( stm, unicode, &This->sIcoPath );
+ TRACE("Icon file -> %s\n",debugstr_w(This->sIcoPath));
+ }
+ if( FAILED( r ) )
+ goto end;
+
+#if (NTDDI_VERSION < NTDDI_LONGHORN)
+ if( hdr.dwFlags & SLDF_HAS_LOGO3ID )
+ {
+ r = Stream_LoadAdvertiseInfo( stm, &This->sProduct );
+ TRACE("Product -> %s\n",debugstr_w(This->sProduct));
+ }
+ if( FAILED( r ) )
+ goto end;
+#endif
+
+ if( hdr.dwFlags & SLDF_HAS_DARWINID )
+ {
+ r = Stream_LoadAdvertiseInfo( stm, &This->sComponent );
+ TRACE("Component -> %s\n",debugstr_w(This->sComponent));
+ }
+ if( hdr.dwFlags & SLDF_RUNAS_USER )
+ {
+ This->bRunAs = TRUE;
+ }
+ else
+ {
+ This->bRunAs = FALSE;
+ }
+
+ if( FAILED( r ) )
+ goto end;
+
+ r = IStream_Read(stm, &zero, sizeof zero, &dwBytesRead);
+ if( FAILED( r ) || zero || dwBytesRead != sizeof zero )
+ ERR("Last word was not zero\n");
+
+ TRACE("OK\n");
+
+ pdump (This->pPidl);
+
+ return S_OK;
+end:
+ return r;
+}
+
+/************************************************************************
+ * Stream_WriteString
+ *
+ * Helper function for IPersistStream_Save. Writes a unicode string
+ * with terminating nul byte to a stream, preceded by the its length.
+ */
+static HRESULT Stream_WriteString( IStream* stm, LPCWSTR str )
+{
+ USHORT len = wcslen( str ) + 1;
+ DWORD count;
+ HRESULT r;
+
+ r = IStream_Write( stm, &len, sizeof(len), &count );
+ if( FAILED( r ) )
+ return r;
+
+ len *= sizeof(WCHAR);
+
+ r = IStream_Write( stm, str, len, &count );
+ if( FAILED( r ) )
+ return r;
+
+ return S_OK;
+}
+
+/************************************************************************
+ * Stream_WriteLocationInfo
+ *
+ * Writes the location info to a stream
+ *
+ * FIXME: One day we might want to write the network volume information
+ * and the final path.
+ * Figure out how Windows deals with unicode paths here.
+ */
+static HRESULT Stream_WriteLocationInfo( IStream* stm, LPCWSTR path,
+ volume_info *volume )
+{
+ DWORD total_size, path_size, volume_info_size, label_size, final_path_size;
+ LOCAL_VOLUME_INFO *vol;
+ LOCATION_INFO *loc;
+ LPSTR szLabel, szPath, szFinalPath;
+ ULONG count = 0;
+ HRESULT hr;
+
+ TRACE("%p %s %p\n", stm, debugstr_w(path), volume);
+
+ /* figure out the size of everything */
+ label_size = WideCharToMultiByte( CP_ACP, 0, volume->label, -1,
+ NULL, 0, NULL, NULL );
+ path_size = WideCharToMultiByte( CP_ACP, 0, path, -1,
+ NULL, 0, NULL, NULL );
+ volume_info_size = sizeof *vol + label_size;
+ final_path_size = 1;
+ total_size = sizeof *loc + volume_info_size + path_size + final_path_size;
+
+ /* create pointers to everything */
+ loc = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, total_size);
+ vol = (LOCAL_VOLUME_INFO*) &loc[1];
+ szLabel = (LPSTR) &vol[1];
+ szPath = &szLabel[label_size];
+ szFinalPath = &szPath[path_size];
+
+ /* fill in the location information header */
+ loc->dwTotalSize = total_size;
+ loc->dwHeaderSize = sizeof (*loc);
+ loc->dwFlags = 1;
+ loc->dwVolTableOfs = sizeof (*loc);
+ loc->dwLocalPathOfs = sizeof (*loc) + volume_info_size;
+ loc->dwNetworkVolTableOfs = 0;
+ loc->dwFinalPathOfs = sizeof (*loc) + volume_info_size + path_size;
+
+ /* fill in the volume information */
+ vol->dwSize = volume_info_size;
+ vol->dwType = volume->type;
+ vol->dwVolSerial = volume->serial;
+ vol->dwVolLabelOfs = sizeof (*vol);
+
+ /* copy in the strings */
+ WideCharToMultiByte( CP_ACP, 0, volume->label, -1,
+ szLabel, label_size, NULL, NULL );
+ WideCharToMultiByte( CP_ACP, 0, path, -1,
+ szPath, path_size, NULL, NULL );
+ szFinalPath[0] = 0;
+
+ hr = IStream_Write( stm, loc, total_size, &count );
+ HeapFree(GetProcessHeap(), 0, loc);
+
+ return hr;
+}
+
+static EXP_DARWIN_LINK* shelllink_build_darwinid( LPCWSTR string, DWORD magic )
+{
+ EXP_DARWIN_LINK *buffer;
+
+ buffer = LocalAlloc( LMEM_ZEROINIT, sizeof *buffer );
+ buffer->dbh.cbSize = sizeof *buffer;
+ buffer->dbh.dwSignature = magic;
+ lstrcpynW( buffer->szwDarwinID, string, MAX_PATH );
+ WideCharToMultiByte(CP_ACP, 0, string, -1, buffer->szDarwinID, MAX_PATH, NULL, NULL );
+
+ return buffer;
+}
+
+static HRESULT Stream_WriteAdvertiseInfo( IStream* stm, LPCWSTR string, DWORD magic )
+{
+ EXP_DARWIN_LINK *buffer;
+ ULONG count;
+
+ TRACE("%p\n",stm);
+
+ buffer = shelllink_build_darwinid( string, magic );
+
+ return IStream_Write( stm, buffer, buffer->dbh.cbSize, &count );
+}
+
+/************************************************************************
+ * IPersistStream_Save (IPersistStream)
+ *
+ * FIXME: makes assumptions about byte order
+ */
+static HRESULT WINAPI IPersistStream_fnSave(
+ IPersistStream* iface,
+ IStream* stm,
+ BOOL fClearDirty)
+{
+ LINK_HEADER header;
+ ULONG count;
+ DWORD zero;
+ HRESULT r;
+
+ IShellLinkImpl *This = impl_from_IPersistStream(iface);
+
+ TRACE("%p %p %x\n", This, stm, fClearDirty);
+
+ memset(&header, 0, sizeof(header));
+ header.dwSize = sizeof(header);
+ header.fStartup = This->iShowCmd;
+ header.MagicGuid = CLSID_ShellLink;
+
+ header.wHotKey = This->wHotKey;
+ header.nIcon = This->iIcoNdx;
+ header.dwFlags = SLDF_UNICODE; /* strings are in unicode */
+ if( This->pPidl )
+ header.dwFlags |= SLDF_HAS_ID_LIST;
+ if( This->sPath )
+ header.dwFlags |= SLDF_HAS_LINK_INFO;
+ if( This->sDescription )
+ header.dwFlags |= SLDF_HAS_NAME;
+ if( This->sWorkDir )
+ header.dwFlags |= SLDF_HAS_WORKINGDIR;
+ if( This->sArgs )
+ header.dwFlags |= SLDF_HAS_ARGS;
+ if( This->sIcoPath )
+ header.dwFlags |= SLDF_HAS_ICONLOCATION;
+#if (NTDDI_VERSION < NTDDI_LONGHORN)
+ if( This->sProduct )
+ header.dwFlags |= SLDF_HAS_LOGO3ID;
+#endif
+ if( This->sComponent )
+ header.dwFlags |= SLDF_HAS_DARWINID;
+ if( This->bRunAs )
+ header.dwFlags |= SLDF_RUNAS_USER;
+
+ SystemTimeToFileTime ( &This->time1, &header.Time1 );
+ SystemTimeToFileTime ( &This->time2, &header.Time2 );
+ SystemTimeToFileTime ( &This->time3, &header.Time3 );
+
+ /* write the Shortcut header */
+ r = IStream_Write( stm, &header, sizeof(header), &count );
+ if( FAILED( r ) )
+ {
+ ERR("Write failed at %d\n",__LINE__);
+ return r;
+ }
+
+ TRACE("Writing pidl\n");
+
+ /* write the PIDL to the shortcut */
+ if( This->pPidl )
+ {
+ r = ILSaveToStream( stm, This->pPidl );
+ if( FAILED( r ) )
+ {
+ ERR("Failed to write PIDL at %d\n",__LINE__);
+ return r;
+ }
+ }
+
+ if( This->sPath )
+ Stream_WriteLocationInfo( stm, This->sPath, &This->volume );
+
+ if( This->sDescription )
+ r = Stream_WriteString( stm, This->sDescription );
+
+ if( This->sPathRel )
+ r = Stream_WriteString( stm, This->sPathRel );
+
+ if( This->sWorkDir )
+ r = Stream_WriteString( stm, This->sWorkDir );
+
+ if( This->sArgs )
+ r = Stream_WriteString( stm, This->sArgs );
+
+ if( This->sIcoPath )
+ r = Stream_WriteString( stm, This->sIcoPath );
+
+ if( This->sProduct )
+ r = Stream_WriteAdvertiseInfo( stm, This->sProduct, EXP_SZ_ICON_SIG );
+
+ if( This->sComponent )
+ r = Stream_WriteAdvertiseInfo( stm, This->sComponent, EXP_DARWIN_ID_SIG );
+
+ /* the last field is a single zero dword */
+ zero = 0;
+ r = IStream_Write( stm, &zero, sizeof zero, &count );
+
+ return S_OK;
+}
+
+/************************************************************************
+ * IPersistStream_GetSizeMax (IPersistStream)
+ */
+static HRESULT WINAPI IPersistStream_fnGetSizeMax(
+ IPersistStream* iface,
+ ULARGE_INTEGER* pcbSize)
+{
+ IShellLinkImpl *This = impl_from_IPersistStream(iface);
+
+ TRACE("(%p)\n", This);
+
+ return E_NOTIMPL;
+}
+
+static const IPersistStreamVtbl psvt =
+{
+ IPersistStream_fnQueryInterface,
+ IPersistStream_fnAddRef,
+ IPersistStream_fnRelease,
+ IPersistStream_fnGetClassID,
+ IPersistStream_fnIsDirty,
+ IPersistStream_fnLoad,
+ IPersistStream_fnSave,
+ IPersistStream_fnGetSizeMax
+};
+
+/**************************************************************************
+ * IShellLink_Constructor
+ */
+HRESULT WINAPI IShellLink_Constructor( IUnknown *pUnkOuter,
+ REFIID riid, LPVOID *ppv )
+{
+ IShellLinkImpl * sl;
+ HRESULT r;
+
+ TRACE("unkOut=%p riid=%s\n",pUnkOuter, debugstr_guid(riid));
+
+ *ppv = NULL;
+
+ if (pUnkOuter)
+ return CLASS_E_NOAGGREGATION;
+ sl = LocalAlloc(LMEM_ZEROINIT,sizeof(IShellLinkImpl));
+ if (!sl)
+ return E_OUTOFMEMORY;
+
+ sl->ref = 1;
+ sl->lpVtbl = &slvt;
+ sl->lpvtblw = &slvtw;
+ sl->lpvtblPersistFile = &pfvt;
+ sl->lpvtblPersistStream = &psvt;
+ sl->lpvtblShellLinkDataList = &dlvt;
+ sl->lpvtblShellExtInit = &eivt;
+ sl->lpvtblContextMenu = &cmvt;
+ sl->lpvtblObjectWithSite = &owsvt;
+ sl->lpvtblPropSheetExt = &pse;
+ sl->iShowCmd = SW_SHOWNORMAL;
+ sl->bDirty = FALSE;
+ sl->iIdOpen = -1;
+ sl->site = NULL;
+ sl->bRunAs = FALSE;
+
+ TRACE("(%p)->()\n",sl);
+
+ r = ShellLink_QueryInterface( sl, riid, ppv );
+ ShellLink_Release( sl );
+ return r;
+}
+
+
+static BOOL SHELL_ExistsFileW(LPCWSTR path)
+{
+ if (INVALID_FILE_ATTRIBUTES == GetFileAttributesW(path))
+ return FALSE;
+ return TRUE;
+}
+
+/**************************************************************************
+ * ShellLink_UpdatePath
+ * update absolute path in sPath using relative path in sPathRel
+ */
+static HRESULT ShellLink_UpdatePath(LPCWSTR sPathRel, LPCWSTR path, LPCWSTR sWorkDir, LPWSTR* psPath)
+{
+ if (!path || !psPath)
+ return E_INVALIDARG;
+
+ if (!*psPath && sPathRel) {
+ WCHAR buffer[2*MAX_PATH], abs_path[2*MAX_PATH];
+ LPWSTR final = NULL;
+
+ /* first try if [directory of link file] + [relative path] finds an existing file */
+
+ GetFullPathNameW( path, MAX_PATH*2, buffer, &final );
+ if( !final )
+ final = buffer;
+ wcscpy(final, sPathRel);
+
+ *abs_path = '\0';
+
+ if (SHELL_ExistsFileW(buffer)) {
+ if (!GetFullPathNameW(buffer, MAX_PATH, abs_path, &final))
+ wcscpy(abs_path, buffer);
+ } else {
+ /* try if [working directory] + [relative path] finds an existing file */
+ if (sWorkDir) {
+ wcscpy(buffer, sWorkDir);
+ wcscpy(PathAddBackslashW(buffer), sPathRel);
+
+ if (SHELL_ExistsFileW(buffer))
+ if (!GetFullPathNameW(buffer, MAX_PATH, abs_path, &final))
+ wcscpy(abs_path, buffer);
+ }
+ }
+
+ /* FIXME: This is even not enough - not all shell links can be resolved using this algorithm. */
+ if (!*abs_path)
+ wcscpy(abs_path, sPathRel);
+
+ *psPath = HeapAlloc(GetProcessHeap(), 0, (wcslen(abs_path)+1)*sizeof(WCHAR));
+ if (!*psPath)
+ return E_OUTOFMEMORY;
+
+ wcscpy(*psPath, abs_path);
+ }
+
+ return S_OK;
+}
+
+/**************************************************************************
+ * IShellLink_ConstructFromFile
+ */
+HRESULT WINAPI IShellLink_ConstructFromFile( IUnknown* pUnkOuter, REFIID riid,
+ LPCITEMIDLIST pidl, LPVOID* ppv)
+{
+ IShellLinkW* psl;
+
+ HRESULT hr = IShellLink_Constructor(NULL, riid, (LPVOID*)&psl);
+
+ if (SUCCEEDED(hr)) {
+ IPersistFile* ppf;
+
+ *ppv = NULL;
+
+ hr = IShellLinkW_QueryInterface(psl, &IID_IPersistFile, (LPVOID*)&ppf);
+
+ if (SUCCEEDED(hr)) {
+ WCHAR path[MAX_PATH];
+
+ if (SHGetPathFromIDListW(pidl, path))
+ hr = IPersistFile_Load(ppf, path, 0);
+ else
+ hr = E_FAIL;
+
+ if (SUCCEEDED(hr))
+ *ppv = psl;
+
+ IPersistFile_Release(ppf);
+ }
+
+ if (!*ppv)
+ IShellLinkW_Release(psl);
+ }
+
+ return hr;
+}
+
+/**************************************************************************
+ * IShellLinkA_QueryInterface
+ */
+static HRESULT WINAPI IShellLinkA_fnQueryInterface( IShellLinkA * iface, REFIID riid, LPVOID *ppvObj)
+{
+ IShellLinkImpl *This = (IShellLinkImpl *)iface;
+ return ShellLink_QueryInterface( This, riid, ppvObj );
+}
+
+/******************************************************************************
+ * IShellLinkA_AddRef
+ */
+static ULONG WINAPI IShellLinkA_fnAddRef(IShellLinkA * iface)
+{
+ IShellLinkImpl *This = (IShellLinkImpl *)iface;
+ return ShellLink_AddRef( This );
+}
+
+/******************************************************************************
+ * IShellLinkA_Release
+ */
+static ULONG WINAPI IShellLinkA_fnRelease(IShellLinkA * iface)
+{
+ IShellLinkImpl *This = (IShellLinkImpl *)iface;
+ return ShellLink_Release( This );
+}
+
+static HRESULT WINAPI IShellLinkA_fnGetPath(IShellLinkA * iface, LPSTR pszFile,
+ INT cchMaxPath, WIN32_FIND_DATAA *pfd, DWORD fFlags)
+{
+ IShellLinkImpl *This = (IShellLinkImpl *)iface;
+
+ TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%u)(%s)\n",
+ This, pszFile, cchMaxPath, pfd, fFlags, debugstr_w(This->sPath));
+
- This->sDescription = HEAP_strdupAtoW( GetProcessHeap(), 0, pszName);
- if ( !This->sDescription )
- return E_OUTOFMEMORY;
+ if (cchMaxPath)
+ pszFile[0] = 0;
+ if (This->sPath)
+ WideCharToMultiByte( CP_ACP, 0, This->sPath, -1,
+ pszFile, cchMaxPath, NULL, NULL);
+
+ if (pfd) FIXME("(%p): WIN32_FIND_DATA is not yet filled.\n", This);
+
+ return S_OK;
+}
+
+static HRESULT WINAPI IShellLinkA_fnGetIDList(IShellLinkA * iface, LPITEMIDLIST * ppidl)
+{
+ IShellLinkImpl *This = (IShellLinkImpl *)iface;
+
+ TRACE("(%p)->(ppidl=%p)\n",This, ppidl);
+
+ return IShellLinkW_GetIDList((IShellLinkW*)&(This->lpvtblw), ppidl);
+}
+
+static HRESULT WINAPI IShellLinkA_fnSetIDList(IShellLinkA * iface, LPCITEMIDLIST pidl)
+{
+ IShellLinkImpl *This = (IShellLinkImpl *)iface;
+
+ TRACE("(%p)->(pidl=%p)\n",This, pidl);
+
+ if (This->pPidl)
+ ILFree(This->pPidl);
+ This->pPidl = ILClone (pidl);
+ This->bDirty = TRUE;
+
+ return S_OK;
+}
+
+static HRESULT WINAPI IShellLinkA_fnGetDescription(IShellLinkA * iface, LPSTR pszName,INT cchMaxName)
+{
+ IShellLinkImpl *This = (IShellLinkImpl *)iface;
+
+ TRACE("(%p)->(%p len=%u)\n",This, pszName, cchMaxName);
+
+ if( cchMaxName )
+ pszName[0] = 0;
+ if( This->sDescription )
+ WideCharToMultiByte( CP_ACP, 0, This->sDescription, -1,
+ pszName, cchMaxName, NULL, NULL);
+
+ return S_OK;
+}
+
+static HRESULT WINAPI IShellLinkA_fnSetDescription(IShellLinkA * iface, LPCSTR pszName)
+{
+ IShellLinkImpl *This = (IShellLinkImpl *)iface;
+
+ TRACE("(%p)->(pName=%s)\n", This, pszName);
+
+ HeapFree(GetProcessHeap(), 0, This->sDescription);
- This->sWorkDir = HEAP_strdupAtoW( GetProcessHeap(), 0, pszDir);
- if ( !This->sWorkDir )
- return E_OUTOFMEMORY;
++ This->sDescription = NULL;
+
++ if ( pszName ) {
++ This->sDescription = HEAP_strdupAtoW( GetProcessHeap(), 0, pszName);
++ if ( !This->sDescription )
++ return E_OUTOFMEMORY;
++ }
+ This->bDirty = TRUE;
+
+ return S_OK;
+}
+
+static HRESULT WINAPI IShellLinkA_fnGetWorkingDirectory(IShellLinkA * iface, LPSTR pszDir,INT cchMaxPath)
+{
+ IShellLinkImpl *This = (IShellLinkImpl *)iface;
+
+ TRACE("(%p)->(%p len=%u)\n", This, pszDir, cchMaxPath);
+
+ if( cchMaxPath )
+ pszDir[0] = 0;
+ if( This->sWorkDir )
+ WideCharToMultiByte( CP_ACP, 0, This->sWorkDir, -1,
+ pszDir, cchMaxPath, NULL, NULL);
+
+ return S_OK;
+}
+
+static HRESULT WINAPI IShellLinkA_fnSetWorkingDirectory(IShellLinkA * iface, LPCSTR pszDir)
+{
+ IShellLinkImpl *This = (IShellLinkImpl *)iface;
+
+ TRACE("(%p)->(dir=%s)\n",This, pszDir);
+
+ HeapFree(GetProcessHeap(), 0, This->sWorkDir);
- This->sArgs = HEAP_strdupAtoW( GetProcessHeap(), 0, pszArgs);
- if( !This->sArgs )
- return E_OUTOFMEMORY;
++ This->sWorkDir = NULL;
+
++ if ( pszDir ) {
++ This->sWorkDir = HEAP_strdupAtoW( GetProcessHeap(), 0, pszDir);
++ if ( !This->sWorkDir )
++ return E_OUTOFMEMORY;
++ }
+ This->bDirty = TRUE;
+
+ return S_OK;
+}
+
+static HRESULT WINAPI IShellLinkA_fnGetArguments(IShellLinkA * iface, LPSTR pszArgs,INT cchMaxPath)
+{
+ IShellLinkImpl *This = (IShellLinkImpl *)iface;
+
+ TRACE("(%p)->(%p len=%u)\n", This, pszArgs, cchMaxPath);
+
+ if( cchMaxPath )
+ pszArgs[0] = 0;
+ if( This->sArgs )
+ WideCharToMultiByte( CP_ACP, 0, This->sArgs, -1,
+ pszArgs, cchMaxPath, NULL, NULL);
+
+ return S_OK;
+}
+
+static HRESULT WINAPI IShellLinkA_fnSetArguments(IShellLinkA * iface, LPCSTR pszArgs)
+{
+ IShellLinkImpl *This = (IShellLinkImpl *)iface;
+
+ TRACE("(%p)->(args=%s)\n",This, pszArgs);
+
+ HeapFree(GetProcessHeap(), 0, This->sArgs);
- This->sIcoPath = HEAP_strdupAtoW(GetProcessHeap(), 0, pszIconPath);
- if ( !This->sIcoPath )
- return E_OUTOFMEMORY;
++ This->sArgs = NULL;
++
++ if ( pszArgs ) {
++ This->sArgs = HEAP_strdupAtoW( GetProcessHeap(), 0, pszArgs);
++ if( !This->sArgs )
++ return E_OUTOFMEMORY;
++ }
+
+ This->bDirty = TRUE;
+
+ return S_OK;
+}
+
+static HRESULT WINAPI IShellLinkA_fnGetHotkey(IShellLinkA * iface, WORD *pwHotkey)
+{
+ IShellLinkImpl *This = (IShellLinkImpl *)iface;
+
+ TRACE("(%p)->(%p)(0x%08x)\n",This, pwHotkey, This->wHotKey);
+
+ *pwHotkey = This->wHotKey;
+
+ return S_OK;
+}
+
+static HRESULT WINAPI IShellLinkA_fnSetHotkey(IShellLinkA * iface, WORD wHotkey)
+{
+ IShellLinkImpl *This = (IShellLinkImpl *)iface;
+
+ TRACE("(%p)->(hotkey=%x)\n",This, wHotkey);
+
+ This->wHotKey = wHotkey;
+ This->bDirty = TRUE;
+
+ return S_OK;
+}
+
+static HRESULT WINAPI IShellLinkA_fnGetShowCmd(IShellLinkA * iface, INT *piShowCmd)
+{
+ IShellLinkImpl *This = (IShellLinkImpl *)iface;
+
+ TRACE("(%p)->(%p)\n",This, piShowCmd);
+ *piShowCmd = This->iShowCmd;
+ return S_OK;
+}
+
+static HRESULT WINAPI IShellLinkA_fnSetShowCmd(IShellLinkA * iface, INT iShowCmd)
+{
+ IShellLinkImpl *This = (IShellLinkImpl *)iface;
+
+ TRACE("(%p) %d\n",This, iShowCmd);
+
+ This->iShowCmd = iShowCmd;
+ This->bDirty = TRUE;
+
+ return NOERROR;
+}
+
+static HRESULT SHELL_PidlGeticonLocationA(IShellFolder* psf, LPCITEMIDLIST pidl,
+ LPSTR pszIconPath, int cchIconPath, int* piIcon)
+{
+ LPCITEMIDLIST pidlLast;
+
+ HRESULT hr = SHBindToParent(pidl, &IID_IShellFolder, (LPVOID*)&psf, &pidlLast);
+
+ if (SUCCEEDED(hr)) {
+ IExtractIconA* pei;
+
+ hr = IShellFolder_GetUIObjectOf(psf, 0, 1, &pidlLast, &IID_IExtractIconA, NULL, (LPVOID*)&pei);
+
+ if (SUCCEEDED(hr)) {
+ hr = IExtractIconA_GetIconLocation(pei, 0, pszIconPath, MAX_PATH, piIcon, NULL);
+
+ IExtractIconA_Release(pei);
+ }
+
+ IShellFolder_Release(psf);
+ }
+
+ return hr;
+}
+
+static HRESULT WINAPI IShellLinkA_fnGetIconLocation(IShellLinkA * iface, LPSTR pszIconPath,INT cchIconPath,INT *piIcon)
+{
+ IShellLinkImpl *This = (IShellLinkImpl *)iface;
+
+ TRACE("(%p)->(%p len=%u iicon=%p)\n", This, pszIconPath, cchIconPath, piIcon);
+
+ pszIconPath[0] = 0;
+ *piIcon = This->iIcoNdx;
+
+ if (This->sIcoPath)
+ {
+ WideCharToMultiByte(CP_ACP, 0, This->sIcoPath, -1, pszIconPath, cchIconPath, NULL, NULL);
+ return S_OK;
+ }
+
+ if (This->pPidl || This->sPath)
+ {
+ IShellFolder* pdsk;
+
+ HRESULT hr = SHGetDesktopFolder(&pdsk);
+
+ if (SUCCEEDED(hr))
+ {
+ /* first look for an icon using the PIDL (if present) */
+ if (This->pPidl)
+ hr = SHELL_PidlGeticonLocationA(pdsk, This->pPidl, pszIconPath, cchIconPath, piIcon);
+ else
+ hr = E_FAIL;
+
+ /* if we couldn't find an icon yet, look for it using the file system path */
+ if (FAILED(hr) && This->sPath)
+ {
+ LPITEMIDLIST pidl;
+
+ hr = IShellFolder_ParseDisplayName(pdsk, 0, NULL, This->sPath, NULL, &pidl, NULL);
+
+ if (SUCCEEDED(hr)) {
+ hr = SHELL_PidlGeticonLocationA(pdsk, pidl, pszIconPath, cchIconPath, piIcon);
+
+ SHFree(pidl);
+ }
+ }
+
+ IShellFolder_Release(pdsk);
+ }
+
+ return hr;
+ }
+ return S_OK;
+}
+
+static HRESULT WINAPI IShellLinkA_fnSetIconLocation(IShellLinkA * iface, LPCSTR pszIconPath,INT iIcon)
+{
+ IShellLinkImpl *This = (IShellLinkImpl *)iface;
+
+ TRACE("(%p)->(path=%s iicon=%u)\n",This, pszIconPath, iIcon);
+
+ HeapFree(GetProcessHeap(), 0, This->sIcoPath);
- This->sPathRel = HEAP_strdupAtoW(GetProcessHeap(), 0, pszPathRel);
++ This->sIcoPath = NULL;
++
++ if ( pszIconPath ) {
++ This->sIcoPath = HEAP_strdupAtoW(GetProcessHeap(), 0, pszIconPath);
++ if ( !This->sIcoPath )
++ return E_OUTOFMEMORY;
++ }
+
+ This->iIcoNdx = iIcon;
+ This->bDirty = TRUE;
+
+ return S_OK;
+}
+
+static HRESULT WINAPI IShellLinkA_fnSetRelativePath(IShellLinkA * iface, LPCSTR pszPathRel, DWORD dwReserved)
+{
+ IShellLinkImpl *This = (IShellLinkImpl *)iface;
+
+ TRACE("(%p)->(path=%s %x)\n",This, pszPathRel, dwReserved);
+
+ HeapFree(GetProcessHeap(), 0, This->sPathRel);
++ This->sPathRel = NULL;
++
++ if ( pszPathRel ) {
++ This->sPathRel = HEAP_strdupAtoW(GetProcessHeap(), 0, pszPathRel);
++
++ if ( !This->sPathRel )
++ return E_OUTOFMEMORY;
++ }
++
+ This->bDirty = TRUE;
+
+ return ShellLink_UpdatePath(This->sPathRel, This->sPath, This->sWorkDir, &This->sPath);
+}
+
+static HRESULT WINAPI IShellLinkA_fnResolve(IShellLinkA * iface, HWND hwnd, DWORD fFlags)
+{
+ IShellLinkImpl *This = (IShellLinkImpl *)iface;
+
+ TRACE("(%p)->(hwnd=%p flags=%x)\n",This, hwnd, fFlags);
+
+ return IShellLinkW_Resolve( (IShellLinkW*)&(This->lpvtblw), hwnd, fFlags );
+}
+
+static HRESULT WINAPI IShellLinkA_fnSetPath(IShellLinkA * iface, LPCSTR pszFile)
+{
+ HRESULT r;
+ LPWSTR str;
+ IShellLinkImpl *This = (IShellLinkImpl *)iface;
+
+ TRACE("(%p)->(path=%s)\n",This, pszFile);
+
+ if (!pszFile) return E_INVALIDARG;
+
+ str = HEAP_strdupAtoW(GetProcessHeap(), 0, pszFile);
+ if( !str )
+ return E_OUTOFMEMORY;
+
+ r = IShellLinkW_SetPath((IShellLinkW*)&(This->lpvtblw), str);
+ HeapFree( GetProcessHeap(), 0, str );
+
+ return r;
+}
+
+/**************************************************************************
+* IShellLink Implementation
+*/
+
+static const IShellLinkAVtbl slvt =
+{
+ IShellLinkA_fnQueryInterface,
+ IShellLinkA_fnAddRef,
+ IShellLinkA_fnRelease,
+ IShellLinkA_fnGetPath,
+ IShellLinkA_fnGetIDList,
+ IShellLinkA_fnSetIDList,
+ IShellLinkA_fnGetDescription,
+ IShellLinkA_fnSetDescription,
+ IShellLinkA_fnGetWorkingDirectory,
+ IShellLinkA_fnSetWorkingDirectory,
+ IShellLinkA_fnGetArguments,
+ IShellLinkA_fnSetArguments,
+ IShellLinkA_fnGetHotkey,
+ IShellLinkA_fnSetHotkey,
+ IShellLinkA_fnGetShowCmd,
+ IShellLinkA_fnSetShowCmd,
+ IShellLinkA_fnGetIconLocation,
+ IShellLinkA_fnSetIconLocation,
+ IShellLinkA_fnSetRelativePath,
+ IShellLinkA_fnResolve,
+ IShellLinkA_fnSetPath
+};
+
+
+/**************************************************************************
+ * IShellLinkW_fnQueryInterface
+ */
+static HRESULT WINAPI IShellLinkW_fnQueryInterface(
+ IShellLinkW * iface, REFIID riid, LPVOID *ppvObj)
+{
+ IShellLinkImpl *This = impl_from_IShellLinkW(iface);
+ return ShellLink_QueryInterface( This, riid, ppvObj );
+}
+
+/******************************************************************************
+ * IShellLinkW_fnAddRef
+ */
+static ULONG WINAPI IShellLinkW_fnAddRef(IShellLinkW * iface)
+{
+ IShellLinkImpl *This = impl_from_IShellLinkW(iface);
+ return ShellLink_AddRef( This );
+}
+
+/******************************************************************************
+ * IShellLinkW_fnRelease
+ */
+static ULONG WINAPI IShellLinkW_fnRelease(IShellLinkW * iface)
+{
+ IShellLinkImpl *This = impl_from_IShellLinkW(iface);
+ return ShellLink_Release( This );
+}
+
+static HRESULT WINAPI IShellLinkW_fnGetPath(IShellLinkW * iface, LPWSTR pszFile,INT cchMaxPath, WIN32_FIND_DATAW *pfd, DWORD fFlags)
+{
+ IShellLinkImpl *This = impl_from_IShellLinkW(iface);
+
+ TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%u)(%s)\n",
+ This, pszFile, cchMaxPath, pfd, fFlags, debugstr_w(This->sPath));
+
+ if (This->sComponent || This->sProduct)
+ return S_FALSE;
+
+ if (cchMaxPath)
+ pszFile[0] = 0;
+ if (This->sPath)
+ lstrcpynW( pszFile, This->sPath, cchMaxPath );
+
+ if (pfd) FIXME("(%p): WIN32_FIND_DATA is not yet filled.\n", This);
+
+ return S_OK;
+}
+
+static HRESULT WINAPI IShellLinkW_fnGetIDList(IShellLinkW * iface, LPITEMIDLIST * ppidl)
+{
+ IShellLinkImpl *This = impl_from_IShellLinkW(iface);
+
+ TRACE("(%p)->(ppidl=%p)\n",This, ppidl);
+
+ if (!This->pPidl)
+ {
+ *ppidl = NULL;
+ return S_FALSE;
+ }
+ *ppidl = ILClone(This->pPidl);
+ return S_OK;
+}
+
+static HRESULT WINAPI IShellLinkW_fnSetIDList(IShellLinkW * iface, LPCITEMIDLIST pidl)
+{
+ IShellLinkImpl *This = impl_from_IShellLinkW(iface);
+
+ TRACE("(%p)->(pidl=%p)\n",This, pidl);
+
+ if( This->pPidl )
+ ILFree( This->pPidl );
+ This->pPidl = ILClone( pidl );
+ if( !This->pPidl )
+ return E_FAIL;
+
+ This->bDirty = TRUE;
+
+ return S_OK;
+}
+
+static HRESULT WINAPI IShellLinkW_fnGetDescription(IShellLinkW * iface, LPWSTR pszName,INT cchMaxName)
+{
+ IShellLinkImpl *This = impl_from_IShellLinkW(iface);
+
+ TRACE("(%p)->(%p len=%u)\n",This, pszName, cchMaxName);
+
+ pszName[0] = 0;
+ if( This->sDescription )
+ lstrcpynW( pszName, This->sDescription, cchMaxName );
+
+ return S_OK;
+}
+
+static HRESULT WINAPI IShellLinkW_fnSetDescription(IShellLinkW * iface, LPCWSTR pszName)
+{
+ IShellLinkImpl *This = impl_from_IShellLinkW(iface);
+
+ TRACE("(%p)->(desc=%s)\n",This, debugstr_w(pszName));
+
+ HeapFree(GetProcessHeap(), 0, This->sDescription);
+ This->sDescription = HeapAlloc( GetProcessHeap(), 0,
+ (wcslen( pszName )+1)*sizeof(WCHAR) );
+ if ( !This->sDescription )
+ return E_OUTOFMEMORY;
+
+ wcscpy( This->sDescription, pszName );
+ This->bDirty = TRUE;
+
+ return S_OK;
+}
+
+static HRESULT WINAPI IShellLinkW_fnGetWorkingDirectory(IShellLinkW * iface, LPWSTR pszDir,INT cchMaxPath)
+{
+ IShellLinkImpl *This = impl_from_IShellLinkW(iface);
+
+ TRACE("(%p)->(%p len %u)\n", This, pszDir, cchMaxPath);
+
+ if( cchMaxPath )
+ pszDir[0] = 0;
+ if( This->sWorkDir )
+ lstrcpynW( pszDir, This->sWorkDir, cchMaxPath );
+
+ return S_OK;
+}
+
+static HRESULT WINAPI IShellLinkW_fnSetWorkingDirectory(IShellLinkW * iface, LPCWSTR pszDir)
+{
+ IShellLinkImpl *This = impl_from_IShellLinkW(iface);
+
+ TRACE("(%p)->(dir=%s)\n",This, debugstr_w(pszDir));
+
+ HeapFree(GetProcessHeap(), 0, This->sWorkDir);
+ This->sWorkDir = HeapAlloc( GetProcessHeap(), 0,
+ (wcslen( pszDir )+1)*sizeof (WCHAR) );
+ if ( !This->sWorkDir )
+ return E_OUTOFMEMORY;
+ wcscpy( This->sWorkDir, pszDir );
+ This->bDirty = TRUE;
+
+ return S_OK;
+}
+
+static HRESULT WINAPI IShellLinkW_fnGetArguments(IShellLinkW * iface, LPWSTR pszArgs,INT cchMaxPath)
+{
+ IShellLinkImpl *This = impl_from_IShellLinkW(iface);
+
+ TRACE("(%p)->(%p len=%u)\n", This, pszArgs, cchMaxPath);
+
+ if( cchMaxPath )
+ pszArgs[0] = 0;
+ if( This->sArgs )
+ lstrcpynW( pszArgs, This->sArgs, cchMaxPath );
+
+ return NOERROR;
+}
+
+static HRESULT WINAPI IShellLinkW_fnSetArguments(IShellLinkW * iface, LPCWSTR pszArgs)
+{
+ IShellLinkImpl *This = impl_from_IShellLinkW(iface);
+
+ TRACE("(%p)->(args=%s)\n",This, debugstr_w(pszArgs));
+
+ HeapFree(GetProcessHeap(), 0, This->sArgs);
+ This->sArgs = HeapAlloc( GetProcessHeap(), 0,
+ (wcslen( pszArgs )+1)*sizeof (WCHAR) );
+ if ( !This->sArgs )
+ return E_OUTOFMEMORY;
+ wcscpy( This->sArgs, pszArgs );
+ This->bDirty = TRUE;
+
+ return S_OK;
+}
+
+static HRESULT WINAPI IShellLinkW_fnGetHotkey(IShellLinkW * iface, WORD *pwHotkey)
+{
+ IShellLinkImpl *This = impl_from_IShellLinkW(iface);
+
+ TRACE("(%p)->(%p)\n",This, pwHotkey);
+
+ *pwHotkey=This->wHotKey;
+
+ return S_OK;
+}
+
+static HRESULT WINAPI IShellLinkW_fnSetHotkey(IShellLinkW * iface, WORD wHotkey)
+{
+ IShellLinkImpl *This = impl_from_IShellLinkW(iface);
+
+ TRACE("(%p)->(hotkey=%x)\n",This, wHotkey);
+
+ This->wHotKey = wHotkey;
+ This->bDirty = TRUE;
+
+ return S_OK;
+}
+
+static HRESULT WINAPI IShellLinkW_fnGetShowCmd(IShellLinkW * iface, INT *piShowCmd)
+{
+ IShellLinkImpl *This = impl_from_IShellLinkW(iface);
+
+ TRACE("(%p)->(%p)\n",This, piShowCmd);
+
+ *piShowCmd = This->iShowCmd;
+
+ return S_OK;
+}
+
+static HRESULT WINAPI IShellLinkW_fnSetShowCmd(IShellLinkW * iface, INT iShowCmd)
+{
+ IShellLinkImpl *This = impl_from_IShellLinkW(iface);
+
+ This->iShowCmd = iShowCmd;
+ This->bDirty = TRUE;
+
+ return S_OK;
+}
+
+static HRESULT SHELL_PidlGeticonLocationW(IShellFolder* psf, LPCITEMIDLIST pidl,
+ LPWSTR pszIconPath, int cchIconPath, int* piIcon)
+{
+ LPCITEMIDLIST pidlLast;
+ UINT wFlags;
+
+ HRESULT hr = SHBindToParent(pidl, &IID_IShellFolder, (LPVOID*)&psf, &pidlLast);
+
+ if (SUCCEEDED(hr)) {
+ IExtractIconW* pei;
+
+ hr = IShellFolder_GetUIObjectOf(psf, 0, 1, &pidlLast, &IID_IExtractIconW, NULL, (LPVOID*)&pei);
+
+ if (SUCCEEDED(hr)) {
+ hr = IExtractIconW_GetIconLocation(pei, 0, pszIconPath, MAX_PATH, piIcon, &wFlags);
+
+ IExtractIconW_Release(pei);
+ }
+
+ IShellFolder_Release(psf);
+ }
+
+ return hr;
+}
+
+static HRESULT WINAPI IShellLinkW_fnGetIconLocation(IShellLinkW * iface, LPWSTR pszIconPath,INT cchIconPath,INT *piIcon)
+{
+ IShellLinkImpl *This = impl_from_IShellLinkW(iface);
+
+ TRACE("(%p)->(%p len=%u iicon=%p)\n", This, pszIconPath, cchIconPath, piIcon);
+
+ pszIconPath[0] = 0;
+ *piIcon = This->iIcoNdx;
+
+ if (This->sIcoPath)
+ {
+ lstrcpynW(pszIconPath, This->sIcoPath, cchIconPath);
+ return S_OK;
+ }
+
+ if (This->pPidl || This->sPath)
+ {
+ IShellFolder* pdsk;
+
+ HRESULT hr = SHGetDesktopFolder(&pdsk);
+
+ if (SUCCEEDED(hr))
+ {
+ /* first look for an icon using the PIDL (if present) */
+ if (This->pPidl)
+ hr = SHELL_PidlGeticonLocationW(pdsk, This->pPidl, pszIconPath, cchIconPath, piIcon);
+ else
+ hr = E_FAIL;
+
+ /* if we couldn't find an icon yet, look for it using the file system path */
+ if (FAILED(hr) && This->sPath)
+ {
+ LPITEMIDLIST pidl;
+
+ hr = IShellFolder_ParseDisplayName(pdsk, 0, NULL, This->sPath, NULL, &pidl, NULL);
+
+ if (SUCCEEDED(hr))
+ {
+ hr = SHELL_PidlGeticonLocationW(pdsk, pidl, pszIconPath, cchIconPath, piIcon);
+
+ SHFree(pidl);
+ }
+ }
+
+ IShellFolder_Release(pdsk);
+ }
+ return hr;
+ }
+ return S_OK;
+}
+
+static HRESULT WINAPI IShellLinkW_fnSetIconLocation(IShellLinkW * iface, LPCWSTR pszIconPath,INT iIcon)
+{
+ IShellLinkImpl *This = impl_from_IShellLinkW(iface);
+
+ TRACE("(%p)->(path=%s iicon=%u)\n",This, debugstr_w(pszIconPath), iIcon);
+
+ HeapFree(GetProcessHeap(), 0, This->sIcoPath);
+ This->sIcoPath = HeapAlloc( GetProcessHeap(), 0,
+ (wcslen( pszIconPath )+1)*sizeof (WCHAR) );
+ if ( !This->sIcoPath )
+ return E_OUTOFMEMORY;
+ wcscpy( This->sIcoPath, pszIconPath );
+
+ This->iIcoNdx = iIcon;
+ This->bDirty = TRUE;
+
+ return S_OK;
+}
+
+static HRESULT WINAPI IShellLinkW_fnSetRelativePath(IShellLinkW * iface, LPCWSTR pszPathRel, DWORD dwReserved)
+{
+ IShellLinkImpl *This = impl_from_IShellLinkW(iface);
+
+ TRACE("(%p)->(path=%s %x)\n",This, debugstr_w(pszPathRel), dwReserved);
+
+ HeapFree(GetProcessHeap(), 0, This->sPathRel);
+ This->sPathRel = HeapAlloc( GetProcessHeap(), 0,
+ (wcslen( pszPathRel )+1) * sizeof (WCHAR) );
+ if ( !This->sPathRel )
+ return E_OUTOFMEMORY;
+ wcscpy( This->sPathRel, pszPathRel );
+ This->bDirty = TRUE;
+
+ return ShellLink_UpdatePath(This->sPathRel, This->sPath, This->sWorkDir, &This->sPath);
+}
+
+static HRESULT WINAPI IShellLinkW_fnResolve(IShellLinkW * iface, HWND hwnd, DWORD fFlags)
+{
+ HRESULT hr = S_OK;
+ BOOL bSuccess;
+
+ IShellLinkImpl *This = impl_from_IShellLinkW(iface);
+
+ TRACE("(%p)->(hwnd=%p flags=%x)\n",This, hwnd, fFlags);
+
+ /*FIXME: use IResolveShellLink interface */
+
+ if (!This->sPath && This->pPidl) {
+ WCHAR buffer[MAX_PATH];
+
+ bSuccess = SHGetPathFromIDListW(This->pPidl, buffer);
+
+ if (bSuccess && *buffer) {
+ This->sPath = HeapAlloc(GetProcessHeap(), 0, (wcslen(buffer)+1)*sizeof(WCHAR));
+ if (!This->sPath)
+ return E_OUTOFMEMORY;
+
+ wcscpy(This->sPath, buffer);
+
+ This->bDirty = TRUE;
+ } else
+ hr = S_OK; /* don't report an error occurred while just caching information */
+ }
+
+ if (!This->sIcoPath && This->sPath) {
+ This->sIcoPath = HeapAlloc(GetProcessHeap(), 0, (wcslen(This->sPath)+1)*sizeof(WCHAR));
+ if (!This->sIcoPath)
+ return E_OUTOFMEMORY;
+
+ wcscpy(This->sIcoPath, This->sPath);
+ This->iIcoNdx = 0;
+
+ This->bDirty = TRUE;
+ }
+
+ return hr;
+}
+
+static LPWSTR ShellLink_GetAdvertisedArg(LPCWSTR str)
+{
+ LPWSTR ret;
+ LPCWSTR p;
+ DWORD len;
+
+ if( !str )
+ return NULL;
+
+ p = wcschr( str, ':' );
+ if( !p )
+ return NULL;
+ len = p - str;
+ ret = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR)*(len+1));
+ if( !ret )
+ return ret;
+ memcpy( ret, str, sizeof(WCHAR)*len );
+ ret[len] = 0;
+ return ret;
+}
+
+static HRESULT ShellLink_SetAdvertiseInfo(IShellLinkImpl *This, LPCWSTR str)
+{
+ LPCWSTR szComponent = NULL, szProduct = NULL, p;
+ WCHAR szGuid[39];
+ HRESULT r;
+ GUID guid;
+ int len;
+
+ while( str[0] )
+ {
+ /* each segment must start with two colons */
+ if( str[0] != ':' || str[1] != ':' )
+ return E_FAIL;
+
+ /* the last segment is just two colons */
+ if( !str[2] )
+ break;
+ str += 2;
+
+ /* there must be a colon straight after a guid */
+ p = wcschr( str, ':' );
+ if( !p )
+ return E_FAIL;
+ len = p - str;
+ if( len != 38 )
+ return E_FAIL;
+
+ /* get the guid, and check it's validly formatted */
+ memcpy( szGuid, str, sizeof(WCHAR)*len );
+ szGuid[len] = 0;
+ r = CLSIDFromString( szGuid, &guid );
+ if( r != S_OK )
+ return r;
+ str = p + 1;
+
+ /* match it up to a guid that we care about */
+ if( IsEqualGUID( &guid, &SHELL32_AdvtShortcutComponent ) && !szComponent )
+ szComponent = str;
+ else if( IsEqualGUID( &guid, &SHELL32_AdvtShortcutProduct ) && !szProduct )
+ szProduct = str;
+ else
+ return E_FAIL;
+
+ /* skip to the next field */
+ str = wcschr( str, ':' );
+ if( !str )
+ return E_FAIL;
+ }
+
+ /* we have to have a component for an advertised shortcut */
+ if( !szComponent )
+ return E_FAIL;
+
+ This->sComponent = ShellLink_GetAdvertisedArg( szComponent );
+ This->sProduct = ShellLink_GetAdvertisedArg( szProduct );
+
+ TRACE("Component = %s\n", debugstr_w(This->sComponent));
+ TRACE("Product = %s\n", debugstr_w(This->sProduct));
+
+ return S_OK;
+}
+
+static BOOL ShellLink_GetVolumeInfo(LPCWSTR path, volume_info *volume)
+{
+ const int label_sz = sizeof volume->label/sizeof volume->label[0];
+ WCHAR drive[4] = { path[0], ':', '\\', 0 };
+ BOOL r;
+
+ volume->type = GetDriveTypeW(drive);
+ r = GetVolumeInformationW(drive, volume->label, label_sz,
+ &volume->serial, NULL, NULL, NULL, 0);
+ TRACE("r = %d type %d serial %08x name %s\n", r,
+ volume->type, volume->serial, debugstr_w(volume->label));
+ return r;
+}
+
+static HRESULT WINAPI IShellLinkW_fnSetPath(IShellLinkW * iface, LPCWSTR pszFile)
+{
+ IShellLinkImpl *This = impl_from_IShellLinkW(iface);
+ WCHAR buffer[MAX_PATH];
+ LPWSTR fname, unquoted = NULL;
+ HRESULT hr = S_OK;
+ UINT len;
+
+ TRACE("(%p)->(path=%s)\n",This, debugstr_w(pszFile));
+
+ if (!pszFile) return E_INVALIDARG;
+
+ /* quotes at the ends of the string are stripped */
+ len = wcslen(pszFile);
+ if (pszFile[0] == '"' && pszFile[len-1] == '"')
+ {
+ unquoted = strdupW(pszFile);
+ PathUnquoteSpacesW(unquoted);
+ pszFile = unquoted;
+ }
+
+ /* any other quote marks are invalid */
+ if (wcschr(pszFile, '"'))
+ {
+ HeapFree(GetProcessHeap(), 0, unquoted);
+ return S_FALSE;
+ }
+
+ HeapFree(GetProcessHeap(), 0, This->sPath);
+ This->sPath = NULL;
+
+ HeapFree(GetProcessHeap(), 0, This->sComponent);
+ This->sComponent = NULL;
+
+ if (This->pPidl)
+ ILFree(This->pPidl);
+ This->pPidl = NULL;
+
+ if (S_OK != ShellLink_SetAdvertiseInfo( This, pszFile ))
+ {
+ if (*pszFile == '\0')
+ *buffer = '\0';
+ else if (!GetFullPathNameW(pszFile, MAX_PATH, buffer, &fname))
+ return E_FAIL;
+ else if(!PathFileExistsW(buffer) &&
+ !SearchPathW(NULL, pszFile, NULL, MAX_PATH, buffer, NULL))
+ hr = S_FALSE;
+
+ This->pPidl = SHSimpleIDListFromPathW(pszFile);
+ ShellLink_GetVolumeInfo(buffer, &This->volume);
+
+ This->sPath = HeapAlloc( GetProcessHeap(), 0,
+ (wcslen( buffer )+1) * sizeof (WCHAR) );
+ if (!This->sPath)
+ return E_OUTOFMEMORY;
+
+ wcscpy(This->sPath, buffer);
+ }
+ This->bDirty = TRUE;
+ HeapFree(GetProcessHeap(), 0, unquoted);
+
+ return hr;
+}
+
+/**************************************************************************
+* IShellLinkW Implementation
+*/
+
+static const IShellLinkWVtbl slvtw =
+{
+ IShellLinkW_fnQueryInterface,
+ IShellLinkW_fnAddRef,
+ IShellLinkW_fnRelease,
+ IShellLinkW_fnGetPath,
+ IShellLinkW_fnGetIDList,
+ IShellLinkW_fnSetIDList,
+ IShellLinkW_fnGetDescription,
+ IShellLinkW_fnSetDescription,
+ IShellLinkW_fnGetWorkingDirectory,
+ IShellLinkW_fnSetWorkingDirectory,
+ IShellLinkW_fnGetArguments,
+ IShellLinkW_fnSetArguments,
+ IShellLinkW_fnGetHotkey,
+ IShellLinkW_fnSetHotkey,
+ IShellLinkW_fnGetShowCmd,
+ IShellLinkW_fnSetShowCmd,
+ IShellLinkW_fnGetIconLocation,
+ IShellLinkW_fnSetIconLocation,
+ IShellLinkW_fnSetRelativePath,
+ IShellLinkW_fnResolve,
+ IShellLinkW_fnSetPath
+};
+
+static HRESULT WINAPI
+ShellLink_DataList_QueryInterface( IShellLinkDataList* iface, REFIID riid, void** ppvObject)
+{
+ IShellLinkImpl *This = impl_from_IShellLinkDataList(iface);
+ return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvObject);
+}
+
+static ULONG WINAPI
+ShellLink_DataList_AddRef( IShellLinkDataList* iface )
+{
+ IShellLinkImpl *This = impl_from_IShellLinkDataList(iface);
+ return IShellLinkA_AddRef((IShellLinkA*)This);
+}
+
+static ULONG WINAPI
+ShellLink_DataList_Release( IShellLinkDataList* iface )
+{
+ IShellLinkImpl *This = impl_from_IShellLinkDataList(iface);
+ return ShellLink_Release( This );
+}
+
+static HRESULT WINAPI
+ShellLink_AddDataBlock( IShellLinkDataList* iface, void* pDataBlock )
+{
+ FIXME("\n");
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI
+ShellLink_CopyDataBlock( IShellLinkDataList* iface, DWORD dwSig, void** ppDataBlock )
+{
+ IShellLinkImpl *This = impl_from_IShellLinkDataList(iface);
+ LPVOID block = NULL;
+ HRESULT r = E_FAIL;
+
+ TRACE("%p %08x %p\n", iface, dwSig, ppDataBlock );
+
+ switch (dwSig)
+ {
+ case EXP_DARWIN_ID_SIG:
+ if (!This->sComponent)
+ break;
+ block = shelllink_build_darwinid( This->sComponent, dwSig );
+ r = S_OK;
+ break;
+ case EXP_SZ_LINK_SIG:
+ case NT_CONSOLE_PROPS_SIG:
+ case NT_FE_CONSOLE_PROPS_SIG:
+ case EXP_SPECIAL_FOLDER_SIG:
+ case EXP_SZ_ICON_SIG:
+ FIXME("valid but unhandled datablock %08x\n", dwSig);
+ break;
+ default:
+ ERR("unknown datablock %08x\n", dwSig);
+ }
+ *ppDataBlock = block;
+ return r;
+}
+
+static HRESULT WINAPI
+ShellLink_RemoveDataBlock( IShellLinkDataList* iface, DWORD dwSig )
+{
+ FIXME("\n");
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI
+ShellLink_GetFlags( IShellLinkDataList* iface, DWORD* pdwFlags )
+{
+ IShellLinkImpl *This = impl_from_IShellLinkDataList(iface);
+ DWORD flags = 0;
+
+ FIXME("%p %p\n", This, pdwFlags );
+
+ /* FIXME: add more */
+ if (This->sArgs)
+ flags |= SLDF_HAS_ARGS;
+ if (This->sComponent)
+ flags |= SLDF_HAS_DARWINID;
+ if (This->sIcoPath)
+ flags |= SLDF_HAS_ICONLOCATION;
+#if (NTDDI_VERSION < NTDDI_LONGHORN)
+ if (This->sProduct)
+ flags |= SLDF_HAS_LOGO3ID;
+#endif
+ if (This->pPidl)
+ flags |= SLDF_HAS_ID_LIST;
+
+ *pdwFlags = flags;
+
+ return S_OK;
+}
+
+static HRESULT WINAPI
+ShellLink_SetFlags( IShellLinkDataList* iface, DWORD dwFlags )
+{
+ FIXME("\n");
+ return E_NOTIMPL;
+}
+
+static const IShellLinkDataListVtbl dlvt =
+{
+ ShellLink_DataList_QueryInterface,
+ ShellLink_DataList_AddRef,
+ ShellLink_DataList_Release,
+ ShellLink_AddDataBlock,
+ ShellLink_CopyDataBlock,
+ ShellLink_RemoveDataBlock,
+ ShellLink_GetFlags,
+ ShellLink_SetFlags
+};
+
+static HRESULT WINAPI
+ShellLink_ExtInit_QueryInterface( IShellExtInit* iface, REFIID riid, void** ppvObject )
+{
+ IShellLinkImpl *This = impl_from_IShellExtInit(iface);
+ return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvObject);
+}
+
+static ULONG WINAPI
+ShellLink_ExtInit_AddRef( IShellExtInit* iface )
+{
+ IShellLinkImpl *This = impl_from_IShellExtInit(iface);
+ return IShellLinkA_AddRef((IShellLinkA*)This);
+}
+
+static ULONG WINAPI
+ShellLink_ExtInit_Release( IShellExtInit* iface )
+{
+ IShellLinkImpl *This = impl_from_IShellExtInit(iface);
+ return ShellLink_Release( This );
+}
+
+/**************************************************************************
+ * ShellLink implementation of IShellExtInit::Initialize()
+ *
+ * Loads the shelllink from the dataobject the shell is pointing to.
+ */
+static HRESULT WINAPI
+ShellLink_ExtInit_Initialize( IShellExtInit* iface, LPCITEMIDLIST pidlFolder,
+ IDataObject *pdtobj, HKEY hkeyProgID )
+{
+ IShellLinkImpl *This = impl_from_IShellExtInit(iface);
+ FORMATETC format;
+ STGMEDIUM stgm;
+ UINT count;
+ HRESULT r = E_FAIL;
+
+ TRACE("%p %p %p %p\n", This, pidlFolder, pdtobj, hkeyProgID );
+
+ if( !pdtobj )
+ return r;
+
+ format.cfFormat = CF_HDROP;
+ format.ptd = NULL;
+ format.dwAspect = DVASPECT_CONTENT;
+ format.lindex = -1;
+ format.tymed = TYMED_HGLOBAL;
+
+ if( FAILED( IDataObject_GetData( pdtobj, &format, &stgm ) ) )
+ return r;
+
+ count = DragQueryFileW( stgm.u.hGlobal, -1, NULL, 0 );
+ if( count == 1 )
+ {
+ LPWSTR path;
+
+ count = DragQueryFileW( stgm.u.hGlobal, 0, NULL, 0 );
+ count++;
+ path = HeapAlloc( GetProcessHeap(), 0, count*sizeof(WCHAR) );
+ if( path )
+ {
+ IPersistFile *pf = (IPersistFile*) &This->lpvtblPersistFile;
+
+ count = DragQueryFileW( stgm.u.hGlobal, 0, path, count );
+ r = IPersistFile_Load( pf, path, 0 );
+ HeapFree( GetProcessHeap(), 0, path );
+ }
+ }
+ ReleaseStgMedium( &stgm );
+
+ return r;
+}
+
+static const IShellExtInitVtbl eivt =
+{
+ ShellLink_ExtInit_QueryInterface,
+ ShellLink_ExtInit_AddRef,
+ ShellLink_ExtInit_Release,
+ ShellLink_ExtInit_Initialize
+};
+
+static HRESULT WINAPI
+ShellLink_ContextMenu_QueryInterface( IContextMenu* iface, REFIID riid, void** ppvObject )
+{
+ IShellLinkImpl *This = impl_from_IContextMenu(iface);
+ return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvObject);
+}
+
+static ULONG WINAPI
+ShellLink_ContextMenu_AddRef( IContextMenu* iface )
+{
+ IShellLinkImpl *This = impl_from_IContextMenu(iface);
+ return IShellLinkA_AddRef((IShellLinkA*)This);
+}
+
+static ULONG WINAPI
+ShellLink_ContextMenu_Release( IContextMenu* iface )
+{
+ IShellLinkImpl *This = impl_from_IContextMenu(iface);
+ return ShellLink_Release( This );
+}
+
+static HRESULT WINAPI
+ShellLink_QueryContextMenu( IContextMenu* iface, HMENU hmenu, UINT indexMenu,
+ UINT idCmdFirst, UINT idCmdLast, UINT uFlags )
+{
+ IShellLinkImpl *This = impl_from_IContextMenu(iface);
+ WCHAR szOpen[20];
+ MENUITEMINFOW mii;
+ int id = 1;
+
+ TRACE("%p %p %u %u %u %u\n", This,
+ hmenu, indexMenu, idCmdFirst, idCmdLast, uFlags );
+
+ if ( !hmenu )
+ return E_INVALIDARG;
+
+ if (!LoadStringW(shell32_hInstance, IDS_OPEN_VERB, szOpen, sizeof(szOpen)/sizeof(WCHAR)))
+ szOpen[0] = L'\0';
+ else
+ szOpen[(sizeof(szOpen)/sizeof(WCHAR))-1] = L'\0';
+
+ memset( &mii, 0, sizeof(mii) );
+ mii.cbSize = sizeof (mii);
+ mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE;
+ mii.dwTypeData = (LPWSTR)szOpen;
+ mii.cch = wcslen( mii.dwTypeData );
+ mii.wID = idCmdFirst + id++;
+ mii.fState = MFS_DEFAULT | MFS_ENABLED;
+ mii.fType = MFT_STRING;
+ if (!InsertMenuItemW( hmenu, indexMenu, TRUE, &mii ))
+ return E_FAIL;
+ This->iIdOpen = 1;
+
+ return MAKE_HRESULT( SEVERITY_SUCCESS, 0, id );
+}
+
+static LPWSTR
+shelllink_get_msi_component_path( LPWSTR component )
+{
+ LPWSTR path;
+ DWORD r, sz = 0;
+
+ r = CommandLineFromMsiDescriptor( component, NULL, &sz );
+ if (r != ERROR_SUCCESS)
+ return NULL;
+
+ sz++;
+ path = HeapAlloc( GetProcessHeap(), 0, sz*sizeof(WCHAR) );
+ r = CommandLineFromMsiDescriptor( component, path, &sz );
+ if (r != ERROR_SUCCESS)
+ {
+ HeapFree( GetProcessHeap(), 0, path );
+ path = NULL;
+ }
+
+ TRACE("returning %s\n", debugstr_w( path ) );
+
+ return path;
+}
+
+INT_PTR CALLBACK ExtendedShortcutProc(
+ HWND hwndDlg,
+ UINT uMsg,
+ WPARAM wParam,
+ LPARAM lParam
+)
+{
+ HWND hDlgCtrl;
+
+ switch(uMsg)
+ {
+ case WM_INITDIALOG:
+ if (lParam)
+ {
+ hDlgCtrl = GetDlgItem(hwndDlg, 14000);
+ SendMessage(hDlgCtrl, BM_SETCHECK, BST_CHECKED, 0);
+ }
+ return TRUE;
+ case WM_COMMAND:
+ hDlgCtrl = GetDlgItem(hwndDlg, 14000);
+ if (LOWORD(wParam) == IDOK)
+ {
+ if ( SendMessage(hDlgCtrl, BM_GETCHECK, 0, 0) == BST_CHECKED )
+ EndDialog(hwndDlg, 1);
+ else
+ EndDialog(hwndDlg, 0);
+ }
+ else if (LOWORD(wParam) == IDCANCEL)
+ {
+ EndDialog(hwndDlg, -1);
+ }
+ else if (LOWORD(wParam) == 14000)
+ {
+ if ( SendMessage(hDlgCtrl, BM_GETCHECK, 0, 0) == BST_CHECKED)
+ SendMessage(hDlgCtrl, BM_SETCHECK, BST_UNCHECKED, 0);
+ else
+ SendMessage(hDlgCtrl, BM_SETCHECK, BST_CHECKED, 0);
+
+ }
+ }
+ return FALSE;
+}
+
+/**************************************************************************
+ * SH_ShellLinkDlgProc
+ *
+ * dialog proc of the shortcut property dialog
+ */
+
+INT_PTR
+CALLBACK
+SH_ShellLinkDlgProc(
+ HWND hwndDlg,
+ UINT uMsg,
+ WPARAM wParam,
+ LPARAM lParam
+)
+{
+ LPPROPSHEETPAGEW ppsp;
+ LPPSHNOTIFY lppsn;
+ IShellLinkImpl *This;
+ HWND hDlgCtrl;
+ WCHAR szBuffer[MAX_PATH];
+ WCHAR * ptr;
+ int IconIndex;
+ INT_PTR result;
+
+ This = (IShellLinkImpl *)GetWindowLongPtr(hwndDlg, DWLP_USER);
+
+ switch(uMsg)
+ {
+ case WM_INITDIALOG:
+ ppsp = (LPPROPSHEETPAGEW)lParam;
+ if (ppsp == NULL)
+ break;
+
+ TRACE("ShellLink_DlgProc (WM_INITDIALOG hwnd %p lParam %p ppsplParam %x)\n",hwndDlg, lParam, ppsp->lParam);
+
+ This = (IShellLinkImpl *)ppsp->lParam;
+ SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)This);
+
+ TRACE("sArgs: %S sComponent: %S sDescription: %S sIcoPath: %S sPath: %S sPathRel: %S sProduct: %S sWorkDir: %S\n", This->sArgs, This->sComponent ,This->sDescription,
+ This->sIcoPath, This->sPath, This->sPathRel, This->sProduct, This->sWorkDir);
+
+ /* target path */
+ hDlgCtrl = GetDlgItem( hwndDlg, 14009 );
+ if ( hDlgCtrl != NULL )
+ SendMessageW( hDlgCtrl, WM_SETTEXT, (WPARAM)NULL, (LPARAM)This->sPath );
+
+ /* working dir */
+ hDlgCtrl = GetDlgItem( hwndDlg, 14011 );
+ if ( hDlgCtrl != NULL )
+ SendMessageW( hDlgCtrl, WM_SETTEXT, (WPARAM)NULL, (LPARAM)This->sWorkDir );
+
+ /* description */
+ hDlgCtrl = GetDlgItem( hwndDlg, 14019 );
+ if ( hDlgCtrl != NULL )
+ SendMessageW( hDlgCtrl, WM_SETTEXT, (WPARAM)NULL, (LPARAM)This->sDescription );
+ return TRUE;
+ case WM_NOTIFY:
+ lppsn = (LPPSHNOTIFY) lParam;
+ if ( lppsn->hdr.code == PSN_APPLY )
+ {
+ /* set working directory */
+ hDlgCtrl = GetDlgItem( hwndDlg, 14011 );
+ SendMessageW( hDlgCtrl, WM_GETTEXT, (WPARAM)MAX_PATH, (LPARAM)szBuffer );
+ IShellLinkW_fnSetWorkingDirectory((IShellLinkW*)&This->lpvtblw, szBuffer);
+ /* set link destination */
+ hDlgCtrl = GetDlgItem( hwndDlg, 14009 );
+ SendMessageW( hDlgCtrl, WM_GETTEXT, (WPARAM)MAX_PATH, (LPARAM)szBuffer);
+ if ( !SHELL_ExistsFileW(szBuffer) )
+ {
+ //FIXME load localized error msg
+ MessageBoxW( hwndDlg, L"file not existing", szBuffer, MB_OK );
+ SetWindowLongPtr( hwndDlg, DWL_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE );
+ return TRUE;
+ }
+ ptr = wcsrchr(szBuffer, L'.');
+ if (ptr && !_wcsnicmp(ptr, L".lnk", 4))
+ {
+ // FIXME load localized error msg
+ MessageBoxW( hwndDlg, L"You cannot create a link to a shortcut", L"Error", MB_ICONERROR );
+ SetWindowLongPtr( hwndDlg, DWL_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE );
+ return TRUE;
+ }
+
+ IShellLinkW_fnSetPath((IShellLinkW*)&This->lpvtblw, szBuffer);
+
+ TRACE("This %p sLinkPath %S\n", This, This->sLinkPath);
+ IPersistFile_fnSave( (IPersistFile*)&This->lpvtblPersistFile, This->sLinkPath, TRUE );
+ SetWindowLongPtr( hwndDlg, DWL_MSGRESULT, PSNRET_NOERROR );
+ return TRUE;
+ }
+ break;
+ case WM_COMMAND:
+ switch(LOWORD(wParam))
+ {
+ case 14020:
+ ///
+ /// FIXME
+ /// open target directory
+ ///
+ return TRUE;
+ case 14021:
+ if (This->sIcoPath)
+ wcscpy(szBuffer, This->sIcoPath);
+ else
+ wcscpy(szBuffer, This->sPath);
+
+ IconIndex = This->iIcoNdx;
+ if (PickIconDlg(hwndDlg, szBuffer, MAX_PATH, &IconIndex))
+ {
+ IShellLinkW_fnSetIconLocation((IShellLinkW*)&This->lpvtblw, szBuffer, IconIndex);
+ ///
+ /// FIXME redraw icon
+ }
+ return TRUE;
+ case 14022:
+ result = DialogBoxParamW(shell32_hInstance, MAKEINTRESOURCEW(SHELL_EXTENDED_SHORTCUT_DLG), hwndDlg, ExtendedShortcutProc, (LPARAM)This->bRunAs);
+ if (result == 1 || result == 0)
+ {
+ if ( This->bRunAs != result )
+ {
+ PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
+ }
+
+ This->bRunAs = result;
+ }
+ return TRUE;
+ }
+ switch(HIWORD(wParam))
+ {
+ case EN_CHANGE:
+ PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ return FALSE;
+}
+
+/**************************************************************************
+ * ShellLink_IShellPropSheetExt interface
+ */
+
+static HRESULT WINAPI
+ ShellLink_IShellPropSheetExt_QueryInterface( IShellPropSheetExt* iface, REFIID riid, void** ppvObject )
+{
+ IShellLinkImpl *This = impl_from_IShellPropSheetExt(iface);
+ return ShellLink_QueryInterface( This, riid, ppvObject );
+}
+
+static ULONG WINAPI
+ ShellLink_IShellPropSheetExt_AddRef( IShellPropSheetExt* iface )
+{
+ IShellLinkImpl *This = impl_from_IShellPropSheetExt(iface);
+ return ShellLink_AddRef( This );
+}
+
+static ULONG WINAPI
+ ShellLink_IShellPropSheetExt_Release( IShellPropSheetExt* iface )
+{
+ IShellLinkImpl *This = impl_from_IShellPropSheetExt(iface);
+ return ShellLink_Release( This );
+}
+
+static HRESULT WINAPI
+ ShellLink_IShellPropSheetExt_AddPages( IShellPropSheetExt *iface, LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam)
+{
+ HPROPSHEETPAGE hPage;
+ BOOL bRet;
+ IShellLinkImpl *This = impl_from_IShellPropSheetExt(iface);
+
+ hPage = SH_CreatePropertySheetPage("SHELL_GENERAL_SHORTCUT_DLG", SH_ShellLinkDlgProc, (LPARAM)This, NULL);
+ if (hPage == NULL)
+ {
+ ERR("failed to create property sheet page\n");
+ return E_FAIL;
+ }
+
+ bRet = pfnAddPage(hPage, lParam);
+ if (bRet)
+ return S_OK;
+ else
+ return E_FAIL;
+}
+
+static HRESULT WINAPI
+ ShellLink_IShellPropSheetExt_ReplacePages( IShellPropSheetExt *iface, UINT uPageID, LPFNADDPROPSHEETPAGE pfnReplacePage, LPARAM lParam)
+{
+ IShellLinkImpl *This = impl_from_IShellPropSheetExt(iface);
+ TRACE("(%p) (uPageID %u, pfnReplacePage %p lParam %p\n", This, uPageID, pfnReplacePage, lParam);
+ return E_NOTIMPL;
+}
+
+static const IShellPropSheetExtVtbl pse =
+{
+ ShellLink_IShellPropSheetExt_QueryInterface,
+ ShellLink_IShellPropSheetExt_AddRef,
+ ShellLink_IShellPropSheetExt_Release,
+ ShellLink_IShellPropSheetExt_AddPages,
+ ShellLink_IShellPropSheetExt_ReplacePages
+};
+
+static HRESULT WINAPI
+ShellLink_InvokeCommand( IContextMenu* iface, LPCMINVOKECOMMANDINFO lpici )
+{
+ IShellLinkImpl *This = impl_from_IContextMenu(iface);
+ static const WCHAR szOpen[] = { 'o','p','e','n',0 };
+ static const WCHAR szCplOpen[] = { 'c','p','l','o','p','e','n',0 };
+ SHELLEXECUTEINFOW sei;
+ HWND hwnd = NULL; /* FIXME: get using interface set from IObjectWithSite */
+ LPWSTR args = NULL;
+ LPWSTR path = NULL;
+ HRESULT r;
+
+ TRACE("%p %p\n", This, lpici );
+
+ if ( lpici->cbSize < sizeof (CMINVOKECOMMANDINFO) )
+ return E_INVALIDARG;
+
+ r = IShellLinkW_Resolve( (IShellLinkW*)&(This->lpvtblw), hwnd, 0 );
+ if ( FAILED( r ) )
+ {
+ TRACE("failed to resolve component with error 0x%08x", r);
+ return r;
+ }
+ if ( This->sComponent )
+ {
+ path = shelllink_get_msi_component_path( This->sComponent );
+ if (!path)
+ return E_FAIL;
+ }
+ else
+ path = strdupW( This->sPath );
+
+ if ( lpici->cbSize == sizeof (CMINVOKECOMMANDINFOEX) &&
+ ( lpici->fMask & CMIC_MASK_UNICODE ) )
+ {
+ LPCMINVOKECOMMANDINFOEX iciex = (LPCMINVOKECOMMANDINFOEX) lpici;
+ DWORD len = 2;
+
+ if ( This->sArgs )
+ len += wcslen( This->sArgs );
+ if ( iciex->lpParametersW )
+ len += wcslen( iciex->lpParametersW );
+
+ args = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
+ args[0] = 0;
+ if ( This->sArgs )
+ wcscat( args, This->sArgs );
+ if ( iciex->lpParametersW )
+ {
+ static const WCHAR space[] = { ' ', 0 };
+ wcscat( args, space );
+ wcscat( args, iciex->lpParametersW );
+ }
+ }
+ else if (This->sArgs != NULL)
+ {
+ args = strdupW( This->sArgs );
+ }
+
+ memset( &sei, 0, sizeof sei );
+ sei.cbSize = sizeof sei;
+ sei.fMask = SEE_MASK_UNICODE | (lpici->fMask & (SEE_MASK_NOASYNC|SEE_MASK_ASYNCOK|SEE_MASK_FLAG_NO_UI));
+ sei.lpFile = path;
+ sei.nShow = This->iShowCmd;
+ sei.lpDirectory = This->sWorkDir;
+ sei.lpParameters = args;
+ sei.lpVerb = szOpen;
+
+ // HACK for ShellExecuteExW
+ if (!wcsstr(This->sPath, L".cpl"))
+ sei.lpVerb = szOpen;
+ else
+ sei.lpVerb = szCplOpen;
+
+ if( ShellExecuteExW( &sei ) )
+ r = S_OK;
+ else
+ r = E_FAIL;
+
+ HeapFree( GetProcessHeap(), 0, args );
+ HeapFree( GetProcessHeap(), 0, path );
+
+ return r;
+}
+
+static HRESULT WINAPI
+ShellLink_GetCommandString( IContextMenu* iface, UINT_PTR idCmd, UINT uType,
+ UINT* pwReserved, LPSTR pszName, UINT cchMax )
+{
+ IShellLinkImpl *This = impl_from_IContextMenu(iface);
+
+ FIXME("%p %lu %u %p %p %u\n", This,
+ idCmd, uType, pwReserved, pszName, cchMax );
+
+ return E_NOTIMPL;
+}
+
+static const IContextMenuVtbl cmvt =
+{
+ ShellLink_ContextMenu_QueryInterface,
+ ShellLink_ContextMenu_AddRef,
+ ShellLink_ContextMenu_Release,
+ ShellLink_QueryContextMenu,
+ ShellLink_InvokeCommand,
+ ShellLink_GetCommandString
+};
+
+static HRESULT WINAPI
+ShellLink_ObjectWithSite_QueryInterface( IObjectWithSite* iface, REFIID riid, void** ppvObject )
+{
+ IShellLinkImpl *This = impl_from_IObjectWithSite(iface);
+ return ShellLink_QueryInterface( This, riid, ppvObject );
+}
+
+static ULONG WINAPI
+ShellLink_ObjectWithSite_AddRef( IObjectWithSite* iface )
+{
+ IShellLinkImpl *This = impl_from_IObjectWithSite(iface);
+ return ShellLink_AddRef( This );
+}
+
+static ULONG WINAPI
+ShellLink_ObjectWithSite_Release( IObjectWithSite* iface )
+{
+ IShellLinkImpl *This = impl_from_IObjectWithSite(iface);
+ return ShellLink_Release( This );
+}
+
+static HRESULT WINAPI
+ShellLink_GetSite( IObjectWithSite *iface, REFIID iid, void ** ppvSite )
+{
+ IShellLinkImpl *This = impl_from_IObjectWithSite(iface);
+
+ TRACE("%p %s %p\n", This, debugstr_guid( iid ), ppvSite );
+
+ if ( !This->site )
+ return E_FAIL;
+ return IUnknown_QueryInterface( This->site, iid, ppvSite );
+}
+
+static HRESULT WINAPI
+ShellLink_SetSite( IObjectWithSite *iface, IUnknown *punk )
+{
+ IShellLinkImpl *This = impl_from_IObjectWithSite(iface);
+
+ TRACE("%p %p\n", iface, punk);
+
+ if ( punk )
+ IUnknown_AddRef( punk );
+ This->site = punk;
+
+ return S_OK;
+}
+
+static const IObjectWithSiteVtbl owsvt =
+{
+ ShellLink_ObjectWithSite_QueryInterface,
+ ShellLink_ObjectWithSite_AddRef,
+ ShellLink_ObjectWithSite_Release,
+ ShellLink_SetSite,
+ ShellLink_GetSite,
+};
--- /dev/null
- int dx;
+/*
+ * PROJECT: Win32 subsystem
+ * LICENSE: See COPYING in the top level directory
+ * FILE: subsystems/win32/win32k/dib/dib1bpp.c
+ * PURPOSE: Device Independant Bitmap functions, 1bpp
+ * PROGRAMMERS: Jason Filby
+ */
+
+#include <win32k.h>
+
+#define NDEBUG
+#include <debug.h>
+
+VOID
+DIB_1BPP_PutPixel(SURFOBJ *SurfObj, LONG x, LONG y, ULONG c)
+{
+ PBYTE addr = (PBYTE)SurfObj->pvScan0 + y * SurfObj->lDelta + (x >> 3);
+
+ if (0 == (c & 0x01))
+ *addr &= ~MASK1BPP(x);
+ else
+ *addr |= MASK1BPP(x);
+}
+
+ULONG
+DIB_1BPP_GetPixel(SURFOBJ *SurfObj, LONG x, LONG y)
+{
+ PBYTE addr = (PBYTE)SurfObj->pvScan0 + y * SurfObj->lDelta + (x >> 3);
+
+ return (*addr & MASK1BPP(x) ? 1 : 0);
+}
+
+VOID
+DIB_1BPP_HLine(SURFOBJ *SurfObj, LONG x1, LONG x2, LONG y, ULONG c)
+{
+ while(x1 < x2)
+ {
+ DIB_1BPP_PutPixel(SurfObj, x1, y, c);
+ x1++;
+ }
+}
+
+VOID
+DIB_1BPP_VLine(SURFOBJ *SurfObj, LONG x, LONG y1, LONG y2, ULONG c)
+{
+ while(y1 < y2)
+ {
+ DIB_1BPP_PutPixel(SurfObj, x, y1, c);
+ y1++;
+ }
+}
+
+static
+void
+DIB_1BPP_BitBltSrcCopy_From1BPP (
+ SURFOBJ* DestSurf,
+ SURFOBJ* SourceSurf,
+ XLATEOBJ* pxlo,
+ PRECTL DestRect,
+ POINTL *SourcePoint )
+{
+ // the 'window' in this sense is the x-position that corresponds
+ // to the left-edge of the 8-pixel byte we are currently working with.
+ // dwx is current x-window, dwx2 is the 'last' window we need to process
+ int dwx, dwx2; // destination window x-position
+ int swx; // source window y-position
+
+ // left and right edges of source and dest rectangles
+ int dl = DestRect->left; // dest left
+ int dr = DestRect->right-1; // dest right (inclusive)
+ int sl = SourcePoint->x; // source left
+ int sr = sl + dr - dl; // source right (inclusive)
+
+ // which direction are we going?
+ int xinc;
+ int yinc;
+ int ySrcDelta, yDstDelta;
+
+ // following 4 variables are used for the y-sweep
+ int dy; // dest y
+ int dy1; // dest y start
+ int dy2; // dest y end
+ int sy1; // src y start
+
- dx = dwx; /* dest x for this pass */
+ int shift;
+ BYTE srcmask, dstmask, xormask;
+
+ // 'd' and 's' are the dest & src buffer pointers that I use on my x-sweep
+ // 'pd' and 'ps' are the dest & src buffer pointers used on the inner y-sweep
+ PBYTE d, pd; // dest ptrs
+ PBYTE s, ps; // src ptrs
+
+ shift = (dl-sl)&7;
+
+ xormask = 0xFF * XLATEOBJ_iXlate(pxlo, 0);
+
+ if ( DestRect->top <= SourcePoint->y )
+ {
+ // moving up ( scan top -> bottom )
+ dy1 = DestRect->top;
+ dy2 = DestRect->bottom - 1;
+ sy1 = SourcePoint->y;
+ yinc = 1;
+ ySrcDelta = SourceSurf->lDelta;
+ yDstDelta = DestSurf->lDelta;
+ }
+ else
+ {
+ // moving down ( scan bottom -> top )
+ dy1 = DestRect->bottom - 1;
+ dy2 = DestRect->top;
+ sy1 = SourcePoint->y + dy1 - dy2;
+ yinc = -1;
+ ySrcDelta = -SourceSurf->lDelta;
+ yDstDelta = -DestSurf->lDelta;
+ }
+ if ( DestRect->left <= SourcePoint->x )
+ {
+ // moving left ( scan left->right )
+ dwx = dl&~7;
+ swx = (sl-(dl&7))&~7;
+ dwx2 = dr&~7;
+ xinc = 1;
+ }
+ else
+ {
+ // moving right ( scan right->left )
+ dwx = dr&~7;
+ swx = (sr-(dr&7))&~7; //(sr-7)&~7; // we need the left edge of this block... thus the -7
+ dwx2 = dl&~7;
+ xinc = -1;
+ }
+ d = &(((PBYTE)DestSurf->pvScan0)[dy1*DestSurf->lDelta + (dwx>>3)]);
+ s = &(((PBYTE)SourceSurf->pvScan0)[sy1*SourceSurf->lDelta + (swx>>3)]);
+ for ( ;; )
+ {
+ dy = dy1;
+ pd = d;
+ ps = s;
+ srcmask = 0xff;
- dx = dl;
+ if ( dwx < dl )
+ {
+ int diff = dl-dwx;
+ srcmask &= (1<<(8-diff))-1;
+ }
+ if ( dwx+7 > dr )
+ {
+ int diff = dr-dwx+1;
+ srcmask &= ~((1<<(8-diff))-1);
+ }
+ dstmask = ~srcmask;
+
+ // we unfortunately *must* have 5 different versions of the inner
+ // loop to be certain we don't try to read from memory that is not
+ // needed and may in fact be invalid
+ if ( !shift )
+ {
+ for ( ;; )
+ {
+ *pd = (BYTE)((*pd & dstmask) | ((ps[0]^xormask) & srcmask));
+
+ // this *must* be here, because we could be going up *or* down...
+ if ( dy == dy2 )
+ break;
+ dy += yinc;
+ pd += yDstDelta;
+ ps += ySrcDelta;
+ }
+ }
+ else if ( !(0xFF00 & (srcmask<<shift) ) ) // check if ps[0] not needed...
+ {
+ for ( ;; )
+ {
+ *pd = (BYTE)((*pd & dstmask)
+ | ( ( (ps[1]^xormask) >> shift ) & srcmask ));
+
+ // this *must* be here, because we could be going up *or* down...
+ if ( dy == dy2 )
+ break;
+ dy += yinc;
+ pd += yDstDelta;
+ ps += ySrcDelta;
+ }
+ }
+ else if ( !(0xFF & (srcmask<<shift) ) ) // check if ps[1] not needed...
+ {
+ for ( ;; )
+ {
+ *pd = (*pd & dstmask)
+ | ( ( (ps[0]^xormask) << ( 8 - shift ) ) & srcmask );
+
+ // this *must* be here, because we could be going up *or* down...
+ if ( dy == dy2 )
+ break;
+ dy += yinc;
+ pd += yDstDelta;
+ ps += ySrcDelta;
+ }
+ }
+ else // both ps[0] and ps[1] are needed
+ {
+ for ( ;; )
+ {
+ *pd = (*pd & dstmask)
+ | ( ( ( ((ps[1]^xormask))|((ps[0]^xormask)<<8) ) >> shift ) & srcmask );
+
+ // this *must* be here, because we could be going up *or* down...
+ if ( dy == dy2 )
+ break;
+ dy += yinc;
+ pd += yDstDelta;
+ ps += ySrcDelta;
+ }
+ }
+
+ // this *must* be here, because we could be going right *or* left...
+ if ( dwx == dwx2 )
+ break;
+ d += xinc;
+ s += xinc;
+ dwx += xinc<<3;
+ swx += xinc<<3;
+ }
+}
+
+BOOLEAN
+DIB_1BPP_BitBltSrcCopy(PBLTINFO BltInfo)
+{
+ ULONG Color;
+ LONG i, j, sx, sy = BltInfo->SourcePoint.y;
+
+ switch ( BltInfo->SourceSurface->iBitmapFormat )
+ {
+ case BMF_1BPP:
+ DIB_1BPP_BitBltSrcCopy_From1BPP ( BltInfo->DestSurface, BltInfo->SourceSurface, BltInfo->XlateSourceToDest, &BltInfo->DestRect, &BltInfo->SourcePoint );
+ break;
+
+ case BMF_4BPP:
+ for (j=BltInfo->DestRect.top; j<BltInfo->DestRect.bottom; j++)
+ {
+ sx = BltInfo->SourcePoint.x;
+ for (i=BltInfo->DestRect.left; i<BltInfo->DestRect.right; i++)
+ {
+ Color = XLATEOBJ_iXlate(BltInfo->XlateSourceToDest, DIB_4BPP_GetPixel(BltInfo->SourceSurface, sx, sy));
+ DIB_1BPP_PutPixel(BltInfo->DestSurface, i, j, Color);
+ sx++;
+ }
+ sy++;
+ }
+ break;
+
+ case BMF_8BPP:
+ for (j=BltInfo->DestRect.top; j<BltInfo->DestRect.bottom; j++)
+ {
+ sx = BltInfo->SourcePoint.x;
+ for (i=BltInfo->DestRect.left; i<BltInfo->DestRect.right; i++)
+ {
+ Color = XLATEOBJ_iXlate(BltInfo->XlateSourceToDest, DIB_8BPP_GetPixel(BltInfo->SourceSurface, sx, sy));
+ DIB_1BPP_PutPixel(BltInfo->DestSurface, i, j, Color);
+ sx++;
+ }
+ sy++;
+ }
+ break;
+
+ case BMF_16BPP:
+ for (j=BltInfo->DestRect.top; j<BltInfo->DestRect.bottom; j++)
+ {
+ sx = BltInfo->SourcePoint.x;
+ for (i=BltInfo->DestRect.left; i<BltInfo->DestRect.right; i++)
+ {
+ Color = XLATEOBJ_iXlate(BltInfo->XlateSourceToDest, DIB_16BPP_GetPixel(BltInfo->SourceSurface, sx, sy));
+ DIB_1BPP_PutPixel(BltInfo->DestSurface, i, j, Color);
+ sx++;
+ }
+ sy++;
+ }
+ break;
+
+ case BMF_24BPP:
+ for (j=BltInfo->DestRect.top; j<BltInfo->DestRect.bottom; j++)
+ {
+ sx = BltInfo->SourcePoint.x;
+ for (i=BltInfo->DestRect.left; i<BltInfo->DestRect.right; i++)
+ {
+ Color = XLATEOBJ_iXlate(BltInfo->XlateSourceToDest, DIB_24BPP_GetPixel(BltInfo->SourceSurface, sx, sy));
+ DIB_1BPP_PutPixel(BltInfo->DestSurface, i, j, Color);
+ sx++;
+ }
+ sy++;
+ }
+ break;
+
+ case BMF_32BPP:
+ for (j=BltInfo->DestRect.top; j<BltInfo->DestRect.bottom; j++)
+ {
+ sx = BltInfo->SourcePoint.x;
+ for (i=BltInfo->DestRect.left; i<BltInfo->DestRect.right; i++)
+ {
+ Color = XLATEOBJ_iXlate(BltInfo->XlateSourceToDest, DIB_32BPP_GetPixel(BltInfo->SourceSurface, sx, sy));
+ DIB_1BPP_PutPixel(BltInfo->DestSurface, i, j, Color);
+ sx++;
+ }
+ sy++;
+ }
+ break;
+
+ default:
+ DbgPrint("DIB_1BPP_BitBlt: Unhandled Source BPP: %u\n", BitsPerFormat(BltInfo->SourceSurface->iBitmapFormat));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+BOOLEAN
+DIB_1BPP_BitBlt(PBLTINFO BltInfo)
+{
+ ULONG DestX, DestY;
+ ULONG SourceX, SourceY;
+ ULONG PatternY = 0;
+ ULONG Dest, Source = 0, Pattern = 0;
+ ULONG Index;
+ BOOLEAN UsesSource;
+ BOOLEAN UsesPattern;
+ PULONG DestBits;
+ ULONG RoundedRight;
+
+ UsesSource = ROP4_USES_SOURCE(BltInfo->Rop4);
+ UsesPattern = ROP4_USES_PATTERN(BltInfo->Rop4);
+
+ RoundedRight = BltInfo->DestRect.right -
+ ((BltInfo->DestRect.right - BltInfo->DestRect.left) & 31);
+ SourceY = BltInfo->SourcePoint.y;
+
+ if (UsesPattern)
+ {
+ if (BltInfo->PatternSurface)
+ {
+ PatternY = (BltInfo->DestRect.top + BltInfo->BrushOrigin.y) %
+ BltInfo->PatternSurface->sizlBitmap.cy;
+ }
+ else
+ {
+ /* FIXME: Shouldn't it be expanded? */
+ if (BltInfo->Brush)
+ Pattern = BltInfo->Brush->iSolidColor;
+ }
+ }
+
+ for (DestY = BltInfo->DestRect.top; DestY < BltInfo->DestRect.bottom; DestY++)
+ {
+ DestX = BltInfo->DestRect.left;
+ SourceX = BltInfo->SourcePoint.x;
+ DestBits = (PULONG)(
+ (PBYTE)BltInfo->DestSurface->pvScan0 +
+ (BltInfo->DestRect.left >> 3) +
+ DestY * BltInfo->DestSurface->lDelta);
+
+ if (DestX & 31)
+ {
+#if 0
+ /* FIXME: This case is completely untested!!! */
+
+ Dest = *((PBYTE)DestBits);
+ NoBits = 31 - (DestX & 31);
+
+ if (UsesSource)
+ {
+ Source = 0;
+ /* FIXME: This is incorrect! */
+ for (Index = 31 - NoBits; Index >= 0; Index++)
+ Source |= (DIB_GetSource(SourceSurf, SourceX + Index, SourceY, ColorTranslation) << (31 - Index));
+ }
+
+ if (BltInfo->PatternSurface)
+ {
+ Pattern = 0;
+ for (k = 31 - NoBits; k >= 0; k++)
+ Pattern |= (DIB_GetSourceIndex(PatternObj, (X + BrushOrigin.x + k) % PatternWidth, PatternY) << (31 - k));
+ }
+
+ Dest = DIB_DoRop(Rop4, Dest, Source, Pattern);
+ Dest &= ~((1 << (31 - NoBits)) - 1);
+ Dest |= *((PBYTE)DestBits) & ((1 << (31 - NoBits)) - 1);
+
+ *DestBits = Dest;
+
+ DestX += NoBits;
+ SourceX += NoBits;
+#endif
+ }
+
+ for (; DestX < RoundedRight; DestX += 32, DestBits++, SourceX += 32)
+ {
+ Dest = *DestBits;
+
+ if (UsesSource)
+ {
+ Source = 0;
+ for (Index = 0; Index < 8; Index++)
+ {
+ Source |= DIB_GetSource(BltInfo->SourceSurface, SourceX + Index, SourceY, BltInfo->XlateSourceToDest) << (7 - Index);
+ Source |= DIB_GetSource(BltInfo->SourceSurface, SourceX + Index + 8, SourceY, BltInfo->XlateSourceToDest) << (8 + (7 - Index));
+ Source |= DIB_GetSource(BltInfo->SourceSurface, SourceX + Index + 16, SourceY, BltInfo->XlateSourceToDest) << (16 + (7 - Index));
+ Source |= DIB_GetSource(BltInfo->SourceSurface, SourceX + Index + 24, SourceY, BltInfo->XlateSourceToDest) << (24 + (7 - Index));
+ }
+ }
+
+ if (BltInfo->PatternSurface)
+ {
+ Pattern = 0;
+ for (Index = 0; Index < 8; Index++)
+ {
+ Pattern |= DIB_GetSourceIndex(BltInfo->PatternSurface, (DestX + BltInfo->BrushOrigin.x + Index) % BltInfo->PatternSurface->sizlBitmap.cx, PatternY) << (7 - Index);
+ Pattern |= DIB_GetSourceIndex(BltInfo->PatternSurface, (DestX + BltInfo->BrushOrigin.x + Index + 8) % BltInfo->PatternSurface->sizlBitmap.cx, PatternY) << (8 + (7 - Index));
+ Pattern |= DIB_GetSourceIndex(BltInfo->PatternSurface, (DestX + BltInfo->BrushOrigin.x + Index + 16) % BltInfo->PatternSurface->sizlBitmap.cx, PatternY) << (16 + (7 - Index));
+ Pattern |= DIB_GetSourceIndex(BltInfo->PatternSurface, (DestX + BltInfo->BrushOrigin.x + Index + 24) % BltInfo->PatternSurface->sizlBitmap.cx, PatternY) << (24 + (7 - Index));
+ }
+ }
+
+ *DestBits = DIB_DoRop(BltInfo->Rop4, Dest, Source, Pattern);
+ }
+
+ if (DestX < BltInfo->DestRect.right)
+ {
+ for (; DestX < BltInfo->DestRect.right; DestX++, SourceX++)
+ {
+ Dest = DIB_1BPP_GetPixel(BltInfo->DestSurface, DestX, DestY);
+
+ if (UsesSource)
+ {
+ Source = DIB_GetSource(BltInfo->SourceSurface, SourceX, SourceY, BltInfo->XlateSourceToDest);
+ }
+
+ if (BltInfo->PatternSurface)
+ {
+ Pattern = DIB_GetSourceIndex(BltInfo->PatternSurface, (DestX + BltInfo->BrushOrigin.x) % BltInfo->PatternSurface->sizlBitmap.cx, PatternY);
+ }
+
+ DIB_1BPP_PutPixel(BltInfo->DestSurface, DestX, DestY, DIB_DoRop(BltInfo->Rop4, Dest, Source, Pattern) & 0xF);
+ }
+ }
+
+ SourceY++;
+ if (BltInfo->PatternSurface)
+ {
+ PatternY++;
+ PatternY %= BltInfo->PatternSurface->sizlBitmap.cy;
+ }
+ }
+
+ return TRUE;
+}
+
+/* BitBlt Optimize */
+BOOLEAN
+DIB_1BPP_ColorFill(SURFOBJ* DestSurface, RECTL* DestRect, ULONG color)
+{
+ ULONG DestY;
+
+ for (DestY = DestRect->top; DestY< DestRect->bottom; DestY++)
+ {
+ DIB_1BPP_HLine(DestSurface, DestRect->left, DestRect->right, DestY, color);
+ }
+ return TRUE;
+}
+
+BOOLEAN
+DIB_1BPP_TransparentBlt(SURFOBJ *DestSurf, SURFOBJ *SourceSurf,
+ RECTL* DestRect, RECTL *SourceRect,
+ XLATEOBJ *ColorTranslation, ULONG iTransColor)
+{
+ return FALSE;
+}
+
+/* EOF */
--- /dev/null
- ULONG RoundedRight, X, Y, SourceX = 0, SourceY = 0, Source, wd, Dest;
+/*
+ * PROJECT: Win32 subsystem
+ * LICENSE: See COPYING in the top level directory
+ * FILE: subsystems/win32/win32k/dib/dib8bpp.c
+ * PURPOSE: Device Independant Bitmap functions, 8bpp
+ * PROGRAMMERS: Jason Filby
+ * Thomas Bluemel
+ * Gregor Anich
+ */
+
+#include <win32k.h>
+
+#define NDEBUG
+#include <debug.h>
+
+VOID
+DIB_8BPP_PutPixel(SURFOBJ *SurfObj, LONG x, LONG y, ULONG c)
+{
+ PBYTE byteaddr = (PBYTE)SurfObj->pvScan0 + y * SurfObj->lDelta + x;
+
+ *byteaddr = c;
+}
+
+ULONG
+DIB_8BPP_GetPixel(SURFOBJ *SurfObj, LONG x, LONG y)
+{
+ PBYTE byteaddr = (PBYTE)SurfObj->pvScan0 + y * SurfObj->lDelta + x;
+
+ return (ULONG)(*byteaddr);
+}
+
+VOID
+DIB_8BPP_HLine(SURFOBJ *SurfObj, LONG x1, LONG x2, LONG y, ULONG c)
+{
+ memset((PBYTE)SurfObj->pvScan0 + y * SurfObj->lDelta + x1, (BYTE) c, x2 - x1);
+}
+
+VOID
+DIB_8BPP_VLine(SURFOBJ *SurfObj, LONG x, LONG y1, LONG y2, ULONG c)
+{
+ PBYTE byteaddr = (PBYTE)SurfObj->pvScan0 + y1 * SurfObj->lDelta;
+ PBYTE addr = byteaddr + x;
+ LONG lDelta = SurfObj->lDelta;
+
+ byteaddr = addr;
+ while(y1++ < y2)
+ {
+ *addr = c;
+
+ addr += lDelta;
+ }
+}
+
+BOOLEAN
+DIB_8BPP_BitBltSrcCopy(PBLTINFO BltInfo)
+{
+ LONG i, j, sx, sy, xColor, f1;
+ PBYTE SourceBits, DestBits, SourceLine, DestLine;
+ PBYTE SourceBits_4BPP, SourceLine_4BPP;
+
+ DestBits = (PBYTE)BltInfo->DestSurface->pvScan0 + (BltInfo->DestRect.top * BltInfo->DestSurface->lDelta) + BltInfo->DestRect.left;
+
+ switch(BltInfo->SourceSurface->iBitmapFormat)
+ {
+ case BMF_1BPP:
+ sx = BltInfo->SourcePoint.x;
+ sy = BltInfo->SourcePoint.y;
+
+ for (j=BltInfo->DestRect.top; j<BltInfo->DestRect.bottom; j++)
+ {
+ sx = BltInfo->SourcePoint.x;
+ for (i=BltInfo->DestRect.left; i<BltInfo->DestRect.right; i++)
+ {
+ if(DIB_1BPP_GetPixel(BltInfo->SourceSurface, sx, sy) == 0)
+ {
+ DIB_8BPP_PutPixel(BltInfo->DestSurface, i, j, XLATEOBJ_iXlate(BltInfo->XlateSourceToDest, 0));
+ }
+ else
+ {
+ DIB_8BPP_PutPixel(BltInfo->DestSurface, i, j, XLATEOBJ_iXlate(BltInfo->XlateSourceToDest, 1));
+ }
+ sx++;
+ }
+ sy++;
+ }
+ break;
+
+ case BMF_4BPP:
+ SourceBits_4BPP = (PBYTE)BltInfo->SourceSurface->pvScan0 + (BltInfo->SourcePoint.y * BltInfo->SourceSurface->lDelta) + (BltInfo->SourcePoint.x >> 1);
+
+ for (j=BltInfo->DestRect.top; j<BltInfo->DestRect.bottom; j++)
+ {
+ SourceLine_4BPP = SourceBits_4BPP;
+ sx = BltInfo->SourcePoint.x;
+ f1 = sx & 1;
+
+ for (i=BltInfo->DestRect.left; i<BltInfo->DestRect.right; i++)
+ {
+ xColor = XLATEOBJ_iXlate(BltInfo->XlateSourceToDest,
+ (*SourceLine_4BPP & altnotmask[f1]) >> (4 * (1 - f1)));
+ DIB_8BPP_PutPixel(BltInfo->DestSurface, i, j, xColor);
+ if(f1 == 1) { SourceLine_4BPP++; f1 = 0; } else { f1 = 1; }
+ sx++;
+ }
+
+ SourceBits_4BPP += BltInfo->SourceSurface->lDelta;
+ }
+ break;
+
+ case BMF_8BPP:
+ if (NULL == BltInfo->XlateSourceToDest || 0 != (BltInfo->XlateSourceToDest->flXlate & XO_TRIVIAL))
+ {
+ if (BltInfo->DestRect.top < BltInfo->SourcePoint.y)
+ {
+ SourceBits = (PBYTE)BltInfo->SourceSurface->pvScan0 + (BltInfo->SourcePoint.y * BltInfo->SourceSurface->lDelta) + BltInfo->SourcePoint.x;
+ for (j = BltInfo->DestRect.top; j < BltInfo->DestRect.bottom; j++)
+ {
+ RtlMoveMemory(DestBits, SourceBits, BltInfo->DestRect.right - BltInfo->DestRect.left);
+ SourceBits += BltInfo->SourceSurface->lDelta;
+ DestBits += BltInfo->DestSurface->lDelta;
+ }
+ }
+ else
+ {
+ SourceBits = (PBYTE)BltInfo->SourceSurface->pvScan0 + ((BltInfo->SourcePoint.y + BltInfo->DestRect.bottom - BltInfo->DestRect.top - 1) * BltInfo->SourceSurface->lDelta) + BltInfo->SourcePoint.x;
+ DestBits = (PBYTE)BltInfo->DestSurface->pvScan0 + ((BltInfo->DestRect.bottom - 1) * BltInfo->DestSurface->lDelta) + BltInfo->DestRect.left;
+ for (j = BltInfo->DestRect.bottom - 1; BltInfo->DestRect.top <= j; j--)
+ {
+ RtlMoveMemory(DestBits, SourceBits, BltInfo->DestRect.right - BltInfo->DestRect.left);
+ SourceBits -= BltInfo->SourceSurface->lDelta;
+ DestBits -= BltInfo->DestSurface->lDelta;
+ }
+ }
+ }
+ else
+ {
+ if (BltInfo->DestRect.top < BltInfo->SourcePoint.y)
+ {
+ SourceLine = (PBYTE)BltInfo->SourceSurface->pvScan0 + (BltInfo->SourcePoint.y * BltInfo->SourceSurface->lDelta) + BltInfo->SourcePoint.x;
+ DestLine = DestBits;
+ for (j = BltInfo->DestRect.top; j < BltInfo->DestRect.bottom; j++)
+ {
+ SourceBits = SourceLine;
+ DestBits = DestLine;
+ for (i=BltInfo->DestRect.left; i<BltInfo->DestRect.right; i++)
+ {
+ *DestBits++ = XLATEOBJ_iXlate(BltInfo->XlateSourceToDest, *SourceBits++);
+ }
+ SourceLine += BltInfo->SourceSurface->lDelta;
+ DestLine += BltInfo->DestSurface->lDelta;
+ }
+ }
+ else
+ {
+ SourceLine = (PBYTE)BltInfo->SourceSurface->pvScan0 + ((BltInfo->SourcePoint.y + BltInfo->DestRect.bottom - BltInfo->DestRect.top - 1) * BltInfo->SourceSurface->lDelta) + BltInfo->SourcePoint.x;
+ DestLine = (PBYTE)BltInfo->DestSurface->pvScan0 + ((BltInfo->DestRect.bottom - 1) * BltInfo->DestSurface->lDelta) + BltInfo->DestRect.left;
+ for (j = BltInfo->DestRect.bottom - 1; BltInfo->DestRect.top <= j; j--)
+ {
+ SourceBits = SourceLine;
+ DestBits = DestLine;
+ for (i=BltInfo->DestRect.left; i<BltInfo->DestRect.right; i++)
+ {
+ *DestBits++ = XLATEOBJ_iXlate(BltInfo->XlateSourceToDest, *SourceBits++);
+ }
+ SourceLine -= BltInfo->SourceSurface->lDelta;
+ DestLine -= BltInfo->DestSurface->lDelta;
+ }
+ }
+ }
+ break;
+
+ case BMF_16BPP:
+ SourceLine = (PBYTE)BltInfo->SourceSurface->pvScan0 + (BltInfo->SourcePoint.y * BltInfo->SourceSurface->lDelta) + 2 * BltInfo->SourcePoint.x;
+ DestLine = DestBits;
+
+ for (j = BltInfo->DestRect.top; j < BltInfo->DestRect.bottom; j++)
+ {
+ SourceBits = SourceLine;
+ DestBits = DestLine;
+
+ for (i = BltInfo->DestRect.left; i < BltInfo->DestRect.right; i++)
+ {
+ xColor = *((PWORD) SourceBits);
+ *DestBits = XLATEOBJ_iXlate(BltInfo->XlateSourceToDest, xColor);
+ SourceBits += 2;
+ DestBits += 1;
+ }
+
+ SourceLine += BltInfo->SourceSurface->lDelta;
+ DestLine += BltInfo->DestSurface->lDelta;
+ }
+ break;
+
+ case BMF_24BPP:
+ SourceLine = (PBYTE)BltInfo->SourceSurface->pvScan0 + (BltInfo->SourcePoint.y * BltInfo->SourceSurface->lDelta) + 3 * BltInfo->SourcePoint.x;
+ DestLine = DestBits;
+
+ for (j = BltInfo->DestRect.top; j < BltInfo->DestRect.bottom; j++)
+ {
+ SourceBits = SourceLine;
+ DestBits = DestLine;
+
+ for (i = BltInfo->DestRect.left; i < BltInfo->DestRect.right; i++)
+ {
+ xColor = (*(SourceBits + 2) << 0x10) +
+ (*(SourceBits + 1) << 0x08) +
+ (*(SourceBits));
+ *DestBits = XLATEOBJ_iXlate(BltInfo->XlateSourceToDest, xColor);
+ SourceBits += 3;
+ DestBits += 1;
+ }
+
+ SourceLine += BltInfo->SourceSurface->lDelta;
+ DestLine += BltInfo->DestSurface->lDelta;
+ }
+ break;
+
+ case BMF_32BPP:
+ SourceLine = (PBYTE)BltInfo->SourceSurface->pvScan0 + (BltInfo->SourcePoint.y * BltInfo->SourceSurface->lDelta) + 4 * BltInfo->SourcePoint.x;
+ DestLine = DestBits;
+
+ for (j = BltInfo->DestRect.top; j < BltInfo->DestRect.bottom; j++)
+ {
+ SourceBits = SourceLine;
+ DestBits = DestLine;
+
+ for (i = BltInfo->DestRect.left; i < BltInfo->DestRect.right; i++)
+ {
+ xColor = *((PDWORD) SourceBits);
+ *DestBits = XLATEOBJ_iXlate(BltInfo->XlateSourceToDest, xColor);
+ SourceBits += 4;
+ DestBits += 1;
+ }
+
+ SourceLine += BltInfo->SourceSurface->lDelta;
+ DestLine += BltInfo->DestSurface->lDelta;
+ }
+ break;
+
+ default:
+ DPRINT1("DIB_8BPP_Bitblt: Unhandled Source BPP: %u\n", BitsPerFormat(BltInfo->SourceSurface->iBitmapFormat));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* BitBlt Optimize */
+BOOLEAN
+DIB_8BPP_ColorFill(SURFOBJ* DestSurface, RECTL* DestRect, ULONG color)
+{
+ ULONG DestY;
+ for (DestY = DestRect->top; DestY< DestRect->bottom; DestY++)
+ {
+ DIB_8BPP_HLine(DestSurface, DestRect->left, DestRect->right, DestY, color);
+ }
+ return TRUE;
+}
+
+
+BOOLEAN
+DIB_8BPP_TransparentBlt(SURFOBJ *DestSurf, SURFOBJ *SourceSurf,
+ RECTL* DestRect, RECTL *SourceRect,
+ XLATEOBJ *ColorTranslation, ULONG iTransColor)
+{
- wd = DestSurf->lDelta - (DestRect->right - DestRect->left);
++ ULONG RoundedRight, X, Y, SourceX = 0, SourceY = 0, Source, Dest;
+ ULONG *DestBits;
+
+ LONG DstHeight;
+ LONG DstWidth;
+ LONG SrcHeight;
+ LONG SrcWidth;
+
+ DstHeight = DestRect->bottom - DestRect->top;
+ DstWidth = DestRect->right - DestRect->left;
+ SrcHeight = SourceRect->bottom - SourceRect->top;
+ SrcWidth = SourceRect->right - SourceRect->left;
+
+ RoundedRight = DestRect->right - ((DestRect->right - DestRect->left) & 0x3);
+ DestBits = (ULONG*)((PBYTE)DestSurf->pvScan0 + DestRect->left +
+ (DestRect->top * DestSurf->lDelta));
+
+ for(Y = DestRect->top; Y < DestRect->bottom; Y++)
+ {
+ DestBits = (ULONG*)((PBYTE)DestSurf->pvScan0 + DestRect->left +
+ (Y * DestSurf->lDelta));
+ SourceY = SourceRect->top+(Y - DestRect->top) * SrcHeight / DstHeight;
+ for (X = DestRect->left; X < RoundedRight; X += 4, DestBits++)
+ {
+ Dest = *DestBits;
+
+ SourceX = SourceRect->left+(X - DestRect->left) * SrcWidth / DstWidth;
+ if (SourceX >= 0 && SourceY >= 0 &&
+ SourceSurf->sizlBitmap.cx > SourceX && SourceSurf->sizlBitmap.cy > SourceY)
+ {
+ Source = DIB_GetSourceIndex(SourceSurf, SourceX, SourceY);
+ if(Source != iTransColor)
+ {
+ Dest &= 0xFFFFFF00;
+ Dest |= (XLATEOBJ_iXlate(ColorTranslation, Source) & 0xFF);
+ }
+ }
+
+ SourceX = SourceRect->left+(X+1 - DestRect->left) * SrcWidth / DstWidth;
+ if (SourceX >= 0 && SourceY >= 0 &&
+ SourceSurf->sizlBitmap.cx > SourceX && SourceSurf->sizlBitmap.cy > SourceY)
+ {
+ Source = DIB_GetSourceIndex(SourceSurf, SourceX, SourceY);
+ if(Source != iTransColor)
+ {
+ Dest &= 0xFFFF00FF;
+ Dest |= ((XLATEOBJ_iXlate(ColorTranslation, Source) << 8) & 0xFF00);
+ }
+ }
+
+ SourceX = SourceRect->left+(X+2 - DestRect->left) * SrcWidth / DstWidth;
+ if (SourceX >= 0 && SourceY >= 0 &&
+ SourceSurf->sizlBitmap.cx > SourceX && SourceSurf->sizlBitmap.cy > SourceY)
+ {
+ Source = DIB_GetSourceIndex(SourceSurf, SourceX, SourceY);
+ if(Source != iTransColor)
+ {
+ Dest &= 0xFF00FFFF;
+ Dest |= ((XLATEOBJ_iXlate(ColorTranslation, Source) << 16) & 0xFF0000);
+ }
+ }
+
+ SourceX = SourceRect->left+(X+3 - DestRect->left) * SrcWidth / DstWidth;
+ if (SourceX >= 0 && SourceY >= 0 &&
+ SourceSurf->sizlBitmap.cx > SourceX && SourceSurf->sizlBitmap.cy > SourceY)
+ {
+ Source = DIB_GetSourceIndex(SourceSurf, SourceX, SourceY);
+ if(Source != iTransColor)
+ {
+ Dest &= 0x00FFFFFF;
+ Dest |= ((XLATEOBJ_iXlate(ColorTranslation, Source) << 24) & 0xFF000000);
+ }
+ }
+
+ *DestBits = Dest;
+ }
+
+ if(X < DestRect->right)
+ {
+ for (; X < DestRect->right; X++)
+ {
+ SourceX = SourceRect->left+(X - DestRect->left) * SrcWidth / DstWidth;
+ if (SourceX >= 0 && SourceY >= 0 &&
+ SourceSurf->sizlBitmap.cx > SourceX && SourceSurf->sizlBitmap.cy > SourceY)
+ {
+ Source = DIB_GetSourceIndex(SourceSurf, SourceX, SourceY);
+ if(Source != iTransColor)
+ {
+ *((BYTE*)DestBits) = (BYTE)(XLATEOBJ_iXlate(ColorTranslation, Source) & 0xFF);
+ }
+ }
+ DestBits = (PULONG)((ULONG_PTR)DestBits + 1);
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+/* EOF */
--- /dev/null
- BOOL UsesPattern;
+/*
+ * COPYRIGHT: See COPYING in the top level directory
+ * PROJECT: ReactOS kernel
+ * PURPOSE: GDI BitBlt Functions
+ * FILE: subsys/win32k/eng/bitblt.c
+ * PROGRAMER: Jason Filby
+ * Timo Kreuzer
+ * REVISION HISTORY:
+ * 2/10/1999: Created
+ */
+
+#include <win32k.h>
+
+#define NDEBUG
+#include <debug.h>
+
+typedef BOOLEAN (APIENTRY *PBLTRECTFUNC)(SURFOBJ* OutputObj,
+ SURFOBJ* InputObj,
+ SURFOBJ* Mask,
+ XLATEOBJ* ColorTranslation,
+ RECTL* OutputRect,
+ POINTL* InputPoint,
+ POINTL* MaskOrigin,
+ BRUSHOBJ* pbo,
+ POINTL* BrushOrigin,
+ ROP4 Rop4);
+
+static BOOLEAN APIENTRY
+BltMask(SURFOBJ* psoDest,
+ SURFOBJ* psoSource, // unused
+ SURFOBJ* psoMask,
+ XLATEOBJ* ColorTranslation, // unused
+ RECTL* prclDest,
+ POINTL* pptlSource, // unused
+ POINTL* pptlMask,
+ BRUSHOBJ* pbo,
+ POINTL* pptlBrush,
+ ROP4 Rop4)
+{
+ LONG x, y;
+ BYTE *pjMskLine, *pjMskCurrent;
+ BYTE fjMaskBit0, fjMaskBit;
+ /* Pattern brushes */
+ PEBRUSHOBJ pebo = NULL;
+ SURFOBJ *psoPattern = NULL;
+ PSURFACE psurfPattern;
+ ULONG PatternWidth = 0, PatternHeight = 0;
+ LONG PatternX0 = 0, PatternX = 0, PatternY = 0;
+ PFN_DIB_PutPixel fnDest_PutPixel = NULL;
+ PFN_DIB_GetPixel fnPattern_GetPixel = NULL;
+ ULONG Pattern = 0;
+ HBITMAP hbmPattern;
+
+ ASSERT(psoSource == NULL);
+ ASSERT(pptlSource == NULL);
+
+ if (psoMask == NULL)
+ {
+ return FALSE;
+ }
+
+ if (pbo && pbo->iSolidColor == 0xFFFFFFFF)
+ {
+ pebo = CONTAINING_RECORD(pbo, EBRUSHOBJ, BrushObject);
+
+ hbmPattern = EBRUSHOBJ_pvGetEngBrush(pebo);
+ psurfPattern = SURFACE_LockSurface(hbmPattern);
+ if (psurfPattern != NULL)
+ {
+ psoPattern = &psurfPattern->SurfObj;
+ PatternWidth = psoPattern->sizlBitmap.cx;
+ PatternHeight = psoPattern->sizlBitmap.cy;
+ fnPattern_GetPixel = DibFunctionsForBitmapFormat[psoPattern->iBitmapFormat].DIB_GetPixel;
+ }
+ }
+ else
+ psurfPattern = NULL;
+
+ pjMskLine = (PBYTE)psoMask->pvScan0 + pptlMask->y * psoMask->lDelta + (pptlMask->x >> 3);
+ fjMaskBit0 = 0x80 >> (pptlMask->x & 0x07);
+
+ fnDest_PutPixel = DibFunctionsForBitmapFormat[psoDest->iBitmapFormat].DIB_PutPixel;
+ if (psurfPattern)
+ {
+ PatternY = (prclDest->top - pptlBrush->y) % PatternHeight;
+ if (PatternY < 0)
+ {
+ PatternY += PatternHeight;
+ }
+ PatternX0 = (prclDest->left - pptlBrush->x) % PatternWidth;
+ if (PatternX0 < 0)
+ {
+ PatternX0 += PatternWidth;
+ }
+
+ for (y = prclDest->top; y < prclDest->bottom; y++)
+ {
+ pjMskCurrent = pjMskLine;
+ fjMaskBit = fjMaskBit0;
+ PatternX = PatternX0;
+
+ for (x = prclDest->left; x < prclDest->right; x++)
+ {
+ if (*pjMskCurrent & fjMaskBit)
+ {
+ fnDest_PutPixel(psoDest, x, y,
+ fnPattern_GetPixel(psoPattern, PatternX, PatternY));
+ }
+ fjMaskBit = _rotr8(fjMaskBit, 1);
+ pjMskCurrent += (fjMaskBit >> 7);
+ PatternX++;
+ PatternX %= PatternWidth;
+ }
+ pjMskLine += psoMask->lDelta;
+ PatternY++;
+ PatternY %= PatternHeight;
+ }
+ }
+ else
+ {
+ Pattern = pbo ? pbo->iSolidColor : 0;
+ for (y = prclDest->top; y < prclDest->bottom; y++)
+ {
+ pjMskCurrent = pjMskLine;
+ fjMaskBit = fjMaskBit0;
+
+ for (x = prclDest->left; x < prclDest->right; x++)
+ {
+ if (*pjMskCurrent & fjMaskBit)
+ {
+ fnDest_PutPixel(psoDest, x, y, Pattern);
+ }
+ fjMaskBit = _rotr8(fjMaskBit, 1);
+ pjMskCurrent += (fjMaskBit >> 7);
+ }
+ pjMskLine += psoMask->lDelta;
+ }
+ }
+
+ if (psurfPattern)
+ SURFACE_UnlockSurface(psurfPattern);
+
+ return TRUE;
+}
+
+static BOOLEAN APIENTRY
+BltPatCopy(SURFOBJ* Dest,
+ SURFOBJ* Source,
+ SURFOBJ* Mask,
+ XLATEOBJ* ColorTranslation,
+ RECTL* DestRect,
+ POINTL* SourcePoint,
+ POINTL* MaskPoint,
+ BRUSHOBJ* pbo,
+ POINTL* BrushPoint,
+ ROP4 Rop4)
+{
+ // These functions are assigned if we're working with a DIB
+ // The assigned functions depend on the bitsPerPixel of the DIB
+
+ DibFunctionsForBitmapFormat[Dest->iBitmapFormat].DIB_ColorFill(Dest, DestRect, pbo ? pbo->iSolidColor : 0);
+
+ return TRUE;
+}
+
+static BOOLEAN APIENTRY
+CallDibBitBlt(SURFOBJ* OutputObj,
+ SURFOBJ* InputObj,
+ SURFOBJ* Mask,
+ XLATEOBJ* ColorTranslation,
+ RECTL* OutputRect,
+ POINTL* InputPoint,
+ POINTL* MaskOrigin,
+ BRUSHOBJ* pbo,
+ POINTL* BrushOrigin,
+ ROP4 Rop4)
+{
+ BLTINFO BltInfo;
+ PEBRUSHOBJ GdiBrush = NULL;
+ SURFACE *psurfPattern;
+ BOOLEAN Result;
+ HBITMAP hbmPattern;
+
+ BltInfo.DestSurface = OutputObj;
+ BltInfo.SourceSurface = InputObj;
+ BltInfo.PatternSurface = NULL;
+ BltInfo.XlateSourceToDest = ColorTranslation;
+ BltInfo.DestRect = *OutputRect;
+ BltInfo.SourcePoint = *InputPoint;
+
+ if (ROP3_TO_ROP4(SRCCOPY) == Rop4)
+ return DibFunctionsForBitmapFormat[OutputObj->iBitmapFormat].DIB_BitBltSrcCopy(&BltInfo);
+
+ BltInfo.Brush = pbo;
+ BltInfo.BrushOrigin = *BrushOrigin;
+ BltInfo.Rop4 = Rop4;
+
+ /* Pattern brush */
+ if (ROP4_USES_PATTERN(Rop4) && pbo && pbo->iSolidColor == 0xFFFFFFFF)
+ {
+ GdiBrush = CONTAINING_RECORD(pbo, EBRUSHOBJ, BrushObject);
+ hbmPattern = EBRUSHOBJ_pvGetEngBrush(GdiBrush);
+ psurfPattern = SURFACE_LockSurface(hbmPattern);
+ if (psurfPattern)
+ {
+ BltInfo.PatternSurface = &psurfPattern->SurfObj;
+ }
+ else
+ {
+ /* FIXME - What to do here? */
+ }
+ }
+ else
+ {
+ psurfPattern = NULL;
+ }
+
+ Result = DibFunctionsForBitmapFormat[OutputObj->iBitmapFormat].DIB_BitBlt(&BltInfo);
+
+ /* Pattern brush */
+ if (psurfPattern)
+ {
+ SURFACE_UnlockSurface(psurfPattern);
+ }
+
+ return Result;
+}
+
+INT __cdecl abs(INT nm);
+
+
+/*
+ * @implemented
+ */
+BOOL APIENTRY
+NtGdiEngBitBlt(
+ IN SURFOBJ *psoTrg,
+ IN SURFOBJ *psoSrc,
+ IN SURFOBJ *psoMask,
+ IN CLIPOBJ *pco,
+ IN XLATEOBJ *pxlo,
+ IN RECTL *prclTrg,
+ IN POINTL *pptlSrc,
+ IN POINTL *pptlMask,
+ IN BRUSHOBJ *pbo,
+ IN POINTL *pptlBrush,
+ IN ROP4 rop4 )
+{
+ RECTL rclTrg;
+ POINTL ptlSrc;
+ POINTL ptlMask;
+ POINTL ptlBrush;
+
+ _SEH2_TRY
+ {
+ ProbeForRead(prclTrg, sizeof(RECTL), 1);
+ RtlCopyMemory(&rclTrg,prclTrg, sizeof(RECTL));
+
+ ProbeForRead(pptlSrc, sizeof(POINTL), 1);
+ RtlCopyMemory(&ptlSrc, pptlSrc, sizeof(POINTL));
+
+ ProbeForRead(pptlMask, sizeof(POINTL), 1);
+ RtlCopyMemory(&ptlMask, pptlMask, sizeof(POINTL));
+
+ ProbeForRead(pptlBrush, sizeof(POINTL), 1);
+ RtlCopyMemory(&ptlBrush, pptlBrush, sizeof(POINTL));
+
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ _SEH2_YIELD(return FALSE);
+ }
+ _SEH2_END;
+
+ return EngBitBlt(psoTrg, psoSrc, psoMask, pco, pxlo, &rclTrg, &ptlSrc, &ptlMask, pbo, &ptlBrush, rop4);
+}
+
+/*
+ * @implemented
+ */
+BOOL APIENTRY
+EngBitBlt(SURFOBJ *DestObj,
+ SURFOBJ *SourceObj,
+ SURFOBJ *Mask,
+ CLIPOBJ *ClipRegion,
+ XLATEOBJ *ColorTranslation,
+ RECTL *DestRect,
+ POINTL *SourcePoint,
+ POINTL *MaskOrigin,
+ BRUSHOBJ *pbo,
+ POINTL *BrushOrigin,
+ ROP4 rop4)
+{
+ BYTE clippingType;
+ RECTL CombinedRect;
+ RECT_ENUM RectEnum;
+ BOOL EnumMore;
+ POINTL InputPoint;
+ RECTL InputRect;
+ RECTL OutputRect;
+ SURFOBJ* InputObj = 0;
+ SURFOBJ* OutputObj;
+ PBLTRECTFUNC BltRectFunc;
+ BOOLEAN Ret = TRUE;
+ RECTL ClipRect;
+ unsigned i;
+ POINTL Pt;
+ ULONG Direction;
+ BOOL UsesSource;
- UsesPattern = ROP4_USES_PATTERN(rop4);
+ POINTL AdjustedBrushOrigin;
+
+ UsesSource = ROP4_USES_SOURCE(rop4);
+ if (R4_NOOP == rop4)
+ {
+ /* Copy destination onto itself: nop */
+ return TRUE;
+ }
+
+ OutputRect = *DestRect;
+ if (OutputRect.right < OutputRect.left)
+ {
+ OutputRect.left = DestRect->right;
+ OutputRect.right = DestRect->left;
+ }
+ if (OutputRect.bottom < OutputRect.top)
+ {
+ OutputRect.left = DestRect->right;
+ OutputRect.right = DestRect->left;
+ }
+
+ if (UsesSource)
+ {
+ if (NULL == SourcePoint)
+ {
+ return FALSE;
+ }
+
+ /* Make sure we don't try to copy anything outside the valid source
+ region */
+ InputPoint = *SourcePoint;
+ if (InputPoint.x < 0)
+ {
+ OutputRect.left -= InputPoint.x;
+ InputPoint.x = 0;
+ }
+ if (InputPoint.y < 0)
+ {
+ OutputRect.top -= InputPoint.y;
+ InputPoint.y = 0;
+ }
+ if (SourceObj->sizlBitmap.cx < InputPoint.x +
+ OutputRect.right - OutputRect.left)
+ {
+ OutputRect.right = OutputRect.left +
+ SourceObj->sizlBitmap.cx - InputPoint.x;
+ }
+ if (SourceObj->sizlBitmap.cy < InputPoint.y +
+ OutputRect.bottom - OutputRect.top)
+ {
+ OutputRect.bottom = OutputRect.top +
+ SourceObj->sizlBitmap.cy - InputPoint.y;
+ }
+
+ InputRect.left = InputPoint.x;
+ InputRect.right = InputPoint.x + (OutputRect.right - OutputRect.left);
+ InputRect.top = InputPoint.y;
+ InputRect.bottom = InputPoint.y + (OutputRect.bottom - OutputRect.top);
+
+ InputObj = SourceObj;
+ }
+ else
+ {
+ InputRect.left = 0;
+ InputRect.right = DestRect->right - DestRect->left;
+ InputRect.top = 0;
+ InputRect.bottom = DestRect->bottom - DestRect->top;
+ }
+
+ if (NULL != ClipRegion)
+ {
+ if (OutputRect.left < ClipRegion->rclBounds.left)
+ {
+ InputRect.left += ClipRegion->rclBounds.left - OutputRect.left;
+ InputPoint.x += ClipRegion->rclBounds.left - OutputRect.left;
+ OutputRect.left = ClipRegion->rclBounds.left;
+ }
+ if (ClipRegion->rclBounds.right < OutputRect.right)
+ {
+ InputRect.right -= OutputRect.right - ClipRegion->rclBounds.right;
+ OutputRect.right = ClipRegion->rclBounds.right;
+ }
+ if (OutputRect.top < ClipRegion->rclBounds.top)
+ {
+ InputRect.top += ClipRegion->rclBounds.top - OutputRect.top;
+ InputPoint.y += ClipRegion->rclBounds.top - OutputRect.top;
+ OutputRect.top = ClipRegion->rclBounds.top;
+ }
+ if (ClipRegion->rclBounds.bottom < OutputRect.bottom)
+ {
+ InputRect.bottom -= OutputRect.bottom - ClipRegion->rclBounds.bottom;
+ OutputRect.bottom = ClipRegion->rclBounds.bottom;
+ }
+ }
+
+ /* Check for degenerate case: if height or width of OutputRect is 0 pixels
+ there's nothing to do */
+ if (OutputRect.right <= OutputRect.left ||
+ OutputRect.bottom <= OutputRect.top)
+ {
+ return TRUE;
+ }
+
+ OutputObj = DestObj;
+
+ if (BrushOrigin)
+ {
+ AdjustedBrushOrigin.x = BrushOrigin->x;
+ AdjustedBrushOrigin.y = BrushOrigin->y;
+ }
+ else
+ {
+ AdjustedBrushOrigin.x = 0;
+ AdjustedBrushOrigin.y = 0;
+ }
+
+ /* Determine clipping type */
+ if (ClipRegion == (CLIPOBJ *) NULL)
+ {
+ clippingType = DC_TRIVIAL;
+ }
+ else
+ {
+ clippingType = ClipRegion->iDComplexity;
+ }
+
+ if (R4_MASK == rop4)
+ {
+ BltRectFunc = BltMask;
+ }
+ else if (ROP3_TO_ROP4(PATCOPY) == rop4)
+ {
+ if (pbo && pbo->iSolidColor == 0xFFFFFFFF)
+ BltRectFunc = CallDibBitBlt;
+ else
+ BltRectFunc = BltPatCopy;
+ }
+ else
+ {
+ BltRectFunc = CallDibBitBlt;
+ }
+
+
+ switch (clippingType)
+ {
+ case DC_TRIVIAL:
+ Ret = (*BltRectFunc)(OutputObj, InputObj, Mask, ColorTranslation,
+ &OutputRect, &InputPoint, MaskOrigin, pbo,
+ &AdjustedBrushOrigin, rop4);
+ break;
+ case DC_RECT:
+ /* Clip the blt to the clip rectangle */
+ ClipRect.left = ClipRegion->rclBounds.left;
+ ClipRect.right = ClipRegion->rclBounds.right;
+ ClipRect.top = ClipRegion->rclBounds.top;
+ ClipRect.bottom = ClipRegion->rclBounds.bottom;
+ if (RECTL_bIntersectRect(&CombinedRect, &OutputRect, &ClipRect))
+ {
+ Pt.x = InputPoint.x + CombinedRect.left - OutputRect.left;
+ Pt.y = InputPoint.y + CombinedRect.top - OutputRect.top;
+ Ret = (*BltRectFunc)(OutputObj, InputObj, Mask, ColorTranslation,
+ &CombinedRect, &Pt, MaskOrigin, pbo,
+ &AdjustedBrushOrigin, rop4);
+ }
+ break;
+ case DC_COMPLEX:
+ Ret = TRUE;
+ if (OutputObj == InputObj)
+ {
+ if (OutputRect.top < InputPoint.y)
+ {
+ Direction = OutputRect.left < InputPoint.x ?
+ CD_RIGHTDOWN : CD_LEFTDOWN;
+ }
+ else
+ {
+ Direction = OutputRect.left < InputPoint.x ?
+ CD_RIGHTUP : CD_LEFTUP;
+ }
+ }
+ else
+ {
+ Direction = CD_ANY;
+ }
+ CLIPOBJ_cEnumStart(ClipRegion, FALSE, CT_RECTANGLES, Direction, 0);
+ do
+ {
+ EnumMore = CLIPOBJ_bEnum(ClipRegion,(ULONG) sizeof(RectEnum),
+ (PVOID) &RectEnum);
+
+ for (i = 0; i < RectEnum.c; i++)
+ {
+ ClipRect.left = RectEnum.arcl[i].left;
+ ClipRect.right = RectEnum.arcl[i].right;
+ ClipRect.top = RectEnum.arcl[i].top;
+ ClipRect.bottom = RectEnum.arcl[i].bottom;
+ if (RECTL_bIntersectRect(&CombinedRect, &OutputRect, &ClipRect))
+ {
+ Pt.x = InputPoint.x + CombinedRect.left - OutputRect.left;
+ Pt.y = InputPoint.y + CombinedRect.top - OutputRect.top;
+ Ret = (*BltRectFunc)(OutputObj, InputObj, Mask,
+ ColorTranslation, &CombinedRect, &Pt,
+ MaskOrigin, pbo, &AdjustedBrushOrigin,
+ rop4) && Ret;
+ }
+ }
+ }
+ while (EnumMore);
+ break;
+ }
+
+ return Ret;
+}
+
+BOOL APIENTRY
+IntEngBitBlt(
+ SURFOBJ *psoTrg,
+ SURFOBJ *psoSrc,
+ SURFOBJ *psoMask,
+ CLIPOBJ *pco,
+ XLATEOBJ *pxlo,
+ RECTL *prclTrg,
+ POINTL *pptlSrc,
+ POINTL *pptlMask,
+ BRUSHOBJ *pbo,
+ POINTL *pptlBrush,
+ ROP4 rop4)
+{
+ SURFACE *psurfTrg;
+ SURFACE *psurfSrc = NULL;
+ BOOL bResult;
+ RECTL rclClipped;
+ RECTL rclSrc;
+// INTENG_ENTER_LEAVE EnterLeaveSource;
+// INTENG_ENTER_LEAVE EnterLeaveDest;
+ PFN_DrvBitBlt pfnBitBlt;
+
+ ASSERT(psoTrg);
+ psurfTrg = CONTAINING_RECORD(psoTrg, SURFACE, SurfObj);
+
+ /* FIXME: Should we really allow to pass non-well-ordered rects? */
+ rclClipped = *prclTrg;
+ RECTL_vMakeWellOrdered(&rclClipped);
+
+ /* Clip target rect against the bounds of the clipping region */
+ if (pco)
+ {
+ if (!RECTL_bIntersectRect(&rclClipped, &rclClipped, &pco->rclBounds))
+ {
+ /* Nothing left */
+ return TRUE;
+ }
+
+ /* Don't pass a clipobj with only a single rect */
+ if (pco->iDComplexity == DC_RECT)
+ pco = NULL;
+ }
+
+ if (ROP4_USES_SOURCE(rop4))
+ {
+ ASSERT(psoSrc);
+ psurfSrc = CONTAINING_RECORD(psoSrc, SURFACE, SurfObj);
+
+ /* Calculate source rect */
+ rclSrc.left = pptlSrc->x + rclClipped.left - prclTrg->left;
+ rclSrc.top = pptlSrc->y + rclClipped.top - prclTrg->top;
+ rclSrc.right = rclSrc.left + rclClipped.right - rclClipped.left;
+ rclSrc.bottom = rclSrc.top + rclClipped.bottom - rclClipped.top;
+ }
+ else
+ {
+ psoSrc = NULL;
+ psurfSrc = NULL;
+ }
+
+ /* Is the target surface device managed? */
+ if (psurfTrg->flags & HOOK_BITBLT)
+ {
+ /* Is the source a different device managed surface? */
+ if (psoSrc && psoSrc->hdev != psoTrg->hdev && psurfSrc->flags & HOOK_BITBLT)
+ {
+ DPRINT1("Need to copy to standard bitmap format!\n");
+ ASSERT(FALSE);
+ }
+
+ pfnBitBlt = GDIDEVFUNCS(psoTrg).BitBlt;
+ }
+
+ /* Is the source surface device managed? */
+ else if (psoSrc && psurfSrc->flags & HOOK_BITBLT)
+ {
+ pfnBitBlt = GDIDEVFUNCS(psoSrc).BitBlt;
+ }
+ else
+ {
+ pfnBitBlt = EngBitBlt;
+ }
+
+ bResult = pfnBitBlt(psoTrg,
+ psoSrc,
+ psoMask,
+ pco,
+ pxlo,
+ &rclClipped,
+ (POINTL*)&rclSrc,
+ pptlMask,
+ pbo,
+ pptlBrush,
+ rop4);
+
+ // FIXME: cleanup temp surface!
+
+ return bResult;
+}
+
+
+/**** REACTOS FONT RENDERING CODE *********************************************/
+
+/* renders the alpha mask bitmap */
+static BOOLEAN APIENTRY
+AlphaBltMask(SURFOBJ* psoDest,
+ SURFOBJ* psoSource, // unused
+ SURFOBJ* psoMask,
+ XLATEOBJ* pxloRGB2Dest,
+ XLATEOBJ* pxloBrush,
+ RECTL* prclDest,
+ POINTL* pptlSource, // unused
+ POINTL* pptlMask,
+ BRUSHOBJ* pbo,
+ POINTL* pptlBrush)
+{
+ LONG i, j, dx, dy;
+ int r, g, b;
+ ULONG Background, BrushColor, NewColor;
+ BYTE *tMask, *lMask;
+
+ ASSERT(psoSource == NULL);
+ ASSERT(pptlSource == NULL);
+
+ dx = prclDest->right - prclDest->left;
+ dy = prclDest->bottom - prclDest->top;
+
+ if (psoMask != NULL)
+ {
+ BrushColor = XLATEOBJ_iXlate(pxloBrush, pbo ? pbo->iSolidColor : 0);
+ r = (int)GetRValue(BrushColor);
+ g = (int)GetGValue(BrushColor);
+ b = (int)GetBValue(BrushColor);
+
+ tMask = (PBYTE)psoMask->pvScan0 + (pptlMask->y * psoMask->lDelta) + pptlMask->x;
+ for (j = 0; j < dy; j++)
+ {
+ lMask = tMask;
+ for (i = 0; i < dx; i++)
+ {
+ if (*lMask > 0)
+ {
+ if (*lMask == 0xff)
+ {
+ DibFunctionsForBitmapFormat[psoDest->iBitmapFormat].DIB_PutPixel(
+ psoDest, prclDest->left + i, prclDest->top + j, pbo ? pbo->iSolidColor : 0);
+ }
+ else
+ {
+ Background = DIB_GetSource(psoDest, prclDest->left + i, prclDest->top + j,
+ pxloBrush);
+
+ NewColor =
+ RGB((*lMask * (r - GetRValue(Background)) >> 8) + GetRValue(Background),
+ (*lMask * (g - GetGValue(Background)) >> 8) + GetGValue(Background),
+ (*lMask * (b - GetBValue(Background)) >> 8) + GetBValue(Background));
+
+ Background = XLATEOBJ_iXlate(pxloRGB2Dest, NewColor);
+ DibFunctionsForBitmapFormat[psoDest->iBitmapFormat].DIB_PutPixel(
+ psoDest, prclDest->left + i, prclDest->top + j, Background);
+ }
+ }
+ lMask++;
+ }
+ tMask += psoMask->lDelta;
+ }
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+static
+BOOL APIENTRY
+EngMaskBitBlt(SURFOBJ *psoDest,
+ SURFOBJ *psoMask,
+ CLIPOBJ *ClipRegion,
+ XLATEOBJ *DestColorTranslation,
+ XLATEOBJ *SourceColorTranslation,
+ RECTL *DestRect,
+ POINTL *pptlMask,
+ BRUSHOBJ *pbo,
+ POINTL *BrushOrigin)
+{
+ BYTE clippingType;
+ RECTL CombinedRect;
+ RECT_ENUM RectEnum;
+ BOOL EnumMore;
+ POINTL InputPoint;
+ RECTL InputRect;
+ RECTL OutputRect;
+ POINTL Translate;
+ INTENG_ENTER_LEAVE EnterLeaveSource;
+ INTENG_ENTER_LEAVE EnterLeaveDest;
+ SURFOBJ* psoInput;
+ SURFOBJ* psoOutput;
+ BOOLEAN Ret = TRUE;
+ RECTL ClipRect;
+ unsigned i;
+ POINTL Pt;
+ ULONG Direction;
+ POINTL AdjustedBrushOrigin;
+
+ ASSERT(psoMask);
+
+ if (pptlMask)
+ {
+ InputRect.left = pptlMask->x;
+ InputRect.right = pptlMask->x + (DestRect->right - DestRect->left);
+ InputRect.top = pptlMask->y;
+ InputRect.bottom = pptlMask->y + (DestRect->bottom - DestRect->top);
+ }
+ else
+ {
+ InputRect.left = 0;
+ InputRect.right = DestRect->right - DestRect->left;
+ InputRect.top = 0;
+ InputRect.bottom = DestRect->bottom - DestRect->top;
+ }
+
+ OutputRect = *DestRect;
+ if (NULL != ClipRegion)
+ {
+ if (OutputRect.left < ClipRegion->rclBounds.left)
+ {
+ InputRect.left += ClipRegion->rclBounds.left - OutputRect.left;
+ OutputRect.left = ClipRegion->rclBounds.left;
+ }
+ if (ClipRegion->rclBounds.right < OutputRect.right)
+ {
+ InputRect.right -= OutputRect.right - ClipRegion->rclBounds.right;
+ OutputRect.right = ClipRegion->rclBounds.right;
+ }
+ if (OutputRect.top < ClipRegion->rclBounds.top)
+ {
+ InputRect.top += ClipRegion->rclBounds.top - OutputRect.top;
+ OutputRect.top = ClipRegion->rclBounds.top;
+ }
+ if (ClipRegion->rclBounds.bottom < OutputRect.bottom)
+ {
+ InputRect.bottom -= OutputRect.bottom - ClipRegion->rclBounds.bottom;
+ OutputRect.bottom = ClipRegion->rclBounds.bottom;
+ }
+ }
+
+ if (! IntEngEnter(&EnterLeaveSource, psoMask, &InputRect, TRUE, &Translate, &psoInput))
+ {
+ return FALSE;
+ }
+
+ InputPoint.x = InputRect.left + Translate.x;
+ InputPoint.y = InputRect.top + Translate.y;
+
+ /* Check for degenerate case: if height or width of OutputRect is 0 pixels there's
+ nothing to do */
+ if (OutputRect.right <= OutputRect.left || OutputRect.bottom <= OutputRect.top)
+ {
+ IntEngLeave(&EnterLeaveSource);
+ return TRUE;
+ }
+
+ if (! IntEngEnter(&EnterLeaveDest, psoDest, &OutputRect, FALSE, &Translate, &psoOutput))
+ {
+ IntEngLeave(&EnterLeaveSource);
+ return FALSE;
+ }
+
+ OutputRect.left = DestRect->left + Translate.x;
+ OutputRect.right = DestRect->right + Translate.x;
+ OutputRect.top = DestRect->top + Translate.y;
+ OutputRect.bottom = DestRect->bottom + Translate.y;
+
+ if (BrushOrigin)
+ {
+ AdjustedBrushOrigin.x = BrushOrigin->x + Translate.x;
+ AdjustedBrushOrigin.y = BrushOrigin->y + Translate.y;
+ }
+ else
+ AdjustedBrushOrigin = Translate;
+
+ // Determine clipping type
+ if (ClipRegion == (CLIPOBJ *) NULL)
+ {
+ clippingType = DC_TRIVIAL;
+ } else {
+ clippingType = ClipRegion->iDComplexity;
+ }
+
+ switch (clippingType)
+ {
+ case DC_TRIVIAL:
+ if (psoMask->iBitmapFormat == BMF_8BPP)
+ Ret = AlphaBltMask(psoOutput, NULL , psoInput, DestColorTranslation, SourceColorTranslation,
+ &OutputRect, NULL, &InputPoint, pbo, &AdjustedBrushOrigin);
+ else
+ Ret = BltMask(psoOutput, NULL, psoInput, DestColorTranslation,
+ &OutputRect, NULL, &InputPoint, pbo, &AdjustedBrushOrigin,
+ R4_MASK);
+ break;
+ case DC_RECT:
+ // Clip the blt to the clip rectangle
+ ClipRect.left = ClipRegion->rclBounds.left + Translate.x;
+ ClipRect.right = ClipRegion->rclBounds.right + Translate.x;
+ ClipRect.top = ClipRegion->rclBounds.top + Translate.y;
+ ClipRect.bottom = ClipRegion->rclBounds.bottom + Translate.y;
+ if (RECTL_bIntersectRect(&CombinedRect, &OutputRect, &ClipRect))
+ {
+ Pt.x = InputPoint.x + CombinedRect.left - OutputRect.left;
+ Pt.y = InputPoint.y + CombinedRect.top - OutputRect.top;
+ if (psoMask->iBitmapFormat == BMF_8BPP)
+ {
+ Ret = AlphaBltMask(psoOutput, NULL, psoInput, DestColorTranslation, SourceColorTranslation,
+ &CombinedRect, NULL, &Pt, pbo, &AdjustedBrushOrigin);
+ }
+ else
+ {
+ Ret = BltMask(psoOutput, NULL, psoInput, DestColorTranslation,
+ &CombinedRect, NULL, &Pt, pbo, &AdjustedBrushOrigin, R4_MASK);
+ }
+ }
+ break;
+ case DC_COMPLEX:
+ Ret = TRUE;
+ if (psoOutput == psoInput)
+ {
+ if (OutputRect.top < InputPoint.y)
+ {
+ Direction = OutputRect.left < InputPoint.x ? CD_RIGHTDOWN : CD_LEFTDOWN;
+ }
+ else
+ {
+ Direction = OutputRect.left < InputPoint.x ? CD_RIGHTUP : CD_LEFTUP;
+ }
+ }
+ else
+ {
+ Direction = CD_ANY;
+ }
+ CLIPOBJ_cEnumStart(ClipRegion, FALSE, CT_RECTANGLES, Direction, 0);
+ do
+ {
+ EnumMore = CLIPOBJ_bEnum(ClipRegion,(ULONG) sizeof(RectEnum), (PVOID) &RectEnum);
+
+ for (i = 0; i < RectEnum.c; i++)
+ {
+ ClipRect.left = RectEnum.arcl[i].left + Translate.x;
+ ClipRect.right = RectEnum.arcl[i].right + Translate.x;
+ ClipRect.top = RectEnum.arcl[i].top + Translate.y;
+ ClipRect.bottom = RectEnum.arcl[i].bottom + Translate.y;
+ if (RECTL_bIntersectRect(&CombinedRect, &OutputRect, &ClipRect))
+ {
+ Pt.x = InputPoint.x + CombinedRect.left - OutputRect.left;
+ Pt.y = InputPoint.y + CombinedRect.top - OutputRect.top;
+ if (psoMask->iBitmapFormat == BMF_8BPP)
+ {
+ Ret = AlphaBltMask(psoOutput, NULL, psoInput,
+ DestColorTranslation,
+ SourceColorTranslation,
+ &CombinedRect, NULL, &Pt, pbo,
+ &AdjustedBrushOrigin) && Ret;
+ }
+ else
+ {
+ Ret = BltMask(psoOutput, NULL, psoInput,
+ DestColorTranslation, &CombinedRect, NULL,
+ &Pt, pbo, &AdjustedBrushOrigin,
+ R4_MASK) && Ret;
+ }
+ }
+ }
+ }
+ while (EnumMore);
+ break;
+ }
+
+
+ IntEngLeave(&EnterLeaveDest);
+ IntEngLeave(&EnterLeaveSource);
+
+ return Ret;
+}
+
+BOOL APIENTRY
+IntEngMaskBlt(SURFOBJ *psoDest,
+ SURFOBJ *psoMask,
+ CLIPOBJ *ClipRegion,
+ XLATEOBJ *DestColorTranslation,
+ XLATEOBJ *SourceColorTranslation,
+ RECTL *DestRect,
+ POINTL *pptlMask,
+ BRUSHOBJ *pbo,
+ POINTL *BrushOrigin)
+{
+ BOOLEAN ret;
+ RECTL OutputRect;
+ POINTL InputPoint;
+ SURFACE *psurfDest;
+
+ ASSERT(psoMask);
+
+ if (pptlMask)
+ {
+ InputPoint = *pptlMask;
+ }
+
+ /* Clip against the bounds of the clipping region so we won't try to write
+ * outside the surface */
+ if (NULL != ClipRegion)
+ {
+ if (!RECTL_bIntersectRect(&OutputRect, DestRect, &ClipRegion->rclBounds))
+ {
+ return TRUE;
+ }
+ InputPoint.x += OutputRect.left - DestRect->left;
+ InputPoint.y += OutputRect.top - DestRect->top;
+ }
+ else
+ {
+ OutputRect = *DestRect;
+ }
+
+ /* No success yet */
+ ret = FALSE;
+ ASSERT(psoDest);
+ psurfDest = CONTAINING_RECORD(psoDest, SURFACE, SurfObj);
+
+ /* Dummy BitBlt to let driver know that it should flush its changes.
+ This should really be done using a call to DrvSynchronizeSurface,
+ but the VMware driver doesn't hook that call. */
+ IntEngBitBlt(psoDest, NULL, psoMask, ClipRegion, DestColorTranslation,
+ DestRect, pptlMask, pptlMask, pbo, BrushOrigin,
+ R4_NOOP);
+
+ ret = EngMaskBitBlt(psoDest, psoMask, ClipRegion, DestColorTranslation, SourceColorTranslation,
+ &OutputRect, &InputPoint, pbo, BrushOrigin);
+
+ /* Dummy BitBlt to let driver know that something has changed. */
+ IntEngBitBlt(psoDest, NULL, psoMask, ClipRegion, DestColorTranslation,
+ DestRect, pptlMask, pptlMask, pbo, BrushOrigin,
+ R4_NOOP);
+
+ return ret;
+}
+
+/* EOF */
--- /dev/null
- SURFACE *psurf;
+/*
+ * ReactOS W32 Subsystem
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+/* $Id$
+ *
+ * COPYRIGHT: See COPYING in the top level directory
+ * PROJECT: ReactOS kernel
+ * PURPOSE: GDI Driver Paint Functions
+ * FILE: subsys/win32k/eng/paint.c
+ * PROGRAMER: Jason Filby
+ * REVISION HISTORY:
+ * 3/7/1999: Created
+ */
+
+#include <win32k.h>
+
+#define NDEBUG
+#include <debug.h>
+
+BOOL APIENTRY FillSolid(SURFOBJ *pso, PRECTL pRect, ULONG iColor)
+{
+ LONG y;
+ ULONG LineWidth;
- psurf = CONTAINING_RECORD(pso, SURFACE, SurfObj);
+
+ ASSERT(pso);
+ ASSERT(pRect);
+ LineWidth = pRect->right - pRect->left;
+ DPRINT(" LineWidth: %d, top: %d, bottom: %d\n", LineWidth, pRect->top, pRect->bottom);
+ for (y = pRect->top; y < pRect->bottom; y++)
+ {
+ DibFunctionsForBitmapFormat[pso->iBitmapFormat].DIB_HLine(
+ pso, pRect->left, pRect->right, y, iColor);
+ }
+ return TRUE;
+}
+
+BOOL APIENTRY
+EngPaintRgn(SURFOBJ *pso, CLIPOBJ *ClipRegion, ULONG iColor, MIX Mix,
+ BRUSHOBJ *BrushObj, POINTL *BrushPoint)
+{
+ RECT_ENUM RectEnum;
+ BOOL EnumMore;
+ ULONG i;
+
+ ASSERT(pso);
+ ASSERT(ClipRegion);
+
+ DPRINT("ClipRegion->iMode:%d, ClipRegion->iDComplexity: %d\n Color: %d", ClipRegion->iMode, ClipRegion->iDComplexity, iColor);
+ switch(ClipRegion->iMode) {
+
+ case TC_RECTANGLES:
+
+ /* Rectangular clipping can be handled without enumeration.
+ Note that trivial clipping is not possible, since the clipping
+ region defines the area to fill */
+
+ if (ClipRegion->iDComplexity == DC_RECT)
+ {
+ FillSolid(pso, &(ClipRegion->rclBounds), iColor);
+ } else {
+
+ /* Enumerate all the rectangles and draw them */
+ CLIPOBJ_cEnumStart(ClipRegion, FALSE, CT_RECTANGLES, CD_ANY, 0);
+
+ do {
+ EnumMore = CLIPOBJ_bEnum(ClipRegion, sizeof(RectEnum), (PVOID) &RectEnum);
+ for (i = 0; i < RectEnum.c; i++) {
+ FillSolid(pso, RectEnum.arcl + i, iColor);
+ }
+ } while (EnumMore);
+ }
+
+ return(TRUE);
+
+ default:
+ return(FALSE);
+ }
+}
+
+/*
+ * @unimplemented
+ */
+BOOL APIENTRY
+EngPaint(IN SURFOBJ *pso,
+ IN CLIPOBJ *ClipRegion,
+ IN BRUSHOBJ *Brush,
+ IN POINTL *BrushOrigin,
+ IN MIX Mix)
+{
+ BOOLEAN ret;
+
+ // FIXME: We only support a brush's solid color attribute
+ ret = EngPaintRgn(pso, ClipRegion, Brush->iSolidColor, Mix, Brush, BrushOrigin);
+
+ return ret;
+}
+
+BOOL APIENTRY
+IntEngPaint(IN SURFOBJ *pso,
+ IN CLIPOBJ *ClipRegion,
+ IN BRUSHOBJ *Brush,
+ IN POINTL *BrushOrigin,
+ IN MIX Mix)
+{
+ SURFACE *psurf = CONTAINING_RECORD(pso, SURFACE, SurfObj);
+ BOOL ret;
+
+ DPRINT("pso->iType == %d\n", pso->iType);
+ /* Is the surface's Paint function hooked? */
+ if((pso->iType!=STYPE_BITMAP) && (psurf->flags & HOOK_PAINT))
+ {
+ // Call the driver's DrvPaint
+ ret = GDIDEVFUNCS(pso).Paint(
+ pso, ClipRegion, Brush, BrushOrigin, Mix);
+ return ret;
+ }
+ return EngPaint(pso, ClipRegion, Brush, BrushOrigin, Mix );
+
+}
+/* EOF */
--- /dev/null
- INT ret;
+/*
+ * COPYRIGHT: See COPYING in the top level directory
+ * PROJECT: ReactOS kernel
+ * PURPOSE: Clipboard routines
+ * FILE: subsys/win32k/ntuser/clipboard.c
+ * PROGRAMER: Filip Navara <xnavara@volny.cz>
+ * Pablo Borobia <pborobia@gmail.com>
+ */
+
+#include <win32k.h>
+
+#define NDEBUG
+#include <debug.h>
+
+#define DATA_DELAYED_RENDER 0
+#define DATA_SYNTHESIZED_RENDER -1
+
+PTHREADINFO ClipboardThread;
+PTHREADINFO ClipboardOwnerThread;
+PWINDOW_OBJECT ClipboardWindow;
+PWINDOW_OBJECT ClipboardViewerWindow;
+PWINDOW_OBJECT ClipboardOwnerWindow;
+BOOL sendDrawClipboardMsg;
+BOOL recentlySetClipboard;
+BOOL delayedRender;
+UINT lastEnumClipboardFormats;
+DWORD ClipboardSequenceNumber = 0;
+
+PCLIPBOARDCHAINELEMENT WindowsChain = NULL;
+PCLIPBOARDELEMENT ClipboardData = NULL;
+
+PCHAR synthesizedData;
+DWORD synthesizedDataSize;
+
+
+/*==============================================================*/
+
+/* return the pointer to the prev window of the finded window,
+ if NULL does not exists in the chain */
+PCLIPBOARDCHAINELEMENT FASTCALL
+IntIsWindowInChain(PWINDOW_OBJECT window)
+{
+ PCLIPBOARDCHAINELEMENT wce = WindowsChain;
+
+ while (wce)
+ {
+ if (wce->window == window)
+ {
+ break;
+ }
+ wce = wce->next;
+ }
+
+ return wce;
+}
+
+VOID FASTCALL printChain(VOID)
+{
+ /*test*/
+ PCLIPBOARDCHAINELEMENT wce2 = WindowsChain;
+ while (wce2)
+ {
+ DPRINT1("chain: %p\n", wce2->window->hSelf);
+ wce2 = wce2->next;
+ }
+}
+
+/* the new window always have to be the first in the chain */
+PCLIPBOARDCHAINELEMENT FASTCALL
+IntAddWindowToChain(PWINDOW_OBJECT window)
+{
+ PCLIPBOARDCHAINELEMENT wce = NULL;
+
+ if (!IntIsWindowInChain(window))
+ {
+ wce = WindowsChain;
+
+ wce = ExAllocatePoolWithTag(PagedPool, sizeof(CLIPBOARDCHAINELEMENT), USERTAG_CLIPBOARD);
+ if (wce == NULL)
+ {
+ SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
+ goto exit_addChain;
+ }
+
+ wce->window = window;
+ wce->next = WindowsChain;
+
+ WindowsChain = wce;
+
+ //printChain();
+ }
+exit_addChain:
+
+ /* return the next window to beremoved later */
+ return wce;
+}
+
+PCLIPBOARDCHAINELEMENT FASTCALL
+IntRemoveWindowFromChain(PWINDOW_OBJECT window)
+{
+ PCLIPBOARDCHAINELEMENT wce = WindowsChain;
+ PCLIPBOARDCHAINELEMENT *link = &WindowsChain;
+
+ if (IntIsWindowInChain(window))
+ {
+ while (wce != NULL)
+ {
+ if (wce->window == window)
+ {
+ *link = wce->next;
+ break;
+ }
+
+ link = &wce->next;
+ wce = wce->next;
+ }
+
+ //printChain();
+
+ return wce;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+
+/*==============================================================*/
+/* if format exists, returns a non zero value (pointing to format object) */
+PCLIPBOARDELEMENT FASTCALL
+intIsFormatAvailable(UINT format)
+{
+ PCLIPBOARDELEMENT ret = NULL;
+ PCLIPBOARDELEMENT ce = ClipboardData;
+
+ while(ce)
+ {
+ if (ce->format == format)
+ {
+ ret = ce;
+ break;
+ }
+ ce = ce->next;
+ }
+ return ret;
+}
+
+/* counts how many distinct format were are in the clipboard */
+DWORD FASTCALL
+IntCountClipboardFormats(VOID)
+{
+ DWORD ret = 0;
+ PCLIPBOARDELEMENT ce = ClipboardData;
+
+ while(ce)
+ {
+ ret++;
+ ce = ce->next;
+ }
+ return ret;
+}
+
+/* adds a new format and data to the clipboard */
+PCLIPBOARDELEMENT FASTCALL
+intAddFormatedData(UINT format, HANDLE hData, DWORD size)
+{
+ PCLIPBOARDELEMENT ce = NULL;
+
+ ce = ExAllocatePoolWithTag(PagedPool, sizeof(CLIPBOARDELEMENT), USERTAG_CLIPBOARD);
+ if (ce == NULL)
+ {
+ SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
+ }
+ else
+ {
+ ce->format = format;
+ ce->size = size;
+ ce->hData = hData;
+ ce->next = ClipboardData;
+
+ ClipboardData = ce;
+
+ IntIncrementSequenceNumber();
+ }
+
+ return ce;
+}
+
+/* removes a format and its data from the clipboard */
+BOOL FASTCALL
+intRemoveFormatedData(UINT format)
+{
+ BOOL ret = FALSE;
+ PCLIPBOARDELEMENT ce = ClipboardData;
+ PCLIPBOARDELEMENT *link = &ClipboardData;
+
+ if (intIsFormatAvailable(format))
+ {
+ while (ce != NULL)
+ {
+ if (ce->format == format)
+ {
+ *link = ce->next;
+ break;
+ }
+
+ link = &ce->next;
+ ce = ce->next;
+ }
+
+ if (ce->hData)
+ {
+ ExFreePool(ce->hData);
+ }
+ ExFreePool(ce);
+ ret = TRUE;
+ }
+
+ return ret;
+}
+
+VOID FASTCALL
+IntEmptyClipboardData(VOID)
+{
+ PCLIPBOARDELEMENT ce = ClipboardData;
+ PCLIPBOARDELEMENT tmp;
+
+ while(ce)
+ {
+ tmp = ce->next;
+ if (ce->hData)
+ {
+ ExFreePool(ce->hData);
+ }
+ ExFreePool(ce);
+ ce = tmp;
+ }
+
+ ClipboardData = NULL;
+}
+
+/*==============================================================*/
+
+HANDLE FASTCALL
+renderBITMAPfromDIB(LPBYTE pDIB)
+{
+ HDC hdc;
+ HBITMAP hbitmap;
+ PBITMAPINFO pBmi, pConvertedBmi = NULL;
+ NTSTATUS Status ;
+ UINT offset = 0; /* Stupid compiler */
+
+ pBmi = (BITMAPINFO*)pDIB;
+
+ //hdc = UserGetDCEx(NULL, NULL, DCX_USESTYLE);
+ hdc = UserGetDCEx(ClipboardWindow, NULL, DCX_USESTYLE);
+
+ /* Probe it */
+ _SEH2_TRY
+ {
+ ProbeForRead(&pBmi->bmiHeader.biSize, sizeof(DWORD), 1);
+ ProbeForRead(pBmi, pBmi->bmiHeader.biSize, 1);
+ ProbeForRead(pBmi, DIB_BitmapInfoSize(pBmi, DIB_RGB_COLORS), 1);
+ pConvertedBmi = DIB_ConvertBitmapInfo(pBmi, DIB_RGB_COLORS);
+ if(!pConvertedBmi)
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ else
+ {
+ offset = DIB_BitmapInfoSize((BITMAPINFO*)pBmi, DIB_RGB_COLORS);
+ ProbeForRead(pDIB + offset, pConvertedBmi->bmiHeader.biSizeImage, 1);
+ }
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ Status = _SEH2_GetExceptionCode();
+ }
+ _SEH2_END
+
+ if(!NT_SUCCESS(Status))
+ {
+ UserReleaseDC(ClipboardWindow, hdc, FALSE);
+ return NULL;
+ }
+
+ hbitmap = GreCreateDIBitmapInternal(hdc,
+ pConvertedBmi->bmiHeader.biWidth,
+ pConvertedBmi->bmiHeader.biHeight,
+ CBM_INIT,
+ pDIB+offset,
+ pConvertedBmi,
+ DIB_RGB_COLORS,
+ 0,
+ 0);
+ //UserReleaseDC(NULL, hdc, FALSE);
+ UserReleaseDC(ClipboardWindow, hdc, FALSE);
+
+ DIB_FreeConvertedBitmapInfo(pConvertedBmi, pBmi);
+
+ return hbitmap;
+}
+
+BOOL FASTCALL
+canSinthesize(UINT format)
+{
+ BOOL ret = FALSE;
+
+ switch(format)
+ {
+ case CF_BITMAP:
+ case CF_METAFILEPICT:
+ ret = TRUE;
+ }
+
+ return ret;
+}
+
+/* returns the size of the sinthesized data */
+DWORD FASTCALL
+synthesizeData(UINT format)
+{
+ DWORD ret = 0;
+
+ synthesizedData = NULL;
+ synthesizedDataSize = 0;
+
+ if (!canSinthesize(format))
+ {
+ return 0;
+ }
+
+ switch (format)
+ {
+ case CF_BITMAP:
+ {
+ break;
+ }
+
+ case CF_METAFILEPICT:
+ {
+ break;
+ }
+ }
+
+ ret = 1;
+
+ return ret;
+}
+
+VOID FASTCALL
+freeSynthesizedData(VOID)
+{
+ ExFreePool(synthesizedData);
+}
+
+/*==============================================================*/
+
+BOOL FASTCALL
+intIsClipboardOpenByMe(VOID)
+{
+ /* check if we open the clipboard */
+ if (ClipboardThread && ClipboardThread == PsGetCurrentThreadWin32Thread())
+ {
+ /* yes, we got a thread and its the same that opens the clipboard */
+ return TRUE;
+
+ }
+ /* will fail if not thread (closed) or not open by me*/
+ return FALSE;
+}
+
+/* IntClipboardFreeWindow it's called when a window was destroyed */
+VOID FASTCALL
+IntClipboardFreeWindow(PWINDOW_OBJECT window)
+{
+ /* called from co_UserFreeWindow in window.c */
+ /* check if clipboard is not locked by this window, if yes, unlock it */
+ if (ClipboardThread == PsGetCurrentThreadWin32Thread())
+ {
+ /* the window that opens the clipboard was destroyed */
+ ClipboardThread = NULL;
+ ClipboardWindow = NULL;
+ //TODO: free clipboard
+ }
+ if (window == ClipboardOwnerWindow)
+ {
+ /* the owner window was destroyed */
+ ClipboardOwnerWindow = NULL;
+ ClipboardOwnerThread = NULL;
+ }
+ /* remove window from window chain */
+ if (IntIsWindowInChain(window))
+ {
+ PCLIPBOARDCHAINELEMENT w = IntRemoveWindowFromChain(window);
+ if (w)
+ {
+ ExFreePool(w);
+ }
+ }
+}
+
+BOOL APIENTRY
+NtUserOpenClipboard(HWND hWnd, DWORD Unknown1)
+{
+
+ PWINDOW_OBJECT Window;
+ BOOL ret = FALSE;
+
+ UserEnterExclusive();
+
+ sendDrawClipboardMsg = FALSE;
+ recentlySetClipboard = FALSE;
+
+ if (ClipboardThread)
+ {
+ /* clipboard is already open */
+ if (ClipboardThread == PsGetCurrentThreadWin32Thread())
+ {
+ if (ClipboardOwnerWindow)
+ {
+ if (ClipboardOwnerWindow->hSelf == hWnd)
+ {
+ ret = TRUE;
+ }
+ }
+ else
+ {
+ if (hWnd == NULL)
+ {
+ ret = TRUE;
+ }
+ }
+ }
+ }
+ else
+ {
+
+ if (hWnd != NULL)
+ {
+ Window = UserGetWindowObject(hWnd);
+
+ if (Window != NULL)
+ {
+ ClipboardWindow = Window;
+ ClipboardThread = PsGetCurrentThreadWin32Thread();
+ ret = TRUE;
+ }
+ else
+ {
+ ClipboardWindow = NULL;
+ ClipboardThread = NULL;
+ ClipboardOwnerWindow = NULL;
+ ClipboardOwnerThread = NULL;
+ }
+ }
+ else
+ {
+ ClipboardWindow = NULL;
+ ClipboardThread = PsGetCurrentThreadWin32Thread();
+ ret = TRUE;
+ }
+ }
+
+ UserLeave();
+
+ return ret;
+}
+
+BOOL APIENTRY
+NtUserCloseClipboard(VOID)
+{
+ BOOL ret = FALSE;
+
+ UserEnterExclusive();
+
+ if (intIsClipboardOpenByMe())
+ {
+ ClipboardWindow = NULL;
+ ClipboardThread = NULL;
+ ret = TRUE;
+ }
+ else
+ {
+ SetLastWin32Error(ERROR_CLIPBOARD_NOT_OPEN);
+ }
+
+ recentlySetClipboard = FALSE;
+
+ UserLeave();
+
+ if (sendDrawClipboardMsg && WindowsChain)
+ {
+ /* only send message to the first window in the chain, then they'll do the chain */
+ /* commented because it makes a crash in co_MsqSendMessage
+ ASSERT(WindowsChain->window);
+ ASSERT(WindowsChain->window->hSelf);
+ DPRINT1("Clipboard: sending WM_DRAWCLIPBOARD to %p\n", WindowsChain->window->hSelf);
+ co_IntSendMessage(WindowsChain->window->hSelf, WM_DRAWCLIPBOARD, 0, 0);
+ */
+ }
+
+ return ret;
+}
+
+HWND APIENTRY
+NtUserGetOpenClipboardWindow(VOID)
+{
+ HWND ret = NULL;
+
+ UserEnterShared();
+
+ if (ClipboardWindow)
+ {
+ ret = ClipboardWindow->hSelf;
+ }
+
+ UserLeave();
+
+ return ret;
+}
+
+BOOL APIENTRY
+NtUserChangeClipboardChain(HWND hWndRemove, HWND hWndNewNext)
+{
+ BOOL ret = FALSE;
+ PCLIPBOARDCHAINELEMENT w = NULL;
+ PWINDOW_OBJECT removeWindow;
+ UserEnterExclusive();
+
+ removeWindow = UserGetWindowObject(hWndRemove);
+
+ if (removeWindow)
+ {
+ if ((ret = !!IntIsWindowInChain(removeWindow)))
+ {
+ w = IntRemoveWindowFromChain(removeWindow);
+ if (w)
+ {
+ ExFreePool(w);
+ }
+ }
+ }
+
+ if (ret && WindowsChain)
+ {
+ // only send message to the first window in the chain,
+ // then they do the chain
+
+ /* WindowsChain->window may be NULL */
+ LPARAM lparam = WindowsChain->window == NULL ? 0 : (LPARAM)WindowsChain->window->hSelf;
+ DPRINT1("Message: WM_CHANGECBCHAIN to %p", WindowsChain->window->hSelf);
+ co_IntSendMessage(WindowsChain->window->hSelf, WM_CHANGECBCHAIN, (WPARAM)hWndRemove, lparam);
+ }
+
+ UserLeave();
+
+ return ret;
+}
+
+DWORD APIENTRY
+NtUserCountClipboardFormats(VOID)
+{
+ DWORD ret = 0;
+
+ if (ClipboardData)
+ {
+ ret = IntCountClipboardFormats();
+ }
+
+ return ret;
+}
+
+DWORD APIENTRY
+NtUserEmptyClipboard(VOID)
+{
+ BOOL ret = FALSE;
+
+ UserEnterExclusive();
+
+ if (intIsClipboardOpenByMe())
+ {
+ if (ClipboardData)
+ {
+ IntEmptyClipboardData();
+ }
+
+ ClipboardOwnerWindow = ClipboardWindow;
+ ClipboardOwnerThread = ClipboardThread;
+
+ IntIncrementSequenceNumber();
+
+ ret = TRUE;
+ }
+ else
+ {
+ SetLastWin32Error(ERROR_CLIPBOARD_NOT_OPEN);
+ }
+
+ if (ret && ClipboardOwnerWindow)
+ {
+ DPRINT("Clipboard: WM_DESTROYCLIPBOARD to %p", ClipboardOwnerWindow->hSelf);
+ co_IntSendMessage( ClipboardOwnerWindow->hSelf, WM_DESTROYCLIPBOARD, 0, 0);
+ }
+
+ UserLeave();
+
+ return ret;
+}
+
+HANDLE APIENTRY
+NtUserGetClipboardData(UINT uFormat, PVOID pBuffer)
+{
+ HANDLE ret = NULL;
+
+ UserEnterShared();
+
+ if (intIsClipboardOpenByMe())
+ {
+ /* when Unknown1 is zero, we returns to user32 the data size */
+ if (!pBuffer)
+ {
+ PCLIPBOARDELEMENT data = intIsFormatAvailable(uFormat);
+
+ if (data)
+ {
+ /* format exists in clipboard */
+ if (data->size == DATA_DELAYED_RENDER)
+ {
+ /* tell owner what data needs to be rendered */
+ if (ClipboardOwnerWindow)
+ {
+ ASSERT(ClipboardOwnerWindow->hSelf);
+ co_IntSendMessage(ClipboardOwnerWindow->hSelf, WM_RENDERFORMAT, (WPARAM)uFormat, 0);
+ data = intIsFormatAvailable(uFormat);
+ ASSERT(data->size);
+ ret = (HANDLE)(ULONG_PTR)data->size;
+ }
+ }
+ else
+ {
+ if (data->size == DATA_SYNTHESIZED_RENDER)
+ {
+ data->size = synthesizeData(uFormat);
+ }
+
+ }
+ ret = (HANDLE)(ULONG_PTR)data->size;
+ }
+ else
+ {
+ /* there is no data in this format */
+ //ret = (HANDLE)FALSE;
+ }
+ }
+ else
+ {
+ PCLIPBOARDELEMENT data = intIsFormatAvailable(uFormat);
+
+ if (data)
+ {
+ if (data->size == DATA_DELAYED_RENDER)
+ {
+ // we rendered it in 1st call of getclipboard data
+ }
+ else
+ {
+ if (data->size == DATA_SYNTHESIZED_RENDER)
+ {
+ if (uFormat == CF_BITMAP)
+ {
+ /* BITMAP & METAFILEs returns a GDI handle */
+ PCLIPBOARDELEMENT data = intIsFormatAvailable(CF_DIB);
+ if (data)
+ {
+ ret = renderBITMAPfromDIB(data->hData);
+ }
+ }
+ else
+ {
+ ret = (HANDLE)pBuffer;
+
+ _SEH2_TRY
+ {
+ ProbeForWrite(pBuffer, synthesizedDataSize, 1);
+ memcpy(pBuffer, (PCHAR)synthesizedData, synthesizedDataSize);
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ ret = NULL;
+ }
+ _SEH2_END
+
+ freeSynthesizedData();
+ }
+ }
+ else
+ {
+ ret = (HANDLE)pBuffer;
+
+ _SEH2_TRY
+ {
+ ProbeForWrite(pBuffer, data->size, 1);
+ memcpy(pBuffer, (PCHAR)data->hData, data->size);
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ ret = NULL;
+ }
+ _SEH2_END
+ }
+ }
+
+ }
+
+ }
+ }
+ else
+ {
+ SetLastWin32Error(ERROR_CLIPBOARD_NOT_OPEN);
+ }
+
+ UserLeave();
+
+ return ret;
+}
+
+INT APIENTRY
+NtUserGetClipboardFormatName(UINT format, PUNICODE_STRING FormatName,
+ INT cchMaxCount)
+{
+ UNICODE_STRING sFormatName;
+ INT ret = 0;
+
+ /* if the format is built-in we fail */
+ if (format < 0xc000)
+ {
+ /* registetrated formats are >= 0xc000 */
+ return 0;
+ }
+
+ if((cchMaxCount < 1) || !FormatName)
+ {
+ SetLastWin32Error(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+
+ _SEH2_TRY
+ {
+ ProbeForWriteUnicodeString(FormatName);
+ sFormatName = *(volatile UNICODE_STRING *)FormatName;
+ ProbeForWrite(sFormatName.Buffer, sFormatName.MaximumLength, 1);
+
+ ret = IntGetAtomName((RTL_ATOM)format, sFormatName.Buffer, cchMaxCount * sizeof(WCHAR));
+
+ if (ret >= 0)
+ {
+ ret = ret / sizeof(WCHAR);
+ sFormatName.Length = ret;
+ }
+ else
+ {
+ ret = 0;
+ }
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ SetLastNtError(_SEH2_GetExceptionCode());
+ }
+ _SEH2_END;
+
+ return ret;
+}
+
+HWND APIENTRY
+NtUserGetClipboardOwner(VOID)
+{
+ HWND ret = NULL;
+
+ UserEnterShared();
+
+ if (ClipboardOwnerWindow)
+ {
+ ret = ClipboardOwnerWindow->hSelf;
+ }
+
+ UserLeave();
+
+ return ret;
+}
+
+HWND APIENTRY
+NtUserGetClipboardViewer(VOID)
+{
+ HWND ret = NULL;
+
+ UserEnterShared();
+
+ if (WindowsChain)
+ {
+ ret = WindowsChain->window->hSelf;
+ }
+
+ UserLeave();
+
+ return ret;
+}
+
+INT APIENTRY
+NtUserGetPriorityClipboardFormat(UINT *paFormatPriorityList, INT cFormats)
+{
+ INT i;
+ UINT *priorityList;
+ INT ret = 0;
+
+ UserEnterExclusive();
+
+ _SEH2_TRY
+ {
+ if (IntCountClipboardFormats() == 0)
+ {
+ ret = 0;
+ }
+ else
+ {
+ ProbeForRead(paFormatPriorityList, cFormats, sizeof(UINT));
+
+ priorityList = paFormatPriorityList;
+
+ ret = -1;
+
+ for (i = 0; i < cFormats; i++)
+ {
+ if (intIsFormatAvailable(priorityList[i]))
+ {
+ ret = priorityList[i];
+ break;
+ }
+ }
+
+ }
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ SetLastNtError(_SEH2_GetExceptionCode());
+ }
+ _SEH2_END;
+
+ UserLeave();
+
+ return ret;
+
+}
+
+BOOL APIENTRY
+NtUserIsClipboardFormatAvailable(UINT format)
+{
+ BOOL ret = FALSE;
+
+ UserEnterShared();
+
+ ret = (intIsFormatAvailable(format) != NULL);
+
+ UserLeave();
+
+ return ret;
+}
+
+
+
+HANDLE APIENTRY
+NtUserSetClipboardData(UINT uFormat, HANDLE hMem, DWORD size)
+{
+ HANDLE hCBData = NULL;
+ UNICODE_STRING unicodeString;
+ OEM_STRING oemString;
+ ANSI_STRING ansiString;
+
+ UserEnterExclusive();
+
+ /* to place data here the we need to be the owner */
+ if (ClipboardOwnerThread == PsGetCurrentThreadWin32Thread())
+ {
+ PCLIPBOARDELEMENT data = intIsFormatAvailable(uFormat);
+ if (data)
+ {
+
+ if (data->size == DATA_DELAYED_RENDER)
+ {
+ intRemoveFormatedData(uFormat);
+ }
+ else
+ {
+ // we already have this format on clipboard
+ goto exit_setCB;
+ }
+ }
+
+ if (hMem)
+ {
+ _SEH2_TRY
+ {
+ ProbeForRead(hMem, size, 1);
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ SetLastNtError(_SEH2_GetExceptionCode());
+ _SEH2_YIELD(goto exit_setCB);
+ }
+ _SEH2_END;
+
+ if (intIsClipboardOpenByMe())
+ {
+ delayedRender = FALSE;
+ }
+
+ if (!canSinthesize(uFormat))
+ {
+ hCBData = ExAllocatePoolWithTag(PagedPool, size, USERTAG_CLIPBOARD);
+ memcpy(hCBData, hMem, size);
+ intAddFormatedData(uFormat, hCBData, size);
+ DPRINT1("Data stored\n");
+ }
+
+ sendDrawClipboardMsg = TRUE;
+ recentlySetClipboard = TRUE;
+ lastEnumClipboardFormats = uFormat;
+
+ /* conversions */
+ switch (uFormat)
+ {
+ case CF_TEXT:
+ {
+ //TODO : sinthesize CF_UNICODETEXT & CF_OEMTEXT
+ // CF_TEXT -> CF_UNICODETEXT
+ ansiString.Buffer = hCBData;
+ ansiString.Length = size;
+ RtlAnsiStringToUnicodeString(&unicodeString, &ansiString, TRUE);
+ intAddFormatedData(CF_UNICODETEXT, unicodeString.Buffer, unicodeString.Length * sizeof(WCHAR));
+ // CF_TEXT -> CF_OEMTEXT
+ RtlUnicodeStringToOemString(&oemString, &unicodeString, TRUE);
+ intAddFormatedData(CF_OEMTEXT, oemString.Buffer, oemString.Length);
+ //HKCU\Control Panel\International\Locale
+ //intAddFormatedData(CF_LOCALE, oemString.Buffer, oemString.Length);
+ break;
+ }
+ case CF_UNICODETEXT:
+ {
+ //TODO : sinthesize CF_TEXT & CF_OEMTEXT
+ //CF_UNICODETEXT -> CF_TEXT
+ unicodeString.Buffer = hCBData;
+ unicodeString.Length = size;
+ RtlUnicodeStringToAnsiString(&ansiString, &unicodeString, TRUE);
+ intAddFormatedData(CF_TEXT, ansiString.Buffer, ansiString.Length);
+ //CF_UNICODETEXT -> CF_OEMTEXT
+ RtlUnicodeStringToOemString(&oemString, &unicodeString, TRUE);
+ intAddFormatedData(CF_OEMTEXT, oemString.Buffer, oemString.Length);
+ break;
+ }
+ case CF_OEMTEXT:
+ {
+ //TODO : sinthesize CF_TEXT & CF_UNICODETEXT
+ //CF_OEMTEXT -> CF_UNICODETEXT
+ oemString.Buffer = hCBData;
+ oemString.Length = size;
+ RtlOemStringToUnicodeString(&unicodeString, &oemString, TRUE);
+ intAddFormatedData(CF_UNICODETEXT, unicodeString.Buffer, unicodeString.Length * sizeof(WCHAR));
+ //CF_OEMTEXT -> CF_TEXT
+ RtlUnicodeStringToAnsiString(&ansiString, &unicodeString, TRUE);
+ intAddFormatedData(CF_TEXT, ansiString.Buffer, ansiString.Length);
+ break;
+ }
+ case CF_BITMAP:
+ {
+ // we need to render the DIB or DIBV5 format as soon as possible
+ // because pallette information may change
+
+ HDC hdc;
- ret = NtGdiGetDIBitsInternal(hdc, hMem, 0, bm.bmHeight, NULL, &bi, DIB_RGB_COLORS, 0, 0);
+ BITMAP bm;
+ BITMAPINFO bi;
+ SURFACE *psurf;
+
+ hdc = UserGetDCEx(NULL, NULL, DCX_USESTYLE);
+
+
+ psurf = SURFACE_LockSurface(hMem);
+ BITMAP_GetObject(psurf, sizeof(BITMAP), (PVOID)&bm);
+ if(psurf)
+ {
+ SURFACE_UnlockSurface(psurf);
+ }
+
+ bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bi.bmiHeader.biWidth = bm.bmWidth;
+ bi.bmiHeader.biHeight = bm.bmHeight;
+ bi.bmiHeader.biPlanes = 1;
+ bi.bmiHeader.biBitCount = bm.bmPlanes * bm.bmBitsPixel;
+ bi.bmiHeader.biCompression = BI_RGB;
+ bi.bmiHeader.biSizeImage = 0;
+ bi.bmiHeader.biXPelsPerMeter = 0;
+ bi.bmiHeader.biYPelsPerMeter = 0;
+ bi.bmiHeader.biClrUsed = 0;
+
- ret = NtGdiGetDIBitsInternal(hdc, hMem, 0, bm.bmHeight, (LPBYTE)hCBData + sizeof(BITMAPINFOHEADER), &bi, DIB_RGB_COLORS, 0, 0);
++ NtGdiGetDIBitsInternal(hdc, hMem, 0, bm.bmHeight, NULL, &bi, DIB_RGB_COLORS, 0, 0);
+
+ size = bi.bmiHeader.biSizeImage + sizeof(BITMAPINFOHEADER);
+
+ hCBData = ExAllocatePoolWithTag(PagedPool, size, USERTAG_CLIPBOARD);
+ memcpy(hCBData, &bi, sizeof(BITMAPINFOHEADER));
+
++ NtGdiGetDIBitsInternal(hdc, hMem, 0, bm.bmHeight, (LPBYTE)hCBData + sizeof(BITMAPINFOHEADER), &bi, DIB_RGB_COLORS, 0, 0);
+
+ UserReleaseDC(NULL, hdc, FALSE);
+
+ intAddFormatedData(CF_DIB, hCBData, size);
+ intAddFormatedData(CF_BITMAP, 0, DATA_SYNTHESIZED_RENDER);
+ // intAddFormatedData(CF_DIBV5, hCBData, size);
+
+ break;
+ }
+ case CF_DIB:
+ {
+ intAddFormatedData(CF_BITMAP, 0, DATA_SYNTHESIZED_RENDER);
+ // intAddFormatedData(CF_DIBV5, hCBData, size);
+ /* investigate */
+ // intAddFormatedData(CF_PALETTE, hCBData, size);
+ break;
+ }
+ case CF_DIBV5:
+ // intAddFormatedData(CF_BITMAP, hCBData, size);
+ // intAddFormatedData(CF_PALETTE, hCBData, size);
+ // intAddFormatedData(CF_DIB, hCBData, size);
+ break;
+ case CF_ENHMETAFILE:
+ // intAddFormatedData(CF_METAFILEPICT, hCBData, size);
+ break;
+ case CF_METAFILEPICT:
+ // intAddFormatedData(CF_ENHMETAFILE, hCBData, size);
+ break;
+ }
+
+ }
+ else
+ {
+ // the window provides data in the specified format
+ delayedRender = TRUE;
+ sendDrawClipboardMsg = TRUE;
+ intAddFormatedData(uFormat, NULL, 0);
+ DPRINT1("SetClipboardData delayed format: %d\n", uFormat);
+ }
+
+
+ }
+
+exit_setCB:
+
+ UserLeave();
+
+ return hMem;
+}
+
+HWND APIENTRY
+NtUserSetClipboardViewer(HWND hWndNewViewer)
+{
+ HWND ret = NULL;
+ PCLIPBOARDCHAINELEMENT newWC = NULL;
+ PWINDOW_OBJECT window;
+
+ UserEnterExclusive();
+
+ window = UserGetWindowObject(hWndNewViewer);
+
+ if (window)
+ {
+ if ((newWC = IntAddWindowToChain(window)))
+ {
+ if (newWC)
+ {
+ // newWC->next may be NULL if we are the first window in the chain
+ if (newWC->next)
+ {
+ // return the next HWND available window in the chain
+ ret = newWC->next->window->hSelf;
+ }
+ }
+ }
+ }
+
+ UserLeave();
+
+ return ret;
+}
+
+UINT APIENTRY
+IntEnumClipboardFormats(UINT uFormat)
+{
+ UINT ret = 0;
+
+ if (intIsClipboardOpenByMe())
+ {
+ if (uFormat == 0)
+ {
+ if (recentlySetClipboard)
+ {
+ ret = lastEnumClipboardFormats;
+ }
+ else
+ {
+ /* return the first available format */
+ if (ClipboardData)
+ {
+ ret = ClipboardData->format;
+ }
+ }
+ }
+ else
+ {
+ if (recentlySetClipboard)
+ {
+ ret = 0;
+ }
+ else
+ {
+ /* querying nextt available format */
+ PCLIPBOARDELEMENT data = intIsFormatAvailable(uFormat);
+
+ if (data)
+ {
+ if (data->next)
+ {
+ ret = data->next->format;
+ }
+ else
+ {
+ /* reached the end */
+ ret = 0;
+ }
+ }
+ }
+
+ }
+ }
+ else
+ {
+ SetLastWin32Error(ERROR_CLIPBOARD_NOT_OPEN);
+ }
+
+ return ret;
+}
+
+// This number is incremented whenever the contents of the clipboard change
+// or the clipboard is emptied.
+// If clipboard rendering is delayed,
+// the sequence number is not incremented until the changes are rendered.
+VOID FASTCALL
+IntIncrementSequenceNumber(VOID)
+{
+ PTHREADINFO pti;
+ PWINSTATION_OBJECT WinStaObj;
+
+ pti = PsGetCurrentThreadWin32Thread();
+ WinStaObj = pti->rpdesk->rpwinstaParent;
+
+ WinStaObj->Clipboard->ClipboardSequenceNumber++;
+}
+
+DWORD APIENTRY
+NtUserGetClipboardSequenceNumber(VOID)
+{
+ //windowstation sequence number
+ //if no WINSTA_ACCESSCLIPBOARD access to the window station,
+ //the function returns zero.
+ DWORD sn;
+
+ HWINSTA WinSta;
+ PWINSTATION_OBJECT WinStaObj;
+ NTSTATUS Status;
+
+ WinSta = UserGetProcessWindowStation();
+
+ Status = IntValidateWindowStationHandle(WinSta, KernelMode, WINSTA_ACCESSCLIPBOARD, &WinStaObj);
+
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("No WINSTA_ACCESSCLIPBOARD access\n");
+ SetLastNtError(Status);
+ return 0;
+ }
+
+ sn = WinStaObj->ClipboardSequenceNumber;
+
+ ObDereferenceObject(WinStaObj);
+
+ //local copy
+ //sn = ClipboardSequenceNumber;
+
+ return sn;
+}
+
+
+/**************** VISTA FUNCTIONS******************/
+
+BOOL APIENTRY NtUserAddClipboardFormatListener(
+ HWND hwnd
+)
+{
+ UNIMPLEMENTED;
+ return FALSE;
+}
+
+BOOL APIENTRY NtUserRemoveClipboardFormatListener(
+ HWND hwnd
+)
+{
+ UNIMPLEMENTED;
+ return FALSE;
+}
+
+BOOL APIENTRY NtUserGetUpdatedClipboardFormats(
+ PUINT lpuiFormats,
+ UINT cFormats,
+ PUINT pcFormatsOut
+)
+{
+ UNIMPLEMENTED;
+ return FALSE;
+}
+
+/* EOF */
--- /dev/null
- BOOL bResult;
+/*
+ * ReactOS W32 Subsystem
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*
+ * We handle two types of cursors/icons:
+ * - Private
+ * Loaded without LR_SHARED flag
+ * Private to a process
+ * Can be deleted by calling NtDestroyCursorIcon()
+ * CurIcon->hModule, CurIcon->hRsrc and CurIcon->hGroupRsrc set to NULL
+ * - Shared
+ * Loaded with LR_SHARED flag
+ * Possibly shared by multiple processes
+ * Immune to NtDestroyCursorIcon()
+ * CurIcon->hModule, CurIcon->hRsrc and CurIcon->hGroupRsrc are valid
+ * There's a M:N relationship between processes and (shared) cursor/icons.
+ * A process can have multiple cursor/icons and a cursor/icon can be used
+ * by multiple processes. To keep track of this we keep a list of all
+ * cursor/icons (CurIconList) and per cursor/icon we keep a list of
+ * CURICON_PROCESS structs starting at CurIcon->ProcessList.
+ */
+
+#include <win32k.h>
+
+#define NDEBUG
+#include <debug.h>
+
+static PAGED_LOOKASIDE_LIST gProcessLookasideList;
+static LIST_ENTRY gCurIconList;
+
+SYSTEM_CURSORINFO gSysCursorInfo;
+
+BOOL
+InitCursorImpl()
+{
+ ExInitializePagedLookasideList(&gProcessLookasideList,
+ NULL,
+ NULL,
+ 0,
+ sizeof(CURICON_PROCESS),
+ TAG_DIB,
+ 128);
+ InitializeListHead(&gCurIconList);
+
+ gSysCursorInfo.Enabled = FALSE;
+ gSysCursorInfo.ButtonsDown = 0;
+ gSysCursorInfo.CursorClipInfo.IsClipped = FALSE;
+ gSysCursorInfo.LastBtnDown = 0;
+ gSysCursorInfo.CurrentCursorObject = NULL;
+ gSysCursorInfo.ShowingCursor = 0;
+ gSysCursorInfo.ClickLockActive = FALSE;
+ gSysCursorInfo.ClickLockTime = 0;
+
+ return TRUE;
+}
+
+PSYSTEM_CURSORINFO
+IntGetSysCursorInfo()
+{
+ return &gSysCursorInfo;
+}
+
+/* This function creates a reference for the object! */
+PCURICON_OBJECT FASTCALL UserGetCurIconObject(HCURSOR hCurIcon)
+{
+ PCURICON_OBJECT CurIcon;
+
+ if (!hCurIcon)
+ {
+ SetLastWin32Error(ERROR_INVALID_CURSOR_HANDLE);
+ return NULL;
+ }
+
+ CurIcon = (PCURICON_OBJECT)UserReferenceObjectByHandle(hCurIcon, otCursorIcon);
+ if (!CurIcon)
+ {
+ /* we never set ERROR_INVALID_ICON_HANDLE. lets hope noone ever checks for it */
+ SetLastWin32Error(ERROR_INVALID_CURSOR_HANDLE);
+ return NULL;
+ }
+
+ ASSERT(CurIcon->head.cLockObj >= 1);
+ return CurIcon;
+}
+
+HCURSOR
+FASTCALL
+UserSetCursor(
+ PCURICON_OBJECT NewCursor,
+ BOOL ForceChange)
+{
+ PSYSTEM_CURSORINFO CurInfo;
+ PCURICON_OBJECT OldCursor;
+ HCURSOR hOldCursor = (HCURSOR)0;
+ HDC hdcScreen;
- bResult = GreSetPointerShape(hdcScreen,
- NewCursor->IconInfo.hbmMask,
- NewCursor->IconInfo.hbmColor,
- NewCursor->IconInfo.xHotspot,
- NewCursor->IconInfo.yHotspot,
- gpsi->ptCursor.x,
- gpsi->ptCursor.y);
+
+ CurInfo = IntGetSysCursorInfo();
+
+ OldCursor = CurInfo->CurrentCursorObject;
+ if (OldCursor)
+ {
+ hOldCursor = (HCURSOR)OldCursor->Self;
+ }
+
+ /* Is the new cursor the same as the old cursor? */
+ if (OldCursor == NewCursor)
+ {
+ /* Nothing to to do in this case */
+ return hOldCursor;
+ }
+
+ /* Get the screen DC */
+ if(!(hdcScreen = IntGetScreenDC()))
+ {
+ return (HCURSOR)0;
+ }
+
+ /* Do we have a new cursor? */
+ if (NewCursor)
+ {
+ UserReferenceObject(NewCursor);
+
+ CurInfo->ShowingCursor = 1;
+ CurInfo->CurrentCursorObject = NewCursor;
+
+ /* Call GDI to set the new screen cursor */
++ GreSetPointerShape(hdcScreen,
++ NewCursor->IconInfo.hbmMask,
++ NewCursor->IconInfo.hbmColor,
++ NewCursor->IconInfo.xHotspot,
++ NewCursor->IconInfo.yHotspot,
++ gpsi->ptCursor.x,
++ gpsi->ptCursor.y);
+
+
+ }
+ else
+ {
+ /* Check if were diplaying a cursor */
+ if (OldCursor && CurInfo->ShowingCursor)
+ {
+ /* Remove the cursor */
+ GreMovePointer(hdcScreen, -1, -1);
+ DPRINT("Removing pointer!\n");
+ }
+
+ CurInfo->CurrentCursorObject = NULL;
+ CurInfo->ShowingCursor = 0;
+ }
+
+ /* OldCursor is not in use anymore */
+ if (OldCursor)
+ {
+ UserDereferenceObject(OldCursor);
+ }
+
+ /* Return handle of the old cursor */
+ return hOldCursor;
+}
+
+BOOL UserSetCursorPos( INT x, INT y, BOOL SendMouseMoveMsg)
+{
+ PWINDOW_OBJECT DesktopWindow;
+ PSYSTEM_CURSORINFO CurInfo;
+ HDC hDC;
+ MSG Msg;
+
+ if(!(hDC = IntGetScreenDC()))
+ {
+ return FALSE;
+ }
+
+ CurInfo = IntGetSysCursorInfo();
+
+ DesktopWindow = UserGetDesktopWindow();
+
+ if (DesktopWindow)
+ {
+ if(x >= DesktopWindow->Wnd->rcClient.right)
+ x = DesktopWindow->Wnd->rcClient.right - 1;
+ if(y >= DesktopWindow->Wnd->rcClient.bottom)
+ y = DesktopWindow->Wnd->rcClient.bottom - 1;
+ }
+
+ if(x < 0)
+ x = 0;
+ if(y < 0)
+ y = 0;
+
+ //Clip cursor position
+ if(CurInfo->CursorClipInfo.IsClipped)
+ {
+ if(x >= (LONG)CurInfo->CursorClipInfo.Right)
+ x = (LONG)CurInfo->CursorClipInfo.Right - 1;
+ if(x < (LONG)CurInfo->CursorClipInfo.Left)
+ x = (LONG)CurInfo->CursorClipInfo.Left;
+ if(y >= (LONG)CurInfo->CursorClipInfo.Bottom)
+ y = (LONG)CurInfo->CursorClipInfo.Bottom - 1;
+ if(y < (LONG)CurInfo->CursorClipInfo.Top)
+ y = (LONG)CurInfo->CursorClipInfo.Top;
+ }
+
+ //Store the new cursor position
+ gpsi->ptCursor.x = x;
+ gpsi->ptCursor.y = y;
+
+ //Move the mouse pointer
+ GreMovePointer(hDC, x, y);
+
+ if (!SendMouseMoveMsg)
+ return TRUE;
+
+ //Generate a mouse move message
+ Msg.message = WM_MOUSEMOVE;
+ Msg.wParam = CurInfo->ButtonsDown;
+ Msg.lParam = MAKELPARAM(x, y);
+ Msg.pt = gpsi->ptCursor;
+ MsqInsertSystemMessage(&Msg);
+
+ return TRUE;
+}
+
+/* Called from NtUserCallOneParam with Routine ONEPARAM_ROUTINE_SHOWCURSOR
+ * User32 macro NtUserShowCursor */
+int UserShowCursor(BOOL bShow)
+{
+ PSYSTEM_CURSORINFO CurInfo = IntGetSysCursorInfo();
+ HDC hdcScreen;
+
+ if (!(hdcScreen = IntGetScreenDC()))
+ {
+ return 0; /* No mouse */
+ }
+
+ if (bShow == FALSE)
+ {
+ /* Check if were diplaying a cursor */
+ if (CurInfo->ShowingCursor == 1)
+ {
+ /* Remove the pointer */
+ GreMovePointer(hdcScreen, -1, -1);
+ DPRINT("Removing pointer!\n");
+ }
+ CurInfo->ShowingCursor--;
+ }
+ else
+ {
+ if (CurInfo->ShowingCursor == 0)
+ {
+ /*Show the pointer*/
+ GreMovePointer(hdcScreen, gpsi->ptCursor.x, gpsi->ptCursor.y);
+ }
+ CurInfo->ShowingCursor++;
+ }
+
+ return CurInfo->ShowingCursor;
+}
+
+/*
+ * We have to register that this object is in use by the current
+ * process. The only way to do that seems to be to walk the list
+ * of cursor/icon objects starting at W32Process->CursorIconListHead.
+ * If the object is already present in the list, we don't have to do
+ * anything, if it's not present we add it and inc the ProcessCount
+ * in the object. Having to walk the list kind of sucks, but that's
+ * life...
+ */
+static BOOLEAN FASTCALL
+ReferenceCurIconByProcess(PCURICON_OBJECT CurIcon)
+{
+ PPROCESSINFO Win32Process;
+ PCURICON_PROCESS Current;
+
+ Win32Process = PsGetCurrentProcessWin32Process();
+
+ LIST_FOR_EACH(Current, &CurIcon->ProcessList, CURICON_PROCESS, ListEntry)
+ {
+ if (Current->Process == Win32Process)
+ {
+ /* Already registered for this process */
+ return TRUE;
+ }
+ }
+
+ /* Not registered yet */
+ Current = ExAllocateFromPagedLookasideList(&gProcessLookasideList);
+ if (NULL == Current)
+ {
+ return FALSE;
+ }
+ InsertHeadList(&CurIcon->ProcessList, &Current->ListEntry);
+ Current->Process = Win32Process;
+
+ return TRUE;
+}
+
+PCURICON_OBJECT FASTCALL
+IntFindExistingCurIconObject(HMODULE hModule,
+ HRSRC hRsrc, LONG cx, LONG cy)
+{
+ PCURICON_OBJECT CurIcon;
+
+ LIST_FOR_EACH(CurIcon, &gCurIconList, CURICON_OBJECT, ListEntry)
+ {
+
+ // if(NT_SUCCESS(UserReferenceObjectByPointer(Object, otCursorIcon))) //<- huh????
+// UserReferenceObject( CurIcon);
+// {
+ if ((CurIcon->hModule == hModule) && (CurIcon->hRsrc == hRsrc))
+ {
+ if (cx && ((cx != CurIcon->Size.cx) || (cy != CurIcon->Size.cy)))
+ {
+// UserDereferenceObject(CurIcon);
+ continue;
+ }
+ if (! ReferenceCurIconByProcess(CurIcon))
+ {
+ return NULL;
+ }
+
+ return CurIcon;
+ }
+// }
+// UserDereferenceObject(CurIcon);
+
+ }
+
+ return NULL;
+}
+
+PCURICON_OBJECT
+IntCreateCurIconHandle()
+{
+ PCURICON_OBJECT CurIcon;
+ HANDLE hCurIcon;
+
+ CurIcon = UserCreateObject(gHandleTable, NULL, &hCurIcon, otCursorIcon, sizeof(CURICON_OBJECT));
+
+ if (!CurIcon)
+ {
+ SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
+ return FALSE;
+ }
+
+ CurIcon->Self = hCurIcon;
+ InitializeListHead(&CurIcon->ProcessList);
+
+ if (! ReferenceCurIconByProcess(CurIcon))
+ {
+ DPRINT1("Failed to add process\n");
+ UserDeleteObject(hCurIcon, otCursorIcon);
+ UserDereferenceObject(CurIcon);
+ return NULL;
+ }
+
+ InsertHeadList(&gCurIconList, &CurIcon->ListEntry);
+
+ return CurIcon;
+}
+
+BOOLEAN FASTCALL
+IntDestroyCurIconObject(PCURICON_OBJECT CurIcon, BOOL ProcessCleanup)
+{
+ PSYSTEM_CURSORINFO CurInfo;
+ HBITMAP bmpMask, bmpColor;
+ BOOLEAN Ret;
+ PCURICON_PROCESS Current = NULL;
+ PPROCESSINFO W32Process = PsGetCurrentProcessWin32Process();
+
+ /* Private objects can only be destroyed by their own process */
+ if (NULL == CurIcon->hModule)
+ {
+ ASSERT(CurIcon->ProcessList.Flink->Flink == &CurIcon->ProcessList);
+ Current = CONTAINING_RECORD(CurIcon->ProcessList.Flink, CURICON_PROCESS, ListEntry);
+ if (Current->Process != W32Process)
+ {
+ DPRINT1("Trying to destroy private icon/cursor of another process\n");
+ return FALSE;
+ }
+ }
+ else if (! ProcessCleanup)
+ {
+ DPRINT("Trying to destroy shared icon/cursor\n");
+ return FALSE;
+ }
+
+ /* Now find this process in the list of processes referencing this object and
+ remove it from that list */
+ LIST_FOR_EACH(Current, &CurIcon->ProcessList, CURICON_PROCESS, ListEntry)
+ {
+ if (Current->Process == W32Process)
+ {
+ RemoveEntryList(&Current->ListEntry);
+ break;
+ }
+ }
+
+ ExFreeToPagedLookasideList(&gProcessLookasideList, Current);
+
+ /* If there are still processes referencing this object we can't destroy it yet */
+ if (! IsListEmpty(&CurIcon->ProcessList))
+ {
+ return TRUE;
+ }
+
+
+ if (! ProcessCleanup)
+ {
+ RemoveEntryList(&CurIcon->ListEntry);
+ }
+
+ CurInfo = IntGetSysCursorInfo();
+
+ if (CurInfo->CurrentCursorObject == CurIcon)
+ {
+ /* Hide the cursor if we're destroying the current cursor */
+ UserSetCursor(NULL, TRUE);
+ }
+
+ bmpMask = CurIcon->IconInfo.hbmMask;
+ bmpColor = CurIcon->IconInfo.hbmColor;
+
+ /* delete bitmaps */
+ if (bmpMask)
+ {
+ GDIOBJ_SetOwnership(bmpMask, PsGetCurrentProcess());
+ GreDeleteObject(bmpMask);
+ CurIcon->IconInfo.hbmMask = NULL;
+ }
+ if (bmpColor)
+ {
+ GDIOBJ_SetOwnership(bmpColor, PsGetCurrentProcess());
+ GreDeleteObject(bmpColor);
+ CurIcon->IconInfo.hbmColor = NULL;
+ }
+
+ /* We were given a pointer, no need to keep the reference anylonger! */
+ UserDereferenceObject(CurIcon);
+ Ret = UserDeleteObject(CurIcon->Self, otCursorIcon);
+
+ return Ret;
+}
+
+VOID FASTCALL
+IntCleanupCurIcons(struct _EPROCESS *Process, PPROCESSINFO Win32Process)
+{
+ PCURICON_OBJECT CurIcon, tmp;
+ PCURICON_PROCESS ProcessData;
+
+ LIST_FOR_EACH_SAFE(CurIcon, tmp, &gCurIconList, CURICON_OBJECT, ListEntry)
+ {
+ UserReferenceObject(CurIcon);
+ // if(NT_SUCCESS(UserReferenceObjectByPointer(Object, otCursorIcon)))
+ {
+ LIST_FOR_EACH(ProcessData, &CurIcon->ProcessList, CURICON_PROCESS, ListEntry)
+ {
+ if (Win32Process == ProcessData->Process)
+ {
+ RemoveEntryList(&CurIcon->ListEntry);
+ IntDestroyCurIconObject(CurIcon, TRUE);
+ CurIcon = NULL;
+ break;
+ }
+ }
+
+// UserDereferenceObject(Object);
+ }
+
+ if (CurIcon)
+ {
+ UserDereferenceObject(CurIcon);
+ }
+ }
+
+}
+
+
+/*
+ * @implemented
+ */
+BOOL
+APIENTRY
+NtUserGetIconInfo(
+ HANDLE hCurIcon,
+ PICONINFO IconInfo,
+ PUNICODE_STRING lpInstName, // optional
+ PUNICODE_STRING lpResName, // optional
+ LPDWORD pbpp, // optional
+ BOOL bInternal)
+{
+ ICONINFO ii;
+ PCURICON_OBJECT CurIcon;
+ NTSTATUS Status = STATUS_SUCCESS;
+ BOOL Ret = FALSE;
+ DWORD colorBpp = 0;
+
+ DPRINT("Enter NtUserGetIconInfo\n");
+ UserEnterExclusive();
+
+ if (!IconInfo)
+ {
+ SetLastWin32Error(ERROR_INVALID_PARAMETER);
+ goto leave;
+ }
+
+ if (!(CurIcon = UserGetCurIconObject(hCurIcon)))
+ {
+ goto leave;
+ }
+
+ RtlCopyMemory(&ii, &CurIcon->IconInfo, sizeof(ICONINFO));
+
+ /* Copy bitmaps */
+ ii.hbmMask = BITMAP_CopyBitmap(CurIcon->IconInfo.hbmMask);
+ ii.hbmColor = BITMAP_CopyBitmap(CurIcon->IconInfo.hbmColor);
+
+ if (pbpp)
+ {
+ PSURFACE psurfBmp;
+
+ psurfBmp = SURFACE_LockSurface(CurIcon->IconInfo.hbmColor);
+ if (psurfBmp)
+ {
+ colorBpp = BitsPerFormat(psurfBmp->SurfObj.iBitmapFormat);
+ SURFACE_UnlockSurface(psurfBmp);
+ }
+ }
+
+ /* Copy fields */
+ _SEH2_TRY
+ {
+ ProbeForWrite(IconInfo, sizeof(ICONINFO), 1);
+ RtlCopyMemory(IconInfo, &ii, sizeof(ICONINFO));
+
+ if (pbpp)
+ {
+ ProbeForWrite(pbpp, sizeof(DWORD), 1);
+ *pbpp = colorBpp;
+ }
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ Status = _SEH2_GetExceptionCode();
+ }
+ _SEH2_END
+
+ if (NT_SUCCESS(Status))
+ Ret = TRUE;
+ else
+ SetLastNtError(Status);
+
+ UserDereferenceObject(CurIcon);
+
+leave:
+ DPRINT("Leave NtUserGetIconInfo, ret=%i\n", Ret);
+ UserLeave();
+
+ return Ret;
+}
+
+
+/*
+ * @implemented
+ */
+BOOL
+APIENTRY
+NtUserGetIconSize(
+ HANDLE hCurIcon,
+ UINT istepIfAniCur,
+ PLONG plcx, // &size.cx
+ PLONG plcy) // &size.cy
+{
+ PCURICON_OBJECT CurIcon;
+ NTSTATUS Status = STATUS_SUCCESS;
+ BOOL bRet = FALSE;
+
+ DPRINT("Enter NtUserGetIconSize\n");
+ UserEnterExclusive();
+
+ if (!(CurIcon = UserGetCurIconObject(hCurIcon)))
+ {
+ goto cleanup;
+ }
+
+ _SEH2_TRY
+ {
+ ProbeForWrite(plcx, sizeof(LONG), 1);
+ RtlCopyMemory(plcx, &CurIcon->Size.cx, sizeof(LONG));
+ ProbeForWrite(plcy, sizeof(LONG), 1);
+ RtlCopyMemory(plcy, &CurIcon->Size.cy, sizeof(LONG));
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ Status = _SEH2_GetExceptionCode();
+ }
+ _SEH2_END
+
+ if (NT_SUCCESS(Status))
+ bRet = TRUE;
+ else
+ SetLastNtError(Status); // maybe not, test this
+
+ UserDereferenceObject(CurIcon);
+
+cleanup:
+ DPRINT("Leave NtUserGetIconSize, ret=%i\n", bRet);
+ UserLeave();
+ return bRet;
+}
+
+
+/*
+ * @unimplemented
+ */
+DWORD
+APIENTRY
+NtUserGetCursorFrameInfo(
+ DWORD Unknown0,
+ DWORD Unknown1,
+ DWORD Unknown2,
+ DWORD Unknown3)
+{
+ UNIMPLEMENTED
+
+ return 0;
+}
+
+
+/*
+ * @implemented
+ */
+BOOL
+APIENTRY
+NtUserGetCursorInfo(
+ PCURSORINFO pci)
+{
+ CURSORINFO SafeCi;
+ PSYSTEM_CURSORINFO CurInfo;
+ NTSTATUS Status = STATUS_SUCCESS;
+ PCURICON_OBJECT CurIcon;
+ BOOL Ret = FALSE;
+ DECLARE_RETURN(BOOL);
+
+ DPRINT("Enter NtUserGetCursorInfo\n");
+ UserEnterExclusive();
+
+ CurInfo = IntGetSysCursorInfo();
+ CurIcon = (PCURICON_OBJECT)CurInfo->CurrentCursorObject;
+
+ SafeCi.cbSize = sizeof(CURSORINFO);
+ SafeCi.flags = ((CurInfo->ShowingCursor && CurIcon) ? CURSOR_SHOWING : 0);
+ SafeCi.hCursor = (CurIcon ? (HCURSOR)CurIcon->Self : (HCURSOR)0);
+
+ SafeCi.ptScreenPos = gpsi->ptCursor;
+
+ _SEH2_TRY
+ {
+ if (pci->cbSize == sizeof(CURSORINFO))
+ {
+ ProbeForWrite(pci, sizeof(CURSORINFO), 1);
+ RtlCopyMemory(pci, &SafeCi, sizeof(CURSORINFO));
+ Ret = TRUE;
+ }
+ else
+ {
+ SetLastWin32Error(ERROR_INVALID_PARAMETER);
+ }
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ Status = _SEH2_GetExceptionCode();
+ }
+ _SEH2_END;
+ if (!NT_SUCCESS(Status))
+ {
+ SetLastNtError(Status);
+ }
+
+ RETURN(Ret);
+
+CLEANUP:
+ DPRINT("Leave NtUserGetCursorInfo, ret=%i\n",_ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+BOOL
+APIENTRY
+UserClipCursor(
+ RECTL *prcl)
+{
+ /* FIXME - check if process has WINSTA_WRITEATTRIBUTES */
+ PSYSTEM_CURSORINFO CurInfo;
+ PWINDOW_OBJECT DesktopWindow = NULL;
+
+ CurInfo = IntGetSysCursorInfo();
+
+ DesktopWindow = UserGetDesktopWindow();
+
+ if (prcl != NULL &&
+ (prcl->right > prcl->left) &&
+ (prcl->bottom > prcl->top) &&
+ DesktopWindow != NULL)
+ {
+ CurInfo->CursorClipInfo.IsClipped = TRUE;
+ CurInfo->CursorClipInfo.Left = max(prcl->left, DesktopWindow->Wnd->rcWindow.left);
+ CurInfo->CursorClipInfo.Top = max(prcl->top, DesktopWindow->Wnd->rcWindow.top);
+ CurInfo->CursorClipInfo.Right = min(prcl->right, DesktopWindow->Wnd->rcWindow.right);
+ CurInfo->CursorClipInfo.Bottom = min(prcl->bottom, DesktopWindow->Wnd->rcWindow.bottom);
+
+ UserSetCursorPos(gpsi->ptCursor.x, gpsi->ptCursor.y, FALSE);
+ }
+ else
+ {
+ CurInfo->CursorClipInfo.IsClipped = FALSE;
+ }
+
+ return TRUE;
+}
+
+/*
+ * @implemented
+ */
+BOOL
+APIENTRY
+NtUserClipCursor(
+ RECTL *prcl)
+{
+ /* FIXME - check if process has WINSTA_WRITEATTRIBUTES */
+ RECTL rclLocal;
+ BOOL bResult;
+
+ if (prcl)
+ {
+ _SEH2_TRY
+ {
+ /* Probe and copy rect */
+ ProbeForRead(prcl, sizeof(RECTL), 1);
+ rclLocal = *prcl;
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ SetLastWin32Error(ERROR_INVALID_PARAMETER);
+ _SEH2_YIELD(return FALSE;)
+ }
+ _SEH2_END
+
+ prcl = &rclLocal;
+ }
+
+ UserEnterExclusive();
+
+ /* Call the internal function */
+ bResult = UserClipCursor(prcl);
+
+ UserLeave();
+
+ return bResult;
+}
+
+
+/*
+ * @implemented
+ */
+BOOL
+APIENTRY
+NtUserDestroyCursor(
+ HANDLE hCurIcon,
+ DWORD Unknown)
+{
+ PCURICON_OBJECT CurIcon;
+ BOOL ret;
+ DECLARE_RETURN(BOOL);
+
+ DPRINT("Enter NtUserDestroyCursorIcon\n");
+ UserEnterExclusive();
+
+ if (!(CurIcon = UserGetCurIconObject(hCurIcon)))
+ {
+ RETURN(FALSE);
+ }
+
+ ret = IntDestroyCurIconObject(CurIcon, FALSE);
+ /* Note: IntDestroyCurIconObject will remove our reference for us! */
+
+ RETURN(ret);
+
+CLEANUP:
+ DPRINT("Leave NtUserDestroyCursorIcon, ret=%i\n",_ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+
+/*
+ * @implemented
+ */
+HICON
+APIENTRY
+NtUserFindExistingCursorIcon(
+ HMODULE hModule,
+ HRSRC hRsrc,
+ LONG cx,
+ LONG cy)
+{
+ PCURICON_OBJECT CurIcon;
+ HANDLE Ret = (HANDLE)0;
+ DECLARE_RETURN(HICON);
+
+ DPRINT("Enter NtUserFindExistingCursorIcon\n");
+ UserEnterExclusive();
+
+ CurIcon = IntFindExistingCurIconObject(hModule, hRsrc, cx, cy);
+ if (CurIcon)
+ {
+ Ret = CurIcon->Self;
+
+// IntReleaseCurIconObject(CurIcon);//faxme: is this correct? does IntFindExistingCurIconObject add a ref?
+ RETURN(Ret);
+ }
+
+ SetLastWin32Error(ERROR_INVALID_CURSOR_HANDLE);
+ RETURN((HANDLE)0);
+
+CLEANUP:
+ DPRINT("Leave NtUserFindExistingCursorIcon, ret=%i\n",_ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+
+/*
+ * @implemented
+ */
+BOOL
+APIENTRY
+NtUserGetClipCursor(
+ RECTL *lpRect)
+{
+ /* FIXME - check if process has WINSTA_READATTRIBUTES */
+ PSYSTEM_CURSORINFO CurInfo;
+ RECTL Rect;
+ NTSTATUS Status;
+ DECLARE_RETURN(BOOL);
+
+ DPRINT("Enter NtUserGetClipCursor\n");
+ UserEnterExclusive();
+
+ if (!lpRect)
+ RETURN(FALSE);
+
+ CurInfo = IntGetSysCursorInfo();
+ if (CurInfo->CursorClipInfo.IsClipped)
+ {
+ Rect.left = CurInfo->CursorClipInfo.Left;
+ Rect.top = CurInfo->CursorClipInfo.Top;
+ Rect.right = CurInfo->CursorClipInfo.Right;
+ Rect.bottom = CurInfo->CursorClipInfo.Bottom;
+ }
+ else
+ {
+ Rect.left = 0;
+ Rect.top = 0;
+ Rect.right = UserGetSystemMetrics(SM_CXSCREEN);
+ Rect.bottom = UserGetSystemMetrics(SM_CYSCREEN);
+ }
+
+ Status = MmCopyToCaller(lpRect, &Rect, sizeof(RECT));
+ if (!NT_SUCCESS(Status))
+ {
+ SetLastNtError(Status);
+ RETURN(FALSE);
+ }
+
+ RETURN(TRUE);
+
+CLEANUP:
+ DPRINT("Leave NtUserGetClipCursor, ret=%i\n",_ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+
+/*
+ * @implemented
+ */
+HCURSOR
+APIENTRY
+NtUserSetCursor(
+ HCURSOR hCursor)
+{
+ PCURICON_OBJECT CurIcon;
+ HICON OldCursor;
+ DECLARE_RETURN(HCURSOR);
+
+ DPRINT("Enter NtUserSetCursor\n");
+ UserEnterExclusive();
+
+ if (hCursor)
+ {
+ if (!(CurIcon = UserGetCurIconObject(hCursor)))
+ {
+ RETURN(NULL);
+ }
+ }
+ else
+ {
+ CurIcon = NULL;
+ }
+
+ OldCursor = UserSetCursor(CurIcon, FALSE);
+
+ if (CurIcon)
+ {
+ UserDereferenceObject(CurIcon);
+ }
+
+ RETURN(OldCursor);
+
+CLEANUP:
+ DPRINT("Leave NtUserSetCursor, ret=%i\n",_ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+
+/*
+ * @implemented
+ */
+BOOL
+APIENTRY
+NtUserSetCursorContents(
+ HANDLE hCurIcon,
+ PICONINFO UnsafeIconInfo)
+{
+ PCURICON_OBJECT CurIcon;
+ ICONINFO IconInfo;
+ PSURFACE psurfBmp;
+ NTSTATUS Status;
+ BOOL Ret = FALSE;
+ DECLARE_RETURN(BOOL);
+
+ DPRINT("Enter NtUserSetCursorContents\n");
+ UserEnterExclusive();
+
+ if (!(CurIcon = UserGetCurIconObject(hCurIcon)))
+ {
+ RETURN(FALSE);
+ }
+
+ /* Copy fields */
+ Status = MmCopyFromCaller(&IconInfo, UnsafeIconInfo, sizeof(ICONINFO));
+ if (!NT_SUCCESS(Status))
+ {
+ SetLastNtError(Status);
+ goto done;
+ }
+
+ /* Delete old bitmaps */
+ if ((CurIcon->IconInfo.hbmColor)
+ && (CurIcon->IconInfo.hbmColor != IconInfo.hbmColor))
+ {
+ GreDeleteObject(CurIcon->IconInfo.hbmColor);
+ }
+ if ((CurIcon->IconInfo.hbmMask)
+ && CurIcon->IconInfo.hbmMask != IconInfo.hbmMask)
+ {
+ GreDeleteObject(CurIcon->IconInfo.hbmMask);
+ }
+
+ /* Copy new IconInfo field */
+ CurIcon->IconInfo = IconInfo;
+
+ psurfBmp = SURFACE_LockSurface(CurIcon->IconInfo.hbmColor);
+ if (psurfBmp)
+ {
+ CurIcon->Size.cx = psurfBmp->SurfObj.sizlBitmap.cx;
+ CurIcon->Size.cy = psurfBmp->SurfObj.sizlBitmap.cy;
+ SURFACE_UnlockSurface(psurfBmp);
+ GDIOBJ_SetOwnership(CurIcon->IconInfo.hbmColor, NULL);
+ }
+ else
+ {
+ psurfBmp = SURFACE_LockSurface(CurIcon->IconInfo.hbmMask);
+ if (!psurfBmp)
+ goto done;
+
+ CurIcon->Size.cx = psurfBmp->SurfObj.sizlBitmap.cx;
+ CurIcon->Size.cy = psurfBmp->SurfObj.sizlBitmap.cy / 2;
+
+ SURFACE_UnlockSurface(psurfBmp);
+ }
+ GDIOBJ_SetOwnership(CurIcon->IconInfo.hbmMask, NULL);
+
+ Ret = TRUE;
+
+done:
+
+ if (CurIcon)
+ {
+ UserDereferenceObject(CurIcon);
+ }
+ RETURN(Ret);
+
+CLEANUP:
+ DPRINT("Leave NtUserSetCursorContents, ret=%i\n",_ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+
+/*
+ * @implemented
+ */
+#if 0
+BOOL
+APIENTRY
+NtUserSetCursorIconData(
+ HANDLE Handle,
+ HMODULE hModule,
+ PUNICODE_STRING pstrResName,
+ PICONINFO pIconInfo)
+{
+ PCURICON_OBJECT CurIcon;
+ PSURFACE psurfBmp;
+ NTSTATUS Status = STATUS_SUCCESS;
+ BOOL Ret = FALSE;
+ DECLARE_RETURN(BOOL);
+
+ DPRINT("Enter NtUserSetCursorIconData\n");
+ UserEnterExclusive();
+
+ if (!(CurIcon = UserGetCurIconObject(Handle)))
+ {
+ RETURN(FALSE);
+ }
+
+ CurIcon->hModule = hModule;
+ CurIcon->hRsrc = NULL; //hRsrc;
+ CurIcon->hGroupRsrc = NULL; //hGroupRsrc;
+
+ _SEH2_TRY
+ {
+ ProbeForRead(pIconInfo, sizeof(ICONINFO), 1);
+ RtlCopyMemory(&CurIcon->IconInfo, pIconInfo, sizeof(ICONINFO));
+
+ CurIcon->IconInfo.hbmMask = BITMAP_CopyBitmap(pIconInfo->hbmMask);
+ CurIcon->IconInfo.hbmColor = BITMAP_CopyBitmap(pIconInfo->hbmColor);
+
+ if (CurIcon->IconInfo.hbmColor)
+ {
+ if ((psurfBmp = SURFACE_LockSurface(CurIcon->IconInfo.hbmColor)))
+ {
+ CurIcon->Size.cx = psurfBmp->SurfObj.sizlBitmap.cx;
+ CurIcon->Size.cy = psurfBmp->SurfObj.sizlBitmap.cy;
+ SURFACE_UnlockSurface(psurfBmp);
+ GDIOBJ_SetOwnership(GdiHandleTable, CurIcon->IconInfo.hbmMask, NULL);
+ }
+ }
+ if (CurIcon->IconInfo.hbmMask)
+ {
+ if (CurIcon->IconInfo.hbmColor == NULL)
+ {
+ if ((psurfBmp = SURFACE_LockSurface(CurIcon->IconInfo.hbmMask)))
+ {
+ CurIcon->Size.cx = psurfBmp->SurfObj.sizlBitmap.cx;
+ CurIcon->Size.cy = psurfBmp->SurfObj.sizlBitmap.cy;
+ SURFACE_UnlockSurface(psurfBmp);
+ }
+ }
+ GDIOBJ_SetOwnership(GdiHandleTable, CurIcon->IconInfo.hbmMask, NULL);
+ }
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ Status = _SEH2_GetExceptionCode();
+ }
+ _SEH2_END
+
+ if (!NT_SUCCESS(Status))
+ SetLastNtError(Status);
+ else
+ Ret = TRUE;
+
+ UserDereferenceObject(CurIcon);
+ RETURN(Ret);
+
+CLEANUP:
+ DPRINT("Leave NtUserSetCursorIconData, ret=%i\n",_ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+#else
+BOOL
+APIENTRY
+NtUserSetCursorIconData(
+ HANDLE hCurIcon,
+ PBOOL fIcon,
+ POINT *Hotspot,
+ HMODULE hModule,
+ HRSRC hRsrc,
+ HRSRC hGroupRsrc)
+{
+ PCURICON_OBJECT CurIcon;
+ NTSTATUS Status;
+ POINT SafeHotspot;
+ BOOL Ret = FALSE;
+ DECLARE_RETURN(BOOL);
+
+ DPRINT("Enter NtUserSetCursorIconData\n");
+ UserEnterExclusive();
+
+ if (!(CurIcon = UserGetCurIconObject(hCurIcon)))
+ {
+ RETURN(FALSE);
+ }
+
+ CurIcon->hModule = hModule;
+ CurIcon->hRsrc = hRsrc;
+ CurIcon->hGroupRsrc = hGroupRsrc;
+
+ /* Copy fields */
+ if (fIcon)
+ {
+ Status = MmCopyFromCaller(&CurIcon->IconInfo.fIcon, fIcon, sizeof(BOOL));
+ if (!NT_SUCCESS(Status))
+ {
+ SetLastNtError(Status);
+ goto done;
+ }
+ }
+ else
+ {
+ if (!Hotspot)
+ Ret = TRUE;
+ }
+
+ if (Hotspot)
+ {
+ Status = MmCopyFromCaller(&SafeHotspot, Hotspot, sizeof(POINT));
+ if (NT_SUCCESS(Status))
+ {
+ CurIcon->IconInfo.xHotspot = SafeHotspot.x;
+ CurIcon->IconInfo.yHotspot = SafeHotspot.y;
+
+ Ret = TRUE;
+ }
+ else
+ SetLastNtError(Status);
+ }
+
+ if (!fIcon && !Hotspot)
+ {
+ Ret = TRUE;
+ }
+
+done:
+ if(Ret)
+ {
+ /* This icon is shared now */
+ GDIOBJ_SetOwnership(CurIcon->IconInfo.hbmMask, NULL);
+ if(CurIcon->IconInfo.hbmColor)
+ {
+ GDIOBJ_SetOwnership(CurIcon->IconInfo.hbmColor, NULL);
+ }
+ }
+ UserDereferenceObject(CurIcon);
+ RETURN(Ret);
+
+
+CLEANUP:
+ DPRINT("Leave NtUserSetCursorIconData, ret=%i\n",_ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+#endif
+
+/*
+ * @unimplemented
+ */
+BOOL
+APIENTRY
+NtUserSetSystemCursor(
+ HCURSOR hcur,
+ DWORD id)
+{
+ return FALSE;
+}
+
+/* Mostly inspired from wine code */
+BOOL
+UserDrawIconEx(
+ HDC hDc,
+ INT xLeft,
+ INT yTop,
+ PCURICON_OBJECT pIcon,
+ INT cxWidth,
+ INT cyHeight,
+ UINT istepIfAniCur,
+ HBRUSH hbrFlickerFreeDraw,
+ UINT diFlags)
+{
+ BOOL Ret = FALSE;
+ HBITMAP hbmMask, hbmColor;
+ BITMAP bmpColor, bm;
+ BOOL DoFlickerFree;
+ INT iOldBkColor = 0, iOldTxtColor = 0;
+
+ HDC hMemDC, hDestDC = hDc;
+ HGDIOBJ hOldOffBrush = 0;
+ HGDIOBJ hOldOffBmp = 0;
+ HBITMAP hTmpBmp = 0, hOffBmp = 0;
+ BOOL bAlpha = FALSE;
+ INT x=xLeft, y=yTop;
+
+ hbmMask = pIcon->IconInfo.hbmMask;
+ hbmColor = pIcon->IconInfo.hbmColor;
+
+ if (istepIfAniCur)
+ DPRINT1("NtUserDrawIconEx: istepIfAniCur is not supported!\n");
+
+ if (!hbmMask || !IntGdiGetObject(hbmMask, sizeof(BITMAP), (PVOID)&bm))
+ {
+ return FALSE;
+ }
+
+ if (hbmColor && !IntGdiGetObject(hbmColor, sizeof(BITMAP), (PVOID)&bmpColor))
+ {
+ return FALSE;
+ }
+
+ if(!(hMemDC = NtGdiCreateCompatibleDC(hDc)))
+ {
+ DPRINT1("NtGdiCreateCompatibleDC failed!\n");
+ return FALSE;
+ }
+
+ /* Check for alpha */
+ if (hbmColor
+ && (bmpColor.bmBitsPixel == 32)
+ && (diFlags & DI_IMAGE))
+ {
+ SURFACE *psurfOff = NULL;
+ PFN_DIB_GetPixel fnSource_GetPixel = NULL;
+ INT i, j;
+
+ /* In order to correctly display 32 bit icons Windows first scans the image,
+ because information about transparency is not stored in any image's headers */
+ psurfOff = SURFACE_LockSurface(hbmColor);
+ if (psurfOff)
+ {
+ fnSource_GetPixel = DibFunctionsForBitmapFormat[psurfOff->SurfObj.iBitmapFormat].DIB_GetPixel;
+ if (fnSource_GetPixel)
+ {
+ for (i = 0; i < psurfOff->SurfObj.sizlBitmap.cx; i++)
+ {
+ for (j = 0; j < psurfOff->SurfObj.sizlBitmap.cy; j++)
+ {
+ bAlpha = ((BYTE)(fnSource_GetPixel(&psurfOff->SurfObj, i, j) >> 24) & 0xff);
+ if (bAlpha)
+ break;
+ }
+ if (bAlpha)
+ break;
+ }
+ }
+ SURFACE_UnlockSurface(psurfOff);
+ }
+ }
+
+ if (!cxWidth)
+ cxWidth = ((diFlags & DI_DEFAULTSIZE) ?
+ UserGetSystemMetrics(SM_CXICON) : pIcon->Size.cx);
+
+ if (!cyHeight)
+ cyHeight = ((diFlags & DI_DEFAULTSIZE) ?
+ UserGetSystemMetrics(SM_CYICON) : pIcon->Size.cy);
+
+ DoFlickerFree = (hbrFlickerFreeDraw &&
+ (GDI_HANDLE_GET_TYPE(hbrFlickerFreeDraw) == GDI_OBJECT_TYPE_BRUSH));
+
+ if (DoFlickerFree)
+ {
+ hDestDC = NtGdiCreateCompatibleDC(hDc);
+ if(!hDestDC)
+ {
+ DPRINT1("NtGdiCreateCompatibleDC failed!\n");
+ Ret = FALSE;
+ goto Cleanup ;
+ }
+ hOffBmp = NtGdiCreateCompatibleBitmap(hDc, cxWidth, cyHeight);
+ if(!hOffBmp)
+ {
+ DPRINT1("NtGdiCreateCompatibleBitmap failed!\n");
+ goto Cleanup ;
+ }
+ hOldOffBmp = NtGdiSelectBitmap(hDestDC, hOffBmp);
+ hOldOffBrush = NtGdiSelectBrush(hDestDC, hbrFlickerFreeDraw);
+ NtGdiPatBlt(hDestDC, 0, 0, cxWidth, cyHeight, PATCOPY);
+ NtGdiSelectBrush(hDestDC, hOldOffBrush);
+ x=y=0;
+ }
+
+ /* Set Background/foreground colors */
+ iOldTxtColor = IntGdiSetTextColor(hDc, 0); //black
+ iOldBkColor = IntGdiSetBkColor(hDc, 0x00FFFFFF); //white
+
+ if(bAlpha && (diFlags & DI_IMAGE))
+ {
+ BLENDFUNCTION pixelblend = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
+ BYTE Alpha;
+ INT i, j;
+ PSURFACE psurf;
+ PBYTE ptr ;
+ HBITMAP hMemBmp = NULL;
+
+ hMemBmp = BITMAP_CopyBitmap(hbmColor);
+ if(!hMemBmp)
+ {
+ DPRINT1("BITMAP_CopyBitmap failed!");
+ goto CleanupAlpha;
+ }
+
+ psurf = SURFACE_LockSurface(hMemBmp);
+ if(!psurf)
+ {
+ DPRINT1("SURFACE_LockSurface failed!\n");
+ goto CleanupAlpha;
+ }
+
+ /* premultiply with the alpha channel value */
+ for (i = 0; i < psurf->SurfObj.sizlBitmap.cy; i++)
+ {
+ ptr = (PBYTE)psurf->SurfObj.pvScan0 + i*psurf->SurfObj.lDelta;
+ for (j = 0; j < psurf->SurfObj.sizlBitmap.cx; j++)
+ {
+ Alpha = ptr[3];
+ ptr[0] = (ptr[0] * Alpha) / 0xff;
+ ptr[1] = (ptr[1] * Alpha) / 0xff;
+ ptr[2] = (ptr[2] * Alpha) / 0xff;
+
+ ptr += 4;
+ }
+ }
+
+ SURFACE_UnlockSurface(psurf);
+
+ hTmpBmp = NtGdiSelectBitmap(hMemDC, hMemBmp);
+
+ Ret = NtGdiAlphaBlend(hDestDC,
+ x,
+ y,
+ cxWidth,
+ cyHeight,
+ hMemDC,
+ 0,
+ 0,
+ pIcon->Size.cx,
+ pIcon->Size.cy,
+ pixelblend,
+ NULL);
+ NtGdiSelectBitmap(hMemDC, hTmpBmp);
+ CleanupAlpha:
+ if(hMemBmp) NtGdiDeleteObjectApp(hMemBmp);
+ if(Ret) goto done;
+ }
+
+ if (diFlags & DI_MASK)
+ {
+ hTmpBmp = NtGdiSelectBitmap(hMemDC, hbmMask);
+ NtGdiStretchBlt(hDestDC,
+ x,
+ y,
+ cxWidth,
+ cyHeight,
+ hMemDC,
+ 0,
+ 0,
+ pIcon->Size.cx,
+ pIcon->Size.cy,
+ SRCAND,
+ 0);
+ NtGdiSelectBitmap(hMemDC, hTmpBmp);
+ }
+
+ if(diFlags & DI_IMAGE)
+ {
+ if (hbmColor)
+ {
+ DWORD rop = (diFlags & DI_MASK) ? SRCINVERT : SRCCOPY ;
+ hTmpBmp = NtGdiSelectBitmap(hMemDC, hbmColor);
+ NtGdiStretchBlt(hDestDC,
+ x,
+ y,
+ cxWidth,
+ cyHeight,
+ hMemDC,
+ 0,
+ 0,
+ pIcon->Size.cx,
+ pIcon->Size.cy,
+ rop,
+ 0);
+ NtGdiSelectBitmap(hMemDC, hTmpBmp);
+ }
+ else
+ {
+ /* Mask bitmap holds the information in its second half */
+ DWORD rop = (diFlags & DI_MASK) ? SRCINVERT : SRCCOPY ;
+ hTmpBmp = NtGdiSelectBitmap(hMemDC, hbmMask);
+ NtGdiStretchBlt(hDestDC,
+ x,
+ y,
+ cxWidth,
+ cyHeight,
+ hMemDC,
+ 0,
+ pIcon->Size.cy,
+ pIcon->Size.cx,
+ pIcon->Size.cy,
+ rop,
+ 0);
+ NtGdiSelectBitmap(hMemDC, hTmpBmp);
+ }
+ }
+
+done:
+ if(hDestDC != hDc)
+ {
+ NtGdiBitBlt(hDc, xLeft, yTop, cxWidth, cyHeight, hDestDC, 0, 0, SRCCOPY, 0, 0);
+ }
+
+ /* Restore foreground and background colors */
+ IntGdiSetBkColor(hDc, iOldBkColor);
+ IntGdiSetTextColor(hDc, iOldTxtColor);
+
+ Ret = TRUE ;
+
+Cleanup:
+ NtGdiDeleteObjectApp(hMemDC);
+ if(hDestDC != hDc)
+ {
+ if(hOldOffBmp) NtGdiSelectBitmap(hDestDC, hOldOffBmp);
+ NtGdiDeleteObjectApp(hDestDC);
+ if(hOffBmp) NtGdiDeleteObjectApp(hOffBmp);
+ }
+
+ return Ret;
+}
+
+/*
+ * @implemented
+ */
+BOOL
+APIENTRY
+NtUserDrawIconEx(
+ HDC hdc,
+ int xLeft,
+ int yTop,
+ HICON hIcon,
+ int cxWidth,
+ int cyHeight,
+ UINT istepIfAniCur,
+ HBRUSH hbrFlickerFreeDraw,
+ UINT diFlags,
+ BOOL bMetaHDC, // When TRUE, GDI functions need to be handled in User32!
+ PVOID pDIXData)
+{
+ PCURICON_OBJECT pIcon;
+ BOOL Ret;
+
+ DPRINT("Enter NtUserDrawIconEx\n");
+ UserEnterExclusive();
+
+ if (!(pIcon = UserGetCurIconObject(hIcon)))
+ {
+ DPRINT1("UserGetCurIconObject() failed!\n");
+ UserLeave();
+ return FALSE;
+ }
+
+ Ret = UserDrawIconEx(hdc,
+ xLeft,
+ yTop,
+ pIcon,
+ cxWidth,
+ cyHeight,
+ istepIfAniCur,
+ hbrFlickerFreeDraw,
+ diFlags);
+
+ UserDereferenceObject(pIcon);
+
+ UserLeave();
+ return Ret;
+}
+
--- /dev/null
- MSG Message;
+/*
+ * COPYRIGHT: See COPYING in the top level directory
+ * PROJECT: ReactOS kernel
+ * PURPOSE: Messages
+ * FILE: subsys/win32k/ntuser/message.c
+ * PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
+ * REVISION HISTORY:
+ * 06-06-2001 CSH Created
+ */
+
+/* INCLUDES ******************************************************************/
+
+#include <win32k.h>
+
+#define NDEBUG
+#include <debug.h>
+
+#define PM_BADMSGFLAGS ~((QS_RAWINPUT << 16)|PM_QS_SENDMESSAGE|PM_QS_PAINT|PM_QS_POSTMESSAGE|PM_QS_INPUT|PM_NOYIELD|PM_REMOVE)
+
+typedef struct
+{
+ UINT uFlags;
+ UINT uTimeout;
+ ULONG_PTR Result;
+}
+DOSENDMESSAGE, *PDOSENDMESSAGE;
+
+/* FUNCTIONS *****************************************************************/
+
+NTSTATUS FASTCALL
+IntInitMessageImpl(VOID)
+{
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS FASTCALL
+IntCleanupMessageImpl(VOID)
+{
+ return STATUS_SUCCESS;
+}
+
+#define MMS_SIZE_WPARAM -1
+#define MMS_SIZE_WPARAMWCHAR -2
+#define MMS_SIZE_LPARAMSZ -3
+#define MMS_SIZE_SPECIAL -4
+#define MMS_FLAG_READ 0x01
+#define MMS_FLAG_WRITE 0x02
+#define MMS_FLAG_READWRITE (MMS_FLAG_READ | MMS_FLAG_WRITE)
+typedef struct tagMSGMEMORY
+{
+ UINT Message;
+ UINT Size;
+ INT Flags;
+}
+MSGMEMORY, *PMSGMEMORY;
+
+static MSGMEMORY MsgMemory[] =
+ {
+ { WM_CREATE, MMS_SIZE_SPECIAL, MMS_FLAG_READWRITE },
+ { WM_DDE_ACK, sizeof(KMDDELPARAM), MMS_FLAG_READ },
+ { WM_DDE_EXECUTE, MMS_SIZE_WPARAM, MMS_FLAG_READ },
+ { WM_GETMINMAXINFO, sizeof(MINMAXINFO), MMS_FLAG_READWRITE },
+ { WM_GETTEXT, MMS_SIZE_WPARAMWCHAR, MMS_FLAG_WRITE },
+ { WM_NCCALCSIZE, MMS_SIZE_SPECIAL, MMS_FLAG_READWRITE },
+ { WM_NCCREATE, MMS_SIZE_SPECIAL, MMS_FLAG_READWRITE },
+ { WM_SETTEXT, MMS_SIZE_LPARAMSZ, MMS_FLAG_READ },
+ { WM_STYLECHANGED, sizeof(STYLESTRUCT), MMS_FLAG_READ },
+ { WM_STYLECHANGING, sizeof(STYLESTRUCT), MMS_FLAG_READWRITE },
+ { WM_COPYDATA, MMS_SIZE_SPECIAL, MMS_FLAG_READ },
+ { WM_WINDOWPOSCHANGED, sizeof(WINDOWPOS), MMS_FLAG_READ },
+ { WM_WINDOWPOSCHANGING, sizeof(WINDOWPOS), MMS_FLAG_READWRITE },
+ };
+
+static PMSGMEMORY FASTCALL
+FindMsgMemory(UINT Msg)
+{
+ PMSGMEMORY MsgMemoryEntry;
+
+ /* See if this message type is present in the table */
+ for (MsgMemoryEntry = MsgMemory;
+ MsgMemoryEntry < MsgMemory + sizeof(MsgMemory) / sizeof(MSGMEMORY);
+ MsgMemoryEntry++)
+ {
+ if (Msg == MsgMemoryEntry->Message)
+ {
+ return MsgMemoryEntry;
+ }
+ }
+
+ return NULL;
+}
+
+static UINT FASTCALL
+MsgMemorySize(PMSGMEMORY MsgMemoryEntry, WPARAM wParam, LPARAM lParam)
+{
+ CREATESTRUCTW *Cs;
+ PUNICODE_STRING WindowName;
+ PUNICODE_STRING ClassName;
+ UINT Size = 0;
+
+ _SEH2_TRY
+ {
+ if (MMS_SIZE_WPARAM == MsgMemoryEntry->Size)
+ {
+ Size = (UINT)wParam;
+ }
+ else if (MMS_SIZE_WPARAMWCHAR == MsgMemoryEntry->Size)
+ {
+ Size = (UINT) (wParam * sizeof(WCHAR));
+ }
+ else if (MMS_SIZE_LPARAMSZ == MsgMemoryEntry->Size)
+ {
+ Size = (UINT) ((wcslen((PWSTR) lParam) + 1) * sizeof(WCHAR));
+ }
+ else if (MMS_SIZE_SPECIAL == MsgMemoryEntry->Size)
+ {
+ switch(MsgMemoryEntry->Message)
+ {
+ case WM_CREATE:
+ case WM_NCCREATE:
+ Cs = (CREATESTRUCTW *) lParam;
+ WindowName = (PUNICODE_STRING) Cs->lpszName;
+ ClassName = (PUNICODE_STRING) Cs->lpszClass;
+ Size = sizeof(CREATESTRUCTW) + WindowName->Length + sizeof(WCHAR);
+ if (IS_ATOM(ClassName->Buffer))
+ {
+ Size += sizeof(WCHAR) + sizeof(ATOM);
+ }
+ else
+ {
+ Size += sizeof(WCHAR) + ClassName->Length + sizeof(WCHAR);
+ }
+ break;
+
+ case WM_NCCALCSIZE:
+ Size = wParam ? sizeof(NCCALCSIZE_PARAMS) + sizeof(WINDOWPOS) : sizeof(RECT);
+ break;
+
+ case WM_COPYDATA:
+ Size = sizeof(COPYDATASTRUCT) + ((PCOPYDATASTRUCT)lParam)->cbData;
+ break;
+
+ case WM_COPYGLOBALDATA:
+ Size = wParam;
+ break;
+
+ default:
+ ASSERT(FALSE);
+ Size = 0;
+ break;
+ }
+ }
+ else
+ {
+ Size = MsgMemoryEntry->Size;
+ }
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ DPRINT1("Exception caught in MsgMemorySize()! Status: 0x%x\n", _SEH2_GetExceptionCode());
+ Size = 0;
+ }
+ _SEH2_END;
+ return Size;
+}
+
+static NTSTATUS
+PackParam(LPARAM *lParamPacked, UINT Msg, WPARAM wParam, LPARAM lParam, BOOL NonPagedPoolNeeded)
+{
+ NCCALCSIZE_PARAMS *UnpackedNcCalcsize;
+ NCCALCSIZE_PARAMS *PackedNcCalcsize;
+ CREATESTRUCTW *UnpackedCs;
+ CREATESTRUCTW *PackedCs;
+ PLARGE_STRING WindowName;
+ PUNICODE_STRING ClassName;
+ POOL_TYPE PoolType;
+ UINT Size;
+ PCHAR CsData;
+
+ *lParamPacked = lParam;
+
+ if (NonPagedPoolNeeded)
+ PoolType = NonPagedPool;
+ else
+ PoolType = PagedPool;
+
+ if (WM_NCCALCSIZE == Msg && wParam)
+ {
+
+ UnpackedNcCalcsize = (NCCALCSIZE_PARAMS *) lParam;
+ PackedNcCalcsize = ExAllocatePoolWithTag(PoolType,
+ sizeof(NCCALCSIZE_PARAMS) + sizeof(WINDOWPOS),
+ TAG_MSG);
+
+ if (NULL == PackedNcCalcsize)
+ {
+ DPRINT1("Not enough memory to pack lParam\n");
+ return STATUS_NO_MEMORY;
+ }
+ RtlCopyMemory(PackedNcCalcsize, UnpackedNcCalcsize, sizeof(NCCALCSIZE_PARAMS));
+ PackedNcCalcsize->lppos = (PWINDOWPOS) (PackedNcCalcsize + 1);
+ RtlCopyMemory(PackedNcCalcsize->lppos, UnpackedNcCalcsize->lppos, sizeof(WINDOWPOS));
+ *lParamPacked = (LPARAM) PackedNcCalcsize;
+ }
+ else if (WM_CREATE == Msg || WM_NCCREATE == Msg)
+ {
+ UnpackedCs = (CREATESTRUCTW *) lParam;
+ WindowName = (PLARGE_STRING) UnpackedCs->lpszName;
+ ClassName = (PUNICODE_STRING) UnpackedCs->lpszClass;
+ Size = sizeof(CREATESTRUCTW) + WindowName->Length + sizeof(WCHAR);
+ if (IS_ATOM(ClassName->Buffer))
+ {
+ Size += sizeof(WCHAR) + sizeof(ATOM);
+ }
+ else
+ {
+ Size += sizeof(WCHAR) + ClassName->Length + sizeof(WCHAR);
+ }
+ PackedCs = ExAllocatePoolWithTag(PoolType, Size, TAG_MSG);
+ if (NULL == PackedCs)
+ {
+ DPRINT1("Not enough memory to pack lParam\n");
+ return STATUS_NO_MEMORY;
+ }
+ RtlCopyMemory(PackedCs, UnpackedCs, sizeof(CREATESTRUCTW));
+ CsData = (PCHAR) (PackedCs + 1);
+ PackedCs->lpszName = (LPCWSTR) (CsData - (PCHAR) PackedCs);
+ RtlCopyMemory(CsData, WindowName->Buffer, WindowName->Length);
+ CsData += WindowName->Length;
+ *((WCHAR *) CsData) = L'\0';
+ CsData += sizeof(WCHAR);
+ PackedCs->lpszClass = (LPCWSTR) (CsData - (PCHAR) PackedCs);
+ if (IS_ATOM(ClassName->Buffer))
+ {
+ *((WCHAR *) CsData) = L'A';
+ CsData += sizeof(WCHAR);
+ *((ATOM *) CsData) = (ATOM)(DWORD_PTR) ClassName->Buffer;
+ CsData += sizeof(ATOM);
+ }
+ else
+ {
+ *((WCHAR *) CsData) = L'S';
+ CsData += sizeof(WCHAR);
+ RtlCopyMemory(CsData, ClassName->Buffer, ClassName->Length);
+ CsData += ClassName->Length;
+ *((WCHAR *) CsData) = L'\0';
+ CsData += sizeof(WCHAR);
+ }
+ ASSERT(CsData == (PCHAR) PackedCs + Size);
+ *lParamPacked = (LPARAM) PackedCs;
+ }
+
+ else if (PoolType == NonPagedPool)
+ {
+ PMSGMEMORY MsgMemoryEntry;
+ PVOID PackedData;
+
+ MsgMemoryEntry = FindMsgMemory(Msg);
+
+ if ((!MsgMemoryEntry) || (MsgMemoryEntry->Size < 0))
+ {
+ /* Keep previous behavior */
+ return STATUS_SUCCESS;
+ }
+ PackedData = ExAllocatePoolWithTag(NonPagedPool, MsgMemorySize(MsgMemoryEntry, wParam, lParam), TAG_MSG);
+ RtlCopyMemory(PackedData, (PVOID)lParam, MsgMemorySize(MsgMemoryEntry, wParam, lParam));
+ *lParamPacked = (LPARAM)PackedData;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS
+UnpackParam(LPARAM lParamPacked, UINT Msg, WPARAM wParam, LPARAM lParam, BOOL NonPagedPoolUsed)
+{
+ NCCALCSIZE_PARAMS *UnpackedParams;
+ NCCALCSIZE_PARAMS *PackedParams;
+ PWINDOWPOS UnpackedWindowPos;
+
+ if (lParamPacked == lParam)
+ {
+ return STATUS_SUCCESS;
+ }
+
+ if (WM_NCCALCSIZE == Msg && wParam)
+ {
+ PackedParams = (NCCALCSIZE_PARAMS *) lParamPacked;
+ UnpackedParams = (NCCALCSIZE_PARAMS *) lParam;
+ UnpackedWindowPos = UnpackedParams->lppos;
+ RtlCopyMemory(UnpackedParams, PackedParams, sizeof(NCCALCSIZE_PARAMS));
+ UnpackedParams->lppos = UnpackedWindowPos;
+ RtlCopyMemory(UnpackedWindowPos, PackedParams + 1, sizeof(WINDOWPOS));
+ ExFreePool((PVOID) lParamPacked);
+
+ return STATUS_SUCCESS;
+ }
+ else if (WM_CREATE == Msg || WM_NCCREATE == Msg)
+ {
+ ExFreePool((PVOID) lParamPacked);
+
+ return STATUS_SUCCESS;
+ }
+ else if (NonPagedPoolUsed)
+ {
+ PMSGMEMORY MsgMemoryEntry;
+ MsgMemoryEntry = FindMsgMemory(Msg);
+ if (MsgMemoryEntry->Size < 0)
+ {
+ /* Keep previous behavior */
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if (MsgMemory->Flags == MMS_FLAG_READWRITE)
+ {
+ //RtlCopyMemory((PVOID)lParam, (PVOID)lParamPacked, MsgMemory->Size);
+ }
+ ExFreePool((PVOID) lParamPacked);
+ return STATUS_SUCCESS;
+ }
+
+ ASSERT(FALSE);
+
+ return STATUS_INVALID_PARAMETER;
+}
+
+static
+VOID
+FASTCALL
+IntCallWndProc
+( PWINDOW_OBJECT Window, HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
+{
+ BOOL SameThread = FALSE;
+
+ if (Window->pti == ((PTHREADINFO)PsGetCurrentThreadWin32Thread()))
+ SameThread = TRUE;
+
+ if ((!SameThread && (Window->pti->fsHooks & HOOKID_TO_FLAG(WH_CALLWNDPROC))) ||
+ (SameThread && ISITHOOKED(WH_CALLWNDPROC)) )
+ {
+ CWPSTRUCT CWP;
+ CWP.hwnd = hWnd;
+ CWP.message = Msg;
+ CWP.wParam = wParam;
+ CWP.lParam = lParam;
+ co_HOOK_CallHooks( WH_CALLWNDPROC, HC_ACTION, SameThread, (LPARAM)&CWP );
+ }
+}
+
+static
+VOID
+FASTCALL
+IntCallWndProcRet
+( PWINDOW_OBJECT Window, HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT *uResult)
+{
+ BOOL SameThread = FALSE;
+
+ if (Window->pti == ((PTHREADINFO)PsGetCurrentThreadWin32Thread()))
+ SameThread = TRUE;
+
+ if ((!SameThread && (Window->pti->fsHooks & HOOKID_TO_FLAG(WH_CALLWNDPROCRET))) ||
+ (SameThread && ISITHOOKED(WH_CALLWNDPROCRET)) )
+ {
+ CWPRETSTRUCT CWPR;
+ CWPR.hwnd = hWnd;
+ CWPR.message = Msg;
+ CWPR.wParam = wParam;
+ CWPR.lParam = lParam;
+ CWPR.lResult = *uResult;
+ co_HOOK_CallHooks( WH_CALLWNDPROCRET, HC_ACTION, SameThread, (LPARAM)&CWPR );
+ }
+}
+
+LRESULT
+FASTCALL
+IntDispatchMessage(PMSG pMsg)
+{
+ LARGE_INTEGER TickCount;
+ LONG Time;
+ LRESULT retval;
+ PMSGMEMORY MsgMemoryEntry;
+ INT lParamBufferSize;
+ LPARAM lParamPacked;
+ PWINDOW_OBJECT Window = NULL;
+
+ if (pMsg->hwnd)
+ {
+ Window = UserGetWindowObject(pMsg->hwnd);
+ if (!Window || !Window->Wnd) return 0;
+ }
+
+ if (((pMsg->message == WM_SYSTIMER) ||
+ (pMsg->message == WM_TIMER)) &&
+ (pMsg->lParam) )
+ {
+ if (pMsg->message == WM_TIMER)
+ {
+ if (ValidateTimerCallback(PsGetCurrentThreadWin32Thread(),Window,pMsg->wParam,pMsg->lParam))
+ {
+ KeQueryTickCount(&TickCount);
+ Time = MsqCalculateMessageTime(&TickCount);
+ return co_IntCallWindowProc((WNDPROC)pMsg->lParam,
+ TRUE,
+ pMsg->hwnd,
+ WM_TIMER,
+ pMsg->wParam,
+ (LPARAM)Time,
+ sizeof(LPARAM));
+ }
+ return 0;
+ }
+ else
+ {
+ PTIMER pTimer = FindSystemTimer(pMsg);
+ if (pTimer && pTimer->pfn)
+ {
+ KeQueryTickCount(&TickCount);
+ Time = MsqCalculateMessageTime(&TickCount);
+ pTimer->pfn(pMsg->hwnd, WM_SYSTIMER, (UINT)pMsg->wParam, Time);
+ }
+ return 0;
+ }
+ }
+ // Need a window!
+ if ( !Window || !Window->Wnd ) return 0;
+
+ /* See if this message type is present in the table */
+ MsgMemoryEntry = FindMsgMemory(pMsg->message);
+ if ( !MsgMemoryEntry )
+ {
+ lParamBufferSize = -1;
+ }
+ else
+ {
+ lParamBufferSize = MsgMemorySize(MsgMemoryEntry, pMsg->wParam, pMsg->lParam);
+ }
+
+ if (! NT_SUCCESS(PackParam(&lParamPacked, pMsg->message, pMsg->wParam, pMsg->lParam, FALSE)))
+ {
+ DPRINT1("Failed to pack message parameters\n");
+ return 0;
+ }
+
+ retval = co_IntCallWindowProc( Window->Wnd->lpfnWndProc,
+ !Window->Wnd->Unicode,
+ pMsg->hwnd,
+ pMsg->message,
+ pMsg->wParam,
+ lParamPacked,
+ lParamBufferSize);
+
+ if (! NT_SUCCESS(UnpackParam(lParamPacked, pMsg->message, pMsg->wParam, pMsg->lParam, FALSE)))
+ {
+ DPRINT1("Failed to unpack message parameters\n");
+ }
+
+ if (pMsg->message == WM_PAINT)
+ {
+ /* send a WM_NCPAINT and WM_ERASEBKGND if the non-client area is still invalid */
+ HRGN hrgn = IntSysCreateRectRgn( 0, 0, 0, 0 );
+ co_UserGetUpdateRgn( Window, hrgn, TRUE );
+ REGION_FreeRgnByHandle( hrgn );
+ }
+ return retval;
+}
+
+VOID FASTCALL
+co_IntSendHitTestMessages(PUSER_MESSAGE_QUEUE ThreadQueue, LPMSG Msg)
+{
+ if(!Msg->hwnd || ThreadQueue->CaptureWindow)
+ {
+ return;
+ }
+
+ switch(Msg->message)
+ {
+ case WM_MOUSEMOVE:
+ {
+ co_IntSendMessage(Msg->hwnd, WM_SETCURSOR, (WPARAM)Msg->hwnd, MAKELPARAM(HTCLIENT, Msg->message));
+ break;
+ }
+ case WM_NCMOUSEMOVE:
+ {
+ co_IntSendMessage(Msg->hwnd, WM_SETCURSOR, (WPARAM)Msg->hwnd, MAKELPARAM(Msg->wParam, Msg->message));
+ break;
+ }
+ case WM_LBUTTONDOWN:
+ case WM_MBUTTONDOWN:
+ case WM_RBUTTONDOWN:
+ case WM_XBUTTONDOWN:
+ case WM_LBUTTONDBLCLK:
+ case WM_MBUTTONDBLCLK:
+ case WM_RBUTTONDBLCLK:
+ case WM_XBUTTONDBLCLK:
+ {
+ WPARAM wParam;
+ PSYSTEM_CURSORINFO CurInfo;
+ CurInfo = IntGetSysCursorInfo();
+
+ wParam = (WPARAM)(CurInfo->ButtonsDown);
+
+ co_IntSendMessage(Msg->hwnd, WM_MOUSEMOVE, wParam, Msg->lParam);
+ co_IntSendMessage(Msg->hwnd, WM_SETCURSOR, (WPARAM)Msg->hwnd, MAKELPARAM(HTCLIENT, Msg->message));
+ break;
+ }
+ case WM_NCLBUTTONDOWN:
+ case WM_NCMBUTTONDOWN:
+ case WM_NCRBUTTONDOWN:
+ case WM_NCXBUTTONDOWN:
+ case WM_NCLBUTTONDBLCLK:
+ case WM_NCMBUTTONDBLCLK:
+ case WM_NCRBUTTONDBLCLK:
+ case WM_NCXBUTTONDBLCLK:
+ {
+ co_IntSendMessage(Msg->hwnd, WM_NCMOUSEMOVE, (WPARAM)Msg->wParam, Msg->lParam);
+ co_IntSendMessage(Msg->hwnd, WM_SETCURSOR, (WPARAM)Msg->hwnd, MAKELPARAM(Msg->wParam, Msg->message));
+ break;
+ }
+ }
+}
+
+BOOL FASTCALL
+co_IntActivateWindowMouse(
+ PUSER_MESSAGE_QUEUE ThreadQueue,
+ LPMSG Msg,
+ PWINDOW_OBJECT MsgWindow,
+ USHORT *HitTest)
+{
+ ULONG Result;
+ PWINDOW_OBJECT Parent;
+
+ ASSERT_REFS_CO(MsgWindow);
+
+ if(*HitTest == (USHORT)HTTRANSPARENT)
+ {
+ /* eat the message, search again! */
+ return TRUE;
+ }
+
+ Parent = IntGetParent(MsgWindow);//fixme: deref retval?
+
+ /* If no parent window, pass MsgWindows HWND as wParam. Fixes bug #3111 */
+ Result = co_IntSendMessage(MsgWindow->hSelf,
+ WM_MOUSEACTIVATE,
+ (WPARAM) (Parent ? Parent->hSelf : MsgWindow->hSelf),
+ (LPARAM)MAKELONG(*HitTest, Msg->message)
+ );
+
+ switch (Result)
+ {
+ case MA_NOACTIVATEANDEAT:
+ return TRUE;
+ case MA_NOACTIVATE:
+ break;
+ case MA_ACTIVATEANDEAT:
+ co_IntMouseActivateWindow(MsgWindow);
+ return TRUE;
+ default:
+ /* MA_ACTIVATE */
+ co_IntMouseActivateWindow(MsgWindow);
+ break;
+ }
+
+ return FALSE;
+}
+
+BOOL FASTCALL
+co_IntTranslateMouseMessage(
+ PUSER_MESSAGE_QUEUE ThreadQueue,
+ LPMSG Msg,
+ USHORT *HitTest,
+ BOOL Remove)
+{
+ PWINDOW_OBJECT Window;
+ USER_REFERENCE_ENTRY Ref, DesktopRef;
+
+ if(!(Window = UserGetWindowObject(Msg->hwnd)))
+ {
+ /* let's just eat the message?! */
+ return TRUE;
+ }
+
+ *HitTest = HTCLIENT;
+
+ UserRefObjectCo(Window, &Ref);
+
+ if ( ThreadQueue == Window->pti->MessageQueue &&
+ ThreadQueue->CaptureWindow != Window->hSelf)
+ {
+ /* only send WM_NCHITTEST messages if we're not capturing the window! */
+ if (Remove )
+ {
+ *HitTest = co_IntSendMessage(Window->hSelf, WM_NCHITTEST, 0,
+ MAKELONG(Msg->pt.x, Msg->pt.y));
+ }
+ /* else we are going to see this message again, but then with Remove == TRUE */
+
+ if (*HitTest == (USHORT)HTTRANSPARENT)
+ {
+ PWINDOW_OBJECT DesktopWindow;
+ HWND hDesktop = IntGetDesktopWindow();
+
+ if ((DesktopWindow = UserGetWindowObject(hDesktop)))
+ {
+ PWINDOW_OBJECT Wnd;
+
+ UserRefObjectCo(DesktopWindow, &DesktopRef);
+
+ co_WinPosWindowFromPoint(DesktopWindow, Window->pti->MessageQueue, &Msg->pt, &Wnd);
+ if (Wnd)
+ {
+ if (Wnd != Window)
+ {
+ /* post the message to the other window */
+ Msg->hwnd = Wnd->hSelf;
+ if(!(Wnd->state & WINDOWSTATUS_DESTROYING))
+ {
+ MsqPostMessage(Wnd->pti->MessageQueue, Msg, FALSE,
+ Msg->message == WM_MOUSEMOVE ? QS_MOUSEMOVE :
+ QS_MOUSEBUTTON);
+ }
+
+ /* eat the message */
+ UserDereferenceObject(Wnd);
+ UserDerefObjectCo(DesktopWindow);
+ UserDerefObjectCo(Window);
+ return TRUE;
+ }
+ UserDereferenceObject(Wnd);
+ }
+
+ UserDerefObjectCo(DesktopWindow);
+ }
+ }
+ }
+
+ if ( gspv.bMouseClickLock &&
+ ( (Msg->message == WM_LBUTTONUP) ||
+ (Msg->message == WM_LBUTTONDOWN) ) )
+ {
+ if (MsqIsClkLck(Msg, Remove))
+ {
+ // FIXME: drop the message, hack: use WM_NULL
+ Msg->message = WM_NULL;
+ }
+ }
+
+ if (IS_BTN_MESSAGE(Msg->message, DOWN))
+ {
+ /* generate double click messages, if necessary */
+ if ((((*HitTest) != HTCLIENT) ||
+ (Window->Wnd->pcls->style & CS_DBLCLKS)) &&
+ MsqIsDblClk(Msg, Remove))
+ {
+ Msg->message += WM_LBUTTONDBLCLK - WM_LBUTTONDOWN;
+ }
+ }
+
+ if(Msg->message != WM_MOUSEWHEEL)
+ {
+
+ if ((*HitTest) != HTCLIENT)
+ {
+ Msg->message += WM_NCMOUSEMOVE - WM_MOUSEMOVE;
+ if ( (Msg->message == WM_NCRBUTTONUP) &&
+ (((*HitTest) == HTCAPTION) || ((*HitTest) == HTSYSMENU)) )
+ {
+ Msg->message = WM_CONTEXTMENU;
+ Msg->wParam = (WPARAM)Window->hSelf;
+ }
+ else
+ {
+ Msg->wParam = *HitTest;
+ }
+ Msg->lParam = MAKELONG(Msg->pt.x, Msg->pt.y);
+ }
+ else if ( ThreadQueue->MoveSize == NULL &&
+ ThreadQueue->MenuOwner == NULL )
+ {
+ /* NOTE: Msg->pt should remain in screen coordinates. -- FiN */
+ Msg->lParam = MAKELONG(
+ Msg->pt.x - (WORD)Window->Wnd->rcClient.left,
+ Msg->pt.y - (WORD)Window->Wnd->rcClient.top);
+ }
+ }
+
+ UserDerefObjectCo(Window);
+ return FALSE;
+}
+
+BOOL ProcessMouseMessage(MSG* Msg, USHORT HitTest, UINT RemoveMsg)
+{
+ MOUSEHOOKSTRUCT MHook;
+ EVENTMSG Event;
+
+ Event.message = Msg->message;
+ Event.time = Msg->time;
+ Event.hwnd = Msg->hwnd;
+ Event.paramL = Msg->pt.x;
+ Event.paramH = Msg->pt.y;
+ co_HOOK_CallHooks( WH_JOURNALRECORD, HC_ACTION, 0, (LPARAM)&Event);
+
+
+ MHook.pt = Msg->pt;
+ MHook.hwnd = Msg->hwnd;
+ MHook.wHitTestCode = HitTest;
+ MHook.dwExtraInfo = 0;
+ if (co_HOOK_CallHooks( WH_MOUSE,
+ RemoveMsg ? HC_ACTION : HC_NOREMOVE,
+ Msg->message,
+ (LPARAM)&MHook ))
+ {
+ if (ISITHOOKED(WH_CBT))
+ {
+ MHook.pt = Msg->pt;
+ MHook.hwnd = Msg->hwnd;
+ MHook.wHitTestCode = HitTest;
+ MHook.dwExtraInfo = 0;
+ co_HOOK_CallHooks( WH_CBT,
+ HCBT_CLICKSKIPPED,
+ Msg->message,
+ (LPARAM)&MHook);
+ }
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+BOOL ProcessKeyboardMessage(MSG* Msg, UINT RemoveMsg)
+{
+ EVENTMSG Event;
+
+ Event.message = Msg->message;
+ Event.hwnd = Msg->hwnd;
+ Event.time = Msg->time;
+ Event.paramL = (Msg->wParam & 0xFF) | (HIWORD(Msg->lParam) << 8);
+ Event.paramH = Msg->lParam & 0x7FFF;
+ if (HIWORD(Msg->lParam) & 0x0100) Event.paramH |= 0x8000;
+ co_HOOK_CallHooks( WH_JOURNALRECORD, HC_ACTION, 0, (LPARAM)&Event);
+
+ if (co_HOOK_CallHooks( WH_KEYBOARD,
+ RemoveMsg ? HC_ACTION : HC_NOREMOVE,
+ LOWORD(Msg->wParam),
+ Msg->lParam))
+ {
+ if (ISITHOOKED(WH_CBT))
+ {
+ /* skip this message */
+ co_HOOK_CallHooks( WH_CBT,
+ HCBT_KEYSKIPPED,
+ LOWORD(Msg->wParam),
+ Msg->lParam );
+ }
+ return FALSE;
+ }
+ return TRUE;
+}
+/*
+ * Internal version of PeekMessage() doing all the work
+ */
+BOOL FASTCALL
+co_IntPeekMessage( PUSER_MESSAGE Msg,
+ PWINDOW_OBJECT Window,
+ UINT MsgFilterMin,
+ UINT MsgFilterMax,
+ UINT RemoveMsg )
+{
+ PTHREADINFO pti;
+ LARGE_INTEGER LargeTickCount;
+ PUSER_MESSAGE_QUEUE ThreadQueue;
+ PUSER_MESSAGE Message;
+ BOOL Present, RemoveMessages;
+ USER_REFERENCE_ENTRY Ref;
+ USHORT HitTest;
+
+ /* The queues and order in which they are checked are documented in the MSDN
+ article on GetMessage() */
+
+ pti = PsGetCurrentThreadWin32Thread();
+ ThreadQueue = pti->MessageQueue;
+
+ /* Inspect RemoveMsg flags */
+ /* Note:
+ The only flag we process is PM_REMOVE.
+ Processing (High word) PM_QS_Xx Is needed. This and MsgFilterXxx can result
+ with QS_Xx flags to be used to isolate which message check to test for.
+ ATM, we look at all messages and the filters are sent to co_MsqFindMessage
+ and there, it is cross checked.
+ Example: Wine server/queue.c is_keyboard_msg, check_msg_filter and
+ filter_contains_hw_range.
+ */
+ RemoveMessages = RemoveMsg & PM_REMOVE;
+
+/*
+ If no filter is specified, messages are processed in the following order:
+
+ * Sent messages
+ * Posted messages
+ * Input (hardware) messages and system internal events
+ * Sent messages (again)
+ * WM_PAINT messages
+ * WM_TIMER messages
+ */
+CheckMessages:
+
+ HitTest = HTNOWHERE;
+
+ Present = FALSE;
+
+ KeQueryTickCount(&LargeTickCount);
+ ThreadQueue->LastMsgRead = LargeTickCount.u.LowPart;
+
+ /* Dispatch sent messages here. */
+ while (co_MsqDispatchOneSentMessage(ThreadQueue))
+ ;
+
+ /* Now look for a quit message. */
+
+ if (ThreadQueue->QuitPosted)
+ {
+ /* According to the PSDK, WM_QUIT messages are always returned, regardless
+ of the filter specified */
+ Msg->Msg.hwnd = NULL;
+ Msg->Msg.message = WM_QUIT;
+ Msg->Msg.wParam = ThreadQueue->QuitExitCode;
+ Msg->Msg.lParam = 0;
+ Msg->FreeLParam = FALSE;
+ if (RemoveMessages)
+ {
+ ThreadQueue->QuitPosted = FALSE;
+ }
+ goto MsgExit;
+ }
+
+ /* Now check for normal messages. */
+ Present = co_MsqFindMessage( ThreadQueue,
+ FALSE,
+ RemoveMessages,
+ Window,
+ MsgFilterMin,
+ MsgFilterMax,
+ &Message );
+ if (Present)
+ {
+ RtlCopyMemory(Msg, Message, sizeof(USER_MESSAGE));
+ if (RemoveMessages)
+ {
+ MsqDestroyMessage(Message);
+ }
+ goto MessageFound;
+ }
+
+ /* Check for hardware events. */
+ Present = co_MsqFindMessage( ThreadQueue,
+ TRUE,
+ RemoveMessages,
+ Window,
+ MsgFilterMin,
+ MsgFilterMax,
+ &Message );
+ if (Present)
+ {
+ RtlCopyMemory(Msg, Message, sizeof(USER_MESSAGE));
+ if (RemoveMessages)
+ {
+ MsqDestroyMessage(Message);
+ }
+ goto MessageFound;
+ }
+
+ /* Check for sent messages again. */
+ while (co_MsqDispatchOneSentMessage(ThreadQueue))
+ ;
+
+ /* Check for paint messages. */
+ if ( IntGetPaintMessage( Window,
+ MsgFilterMin,
+ MsgFilterMax,
+ pti,
+ &Msg->Msg,
+ RemoveMessages))
+ {
+ Msg->FreeLParam = FALSE;
+ goto MsgExit;
+ }
+
+ if (PostTimerMessages(Window))
+ goto CheckMessages;
+
+ if(Present)
+ {
+MessageFound:
+
+ if(RemoveMessages)
+ {
+ PWINDOW_OBJECT MsgWindow = NULL;
+
+ /* Mouse message process */
+
+ if( Msg->Msg.hwnd &&
+ ( MsgWindow = UserGetWindowObject(Msg->Msg.hwnd) ) &&
+ Msg->Msg.message >= WM_MOUSEFIRST &&
+ Msg->Msg.message <= WM_MOUSELAST )
+ {
+ USHORT HitTest;
+
+ UserRefObjectCo(MsgWindow, &Ref);
+
+ if ( co_IntTranslateMouseMessage( ThreadQueue,
+ &Msg->Msg,
+ &HitTest,
+ TRUE))
+ /* FIXME - check message filter again, if the message doesn't match anymore,
+ search again */
+ {
+ UserDerefObjectCo(MsgWindow);
+ /* eat the message, search again */
+ goto CheckMessages;
+ }
+
+ if(ThreadQueue->CaptureWindow == NULL)
+ {
+ co_IntSendHitTestMessages(ThreadQueue, &Msg->Msg);
+
+ if ( ( Msg->Msg.message != WM_MOUSEMOVE &&
+ Msg->Msg.message != WM_NCMOUSEMOVE ) &&
+ IS_BTN_MESSAGE(Msg->Msg.message, DOWN) &&
+ co_IntActivateWindowMouse(ThreadQueue, &Msg->Msg, MsgWindow, &HitTest) )
+ {
+ UserDerefObjectCo(MsgWindow);
+ /* eat the message, search again */
+ goto CheckMessages;
+ }
+ }
+
+ UserDerefObjectCo(MsgWindow);
+ }
+ else
+ {
+ co_IntSendHitTestMessages(ThreadQueue, &Msg->Msg);
+ }
+
+// if(MsgWindow)
+// {
+// UserDereferenceObject(MsgWindow);
+// }
+
+ goto MsgExit;
+ }
+
+ if ( ( Msg->Msg.hwnd &&
+ Msg->Msg.message >= WM_MOUSEFIRST &&
+ Msg->Msg.message <= WM_MOUSELAST ) &&
+ co_IntTranslateMouseMessage( ThreadQueue,
+ &Msg->Msg,
+ &HitTest,
+ FALSE) )
+ /* FIXME - check message filter again, if the message doesn't match anymore,
+ search again */
+ {
+ /* eat the message, search again */
+ goto CheckMessages;
+ }
+
+MsgExit:
+ if ( ISITHOOKED(WH_MOUSE) && IS_MOUSE_MESSAGE(Msg->Msg.message))
+ {
+ if(!ProcessMouseMessage(&Msg->Msg, HitTest, RemoveMsg))
+ {
+ return FALSE;
+ }
+ }
+
+ if ( ISITHOOKED(WH_KEYBOARD) && IS_KBD_MESSAGE(Msg->Msg.message))
+ {
+ if(!ProcessKeyboardMessage(&Msg->Msg, RemoveMsg))
+ {
+ return FALSE;
+ }
+ }
+ // The WH_GETMESSAGE hook enables an application to monitor messages about to
+ // be returned by the GetMessage or PeekMessage function.
+ if (ISITHOOKED(WH_GETMESSAGE))
+ {
+ //DPRINT1("Peek WH_GETMESSAGE -> %x\n",&Msg);
+ co_HOOK_CallHooks( WH_GETMESSAGE, HC_ACTION, RemoveMsg & PM_REMOVE, (LPARAM)&Msg->Msg);
+ }
+ return TRUE;
+ }
+
+ return Present;
+}
+
+static NTSTATUS FASTCALL
+CopyMsgToKernelMem(MSG *KernelModeMsg, MSG *UserModeMsg, PMSGMEMORY MsgMemoryEntry)
+{
+ NTSTATUS Status;
+
+ PVOID KernelMem;
+ UINT Size;
+
+ *KernelModeMsg = *UserModeMsg;
+
+ /* See if this message type is present in the table */
+ if (NULL == MsgMemoryEntry)
+ {
+ /* Not present, no copying needed */
+ return STATUS_SUCCESS;
+ }
+
+ /* Determine required size */
+ Size = MsgMemorySize(MsgMemoryEntry, UserModeMsg->wParam, UserModeMsg->lParam);
+
+ if (0 != Size)
+ {
+ /* Allocate kernel mem */
+ KernelMem = ExAllocatePoolWithTag(PagedPool, Size, TAG_MSG);
+ if (NULL == KernelMem)
+ {
+ DPRINT1("Not enough memory to copy message to kernel mem\n");
+ return STATUS_NO_MEMORY;
+ }
+ KernelModeMsg->lParam = (LPARAM) KernelMem;
+
+ /* Copy data if required */
+ if (0 != (MsgMemoryEntry->Flags & MMS_FLAG_READ))
+ {
+ Status = MmCopyFromCaller(KernelMem, (PVOID) UserModeMsg->lParam, Size);
+ if (! NT_SUCCESS(Status))
+ {
+ DPRINT1("Failed to copy message to kernel: invalid usermode buffer\n");
+ ExFreePoolWithTag(KernelMem, TAG_MSG);
+ return Status;
+ }
+ }
+ else
+ {
+ /* Make sure we don't pass any secrets to usermode */
+ RtlZeroMemory(KernelMem, Size);
+ }
+ }
+ else
+ {
+ KernelModeMsg->lParam = 0;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS FASTCALL
+CopyMsgToUserMem(MSG *UserModeMsg, MSG *KernelModeMsg)
+{
+ NTSTATUS Status;
+ PMSGMEMORY MsgMemoryEntry;
+ UINT Size;
+
+ /* See if this message type is present in the table */
+ MsgMemoryEntry = FindMsgMemory(UserModeMsg->message);
+ if (NULL == MsgMemoryEntry)
+ {
+ /* Not present, no copying needed */
+ return STATUS_SUCCESS;
+ }
+
+ /* Determine required size */
+ Size = MsgMemorySize(MsgMemoryEntry, UserModeMsg->wParam, UserModeMsg->lParam);
+
+ if (0 != Size)
+ {
+ /* Copy data if required */
+ if (0 != (MsgMemoryEntry->Flags & MMS_FLAG_WRITE))
+ {
+ Status = MmCopyToCaller((PVOID) UserModeMsg->lParam, (PVOID) KernelModeMsg->lParam, Size);
+ if (! NT_SUCCESS(Status))
+ {
+ DPRINT1("Failed to copy message from kernel: invalid usermode buffer\n");
+ ExFreePool((PVOID) KernelModeMsg->lParam);
+ return Status;
+ }
+ }
+
+ ExFreePool((PVOID) KernelModeMsg->lParam);
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static BOOL FASTCALL
+co_IntWaitMessage( PWINDOW_OBJECT Window,
+ UINT MsgFilterMin,
+ UINT MsgFilterMax )
+{
+ PTHREADINFO pti;
+ PUSER_MESSAGE_QUEUE ThreadQueue;
+ NTSTATUS Status = STATUS_SUCCESS;
+ USER_MESSAGE Msg;
+
+ pti = PsGetCurrentThreadWin32Thread();
+ ThreadQueue = pti->MessageQueue;
+
+ do
+ {
+ if ( co_IntPeekMessage( &Msg,
+ Window,
+ MsgFilterMin,
+ MsgFilterMax,
+ PM_NOREMOVE))
+ {
+ return TRUE;
+ }
+ /* Nothing found. Wait for new messages. */
+ Status = co_MsqWaitForNewMessages( ThreadQueue,
+ Window,
+ MsgFilterMin,
+ MsgFilterMax);
+ }
+ while ( (STATUS_WAIT_0 <= Status && Status <= STATUS_WAIT_63) ||
+ STATUS_TIMEOUT == Status );
+
+ if (!NT_SUCCESS(Status))
+ {
+ SetLastNtError(Status);
+ DPRINT1("Exit co_IntWaitMessage on error!\n");
+ }
+
+ return FALSE;
+}
+
+BOOL FASTCALL
+co_IntGetPeekMessage( PMSG pMsg,
+ HWND hWnd,
+ UINT MsgFilterMin,
+ UINT MsgFilterMax,
+ UINT RemoveMsg,
+ BOOL bGMSG )
+{
+ BOOL Present;
+ PWINDOW_OBJECT Window;
+ USER_MESSAGE Msg;
+
+ if ( hWnd == HWND_TOPMOST ||
+ hWnd == HWND_BROADCAST )
+ hWnd = HWND_BOTTOM;
+
+ /* Validate input */
+ if (hWnd && hWnd != HWND_BOTTOM)
+ {
+ if (!(Window = UserGetWindowObject(hWnd)))
+ {
+ if (bGMSG)
+ return -1;
+ else
+ return FALSE;
+ }
+ }
+ else
+ {
+ Window = (PWINDOW_OBJECT)hWnd;
+ }
+
+ if (MsgFilterMax < MsgFilterMin)
+ {
+ MsgFilterMin = 0;
+ MsgFilterMax = 0;
+ }
+
+ do
+ {
+ Present = co_IntPeekMessage( &Msg,
+ Window,
+ MsgFilterMin,
+ MsgFilterMax,
+ RemoveMsg );
+ if (Present)
+ {
+ RtlCopyMemory( pMsg, &Msg.Msg, sizeof(MSG));
+
+ if (bGMSG)
+ return (WM_QUIT != pMsg->message);
+ else
+ return TRUE;
+ }
+
+ if ( bGMSG && !co_IntWaitMessage(Window, MsgFilterMin, MsgFilterMax) )
+ {
+ return -1;
+ }
+ else
+ {
+ if (!(RemoveMsg & PM_NOYIELD))
+ {
+ // Yield this thread!
+ UserLeave();
+ ZwYieldExecution();
+ UserEnterExclusive();
+ // Fall through to fail.
+ }
+ }
+ }
+ while( bGMSG && !Present );
+
+ return FALSE;
+}
+
+BOOL FASTCALL
+UserPostThreadMessage( DWORD idThread,
+ UINT Msg,
+ WPARAM wParam,
+ LPARAM lParam )
+{
+ MSG Message;
+ PETHREAD peThread;
+ PTHREADINFO pThread;
+ LARGE_INTEGER LargeTickCount;
+ NTSTATUS Status;
+
+ DPRINT1("UserPostThreadMessage wParam 0x%x lParam 0x%x\n", wParam,lParam);
+
+ if (FindMsgMemory(Msg) != 0)
+ {
+ SetLastWin32Error(ERROR_MESSAGE_SYNC_ONLY );
+ return FALSE;
+ }
+
+ Status = PsLookupThreadByThreadId((HANDLE)idThread,&peThread);
+
+ if( Status == STATUS_SUCCESS )
+ {
+ pThread = (PTHREADINFO)peThread->Tcb.Win32Thread;
+ if( !pThread ||
+ !pThread->MessageQueue ||
+ (pThread->TIF_flags & TIF_INCLEANUP))
+ {
+ ObDereferenceObject( peThread );
+ return FALSE;
+ }
+
+ Message.hwnd = NULL;
+ Message.message = Msg;
+ Message.wParam = wParam;
+ Message.lParam = lParam;
+ Message.pt = gpsi->ptCursor;
+
+ KeQueryTickCount(&LargeTickCount);
+ pThread->timeLast = Message.time = MsqCalculateMessageTime(&LargeTickCount);
+ MsqPostMessage(pThread->MessageQueue, &Message, FALSE, QS_POSTMESSAGE);
+ ObDereferenceObject( peThread );
+ return TRUE;
+ }
+ else
+ {
+ SetLastNtError( Status );
+ }
+ return FALSE;
+}
+
+BOOL FASTCALL
+UserPostMessage( HWND Wnd,
+ UINT Msg,
+ WPARAM wParam,
+ LPARAM lParam )
+{
+ PTHREADINFO pti;
+ MSG Message;
+ LARGE_INTEGER LargeTickCount;
+
+ if (FindMsgMemory(Msg) != 0)
+ {
+ SetLastWin32Error(ERROR_MESSAGE_SYNC_ONLY );
+ return FALSE;
+ }
+
+ if (!Wnd)
+ return UserPostThreadMessage( PtrToInt(PsGetCurrentThreadId()),
+ Msg,
+ wParam,
+ lParam);
+
+ if (Wnd == HWND_BROADCAST)
+ {
+ HWND *List;
+ PWINDOW_OBJECT DesktopWindow;
+ ULONG i;
+
+ DesktopWindow = UserGetWindowObject(IntGetDesktopWindow());
+ List = IntWinListChildren(DesktopWindow);
+
+ if (List != NULL)
+ {
+ UserPostMessage(DesktopWindow->hSelf, Msg, wParam, lParam);
+ for (i = 0; List[i]; i++)
+ UserPostMessage(List[i], Msg, wParam, lParam);
+ ExFreePool(List);
+ }
+ }
+ else
+ {
+ PWINDOW_OBJECT Window;
+
+ Window = UserGetWindowObject(Wnd);
+ if ( !Window || !Window->Wnd )
+ {
+ return FALSE;
+ }
+
+ pti = Window->Wnd->head.pti;
+ if ( pti->TIF_flags & TIF_INCLEANUP )
+ {
+ DPRINT1("Attempted to post message to window 0x%x when the thread is in cleanup!\n", Wnd);
+ return FALSE;
+ }
+
+ if ( Window->state & WINDOWSTATUS_DESTROYING )
+ {
+ DPRINT1("Attempted to post message to window 0x%x that is being destroyed!\n", Wnd);
+ /* FIXME - last error code? */
+ return FALSE;
+ }
+
+ if (WM_QUIT == Msg)
+ {
+ MsqPostQuitMessage(Window->pti->MessageQueue, wParam);
+ }
+ else
+ {
+ Message.hwnd = Wnd;
+ Message.message = Msg;
+ Message.wParam = wParam;
+ Message.lParam = lParam;
+ Message.pt = gpsi->ptCursor;
+ KeQueryTickCount(&LargeTickCount);
+ pti->timeLast = Message.time = MsqCalculateMessageTime(&LargeTickCount);
+ MsqPostMessage(Window->pti->MessageQueue, &Message, FALSE, QS_POSTMESSAGE);
+ }
+ }
+ return TRUE;
+}
+
+
+LRESULT FASTCALL
+co_IntSendMessage( HWND hWnd,
+ UINT Msg,
+ WPARAM wParam,
+ LPARAM lParam )
+{
+ ULONG_PTR Result = 0;
+ if(co_IntSendMessageTimeout(hWnd, Msg, wParam, lParam, SMTO_NORMAL, 0, &Result))
+ {
+ return (LRESULT)Result;
+ }
+ return 0;
+}
+
+static
+LRESULT FASTCALL
+co_IntSendMessageTimeoutSingle( HWND hWnd,
+ UINT Msg,
+ WPARAM wParam,
+ LPARAM lParam,
+ UINT uFlags,
+ UINT uTimeout,
+ ULONG_PTR *uResult )
+{
+ ULONG_PTR Result;
+ NTSTATUS Status;
+ PWINDOW_OBJECT Window = NULL;
+ PMSGMEMORY MsgMemoryEntry;
+ INT lParamBufferSize;
+ LPARAM lParamPacked;
+ PTHREADINFO Win32Thread;
+ DECLARE_RETURN(LRESULT);
+ USER_REFERENCE_ENTRY Ref;
+
+ if (!(Window = UserGetWindowObject(hWnd)))
+ {
+ RETURN( FALSE);
+ }
+
+ UserRefObjectCo(Window, &Ref);
+
+ Win32Thread = PsGetCurrentThreadWin32Thread();
+
+ IntCallWndProc( Window, hWnd, Msg, wParam, lParam);
+
+ if ( NULL != Win32Thread &&
+ Window->pti->MessageQueue == Win32Thread->MessageQueue)
+ {
+ if (Win32Thread->TIF_flags & TIF_INCLEANUP)
+ {
+ /* Never send messages to exiting threads */
+ RETURN( FALSE);
+ }
+
+ /* See if this message type is present in the table */
+ MsgMemoryEntry = FindMsgMemory(Msg);
+ if (NULL == MsgMemoryEntry)
+ {
+ lParamBufferSize = -1;
+ }
+ else
+ {
+ lParamBufferSize = MsgMemorySize(MsgMemoryEntry, wParam, lParam);
+ }
+
+ if (! NT_SUCCESS(PackParam(&lParamPacked, Msg, wParam, lParam, FALSE)))
+ {
+ DPRINT1("Failed to pack message parameters\n");
+ RETURN( FALSE);
+ }
+
+ Result = (ULONG_PTR)co_IntCallWindowProc( Window->Wnd->lpfnWndProc,
+ !Window->Wnd->Unicode,
+ hWnd,
+ Msg,
+ wParam,
+ lParamPacked,
+ lParamBufferSize );
+ if(uResult)
+ {
+ *uResult = Result;
+ }
+
+ IntCallWndProcRet( Window, hWnd, Msg, wParam, lParam, (LRESULT *)uResult);
+
+ if (! NT_SUCCESS(UnpackParam(lParamPacked, Msg, wParam, lParam, FALSE)))
+ {
+ DPRINT1("Failed to unpack message parameters\n");
+ RETURN( TRUE);
+ }
+
+ RETURN( TRUE);
+ }
+
+ if (uFlags & SMTO_ABORTIFHUNG && MsqIsHung(Window->pti->MessageQueue))
+ {
+ /* FIXME - Set a LastError? */
+ RETURN( FALSE);
+ }
+
+ if (Window->state & WINDOWSTATUS_DESTROYING)
+ {
+ /* FIXME - last error? */
+ DPRINT1("Attempted to send message to window 0x%x that is being destroyed!\n", hWnd);
+ RETURN( FALSE);
+ }
+
+ do
+ {
+ Status = co_MsqSendMessage( Window->pti->MessageQueue,
+ hWnd,
+ Msg,
+ wParam,
+ lParam,
+ uTimeout,
+ (uFlags & SMTO_BLOCK),
+ MSQ_NORMAL,
+ uResult );
+ }
+ while ((STATUS_TIMEOUT == Status) &&
+ (uFlags & SMTO_NOTIMEOUTIFNOTHUNG) &&
+ !MsqIsHung(Window->pti->MessageQueue));
+
+ IntCallWndProcRet( Window, hWnd, Msg, wParam, lParam, (LRESULT *)uResult);
+
+ if (STATUS_TIMEOUT == Status)
+ {
+/*
+ MSDN says:
+ Microsoft Windows 2000: If GetLastError returns zero, then the function
+ timed out.
+ XP+ : If the function fails or times out, the return value is zero.
+ To get extended error information, call GetLastError. If GetLastError
+ returns ERROR_TIMEOUT, then the function timed out.
+ */
+ SetLastWin32Error(ERROR_TIMEOUT);
+ RETURN( FALSE);
+ }
+ else if (! NT_SUCCESS(Status))
+ {
+ SetLastNtError(Status);
+ RETURN( FALSE);
+ }
+
+ RETURN( TRUE);
+
+CLEANUP:
+ if (Window) UserDerefObjectCo(Window);
+ END_CLEANUP;
+}
+
+LRESULT FASTCALL
+co_IntSendMessageTimeout( HWND hWnd,
+ UINT Msg,
+ WPARAM wParam,
+ LPARAM lParam,
+ UINT uFlags,
+ UINT uTimeout,
+ ULONG_PTR *uResult )
+{
+ PWINDOW_OBJECT DesktopWindow;
+ HWND *Children;
+ HWND *Child;
+
+ if (HWND_BROADCAST != hWnd)
+ {
+ return co_IntSendMessageTimeoutSingle(hWnd, Msg, wParam, lParam, uFlags, uTimeout, uResult);
+ }
+
+ DesktopWindow = UserGetWindowObject(IntGetDesktopWindow());
+ if (NULL == DesktopWindow)
+ {
+ SetLastWin32Error(ERROR_INTERNAL_ERROR);
+ return 0;
+ }
+
+ /* Send message to the desktop window too! */
+ co_IntSendMessageTimeoutSingle(DesktopWindow->hSelf, Msg, wParam, lParam, uFlags, uTimeout, uResult);
+
+ Children = IntWinListChildren(DesktopWindow);
+ if (NULL == Children)
+ {
+ return 0;
+ }
+
+ for (Child = Children; NULL != *Child; Child++)
+ {
+ co_IntSendMessageTimeoutSingle(*Child, Msg, wParam, lParam, uFlags, uTimeout, uResult);
+ }
+
+ ExFreePool(Children);
+
+ return (LRESULT) TRUE;
+}
+
+LRESULT FASTCALL co_IntSendMessageNoWait(HWND hWnd,
+ UINT Msg,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ ULONG_PTR Result = 0;
+ co_IntSendMessageWithCallBack(hWnd,
+ Msg,
+ wParam,
+ lParam,
+ NULL,
+ 0,
+ &Result);
+ return Result;
+}
+
+LRESULT FASTCALL
+co_IntSendMessageWithCallBack( HWND hWnd,
+ UINT Msg,
+ WPARAM wParam,
+ LPARAM lParam,
+ SENDASYNCPROC CompletionCallback,
+ ULONG_PTR CompletionCallbackContext,
+ ULONG_PTR *uResult)
+{
+ ULONG_PTR Result;
+ PWINDOW_OBJECT Window = NULL;
+ PMSGMEMORY MsgMemoryEntry;
+ INT lParamBufferSize;
+ LPARAM lParamPacked;
+ PTHREADINFO Win32Thread;
+ DECLARE_RETURN(LRESULT);
+ USER_REFERENCE_ENTRY Ref;
+ PUSER_SENT_MESSAGE Message;
+
+ if (!(Window = UserGetWindowObject(hWnd)))
+ {
+ RETURN(FALSE);
+ }
+
+ UserRefObjectCo(Window, &Ref);
+
+ if (Window->state & WINDOWSTATUS_DESTROYING)
+ {
+ /* FIXME - last error? */
+ DPRINT1("Attempted to send message to window 0x%x that is being destroyed!\n", hWnd);
+ RETURN(FALSE);
+ }
+
+ Win32Thread = PsGetCurrentThreadWin32Thread();
+
+ IntCallWndProc( Window, hWnd, Msg, wParam, lParam);
+
+ if (Win32Thread == NULL)
+ {
+ ASSERT(FALSE);
+ RETURN(FALSE);
+ }
+
+ if (Win32Thread->TIF_flags & TIF_INCLEANUP)
+ {
+ /* Never send messages to exiting threads */
+ RETURN(FALSE);
+ }
+
+ /* See if this message type is present in the table */
+ MsgMemoryEntry = FindMsgMemory(Msg);
+ if (NULL == MsgMemoryEntry)
+ {
+ lParamBufferSize = -1;
+ }
+ else
+ {
+ lParamBufferSize = MsgMemorySize(MsgMemoryEntry, wParam, lParam);
+ }
+
+ if (! NT_SUCCESS(PackParam(&lParamPacked, Msg, wParam, lParam, Window->pti->MessageQueue != Win32Thread->MessageQueue)))
+ {
+ DPRINT1("Failed to pack message parameters\n");
+ RETURN( FALSE);
+ }
+
+ /* If this is not a callback and it can be sent now, then send it. */
+ if ((Window->pti->MessageQueue == Win32Thread->MessageQueue) && (CompletionCallback == NULL))
+ {
+
+ Result = (ULONG_PTR)co_IntCallWindowProc( Window->Wnd->lpfnWndProc,
+ !Window->Wnd->Unicode,
+ hWnd,
+ Msg,
+ wParam,
+ lParamPacked,
+ lParamBufferSize );
+ if(uResult)
+ {
+ *uResult = Result;
+ }
+ }
+
+ IntCallWndProcRet( Window, hWnd, Msg, wParam, lParam, (LRESULT *)uResult);
+
+ if ((Window->pti->MessageQueue == Win32Thread->MessageQueue) && (CompletionCallback == NULL))
+ {
+ if (! NT_SUCCESS(UnpackParam(lParamPacked, Msg, wParam, lParam, FALSE)))
+ {
+ DPRINT1("Failed to unpack message parameters\n");
+ }
+ RETURN(TRUE);
+ }
+
+ if(!(Message = ExAllocatePoolWithTag(NonPagedPool, sizeof(USER_SENT_MESSAGE), TAG_USRMSG)))
+ {
+ DPRINT1("MsqSendMessage(): Not enough memory to allocate a message");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ Message->Msg.hwnd = hWnd;
+ Message->Msg.message = Msg;
+ Message->Msg.wParam = wParam;
+ Message->Msg.lParam = lParamPacked;
+ Message->CompletionEvent = NULL;
+ Message->Result = 0;
+ Message->SenderQueue = NULL; //Win32Thread->MessageQueue;
+
+ IntReferenceMessageQueue(Window->pti->MessageQueue);
+ Message->CompletionCallback = CompletionCallback;
+ Message->CompletionCallbackContext = CompletionCallbackContext;
+ Message->HookMessage = MSQ_NORMAL | MSQ_SENTNOWAIT;
+ Message->HasPackedLParam = (lParamBufferSize > 0);
+
+ InsertTailList(&Window->pti->MessageQueue->SentMessagesListHead, &Message->ListEntry);
+ IntDereferenceMessageQueue(Window->pti->MessageQueue);
+
+ RETURN(TRUE);
+
+CLEANUP:
+ if (Window) UserDerefObjectCo(Window);
+ END_CLEANUP;
+}
+
+/* This function posts a message if the destination's message queue belongs to
+ another thread, otherwise it sends the message. It does not support broadcast
+ messages! */
+LRESULT FASTCALL
+co_IntPostOrSendMessage( HWND hWnd,
+ UINT Msg,
+ WPARAM wParam,
+ LPARAM lParam )
+{
+ ULONG_PTR Result;
+ PTHREADINFO pti;
+ PWINDOW_OBJECT Window;
+
+ if ( hWnd == HWND_BROADCAST )
+ {
+ return 0;
+ }
+
+ if(!(Window = UserGetWindowObject(hWnd)))
+ {
+ return 0;
+ }
+
+ pti = PsGetCurrentThreadWin32Thread();
+
+ if ( Window->pti->MessageQueue != pti->MessageQueue &&
+ FindMsgMemory(Msg) == 0 )
+ {
+ Result = UserPostMessage(hWnd, Msg, wParam, lParam);
+ }
+ else
+ {
+ if ( !co_IntSendMessageTimeoutSingle(hWnd, Msg, wParam, lParam, SMTO_NORMAL, 0, &Result) )
+ {
+ Result = 0;
+ }
+ }
+
+ return (LRESULT)Result;
+}
+
+LRESULT FASTCALL
+co_IntDoSendMessage( HWND hWnd,
+ UINT Msg,
+ WPARAM wParam,
+ LPARAM lParam,
+ PDOSENDMESSAGE dsm,
+ PNTUSERSENDMESSAGEINFO UnsafeInfo )
+{
+ PTHREADINFO pti;
+ LRESULT Result = TRUE;
+ NTSTATUS Status;
+ PWINDOW_OBJECT Window = NULL;
+ NTUSERSENDMESSAGEINFO Info;
+ MSG UserModeMsg;
+ MSG KernelModeMsg;
+ PMSGMEMORY MsgMemoryEntry;
+
+ RtlZeroMemory(&Info, sizeof(NTUSERSENDMESSAGEINFO));
+
+ /* FIXME: Call hooks. */
+ if (HWND_BROADCAST != hWnd)
+ {
+ Window = UserGetWindowObject(hWnd);
+ if ( !Window || !Window->Wnd )
+ {
+ /* Tell usermode to not touch this one */
+ Info.HandledByKernel = TRUE;
+ MmCopyToCaller(UnsafeInfo, &Info, sizeof(NTUSERSENDMESSAGEINFO));
+ return 0;
+ }
+ }
+
+ /* Check for an exiting window. */
+ if (Window && Window->state & WINDOWSTATUS_DESTROYING)
+ {
+ DPRINT1("co_IntDoSendMessage Window Exiting!\n");
+ }
+
+ /* See if the current thread can handle the message */
+ pti = PsGetCurrentThreadWin32Thread();
+
+ // This is checked in user mode!!!!!!!
+ if ( HWND_BROADCAST != hWnd &&
+ NULL != pti &&
+ Window->pti->MessageQueue == pti->MessageQueue &&
+ !ISITHOOKED(WH_CALLWNDPROC) &&
+ !ISITHOOKED(WH_CALLWNDPROCRET) &&
+ ( Msg < WM_DDE_FIRST || Msg > WM_DDE_LAST ) )
+ {
+ /* Gather the information usermode needs to call the window proc directly */
+ Info.HandledByKernel = FALSE;
+
+ Status = MmCopyFromCaller(&(Info.Ansi), &(UnsafeInfo->Ansi),
+ sizeof(BOOL));
+ if (! NT_SUCCESS(Status))
+ {
+ Info.Ansi = ! Window->Wnd->Unicode;
+ }
+
+ Info.Ansi = !Window->Wnd->Unicode;
+ Info.Proc = Window->Wnd->lpfnWndProc;
+ }
+ else
+ {
+ /* Must be handled by other thread */
+// if (HWND_BROADCAST != hWnd)
+// {
+// UserDereferenceObject(Window);
+// }
+ Info.HandledByKernel = TRUE;
+ UserModeMsg.hwnd = hWnd;
+ UserModeMsg.message = Msg;
+ UserModeMsg.wParam = wParam;
+ UserModeMsg.lParam = lParam;
+ MsgMemoryEntry = FindMsgMemory(UserModeMsg.message);
+
+ Status = CopyMsgToKernelMem(&KernelModeMsg, &UserModeMsg, MsgMemoryEntry);
+ if (! NT_SUCCESS(Status))
+ {
+ MmCopyToCaller(UnsafeInfo, &Info, sizeof(NTUSERSENDMESSAGEINFO));
+ SetLastWin32Error(ERROR_INVALID_PARAMETER);
+ return (dsm ? 0 : -1);
+ }
+
+ if(!dsm)
+ {
+ Result = co_IntSendMessage( KernelModeMsg.hwnd,
+ KernelModeMsg.message,
+ KernelModeMsg.wParam,
+ KernelModeMsg.lParam );
+ }
+ else
+ {
+ Result = co_IntSendMessageTimeout( KernelModeMsg.hwnd,
+ KernelModeMsg.message,
+ KernelModeMsg.wParam,
+ KernelModeMsg.lParam,
+ dsm->uFlags,
+ dsm->uTimeout,
+ &dsm->Result );
+ }
+
+ Status = CopyMsgToUserMem(&UserModeMsg, &KernelModeMsg);
+ if (! NT_SUCCESS(Status))
+ {
+ MmCopyToCaller(UnsafeInfo, &Info, sizeof(NTUSERSENDMESSAGEINFO));
+ SetLastWin32Error(ERROR_INVALID_PARAMETER);
+ return(dsm ? 0 : -1);
+ }
+ }
+
+ Status = MmCopyToCaller(UnsafeInfo, &Info, sizeof(NTUSERSENDMESSAGEINFO));
+ if (! NT_SUCCESS(Status))
+ {
+ SetLastWin32Error(ERROR_INVALID_PARAMETER);
+ }
+
+ return (LRESULT)Result;
+}
+
+
+BOOL FASTCALL
+UserSendNotifyMessage( HWND hWnd,
+ UINT Msg,
+ WPARAM wParam,
+ LPARAM lParam )
+{
+ BOOL Result = TRUE;
+
+ if (FindMsgMemory(Msg) != 0)
+ {
+ SetLastWin32Error(ERROR_MESSAGE_SYNC_ONLY );
+ return FALSE;
+ }
+
+ // Basicly the same as IntPostOrSendMessage
+ if (hWnd == HWND_BROADCAST) //Handle Broadcast
+ {
+ HWND *List;
+ PWINDOW_OBJECT DesktopWindow;
+ ULONG i;
+
+ DesktopWindow = UserGetWindowObject(IntGetDesktopWindow());
+ List = IntWinListChildren(DesktopWindow);
+
+ if (List != NULL)
+ {
+ UserSendNotifyMessage(DesktopWindow->hSelf, Msg, wParam, lParam);
+ for (i = 0; List[i]; i++)
+ {
+ UserSendNotifyMessage(List[i], Msg, wParam, lParam);
+ }
+ ExFreePool(List);
+ }
+ }
+ else
+ {
+ ULONG_PTR PResult;
+ PTHREADINFO pti;
+ PWINDOW_OBJECT Window;
- Message.hwnd = hWnd;
- Message.message = Msg;
- Message.wParam = wParam;
- Message.lParam = lParam;
-
+
+ if ( !(Window = UserGetWindowObject(hWnd)) ) return FALSE;
+
+ pti = PsGetCurrentThreadWin32Thread();
+
+ if (Window->pti->MessageQueue != pti->MessageQueue)
+ { // Send message w/o waiting for it.
+ Result = UserPostMessage(hWnd, Msg, wParam, lParam);
+ }
+ else
+ { // Handle message and callback.
+ Result = co_IntSendMessageTimeoutSingle( hWnd,
+ Msg,
+ wParam,
+ lParam,
+ SMTO_NORMAL,
+ 0,
+ &PResult );
+ }
+ }
+ return Result;
+}
+
+
+DWORD APIENTRY
+IntGetQueueStatus(BOOL ClearChanges)
+{
+ PTHREADINFO pti;
+ PUSER_MESSAGE_QUEUE Queue;
+ DWORD Result;
+ DECLARE_RETURN(DWORD);
+
+ DPRINT("Enter IntGetQueueStatus\n");
+
+ pti = PsGetCurrentThreadWin32Thread();
+ Queue = pti->MessageQueue;
+
+ Result = MAKELONG(Queue->QueueBits, Queue->ChangedBits);
+ if (ClearChanges)
+ {
+ Queue->ChangedBits = 0;
+ }
+
+ RETURN(Result);
+
+CLEANUP:
+ DPRINT("Leave IntGetQueueStatus, ret=%i\n",_ret_);
+ END_CLEANUP;
+}
+
+BOOL APIENTRY
+IntInitMessagePumpHook()
+{
+ if (((PTHREADINFO)PsGetCurrentThread()->Tcb.Win32Thread)->pcti)
+ {
+ ((PTHREADINFO)PsGetCurrentThread()->Tcb.Win32Thread)->pcti->dwcPumpHook++;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL APIENTRY
+IntUninitMessagePumpHook()
+{
+ if (((PTHREADINFO)PsGetCurrentThread()->Tcb.Win32Thread)->pcti)
+ {
+ if (((PTHREADINFO)PsGetCurrentThread()->Tcb.Win32Thread)->pcti->dwcPumpHook <= 0)
+ {
+ return FALSE;
+ }
+ ((PTHREADINFO)PsGetCurrentThread()->Tcb.Win32Thread)->pcti->dwcPumpHook--;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/** Functions ******************************************************************/
+
+BOOL APIENTRY
+NtUserPostMessage(HWND hWnd,
+ UINT Msg,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ DECLARE_RETURN(BOOL);
+
+ DPRINT("Enter NtUserPostMessage\n");
+ UserEnterExclusive();
+
+ RETURN( UserPostMessage(hWnd, Msg, wParam, lParam));
+
+CLEANUP:
+ DPRINT("Leave NtUserPostMessage, ret=%i\n",_ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+BOOL APIENTRY
+NtUserPostThreadMessage(DWORD idThread,
+ UINT Msg,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ DECLARE_RETURN(BOOL);
+
+ DPRINT("Enter NtUserPostThreadMessage\n");
+ UserEnterExclusive();
+
+ RETURN( UserPostThreadMessage( idThread,
+ Msg,
+ wParam,
+ lParam));
+
+CLEANUP:
+ DPRINT("Leave NtUserPostThreadMessage, ret=%i\n",_ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+DWORD APIENTRY
+NtUserQuerySendMessage(DWORD Unknown0)
+{
+ UNIMPLEMENTED;
+
+ return 0;
+}
+
+
+////////// API on the way out!
+LRESULT APIENTRY
+NtUserSendMessageTimeout( HWND hWnd,
+ UINT Msg,
+ WPARAM wParam,
+ LPARAM lParam,
+ UINT uFlags,
+ UINT uTimeout,
+ ULONG_PTR *uResult,
+ PNTUSERSENDMESSAGEINFO UnsafeInfo )
+{
+ DOSENDMESSAGE dsm;
+ LRESULT Result;
+ DECLARE_RETURN(BOOL);
+
+ DPRINT("Enter NtUserSendMessageTimeout\n");
+ UserEnterExclusive();
+
+ dsm.uFlags = uFlags;
+ dsm.uTimeout = uTimeout;
+ Result = co_IntDoSendMessage(hWnd, Msg, wParam, lParam, &dsm, UnsafeInfo);
+ if(uResult != NULL && Result != 0)
+ {
+ NTSTATUS Status;
+
+ Status = MmCopyToCaller(uResult, &dsm.Result, sizeof(ULONG_PTR));
+ if(!NT_SUCCESS(Status))
+ {
+ SetLastWin32Error(ERROR_INVALID_PARAMETER);
+ RETURN( FALSE);
+ }
+ }
+ RETURN( Result);
+
+CLEANUP:
+ DPRINT("Leave NtUserSendMessageTimeout, ret=%i\n",_ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+LRESULT APIENTRY
+NtUserSendMessage( HWND Wnd,
+ UINT Msg,
+ WPARAM wParam,
+ LPARAM lParam,
+ PNTUSERSENDMESSAGEINFO UnsafeInfo )
+{
+ DECLARE_RETURN(BOOL);
+
+ DPRINT("Enter NtUserSendMessage\n");
+ UserEnterExclusive();
+
+ RETURN(co_IntDoSendMessage(Wnd, Msg, wParam, lParam, NULL, UnsafeInfo));
+
+CLEANUP:
+ DPRINT("Leave NtUserSendMessage, ret=%i\n",_ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+//////////
+
+BOOL APIENTRY
+NtUserWaitMessage(VOID)
+{
+ DECLARE_RETURN(BOOL);
+
+ DPRINT("EnterNtUserWaitMessage\n");
+ UserEnterExclusive();
+
+ RETURN(co_IntWaitMessage(NULL, 0, 0));
+
+CLEANUP:
+ DPRINT("Leave NtUserWaitMessage, ret=%i\n",_ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+
+BOOL APIENTRY
+NtUserGetMessage( PNTUSERGETMESSAGEINFO UnsafeInfo,
+ HWND hWnd,
+ UINT MsgFilterMin,
+ UINT MsgFilterMax )
+/*
+ * FUNCTION: Get a message from the calling thread's message queue.
+ * ARGUMENTS:
+ * UnsafeMsg - Pointer to the structure which receives the returned message.
+ * Wnd - Window whose messages are to be retrieved.
+ * MsgFilterMin - Integer value of the lowest message value to be
+ * retrieved.
+ * MsgFilterMax - Integer value of the highest message value to be
+ * retrieved.
+ */
+{
+ BOOL GotMessage;
+ NTUSERGETMESSAGEINFO Info;
+ NTSTATUS Status;
+ /* FIXME: if initialization is removed, gcc complains that this may be used before initialization. Please review */
+ PWINDOW_OBJECT Window = NULL;
+ PMSGMEMORY MsgMemoryEntry;
+ PVOID UserMem;
+ UINT Size;
+ USER_MESSAGE Msg;
+ DECLARE_RETURN(BOOL);
+// USER_REFERENCE_ENTRY Ref;
+
+ DPRINT("Enter NtUserGetMessage\n");
+ UserEnterExclusive();
+
+ /* Validate input */
+ if (hWnd && !(Window = UserGetWindowObject(hWnd)))
+ {
+ RETURN(-1);
+ }
+
+// if (Window) UserRefObjectCo(Window, &Ref);
+
+ if (MsgFilterMax < MsgFilterMin)
+ {
+ MsgFilterMin = 0;
+ MsgFilterMax = 0;
+ }
+
+ do
+ {
+ GotMessage = co_IntPeekMessage(&Msg, Window, MsgFilterMin, MsgFilterMax, PM_REMOVE);
+ if (GotMessage)
+ {
+ Info.Msg = Msg.Msg;
+ /* See if this message type is present in the table */
+ MsgMemoryEntry = FindMsgMemory(Info.Msg.message);
+ if (NULL == MsgMemoryEntry)
+ {
+ /* Not present, no copying needed */
+ Info.LParamSize = 0;
+ }
+ else
+ {
+ /* Determine required size */
+ Size = MsgMemorySize(MsgMemoryEntry, Info.Msg.wParam,
+ Info.Msg.lParam);
+ /* Allocate required amount of user-mode memory */
+ Info.LParamSize = Size;
+ UserMem = NULL;
+ Status = ZwAllocateVirtualMemory(NtCurrentProcess(), &UserMem, 0,
+ &Info.LParamSize, MEM_COMMIT, PAGE_READWRITE);
+
+ if (! NT_SUCCESS(Status))
+ {
+ SetLastNtError(Status);
+ RETURN( (BOOL) -1);
+ }
+ /* Transfer lParam data to user-mode mem */
+ Status = MmCopyToCaller(UserMem, (PVOID) Info.Msg.lParam, Size);
+ if (! NT_SUCCESS(Status))
+ {
+ ZwFreeVirtualMemory(NtCurrentProcess(), (PVOID *) &UserMem,
+ &Info.LParamSize, MEM_DECOMMIT);
+ SetLastNtError(Status);
+ RETURN( (BOOL) -1);
+ }
+ Info.Msg.lParam = (LPARAM) UserMem;
+ }
+ if (Msg.FreeLParam && 0 != Msg.Msg.lParam)
+ {
+ ExFreePool((void *) Msg.Msg.lParam);
+ }
+ Status = MmCopyToCaller(UnsafeInfo, &Info, sizeof(NTUSERGETMESSAGEINFO));
+ if (! NT_SUCCESS(Status))
+ {
+ SetLastNtError(Status);
+ RETURN( (BOOL) -1);
+ }
+ }
+ else if (! co_IntWaitMessage(Window, MsgFilterMin, MsgFilterMax))
+ {
+ RETURN( (BOOL) -1);
+ }
+ }
+ while (! GotMessage);
+
+ RETURN( WM_QUIT != Info.Msg.message);
+
+CLEANUP:
+// if (Window) UserDerefObjectCo(Window);
+
+ DPRINT("Leave NtUserGetMessage\n");
+ UserLeave();
+ END_CLEANUP;
+}
+
+
+BOOL
+APIENTRY
+NtUserGetMessageX(
+ PMSG pMsg,
+ HWND hWnd,
+ UINT MsgFilterMin,
+ UINT MsgFilterMax)
+{
+ MSG Msg;
+ BOOL Ret = FALSE;
+ DECLARE_RETURN(BOOL);
+
+ DPRINT("Enter NtUserGetMessage\n");
+ UserEnterExclusive();
+
+ if ( (MsgFilterMin|MsgFilterMax) & ~WM_MAXIMUM )
+ {
+ SetLastWin32Error(ERROR_INVALID_PARAMETER);
+ RETURN( Ret);
+ }
+
+ RtlZeroMemory(&Msg, sizeof(MSG));
+
+ Ret = co_IntGetPeekMessage(&Msg, hWnd, MsgFilterMin, MsgFilterMax, PM_REMOVE, TRUE);
+
+ if (Ret)
+ {
+ _SEH2_TRY
+ {
+ ProbeForWrite(pMsg, sizeof(MSG), 1);
+ RtlCopyMemory(pMsg, &Msg, sizeof(MSG));
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ SetLastNtError(_SEH2_GetExceptionCode());
+ Ret = FALSE;
+ }
+ _SEH2_END;
+ }
+ RETURN( Ret);
+
+CLEANUP:
+ DPRINT("Leave NtUserGetMessage\n");
+ UserLeave();
+ END_CLEANUP;
+}
+
+BOOL APIENTRY
+NtUserPeekMessage(PNTUSERGETMESSAGEINFO UnsafeInfo,
+ HWND hWnd,
+ UINT MsgFilterMin,
+ UINT MsgFilterMax,
+ UINT RemoveMsg)
+{
+ NTSTATUS Status;
+ BOOL Present;
+ NTUSERGETMESSAGEINFO Info;
+ PWINDOW_OBJECT Window;
+ PMSGMEMORY MsgMemoryEntry;
+ PVOID UserMem;
+ UINT Size;
+ USER_MESSAGE Msg;
+ DECLARE_RETURN(BOOL);
+
+ DPRINT("Enter NtUserPeekMessage\n");
+ UserEnterExclusive();
+
+ if (hWnd == (HWND)-1 || hWnd == (HWND)0x0000FFFF || hWnd == (HWND)0xFFFFFFFF)
+ hWnd = (HWND)1;
+
+ /* Validate input */
+ if (hWnd && hWnd != (HWND)1)
+ {
+ if (!(Window = UserGetWindowObject(hWnd)))
+ {
+ RETURN(-1);
+ }
+ }
+ else
+ {
+ Window = (PWINDOW_OBJECT)hWnd;
+ }
+
+ if (MsgFilterMax < MsgFilterMin)
+ {
+ MsgFilterMin = 0;
+ MsgFilterMax = 0;
+ }
+
+ Present = co_IntPeekMessage(&Msg, Window, MsgFilterMin, MsgFilterMax, RemoveMsg);
+ if (Present)
+ {
+
+ Info.Msg = Msg.Msg;
+ /* See if this message type is present in the table */
+ MsgMemoryEntry = FindMsgMemory(Info.Msg.message);
+ if (NULL == MsgMemoryEntry)
+ {
+ /* Not present, no copying needed */
+ Info.LParamSize = 0;
+ }
+ else
+ {
+ /* Determine required size */
+ Size = MsgMemorySize(MsgMemoryEntry, Info.Msg.wParam,
+ Info.Msg.lParam);
+ /* Allocate required amount of user-mode memory */
+ Info.LParamSize = Size;
+ UserMem = NULL;
+ Status = ZwAllocateVirtualMemory(NtCurrentProcess(), &UserMem, 0,
+ &Info.LParamSize, MEM_COMMIT, PAGE_READWRITE);
+ if (! NT_SUCCESS(Status))
+ {
+ SetLastNtError(Status);
+ RETURN( (BOOL) -1);
+ }
+ /* Transfer lParam data to user-mode mem */
+ Status = MmCopyToCaller(UserMem, (PVOID) Info.Msg.lParam, Size);
+ if (! NT_SUCCESS(Status))
+ {
+ ZwFreeVirtualMemory(NtCurrentProcess(), (PVOID *) &UserMem,
+ &Info.LParamSize, MEM_RELEASE);
+ SetLastNtError(Status);
+ RETURN( (BOOL) -1);
+ }
+ Info.Msg.lParam = (LPARAM) UserMem;
+ }
+ if (RemoveMsg && Msg.FreeLParam && 0 != Msg.Msg.lParam)
+ {
+ ExFreePool((void *) Msg.Msg.lParam);
+ }
+ Status = MmCopyToCaller(UnsafeInfo, &Info, sizeof(NTUSERGETMESSAGEINFO));
+ if (! NT_SUCCESS(Status))
+ {
+ SetLastNtError(Status);
+ RETURN( (BOOL) -1);
+ }
+ }
+
+ RETURN( Present);
+
+CLEANUP:
+ DPRINT("Leave NtUserPeekMessage, ret=%i\n",_ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+BOOL
+APIENTRY
+NtUserPeekMessageX(
+ PMSG pMsg,
+ HWND hWnd,
+ UINT MsgFilterMin,
+ UINT MsgFilterMax,
+ UINT RemoveMsg)
+{
+ MSG Msg;
+ BOOL Ret = FALSE;
+ DECLARE_RETURN(BOOL);
+
+ DPRINT("Enter NtUserPeekMessage\n");
+ UserEnterExclusive();
+
+ if ( RemoveMsg & PM_BADMSGFLAGS )
+ {
+ SetLastWin32Error(ERROR_INVALID_FLAGS);
+ RETURN( Ret);
+ }
+
+ RtlZeroMemory(&Msg, sizeof(MSG));
+
+ Ret = co_IntGetPeekMessage(&Msg, hWnd, MsgFilterMin, MsgFilterMax, RemoveMsg, FALSE);
+
+ if (Ret)
+ {
+ _SEH2_TRY
+ {
+ ProbeForWrite(pMsg, sizeof(MSG), 1);
+ RtlCopyMemory(pMsg, &Msg, sizeof(MSG));
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ SetLastNtError(_SEH2_GetExceptionCode());
+ Ret = FALSE;
+ }
+ _SEH2_END;
+ }
+ RETURN( Ret);
+
+CLEANUP:
+ DPRINT("Leave NtUserPeekMessage, ret=%i\n",_ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+BOOL
+APIENTRY
+NtUserCallMsgFilter(
+ LPMSG lpmsg,
+ INT code)
+{
+ BOOL BadChk = FALSE, Ret = FALSE;
+ MSG Msg;
+ DECLARE_RETURN(BOOL);
+
+ DPRINT("Enter NtUserCallMsgFilter\n");
+ UserEnterExclusive();
+ if (lpmsg)
+ {
+ _SEH2_TRY
+ {
+ ProbeForRead((PVOID)lpmsg,
+ sizeof(MSG),
+ 1);
+ RtlCopyMemory( &Msg,
+ (PVOID)lpmsg,
+ sizeof(MSG));
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ BadChk = TRUE;
+ }
+ _SEH2_END;
+ }
+ else
+ RETURN( FALSE);
+
+ if (BadChk) RETURN( FALSE);
+
+ if ( ISITHOOKED(WH_SYSMSGFILTER) &&
+ co_HOOK_CallHooks( WH_SYSMSGFILTER, code, 0, (LPARAM)&Msg))
+ {
+ Ret = TRUE;
+ }
+ else
+ {
+ if ( ISITHOOKED(WH_MSGFILTER) )
+ {
+ Ret = co_HOOK_CallHooks( WH_MSGFILTER, code, 0, (LPARAM)&Msg);
+ }
+ }
+
+ _SEH2_TRY
+ {
+ ProbeForWrite((PVOID)lpmsg,
+ sizeof(MSG),
+ 1);
+ RtlCopyMemory((PVOID)lpmsg,
+ &Msg,
+ sizeof(MSG));
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ BadChk = TRUE;
+ }
+ _SEH2_END;
+ if (BadChk) RETURN( FALSE);
+ RETURN( Ret)
+
+CLEANUP:
+ DPRINT("Leave NtUserCallMsgFilter. ret=%i\n", _ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+LRESULT APIENTRY
+NtUserDispatchMessage(PMSG UnsafeMsgInfo)
+{
+ LRESULT Res = 0;
+ BOOL Hit = FALSE;
+ MSG SafeMsg;
+
+ UserEnterExclusive();
+ _SEH2_TRY
+ {
+ ProbeForRead(UnsafeMsgInfo, sizeof(MSG), 1);
+ RtlCopyMemory(&SafeMsg, UnsafeMsgInfo, sizeof(MSG));
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ SetLastNtError(_SEH2_GetExceptionCode());
+ Hit = TRUE;
+ }
+ _SEH2_END;
+
+ if (!Hit) Res = IntDispatchMessage(&SafeMsg);
+
+ UserLeave();
+ return Res;
+}
+
+
+BOOL APIENTRY
+NtUserTranslateMessage(LPMSG lpMsg,
+ UINT flags)
+{
+ NTSTATUS Status;
+ MSG SafeMsg;
+ DECLARE_RETURN(BOOL);
+
+ DPRINT("Enter NtUserTranslateMessage\n");
+ UserEnterExclusive();
+
+ Status = MmCopyFromCaller(&SafeMsg, lpMsg, sizeof(MSG));
+ if(!NT_SUCCESS(Status))
+ {
+ SetLastNtError(Status);
+ RETURN( FALSE);
+ }
+
+ RETURN( IntTranslateKbdMessage(&SafeMsg, flags));
+
+CLEANUP:
+ DPRINT("Leave NtUserTranslateMessage: ret=%i\n",_ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+BOOL APIENTRY
+NtUserMessageCall(
+ HWND hWnd,
+ UINT Msg,
+ WPARAM wParam,
+ LPARAM lParam,
+ ULONG_PTR ResultInfo,
+ DWORD dwType, // fnID?
+ BOOL Ansi)
+{
+ LRESULT lResult = 0;
+ BOOL Ret = FALSE;
+ BOOL BadChk = FALSE;
+ PWINDOW_OBJECT Window = NULL;
+ USER_REFERENCE_ENTRY Ref;
+
+ UserEnterExclusive();
+
+ /* Validate input */
+ if (hWnd && (hWnd != INVALID_HANDLE_VALUE) && !(Window = UserGetWindowObject(hWnd)))
+ {
+ UserLeave();
+ return FALSE;
+ }
+ switch(dwType)
+ {
+ case FNID_DEFWINDOWPROC:
+ UserRefObjectCo(Window, &Ref);
+ lResult = IntDefWindowProc(Window, Msg, wParam, lParam, Ansi);
+ Ret = TRUE;
+ UserDerefObjectCo(Window);
+ break;
+ case FNID_SENDNOTIFYMESSAGE:
+ Ret = UserSendNotifyMessage(hWnd, Msg, wParam, lParam);
+ break;
+ case FNID_BROADCASTSYSTEMMESSAGE:
+ {
+ BROADCASTPARM parm;
+ DWORD_PTR RetVal = 0;
+
+ if (ResultInfo)
+ {
+ _SEH2_TRY
+ {
+ ProbeForWrite((PVOID)ResultInfo,
+ sizeof(BROADCASTPARM),
+ 1);
+ RtlCopyMemory(&parm, (PVOID)ResultInfo, sizeof(BROADCASTPARM));
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ BadChk = TRUE;
+ }
+ _SEH2_END;
+ if (BadChk) break;
+ }
+ else
+ break;
+
+ if ( parm.recipients & BSM_ALLDESKTOPS ||
+ parm.recipients == BSM_ALLCOMPONENTS )
+ {
+ }
+ else if (parm.recipients & BSM_APPLICATIONS)
+ {
+ if (parm.flags & BSF_QUERY)
+ {
+ if (parm.flags & BSF_FORCEIFHUNG || parm.flags & BSF_NOHANG)
+ {
+ co_IntSendMessageTimeout( HWND_BROADCAST,
+ Msg,
+ wParam,
+ lParam,
+ SMTO_ABORTIFHUNG,
+ 2000,
+ &RetVal);
+ }
+ else if (parm.flags & BSF_NOTIMEOUTIFNOTHUNG)
+ {
+ co_IntSendMessageTimeout( HWND_BROADCAST,
+ Msg,
+ wParam,
+ lParam,
+ SMTO_NOTIMEOUTIFNOTHUNG,
+ 2000,
+ &RetVal);
+ }
+ else
+ {
+ co_IntSendMessageTimeout( HWND_BROADCAST,
+ Msg,
+ wParam,
+ lParam,
+ SMTO_NORMAL,
+ 2000,
+ &RetVal);
+ }
+ }
+ else if (parm.flags & BSF_POSTMESSAGE)
+ {
+ Ret = UserPostMessage(HWND_BROADCAST, Msg, wParam, lParam);
+ }
+ else if ( parm.flags & BSF_SENDNOTIFYMESSAGE)
+ {
+ Ret = UserSendNotifyMessage(HWND_BROADCAST, Msg, wParam, lParam);
+ }
+ }
+ }
+ break;
+ case FNID_SENDMESSAGECALLBACK:
+ {
+ PCALL_BACK_INFO CallBackInfo = (PCALL_BACK_INFO)ResultInfo;
+
+ if (!CallBackInfo)
+ break;
+
+ if (!co_IntSendMessageWithCallBack(hWnd, Msg, wParam, lParam,
+ CallBackInfo->CallBack, CallBackInfo->Context, NULL))
+ {
+ DPRINT1("Callback failure!\n");
+ }
+ }
+ break;
+ // CallNextHook bypass.
+ case FNID_CALLWNDPROC:
+ case FNID_CALLWNDPROCRET:
+ {
+ PCLIENTINFO ClientInfo = GetWin32ClientInfo();
+ PHOOK NextObj, Hook = ClientInfo->phkCurrent;
+
+ if (!ClientInfo || !Hook) break;
+
+ UserReferenceObject(Hook);
+
+ if (Hook->Thread && (Hook->Thread != PsGetCurrentThread()))
+ {
+ UserDereferenceObject(Hook);
+ break;
+ }
+
+ NextObj = IntGetNextHook(Hook);
+ ClientInfo->phkCurrent = NextObj;
+
+ if ( Hook->HookId == WH_CALLWNDPROC)
+ {
+ CWPSTRUCT CWP;
+ CWP.hwnd = hWnd;
+ CWP.message = Msg;
+ CWP.wParam = wParam;
+ CWP.lParam = lParam;
+ DPRINT("WH_CALLWNDPROC: Hook %x NextHook %x\n", Hook, NextObj );
+
+ lResult = co_IntCallHookProc( Hook->HookId,
+ HC_ACTION,
+ ((ClientInfo->CI_flags & CI_CURTHPRHOOK) ? 1 : 0),
+ (LPARAM)&CWP,
+ Hook->Proc,
+ Hook->Ansi,
+ &Hook->ModuleName);
+ }
+ else
+ {
+ CWPRETSTRUCT CWPR;
+ CWPR.hwnd = hWnd;
+ CWPR.message = Msg;
+ CWPR.wParam = wParam;
+ CWPR.lParam = lParam;
+ CWPR.lResult = ClientInfo->dwHookData;
+
+ lResult = co_IntCallHookProc( Hook->HookId,
+ HC_ACTION,
+ ((ClientInfo->CI_flags & CI_CURTHPRHOOK) ? 1 : 0),
+ (LPARAM)&CWPR,
+ Hook->Proc,
+ Hook->Ansi,
+ &Hook->ModuleName);
+ }
+ UserDereferenceObject(Hook);
+ lResult = (LRESULT) NextObj;
+ }
+ break;
+ }
+
+ switch(dwType)
+ {
+ case FNID_DEFWINDOWPROC:
+ case FNID_CALLWNDPROC:
+ case FNID_CALLWNDPROCRET:
+ if (ResultInfo)
+ {
+ _SEH2_TRY
+ {
+ ProbeForWrite((PVOID)ResultInfo, sizeof(LRESULT), 1);
+ RtlCopyMemory((PVOID)ResultInfo, &lResult, sizeof(LRESULT));
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ BadChk = TRUE;
+ }
+ _SEH2_END;
+ }
+ break;
+ default:
+ break;
+ }
+
+ UserLeave();
+
+ return BadChk ? FALSE : Ret;
+}
+
+#define INFINITE 0xFFFFFFFF
+#define WAIT_FAILED ((DWORD)0xFFFFFFFF)
+
+DWORD
+APIENTRY
+NtUserWaitForInputIdle(
+ IN HANDLE hProcess,
+ IN DWORD dwMilliseconds,
+ IN BOOL Unknown2)
+{
+ PEPROCESS Process;
+ PPROCESSINFO W32Process;
+ NTSTATUS Status;
+ HANDLE Handles[2];
+ LARGE_INTEGER Timeout;
+ ULONGLONG StartTime, Run, Elapsed = 0;
+
+ UserEnterExclusive();
+
+ Status = ObReferenceObjectByHandle(hProcess,
+ PROCESS_QUERY_INFORMATION,
+ PsProcessType,
+ UserMode,
+ (PVOID*)&Process,
+ NULL);
+
+ if (!NT_SUCCESS(Status))
+ {
+ UserLeave();
+ SetLastNtError(Status);
+ return WAIT_FAILED;
+ }
+
+ W32Process = (PPROCESSINFO)Process->Win32Process;
+ if (!W32Process)
+ {
+ ObDereferenceObject(Process);
+ UserLeave();
+ SetLastWin32Error(ERROR_INVALID_PARAMETER);
+ return WAIT_FAILED;
+ }
+
+ EngCreateEvent((PEVENT *)&W32Process->InputIdleEvent);
+
+ Handles[0] = Process;
+ Handles[1] = W32Process->InputIdleEvent;
+
+ if (!Handles[1])
+ {
+ ObDereferenceObject(Process);
+ UserLeave();
+ return STATUS_SUCCESS; /* no event to wait on */
+ }
+
+ StartTime = EngGetTickCount();
+
+ Run = dwMilliseconds;
+
+ DPRINT("WFII: waiting for %p\n", Handles[1] );
+ do
+ {
+ Timeout.QuadPart = Run - Elapsed;
+ UserLeave();
+ Status = KeWaitForMultipleObjects( 2,
+ Handles,
+ WaitAny,
+ UserRequest,
+ UserMode,
+ FALSE,
+ dwMilliseconds == INFINITE ? NULL : &Timeout,
+ NULL);
+ UserEnterExclusive();
+
+ if (!NT_SUCCESS(Status))
+ {
+ SetLastNtError(Status);
+ Status = WAIT_FAILED;
+ goto WaitExit;
+ }
+
+ switch (Status)
+ {
+ case STATUS_WAIT_0:
+ Status = WAIT_FAILED;
+ goto WaitExit;
+
+ case STATUS_WAIT_2:
+ {
+ USER_MESSAGE Msg;
+ co_IntPeekMessage( &Msg, 0, 0, 0, PM_REMOVE | PM_QS_SENDMESSAGE );
+ break;
+ }
+
+ case STATUS_USER_APC:
+ case STATUS_ALERTED:
+ case STATUS_TIMEOUT:
+ DPRINT1("WFII: timeout\n");
+ Status = STATUS_TIMEOUT;
+ goto WaitExit;
+
+ default:
+ DPRINT1("WFII: finished\n");
+ Status = STATUS_SUCCESS;
+ goto WaitExit;
+ }
+
+ if (dwMilliseconds != INFINITE)
+ {
+ Elapsed = EngGetTickCount() - StartTime;
+
+ if (Elapsed > Run)
+ Status = STATUS_TIMEOUT;
+ break;
+ }
+ }
+ while (1);
+
+WaitExit:
+ if (W32Process->InputIdleEvent)
+ {
+ EngFreeMem((PVOID)W32Process->InputIdleEvent);
+ W32Process->InputIdleEvent = NULL;
+ }
+ ObDereferenceObject(Process);
+ UserLeave();
+ return Status;
+}
+
+/* EOF */
--- /dev/null
- NTSTATUS Status;
-
+/*
+ * COPYRIGHT: See COPYING in the top level directory
+ * PROJECT: ReactOS kernel
+ * PURPOSE: ntuser init. and main funcs.
+ * FILE: subsystems/win32/win32k/ntuser/ntuser.c
+ * REVISION HISTORY:
+ * 16 July 2005 Created (hardon)
+ */
+
+/* INCLUDES ******************************************************************/
+
+#include <win32k.h>
+
+#define NDEBUG
+#include <debug.h>
+
+BOOL InitSysParams();
+
+/* GLOBALS *******************************************************************/
+
+ERESOURCE UserLock;
+ATOM AtomMessage; // Window Message atom.
+ATOM AtomWndObj; // Window Object atom.
+BOOL gbInitialized;
+HINSTANCE hModClient = NULL;
+BOOL ClientPfnInit = FALSE;
+
+/* PRIVATE FUNCTIONS *********************************************************/
+
+static
+NTSTATUS FASTCALL
+InitUserAtoms(VOID)
+{
+
+ gpsi->atomSysClass[ICLS_MENU] = 32768;
+ gpsi->atomSysClass[ICLS_DESKTOP] = 32769;
+ gpsi->atomSysClass[ICLS_DIALOG] = 32770;
+ gpsi->atomSysClass[ICLS_SWITCH] = 32771;
+ gpsi->atomSysClass[ICLS_ICONTITLE] = 32772;
+ gpsi->atomSysClass[ICLS_TOOLTIPS] = 32774;
+
+ /* System Message Atom */
+ AtomMessage = IntAddGlobalAtom(L"Message", TRUE);
+ gpsi->atomSysClass[ICLS_HWNDMESSAGE] = AtomMessage;
+
+ /* System Context Help Id Atom */
+ gpsi->atomContextHelpIdProp = IntAddGlobalAtom(L"SysCH", TRUE);
+
+ AtomWndObj = IntAddGlobalAtom(L"SysWNDO", TRUE);
+
+ return STATUS_SUCCESS;
+}
+
+/* FUNCTIONS *****************************************************************/
+
+
+NTSTATUS FASTCALL InitUserImpl(VOID)
+{
+ NTSTATUS Status;
+
+ ExInitializeResourceLite(&UserLock);
+
+ if (!UserCreateHandleTable())
+ {
+ DPRINT1("Failed creating handle table\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ Status = InitSessionImpl();
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("Error init session impl.\n");
+ return Status;
+ }
+
+ InitUserAtoms();
+
+ InitSysParams();
+
+ return STATUS_SUCCESS;
+}
+
+BOOL
+InitVideo(ULONG);
+
+NTSTATUS
+NTAPI
+UserInitialize(
+ HANDLE hPowerRequestEvent,
+ HANDLE hMediaRequestEvent)
+{
- Status = co_IntClientThreadSetup();
+// Set W32PF_Flags |= (W32PF_READSCREENACCESSGRANTED | W32PF_IOWINSTA)
+// Create Object Directory,,, Looks like create workstation. "\\Windows\\WindowStations"
+// Create Event for Diconnect Desktop.
+ InitVideo(0);
+// Initialize Video.
+// {
+// DrvInitConsole.
+// DrvChangeDisplaySettings.
+// Update Shared Device Caps.
+// Initialize User Screen.
+// }
+// Create ThreadInfo for this Thread!
+// {
+
+ GetW32ThreadInfo();
+
+// Callback to User32 Client Thread Setup
+
++ co_IntClientThreadSetup();
+
+// }
+// Set Global SERVERINFO Error flags.
+// Load Resources.
+
+ NtUserUpdatePerUserSystemParameters(0, TRUE);
+
+ CsrInit();
+
+ return STATUS_SUCCESS;
+}
+
+/*
+ Called from win32csr.
+ */
+NTSTATUS
+APIENTRY
+NtUserInitialize(
+ DWORD dwWinVersion,
+ HANDLE hPowerRequestEvent,
+ HANDLE hMediaRequestEvent)
+{
+ NTSTATUS Status;
+
+ DPRINT1("Enter NtUserInitialize(%lx, %p, %p)\n",
+ dwWinVersion, hPowerRequestEvent, hMediaRequestEvent);
+
+ /* Check the Windows version */
+ if (dwWinVersion != 0)
+ {
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ /* Acquire exclusive lock */
+ UserEnterExclusive();
+
+ /* Check if we are already initialized */
+ if (gbInitialized)
+ {
+ UserLeave();
+ return STATUS_UNSUCCESSFUL;
+ }
+
+// Initialize Power Request List.
+// Initialize Media Change.
+// InitializeGreCSRSS();
+// {
+// Startup DxGraphics.
+// calls ** IntGdiGetLanguageID() and sets it **.
+// Enables Fonts drivers, Initialize Font table & Stock Fonts.
+// }
+
+ /* Initialize USER */
+ Status = UserInitialize(hPowerRequestEvent, hMediaRequestEvent);
+
+ /* Set us as initialized */
+ gbInitialized = TRUE;
+
+ /* Return */
+ UserLeave();
+ return Status;
+}
+
+
+/*
+RETURN
+ True if current thread owns the lock (possibly shared)
+*/
+BOOL FASTCALL UserIsEntered(VOID)
+{
+ return ExIsResourceAcquiredExclusiveLite(&UserLock)
+ || ExIsResourceAcquiredSharedLite(&UserLock);
+}
+
+BOOL FASTCALL UserIsEnteredExclusive(VOID)
+{
+ return ExIsResourceAcquiredExclusiveLite(&UserLock);
+}
+
+VOID FASTCALL CleanupUserImpl(VOID)
+{
+ ExDeleteResourceLite(&UserLock);
+}
+
+VOID FASTCALL UserEnterShared(VOID)
+{
+ KeEnterCriticalRegion();
+ ExAcquireResourceSharedLite(&UserLock, TRUE);
+}
+
+VOID FASTCALL UserEnterExclusive(VOID)
+{
+ KeEnterCriticalRegion();
+ ExAcquireResourceExclusiveLite(&UserLock, TRUE);
+}
+
+VOID FASTCALL UserLeave(VOID)
+{
+ ExReleaseResourceLite(&UserLock);
+ KeLeaveCriticalRegion();
+}
--- /dev/null
- RECTL rc, WorkArea;
+/*
+ * COPYRIGHT: See COPYING in the top level directory
+ * PROJECT: ReactOS kernel
+ * PURPOSE: Windows
+ * FILE: subsystems/win32/win32k/ntuser/window.c
+ * PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
+ * REVISION HISTORY:
+ * 06-06-2001 CSH Created
+ */
+
+/* INCLUDES ******************************************************************/
+
+#include <win32k.h>
+
+#define NDEBUG
+#include <debug.h>
+
+
+/* dialog resources appear to pass this in 16 bits, handle them properly */
+#define CW_USEDEFAULT16 (0x8000)
+
+#define POINT_IN_RECT(p, r) (((r.bottom >= p.y) && (r.top <= p.y))&&((r.left <= p.x )&&( r.right >= p.x )))
+
+/* PRIVATE FUNCTIONS **********************************************************/
+
+/*
+ * InitWindowImpl
+ *
+ * Initialize windowing implementation.
+ */
+
+NTSTATUS FASTCALL
+InitWindowImpl(VOID)
+{
+ return STATUS_SUCCESS;
+}
+
+/*
+ * CleanupWindowImpl
+ *
+ * Cleanup windowing implementation.
+ */
+
+NTSTATUS FASTCALL
+CleanupWindowImpl(VOID)
+{
+ return STATUS_SUCCESS;
+}
+
+/* HELPER FUNCTIONS ***********************************************************/
+
+BOOL FASTCALL UserUpdateUiState(PWND Wnd, WPARAM wParam)
+{
+ WORD Action = LOWORD(wParam);
+ WORD Flags = HIWORD(wParam);
+
+ if (Flags & ~(UISF_HIDEFOCUS | UISF_HIDEACCEL | UISF_ACTIVE))
+ {
+ SetLastWin32Error(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+
+ switch (Action)
+ {
+ case UIS_INITIALIZE:
+ SetLastWin32Error(ERROR_INVALID_PARAMETER);
+ return FALSE;
+
+ case UIS_SET:
+ if (Flags & UISF_HIDEFOCUS)
+ Wnd->HideFocus = TRUE;
+ if (Flags & UISF_HIDEACCEL)
+ Wnd->HideAccel = TRUE;
+ break;
+
+ case UIS_CLEAR:
+ if (Flags & UISF_HIDEFOCUS)
+ Wnd->HideFocus = FALSE;
+ if (Flags & UISF_HIDEACCEL)
+ Wnd->HideAccel = FALSE;
+ break;
+ }
+
+ return TRUE;
+}
+
+PWINDOW_OBJECT FASTCALL IntGetWindowObject(HWND hWnd)
+{
+ PWINDOW_OBJECT Window;
+
+ if (!hWnd) return NULL;
+
+ Window = UserGetWindowObject(hWnd);
+ if (Window)
+ {
+ ASSERT(Window->head.cLockObj >= 0);
+
+ Window->head.cLockObj++;
+
+ ASSERT(Window->Wnd);
+ }
+ return Window;
+}
+
+/* temp hack */
+PWINDOW_OBJECT FASTCALL UserGetWindowObject(HWND hWnd)
+{
+ PTHREADINFO ti;
+ PWINDOW_OBJECT Window;
+
+ if (PsGetCurrentProcess() != PsInitialSystemProcess)
+ {
+ ti = GetW32ThreadInfo();
+ if (ti == NULL)
+ {
+ SetLastWin32Error(ERROR_ACCESS_DENIED);
+ return NULL;
+ }
+ }
+
+ if (!hWnd)
+ {
+ SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
+ return NULL;
+ }
+
+ Window = (PWINDOW_OBJECT)UserGetObject(gHandleTable, hWnd, otWindow);
+ if (!Window || 0 != (Window->state & WINDOWSTATUS_DESTROYED))
+ {
+ SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
+ return NULL;
+ }
+
+ ASSERT(Window->head.cLockObj >= 0);
+
+ ASSERT(Window->Wnd);
+
+ return Window;
+}
+
+
+/*
+ * IntIsWindow
+ *
+ * The function determines whether the specified window handle identifies
+ * an existing window.
+ *
+ * Parameters
+ * hWnd
+ * Handle to the window to test.
+ *
+ * Return Value
+ * If the window handle identifies an existing window, the return value
+ * is TRUE. If the window handle does not identify an existing window,
+ * the return value is FALSE.
+ */
+
+BOOL FASTCALL
+IntIsWindow(HWND hWnd)
+{
+ PWINDOW_OBJECT Window;
+
+ if (!(Window = UserGetWindowObject(hWnd)))
+ return FALSE;
+
+ return TRUE;
+}
+
+
+
+PWINDOW_OBJECT FASTCALL
+IntGetParent(PWINDOW_OBJECT Wnd)
+{
+ if (Wnd->Wnd->style & WS_POPUP)
+ {
+ return Wnd->spwndOwner;
+ }
+ else if (Wnd->Wnd->style & WS_CHILD)
+ {
+ return Wnd->spwndParent;
+ }
+
+ return NULL;
+}
+
+
+/*
+ * IntWinListChildren
+ *
+ * Compile a list of all child window handles from given window.
+ *
+ * Remarks
+ * This function is similar to Wine WIN_ListChildren. The caller
+ * must free the returned list with ExFreePool.
+ */
+
+HWND* FASTCALL
+IntWinListChildren(PWINDOW_OBJECT Window)
+{
+ PWINDOW_OBJECT Child;
+ HWND *List;
+ UINT Index, NumChildren = 0;
+
+ if (!Window) return NULL;
+
+ for (Child = Window->spwndChild; Child; Child = Child->spwndNext)
+ ++NumChildren;
+
+ List = ExAllocatePoolWithTag(PagedPool, (NumChildren + 1) * sizeof(HWND), TAG_WINLIST);
+ if(!List)
+ {
+ DPRINT1("Failed to allocate memory for children array\n");
+ SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
+ return NULL;
+ }
+ for (Child = Window->spwndChild, Index = 0;
+ Child != NULL;
+ Child = Child->spwndNext, ++Index)
+ List[Index] = Child->hSelf;
+ List[Index] = NULL;
+
+ return List;
+}
+
+/***********************************************************************
+ * IntSendDestroyMsg
+ */
+static void IntSendDestroyMsg(HWND hWnd)
+{
+
+ PWINDOW_OBJECT Window;
+#if 0 /* FIXME */
+
+ GUITHREADINFO info;
+
+ if (GetGUIThreadInfo(GetCurrentThreadId(), &info))
+ {
+ if (hWnd == info.hwndCaret)
+ {
+ DestroyCaret();
+ }
+ }
+#endif
+
+ Window = UserGetWindowObject(hWnd);
+ if (Window)
+ {
+// USER_REFERENCE_ENTRY Ref;
+// UserRefObjectCo(Window, &Ref);
+
+ if (!Window->spwndOwner && !IntGetParent(Window))
+ {
+ co_IntShellHookNotify(HSHELL_WINDOWDESTROYED, (LPARAM) hWnd);
+ }
+
+// UserDerefObjectCo(Window);
+ }
+
+ /* The window could already be destroyed here */
+
+ /*
+ * Send the WM_DESTROY to the window.
+ */
+
+ co_IntSendMessage(hWnd, WM_DESTROY, 0, 0);
+
+ /*
+ * This WM_DESTROY message can trigger re-entrant calls to DestroyWindow
+ * make sure that the window still exists when we come back.
+ */
+#if 0 /* FIXME */
+
+ if (IsWindow(Wnd))
+ {
+ HWND* pWndArray;
+ int i;
+
+ if (!(pWndArray = WIN_ListChildren( hwnd )))
+ return;
+
+ /* start from the end (FIXME: is this needed?) */
+ for (i = 0; pWndArray[i]; i++)
+ ;
+
+ while (--i >= 0)
+ {
+ if (IsWindow( pWndArray[i] ))
+ WIN_SendDestroyMsg( pWndArray[i] );
+ }
+ HeapFree(GetProcessHeap(), 0, pWndArray);
+ }
+ else
+ {
+ DPRINT("destroyed itself while in WM_DESTROY!\n");
+ }
+#endif
+}
+
+static VOID
+UserFreeWindowInfo(PTHREADINFO ti, PWINDOW_OBJECT WindowObject)
+{
+ PCLIENTINFO ClientInfo = GetWin32ClientInfo();
+ PWND Wnd = WindowObject->Wnd;
+
+ if (!Wnd) return;
+
+ if (ClientInfo->CallbackWnd.pvWnd == DesktopHeapAddressToUser(WindowObject->Wnd))
+ {
+ ClientInfo->CallbackWnd.hWnd = NULL;
+ ClientInfo->CallbackWnd.pvWnd = NULL;
+ }
+
+ if (Wnd->strName.Buffer != NULL)
+ {
+ Wnd->strName.Length = 0;
+ Wnd->strName.MaximumLength = 0;
+ DesktopHeapFree(Wnd->head.rpdesk,
+ Wnd->strName.Buffer);
+ Wnd->strName.Buffer = NULL;
+ }
+
+ DesktopHeapFree(Wnd->head.rpdesk, Wnd);
+ WindowObject->Wnd = NULL;
+}
+
+/***********************************************************************
+ * IntDestroyWindow
+ *
+ * Destroy storage associated to a window. "Internals" p.358
+ *
+ * This is the "functional" DestroyWindows function ei. all stuff
+ * done in CreateWindow is undone here and not in DestroyWindow:-P
+
+ */
+static LRESULT co_UserFreeWindow(PWINDOW_OBJECT Window,
+ PPROCESSINFO ProcessData,
+ PTHREADINFO ThreadData,
+ BOOLEAN SendMessages)
+{
+ HWND *Children;
+ HWND *ChildHandle;
+ PWINDOW_OBJECT Child;
+ PMENU_OBJECT Menu;
+ BOOLEAN BelongsToThreadData;
+ PWND Wnd;
+
+ ASSERT(Window);
+
+ Wnd = Window->Wnd;
+
+ if(Window->state & WINDOWSTATUS_DESTROYING)
+ {
+ DPRINT("Tried to call IntDestroyWindow() twice\n");
+ return 0;
+ }
+ Window->state |= WINDOWSTATUS_DESTROYING;
+ Wnd->style &= ~WS_VISIBLE;
+
+ IntNotifyWinEvent(EVENT_OBJECT_DESTROY, Wnd, OBJID_WINDOW, 0);
+
+ /* remove the window already at this point from the thread window list so we
+ don't get into trouble when destroying the thread windows while we're still
+ in IntDestroyWindow() */
+ RemoveEntryList(&Window->ThreadListEntry);
+
+ BelongsToThreadData = IntWndBelongsToThread(Window, ThreadData);
+
+ IntDeRegisterShellHookWindow(Window->hSelf);
+
+ if(SendMessages)
+ {
+ /* Send destroy messages */
+ IntSendDestroyMsg(Window->hSelf);
+ }
+
+ /* free child windows */
+ Children = IntWinListChildren(Window);
+ if (Children)
+ {
+ for (ChildHandle = Children; *ChildHandle; ++ChildHandle)
+ {
+ if ((Child = IntGetWindowObject(*ChildHandle)))
+ {
+ if(!IntWndBelongsToThread(Child, ThreadData))
+ {
+ /* send WM_DESTROY messages to windows not belonging to the same thread */
+ IntSendDestroyMsg(Child->hSelf);
+ }
+ else
+ co_UserFreeWindow(Child, ProcessData, ThreadData, SendMessages);
+
+ UserDereferenceObject(Child);
+ }
+ }
+ ExFreePool(Children);
+ }
+
+ if(SendMessages)
+ {
+ /*
+ * Clear the update region to make sure no WM_PAINT messages will be
+ * generated for this window while processing the WM_NCDESTROY.
+ */
+ co_UserRedrawWindow(Window, NULL, 0,
+ RDW_VALIDATE | RDW_NOFRAME | RDW_NOERASE |
+ RDW_NOINTERNALPAINT | RDW_NOCHILDREN);
+ if(BelongsToThreadData)
+ co_IntSendMessage(Window->hSelf, WM_NCDESTROY, 0, 0);
+ }
+ DestroyTimersForWindow(ThreadData, Window);
+ HOOK_DestroyThreadHooks(ThreadData->pEThread); // This is needed here too!
+
+ /* flush the message queue */
+ MsqRemoveWindowMessagesFromQueue(Window);
+
+ /* from now on no messages can be sent to this window anymore */
+ Window->state |= WINDOWSTATUS_DESTROYED;
+ Wnd->state |= WNDS_DESTROYED;
+ Wnd->fnid |= FNID_FREED;
+
+ /* don't remove the WINDOWSTATUS_DESTROYING bit */
+
+ /* reset shell window handles */
+ if(ThreadData->rpdesk)
+ {
+ if (Window->hSelf == ThreadData->rpdesk->rpwinstaParent->ShellWindow)
+ ThreadData->rpdesk->rpwinstaParent->ShellWindow = NULL;
+
+ if (Window->hSelf == ThreadData->rpdesk->rpwinstaParent->ShellListView)
+ ThreadData->rpdesk->rpwinstaParent->ShellListView = NULL;
+ }
+
+ /* Unregister hot keys */
+ UnregisterWindowHotKeys (Window);
+
+ /* FIXME: do we need to fake QS_MOUSEMOVE wakebit? */
+
+#if 0 /* FIXME */
+
+ WinPosCheckInternalPos(Window->hSelf);
+ if (Window->hSelf == GetCapture())
+ {
+ ReleaseCapture();
+ }
+
+ /* free resources associated with the window */
+ TIMER_RemoveWindowTimers(Window->hSelf);
+#endif
+
+ if (!(Wnd->style & WS_CHILD) && Wnd->IDMenu
+ && (Menu = UserGetMenuObject((HMENU)Wnd->IDMenu)))
+ {
+ IntDestroyMenuObject(Menu, TRUE, TRUE);
+ Wnd->IDMenu = 0;
+ }
+
+ if(Window->SystemMenu
+ && (Menu = UserGetMenuObject(Window->SystemMenu)))
+ {
+ IntDestroyMenuObject(Menu, TRUE, TRUE);
+ Window->SystemMenu = (HMENU)0;
+ }
+
+ DceFreeWindowDCE(Window); /* Always do this to catch orphaned DCs */
+#if 0 /* FIXME */
+
+ WINPROC_FreeProc(Window->winproc, WIN_PROC_WINDOW);
+ CLASS_RemoveWindow(Window->Class);
+#endif
+
+ IntUnlinkWindow(Window);
+
+ UserReferenceObject(Window);
+ UserDeleteObject(Window->hSelf, otWindow);
+
+ IntDestroyScrollBars(Window);
+
+ /* dereference the class */
+ IntDereferenceClass(Wnd->pcls,
+ Window->pti->pDeskInfo,
+ Window->pti->ppi);
+ Wnd->pcls = NULL;
+
+ if(Window->hrgnClip)
+ {
+ GreDeleteObject(Window->hrgnClip);
+ }
+
+ ASSERT(Window->Wnd != NULL);
+ UserFreeWindowInfo(Window->pti, Window);
+
+ UserDereferenceObject(Window);
+
+ IntClipboardFreeWindow(Window);
+
+ return 0;
+}
+
+VOID FASTCALL
+IntGetWindowBorderMeasures(PWINDOW_OBJECT Window, UINT *cx, UINT *cy)
+{
+ PWND Wnd = Window->Wnd;
+ if(HAS_DLGFRAME(Wnd->style, Wnd->ExStyle) && !(Wnd->style & WS_MINIMIZE))
+ {
+ *cx = UserGetSystemMetrics(SM_CXDLGFRAME);
+ *cy = UserGetSystemMetrics(SM_CYDLGFRAME);
+ }
+ else
+ {
+ if(HAS_THICKFRAME(Wnd->style, Wnd->ExStyle)&& !(Wnd->style & WS_MINIMIZE))
+ {
+ *cx = UserGetSystemMetrics(SM_CXFRAME);
+ *cy = UserGetSystemMetrics(SM_CYFRAME);
+ }
+ else if(HAS_THINFRAME(Wnd->style, Wnd->ExStyle))
+ {
+ *cx = UserGetSystemMetrics(SM_CXBORDER);
+ *cy = UserGetSystemMetrics(SM_CYBORDER);
+ }
+ else
+ {
+ *cx = *cy = 0;
+ }
+ }
+}
+
+//
+// Same as User32:IntGetWndProc.
+//
+WNDPROC FASTCALL
+IntGetWindowProc(PWND pWnd,
+ BOOL Ansi)
+{
+ INT i;
+ PCLS Class;
+ WNDPROC gcpd, Ret = 0;
+
+ ASSERT(UserIsEnteredExclusive() == TRUE);
+
+ Class = pWnd->pcls;
+
+ if (pWnd->state & WNDS_SERVERSIDEWINDOWPROC)
+ {
+ for ( i = FNID_FIRST; i <= FNID_SWITCH; i++)
+ {
+ if (GETPFNSERVER(i) == pWnd->lpfnWndProc)
+ {
+ if (Ansi)
+ Ret = GETPFNCLIENTA(i);
+ else
+ Ret = GETPFNCLIENTW(i);
+ }
+ }
+ return Ret;
+ }
+
+ if (Class->fnid == FNID_EDIT)
+ Ret = pWnd->lpfnWndProc;
+ else
+ {
+ Ret = pWnd->lpfnWndProc;
+
+ if (Class->fnid <= FNID_GHOST && Class->fnid >= FNID_BUTTON)
+ {
+ if (Ansi)
+ {
+ if (GETPFNCLIENTW(Class->fnid) == pWnd->lpfnWndProc)
+ Ret = GETPFNCLIENTA(Class->fnid);
+ }
+ else
+ {
+ if (GETPFNCLIENTA(Class->fnid) == pWnd->lpfnWndProc)
+ Ret = GETPFNCLIENTW(Class->fnid);
+ }
+ }
+ if ( Ret != pWnd->lpfnWndProc)
+ return Ret;
+ }
+ if ( Ansi == !!(pWnd->state & WNDS_ANSIWINDOWPROC) )
+ return Ret;
+
+ gcpd = (WNDPROC)UserGetCPD(
+ pWnd,
+ (Ansi ? UserGetCPDA2U : UserGetCPDU2A )|UserGetCPDWindow,
+ (ULONG_PTR)Ret);
+
+ return (gcpd ? gcpd : Ret);
+}
+
+static WNDPROC
+IntSetWindowProc(PWND pWnd,
+ WNDPROC NewWndProc,
+ BOOL Ansi)
+{
+ INT i;
+ PCALLPROCDATA CallProc;
+ PCLS Class;
+ WNDPROC Ret, chWndProc = NULL;
+
+ // Retrieve previous window proc.
+ Ret = IntGetWindowProc(pWnd, Ansi);
+
+ Class = pWnd->pcls;
+
+ if (IsCallProcHandle(NewWndProc))
+ {
+ CallProc = UserGetObject(gHandleTable, NewWndProc, otCallProc);
+ if (CallProc)
+ { // Reset new WndProc.
+ NewWndProc = CallProc->pfnClientPrevious;
+ // Reset Ansi from CallProc handle. This is expected with wine "deftest".
+ Ansi = !!(CallProc->wType & UserGetCPDU2A);
+ }
+ }
+ // Switch from Client Side call to Server Side call if match. Ref: "deftest".
+ for ( i = FNID_FIRST; i <= FNID_SWITCH; i++)
+ {
+ if (GETPFNCLIENTW(i) == NewWndProc)
+ {
+ chWndProc = GETPFNSERVER(i);
+ break;
+ }
+ if (GETPFNCLIENTA(i) == NewWndProc)
+ {
+ chWndProc = GETPFNSERVER(i);
+ break;
+ }
+ }
+ // If match, set/reset to Server Side and clear ansi.
+ if (chWndProc)
+ {
+ pWnd->lpfnWndProc = chWndProc;
+ pWnd->Unicode = TRUE;
+ pWnd->state &= ~WNDS_ANSIWINDOWPROC;
+ pWnd->state |= WNDS_SERVERSIDEWINDOWPROC;
+ }
+ else
+ {
+ pWnd->Unicode = !Ansi;
+ // Handle the state change in here.
+ if (Ansi)
+ pWnd->state |= WNDS_ANSIWINDOWPROC;
+ else
+ pWnd->state &= ~WNDS_ANSIWINDOWPROC;
+
+ if (pWnd->state & WNDS_SERVERSIDEWINDOWPROC)
+ pWnd->state &= ~WNDS_SERVERSIDEWINDOWPROC;
+
+ if (!NewWndProc) NewWndProc = pWnd->lpfnWndProc;
+
+ if (Class->fnid <= FNID_GHOST && Class->fnid >= FNID_BUTTON)
+ {
+ if (Ansi)
+ {
+ if (GETPFNCLIENTW(Class->fnid) == NewWndProc)
+ chWndProc = GETPFNCLIENTA(Class->fnid);
+ }
+ else
+ {
+ if (GETPFNCLIENTA(Class->fnid) == NewWndProc)
+ chWndProc = GETPFNCLIENTW(Class->fnid);
+ }
+ }
+ // Now set the new window proc.
+ pWnd->lpfnWndProc = (chWndProc ? chWndProc : NewWndProc);
+ }
+ return Ret;
+}
+
+// Move this to user space!
+BOOL FASTCALL
+IntGetWindowInfo(PWINDOW_OBJECT Window, PWINDOWINFO pwi)
+{
+ PWND Wnd = Window->Wnd;
+
+ pwi->cbSize = sizeof(WINDOWINFO);
+ pwi->rcWindow = Window->Wnd->rcWindow;
+ pwi->rcClient = Window->Wnd->rcClient;
+ pwi->dwStyle = Wnd->style;
+ pwi->dwExStyle = Wnd->ExStyle;
+ pwi->dwWindowStatus = (UserGetForegroundWindow() == Window->hSelf); /* WS_ACTIVECAPTION */
+ IntGetWindowBorderMeasures(Window, &pwi->cxWindowBorders, &pwi->cyWindowBorders);
+ pwi->atomWindowType = (Wnd->pcls ? Wnd->pcls->atomClassName : 0);
+ pwi->wCreatorVersion = 0x400; /* FIXME - return a real version number */
+ return TRUE;
+}
+
+static BOOL FASTCALL
+IntSetMenu(
+ PWINDOW_OBJECT Window,
+ HMENU Menu,
+ BOOL *Changed)
+{
+ PMENU_OBJECT OldMenu, NewMenu = NULL;
+ PWND Wnd = Window->Wnd;
+
+ if ((Wnd->style & (WS_CHILD | WS_POPUP)) == WS_CHILD)
+ {
+ SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
+ return FALSE;
+ }
+
+ *Changed = (Wnd->IDMenu != (UINT) Menu);
+ if (! *Changed)
+ {
+ return TRUE;
+ }
+
+ if (Wnd->IDMenu)
+ {
+ OldMenu = IntGetMenuObject((HMENU) Wnd->IDMenu);
+ ASSERT(NULL == OldMenu || OldMenu->MenuInfo.Wnd == Window->hSelf);
+ }
+ else
+ {
+ OldMenu = NULL;
+ }
+
+ if (NULL != Menu)
+ {
+ NewMenu = IntGetMenuObject(Menu);
+ if (NULL == NewMenu)
+ {
+ if (NULL != OldMenu)
+ {
+ IntReleaseMenuObject(OldMenu);
+ }
+ SetLastWin32Error(ERROR_INVALID_MENU_HANDLE);
+ return FALSE;
+ }
+ if (NULL != NewMenu->MenuInfo.Wnd)
+ {
+ /* Can't use the same menu for two windows */
+ if (NULL != OldMenu)
+ {
+ IntReleaseMenuObject(OldMenu);
+ }
+ SetLastWin32Error(ERROR_INVALID_MENU_HANDLE);
+ return FALSE;
+ }
+
+ }
+
+ Wnd->IDMenu = (UINT) Menu;
+ if (NULL != NewMenu)
+ {
+ NewMenu->MenuInfo.Wnd = Window->hSelf;
+ IntReleaseMenuObject(NewMenu);
+ }
+ if (NULL != OldMenu)
+ {
+ OldMenu->MenuInfo.Wnd = NULL;
+ IntReleaseMenuObject(OldMenu);
+ }
+
+ return TRUE;
+}
+
+
+/* INTERNAL ******************************************************************/
+
+
+VOID FASTCALL
+co_DestroyThreadWindows(struct _ETHREAD *Thread)
+{
+ PTHREADINFO WThread;
+ PLIST_ENTRY Current;
+ PWINDOW_OBJECT Wnd;
+ USER_REFERENCE_ENTRY Ref;
+ WThread = (PTHREADINFO)Thread->Tcb.Win32Thread;
+
+ while (!IsListEmpty(&WThread->WindowListHead))
+ {
+ Current = WThread->WindowListHead.Flink;
+ Wnd = CONTAINING_RECORD(Current, WINDOW_OBJECT, ThreadListEntry);
+
+ DPRINT("thread cleanup: while destroy wnds, wnd=0x%x\n",Wnd);
+
+ /* window removes itself from the list */
+
+ /*
+ fixme: it is critical that the window removes itself! if now, we will loop
+ here forever...
+ */
+
+ //ASSERT(co_UserDestroyWindow(Wnd));
+
+ UserRefObjectCo(Wnd, &Ref);//faxme: temp hack??
+ if (!co_UserDestroyWindow(Wnd))
+ {
+ DPRINT1("Unable to destroy window 0x%x at thread cleanup... This is _VERY_ bad!\n", Wnd);
+ }
+ UserDerefObjectCo(Wnd);//faxme: temp hack??
+ }
+}
+
+
+
+/*!
+ * Internal function.
+ * Returns client window rectangle relative to the upper-left corner of client area.
+ *
+ * \note Does not check the validity of the parameters
+*/
+VOID FASTCALL
+IntGetClientRect(PWINDOW_OBJECT Window, RECTL *Rect)
+{
+ ASSERT( Window );
+ ASSERT( Rect );
+
+ Rect->left = Rect->top = 0;
+ Rect->right = Window->Wnd->rcClient.right - Window->Wnd->rcClient.left;
+ Rect->bottom = Window->Wnd->rcClient.bottom - Window->Wnd->rcClient.top;
+}
+
+
+#if 0
+HWND FASTCALL
+IntGetFocusWindow(VOID)
+{
+ PUSER_MESSAGE_QUEUE Queue;
+ PDESKTOP pdo = IntGetActiveDesktop();
+
+ if( !pdo )
+ return NULL;
+
+ Queue = (PUSER_MESSAGE_QUEUE)pdo->ActiveMessageQueue;
+
+ if (Queue == NULL)
+ return(NULL);
+ else
+ return(Queue->FocusWindow);
+}
+#endif
+
+PMENU_OBJECT FASTCALL
+IntGetSystemMenu(PWINDOW_OBJECT Window, BOOL bRevert, BOOL RetMenu)
+{
+ PMENU_OBJECT Menu, NewMenu = NULL, SysMenu = NULL, ret = NULL;
+ PTHREADINFO W32Thread;
+ HMENU hNewMenu, hSysMenu;
+ ROSMENUITEMINFO ItemInfo;
+
+ if(bRevert)
+ {
+ W32Thread = PsGetCurrentThreadWin32Thread();
+
+ if(!W32Thread->rpdesk)
+ return NULL;
+
+ if(Window->SystemMenu)
+ {
+ Menu = UserGetMenuObject(Window->SystemMenu);
+ if(Menu)
+ {
+ IntDestroyMenuObject(Menu, TRUE, TRUE);
+ Window->SystemMenu = (HMENU)0;
+ }
+ }
+
+ if(W32Thread->rpdesk->rpwinstaParent->SystemMenuTemplate)
+ {
+ /* clone system menu */
+ Menu = UserGetMenuObject(W32Thread->rpdesk->rpwinstaParent->SystemMenuTemplate);
+ if(!Menu)
+ return NULL;
+
+ NewMenu = IntCloneMenu(Menu);
+ if(NewMenu)
+ {
+ Window->SystemMenu = NewMenu->MenuInfo.Self;
+ NewMenu->MenuInfo.Flags |= MF_SYSMENU;
+ NewMenu->MenuInfo.Wnd = Window->hSelf;
+ ret = NewMenu;
+ //IntReleaseMenuObject(NewMenu);
+ }
+ }
+ else
+ {
+ hSysMenu = UserCreateMenu(FALSE);
+ if (NULL == hSysMenu)
+ {
+ return NULL;
+ }
+ SysMenu = IntGetMenuObject(hSysMenu);
+ if (NULL == SysMenu)
+ {
+ UserDestroyMenu(hSysMenu);
+ return NULL;
+ }
+ SysMenu->MenuInfo.Flags |= MF_SYSMENU;
+ SysMenu->MenuInfo.Wnd = Window->hSelf;
+ hNewMenu = co_IntLoadSysMenuTemplate();
+ if(!hNewMenu)
+ {
+ IntReleaseMenuObject(SysMenu);
+ UserDestroyMenu(hSysMenu);
+ return NULL;
+ }
+ Menu = IntGetMenuObject(hNewMenu);
+ if(!Menu)
+ {
+ IntReleaseMenuObject(SysMenu);
+ UserDestroyMenu(hSysMenu);
+ return NULL;
+ }
+
+ NewMenu = IntCloneMenu(Menu);
+ if(NewMenu)
+ {
+ NewMenu->MenuInfo.Flags |= MF_SYSMENU | MF_POPUP;
+ IntReleaseMenuObject(NewMenu);
+ UserSetMenuDefaultItem(NewMenu, SC_CLOSE, FALSE);
+
+ ItemInfo.cbSize = sizeof(MENUITEMINFOW);
+ ItemInfo.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_STATE | MIIM_SUBMENU;
+ ItemInfo.fType = MF_POPUP;
+ ItemInfo.fState = MFS_ENABLED;
+ ItemInfo.dwTypeData = NULL;
+ ItemInfo.cch = 0;
+ ItemInfo.hSubMenu = NewMenu->MenuInfo.Self;
+ IntInsertMenuItem(SysMenu, (UINT) -1, TRUE, &ItemInfo);
+
+ Window->SystemMenu = SysMenu->MenuInfo.Self;
+
+ ret = SysMenu;
+ }
+ IntDestroyMenuObject(Menu, FALSE, TRUE);
+ }
+ if(RetMenu)
+ return ret;
+ else
+ return NULL;
+ }
+ else
+ {
+ if(Window->SystemMenu)
+ return IntGetMenuObject((HMENU)Window->SystemMenu);
+ else
+ return NULL;
+ }
+}
+
+
+BOOL FASTCALL
+IntIsChildWindow(PWINDOW_OBJECT Parent, PWINDOW_OBJECT BaseWindow)
+{
+ PWINDOW_OBJECT Window;
+ PWND Wnd;
+
+ Window = BaseWindow;
+ while (Window)
+ {
+ Wnd = Window->Wnd;
+ if (Window == Parent)
+ {
+ return(TRUE);
+ }
+ if(!(Wnd->style & WS_CHILD))
+ {
+ break;
+ }
+
+ Window = Window->spwndParent;
+ }
+
+ return(FALSE);
+}
+
+BOOL FASTCALL
+IntIsWindowVisible(PWINDOW_OBJECT BaseWindow)
+{
+ PWINDOW_OBJECT Window;
+ PWND Wnd;
+
+ Window = BaseWindow;
+ while(Window)
+ {
+ Wnd = Window->Wnd;
+ if(!(Wnd->style & WS_CHILD))
+ {
+ break;
+ }
+ if(!(Wnd->style & WS_VISIBLE))
+ {
+ return FALSE;
+ }
+
+ Window = Window->spwndParent;
+ }
+
+ if(Window && Wnd->style & WS_VISIBLE)
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+VOID FASTCALL
+IntLinkWnd(
+ PWND Wnd,
+ PWND WndParent,
+ PWND WndPrevSibling) /* set to NULL if top sibling */
+{
+ Wnd->spwndParent = WndParent;
+ if ((Wnd->spwndPrev = WndPrevSibling))
+ {
+ /* link after WndPrevSibling */
+ if ((Wnd->spwndNext = WndPrevSibling->spwndNext))
+ Wnd->spwndNext->spwndPrev = Wnd;
+
+ Wnd->spwndPrev->spwndNext = Wnd;
+ }
+ else
+ {
+ /* link at top */
+ if ((Wnd->spwndNext = WndParent->spwndChild))
+ Wnd->spwndNext->spwndPrev = Wnd;
+
+ WndParent->spwndChild = Wnd;
+ }
+
+}
+
+/* link the window into siblings and parent. children are kept in place. */
+VOID FASTCALL
+IntLinkWindow(
+ PWINDOW_OBJECT Wnd,
+ PWINDOW_OBJECT WndParent,
+ PWINDOW_OBJECT WndPrevSibling /* set to NULL if top sibling */
+)
+{
+ PWINDOW_OBJECT Parent;
+
+ IntLinkWnd(Wnd->Wnd,
+ WndParent->Wnd,
+ WndPrevSibling ? WndPrevSibling->Wnd : NULL);
+
+ Wnd->spwndParent = WndParent;
+ if ((Wnd->spwndPrev = WndPrevSibling))
+ {
+ /* link after WndPrevSibling */
+ if ((Wnd->spwndNext = WndPrevSibling->spwndNext))
+ Wnd->spwndNext->spwndPrev = Wnd;
+ Wnd->spwndPrev->spwndNext = Wnd;
+ }
+ else
+ {
+ /* link at top */
+ Parent = Wnd->spwndParent;
+ if ((Wnd->spwndNext = WndParent->spwndChild))
+ Wnd->spwndNext->spwndPrev = Wnd;
+ else if (Parent)
+ {
+ Parent->spwndChild = Wnd;
+ return;
+ }
+ if(Parent)
+ {
+ Parent->spwndChild = Wnd;
+ }
+ }
+
+}
+
+HWND FASTCALL
+IntSetOwner(HWND hWnd, HWND hWndNewOwner)
+{
+ PWINDOW_OBJECT Wnd, WndOldOwner, WndNewOwner;
+ HWND ret;
+
+ Wnd = IntGetWindowObject(hWnd);
+ if(!Wnd)
+ return NULL;
+
+ WndOldOwner = Wnd->spwndOwner;
+
+ ret = WndOldOwner ? WndOldOwner->hSelf : 0;
+
+ if((WndNewOwner = UserGetWindowObject(hWndNewOwner)))
+ {
+ Wnd->spwndOwner= WndNewOwner;
+ Wnd->Wnd->spwndOwner = WndNewOwner->Wnd;
+ }
+ else
+ {
+ Wnd->spwndOwner = NULL;
+ Wnd->Wnd->spwndOwner = NULL;
+ }
+
+ UserDereferenceObject(Wnd);
+ return ret;
+}
+
+PWINDOW_OBJECT FASTCALL
+co_IntSetParent(PWINDOW_OBJECT Wnd, PWINDOW_OBJECT WndNewParent)
+{
+ PWINDOW_OBJECT WndOldParent, Sibling, InsertAfter;
+// HWND hWnd, hWndNewParent;
+ BOOL WasVisible;
+
+ ASSERT(Wnd);
+ ASSERT(WndNewParent);
+ ASSERT_REFS_CO(Wnd);
+ ASSERT_REFS_CO(WndNewParent);
+
+// hWnd = Wnd->hSelf;
+// hWndNewParent = WndNewParent->hSelf;
+
+ /* Some applications try to set a child as a parent */
+ if (IntIsChildWindow(Wnd, WndNewParent))
+ {
+ SetLastWin32Error( ERROR_INVALID_PARAMETER );
+ return NULL;
+ }
+
+ /*
+ * Windows hides the window first, then shows it again
+ * including the WM_SHOWWINDOW messages and all
+ */
+ WasVisible = co_WinPosShowWindow(Wnd, SW_HIDE);
+
+// /* Validate that window and parent still exist */
+// if (!IntIsWindow(hWnd) || !IntIsWindow(hWndNewParent))
+// return NULL;
+
+ /* Window must belong to current process */
+ if (Wnd->pti->pEThread->ThreadsProcess != PsGetCurrentProcess())
+ return NULL;
+
+ WndOldParent = Wnd->spwndParent;
+
+ if (WndOldParent) UserReferenceObject(WndOldParent); /* caller must deref */
+
+ if (WndNewParent != WndOldParent)
+ {
+ IntUnlinkWindow(Wnd);
+ InsertAfter = NULL;
+ if (0 == (Wnd->Wnd->ExStyle & WS_EX_TOPMOST))
+ {
+ /* Not a TOPMOST window, put after TOPMOSTs of new parent */
+ Sibling = WndNewParent->spwndChild;
+ while (NULL != Sibling && 0 != (Sibling->Wnd->ExStyle & WS_EX_TOPMOST))
+ {
+ InsertAfter = Sibling;
+ Sibling = Sibling->spwndNext;
+ }
+ }
+ if (NULL == InsertAfter)
+ {
+ IntLinkWindow(Wnd, WndNewParent, InsertAfter /*prev sibling*/);
+ }
+ else
+ {
+// UserReferenceObject(InsertAfter);
+ IntLinkWindow(Wnd, WndNewParent, InsertAfter /*prev sibling*/);
+// UserDereferenceObject(InsertAfter);
+ }
+ }
+
+ /*
+ * SetParent additionally needs to make hwnd the top window
+ * in the z-order and send the expected WM_WINDOWPOSCHANGING and
+ * WM_WINDOWPOSCHANGED notification messages.
+ */
+ co_WinPosSetWindowPos(Wnd, (0 == (Wnd->Wnd->ExStyle & WS_EX_TOPMOST) ? HWND_TOP : HWND_TOPMOST),
+ 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE
+ | (WasVisible ? SWP_SHOWWINDOW : 0));
+
+ /*
+ * FIXME: a WM_MOVE is also generated (in the DefWindowProc handler
+ * for WM_WINDOWPOSCHANGED) in Windows, should probably remove SWP_NOMOVE
+ */
+
+ /*
+ * Validate that the old parent still exist, since it migth have been
+ * destroyed during the last callbacks to user-mode
+ */
+// if(WndOldParent)
+// {
+// if(!IntIsWindow(WndOldParent->hSelf))
+// {
+// UserDereferenceObject(WndOldParent);
+// return NULL;
+// }
+
+ /* don't dereference the window object here, it must be done by the caller
+ of IntSetParent() */
+// return WndOldParent;
+// }
+
+ return WndOldParent;//NULL;
+}
+
+BOOL FASTCALL
+IntSetSystemMenu(PWINDOW_OBJECT Window, PMENU_OBJECT Menu)
+{
+ PMENU_OBJECT OldMenu;
+ if(Window->SystemMenu)
+ {
+ OldMenu = IntGetMenuObject(Window->SystemMenu);
+ if(OldMenu)
+ {
+ OldMenu->MenuInfo.Flags &= ~ MF_SYSMENU;
+ IntReleaseMenuObject(OldMenu);
+ }
+ }
+
+ if(Menu)
+ {
+ /* FIXME check window style, propably return FALSE ? */
+ Window->SystemMenu = Menu->MenuInfo.Self;
+ Menu->MenuInfo.Flags |= MF_SYSMENU;
+ }
+ else
+ Window->SystemMenu = (HMENU)0;
+
+ return TRUE;
+}
+
+/* unlink the window from siblings and parent. children are kept in place. */
+VOID FASTCALL
+IntUnlinkWnd(PWND Wnd)
+{
+ if (Wnd->spwndNext)
+ Wnd->spwndNext->spwndPrev = Wnd->spwndPrev;
+
+ if (Wnd->spwndPrev)
+ Wnd->spwndPrev->spwndNext = Wnd->spwndNext;
+
+ if (Wnd->spwndParent && Wnd->spwndParent->spwndChild == Wnd)
+ Wnd->spwndParent->spwndChild = Wnd->spwndNext;
+
+ Wnd->spwndPrev = Wnd->spwndNext = Wnd->spwndParent = NULL;
+}
+
+
+/* unlink the window from siblings and parent. children are kept in place. */
+VOID FASTCALL
+IntUnlinkWindow(PWINDOW_OBJECT Wnd)
+{
+ PWINDOW_OBJECT WndParent = Wnd->spwndParent;
+
+ IntUnlinkWnd(Wnd->Wnd);
+
+ if (Wnd->spwndNext)
+ Wnd->spwndNext->spwndPrev = Wnd->spwndPrev;
+
+ if (Wnd->spwndPrev)
+ Wnd->spwndPrev->spwndNext = Wnd->spwndNext;
+ else if (WndParent && WndParent->spwndChild == Wnd)
+ WndParent->spwndChild = Wnd->spwndNext;
+
+ Wnd->spwndPrev = Wnd->spwndNext = Wnd->spwndParent = NULL;
+}
+
+BOOL FASTCALL
+IntIsWindowInDestroy(PWINDOW_OBJECT Window)
+{
+ return ((Window->state & WINDOWSTATUS_DESTROYING) == WINDOWSTATUS_DESTROYING);
+}
+
+
+BOOL
+FASTCALL
+IntGetWindowPlacement(PWINDOW_OBJECT Window, WINDOWPLACEMENT *lpwndpl)
+{
+ PWND Wnd;
+ POINT Size;
+
+ Wnd = Window->Wnd;
+ if (!Wnd) return FALSE;
+
+ if(lpwndpl->length != sizeof(WINDOWPLACEMENT))
+ {
+ return FALSE;
+ }
+
+ lpwndpl->flags = 0;
+ if (0 == (Wnd->style & WS_VISIBLE))
+ {
+ lpwndpl->showCmd = SW_HIDE;
+ }
+ else if (0 != (Window->state & WINDOWOBJECT_RESTOREMAX) ||
+ 0 != (Wnd->style & WS_MAXIMIZE))
+ {
+ lpwndpl->showCmd = SW_MAXIMIZE;
+ }
+ else if (0 != (Wnd->style & WS_MINIMIZE))
+ {
+ lpwndpl->showCmd = SW_MINIMIZE;
+ }
+ else if (0 != (Wnd->style & WS_VISIBLE))
+ {
+ lpwndpl->showCmd = SW_SHOWNORMAL;
+ }
+
+ Size.x = Wnd->rcWindow.left;
+ Size.y = Wnd->rcWindow.top;
+ WinPosInitInternalPos(Window, &Size,
+ &Wnd->rcWindow);
+
+ lpwndpl->rcNormalPosition = Wnd->InternalPos.NormalRect;
+ lpwndpl->ptMinPosition = Wnd->InternalPos.IconPos;
+ lpwndpl->ptMaxPosition = Wnd->InternalPos.MaxPos;
+
+ return TRUE;
+}
+
+
+/* FUNCTIONS *****************************************************************/
+
+/*
+ * @unimplemented
+ */
+DWORD APIENTRY
+NtUserAlterWindowStyle(DWORD Unknown0,
+ DWORD Unknown1,
+ DWORD Unknown2)
+{
+ UNIMPLEMENTED
+
+ return(0);
+}
+
+/*
+ * As best as I can figure, this function is used by EnumWindows,
+ * EnumChildWindows, EnumDesktopWindows, & EnumThreadWindows.
+ *
+ * It's supposed to build a list of HWNDs to return to the caller.
+ * We can figure out what kind of list by what parameters are
+ * passed to us.
+ */
+/*
+ * @implemented
+ */
+NTSTATUS
+APIENTRY
+NtUserBuildHwndList(
+ HDESK hDesktop,
+ HWND hwndParent,
+ BOOLEAN bChildren,
+ ULONG dwThreadId,
+ ULONG lParam,
+ HWND* pWnd,
+ ULONG* pBufSize)
+{
+ NTSTATUS Status;
+ ULONG dwCount = 0;
+
+ if (pBufSize == 0)
+ return ERROR_INVALID_PARAMETER;
+
+ if (hwndParent || !dwThreadId)
+ {
+ PDESKTOP Desktop;
+ PWINDOW_OBJECT Parent, Window;
+
+ if(!hwndParent)
+ {
+ if(hDesktop == NULL && !(Desktop = IntGetActiveDesktop()))
+ {
+ return ERROR_INVALID_HANDLE;
+ }
+
+ if(hDesktop)
+ {
+ Status = IntValidateDesktopHandle(hDesktop,
+ UserMode,
+ 0,
+ &Desktop);
+ if(!NT_SUCCESS(Status))
+ {
+ return ERROR_INVALID_HANDLE;
+ }
+ }
+ hwndParent = Desktop->DesktopWindow;
+ }
+ else
+ {
+ hDesktop = 0;
+ }
+
+ if((Parent = UserGetWindowObject(hwndParent)) &&
+ (Window = Parent->spwndChild))
+ {
+ BOOL bGoDown = TRUE;
+
+ Status = STATUS_SUCCESS;
+ while(TRUE)
+ {
+ if (bGoDown)
+ {
+ if(dwCount++ < *pBufSize && pWnd)
+ {
+ _SEH2_TRY
+ {
+ ProbeForWrite(pWnd, sizeof(HWND), 1);
+ *pWnd = Window->hSelf;
+ pWnd++;
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ Status = _SEH2_GetExceptionCode();
+ }
+ _SEH2_END
+ if(!NT_SUCCESS(Status))
+ {
+ SetLastNtError(Status);
+ break;
+ }
+ }
+ if (Window->spwndChild && bChildren)
+ {
+ Window = Window->spwndChild;
+ continue;
+ }
+ bGoDown = FALSE;
+ }
+ if (Window->spwndNext)
+ {
+ Window = Window->spwndNext;
+ bGoDown = TRUE;
+ continue;
+ }
+ Window = Window->spwndParent;
+ if (Window == Parent)
+ {
+ break;
+ }
+ }
+ }
+
+ if(hDesktop)
+ {
+ ObDereferenceObject(Desktop);
+ }
+ }
+ else
+ {
+ PETHREAD Thread;
+ PTHREADINFO W32Thread;
+ PLIST_ENTRY Current;
+ PWINDOW_OBJECT Window;
+
+ Status = PsLookupThreadByThreadId((HANDLE)dwThreadId, &Thread);
+ if(!NT_SUCCESS(Status))
+ {
+ return ERROR_INVALID_PARAMETER;
+ }
+ if(!(W32Thread = (PTHREADINFO)Thread->Tcb.Win32Thread))
+ {
+ ObDereferenceObject(Thread);
+ DPRINT("Thread is not a GUI Thread!\n");
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ Current = W32Thread->WindowListHead.Flink;
+ while(Current != &(W32Thread->WindowListHead))
+ {
+ Window = CONTAINING_RECORD(Current, WINDOW_OBJECT, ThreadListEntry);
+ ASSERT(Window);
+
+ if(bChildren || Window->spwndOwner != NULL)
+ {
+ if(dwCount < *pBufSize && pWnd)
+ {
+ Status = MmCopyToCaller(pWnd++, &Window->hSelf, sizeof(HWND));
+ if(!NT_SUCCESS(Status))
+ {
+ SetLastNtError(Status);
+ break;
+ }
+ }
+ dwCount++;
+ }
+ Current = Current->Flink;
+ }
+
+ ObDereferenceObject(Thread);
+ }
+
+ *pBufSize = dwCount;
+ return STATUS_SUCCESS;
+}
+
+
+/*
+ * @implemented
+ */
+HWND APIENTRY
+NtUserChildWindowFromPointEx(HWND hwndParent,
+ LONG x,
+ LONG y,
+ UINT uiFlags)
+{
+ PWINDOW_OBJECT Parent;
+ POINTL Pt;
+ HWND Ret;
+ HWND *List, *phWnd;
+
+ if(!(Parent = UserGetWindowObject(hwndParent)))
+ {
+ return NULL;
+ }
+
+ Pt.x = x;
+ Pt.y = y;
+
+ if(Parent->hSelf != IntGetDesktopWindow())
+ {
+ Pt.x += Parent->Wnd->rcClient.left;
+ Pt.y += Parent->Wnd->rcClient.top;
+ }
+
+ if(!IntPtInWindow(Parent, Pt.x, Pt.y))
+ {
+ return NULL;
+ }
+
+ Ret = Parent->hSelf;
+ if((List = IntWinListChildren(Parent)))
+ {
+ for(phWnd = List; *phWnd; phWnd++)
+ {
+ PWINDOW_OBJECT Child;
+ PWND ChildWnd;
+ if((Child = UserGetWindowObject(*phWnd)))
+ {
+ ChildWnd = Child->Wnd;
+ if(!(ChildWnd->style & WS_VISIBLE) && (uiFlags & CWP_SKIPINVISIBLE))
+ {
+ continue;
+ }
+ if((ChildWnd->style & WS_DISABLED) && (uiFlags & CWP_SKIPDISABLED))
+ {
+ continue;
+ }
+ if((ChildWnd->ExStyle & WS_EX_TRANSPARENT) && (uiFlags & CWP_SKIPTRANSPARENT))
+ {
+ continue;
+ }
+ if(IntPtInWindow(Child, Pt.x, Pt.y))
+ {
+ Ret = Child->hSelf;
+ break;
+ }
+ }
+ }
+ ExFreePool(List);
+ }
+
+ return Ret;
+}
+
+void FASTCALL
+IntFixWindowCoordinates(CREATESTRUCTW* Cs, PWINDOW_OBJECT ParentWindow, DWORD* dwShowMode)
+{
+#define IS_DEFAULT(x) ((x) == CW_USEDEFAULT || (x) == (SHORT)0x8000)
+
+ /* default positioning for overlapped windows */
+ if(!(Cs->style & (WS_POPUP | WS_CHILD)))
+ {
- rc = WorkArea;
++ RECTL WorkArea;
+ PRTL_USER_PROCESS_PARAMETERS ProcessParams;
+
+ UserSystemParametersInfo(SPI_GETWORKAREA, 0, &WorkArea, 0);
+
- if(!Window || !Wnd)
+ ProcessParams = PsGetCurrentProcess()->Peb->ProcessParameters;
+
+ if (IS_DEFAULT(Cs->x))
+ {
+ if (!IS_DEFAULT(Cs->y)) *dwShowMode = Cs->y;
+
+ if(ProcessParams->WindowFlags & STARTF_USEPOSITION)
+ {
+ Cs->x = ProcessParams->StartingX;
+ Cs->y = ProcessParams->StartingY;
+ }
+ else
+ {
+ Cs->x = WorkArea.left;
+ Cs->y = WorkArea.top;
+ }
+ }
+
+ if (IS_DEFAULT(Cs->cx))
+ {
+ if (ProcessParams->WindowFlags & STARTF_USEPOSITION)
+ {
+ Cs->cx = ProcessParams->CountX;
+ Cs->cy = ProcessParams->CountY;
+ }
+ else
+ {
+ Cs->cx = (WorkArea.right - WorkArea.left) * 3 / 4 - Cs->x;
+ Cs->cy = (WorkArea.bottom - WorkArea.top) * 3 / 4 - Cs->y;
+ }
+ }
+ /* neither x nor cx are default. Check the y values .
+ * In the trace we see Outlook and Outlook Express using
+ * cy set to CW_USEDEFAULT when opening the address book.
+ */
+ else if (IS_DEFAULT(Cs->cy))
+ {
+ DPRINT("Strange use of CW_USEDEFAULT in nHeight\n");
+ Cs->cy = (WorkArea.bottom - WorkArea.top) * 3 / 4 - Cs->y;
+ }
+ }
+ else
+ {
+ /* if CW_USEDEFAULT is set for non-overlapped windows, both values are set to zero */
+ if(IS_DEFAULT(Cs->x))
+ {
+ Cs->x = 0;
+ Cs->y = 0;
+ }
+ if(IS_DEFAULT(Cs->cx))
+ {
+ Cs->cx = 0;
+ Cs->cy = 0;
+ }
+ }
+
+#undef IS_DEFAULT
+}
+
+/* Allocates and initializes a window*/
+PWINDOW_OBJECT FASTCALL IntCreateWindow(CREATESTRUCTW* Cs,
+ PLARGE_STRING WindowName,
+ PCLS Class,
+ PWINDOW_OBJECT ParentWindow,
+ PWINDOW_OBJECT OwnerWindow)
+{
+ PWND Wnd = NULL;
+ PWINDOW_OBJECT Window;
+ HWND hWnd;
+ PTHREADINFO pti = NULL;
+ PMENU_OBJECT SystemMenu;
+ BOOL MenuChanged;
+ BOOL bUnicodeWindow;
+
+ pti = PsGetCurrentThreadWin32Thread();
+
+ /* Automatically add WS_EX_WINDOWEDGE */
+ if ((Cs->dwExStyle & WS_EX_DLGMODALFRAME) ||
+ ((!(Cs->dwExStyle & WS_EX_STATICEDGE)) &&
+ (Cs->style & (WS_DLGFRAME | WS_THICKFRAME))))
+ Cs->dwExStyle |= WS_EX_WINDOWEDGE;
+ else
+ Cs->dwExStyle &= ~WS_EX_WINDOWEDGE;
+
+ /* Is it a unicode window? */
+ bUnicodeWindow =!(Cs->dwExStyle & WS_EX_SETANSICREATOR);
+ Cs->dwExStyle &= ~WS_EX_SETANSICREATOR;
+
+ /* Allocate the new window */
+ Window = (PWINDOW_OBJECT) UserCreateObject( gHandleTable,
+ pti->rpdesk,
+ (PHANDLE)&hWnd,
+ otWindow,
+ sizeof(WINDOW_OBJECT));
++ if (!Window)
++ {
++ goto AllocError;
++ }
+
+ Wnd = DesktopHeapAlloc(pti->rpdesk, sizeof(WND) + Class->cbwndExtra);
+
- else
++ if (!Wnd)
+ {
+ goto AllocError;
+ }
+
+ RtlZeroMemory(Wnd, sizeof(WND) + Class->cbwndExtra);
+
+ DPRINT("Created object with handle %X\n", hWnd);
+
+ if (NULL == pti->rpdesk->DesktopWindow)
+ {
+ /* If there is no desktop window yet, we must be creating it */
+ pti->rpdesk->DesktopWindow = hWnd;
+ pti->rpdesk->pDeskInfo->spwnd = Wnd;
+ }
+
+ /*
+ * Fill out the structure describing it.
+ */
+ Window->Wnd = Wnd;
+ Window->pti = pti;
+ Window->hSelf = hWnd;
+ Window->spwndParent = ParentWindow;
+ Window->spwndOwner = OwnerWindow;
+
+ Wnd->head.h = hWnd;
+ Wnd->head.pti = pti;
+ Wnd->head.rpdesk = pti->rpdesk;
+ Wnd->fnid = 0;
+ Wnd->hWndLastActive = hWnd;
+ Wnd->state2 |= WNDS2_WIN40COMPAT;
+ Wnd->pcls = Class;
+ Wnd->hModule = Cs->hInstance;
+ Wnd->style = Cs->style & ~WS_VISIBLE;
+ Wnd->ExStyle = Cs->dwExStyle;
+ Wnd->cbwndExtra = Wnd->pcls->cbwndExtra;
+ Wnd->spwndOwner = OwnerWindow ? OwnerWindow->Wnd : NULL;
+ Wnd->spwndParent = ParentWindow ? ParentWindow->Wnd : NULL;
+
+ IntReferenceMessageQueue(Window->pti->MessageQueue);
+ if (Wnd->spwndParent != NULL && Cs->hwndParent != 0)
+ {
+ Wnd->HideFocus = Wnd->spwndParent->HideFocus;
+ Wnd->HideAccel = Wnd->spwndParent->HideAccel;
+ }
+
+ if (Wnd->pcls->CSF_flags & CSF_SERVERSIDEPROC)
+ Wnd->state |= WNDS_SERVERSIDEWINDOWPROC;
+
+ /* BugBoy Comments: Comment below say that System classes are always created
+ as UNICODE. In windows, creating a window with the ANSI version of CreateWindow
+ sets the window to ansi as verified by testing with IsUnicodeWindow API.
+
+ No where can I see in code or through testing does the window change back
+ to ANSI after being created as UNICODE in ROS. I didnt do more testing to
+ see what problems this would cause.*/
+
+ // Set WndProc from Class.
+ Wnd->lpfnWndProc = Wnd->pcls->lpfnWndProc;
+
+ // GetWindowProc, test for non server side default classes and set WndProc.
+ if ( Wnd->pcls->fnid <= FNID_GHOST && Wnd->pcls->fnid >= FNID_BUTTON )
+ {
+ if (bUnicodeWindow)
+ {
+ if (GETPFNCLIENTA(Wnd->pcls->fnid) == Wnd->lpfnWndProc)
+ Wnd->lpfnWndProc = GETPFNCLIENTW(Wnd->pcls->fnid);
+ }
+ else
+ {
+ if (GETPFNCLIENTW(Wnd->pcls->fnid) == Wnd->lpfnWndProc)
+ Wnd->lpfnWndProc = GETPFNCLIENTA(Wnd->pcls->fnid);
+ }
+ }
+
+ // If not an Unicode caller, set Ansi creator bit.
+ if (!bUnicodeWindow) Wnd->state |= WNDS_ANSICREATOR;
+
+ // Clone Class Ansi/Unicode proc type.
+ if (Wnd->pcls->CSF_flags & CSF_ANSIPROC)
+ {
+ Wnd->state |= WNDS_ANSIWINDOWPROC;
+ Wnd->Unicode = FALSE;
+ }
+ else
+ { /*
+ It seems there can be both an Ansi creator and Unicode Class Window
+ WndProc, unless the following overriding conditions occur:
+ */
+ if ( !bUnicodeWindow &&
+ ( Class->atomClassName == gpsi->atomSysClass[ICLS_BUTTON] ||
+ Class->atomClassName == gpsi->atomSysClass[ICLS_COMBOBOX] ||
+ Class->atomClassName == gpsi->atomSysClass[ICLS_COMBOLBOX] ||
+ Class->atomClassName == gpsi->atomSysClass[ICLS_DIALOG] ||
+ Class->atomClassName == gpsi->atomSysClass[ICLS_EDIT] ||
+ Class->atomClassName == gpsi->atomSysClass[ICLS_IME] ||
+ Class->atomClassName == gpsi->atomSysClass[ICLS_LISTBOX] ||
+ Class->atomClassName == gpsi->atomSysClass[ICLS_MDICLIENT] ||
+ Class->atomClassName == gpsi->atomSysClass[ICLS_STATIC] ) )
+ { // Override Class and set the window Ansi WndProc.
+ Wnd->state |= WNDS_ANSIWINDOWPROC;
+ Wnd->Unicode = FALSE;
+ }
+ else
+ { // Set the window Unicode WndProc.
+ Wnd->state &= ~WNDS_ANSIWINDOWPROC;
+ Wnd->Unicode = TRUE;
+ }
+ }
+
+ /* BugBoy Comments: if the window being created is a edit control, ATOM 0xCxxx,
+ then my testing shows that windows (2k and XP) creates a CallProc for it immediately
+ Dont understand why it does this. */
+ if (Class->atomClassName == gpsi->atomSysClass[ICLS_EDIT])
+ {
+ PCALLPROCDATA CallProc;
+ //CallProc = CreateCallProc(NULL, Wnd->lpfnWndProc, bUnicodeWindow, Wnd->ti->ppi);
+ CallProc = CreateCallProc(NULL, Wnd->lpfnWndProc, Wnd->Unicode , Wnd->head.pti->ppi);
+
+ if (!CallProc)
+ {
+ SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
+ DPRINT1("Warning: Unable to create CallProc for edit control. Control may not operate correctly! hwnd %x\n",hWnd);
+ }
+ else
+ {
+ UserAddCallProcToClass(Wnd->pcls, CallProc);
+ }
+ }
+
+ InitializeListHead(&Wnd->PropListHead);
+
+ if ( WindowName->Buffer != NULL && WindowName->Length > 0 )
+ {
+ Wnd->strName.Buffer = DesktopHeapAlloc(Wnd->head.rpdesk,
+ WindowName->Length + sizeof(UNICODE_NULL));
+ if (Wnd->strName.Buffer == NULL)
+ {
+ goto AllocError;
+ }
+
+ RtlCopyMemory(Wnd->strName.Buffer, WindowName->Buffer, WindowName->Length);
+ Wnd->strName.Buffer[WindowName->Length / sizeof(WCHAR)] = L'\0';
+ Wnd->strName.Length = WindowName->Length;
+ }
+
+ /* Correct the window style. */
+ if ((Wnd->style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
+ {
+ Wnd->style |= WS_CLIPSIBLINGS;
+ if (!(Wnd->style & WS_POPUP))
+ {
+ Wnd->style |= WS_CAPTION;
+ Window->state |= WINDOWOBJECT_NEED_SIZE;
+ }
+ }
+
+ if ((Wnd->ExStyle & WS_EX_DLGMODALFRAME) ||
+ (Wnd->style & (WS_DLGFRAME | WS_THICKFRAME)))
+ Wnd->ExStyle |= WS_EX_WINDOWEDGE;
+ else
+ Wnd->ExStyle &= ~WS_EX_WINDOWEDGE;
+
+ /* create system menu */
+ if((Cs->style & WS_SYSMENU) )//&& (dwStyle & WS_CAPTION) == WS_CAPTION)
+ {
+ SystemMenu = IntGetSystemMenu(Window, TRUE, TRUE);
+ if(SystemMenu)
+ {
+ Window->SystemMenu = SystemMenu->MenuInfo.Self;
+ IntReleaseMenuObject(SystemMenu);
+ }
+ }
+
+ /* Set the window menu */
+ if ((Cs->style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
+ {
+ if (Cs->hMenu)
+ IntSetMenu(Window, Cs->hMenu, &MenuChanged);
+ else if (Wnd->pcls->lpszMenuName) // Take it from the parent.
+ {
+ UNICODE_STRING MenuName;
+ HMENU hMenu;
+
+ if (IS_INTRESOURCE(Wnd->pcls->lpszMenuName))
+ {
+ MenuName.Length = 0;
+ MenuName.MaximumLength = 0;
+ MenuName.Buffer = Wnd->pcls->lpszMenuName;
+ }
+ else
+ {
+ RtlInitUnicodeString( &MenuName, Wnd->pcls->lpszMenuName);
+ }
+ hMenu = co_IntCallLoadMenu( Wnd->pcls->hModule, &MenuName);
+ if (hMenu) IntSetMenu(Window, hMenu, &MenuChanged);
+ }
+ }
+ else // Not a child
+ Wnd->IDMenu = (UINT) Cs->hMenu;
+
+ /* Insert the window into the thread's window list. */
+ InsertTailList (&pti->WindowListHead, &Window->ThreadListEntry);
+
+ /* Handle "CS_CLASSDC", it is tested first. */
+ if ( (Wnd->pcls->style & CS_CLASSDC) && !(Wnd->pcls->pdce) )
+ { /* One DCE per class to have CLASS. */
+ Wnd->pcls->pdce = DceAllocDCE( Window, DCE_CLASS_DC );
+ }
+ else if ( Wnd->pcls->style & CS_OWNDC)
+ { /* Allocate a DCE for this window. */
+ DceAllocDCE(Window, DCE_WINDOW_DC);
+ }
+
+ return Window;
+
+AllocError:
+
+ if(Window)
+ UserDereferenceObject(Window);
+
+ if(Wnd)
+ DesktopHeapFree(Wnd->head.rpdesk, Wnd);
+
+ SetLastNtError(STATUS_INSUFFICIENT_RESOURCES);
+ return NULL;
+}
+
+/*
+ * @implemented
+ */
+PWND FASTCALL
+co_UserCreateWindowEx(CREATESTRUCTW* Cs,
+ PUNICODE_STRING ClassName,
+ PLARGE_STRING WindowName)
+{
+ PWINDOW_OBJECT Window = NULL, ParentWindow = NULL, OwnerWindow;
+ HWND hWnd, hWndParent, hWndOwner;
+ DWORD dwStyle;
+ PWINSTATION_OBJECT WinSta;
+ PWND Wnd = NULL;
+ PCLS Class = NULL;
+ SIZE Size;
+ POINT MaxPos;
+ CBT_CREATEWNDW CbtCreate;
+ LRESULT Result;
+ USER_REFERENCE_ENTRY ParentRef, Ref;
+ PTHREADINFO pti;
+ DWORD dwShowMode = SW_SHOW;
+ DECLARE_RETURN(PWND);
+
+ /* Get the current window station and reference it */
+ pti = GetW32ThreadInfo();
+ if (pti == NULL || pti->rpdesk == NULL)
+ {
+ DPRINT1("Thread is not attached to a desktop! Cannot create window!\n");
+ return NULL; //There is nothing to cleanup
+ }
+ WinSta = pti->rpdesk->rpwinstaParent;
+ ObReferenceObjectByPointer(WinSta, KernelMode, ExWindowStationObjectType, 0);
+
+ /* Get the class and reference it*/
+ Class = IntGetAndReferenceClass(ClassName, Cs->hInstance);
+ if(!Class)
+ {
+ DPRINT1("Failed to find class %wZ\n", ClassName);
+ RETURN(NULL);
+ }
+
+ /* Now find the parent and the owner window */
+ hWndParent = IntGetDesktopWindow();
+ hWndOwner = NULL;
+
+ if (Cs->hwndParent == HWND_MESSAGE)
+ {
+ Cs->hwndParent = hWndParent = IntGetMessageWindow();
+ }
+ else if (Cs->hwndParent)
+ {
+ if ((Cs->style & (WS_CHILD|WS_POPUP)) != WS_CHILD)
+ hWndOwner = Cs->hwndParent;
+ else
+ hWndParent = Cs->hwndParent;
+ }
+ else if ((Cs->style & (WS_CHILD|WS_POPUP)) == WS_CHILD)
+ {
+ DPRINT1("Cannot create a child window without a parrent!\n");
+ SetLastWin32Error(ERROR_TLW_WITH_WSCHILD);
+ RETURN(NULL); /* WS_CHILD needs a parent, but WS_POPUP doesn't */
+ }
+
+ ParentWindow = hWndParent ? UserGetWindowObject(hWndParent): NULL;
+ OwnerWindow = hWndOwner ? UserGetWindowObject(hWndOwner): NULL;
+
+ /* FIXME: is this correct?*/
+ if(OwnerWindow)
+ OwnerWindow = UserGetAncestor(OwnerWindow, GA_ROOT);
+
+ /* Fix the position and the size of the window */
+ if (ParentWindow)
+ {
+ UserRefObjectCo(ParentWindow, &ParentRef);
+ IntFixWindowCoordinates(Cs, ParentWindow, &dwShowMode);
+ }
+
+ /* Allocate and initialize the new window */
+ Window = IntCreateWindow(Cs,
+ WindowName,
+ Class,
+ ParentWindow,
+ OwnerWindow);
+ if(!Window)
+ {
+ DPRINT1("IntCreateWindow failed!\n");
+ RETURN(0);
+ }
+
+ Wnd = Window->Wnd;
+ hWnd = Window->hSelf;
+
+ UserRefObjectCo(Window, &Ref);
+ ObDereferenceObject(WinSta);
+
+ /* Call the WH_CBT hook */
+ dwStyle = Cs->style;
+ Cs->style = Wnd->style; /* HCBT_CREATEWND needs the real window style */
+ CbtCreate.lpcs = Cs;
+ CbtCreate.hwndInsertAfter = HWND_TOP;
+ if (ISITHOOKED(WH_CBT))
+ {
+ if (co_HOOK_CallHooks(WH_CBT, HCBT_CREATEWND, (WPARAM) hWnd, (LPARAM) &CbtCreate))
+ {
+ DPRINT1("HCBT_CREATEWND hook failed!\n");
+ RETURN( (PWND) NULL);
+ }
+ }
+ Cs->style = dwStyle; /* NCCREATE and WM_NCCALCSIZE need the original values*/
+
+ /* Send the WM_GETMINMAXINFO message*/
+ Size.cx = Cs->cx;
+ Size.cy = Cs->cy;
+
+ if ((dwStyle & WS_THICKFRAME) || !(dwStyle & (WS_POPUP | WS_CHILD)))
+ {
+ POINT MaxSize, MaxPos, MinTrack, MaxTrack;
+
+ co_WinPosGetMinMaxInfo(Window, &MaxSize, &MaxPos, &MinTrack, &MaxTrack);
+ if (Size.cx > MaxTrack.x) Size.cx = MaxTrack.x;
+ if (Size.cy > MaxTrack.y) Size.cy = MaxTrack.y;
+ if (Size.cx < MinTrack.x) Size.cx = MinTrack.x;
+ if (Size.cy < MinTrack.y) Size.cy = MinTrack.y;
+ }
+
+ Wnd->rcWindow.left = Cs->x;
+ Wnd->rcWindow.top = Cs->y;
+ Wnd->rcWindow.right = Cs->x + Size.cx;
+ Wnd->rcWindow.bottom = Cs->y + Size.cy;
+ if (0 != (Wnd->style & WS_CHILD) && ParentWindow)
+ {
+ RECTL_vOffsetRect(&Wnd->rcWindow,
+ ParentWindow->Wnd->rcClient.left,
+ ParentWindow->Wnd->rcClient.top);
+ }
+ Wnd->rcClient = Wnd->rcWindow;
+
+
+ /* Link the window*/
+ if (NULL != ParentWindow)
+ {
+ /* link the window into the parent's child list */
+ if ((dwStyle & (WS_CHILD|WS_MAXIMIZE)) == WS_CHILD)
+ {
+ PWINDOW_OBJECT PrevSibling;
+
+ PrevSibling = ParentWindow->spwndChild;
+
+ if(PrevSibling)
+ {
+ while (PrevSibling->spwndNext)
+ PrevSibling = PrevSibling->spwndNext;
+ }
+
+ /* link window as bottom sibling */
+ IntLinkWindow(Window, ParentWindow, PrevSibling /*prev sibling*/);
+ }
+ else
+ {
+ /* link window as top sibling (but after topmost siblings) */
+ PWINDOW_OBJECT InsertAfter, Sibling;
+ if (!(Cs->dwExStyle & WS_EX_TOPMOST))
+ {
+ InsertAfter = NULL;
+ Sibling = ParentWindow->spwndChild;
+ while (Sibling && (Sibling->Wnd->ExStyle & WS_EX_TOPMOST))
+ {
+ InsertAfter = Sibling;
+ Sibling = Sibling->spwndNext;
+ }
+ }
+ else
+ {
+ InsertAfter = NULL;
+ }
+
+ IntLinkWindow(Window, ParentWindow, InsertAfter /* prev sibling */);
+ }
+ }
+
+ /* Send the NCCREATE message */
+ Result = co_IntSendMessage(Window->hSelf, WM_NCCREATE, 0, (LPARAM) Cs);
+ if (!Result)
+ {
+ DPRINT1("co_UserCreateWindowEx(): NCCREATE message failed\n");
+ RETURN((PWND)0);
+ }
+
+ /* Send the WM_NCCALCSIZE message */
+ MaxPos.x = Window->Wnd->rcWindow.left;
+ MaxPos.y = Window->Wnd->rcWindow.top;
+
+ Result = co_WinPosGetNonClientSize(Window, &Wnd->rcWindow, &Wnd->rcClient);
+
+ RECTL_vOffsetRect(&Wnd->rcWindow, MaxPos.x - Wnd->rcWindow.left,
+ MaxPos.y - Wnd->rcWindow.top);
+
+
+ /* Send the WM_CREATE message. */
+ Result = co_IntSendMessage(Window->hSelf, WM_CREATE, 0, (LPARAM) Cs);
+ if (Result == (LRESULT)-1)
+ {
+ DPRINT1("co_UserCreateWindowEx(): WM_CREATE message failed\n");
+ IntUnlinkWindow(Window);
+ RETURN((PWND)0);
+ }
+
+ /* Send the EVENT_OBJECT_CREATE event*/
+ IntNotifyWinEvent(EVENT_OBJECT_CREATE, Window->Wnd, OBJID_WINDOW, 0);
+
+ /* By setting the flag below it can be examined to determine if the window
+ was created successfully and a valid pwnd was passed back to caller since
+ from here the function has to succeed. */
+ Window->Wnd->state2 |= WNDS2_WMCREATEMSGPROCESSED;
+
+ /* Send the WM_SIZE and WM_MOVE messages. */
+ if (!(Window->state & WINDOWOBJECT_NEED_SIZE))
+ {
+ co_WinPosSendSizeMove(Window);
+ }
+
+ /* Show or maybe minimize or maximize the window. */
+ if (Wnd->style & (WS_MINIMIZE | WS_MAXIMIZE))
+ {
+ RECTL NewPos;
+ UINT16 SwFlag;
+
+ SwFlag = (Wnd->style & WS_MINIMIZE) ? SW_MINIMIZE : SW_MAXIMIZE;
+
+ co_WinPosMinMaximize(Window, SwFlag, &NewPos);
+
+ SwFlag = ((Wnd->style & WS_CHILD) || UserGetActiveWindow()) ?
+ SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED :
+ SWP_NOZORDER | SWP_FRAMECHANGED;
+
+ co_WinPosSetWindowPos(Window, 0, NewPos.left, NewPos.top,
+ NewPos.right, NewPos.bottom, SwFlag);
+ }
+
+ /* Send the WM_PARENTNOTIFY message */
+ if ((Wnd->style & WS_CHILD) &&
+ (!(Wnd->ExStyle & WS_EX_NOPARENTNOTIFY)) && ParentWindow)
+ {
+ co_IntSendMessage(ParentWindow->hSelf,
+ WM_PARENTNOTIFY,
+ MAKEWPARAM(WM_CREATE, Wnd->IDMenu),
+ (LPARAM)Window->hSelf);
+ }
+
+ /* Notify the shell that a new window was created */
+ if ((!hWndParent) && (!hWndOwner))
+ {
+ co_IntShellHookNotify(HSHELL_WINDOWCREATED, (LPARAM)hWnd);
+ }
+
+ /* Initialize and show the window's scrollbars */
+ if (Wnd->style & WS_VSCROLL)
+ {
+ co_UserShowScrollBar(Window, SB_VERT, TRUE);
+ }
+ if (Wnd->style & WS_HSCROLL)
+ {
+ co_UserShowScrollBar(Window, SB_HORZ, TRUE);
+ }
+
+ /* Show the new window */
+ if (Cs->style & WS_VISIBLE)
+ {
+ if (Wnd->style & WS_MAXIMIZE)
+ dwShowMode = SW_SHOW;
+ else if (Wnd->style & WS_MINIMIZE)
+ dwShowMode = SW_SHOWMINIMIZED;
+
+ co_WinPosShowWindow(Window, dwShowMode);
+
+ if (Wnd->ExStyle & WS_EX_MDICHILD)
+ {
+ co_IntSendMessage(ParentWindow->hSelf, WM_MDIREFRESHMENU, 0, 0);
+ /* ShowWindow won't activate child windows */
+ co_WinPosSetWindowPos(Window, HWND_TOP, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
+ }
+ }
+
+ DPRINT("co_UserCreateWindowEx(): Created window %X\n", hWnd);
+ RETURN( Wnd);
+
+CLEANUP:
+ if (!_ret_)
+ {
+ /* If the window was created, the class will be dereferenced by co_UserDestroyWindow */
+ if (Window)
+ co_UserDestroyWindow(Window);
- PWINDOW_OBJECT Parent, Old;
++ else if (Class)
+ IntDereferenceClass(Class, pti->pDeskInfo, pti->ppi);
+ }
+
+ if (Window)
+ {
+ UserDerefObjectCo(Window);
+ UserDereferenceObject(Window);
+ }
+ if (ParentWindow) UserDerefObjectCo(ParentWindow);
+
+ END_CLEANUP;
+}
+
+NTSTATUS
+NTAPI
+ProbeAndCaptureLargeString(
+ OUT PLARGE_STRING plstrSafe,
+ IN PLARGE_STRING plstrUnsafe)
+{
+ LARGE_STRING lstrTemp;
+ PVOID pvBuffer = NULL;
+
+ _SEH2_TRY
+ {
+ /* Probe and copy the string */
+ ProbeForRead(plstrUnsafe, sizeof(LARGE_STRING), sizeof(ULONG));
+ lstrTemp = *plstrUnsafe;
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ /* Fail */
+ _SEH2_YIELD(return _SEH2_GetExceptionCode();)
+ }
+ _SEH2_END
+
+ if (lstrTemp.Length != 0)
+ {
+ /* Allocate a buffer from paged pool */
+ pvBuffer = ExAllocatePoolWithTag(PagedPool, lstrTemp.Length, TAG_STRING);
+ if (!pvBuffer)
+ {
+ return STATUS_NO_MEMORY;
+ }
+
+ _SEH2_TRY
+ {
+ /* Probe and copy the buffer */
+ ProbeForRead(lstrTemp.Buffer, lstrTemp.Length, sizeof(WCHAR));
+ RtlCopyMemory(pvBuffer, lstrTemp.Buffer, lstrTemp.Length);
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ /* Cleanup and fail */
+ ExFreePool(pvBuffer);
+ _SEH2_YIELD(return _SEH2_GetExceptionCode();)
+ }
+ _SEH2_END
+ }
+
+ /* Set the output string */
+ plstrSafe->Buffer = pvBuffer;
+ plstrSafe->Length = lstrTemp.Length;
+ plstrSafe->MaximumLength = lstrTemp.Length;
+
+ return STATUS_SUCCESS;
+}
+
+/**
+ * \todo Allow passing plstrClassName as ANSI.
+ */
+HWND
+NTAPI
+NtUserCreateWindowEx(
+ DWORD dwExStyle,
+ PLARGE_STRING plstrClassName,
+ PLARGE_STRING plstrClsVersion,
+ PLARGE_STRING plstrWindowName,
+ DWORD dwStyle,
+ int x,
+ int y,
+ int nWidth,
+ int nHeight,
+ HWND hWndParent,
+ HMENU hMenu,
+ HINSTANCE hInstance,
+ LPVOID lpParam,
+ DWORD dwFlags,
+ PVOID acbiBuffer)
+{
+ NTSTATUS Status;
+ LARGE_STRING lstrWindowName;
+ LARGE_STRING lstrClassName;
+ UNICODE_STRING ustrClassName;
+ CREATESTRUCTW Cs;
+ HWND hwnd = NULL;
+ PWND pwnd;
+
+ lstrWindowName.Buffer = NULL;
+ lstrClassName.Buffer = NULL;
+
+ /* Check if we got a Window name */
+ if (plstrWindowName)
+ {
+ /* Copy the string to kernel mode */
+ Status = ProbeAndCaptureLargeString(&lstrWindowName, plstrWindowName);
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("NtUserCreateWindowEx: failed to capture plstrWindowName\n");
+ SetLastNtError(Status);
+ return NULL;
+ }
+ plstrWindowName = &lstrWindowName;
+ }
+
+ /* Check if the class is an atom */
+ if (IS_ATOM(plstrClassName))
+ {
+ /* It is, pass the atom in the UNICODE_STRING */
+ ustrClassName.Buffer = (PVOID)plstrClassName;
+ ustrClassName.Length = 0;
+ ustrClassName.MaximumLength = 0;
+ }
+ else
+ {
+ /* It's not, capture the class name */
+ Status = ProbeAndCaptureLargeString(&lstrClassName, plstrClassName);
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("NtUserCreateWindowEx: failed to capture plstrClassName\n");
+ /* Set last error, cleanup and return */
+ SetLastNtError(Status);
+ goto cleanup;
+ }
+
+ /* We pass it on as a UNICODE_STRING */
+ ustrClassName.Buffer = lstrClassName.Buffer;
+ ustrClassName.Length = lstrClassName.Length;
+ ustrClassName.MaximumLength = lstrClassName.MaximumLength;
+ }
+
+ /* Fill the CREATESTRUCTW */
+ /* we will keep here the original parameters */
+ Cs.style = dwStyle;
+ Cs.lpCreateParams = lpParam;
+ Cs.hInstance = hInstance;
+ Cs.hMenu = hMenu;
+ Cs.hwndParent = hWndParent;
+ Cs.cx = nWidth;
+ Cs.cy = nHeight;
+ Cs.x = x;
+ Cs.y = y;
+// Cs.lpszName = (LPCWSTR) WindowName->Buffer;
+// Cs.lpszClass = (LPCWSTR) ClassName->Buffer;
+ Cs.lpszName = (LPCWSTR) plstrWindowName;
+ Cs.lpszClass = (LPCWSTR) &ustrClassName;
+ Cs.dwExStyle = dwExStyle;
+
+ UserEnterExclusive();
+
+ /* Call the internal function */
+ pwnd = co_UserCreateWindowEx(&Cs, &ustrClassName, plstrWindowName);
+
+ if(!pwnd)
+ {
+ DPRINT1("co_UserCreateWindowEx failed!\n");
+ }
+ hwnd = pwnd ? UserHMGetHandle(pwnd) : NULL;
+
+ UserLeave();
+
+cleanup:
+ if (lstrWindowName.Buffer)
+ {
+ ExFreePoolWithTag(lstrWindowName.Buffer, TAG_STRING);
+ }
+ if (lstrClassName.Buffer)
+ {
+ ExFreePoolWithTag(lstrClassName.Buffer, TAG_STRING);
+ }
+
+ return hwnd;
+}
+
+/*
+ * @unimplemented
+ */
+HDWP APIENTRY
+NtUserDeferWindowPos(HDWP WinPosInfo,
+ HWND Wnd,
+ HWND WndInsertAfter,
+ int x,
+ int y,
+ int cx,
+ int cy,
+ UINT Flags)
+{
+ UNIMPLEMENTED
+
+ return 0;
+}
+
+
+BOOLEAN FASTCALL co_UserDestroyWindow(PWINDOW_OBJECT Window)
+{
+ BOOLEAN isChild;
+ PWND Wnd;
+ HWND hWnd;
+ PTHREADINFO ti;
+ MSG msg;
+
+ ASSERT_REFS_CO(Window); // FIXME: temp hack?
+
+ hWnd = Window->hSelf;
+
+ Wnd = Window->Wnd;
+
+ if (!Wnd) return TRUE; // FIXME: Need to finish object rewrite or lock the thread when killing the window!
+
+ DPRINT("co_UserDestroyWindow \n");
+
+ /* Check for owner thread */
+ if ( (Window->pti->pEThread != PsGetCurrentThread()) ||
+ Wnd->head.pti != PsGetCurrentThreadWin32Thread() )
+ {
+ SetLastWin32Error(ERROR_ACCESS_DENIED);
+ return FALSE;
+ }
+
+ /* If window was created successfully and it is hooked */
+ if ((Wnd->state2 & WNDS2_WMCREATEMSGPROCESSED) && (ISITHOOKED(WH_CBT)))
+ {
+ if (co_HOOK_CallHooks(WH_CBT, HCBT_DESTROYWND, (WPARAM) hWnd, 0)) return FALSE;
+ }
+
+ /* Look whether the focus is within the tree of windows we will
+ * be destroying.
+ */
+ if (!co_WinPosShowWindow(Window, SW_HIDE))
+ {
+ if (UserGetActiveWindow() == Window->hSelf)
+ {
+ co_WinPosActivateOtherWindow(Window);
+ }
+ }
+
+ if (Window->pti->MessageQueue->ActiveWindow == Window->hSelf)
+ Window->pti->MessageQueue->ActiveWindow = NULL;
+ if (Window->pti->MessageQueue->FocusWindow == Window->hSelf)
+ Window->pti->MessageQueue->FocusWindow = NULL;
+ if (Window->pti->MessageQueue->CaptureWindow == Window->hSelf)
+ Window->pti->MessageQueue->CaptureWindow = NULL;
+
+ /*
+ * Check if this window is the Shell's Desktop Window. If so set hShellWindow to NULL
+ */
+
+ ti = PsGetCurrentThreadWin32Thread();
+
+ if ((ti != NULL) & (ti->pDeskInfo != NULL))
+ {
+ if (ti->pDeskInfo->hShellWindow == hWnd)
+ {
+ DPRINT1("Destroying the ShellWindow!\n");
+ ti->pDeskInfo->hShellWindow = NULL;
+ }
+ }
+
+ IntDereferenceMessageQueue(Window->pti->MessageQueue);
+
+ IntEngWindowChanged(Window, WOC_DELETE);
+ isChild = (0 != (Wnd->style & WS_CHILD));
+
+#if 0 /* FIXME */
+
+ if (isChild)
+ {
+ if (! USER_IsExitingThread(GetCurrentThreadId()))
+ {
+ send_parent_notify(hwnd, WM_DESTROY);
+ }
+ }
+ else if (NULL != GetWindow(Wnd, GW_OWNER))
+ {
+ co_HOOK_CallHooks( WH_SHELL, HSHELL_WINDOWDESTROYED, (WPARAM)hwnd, 0L, TRUE );
+ /* FIXME: clean up palette - see "Internals" p.352 */
+ }
+#endif
+
+ if (!IntIsWindow(Window->hSelf))
+ {
+ return TRUE;
+ }
+
+ /* Recursively destroy owned windows */
+ if (! isChild)
+ {
+ for (;;)
+ {
+ BOOL GotOne = FALSE;
+ HWND *Children;
+ HWND *ChildHandle;
+ PWINDOW_OBJECT Child, Desktop;
+
+ Desktop = IntIsDesktopWindow(Window) ? Window :
+ UserGetWindowObject(IntGetDesktopWindow());
+ Children = IntWinListChildren(Desktop);
+
+ if (Children)
+ {
+ for (ChildHandle = Children; *ChildHandle; ++ChildHandle)
+ {
+ Child = UserGetWindowObject(*ChildHandle);
+ if (Child == NULL)
+ continue;
+ if (Child->spwndOwner != Window)
+ {
+ continue;
+ }
+
+ if (IntWndBelongsToThread(Child, PsGetCurrentThreadWin32Thread()))
+ {
+ USER_REFERENCE_ENTRY ChildRef;
+ UserRefObjectCo(Child, &ChildRef);//temp hack?
+ co_UserDestroyWindow(Child);
+ UserDerefObjectCo(Child);//temp hack?
+
+ GotOne = TRUE;
+ continue;
+ }
+
+ if (Child->spwndOwner != NULL)
+ {
+ Child->spwndOwner = NULL;
+ Child->Wnd->spwndOwner = NULL;
+ }
+
+ }
+ ExFreePool(Children);
+ }
+ if (! GotOne)
+ {
+ break;
+ }
+ }
+ }
+
+ /* Generate mouse move message for the next window */
+ msg.message = WM_MOUSEMOVE;
+ msg.wParam = IntGetSysCursorInfo()->ButtonsDown;
+ msg.lParam = MAKELPARAM(gpsi->ptCursor.x, gpsi->ptCursor.y);
+ msg.pt = gpsi->ptCursor;
+ MsqInsertSystemMessage(&msg);
+
+ if (!IntIsWindow(Window->hSelf))
+ {
+ return TRUE;
+ }
+
+ /* Destroy the window storage */
+ co_UserFreeWindow(Window, PsGetCurrentProcessWin32Process(), PsGetCurrentThreadWin32Thread(), TRUE);
+
+ return TRUE;
+}
+
+
+/*
+ * @implemented
+ */
+BOOLEAN APIENTRY
+NtUserDestroyWindow(HWND Wnd)
+{
+ PWINDOW_OBJECT Window;
+ DECLARE_RETURN(BOOLEAN);
+ BOOLEAN ret;
+ USER_REFERENCE_ENTRY Ref;
+
+ DPRINT("Enter NtUserDestroyWindow\n");
+ UserEnterExclusive();
+
+ if (!(Window = UserGetWindowObject(Wnd)))
+ {
+ RETURN(FALSE);
+ }
+
+ UserRefObjectCo(Window, &Ref);//faxme: dunno if win should be reffed during destroy..
+ ret = co_UserDestroyWindow(Window);
+ UserDerefObjectCo(Window);//faxme: dunno if win should be reffed during destroy..
+
+ RETURN(ret);
+
+CLEANUP:
+ DPRINT("Leave NtUserDestroyWindow, ret=%i\n",_ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+
+
+/*
+ * @unimplemented
+ */
+DWORD
+APIENTRY
+NtUserDrawMenuBarTemp(
+ HWND hWnd,
+ HDC hDC,
+ PRECT hRect,
+ HMENU hMenu,
+ HFONT hFont)
+{
+ /* we'll use this function just for caching the menu bar */
+ UNIMPLEMENTED
+ return 0;
+}
+
+
+/*
+ * @unimplemented
+ */
+DWORD APIENTRY
+NtUserEndDeferWindowPosEx(DWORD Unknown0,
+ DWORD Unknown1)
+{
+ UNIMPLEMENTED
+
+ return 0;
+}
+
+
+/*
+ * FillWindow: Called from User; Dialog, Edit and ListBox procs during a WM_ERASEBKGND.
+ */
+/*
+ * @unimplemented
+ */
+BOOL APIENTRY
+NtUserFillWindow(HWND hWndPaint,
+ HWND hWndPaint1,
+ HDC hDC,
+ HBRUSH hBrush)
+{
+ UNIMPLEMENTED
+
+ return 0;
+}
+
+
+static HWND FASTCALL
+IntFindWindow(PWINDOW_OBJECT Parent,
+ PWINDOW_OBJECT ChildAfter,
+ RTL_ATOM ClassAtom,
+ PUNICODE_STRING WindowName)
+{
+ BOOL CheckWindowName;
+ HWND *List, *phWnd;
+ HWND Ret = NULL;
+ UNICODE_STRING CurrentWindowName;
+
+ ASSERT(Parent);
+
+ CheckWindowName = WindowName->Length != 0;
+
+ if((List = IntWinListChildren(Parent)))
+ {
+ phWnd = List;
+ if(ChildAfter)
+ {
+ /* skip handles before and including ChildAfter */
+ while(*phWnd && (*(phWnd++) != ChildAfter->hSelf))
+ ;
+ }
+
+ /* search children */
+ while(*phWnd)
+ {
+ PWINDOW_OBJECT Child;
+ if(!(Child = UserGetWindowObject(*(phWnd++))))
+ {
+ continue;
+ }
+
+ /* Do not send WM_GETTEXT messages in the kernel mode version!
+ The user mode version however calls GetWindowText() which will
+ send WM_GETTEXT messages to windows belonging to its processes */
+ if (!ClassAtom || Child->Wnd->pcls->atomClassName == ClassAtom)
+ {
+ // HACK: use UNICODE_STRING instead of LARGE_STRING
+ CurrentWindowName.Buffer = Child->Wnd->strName.Buffer;
+ CurrentWindowName.Length = Child->Wnd->strName.Length;
+ CurrentWindowName.MaximumLength = Child->Wnd->strName.MaximumLength;
+ if(!CheckWindowName ||
+ (Child->Wnd->strName.Length < 0xFFFF &&
+ !RtlCompareUnicodeString(WindowName, &CurrentWindowName, TRUE)))
+ {
+ Ret = Child->hSelf;
+ break;
+ }
+ }
+ }
+ ExFreePool(List);
+ }
+
+ return Ret;
+}
+
+/*
+ * FUNCTION:
+ * Searches a window's children for a window with the specified
+ * class and name
+ * ARGUMENTS:
+ * hwndParent = The window whose childs are to be searched.
+ * NULL = desktop
+ * HWND_MESSAGE = message-only windows
+ *
+ * hwndChildAfter = Search starts after this child window.
+ * NULL = start from beginning
+ *
+ * ucClassName = Class name to search for
+ * Reguired parameter.
+ *
+ * ucWindowName = Window name
+ * ->Buffer == NULL = don't care
+ *
+ * RETURNS:
+ * The HWND of the window if it was found, otherwise NULL
+ */
+/*
+ * @implemented
+ */
+HWND APIENTRY
+NtUserFindWindowEx(HWND hwndParent,
+ HWND hwndChildAfter,
+ PUNICODE_STRING ucClassName,
+ PUNICODE_STRING ucWindowName,
+ DWORD dwUnknown)
+{
+ PWINDOW_OBJECT Parent, ChildAfter;
+ UNICODE_STRING ClassName = {0}, WindowName = {0};
+ HWND Desktop, Ret = NULL;
+ RTL_ATOM ClassAtom = (RTL_ATOM)0;
+ DECLARE_RETURN(HWND);
+
+ DPRINT("Enter NtUserFindWindowEx\n");
+ UserEnterShared();
+
+ if (ucClassName != NULL || ucWindowName != NULL)
+ {
+ _SEH2_TRY
+ {
+ if (ucClassName != NULL)
+ {
+ ClassName = ProbeForReadUnicodeString(ucClassName);
+ if (ClassName.Length != 0)
+ {
+ ProbeForRead(ClassName.Buffer,
+ ClassName.Length,
+ sizeof(WCHAR));
+ }
+ else if (!IS_ATOM(ClassName.Buffer))
+ {
+ SetLastWin32Error(ERROR_INVALID_PARAMETER);
+ _SEH2_LEAVE;
+ }
+
+ if (!IntGetAtomFromStringOrAtom(&ClassName,
+ &ClassAtom))
+ {
+ _SEH2_LEAVE;
+ }
+ }
+
+ if (ucWindowName != NULL)
+ {
+ WindowName = ProbeForReadUnicodeString(ucWindowName);
+ if (WindowName.Length != 0)
+ {
+ ProbeForRead(WindowName.Buffer,
+ WindowName.Length,
+ sizeof(WCHAR));
+ }
+ }
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ SetLastNtError(_SEH2_GetExceptionCode());
+ _SEH2_YIELD(RETURN(NULL));
+ }
+ _SEH2_END;
+
+ if (ucClassName != NULL)
+ {
+ if (ClassName.Length == 0 && ClassName.Buffer != NULL &&
+ !IS_ATOM(ClassName.Buffer))
+ {
+ SetLastWin32Error(ERROR_INVALID_PARAMETER);
+ RETURN(NULL);
+ }
+ else if (ClassAtom == (RTL_ATOM)0)
+ {
+ /* LastError code was set by IntGetAtomFromStringOrAtom */
+ RETURN(NULL);
+ }
+ }
+ }
+
+ Desktop = IntGetCurrentThreadDesktopWindow();
+
+ if(hwndParent == NULL)
+ hwndParent = Desktop;
+ else if(hwndParent == HWND_MESSAGE)
+ {
+ hwndParent = IntGetMessageWindow();
+ }
+
+ if(!(Parent = UserGetWindowObject(hwndParent)))
+ {
+ RETURN( NULL);
+ }
+
+ ChildAfter = NULL;
+ if(hwndChildAfter && !(ChildAfter = UserGetWindowObject(hwndChildAfter)))
+ {
+ RETURN( NULL);
+ }
+
+ _SEH2_TRY
+ {
+ if(Parent->hSelf == Desktop)
+ {
+ HWND *List, *phWnd;
+ PWINDOW_OBJECT TopLevelWindow;
+ BOOLEAN CheckWindowName;
+ BOOLEAN WindowMatches;
+ BOOLEAN ClassMatches;
+
+ /* windows searches through all top-level windows if the parent is the desktop
+ window */
+
+ if((List = IntWinListChildren(Parent)))
+ {
+ phWnd = List;
+
+ if(ChildAfter)
+ {
+ /* skip handles before and including ChildAfter */
+ while(*phWnd && (*(phWnd++) != ChildAfter->hSelf))
+ ;
+ }
+
+ CheckWindowName = WindowName.Length != 0;
+
+ /* search children */
+ while(*phWnd)
+ {
+ UNICODE_STRING ustr;
+
+ if(!(TopLevelWindow = UserGetWindowObject(*(phWnd++))))
+ {
+ continue;
+ }
+
+ /* Do not send WM_GETTEXT messages in the kernel mode version!
+ The user mode version however calls GetWindowText() which will
+ send WM_GETTEXT messages to windows belonging to its processes */
+ ustr.Buffer = TopLevelWindow->Wnd->strName.Buffer;
+ ustr.Length = TopLevelWindow->Wnd->strName.Length;
+ ustr.MaximumLength = TopLevelWindow->Wnd->strName.MaximumLength;
+ WindowMatches = !CheckWindowName ||
+ (TopLevelWindow->Wnd->strName.Length < 0xFFFF &&
+ !RtlCompareUnicodeString(&WindowName, &ustr, TRUE));
+ ClassMatches = (ClassAtom == (RTL_ATOM)0) ||
+ ClassAtom == TopLevelWindow->Wnd->pcls->atomClassName;
+
+ if (WindowMatches && ClassMatches)
+ {
+ Ret = TopLevelWindow->hSelf;
+ break;
+ }
+
+ if (IntFindWindow(TopLevelWindow, NULL, ClassAtom, &WindowName))
+ {
+ /* window returns the handle of the top-level window, in case it found
+ the child window */
+ Ret = TopLevelWindow->hSelf;
+ break;
+ }
+
+ }
+ ExFreePool(List);
+ }
+ }
+ else
+ Ret = IntFindWindow(Parent, ChildAfter, ClassAtom, &WindowName);
+
+#if 0
+
+ if(Ret == NULL && hwndParent == NULL && hwndChildAfter == NULL)
+ {
+ /* FIXME - if both hwndParent and hwndChildAfter are NULL, we also should
+ search the message-only windows. Should this also be done if
+ Parent is the desktop window??? */
+ PWINDOW_OBJECT MsgWindows;
+
+ if((MsgWindows = UserGetWindowObject(IntGetMessageWindow())))
+ {
+ Ret = IntFindWindow(MsgWindows, ChildAfter, ClassAtom, &WindowName);
+ }
+ }
+#endif
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ SetLastNtError(_SEH2_GetExceptionCode());
+ Ret = NULL;
+ }
+ _SEH2_END;
+
+ RETURN( Ret);
+
+CLEANUP:
+ DPRINT("Leave NtUserFindWindowEx, ret %i\n",_ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+
+/*
+ * @unimplemented
+ */
+BOOL APIENTRY
+NtUserFlashWindowEx(IN PFLASHWINFO pfwi)
+{
+ UNIMPLEMENTED
+
+ return 0;
+}
+
+
+/*
+ * @implemented
+ */
+PWINDOW_OBJECT FASTCALL UserGetAncestor(PWINDOW_OBJECT Wnd, UINT Type)
+{
+ PWINDOW_OBJECT WndAncestor, Parent;
+
+ if (Wnd->hSelf == IntGetDesktopWindow())
+ {
+ return NULL;
+ }
+
+ switch (Type)
+ {
+ case GA_PARENT:
+ {
+ WndAncestor = Wnd->spwndParent;
+ break;
+ }
+
+ case GA_ROOT:
+ {
+ WndAncestor = Wnd;
+ Parent = NULL;
+
+ for(;;)
+ {
+ if(!(Parent = WndAncestor->spwndParent))
+ {
+ break;
+ }
+ if(IntIsDesktopWindow(Parent))
+ {
+ break;
+ }
+
+ WndAncestor = Parent;
+ }
+ break;
+ }
+
+ case GA_ROOTOWNER:
+ {
+ WndAncestor = Wnd;
+
+ for (;;)
+ {
- Old = WndAncestor;
++ PWINDOW_OBJECT Parent;
+
- PWND Wnd;
+ Parent = IntGetParent(WndAncestor);
+
+ if (!Parent)
+ {
+ break;
+ }
+
+ WndAncestor = Parent;
+ }
+ break;
+ }
+
+ default:
+ {
+ return NULL;
+ }
+ }
+
+ return WndAncestor;
+}
+
+/*
+ * @implemented
+ */
+HWND APIENTRY
+NtUserGetAncestor(HWND hWnd, UINT Type)
+{
+ PWINDOW_OBJECT Window, Ancestor;
+ DECLARE_RETURN(HWND);
+
+ DPRINT("Enter NtUserGetAncestor\n");
+ UserEnterExclusive();
+
+ if (!(Window = UserGetWindowObject(hWnd)))
+ {
+ RETURN(NULL);
+ }
+
+ Ancestor = UserGetAncestor(Window, Type);
+ /* faxme: can UserGetAncestor ever return NULL for a valid window? */
+
+ RETURN(Ancestor ? Ancestor->hSelf : NULL);
+
+CLEANUP:
+ DPRINT("Leave NtUserGetAncestor, ret=%i\n",_ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+
+BOOL
+APIENTRY
+NtUserGetComboBoxInfo(
+ HWND hWnd,
+ PCOMBOBOXINFO pcbi)
+{
+ PWINDOW_OBJECT Wnd;
+ DECLARE_RETURN(BOOL);
+
+ DPRINT("Enter NtUserGetComboBoxInfo\n");
+ UserEnterShared();
+
+ if (!(Wnd = UserGetWindowObject(hWnd)))
+ {
+ RETURN( FALSE );
+ }
+ _SEH2_TRY
+ {
+ if(pcbi)
+ {
+ ProbeForWrite(pcbi,
+ sizeof(COMBOBOXINFO),
+ 1);
+ }
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ SetLastNtError(_SEH2_GetExceptionCode());
+ _SEH2_YIELD(RETURN(FALSE));
+ }
+ _SEH2_END;
+
+ // Pass the user pointer, it was already probed.
+ RETURN( (BOOL) co_IntSendMessage( Wnd->hSelf, CB_GETCOMBOBOXINFO, 0, (LPARAM)pcbi));
+
+CLEANUP:
+ DPRINT("Leave NtUserGetComboBoxInfo, ret=%i\n",_ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+
+/*
+ * @implemented
+ */
+DWORD APIENTRY
+NtUserGetInternalWindowPos( HWND hWnd,
+ LPRECT rectWnd,
+ LPPOINT ptIcon)
+{
+ PWINDOW_OBJECT Window;
- Wnd = Window->Wnd;
+ DWORD Ret = 0;
+ BOOL Hit = FALSE;
+ WINDOWPLACEMENT wndpl;
+
+ UserEnterShared();
+
+ if (!(Window = UserGetWindowObject(hWnd)) || !Window->Wnd)
+ {
+ Hit = FALSE;
+ goto Exit;
+ }
- USHORT Hit;
+
+ _SEH2_TRY
+ {
+ if(rectWnd)
+ {
+ ProbeForWrite(rectWnd,
+ sizeof(RECT),
+ 1);
+ }
+ if(ptIcon)
+ {
+ ProbeForWrite(ptIcon,
+ sizeof(POINT),
+ 1);
+ }
+
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ SetLastNtError(_SEH2_GetExceptionCode());
+ Hit = TRUE;
+ }
+ _SEH2_END;
+
+ wndpl.length = sizeof(WINDOWPLACEMENT);
+
+ if (IntGetWindowPlacement(Window, &wndpl) && !Hit)
+ {
+ _SEH2_TRY
+ {
+ if (rectWnd)
+ {
+ RtlCopyMemory(rectWnd, &wndpl.rcNormalPosition , sizeof(RECT));
+ }
+ if (ptIcon)
+ {
+ RtlCopyMemory(ptIcon, &wndpl.ptMinPosition, sizeof(POINT));
+ }
+
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ SetLastNtError(_SEH2_GetExceptionCode());
+ Hit = TRUE;
+ }
+ _SEH2_END;
+
+ if (!Hit) Ret = wndpl.showCmd;
+ }
+Exit:
+ UserLeave();
+ return Ret;
+}
+
+DWORD
+APIENTRY
+NtUserGetListBoxInfo(
+ HWND hWnd)
+{
+ PWINDOW_OBJECT Wnd;
+ DECLARE_RETURN(DWORD);
+
+ DPRINT("Enter NtUserGetListBoxInfo\n");
+ UserEnterShared();
+
+ if (!(Wnd = UserGetWindowObject(hWnd)))
+ {
+ RETURN( 0 );
+ }
+
+ RETURN( (DWORD) co_IntSendMessage( Wnd->hSelf, LB_GETLISTBOXINFO, 0, 0 ));
+
+CLEANUP:
+ DPRINT("Leave NtUserGetListBoxInfo, ret=%i\n",_ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+
+HWND FASTCALL
+co_UserSetParent(HWND hWndChild, HWND hWndNewParent)
+{
+ PWINDOW_OBJECT Wnd = NULL, WndParent = NULL, WndOldParent;
+ HWND hWndOldParent = NULL;
+ USER_REFERENCE_ENTRY Ref, ParentRef;
+
+ if (IntIsBroadcastHwnd(hWndChild) || IntIsBroadcastHwnd(hWndNewParent))
+ {
+ SetLastWin32Error(ERROR_INVALID_PARAMETER);
+ return( NULL);
+ }
+
+ if (hWndChild == IntGetDesktopWindow())
+ {
+ SetLastWin32Error(ERROR_ACCESS_DENIED);
+ return( NULL);
+ }
+
+ if (hWndNewParent)
+ {
+ if (!(WndParent = UserGetWindowObject(hWndNewParent)))
+ {
+ return( NULL);
+ }
+ }
+ else
+ {
+ if (!(WndParent = UserGetWindowObject(IntGetDesktopWindow())))
+ {
+ return( NULL);
+ }
+ }
+
+ if (!(Wnd = UserGetWindowObject(hWndChild)))
+ {
+ return( NULL);
+ }
+
+ UserRefObjectCo(Wnd, &Ref);
+ UserRefObjectCo(WndParent, &ParentRef);
+
+ WndOldParent = co_IntSetParent(Wnd, WndParent);
+
+ UserDerefObjectCo(WndParent);
+ UserDerefObjectCo(Wnd);
+
+ if (WndOldParent)
+ {
+ hWndOldParent = WndOldParent->hSelf;
+ UserDereferenceObject(WndOldParent);
+ }
+
+ return( hWndOldParent);
+}
+
+/*
+ * NtUserSetParent
+ *
+ * The NtUserSetParent function changes the parent window of the specified
+ * child window.
+ *
+ * Remarks
+ * The new parent window and the child window must belong to the same
+ * application. If the window identified by the hWndChild parameter is
+ * visible, the system performs the appropriate redrawing and repainting.
+ * For compatibility reasons, NtUserSetParent does not modify the WS_CHILD
+ * or WS_POPUP window styles of the window whose parent is being changed.
+ *
+ * Status
+ * @implemented
+ */
+
+HWND APIENTRY
+NtUserSetParent(HWND hWndChild, HWND hWndNewParent)
+{
+ DECLARE_RETURN(HWND);
+
+ DPRINT("Enter NtUserSetParent\n");
+ UserEnterExclusive();
+
+ /*
+ Check Parent first from user space, set it here.
+ */
+ if (!hWndNewParent)
+ {
+ hWndNewParent = IntGetDesktopWindow();
+ }
+ else if (hWndNewParent == HWND_MESSAGE)
+ {
+ hWndNewParent = IntGetMessageWindow();
+ }
+
+ RETURN( co_UserSetParent(hWndChild, hWndNewParent));
+
+CLEANUP:
+ DPRINT("Leave NtUserSetParent, ret=%i\n",_ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+/*
+ * UserGetShellWindow
+ *
+ * Returns a handle to shell window that was set by NtUserSetShellWindowEx.
+ *
+ * Status
+ * @implemented
+ */
+HWND FASTCALL UserGetShellWindow(VOID)
+{
+ PWINSTATION_OBJECT WinStaObject;
+ HWND Ret;
+
+ NTSTATUS Status = IntValidateWindowStationHandle(PsGetCurrentProcess()->Win32WindowStation,
+ KernelMode,
+ 0,
+ &WinStaObject);
+
+ if (!NT_SUCCESS(Status))
+ {
+ SetLastNtError(Status);
+ return( (HWND)0);
+ }
+
+ Ret = (HWND)WinStaObject->ShellWindow;
+
+ ObDereferenceObject(WinStaObject);
+ return( Ret);
+}
+
+/*
+ * NtUserSetShellWindowEx
+ *
+ * This is undocumented function to set global shell window. The global
+ * shell window has special handling of window position.
+ *
+ * Status
+ * @implemented
+ */
+BOOL APIENTRY
+NtUserSetShellWindowEx(HWND hwndShell, HWND hwndListView)
+{
+ PWINSTATION_OBJECT WinStaObject;
+ PWINDOW_OBJECT WndShell, WndListView;
+ DECLARE_RETURN(BOOL);
+ USER_REFERENCE_ENTRY Ref;
+ NTSTATUS Status;
+ PTHREADINFO ti;
+
+ DPRINT("Enter NtUserSetShellWindowEx\n");
+ UserEnterExclusive();
+
+ if (!(WndShell = UserGetWindowObject(hwndShell)))
+ {
+ RETURN(FALSE);
+ }
+
+ if(!(WndListView = UserGetWindowObject(hwndListView)))
+ {
+ RETURN(FALSE);
+ }
+
+ Status = IntValidateWindowStationHandle(PsGetCurrentProcess()->Win32WindowStation,
+ KernelMode,
+ 0,
+ &WinStaObject);
+
+ if (!NT_SUCCESS(Status))
+ {
+ SetLastNtError(Status);
+ RETURN( FALSE);
+ }
+
+ /*
+ * Test if we are permitted to change the shell window.
+ */
+ if (WinStaObject->ShellWindow)
+ {
+ ObDereferenceObject(WinStaObject);
+ RETURN( FALSE);
+ }
+
+ /*
+ * Move shell window into background.
+ */
+ if (hwndListView && hwndListView != hwndShell)
+ {
+ /*
+ * Disabled for now to get Explorer working.
+ * -- Filip, 01/nov/2003
+ */
+#if 0
+ co_WinPosSetWindowPos(hwndListView, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE);
+#endif
+
+ if (WndListView->Wnd->ExStyle & WS_EX_TOPMOST)
+ {
+ ObDereferenceObject(WinStaObject);
+ RETURN( FALSE);
+ }
+ }
+
+ if (WndShell->Wnd->ExStyle & WS_EX_TOPMOST)
+ {
+ ObDereferenceObject(WinStaObject);
+ RETURN( FALSE);
+ }
+
+ UserRefObjectCo(WndShell, &Ref);
+ co_WinPosSetWindowPos(WndShell, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE);
+
+ WinStaObject->ShellWindow = hwndShell;
+ WinStaObject->ShellListView = hwndListView;
+
+ ti = GetW32ThreadInfo();
+ if (ti->pDeskInfo) ti->pDeskInfo->hShellWindow = hwndShell;
+
+ UserDerefObjectCo(WndShell);
+
+ ObDereferenceObject(WinStaObject);
+ RETURN( TRUE);
+
+CLEANUP:
+ DPRINT("Leave NtUserSetShellWindowEx, ret=%i\n",_ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+/*
+ * NtUserGetSystemMenu
+ *
+ * The NtUserGetSystemMenu function allows the application to access the
+ * window menu (also known as the system menu or the control menu) for
+ * copying and modifying.
+ *
+ * Parameters
+ * hWnd
+ * Handle to the window that will own a copy of the window menu.
+ * bRevert
+ * Specifies the action to be taken. If this parameter is FALSE,
+ * NtUserGetSystemMenu returns a handle to the copy of the window menu
+ * currently in use. The copy is initially identical to the window menu
+ * but it can be modified.
+ * If this parameter is TRUE, GetSystemMenu resets the window menu back
+ * to the default state. The previous window menu, if any, is destroyed.
+ *
+ * Return Value
+ * If the bRevert parameter is FALSE, the return value is a handle to a
+ * copy of the window menu. If the bRevert parameter is TRUE, the return
+ * value is NULL.
+ *
+ * Status
+ * @implemented
+ */
+
+HMENU APIENTRY
+NtUserGetSystemMenu(HWND hWnd, BOOL bRevert)
+{
+ PWINDOW_OBJECT Window;
+ PMENU_OBJECT Menu;
+ DECLARE_RETURN(HMENU);
+
+ DPRINT("Enter NtUserGetSystemMenu\n");
+ UserEnterShared();
+
+ if (!(Window = UserGetWindowObject(hWnd)))
+ {
+ RETURN(NULL);
+ }
+
+ if (!(Menu = IntGetSystemMenu(Window, bRevert, FALSE)))
+ {
+ RETURN(NULL);
+ }
+
+ RETURN(Menu->MenuInfo.Self);
+
+CLEANUP:
+ DPRINT("Leave NtUserGetSystemMenu, ret=%i\n",_ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+/*
+ * NtUserSetSystemMenu
+ *
+ * Status
+ * @implemented
+ */
+
+BOOL APIENTRY
+NtUserSetSystemMenu(HWND hWnd, HMENU hMenu)
+{
+ BOOL Result = FALSE;
+ PWINDOW_OBJECT Window;
+ PMENU_OBJECT Menu;
+ DECLARE_RETURN(BOOL);
+
+ DPRINT("Enter NtUserSetSystemMenu\n");
+ UserEnterExclusive();
+
+ if (!(Window = UserGetWindowObject(hWnd)))
+ {
+ RETURN( FALSE);
+ }
+
+ if (hMenu)
+ {
+ /*
+ * Assign new menu handle.
+ */
+ if (!(Menu = UserGetMenuObject(hMenu)))
+ {
+ RETURN( FALSE);
+ }
+
+ Result = IntSetSystemMenu(Window, Menu);
+ }
+
+ RETURN( Result);
+
+CLEANUP:
+ DPRINT("Leave NtUserSetSystemMenu, ret=%i\n",_ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+LONG FASTCALL
+co_UserSetWindowLong(HWND hWnd, DWORD Index, LONG NewValue, BOOL Ansi)
+{
+ PWINDOW_OBJECT Window, Parent;
+ PWND Wnd;
+ PWINSTATION_OBJECT WindowStation;
+ LONG OldValue;
+ STYLESTRUCT Style;
+
+ if (hWnd == IntGetDesktopWindow())
+ {
+ SetLastWin32Error(STATUS_ACCESS_DENIED);
+ return( 0);
+ }
+
+ if (!(Window = UserGetWindowObject(hWnd)))
+ {
+ return( 0);
+ }
+
+ Wnd = Window->Wnd;
+
+ if (!Wnd) return 0; // No go on zero.
+
+ if ((INT)Index >= 0)
+ {
+ if ((Index + sizeof(LONG)) > Wnd->cbwndExtra)
+ {
+ SetLastWin32Error(ERROR_INVALID_INDEX);
+ return( 0);
+ }
+
+ OldValue = *((LONG *)((PCHAR)(Wnd + 1) + Index));
+/*
+ if ( Index == DWLP_DLGPROC && Wnd->state & WNDS_DIALOGWINDOW)
+ {
+ OldValue = (LONG)IntSetWindowProc( Wnd,
+ (WNDPROC)NewValue,
+ Ansi);
+ if (!OldValue) return 0;
+ }
+*/
+ *((LONG *)((PCHAR)(Wnd + 1) + Index)) = NewValue;
+ }
+ else
+ {
+ switch (Index)
+ {
+ case GWL_EXSTYLE:
+ OldValue = (LONG) Wnd->ExStyle;
+ Style.styleOld = OldValue;
+ Style.styleNew = NewValue;
+
+ /*
+ * Remove extended window style bit WS_EX_TOPMOST for shell windows.
+ */
+ WindowStation = Window->pti->rpdesk->rpwinstaParent;
+ if(WindowStation)
+ {
+ if (hWnd == WindowStation->ShellWindow || hWnd == WindowStation->ShellListView)
+ Style.styleNew &= ~WS_EX_TOPMOST;
+ }
+
+ co_IntSendMessage(hWnd, WM_STYLECHANGING, GWL_EXSTYLE, (LPARAM) &Style);
+ Wnd->ExStyle = (DWORD)Style.styleNew;
+ co_IntSendMessage(hWnd, WM_STYLECHANGED, GWL_EXSTYLE, (LPARAM) &Style);
+ break;
+
+ case GWL_STYLE:
+ OldValue = (LONG) Wnd->style;
+ Style.styleOld = OldValue;
+ Style.styleNew = NewValue;
+ co_IntSendMessage(hWnd, WM_STYLECHANGING, GWL_STYLE, (LPARAM) &Style);
+ Wnd->style = (DWORD)Style.styleNew;
+ co_IntSendMessage(hWnd, WM_STYLECHANGED, GWL_STYLE, (LPARAM) &Style);
+ break;
+
+ case GWL_WNDPROC:
+ {
+ if ( Wnd->head.pti->ppi != PsGetCurrentProcessWin32Process() ||
+ Wnd->fnid & FNID_FREED)
+ {
+ SetLastWin32Error(ERROR_ACCESS_DENIED);
+ return( 0);
+ }
+ OldValue = (LONG)IntSetWindowProc(Wnd,
+ (WNDPROC)NewValue,
+ Ansi);
+ break;
+ }
+
+ case GWL_HINSTANCE:
+ OldValue = (LONG) Wnd->hModule;
+ Wnd->hModule = (HINSTANCE) NewValue;
+ break;
+
+ case GWL_HWNDPARENT:
+ Parent = Window->spwndParent;
+ if (Parent && (Parent->hSelf == IntGetDesktopWindow()))
+ OldValue = (LONG) IntSetOwner(Window->hSelf, (HWND) NewValue);
+ else
+ OldValue = (LONG) co_UserSetParent(Window->hSelf, (HWND) NewValue);
+ break;
+
+ case GWL_ID:
+ OldValue = (LONG) Wnd->IDMenu;
+ Wnd->IDMenu = (UINT) NewValue;
+ break;
+
+ case GWL_USERDATA:
+ OldValue = Wnd->dwUserData;
+ Wnd->dwUserData = NewValue;
+ break;
+
+ default:
+ DPRINT1("NtUserSetWindowLong(): Unsupported index %d\n", Index);
+ SetLastWin32Error(ERROR_INVALID_INDEX);
+ OldValue = 0;
+ break;
+ }
+ }
+
+ return( OldValue);
+}
+
+/*
+ * NtUserSetWindowLong
+ *
+ * The NtUserSetWindowLong function changes an attribute of the specified
+ * window. The function also sets the 32-bit (long) value at the specified
+ * offset into the extra window memory.
+ *
+ * Status
+ * @implemented
+ */
+
+LONG APIENTRY
+NtUserSetWindowLong(HWND hWnd, DWORD Index, LONG NewValue, BOOL Ansi)
+{
+ DECLARE_RETURN(LONG);
+
+ DPRINT("Enter NtUserSetWindowLong\n");
+ UserEnterExclusive();
+
+ RETURN( co_UserSetWindowLong(hWnd, Index, NewValue, Ansi));
+
+CLEANUP:
+ DPRINT("Leave NtUserSetWindowLong, ret=%i\n",_ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+/*
+ * NtUserSetWindowWord
+ *
+ * Legacy function similar to NtUserSetWindowLong.
+ *
+ * Status
+ * @implemented
+ */
+
+WORD APIENTRY
+NtUserSetWindowWord(HWND hWnd, INT Index, WORD NewValue)
+{
+ PWINDOW_OBJECT Window;
+ WORD OldValue;
+ DECLARE_RETURN(WORD);
+
+ DPRINT("Enter NtUserSetWindowWord\n");
+ UserEnterExclusive();
+
+ if (!(Window = UserGetWindowObject(hWnd)))
+ {
+ RETURN( 0);
+ }
+
+ switch (Index)
+ {
+ case GWL_ID:
+ case GWL_HINSTANCE:
+ case GWL_HWNDPARENT:
+ RETURN( co_UserSetWindowLong(Window->hSelf, Index, (UINT)NewValue, TRUE));
+ default:
+ if (Index < 0)
+ {
+ SetLastWin32Error(ERROR_INVALID_INDEX);
+ RETURN( 0);
+ }
+ }
+
+ if (Index > Window->Wnd->cbwndExtra - sizeof(WORD))
+ {
+ SetLastWin32Error(ERROR_INVALID_PARAMETER);
+ RETURN( 0);
+ }
+
+ OldValue = *((WORD *)((PCHAR)(Window->Wnd + 1) + Index));
+ *((WORD *)((PCHAR)(Window->Wnd + 1) + Index)) = NewValue;
+
+ RETURN( OldValue);
+
+CLEANUP:
+ DPRINT("Leave NtUserSetWindowWord, ret=%i\n",_ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+/*
+ * @implemented
+ */
+BOOL APIENTRY
+NtUserGetWindowPlacement(HWND hWnd,
+ WINDOWPLACEMENT *lpwndpl)
+{
+ PWINDOW_OBJECT Window;
+ PWND Wnd;
+ POINT Size;
+ WINDOWPLACEMENT Safepl;
+ NTSTATUS Status;
+ DECLARE_RETURN(BOOL);
+
+ DPRINT("Enter NtUserGetWindowPlacement\n");
+ UserEnterShared();
+
+ if (!(Window = UserGetWindowObject(hWnd)))
+ {
+ RETURN( FALSE);
+ }
+ Wnd = Window->Wnd;
+
+ Status = MmCopyFromCaller(&Safepl, lpwndpl, sizeof(WINDOWPLACEMENT));
+ if(!NT_SUCCESS(Status))
+ {
+ SetLastNtError(Status);
+ RETURN( FALSE);
+ }
+ if(Safepl.length != sizeof(WINDOWPLACEMENT))
+ {
+ RETURN( FALSE);
+ }
+
+ Safepl.flags = 0;
+ if (0 == (Wnd->style & WS_VISIBLE))
+ {
+ Safepl.showCmd = SW_HIDE;
+ }
+ else if ((0 != (Window->state & WINDOWOBJECT_RESTOREMAX) ||
+ 0 != (Wnd->style & WS_MAXIMIZE)) &&
+ 0 == (Wnd->style & WS_MINIMIZE))
+ {
+ Safepl.showCmd = SW_SHOWMAXIMIZED;
+ }
+ else if (0 != (Wnd->style & WS_MINIMIZE))
+ {
+ Safepl.showCmd = SW_SHOWMINIMIZED;
+ }
+ else if (0 != (Wnd->style & WS_VISIBLE))
+ {
+ Safepl.showCmd = SW_SHOWNORMAL;
+ }
+
+ Size.x = Wnd->rcWindow.left;
+ Size.y = Wnd->rcWindow.top;
+ WinPosInitInternalPos(Window, &Size,
+ &Wnd->rcWindow);
+
+ Safepl.rcNormalPosition = Wnd->InternalPos.NormalRect;
+ Safepl.ptMinPosition = Wnd->InternalPos.IconPos;
+ Safepl.ptMaxPosition = Wnd->InternalPos.MaxPos;
+
+ Status = MmCopyToCaller(lpwndpl, &Safepl, sizeof(WINDOWPLACEMENT));
+ if(!NT_SUCCESS(Status))
+ {
+ SetLastNtError(Status);
+ RETURN( FALSE);
+ }
+
+ RETURN( TRUE);
+
+CLEANUP:
+ DPRINT("Leave NtUserGetWindowPlacement, ret=%i\n",_ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+
+/*
+ * @unimplemented
+ */
+BOOL APIENTRY
+NtUserLockWindowUpdate(HWND hWnd)
+{
+ UNIMPLEMENTED
+
+ return 0;
+}
+
+
+/*
+ * @implemented
+ */
+BOOL APIENTRY
+NtUserMoveWindow(
+ HWND hWnd,
+ int X,
+ int Y,
+ int nWidth,
+ int nHeight,
+ BOOL bRepaint)
+{
+ return NtUserSetWindowPos(hWnd, 0, X, Y, nWidth, nHeight,
+ (bRepaint ? SWP_NOZORDER | SWP_NOACTIVATE :
+ SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW));
+}
+
+/*
+ QueryWindow based on KJK::Hyperion and James Tabor.
+
+ 0 = QWUniqueProcessId
+ 1 = QWUniqueThreadId
+ 2 = QWActiveWindow
+ 3 = QWFocusWindow
+ 4 = QWIsHung Implements IsHungAppWindow found
+ by KJK::Hyperion.
+
+ 9 = QWKillWindow When I called this with hWnd ==
+ DesktopWindow, it shutdown the system
+ and rebooted.
+*/
+/*
+ * @implemented
+ */
+DWORD APIENTRY
+NtUserQueryWindow(HWND hWnd, DWORD Index)
+{
+ PWINDOW_OBJECT Window;
+ PWND pWnd;
+ DWORD Result;
+ DECLARE_RETURN(UINT);
+
+ DPRINT("Enter NtUserQueryWindow\n");
+ UserEnterShared();
+
+ if (!(Window = UserGetWindowObject(hWnd)) || !Window->Wnd)
+ {
+ RETURN( 0);
+ }
+
+ pWnd = Window->Wnd;
+
+ switch(Index)
+ {
+ case QUERY_WINDOW_UNIQUE_PROCESS_ID:
+ Result = (DWORD)IntGetWndProcessId(Window);
+ break;
+
+ case QUERY_WINDOW_UNIQUE_THREAD_ID:
+ Result = (DWORD)IntGetWndThreadId(Window);
+ break;
+
+ case QUERY_WINDOW_ACTIVE:
+ Result = (DWORD)UserGetActiveWindow();
+ break;
+
+ case QUERY_WINDOW_FOCUS:
+ Result = (DWORD)IntGetFocusWindow();
+ break;
+
+ case QUERY_WINDOW_ISHUNG:
+ Result = (DWORD)MsqIsHung(Window->pti->MessageQueue);
+ break;
+
+ case QUERY_WINDOW_REAL_ID:
+ Result = (DWORD)pWnd->head.pti->pEThread->Cid.UniqueProcess;
+
+ default:
+ Result = (DWORD)NULL;
+ break;
+ }
+
+ RETURN( Result);
+
+CLEANUP:
+ DPRINT("Leave NtUserQueryWindow, ret=%i\n",_ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+
+/*
+ * @unimplemented
+ */
+DWORD APIENTRY
+NtUserRealChildWindowFromPoint(DWORD Unknown0,
+ DWORD Unknown1,
+ DWORD Unknown2)
+{
+ UNIMPLEMENTED
+
+ return 0;
+}
+
+
+/*
+ * @implemented
+ */
+UINT APIENTRY
+NtUserRegisterWindowMessage(PUNICODE_STRING MessageNameUnsafe)
+{
+ UNICODE_STRING SafeMessageName;
+ NTSTATUS Status;
+ UINT Ret;
+ DECLARE_RETURN(UINT);
+
+ DPRINT("Enter NtUserRegisterWindowMessage\n");
+ UserEnterExclusive();
+
+ if(MessageNameUnsafe == NULL)
+ {
+ SetLastWin32Error(ERROR_INVALID_PARAMETER);
+ RETURN( 0);
+ }
+
+ Status = IntSafeCopyUnicodeStringTerminateNULL(&SafeMessageName, MessageNameUnsafe);
+ if(!NT_SUCCESS(Status))
+ {
+ SetLastNtError(Status);
+ RETURN( 0);
+ }
+
+ Ret = (UINT)IntAddAtom(SafeMessageName.Buffer);
+ if (SafeMessageName.Buffer)
+ ExFreePoolWithTag(SafeMessageName.Buffer, TAG_STRING);
+ RETURN( Ret);
+
+CLEANUP:
+ DPRINT("Leave NtUserRegisterWindowMessage, ret=%i\n",_ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+
+/*
+ * @unimplemented
+ */
+DWORD APIENTRY
+NtUserSetImeOwnerWindow(DWORD Unknown0,
+ DWORD Unknown1)
+{
+ UNIMPLEMENTED
+
+ return 0;
+}
+
+
+/*
+ * @unimplemented
+ */
+DWORD APIENTRY
+NtUserSetInternalWindowPos(
+ HWND hwnd,
+ UINT showCmd,
+ LPRECT rect,
+ LPPOINT pt)
+{
+ UNIMPLEMENTED
+
+ return 0;
+
+}
+
+
+/*
+ * @unimplemented
+ */
+BOOL APIENTRY
+NtUserSetLayeredWindowAttributes(HWND hwnd,
+ COLORREF crKey,
+ BYTE bAlpha,
+ DWORD dwFlags)
+{
+ UNIMPLEMENTED;
+ return FALSE;
+}
+
+
+/*
+ * @unimplemented
+ */
+BOOL APIENTRY
+NtUserSetLogonNotifyWindow(HWND hWnd)
+{
+ UNIMPLEMENTED
+
+ return 0;
+}
+
+
+/*
+ * @implemented
+ */
+BOOL APIENTRY
+NtUserSetMenu(
+ HWND hWnd,
+ HMENU Menu,
+ BOOL Repaint)
+{
+ PWINDOW_OBJECT Window;
+ BOOL Changed;
+ DECLARE_RETURN(BOOL);
+
+ DPRINT("Enter NtUserSetMenu\n");
+ UserEnterExclusive();
+
+ if (!(Window = UserGetWindowObject(hWnd)))
+ {
+ RETURN( FALSE);
+ }
+
+ if (! IntSetMenu(Window, Menu, &Changed))
+ {
+ RETURN( FALSE);
+ }
+
+ if (Changed && Repaint)
+ {
+ USER_REFERENCE_ENTRY Ref;
+
+ UserRefObjectCo(Window, &Ref);
+ co_WinPosSetWindowPos(Window, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
+ SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED);
+
+ UserDerefObjectCo(Window);
+ }
+
+ RETURN( TRUE);
+
+CLEANUP:
+ DPRINT("Leave NtUserSetMenu, ret=%i\n",_ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+
+/*
+ * @implemented
+ */
+BOOL APIENTRY
+NtUserSetWindowFNID(HWND hWnd,
+ WORD fnID)
+{
+ PWINDOW_OBJECT Window;
+ PWND Wnd;
+ DECLARE_RETURN(BOOL);
+
+ DPRINT("Enter NtUserSetWindowFNID\n");
+ UserEnterExclusive();
+
+ if (!(Window = UserGetWindowObject(hWnd)))
+ {
+ RETURN( FALSE);
+ }
+ Wnd = Window->Wnd;
+
+ if (Wnd->pcls)
+ { // From user land we only set these.
+ if ((fnID != FNID_DESTROY) || ((fnID < FNID_BUTTON) && (fnID > FNID_IME)) )
+ {
+ RETURN( FALSE);
+ }
+ else
+ Wnd->pcls->fnid |= fnID;
+ }
+ RETURN( TRUE);
+
+CLEANUP:
+ DPRINT("Leave NtUserSetWindowFNID\n");
+ UserLeave();
+ END_CLEANUP;
+}
+
+
+/*
+ * @implemented
+ */
+BOOL APIENTRY
+NtUserSetWindowPlacement(HWND hWnd,
+ WINDOWPLACEMENT *lpwndpl)
+{
+ PWINDOW_OBJECT Window;
+ PWND Wnd;
+ WINDOWPLACEMENT Safepl;
+ NTSTATUS Status;
+ DECLARE_RETURN(BOOL);
+ USER_REFERENCE_ENTRY Ref;
+
+ DPRINT("Enter NtUserSetWindowPlacement\n");
+ UserEnterExclusive();
+
+ if (!(Window = UserGetWindowObject(hWnd)))
+ {
+ RETURN( FALSE);
+ }
+ Wnd = Window->Wnd;
+
+ Status = MmCopyFromCaller(&Safepl, lpwndpl, sizeof(WINDOWPLACEMENT));
+ if(!NT_SUCCESS(Status))
+ {
+ SetLastNtError(Status);
+ RETURN( FALSE);
+ }
+ if(Safepl.length != sizeof(WINDOWPLACEMENT))
+ {
+ RETURN( FALSE);
+ }
+
+ UserRefObjectCo(Window, &Ref);
+
+ if ((Wnd->style & (WS_MAXIMIZE | WS_MINIMIZE)) == 0)
+ {
+ co_WinPosSetWindowPos(Window, NULL,
+ Safepl.rcNormalPosition.left, Safepl.rcNormalPosition.top,
+ Safepl.rcNormalPosition.right - Safepl.rcNormalPosition.left,
+ Safepl.rcNormalPosition.bottom - Safepl.rcNormalPosition.top,
+ SWP_NOZORDER | SWP_NOACTIVATE);
+ }
+
+ /* FIXME - change window status */
+ co_WinPosShowWindow(Window, Safepl.showCmd);
+
+ Wnd->InternalPosInitialized = TRUE;
+ Wnd->InternalPos.NormalRect = Safepl.rcNormalPosition;
+ Wnd->InternalPos.IconPos = Safepl.ptMinPosition;
+ Wnd->InternalPos.MaxPos = Safepl.ptMaxPosition;
+
+ UserDerefObjectCo(Window);
+ RETURN(TRUE);
+
+CLEANUP:
+ DPRINT("Leave NtUserSetWindowPlacement, ret=%i\n",_ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+
+/*
+ * @implemented
+ */
+BOOL APIENTRY
+NtUserSetWindowPos(
+ HWND hWnd,
+ HWND hWndInsertAfter,
+ int X,
+ int Y,
+ int cx,
+ int cy,
+ UINT uFlags)
+{
+ DECLARE_RETURN(BOOL);
+ PWINDOW_OBJECT Window;
+ BOOL ret;
+ USER_REFERENCE_ENTRY Ref;
+
+ DPRINT("Enter NtUserSetWindowPos\n");
+ UserEnterExclusive();
+
+ if (!(Window = UserGetWindowObject(hWnd)))
+ {
+ RETURN(FALSE);
+ }
+
+ /* First make sure that coordinates are valid for WM_WINDOWPOSCHANGING */
+ if (!(uFlags & SWP_NOMOVE))
+ {
+ if (X < -32768) X = -32768;
+ else if (X > 32767) X = 32767;
+ if (Y < -32768) Y = -32768;
+ else if (Y > 32767) Y = 32767;
+ }
+ if (!(uFlags & SWP_NOSIZE))
+ {
+ if (cx < 0) cx = 0;
+ else if (cx > 32767) cx = 32767;
+ if (cy < 0) cy = 0;
+ else if (cy > 32767) cy = 32767;
+ }
+
+ UserRefObjectCo(Window, &Ref);
+ ret = co_WinPosSetWindowPos(Window, hWndInsertAfter, X, Y, cx, cy, uFlags);
+ UserDerefObjectCo(Window);
+
+ RETURN(ret);
+
+CLEANUP:
+ DPRINT("Leave NtUserSetWindowPos, ret=%i\n",_ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+
+INT FASTCALL
+IntGetWindowRgn(PWINDOW_OBJECT Window, HRGN hRgn)
+{
+ INT Ret;
+ HRGN VisRgn;
+ ROSRGNDATA *pRgn;
+ PWND Wnd;
+
+ if(!Window)
+ {
+ return ERROR;
+ }
+ if(!hRgn)
+ {
+ return ERROR;
+ }
+
+ Wnd = Window->Wnd;
+
+ /* Create a new window region using the window rectangle */
+ VisRgn = IntSysCreateRectRgnIndirect(&Window->Wnd->rcWindow);
+ NtGdiOffsetRgn(VisRgn, -Window->Wnd->rcWindow.left, -Window->Wnd->rcWindow.top);
+ /* if there's a region assigned to the window, combine them both */
+ if(Window->hrgnClip && !(Wnd->style & WS_MINIMIZE))
+ NtGdiCombineRgn(VisRgn, VisRgn, Window->hrgnClip, RGN_AND);
+ /* Copy the region into hRgn */
+ NtGdiCombineRgn(hRgn, VisRgn, NULL, RGN_COPY);
+
+ if((pRgn = RGNOBJAPI_Lock(hRgn, NULL)))
+ {
+ Ret = pRgn->rdh.iType;
+ RGNOBJAPI_Unlock(pRgn);
+ }
+ else
+ Ret = ERROR;
+
+ REGION_FreeRgnByHandle(VisRgn);
+
+ return Ret;
+}
+
+INT FASTCALL
+IntGetWindowRgnBox(PWINDOW_OBJECT Window, RECTL *Rect)
+{
+ INT Ret;
+ HRGN VisRgn;
+ ROSRGNDATA *pRgn;
+ PWND Wnd;
+
+ if(!Window)
+ {
+ return ERROR;
+ }
+ if(!Rect)
+ {
+ return ERROR;
+ }
+
+ Wnd = Window->Wnd;
+
+ /* Create a new window region using the window rectangle */
+ VisRgn = IntSysCreateRectRgnIndirect(&Window->Wnd->rcWindow);
+ NtGdiOffsetRgn(VisRgn, -Window->Wnd->rcWindow.left, -Window->Wnd->rcWindow.top);
+ /* if there's a region assigned to the window, combine them both */
+ if(Window->hrgnClip && !(Wnd->style & WS_MINIMIZE))
+ NtGdiCombineRgn(VisRgn, VisRgn, Window->hrgnClip, RGN_AND);
+
+ if((pRgn = RGNOBJAPI_Lock(VisRgn, NULL)))
+ {
+ Ret = pRgn->rdh.iType;
+ *Rect = pRgn->rdh.rcBound;
+ RGNOBJAPI_Unlock(pRgn);
+ }
+ else
+ Ret = ERROR;
+
+ REGION_FreeRgnByHandle(VisRgn);
+
+ return Ret;
+}
+
+
+/*
+ * @implemented
+ */
+INT APIENTRY
+NtUserSetWindowRgn(
+ HWND hWnd,
+ HRGN hRgn,
+ BOOL bRedraw)
+{
+ HRGN hrgnCopy;
+ PWINDOW_OBJECT Window;
+ DECLARE_RETURN(INT);
+
+ DPRINT("Enter NtUserSetWindowRgn\n");
+ UserEnterExclusive();
+
+ if (!(Window = UserGetWindowObject(hWnd)))
+ {
+ RETURN( 0);
+ }
+
+ if (hRgn) // The region will be deleted in user32.
+ {
+ if (GDIOBJ_ValidateHandle(hRgn, GDI_OBJECT_TYPE_REGION))
+ {
+ hrgnCopy = IntSysCreateRectRgn(0, 0, 0, 0);
+ NtGdiCombineRgn(hrgnCopy, hRgn, 0, RGN_COPY);
+ }
+ else
+ RETURN( 0);
+ }
+ else
+ hrgnCopy = (HRGN) 1;
+
+ if (Window->hrgnClip)
+ {
+ /* Delete no longer needed region handle */
+ GreDeleteObject(Window->hrgnClip);
+ }
+ Window->hrgnClip = hrgnCopy;
+
+ /* FIXME - send WM_WINDOWPOSCHANGING and WM_WINDOWPOSCHANGED messages to the window */
+
+ if(bRedraw)
+ {
+ USER_REFERENCE_ENTRY Ref;
+ UserRefObjectCo(Window, &Ref);
+ co_UserRedrawWindow(Window, NULL, NULL, RDW_INVALIDATE);
+ UserDerefObjectCo(Window);
+ }
+
+ RETURN( (INT)hRgn);
+
+CLEANUP:
+ DPRINT("Leave NtUserSetWindowRgn, ret=%i\n",_ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+
+/*
+ * @implemented
+ */
+BOOL APIENTRY
+NtUserShowWindow(HWND hWnd, LONG nCmdShow)
+{
+ PWINDOW_OBJECT Window;
+ BOOL ret;
+ DECLARE_RETURN(BOOL);
+ USER_REFERENCE_ENTRY Ref;
+
+ DPRINT("Enter NtUserShowWindow\n");
+ UserEnterExclusive();
+
+ if (!(Window = UserGetWindowObject(hWnd)))
+ {
+ RETURN(FALSE);
+ }
+
+ UserRefObjectCo(Window, &Ref);
+ ret = co_WinPosShowWindow(Window, nCmdShow);
+ UserDerefObjectCo(Window);
+
+ RETURN(ret);
+
+CLEANUP:
+ DPRINT("Leave NtUserShowWindow, ret=%i\n",_ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+
+/*
+ * @unimplemented
+ */
+BOOL APIENTRY
+NtUserShowWindowAsync(HWND hWnd, LONG nCmdShow)
+{
+#if 0
+ UNIMPLEMENTED
+ return 0;
+#else
+ return NtUserShowWindow(hWnd, nCmdShow);
+#endif
+}
+
+
+/*
+ * @unimplemented
+ */
+BOOL
+APIENTRY
+NtUserUpdateLayeredWindow(
+ HWND hwnd,
+ HDC hdcDst,
+ POINT *pptDst,
+ SIZE *psize,
+ HDC hdcSrc,
+ POINT *pptSrc,
+ COLORREF crKey,
+ BLENDFUNCTION *pblend,
+ DWORD dwFlags,
+ RECT *prcDirty)
+{
+ UNIMPLEMENTED
+
+ return 0;
+}
+
+/*
+ * @unimplemented
+ */
+HWND APIENTRY
+NtUserWindowFromPhysicalPoint(POINT Point)
+{
+ UNIMPLEMENTED
+
+ return NULL;
+}
+
+/*
+ * @implemented
+ */
+HWND APIENTRY
+NtUserWindowFromPoint(LONG X, LONG Y)
+{
+ POINT pt;
+ HWND Ret;
+ PWINDOW_OBJECT DesktopWindow = NULL, Window = NULL;
+ DECLARE_RETURN(HWND);
+ USER_REFERENCE_ENTRY Ref;
+
+ DPRINT("Enter NtUserWindowFromPoint\n");
+ UserEnterExclusive();
+
+ if ((DesktopWindow = UserGetWindowObject(IntGetDesktopWindow())))
+ {
+ PTHREADINFO pti;
- Hit = co_WinPosWindowFromPoint(DesktopWindow, pti->MessageQueue, &pt, &Window);
+
+ pt.x = X;
+ pt.y = Y;
+
+ //hmm... threads live on desktops thus we have a reference on the desktop and indirectly the desktop window
+ //its possible this referencing is useless, thou it shouldnt hurt...
+ UserRefObjectCo(DesktopWindow, &Ref);
+
+ pti = PsGetCurrentThreadWin32Thread();
++ co_WinPosWindowFromPoint(DesktopWindow, pti->MessageQueue, &pt, &Window);
+
+ if(Window)
+ {
+ Ret = Window->hSelf;
+
+ RETURN( Ret);
+ }
+ }
+
+ RETURN( NULL);
+
+CLEANUP:
+ if (Window) UserDereferenceObject(Window);
+ if (DesktopWindow) UserDerefObjectCo(DesktopWindow);
+
+ DPRINT("Leave NtUserWindowFromPoint, ret=%i\n",_ret_);
+ UserLeave();
+ END_CLEANUP;
+
+}
+
+
+/*
+ * NtUserDefSetText
+ *
+ * Undocumented function that is called from DefWindowProc to set
+ * window text.
+ *
+ * Status
+ * @implemented
+ */
+BOOL APIENTRY
+NtUserDefSetText(HWND hWnd, PLARGE_STRING WindowText)
+{
+ PWINDOW_OBJECT Window;
+ PWND Wnd;
+ LARGE_STRING SafeText;
+ UNICODE_STRING UnicodeString;
+ BOOL Ret = TRUE;
+
+ DPRINT("Enter NtUserDefSetText\n");
+
+ if (WindowText != NULL)
+ {
+ _SEH2_TRY
+ {
+ SafeText = ProbeForReadLargeString(WindowText);
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ Ret = FALSE;
+ SetLastNtError(_SEH2_GetExceptionCode());
+ }
+ _SEH2_END;
+
+ if (!Ret)
+ return FALSE;
+ }
+ else
+ return TRUE;
+
+ UserEnterExclusive();
+
+ if(!(Window = UserGetWindowObject(hWnd)) || !Window->Wnd)
+ {
+ UserLeave();
+ return FALSE;
+ }
+ Wnd = Window->Wnd;
+
+ // ReactOS uses Unicode and not mixed. Up/Down converting will take time.
+ // Brought to you by: The Wine Project! Dysfunctional Thought Processes!
+ // Now we know what the bAnsi is for.
+ RtlInitUnicodeString(&UnicodeString, NULL);
+ if (SafeText.Buffer)
+ {
+ _SEH2_TRY
+ {
+ if (SafeText.bAnsi)
+ ProbeForRead(SafeText.Buffer, SafeText.Length, sizeof(CHAR));
+ else
+ ProbeForRead(SafeText.Buffer, SafeText.Length, sizeof(WCHAR));
+ Ret = RtlLargeStringToUnicodeString(&UnicodeString, &SafeText);
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ Ret = FALSE;
+ SetLastNtError(_SEH2_GetExceptionCode());
+ }
+ _SEH2_END;
+ if (!Ret) goto Exit;
+ }
+
+ if (UnicodeString.Length != 0)
+ {
+ if (Wnd->strName.MaximumLength > 0 &&
+ UnicodeString.Length <= Wnd->strName.MaximumLength - sizeof(UNICODE_NULL))
+ {
+ ASSERT(Wnd->strName.Buffer != NULL);
+
+ Wnd->strName.Length = UnicodeString.Length;
+ Wnd->strName.Buffer[UnicodeString.Length / sizeof(WCHAR)] = L'\0';
+ RtlCopyMemory(Wnd->strName.Buffer,
+ UnicodeString.Buffer,
+ UnicodeString.Length);
+ }
+ else
+ {
+ PWCHAR buf;
+ Wnd->strName.MaximumLength = Wnd->strName.Length = 0;
+ buf = Wnd->strName.Buffer;
+ Wnd->strName.Buffer = NULL;
+ if (buf != NULL)
+ {
+ DesktopHeapFree(Wnd->head.rpdesk, buf);
+ }
+
+ Wnd->strName.Buffer = DesktopHeapAlloc(Wnd->head.rpdesk,
+ UnicodeString.Length + sizeof(UNICODE_NULL));
+ if (Wnd->strName.Buffer != NULL)
+ {
+ Wnd->strName.Buffer[UnicodeString.Length / sizeof(WCHAR)] = L'\0';
+ RtlCopyMemory(Wnd->strName.Buffer,
+ UnicodeString.Buffer,
+ UnicodeString.Length);
+ Wnd->strName.MaximumLength = UnicodeString.Length + sizeof(UNICODE_NULL);
+ Wnd->strName.Length = UnicodeString.Length;
+ }
+ else
+ {
+ SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
+ Ret = FALSE;
+ goto Exit;
+ }
+ }
+ }
+ else
+ {
+ Wnd->strName.Length = 0;
+ if (Wnd->strName.Buffer != NULL)
+ Wnd->strName.Buffer[0] = L'\0';
+ }
+
+ // HAX! FIXME! Windows does not do this in here!
+ // In User32, these are called after: NotifyWinEvent EVENT_OBJECT_NAMECHANGE than
+ // RepaintButton, StaticRepaint, NtUserCallHwndLock HWNDLOCK_ROUTINE_REDRAWFRAMEANDHOOK, etc.
+ /* Send shell notifications */
+ if (!Window->spwndOwner && !IntGetParent(Window))
+ {
+ co_IntShellHookNotify(HSHELL_REDRAW, (LPARAM) hWnd);
+ }
+
+ Ret = TRUE;
+Exit:
+ if (UnicodeString.Buffer) RtlFreeUnicodeString(&UnicodeString);
+ DPRINT("Leave NtUserDefSetText, ret=%i\n", Ret);
+ UserLeave();
+ return Ret;
+}
+
+/*
+ * NtUserInternalGetWindowText
+ *
+ * Status
+ * @implemented
+ */
+
+INT APIENTRY
+NtUserInternalGetWindowText(HWND hWnd, LPWSTR lpString, INT nMaxCount)
+{
+ PWINDOW_OBJECT Window;
+ PWND Wnd;
+ NTSTATUS Status;
+ INT Result;
+ DECLARE_RETURN(INT);
+
+ DPRINT("Enter NtUserInternalGetWindowText\n");
+ UserEnterShared();
+
+ if(lpString && (nMaxCount <= 1))
+ {
+ SetLastWin32Error(ERROR_INVALID_PARAMETER);
+ RETURN( 0);
+ }
+
+ if(!(Window = UserGetWindowObject(hWnd)))
+ {
+ RETURN( 0);
+ }
+ Wnd = Window->Wnd;
+
+ Result = Wnd->strName.Length / sizeof(WCHAR);
+ if(lpString)
+ {
+ const WCHAR Terminator = L'\0';
+ INT Copy;
+ WCHAR *Buffer = (WCHAR*)lpString;
+
+ Copy = min(nMaxCount - 1, Result);
+ if(Copy > 0)
+ {
+ Status = MmCopyToCaller(Buffer, Wnd->strName.Buffer, Copy * sizeof(WCHAR));
+ if(!NT_SUCCESS(Status))
+ {
+ SetLastNtError(Status);
+ RETURN( 0);
+ }
+ Buffer += Copy;
+ }
+
+ Status = MmCopyToCaller(Buffer, &Terminator, sizeof(WCHAR));
+ if(!NT_SUCCESS(Status))
+ {
+ SetLastNtError(Status);
+ RETURN( 0);
+ }
+
+ Result = Copy;
+ }
+
+ RETURN( Result);
+
+CLEANUP:
+ DPRINT("Leave NtUserInternalGetWindowText, ret=%i\n",_ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+
+BOOL
+FASTCALL
+IntShowOwnedPopups(PWINDOW_OBJECT OwnerWnd, BOOL fShow )
+{
+ int count = 0;
+ PWINDOW_OBJECT pWnd;
+ HWND *win_array;
+
+// ASSERT(OwnerWnd);
+
+ win_array = IntWinListChildren(UserGetWindowObject(IntGetDesktopWindow()));
+
+ if (!win_array)
+ return TRUE;
+
+ while (win_array[count])
+ count++;
+ while (--count >= 0)
+ {
+ if (!(pWnd = UserGetWindowObject( win_array[count] )))
+ continue;
+ if (pWnd->spwndOwner != OwnerWnd)
+ continue;
+
+ if (fShow)
+ {
+ if (pWnd->Wnd->state & WNDS_HIDDENPOPUP)
+ {
+ /* In Windows, ShowOwnedPopups(TRUE) generates
+ * WM_SHOWWINDOW messages with SW_PARENTOPENING,
+ * regardless of the state of the owner
+ */
+ co_IntSendMessage(win_array[count], WM_SHOWWINDOW, SW_SHOWNORMAL, SW_PARENTOPENING);
+ continue;
+ }
+ }
+ else
+ {
+ if (pWnd->Wnd->style & WS_VISIBLE)
+ {
+ /* In Windows, ShowOwnedPopups(FALSE) generates
+ * WM_SHOWWINDOW messages with SW_PARENTCLOSING,
+ * regardless of the state of the owner
+ */
+ co_IntSendMessage(win_array[count], WM_SHOWWINDOW, SW_HIDE, SW_PARENTCLOSING);
+ continue;
+ }
+ }
+
+ }
+ ExFreePool( win_array );
+ return TRUE;
+}
+
+/*
+ * NtUserValidateHandleSecure
+ *
+ * Status
+ * @implemented
+ */
+
+BOOL
+APIENTRY
+NtUserValidateHandleSecure(
+ HANDLE handle,
+ BOOL Restricted)
+{
+ if(!Restricted)
+ {
+ UINT uType;
+ {
+ PUSER_HANDLE_ENTRY entry;
+ if (!(entry = handle_to_entry(gHandleTable, handle )))
+ {
+ SetLastWin32Error(ERROR_INVALID_HANDLE);
+ return FALSE;
+ }
+ uType = entry->type;
+ }
+ switch (uType)
+ {
+ case otWindow:
+ {
+ PWINDOW_OBJECT Window;
+ if ((Window = UserGetWindowObject((HWND) handle))) return TRUE;
+ return FALSE;
+ }
+ case otMenu:
+ {
+ PMENU_OBJECT Menu;
+ if ((Menu = UserGetMenuObject((HMENU) handle))) return TRUE;
+ return FALSE;
+ }
+ case otAccel:
+ {
+ PACCELERATOR_TABLE Accel;
+ if ((Accel = UserGetAccelObject((HACCEL) handle))) return TRUE;
+ return FALSE;
+ }
+ case otCursorIcon:
+ {
+ PCURICON_OBJECT Cursor;
+ if ((Cursor = UserGetCurIconObject((HCURSOR) handle))) return TRUE;
+ return FALSE;
+ }
+ case otHook:
+ {
+ PHOOK Hook;
+ if ((Hook = IntGetHookObject((HHOOK) handle))) return TRUE;
+ return FALSE;
+ }
+ case otMonitor:
+ {
+ PMONITOR Monitor;
+ if ((Monitor = UserGetMonitorObject((HMONITOR) handle))) return TRUE;
+ return FALSE;
+ }
+ case otCallProc:
+ {
+ WNDPROC_INFO Proc;
+ return UserGetCallProcInfo( handle, &Proc );
+ }
+ default:
+ SetLastWin32Error(ERROR_INVALID_HANDLE);
+ }
+ }
+ else
+ { /* Is handle entry restricted? */
+ UNIMPLEMENTED
+ }
+ return FALSE;
+}
+
+
+/* EOF */
--- /dev/null
- NTSTATUS Status = STATUS_SUCCESS;
+/*
+ * COPYRIGHT: See COPYING in the top level directory
+ * PROJECT: ReactOS kernel
+ * PURPOSE: Functions for creation and destruction of DCs
+ * FILE: subsystem/win32/win32k/objects/dcattr.c
+ * PROGRAMER: Timo Kreuzer (timo.kreuzer@rectos.org)
+ */
+
+#include <win32k.h>
+
+#define NDEBUG
+#include <debug.h>
+
+#define GDIDCATTRFREE 8
+
+typedef struct _GDI_DC_ATTR_FREELIST
+{
+ LIST_ENTRY Entry;
+ DWORD nEntries;
+ PVOID AttrList[GDIDCATTRFREE];
+} GDI_DC_ATTR_FREELIST, *PGDI_DC_ATTR_FREELIST;
+
+typedef struct _GDI_DC_ATTR_ENTRY
+{
+ DC_ATTR Attr[GDIDCATTRFREE];
+} GDI_DC_ATTR_ENTRY, *PGDI_DC_ATTR_ENTRY;
+
+
+PDC_ATTR
+FASTCALL
+AllocateDcAttr(VOID)
+{
+ PTHREADINFO pti;
+ PPROCESSINFO ppi;
+ PDC_ATTR pDc_Attr;
+ PGDI_DC_ATTR_FREELIST pGdiDcAttrFreeList;
+ PGDI_DC_ATTR_ENTRY pGdiDcAttrEntry;
+ int i;
+
+ pti = PsGetCurrentThreadWin32Thread();
+ if (pti->pgdiDcattr)
+ {
+ pDc_Attr = pti->pgdiDcattr; // Get the free one.
+ pti->pgdiDcattr = NULL;
+ return pDc_Attr;
+ }
+
+ ppi = PsGetCurrentProcessWin32Process();
+
+ if (!ppi->pDCAttrList) // If set point is null, allocate new group.
+ {
+ pGdiDcAttrEntry = EngAllocUserMem(sizeof(GDI_DC_ATTR_ENTRY), 0);
+
+ if (!pGdiDcAttrEntry)
+ {
+ DPRINT1("DcAttr Failed User Allocation!\n");
+ return NULL;
+ }
+
+ DPRINT("AllocDcAttr User 0x%x\n",pGdiDcAttrEntry);
+
+ pGdiDcAttrFreeList = ExAllocatePoolWithTag( PagedPool,
+ sizeof(GDI_DC_ATTR_FREELIST),
+ GDITAG_DC_FREELIST);
+ if ( !pGdiDcAttrFreeList )
+ {
+ EngFreeUserMem(pGdiDcAttrEntry);
+ return NULL;
+ }
+
+ RtlZeroMemory(pGdiDcAttrFreeList, sizeof(GDI_DC_ATTR_FREELIST));
+
+ DPRINT("AllocDcAttr Ex 0x%x\n",pGdiDcAttrFreeList);
+
+ InsertHeadList( &ppi->GDIDcAttrFreeList, &pGdiDcAttrFreeList->Entry);
+
+ pGdiDcAttrFreeList->nEntries = GDIDCATTRFREE;
+ // Start at the bottom up and set end of free list point.
+ ppi->pDCAttrList = &pGdiDcAttrEntry->Attr[GDIDCATTRFREE-1];
+ // Build the free attr list.
+ for ( i = 0; i < GDIDCATTRFREE; i++)
+ {
+ pGdiDcAttrFreeList->AttrList[i] = &pGdiDcAttrEntry->Attr[i];
+ }
+ }
+
+ pDc_Attr = ppi->pDCAttrList;
+ pGdiDcAttrFreeList = (PGDI_DC_ATTR_FREELIST)ppi->GDIDcAttrFreeList.Flink;
+
+ // Free the list when it is full!
+ if ( pGdiDcAttrFreeList->nEntries-- == 1)
+ { // No more free entries, so yank the list.
+ RemoveEntryList( &pGdiDcAttrFreeList->Entry );
+
+ ExFreePoolWithTag( pGdiDcAttrFreeList, GDITAG_DC_FREELIST );
+
+ if ( IsListEmpty( &ppi->GDIDcAttrFreeList ) )
+ {
+ ppi->pDCAttrList = NULL;
+ return pDc_Attr;
+ }
+
+ pGdiDcAttrFreeList = (PGDI_DC_ATTR_FREELIST)ppi->GDIDcAttrFreeList.Flink;
+ }
+
+ ppi->pDCAttrList = pGdiDcAttrFreeList->AttrList[pGdiDcAttrFreeList->nEntries-1];
+
+ return pDc_Attr;
+}
+
+VOID
+FASTCALL
+FreeDcAttr(PDC_ATTR pDc_Attr)
+{
+ PTHREADINFO pti;
+ PPROCESSINFO ppi;
+ PGDI_DC_ATTR_FREELIST pGdiDcAttrFreeList;
+
+ pti = PsGetCurrentThreadWin32Thread();
+
+ if (!pti) return;
+
+ if (!pti->pgdiDcattr)
+ { // If it is null, just cache it for the next time.
+ pti->pgdiDcattr = pDc_Attr;
+ return;
+ }
+
+ ppi = PsGetCurrentProcessWin32Process();
+
+ pGdiDcAttrFreeList = (PGDI_DC_ATTR_FREELIST)ppi->GDIDcAttrFreeList.Flink;
+
+ // We add to the list of free entries, so this will grows!
+ if ( IsListEmpty(&ppi->GDIDcAttrFreeList) ||
+ pGdiDcAttrFreeList->nEntries == GDIDCATTRFREE )
+ {
+ pGdiDcAttrFreeList = ExAllocatePoolWithTag( PagedPool,
+ sizeof(GDI_DC_ATTR_FREELIST),
+ GDITAG_DC_FREELIST);
+ if ( !pGdiDcAttrFreeList )
+ {
+ return;
+ }
+ InsertHeadList( &ppi->GDIDcAttrFreeList, &pGdiDcAttrFreeList->Entry);
+ pGdiDcAttrFreeList->nEntries = 0;
+ }
+ // Up count, save the entry and set end of free list point.
+ ++pGdiDcAttrFreeList->nEntries; // Top Down...
+ pGdiDcAttrFreeList->AttrList[pGdiDcAttrFreeList->nEntries-1] = pDc_Attr;
+ ppi->pDCAttrList = pDc_Attr;
+
+ return;
+}
+
+BOOL
+FASTCALL
+DC_AllocDcAttr(PDC pdc)
+{
+ DC_AllocateDcAttr(pdc->BaseObject.hHmgr);
+ *pdc->pdcattr = pdc->dcattr;
+ return TRUE;
+}
+
+// CHECK against current head
+VOID
+FASTCALL
+DC_AllocateDcAttr(HDC hDC)
+{
+ PVOID NewMem = NULL;
+ PDC pDC;
+ HANDLE Pid = NtCurrentProcess();
+ ULONG MemSize = sizeof(DC_ATTR); //PAGE_SIZE it will allocate that size
+
+ NTSTATUS Status = ZwAllocateVirtualMemory(Pid,
+ &NewMem,
+ 0,
+ &MemSize,
+ MEM_COMMIT|MEM_RESERVE,
+ PAGE_READWRITE);
+ {
+ INT Index = GDI_HANDLE_GET_INDEX((HGDIOBJ)hDC);
+ PGDI_TABLE_ENTRY Entry = &GdiHandleTable->Entries[Index];
+ // FIXME: dc could have been deleted!!! use GDIOBJ_InsertUserData
+ if (NT_SUCCESS(Status))
+ {
+ RtlZeroMemory(NewMem, MemSize);
+ Entry->UserData = NewMem;
+ DPRINT("DC_ATTR allocated! 0x%x\n",NewMem);
+ }
+ else
+ {
+ DPRINT("DC_ATTR not allocated!\n");
+ }
+ }
+ pDC = DC_LockDc(hDC);
+ ASSERT(pDC->pdcattr == &pDC->dcattr);
+ if(NewMem)
+ {
+ pDC->pdcattr = NewMem; // Store pointer
+ }
+ DC_UnlockDc(pDC);
+}
+
+VOID
+NTAPI
+DC_vFreeDcAttr(PDC pdc)
+{
+ HANDLE Pid = NtCurrentProcess();
+ INT Index;
+ PGDI_TABLE_ENTRY pent;
+
+ if (pdc->pdcattr == &pdc->dcattr)
+ {
+ // Internal DC object!
+ return;
+ }
+
+ pdc->pdcattr = &pdc->dcattr;
+
+ Index = GDI_HANDLE_GET_INDEX(pdc->BaseObject.hHmgr);
+ pent = &GdiHandleTable->Entries[Index];
+ if(pent->UserData)
+ {
+ ULONG MemSize = sizeof(DC_ATTR);
+ NTSTATUS Status = ZwFreeVirtualMemory(Pid,
+ &pent->UserData,
+ &MemSize,
+ MEM_RELEASE);
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("DC_FreeDC failed to free DC_ATTR 0x%p\n", pent->UserData);
+ ASSERT(FALSE);
+ }
+ pent->UserData = NULL;
+ }
+}
+
+
+static
+VOID
+CopytoUserDcAttr(PDC dc, PDC_ATTR pdcattr)
+{
- Status = _SEH2_GetExceptionCode();
+ dc->dcattr.mxWorldToDevice = dc->dclevel.mxWorldToDevice;
+ dc->dcattr.mxDeviceToWorld = dc->dclevel.mxDeviceToWorld;
+ dc->dcattr.mxWorldToPage = dc->dclevel.mxWorldToPage;
+
+ _SEH2_TRY
+ {
+ ProbeForWrite(pdcattr, sizeof(DC_ATTR), 1);
+ RtlCopyMemory(pdcattr, &dc->dcattr, sizeof(DC_ATTR));
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ ASSERT(FALSE);
+ }
+ _SEH2_END;
+}
+
+// FIXME: wtf? 2 functions, where one has a typo in the name????
+BOOL
+FASTCALL
+DCU_SyncDcAttrtoUser(PDC dc)
+{
+ PDC_ATTR pdcattr = dc->pdcattr;
+
+ if (pdcattr == &dc->dcattr) return TRUE; // No need to copy self.
+ ASSERT(pdcattr);
+ CopytoUserDcAttr( dc, pdcattr);
+ return TRUE;
+}
+// LOL! DCU_ Sync hDc Attr to User,,, need it speeled out for you?
+BOOL
+FASTCALL
+DCU_SynchDcAttrtoUser(HDC hDC)
+{
+ BOOL Ret;
+ PDC pDC = DC_LockDc ( hDC );
+ if (!pDC) return FALSE;
+ Ret = DCU_SyncDcAttrtoUser(pDC);
+ DC_UnlockDc( pDC );
+ return Ret;
+}
+
--- /dev/null
- #if 0 /* This (Wine) code doesn't seem to work correctly for us */
+/*
+ * FreeType font engine interface
+ *
+ * Copyright 2001 Huw D M Davies for CodeWeavers.
+ * Copyright 2006 Dmitry Timoshkov for CodeWeavers.
+ *
+ * This file contains the WineEng* functions.
+ *
+ * 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
+ */
+/*
+ *
+ * Addaped for the use in ReactOS.
+ *
+ */
+/*
+ * PROJECT: ReactOS win32 kernel mode subsystem
+ * LICENSE: GPL - See COPYING in the top level directory
+ * FILE: subsystems/win32/win32k/objects/freetype.c
+ * PURPOSE: Freetype library support
+ * PROGRAMMER:
+ */
+
+/** Includes ******************************************************************/
+
+#include <win32k.h>
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_GLYPH_H
+#include FT_TYPE1_TABLES_H
+#include <freetype/tttables.h>
+#include <freetype/fttrigon.h>
+#include <freetype/ftglyph.h>
+#include <freetype/ftbitmap.h>
+#include <freetype/ftoutln.h>
+#include <freetype/ftwinfnt.h>
+
+#define NDEBUG
+#include <debug.h>
+
+#ifndef FT_MAKE_TAG
+#define FT_MAKE_TAG( ch0, ch1, ch2, ch3 ) \
+ ( ((DWORD)(BYTE)(ch0) << 24) | ((DWORD)(BYTE)(ch1) << 16) | \
+ ((DWORD)(BYTE)(ch2) << 8) | (DWORD)(BYTE)(ch3) )
+#endif
+
+FT_Library library;
+
+typedef struct _FONT_ENTRY
+{
+ LIST_ENTRY ListEntry;
+ FONTGDI *Font;
+ UNICODE_STRING FaceName;
+ BYTE NotEnum;
+} FONT_ENTRY, *PFONT_ENTRY;
+
+/* The FreeType library is not thread safe, so we have
+ to serialize access to it */
+static FAST_MUTEX FreeTypeLock;
+
+static LIST_ENTRY FontListHead;
+static FAST_MUTEX FontListLock;
+static BOOL RenderingEnabled = TRUE;
+
+#define MAX_FONT_CACHE 256
+
+typedef struct _FONT_CACHE_ENTRY
+{
+ LIST_ENTRY ListEntry;
+ int GlyphIndex;
+ FT_Face Face;
+ FT_BitmapGlyph BitmapGlyph;
+ int Height;
+} FONT_CACHE_ENTRY, *PFONT_CACHE_ENTRY;
+static LIST_ENTRY FontCacheListHead;
+static UINT FontCacheNumEntries;
+
+static PWCHAR ElfScripts[32] = /* these are in the order of the fsCsb[0] bits */
+{
+ L"Western", /*00*/
+ L"Central_European",
+ L"Cyrillic",
+ L"Greek",
+ L"Turkish",
+ L"Hebrew",
+ L"Arabic",
+ L"Baltic",
+ L"Vietnamese", /*08*/
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, /*15*/
+ L"Thai",
+ L"Japanese",
+ L"CHINESE_GB2312",
+ L"Hangul",
+ L"CHINESE_BIG5",
+ L"Hangul(Johab)",
+ NULL, NULL, /*23*/
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ L"Symbol" /*31*/
+};
+
+/*
+ * For TranslateCharsetInfo
+ */
+#define CP_SYMBOL 42
+#define MAXTCIINDEX 32
+static const CHARSETINFO FontTci[MAXTCIINDEX] =
+{
+ /* ANSI */
+ { ANSI_CHARSET, 1252, {{0,0,0,0},{FS_LATIN1,0}} },
+ { EASTEUROPE_CHARSET, 1250, {{0,0,0,0},{FS_LATIN2,0}} },
+ { RUSSIAN_CHARSET, 1251, {{0,0,0,0},{FS_CYRILLIC,0}} },
+ { GREEK_CHARSET, 1253, {{0,0,0,0},{FS_GREEK,0}} },
+ { TURKISH_CHARSET, 1254, {{0,0,0,0},{FS_TURKISH,0}} },
+ { HEBREW_CHARSET, 1255, {{0,0,0,0},{FS_HEBREW,0}} },
+ { ARABIC_CHARSET, 1256, {{0,0,0,0},{FS_ARABIC,0}} },
+ { BALTIC_CHARSET, 1257, {{0,0,0,0},{FS_BALTIC,0}} },
+ { VIETNAMESE_CHARSET, 1258, {{0,0,0,0},{FS_VIETNAMESE,0}} },
+ /* reserved by ANSI */
+ { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
+ { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
+ { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
+ { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
+ { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
+ { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
+ { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
+ /* ANSI and OEM */
+ { THAI_CHARSET, 874, {{0,0,0,0},{FS_THAI,0}} },
+ { SHIFTJIS_CHARSET, 932, {{0,0,0,0},{FS_JISJAPAN,0}} },
+ { GB2312_CHARSET, 936, {{0,0,0,0},{FS_CHINESESIMP,0}} },
+ { HANGEUL_CHARSET, 949, {{0,0,0,0},{FS_WANSUNG,0}} },
+ { CHINESEBIG5_CHARSET, 950, {{0,0,0,0},{FS_CHINESETRAD,0}} },
+ { JOHAB_CHARSET, 1361, {{0,0,0,0},{FS_JOHAB,0}} },
+ /* reserved for alternate ANSI and OEM */
+ { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
+ { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
+ { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
+ { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
+ { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
+ { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
+ { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
+ { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
+ /* reserved for system */
+ { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
+ { SYMBOL_CHARSET, CP_SYMBOL, {{0,0,0,0},{FS_SYMBOL,0}} }
+};
+
+BOOL FASTCALL
+InitFontSupport(VOID)
+{
+ ULONG ulError;
+
+ InitializeListHead(&FontListHead);
+ InitializeListHead(&FontCacheListHead);
+ FontCacheNumEntries = 0;
+ ExInitializeFastMutex(&FontListLock);
+ ExInitializeFastMutex(&FreeTypeLock);
+
+ ulError = FT_Init_FreeType(&library);
+ if (ulError)
+ {
+ DPRINT1("FT_Init_FreeType failed with error code 0x%x\n", ulError);
+ return FALSE;
+ }
+
+ IntLoadSystemFonts();
+
+ return TRUE;
+}
+
+/*
+ * IntLoadSystemFonts
+ *
+ * Search the system font directory and adds each font found.
+ */
+
+VOID FASTCALL
+IntLoadSystemFonts(VOID)
+{
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ UNICODE_STRING Directory, SearchPattern, FileName, TempString;
+ IO_STATUS_BLOCK Iosb;
+ HANDLE hDirectory;
+ BYTE *DirInfoBuffer;
+ PFILE_DIRECTORY_INFORMATION DirInfo;
+ BOOLEAN bRestartScan = TRUE;
+ NTSTATUS Status;
+
+ RtlInitUnicodeString(&Directory, L"\\SystemRoot\\Fonts\\");
+ /* FIXME: Add support for other font types */
+ RtlInitUnicodeString(&SearchPattern, L"*.ttf");
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &Directory,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL);
+
+ Status = ZwOpenFile(
+ &hDirectory,
+ SYNCHRONIZE | FILE_LIST_DIRECTORY,
+ &ObjectAttributes,
+ &Iosb,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE);
+
+ if (NT_SUCCESS(Status))
+ {
+ DirInfoBuffer = ExAllocatePoolWithTag(PagedPool, 0x4000, TAG_FONT);
+ if (DirInfoBuffer == NULL)
+ {
+ ZwClose(hDirectory);
+ return;
+ }
+
+ FileName.Buffer = ExAllocatePoolWithTag(PagedPool, MAX_PATH * sizeof(WCHAR), TAG_FONT);
+ if (FileName.Buffer == NULL)
+ {
+ ExFreePoolWithTag(DirInfoBuffer, TAG_FONT);
+ ZwClose(hDirectory);
+ return;
+ }
+ FileName.Length = 0;
+ FileName.MaximumLength = MAX_PATH * sizeof(WCHAR);
+
+ while (1)
+ {
+ Status = ZwQueryDirectoryFile(
+ hDirectory,
+ NULL,
+ NULL,
+ NULL,
+ &Iosb,
+ DirInfoBuffer,
+ 0x4000,
+ FileDirectoryInformation,
+ FALSE,
+ &SearchPattern,
+ bRestartScan);
+
+ if (!NT_SUCCESS(Status) || Status == STATUS_NO_MORE_FILES)
+ {
+ break;
+ }
+
+ DirInfo = (PFILE_DIRECTORY_INFORMATION)DirInfoBuffer;
+ while (1)
+ {
+ TempString.Buffer = DirInfo->FileName;
+ TempString.Length =
+ TempString.MaximumLength = DirInfo->FileNameLength;
+ RtlCopyUnicodeString(&FileName, &Directory);
+ RtlAppendUnicodeStringToString(&FileName, &TempString);
+ IntGdiAddFontResource(&FileName, 0);
+ if (DirInfo->NextEntryOffset == 0)
+ break;
+ DirInfo = (PFILE_DIRECTORY_INFORMATION)((ULONG_PTR)DirInfo + DirInfo->NextEntryOffset);
+ }
+
+ bRestartScan = FALSE;
+ }
+
+ ExFreePoolWithTag(FileName.Buffer, TAG_FONT);
+ ExFreePoolWithTag(DirInfoBuffer, TAG_FONT);
+ ZwClose(hDirectory);
+ }
+}
+
+
+/*
+ * IntGdiAddFontResource
+ *
+ * Adds the font resource from the specified file to the system.
+ */
+
+INT FASTCALL
+IntGdiAddFontResource(PUNICODE_STRING FileName, DWORD Characteristics)
+{
+ FONTGDI *FontGDI;
+ NTSTATUS Status;
+ HANDLE FileHandle, KeyHandle;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ PVOID Buffer = NULL;
+ IO_STATUS_BLOCK Iosb;
+ INT Error;
+ FT_Face Face;
+ ANSI_STRING AnsiFaceName;
+ PFONT_ENTRY Entry;
+ PSECTION_OBJECT SectionObject;
+ ULONG ViewSize = 0;
+ LARGE_INTEGER SectionSize;
++#if 0 // Wine code
+ FT_Fixed XScale, YScale;
++#endif
+ UNICODE_STRING FontRegPath = RTL_CONSTANT_STRING(L"\\REGISTRY\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts");
+
+ /* Open the font file */
+
+ InitializeObjectAttributes(&ObjectAttributes, FileName, 0, NULL, NULL);
+ Status = ZwOpenFile(
+ &FileHandle,
+ FILE_GENERIC_READ | SYNCHRONIZE,
+ &ObjectAttributes,
+ &Iosb,
+ FILE_SHARE_READ,
+ FILE_SYNCHRONOUS_IO_NONALERT);
+
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT("Could not load font file: %wZ\n", FileName);
+ return 0;
+ }
+
+ SectionSize.QuadPart = 0LL;
+ Status = MmCreateSection((PVOID)&SectionObject, SECTION_ALL_ACCESS,
+ NULL, &SectionSize, PAGE_READONLY,
+ SEC_COMMIT, FileHandle, NULL);
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT("Could not map file: %wZ\n", FileName);
+ ZwClose(FileHandle);
+ return 0;
+ }
+
+ ZwClose(FileHandle);
+
+ Status = MmMapViewInSystemSpace(SectionObject, &Buffer, &ViewSize);
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT("Could not map file: %wZ\n", FileName);
+ return Status;
+ }
+
+ IntLockFreeType;
+ Error = FT_New_Memory_Face(
+ library,
+ Buffer,
+ ViewSize,
+ 0,
+ &Face);
+ IntUnLockFreeType;
+
+ if (Error)
+ {
+ if (Error == FT_Err_Unknown_File_Format)
+ DPRINT("Unknown font file format\n");
+ else
+ DPRINT("Error reading font file (error code: %u)\n", Error);
+ ObDereferenceObject(SectionObject);
+ return 0;
+ }
+
+ Entry = ExAllocatePoolWithTag(PagedPool, sizeof(FONT_ENTRY), TAG_FONT);
+ if (!Entry)
+ {
+ FT_Done_Face(Face);
+ ObDereferenceObject(SectionObject);
+ SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
+ return 0;
+ }
+
+ FontGDI = EngAllocMem(FL_ZERO_MEMORY, sizeof(FONTGDI), TAG_FONTOBJ);
+ if (FontGDI == NULL)
+ {
+ FT_Done_Face(Face);
+ ObDereferenceObject(SectionObject);
+ ExFreePoolWithTag(Entry, TAG_FONT);
+ SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
+ return 0;
+ }
+
+ FontGDI->Filename = ExAllocatePoolWithTag(PagedPool, FileName->Length + sizeof(WCHAR), TAG_PFF);
+ if (FontGDI->Filename == NULL)
+ {
+ EngFreeMem(FontGDI);
+ FT_Done_Face(Face);
+ ObDereferenceObject(SectionObject);
+ ExFreePoolWithTag(Entry, TAG_FONT);
+ SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
+ return 0;
+ }
+ RtlCopyMemory(FontGDI->Filename, FileName->Buffer, FileName->Length);
+ FontGDI->Filename[FileName->Length / sizeof(WCHAR)] = L'\0';
+ FontGDI->face = Face;
+
+ /* FIXME: Complete text metrics */
++#if 0 /* This (Wine) code doesn't seem to work correctly for us */
+ XScale = Face->size->metrics.x_scale;
+ YScale = Face->size->metrics.y_scale;
+ FontGDI->TextMetric.tmAscent = (FT_MulFix(Face->ascender, YScale) + 32) >> 6;
+ FontGDI->TextMetric.tmDescent = (FT_MulFix(Face->descender, YScale) + 32) >> 6;
+ FontGDI->TextMetric.tmHeight = (FT_MulFix(Face->ascender, YScale) -
+ FT_MulFix(Face->descender, YScale)) >> 6;
+#else
+ FontGDI->TextMetric.tmAscent = (Face->size->metrics.ascender + 32) >> 6; /* units above baseline */
+ FontGDI->TextMetric.tmDescent = (32 - Face->size->metrics.descender) >> 6; /* units below baseline */
+ FontGDI->TextMetric.tmHeight = (Face->size->metrics.ascender - Face->size->metrics.descender) >> 6;
+#endif
+
+
+
+ DPRINT("Font loaded: %s (%s)\n", Face->family_name, Face->style_name);
+ DPRINT("Num glyphs: %u\n", Face->num_glyphs);
+
+ /* Add this font resource to the font table */
+
+ Entry->Font = FontGDI;
+ Entry->NotEnum = (Characteristics & FR_NOT_ENUM);
+ RtlInitAnsiString(&AnsiFaceName, (LPSTR)Face->family_name);
+ RtlAnsiStringToUnicodeString(&Entry->FaceName, &AnsiFaceName, TRUE);
+
+ if (Characteristics & FR_PRIVATE)
+ {
+ PPROCESSINFO Win32Process = PsGetCurrentProcessWin32Process();
+ IntLockProcessPrivateFonts(Win32Process);
+ InsertTailList(&Win32Process->PrivateFontListHead, &Entry->ListEntry);
+ IntUnLockProcessPrivateFonts(Win32Process);
+ }
+ else
+ {
+ IntLockGlobalFonts;
+ InsertTailList(&FontListHead, &Entry->ListEntry);
+ InitializeObjectAttributes(&ObjectAttributes, &FontRegPath, OBJ_CASE_INSENSITIVE, NULL, NULL);
+ Status = ZwOpenKey(&KeyHandle, KEY_WRITE, &ObjectAttributes);
+ if (NT_SUCCESS(Status))
+ {
+ LPWSTR pName = wcsrchr(FileName->Buffer, L'\\');
+ if (pName)
+ {
+ pName++;
+ ZwSetValueKey(KeyHandle, &Entry->FaceName, 0, REG_SZ, pName, (wcslen(pName) + 1) * sizeof(WCHAR));
+ }
+ ZwClose(KeyHandle);
+ }
+ IntUnLockGlobalFonts;
+ }
+ return 1;
+}
+
+BOOL FASTCALL
+IntIsFontRenderingEnabled(VOID)
+{
+ BOOL Ret = RenderingEnabled;
+ HDC hDC;
+
+ hDC = IntGetScreenDC();
+ if (hDC)
+ Ret = (NtGdiGetDeviceCaps(hDC, BITSPIXEL) > 8) && RenderingEnabled;
+
+ return Ret;
+}
+
+VOID FASTCALL
+IntEnableFontRendering(BOOL Enable)
+{
+ RenderingEnabled = Enable;
+}
+
+FT_Render_Mode FASTCALL
+IntGetFontRenderMode(LOGFONTW *logfont)
+{
+ switch (logfont->lfQuality)
+ {
+ case NONANTIALIASED_QUALITY:
+ return FT_RENDER_MODE_MONO;
+ case DRAFT_QUALITY:
+ return FT_RENDER_MODE_LIGHT;
+ /* case CLEARTYPE_QUALITY:
+ return FT_RENDER_MODE_LCD; */
+ }
+ return FT_RENDER_MODE_NORMAL;
+}
+
+
+NTSTATUS FASTCALL
+TextIntCreateFontIndirect(CONST LPLOGFONTW lf, HFONT *NewFont)
+{
+ PTEXTOBJ TextObj;
+
+ TextObj = TEXTOBJ_AllocTextWithHandle();
+ if (!TextObj)
+ {
+ return STATUS_NO_MEMORY;
+ }
+
+ *NewFont = TextObj->BaseObject.hHmgr;
+ RtlCopyMemory(&TextObj->logfont.elfEnumLogfontEx.elfLogFont, lf, sizeof(LOGFONTW));
+ if (lf->lfEscapement != lf->lfOrientation)
+ {
+ /* this should really depend on whether GM_ADVANCED is set */
+ TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfOrientation =
+ TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfEscapement;
+ }
+ TEXTOBJ_UnlockText(TextObj);
+
+ return STATUS_SUCCESS;
+}
+
+/*************************************************************************
+ * TranslateCharsetInfo
+ *
+ * Fills a CHARSETINFO structure for a character set, code page, or
+ * font. This allows making the correspondance between different labelings
+ * (character set, Windows, ANSI, and OEM codepages, and Unicode ranges)
+ * of the same encoding.
+ *
+ * Only one codepage will be set in Cs->fs. If TCI_SRCFONTSIG is used,
+ * only one codepage should be set in *Src.
+ *
+ * RETURNS
+ * TRUE on success, FALSE on failure.
+ *
+ */
+static BOOLEAN APIENTRY
+IntTranslateCharsetInfo(PDWORD Src, /* [in]
+ if flags == TCI_SRCFONTSIG: pointer to fsCsb of a FONTSIGNATURE
+ if flags == TCI_SRCCHARSET: a character set value
+ if flags == TCI_SRCCODEPAGE: a code page value */
+ LPCHARSETINFO Cs, /* [out] structure to receive charset information */
+ DWORD Flags /* [in] determines interpretation of lpSrc */)
+{
+ int Index = 0;
+
+ switch (Flags)
+ {
+ case TCI_SRCFONTSIG:
+ while (0 == (*Src >> Index & 0x0001) && Index < MAXTCIINDEX)
+ {
+ Index++;
+ }
+ break;
+ case TCI_SRCCODEPAGE:
+ while ( *Src != FontTci[Index].ciACP && Index < MAXTCIINDEX)
+ {
+ Index++;
+ }
+ break;
+ case TCI_SRCCHARSET:
+ while ( *Src != FontTci[Index].ciCharset && Index < MAXTCIINDEX)
+ {
+ Index++;
+ }
+ break;
+ default:
+ return FALSE;
+ }
+
+ if (Index >= MAXTCIINDEX || DEFAULT_CHARSET == FontTci[Index].ciCharset)
+ {
+ return FALSE;
+ }
+
+ RtlCopyMemory(Cs, &FontTci[Index], sizeof(CHARSETINFO));
+
+ return TRUE;
+}
+
+
+static BOOL face_has_symbol_charmap(FT_Face ft_face)
+{
+ int i;
+
+ for(i = 0; i < ft_face->num_charmaps; i++)
+ {
+ if(ft_face->charmaps[i]->encoding == FT_ENCODING_MS_SYMBOL)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void FASTCALL
+FillTM(TEXTMETRICW *TM, PFONTGDI FontGDI, TT_OS2 *pOS2, TT_HoriHeader *pHori, FT_WinFNT_HeaderRec *pWin)
+{
+ FT_Fixed XScale, YScale;
+ int Ascent, Descent;
+ FT_Face Face = FontGDI->face;
+
+ XScale = Face->size->metrics.x_scale;
+ YScale = Face->size->metrics.y_scale;
+
+ if (pWin)
+ {
+ TM->tmHeight = pWin->pixel_height;
+ TM->tmAscent = pWin->ascent;
+ TM->tmDescent = TM->tmHeight - TM->tmAscent;
+ TM->tmInternalLeading = pWin->internal_leading;
+ TM->tmExternalLeading = pWin->external_leading;
+ TM->tmAveCharWidth = pWin->avg_width;
+ TM->tmMaxCharWidth = pWin->max_width;
+ TM->tmWeight = pWin->weight;
+ TM->tmOverhang = 0;
+ TM->tmDigitizedAspectX = pWin->horizontal_resolution;
+ TM->tmDigitizedAspectY = pWin->vertical_resolution;
+ TM->tmFirstChar = pWin->first_char;
+ TM->tmLastChar = pWin->last_char;
+ TM->tmDefaultChar = pWin->default_char + pWin->first_char;
+ TM->tmBreakChar = pWin->break_char + pWin->first_char;
+ TM->tmItalic = pWin->italic;
+ TM->tmUnderlined = FontGDI->Underline;
+ TM->tmStruckOut = FontGDI->StrikeOut;
+ TM->tmPitchAndFamily = pWin->pitch_and_family;
+ TM->tmCharSet = pWin->charset;
+ return;
+ }
+
+ if (pOS2->usWinAscent + pOS2->usWinDescent == 0)
+ {
+ Ascent = pHori->Ascender;
+ Descent = -pHori->Descender;
+ }
+ else
+ {
+ Ascent = pOS2->usWinAscent;
+ Descent = pOS2->usWinDescent;
+ }
+
+#if 0 /* This (Wine) code doesn't seem to work correctly for us, cmd issue */
+ TM->tmAscent = (FT_MulFix(Ascent, YScale) + 32) >> 6;
+ TM->tmDescent = (FT_MulFix(Descent, YScale) + 32) >> 6;
+#else /* This (ros) code was previously affected by a FreeType bug, but it works now */
+ TM->tmAscent = (Face->size->metrics.ascender + 32) >> 6; /* units above baseline */
+ TM->tmDescent = (32 - Face->size->metrics.descender) >> 6; /* units below baseline */
+#endif
+ TM->tmInternalLeading = (FT_MulFix(Ascent + Descent - Face->units_per_EM, YScale) + 32) >> 6;
+
+ TM->tmHeight = TM->tmAscent + TM->tmDescent;
+
+ /* MSDN says:
+ * el = MAX(0, LineGap - ((WinAscent + WinDescent) - (Ascender - Descender)))
+ */
+ TM->tmExternalLeading = max(0, (FT_MulFix(pHori->Line_Gap
+ - ((Ascent + Descent)
+ - (pHori->Ascender - pHori->Descender)),
+ YScale) + 32) >> 6);
+
+ TM->tmAveCharWidth = (FT_MulFix(pOS2->xAvgCharWidth, XScale) + 32) >> 6;
+ if (TM->tmAveCharWidth == 0)
+ {
+ TM->tmAveCharWidth = 1;
+ }
+
+ /* Correct forumla to get the maxcharwidth from unicode and ansi font */
+ TM->tmMaxCharWidth = (FT_MulFix(Face->max_advance_width, XScale) + 32) >> 6;
+
+ TM->tmWeight = pOS2->usWeightClass;
+ TM->tmOverhang = 0;
+ TM->tmDigitizedAspectX = 96;
+ TM->tmDigitizedAspectY = 96;
+ if (face_has_symbol_charmap(Face) || (pOS2->usFirstCharIndex >= 0xf000 && pOS2->usFirstCharIndex < 0xf100))
+ {
+ USHORT cpOEM, cpAnsi;
+
+ EngGetCurrentCodePage(&cpOEM, &cpAnsi);
+ TM->tmFirstChar = 0;
+ switch(cpAnsi)
+ {
+ case 1257: /* Baltic */
+ TM->tmLastChar = 0xf8fd;
+ break;
+ default:
+ TM->tmLastChar = 0xf0ff;
+ }
+ TM->tmBreakChar = 0x20;
+ TM->tmDefaultChar = 0x1f;
+ }
+ else
+ {
+ TM->tmFirstChar = pOS2->usFirstCharIndex; /* Should be the first char in the cmap */
+ TM->tmLastChar = pOS2->usLastCharIndex; /* Should be min(cmap_last, os2_last) */
+
+ if(pOS2->usFirstCharIndex <= 1)
+ TM->tmBreakChar = pOS2->usFirstCharIndex + 2;
+ else if (pOS2->usFirstCharIndex > 0xff)
+ TM->tmBreakChar = 0x20;
+ else
+ TM->tmBreakChar = pOS2->usFirstCharIndex;
+ TM->tmDefaultChar = TM->tmBreakChar - 1;
+ }
+ TM->tmItalic = (Face->style_flags & FT_STYLE_FLAG_ITALIC) ? 255 : 0;
+ TM->tmUnderlined = FontGDI->Underline;
+ TM->tmStruckOut = FontGDI->StrikeOut;
+
+ /* Yes TPMF_FIXED_PITCH is correct; braindead api */
+ if (! FT_IS_FIXED_WIDTH(Face))
+ {
+ TM->tmPitchAndFamily = TMPF_FIXED_PITCH;
+ }
+ else
+ {
+ TM->tmPitchAndFamily = 0;
+ }
+
+ switch (pOS2->panose[PAN_FAMILYTYPE_INDEX])
+ {
+ case PAN_FAMILY_SCRIPT:
+ TM->tmPitchAndFamily |= FF_SCRIPT;
+ break;
+ case PAN_FAMILY_DECORATIVE:
+ TM->tmPitchAndFamily |= FF_DECORATIVE;
+ break;
+
+ case PAN_ANY:
+ case PAN_NO_FIT:
+ case PAN_FAMILY_TEXT_DISPLAY:
+ case PAN_FAMILY_PICTORIAL: /* symbol fonts get treated as if they were text */
+ /* which is clearly not what the panose spec says. */
+ if (TM->tmPitchAndFamily == 0) /* fixed */
+ {
+ TM->tmPitchAndFamily = FF_MODERN;
+ }
+ else
+ {
+ switch (pOS2->panose[PAN_SERIFSTYLE_INDEX])
+ {
+ case PAN_ANY:
+ case PAN_NO_FIT:
+ default:
+ TM->tmPitchAndFamily |= FF_DONTCARE;
+ break;
+
+ case PAN_SERIF_COVE:
+ case PAN_SERIF_OBTUSE_COVE:
+ case PAN_SERIF_SQUARE_COVE:
+ case PAN_SERIF_OBTUSE_SQUARE_COVE:
+ case PAN_SERIF_SQUARE:
+ case PAN_SERIF_THIN:
+ case PAN_SERIF_BONE:
+ case PAN_SERIF_EXAGGERATED:
+ case PAN_SERIF_TRIANGLE:
+ TM->tmPitchAndFamily |= FF_ROMAN;
+ break;
+
+ case PAN_SERIF_NORMAL_SANS:
+ case PAN_SERIF_OBTUSE_SANS:
+ case PAN_SERIF_PERP_SANS:
+ case PAN_SERIF_FLARED:
+ case PAN_SERIF_ROUNDED:
+ TM->tmPitchAndFamily |= FF_SWISS;
+ break;
+ }
+ }
+ break;
+ default:
+ TM->tmPitchAndFamily |= FF_DONTCARE;
+ }
+
+ if (FT_IS_SCALABLE(Face))
+ {
+ TM->tmPitchAndFamily |= TMPF_VECTOR;
+ }
+ if (FT_IS_SFNT(Face))
+ {
+ TM->tmPitchAndFamily |= TMPF_TRUETYPE;
+ }
+
+ TM->tmCharSet = DEFAULT_CHARSET;
+}
+
+/*************************************************************
+ * IntGetOutlineTextMetrics
+ *
+ */
+INT FASTCALL
+IntGetOutlineTextMetrics(PFONTGDI FontGDI,
+ UINT Size,
+ OUTLINETEXTMETRICW *Otm)
+{
+ unsigned Needed;
+ TT_OS2 *pOS2;
+ TT_HoriHeader *pHori;
+ TT_Postscript *pPost;
+ FT_Fixed XScale, YScale;
+ ANSI_STRING FamilyNameA, StyleNameA;
+ UNICODE_STRING FamilyNameW, StyleNameW, Regular;
+ FT_WinFNT_HeaderRec Win;
+ FT_Error Error;
+ char *Cp;
+
+ Needed = sizeof(OUTLINETEXTMETRICW);
+
+ RtlInitAnsiString(&FamilyNameA, FontGDI->face->family_name);
+ RtlAnsiStringToUnicodeString(&FamilyNameW, &FamilyNameA, TRUE);
+
+ RtlInitAnsiString(&StyleNameA, FontGDI->face->style_name);
+ RtlAnsiStringToUnicodeString(&StyleNameW, &StyleNameA, TRUE);
+
+ /* These names should be read from the TT name table */
+
+ /* length of otmpFamilyName */
+ Needed += FamilyNameW.Length + sizeof(WCHAR);
+
+ RtlInitUnicodeString(&Regular, L"regular");
+ /* length of otmpFaceName */
+ if (0 == RtlCompareUnicodeString(&StyleNameW, &Regular, TRUE))
+ {
+ Needed += FamilyNameW.Length + sizeof(WCHAR); /* just the family name */
+ }
+ else
+ {
+ Needed += FamilyNameW.Length + StyleNameW.Length + (sizeof(WCHAR) << 1); /* family + " " + style */
+ }
+
+ /* length of otmpStyleName */
+ Needed += StyleNameW.Length + sizeof(WCHAR);
+
+ /* length of otmpFullName */
+ Needed += FamilyNameW.Length + StyleNameW.Length + (sizeof(WCHAR) << 1);
+
+ if (Size < Needed)
+ {
+ RtlFreeUnicodeString(&FamilyNameW);
+ RtlFreeUnicodeString(&StyleNameW);
+ return Needed;
+ }
+
+ XScale = FontGDI->face->size->metrics.x_scale;
+ YScale = FontGDI->face->size->metrics.y_scale;
+
+ IntLockFreeType;
+ pOS2 = FT_Get_Sfnt_Table(FontGDI->face, ft_sfnt_os2);
+ if (NULL == pOS2)
+ {
+ IntUnLockFreeType;
+ DPRINT1("Can't find OS/2 table - not TT font?\n");
+ RtlFreeUnicodeString(&StyleNameW);
+ RtlFreeUnicodeString(&FamilyNameW);
+ return 0;
+ }
+
+ pHori = FT_Get_Sfnt_Table(FontGDI->face, ft_sfnt_hhea);
+ if (NULL == pHori)
+ {
+ IntUnLockFreeType;
+ DPRINT1("Can't find HHEA table - not TT font?\n");
+ RtlFreeUnicodeString(&StyleNameW);
+ RtlFreeUnicodeString(&FamilyNameW);
+ return 0;
+ }
+
+ pPost = FT_Get_Sfnt_Table(FontGDI->face, ft_sfnt_post); /* we can live with this failing */
+
+ Error = FT_Get_WinFNT_Header(FontGDI->face , &Win);
+
+ Otm->otmSize = Needed;
+
+// FillTM(&Otm->otmTextMetrics, FontGDI, pOS2, pHori, !Error ? &Win : 0);
+ if (!(FontGDI->flRealizedType & FDM_TYPE_TEXT_METRIC))
+ {
+ FillTM(&FontGDI->TextMetric, FontGDI, pOS2, pHori, !Error ? &Win : 0);
+ FontGDI->flRealizedType |= FDM_TYPE_TEXT_METRIC;
+ }
+
+ RtlCopyMemory(&Otm->otmTextMetrics, &FontGDI->TextMetric, sizeof(TEXTMETRICW));
+
+ Otm->otmFiller = 0;
+ RtlCopyMemory(&Otm->otmPanoseNumber, pOS2->panose, PANOSE_COUNT);
+ Otm->otmfsSelection = pOS2->fsSelection;
+ Otm->otmfsType = pOS2->fsType;
+ Otm->otmsCharSlopeRise = pHori->caret_Slope_Rise;
+ Otm->otmsCharSlopeRun = pHori->caret_Slope_Run;
+ Otm->otmItalicAngle = 0; /* POST table */
+ Otm->otmEMSquare = FontGDI->face->units_per_EM;
+ Otm->otmAscent = (FT_MulFix(pOS2->sTypoAscender, YScale) + 32) >> 6;
+ Otm->otmDescent = (FT_MulFix(pOS2->sTypoDescender, YScale) + 32) >> 6;
+ Otm->otmLineGap = (FT_MulFix(pOS2->sTypoLineGap, YScale) + 32) >> 6;
+ Otm->otmsCapEmHeight = (FT_MulFix(pOS2->sCapHeight, YScale) + 32) >> 6;
+ Otm->otmsXHeight = (FT_MulFix(pOS2->sxHeight, YScale) + 32) >> 6;
+ Otm->otmrcFontBox.left = (FT_MulFix(FontGDI->face->bbox.xMin, XScale) + 32) >> 6;
+ Otm->otmrcFontBox.right = (FT_MulFix(FontGDI->face->bbox.xMax, XScale) + 32) >> 6;
+ Otm->otmrcFontBox.top = (FT_MulFix(FontGDI->face->bbox.yMax, YScale) + 32) >> 6;
+ Otm->otmrcFontBox.bottom = (FT_MulFix(FontGDI->face->bbox.yMin, YScale) + 32) >> 6;
+ Otm->otmMacAscent = FontGDI->TextMetric.tmAscent;
+ Otm->otmMacDescent = -FontGDI->TextMetric.tmDescent;
+ Otm->otmMacLineGap = Otm->otmLineGap;
+ Otm->otmusMinimumPPEM = 0; /* TT Header */
+ Otm->otmptSubscriptSize.x = (FT_MulFix(pOS2->ySubscriptXSize, XScale) + 32) >> 6;
+ Otm->otmptSubscriptSize.y = (FT_MulFix(pOS2->ySubscriptYSize, YScale) + 32) >> 6;
+ Otm->otmptSubscriptOffset.x = (FT_MulFix(pOS2->ySubscriptXOffset, XScale) + 32) >> 6;
+ Otm->otmptSubscriptOffset.y = (FT_MulFix(pOS2->ySubscriptYOffset, YScale) + 32) >> 6;
+ Otm->otmptSuperscriptSize.x = (FT_MulFix(pOS2->ySuperscriptXSize, XScale) + 32) >> 6;
+ Otm->otmptSuperscriptSize.y = (FT_MulFix(pOS2->ySuperscriptYSize, YScale) + 32) >> 6;
+ Otm->otmptSuperscriptOffset.x = (FT_MulFix(pOS2->ySuperscriptXOffset, XScale) + 32) >> 6;
+ Otm->otmptSuperscriptOffset.y = (FT_MulFix(pOS2->ySuperscriptYOffset, YScale) + 32) >> 6;
+ Otm->otmsStrikeoutSize = (FT_MulFix(pOS2->yStrikeoutSize, YScale) + 32) >> 6;
+ Otm->otmsStrikeoutPosition = (FT_MulFix(pOS2->yStrikeoutPosition, YScale) + 32) >> 6;
+ if (!pPost)
+ {
+ Otm->otmsUnderscoreSize = 0;
+ Otm->otmsUnderscorePosition = 0;
+ }
+ else
+ {
+ Otm->otmsUnderscoreSize = (FT_MulFix(pPost->underlineThickness, YScale) + 32) >> 6;
+ Otm->otmsUnderscorePosition = (FT_MulFix(pPost->underlinePosition, YScale) + 32) >> 6;
+ }
+
+ IntUnLockFreeType;
+
+ /* otmp* members should clearly have type ptrdiff_t, but M$ knows best */
+ Cp = (char*) Otm + sizeof(OUTLINETEXTMETRICW);
+ Otm->otmpFamilyName = (LPSTR)(Cp - (char*) Otm);
+ wcscpy((WCHAR*) Cp, FamilyNameW.Buffer);
+ Cp += FamilyNameW.Length + sizeof(WCHAR);
+ Otm->otmpStyleName = (LPSTR)(Cp - (char*) Otm);
+ wcscpy((WCHAR*) Cp, StyleNameW.Buffer);
+ Cp += StyleNameW.Length + sizeof(WCHAR);
+ Otm->otmpFaceName = (LPSTR)(Cp - (char*) Otm);
+ wcscpy((WCHAR*) Cp, FamilyNameW.Buffer);
+ if (0 != RtlCompareUnicodeString(&StyleNameW, &Regular, TRUE))
+ {
+ wcscat((WCHAR*) Cp, L" ");
+ wcscat((WCHAR*) Cp, StyleNameW.Buffer);
+ Cp += FamilyNameW.Length + StyleNameW.Length + (sizeof(WCHAR) << 1);
+ }
+ else
+ {
+ Cp += FamilyNameW.Length + sizeof(WCHAR);
+ }
+ Otm->otmpFullName = (LPSTR)(Cp - (char*) Otm);
+ wcscpy((WCHAR*) Cp, FamilyNameW.Buffer);
+ wcscat((WCHAR*) Cp, L" ");
+ wcscat((WCHAR*) Cp, StyleNameW.Buffer);
+
+ RtlFreeUnicodeString(&StyleNameW);
+ RtlFreeUnicodeString(&FamilyNameW);
+
+ return Needed;
+}
+
+static PFONTGDI FASTCALL
+FindFaceNameInList(PUNICODE_STRING FaceName, PLIST_ENTRY Head)
+{
+ PLIST_ENTRY Entry;
+ PFONT_ENTRY CurrentEntry;
+ ANSI_STRING EntryFaceNameA;
+ UNICODE_STRING EntryFaceNameW;
+ FONTGDI *FontGDI;
+
+ Entry = Head->Flink;
+ while (Entry != Head)
+ {
+ CurrentEntry = (PFONT_ENTRY) CONTAINING_RECORD(Entry, FONT_ENTRY, ListEntry);
+
+ FontGDI = CurrentEntry->Font;
+ ASSERT(FontGDI);
+
+ RtlInitAnsiString(&EntryFaceNameA, FontGDI->face->family_name);
+ RtlAnsiStringToUnicodeString(&EntryFaceNameW, &EntryFaceNameA, TRUE);
+ if ((LF_FACESIZE - 1) * sizeof(WCHAR) < EntryFaceNameW.Length)
+ {
+ EntryFaceNameW.Length = (LF_FACESIZE - 1) * sizeof(WCHAR);
+ EntryFaceNameW.Buffer[LF_FACESIZE - 1] = L'\0';
+ }
+
+ if (0 == RtlCompareUnicodeString(FaceName, &EntryFaceNameW, TRUE))
+ {
+ RtlFreeUnicodeString(&EntryFaceNameW);
+ return FontGDI;
+ }
+
+ RtlFreeUnicodeString(&EntryFaceNameW);
+ Entry = Entry->Flink;
+ }
+
+ return NULL;
+}
+
+static PFONTGDI FASTCALL
+FindFaceNameInLists(PUNICODE_STRING FaceName)
+{
+ PPROCESSINFO Win32Process;
+ PFONTGDI Font;
+
+ /* Search the process local list */
+ Win32Process = PsGetCurrentProcessWin32Process();
+ IntLockProcessPrivateFonts(Win32Process);
+ Font = FindFaceNameInList(FaceName, &Win32Process->PrivateFontListHead);
+ IntUnLockProcessPrivateFonts(Win32Process);
+ if (NULL != Font)
+ {
+ return Font;
+ }
+
+ /* Search the global list */
+ IntLockGlobalFonts;
+ Font = FindFaceNameInList(FaceName, &FontListHead);
+ IntUnLockGlobalFonts;
+
+ return Font;
+}
+
+static void FASTCALL
+FontFamilyFillInfo(PFONTFAMILYINFO Info, PCWSTR FaceName, PFONTGDI FontGDI)
+{
+ ANSI_STRING StyleA;
+ UNICODE_STRING StyleW;
+ TT_OS2 *pOS2;
+ FONTSIGNATURE fs;
+ CHARSETINFO CharSetInfo;
+ unsigned i, Size;
+ OUTLINETEXTMETRICW *Otm;
+ LOGFONTW *Lf;
+ TEXTMETRICW *TM;
+ NEWTEXTMETRICW *Ntm;
+ DWORD fs0;
+
+ RtlZeroMemory(Info, sizeof(FONTFAMILYINFO));
+ Size = IntGetOutlineTextMetrics(FontGDI, 0, NULL);
+ Otm = ExAllocatePoolWithTag(PagedPool, Size, TAG_GDITEXT);
+ if (!Otm)
+ {
+ return;
+ }
+ IntGetOutlineTextMetrics(FontGDI, Size, Otm);
+
+ Lf = &Info->EnumLogFontEx.elfLogFont;
+ TM = &Otm->otmTextMetrics;
+
+ Lf->lfHeight = TM->tmHeight;
+ Lf->lfWidth = TM->tmAveCharWidth;
+ Lf->lfWeight = TM->tmWeight;
+ Lf->lfItalic = TM->tmItalic;
+ Lf->lfPitchAndFamily = (TM->tmPitchAndFamily & 0xf1) + 1;
+ Lf->lfCharSet = TM->tmCharSet;
+ Lf->lfOutPrecision = OUT_OUTLINE_PRECIS;
+ Lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
+ Lf->lfQuality = PROOF_QUALITY;
+
+ Ntm = &Info->NewTextMetricEx.ntmTm;
+ Ntm->tmHeight = TM->tmHeight;
+ Ntm->tmAscent = TM->tmAscent;
+ Ntm->tmDescent = TM->tmDescent;
+ Ntm->tmInternalLeading = TM->tmInternalLeading;
+ Ntm->tmExternalLeading = TM->tmExternalLeading;
+ Ntm->tmAveCharWidth = TM->tmAveCharWidth;
+ Ntm->tmMaxCharWidth = TM->tmMaxCharWidth;
+ Ntm->tmWeight = TM->tmWeight;
+ Ntm->tmOverhang = TM->tmOverhang;
+ Ntm->tmDigitizedAspectX = TM->tmDigitizedAspectX;
+ Ntm->tmDigitizedAspectY = TM->tmDigitizedAspectY;
+ Ntm->tmFirstChar = TM->tmFirstChar;
+ Ntm->tmLastChar = TM->tmLastChar;
+ Ntm->tmDefaultChar = TM->tmDefaultChar;
+ Ntm->tmBreakChar = TM->tmBreakChar;
+ Ntm->tmItalic = TM->tmItalic;
+ Ntm->tmUnderlined = TM->tmUnderlined;
+ Ntm->tmStruckOut = TM->tmStruckOut;
+ Ntm->tmPitchAndFamily = TM->tmPitchAndFamily;
+ Ntm->tmCharSet = TM->tmCharSet;
+ Ntm->ntmFlags = TM->tmItalic ? NTM_ITALIC : 0;
+
+ if (550 < TM->tmWeight) Ntm->ntmFlags |= NTM_BOLD;
+
+ if (0 == Ntm->ntmFlags) Ntm->ntmFlags = NTM_REGULAR;
+
+ Ntm->ntmSizeEM = Otm->otmEMSquare;
+ Ntm->ntmCellHeight = 0;
+ Ntm->ntmAvgWidth = 0;
+
+ Info->FontType = (0 != (TM->tmPitchAndFamily & TMPF_TRUETYPE)
+ ? TRUETYPE_FONTTYPE : 0);
+
+ if (0 == (TM->tmPitchAndFamily & TMPF_VECTOR))
+ Info->FontType |= RASTER_FONTTYPE;
+
+ ExFreePoolWithTag(Otm, TAG_GDITEXT);
+
+ wcsncpy(Info->EnumLogFontEx.elfLogFont.lfFaceName, FaceName, LF_FACESIZE);
+ wcsncpy(Info->EnumLogFontEx.elfFullName, FaceName, LF_FULLFACESIZE);
+ RtlInitAnsiString(&StyleA, FontGDI->face->style_name);
+ RtlAnsiStringToUnicodeString(&StyleW, &StyleA, TRUE);
+ wcsncpy(Info->EnumLogFontEx.elfStyle, StyleW.Buffer, LF_FACESIZE);
+ RtlFreeUnicodeString(&StyleW);
+
+ Info->EnumLogFontEx.elfLogFont.lfCharSet = DEFAULT_CHARSET;
+ Info->EnumLogFontEx.elfScript[0] = L'\0';
+ IntLockFreeType;
+ pOS2 = FT_Get_Sfnt_Table(FontGDI->face, ft_sfnt_os2);
+ IntUnLockFreeType;
+ if (NULL != pOS2)
+ {
+ fs.fsCsb[0] = pOS2->ulCodePageRange1;
+ fs.fsCsb[1] = pOS2->ulCodePageRange2;
+ fs.fsUsb[0] = pOS2->ulUnicodeRange1;
+ fs.fsUsb[1] = pOS2->ulUnicodeRange2;
+ fs.fsUsb[2] = pOS2->ulUnicodeRange3;
+ fs.fsUsb[3] = pOS2->ulUnicodeRange4;
+
+ if (0 == pOS2->version)
+ {
+ FT_UInt Dummy;
+
+ if (FT_Get_First_Char(FontGDI->face, &Dummy) < 0x100)
+ fs.fsCsb[0] |= FS_LATIN1;
+ else
+ fs.fsCsb[0] |= FS_SYMBOL;
+ }
+ if (fs.fsCsb[0] == 0)
+ { /* let's see if we can find any interesting cmaps */
+ for (i = 0; i < FontGDI->face->num_charmaps; i++)
+ {
+ switch (FontGDI->face->charmaps[i]->encoding)
+ {
+ case FT_ENCODING_UNICODE:
+ case FT_ENCODING_APPLE_ROMAN:
+ fs.fsCsb[0] |= FS_LATIN1;
+ break;
+ case FT_ENCODING_MS_SYMBOL:
+ fs.fsCsb[0] |= FS_SYMBOL;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ for (i = 0; i < MAXTCIINDEX; i++)
+ {
+ fs0 = 1L << i;
+ if (fs.fsCsb[0] & fs0)
+ {
+ if (!IntTranslateCharsetInfo(&fs0, &CharSetInfo, TCI_SRCFONTSIG))
+ {
+ CharSetInfo.ciCharset = DEFAULT_CHARSET;
+ }
+ if (DEFAULT_CHARSET != CharSetInfo.ciCharset)
+ {
+ Info->EnumLogFontEx.elfLogFont.lfCharSet = CharSetInfo.ciCharset;
+ if (NULL != ElfScripts[i])
+ wcscpy(Info->EnumLogFontEx.elfScript, ElfScripts[i]);
+ else
+ {
+ DPRINT1("Unknown elfscript for bit %d\n", i);
+ }
+ }
+ }
+ }
+ Info->NewTextMetricEx.ntmFontSig = fs;
+ }
+}
+
+static int FASTCALL
+FindFaceNameInInfo(PUNICODE_STRING FaceName, PFONTFAMILYINFO Info, DWORD InfoEntries)
+{
+ DWORD i;
+ UNICODE_STRING InfoFaceName;
+
+ for (i = 0; i < InfoEntries; i++)
+ {
+ RtlInitUnicodeString(&InfoFaceName, Info[i].EnumLogFontEx.elfLogFont.lfFaceName);
+ if (0 == RtlCompareUnicodeString(&InfoFaceName, FaceName, TRUE))
+ {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+static BOOLEAN FASTCALL
+FontFamilyInclude(LPLOGFONTW LogFont, PUNICODE_STRING FaceName,
+ PFONTFAMILYINFO Info, DWORD InfoEntries)
+{
+ UNICODE_STRING LogFontFaceName;
+
+ RtlInitUnicodeString(&LogFontFaceName, LogFont->lfFaceName);
+ if (0 != LogFontFaceName.Length
+ && 0 != RtlCompareUnicodeString(&LogFontFaceName, FaceName, TRUE))
+ {
+ return FALSE;
+ }
+
+ return FindFaceNameInInfo(FaceName, Info, InfoEntries) < 0;
+}
+
+static BOOLEAN FASTCALL
+GetFontFamilyInfoForList(LPLOGFONTW LogFont,
+ PFONTFAMILYINFO Info,
+ DWORD *Count,
+ DWORD Size,
+ PLIST_ENTRY Head)
+{
+ PLIST_ENTRY Entry;
+ PFONT_ENTRY CurrentEntry;
+ ANSI_STRING EntryFaceNameA;
+ UNICODE_STRING EntryFaceNameW;
+ FONTGDI *FontGDI;
+
+ Entry = Head->Flink;
+ while (Entry != Head)
+ {
+ CurrentEntry = (PFONT_ENTRY) CONTAINING_RECORD(Entry, FONT_ENTRY, ListEntry);
+
+ FontGDI = CurrentEntry->Font;
+ ASSERT(FontGDI);
+
+ RtlInitAnsiString(&EntryFaceNameA, FontGDI->face->family_name);
+ RtlAnsiStringToUnicodeString(&EntryFaceNameW, &EntryFaceNameA, TRUE);
+ if ((LF_FACESIZE - 1) * sizeof(WCHAR) < EntryFaceNameW.Length)
+ {
+ EntryFaceNameW.Length = (LF_FACESIZE - 1) * sizeof(WCHAR);
+ EntryFaceNameW.Buffer[LF_FACESIZE - 1] = L'\0';
+ }
+
+ if (FontFamilyInclude(LogFont, &EntryFaceNameW, Info, min(*Count, Size)))
+ {
+ if (*Count < Size)
+ {
+ FontFamilyFillInfo(Info + *Count, EntryFaceNameW.Buffer, FontGDI);
+ }
+ (*Count)++;
+ }
+ RtlFreeUnicodeString(&EntryFaceNameW);
+ Entry = Entry->Flink;
+ }
+
+ return TRUE;
+}
+
+typedef struct FontFamilyInfoCallbackContext
+{
+ LPLOGFONTW LogFont;
+ PFONTFAMILYINFO Info;
+ DWORD Count;
+ DWORD Size;
+} FONT_FAMILY_INFO_CALLBACK_CONTEXT, *PFONT_FAMILY_INFO_CALLBACK_CONTEXT;
+
+static NTSTATUS APIENTRY
+FontFamilyInfoQueryRegistryCallback(IN PWSTR ValueName, IN ULONG ValueType,
+ IN PVOID ValueData, IN ULONG ValueLength,
+ IN PVOID Context, IN PVOID EntryContext)
+{
+ PFONT_FAMILY_INFO_CALLBACK_CONTEXT InfoContext;
+ UNICODE_STRING RegistryName, RegistryValue;
+ int Existing;
+ PFONTGDI FontGDI;
+
+ if (REG_SZ != ValueType)
+ {
+ return STATUS_SUCCESS;
+ }
+ InfoContext = (PFONT_FAMILY_INFO_CALLBACK_CONTEXT) Context;
+ RtlInitUnicodeString(&RegistryName, ValueName);
+
+ /* Do we need to include this font family? */
+ if (FontFamilyInclude(InfoContext->LogFont, &RegistryName, InfoContext->Info,
+ min(InfoContext->Count, InfoContext->Size)))
+ {
+ RtlInitUnicodeString(&RegistryValue, (PCWSTR) ValueData);
+ Existing = FindFaceNameInInfo(&RegistryValue, InfoContext->Info,
+ min(InfoContext->Count, InfoContext->Size));
+ if (0 <= Existing)
+ {
+ /* We already have the information about the "real" font. Just copy it */
+ if (InfoContext->Count < InfoContext->Size)
+ {
+ InfoContext->Info[InfoContext->Count] = InfoContext->Info[Existing];
+ wcsncpy(InfoContext->Info[InfoContext->Count].EnumLogFontEx.elfLogFont.lfFaceName,
+ RegistryName.Buffer, LF_FACESIZE);
+ }
+ InfoContext->Count++;
+ return STATUS_SUCCESS;
+ }
+
+ /* Try to find information about the "real" font */
+ FontGDI = FindFaceNameInLists(&RegistryValue);
+ if (NULL == FontGDI)
+ {
+ /* "Real" font not found, discard this registry entry */
+ return STATUS_SUCCESS;
+ }
+
+ /* Return info about the "real" font but with the name of the alias */
+ if (InfoContext->Count < InfoContext->Size)
+ {
+ FontFamilyFillInfo(InfoContext->Info + InfoContext->Count,
+ RegistryName.Buffer, FontGDI);
+ }
+ InfoContext->Count++;
+ return STATUS_SUCCESS;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static BOOLEAN FASTCALL
+GetFontFamilyInfoForSubstitutes(LPLOGFONTW LogFont,
+ PFONTFAMILYINFO Info,
+ DWORD *Count,
+ DWORD Size)
+{
+ RTL_QUERY_REGISTRY_TABLE QueryTable[2] = {{0}};
+ FONT_FAMILY_INFO_CALLBACK_CONTEXT Context;
+ NTSTATUS Status;
+
+ /* Enumerate font families found in HKLM\Software\Microsoft\Windows NT\CurrentVersion\SysFontSubstitutes
+ The real work is done in the registry callback function */
+ Context.LogFont = LogFont;
+ Context.Info = Info;
+ Context.Count = *Count;
+ Context.Size = Size;
+
+ QueryTable[0].QueryRoutine = FontFamilyInfoQueryRegistryCallback;
+ QueryTable[0].Flags = 0;
+ QueryTable[0].Name = NULL;
+ QueryTable[0].EntryContext = NULL;
+ QueryTable[0].DefaultType = REG_NONE;
+ QueryTable[0].DefaultData = NULL;
+ QueryTable[0].DefaultLength = 0;
+
+ QueryTable[1].QueryRoutine = NULL;
+ QueryTable[1].Name = NULL;
+
+ Status = RtlQueryRegistryValues(RTL_REGISTRY_WINDOWS_NT,
+ L"SysFontSubstitutes",
+ QueryTable,
+ &Context,
+ NULL);
+ if (NT_SUCCESS(Status))
+ {
+ *Count = Context.Count;
+ }
+
+ return NT_SUCCESS(Status) || STATUS_OBJECT_NAME_NOT_FOUND == Status;
+}
+
+BOOL
+FASTCALL
+ftGdiGetRasterizerCaps(LPRASTERIZER_STATUS lprs)
+{
+ if ( lprs )
+ {
+ lprs->nSize = sizeof(RASTERIZER_STATUS);
+ lprs->wFlags = TT_AVAILABLE | TT_ENABLED;
+ lprs->nLanguageID = gusLanguageID;
+ return TRUE;
+ }
+ SetLastWin32Error(ERROR_INVALID_PARAMETER);
+ return FALSE;
+}
+
+
+FT_BitmapGlyph APIENTRY
+ftGdiGlyphCacheGet(
+ FT_Face Face,
+ INT GlyphIndex,
+ INT Height)
+{
+ PLIST_ENTRY CurrentEntry;
+ PFONT_CACHE_ENTRY FontEntry;
+
+ CurrentEntry = FontCacheListHead.Flink;
+ while (CurrentEntry != &FontCacheListHead)
+ {
+ FontEntry = (PFONT_CACHE_ENTRY)CurrentEntry;
+ if (FontEntry->Face == Face &&
+ FontEntry->GlyphIndex == GlyphIndex &&
+ FontEntry->Height == Height)
+ break;
+ CurrentEntry = CurrentEntry->Flink;
+ }
+
+ if (CurrentEntry == &FontCacheListHead)
+ {
+ return NULL;
+ }
+
+ RemoveEntryList(CurrentEntry);
+ InsertHeadList(&FontCacheListHead, CurrentEntry);
+ return FontEntry->BitmapGlyph;
+}
+
+FT_BitmapGlyph APIENTRY
+ftGdiGlyphCacheSet(
+ FT_Face Face,
+ INT GlyphIndex,
+ INT Height,
+ FT_GlyphSlot GlyphSlot,
+ FT_Render_Mode RenderMode)
+{
+ FT_Glyph GlyphCopy;
+ INT error;
+ PFONT_CACHE_ENTRY NewEntry;
+ FT_Bitmap AlignedBitmap;
+ FT_BitmapGlyph BitmapGlyph;
+
+ error = FT_Get_Glyph(GlyphSlot, &GlyphCopy);
+ if (error)
+ {
+ DPRINT1("Failure caching glyph.\n");
+ return NULL;
+ };
+
+ error = FT_Glyph_To_Bitmap(&GlyphCopy, RenderMode, 0, 1);
+ if (error)
+ {
+ DPRINT1("Failure rendering glyph.\n");
+ return NULL;
+ };
+
+ NewEntry = ExAllocatePoolWithTag(PagedPool, sizeof(FONT_CACHE_ENTRY), TAG_FONT);
+ if (!NewEntry)
+ {
+ DPRINT1("Alloc failure caching glyph.\n");
+ FT_Done_Glyph(GlyphCopy);
+ return NULL;
+ }
+
+ BitmapGlyph = (FT_BitmapGlyph)GlyphCopy;
+ FT_Bitmap_New(&AlignedBitmap);
+ if(FT_Bitmap_Convert(GlyphSlot->library, &BitmapGlyph->bitmap, &AlignedBitmap, 4))
+ {
+ DPRINT1("Conversion failed\n");
+ FT_Done_Glyph((FT_Glyph)BitmapGlyph);
+ return NULL;
+ }
+
+ FT_Bitmap_Done(GlyphSlot->library, &BitmapGlyph->bitmap);
+ BitmapGlyph->bitmap = AlignedBitmap;
+
+ NewEntry->GlyphIndex = GlyphIndex;
+ NewEntry->Face = Face;
+ NewEntry->BitmapGlyph = BitmapGlyph;
+ NewEntry->Height = Height;
+
+ InsertHeadList(&FontCacheListHead, &NewEntry->ListEntry);
+ if (FontCacheNumEntries++ > MAX_FONT_CACHE)
+ {
+ NewEntry = (PFONT_CACHE_ENTRY)FontCacheListHead.Blink;
+ FT_Done_Glyph((FT_Glyph)NewEntry->BitmapGlyph);
+ RemoveTailList(&FontCacheListHead);
+ ExFreePool(NewEntry);
+ FontCacheNumEntries--;
+ }
+
+ return BitmapGlyph;
+}
+
+
+static
+void
+FTVectorToPOINTFX(FT_Vector *vec, POINTFX *pt)
+{
+ pt->x.value = vec->x >> 6;
+ pt->x.fract = (vec->x & 0x3f) << 10;
+ pt->x.fract |= ((pt->x.fract >> 6) | (pt->x.fract >> 12));
+ pt->y.value = vec->y >> 6;
+ pt->y.fract = (vec->y & 0x3f) << 10;
+ pt->y.fract |= ((pt->y.fract >> 6) | (pt->y.fract >> 12));
+ return;
+}
+
+/*
+ This function builds an FT_Fixed from a float. It puts the integer part
+ in the highest 16 bits and the decimal part in the lowest 16 bits of the FT_Fixed.
+ It fails if the integer part of the float number is greater than SHORT_MAX.
+*/
+static __inline FT_Fixed FT_FixedFromFloat(float f)
+{
+ short value = f;
+ unsigned short fract = (f - value) * 0xFFFF;
+ return (FT_Fixed)((long)value << 16 | (unsigned long)fract);
+}
+
+/*
+ This function builds an FT_Fixed from a FIXED. It simply put f.value
+ in the highest 16 bits and f.fract in the lowest 16 bits of the FT_Fixed.
+*/
+static __inline FT_Fixed FT_FixedFromFIXED(FIXED f)
+{
+ return (FT_Fixed)((long)f.value << 16 | (unsigned long)f.fract);
+}
+
+/*
+ * Based on WineEngGetGlyphOutline
+ *
+ */
+ULONG
+FASTCALL
+ftGdiGetGlyphOutline(
+ PDC dc,
+ WCHAR wch,
+ UINT iFormat,
+ LPGLYPHMETRICS pgm,
+ ULONG cjBuf,
+ PVOID pvBuf,
+ LPMAT2 pmat2,
+ BOOL bIgnoreRotation)
+{
+ static const FT_Matrix identityMat = {(1 << 16), 0, 0, (1 << 16)};
+ PDC_ATTR pdcattr;
+ PTEXTOBJ TextObj;
+ PFONTGDI FontGDI;
+ HFONT hFont = 0;
+ GLYPHMETRICS gm;
+ ULONG Size;
+ FT_Face ft_face;
+ FT_UInt glyph_index;
+ DWORD width, height, pitch, needed = 0;
+ FT_Bitmap ft_bitmap;
+ FT_Error error;
+ INT left, right, top = 0, bottom = 0;
+ FT_Angle angle = 0;
+ FT_Int load_flags = FT_LOAD_DEFAULT | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
+ FLOAT eM11, widthRatio = 1.0;
+ FT_Matrix transMat = identityMat;
+ BOOL needsTransform = FALSE;
+ INT orientation;
+ LONG aveWidth;
+ INT adv, lsb, bbx; /* These three hold to widths of the unrotated chars */
+ OUTLINETEXTMETRICW *potm;
+ int n = 0;
+ FT_CharMap found = 0, charmap;
+ XFORM xForm;
+
+ DPRINT("%d, %08x, %p, %08lx, %p, %p\n", wch, iFormat, pgm,
+ cjBuf, pvBuf, pmat2);
+
+ pdcattr = dc->pdcattr;
+
+ MatrixS2XForm(&xForm, &dc->dclevel.mxWorldToDevice);
+ eM11 = xForm.eM11;
+
+ hFont = pdcattr->hlfntNew;
+ TextObj = RealizeFontInit(hFont);
+
+ if (!TextObj)
+ {
+ SetLastWin32Error(ERROR_INVALID_HANDLE);
+ return GDI_ERROR;
+ }
+ FontGDI = ObjToGDI(TextObj->Font, FONT);
+ ft_face = FontGDI->face;
+
+ aveWidth = FT_IS_SCALABLE(ft_face) ? TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfWidth: 0;
+ orientation = FT_IS_SCALABLE(ft_face) ? TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfOrientation: 0;
+
+ Size = IntGetOutlineTextMetrics(FontGDI, 0, NULL);
+ potm = ExAllocatePoolWithTag(PagedPool, Size, TAG_GDITEXT);
+ if (!potm)
+ {
+ SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
+ TEXTOBJ_UnlockText(TextObj);
+ return GDI_ERROR;
+ }
+ IntGetOutlineTextMetrics(FontGDI, Size, potm);
+
+ IntLockFreeType;
+
+ /* During testing, I never saw this used. In here just incase.*/
+ if (ft_face->charmap == NULL)
+ {
+ DPRINT("WARNING: No charmap selected!\n");
+ DPRINT("This font face has %d charmaps\n", ft_face->num_charmaps);
+
+ for (n = 0; n < ft_face->num_charmaps; n++)
+ {
+ charmap = ft_face->charmaps[n];
+ DPRINT("found charmap encoding: %u\n", charmap->encoding);
+ if (charmap->encoding != 0)
+ {
+ found = charmap;
+ break;
+ }
+ }
+ if (!found)
+ {
+ DPRINT1("WARNING: Could not find desired charmap!\n");
+ }
+ error = FT_Set_Charmap(ft_face, found);
+ if (error)
+ {
+ DPRINT1("WARNING: Could not set the charmap!\n");
+ }
+ }
+
+// FT_Set_Pixel_Sizes(ft_face,
+// TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfWidth,
+ /* FIXME should set character height if neg */
+// (TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight < 0 ? - TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight :
+// TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight == 0 ? 11 : TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight));
+
+ TEXTOBJ_UnlockText(TextObj);
+
+ if (iFormat & GGO_GLYPH_INDEX)
+ {
+ glyph_index = wch;
+ iFormat &= ~GGO_GLYPH_INDEX;
+ }
+ else glyph_index = FT_Get_Char_Index(ft_face, wch);
+
+ if (orientation || (iFormat != GGO_METRICS && iFormat != GGO_BITMAP) || aveWidth || pmat2)
+ load_flags |= FT_LOAD_NO_BITMAP;
+
+ if (iFormat & GGO_UNHINTED)
+ {
+ load_flags |= FT_LOAD_NO_HINTING;
+ iFormat &= ~GGO_UNHINTED;
+ }
+
+ error = FT_Load_Glyph(ft_face, glyph_index, load_flags);
+ if (error)
+ {
+ DPRINT1("WARNING: Failed to load and render glyph! [index: %u]\n", glyph_index);
+ IntUnLockFreeType;
+ if (potm) ExFreePoolWithTag(potm, TAG_GDITEXT);
+ return GDI_ERROR;
+ }
+ IntUnLockFreeType;
+
+ if (aveWidth && potm)
+ {
+ widthRatio = (FLOAT)aveWidth * eM11 /
+ (FLOAT) potm->otmTextMetrics.tmAveCharWidth;
+ }
+
+ left = (INT)(ft_face->glyph->metrics.horiBearingX * widthRatio) & -64;
+ right = (INT)((ft_face->glyph->metrics.horiBearingX +
+ ft_face->glyph->metrics.width) * widthRatio + 63) & -64;
+
+ adv = (INT)((ft_face->glyph->metrics.horiAdvance * widthRatio) + 63) >> 6;
+ lsb = left >> 6;
+ bbx = (right - left) >> 6;
+
+ DPRINT("Advance = %d, lsb = %d, bbx = %d\n",adv, lsb, bbx);
+
+ IntLockFreeType;
+
+ /* Scaling transform */
+ if (aveWidth)
+ {
+ FT_Matrix scaleMat;
+ DPRINT("Scaling Trans!\n");
+ scaleMat.xx = FT_FixedFromFloat(widthRatio);
+ scaleMat.xy = 0;
+ scaleMat.yx = 0;
+ scaleMat.yy = (1 << 16);
+ FT_Matrix_Multiply(&scaleMat, &transMat);
+ needsTransform = TRUE;
+ }
+
+ /* Slant transform */
+ if (potm->otmTextMetrics.tmItalic)
+ {
+ FT_Matrix slantMat;
+ DPRINT("Slant Trans!\n");
+ slantMat.xx = (1 << 16);
+ slantMat.xy = ((1 << 16) >> 2);
+ slantMat.yx = 0;
+ slantMat.yy = (1 << 16);
+ FT_Matrix_Multiply(&slantMat, &transMat);
+ needsTransform = TRUE;
+ }
+
+ /* Rotation transform */
+ if (orientation)
+ {
+ FT_Matrix rotationMat;
+ FT_Vector vecAngle;
+ DPRINT("Rotation Trans!\n");
+ angle = FT_FixedFromFloat((float)orientation / 10.0);
+ FT_Vector_Unit(&vecAngle, angle);
+ rotationMat.xx = vecAngle.x;
+ rotationMat.xy = -vecAngle.y;
+ rotationMat.yx = -rotationMat.xy;
+ rotationMat.yy = rotationMat.xx;
+ FT_Matrix_Multiply(&rotationMat, &transMat);
+ needsTransform = TRUE;
+ }
+
+ /* Extra transformation specified by caller */
+ if (pmat2)
+ {
+ FT_Matrix extraMat;
+ DPRINT("MAT2 Matrix Trans!\n");
+ extraMat.xx = FT_FixedFromFIXED(pmat2->eM11);
+ extraMat.xy = FT_FixedFromFIXED(pmat2->eM21);
+ extraMat.yx = FT_FixedFromFIXED(pmat2->eM12);
+ extraMat.yy = FT_FixedFromFIXED(pmat2->eM22);
+ FT_Matrix_Multiply(&extraMat, &transMat);
+ needsTransform = TRUE;
+ }
+
+ if (potm) ExFreePoolWithTag(potm, TAG_GDITEXT); /* It looks like we are finished with potm ATM.*/
+
+ if (!needsTransform)
+ {
+ DPRINT("No Need to be Transformed!\n");
+ top = (ft_face->glyph->metrics.horiBearingY + 63) & -64;
+ bottom = (ft_face->glyph->metrics.horiBearingY -
+ ft_face->glyph->metrics.height) & -64;
+ gm.gmCellIncX = adv;
+ gm.gmCellIncY = 0;
+ }
+ else
+ {
+ INT xc, yc;
+ FT_Vector vec;
+ for (xc = 0; xc < 2; xc++)
+ {
+ for (yc = 0; yc < 2; yc++)
+ {
+ vec.x = (ft_face->glyph->metrics.horiBearingX +
+ xc * ft_face->glyph->metrics.width);
+ vec.y = ft_face->glyph->metrics.horiBearingY -
+ yc * ft_face->glyph->metrics.height;
+ DPRINT("Vec %ld,%ld\n", vec.x, vec.y);
+ FT_Vector_Transform(&vec, &transMat);
+ if (xc == 0 && yc == 0)
+ {
+ left = right = vec.x;
+ top = bottom = vec.y;
+ }
+ else
+ {
+ if (vec.x < left) left = vec.x;
+ else if (vec.x > right) right = vec.x;
+ if (vec.y < bottom) bottom = vec.y;
+ else if (vec.y > top) top = vec.y;
+ }
+ }
+ }
+ left = left & -64;
+ right = (right + 63) & -64;
+ bottom = bottom & -64;
+ top = (top + 63) & -64;
+
+ DPRINT("transformed box: (%d,%d - %d,%d)\n", left, top, right, bottom);
+ vec.x = ft_face->glyph->metrics.horiAdvance;
+ vec.y = 0;
+ FT_Vector_Transform(&vec, &transMat);
+ gm.gmCellIncX = (vec.x+63) >> 6;
+ gm.gmCellIncY = -((vec.y+63) >> 6);
+ }
+ gm.gmBlackBoxX = (right - left) >> 6;
+ gm.gmBlackBoxY = (top - bottom) >> 6;
+ gm.gmptGlyphOrigin.x = left >> 6;
+ gm.gmptGlyphOrigin.y = top >> 6;
+
+ DPRINT("CX %d CY %d BBX %d BBY %d GOX %d GOY %d\n",
+ gm.gmCellIncX, gm.gmCellIncY,
+ gm.gmBlackBoxX, gm.gmBlackBoxY,
+ gm.gmptGlyphOrigin.x, gm.gmptGlyphOrigin.y);
+
+ IntUnLockFreeType;
+
+ if (pgm) RtlCopyMemory(pgm, &gm, sizeof(GLYPHMETRICS));
+
+ if (iFormat == GGO_METRICS)
+ {
+ DPRINT("GGO_METRICS Exit!\n");
+ return 1; /* FIXME */
+ }
+
+ if (ft_face->glyph->format != ft_glyph_format_outline && iFormat != GGO_BITMAP)
+ {
+ DPRINT1("loaded a bitmap\n");
+ return GDI_ERROR;
+ }
+
+ switch (iFormat)
+ {
+ case GGO_BITMAP:
+ width = gm.gmBlackBoxX;
+ height = gm.gmBlackBoxY;
+ pitch = ((width + 31) >> 5) << 2;
+ needed = pitch * height;
+
+ if (!pvBuf || !cjBuf) break;
+
+ switch (ft_face->glyph->format)
+ {
+ case ft_glyph_format_bitmap:
+ {
+ BYTE *src = ft_face->glyph->bitmap.buffer, *dst = pvBuf;
+ INT w = (ft_face->glyph->bitmap.width + 7) >> 3;
+ INT h = ft_face->glyph->bitmap.rows;
+ while (h--)
+ {
+ RtlCopyMemory(dst, src, w);
+ src += ft_face->glyph->bitmap.pitch;
+ dst += pitch;
+ }
+ break;
+ }
+
+ case ft_glyph_format_outline:
+ ft_bitmap.width = width;
+ ft_bitmap.rows = height;
+ ft_bitmap.pitch = pitch;
+ ft_bitmap.pixel_mode = ft_pixel_mode_mono;
+ ft_bitmap.buffer = pvBuf;
+
+ IntLockFreeType;
+ if (needsTransform)
+ {
+ FT_Outline_Transform(&ft_face->glyph->outline, &transMat);
+ }
+ FT_Outline_Translate(&ft_face->glyph->outline, -left, -bottom );
+ /* Note: FreeType will only set 'black' bits for us. */
+ RtlZeroMemory(pvBuf, needed);
+ FT_Outline_Get_Bitmap(library, &ft_face->glyph->outline, &ft_bitmap);
+ IntUnLockFreeType;
+ break;
+
+ default:
+ DPRINT1("loaded glyph format %x\n", ft_face->glyph->format);
+ return GDI_ERROR;
+ }
+ break;
+
+ case GGO_GRAY2_BITMAP:
+ case GGO_GRAY4_BITMAP:
+ case GGO_GRAY8_BITMAP:
+ {
+ unsigned int mult, row, col;
+ BYTE *start, *ptr;
+
+ width = gm.gmBlackBoxX;
+ height = gm.gmBlackBoxY;
+ pitch = (width + 3) / 4 * 4;
+ needed = pitch * height;
+
+ if (!pvBuf || !cjBuf) break;
+
+ switch (ft_face->glyph->format)
+ {
+ case ft_glyph_format_bitmap:
+ {
+ BYTE *src = ft_face->glyph->bitmap.buffer, *dst = pvBuf;
+ INT h = ft_face->glyph->bitmap.rows;
+ INT x;
+ while (h--)
+ {
+ for (x = 0; x < pitch; x++)
+ {
+ if (x < ft_face->glyph->bitmap.width)
+ dst[x] = (src[x / 8] & (1 << ( (7 - (x % 8))))) ? 0xff : 0;
+ else
+ dst[x] = 0;
+ }
+ src += ft_face->glyph->bitmap.pitch;
+ dst += pitch;
+ }
+ return needed;
+ }
+ case ft_glyph_format_outline:
+ {
+ ft_bitmap.width = width;
+ ft_bitmap.rows = height;
+ ft_bitmap.pitch = pitch;
+ ft_bitmap.pixel_mode = ft_pixel_mode_grays;
+ ft_bitmap.buffer = pvBuf;
+
+ IntLockFreeType;
+ if (needsTransform)
+ {
+ FT_Outline_Transform(&ft_face->glyph->outline, &transMat);
+ }
+ FT_Outline_Translate(&ft_face->glyph->outline, -left, -bottom );
+ RtlZeroMemory(ft_bitmap.buffer, cjBuf);
+ FT_Outline_Get_Bitmap(library, &ft_face->glyph->outline, &ft_bitmap);
+ IntUnLockFreeType;
+
+ if (iFormat == GGO_GRAY2_BITMAP)
+ mult = 4;
+ else if (iFormat == GGO_GRAY4_BITMAP)
+ mult = 16;
+ else if (iFormat == GGO_GRAY8_BITMAP)
+ mult = 64;
+ else
+ {
+ return GDI_ERROR;
+ }
+ }
+ default:
+ DPRINT1("loaded glyph format %x\n", ft_face->glyph->format);
+ return GDI_ERROR;
+ }
+ start = pvBuf;
+ for (row = 0; row < height; row++)
+ {
+ ptr = start;
+ for (col = 0; col < width; col++, ptr++)
+ {
+ *ptr = (((int)*ptr) * mult + 128) / 256;
+ }
+ start += pitch;
+ }
+ break;
+ }
+
+ case GGO_NATIVE:
+ {
+ int contour, point = 0, first_pt;
+ FT_Outline *outline = &ft_face->glyph->outline;
+ TTPOLYGONHEADER *pph;
+ TTPOLYCURVE *ppc;
+ DWORD pph_start, cpfx, type;
+
+ if (cjBuf == 0) pvBuf = NULL; /* This is okay, need cjBuf to allocate. */
+
+ IntLockFreeType;
+ if (needsTransform && pvBuf) FT_Outline_Transform(outline, &transMat);
+
+ for (contour = 0; contour < outline->n_contours; contour++)
+ {
+ pph_start = needed;
+ pph = (TTPOLYGONHEADER *)((char *)pvBuf + needed);
+ first_pt = point;
+ if (pvBuf)
+ {
+ pph->dwType = TT_POLYGON_TYPE;
+ FTVectorToPOINTFX(&outline->points[point], &pph->pfxStart);
+ }
+ needed += sizeof(*pph);
+ point++;
+ while (point <= outline->contours[contour])
+ {
+ ppc = (TTPOLYCURVE *)((char *)pvBuf + needed);
+ type = (outline->tags[point] & FT_Curve_Tag_On) ?
+ TT_PRIM_LINE : TT_PRIM_QSPLINE;
+ cpfx = 0;
+ do
+ {
+ if (pvBuf)
+ FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
+ cpfx++;
+ point++;
+ }
+ while (point <= outline->contours[contour] &&
+ (outline->tags[point] & FT_Curve_Tag_On) ==
+ (outline->tags[point-1] & FT_Curve_Tag_On));
+
+ /* At the end of a contour Windows adds the start point, but
+ only for Beziers */
+ if (point > outline->contours[contour] &&
+ !(outline->tags[point-1] & FT_Curve_Tag_On))
+ {
+ if (pvBuf)
+ FTVectorToPOINTFX(&outline->points[first_pt], &ppc->apfx[cpfx]);
+ cpfx++;
+ }
+ else if (point <= outline->contours[contour] &&
+ outline->tags[point] & FT_Curve_Tag_On)
+ {
+ /* add closing pt for bezier */
+ if (pvBuf)
+ FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
+ cpfx++;
+ point++;
+ }
+ if (pvBuf)
+ {
+ ppc->wType = type;
+ ppc->cpfx = cpfx;
+ }
+ needed += sizeof(*ppc) + (cpfx - 1) * sizeof(POINTFX);
+ }
+ if (pvBuf) pph->cb = needed - pph_start;
+ }
+ IntUnLockFreeType;
+ break;
+ }
+ case GGO_BEZIER:
+ {
+ /* Convert the quadratic Beziers to cubic Beziers.
+ The parametric eqn for a cubic Bezier is, from PLRM:
+ r(t) = at^3 + bt^2 + ct + r0
+ with the control points:
+ r1 = r0 + c/3
+ r2 = r1 + (c + b)/3
+ r3 = r0 + c + b + a
+
+ A quadratic Beizer has the form:
+ p(t) = (1-t)^2 p0 + 2(1-t)t p1 + t^2 p2
+
+ So equating powers of t leads to:
+ r1 = 2/3 p1 + 1/3 p0
+ r2 = 2/3 p1 + 1/3 p2
+ and of course r0 = p0, r3 = p2
+ */
+
+ int contour, point = 0, first_pt;
+ FT_Outline *outline = &ft_face->glyph->outline;
+ TTPOLYGONHEADER *pph;
+ TTPOLYCURVE *ppc;
+ DWORD pph_start, cpfx, type;
+ FT_Vector cubic_control[4];
+ if (cjBuf == 0) pvBuf = NULL;
+
+ if (needsTransform && pvBuf)
+ {
+ IntLockFreeType;
+ FT_Outline_Transform(outline, &transMat);
+ IntUnLockFreeType;
+ }
+
+ for (contour = 0; contour < outline->n_contours; contour++)
+ {
+ pph_start = needed;
+ pph = (TTPOLYGONHEADER *)((char *)pvBuf + needed);
+ first_pt = point;
+ if (pvBuf)
+ {
+ pph->dwType = TT_POLYGON_TYPE;
+ FTVectorToPOINTFX(&outline->points[point], &pph->pfxStart);
+ }
+ needed += sizeof(*pph);
+ point++;
+ while (point <= outline->contours[contour])
+ {
+ ppc = (TTPOLYCURVE *)((char *)pvBuf + needed);
+ type = (outline->tags[point] & FT_Curve_Tag_On) ?
+ TT_PRIM_LINE : TT_PRIM_CSPLINE;
+ cpfx = 0;
+ do
+ {
+ if (type == TT_PRIM_LINE)
+ {
+ if (pvBuf)
+ FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
+ cpfx++;
+ point++;
+ }
+ else
+ {
+ /* Unlike QSPLINEs, CSPLINEs always have their endpoint
+ so cpfx = 3n */
+
+ /* FIXME: Possible optimization in endpoint calculation
+ if there are two consecutive curves */
+ cubic_control[0] = outline->points[point-1];
+ if (!(outline->tags[point-1] & FT_Curve_Tag_On))
+ {
+ cubic_control[0].x += outline->points[point].x + 1;
+ cubic_control[0].y += outline->points[point].y + 1;
+ cubic_control[0].x >>= 1;
+ cubic_control[0].y >>= 1;
+ }
+ if (point+1 > outline->contours[contour])
+ cubic_control[3] = outline->points[first_pt];
+ else
+ {
+ cubic_control[3] = outline->points[point+1];
+ if (!(outline->tags[point+1] & FT_Curve_Tag_On))
+ {
+ cubic_control[3].x += outline->points[point].x + 1;
+ cubic_control[3].y += outline->points[point].y + 1;
+ cubic_control[3].x >>= 1;
+ cubic_control[3].y >>= 1;
+ }
+ }
+ /* r1 = 1/3 p0 + 2/3 p1
+ r2 = 1/3 p2 + 2/3 p1 */
+ cubic_control[1].x = (2 * outline->points[point].x + 1) / 3;
+ cubic_control[1].y = (2 * outline->points[point].y + 1) / 3;
+ cubic_control[2] = cubic_control[1];
+ cubic_control[1].x += (cubic_control[0].x + 1) / 3;
+ cubic_control[1].y += (cubic_control[0].y + 1) / 3;
+ cubic_control[2].x += (cubic_control[3].x + 1) / 3;
+ cubic_control[2].y += (cubic_control[3].y + 1) / 3;
+ if (pvBuf)
+ {
+ FTVectorToPOINTFX(&cubic_control[1], &ppc->apfx[cpfx]);
+ FTVectorToPOINTFX(&cubic_control[2], &ppc->apfx[cpfx+1]);
+ FTVectorToPOINTFX(&cubic_control[3], &ppc->apfx[cpfx+2]);
+ }
+ cpfx += 3;
+ point++;
+ }
+ }
+ while (point <= outline->contours[contour] &&
+ (outline->tags[point] & FT_Curve_Tag_On) ==
+ (outline->tags[point-1] & FT_Curve_Tag_On));
+ /* At the end of a contour Windows adds the start point,
+ but only for Beziers and we've already done that.
+ */
+ if (point <= outline->contours[contour] &&
+ outline->tags[point] & FT_Curve_Tag_On)
+ {
+ /* This is the closing pt of a bezier, but we've already
+ added it, so just inc point and carry on */
+ point++;
+ }
+ if (pvBuf)
+ {
+ ppc->wType = type;
+ ppc->cpfx = cpfx;
+ }
+ needed += sizeof(*ppc) + (cpfx - 1) * sizeof(POINTFX);
+ }
+ if (pvBuf) pph->cb = needed - pph_start;
+ }
+ break;
+ }
+
+ default:
+ DPRINT1("Unsupported format %d\n", iFormat);
+ return GDI_ERROR;
+ }
+
+ DPRINT("ftGdiGetGlyphOutline END and needed %d\n", needed);
+ return needed;
+}
+
+BOOL
+FASTCALL
+TextIntGetTextExtentPoint(PDC dc,
+ PTEXTOBJ TextObj,
+ LPCWSTR String,
+ INT Count,
+ ULONG MaxExtent,
+ LPINT Fit,
+ LPINT Dx,
+ LPSIZE Size,
+ FLONG fl)
+{
+ PFONTGDI FontGDI;
+ FT_Face face;
+ FT_GlyphSlot glyph;
+ FT_BitmapGlyph realglyph;
+ INT error, n, glyph_index, i, previous;
+ ULONGLONG TotalWidth = 0;
+ FT_CharMap charmap, found = NULL;
+ BOOL use_kerning;
+ FT_Render_Mode RenderMode;
+ BOOLEAN Render;
+
+ FontGDI = ObjToGDI(TextObj->Font, FONT);
+
+ face = FontGDI->face;
+ if (NULL != Fit)
+ {
+ *Fit = 0;
+ }
+
+ IntLockFreeType;
+ if (face->charmap == NULL)
+ {
+ DPRINT("WARNING: No charmap selected!\n");
+ DPRINT("This font face has %d charmaps\n", face->num_charmaps);
+
+ for (n = 0; n < face->num_charmaps; n++)
+ {
+ charmap = face->charmaps[n];
+ DPRINT("found charmap encoding: %u\n", charmap->encoding);
+ if (charmap->encoding != 0)
+ {
+ found = charmap;
+ break;
+ }
+ }
+
+ if (! found)
+ {
+ DPRINT1("WARNING: Could not find desired charmap!\n");
+ }
+
+ error = FT_Set_Charmap(face, found);
+ if (error)
+ {
+ DPRINT1("WARNING: Could not set the charmap!\n");
+ }
+ }
+
+ Render = IntIsFontRenderingEnabled();
+ if (Render)
+ RenderMode = IntGetFontRenderMode(&TextObj->logfont.elfEnumLogfontEx.elfLogFont);
+ else
+ RenderMode = FT_RENDER_MODE_MONO;
+
+ error = FT_Set_Pixel_Sizes(face,
+ TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfWidth,
+ /* FIXME should set character height if neg */
+ (TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight < 0 ?
+ - TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight :
+ TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight == 0 ? 11 : TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight));
+ if (error)
+ {
+ DPRINT1("Error in setting pixel sizes: %u\n", error);
+ }
+
+ use_kerning = FT_HAS_KERNING(face);
+ previous = 0;
+
+ for (i = 0; i < Count; i++)
+ {
+ if (fl & GTEF_INDICES)
+ glyph_index = *String;
+ else
+ glyph_index = FT_Get_Char_Index(face, *String);
+
+ if (!(realglyph = ftGdiGlyphCacheGet(face, glyph_index,
+ TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight)))
+ {
+ error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
+ if (error)
+ {
+ DPRINT1("WARNING: Failed to load and render glyph! [index: %u]\n", glyph_index);
+ break;
+ }
+
+ glyph = face->glyph;
+ realglyph = ftGdiGlyphCacheSet(face, glyph_index,
+ TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight, glyph, RenderMode);
+ if (!realglyph)
+ {
+ DPRINT1("Failed to render glyph! [index: %u]\n", glyph_index);
+ break;
+ }
+ }
+
+ /* retrieve kerning distance */
+ if (use_kerning && previous && glyph_index)
+ {
+ FT_Vector delta;
+ FT_Get_Kerning(face, previous, glyph_index, 0, &delta);
+ TotalWidth += delta.x;
+ }
+
+ TotalWidth += realglyph->root.advance.x >> 10;
+
+ if (((TotalWidth + 32) >> 6) <= MaxExtent && NULL != Fit)
+ {
+ *Fit = i + 1;
+ }
+ if (NULL != Dx)
+ {
+ Dx[i] = (TotalWidth + 32) >> 6;
+ }
+
+ previous = glyph_index;
+ String++;
+ }
+ IntUnLockFreeType;
+
+ Size->cx = (TotalWidth + 32) >> 6;
+ Size->cy = (TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight < 0 ? - TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight : TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight);
+ Size->cy = EngMulDiv(Size->cy, dc->ppdev->gdiinfo.ulLogPixelsY, 72);
+
+ return TRUE;
+}
+
+
+INT
+FASTCALL
+ftGdiGetTextCharsetInfo(
+ PDC Dc,
+ LPFONTSIGNATURE lpSig,
+ DWORD dwFlags)
+{
+ PDC_ATTR pdcattr;
+ UINT Ret = DEFAULT_CHARSET, i;
+ HFONT hFont;
+ PTEXTOBJ TextObj;
+ PFONTGDI FontGdi;
+ FONTSIGNATURE fs;
+ TT_OS2 *pOS2;
+ FT_Face Face;
+ CHARSETINFO csi;
+ DWORD cp, fs0;
+ USHORT usACP, usOEM;
+
+ pdcattr = Dc->pdcattr;
+ hFont = pdcattr->hlfntNew;
+ TextObj = RealizeFontInit(hFont);
+
+ if (!TextObj)
+ {
+ SetLastWin32Error(ERROR_INVALID_HANDLE);
+ return Ret;
+ }
+ FontGdi = ObjToGDI(TextObj->Font, FONT);
+ Face = FontGdi->face;
+ TEXTOBJ_UnlockText(TextObj);
+
+ IntLockFreeType;
+ pOS2 = FT_Get_Sfnt_Table(Face, ft_sfnt_os2);
+ IntUnLockFreeType;
+ memset(&fs, 0, sizeof(FONTSIGNATURE));
+ if (NULL != pOS2)
+ {
+ fs.fsCsb[0] = pOS2->ulCodePageRange1;
+ fs.fsCsb[1] = pOS2->ulCodePageRange2;
+ fs.fsUsb[0] = pOS2->ulUnicodeRange1;
+ fs.fsUsb[1] = pOS2->ulUnicodeRange2;
+ fs.fsUsb[2] = pOS2->ulUnicodeRange3;
+ fs.fsUsb[3] = pOS2->ulUnicodeRange4;
+ if (pOS2->version == 0)
+ {
+ FT_UInt dummy;
+
+ if (FT_Get_First_Char( Face, &dummy ) < 0x100)
+ fs.fsCsb[0] |= FS_LATIN1;
+ else
+ fs.fsCsb[0] |= FS_SYMBOL;
+ }
+ }
+ DPRINT("Csb 1=%x 0=%x\n", fs.fsCsb[1],fs.fsCsb[0]);
+ if (fs.fsCsb[0] == 0)
+ { /* let's see if we can find any interesting cmaps */
+ for (i = 0; i < Face->num_charmaps; i++)
+ {
+ switch (Face->charmaps[i]->encoding)
+ {
+ case FT_ENCODING_UNICODE:
+ case FT_ENCODING_APPLE_ROMAN:
+ fs.fsCsb[0] |= FS_LATIN1;
+ break;
+ case FT_ENCODING_MS_SYMBOL:
+ fs.fsCsb[0] |= FS_SYMBOL;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ if (lpSig)
+ {
+ RtlCopyMemory(lpSig, &fs, sizeof(FONTSIGNATURE));
+ }
+
+ RtlGetDefaultCodePage(&usACP, &usOEM);
+ cp = usACP;
+
+ if (IntTranslateCharsetInfo(&cp, &csi, TCI_SRCCODEPAGE))
+ if (csi.fs.fsCsb[0] & fs.fsCsb[0])
+ {
+ DPRINT("Hit 1\n");
+ Ret = csi.ciCharset;
+ goto Exit;
+ }
+
+ for (i = 0; i < MAXTCIINDEX; i++)
+ {
+ fs0 = 1L << i;
+ if (fs.fsCsb[0] & fs0)
+ {
+ if (IntTranslateCharsetInfo(&fs0, &csi, TCI_SRCFONTSIG))
+ {
+ //*cp = csi.ciACP;
+ DPRINT("Hit 2\n");
+ Ret = csi.ciCharset;
+ goto Exit;
+ }
+ else
+ DPRINT1("TCI failing on %x\n", fs0);
+ }
+ }
+Exit:
+ DPRINT("CharSet %d CodePage %d\n",csi.ciCharset, csi.ciACP);
+ return (MAKELONG(csi.ciACP, csi.ciCharset));
+}
+
+
+DWORD
+FASTCALL
+ftGetFontUnicodeRanges(PFONTGDI Font, PGLYPHSET glyphset)
+{
+ DWORD size = 0;
+ DWORD num_ranges = 0;
+ FT_Face face = Font->face;
+
+ if (face->charmap->encoding == FT_ENCODING_UNICODE)
+ {
+ FT_UInt glyph_code = 0;
+ FT_ULong char_code, char_code_prev;
+
+ char_code_prev = char_code = FT_Get_First_Char(face, &glyph_code);
+
+ DPRINT("face encoding FT_ENCODING_UNICODE, number of glyphs %ld, first glyph %u, first char %04lx\n",
+ face->num_glyphs, glyph_code, char_code);
+
+ if (!glyph_code) return 0;
+
+ if (glyphset)
+ {
+ glyphset->ranges[0].wcLow = (USHORT)char_code;
+ glyphset->ranges[0].cGlyphs = 0;
+ glyphset->cGlyphsSupported = 0;
+ }
+
+ num_ranges = 1;
+ while (glyph_code)
+ {
+ if (char_code < char_code_prev)
+ {
+ DPRINT1("expected increasing char code from FT_Get_Next_Char\n");
+ return 0;
+ }
+ if (char_code - char_code_prev > 1)
+ {
+ num_ranges++;
+ if (glyphset)
+ {
+ glyphset->ranges[num_ranges - 1].wcLow = (USHORT)char_code;
+ glyphset->ranges[num_ranges - 1].cGlyphs = 1;
+ glyphset->cGlyphsSupported++;
+ }
+ }
+ else if (glyphset)
+ {
+ glyphset->ranges[num_ranges - 1].cGlyphs++;
+ glyphset->cGlyphsSupported++;
+ }
+ char_code_prev = char_code;
+ char_code = FT_Get_Next_Char(face, char_code, &glyph_code);
+ }
+ }
+ else
+ DPRINT1("encoding %u not supported\n", face->charmap->encoding);
+
+ size = sizeof(GLYPHSET) + sizeof(WCRANGE) * (num_ranges - 1);
+ if (glyphset)
+ {
+ glyphset->cbThis = size;
+ glyphset->cRanges = num_ranges;
+ }
+ return size;
+}
+
+
+BOOL
+FASTCALL
+ftGdiGetTextMetricsW(
+ HDC hDC,
+ PTMW_INTERNAL ptmwi)
+{
+ PDC dc;
+ PDC_ATTR pdcattr;
+ PTEXTOBJ TextObj;
+ PFONTGDI FontGDI;
+ FT_Face Face;
+ TT_OS2 *pOS2;
+ TT_HoriHeader *pHori;
+ FT_WinFNT_HeaderRec Win;
+ ULONG Error;
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ if (!ptmwi)
+ {
+ SetLastWin32Error(STATUS_INVALID_PARAMETER);
+ return FALSE;
+ }
+
+ if (!(dc = DC_LockDc(hDC)))
+ {
+ SetLastWin32Error(ERROR_INVALID_HANDLE);
+ return FALSE;
+ }
+ pdcattr = dc->pdcattr;
+ TextObj = RealizeFontInit(pdcattr->hlfntNew);
+ if (NULL != TextObj)
+ {
+ FontGDI = ObjToGDI(TextObj->Font, FONT);
+
+ Face = FontGDI->face;
+ IntLockFreeType;
+ Error = FT_Set_Pixel_Sizes(Face,
+ TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfWidth,
+ /* FIXME should set character height if neg */
+ (TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight < 0 ?
+ - TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight :
+ TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight == 0 ? 11 : TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight));
+ IntUnLockFreeType;
+ if (0 != Error)
+ {
+ DPRINT1("Error in setting pixel sizes: %u\n", Error);
+ Status = STATUS_UNSUCCESSFUL;
+ }
+ else
+ {
+ Status = STATUS_SUCCESS;
+
+ IntLockFreeType;
+ pOS2 = FT_Get_Sfnt_Table(FontGDI->face, ft_sfnt_os2);
+ if (NULL == pOS2)
+ {
+ DPRINT1("Can't find OS/2 table - not TT font?\n");
+ Status = STATUS_INTERNAL_ERROR;
+ }
+
+ pHori = FT_Get_Sfnt_Table(FontGDI->face, ft_sfnt_hhea);
+ if (NULL == pHori)
+ {
+ DPRINT1("Can't find HHEA table - not TT font?\n");
+ Status = STATUS_INTERNAL_ERROR;
+ }
+
+ Error = FT_Get_WinFNT_Header(FontGDI->face , &Win);
+
+ IntUnLockFreeType;
+
+ if (NT_SUCCESS(Status))
+ {
+ if (!(FontGDI->flRealizedType & FDM_TYPE_TEXT_METRIC))
+ {
+ FillTM(&FontGDI->TextMetric, FontGDI, pOS2, pHori, !Error ? &Win : 0);
+ FontGDI->flRealizedType |= FDM_TYPE_TEXT_METRIC;
+ }
+
+ RtlCopyMemory(&ptmwi->TextMetric, &FontGDI->TextMetric, sizeof(TEXTMETRICW));
+ /* FIXME: Fill Diff member */
+ RtlZeroMemory(&ptmwi->Diff, sizeof(ptmwi->Diff));
+ }
+ }
+ TEXTOBJ_UnlockText(TextObj);
+ }
+ else
+ {
+ Status = STATUS_INVALID_HANDLE;
+ }
+ DC_UnlockDc(dc);
+
+ if (!NT_SUCCESS(Status))
+ {
+ SetLastNtError(Status);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+DWORD
+FASTCALL
+ftGdiGetFontData(
+ PFONTGDI FontGdi,
+ DWORD Table,
+ DWORD Offset,
+ PVOID Buffer,
+ DWORD Size)
+{
+ DWORD Result = GDI_ERROR;
+
+ IntLockFreeType;
+
+ if (FT_IS_SFNT(FontGdi->face))
+ {
+ if (Table)
+ Table = Table >> 24 | Table << 24 | (Table >> 8 & 0xFF00) |
+ (Table << 8 & 0xFF0000);
+
+ if (!Buffer) Size = 0;
+
+ if (Buffer && Size)
+ {
+ FT_Error Error;
+ FT_ULong Needed = 0;
+
+ Error = FT_Load_Sfnt_Table(FontGdi->face, Table, Offset, NULL, &Needed);
+
+ if ( !Error && Needed < Size) Size = Needed;
+ }
+ if (!FT_Load_Sfnt_Table(FontGdi->face, Table, Offset, Buffer, &Size))
+ Result = Size;
+ }
+
+ IntUnLockFreeType;
+
+ return Result;
+}
+
+static UINT FASTCALL
+GetFontScore(LOGFONTW *LogFont, PUNICODE_STRING FaceName, PFONTGDI FontGDI)
+{
+ ANSI_STRING EntryFaceNameA;
+ UNICODE_STRING EntryFaceNameW;
+ unsigned Size;
+ OUTLINETEXTMETRICW *Otm;
+ LONG WeightDiff;
+ NTSTATUS Status;
+ UINT Score = 1;
+
+ RtlInitAnsiString(&EntryFaceNameA, FontGDI->face->family_name);
+ Status = RtlAnsiStringToUnicodeString(&EntryFaceNameW, &EntryFaceNameA, TRUE);
+ if (NT_SUCCESS(Status))
+ {
+ if ((LF_FACESIZE - 1) * sizeof(WCHAR) < EntryFaceNameW.Length)
+ {
+ EntryFaceNameW.Length = (LF_FACESIZE - 1) * sizeof(WCHAR);
+ EntryFaceNameW.Buffer[LF_FACESIZE - 1] = L'\0';
+ }
+ if (0 == RtlCompareUnicodeString(FaceName, &EntryFaceNameW, TRUE))
+ {
+ Score += 49;
+ }
+ RtlFreeUnicodeString(&EntryFaceNameW);
+ }
+
+ Size = IntGetOutlineTextMetrics(FontGDI, 0, NULL);
+ Otm = ExAllocatePoolWithTag(PagedPool, Size, TAG_GDITEXT);
+ if (NULL == Otm)
+ {
+ return Score;
+ }
+ IntGetOutlineTextMetrics(FontGDI, Size, Otm);
+
+ if ((0 != LogFont->lfItalic && 0 != Otm->otmTextMetrics.tmItalic) ||
+ (0 == LogFont->lfItalic && 0 == Otm->otmTextMetrics.tmItalic))
+ {
+ Score += 25;
+ }
+ if (LogFont->lfWeight != FW_DONTCARE)
+ {
+ if (LogFont->lfWeight < Otm->otmTextMetrics.tmWeight)
+ {
+ WeightDiff = Otm->otmTextMetrics.tmWeight - LogFont->lfWeight;
+ }
+ else
+ {
+ WeightDiff = LogFont->lfWeight - Otm->otmTextMetrics.tmWeight;
+ }
+ Score += (1000 - WeightDiff) / (1000 / 25);
+ }
+ else
+ {
+ Score += 25;
+ }
+
+ ExFreePool(Otm);
+
+ return Score;
+}
+
+static __inline VOID
+FindBestFontFromList(FONTOBJ **FontObj, UINT *MatchScore, LOGFONTW *LogFont,
+ PUNICODE_STRING FaceName, PLIST_ENTRY Head)
+{
+ PLIST_ENTRY Entry;
+ PFONT_ENTRY CurrentEntry;
+ FONTGDI *FontGDI;
+ UINT Score;
+
+ Entry = Head->Flink;
+ while (Entry != Head)
+ {
+ CurrentEntry = (PFONT_ENTRY) CONTAINING_RECORD(Entry, FONT_ENTRY, ListEntry);
+
+ FontGDI = CurrentEntry->Font;
+ ASSERT(FontGDI);
+
+ Score = GetFontScore(LogFont, FaceName, FontGDI);
+ if (*MatchScore == 0 || *MatchScore < Score)
+ {
+ *FontObj = GDIToObj(FontGDI, FONT);
+ *MatchScore = Score;
+ }
+ Entry = Entry->Flink;
+ }
+}
+
+static __inline BOOLEAN
+SubstituteFontFamilyKey(PUNICODE_STRING FaceName,
+ LPCWSTR Key)
+{
+ RTL_QUERY_REGISTRY_TABLE QueryTable[2] = {{0}};
+ NTSTATUS Status;
+ UNICODE_STRING Value;
+
+ RtlInitUnicodeString(&Value, NULL);
+
+ QueryTable[0].QueryRoutine = NULL;
+ QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_NOEXPAND |
+ RTL_QUERY_REGISTRY_REQUIRED;
+ QueryTable[0].Name = FaceName->Buffer;
+ QueryTable[0].EntryContext = &Value;
+ QueryTable[0].DefaultType = REG_NONE;
+ QueryTable[0].DefaultData = NULL;
+ QueryTable[0].DefaultLength = 0;
+
+ QueryTable[1].QueryRoutine = NULL;
+ QueryTable[1].Name = NULL;
+
+ Status = RtlQueryRegistryValues(RTL_REGISTRY_WINDOWS_NT,
+ Key,
+ QueryTable,
+ NULL,
+ NULL);
+ if (NT_SUCCESS(Status))
+ {
+ RtlFreeUnicodeString(FaceName);
+ *FaceName = Value;
+ }
+
+ return NT_SUCCESS(Status);
+}
+
+static __inline void
+SubstituteFontFamily(PUNICODE_STRING FaceName, UINT Level)
+{
+ if (10 < Level) /* Enough is enough */
+ {
+ return;
+ }
+
+ if (SubstituteFontFamilyKey(FaceName, L"SysFontSubstitutes") ||
+ SubstituteFontFamilyKey(FaceName, L"FontSubstitutes"))
+ {
+ SubstituteFontFamily(FaceName, Level + 1);
+ }
+}
+
+static
+VOID
+FASTCALL
+IntFontType(PFONTGDI Font)
+{
+ PS_FontInfoRec psfInfo;
+ FT_ULong tmp_size = 0;
+
+ if (FT_HAS_MULTIPLE_MASTERS(Font->face))
+ Font->FontObj.flFontType |= FO_MULTIPLEMASTER;
+ if (FT_HAS_VERTICAL( Font->face ))
+ Font->FontObj.flFontType |= FO_VERT_FACE;
+ if (FT_IS_SCALABLE( Font->face ))
+ Font->FontObj.flFontType |= FO_TYPE_RASTER;
+ if (FT_IS_SFNT(Font->face))
+ {
+ Font->FontObj.flFontType |= FO_TYPE_TRUETYPE;
+ if (FT_Get_Sfnt_Table(Font->face, ft_sfnt_post))
+ Font->FontObj.flFontType |= FO_POSTSCRIPT;
+ }
+ if (!FT_Get_PS_Font_Info(Font->face, &psfInfo ))
+ {
+ Font->FontObj.flFontType |= FO_POSTSCRIPT;
+ }
+ /* check for the presence of the 'CFF ' table to check if the font is Type1 */
+ if (!FT_Load_Sfnt_Table(Font->face, FT_MAKE_TAG('C','F','F',' '), 0, NULL, &tmp_size))
+ {
+ Font->FontObj.flFontType |= (FO_CFF|FO_POSTSCRIPT);
+ }
+}
+
+NTSTATUS
+FASTCALL
+TextIntRealizeFont(HFONT FontHandle, PTEXTOBJ pTextObj)
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PTEXTOBJ TextObj;
+ UNICODE_STRING FaceName;
+ PPROCESSINFO Win32Process;
+ UINT MatchScore;
+
+ if (!pTextObj)
+ {
+ TextObj = TEXTOBJ_LockText(FontHandle);
+ if (NULL == TextObj)
+ {
+ return STATUS_INVALID_HANDLE;
+ }
+
+ if (TextObj->fl & TEXTOBJECT_INIT)
+ {
+ TEXTOBJ_UnlockText(TextObj);
+ return STATUS_SUCCESS;
+ }
+ }
+ else
+ TextObj = pTextObj;
+
+ if (! RtlCreateUnicodeString(&FaceName, TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfFaceName))
+ {
+ if (!pTextObj) TEXTOBJ_UnlockText(TextObj);
+ return STATUS_NO_MEMORY;
+ }
+ SubstituteFontFamily(&FaceName, 0);
+ MatchScore = 0;
+ TextObj->Font = NULL;
+
+ /* First search private fonts */
+ Win32Process = PsGetCurrentProcessWin32Process();
+ IntLockProcessPrivateFonts(Win32Process);
+ FindBestFontFromList(&TextObj->Font, &MatchScore,
+ &TextObj->logfont.elfEnumLogfontEx.elfLogFont, &FaceName,
+ &Win32Process->PrivateFontListHead);
+ IntUnLockProcessPrivateFonts(Win32Process);
+
+ /* Search system fonts */
+ IntLockGlobalFonts;
+ FindBestFontFromList(&TextObj->Font, &MatchScore,
+ &TextObj->logfont.elfEnumLogfontEx.elfLogFont, &FaceName,
+ &FontListHead);
+ IntUnLockGlobalFonts;
+ if (NULL == TextObj->Font)
+ {
+ DPRINT1("Requested font %S not found, no fonts loaded at all\n",
+ TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfFaceName);
+ Status = STATUS_NOT_FOUND;
+ }
+ else
+ {
+ PFONTGDI FontGdi = ObjToGDI(TextObj->Font, FONT);
+ // Need hdev, when freetype is loaded need to create DEVOBJ for
+ // Consumer and Producer.
+ TextObj->Font->iUniq = 1; // Now it can be cached.
+ IntFontType(FontGdi);
+ FontGdi->flType = TextObj->Font->flFontType;
+ FontGdi->flRealizedType = 0;
+ FontGdi->Underline = TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfUnderline ? 0xff : 0;
+ FontGdi->StrikeOut = TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfStrikeOut ? 0xff : 0;
+ TextObj->fl |= TEXTOBJECT_INIT;
+ Status = STATUS_SUCCESS;
+ }
+
+ RtlFreeUnicodeString(&FaceName);
+ if (!pTextObj) TEXTOBJ_UnlockText(TextObj);
+
+ ASSERT((NT_SUCCESS(Status) ^ (NULL == TextObj->Font)) != 0);
+
+ return Status;
+}
+
+
+static
+BOOL
+FASTCALL
+IntGetFullFileName(
+ POBJECT_NAME_INFORMATION NameInfo,
+ ULONG Size,
+ PUNICODE_STRING FileName)
+{
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ HANDLE hFile;
+ IO_STATUS_BLOCK IoStatusBlock;
+ ULONG Desired;
+
+ InitializeObjectAttributes(&ObjectAttributes,
+ FileName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL);
+
+ Status = ZwOpenFile(
+ &hFile,
+ 0, //FILE_READ_ATTRIBUTES,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ 0);
+
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT("ZwOpenFile() failed (Status = 0x%lx)\n", Status);
+ return FALSE;
+ }
+
+ Status = ZwQueryObject(hFile, ObjectNameInformation, NameInfo, Size, &Desired);
+ ZwClose(hFile);
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT("ZwQueryObject() failed (Status = %lx)\n", Status);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+BOOL
+FASTCALL
+IntGdiGetFontResourceInfo(
+ PUNICODE_STRING FileName,
+ PVOID pBuffer,
+ DWORD *pdwBytes,
+ DWORD dwType)
+{
+ UNICODE_STRING EntryFileName;
+ POBJECT_NAME_INFORMATION NameInfo1, NameInfo2;
+ PLIST_ENTRY ListEntry;
+ PFONT_ENTRY FontEntry;
+ FONTFAMILYINFO Info;
+ ULONG Size;
+ BOOL bFound = FALSE;
+
+ /* Create buffer for full path name */
+ Size = sizeof(OBJECT_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR);
+ NameInfo1 = ExAllocatePoolWithTag(PagedPool, Size, TAG_FINF);
+ if (!NameInfo1)
+ {
+ SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
+ return FALSE;
+ }
+
+ /* Get the full path name */
+ if (!IntGetFullFileName(NameInfo1, Size, FileName))
+ {
+ ExFreePool(NameInfo1);
+ return FALSE;
+ }
+
+ /* Create a buffer for the entries' names */
+ NameInfo2 = ExAllocatePoolWithTag(PagedPool, Size, TAG_FINF);
+ if (!NameInfo2)
+ {
+ ExFreePool(NameInfo1);
+ SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
+ return FALSE;
+ }
+
+ /* Try to find the pathname in the global font list */
+ IntLockGlobalFonts;
+ for (ListEntry = FontListHead.Flink;
+ ListEntry != &FontListHead;
+ ListEntry = ListEntry->Flink)
+ {
+ FontEntry = CONTAINING_RECORD(ListEntry, FONT_ENTRY, ListEntry);
+ if (FontEntry->Font->Filename != NULL)
+ {
+ RtlInitUnicodeString(&EntryFileName , FontEntry->Font->Filename);
+ if (IntGetFullFileName(NameInfo2, Size, &EntryFileName))
+ {
+ if (RtlEqualUnicodeString(&NameInfo1->Name, &NameInfo2->Name, FALSE))
+ {
+ /* found */
+ FontFamilyFillInfo(&Info, FontEntry->FaceName.Buffer, FontEntry->Font);
+ bFound = TRUE;
+ break;
+ }
+ }
+ }
+ }
+ IntUnLockGlobalFonts;
+
+ /* Free the buffers */
+ ExFreePool(NameInfo1);
+ ExFreePool(NameInfo2);
+
+ if (!bFound && dwType != 5)
+ {
+ /* Font could not be found in system table
+ dwType == 5 will still handle this */
+ return FALSE;
+ }
+
+ switch (dwType)
+ {
+ case 0: /* FIXME: returns 1 or 2, don't know what this is atm */
+ *(DWORD*)pBuffer = 1;
+ *pdwBytes = sizeof(DWORD);
+ break;
+
+ case 1: /* Copy the full font name */
+ Size = wcslen(Info.EnumLogFontEx.elfFullName) + 1;
+ Size = min(Size , LF_FULLFACESIZE) * sizeof(WCHAR);
+ RtlCopyMemory(pBuffer, Info.EnumLogFontEx.elfFullName, Size);
+ // FIXME: Do we have to zeroterminate?
+ *pdwBytes = Size;
+ break;
+
+ case 2: /* Copy a LOGFONTW structure */
+ Info.EnumLogFontEx.elfLogFont.lfWidth = 0;
+ RtlCopyMemory(pBuffer, &Info.EnumLogFontEx.elfLogFont, sizeof(LOGFONTW));
+ *pdwBytes = sizeof(LOGFONTW);
+ break;
+
+ case 3: /* FIXME: What exactly is copied here? */
+ *(DWORD*)pBuffer = 1;
+ *pdwBytes = sizeof(DWORD*);
+ break;
+
+ case 5: /* Looks like a BOOL that is copied, TRUE, if the font was not found */
+ *(BOOL*)pBuffer = !bFound;
+ *pdwBytes = sizeof(BOOL);
+ break;
+
+ default:
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+BOOL
+FASTCALL
+ftGdiRealizationInfo(PFONTGDI Font, PREALIZATION_INFO Info)
+{
+ if (FT_HAS_FIXED_SIZES(Font->face))
+ Info->iTechnology = RI_TECH_BITMAP;
+ else
+ {
+ if (FT_IS_SCALABLE(Font->face))
+ Info->iTechnology = RI_TECH_SCALABLE;
+ else
+ Info->iTechnology = RI_TECH_FIXED;
+ }
+ Info->iUniq = Font->FontObj.iUniq;
+ Info->dwUnknown = -1;
+ return TRUE;
+}
+
+
+DWORD
+FASTCALL
+ftGdiGetKerningPairs( PFONTGDI Font,
+ DWORD cPairs,
+ LPKERNINGPAIR pKerningPair)
+{
+ DWORD Count = 0;
+ INT i = 0;
+ FT_Face face = Font->face;
+
+ if (FT_HAS_KERNING(face) && face->charmap->encoding == FT_ENCODING_UNICODE)
+ {
+ FT_UInt previous_index = 0, glyph_index = 0;
+ FT_ULong char_code, char_previous;
+ FT_Vector delta;
+
+ char_previous = char_code = FT_Get_First_Char(face, &glyph_index);
+
+ IntLockFreeType;
+
+ while (glyph_index)
+ {
+ if (previous_index && glyph_index)
+ {
+ FT_Get_Kerning(face, previous_index, glyph_index, FT_KERNING_DEFAULT, &delta);
+
+ if (pKerningPair && cPairs)
+ {
+ pKerningPair[i].wFirst = char_previous;
+ pKerningPair[i].wSecond = char_code;
+ pKerningPair[i].iKernAmount = delta.x;
+ i++;
+ if (i == cPairs) break;
+ }
+ Count++;
+ }
+ previous_index = glyph_index;
+ char_previous = char_code;
+ char_code = FT_Get_Next_Char(face, char_code, &glyph_index);
+ }
+ IntUnLockFreeType;
+ }
+ return Count;
+}
+
+
+//////////////////
+//
+// Functions needing sorting.
+//
+///////////////
+int APIENTRY
+NtGdiGetFontFamilyInfo(HDC Dc,
+ LPLOGFONTW UnsafeLogFont,
+ PFONTFAMILYINFO UnsafeInfo,
+ DWORD Size)
+{
+ NTSTATUS Status;
+ LOGFONTW LogFont;
+ PFONTFAMILYINFO Info;
+ DWORD Count;
+ PPROCESSINFO Win32Process;
+
+ /* Make a safe copy */
+ Status = MmCopyFromCaller(&LogFont, UnsafeLogFont, sizeof(LOGFONTW));
+ if (! NT_SUCCESS(Status))
+ {
+ SetLastWin32Error(ERROR_INVALID_PARAMETER);
+ return -1;
+ }
+
+ /* Allocate space for a safe copy */
+ Info = ExAllocatePoolWithTag(PagedPool, Size * sizeof(FONTFAMILYINFO), TAG_GDITEXT);
+ if (NULL == Info)
+ {
+ SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
+ return -1;
+ }
+
+ /* Enumerate font families in the global list */
+ IntLockGlobalFonts;
+ Count = 0;
+ if (! GetFontFamilyInfoForList(&LogFont, Info, &Count, Size, &FontListHead) )
+ {
+ IntUnLockGlobalFonts;
+ ExFreePool(Info);
+ return -1;
+ }
+ IntUnLockGlobalFonts;
+
+ /* Enumerate font families in the process local list */
+ Win32Process = PsGetCurrentProcessWin32Process();
+ IntLockProcessPrivateFonts(Win32Process);
+ if (! GetFontFamilyInfoForList(&LogFont, Info, &Count, Size,
+ &Win32Process->PrivateFontListHead))
+ {
+ IntUnLockProcessPrivateFonts(Win32Process);
+ ExFreePool(Info);
+ return -1;
+ }
+ IntUnLockProcessPrivateFonts(Win32Process);
+
+ /* Enumerate font families in the registry */
+ if (! GetFontFamilyInfoForSubstitutes(&LogFont, Info, &Count, Size))
+ {
+ ExFreePool(Info);
+ return -1;
+ }
+
+ /* Return data to caller */
+ if (0 != Count)
+ {
+ Status = MmCopyToCaller(UnsafeInfo, Info,
+ (Count < Size ? Count : Size) * sizeof(FONTFAMILYINFO));
+ if (! NT_SUCCESS(Status))
+ {
+ ExFreePool(Info);
+ SetLastWin32Error(ERROR_INVALID_PARAMETER);
+ return -1;
+ }
+ }
+
+ ExFreePool(Info);
+
+ return Count;
+}
+
+BOOL
+APIENTRY
+GreExtTextOutW(
+ IN HDC hDC,
+ IN INT XStart,
+ IN INT YStart,
+ IN UINT fuOptions,
+ IN OPTIONAL PRECTL lprc,
+ IN LPWSTR String,
+ IN INT Count,
+ IN OPTIONAL LPINT Dx,
+ IN DWORD dwCodePage)
+{
+ /*
+ * FIXME:
+ * Call EngTextOut, which does the real work (calling DrvTextOut where
+ * appropriate)
+ */
+
+ DC *dc;
+ PDC_ATTR pdcattr;
+ SURFOBJ *SurfObj;
+ SURFACE *psurf = NULL;
+ int error, glyph_index, n, i;
+ FT_Face face;
+ FT_GlyphSlot glyph;
+ FT_BitmapGlyph realglyph;
+ LONGLONG TextLeft, RealXStart;
+ ULONG TextTop, previous, BackgroundLeft;
+ FT_Bool use_kerning;
+ RECTL DestRect, MaskRect, DummyRect = {0, 0, 0, 0};
+ POINTL SourcePoint, BrushOrigin;
+ HBITMAP HSourceGlyph;
+ SURFOBJ *SourceGlyphSurf;
+ SIZEL bitSize;
+ FT_CharMap found = 0, charmap;
+ INT yoff;
+ FONTOBJ *FontObj;
+ PFONTGDI FontGDI;
+ PTEXTOBJ TextObj = NULL;
+ EXLATEOBJ exloRGB2Dst, exloDst2RGB;
+ FT_Render_Mode RenderMode;
+ BOOLEAN Render;
+ POINT Start;
+ BOOL DoBreak = FALSE;
+ USHORT DxShift;
+
+ // TODO: Write test-cases to exactly match real Windows in different
+ // bad parameters (e.g. does Windows check the DC or the RECT first?).
+ dc = DC_LockDc(hDC);
+ if (!dc)
+ {
+ SetLastWin32Error(ERROR_INVALID_HANDLE);
+ return FALSE;
+ }
+ if (dc->dctype == DC_TYPE_INFO)
+ {
+ DC_UnlockDc(dc);
+ /* Yes, Windows really returns TRUE in this case */
+ return TRUE;
+ }
+
+ pdcattr = dc->pdcattr;
+
+ if ((fuOptions & ETO_OPAQUE) || pdcattr->jBkMode == OPAQUE)
+ {
+ if (pdcattr->ulDirty_ & DIRTY_BACKGROUND)
+ DC_vUpdateBackgroundBrush(dc);
+ }
+
+ /* Check if String is valid */
+ if ((Count > 0xFFFF) || (Count > 0 && String == NULL))
+ {
+ SetLastWin32Error(ERROR_INVALID_PARAMETER);
+ goto fail;
+ }
+
+ DxShift = fuOptions & ETO_PDY ? 1 : 0;
+
+ if (PATH_IsPathOpen(dc->dclevel))
+ {
+ if (!PATH_ExtTextOut( dc,
+ XStart,
+ YStart,
+ fuOptions,
+ (const RECTL *)lprc,
+ String,
+ Count,
+ (const INT *)Dx)) goto fail;
+ goto good;
+ }
+
+ if (lprc && (fuOptions & (ETO_OPAQUE | ETO_CLIPPED)))
+ {
+ IntLPtoDP(dc, (POINT *)lprc, 2);
+ }
+
+ Start.x = XStart;
+ Start.y = YStart;
+ IntLPtoDP(dc, &Start, 1);
+
+ RealXStart = (Start.x + dc->ptlDCOrig.x) << 6;
+ YStart = Start.y + dc->ptlDCOrig.y;
+
+ SourcePoint.x = 0;
+ SourcePoint.y = 0;
+ MaskRect.left = 0;
+ MaskRect.top = 0;
+ BrushOrigin.x = 0;
+ BrushOrigin.y = 0;
+
+ if ((fuOptions & ETO_OPAQUE) && lprc)
+ {
+ DestRect.left = lprc->left;
+ DestRect.top = lprc->top;
+ DestRect.right = lprc->right;
+ DestRect.bottom = lprc->bottom;
+
+ IntLPtoDP(dc, (LPPOINT)&DestRect, 2);
+
+ DestRect.left += dc->ptlDCOrig.x;
+ DestRect.top += dc->ptlDCOrig.y;
+ DestRect.right += dc->ptlDCOrig.x;
+ DestRect.bottom += dc->ptlDCOrig.y;
+
+ DC_vPrepareDCsForBlit(dc, DestRect, NULL, DestRect);
+
+ if (pdcattr->ulDirty_ & DIRTY_BACKGROUND)
+ DC_vUpdateBackgroundBrush(dc);
+
+ IntEngBitBlt(
+ &dc->dclevel.pSurface->SurfObj,
+ NULL,
+ NULL,
+ dc->rosdc.CombinedClip,
+ NULL,
+ &DestRect,
+ &SourcePoint,
+ &SourcePoint,
+ &dc->eboBackground.BrushObject,
+ &BrushOrigin,
+ ROP3_TO_ROP4(PATCOPY));
+ fuOptions &= ~ETO_OPAQUE;
+ DC_vFinishBlit(dc, NULL);
+ }
+ else
+ {
+ if (pdcattr->jBkMode == OPAQUE)
+ {
+ fuOptions |= ETO_OPAQUE;
+ }
+ }
+
+ TextObj = RealizeFontInit(pdcattr->hlfntNew);
+ if (TextObj == NULL)
+ {
+ goto fail;
+ }
+
+ FontObj = TextObj->Font;
+ ASSERT(FontObj);
+ FontGDI = ObjToGDI(FontObj, FONT);
+ ASSERT(FontGDI);
+
+ IntLockFreeType;
+ face = FontGDI->face;
+ if (face->charmap == NULL)
+ {
+ DPRINT("WARNING: No charmap selected!\n");
+ DPRINT("This font face has %d charmaps\n", face->num_charmaps);
+
+ for (n = 0; n < face->num_charmaps; n++)
+ {
+ charmap = face->charmaps[n];
+ DPRINT("found charmap encoding: %u\n", charmap->encoding);
+ if (charmap->encoding != 0)
+ {
+ found = charmap;
+ break;
+ }
+ }
+ if (!found)
+ {
+ DPRINT1("WARNING: Could not find desired charmap!\n");
+ }
+ error = FT_Set_Charmap(face, found);
+ if (error)
+ {
+ DPRINT1("WARNING: Could not set the charmap!\n");
+ }
+ }
+
+ Render = IntIsFontRenderingEnabled();
+ if (Render)
+ RenderMode = IntGetFontRenderMode(&TextObj->logfont.elfEnumLogfontEx.elfLogFont);
+ else
+ RenderMode = FT_RENDER_MODE_MONO;
+
+ error = FT_Set_Pixel_Sizes(
+ face,
+ TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfWidth,
+ /* FIXME should set character height if neg */
+ (TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight < 0 ?
+ - TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight :
+ TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight == 0 ? 11 : TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight));
+ if (error)
+ {
+ DPRINT1("Error in setting pixel sizes: %u\n", error);
+ IntUnLockFreeType;
+ goto fail;
+ }
+
+ /*
+ * Process the vertical alignment and determine the yoff.
+ */
+
+ if (pdcattr->lTextAlign & TA_BASELINE)
+ yoff = 0;
+ else if (pdcattr->lTextAlign & TA_BOTTOM)
+ yoff = -face->size->metrics.descender >> 6;
+ else /* TA_TOP */
+ yoff = face->size->metrics.ascender >> 6;
+
+ use_kerning = FT_HAS_KERNING(face);
+ previous = 0;
+
+ /*
+ * Process the horizontal alignment and modify XStart accordingly.
+ */
+
+ if (pdcattr->lTextAlign & (TA_RIGHT | TA_CENTER))
+ {
+ ULONGLONG TextWidth = 0;
+ LPCWSTR TempText = String;
+ int Start;
+
+ /*
+ * Calculate width of the text.
+ */
+
+ if (NULL != Dx)
+ {
+ Start = Count < 2 ? 0 : Count - 2;
+ TextWidth = Count < 2 ? 0 : (Dx[(Count-2)<<DxShift] << 6);
+ }
+ else
+ {
+ Start = 0;
+ }
+ TempText = String + Start;
+
+ for (i = Start; i < Count; i++)
+ {
+ if (fuOptions & ETO_GLYPH_INDEX)
+ glyph_index = *TempText;
+ else
+ glyph_index = FT_Get_Char_Index(face, *TempText);
+
+ if (!(realglyph = ftGdiGlyphCacheGet(face, glyph_index,
+ TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight)))
+ {
+ error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
+ if (error)
+ {
+ DPRINT1("WARNING: Failed to load and render glyph! [index: %u]\n", glyph_index);
+ }
+
+ glyph = face->glyph;
+ realglyph = ftGdiGlyphCacheSet(face, glyph_index,
+ TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight, glyph, RenderMode);
+ if (!realglyph)
+ {
+ DPRINT1("Failed to render glyph! [index: %u]\n", glyph_index);
+ IntUnLockFreeType;
+ goto fail;
+ }
+
+ }
+ /* retrieve kerning distance */
+ if (use_kerning && previous && glyph_index)
+ {
+ FT_Vector delta;
+ FT_Get_Kerning(face, previous, glyph_index, 0, &delta);
+ TextWidth += delta.x;
+ }
+
+ TextWidth += realglyph->root.advance.x >> 10;
+
+ previous = glyph_index;
+ TempText++;
+ }
+
+ previous = 0;
+
+ if (pdcattr->lTextAlign & TA_RIGHT)
+ {
+ RealXStart -= TextWidth;
+ }
+ else
+ {
+ RealXStart -= TextWidth / 2;
+ }
+ }
+
+ TextLeft = RealXStart;
+ TextTop = YStart;
+ BackgroundLeft = (RealXStart + 32) >> 6;
+
+ /* Lock blit with a dummy rect */
+ DC_vPrepareDCsForBlit(dc, DummyRect, NULL, DummyRect);
+
+ psurf = dc->dclevel.pSurface ;
+ SurfObj = &psurf->SurfObj ;
+
+ EXLATEOBJ_vInitialize(&exloRGB2Dst, &gpalRGB, psurf->ppal, 0, 0, 0);
+ EXLATEOBJ_vInitialize(&exloDst2RGB, psurf->ppal, &gpalRGB, 0, 0, 0);
+
+ if ((fuOptions & ETO_OPAQUE) && (dc->pdcattr->ulDirty_ & DIRTY_BACKGROUND))
+ DC_vUpdateBackgroundBrush(dc) ;
+
+ if(dc->pdcattr->ulDirty_ & DIRTY_TEXT)
+ DC_vUpdateTextBrush(dc) ;
+
+ /*
+ * The main rendering loop.
+ */
+ for (i = 0; i < Count; i++)
+ {
+ if (fuOptions & ETO_GLYPH_INDEX)
+ glyph_index = *String;
+ else
+ glyph_index = FT_Get_Char_Index(face, *String);
+
+ if (!(realglyph = ftGdiGlyphCacheGet(face, glyph_index,
+ TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight)))
+ {
+ error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
+ if (error)
+ {
+ DPRINT1("Failed to load and render glyph! [index: %u]\n", glyph_index);
+ IntUnLockFreeType;
+ goto fail2;
+ }
+ glyph = face->glyph;
+ realglyph = ftGdiGlyphCacheSet(face,
+ glyph_index,
+ TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight,
+ glyph,
+ RenderMode);
+ if (!realglyph)
+ {
+ DPRINT1("Failed to render glyph! [index: %u]\n", glyph_index);
+ IntUnLockFreeType;
+ goto fail2;
+ }
+ }
+
+ /* retrieve kerning distance and move pen position */
+ if (use_kerning && previous && glyph_index && NULL == Dx)
+ {
+ FT_Vector delta;
+ FT_Get_Kerning(face, previous, glyph_index, 0, &delta);
+ TextLeft += delta.x;
+ }
+ DPRINT("TextLeft: %d\n", TextLeft);
+ DPRINT("TextTop: %d\n", TextTop);
+ DPRINT("Advance: %d\n", realglyph->root.advance.x);
+
+ if (fuOptions & ETO_OPAQUE)
+ {
+ DestRect.left = BackgroundLeft;
+ DestRect.right = (TextLeft + (realglyph->root.advance.x >> 10) + 32) >> 6;
+ DestRect.top = TextTop + yoff - ((face->size->metrics.ascender + 32) >> 6);
+ DestRect.bottom = TextTop + yoff + ((32 - face->size->metrics.descender) >> 6);
+ MouseSafetyOnDrawStart(dc->ppdev, DestRect.left, DestRect.top, DestRect.right, DestRect.bottom);
+ IntEngBitBlt(
+ &psurf->SurfObj,
+ NULL,
+ NULL,
+ dc->rosdc.CombinedClip,
+ NULL,
+ &DestRect,
+ &SourcePoint,
+ &SourcePoint,
+ &dc->eboBackground.BrushObject,
+ &BrushOrigin,
+ ROP3_TO_ROP4(PATCOPY));
+ MouseSafetyOnDrawEnd(dc->ppdev);
+ BackgroundLeft = DestRect.right;
+
+ }
+
+ DestRect.left = ((TextLeft + 32) >> 6) + realglyph->left;
+ DestRect.right = DestRect.left + realglyph->bitmap.width;
+ DestRect.top = TextTop + yoff - realglyph->top;
+ DestRect.bottom = DestRect.top + realglyph->bitmap.rows;
+
+ bitSize.cx = realglyph->bitmap.width;
+ bitSize.cy = realglyph->bitmap.rows;
+ MaskRect.right = realglyph->bitmap.width;
+ MaskRect.bottom = realglyph->bitmap.rows;
+
+ /*
+ * We should create the bitmap out of the loop at the biggest possible
+ * glyph size. Then use memset with 0 to clear it and sourcerect to
+ * limit the work of the transbitblt.
+ */
+
+ HSourceGlyph = EngCreateBitmap(bitSize, realglyph->bitmap.pitch,
+ BMF_8BPP, BMF_TOPDOWN,
+ realglyph->bitmap.buffer);
+ if ( !HSourceGlyph )
+ {
+ DPRINT1("WARNING: EngLockSurface() failed!\n");
+ // FT_Done_Glyph(realglyph);
+ IntUnLockFreeType;
+ goto fail2;
+ }
+ SourceGlyphSurf = EngLockSurface((HSURF)HSourceGlyph);
+ if ( !SourceGlyphSurf )
+ {
+ EngDeleteSurface((HSURF)HSourceGlyph);
+ DPRINT1("WARNING: EngLockSurface() failed!\n");
+ IntUnLockFreeType;
+ goto fail2;
+ }
+
+ /*
+ * Use the font data as a mask to paint onto the DCs surface using a
+ * brush.
+ */
+
+ if (lprc &&
+ (fuOptions & ETO_CLIPPED) &&
+ DestRect.right >= lprc->right + dc->ptlDCOrig.x)
+ {
+ // We do the check '>=' instead of '>' to possibly save an iteration
+ // through this loop, since it's breaking after the drawing is done,
+ // and x is always incremented.
+ DestRect.right = lprc->right + dc->ptlDCOrig.x;
+ DoBreak = TRUE;
+ }
+ MouseSafetyOnDrawStart(dc->ppdev, DestRect.left, DestRect.top, DestRect.right, DestRect.bottom);
+ IntEngMaskBlt(
+ SurfObj,
+ SourceGlyphSurf,
+ dc->rosdc.CombinedClip,
+ &exloRGB2Dst.xlo,
+ &exloDst2RGB.xlo,
+ &DestRect,
+ (PPOINTL)&MaskRect,
+ &dc->eboText.BrushObject,
+ &BrushOrigin);
+ MouseSafetyOnDrawEnd(dc->ppdev) ;
+
+ EngUnlockSurface(SourceGlyphSurf);
+ EngDeleteSurface((HSURF)HSourceGlyph);
+
+ if (DoBreak)
+ {
+ break;
+ }
+
+ if (NULL == Dx)
+ {
+ TextLeft += realglyph->root.advance.x >> 10;
+ DPRINT("new TextLeft: %d\n", TextLeft);
+ }
+ else
+ {
+ TextLeft += Dx[i<<DxShift] << 6;
+ DPRINT("new TextLeft2: %d\n", TextLeft);
+ }
+
+ if (DxShift)
+ {
+ TextTop -= Dx[2 * i + 1] << 6;
+ }
+
+ previous = glyph_index;
+
+ String++;
+ }
+ IntUnLockFreeType;
+
+ DC_vFinishBlit(dc, NULL) ;
+ EXLATEOBJ_vCleanup(&exloRGB2Dst);
+ EXLATEOBJ_vCleanup(&exloDst2RGB);
+ if (TextObj != NULL)
+ TEXTOBJ_UnlockText(TextObj);
+good:
+ DC_UnlockDc( dc );
+
+ return TRUE;
+
+fail2:
+ EXLATEOBJ_vCleanup(&exloRGB2Dst);
+ EXLATEOBJ_vCleanup(&exloDst2RGB);
+fail:
+ if (TextObj != NULL)
+ TEXTOBJ_UnlockText(TextObj);
+
+ DC_UnlockDc(dc);
+
+ return FALSE;
+}
+
+#define STACK_TEXT_BUFFER_SIZE 100
+BOOL
+APIENTRY
+NtGdiExtTextOutW(
+ IN HDC hDC,
+ IN INT XStart,
+ IN INT YStart,
+ IN UINT fuOptions,
+ IN OPTIONAL LPRECT UnsafeRect,
+ IN LPWSTR UnsafeString,
+ IN INT Count,
+ IN OPTIONAL LPINT UnsafeDx,
+ IN DWORD dwCodePage)
+{
+ BOOL Result = FALSE;
+ NTSTATUS Status = STATUS_SUCCESS;
+ RECTL SafeRect;
+ BYTE LocalBuffer[STACK_TEXT_BUFFER_SIZE];
+ PVOID Buffer = LocalBuffer;
+ LPWSTR SafeString = NULL;
+ LPINT SafeDx = NULL;
+ ULONG BufSize, StringSize, DxSize = 0;
+
+ /* Check if String is valid */
+ if ((Count > 0xFFFF) || (Count > 0 && UnsafeString == NULL))
+ {
+ SetLastWin32Error(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+
+ if (Count > 0)
+ {
+ /* Calculate buffer size for string and Dx values */
+ BufSize = StringSize = Count * sizeof(WCHAR);
+ if (UnsafeDx)
+ {
+ /* If ETO_PDY is specified, we have pairs of INTs */
+ DxSize = (Count * sizeof(INT)) * (fuOptions & ETO_PDY ? 2 : 1);
+ BufSize += DxSize;
+ }
+
+ /* Check if our local buffer is large enough */
+ if (BufSize > STACK_TEXT_BUFFER_SIZE)
+ {
+ /* It's not, allocate a temp buffer */
+ Buffer = ExAllocatePoolWithTag(PagedPool, BufSize, TAG_GDITEXT);
+ if (!Buffer)
+ {
+ return FALSE;
+ }
+ }
+
+ /* Probe and copy user mode data to the buffer */
+ _SEH2_TRY
+ {
+ /* Put the Dx before the String to assure alignment of 4 */
+ SafeString = (LPWSTR)(((ULONG_PTR)Buffer) + DxSize);
+
+ /* Probe and copy the string */
+ ProbeForRead(UnsafeString, StringSize, 1);
+ memcpy((PVOID)SafeString, UnsafeString, StringSize);
+
+ /* If we have Dx values... */
+ if (UnsafeDx)
+ {
+ /* ... probe and copy them */
+ SafeDx = Buffer;
+ ProbeForRead(UnsafeDx, DxSize, 1);
+ memcpy(SafeDx, UnsafeDx, DxSize);
+ }
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ Status = _SEH2_GetExceptionCode();
+ }
+ _SEH2_END
+ if (!NT_SUCCESS(Status))
+ {
+ goto cleanup;
+ }
+ }
+
+ /* If we have a rect, copy it */
+ if (UnsafeRect)
+ {
+ _SEH2_TRY
+ {
+ ProbeForRead(UnsafeRect, sizeof(RECT), 1);
+ SafeRect = *UnsafeRect;
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ Status = _SEH2_GetExceptionCode();
+ }
+ _SEH2_END
+ if (!NT_SUCCESS(Status))
+ {
+ goto cleanup;
+ }
+ }
+
+ /* Finally call the internal routine */
+ Result = GreExtTextOutW(hDC,
+ XStart,
+ YStart,
+ fuOptions,
+ &SafeRect,
+ SafeString,
+ Count,
+ SafeDx,
+ dwCodePage);
+
+cleanup:
+ /* If we allocated a buffer, free it */
+ if (Buffer != LocalBuffer)
+ {
+ ExFreePoolWithTag(Buffer, TAG_GDITEXT);
+ }
+
+ return Result;
+}
+
+
+/*
+* @implemented
+*/
+BOOL
+APIENTRY
+NtGdiGetCharABCWidthsW(
+ IN HDC hDC,
+ IN UINT FirstChar,
+ IN ULONG Count,
+ IN OPTIONAL PWCHAR pwch,
+ IN FLONG fl,
+ OUT PVOID Buffer)
+{
+ LPABC SafeBuff;
+ LPABCFLOAT SafeBuffF = NULL;
+ PDC dc;
+ PDC_ATTR pdcattr;
+ PTEXTOBJ TextObj;
+ PFONTGDI FontGDI;
+ FT_Face face;
+ FT_CharMap charmap, found = NULL;
+ UINT i, glyph_index, BufferSize;
+ HFONT hFont = 0;
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ if (pwch)
+ {
+ _SEH2_TRY
+ {
+ ProbeForRead(pwch,
+ sizeof(PWSTR),
+ 1);
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ Status = _SEH2_GetExceptionCode();
+ }
+ _SEH2_END;
+ }
+ if (!NT_SUCCESS(Status))
+ {
+ SetLastWin32Error(Status);
+ return FALSE;
+ }
+
+ if (!Buffer)
+ {
+ SetLastWin32Error(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+
+ BufferSize = Count * sizeof(ABC); // Same size!
+ SafeBuff = ExAllocatePoolWithTag(PagedPool, BufferSize, TAG_GDITEXT);
+ if (!fl) SafeBuffF = (LPABCFLOAT) SafeBuff;
+ if (SafeBuff == NULL)
+ {
+ SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
+ return FALSE;
+ }
+
+ dc = DC_LockDc(hDC);
+ if (dc == NULL)
+ {
+ ExFreePool(SafeBuff);
+ SetLastWin32Error(ERROR_INVALID_HANDLE);
+ return FALSE;
+ }
+ pdcattr = dc->pdcattr;
+ hFont = pdcattr->hlfntNew;
+ TextObj = RealizeFontInit(hFont);
+ DC_UnlockDc(dc);
+
+ if (TextObj == NULL)
+ {
+ ExFreePool(SafeBuff);
+ SetLastWin32Error(ERROR_INVALID_HANDLE);
+ return FALSE;
+ }
+
+ FontGDI = ObjToGDI(TextObj->Font, FONT);
+
+ face = FontGDI->face;
+ if (face->charmap == NULL)
+ {
+ for (i = 0; i < face->num_charmaps; i++)
+ {
+ charmap = face->charmaps[i];
+ if (charmap->encoding != 0)
+ {
+ found = charmap;
+ break;
+ }
+ }
+
+ if (!found)
+ {
+ DPRINT1("WARNING: Could not find desired charmap!\n");
+ ExFreePool(SafeBuff);
+ SetLastWin32Error(ERROR_INVALID_HANDLE);
+ return FALSE;
+ }
+
+ IntLockFreeType;
+ FT_Set_Charmap(face, found);
+ IntUnLockFreeType;
+ }
+
+ IntLockFreeType;
+ FT_Set_Pixel_Sizes(face,
+ TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfWidth,
+ /* FIXME should set character height if neg */
+ (TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight < 0 ? - TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight :
+ TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight == 0 ? 11 : TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight));
+
+ for (i = FirstChar; i < FirstChar+Count; i++)
+ {
+ int adv, lsb, bbx, left, right;
+
+ if (pwch)
+ {
+ if (fl & GCABCW_INDICES)
+ glyph_index = pwch[i - FirstChar];
+ else
+ glyph_index = FT_Get_Char_Index(face, pwch[i - FirstChar]);
+ }
+ else
+ {
+ if (fl & GCABCW_INDICES)
+ glyph_index = i;
+ else
+ glyph_index = FT_Get_Char_Index(face, i);
+ }
+ FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
+
+ left = (INT)face->glyph->metrics.horiBearingX & -64;
+ right = (INT)((face->glyph->metrics.horiBearingX + face->glyph->metrics.width) + 63) & -64;
+ adv = (face->glyph->advance.x + 32) >> 6;
+
+// int test = (INT)(face->glyph->metrics.horiAdvance + 63) >> 6;
+// DPRINT1("Advance Wine %d and Advance Ros %d\n",test, adv ); /* It's the same!*/
+
+ lsb = left >> 6;
+ bbx = (right - left) >> 6;
+ /*
+ DPRINT1("lsb %d and bbx %d\n", lsb, bbx );
+ */
+ if (!fl)
+ {
+ SafeBuffF[i - FirstChar].abcfA = (FLOAT) lsb;
+ SafeBuffF[i - FirstChar].abcfB = (FLOAT) bbx;
+ SafeBuffF[i - FirstChar].abcfC = (FLOAT) (adv - lsb - bbx);
+ }
+ else
+ {
+ SafeBuff[i - FirstChar].abcA = lsb;
+ SafeBuff[i - FirstChar].abcB = bbx;
+ SafeBuff[i - FirstChar].abcC = adv - lsb - bbx;
+ }
+ }
+ IntUnLockFreeType;
+ TEXTOBJ_UnlockText(TextObj);
+ Status = MmCopyToCaller(Buffer, SafeBuff, BufferSize);
+ if (! NT_SUCCESS(Status))
+ {
+ SetLastNtError(Status);
+ ExFreePool(SafeBuff);
+ return FALSE;
+ }
+ ExFreePool(SafeBuff);
+ DPRINT("NtGdiGetCharABCWidths Worked!\n");
+ return TRUE;
+}
+
+/*
+* @implemented
+*/
+BOOL
+APIENTRY
+NtGdiGetCharWidthW(
+ IN HDC hDC,
+ IN UINT FirstChar,
+ IN UINT Count,
+ IN OPTIONAL PWCHAR pwc,
+ IN FLONG fl,
+ OUT PVOID Buffer)
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ LPINT SafeBuff;
+ PFLOAT SafeBuffF = NULL;
+ PDC dc;
+ PDC_ATTR pdcattr;
+ PTEXTOBJ TextObj;
+ PFONTGDI FontGDI;
+ FT_Face face;
+ FT_CharMap charmap, found = NULL;
+ UINT i, glyph_index, BufferSize;
+ HFONT hFont = 0;
+
+ if (pwc)
+ {
+ _SEH2_TRY
+ {
+ ProbeForRead(pwc,
+ sizeof(PWSTR),
+ 1);
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ Status = _SEH2_GetExceptionCode();
+ }
+ _SEH2_END;
+ }
+ if (!NT_SUCCESS(Status))
+ {
+ SetLastWin32Error(Status);
+ return FALSE;
+ }
+
+ BufferSize = Count * sizeof(INT); // Same size!
+ SafeBuff = ExAllocatePoolWithTag(PagedPool, BufferSize, TAG_GDITEXT);
+ if (!fl) SafeBuffF = (PFLOAT) SafeBuff;
+ if (SafeBuff == NULL)
+ {
+ SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
+ return FALSE;
+ }
+
+ dc = DC_LockDc(hDC);
+ if (dc == NULL)
+ {
+ ExFreePool(SafeBuff);
+ SetLastWin32Error(ERROR_INVALID_HANDLE);
+ return FALSE;
+ }
+ pdcattr = dc->pdcattr;
+ hFont = pdcattr->hlfntNew;
+ TextObj = RealizeFontInit(hFont);
+ DC_UnlockDc(dc);
+
+ if (TextObj == NULL)
+ {
+ ExFreePool(SafeBuff);
+ SetLastWin32Error(ERROR_INVALID_HANDLE);
+ return FALSE;
+ }
+
+ FontGDI = ObjToGDI(TextObj->Font, FONT);
+
+ face = FontGDI->face;
+ if (face->charmap == NULL)
+ {
+ for (i = 0; i < face->num_charmaps; i++)
+ {
+ charmap = face->charmaps[i];
+ if (charmap->encoding != 0)
+ {
+ found = charmap;
+ break;
+ }
+ }
+
+ if (!found)
+ {
+ DPRINT1("WARNING: Could not find desired charmap!\n");
+ ExFreePool(SafeBuff);
+ SetLastWin32Error(ERROR_INVALID_HANDLE);
+ return FALSE;
+ }
+
+ IntLockFreeType;
+ FT_Set_Charmap(face, found);
+ IntUnLockFreeType;
+ }
+
+ IntLockFreeType;
+ FT_Set_Pixel_Sizes(face,
+ TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfWidth,
+ /* FIXME should set character height if neg */
+ (TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight < 0 ?
+ - TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight :
+ TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight == 0 ? 11 : TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight));
+
+ for (i = FirstChar; i < FirstChar+Count; i++)
+ {
+ if (pwc)
+ {
+ if (fl & GCW_INDICES)
+ glyph_index = pwc[i - FirstChar];
+ else
+ glyph_index = FT_Get_Char_Index(face, pwc[i - FirstChar]);
+ }
+ else
+ {
+ if (fl & GCW_INDICES)
+ glyph_index = i;
+ else
+ glyph_index = FT_Get_Char_Index(face, i);
+ }
+ FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
+ if (!fl)
+ SafeBuffF[i - FirstChar] = (FLOAT) ((face->glyph->advance.x + 32) >> 6);
+ else
+ SafeBuff[i - FirstChar] = (face->glyph->advance.x + 32) >> 6;
+ }
+ IntUnLockFreeType;
+ TEXTOBJ_UnlockText(TextObj);
+ MmCopyToCaller(Buffer, SafeBuff, BufferSize);
+ ExFreePool(SafeBuff);
+ return TRUE;
+}
+
+DWORD
+FASTCALL
+GreGetGlyphIndicesW(
+ HDC hdc,
+ LPWSTR pwc,
+ INT cwc,
+ LPWORD pgi,
+ DWORD iMode,
+ DWORD Unknown)
+{
+ PDC dc;
+ PDC_ATTR pdcattr;
+ PTEXTOBJ TextObj;
+ PFONTGDI FontGDI;
+ HFONT hFont = 0;
+ OUTLINETEXTMETRICW *potm;
+ INT i;
+ FT_Face face;
+ WCHAR DefChar = 0xffff;
+ PWSTR Buffer = NULL;
+ ULONG Size;
+
+ if ((!pwc) && (!pgi)) return cwc;
+
+ dc = DC_LockDc(hdc);
+ if (!dc)
+ {
+ SetLastWin32Error(ERROR_INVALID_HANDLE);
+ return GDI_ERROR;
+ }
+ pdcattr = dc->pdcattr;
+ hFont = pdcattr->hlfntNew;
+ TextObj = RealizeFontInit(hFont);
+ DC_UnlockDc(dc);
+ if (!TextObj)
+ {
+ SetLastWin32Error(ERROR_INVALID_HANDLE);
+ return GDI_ERROR;
+ }
+
+ FontGDI = ObjToGDI(TextObj->Font, FONT);
+ TEXTOBJ_UnlockText(TextObj);
+
+ Buffer = ExAllocatePoolWithTag(PagedPool, cwc*sizeof(WORD), TAG_GDITEXT);
+ if (!Buffer)
+ {
+ SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
+ return GDI_ERROR;
+ }
+
+ if (iMode & GGI_MARK_NONEXISTING_GLYPHS) DefChar = 0x001f; /* Indicate non existence */
+ else
+ {
+ Size = IntGetOutlineTextMetrics(FontGDI, 0, NULL);
+ potm = ExAllocatePoolWithTag(PagedPool, Size, TAG_GDITEXT);
+ if (!potm)
+ {
+ SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
+ cwc = GDI_ERROR;
+ goto ErrorRet;
+ }
+ IntGetOutlineTextMetrics(FontGDI, Size, potm);
+ DefChar = potm->otmTextMetrics.tmDefaultChar; // May need this.
+ ExFreePool(potm);
+ }
+
+ IntLockFreeType;
+ face = FontGDI->face;
+
+ for (i = 0; i < cwc; i++)
+ {
+ Buffer[i] = FT_Get_Char_Index(face, pwc[i]);
+ if (Buffer[i] == 0)
+ {
+ if (DefChar == 0xffff && FT_IS_SFNT(face))
+ {
+ TT_OS2 *pOS2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2);
+ DefChar = (pOS2->usDefaultChar ? FT_Get_Char_Index(face, pOS2->usDefaultChar) : 0);
+ }
+ Buffer[i] = DefChar;
+ }
+ }
+
+ IntUnLockFreeType;
+
+ RtlCopyMemory( pgi, Buffer, cwc*sizeof(WORD));
+
+ErrorRet:
+ if (Buffer) ExFreePoolWithTag(Buffer, TAG_GDITEXT);
+ return cwc;
+}
+
+
+/*
+* @implemented
+*/
+DWORD
+APIENTRY
+NtGdiGetGlyphIndicesW(
+ IN HDC hdc,
+ IN OPTIONAL LPWSTR UnSafepwc,
+ IN INT cwc,
+ OUT OPTIONAL LPWORD UnSafepgi,
+ IN DWORD iMode)
+{
+ PDC dc;
+ PDC_ATTR pdcattr;
+ PTEXTOBJ TextObj;
+ PFONTGDI FontGDI;
+ HFONT hFont = 0;
+ NTSTATUS Status = STATUS_SUCCESS;
+ OUTLINETEXTMETRICW *potm;
+ INT i;
+ FT_Face face;
+ WCHAR DefChar = 0xffff;
+ PWSTR Buffer = NULL;
+ ULONG Size;
+
+ if ((!UnSafepwc) && (!UnSafepgi)) return cwc;
+
+ dc = DC_LockDc(hdc);
+ if (!dc)
+ {
+ SetLastWin32Error(ERROR_INVALID_HANDLE);
+ return GDI_ERROR;
+ }
+ pdcattr = dc->pdcattr;
+ hFont = pdcattr->hlfntNew;
+ TextObj = RealizeFontInit(hFont);
+ DC_UnlockDc(dc);
+ if (!TextObj)
+ {
+ SetLastWin32Error(ERROR_INVALID_HANDLE);
+ return GDI_ERROR;
+ }
+
+ FontGDI = ObjToGDI(TextObj->Font, FONT);
+ TEXTOBJ_UnlockText(TextObj);
+
+ Buffer = ExAllocatePoolWithTag(PagedPool, cwc*sizeof(WORD), TAG_GDITEXT);
+ if (!Buffer)
+ {
+ SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
+ return GDI_ERROR;
+ }
+
+ if (iMode & GGI_MARK_NONEXISTING_GLYPHS) DefChar = 0x001f; /* Indicate non existence */
+ else
+ {
+ Size = IntGetOutlineTextMetrics(FontGDI, 0, NULL);
+ potm = ExAllocatePoolWithTag(PagedPool, Size, TAG_GDITEXT);
+ if (!potm)
+ {
+ Status = ERROR_NOT_ENOUGH_MEMORY;
+ goto ErrorRet;
+ }
+ IntGetOutlineTextMetrics(FontGDI, Size, potm);
+ DefChar = potm->otmTextMetrics.tmDefaultChar; // May need this.
+ ExFreePool(potm);
+ }
+
+ _SEH2_TRY
+ {
+ ProbeForRead(UnSafepwc,
+ sizeof(PWSTR),
+ 1);
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ Status = _SEH2_GetExceptionCode();
+ }
+ _SEH2_END;
+
+ if (!NT_SUCCESS(Status)) goto ErrorRet;
+
+ IntLockFreeType;
+ face = FontGDI->face;
+
+ if (DefChar == 0xffff && FT_IS_SFNT(face))
+ {
+ TT_OS2 *pOS2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2);
+ DefChar = (pOS2->usDefaultChar ? FT_Get_Char_Index(face, pOS2->usDefaultChar) : 0);
+ }
+
+ for (i = 0; i < cwc; i++)
+ {
+ Buffer[i] = FT_Get_Char_Index(face, UnSafepwc[i]); // FIXME: unsafe!
+ if (Buffer[i] == 0)
+ {
+ Buffer[i] = DefChar;
+ }
+ }
+
+ IntUnLockFreeType;
+
+ _SEH2_TRY
+ {
+ ProbeForWrite(UnSafepgi,
+ sizeof(WORD),
+ 1);
+ RtlCopyMemory(UnSafepgi,
+ Buffer,
+ cwc*sizeof(WORD));
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ Status = _SEH2_GetExceptionCode();
+ }
+ _SEH2_END;
+
+ErrorRet:
+ ExFreePoolWithTag(Buffer, TAG_GDITEXT);
+ if (NT_SUCCESS(Status)) return cwc;
+ SetLastWin32Error(Status);
+ return GDI_ERROR;
+}
+
+
+/* EOF */
--- /dev/null
- PTEXTOBJ pOrgFnt, pNewFnt = NULL;
- HFONT hOrgFont = NULL;
+
+#include <win32k.h>
+
+#define NDEBUG
+#include <debug.h>
+
+
+//
+//
+// Gdi Batch Flush support functions.
+//
+
+//
+// DoDeviceSync
+//
+// based on IntEngEnter from eng/engmisc.c
+//
+VOID
+FASTCALL
+DoDeviceSync( SURFOBJ *Surface, PRECTL Rect, FLONG fl)
+{
+ PPDEVOBJ Device = (PDEVOBJ*)Surface->hdev;
+// No punting and "Handle to a surface, provided that the surface is device-managed.
+// Otherwise, dhsurf is zero".
+ if (!(Device->flFlags & PDEV_DRIVER_PUNTED_CALL) && (Surface->dhsurf))
+ {
+ if (Device->DriverFunctions.SynchronizeSurface)
+ {
+ Device->DriverFunctions.SynchronizeSurface(Surface, Rect, fl);
+ }
+ else
+ {
+ if (Device->DriverFunctions.Synchronize)
+ {
+ Device->DriverFunctions.Synchronize(Surface->dhpdev, Rect);
+ }
+ }
+ }
+}
+
+VOID
+FASTCALL
+SynchonizeDriver(FLONG Flags)
+{
+ SURFOBJ *SurfObj;
+ PPDEVOBJ Device;
+
+ if (Flags & GCAPS2_SYNCFLUSH)
+ Flags = DSS_FLUSH_EVENT;
+ if (Flags & GCAPS2_SYNCTIMER)
+ Flags = DSS_TIMER_EVENT;
+
+ Device = IntEnumHDev();
+// UNIMPLEMENTED;
+//ASSERT(FALSE);
+ SurfObj = 0;// EngLockSurface( Device->pSurface );
+ if(!SurfObj) return;
+ DoDeviceSync( SurfObj, NULL, Flags);
+ EngUnlockSurface(SurfObj);
+ return;
+}
+
+//
+// Process the batch.
+//
+ULONG
+FASTCALL
+GdiFlushUserBatch(PDC dc, PGDIBATCHHDR pHdr)
+{
+ BOOL Hit = FALSE;
+ ULONG Cmd = 0, Size = 0;
+ PDC_ATTR pdcattr = NULL;
+
+ if (dc)
+ {
+ pdcattr = dc->pdcattr;
+ }
+
+ _SEH2_TRY
+ {
+ Cmd = pHdr->Cmd;
+ Size = pHdr->Size; // Return the full size of the structure.
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ Hit = TRUE;
+ }
+ _SEH2_END;
+
+ if (Hit)
+ {
+ DPRINT1("WARNING! GdiBatch Fault!\n");
+ return 0;
+ }
+
+ // FYI! The thread is approaching the end of sunset.
+ switch(Cmd)
+ {
+ case GdiBCPatBlt: // Highest pri first!
+ break;
+ case GdiBCPolyPatBlt:
+ break;
+ case GdiBCTextOut:
+ break;
+ case GdiBCExtTextOut:
+ break;
+ case GdiBCSetBrushOrg:
+ {
+ PGDIBSSETBRHORG pgSBO;
+ if (!dc) break;
+ pgSBO = (PGDIBSSETBRHORG) pHdr;
+ pdcattr->ptlBrushOrigin = pgSBO->ptlBrushOrigin;
+ IntptlBrushOrigin(dc, pgSBO->ptlBrushOrigin.x, pgSBO->ptlBrushOrigin.y);
+ break;
+ }
+ case GdiBCExtSelClipRgn:
+ break;
+ case GdiBCSelObj:
+ {
+ PGDIBSOBJECT pgO;
- pOrgFnt = dc->dclevel.plfnt;
- if (pOrgFnt)
- {
- hOrgFont = pOrgFnt->BaseObject.hHmgr;
- }
- else
- {
- hOrgFont = pdcattr->hlfntNew;
- }
++ PTEXTOBJ pNewFnt = NULL;
+
+ if (!dc) break;
+ pgO = (PGDIBSOBJECT) pHdr;
+
+ if (NT_SUCCESS(TextIntRealizeFont((HFONT)pgO->hgdiobj,NULL)))
+ {
+ /* LFONTOBJ use share and locking. */
+ pNewFnt = TEXTOBJ_LockText(pgO->hgdiobj);
+
+ dc->dclevel.plfnt = pNewFnt;
+ dc->hlfntCur = pgO->hgdiobj;
+ pdcattr->hlfntNew = pgO->hgdiobj;
+ pdcattr->ulDirty_ |= DIRTY_CHARSET;
+ pdcattr->ulDirty_ &= ~SLOW_WIDTHS;
+ }
+ if (pNewFnt) TEXTOBJ_UnlockText(pNewFnt);
+ break;
+ }
+ case GdiBCDelRgn:
+ DPRINT("Delete Region Object!\n");
+ case GdiBCDelObj:
+ {
+ PGDIBSOBJECT pgO = (PGDIBSOBJECT) pHdr;
+ GreDeleteObject( pgO->hgdiobj );
+ break;
+ }
+ default:
+ break;
+ }
+
+ return Size;
+}
+
+/*
+ * NtGdiFlush
+ *
+ * Flushes the calling thread's current batch.
+ */
+VOID
+APIENTRY
+NtGdiFlush(VOID)
+{
+ SynchonizeDriver(GCAPS2_SYNCFLUSH);
+}
+
+/*
+ * NtGdiFlushUserBatch
+ *
+ * Callback for thread batch flush routine.
+ *
+ * Think small & fast!
+ */
+NTSTATUS
+APIENTRY
+NtGdiFlushUserBatch(VOID)
+{
+ PTEB pTeb = NtCurrentTeb();
+ ULONG GdiBatchCount = pTeb->GdiBatchCount;
+
+ if( (GdiBatchCount > 0) && (GdiBatchCount <= (GDIBATCHBUFSIZE/4)))
+ {
+ HDC hDC = (HDC) pTeb->GdiTebBatch.HDC;
+
+ /* If hDC is zero and the buffer fills up with delete objects we need
+ to run anyway.
+ */
+ if (hDC || GdiBatchCount)
+ {
+ PCHAR pHdr = (PCHAR)&pTeb->GdiTebBatch.Buffer[0];
+ PDC pDC = NULL;
+
+ if (hDC && !IsObjectDead(hDC))
+ {
+ pDC = DC_LockDc(hDC);
+ }
+
+ // No need to init anything, just go!
+ for (; GdiBatchCount > 0; GdiBatchCount--)
+ {
+ ULONG Size;
+ // Process Gdi Batch!
+ Size = GdiFlushUserBatch(pDC, (PGDIBATCHHDR) pHdr);
+ if (!Size) break;
+ pHdr += Size;
+ }
+
+ if (pDC)
+ {
+ DC_UnlockDc(pDC);
+ }
+
+ // Exit and clear out for the next round.
+ pTeb->GdiTebBatch.Offset = 0;
+ pTeb->GdiBatchCount = 0;
+ pTeb->GdiTebBatch.HDC = 0;
+ }
+ }
+
+ // FIXME: on xp the function returns &pTeb->RealClientId, maybe VOID?
+ return STATUS_SUCCESS;
+}
+
+
--- /dev/null
- USHORT sysMode, palMode;
+/*
+ * COPYRIGHT: See COPYING in the top level directory
+ * PROJECT: ReactOS kernel
+ * PURPOSE: GDI Palette Functions
+ * FILE: subsys/win32k/eng/palette.c
+ * PROGRAMERS: Jason Filby
+ * Timo Kreuzer
+ */
+
+#include <win32k.h>
+
+#define NDEBUG
+#include <debug.h>
+
+static UINT SystemPaletteUse = SYSPAL_NOSTATIC; /* the program need save the pallete and restore it */
+
+PALETTE gpalRGB, gpalBGR, gpalMono, gpalRGB555, gpalRGB565, *gppalDefault;
+PPALETTE appalSurfaceDefault[11];
+
+const PALETTEENTRY g_sysPalTemplate[NB_RESERVED_COLORS] =
+{
+ // first 10 entries in the system palette
+ // red green blue flags
+ { 0x00, 0x00, 0x00, PC_SYS_USED },
+ { 0x80, 0x00, 0x00, PC_SYS_USED },
+ { 0x00, 0x80, 0x00, PC_SYS_USED },
+ { 0x80, 0x80, 0x00, PC_SYS_USED },
+ { 0x00, 0x00, 0x80, PC_SYS_USED },
+ { 0x80, 0x00, 0x80, PC_SYS_USED },
+ { 0x00, 0x80, 0x80, PC_SYS_USED },
+ { 0xc0, 0xc0, 0xc0, PC_SYS_USED },
+ { 0xc0, 0xdc, 0xc0, PC_SYS_USED },
+ { 0xa6, 0xca, 0xf0, PC_SYS_USED },
+
+ // ... c_min/2 dynamic colorcells
+ // ... gap (for sparse palettes)
+ // ... c_min/2 dynamic colorcells
+
+ { 0xff, 0xfb, 0xf0, PC_SYS_USED },
+ { 0xa0, 0xa0, 0xa4, PC_SYS_USED },
+ { 0x80, 0x80, 0x80, PC_SYS_USED },
+ { 0xff, 0x00, 0x00, PC_SYS_USED },
+ { 0x00, 0xff, 0x00, PC_SYS_USED },
+ { 0xff, 0xff, 0x00, PC_SYS_USED },
+ { 0x00, 0x00, 0xff, PC_SYS_USED },
+ { 0xff, 0x00, 0xff, PC_SYS_USED },
+ { 0x00, 0xff, 0xff, PC_SYS_USED },
+ { 0xff, 0xff, 0xff, PC_SYS_USED } // last 10
+};
+
+unsigned short GetNumberOfBits(unsigned int dwMask)
+{
+ unsigned short wBits;
+ for (wBits = 0; dwMask; dwMask = dwMask & (dwMask - 1))
+ wBits++;
+ return wBits;
+}
+
+// Create the system palette
+HPALETTE FASTCALL PALETTE_Init(VOID)
+{
+ int i;
+ HPALETTE hpalette;
+ PLOGPALETTE palPtr;
+
+ // create default palette (20 system colors)
+ palPtr = ExAllocatePoolWithTag(PagedPool,
+ sizeof(LOGPALETTE) +
+ (NB_RESERVED_COLORS * sizeof(PALETTEENTRY)),
+ TAG_PALETTE);
+ if (!palPtr) return FALSE;
+
+ palPtr->palVersion = 0x300;
+ palPtr->palNumEntries = NB_RESERVED_COLORS;
+ for (i=0; i<NB_RESERVED_COLORS; i++)
+ {
+ palPtr->palPalEntry[i].peRed = g_sysPalTemplate[i].peRed;
+ palPtr->palPalEntry[i].peGreen = g_sysPalTemplate[i].peGreen;
+ palPtr->palPalEntry[i].peBlue = g_sysPalTemplate[i].peBlue;
+ palPtr->palPalEntry[i].peFlags = 0;
+ }
+
+ hpalette = NtGdiCreatePaletteInternal(palPtr,NB_RESERVED_COLORS);
+ ExFreePoolWithTag(palPtr, TAG_PALETTE);
+
+ /* palette_size = visual->map_entries; */
+
+ gpalRGB.Mode = PAL_RGB;
+ gpalRGB.RedMask = RGB(0xFF, 0x00, 0x00);
+ gpalRGB.GreenMask = RGB(0x00, 0xFF, 0x00);
+ gpalRGB.BlueMask = RGB(0x00, 0x00, 0xFF);
+ gpalRGB.BaseObject.ulShareCount = 0;
+ gpalRGB.BaseObject.BaseFlags = 0 ;
+
+ gpalBGR.Mode = PAL_BGR;
+ gpalBGR.RedMask = RGB(0x00, 0x00, 0xFF);
+ gpalBGR.GreenMask = RGB(0x00, 0xFF, 0x00);
+ gpalBGR.BlueMask = RGB(0xFF, 0x00, 0x00);
+ gpalBGR.BaseObject.ulShareCount = 0;
+ gpalBGR.BaseObject.BaseFlags = 0 ;
+
+ gpalRGB555.Mode = PAL_RGB16_555 | PAL_BITFIELDS;
+ gpalRGB555.RedMask = 0x7C00;
+ gpalRGB555.GreenMask = 0x3E0;
+ gpalRGB555.BlueMask = 0x1F;
+ gpalRGB555.BaseObject.ulShareCount = 0;
+ gpalRGB555.BaseObject.BaseFlags = 0 ;
+
+ gpalRGB565.Mode = PAL_RGB16_565 | PAL_BITFIELDS;
+ gpalRGB565.RedMask = 0xF800;
+ gpalRGB565.GreenMask = 0x7E0;
+ gpalRGB565.BlueMask = 0x1F;
+ gpalRGB565.BaseObject.ulShareCount = 0;
+ gpalRGB565.BaseObject.BaseFlags = 0 ;
+
+ memset(&gpalMono, 0, sizeof(PALETTE));
+ gpalMono.Mode = PAL_MONOCHROME;
+ gpalMono.BaseObject.ulShareCount = 0;
+ gpalMono.BaseObject.BaseFlags = 0 ;
+
+ /* Initialize default surface palettes */
+ gppalDefault = PALETTE_ShareLockPalette(hpalette);
+ appalSurfaceDefault[BMF_1BPP] = &gpalMono;
+ appalSurfaceDefault[BMF_4BPP] = gppalDefault;
+ appalSurfaceDefault[BMF_8BPP] = gppalDefault;
+ appalSurfaceDefault[BMF_16BPP] = &gpalRGB565;
+ appalSurfaceDefault[BMF_24BPP] = &gpalBGR;
+ appalSurfaceDefault[BMF_32BPP] = &gpalBGR;
+ appalSurfaceDefault[BMF_4RLE] = gppalDefault;
+ appalSurfaceDefault[BMF_8RLE] = gppalDefault;
+ appalSurfaceDefault[BMF_JPEG] = &gpalRGB;
+ appalSurfaceDefault[BMF_PNG] = &gpalRGB;
+
+ return hpalette;
+}
+
+VOID FASTCALL PALETTE_ValidateFlags(PALETTEENTRY* lpPalE, INT size)
+{
+ int i = 0;
+ for (; i<size ; i++)
+ lpPalE[i].peFlags = PC_SYS_USED | (lpPalE[i].peFlags & 0x07);
+}
+
+HPALETTE
+FASTCALL
+PALETTE_AllocPalette(ULONG Mode,
+ ULONG NumColors,
+ ULONG *Colors,
+ ULONG Red,
+ ULONG Green,
+ ULONG Blue)
+{
+ HPALETTE NewPalette;
+ PPALETTE PalGDI;
+
+ PalGDI = (PPALETTE)GDIOBJ_AllocObjWithHandle(GDI_OBJECT_TYPE_PALETTE);
+ if (!PalGDI)
+ {
+ return NULL;
+ }
+
+ NewPalette = PalGDI->BaseObject.hHmgr;
+
+ PalGDI->Self = NewPalette;
+ PalGDI->Mode = Mode;
+
+ if (NULL != Colors)
+ {
+ PalGDI->IndexedColors = ExAllocatePoolWithTag(PagedPool,
+ sizeof(PALETTEENTRY) * NumColors,
+ TAG_PALETTE);
+ if (NULL == PalGDI->IndexedColors)
+ {
+ PALETTE_UnlockPalette(PalGDI);
+ PALETTE_FreePaletteByHandle(NewPalette);
+ return NULL;
+ }
+ RtlCopyMemory(PalGDI->IndexedColors, Colors, sizeof(PALETTEENTRY) * NumColors);
+ }
+
+ if (PAL_INDEXED == Mode)
+ {
+ PalGDI->NumColors = NumColors;
+ }
+ else if (PAL_BITFIELDS == Mode)
+ {
+ PalGDI->RedMask = Red;
+ PalGDI->GreenMask = Green;
+ PalGDI->BlueMask = Blue;
+
+ if (Red == 0x7c00 && Green == 0x3E0 && Blue == 0x1F)
+ PalGDI->Mode |= PAL_RGB16_555;
+ else if (Red == 0xF800 && Green == 0x7E0 && Blue == 0x1F)
+ PalGDI->Mode |= PAL_RGB16_565;
+ }
+
+ PALETTE_UnlockPalette(PalGDI);
+
+ return NewPalette;
+}
+
+HPALETTE
+FASTCALL
+PALETTE_AllocPaletteIndexedRGB(ULONG NumColors,
+ CONST RGBQUAD *Colors)
+{
+ HPALETTE NewPalette;
+ PPALETTE PalGDI;
+ UINT i;
+
+ PalGDI = (PPALETTE)GDIOBJ_AllocObjWithHandle(GDI_OBJECT_TYPE_PALETTE);
+ if (!PalGDI)
+ {
+ return NULL;
+ }
+
+ NewPalette = PalGDI->BaseObject.hHmgr;
+
+ PalGDI->Self = NewPalette;
+ PalGDI->Mode = PAL_INDEXED;
+
+ PalGDI->IndexedColors = ExAllocatePoolWithTag(PagedPool,
+ sizeof(PALETTEENTRY) * NumColors,
+ TAG_PALETTE);
+ if (NULL == PalGDI->IndexedColors)
+ {
+ PALETTE_UnlockPalette(PalGDI);
+ PALETTE_FreePaletteByHandle(NewPalette);
+ return NULL;
+ }
+
+ for (i = 0; i < NumColors; i++)
+ {
+ PalGDI->IndexedColors[i].peRed = Colors[i].rgbRed;
+ PalGDI->IndexedColors[i].peGreen = Colors[i].rgbGreen;
+ PalGDI->IndexedColors[i].peBlue = Colors[i].rgbBlue;
+ PalGDI->IndexedColors[i].peFlags = 0;
+ }
+
+ PalGDI->NumColors = NumColors;
+
+ PALETTE_UnlockPalette(PalGDI);
+
+ return NewPalette;
+}
+
+BOOL INTERNAL_CALL
+PALETTE_Cleanup(PVOID ObjectBody)
+{
+ PPALETTE pPal = (PPALETTE)ObjectBody;
+ if (NULL != pPal->IndexedColors)
+ {
+ ExFreePoolWithTag(pPal->IndexedColors, TAG_PALETTE);
+ }
+
+ return TRUE;
+}
+
+INT FASTCALL
+PALETTE_GetObject(PPALETTE ppal, INT cbCount, LPLOGBRUSH lpBuffer)
+{
+ if (!lpBuffer)
+ {
+ return sizeof(WORD);
+ }
+
+ if ((UINT)cbCount < sizeof(WORD)) return 0;
+ *((WORD*)lpBuffer) = (WORD)ppal->NumColors;
+ return sizeof(WORD);
+}
+
+ULONG
+NTAPI
+PALETTE_ulGetNearestPaletteIndex(PALETTE* ppal, ULONG iColor)
+{
+ ULONG ulDiff, ulColorDiff, ulMinimalDiff = 0xFFFFFF;
+ ULONG i, ulBestIndex = 0;
+ PALETTEENTRY peColor = *(PPALETTEENTRY)&iColor;
+
+ /* Loop all palette entries, break on exact match */
+ for (i = 0; i < ppal->NumColors && ulMinimalDiff != 0; i++)
+ {
+ /* Calculate distance in the color cube */
+ ulDiff = peColor.peRed - ppal->IndexedColors[i].peRed;
+ ulColorDiff = ulDiff * ulDiff;
+ ulDiff = peColor.peGreen - ppal->IndexedColors[i].peGreen;
+ ulColorDiff += ulDiff * ulDiff;
+ ulDiff = peColor.peBlue - ppal->IndexedColors[i].peBlue;
+ ulColorDiff += ulDiff * ulDiff;
+
+ /* Check for a better match */
+ if (ulColorDiff < ulMinimalDiff)
+ {
+ ulBestIndex = i;
+ ulMinimalDiff = ulColorDiff;
+ }
+ }
+
+ return ulBestIndex;
+}
+
+ULONG
+NTAPI
+PALETTE_ulGetNearestBitFieldsIndex(PALETTE* ppal, ULONG ulColor)
+{
+ ULONG ulNewColor;
+
+ // FIXME: HACK, should be stored already
+ ppal->ulRedShift = CalculateShift(RGB(0xff,0,0), ppal->RedMask);
+ ppal->ulGreenShift = CalculateShift(RGB(0,0xff,0), ppal->GreenMask);
+ ppal->ulBlueShift = CalculateShift(RGB(0,0,0xff), ppal->BlueMask);
+
+ ulNewColor = _rotl(ulColor, ppal->ulRedShift) & ppal->RedMask;
+ ulNewColor |= _rotl(ulColor, ppal->ulGreenShift) & ppal->GreenMask;
+ ulNewColor |= _rotl(ulColor, ppal->ulBlueShift) & ppal->BlueMask;
+
+ return ulNewColor;
+}
+
+ULONG
+NTAPI
+PALETTE_ulGetNearestIndex(PALETTE* ppal, ULONG ulColor)
+{
+ if (ppal->Mode & PAL_INDEXED) // use fl & PALINDEXED
+ return PALETTE_ulGetNearestPaletteIndex(ppal, ulColor);
+ else
+ return PALETTE_ulGetNearestBitFieldsIndex(ppal, ulColor);
+}
+
+VOID
+NTAPI
+PALETTE_vGetBitMasks(PPALETTE ppal, PULONG pulColors)
+{
+ ASSERT(pulColors);
+
+ if (ppal->Mode & PAL_INDEXED || ppal->Mode & PAL_RGB)
+ {
+ pulColors[0] = RGB(0xFF, 0x00, 0x00);
+ pulColors[1] = RGB(0x00, 0xFF, 0x00);
+ pulColors[2] = RGB(0x00, 0x00, 0xFF);
+ }
+ else if (ppal->Mode & PAL_BGR)
+ {
+ pulColors[0] = RGB(0x00, 0x00, 0xFF);
+ pulColors[1] = RGB(0x00, 0xFF, 0x00);
+ pulColors[2] = RGB(0xFF, 0x00, 0x00);
+ }
+ else if (ppal->Mode & PAL_BITFIELDS)
+ {
+ pulColors[0] = ppal->RedMask;
+ pulColors[1] = ppal->GreenMask;
+ pulColors[2] = ppal->BlueMask;
+ }
+}
+
+VOID
+FASTCALL
+ColorCorrection(PPALETTE PalGDI, PPALETTEENTRY PaletteEntry, ULONG Colors)
+{
+ PPDEVOBJ ppdev = (PPDEVOBJ)PalGDI->hPDev;
+
+ if (!ppdev) return;
+
+ if (ppdev->flFlags & PDEV_GAMMARAMP_TABLE)
+ {
+ INT i;
+ PGAMMARAMP GammaRamp = (PGAMMARAMP)ppdev->pvGammaRamp;
+ for ( i = 0; i < Colors; i++)
+ {
+ PaletteEntry[i].peRed += GammaRamp->Red[i];
+ PaletteEntry[i].peGreen += GammaRamp->Green[i];
+ PaletteEntry[i].peBlue += GammaRamp->Blue[i];
+ }
+ }
+ return;
+}
+
+/** Display Driver Interface **************************************************/
+
+/*
+ * @implemented
+ */
+HPALETTE
+APIENTRY
+EngCreatePalette(
+ ULONG Mode,
+ ULONG NumColors,
+ ULONG *Colors,
+ ULONG Red,
+ ULONG Green,
+ ULONG Blue)
+{
+ HPALETTE Palette;
+
+ Palette = PALETTE_AllocPalette(Mode, NumColors, Colors, Red, Green, Blue);
+ if (Palette != NULL)
+ {
+ GDIOBJ_SetOwnership(Palette, NULL);
+ }
+
+ return Palette;
+}
+
+/*
+ * @implemented
+ */
+BOOL
+APIENTRY
+EngDeletePalette(IN HPALETTE Palette)
+{
+ GDIOBJ_SetOwnership(Palette, PsGetCurrentProcess());
+
+ return PALETTE_FreePaletteByHandle(Palette);
+}
+
+/*
+ * @implemented
+ */
+ULONG
+APIENTRY
+PALOBJ_cGetColors(PALOBJ *PalObj, ULONG Start, ULONG Colors, ULONG *PaletteEntry)
+{
+ PALETTE *PalGDI;
+
+ PalGDI = (PALETTE*)PalObj;
+ /* PalGDI = (PALETTE*)AccessInternalObjectFromUserObject(PalObj); */
+
+ if (Start >= PalGDI->NumColors)
+ return 0;
+
+ Colors = min(Colors, PalGDI->NumColors - Start);
+
+ /* NOTE: PaletteEntry ULONGs are in the same order as PALETTEENTRY. */
+ RtlCopyMemory(PaletteEntry, PalGDI->IndexedColors + Start, sizeof(ULONG) * Colors);
+
+ if (PalGDI->Mode & PAL_GAMMACORRECTION)
+ ColorCorrection(PalGDI, (PPALETTEENTRY)PaletteEntry, Colors);
+
+ return Colors;
+}
+
+
+/** Systemcall Interface ******************************************************/
+
+/*
+ * @implemented
+ */
+HPALETTE APIENTRY
+NtGdiCreatePaletteInternal ( IN LPLOGPALETTE pLogPal, IN UINT cEntries )
+{
+ PPALETTE PalGDI;
+ HPALETTE NewPalette;
+
+ pLogPal->palNumEntries = cEntries;
+ NewPalette = PALETTE_AllocPalette( PAL_INDEXED,
+ cEntries,
+ (PULONG)pLogPal->palPalEntry,
+ 0, 0, 0);
+
+ if (NewPalette == NULL)
+ {
+ return NULL;
+ }
+
+ PalGDI = (PPALETTE) PALETTE_LockPalette(NewPalette);
+ if (PalGDI != NULL)
+ {
+ PALETTE_ValidateFlags(PalGDI->IndexedColors, PalGDI->NumColors);
+ PALETTE_UnlockPalette(PalGDI);
+ }
+ else
+ {
+ /* FIXME - Handle PalGDI == NULL!!!! */
+ DPRINT1("PalGDI is NULL\n");
+ }
+ return NewPalette;
+}
+
+HPALETTE APIENTRY NtGdiCreateHalftonePalette(HDC hDC)
+{
+ int i, r, g, b;
+ struct {
+ WORD Version;
+ WORD NumberOfEntries;
+ PALETTEENTRY aEntries[256];
+ } Palette;
+
+ Palette.Version = 0x300;
+ Palette.NumberOfEntries = 256;
+ if (IntGetSystemPaletteEntries(hDC, 0, 256, Palette.aEntries) == 0)
+ {
+ /* from wine, more that 256 color math */
+ Palette.NumberOfEntries = 20;
+ for (i = 0; i < Palette.NumberOfEntries; i++)
+ {
+ Palette.aEntries[i].peRed=0xff;
+ Palette.aEntries[i].peGreen=0xff;
+ Palette.aEntries[i].peBlue=0xff;
+ Palette.aEntries[i].peFlags=0x00;
+ }
+
+ Palette.aEntries[0].peRed=0x00;
+ Palette.aEntries[0].peBlue=0x00;
+ Palette.aEntries[0].peGreen=0x00;
+
+ /* the first 6 */
+ for (i=1; i <= 6; i++)
+ {
+ Palette.aEntries[i].peRed=(i%2)?0x80:0;
+ Palette.aEntries[i].peGreen=(i==2)?0x80:(i==3)?0x80:(i==6)?0x80:0;
+ Palette.aEntries[i].peBlue=(i>3)?0x80:0;
+ }
+
+ for (i=7; i <= 12; i++)
+ {
+ switch(i)
+ {
+ case 7:
+ Palette.aEntries[i].peRed=0xc0;
+ Palette.aEntries[i].peBlue=0xc0;
+ Palette.aEntries[i].peGreen=0xc0;
+ break;
+ case 8:
+ Palette.aEntries[i].peRed=0xc0;
+ Palette.aEntries[i].peGreen=0xdc;
+ Palette.aEntries[i].peBlue=0xc0;
+ break;
+ case 9:
+ Palette.aEntries[i].peRed=0xa6;
+ Palette.aEntries[i].peGreen=0xca;
+ Palette.aEntries[i].peBlue=0xf0;
+ break;
+ case 10:
+ Palette.aEntries[i].peRed=0xff;
+ Palette.aEntries[i].peGreen=0xfb;
+ Palette.aEntries[i].peBlue=0xf0;
+ break;
+ case 11:
+ Palette.aEntries[i].peRed=0xa0;
+ Palette.aEntries[i].peGreen=0xa0;
+ Palette.aEntries[i].peBlue=0xa4;
+ break;
+ case 12:
+ Palette.aEntries[i].peRed=0x80;
+ Palette.aEntries[i].peGreen=0x80;
+ Palette.aEntries[i].peBlue=0x80;
+ }
+ }
+
+ for (i=13; i <= 18; i++)
+ {
+ Palette.aEntries[i].peRed=(i%2)?0xff:0;
+ Palette.aEntries[i].peGreen=(i==14)?0xff:(i==15)?0xff:(i==18)?0xff:0;
+ Palette.aEntries[i].peBlue=(i>15)?0xff:0x00;
+ }
+ }
+ else
+ {
+ /* 256 color table */
+ for (r = 0; r < 6; r++)
+ for (g = 0; g < 6; g++)
+ for (b = 0; b < 6; b++)
+ {
+ i = r + g*6 + b*36 + 10;
+ Palette.aEntries[i].peRed = r * 51;
+ Palette.aEntries[i].peGreen = g * 51;
+ Palette.aEntries[i].peBlue = b * 51;
+ }
+
+ for (i = 216; i < 246; i++)
+ {
+ int v = (i - 216) << 3;
+ Palette.aEntries[i].peRed = v;
+ Palette.aEntries[i].peGreen = v;
+ Palette.aEntries[i].peBlue = v;
+ }
+ }
+
+ return NtGdiCreatePaletteInternal((LOGPALETTE *)&Palette, Palette.NumberOfEntries);
+}
+
+BOOL
+APIENTRY
+NtGdiResizePalette(
+ HPALETTE hpal,
+ UINT Entries)
+{
+/* PALOBJ *palPtr = (PALOBJ*)AccessUserObject(hPal);
+ UINT cPrevEnt, prevVer;
+ INT prevsize, size = sizeof(LOGPALETTE) + (cEntries - 1) * sizeof(PALETTEENTRY);
+ XLATEOBJ *XlateObj = NULL;
+
+ if(!palPtr) return FALSE;
+ cPrevEnt = palPtr->logpalette->palNumEntries;
+ prevVer = palPtr->logpalette->palVersion;
+ prevsize = sizeof(LOGPALETTE) + (cPrevEnt - 1) * sizeof(PALETTEENTRY) + sizeof(int*) + sizeof(GDIOBJHDR);
+ size += sizeof(int*) + sizeof(GDIOBJHDR);
+ XlateObj = palPtr->logicalToSystem;
+
+ if (!(palPtr = GDI_ReallocObject(size, hPal, palPtr))) return FALSE;
+
+ if(XlateObj)
+ {
+ XLATEOBJ *NewXlateObj = (int*) HeapReAlloc(GetProcessHeap(), 0, XlateObj, cEntries * sizeof(int));
+ if(NewXlateObj == NULL)
+ {
+ ERR("Can not resize logicalToSystem -- out of memory!");
+ GDI_ReleaseObj( hPal );
+ return FALSE;
+ }
+ palPtr->logicalToSystem = NewXlateObj;
+ }
+
+ if(cEntries > cPrevEnt)
+ {
+ if(XlateObj) memset(palPtr->logicalToSystem + cPrevEnt, 0, (cEntries - cPrevEnt)*sizeof(int));
+ memset( (BYTE*)palPtr + prevsize, 0, size - prevsize );
+ PALETTE_ValidateFlags((PALETTEENTRY*)((BYTE*)palPtr + prevsize), cEntries - cPrevEnt );
+ }
+ palPtr->logpalette->palNumEntries = cEntries;
+ palPtr->logpalette->palVersion = prevVer;
+// GDI_ReleaseObj( hPal );
+ return TRUE; */
+
+ UNIMPLEMENTED;
+ return FALSE;
+}
+
+BOOL
+APIENTRY
+NtGdiGetColorAdjustment(
+ HDC hdc,
+ LPCOLORADJUSTMENT pca)
+{
+ UNIMPLEMENTED;
+ return FALSE;
+}
+
+BOOL
+APIENTRY
+NtGdiSetColorAdjustment(
+ HDC hdc,
+ LPCOLORADJUSTMENT pca)
+{
+ UNIMPLEMENTED;
+ return FALSE;
+}
+
+COLORREF APIENTRY NtGdiGetNearestColor(HDC hDC, COLORREF Color)
+{
+ COLORREF nearest = CLR_INVALID;
+ PDC dc;
+ PPALETTE palGDI;
+ LONG RBits, GBits, BBits;
+
+ dc = DC_LockDc(hDC);
+ if (NULL != dc)
+ {
+ HPALETTE hpal = dc->dclevel.hpal;
+ palGDI = (PPALETTE) PALETTE_LockPalette(hpal);
+ if (!palGDI)
+ {
+ DC_UnlockDc(dc);
+ return nearest;
+ }
+
+ if (palGDI->Mode & PAL_INDEXED)
+ {
+ ULONG index;
+ index = PALETTE_ulGetNearestPaletteIndex(palGDI, Color);
+ nearest = PALETTE_ulGetRGBColorFromIndex(palGDI, index);
+ }
+ else if (palGDI->Mode & PAL_RGB || palGDI->Mode & PAL_BGR)
+ {
+ nearest = Color;
+ }
+ else if (palGDI->Mode & PAL_BITFIELDS)
+ {
+ RBits = 8 - GetNumberOfBits(palGDI->RedMask);
+ GBits = 8 - GetNumberOfBits(palGDI->GreenMask);
+ BBits = 8 - GetNumberOfBits(palGDI->BlueMask);
+ nearest = RGB(
+ (GetRValue(Color) >> RBits) << RBits,
+ (GetGValue(Color) >> GBits) << GBits,
+ (GetBValue(Color) >> BBits) << BBits);
+ }
+ PALETTE_UnlockPalette(palGDI);
+ DC_UnlockDc(dc);
+ }
+
+ return nearest;
+}
+
+UINT
+APIENTRY
+NtGdiGetNearestPaletteIndex(
+ HPALETTE hpal,
+ COLORREF crColor)
+{
+ PPALETTE ppal = (PPALETTE) PALETTE_LockPalette(hpal);
+ UINT index = 0;
+
+ if (ppal)
+ {
+ if (ppal->Mode & PAL_INDEXED)
+ {
+ /* Return closest match for the given RGB color */
+ index = PALETTE_ulGetNearestPaletteIndex(ppal, crColor);
+ }
+ // else SetLastError ?
+ PALETTE_UnlockPalette(ppal);
+ }
+
+ return index;
+}
+
+UINT
+FASTCALL
+IntGdiRealizePalette(HDC hDC)
+{
+ /*
+ * This function doesn't do any real work now and there's plenty
+ * of bugs in it.
+ */
+
+ PPALETTE palGDI, sysGDI;
+ int realized = 0;
+ PDC dc;
+ HPALETTE systemPalette;
- sysMode = sysGDI->Mode;
- palMode = palGDI->Mode;
+
+ dc = DC_LockDc(hDC);
+ if (!dc) return 0;
+
+ systemPalette = NtGdiGetStockObject(DEFAULT_PALETTE);
+ palGDI = PALETTE_LockPalette(dc->dclevel.hpal);
+
+ if (palGDI == NULL)
+ {
+ DPRINT1("IntGdiRealizePalette(): palGDI is NULL, exiting\n");
+ DC_UnlockDc(dc);
+ return 0;
+ }
+
+ sysGDI = PALETTE_LockPalette(systemPalette);
+
+ if (sysGDI == NULL)
+ {
+ DPRINT1("IntGdiRealizePalette(): sysGDI is NULL, exiting\n");
+ PALETTE_UnlockPalette(palGDI);
+ DC_UnlockDc(dc);
+ return 0;
+ }
+
+ // The RealizePalette function modifies the palette for the device associated with the specified device context. If the
+ // device context is a memory DC, the color table for the bitmap selected into the DC is modified. If the device
+ // context is a display DC, the physical palette for that device is modified.
+ if(dc->dctype == DC_TYPE_MEMORY)
+ {
+ // Memory managed DC
+ DPRINT1("RealizePalette unimplemented for memory managed DCs\n");
+ } else
+ {
+ DPRINT1("RealizePalette unimplemented for device DCs\n");
+ }
+
+ // need to pass this to IntEngCreateXlate with palettes unlocked
+ PALETTE_UnlockPalette(sysGDI);
+ PALETTE_UnlockPalette(palGDI);
+
+ DC_UnlockDc(dc);
+
+ return realized;
+}
+
+UINT APIENTRY
+IntAnimatePalette(HPALETTE hPal,
+ UINT StartIndex,
+ UINT NumEntries,
+ CONST PPALETTEENTRY PaletteColors)
+{
+ UINT ret = 0;
+
+ if( hPal != NtGdiGetStockObject(DEFAULT_PALETTE) )
+ {
+ PPALETTE palPtr;
+ UINT pal_entries;
+ HDC hDC;
+ PDC dc;
+ PWINDOW_OBJECT Wnd;
+ const PALETTEENTRY *pptr = PaletteColors;
+
+ palPtr = (PPALETTE)PALETTE_LockPalette(hPal);
+ if (!palPtr) return FALSE;
+
+ pal_entries = palPtr->NumColors;
+ if (StartIndex >= pal_entries)
+ {
+ PALETTE_UnlockPalette(palPtr);
+ return FALSE;
+ }
+ if (StartIndex+NumEntries > pal_entries) NumEntries = pal_entries - StartIndex;
+
+ for (NumEntries += StartIndex; StartIndex < NumEntries; StartIndex++, pptr++)
+ {
+ /* According to MSDN, only animate PC_RESERVED colours */
+ if (palPtr->IndexedColors[StartIndex].peFlags & PC_RESERVED)
+ {
+ memcpy( &palPtr->IndexedColors[StartIndex], pptr,
+ sizeof(PALETTEENTRY) );
+ ret++;
+ PALETTE_ValidateFlags(&palPtr->IndexedColors[StartIndex], 1);
+ }
+ }
+
+ PALETTE_UnlockPalette(palPtr);
+
+ /* Immediately apply the new palette if current window uses it */
+ Wnd = UserGetDesktopWindow();
+ hDC = UserGetWindowDC(Wnd);
+ dc = DC_LockDc(hDC);
+ if (NULL != dc)
+ {
+ if (dc->dclevel.hpal == hPal)
+ {
+ DC_UnlockDc(dc);
+ IntGdiRealizePalette(hDC);
+ }
+ else
+ DC_UnlockDc(dc);
+ }
+ UserReleaseDC(Wnd,hDC, FALSE);
+ }
+ return ret;
+}
+
+UINT APIENTRY
+IntGetPaletteEntries(
+ HPALETTE hpal,
+ UINT StartIndex,
+ UINT Entries,
+ LPPALETTEENTRY pe)
+{
+ PPALETTE palGDI;
+ UINT numEntries;
+
+ palGDI = (PPALETTE) PALETTE_LockPalette(hpal);
+ if (NULL == palGDI)
+ {
+ return 0;
+ }
+
+ numEntries = palGDI->NumColors;
+ if (NULL != pe)
+ {
+ if (numEntries < StartIndex + Entries)
+ {
+ Entries = numEntries - StartIndex;
+ }
+ if (numEntries <= StartIndex)
+ {
+ PALETTE_UnlockPalette(palGDI);
+ return 0;
+ }
+ memcpy(pe, palGDI->IndexedColors + StartIndex, Entries * sizeof(PALETTEENTRY));
+ }
+ else
+ {
+ Entries = numEntries;
+ }
+
+ PALETTE_UnlockPalette(palGDI);
+ return Entries;
+}
+
+UINT APIENTRY
+IntGetSystemPaletteEntries(HDC hDC,
+ UINT StartIndex,
+ UINT Entries,
+ LPPALETTEENTRY pe)
+{
+ PPALETTE palGDI = NULL;
+ PDC dc = NULL;
+ UINT EntriesSize = 0;
+ UINT Ret = 0;
+
+ if (Entries == 0)
+ {
+ SetLastWin32Error(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+
+ if (pe != NULL)
+ {
+ EntriesSize = Entries * sizeof(pe[0]);
+ if (Entries != EntriesSize / sizeof(pe[0]))
+ {
+ /* Integer overflow! */
+ SetLastWin32Error(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+ }
+
+ if (!(dc = DC_LockDc(hDC)))
+ {
+ SetLastWin32Error(ERROR_INVALID_HANDLE);
+ return 0;
+ }
+
+ palGDI = PALETTE_LockPalette(dc->dclevel.hpal);
+ if (palGDI != NULL)
+ {
+ if (pe != NULL)
+ {
+ if (StartIndex >= palGDI->NumColors)
+ Entries = 0;
+ else if (Entries > palGDI->NumColors - StartIndex)
+ Entries = palGDI->NumColors - StartIndex;
+
+ memcpy(pe,
+ palGDI->IndexedColors + StartIndex,
+ Entries * sizeof(pe[0]));
+
+ Ret = Entries;
+ }
+ else
+ {
+ Ret = dc->ppdev->gdiinfo.ulNumPalReg;
+ }
+ }
+
+ if (palGDI != NULL)
+ PALETTE_UnlockPalette(palGDI);
+
+ if (dc != NULL)
+ DC_UnlockDc(dc);
+
+ return Ret;
+}
+
+UINT
+APIENTRY
+IntSetPaletteEntries(
+ HPALETTE hpal,
+ UINT Start,
+ UINT Entries,
+ CONST LPPALETTEENTRY pe)
+{
+ PPALETTE palGDI;
+ WORD numEntries;
+
+ if ((UINT)hpal & GDI_HANDLE_STOCK_MASK)
+ {
+ return 0;
+ }
+
+ palGDI = PALETTE_LockPalette(hpal);
+ if (!palGDI) return 0;
+
+ numEntries = palGDI->NumColors;
+ if (Start >= numEntries)
+ {
+ PALETTE_UnlockPalette(palGDI);
+ return 0;
+ }
+ if (numEntries < Start + Entries)
+ {
+ Entries = numEntries - Start;
+ }
+ memcpy(palGDI->IndexedColors + Start, pe, Entries * sizeof(PALETTEENTRY));
+ PALETTE_UnlockPalette(palGDI);
+
+ return Entries;
+}
+
+W32KAPI
+LONG
+APIENTRY
+NtGdiDoPalette(
+ IN HGDIOBJ hObj,
+ IN WORD iStart,
+ IN WORD cEntries,
+ IN LPVOID pUnsafeEntries,
+ IN DWORD iFunc,
+ IN BOOL bInbound)
+{
+ LONG ret;
+ LPVOID pEntries = NULL;
+
+ /* FIXME: Handle bInbound correctly */
+
+ if (bInbound &&
+ (pUnsafeEntries == NULL || cEntries == 0))
+ {
+ return 0;
+ }
+
+ if (pUnsafeEntries)
+ {
+ pEntries = ExAllocatePoolWithTag(PagedPool, cEntries * sizeof(PALETTEENTRY), TAG_PALETTE);
+ if (!pEntries)
+ return 0;
+ if (bInbound)
+ {
+ _SEH2_TRY
+ {
+ ProbeForRead(pUnsafeEntries, cEntries * sizeof(PALETTEENTRY), 1);
+ memcpy(pEntries, pUnsafeEntries, cEntries * sizeof(PALETTEENTRY));
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ ExFreePoolWithTag(pEntries, TAG_PALETTE);
+ _SEH2_YIELD(return 0);
+ }
+ _SEH2_END
+ }
+ }
+
+ ret = 0;
+ switch(iFunc)
+ {
+ case GdiPalAnimate:
+ if (pEntries)
+ ret = IntAnimatePalette((HPALETTE)hObj, iStart, cEntries, (CONST PPALETTEENTRY)pEntries);
+ break;
+
+ case GdiPalSetEntries:
+ if (pEntries)
+ ret = IntSetPaletteEntries((HPALETTE)hObj, iStart, cEntries, (CONST LPPALETTEENTRY)pEntries);
+ break;
+
+ case GdiPalGetEntries:
+ ret = IntGetPaletteEntries((HPALETTE)hObj, iStart, cEntries, (LPPALETTEENTRY)pEntries);
+ break;
+
+ case GdiPalGetSystemEntries:
+ ret = IntGetSystemPaletteEntries((HDC)hObj, iStart, cEntries, (LPPALETTEENTRY)pEntries);
+ break;
+
+ case GdiPalSetColorTable:
+ if (pEntries)
+ ret = IntSetDIBColorTable((HDC)hObj, iStart, cEntries, (RGBQUAD*)pEntries);
+ break;
+
+ case GdiPalGetColorTable:
+ if (pEntries)
+ ret = IntGetDIBColorTable((HDC)hObj, iStart, cEntries, (RGBQUAD*)pEntries);
+ break;
+ }
+
+ if (pEntries)
+ {
+ if (!bInbound)
+ {
+ _SEH2_TRY
+ {
+ ProbeForWrite(pUnsafeEntries, cEntries * sizeof(PALETTEENTRY), 1);
+ memcpy(pUnsafeEntries, pEntries, cEntries * sizeof(PALETTEENTRY));
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ ret = 0;
+ }
+ _SEH2_END
+ }
+ ExFreePoolWithTag(pEntries, TAG_PALETTE);
+ }
+
+ return ret;
+}
+
+UINT APIENTRY
+NtGdiSetSystemPaletteUse(HDC hDC, UINT Usage)
+{
+ UINT old = SystemPaletteUse;
+
+ /* Device doesn't support colour palettes */
+ if (!(NtGdiGetDeviceCaps(hDC, RASTERCAPS) & RC_PALETTE)) {
+ return SYSPAL_ERROR;
+ }
+
+ switch (Usage)
+ {
+ case SYSPAL_NOSTATIC:
+ case SYSPAL_NOSTATIC256:
+ case SYSPAL_STATIC:
+ SystemPaletteUse = Usage;
+ break;
+
+ default:
+ old=SYSPAL_ERROR;
+ break;
+ }
+
+ return old;
+}
+
+UINT
+APIENTRY
+NtGdiGetSystemPaletteUse(HDC hDC)
+{
+ return SystemPaletteUse;
+}
+
+BOOL
+APIENTRY
+NtGdiUpdateColors(HDC hDC)
+{
+ PWINDOW_OBJECT Wnd;
+ BOOL calledFromUser, ret;
+ USER_REFERENCE_ENTRY Ref;
+
+ calledFromUser = UserIsEntered();
+
+ if (!calledFromUser){
+ UserEnterExclusive();
+ }
+
+ Wnd = UserGetWindowObject(IntWindowFromDC(hDC));
+ if (Wnd == NULL)
+ {
+ SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
+
+ if (!calledFromUser){
+ UserLeave();
+ }
+
+ return FALSE;
+ }
+
+ UserRefObjectCo(Wnd, &Ref);
+ ret = co_UserRedrawWindow(Wnd, NULL, 0, RDW_INVALIDATE);
+ UserDerefObjectCo(Wnd);
+
+ if (!calledFromUser){
+ UserLeave();
+ }
+
+ return ret;
+}
+
+BOOL
+APIENTRY
+NtGdiUnrealizeObject(HGDIOBJ hgdiobj)
+{
+ BOOL Ret = FALSE;
+ PPALETTE palGDI;
+
+ if ( !hgdiobj ||
+ ((UINT)hgdiobj & GDI_HANDLE_STOCK_MASK) ||
+ !GDI_HANDLE_IS_TYPE(hgdiobj, GDI_OBJECT_TYPE_PALETTE) )
+ return Ret;
+
+ palGDI = PALETTE_LockPalette(hgdiobj);
+ if (!palGDI) return FALSE;
+
+ // FIXME!!
+ // Need to do something!!!
+ // Zero out Current and Old Translated pointers?
+ //
+ Ret = TRUE;
+ PALETTE_UnlockPalette(palGDI);
+ return Ret;
+}
+
+
+/* EOF */
--- /dev/null
- INT mapMode, graphicsMode;
- SIZE ptViewportExt, ptWindowExt;
- POINTL ptViewportOrg, ptWindowOrg;
+/*
+ * Graphics paths (BeginPath, EndPath etc.)
+ *
+ * Copyright 1997, 1998 Martin Boehme
+ * 1999 Huw D M Davies
+ * Copyright 2005 Dmitry Timoshkov
+ *
+ * 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
+ */
+/*
+ *
+ * Addaped for the use in ReactOS.
+ *
+ */
+/*
+ * PROJECT: ReactOS win32 kernel mode subsystem
+ * LICENSE: GPL - See COPYING in the top level directory
+ * FILE: subsystems/win32/win32k/objects/path.c
+ * PURPOSE: Path support
+ * PROGRAMMER:
+ */
+
+#include <win32k.h>
+#include "math.h"
+
+#define NDEBUG
+#include <debug.h>
+
+#define NUM_ENTRIES_INITIAL 16 /* Initial size of points / flags arrays */
+#define GROW_FACTOR_NUMER 2 /* Numerator of grow factor for the array */
+#define GROW_FACTOR_DENOM 1 /* Denominator of grow factor */
+
+BOOL FASTCALL PATH_AddEntry (PPATH pPath, const POINT *pPoint, BYTE flags);
+BOOL FASTCALL PATH_AddFlatBezier (PPATH pPath, POINT *pt, BOOL closed);
+BOOL FASTCALL PATH_DoArcPart (PPATH pPath, FLOAT_POINT corners[], double angleStart, double angleEnd, BYTE startEntryType);
+BOOL FASTCALL PATH_FillPath( PDC dc, PPATH pPath );
+BOOL FASTCALL PATH_FlattenPath (PPATH pPath);
+VOID FASTCALL PATH_NormalizePoint (FLOAT_POINT corners[], const FLOAT_POINT *pPoint, double *pX, double *pY);
+
+BOOL FASTCALL PATH_ReserveEntries (PPATH pPath, INT numEntries);
+VOID FASTCALL PATH_ScaleNormalizedPoint (FLOAT_POINT corners[], double x, double y, POINT *pPoint);
+BOOL FASTCALL PATH_StrokePath(DC *dc, PPATH pPath);
+BOOL PATH_CheckCorners(DC *dc, POINT corners[], INT x1, INT y1, INT x2, INT y2);
+
+VOID FASTCALL IntGetCurrentPositionEx(PDC dc, LPPOINT pt);
+
+/***********************************************************************
+ * Internal functions
+ */
+
+BOOL
+FASTCALL
+PATH_Delete(HPATH hPath)
+{
+ PPATH pPath;
+ if (!hPath) return FALSE;
+ pPath = PATH_LockPath( hPath );
+ if (!pPath) return FALSE;
+ PATH_DestroyGdiPath( pPath );
+ PATH_UnlockPath( pPath );
+ PATH_FreeExtPathByHandle(hPath);
+ return TRUE;
+}
+
+
+VOID
+FASTCALL
+IntGdiCloseFigure(PPATH pPath)
+{
+ ASSERT(pPath->state == PATH_Open);
+
+ // FIXME: Shouldn't we draw a line to the beginning of the figure?
+ // Set PT_CLOSEFIGURE on the last entry and start a new stroke
+ if(pPath->numEntriesUsed)
+ {
+ pPath->pFlags[pPath->numEntriesUsed-1]|=PT_CLOSEFIGURE;
+ pPath->newStroke=TRUE;
+ }
+}
+
+/* PATH_FillPath
+ *
+ *
+ */
+BOOL
+FASTCALL
+PATH_FillPath( PDC dc, PPATH pPath )
+{
- mapMode = pdcattr->iMapMode;
- ptViewportExt = pdcattr->szlViewportExt;
- ptViewportOrg = pdcattr->ptlViewportOrg;
- ptWindowExt = pdcattr->szlWindowExt;
- ptWindowOrg = pdcattr->ptlWindowOrg;
++ //INT mapMode, graphicsMode;
++ //SIZE ptViewportExt, ptWindowExt;
++ //POINTL ptViewportOrg, ptWindowOrg;
+ XFORM xform;
+ HRGN hrgn;
+ PDC_ATTR pdcattr = dc->pdcattr;
+
+ if( pPath->state != PATH_Closed )
+ {
+ SetLastWin32Error(ERROR_CAN_NOT_COMPLETE);
+ return FALSE;
+ }
+
+ if( PATH_PathToRegion( pPath, pdcattr->jFillMode, &hrgn ))
+ {
+ /* Since PaintRgn interprets the region as being in logical coordinates
+ * but the points we store for the path are already in device
+ * coordinates, we have to set the mapping mode to MM_TEXT temporarily.
+ * Using SaveDC to save information about the mapping mode / world
+ * transform would be easier but would require more overhead, especially
+ * now that SaveDC saves the current path.
+ */
+
+ /* Save the information about the old mapping mode */
- graphicsMode = pdcattr->iGraphicsMode;
++ //mapMode = pdcattr->iMapMode;
++ //ptViewportExt = pdcattr->szlViewportExt;
++ //ptViewportOrg = pdcattr->ptlViewportOrg;
++ //ptWindowExt = pdcattr->szlWindowExt;
++ //ptWindowOrg = pdcattr->ptlWindowOrg;
+
+ /* Save world transform
+ * NB: The Windows documentation on world transforms would lead one to
+ * believe that this has to be done only in GM_ADVANCED; however, my
+ * tests show that resetting the graphics mode to GM_COMPATIBLE does
+ * not reset the world transform.
+ */
+ MatrixS2XForm(&xform, &dc->dclevel.mxWorldToPage);
+
+ /* Set MM_TEXT */
+// IntGdiSetMapMode( dc, MM_TEXT );
+// pdcattr->ptlViewportOrg.x = 0;
+// pdcattr->ptlViewportOrg.y = 0;
+// pdcattr->ptlWindowOrg.x = 0;
+// pdcattr->ptlWindowOrg.y = 0;
+
- graphicsMode = pdcattr->iGraphicsMode;
++ // graphicsMode = pdcattr->iGraphicsMode;
+// pdcattr->iGraphicsMode = GM_ADVANCED;
+// IntGdiModifyWorldTransform( dc, &xform, MWT_IDENTITY );
+// pdcattr->iGraphicsMode = graphicsMode;
+
+ /* Paint the region */
+ IntGdiPaintRgn( dc, hrgn );
+ GreDeleteObject( hrgn );
+ /* Restore the old mapping mode */
+// IntGdiSetMapMode( dc, mapMode );
+// pdcattr->szlViewportExt = ptViewportExt;
+// pdcattr->ptlViewportOrg = ptViewportOrg;
+// pdcattr->szlWindowExt = ptWindowExt;
+// pdcattr->ptlWindowOrg = ptWindowOrg;
+
+ /* Go to GM_ADVANCED temporarily to restore the world transform */
++ //graphicsMode = pdcattr->iGraphicsMode;
+// pdcattr->iGraphicsMode = GM_ADVANCED;
+// IntGdiModifyWorldTransform( dc, &xform, MWT_MAX+1 );
+// pdcattr->iGraphicsMode = graphicsMode;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/* PATH_InitGdiPath
+ *
+ * Initializes the GdiPath structure.
+ */
+VOID
+FASTCALL
+PATH_InitGdiPath ( PPATH pPath )
+{
+ ASSERT(pPath!=NULL);
+
+ pPath->state=PATH_Null;
+ pPath->pPoints=NULL;
+ pPath->pFlags=NULL;
+ pPath->numEntriesUsed=0;
+ pPath->numEntriesAllocated=0;
+}
+
+/* PATH_DestroyGdiPath
+ *
+ * Destroys a GdiPath structure (frees the memory in the arrays).
+ */
+VOID
+FASTCALL
+PATH_DestroyGdiPath ( PPATH pPath )
+{
+ ASSERT(pPath!=NULL);
+
+ if (pPath->pPoints) ExFreePoolWithTag(pPath->pPoints, TAG_PATH);
+ if (pPath->pFlags) ExFreePoolWithTag(pPath->pFlags, TAG_PATH);
+}
+
+/* PATH_AssignGdiPath
+ *
+ * Copies the GdiPath structure "pPathSrc" to "pPathDest". A deep copy is
+ * performed, i.e. the contents of the pPoints and pFlags arrays are copied,
+ * not just the pointers. Since this means that the arrays in pPathDest may
+ * need to be resized, pPathDest should have been initialized using
+ * PATH_InitGdiPath (in C++, this function would be an assignment operator,
+ * not a copy constructor).
+ * Returns TRUE if successful, else FALSE.
+ */
+BOOL
+FASTCALL
+PATH_AssignGdiPath ( PPATH pPathDest, const PPATH pPathSrc )
+{
+ ASSERT(pPathDest!=NULL && pPathSrc!=NULL);
+
+ /* Make sure destination arrays are big enough */
+ if ( !PATH_ReserveEntries(pPathDest, pPathSrc->numEntriesUsed) )
+ return FALSE;
+
+ /* Perform the copy operation */
+ memcpy(pPathDest->pPoints, pPathSrc->pPoints,
+ sizeof(POINT)*pPathSrc->numEntriesUsed);
+ memcpy(pPathDest->pFlags, pPathSrc->pFlags,
+ sizeof(BYTE)*pPathSrc->numEntriesUsed);
+
+ pPathDest->state=pPathSrc->state;
+ pPathDest->numEntriesUsed=pPathSrc->numEntriesUsed;
+ pPathDest->newStroke=pPathSrc->newStroke;
+ return TRUE;
+}
+
+/* PATH_MoveTo
+ *
+ * Should be called when a MoveTo is performed on a DC that has an
+ * open path. This starts a new stroke. Returns TRUE if successful, else
+ * FALSE.
+ */
+BOOL
+FASTCALL
+PATH_MoveTo ( PDC dc )
+{
+ PPATH pPath = PATH_LockPath( dc->dclevel.hPath );
+ if (!pPath) return FALSE;
+
+ /* Check that path is open */
+ if ( pPath->state != PATH_Open )
+ {
+ PATH_UnlockPath( pPath );
+ /* FIXME: Do we have to call SetLastError? */
+ return FALSE;
+ }
+ /* Start a new stroke */
+ pPath->newStroke = TRUE;
+ PATH_UnlockPath( pPath );
+ return TRUE;
+}
+
+/* PATH_LineTo
+ *
+ * Should be called when a LineTo is performed on a DC that has an
+ * open path. This adds a PT_LINETO entry to the path (and possibly
+ * a PT_MOVETO entry, if this is the first LineTo in a stroke).
+ * Returns TRUE if successful, else FALSE.
+ */
+BOOL
+FASTCALL
+PATH_LineTo ( PDC dc, INT x, INT y )
+{
+ BOOL Ret;
+ PPATH pPath;
+ POINT point, pointCurPos;
+
+ pPath = PATH_LockPath( dc->dclevel.hPath );
+ if (!pPath) return FALSE;
+
+ /* Check that path is open */
+ if ( pPath->state != PATH_Open )
+ {
+ PATH_UnlockPath( pPath );
+ return FALSE;
+ }
+
+ /* Convert point to device coordinates */
+ point.x=x;
+ point.y=y;
+ CoordLPtoDP ( dc, &point );
+
+ /* Add a PT_MOVETO if necessary */
+ if ( pPath->newStroke )
+ {
+ pPath->newStroke = FALSE;
+ IntGetCurrentPositionEx ( dc, &pointCurPos );
+ CoordLPtoDP ( dc, &pointCurPos );
+ if ( !PATH_AddEntry(pPath, &pointCurPos, PT_MOVETO) )
+ {
+ PATH_UnlockPath( pPath );
+ return FALSE;
+ }
+ }
+
+ /* Add a PT_LINETO entry */
+ Ret = PATH_AddEntry(pPath, &point, PT_LINETO);
+ PATH_UnlockPath( pPath );
+ return Ret;
+}
+
+/* PATH_Rectangle
+ *
+ * Should be called when a call to Rectangle is performed on a DC that has
+ * an open path. Returns TRUE if successful, else FALSE.
+ */
+BOOL
+FASTCALL
+PATH_Rectangle ( PDC dc, INT x1, INT y1, INT x2, INT y2 )
+{
+ PPATH pPath;
+ POINT corners[2], pointTemp;
+ INT temp;
+
+ pPath = PATH_LockPath( dc->dclevel.hPath );
+ if (!pPath) return FALSE;
+
+ /* Check that path is open */
+ if ( pPath->state != PATH_Open )
+ {
+ PATH_UnlockPath( pPath );
+ return FALSE;
+ }
+
+ /* Convert points to device coordinates */
+ corners[0].x=x1;
+ corners[0].y=y1;
+ corners[1].x=x2;
+ corners[1].y=y2;
+ IntLPtoDP ( dc, corners, 2 );
+
+ /* Make sure first corner is top left and second corner is bottom right */
+ if ( corners[0].x > corners[1].x )
+ {
+ temp=corners[0].x;
+ corners[0].x=corners[1].x;
+ corners[1].x=temp;
+ }
+ if ( corners[0].y > corners[1].y )
+ {
+ temp=corners[0].y;
+ corners[0].y=corners[1].y;
+ corners[1].y=temp;
+ }
+
+ /* In GM_COMPATIBLE, don't include bottom and right edges */
+ if (dc->pdcattr->iGraphicsMode == GM_COMPATIBLE)
+ {
+ corners[1].x--;
+ corners[1].y--;
+ }
+
+ /* Close any previous figure */
+ IntGdiCloseFigure(pPath);
+
+ /* Add four points to the path */
+ pointTemp.x=corners[1].x;
+ pointTemp.y=corners[0].y;
+ if ( !PATH_AddEntry(pPath, &pointTemp, PT_MOVETO) )
+ {
+ PATH_UnlockPath( pPath );
+ return FALSE;
+ }
+ if ( !PATH_AddEntry(pPath, corners, PT_LINETO) )
+ {
+ PATH_UnlockPath( pPath );
+ return FALSE;
+ }
+ pointTemp.x=corners[0].x;
+ pointTemp.y=corners[1].y;
+ if ( !PATH_AddEntry(pPath, &pointTemp, PT_LINETO) )
+ {
+ PATH_UnlockPath( pPath );
+ return FALSE;
+ }
+ if ( !PATH_AddEntry(pPath, corners+1, PT_LINETO) )
+ {
+ PATH_UnlockPath( pPath );
+ return FALSE;
+ }
+
+ /* Close the rectangle figure */
+ IntGdiCloseFigure(pPath) ;
+ PATH_UnlockPath( pPath );
+ return TRUE;
+}
+
+/* PATH_RoundRect
+ *
+ * Should be called when a call to RoundRect is performed on a DC that has
+ * an open path. Returns TRUE if successful, else FALSE.
+ *
+ * FIXME: it adds the same entries to the path as windows does, but there
+ * is an error in the bezier drawing code so that there are small pixel-size
+ * gaps when the resulting path is drawn by StrokePath()
+ */
+BOOL FASTCALL PATH_RoundRect(DC *dc, INT x1, INT y1, INT x2, INT y2, INT ell_width, INT ell_height)
+{
+ PPATH pPath;
+ POINT corners[2], pointTemp;
+ FLOAT_POINT ellCorners[2];
+
+ pPath = PATH_LockPath( dc->dclevel.hPath );
+ if (!pPath) return FALSE;
+
+ /* Check that path is open */
+ if(pPath->state!=PATH_Open)
+ {
+ PATH_UnlockPath( pPath );
+ return FALSE;
+ }
+
+ if(!PATH_CheckCorners(dc,corners,x1,y1,x2,y2))
+ {
+ PATH_UnlockPath( pPath );
+ return FALSE;
+ }
+
+ /* Add points to the roundrect path */
+ ellCorners[0].x = corners[1].x-ell_width;
+ ellCorners[0].y = corners[0].y;
+ ellCorners[1].x = corners[1].x;
+ ellCorners[1].y = corners[0].y+ell_height;
+ if(!PATH_DoArcPart(pPath, ellCorners, 0, -M_PI_2, PT_MOVETO))
+ {
+ PATH_UnlockPath( pPath );
+ return FALSE;
+ }
+ pointTemp.x = corners[0].x+ell_width/2;
+ pointTemp.y = corners[0].y;
+ if(!PATH_AddEntry(pPath, &pointTemp, PT_LINETO))
+ {
+ PATH_UnlockPath( pPath );
+ return FALSE;
+ }
+ ellCorners[0].x = corners[0].x;
+ ellCorners[1].x = corners[0].x+ell_width;
+ if(!PATH_DoArcPart(pPath, ellCorners, -M_PI_2, -M_PI, FALSE))
+ {
+ PATH_UnlockPath( pPath );
+ return FALSE;
+ }
+ pointTemp.x = corners[0].x;
+ pointTemp.y = corners[1].y-ell_height/2;
+ if(!PATH_AddEntry(pPath, &pointTemp, PT_LINETO))
+ {
+ PATH_UnlockPath( pPath );
+ return FALSE;
+ }
+ ellCorners[0].y = corners[1].y-ell_height;
+ ellCorners[1].y = corners[1].y;
+ if(!PATH_DoArcPart(pPath, ellCorners, M_PI, M_PI_2, FALSE))
+ {
+ PATH_UnlockPath( pPath );
+ return FALSE;
+ }
+ pointTemp.x = corners[1].x-ell_width/2;
+ pointTemp.y = corners[1].y;
+ if(!PATH_AddEntry(pPath, &pointTemp, PT_LINETO))
+ {
+ PATH_UnlockPath( pPath );
+ return FALSE;
+ }
+ ellCorners[0].x = corners[1].x-ell_width;
+ ellCorners[1].x = corners[1].x;
+ if(!PATH_DoArcPart(pPath, ellCorners, M_PI_2, 0, FALSE))
+ {
+ PATH_UnlockPath( pPath );
+ return FALSE;
+ }
+
+ IntGdiCloseFigure(pPath);
+ PATH_UnlockPath( pPath );
+ return TRUE;
+}
+
+/* PATH_Ellipse
+ *
+ * Should be called when a call to Ellipse is performed on a DC that has
+ * an open path. This adds four Bezier splines representing the ellipse
+ * to the path. Returns TRUE if successful, else FALSE.
+ */
+BOOL
+FASTCALL
+PATH_Ellipse ( PDC dc, INT x1, INT y1, INT x2, INT y2 )
+{
+ PPATH pPath;
+ /* TODO: This should probably be revised to call PATH_AngleArc */
+ /* (once it exists) */
+ BOOL Ret = PATH_Arc ( dc, x1, y1, x2, y2, x1, (y1+y2)/2, x1, (y1+y2)/2, GdiTypeArc );
+ if (Ret)
+ {
+ pPath = PATH_LockPath( dc->dclevel.hPath );
+ if (!pPath) return FALSE;
+ IntGdiCloseFigure(pPath);
+ PATH_UnlockPath( pPath );
+ }
+ return Ret;
+}
+
+/* PATH_Arc
+ *
+ * Should be called when a call to Arc is performed on a DC that has
+ * an open path. This adds up to five Bezier splines representing the arc
+ * to the path. When 'lines' is 1, we add 1 extra line to get a chord,
+ * when 'lines' is 2, we add 2 extra lines to get a pie, and when 'lines' is
+ * -1 we add 1 extra line from the current DC position to the starting position
+ * of the arc before drawing the arc itself (arcto). Returns TRUE if successful,
+ * else FALSE.
+ */
+BOOL
+FASTCALL
+PATH_Arc ( PDC dc, INT x1, INT y1, INT x2, INT y2,
+ INT xStart, INT yStart, INT xEnd, INT yEnd, INT lines)
+{
+ double angleStart, angleEnd, angleStartQuadrant, angleEndQuadrant=0.0;
+ /* Initialize angleEndQuadrant to silence gcc's warning */
+ double x, y;
+ FLOAT_POINT corners[2], pointStart, pointEnd;
+ POINT centre, pointCurPos;
+ BOOL start, end, Ret = TRUE;
+ INT temp;
+ BOOL clockwise;
+ PPATH pPath;
+
+ /* FIXME: This function should check for all possible error returns */
+ /* FIXME: Do we have to respect newStroke? */
+
+ ASSERT ( dc );
+
+ pPath = PATH_LockPath( dc->dclevel.hPath );
+ if (!pPath) return FALSE;
+
+ clockwise = ((dc->dclevel.flPath & DCPATH_CLOCKWISE) != 0);
+
+ /* Check that path is open */
+ if ( pPath->state != PATH_Open )
+ {
+ Ret = FALSE;
+ goto ArcExit;
+ }
+
+ /* Check for zero height / width */
+ /* FIXME: Only in GM_COMPATIBLE? */
+ if ( x1==x2 || y1==y2 )
+ {
+ Ret = TRUE;
+ goto ArcExit;
+ }
+ /* Convert points to device coordinates */
+ corners[0].x=(FLOAT)x1;
+ corners[0].y=(FLOAT)y1;
+ corners[1].x=(FLOAT)x2;
+ corners[1].y=(FLOAT)y2;
+ pointStart.x=(FLOAT)xStart;
+ pointStart.y=(FLOAT)yStart;
+ pointEnd.x=(FLOAT)xEnd;
+ pointEnd.y=(FLOAT)yEnd;
+ INTERNAL_LPTODP_FLOAT(dc, corners);
+ INTERNAL_LPTODP_FLOAT(dc, corners+1);
+ INTERNAL_LPTODP_FLOAT(dc, &pointStart);
+ INTERNAL_LPTODP_FLOAT(dc, &pointEnd);
+
+ /* Make sure first corner is top left and second corner is bottom right */
+ if ( corners[0].x > corners[1].x )
+ {
+ temp=corners[0].x;
+ corners[0].x=corners[1].x;
+ corners[1].x=temp;
+ }
+ if ( corners[0].y > corners[1].y )
+ {
+ temp=corners[0].y;
+ corners[0].y=corners[1].y;
+ corners[1].y=temp;
+ }
+
+ /* Compute start and end angle */
+ PATH_NormalizePoint(corners, &pointStart, &x, &y);
+ angleStart=atan2(y, x);
+ PATH_NormalizePoint(corners, &pointEnd, &x, &y);
+ angleEnd=atan2(y, x);
+
+ /* Make sure the end angle is "on the right side" of the start angle */
+ if ( clockwise )
+ {
+ if ( angleEnd <= angleStart )
+ {
+ angleEnd+=2*M_PI;
+ ASSERT(angleEnd>=angleStart);
+ }
+ }
+ else
+ {
+ if(angleEnd>=angleStart)
+ {
+ angleEnd-=2*M_PI;
+ ASSERT(angleEnd<=angleStart);
+ }
+ }
+
+ /* In GM_COMPATIBLE, don't include bottom and right edges */
+ if (dc->pdcattr->iGraphicsMode == GM_COMPATIBLE )
+ {
+ corners[1].x--;
+ corners[1].y--;
+ }
+
+ /* arcto: Add a PT_MOVETO only if this is the first entry in a stroke */
+ if(lines==GdiTypeArcTo && pPath->newStroke) // -1
+ {
+ pPath->newStroke=FALSE;
+ IntGetCurrentPositionEx ( dc, &pointCurPos );
+ CoordLPtoDP(dc, &pointCurPos);
+ if(!PATH_AddEntry(pPath, &pointCurPos, PT_MOVETO))
+ {
+ Ret = FALSE;
+ goto ArcExit;
+ }
+ }
+
+ /* Add the arc to the path with one Bezier spline per quadrant that the
+ * arc spans */
+ start=TRUE;
+ end=FALSE;
+ do
+ {
+ /* Determine the start and end angles for this quadrant */
+ if(start)
+ {
+ angleStartQuadrant=angleStart;
+ if ( clockwise )
+ angleEndQuadrant=(floor(angleStart/M_PI_2)+1.0)*M_PI_2;
+ else
+ angleEndQuadrant=(ceil(angleStart/M_PI_2)-1.0)*M_PI_2;
+ }
+ else
+ {
+ angleStartQuadrant=angleEndQuadrant;
+ if ( clockwise )
+ angleEndQuadrant+=M_PI_2;
+ else
+ angleEndQuadrant-=M_PI_2;
+ }
+
+ /* Have we reached the last part of the arc? */
+ if ( (clockwise && angleEnd<angleEndQuadrant)
+ || (!clockwise && angleEnd>angleEndQuadrant)
+ )
+ {
+ /* Adjust the end angle for this quadrant */
+ angleEndQuadrant = angleEnd;
+ end = TRUE;
+ }
+
+ /* Add the Bezier spline to the path */
+ PATH_DoArcPart ( pPath, corners, angleStartQuadrant, angleEndQuadrant,
+ start ? (lines==GdiTypeArcTo ? PT_LINETO : PT_MOVETO) : FALSE ); // -1
+ start = FALSE;
+ } while(!end);
+
+ /* chord: close figure. pie: add line and close figure */
+ if (lines==GdiTypeChord) // 1
+ {
+ IntGdiCloseFigure(pPath);
+ }
+ else if (lines==GdiTypePie) // 2
+ {
+ centre.x = (corners[0].x+corners[1].x)/2;
+ centre.y = (corners[0].y+corners[1].y)/2;
+ if(!PATH_AddEntry(pPath, ¢re, PT_LINETO | PT_CLOSEFIGURE))
+ Ret = FALSE;
+ }
+ArcExit:
+ PATH_UnlockPath( pPath );
+ return Ret;
+}
+
+BOOL
+FASTCALL
+PATH_PolyBezierTo ( PDC dc, const POINT *pts, DWORD cbPoints )
+{
+ POINT pt;
+ ULONG i;
+ PPATH pPath;
+
+ ASSERT ( dc );
+ ASSERT ( pts );
+ ASSERT ( cbPoints );
+
+ pPath = PATH_LockPath( dc->dclevel.hPath );
+ if (!pPath) return FALSE;
+
+ /* Check that path is open */
+ if ( pPath->state != PATH_Open )
+ {
+ PATH_UnlockPath( pPath );
+ return FALSE;
+ }
+
+ /* Add a PT_MOVETO if necessary */
+ if ( pPath->newStroke )
+ {
+ pPath->newStroke=FALSE;
+ IntGetCurrentPositionEx ( dc, &pt );
+ CoordLPtoDP ( dc, &pt );
+ if ( !PATH_AddEntry(pPath, &pt, PT_MOVETO) )
+ {
+ PATH_UnlockPath( pPath );
+ return FALSE;
+ }
+ }
+
+ for(i = 0; i < cbPoints; i++)
+ {
+ pt = pts[i];
+ CoordLPtoDP ( dc, &pt );
+ PATH_AddEntry(pPath, &pt, PT_BEZIERTO);
+ }
+ PATH_UnlockPath( pPath );
+ return TRUE;
+}
+
+BOOL
+FASTCALL
+PATH_PolyBezier ( PDC dc, const POINT *pts, DWORD cbPoints )
+{
+ POINT pt;
+ ULONG i;
+ PPATH pPath;
+
+ ASSERT ( dc );
+ ASSERT ( pts );
+ ASSERT ( cbPoints );
+
+ pPath = PATH_LockPath( dc->dclevel.hPath );
+ if (!pPath) return FALSE;
+
+ /* Check that path is open */
+ if ( pPath->state != PATH_Open )
+ {
+ PATH_UnlockPath( pPath );
+ return FALSE;
+ }
+
+ for ( i = 0; i < cbPoints; i++ )
+ {
+ pt = pts[i];
+ CoordLPtoDP ( dc, &pt );
+ PATH_AddEntry ( pPath, &pt, (i == 0) ? PT_MOVETO : PT_BEZIERTO );
+ }
+ PATH_UnlockPath( pPath );
+ return TRUE;
+}
+
+BOOL
+FASTCALL
+PATH_Polyline ( PDC dc, const POINT *pts, DWORD cbPoints )
+{
+ POINT pt;
+ ULONG i;
+ PPATH pPath;
+
+ ASSERT ( dc );
+ ASSERT ( pts );
+ ASSERT ( cbPoints );
+
+ pPath = PATH_LockPath( dc->dclevel.hPath );
+ if (!pPath) return FALSE;
+
+ /* Check that path is open */
+ if ( pPath->state != PATH_Open )
+ {
+ PATH_UnlockPath( pPath );
+ return FALSE;
+ }
+ for ( i = 0; i < cbPoints; i++ )
+ {
+ pt = pts[i];
+ CoordLPtoDP ( dc, &pt );
+ PATH_AddEntry(pPath, &pt, (i == 0) ? PT_MOVETO : PT_LINETO);
+ }
+ PATH_UnlockPath( pPath );
+ return TRUE;
+}
+
+BOOL
+FASTCALL
+PATH_PolylineTo ( PDC dc, const POINT *pts, DWORD cbPoints )
+{
+ POINT pt;
+ ULONG i;
+ PPATH pPath;
+
+ ASSERT ( dc );
+ ASSERT ( pts );
+ ASSERT ( cbPoints );
+
+ pPath = PATH_LockPath( dc->dclevel.hPath );
+ if (!pPath) return FALSE;
+
+ /* Check that path is open */
+ if ( pPath->state != PATH_Open )
+ {
+ PATH_UnlockPath( pPath );
+ return FALSE;
+ }
+
+ /* Add a PT_MOVETO if necessary */
+ if ( pPath->newStroke )
+ {
+ pPath->newStroke = FALSE;
+ IntGetCurrentPositionEx ( dc, &pt );
+ CoordLPtoDP ( dc, &pt );
+ if ( !PATH_AddEntry(pPath, &pt, PT_MOVETO) )
+ {
+ PATH_UnlockPath( pPath );
+ return FALSE;
+ }
+ }
+
+ for(i = 0; i < cbPoints; i++)
+ {
+ pt = pts[i];
+ CoordLPtoDP ( dc, &pt );
+ PATH_AddEntry(pPath, &pt, PT_LINETO);
+ }
+ PATH_UnlockPath( pPath );
+ return TRUE;
+}
+
+
+BOOL
+FASTCALL
+PATH_Polygon ( PDC dc, const POINT *pts, DWORD cbPoints )
+{
+ POINT pt;
+ ULONG i;
+ PPATH pPath;
+
+ ASSERT ( dc );
+ ASSERT ( pts );
+
+ pPath = PATH_LockPath( dc->dclevel.hPath );
+ if (!pPath) return FALSE;
+
+ /* Check that path is open */
+ if ( pPath->state != PATH_Open )
+ {
+ PATH_UnlockPath( pPath );
+ return FALSE;
+ }
+
+ for(i = 0; i < cbPoints; i++)
+ {
+ pt = pts[i];
+ CoordLPtoDP ( dc, &pt );
+ PATH_AddEntry(pPath, &pt, (i == 0) ? PT_MOVETO :
+ ((i == cbPoints-1) ? PT_LINETO | PT_CLOSEFIGURE :
+ PT_LINETO));
+ }
+ PATH_UnlockPath( pPath );
+ return TRUE;
+}
+
+BOOL
+FASTCALL
+PATH_PolyPolygon ( PDC dc, const POINT* pts, const INT* counts, UINT polygons )
+{
+ POINT pt, startpt;
+ ULONG poly, point, i;
+ PPATH pPath;
+
+ ASSERT ( dc );
+ ASSERT ( pts );
+ ASSERT ( counts );
+ ASSERT ( polygons );
+
+ pPath = PATH_LockPath( dc->dclevel.hPath );
+ if (!pPath) return FALSE;
+
+ /* Check that path is open */
+ if ( pPath->state != PATH_Open )
+ {
+ PATH_UnlockPath( pPath );
+ return FALSE;
+ }
+
+ for(i = 0, poly = 0; poly < polygons; poly++)
+ {
+ for(point = 0; point < (ULONG) counts[poly]; point++, i++)
+ {
+ pt = pts[i];
+ CoordLPtoDP ( dc, &pt );
+ if(point == 0) startpt = pt;
+ PATH_AddEntry(pPath, &pt, (point == 0) ? PT_MOVETO : PT_LINETO);
+ }
+ /* win98 adds an extra line to close the figure for some reason */
+ PATH_AddEntry(pPath, &startpt, PT_LINETO | PT_CLOSEFIGURE);
+ }
+ PATH_UnlockPath( pPath );
+ return TRUE;
+}
+
+BOOL
+FASTCALL
+PATH_PolyPolyline ( PDC dc, const POINT* pts, const DWORD* counts, DWORD polylines )
+{
+ POINT pt;
+ ULONG poly, point, i;
+ PPATH pPath;
+
+ ASSERT ( dc );
+ ASSERT ( pts );
+ ASSERT ( counts );
+ ASSERT ( polylines );
+
+ pPath = PATH_LockPath( dc->dclevel.hPath );
+ if (!pPath) return FALSE;
+
+ /* Check that path is open */
+ if ( pPath->state != PATH_Open )
+ {
+ PATH_UnlockPath( pPath );
+ return FALSE;
+ }
+
+ for(i = 0, poly = 0; poly < polylines; poly++)
+ {
+ for(point = 0; point < counts[poly]; point++, i++)
+ {
+ pt = pts[i];
+ CoordLPtoDP ( dc, &pt );
+ PATH_AddEntry(pPath, &pt, (point == 0) ? PT_MOVETO : PT_LINETO);
+ }
+ }
+ PATH_UnlockPath( pPath );
+ return TRUE;
+}
+
+
+/* PATH_CheckCorners
+ *
+ * Helper function for PATH_RoundRect() and PATH_Rectangle()
+ */
+BOOL PATH_CheckCorners(DC *dc, POINT corners[], INT x1, INT y1, INT x2, INT y2)
+{
+ INT temp;
+ PDC_ATTR pdcattr = dc->pdcattr;
+
+ /* Convert points to device coordinates */
+ corners[0].x=x1;
+ corners[0].y=y1;
+ corners[1].x=x2;
+ corners[1].y=y2;
+ CoordLPtoDP(dc, &corners[0]);
+ CoordLPtoDP(dc, &corners[1]);
+
+ /* Make sure first corner is top left and second corner is bottom right */
+ if(corners[0].x>corners[1].x)
+ {
+ temp=corners[0].x;
+ corners[0].x=corners[1].x;
+ corners[1].x=temp;
+ }
+ if(corners[0].y>corners[1].y)
+ {
+ temp=corners[0].y;
+ corners[0].y=corners[1].y;
+ corners[1].y=temp;
+ }
+
+ /* In GM_COMPATIBLE, don't include bottom and right edges */
+ if(pdcattr->iGraphicsMode==GM_COMPATIBLE)
+ {
+ corners[1].x--;
+ corners[1].y--;
+ }
+
+ return TRUE;
+}
+
+
+/* PATH_AddFlatBezier
+ *
+ */
+BOOL
+FASTCALL
+PATH_AddFlatBezier ( PPATH pPath, POINT *pt, BOOL closed )
+{
+ POINT *pts;
+ INT no, i;
+
+ pts = GDI_Bezier( pt, 4, &no );
+ if ( !pts ) return FALSE;
+
+ for(i = 1; i < no; i++)
+ PATH_AddEntry(pPath, &pts[i], (i == no-1 && closed) ? PT_LINETO | PT_CLOSEFIGURE : PT_LINETO);
+
+ ExFreePoolWithTag(pts, TAG_BEZIER);
+ return TRUE;
+}
+
+/* PATH_FlattenPath
+ *
+ * Replaces Beziers with line segments
+ *
+ */
+BOOL
+FASTCALL
+PATH_FlattenPath(PPATH pPath)
+{
+ PATH newPath;
+ INT srcpt;
+
+ RtlZeroMemory(&newPath, sizeof(newPath));
+ newPath.state = PATH_Open;
+ for(srcpt = 0; srcpt < pPath->numEntriesUsed; srcpt++) {
+ switch(pPath->pFlags[srcpt] & ~PT_CLOSEFIGURE) {
+ case PT_MOVETO:
+ case PT_LINETO:
+ PATH_AddEntry(&newPath, &pPath->pPoints[srcpt], pPath->pFlags[srcpt]);
+ break;
+ case PT_BEZIERTO:
+ PATH_AddFlatBezier(&newPath, &pPath->pPoints[srcpt-1], pPath->pFlags[srcpt+2] & PT_CLOSEFIGURE);
+ srcpt += 2;
+ break;
+ }
+ }
+ newPath.state = PATH_Closed;
+ PATH_AssignGdiPath(pPath, &newPath);
+ PATH_EmptyPath(&newPath);
+ return TRUE;
+}
+
+
+/* PATH_PathToRegion
+ *
+ * Creates a region from the specified path using the specified polygon
+ * filling mode. The path is left unchanged. A handle to the region that
+ * was created is stored in *pHrgn. If successful, TRUE is returned; if an
+ * error occurs, SetLastError is called with the appropriate value and
+ * FALSE is returned.
+ */
+BOOL
+FASTCALL
+PATH_PathToRegion ( PPATH pPath, INT nPolyFillMode, HRGN *pHrgn )
+{
+ int numStrokes, iStroke, i;
+ PULONG pNumPointsInStroke;
+ HRGN hrgn = 0;
+
+ ASSERT(pPath!=NULL);
+ ASSERT(pHrgn!=NULL);
+
+ PATH_FlattenPath ( pPath );
+
+ /* FIXME: What happens when number of points is zero? */
+
+ /* First pass: Find out how many strokes there are in the path */
+ /* FIXME: We could eliminate this with some bookkeeping in GdiPath */
+ numStrokes=0;
+ for(i=0; i<pPath->numEntriesUsed; i++)
+ if((pPath->pFlags[i] & ~PT_CLOSEFIGURE) == PT_MOVETO)
+ numStrokes++;
+
+ /* Allocate memory for number-of-points-in-stroke array */
+ pNumPointsInStroke = ExAllocatePoolWithTag(PagedPool, sizeof(ULONG) * numStrokes, TAG_PATH);
+ if(!pNumPointsInStroke)
+ {
+ SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
+ return FALSE;
+ }
+
+ /* Second pass: remember number of points in each polygon */
+ iStroke=-1; /* Will get incremented to 0 at beginning of first stroke */
+ for(i=0; i<pPath->numEntriesUsed; i++)
+ {
+ /* Is this the beginning of a new stroke? */
+ if((pPath->pFlags[i] & ~PT_CLOSEFIGURE) == PT_MOVETO)
+ {
+ iStroke++;
+ pNumPointsInStroke[iStroke]=0;
+ }
+
+ pNumPointsInStroke[iStroke]++;
+ }
+
+ /* Create a region from the strokes */
+ hrgn = IntCreatePolyPolygonRgn( pPath->pPoints,
+ pNumPointsInStroke,
+ numStrokes,
+ nPolyFillMode);
+ if(hrgn==(HRGN)0)
+ {
+ SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
+ return FALSE;
+ }
+
+ /* Free memory for number-of-points-in-stroke array */
+ ExFreePoolWithTag(pNumPointsInStroke, TAG_PATH);
+
+ /* Success! */
+ *pHrgn=hrgn;
+ return TRUE;
+}
+
+/* PATH_EmptyPath
+ *
+ * Removes all entries from the path and sets the path state to PATH_Null.
+ */
+VOID
+FASTCALL
+PATH_EmptyPath ( PPATH pPath )
+{
+ ASSERT(pPath!=NULL);
+
+ pPath->state=PATH_Null;
+ pPath->numEntriesUsed=0;
+}
+
+/* PATH_AddEntry
+ *
+ * Adds an entry to the path. For "flags", pass either PT_MOVETO, PT_LINETO
+ * or PT_BEZIERTO, optionally ORed with PT_CLOSEFIGURE. Returns TRUE if
+ * successful, FALSE otherwise (e.g. if not enough memory was available).
+ */
+BOOL
+FASTCALL
+PATH_AddEntry ( PPATH pPath, const POINT *pPoint, BYTE flags )
+{
+ ASSERT(pPath!=NULL);
+
+ /* FIXME: If newStroke is true, perhaps we want to check that we're
+ * getting a PT_MOVETO
+ */
+
+ /* Check that path is open */
+ if ( pPath->state != PATH_Open )
+ return FALSE;
+
+ /* Reserve enough memory for an extra path entry */
+ if ( !PATH_ReserveEntries(pPath, pPath->numEntriesUsed+1) )
+ return FALSE;
+
+ /* Store information in path entry */
+ pPath->pPoints[pPath->numEntriesUsed]=*pPoint;
+ pPath->pFlags[pPath->numEntriesUsed]=flags;
+
+ /* If this is PT_CLOSEFIGURE, we have to start a new stroke next time */
+ if((flags & PT_CLOSEFIGURE) == PT_CLOSEFIGURE)
+ pPath->newStroke=TRUE;
+
+ /* Increment entry count */
+ pPath->numEntriesUsed++;
+
+ return TRUE;
+}
+
+/* PATH_ReserveEntries
+ *
+ * Ensures that at least "numEntries" entries (for points and flags) have
+ * been allocated; allocates larger arrays and copies the existing entries
+ * to those arrays, if necessary. Returns TRUE if successful, else FALSE.
+ */
+BOOL
+FASTCALL
+PATH_ReserveEntries ( PPATH pPath, INT numEntries )
+{
+ INT numEntriesToAllocate;
+ POINT *pPointsNew;
+ BYTE *pFlagsNew;
+
+ ASSERT(pPath!=NULL);
+ ASSERT(numEntries>=0);
+
+ /* Do we have to allocate more memory? */
+ if(numEntries > pPath->numEntriesAllocated)
+ {
+ /* Find number of entries to allocate. We let the size of the array
+ * grow exponentially, since that will guarantee linear time
+ * complexity. */
+ if(pPath->numEntriesAllocated)
+ {
+ numEntriesToAllocate=pPath->numEntriesAllocated;
+ while(numEntriesToAllocate<numEntries)
+ numEntriesToAllocate=numEntriesToAllocate*GROW_FACTOR_NUMER/GROW_FACTOR_DENOM;
+ } else
+ numEntriesToAllocate=numEntries;
+
+ /* Allocate new arrays */
+ pPointsNew=(POINT *)ExAllocatePoolWithTag(PagedPool, numEntriesToAllocate * sizeof(POINT), TAG_PATH);
+ if(!pPointsNew)
+ return FALSE;
+ pFlagsNew=(BYTE *)ExAllocatePoolWithTag(PagedPool, numEntriesToAllocate * sizeof(BYTE), TAG_PATH);
+ if(!pFlagsNew)
+ {
+ ExFreePoolWithTag(pPointsNew, TAG_PATH);
+ return FALSE;
+ }
+
+ /* Copy old arrays to new arrays and discard old arrays */
+ if(pPath->pPoints)
+ {
+ ASSERT(pPath->pFlags);
+
+ memcpy(pPointsNew, pPath->pPoints, sizeof(POINT)*pPath->numEntriesUsed);
+ memcpy(pFlagsNew, pPath->pFlags, sizeof(BYTE)*pPath->numEntriesUsed);
+
+ ExFreePoolWithTag(pPath->pPoints, TAG_PATH);
+ ExFreePoolWithTag(pPath->pFlags, TAG_PATH);
+ }
+ pPath->pPoints=pPointsNew;
+ pPath->pFlags=pFlagsNew;
+ pPath->numEntriesAllocated=numEntriesToAllocate;
+ }
+
+ return TRUE;
+}
+
+/* PATH_DoArcPart
+ *
+ * Creates a Bezier spline that corresponds to part of an arc and appends the
+ * corresponding points to the path. The start and end angles are passed in
+ * "angleStart" and "angleEnd"; these angles should span a quarter circle
+ * at most. If "startEntryType" is non-zero, an entry of that type for the first
+ * control point is added to the path; otherwise, it is assumed that the current
+ * position is equal to the first control point.
+ */
+BOOL
+FASTCALL
+PATH_DoArcPart ( PPATH pPath, FLOAT_POINT corners[],
+ double angleStart, double angleEnd, BYTE startEntryType )
+{
+ double halfAngle, a;
+ double xNorm[4], yNorm[4];
+ POINT point;
+ int i;
+
+ ASSERT(fabs(angleEnd-angleStart)<=M_PI_2);
+
+ /* FIXME: Is there an easier way of computing this? */
+
+ /* Compute control points */
+ halfAngle=(angleEnd-angleStart)/2.0;
+ if(fabs(halfAngle)>1e-8)
+ {
+ a=4.0/3.0*(1-cos(halfAngle))/sin(halfAngle);
+ xNorm[0]=cos(angleStart);
+ yNorm[0]=sin(angleStart);
+ xNorm[1]=xNorm[0] - a*yNorm[0];
+ yNorm[1]=yNorm[0] + a*xNorm[0];
+ xNorm[3]=cos(angleEnd);
+ yNorm[3]=sin(angleEnd);
+ xNorm[2]=xNorm[3] + a*yNorm[3];
+ yNorm[2]=yNorm[3] - a*xNorm[3];
+ } else
+ for(i=0; i<4; i++)
+ {
+ xNorm[i]=cos(angleStart);
+ yNorm[i]=sin(angleStart);
+ }
+
+ /* Add starting point to path if desired */
+ if(startEntryType)
+ {
+ PATH_ScaleNormalizedPoint(corners, xNorm[0], yNorm[0], &point);
+ if(!PATH_AddEntry(pPath, &point, startEntryType))
+ return FALSE;
+ }
+
+ /* Add remaining control points */
+ for(i=1; i<4; i++)
+ {
+ PATH_ScaleNormalizedPoint(corners, xNorm[i], yNorm[i], &point);
+ if(!PATH_AddEntry(pPath, &point, PT_BEZIERTO))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* PATH_ScaleNormalizedPoint
+ *
+ * Scales a normalized point (x, y) with respect to the box whose corners are
+ * passed in "corners". The point is stored in "*pPoint". The normalized
+ * coordinates (-1.0, -1.0) correspond to corners[0], the coordinates
+ * (1.0, 1.0) correspond to corners[1].
+ */
+VOID
+FASTCALL
+PATH_ScaleNormalizedPoint ( FLOAT_POINT corners[], double x,
+ double y, POINT *pPoint )
+{
+ ASSERT ( corners );
+ ASSERT ( pPoint );
+ pPoint->x=GDI_ROUND( (double)corners[0].x + (double)(corners[1].x-corners[0].x)*0.5*(x+1.0) );
+ pPoint->y=GDI_ROUND( (double)corners[0].y + (double)(corners[1].y-corners[0].y)*0.5*(y+1.0) );
+}
+
+/* PATH_NormalizePoint
+ *
+ * Normalizes a point with respect to the box whose corners are passed in
+ * corners. The normalized coordinates are stored in *pX and *pY.
+ */
+VOID
+FASTCALL
+PATH_NormalizePoint ( FLOAT_POINT corners[],
+ const FLOAT_POINT *pPoint,
+ double *pX, double *pY)
+{
+ ASSERT ( corners );
+ ASSERT ( pPoint );
+ ASSERT ( pX );
+ ASSERT ( pY );
+ *pX=(double)(pPoint->x-corners[0].x)/(double)(corners[1].x-corners[0].x) * 2.0 - 1.0;
+ *pY=(double)(pPoint->y-corners[0].y)/(double)(corners[1].y-corners[0].y) * 2.0 - 1.0;
+}
+
+
+BOOL FASTCALL PATH_StrokePath(DC *dc, PPATH pPath)
+{
+ BOOL ret = FALSE;
+ INT i=0;
+ INT nLinePts, nAlloc;
+ POINT *pLinePts = NULL;
+ POINT ptViewportOrg, ptWindowOrg;
+ SIZE szViewportExt, szWindowExt;
+ DWORD mapMode, graphicsMode;
+ XFORM xform;
+ PDC_ATTR pdcattr = dc->pdcattr;
+
+ DPRINT("Enter %s\n", __FUNCTION__);
+
+ if (pPath->state != PATH_Closed)
+ return FALSE;
+
+ /* Save the mapping mode info */
+ mapMode = pdcattr->iMapMode;
+
+ DC_vUpdateViewportExt(dc);
+ szViewportExt = dc->pdcattr->szlViewportExt;
+ ptViewportOrg = dc->pdcattr->ptlViewportOrg;
+ szWindowExt = dc->pdcattr->szlWindowExt;
+ ptWindowOrg = dc->pdcattr->ptlWindowOrg;
+
+ MatrixS2XForm(&xform, &dc->dclevel.mxWorldToPage);
+
+ /* Set MM_TEXT */
+ pdcattr->iMapMode = MM_TEXT;
+ pdcattr->ptlViewportOrg.x = 0;
+ pdcattr->ptlViewportOrg.y = 0;
+ pdcattr->ptlWindowOrg.x = 0;
+ pdcattr->ptlWindowOrg.y = 0;
+ graphicsMode = pdcattr->iGraphicsMode;
+ pdcattr->iGraphicsMode = GM_ADVANCED;
+ IntGdiModifyWorldTransform(dc, &xform, MWT_IDENTITY);
+ pdcattr->iGraphicsMode = graphicsMode;
+
+ /* Allocate enough memory for the worst case without beziers (one PT_MOVETO
+ * and the rest PT_LINETO with PT_CLOSEFIGURE at the end) plus some buffer
+ * space in case we get one to keep the number of reallocations small. */
+ nAlloc = pPath->numEntriesUsed + 1 + 300;
+ pLinePts = ExAllocatePoolWithTag(PagedPool, nAlloc * sizeof(POINT), TAG_PATH);
+ if(!pLinePts)
+ {
+ DPRINT1("Can't allocate pool!\n");
+ SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
+ goto end;
+ }
+ nLinePts = 0;
+
+ for(i = 0; i < pPath->numEntriesUsed; i++)
+ {
+ if((i == 0 || (pPath->pFlags[i-1] & PT_CLOSEFIGURE))
+ && (pPath->pFlags[i] != PT_MOVETO))
+ {
+ DPRINT1("Expected PT_MOVETO %s, got path flag %d\n",
+ i == 0 ? "as first point" : "after PT_CLOSEFIGURE",
+ (INT)pPath->pFlags[i]);
+ goto end;
+ }
+
+ switch(pPath->pFlags[i])
+ {
+ case PT_MOVETO:
+ DPRINT("Got PT_MOVETO (%ld, %ld)\n",
+ pPath->pPoints[i].x, pPath->pPoints[i].y);
+ if(nLinePts >= 2) IntGdiPolyline(dc, pLinePts, nLinePts);
+ nLinePts = 0;
+ pLinePts[nLinePts++] = pPath->pPoints[i];
+ break;
+ case PT_LINETO:
+ case (PT_LINETO | PT_CLOSEFIGURE):
+ DPRINT("Got PT_LINETO (%ld, %ld)\n",
+ pPath->pPoints[i].x, pPath->pPoints[i].y);
+ pLinePts[nLinePts++] = pPath->pPoints[i];
+ break;
+ case PT_BEZIERTO:
+ DPRINT("Got PT_BEZIERTO\n");
+ if(pPath->pFlags[i+1] != PT_BEZIERTO ||
+ (pPath->pFlags[i+2] & ~PT_CLOSEFIGURE) != PT_BEZIERTO)
+ {
+ DPRINT1("Path didn't contain 3 successive PT_BEZIERTOs\n");
+ ret = FALSE;
+ goto end;
+ }
+ else
+ {
+ INT nBzrPts, nMinAlloc;
+ POINT *pBzrPts = GDI_Bezier(&pPath->pPoints[i-1], 4, &nBzrPts);
+ /* Make sure we have allocated enough memory for the lines of
+ * this bezier and the rest of the path, assuming we won't get
+ * another one (since we won't reallocate again then). */
+ nMinAlloc = nLinePts + (pPath->numEntriesUsed - i) + nBzrPts;
+ if(nAlloc < nMinAlloc)
+ {
+ // Reallocate memory
+
+ POINT *Realloc = NULL;
+ nAlloc = nMinAlloc * 2;
+
+ Realloc = ExAllocatePoolWithTag(PagedPool,
+ nAlloc * sizeof(POINT),
+ TAG_PATH);
+
+ if(!Realloc)
+ {
+ DPRINT1("Can't allocate pool!\n");
+ goto end;
+ }
+
+ memcpy(Realloc, pLinePts, nLinePts*sizeof(POINT));
+ ExFreePoolWithTag(pLinePts, TAG_PATH);
+ pLinePts = Realloc;
+ }
+ memcpy(&pLinePts[nLinePts], &pBzrPts[1], (nBzrPts - 1) * sizeof(POINT));
+ nLinePts += nBzrPts - 1;
+ ExFreePoolWithTag(pBzrPts, TAG_BEZIER);
+ i += 2;
+ }
+ break;
+ default:
+ DPRINT1("Got path flag %d (not supported)\n", (INT)pPath->pFlags[i]);
+ goto end;
+ }
+
+ if(pPath->pFlags[i] & PT_CLOSEFIGURE)
+ {
+ pLinePts[nLinePts++] = pLinePts[0];
+ }
+ }
+ if(nLinePts >= 2)
+ IntGdiPolyline(dc, pLinePts, nLinePts);
+
+ ret = TRUE;
+
+end:
+ if(pLinePts) ExFreePoolWithTag(pLinePts, TAG_PATH);
+
+ /* Restore the old mapping mode */
+ pdcattr->iMapMode = mapMode;
+ pdcattr->szlWindowExt.cx = szWindowExt.cx;
+ pdcattr->szlWindowExt.cy = szWindowExt.cy;
+ pdcattr->ptlWindowOrg.x = ptWindowOrg.x;
+ pdcattr->ptlWindowOrg.y = ptWindowOrg.y;
+
+ pdcattr->szlViewportExt.cx = szViewportExt.cx;
+ pdcattr->szlViewportExt.cy = szViewportExt.cy;
+ pdcattr->ptlViewportOrg.x = ptViewportOrg.x;
+ pdcattr->ptlViewportOrg.y = ptViewportOrg.y;
+
+ /* Restore the world transform */
+ XForm2MatrixS(&dc->dclevel.mxWorldToPage, &xform);
+
+ /* If we've moved the current point then get its new position
+ which will be in device (MM_TEXT) co-ords, convert it to
+ logical co-ords and re-set it. This basically updates
+ dc->CurPosX|Y so that their values are in the correct mapping
+ mode.
+ */
+ if(i > 0)
+ {
+ POINT pt;
+ IntGetCurrentPositionEx(dc, &pt);
+ IntDPtoLP(dc, &pt, 1);
+ IntGdiMoveToEx(dc, pt.x, pt.y, NULL, FALSE);
+ }
+ DPRINT("Leave %s, ret=%d\n", __FUNCTION__, ret);
+ return ret;
+}
+
+#define round(x) ((int)((x)>0?(x)+0.5:(x)-0.5))
+
+static
+BOOL
+FASTCALL
+PATH_WidenPath(DC *dc)
+{
+ INT i, j, numStrokes, penWidth, penWidthIn, penWidthOut, size, penStyle;
+ BOOL ret = FALSE;
+ PPATH pPath, pNewPath, *pStrokes = NULL, *pOldStrokes, pUpPath, pDownPath;
+ EXTLOGPEN *elp;
+ DWORD obj_type, joint, endcap, penType;
+ PDC_ATTR pdcattr = dc->pdcattr;
+
+ pPath = PATH_LockPath( dc->dclevel.hPath );
+ if (!pPath) return FALSE;
+
+ if(pPath->state == PATH_Open)
+ {
+ PATH_UnlockPath( pPath );
+ SetLastWin32Error(ERROR_CAN_NOT_COMPLETE);
+ return FALSE;
+ }
+
+ PATH_FlattenPath(pPath);
+
+ size = IntGdiGetObject( pdcattr->hpen, 0, NULL);
+ if (!size)
+ {
+ PATH_UnlockPath( pPath );
+ SetLastWin32Error(ERROR_CAN_NOT_COMPLETE);
+ return FALSE;
+ }
+
+ elp = ExAllocatePoolWithTag(PagedPool, size, TAG_PATH);
+ (VOID) IntGdiGetObject( pdcattr->hpen, size, elp);
+
+ obj_type = GDIOBJ_GetObjectType(pdcattr->hpen);
+ if(obj_type == GDI_OBJECT_TYPE_PEN)
+ {
+ penStyle = ((LOGPEN*)elp)->lopnStyle;
+ }
+ else if(obj_type == GDI_OBJECT_TYPE_EXTPEN)
+ {
+ penStyle = elp->elpPenStyle;
+ }
+ else
+ {
+ SetLastWin32Error(ERROR_CAN_NOT_COMPLETE);
+ ExFreePoolWithTag(elp, TAG_PATH);
+ PATH_UnlockPath( pPath );
+ return FALSE;
+ }
+
+ penWidth = elp->elpWidth;
+ ExFreePoolWithTag(elp, TAG_PATH);
+
+ endcap = (PS_ENDCAP_MASK & penStyle);
+ joint = (PS_JOIN_MASK & penStyle);
+ penType = (PS_TYPE_MASK & penStyle);
+
+ /* The function cannot apply to cosmetic pens */
+ if(obj_type == GDI_OBJECT_TYPE_EXTPEN && penType == PS_COSMETIC)
+ {
+ PATH_UnlockPath( pPath );
+ SetLastWin32Error(ERROR_CAN_NOT_COMPLETE);
+ return FALSE;
+ }
+
+ penWidthIn = penWidth / 2;
+ penWidthOut = penWidth / 2;
+ if(penWidthIn + penWidthOut < penWidth)
+ penWidthOut++;
+
+ numStrokes = 0;
+
+ for(i = 0, j = 0; i < pPath->numEntriesUsed; i++, j++)
+ {
+ POINT point;
+ if((i == 0 || (pPath->pFlags[i-1] & PT_CLOSEFIGURE)) &&
+ (pPath->pFlags[i] != PT_MOVETO))
+ {
+ DPRINT1("Expected PT_MOVETO %s, got path flag %c\n",
+ i == 0 ? "as first point" : "after PT_CLOSEFIGURE",
+ pPath->pFlags[i]);
+ return FALSE;
+ }
+ switch(pPath->pFlags[i])
+ {
+ case PT_MOVETO:
+ if(numStrokes > 0)
+ {
+ pStrokes[numStrokes - 1]->state = PATH_Closed;
+ }
+ numStrokes++;
+ j = 0;
+ if (numStrokes == 1)
+ pStrokes = ExAllocatePoolWithTag(PagedPool, numStrokes * sizeof(PPATH), TAG_PATH);
+ else
+ {
+ pOldStrokes = pStrokes; // Save old pointer.
+ pStrokes = ExAllocatePoolWithTag(PagedPool, numStrokes * sizeof(PPATH), TAG_PATH);
+ if (!pStrokes) return FALSE;
+ RtlCopyMemory(pStrokes, pOldStrokes, numStrokes * sizeof(PPATH));
+ ExFreePoolWithTag(pOldStrokes, TAG_PATH); // Free old pointer.
+ }
+ if (!pStrokes) return FALSE;
+ pStrokes[numStrokes - 1] = ExAllocatePoolWithTag(PagedPool, sizeof(PATH), TAG_PATH);
+
+ PATH_InitGdiPath(pStrokes[numStrokes - 1]);
+ pStrokes[numStrokes - 1]->state = PATH_Open;
+ case PT_LINETO:
+ case (PT_LINETO | PT_CLOSEFIGURE):
+ point.x = pPath->pPoints[i].x;
+ point.y = pPath->pPoints[i].y;
+ PATH_AddEntry(pStrokes[numStrokes - 1], &point, pPath->pFlags[i]);
+ break;
+ case PT_BEZIERTO:
+ /* should never happen because of the FlattenPath call */
+ DPRINT1("Should never happen\n");
+ break;
+ default:
+ DPRINT1("Got path flag %c\n", pPath->pFlags[i]);
+ return FALSE;
+ }
+ }
+
+ pNewPath = ExAllocatePoolWithTag(PagedPool, sizeof(PATH), TAG_PATH);
+ PATH_InitGdiPath(pNewPath);
+ pNewPath->state = PATH_Open;
+
+ for(i = 0; i < numStrokes; i++)
+ {
+ pUpPath = ExAllocatePoolWithTag(PagedPool, sizeof(PATH), TAG_PATH);
+ PATH_InitGdiPath(pUpPath);
+ pUpPath->state = PATH_Open;
+ pDownPath = ExAllocatePoolWithTag(PagedPool, sizeof(PATH), TAG_PATH);
+ PATH_InitGdiPath(pDownPath);
+ pDownPath->state = PATH_Open;
+
+ for(j = 0; j < pStrokes[i]->numEntriesUsed; j++)
+ {
+ /* Beginning or end of the path if not closed */
+ if((!(pStrokes[i]->pFlags[pStrokes[i]->numEntriesUsed - 1] & PT_CLOSEFIGURE)) && (j == 0 || j == pStrokes[i]->numEntriesUsed - 1) )
+ {
+ /* Compute segment angle */
+ double xo, yo, xa, ya, theta;
+ POINT pt;
+ FLOAT_POINT corners[2];
+ if(j == 0)
+ {
+ xo = pStrokes[i]->pPoints[j].x;
+ yo = pStrokes[i]->pPoints[j].y;
+ xa = pStrokes[i]->pPoints[1].x;
+ ya = pStrokes[i]->pPoints[1].y;
+ }
+ else
+ {
+ xa = pStrokes[i]->pPoints[j - 1].x;
+ ya = pStrokes[i]->pPoints[j - 1].y;
+ xo = pStrokes[i]->pPoints[j].x;
+ yo = pStrokes[i]->pPoints[j].y;
+ }
+ theta = atan2( ya - yo, xa - xo );
+ switch(endcap)
+ {
+ case PS_ENDCAP_SQUARE :
+ pt.x = xo + round(sqrt(2) * penWidthOut * cos(M_PI_4 + theta));
+ pt.y = yo + round(sqrt(2) * penWidthOut * sin(M_PI_4 + theta));
+ PATH_AddEntry(pUpPath, &pt, (j == 0 ? PT_MOVETO : PT_LINETO) );
+ pt.x = xo + round(sqrt(2) * penWidthIn * cos(- M_PI_4 + theta));
+ pt.y = yo + round(sqrt(2) * penWidthIn * sin(- M_PI_4 + theta));
+ PATH_AddEntry(pUpPath, &pt, PT_LINETO);
+ break;
+ case PS_ENDCAP_FLAT :
+ pt.x = xo + round( penWidthOut * cos(theta + M_PI_2) );
+ pt.y = yo + round( penWidthOut * sin(theta + M_PI_2) );
+ PATH_AddEntry(pUpPath, &pt, (j == 0 ? PT_MOVETO : PT_LINETO));
+ pt.x = xo - round( penWidthIn * cos(theta + M_PI_2) );
+ pt.y = yo - round( penWidthIn * sin(theta + M_PI_2) );
+ PATH_AddEntry(pUpPath, &pt, PT_LINETO);
+ break;
+ case PS_ENDCAP_ROUND :
+ default :
+ corners[0].x = xo - penWidthIn;
+ corners[0].y = yo - penWidthIn;
+ corners[1].x = xo + penWidthOut;
+ corners[1].y = yo + penWidthOut;
+ PATH_DoArcPart(pUpPath ,corners, theta + M_PI_2 , theta + 3 * M_PI_4, (j == 0 ? PT_MOVETO : FALSE));
+ PATH_DoArcPart(pUpPath ,corners, theta + 3 * M_PI_4 , theta + M_PI, FALSE);
+ PATH_DoArcPart(pUpPath ,corners, theta + M_PI, theta + 5 * M_PI_4, FALSE);
+ PATH_DoArcPart(pUpPath ,corners, theta + 5 * M_PI_4 , theta + 3 * M_PI_2, FALSE);
+ break;
+ }
+ }
+ /* Corpse of the path */
+ else
+ {
+ /* Compute angle */
+ INT previous, next;
+ double xa, ya, xb, yb, xo, yo;
+ double alpha, theta, miterWidth;
+ DWORD _joint = joint;
+ POINT pt;
+ PPATH pInsidePath, pOutsidePath;
+ if(j > 0 && j < pStrokes[i]->numEntriesUsed - 1)
+ {
+ previous = j - 1;
+ next = j + 1;
+ }
+ else if (j == 0)
+ {
+ previous = pStrokes[i]->numEntriesUsed - 1;
+ next = j + 1;
+ }
+ else
+ {
+ previous = j - 1;
+ next = 0;
+ }
+ xo = pStrokes[i]->pPoints[j].x;
+ yo = pStrokes[i]->pPoints[j].y;
+ xa = pStrokes[i]->pPoints[previous].x;
+ ya = pStrokes[i]->pPoints[previous].y;
+ xb = pStrokes[i]->pPoints[next].x;
+ yb = pStrokes[i]->pPoints[next].y;
+ theta = atan2( yo - ya, xo - xa );
+ alpha = atan2( yb - yo, xb - xo ) - theta;
+ if (alpha > 0) alpha -= M_PI;
+ else alpha += M_PI;
+ if(_joint == PS_JOIN_MITER && dc->dclevel.laPath.eMiterLimit < fabs(1 / sin(alpha/2)))
+ {
+ _joint = PS_JOIN_BEVEL;
+ }
+ if(alpha > 0)
+ {
+ pInsidePath = pUpPath;
+ pOutsidePath = pDownPath;
+ }
+ else if(alpha < 0)
+ {
+ pInsidePath = pDownPath;
+ pOutsidePath = pUpPath;
+ }
+ else
+ {
+ continue;
+ }
+ /* Inside angle points */
+ if(alpha > 0)
+ {
+ pt.x = xo - round( penWidthIn * cos(theta + M_PI_2) );
+ pt.y = yo - round( penWidthIn * sin(theta + M_PI_2) );
+ }
+ else
+ {
+ pt.x = xo + round( penWidthIn * cos(theta + M_PI_2) );
+ pt.y = yo + round( penWidthIn * sin(theta + M_PI_2) );
+ }
+ PATH_AddEntry(pInsidePath, &pt, PT_LINETO);
+ if(alpha > 0)
+ {
+ pt.x = xo + round( penWidthIn * cos(M_PI_2 + alpha + theta) );
+ pt.y = yo + round( penWidthIn * sin(M_PI_2 + alpha + theta) );
+ }
+ else
+ {
+ pt.x = xo - round( penWidthIn * cos(M_PI_2 + alpha + theta) );
+ pt.y = yo - round( penWidthIn * sin(M_PI_2 + alpha + theta) );
+ }
+ PATH_AddEntry(pInsidePath, &pt, PT_LINETO);
+ /* Outside angle point */
+ switch(_joint)
+ {
+ case PS_JOIN_MITER :
+ miterWidth = fabs(penWidthOut / cos(M_PI_2 - fabs(alpha) / 2));
+ pt.x = xo + round( miterWidth * cos(theta + alpha / 2) );
+ pt.y = yo + round( miterWidth * sin(theta + alpha / 2) );
+ PATH_AddEntry(pOutsidePath, &pt, PT_LINETO);
+ break;
+ case PS_JOIN_BEVEL :
+ if(alpha > 0)
+ {
+ pt.x = xo + round( penWidthOut * cos(theta + M_PI_2) );
+ pt.y = yo + round( penWidthOut * sin(theta + M_PI_2) );
+ }
+ else
+ {
+ pt.x = xo - round( penWidthOut * cos(theta + M_PI_2) );
+ pt.y = yo - round( penWidthOut * sin(theta + M_PI_2) );
+ }
+ PATH_AddEntry(pOutsidePath, &pt, PT_LINETO);
+ if(alpha > 0)
+ {
+ pt.x = xo - round( penWidthOut * cos(M_PI_2 + alpha + theta) );
+ pt.y = yo - round( penWidthOut * sin(M_PI_2 + alpha + theta) );
+ }
+ else
+ {
+ pt.x = xo + round( penWidthOut * cos(M_PI_2 + alpha + theta) );
+ pt.y = yo + round( penWidthOut * sin(M_PI_2 + alpha + theta) );
+ }
+ PATH_AddEntry(pOutsidePath, &pt, PT_LINETO);
+ break;
+ case PS_JOIN_ROUND :
+ default :
+ if(alpha > 0)
+ {
+ pt.x = xo + round( penWidthOut * cos(theta + M_PI_2) );
+ pt.y = yo + round( penWidthOut * sin(theta + M_PI_2) );
+ }
+ else
+ {
+ pt.x = xo - round( penWidthOut * cos(theta + M_PI_2) );
+ pt.y = yo - round( penWidthOut * sin(theta + M_PI_2) );
+ }
+ PATH_AddEntry(pOutsidePath, &pt, PT_BEZIERTO);
+ pt.x = xo + round( penWidthOut * cos(theta + alpha / 2) );
+ pt.y = yo + round( penWidthOut * sin(theta + alpha / 2) );
+ PATH_AddEntry(pOutsidePath, &pt, PT_BEZIERTO);
+ if(alpha > 0)
+ {
+ pt.x = xo - round( penWidthOut * cos(M_PI_2 + alpha + theta) );
+ pt.y = yo - round( penWidthOut * sin(M_PI_2 + alpha + theta) );
+ }
+ else
+ {
+ pt.x = xo + round( penWidthOut * cos(M_PI_2 + alpha + theta) );
+ pt.y = yo + round( penWidthOut * sin(M_PI_2 + alpha + theta) );
+ }
+ PATH_AddEntry(pOutsidePath, &pt, PT_BEZIERTO);
+ break;
+ }
+ }
+ }
+ for(j = 0; j < pUpPath->numEntriesUsed; j++)
+ {
+ POINT pt;
+ pt.x = pUpPath->pPoints[j].x;
+ pt.y = pUpPath->pPoints[j].y;
+ PATH_AddEntry(pNewPath, &pt, (j == 0 ? PT_MOVETO : PT_LINETO));
+ }
+ for(j = 0; j < pDownPath->numEntriesUsed; j++)
+ {
+ POINT pt;
+ pt.x = pDownPath->pPoints[pDownPath->numEntriesUsed - j - 1].x;
+ pt.y = pDownPath->pPoints[pDownPath->numEntriesUsed - j - 1].y;
+ PATH_AddEntry(pNewPath, &pt, ( (j == 0 && (pStrokes[i]->pFlags[pStrokes[i]->numEntriesUsed - 1] & PT_CLOSEFIGURE)) ? PT_MOVETO : PT_LINETO));
+ }
+
+ PATH_DestroyGdiPath(pStrokes[i]);
+ ExFreePoolWithTag(pStrokes[i], TAG_PATH);
+ PATH_DestroyGdiPath(pUpPath);
+ ExFreePoolWithTag(pUpPath, TAG_PATH);
+ PATH_DestroyGdiPath(pDownPath);
+ ExFreePoolWithTag(pDownPath, TAG_PATH);
+ }
+ ExFreePoolWithTag(pStrokes, TAG_PATH);
+
+ pNewPath->state = PATH_Closed;
+ if (!(ret = PATH_AssignGdiPath(pPath, pNewPath)))
+ DPRINT1("Assign path failed\n");
+ PATH_DestroyGdiPath(pNewPath);
+ ExFreePoolWithTag(pNewPath, TAG_PATH);
+ return ret;
+}
+
+static inline INT int_from_fixed(FIXED f)
+{
+ return (f.fract >= 0x8000) ? (f.value + 1) : f.value;
+}
+
+/**********************************************************************
+ * PATH_BezierTo
+ *
+ * internally used by PATH_add_outline
+ */
+static
+VOID
+FASTCALL
+PATH_BezierTo(PPATH pPath, POINT *lppt, INT n)
+{
+ if (n < 2) return;
+
+ if (n == 2)
+ {
+ PATH_AddEntry(pPath, &lppt[1], PT_LINETO);
+ }
+ else if (n == 3)
+ {
+ PATH_AddEntry(pPath, &lppt[0], PT_BEZIERTO);
+ PATH_AddEntry(pPath, &lppt[1], PT_BEZIERTO);
+ PATH_AddEntry(pPath, &lppt[2], PT_BEZIERTO);
+ }
+ else
+ {
+ POINT pt[3];
+ INT i = 0;
+
+ pt[2] = lppt[0];
+ n--;
+
+ while (n > 2)
+ {
+ pt[0] = pt[2];
+ pt[1] = lppt[i+1];
+ pt[2].x = (lppt[i+2].x + lppt[i+1].x) / 2;
+ pt[2].y = (lppt[i+2].y + lppt[i+1].y) / 2;
+ PATH_BezierTo(pPath, pt, 3);
+ n--;
+ i++;
+ }
+
+ pt[0] = pt[2];
+ pt[1] = lppt[i+1];
+ pt[2] = lppt[i+2];
+ PATH_BezierTo(pPath, pt, 3);
+ }
+}
+
+static
+BOOL
+FASTCALL
+PATH_add_outline(PDC dc, INT x, INT y, TTPOLYGONHEADER *header, DWORD size)
+{
+ PPATH pPath;
+ TTPOLYGONHEADER *start;
+ POINT pt;
+
+ start = header;
+
+ pPath = PATH_LockPath(dc->dclevel.hPath);
+ {
+ return FALSE;
+ }
+
+ while ((char *)header < (char *)start + size)
+ {
+ TTPOLYCURVE *curve;
+
+ if (header->dwType != TT_POLYGON_TYPE)
+ {
+ DPRINT1("Unknown header type %d\n", header->dwType);
+ return FALSE;
+ }
+
+ pt.x = x + int_from_fixed(header->pfxStart.x);
+ pt.y = y - int_from_fixed(header->pfxStart.y);
+ IntLPtoDP(dc, &pt, 1);
+ PATH_AddEntry(pPath, &pt, PT_MOVETO);
+
+ curve = (TTPOLYCURVE *)(header + 1);
+
+ while ((char *)curve < (char *)header + header->cb)
+ {
+ /*DPRINT1("curve->wType %d\n", curve->wType);*/
+
+ switch(curve->wType)
+ {
+ case TT_PRIM_LINE:
+ {
+ WORD i;
+
+ for (i = 0; i < curve->cpfx; i++)
+ {
+ pt.x = x + int_from_fixed(curve->apfx[i].x);
+ pt.y = y - int_from_fixed(curve->apfx[i].y);
+ IntLPtoDP(dc, &pt, 1);
+ PATH_AddEntry(pPath, &pt, PT_LINETO);
+ }
+ break;
+ }
+
+ case TT_PRIM_QSPLINE:
+ case TT_PRIM_CSPLINE:
+ {
+ WORD i;
+ POINTFX ptfx;
+ POINT *pts = ExAllocatePoolWithTag(PagedPool, (curve->cpfx + 1) * sizeof(POINT), TAG_PATH);
+
+ if (!pts) return FALSE;
+
+ ptfx = *(POINTFX *)((char *)curve - sizeof(POINTFX));
+
+ pts[0].x = x + int_from_fixed(ptfx.x);
+ pts[0].y = y - int_from_fixed(ptfx.y);
+ IntLPtoDP(dc, &pts[0], 1);
+
+ for (i = 0; i < curve->cpfx; i++)
+ {
+ pts[i + 1].x = x + int_from_fixed(curve->apfx[i].x);
+ pts[i + 1].y = y - int_from_fixed(curve->apfx[i].y);
+ IntLPtoDP(dc, &pts[i + 1], 1);
+ }
+
+ PATH_BezierTo(pPath, pts, curve->cpfx + 1);
+
+ ExFreePoolWithTag(pts, TAG_PATH);
+ break;
+ }
+
+ default:
+ DPRINT1("Unknown curve type %04x\n", curve->wType);
+ return FALSE;
+ }
+
+ curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
+ }
+ header = (TTPOLYGONHEADER *)((char *)header + header->cb);
+ }
+
+ IntGdiCloseFigure( pPath );
+ PATH_UnlockPath( pPath );
+ return TRUE;
+}
+
+/**********************************************************************
+ * PATH_ExtTextOut
+ */
+BOOL
+FASTCALL
+PATH_ExtTextOut(PDC dc, INT x, INT y, UINT flags, const RECTL *lprc,
+ LPCWSTR str, UINT count, const INT *dx)
+{
+ unsigned int idx;
+ double cosEsc, sinEsc;
+ PDC_ATTR pdcattr;
+ PTEXTOBJ TextObj;
+ LOGFONTW lf;
+ POINTL org;
+ INT offset = 0, xoff = 0, yoff = 0;
+
+ if (!count) return TRUE;
+
+ pdcattr = dc->pdcattr;
+
+ TextObj = RealizeFontInit( pdcattr->hlfntNew);
+ if ( !TextObj ) return FALSE;
+
+ FontGetObject( TextObj, sizeof(lf), &lf);
+
+ if (lf.lfEscapement != 0)
+ {
+ cosEsc = cos(lf.lfEscapement * M_PI / 1800);
+ sinEsc = sin(lf.lfEscapement * M_PI / 1800);
+ } else
+ {
+ cosEsc = 1;
+ sinEsc = 0;
+ }
+
+ IntGdiGetDCOrg(dc, &org);
+
+ for (idx = 0; idx < count; idx++)
+ {
+ GLYPHMETRICS gm;
+ DWORD dwSize;
+ void *outline;
+
+ dwSize = ftGdiGetGlyphOutline( dc,
+ str[idx],
+ GGO_GLYPH_INDEX | GGO_NATIVE,
+ &gm,
+ 0,
+ NULL,
+ NULL,
+ TRUE);
+ if (!dwSize) return FALSE;
+
+ outline = ExAllocatePoolWithTag(PagedPool, dwSize, TAG_PATH);
+ if (!outline) return FALSE;
+
+ ftGdiGetGlyphOutline( dc,
+ str[idx],
+ GGO_GLYPH_INDEX | GGO_NATIVE,
+ &gm,
+ dwSize,
+ outline,
+ NULL,
+ TRUE);
+
+ PATH_add_outline(dc, org.x + x + xoff, org.x + y + yoff, outline, dwSize);
+
+ ExFreePoolWithTag(outline, TAG_PATH);
+
+ if (dx)
+ {
+ offset += dx[idx];
+ xoff = offset * cosEsc;
+ yoff = offset * -sinEsc;
+ }
+ else
+ {
+ xoff += gm.gmCellIncX;
+ yoff += gm.gmCellIncY;
+ }
+ }
+ return TRUE;
+}
+
+
+/***********************************************************************
+ * Exported functions
+ */
+
+BOOL
+APIENTRY
+NtGdiAbortPath(HDC hDC)
+{
+ PPATH pPath;
+ PDC dc = DC_LockDc ( hDC );
+ if ( !dc )
+ {
+ SetLastWin32Error(ERROR_INVALID_HANDLE);
+ return FALSE;
+ }
+
+ pPath = PATH_LockPath(dc->dclevel.hPath);
+ {
+ DC_UnlockDc(dc);
+ return FALSE;
+ }
+
+ PATH_EmptyPath(pPath);
+
+ PATH_UnlockPath(pPath);
+ DC_UnlockDc ( dc );
+ return TRUE;
+}
+
+BOOL
+APIENTRY
+NtGdiBeginPath( HDC hDC )
+{
+ PPATH pPath;
+ PDC dc;
+
+ dc = DC_LockDc ( hDC );
+ if ( !dc )
+ {
+ SetLastWin32Error(ERROR_INVALID_HANDLE);
+ return FALSE;
+ }
+
+ /* If path is already open, do nothing. Check if not Save DC state */
+ if ((dc->dclevel.flPath & DCPATH_ACTIVE) && !(dc->dclevel.flPath & DCPATH_SAVE))
+ {
+ DC_UnlockDc ( dc );
+ return TRUE;
+ }
+
+ if ( dc->dclevel.hPath )
+ {
+ DPRINT1("BeginPath 1 0x%x\n", dc->dclevel.hPath);
+ if ( !(dc->dclevel.flPath & DCPATH_SAVE) )
+ { // Remove previous handle.
+ if (!PATH_Delete(dc->dclevel.hPath))
+ {
+ DC_UnlockDc ( dc );
+ return FALSE;
+ }
+ }
+ else
+ { // Clear flags and Handle.
+ dc->dclevel.flPath &= ~(DCPATH_SAVE|DCPATH_ACTIVE);
+ dc->dclevel.hPath = NULL;
+ }
+ }
+ pPath = PATH_AllocPathWithHandle();
+ if (!pPath)
+ {
+ SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
+ return FALSE;
+ }
+ dc->dclevel.flPath |= DCPATH_ACTIVE; // Set active ASAP!
+
+ dc->dclevel.hPath = pPath->BaseObject.hHmgr;
+
+ DPRINT1("BeginPath 2 h 0x%x p 0x%x\n", dc->dclevel.hPath, pPath);
+ // Path handles are shared. Also due to recursion with in the same thread.
+ GDIOBJ_UnlockObjByPtr((POBJ)pPath); // Unlock
+ pPath = PATH_LockPath(dc->dclevel.hPath); // Share Lock.
+
+ /* Make sure that path is empty */
+ PATH_EmptyPath( pPath );
+
+ /* Initialize variables for new path */
+ pPath->newStroke = TRUE;
+ pPath->state = PATH_Open;
+
+ PATH_UnlockPath(pPath);
+ DC_UnlockDc ( dc );
+ return TRUE;
+}
+
+BOOL
+APIENTRY
+NtGdiCloseFigure(HDC hDC)
+{
+ BOOL Ret = FALSE; // default to failure
+ PDC pDc;
+ PPATH pPath;
+
+ DPRINT("Enter %s\n", __FUNCTION__);
+
+ pDc = DC_LockDc(hDC);
+ if (!pDc)
+ {
+ SetLastWin32Error(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+ pPath = PATH_LockPath( pDc->dclevel.hPath );
+ if (!pPath)
+ {
+ DC_UnlockDc(pDc);
+ return FALSE;
+ }
+
+ if (pPath->state==PATH_Open)
+ {
+ IntGdiCloseFigure(pPath);
+ Ret = TRUE;
+ }
+ else
+ {
+ // FIXME: check if lasterror is set correctly
+ SetLastWin32Error(ERROR_CAN_NOT_COMPLETE);
+ }
+
+ PATH_UnlockPath( pPath );
+ DC_UnlockDc(pDc);
+ return Ret;
+}
+
+BOOL
+APIENTRY
+NtGdiEndPath(HDC hDC)
+{
+ BOOL ret = TRUE;
+ PPATH pPath;
+ PDC dc = DC_LockDc ( hDC );
+
+ if ( !dc )
+ {
+ SetLastWin32Error(ERROR_INVALID_HANDLE);
+ return FALSE;
+ }
+
+ pPath = PATH_LockPath( dc->dclevel.hPath );
+ if (!pPath)
+ {
+ DC_UnlockDc ( dc );
+ return FALSE;
+ }
+ /* Check that path is currently being constructed */
+ if ( (pPath->state != PATH_Open) || !(dc->dclevel.flPath & DCPATH_ACTIVE) )
+ {
+ DPRINT1("EndPath ERROR! 0x%x\n", dc->dclevel.hPath);
+ SetLastWin32Error(ERROR_CAN_NOT_COMPLETE);
+ ret = FALSE;
+ }
+ /* Set flag to indicate that path is finished */
+ else
+ {
+ DPRINT1("EndPath 0x%x\n", dc->dclevel.hPath);
+ pPath->state = PATH_Closed;
+ dc->dclevel.flPath &= ~DCPATH_ACTIVE;
+ }
+ PATH_UnlockPath( pPath );
+ DC_UnlockDc ( dc );
+ return ret;
+}
+
+BOOL
+APIENTRY
+NtGdiFillPath(HDC hDC)
+{
+ BOOL ret = FALSE;
+ PPATH pPath;
+ PDC_ATTR pdcattr;
+ PDC dc = DC_LockDc ( hDC );
+
+ if ( !dc )
+ {
+ SetLastWin32Error(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+ pPath = PATH_LockPath( dc->dclevel.hPath );
+ if (!pPath)
+ {
+ DC_UnlockDc ( dc );
+ return FALSE;
+ }
+
+ DC_vPrepareDCsForBlit(dc, dc->rosdc.CombinedClip->rclBounds,
+ NULL, dc->rosdc.CombinedClip->rclBounds);
+
+ pdcattr = dc->pdcattr;
+
+ if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
+ DC_vUpdateLineBrush(dc);
+
+ if (pdcattr->ulDirty_ & (DIRTY_FILL | DC_BRUSH_DIRTY))
+ DC_vUpdateFillBrush(dc);
+
+ ret = PATH_FillPath( dc, pPath );
+ if ( ret )
+ {
+ /* FIXME: Should the path be emptied even if conversion
+ failed? */
+ PATH_EmptyPath( pPath );
+ }
+
+ PATH_UnlockPath( pPath );
+ DC_vFinishBlit(dc, NULL);
+ DC_UnlockDc ( dc );
+ return ret;
+}
+
+BOOL
+APIENTRY
+NtGdiFlattenPath(HDC hDC)
+{
+ BOOL Ret = FALSE;
+ DC *pDc;
+ PPATH pPath;
+
+ DPRINT("Enter %s\n", __FUNCTION__);
+
+ pDc = DC_LockDc(hDC);
+ if (!pDc)
+ {
+ SetLastWin32Error(ERROR_INVALID_HANDLE);
+ return FALSE;
+ }
+
+ pPath = PATH_LockPath( pDc->dclevel.hPath );
+ if (!pPath)
+ {
+ DC_UnlockDc ( pDc );
+ return FALSE;
+ }
+ if (pPath->state == PATH_Open)
+ Ret = PATH_FlattenPath(pPath);
+
+ PATH_UnlockPath( pPath );
+ DC_UnlockDc(pDc);
+ return Ret;
+}
+
+
+BOOL
+APIENTRY
+NtGdiGetMiterLimit(
+ IN HDC hdc,
+ OUT PDWORD pdwOut)
+{
+ DC *pDc;
+ gxf_long worker;
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ if (!(pDc = DC_LockDc(hdc)))
+ {
+ SetLastWin32Error(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+
+ worker.f = pDc->dclevel.laPath.eMiterLimit;
+
+ if (pdwOut)
+ {
+ _SEH2_TRY
+ {
+ ProbeForWrite(pdwOut,
+ sizeof(DWORD),
+ 1);
+ *pdwOut = worker.l;
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ Status = _SEH2_GetExceptionCode();
+ }
+ _SEH2_END;
+ if (!NT_SUCCESS(Status))
+ {
+ SetLastNtError(Status);
+ DC_UnlockDc(pDc);
+ return FALSE;
+ }
+ }
+
+ DC_UnlockDc(pDc);
+ return TRUE;
+
+}
+
+INT
+APIENTRY
+NtGdiGetPath(
+ HDC hDC,
+ LPPOINT Points,
+ LPBYTE Types,
+ INT nSize)
+{
+ INT ret = -1;
+ PPATH pPath;
+
+ DC *dc = DC_LockDc(hDC);
+ if (!dc)
+ {
+ DPRINT1("Can't lock dc!\n");
+ SetLastWin32Error(ERROR_INVALID_PARAMETER);
+ return -1;
+ }
+
+ pPath = PATH_LockPath( dc->dclevel.hPath );
+ if (!pPath)
+ {
+ DC_UnlockDc ( dc );
+ return -1;
+ }
+
+ if (pPath->state != PATH_Closed)
+ {
+ SetLastWin32Error(ERROR_CAN_NOT_COMPLETE);
+ goto done;
+ }
+
+ if (nSize==0)
+ {
+ ret = pPath->numEntriesUsed;
+ }
+ else if(nSize<pPath->numEntriesUsed)
+ {
+ SetLastWin32Error(ERROR_INVALID_PARAMETER);
+ goto done;
+ }
+ else
+ {
+ _SEH2_TRY
+ {
+ memcpy(Points, pPath->pPoints, sizeof(POINT)*pPath->numEntriesUsed);
+ memcpy(Types, pPath->pFlags, sizeof(BYTE)*pPath->numEntriesUsed);
+
+ /* Convert the points to logical coordinates */
+ IntDPtoLP(dc, Points, pPath->numEntriesUsed);
+
+ ret = pPath->numEntriesUsed;
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ SetLastNtError(_SEH2_GetExceptionCode());
+ }
+ _SEH2_END
+ }
+
+done:
+ PATH_UnlockPath( pPath );
+ DC_UnlockDc(dc);
+ return ret;
+}
+
+HRGN
+APIENTRY
+NtGdiPathToRegion(HDC hDC)
+{
+ PPATH pPath;
+ HRGN hrgnRval = 0;
+ DC *pDc;
+ PDC_ATTR pdcattr;
+
+ DPRINT("Enter %s\n", __FUNCTION__);
+
+ pDc = DC_LockDc(hDC);
+ if (!pDc)
+ {
+ SetLastWin32Error(ERROR_INVALID_PARAMETER);
+ return NULL;
+ }
+
+ pdcattr = pDc->pdcattr;
+
+ pPath = PATH_LockPath( pDc->dclevel.hPath );
+ if (!pPath)
+ {
+ DC_UnlockDc ( pDc );
+ return NULL;
+ }
+
+ if (pPath->state!=PATH_Closed)
+ {
+ //FIXME: check that setlasterror is being called correctly
+ SetLastWin32Error(ERROR_CAN_NOT_COMPLETE);
+ }
+ else
+ {
+ /* FIXME: Should we empty the path even if conversion failed? */
+ if(PATH_PathToRegion(pPath, pdcattr->jFillMode, &hrgnRval))
+ PATH_EmptyPath(pPath);
+ }
+
+ PATH_UnlockPath( pPath );
+ DC_UnlockDc(pDc);
+ return hrgnRval;
+}
+
+BOOL
+APIENTRY
+NtGdiSetMiterLimit(
+ IN HDC hdc,
+ IN DWORD dwNew,
+ IN OUT OPTIONAL PDWORD pdwOut)
+{
+ DC *pDc;
+ gxf_long worker, worker1;
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ if (!(pDc = DC_LockDc(hdc)))
+ {
+ SetLastWin32Error(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+
+ worker.l = dwNew;
+ worker1.f = pDc->dclevel.laPath.eMiterLimit;
+ pDc->dclevel.laPath.eMiterLimit = worker.f;
+
+ if (pdwOut)
+ {
+ _SEH2_TRY
+ {
+ ProbeForWrite(pdwOut,
+ sizeof(DWORD),
+ 1);
+ *pdwOut = worker1.l;
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ Status = _SEH2_GetExceptionCode();
+ }
+ _SEH2_END;
+ if (!NT_SUCCESS(Status))
+ {
+ SetLastNtError(Status);
+ DC_UnlockDc(pDc);
+ return FALSE;
+ }
+ }
+
+ DC_UnlockDc(pDc);
+ return TRUE;
+}
+
+BOOL
+APIENTRY
+NtGdiStrokeAndFillPath(HDC hDC)
+{
+ DC *pDc;
+ PDC_ATTR pdcattr;
+ PPATH pPath;
+ BOOL bRet = FALSE;
+
+ DPRINT1("Enter %s\n", __FUNCTION__);
+
+ if (!(pDc = DC_LockDc(hDC)))
+ {
+ SetLastWin32Error(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+ pPath = PATH_LockPath( pDc->dclevel.hPath );
+ if (!pPath)
+ {
+ DC_UnlockDc ( pDc );
+ return FALSE;
+ }
+
+ DC_vPrepareDCsForBlit(pDc, pDc->rosdc.CombinedClip->rclBounds,
+ NULL, pDc->rosdc.CombinedClip->rclBounds);
+
+ pdcattr = pDc->pdcattr;
+
+ if (pdcattr->ulDirty_ & (DIRTY_FILL | DC_BRUSH_DIRTY))
+ DC_vUpdateFillBrush(pDc);
+
+ if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
+ DC_vUpdateLineBrush(pDc);
+
+ bRet = PATH_FillPath(pDc, pPath);
+ if (bRet) bRet = PATH_StrokePath(pDc, pPath);
+ if (bRet) PATH_EmptyPath(pPath);
+
+ PATH_UnlockPath( pPath );
+ DC_vFinishBlit(pDc, NULL);
+ DC_UnlockDc(pDc);
+ return bRet;
+}
+
+BOOL
+APIENTRY
+NtGdiStrokePath(HDC hDC)
+{
+ DC *pDc;
+ PDC_ATTR pdcattr;
+ PPATH pPath;
+ BOOL bRet = FALSE;
+
+ DPRINT("Enter %s\n", __FUNCTION__);
+
+ if (!(pDc = DC_LockDc(hDC)))
+ {
+ SetLastWin32Error(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+ pPath = PATH_LockPath( pDc->dclevel.hPath );
+ if (!pPath)
+ {
+ DC_UnlockDc ( pDc );
+ return FALSE;
+ }
+
+ DC_vPrepareDCsForBlit(pDc, pDc->rosdc.CombinedClip->rclBounds,
+ NULL, pDc->rosdc.CombinedClip->rclBounds);
+
+ pdcattr = pDc->pdcattr;
+
+ if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
+ DC_vUpdateLineBrush(pDc);
+
+ bRet = PATH_StrokePath(pDc, pPath);
+
+ DC_vFinishBlit(pDc, NULL);
+ PATH_EmptyPath(pPath);
+
+ PATH_UnlockPath( pPath );
+ DC_UnlockDc(pDc);
+ return bRet;
+}
+
+BOOL
+APIENTRY
+NtGdiWidenPath(HDC hDC)
+{
+ BOOL Ret;
+ PDC pdc = DC_LockDc ( hDC );
+ if ( !pdc )
+ {
+ SetLastWin32Error(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+ Ret = PATH_WidenPath(pdc);
+ DC_UnlockDc ( pdc );
+ return Ret;
+}
+
+/* EOF */
--- /dev/null
- return TRUE;
+/*
+ * ReactOS W32 Subsystem
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*
+ * GDI region objects. Shamelessly ripped out from the X11 distribution
+ * Thanks for the nice licence.
+ *
+ * Copyright 1993, 1994, 1995 Alexandre Julliard
+ * Modifications and additions: Copyright 1998 Huw Davies
+ * 1999 Alex Korobka
+ *
+ * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/************************************************************************
+
+Copyright (c) 1987, 1988 X Consortium
+
+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 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
+X CONSORTIUM 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.
+
+Except as contained in this notice, the name of the X Consortium shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from the X Consortium.
+
+
+Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Digital not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+************************************************************************/
+/*
+ * The functions in this file implement the Region abstraction, similar to one
+ * used in the X11 sample server. A Region is simply an area, as the name
+ * implies, and is implemented as a "y-x-banded" array of rectangles. To
+ * explain: Each Region is made up of a certain number of rectangles sorted
+ * by y coordinate first, and then by x coordinate.
+ *
+ * Furthermore, the rectangles are banded such that every rectangle with a
+ * given upper-left y coordinate (y1) will have the same lower-right y
+ * coordinate (y2) and vice versa. If a rectangle has scanlines in a band, it
+ * will span the entire vertical distance of the band. This means that some
+ * areas that could be merged into a taller rectangle will be represented as
+ * several shorter rectangles to account for shorter rectangles to its left
+ * or right but within its "vertical scope".
+ *
+ * An added constraint on the rectangles is that they must cover as much
+ * horizontal area as possible. E.g. no two rectangles in a band are allowed
+ * to touch.
+ *
+ * Whenever possible, bands will be merged together to cover a greater vertical
+ * distance (and thus reduce the number of rectangles). Two bands can be merged
+ * only if the bottom of one touches the top of the other and they have
+ * rectangles in the same places (of the same width, of course). This maintains
+ * the y-x-banding that's so nice to have...
+ */
+
+#include <win32k.h>
+
+#define NDEBUG
+#include <debug.h>
+
+PROSRGNDATA prgnDefault = NULL;
+HRGN hrgnDefault = NULL;
+
+// Internal Functions
+
+#if 1
+#define COPY_RECTS(dest, src, nRects) \
+ do { \
+ PRECTL xDest = (dest); \
+ PRECTL xSrc = (src); \
+ UINT xRects = (nRects); \
+ while(xRects-- > 0) { \
+ *(xDest++) = *(xSrc++); \
+ } \
+ } while(0)
+#else
+#define COPY_RECTS(dest, src, nRects) RtlCopyMemory(dest, src, (nRects) * sizeof(RECTL))
+#endif
+
+#define EMPTY_REGION(pReg) { \
+ (pReg)->rdh.nCount = 0; \
+ (pReg)->rdh.rcBound.left = (pReg)->rdh.rcBound.top = 0; \
+ (pReg)->rdh.rcBound.right = (pReg)->rdh.rcBound.bottom = 0; \
+ (pReg)->rdh.iType = RDH_RECTANGLES; \
+}
+
+#define REGION_NOT_EMPTY(pReg) pReg->rdh.nCount
+
+#define INRECT(r, x, y) \
+ ( ( ((r).right > x)) && \
+ ( ((r).left <= x)) && \
+ ( ((r).bottom > y)) && \
+ ( ((r).top <= y)) )
+
+/* 1 if two RECTs overlap.
+ * 0 if two RECTs do not overlap.
+ */
+#define EXTENTCHECK(r1, r2) \
+ ((r1)->right > (r2)->left && \
+ (r1)->left < (r2)->right && \
+ (r1)->bottom > (r2)->top && \
+ (r1)->top < (r2)->bottom)
+
+/*
+ * In scan converting polygons, we want to choose those pixels
+ * which are inside the polygon. Thus, we add .5 to the starting
+ * x coordinate for both left and right edges. Now we choose the
+ * first pixel which is inside the pgon for the left edge and the
+ * first pixel which is outside the pgon for the right edge.
+ * Draw the left pixel, but not the right.
+ *
+ * How to add .5 to the starting x coordinate:
+ * If the edge is moving to the right, then subtract dy from the
+ * error term from the general form of the algorithm.
+ * If the edge is moving to the left, then add dy to the error term.
+ *
+ * The reason for the difference between edges moving to the left
+ * and edges moving to the right is simple: If an edge is moving
+ * to the right, then we want the algorithm to flip immediately.
+ * If it is moving to the left, then we don't want it to flip until
+ * we traverse an entire pixel.
+ */
+#define BRESINITPGON(dy, x1, x2, xStart, d, m, m1, incr1, incr2) { \
+ int dx; /* local storage */ \
+\
+ /* \
+ * if the edge is horizontal, then it is ignored \
+ * and assumed not to be processed. Otherwise, do this stuff. \
+ */ \
+ if ((dy) != 0) { \
+ xStart = (x1); \
+ dx = (x2) - xStart; \
+ if (dx < 0) { \
+ m = dx / (dy); \
+ m1 = m - 1; \
+ incr1 = -2 * dx + 2 * (dy) * m1; \
+ incr2 = -2 * dx + 2 * (dy) * m; \
+ d = 2 * m * (dy) - 2 * dx - 2 * (dy); \
+ } else { \
+ m = dx / (dy); \
+ m1 = m + 1; \
+ incr1 = 2 * dx - 2 * (dy) * m1; \
+ incr2 = 2 * dx - 2 * (dy) * m; \
+ d = -2 * m * (dy) + 2 * dx; \
+ } \
+ } \
+}
+
+#define BRESINCRPGON(d, minval, m, m1, incr1, incr2) { \
+ if (m1 > 0) { \
+ if (d > 0) { \
+ minval += m1; \
+ d += incr1; \
+ } \
+ else { \
+ minval += m; \
+ d += incr2; \
+ } \
+ } else {\
+ if (d >= 0) { \
+ minval += m1; \
+ d += incr1; \
+ } \
+ else { \
+ minval += m; \
+ d += incr2; \
+ } \
+ } \
+}
+
+/*
+ * This structure contains all of the information needed
+ * to run the bresenham algorithm.
+ * The variables may be hardcoded into the declarations
+ * instead of using this structure to make use of
+ * register declarations.
+ */
+typedef struct
+{
+ INT minor_axis; /* minor axis */
+ INT d; /* decision variable */
+ INT m, m1; /* slope and slope+1 */
+ INT incr1, incr2; /* error increments */
+} BRESINFO;
+
+
+#define BRESINITPGONSTRUCT(dmaj, min1, min2, bres) \
+ BRESINITPGON(dmaj, min1, min2, bres.minor_axis, bres.d, \
+ bres.m, bres.m1, bres.incr1, bres.incr2)
+
+#define BRESINCRPGONSTRUCT(bres) \
+ BRESINCRPGON(bres.d, bres.minor_axis, bres.m, bres.m1, bres.incr1, bres.incr2)
+
+
+
+/*
+ * These are the data structures needed to scan
+ * convert regions. Two different scan conversion
+ * methods are available -- the even-odd method, and
+ * the winding number method.
+ * The even-odd rule states that a point is inside
+ * the polygon if a ray drawn from that point in any
+ * direction will pass through an odd number of
+ * path segments.
+ * By the winding number rule, a point is decided
+ * to be inside the polygon if a ray drawn from that
+ * point in any direction passes through a different
+ * number of clockwise and counter-clockwise path
+ * segments.
+ *
+ * These data structures are adapted somewhat from
+ * the algorithm in (Foley/Van Dam) for scan converting
+ * polygons.
+ * The basic algorithm is to start at the top (smallest y)
+ * of the polygon, stepping down to the bottom of
+ * the polygon by incrementing the y coordinate. We
+ * keep a list of edges which the current scanline crosses,
+ * sorted by x. This list is called the Active Edge Table (AET)
+ * As we change the y-coordinate, we update each entry in
+ * in the active edge table to reflect the edges new xcoord.
+ * This list must be sorted at each scanline in case
+ * two edges intersect.
+ * We also keep a data structure known as the Edge Table (ET),
+ * which keeps track of all the edges which the current
+ * scanline has not yet reached. The ET is basically a
+ * list of ScanLineList structures containing a list of
+ * edges which are entered at a given scanline. There is one
+ * ScanLineList per scanline at which an edge is entered.
+ * When we enter a new edge, we move it from the ET to the AET.
+ *
+ * From the AET, we can implement the even-odd rule as in
+ * (Foley/Van Dam).
+ * The winding number rule is a little trickier. We also
+ * keep the EdgeTableEntries in the AET linked by the
+ * nextWETE (winding EdgeTableEntry) link. This allows
+ * the edges to be linked just as before for updating
+ * purposes, but only uses the edges linked by the nextWETE
+ * link as edges representing spans of the polygon to
+ * drawn (as with the even-odd rule).
+ */
+
+/*
+ * for the winding number rule
+ */
+#define CLOCKWISE 1
+#define COUNTERCLOCKWISE -1
+
+typedef struct _EdgeTableEntry
+{
+ INT ymax; /* ycoord at which we exit this edge. */
+ BRESINFO bres; /* Bresenham info to run the edge */
+ struct _EdgeTableEntry *next; /* next in the list */
+ struct _EdgeTableEntry *back; /* for insertion sort */
+ struct _EdgeTableEntry *nextWETE; /* for winding num rule */
+ int ClockWise; /* flag for winding number rule */
+} EdgeTableEntry;
+
+
+typedef struct _ScanLineList
+{
+ INT scanline; /* the scanline represented */
+ EdgeTableEntry *edgelist; /* header node */
+ struct _ScanLineList *next; /* next in the list */
+} ScanLineList;
+
+
+typedef struct
+{
+ INT ymax; /* ymax for the polygon */
+ INT ymin; /* ymin for the polygon */
+ ScanLineList scanlines; /* header node */
+} EdgeTable;
+
+
+/*
+ * Here is a struct to help with storage allocation
+ * so we can allocate a big chunk at a time, and then take
+ * pieces from this heap when we need to.
+ */
+#define SLLSPERBLOCK 25
+
+typedef struct _ScanLineListBlock
+{
+ ScanLineList SLLs[SLLSPERBLOCK];
+ struct _ScanLineListBlock *next;
+} ScanLineListBlock;
+
+
+/*
+ *
+ * a few macros for the inner loops of the fill code where
+ * performance considerations don't allow a procedure call.
+ *
+ * Evaluate the given edge at the given scanline.
+ * If the edge has expired, then we leave it and fix up
+ * the active edge table; otherwise, we increment the
+ * x value to be ready for the next scanline.
+ * The winding number rule is in effect, so we must notify
+ * the caller when the edge has been removed so he
+ * can reorder the Winding Active Edge Table.
+ */
+#define EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET) { \
+ if (pAET->ymax == y) { /* leaving this edge */ \
+ pPrevAET->next = pAET->next; \
+ pAET = pPrevAET->next; \
+ fixWAET = 1; \
+ if (pAET) \
+ pAET->back = pPrevAET; \
+ } \
+ else { \
+ BRESINCRPGONSTRUCT(pAET->bres); \
+ pPrevAET = pAET; \
+ pAET = pAET->next; \
+ } \
+}
+
+
+/*
+ * Evaluate the given edge at the given scanline.
+ * If the edge has expired, then we leave it and fix up
+ * the active edge table; otherwise, we increment the
+ * x value to be ready for the next scanline.
+ * The even-odd rule is in effect.
+ */
+#define EVALUATEEDGEEVENODD(pAET, pPrevAET, y) { \
+ if (pAET->ymax == y) { /* leaving this edge */ \
+ pPrevAET->next = pAET->next; \
+ pAET = pPrevAET->next; \
+ if (pAET) \
+ pAET->back = pPrevAET; \
+ } \
+ else { \
+ BRESINCRPGONSTRUCT(pAET->bres); \
+ pPrevAET = pAET; \
+ pAET = pAET->next; \
+ } \
+}
+
+/**************************************************************************
+ *
+ * Poly Regions
+ *
+ *************************************************************************/
+
+#define LARGE_COORDINATE 0x7fffffff /* FIXME */
+#define SMALL_COORDINATE 0x80000000
+
+/*
+ * Check to see if there is enough memory in the present region.
+ */
+static __inline int xmemcheck(ROSRGNDATA *reg, PRECTL *rect, PRECTL *firstrect)
+{
+ if ( (reg->rdh.nCount+1) * sizeof(RECT) >= reg->rdh.nRgnSize )
+ {
+ PRECTL temp;
+ DWORD NewSize = 2 * reg->rdh.nRgnSize;
+ if (NewSize < (reg->rdh.nCount + 1) * sizeof(RECT))
+ {
+ NewSize = (reg->rdh.nCount + 1) * sizeof(RECT);
+ }
+ temp = ExAllocatePoolWithTag(PagedPool, NewSize, TAG_REGION);
+
+ if (temp == NULL)
+ {
+ return 0;
+ }
+
+ /* Copy the rectangles */
+ COPY_RECTS(temp, *firstrect, reg->rdh.nCount);
+
+ reg->rdh.nRgnSize = NewSize;
+ if (*firstrect != ®->rdh.rcBound)
+ {
+ ExFreePoolWithTag(*firstrect, TAG_REGION);
+ }
+ *firstrect = temp;
+ *rect = (*firstrect)+reg->rdh.nCount;
+ }
+ return 1;
+}
+
+#define MEMCHECK(reg, rect, firstrect) xmemcheck(reg,&(rect),(PRECTL *)&(firstrect))
+
+typedef void (FASTCALL *overlapProcp)(PROSRGNDATA, PRECT, PRECT, PRECT, PRECT, INT, INT);
+typedef void (FASTCALL *nonOverlapProcp)(PROSRGNDATA, PRECT, PRECT, INT, INT);
+
+// Number of points to buffer before sending them off to scanlines() : Must be an even number
+#define NUMPTSTOBUFFER 200
+
+#define RGN_DEFAULT_RECTS 2
+
+// used to allocate buffers for points and link the buffers together
+
+typedef struct _POINTBLOCK
+{
+ POINT pts[NUMPTSTOBUFFER];
+ struct _POINTBLOCK *next;
+} POINTBLOCK;
+
+#ifndef NDEBUG
+/*
+ * This function is left there for debugging purposes.
+ */
+
+VOID FASTCALL
+IntDumpRegion(HRGN hRgn)
+{
+ ROSRGNDATA *Data;
+
+ Data = RGNOBJAPI_Lock(hRgn, NULL);
+ if (Data == NULL)
+ {
+ DbgPrint("IntDumpRegion called with invalid region!\n");
+ return;
+ }
+
+ DbgPrint("IntDumpRegion(%x): %d,%d-%d,%d %d\n",
+ hRgn,
+ Data->rdh.rcBound.left,
+ Data->rdh.rcBound.top,
+ Data->rdh.rcBound.right,
+ Data->rdh.rcBound.bottom,
+ Data->rdh.iType);
+
+ RGNOBJAPI_Unlock(Data);
+}
+#endif /* not NDEBUG */
+
+
+INT
+FASTCALL
+REGION_Complexity( PROSRGNDATA obj )
+{
+ if (!obj) return NULLREGION;
+ switch(obj->rdh.nCount)
+ {
+ DPRINT("Region Complexity -> %d",obj->rdh.nCount);
+ case 0: return NULLREGION;
+ case 1: return SIMPLEREGION;
+ default: return COMPLEXREGION;
+ }
+}
+
+static
+BOOL
+FASTCALL
+REGION_CopyRegion(
+ PROSRGNDATA dst,
+ PROSRGNDATA src
+)
+{
+ if (dst != src) // don't want to copy to itself
+ {
+ if (dst->rdh.nRgnSize < src->rdh.nCount * sizeof(RECT))
+ {
+ PRECTL temp;
+
+ temp = ExAllocatePoolWithTag(PagedPool, src->rdh.nCount * sizeof(RECT), TAG_REGION );
+ if (!temp)
+ return FALSE;
+
+ if (dst->Buffer && dst->Buffer != &dst->rdh.rcBound)
+ ExFreePoolWithTag(dst->Buffer, TAG_REGION); //free the old buffer
+ dst->Buffer = temp;
+ dst->rdh.nRgnSize = src->rdh.nCount * sizeof(RECT); //size of region buffer
+ }
+ dst->rdh.nCount = src->rdh.nCount; //number of rectangles present in Buffer
+ dst->rdh.rcBound.left = src->rdh.rcBound.left;
+ dst->rdh.rcBound.top = src->rdh.rcBound.top;
+ dst->rdh.rcBound.right = src->rdh.rcBound.right;
+ dst->rdh.rcBound.bottom = src->rdh.rcBound.bottom;
+ dst->rdh.iType = src->rdh.iType;
+ COPY_RECTS(dst->Buffer, src->Buffer, src->rdh.nCount);
+ }
+ return TRUE;
+}
+
+static void FASTCALL
+REGION_SetExtents(ROSRGNDATA *pReg)
+{
+ RECTL *pRect, *pRectEnd, *pExtents;
+
+ if (pReg->rdh.nCount == 0)
+ {
+ pReg->rdh.rcBound.left = 0;
+ pReg->rdh.rcBound.top = 0;
+ pReg->rdh.rcBound.right = 0;
+ pReg->rdh.rcBound.bottom = 0;
+ pReg->rdh.iType = RDH_RECTANGLES;
+ return;
+ }
+
+ pExtents = &pReg->rdh.rcBound;
+ pRect = pReg->Buffer;
+ pRectEnd = pReg->Buffer + pReg->rdh.nCount - 1;
+
+ /*
+ * Since pRect is the first rectangle in the region, it must have the
+ * smallest top and since pRectEnd is the last rectangle in the region,
+ * it must have the largest bottom, because of banding. Initialize left and
+ * right from pRect and pRectEnd, resp., as good things to initialize them
+ * to...
+ */
+ pExtents->left = pRect->left;
+ pExtents->top = pRect->top;
+ pExtents->right = pRectEnd->right;
+ pExtents->bottom = pRectEnd->bottom;
+
+ while (pRect <= pRectEnd)
+ {
+ if (pRect->left < pExtents->left)
+ pExtents->left = pRect->left;
+ if (pRect->right > pExtents->right)
+ pExtents->right = pRect->right;
+ pRect++;
+ }
+ pReg->rdh.iType = RDH_RECTANGLES;
+}
+
+// FIXME: This seems to be wrong
+/***********************************************************************
+ * REGION_CropAndOffsetRegion
+ */
+BOOL FASTCALL
+REGION_CropAndOffsetRegion(
+ PROSRGNDATA rgnDst,
+ PROSRGNDATA rgnSrc,
+ const RECTL *rect,
+ const POINTL *offset
+)
+{
+ POINT pt = {0,0};
+ const POINT *off = offset;
+
+ if (!off) off = &pt;
+
+ if (!rect) // just copy and offset
+ {
+ PRECTL xrect;
+ if (rgnDst == rgnSrc)
+ {
+ if (off->x || off->y)
+ xrect = rgnDst->Buffer;
+ else
+ return TRUE;
+ }
+ else
+ {
+ xrect = ExAllocatePoolWithTag(PagedPool, rgnSrc->rdh.nCount * sizeof(RECT), TAG_REGION);
+ if(!xrect)
+ return FALSE;
+ if (rgnDst->Buffer && rgnDst->Buffer != &rgnDst->rdh.rcBound)
+ ExFreePoolWithTag(rgnDst->Buffer, TAG_REGION); //free the old buffer. will be assigned to xrect below.
+ }
+
+ if (rgnDst != rgnSrc)
+ {
+ *rgnDst = *rgnSrc;
+ }
+
+ if (off->x || off->y)
+ {
+ ULONG i;
+ for (i = 0; i < rgnDst->rdh.nCount; i++)
+ {
+ xrect[i].left = (rgnSrc->Buffer + i)->left + off->x;
+ xrect[i].right = (rgnSrc->Buffer + i)->right + off->x;
+ xrect[i].top = (rgnSrc->Buffer + i)->top + off->y;
+ xrect[i].bottom = (rgnSrc->Buffer + i)->bottom + off->y;
+ }
+ rgnDst->rdh.rcBound.left += off->x;
+ rgnDst->rdh.rcBound.right += off->x;
+ rgnDst->rdh.rcBound.top += off->y;
+ rgnDst->rdh.rcBound.bottom += off->y;
+ }
+ else
+ {
+ COPY_RECTS(xrect, rgnSrc->Buffer, rgnDst->rdh.nCount);
+ }
+
+ rgnDst->Buffer = xrect;
+ }
+ else if ((rect->left >= rect->right) ||
+ (rect->top >= rect->bottom) ||
+ !EXTENTCHECK(rect, &rgnSrc->rdh.rcBound))
+ {
+ goto empty;
+ }
+ else // region box and clipping rect appear to intersect
+ {
+ PRECTL lpr, rpr;
+ ULONG i, j, clipa, clipb;
+ INT left = rgnSrc->rdh.rcBound.right + off->x;
+ INT right = rgnSrc->rdh.rcBound.left + off->x;
+
+ for (clipa = 0; (rgnSrc->Buffer + clipa)->bottom <= rect->top; clipa++)
+ //region and rect intersect so we stop before clipa > rgnSrc->rdh.nCount
+ ; // skip bands above the clipping rectangle
+
+ for (clipb = clipa; clipb < rgnSrc->rdh.nCount; clipb++)
+ if ((rgnSrc->Buffer + clipb)->top >= rect->bottom)
+ break; // and below it
+
+ // clipa - index of the first rect in the first intersecting band
+ // clipb - index of the last rect in the last intersecting band
+
+ if ((rgnDst != rgnSrc) && (rgnDst->rdh.nCount < (i = (clipb - clipa))))
+ {
+ PRECTL temp;
+ temp = ExAllocatePoolWithTag(PagedPool, i * sizeof(RECT), TAG_REGION);
+ if (!temp)
+ return FALSE;
+
+ if (rgnDst->Buffer && rgnDst->Buffer != &rgnDst->rdh.rcBound)
+ ExFreePoolWithTag(rgnDst->Buffer, TAG_REGION); //free the old buffer
+ rgnDst->Buffer = temp;
+ rgnDst->rdh.nCount = i;
+ rgnDst->rdh.nRgnSize = i * sizeof(RECT);
+ }
+
+ for (i = clipa, j = 0; i < clipb ; i++)
+ {
+ // i - src index, j - dst index, j is always <= i for obvious reasons
+
+ lpr = rgnSrc->Buffer + i;
+
+ if (lpr->left < rect->right && lpr->right > rect->left)
+ {
+ rpr = rgnDst->Buffer + j;
+
+ rpr->top = lpr->top + off->y;
+ rpr->bottom = lpr->bottom + off->y;
+ rpr->left = ((lpr->left > rect->left) ? lpr->left : rect->left) + off->x;
+ rpr->right = ((lpr->right < rect->right) ? lpr->right : rect->right) + off->x;
+
+ if (rpr->left < left) left = rpr->left;
+ if (rpr->right > right) right = rpr->right;
+
+ j++;
+ }
+ }
+
+ if (j == 0) goto empty;
+
+ rgnDst->rdh.rcBound.left = left;
+ rgnDst->rdh.rcBound.right = right;
+
+ left = rect->top + off->y;
+ right = rect->bottom + off->y;
+
+ rgnDst->rdh.nCount = j--;
+ for (i = 0; i <= j; i++) // fixup top band
+ if ((rgnDst->Buffer + i)->top < left)
+ (rgnDst->Buffer + i)->top = left;
+ else
+ break;
+
+ for (i = j; i > 0; i--) // fixup bottom band
+ if ((rgnDst->Buffer + i)->bottom > right)
+ (rgnDst->Buffer + i)->bottom = right;
+ else
+ break;
+
+ rgnDst->rdh.rcBound.top = (rgnDst->Buffer)->top;
+ rgnDst->rdh.rcBound.bottom = (rgnDst->Buffer + j)->bottom;
+
+ rgnDst->rdh.iType = RDH_RECTANGLES;
+ }
+
+ return TRUE;
+
+empty:
+ if (!rgnDst->Buffer)
+ {
+ rgnDst->Buffer = ExAllocatePoolWithTag(PagedPool, RGN_DEFAULT_RECTS * sizeof(RECT), TAG_REGION);
+ if (rgnDst->Buffer)
+ {
+ rgnDst->rdh.nCount = RGN_DEFAULT_RECTS;
+ rgnDst->rdh.nRgnSize = RGN_DEFAULT_RECTS * sizeof(RECT);
+ }
+ else
+ return FALSE;
+ }
+ EMPTY_REGION(rgnDst);
+ return TRUE;
+}
+
+
+/*!
+ * Attempt to merge the rects in the current band with those in the
+ * previous one. Used only by REGION_RegionOp.
+ *
+ * Results:
+ * The new index for the previous band.
+ *
+ * \note Side Effects:
+ * If coalescing takes place:
+ * - rectangles in the previous band will have their bottom fields
+ * altered.
+ * - pReg->numRects will be decreased.
+ *
+ */
+static INT FASTCALL
+REGION_Coalesce(
+ PROSRGNDATA pReg, /* Region to coalesce */
+ INT prevStart, /* Index of start of previous band */
+ INT curStart /* Index of start of current band */
+)
+{
+ RECTL *pPrevRect; /* Current rect in previous band */
+ RECTL *pCurRect; /* Current rect in current band */
+ RECTL *pRegEnd; /* End of region */
+ INT curNumRects; /* Number of rectangles in current band */
+ INT prevNumRects; /* Number of rectangles in previous band */
+ INT bandtop; /* top coordinate for current band */
+
+ pRegEnd = pReg->Buffer + pReg->rdh.nCount;
+ pPrevRect = pReg->Buffer + prevStart;
+ prevNumRects = curStart - prevStart;
+
+ /*
+ * Figure out how many rectangles are in the current band. Have to do
+ * this because multiple bands could have been added in REGION_RegionOp
+ * at the end when one region has been exhausted.
+ */
+ pCurRect = pReg->Buffer + curStart;
+ bandtop = pCurRect->top;
+ for (curNumRects = 0;
+ (pCurRect != pRegEnd) && (pCurRect->top == bandtop);
+ curNumRects++)
+ {
+ pCurRect++;
+ }
+
+ if (pCurRect != pRegEnd)
+ {
+ /*
+ * If more than one band was added, we have to find the start
+ * of the last band added so the next coalescing job can start
+ * at the right place... (given when multiple bands are added,
+ * this may be pointless -- see above).
+ */
+ pRegEnd--;
+ while ((pRegEnd-1)->top == pRegEnd->top)
+ {
+ pRegEnd--;
+ }
+ curStart = pRegEnd - pReg->Buffer;
+ pRegEnd = pReg->Buffer + pReg->rdh.nCount;
+ }
+
+ if ((curNumRects == prevNumRects) && (curNumRects != 0))
+ {
+ pCurRect -= curNumRects;
+ /*
+ * The bands may only be coalesced if the bottom of the previous
+ * matches the top scanline of the current.
+ */
+ if (pPrevRect->bottom == pCurRect->top)
+ {
+ /*
+ * Make sure the bands have rects in the same places. This
+ * assumes that rects have been added in such a way that they
+ * cover the most area possible. I.e. two rects in a band must
+ * have some horizontal space between them.
+ */
+ do
+ {
+ if ((pPrevRect->left != pCurRect->left) ||
+ (pPrevRect->right != pCurRect->right))
+ {
+ /*
+ * The bands don't line up so they can't be coalesced.
+ */
+ return (curStart);
+ }
+ pPrevRect++;
+ pCurRect++;
+ prevNumRects -= 1;
+ }
+ while (prevNumRects != 0);
+
+ pReg->rdh.nCount -= curNumRects;
+ pCurRect -= curNumRects;
+ pPrevRect -= curNumRects;
+
+ /*
+ * The bands may be merged, so set the bottom of each rect
+ * in the previous band to that of the corresponding rect in
+ * the current band.
+ */
+ do
+ {
+ pPrevRect->bottom = pCurRect->bottom;
+ pPrevRect++;
+ pCurRect++;
+ curNumRects -= 1;
+ }
+ while (curNumRects != 0);
+
+ /*
+ * If only one band was added to the region, we have to backup
+ * curStart to the start of the previous band.
+ *
+ * If more than one band was added to the region, copy the
+ * other bands down. The assumption here is that the other bands
+ * came from the same region as the current one and no further
+ * coalescing can be done on them since it's all been done
+ * already... curStart is already in the right place.
+ */
+ if (pCurRect == pRegEnd)
+ {
+ curStart = prevStart;
+ }
+ else
+ {
+ do
+ {
+ *pPrevRect++ = *pCurRect++;
+ }
+ while (pCurRect != pRegEnd);
+ }
+ }
+ }
+ return (curStart);
+}
+
+/*!
+ * Apply an operation to two regions. Called by REGION_Union,
+ * REGION_Inverse, REGION_Subtract, REGION_Intersect...
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The new region is overwritten.
+ *
+ *\note The idea behind this function is to view the two regions as sets.
+ * Together they cover a rectangle of area that this function divides
+ * into horizontal bands where points are covered only by one region
+ * or by both. For the first case, the nonOverlapFunc is called with
+ * each the band and the band's upper and lower extents. For the
+ * second, the overlapFunc is called to process the entire band. It
+ * is responsible for clipping the rectangles in the band, though
+ * this function provides the boundaries.
+ * At the end of each band, the new region is coalesced, if possible,
+ * to reduce the number of rectangles in the region.
+ *
+ */
+static void FASTCALL
+REGION_RegionOp(
+ ROSRGNDATA *newReg, /* Place to store result */
+ ROSRGNDATA *reg1, /* First region in operation */
+ ROSRGNDATA *reg2, /* 2nd region in operation */
+ overlapProcp overlapFunc, /* Function to call for over-lapping bands */
+ nonOverlapProcp nonOverlap1Func, /* Function to call for non-overlapping bands in region 1 */
+ nonOverlapProcp nonOverlap2Func /* Function to call for non-overlapping bands in region 2 */
+)
+{
+ RECTL *r1; /* Pointer into first region */
+ RECTL *r2; /* Pointer into 2d region */
+ RECTL *r1End; /* End of 1st region */
+ RECTL *r2End; /* End of 2d region */
+ INT ybot; /* Bottom of intersection */
+ INT ytop; /* Top of intersection */
+ RECTL *oldRects; /* Old rects for newReg */
+ ULONG prevBand; /* Index of start of
+ * previous band in newReg */
+ ULONG curBand; /* Index of start of current band in newReg */
+ RECTL *r1BandEnd; /* End of current band in r1 */
+ RECTL *r2BandEnd; /* End of current band in r2 */
+ ULONG top; /* Top of non-overlapping band */
+ ULONG bot; /* Bottom of non-overlapping band */
+
+ /*
+ * Initialization:
+ * set r1, r2, r1End and r2End appropriately, preserve the important
+ * parts of the destination region until the end in case it's one of
+ * the two source regions, then mark the "new" region empty, allocating
+ * another array of rectangles for it to use.
+ */
+ r1 = reg1->Buffer;
+ r2 = reg2->Buffer;
+ r1End = r1 + reg1->rdh.nCount;
+ r2End = r2 + reg2->rdh.nCount;
+
+
+ /*
+ * newReg may be one of the src regions so we can't empty it. We keep a
+ * note of its rects pointer (so that we can free them later), preserve its
+ * extents and simply set numRects to zero.
+ */
+
+ oldRects = newReg->Buffer;
+ newReg->rdh.nCount = 0;
+
+ /*
+ * Allocate a reasonable number of rectangles for the new region. The idea
+ * is to allocate enough so the individual functions don't need to
+ * reallocate and copy the array, which is time consuming, yet we don't
+ * have to worry about using too much memory. I hope to be able to
+ * nuke the Xrealloc() at the end of this function eventually.
+ */
+ newReg->rdh.nRgnSize = max(reg1->rdh.nCount,reg2->rdh.nCount) * 2 * sizeof(RECT);
+
+ if (! (newReg->Buffer = ExAllocatePoolWithTag(PagedPool, newReg->rdh.nRgnSize, TAG_REGION)))
+ {
+ newReg->rdh.nRgnSize = 0;
+ return;
+ }
+
+ /*
+ * Initialize ybot and ytop.
+ * In the upcoming loop, ybot and ytop serve different functions depending
+ * on whether the band being handled is an overlapping or non-overlapping
+ * band.
+ * In the case of a non-overlapping band (only one of the regions
+ * has points in the band), ybot is the bottom of the most recent
+ * intersection and thus clips the top of the rectangles in that band.
+ * ytop is the top of the next intersection between the two regions and
+ * serves to clip the bottom of the rectangles in the current band.
+ * For an overlapping band (where the two regions intersect), ytop clips
+ * the top of the rectangles of both regions and ybot clips the bottoms.
+ */
+ if (reg1->rdh.rcBound.top < reg2->rdh.rcBound.top)
+ ybot = reg1->rdh.rcBound.top;
+ else
+ ybot = reg2->rdh.rcBound.top;
+
+ /*
+ * prevBand serves to mark the start of the previous band so rectangles
+ * can be coalesced into larger rectangles. qv. miCoalesce, above.
+ * In the beginning, there is no previous band, so prevBand == curBand
+ * (curBand is set later on, of course, but the first band will always
+ * start at index 0). prevBand and curBand must be indices because of
+ * the possible expansion, and resultant moving, of the new region's
+ * array of rectangles.
+ */
+ prevBand = 0;
+
+ do
+ {
+ curBand = newReg->rdh.nCount;
+
+ /*
+ * This algorithm proceeds one source-band (as opposed to a
+ * destination band, which is determined by where the two regions
+ * intersect) at a time. r1BandEnd and r2BandEnd serve to mark the
+ * rectangle after the last one in the current band for their
+ * respective regions.
+ */
+ r1BandEnd = r1;
+ while ((r1BandEnd != r1End) && (r1BandEnd->top == r1->top))
+ {
+ r1BandEnd++;
+ }
+
+ r2BandEnd = r2;
+ while ((r2BandEnd != r2End) && (r2BandEnd->top == r2->top))
+ {
+ r2BandEnd++;
+ }
+
+ /*
+ * First handle the band that doesn't intersect, if any.
+ *
+ * Note that attention is restricted to one band in the
+ * non-intersecting region at once, so if a region has n
+ * bands between the current position and the next place it overlaps
+ * the other, this entire loop will be passed through n times.
+ */
+ if (r1->top < r2->top)
+ {
+ top = max(r1->top,ybot);
+ bot = min(r1->bottom,r2->top);
+
+ if ((top != bot) && (nonOverlap1Func != NULL))
+ {
+ (* nonOverlap1Func) (newReg, r1, r1BandEnd, top, bot);
+ }
+
+ ytop = r2->top;
+ }
+ else if (r2->top < r1->top)
+ {
+ top = max(r2->top,ybot);
+ bot = min(r2->bottom,r1->top);
+
+ if ((top != bot) && (nonOverlap2Func != NULL))
+ {
+ (* nonOverlap2Func) (newReg, r2, r2BandEnd, top, bot);
+ }
+
+ ytop = r1->top;
+ }
+ else
+ {
+ ytop = r1->top;
+ }
+
+ /*
+ * If any rectangles got added to the region, try and coalesce them
+ * with rectangles from the previous band. Note we could just do
+ * this test in miCoalesce, but some machines incur a not
+ * inconsiderable cost for function calls, so...
+ */
+ if (newReg->rdh.nCount != curBand)
+ {
+ prevBand = REGION_Coalesce (newReg, prevBand, curBand);
+ }
+
+ /*
+ * Now see if we've hit an intersecting band. The two bands only
+ * intersect if ybot > ytop
+ */
+ ybot = min(r1->bottom, r2->bottom);
+ curBand = newReg->rdh.nCount;
+ if (ybot > ytop)
+ {
+ (* overlapFunc) (newReg, r1, r1BandEnd, r2, r2BandEnd, ytop, ybot);
+ }
+
+ if (newReg->rdh.nCount != curBand)
+ {
+ prevBand = REGION_Coalesce (newReg, prevBand, curBand);
+ }
+
+ /*
+ * If we've finished with a band (bottom == ybot) we skip forward
+ * in the region to the next band.
+ */
+ if (r1->bottom == ybot)
+ {
+ r1 = r1BandEnd;
+ }
+ if (r2->bottom == ybot)
+ {
+ r2 = r2BandEnd;
+ }
+ }
+ while ((r1 != r1End) && (r2 != r2End));
+
+ /*
+ * Deal with whichever region still has rectangles left.
+ */
+ curBand = newReg->rdh.nCount;
+ if (r1 != r1End)
+ {
+ if (nonOverlap1Func != NULL)
+ {
+ do
+ {
+ r1BandEnd = r1;
+ while ((r1BandEnd < r1End) && (r1BandEnd->top == r1->top))
+ {
+ r1BandEnd++;
+ }
+ (* nonOverlap1Func) (newReg, r1, r1BandEnd,
+ max(r1->top,ybot), r1->bottom);
+ r1 = r1BandEnd;
+ }
+ while (r1 != r1End);
+ }
+ }
+ else if ((r2 != r2End) && (nonOverlap2Func != NULL))
+ {
+ do
+ {
+ r2BandEnd = r2;
+ while ((r2BandEnd < r2End) && (r2BandEnd->top == r2->top))
+ {
+ r2BandEnd++;
+ }
+ (* nonOverlap2Func) (newReg, r2, r2BandEnd,
+ max(r2->top,ybot), r2->bottom);
+ r2 = r2BandEnd;
+ }
+ while (r2 != r2End);
+ }
+
+ if (newReg->rdh.nCount != curBand)
+ {
+ (void) REGION_Coalesce (newReg, prevBand, curBand);
+ }
+
+ /*
+ * A bit of cleanup. To keep regions from growing without bound,
+ * we shrink the array of rectangles to match the new number of
+ * rectangles in the region. This never goes to 0, however...
+ *
+ * Only do this stuff if the number of rectangles allocated is more than
+ * twice the number of rectangles in the region (a simple optimization...).
+ */
+ if ((2 * newReg->rdh.nCount*sizeof(RECT) < newReg->rdh.nRgnSize && (newReg->rdh.nCount > 2)))
+ {
+ if (REGION_NOT_EMPTY(newReg))
+ {
+ RECTL *prev_rects = newReg->Buffer;
+ newReg->Buffer = ExAllocatePoolWithTag(PagedPool, newReg->rdh.nCount*sizeof(RECT), TAG_REGION);
+
+ if (! newReg->Buffer)
+ newReg->Buffer = prev_rects;
+ else
+ {
+ newReg->rdh.nRgnSize = newReg->rdh.nCount*sizeof(RECT);
+ COPY_RECTS(newReg->Buffer, prev_rects, newReg->rdh.nCount);
+ if (prev_rects != &newReg->rdh.rcBound)
+ ExFreePoolWithTag(prev_rects, TAG_REGION);
+ }
+ }
+ else
+ {
+ /*
+ * No point in doing the extra work involved in an Xrealloc if
+ * the region is empty
+ */
+ newReg->rdh.nRgnSize = sizeof(RECT);
+ if (newReg->Buffer != &newReg->rdh.rcBound)
+ ExFreePoolWithTag(newReg->Buffer, TAG_REGION);
+ newReg->Buffer = ExAllocatePoolWithTag(PagedPool, sizeof(RECT), TAG_REGION);
+ ASSERT(newReg->Buffer);
+ }
+ }
+ newReg->rdh.iType = RDH_RECTANGLES;
+
+ if (oldRects != &newReg->rdh.rcBound)
+ ExFreePoolWithTag(oldRects, TAG_REGION);
+ return;
+}
+
+/***********************************************************************
+ * Region Intersection
+ ***********************************************************************/
+
+
+/*!
+ * Handle an overlapping band for REGION_Intersect.
+ *
+ * Results:
+ * None.
+ *
+ * \note Side Effects:
+ * Rectangles may be added to the region.
+ *
+ */
+static void FASTCALL
+REGION_IntersectO(
+ PROSRGNDATA pReg,
+ PRECTL r1,
+ PRECTL r1End,
+ PRECTL r2,
+ PRECTL r2End,
+ INT top,
+ INT bottom
+)
+{
+ INT left, right;
+ RECTL *pNextRect;
+
+ pNextRect = pReg->Buffer + pReg->rdh.nCount;
+
+ while ((r1 != r1End) && (r2 != r2End))
+ {
+ left = max(r1->left, r2->left);
+ right = min(r1->right, r2->right);
+
+ /*
+ * If there's any overlap between the two rectangles, add that
+ * overlap to the new region.
+ * There's no need to check for subsumption because the only way
+ * such a need could arise is if some region has two rectangles
+ * right next to each other. Since that should never happen...
+ */
+ if (left < right)
+ {
+ MEMCHECK(pReg, pNextRect, pReg->Buffer);
+ pNextRect->left = left;
+ pNextRect->top = top;
+ pNextRect->right = right;
+ pNextRect->bottom = bottom;
+ pReg->rdh.nCount += 1;
+ pNextRect++;
+ }
+
+ /*
+ * Need to advance the pointers. Shift the one that extends
+ * to the right the least, since the other still has a chance to
+ * overlap with that region's next rectangle, if you see what I mean.
+ */
+ if (r1->right < r2->right)
+ {
+ r1++;
+ }
+ else if (r2->right < r1->right)
+ {
+ r2++;
+ }
+ else
+ {
+ r1++;
+ r2++;
+ }
+ }
+ return;
+}
+
+/***********************************************************************
+ * REGION_IntersectRegion
+ */
+static void FASTCALL
+REGION_IntersectRegion(
+ ROSRGNDATA *newReg,
+ ROSRGNDATA *reg1,
+ ROSRGNDATA *reg2
+)
+{
+ /* check for trivial reject */
+ if ( (!(reg1->rdh.nCount)) || (!(reg2->rdh.nCount)) ||
+ (!EXTENTCHECK(®1->rdh.rcBound, ®2->rdh.rcBound)) )
+ newReg->rdh.nCount = 0;
+ else
+ REGION_RegionOp (newReg, reg1, reg2,
+ REGION_IntersectO, NULL, NULL);
+
+ /*
+ * Can't alter newReg's extents before we call miRegionOp because
+ * it might be one of the source regions and miRegionOp depends
+ * on the extents of those regions being the same. Besides, this
+ * way there's no checking against rectangles that will be nuked
+ * due to coalescing, so we have to examine fewer rectangles.
+ */
+
+ REGION_SetExtents(newReg);
+}
+
+/***********************************************************************
+ * Region Union
+ ***********************************************************************/
+
+/*!
+ * Handle a non-overlapping band for the union operation. Just
+ * Adds the rectangles into the region. Doesn't have to check for
+ * subsumption or anything.
+ *
+ * Results:
+ * None.
+ *
+ * \note Side Effects:
+ * pReg->numRects is incremented and the final rectangles overwritten
+ * with the rectangles we're passed.
+ *
+ */
+static void FASTCALL
+REGION_UnionNonO (
+ PROSRGNDATA pReg,
+ PRECTL r,
+ PRECTL rEnd,
+ INT top,
+ INT bottom
+)
+{
+ RECTL *pNextRect;
+
+ pNextRect = pReg->Buffer + pReg->rdh.nCount;
+
+ while (r != rEnd)
+ {
+ MEMCHECK(pReg, pNextRect, pReg->Buffer);
+ pNextRect->left = r->left;
+ pNextRect->top = top;
+ pNextRect->right = r->right;
+ pNextRect->bottom = bottom;
+ pReg->rdh.nCount += 1;
+ pNextRect++;
+ r++;
+ }
+ return;
+}
+
+/*!
+ * Handle an overlapping band for the union operation. Picks the
+ * left-most rectangle each time and merges it into the region.
+ *
+ * Results:
+ * None.
+ *
+ * \note Side Effects:
+ * Rectangles are overwritten in pReg->rects and pReg->numRects will
+ * be changed.
+ *
+ */
+static void FASTCALL
+REGION_UnionO (
+ PROSRGNDATA pReg,
+ PRECTL r1,
+ PRECTL r1End,
+ PRECTL r2,
+ PRECTL r2End,
+ INT top,
+ INT bottom
+)
+{
+ RECTL *pNextRect;
+
+ pNextRect = pReg->Buffer + pReg->rdh.nCount;
+
+#define MERGERECT(r) \
+ if ((pReg->rdh.nCount != 0) && \
+ ((pNextRect-1)->top == top) && \
+ ((pNextRect-1)->bottom == bottom) && \
+ ((pNextRect-1)->right >= r->left)) \
+ { \
+ if ((pNextRect-1)->right < r->right) \
+ { \
+ (pNextRect-1)->right = r->right; \
+ } \
+ } \
+ else \
+ { \
+ MEMCHECK(pReg, pNextRect, pReg->Buffer); \
+ pNextRect->top = top; \
+ pNextRect->bottom = bottom; \
+ pNextRect->left = r->left; \
+ pNextRect->right = r->right; \
+ pReg->rdh.nCount += 1; \
+ pNextRect += 1; \
+ } \
+ r++;
+
+ while ((r1 != r1End) && (r2 != r2End))
+ {
+ if (r1->left < r2->left)
+ {
+ MERGERECT(r1);
+ }
+ else
+ {
+ MERGERECT(r2);
+ }
+ }
+
+ if (r1 != r1End)
+ {
+ do
+ {
+ MERGERECT(r1);
+ }
+ while (r1 != r1End);
+ }
+ else while (r2 != r2End)
+ {
+ MERGERECT(r2);
+ }
+ return;
+}
+
+/***********************************************************************
+ * REGION_UnionRegion
+ */
+static void FASTCALL
+REGION_UnionRegion(
+ ROSRGNDATA *newReg,
+ ROSRGNDATA *reg1,
+ ROSRGNDATA *reg2
+)
+{
+ /* checks all the simple cases */
+
+ /*
+ * Region 1 and 2 are the same or region 1 is empty
+ */
+ if (reg1 == reg2 || 0 == reg1->rdh.nCount ||
+ reg1->rdh.rcBound.right <= reg1->rdh.rcBound.left ||
+ reg1->rdh.rcBound.bottom <= reg1->rdh.rcBound.top)
+ {
+ if (newReg != reg2)
+ {
+ REGION_CopyRegion(newReg, reg2);
+ }
+ return;
+ }
+
+ /*
+ * if nothing to union (region 2 empty)
+ */
+ if (0 == reg2->rdh.nCount ||
+ reg2->rdh.rcBound.right <= reg2->rdh.rcBound.left ||
+ reg2->rdh.rcBound.bottom <= reg2->rdh.rcBound.top)
+ {
+ if (newReg != reg1)
+ {
+ REGION_CopyRegion(newReg, reg1);
+ }
+ return;
+ }
+
+ /*
+ * Region 1 completely subsumes region 2
+ */
+ if (1 == reg1->rdh.nCount &&
+ reg1->rdh.rcBound.left <= reg2->rdh.rcBound.left &&
+ reg1->rdh.rcBound.top <= reg2->rdh.rcBound.top &&
+ reg2->rdh.rcBound.right <= reg1->rdh.rcBound.right &&
+ reg2->rdh.rcBound.bottom <= reg1->rdh.rcBound.bottom)
+ {
+ if (newReg != reg1)
+ {
+ REGION_CopyRegion(newReg, reg1);
+ }
+ return;
+ }
+
+ /*
+ * Region 2 completely subsumes region 1
+ */
+ if (1 == reg2->rdh.nCount &&
+ reg2->rdh.rcBound.left <= reg1->rdh.rcBound.left &&
+ reg2->rdh.rcBound.top <= reg1->rdh.rcBound.top &&
+ reg1->rdh.rcBound.right <= reg2->rdh.rcBound.right &&
+ reg1->rdh.rcBound.bottom <= reg2->rdh.rcBound.bottom)
+ {
+ if (newReg != reg2)
+ {
+ REGION_CopyRegion(newReg, reg2);
+ }
+ return;
+ }
+
+ REGION_RegionOp (newReg, reg1, reg2, REGION_UnionO,
+ REGION_UnionNonO, REGION_UnionNonO);
+ newReg->rdh.rcBound.left = min(reg1->rdh.rcBound.left, reg2->rdh.rcBound.left);
+ newReg->rdh.rcBound.top = min(reg1->rdh.rcBound.top, reg2->rdh.rcBound.top);
+ newReg->rdh.rcBound.right = max(reg1->rdh.rcBound.right, reg2->rdh.rcBound.right);
+ newReg->rdh.rcBound.bottom = max(reg1->rdh.rcBound.bottom, reg2->rdh.rcBound.bottom);
+}
+
+/***********************************************************************
+ * Region Subtraction
+ ***********************************************************************/
+
+/*!
+ * Deal with non-overlapping band for subtraction. Any parts from
+ * region 2 we discard. Anything from region 1 we add to the region.
+ *
+ * Results:
+ * None.
+ *
+ * \note Side Effects:
+ * pReg may be affected.
+ *
+ */
+static void FASTCALL
+REGION_SubtractNonO1(
+ PROSRGNDATA pReg,
+ PRECTL r,
+ PRECTL rEnd,
+ INT top,
+ INT bottom
+)
+{
+ RECTL *pNextRect;
+
+ pNextRect = pReg->Buffer + pReg->rdh.nCount;
+
+ while (r != rEnd)
+ {
+ MEMCHECK(pReg, pNextRect, pReg->Buffer);
+ pNextRect->left = r->left;
+ pNextRect->top = top;
+ pNextRect->right = r->right;
+ pNextRect->bottom = bottom;
+ pReg->rdh.nCount += 1;
+ pNextRect++;
+ r++;
+ }
+ return;
+}
+
+
+/*!
+ * Overlapping band subtraction. x1 is the left-most point not yet
+ * checked.
+ *
+ * Results:
+ * None.
+ *
+ * \note Side Effects:
+ * pReg may have rectangles added to it.
+ *
+ */
+static void FASTCALL
+REGION_SubtractO(
+ PROSRGNDATA pReg,
+ PRECTL r1,
+ PRECTL r1End,
+ PRECTL r2,
+ PRECTL r2End,
+ INT top,
+ INT bottom
+)
+{
+ RECTL *pNextRect;
+ INT left;
+
+ left = r1->left;
+ pNextRect = pReg->Buffer + pReg->rdh.nCount;
+
+ while ((r1 != r1End) && (r2 != r2End))
+ {
+ if (r2->right <= left)
+ {
+ /*
+ * Subtrahend missed the boat: go to next subtrahend.
+ */
+ r2++;
+ }
+ else if (r2->left <= left)
+ {
+ /*
+ * Subtrahend preceeds minuend: nuke left edge of minuend.
+ */
+ left = r2->right;
+ if (left >= r1->right)
+ {
+ /*
+ * Minuend completely covered: advance to next minuend and
+ * reset left fence to edge of new minuend.
+ */
+ r1++;
+ if (r1 != r1End)
+ left = r1->left;
+ }
+ else
+ {
+ /*
+ * Subtrahend now used up since it doesn't extend beyond
+ * minuend
+ */
+ r2++;
+ }
+ }
+ else if (r2->left < r1->right)
+ {
+ /*
+ * Left part of subtrahend covers part of minuend: add uncovered
+ * part of minuend to region and skip to next subtrahend.
+ */
+ MEMCHECK(pReg, pNextRect, pReg->Buffer);
+ pNextRect->left = left;
+ pNextRect->top = top;
+ pNextRect->right = r2->left;
+ pNextRect->bottom = bottom;
+ pReg->rdh.nCount += 1;
+ pNextRect++;
+ left = r2->right;
+ if (left >= r1->right)
+ {
+ /*
+ * Minuend used up: advance to new...
+ */
+ r1++;
+ if (r1 != r1End)
+ left = r1->left;
+ }
+ else
+ {
+ /*
+ * Subtrahend used up
+ */
+ r2++;
+ }
+ }
+ else
+ {
+ /*
+ * Minuend used up: add any remaining piece before advancing.
+ */
+ if (r1->right > left)
+ {
+ MEMCHECK(pReg, pNextRect, pReg->Buffer);
+ pNextRect->left = left;
+ pNextRect->top = top;
+ pNextRect->right = r1->right;
+ pNextRect->bottom = bottom;
+ pReg->rdh.nCount += 1;
+ pNextRect++;
+ }
+ r1++;
+ if (r1 != r1End)
+ left = r1->left;
+ }
+ }
+
+ /*
+ * Add remaining minuend rectangles to region.
+ */
+ while (r1 != r1End)
+ {
+ MEMCHECK(pReg, pNextRect, pReg->Buffer);
+ pNextRect->left = left;
+ pNextRect->top = top;
+ pNextRect->right = r1->right;
+ pNextRect->bottom = bottom;
+ pReg->rdh.nCount += 1;
+ pNextRect++;
+ r1++;
+ if (r1 != r1End)
+ {
+ left = r1->left;
+ }
+ }
+ return;
+}
+
+/*!
+ * Subtract regS from regM and leave the result in regD.
+ * S stands for subtrahend, M for minuend and D for difference.
+ *
+ * Results:
+ * TRUE.
+ *
+ * \note Side Effects:
+ * regD is overwritten.
+ *
+ */
+static void FASTCALL
+REGION_SubtractRegion(
+ ROSRGNDATA *regD,
+ ROSRGNDATA *regM,
+ ROSRGNDATA *regS
+)
+{
+ /* check for trivial reject */
+ if ( (!(regM->rdh.nCount)) || (!(regS->rdh.nCount)) ||
+ (!EXTENTCHECK(®M->rdh.rcBound, ®S->rdh.rcBound)) )
+ {
+ REGION_CopyRegion(regD, regM);
+ return;
+ }
+
+ REGION_RegionOp (regD, regM, regS, REGION_SubtractO,
+ REGION_SubtractNonO1, NULL);
+
+ /*
+ * Can't alter newReg's extents before we call miRegionOp because
+ * it might be one of the source regions and miRegionOp depends
+ * on the extents of those regions being the unaltered. Besides, this
+ * way there's no checking against rectangles that will be nuked
+ * due to coalescing, so we have to examine fewer rectangles.
+ */
+ REGION_SetExtents (regD);
+}
+
+/***********************************************************************
+ * REGION_XorRegion
+ */
+static void FASTCALL
+REGION_XorRegion(
+ ROSRGNDATA *dr,
+ ROSRGNDATA *sra,
+ ROSRGNDATA *srb
+)
+{
+ HRGN htra, htrb;
+ ROSRGNDATA *tra, *trb;
+
+ // FIXME: don't use a handle
+ tra = REGION_AllocRgnWithHandle(sra->rdh.nCount + 1);
+ if (!tra )
+ {
+ return;
+ }
+ htra = tra->BaseObject.hHmgr;
+
+ // FIXME: don't use a handle
+ trb = REGION_AllocRgnWithHandle(srb->rdh.nCount + 1);
+ if (!trb)
+ {
+ RGNOBJAPI_Unlock(tra);
+ GreDeleteObject(htra);
+ return;
+ }
+ htrb = trb->BaseObject.hHmgr;
+
+ REGION_SubtractRegion(tra, sra, srb);
+ REGION_SubtractRegion(trb, srb, sra);
+ REGION_UnionRegion(dr, tra, trb);
+ RGNOBJAPI_Unlock(tra);
+ RGNOBJAPI_Unlock(trb);
+
+ GreDeleteObject(htra);
+ GreDeleteObject(htrb);
+ return;
+}
+
+
+/*!
+ * Adds a rectangle to a REGION
+ */
+VOID FASTCALL
+REGION_UnionRectWithRgn(
+ ROSRGNDATA *rgn,
+ const RECTL *rect
+)
+{
+ ROSRGNDATA region;
+
+ region.Buffer = ®ion.rdh.rcBound;
+ region.rdh.nCount = 1;
+ region.rdh.nRgnSize = sizeof(RECT);
+ region.rdh.rcBound = *rect;
+ REGION_UnionRegion(rgn, rgn, ®ion);
+}
+
+BOOL FASTCALL
+REGION_CreateSimpleFrameRgn(
+ PROSRGNDATA rgn,
+ INT x,
+ INT y
+)
+{
+ RECTL rc[4];
+ PRECTL prc;
+
+ if (x != 0 || y != 0)
+ {
+ prc = rc;
+
+ if (rgn->rdh.rcBound.bottom - rgn->rdh.rcBound.top > y * 2 &&
+ rgn->rdh.rcBound.right - rgn->rdh.rcBound.left > x * 2)
+ {
+ if (y != 0)
+ {
+ /* top rectangle */
+ prc->left = rgn->rdh.rcBound.left;
+ prc->top = rgn->rdh.rcBound.top;
+ prc->right = rgn->rdh.rcBound.right;
+ prc->bottom = prc->top + y;
+ prc++;
+ }
+
+ if (x != 0)
+ {
+ /* left rectangle */
+ prc->left = rgn->rdh.rcBound.left;
+ prc->top = rgn->rdh.rcBound.top + y;
+ prc->right = prc->left + x;
+ prc->bottom = rgn->rdh.rcBound.bottom - y;
+ prc++;
+
+ /* right rectangle */
+ prc->left = rgn->rdh.rcBound.right - x;
+ prc->top = rgn->rdh.rcBound.top + y;
+ prc->right = rgn->rdh.rcBound.right;
+ prc->bottom = rgn->rdh.rcBound.bottom - y;
+ prc++;
+ }
+
+ if (y != 0)
+ {
+ /* bottom rectangle */
+ prc->left = rgn->rdh.rcBound.left;
+ prc->top = rgn->rdh.rcBound.bottom - y;
+ prc->right = rgn->rdh.rcBound.right;
+ prc->bottom = rgn->rdh.rcBound.bottom;
+ prc++;
+ }
+ }
+
+ if (prc != rc)
+ {
+ /* The frame results in a complex region. rcBounds remains
+ the same, though. */
+ rgn->rdh.nCount = (DWORD)(prc - rc);
+ ASSERT(rgn->rdh.nCount > 1);
+ rgn->rdh.nRgnSize = rgn->rdh.nCount * sizeof(RECT);
+ rgn->Buffer = ExAllocatePoolWithTag(PagedPool, rgn->rdh.nRgnSize, TAG_REGION);
+ if (!rgn->Buffer)
+ {
+ rgn->rdh.nRgnSize = 0;
+ return FALSE;
+ }
+
+ COPY_RECTS(rgn->Buffer, rc, rgn->rdh.nCount);
+ }
+ }
+
+ return TRUE;
+}
+
+BOOL FASTCALL
+REGION_CreateFrameRgn(
+ HRGN hDest,
+ HRGN hSrc,
+ INT x,
+ INT y
+)
+{
+ PROSRGNDATA srcObj, destObj;
+ PRECTL rc;
+ ULONG i;
+
+ if (!(srcObj = RGNOBJAPI_Lock(hSrc, NULL)))
+ {
+ return FALSE;
+ }
+ if (!REGION_NOT_EMPTY(srcObj))
+ {
+ RGNOBJAPI_Unlock(srcObj);
+ return FALSE;
+ }
+ if (!(destObj = RGNOBJAPI_Lock(hDest, NULL)))
+ {
+ RGNOBJAPI_Unlock(srcObj);
+ return FALSE;
+ }
+
+ EMPTY_REGION(destObj);
+ if (!REGION_CopyRegion(destObj, srcObj))
+ {
+ RGNOBJAPI_Unlock(destObj);
+ RGNOBJAPI_Unlock(srcObj);
+ return FALSE;
+ }
+
+ if (REGION_Complexity(srcObj) == SIMPLEREGION)
+ {
+ if (!REGION_CreateSimpleFrameRgn(destObj, x, y))
+ {
+ EMPTY_REGION(destObj);
+ RGNOBJAPI_Unlock(destObj);
+ RGNOBJAPI_Unlock(srcObj);
+ return FALSE;
+ }
+ }
+ else
+ {
+ /* Original region moved to right */
+ rc = srcObj->Buffer;
+ for (i = 0; i < srcObj->rdh.nCount; i++)
+ {
+ rc->left += x;
+ rc->right += x;
+ rc++;
+ }
+ REGION_IntersectRegion(destObj, destObj, srcObj);
+
+ /* Original region moved to left */
+ rc = srcObj->Buffer;
+ for (i = 0; i < srcObj->rdh.nCount; i++)
+ {
+ rc->left -= 2 * x;
+ rc->right -= 2 * x;
+ rc++;
+ }
+ REGION_IntersectRegion(destObj, destObj, srcObj);
+
+ /* Original region moved down */
+ rc = srcObj->Buffer;
+ for (i = 0; i < srcObj->rdh.nCount; i++)
+ {
+ rc->left += x;
+ rc->right += x;
+ rc->top += y;
+ rc->bottom += y;
+ rc++;
+ }
+ REGION_IntersectRegion(destObj, destObj, srcObj);
+
+ /* Original region moved up */
+ rc = srcObj->Buffer;
+ for (i = 0; i < srcObj->rdh.nCount; i++)
+ {
+ rc->top -= 2 * y;
+ rc->bottom -= 2 * y;
+ rc++;
+ }
+ REGION_IntersectRegion(destObj, destObj, srcObj);
+
+ /* Restore the original region */
+ rc = srcObj->Buffer;
+ for (i = 0; i < srcObj->rdh.nCount; i++)
+ {
+ rc->top += y;
+ rc->bottom += y;
+ rc++;
+ }
+ REGION_SubtractRegion(destObj, srcObj, destObj);
+ }
+
+ RGNOBJAPI_Unlock(destObj);
+ RGNOBJAPI_Unlock(srcObj);
+ return TRUE;
+}
+
+
+BOOL FASTCALL
+REGION_LPTODP(
+ PDC dc,
+ HRGN hDest,
+ HRGN hSrc)
+{
+ RECTL *pCurRect, *pEndRect;
+ PROSRGNDATA srcObj = NULL;
+ PROSRGNDATA destObj = NULL;
+
+ RECTL tmpRect;
+ BOOL ret = FALSE;
+ PDC_ATTR pdcattr;
+
+ if (!dc)
+ return ret;
+ pdcattr = dc->pdcattr;
+
+ if (pdcattr->iMapMode == MM_TEXT) // Requires only a translation
+ {
+ if (NtGdiCombineRgn(hDest, hSrc, 0, RGN_COPY) == ERROR)
+ goto done;
+
+ NtGdiOffsetRgn(hDest, pdcattr->ptlViewportOrg.x - pdcattr->ptlWindowOrg.x,
+ pdcattr->ptlViewportOrg.y - pdcattr->ptlWindowOrg.y);
+ ret = TRUE;
+ goto done;
+ }
+
+ if ( !(srcObj = RGNOBJAPI_Lock(hSrc, NULL)) )
+ goto done;
+ if ( !(destObj = RGNOBJAPI_Lock(hDest, NULL)) )
+ {
+ RGNOBJAPI_Unlock(srcObj);
+ goto done;
+ }
+ EMPTY_REGION(destObj);
+
+ pEndRect = srcObj->Buffer + srcObj->rdh.nCount;
+ for (pCurRect = srcObj->Buffer; pCurRect < pEndRect; pCurRect++)
+ {
+ tmpRect = *pCurRect;
+ tmpRect.left = XLPTODP(pdcattr, tmpRect.left);
+ tmpRect.top = YLPTODP(pdcattr, tmpRect.top);
+ tmpRect.right = XLPTODP(pdcattr, tmpRect.right);
+ tmpRect.bottom = YLPTODP(pdcattr, tmpRect.bottom);
+
+ if (tmpRect.left > tmpRect.right)
+ {
+ INT tmp = tmpRect.left;
+ tmpRect.left = tmpRect.right;
+ tmpRect.right = tmp;
+ }
+ if (tmpRect.top > tmpRect.bottom)
+ {
+ INT tmp = tmpRect.top;
+ tmpRect.top = tmpRect.bottom;
+ tmpRect.bottom = tmp;
+ }
+
+ REGION_UnionRectWithRgn(destObj, &tmpRect);
+ }
+ ret = TRUE;
+
+ RGNOBJAPI_Unlock(srcObj);
+ RGNOBJAPI_Unlock(destObj);
+
+done:
+ return ret;
+}
+
+PROSRGNDATA
+FASTCALL
+REGION_AllocRgnWithHandle(INT nReg)
+{
+ HRGN hReg;
+ PROSRGNDATA pReg;
+
+ pReg = (PROSRGNDATA)GDIOBJ_AllocObjWithHandle(GDI_OBJECT_TYPE_REGION);
+ if(!pReg)
+ {
+ return NULL;
+ }
+
+ hReg = pReg->BaseObject.hHmgr;
+
+ if (nReg == 0 || nReg == 1)
+ {
+ /* Testing shows that > 95% of all regions have only 1 rect.
+ Including that here saves us from having to do another allocation */
+ pReg->Buffer = &pReg->rdh.rcBound;
+ }
+ else
+ {
+ pReg->Buffer = ExAllocatePoolWithTag(PagedPool, nReg * sizeof(RECT), TAG_REGION);
+ if (!pReg->Buffer)
+ {
+ RGNOBJAPI_Unlock(pReg);
+ GDIOBJ_FreeObjByHandle(hReg, GDI_OBJECT_TYPE_REGION);
+ return NULL;
+ }
+ }
+
+ EMPTY_REGION(pReg);
+ pReg->rdh.dwSize = sizeof(RGNDATAHEADER);
+ pReg->rdh.nCount = nReg;
+ pReg->rdh.nRgnSize = nReg * sizeof(RECT);
+
+ return pReg;
+}
+
+//
+// Allocate User Space Region Handle.
+//
+PROSRGNDATA
+FASTCALL
+REGION_AllocUserRgnWithHandle(INT nRgn)
+{
+ PROSRGNDATA pRgn;
+ INT Index;
+ PGDI_TABLE_ENTRY Entry;
+
+ pRgn = REGION_AllocRgnWithHandle(nRgn);
+ if (pRgn)
+ {
+ Index = GDI_HANDLE_GET_INDEX(pRgn->BaseObject.hHmgr);
+ Entry = &GdiHandleTable->Entries[Index];
+ Entry->UserData = AllocateObjectAttr();
+ }
+ return pRgn;
+}
+
+PROSRGNDATA
+FASTCALL
+RGNOBJAPI_Lock(HRGN hRgn, PRGN_ATTR *ppRgn_Attr)
+{
+ INT Index;
+ PGDI_TABLE_ENTRY Entry;
+ PROSRGNDATA pRgn;
+ PRGN_ATTR pRgn_Attr;
+ HANDLE pid;
+
+ pRgn = REGION_LockRgn(hRgn);
+
+ if (pRgn)
+ {
+ Index = GDI_HANDLE_GET_INDEX(hRgn);
+ Entry = &GdiHandleTable->Entries[Index];
+ pRgn_Attr = Entry->UserData;
+ pid = (HANDLE)((ULONG_PTR)Entry->ProcessId & ~0x1);
+
+ if ( pid == NtCurrentTeb()->ClientId.UniqueProcess &&
+ pRgn_Attr )
+ {
+ _SEH2_TRY
+ {
+ if ( !(pRgn_Attr->AttrFlags & ATTR_CACHED) &&
+ pRgn_Attr->AttrFlags & (ATTR_RGN_VALID|ATTR_RGN_DIRTY) )
+ {
+ switch (pRgn_Attr->Flags)
+ {
+ case NULLREGION:
+ EMPTY_REGION( pRgn );
+ break;
+
+ case SIMPLEREGION:
+ REGION_SetRectRgn( pRgn,
+ pRgn_Attr->Rect.left,
+ pRgn_Attr->Rect.top,
+ pRgn_Attr->Rect.right,
+ pRgn_Attr->Rect.bottom );
+ break;
+ }
+ pRgn_Attr->AttrFlags &= ~ATTR_RGN_DIRTY;
+ }
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ }
+ _SEH2_END;
+
+ if (ppRgn_Attr)
+ *ppRgn_Attr = pRgn_Attr;
+ }
+ else
+ {
+ if (ppRgn_Attr)
+ *ppRgn_Attr = NULL;
+ }
+ }
+ return pRgn;
+}
+
+VOID
+FASTCALL
+RGNOBJAPI_Unlock(PROSRGNDATA pRgn)
+{
+ INT Index;
+ PGDI_TABLE_ENTRY Entry;
+ PRGN_ATTR pRgn_Attr;
+ HANDLE pid;
+
+ if (pRgn)
+ {
+ Index = GDI_HANDLE_GET_INDEX(pRgn->BaseObject.hHmgr);
+ Entry = &GdiHandleTable->Entries[Index];
+ pRgn_Attr = Entry->UserData;
+ pid = (HANDLE)((ULONG_PTR)Entry->ProcessId & ~0x1);
+
+ if ( pid == NtCurrentTeb()->ClientId.UniqueProcess &&
+ pRgn_Attr )
+ {
+ _SEH2_TRY
+ {
+ if ( pRgn_Attr->AttrFlags & ATTR_RGN_VALID )
+ {
+ pRgn_Attr->Flags = REGION_Complexity( pRgn );
+ pRgn_Attr->Rect.left = pRgn->rdh.rcBound.left;
+ pRgn_Attr->Rect.top = pRgn->rdh.rcBound.top;
+ pRgn_Attr->Rect.right = pRgn->rdh.rcBound.right;
+ pRgn_Attr->Rect.bottom = pRgn->rdh.rcBound.bottom;
+ }
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ }
+ _SEH2_END;
+ }
+ }
+ REGION_UnlockRgn(pRgn);
+}
+
+/*
+ System Regions:
+ These regions do not use attribute sections and when allocated, use gdiobj
+ level functions.
+*/
+//
+// System Region Functions
+//
+PROSRGNDATA
+FASTCALL
+IntSysCreateRectpRgn(INT LeftRect, INT TopRect, INT RightRect, INT BottomRect)
+{
+ PROSRGNDATA pRgn;
+
+ pRgn = (PROSRGNDATA)GDIOBJ_AllocObjWithHandle(GDI_OBJECT_TYPE_REGION);
+ if (!pRgn)
+ {
+ return NULL;
+ }
+ pRgn->Buffer = &pRgn->rdh.rcBound;
+ REGION_SetRectRgn(pRgn, LeftRect, TopRect, RightRect, BottomRect);
+ REGION_UnlockRgn(pRgn);
+ return pRgn;
+}
+
+HRGN
+FASTCALL
+IntSysCreateRectRgn(INT LeftRect, INT TopRect, INT RightRect, INT BottomRect)
+{
+ PROSRGNDATA pRgn = IntSysCreateRectpRgn(LeftRect,TopRect,RightRect,BottomRect);
+ return (pRgn ? pRgn->BaseObject.hHmgr : NULL);
+}
+
+BOOL INTERNAL_CALL
+REGION_Cleanup(PVOID ObjectBody)
+{
+ PROSRGNDATA pRgn = (PROSRGNDATA)ObjectBody;
+ if (pRgn->Buffer && pRgn->Buffer != &pRgn->rdh.rcBound)
+ ExFreePoolWithTag(pRgn->Buffer, TAG_REGION);
+ return TRUE;
+}
+
+// use REGION_FreeRgnByHandle(hRgn); for systems regions.
+VOID FASTCALL
+REGION_Delete(PROSRGNDATA pRgn)
+{
+ if ( pRgn == prgnDefault) return;
+ REGION_FreeRgn(pRgn);
+}
+
+VOID FASTCALL
+IntGdiReleaseRaoRgn(PDC pDC)
+{
+ INT Index = GDI_HANDLE_GET_INDEX(pDC->BaseObject.hHmgr);
+ PGDI_TABLE_ENTRY Entry = &GdiHandleTable->Entries[Index];
+ pDC->fs |= DC_FLAG_DIRTY_RAO;
+ Entry->Flags |= GDI_ENTRY_VALIDATE_VIS;
+ RECTL_vSetEmptyRect(&pDC->erclClip);
+}
+
+VOID FASTCALL
+IntGdiReleaseVisRgn(PDC pDC)
+{
+ INT Index = GDI_HANDLE_GET_INDEX(pDC->BaseObject.hHmgr);
+ PGDI_TABLE_ENTRY Entry = &GdiHandleTable->Entries[Index];
+ pDC->fs |= DC_FLAG_DIRTY_RAO;
+ Entry->Flags |= GDI_ENTRY_VALIDATE_VIS;
+ RECTL_vSetEmptyRect(&pDC->erclClip);
+ REGION_Delete(pDC->prgnVis);
+ pDC->prgnVis = prgnDefault;
+}
+
+VOID FASTCALL
+IntUpdateVisRectRgn(PDC pDC, PROSRGNDATA pRgn)
+{
+ INT Index = GDI_HANDLE_GET_INDEX(pDC->BaseObject.hHmgr);
+ PGDI_TABLE_ENTRY Entry = &GdiHandleTable->Entries[Index];
+ PDC_ATTR pdcattr;
+ RECTL rcl;
+
+ if (Entry->Flags & GDI_ENTRY_VALIDATE_VIS)
+ {
+ pdcattr = pDC->pdcattr;
+
+ pdcattr->VisRectRegion.Flags = REGION_Complexity(pRgn);
+
+ if (pRgn && pdcattr->VisRectRegion.Flags != NULLREGION)
+ {
+ rcl.left = pRgn->rdh.rcBound.left;
+ rcl.top = pRgn->rdh.rcBound.top;
+ rcl.right = pRgn->rdh.rcBound.right;
+ rcl.bottom = pRgn->rdh.rcBound.bottom;
+
+ rcl.left -= pDC->erclWindow.left;
+ rcl.top -= pDC->erclWindow.top;
+ rcl.right -= pDC->erclWindow.left;
+ rcl.bottom -= pDC->erclWindow.top;
+ }
+ else
+ RECTL_vSetEmptyRect(&rcl);
+
+ pdcattr->VisRectRegion.Rect = rcl;
+
+ Entry->Flags &= ~GDI_ENTRY_VALIDATE_VIS;
+ }
+}
+
+INT
+FASTCALL
+IntGdiCombineRgn(PROSRGNDATA destRgn,
+ PROSRGNDATA src1Rgn,
+ PROSRGNDATA src2Rgn,
+ INT CombineMode)
+{
+ INT result = ERROR;
+
+ if (destRgn)
+ {
+ if (src1Rgn)
+ {
+ if (CombineMode == RGN_COPY)
+ {
+ if ( !REGION_CopyRegion(destRgn, src1Rgn) )
+ return ERROR;
+ result = REGION_Complexity(destRgn);
+ }
+ else
+ {
+ if (src2Rgn)
+ {
+ switch (CombineMode)
+ {
+ case RGN_AND:
+ REGION_IntersectRegion(destRgn, src1Rgn, src2Rgn);
+ break;
+ case RGN_OR:
+ REGION_UnionRegion(destRgn, src1Rgn, src2Rgn);
+ break;
+ case RGN_XOR:
+ REGION_XorRegion(destRgn, src1Rgn, src2Rgn);
+ break;
+ case RGN_DIFF:
+ REGION_SubtractRegion(destRgn, src1Rgn, src2Rgn);
+ break;
+ }
+ result = REGION_Complexity(destRgn);
+ }
+ else if (src2Rgn == NULL)
+ {
+ DPRINT1("IntGdiCombineRgn requires hSrc2 != NULL for combine mode %d!\n", CombineMode);
+ SetLastWin32Error(ERROR_INVALID_HANDLE);
+ }
+ }
+ }
+ else
+ {
+ DPRINT("IntGdiCombineRgn: hSrc1 unavailable\n");
+ SetLastWin32Error(ERROR_INVALID_HANDLE);
+ }
+ }
+ else
+ {
+ DPRINT("IntGdiCombineRgn: hDest unavailable\n");
+ SetLastWin32Error(ERROR_INVALID_HANDLE);
+ }
+ return result;
+}
+
+INT FASTCALL
+REGION_GetRgnBox(
+ PROSRGNDATA Rgn,
+ PRECTL pRect
+)
+{
+ DWORD ret;
+
+ if (Rgn)
+ {
+ *pRect = Rgn->rdh.rcBound;
+ ret = REGION_Complexity(Rgn);
+
+ return ret;
+ }
+ return 0; //if invalid region return zero
+}
+
+INT APIENTRY
+IntGdiGetRgnBox(
+ HRGN hRgn,
+ PRECTL pRect
+)
+{
+ PROSRGNDATA Rgn;
+ DWORD ret;
+
+ if (!(Rgn = RGNOBJAPI_Lock(hRgn, NULL)))
+ {
+ return ERROR;
+ }
+
+ ret = REGION_GetRgnBox(Rgn, pRect);
+ RGNOBJAPI_Unlock(Rgn);
+
+ return ret;
+}
+
+BOOL
+FASTCALL
+IntGdiPaintRgn(
+ PDC dc,
+ HRGN hRgn
+)
+{
+ HRGN tmpVisRgn;
+ PROSRGNDATA visrgn;
+ CLIPOBJ* ClipRegion;
+ BOOL bRet = FALSE;
+ POINTL BrushOrigin;
+ SURFACE *psurf;
+ PDC_ATTR pdcattr;
+
+ if (!dc) return FALSE;
+ pdcattr = dc->pdcattr;
+
+ ASSERT(!(pdcattr->ulDirty_ & (DIRTY_FILL | DC_BRUSH_DIRTY)));
+
+ if (!(tmpVisRgn = IntSysCreateRectRgn(0, 0, 0, 0))) return FALSE;
+
+ // Transform region into device co-ords
+ if (!REGION_LPTODP(dc, tmpVisRgn, hRgn) ||
+ NtGdiOffsetRgn(tmpVisRgn, dc->ptlDCOrig.x, dc->ptlDCOrig.y) == ERROR)
+ {
+ REGION_FreeRgnByHandle(tmpVisRgn);
+ return FALSE;
+ }
+
+ NtGdiCombineRgn(tmpVisRgn, tmpVisRgn, dc->rosdc.hGCClipRgn, RGN_AND);
+
+ visrgn = RGNOBJAPI_Lock(tmpVisRgn, NULL);
+ if (visrgn == NULL)
+ {
+ REGION_FreeRgnByHandle(tmpVisRgn);
+ return FALSE;
+ }
+
+ ClipRegion = IntEngCreateClipRegion(visrgn->rdh.nCount,
+ visrgn->Buffer,
+ &visrgn->rdh.rcBound );
+ ASSERT(ClipRegion);
+
+ BrushOrigin.x = pdcattr->ptlBrushOrigin.x;
+ BrushOrigin.y = pdcattr->ptlBrushOrigin.y;
+ psurf = dc->dclevel.pSurface;
+ /* FIXME - Handle psurf == NULL !!!! */
+
+ bRet = IntEngPaint(&psurf->SurfObj,
+ ClipRegion,
+ &dc->eboFill.BrushObject,
+ &BrushOrigin,
+ 0xFFFF);//FIXME:don't know what to put here
+
+ RGNOBJAPI_Unlock(visrgn);
+ REGION_FreeRgnByHandle(tmpVisRgn);
+
+ // Fill the region
++ return bRet;
+}
+
+BOOL
+FASTCALL
+REGION_RectInRegion(
+ PROSRGNDATA Rgn,
+ const RECTL *rect
+)
+{
+ PRECTL pCurRect, pRectEnd;
+ RECT rc;
+
+ /* swap the coordinates to make right >= left and bottom >= top */
+ /* (region building rectangles are normalized the same way) */
+ if( rect->top > rect->bottom) {
+ rc.top = rect->bottom;
+ rc.bottom = rect->top;
+ } else {
+ rc.top = rect->top;
+ rc.bottom = rect->bottom;
+ }
+ if( rect->right < rect->left) {
+ rc.right = rect->left;
+ rc.left = rect->right;
+ } else {
+ rc.right = rect->right;
+ rc.left = rect->left;
+ }
+
+ /* this is (just) a useful optimization */
+ if ((Rgn->rdh.nCount > 0) && EXTENTCHECK(&Rgn->rdh.rcBound, &rc))
+ {
+ for (pCurRect = Rgn->Buffer, pRectEnd = pCurRect +
+ Rgn->rdh.nCount; pCurRect < pRectEnd; pCurRect++)
+ {
+ if (pCurRect->bottom <= rc.top)
+ continue; /* not far enough down yet */
+
+ if (pCurRect->top >= rc.bottom)
+ break; /* too far down */
+
+ if (pCurRect->right <= rc.left)
+ continue; /* not far enough over yet */
+
+ if (pCurRect->left >= rc.right) {
+ continue;
+ }
+
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+VOID
+FASTCALL
+REGION_SetRectRgn(
+ PROSRGNDATA rgn,
+ INT LeftRect,
+ INT TopRect,
+ INT RightRect,
+ INT BottomRect
+)
+{
+ PRECTL firstRect;
+
+ if (LeftRect > RightRect)
+ {
+ INT tmp = LeftRect;
+ LeftRect = RightRect;
+ RightRect = tmp;
+ }
+ if (TopRect > BottomRect)
+ {
+ INT tmp = TopRect;
+ TopRect = BottomRect;
+ BottomRect = tmp;
+ }
+
+ if ((LeftRect != RightRect) && (TopRect != BottomRect))
+ {
+ firstRect = rgn->Buffer;
+ ASSERT(firstRect);
+ firstRect->left = rgn->rdh.rcBound.left = LeftRect;
+ firstRect->top = rgn->rdh.rcBound.top = TopRect;
+ firstRect->right = rgn->rdh.rcBound.right = RightRect;
+ firstRect->bottom = rgn->rdh.rcBound.bottom = BottomRect;
+ rgn->rdh.nCount = 1;
+ rgn->rdh.iType = RDH_RECTANGLES;
+ }
+ else
+ {
+ EMPTY_REGION(rgn);
+ }
+}
+
+INT
+FASTCALL
+IntGdiOffsetRgn(
+ PROSRGNDATA rgn,
+ INT XOffset,
+ INT YOffset )
+{
+ if (XOffset || YOffset)
+ {
+ int nbox = rgn->rdh.nCount;
+ PRECTL pbox = rgn->Buffer;
+
+ if (nbox && pbox)
+ {
+ while (nbox--)
+ {
+ pbox->left += XOffset;
+ pbox->right += XOffset;
+ pbox->top += YOffset;
+ pbox->bottom += YOffset;
+ pbox++;
+ }
+ if (rgn->Buffer != &rgn->rdh.rcBound)
+ {
+ rgn->rdh.rcBound.left += XOffset;
+ rgn->rdh.rcBound.right += XOffset;
+ rgn->rdh.rcBound.top += YOffset;
+ rgn->rdh.rcBound.bottom += YOffset;
+ }
+ }
+ }
+ return REGION_Complexity(rgn);
+}
+
+/***********************************************************************
+ * REGION_InsertEdgeInET
+ *
+ * Insert the given edge into the edge table.
+ * First we must find the correct bucket in the
+ * Edge table, then find the right slot in the
+ * bucket. Finally, we can insert it.
+ *
+ */
+static void FASTCALL
+REGION_InsertEdgeInET(
+ EdgeTable *ET,
+ EdgeTableEntry *ETE,
+ INT scanline,
+ ScanLineListBlock **SLLBlock,
+ INT *iSLLBlock
+)
+{
+ EdgeTableEntry *start, *prev;
+ ScanLineList *pSLL, *pPrevSLL;
+ ScanLineListBlock *tmpSLLBlock;
+
+ /*
+ * find the right bucket to put the edge into
+ */
+ pPrevSLL = &ET->scanlines;
+ pSLL = pPrevSLL->next;
+ while (pSLL && (pSLL->scanline < scanline))
+ {
+ pPrevSLL = pSLL;
+ pSLL = pSLL->next;
+ }
+
+ /*
+ * reassign pSLL (pointer to ScanLineList) if necessary
+ */
+ if ((!pSLL) || (pSLL->scanline > scanline))
+ {
+ if (*iSLLBlock > SLLSPERBLOCK-1)
+ {
+ tmpSLLBlock = ExAllocatePoolWithTag(PagedPool, sizeof(ScanLineListBlock), TAG_REGION);
+ if (!tmpSLLBlock)
+ {
+ DPRINT1("REGION_InsertEdgeInETL(): Can't alloc SLLB\n");
+ /* FIXME - free resources? */
+ return;
+ }
+ (*SLLBlock)->next = tmpSLLBlock;
+ tmpSLLBlock->next = (ScanLineListBlock *)NULL;
+ *SLLBlock = tmpSLLBlock;
+ *iSLLBlock = 0;
+ }
+ pSLL = &((*SLLBlock)->SLLs[(*iSLLBlock)++]);
+
+ pSLL->next = pPrevSLL->next;
+ pSLL->edgelist = (EdgeTableEntry *)NULL;
+ pPrevSLL->next = pSLL;
+ }
+ pSLL->scanline = scanline;
+
+ /*
+ * now insert the edge in the right bucket
+ */
+ prev = (EdgeTableEntry *)NULL;
+ start = pSLL->edgelist;
+ while (start && (start->bres.minor_axis < ETE->bres.minor_axis))
+ {
+ prev = start;
+ start = start->next;
+ }
+ ETE->next = start;
+
+ if (prev)
+ prev->next = ETE;
+ else
+ pSLL->edgelist = ETE;
+}
+
+/***********************************************************************
+ * REGION_loadAET
+ *
+ * This routine moves EdgeTableEntries from the
+ * EdgeTable into the Active Edge Table,
+ * leaving them sorted by smaller x coordinate.
+ *
+ */
+static void FASTCALL
+REGION_loadAET(
+ EdgeTableEntry *AET,
+ EdgeTableEntry *ETEs
+)
+{
+ EdgeTableEntry *pPrevAET;
+ EdgeTableEntry *tmp;
+
+ pPrevAET = AET;
+ AET = AET->next;
+ while (ETEs)
+ {
+ while (AET && (AET->bres.minor_axis < ETEs->bres.minor_axis))
+ {
+ pPrevAET = AET;
+ AET = AET->next;
+ }
+ tmp = ETEs->next;
+ ETEs->next = AET;
+ if (AET)
+ AET->back = ETEs;
+ ETEs->back = pPrevAET;
+ pPrevAET->next = ETEs;
+ pPrevAET = ETEs;
+
+ ETEs = tmp;
+ }
+}
+
+/***********************************************************************
+ * REGION_computeWAET
+ *
+ * This routine links the AET by the
+ * nextWETE (winding EdgeTableEntry) link for
+ * use by the winding number rule. The final
+ * Active Edge Table (AET) might look something
+ * like:
+ *
+ * AET
+ * ---------- --------- ---------
+ * |ymax | |ymax | |ymax |
+ * | ... | |... | |... |
+ * |next |->|next |->|next |->...
+ * |nextWETE| |nextWETE| |nextWETE|
+ * --------- --------- ^--------
+ * | | |
+ * V-------------------> V---> ...
+ *
+ */
+static void FASTCALL
+REGION_computeWAET(EdgeTableEntry *AET)
+{
+ register EdgeTableEntry *pWETE;
+ register int inside = 1;
+ register int isInside = 0;
+
+ AET->nextWETE = (EdgeTableEntry *)NULL;
+ pWETE = AET;
+ AET = AET->next;
+ while (AET)
+ {
+ if (AET->ClockWise)
+ isInside++;
+ else
+ isInside--;
+
+ if ( (!inside && !isInside) ||
+ ( inside && isInside) )
+ {
+ pWETE->nextWETE = AET;
+ pWETE = AET;
+ inside = !inside;
+ }
+ AET = AET->next;
+ }
+ pWETE->nextWETE = (EdgeTableEntry *)NULL;
+}
+
+/***********************************************************************
+ * REGION_InsertionSort
+ *
+ * Just a simple insertion sort using
+ * pointers and back pointers to sort the Active
+ * Edge Table.
+ *
+ */
+static BOOL FASTCALL
+REGION_InsertionSort(EdgeTableEntry *AET)
+{
+ EdgeTableEntry *pETEchase;
+ EdgeTableEntry *pETEinsert;
+ EdgeTableEntry *pETEchaseBackTMP;
+ BOOL changed = FALSE;
+
+ AET = AET->next;
+ while (AET)
+ {
+ pETEinsert = AET;
+ pETEchase = AET;
+ while (pETEchase->back->bres.minor_axis > AET->bres.minor_axis)
+ pETEchase = pETEchase->back;
+
+ AET = AET->next;
+ if (pETEchase != pETEinsert)
+ {
+ pETEchaseBackTMP = pETEchase->back;
+ pETEinsert->back->next = AET;
+ if (AET)
+ AET->back = pETEinsert->back;
+ pETEinsert->next = pETEchase;
+ pETEchase->back->next = pETEinsert;
+ pETEchase->back = pETEinsert;
+ pETEinsert->back = pETEchaseBackTMP;
+ changed = TRUE;
+ }
+ }
+ return changed;
+}
+
+/***********************************************************************
+ * REGION_FreeStorage
+ *
+ * Clean up our act.
+ */
+static void FASTCALL
+REGION_FreeStorage(ScanLineListBlock *pSLLBlock)
+{
+ ScanLineListBlock *tmpSLLBlock;
+
+ while (pSLLBlock)
+ {
+ tmpSLLBlock = pSLLBlock->next;
+ ExFreePool(pSLLBlock);
+ pSLLBlock = tmpSLLBlock;
+ }
+}
+
+
+/***********************************************************************
+ * REGION_PtsToRegion
+ *
+ * Create an array of rectangles from a list of points.
+ */
+static int FASTCALL
+REGION_PtsToRegion(
+ int numFullPtBlocks,
+ int iCurPtBlock,
+ POINTBLOCK *FirstPtBlock,
+ ROSRGNDATA *reg)
+{
+ RECTL *rects;
+ POINT *pts;
+ POINTBLOCK *CurPtBlock;
+ int i;
+ RECTL *extents, *temp;
+ INT numRects;
+
+ extents = ®->rdh.rcBound;
+
+ numRects = ((numFullPtBlocks * NUMPTSTOBUFFER) + iCurPtBlock) >> 1;
+
+ if (!(temp = ExAllocatePoolWithTag(PagedPool, numRects * sizeof(RECT), TAG_REGION)))
+ {
+ return 0;
+ }
+ if (reg->Buffer != NULL)
+ {
+ COPY_RECTS(temp, reg->Buffer, reg->rdh.nCount);
+ if (reg->Buffer != ®->rdh.rcBound)
+ ExFreePoolWithTag(reg->Buffer, TAG_REGION);
+ }
+ reg->Buffer = temp;
+
+ reg->rdh.nCount = numRects;
+ CurPtBlock = FirstPtBlock;
+ rects = reg->Buffer - 1;
+ numRects = 0;
+ extents->left = LARGE_COORDINATE, extents->right = SMALL_COORDINATE;
+
+ for ( ; numFullPtBlocks >= 0; numFullPtBlocks--)
+ {
+ /* the loop uses 2 points per iteration */
+ i = NUMPTSTOBUFFER >> 1;
+ if (!numFullPtBlocks)
+ i = iCurPtBlock >> 1;
+ for (pts = CurPtBlock->pts; i--; pts += 2)
+ {
+ if (pts->x == pts[1].x)
+ continue;
+ if (numRects && pts->x == rects->left && pts->y == rects->bottom &&
+ pts[1].x == rects->right &&
+ (numRects == 1 || rects[-1].top != rects->top) &&
+ (i && pts[2].y > pts[1].y))
+ {
+ rects->bottom = pts[1].y + 1;
+ continue;
+ }
+ numRects++;
+ rects++;
+ rects->left = pts->x;
+ rects->top = pts->y;
+ rects->right = pts[1].x;
+ rects->bottom = pts[1].y + 1;
+ if (rects->left < extents->left)
+ extents->left = rects->left;
+ if (rects->right > extents->right)
+ extents->right = rects->right;
+ }
+ CurPtBlock = CurPtBlock->next;
+ }
+
+ if (numRects)
+ {
+ extents->top = reg->Buffer->top;
+ extents->bottom = rects->bottom;
+ }
+ else
+ {
+ extents->left = 0;
+ extents->top = 0;
+ extents->right = 0;
+ extents->bottom = 0;
+ }
+ reg->rdh.nCount = numRects;
+
+ return(TRUE);
+}
+
+/***********************************************************************
+ * REGION_CreateEdgeTable
+ *
+ * This routine creates the edge table for
+ * scan converting polygons.
+ * The Edge Table (ET) looks like:
+ *
+ * EdgeTable
+ * --------
+ * | ymax | ScanLineLists
+ * |scanline|-->------------>-------------->...
+ * -------- |scanline| |scanline|
+ * |edgelist| |edgelist|
+ * --------- ---------
+ * | |
+ * | |
+ * V V
+ * list of ETEs list of ETEs
+ *
+ * where ETE is an EdgeTableEntry data structure,
+ * and there is one ScanLineList per scanline at
+ * which an edge is initially entered.
+ *
+ */
+static void FASTCALL
+REGION_CreateETandAET(
+ const ULONG *Count,
+ INT nbpolygons,
+ const POINT *pts,
+ EdgeTable *ET,
+ EdgeTableEntry *AET,
+ EdgeTableEntry *pETEs,
+ ScanLineListBlock *pSLLBlock
+)
+{
+ const POINT *top, *bottom;
+ const POINT *PrevPt, *CurrPt, *EndPt;
+ INT poly, count;
+ int iSLLBlock = 0;
+ int dy;
+
+
+ /*
+ * initialize the Active Edge Table
+ */
+ AET->next = (EdgeTableEntry *)NULL;
+ AET->back = (EdgeTableEntry *)NULL;
+ AET->nextWETE = (EdgeTableEntry *)NULL;
+ AET->bres.minor_axis = SMALL_COORDINATE;
+
+ /*
+ * initialize the Edge Table.
+ */
+ ET->scanlines.next = (ScanLineList *)NULL;
+ ET->ymax = SMALL_COORDINATE;
+ ET->ymin = LARGE_COORDINATE;
+ pSLLBlock->next = (ScanLineListBlock *)NULL;
+
+ EndPt = pts - 1;
+ for (poly = 0; poly < nbpolygons; poly++)
+ {
+ count = Count[poly];
+ EndPt += count;
+ if (count < 2)
+ continue;
+
+ PrevPt = EndPt;
+
+ /*
+ * for each vertex in the array of points.
+ * In this loop we are dealing with two vertices at
+ * a time -- these make up one edge of the polygon.
+ */
+ while (count--)
+ {
+ CurrPt = pts++;
+
+ /*
+ * find out which point is above and which is below.
+ */
+ if (PrevPt->y > CurrPt->y)
+ {
+ bottom = PrevPt, top = CurrPt;
+ pETEs->ClockWise = 0;
+ }
+ else
+ {
+ bottom = CurrPt, top = PrevPt;
+ pETEs->ClockWise = 1;
+ }
+
+ /*
+ * don't add horizontal edges to the Edge table.
+ */
+ if (bottom->y != top->y)
+ {
+ pETEs->ymax = bottom->y-1;
+ /* -1 so we don't get last scanline */
+
+ /*
+ * initialize integer edge algorithm
+ */
+ dy = bottom->y - top->y;
+ BRESINITPGONSTRUCT(dy, top->x, bottom->x, pETEs->bres);
+
+ REGION_InsertEdgeInET(ET, pETEs, top->y, &pSLLBlock,
+ &iSLLBlock);
+
+ if (PrevPt->y > ET->ymax)
+ ET->ymax = PrevPt->y;
+ if (PrevPt->y < ET->ymin)
+ ET->ymin = PrevPt->y;
+ pETEs++;
+ }
+
+ PrevPt = CurrPt;
+ }
+ }
+}
+
+HRGN FASTCALL
+IntCreatePolyPolygonRgn(
+ POINT *Pts,
+ PULONG Count,
+ INT nbpolygons,
+ INT mode
+)
+{
+ HRGN hrgn;
+ ROSRGNDATA *region;
+ EdgeTableEntry *pAET; /* Active Edge Table */
+ INT y; /* current scanline */
+ int iPts = 0; /* number of pts in buffer */
+ EdgeTableEntry *pWETE; /* Winding Edge Table Entry*/
+ ScanLineList *pSLL; /* current scanLineList */
+ POINT *pts; /* output buffer */
+ EdgeTableEntry *pPrevAET; /* ptr to previous AET */
+ EdgeTable ET; /* header node for ET */
+ EdgeTableEntry AET; /* header node for AET */
+ EdgeTableEntry *pETEs; /* EdgeTableEntries pool */
+ ScanLineListBlock SLLBlock; /* header for scanlinelist */
+ int fixWAET = FALSE;
+ POINTBLOCK FirstPtBlock, *curPtBlock; /* PtBlock buffers */
+ POINTBLOCK *tmpPtBlock;
+ int numFullPtBlocks = 0;
+ INT poly, total;
+
+ if (mode == 0 || mode > 2) return 0;
+
+ if (!(region = REGION_AllocUserRgnWithHandle(nbpolygons)))
+ return 0;
+ hrgn = region->BaseObject.hHmgr;
+
+ /* special case a rectangle */
+
+ if (((nbpolygons == 1) && ((*Count == 4) ||
+ ((*Count == 5) && (Pts[4].x == Pts[0].x) && (Pts[4].y == Pts[0].y)))) &&
+ (((Pts[0].y == Pts[1].y) &&
+ (Pts[1].x == Pts[2].x) &&
+ (Pts[2].y == Pts[3].y) &&
+ (Pts[3].x == Pts[0].x)) ||
+ ((Pts[0].x == Pts[1].x) &&
+ (Pts[1].y == Pts[2].y) &&
+ (Pts[2].x == Pts[3].x) &&
+ (Pts[3].y == Pts[0].y))))
+ {
+ RGNOBJAPI_Unlock(region);
+ NtGdiSetRectRgn(hrgn, min(Pts[0].x, Pts[2].x), min(Pts[0].y, Pts[2].y),
+ max(Pts[0].x, Pts[2].x), max(Pts[0].y, Pts[2].y));
+ return hrgn;
+ }
+
+ for (poly = total = 0; poly < nbpolygons; poly++)
+ total += Count[poly];
+ if (! (pETEs = ExAllocatePoolWithTag(PagedPool, sizeof(EdgeTableEntry) * total, TAG_REGION)) )
+ {
+ GreDeleteObject(hrgn);
+ return 0;
+ }
+ pts = FirstPtBlock.pts;
+ REGION_CreateETandAET(Count, nbpolygons, Pts, &ET, &AET, pETEs, &SLLBlock);
+ pSLL = ET.scanlines.next;
+ curPtBlock = &FirstPtBlock;
+
+ if (mode != WINDING)
+ {
+ /*
+ * for each scanline
+ */
+ for (y = ET.ymin; y < ET.ymax; y++)
+ {
+ /*
+ * Add a new edge to the active edge table when we
+ * get to the next edge.
+ */
+ if (pSLL != NULL && y == pSLL->scanline)
+ {
+ REGION_loadAET(&AET, pSLL->edgelist);
+ pSLL = pSLL->next;
+ }
+ pPrevAET = &AET;
+ pAET = AET.next;
+
+ /*
+ * for each active edge
+ */
+ while (pAET)
+ {
+ pts->x = pAET->bres.minor_axis, pts->y = y;
+ pts++, iPts++;
+
+ /*
+ * send out the buffer
+ */
+ if (iPts == NUMPTSTOBUFFER)
+ {
+ tmpPtBlock = ExAllocatePoolWithTag(PagedPool, sizeof(POINTBLOCK), TAG_REGION);
+ if (!tmpPtBlock)
+ {
+ DPRINT1("Can't alloc tPB\n");
+ ExFreePoolWithTag(pETEs, TAG_REGION);
+ return 0;
+ }
+ curPtBlock->next = tmpPtBlock;
+ curPtBlock = tmpPtBlock;
+ pts = curPtBlock->pts;
+ numFullPtBlocks++;
+ iPts = 0;
+ }
+ EVALUATEEDGEEVENODD(pAET, pPrevAET, y);
+ }
+ REGION_InsertionSort(&AET);
+ }
+ }
+ else
+ {
+ /*
+ * for each scanline
+ */
+ for (y = ET.ymin; y < ET.ymax; y++)
+ {
+ /*
+ * Add a new edge to the active edge table when we
+ * get to the next edge.
+ */
+ if (pSLL != NULL && y == pSLL->scanline)
+ {
+ REGION_loadAET(&AET, pSLL->edgelist);
+ REGION_computeWAET(&AET);
+ pSLL = pSLL->next;
+ }
+ pPrevAET = &AET;
+ pAET = AET.next;
+ pWETE = pAET;
+
+ /*
+ * for each active edge
+ */
+ while (pAET)
+ {
+ /*
+ * add to the buffer only those edges that
+ * are in the Winding active edge table.
+ */
+ if (pWETE == pAET)
+ {
+ pts->x = pAET->bres.minor_axis, pts->y = y;
+ pts++, iPts++;
+
+ /*
+ * send out the buffer
+ */
+ if (iPts == NUMPTSTOBUFFER)
+ {
+ tmpPtBlock = ExAllocatePoolWithTag(PagedPool,
+ sizeof(POINTBLOCK), TAG_REGION);
+ if (!tmpPtBlock)
+ {
+ DPRINT1("Can't alloc tPB\n");
+ ExFreePoolWithTag(pETEs, TAG_REGION);
+ GreDeleteObject(hrgn);
+ return 0;
+ }
+ curPtBlock->next = tmpPtBlock;
+ curPtBlock = tmpPtBlock;
+ pts = curPtBlock->pts;
+ numFullPtBlocks++;
+ iPts = 0;
+ }
+ pWETE = pWETE->nextWETE;
+ }
+ EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET);
+ }
+
+ /*
+ * recompute the winding active edge table if
+ * we just resorted or have exited an edge.
+ */
+ if (REGION_InsertionSort(&AET) || fixWAET)
+ {
+ REGION_computeWAET(&AET);
+ fixWAET = FALSE;
+ }
+ }
+ }
+ REGION_FreeStorage(SLLBlock.next);
+ REGION_PtsToRegion(numFullPtBlocks, iPts, &FirstPtBlock, region);
+
+ for (curPtBlock = FirstPtBlock.next; --numFullPtBlocks >= 0;)
+ {
+ tmpPtBlock = curPtBlock->next;
+ ExFreePoolWithTag(curPtBlock, TAG_REGION);
+ curPtBlock = tmpPtBlock;
+ }
+ ExFreePoolWithTag(pETEs, TAG_REGION);
+ RGNOBJAPI_Unlock(region);
+ return hrgn;
+}
+
+//
+// NtGdi Exported Functions
+//
+INT
+APIENTRY
+NtGdiCombineRgn(HRGN hDest,
+ HRGN hSrc1,
+ HRGN hSrc2,
+ INT CombineMode)
+{
+ INT result = ERROR;
+ PROSRGNDATA destRgn, src1Rgn, src2Rgn = NULL;
+
+ if ( CombineMode > RGN_COPY && CombineMode < RGN_AND)
+ {
+ SetLastWin32Error(ERROR_INVALID_PARAMETER);
+ return ERROR;
+ }
+
+ destRgn = RGNOBJAPI_Lock(hDest, NULL);
+ if (!destRgn)
+ {
+ SetLastWin32Error(ERROR_INVALID_HANDLE);
+ return ERROR;
+ }
+
+ src1Rgn = RGNOBJAPI_Lock(hSrc1, NULL);
+ if (!src1Rgn)
+ {
+ RGNOBJAPI_Unlock(destRgn);
+ SetLastWin32Error(ERROR_INVALID_HANDLE);
+ return ERROR;
+ }
+
+ if (hSrc2)
+ src2Rgn = RGNOBJAPI_Lock(hSrc2, NULL);
+
+ result = IntGdiCombineRgn( destRgn, src1Rgn, src2Rgn, CombineMode);
+
+ if (src2Rgn)
+ RGNOBJAPI_Unlock(src2Rgn);
+ RGNOBJAPI_Unlock(src1Rgn);
+ RGNOBJAPI_Unlock(destRgn);
+
+ return result;
+}
+
+HRGN
+APIENTRY
+NtGdiCreateEllipticRgn(
+ INT Left,
+ INT Top,
+ INT Right,
+ INT Bottom
+)
+{
+ return NtGdiCreateRoundRectRgn(Left, Top, Right, Bottom,
+ Right - Left, Bottom - Top);
+}
+
+HRGN APIENTRY
+NtGdiCreateRectRgn(INT LeftRect, INT TopRect, INT RightRect, INT BottomRect)
+{
+ PROSRGNDATA pRgn;
+ HRGN hRgn;
+
+ /* Allocate region data structure with space for 1 RECTL */
+ if (!(pRgn = REGION_AllocUserRgnWithHandle(1)))
+ {
+ SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
+ return NULL;
+ }
+ hRgn = pRgn->BaseObject.hHmgr;
+
+ REGION_SetRectRgn(pRgn, LeftRect, TopRect, RightRect, BottomRect);
+ RGNOBJAPI_Unlock(pRgn);
+
+ return hRgn;
+}
+
+HRGN
+APIENTRY
+NtGdiCreateRoundRectRgn(
+ INT left,
+ INT top,
+ INT right,
+ INT bottom,
+ INT ellipse_width,
+ INT ellipse_height
+)
+{
+ PROSRGNDATA obj;
+ HRGN hrgn;
+ int asq, bsq, d, xd, yd;
+ RECTL rect;
+
+ /* Make the dimensions sensible */
+
+ if (left > right)
+ {
+ INT tmp = left;
+ left = right;
+ right = tmp;
+ }
+ if (top > bottom)
+ {
+ INT tmp = top;
+ top = bottom;
+ bottom = tmp;
+ }
+
+ ellipse_width = abs(ellipse_width);
+ ellipse_height = abs(ellipse_height);
+
+ /* Check parameters */
+
+ if (ellipse_width > right-left) ellipse_width = right-left;
+ if (ellipse_height > bottom-top) ellipse_height = bottom-top;
+
+ /* Check if we can do a normal rectangle instead */
+
+ if ((ellipse_width < 2) || (ellipse_height < 2))
+ return NtGdiCreateRectRgn(left, top, right, bottom);
+
+ /* Create region */
+
+ d = (ellipse_height < 128) ? ((3 * ellipse_height) >> 2) : 64;
+ if (!(obj = REGION_AllocUserRgnWithHandle(d))) return 0;
+ hrgn = obj->BaseObject.hHmgr;
+
+ /* Ellipse algorithm, based on an article by K. Porter */
+ /* in DDJ Graphics Programming Column, 8/89 */
+
+ asq = ellipse_width * ellipse_width / 4; /* a^2 */
+ bsq = ellipse_height * ellipse_height / 4; /* b^2 */
+ d = bsq - asq * ellipse_height / 2 + asq / 4; /* b^2 - a^2b + a^2/4 */
+ xd = 0;
+ yd = asq * ellipse_height; /* 2a^2b */
+
+ rect.left = left + ellipse_width / 2;
+ rect.right = right - ellipse_width / 2;
+
+ /* Loop to draw first half of quadrant */
+
+ while (xd < yd)
+ {
+ if (d > 0) /* if nearest pixel is toward the center */
+ {
+ /* move toward center */
+ rect.top = top++;
+ rect.bottom = rect.top + 1;
+ REGION_UnionRectWithRgn(obj, &rect);
+ rect.top = --bottom;
+ rect.bottom = rect.top + 1;
+ REGION_UnionRectWithRgn(obj, &rect);
+ yd -= 2*asq;
+ d -= yd;
+ }
+ rect.left--; /* next horiz point */
+ rect.right++;
+ xd += 2*bsq;
+ d += bsq + xd;
+ }
+ /* Loop to draw second half of quadrant */
+
+ d += (3 * (asq-bsq) / 2 - (xd+yd)) / 2;
+ while (yd >= 0)
+ {
+ /* next vertical point */
+ rect.top = top++;
+ rect.bottom = rect.top + 1;
+ REGION_UnionRectWithRgn(obj, &rect);
+ rect.top = --bottom;
+ rect.bottom = rect.top + 1;
+ REGION_UnionRectWithRgn(obj, &rect);
+ if (d < 0) /* if nearest pixel is outside ellipse */
+ {
+ rect.left--; /* move away from center */
+ rect.right++;
+ xd += 2*bsq;
+ d += xd;
+ }
+ yd -= 2*asq;
+ d += asq - yd;
+ }
+ /* Add the inside rectangle */
+
+ if (top <= bottom)
+ {
+ rect.top = top;
+ rect.bottom = bottom;
+ REGION_UnionRectWithRgn(obj, &rect);
+ }
+
+ RGNOBJAPI_Unlock(obj);
+ return hrgn;
+}
+
+BOOL
+APIENTRY
+NtGdiEqualRgn(
+ HRGN hSrcRgn1,
+ HRGN hSrcRgn2
+)
+{
+ PROSRGNDATA rgn1, rgn2;
+ PRECTL tRect1, tRect2;
+ ULONG i;
+ BOOL bRet = FALSE;
+
+ if ( !(rgn1 = RGNOBJAPI_Lock(hSrcRgn1, NULL)) )
+ return ERROR;
+
+ if ( !(rgn2 = RGNOBJAPI_Lock(hSrcRgn2, NULL)) )
+ {
+ RGNOBJAPI_Unlock(rgn1);
+ return ERROR;
+ }
+
+ if ( rgn1->rdh.nCount != rgn2->rdh.nCount ) goto exit;
+
+ if ( rgn1->rdh.nCount == 0 )
+ {
+ bRet = TRUE;
+ goto exit;
+ }
+
+ if ( rgn1->rdh.rcBound.left != rgn2->rdh.rcBound.left ||
+ rgn1->rdh.rcBound.right != rgn2->rdh.rcBound.right ||
+ rgn1->rdh.rcBound.top != rgn2->rdh.rcBound.top ||
+ rgn1->rdh.rcBound.bottom != rgn2->rdh.rcBound.bottom )
+ goto exit;
+
+ tRect1 = rgn1->Buffer;
+ tRect2 = rgn2->Buffer;
+
+ if (!tRect1 || !tRect2)
+ goto exit;
+
+ for (i=0; i < rgn1->rdh.nCount; i++)
+ {
+ if ( tRect1[i].left != tRect2[i].left ||
+ tRect1[i].right != tRect2[i].right ||
+ tRect1[i].top != tRect2[i].top ||
+ tRect1[i].bottom != tRect2[i].bottom )
+ goto exit;
+ }
+ bRet = TRUE;
+
+exit:
+ RGNOBJAPI_Unlock(rgn1);
+ RGNOBJAPI_Unlock(rgn2);
+ return bRet;
+}
+
+HRGN
+APIENTRY
+NtGdiExtCreateRegion(
+ OPTIONAL LPXFORM Xform,
+ DWORD Count,
+ LPRGNDATA RgnData
+)
+{
+ HRGN hRgn;
+ PROSRGNDATA Region;
+ DWORD nCount = 0;
+ DWORD iType = 0;
+ DWORD dwSize = 0;
+ NTSTATUS Status = STATUS_SUCCESS;
+ MATRIX matrix;
+
+ DPRINT("NtGdiExtCreateRegion\n");
+ _SEH2_TRY
+ {
+ ProbeForRead(RgnData, Count, 1);
+ nCount = RgnData->rdh.nCount;
+ iType = RgnData->rdh.iType;
+ dwSize = RgnData->rdh.dwSize;
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ Status = _SEH2_GetExceptionCode();
+ }
+ _SEH2_END;
+ if (!NT_SUCCESS(Status))
+ {
+ SetLastNtError(Status);
+ return NULL;
+ }
+
+ /* Check parameters, but don't set last error here */
+ if (Count < sizeof(RGNDATAHEADER) + nCount * sizeof(RECT) ||
+ iType != RDH_RECTANGLES ||
+ dwSize != sizeof(RGNDATAHEADER))
+ {
+ return NULL;
+ }
+
+ Region = REGION_AllocUserRgnWithHandle(nCount);
+
+ if (Region == NULL)
+ {
+ SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
+ return FALSE;
+ }
+ hRgn = Region->BaseObject.hHmgr;
+
+ _SEH2_TRY
+ {
+ if (Xform)
+ {
+ ULONG ret;
+
+ /* Init the XFORMOBJ from the Xform struct */
+ Status = STATUS_INVALID_PARAMETER;
+ ret = XFORMOBJ_iSetXform((XFORMOBJ*)&matrix, (XFORML*)Xform);
+
+ /* Check for error, also no scale and shear allowed */
+ if (ret != DDI_ERROR && ret != GX_GENERAL)
+ {
+ /* Apply the coordinate transformation on the rects */
+ if (XFORMOBJ_bApplyXform((XFORMOBJ*)&matrix,
+ XF_LTOL,
+ nCount * 2,
+ RgnData->Buffer,
+ Region->Buffer))
+ {
+ Status = STATUS_SUCCESS;
+ }
+ }
+ }
+ else
+ {
+ /* Copy rect coordinates */
+ RtlCopyMemory(Region->Buffer,
+ RgnData->Buffer,
+ nCount * sizeof(RECT));
+ }
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ Status = _SEH2_GetExceptionCode();
+ }
+ _SEH2_END;
+ if (!NT_SUCCESS(Status))
+ {
+ SetLastWin32Error(ERROR_INVALID_PARAMETER);
+ RGNOBJAPI_Unlock(Region);
+ GreDeleteObject(hRgn);
+ return NULL;
+ }
+
+ RGNOBJAPI_Unlock(Region);
+
+ return hRgn;
+}
+
+BOOL
+APIENTRY
+NtGdiFillRgn(
+ HDC hDC,
+ HRGN hRgn,
+ HBRUSH hBrush
+)
+{
+ HBRUSH oldhBrush;
+ PROSRGNDATA rgn;
+ PRECTL r;
+
+ if (NULL == (rgn = RGNOBJAPI_Lock(hRgn, NULL)))
+ {
+ return FALSE;
+ }
+
+ if (NULL == (oldhBrush = NtGdiSelectBrush(hDC, hBrush)))
+ {
+ RGNOBJAPI_Unlock(rgn);
+ return FALSE;
+ }
+
+ for (r = rgn->Buffer; r < rgn->Buffer + rgn->rdh.nCount; r++)
+ {
+ NtGdiPatBlt(hDC, r->left, r->top, r->right - r->left, r->bottom - r->top, PATCOPY);
+ }
+
+ RGNOBJAPI_Unlock(rgn);
+ NtGdiSelectBrush(hDC, oldhBrush);
+
+ return TRUE;
+}
+
+BOOL
+APIENTRY
+NtGdiFrameRgn(
+ HDC hDC,
+ HRGN hRgn,
+ HBRUSH hBrush,
+ INT Width,
+ INT Height
+)
+{
+ HRGN FrameRgn;
+ BOOL Ret;
+
+ if (!(FrameRgn = IntSysCreateRectRgn(0, 0, 0, 0)))
+ {
+ return FALSE;
+ }
+ if (!REGION_CreateFrameRgn(FrameRgn, hRgn, Width, Height))
+ {
+ REGION_FreeRgnByHandle(FrameRgn);
+ return FALSE;
+ }
+
+ Ret = NtGdiFillRgn(hDC, FrameRgn, hBrush);
+
+ REGION_FreeRgnByHandle(FrameRgn);
+ return Ret;
+}
+
+
+/* See wine, msdn, osr and Feng Yuan - Windows Graphics Programming Win32 Gdi And Directdraw
+
+ 1st: http://www.codeproject.com/gdi/cliprgnguide.asp is wrong!
+
+ The intersection of the clip with the meta region is not Rao it's API!
+ Go back and read 7.2 Clipping pages 418-19:
+ Rao = API & Vis:
+ 1) The Rao region is the intersection of the API region and the system region,
+ named after the Microsoft engineer who initially proposed it.
+ 2) The Rao region can be calculated from the API region and the system region.
+
+ API:
+ API region is the intersection of the meta region and the clipping region,
+ clearly named after the fact that it is controlled by GDI API calls.
+*/
+INT APIENTRY
+NtGdiGetRandomRgn(
+ HDC hDC,
+ HRGN hDest,
+ INT iCode
+)
+{
+ INT ret = 0;
+ PDC pDC;
+ HRGN hSrc = NULL;
+ POINT org;
+
+ pDC = DC_LockDc(hDC);
+ if (pDC == NULL)
+ {
+ SetLastWin32Error(ERROR_INVALID_HANDLE);
+ return -1;
+ }
+
+ switch (iCode)
+ {
+ case CLIPRGN:
+ hSrc = pDC->rosdc.hClipRgn;
+// if (pDC->dclevel.prgnClip) hSrc = ((PROSRGNDATA)pDC->dclevel.prgnClip)->BaseObject.hHmgr;
+ break;
+ case METARGN:
+ if (pDC->dclevel.prgnMeta) hSrc = ((PROSRGNDATA)pDC->dclevel.prgnMeta)->BaseObject.hHmgr;
+ break;
+ case APIRGN:
+ if (pDC->prgnAPI) hSrc = ((PROSRGNDATA)pDC->prgnAPI)->BaseObject.hHmgr;
+// else if (pDC->dclevel.prgnClip) hSrc = ((PROSRGNDATA)pDC->dclevel.prgnClip)->BaseObject.hHmgr;
+ else if (pDC->rosdc.hClipRgn) hSrc = pDC->rosdc.hClipRgn;
+ else if (pDC->dclevel.prgnMeta) hSrc = ((PROSRGNDATA)pDC->dclevel.prgnMeta)->BaseObject.hHmgr;
+ break;
+ case SYSRGN:
+ if (pDC->prgnVis) hSrc = pDC->prgnVis->BaseObject.hHmgr;
+ break;
+ default:
+ hSrc = 0;
+ }
+ if (hSrc)
+ {
+ if (NtGdiCombineRgn(hDest, hSrc, 0, RGN_COPY) == ERROR)
+ {
+ ret = -1;
+ }
+ else
+ {
+ ret = 1;
+ }
+ }
+ if (iCode == SYSRGN)
+ {
+ IntGdiGetDCOrg(pDC, &org);
+ NtGdiOffsetRgn(hDest, org.x, org.y );
+ }
+
+ DC_UnlockDc(pDC);
+
+ return ret;
+}
+
+INT APIENTRY
+NtGdiGetRgnBox(
+ HRGN hRgn,
+ PRECTL pRect
+)
+{
+ PROSRGNDATA Rgn;
+ RECTL SafeRect;
+ DWORD ret;
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ if (!(Rgn = RGNOBJAPI_Lock(hRgn, NULL)))
+ {
+ return ERROR;
+ }
+
+ ret = REGION_GetRgnBox(Rgn, &SafeRect);
+ RGNOBJAPI_Unlock(Rgn);
+ if (ERROR == ret)
+ {
+ return ret;
+ }
+
+ _SEH2_TRY
+ {
+ ProbeForWrite(pRect, sizeof(RECT), 1);
+ *pRect = SafeRect;
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ Status = _SEH2_GetExceptionCode();
+ }
+ _SEH2_END;
+ if (!NT_SUCCESS(Status))
+ {
+ return ERROR;
+ }
+
+ return ret;
+}
+
+BOOL
+APIENTRY
+NtGdiInvertRgn(
+ HDC hDC,
+ HRGN hRgn
+)
+{
+ PROSRGNDATA RgnData;
+ ULONG i;
+ PRECTL rc;
+
+ if (!(RgnData = RGNOBJAPI_Lock(hRgn, NULL)))
+ {
+ SetLastWin32Error(ERROR_INVALID_HANDLE);
+ return FALSE;
+ }
+
+ rc = RgnData->Buffer;
+ for (i = 0; i < RgnData->rdh.nCount; i++)
+ {
+
+ if (!NtGdiPatBlt(hDC, rc->left, rc->top, rc->right - rc->left, rc->bottom - rc->top, DSTINVERT))
+ {
+ RGNOBJAPI_Unlock(RgnData);
+ return FALSE;
+ }
+ rc++;
+ }
+
+ RGNOBJAPI_Unlock(RgnData);
+ return TRUE;
+}
+
+INT
+APIENTRY
+NtGdiOffsetRgn(
+ HRGN hRgn,
+ INT XOffset,
+ INT YOffset
+)
+{
+ PROSRGNDATA rgn = RGNOBJAPI_Lock(hRgn, NULL);
+ INT ret;
+
+ DPRINT("NtGdiOffsetRgn: hRgn %d Xoffs %d Yoffs %d rgn %x\n", hRgn, XOffset, YOffset, rgn );
+
+ if (!rgn)
+ {
+ DPRINT("NtGdiOffsetRgn: hRgn error\n");
+ return ERROR;
+ }
+
+ ret = IntGdiOffsetRgn(rgn, XOffset, YOffset);
+
+ RGNOBJAPI_Unlock(rgn);
+ return ret;
+}
+
+BOOL
+APIENTRY
+NtGdiPtInRegion(
+ HRGN hRgn,
+ INT X,
+ INT Y
+)
+{
+ PROSRGNDATA rgn;
+ ULONG i;
+ PRECTL r;
+
+ if (!(rgn = RGNOBJAPI_Lock(hRgn, NULL) ) )
+ return FALSE;
+
+ if (rgn->rdh.nCount > 0 && INRECT(rgn->rdh.rcBound, X, Y))
+ {
+ r = rgn->Buffer;
+ for (i = 0; i < rgn->rdh.nCount; i++)
+ {
+ if (INRECT(*r, X, Y))
+ {
+ RGNOBJAPI_Unlock(rgn);
+ return TRUE;
+ }
+ r++;
+ }
+ }
+ RGNOBJAPI_Unlock(rgn);
+ return FALSE;
+}
+
+BOOL
+APIENTRY
+NtGdiRectInRegion(
+ HRGN hRgn,
+ LPRECTL unsaferc
+)
+{
+ PROSRGNDATA Rgn;
+ RECTL rc = {0};
+ BOOL Ret;
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ if (!(Rgn = RGNOBJAPI_Lock(hRgn, NULL)))
+ {
+ return ERROR;
+ }
+
+ _SEH2_TRY
+ {
+ ProbeForRead(unsaferc, sizeof(RECT), 1);
+ rc = *unsaferc;
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ Status = _SEH2_GetExceptionCode();
+ }
+ _SEH2_END;
+
+ if (!NT_SUCCESS(Status))
+ {
+ RGNOBJAPI_Unlock(Rgn);
+ SetLastNtError(Status);
+ DPRINT1("NtGdiRectInRegion: bogus rc\n");
+ return ERROR;
+ }
+
+ Ret = REGION_RectInRegion(Rgn, &rc);
+ RGNOBJAPI_Unlock(Rgn);
+ return Ret;
+}
+
+BOOL
+APIENTRY
+NtGdiSetRectRgn(
+ HRGN hRgn,
+ INT LeftRect,
+ INT TopRect,
+ INT RightRect,
+ INT BottomRect
+)
+{
+ PROSRGNDATA rgn;
+
+ if ( !(rgn = RGNOBJAPI_Lock(hRgn, NULL)) )
+ {
+ return 0; //per documentation
+ }
+
+ REGION_SetRectRgn(rgn, LeftRect, TopRect, RightRect, BottomRect);
+
+ RGNOBJAPI_Unlock(rgn);
+ return TRUE;
+}
+
+HRGN APIENTRY
+NtGdiUnionRectWithRgn(
+ HRGN hDest,
+ const RECTL *UnsafeRect
+)
+{
+ RECTL SafeRect = {0};
+ PROSRGNDATA Rgn;
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ if (!(Rgn = RGNOBJAPI_Lock(hDest, NULL)))
+ {
+ SetLastWin32Error(ERROR_INVALID_HANDLE);
+ return NULL;
+ }
+
+ _SEH2_TRY
+ {
+ ProbeForRead(UnsafeRect, sizeof(RECT), 1);
+ SafeRect = *UnsafeRect;
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ Status = _SEH2_GetExceptionCode();
+ }
+ _SEH2_END;
+
+ if (! NT_SUCCESS(Status))
+ {
+ RGNOBJAPI_Unlock(Rgn);
+ SetLastNtError(Status);
+ return NULL;
+ }
+
+ REGION_UnionRectWithRgn(Rgn, &SafeRect);
+ RGNOBJAPI_Unlock(Rgn);
+ return hDest;
+}
+
+/*!
+ * MSDN: GetRegionData, Return Values:
+ *
+ * "If the function succeeds and dwCount specifies an adequate number of bytes,
+ * the return value is always dwCount. If dwCount is too small or the function
+ * fails, the return value is 0. If lpRgnData is NULL, the return value is the
+ * required number of bytes.
+ *
+ * If the function fails, the return value is zero."
+ */
+DWORD APIENTRY
+NtGdiGetRegionData(
+ HRGN hrgn,
+ DWORD count,
+ LPRGNDATA rgndata
+)
+{
+ DWORD size;
+ PROSRGNDATA obj = RGNOBJAPI_Lock(hrgn, NULL);
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ if (!obj)
+ return 0;
+
+ size = obj->rdh.nCount * sizeof(RECT);
+ if (count < (size + sizeof(RGNDATAHEADER)) || rgndata == NULL)
+ {
+ RGNOBJAPI_Unlock(obj);
+ if (rgndata) /* buffer is too small, signal it by return 0 */
+ return 0;
+ else /* user requested buffer size with rgndata NULL */
+ return size + sizeof(RGNDATAHEADER);
+ }
+
+ _SEH2_TRY
+ {
+ ProbeForWrite(rgndata, count, 1);
+ RtlCopyMemory(rgndata, &obj->rdh, sizeof(RGNDATAHEADER));
+ RtlCopyMemory(rgndata->Buffer, obj->Buffer, size);
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ Status = _SEH2_GetExceptionCode();
+ }
+ _SEH2_END;
+
+ if (!NT_SUCCESS(Status))
+ {
+ SetLastNtError(Status);
+ RGNOBJAPI_Unlock(obj);
+ return 0;
+ }
+
+ RGNOBJAPI_Unlock(obj);
+ return size + sizeof(RGNDATAHEADER);
+}
+
+/* EOF */