From: Amine Khaldi Date: Thu, 13 Nov 2014 19:27:12 +0000 (+0000) Subject: * Sync up to trunk head (r65394). X-Git-Tag: backups/shell-experiments@75904~11 X-Git-Url: https://git.reactos.org/?p=reactos.git;a=commitdiff_plain;h=81dea422f67f60d295af47c3de621015233e70d8 * Sync up to trunk head (r65394). svn path=/branches/shell-experiments/; revision=65395 --- 81dea422f67f60d295af47c3de621015233e70d8 diff --cc boot/freeldr/freeldr/cache/blocklist.c index fa0fdffad28,00000000000..1b4c37a6866 mode 100644,000000..100644 --- a/boot/freeldr/freeldr/cache/blocklist.c +++ b/boot/freeldr/freeldr/cache/blocklist.c @@@ -1,237 -1,0 +1,237 @@@ +/* + * FreeLoader + * Copyright (C) 1998-2003 Brian Palmer + * + * 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. + */ + +#include +#include + +DBG_DEFAULT_CHANNEL(CACHE); + +// Returns a pointer to a CACHE_BLOCK structure +// Adds the block to the cache manager block list +// in cache memory if it isn't already there +PCACHE_BLOCK CacheInternalGetBlockPointer(PCACHE_DRIVE CacheDrive, ULONG BlockNumber) +{ + PCACHE_BLOCK CacheBlock = NULL; + + TRACE("CacheInternalGetBlockPointer() BlockNumber = %d\n", BlockNumber); + + CacheBlock = CacheInternalFindBlock(CacheDrive, BlockNumber); + + if (CacheBlock != NULL) + { + TRACE("Cache hit! BlockNumber: %d CacheBlock->BlockNumber: %d\n", BlockNumber, CacheBlock->BlockNumber); + + return CacheBlock; + } + + TRACE("Cache miss! BlockNumber: %d\n", BlockNumber); + + CacheBlock = CacheInternalAddBlockToCache(CacheDrive, BlockNumber); + + // Optimize the block list so it has a LRU structure + CacheInternalOptimizeBlockList(CacheDrive, CacheBlock); + + return CacheBlock; +} + +PCACHE_BLOCK CacheInternalFindBlock(PCACHE_DRIVE CacheDrive, ULONG BlockNumber) +{ + PCACHE_BLOCK CacheBlock = NULL; + + TRACE("CacheInternalFindBlock() BlockNumber = %d\n", BlockNumber); + + // + // Make sure the block list has entries before I start searching it. + // + if (!IsListEmpty(&CacheDrive->CacheBlockHead)) + { + // + // Search the list and find the BIOS drive number + // + CacheBlock = CONTAINING_RECORD(CacheDrive->CacheBlockHead.Flink, CACHE_BLOCK, ListEntry); + + while (&CacheBlock->ListEntry != &CacheDrive->CacheBlockHead) + { + // + // We found the block, so return it + // + if (CacheBlock->BlockNumber == BlockNumber) + { + // + // Increment the blocks access count + // + CacheBlock->AccessCount++; + + return CacheBlock; + } + + CacheBlock = CONTAINING_RECORD(CacheBlock->ListEntry.Flink, CACHE_BLOCK, ListEntry); + } + } + + return NULL; +} + +PCACHE_BLOCK CacheInternalAddBlockToCache(PCACHE_DRIVE CacheDrive, ULONG BlockNumber) +{ + PCACHE_BLOCK CacheBlock = NULL; + + TRACE("CacheInternalAddBlockToCache() BlockNumber = %d\n", BlockNumber); + + // Check the size of the cache so we don't exceed our limits + CacheInternalCheckCacheSizeLimits(CacheDrive); + + // We will need to add the block to the + // drive's list of cached blocks. So allocate + // the block memory. + CacheBlock = FrLdrTempAlloc(sizeof(CACHE_BLOCK), TAG_CACHE_BLOCK); + if (CacheBlock == NULL) + { + return NULL; + } + + // Now initialize the structure and + // allocate room for the block data + RtlZeroMemory(CacheBlock, sizeof(CACHE_BLOCK)); + CacheBlock->BlockNumber = BlockNumber; + CacheBlock->BlockData = FrLdrTempAlloc(CacheDrive->BlockSize * CacheDrive->BytesPerSector, + TAG_CACHE_DATA); + if (CacheBlock->BlockData ==NULL) + { + FrLdrTempFree(CacheBlock, TAG_CACHE_BLOCK); + return NULL; + } + + // Now try to read in the block + if (!MachDiskReadLogicalSectors(CacheDrive->DriveNumber, (BlockNumber * CacheDrive->BlockSize), CacheDrive->BlockSize, (PVOID)DISKREADBUFFER)) + { + FrLdrTempFree(CacheBlock->BlockData, TAG_CACHE_DATA); + FrLdrTempFree(CacheBlock, TAG_CACHE_BLOCK); + return NULL; + } + RtlCopyMemory(CacheBlock->BlockData, (PVOID)DISKREADBUFFER, CacheDrive->BlockSize * CacheDrive->BytesPerSector); + + // Add it to our list of blocks managed by the cache + InsertTailList(&CacheDrive->CacheBlockHead, &CacheBlock->ListEntry); + + // Update the cache data + CacheBlockCount++; + CacheSizeCurrent = CacheBlockCount * (CacheDrive->BlockSize * CacheDrive->BytesPerSector); + + CacheInternalDumpBlockList(CacheDrive); + + return CacheBlock; +} + +BOOLEAN CacheInternalFreeBlock(PCACHE_DRIVE CacheDrive) +{ + PCACHE_BLOCK CacheBlockToFree; + + TRACE("CacheInternalFreeBlock()\n"); + + // Get a pointer to the last item in the block list + // that isn't forced to be in the cache and remove + // it from the list + CacheBlockToFree = CONTAINING_RECORD(CacheDrive->CacheBlockHead.Blink, CACHE_BLOCK, ListEntry); - while (&CacheBlockToFree->ListEntry != &CacheDrive->CacheBlockHead && CacheBlockToFree->LockedInCache == TRUE) ++ while (&CacheBlockToFree->ListEntry != &CacheDrive->CacheBlockHead && CacheBlockToFree->LockedInCache) + { + CacheBlockToFree = CONTAINING_RECORD(CacheBlockToFree->ListEntry.Blink, CACHE_BLOCK, ListEntry); + } + + // No blocks left in cache that can be freed + // so just return + if (IsListEmpty(&CacheDrive->CacheBlockHead)) + { + return FALSE; + } + + RemoveEntryList(&CacheBlockToFree->ListEntry); + + // Free the block memory and the block structure + FrLdrTempFree(CacheBlockToFree->BlockData, TAG_CACHE_DATA); + FrLdrTempFree(CacheBlockToFree, TAG_CACHE_BLOCK); + + // Update the cache data + CacheBlockCount--; + CacheSizeCurrent = CacheBlockCount * (CacheDrive->BlockSize * CacheDrive->BytesPerSector); + + return TRUE; +} + +VOID CacheInternalCheckCacheSizeLimits(PCACHE_DRIVE CacheDrive) +{ + SIZE_T NewCacheSize; + + TRACE("CacheInternalCheckCacheSizeLimits()\n"); + + // Calculate the size of the cache if we added a block + NewCacheSize = (CacheBlockCount + 1) * (CacheDrive->BlockSize * CacheDrive->BytesPerSector); + + // Check the new size against the cache size limit + if (NewCacheSize > CacheSizeLimit) + { + CacheInternalFreeBlock(CacheDrive); + CacheInternalDumpBlockList(CacheDrive); + } +} + +VOID CacheInternalDumpBlockList(PCACHE_DRIVE CacheDrive) +{ + PCACHE_BLOCK CacheBlock; + + TRACE("Dumping block list for BIOS drive 0x%x.\n", CacheDrive->DriveNumber); + TRACE("BytesPerSector: %d.\n", CacheDrive->BytesPerSector); + TRACE("BlockSize: %d.\n", CacheDrive->BlockSize); + TRACE("CacheSizeLimit: %d.\n", CacheSizeLimit); + TRACE("CacheSizeCurrent: %d.\n", CacheSizeCurrent); + TRACE("CacheBlockCount: %d.\n", CacheBlockCount); + + CacheBlock = CONTAINING_RECORD(CacheDrive->CacheBlockHead.Flink, CACHE_BLOCK, ListEntry); + while (&CacheBlock->ListEntry != &CacheDrive->CacheBlockHead) + { + TRACE("Cache Block: CacheBlock: 0x%x\n", CacheBlock); + TRACE("Cache Block: Block Number: %d\n", CacheBlock->BlockNumber); + TRACE("Cache Block: Access Count: %d\n", CacheBlock->AccessCount); + TRACE("Cache Block: Block Data: 0x%x\n", CacheBlock->BlockData); + TRACE("Cache Block: Locked In Cache: %d\n", CacheBlock->LockedInCache); + + if (CacheBlock->BlockData == NULL) + { + BugCheck("CacheBlock->BlockData == NULL\n"); + } + + CacheBlock = CONTAINING_RECORD(CacheBlock->ListEntry.Flink, CACHE_BLOCK, ListEntry); + } +} + +VOID CacheInternalOptimizeBlockList(PCACHE_DRIVE CacheDrive, PCACHE_BLOCK CacheBlock) +{ + + TRACE("CacheInternalOptimizeBlockList()\n"); + + // Don't do this if this block is already at the head of the list + if (&CacheBlock->ListEntry != CacheDrive->CacheBlockHead.Flink) + { + // Remove this item from the block list + RemoveEntryList(&CacheBlock->ListEntry); + + // Re-insert it at the head of the list + InsertHeadList(&CacheDrive->CacheBlockHead, &CacheBlock->ListEntry); + } +} diff --cc boot/freeldr/freeldr/cache/cache.c index 105a3c46129,00000000000..92f52375547 mode 100644,000000..100644 --- a/boot/freeldr/freeldr/cache/cache.c +++ b/boot/freeldr/freeldr/cache/cache.c @@@ -1,324 -1,0 +1,324 @@@ +/* + * FreeLoader + * Copyright (C) 1998-2003 Brian Palmer + * + * 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. + */ + +#include +#include + +DBG_DEFAULT_CHANNEL(CACHE); + +/////////////////////////////////////////////////////////////////////////////////////// +// +// Internal data +// +/////////////////////////////////////////////////////////////////////////////////////// +CACHE_DRIVE CacheManagerDrive; +BOOLEAN CacheManagerInitialized = FALSE; +BOOLEAN CacheManagerDataInvalid = FALSE; +ULONG CacheBlockCount = 0; +SIZE_T CacheSizeLimit = 0; +SIZE_T CacheSizeCurrent = 0; + +BOOLEAN CacheInitializeDrive(UCHAR DriveNumber) +{ + PCACHE_BLOCK NextCacheBlock; + GEOMETRY DriveGeometry; + + // If we already have a cache for this drive then + // by all means lets keep it, unless it is a removable + // drive, in which case we'll invalidate the cache - if ((CacheManagerInitialized == TRUE) && ++ if ((CacheManagerInitialized) && + (DriveNumber == CacheManagerDrive.DriveNumber) && + (DriveNumber >= 0x80) && - (CacheManagerDataInvalid != TRUE)) ++ (!CacheManagerDataInvalid)) + { + return TRUE; + } + + CacheManagerDataInvalid = FALSE; + + // + // If we have already been initialized then free + // the old data + // + if (CacheManagerInitialized) + { + CacheManagerInitialized = FALSE; + + TRACE("CacheBlockCount: %d\n", CacheBlockCount); + TRACE("CacheSizeLimit: %d\n", CacheSizeLimit); + TRACE("CacheSizeCurrent: %d\n", CacheSizeCurrent); + // + // Loop through and free the cache blocks + // + while (!IsListEmpty(&CacheManagerDrive.CacheBlockHead)) + { + NextCacheBlock = CONTAINING_RECORD(RemoveHeadList(&CacheManagerDrive.CacheBlockHead), + CACHE_BLOCK, + ListEntry); + + FrLdrTempFree(NextCacheBlock->BlockData, TAG_CACHE_DATA); + FrLdrTempFree(NextCacheBlock, TAG_CACHE_BLOCK); + } + } + + // Initialize the structure + RtlZeroMemory(&CacheManagerDrive, sizeof(CACHE_DRIVE)); + InitializeListHead(&CacheManagerDrive.CacheBlockHead); + CacheManagerDrive.DriveNumber = DriveNumber; + if (!MachDiskGetDriveGeometry(DriveNumber, &DriveGeometry)) + { + return FALSE; + } + CacheManagerDrive.BytesPerSector = DriveGeometry.BytesPerSector; + + // Get the number of sectors in each cache block + CacheManagerDrive.BlockSize = MachDiskGetCacheableBlockCount(DriveNumber); + + CacheBlockCount = 0; + CacheSizeLimit = TotalPagesInLookupTable / 8 * MM_PAGE_SIZE; + CacheSizeCurrent = 0; + if (CacheSizeLimit > TEMP_HEAP_SIZE - (128 * 1024)) + { + CacheSizeLimit = TEMP_HEAP_SIZE - (128 * 1024); + } + + CacheManagerInitialized = TRUE; + + TRACE("Initializing BIOS drive 0x%x.\n", DriveNumber); + TRACE("BytesPerSector: %d.\n", CacheManagerDrive.BytesPerSector); + TRACE("BlockSize: %d.\n", CacheManagerDrive.BlockSize); + TRACE("CacheSizeLimit: %d.\n", CacheSizeLimit); + + return TRUE; +} + +VOID CacheInvalidateCacheData(VOID) +{ + CacheManagerDataInvalid = TRUE; +} + +BOOLEAN CacheReadDiskSectors(UCHAR DiskNumber, ULONGLONG StartSector, ULONG SectorCount, PVOID Buffer) +{ + PCACHE_BLOCK CacheBlock; + ULONG StartBlock; + ULONG SectorOffsetInStartBlock; + ULONG CopyLengthInStartBlock; + ULONG EndBlock; + ULONG SectorOffsetInEndBlock; + ULONG BlockCount; + ULONG Idx; + + TRACE("CacheReadDiskSectors() DiskNumber: 0x%x StartSector: %I64d SectorCount: %d Buffer: 0x%x\n", DiskNumber, StartSector, SectorCount, Buffer); + + // If we aren't initialized yet then they can't do this + if (CacheManagerInitialized == FALSE) + { + return FALSE; + } + + // + // Caculate which blocks we must cache + // + StartBlock = (ULONG)(StartSector / CacheManagerDrive.BlockSize); + SectorOffsetInStartBlock = (ULONG)(StartSector % CacheManagerDrive.BlockSize); + CopyLengthInStartBlock = (ULONG)((SectorCount > (CacheManagerDrive.BlockSize - SectorOffsetInStartBlock)) ? (CacheManagerDrive.BlockSize - SectorOffsetInStartBlock) : SectorCount); + EndBlock = (ULONG)((StartSector + (SectorCount - 1)) / CacheManagerDrive.BlockSize); + SectorOffsetInEndBlock = (ULONG)(1 + (StartSector + (SectorCount - 1)) % CacheManagerDrive.BlockSize); + BlockCount = (EndBlock - StartBlock) + 1; + TRACE("StartBlock: %d SectorOffsetInStartBlock: %d CopyLengthInStartBlock: %d EndBlock: %d SectorOffsetInEndBlock: %d BlockCount: %d\n", StartBlock, SectorOffsetInStartBlock, CopyLengthInStartBlock, EndBlock, SectorOffsetInEndBlock, BlockCount); + + // + // Read the first block into the buffer + // + if (BlockCount > 0) + { + // + // Get cache block pointer (this forces the disk sectors into the cache memory) + // + CacheBlock = CacheInternalGetBlockPointer(&CacheManagerDrive, StartBlock); + if (CacheBlock == NULL) + { + return FALSE; + } + + // + // Copy the portion requested into the buffer + // + RtlCopyMemory(Buffer, + (PVOID)((ULONG_PTR)CacheBlock->BlockData + (SectorOffsetInStartBlock * CacheManagerDrive.BytesPerSector)), + (CopyLengthInStartBlock * CacheManagerDrive.BytesPerSector)); + TRACE("1 - RtlCopyMemory(0x%x, 0x%x, %d)\n", Buffer, ((ULONG_PTR)CacheBlock->BlockData + (SectorOffsetInStartBlock * CacheManagerDrive.BytesPerSector)), (CopyLengthInStartBlock * CacheManagerDrive.BytesPerSector)); + + // + // Update the buffer address + // + Buffer = (PVOID)((ULONG_PTR)Buffer + (CopyLengthInStartBlock * CacheManagerDrive.BytesPerSector)); + + // + // Update the block count + // + BlockCount--; + } + + // + // Loop through the middle blocks and read them into the buffer + // + for (Idx=StartBlock+1; BlockCount>1; Idx++) + { + // + // Get cache block pointer (this forces the disk sectors into the cache memory) + // + CacheBlock = CacheInternalGetBlockPointer(&CacheManagerDrive, Idx); + if (CacheBlock == NULL) + { + return FALSE; + } + + // + // Copy the portion requested into the buffer + // + RtlCopyMemory(Buffer, + CacheBlock->BlockData, + CacheManagerDrive.BlockSize * CacheManagerDrive.BytesPerSector); + TRACE("2 - RtlCopyMemory(0x%x, 0x%x, %d)\n", Buffer, CacheBlock->BlockData, CacheManagerDrive.BlockSize * CacheManagerDrive.BytesPerSector); + + // + // Update the buffer address + // + Buffer = (PVOID)((ULONG_PTR)Buffer + (CacheManagerDrive.BlockSize * CacheManagerDrive.BytesPerSector)); + + // + // Update the block count + // + BlockCount--; + } + + // + // Read the last block into the buffer + // + if (BlockCount > 0) + { + // + // Get cache block pointer (this forces the disk sectors into the cache memory) + // + CacheBlock = CacheInternalGetBlockPointer(&CacheManagerDrive, EndBlock); + if (CacheBlock == NULL) + { + return FALSE; + } + + // + // Copy the portion requested into the buffer + // + RtlCopyMemory(Buffer, + CacheBlock->BlockData, + SectorOffsetInEndBlock * CacheManagerDrive.BytesPerSector); + TRACE("3 - RtlCopyMemory(0x%x, 0x%x, %d)\n", Buffer, CacheBlock->BlockData, SectorOffsetInEndBlock * CacheManagerDrive.BytesPerSector); + + // + // Update the buffer address + // + Buffer = (PVOID)((ULONG_PTR)Buffer + (SectorOffsetInEndBlock * CacheManagerDrive.BytesPerSector)); + + // + // Update the block count + // + BlockCount--; + } + + return TRUE; +} + +#if 0 +BOOLEAN CacheForceDiskSectorsIntoCache(UCHAR DiskNumber, ULONGLONG StartSector, ULONG SectorCount) +{ + PCACHE_BLOCK CacheBlock; + ULONG StartBlock; + ULONG EndBlock; + ULONG BlockCount; + ULONG Idx; + + TRACE("CacheForceDiskSectorsIntoCache() DiskNumber: 0x%x StartSector: %d SectorCount: %d\n", DiskNumber, StartSector, SectorCount); + + // If we aren't initialized yet then they can't do this + if (CacheManagerInitialized == FALSE) + { + return FALSE; + } + + // + // Caculate which blocks we must cache + // + StartBlock = StartSector / CacheManagerDrive.BlockSize; + EndBlock = (StartSector + SectorCount) / CacheManagerDrive.BlockSize; + BlockCount = (EndBlock - StartBlock) + 1; + + // + // Loop through and cache them + // + for (Idx=StartBlock; Idx<(StartBlock+BlockCount); Idx++) + { + // + // Get cache block pointer (this forces the disk sectors into the cache memory) + // + CacheBlock = CacheInternalGetBlockPointer(&CacheManagerDrive, Idx); + if (CacheBlock == NULL) + { + return FALSE; + } + + // + // Lock the sectors into the cache + // + CacheBlock->LockedInCache = TRUE; + } + + return TRUE; +} +#endif + +BOOLEAN CacheReleaseMemory(ULONG MinimumAmountToRelease) +{ + ULONG AmountReleased; + + TRACE("CacheReleaseMemory() MinimumAmountToRelease = %d\n", MinimumAmountToRelease); + + // If we aren't initialized yet then they can't do this + if (CacheManagerInitialized == FALSE) + { + return FALSE; + } + + // Loop through and try to free the requested amount of memory + for (AmountReleased=0; AmountReleased= MinimumAmountToRelease); +} diff --cc boot/freeldr/freeldr/linuxboot.c index f7cb5bfe17f,00000000000..7d431d5ae10 mode 100644,000000..100644 --- a/boot/freeldr/freeldr/linuxboot.c +++ b/boot/freeldr/freeldr/linuxboot.c @@@ -1,517 -1,0 +1,517 @@@ +/* + * FreeLoader + * Copyright (C) 1998-2003 Brian Palmer + * + * 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. + */ + +#ifndef _M_ARM + +#include +#include +#ifdef __i386__ +#define LINUX_READ_CHUNK_SIZE 0x20000 // Read 128k at a time + +DBG_DEFAULT_CHANNEL(LINUX); + +PLINUX_BOOTSECTOR LinuxBootSector = NULL; +PLINUX_SETUPSECTOR LinuxSetupSector = NULL; +ULONG SetupSectorSize = 0; +BOOLEAN NewStyleLinuxKernel = FALSE; +ULONG LinuxKernelSize = 0; +ULONG LinuxInitrdSize = 0; +CHAR LinuxKernelName[260]; +CHAR LinuxInitrdName[260]; +BOOLEAN LinuxHasInitrd = FALSE; +CHAR LinuxCommandLine[260] = ""; +ULONG LinuxCommandLineSize = 0; +PVOID LinuxKernelLoadAddress = NULL; +PVOID LinuxInitrdLoadAddress = NULL; +CHAR LinuxBootDescription[80]; +CHAR LinuxBootPath[260] = ""; + +BOOLEAN RemoveQuotes(PCHAR QuotedString) +{ + CHAR TempString[200]; + PCHAR p; + PSTR Start; + + // + // Skip spaces up to " + // + p = QuotedString; + while (*p == ' ' || *p == '"') + p++; + Start = p; + + // + // Go up to next " + // + while (*p != '"' && *p != ANSI_NULL) + p++; + *p = ANSI_NULL; + + // + // Copy result + // + strcpy(TempString, Start); + strcpy(QuotedString, TempString); + + return TRUE; +} + +VOID +LoadAndBootLinux(IN OperatingSystemItem* OperatingSystem, + IN USHORT OperatingSystemVersion) +{ + PCSTR SectionName = OperatingSystem->SystemPartition; + PCSTR Description = OperatingSystem->LoadIdentifier; + PFILE LinuxKernel = 0; + PFILE LinuxInitrdFile = 0; + CHAR TempString[260]; + + UiDrawBackdrop(); + + if (Description) + { + sprintf(LinuxBootDescription, "Loading %s...", Description); + } + else + { + strcpy(LinuxBootDescription, "Loading Linux..."); + } + + UiDrawStatusText(LinuxBootDescription); + UiDrawProgressBarCenter(0, 100, LinuxBootDescription); + + // Parse the .ini file section + if (!LinuxParseIniSection(SectionName)) + { + goto LinuxBootFailed; + } + + // Open the kernel + LinuxKernel = FsOpenFile(LinuxKernelName); + if (!LinuxKernel) + { + sprintf(TempString, "Linux kernel \'%s\' not found.", LinuxKernelName); + UiMessageBox(TempString); + goto LinuxBootFailed; + } + + // Open the initrd file image (if necessary) + if (LinuxHasInitrd) + { + LinuxInitrdFile = FsOpenFile(LinuxInitrdName); + if (!LinuxInitrdFile) + { + sprintf(TempString, "Linux initrd image \'%s\' not found.", LinuxInitrdName); + UiMessageBox(TempString); + goto LinuxBootFailed; + } + } + + // Read the boot sector + if (!LinuxReadBootSector(LinuxKernel)) + { + goto LinuxBootFailed; + } + + // Read the setup sector + if (!LinuxReadSetupSector(LinuxKernel)) + { + goto LinuxBootFailed; + } + + // Calc kernel size + LinuxKernelSize = FsGetFileSize(LinuxKernel) - (512 + SetupSectorSize); + + // Get the file size + LinuxInitrdSize = FsGetFileSize(LinuxInitrdFile); + + // Read the kernel + if (!LinuxReadKernel(LinuxKernel)) + { + goto LinuxBootFailed; + } + + // Read the initrd (if necessary) + if (LinuxHasInitrd) + { + if (!LinuxReadInitrd(LinuxInitrdFile)) + { + goto LinuxBootFailed; + } + } + + // If the default root device is set to FLOPPY (0000h), change to /dev/fd0 (0200h) + if (LinuxBootSector->RootDevice == 0x0000) + { + LinuxBootSector->RootDevice = 0x0200; + } + + if (LinuxSetupSector->Version >= 0x0202) + { + LinuxSetupSector->CommandLinePointer = 0x99000; + } + else + { + LinuxBootSector->CommandLineMagic = LINUX_COMMAND_LINE_MAGIC; + LinuxBootSector->CommandLineOffset = 0x9000; + } + + if (NewStyleLinuxKernel) + { + LinuxSetupSector->TypeOfLoader = LINUX_LOADER_TYPE_FREELOADER; + } + else + { + LinuxSetupSector->LoadFlags = 0; + } + + RtlCopyMemory((PVOID)0x90000, LinuxBootSector, 512); + RtlCopyMemory((PVOID)0x90200, LinuxSetupSector, SetupSectorSize); + RtlCopyMemory((PVOID)0x99000, LinuxCommandLine, LinuxCommandLineSize); + + UiUnInitialize("Booting Linux..."); + + DiskStopFloppyMotor(); + + if (LinuxSetupSector->LoadFlags & LINUX_FLAG_LOAD_HIGH) + { + BootNewLinuxKernel(); + } + else + { + BootOldLinuxKernel(LinuxKernelSize); + } + + +LinuxBootFailed: + + if (LinuxKernel) + { + FsCloseFile(LinuxKernel); + } + if (LinuxInitrdFile) + { + FsCloseFile(LinuxInitrdFile); + } + + if (LinuxBootSector != NULL) + { + MmFreeMemory(LinuxBootSector); + } + if (LinuxSetupSector != NULL) + { + MmFreeMemory(LinuxSetupSector); + } + if (LinuxKernelLoadAddress != NULL) + { + MmFreeMemory(LinuxKernelLoadAddress); + } + if (LinuxInitrdLoadAddress != NULL) + { + MmFreeMemory(LinuxInitrdLoadAddress); + } + + LinuxBootSector = NULL; + LinuxSetupSector = NULL; + LinuxKernelLoadAddress = NULL; + LinuxInitrdLoadAddress = NULL; + SetupSectorSize = 0; + NewStyleLinuxKernel = FALSE; + LinuxKernelSize = 0; + LinuxHasInitrd = FALSE; + strcpy(LinuxCommandLine, ""); + LinuxCommandLineSize = 0; +} + +BOOLEAN LinuxParseIniSection(PCSTR SectionName) +{ + ULONG_PTR SectionId; + CHAR SettingName[260]; + + // Find all the message box settings and run them + UiShowMessageBoxesInSection(SectionName); + + // Try to open the operating system section in the .ini file + if (!IniOpenSection(SectionName, &SectionId)) + { + sprintf(SettingName, "Section [%s] not found in freeldr.ini.\n", SectionName); + UiMessageBox(SettingName); + return FALSE; + } + + if (!IniReadSettingByName(SectionId, "BootPath", LinuxBootPath, sizeof(LinuxBootPath))) + { + UiMessageBox("Boot path not specified for selected OS!"); + return FALSE; + } + + // Get the kernel name + if (!IniReadSettingByName(SectionId, "Kernel", LinuxKernelName, sizeof(LinuxKernelName))) + { + UiMessageBox("Linux kernel filename not specified for selected OS!"); + return FALSE; + } + + // Get the initrd name + if (IniReadSettingByName(SectionId, "Initrd", LinuxInitrdName, sizeof(LinuxInitrdName))) + { + LinuxHasInitrd = TRUE; + } + + // Get the command line + if (IniReadSettingByName(SectionId, "CommandLine", LinuxCommandLine, sizeof(LinuxCommandLine))) + { + RemoveQuotes(LinuxCommandLine); + LinuxCommandLineSize = strlen(LinuxCommandLine) + 1; + } + + return TRUE; +} + +BOOLEAN LinuxReadBootSector(PFILE LinuxKernelFile) +{ + // Allocate memory for boot sector + LinuxBootSector = MmAllocateMemoryWithType(512, LoaderSystemCode); + if (LinuxBootSector == NULL) + { + return FALSE; + } + + // Read linux boot sector + FsSetFilePointer(LinuxKernelFile, 0); + if (!FsReadFile(LinuxKernelFile, 512, NULL, LinuxBootSector)) + { + return FALSE; + } + + // Check for validity + if (LinuxBootSector->BootFlag != LINUX_BOOT_SECTOR_MAGIC) + { + UiMessageBox("Invalid boot sector magic (0xaa55)"); + return FALSE; + } + + DbgDumpBuffer(DPRINT_LINUX, LinuxBootSector, 512); + + TRACE("SetupSectors: %d\n", LinuxBootSector->SetupSectors); + TRACE("RootFlags: 0x%x\n", LinuxBootSector->RootFlags); + TRACE("SystemSize: 0x%x\n", LinuxBootSector->SystemSize); + TRACE("SwapDevice: 0x%x\n", LinuxBootSector->SwapDevice); + TRACE("RamSize: 0x%x\n", LinuxBootSector->RamSize); + TRACE("VideoMode: 0x%x\n", LinuxBootSector->VideoMode); + TRACE("RootDevice: 0x%x\n", LinuxBootSector->RootDevice); + TRACE("BootFlag: 0x%x\n", LinuxBootSector->BootFlag); + + return TRUE; +} + +BOOLEAN LinuxReadSetupSector(PFILE LinuxKernelFile) +{ + UCHAR TempLinuxSetupSector[512]; + + LinuxSetupSector = (PLINUX_SETUPSECTOR)TempLinuxSetupSector; + + // Read first linux setup sector + FsSetFilePointer(LinuxKernelFile, 512); + if (!FsReadFile(LinuxKernelFile, 512, NULL, TempLinuxSetupSector)) + { + return FALSE; + } + + // Check the kernel version + if (!LinuxCheckKernelVersion()) + { + return FALSE; + } + + if (NewStyleLinuxKernel) + { + SetupSectorSize = 512 * LinuxBootSector->SetupSectors; + } + else + { + SetupSectorSize = 4 * 512; // Always 4 setup sectors + } + + // Allocate memory for setup sectors + LinuxSetupSector = MmAllocateMemoryWithType(SetupSectorSize, LoaderSystemCode); + if (LinuxSetupSector == NULL) + { + return FALSE; + } + + // Copy over first setup sector + RtlCopyMemory(LinuxSetupSector, TempLinuxSetupSector, 512); + + // Read in the rest of the linux setup sectors + FsSetFilePointer(LinuxKernelFile, 1024); + if (!FsReadFile(LinuxKernelFile, SetupSectorSize - 512, NULL, (PVOID)((ULONG_PTR)LinuxSetupSector + 512))) + { + return FALSE; + } + + DbgDumpBuffer(DPRINT_LINUX, LinuxSetupSector, SetupSectorSize); + + TRACE("SetupHeaderSignature: 0x%x (HdrS)\n", LinuxSetupSector->SetupHeaderSignature); + TRACE("Version: 0x%x\n", LinuxSetupSector->Version); + TRACE("RealModeSwitch: 0x%x\n", LinuxSetupSector->RealModeSwitch); + TRACE("SetupSeg: 0x%x\n", LinuxSetupSector->SetupSeg); + TRACE("StartSystemSeg: 0x%x\n", LinuxSetupSector->StartSystemSeg); + TRACE("KernelVersion: 0x%x\n", LinuxSetupSector->KernelVersion); + TRACE("TypeOfLoader: 0x%x\n", LinuxSetupSector->TypeOfLoader); + TRACE("LoadFlags: 0x%x\n", LinuxSetupSector->LoadFlags); + TRACE("SetupMoveSize: 0x%x\n", LinuxSetupSector->SetupMoveSize); + TRACE("Code32Start: 0x%x\n", LinuxSetupSector->Code32Start); + TRACE("RamdiskAddress: 0x%x\n", LinuxSetupSector->RamdiskAddress); + TRACE("RamdiskSize: 0x%x\n", LinuxSetupSector->RamdiskSize); + TRACE("BootSectKludgeOffset: 0x%x\n", LinuxSetupSector->BootSectKludgeOffset); + TRACE("BootSectKludgeSegment: 0x%x\n", LinuxSetupSector->BootSectKludgeSegment); + TRACE("HeapEnd: 0x%x\n", LinuxSetupSector->HeapEnd); + + return TRUE; +} + +BOOLEAN LinuxReadKernel(PFILE LinuxKernelFile) +{ + ULONG BytesLoaded; + CHAR StatusText[260]; + PVOID LoadAddress; + + sprintf(StatusText, "Loading %s", LinuxKernelName); + UiDrawStatusText(StatusText); + + // Allocate memory for Linux kernel + LinuxKernelLoadAddress = MmAllocateMemoryAtAddress(LinuxKernelSize, (PVOID)LINUX_KERNEL_LOAD_ADDRESS, LoaderSystemCode); + if (LinuxKernelLoadAddress != (PVOID)LINUX_KERNEL_LOAD_ADDRESS) + { + return FALSE; + } + + LoadAddress = LinuxKernelLoadAddress; + + // Read linux kernel to 0x100000 (1mb) + FsSetFilePointer(LinuxKernelFile, 512 + SetupSectorSize); + for (BytesLoaded=0; BytesLoadedSetupHeaderSignature != LINUX_SETUP_HEADER_ID) + { + NewStyleLinuxKernel = FALSE; + } + // Check for version below 2.0 + else if (LinuxSetupSector->Version < 0x0200) + { + NewStyleLinuxKernel = FALSE; + } + // Check for version 2.0 + else if (LinuxSetupSector->Version == 0x0200) + { + NewStyleLinuxKernel = TRUE; + } + // Check for version 2.01+ + else if (LinuxSetupSector->Version >= 0x0201) + { + NewStyleLinuxKernel = TRUE; + LinuxSetupSector->HeapEnd = 0x9000; + LinuxSetupSector->LoadFlags |= LINUX_FLAG_CAN_USE_HEAP; + } + - if ((NewStyleLinuxKernel == FALSE) && (LinuxHasInitrd == TRUE)) ++ if ((NewStyleLinuxKernel == FALSE) && (LinuxHasInitrd)) + { + UiMessageBox("Error: Cannot load a ramdisk (initrd) with an old kernel image."); + return FALSE; + } + + return TRUE; +} + +BOOLEAN LinuxReadInitrd(PFILE LinuxInitrdFile) +{ + ULONG BytesLoaded; + CHAR StatusText[260]; + + sprintf(StatusText, "Loading %s", LinuxInitrdName); + UiDrawStatusText(StatusText); + + // Allocate memory for the ramdisk + //LinuxInitrdLoadAddress = MmAllocateMemory(LinuxInitrdSize); + // Try to align it at the next MB boundary after the kernel + //LinuxInitrdLoadAddress = MmAllocateMemoryAtAddress(LinuxInitrdSize, (PVOID)ROUND_UP((LINUX_KERNEL_LOAD_ADDRESS + LinuxKernelSize), 0x100000)); + if (LinuxSetupSector->Version <= 0x0202) + { + LinuxInitrdLoadAddress = MmAllocateHighestMemoryBelowAddress(LinuxInitrdSize, (PVOID)LINUX_MAX_INITRD_ADDRESS, LoaderSystemCode); + } + else + { + LinuxInitrdLoadAddress = MmAllocateHighestMemoryBelowAddress(LinuxInitrdSize, (PVOID)LinuxSetupSector->InitrdAddressMax, LoaderSystemCode); + } + if (LinuxInitrdLoadAddress == NULL) + { + return FALSE; + } + + // Set the information in the setup struct + LinuxSetupSector->RamdiskAddress = (ULONG)LinuxInitrdLoadAddress; + LinuxSetupSector->RamdiskSize = LinuxInitrdSize; + + TRACE("RamdiskAddress: 0x%x\n", LinuxSetupSector->RamdiskAddress); + TRACE("RamdiskSize: 0x%x\n", LinuxSetupSector->RamdiskSize); + + if (LinuxSetupSector->Version >= 0x0203) + { + TRACE("InitrdAddressMax: 0x%x\n", LinuxSetupSector->InitrdAddressMax); + } + + // Read in the ramdisk + for (BytesLoaded=0; BytesLoaded + * + * 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. + */ + +#include +#include +#include + +DBG_DEFAULT_CHANNEL(REGISTRY); + +static PCMHIVE CmHive; +static PCM_KEY_NODE RootKeyNode; +static FRLDRHKEY CurrentControlSetKey; + +BOOLEAN +RegImportBinaryHive( + _In_ PCHAR ChunkBase, + _In_ ULONG ChunkSize) +{ + NTSTATUS Status; + TRACE("RegImportBinaryHive(%p, 0x%lx)\n", ChunkBase, ChunkSize); + + /* Allocate and initialize the hive */ + CmHive = FrLdrTempAlloc(sizeof(CMHIVE), 'eviH'); + Status = HvInitialize(&CmHive->Hive, + HINIT_FLAT, + 0, + 0, + ChunkBase, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + 1, + NULL); + if (!NT_SUCCESS(Status)) + { + FrLdrTempFree(CmHive, 'eviH'); + ERR("Invalid hive Signature!\n"); + return FALSE; + } + + /* Save the root key node */ + RootKeyNode = HvGetCell(&CmHive->Hive, CmHive->Hive.BaseBlock->RootCell); + + TRACE("RegImportBinaryHive done\n"); + return TRUE; +} + +VOID +RegInitializeRegistry(VOID) +{ + /* Nothing to do */ +} + + +LONG +RegInitCurrentControlSet( + _In_ BOOLEAN LastKnownGood) +{ + WCHAR ControlSetKeyName[80]; + FRLDRHKEY SelectKey; + FRLDRHKEY SystemKey; + ULONG CurrentSet = 0; + ULONG DefaultSet = 0; + ULONG LastKnownGoodSet = 0; + ULONG DataSize; + LONG Error; + TRACE("RegInitCurrentControlSet\n"); + + Error = RegOpenKey(NULL, + L"\\Registry\\Machine\\SYSTEM\\Select", + &SelectKey); + if (Error != ERROR_SUCCESS) + { + ERR("RegOpenKey() failed (Error %u)\n", (int)Error); + return Error; + } + + DataSize = sizeof(ULONG); + Error = RegQueryValue(SelectKey, + L"Default", + NULL, + (PUCHAR)&DefaultSet, + &DataSize); + if (Error != ERROR_SUCCESS) + { + ERR("RegQueryValue('Default') failed (Error %u)\n", (int)Error); + return Error; + } + + DataSize = sizeof(ULONG); + Error = RegQueryValue(SelectKey, + L"LastKnownGood", + NULL, + (PUCHAR)&LastKnownGoodSet, + &DataSize); + if (Error != ERROR_SUCCESS) + { + ERR("RegQueryValue('Default') failed (Error %u)\n", (int)Error); + return Error; + } + - CurrentSet = (LastKnownGood == TRUE) ? LastKnownGoodSet : DefaultSet; ++ CurrentSet = (LastKnownGood) ? LastKnownGoodSet : DefaultSet; + wcscpy(ControlSetKeyName, L"ControlSet"); + switch(CurrentSet) + { + case 1: + wcscat(ControlSetKeyName, L"001"); + break; + case 2: + wcscat(ControlSetKeyName, L"002"); + break; + case 3: + wcscat(ControlSetKeyName, L"003"); + break; + case 4: + wcscat(ControlSetKeyName, L"004"); + break; + case 5: + wcscat(ControlSetKeyName, L"005"); + break; + } + + Error = RegOpenKey(NULL, + L"\\Registry\\Machine\\SYSTEM", + &SystemKey); + if (Error != ERROR_SUCCESS) + { + ERR("RegOpenKey(SystemKey) failed (Error %lu)\n", Error); + return Error; + } + + Error = RegOpenKey(SystemKey, + ControlSetKeyName, + &CurrentControlSetKey); + if (Error != ERROR_SUCCESS) + { + ERR("RegOpenKey(CurrentControlSetKey) failed (Error %lu)\n", Error); + return Error; + } + + TRACE("RegInitCurrentControlSet done\n"); + return ERROR_SUCCESS; +} + +static +BOOLEAN +GetNextPathElement( + _Out_ PUNICODE_STRING NextElement, + _Inout_ PUNICODE_STRING RemainingPath) +{ + /* Check if there are any characters left */ + if (RemainingPath->Length < sizeof(WCHAR)) + { + /* Nothing left, bail out early */ + return FALSE; + } + + /* The next path elements starts with the remaining path */ + NextElement->Buffer = RemainingPath->Buffer; + + /* Loop until the path element ends */ + while ((RemainingPath->Length >= sizeof(WCHAR)) && + (RemainingPath->Buffer[0] != '\\')) + { + /* Skip this character */ + RemainingPath->Buffer++; + RemainingPath->Length -= sizeof(WCHAR); + } + + NextElement->Length = (RemainingPath->Buffer - NextElement->Buffer) * sizeof(WCHAR); + NextElement->MaximumLength = NextElement->Length; + + /* Check if the path element ended with a path separator */ + if (RemainingPath->Length >= sizeof(WCHAR)) + { + /* Skip the path separator */ + ASSERT(RemainingPath->Buffer[0] == '\\'); + RemainingPath->Buffer++; + RemainingPath->Length -= sizeof(WCHAR); + } + + /* Return whether we got any characters */ + return TRUE; +} + +static +PCM_KEY_NODE +RegpFindSubkeyInIndex( + _In_ PHHIVE Hive, + _In_ PCM_KEY_INDEX IndexCell, + _In_ PUNICODE_STRING SubKeyName) +{ + PCM_KEY_NODE SubKeyNode; + ULONG i; + TRACE("RegpFindSubkeyInIndex('%wZ')\n", SubKeyName); + + /* Check the cell type */ + if ((IndexCell->Signature == CM_KEY_INDEX_ROOT) || + (IndexCell->Signature == CM_KEY_INDEX_LEAF)) + { + ASSERT(FALSE); + + /* Enumerate subindex cells */ + for (i = 0; i < IndexCell->Count; i++) + { + /* Get the subindex cell and call the function recursively */ + PCM_KEY_INDEX SubIndexCell = HvGetCell(Hive, IndexCell->List[i]); + + SubKeyNode = RegpFindSubkeyInIndex(Hive, SubIndexCell, SubKeyName); + if (SubKeyNode != NULL) + { + return SubKeyNode; + } + } + } + else if ((IndexCell->Signature == CM_KEY_FAST_LEAF) || + (IndexCell->Signature == CM_KEY_HASH_LEAF)) + { + /* Directly enumerate subkey nodes */ + PCM_KEY_FAST_INDEX HashCell = (PCM_KEY_FAST_INDEX)IndexCell; + for (i = 0; i < HashCell->Count; i++) + { + SubKeyNode = HvGetCell(Hive, HashCell->List[i].Cell); + ASSERT(SubKeyNode->Signature == CM_KEY_NODE_SIGNATURE); + + TRACE(" RegpFindSubkeyInIndex: checking '%.*s'\n", + SubKeyNode->NameLength, SubKeyNode->Name); + if (CmCompareKeyName(SubKeyNode, SubKeyName, TRUE)) + { + return SubKeyNode; + } + } + } + else + { + ASSERT(FALSE); + } + + return NULL; +} + +LONG +RegEnumKey( + _In_ FRLDRHKEY Key, + _In_ ULONG Index, + _Out_ PWCHAR Name, + _Inout_ ULONG* NameSize, + _Out_opt_ FRLDRHKEY *SubKey) +{ + PHHIVE Hive = &CmHive->Hive; + PCM_KEY_NODE KeyNode, SubKeyNode; + PCM_KEY_INDEX IndexCell; + PCM_KEY_FAST_INDEX HashCell; + TRACE("RegEnumKey(%p, %lu, %p, %p->%u)\n", + Key, Index, Name, NameSize, NameSize ? *NameSize : 0); + + /* Get the key node */ + KeyNode = (PCM_KEY_NODE)Key; + ASSERT(KeyNode->Signature == CM_KEY_NODE_SIGNATURE); + + /* Check if the index is valid */ + if ((KeyNode->SubKeyCounts[Stable] == 0) || + (Index >= KeyNode->SubKeyCounts[Stable])) + { + TRACE("RegEnumKey index out of bounds\n"); + return ERROR_NO_MORE_ITEMS; + } + + /* Get the index cell */ + IndexCell = HvGetCell(Hive, KeyNode->SubKeyLists[Stable]); + TRACE("IndexCell: %x, SubKeyCounts: %x\n", IndexCell, KeyNode->SubKeyCounts[Stable]); + + /* Check the cell type */ + if ((IndexCell->Signature == CM_KEY_FAST_LEAF) || + (IndexCell->Signature == CM_KEY_HASH_LEAF)) + { + /* Get the value cell */ + HashCell = (PCM_KEY_FAST_INDEX)IndexCell; + SubKeyNode = HvGetCell(Hive, HashCell->List[Index].Cell); + } + else + { + ASSERT(FALSE); + } + + *NameSize = CmCopyKeyName(SubKeyNode, Name, *NameSize); + + if (SubKey != NULL) + { + *SubKey = (FRLDRHKEY)SubKeyNode; + } + + TRACE("RegEnumKey done -> %u, '%.*s'\n", *NameSize, *NameSize, Name); + return STATUS_SUCCESS; +} + +LONG +RegOpenKey( + _In_ FRLDRHKEY ParentKey, + _In_z_ PCWSTR KeyName, + _Out_ PFRLDRHKEY Key) +{ + UNICODE_STRING RemainingPath, SubKeyName; + UNICODE_STRING CurrentControlSet = RTL_CONSTANT_STRING(L"CurrentControlSet"); + PHHIVE Hive = &CmHive->Hive; + PCM_KEY_NODE KeyNode; + PCM_KEY_INDEX IndexCell; + TRACE("RegOpenKey(%p, '%S', %p)\n", ParentKey, KeyName, Key); + + /* Initialize the remaining path name */ + RtlInitUnicodeString(&RemainingPath, KeyName); + + /* Get the parent key node */ + KeyNode = (PCM_KEY_NODE)ParentKey; + + /* Check if we have a parent key */ + if (KeyNode == NULL) + { + UNICODE_STRING SubKeyName1, SubKeyName2, SubKeyName3; + UNICODE_STRING RegistryPath = RTL_CONSTANT_STRING(L"Registry"); + UNICODE_STRING MachinePath = RTL_CONSTANT_STRING(L"MACHINE"); + UNICODE_STRING SystemPath = RTL_CONSTANT_STRING(L"SYSTEM"); + TRACE("RegOpenKey: absolute path\n"); + + if ((RemainingPath.Length < sizeof(WCHAR)) || + RemainingPath.Buffer[0] != '\\') + { + /* The key path is not absolute */ + ERR("RegOpenKey: invalid path '%S' (%wZ)\n", KeyName, &RemainingPath); + return ERROR_PATH_NOT_FOUND; + } + + /* Skip initial path separator */ + RemainingPath.Buffer++; + RemainingPath.Length -= sizeof(WCHAR); + + /* Get the first 3 path elements */ + GetNextPathElement(&SubKeyName1, &RemainingPath); + GetNextPathElement(&SubKeyName2, &RemainingPath); + GetNextPathElement(&SubKeyName3, &RemainingPath); + TRACE("RegOpenKey: %wZ / %wZ / %wZ\n", &SubKeyName1, &SubKeyName2, &SubKeyName3); + + /* Check if we have the correct path */ + if (!RtlEqualUnicodeString(&SubKeyName1, &RegistryPath, TRUE) || + !RtlEqualUnicodeString(&SubKeyName2, &MachinePath, TRUE) || + !RtlEqualUnicodeString(&SubKeyName3, &SystemPath, TRUE)) + { + /* The key path is not inside HKLM\Machine\System */ + ERR("RegOpenKey: invalid path '%S' (%wZ)\n", KeyName, &RemainingPath); + return ERROR_PATH_NOT_FOUND; + } + + /* Use the root key */ + KeyNode = RootKeyNode; + } + + ASSERT(KeyNode->Signature == CM_KEY_NODE_SIGNATURE); + + /* Check if this is the root key */ + if (KeyNode == RootKeyNode) + { + UNICODE_STRING TempPath = RemainingPath; + + /* Get the first path element */ + GetNextPathElement(&SubKeyName, &TempPath); + + /* Check if this is CurrentControlSet */ + if (RtlEqualUnicodeString(&SubKeyName, &CurrentControlSet, TRUE)) + { + /* Use the CurrentControlSetKey and update the remaining path */ + KeyNode = (PCM_KEY_NODE)CurrentControlSetKey; + RemainingPath = TempPath; + } + } + + TRACE("RegOpenKey: RemainingPath '%wZ'\n", &RemainingPath); + + /* Loop while there are path elements */ + while (GetNextPathElement(&SubKeyName, &RemainingPath)) + { + TRACE("RegOpenKey: next element '%wZ'\n", &SubKeyName); + + /* Check if there is any subkey */ + if (KeyNode->SubKeyCounts[Stable] == 0) + { + return ERROR_PATH_NOT_FOUND; + } + + /* Get the top level index cell */ + IndexCell = HvGetCell(Hive, KeyNode->SubKeyLists[Stable]); + + /* Get the next sub key */ + KeyNode = RegpFindSubkeyInIndex(Hive, IndexCell, &SubKeyName); + if (KeyNode == NULL) + { + + ERR("Did not find sub key '%wZ' (full %S)\n", &RemainingPath, KeyName); + return ERROR_PATH_NOT_FOUND; + } + } + + TRACE("RegOpenKey done\n"); + *Key = (FRLDRHKEY)KeyNode; + return ERROR_SUCCESS; +} + +static +VOID +RepGetValueData( + _In_ PHHIVE Hive, + _In_ PCM_KEY_VALUE ValueCell, + _Out_opt_ ULONG* Type, + _Out_opt_ PUCHAR Data, + _Inout_opt_ ULONG* DataSize) +{ + ULONG DataLength; + + /* Does the caller want the type? */ + if (Type != NULL) + { + *Type = ValueCell->Type; + } + + /* Does the caller provide DataSize? */ + if (DataSize != NULL) + { + /* Get the data length */ + DataLength = ValueCell->DataLength & REG_DATA_SIZE_MASK; + + /* Does the caller want the data? */ + if ((Data != NULL) && (*DataSize != 0)) + { + /* Check where the data is stored */ + if ((DataLength <= sizeof(HCELL_INDEX)) && + (ValueCell->DataLength & REG_DATA_IN_OFFSET)) + { + /* The data member contains the data */ + RtlCopyMemory(Data, + &ValueCell->Data, + min(*DataSize, DataLength)); + } + else + { + /* The data member contains the data cell index */ + PVOID DataCell = HvGetCell(Hive, ValueCell->Data); + RtlCopyMemory(Data, + DataCell, + min(*DataSize, ValueCell->DataLength)); + } + + } + + /* Return the actual data length */ + *DataSize = DataLength; + } +} + +LONG +RegQueryValue( + _In_ FRLDRHKEY Key, + _In_z_ PCWSTR ValueName, + _Out_opt_ ULONG* Type, + _Out_opt_ PUCHAR Data, + _Inout_opt_ ULONG* DataSize) +{ + PHHIVE Hive = &CmHive->Hive; + PCM_KEY_NODE KeyNode; + PCM_KEY_VALUE ValueCell; + PVALUE_LIST_CELL ValueListCell; + UNICODE_STRING ValueNameString; + ULONG i; + TRACE("RegQueryValue(%p, '%S', %p, %p, %p)\n", + Key, ValueName, Type, Data, DataSize); + + /* Get the key node */ + KeyNode = (PCM_KEY_NODE)Key; + ASSERT(KeyNode->Signature == CM_KEY_NODE_SIGNATURE); + + /* Check if there are any values */ + if (KeyNode->ValueList.Count == 0) + { + TRACE("RegQueryValue no values in key (%.*s)\n", + KeyNode->NameLength, KeyNode->Name); + return ERROR_INVALID_PARAMETER; + } + + /* Initialize value name string */ + RtlInitUnicodeString(&ValueNameString, ValueName); + + ValueListCell = (PVALUE_LIST_CELL)HvGetCell(Hive, KeyNode->ValueList.List); + TRACE("ValueListCell: %x\n", ValueListCell); + + /* Loop all values */ + for (i = 0; i < KeyNode->ValueList.Count; i++) + { + /* Get the subkey node and check the name */ + ValueCell = HvGetCell(Hive, ValueListCell->ValueOffset[i]); + + /* Compare the value name */ + TRACE("checking %.*s\n", ValueCell->NameLength, ValueCell->Name); + if (CmCompareKeyValueName(ValueCell, &ValueNameString, TRUE)) + { + RepGetValueData(Hive, ValueCell, Type, Data, DataSize); + TRACE("RegQueryValue success\n"); + return STATUS_SUCCESS; + } + } + + TRACE("RegQueryValue value not found\n"); + return ERROR_INVALID_PARAMETER; +} + + +LONG +RegEnumValue( + _In_ FRLDRHKEY Key, + _In_ ULONG Index, + _Out_ PWCHAR ValueName, + _Inout_ ULONG* NameSize, + _Out_ ULONG* Type, + _Out_ PUCHAR Data, + _Inout_ ULONG* DataSize) +{ + PHHIVE Hive = &CmHive->Hive; + PCM_KEY_NODE KeyNode; + PCM_KEY_VALUE ValueCell; + PVALUE_LIST_CELL ValueListCell; + TRACE("RegEnumValue(%p, %lu, %S, %p, %p, %p, %p (%lu))\n", + Key, Index, ValueName, NameSize, Type, Data, DataSize, *DataSize); + + /* Get the key node */ + KeyNode = (PCM_KEY_NODE)Key; + ASSERT(KeyNode->Signature == CM_KEY_NODE_SIGNATURE); + + /* Check if the index is valid */ + if ((KeyNode->ValueList.Count == 0) || + (Index >= KeyNode->ValueList.Count)) + { + ERR("RegEnumValue: index invalid\n"); + return ERROR_NO_MORE_ITEMS; + } + + ValueListCell = (PVALUE_LIST_CELL)HvGetCell(Hive, KeyNode->ValueList.List); + TRACE("ValueListCell: %x\n", ValueListCell); + + /* Get the value cell */ + ValueCell = HvGetCell(Hive, ValueListCell->ValueOffset[Index]); + ASSERT(ValueCell != NULL); + + if (NameSize != NULL) + { + *NameSize = CmCopyKeyValueName(ValueCell, ValueName, *NameSize); + } + + RepGetValueData(Hive, ValueCell, Type, Data, DataSize); + + if (DataSize != NULL) + { + if ((Data != NULL) && (*DataSize != 0)) + { + RtlCopyMemory(Data, + &ValueCell->Data, + min(*DataSize, ValueCell->DataLength)); + } + + *DataSize = ValueCell->DataLength; + } + + TRACE("RegEnumValue done\n"); + return STATUS_SUCCESS; +} + +/* EOF */ diff --cc dll/win32/npptools/CMakeLists.txt index 00000000000,f16d6c71961..f16d6c71961 mode 000000,100644..100644 --- a/dll/win32/npptools/CMakeLists.txt +++ b/dll/win32/npptools/CMakeLists.txt diff --cc dll/win32/npptools/main.c index 00000000000,a4a3ee30656..a4a3ee30656 mode 000000,100644..100644 --- a/dll/win32/npptools/main.c +++ b/dll/win32/npptools/main.c diff --cc dll/win32/npptools/npptools.spec index 00000000000,9f69e21dd7b..9f69e21dd7b mode 000000,100644..100644 --- a/dll/win32/npptools/npptools.spec +++ b/dll/win32/npptools/npptools.spec diff --cc win32ss/drivers/displays/framebuf/ddenable.c index 964dd24eaec,00000000000..e2585fc2e2b mode 100644,000000..100644 --- a/win32ss/drivers/displays/framebuf/ddenable.c +++ b/win32ss/drivers/displays/framebuf/ddenable.c @@@ -1,259 -1,0 +1,259 @@@ +/* + * ReactOS Generic Framebuffer display driver directdraw interface + * + * Copyright (C) 2006 Magnus Olsen + * + * 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. + */ + +#include "framebuf.h" + +VOID APIENTRY +DrvDisableDirectDraw( IN DHPDEV dhpdev) +{ + PPDEV ppdev = (PPDEV)dhpdev; + ppdev->bDDInitialized = FALSE; + /* Add Clean up code here if we need it + when we shout down directx interface */ +} + +BOOL APIENTRY +DrvEnableDirectDraw( + IN DHPDEV dhpdev, + OUT DD_CALLBACKS *pCallBacks, + OUT DD_SURFACECALLBACKS *pSurfaceCallBacks, + OUT DD_PALETTECALLBACKS *pPaletteCallBacks) +{ + PPDEV ppdev = (PPDEV)dhpdev; + - if (ppdev->bDDInitialized == TRUE) ++ if (ppdev->bDDInitialized) + { + return TRUE; + } + + /* Setup pixel format */ + ppdev->ddpfDisplay.dwSize = sizeof( DDPIXELFORMAT ); + ppdev->ddpfDisplay.dwFourCC = 0; + + ppdev->ddpfDisplay.dwRBitMask = ppdev->RedMask; + ppdev->ddpfDisplay.dwGBitMask = ppdev->GreenMask; + ppdev->ddpfDisplay.dwBBitMask = ppdev->BlueMask; + + ppdev->ddpfDisplay.dwRGBBitCount=ppdev->BitsPerPixel; + ppdev->ddpfDisplay.dwRGBAlphaBitMask = 0; + ppdev->ddpfDisplay.dwFlags = DDPF_RGB; + + ppdev->pvmList = NULL; + + switch(ppdev->iDitherFormat) + { + case BMF_8BPP: + ppdev->ddpfDisplay.dwFlags |= DDPF_PALETTEINDEXED8; + break; + + case BMF_16BPP: + switch(ppdev->RedMask) + { + case 0x7C00: + ppdev->ddpfDisplay.dwRGBAlphaBitMask = 0x8000; + break; + + default: + break; + } + break; + + case BMF_24BPP: + break; + + case BMF_32BPP: + ppdev->ddpfDisplay.dwRGBAlphaBitMask = 0xff000000; + break; + + default: + /* FIXME unknown pixel bits */ + ppdev->ddpfDisplay.dwRGBBitCount=0; + break; + } + + if (pCallBacks !=NULL) + { + memset(pCallBacks,0,sizeof(DD_CALLBACKS)); + + /* FILL pCallBacks with hal stuff */ + pCallBacks->dwSize = sizeof(DDHAL_DDCALLBACKS); + pCallBacks->CanCreateSurface = (PDD_CANCREATESURFACE)DdCanCreateSurface; + pCallBacks->CreateSurface = (PDD_CREATESURFACE)DdCreateSurface; + + /* Fill in the HAL Callback flags */ + pCallBacks->dwFlags = DDHAL_CB32_CANCREATESURFACE | DDHAL_CB32_CREATESURFACE; + } + + if (pSurfaceCallBacks !=NULL) + { + memset(pSurfaceCallBacks,0,sizeof(DD_SURFACECALLBACKS)); + + /* FILL pSurfaceCallBacks with hal stuff */ + // pSurfaceCallBacks.dwSize = sizeof(DDHAL_DDSURFACECALLBACKS); + // pSurfaceCallBacks.DestroySurface = DdDestroySurface; + // pSurfaceCallBacks.Lock = DdLock; + // pSurfaceCallBacks.Blt = DdBlt; + + // pSurfaceCallBacks->dwFlags = DDHAL_SURFCB32_DESTROYSURFACE | DDHAL_SURFCB32_LOCK | DDHAL_SURFCB32_BLT ; + } + + if (pPaletteCallBacks !=NULL) + { + memset(pPaletteCallBacks,0,sizeof(DD_PALETTECALLBACKS)); + /* FILL pPaletteCallBacks with hal stuff */ + /* We will not support this callback in the framebuf.dll */ + } + + + /* Fixme fill the ppdev->dxHalInfo with the info we need */ + ppdev->bDDInitialized = TRUE; + return ppdev->bDDInitialized; +} + +BOOL APIENTRY +DrvGetDirectDrawInfo( + IN DHPDEV dhpdev, + OUT DD_HALINFO *pHalInfo, + OUT DWORD *pdwNumHeaps, + OUT VIDEOMEMORY *pvmList, + OUT DWORD *pdwNumFourCCCodes, + OUT DWORD *pdwFourCC) +{ + PPDEV ppdev = (PPDEV)dhpdev; + LONG i; + DWORD heap = 1; /* we always alloc one heap */ + BOOL bDDrawHeap = FALSE; + + if (ppdev == NULL) + return FALSE; + + /* check so pHalInfo, pdwNumHeaps, pdwNumFourCCCodes is not NULL + pdwFourCC and pvmList can be null + */ + + if (pHalInfo == NULL) + return FALSE; + + if (pdwNumHeaps == NULL) + return FALSE; + + if (pdwNumFourCCCodes == NULL) + return FALSE; + + /* Setup heap */ + if ( (ppdev->ScreenWidth < ppdev->MemWidth) || (ppdev->ScreenHeight < ppdev->MemHeight)) + { + bDDrawHeap = TRUE; + heap++; + } + + ppdev->dwHeap = heap; + *pdwNumHeaps = heap; + + /* We do not support other fourcc */ + *pdwNumFourCCCodes = 0; + + + /* + check see if pvmList and pdwFourCC are frist call + or frist. Secon call we fill in pHalInfo info + */ + + if(!(pvmList && pdwFourCC)) + { + + RtlZeroMemory(pHalInfo, sizeof(DD_HALINFO)); + pHalInfo->dwSize = sizeof(DD_HALINFO); + + pHalInfo->ddCaps.dwCaps = DDCAPS_BLT | DDCAPS_BLTQUEUE | DDCAPS_BLTCOLORFILL | DDCAPS_READSCANLINE | + DDCAPS_BLTSTRETCH | DDCAPS_COLORKEY | DDCAPS_CANBLTSYSMEM; + + pHalInfo->ddCaps.dwFXCaps = DDFXCAPS_BLTSTRETCHY | DDFXCAPS_BLTSTRETCHX | + DDFXCAPS_BLTSTRETCHYN | DDFXCAPS_BLTSTRETCHXN | + DDFXCAPS_BLTSHRINKY | DDFXCAPS_BLTSHRINKX | + DDFXCAPS_BLTSHRINKYN | DDFXCAPS_BLTSHRINKXN | + DDFXCAPS_BLTMIRRORUPDOWN | DDFXCAPS_BLTMIRRORLEFTRIGHT; + + pHalInfo->ddCaps.dwCaps2 = DDCAPS2_NONLOCALVIDMEM | DDCAPS2_NONLOCALVIDMEMCAPS; + + pHalInfo->ddCaps.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP; + + pHalInfo->ddCaps.dwCKeyCaps = DDCKEYCAPS_SRCBLT | DDCKEYCAPS_SRCBLTCLRSPACE; + + pHalInfo->ddCaps.dwSVBCaps = DDCAPS_BLT; + pHalInfo->ddCaps.ddsCaps.dwCaps |= DDSCAPS_LOCALVIDMEM | DDSCAPS_NONLOCALVIDMEM; + + /* Calc how much memmory is left on the video cards memmory */ + pHalInfo->ddCaps.dwVidMemTotal = (ppdev->MemHeight - ppdev->ScreenHeight) * ppdev->ScreenDelta; + + /* fill in some basic info that we need */ + pHalInfo->vmiData.pvPrimary = ppdev->ScreenPtr; + pHalInfo->vmiData.dwDisplayWidth = ppdev->ScreenWidth; + pHalInfo->vmiData.dwDisplayHeight = ppdev->ScreenHeight; + pHalInfo->vmiData.lDisplayPitch = ppdev->ScreenDelta; + pHalInfo->vmiData.ddpfDisplay.dwSize = sizeof(DDPIXELFORMAT); + pHalInfo->vmiData.ddpfDisplay.dwFlags = DDPF_RGB; + pHalInfo->vmiData.ddpfDisplay.dwRGBBitCount = ppdev->BitsPerPixel; + pHalInfo->vmiData.ddpfDisplay.dwRBitMask = ppdev->RedMask; + pHalInfo->vmiData.ddpfDisplay.dwGBitMask = ppdev->GreenMask; + pHalInfo->vmiData.ddpfDisplay.dwBBitMask = ppdev->BlueMask; + pHalInfo->vmiData.dwOffscreenAlign = 4; + + if ( ppdev->BitsPerPixel == 8 ) + { + pHalInfo->vmiData.ddpfDisplay.dwFlags |= DDPF_PALETTEINDEXED8; + } + + /* FIXME + Config the rops we do not doing that yet + for we need write the rops table + */ + for(i=0;iddCaps.dwSVBRops[i] = rops[i]; + // pHALInfo->ddCaps.dwRops[i] = rops[i]; + } + } + + /* Now build pvmList info */ + if(pvmList) + { + ppdev->pvmList = pvmList; + - if ( bDDrawHeap == TRUE) ++ if (bDDrawHeap) + { + pvmList->dwFlags = VIDMEM_ISLINEAR ; + pvmList->fpStart = ppdev->ScreenHeight * ppdev->ScreenDelta; + pvmList->fpEnd = ppdev->MemHeight * ppdev->ScreenDelta - 1; + pvmList->ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; + pvmList++; + } + + pvmList->fpStart = 0; + pvmList->fpEnd = (ppdev->MemHeight * ppdev->ScreenDelta) - 1; + pvmList->dwFlags = VIDMEM_ISNONLOCAL | VIDMEM_ISLINEAR | VIDMEM_ISWC; + pvmList->ddsCaps.dwCaps = DDSCAPS_FRONTBUFFER | DDSCAPS_BACKBUFFER ; + pvmList->ddsCapsAlt.dwCaps = DDSCAPS_FRONTBUFFER | DDSCAPS_BACKBUFFER; + + pvmList = ppdev->pvmList; + } + + return TRUE; +} diff --cc win32ss/drivers/displays/framebuf/pointer.c index b1bfce582e9,00000000000..e3a6ed68881 mode 100644,000000..100644 --- a/win32ss/drivers/displays/framebuf/pointer.c +++ b/win32ss/drivers/displays/framebuf/pointer.c @@@ -1,370 -1,0 +1,370 @@@ +/* + * ReactOS Generic Framebuffer display driver + * + * Copyright (C) 2004 Filip Navara + * + * 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. + */ + +#include "framebuf.h" + +#ifndef EXPERIMENTAL_MOUSE_CURSOR_SUPPORT + +/* + * DrvSetPointerShape + * + * Sets the new pointer shape. + * + * Status + * @unimplemented + */ + +ULONG APIENTRY +DrvSetPointerShape( + IN SURFOBJ *pso, + IN SURFOBJ *psoMask, + IN SURFOBJ *psoColor, + IN XLATEOBJ *pxlo, + IN LONG xHot, + IN LONG yHot, + IN LONG x, + IN LONG y, + IN RECTL *prcl, + IN FLONG fl) +{ +/* return SPS_DECLINE;*/ + return EngSetPointerShape(pso, psoMask, psoColor, pxlo, xHot, yHot, x, y, prcl, fl); +} + +/* + * DrvMovePointer + * + * Moves the pointer to a new position and ensures that GDI does not interfere + * with the display of the pointer. + * + * Status + * @unimplemented + */ + +VOID APIENTRY +DrvMovePointer( + IN SURFOBJ *pso, + IN LONG x, + IN LONG y, + IN RECTL *prcl) +{ + EngMovePointer(pso, x, y, prcl); +} + +#else + +VOID FASTCALL +IntHideMousePointer(PPDEV ppdev, SURFOBJ *DestSurface) +{ + if (ppdev->PointerAttributes.Enable == FALSE) + { + return; + } + + ppdev->PointerAttributes.Enable = FALSE; + if (ppdev->PointerSaveSurface != NULL) + { + RECTL DestRect; + POINTL SrcPoint; + SURFOBJ *SaveSurface; + SURFOBJ *MaskSurface; + + DestRect.left = max(ppdev->PointerAttributes.Column, 0); + DestRect.top = max(ppdev->PointerAttributes.Row, 0); + DestRect.right = min( + ppdev->PointerAttributes.Column + ppdev->PointerAttributes.Width, + ppdev->ScreenWidth - 1); + DestRect.bottom = min( + ppdev->PointerAttributes.Row + ppdev->PointerAttributes.Height, + ppdev->ScreenHeight - 1); + + SrcPoint.x = max(-ppdev->PointerAttributes.Column, 0); + SrcPoint.y = max(-ppdev->PointerAttributes.Row, 0); + + SaveSurface = EngLockSurface(ppdev->PointerSaveSurface); + MaskSurface = EngLockSurface(ppdev->PointerMaskSurface); + EngBitBlt(DestSurface, SaveSurface, MaskSurface, NULL, NULL, + &DestRect, &SrcPoint, &SrcPoint, NULL, NULL, SRCCOPY); + EngUnlockSurface(MaskSurface); + EngUnlockSurface(SaveSurface); + } +} + +VOID FASTCALL +IntShowMousePointer(PPDEV ppdev, SURFOBJ *DestSurface) +{ - if (ppdev->PointerAttributes.Enable == TRUE) ++ if (ppdev->PointerAttributes.Enable) + { + return; + } + + ppdev->PointerAttributes.Enable = TRUE; + + /* + * Copy the pixels under the cursor to temporary surface. + */ + + if (ppdev->PointerSaveSurface != NULL) + { + RECTL DestRect; + POINTL SrcPoint; + SURFOBJ *SaveSurface; + + SrcPoint.x = max(ppdev->PointerAttributes.Column, 0); + SrcPoint.y = max(ppdev->PointerAttributes.Row, 0); + + DestRect.left = SrcPoint.x - ppdev->PointerAttributes.Column; + DestRect.top = SrcPoint.y - ppdev->PointerAttributes.Row; + DestRect.right = min( + ppdev->PointerAttributes.Width, + ppdev->ScreenWidth - ppdev->PointerAttributes.Column - 1); + DestRect.bottom = min( + ppdev->PointerAttributes.Height, + ppdev->ScreenHeight - ppdev->PointerAttributes.Row - 1); + + SaveSurface = EngLockSurface(ppdev->PointerSaveSurface); + EngBitBlt(SaveSurface, DestSurface, NULL, NULL, NULL, + &DestRect, &SrcPoint, NULL, NULL, NULL, SRCCOPY); + EngUnlockSurface(SaveSurface); + } + + /* + * Blit the cursor on the screen. + */ + + { + RECTL DestRect; + POINTL SrcPoint; + SURFOBJ *ColorSurf; + SURFOBJ *MaskSurf; + + DestRect.left = max(ppdev->PointerAttributes.Column, 0); + DestRect.top = max(ppdev->PointerAttributes.Row, 0); + DestRect.right = min( + ppdev->PointerAttributes.Column + ppdev->PointerAttributes.Width, + ppdev->ScreenWidth - 1); + DestRect.bottom = min( + ppdev->PointerAttributes.Row + ppdev->PointerAttributes.Height, + ppdev->ScreenHeight - 1); + + SrcPoint.x = max(-ppdev->PointerAttributes.Column, 0); + SrcPoint.y = max(-ppdev->PointerAttributes.Row, 0); + + MaskSurf = EngLockSurface(ppdev->PointerMaskSurface); + if (ppdev->PointerColorSurface != NULL) + { + ColorSurf = EngLockSurface(ppdev->PointerColorSurface); + EngBitBlt(DestSurface, ColorSurf, MaskSurf, NULL, ppdev->PointerXlateObject, + &DestRect, &SrcPoint, &SrcPoint, NULL, NULL, 0xAACC); + EngUnlockSurface(ColorSurf); + } + else + { + /* FIXME */ + EngBitBlt(DestSurface, MaskSurf, NULL, NULL, ppdev->PointerXlateObject, + &DestRect, &SrcPoint, NULL, NULL, NULL, SRCAND); + SrcPoint.y += ppdev->PointerAttributes.Height; + EngBitBlt(DestSurface, MaskSurf, NULL, NULL, ppdev->PointerXlateObject, + &DestRect, &SrcPoint, NULL, NULL, NULL, SRCINVERT); + } + EngUnlockSurface(MaskSurf); + } +} + +/* + * DrvSetPointerShape + * + * Sets the new pointer shape. + * + * Status + * @implemented + */ + +ULONG APIENTRY +DrvSetPointerShape( + IN SURFOBJ *pso, + IN SURFOBJ *psoMask, + IN SURFOBJ *psoColor, + IN XLATEOBJ *pxlo, + IN LONG xHot, + IN LONG yHot, + IN LONG x, + IN LONG y, + IN RECTL *prcl, + IN FLONG fl) +{ + PPDEV ppdev = (PPDEV)pso->dhpdev; + SURFOBJ *TempSurfObj; + + IntHideMousePointer(ppdev, pso); + + if (ppdev->PointerColorSurface != NULL) + { + /* FIXME: Is this really needed? */ + TempSurfObj = EngLockSurface(ppdev->PointerColorSurface); + EngFreeMem(TempSurfObj->pvBits); + TempSurfObj->pvBits = 0; + EngUnlockSurface(TempSurfObj); + + EngDeleteSurface(ppdev->PointerColorSurface); + ppdev->PointerMaskSurface = NULL; + } + + if (ppdev->PointerMaskSurface != NULL) + { + /* FIXME: Is this really needed? */ + TempSurfObj = EngLockSurface(ppdev->PointerMaskSurface); + EngFreeMem(TempSurfObj->pvBits); + TempSurfObj->pvBits = 0; + EngUnlockSurface(TempSurfObj); + + EngDeleteSurface(ppdev->PointerMaskSurface); + ppdev->PointerMaskSurface = NULL; + } + + if (ppdev->PointerSaveSurface != NULL) + { + EngDeleteSurface(ppdev->PointerSaveSurface); + ppdev->PointerSaveSurface = NULL; + } + + /* + * See if we are being asked to hide the pointer. + */ + + if (psoMask == NULL) + { + return SPS_ACCEPT_EXCLUDE; + } + + ppdev->PointerHotSpot.x = xHot; + ppdev->PointerHotSpot.y = yHot; + + ppdev->PointerXlateObject = pxlo; + ppdev->PointerAttributes.Column = x - xHot; + ppdev->PointerAttributes.Row = y - yHot; + ppdev->PointerAttributes.Width = psoMask->lDelta << 3; + ppdev->PointerAttributes.Height = (psoMask->cjBits / psoMask->lDelta) >> 1; + + if (psoColor != NULL) + { + SIZEL Size; + PBYTE Bits; + + Size.cx = ppdev->PointerAttributes.Width; + Size.cy = ppdev->PointerAttributes.Height; + Bits = EngAllocMem(0, psoColor->cjBits, ALLOC_TAG); + memcpy(Bits, psoColor->pvBits, psoColor->cjBits); + + ppdev->PointerColorSurface = (HSURF)EngCreateBitmap(Size, + psoColor->lDelta, psoColor->iBitmapFormat, 0, Bits); + } + else + { + ppdev->PointerColorSurface = NULL; + } + + if (psoMask != NULL) + { + SIZEL Size; + PBYTE Bits; + + Size.cx = ppdev->PointerAttributes.Width; + Size.cy = ppdev->PointerAttributes.Height << 1; + Bits = EngAllocMem(0, psoMask->cjBits, ALLOC_TAG); + memcpy(Bits, psoMask->pvBits, psoMask->cjBits); + + ppdev->PointerMaskSurface = (HSURF)EngCreateBitmap(Size, + psoMask->lDelta, psoMask->iBitmapFormat, 0, Bits); + } + else + { + ppdev->PointerMaskSurface = NULL; + } + + /* + * Create surface for saving the pixels under the cursor. + */ + + { + SIZEL Size; + LONG lDelta; + + Size.cx = ppdev->PointerAttributes.Width; + Size.cy = ppdev->PointerAttributes.Height; + + switch (pso->iBitmapFormat) + { + case BMF_8BPP: lDelta = Size.cx; break; + case BMF_16BPP: lDelta = Size.cx << 1; break; + case BMF_24BPP: lDelta = Size.cx * 3; break; + case BMF_32BPP: lDelta = Size.cx << 2; break; + } + + ppdev->PointerSaveSurface = (HSURF)EngCreateBitmap( + Size, lDelta, pso->iBitmapFormat, BMF_NOZEROINIT, NULL); + } + + IntShowMousePointer(ppdev, pso); + + return SPS_ACCEPT_EXCLUDE; +} + +/* + * DrvMovePointer + * + * Moves the pointer to a new position and ensures that GDI does not interfere + * with the display of the pointer. + * + * Status + * @implemented + */ + +VOID APIENTRY +DrvMovePointer( + IN SURFOBJ *pso, + IN LONG x, + IN LONG y, + IN RECTL *prcl) +{ + PPDEV ppdev = (PPDEV)pso->dhpdev; + BOOL WasVisible; + + WasVisible = ppdev->PointerAttributes.Enable; + if (WasVisible) + { + IntHideMousePointer(ppdev, pso); + } + + if (x == -1) + { + return; + } + + ppdev->PointerAttributes.Column = x - ppdev->PointerHotSpot.x; + ppdev->PointerAttributes.Row = y - ppdev->PointerHotSpot.y; + + if (WasVisible) + { + IntShowMousePointer(ppdev, pso); + } +} + +#endif diff --cc win32ss/drivers/displays/framebufacc/ddenable.c index 5b93b918902,00000000000..9b352a2afe3 mode 100644,000000..100644 --- a/win32ss/drivers/displays/framebufacc/ddenable.c +++ b/win32ss/drivers/displays/framebufacc/ddenable.c @@@ -1,260 -1,0 +1,260 @@@ +/* + * ReactOS Generic Framebuffer display driver directdraw interface + * + * Copyright (C) 2006 Magnus Olsen + * + * 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. + */ + +#include "framebuf.h" + +VOID APIENTRY +DrvDisableDirectDraw( IN DHPDEV dhpdev) +{ + PPDEV ppdev = (PPDEV)dhpdev; + ppdev->bDDInitialized = FALSE; + /* Add Clean up code here if we need it + when we shout down directx interface */ +} + +BOOL APIENTRY +DrvEnableDirectDraw( + IN DHPDEV dhpdev, + OUT DD_CALLBACKS *pCallBacks, + OUT DD_SURFACECALLBACKS *pSurfaceCallBacks, + OUT DD_PALETTECALLBACKS *pPaletteCallBacks) +{ + PPDEV ppdev = (PPDEV)dhpdev; + - if (ppdev->bDDInitialized == TRUE) ++ if (ppdev->bDDInitialized) + { + return TRUE; + } + + /* Setup pixel format */ + ppdev->ddpfDisplay.dwSize = sizeof( DDPIXELFORMAT ); + ppdev->ddpfDisplay.dwFourCC = 0; + + ppdev->ddpfDisplay.dwRBitMask = ppdev->RedMask; + ppdev->ddpfDisplay.dwGBitMask = ppdev->GreenMask; + ppdev->ddpfDisplay.dwBBitMask = ppdev->BlueMask; + + ppdev->ddpfDisplay.dwRGBBitCount=ppdev->BitsPerPixel; + ppdev->ddpfDisplay.dwRGBAlphaBitMask = 0; + ppdev->ddpfDisplay.dwFlags = DDPF_RGB; + + ppdev->pvmList = NULL; + + switch(ppdev->iDitherFormat) + { + case BMF_8BPP: + ppdev->ddpfDisplay.dwFlags |= DDPF_PALETTEINDEXED8; + break; + + case BMF_16BPP: + switch(ppdev->RedMask) + { + case 0x7C00: + ppdev->ddpfDisplay.dwRGBAlphaBitMask = 0x8000; + break; + + default: + break; + } + break; + + case BMF_24BPP: + break; + + case BMF_32BPP: + ppdev->ddpfDisplay.dwRGBAlphaBitMask = 0xff000000; + break; + + default: + /* FIXME unknown pixel bits */ + ppdev->ddpfDisplay.dwRGBBitCount=0; + break; + } + + if (pCallBacks !=NULL) + { + memset(pCallBacks,0,sizeof(DD_CALLBACKS)); + + /* FILL pCallBacks with hal stuff */ + pCallBacks->dwSize = sizeof(DDHAL_DDCALLBACKS); + pCallBacks->CanCreateSurface = (PDD_CANCREATESURFACE)DdCanCreateSurface; + pCallBacks->CreateSurface = (PDD_CREATESURFACE)DdCreateSurface; + + /* Fill in the HAL Callback flags */ + pCallBacks->dwFlags = DDHAL_CB32_CANCREATESURFACE | DDHAL_CB32_CREATESURFACE; + } + + if (pSurfaceCallBacks !=NULL) + { + memset(pSurfaceCallBacks,0,sizeof(DD_SURFACECALLBACKS)); + + /* FILL pSurfaceCallBacks with hal stuff */ + // pSurfaceCallBacks.dwSize = sizeof(DDHAL_DDSURFACECALLBACKS); + // pSurfaceCallBacks.DestroySurface = DdDestroySurface; + // pSurfaceCallBacks.Lock = DdLock; + // pSurfaceCallBacks.Blt = DdBlt; + + // pSurfaceCallBacks->dwFlags = DDHAL_SURFCB32_DESTROYSURFACE | DDHAL_SURFCB32_LOCK | DDHAL_SURFCB32_BLT ; + } + + if (pPaletteCallBacks !=NULL) + { + memset(pPaletteCallBacks,0,sizeof(DD_PALETTECALLBACKS)); + /* FILL pPaletteCallBacks with hal stuff */ + /* We will not support this callback in the framebuf.dll */ + } + + + /* Fixme fill the ppdev->dxHalInfo with the info we need */ + ppdev->bDDInitialized = TRUE; + return ppdev->bDDInitialized; +} + +BOOL APIENTRY +DrvGetDirectDrawInfo( + IN DHPDEV dhpdev, + OUT DD_HALINFO *pHalInfo, + OUT DWORD *pdwNumHeaps, + OUT VIDEOMEMORY *pvmList, + OUT DWORD *pdwNumFourCCCodes, + OUT DWORD *pdwFourCC) +{ + PPDEV ppdev = (PPDEV)dhpdev; + LONG i; + DWORD heap = 1; /* we always alloc one heap */ + BOOL bDDrawHeap = FALSE; + + if (ppdev == NULL) + return FALSE; + + /* check so pHalInfo, pdwNumHeaps, pdwNumFourCCCodes is not NULL + pdwFourCC and pvmList can be null + */ + + if (pHalInfo == NULL) + return FALSE; + + if (pdwNumHeaps == NULL) + return FALSE; + + if (pdwNumFourCCCodes == NULL) + return FALSE; + + /* Setup heap */ + if ( (ppdev->ScreenWidth < ppdev->MemWidth) || (ppdev->ScreenHeight < ppdev->MemHeight)) + { + bDDrawHeap = TRUE; + heap++; + } + + ppdev->dwHeap = heap; + *pdwNumHeaps = heap; + + /* We do not support other fourcc */ + *pdwNumFourCCCodes = 0; + + + /* + check see if pvmList and pdwFourCC are frist call + or frist. Secon call we fill in pHalInfo info + */ + + if(!(pvmList && pdwFourCC)) + { + + RtlZeroMemory(pHalInfo, sizeof(DD_HALINFO)); + pHalInfo->dwSize = sizeof(DD_HALINFO); + + pHalInfo->ddCaps.dwCaps = DDCAPS_BLT | DDCAPS_BLTQUEUE | DDCAPS_BLTCOLORFILL | DDCAPS_READSCANLINE | + DDCAPS_BLTSTRETCH | DDCAPS_COLORKEY | DDCAPS_CANBLTSYSMEM; + + pHalInfo->ddCaps.dwFXCaps = DDFXCAPS_BLTSTRETCHY | DDFXCAPS_BLTSTRETCHX | + DDFXCAPS_BLTSTRETCHYN | DDFXCAPS_BLTSTRETCHXN | + DDFXCAPS_BLTSHRINKY | DDFXCAPS_BLTSHRINKX | + DDFXCAPS_BLTSHRINKYN | DDFXCAPS_BLTSHRINKXN | + DDFXCAPS_BLTMIRRORUPDOWN | DDFXCAPS_BLTMIRRORLEFTRIGHT; + + pHalInfo->ddCaps.dwCaps2 = DDCAPS2_NONLOCALVIDMEM | DDCAPS2_NONLOCALVIDMEMCAPS; + + pHalInfo->ddCaps.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP; + + pHalInfo->ddCaps.dwCKeyCaps = DDCKEYCAPS_SRCBLT | DDCKEYCAPS_SRCBLTCLRSPACE; + + pHalInfo->ddCaps.dwSVBCaps = DDCAPS_BLT; + pHalInfo->ddCaps.ddsCaps.dwCaps |= DDSCAPS_LOCALVIDMEM | DDSCAPS_NONLOCALVIDMEM; + + /* Calc how much memmory is left on the video cards memmory */ + pHalInfo->ddCaps.dwVidMemTotal = (ppdev->MemHeight - ppdev->ScreenHeight) * ppdev->ScreenDelta; + + /* fill in some basic info that we need */ + pHalInfo->vmiData.pvPrimary = ppdev->ScreenPtr; + pHalInfo->vmiData.dwDisplayWidth = ppdev->ScreenWidth; + pHalInfo->vmiData.dwDisplayHeight = ppdev->ScreenHeight; + pHalInfo->vmiData.lDisplayPitch = ppdev->ScreenDelta; + pHalInfo->vmiData.ddpfDisplay.dwSize = sizeof(DDPIXELFORMAT); + pHalInfo->vmiData.ddpfDisplay.dwFlags = DDPF_RGB; + pHalInfo->vmiData.ddpfDisplay.dwRGBBitCount = ppdev->BitsPerPixel; + pHalInfo->vmiData.ddpfDisplay.dwRBitMask = ppdev->RedMask; + pHalInfo->vmiData.ddpfDisplay.dwGBitMask = ppdev->GreenMask; + pHalInfo->vmiData.ddpfDisplay.dwBBitMask = ppdev->BlueMask; + pHalInfo->vmiData.dwOffscreenAlign = 4; + + if ( ppdev->BitsPerPixel == 8 ) + { + pHalInfo->vmiData.ddpfDisplay.dwFlags |= DDPF_PALETTEINDEXED8; + } + + /* FIXME + Config the rops we do not doing that yet + for we need write the rops table + */ + for(i=0;iddCaps.dwSVBRops[i] = rops[i]; + // pHALInfo->ddCaps.dwRops[i] = rops[i]; + } + } + + /* Now build pvmList info */ + if(pvmList) + { + ppdev->pvmList = pvmList; + - if ( bDDrawHeap == TRUE) ++ if (bDDrawHeap) + { + pvmList->dwFlags = VIDMEM_ISLINEAR ; + pvmList->fpStart = ppdev->ScreenHeight * ppdev->ScreenDelta; + pvmList->fpEnd = ppdev->MemHeight * ppdev->ScreenDelta - 1; + pvmList->ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; + pvmList++; + } + + pvmList->fpStart = 0; + pvmList->fpEnd = (ppdev->MemHeight * ppdev->ScreenDelta) - 1; + pvmList->dwFlags = VIDMEM_ISNONLOCAL | VIDMEM_ISLINEAR | VIDMEM_ISWC; + pvmList->ddsCaps.dwCaps = DDSCAPS_FRONTBUFFER | DDSCAPS_BACKBUFFER ; + pvmList->ddsCapsAlt.dwCaps = DDSCAPS_FRONTBUFFER | DDSCAPS_BACKBUFFER; + + pvmList = ppdev->pvmList; + } + + return TRUE; +} + diff --cc win32ss/drivers/displays/vga/main/enable.c index 80d032cddfb,00000000000..eb17d5df9a5 mode 100644,000000..100644 --- a/win32ss/drivers/displays/vga/main/enable.c +++ b/win32ss/drivers/displays/vga/main/enable.c @@@ -1,598 -1,0 +1,598 @@@ +/* + * PROJECT: ReactOS VGA display driver + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/video/displays/vga/main/enable.c + * PURPOSE: + * PROGRAMMERS: + */ + +#include + +static BOOL VGAInitialized = FALSE; + +static DRVFN FuncList[] = +{ + /* Required Display driver fuctions */ + {INDEX_DrvAssertMode, (PFN) DrvAssertMode}, + {INDEX_DrvCompletePDEV, (PFN) DrvCompletePDEV}, + {INDEX_DrvCopyBits, (PFN) DrvCopyBits}, + {INDEX_DrvDisablePDEV, (PFN) DrvDisablePDEV}, + {INDEX_DrvDisableSurface, (PFN) DrvDisableSurface}, + {INDEX_DrvEnablePDEV, (PFN) DrvEnablePDEV}, + {INDEX_DrvEnableSurface, (PFN) DrvEnableSurface}, + {INDEX_DrvGetModes, (PFN) DrvGetModes}, + {INDEX_DrvLineTo, (PFN) DrvLineTo}, + {INDEX_DrvPaint, (PFN) DrvPaint}, + {INDEX_DrvBitBlt, (PFN) DrvBitBlt}, + {INDEX_DrvTransparentBlt, (PFN) DrvTransparentBlt}, + {INDEX_DrvMovePointer, (PFN) DrvMovePointer}, + {INDEX_DrvSetPointerShape, (PFN) DrvSetPointerShape}, + +#if 0 + /* Optional Display driver functions */ + {INDEX_, }, + {INDEX_DescribePixelFormat, (PFN) VGADDIDescribePixelFormat}, + {INDEX_DrvDitherColor, (PFN) VGADDIDitherColor}, + {INDEX_DrvFillPath, (PFN) VGADDIFillPath}, + {INDEX_DrvGetTrueTypeFile, (PFN) VGADDIGetTrueTypeFile}, + {INDEX_DrvLoadFontFile, (PFN) VGADDILoadFontFile}, + {INDEX_DrvQueryFont, (PFN) VGADDIQueryFont}, + {INDEX_DrvQueryFontCaps, (PFN) VGADDIQueryFontCaps}, + {INDEX_DrvQueryFontData, (PFN) VGADDIQueryFontData}, + {INDEX_DrvQueryFontFile, (PFN) VGADDIQueryFontFile}, + {INDEX_DrvQueryFontTree, (PFN) VGADDIQueryFontTree}, + {INDEX_DrvQueryTrueTypeOutline, (PFN) VGADDIQueryTrueTypeOutline}, + {INDEX_DrvQueryTrueTypeTable, (PFN) VGADDIQueryTrueTypeTable}, + {INDEX_DrvRealizeBrush, (PFN) VGADDIRealizeBrush}, + {INDEX_DrvResetPDEV, (PFN) VGADDIResetPDEV}, + {INDEX_DrvSetPalette, (PFN) VGADDISetPalette}, + {INDEX_DrvSetPixelFormat, (PFN) VGADDISetPixelFormat}, + {INDEX_DrvStretchBlt, (PFN) VGADDIStretchBlt}, + {INDEX_DrvStrokePath, (PFN) VGADDIStrokePath}, + {INDEX_DrvSwapBuffers, (PFN) VGADDISwapBuffers}, + {INDEX_DrvTextOut, (PFN) VGADDITextOut}, + {INDEX_DrvUnloadFontFile, (PFN) VGADDIUnloadFontFile}, +#endif +}; + +static GDIINFO gaulCap = { + GDI_DRIVER_VERSION, // ulVersion + DT_RASDISPLAY, // ulTechnology + 0, // ulHorzSize + 0, // ulVertSize + 0, // ulHorzRes (filled in at initialization) + 0, // ulVertRes (filled in at initialization) + 4, // cBitsPixel + 1, // cPlanes + 16, // ulNumColors + 0, // flRaster (DDI reserved field) + + 96, // ulLogPixelsX (must be set to 96 according to MSDN) + 96, // ulLogPixelsY (must be set to 96 according to MSDN) + + TC_RA_ABLE | TC_SCROLLBLT, // flTextCaps + + 6, // ulDACRed + 6, // ulDACGreen + 6, // ulDACBlue + + 0x0024, // ulAspectX (one-to-one aspect ratio) + 0x0024, // ulAspectY + 0x0033, // ulAspectXY + + 1, // xStyleStep + 1, // yStyleSte; + 3, // denStyleStep + + { 0, 0 }, // ptlPhysOffset + { 0, 0 }, // szlPhysSize + + 0, // ulNumPalReg (win3.1 16 color drivers say 0 too) + +// These fields are for halftone initialization. + + { // ciDevice, ColorInfo + { 6700, 3300, 0 }, // Red + { 2100, 7100, 0 }, // Green + { 1400, 800, 0 }, // Blue + { 1750, 3950, 0 }, // Cyan + { 4050, 2050, 0 }, // Magenta + { 4400, 5200, 0 }, // Yellow + { 3127, 3290, 0 }, // AlignmentWhite + 20000, // RedGamma + 20000, // GreenGamma + 20000, // BlueGamma + 0, 0, 0, 0, 0, 0 + }, + + 0, // ulDevicePelsDPI + PRIMARY_ORDER_CBA, // ulPrimaryOrder + HT_PATSIZE_4x4_M, // ulHTPatternSize + HT_FORMAT_4BPP_IRGB, // ulHTOutputFormat + HT_FLAG_ADDITIVE_PRIMS, // flHTFlags + + 0, // ulVRefresh + 8, // ulBltAlignment + 0, // ulPanningHorzRes + 0, // ulPanningVertRes + + 0, // xPanningAlignment + 0, // yPanningAlignment + 0, // cxHTPat + 0, // cyHTPat + NULL, // pHTPatA + NULL, // pHTPatB + NULL, // pHTPatC + 0, // flShadeBlend + 0, // ulPhysicalPixelCharacteristics + 0 // ulPhysicalPixelGamma +}; + +// Palette for VGA + +typedef struct _VGALOGPALETTE +{ + USHORT ident; + USHORT NumEntries; + PALETTEENTRY PaletteEntry[16]; +} VGALOGPALETTE; + +const VGALOGPALETTE VGApalette = +{ + + 0x400, // driver version + 16, // num entries + { + { 0x00, 0x00, 0x00, 0x00 }, // 0 + { 0x80, 0x00, 0x00, 0x00 }, // 1 + { 0x00, 0x80, 0x00, 0x00 }, // 2 + { 0x80, 0x80, 0x00, 0x00 }, // 3 + { 0x00, 0x00, 0x80, 0x00 }, // 4 + { 0x80, 0x00, 0x80, 0x00 }, // 5 + { 0x00, 0x80, 0x80, 0x00 }, // 6 + { 0x80, 0x80, 0x80, 0x00 }, // 7 + { 0xc0, 0xc0, 0xc0, 0x00 }, // 8 + { 0xff, 0x00, 0x00, 0x00 }, // 9 + { 0x00, 0xff, 0x00, 0x00 }, // 10 + { 0xff, 0xff, 0x00, 0x00 }, // 11 + { 0x00, 0x00, 0xff, 0x00 }, // 12 + { 0xff, 0x00, 0xff, 0x00 }, // 13 + { 0x00, 0xff, 0xff, 0x00 }, // 14 + { 0xff, 0xff, 0xff, 0x00 } // 15 + } +}; + +// Devinfo structure passed back to the engine in DrvEnablePDEV + +#define SYSTM_LOGFONT {16,7,0,0,700,0,0,0,ANSI_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,VARIABLE_PITCH | FF_DONTCARE, L"System"} +#define HELVE_LOGFONT {12,9,0,0,400,0,0,0,ANSI_CHARSET,OUT_DEFAULT_PRECIS,CLIP_STROKE_PRECIS,PROOF_QUALITY,VARIABLE_PITCH | FF_DONTCARE, L"MS Sans Serif"} +#define COURI_LOGFONT {12,9,0,0,400,0,0,0,ANSI_CHARSET,OUT_DEFAULT_PRECIS,CLIP_STROKE_PRECIS,PROOF_QUALITY,FIXED_PITCH | FF_DONTCARE, L"Courier"} + +DEVINFO devinfoVGA = +{ + (GCAPS_OPAQUERECT | GCAPS_HORIZSTRIKE | GCAPS_ALTERNATEFILL | GCAPS_MONO_DITHER | GCAPS_COLOR_DITHER | + GCAPS_WINDINGFILL | GCAPS_DITHERONREALIZE + ), // Graphics capabilities + + SYSTM_LOGFONT, // Default font description + HELVE_LOGFONT, // ANSI variable font description + COURI_LOGFONT, // ANSI fixed font description + 0, // Count of device fonts + BMF_4BPP, // preferred DIB format + 8, // Width of color dither + 8, // Height of color dither + NULL, // Default palette to use for this device + 0 // flGraphicsCaps2 +}; + +BOOL APIENTRY +DrvEnableDriver(IN ULONG EngineVersion, + IN ULONG SizeOfDED, + OUT PDRVENABLEDATA DriveEnableData) +{ + DPRINT("DrvEnableDriver called...\n"); + + vgaPreCalc(); + + VGADDI_InitializeOffScreenMem((SCREEN_X * SCREEN_Y) >> 3, 65536 - ((SCREEN_X * SCREEN_Y) >> 3)); + + DriveEnableData->pdrvfn = FuncList; + DriveEnableData->c = sizeof(FuncList) / sizeof(DRVFN); + DriveEnableData->iDriverVersion = DDI_DRIVER_VERSION_NT4; + + return TRUE; +} + +// DrvDisableDriver +// DESCRIPTION: +// This function is called by the KMGDI at exit. It should cleanup. +// ARGUMENTS: +// NONE +// RETURNS: +// NONE + +VOID APIENTRY +DrvDisableDriver(VOID) +{ + return; +} + +// ----------------------------------------------- Driver Implementation + + +// DrvEnablePDEV +// DESCRIPTION: +// This function is called after DrvEnableDriver to get information +// about the mode that is to be used. This function just returns +// information, and should not yet initialize the mode. +// ARGUMENTS: +// IN DEVMODEW * DM Describes the mode requested +// IN LPWSTR LogAddress +// IN ULONG PatternCount number of patterns expected +// OUT HSURF * SurfPatterns array to contain pattern handles +// IN ULONG GDIInfoSize the size of the GDIInfo object passed in +// OUT ULONG * GDIInfo GDI Info object +// IN ULONG DevInfoSize the size of the DevInfo object passed in +// OUT ULONG * DevInfo Device Info object +// IN LPWSTR DevDataFile ignore +// IN LPWSTR DeviceName Device name +// IN HANDLE Driver handle to KM driver +// RETURNS: +// DHPDEV a handle to a DPev object + +DHPDEV APIENTRY +DrvEnablePDEV(IN DEVMODEW *DM, + IN LPWSTR LogAddress, + IN ULONG PatternCount, + OUT HSURF *SurfPatterns, + IN ULONG GDIInfoSize, + OUT ULONG *GDIInfo, + IN ULONG DevInfoSize, + OUT DEVINFO *DevInfo, + IN HDEV Dev, + IN LPWSTR DeviceName, + IN HANDLE Driver) +{ + PPDEV PDev; + + PDev = EngAllocMem(FL_ZERO_MEMORY, sizeof(PDEV), ALLOC_TAG); + if (PDev == NULL) + { + DPRINT1("EngAllocMem failed for PDEV\n"); + return NULL; + } + PDev->KMDriver = Driver; + DPRINT( "PDev: %x, Driver: %x\n", PDev, PDev->KMDriver ); + + gaulCap.ulHorzRes = SCREEN_X; + gaulCap.ulVertRes = SCREEN_Y; + if (sizeof(GDIINFO) < GDIInfoSize) + GDIInfoSize = sizeof(GDIINFO); + memcpy(GDIInfo, &gaulCap, GDIInfoSize); + DM->dmBitsPerPel = gaulCap.cBitsPixel * gaulCap.cPlanes; + DM->dmPelsWidth = gaulCap.ulHorzRes; + DM->dmPelsHeight = gaulCap.ulVertRes; + + devinfoVGA.hpalDefault = EngCreatePalette(PAL_INDEXED, 16, (ULONG *) VGApalette.PaletteEntry, 0, 0, 0); + if (sizeof(DEVINFO) < DevInfoSize) + DevInfoSize = sizeof(DEVINFO); + memcpy(DevInfo, &devinfoVGA, DevInfoSize); + + return (DHPDEV) PDev; +} + + +// DrvCompletePDEV +// DESCRIPTION +// Called after initialization of PDEV is complete. Supplies +// a reference to the GDI handle for the PDEV. + +VOID APIENTRY +DrvCompletePDEV(IN DHPDEV PDev, + IN HDEV Dev) +{ + ((PPDEV) PDev)->GDIDevHandle = Dev; // Handle to the DC +} + + +BOOL APIENTRY +DrvAssertMode(IN DHPDEV DPev, + IN BOOL Enable) +{ + PPDEV ppdev = (PPDEV)DPev; + ULONG returnedDataLength; + - if(Enable==TRUE) ++ if (Enable) + { + /* Reenable our graphics mode */ + if (!InitPointer(ppdev)) + { + /* Failed to set pointer */ + return FALSE; + } + + if (!VGAInitialized) + { + if (!InitVGA(ppdev, FALSE)) + { + /* Failed to initialize the VGA */ + return FALSE; + } + VGAInitialized = TRUE; + } + } + else + { + /* Go back to last known mode */ + DPRINT( "ppdev: %x, KMDriver: %x", ppdev, ppdev->KMDriver ); + if (EngDeviceIoControl(ppdev->KMDriver, IOCTL_VIDEO_RESET_DEVICE, NULL, 0, NULL, 0, &returnedDataLength)) + { + /* Failed to go back to mode */ + return FALSE; + } + VGAInitialized = FALSE; + } + return TRUE; +} + + +VOID APIENTRY +DrvDisablePDEV(IN DHPDEV PDev) +{ + PPDEV ppdev = (PPDEV)PDev; + + /* EngDeletePalette(devinfoVGA.hpalDefault); */ + if (ppdev->pjPreallocSSBBuffer) + EngFreeMem(ppdev->pjPreallocSSBBuffer); + + if (ppdev->pucDIB4ToVGAConvBuffer) + EngFreeMem(ppdev->pucDIB4ToVGAConvBuffer); + + DPRINT("Freeing PDEV\n"); + EngFreeMem(PDev); +} + + +VOID APIENTRY +DrvDisableSurface(IN DHPDEV PDev) +{ + PPDEV ppdev = (PPDEV)PDev; + PDEVSURF pdsurf = ppdev->AssociatedSurf; + + DPRINT("KMDriver: %x\n", ppdev->KMDriver); + DeinitVGA(ppdev); + /* EngFreeMem(pdsurf->BankSelectInfo); */ + + if (pdsurf->BankInfo != NULL) + EngFreeMem(pdsurf->BankInfo); + if (pdsurf->BankInfo2RW != NULL) + EngFreeMem(pdsurf->BankInfo2RW); + if (pdsurf->BankBufferPlane0 != NULL) + EngFreeMem(pdsurf->BankBufferPlane0); + if (ppdev->pPointerAttributes != NULL) + EngFreeMem(ppdev->pPointerAttributes); + + /* free any pending saved screen bit blocks */ +#if 0 + pSSB = pdsurf->ssbList; + while (pSSB != (PSAVED_SCREEN_BITS) NULL) + { + /* Point to the next saved screen bits block */ + pSSBNext = (PSAVED_SCREEN_BITS) pSSB->pvNextSSB; + + /* Free the current block */ + EngFreeMem(pSSB); + pSSB = pSSBNext; + } +#endif + EngDeleteSurface((HSURF) ppdev->SurfHandle); + /* EngFreeMem(pdsurf); */ /* free the surface */ +} + + +static VOID +InitSavedBits(IN PPDEV ppdev) +{ + if (!(ppdev->fl & DRIVER_OFFSCREEN_REFRESHED)) + return; + + /* set up rect to right of visible screen */ + ppdev->SavedBitsRight.left = ppdev->sizeSurf.cx; + ppdev->SavedBitsRight.top = 0; + ppdev->SavedBitsRight.right = ppdev->sizeMem.cx - PLANAR_PELS_PER_CPU_ADDRESS; + ppdev->SavedBitsRight.bottom = ppdev->sizeSurf.cy; + + if ((ppdev->SavedBitsRight.right <= ppdev->SavedBitsRight.left) || + (ppdev->SavedBitsRight.bottom <= ppdev->SavedBitsRight.top)) + { + ppdev->SavedBitsRight.left = 0; + ppdev->SavedBitsRight.top = 0; + ppdev->SavedBitsRight.right = 0; + ppdev->SavedBitsRight.bottom = 0; + } + + /* set up rect below visible screen */ + ppdev->SavedBitsBottom.left = 0; + ppdev->SavedBitsBottom.top = ppdev->sizeSurf.cy; + ppdev->SavedBitsBottom.right = ppdev->sizeMem.cx - PLANAR_PELS_PER_CPU_ADDRESS; + ppdev->SavedBitsBottom.bottom = ppdev->sizeMem.cy - ppdev->NumScansUsedByPointer; + + if ((ppdev->SavedBitsBottom.right <= ppdev->SavedBitsBottom.left) || + (ppdev->SavedBitsBottom.bottom <= ppdev->SavedBitsBottom.top)) + { + ppdev->SavedBitsBottom.left = 0; + ppdev->SavedBitsBottom.top = 0; + ppdev->SavedBitsBottom.right = 0; + ppdev->SavedBitsBottom.bottom = 0; + } + + ppdev->BitsSaved = FALSE; +} + + +HSURF APIENTRY +DrvEnableSurface(IN DHPDEV PDev) +{ + PPDEV ppdev = (PPDEV)PDev; + PDEVSURF pdsurf; + DHSURF dhsurf; + HSURF hsurf; + + DPRINT("DrvEnableSurface() called\n"); + + /* Initialize the VGA */ + if (!VGAInitialized) + { + if (!InitVGA(ppdev, TRUE)) + goto error_done; + VGAInitialized = TRUE; + } + + /* dhsurf is of type DEVSURF, which is the drivers specialized surface type */ + dhsurf = (DHSURF)EngAllocMem(0, sizeof(DEVSURF), ALLOC_TAG); + if (dhsurf == (DHSURF) 0) + goto error_done; + + pdsurf = (PDEVSURF) dhsurf; + pdsurf->ident = DEVSURF_IDENT; + pdsurf->flSurf = 0; + pdsurf->Format = BMF_PHYSDEVICE; + pdsurf->jReserved1 = 0; + pdsurf->jReserved2 = 0; + pdsurf->ppdev = ppdev; + pdsurf->sizeSurf.cx = ppdev->sizeSurf.cx; + pdsurf->sizeSurf.cy = ppdev->sizeSurf.cy; + pdsurf->NextPlane = 0; + pdsurf->Scan0 = ppdev->fbScreen; + pdsurf->BitmapStart = ppdev->fbScreen; + pdsurf->StartBmp = ppdev->fbScreen; + pdsurf->BankInfo = NULL; + pdsurf->BankInfo2RW = NULL; + pdsurf->BankBufferPlane0 = NULL; + +/* pdsurf->Conv = &ConvertBuffer[0]; */ + + if (!InitPointer(ppdev)) + { + DPRINT1("DrvEnablePDEV failed bInitPointer\n"); + goto error_clean; + } + +/* if (!SetUpBanking(pdsurf, ppdev)) + { + DPRINT1("DrvEnablePDEV failed SetUpBanking\n"); + goto error_clean; + } BANKING CODE UNIMPLEMENTED */ + + if ((hsurf = EngCreateDeviceSurface(dhsurf, ppdev->sizeSurf, BMF_4BPP)) == + (HSURF)0) + { + /* Call to EngCreateDeviceSurface failed */ + DPRINT("EngCreateDeviceSurface call failed\n"); + goto error_clean; + } + + InitSavedBits(ppdev); + + if (EngAssociateSurface(hsurf, ppdev->GDIDevHandle, HOOK_BITBLT | HOOK_PAINT | HOOK_LINETO | HOOK_COPYBITS | + HOOK_TRANSPARENTBLT)) + { + DPRINT("Successfully associated surface\n"); + ppdev->SurfHandle = hsurf; + ppdev->AssociatedSurf = pdsurf; + + /* Set up an empty saved screen block list */ + pdsurf->ssbList = NULL; + + return hsurf; + } + DPRINT("EngAssociateSurface() failed\n"); + EngDeleteSurface(hsurf); + +error_clean: + EngFreeMem(dhsurf); + +error_done: + return (HSURF)0; +} + + +ULONG APIENTRY +DrvGetModes(IN HANDLE Driver, + IN ULONG DataSize, + OUT PDEVMODEW DM) +{ + DWORD NumModes; + DWORD ModeSize; + DWORD OutputSize; + DWORD OutputModes = DataSize / (sizeof(DEVMODEW) + DRIVER_EXTRA_SIZE); + PVIDEO_MODE_INFORMATION VideoModeInformation, VideoTemp; + + NumModes = getAvailableModes(Driver, + (PVIDEO_MODE_INFORMATION *) &VideoModeInformation, + &ModeSize); + + if (NumModes == 0) + return 0; + + if (DM == NULL) + { + OutputSize = NumModes * (sizeof(DEVMODEW) + DRIVER_EXTRA_SIZE); + } + else + { + OutputSize=0; + VideoTemp = VideoModeInformation; + + do + { + if (VideoTemp->Length != 0) + { + if (OutputModes == 0) + break; + + memset(DM, 0, sizeof(DEVMODEW)); + memcpy(DM->dmDeviceName, DLL_NAME, sizeof(DLL_NAME)); + + DM->dmSpecVersion = DM_SPECVERSION; + DM->dmDriverVersion = DM_SPECVERSION; + DM->dmSize = sizeof(DEVMODEW); + DM->dmDriverExtra = DRIVER_EXTRA_SIZE; + DM->dmBitsPerPel = VideoTemp->NumberOfPlanes * + VideoTemp->BitsPerPlane; + DM->dmPelsWidth = VideoTemp->VisScreenWidth; + DM->dmPelsHeight = VideoTemp->VisScreenHeight; + DM->dmDisplayFrequency = VideoTemp->Frequency; + DM->dmDisplayFlags = 0; + + DM->dmFields = DM_BITSPERPEL | + DM_PELSWIDTH | + DM_PELSHEIGHT | + DM_DISPLAYFREQUENCY | + DM_DISPLAYFLAGS ; + + /* next DEVMODE entry */ + OutputModes--; + + DM = (PDEVMODEW) ( ((ULONG_PTR)DM) + sizeof(DEVMODEW) + DRIVER_EXTRA_SIZE); + + OutputSize += (sizeof(DEVMODEW) + DRIVER_EXTRA_SIZE); + } + + VideoTemp = (PVIDEO_MODE_INFORMATION)(((PUCHAR)VideoTemp) + ModeSize); + + } while (--NumModes); + } + return OutputSize; +} + +ULONG DbgPrint(PCCH Format,...) +{ + va_list ap; + va_start(ap, Format); + EngDebugPrint("VGADDI", (PCHAR)Format, ap); + va_end(ap); + return 0; +} + +/* EOF */ diff --cc win32ss/drivers/displays/vga/vgavideo/vgavideo.c index 492761ce186,00000000000..29ce0a7a4d0 mode 100644,000000..100644 --- a/win32ss/drivers/displays/vga/vgavideo/vgavideo.c +++ b/win32ss/drivers/displays/vga/vgavideo/vgavideo.c @@@ -1,944 -1,0 +1,944 @@@ +/* + * PROJECT: ReactOS VGA display driver + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/video/displays/vga/vgavideo/vgavideo.c + * PURPOSE: + * PROGRAMMERS: + */ + +#include + +UCHAR PreCalcReverseByte[256]; +int maskbit[640]; +int y80[480]; +int xconv[640]; +int bit8[640]; +int startmasks[8]; +int endmasks[8]; +PBYTE vidmem; +static ULONG UnpackPixel[256]; + +static unsigned char leftMask; +static int byteCounter; +static unsigned char rightMask; + +UCHAR bytesPerPixel(ULONG Format) +{ + /* This function is taken from /subsys/win32k/eng/surface.c + * FIXME: GDI bitmaps are supposed to be pixel-packed. Right now if the + * pixel size if < 1 byte we expand it to 1 byte for simplicities sake */ + + switch (Format) + { + case BMF_1BPP: + return 1; + + case BMF_4BPP: + case BMF_4RLE: + return 1; + + case BMF_8BPP: + case BMF_8RLE: + return 1; + + case BMF_16BPP: + return 2; + + case BMF_24BPP: + return 3; + + case BMF_32BPP: + return 4; + + default: + return 0; + } +} + +VOID vgaPreCalc() +{ + ULONG j; + + startmasks[0] = 255; + startmasks[1] = 1; + startmasks[2] = 3; + startmasks[3] = 7; + startmasks[4] = 15; + startmasks[5] = 31; + startmasks[6] = 63; + startmasks[7] = 127; + + endmasks[0] = 0; + endmasks[1] = 128; + endmasks[2] = 192; + endmasks[3] = 224; + endmasks[4] = 240; + endmasks[5] = 248; + endmasks[6] = 252; + endmasks[7] = 254; + + for (j = 0; j < 80; j++) + { + maskbit[j*8] = 128; + maskbit[j*8+1] = 64; + maskbit[j*8+2] = 32; + maskbit[j*8+3] = 16; + maskbit[j*8+4] = 8; + maskbit[j*8+5] = 4; + maskbit[j*8+6] = 2; + maskbit[j*8+7] = 1; + + bit8[j*8] = 7; + bit8[j*8+1] = 6; + bit8[j*8+2] = 5; + bit8[j*8+3] = 4; + bit8[j*8+4] = 3; + bit8[j*8+5] = 2; + bit8[j*8+6] = 1; + bit8[j*8+7] = 0; + } + for (j = 0; j < SCREEN_Y; j++) + y80[j] = j*80; + for (j = 0; j < SCREEN_X; j++) + xconv[j] = j >> 3; + + for (j = 0; j < 256; j++) + { + PreCalcReverseByte[j] = + (((j >> 0) & 0x1) << 7) | + (((j >> 1) & 0x1) << 6) | + (((j >> 2) & 0x1) << 5) | + (((j >> 3) & 0x1) << 4) | + (((j >> 4) & 0x1) << 3) | + (((j >> 5) & 0x1) << 2) | + (((j >> 6) & 0x1) << 1) | + (((j >> 7) & 0x1) << 0); + } + + for (j = 0; j < 256; j++) + { + UnpackPixel[j] = + (((j >> 0) & 0x1) << 4) | + (((j >> 1) & 0x1) << 0) | + (((j >> 2) & 0x1) << 12) | + (((j >> 3) & 0x1) << 8) | + (((j >> 4) & 0x1) << 20) | + (((j >> 5) & 0x1) << 16) | + (((j >> 6) & 0x1) << 28) | + (((j >> 7) & 0x1) << 24); + } +} + +void +get_masks(int x, int w) +{ + register int tmp; + + leftMask = rightMask = 0; + byteCounter = w; + /* right margin */ + tmp = (x+w) & 7; + if (tmp) + { + byteCounter -= tmp; + rightMask = (unsigned char)(0xff00 >> tmp); + } + /* left margin */ + tmp = x & 7; + if (tmp) + { + byteCounter -= (8 - tmp); + leftMask = (0xff >> tmp); + } + /* too small ? */ + if (byteCounter < 0) + { + leftMask &= rightMask; + rightMask = 0; + byteCounter = 0; + } + byteCounter /= 8; +} + +VOID vgaPutPixel(INT x, INT y, UCHAR c) +{ + ULONG offset; + + offset = xconv[x]+y80[y]; + + WRITE_PORT_UCHAR((PUCHAR)GRA_I,0x08); + WRITE_PORT_UCHAR((PUCHAR)GRA_D,maskbit[x]); + + READ_REGISTER_UCHAR(vidmem + offset); + WRITE_REGISTER_UCHAR(vidmem + offset, c); +} + +VOID vgaPutByte(INT x, INT y, UCHAR c) +{ + ULONG offset; + + offset = xconv[x]+y80[y]; + + /* Set the write mode */ + WRITE_PORT_UCHAR((PUCHAR)GRA_I,0x08); + WRITE_PORT_UCHAR((PUCHAR)GRA_D,0xff); + + WRITE_REGISTER_UCHAR(vidmem + offset, c); +} + +VOID vgaGetByte( + IN ULONG offset, + OUT UCHAR *b, + OUT UCHAR *g, + OUT UCHAR *r, + OUT UCHAR *i) +{ + WRITE_PORT_USHORT((PUSHORT)GRA_I, 0x0304); + *i = READ_REGISTER_UCHAR(vidmem + offset); + WRITE_PORT_UCHAR((PUCHAR)GRA_D, 0x02); + *r = READ_REGISTER_UCHAR(vidmem + offset); + WRITE_PORT_UCHAR((PUCHAR)GRA_D, 0x01); + *g = READ_REGISTER_UCHAR(vidmem + offset); + WRITE_PORT_UCHAR((PUCHAR)GRA_D, 0x00); + *b = READ_REGISTER_UCHAR(vidmem + offset); +} + +INT vgaGetPixel( + IN INT x, + IN INT y) +{ + UCHAR mask, b, g, r, i; + ULONG offset; + + offset = xconv[x] + y80[y]; + vgaGetByte(offset, &b, &g, &r, &i); + + mask = maskbit[x]; + b = b & mask; + g = g & mask; + r = r & mask; + i = i & mask; + + mask = bit8[x]; + g = g >> mask; + b = b >> mask; + r = r >> mask; + i = i >> mask; + + return (b + 2 * g + 4 * r + 8 * i); +} + +BOOL vgaHLine(INT x, INT y, INT len, UCHAR c) +{ + ULONG orgx, pre1, midpre1; + //ULONG orgpre1; + LONG ileftpix, imidpix, irightpix; + + orgx = x; + + /*if ( len < 8 ) + { + for (i = x; i < x+len; i++ ) + vgaPutPixel ( i, y, c ); + + return TRUE; + }*/ + + /* Calculate the left mask pixels, middle bytes and right mask pixel */ + ileftpix = 7 - mod8(x-1); + irightpix = mod8(x+len); + imidpix = (len-ileftpix-irightpix) / 8; + + pre1 = xconv[(x-1)&~7] + y80[y]; + //orgpre1=pre1; + + /* check for overlap ( very short line ) */ + if ( (ileftpix+irightpix) > len ) + { + int mask = startmasks[ileftpix] & endmasks[irightpix]; + /* Write left pixels */ + WRITE_PORT_UCHAR((PUCHAR)GRA_I,0x08); // set the mask + WRITE_PORT_UCHAR((PUCHAR)GRA_D,mask); + + READ_REGISTER_UCHAR(vidmem + pre1); + WRITE_REGISTER_UCHAR(vidmem + pre1, c); + + return TRUE; + } + + /* Left */ + if ( ileftpix > 0 ) + { + /* Write left pixels */ + WRITE_PORT_UCHAR((PUCHAR)GRA_I,0x08); // set the mask + WRITE_PORT_UCHAR((PUCHAR)GRA_D,startmasks[ileftpix]); + + READ_REGISTER_UCHAR(vidmem + pre1); + WRITE_REGISTER_UCHAR(vidmem + pre1, c); + + /* Prepare new x for the middle */ + x = orgx + 8; + } + + if ( imidpix > 0 ) + { + midpre1 = xconv[x] + y80[y]; + + /* Set mask to all pixels in byte */ + WRITE_PORT_UCHAR((PUCHAR)GRA_I, 0x08); + WRITE_PORT_UCHAR((PUCHAR)GRA_D, 0xff); + memset(vidmem+midpre1, c, imidpix); // write middle pixels, no need to read in latch because of the width + } + + if ( irightpix > 0 ) + { + x = orgx + len - irightpix; + pre1 = xconv[x] + y80[y]; + + /* Write right pixels */ + WRITE_PORT_UCHAR((PUCHAR)GRA_I,0x08); // set the mask bits + WRITE_PORT_UCHAR((PUCHAR)GRA_D, endmasks[irightpix]); + READ_REGISTER_UCHAR(vidmem + pre1); + WRITE_REGISTER_UCHAR(vidmem + pre1, c); + } + + return TRUE; +} + +BOOL vgaVLine(INT x, INT y, INT len, UCHAR c) +{ + INT offset, i; + + offset = xconv[x]+y80[y]; + +#ifdef VGA_PERF + vgaSetBitMaskRegister ( maskbit[x] ); +#else + WRITE_PORT_UCHAR((PUCHAR)GRA_I,0x08); // set the mask + WRITE_PORT_UCHAR((PUCHAR)GRA_D,maskbit[x]); +#endif + + for(i=y; ileft = max(prcSrc1->left, prcSrc2->left); + prcDst->right = min(prcSrc1->right, prcSrc2->right); + + if (prcDst->left < prcDst->right) + { + prcDst->top = max(prcSrc1->top, prcSrc2->top); + prcDst->bottom = min(prcSrc1->bottom, prcSrc2->bottom); + + if (prcDst->top < prcDst->bottom) + return TRUE; + } + + *prcDst = rclEmpty; + + return FALSE; +} + +void DIB_BltFromVGA(int x, int y, int w, int h, void *b, int Dest_lDelta) +{ + ULONG plane; + ULONG left = x >> 3; + ULONG shift = x - (x & ~0x7); + UCHAR pixel, nextpixel; + LONG rightcount; + INT i, j; + LONG stride = w >> 3; + + /* Calculate the number of rightmost bytes not in a dword block. */ + if (w >= 8) + { + rightcount = w % 8; + } + else + { + stride = 0; + rightcount = w; + } + + /* Reset the destination. */ + for (j = 0; j < h; j++) + memset((PVOID)((ULONG_PTR)b + (j * Dest_lDelta)), 0, abs(Dest_lDelta)); + + for (plane = 0; plane < 4; plane++) + { + PUCHAR dest = b; + + /* Select the plane we are reading in this iteration. */ + WRITE_PORT_UCHAR((PUCHAR)GRA_I, 0x04); + WRITE_PORT_UCHAR((PUCHAR)GRA_D, plane); + + for (j = 0; j < h; j++) + { + PULONG destline = (PULONG)dest; + PUCHAR src = vidmem + (y + j) * SCREEN_STRIDE + left; + /* Read the data for one plane for an eight aligned pixel block. */ + nextpixel = PreCalcReverseByte[READ_REGISTER_UCHAR(src)]; + for (i = 0; i < stride; i++, src++, destline++) + { + /* Form the data for one plane for an aligned block in the destination. */ + pixel = nextpixel; + pixel >>= shift; + + nextpixel = PreCalcReverseByte[READ_REGISTER_UCHAR(src + 1)]; + pixel |= (nextpixel << (8 - shift)); + + /* Expand the plane data to 'chunky' format and store. */ + *destline |= (UnpackPixel[pixel] << plane); + } + /* Handle any pixels not falling into a full block. */ + if (rightcount != 0) + { + ULONG row; + + /* Form the data for a complete block. */ + pixel = nextpixel; + pixel >>= shift; + + nextpixel = PreCalcReverseByte[READ_REGISTER_UCHAR(src + 1)]; + pixel |= (nextpixel << (8 - shift)); + + row = UnpackPixel[pixel] << plane; + + /* Store the data for each pixel in the destination. */ + for (i = 0; i < rightcount; i++) + { + ((PUCHAR)destline)[i] |= (row & 0xFF); + row >>= 8; + } + } + dest += Dest_lDelta; + } + } + +#ifdef VGA_VERIFY + for (j = 0; j < h; j++) + { + for (i = 0; i < w; i += 2) + { + UCHAR c1, c2; + ULONG mask = (i < (w - 1)) ? 0xFF : 0xF0; + + c1 = (vgaGetPixel(x + i, y + j) << 4) | (vgaGetPixel(x + i + 1, y + j)); + c2 = ((PUCHAR)b)[(j * Dest_lDelta) + (i >> 1)]; + if ((c1 & mask) != (c2 & mask)) + EngDebugBreak(); + } + } +#endif /* VGA_VERIFY */ +} + +/* DIB blt to the VGA. */ +void DIB_BltToVGA(int x, int y, int w, int h, void *b, int Source_lDelta, int StartMod) +{ + PUCHAR pb, opb = b; + LONG i, j; + LONG x2 = x + w; + LONG y2 = y + h; + ULONG offset; + + for (i = x; i < x2; i++) + { + pb = opb; + offset = xconv[i] + y80[y]; + + WRITE_PORT_UCHAR((PUCHAR)GRA_I, 0x08); // set the mask + WRITE_PORT_UCHAR((PUCHAR)GRA_D, maskbit[i]); + + if (StartMod == ((i - x) % 2)) + { + for (j = y; j < y2; j++) + { + READ_REGISTER_UCHAR(vidmem + offset); + WRITE_REGISTER_UCHAR(vidmem + offset, (*pb & 0xf0) >> 4); + offset += 80; + pb += Source_lDelta; + } + } + else + { + for (j = y; j < y2; j++) + { + READ_REGISTER_UCHAR(vidmem + offset); + WRITE_REGISTER_UCHAR(vidmem + offset, *pb & 0x0f); + offset += 80; + pb += Source_lDelta; + } + } + + if (StartMod != ((i - x) % 2)) + opb++; + } +} + + +/* DIB blt to the VGA. */ +void DIB_BltToVGAWithXlate(int x, int y, int w, int h, void *b, int Source_lDelta, XLATEOBJ* Xlate) +{ + PUCHAR pb, opb = b; + ULONG i, j; + ULONG x2 = x + w; + ULONG y2 = y + h; + ULONG offset; + + for (i = x; i < x2; i++) + { + pb = opb; + offset = xconv[i] + y80[y]; + + WRITE_PORT_UCHAR((PUCHAR)GRA_I, 0x08); // set the mask + WRITE_PORT_UCHAR((PUCHAR)GRA_D, maskbit[i]); + + if (0 == ((i - x) % 2)) + { + for (j = y; j < y2; j++) + { + READ_REGISTER_UCHAR(vidmem + offset); + WRITE_REGISTER_UCHAR(vidmem + offset, XLATEOBJ_iXlate(Xlate, (*pb & 0xf0) >> 4)); + offset += 80; + pb += Source_lDelta; + } + } + else + { + for (j = y; j < y2; j++) + { + READ_REGISTER_UCHAR(vidmem + offset); + WRITE_REGISTER_UCHAR(vidmem + offset, XLATEOBJ_iXlate(Xlate, *pb & 0x0f)); + offset += 80; + pb += Source_lDelta; + } + } + + if (0 != ((i - x) % 2)) + opb++; + } +} + +/* DIB blt to the VGA. + * For now we just do slow writes -- pixel by pixel, + * packing each one into the correct 4BPP format. */ +void DIB_TransparentBltToVGA(int x, int y, int w, int h, void *b, int Source_lDelta, ULONG trans) + +{ + PUCHAR pb = b, opb = b; + BOOLEAN edgePixel = FALSE; + ULONG i, j; + ULONG x2 = x + w; + ULONG y2 = y + h; + UCHAR b1, b2; + + /* Check if the width is odd */ + if(mod2(w) > 0) + { + edgePixel = TRUE; + x2 -= 1; + } + + for (j=y; j> 4; + b2 = *pb & 0x0f; + if(b1 != trans) vgaPutPixel(i, j, b1); + if(b2 != trans) vgaPutPixel(i+1, j, b2); + pb++; + } + - if (edgePixel == TRUE) ++ if (edgePixel) + { + b1 = *pb; + if(b1 != trans) vgaPutPixel(x2, j, b1); + pb++; + } + + opb += Source_lDelta; + pb = opb; // new test code + } +} + +// This algorithm goes from left to right, storing each 4BPP pixel +// in an entire byte. +void FASTCALL +vgaReadScan( int x, int y, int w, void *b ) +{ + unsigned char *vp, *vpP; + unsigned char data, mask, maskP; + unsigned char *bp; + unsigned char plane_mask; + int plane, i; + + ASSIGNVP4(x, y, vpP) + ASSIGNMK4(x, y, maskP) + get_masks(x, w); + WRITE_PORT_USHORT((PUSHORT)GRA_I, 0x0005); // read mode 0 + WRITE_PORT_UCHAR((PUCHAR)GRA_I, 0x04); // read map select + + memset ( b, 0, w ); + + for ( plane=0, plane_mask=1; plane < 4; plane++, plane_mask<<=1 ) + { + WRITE_PORT_UCHAR((PUCHAR)GRA_D, plane); // read map select + + vp = vpP; + bp = b; + if ( leftMask ) + { + mask = maskP; + data = *vp++; + do + { + if (data & mask) + *bp |= plane_mask; + bp++; + mask >>= 1; + } while (mask & leftMask); + } + if (byteCounter) + { + for (i=byteCounter; i>0; i--) + { + data = *vp++; + if (data & 0x80) *bp |= plane_mask; + bp++; + + if (data & 0x40) *bp |= plane_mask; + bp++; + if (data & 0x20) *bp |= plane_mask; + bp++; + if (data & 0x10) *bp |= plane_mask; + bp++; + if (data & 0x08) *bp |= plane_mask; + bp++; + if (data & 0x04) *bp |= plane_mask; + bp++; + if (data & 0x02) *bp |= plane_mask; + bp++; + if (data & 0x01) *bp |= plane_mask; + bp++; + } + } + if (rightMask) + { + mask = 0x80; + data = *vp; + do + { + if (data & mask) + *bp |= plane_mask; + bp++; + mask >>= 1; + } while (mask & rightMask); + } + } +} + +/* This algorithm goes from left to right + * It stores each 4BPP pixel in an entire byte. */ +void FASTCALL +vgaWriteScan ( int x, int y, int w, void *b ) +{ + unsigned char *bp; + unsigned char *vp; + //unsigned char init_mask; + volatile unsigned char dummy; + //int byte_per_line; + int i, j, off, init_off = x&7; + + bp = b; + ASSIGNVP4(x, y, vp) + //ASSIGNMK4(x, y, init_mask) + //byte_per_line = SCREEN_X >> 3; + + WRITE_PORT_UCHAR((PUCHAR)GRA_I, 0x05); // write mode 2 + WRITE_PORT_UCHAR((PUCHAR)GRA_D, 0x02); + WRITE_PORT_UCHAR((PUCHAR)GRA_I, 0x03); // replace + WRITE_PORT_UCHAR((PUCHAR)GRA_D, 0x00); + WRITE_PORT_UCHAR((PUCHAR)GRA_I, 0x08); // bit mask + + for ( j = 0; j < 8; j++) + { + unsigned int mask = 0x80 >> j; + WRITE_PORT_UCHAR ( (PUCHAR)GRA_D, (unsigned char)mask ); + i = j - init_off; + off = 0; + if (j < init_off) + i += 8, off++; + while (i < w) + { + dummy = vp[off]; + dummy = bp[i]; + vp[off] = dummy; + i += 8; + off++; + } + } +} + +/* This algorithm goes from left to right, and inside that loop, top to bottom. + * It also stores each 4BPP pixel in an entire byte. */ +void DFB_BltFromVGA(int x, int y, int w, int h, void *b, int bw) +{ + unsigned char *vp, *vpY, *vpP; + unsigned char data, mask, maskP; + unsigned char *bp, *bpY; + unsigned char plane_mask; + int byte_per_line = SCREEN_X >> 3; + int plane, i, j; + + ASSIGNVP4(x, y, vpP) + ASSIGNMK4(x, y, maskP) + get_masks(x, w); + WRITE_PORT_UCHAR((PUCHAR)GRA_I, 0x05); // read mode 0 + WRITE_PORT_UCHAR((PUCHAR)GRA_D, 0x00); + WRITE_PORT_UCHAR((PUCHAR)GRA_I, 0x04); // read map select + + /* clear buffer */ + bp = b; + for (j = h; j > 0; j--) + { + memset(bp, 0, w); + bp += bw; + } + + for (plane = 0, plane_mask = 1; plane < 4; plane++, plane_mask <<= 1) + { + WRITE_PORT_UCHAR((PUCHAR)GRA_D, plane); // read map select + vpY = vpP; + bpY = b; + for (j = h; j > 0; j--) + { + vp = vpY; + bp = bpY; + if (leftMask) + { + mask = maskP; + data = *vp++; + do + { + if (data & mask) + *bp |= plane_mask; + bp++; + mask >>= 1; + } while (mask & leftMask); + } + if (byteCounter) + { + for (i=byteCounter; i>0; i--) + { + data = *vp++; + if (data & 0x80) *bp |= plane_mask; + bp++; + if (data & 0x40) *bp |= plane_mask; + bp++; + if (data & 0x20) *bp |= plane_mask; + bp++; + if (data & 0x10) *bp |= plane_mask; + bp++; + if (data & 0x08) *bp |= plane_mask; + bp++; + if (data & 0x04) *bp |= plane_mask; + bp++; + if (data & 0x02) *bp |= plane_mask; + bp++; + if (data & 0x01) *bp |= plane_mask; + bp++; + } + } + if (rightMask) + { + mask = 0x80; + data = *vp; + do + { + if (data & mask) *bp |= plane_mask; + bp++; + mask >>= 1; + } while (mask & rightMask); + } + bpY += bw; + vpY += byte_per_line; + } + } + + // We don't need this if the next call is a DFB blt to VGA (as in the case of moving the mouse pointer) + WRITE_PORT_UCHAR((PUCHAR)GRA_I, 0x05); // write mode 2 + WRITE_PORT_UCHAR((PUCHAR)GRA_D, 0x02); + WRITE_PORT_UCHAR((PUCHAR)GRA_I, 0x03); // replace + WRITE_PORT_UCHAR((PUCHAR)GRA_D, 0x00); +} + +/* This algorithm goes from left to right, and inside that loop, top to bottom. + * It also stores each 4BPP pixel in an entire byte. */ +void DFB_BltToVGA(int x, int y, int w, int h, void *b, int bw) +{ + unsigned char *bp, *bpX; + unsigned char *vp, *vpX; + unsigned char mask; + //volatile unsigned char dummy; + int byte_per_line; + int i, j; + + bpX = b; + ASSIGNVP4(x, y, vpX) + ASSIGNMK4(x, y, mask) + byte_per_line = SCREEN_X >> 3; + + WRITE_PORT_UCHAR((PUCHAR)GRA_I, 0x05); // write mode 2 + WRITE_PORT_UCHAR((PUCHAR)GRA_D, 0x02); + WRITE_PORT_UCHAR((PUCHAR)GRA_I, 0x03); // replace + WRITE_PORT_UCHAR((PUCHAR)GRA_D, 0x00); + WRITE_PORT_UCHAR((PUCHAR)GRA_I, 0x08); // bit mask + + for (i=w; i>0; i--) + { + WRITE_PORT_UCHAR((PUCHAR)GRA_D, mask); + bp = bpX; + vp = vpX; + for (j = h; j > 0; j--) + { + //dummy = *vp; + *vp = *bp; + bp += bw; + vp += byte_per_line; + } + bpX++; + if ((mask >>= 1) == 0) + { + vpX++; + mask = 0x80; + } + } +} + +/* This algorithm goes from goes from left to right, and inside that loop, top to bottom. + * It also stores each 4BPP pixel in an entire byte. */ +void DFB_BltToVGA_Transparent(int x, int y, int w, int h, void *b, int bw, char Trans) +{ + unsigned char *bp, *bpX; + unsigned char *vp, *vpX; + unsigned char mask; + //volatile unsigned char dummy; + int byte_per_line; + int i, j; + + bpX = b; + ASSIGNVP4(x, y, vpX) + ASSIGNMK4(x, y, mask) + byte_per_line = SCREEN_X >> 3; + + WRITE_PORT_UCHAR((PUCHAR)GRA_I, 0x05); // write mode 2 + WRITE_PORT_UCHAR((PUCHAR)GRA_D, 0x02); + WRITE_PORT_UCHAR((PUCHAR)GRA_I, 0x03); // replace + WRITE_PORT_UCHAR((PUCHAR)GRA_D, 0x00); + WRITE_PORT_UCHAR((PUCHAR)GRA_I, 0x08); // bit mask + + for (i=w; i>0; i--) + { + WRITE_PORT_UCHAR((PUCHAR)GRA_D, mask); + bp = bpX; + vp = vpX; + for (j=h; j>0; j--) + { + if (*bp != Trans) + { + //dummy = *vp; + *vp = *bp; + } + bp += bw; + vp += byte_per_line; + } + bpX++; + if ((mask >>= 1) == 0) + { + vpX++; + mask = 0x80; + } + } +} + +/* This algorithm converts a DFB into a DIB + * WARNING: This algorithm is buggy */ +void DFB_BltToDIB(int x, int y, int w, int h, void *b, int bw, void *bdib, int dibw) +{ + unsigned char *bp, *bpX, *dib, *dibTmp; + int i, j, dib_shift; + + bpX = b; + dib = (unsigned char *)bdib + y * dibw + (x / 2); + + for (i=w; i>0; i--) + { + /* determine the bit shift for the DIB pixel */ + dib_shift = mod2(w-i); + if(dib_shift > 0) + dib_shift = 4; + dibTmp = dib; + + bp = bpX; + for (j = h; j > 0; j--) + { + *dibTmp = *bp << dib_shift | *(bp + 1); + dibTmp += dibw; + bp += bw; + } + bpX++; + if(dib_shift == 0) + dib++; + } +} + +/* This algorithm converts a DIB into a DFB */ +void DIB_BltToDFB(int x, int y, int w, int h, void *b, int bw, void *bdib, int dibw) +{ + unsigned char *bp, *bpX, *dib, *dibTmp; + int i, j, dib_shift, dib_and; + + bpX = b; + dib = (unsigned char *)bdib + y * dibw + (x / 2); + + for (i=w; i>0; i--) + { + /* determine the bit shift for the DIB pixel */ + dib_shift = mod2(w-i); + if(dib_shift > 0) + { + dib_shift = 0; + dib_and = 0x0f; + } + else + { + dib_shift = 4; + dib_and = 0xf0; + } + + dibTmp = dib; + bp = bpX; + + for (j=h; j>0; j--) + { + *bp = (*dibTmp & dib_and) >> dib_shift; + dibTmp += dibw; + bp += bw; + } + + bpX++; + if (dib_shift == 0) + dib++; + } +} diff --cc win32ss/gdi/dib/floodfill.c index a24ade73a3d,00000000000..13a0fed00e3 mode 100644,000000..100644 --- a/win32ss/gdi/dib/floodfill.c +++ b/win32ss/gdi/dib/floodfill.c @@@ -1,173 -1,0 +1,173 @@@ +/* +* COPYRIGHT: See COPYING in the top level directory +* PROJECT: ReactOS win32 subsystem +* PURPOSE: Flood filling support +* FILE: subsystems/win32/win32k/dib/floodfill.c +* PROGRAMMER: Gregor Schneider, +*/ + +#include + +#define NDEBUG +#include + +/* +* This floodfill algorithm is an iterative four neighbors version. It works with an internal stack like data structure. +* The stack is kept in an array, sized for the worst case scenario of having to add all pixels of the surface. +* This avoids having to allocate and free memory blocks all the time. The stack grows from the end of the array towards the start. +* All pixels are checked before being added, against belonging to the fill rule (FLOODFILLBORDER or FLOODFILLSURFACE) +* and the position in respect to the clip region. This guarantees all pixels lying on the stack belong to the filled surface. +* Further optimisations of the algorithm are possible. +*/ + +/* Floodfil helper structures and functions */ +typedef struct _floodItem +{ + ULONG x; + ULONG y; +} FLOODITEM; + +typedef struct _floodInfo +{ + ULONG floodLen; + FLOODITEM *floodStart; + FLOODITEM *floodData; +} FLOODINFO; + +static __inline BOOL initFlood(FLOODINFO *info, RECTL *DstRect) +{ + ULONG width = DstRect->right - DstRect->left; + ULONG height = DstRect->bottom - DstRect->top; + info->floodData = ExAllocatePoolWithTag(NonPagedPool, width * height * sizeof(FLOODITEM), TAG_DIB); + if (info->floodData == NULL) + { + return FALSE; + } + info->floodStart = info->floodData + (width * height); + DPRINT("Allocated flood stack from %p to %p\n", info->floodData, info->floodStart); + return TRUE; +} +static __inline VOID finalizeFlood(FLOODINFO *info) +{ + ExFreePoolWithTag(info->floodData, TAG_DIB); +} +static __inline VOID addItemFlood(FLOODINFO *info, + ULONG x, + ULONG y, + SURFOBJ *DstSurf, + RECTL *DstRect, + ULONG Color, + BOOL isSurf) +{ + if (RECTL_bPointInRect(DstRect,x,y)) + { - if (isSurf == TRUE && ++ if (isSurf && + DibFunctionsForBitmapFormat[DstSurf->iBitmapFormat].DIB_GetPixel(DstSurf, x, y) != Color) + { + return; + } + else if (isSurf == FALSE && + DibFunctionsForBitmapFormat[DstSurf->iBitmapFormat].DIB_GetPixel(DstSurf, x, y) == Color) + { + return; + } + info->floodStart--; + info->floodStart->x = x; + info->floodStart->y = y; + info->floodLen++; + } +} +static __inline VOID removeItemFlood(FLOODINFO *info) +{ + info->floodStart++; + info->floodLen--; +} + +BOOLEAN DIB_XXBPP_FloodFillSolid(SURFOBJ *DstSurf, + BRUSHOBJ *Brush, + RECTL *DstRect, + POINTL *Origin, + ULONG ConvColor, + UINT FillType) +{ + ULONG x, y; + ULONG BrushColor; + FLOODINFO flood = {0, NULL, NULL}; + + BrushColor = Brush->iSolidColor; + x = Origin->x; + y = Origin->y; + + if (FillType == FLOODFILLBORDER) + { + /* Check if the start pixel has the border color */ + if (DibFunctionsForBitmapFormat[DstSurf->iBitmapFormat].DIB_GetPixel(DstSurf, x, y) == ConvColor) + { + return FALSE; + } + + if (initFlood(&flood, DstRect) == FALSE) + { + return FALSE; + } + addItemFlood(&flood, x, y, DstSurf, DstRect, ConvColor, FALSE); + while (flood.floodLen != 0) + { + x = flood.floodStart->x; + y = flood.floodStart->y; + removeItemFlood(&flood); + + DibFunctionsForBitmapFormat[DstSurf->iBitmapFormat].DIB_PutPixel(DstSurf, x, y, BrushColor); + if (flood.floodStart - 4 < flood.floodData) + { + DPRINT1("Can't finish flooding!\n"); + finalizeFlood(&flood); + return FALSE; + } + addItemFlood(&flood, x, y + 1, DstSurf, DstRect, ConvColor, FALSE); + addItemFlood(&flood, x, y - 1, DstSurf, DstRect, ConvColor, FALSE); + addItemFlood(&flood, x + 1, y, DstSurf, DstRect, ConvColor, FALSE); + addItemFlood(&flood, x - 1, y, DstSurf, DstRect, ConvColor, FALSE); + } + finalizeFlood(&flood); + } + else if (FillType == FLOODFILLSURFACE) + { + /* Check if the start pixel has the surface color */ + if (DibFunctionsForBitmapFormat[DstSurf->iBitmapFormat].DIB_GetPixel(DstSurf, x, y) != ConvColor) + { + return FALSE; + } + + if (initFlood(&flood, DstRect) == FALSE) + { + return FALSE; + } + addItemFlood(&flood, x, y, DstSurf, DstRect, ConvColor, TRUE); + while (flood.floodLen != 0) + { + x = flood.floodStart->x; + y = flood.floodStart->y; + removeItemFlood(&flood); + + DibFunctionsForBitmapFormat[DstSurf->iBitmapFormat].DIB_PutPixel(DstSurf, x, y, BrushColor); + if (flood.floodStart - 4 < flood.floodData) + { + DPRINT1("Can't finish flooding!\n"); + finalizeFlood(&flood); + return FALSE; + } + addItemFlood(&flood, x, y + 1, DstSurf, DstRect, ConvColor, TRUE); + addItemFlood(&flood, x, y - 1, DstSurf, DstRect, ConvColor, TRUE); + addItemFlood(&flood, x + 1, y, DstSurf, DstRect, ConvColor, TRUE); + addItemFlood(&flood, x - 1, y, DstSurf, DstRect, ConvColor, TRUE); + } + finalizeFlood(&flood); + } + else + { + DPRINT1("Unsupported FloodFill type!\n"); + return FALSE; + } + return TRUE; +} diff --cc win32ss/gdi/gdi32/misc/gdientry.c index 8f37312bd37,00000000000..eb933dff41a mode 100644,000000..100644 --- a/win32ss/gdi/gdi32/misc/gdientry.c +++ b/win32ss/gdi/gdi32/misc/gdientry.c @@@ -1,2085 -1,0 +1,2085 @@@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS GDI32 + * PURPOSE: GDI DirectX inteface + * FILE: lib/gdi32/misc/gdientry.c + * PROGRAMERS: Alex Ionescu (alex@relsoft.net) + * Magnus Olsen (magnus@greatlord.com) + */ + +/* INCLUDES ******************************************************************/ + +#include + +#include +#include + +/* DATA **********************************************************************/ + +HANDLE ghDirectDraw; +ULONG gcDirectDraw; + +#define GetDdHandle(Handle) ((HANDLE)Handle ? (HANDLE)Handle : ghDirectDraw) + + + +/* CALLBACKS *****************************************************************/ + +/* + * @implemented + * + * DdAddAttachedSurface + */ +DWORD +WINAPI +DdAddAttachedSurface(LPDDHAL_ADDATTACHEDSURFACEDATA Attach) +{ + /* Call win32k */ + return NtGdiDdAddAttachedSurface((HANDLE)Attach->lpDDSurface->hDDSurface, + (HANDLE)Attach->lpSurfAttached->hDDSurface, + (PDD_ADDATTACHEDSURFACEDATA)Attach); +} + +/* + * @implemented + * + * DdBlt + */ +DWORD +WINAPI +DdBlt(LPDDHAL_BLTDATA Blt) +{ + HANDLE Surface = 0; + + /* Use the right surface */ + if (Blt->lpDDSrcSurface) + { + Surface = (HANDLE)Blt->lpDDSrcSurface->hDDSurface; + } + + /* Call win32k */ + return NtGdiDdBlt((HANDLE)Blt->lpDDDestSurface->hDDSurface, Surface, (PDD_BLTDATA)Blt); +} + +/* + * @implemented + * + * DdDestroySurface + */ +DWORD +WINAPI +DdDestroySurface(LPDDHAL_DESTROYSURFACEDATA pDestroySurface) +{ + DWORD Return = DDHAL_DRIVER_NOTHANDLED; + BOOL RealDestroy; + + if (pDestroySurface->lpDDSurface->hDDSurface) + { + /* Check if we shoudl really destroy it */ + RealDestroy = !(pDestroySurface->lpDDSurface->dwFlags & DDRAWISURF_DRIVERMANAGED) || + !(pDestroySurface->lpDDSurface->dwFlags & DDRAWISURF_INVALID); + + /* Call win32k */ + Return = NtGdiDdDestroySurface((HANDLE)pDestroySurface->lpDDSurface->hDDSurface, RealDestroy); + } + + return Return; +} + +/* + * @implemented + * + * DdFlip + */ +DWORD +WINAPI +DdFlip(LPDDHAL_FLIPDATA Flip) +{ + /* Note : + * See http://msdn2.microsoft.com/en-us/library/ms794213.aspx and + * http://msdn2.microsoft.com/en-us/library/ms792675.aspx + */ + + HANDLE hSurfaceCurrentLeft = NULL; + HANDLE hSurfaceTargetLeft = NULL; + + /* Auto flip off or on */ + if (Flip->dwFlags & DDFLIP_STEREO ) + { + if ( (Flip->lpSurfTargLeft) && + (Flip->lpSurfCurrLeft)) + { + /* Auto flip on */ + hSurfaceTargetLeft = (HANDLE) Flip->lpSurfTargLeft->hDDSurface; + hSurfaceCurrentLeft = (HANDLE) Flip->lpSurfCurrLeft->hDDSurface; + } + } + + /* Call win32k */ + return NtGdiDdFlip( (HANDLE) Flip->lpSurfCurr->hDDSurface, + (HANDLE) Flip->lpSurfTarg->hDDSurface, + hSurfaceCurrentLeft, + hSurfaceTargetLeft, + (PDD_FLIPDATA) Flip); +} + +/* + * @implemented + * + * DdLock + */ +DWORD +WINAPI +DdLock(LPDDHAL_LOCKDATA Lock) +{ + + /* Call win32k */ + return NtGdiDdLock((HANDLE)Lock->lpDDSurface->hDDSurface, + (PDD_LOCKDATA)Lock, + (HANDLE)Lock->lpDDSurface->hDC); +} + +/* + * @implemented + * + * DdUnlock + */ +DWORD +WINAPI +DdUnlock(LPDDHAL_UNLOCKDATA Unlock) +{ + /* Call win32k */ + return NtGdiDdUnlock((HANDLE)Unlock->lpDDSurface->hDDSurface, + (PDD_UNLOCKDATA)Unlock); +} + +/* + * @implemented + * + * DdGetBltStatus + */ +DWORD +WINAPI +DdGetBltStatus(LPDDHAL_GETBLTSTATUSDATA GetBltStatus) +{ + /* Call win32k */ + return NtGdiDdGetBltStatus((HANDLE)GetBltStatus->lpDDSurface->hDDSurface, + (PDD_GETBLTSTATUSDATA)GetBltStatus); +} + +/* + * @implemented + * + * DdGetBltStatus + */ +DWORD +WINAPI +DdGetFlipStatus(LPDDHAL_GETFLIPSTATUSDATA GetFlipStatus) +{ + /* Call win32k */ + return NtGdiDdGetFlipStatus((HANDLE)GetFlipStatus->lpDDSurface->hDDSurface, + (PDD_GETFLIPSTATUSDATA)GetFlipStatus); +} + +/* + * @implemented + * + * DdUpdateOverlay + */ +DWORD +WINAPI +DdUpdateOverlay(LPDDHAL_UPDATEOVERLAYDATA UpdateOverlay) +{ + + /* We have to handle this manually here */ + if (UpdateOverlay->dwFlags & DDOVER_KEYDEST) + { + /* Use the override */ + UpdateOverlay->dwFlags &= ~DDOVER_KEYDEST; + UpdateOverlay->dwFlags |= DDOVER_KEYDESTOVERRIDE; + + /* Set the overlay */ + UpdateOverlay->overlayFX.dckDestColorkey = + UpdateOverlay->lpDDDestSurface->ddckCKDestOverlay; + } + if (UpdateOverlay->dwFlags & DDOVER_KEYSRC) + { + /* Use the override */ + UpdateOverlay->dwFlags &= ~DDOVER_KEYSRC; + UpdateOverlay->dwFlags |= DDOVER_KEYSRCOVERRIDE; + + /* Set the overlay */ + UpdateOverlay->overlayFX.dckSrcColorkey = + UpdateOverlay->lpDDSrcSurface->ddckCKSrcOverlay; + } + + /* Call win32k */ + return NtGdiDdUpdateOverlay((HANDLE)UpdateOverlay->lpDDDestSurface->hDDSurface, + (HANDLE)UpdateOverlay->lpDDSrcSurface->hDDSurface, + (PDD_UPDATEOVERLAYDATA)UpdateOverlay); +} + +/* + * @implemented + * + * DdSetOverlayPosition + */ +DWORD +WINAPI +DdSetOverlayPosition(LPDDHAL_SETOVERLAYPOSITIONDATA SetOverlayPosition) +{ + /* Call win32k */ + return NtGdiDdSetOverlayPosition( (HANDLE)SetOverlayPosition->lpDDSrcSurface->hDDSurface, + (HANDLE)SetOverlayPosition->lpDDDestSurface->hDDSurface, + (PDD_SETOVERLAYPOSITIONDATA) SetOverlayPosition); +} + +/* + * @implemented + * + * DdWaitForVerticalBlank + */ +DWORD +WINAPI +DdWaitForVerticalBlank(LPDDHAL_WAITFORVERTICALBLANKDATA WaitForVerticalBlank) +{ + /* Call win32k */ + return NtGdiDdWaitForVerticalBlank(GetDdHandle( + WaitForVerticalBlank->lpDD->hDD), + (PDD_WAITFORVERTICALBLANKDATA) + WaitForVerticalBlank); +} + +/* + * @implemented + * + * DdCanCreateSurface + */ +DWORD +WINAPI +DdCanCreateSurface(LPDDHAL_CANCREATESURFACEDATA CanCreateSurface) +{ + /* + * Note : This functions are basic same, in win32k + * NtGdiDdCanCreateD3DBuffer and NtGdiDdCanCreateSurface are mergs + * toghter in win32k at end and retrurn same data, it is still sepreated + * at user mode but in kmode it is not. + */ + + /* Call win32k */ + return NtGdiDdCanCreateSurface(GetDdHandle(CanCreateSurface->lpDD->hDD), + (PDD_CANCREATESURFACEDATA)CanCreateSurface); +} + +/* + * @implemented + * + * DdCreateSurface + */ +DWORD +WINAPI +DdCreateSurface(LPDDHAL_CREATESURFACEDATA pCreateSurface) +{ + DWORD Return = DDHAL_DRIVER_NOTHANDLED; + ULONG SurfaceCount = pCreateSurface->dwSCnt; + DD_SURFACE_LOCAL DdSurfaceLocal; + DD_SURFACE_MORE DdSurfaceMore; + DD_SURFACE_GLOBAL DdSurfaceGlobal; + + HANDLE hPrevSurface, hSurface; + + PDD_SURFACE_LOCAL pDdSurfaceLocal = NULL; + PDD_SURFACE_MORE pDdSurfaceMore = NULL; + PDD_SURFACE_GLOBAL pDdSurfaceGlobal = NULL; + + PDD_SURFACE_LOCAL ptmpDdSurfaceLocal = NULL; + PDD_SURFACE_MORE ptmpDdSurfaceMore = NULL; + PDD_SURFACE_GLOBAL ptmpDdSurfaceGlobal = NULL; + PHANDLE phSurface = NULL, puhSurface = NULL; + ULONG i; + LPDDSURFACEDESC pSurfaceDesc = NULL; + + /* TODO: Optimize speed. Most games/dx apps/programs do not want one surface, they want at least two. + * So we need increase the stack to contain two surfaces instead of one. This will increase + * the speed of the apps when allocating buffers. How to increase the surface stack space: + * we need to create a struct for DD_SURFACE_LOCAL DdSurfaceLocal, DD_SURFACE_MORE DdSurfaceMore + * DD_SURFACE_GLOBAL DdSurfaceGlobal, HANDLE hPrevSurface, hSurface like + * struct { DD_SURFACE_LOCAL DdSurfaceLocal1, DD_SURFACE_LOCAL DdSurfaceLocal2 } + * in a way that it may contain two surfaces, maybe even four. We need to watch what is most common before + * we create the size. Activate this IF when you start doing the optimze and please also + * take reports from users which value they got here. + */ +#if 1 + { + char buffer[1024]; + \ + sprintf ( buffer, "Function %s : Optimze max to %d Surface ? (%s:%d)\n", __FUNCTION__, (int)SurfaceCount,__FILE__,__LINE__ ); + OutputDebugStringA(buffer); + } +#endif + + /* Check how many surfaces there are */ + if (SurfaceCount != 1) + { + /* We got more than one surface, so we need to allocate memory for them */ + pDdSurfaceLocal = (PDD_SURFACE_LOCAL) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (sizeof(DD_SURFACE_LOCAL) * SurfaceCount )); + pDdSurfaceMore = (PDD_SURFACE_MORE) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (sizeof(DD_SURFACE_MORE) * SurfaceCount )); + pDdSurfaceGlobal = (PDD_SURFACE_GLOBAL) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (sizeof(DD_SURFACE_GLOBAL) * SurfaceCount )); + phSurface = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (sizeof(HANDLE) * SurfaceCount )); + puhSurface = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (sizeof(HANDLE) * SurfaceCount )); + + /* Check if we successfully allocated all memory we need */ + if ((pDdSurfaceLocal == NULL) || (pDdSurfaceMore == NULL) || (pDdSurfaceGlobal == NULL) || (phSurface == NULL) || (puhSurface == NULL)) + { + pCreateSurface->ddRVal = DDERR_OUTOFMEMORY; + + if ( pDdSurfaceLocal != NULL ) + { + HeapFree(GetProcessHeap(), 0, pDdSurfaceLocal); + } + + if ( pDdSurfaceMore != NULL ) + { + HeapFree(GetProcessHeap(), 0, pDdSurfaceMore); + } + + if ( pDdSurfaceGlobal != NULL ) + { + HeapFree(GetProcessHeap(), 0, pDdSurfaceGlobal); + } + + if ( phSurface != NULL ) + { + HeapFree(GetProcessHeap(), 0, phSurface); + } + + if ( puhSurface != NULL ) + { + HeapFree(GetProcessHeap(), 0, puhSurface); + } + + return DDHAL_DRIVER_HANDLED; + } + } + else + { + /* We'll use what we have on the stack */ + pDdSurfaceLocal = &DdSurfaceLocal; + pDdSurfaceMore = &DdSurfaceMore; + pDdSurfaceGlobal = &DdSurfaceGlobal; + phSurface = &hPrevSurface; + puhSurface = &hSurface; + + /* Clear the structures */ + RtlZeroMemory(&DdSurfaceLocal, sizeof(DdSurfaceLocal)); + RtlZeroMemory(&DdSurfaceGlobal, sizeof(DdSurfaceGlobal)); + RtlZeroMemory(&DdSurfaceMore, sizeof(DdSurfaceMore)); + } + + /* check if we got a surface or not */ + if (SurfaceCount!=0) + { + /* Loop for each surface */ + ptmpDdSurfaceGlobal = pDdSurfaceGlobal; + ptmpDdSurfaceLocal = pDdSurfaceLocal; + ptmpDdSurfaceMore = pDdSurfaceMore; + pSurfaceDesc = pCreateSurface->lpDDSurfaceDesc; + + for (i = 0; i < SurfaceCount; i++) + { + LPDDRAWI_DDRAWSURFACE_LCL lcl = pCreateSurface->lplpSList[i]; + LPDDRAWI_DDRAWSURFACE_GBL gpl = pCreateSurface->lplpSList[i]->lpGbl; + + phSurface[i] = (HANDLE)lcl->hDDSurface; + ptmpDdSurfaceLocal->ddsCaps.dwCaps = lcl->ddsCaps.dwCaps; + + ptmpDdSurfaceLocal->dwFlags = (ptmpDdSurfaceLocal->dwFlags & + (0xB0000000 | DDRAWISURF_INMASTERSPRITELIST | + DDRAWISURF_HELCB | DDRAWISURF_FRONTBUFFER | + DDRAWISURF_BACKBUFFER | DDRAWISURF_INVALID | + DDRAWISURF_DCIBUSY | DDRAWISURF_DCILOCK)) | + (lcl->dwFlags & DDRAWISURF_DRIVERMANAGED); + + ptmpDdSurfaceGlobal->wWidth = gpl->wWidth; + ptmpDdSurfaceGlobal->wHeight = gpl->wHeight; + ptmpDdSurfaceGlobal->lPitch = gpl->lPitch; + ptmpDdSurfaceGlobal->fpVidMem = gpl->fpVidMem; + ptmpDdSurfaceGlobal->dwBlockSizeX = gpl->dwBlockSizeX; + ptmpDdSurfaceGlobal->dwBlockSizeY = gpl->dwBlockSizeY; + + if (lcl->dwFlags & DDRAWISURF_HASPIXELFORMAT) + { + RtlCopyMemory( &ptmpDdSurfaceGlobal->ddpfSurface , + &gpl->ddpfSurface, + sizeof(DDPIXELFORMAT)); + + ptmpDdSurfaceGlobal->ddpfSurface.dwSize = sizeof(DDPIXELFORMAT); + } + else + { + RtlCopyMemory( &ptmpDdSurfaceGlobal->ddpfSurface , + &gpl->lpDD->vmiData.ddpfDisplay, + sizeof(DDPIXELFORMAT)); + } + + /* Note if lcl->lpSurfMore is NULL zero out + * ptmpDdSurfaceMore->ddsCapsEx.dwCaps2, + * dwCaps3, dwCaps4, ptmpDdSurfaceMore->dwSurfaceHandle + */ + if (lcl->lpSurfMore) + { + ptmpDdSurfaceMore->ddsCapsEx.dwCaps2 = lcl->lpSurfMore->ddsCapsEx.dwCaps2; + ptmpDdSurfaceMore->ddsCapsEx.dwCaps3 = lcl->lpSurfMore->ddsCapsEx.dwCaps3; + ptmpDdSurfaceMore->ddsCapsEx.dwCaps4 = lcl->lpSurfMore->ddsCapsEx.dwCaps4; + ptmpDdSurfaceMore->dwSurfaceHandle = lcl->lpSurfMore->dwSurfaceHandle; + } + + + /* count to next SurfaceCount */ + ptmpDdSurfaceGlobal = (PDD_SURFACE_GLOBAL) (((PBYTE) ((ULONG_PTR) ptmpDdSurfaceGlobal)) + sizeof(DD_SURFACE_GLOBAL)); + ptmpDdSurfaceLocal = (PDD_SURFACE_LOCAL) (((PBYTE) ((ULONG_PTR) ptmpDdSurfaceLocal)) + sizeof(DD_SURFACE_LOCAL)); + ptmpDdSurfaceMore = (PDD_SURFACE_MORE) (((PBYTE) ((ULONG_PTR) ptmpDdSurfaceMore)) + sizeof(DD_SURFACE_MORE)); + } + } + + /* Call win32k now */ + pCreateSurface->ddRVal = DDERR_GENERIC; + + Return = NtGdiDdCreateSurface(GetDdHandle(pCreateSurface->lpDD->hDD), + (HANDLE *)phSurface, + pSurfaceDesc, + pDdSurfaceGlobal, + pDdSurfaceLocal, + pDdSurfaceMore, + (PDD_CREATESURFACEDATA)pCreateSurface, + puhSurface); + + if (SurfaceCount == 0) + { + pCreateSurface->ddRVal = DDERR_GENERIC; + } + else + { + ptmpDdSurfaceMore = pDdSurfaceMore; + ptmpDdSurfaceGlobal = pDdSurfaceGlobal; + ptmpDdSurfaceLocal = pDdSurfaceLocal; + + for (i=0; ilplpSList[i]; + LPDDRAWI_DDRAWSURFACE_GBL gpl = pCreateSurface->lplpSList[i]->lpGbl; + + gpl->lPitch = ptmpDdSurfaceGlobal->lPitch; + gpl->fpVidMem = ptmpDdSurfaceGlobal->fpVidMem; + gpl->dwBlockSizeX = ptmpDdSurfaceGlobal->dwBlockSizeX; + gpl->dwBlockSizeY = ptmpDdSurfaceGlobal->dwBlockSizeY; + + if (lcl->dwFlags & DDRAWISURF_HASPIXELFORMAT) + { + RtlCopyMemory( &gpl->ddpfSurface, &ptmpDdSurfaceGlobal->ddpfSurface , sizeof(DDPIXELFORMAT)); + } + + if (pCreateSurface->ddRVal != DD_OK) + { + gpl->fpVidMem = 0; + if (lcl->hDDSurface) + { + NtGdiDdDeleteSurfaceObject( (HANDLE)lcl->hDDSurface); + } + lcl->hDDSurface = 0; + } + else + { + + lcl->hDDSurface = (ULONG_PTR) puhSurface[i]; + } + + lcl->ddsCaps.dwCaps = ptmpDdSurfaceLocal->ddsCaps.dwCaps; + if (lcl->lpSurfMore) + { + lcl->lpSurfMore->ddsCapsEx.dwCaps2 = ptmpDdSurfaceMore->ddsCapsEx.dwCaps2; + lcl->lpSurfMore->ddsCapsEx.dwCaps3 = ptmpDdSurfaceMore->ddsCapsEx.dwCaps3; + lcl->lpSurfMore->ddsCapsEx.dwCaps4 = ptmpDdSurfaceMore->ddsCapsEx.dwCaps4; + } + + /* count to next SurfaceCount */ + ptmpDdSurfaceGlobal = (PDD_SURFACE_GLOBAL) (((PBYTE) ((ULONG_PTR) ptmpDdSurfaceGlobal)) + sizeof(DD_SURFACE_GLOBAL)); + ptmpDdSurfaceLocal = (PDD_SURFACE_LOCAL) (((PBYTE) ((ULONG_PTR) ptmpDdSurfaceLocal)) + sizeof(DD_SURFACE_LOCAL)); + ptmpDdSurfaceMore = (PDD_SURFACE_MORE) (((PBYTE) ((ULONG_PTR) ptmpDdSurfaceMore)) + sizeof(DD_SURFACE_MORE)); + } + } + + /* Check if we have to free all our local allocations */ + if (SurfaceCount > 1) + { + if ( pDdSurfaceLocal != NULL ) + { + HeapFree(GetProcessHeap(), 0, pDdSurfaceLocal); + } + + if ( pDdSurfaceMore != NULL ) + { + HeapFree(GetProcessHeap(), 0, pDdSurfaceMore); + } + + if ( pDdSurfaceGlobal != NULL ) + { + HeapFree(GetProcessHeap(), 0, pDdSurfaceGlobal); + } + + if ( phSurface != NULL ) + { + HeapFree(GetProcessHeap(), 0, phSurface); + } + + if ( puhSurface != NULL ) + { + HeapFree(GetProcessHeap(), 0, puhSurface); + } + } + + /* Return */ + return Return; +} + +/* + * @implemented + * + * DdSetColorKey + */ +DWORD +WINAPI +DdSetColorKey(LPDDHAL_SETCOLORKEYDATA pSetColorKey) +{ + /* Call win32k */ + return NtGdiDdSetColorKey((HANDLE)pSetColorKey->lpDDSurface->hDDSurface, + (PDD_SETCOLORKEYDATA)pSetColorKey); +} + +/* + * @implemented + * + * DdGetScanLine + */ +DWORD +WINAPI +DdGetScanLine(LPDDHAL_GETSCANLINEDATA pGetScanLine) +{ + /* Call win32k */ + return NtGdiDdGetScanLine(GetDdHandle(pGetScanLine->lpDD->hDD), + (PDD_GETSCANLINEDATA)pGetScanLine); +} + + +/* + * @implemented + * + * DvpCreateVideoPort + */ +BOOL +WINAPI +DvpCreateVideoPort(LPDDHAL_CREATEVPORTDATA pDvdCreatePort) +{ + pDvdCreatePort->lpVideoPort->hDDVideoPort = + NtGdiDvpCreateVideoPort(GetDdHandle(pDvdCreatePort->lpDD->lpGbl->hDD), + (PDD_CREATEVPORTDATA) pDvdCreatePort); + + return TRUE; +} + +/* + * @implemented + * + * DvpCreateVideoPort + */ +DWORD +WINAPI +DvpDestroyVideoPort(LPDDHAL_DESTROYVPORTDATA pDvdDestoryPort) +{ + return NtGdiDvpDestroyVideoPort(pDvdDestoryPort->lpVideoPort->hDDVideoPort, (PDD_DESTROYVPORTDATA)pDvdDestoryPort); +} + +/* + * @implemented + * + * DvpCreateVideoPort + */ +DWORD +WINAPI +DvpFlipVideoPort(LPDDHAL_FLIPVPORTDATA pDvdPortFlip) +{ + return NtGdiDvpFlipVideoPort(pDvdPortFlip->lpVideoPort->hDDVideoPort, + (HANDLE)pDvdPortFlip->lpSurfCurr->hDDSurface, + (HANDLE)pDvdPortFlip->lpSurfTarg->hDDSurface, + (PDD_FLIPVPORTDATA) pDvdPortFlip); +} + +/* + * @implemented + * + * DvpGetVideoPortBandwidth + */ +DWORD +WINAPI +DvpGetVideoPortBandwidth(LPDDHAL_GETVPORTBANDWIDTHDATA pDvdPortBandWidth) +{ + return NtGdiDvpGetVideoPortBandwidth(pDvdPortBandWidth->lpVideoPort->hDDVideoPort, (PDD_GETVPORTBANDWIDTHDATA)pDvdPortBandWidth); +} + +/* + * @implemented + * + * DvpColorControl + */ +DWORD +WINAPI +DvpColorControl(LPDDHAL_VPORTCOLORDATA pDvdPortColorControl) +{ + return NtGdiDvpColorControl(pDvdPortColorControl->lpVideoPort->hDDVideoPort, (PDD_VPORTCOLORDATA) pDvdPortColorControl); +} + +/* + * @implemented + * + * DvpGetVideoSignalStatus + */ +DWORD +WINAPI +DvpGetVideoSignalStatus(LPDDHAL_GETVPORTSIGNALDATA pDvdPortVideoSignalStatus) +{ + return NtGdiDvpGetVideoSignalStatus(pDvdPortVideoSignalStatus->lpVideoPort->hDDVideoPort, (PDD_GETVPORTSIGNALDATA) pDvdPortVideoSignalStatus); +} + +/* + * @implemented + * + * DvpGetVideoPortFlipStatus + */ +DWORD +WINAPI +DvpGetVideoPortFlipStatus(LPDDHAL_GETVPORTFLIPSTATUSDATA pDvdPortVideoPortFlipStatus) +{ + return NtGdiDvpGetVideoPortFlipStatus(GetDdHandle(pDvdPortVideoPortFlipStatus->lpDD->lpGbl->hDD), (PDD_GETVPORTFLIPSTATUSDATA) pDvdPortVideoPortFlipStatus); + +} + +/* + * @implemented + * + * DvpCanCreateVideoPort + */ +DWORD +WINAPI +DvpCanCreateVideoPort(LPDDHAL_CANCREATEVPORTDATA pDvdCanCreateVideoPort) +{ + return NtGdiDvpCanCreateVideoPort(GetDdHandle(pDvdCanCreateVideoPort->lpDD->lpGbl->hDD), (PDD_CANCREATEVPORTDATA) pDvdCanCreateVideoPort); +} +/* + * @implemented + * + * DvpWaitForVideoPortSync + */ +DWORD +WINAPI +DvpWaitForVideoPortSync(LPDDHAL_WAITFORVPORTSYNCDATA pDvdWaitForVideoPortSync) +{ + return NtGdiDvpWaitForVideoPortSync(pDvdWaitForVideoPortSync->lpVideoPort->hDDVideoPort, (PDD_WAITFORVPORTSYNCDATA) pDvdWaitForVideoPortSync); +} + +/* + * @implemented + * + * DvpUpdateVideoPort + */ +DWORD +WINAPI +DvpUpdateVideoPort(LPDDHAL_UPDATEVPORTDATA pDvdUpdateVideoPort) +{ + /* + * Windows XP limit to max 10 handles of videoport surface and Vbi + * ReactOS doing same to keep compatible, if it is more that 10 + * videoport surface or vbi the stack will be curpted in windows xp + * ReactOS safe guard againts that + * + */ + + HANDLE phSurfaceVideo[10]; + HANDLE phSurfaceVbi[10]; + + if (pDvdUpdateVideoPort->dwFlags != DDRAWI_VPORTSTOP) + { + DWORD dwNumAutoflip; + DWORD dwNumVBIAutoflip; + + /* Take copy of lplpDDSurface for the handle value will be modify in dxg */ + dwNumAutoflip = pDvdUpdateVideoPort->dwNumAutoflip; + if ((dwNumAutoflip == 0) && + (pDvdUpdateVideoPort->lplpDDSurface == 0)) + { + dwNumAutoflip++; + } + + if (dwNumAutoflip != 0) + { + if (dwNumAutoflip>10) + { + dwNumAutoflip = 10; + } + memcpy(phSurfaceVideo,pDvdUpdateVideoPort->lplpDDSurface,dwNumAutoflip*sizeof(HANDLE)); + } + + /* Take copy of lplpDDVBISurface for the handle value will be modify in dxg */ + dwNumVBIAutoflip = pDvdUpdateVideoPort->dwNumVBIAutoflip; + if ( (dwNumVBIAutoflip == 0) && + (pDvdUpdateVideoPort->lplpDDVBISurface == 0) ) + { + dwNumVBIAutoflip++; + } + + if (dwNumVBIAutoflip != 0) + { + if (dwNumVBIAutoflip>10) + { + dwNumVBIAutoflip = 10; + } + memcpy(phSurfaceVbi,pDvdUpdateVideoPort->lplpDDVBISurface,dwNumVBIAutoflip*sizeof(HANDLE)); + } + } + + /* Call Win32k */ + return NtGdiDvpUpdateVideoPort(pDvdUpdateVideoPort->lpVideoPort->hDDVideoPort,phSurfaceVideo,phSurfaceVbi, (PDD_UPDATEVPORTDATA)pDvdUpdateVideoPort); +} + +/* + * @implemented + * + * DvpWaitForVideoPortSync + */ +DWORD +WINAPI +DvpGetVideoPortField(LPDDHAL_FLIPVPORTDATA pDvdGetVideoPortField) +{ + return NtGdiDvpGetVideoPortField(pDvdGetVideoPortField->lpVideoPort->hDDVideoPort, (PDD_GETVPORTFIELDDATA)pDvdGetVideoPortField); +} + +/* + * @implemented + * + * DvpWaitForVideoPortSync + */ +DWORD +WINAPI +DvpGetVideoPortInputFormats(LPDDHAL_GETVPORTINPUTFORMATDATA pDvdGetVideoPortInputFormat) +{ + return NtGdiDvpGetVideoPortInputFormats(pDvdGetVideoPortInputFormat->lpVideoPort->hDDVideoPort, (PDD_GETVPORTINPUTFORMATDATA) pDvdGetVideoPortInputFormat); +} + +/* + * @implemented + * + * DvpGetVideoPortLine + */ +DWORD +WINAPI +DvpGetVideoPortLine(LPDDHAL_GETVPORTLINEDATA pDvdGetVideoPortLine) +{ + return NtGdiDvpGetVideoPortLine(pDvdGetVideoPortLine->lpVideoPort->hDDVideoPort, (PDD_GETVPORTLINEDATA)pDvdGetVideoPortLine); +} + +/* + * @implemented + * + * DvpGetVideoPortOutputFormats + */ +DWORD +WINAPI +DvpGetVideoPortOutputFormats(LPDDHAL_GETVPORTLINEDATA pDvdGetVideoPortOutputFormat) +{ + return NtGdiDvpGetVideoPortLine(pDvdGetVideoPortOutputFormat->lpVideoPort->hDDVideoPort, (PDD_GETVPORTLINEDATA)pDvdGetVideoPortOutputFormat); +} + +/* + * @implemented + * + * DvpGetVideoPortConnectInfo + */ +DWORD +WINAPI +DvpGetVideoPortConnectInfo(LPDDHAL_GETVPORTCONNECTDATA pDvdGetVideoPortInfo) +{ + return NtGdiDvpGetVideoPortConnectInfo( GetDdHandle( pDvdGetVideoPortInfo->lpDD->lpGbl->hDD) , (PDD_GETVPORTCONNECTDATA) pDvdGetVideoPortInfo); +} + +/* + * @implemented + * + * DdGetAvailDriverMemory + */ +DWORD +WINAPI +DdGetAvailDriverMemory(LPDDHAL_GETAVAILDRIVERMEMORYDATA pDdGetAvailDriverMemory) +{ + return NtGdiDdGetAvailDriverMemory(GetDdHandle( pDdGetAvailDriverMemory->lpDD->hDD), (PDD_GETAVAILDRIVERMEMORYDATA) pDdGetAvailDriverMemory); +} + +/* + * @implemented + * + * DdAlphaBlt + */ +DWORD +WINAPI +DdAlphaBlt(LPDDHAL_BLTDATA pDdAlphaBlt) +{ + HANDLE hDDSrcSurface = 0; + + if (pDdAlphaBlt->lpDDSrcSurface != 0) + { + hDDSrcSurface = (HANDLE) pDdAlphaBlt->lpDDSrcSurface->hDDSurface; + } + + return NtGdiDdAlphaBlt((HANDLE)pDdAlphaBlt->lpDDDestSurface->hDDSurface, hDDSrcSurface, (PDD_BLTDATA)&pDdAlphaBlt); +} + +/* + * @implemented + * + * DdCreateSurfaceEx + */ +DWORD +WINAPI +DdCreateSurfaceEx(LPDDHAL_CREATESURFACEEXDATA pDdCreateSurfaceEx) +{ + pDdCreateSurfaceEx->ddRVal = NtGdiDdCreateSurfaceEx( GetDdHandle(pDdCreateSurfaceEx->lpDDLcl->lpGbl->hDD), + (HANDLE)pDdCreateSurfaceEx->lpDDSLcl->hDDSurface, + pDdCreateSurfaceEx->lpDDSLcl->lpSurfMore->dwSurfaceHandle); + return TRUE; +} + +/* + * @implemented + * + * DdColorControl + */ +DWORD +WINAPI +DdColorControl(LPDDHAL_COLORCONTROLDATA pDdColorControl) +{ + return NtGdiDdColorControl( (HANDLE) pDdColorControl->lpDDSurface->hDDSurface, (PDD_COLORCONTROLDATA) &pDdColorControl); +} + +/* + * @implemented + * + * DdSetExclusiveMode + */ +DWORD +WINAPI +DdSetExclusiveMode(LPDDHAL_SETEXCLUSIVEMODEDATA pDdSetExclusiveMode) +{ + return NtGdiDdSetExclusiveMode( GetDdHandle(pDdSetExclusiveMode->lpDD->hDD), (PDD_SETEXCLUSIVEMODEDATA) &pDdSetExclusiveMode); +} + +/* + * @implemented + * + * DdFlipToGDISurface + */ +DWORD +WINAPI +DdFlipToGDISurface(LPDDHAL_FLIPTOGDISURFACEDATA pDdFlipToGDISurface) +{ + return NtGdiDdFlipToGDISurface( GetDdHandle(pDdFlipToGDISurface->lpDD->hDD), (PDD_FLIPTOGDISURFACEDATA) &pDdFlipToGDISurface); +} + +/* TODO */ +DWORD +WINAPI +DdGetDriverInfo(LPDDHAL_GETDRIVERINFODATA pData) +{ + DDHAL_GETDRIVERINFODATA pDrvInfoData; + DWORD retValue = DDHAL_DRIVER_NOTHANDLED; + HANDLE hDD; + + /* FIXME add SEH around this functions */ + + RtlZeroMemory(&pDrvInfoData, sizeof (DDHAL_GETDRIVERINFODATA)); + RtlCopyMemory(&pDrvInfoData.guidInfo, &pData->guidInfo, sizeof(GUID)); + + hDD = GetDdHandle(pData->dwContext); + + pDrvInfoData.dwSize = sizeof (DDHAL_GETDRIVERINFODATA); + pDrvInfoData.ddRVal = DDERR_GENERIC; + pDrvInfoData.dwContext = (ULONG_PTR)hDD; + + + /* Videoport Callbacks check and setup for DirectX/ ReactX */ + if (IsEqualGUID(&pData->guidInfo, &GUID_VideoPortCallbacks)) + { + DDHAL_DDVIDEOPORTCALLBACKS pDvdPort; + DDHAL_DDVIDEOPORTCALLBACKS* pUserDvdPort = (DDHAL_DDVIDEOPORTCALLBACKS *)pData->lpvData; + + /* Clear internal out buffer and set it up*/ + RtlZeroMemory(&pDvdPort, DDVIDEOPORTCALLBACKSSIZE); + pDvdPort.dwSize = DDVIDEOPORTCALLBACKSSIZE; + + /* set up internal buffer */ + pDrvInfoData.lpvData = (PVOID)&pDvdPort; + pDrvInfoData.dwExpectedSize = DDVIDEOPORTCALLBACKSSIZE ; + + /* Call win32k */ + retValue = NtGdiDdGetDriverInfo(hDD, (PDD_GETDRIVERINFODATA)&pDrvInfoData); + + /* Setup user out buffer and convert kmode callbacks to user mode */ + pUserDvdPort->dwSize = DDVIDEOPORTCALLBACKSSIZE; + pUserDvdPort->dwFlags = pDrvInfoData.dwFlags = 0; + + pUserDvdPort->dwFlags = (pDrvInfoData.dwFlags & ~(DDHAL_VPORT32_CREATEVIDEOPORT | DDHAL_VPORT32_FLIP | + DDHAL_VPORT32_DESTROY | DDHAL_VPORT32_UPDATE | DDHAL_VPORT32_WAITFORSYNC)) | + (DDHAL_VPORT32_CREATEVIDEOPORT | DDHAL_VPORT32_FLIP | + DDHAL_VPORT32_DESTROY | DDHAL_VPORT32_UPDATE); + + pData->dwActualSize = DDVIDEOPORTCALLBACKSSIZE; + pUserDvdPort->CreateVideoPort = (LPDDHALVPORTCB_CREATEVIDEOPORT) DvpCreateVideoPort; + pUserDvdPort->FlipVideoPort = (LPDDHALVPORTCB_FLIP) DvpFlipVideoPort; + pUserDvdPort->DestroyVideoPort = (LPDDHALVPORTCB_DESTROYVPORT) DvpDestroyVideoPort; + pUserDvdPort->UpdateVideoPort = (LPDDHALVPORTCB_UPDATE) DvpUpdateVideoPort; + + if (pDvdPort.CanCreateVideoPort) + { + pUserDvdPort->CanCreateVideoPort = (LPDDHALVPORTCB_CANCREATEVIDEOPORT) DvpCanCreateVideoPort; + } + + if (pDvdPort.GetVideoPortBandwidth) + { + pUserDvdPort->GetVideoPortBandwidth = (LPDDHALVPORTCB_GETBANDWIDTH) DvpGetVideoPortBandwidth; + } + + if (pDvdPort.GetVideoPortInputFormats) + { + pUserDvdPort->GetVideoPortInputFormats = (LPDDHALVPORTCB_GETINPUTFORMATS) DvpGetVideoPortInputFormats; + } + + if (pDvdPort.GetVideoPortOutputFormats) + { + pUserDvdPort->GetVideoPortOutputFormats = (LPDDHALVPORTCB_GETOUTPUTFORMATS) DvpGetVideoPortOutputFormats; + } + + if (pDvdPort.GetVideoPortField) + { + pUserDvdPort->GetVideoPortField = (LPDDHALVPORTCB_GETFIELD) DvpGetVideoPortField; + } + + if (pDvdPort.GetVideoPortLine) + { + pUserDvdPort->GetVideoPortLine = (LPDDHALVPORTCB_GETLINE) DvpGetVideoPortLine; + } + + if (pDvdPort.GetVideoPortConnectInfo) + { + pUserDvdPort->GetVideoPortConnectInfo = (LPDDHALVPORTCB_GETVPORTCONNECT) DvpGetVideoPortConnectInfo; + } + + if (pDvdPort.GetVideoPortFlipStatus) + { + pUserDvdPort->GetVideoPortFlipStatus = (LPDDHALVPORTCB_GETFLIPSTATUS) DvpGetVideoPortFlipStatus; + } + + if (pDvdPort.WaitForVideoPortSync) + { + pUserDvdPort->WaitForVideoPortSync = (LPDDHALVPORTCB_WAITFORSYNC) DvpWaitForVideoPortSync; + } + + if (pDvdPort.GetVideoSignalStatus) + { + pUserDvdPort->GetVideoSignalStatus = (LPDDHALVPORTCB_GETSIGNALSTATUS) DvpGetVideoSignalStatus; + } + + if (pDvdPort.ColorControl) + { + pUserDvdPort->ColorControl = (LPDDHALVPORTCB_COLORCONTROL) DvpColorControl; + } + + /* Windows XP never repot back the true return value, + * it only report back if we have a driver or not + * ReactOS keep this behoir to be compatible with + * Windows XP + */ + pData->ddRVal = retValue; + } + + /* Color Control Callbacks check and setup for DirectX/ ReactX */ + if (IsEqualGUID(&pData->guidInfo, &GUID_ColorControlCallbacks)) + { + DDHAL_DDCOLORCONTROLCALLBACKS pColorControl; + DDHAL_DDCOLORCONTROLCALLBACKS* pUserColorControl = (DDHAL_DDCOLORCONTROLCALLBACKS *)pData->lpvData; + + /* Clear internal out buffer and set it up*/ + RtlZeroMemory(&pColorControl, DDCOLORCONTROLCALLBACKSSIZE); + pColorControl.dwSize = DDCOLORCONTROLCALLBACKSSIZE; + + /* set up internal buffer */ + pDrvInfoData.lpvData = (PVOID)&pColorControl; + pDrvInfoData.dwExpectedSize = DDCOLORCONTROLCALLBACKSSIZE ; + + /* Call win32k */ + retValue = NtGdiDdGetDriverInfo(hDD, (PDD_GETDRIVERINFODATA)&pDrvInfoData); + + pData->dwActualSize = DDCOLORCONTROLCALLBACKSSIZE; + pData->dwFlags = pDrvInfoData.dwFlags; + + pUserColorControl->dwSize = DDCOLORCONTROLCALLBACKSSIZE; + pUserColorControl->dwFlags = pColorControl.dwFlags; + + if (pColorControl.ColorControl != NULL) + { + pUserColorControl->ColorControl = (LPDDHALCOLORCB_COLORCONTROL) DdColorControl; + } + + /* Windows XP never repot back the true return value, + * it only report back if we have a driver or not + * ReactOS keep this behoir to be compatible with + * Windows XP + */ + pData->ddRVal = retValue; + } + + /* Misc Callbacks check and setup for DirectX/ ReactX */ + else if (IsEqualGUID(&pData->guidInfo, &GUID_MiscellaneousCallbacks)) + { + DDHAL_DDMISCELLANEOUSCALLBACKS pMisc; + DDHAL_DDMISCELLANEOUSCALLBACKS* pUserMisc = (DDHAL_DDMISCELLANEOUSCALLBACKS *)pData->lpvData; + + /* Clear internal out buffer and set it up*/ + RtlZeroMemory(&pMisc, DDMISCELLANEOUSCALLBACKSSIZE); + pMisc.dwSize = DDMISCELLANEOUSCALLBACKSSIZE; + + /* set up internal buffer */ + pDrvInfoData.lpvData = (PVOID)&pMisc; + pDrvInfoData.dwExpectedSize = DDMISCELLANEOUSCALLBACKSSIZE ; + + /* Call win32k */ + retValue = NtGdiDdGetDriverInfo(hDD, (PDD_GETDRIVERINFODATA)&pDrvInfoData); + + pData->dwActualSize = DDMISCELLANEOUSCALLBACKSSIZE; + + /* Only one callbacks are supported */ + pUserMisc->dwFlags = pMisc.dwFlags & DDHAL_MISCCB32_GETAVAILDRIVERMEMORY; + pUserMisc->GetAvailDriverMemory = (LPDDHAL_GETAVAILDRIVERMEMORY) DdGetAvailDriverMemory; + + /* This callbacks are only for win9x and theirfor it is not longer use in NT or ReactOS + * pUserMisc->UpdateNonLocalHeap; + * pUserMisc->GetHeapAlignment; + * pUserMisc->GetSysmemBltStatus; */ + + /* Windows XP never repot back the true return value, + * it only report back if we have a driver or not + * ReactOS keep this behoir to be compatible with + * Windows XP + */ + pData->ddRVal = retValue; + } + + /* Misc 2 Callbacks check and setup for DirectX/ ReactX */ + else if (IsEqualGUID(&pData->guidInfo, &GUID_Miscellaneous2Callbacks)) + { + DDHAL_DDMISCELLANEOUS2CALLBACKS pMisc; + DDHAL_DDMISCELLANEOUS2CALLBACKS* pUserMisc = (DDHAL_DDMISCELLANEOUS2CALLBACKS *)pData->lpvData; + + /* Clear internal out buffer and set it up*/ + RtlZeroMemory(&pMisc, DDMISCELLANEOUS2CALLBACKSSIZE); + pMisc.dwSize = DDMISCELLANEOUS2CALLBACKSSIZE; + + /* set up internal buffer */ + pDrvInfoData.lpvData = (PVOID)&pMisc; + pDrvInfoData.dwExpectedSize = DDMISCELLANEOUS2CALLBACKSSIZE ; + + /* Call win32k */ + retValue = NtGdiDdGetDriverInfo(hDD, (PDD_GETDRIVERINFODATA)&pDrvInfoData); + + pData->dwActualSize = DDMISCELLANEOUS2CALLBACKSSIZE; + + pUserMisc->dwFlags = pMisc.dwFlags; + + /* This functions are not documneted in MSDN for this struct, here is directx/reactx alpha blend */ + if ( pMisc.Reserved ) + { + pUserMisc->Reserved = (LPVOID) DdAlphaBlt; + } + + if ( pMisc.CreateSurfaceEx ) + { + pUserMisc->CreateSurfaceEx = (LPDDHAL_CREATESURFACEEX) DdCreateSurfaceEx; + } + + if ( pMisc.GetDriverState ) + { + pUserMisc->GetDriverState = (LPDDHAL_GETDRIVERSTATE) NtGdiDdGetDriverState; + } + + /* NOTE : pUserMisc->DestroyDDLocal is outdated and are not beign tuch */ + + /* Windows XP never repot back the true return value, + * it only report back if we have a driver or not + * ReactOS keep this behoir to be compatible with + * Windows XP + */ + pData->ddRVal = retValue; + } + + /* NT Callbacks check and setup for DirectX/ ReactX */ + else if (IsEqualGUID(&pData->guidInfo, &GUID_NTCallbacks)) + { + /* MS does not have DHAL_* version of this callbacks + * so we are force using PDD_* callbacks here + */ + DD_NTCALLBACKS pNtKernel; + PDD_NTCALLBACKS pUserNtKernel = (PDD_NTCALLBACKS)pData->lpvData; + + /* Clear internal out buffer and set it up*/ + RtlZeroMemory(&pNtKernel, sizeof(DD_NTCALLBACKS)); + pNtKernel.dwSize = sizeof(DD_NTCALLBACKS); + + /* set up internal buffer */ + pDrvInfoData.lpvData = (PVOID)&pNtKernel; + pDrvInfoData.dwExpectedSize = sizeof(DD_NTCALLBACKS) ; + + /* Call win32k */ + retValue = NtGdiDdGetDriverInfo(hDD, (PDD_GETDRIVERINFODATA)&pDrvInfoData); + + pData->dwActualSize = sizeof(DD_NTCALLBACKS); + + pUserNtKernel->dwSize = sizeof(DD_NTCALLBACKS); + pUserNtKernel->dwFlags = pNtKernel.dwFlags; + pUserNtKernel->FreeDriverMemory = 0; + + if (pNtKernel.SetExclusiveMode) + { + pUserNtKernel->SetExclusiveMode = (PDD_SETEXCLUSIVEMODE) DdSetExclusiveMode; + } + + if (pNtKernel.FlipToGDISurface) + { + pUserNtKernel->FlipToGDISurface = (PDD_FLIPTOGDISURFACE) DdFlipToGDISurface; + } + + /* Windows XP never repot back the true return value, + * it only report back if we have a driver or not + * ReactOS keep this behoir to be compatible with + * Windows XP + */ + pData->ddRVal = retValue; + } + + /* D3D Callbacks version 2 check and setup for DirectX/ ReactX */ + else if (IsEqualGUID(&pData->guidInfo, &GUID_D3DCallbacks2)) + { + // FIXME GUID_D3DCallbacks2 + } + + /* D3D Callbacks version 3 check and setup for DirectX/ ReactX */ + else if (IsEqualGUID(&pData->guidInfo, &GUID_D3DCallbacks3)) + { + // FIXME GUID_D3DCallbacks3 + } + + /* D3DParseUnknownCommand Callbacks check and setup for DirectX/ ReactX */ + else if (IsEqualGUID(&pData->guidInfo, &GUID_D3DParseUnknownCommandCallback)) + { + // FIXME GUID_D3DParseUnknownCommandCallback + } + + /* MotionComp Callbacks check and setup for DirectX/ ReactX */ + else if (IsEqualGUID(&pData->guidInfo, &GUID_MotionCompCallbacks)) + { + // FIXME GUID_MotionCompCallbacks + } + + /* FIXME VPE2 Callbacks check and setup for DirectX/ ReactX */ + //else if (IsEqualGUID(&pData->guidInfo, &GUID_VPE2Callbacks)) + //{ + // FIXME GUID_VPE2Callbacks + //} + else + { + /* set up internal buffer */ + pDrvInfoData.dwExpectedSize = pData->dwExpectedSize; + pDrvInfoData.lpvData = pData->lpvData; + + /* We do not cover all callbacks for user mode, they are only cover by kmode */ + retValue = NtGdiDdGetDriverInfo(hDD, (PDD_GETDRIVERINFODATA)&pDrvInfoData); + + /* Setup return data */ + pData->dwActualSize = pDrvInfoData.dwActualSize; + pData->lpvData = pDrvInfoData.lpvData; + /* Windows XP never repot back the true return value, + * it only report back if we have a driver or not + * ReactOS keep this behoir to be compatible with + * Windows XP + */ + pData->ddRVal = retValue; + } + + return retValue; +} + + +/* + * @implemented + * + * D3dContextCreate + */ +BOOL +WINAPI +D3dContextCreate(LPD3DHAL_CONTEXTCREATEDATA pdcci) +{ + HANDLE hSurfZ = NULL; + + if (pdcci->lpDDSZLcl) + { + hSurfZ = (HANDLE)pdcci->lpDDSZLcl->hDDSurface; + } + + return NtGdiD3dContextCreate(GetDdHandle(pdcci->lpDDLcl->hDD), + (HANDLE)pdcci->lpDDSLcl->hDDSurface, + hSurfZ, + (D3DNTHAL_CONTEXTCREATEI *)pdcci); +} + +/* + * @implemented + * + * DdCanCreateD3DBuffer + */ +DWORD +WINAPI +DdCanCreateD3DBuffer(LPDDHAL_CANCREATESURFACEDATA CanCreateD3DBuffer) +{ + /* + * Note : This functions are basic same, in win32k + * NtGdiDdCanCreateD3DBuffer and NtGdiDdCanCreateSurface are mergs + * toghter in win32k at end and retrurn same data, it is still sepreated + * at user mode but in kmode it is not. + */ + + /* Call win32k */ + return NtGdiDdCanCreateD3DBuffer(GetDdHandle(CanCreateD3DBuffer->lpDD->hDD), + (PDD_CANCREATESURFACEDATA)CanCreateD3DBuffer); +} + + +/* + * @implemented + * + * DdCreateD3DBuffer + */ +DWORD +WINAPI +DdCreateD3DBuffer(LPDDHAL_CREATESURFACEDATA pCreateSurface) +{ + HANDLE puhSurface = 0; + DDRAWI_DDRAWSURFACE_GBL *pSurfGBL; + DDRAWI_DDRAWSURFACE_LCL *pSurfLcl; + DD_SURFACE_GLOBAL puSurfaceGlobalData; + DD_SURFACE_MORE puSurfaceMoreData; + DD_SURFACE_LOCAL puSurfaceLocalData; + DWORD retValue; + + /* Zero all local memory pointer */ + RtlZeroMemory(&puSurfaceGlobalData, sizeof(DD_SURFACE_GLOBAL) ); + RtlZeroMemory(&puSurfaceMoreData, sizeof(DD_SURFACE_MORE) ) ; + RtlZeroMemory(&puSurfaceLocalData, sizeof(DD_SURFACE_LOCAL) ); + + pCreateSurface->dwSCnt = 1; + pSurfLcl = pCreateSurface->lplpSList[0]; + pSurfGBL = pSurfLcl->lpGbl; + + /* Convert DDRAWI_DDRAWSURFACE_GBL to DD_SURFACE_GLOBAL */ + puSurfaceGlobalData.wWidth = pSurfGBL->wWidth; + puSurfaceGlobalData.wHeight = pSurfGBL->wHeight; + puSurfaceGlobalData.dwLinearSize = pSurfGBL->dwLinearSize; + puSurfaceGlobalData.fpVidMem = pSurfGBL->fpVidMem; + puSurfaceGlobalData.dwBlockSizeX = pSurfGBL->dwBlockSizeX; + puSurfaceGlobalData.dwBlockSizeY = pSurfGBL->dwBlockSizeY; + + /* Convert DDRAWI_DDRAWSURFACE_MORE to DD_SURFACE_MORE */ + puSurfaceMoreData.dwSurfaceHandle = pSurfLcl->lpSurfMore->dwSurfaceHandle; + puSurfaceMoreData.ddsCapsEx.dwCaps2 = pSurfLcl->lpSurfMore->ddsCapsEx.dwCaps2; + puSurfaceMoreData.ddsCapsEx.dwCaps3 = pSurfLcl->lpSurfMore->ddsCapsEx.dwCaps3; + puSurfaceMoreData.ddsCapsEx.dwCaps4 = pSurfLcl->lpSurfMore->ddsCapsEx.dwCaps4; + + /* Convert DDRAWI_DDRAWSURFACE_LCL to DD_SURFACE_LOCAL */ + puSurfaceLocalData.dwFlags = pSurfLcl->dwFlags; + puSurfaceLocalData.ddsCaps.dwCaps = pSurfLcl->ddsCaps.dwCaps; + + /* Call win32k */ + retValue = NtGdiDdCreateD3DBuffer( GetDdHandle(pCreateSurface->lpDD->hDD), + (HANDLE*)&pSurfLcl->hDDSurface, + pCreateSurface->lpDDSurfaceDesc, + &puSurfaceGlobalData, + &puSurfaceLocalData, + &puSurfaceMoreData, + (DD_CREATESURFACEDATA *) pCreateSurface, + &puhSurface); + + /* Setup surface handle if we got one back */ + if ( puhSurface != NULL ) + { + pCreateSurface->lplpSList[0]->hDDSurface = (ULONG_PTR)puhSurface; + } + + /* Convert DD_SURFACE_GLOBAL to DDRAWI_DDRAWSURFACE_GBL */ + pSurfGBL->dwLinearSize = puSurfaceGlobalData.dwLinearSize; + pSurfGBL->fpVidMem = puSurfaceGlobalData.fpVidMem; + pSurfGBL->dwBlockSizeX = puSurfaceGlobalData.dwBlockSizeX; + pSurfGBL->dwBlockSizeY = puSurfaceGlobalData.dwBlockSizeY; + + return retValue; +} + +/* + * @implemented + * + * DdDestroyD3DBuffer + */ +DWORD +WINAPI +DdDestroyD3DBuffer(LPDDHAL_DESTROYSURFACEDATA pDestroySurface) +{ + DWORD retValue = 0; + if ( pDestroySurface->lpDDSurface->hDDSurface) + { + /* Call win32k */ + retValue = NtGdiDdDestroyD3DBuffer((HANDLE)pDestroySurface->lpDDSurface->hDDSurface); + } + + return retValue; +} + +/* + * @implemented + * + * DdLockD3D + */ +DWORD +WINAPI +DdLockD3D(LPDDHAL_LOCKDATA Lock) +{ + + /* Call win32k */ + return NtGdiDdLockD3D((HANDLE)Lock->lpDDSurface->hDDSurface, (PDD_LOCKDATA)Lock); +} + +/* + * @implemented + * + * DdUnlockD3D + */ +DWORD +WINAPI +DdUnlockD3D(LPDDHAL_UNLOCKDATA Unlock) +{ + /* Call win32k */ + return NtGdiDdUnlock((HANDLE)Unlock->lpDDSurface->hDDSurface, + (PDD_UNLOCKDATA)Unlock); +} + + +/* PRIVATE FUNCTIONS *********************************************************/ + +BOOL +WINAPI +bDDCreateSurface(LPDDRAWI_DDRAWSURFACE_LCL pSurface, + BOOL bComplete) +{ + DD_SURFACE_LOCAL SurfaceLocal; + DD_SURFACE_GLOBAL SurfaceGlobal; + DD_SURFACE_MORE SurfaceMore; + + /* Zero struct */ + RtlZeroMemory(&SurfaceLocal, sizeof(DD_SURFACE_LOCAL)); + RtlZeroMemory(&SurfaceGlobal, sizeof(DD_SURFACE_GLOBAL)); + RtlZeroMemory(&SurfaceMore, sizeof(DD_SURFACE_MORE)); + + /* Set up SurfaceLocal struct */ + SurfaceLocal.ddsCaps.dwCaps = pSurface->ddsCaps.dwCaps; + SurfaceLocal.dwFlags = pSurface->dwFlags; + + /* Set up SurfaceMore struct */ + RtlMoveMemory(&SurfaceMore.ddsCapsEx, + &pSurface->ddckCKDestBlt, + sizeof(DDSCAPSEX)); + SurfaceMore.dwSurfaceHandle = (DWORD)pSurface->dbnOverlayNode.object_int->lpVtbl; + + /* Set up SurfaceGlobal struct */ + SurfaceGlobal.fpVidMem = pSurface->lpGbl->fpVidMem; + SurfaceGlobal.dwLinearSize = pSurface->lpGbl->dwLinearSize; + SurfaceGlobal.wHeight = pSurface->lpGbl->wHeight; + SurfaceGlobal.wWidth = pSurface->lpGbl->wWidth; + + /* Check if we have a pixel format */ + if (pSurface->dwFlags & DDSD_PIXELFORMAT) + { + /* Use global one */ + SurfaceGlobal.ddpfSurface = pSurface->lpGbl->lpDD->vmiData.ddpfDisplay; + SurfaceGlobal.ddpfSurface.dwSize = sizeof(DDPIXELFORMAT); + } + else + { + /* Use local one */ + SurfaceGlobal.ddpfSurface = pSurface->lpGbl->lpDD->vmiData.ddpfDisplay; + } + + /* Create the object */ + pSurface->hDDSurface = (DWORD)NtGdiDdCreateSurfaceObject(GetDdHandle(pSurface->lpGbl->lpDD->hDD), + (HANDLE)pSurface->hDDSurface, + &SurfaceLocal, + &SurfaceMore, + &SurfaceGlobal, + bComplete); + + /* Return status */ + if (pSurface->hDDSurface) return TRUE; + return FALSE; +} + +/* PUBLIC FUNCTIONS **********************************************************/ + +/* + * @implemented + * + * GDIEntry 1 + */ +BOOL +WINAPI +DdCreateDirectDrawObject(LPDDRAWI_DIRECTDRAW_GBL pDirectDrawGlobal, + HDC hdc) +{ + BOOL Return = FALSE; + + /* Check if the global hDC (hdc == 0) is being used */ + if (!hdc) + { + /* We'll only allow this if the global object doesn't exist yet */ + if (!ghDirectDraw) + { + /* Create the DC */ + if ((hdc = CreateDCW(L"Display", NULL, NULL, NULL))) + { + /* Create the DDraw Object */ + ghDirectDraw = NtGdiDdCreateDirectDrawObject(hdc); + + /* Delete our DC */ + DeleteDC(hdc); + } + } + + /* If we created the object, or had one ...*/ + if (ghDirectDraw) + { + /* Increase count and set success */ + gcDirectDraw++; + Return = TRUE; + } + + /* Zero the handle */ + pDirectDrawGlobal->hDD = 0; + } + else + { + /* Using the per-process object, so create it */ + pDirectDrawGlobal->hDD = (ULONG_PTR)NtGdiDdCreateDirectDrawObject(hdc); + + /* Set the return value */ + Return = pDirectDrawGlobal->hDD ? TRUE : FALSE; + } + + /* Return to caller */ + return Return; +} + +/* + * @implemented + * + * GDIEntry 2 + */ +BOOL +WINAPI +DdQueryDirectDrawObject(LPDDRAWI_DIRECTDRAW_GBL pDirectDrawGlobal, + LPDDHALINFO pHalInfo, + LPDDHAL_DDCALLBACKS pDDCallbacks, + LPDDHAL_DDSURFACECALLBACKS pDDSurfaceCallbacks, + LPDDHAL_DDPALETTECALLBACKS pDDPaletteCallbacks, + LPD3DHAL_CALLBACKS pD3dCallbacks, + LPD3DHAL_GLOBALDRIVERDATA pD3dDriverData, + LPDDHAL_DDEXEBUFCALLBACKS pD3dBufferCallbacks, + LPDDSURFACEDESC pD3dTextureFormats, + LPDWORD pdwFourCC, + LPVIDMEM pvmList) +{ + PVIDEOMEMORY VidMemList = NULL; + DD_HALINFO HalInfo; + D3DNTHAL_CALLBACKS D3dCallbacks; + D3DNTHAL_GLOBALDRIVERDATA D3dDriverData; + DD_D3DBUFCALLBACKS D3dBufferCallbacks; + DWORD CallbackFlags[3]; + DWORD dwNumHeaps=0, FourCCs=0; + DWORD Flags; + BOOL retVal = TRUE; + + /* Clear the structures */ + RtlZeroMemory(&HalInfo, sizeof(DD_HALINFO)); + RtlZeroMemory(&D3dCallbacks, sizeof(D3DNTHAL_CALLBACKS)); + RtlZeroMemory(&D3dDriverData, sizeof(D3DNTHAL_GLOBALDRIVERDATA)); + RtlZeroMemory(&D3dBufferCallbacks, sizeof(DD_D3DBUFCALLBACKS)); + RtlZeroMemory(CallbackFlags, sizeof(DWORD)*3); + + /* Note : XP always alloc 24*sizeof(VIDEOMEMORY) of pvmlist so we change it to it */ + if ( (pvmList != NULL) && + (pHalInfo->vmiData.dwNumHeaps != 0) ) + { + VidMemList = (PVIDEOMEMORY) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (sizeof(VIDEOMEMORY) * 24 ) * pHalInfo->vmiData.dwNumHeaps); + } + + + /* Do the query */ + if (!NtGdiDdQueryDirectDrawObject(GetDdHandle(pDirectDrawGlobal->hDD), + &HalInfo, + CallbackFlags, + &D3dCallbacks, + &D3dDriverData, + &D3dBufferCallbacks, + pD3dTextureFormats, + &dwNumHeaps, + VidMemList, + &FourCCs, + pdwFourCC)) + { + /* We failed, free the memory and return */ + retVal = FALSE; + goto cleanup; + } + + /* Clear the incoming pointer */ + RtlZeroMemory(pHalInfo, sizeof(DDHALINFO)); + + /* Convert all the data */ + pHalInfo->dwSize = sizeof(DDHALINFO); + pHalInfo->lpDDCallbacks = pDDCallbacks; + pHalInfo->lpDDSurfaceCallbacks = pDDSurfaceCallbacks; + pHalInfo->lpDDPaletteCallbacks = pDDPaletteCallbacks; + + /* Check for NT5+ D3D Data */ + if ( (D3dCallbacks.dwSize != 0) && + (D3dDriverData.dwSize != 0) ) + { + /* Write these down */ + pHalInfo->lpD3DGlobalDriverData = (ULONG_PTR)pD3dDriverData; + pHalInfo->lpD3DHALCallbacks = (ULONG_PTR)pD3dCallbacks; + + /* Check for Buffer Callbacks */ + if (D3dBufferCallbacks.dwSize) + { + /* Write this one too */ + pHalInfo->lpDDExeBufCallbacks = pD3dBufferCallbacks; + } + } + + /* Continue converting the rest */ + pHalInfo->vmiData.dwFlags = HalInfo.vmiData.dwFlags; + pHalInfo->vmiData.dwDisplayWidth = HalInfo.vmiData.dwDisplayWidth; + pHalInfo->vmiData.dwDisplayHeight = HalInfo.vmiData.dwDisplayHeight; + pHalInfo->vmiData.lDisplayPitch = HalInfo.vmiData.lDisplayPitch; + pHalInfo->vmiData.fpPrimary = 0; + + RtlCopyMemory( &pHalInfo->vmiData.ddpfDisplay, + &HalInfo.vmiData.ddpfDisplay, + sizeof(DDPIXELFORMAT)); + + pHalInfo->vmiData.dwOffscreenAlign = HalInfo.vmiData.dwOffscreenAlign; + pHalInfo->vmiData.dwOverlayAlign = HalInfo.vmiData.dwOverlayAlign; + pHalInfo->vmiData.dwTextureAlign = HalInfo.vmiData.dwTextureAlign; + pHalInfo->vmiData.dwZBufferAlign = HalInfo.vmiData.dwZBufferAlign; + pHalInfo->vmiData.dwAlphaAlign = HalInfo.vmiData.dwAlphaAlign; + + pHalInfo->vmiData.dwNumHeaps = dwNumHeaps; + pHalInfo->vmiData.pvmList = pvmList; + + RtlCopyMemory( &pHalInfo->ddCaps, + &HalInfo.ddCaps, + sizeof(DDCORECAPS )); + + pHalInfo->ddCaps.dwNumFourCCCodes = FourCCs; + pHalInfo->lpdwFourCC = pdwFourCC; + + /* always force rope 0x1000 for hal it mean only source copy is supported */ + pHalInfo->ddCaps.dwRops[6] = 0x1000; + + /* Set the HAL flags what ReactX got from the driver + * Windows XP force setting DDHALINFO_GETDRIVERINFOSET if the driver does not set it + * and ReactX doing same to keep compatible with drivers, but the driver are + * force support DdGetDriverInfo acoriding MSDN but it seam some driver do not set + * this flag even it is being supported. that is mean. It is small hack to keep + * bad driver working, that trust this is always being setting by it self at end + */ + pHalInfo->dwFlags = (HalInfo.dwFlags & ~DDHALINFO_GETDRIVERINFOSET) | DDHALINFO_GETDRIVERINFOSET; + pHalInfo->GetDriverInfo = (LPDDHAL_GETDRIVERINFO) DdGetDriverInfo; + + /* Now check if we got any DD callbacks */ + if (pDDCallbacks) + { + /* Zero the structure */ + RtlZeroMemory(pDDCallbacks, sizeof(DDHAL_DDCALLBACKS)); + pDDCallbacks->dwSize = sizeof(DDHAL_DDCALLBACKS); + + /* Set the flags for this structure + * Windows XP force setting DDHAL_CB32_CREATESURFACE if the driver does not set it + * and ReactX doing same to keep compatible with drivers, but the driver are + * force support pDDCallbacks acoriding MSDN but it seam some driver do not set + * this flag even it is being supported. that is mean. It is small hack to keep + * bad driver working, that trust this is always being setting by it self at end + */ + Flags = (CallbackFlags[0] & ~DDHAL_CB32_CREATESURFACE) | DDHAL_CB32_CREATESURFACE; + pDDCallbacks->dwFlags = Flags; + + /* Write the always-on functions */ + pDDCallbacks->CreateSurface = DdCreateSurface; + + /* Now write the pointers, if applicable */ + if (Flags & DDHAL_CB32_WAITFORVERTICALBLANK) + { + pDDCallbacks->WaitForVerticalBlank = DdWaitForVerticalBlank; + } + if (Flags & DDHAL_CB32_CANCREATESURFACE) + { + pDDCallbacks->CanCreateSurface = DdCanCreateSurface; + } + if (Flags & DDHAL_CB32_GETSCANLINE) + { + pDDCallbacks->GetScanLine = DdGetScanLine; + } + } + + /* Check for DD Surface Callbacks */ + if (pDDSurfaceCallbacks) + { + /* Zero the structures */ + RtlZeroMemory(pDDSurfaceCallbacks, sizeof(DDHAL_DDSURFACECALLBACKS)); + pDDSurfaceCallbacks->dwSize = sizeof(DDHAL_DDSURFACECALLBACKS); + + /* Set the flags for this structure + * Windows XP force setting DDHAL_SURFCB32_LOCK, DDHAL_SURFCB32_UNLOCK, + * DDHAL_SURFCB32_SETCOLORKEY, DDHAL_SURFCB32_DESTROYSURFACE if the driver + * does not set it and ReactX doing same to keep compatible with drivers, + * but the driver are force support pDDSurfaceCallbacks acoriding MSDN but it seam + * some driver do not set this flag even it is being supported. that is mean. + * It is small hack to keep bad driver working, that trust this is always being + * setting by it self at end + */ + + Flags = (CallbackFlags[1] & ~(DDHAL_SURFCB32_LOCK | DDHAL_SURFCB32_UNLOCK | + DDHAL_SURFCB32_SETCOLORKEY | DDHAL_SURFCB32_DESTROYSURFACE)) | + (DDHAL_SURFCB32_LOCK | DDHAL_SURFCB32_UNLOCK | + DDHAL_SURFCB32_SETCOLORKEY | DDHAL_SURFCB32_DESTROYSURFACE); + + pDDSurfaceCallbacks->dwFlags = Flags; + + /* Write the always-on functions */ + pDDSurfaceCallbacks->Lock = DdLock; + pDDSurfaceCallbacks->Unlock = DdUnlock; + pDDSurfaceCallbacks->SetColorKey = DdSetColorKey; + pDDSurfaceCallbacks->DestroySurface = DdDestroySurface; + + /* Write the optional ones */ + if (Flags & DDHAL_SURFCB32_FLIP) + { + pDDSurfaceCallbacks->Flip = DdFlip; + } + if (Flags & DDHAL_SURFCB32_BLT) + { + pDDSurfaceCallbacks->Blt = DdBlt; + } + if (Flags & DDHAL_SURFCB32_GETBLTSTATUS) + { + pDDSurfaceCallbacks->GetBltStatus = DdGetBltStatus; + } + if (Flags & DDHAL_SURFCB32_GETFLIPSTATUS) + { + pDDSurfaceCallbacks->GetFlipStatus = DdGetFlipStatus; + } + if (Flags & DDHAL_SURFCB32_UPDATEOVERLAY) + { + pDDSurfaceCallbacks->UpdateOverlay = DdUpdateOverlay; + } + if (Flags & DDHAL_SURFCB32_SETOVERLAYPOSITION) + { + pDDSurfaceCallbacks->SetOverlayPosition = DdSetOverlayPosition; + } + if (Flags & DDHAL_SURFCB32_ADDATTACHEDSURFACE) + { + pDDSurfaceCallbacks->AddAttachedSurface = DdAddAttachedSurface; + } + } + + /* Check for DD Palette Callbacks, This interface are dead for user mode, + * only what it can support are being report back. + */ + if (pDDPaletteCallbacks) + { + /* Zero the struct */ + RtlZeroMemory(pDDPaletteCallbacks, sizeof(DDHAL_DDPALETTECALLBACKS)); + + /* Write the header */ + pDDPaletteCallbacks->dwSize = sizeof(DDHAL_DDPALETTECALLBACKS); + pDDPaletteCallbacks->dwFlags = CallbackFlags[2]; + } + + if (pD3dCallbacks) + { + /* Zero the struct */ + RtlZeroMemory(pD3dCallbacks, sizeof(DDHAL_DDEXEBUFCALLBACKS)); + + /* Check if we have one */ + if (D3dCallbacks.dwSize) + { + /* Write the header */ + pD3dCallbacks->dwSize = sizeof(DDHAL_DDEXEBUFCALLBACKS); + + /* Now check for each callback */ + if (D3dCallbacks.ContextCreate) + { + pD3dCallbacks->ContextCreate = (LPD3DHAL_CONTEXTCREATECB) D3dContextCreate; + } + if (D3dCallbacks.ContextDestroy) + { + pD3dCallbacks->ContextDestroy = (LPD3DHAL_CONTEXTDESTROYCB) NtGdiD3dContextDestroy; + } + if (D3dCallbacks.ContextDestroyAll) + { + pD3dCallbacks->ContextDestroyAll = (LPD3DHAL_CONTEXTDESTROYALLCB) NtGdiD3dContextDestroyAll; + } + } + } + + /* Check for D3D Driver Data */ + if (pD3dDriverData) + { + /* Copy the struct */ + RtlMoveMemory(pD3dDriverData, &D3dDriverData, sizeof(D3DHAL_GLOBALDRIVERDATA)); + + /* Write the pointer to the texture formats */ + pD3dDriverData->lpTextureFormats = pD3dTextureFormats; + } + + /* Check for D3D Buffer Callbacks */ + if (pD3dBufferCallbacks) + { + /* Zero the struct */ + RtlZeroMemory(pD3dBufferCallbacks, sizeof(DDHAL_DDEXEBUFCALLBACKS)); + + if ( D3dBufferCallbacks.dwSize) + { + pD3dBufferCallbacks->dwSize = D3dBufferCallbacks.dwSize; + + pD3dBufferCallbacks->dwFlags = D3dBufferCallbacks.dwFlags; + if ( D3dBufferCallbacks.CanCreateD3DBuffer) + { + pD3dBufferCallbacks->CanCreateExecuteBuffer = (LPDDHALEXEBUFCB_CANCREATEEXEBUF)DdCanCreateD3DBuffer; + } + + if (D3dBufferCallbacks.CreateD3DBuffer) + { + pD3dBufferCallbacks->CreateExecuteBuffer = (LPDDHALEXEBUFCB_CREATEEXEBUF) DdCreateD3DBuffer; + } + + if ( D3dBufferCallbacks.DestroyD3DBuffer ) + { + pD3dBufferCallbacks->DestroyExecuteBuffer = (LPDDHALEXEBUFCB_DESTROYEXEBUF) DdDestroyD3DBuffer; + } + + if ( D3dBufferCallbacks.LockD3DBuffer ) + { + pD3dBufferCallbacks->LockExecuteBuffer = (LPDDHALEXEBUFCB_LOCKEXEBUF) DdLockD3D; + } + + if ( D3dBufferCallbacks.UnlockD3DBuffer ) + { + pD3dBufferCallbacks->UnlockExecuteBuffer = (LPDDHALEXEBUFCB_UNLOCKEXEBUF) DdUnlockD3D; + } + + } + } + + /* FIXME VidMemList */ + +cleanup: + if (VidMemList) + { + HeapFree(GetProcessHeap(), 0, VidMemList); + } + + return retVal; +} + +/* + * @implemented + * + * GDIEntry 3 + */ +BOOL +WINAPI +DdDeleteDirectDrawObject(LPDDRAWI_DIRECTDRAW_GBL pDirectDrawGlobal) +{ + BOOL Return = FALSE; + + /* If this is the global object */ + if(pDirectDrawGlobal->hDD) + { + /* Free it */ + Return = NtGdiDdDeleteDirectDrawObject((HANDLE)pDirectDrawGlobal->hDD); - if (Return == TRUE) ++ if (Return) + { + pDirectDrawGlobal->hDD = 0; + } + } + else if (ghDirectDraw) + { + /* Always success here */ + Return = TRUE; + + /* Make sure this is the last instance */ + if (!(--gcDirectDraw)) + { + /* Delete the object */ + Return = NtGdiDdDeleteDirectDrawObject(ghDirectDraw); - if (Return == TRUE) ++ if (Return) + { + ghDirectDraw = 0; + } + } + } + + /* Return */ + return Return; +} + +/* + * @implemented + * + * GDIEntry 4 + */ +BOOL +WINAPI +DdCreateSurfaceObject( LPDDRAWI_DDRAWSURFACE_LCL pSurfaceLocal, + BOOL bPrimarySurface) +{ + return bDDCreateSurface(pSurfaceLocal, TRUE); +} + + +/* + * @implemented + * + * GDIEntry 5 + */ +BOOL +WINAPI +DdDeleteSurfaceObject(LPDDRAWI_DDRAWSURFACE_LCL pSurfaceLocal) +{ + BOOL Return = FALSE; + + /* Make sure there is one */ + if (pSurfaceLocal->hDDSurface) + { + /* Delete it */ + Return = NtGdiDdDeleteSurfaceObject((HANDLE)pSurfaceLocal->hDDSurface); + pSurfaceLocal->hDDSurface = 0; + } + + return Return; +} + +/* + * @implemented + * + * GDIEntry 6 + */ +BOOL +WINAPI +DdResetVisrgn(LPDDRAWI_DDRAWSURFACE_LCL pSurfaceLocal, + HWND hWnd) +{ + /* Call win32k directly */ + return NtGdiDdResetVisrgn((HANDLE) pSurfaceLocal->hDDSurface, hWnd); +} + +/* + * @implemented + * + * GDIEntry 7 + */ +HDC +WINAPI +DdGetDC(LPDDRAWI_DDRAWSURFACE_LCL pSurfaceLocal, + LPPALETTEENTRY pColorTable) +{ + /* Call win32k directly */ + return NtGdiDdGetDC(pColorTable, (HANDLE) pSurfaceLocal->hDDSurface); +} + +/* + * @implemented + * + * GDIEntry 8 + */ +BOOL +WINAPI +DdReleaseDC(LPDDRAWI_DDRAWSURFACE_LCL pSurfaceLocal) +{ + /* Call win32k directly */ + return NtGdiDdReleaseDC((HANDLE) pSurfaceLocal->hDDSurface); +} + +/* + * @unimplemented + * GDIEntry 9 + */ +HBITMAP +WINAPI +DdCreateDIBSection(HDC hdc, + CONST BITMAPINFO *pbmi, + UINT iUsage, + VOID **ppvBits, + HANDLE hSectionApp, + DWORD dwOffset) +{ + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return 0; +} + +/* + * @implemented + * + * GDIEntry 10 + */ +BOOL +WINAPI +DdReenableDirectDrawObject(LPDDRAWI_DIRECTDRAW_GBL pDirectDrawGlobal, + BOOL *pbNewMode) +{ + /* Call win32k directly */ + return NtGdiDdReenableDirectDrawObject(GetDdHandle(pDirectDrawGlobal->hDD), + pbNewMode); +} + + +/* + * @implemented + * + * GDIEntry 11 + */ +BOOL +WINAPI +DdAttachSurface( LPDDRAWI_DDRAWSURFACE_LCL pSurfaceFrom, + LPDDRAWI_DDRAWSURFACE_LCL pSurfaceTo) +{ + /* Create Surface if it does not exits one */ + if (!pSurfaceFrom->hDDSurface) + { + if (!bDDCreateSurface(pSurfaceFrom, FALSE)) + { + return FALSE; + } + } + + /* Create Surface if it does not exits one */ + if (!pSurfaceTo->hDDSurface) + { + if (!bDDCreateSurface(pSurfaceTo, FALSE)) + { + return FALSE; + } + } + + /* Call win32k */ + return NtGdiDdAttachSurface((HANDLE)pSurfaceFrom->hDDSurface, + (HANDLE)pSurfaceTo->hDDSurface); +} + +/* + * @implemented + * + * GDIEntry 12 + */ +VOID +WINAPI +DdUnattachSurface(LPDDRAWI_DDRAWSURFACE_LCL pSurface, + LPDDRAWI_DDRAWSURFACE_LCL pSurfaceAttached) +{ + /* Call win32k */ + NtGdiDdUnattachSurface((HANDLE)pSurface->hDDSurface, + (HANDLE)pSurfaceAttached->hDDSurface); +} + +/* + * @implemented + * + * GDIEntry 13 + */ +ULONG +WINAPI +DdQueryDisplaySettingsUniqueness() +{ + return GdiSharedHandleTable->flDeviceUniq; +} + +/* + * @implemented + * + * GDIEntry 14 + */ +HANDLE +WINAPI +DdGetDxHandle(LPDDRAWI_DIRECTDRAW_LCL pDDraw, + LPDDRAWI_DDRAWSURFACE_LCL pSurface, + BOOL bRelease) +{ + HANDLE hDD = NULL; + HANDLE hSurface = NULL; + + /* Check if we already have a surface */ + if (!pSurface) + { + /* We don't have one, use the DirectDraw Object handle instead */ + hDD = GetDdHandle(pDDraw->lpGbl->hDD); + } + else + { + hSurface = (HANDLE)pSurface->hDDSurface; + } + + /* Call the API */ + return (HANDLE)NtGdiDdGetDxHandle(hDD, hSurface, bRelease); +} + +/* + * @implemented + * + * GDIEntry 15 + */ +BOOL +WINAPI +DdSetGammaRamp(LPDDRAWI_DIRECTDRAW_LCL pDDraw, + HDC hdc, + LPVOID lpGammaRamp) +{ + /* Call win32k directly */ + return NtGdiDdSetGammaRamp(GetDdHandle(pDDraw->lpGbl->hDD), + hdc, + lpGammaRamp); +} + + + + + diff --cc win32ss/gdi/ntgdi/xformobj.c index 6d7ca2db370,00000000000..dbd8e22501b mode 100644,000000..100644 --- a/win32ss/gdi/ntgdi/xformobj.c +++ b/win32ss/gdi/ntgdi/xformobj.c @@@ -1,554 -1,0 +1,554 @@@ +/* + * PROJECT: ReactOS win32 kernel mode subsystem + * LICENSE: GPL - See COPYING in the top level directory + * FILE: win32ss/gdi/ntgdi/xformobj.c + * PURPOSE: XFORMOBJ API + * PROGRAMMER: Timo Kreuzer + */ + +/** Includes ******************************************************************/ + +#include +#define NDEBUG +#include + +C_ASSERT(sizeof(FIX) == sizeof(LONG)); - #define FIX2LONG(x) ((x) >> 4) ++#define FIX2LONG(x) (((x) + 8) >> 4) +#define LONG2FIX(x) ((x) << 4) + +#define FLOATOBJ_Equal _FLOATOBJ_Equal +#define FLOATOBJ_GetLong _FLOATOBJ_GetLong +#define FLOATOBJ_GetFix _FLOATOBJ_GetFix +#define FLOATOBJ_IsLong _FLOATOBJ_IsLong +#define FLOATOBJ_Equal0 _FLOATOBJ_Equal0 +#define FLOATOBJ_Equal1 _FLOATOBJ_Equal1 + +/** Inline helper functions ***************************************************/ + +/* + * Inline helper to calculate pfo1 * pfo2 + pfo3 * pfo4 + */ +FORCEINLINE +VOID +MulAdd( + PFLOATOBJ pfoDest, + PFLOATOBJ pfo1, + PFLOATOBJ pfo2, + PFLOATOBJ pfo3, + PFLOATOBJ pfo4) +{ + FLOATOBJ foTmp; + + *pfoDest = *pfo1; + FLOATOBJ_Mul(pfoDest, pfo2); + foTmp = *pfo3; + FLOATOBJ_Mul(&foTmp, pfo4); + FLOATOBJ_Add(pfoDest, &foTmp); +} + +/* + * Inline helper to calculate pfo1 * l2 + pfo3 * l4 + */ +FORCEINLINE +VOID +MulAddLong( + PFLOATOBJ pfoDest, + PFLOATOBJ pfo1, + LONG l2, + PFLOATOBJ pfo3, + LONG l4) +{ + FLOATOBJ foTmp; + + *pfoDest = *pfo1; + FLOATOBJ_MulLong(pfoDest, l2); + foTmp = *pfo3; + FLOATOBJ_MulLong(&foTmp, l4); + FLOATOBJ_Add(pfoDest, &foTmp); +} + +/* + * Inline helper to calculate pfo1 * pfo2 - pfo3 * pfo4 + */ +FORCEINLINE +VOID +MulSub( + PFLOATOBJ pfoDest, + PFLOATOBJ pfo1, + PFLOATOBJ pfo2, + PFLOATOBJ pfo3, + PFLOATOBJ pfo4) +{ + FLOATOBJ foTmp; + + *pfoDest = *pfo1; + FLOATOBJ_Mul(pfoDest, pfo2); + foTmp = *pfo3; + FLOATOBJ_Mul(&foTmp, pfo4); + FLOATOBJ_Sub(pfoDest, &foTmp); +} + +/* + * Inline helper to get the complexity hint from flAccel + */ +FORCEINLINE +ULONG +HintFromAccel(ULONG flAccel) +{ + switch (flAccel & (XFORM_SCALE|XFORM_UNITY|XFORM_NO_TRANSLATION)) + { + case (XFORM_SCALE|XFORM_UNITY|XFORM_NO_TRANSLATION): + return GX_IDENTITY; + case (XFORM_SCALE|XFORM_UNITY): + return GX_OFFSET; + case XFORM_SCALE: + return GX_SCALE; + default: + return GX_GENERAL; + } +} + +/** Internal functions ********************************************************/ + +ULONG +NTAPI +XFORMOBJ_UpdateAccel( + IN XFORMOBJ *pxo) +{ + PMATRIX pmx = XFORMOBJ_pmx(pxo); + + /* Copy Dx and Dy to FIX format */ + pmx->fxDx = FLOATOBJ_GetFix(&pmx->efDx); + pmx->fxDy = FLOATOBJ_GetFix(&pmx->efDy); + + pmx->flAccel = 0; + + if (FLOATOBJ_Equal0(&pmx->efDx) && + FLOATOBJ_Equal0(&pmx->efDy)) + { + pmx->flAccel |= XFORM_NO_TRANSLATION; + } + + if (FLOATOBJ_Equal0(&pmx->efM12) && + FLOATOBJ_Equal0(&pmx->efM21)) + { + pmx->flAccel |= XFORM_SCALE; + } + + if (FLOATOBJ_Equal1(&pmx->efM11) && + FLOATOBJ_Equal1(&pmx->efM22)) + { + pmx->flAccel |= XFORM_UNITY; + } + + if (FLOATOBJ_IsLong(&pmx->efM11) && FLOATOBJ_IsLong(&pmx->efM12) && + FLOATOBJ_IsLong(&pmx->efM21) && FLOATOBJ_IsLong(&pmx->efM22)) + { + pmx->flAccel |= XFORM_INTEGER; + } + + return HintFromAccel(pmx->flAccel); +} + + +ULONG +NTAPI +XFORMOBJ_iSetXform( + OUT XFORMOBJ *pxo, + IN const XFORML *pxform) +{ + PMATRIX pmx = XFORMOBJ_pmx(pxo); + + /* Check parameters */ + if (!pxo || !pxform) return DDI_ERROR; + + /* Check if the xform is valid */ + if ((pxform->eM11 == 0) || (pxform->eM22 == 0)) return DDI_ERROR; + + /* Copy members */ + FLOATOBJ_SetFloat(&pmx->efM11, pxform->eM11); + FLOATOBJ_SetFloat(&pmx->efM12, pxform->eM12); + FLOATOBJ_SetFloat(&pmx->efM21, pxform->eM21); + FLOATOBJ_SetFloat(&pmx->efM22, pxform->eM22); + FLOATOBJ_SetFloat(&pmx->efDx, pxform->eDx); + FLOATOBJ_SetFloat(&pmx->efDy, pxform->eDy); + + /* Update accelerators and return complexity */ + return XFORMOBJ_UpdateAccel(pxo); +} + + +/* + * Multiplies pxo1 with pxo2 and stores the result in pxo. + * returns complexity hint + * | efM11 efM12 0 | + * | efM21 efM22 0 | + * | efDx efDy 1 | + */ +ULONG +NTAPI +XFORMOBJ_iCombine( + IN XFORMOBJ *pxo, + IN XFORMOBJ *pxo1, + IN XFORMOBJ *pxo2) +{ + MATRIX mx; + PMATRIX pmx, pmx1, pmx2; + + /* Get the source matrices */ + pmx1 = XFORMOBJ_pmx(pxo1); + pmx2 = XFORMOBJ_pmx(pxo2); + + /* Do a 3 x 3 matrix multiplication with mx as destinantion */ + MulAdd(&mx.efM11, &pmx1->efM11, &pmx2->efM11, &pmx1->efM12, &pmx2->efM21); + MulAdd(&mx.efM12, &pmx1->efM11, &pmx2->efM12, &pmx1->efM12, &pmx2->efM22); + MulAdd(&mx.efM21, &pmx1->efM21, &pmx2->efM11, &pmx1->efM22, &pmx2->efM21); + MulAdd(&mx.efM22, &pmx1->efM21, &pmx2->efM12, &pmx1->efM22, &pmx2->efM22); + MulAdd(&mx.efDx, &pmx1->efDx, &pmx2->efM11, &pmx1->efDy, &pmx2->efM21); + FLOATOBJ_Add(&mx.efDx, &pmx2->efDx); + MulAdd(&mx.efDy, &pmx1->efDx, &pmx2->efM12, &pmx1->efDy, &pmx2->efM22); + FLOATOBJ_Add(&mx.efDy, &pmx2->efDy); + + /* Copy back */ + pmx = XFORMOBJ_pmx(pxo); + *pmx = mx; + + /* Update accelerators and return complexity */ + return XFORMOBJ_UpdateAccel(pxo); +} + + +ULONG +NTAPI +XFORMOBJ_iCombineXform( + IN XFORMOBJ *pxo, + IN XFORMOBJ *pxo1, + IN XFORML *pxform, + IN BOOL bLeftMultiply) +{ + MATRIX mx; + XFORMOBJ xo2; + + XFORMOBJ_vInit(&xo2, &mx); + XFORMOBJ_iSetXform(&xo2, pxform); + + if (bLeftMultiply) + { + return XFORMOBJ_iCombine(pxo, &xo2, pxo1); + } + else + { + return XFORMOBJ_iCombine(pxo, pxo1, &xo2); + } +} + +/* + * A^-1 = adj(A) / det(AT) + * A^-1 = 1/(a*d - b*c) * (a22,-a12,a21,-a11) + */ +ULONG +NTAPI +XFORMOBJ_iInverse( + OUT XFORMOBJ *pxoDst, + IN XFORMOBJ *pxoSrc) +{ + PMATRIX pmxDst, pmxSrc; + FLOATOBJ foDet; + XFORM xformSrc; + + pmxDst = XFORMOBJ_pmx(pxoDst); + pmxSrc = XFORMOBJ_pmx(pxoSrc); + + XFORMOBJ_iGetXform(pxoSrc, (XFORML*)&xformSrc); + + /* det = M11 * M22 - M12 * M21 */ + MulSub(&foDet, &pmxSrc->efM11, &pmxSrc->efM22, &pmxSrc->efM12, &pmxSrc->efM21); + + if (FLOATOBJ_Equal0(&foDet)) + { + /* Determinant is 0! */ + return DDI_ERROR; + } + + /* Calculate adj(A) / det(A) */ + pmxDst->efM11 = pmxSrc->efM22; + FLOATOBJ_Div(&pmxDst->efM11, &foDet); + pmxDst->efM22 = pmxSrc->efM11; + FLOATOBJ_Div(&pmxDst->efM22, &foDet); + + /* The other 2 are negative, negate foDet for that */ + FLOATOBJ_Neg(&foDet); + pmxDst->efM12 = pmxSrc->efM12; + FLOATOBJ_Div(&pmxDst->efM12, &foDet); + pmxDst->efM21 = pmxSrc->efM21; + FLOATOBJ_Div(&pmxDst->efM21, &foDet); + + /* Calculate the inverted x shift: Dx' = -Dx * M11' - Dy * M21' */ + pmxDst->efDx = pmxSrc->efDx; + FLOATOBJ_Neg(&pmxDst->efDx); + MulSub(&pmxDst->efDx, &pmxDst->efDx, &pmxDst->efM11, &pmxSrc->efDy, &pmxDst->efM21); + + /* Calculate the inverted y shift: Dy' = -Dy * M22' - Dx * M12' */ + pmxDst->efDy = pmxSrc->efDy; + FLOATOBJ_Neg(&pmxDst->efDy); + MulSub(&pmxDst->efDy, &pmxDst->efDy, &pmxDst->efM22, &pmxSrc->efDx, &pmxDst->efM12); + + /* Update accelerators and return complexity */ + return XFORMOBJ_UpdateAccel(pxoDst); +} + + +BOOL +NTAPI +XFORMOBJ_bXformFixPoints( + IN XFORMOBJ *pxo, + IN ULONG cPoints, + IN PPOINTL pptIn, + OUT PPOINTL pptOut) +{ + PMATRIX pmx; + INT i; + FLOATOBJ fo1, fo2; + FLONG flAccel; + + pmx = XFORMOBJ_pmx(pxo); + flAccel = pmx->flAccel; + + if ((flAccel & (XFORM_SCALE|XFORM_UNITY)) == (XFORM_SCALE|XFORM_UNITY)) + { + /* Identity transformation, nothing todo */ + } + else if (flAccel & XFORM_INTEGER) + { + if (flAccel & XFORM_UNITY) + { + /* 1-scale integer transform */ + LONG lM12 = FLOATOBJ_GetLong(&pmx->efM12); + LONG lM21 = FLOATOBJ_GetLong(&pmx->efM21); + + i = cPoints - 1; + do + { + LONG x = pptIn[i].x + pptIn[i].y * lM21; + LONG y = pptIn[i].y + pptIn[i].x * lM12; + pptOut[i].y = y; + pptOut[i].x = x; + } + while (--i >= 0); + } + else if (flAccel & XFORM_SCALE) + { + /* Diagonal integer transform */ + LONG lM11 = FLOATOBJ_GetLong(&pmx->efM11); + LONG lM22 = FLOATOBJ_GetLong(&pmx->efM22); + + i = cPoints - 1; + do + { + pptOut[i].x = pptIn[i].x * lM11; + pptOut[i].y = pptIn[i].y * lM22; + } + while (--i >= 0); + } + else + { + /* Full integer transform */ + LONG lM11 = FLOATOBJ_GetLong(&pmx->efM11); + LONG lM12 = FLOATOBJ_GetLong(&pmx->efM12); + LONG lM21 = FLOATOBJ_GetLong(&pmx->efM21); + LONG lM22 = FLOATOBJ_GetLong(&pmx->efM22); + + i = cPoints - 1; + do + { + LONG x; + x = pptIn[i].x * lM11; + x += pptIn[i].y * lM21; + pptOut[i].y = pptIn[i].y * lM22; + pptOut[i].y += pptIn[i].x * lM12; + pptOut[i].x = x; + } + while (--i >= 0); + } + } + else if (flAccel & XFORM_UNITY) + { + /* 1-scale transform */ + i = cPoints - 1; + do + { + fo1 = pmx->efM21; + FLOATOBJ_MulLong(&fo1, pptIn[i].y); + fo2 = pmx->efM12; + FLOATOBJ_MulLong(&fo2, pptIn[i].x); + pptOut[i].x = pptIn[i].x + FLOATOBJ_GetLong(&fo1); + pptOut[i].y = pptIn[i].y + FLOATOBJ_GetLong(&fo2); + } + while (--i >= 0); + } + else if (flAccel & XFORM_SCALE) + { + /* Diagonal float transform */ + i = cPoints - 1; + do + { + fo1 = pmx->efM11; + FLOATOBJ_MulLong(&fo1, pptIn[i].x); + pptOut[i].x = FLOATOBJ_GetLong(&fo1); + fo2 = pmx->efM22; + FLOATOBJ_MulLong(&fo2, pptIn[i].y); + pptOut[i].y = FLOATOBJ_GetLong(&fo2); + } + while (--i >= 0); + } + else + { + /* Full float transform */ + i = cPoints - 1; + do + { + MulAddLong(&fo1, &pmx->efM11, pptIn[i].x, &pmx->efM21, pptIn[i].y); + MulAddLong(&fo2, &pmx->efM12, pptIn[i].x, &pmx->efM22, pptIn[i].y); + pptOut[i].x = FLOATOBJ_GetLong(&fo1); + pptOut[i].y = FLOATOBJ_GetLong(&fo2); + } + while (--i >= 0); + } + + if (!(pmx->flAccel & XFORM_NO_TRANSLATION)) + { + /* Translate points */ + i = cPoints - 1; + do + { + pptOut[i].x += pmx->fxDx; + pptOut[i].y += pmx->fxDy; + } + while (--i >= 0); + } + + return TRUE; +} + +/** Public functions **********************************************************/ + +// www.osr.com/ddk/graphics/gdifncs_0s2v.htm +ULONG +APIENTRY +XFORMOBJ_iGetXform( + IN XFORMOBJ *pxo, + OUT XFORML *pxform) +{ + PMATRIX pmx = XFORMOBJ_pmx(pxo); + + /* Check parameters */ + if (!pxo || !pxform) + { + return DDI_ERROR; + } + + /* Copy members */ + pxform->eM11 = FLOATOBJ_GetFloat(&pmx->efM11); + pxform->eM12 = FLOATOBJ_GetFloat(&pmx->efM12); + pxform->eM21 = FLOATOBJ_GetFloat(&pmx->efM21); + pxform->eM22 = FLOATOBJ_GetFloat(&pmx->efM22); + pxform->eDx = FLOATOBJ_GetFloat(&pmx->efDx); + pxform->eDy = FLOATOBJ_GetFloat(&pmx->efDy); + + /* Return complexity hint */ + return HintFromAccel(pmx->flAccel); +} + + +// www.osr.com/ddk/graphics/gdifncs_5ig7.htm +ULONG +APIENTRY +XFORMOBJ_iGetFloatObjXform( + IN XFORMOBJ *pxo, + OUT FLOATOBJ_XFORM *pxfo) +{ + PMATRIX pmx = XFORMOBJ_pmx(pxo); + + /* Check parameters */ + if (!pxo || !pxfo) + { + return DDI_ERROR; + } + + /* Copy members */ + pxfo->eM11 = pmx->efM11; + pxfo->eM12 = pmx->efM12; + pxfo->eM21 = pmx->efM21; + pxfo->eM22 = pmx->efM22; + pxfo->eDx = pmx->efDx; + pxfo->eDy = pmx->efDy; + + /* Return complexity hint */ + return HintFromAccel(pmx->flAccel); +} + + +// www.osr.com/ddk/graphics/gdifncs_027b.htm +BOOL +APIENTRY +XFORMOBJ_bApplyXform( + IN XFORMOBJ *pxo, + IN ULONG iMode, + IN ULONG cPoints, + IN PVOID pvIn, + OUT PVOID pvOut) +{ + MATRIX mx; + XFORMOBJ xoInv; + POINTL *pptl; + INT i; + + /* Check parameters */ + if (!pxo || !pvIn || !pvOut || cPoints < 1) + { + return FALSE; + } + + /* Use inverse xform? */ + if (iMode == XF_INV_FXTOL || iMode == XF_INV_LTOL) + { + XFORMOBJ_vInit(&xoInv, &mx); + if (XFORMOBJ_iInverse(&xoInv, pxo) == DDI_ERROR) + { + return FALSE; + } + pxo = &xoInv; + } + + /* Convert POINTL to POINTFIX? */ + if (iMode == XF_LTOFX || iMode == XF_LTOL || iMode == XF_INV_LTOL) + { + pptl = pvIn; + for (i = cPoints - 1; i >= 0; i--) + { + pptl[i].x = LONG2FIX(pptl[i].x); + pptl[i].y = LONG2FIX(pptl[i].y); + } + } + + /* Do the actual fixpoint transformation */ + if (!XFORMOBJ_bXformFixPoints(pxo, cPoints, pvIn, pvOut)) + { + return FALSE; + } + + /* Convert POINTFIX to POINTL? */ + if (iMode == XF_INV_FXTOL || iMode == XF_INV_LTOL || iMode == XF_LTOL) + { + pptl = pvOut; + for (i = cPoints - 1; i >= 0; i--) + { + pptl[i].x = FIX2LONG(pptl[i].x); + pptl[i].y = FIX2LONG(pptl[i].y); + } + } + + return TRUE; +} + +/* EOF */ diff --cc win32ss/user/ntuser/hotkey.c index 4be4c216988,00000000000..ddb0ce7d8a6 mode 100644,000000..100644 --- a/win32ss/user/ntuser/hotkey.c +++ b/win32ss/user/ntuser/hotkey.c @@@ -1,607 -1,0 +1,607 @@@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Win32k subsystem + * PURPOSE: HotKey support + * FILE: win32ss/user/ntuser/hotkey.c + * PROGRAMER: Eric Kohl + */ + +/* + * FIXME: Hotkey notifications are triggered by keyboard input (physical or programatically) + * and since only desktops on WinSta0 can receive input in seems very wrong to allow + * windows/threads on destops not belonging to WinSta0 to set hotkeys (receive notifications). + * -- Gunnar + */ + +#include +DBG_DEFAULT_CHANNEL(UserHotkey); + +/* GLOBALS *******************************************************************/ + +/* + * Hardcoded hotkeys. See http://ivanlef0u.fr/repo/windoz/VI20051005.html + * or http://repo.meh.or.id/Windows/VI20051005.html . + * + * NOTE: The (Shift-)F12 keys are used only for the "UserDebuggerHotKey" setting + * which enables setting a key shortcut which, when pressed, establishes a + * breakpoint in the code being debugged: + * see http://technet.microsoft.com/en-us/library/cc786263(v=ws.10).aspx + * and http://flylib.com/books/en/4.441.1.33/1/ for more details. + * By default the key is VK-F12 on a 101-key keyboard, and is VK_SUBTRACT + * (hyphen / substract sign) on a 82-key keyboard. + */ +/* pti pwnd modifiers vk id next */ +// HOT_KEY hkF12 = {NULL, 1, 0, VK_F12, IDHK_F12, NULL}; +// HOT_KEY hkShiftF12 = {NULL, 1, MOD_SHIFT, VK_F12, IDHK_SHIFTF12, &hkF12}; +// HOT_KEY hkWinKey = {NULL, 1, MOD_WIN, 0, IDHK_WINKEY, &hkShiftF12}; + +PHOT_KEY gphkFirst = NULL; +BOOL bWinHotkeyActive = FALSE; + +/* FUNCTIONS *****************************************************************/ + +VOID FASTCALL +StartDebugHotKeys(VOID) +{ + UINT vk = VK_F12; + UserUnregisterHotKey(PWND_BOTTOM, IDHK_F12); + UserUnregisterHotKey(PWND_BOTTOM, IDHK_SHIFTF12); + if (!ENHANCED_KEYBOARD(gKeyboardInfo.KeyboardIdentifier)) + { + vk = VK_SUBTRACT; + } + UserRegisterHotKey(PWND_BOTTOM, IDHK_SHIFTF12, MOD_SHIFT, vk); + UserRegisterHotKey(PWND_BOTTOM, IDHK_F12, 0, vk); + TRACE("Start up the debugger hotkeys!! If you see this you eneabled debugprints. Congrats!\n"); +} + +/* + * IntGetModifiers + * + * Returns a value that indicates if the key is a modifier key, and + * which one. + */ +static +UINT FASTCALL +IntGetModifiers(PBYTE pKeyState) +{ + UINT fModifiers = 0; + + if (IS_KEY_DOWN(pKeyState, VK_SHIFT)) + fModifiers |= MOD_SHIFT; + + if (IS_KEY_DOWN(pKeyState, VK_CONTROL)) + fModifiers |= MOD_CONTROL; + + if (IS_KEY_DOWN(pKeyState, VK_MENU)) + fModifiers |= MOD_ALT; + + if (IS_KEY_DOWN(pKeyState, VK_LWIN) || IS_KEY_DOWN(pKeyState, VK_RWIN)) + fModifiers |= MOD_WIN; + + return fModifiers; +} + +/* + * UnregisterWindowHotKeys + * + * Removes hotkeys registered by specified window on its cleanup + */ +VOID FASTCALL +UnregisterWindowHotKeys(PWND pWnd) +{ + PHOT_KEY pHotKey = gphkFirst, phkNext, *pLink = &gphkFirst; + + while (pHotKey) + { + /* Save next ptr for later use */ + phkNext = pHotKey->pNext; + + /* Should we delete this hotkey? */ + if (pHotKey->pWnd == pWnd) + { + /* Update next ptr for previous hotkey and free memory */ + *pLink = phkNext; + ExFreePoolWithTag(pHotKey, USERTAG_HOTKEY); + } + else /* This hotkey will stay, use its next ptr */ + pLink = &pHotKey->pNext; + + /* Move to the next entry */ + pHotKey = phkNext; + } +} + +/* + * UnregisterThreadHotKeys + * + * Removes hotkeys registered by specified thread on its cleanup + */ +VOID FASTCALL +UnregisterThreadHotKeys(PTHREADINFO pti) +{ + PHOT_KEY pHotKey = gphkFirst, phkNext, *pLink = &gphkFirst; + + while (pHotKey) + { + /* Save next ptr for later use */ + phkNext = pHotKey->pNext; + + /* Should we delete this hotkey? */ + if (pHotKey->pti == pti) + { + /* Update next ptr for previous hotkey and free memory */ + *pLink = phkNext; + ExFreePoolWithTag(pHotKey, USERTAG_HOTKEY); + } + else /* This hotkey will stay, use its next ptr */ + pLink = &pHotKey->pNext; + + /* Move to the next entry */ + pHotKey = phkNext; + } +} + +/* + * IsHotKey + * + * Checks if given key and modificators have corresponding hotkey + */ +static PHOT_KEY FASTCALL +IsHotKey(UINT fsModifiers, WORD wVk) +{ + PHOT_KEY pHotKey = gphkFirst; + + while (pHotKey) + { + if (pHotKey->fsModifiers == fsModifiers && + pHotKey->vk == wVk) + { + /* We have found it */ + return pHotKey; + } + + /* Move to the next entry */ + pHotKey = pHotKey->pNext; + } + + return NULL; +} + +/* + * co_UserProcessHotKeys + * + * Sends WM_HOTKEY message if given keys are hotkey + */ +BOOL NTAPI +co_UserProcessHotKeys(WORD wVk, BOOL bIsDown) +{ + UINT fModifiers; + PHOT_KEY pHotKey; + PWND pWnd; + BOOL DoNotPostMsg = FALSE; + + if (wVk == VK_SHIFT || wVk == VK_CONTROL || wVk == VK_MENU || + wVk == VK_LWIN || wVk == VK_RWIN) + { + /* Those keys are specified by modifiers */ + wVk = 0; + } + + fModifiers = IntGetModifiers(gafAsyncKeyState); + + /* Check if it is a hotkey */ + pHotKey = IsHotKey(fModifiers, wVk); + + if (pHotKey) + { + TRACE("Hot key pressed (pWnd %p, id %d)\n", pHotKey->pWnd, pHotKey->id); + + /* FIXME: See comment about "UserDebuggerHotKey" on top of this file. */ + if (pHotKey->id == IDHK_SHIFTF12 || pHotKey->id == IDHK_F12) + { + if (bIsDown) + { + ERR("Hot key pressed for Debug Activation! ShiftF12 = %d or F12 = %d\n",pHotKey->id == IDHK_SHIFTF12 , pHotKey->id == IDHK_F12); + //DoNotPostMsg = co_ActivateDebugger(); // FIXME + } + return DoNotPostMsg; + } + + /* Process hotkey if it is key up event */ + if (!bIsDown) + { + /* WIN and F12 keys are not hardcoded here. See comments on top of this file. */ - if (pHotKey->id == IDHK_WINKEY && bWinHotkeyActive == TRUE) ++ if (pHotKey->id == IDHK_WINKEY && bWinHotkeyActive) + { + pWnd = ValidateHwndNoErr(InputWindowStation->ShellWindow); + if (pWnd) + { + TRACE("System Hot key Id %d Key %d\n",pHotKey->id, wVk ); + UserPostMessage(UserHMGetHandle(pWnd), WM_SYSCOMMAND, SC_TASKLIST, 0); + co_IntShellHookNotify(HSHELL_TASKMAN, 0, 0); + bWinHotkeyActive = FALSE; + return FALSE; + } + } + } + else + { /* The user pressed the win key */ + if (pHotKey->id == IDHK_WINKEY) + { + bWinHotkeyActive = TRUE; + return FALSE; + } + } + + if (bIsDown) + { + if (!pHotKey->pWnd) + { + TRACE("UPTM Hot key Id %d Key %d\n",pHotKey->id, wVk ); + UserPostThreadMessage(pHotKey->pti, WM_HOTKEY, pHotKey->id, MAKELONG(fModifiers, wVk)); + //ptiLastInput = pHotKey->pti; + return TRUE; /* Don't send any message */ + } + else + { + if (pHotKey->pWnd == PWND_BOTTOM) + { + if (gpqForeground != NULL) + { + pWnd = gpqForeground->spwndFocus; + } + else + return FALSE; + } + else + { + pWnd = pHotKey->pWnd; + } + if (pWnd) + { // pWnd->head.rpdesk->pDeskInfo->spwndShell needs testing. + if (pWnd == ValidateHwndNoErr(InputWindowStation->ShellWindow) && pHotKey->id == SC_TASKLIST) + { + ERR("Sending to shell window w/o IDHK_WINKEY..\n"); + UserPostMessage(UserHMGetHandle(pWnd), WM_SYSCOMMAND, SC_TASKLIST, 0); + } + else + { + TRACE("UPM Hot key Id %d Key %d\n",pHotKey->id, wVk ); + UserPostMessage(UserHMGetHandle(pWnd), WM_HOTKEY, pHotKey->id, MAKELONG(fModifiers, wVk)); + } + //ptiLastInput = pWnd->head.pti; + return TRUE; /* Don't send any message */ + } + } + } + } + return FALSE; +} + + +/* + * DefWndGetHotKey + * + * GetHotKey message support + */ +UINT FASTCALL +DefWndGetHotKey(PWND pWnd) +{ + PHOT_KEY pHotKey = gphkFirst; + + WARN("DefWndGetHotKey\n"); + + while (pHotKey) + { + if (pHotKey->pWnd == pWnd && pHotKey->id == IDHK_REACTOS) + { + /* We have found it */ + return MAKELONG(pHotKey->vk, pHotKey->fsModifiers); + } + + /* Move to the next entry */ + pHotKey = pHotKey->pNext; + } + + return 0; +} + +/* + * DefWndSetHotKey + * + * SetHotKey message support + */ +INT FASTCALL +DefWndSetHotKey(PWND pWnd, WPARAM wParam) +{ + UINT fsModifiers, vk; + PHOT_KEY pHotKey, *pLink; + INT iRet = 1; + + WARN("DefWndSetHotKey wParam 0x%x\n", wParam); + + // A hot key cannot be associated with a child window. + if (pWnd->style & WS_CHILD) + return 0; + + // VK_ESCAPE, VK_SPACE, and VK_TAB are invalid hot keys. + if (LOWORD(wParam) == VK_ESCAPE || + LOWORD(wParam) == VK_SPACE || + LOWORD(wParam) == VK_TAB) + { + return -1; + } + + vk = LOWORD(wParam); + fsModifiers = HIWORD(wParam); + + if (wParam) + { + pHotKey = gphkFirst; + while (pHotKey) + { + if (pHotKey->fsModifiers == fsModifiers && + pHotKey->vk == vk && + pHotKey->id == IDHK_REACTOS) + { + if (pHotKey->pWnd != pWnd) + iRet = 2; // Another window already has the same hot key. + break; + } + + /* Move to the next entry */ + pHotKey = pHotKey->pNext; + } + } + + pHotKey = gphkFirst; + pLink = &gphkFirst; + while (pHotKey) + { + if (pHotKey->pWnd == pWnd && + pHotKey->id == IDHK_REACTOS) + { + /* This window has already hotkey registered */ + break; + } + + /* Move to the next entry */ + pLink = &pHotKey->pNext; + pHotKey = pHotKey->pNext; + } + + if (wParam) + { + if (!pHotKey) + { + /* Create new hotkey */ + pHotKey = ExAllocatePoolWithTag(PagedPool, sizeof(HOT_KEY), USERTAG_HOTKEY); + if (pHotKey == NULL) + return 0; + + pHotKey->pWnd = pWnd; + pHotKey->id = IDHK_REACTOS; // Don't care, these hot keys are unrelated to the hot keys set by RegisterHotKey + pHotKey->pNext = gphkFirst; + gphkFirst = pHotKey; + } + + /* A window can only have one hot key. If the window already has a + hot key associated with it, the new hot key replaces the old one. */ + pHotKey->pti = NULL; + pHotKey->fsModifiers = fsModifiers; + pHotKey->vk = vk; + } + else if (pHotKey) + { + /* Remove hotkey */ + *pLink = pHotKey->pNext; + ExFreePoolWithTag(pHotKey, USERTAG_HOTKEY); + } + + return iRet; +} + + +BOOL FASTCALL +UserRegisterHotKey(PWND pWnd, + int id, + UINT fsModifiers, + UINT vk) +{ + PHOT_KEY pHotKey; + PTHREADINFO pHotKeyThread; + + /* Find hotkey thread */ + if (pWnd == NULL || pWnd == PWND_BOTTOM) + { + pHotKeyThread = PsGetCurrentThreadWin32Thread(); + } + else + { + pHotKeyThread = pWnd->head.pti; + } + + /* Check for existing hotkey */ + if (IsHotKey(fsModifiers, vk)) + { + EngSetLastError(ERROR_HOTKEY_ALREADY_REGISTERED); + WARN("Hotkey already exists\n"); + return FALSE; + } + + /* Create new hotkey */ + pHotKey = ExAllocatePoolWithTag(PagedPool, sizeof(HOT_KEY), USERTAG_HOTKEY); + if (pHotKey == NULL) + { + EngSetLastError(ERROR_NOT_ENOUGH_MEMORY); + return FALSE; + } + + pHotKey->pti = pHotKeyThread; + pHotKey->pWnd = pWnd; + pHotKey->fsModifiers = fsModifiers; + pHotKey->vk = vk; + pHotKey->id = id; + + /* Insert hotkey to the global list */ + pHotKey->pNext = gphkFirst; + gphkFirst = pHotKey; + + return TRUE; +} + +BOOL FASTCALL +UserUnregisterHotKey(PWND pWnd, int id) +{ + PHOT_KEY pHotKey = gphkFirst, phkNext, *pLink = &gphkFirst; + BOOL bRet = FALSE; + + while (pHotKey) + { + /* Save next ptr for later use */ + phkNext = pHotKey->pNext; + + /* Should we delete this hotkey? */ + if (pHotKey->pWnd == pWnd && pHotKey->id == id) + { + /* Update next ptr for previous hotkey and free memory */ + *pLink = phkNext; + ExFreePoolWithTag(pHotKey, USERTAG_HOTKEY); + + bRet = TRUE; + } + else /* This hotkey will stay, use its next ptr */ + pLink = &pHotKey->pNext; + + /* Move to the next entry */ + pHotKey = phkNext; + } + return bRet; +} + + +/* SYSCALLS *****************************************************************/ + + +BOOL APIENTRY +NtUserRegisterHotKey(HWND hWnd, + int id, + UINT fsModifiers, + UINT vk) +{ + PHOT_KEY pHotKey; + PWND pWnd = NULL; + PTHREADINFO pHotKeyThread; + BOOL bRet = FALSE; + + TRACE("Enter NtUserRegisterHotKey\n"); + + if (fsModifiers & ~(MOD_ALT|MOD_CONTROL|MOD_SHIFT|MOD_WIN)) // FIXME: Does Win2k3 support MOD_NOREPEAT? + { + WARN("Invalid modifiers: %x\n", fsModifiers); + EngSetLastError(ERROR_INVALID_FLAGS); + return 0; + } + + UserEnterExclusive(); + + /* Find hotkey thread */ + if (hWnd == NULL) + { + pHotKeyThread = gptiCurrent; + } + else + { + pWnd = UserGetWindowObject(hWnd); + if (!pWnd) + goto cleanup; + + pHotKeyThread = pWnd->head.pti; + + /* Fix wine msg "Window on another thread" test_hotkey */ + if (pWnd->head.pti != gptiCurrent) + { + EngSetLastError(ERROR_WINDOW_OF_OTHER_THREAD); + WARN("Must be from the same Thread.\n"); + goto cleanup; + } + } + + /* Check for existing hotkey */ + if (IsHotKey(fsModifiers, vk)) + { + EngSetLastError(ERROR_HOTKEY_ALREADY_REGISTERED); + WARN("Hotkey already exists\n"); + goto cleanup; + } + + /* Create new hotkey */ + pHotKey = ExAllocatePoolWithTag(PagedPool, sizeof(HOT_KEY), USERTAG_HOTKEY); + if (pHotKey == NULL) + { + EngSetLastError(ERROR_NOT_ENOUGH_MEMORY); + goto cleanup; + } + + pHotKey->pti = pHotKeyThread; + pHotKey->pWnd = pWnd; + pHotKey->fsModifiers = fsModifiers; + pHotKey->vk = vk; + pHotKey->id = id; + + /* Insert hotkey to the global list */ + pHotKey->pNext = gphkFirst; + gphkFirst = pHotKey; + + bRet = TRUE; + +cleanup: + TRACE("Leave NtUserRegisterHotKey, ret=%i\n", bRet); + UserLeave(); + return bRet; +} + + +BOOL APIENTRY +NtUserUnregisterHotKey(HWND hWnd, int id) +{ + PHOT_KEY pHotKey = gphkFirst, phkNext, *pLink = &gphkFirst; + BOOL bRet = FALSE; + PWND pWnd = NULL; + + TRACE("Enter NtUserUnregisterHotKey\n"); + UserEnterExclusive(); + + /* Fail if given window is invalid */ + if (hWnd && !(pWnd = UserGetWindowObject(hWnd))) + goto cleanup; + + while (pHotKey) + { + /* Save next ptr for later use */ + phkNext = pHotKey->pNext; + + /* Should we delete this hotkey? */ + if (pHotKey->pWnd == pWnd && pHotKey->id == id) + { + /* Update next ptr for previous hotkey and free memory */ + *pLink = phkNext; + ExFreePoolWithTag(pHotKey, USERTAG_HOTKEY); + + bRet = TRUE; + } + else /* This hotkey will stay, use its next ptr */ + pLink = &pHotKey->pNext; + + /* Move to the next entry */ + pHotKey = phkNext; + } + +cleanup: + TRACE("Leave NtUserUnregisterHotKey, ret=%i\n", bRet); + UserLeave(); + return bRet; +} + +/* EOF */ diff --cc win32ss/user/ntuser/message.c index eb1e1272117,00000000000..1df412b2361 mode 100644,000000..100644 --- a/win32ss/user/ntuser/message.c +++ b/win32ss/user/ntuser/message.c @@@ -1,2951 -1,0 +1,2951 @@@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Win32k subsystem + * PURPOSE: Messages + * FILE: subsystems/win32/win32k/ntuser/message.c + * PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net) + */ + +#include + +#include + +DBG_DEFAULT_CHANNEL(UserMsg); + +#define PM_BADMSGFLAGS ~((QS_RAWINPUT << 16)|PM_QS_SENDMESSAGE|PM_QS_PAINT|PM_QS_POSTMESSAGE|PM_QS_INPUT|PM_NOYIELD|PM_REMOVE) + +/* FUNCTIONS *****************************************************************/ + +NTSTATUS FASTCALL +IntInitMessageImpl(VOID) +{ + return STATUS_SUCCESS; +} + +NTSTATUS FASTCALL +IntCleanupMessageImpl(VOID) +{ + return STATUS_SUCCESS; +} + +/* From wine: */ +/* flag for messages that contain pointers */ +/* 32 messages per entry, messages 0..31 map to bits 0..31 */ + +#define SET(msg) (1 << ((msg) & 31)) + +static const unsigned int message_pointer_flags[] = +{ + /* 0x00 - 0x1f */ + SET(WM_CREATE) | SET(WM_SETTEXT) | SET(WM_GETTEXT) | + SET(WM_WININICHANGE) | SET(WM_DEVMODECHANGE), + /* 0x20 - 0x3f */ + SET(WM_GETMINMAXINFO) | SET(WM_DRAWITEM) | SET(WM_MEASUREITEM) | SET(WM_DELETEITEM) | + SET(WM_COMPAREITEM), + /* 0x40 - 0x5f */ + SET(WM_WINDOWPOSCHANGING) | SET(WM_WINDOWPOSCHANGED) | SET(WM_COPYDATA) | + SET(WM_COPYGLOBALDATA) | SET(WM_NOTIFY) | SET(WM_HELP), + /* 0x60 - 0x7f */ + SET(WM_STYLECHANGING) | SET(WM_STYLECHANGED), + /* 0x80 - 0x9f */ + SET(WM_NCCREATE) | SET(WM_NCCALCSIZE) | SET(WM_GETDLGCODE), + /* 0xa0 - 0xbf */ + SET(EM_GETSEL) | SET(EM_GETRECT) | SET(EM_SETRECT) | SET(EM_SETRECTNP), + /* 0xc0 - 0xdf */ + SET(EM_REPLACESEL) | SET(EM_GETLINE) | SET(EM_SETTABSTOPS), + /* 0xe0 - 0xff */ + SET(SBM_GETRANGE) | SET(SBM_SETSCROLLINFO) | SET(SBM_GETSCROLLINFO) | SET(SBM_GETSCROLLBARINFO), + /* 0x100 - 0x11f */ + 0, + /* 0x120 - 0x13f */ + 0, + /* 0x140 - 0x15f */ + SET(CB_GETEDITSEL) | SET(CB_ADDSTRING) | SET(CB_DIR) | SET(CB_GETLBTEXT) | + SET(CB_INSERTSTRING) | SET(CB_FINDSTRING) | SET(CB_SELECTSTRING) | + SET(CB_GETDROPPEDCONTROLRECT) | SET(CB_FINDSTRINGEXACT), + /* 0x160 - 0x17f */ + 0, + /* 0x180 - 0x19f */ + SET(LB_ADDSTRING) | SET(LB_INSERTSTRING) | SET(LB_GETTEXT) | SET(LB_SELECTSTRING) | + SET(LB_DIR) | SET(LB_FINDSTRING) | + SET(LB_GETSELITEMS) | SET(LB_SETTABSTOPS) | SET(LB_ADDFILE) | SET(LB_GETITEMRECT), + /* 0x1a0 - 0x1bf */ + SET(LB_FINDSTRINGEXACT), + /* 0x1c0 - 0x1df */ + 0, + /* 0x1e0 - 0x1ff */ + 0, + /* 0x200 - 0x21f */ + SET(WM_NEXTMENU) | SET(WM_SIZING) | SET(WM_MOVING) | SET(WM_DEVICECHANGE), + /* 0x220 - 0x23f */ + SET(WM_MDICREATE) | SET(WM_MDIGETACTIVE) | SET(WM_DROPOBJECT) | + SET(WM_QUERYDROPOBJECT) | SET(WM_DRAGLOOP) | SET(WM_DRAGSELECT) | SET(WM_DRAGMOVE), + /* 0x240 - 0x25f */ + 0, + /* 0x260 - 0x27f */ + 0, + /* 0x280 - 0x29f */ + 0, + /* 0x2a0 - 0x2bf */ + 0, + /* 0x2c0 - 0x2df */ + 0, + /* 0x2e0 - 0x2ff */ + 0, + /* 0x300 - 0x31f */ + SET(WM_ASKCBFORMATNAME) +}; + +/* check whether a given message type includes pointers */ +static inline int is_pointer_message( UINT message ) +{ + if (message >= 8*sizeof(message_pointer_flags)) return FALSE; + return (message_pointer_flags[message / 32] & SET(message)) != 0; +} +#undef SET + +#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 g_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_SETTINGCHANGE, MMS_SIZE_LPARAMSZ, MMS_FLAG_READ }, + { WM_COPYDATA, MMS_SIZE_SPECIAL, MMS_FLAG_READ }, + { WM_COPYGLOBALDATA, MMS_SIZE_WPARAM, MMS_FLAG_READ }, + { WM_WINDOWPOSCHANGED, sizeof(WINDOWPOS), MMS_FLAG_READWRITE }, + { WM_WINDOWPOSCHANGING, sizeof(WINDOWPOS), MMS_FLAG_READWRITE }, + { WM_SIZING, sizeof(RECT), MMS_FLAG_READWRITE }, + { WM_MOVING, sizeof(RECT), MMS_FLAG_READWRITE }, +}; + +static PMSGMEMORY FASTCALL +FindMsgMemory(UINT Msg) +{ + PMSGMEMORY MsgMemoryEntry; + + /* See if this message type is present in the table */ + for (MsgMemoryEntry = g_MsgMemory; + MsgMemoryEntry < g_MsgMemory + sizeof(g_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) + { + // WM_SETTEXT and WM_SETTINGCHANGE can be null! + if (!lParam) + { + TRACE("lParam is NULL!\n"); + Size = 0; + } + else + 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; + + default: + ASSERT(FALSE); + Size = 0; + break; + } + } + else + { + Size = MsgMemoryEntry->Size; + } + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + ERR("Exception caught in MsgMemorySize()! Status: 0x%x\n", _SEH2_GetExceptionCode()); + Size = 0; + } + _SEH2_END; + return Size; +} + +UINT lParamMemorySize(UINT Msg, WPARAM wParam, LPARAM lParam) +{ + PMSGMEMORY MsgMemoryEntry = FindMsgMemory(Msg); + if(MsgMemoryEntry == NULL) return 0; + return MsgMemorySize(MsgMemoryEntry, wParam, lParam); +} + +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) + { + ERR("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) + { + ERR("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; + SIZE_T size; + + MsgMemoryEntry = FindMsgMemory(Msg); + + if (!MsgMemoryEntry) + { + /* Keep previous behavior */ + return STATUS_SUCCESS; + } + size = MsgMemorySize(MsgMemoryEntry, wParam, lParam); + if (!size) + { + ERR("No size for lParamPacked\n"); + return STATUS_SUCCESS; + } + PackedData = ExAllocatePoolWithTag(NonPagedPool, size, TAG_MSG); + if (PackedData == NULL) + { + ERR("Not enough memory to pack lParam\n"); + return STATUS_NO_MEMORY; + } + 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); + ASSERT(MsgMemoryEntry); + + if (MsgMemoryEntry->Flags == MMS_FLAG_READWRITE) + { + //RtlCopyMemory((PVOID)lParam, (PVOID)lParamPacked, MsgMemoryEntry->Size); + } + ExFreePool((PVOID) lParamPacked); + return STATUS_SUCCESS; + } + + ASSERT(FALSE); + + return STATUS_INVALID_PARAMETER; +} + +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) + { + ERR("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)) + { + TRACE("Copy Message %d from usermode buffer\n", KernelModeMsg->message); + Status = MmCopyFromCaller(KernelMem, (PVOID) UserModeMsg->lParam, Size); + if (! NT_SUCCESS(Status)) + { + ERR("Failed to copy message to kernel: invalid usermode lParam 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)) + { + ERR("Failed to copy message from kernel: invalid usermode lParam buffer\n"); + ExFreePool((PVOID) KernelModeMsg->lParam); + return Status; + } + } + + ExFreePool((PVOID) KernelModeMsg->lParam); + } + + return STATUS_SUCCESS; +} + +// +// Wakeup any thread/process waiting on idle input. +// +VOID FASTCALL +IdlePing(VOID) +{ + PPROCESSINFO ppi = PsGetCurrentProcessWin32Process(); + PTHREADINFO pti; + + pti = PsGetCurrentThreadWin32Thread(); + + if ( pti ) + { + pti->pClientInfo->cSpins = 0; // Reset spins. + + if ( pti->pDeskInfo && pti == gptiForeground ) + { + if ( pti->fsHooks & HOOKID_TO_FLAG(WH_FOREGROUNDIDLE) || + pti->pDeskInfo->fsHooks & HOOKID_TO_FLAG(WH_FOREGROUNDIDLE) ) + { + co_HOOK_CallHooks(WH_FOREGROUNDIDLE,HC_ACTION,0,0); + } + } + } + + TRACE("IdlePing ppi %p\n", ppi); + if ( ppi && ppi->InputIdleEvent ) + { + TRACE("InputIdleEvent\n"); + KeSetEvent( ppi->InputIdleEvent, IO_NO_INCREMENT, FALSE); + } +} + +VOID FASTCALL +IdlePong(VOID) +{ + PPROCESSINFO ppi = PsGetCurrentProcessWin32Process(); + + TRACE("IdlePong ppi %p\n", ppi); + if ( ppi && ppi->InputIdleEvent ) + { + KeClearEvent(ppi->InputIdleEvent); + } +} + +UINT FASTCALL +GetWakeMask(UINT first, UINT last ) +{ + UINT mask = QS_POSTMESSAGE | QS_SENDMESSAGE; /* Always selected */ + + if (first || last) + { + if ((first <= WM_KEYLAST) && (last >= WM_KEYFIRST)) mask |= QS_KEY; + if ( ((first <= WM_MOUSELAST) && (last >= WM_MOUSEFIRST)) || + ((first <= WM_NCMOUSELAST) && (last >= WM_NCMOUSEFIRST)) ) mask |= QS_MOUSE; + if ((first <= WM_TIMER) && (last >= WM_TIMER)) mask |= QS_TIMER; + if ((first <= WM_SYSTIMER) && (last >= WM_SYSTIMER)) mask |= QS_TIMER; + if ((first <= WM_PAINT) && (last >= WM_PAINT)) mask |= QS_PAINT; + } + else mask = QS_ALLINPUT; + + return mask; +} + +static VOID FASTCALL +IntCallWndProc( PWND Window, HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) +{ + BOOL SameThread = FALSE; + CWPSTRUCT CWP; + + if (Window->head.pti == ((PTHREADINFO)PsGetCurrentThreadWin32Thread())) + SameThread = TRUE; + + 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 ( PWND Window, HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT *uResult) +{ + BOOL SameThread = FALSE; + CWPRETSTRUCT CWPR; + + if (Window->head.pti == ((PTHREADINFO)PsGetCurrentThreadWin32Thread())) + SameThread = TRUE; + + CWPR.hwnd = hWnd; + CWPR.message = Msg; + CWPR.wParam = wParam; + CWPR.lParam = lParam; + CWPR.lResult = uResult ? (*uResult) : 0; + co_HOOK_CallHooks( WH_CALLWNDPROCRET, HC_ACTION, SameThread, (LPARAM)&CWPR ); +} + +static LRESULT handle_internal_message( PWND pWnd, UINT msg, WPARAM wparam, LPARAM lparam ) +{ + LRESULT lRes; + USER_REFERENCE_ENTRY Ref; +// PTHREADINFO pti = PsGetCurrentThreadWin32Thread(); + + if (!pWnd || + pWnd == UserGetDesktopWindow() || // pWnd->fnid == FNID_DESKTOP + pWnd == UserGetMessageWindow() ) // pWnd->fnid == FNID_MESSAGEWND + return 0; + + TRACE("Internal Event Msg %p hWnd 0x%x\n",msg,pWnd->head.h); + + switch(msg) + { + case WM_ASYNC_SHOWWINDOW: + return co_WinPosShowWindow( pWnd, wparam ); + case WM_ASYNC_SETWINDOWPOS: + { + PWINDOWPOS winpos = (PWINDOWPOS)lparam; + if (!winpos) return 0; + lRes = co_WinPosSetWindowPos( pWnd, + winpos->hwndInsertAfter, + winpos->x, + winpos->y, + winpos->cx, + winpos->cy, + winpos->flags); + ExFreePoolWithTag(winpos, USERTAG_SWP); + return lRes; + } + case WM_ASYNC_SETACTIVEWINDOW: + { + PWND Window = (PWND)wparam; + if (wparam) UserRefObjectCo(Window, &Ref); + lRes = (LRESULT)co_IntSetActiveWindow(Window,(BOOL)lparam,TRUE,TRUE); + if (wparam) UserDerefObjectCo(Window); + return lRes; + } + } + return 0; +} + +LRESULT FASTCALL +IntDispatchMessage(PMSG pMsg) +{ + LARGE_INTEGER TickCount; + LONG Time; + LRESULT retval = 0; + PTHREADINFO pti; + PWND Window = NULL; + BOOL DoCallBack = TRUE; + + if (pMsg->hwnd) + { + Window = UserGetWindowObject(pMsg->hwnd); + if (!Window) return 0; + } + + pti = PsGetCurrentThreadWin32Thread(); + + if ( Window && Window->head.pti != pti) + { + EngSetLastError( ERROR_MESSAGE_SYNC_ONLY ); + return 0; + } + + if (((pMsg->message == WM_SYSTIMER) || + (pMsg->message == WM_TIMER)) && + (pMsg->lParam) ) + { + if (pMsg->message == WM_TIMER) + { + if (ValidateTimerCallback(pti,pMsg->lParam)) + { + KeQueryTickCount(&TickCount); + Time = MsqCalculateMessageTime(&TickCount); + retval = co_IntCallWindowProc((WNDPROC)pMsg->lParam, + TRUE, + pMsg->hwnd, + WM_TIMER, + pMsg->wParam, + (LPARAM)Time, + -1); + } + return retval; + } + 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 ) return 0; + + if (pMsg->message == WM_PAINT) Window->state |= WNDS_PAINTNOTPROCESSED; + + if ( Window->state & WNDS_SERVERSIDEWINDOWPROC ) + { + TRACE("Dispatch: Server Side Window Procedure\n"); + switch(Window->fnid) + { + case FNID_DESKTOP: + DoCallBack = !DesktopWindowProc( Window, + pMsg->message, + pMsg->wParam, + pMsg->lParam, + &retval); + break; + case FNID_MESSAGEWND: + DoCallBack = !UserMessageWindowProc( Window, + pMsg->message, + pMsg->wParam, + pMsg->lParam, + &retval); + break; + } + } + + /* Since we are doing a callback on the same thread right away, there is + no need to copy the lparam to kernel mode and then back to usermode. + We just pretend it isn't a pointer */ + + if (DoCallBack) + retval = co_IntCallWindowProc( Window->lpfnWndProc, + !Window->Unicode, + pMsg->hwnd, + pMsg->message, + pMsg->wParam, + pMsg->lParam, + -1); + + if (pMsg->message == WM_PAINT) + { + PREGION Rgn; + Window->state2 &= ~WNDS2_WMPAINTSENT; + /* send a WM_NCPAINT and WM_ERASEBKGND if the non-client area is still invalid */ + Rgn = IntSysCreateRectpRgn( 0, 0, 0, 0 ); + co_UserGetUpdateRgn( Window, Rgn, TRUE ); + REGION_Delete(Rgn); + } + + return retval; +} + +/* + * Internal version of PeekMessage() doing all the work + * + * MSDN: + * Sent messages + * Posted messages + * Input (hardware) messages and system internal events + * Sent messages (again) + * WM_PAINT messages + * WM_TIMER messages + */ +BOOL FASTCALL +co_IntPeekMessage( PMSG Msg, + PWND Window, + UINT MsgFilterMin, + UINT MsgFilterMax, + UINT RemoveMsg, + BOOL bGMSG ) +{ + PTHREADINFO pti; + LARGE_INTEGER LargeTickCount; + BOOL RemoveMessages; + UINT ProcessMask; + BOOL Hit = FALSE; + + pti = PsGetCurrentThreadWin32Thread(); + + RemoveMessages = RemoveMsg & PM_REMOVE; + ProcessMask = HIWORD(RemoveMsg); + + /* Hint, "If wMsgFilterMin and wMsgFilterMax are both zero, PeekMessage returns + all available messages (that is, no range filtering is performed)". */ + if (!ProcessMask) ProcessMask = (QS_ALLPOSTMESSAGE|QS_ALLINPUT); + + IdlePong(); + + do + { + KeQueryTickCount(&LargeTickCount); + pti->timeLast = LargeTickCount.u.LowPart; + pti->pcti->tickLastMsgChecked = LargeTickCount.u.LowPart; + + /* Dispatch sent messages here. */ + while ( co_MsqDispatchOneSentMessage(pti) ) + { + /* if some PM_QS* flags were specified, only handle sent messages from now on */ + if (HIWORD(RemoveMsg) && !bGMSG) Hit = TRUE; // wine does this; ProcessMask = QS_SENDMESSAGE; + } + if (Hit) return FALSE; + + /* Clear changed bits so we can wait on them if we don't find a message */ + if (ProcessMask & QS_POSTMESSAGE) + { + pti->pcti->fsChangeBits &= ~(QS_POSTMESSAGE | QS_HOTKEY | QS_TIMER); + if (MsgFilterMin == 0 && MsgFilterMax == 0) // Wine hack does this; ~0U) + { + pti->pcti->fsChangeBits &= ~QS_ALLPOSTMESSAGE; + } + } + + if (ProcessMask & QS_INPUT) + { + pti->pcti->fsChangeBits &= ~QS_INPUT; + } + + /* Now check for normal messages. */ + if (( (ProcessMask & QS_POSTMESSAGE) || + (ProcessMask & QS_HOTKEY) ) && + MsqPeekMessage( pti, + RemoveMessages, + Window, + MsgFilterMin, + MsgFilterMax, + ProcessMask, + Msg )) + { + return TRUE; + } + + /* Now look for a quit message. */ + if (pti->QuitPosted) + { + /* According to the PSDK, WM_QUIT messages are always returned, regardless + of the filter specified */ + Msg->hwnd = NULL; + Msg->message = WM_QUIT; + Msg->wParam = pti->exitCode; + Msg->lParam = 0; + if (RemoveMessages) + { + pti->QuitPosted = FALSE; + ClearMsgBitsMask(pti, QS_POSTMESSAGE); + pti->pcti->fsWakeBits &= ~QS_ALLPOSTMESSAGE; + pti->pcti->fsChangeBits &= ~QS_ALLPOSTMESSAGE; + } + return TRUE; + } + + if ((ProcessMask & QS_MOUSE) && + co_MsqPeekMouseMove( pti, + RemoveMessages, + Window, + MsgFilterMin, + MsgFilterMax, + Msg )) + { + return TRUE; + } + + /* Check for hardware events. */ + if ((ProcessMask & QS_INPUT) && + co_MsqPeekHardwareMessage( pti, + RemoveMessages, + Window, + MsgFilterMin, + MsgFilterMax, + ProcessMask, + Msg)) + { + return TRUE; + } + + /* Check for sent messages again. */ + while ( co_MsqDispatchOneSentMessage(pti) ) + { + if (HIWORD(RemoveMsg) && !bGMSG) Hit = TRUE; + } + if (Hit) return FALSE; + + /* Check for paint messages. */ + if ((ProcessMask & QS_PAINT) && + pti->cPaintsReady && + IntGetPaintMessage( Window, + MsgFilterMin, + MsgFilterMax, + pti, + Msg, + RemoveMessages)) + { + return TRUE; + } + + /* This is correct, check for the current threads timers waiting to be + posted to this threads message queue. If any we loop again. + */ + if ((ProcessMask & QS_TIMER) && + PostTimerMessages(Window)) + { + continue; + } + + return FALSE; + } + while (TRUE); + + return TRUE; +} + +static BOOL FASTCALL +co_IntWaitMessage( PWND Window, + UINT MsgFilterMin, + UINT MsgFilterMax ) +{ + PTHREADINFO pti; + NTSTATUS Status = STATUS_SUCCESS; + MSG Msg; + + pti = PsGetCurrentThreadWin32Thread(); + + do + { + if ( co_IntPeekMessage( &Msg, // Dont reenter! + Window, + MsgFilterMin, + MsgFilterMax, + MAKELONG( PM_NOREMOVE, GetWakeMask( MsgFilterMin, MsgFilterMax)), + TRUE ) ) // act like GetMessage. + { + return TRUE; + } + + /* Nothing found. Wait for new messages. */ + Status = co_MsqWaitForNewMessages( pti, + Window, + MsgFilterMin, + MsgFilterMax); + if (!NT_SUCCESS(Status)) + { + SetLastNtError(Status); + ERR("Exit co_IntWaitMessage on error!\n"); + return FALSE; + } + if (Status == STATUS_USER_APC || Status == STATUS_TIMEOUT) + { + return FALSE; + } + } + while ( TRUE ); + + return FALSE; +} + +BOOL FASTCALL +co_IntGetPeekMessage( PMSG pMsg, + HWND hWnd, + UINT MsgFilterMin, + UINT MsgFilterMax, + UINT RemoveMsg, + BOOL bGMSG ) +{ + PWND Window; + PTHREADINFO pti; + BOOL Present = FALSE; + NTSTATUS Status; + + 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 = (PWND)hWnd; + } + + if (MsgFilterMax < MsgFilterMin) + { + MsgFilterMin = 0; + MsgFilterMax = 0; + } + + if (bGMSG) + { + RemoveMsg |= ((GetWakeMask( MsgFilterMin, MsgFilterMax ))<< 16); + } + + pti = PsGetCurrentThreadWin32Thread(); + pti->pClientInfo->cSpins++; // Bump up the spin count. + + do + { + Present = co_IntPeekMessage( pMsg, + Window, + MsgFilterMin, + MsgFilterMax, + RemoveMsg, + bGMSG ); + if (Present) + { + /* GetMessage or PostMessage must never get messages that contain pointers */ + ASSERT(FindMsgMemory(pMsg->message) == NULL); + + if (pMsg->message != WM_PAINT && pMsg->message != WM_QUIT) + { + pti->timeLast = pMsg->time; + pti->ptLast = pMsg->pt; + } + + // The WH_GETMESSAGE hook enables an application to monitor messages about to + // be returned by the GetMessage or PeekMessage function. + + co_HOOK_CallHooks( WH_GETMESSAGE, HC_ACTION, RemoveMsg & PM_REMOVE, (LPARAM)pMsg); + + if ( bGMSG ) break; + } + + if ( bGMSG ) + { + Status = co_MsqWaitForNewMessages( pti, + Window, + MsgFilterMin, + MsgFilterMax); + if ( !NT_SUCCESS(Status) || + Status == STATUS_USER_APC || + Status == STATUS_TIMEOUT ) + { + Present = -1; + break; + } + } + else + { + if (!(RemoveMsg & PM_NOYIELD)) + { + IdlePing(); + // Yield this thread! + UserLeave(); + ZwYieldExecution(); + UserEnterExclusive(); + // Fall through to exit. + IdlePong(); + } + break; + } + } + while( bGMSG && !Present ); + + // Been spinning, time to swap vinyl... + if (pti->pClientInfo->cSpins >= 100) + { + // Clear the spin cycle to fix the mix. + pti->pClientInfo->cSpins = 0; + //if (!(pti->TIF_flags & TIF_SPINNING)) // FIXME: Need to swap vinyl... + } + return Present; +} + +BOOL FASTCALL +UserPostThreadMessage( PTHREADINFO pti, + UINT Msg, + WPARAM wParam, + LPARAM lParam ) +{ + MSG Message; + LARGE_INTEGER LargeTickCount; + + if (is_pointer_message(Msg)) + { + EngSetLastError(ERROR_MESSAGE_SYNC_ONLY ); + return FALSE; + } + Message.hwnd = NULL; + Message.message = Msg; + Message.wParam = wParam; + Message.lParam = lParam; + Message.pt = gpsi->ptCursor; + + KeQueryTickCount(&LargeTickCount); + Message.time = MsqCalculateMessageTime(&LargeTickCount); + MsqPostMessage(pti, &Message, FALSE, QS_POSTMESSAGE, 0); + return TRUE; +} + +PTHREADINFO FASTCALL +IntSendTo(PWND Window, PTHREADINFO ptiCur, UINT Msg) +{ + if ( ptiCur ) + { + if (!Window || + Window->head.pti->MessageQueue == ptiCur->MessageQueue ) + { + return NULL; + } + } + return Window ? Window->head.pti : NULL; +} + +BOOL FASTCALL +UserPostMessage( HWND Wnd, + UINT Msg, + WPARAM wParam, + LPARAM lParam ) +{ + PTHREADINFO pti; + MSG Message, KernelModeMsg; + LARGE_INTEGER LargeTickCount; + + Message.hwnd = Wnd; + Message.message = Msg; + Message.wParam = wParam; + Message.lParam = lParam; + Message.pt = gpsi->ptCursor; + KeQueryTickCount(&LargeTickCount); + Message.time = MsqCalculateMessageTime(&LargeTickCount); + + if (is_pointer_message(Message.message)) + { + EngSetLastError(ERROR_MESSAGE_SYNC_ONLY ); + return FALSE; + } + + if( Msg >= WM_DDE_FIRST && Msg <= WM_DDE_LAST ) + { + NTSTATUS Status; + PMSGMEMORY MsgMemoryEntry; + + MsgMemoryEntry = FindMsgMemory(Message.message); + + Status = CopyMsgToKernelMem(&KernelModeMsg, &Message, MsgMemoryEntry); + if (! NT_SUCCESS(Status)) + { + EngSetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + co_IntSendMessageNoWait(KernelModeMsg.hwnd, + KernelModeMsg.message, + KernelModeMsg.wParam, + KernelModeMsg.lParam); + + if (MsgMemoryEntry && KernelModeMsg.lParam) + ExFreePool((PVOID) KernelModeMsg.lParam); + + return TRUE; + } + + if (!Wnd) + { + pti = PsGetCurrentThreadWin32Thread(); + return UserPostThreadMessage( pti, + Msg, + wParam, + lParam); + } + if (Wnd == HWND_BROADCAST) + { + HWND *List; + PWND DesktopWindow; + ULONG i; + + DesktopWindow = UserGetDesktopWindow(); + List = IntWinListChildren(DesktopWindow); + + if (List != NULL) + { + UserPostMessage(DesktopWindow->head.h, Msg, wParam, lParam); + for (i = 0; List[i]; i++) + { + PWND pwnd = UserGetWindowObject(List[i]); + if (!pwnd) continue; + + if ( pwnd->fnid == FNID_MENU || // Also need pwnd->pcls->atomClassName == gaOleMainThreadWndClass + pwnd->pcls->atomClassName == gpsi->atomSysClass[ICLS_SWITCH] ) + continue; + + UserPostMessage(List[i], Msg, wParam, lParam); + } + ExFreePoolWithTag(List, USERTAG_WINDOWLIST); + } + } + else + { + PWND Window; + + Window = UserGetWindowObject(Wnd); + if ( !Window ) + { + ERR("UserPostMessage: Invalid handle 0x%p Msg %d!\n",Wnd,Msg); + return FALSE; + } + + pti = Window->head.pti; + if ( pti->TIF_flags & TIF_INCLEANUP ) + { + ERR("Attempted to post message to window %p when the thread is in cleanup!\n", Wnd); + return FALSE; + } + + if ( Window->state & WNDS_DESTROYED ) + { + ERR("Attempted to post message to window %p that is being destroyed!\n", Wnd); + /* FIXME: Last error code? */ + return FALSE; + } + + if (WM_QUIT == Msg) + { + MsqPostQuitMessage(pti, wParam); + } + else + { + MsqPostMessage(pti, &Message, FALSE, QS_POSTMESSAGE, 0); + } + } + 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 ) +{ + NTSTATUS Status; + PWND Window = NULL; + PMSGMEMORY MsgMemoryEntry; + INT lParamBufferSize; + LPARAM lParamPacked; + PTHREADINFO Win32Thread, ptiSendTo = NULL; + ULONG_PTR Hi, Lo, Result = 0; + DECLARE_RETURN(LRESULT); + USER_REFERENCE_ENTRY Ref; + BOOL DoCallBack = TRUE; + + if (!(Window = UserGetWindowObject(hWnd))) + { + TRACE("SendMessageTimeoutSingle: Invalid handle 0x%p!\n",hWnd); + RETURN( FALSE); + } + + UserRefObjectCo(Window, &Ref); + + Win32Thread = PsGetCurrentThreadWin32Thread(); + + ptiSendTo = IntSendTo(Window, Win32Thread, Msg); + + if ( !ptiSendTo ) + { + if (Win32Thread->TIF_flags & TIF_INCLEANUP) + { + /* Never send messages to exiting threads */ + RETURN( FALSE); + } + + if (Msg & 0x80000000) + { + TRACE("SMTS: Internal Message!\n"); + Result = (ULONG_PTR)handle_internal_message( Window, Msg, wParam, lParam ); + if (uResult) *uResult = Result; + RETURN( TRUE); + } + + // Only happens when calling the client! + IntCallWndProc( Window, hWnd, Msg, wParam, lParam); + + if ( Window->state & WNDS_SERVERSIDEWINDOWPROC ) + { + TRACE("SMT: Server Side Window Procedure\n"); + IoGetStackLimits(&Lo, &Hi); + // Handle it here. Safeguard against excessive recursions. + if (((ULONG_PTR)&uResult - Lo) < 4096 ) + { + ERR("Server Callback Exceeded Stack!\n"); + RETURN( FALSE); + } + /* Return after server side call, IntCallWndProcRet will not be called. */ + switch(Window->fnid) + { + case FNID_DESKTOP: + DoCallBack = !DesktopWindowProc(Window, Msg, wParam, lParam,(LRESULT*)&Result); + break; + case FNID_MESSAGEWND: + DoCallBack = !UserMessageWindowProc(Window, Msg, wParam, lParam,(LRESULT*)&Result); + break; + } + if (!DoCallBack) + { + if (uResult) *uResult = Result; + RETURN( TRUE); + } + } + /* 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 zero, do not allow callback on client side to allocate a buffer!!!!! See CORE-7695. + if (!lParamBufferSize) lParamBufferSize = -1; + } + + if (! NT_SUCCESS(PackParam(&lParamPacked, Msg, wParam, lParam, FALSE))) + { + ERR("Failed to pack message parameters\n"); + RETURN( FALSE); + } + + Result = (ULONG_PTR)co_IntCallWindowProc( Window->lpfnWndProc, + !Window->Unicode, + hWnd, + Msg, + wParam, + lParamPacked, + lParamBufferSize ); + if(uResult) + { + *uResult = Result; + } + + if (! NT_SUCCESS(UnpackParam(lParamPacked, Msg, wParam, lParam, FALSE))) + { + ERR("Failed to unpack message parameters\n"); + RETURN( TRUE); + } + + // Only happens when calling the client! + IntCallWndProcRet( Window, hWnd, Msg, wParam, lParam, (LRESULT *)uResult); + + RETURN( TRUE); + } + + if (uFlags & SMTO_ABORTIFHUNG && MsqIsHung(ptiSendTo)) + { + // FIXME: Set window hung and add to a list. + /* FIXME: Set a LastError? */ + RETURN( FALSE); + } + + if (Window->state & WNDS_DESTROYED) + { + /* FIXME: Last error? */ + ERR("Attempted to send message to window %p that is being destroyed!\n", hWnd); + RETURN( FALSE); + } + + do + { + Status = co_MsqSendMessage( ptiSendTo, + hWnd, + Msg, + wParam, + lParam, + uTimeout, + (uFlags & SMTO_BLOCK), + MSQ_NORMAL, + uResult ); + } + while ((STATUS_TIMEOUT == Status) && + (uFlags & SMTO_NOTIMEOUTIFNOTHUNG) && + !MsqIsHung(ptiSendTo)); // FIXME: Set window hung and add to a list. + + 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. + */ + EngSetLastError(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 ) +{ + PWND DesktopWindow; + HWND *Children; + HWND *Child; + + if (hWnd != HWND_BROADCAST && hWnd != HWND_TOPMOST) + { + return co_IntSendMessageTimeoutSingle(hWnd, Msg, wParam, lParam, uFlags, uTimeout, uResult); + } + + DesktopWindow = UserGetDesktopWindow(); + if (NULL == DesktopWindow) + { + EngSetLastError(ERROR_INTERNAL_ERROR); + return 0; + } + + if (hWnd != HWND_TOPMOST) + { + /* Send message to the desktop window too! */ + co_IntSendMessageTimeoutSingle(DesktopWindow->head.h, Msg, wParam, lParam, uFlags, uTimeout, uResult); + } + + Children = IntWinListChildren(DesktopWindow); + if (NULL == Children) + { + return 0; + } + + for (Child = Children; NULL != *Child; Child++) + { + if (hWnd == HWND_TOPMOST) + { + DesktopWindow = UserGetWindowObject(*Child); + if (DesktopWindow && DesktopWindow->ExStyle & WS_EX_TOPMOST) + { + ERR("HWND_TOPMOST Found\n"); + co_IntSendMessageTimeoutSingle(*Child, Msg, wParam, lParam, uFlags, uTimeout, uResult); + } + } + else + { + PWND pwnd = UserGetWindowObject(*Child); + if (!pwnd) continue; + + if ( pwnd->fnid == FNID_MENU || + pwnd->pcls->atomClassName == gpsi->atomSysClass[ICLS_SWITCH] ) + continue; + + 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; + return co_IntSendMessageWithCallBack( hWnd, + Msg, + wParam, + lParam, + NULL, + 0, + &Result); +} +/* MSDN: + If you send a message in the range below WM_USER to the asynchronous message + functions (PostMessage, SendNotifyMessage, and SendMessageCallback), its + message parameters cannot include pointers. Otherwise, the operation will fail. + The functions will return before the receiving thread has had a chance to + process the message and the sender will free the memory before it is used. +*/ +LRESULT FASTCALL +co_IntSendMessageWithCallBack( HWND hWnd, + UINT Msg, + WPARAM wParam, + LPARAM lParam, + SENDASYNCPROC CompletionCallback, + ULONG_PTR CompletionCallbackContext, + ULONG_PTR *uResult) +{ + ULONG_PTR Result; + PWND Window = NULL; + PMSGMEMORY MsgMemoryEntry; + INT lParamBufferSize; + LPARAM lParamPacked; + PTHREADINFO Win32Thread, ptiSendTo = NULL; + DECLARE_RETURN(LRESULT); + USER_REFERENCE_ENTRY Ref; + PUSER_SENT_MESSAGE Message; + BOOL DoCallBack = TRUE; + + if (!(Window = UserGetWindowObject(hWnd))) + { + TRACE("SendMessageWithCallBack: Invalid handle 0x%p!\n",hWnd); + RETURN(FALSE); + } + + UserRefObjectCo(Window, &Ref); + + if (Window->state & WNDS_DESTROYED) + { + /* FIXME: last error? */ + ERR("Attempted to send message to window %p that is being destroyed!\n", hWnd); + RETURN(FALSE); + } + + Win32Thread = PsGetCurrentThreadWin32Thread(); + + if (Win32Thread == NULL || + Win32Thread->TIF_flags & TIF_INCLEANUP) + { + RETURN(FALSE); + } + + ptiSendTo = IntSendTo(Window, Win32Thread, Msg); + + if (Msg & 0x80000000 && + !ptiSendTo) + { + if (Win32Thread->TIF_flags & TIF_INCLEANUP) RETURN( FALSE); + + TRACE("SMWCB: Internal Message!\n"); + Result = (ULONG_PTR)handle_internal_message( Window, Msg, wParam, lParam ); + if (uResult) *uResult = Result; + RETURN( TRUE); + } + + /* 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 (!lParamBufferSize) lParamBufferSize = -1; + } + + if (! NT_SUCCESS(PackParam(&lParamPacked, Msg, wParam, lParam, !!ptiSendTo))) + { + ERR("Failed to pack message parameters\n"); + RETURN( FALSE); + } + + /* If it can be sent now, then send it. */ + if ( !ptiSendTo ) + { + if (Win32Thread->TIF_flags & TIF_INCLEANUP) + { + UnpackParam(lParamPacked, Msg, wParam, lParam, FALSE); + /* Never send messages to exiting threads */ + RETURN(FALSE); + } + + IntCallWndProc( Window, hWnd, Msg, wParam, lParam); + + if ( Window->state & WNDS_SERVERSIDEWINDOWPROC ) + { + TRACE("SMWCB: Server Side Window Procedure\n"); + switch(Window->fnid) + { + case FNID_DESKTOP: + DoCallBack = !DesktopWindowProc(Window, Msg, wParam, lParamPacked, (LRESULT*)&Result); + break; + case FNID_MESSAGEWND: + DoCallBack = !UserMessageWindowProc(Window, Msg, wParam, lParam,(LRESULT*)&Result); + break; + } + } + + if (DoCallBack) + Result = (ULONG_PTR)co_IntCallWindowProc( Window->lpfnWndProc, + !Window->Unicode, + hWnd, + Msg, + wParam, + lParamPacked, + lParamBufferSize ); + if(uResult) + { + *uResult = Result; + } + + IntCallWndProcRet( Window, hWnd, Msg, wParam, lParam, (LRESULT *)uResult); + + if (CompletionCallback) + { + co_IntCallSentMessageCallback(CompletionCallback, + hWnd, + Msg, + CompletionCallbackContext, + Result); + } + } + + if ( !ptiSendTo) + { + if (! NT_SUCCESS(UnpackParam(lParamPacked, Msg, wParam, lParam, FALSE))) + { + ERR("Failed to unpack message parameters\n"); + } + RETURN(TRUE); + } + + if(!(Message = ExAllocatePoolWithTag(NonPagedPool, sizeof(USER_SENT_MESSAGE), TAG_USRMSG))) + { + ERR("MsqSendMessage(): Not enough memory to allocate a message"); + RETURN( FALSE); + } + + Message->Msg.hwnd = hWnd; + Message->Msg.message = Msg; + Message->Msg.wParam = wParam; + Message->Msg.lParam = lParamPacked; + Message->CompletionEvent = NULL; + Message->Result = 0; + Message->lResult = 0; + Message->QS_Flags = 0; + Message->ptiReceiver = ptiSendTo; + Message->ptiSender = NULL; // mjmartin, you are right! This is null. + Message->ptiCallBackSender = Win32Thread; + Message->DispatchingListEntry.Flink = NULL; + Message->CompletionCallback = CompletionCallback; + Message->CompletionCallbackContext = CompletionCallbackContext; + Message->HookMessage = MSQ_NORMAL; + Message->HasPackedLParam = (lParamBufferSize > 0); + Message->QS_Flags = QS_SENDMESSAGE; + + if (Msg & 0x80000000) // Higher priority event message! + InsertHeadList(&ptiSendTo->SentMessagesListHead, &Message->ListEntry); + else + InsertTailList(&ptiSendTo->SentMessagesListHead, &Message->ListEntry); + MsqWakeQueue(ptiSendTo, QS_SENDMESSAGE, TRUE); + + RETURN(TRUE); + +CLEANUP: + if (Window) UserDerefObjectCo(Window); + END_CLEANUP; +} + + +/* + This HACK 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; + PWND Window; + + if ( hWnd == HWND_BROADCAST ) + { + return 0; + } + + if(!(Window = UserGetWindowObject(hWnd))) + { + TRACE("PostOrSendMessage: Invalid handle 0x%p!\n",hWnd); + return 0; + } + + pti = PsGetCurrentThreadWin32Thread(); + + if ( IntSendTo(Window, pti, Msg) && + 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; +} + +static LRESULT FASTCALL +co_IntDoSendMessage( HWND hWnd, + UINT Msg, + WPARAM wParam, + LPARAM lParam, + PDOSENDMESSAGE dsm) +{ + LRESULT Result = TRUE; + NTSTATUS Status; + PWND Window = NULL; + MSG UserModeMsg, KernelModeMsg; + PMSGMEMORY MsgMemoryEntry; + PTHREADINFO ptiSendTo; + + if (hWnd != HWND_BROADCAST && hWnd != HWND_TOPMOST) + { + Window = UserGetWindowObject(hWnd); + if ( !Window ) + { + return 0; + } + } + + /* Check for an exiting window. */ + if (Window && Window->state & WNDS_DESTROYED) + { + ERR("co_IntDoSendMessage Window Exiting!\n"); + } + + /* See if the current thread can handle this message */ + ptiSendTo = IntSendTo(Window, gptiCurrent, Msg); + + // If broadcasting or sending to another thread, save the users data. + if (!Window || ptiSendTo ) + { + 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)) + { + EngSetLastError(ERROR_INVALID_PARAMETER); + return (dsm ? 0 : -1); + } + } + else + { + KernelModeMsg.hwnd = hWnd; + KernelModeMsg.message = Msg; + KernelModeMsg.wParam = wParam; + KernelModeMsg.lParam = lParam; + } + + 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 ); + } + + if (!Window || ptiSendTo ) + { + Status = CopyMsgToUserMem(&UserModeMsg, &KernelModeMsg); + if (!NT_SUCCESS(Status)) + { + EngSetLastError(ERROR_INVALID_PARAMETER); + return(dsm ? 0 : -1); + } + } + + return (LRESULT)Result; +} + +BOOL FASTCALL +UserSendNotifyMessage( HWND hWnd, + UINT Msg, + WPARAM wParam, + LPARAM lParam ) +{ + BOOL Ret = TRUE; + + if (is_pointer_message(Msg)) + { + EngSetLastError(ERROR_MESSAGE_SYNC_ONLY ); + return FALSE; + } + + // Basicly the same as IntPostOrSendMessage + if (hWnd == HWND_BROADCAST) // Handle Broadcast + { + HWND *List; + PWND DesktopWindow; + ULONG i; + + DesktopWindow = UserGetDesktopWindow(); + List = IntWinListChildren(DesktopWindow); + + if (List != NULL) + { + UserSendNotifyMessage(DesktopWindow->head.h, Msg, wParam, lParam); + for (i = 0; List[i]; i++) + { + PWND pwnd = UserGetWindowObject(List[i]); + if (!pwnd) continue; + + if ( pwnd->fnid == FNID_MENU || + pwnd->pcls->atomClassName == gpsi->atomSysClass[ICLS_SWITCH] ) + continue; + + Ret = UserSendNotifyMessage(List[i], Msg, wParam, lParam); + } + ExFreePool(List); + } + } + else + { + Ret = co_IntSendMessageNoWait( hWnd, Msg, wParam, lParam); + } + return Ret; +} + + +DWORD APIENTRY +IntGetQueueStatus(DWORD Changes) +{ + PTHREADINFO pti; + DWORD Result; + + pti = PsGetCurrentThreadWin32Thread(); +// wine: + Changes &= (QS_ALLINPUT|QS_ALLPOSTMESSAGE|QS_SMRESULT); + + /* High word, types of messages currently in the queue. + Low word, types of messages that have been added to the queue and that + are still in the queue + */ + Result = MAKELONG(pti->pcti->fsChangeBits & Changes, pti->pcti->fsWakeBits & Changes); + + pti->pcti->fsChangeBits &= ~Changes; + + return Result; +} + +BOOL APIENTRY +IntInitMessagePumpHook() +{ + PTHREADINFO pti = PsGetCurrentThreadWin32Thread(); + + if (pti->pcti) + { + pti->pcti->dwcPumpHook++; + return TRUE; + } + return FALSE; +} + +BOOL APIENTRY +IntUninitMessagePumpHook() +{ + PTHREADINFO pti = PsGetCurrentThreadWin32Thread(); + + if (pti->pcti) + { + if (pti->pcti->dwcPumpHook <= 0) + { + return FALSE; + } + pti->pcti->dwcPumpHook--; + return TRUE; + } + return FALSE; +} + +BOOL FASTCALL +IntCallMsgFilter( LPMSG lpmsg, INT code) +{ + BOOL Ret = FALSE; + + if ( co_HOOK_CallHooks( WH_SYSMSGFILTER, code, 0, (LPARAM)lpmsg)) + { + Ret = TRUE; + } + else + { + Ret = co_HOOK_CallHooks( WH_MSGFILTER, code, 0, (LPARAM)lpmsg); + } + return Ret; +} + +/** Functions ******************************************************************/ + +BOOL +APIENTRY +NtUserDragDetect( + HWND hWnd, + POINT pt) // Just like the User call. +{ + MSG msg; + RECT rect; + ULONG wDragWidth, wDragHeight; + DECLARE_RETURN(BOOL); + + TRACE("Enter NtUserDragDetect(%p)\n", hWnd); + UserEnterExclusive(); + + wDragWidth = UserGetSystemMetrics(SM_CXDRAG); + wDragHeight= UserGetSystemMetrics(SM_CYDRAG); + + rect.left = pt.x - wDragWidth; + rect.right = pt.x + wDragWidth; + + rect.top = pt.y - wDragHeight; + rect.bottom = pt.y + wDragHeight; + + co_UserSetCapture(hWnd); + + for (;;) + { + while (co_IntGetPeekMessage( &msg, 0, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE, FALSE ) || + co_IntGetPeekMessage( &msg, 0, WM_QUEUESYNC, WM_QUEUESYNC, PM_REMOVE, FALSE ) || + co_IntGetPeekMessage( &msg, 0, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE, FALSE ) ) + { + if ( msg.message == WM_LBUTTONUP ) + { + co_UserSetCapture(NULL); + RETURN( FALSE); + } + if ( msg.message == WM_MOUSEMOVE ) + { + POINT tmp; + tmp.x = (short)LOWORD(msg.lParam); + tmp.y = (short)HIWORD(msg.lParam); + if( !RECTL_bPointInRect( &rect, tmp.x, tmp.y ) ) + { + co_UserSetCapture(NULL); + RETURN( TRUE); + } + } + if ( msg.message == WM_KEYDOWN ) + { + if ( msg.wParam == VK_ESCAPE ) + { + co_UserSetCapture(NULL); + RETURN( TRUE); + } + } + if ( msg.message == WM_QUEUESYNC ) + { + co_HOOK_CallHooks( WH_CBT, HCBT_QS, 0, 0 ); + } + } + co_IntWaitMessage(NULL, 0, 0); + } + RETURN( FALSE); + +CLEANUP: + TRACE("Leave NtUserDragDetect, ret=%i\n",_ret_); + UserLeave(); + END_CLEANUP; +} + +BOOL APIENTRY +NtUserPostMessage(HWND hWnd, + UINT Msg, + WPARAM wParam, + LPARAM lParam) +{ + BOOL ret; + + UserEnterExclusive(); + + ret = UserPostMessage(hWnd, Msg, wParam, lParam); + + UserLeave(); + + return ret; +} + +BOOL APIENTRY +NtUserPostThreadMessage(DWORD idThread, + UINT Msg, + WPARAM wParam, + LPARAM lParam) +{ + BOOL ret = FALSE; + PETHREAD peThread; + PTHREADINFO pThread; + NTSTATUS Status; + + UserEnterExclusive(); + + 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 ); + goto exit; + } + ret = UserPostThreadMessage( pThread, Msg, wParam, lParam); + ObDereferenceObject( peThread ); + } + else + { + SetLastNtError( Status ); + } +exit: + UserLeave(); + return ret; +} + +BOOL APIENTRY +NtUserWaitMessage(VOID) +{ + BOOL ret; + + UserEnterExclusive(); + TRACE("NtUserWaitMessage Enter\n"); + ret = co_IntWaitMessage(NULL, 0, 0); + TRACE("NtUserWaitMessage Leave\n"); + UserLeave(); + + return ret; +} + +BOOL APIENTRY +NtUserGetMessage(PMSG pMsg, + HWND hWnd, + UINT MsgFilterMin, + UINT MsgFilterMax ) +{ + MSG Msg; + BOOL Ret; + + if ( (MsgFilterMin|MsgFilterMax) & ~WM_MAXIMUM ) + { + EngSetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + UserEnterExclusive(); + + RtlZeroMemory(&Msg, sizeof(MSG)); + + Ret = co_IntGetPeekMessage(&Msg, hWnd, MsgFilterMin, MsgFilterMax, PM_REMOVE, TRUE); + + UserLeave(); + - if (Ret == 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; + } + + if ((INT)Ret != -1) + Ret = Ret ? (WM_QUIT != pMsg->message) : FALSE; + + return Ret; +} + +BOOL APIENTRY +NtUserPeekMessage( PMSG pMsg, + HWND hWnd, + UINT MsgFilterMin, + UINT MsgFilterMax, + UINT RemoveMsg) +{ + MSG Msg; + BOOL Ret; + + if ( RemoveMsg & PM_BADMSGFLAGS ) + { + EngSetLastError(ERROR_INVALID_FLAGS); + return FALSE; + } + + UserEnterExclusive(); + + RtlZeroMemory(&Msg, sizeof(MSG)); + + Ret = co_IntGetPeekMessage(&Msg, hWnd, MsgFilterMin, MsgFilterMax, RemoveMsg, FALSE); + + UserLeave(); + + 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; +} + +BOOL APIENTRY +NtUserCallMsgFilter( LPMSG lpmsg, INT code) +{ + BOOL Ret = FALSE; + MSG Msg; + + _SEH2_TRY + { + ProbeForRead(lpmsg, sizeof(MSG), 1); + RtlCopyMemory( &Msg, lpmsg, sizeof(MSG)); + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + _SEH2_YIELD(return FALSE); + } + _SEH2_END; + + UserEnterExclusive(); + + if ( co_HOOK_CallHooks( WH_SYSMSGFILTER, code, 0, (LPARAM)&Msg)) + { + Ret = TRUE; + } + else + { + Ret = co_HOOK_CallHooks( WH_MSGFILTER, code, 0, (LPARAM)&Msg); + } + + UserLeave(); + + _SEH2_TRY + { + ProbeForWrite(lpmsg, sizeof(MSG), 1); + RtlCopyMemory(lpmsg, &Msg, sizeof(MSG)); + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + Ret = FALSE; + } + _SEH2_END; + + return Ret; +} + +LRESULT APIENTRY +NtUserDispatchMessage(PMSG UnsafeMsgInfo) +{ + LRESULT Res = 0; + MSG SafeMsg; + + _SEH2_TRY + { + ProbeForRead(UnsafeMsgInfo, sizeof(MSG), 1); + RtlCopyMemory(&SafeMsg, UnsafeMsgInfo, sizeof(MSG)); + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + SetLastNtError(_SEH2_GetExceptionCode()); + _SEH2_YIELD(return FALSE); + } + _SEH2_END; + + UserEnterExclusive(); + + Res = IntDispatchMessage(&SafeMsg); + + UserLeave(); + return Res; +} + +BOOL APIENTRY +NtUserTranslateMessage(LPMSG lpMsg, UINT flags) +{ + MSG SafeMsg; + BOOL Ret; + PWND pWnd; + + _SEH2_TRY + { + ProbeForRead(lpMsg, sizeof(MSG), 1); + RtlCopyMemory(&SafeMsg, lpMsg, sizeof(MSG)); + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + SetLastNtError(_SEH2_GetExceptionCode()); + _SEH2_YIELD(return FALSE); + } + _SEH2_END; + + UserEnterExclusive(); + pWnd = UserGetWindowObject(SafeMsg.hwnd); + if (pWnd) // Must have a window! + { + Ret = IntTranslateKbdMessage(&SafeMsg, flags); + } + else + { + TRACE("No Window for Translate. hwnd 0x%p Msg %u\n", SafeMsg.hwnd, SafeMsg.message); + Ret = FALSE; + } + UserLeave(); + + return Ret; +} + +LRESULT APIENTRY ScrollBarWndProc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam); + +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; + PWND Window = NULL; + USER_REFERENCE_ENTRY Ref; + + UserEnterExclusive(); + + switch(dwType) + { + case FNID_SCROLLBAR: + { + lResult = ScrollBarWndProc(hWnd, Msg, wParam, lParam); + break; + } + case FNID_DESKTOP: + { + Window = UserGetWindowObject(hWnd); + if (Window) + { + //ERR("FNID_DESKTOP IN\n"); + Ret = DesktopWindowProc(Window, Msg, wParam, lParam, &lResult); + //ERR("FNID_DESKTOP OUT\n"); + } + break; + } + + case FNID_MESSAGEWND: + { + Window = UserGetWindowObject(hWnd); + if (Window) + { + Ret = !UserMessageWindowProc(Window, Msg, wParam, lParam,&lResult); + } + break; + } + case FNID_DEFWINDOWPROC: + /* Validate input */ + if (hWnd) + { + Window = UserGetWindowObject(hWnd); + if (!Window) + { + UserLeave(); + return FALSE; + } + UserRefObjectCo(Window, &Ref); + } + lResult = IntDefWindowProc(Window, Msg, wParam, lParam, Ansi); + Ret = TRUE; + if (hWnd) + UserDerefObjectCo(Window); + break; + case FNID_SENDNOTIFYMESSAGE: + Ret = UserSendNotifyMessage(hWnd, Msg, wParam, lParam); + break; + case FNID_BROADCASTSYSTEMMESSAGE: + { + BROADCASTPARM parm, *retparam; + 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) + { + _SEH2_YIELD(break); + } + _SEH2_END; + } + else + break; + + if ( parm.recipients & BSM_ALLDESKTOPS || + parm.recipients == BSM_ALLCOMPONENTS ) + { + PLIST_ENTRY DesktopEntry; + PDESKTOP rpdesk; + HWND *List, hwndDenied = NULL; + HDESK hDesk = NULL; + PWND pwnd, pwndDesk; + ULONG i; + UINT fuFlags; + + for (DesktopEntry = InputWindowStation->DesktopListHead.Flink; + DesktopEntry != &InputWindowStation->DesktopListHead; + DesktopEntry = DesktopEntry->Flink) + { + rpdesk = CONTAINING_RECORD(DesktopEntry, DESKTOP, ListEntry); + pwndDesk = rpdesk->pDeskInfo->spwnd; + List = IntWinListChildren(pwndDesk); + + if (parm.flags & BSF_QUERY) + { + if (List != NULL) + { + if (parm.flags & BSF_FORCEIFHUNG || parm.flags & BSF_NOHANG) + { + fuFlags = SMTO_ABORTIFHUNG; + } + else if (parm.flags & BSF_NOTIMEOUTIFNOTHUNG) + { + fuFlags = SMTO_NOTIMEOUTIFNOTHUNG; + } + else + { + fuFlags = SMTO_NORMAL; + } + co_IntSendMessageTimeout( UserHMGetHandle(pwndDesk), + Msg, + wParam, + lParam, + fuFlags, + 2000, + &RetVal); + Ret = TRUE; + for (i = 0; List[i]; i++) + { + pwnd = UserGetWindowObject(List[i]); + if (!pwnd) continue; + + if ( pwnd->fnid == FNID_MENU || + pwnd->pcls->atomClassName == gpsi->atomSysClass[ICLS_SWITCH] ) + continue; + + if ( parm.flags & BSF_IGNORECURRENTTASK ) + { + if ( pwnd->head.pti == gptiCurrent ) + continue; + } + co_IntSendMessageTimeout( List[i], + Msg, + wParam, + lParam, + fuFlags, + 2000, + &RetVal); + + if (!RetVal && EngGetLastError() == ERROR_TIMEOUT) + { + if (!(parm.flags & BSF_FORCEIFHUNG)) + Ret = FALSE; + } + if (RetVal == BROADCAST_QUERY_DENY) + { + hwndDenied = List[i]; + hDesk = UserHMGetHandle(pwndDesk); + Ret = FALSE; + } + } + ExFreePoolWithTag(List, USERTAG_WINDOWLIST); + _SEH2_TRY + { + retparam = (PBROADCASTPARM) ResultInfo; + retparam->hDesk = hDesk; + retparam->hWnd = hwndDenied; + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + _SEH2_YIELD(break); + } + _SEH2_END; + if (!Ret) break; // Have a hit! Let everyone know! + } + } + else if (parm.flags & BSF_POSTMESSAGE) + { + if (List != NULL) + { + UserPostMessage(UserHMGetHandle(pwndDesk), Msg, wParam, lParam); + + for (i = 0; List[i]; i++) + { + pwnd = UserGetWindowObject(List[i]); + if (!pwnd) continue; + + if ( pwnd->fnid == FNID_MENU || + pwnd->pcls->atomClassName == gpsi->atomSysClass[ICLS_SWITCH] ) + continue; + + if ( parm.flags & BSF_IGNORECURRENTTASK ) + { + if ( pwnd->head.pti == gptiCurrent ) + continue; + } + UserPostMessage(List[i], Msg, wParam, lParam); + } + ExFreePoolWithTag(List, USERTAG_WINDOWLIST); + } + Ret = TRUE; + } + else + { + if (List != NULL) + { + UserSendNotifyMessage(UserHMGetHandle(pwndDesk), Msg, wParam, lParam); + + for (i = 0; List[i]; i++) + { + pwnd = UserGetWindowObject(List[i]); + if (!pwnd) continue; + + if ( pwnd->fnid == FNID_MENU || + pwnd->pcls->atomClassName == gpsi->atomSysClass[ICLS_SWITCH] ) + continue; + + if ( parm.flags & BSF_IGNORECURRENTTASK ) + { + if ( pwnd->head.pti == gptiCurrent ) + continue; + } + UserSendNotifyMessage(List[i], Msg, wParam, lParam); + } + ExFreePoolWithTag(List, USERTAG_WINDOWLIST); + } + Ret = TRUE; + } + } + } + else if (parm.recipients & BSM_APPLICATIONS) + { + HWND *List, hwndDenied = NULL; + HDESK hDesk = NULL; + PWND pwnd, pwndDesk; + ULONG i; + UINT fuFlags; + + pwndDesk = UserGetDesktopWindow(); + List = IntWinListChildren(pwndDesk); + + if (parm.flags & BSF_QUERY) + { + if (List != NULL) + { + if (parm.flags & BSF_FORCEIFHUNG || parm.flags & BSF_NOHANG) + { + fuFlags = SMTO_ABORTIFHUNG; + } + else if (parm.flags & BSF_NOTIMEOUTIFNOTHUNG) + { + fuFlags = SMTO_NOTIMEOUTIFNOTHUNG; + } + else + { + fuFlags = SMTO_NORMAL; + } + co_IntSendMessageTimeout( UserHMGetHandle(pwndDesk), + Msg, + wParam, + lParam, + fuFlags, + 2000, + &RetVal); + Ret = TRUE; + for (i = 0; List[i]; i++) + { + pwnd = UserGetWindowObject(List[i]); + if (!pwnd) continue; + + if ( pwnd->fnid == FNID_MENU || + pwnd->pcls->atomClassName == gpsi->atomSysClass[ICLS_SWITCH] ) + continue; + + if ( parm.flags & BSF_IGNORECURRENTTASK ) + { + if ( pwnd->head.pti == gptiCurrent ) + continue; + } + co_IntSendMessageTimeout( List[i], + Msg, + wParam, + lParam, + fuFlags, + 2000, + &RetVal); + + if (!RetVal && EngGetLastError() == ERROR_TIMEOUT) + { + if (!(parm.flags & BSF_FORCEIFHUNG)) + Ret = FALSE; + } + if (RetVal == BROADCAST_QUERY_DENY) + { + hwndDenied = List[i]; + hDesk = UserHMGetHandle(pwndDesk); + Ret = FALSE; + } + } + ExFreePoolWithTag(List, USERTAG_WINDOWLIST); + _SEH2_TRY + { + retparam = (PBROADCASTPARM) ResultInfo; + retparam->hDesk = hDesk; + retparam->hWnd = hwndDenied; + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + _SEH2_YIELD(break); + } + _SEH2_END; + } + } + else if (parm.flags & BSF_POSTMESSAGE) + { + if (List != NULL) + { + UserPostMessage(UserHMGetHandle(pwndDesk), Msg, wParam, lParam); + + for (i = 0; List[i]; i++) + { + pwnd = UserGetWindowObject(List[i]); + if (!pwnd) continue; + + if ( pwnd->fnid == FNID_MENU || + pwnd->pcls->atomClassName == gpsi->atomSysClass[ICLS_SWITCH] ) + continue; + + if ( parm.flags & BSF_IGNORECURRENTTASK ) + { + if ( pwnd->head.pti == gptiCurrent ) + continue; + } + UserPostMessage(List[i], Msg, wParam, lParam); + } + ExFreePoolWithTag(List, USERTAG_WINDOWLIST); + } + Ret = TRUE; + } + else + { + if (List != NULL) + { + UserSendNotifyMessage(UserHMGetHandle(pwndDesk), Msg, wParam, lParam); + + for (i = 0; List[i]; i++) + { + pwnd = UserGetWindowObject(List[i]); + if (!pwnd) continue; + + if ( pwnd->fnid == FNID_MENU || + pwnd->pcls->atomClassName == gpsi->atomSysClass[ICLS_SWITCH] ) + continue; + + if ( parm.flags & BSF_IGNORECURRENTTASK ) + { + if ( pwnd->head.pti == gptiCurrent ) + continue; + } + UserSendNotifyMessage(List[i], Msg, wParam, lParam); + } + ExFreePoolWithTag(List, USERTAG_WINDOWLIST); + } + Ret = TRUE; + } + } + } + break; + case FNID_SENDMESSAGECALLBACK: + { + CALL_BACK_INFO CallBackInfo; + ULONG_PTR uResult; + + _SEH2_TRY + { + ProbeForRead((PVOID)ResultInfo, sizeof(CALL_BACK_INFO), 1); + RtlCopyMemory(&CallBackInfo, (PVOID)ResultInfo, sizeof(CALL_BACK_INFO)); + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + _SEH2_YIELD(break); + } + _SEH2_END; + + if (is_pointer_message(Msg)) + { + EngSetLastError(ERROR_MESSAGE_SYNC_ONLY ); + break; + } + + if (!(Ret = co_IntSendMessageWithCallBack(hWnd, Msg, wParam, lParam, + CallBackInfo.CallBack, CallBackInfo.Context, &uResult))) + { + ERR("Callback failure!\n"); + } + } + break; + case FNID_SENDMESSAGE: + { + Ret = co_IntDoSendMessage(hWnd, Msg, wParam, lParam, 0); + + if (ResultInfo) + { + _SEH2_TRY + { + ProbeForWrite((PVOID)ResultInfo, sizeof(ULONG_PTR), 1); + RtlCopyMemory((PVOID)ResultInfo, &Ret, sizeof(ULONG_PTR)); + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + Ret = FALSE; + _SEH2_YIELD(break); + } + _SEH2_END; + } + break; + } + case FNID_SENDMESSAGEFF: + case FNID_SENDMESSAGEWTOOPTION: + { + DOSENDMESSAGE dsm, *pdsm = (PDOSENDMESSAGE)ResultInfo; + if (ResultInfo) + { + _SEH2_TRY + { + ProbeForRead(pdsm, sizeof(DOSENDMESSAGE), 1); + RtlCopyMemory(&dsm, pdsm, sizeof(DOSENDMESSAGE)); + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + _SEH2_YIELD(break); + } + _SEH2_END; + } + + Ret = co_IntDoSendMessage( hWnd, Msg, wParam, lParam, &dsm ); + + if (pdsm) + { + _SEH2_TRY + { + ProbeForWrite(pdsm, sizeof(DOSENDMESSAGE), 1); + RtlCopyMemory(pdsm, &dsm, sizeof(DOSENDMESSAGE)); + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + Ret = FALSE; + _SEH2_YIELD(break); + } + _SEH2_END; + } + break; + } + // CallNextHook bypass. + case FNID_CALLWNDPROC: + case FNID_CALLWNDPROCRET: + { + PTHREADINFO pti; + PCLIENTINFO ClientInfo; + PHOOK NextObj, Hook; + + pti = GetW32ThreadInfo(); + + Hook = pti->sphkCurrent; + + if (!Hook) break; + + NextObj = Hook->phkNext; + ClientInfo = pti->pClientInfo; + _SEH2_TRY + { + ClientInfo->phkCurrent = NextObj; + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + ClientInfo = NULL; + } + _SEH2_END; + + if (!ClientInfo || !NextObj) break; + + NextObj->phkNext = IntGetNextHook(NextObj); + + if ( Hook->HookId == WH_CALLWNDPROC) + { + CWPSTRUCT CWP; + CWP.hwnd = hWnd; + CWP.message = Msg; + CWP.wParam = wParam; + CWP.lParam = lParam; + TRACE("WH_CALLWNDPROC: Hook %p NextHook %p\n", Hook, NextObj); + + lResult = co_IntCallHookProc( Hook->HookId, + HC_ACTION, + ((ClientInfo->CI_flags & CI_CURTHPRHOOK) ? 1 : 0), + (LPARAM)&CWP, + Hook->Proc, + Hook->ihmod, + Hook->offPfn, + 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->ihmod, + Hook->offPfn, + Hook->Ansi, + &Hook->ModuleName); + } + } + break; + } + + switch(dwType) + { + case FNID_DEFWINDOWPROC: + case FNID_CALLWNDPROC: + case FNID_CALLWNDPROCRET: + case FNID_SCROLLBAR: + case FNID_DESKTOP: + if (ResultInfo) + { + _SEH2_TRY + { + ProbeForWrite((PVOID)ResultInfo, sizeof(LRESULT), 1); + RtlCopyMemory((PVOID)ResultInfo, &lResult, sizeof(LRESULT)); + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + Ret = FALSE; + } + _SEH2_END; + } + break; + default: + break; + } + + UserLeave(); + + return 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; + PTHREADINFO pti; + NTSTATUS Status; + HANDLE Handles[3]; + LARGE_INTEGER Timeout; + + UserEnterExclusive(); + + Status = ObReferenceObjectByHandle(hProcess, + PROCESS_QUERY_INFORMATION, + *PsProcessType, + UserMode, + (PVOID*)&Process, + NULL); + + if (!NT_SUCCESS(Status)) + { + UserLeave(); + SetLastNtError(Status); + return WAIT_FAILED; + } + + pti = PsGetCurrentThreadWin32Thread(); + + W32Process = (PPROCESSINFO)Process->Win32Process; + + if ( PsGetProcessExitProcessCalled(Process) || + !W32Process || + pti->ppi == W32Process) + { + ObDereferenceObject(Process); + UserLeave(); + EngSetLastError(ERROR_INVALID_PARAMETER); + return WAIT_FAILED; + } + + Handles[0] = Process; + Handles[1] = W32Process->InputIdleEvent; + Handles[2] = pti->pEventQueueServer; // IntMsqSetWakeMask returns hEventQueueClient + + if (!Handles[1]) + { + ObDereferenceObject(Process); + UserLeave(); + return STATUS_SUCCESS; /* no event to wait on */ + } + + if (dwMilliseconds != INFINITE) + Timeout.QuadPart = (LONGLONG) dwMilliseconds * (LONGLONG) -10000; + + W32Process->W32PF_flags |= W32PF_WAITFORINPUTIDLE; + for (pti = W32Process->ptiList; pti; pti = pti->ptiSibling) + { + pti->TIF_flags |= TIF_WAITFORINPUTIDLE; + pti->pClientInfo->dwTIFlags = pti->TIF_flags; + } + + TRACE("WFII: ppi %p\n", W32Process); + TRACE("WFII: waiting for %p\n", Handles[1] ); + + /* + * We must add a refcount to our current PROCESSINFO, + * because anything could happen (including process death) we're leaving win32k + */ + IntReferenceProcessInfo(W32Process); + + do + { + UserLeave(); + Status = KeWaitForMultipleObjects( 3, + 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: + goto WaitExit; + + case STATUS_WAIT_2: + { + MSG Msg; + co_IntGetPeekMessage( &Msg, 0, 0, 0, PM_REMOVE | PM_QS_SENDMESSAGE, FALSE); + ERR("WFII: WAIT 2\n"); + } + break; + + case STATUS_TIMEOUT: + ERR("WFII: timeout\n"); + case WAIT_FAILED: + goto WaitExit; + + default: + ERR("WFII: finished\n"); + Status = STATUS_SUCCESS; + goto WaitExit; + } + } + while (TRUE); + +WaitExit: + for (pti = W32Process->ptiList; pti; pti = pti->ptiSibling) + { + pti->TIF_flags &= ~TIF_WAITFORINPUTIDLE; + pti->pClientInfo->dwTIFlags = pti->TIF_flags; + } + W32Process->W32PF_flags &= ~W32PF_WAITFORINPUTIDLE; + IntDereferenceProcessInfo(W32Process); + ObDereferenceObject(Process); + UserLeave(); + return Status; +} + +/* EOF */ diff --cc win32ss/user/ntuser/msgqueue.c index ea922da26d5,00000000000..e82ca127a15 mode 100644,000000..100644 --- a/win32ss/user/ntuser/msgqueue.c +++ b/win32ss/user/ntuser/msgqueue.c @@@ -1,2327 -1,0 +1,2327 @@@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Win32k subsystem + * PURPOSE: Message queues + * FILE: subsystems/win32/win32k/ntuser/msgqueue.c + * PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net) + Alexandre Julliard + Maarten Lankhorst + */ + +#include +DBG_DEFAULT_CHANNEL(UserMsgQ); + +/* GLOBALS *******************************************************************/ + +static PPAGED_LOOKASIDE_LIST pgMessageLookasideList; +PUSER_MESSAGE_QUEUE gpqCursor; + +/* FUNCTIONS *****************************************************************/ + +INIT_FUNCTION +NTSTATUS +NTAPI +MsqInitializeImpl(VOID) +{ + pgMessageLookasideList = ExAllocatePoolWithTag(NonPagedPool, sizeof(PAGED_LOOKASIDE_LIST), TAG_USRMSG); + if(!pgMessageLookasideList) + return STATUS_NO_MEMORY; + ExInitializePagedLookasideList(pgMessageLookasideList, + NULL, + NULL, + 0, + sizeof(USER_MESSAGE), + TAG_USRMSG, + 256); + + return(STATUS_SUCCESS); +} + +PWND FASTCALL +IntChildrenWindowFromPoint(PWND pWndTop, INT x, INT y) +{ + PWND pWnd, pWndChild; + + if ( !pWndTop ) + { + pWndTop = UserGetDesktopWindow(); + if ( !pWndTop ) return NULL; + } + + if (!(pWndTop->style & WS_VISIBLE)) return NULL; + if ((pWndTop->style & WS_DISABLED)) return NULL; + if (!IntPtInWindow(pWndTop, x, y)) return NULL; + + if (RECTL_bPointInRect(&pWndTop->rcClient, x, y)) + { + for (pWnd = pWndTop->spwndChild; + pWnd != NULL; + pWnd = pWnd->spwndNext) + { + if (pWnd->state2 & WNDS2_INDESTROY || pWnd->state & WNDS_DESTROYED ) + { + TRACE("The Window is in DESTROY!\n"); + continue; + } + + pWndChild = IntChildrenWindowFromPoint(pWnd, x, y); + + if (pWndChild) + { + return pWndChild; + } + } + } + return pWndTop; +} + +PWND FASTCALL +IntTopLevelWindowFromPoint(INT x, INT y) +{ + PWND pWnd, pwndDesktop; + + /* Get the desktop window */ + pwndDesktop = UserGetDesktopWindow(); + if (!pwndDesktop) + return NULL; + + /* Loop all top level windows */ + for (pWnd = pwndDesktop->spwndChild; + pWnd != NULL; + pWnd = pWnd->spwndNext) + { + if (pWnd->state2 & WNDS2_INDESTROY || pWnd->state & WNDS_DESTROYED) + { + TRACE("The Window is in DESTROY!\n"); + continue; + } + + if ((pWnd->style & WS_VISIBLE) && IntPtInWindow(pWnd, x, y)) + return pWnd; + } + + /* Window has not been found */ + return pwndDesktop; +} + +PCURICON_OBJECT +FASTCALL +UserSetCursor( + PCURICON_OBJECT NewCursor, + BOOL ForceChange) +{ + PCURICON_OBJECT OldCursor; + HDC hdcScreen; + PTHREADINFO pti; + PUSER_MESSAGE_QUEUE MessageQueue; + PWND pWnd; + + pti = PsGetCurrentThreadWin32Thread(); + MessageQueue = pti->MessageQueue; + + /* Get the screen DC */ + if(!(hdcScreen = IntGetScreenDC())) + { + return NULL; + } + + OldCursor = MessageQueue->CursorObject; + + /* Check if cursors are different */ + if (OldCursor == NewCursor) + return OldCursor; + + /* Update cursor for this message queue */ + MessageQueue->CursorObject = NewCursor; + + /* If cursor is not visible we have nothing to do */ + if (MessageQueue->iCursorLevel < 0) + return OldCursor; + + /* Update cursor if this message queue controls it */ + pWnd = IntTopLevelWindowFromPoint(gpsi->ptCursor.x, gpsi->ptCursor.y); + if (pWnd && pWnd->head.pti->MessageQueue == MessageQueue) + { + if (NewCursor) + { + /* Call GDI to set the new screen cursor */ +#ifdef NEW_CURSORICON + PCURICON_OBJECT CursorFrame = NewCursor; + if(NewCursor->CURSORF_flags & CURSORF_ACON) + { + FIXME("Should animate the cursor, using only the first frame now.\n"); + CursorFrame = ((PACON)NewCursor)->aspcur[0]; + } + GreSetPointerShape(hdcScreen, + CursorFrame->hbmAlpha ? NULL : NewCursor->hbmMask, + CursorFrame->hbmAlpha ? NewCursor->hbmAlpha : NewCursor->hbmColor, + CursorFrame->xHotspot, + CursorFrame->yHotspot, + gpsi->ptCursor.x, + gpsi->ptCursor.y, + CursorFrame->hbmAlpha ? SPS_ALPHA : 0); +#else + GreSetPointerShape(hdcScreen, + NewCursor->IconInfo.hbmMask, + NewCursor->IconInfo.hbmColor, + NewCursor->IconInfo.xHotspot, + NewCursor->IconInfo.yHotspot, + gpsi->ptCursor.x, + gpsi->ptCursor.y, + 0); +#endif + } + else /* Note: OldCursor != NewCursor so we have to hide cursor */ + { + /* Remove the cursor */ + GreMovePointer(hdcScreen, -1, -1); + TRACE("Removing pointer!\n"); + } + IntGetSysCursorInfo()->CurrentCursorObject = NewCursor; + } + + /* Return the old cursor */ + return OldCursor; +} + +/* Called from NtUserCallOneParam with Routine ONEPARAM_ROUTINE_SHOWCURSOR + * User32 macro NtUserShowCursor */ +int UserShowCursor(BOOL bShow) +{ + HDC hdcScreen; + PTHREADINFO pti; + PUSER_MESSAGE_QUEUE MessageQueue; + PWND pWnd; + + if (!(hdcScreen = IntGetScreenDC())) + { + return -1; /* No mouse */ + } + + pti = PsGetCurrentThreadWin32Thread(); + MessageQueue = pti->MessageQueue; + + /* Update counter */ + MessageQueue->iCursorLevel += bShow ? 1 : -1; + pti->iCursorLevel += bShow ? 1 : -1; + + /* Check for trivial cases */ + if ((bShow && MessageQueue->iCursorLevel != 0) || + (!bShow && MessageQueue->iCursorLevel != -1)) + { + /* Note: w don't update global info here because it is used only + internally to check if cursor is visible */ + return MessageQueue->iCursorLevel; + } + + /* Check if cursor is above window owned by this MessageQueue */ + pWnd = IntTopLevelWindowFromPoint(gpsi->ptCursor.x, gpsi->ptCursor.y); + if (pWnd && pWnd->head.pti->MessageQueue == MessageQueue) + { + if (bShow) + { + /* Show the pointer */ + GreMovePointer(hdcScreen, gpsi->ptCursor.x, gpsi->ptCursor.y); + TRACE("Showing pointer!\n"); + } + else + { + /* Remove the pointer */ + GreMovePointer(hdcScreen, -1, -1); + TRACE("Removing pointer!\n"); + } + + /* Update global info */ + IntGetSysCursorInfo()->ShowingCursor = MessageQueue->iCursorLevel; + } + + return MessageQueue->iCursorLevel; +} + +DWORD FASTCALL +UserGetKeyState(DWORD dwKey) +{ + DWORD dwRet = 0; + PTHREADINFO pti; + PUSER_MESSAGE_QUEUE MessageQueue; + + pti = PsGetCurrentThreadWin32Thread(); + MessageQueue = pti->MessageQueue; + + if (dwKey < 0x100) + { + if (IS_KEY_DOWN(MessageQueue->afKeyState, dwKey)) + dwRet |= 0xFF80; // If down, windows returns 0xFF80. + if (IS_KEY_LOCKED(MessageQueue->afKeyState, dwKey)) + dwRet |= 0x1; + } + else + { + EngSetLastError(ERROR_INVALID_PARAMETER); + } + return dwRet; +} + +/* change the input key state for a given key */ +static VOID +UpdateKeyState(PUSER_MESSAGE_QUEUE MessageQueue, WORD wVk, BOOL bIsDown) +{ + TRACE("UpdateKeyState wVk: %u, bIsDown: %d\n", wVk, bIsDown); + + if (bIsDown) + { + /* If it's first key down event, xor lock bit */ + if (!IS_KEY_DOWN(MessageQueue->afKeyState, wVk)) + SET_KEY_LOCKED(MessageQueue->afKeyState, wVk, !IS_KEY_LOCKED(MessageQueue->afKeyState, wVk)); + + SET_KEY_DOWN(MessageQueue->afKeyState, wVk, TRUE); + MessageQueue->afKeyRecentDown[wVk / 8] |= (1 << (wVk % 8)); + } + else + SET_KEY_DOWN(MessageQueue->afKeyState, wVk, FALSE); +} + +/* update the input key state for a keyboard message */ +static VOID +UpdateKeyStateFromMsg(PUSER_MESSAGE_QUEUE MessageQueue, MSG* msg) +{ + UCHAR key; + BOOL down = FALSE; + + TRACE("UpdateKeyStateFromMsg message:%u\n", msg->message); + + switch (msg->message) + { + case WM_LBUTTONDOWN: + down = TRUE; + /* fall through */ + case WM_LBUTTONUP: + UpdateKeyState(MessageQueue, VK_LBUTTON, down); + break; + case WM_MBUTTONDOWN: + down = TRUE; + /* fall through */ + case WM_MBUTTONUP: + UpdateKeyState(MessageQueue, VK_MBUTTON, down); + break; + case WM_RBUTTONDOWN: + down = TRUE; + /* fall through */ + case WM_RBUTTONUP: + UpdateKeyState(MessageQueue, VK_RBUTTON, down); + break; + case WM_XBUTTONDOWN: + down = TRUE; + /* fall through */ + case WM_XBUTTONUP: + if (msg->wParam == XBUTTON1) + UpdateKeyState(MessageQueue, VK_XBUTTON1, down); + else if (msg->wParam == XBUTTON2) + UpdateKeyState(MessageQueue, VK_XBUTTON2, down); + break; + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + down = TRUE; + /* fall through */ + case WM_KEYUP: + case WM_SYSKEYUP: + key = (UCHAR)msg->wParam; + UpdateKeyState(MessageQueue, key, down); + switch(key) + { + case VK_LCONTROL: + case VK_RCONTROL: + down = IS_KEY_DOWN(MessageQueue->afKeyState, VK_LCONTROL) || IS_KEY_DOWN(MessageQueue->afKeyState, VK_RCONTROL); + UpdateKeyState(MessageQueue, VK_CONTROL, down); + break; + case VK_LMENU: + case VK_RMENU: + down = IS_KEY_DOWN(MessageQueue->afKeyState, VK_LMENU) || IS_KEY_DOWN(MessageQueue->afKeyState, VK_RMENU); + UpdateKeyState(MessageQueue, VK_MENU, down); + break; + case VK_LSHIFT: + case VK_RSHIFT: + down = IS_KEY_DOWN(MessageQueue->afKeyState, VK_LSHIFT) || IS_KEY_DOWN(MessageQueue->afKeyState, VK_RSHIFT); + UpdateKeyState(MessageQueue, VK_SHIFT, down); + break; + } + break; + } +} + +HANDLE FASTCALL +IntMsqSetWakeMask(DWORD WakeMask) +{ + PTHREADINFO Win32Thread; + HANDLE MessageEventHandle; + DWORD dwFlags = HIWORD(WakeMask); + + Win32Thread = PsGetCurrentThreadWin32Thread(); + if (Win32Thread == NULL || Win32Thread->MessageQueue == NULL) + return 0; + +// Win32Thread->pEventQueueServer; IntMsqSetWakeMask returns Win32Thread->hEventQueueClient + MessageEventHandle = Win32Thread->hEventQueueClient; + + if (Win32Thread->pcti) + { + if ( (Win32Thread->pcti->fsChangeBits & LOWORD(WakeMask)) || + ( (dwFlags & MWMO_INPUTAVAILABLE) && (Win32Thread->pcti->fsWakeBits & LOWORD(WakeMask)) ) ) + { + ERR("Chg 0x%x Wake 0x%x Mask 0x%x\n",Win32Thread->pcti->fsChangeBits, Win32Thread->pcti->fsWakeBits, WakeMask); + KeSetEvent(Win32Thread->pEventQueueServer, IO_NO_INCREMENT, FALSE); // Wake it up! + return MessageEventHandle; + } + } + + IdlePing(); + + return MessageEventHandle; +} + +BOOL FASTCALL +IntMsqClearWakeMask(VOID) +{ + PTHREADINFO Win32Thread; + + Win32Thread = PsGetCurrentThreadWin32Thread(); + if (Win32Thread == NULL || Win32Thread->MessageQueue == NULL) + return FALSE; + // Very hacky, but that is what they do. + Win32Thread->pcti->fsWakeBits = 0; + + IdlePong(); + + return TRUE; +} + +/* + Due to the uncertainty of knowing what was set in our multilevel message queue, + and even if the bits are all cleared. The same as cTimers/cPaintsReady. + I think this is the best solution... (jt) */ +VOID FASTCALL +MsqWakeQueue(PTHREADINFO pti, DWORD MessageBits, BOOL KeyEvent) +{ + PUSER_MESSAGE_QUEUE Queue; + + Queue = pti->MessageQueue; + + if (Queue->QF_flags & QF_INDESTROY) + { + ERR("This Message Queue is in Destroy!\n"); + } + pti->pcti->fsWakeBits |= MessageBits; + pti->pcti->fsChangeBits |= MessageBits; + + // Start bit accounting to help clear the main set of bits. + if (MessageBits & QS_KEY) + { + pti->nCntsQBits[QSRosKey]++; + } + if (MessageBits & QS_MOUSE) + { + if (MessageBits & QS_MOUSEMOVE) pti->nCntsQBits[QSRosMouseMove]++; + if (MessageBits & QS_MOUSEBUTTON) pti->nCntsQBits[QSRosMouseButton]++; + } + if (MessageBits & QS_POSTMESSAGE) pti->nCntsQBits[QSRosPostMessage]++; + if (MessageBits & QS_SENDMESSAGE) pti->nCntsQBits[QSRosSendMessage]++; + if (MessageBits & QS_HOTKEY) pti->nCntsQBits[QSRosHotKey]++; + if (MessageBits & QS_EVENT) pti->nCntsQBits[QSRosEvent]++; + + if (KeyEvent) + KeSetEvent(pti->pEventQueueServer, IO_NO_INCREMENT, FALSE); +} + +VOID FASTCALL +ClearMsgBitsMask(PTHREADINFO pti, UINT MessageBits) +{ + PUSER_MESSAGE_QUEUE Queue; + UINT ClrMask = 0; + + Queue = pti->MessageQueue; + + if (MessageBits & QS_KEY) + { + if (--pti->nCntsQBits[QSRosKey] == 0) ClrMask |= QS_KEY; + } + if (MessageBits & QS_MOUSEMOVE) // ReactOS hard coded. + { // Account for tracking mouse moves.. + if (--pti->nCntsQBits[QSRosMouseMove] == 0) ClrMask |= QS_MOUSEMOVE; + // Handle mouse move bits here. + if (Queue->MouseMoved) ClrMask |= QS_MOUSEMOVE; + } + if (MessageBits & QS_MOUSEBUTTON) + { + if (--pti->nCntsQBits[QSRosMouseButton] == 0) ClrMask |= QS_MOUSEBUTTON; + } + if (MessageBits & QS_POSTMESSAGE) + { + if (--pti->nCntsQBits[QSRosPostMessage] == 0) ClrMask |= QS_POSTMESSAGE; + } + if (MessageBits & QS_TIMER) // ReactOS hard coded. + { // Handle timer bits here. + if ( pti->cTimersReady ) + { + if (--pti->cTimersReady == 0) ClrMask |= QS_TIMER; + } + } + if (MessageBits & QS_PAINT) // ReactOS hard coded. + { // Handle paint bits here. + if ( pti->cPaintsReady ) + { + if (--pti->cPaintsReady == 0) ClrMask |= QS_PAINT; + } + } + if (MessageBits & QS_SENDMESSAGE) + { + if (--pti->nCntsQBits[QSRosSendMessage] == 0) ClrMask |= QS_SENDMESSAGE; + } + if (MessageBits & QS_HOTKEY) + { + if (--pti->nCntsQBits[QSRosHotKey] == 0) ClrMask |= QS_HOTKEY; + } + if (MessageBits & QS_EVENT) + { + if (--pti->nCntsQBits[QSRosEvent] == 0) ClrMask |= QS_EVENT; + } + + pti->pcti->fsWakeBits &= ~ClrMask; + pti->pcti->fsChangeBits &= ~ClrMask; +} + +VOID FASTCALL +MsqIncPaintCountQueue(PTHREADINFO pti) +{ + pti->cPaintsReady++; + MsqWakeQueue(pti, QS_PAINT, TRUE); +} + +VOID FASTCALL +MsqDecPaintCountQueue(PTHREADINFO pti) +{ + ClearMsgBitsMask(pti, QS_PAINT); +} + +VOID FASTCALL +MsqPostMouseMove(PTHREADINFO pti, MSG* Msg) +{ + pti->MessageQueue->MouseMoveMsg = *Msg; + pti->MessageQueue->MouseMoved = TRUE; + MsqWakeQueue(pti, QS_MOUSEMOVE, TRUE); +} + +VOID FASTCALL +co_MsqInsertMouseMessage(MSG* Msg, DWORD flags, ULONG_PTR dwExtraInfo, BOOL Hook) +{ + LARGE_INTEGER LargeTickCount; + MSLLHOOKSTRUCT MouseHookData; +// PDESKTOP pDesk; + PWND pwnd, pwndDesktop; + HDC hdcScreen; + PTHREADINFO pti; + PUSER_MESSAGE_QUEUE MessageQueue; + PSYSTEM_CURSORINFO CurInfo; + + KeQueryTickCount(&LargeTickCount); + Msg->time = MsqCalculateMessageTime(&LargeTickCount); + + MouseHookData.pt.x = LOWORD(Msg->lParam); + MouseHookData.pt.y = HIWORD(Msg->lParam); + switch (Msg->message) + { + case WM_MOUSEWHEEL: + MouseHookData.mouseData = MAKELONG(0, GET_WHEEL_DELTA_WPARAM(Msg->wParam)); + break; + case WM_XBUTTONDOWN: + case WM_XBUTTONUP: + case WM_XBUTTONDBLCLK: + case WM_NCXBUTTONDOWN: + case WM_NCXBUTTONUP: + case WM_NCXBUTTONDBLCLK: + MouseHookData.mouseData = MAKELONG(0, HIWORD(Msg->wParam)); + break; + default: + MouseHookData.mouseData = 0; + break; + } + + MouseHookData.flags = flags; // LLMHF_INJECTED + MouseHookData.time = Msg->time; + MouseHookData.dwExtraInfo = dwExtraInfo; + + /* If the hook procedure returned non zero, dont send the message */ + if (Hook) + { + if (co_HOOK_CallHooks(WH_MOUSE_LL, HC_ACTION, Msg->message, (LPARAM) &MouseHookData)) + return; + } + + /* Get the desktop window */ + pwndDesktop = UserGetDesktopWindow(); + if (!pwndDesktop) return; +// pDesk = pwndDesktop->head.rpdesk; + + /* Check if the mouse is captured */ + Msg->hwnd = IntGetCaptureWindow(); + if (Msg->hwnd != NULL) + { + pwnd = UserGetWindowObject(Msg->hwnd); + } + else + { + pwnd = IntTopLevelWindowFromPoint(Msg->pt.x, Msg->pt.y); + if (pwnd) Msg->hwnd = pwnd->head.h; + } + + hdcScreen = IntGetScreenDC(); + CurInfo = IntGetSysCursorInfo(); + + /* Check if we found a window */ + if (Msg->hwnd != NULL && pwnd != NULL) + { + pti = pwnd->head.pti; + MessageQueue = pti->MessageQueue; + // MessageQueue->ptiMouse = pti; + + if ( pti->TIF_flags & TIF_INCLEANUP || MessageQueue->QF_flags & QF_INDESTROY) + { + ERR("Mouse is over the Window Thread is Dead!\n"); + return; + } + + if (Msg->message == WM_MOUSEMOVE) + { + /* Check if cursor should be visible */ + if(hdcScreen && + MessageQueue->CursorObject && + MessageQueue->iCursorLevel >= 0) + { + /* Check if shape has changed */ + if(CurInfo->CurrentCursorObject != MessageQueue->CursorObject) + { + /* Call GDI to set the new screen cursor */ +#ifdef NEW_CURSORICON + GreSetPointerShape(hdcScreen, + MessageQueue->CursorObject->hbmAlpha ? + NULL : MessageQueue->CursorObject->hbmMask, + MessageQueue->CursorObject->hbmAlpha ? + MessageQueue->CursorObject->hbmAlpha : MessageQueue->CursorObject->hbmColor, + MessageQueue->CursorObject->xHotspot, + MessageQueue->CursorObject->yHotspot, + gpsi->ptCursor.x, + gpsi->ptCursor.y, + MessageQueue->CursorObject->hbmAlpha ? SPS_ALPHA : 0); +#else + GreSetPointerShape(hdcScreen, + MessageQueue->CursorObject->IconInfo.hbmMask, + MessageQueue->CursorObject->IconInfo.hbmColor, + MessageQueue->CursorObject->IconInfo.xHotspot, + MessageQueue->CursorObject->IconInfo.yHotspot, + gpsi->ptCursor.x, + gpsi->ptCursor.y, + 0); +#endif + } else + GreMovePointer(hdcScreen, Msg->pt.x, Msg->pt.y); + } + /* Check if w have to hide cursor */ + else if (CurInfo->ShowingCursor >= 0) + GreMovePointer(hdcScreen, -1, -1); + + /* Update global cursor info */ + CurInfo->ShowingCursor = MessageQueue->iCursorLevel; + CurInfo->CurrentCursorObject = MessageQueue->CursorObject; + gpqCursor = MessageQueue; + + /* Mouse move is a special case */ + MsqPostMouseMove(pti, Msg); + } + else + { + if (!IntGetCaptureWindow()) + { + // ERR("ptiLastInput is set\n"); + // ptiLastInput = pti; // Once this is set during Reboot or Shutdown, this prevents the exit window having foreground. + } + TRACE("Posting mouse message to hwnd=%p!\n", UserHMGetHandle(pwnd)); + MsqPostMessage(pti, Msg, TRUE, QS_MOUSEBUTTON, 0); + } + } + else if (hdcScreen) + { + /* always show cursor on background; FIXME: set default pointer */ + GreMovePointer(hdcScreen, Msg->pt.x, Msg->pt.y); + CurInfo->ShowingCursor = 0; + } +} + +PUSER_MESSAGE FASTCALL +MsqCreateMessage(LPMSG Msg) +{ + PUSER_MESSAGE Message; + + Message = ExAllocateFromPagedLookasideList(pgMessageLookasideList); + if (!Message) + { + return NULL; + } + + RtlMoveMemory(&Message->Msg, Msg, sizeof(MSG)); + + return Message; +} + +VOID FASTCALL +MsqDestroyMessage(PUSER_MESSAGE Message) +{ + ExFreeToPagedLookasideList(pgMessageLookasideList, Message); +} + +BOOLEAN FASTCALL +co_MsqDispatchOneSentMessage(PTHREADINFO pti) +{ + PUSER_SENT_MESSAGE SaveMsg, Message; + PLIST_ENTRY Entry; + BOOL Ret; + LRESULT Result = 0; + + if (IsListEmpty(&pti->SentMessagesListHead)) + { + return(FALSE); + } + + /* remove it from the list of pending messages */ + Entry = RemoveHeadList(&pti->SentMessagesListHead); + Message = CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry); + + SaveMsg = pti->pusmCurrent; + pti->pusmCurrent = Message; + + // Processing a message sent to it from another thread. + if ( ( Message->ptiSender && pti != Message->ptiSender) || + ( Message->ptiCallBackSender && pti != Message->ptiCallBackSender )) + { // most likely, but, to be sure. + pti->pcti->CTI_flags |= CTI_INSENDMESSAGE; // Let the user know... + } + + /* insert it to the list of messages that are currently dispatched by this + message queue */ + InsertTailList(&pti->LocalDispatchingMessagesHead, + &Message->ListEntry); + + ClearMsgBitsMask(pti, Message->QS_Flags); + + if (Message->HookMessage == MSQ_ISHOOK) + { // Direct Hook Call processor + Result = co_CallHook( Message->Msg.message, // HookId + (INT)(INT_PTR)Message->Msg.hwnd, // Code + Message->Msg.wParam, + Message->Msg.lParam); + } + else if (Message->HookMessage == MSQ_ISEVENT) + { // Direct Event Call processor + Result = co_EVENT_CallEvents( Message->Msg.message, + Message->Msg.hwnd, + Message->Msg.wParam, + Message->Msg.lParam); + } + else if(Message->HookMessage == MSQ_INJECTMODULE) + { + Result = IntLoadHookModule(Message->Msg.message, + (HHOOK)Message->Msg.lParam, + Message->Msg.wParam); + } + else if ((Message->CompletionCallback) && + (Message->ptiCallBackSender == pti)) + { /* Call the callback routine */ + if (Message->QS_Flags & QS_SMRESULT) + { + co_IntCallSentMessageCallback(Message->CompletionCallback, + Message->Msg.hwnd, + Message->Msg.message, + Message->CompletionCallbackContext, + Message->lResult); + /* Set callback to NULL to prevent reentry */ + Message->CompletionCallback = NULL; + } + else + { + /* The message has not been processed yet, reinsert it. */ + RemoveEntryList(&Message->ListEntry); + InsertTailList(&Message->ptiCallBackSender->SentMessagesListHead, &Message->ListEntry); + TRACE("Callback Message not processed yet. Requeuing the message\n"); + Ret = FALSE; + goto Exit; + } + } + else + { /* Call the window procedure. */ + Result = co_IntSendMessage( Message->Msg.hwnd, + Message->Msg.message, + Message->Msg.wParam, + Message->Msg.lParam); + } + + /* remove the message from the local dispatching list, because it doesn't need + to be cleaned up on thread termination anymore */ + RemoveEntryList(&Message->ListEntry); + + /* If the message is a callback, insert it in the callback senders MessageQueue */ + if (Message->CompletionCallback) + { + if (Message->ptiCallBackSender) + { + Message->lResult = Result; + Message->QS_Flags |= QS_SMRESULT; + + /* insert it in the callers message queue */ + InsertTailList(&Message->ptiCallBackSender->SentMessagesListHead, &Message->ListEntry); + MsqWakeQueue(Message->ptiCallBackSender, QS_SENDMESSAGE, TRUE); + } + Ret = TRUE; + goto Exit; + } + + /* remove the message from the dispatching list if needed, so lock the sender's message queue */ + if (Message->ptiSender) + { + if (Message->DispatchingListEntry.Flink != NULL) + { + /* only remove it from the dispatching list if not already removed by a timeout */ + RemoveEntryList(&Message->DispatchingListEntry); + } + } + /* still keep the sender's message queue locked, so the sender can't exit the + MsqSendMessage() function (if timed out) */ + + if (Message->QS_Flags & QS_SMRESULT) + { + Result = Message->lResult; + } + + /* Let the sender know the result. */ + if (Message->Result != NULL) + { + *Message->Result = Result; + } + - if (Message->HasPackedLParam == TRUE) ++ if (Message->HasPackedLParam) + { + if (Message->Msg.lParam) + ExFreePool((PVOID)Message->Msg.lParam); + } + + /* Notify the sender. */ + if (Message->CompletionEvent != NULL) + { + KeSetEvent(Message->CompletionEvent, IO_NO_INCREMENT, FALSE); + } + + /* free the message */ + ExFreePoolWithTag(Message, TAG_USRMSG); + Ret = TRUE; +Exit: + /* do not hangup on the user if this is reentering */ + if (!SaveMsg) pti->pcti->CTI_flags &= ~CTI_INSENDMESSAGE; + pti->pusmCurrent = SaveMsg; + + return Ret; +} + +VOID APIENTRY +MsqRemoveWindowMessagesFromQueue(PWND Window) +{ + PTHREADINFO pti; + PUSER_SENT_MESSAGE SentMessage; + PUSER_MESSAGE PostedMessage; + PLIST_ENTRY CurrentEntry, ListHead; + + ASSERT(Window); + + pti = Window->head.pti; + + /* remove the posted messages for this window */ + CurrentEntry = pti->PostedMessagesListHead.Flink; + ListHead = &pti->PostedMessagesListHead; + while (CurrentEntry != ListHead) + { + PostedMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE, + ListEntry); + if (PostedMessage->Msg.hwnd == Window->head.h) + { + RemoveEntryList(&PostedMessage->ListEntry); + ClearMsgBitsMask(pti, PostedMessage->QS_Flags); + MsqDestroyMessage(PostedMessage); + CurrentEntry = pti->PostedMessagesListHead.Flink; + } + else + { + CurrentEntry = CurrentEntry->Flink; + } + } + + /* remove the sent messages for this window */ + CurrentEntry = pti->SentMessagesListHead.Flink; + ListHead = &pti->SentMessagesListHead; + while (CurrentEntry != ListHead) + { + SentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE, + ListEntry); + if(SentMessage->Msg.hwnd == Window->head.h) + { + TRACE("Notify the sender and remove a message from the queue that had not been dispatched\n"); + + RemoveEntryList(&SentMessage->ListEntry); + ClearMsgBitsMask(pti, SentMessage->QS_Flags); + + /* Only if the message has a sender was the queue referenced */ + if ((SentMessage->ptiSender) + && (SentMessage->DispatchingListEntry.Flink != NULL)) + { + RemoveEntryList(&SentMessage->DispatchingListEntry); + } + + /* wake the sender's thread */ + if (SentMessage->CompletionEvent != NULL) + { + KeSetEvent(SentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE); + } + - if (SentMessage->HasPackedLParam == TRUE) ++ if (SentMessage->HasPackedLParam) + { + if (SentMessage->Msg.lParam) + ExFreePool((PVOID)SentMessage->Msg.lParam); + } + + /* free the message */ + ExFreePoolWithTag(SentMessage, TAG_USRMSG); + + CurrentEntry = pti->SentMessagesListHead.Flink; + } + else + { + CurrentEntry = CurrentEntry->Flink; + } + } +} + +BOOL FASTCALL +co_MsqSendMessageAsync(PTHREADINFO ptiReceiver, + HWND hwnd, + UINT Msg, + WPARAM wParam, + LPARAM lParam, + SENDASYNCPROC CompletionCallback, + ULONG_PTR CompletionCallbackContext, + BOOL HasPackedLParam, + INT HookMessage) +{ + + PTHREADINFO ptiSender; + PUSER_SENT_MESSAGE Message; + + if(!(Message = ExAllocatePoolWithTag(NonPagedPool, sizeof(USER_SENT_MESSAGE), TAG_USRMSG))) + { + ERR("MsqSendMessage(): Not enough memory to allocate a message"); + return FALSE; + } + + ptiSender = PsGetCurrentThreadWin32Thread(); + + Message->Msg.hwnd = hwnd; + Message->Msg.message = Msg; + Message->Msg.wParam = wParam; + Message->Msg.lParam = lParam; + Message->CompletionEvent = NULL; + Message->Result = 0; + Message->lResult = 0; + Message->ptiReceiver = ptiReceiver; + Message->ptiSender = NULL; + Message->ptiCallBackSender = ptiSender; + Message->DispatchingListEntry.Flink = NULL; + Message->CompletionCallback = CompletionCallback; + Message->CompletionCallbackContext = CompletionCallbackContext; + Message->HookMessage = HookMessage; + Message->HasPackedLParam = HasPackedLParam; + Message->QS_Flags = QS_SENDMESSAGE; + + InsertTailList(&ptiReceiver->SentMessagesListHead, &Message->ListEntry); + MsqWakeQueue(ptiReceiver, QS_SENDMESSAGE, TRUE); + + return TRUE; +} + +NTSTATUS FASTCALL +co_MsqSendMessage(PTHREADINFO ptirec, + HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam, + UINT uTimeout, BOOL Block, INT HookMessage, + ULONG_PTR *uResult) +{ + PTHREADINFO pti; + PUSER_SENT_MESSAGE Message; + KEVENT CompletionEvent; + NTSTATUS WaitStatus; + LARGE_INTEGER Timeout; + PLIST_ENTRY Entry; + PWND pWnd; + LRESULT Result = 0; //// Result could be trashed. //// + + pti = PsGetCurrentThreadWin32Thread(); + ASSERT(pti != ptirec); + ASSERT(ptirec->pcti); // Send must have a client side to receive it!!!! + + /* Don't send from or to a dying thread */ + if (pti->TIF_flags & TIF_INCLEANUP || ptirec->TIF_flags & TIF_INCLEANUP) + { + // Unless we are dying and need to tell our parents. + if (pti->TIF_flags & TIF_INCLEANUP && !(ptirec->TIF_flags & TIF_INCLEANUP)) + { + // Parent notify is the big one. Fire and forget! + TRACE("Send message from dying thread %d\n",Msg); + co_MsqSendMessageAsync(ptirec, Wnd, Msg, wParam, lParam, NULL, 0, FALSE, HookMessage); + } + if (uResult) *uResult = -1; + TRACE("MsqSM: Msg %d Current pti %lu or Rec pti %lu\n", Msg, pti->TIF_flags & TIF_INCLEANUP, ptirec->TIF_flags & TIF_INCLEANUP); + return STATUS_UNSUCCESSFUL; + } + + if ( HookMessage == MSQ_NORMAL ) + { + pWnd = ValidateHwndNoErr(Wnd); + + // These can not cross International Border lines! + if ( pti->ppi != ptirec->ppi && pWnd ) + { + switch(Msg) + { + // Handle the special case when working with password transfers across bordering processes. + case EM_GETLINE: + case EM_SETPASSWORDCHAR: + case WM_GETTEXT: + // Look for edit controls setup for passwords. + if ( gpsi->atomSysClass[ICLS_EDIT] == pWnd->pcls->atomClassName && // Use atomNVClassName. + pWnd->style & ES_PASSWORD ) + { + if (uResult) *uResult = -1; + ERR("Running across the border without a passport!\n"); + EngSetLastError(ERROR_ACCESS_DENIED); + return STATUS_UNSUCCESSFUL; + } + break; + case WM_NOTIFY: + if (uResult) *uResult = -1; + ERR("Running across the border without a passport!\n"); + return STATUS_UNSUCCESSFUL; + } + } + + // These can not cross State lines! + if ( Msg == WM_CREATE || Msg == WM_NCCREATE ) + { + if (uResult) *uResult = -1; + ERR("Can not tell the other State we have Create!\n"); + return STATUS_UNSUCCESSFUL; + } + } + + if(!(Message = ExAllocatePoolWithTag(PagedPool, sizeof(USER_SENT_MESSAGE), TAG_USRMSG))) + { + ERR("MsqSendMessage(): Not enough memory to allocate a message"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + KeInitializeEvent(&CompletionEvent, NotificationEvent, FALSE); + + Timeout.QuadPart = (LONGLONG) uTimeout * (LONGLONG) -10000; + + /* FIXME: Increase reference counter of sender's message queue here */ + + Message->Msg.hwnd = Wnd; + Message->Msg.message = Msg; + Message->Msg.wParam = wParam; + Message->Msg.lParam = lParam; + Message->CompletionEvent = &CompletionEvent; + Message->Result = &Result; + Message->lResult = 0; + Message->QS_Flags = 0; + Message->ptiReceiver = ptirec; + Message->ptiSender = pti; + Message->ptiCallBackSender = NULL; + Message->CompletionCallback = NULL; + Message->CompletionCallbackContext = 0; + Message->HookMessage = HookMessage; + Message->HasPackedLParam = FALSE; + + /* Add it to the list of pending messages */ + InsertTailList(&pti->DispatchingMessagesHead, &Message->DispatchingListEntry); + + /* Queue it in the destination's message queue */ + InsertTailList(&ptirec->SentMessagesListHead, &Message->ListEntry); + + Message->QS_Flags = QS_SENDMESSAGE; + MsqWakeQueue(ptirec, QS_SENDMESSAGE, TRUE); + + /* We can't access the Message anymore since it could have already been deleted! */ + + if(Block) + { + UserLeaveCo(); + + /* Don't process messages sent to the thread */ + WaitStatus = KeWaitForSingleObject(&CompletionEvent, UserRequest, UserMode, + FALSE, (uTimeout ? &Timeout : NULL)); + + UserEnterCo(); + + if(WaitStatus == STATUS_TIMEOUT) + { + /* Look up if the message has not yet dispatched, if so + make sure it can't pass a result and it must not set the completion event anymore */ + Entry = ptirec->SentMessagesListHead.Flink; + while (Entry != &ptirec->SentMessagesListHead) + { + if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry) + == Message) + { + /* We can access Message here, it's secure because the message queue is locked + and the message is still hasn't been dispatched */ + Message->CompletionEvent = NULL; + Message->Result = NULL; + break; + } + Entry = Entry->Flink; + } + + /* Remove from the local dispatching list so the other thread knows, + it can't pass a result and it must not set the completion event anymore */ + Entry = pti->DispatchingMessagesHead.Flink; + while (Entry != &pti->DispatchingMessagesHead) + { + if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, DispatchingListEntry) + == Message) + { + /* We can access Message here, it's secure because the sender's message is locked + and the message has definitely not yet been destroyed, otherwise it would + have been removed from this list by the dispatching routine right after + dispatching the message */ + Message->CompletionEvent = NULL; + Message->Result = NULL; + RemoveEntryList(&Message->DispatchingListEntry); + Message->DispatchingListEntry.Flink = NULL; + break; + } + Entry = Entry->Flink; + } + + TRACE("MsqSendMessage (blocked) timed out 1\n"); + } + while (co_MsqDispatchOneSentMessage(ptirec)) + ; + } + else + { + PVOID WaitObjects[3]; + + WaitObjects[0] = &CompletionEvent; // Wait 0 + WaitObjects[1] = pti->pEventQueueServer; // Wait 1 + WaitObjects[2] = ptirec->pEThread; // Wait 2 + + do + { + UserLeaveCo(); + + WaitStatus = KeWaitForMultipleObjects(3, WaitObjects, WaitAny, UserRequest, + UserMode, FALSE, (uTimeout ? &Timeout : NULL), NULL); + + UserEnterCo(); + + if(WaitStatus == STATUS_TIMEOUT) + { + /* Look up if the message has not yet been dispatched, if so + make sure it can't pass a result and it must not set the completion event anymore */ + Entry = ptirec->SentMessagesListHead.Flink; + while (Entry != &ptirec->SentMessagesListHead) + { + if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry) + == Message) + { + /* We can access Message here, it's secure because the message queue is locked + and the message is still hasn't been dispatched */ + Message->CompletionEvent = NULL; + Message->Result = NULL; + break; + } + Entry = Entry->Flink; + } + + /* Remove from the local dispatching list so the other thread knows, + it can't pass a result and it must not set the completion event anymore */ + Entry = pti->DispatchingMessagesHead.Flink; + while (Entry != &pti->DispatchingMessagesHead) + { + if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, DispatchingListEntry) + == Message) + { + /* We can access Message here, it's secure because the sender's message is locked + and the message has definitely not yet been destroyed, otherwise it would + have been removed from this list by the dispatching routine right after + dispatching the message */ + Message->CompletionEvent = NULL; + Message->Result = NULL; + RemoveEntryList(&Message->DispatchingListEntry); + Message->DispatchingListEntry.Flink = NULL; + break; + } + Entry = Entry->Flink; + } + + TRACE("MsqSendMessage timed out 2\n"); + break; + } + // Receiving thread passed on and left us hanging with issues still pending. + if ( WaitStatus == STATUS_WAIT_2 ) + { + ERR("Receiving Thread woken up dead!\n"); + Entry = pti->DispatchingMessagesHead.Flink; + while (Entry != &pti->DispatchingMessagesHead) + { + if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, DispatchingListEntry) + == Message) + { + Message->CompletionEvent = NULL; + Message->Result = NULL; + RemoveEntryList(&Message->DispatchingListEntry); + Message->DispatchingListEntry.Flink = NULL; + break; + } + Entry = Entry->Flink; + } + } + while (co_MsqDispatchOneSentMessage(pti)) + ; + } + while (NT_SUCCESS(WaitStatus) && WaitStatus == STATUS_WAIT_1); + } + + if(WaitStatus != STATUS_TIMEOUT) + if (uResult) *uResult = (STATUS_WAIT_0 == WaitStatus ? Result : -1); + + return WaitStatus; +} + +VOID FASTCALL +MsqPostMessage(PTHREADINFO pti, + MSG* Msg, + BOOLEAN HardwareMessage, + DWORD MessageBits, + DWORD dwQEvent) +{ + PUSER_MESSAGE Message; + PUSER_MESSAGE_QUEUE MessageQueue; + + if ( pti->TIF_flags & TIF_INCLEANUP || pti->MessageQueue->QF_flags & QF_INDESTROY ) + { + ERR("Post Msg; Thread or Q is Dead!\n"); + return; + } + + if(!(Message = MsqCreateMessage(Msg))) + { + return; + } + + MessageQueue = pti->MessageQueue; + + if (dwQEvent) + { + ERR("Post Msg; System Qeued Event Message!\n"); + InsertHeadList(&pti->PostedMessagesListHead, + &Message->ListEntry); + } + else if (!HardwareMessage) + { + InsertTailList(&pti->PostedMessagesListHead, + &Message->ListEntry); + } + else + { + InsertTailList(&MessageQueue->HardwareMessagesListHead, + &Message->ListEntry); + } + + if (Msg->message == WM_HOTKEY) MessageBits |= QS_HOTKEY; // Justin Case, just set it. + Message->dwQEvent = dwQEvent; + Message->QS_Flags = MessageBits; + Message->pti = pti; + MsqWakeQueue(pti, MessageBits, TRUE); +} + +VOID FASTCALL +MsqPostQuitMessage(PTHREADINFO pti, ULONG ExitCode) +{ + pti->QuitPosted = TRUE; + pti->exitCode = ExitCode; + MsqWakeQueue(pti, QS_POSTMESSAGE|QS_ALLPOSTMESSAGE, TRUE); +} + +/*********************************************************************** + * MsqSendParentNotify + * + * Send a WM_PARENTNOTIFY to all ancestors of the given window, unless + * the window has the WS_EX_NOPARENTNOTIFY style. + */ +static void MsqSendParentNotify( PWND pwnd, WORD event, WORD idChild, POINT pt ) +{ + PWND pwndDesktop = UserGetDesktopWindow(); + + /* pt has to be in the client coordinates of the parent window */ + pt.x += pwndDesktop->rcClient.left - pwnd->rcClient.left; + pt.y += pwndDesktop->rcClient.top - pwnd->rcClient.top; + + for (;;) + { + PWND pwndParent; + + if (!(pwnd->style & WS_CHILD)) break; + if (pwnd->ExStyle & WS_EX_NOPARENTNOTIFY) break; + if (!(pwndParent = IntGetParent(pwnd))) break; + if (pwndParent == pwndDesktop) break; + pt.x += pwnd->rcClient.left - pwndParent->rcClient.left; + pt.y += pwnd->rcClient.top - pwndParent->rcClient.top; + + pwnd = pwndParent; + co_IntSendMessage( UserHMGetHandle(pwnd), WM_PARENTNOTIFY, + MAKEWPARAM( event, idChild ), MAKELPARAM( pt.x, pt.y ) ); + } +} + +VOID +FASTCALL +IntTrackMouseMove(PWND pwndTrack, PDESKTOP pDesk, PMSG msg, USHORT hittest) +{ +// PWND pwndTrack = IntChildrenWindowFromPoint(pwndMsg, msg->pt.x, msg->pt.y); +// hittest = (USHORT)GetNCHitEx(pwndTrack, msg->pt); /// @todo WTF is this??? + + if ( pDesk->spwndTrack != pwndTrack || // Change with tracking window or + msg->message != WM_MOUSEMOVE || // Mouse click changes or + pDesk->htEx != hittest) // Change in current hit test states. + { + TRACE("ITMM: Track Mouse Move!\n"); + + /* Handle only the changing window track and mouse move across a border. */ + if ( pDesk->spwndTrack != pwndTrack || + (pDesk->htEx == HTCLIENT) ^ (hittest == HTCLIENT) ) + { + TRACE("ITMM: Another Wnd %d or Across Border %d\n", + pDesk->spwndTrack != pwndTrack,(pDesk->htEx == HTCLIENT) ^ (hittest == HTCLIENT)); + + if ( pDesk->dwDTFlags & DF_TME_LEAVE ) + UserPostMessage( UserHMGetHandle(pDesk->spwndTrack), + (pDesk->htEx != HTCLIENT) ? WM_NCMOUSELEAVE : WM_MOUSELEAVE, + 0, 0); + + if ( pDesk->dwDTFlags & DF_TME_HOVER ) + IntKillTimer(pDesk->spwndTrack, ID_EVENT_SYSTIMER_MOUSEHOVER, TRUE); + + /* Clear the flags to sign a change. */ + pDesk->dwDTFlags &= ~(DF_TME_LEAVE|DF_TME_HOVER); + } + /* Set the Track window and hit test. */ + pDesk->spwndTrack = pwndTrack; + pDesk->htEx = hittest; + } + + /* Reset, Same Track window, Hover set and Mouse Clicks or Clobbered Hover box. */ + if ( pDesk->spwndTrack == pwndTrack && + ( msg->message != WM_MOUSEMOVE || !RECTL_bPointInRect(&pDesk->rcMouseHover, msg->pt.x, msg->pt.y)) && + pDesk->dwDTFlags & DF_TME_HOVER ) + { + TRACE("ITMM: Reset Hover points!\n"); + // Restart timer for the hover period. + IntSetTimer(pDesk->spwndTrack, ID_EVENT_SYSTIMER_MOUSEHOVER, pDesk->dwMouseHoverTime, SystemTimerProc, TMRF_SYSTEM); + // Reset desktop mouse hover from the system default hover rectangle. + RECTL_vSetRect(&pDesk->rcMouseHover, + msg->pt.x - gspv.iMouseHoverWidth / 2, + msg->pt.y - gspv.iMouseHoverHeight / 2, + msg->pt.x + gspv.iMouseHoverWidth / 2, + msg->pt.y + gspv.iMouseHoverHeight / 2); + } +} + +BOOL co_IntProcessMouseMessage(MSG* msg, BOOL* RemoveMessages, UINT first, UINT last) +{ + MSG clk_msg; + POINT pt; + UINT message; + USHORT hittest; + EVENTMSG event; + MOUSEHOOKSTRUCT hook; + BOOL eatMsg = FALSE; + + PWND pwndMsg, pwndDesktop; + PUSER_MESSAGE_QUEUE MessageQueue; + PTHREADINFO pti; + PSYSTEM_CURSORINFO CurInfo; + PDESKTOP pDesk; + DECLARE_RETURN(BOOL); + + pti = PsGetCurrentThreadWin32Thread(); + pwndDesktop = UserGetDesktopWindow(); + MessageQueue = pti->MessageQueue; + CurInfo = IntGetSysCursorInfo(); + pwndMsg = ValidateHwndNoErr(msg->hwnd); + clk_msg = MessageQueue->msgDblClk; + pDesk = pwndDesktop->head.rpdesk; + + /* find the window to dispatch this mouse message to */ + if (MessageQueue->spwndCapture) + { + hittest = HTCLIENT; + pwndMsg = MessageQueue->spwndCapture; + if (pwndMsg) UserReferenceObject(pwndMsg); + } + else + { + pwndMsg = co_WinPosWindowFromPoint(NULL, &msg->pt, &hittest, FALSE);//TRUE); + } + + TRACE("Got mouse message for %p, hittest: 0x%x\n", msg->hwnd, hittest); + + if (pwndMsg == NULL || pwndMsg->head.pti != pti) + { + /* Remove and ignore the message */ + *RemoveMessages = TRUE; + RETURN(FALSE); + } + + if ( MessageQueue == gpqCursor ) // Cursor must use the same Queue! + { + IntTrackMouseMove(pwndMsg, pDesk, msg, hittest); + } + else + { + ERR("Not the same cursor!\n"); + } + + msg->hwnd = UserHMGetHandle(pwndMsg); + + pt = msg->pt; + message = msg->message; + /* Note: windows has no concept of a non-client wheel message */ + if (message != WM_MOUSEWHEEL) + { + if (hittest != HTCLIENT) + { + message += WM_NCMOUSEMOVE - WM_MOUSEMOVE; + msg->wParam = hittest; + } + else + { + /* coordinates don't get translated while tracking a menu */ + /* FIXME: should differentiate popups and top-level menus */ + if (!(MessageQueue->MenuOwner)) + { + pt.x += pwndDesktop->rcClient.left - pwndMsg->rcClient.left; + pt.y += pwndDesktop->rcClient.top - pwndMsg->rcClient.top; + } + } + } + msg->lParam = MAKELONG( pt.x, pt.y ); + + /* translate double clicks */ + + if ((msg->message == WM_LBUTTONDOWN) || + (msg->message == WM_RBUTTONDOWN) || + (msg->message == WM_MBUTTONDOWN) || + (msg->message == WM_XBUTTONDOWN)) + { + BOOL update = *RemoveMessages; + + /* translate double clicks - + * note that ...MOUSEMOVEs can slip in between + * ...BUTTONDOWN and ...BUTTONDBLCLK messages */ + + if ((MessageQueue->MenuOwner || MessageQueue->MoveSize) || + hittest != HTCLIENT || + (pwndMsg->pcls->style & CS_DBLCLKS)) + { + if ((msg->message == clk_msg.message) && + (msg->hwnd == clk_msg.hwnd) && + (msg->wParam == clk_msg.wParam) && + ((msg->time - clk_msg.time) < (ULONG)gspv.iDblClickTime) && + (abs(msg->pt.x - clk_msg.pt.x) < UserGetSystemMetrics(SM_CXDOUBLECLK)/2) && + (abs(msg->pt.y - clk_msg.pt.y) < UserGetSystemMetrics(SM_CYDOUBLECLK)/2)) + { + message += (WM_LBUTTONDBLCLK - WM_LBUTTONDOWN); + if (update) + { + MessageQueue->msgDblClk.message = 0; /* clear the double click conditions */ + update = FALSE; + } + } + } + + if (!((first == 0 && last == 0) || (message >= first || message <= last))) + { + TRACE("Message out of range!!!\n"); + RETURN(FALSE); + } + + /* update static double click conditions */ + if (update) MessageQueue->msgDblClk = *msg; + } + else + { + if (!((first == 0 && last == 0) || (message >= first || message <= last))) + { + TRACE("Message out of range!!!\n"); + RETURN(FALSE); + } + } + + if(gspv.bMouseClickLock) + { + BOOL IsClkLck = FALSE; + + if(msg->message == WM_LBUTTONUP) + { + IsClkLck = ((msg->time - CurInfo->ClickLockTime) >= gspv.dwMouseClickLockTime); + if (IsClkLck && (!CurInfo->ClickLockActive)) + { + CurInfo->ClickLockActive = TRUE; + } + } + else if (msg->message == WM_LBUTTONDOWN) + { + if (CurInfo->ClickLockActive) + { + IsClkLck = TRUE; + CurInfo->ClickLockActive = FALSE; + } + + CurInfo->ClickLockTime = msg->time; + } + + if(IsClkLck) + { + /* Remove and ignore the message */ + *RemoveMessages = TRUE; + RETURN(FALSE); + } + } + + /* message is accepted now (but may still get dropped) */ + + 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 ); + + hook.pt = msg->pt; + hook.hwnd = msg->hwnd; + hook.wHitTestCode = hittest; + hook.dwExtraInfo = 0 /* extra_info */ ; + if (co_HOOK_CallHooks( WH_MOUSE, *RemoveMessages ? HC_ACTION : HC_NOREMOVE, + message, (LPARAM)&hook )) + { + hook.pt = msg->pt; + hook.hwnd = msg->hwnd; + hook.wHitTestCode = hittest; + hook.dwExtraInfo = 0 /* extra_info */ ; + co_HOOK_CallHooks( WH_CBT, HCBT_CLICKSKIPPED, message, (LPARAM)&hook ); + + ERR("WH_MOUSE dropped mouse message!\n"); + + /* Remove and skip message */ + *RemoveMessages = TRUE; + RETURN(FALSE); + } + + if ((hittest == HTERROR) || (hittest == HTNOWHERE)) + { + co_IntSendMessage( msg->hwnd, WM_SETCURSOR, (WPARAM)msg->hwnd, + MAKELONG( hittest, msg->message )); + + /* Remove and skip message */ + *RemoveMessages = TRUE; + RETURN(FALSE); + } + + if ((*RemoveMessages == FALSE) || MessageQueue->spwndCapture) + { + /* Accept the message */ + msg->message = message; + RETURN(TRUE); + } + + if ((msg->message == WM_LBUTTONDOWN) || + (msg->message == WM_RBUTTONDOWN) || + (msg->message == WM_MBUTTONDOWN) || + (msg->message == WM_XBUTTONDOWN)) + { + /* Send the WM_PARENTNOTIFY, + * note that even for double/nonclient clicks + * notification message is still WM_L/M/RBUTTONDOWN. + */ + MsqSendParentNotify(pwndMsg, msg->message, 0, msg->pt ); + + /* Activate the window if needed */ + + if (pwndMsg != MessageQueue->spwndActive) + { + PWND pwndTop = pwndMsg; + pwndTop = IntGetNonChildAncestor(pwndTop); + + if (pwndTop && pwndTop != pwndDesktop) + { + LONG ret = co_IntSendMessage( msg->hwnd, + WM_MOUSEACTIVATE, + (WPARAM)UserHMGetHandle(pwndTop), + MAKELONG( hittest, msg->message)); + switch(ret) + { + case MA_NOACTIVATEANDEAT: + eatMsg = TRUE; + /* fall through */ + case MA_NOACTIVATE: + break; + case MA_ACTIVATEANDEAT: + eatMsg = TRUE; + /* fall through */ + case MA_ACTIVATE: + case 0: + if (!co_IntMouseActivateWindow( pwndTop )) eatMsg = TRUE; + break; + default: + ERR( "unknown WM_MOUSEACTIVATE code %d\n", ret ); + break; + } + } + } + } + + /* send the WM_SETCURSOR message */ + + /* Windows sends the normal mouse message as the message parameter + in the WM_SETCURSOR message even if it's non-client mouse message */ + co_IntSendMessage( msg->hwnd, WM_SETCURSOR, (WPARAM)msg->hwnd, MAKELONG( hittest, msg->message )); + + msg->message = message; + RETURN(!eatMsg); + +CLEANUP: + if(pwndMsg) + UserDereferenceObject(pwndMsg); + + END_CLEANUP; +} + +BOOL co_IntProcessKeyboardMessage(MSG* Msg, BOOL* RemoveMessages) +{ + EVENTMSG Event; + + if (Msg->message == WM_KEYDOWN || Msg->message == WM_SYSKEYDOWN || + Msg->message == WM_KEYUP || Msg->message == WM_SYSKEYUP) + { + switch (Msg->wParam) + { + case VK_LSHIFT: case VK_RSHIFT: + Msg->wParam = VK_SHIFT; + break; + case VK_LCONTROL: case VK_RCONTROL: + Msg->wParam = VK_CONTROL; + break; + case VK_LMENU: case VK_RMENU: + Msg->wParam = VK_MENU; + break; + } + } + + 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, + *RemoveMessages ? HC_ACTION : HC_NOREMOVE, + LOWORD(Msg->wParam), + Msg->lParam)) + { + /* skip this message */ + co_HOOK_CallHooks( WH_CBT, + HCBT_KEYSKIPPED, + LOWORD(Msg->wParam), + Msg->lParam ); + ERR("KeyboardMessage WH_CBT Call Hook return!\n"); + return FALSE; + } + return TRUE; +} + +BOOL co_IntProcessHardwareMessage(MSG* Msg, BOOL* RemoveMessages, UINT first, UINT last) +{ + if ( IS_MOUSE_MESSAGE(Msg->message)) + { + return co_IntProcessMouseMessage(Msg, RemoveMessages, first, last); + } + else if ( IS_KBD_MESSAGE(Msg->message)) + { + return co_IntProcessKeyboardMessage(Msg, RemoveMessages); + } + + return TRUE; +} + +BOOL APIENTRY +co_MsqPeekMouseMove(IN PTHREADINFO pti, + IN BOOL Remove, + IN PWND Window, + IN UINT MsgFilterLow, + IN UINT MsgFilterHigh, + OUT MSG* pMsg) +{ + BOOL AcceptMessage; + MSG msg; + PUSER_MESSAGE_QUEUE MessageQueue = pti->MessageQueue; + + if(!(MessageQueue->MouseMoved)) + return FALSE; + + if (!MessageQueue->ptiSysLock) + { + MessageQueue->ptiSysLock = pti; + pti->pcti->CTI_flags |= CTI_THREADSYSLOCK; + } + + if (MessageQueue->ptiSysLock != pti) + { + ERR("MsqPeekMouseMove: Thread Q is locked to another pti!\n"); + return FALSE; + } + + msg = MessageQueue->MouseMoveMsg; + + AcceptMessage = co_IntProcessMouseMessage(&msg, &Remove, MsgFilterLow, MsgFilterHigh); + + if(AcceptMessage) + *pMsg = msg; + + if(Remove) + { + ClearMsgBitsMask(pti, QS_MOUSEMOVE); + MessageQueue->MouseMoved = FALSE; + } + + MessageQueue->ptiSysLock = NULL; + pti->pcti->CTI_flags &= ~CTI_THREADSYSLOCK; + return AcceptMessage; +} + +/* check whether a message filter contains at least one potential hardware message */ +static INT FASTCALL +filter_contains_hw_range( UINT first, UINT last ) +{ + /* hardware message ranges are (in numerical order): + * WM_NCMOUSEFIRST .. WM_NCMOUSELAST + * WM_KEYFIRST .. WM_KEYLAST + * WM_MOUSEFIRST .. WM_MOUSELAST + */ + if (!last) --last; + if (last < WM_NCMOUSEFIRST) return 0; + if (first > WM_NCMOUSELAST && last < WM_KEYFIRST) return 0; + if (first > WM_KEYLAST && last < WM_MOUSEFIRST) return 0; + if (first > WM_MOUSELAST) return 0; + return 1; +} + +BOOL APIENTRY +co_MsqPeekHardwareMessage(IN PTHREADINFO pti, + IN BOOL Remove, + IN PWND Window, + IN UINT MsgFilterLow, + IN UINT MsgFilterHigh, + IN UINT QSflags, + OUT MSG* pMsg) +{ + + BOOL AcceptMessage; + PUSER_MESSAGE CurrentMessage; + PLIST_ENTRY ListHead, CurrentEntry = NULL; + MSG msg; + BOOL Ret = FALSE; + PUSER_MESSAGE_QUEUE MessageQueue = pti->MessageQueue; + + if (!filter_contains_hw_range( MsgFilterLow, MsgFilterHigh )) return FALSE; + + ListHead = &MessageQueue->HardwareMessagesListHead; + CurrentEntry = ListHead->Flink; + + if (IsListEmpty(CurrentEntry)) return FALSE; + + if (!MessageQueue->ptiSysLock) + { + MessageQueue->ptiSysLock = pti; + pti->pcti->CTI_flags |= CTI_THREADSYSLOCK; + } + + if (MessageQueue->ptiSysLock != pti) + { + ERR("MsqPeekHardwareMessage: Thread Q is locked to another pti!\n"); + return FALSE; + } + + CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE, + ListEntry); + do + { + if (IsListEmpty(CurrentEntry)) break; + if (!CurrentMessage) break; + CurrentEntry = CurrentMessage->ListEntry.Flink; + if (!CurrentEntry) break; //// Fix CORE-6734 reported crash. +/* + MSDN: + 1: any window that belongs to the current thread, and any messages on the current thread's message queue whose hwnd value is NULL. + 2: retrieves only messages on the current thread's message queue whose hwnd value is NULL. + 3: handle to the window whose messages are to be retrieved. + */ + if ( ( !Window || // 1 + ( Window == PWND_BOTTOM && CurrentMessage->Msg.hwnd == NULL ) || // 2 + ( Window != PWND_BOTTOM && Window->head.h == CurrentMessage->Msg.hwnd ) ) && // 3 + ( ( ( MsgFilterLow == 0 && MsgFilterHigh == 0 ) && CurrentMessage->QS_Flags & QSflags ) || + ( MsgFilterLow <= CurrentMessage->Msg.message && MsgFilterHigh >= CurrentMessage->Msg.message ) ) ) + { + msg = CurrentMessage->Msg; + + UpdateKeyStateFromMsg(MessageQueue, &msg); + AcceptMessage = co_IntProcessHardwareMessage(&msg, &Remove, MsgFilterLow, MsgFilterHigh); + + if (Remove) + { + RemoveEntryList(&CurrentMessage->ListEntry); + ClearMsgBitsMask(pti, CurrentMessage->QS_Flags); + MsqDestroyMessage(CurrentMessage); + } + + if (AcceptMessage) + { + *pMsg = msg; + Ret = TRUE; + break; + } + } + CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE, ListEntry); + } + while(CurrentEntry != ListHead); + + MessageQueue->ptiSysLock = NULL; + pti->pcti->CTI_flags &= ~CTI_THREADSYSLOCK; + return Ret; +} + +BOOLEAN APIENTRY +MsqPeekMessage(IN PTHREADINFO pti, + IN BOOLEAN Remove, + IN PWND Window, + IN UINT MsgFilterLow, + IN UINT MsgFilterHigh, + IN UINT QSflags, + OUT PMSG Message) +{ + PLIST_ENTRY CurrentEntry; + PUSER_MESSAGE CurrentMessage; + PLIST_ENTRY ListHead; + BOOL Ret = FALSE; + + CurrentEntry = pti->PostedMessagesListHead.Flink; + ListHead = &pti->PostedMessagesListHead; + + if (IsListEmpty(CurrentEntry)) return FALSE; + + CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE, + ListEntry); + do + { + if (IsListEmpty(CurrentEntry)) break; + if (!CurrentMessage) break; + CurrentEntry = CurrentEntry->Flink; +/* + MSDN: + 1: any window that belongs to the current thread, and any messages on the current thread's message queue whose hwnd value is NULL. + 2: retrieves only messages on the current thread's message queue whose hwnd value is NULL. + 3: handle to the window whose messages are to be retrieved. + */ + if ( ( !Window || // 1 + ( Window == PWND_BOTTOM && CurrentMessage->Msg.hwnd == NULL ) || // 2 + ( Window != PWND_BOTTOM && Window->head.h == CurrentMessage->Msg.hwnd ) ) && // 3 + ( ( ( MsgFilterLow == 0 && MsgFilterHigh == 0 ) && CurrentMessage->QS_Flags & QSflags ) || + ( MsgFilterLow <= CurrentMessage->Msg.message && MsgFilterHigh >= CurrentMessage->Msg.message ) ) ) + { + *Message = CurrentMessage->Msg; + + if (Remove) + { + RemoveEntryList(&CurrentMessage->ListEntry); + ClearMsgBitsMask(pti, CurrentMessage->QS_Flags); + MsqDestroyMessage(CurrentMessage); + } + Ret = TRUE; + break; + } + CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE, + ListEntry); + } + while (CurrentEntry != ListHead); + + return Ret; +} + +NTSTATUS FASTCALL +co_MsqWaitForNewMessages(PTHREADINFO pti, PWND WndFilter, + UINT MsgFilterMin, UINT MsgFilterMax) +{ + NTSTATUS ret; + UserLeaveCo(); + ret = KeWaitForSingleObject( pti->pEventQueueServer, + UserRequest, + UserMode, + FALSE, + NULL ); + UserEnterCo(); + return ret; +} + +BOOL FASTCALL +MsqIsHung(PTHREADINFO pti) +{ + LARGE_INTEGER LargeTickCount; + + KeQueryTickCount(&LargeTickCount); + return ((LargeTickCount.u.LowPart - pti->timeLast) > MSQ_HUNG); +} + +VOID +CALLBACK +HungAppSysTimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) +{ + DoTheScreenSaver(); + TRACE("HungAppSysTimerProc\n"); + // Process list of windows that are hung and waiting. +} + +BOOLEAN FASTCALL +MsqInitializeMessageQueue(PTHREADINFO pti, PUSER_MESSAGE_QUEUE MessageQueue) +{ + MessageQueue->CaretInfo = (PTHRDCARETINFO)(MessageQueue + 1); + InitializeListHead(&MessageQueue->HardwareMessagesListHead); // Keep here! + MessageQueue->spwndFocus = NULL; + MessageQueue->iCursorLevel = 0; + MessageQueue->CursorObject = NULL; + RtlCopyMemory(MessageQueue->afKeyState, gafAsyncKeyState, sizeof(gafAsyncKeyState)); + MessageQueue->ptiMouse = pti; + MessageQueue->ptiKeyboard = pti; + MessageQueue->cThreads++; + + return TRUE; +} + +VOID FASTCALL +MsqCleanupThreadMsgs(PTHREADINFO pti) +{ + PLIST_ENTRY CurrentEntry; + PUSER_MESSAGE CurrentMessage; + PUSER_SENT_MESSAGE CurrentSentMessage; + + /* cleanup posted messages */ + while (!IsListEmpty(&pti->PostedMessagesListHead)) + { + CurrentEntry = RemoveHeadList(&pti->PostedMessagesListHead); + CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE, + ListEntry); + MsqDestroyMessage(CurrentMessage); + } + + /* remove the messages that have not yet been dispatched */ + while (!IsListEmpty(&pti->SentMessagesListHead)) + { + CurrentEntry = RemoveHeadList(&pti->SentMessagesListHead); + CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE, + ListEntry); + + TRACE("Notify the sender and remove a message from the queue that had not been dispatched\n"); + /* Only if the message has a sender was the message in the DispatchingList */ + if ((CurrentSentMessage->ptiSender) + && (CurrentSentMessage->DispatchingListEntry.Flink != NULL)) + { + RemoveEntryList(&CurrentSentMessage->DispatchingListEntry); + } + + /* wake the sender's thread */ + if (CurrentSentMessage->CompletionEvent != NULL) + { + KeSetEvent(CurrentSentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE); + } + - if (CurrentSentMessage->HasPackedLParam == TRUE) ++ if (CurrentSentMessage->HasPackedLParam) + { + if (CurrentSentMessage->Msg.lParam) + ExFreePool((PVOID)CurrentSentMessage->Msg.lParam); + } + + /* free the message */ + ExFreePool(CurrentSentMessage); + } + + /* notify senders of dispatching messages. This needs to be cleaned up if e.g. + ExitThread() was called in a SendMessage() umode callback */ + while (!IsListEmpty(&pti->LocalDispatchingMessagesHead)) + { + CurrentEntry = RemoveHeadList(&pti->LocalDispatchingMessagesHead); + CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE, + ListEntry); + + /* remove the message from the dispatching list */ + if(CurrentSentMessage->DispatchingListEntry.Flink != NULL) + { + RemoveEntryList(&CurrentSentMessage->DispatchingListEntry); + } + + TRACE("Notify the sender, the thread has been terminated while dispatching a message!\n"); + + /* wake the sender's thread */ + if (CurrentSentMessage->CompletionEvent != NULL) + { + KeSetEvent(CurrentSentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE); + } + - if (CurrentSentMessage->HasPackedLParam == TRUE) ++ if (CurrentSentMessage->HasPackedLParam) + { + if (CurrentSentMessage->Msg.lParam) + ExFreePool((PVOID)CurrentSentMessage->Msg.lParam); + } + + /* free the message */ + ExFreePool(CurrentSentMessage); + } + + /* tell other threads not to bother returning any info to us */ + while (! IsListEmpty(&pti->DispatchingMessagesHead)) + { + CurrentEntry = RemoveHeadList(&pti->DispatchingMessagesHead); + CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE, + DispatchingListEntry); + CurrentSentMessage->CompletionEvent = NULL; + CurrentSentMessage->Result = NULL; + + /* do NOT dereference our message queue as it might get attempted to be + locked later */ + } + + // Clear it all out. + if (pti->pcti) + { + pti->pcti->fsWakeBits = 0; + pti->pcti->fsChangeBits = 0; + } + + pti->nCntsQBits[QSRosKey] = 0; + pti->nCntsQBits[QSRosMouseMove] = 0; + pti->nCntsQBits[QSRosMouseButton] = 0; + pti->nCntsQBits[QSRosPostMessage] = 0; + pti->nCntsQBits[QSRosSendMessage] = 0; + pti->nCntsQBits[QSRosHotKey] = 0; + pti->nCntsQBits[QSRosEvent] = 0; +} + +VOID FASTCALL +MsqCleanupMessageQueue(PTHREADINFO pti) +{ + PUSER_MESSAGE_QUEUE MessageQueue; + + MessageQueue = pti->MessageQueue; + MessageQueue->cThreads--; + + if (MessageQueue->cThreads) + { + if (MessageQueue->ptiSysLock == pti) MessageQueue->ptiSysLock = NULL; + } + + if (MessageQueue->CursorObject) + { + PCURICON_OBJECT pCursor = MessageQueue->CursorObject; + + /* Change to another cursor if we going to dereference current one + Note: we can't use UserSetCursor because it uses current thread + message queue instead of queue given for cleanup */ + if (IntGetSysCursorInfo()->CurrentCursorObject == pCursor) + { + HDC hdcScreen; + + /* Get the screen DC */ + hdcScreen = IntGetScreenDC(); + if (hdcScreen) + GreMovePointer(hdcScreen, -1, -1); + IntGetSysCursorInfo()->CurrentCursorObject = NULL; + } + + TRACE("DereferenceObject pCursor\n"); + UserDereferenceObject(pCursor); + } + + if (gpqForeground == MessageQueue) + { + IntSetFocusMessageQueue(NULL); + } + if (gpqForegroundPrev == MessageQueue) + { + gpqForegroundPrev = NULL; + } + if (gpqCursor == MessageQueue) + { + gpqCursor = NULL; + } +} + +PUSER_MESSAGE_QUEUE FASTCALL +MsqCreateMessageQueue(PTHREADINFO pti) +{ + PUSER_MESSAGE_QUEUE MessageQueue; + + MessageQueue = (PUSER_MESSAGE_QUEUE)ExAllocatePoolWithTag(NonPagedPool, + sizeof(USER_MESSAGE_QUEUE) + sizeof(THRDCARETINFO), + USERTAG_Q); + + if (!MessageQueue) + { + return NULL; + } + + RtlZeroMemory(MessageQueue, sizeof(USER_MESSAGE_QUEUE) + sizeof(THRDCARETINFO)); + /* hold at least one reference until it'll be destroyed */ + IntReferenceMessageQueue(MessageQueue); + /* initialize the queue */ + if (!MsqInitializeMessageQueue(pti, MessageQueue)) + { + IntDereferenceMessageQueue(MessageQueue); + return NULL; + } + + return MessageQueue; +} + +VOID FASTCALL +MsqDestroyMessageQueue(PTHREADINFO pti) +{ + PDESKTOP desk; + PUSER_MESSAGE_QUEUE MessageQueue = pti->MessageQueue; + + MessageQueue->QF_flags |= QF_INDESTROY; + + /* remove the message queue from any desktops */ + if ((desk = InterlockedExchangePointer((PVOID*)&MessageQueue->Desktop, 0))) + { + (void)InterlockedExchangePointer((PVOID*)&desk->ActiveMessageQueue, 0); + IntDereferenceMessageQueue(MessageQueue); + } + + /* clean it up */ + MsqCleanupMessageQueue(pti); + + /* decrease the reference counter, if it hits zero, the queue will be freed */ + IntDereferenceMessageQueue(MessageQueue); +} + +LPARAM FASTCALL +MsqSetMessageExtraInfo(LPARAM lParam) +{ + LPARAM Ret; + PTHREADINFO pti; + PUSER_MESSAGE_QUEUE MessageQueue; + + pti = PsGetCurrentThreadWin32Thread(); + MessageQueue = pti->MessageQueue; + if(!MessageQueue) + { + return 0; + } + + Ret = MessageQueue->ExtraInfo; + MessageQueue->ExtraInfo = lParam; + + return Ret; +} + +LPARAM FASTCALL +MsqGetMessageExtraInfo(VOID) +{ + PTHREADINFO pti; + PUSER_MESSAGE_QUEUE MessageQueue; + + pti = PsGetCurrentThreadWin32Thread(); + MessageQueue = pti->MessageQueue; + if(!MessageQueue) + { + return 0; + } + + return MessageQueue->ExtraInfo; +} + +// ReplyMessage is called by the thread receiving the window message. +BOOL FASTCALL +co_MsqReplyMessage( LRESULT lResult ) +{ + PUSER_SENT_MESSAGE Message; + PTHREADINFO pti; + + pti = PsGetCurrentThreadWin32Thread(); + Message = pti->pusmCurrent; + + if (!Message) return FALSE; + + if (Message->QS_Flags & QS_SMRESULT) return FALSE; + + // SendMessageXxx || Callback msg and not a notify msg + if (Message->ptiSender || Message->CompletionCallback) + { + Message->lResult = lResult; + Message->QS_Flags |= QS_SMRESULT; + // See co_MsqDispatchOneSentMessage, change bits already accounted for and cleared and this msg is going away.. + } + return TRUE; +} + +HWND FASTCALL +MsqSetStateWindow(PTHREADINFO pti, ULONG Type, HWND hWnd) +{ + HWND Prev; + PUSER_MESSAGE_QUEUE MessageQueue; + + MessageQueue = pti->MessageQueue; + + switch(Type) + { + case MSQ_STATE_CAPTURE: + Prev = MessageQueue->spwndCapture ? UserHMGetHandle(MessageQueue->spwndCapture) : 0; + MessageQueue->spwndCapture = ValidateHwndNoErr(hWnd); + return Prev; + case MSQ_STATE_ACTIVE: + Prev = MessageQueue->spwndActive ? UserHMGetHandle(MessageQueue->spwndActive) : 0; + MessageQueue->spwndActive = ValidateHwndNoErr(hWnd); + return Prev; + case MSQ_STATE_FOCUS: + Prev = MessageQueue->spwndFocus ? UserHMGetHandle(MessageQueue->spwndFocus) : 0; + MessageQueue->spwndFocus = ValidateHwndNoErr(hWnd); + return Prev; + case MSQ_STATE_MENUOWNER: + Prev = MessageQueue->MenuOwner; + MessageQueue->MenuOwner = hWnd; + return Prev; + case MSQ_STATE_MOVESIZE: + Prev = MessageQueue->MoveSize; + MessageQueue->MoveSize = hWnd; + return Prev; + case MSQ_STATE_CARET: + ASSERT(MessageQueue->CaretInfo); + Prev = MessageQueue->CaretInfo->hWnd; + MessageQueue->CaretInfo->hWnd = hWnd; + return Prev; + } + + return NULL; +} + +SHORT +APIENTRY +NtUserGetKeyState(INT key) +{ + DWORD Ret; + + UserEnterShared(); + + Ret = UserGetKeyState(key); + + UserLeave(); + + return (SHORT)Ret; +} + + +DWORD +APIENTRY +NtUserGetKeyboardState(LPBYTE lpKeyState) +{ + DWORD i, ret = TRUE; + PTHREADINFO pti; + PUSER_MESSAGE_QUEUE MessageQueue; + + UserEnterShared(); + + pti = PsGetCurrentThreadWin32Thread(); + MessageQueue = pti->MessageQueue; + + _SEH2_TRY + { + /* Probe and copy key state to an array */ + ProbeForWrite(lpKeyState, 256 * sizeof(BYTE), 1); + for (i = 0; i < 256; ++i) + { + lpKeyState[i] = 0; + if (IS_KEY_DOWN(MessageQueue->afKeyState, i)) + lpKeyState[i] |= KS_DOWN_BIT; + if (IS_KEY_LOCKED(MessageQueue->afKeyState, i)) + lpKeyState[i] |= KS_LOCK_BIT; + } + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + SetLastNtError(_SEH2_GetExceptionCode()); + ret = FALSE; + } + _SEH2_END; + + UserLeave(); + + return ret; +} + +BOOL +APIENTRY +NtUserSetKeyboardState(LPBYTE pKeyState) +{ + UINT i; + BOOL bRet = TRUE; + PTHREADINFO pti; + PUSER_MESSAGE_QUEUE MessageQueue; + + UserEnterExclusive(); + + pti = PsGetCurrentThreadWin32Thread(); + MessageQueue = pti->MessageQueue; + + _SEH2_TRY + { + ProbeForRead(pKeyState, 256 * sizeof(BYTE), 1); + for (i = 0; i < 256; ++i) + { + SET_KEY_DOWN(MessageQueue->afKeyState, i, pKeyState[i] & KS_DOWN_BIT); + SET_KEY_LOCKED(MessageQueue->afKeyState, i, pKeyState[i] & KS_LOCK_BIT); + } + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + SetLastNtError(_SEH2_GetExceptionCode()); + bRet = FALSE; + } + _SEH2_END; + + UserLeave(); + + return bRet; +} + +/* EOF */ diff --cc win32ss/user/user32/controls/appswitch.c index ab289597f8b,00000000000..3bda965ab8e mode 100644,000000..100644 --- a/win32ss/user/user32/controls/appswitch.c +++ b/win32ss/user/user32/controls/appswitch.c @@@ -1,571 -1,0 +1,571 @@@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS system libraries + * FILE: dll/win32/user32/controls/appswitch.c + * PURPOSE: app switching functionality + * PROGRAMMERS: Johannes Anderwald (johannes.anderwald@reactos.org) + * David Quintana (gigaherz@gmail.com) + */ + +#include + +#include +WINE_DEFAULT_DEBUG_CHANNEL(user32); + +// limit the number of windows shown in the alt-tab window +// 120 windows results in (12*40) by (10*40) pixels worth of icons. +#define MAX_WINDOWS 120 + +// Global variables +HWND switchdialog = NULL; +HFONT dialogFont; +int selectedWindow = 0; +BOOL isOpen = FALSE; + +int fontHeight=0; + +WCHAR windowText[1024]; + +HWND windowList[MAX_WINDOWS]; +HICON iconList[MAX_WINDOWS]; +int windowCount = 0; + +int cxBorder, cyBorder; +int nItems, nCols, nRows; +int itemsW, itemsH; +int totalW, totalH; +int xOffset, yOffset; +POINT pt; + +void ResizeAndCenter(HWND hwnd, int width, int height) +{ + int screenwidth = GetSystemMetrics(SM_CXSCREEN); + int screenheight = GetSystemMetrics(SM_CYSCREEN); + + pt.x = (screenwidth - width) / 2; + pt.y = (screenheight - height) / 2; + + MoveWindow(hwnd, pt.x, pt.y, width, height, FALSE); +} + +void MakeWindowActive(HWND hwnd) +{ + WINDOWPLACEMENT wpl; + + wpl.length = sizeof(WINDOWPLACEMENT); + GetWindowPlacement(hwnd, &wpl); + + TRACE("GetWindowPlacement wpl.showCmd %d\n",wpl.showCmd); + if (wpl.showCmd == SW_SHOWMINIMIZED) + ShowWindowAsync(hwnd, SW_RESTORE); + + BringWindowToTop(hwnd); // same as: SetWindowPos(hwnd,HWND_TOP,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE); ? + SetForegroundWindow(hwnd); +} + +void CompleteSwitch(BOOL doSwitch) +{ + if (!isOpen) + return; + + isOpen = FALSE; + + TRACE("[ATbot] CompleteSwitch Hiding Window.\n"); + ShowWindow(switchdialog, SW_HIDE); + + if(doSwitch) + { + if(selectedWindow >= windowCount) + return; + + // FIXME: workaround because reactos fails to activate the previous window correctly. + //if(selectedWindow != 0) + { + HWND hwnd = windowList[selectedWindow]; + + GetWindowTextW(hwnd, windowText, _countof(windowText)); + + TRACE("[ATbot] CompleteSwitch Switching to 0x%08x (%ls)\n", hwnd, windowText); + + MakeWindowActive(hwnd); + } + } + + windowCount = 0; +} + +BOOL CALLBACK EnumerateCallback(HWND window, LPARAM lParam) +{ + HICON hIcon; + + UNREFERENCED_PARAMETER(lParam); + + if (!IsWindowVisible(window)) + return TRUE; + + GetClassNameW(window, windowText, _countof(windowText)); + if ((wcscmp(L"Shell_TrayWnd", windowText)==0) || + (wcscmp(L"Progman", windowText)==0) ) + return TRUE; + + // First try to get the big icon assigned to the window + hIcon = (HICON)SendMessageW(window, WM_GETICON, ICON_BIG, 0); + if (!hIcon) + { + // If no icon is assigned, try to get the icon assigned to the windows' class + hIcon = (HICON)GetClassLongPtrW(window, GCL_HICON); + if (!hIcon) + { + // If we still don't have an icon, see if we can do with the small icon, + // or a default application icon + hIcon = (HICON)SendMessageW(window, WM_GETICON, ICON_SMALL2, 0); + if (!hIcon) + { + // If all fails, give up and continue with the next window + return TRUE; + } + } + } + + windowList[windowCount] = window; + iconList[windowCount] = CopyIcon(hIcon); + + windowCount++; + + // If we got to the max number of windows, + // we won't be able to add any more + if(windowCount == MAX_WINDOWS) + return FALSE; + + return TRUE; +} + +// Function mostly compatible with the normal EnumWindows, +// except it lists in Z-Order and it doesn't ensure consistency +// if a window is removed while enumerating +void EnumWindowsZOrder(WNDENUMPROC callback, LPARAM lParam) +{ + HWND next = GetTopWindow(NULL); + while (next != NULL) + { + if(!callback(next, lParam)) + break; + next = GetWindow(next, GW_HWNDNEXT); + } +} + +void ProcessMouseMessage(UINT message, LPARAM lParam) +{ + int xPos = LOWORD(lParam); + int yPos = HIWORD(lParam); + + int xIndex = (xPos - xOffset)/40; + int xOff = (xPos - xOffset)%40; + + int yIndex = (yPos - yOffset)/40; + int yOff = (yPos - yOffset)%40; + + if(xOff > 32 || xIndex > nItems) + return; + + if(yOff > 32 || yIndex > nRows) + return; + + selectedWindow = (yIndex*nCols) + xIndex; + if (message == WM_MOUSEMOVE) + { + InvalidateRect(switchdialog, NULL, TRUE); + //RedrawWindow(switchdialog, NULL, NULL, 0); + } + else + { + selectedWindow = (yIndex*nCols) + xIndex; + CompleteSwitch(TRUE); + } +} + +void OnPaint(HWND hWnd) +{ + HDC dialogDC; + PAINTSTRUCT paint; + RECT cRC, textRC; + int i; + HBRUSH hBrush; + HPEN hPen; + HFONT dcFont; + COLORREF cr; + int nch = GetWindowTextW(windowList[selectedWindow], windowText, _countof(windowText)); + + dialogDC = BeginPaint(hWnd, &paint); + { + GetClientRect(hWnd, &cRC); + FillRect(dialogDC, &cRC, GetSysColorBrush(COLOR_MENU)); + + for(i=0; i< windowCount; i++) + { + HICON hIcon = iconList[i]; + + int xpos = xOffset + 40 * (i % nCols); + int ypos = yOffset + 40 * (i / nCols); + + if (selectedWindow == i) + { + hBrush = GetSysColorBrush(COLOR_HIGHLIGHT); + } + else + { + hBrush = GetSysColorBrush(COLOR_MENU); + } +#if TRUE + cr = GetSysColor(COLOR_BTNTEXT); // doesn't look right! >_< + hPen = CreatePen(PS_DOT, 1, cr); + SelectObject(dialogDC, hPen); + SelectObject(dialogDC, hBrush); + Rectangle(dialogDC, xpos-2, ypos-2, xpos+32+2, ypos+32+2); + DeleteObject(hPen); + // Must NOT destroy the system brush! +#else + RECT rc = { xpos-2, ypos-2, xpos+32+2, ypos+32+2 }; + FillRect(dialogDC, &rc, hBrush); +#endif + DrawIcon(dialogDC, xpos, ypos, hIcon); + } + + dcFont = SelectObject(dialogDC, dialogFont); + SetTextColor(dialogDC, GetSysColor(COLOR_BTNTEXT)); + SetBkColor(dialogDC, GetSysColor(COLOR_BTNFACE)); + + textRC.top = itemsH; + textRC.left = 8; + textRC.right = totalW - 8; + textRC.bottom = totalH - 8; + DrawTextW(dialogDC, windowText, nch, &textRC, DT_CENTER|DT_END_ELLIPSIS); + SelectObject(dialogDC, dcFont); + } + EndPaint(hWnd, &paint); +} + +DWORD CreateSwitcherWindow(HINSTANCE hInstance) +{ + switchdialog = CreateWindowExW( WS_EX_TOPMOST|WS_EX_DLGMODALFRAME|WS_EX_TOOLWINDOW, + WC_SWITCH, + L"", + WS_POPUP|WS_BORDER|WS_DISABLED, + CW_USEDEFAULT, + CW_USEDEFAULT, + 400, 150, + NULL, NULL, + hInstance, NULL); + if (!switchdialog) + { + TRACE("[ATbot] Task Switcher Window failed to create.\n"); + return 0; + } + + isOpen = FALSE; + return 1; +} + +DWORD GetDialogFont() +{ + HDC tDC; + TEXTMETRIC tm; + + dialogFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT); + + tDC = GetDC(0); + GetTextMetrics(tDC, &tm); + fontHeight = tm.tmHeight; + ReleaseDC(0, tDC); + + return 1; +} + +void PrepareWindow() +{ + cxBorder = GetSystemMetrics(SM_CXBORDER); + cyBorder = GetSystemMetrics(SM_CYBORDER); + + nItems = windowCount; + nCols = min(max(nItems,8),12); + nRows = (nItems+nCols-1)/nCols; + + itemsW = nCols*32 + (nCols+1)*8; + itemsH = nRows*32 + (nRows+1)*8; + + totalW = itemsW + 2*cxBorder + 4; + totalH = itemsH + 2*cyBorder + fontHeight + 8; // give extra pixels for the window title + + xOffset = 8; + yOffset = 8; + + if (nItems < nCols) + { + int w2 = nItems*32 + (nItems-1)*8; + xOffset = (itemsW-w2)/2; + } + ResizeAndCenter(switchdialog, totalW, totalH); +} + +void ProcessHotKey() +{ + if (!isOpen) + { + windowCount=0; + EnumWindowsZOrder(EnumerateCallback, 0); + + if (windowCount < 2) + return; + + selectedWindow = 1; + + TRACE("[ATbot] HotKey Received. Opening window.\n"); + ShowWindow(switchdialog, SW_SHOWNORMAL); + MakeWindowActive(switchdialog); + isOpen = TRUE; + } + else + { + TRACE("[ATbot] HotKey Received Rotating.\n"); + selectedWindow = (selectedWindow + 1)%windowCount; + InvalidateRect(switchdialog, NULL, TRUE); + } +} + +LRESULT WINAPI DoAppSwitch( WPARAM wParam, LPARAM lParam ) +{ + HWND hwnd, hwndActive; + MSG msg; + BOOL Esc = FALSE; + INT Count = 0; + WCHAR Text[1024]; + + // Already in the loop. + if (switchdialog) return 0; + + hwndActive = GetActiveWindow(); + // Nothing is active so exit. + if (!hwndActive) return 0; + // Capture current active window. + SetCapture( hwndActive ); + + switch (lParam) + { + case VK_TAB: + if( !CreateSwitcherWindow(User32Instance) ) goto Exit; + if( !GetDialogFont() ) goto Exit; + ProcessHotKey(); + break; + + case VK_ESCAPE: + windowCount = 0; + Count = 0; + EnumWindowsZOrder(EnumerateCallback, 0); + if (windowCount < 2) goto Exit; + if (wParam == SC_NEXTWINDOW) + Count = 1; + else + { + if (windowCount == 2) + Count = 0; + else + Count = windowCount - 1; + } + TRACE("DoAppSwitch VK_ESCAPE 1 Count %d windowCount %d\n",Count,windowCount); + hwnd = windowList[Count]; + GetWindowTextW(hwnd, Text, _countof(Text)); + TRACE("[ATbot] Switching to 0x%08x (%ls)\n", hwnd, Text); + MakeWindowActive(hwnd); + Esc = TRUE; + break; + + default: + goto Exit; + } + // Main message loop: + while (1) + { + for (;;) + { + if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE )) + { + if (!CallMsgFilterW( &msg, MSGF_NEXTWINDOW )) break; + /* remove the message from the queue */ + PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE ); + } + else + WaitMessage(); + } + + switch (msg.message) + { + case WM_KEYUP: + { + PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE ); + if (msg.wParam == VK_MENU) + { + CompleteSwitch(TRUE); + } + else if (msg.wParam == VK_RETURN) + { + CompleteSwitch(TRUE); + } + else if (msg.wParam == VK_ESCAPE) + { + TRACE("DoAppSwitch VK_ESCAPE 2\n"); + CompleteSwitch(FALSE); + } + goto Exit; //break; + } + + case WM_SYSKEYDOWN: + { + PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE ); + if (HIWORD(msg.lParam) & KF_ALTDOWN) + { + INT Shift; + if ( msg.wParam == VK_TAB ) + { + if (Esc) break; + Shift = GetKeyState(VK_SHIFT) & 0x8000 ? SC_PREVWINDOW : SC_NEXTWINDOW; + if (Shift == SC_NEXTWINDOW) + { + selectedWindow = (selectedWindow + 1)%windowCount; + } + else + { + selectedWindow = selectedWindow - 1; + if (selectedWindow < 0) + selectedWindow = windowCount - 1; + } + InvalidateRect(switchdialog, NULL, TRUE); + } + else if ( msg.wParam == VK_ESCAPE ) + { + if (!Esc) break; + if (windowCount < 2) + goto Exit; + if (wParam == SC_NEXTWINDOW) + { + Count = (Count + 1)%windowCount; + } + else + { + Count--; + if (Count < 0) + Count = windowCount - 1; + } + hwnd = windowList[Count]; + GetWindowTextW(hwnd, Text, _countof(Text)); + MakeWindowActive(hwnd); + } + } + break; + } + + case WM_LBUTTONUP: + PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE ); + ProcessMouseMessage(msg.message, msg.lParam); + goto Exit; + + default: + if (PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE )) + { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + break; + } + } +Exit: + ReleaseCapture(); + if (switchdialog) DestroyWindow(switchdialog); + switchdialog = NULL; + selectedWindow = 0; + windowCount = 0; + return 0; +} + +VOID +DestroyAppWindows() +{ + INT i; + for (i=0; i< windowCount; i++) + { + HICON hIcon = iconList[i]; + DestroyIcon(hIcon); + } +} + +// +// Switch System Class Window Proc. +// +LRESULT WINAPI SwitchWndProc_common(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL unicode ) +{ + PWND pWnd; + PALTTABINFO ati; + pWnd = ValidateHwnd(hWnd); + if (pWnd) + { + if (!pWnd->fnid) + { + NtUserSetWindowFNID(hWnd, FNID_SWITCH); + } + } + + switch (uMsg) + { + case WM_NCCREATE: + if (!(ati = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*ati)))) + return 0; + SetWindowLongPtrW( hWnd, 0, (LONG_PTR)ati ); + return TRUE; + + case WM_SHOWWINDOW: - if (wParam == TRUE) ++ if (wParam) + { + PrepareWindow(); + ati = (PALTTABINFO)GetWindowLongPtrW(hWnd, 0); + ati->cItems = nItems; + ati->cxItem = ati->cyItem = 43; + ati->cRows = nRows; + ati->cColumns = nCols; + } + return 0; + + case WM_MOUSEMOVE: + ProcessMouseMessage(uMsg, lParam); + return 0; + + case WM_ACTIVATE: + if (wParam == WA_INACTIVE) + { + CompleteSwitch(FALSE); + } + return 0; + + case WM_PAINT: + OnPaint(hWnd); + return 0; + + case WM_DESTROY: + isOpen = FALSE; + ati = (PALTTABINFO)GetWindowLongPtrW(hWnd, 0); + HeapFree( GetProcessHeap(), 0, ati ); + SetWindowLongPtrW( hWnd, 0, 0 ); + DestroyAppWindows(); + NtUserSetWindowFNID(hWnd, FNID_DESTROY); + return 0; + } + return DefWindowProcW(hWnd, uMsg, wParam, lParam); +} + +LRESULT WINAPI SwitchWndProcA(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + return SwitchWndProc_common(hWnd, uMsg, wParam, lParam, FALSE); +} + +LRESULT WINAPI SwitchWndProcW(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + return SwitchWndProc_common(hWnd, uMsg, wParam, lParam, TRUE); +} diff --cc win32ss/user/user32/misc/desktop.c index ae10e8c8a8c,00000000000..50702a34027 mode 100644,000000..100644 --- a/win32ss/user/user32/misc/desktop.c +++ b/win32ss/user/user32/misc/desktop.c @@@ -1,645 -1,0 +1,645 @@@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS user32.dll + * FILE: lib/user32/misc/desktop.c + * PURPOSE: Desktops + * PROGRAMMER: Casper S. Hornstrup (chorns@users.sourceforge.net) + * UPDATE HISTORY: + * 06-06-2001 CSH Created + */ + +#include + +#include +WINE_DEFAULT_DEBUG_CHANNEL(user32); + +/********************************************************************* + * desktop class descriptor + */ +#if 0 // Kept for referencing. +const struct builtin_class_descr DESKTOP_builtin_class = +{ + WC_DESKTOP, /* name */ + CS_DBLCLKS, /* style */ + NULL, /* procA (winproc is Unicode only) */ + DesktopWndProc, /* procW */ + 0, /* extra */ + IDC_ARROW, /* cursor */ + (HBRUSH)(COLOR_BACKGROUND+1) /* brush */ +}; +#endif + +LRESULT +WINAPI +DesktopWndProcW(HWND Wnd, + UINT Msg, + WPARAM wParam, + LPARAM lParam) +{ + TRACE("Desktop W Class Atom! hWnd 0x%x, Msg %d\n", Wnd, Msg); + + switch(Msg) + { + case WM_ERASEBKGND: + case WM_NCCREATE: + case WM_CREATE: + case WM_CLOSE: + case WM_DISPLAYCHANGE: + case WM_PAINT: + case WM_SYSCOLORCHANGE: + { + LRESULT lResult; + NtUserMessageCall( Wnd, Msg, wParam, lParam, (ULONG_PTR)&lResult, FNID_DESKTOP, FALSE); + TRACE("Desktop lResult %d\n", lResult); + return lResult; + } + + case WM_PALETTECHANGED: + if (Wnd == (HWND)wParam) break; + case WM_QUERYNEWPALETTE: + { + HDC hdc = GetWindowDC( Wnd ); + PaintDesktop(hdc); + ReleaseDC( Wnd, hdc ); + break; + } + + case WM_SETCURSOR: + return (LRESULT)SetCursor(LoadCursorW(0, (LPCWSTR)IDC_ARROW)); + + default: + return DefWindowProcW(Wnd, Msg, wParam, lParam); + } + return 0; +} + +VOID +WINAPI +LogFontA2W(LPLOGFONTW pW, CONST LOGFONTA *pA) +{ +#define COPYS(f,len) MultiByteToWideChar ( CP_THREAD_ACP, 0, pA->f, len, pW->f, len ) +#define COPYN(f) pW->f = pA->f + + COPYN(lfHeight); + COPYN(lfWidth); + COPYN(lfEscapement); + COPYN(lfOrientation); + COPYN(lfWeight); + COPYN(lfItalic); + COPYN(lfUnderline); + COPYN(lfStrikeOut); + COPYN(lfCharSet); + COPYN(lfOutPrecision); + COPYN(lfClipPrecision); + COPYN(lfQuality); + COPYN(lfPitchAndFamily); + COPYS(lfFaceName,LF_FACESIZE); + +#undef COPYN +#undef COPYS +} + +VOID +WINAPI +LogFontW2A(LPLOGFONTA pA, CONST LOGFONTW *pW) +{ +#define COPYS(f,len) WideCharToMultiByte ( CP_THREAD_ACP, 0, pW->f, len, pA->f, len, NULL, NULL ) +#define COPYN(f) pA->f = pW->f + + COPYN(lfHeight); + COPYN(lfWidth); + COPYN(lfEscapement); + COPYN(lfOrientation); + COPYN(lfWeight); + COPYN(lfItalic); + COPYN(lfUnderline); + COPYN(lfStrikeOut); + COPYN(lfCharSet); + COPYN(lfOutPrecision); + COPYN(lfClipPrecision); + COPYN(lfQuality); + COPYN(lfPitchAndFamily); + COPYS(lfFaceName,LF_FACESIZE); + +#undef COPYN +#undef COPYS +} + +int WINAPI +RealGetSystemMetrics(int nIndex) +{ + //GetConnected(); + //FIXME("Global Server Data -> %x\n",gpsi); + if (nIndex < 0 || nIndex >= SM_CMETRICS) return 0; + return gpsi->aiSysMet[nIndex]; +} + +/* + * @implemented + */ +int WINAPI +GetSystemMetrics(int nIndex) +{ + BOOL Hook; + int Ret = 0; + + if (!gpsi) // Fixme! Hax! Need Timos delay load support? + { + return RealGetSystemMetrics(nIndex); + } + + LoadUserApiHook(); + + Hook = BeginIfHookedUserApiHook(); + + /* Bypass SEH and go direct. */ + if (!Hook) return RealGetSystemMetrics(nIndex); + + _SEH2_TRY + { + Ret = guah.GetSystemMetrics(nIndex); + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + } + _SEH2_END; + + EndUserApiHook(); + + return Ret; +} + +/* + * @unimplemented + */ +BOOL WINAPI SetDeskWallpaper(LPCSTR filename) +{ + return SystemParametersInfoA(SPI_SETDESKWALLPAPER,0,(PVOID)filename,TRUE); +} + +BOOL WINAPI +RealSystemParametersInfoA(UINT uiAction, + UINT uiParam, + PVOID pvParam, + UINT fWinIni) +{ + switch (uiAction) + { + + case SPI_GETNONCLIENTMETRICS: + { + LPNONCLIENTMETRICSA pnclma = (LPNONCLIENTMETRICSA)pvParam; + NONCLIENTMETRICSW nclmw; + if(pnclma->cbSize != sizeof(NONCLIENTMETRICSA)) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + nclmw.cbSize = sizeof(NONCLIENTMETRICSW); + + if (!SystemParametersInfoW(uiAction, sizeof(NONCLIENTMETRICSW), + &nclmw, fWinIni)) + return FALSE; + + pnclma->iBorderWidth = nclmw.iBorderWidth; + pnclma->iScrollWidth = nclmw.iScrollWidth; + pnclma->iScrollHeight = nclmw.iScrollHeight; + pnclma->iCaptionWidth = nclmw.iCaptionWidth; + pnclma->iCaptionHeight = nclmw.iCaptionHeight; + pnclma->iSmCaptionWidth = nclmw.iSmCaptionWidth; + pnclma->iSmCaptionHeight = nclmw.iSmCaptionHeight; + pnclma->iMenuWidth = nclmw.iMenuWidth; + pnclma->iMenuHeight = nclmw.iMenuHeight; + LogFontW2A(&(pnclma->lfCaptionFont), &(nclmw.lfCaptionFont)); + LogFontW2A(&(pnclma->lfSmCaptionFont), &(nclmw.lfSmCaptionFont)); + LogFontW2A(&(pnclma->lfMenuFont), &(nclmw.lfMenuFont)); + LogFontW2A(&(pnclma->lfStatusFont), &(nclmw.lfStatusFont)); + LogFontW2A(&(pnclma->lfMessageFont), &(nclmw.lfMessageFont)); + return TRUE; + } + case SPI_SETNONCLIENTMETRICS: + { + LPNONCLIENTMETRICSA pnclma = (LPNONCLIENTMETRICSA)pvParam; + NONCLIENTMETRICSW nclmw; + if(pnclma->cbSize != sizeof(NONCLIENTMETRICSA)) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + nclmw.cbSize = sizeof(NONCLIENTMETRICSW); + nclmw.iBorderWidth = pnclma->iBorderWidth; + nclmw.iScrollWidth = pnclma->iScrollWidth; + nclmw.iScrollHeight = pnclma->iScrollHeight; + nclmw.iCaptionWidth = pnclma->iCaptionWidth; + nclmw.iCaptionHeight = pnclma->iCaptionHeight; + nclmw.iSmCaptionWidth = pnclma->iSmCaptionWidth; + nclmw.iSmCaptionHeight = pnclma->iSmCaptionHeight; + nclmw.iMenuWidth = pnclma->iMenuWidth; + nclmw.iMenuHeight = pnclma->iMenuHeight; + LogFontA2W(&(nclmw.lfCaptionFont), &(pnclma->lfCaptionFont)); + LogFontA2W(&(nclmw.lfSmCaptionFont), &(pnclma->lfSmCaptionFont)); + LogFontA2W(&(nclmw.lfMenuFont), &(pnclma->lfMenuFont)); + LogFontA2W(&(nclmw.lfStatusFont), &(pnclma->lfStatusFont)); + LogFontA2W(&(nclmw.lfMessageFont), &(pnclma->lfMessageFont)); + + return SystemParametersInfoW(uiAction, sizeof(NONCLIENTMETRICSW), + &nclmw, fWinIni); + } + case SPI_GETICONMETRICS: + { + LPICONMETRICSA picma = (LPICONMETRICSA)pvParam; + ICONMETRICSW icmw; + if(picma->cbSize != sizeof(ICONMETRICSA)) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + icmw.cbSize = sizeof(ICONMETRICSW); + if (!SystemParametersInfoW(uiAction, sizeof(ICONMETRICSW), + &icmw, fWinIni)) + return FALSE; + + picma->iHorzSpacing = icmw.iHorzSpacing; + picma->iVertSpacing = icmw.iVertSpacing; + picma->iTitleWrap = icmw.iTitleWrap; + LogFontW2A(&(picma->lfFont), &(icmw.lfFont)); + return TRUE; + } + case SPI_SETICONMETRICS: + { + LPICONMETRICSA picma = (LPICONMETRICSA)pvParam; + ICONMETRICSW icmw; + if(picma->cbSize != sizeof(ICONMETRICSA)) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + icmw.cbSize = sizeof(ICONMETRICSW); + icmw.iHorzSpacing = picma->iHorzSpacing; + icmw.iVertSpacing = picma->iVertSpacing; + icmw.iTitleWrap = picma->iTitleWrap; + LogFontA2W(&(icmw.lfFont), &(picma->lfFont)); + + return SystemParametersInfoW(uiAction, sizeof(ICONMETRICSW), + &icmw, fWinIni); + } + case SPI_GETICONTITLELOGFONT: + { + LOGFONTW lfw; + if (!SystemParametersInfoW(uiAction, 0, &lfw, fWinIni)) + return FALSE; + LogFontW2A(pvParam, &lfw); + return TRUE; + } + case SPI_SETICONTITLELOGFONT: + { + LPLOGFONTA plfa = (LPLOGFONTA)pvParam; + LOGFONTW lfw; + LogFontA2W(&lfw,plfa); + return SystemParametersInfoW(uiAction, 0, &lfw, fWinIni); + } + case SPI_GETDESKWALLPAPER: + { + BOOL Ret; + WCHAR awc[MAX_PATH]; + UNICODE_STRING ustrWallpaper; + ANSI_STRING astrWallpaper; + + Ret = NtUserSystemParametersInfo(SPI_GETDESKWALLPAPER, MAX_PATH, awc, fWinIni); + RtlInitUnicodeString(&ustrWallpaper, awc); + RtlUnicodeStringToAnsiString(&astrWallpaper, &ustrWallpaper, TRUE); + + RtlCopyMemory(pvParam, astrWallpaper.Buffer, uiParam); + RtlFreeAnsiString(&astrWallpaper); + return Ret; + } + + case SPI_SETDESKWALLPAPER: + { + UNICODE_STRING ustrWallpaper; + BOOL Ret; + + if (pvParam) + { + if (!RtlCreateUnicodeStringFromAsciiz(&ustrWallpaper, pvParam)) + { + ERR("RtlCreateUnicodeStringFromAsciiz failed\n"); + return FALSE; + } + pvParam = &ustrWallpaper; + } + + Ret = NtUserSystemParametersInfo(SPI_SETDESKWALLPAPER, uiParam, pvParam, fWinIni); + + if (pvParam) + RtlFreeUnicodeString(&ustrWallpaper); + + return Ret; + } + } + return NtUserSystemParametersInfo(uiAction, uiParam, pvParam, fWinIni); +} + +BOOL WINAPI +RealSystemParametersInfoW(UINT uiAction, + UINT uiParam, + PVOID pvParam, + UINT fWinIni) +{ + switch(uiAction) + { + + case SPI_SETDESKWALLPAPER: + { + UNICODE_STRING ustrWallpaper; + + RtlInitUnicodeString(&ustrWallpaper, pvParam); + return NtUserSystemParametersInfo(SPI_SETDESKWALLPAPER, uiParam, &ustrWallpaper, fWinIni); + } + } + return NtUserSystemParametersInfo(uiAction, uiParam, pvParam, fWinIni); +} + + +/* + * @implemented + */ +BOOL WINAPI +SystemParametersInfoA(UINT uiAction, + UINT uiParam, + PVOID pvParam, + UINT fWinIni) +{ + BOOL Hook, Ret = FALSE; + + LoadUserApiHook(); + + Hook = BeginIfHookedUserApiHook(); + + /* Bypass SEH and go direct. */ + if (!Hook) return RealSystemParametersInfoA(uiAction, uiParam, pvParam, fWinIni); + + _SEH2_TRY + { + Ret = guah.SystemParametersInfoA(uiAction, uiParam, pvParam, fWinIni); + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + } + _SEH2_END; + + EndUserApiHook(); + + return Ret; +} + +/* + * @implemented + */ +BOOL WINAPI +SystemParametersInfoW(UINT uiAction, + UINT uiParam, + PVOID pvParam, + UINT fWinIni) +{ + BOOL Hook, Ret = FALSE; + + LoadUserApiHook(); + + Hook = BeginIfHookedUserApiHook(); + + /* Bypass SEH and go direct. */ + if (!Hook) return RealSystemParametersInfoW(uiAction, uiParam, pvParam, fWinIni); + + _SEH2_TRY + { + Ret = guah.SystemParametersInfoW(uiAction, uiParam, pvParam, fWinIni); + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + } + _SEH2_END; + + EndUserApiHook(); + + return Ret; +} + +/* + * @implemented + */ +HDESK WINAPI +CreateDesktopA(LPCSTR lpszDesktop, + LPCSTR lpszDevice, + LPDEVMODEA pDevmode, + DWORD dwFlags, + ACCESS_MASK dwDesiredAccess, + LPSECURITY_ATTRIBUTES lpsa) +{ + UNICODE_STRING DesktopNameU; + HDESK hDesktop; + LPDEVMODEW DevmodeW = NULL; + + if (lpszDesktop) + { + /* After conversion, the buffer is zero-terminated */ + RtlCreateUnicodeStringFromAsciiz(&DesktopNameU, lpszDesktop); + } + else + { + RtlInitUnicodeString(&DesktopNameU, NULL); + } + + if (pDevmode) + DevmodeW = GdiConvertToDevmodeW(pDevmode); + + hDesktop = CreateDesktopW(DesktopNameU.Buffer, + NULL, + DevmodeW, + dwFlags, + dwDesiredAccess, + lpsa); + + /* Free the string, if it was allocated */ + if (lpszDesktop) RtlFreeUnicodeString(&DesktopNameU); + + return hDesktop; +} + + +/* + * @implemented + */ +HDESK WINAPI +CreateDesktopW(LPCWSTR lpszDesktop, + LPCWSTR lpszDevice, + LPDEVMODEW pDevmode, + DWORD dwFlags, + ACCESS_MASK dwDesiredAccess, + LPSECURITY_ATTRIBUTES lpsa) +{ + OBJECT_ATTRIBUTES oas; + UNICODE_STRING DesktopName, DesktopDevice; + HWINSTA hWinSta; + HDESK hDesktop; + ULONG Attributes = (OBJ_OPENIF|OBJ_CASE_INSENSITIVE); + + /* Retrive WinStation handle. */ + hWinSta = NtUserGetProcessWindowStation(); + + /* Initialize the strings. */ + RtlInitUnicodeString(&DesktopName, lpszDesktop); + RtlInitUnicodeString(&DesktopDevice, lpszDevice); + + /* Check for process is inherited, set flag if set. */ + if (lpsa && lpsa->bInheritHandle) Attributes |= OBJ_INHERIT; + + /* Initialize the attributes for the desktop. */ + InitializeObjectAttributes( &oas, + &DesktopName, + Attributes, + hWinSta, + lpsa ? lpsa->lpSecurityDescriptor : NULL); + + /* Send the request and call to win32k. */ + hDesktop = NtUserCreateDesktop( &oas, + &DesktopDevice, + pDevmode, + dwFlags, + dwDesiredAccess); + + return(hDesktop); +} + + +/* + * @implemented + */ +BOOL +WINAPI +EnumDesktopsA( + HWINSTA WindowStation, + DESKTOPENUMPROCA EnumFunc, + LPARAM Context) +{ + return EnumNamesA(WindowStation, EnumFunc, Context, TRUE); +} + + +/* + * @implemented + */ +BOOL +WINAPI +EnumDesktopsW( + HWINSTA WindowStation, + DESKTOPENUMPROCW EnumFunc, + LPARAM Context) +{ + return EnumNamesW(WindowStation, EnumFunc, Context, TRUE); +} + + +/* + * @implemented + */ +HDESK +WINAPI +GetThreadDesktop( + DWORD dwThreadId) +{ + return NtUserGetThreadDesktop(dwThreadId, 0); +} + + +/* + * @implemented + */ +HDESK +WINAPI +OpenDesktopA( + LPCSTR lpszDesktop, + DWORD dwFlags, + BOOL fInherit, + ACCESS_MASK dwDesiredAccess) +{ + UNICODE_STRING DesktopNameU; + HDESK hDesktop; + + if (lpszDesktop) + { + /* After conversion, the buffer is zero-terminated */ + RtlCreateUnicodeStringFromAsciiz(&DesktopNameU, lpszDesktop); + } + else + { + RtlInitUnicodeString(&DesktopNameU, NULL); + } + + hDesktop = OpenDesktopW(DesktopNameU.Buffer, + dwFlags, + fInherit, + dwDesiredAccess); + + /* Free the string, if it was allocated */ + if (lpszDesktop) RtlFreeUnicodeString(&DesktopNameU); + + return hDesktop; +} + + +/* + * @implemented + */ +HDESK +WINAPI +OpenDesktopW( + LPCWSTR lpszDesktop, + DWORD dwFlags, + BOOL fInherit, + ACCESS_MASK dwDesiredAccess) +{ + UNICODE_STRING DesktopName; + OBJECT_ATTRIBUTES ObjectAttributes; + + RtlInitUnicodeString(&DesktopName, lpszDesktop); + + InitializeObjectAttributes(&ObjectAttributes, + &DesktopName, + OBJ_CASE_INSENSITIVE, + GetProcessWindowStation(), + 0); + - if( fInherit == TRUE ) ++ if( fInherit ) + { + ObjectAttributes.Attributes |= OBJ_INHERIT; + } + + return NtUserOpenDesktop(&ObjectAttributes, dwFlags, dwDesiredAccess); +} + + +/* + * @implemented + */ +BOOL WINAPI +SetShellWindow(HWND hwndShell) +{ + return SetShellWindowEx(hwndShell, hwndShell); +} + + +/* + * @implemented + */ +HWND WINAPI +GetShellWindow(VOID) +{ + PDESKTOPINFO pdi; + pdi = GetThreadDesktopInfo(); + if (pdi) return pdi->hShellWindow; + return NULL; +} + + +/* EOF */ diff --cc win32ss/user/user32/misc/winsta.c index ad7768d921a,00000000000..0f70e806bc7 mode 100644,000000..100644 --- a/win32ss/user/user32/misc/winsta.c +++ b/win32ss/user/user32/misc/winsta.c @@@ -1,404 -1,0 +1,404 @@@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS user32.dll + * FILE: lib/user32/misc/winsta.c + * PURPOSE: Window stations + * PROGRAMMER: Casper S. Hornstrup (chorns@users.sourceforge.net) + * UPDATE HISTORY: + * 04-06-2001 CSH Created + */ + +#include + +#include +WINE_DEFAULT_DEBUG_CHANNEL(winsta); + + +/* + * @implemented + */ +HWINSTA WINAPI +CreateWindowStationA(LPCSTR lpwinsta, + DWORD dwReserved, + ACCESS_MASK dwDesiredAccess, + LPSECURITY_ATTRIBUTES lpsa) +{ + UNICODE_STRING WindowStationNameU; + HWINSTA hWinSta; + + if (lpwinsta) + { + /* After conversion, the buffer is zero-terminated */ + RtlCreateUnicodeStringFromAsciiz(&WindowStationNameU, lpwinsta); + } + else + { + RtlInitUnicodeString(&WindowStationNameU, NULL); + } + + hWinSta = CreateWindowStationW(WindowStationNameU.Buffer, + dwReserved, + dwDesiredAccess, + lpsa); + + /* Free the string, if it was allocated */ + if (lpwinsta) RtlFreeUnicodeString(&WindowStationNameU); + + return hWinSta; +} + + +/* + * @implemented + */ +HWINSTA WINAPI +CreateWindowStationW(LPCWSTR lpwinsta, + DWORD dwReserved, + ACCESS_MASK dwDesiredAccess, + LPSECURITY_ATTRIBUTES lpsa) +{ + UNICODE_STRING WindowStationName; + UNICODE_STRING WindowStationsDir = RTL_CONSTANT_STRING(L"\\Windows\\WindowStations"); + OBJECT_ATTRIBUTES ObjectAttributes; + HANDLE hWindowStationsDir; + NTSTATUS Status; + HWINSTA hwinsta; + + /* Open WindowStations directory */ + InitializeObjectAttributes(&ObjectAttributes, + &WindowStationsDir, + OBJ_CASE_INSENSITIVE, + 0, + 0); + + Status = NtOpenDirectoryObject(&hWindowStationsDir, + DIRECTORY_CREATE_OBJECT, + &ObjectAttributes); + if(!NT_SUCCESS(Status)) + { + ERR("Failed to open WindowStations directory\n"); + return NULL; + } + + RtlInitUnicodeString(&WindowStationName, lpwinsta); + + /* Create the window station object */ + InitializeObjectAttributes(&ObjectAttributes, + &WindowStationName, + OBJ_CASE_INSENSITIVE, + hWindowStationsDir, + 0); + + /* Check if the handle should be inheritable */ + if (lpsa && lpsa->bInheritHandle) + { + ObjectAttributes.Attributes |= OBJ_INHERIT; + } + + hwinsta = NtUserCreateWindowStation(&ObjectAttributes, + dwDesiredAccess, + 0, 0, 0, 0, 0); + + NtClose(hWindowStationsDir); + + return hwinsta; +} + +/* + * Common code for EnumDesktopsA/W and EnumWindowStationsA/W + */ +BOOL FASTCALL +EnumNamesW(HWINSTA WindowStation, + NAMEENUMPROCW EnumFunc, + LPARAM Context, + BOOL Desktops) +{ + char Buffer[256]; + PVOID NameList; + PWCHAR Name; + NTSTATUS Status; + ULONG RequiredSize; + ULONG CurrentEntry, EntryCount; + BOOL Ret; + + /* + * Check parameters + */ + if (NULL == WindowStation && Desktops) + { + WindowStation = GetProcessWindowStation(); + } + + /* + * Try with fixed-size buffer + */ + Status = NtUserBuildNameList(WindowStation, sizeof(Buffer), Buffer, &RequiredSize); + if (NT_SUCCESS(Status)) + { + /* Fixed-size buffer is large enough */ + NameList = (PWCHAR) Buffer; + } + else if (Status == STATUS_BUFFER_TOO_SMALL) + { + /* Allocate a larger buffer */ + NameList = HeapAlloc(GetProcessHeap(), 0, RequiredSize); + if (NULL == NameList) + { + return FALSE; + } + /* Try again */ + Status = NtUserBuildNameList(WindowStation, RequiredSize, NameList, NULL); + if (! NT_SUCCESS(Status)) + { + HeapFree(GetProcessHeap(), 0, NameList); + SetLastError(RtlNtStatusToDosError(Status)); + return FALSE; + } + } + else + { + /* Some unrecognized error occured */ + SetLastError(RtlNtStatusToDosError(Status)); + return FALSE; + } + + /* + * Enum the names one by one + */ + EntryCount = *((DWORD *) NameList); + Name = (PWCHAR) ((PCHAR) NameList + sizeof(DWORD)); + Ret = TRUE; + for (CurrentEntry = 0; CurrentEntry < EntryCount && Ret; ++CurrentEntry) + { + Ret = (*EnumFunc)(Name, Context); + Name += wcslen(Name) + 1; + } + + /* + * Cleanup + */ + if (NameList != Buffer) + { + HeapFree(GetProcessHeap(), 0, NameList); + } + + return Ret; +} + + +/* For W->A conversion */ +typedef struct tagENUMNAMESASCIICONTEXT +{ + NAMEENUMPROCA UserEnumFunc; + LPARAM UserContext; +} ENUMNAMESASCIICONTEXT, *PENUMNAMESASCIICONTEXT; + +/* + * Callback used by Ascii versions. Converts the Unicode name to + * Ascii and then calls the user callback + */ +BOOL CALLBACK +EnumNamesCallback(LPWSTR Name, LPARAM Param) +{ + PENUMNAMESASCIICONTEXT Context = (PENUMNAMESASCIICONTEXT) Param; + char FixedNameA[32]; + LPSTR NameA; + int Len; + BOOL Ret; + + /* + * Determine required size of Ascii string and see if we can use + * fixed buffer + */ + Len = WideCharToMultiByte(CP_ACP, 0, Name, -1, NULL, 0, NULL, NULL); + if (Len <= 0) + { + /* Some strange error occured */ + return FALSE; + } + else if (Len <= sizeof(FixedNameA)) + { + /* Fixed-size buffer is large enough */ + NameA = FixedNameA; + } + else + { + /* Allocate a larger buffer */ + NameA = HeapAlloc(GetProcessHeap(), 0, Len); + if (NULL == NameA) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return FALSE; + } + } + + /* + * Do the Unicode ->Ascii conversion + */ + if (0 == WideCharToMultiByte(CP_ACP, 0, Name, -1, NameA, Len, NULL, NULL)) + { + /* Something went wrong, clean up */ + if (NameA != FixedNameA) + { + HeapFree(GetProcessHeap(), 0, NameA); + } + return FALSE; + } + + /* + * Call user callback + */ + Ret = Context->UserEnumFunc(NameA, Context->UserContext); + + /* + * Clean up + */ + if (NameA != FixedNameA) + { + HeapFree(GetProcessHeap(), 0, NameA); + } + + return Ret; +} + +/* + * Common code for EnumDesktopsA and EnumWindowStationsA + */ +BOOL FASTCALL +EnumNamesA(HWINSTA WindowStation, + NAMEENUMPROCA EnumFunc, + LPARAM Context, + BOOL Desktops) +{ + ENUMNAMESASCIICONTEXT PrivateContext; + + PrivateContext.UserEnumFunc = EnumFunc; + PrivateContext.UserContext = Context; + + return EnumNamesW(WindowStation, EnumNamesCallback, (LPARAM) &PrivateContext, Desktops); +} + +/* + * @implemented + */ +BOOL WINAPI +EnumWindowStationsA(WINSTAENUMPROCA EnumFunc, + LPARAM Context) +{ + return EnumNamesA(NULL, EnumFunc, Context, FALSE); +} + + +/* + * @implemented + */ +BOOL WINAPI +EnumWindowStationsW(WINSTAENUMPROCW EnumFunc, + LPARAM Context) +{ + return EnumNamesW(NULL, EnumFunc, Context, FALSE); +} + + +/* + * @implemented + */ +HWINSTA WINAPI +OpenWindowStationA(LPCSTR lpszWinSta, + BOOL fInherit, + ACCESS_MASK dwDesiredAccess) +{ + UNICODE_STRING WindowStationNameU; + HWINSTA hWinSta; + + if (lpszWinSta) + { + /* After conversion, the buffer is zero-terminated */ + RtlCreateUnicodeStringFromAsciiz(&WindowStationNameU, lpszWinSta); + } + else + { + RtlInitUnicodeString(&WindowStationNameU, NULL); + } + + hWinSta = OpenWindowStationW(WindowStationNameU.Buffer, + fInherit, + dwDesiredAccess); + + /* Free the string, if it was allocated */ + if (lpszWinSta) RtlFreeUnicodeString(&WindowStationNameU); + + return hWinSta; +} + + +/* + * @implemented + */ +HWINSTA WINAPI +OpenWindowStationW(LPCWSTR lpszWinSta, + BOOL fInherit, + ACCESS_MASK dwDesiredAccess) +{ + UNICODE_STRING WindowStationName; + UNICODE_STRING WindowStationsDir = RTL_CONSTANT_STRING(L"\\Windows\\WindowStations"); + OBJECT_ATTRIBUTES ObjectAttributes; + HANDLE hWindowStationsDir; + NTSTATUS Status; + HWINSTA hwinsta; + + /* Open WindowStations directory */ + InitializeObjectAttributes(&ObjectAttributes, + &WindowStationsDir, + OBJ_CASE_INSENSITIVE, + 0, + 0); + + Status = NtOpenDirectoryObject(&hWindowStationsDir, + DIRECTORY_TRAVERSE, + &ObjectAttributes); + if(!NT_SUCCESS(Status)) + { + ERR("Failed to open WindowStations directory\n"); + return NULL; + } + + /* Open the window station object */ + RtlInitUnicodeString(&WindowStationName, lpszWinSta); + + InitializeObjectAttributes(&ObjectAttributes, + &WindowStationName, + OBJ_CASE_INSENSITIVE, + hWindowStationsDir, + 0); + - if( fInherit == TRUE ) ++ if( fInherit ) + { + ObjectAttributes.Attributes |= OBJ_INHERIT; + } + + hwinsta = NtUserOpenWindowStation(&ObjectAttributes, dwDesiredAccess); + + NtClose(hWindowStationsDir); + + return hwinsta; +} + + +/* + * @unimplemented + */ +DWORD +WINAPI +SetWindowStationUser( + DWORD Unknown1, + DWORD Unknown2, + DWORD Unknown3, + DWORD Unknown4 + ) +{ + return NtUserSetWindowStationUser(Unknown1, Unknown2, Unknown3, Unknown4); +} + +/* EOF */ + diff --cc win32ss/user/winsrv/consrv/frontends/gui/conwnd.c index 0eae0301fb3,00000000000..4e1519a1ab7 mode 100644,000000..100644 --- a/win32ss/user/winsrv/consrv/frontends/gui/conwnd.c +++ b/win32ss/user/winsrv/consrv/frontends/gui/conwnd.c @@@ -1,2453 -1,0 +1,2453 @@@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Console Server DLL + * FILE: frontends/gui/conwnd.c + * PURPOSE: GUI Console Window Class + * PROGRAMMERS: Gé van Geldorp + * Johannes Anderwald + * Jeffrey Morlan + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +/* INCLUDES *******************************************************************/ + +#include +#include +#include + +#define NDEBUG +#include + +#include "guiterm.h" +#include "conwnd.h" +#include "resource.h" + +/* GLOBALS ********************************************************************/ + +// #define PM_CREATE_CONSOLE (WM_APP + 1) +// #define PM_DESTROY_CONSOLE (WM_APP + 2) + +// See guiterm.c +#define CONGUI_MIN_WIDTH 10 +#define CONGUI_MIN_HEIGHT 10 +#define CONGUI_UPDATE_TIME 0 +#define CONGUI_UPDATE_TIMER 1 + +#define CURSOR_BLINK_TIME 500 + + +/**************************************************************\ +\** Define the Console Leader Process for the console window **/ +#define GWLP_CONWND_ALLOC (2 * sizeof(LONG_PTR)) +#define GWLP_CONSOLE_LEADER_PID 0 +#define GWLP_CONSOLE_LEADER_TID 4 + +VOID +SetConWndConsoleLeaderCID(IN PGUI_CONSOLE_DATA GuiData) +{ + PCONSOLE_PROCESS_DATA ProcessData; + CLIENT_ID ConsoleLeaderCID; + + ProcessData = ConSrvGetConsoleLeaderProcess(GuiData->Console); + ConsoleLeaderCID = ProcessData->Process->ClientId; + SetWindowLongPtrW(GuiData->hWindow, GWLP_CONSOLE_LEADER_PID, + (LONG_PTR)(ConsoleLeaderCID.UniqueProcess)); + SetWindowLongPtrW(GuiData->hWindow, GWLP_CONSOLE_LEADER_TID, + (LONG_PTR)(ConsoleLeaderCID.UniqueThread)); +} +/**************************************************************/ + +HICON ghDefaultIcon = NULL; +HICON ghDefaultIconSm = NULL; +HCURSOR ghDefaultCursor = NULL; + +typedef struct _GUICONSOLE_MENUITEM +{ + UINT uID; + const struct _GUICONSOLE_MENUITEM *SubMenu; + WORD wCmdID; +} GUICONSOLE_MENUITEM, *PGUICONSOLE_MENUITEM; + +static const GUICONSOLE_MENUITEM GuiConsoleEditMenuItems[] = +{ + { IDS_MARK, NULL, ID_SYSTEM_EDIT_MARK }, + { IDS_COPY, NULL, ID_SYSTEM_EDIT_COPY }, + { IDS_PASTE, NULL, ID_SYSTEM_EDIT_PASTE }, + { IDS_SELECTALL, NULL, ID_SYSTEM_EDIT_SELECTALL }, + { IDS_SCROLL, NULL, ID_SYSTEM_EDIT_SCROLL }, + { IDS_FIND, NULL, ID_SYSTEM_EDIT_FIND }, + + { 0, NULL, 0 } /* End of list */ +}; + +static const GUICONSOLE_MENUITEM GuiConsoleMainMenuItems[] = +{ + { IDS_EDIT, GuiConsoleEditMenuItems, 0 }, + { IDS_DEFAULTS, NULL, ID_SYSTEM_DEFAULTS }, + { IDS_PROPERTIES, NULL, ID_SYSTEM_PROPERTIES }, + + { 0, NULL, 0 } /* End of list */ +}; + +/* + * Default 16-color palette for foreground and background + * (corresponding flags in comments). + */ +const COLORREF s_Colors[16] = +{ + RGB(0, 0, 0), // (Black) + RGB(0, 0, 128), // BLUE + RGB(0, 128, 0), // GREEN + RGB(0, 128, 128), // BLUE | GREEN + RGB(128, 0, 0), // RED + RGB(128, 0, 128), // BLUE | RED + RGB(128, 128, 0), // GREEN | RED + RGB(192, 192, 192), // BLUE | GREEN | RED + + RGB(128, 128, 128), // (Grey) INTENSITY + RGB(0, 0, 255), // BLUE | INTENSITY + RGB(0, 255, 0), // GREEN | INTENSITY + RGB(0, 255, 255), // BLUE | GREEN | INTENSITY + RGB(255, 0, 0), // RED | INTENSITY + RGB(255, 0, 255), // BLUE | RED | INTENSITY + RGB(255, 255, 0), // GREEN | RED | INTENSITY + RGB(255, 255, 255) // BLUE | GREEN | RED | INTENSITY +}; + +/* FUNCTIONS ******************************************************************/ + +static LRESULT CALLBACK +ConWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + +BOOLEAN +RegisterConWndClass(IN HINSTANCE hInstance) +{ + WNDCLASSEXW WndClass; + ATOM WndClassAtom; + + ghDefaultIcon = LoadImageW(hInstance, + MAKEINTRESOURCEW(IDI_TERMINAL), + IMAGE_ICON, + GetSystemMetrics(SM_CXICON), + GetSystemMetrics(SM_CYICON), + LR_SHARED); + ghDefaultIconSm = LoadImageW(hInstance, + MAKEINTRESOURCEW(IDI_TERMINAL), + IMAGE_ICON, + GetSystemMetrics(SM_CXSMICON), + GetSystemMetrics(SM_CYSMICON), + LR_SHARED); + ghDefaultCursor = LoadCursorW(NULL, IDC_ARROW); + + WndClass.cbSize = sizeof(WNDCLASSEXW); + WndClass.lpszClassName = GUI_CONWND_CLASS; + WndClass.lpfnWndProc = ConWndProc; + WndClass.style = CS_DBLCLKS /* | CS_HREDRAW | CS_VREDRAW */; + WndClass.hInstance = hInstance; + WndClass.hIcon = ghDefaultIcon; + WndClass.hIconSm = ghDefaultIconSm; + WndClass.hCursor = ghDefaultCursor; + WndClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); // The color of a terminal when it is switched off. + WndClass.lpszMenuName = NULL; + WndClass.cbClsExtra = 0; + WndClass.cbWndExtra = GWLP_CONWND_ALLOC; + + WndClassAtom = RegisterClassExW(&WndClass); + if (WndClassAtom == 0) + { + DPRINT1("Failed to register GUI console class\n"); + } + else + { + NtUserConsoleControl(GuiConsoleWndClassAtom, &WndClassAtom, sizeof(ATOM)); + } + + return (WndClassAtom != 0); +} + +BOOLEAN +UnRegisterConWndClass(HINSTANCE hInstance) +{ + return !!UnregisterClassW(GUI_CONWND_CLASS, hInstance); +} + + + +static VOID +GetScreenBufferSizeUnits(IN PCONSOLE_SCREEN_BUFFER Buffer, + IN PGUI_CONSOLE_DATA GuiData, + OUT PUINT WidthUnit, + OUT PUINT HeightUnit) +{ + if (Buffer == NULL || GuiData == NULL || + WidthUnit == NULL || HeightUnit == NULL) + { + return; + } + + if (GetType(Buffer) == TEXTMODE_BUFFER) + { + *WidthUnit = GuiData->CharWidth ; + *HeightUnit = GuiData->CharHeight; + } + else /* if (GetType(Buffer) == GRAPHICS_BUFFER) */ + { + *WidthUnit = 1; + *HeightUnit = 1; + } +} + +static VOID +AppendMenuItems(HMENU hMenu, + const GUICONSOLE_MENUITEM *Items) +{ + UINT i = 0; + WCHAR szMenuString[255]; + HMENU hSubMenu; + + do + { + if (Items[i].uID != (UINT)-1) + { + if (LoadStringW(ConSrvDllInstance, + Items[i].uID, + szMenuString, + sizeof(szMenuString) / sizeof(szMenuString[0])) > 0) + { + if (Items[i].SubMenu != NULL) + { + hSubMenu = CreatePopupMenu(); + if (hSubMenu != NULL) + { + AppendMenuItems(hSubMenu, Items[i].SubMenu); + + if (!AppendMenuW(hMenu, + MF_STRING | MF_POPUP, + (UINT_PTR)hSubMenu, + szMenuString)) + { + DestroyMenu(hSubMenu); + } + } + } + else + { + AppendMenuW(hMenu, + MF_STRING, + Items[i].wCmdID, + szMenuString); + } + } + } + else + { + AppendMenuW(hMenu, + MF_SEPARATOR, + 0, + NULL); + } + i++; + } while (!(Items[i].uID == 0 && Items[i].SubMenu == NULL && Items[i].wCmdID == 0)); +} + +//static +VOID +CreateSysMenu(HWND hWnd) +{ + MENUITEMINFOW mii; + WCHAR szMenuStringBack[255]; + WCHAR *ptrTab; + HMENU hMenu = GetSystemMenu(hWnd, FALSE); + if (hMenu != NULL) + { + mii.cbSize = sizeof(mii); + mii.fMask = MIIM_STRING; + mii.dwTypeData = szMenuStringBack; + mii.cch = sizeof(szMenuStringBack)/sizeof(WCHAR); + + GetMenuItemInfoW(hMenu, SC_CLOSE, FALSE, &mii); + + ptrTab = wcschr(szMenuStringBack, '\t'); + if (ptrTab) + { + *ptrTab = '\0'; + mii.cch = wcslen(szMenuStringBack); + + SetMenuItemInfoW(hMenu, SC_CLOSE, FALSE, &mii); + } + + AppendMenuItems(hMenu, GuiConsoleMainMenuItems); + DrawMenuBar(hWnd); + } +} + +static VOID +SendMenuEvent(PCONSRV_CONSOLE Console, UINT CmdId) +{ + INPUT_RECORD er; + + DPRINT("Menu item ID: %d\n", CmdId); + + if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return; + + er.EventType = MENU_EVENT; + er.Event.MenuEvent.dwCommandId = CmdId; + ConioProcessInputEvent(Console, &er); + + LeaveCriticalSection(&Console->Lock); +} + +static VOID +Copy(PGUI_CONSOLE_DATA GuiData); +static VOID +Paste(PGUI_CONSOLE_DATA GuiData); +static VOID +UpdateSelection(PGUI_CONSOLE_DATA GuiData, + PCOORD SelectionAnchor OPTIONAL, + PCOORD coord); + +static VOID +Mark(PGUI_CONSOLE_DATA GuiData) +{ + PCONSOLE_SCREEN_BUFFER ActiveBuffer = GuiData->ActiveBuffer; + + /* Clear the old selection */ + GuiData->Selection.dwFlags = CONSOLE_NO_SELECTION; + + /* Restart a new selection */ + GuiData->dwSelectionCursor = ActiveBuffer->ViewOrigin; + UpdateSelection(GuiData, + &GuiData->dwSelectionCursor, + &GuiData->dwSelectionCursor); +} + +static VOID +SelectAll(PGUI_CONSOLE_DATA GuiData) +{ + PCONSOLE_SCREEN_BUFFER ActiveBuffer = GuiData->ActiveBuffer; + COORD SelectionAnchor; + + /* Clear the old selection */ + GuiData->Selection.dwFlags = CONSOLE_NO_SELECTION; + + /* + * The selection area extends to the whole screen buffer's width. + */ + SelectionAnchor.X = SelectionAnchor.Y = 0; + GuiData->dwSelectionCursor.X = ActiveBuffer->ScreenBufferSize.X - 1; + + /* + * Determine whether the selection must extend to just some part + * (for text-mode screen buffers) or to all of the screen buffer's + * height (for graphics ones). + */ + if (GetType(ActiveBuffer) == TEXTMODE_BUFFER) + { + /* + * We select all the characters from the first line + * to the line where the cursor is positioned. + */ + GuiData->dwSelectionCursor.Y = ActiveBuffer->CursorPosition.Y; + } + else /* if (GetType(ActiveBuffer) == GRAPHICS_BUFFER) */ + { + /* + * We select all the screen buffer area. + */ + GuiData->dwSelectionCursor.Y = ActiveBuffer->ScreenBufferSize.Y - 1; + } + + /* Restart a new selection */ + GuiData->Selection.dwFlags |= CONSOLE_MOUSE_SELECTION; + UpdateSelection(GuiData, &SelectionAnchor, &GuiData->dwSelectionCursor); +} + +static LRESULT +OnCommand(PGUI_CONSOLE_DATA GuiData, WPARAM wParam, LPARAM lParam) +{ + LRESULT Ret = TRUE; + PCONSRV_CONSOLE Console = GuiData->Console; + + /* + * In case the selected menu item belongs to the user-reserved menu id range, + * send to him a menu event and return directly. The user must handle those + * reserved menu commands... + */ + if (GuiData->CmdIdLow <= (UINT)wParam && (UINT)wParam <= GuiData->CmdIdHigh) + { + SendMenuEvent(Console, (UINT)wParam); + goto Quit; + } + + /* ... otherwise, perform actions. */ + switch (wParam) + { + case ID_SYSTEM_EDIT_MARK: + Mark(GuiData); + break; + + case ID_SYSTEM_EDIT_COPY: + Copy(GuiData); + break; + + case ID_SYSTEM_EDIT_PASTE: + Paste(GuiData); + break; + + case ID_SYSTEM_EDIT_SELECTALL: + SelectAll(GuiData); + break; + + case ID_SYSTEM_EDIT_SCROLL: + DPRINT1("Scrolling is not handled yet\n"); + break; + + case ID_SYSTEM_EDIT_FIND: + DPRINT1("Finding is not handled yet\n"); + break; + + case ID_SYSTEM_DEFAULTS: + GuiConsoleShowConsoleProperties(GuiData, TRUE); + break; + + case ID_SYSTEM_PROPERTIES: + GuiConsoleShowConsoleProperties(GuiData, FALSE); + break; + + default: + Ret = FALSE; + break; + } + +Quit: + if (!Ret) + Ret = DefWindowProcW(GuiData->hWindow, WM_SYSCOMMAND, wParam, lParam); + + return Ret; +} + +static PGUI_CONSOLE_DATA +GuiGetGuiData(HWND hWnd) +{ + /* This function ensures that the console pointer is not NULL */ + PGUI_CONSOLE_DATA GuiData = (PGUI_CONSOLE_DATA)GetWindowLongPtrW(hWnd, GWLP_USERDATA); + return ( ((GuiData == NULL) || (GuiData->hWindow == hWnd && GuiData->Console != NULL)) ? GuiData : NULL ); +} + +static VOID +ResizeConWnd(PGUI_CONSOLE_DATA GuiData, DWORD WidthUnit, DWORD HeightUnit) +{ + PCONSOLE_SCREEN_BUFFER Buff = GuiData->ActiveBuffer; + SCROLLINFO sInfo; + + DWORD Width, Height; + + Width = Buff->ViewSize.X * WidthUnit + + 2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE)); + Height = Buff->ViewSize.Y * HeightUnit + + 2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION); + + /* Set scrollbar sizes */ + sInfo.cbSize = sizeof(SCROLLINFO); + sInfo.fMask = SIF_RANGE | SIF_PAGE | SIF_POS; + sInfo.nMin = 0; + if (Buff->ScreenBufferSize.Y > Buff->ViewSize.Y) + { + sInfo.nMax = Buff->ScreenBufferSize.Y - 1; + sInfo.nPage = Buff->ViewSize.Y; + sInfo.nPos = Buff->ViewOrigin.Y; + SetScrollInfo(GuiData->hWindow, SB_VERT, &sInfo, TRUE); + Width += GetSystemMetrics(SM_CXVSCROLL); + ShowScrollBar(GuiData->hWindow, SB_VERT, TRUE); + } + else + { + ShowScrollBar(GuiData->hWindow, SB_VERT, FALSE); + } + + if (Buff->ScreenBufferSize.X > Buff->ViewSize.X) + { + sInfo.nMax = Buff->ScreenBufferSize.X - 1; + sInfo.nPage = Buff->ViewSize.X; + sInfo.nPos = Buff->ViewOrigin.X; + SetScrollInfo(GuiData->hWindow, SB_HORZ, &sInfo, TRUE); + Height += GetSystemMetrics(SM_CYHSCROLL); + ShowScrollBar(GuiData->hWindow, SB_HORZ, TRUE); + } + else + { + ShowScrollBar(GuiData->hWindow, SB_HORZ, FALSE); + } + + /* Resize the window */ + SetWindowPos(GuiData->hWindow, NULL, 0, 0, Width, Height, + SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOCOPYBITS); + // NOTE: The SWP_NOCOPYBITS flag can be replaced by a subsequent call + // to: InvalidateRect(GuiData->hWindow, NULL, TRUE); +} + + +VOID +DeleteFonts(PGUI_CONSOLE_DATA GuiData) +{ + ULONG i; + for (i = 0; i < sizeof(GuiData->Font) / sizeof(GuiData->Font[0]); ++i) + { + if (GuiData->Font[i] != NULL) DeleteObject(GuiData->Font[i]); + GuiData->Font[i] = NULL; + } +} + +static HFONT +CreateDerivedFont(HFONT OrgFont, + // COORD FontSize, + ULONG FontWeight, + // BOOLEAN bItalic, + BOOLEAN bUnderline, + BOOLEAN bStrikeOut) +{ + LOGFONT lf; + + /* Initialize the LOGFONT structure */ + RtlZeroMemory(&lf, sizeof(lf)); + + /* Retrieve the details of the current font */ + if (GetObject(OrgFont, sizeof(lf), &lf) == 0) + return NULL; + + /* Change the font attributes */ + // lf.lfHeight = FontSize.Y; + // lf.lfWidth = FontSize.X; + lf.lfWeight = FontWeight; + // lf.lfItalic = bItalic; + lf.lfUnderline = bUnderline; + lf.lfStrikeOut = bStrikeOut; + + /* Build a new font */ + return CreateFontIndirect(&lf); +} + +BOOL +InitFonts(PGUI_CONSOLE_DATA GuiData, + LPWSTR FaceName, // Points to a WCHAR array of LF_FACESIZE elements. + ULONG FontFamily, + COORD FontSize, + ULONG FontWeight) +{ + HDC hDC; + HFONT OldFont, NewFont; + TEXTMETRICW Metrics; + SIZE CharSize; + + hDC = GetDC(GuiData->hWindow); + + /* + * Initialize a new NORMAL font and get its metrics. + */ + + FontSize.Y = FontSize.Y > 0 ? -MulDiv(FontSize.Y, GetDeviceCaps(hDC, LOGPIXELSY), 72) + : FontSize.Y; + + NewFont = CreateFontW(FontSize.Y, + FontSize.X, + 0, + TA_BASELINE, + FontWeight, + FALSE, + FALSE, + FALSE, + OEM_CHARSET, + OUT_DEFAULT_PRECIS, + CLIP_DEFAULT_PRECIS, + DEFAULT_QUALITY, + FIXED_PITCH | FontFamily, + FaceName); + if (NewFont == NULL) + { + DPRINT1("InitFonts: CreateFontW failed\n"); + ReleaseDC(GuiData->hWindow, hDC); + return FALSE; + } + + OldFont = SelectObject(hDC, NewFont); + if (OldFont == NULL) + { + DPRINT1("InitFonts: SelectObject failed\n"); + ReleaseDC(GuiData->hWindow, hDC); + DeleteObject(NewFont); + return FALSE; + } + + if (!GetTextMetricsW(hDC, &Metrics)) + { + DPRINT1("InitFonts: GetTextMetrics failed\n"); + SelectObject(hDC, OldFont); + ReleaseDC(GuiData->hWindow, hDC); + DeleteObject(NewFont); + return FALSE; + } + GuiData->CharWidth = Metrics.tmMaxCharWidth; + GuiData->CharHeight = Metrics.tmHeight + Metrics.tmExternalLeading; + + /* Measure real char width more precisely if possible */ + if (GetTextExtentPoint32W(hDC, L"R", 1, &CharSize)) + GuiData->CharWidth = CharSize.cx; + + SelectObject(hDC, OldFont); + ReleaseDC(GuiData->hWindow, hDC); + + /* + * Initialization succeeded. + */ + // Delete all the old fonts first. + DeleteFonts(GuiData); + GuiData->Font[FONT_NORMAL] = NewFont; + + /* + * Now build the other fonts (bold, underlined, mixed). + */ + GuiData->Font[FONT_BOLD] = + CreateDerivedFont(GuiData->Font[FONT_NORMAL], + FontWeight < FW_BOLD ? FW_BOLD : FontWeight, + FALSE, + FALSE); + GuiData->Font[FONT_UNDERLINE] = + CreateDerivedFont(GuiData->Font[FONT_NORMAL], + FontWeight, + TRUE, + FALSE); + GuiData->Font[FONT_BOLD | FONT_UNDERLINE] = + CreateDerivedFont(GuiData->Font[FONT_NORMAL], + FontWeight < FW_BOLD ? FW_BOLD : FontWeight, + TRUE, + FALSE); + + /* + * Save the settings. + */ + if (FaceName != GuiData->GuiInfo.FaceName) + { + wcsncpy(GuiData->GuiInfo.FaceName, FaceName, LF_FACESIZE); + GuiData->GuiInfo.FaceName[LF_FACESIZE - 1] = UNICODE_NULL; + } + GuiData->GuiInfo.FontFamily = FontFamily; + GuiData->GuiInfo.FontSize = FontSize; + GuiData->GuiInfo.FontWeight = FontWeight; + + return TRUE; +} + + +static BOOL +OnNcCreate(HWND hWnd, LPCREATESTRUCTW Create) +{ + PGUI_CONSOLE_DATA GuiData = (PGUI_CONSOLE_DATA)Create->lpCreateParams; + PCONSRV_CONSOLE Console; + + if (NULL == GuiData) + { + DPRINT1("GuiConsoleNcCreate: No GUI data\n"); + return FALSE; + } + + Console = GuiData->Console; + + GuiData->hWindow = hWnd; + + /* Initialize the fonts */ + if (!InitFonts(GuiData, + GuiData->GuiInfo.FaceName, + GuiData->GuiInfo.FontFamily, + GuiData->GuiInfo.FontSize, + GuiData->GuiInfo.FontWeight)) + { + DPRINT1("GuiConsoleNcCreate: InitFonts failed\n"); + GuiData->hWindow = NULL; + SetEvent(GuiData->hGuiInitEvent); + return FALSE; + } + + /* Initialize the terminal framebuffer */ + GuiData->hMemDC = CreateCompatibleDC(NULL); + GuiData->hBitmap = NULL; + GuiData->hSysPalette = NULL; /* Original system palette */ + + /* Update the icons of the window */ + if (GuiData->hIcon != ghDefaultIcon) + { + DefWindowProcW(GuiData->hWindow, WM_SETICON, ICON_BIG , (LPARAM)GuiData->hIcon ); + DefWindowProcW(GuiData->hWindow, WM_SETICON, ICON_SMALL, (LPARAM)GuiData->hIconSm); + } + + // FIXME: Keep these instructions here ? /////////////////////////////////// + Console->ActiveBuffer->CursorBlinkOn = TRUE; + Console->ActiveBuffer->ForceCursorOff = FALSE; + //////////////////////////////////////////////////////////////////////////// + + SetWindowLongPtrW(GuiData->hWindow, GWLP_USERDATA, (DWORD_PTR)GuiData); + + SetTimer(GuiData->hWindow, CONGUI_UPDATE_TIMER, CONGUI_UPDATE_TIME, NULL); + // FIXME: HACK: Potential HACK for CORE-8129; see revision 63595. + //CreateSysMenu(GuiData->hWindow); + + DPRINT("OnNcCreate - setting start event\n"); + SetEvent(GuiData->hGuiInitEvent); + + return (BOOL)DefWindowProcW(GuiData->hWindow, WM_NCCREATE, 0, (LPARAM)Create); +} + + +BOOL +EnterFullScreen(PGUI_CONSOLE_DATA GuiData); +VOID +LeaveFullScreen(PGUI_CONSOLE_DATA GuiData); +VOID +SwitchFullScreen(PGUI_CONSOLE_DATA GuiData, BOOL FullScreen); +VOID +GuiConsoleSwitchFullScreen(PGUI_CONSOLE_DATA GuiData); + +static VOID +OnActivate(PGUI_CONSOLE_DATA GuiData, WPARAM wParam) +{ + WORD ActivationState = LOWORD(wParam); + + DPRINT("WM_ACTIVATE - ActivationState = %d\n"); + + if ( ActivationState == WA_ACTIVE || + ActivationState == WA_CLICKACTIVE ) + { + if (GuiData->GuiInfo.FullScreen) + { + EnterFullScreen(GuiData); + // // PostMessageW(GuiData->hWindow, WM_SYSCOMMAND, SC_RESTORE, 0); + // SendMessageW(GuiData->hWindow, WM_SYSCOMMAND, SC_RESTORE, 0); + } + } + else // if (ActivationState == WA_INACTIVE) + { + if (GuiData->GuiInfo.FullScreen) + { + SendMessageW(GuiData->hWindow, WM_SYSCOMMAND, SC_MINIMIZE, 0); + LeaveFullScreen(GuiData); + // // PostMessageW(GuiData->hWindow, WM_SYSCOMMAND, SC_MINIMIZE, 0); + // SendMessageW(GuiData->hWindow, WM_SYSCOMMAND, SC_MINIMIZE, 0); + } + } + + /* + * Ignore the next mouse signal when we are going to be enabled again via + * the mouse, in order to prevent, e.g. when we are in Edit mode, erroneous + * mouse actions from the user that could spoil text selection or copy/pastes. + */ + if (ActivationState == WA_CLICKACTIVE) + GuiData->IgnoreNextMouseSignal = TRUE; +} + +static VOID +OnFocus(PGUI_CONSOLE_DATA GuiData, BOOL SetFocus) +{ + PCONSRV_CONSOLE Console = GuiData->Console; + INPUT_RECORD er; + + if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return; + + er.EventType = FOCUS_EVENT; + er.Event.FocusEvent.bSetFocus = SetFocus; + ConioProcessInputEvent(Console, &er); + + LeaveCriticalSection(&Console->Lock); + + if (SetFocus) + DPRINT1("TODO: Create console caret\n"); + else + DPRINT1("TODO: Destroy console caret\n"); +} + +static VOID +SmallRectToRect(PGUI_CONSOLE_DATA GuiData, PRECT Rect, PSMALL_RECT SmallRect) +{ + PCONSOLE_SCREEN_BUFFER Buffer = GuiData->ActiveBuffer; + UINT WidthUnit, HeightUnit; + + GetScreenBufferSizeUnits(Buffer, GuiData, &WidthUnit, &HeightUnit); + + Rect->left = (SmallRect->Left - Buffer->ViewOrigin.X) * WidthUnit ; + Rect->top = (SmallRect->Top - Buffer->ViewOrigin.Y) * HeightUnit; + Rect->right = (SmallRect->Right + 1 - Buffer->ViewOrigin.X) * WidthUnit ; + Rect->bottom = (SmallRect->Bottom + 1 - Buffer->ViewOrigin.Y) * HeightUnit; +} + +VOID +GetSelectionBeginEnd(PCOORD Begin, PCOORD End, + PCOORD SelectionAnchor, + PSMALL_RECT SmallRect) +{ + if (Begin == NULL || End == NULL) return; + + *Begin = *SelectionAnchor; + End->X = (SelectionAnchor->X == SmallRect->Left) ? SmallRect->Right + /* Case X != Left, must be == Right */ : SmallRect->Left; + End->Y = (SelectionAnchor->Y == SmallRect->Top ) ? SmallRect->Bottom + /* Case Y != Top, must be == Bottom */ : SmallRect->Top; + + /* Exchange Begin / End if Begin > End lexicographically */ + if (Begin->Y > End->Y || (Begin->Y == End->Y && Begin->X > End->X)) + { + End->X = _InterlockedExchange16(&Begin->X, End->X); + End->Y = _InterlockedExchange16(&Begin->Y, End->Y); + } +} + +static HRGN +CreateSelectionRgn(PGUI_CONSOLE_DATA GuiData, + BOOL LineSelection, + PCOORD SelectionAnchor, + PSMALL_RECT SmallRect) +{ + if (!LineSelection) + { + RECT rect; + SmallRectToRect(GuiData, &rect, SmallRect); + return CreateRectRgnIndirect(&rect); + } + else + { + HRGN SelRgn; + COORD Begin, End; + + GetSelectionBeginEnd(&Begin, &End, SelectionAnchor, SmallRect); + + if (Begin.Y == End.Y) + { + SMALL_RECT sr; + RECT r ; + + sr.Left = Begin.X; + sr.Top = Begin.Y; + sr.Right = End.X; + sr.Bottom = End.Y; + + // Debug thingie to see whether I can put this corner case + // together with the previous one. + if (SmallRect->Left != sr.Left || + SmallRect->Top != sr.Top || + SmallRect->Right != sr.Right || + SmallRect->Bottom != sr.Bottom) + { + DPRINT1("\n" + "SmallRect = (%d, %d, %d, %d)\n" + "sr = (%d, %d, %d, %d)\n" + "\n", + SmallRect->Left, SmallRect->Top, SmallRect->Right, SmallRect->Bottom, + sr.Left, sr.Top, sr.Right, sr.Bottom); + } + + SmallRectToRect(GuiData, &r, &sr); + SelRgn = CreateRectRgnIndirect(&r); + } + else + { + PCONSOLE_SCREEN_BUFFER ActiveBuffer = GuiData->ActiveBuffer; + + HRGN rg1, rg2, rg3; + SMALL_RECT sr1, sr2, sr3; + RECT r1 , r2 , r3 ; + + sr1.Left = Begin.X; + sr1.Top = Begin.Y; + sr1.Right = ActiveBuffer->ScreenBufferSize.X - 1; + sr1.Bottom = Begin.Y; + + sr2.Left = 0; + sr2.Top = Begin.Y + 1; + sr2.Right = ActiveBuffer->ScreenBufferSize.X - 1; + sr2.Bottom = End.Y - 1; + + sr3.Left = 0; + sr3.Top = End.Y; + sr3.Right = End.X; + sr3.Bottom = End.Y; + + SmallRectToRect(GuiData, &r1, &sr1); + SmallRectToRect(GuiData, &r2, &sr2); + SmallRectToRect(GuiData, &r3, &sr3); + + rg1 = CreateRectRgnIndirect(&r1); + rg2 = CreateRectRgnIndirect(&r2); + rg3 = CreateRectRgnIndirect(&r3); + + CombineRgn(rg1, rg1, rg2, RGN_XOR); + CombineRgn(rg1, rg1, rg3, RGN_XOR); + DeleteObject(rg3); + DeleteObject(rg2); + + SelRgn = rg1; + } + + return SelRgn; + } +} + +static VOID +PaintSelectionRect(PGUI_CONSOLE_DATA GuiData, PPAINTSTRUCT pps) +{ + HRGN rgnPaint = CreateRectRgnIndirect(&pps->rcPaint); + HRGN rgnSel = CreateSelectionRgn(GuiData, GuiData->LineSelection, + &GuiData->Selection.dwSelectionAnchor, + &GuiData->Selection.srSelection); + + /* Invert the selection */ + + int ErrorCode = CombineRgn(rgnPaint, rgnPaint, rgnSel, RGN_AND); + if (ErrorCode != ERROR && ErrorCode != NULLREGION) + { + InvertRgn(pps->hdc, rgnPaint); + } + + DeleteObject(rgnSel); + DeleteObject(rgnPaint); +} + +static VOID +UpdateSelection(PGUI_CONSOLE_DATA GuiData, + PCOORD SelectionAnchor OPTIONAL, + PCOORD coord) +{ + PCONSRV_CONSOLE Console = GuiData->Console; + HRGN oldRgn = CreateSelectionRgn(GuiData, GuiData->LineSelection, + &GuiData->Selection.dwSelectionAnchor, + &GuiData->Selection.srSelection); + + /* Update the anchor if needed (use the old one if NULL) */ + if (SelectionAnchor) + GuiData->Selection.dwSelectionAnchor = *SelectionAnchor; + + if (coord != NULL) + { + SMALL_RECT rc; + HRGN newRgn; + + /* + * Pressing the Control key while selecting text, allows us to enter + * into line-selection mode, the selection mode of *nix terminals. + */ + BOOL OldLineSel = GuiData->LineSelection; + GuiData->LineSelection = !!(GetKeyState(VK_CONTROL) & 0x8000); + + /* Exchange left/top with right/bottom if required */ + rc.Left = min(GuiData->Selection.dwSelectionAnchor.X, coord->X); + rc.Top = min(GuiData->Selection.dwSelectionAnchor.Y, coord->Y); + rc.Right = max(GuiData->Selection.dwSelectionAnchor.X, coord->X); + rc.Bottom = max(GuiData->Selection.dwSelectionAnchor.Y, coord->Y); + + newRgn = CreateSelectionRgn(GuiData, GuiData->LineSelection, + &GuiData->Selection.dwSelectionAnchor, + &rc); + + if (GuiData->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY) + { + if (OldLineSel != GuiData->LineSelection || + memcmp(&rc, &GuiData->Selection.srSelection, sizeof(SMALL_RECT)) != 0) + { + /* Calculate the region that needs to be updated */ + if (oldRgn && newRgn && CombineRgn(newRgn, newRgn, oldRgn, RGN_XOR) != ERROR) + { + InvalidateRgn(GuiData->hWindow, newRgn, FALSE); + } + } + } + else + { + InvalidateRgn(GuiData->hWindow, newRgn, FALSE); + } + + DeleteObject(newRgn); + + GuiData->Selection.dwFlags |= CONSOLE_SELECTION_NOT_EMPTY; + GuiData->Selection.srSelection = rc; + GuiData->dwSelectionCursor = *coord; + + if ((GuiData->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) == 0) + { + LPWSTR SelTypeStr = NULL , WindowTitle = NULL; + SIZE_T SelTypeStrLength = 0, Length = 0; + + /* Clear the old selection */ + if (GuiData->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY) + { + InvalidateRgn(GuiData->hWindow, oldRgn, FALSE); + } + + /* + * When passing a zero-length buffer size, LoadString(...) returns + * a read-only pointer buffer to the program's resource string. + */ + SelTypeStrLength = + LoadStringW(ConSrvDllInstance, + (GuiData->Selection.dwFlags & CONSOLE_MOUSE_SELECTION) + ? IDS_SELECT_TITLE : IDS_MARK_TITLE, + (LPWSTR)&SelTypeStr, 0); + + /* + * Prepend the selection type string to the current console title + * if we succeeded in retrieving a valid localized string. + */ + if (SelTypeStr) + { + // 3 for " - " and 1 for NULL + Length = Console->Title.Length + (SelTypeStrLength + 3 + 1) * sizeof(WCHAR); + WindowTitle = ConsoleAllocHeap(0, Length); + + wcsncpy(WindowTitle, SelTypeStr, SelTypeStrLength); + WindowTitle[SelTypeStrLength] = L'\0'; + wcscat(WindowTitle, L" - "); + wcscat(WindowTitle, Console->Title.Buffer); + + SetWindowText(GuiData->hWindow, WindowTitle); + ConsoleFreeHeap(WindowTitle); + } + + GuiData->Selection.dwFlags |= CONSOLE_SELECTION_IN_PROGRESS; + ConioPause(Console, PAUSED_FROM_SELECTION); + } + } + else + { + /* Clear the selection */ + if (GuiData->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY) + { + InvalidateRgn(GuiData->hWindow, oldRgn, FALSE); + } + + GuiData->Selection.dwFlags = CONSOLE_NO_SELECTION; + ConioUnpause(Console, PAUSED_FROM_SELECTION); + + /* Restore the console title */ + SetWindowText(GuiData->hWindow, Console->Title.Buffer); + } + + DeleteObject(oldRgn); +} + + +VOID +GuiPaintTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer, + PGUI_CONSOLE_DATA GuiData, + PRECT rcView, + PRECT rcFramebuffer); +VOID +GuiPaintGraphicsBuffer(PGRAPHICS_SCREEN_BUFFER Buffer, + PGUI_CONSOLE_DATA GuiData, + PRECT rcView, + PRECT rcFramebuffer); + +static VOID +OnPaint(PGUI_CONSOLE_DATA GuiData) +{ + PCONSOLE_SCREEN_BUFFER ActiveBuffer = GuiData->ActiveBuffer; + PAINTSTRUCT ps; + RECT rcPaint; + + BeginPaint(GuiData->hWindow, &ps); + if (ps.hdc != NULL && + ps.rcPaint.left < ps.rcPaint.right && + ps.rcPaint.top < ps.rcPaint.bottom) + { + EnterCriticalSection(&GuiData->Lock); + + /* Compose the current screen-buffer on-memory */ + if (GetType(ActiveBuffer) == TEXTMODE_BUFFER) + { + GuiPaintTextModeBuffer((PTEXTMODE_SCREEN_BUFFER)ActiveBuffer, + GuiData, &ps.rcPaint, &rcPaint); + } + else /* if (GetType(ActiveBuffer) == GRAPHICS_BUFFER) */ + { + GuiPaintGraphicsBuffer((PGRAPHICS_SCREEN_BUFFER)ActiveBuffer, + GuiData, &ps.rcPaint, &rcPaint); + } + + /* Send it to screen */ + BitBlt(ps.hdc, + ps.rcPaint.left, + ps.rcPaint.top, + rcPaint.right - rcPaint.left, + rcPaint.bottom - rcPaint.top, + GuiData->hMemDC, + rcPaint.left, + rcPaint.top, + SRCCOPY); + + /* Draw the selection region if needed */ + if (GuiData->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY) + { + PaintSelectionRect(GuiData, &ps); + } + + LeaveCriticalSection(&GuiData->Lock); + } + EndPaint(GuiData->hWindow, &ps); + + return; +} + +static VOID +OnPaletteChanged(PGUI_CONSOLE_DATA GuiData) +{ + PCONSOLE_SCREEN_BUFFER ActiveBuffer = GuiData->ActiveBuffer; + + // See WM_PALETTECHANGED message + // if ((HWND)wParam == hWnd) break; + + // if (GetType(ActiveBuffer) == GRAPHICS_BUFFER) + if (ActiveBuffer->PaletteHandle) + { + DPRINT("WM_PALETTECHANGED changing palette\n"); + + /* Specify the use of the system palette for the framebuffer */ + SetSystemPaletteUse(GuiData->hMemDC, ActiveBuffer->PaletteUsage); + + /* Realize the (logical) palette */ + RealizePalette(GuiData->hMemDC); + } +} + +static BOOL +IsSystemKey(WORD VirtualKeyCode) +{ + switch (VirtualKeyCode) + { + /* From MSDN, "Virtual-Key Codes" */ + case VK_RETURN: + case VK_SHIFT: + case VK_CONTROL: + case VK_MENU: + case VK_PAUSE: + case VK_CAPITAL: + case VK_ESCAPE: + case VK_LWIN: + case VK_RWIN: + case VK_NUMLOCK: + case VK_SCROLL: + return TRUE; + default: + return FALSE; + } +} + +static VOID +OnKey(PGUI_CONSOLE_DATA GuiData, UINT msg, WPARAM wParam, LPARAM lParam) +{ + PCONSRV_CONSOLE Console = GuiData->Console; + PCONSOLE_SCREEN_BUFFER ActiveBuffer; + + if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return; + + ActiveBuffer = GuiData->ActiveBuffer; + + if (GuiData->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) + { + WORD VirtualKeyCode = LOWORD(wParam); + + if (msg != WM_KEYDOWN) goto Quit; + + if (VirtualKeyCode == VK_RETURN) + { + /* Copy (and clear) selection if ENTER is pressed */ + Copy(GuiData); + goto Quit; + } + else if ( VirtualKeyCode == VK_ESCAPE || + (VirtualKeyCode == 'C' && (GetKeyState(VK_CONTROL) & 0x8000)) ) + { + /* Cancel selection if ESC or Ctrl-C are pressed */ + UpdateSelection(GuiData, NULL, NULL); + goto Quit; + } + + if ((GuiData->Selection.dwFlags & CONSOLE_MOUSE_SELECTION) == 0) + { + /* Keyboard selection mode */ + BOOL Interpreted = FALSE; + BOOL MajPressed = !!(GetKeyState(VK_SHIFT) & 0x8000); + + switch (VirtualKeyCode) + { + case VK_LEFT: + { + Interpreted = TRUE; + if (GuiData->dwSelectionCursor.X > 0) + GuiData->dwSelectionCursor.X--; + + break; + } + + case VK_RIGHT: + { + Interpreted = TRUE; + if (GuiData->dwSelectionCursor.X < ActiveBuffer->ScreenBufferSize.X - 1) + GuiData->dwSelectionCursor.X++; + + break; + } + + case VK_UP: + { + Interpreted = TRUE; + if (GuiData->dwSelectionCursor.Y > 0) + GuiData->dwSelectionCursor.Y--; + + break; + } + + case VK_DOWN: + { + Interpreted = TRUE; + if (GuiData->dwSelectionCursor.Y < ActiveBuffer->ScreenBufferSize.Y - 1) + GuiData->dwSelectionCursor.Y++; + + break; + } + + case VK_HOME: + { + Interpreted = TRUE; + GuiData->dwSelectionCursor.X = 0; + GuiData->dwSelectionCursor.Y = 0; + break; + } + + case VK_END: + { + Interpreted = TRUE; + GuiData->dwSelectionCursor.Y = ActiveBuffer->ScreenBufferSize.Y - 1; + break; + } + + case VK_PRIOR: + { + Interpreted = TRUE; + GuiData->dwSelectionCursor.Y -= ActiveBuffer->ViewSize.Y; + if (GuiData->dwSelectionCursor.Y < 0) + GuiData->dwSelectionCursor.Y = 0; + + break; + } + + case VK_NEXT: + { + Interpreted = TRUE; + GuiData->dwSelectionCursor.Y += ActiveBuffer->ViewSize.Y; + if (GuiData->dwSelectionCursor.Y >= ActiveBuffer->ScreenBufferSize.Y) + GuiData->dwSelectionCursor.Y = ActiveBuffer->ScreenBufferSize.Y - 1; + + break; + } + + default: + break; + } + + if (Interpreted) + { + UpdateSelection(GuiData, + !MajPressed ? &GuiData->dwSelectionCursor : NULL, + &GuiData->dwSelectionCursor); + } + else if (!IsSystemKey(VirtualKeyCode)) + { + /* Emit an error beep sound */ + SendNotifyMessage(GuiData->hWindow, PM_CONSOLE_BEEP, 0, 0); + } + + goto Quit; + } + else + { + /* Mouse selection mode */ + + if (!IsSystemKey(VirtualKeyCode)) + { + /* Clear the selection and send the key into the input buffer */ + UpdateSelection(GuiData, NULL, NULL); + } + else + { + goto Quit; + } + } + } + + if ((GuiData->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) == 0) + { + MSG Message; + + Message.hwnd = GuiData->hWindow; + Message.message = msg; + Message.wParam = wParam; + Message.lParam = lParam; + + ConioProcessKey(Console, &Message); + } + +Quit: + LeaveCriticalSection(&Console->Lock); +} + + +// FIXME: Remove after fixing OnTimer +VOID +InvalidateCell(PGUI_CONSOLE_DATA GuiData, + SHORT x, SHORT y); + +static VOID +OnTimer(PGUI_CONSOLE_DATA GuiData) +{ + PCONSRV_CONSOLE Console = GuiData->Console; + PCONSOLE_SCREEN_BUFFER Buff; + + SetTimer(GuiData->hWindow, CONGUI_UPDATE_TIMER, CURSOR_BLINK_TIME, NULL); + + if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return; + + Buff = GuiData->ActiveBuffer; + + if (GetType(Buff) == TEXTMODE_BUFFER) + { + InvalidateCell(GuiData, Buff->CursorPosition.X, Buff->CursorPosition.Y); + Buff->CursorBlinkOn = !Buff->CursorBlinkOn; + + if ((GuiData->OldCursor.x != Buff->CursorPosition.X) || + (GuiData->OldCursor.y != Buff->CursorPosition.Y)) + { + SCROLLINFO xScroll; + int OldScrollX = -1, OldScrollY = -1; + int NewScrollX = -1, NewScrollY = -1; + + xScroll.cbSize = sizeof(SCROLLINFO); + xScroll.fMask = SIF_POS; + // Capture the original position of the scroll bars and save them. + if (GetScrollInfo(GuiData->hWindow, SB_HORZ, &xScroll)) OldScrollX = xScroll.nPos; + if (GetScrollInfo(GuiData->hWindow, SB_VERT, &xScroll)) OldScrollY = xScroll.nPos; + + // If we successfully got the info for the horizontal scrollbar + if (OldScrollX >= 0) + { + if ((Buff->CursorPosition.X < Buff->ViewOrigin.X) || + (Buff->CursorPosition.X >= (Buff->ViewOrigin.X + Buff->ViewSize.X))) + { + // Handle the horizontal scroll bar + if (Buff->CursorPosition.X >= Buff->ViewSize.X) + NewScrollX = Buff->CursorPosition.X - Buff->ViewSize.X + 1; + else + NewScrollX = 0; + } + else + { + NewScrollX = OldScrollX; + } + } + // If we successfully got the info for the vertical scrollbar + if (OldScrollY >= 0) + { + if ((Buff->CursorPosition.Y < Buff->ViewOrigin.Y) || + (Buff->CursorPosition.Y >= (Buff->ViewOrigin.Y + Buff->ViewSize.Y))) + { + // Handle the vertical scroll bar + if (Buff->CursorPosition.Y >= Buff->ViewSize.Y) + NewScrollY = Buff->CursorPosition.Y - Buff->ViewSize.Y + 1; + else + NewScrollY = 0; + } + else + { + NewScrollY = OldScrollY; + } + } + + // Adjust scroll bars and refresh the window if the cursor has moved outside the visible area + // NOTE: OldScroll# and NewScroll# will both be -1 (initial value) if the info for the respective scrollbar + // was not obtained successfully in the previous steps. This means their difference is 0 (no scrolling) + // and their associated scrollbar is left alone. + if ((OldScrollX != NewScrollX) || (OldScrollY != NewScrollY)) + { + Buff->ViewOrigin.X = NewScrollX; + Buff->ViewOrigin.Y = NewScrollY; + ScrollWindowEx(GuiData->hWindow, + (OldScrollX - NewScrollX) * GuiData->CharWidth, + (OldScrollY - NewScrollY) * GuiData->CharHeight, + NULL, + NULL, + NULL, + NULL, + SW_INVALIDATE); + if (NewScrollX >= 0) + { + xScroll.nPos = NewScrollX; + SetScrollInfo(GuiData->hWindow, SB_HORZ, &xScroll, TRUE); + } + if (NewScrollY >= 0) + { + xScroll.nPos = NewScrollY; + SetScrollInfo(GuiData->hWindow, SB_VERT, &xScroll, TRUE); + } + UpdateWindow(GuiData->hWindow); + // InvalidateRect(GuiData->hWindow, NULL, FALSE); + GuiData->OldCursor.x = Buff->CursorPosition.X; + GuiData->OldCursor.y = Buff->CursorPosition.Y; + } + } + } + else /* if (GetType(Buff) == GRAPHICS_BUFFER) */ + { + } + + LeaveCriticalSection(&Console->Lock); +} + +static BOOL +OnClose(PGUI_CONSOLE_DATA GuiData) +{ + PCONSRV_CONSOLE Console = GuiData->Console; + + if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) + return TRUE; + + // TODO: Prompt for termination ? (Warn the user about possible apps running in this console) + + /* + * FIXME: Windows will wait up to 5 seconds for the thread to exit. + * We shouldn't wait here, though, since the console lock is entered. + * A copy of the thread list probably needs to be made. + */ + ConSrvConsoleProcessCtrlEvent(Console, 0, CTRL_CLOSE_EVENT); + + LeaveCriticalSection(&Console->Lock); + return FALSE; +} + +static LRESULT +OnNcDestroy(HWND hWnd) +{ + PGUI_CONSOLE_DATA GuiData = GuiGetGuiData(hWnd); + + KillTimer(hWnd, CONGUI_UPDATE_TIMER); + GetSystemMenu(hWnd, TRUE); + + if (GuiData) + { + /* Free the terminal framebuffer */ + if (GuiData->hMemDC ) DeleteDC(GuiData->hMemDC); + if (GuiData->hBitmap) DeleteObject(GuiData->hBitmap); + // if (GuiData->hSysPalette) DeleteObject(GuiData->hSysPalette); + DeleteFonts(GuiData); + } + + /* Free the GuiData registration */ + SetWindowLongPtrW(hWnd, GWLP_USERDATA, (DWORD_PTR)NULL); + + return DefWindowProcW(hWnd, WM_NCDESTROY, 0, 0); +} + +static COORD +PointToCoord(PGUI_CONSOLE_DATA GuiData, LPARAM lParam) +{ + PCONSOLE_SCREEN_BUFFER Buffer = GuiData->ActiveBuffer; + COORD Coord; + UINT WidthUnit, HeightUnit; + + GetScreenBufferSizeUnits(Buffer, GuiData, &WidthUnit, &HeightUnit); + + Coord.X = Buffer->ViewOrigin.X + ((SHORT)LOWORD(lParam) / (int)WidthUnit ); + Coord.Y = Buffer->ViewOrigin.Y + ((SHORT)HIWORD(lParam) / (int)HeightUnit); + + /* Clip coordinate to ensure it's inside buffer */ + if (Coord.X < 0) + Coord.X = 0; + else if (Coord.X >= Buffer->ScreenBufferSize.X) + Coord.X = Buffer->ScreenBufferSize.X - 1; + + if (Coord.Y < 0) + Coord.Y = 0; + else if (Coord.Y >= Buffer->ScreenBufferSize.Y) + Coord.Y = Buffer->ScreenBufferSize.Y - 1; + + return Coord; +} + +static LRESULT +OnMouse(PGUI_CONSOLE_DATA GuiData, UINT msg, WPARAM wParam, LPARAM lParam) +{ + BOOL Err = FALSE; + PCONSRV_CONSOLE Console = GuiData->Console; + + // FIXME: It's here that we need to check whether we has focus or not + // and whether we are in edit mode or not, to know if we need to deal + // with the mouse, or not. + + if (GuiData->IgnoreNextMouseSignal) + { + if (msg != WM_LBUTTONDOWN && + msg != WM_MBUTTONDOWN && + msg != WM_RBUTTONDOWN) + { + /* + * If this mouse signal is not a button-down action + * then it is the last signal being ignored. + */ + GuiData->IgnoreNextMouseSignal = FALSE; + } + else + { + /* + * This mouse signal is a button-down action. + * Ignore it and perform default action. + */ + Err = TRUE; + } + goto Quit; + } + + if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) + { + Err = TRUE; + goto Quit; + } + + if ( (GuiData->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) || + (Console->QuickEdit) ) + { + switch (msg) + { + case WM_LBUTTONDOWN: + { + /* Clear the old selection */ + GuiData->Selection.dwFlags = CONSOLE_NO_SELECTION; + + /* Restart a new selection */ + GuiData->dwSelectionCursor = PointToCoord(GuiData, lParam); + SetCapture(GuiData->hWindow); + GuiData->Selection.dwFlags |= CONSOLE_MOUSE_SELECTION | CONSOLE_MOUSE_DOWN; + UpdateSelection(GuiData, + &GuiData->dwSelectionCursor, + &GuiData->dwSelectionCursor); + + break; + } + + case WM_LBUTTONUP: + { + if (!(GuiData->Selection.dwFlags & CONSOLE_MOUSE_DOWN)) break; + + // GuiData->dwSelectionCursor = PointToCoord(GuiData, lParam); + GuiData->Selection.dwFlags &= ~CONSOLE_MOUSE_DOWN; + // UpdateSelection(GuiData, NULL, &GuiData->dwSelectionCursor); + ReleaseCapture(); + + break; + } + + case WM_LBUTTONDBLCLK: + { + PCONSOLE_SCREEN_BUFFER Buffer = GuiData->ActiveBuffer; + + if (GetType(Buffer) == TEXTMODE_BUFFER) + { +#define IS_WORD_SEP(c) \ + ((c) == L'\0' || (c) == L' ' || (c) == L'\t' || (c) == L'\r' || (c) == L'\n') + + PTEXTMODE_SCREEN_BUFFER TextBuffer = (PTEXTMODE_SCREEN_BUFFER)Buffer; + COORD cL, cR; + PCHAR_INFO ptrL, ptrR; + + /* Starting point */ + cL = cR = PointToCoord(GuiData, lParam); + ptrL = ptrR = ConioCoordToPointer(TextBuffer, cL.X, cL.Y); + + /* Enlarge the selection by checking for whitespace */ + while ((0 < cL.X) && !IS_WORD_SEP(ptrL->Char.UnicodeChar) + && !IS_WORD_SEP((ptrL-1)->Char.UnicodeChar)) + { + --cL.X; + --ptrL; + } + while ((cR.X < TextBuffer->ScreenBufferSize.X - 1) && + !IS_WORD_SEP(ptrR->Char.UnicodeChar) && + !IS_WORD_SEP((ptrR+1)->Char.UnicodeChar)) + { + ++cR.X; + ++ptrR; + } + + /* + * Update the selection started with the single + * left-click that preceded this double-click. + */ + GuiData->Selection.dwFlags |= CONSOLE_MOUSE_SELECTION | CONSOLE_MOUSE_DOWN; + UpdateSelection(GuiData, &cL, &cR); + + /* Ignore the next mouse move signal */ + GuiData->IgnoreNextMouseSignal = TRUE; + } + + break; + } + + case WM_RBUTTONDOWN: + case WM_RBUTTONDBLCLK: + { + if (!(GuiData->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY)) + { + Paste(GuiData); + } + else + { + Copy(GuiData); + } + + /* Ignore the next mouse move signal */ + GuiData->IgnoreNextMouseSignal = TRUE; + break; + } + + case WM_MOUSEMOVE: + { + if (!(wParam & MK_LBUTTON)) break; + if (!(GuiData->Selection.dwFlags & CONSOLE_MOUSE_DOWN)) break; + + // TODO: Scroll buffer to bring SelectionCursor into view + GuiData->dwSelectionCursor = PointToCoord(GuiData, lParam); + UpdateSelection(GuiData, NULL, &GuiData->dwSelectionCursor); + + break; + } + + default: + Err = FALSE; // TRUE; + break; + } + } + else if (Console->InputBuffer.Mode & ENABLE_MOUSE_INPUT) + { + INPUT_RECORD er; + WORD wKeyState = GET_KEYSTATE_WPARAM(wParam); + DWORD dwButtonState = 0; + DWORD dwControlKeyState = 0; + DWORD dwEventFlags = 0; + + switch (msg) + { + case WM_LBUTTONDOWN: + SetCapture(GuiData->hWindow); + dwButtonState = FROM_LEFT_1ST_BUTTON_PRESSED; + dwEventFlags = 0; + break; + + case WM_MBUTTONDOWN: + SetCapture(GuiData->hWindow); + dwButtonState = FROM_LEFT_2ND_BUTTON_PRESSED; + dwEventFlags = 0; + break; + + case WM_RBUTTONDOWN: + SetCapture(GuiData->hWindow); + dwButtonState = RIGHTMOST_BUTTON_PRESSED; + dwEventFlags = 0; + break; + + case WM_LBUTTONUP: + ReleaseCapture(); + dwButtonState = 0; + dwEventFlags = 0; + break; + + case WM_MBUTTONUP: + ReleaseCapture(); + dwButtonState = 0; + dwEventFlags = 0; + break; + + case WM_RBUTTONUP: + ReleaseCapture(); + dwButtonState = 0; + dwEventFlags = 0; + break; + + case WM_LBUTTONDBLCLK: + dwButtonState = FROM_LEFT_1ST_BUTTON_PRESSED; + dwEventFlags = DOUBLE_CLICK; + break; + + case WM_MBUTTONDBLCLK: + dwButtonState = FROM_LEFT_2ND_BUTTON_PRESSED; + dwEventFlags = DOUBLE_CLICK; + break; + + case WM_RBUTTONDBLCLK: + dwButtonState = RIGHTMOST_BUTTON_PRESSED; + dwEventFlags = DOUBLE_CLICK; + break; + + case WM_MOUSEMOVE: + dwButtonState = 0; + dwEventFlags = MOUSE_MOVED; + break; + + case WM_MOUSEWHEEL: + dwButtonState = GET_WHEEL_DELTA_WPARAM(wParam) << 16; + dwEventFlags = MOUSE_WHEELED; + break; + + case WM_MOUSEHWHEEL: + dwButtonState = GET_WHEEL_DELTA_WPARAM(wParam) << 16; + dwEventFlags = MOUSE_HWHEELED; + break; + + default: + Err = TRUE; + break; + } + + /* + * HACK FOR CORE-8394: Ignore the next mouse move signal + * just after mouse down click actions. + */ + switch (msg) + { + case WM_LBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_RBUTTONDOWN: + GuiData->IgnoreNextMouseSignal = TRUE; + default: + break; + } + + if (!Err) + { + if (wKeyState & MK_LBUTTON) + dwButtonState |= FROM_LEFT_1ST_BUTTON_PRESSED; + if (wKeyState & MK_MBUTTON) + dwButtonState |= FROM_LEFT_2ND_BUTTON_PRESSED; + if (wKeyState & MK_RBUTTON) + dwButtonState |= RIGHTMOST_BUTTON_PRESSED; + + if (GetKeyState(VK_RMENU) & 0x8000) + dwControlKeyState |= RIGHT_ALT_PRESSED; + if (GetKeyState(VK_LMENU) & 0x8000) + dwControlKeyState |= LEFT_ALT_PRESSED; + if (GetKeyState(VK_RCONTROL) & 0x8000) + dwControlKeyState |= RIGHT_CTRL_PRESSED; + if (GetKeyState(VK_LCONTROL) & 0x8000) + dwControlKeyState |= LEFT_CTRL_PRESSED; + if (GetKeyState(VK_SHIFT) & 0x8000) + dwControlKeyState |= SHIFT_PRESSED; + if (GetKeyState(VK_NUMLOCK) & 0x0001) + dwControlKeyState |= NUMLOCK_ON; + if (GetKeyState(VK_SCROLL) & 0x0001) + dwControlKeyState |= SCROLLLOCK_ON; + if (GetKeyState(VK_CAPITAL) & 0x0001) + dwControlKeyState |= CAPSLOCK_ON; + /* See WM_CHAR MSDN documentation for instance */ + if (lParam & 0x01000000) + dwControlKeyState |= ENHANCED_KEY; + + er.EventType = MOUSE_EVENT; + er.Event.MouseEvent.dwMousePosition = PointToCoord(GuiData, lParam); + er.Event.MouseEvent.dwButtonState = dwButtonState; + er.Event.MouseEvent.dwControlKeyState = dwControlKeyState; + er.Event.MouseEvent.dwEventFlags = dwEventFlags; + + ConioProcessInputEvent(Console, &er); + } + } + else + { + Err = TRUE; + } + + LeaveCriticalSection(&Console->Lock); + +Quit: + if (Err) + return DefWindowProcW(GuiData->hWindow, msg, wParam, lParam); + else + return 0; +} + +VOID +GuiCopyFromTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer, + PGUI_CONSOLE_DATA GuiData); +VOID +GuiCopyFromGraphicsBuffer(PGRAPHICS_SCREEN_BUFFER Buffer, + PGUI_CONSOLE_DATA GuiData); + +static VOID +Copy(PGUI_CONSOLE_DATA GuiData) +{ - if (OpenClipboard(GuiData->hWindow) == TRUE) ++ if (OpenClipboard(GuiData->hWindow)) + { + PCONSOLE_SCREEN_BUFFER Buffer = GuiData->ActiveBuffer; + + if (GetType(Buffer) == TEXTMODE_BUFFER) + { + GuiCopyFromTextModeBuffer((PTEXTMODE_SCREEN_BUFFER)Buffer, GuiData); + } + else /* if (GetType(Buffer) == GRAPHICS_BUFFER) */ + { + GuiCopyFromGraphicsBuffer((PGRAPHICS_SCREEN_BUFFER)Buffer, GuiData); + } + + CloseClipboard(); + } + + /* Clear the selection */ + UpdateSelection(GuiData, NULL, NULL); +} + +VOID +GuiPasteToTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer, + PGUI_CONSOLE_DATA GuiData); +VOID +GuiPasteToGraphicsBuffer(PGRAPHICS_SCREEN_BUFFER Buffer, + PGUI_CONSOLE_DATA GuiData); + +static VOID +Paste(PGUI_CONSOLE_DATA GuiData) +{ - if (OpenClipboard(GuiData->hWindow) == TRUE) ++ if (OpenClipboard(GuiData->hWindow)) + { + PCONSOLE_SCREEN_BUFFER Buffer = GuiData->ActiveBuffer; + + if (GetType(Buffer) == TEXTMODE_BUFFER) + { + GuiPasteToTextModeBuffer((PTEXTMODE_SCREEN_BUFFER)Buffer, GuiData); + } + else /* if (GetType(Buffer) == GRAPHICS_BUFFER) */ + { + GuiPasteToGraphicsBuffer((PGRAPHICS_SCREEN_BUFFER)Buffer, GuiData); + } + + CloseClipboard(); + } +} + +static VOID +OnGetMinMaxInfo(PGUI_CONSOLE_DATA GuiData, PMINMAXINFO minMaxInfo) +{ + PCONSRV_CONSOLE Console = GuiData->Console; + PCONSOLE_SCREEN_BUFFER ActiveBuffer; + DWORD windx, windy; + UINT WidthUnit, HeightUnit; + + if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return; + + ActiveBuffer = GuiData->ActiveBuffer; + + GetScreenBufferSizeUnits(ActiveBuffer, GuiData, &WidthUnit, &HeightUnit); + + windx = CONGUI_MIN_WIDTH * WidthUnit + 2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE)); + windy = CONGUI_MIN_HEIGHT * HeightUnit + 2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION); + + minMaxInfo->ptMinTrackSize.x = windx; + minMaxInfo->ptMinTrackSize.y = windy; + + windx = (ActiveBuffer->ScreenBufferSize.X) * WidthUnit + 2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE)); + windy = (ActiveBuffer->ScreenBufferSize.Y) * HeightUnit + 2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION); + + if (ActiveBuffer->ViewSize.X < ActiveBuffer->ScreenBufferSize.X) windy += GetSystemMetrics(SM_CYHSCROLL); // window currently has a horizontal scrollbar + if (ActiveBuffer->ViewSize.Y < ActiveBuffer->ScreenBufferSize.Y) windx += GetSystemMetrics(SM_CXVSCROLL); // window currently has a vertical scrollbar + + minMaxInfo->ptMaxTrackSize.x = windx; + minMaxInfo->ptMaxTrackSize.y = windy; + + LeaveCriticalSection(&Console->Lock); +} + +static VOID +OnSize(PGUI_CONSOLE_DATA GuiData, WPARAM wParam, LPARAM lParam) +{ + PCONSRV_CONSOLE Console = GuiData->Console; + + if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return; + + if ((GuiData->WindowSizeLock == FALSE) && + (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED || wParam == SIZE_MINIMIZED)) + { + PCONSOLE_SCREEN_BUFFER Buff = GuiData->ActiveBuffer; + DWORD windx, windy, charx, chary; + UINT WidthUnit, HeightUnit; + + GetScreenBufferSizeUnits(Buff, GuiData, &WidthUnit, &HeightUnit); + + GuiData->WindowSizeLock = TRUE; + + windx = LOWORD(lParam); + windy = HIWORD(lParam); + + // Compensate for existing scroll bars (because lParam values do not accommodate scroll bar) + if (Buff->ViewSize.X < Buff->ScreenBufferSize.X) windy += GetSystemMetrics(SM_CYHSCROLL); // window currently has a horizontal scrollbar + if (Buff->ViewSize.Y < Buff->ScreenBufferSize.Y) windx += GetSystemMetrics(SM_CXVSCROLL); // window currently has a vertical scrollbar + + charx = windx / (int)WidthUnit ; + chary = windy / (int)HeightUnit; + + // Character alignment (round size up or down) + if ((windx % WidthUnit ) >= (WidthUnit / 2)) ++charx; + if ((windy % HeightUnit) >= (HeightUnit / 2)) ++chary; + + // Compensate for added scroll bars in new window + if (charx < Buff->ScreenBufferSize.X) windy -= GetSystemMetrics(SM_CYHSCROLL); // new window will have a horizontal scroll bar + if (chary < Buff->ScreenBufferSize.Y) windx -= GetSystemMetrics(SM_CXVSCROLL); // new window will have a vertical scroll bar + + charx = windx / (int)WidthUnit ; + chary = windy / (int)HeightUnit; + + // Character alignment (round size up or down) + if ((windx % WidthUnit ) >= (WidthUnit / 2)) ++charx; + if ((windy % HeightUnit) >= (HeightUnit / 2)) ++chary; + + // Resize window + if ((charx != Buff->ViewSize.X) || (chary != Buff->ViewSize.Y)) + { + Buff->ViewSize.X = (charx <= Buff->ScreenBufferSize.X) ? charx : Buff->ScreenBufferSize.X; + Buff->ViewSize.Y = (chary <= Buff->ScreenBufferSize.Y) ? chary : Buff->ScreenBufferSize.Y; + } + + ResizeConWnd(GuiData, WidthUnit, HeightUnit); + + // Adjust the start of the visible area if we are attempting to show nonexistent areas + if ((Buff->ScreenBufferSize.X - Buff->ViewOrigin.X) < Buff->ViewSize.X) Buff->ViewOrigin.X = Buff->ScreenBufferSize.X - Buff->ViewSize.X; + if ((Buff->ScreenBufferSize.Y - Buff->ViewOrigin.Y) < Buff->ViewSize.Y) Buff->ViewOrigin.Y = Buff->ScreenBufferSize.Y - Buff->ViewSize.Y; + InvalidateRect(GuiData->hWindow, NULL, TRUE); + + GuiData->WindowSizeLock = FALSE; + } + + LeaveCriticalSection(&Console->Lock); +} + +static VOID +OnMove(PGUI_CONSOLE_DATA GuiData) +{ + RECT rcWnd; + + // TODO: Simplify the code. + // See: GuiConsoleNotifyWndProc() PM_CREATE_CONSOLE. + + /* Retrieve our real position */ + GetWindowRect(GuiData->hWindow, &rcWnd); + GuiData->GuiInfo.WindowOrigin.x = rcWnd.left; + GuiData->GuiInfo.WindowOrigin.y = rcWnd.top; +} + +/* +// HACK: This functionality is standard for general scrollbars. Don't add it by hand. + +VOID +GuiConsoleHandleScrollbarMenu(VOID) +{ + HMENU hMenu; + + hMenu = CreatePopupMenu(); + if (hMenu == NULL) + { + DPRINT("CreatePopupMenu failed\n"); + return; + } + + //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLHERE); + //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1); + //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLTOP); + //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLBOTTOM); + //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1); + //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLPAGE_UP); + //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLPAGE_DOWN); + //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1); + //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLUP); + //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLDOWN); +} +*/ + +static LRESULT +OnScroll(PGUI_CONSOLE_DATA GuiData, UINT uMsg, WPARAM wParam) +{ + PCONSRV_CONSOLE Console = GuiData->Console; + PCONSOLE_SCREEN_BUFFER Buff; + SCROLLINFO sInfo; + int fnBar; + int old_pos, Maximum; + PSHORT pShowXY; + + if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return 0; + + Buff = GuiData->ActiveBuffer; + + if (uMsg == WM_HSCROLL) + { + fnBar = SB_HORZ; + Maximum = Buff->ScreenBufferSize.X - Buff->ViewSize.X; + pShowXY = &Buff->ViewOrigin.X; + } + else + { + fnBar = SB_VERT; + Maximum = Buff->ScreenBufferSize.Y - Buff->ViewSize.Y; + pShowXY = &Buff->ViewOrigin.Y; + } + + /* set scrollbar sizes */ + sInfo.cbSize = sizeof(SCROLLINFO); + sInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE | SIF_TRACKPOS; + + if (!GetScrollInfo(GuiData->hWindow, fnBar, &sInfo)) goto Quit; + + old_pos = sInfo.nPos; + + switch (LOWORD(wParam)) + { + case SB_LINELEFT: + sInfo.nPos -= 1; + break; + + case SB_LINERIGHT: + sInfo.nPos += 1; + break; + + case SB_PAGELEFT: + sInfo.nPos -= sInfo.nPage; + break; + + case SB_PAGERIGHT: + sInfo.nPos += sInfo.nPage; + break; + + case SB_THUMBTRACK: + sInfo.nPos = sInfo.nTrackPos; + ConioPause(Console, PAUSED_FROM_SCROLLBAR); + break; + + case SB_THUMBPOSITION: + ConioUnpause(Console, PAUSED_FROM_SCROLLBAR); + break; + + case SB_TOP: + sInfo.nPos = sInfo.nMin; + break; + + case SB_BOTTOM: + sInfo.nPos = sInfo.nMax; + break; + + default: + break; + } + + sInfo.nPos = max(sInfo.nPos, 0); + sInfo.nPos = min(sInfo.nPos, Maximum); + + if (old_pos != sInfo.nPos) + { + USHORT OldX = Buff->ViewOrigin.X; + USHORT OldY = Buff->ViewOrigin.Y; + UINT WidthUnit, HeightUnit; + + *pShowXY = sInfo.nPos; + + GetScreenBufferSizeUnits(Buff, GuiData, &WidthUnit, &HeightUnit); + + ScrollWindowEx(GuiData->hWindow, + (OldX - Buff->ViewOrigin.X) * WidthUnit , + (OldY - Buff->ViewOrigin.Y) * HeightUnit, + NULL, + NULL, + NULL, + NULL, + SW_INVALIDATE); + + sInfo.fMask = SIF_POS; + SetScrollInfo(GuiData->hWindow, fnBar, &sInfo, TRUE); + + UpdateWindow(GuiData->hWindow); + // InvalidateRect(GuiData->hWindow, NULL, FALSE); + } + +Quit: + LeaveCriticalSection(&Console->Lock); + return 0; +} + + +static LRESULT CALLBACK +ConWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + LRESULT Result = 0; + PGUI_CONSOLE_DATA GuiData = NULL; + PCONSRV_CONSOLE Console = NULL; + + /* + * - If it's the first time we create a window for the terminal, + * just initialize it and return. + * + * - If we are destroying the window, just do it and return. + */ + if (msg == WM_NCCREATE) + { + return (LRESULT)OnNcCreate(hWnd, (LPCREATESTRUCTW)lParam); + } + else if (msg == WM_NCDESTROY) + { + return OnNcDestroy(hWnd); + } + + /* + * Now the terminal window is initialized. + * Get the terminal data via the window's data. + * If there is no data, just go away. + */ + GuiData = GuiGetGuiData(hWnd); + if (GuiData == NULL) return DefWindowProcW(hWnd, msg, wParam, lParam); + + // TEMPORARY HACK until all of the functions can deal with a NULL GuiData->ActiveBuffer ... + if (GuiData->ActiveBuffer == NULL) return DefWindowProcW(hWnd, msg, wParam, lParam); + + /* + * Just retrieve a pointer to the console in case somebody needs it. + * It is not NULL because it was checked in GuiGetGuiData. + * Each helper function which needs the console has to validate and lock it. + */ + Console = GuiData->Console; + + /* We have a console, start message dispatching */ + switch (msg) + { + case WM_ACTIVATE: + OnActivate(GuiData, wParam); + break; + + case WM_CLOSE: + if (OnClose(GuiData)) goto Default; + break; + + case WM_PAINT: + OnPaint(GuiData); + break; + + case WM_TIMER: + OnTimer(GuiData); + break; + + case WM_PALETTECHANGED: + { + DPRINT("WM_PALETTECHANGED called\n"); + + /* + * Protects against infinite loops: + * "... A window that receives this message must not realize + * its palette, unless it determines that wParam does not contain + * its own window handle." (WM_PALETTECHANGED description - MSDN) + * + * This message is sent to all windows, including the one that + * changed the system palette and caused this message to be sent. + * The wParam of this message contains the handle of the window + * that caused the system palette to change. To avoid an infinite + * loop, care must be taken to check that the wParam of this message + * does not match the window's handle. + */ + if ((HWND)wParam == hWnd) break; + + DPRINT("WM_PALETTECHANGED ok\n"); + OnPaletteChanged(GuiData); + DPRINT("WM_PALETTECHANGED quit\n"); + break; + } + + case WM_KEYDOWN: + case WM_KEYUP: + case WM_CHAR: + case WM_DEADCHAR: + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + case WM_SYSCHAR: + case WM_SYSDEADCHAR: + { + /* Detect Alt-Enter presses and switch back and forth to fullscreen mode */ + if (msg == WM_SYSKEYDOWN && (HIWORD(lParam) & KF_ALTDOWN) && wParam == VK_RETURN) + { + /* Switch only at first Alt-Enter press, and ignore subsequent key repetitions */ + if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) != KF_REPEAT) + GuiConsoleSwitchFullScreen(GuiData); + + break; + } + /* Detect Alt-Esc/Space/Tab presses defer to DefWindowProc */ + if ( (HIWORD(lParam) & KF_ALTDOWN) && (wParam == VK_ESCAPE || wParam == VK_SPACE || wParam == VK_TAB)) + { + return DefWindowProcW(hWnd, msg, wParam, lParam); + } + + OnKey(GuiData, msg, wParam, lParam); + break; + } + + case WM_SETCURSOR: + { + /* + * The message was sent because we are manually triggering a change. + * Check whether the mouse is indeed present on this console window + * and take appropriate decisions. + */ + if (wParam == -1 && lParam == -1) + { + POINT mouseCoords; + HWND hWndHit; + + /* Get the placement of the mouse */ + GetCursorPos(&mouseCoords); + + /* On which window is placed the mouse ? */ + hWndHit = WindowFromPoint(mouseCoords); + + /* It's our window. Perform the hit-test to be used later on. */ + if (hWndHit == hWnd) + { + wParam = (WPARAM)hWnd; + lParam = DefWindowProcW(hWndHit, WM_NCHITTEST, 0, + MAKELPARAM(mouseCoords.x, mouseCoords.y)); + } + } + + /* Set the mouse cursor only when we are in the client area */ + if ((HWND)wParam == hWnd && LOWORD(lParam) == HTCLIENT) + { + if (GuiData->MouseCursorRefCount >= 0) + { + /* Show the cursor */ + SetCursor(GuiData->hCursor); + } + else + { + /* Hide the cursor if the reference count is negative */ + SetCursor(NULL); + } + return TRUE; + } + else + { + goto Default; + } + } + + case WM_LBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_RBUTTONDOWN: + case WM_LBUTTONUP: + case WM_MBUTTONUP: + case WM_RBUTTONUP: + case WM_LBUTTONDBLCLK: + case WM_MBUTTONDBLCLK: + case WM_RBUTTONDBLCLK: + case WM_MOUSEMOVE: + case WM_MOUSEWHEEL: + case WM_MOUSEHWHEEL: + { + Result = OnMouse(GuiData, msg, wParam, lParam); + break; + } + + case WM_HSCROLL: + case WM_VSCROLL: + { + Result = OnScroll(GuiData, msg, wParam); + break; + } + + case WM_CONTEXTMENU: + { + if (DefWindowProcW(hWnd /*GuiData->hWindow*/, WM_NCHITTEST, 0, lParam) == HTCLIENT) + { + HMENU hMenu = CreatePopupMenu(); + if (hMenu != NULL) + { + AppendMenuItems(hMenu, GuiConsoleEditMenuItems); + TrackPopupMenuEx(hMenu, + TPM_RIGHTBUTTON, + GET_X_LPARAM(lParam), + GET_Y_LPARAM(lParam), + hWnd, + NULL); + DestroyMenu(hMenu); + } + break; + } + else + { + goto Default; + } + } + + case WM_INITMENU: + { + HMENU hMenu = (HMENU)wParam; + if (hMenu != NULL) + { + /* Enable or disable the Close menu item */ + EnableMenuItem(hMenu, SC_CLOSE, MF_BYCOMMAND | + (GuiData->IsCloseButtonEnabled ? MF_ENABLED : MF_GRAYED)); + + /* Enable or disable the Copy and Paste items */ + EnableMenuItem(hMenu, ID_SYSTEM_EDIT_COPY , MF_BYCOMMAND | + ((GuiData->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) && + (GuiData->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY) ? MF_ENABLED : MF_GRAYED)); + // FIXME: Following whether the active screen buffer is text-mode + // or graphics-mode, search for CF_UNICODETEXT or CF_BITMAP formats. + EnableMenuItem(hMenu, ID_SYSTEM_EDIT_PASTE, MF_BYCOMMAND | + (!(GuiData->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) && + IsClipboardFormatAvailable(CF_UNICODETEXT) ? MF_ENABLED : MF_GRAYED)); + } + + SendMenuEvent(Console, WM_INITMENU); + break; + } + + case WM_MENUSELECT: + { + if (HIWORD(wParam) == 0xFFFF) // Allow all the menu flags + { + SendMenuEvent(Console, WM_MENUSELECT); + } + break; + } + + case WM_COMMAND: + case WM_SYSCOMMAND: + { + Result = OnCommand(GuiData, wParam, lParam); + break; + } + + case WM_SETFOCUS: + case WM_KILLFOCUS: + OnFocus(GuiData, (msg == WM_SETFOCUS)); + break; + + case WM_GETMINMAXINFO: + OnGetMinMaxInfo(GuiData, (PMINMAXINFO)lParam); + break; + + case WM_MOVE: + OnMove(GuiData); + break; + +#if 0 // This code is here to prepare & control dynamic console SB resizing. + case WM_SIZING: + { + PRECT dragRect = (PRECT)lParam; + switch (wParam) + { + case WMSZ_LEFT: + DPRINT1("WMSZ_LEFT\n"); + break; + case WMSZ_RIGHT: + DPRINT1("WMSZ_RIGHT\n"); + break; + case WMSZ_TOP: + DPRINT1("WMSZ_TOP\n"); + break; + case WMSZ_TOPLEFT: + DPRINT1("WMSZ_TOPLEFT\n"); + break; + case WMSZ_TOPRIGHT: + DPRINT1("WMSZ_TOPRIGHT\n"); + break; + case WMSZ_BOTTOM: + DPRINT1("WMSZ_BOTTOM\n"); + break; + case WMSZ_BOTTOMLEFT: + DPRINT1("WMSZ_BOTTOMLEFT\n"); + break; + case WMSZ_BOTTOMRIGHT: + DPRINT1("WMSZ_BOTTOMRIGHT\n"); + break; + default: + DPRINT1("wParam = %d\n", wParam); + break; + } + DPRINT1("dragRect = {.left = %d ; .top = %d ; .right = %d ; .bottom = %d}\n", + dragRect->left, dragRect->top, dragRect->right, dragRect->bottom); + break; + } +#endif + + case WM_SIZE: + OnSize(GuiData, wParam, lParam); + break; + + case PM_RESIZE_TERMINAL: + { + PCONSOLE_SCREEN_BUFFER Buff = GuiData->ActiveBuffer; + HDC hDC; + HBITMAP hnew, hold; + + DWORD Width, Height; + UINT WidthUnit, HeightUnit; + + GetScreenBufferSizeUnits(Buff, GuiData, &WidthUnit, &HeightUnit); + + Width = Buff->ScreenBufferSize.X * WidthUnit ; + Height = Buff->ScreenBufferSize.Y * HeightUnit; + + /* Recreate the framebuffer */ + hDC = GetDC(GuiData->hWindow); + hnew = CreateCompatibleBitmap(hDC, Width, Height); + ReleaseDC(GuiData->hWindow, hDC); + hold = SelectObject(GuiData->hMemDC, hnew); + if (GuiData->hBitmap) + { + if (hold == GuiData->hBitmap) DeleteObject(GuiData->hBitmap); + } + GuiData->hBitmap = hnew; + + /* Resize the window to the user's values */ + GuiData->WindowSizeLock = TRUE; + ResizeConWnd(GuiData, WidthUnit, HeightUnit); + GuiData->WindowSizeLock = FALSE; + break; + } + + case PM_APPLY_CONSOLE_INFO: + { + GuiApplyUserSettings(GuiData, (HANDLE)wParam, (BOOL)lParam); + break; + } + + /* + * Undocumented message sent by Windows' console.dll for applying console info. + * See http://www.catch22.net/sites/default/source/files/setconsoleinfo.c + * and http://www.scn.rain.com/~neighorn/PDF/MSBugPaper.pdf + * for more information. + */ + case WM_SETCONSOLEINFO: + { + DPRINT1("WM_SETCONSOLEINFO message\n"); + GuiApplyWindowsConsoleSettings(GuiData, (HANDLE)wParam); + break; + } + + case PM_CONSOLE_BEEP: + DPRINT1("Beep\n"); + Beep(800, 200); + break; + + // case PM_CONSOLE_SET_TITLE: + // SetWindowText(GuiData->hWindow, GuiData->Console->Title.Buffer); + // break; + + default: Default: + Result = DefWindowProcW(hWnd, msg, wParam, lParam); + break; + } + + return Result; +} + +/* EOF */ diff --cc win32ss/user/winsrv/consrv_new/frontends/gui/guiterm.c index a974e9b47b0,00000000000..e4d3d79dac9 mode 100644,000000..100644 --- a/win32ss/user/winsrv/consrv_new/frontends/gui/guiterm.c +++ b/win32ss/user/winsrv/consrv_new/frontends/gui/guiterm.c @@@ -1,2960 -1,0 +1,2960 @@@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Console Server DLL + * FILE: win32ss/user/winsrv/consrv/frontends/gui/guiterm.c + * PURPOSE: GUI Terminal Front-End + * PROGRAMMERS: Gé van Geldorp + * Johannes Anderwald + * Jeffrey Morlan + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +/* INCLUDES *******************************************************************/ + +#define COBJMACROS +#define NONAMELESSUNION + +#include "consrv.h" +#include "include/conio.h" +#include "include/console.h" +#include "include/settings.h" +#include "conoutput.h" +#include "guiterm.h" +#include "guisettings.h" +#include "resource.h" + +#include + +#include +#include + +#define NDEBUG +#include + +/* GUI Console Window Class name */ +#define GUI_CONSOLE_WINDOW_CLASS L"ConsoleWindowClass" + +#ifndef WM_APP + #define WM_APP 0x8000 +#endif +#define PM_CREATE_CONSOLE (WM_APP + 1) +#define PM_DESTROY_CONSOLE (WM_APP + 2) +#define PM_RESIZE_TERMINAL (WM_APP + 3) +#define PM_CONSOLE_BEEP (WM_APP + 4) +#define PM_CONSOLE_SET_TITLE (WM_APP + 5) + + +/* Not defined in any header file */ +extern VOID WINAPI PrivateCsrssManualGuiCheck(LONG Check); +// See winsrv/usersrv/init.c line 234 + + +/* GLOBALS ********************************************************************/ + +typedef struct _GUI_INIT_INFO +{ + PCONSOLE_INFO ConsoleInfo; + PCONSOLE_START_INFO ConsoleStartInfo; + ULONG ProcessId; +} GUI_INIT_INFO, *PGUI_INIT_INFO; + +/**************************************************************\ +\** Define the Console Leader Process for the console window **/ +#define GWLP_CONSOLEWND_ALLOC (2 * sizeof(LONG_PTR)) +#define GWLP_CONSOLE_LEADER_PID 0 +#define GWLP_CONSOLE_LEADER_TID 4 + +#define SetConsoleWndConsoleLeaderCID(GuiData) \ +do { \ + PCONSOLE_PROCESS_DATA ProcessData; \ + CLIENT_ID ConsoleLeaderCID; \ + ProcessData = CONTAINING_RECORD((GuiData)->Console->ProcessList.Blink, \ + CONSOLE_PROCESS_DATA, \ + ConsoleLink); \ + ConsoleLeaderCID = ProcessData->Process->ClientId; \ + SetWindowLongPtrW((GuiData)->hWindow, GWLP_CONSOLE_LEADER_PID, (LONG_PTR)(ConsoleLeaderCID.UniqueProcess)); \ + SetWindowLongPtrW((GuiData)->hWindow, GWLP_CONSOLE_LEADER_TID, (LONG_PTR)(ConsoleLeaderCID.UniqueThread )); \ +} while (0) +/**************************************************************/ + +static BOOL ConsInitialized = FALSE; +static HICON ghDefaultIcon = NULL; +static HICON ghDefaultIconSm = NULL; +static HCURSOR ghDefaultCursor = NULL; +static HWND NotifyWnd = NULL; + +typedef struct _GUICONSOLE_MENUITEM +{ + UINT uID; + const struct _GUICONSOLE_MENUITEM *SubMenu; + WORD wCmdID; +} GUICONSOLE_MENUITEM, *PGUICONSOLE_MENUITEM; + +static const GUICONSOLE_MENUITEM GuiConsoleEditMenuItems[] = +{ + { IDS_MARK, NULL, ID_SYSTEM_EDIT_MARK }, + { IDS_COPY, NULL, ID_SYSTEM_EDIT_COPY }, + { IDS_PASTE, NULL, ID_SYSTEM_EDIT_PASTE }, + { IDS_SELECTALL, NULL, ID_SYSTEM_EDIT_SELECTALL }, + { IDS_SCROLL, NULL, ID_SYSTEM_EDIT_SCROLL }, + { IDS_FIND, NULL, ID_SYSTEM_EDIT_FIND }, + + { 0, NULL, 0 } /* End of list */ +}; + +static const GUICONSOLE_MENUITEM GuiConsoleMainMenuItems[] = +{ + { IDS_EDIT, GuiConsoleEditMenuItems, 0 }, + { IDS_DEFAULTS, NULL, ID_SYSTEM_DEFAULTS }, + { IDS_PROPERTIES, NULL, ID_SYSTEM_PROPERTIES }, + + { 0, NULL, 0 } /* End of list */ +}; + +/* + * Default 16-color palette for foreground and background + * (corresponding flags in comments). + */ +const COLORREF s_Colors[16] = +{ + RGB(0, 0, 0), // (Black) + RGB(0, 0, 128), // BLUE + RGB(0, 128, 0), // GREEN + RGB(0, 128, 128), // BLUE | GREEN + RGB(128, 0, 0), // RED + RGB(128, 0, 128), // BLUE | RED + RGB(128, 128, 0), // GREEN | RED + RGB(192, 192, 192), // BLUE | GREEN | RED + + RGB(128, 128, 128), // (Grey) INTENSITY + RGB(0, 0, 255), // BLUE | INTENSITY + RGB(0, 255, 0), // GREEN | INTENSITY + RGB(0, 255, 255), // BLUE | GREEN | INTENSITY + RGB(255, 0, 0), // RED | INTENSITY + RGB(255, 0, 255), // BLUE | RED | INTENSITY + RGB(255, 255, 0), // GREEN | RED | INTENSITY + RGB(255, 255, 255) // BLUE | GREEN | RED | INTENSITY +}; + +/* FUNCTIONS ******************************************************************/ + +static VOID +GetScreenBufferSizeUnits(IN PCONSOLE_SCREEN_BUFFER Buffer, + IN PGUI_CONSOLE_DATA GuiData, + OUT PUINT WidthUnit, + OUT PUINT HeightUnit) +{ + if (Buffer == NULL || GuiData == NULL || + WidthUnit == NULL || HeightUnit == NULL) + { + return; + } + + if (GetType(Buffer) == TEXTMODE_BUFFER) + { + *WidthUnit = GuiData->CharWidth ; + *HeightUnit = GuiData->CharHeight; + } + else /* if (GetType(Buffer) == GRAPHICS_BUFFER) */ + { + *WidthUnit = 1; + *HeightUnit = 1; + } +} + + + +static VOID +GuiConsoleAppendMenuItems(HMENU hMenu, + const GUICONSOLE_MENUITEM *Items) +{ + UINT i = 0; + WCHAR szMenuString[255]; + HMENU hSubMenu; + + do + { + if (Items[i].uID != (UINT)-1) + { + if (LoadStringW(ConSrvDllInstance, + Items[i].uID, + szMenuString, + sizeof(szMenuString) / sizeof(szMenuString[0])) > 0) + { + if (Items[i].SubMenu != NULL) + { + hSubMenu = CreatePopupMenu(); + if (hSubMenu != NULL) + { + GuiConsoleAppendMenuItems(hSubMenu, + Items[i].SubMenu); + + if (!AppendMenuW(hMenu, + MF_STRING | MF_POPUP, + (UINT_PTR)hSubMenu, + szMenuString)) + { + DestroyMenu(hSubMenu); + } + } + } + else + { + AppendMenuW(hMenu, + MF_STRING, + Items[i].wCmdID, + szMenuString); + } + } + } + else + { + AppendMenuW(hMenu, + MF_SEPARATOR, + 0, + NULL); + } + i++; + } while (!(Items[i].uID == 0 && Items[i].SubMenu == NULL && Items[i].wCmdID == 0)); +} + +static VOID +GuiConsoleCreateSysMenu(HWND hWnd) +{ + HMENU hMenu = GetSystemMenu(hWnd, FALSE); + if (hMenu != NULL) + { + GuiConsoleAppendMenuItems(hMenu, GuiConsoleMainMenuItems); + DrawMenuBar(hWnd); + } +} + +static VOID +GuiSendMenuEvent(PCONSOLE Console, UINT CmdId) +{ + INPUT_RECORD er; + + er.EventType = MENU_EVENT; + er.Event.MenuEvent.dwCommandId = CmdId; + + ConioProcessInputEvent(Console, &er); +} + +static VOID +GuiConsoleCopy(PGUI_CONSOLE_DATA GuiData); +static VOID +GuiConsolePaste(PGUI_CONSOLE_DATA GuiData); +static VOID +GuiConsoleUpdateSelection(PCONSOLE Console, PCOORD coord); +static VOID WINAPI +GuiDrawRegion(IN OUT PFRONTEND This, SMALL_RECT* Region); +static VOID +GuiConsoleResizeWindow(PGUI_CONSOLE_DATA GuiData); + + +static LRESULT +GuiConsoleHandleSysMenuCommand(PGUI_CONSOLE_DATA GuiData, WPARAM wParam, LPARAM lParam) +{ + LRESULT Ret = TRUE; + PCONSOLE Console = GuiData->Console; + PCONSOLE_SCREEN_BUFFER ActiveBuffer; + + if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) + { + Ret = FALSE; + goto Quit; + } + ActiveBuffer = ConDrvGetActiveScreenBuffer(Console); + + /* + * In case the selected menu item belongs to the user-reserved menu id range, + * send to him a menu event and return directly. The user must handle those + * reserved menu commands... + */ + if (GuiData->cmdIdLow <= (UINT)wParam && (UINT)wParam <= GuiData->cmdIdHigh) + { + GuiSendMenuEvent(Console, (UINT)wParam); + goto Unlock_Quit; + } + + /* ... otherwise, perform actions. */ + switch (wParam) + { + case ID_SYSTEM_EDIT_MARK: + { + LPWSTR WindowTitle = NULL; + SIZE_T Length = 0; + + Console->dwSelectionCursor.X = ActiveBuffer->ViewOrigin.X; + Console->dwSelectionCursor.Y = ActiveBuffer->ViewOrigin.Y; + Console->Selection.dwSelectionAnchor = Console->dwSelectionCursor; + Console->Selection.dwFlags |= CONSOLE_SELECTION_IN_PROGRESS; + GuiConsoleUpdateSelection(Console, &Console->Selection.dwSelectionAnchor); + + Length = Console->Title.Length + sizeof(L"Mark - ")/sizeof(WCHAR) + 1; + WindowTitle = ConsoleAllocHeap(0, Length * sizeof(WCHAR)); + wcscpy(WindowTitle, L"Mark - "); + wcscat(WindowTitle, Console->Title.Buffer); + SetWindowText(GuiData->hWindow, WindowTitle); + ConsoleFreeHeap(WindowTitle); + + break; + } + + case ID_SYSTEM_EDIT_COPY: + GuiConsoleCopy(GuiData); + break; + + case ID_SYSTEM_EDIT_PASTE: + GuiConsolePaste(GuiData); + break; + + case ID_SYSTEM_EDIT_SELECTALL: + { + LPWSTR WindowTitle = NULL; + SIZE_T Length = 0; + + Console->Selection.dwSelectionAnchor.X = 0; + Console->Selection.dwSelectionAnchor.Y = 0; + Console->dwSelectionCursor.X = ActiveBuffer->ViewSize.X - 1; + Console->dwSelectionCursor.Y = ActiveBuffer->ViewSize.Y - 1; + Console->Selection.dwFlags |= CONSOLE_SELECTION_IN_PROGRESS | CONSOLE_MOUSE_SELECTION; + GuiConsoleUpdateSelection(Console, &Console->dwSelectionCursor); + + Length = Console->Title.Length + sizeof(L"Selection - ")/sizeof(WCHAR) + 1; + WindowTitle = ConsoleAllocHeap(0, Length * sizeof(WCHAR)); + wcscpy(WindowTitle, L"Selection - "); + wcscat(WindowTitle, Console->Title.Buffer); + SetWindowText(GuiData->hWindow, WindowTitle); + ConsoleFreeHeap(WindowTitle); + + break; + } + + case ID_SYSTEM_EDIT_SCROLL: + DPRINT1("Scrolling is not handled yet\n"); + break; + + case ID_SYSTEM_EDIT_FIND: + DPRINT1("Finding is not handled yet\n"); + break; + + case ID_SYSTEM_DEFAULTS: + GuiConsoleShowConsoleProperties(GuiData, TRUE); + break; + + case ID_SYSTEM_PROPERTIES: + GuiConsoleShowConsoleProperties(GuiData, FALSE); + break; + + default: + Ret = FALSE; + break; + } + +Unlock_Quit: + LeaveCriticalSection(&Console->Lock); +Quit: + if (!Ret) + Ret = DefWindowProcW(GuiData->hWindow, WM_SYSCOMMAND, wParam, lParam); + + return Ret; +} + +static PGUI_CONSOLE_DATA +GuiGetGuiData(HWND hWnd) +{ + /* This function ensures that the console pointer is not NULL */ + PGUI_CONSOLE_DATA GuiData = (PGUI_CONSOLE_DATA)GetWindowLongPtrW(hWnd, GWLP_USERDATA); + return ( ((GuiData == NULL) || (GuiData->hWindow == hWnd && GuiData->Console != NULL)) ? GuiData : NULL ); +} + +VOID +GuiConsoleMoveWindow(PGUI_CONSOLE_DATA GuiData) +{ + /* Move the window if needed (not positioned by the system) */ + if (!GuiData->GuiInfo.AutoPosition) + { + SetWindowPos(GuiData->hWindow, + NULL, + GuiData->GuiInfo.WindowOrigin.x, + GuiData->GuiInfo.WindowOrigin.y, + 0, + 0, + SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE); + } +} + +static VOID +GuiConsoleResizeWindow(PGUI_CONSOLE_DATA GuiData) +{ + PCONSOLE Console = GuiData->Console; + PCONSOLE_SCREEN_BUFFER Buff = ConDrvGetActiveScreenBuffer(Console); + SCROLLINFO sInfo; + + DWORD Width, Height; + UINT WidthUnit, HeightUnit; + + GetScreenBufferSizeUnits(Buff, GuiData, &WidthUnit, &HeightUnit); + + Width = Buff->ViewSize.X * WidthUnit + + 2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE)); + Height = Buff->ViewSize.Y * HeightUnit + + 2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION); + + /* Set scrollbar sizes */ + sInfo.cbSize = sizeof(SCROLLINFO); + sInfo.fMask = SIF_RANGE | SIF_PAGE | SIF_POS; + sInfo.nMin = 0; + if (Buff->ScreenBufferSize.Y > Buff->ViewSize.Y) + { + sInfo.nMax = Buff->ScreenBufferSize.Y - 1; + sInfo.nPage = Buff->ViewSize.Y; + sInfo.nPos = Buff->ViewOrigin.Y; + SetScrollInfo(GuiData->hWindow, SB_VERT, &sInfo, TRUE); + Width += GetSystemMetrics(SM_CXVSCROLL); + ShowScrollBar(GuiData->hWindow, SB_VERT, TRUE); + } + else + { + ShowScrollBar(GuiData->hWindow, SB_VERT, FALSE); + } + + if (Buff->ScreenBufferSize.X > Buff->ViewSize.X) + { + sInfo.nMax = Buff->ScreenBufferSize.X - 1; + sInfo.nPage = Buff->ViewSize.X; + sInfo.nPos = Buff->ViewOrigin.X; + SetScrollInfo(GuiData->hWindow, SB_HORZ, &sInfo, TRUE); + Height += GetSystemMetrics(SM_CYHSCROLL); + ShowScrollBar(GuiData->hWindow, SB_HORZ, TRUE); + } + else + { + ShowScrollBar(GuiData->hWindow, SB_HORZ, FALSE); + } + + /* Resize the window */ + SetWindowPos(GuiData->hWindow, NULL, 0, 0, Width, Height, + SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOCOPYBITS); + // NOTE: The SWP_NOCOPYBITS flag can be replaced by a subsequent call + // to: InvalidateRect(GuiData->hWindow, NULL, TRUE); +} + +static VOID +GuiConsoleSwitchFullScreen(PGUI_CONSOLE_DATA GuiData) +{ + PCONSOLE Console = GuiData->Console; + // DEVMODE dmScreenSettings; + + if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return; + + /* Switch to full-screen or to windowed mode */ + GuiData->GuiInfo.FullScreen = !GuiData->GuiInfo.FullScreen; + DPRINT1("GuiConsoleSwitchFullScreen - Switch to %s ...\n", + (GuiData->GuiInfo.FullScreen ? "full-screen" : "windowed mode")); + + // TODO: Change window appearance. + // See: + // http://stackoverflow.com/questions/2382464/win32-full-screen-and-hiding-taskbar + // http://stackoverflow.com/questions/3549148/fullscreen-management-with-winapi + // http://blogs.msdn.com/b/oldnewthing/archive/2010/04/12/9994016.aspx + // http://stackoverflow.com/questions/1400654/how-do-i-put-my-opengl-app-into-fullscreen-mode + // http://nehe.gamedev.net/tutorial/creating_an_opengl_window_win32/13001/ +#if 0 + if (GuiData->GuiInfo.FullScreen) + { + memset(&dmScreenSettings, 0, sizeof(dmScreenSettings)); + dmScreenSettings.dmSize = sizeof(dmScreenSettings); + dmScreenSettings.dmDisplayFixedOutput = DMDFO_CENTER; // DMDFO_STRETCH // DMDFO_DEFAULT + dmScreenSettings.dmPelsWidth = 640; // Console->ActiveBuffer->ViewSize.X * GuiData->CharWidth; + dmScreenSettings.dmPelsHeight = 480; // Console->ActiveBuffer->ViewSize.Y * GuiData->CharHeight; + dmScreenSettings.dmBitsPerPel = 32; + dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; + ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN); + } + else + { + } +#endif + + LeaveCriticalSection(&Console->Lock); +} + +static BOOL +GuiConsoleHandleNcCreate(HWND hWnd, LPCREATESTRUCTW Create) +{ + PGUI_CONSOLE_DATA GuiData = (PGUI_CONSOLE_DATA)Create->lpCreateParams; + PCONSOLE Console; + HDC Dc; + HFONT OldFont; + TEXTMETRICW Metrics; + SIZE CharSize; + + DPRINT("GuiConsoleHandleNcCreate\n"); + + if (NULL == GuiData) + { + DPRINT1("GuiConsoleNcCreate: No GUI data\n"); + return FALSE; + } + + Console = GuiData->Console; + + GuiData->hWindow = hWnd; + + GuiData->Font = CreateFontW(LOWORD(GuiData->GuiInfo.FontSize), + 0, // HIWORD(GuiData->GuiInfo.FontSize), + 0, + TA_BASELINE, + GuiData->GuiInfo.FontWeight, + FALSE, + FALSE, + FALSE, + OEM_CHARSET, + OUT_DEFAULT_PRECIS, + CLIP_DEFAULT_PRECIS, + NONANTIALIASED_QUALITY, + FIXED_PITCH | GuiData->GuiInfo.FontFamily /* FF_DONTCARE */, + GuiData->GuiInfo.FaceName); + + if (NULL == GuiData->Font) + { + DPRINT1("GuiConsoleNcCreate: CreateFont failed\n"); + GuiData->hWindow = NULL; + SetEvent(GuiData->hGuiInitEvent); + return FALSE; + } + Dc = GetDC(GuiData->hWindow); + if (NULL == Dc) + { + DPRINT1("GuiConsoleNcCreate: GetDC failed\n"); + DeleteObject(GuiData->Font); + GuiData->hWindow = NULL; + SetEvent(GuiData->hGuiInitEvent); + return FALSE; + } + OldFont = SelectObject(Dc, GuiData->Font); + if (NULL == OldFont) + { + DPRINT1("GuiConsoleNcCreate: SelectObject failed\n"); + ReleaseDC(GuiData->hWindow, Dc); + DeleteObject(GuiData->Font); + GuiData->hWindow = NULL; + SetEvent(GuiData->hGuiInitEvent); + return FALSE; + } + if (!GetTextMetricsW(Dc, &Metrics)) + { + DPRINT1("GuiConsoleNcCreate: GetTextMetrics failed\n"); + SelectObject(Dc, OldFont); + ReleaseDC(GuiData->hWindow, Dc); + DeleteObject(GuiData->Font); + GuiData->hWindow = NULL; + SetEvent(GuiData->hGuiInitEvent); + return FALSE; + } + GuiData->CharWidth = Metrics.tmMaxCharWidth; + GuiData->CharHeight = Metrics.tmHeight + Metrics.tmExternalLeading; + + /* Measure real char width more precisely if possible. */ + if (GetTextExtentPoint32W(Dc, L"R", 1, &CharSize)) + GuiData->CharWidth = CharSize.cx; + + SelectObject(Dc, OldFont); + + ReleaseDC(GuiData->hWindow, Dc); + + // FIXME: Keep these instructions here ? /////////////////////////////////// + Console->ActiveBuffer->CursorBlinkOn = TRUE; + Console->ActiveBuffer->ForceCursorOff = FALSE; + //////////////////////////////////////////////////////////////////////////// + + SetWindowLongPtrW(GuiData->hWindow, GWLP_USERDATA, (DWORD_PTR)GuiData); + + SetTimer(GuiData->hWindow, CONGUI_UPDATE_TIMER, CONGUI_UPDATE_TIME, NULL); + GuiConsoleCreateSysMenu(GuiData->hWindow); + + DPRINT("GuiConsoleHandleNcCreate - setting start event\n"); + SetEvent(GuiData->hGuiInitEvent); + + return (BOOL)DefWindowProcW(GuiData->hWindow, WM_NCCREATE, 0, (LPARAM)Create); +} + +static VOID +SmallRectToRect(PGUI_CONSOLE_DATA GuiData, PRECT Rect, PSMALL_RECT SmallRect) +{ + PCONSOLE Console = GuiData->Console; + PCONSOLE_SCREEN_BUFFER Buffer = ConDrvGetActiveScreenBuffer(Console); + UINT WidthUnit, HeightUnit; + + GetScreenBufferSizeUnits(Buffer, GuiData, &WidthUnit, &HeightUnit); + + Rect->left = (SmallRect->Left - Buffer->ViewOrigin.X) * WidthUnit ; + Rect->top = (SmallRect->Top - Buffer->ViewOrigin.Y) * HeightUnit; + Rect->right = (SmallRect->Right + 1 - Buffer->ViewOrigin.X) * WidthUnit ; + Rect->bottom = (SmallRect->Bottom + 1 - Buffer->ViewOrigin.Y) * HeightUnit; +} + +static VOID +GuiConsoleUpdateSelection(PCONSOLE Console, PCOORD coord) +{ + PGUI_CONSOLE_DATA GuiData = Console->TermIFace.Data; + RECT oldRect, newRect; + + SmallRectToRect(GuiData, &oldRect, &Console->Selection.srSelection); + + if (coord != NULL) + { + SMALL_RECT rc; + /* exchange left/top with right/bottom if required */ + rc.Left = min(Console->Selection.dwSelectionAnchor.X, coord->X); + rc.Top = min(Console->Selection.dwSelectionAnchor.Y, coord->Y); + rc.Right = max(Console->Selection.dwSelectionAnchor.X, coord->X); + rc.Bottom = max(Console->Selection.dwSelectionAnchor.Y, coord->Y); + + SmallRectToRect(GuiData, &newRect, &rc); + + if (Console->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY) + { + if (memcmp(&rc, &Console->Selection.srSelection, sizeof(SMALL_RECT)) != 0) + { + HRGN rgn1, rgn2; + + /* calculate the region that needs to be updated */ + if ((rgn1 = CreateRectRgnIndirect(&oldRect))) + { + if ((rgn2 = CreateRectRgnIndirect(&newRect))) + { + if (CombineRgn(rgn1, rgn2, rgn1, RGN_XOR) != ERROR) + { + InvalidateRgn(GuiData->hWindow, rgn1, FALSE); + } + DeleteObject(rgn2); + } + DeleteObject(rgn1); + } + } + } + else + { + InvalidateRect(GuiData->hWindow, &newRect, FALSE); + } + Console->Selection.dwFlags |= CONSOLE_SELECTION_NOT_EMPTY; + Console->Selection.srSelection = rc; + Console->dwSelectionCursor = *coord; + ConioPause(Console, PAUSED_FROM_SELECTION); + } + else + { + /* clear the selection */ + if (Console->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY) + { + InvalidateRect(GuiData->hWindow, &oldRect, FALSE); + } + Console->Selection.dwFlags = CONSOLE_NO_SELECTION; + ConioUnpause(Console, PAUSED_FROM_SELECTION); + } +} + + +VOID +GuiPaintTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer, + PGUI_CONSOLE_DATA GuiData, + HDC hDC, + PRECT rc); +VOID +GuiPaintGraphicsBuffer(PGRAPHICS_SCREEN_BUFFER Buffer, + PGUI_CONSOLE_DATA GuiData, + HDC hDC, + PRECT rc); + +static VOID +GuiConsoleHandlePaint(PGUI_CONSOLE_DATA GuiData) +{ + BOOL Success = TRUE; + PCONSOLE Console = GuiData->Console; + PCONSOLE_SCREEN_BUFFER ActiveBuffer; + HDC hDC; + PAINTSTRUCT ps; + + if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) + { + Success = FALSE; + goto Quit; + } + ActiveBuffer = ConDrvGetActiveScreenBuffer(Console); + + hDC = BeginPaint(GuiData->hWindow, &ps); + if (hDC != NULL && + ps.rcPaint.left < ps.rcPaint.right && + ps.rcPaint.top < ps.rcPaint.bottom) + { + EnterCriticalSection(&GuiData->Lock); + + if (GetType(ActiveBuffer) == TEXTMODE_BUFFER) + { + GuiPaintTextModeBuffer((PTEXTMODE_SCREEN_BUFFER)ActiveBuffer, + GuiData, hDC, &ps.rcPaint); + } + else /* if (GetType(ActiveBuffer) == GRAPHICS_BUFFER) */ + { + GuiPaintGraphicsBuffer((PGRAPHICS_SCREEN_BUFFER)ActiveBuffer, + GuiData, hDC, &ps.rcPaint); + } + + if (Console->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY) + { + RECT rc; + SmallRectToRect(GuiData, &rc, &Console->Selection.srSelection); + + /* invert the selection */ + if (IntersectRect(&rc, &ps.rcPaint, &rc)) + { + PatBlt(hDC, + rc.left, + rc.top, + rc.right - rc.left, + rc.bottom - rc.top, + DSTINVERT); + } + } + + LeaveCriticalSection(&GuiData->Lock); + } + EndPaint(GuiData->hWindow, &ps); + +Quit: + if (Success) + LeaveCriticalSection(&Console->Lock); + else + DefWindowProcW(GuiData->hWindow, WM_PAINT, 0, 0); + + return; +} + +static BOOL +IsSystemKey(WORD VirtualKeyCode) +{ + switch (VirtualKeyCode) + { + /* From MSDN, "Virtual-Key Codes" */ + case VK_RETURN: + case VK_SHIFT: + case VK_CONTROL: + case VK_MENU: + case VK_PAUSE: + case VK_CAPITAL: + case VK_ESCAPE: + case VK_LWIN: + case VK_RWIN: + case VK_NUMLOCK: + case VK_SCROLL: + return TRUE; + default: + return FALSE; + } +} + +static VOID +GuiConsoleHandleKey(PGUI_CONSOLE_DATA GuiData, UINT msg, WPARAM wParam, LPARAM lParam) +{ + PCONSOLE Console = GuiData->Console; + PCONSOLE_SCREEN_BUFFER ActiveBuffer; + + if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return; + + ActiveBuffer = ConDrvGetActiveScreenBuffer(Console); + + if (Console->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) + { + WORD VirtualKeyCode = LOWORD(wParam); + + if (msg != WM_KEYDOWN) goto Quit; + + if (VirtualKeyCode == VK_RETURN) + { + /* Copy (and clear) selection if ENTER is pressed */ + GuiConsoleCopy(GuiData); + goto Quit; + } + else if ( VirtualKeyCode == VK_ESCAPE || + (VirtualKeyCode == 'C' && GetKeyState(VK_CONTROL) & 0x8000) ) + { + /* Cancel selection if ESC or Ctrl-C are pressed */ + GuiConsoleUpdateSelection(Console, NULL); + SetWindowText(GuiData->hWindow, Console->Title.Buffer); + + goto Quit; + } + + if ((Console->Selection.dwFlags & CONSOLE_MOUSE_SELECTION) == 0) + { + /* Keyboard selection mode */ + BOOL Interpreted = FALSE; + BOOL MajPressed = (GetKeyState(VK_SHIFT) & 0x8000); + + switch (VirtualKeyCode) + { + case VK_LEFT: + { + Interpreted = TRUE; + if (Console->dwSelectionCursor.X > 0) + Console->dwSelectionCursor.X--; + + break; + } + + case VK_RIGHT: + { + Interpreted = TRUE; + if (Console->dwSelectionCursor.X < ActiveBuffer->ScreenBufferSize.X - 1) + Console->dwSelectionCursor.X++; + + break; + } + + case VK_UP: + { + Interpreted = TRUE; + if (Console->dwSelectionCursor.Y > 0) + Console->dwSelectionCursor.Y--; + + break; + } + + case VK_DOWN: + { + Interpreted = TRUE; + if (Console->dwSelectionCursor.Y < ActiveBuffer->ScreenBufferSize.Y - 1) + Console->dwSelectionCursor.Y++; + + break; + } + + case VK_HOME: + { + Interpreted = TRUE; + Console->dwSelectionCursor.X = 0; + Console->dwSelectionCursor.Y = 0; + break; + } + + case VK_END: + { + Interpreted = TRUE; + Console->dwSelectionCursor.Y = ActiveBuffer->ScreenBufferSize.Y - 1; + break; + } + + case VK_PRIOR: + { + Interpreted = TRUE; + Console->dwSelectionCursor.Y -= ActiveBuffer->ViewSize.Y; + if (Console->dwSelectionCursor.Y < 0) + Console->dwSelectionCursor.Y = 0; + + break; + } + + case VK_NEXT: + { + Interpreted = TRUE; + Console->dwSelectionCursor.Y += ActiveBuffer->ViewSize.Y; + if (Console->dwSelectionCursor.Y >= ActiveBuffer->ScreenBufferSize.Y) + Console->dwSelectionCursor.Y = ActiveBuffer->ScreenBufferSize.Y - 1; + + break; + } + + default: + break; + } + + if (Interpreted) + { + if (!MajPressed) + Console->Selection.dwSelectionAnchor = Console->dwSelectionCursor; + + GuiConsoleUpdateSelection(Console, &Console->dwSelectionCursor); + } + else if (!IsSystemKey(VirtualKeyCode)) + { + /* Emit an error beep sound */ + SendNotifyMessage(GuiData->hWindow, PM_CONSOLE_BEEP, 0, 0); + } + + goto Quit; + } + else + { + /* Mouse selection mode */ + + if (!IsSystemKey(VirtualKeyCode)) + { + /* Clear the selection and send the key into the input buffer */ + GuiConsoleUpdateSelection(Console, NULL); + SetWindowText(GuiData->hWindow, Console->Title.Buffer); + } + else + { + goto Quit; + } + } + } + + if ((Console->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) == 0) + { + MSG Message; + + Message.hwnd = GuiData->hWindow; + Message.message = msg; + Message.wParam = wParam; + Message.lParam = lParam; + + ConioProcessKey(Console, &Message); + } + +Quit: + LeaveCriticalSection(&Console->Lock); +} + +static VOID +GuiInvalidateCell(IN OUT PFRONTEND This, SHORT x, SHORT y) +{ + SMALL_RECT CellRect = { x, y, x, y }; + GuiDrawRegion(This, &CellRect); +} + +static VOID +GuiConsoleHandleTimer(PGUI_CONSOLE_DATA GuiData) +{ + PCONSOLE Console = GuiData->Console; + PCONSOLE_SCREEN_BUFFER Buff; + + SetTimer(GuiData->hWindow, CONGUI_UPDATE_TIMER, CURSOR_BLINK_TIME, NULL); + + if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return; + + Buff = ConDrvGetActiveScreenBuffer(Console); + + if (GetType(Buff) == TEXTMODE_BUFFER) + { + GuiInvalidateCell(&Console->TermIFace, Buff->CursorPosition.X, Buff->CursorPosition.Y); + Buff->CursorBlinkOn = !Buff->CursorBlinkOn; + + if ((GuiData->OldCursor.x != Buff->CursorPosition.X) || (GuiData->OldCursor.y != Buff->CursorPosition.Y)) + { + SCROLLINFO xScroll; + int OldScrollX = -1, OldScrollY = -1; + int NewScrollX = -1, NewScrollY = -1; + + xScroll.cbSize = sizeof(SCROLLINFO); + xScroll.fMask = SIF_POS; + // Capture the original position of the scroll bars and save them. + if (GetScrollInfo(GuiData->hWindow, SB_HORZ, &xScroll)) OldScrollX = xScroll.nPos; + if (GetScrollInfo(GuiData->hWindow, SB_VERT, &xScroll)) OldScrollY = xScroll.nPos; + + // If we successfully got the info for the horizontal scrollbar + if (OldScrollX >= 0) + { + if ((Buff->CursorPosition.X < Buff->ViewOrigin.X) || (Buff->CursorPosition.X >= (Buff->ViewOrigin.X + Buff->ViewSize.X))) + { + // Handle the horizontal scroll bar + if (Buff->CursorPosition.X >= Buff->ViewSize.X) NewScrollX = Buff->CursorPosition.X - Buff->ViewSize.X + 1; + else NewScrollX = 0; + } + else + { + NewScrollX = OldScrollX; + } + } + // If we successfully got the info for the vertical scrollbar + if (OldScrollY >= 0) + { + if ((Buff->CursorPosition.Y < Buff->ViewOrigin.Y) || (Buff->CursorPosition.Y >= (Buff->ViewOrigin.Y + Buff->ViewSize.Y))) + { + // Handle the vertical scroll bar + if (Buff->CursorPosition.Y >= Buff->ViewSize.Y) NewScrollY = Buff->CursorPosition.Y - Buff->ViewSize.Y + 1; + else NewScrollY = 0; + } + else + { + NewScrollY = OldScrollY; + } + } + + // Adjust scroll bars and refresh the window if the cursor has moved outside the visible area + // NOTE: OldScroll# and NewScroll# will both be -1 (initial value) if the info for the respective scrollbar + // was not obtained successfully in the previous steps. This means their difference is 0 (no scrolling) + // and their associated scrollbar is left alone. + if ((OldScrollX != NewScrollX) || (OldScrollY != NewScrollY)) + { + Buff->ViewOrigin.X = NewScrollX; + Buff->ViewOrigin.Y = NewScrollY; + ScrollWindowEx(GuiData->hWindow, + (OldScrollX - NewScrollX) * GuiData->CharWidth, + (OldScrollY - NewScrollY) * GuiData->CharHeight, + NULL, + NULL, + NULL, + NULL, + SW_INVALIDATE); + if (NewScrollX >= 0) + { + xScroll.nPos = NewScrollX; + SetScrollInfo(GuiData->hWindow, SB_HORZ, &xScroll, TRUE); + } + if (NewScrollY >= 0) + { + xScroll.nPos = NewScrollY; + SetScrollInfo(GuiData->hWindow, SB_VERT, &xScroll, TRUE); + } + UpdateWindow(GuiData->hWindow); + GuiData->OldCursor.x = Buff->CursorPosition.X; + GuiData->OldCursor.y = Buff->CursorPosition.Y; + } + } + } + else /* if (GetType(Buff) == GRAPHICS_BUFFER) */ + { + } + + LeaveCriticalSection(&Console->Lock); +} + +static BOOL +GuiConsoleHandleClose(PGUI_CONSOLE_DATA GuiData) +{ + PCONSOLE Console = GuiData->Console; + + if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) + return TRUE; + + // TODO: Prompt for termination ? (Warn the user about possible apps running in this console) + + /* + * FIXME: Windows will wait up to 5 seconds for the thread to exit. + * We shouldn't wait here, though, since the console lock is entered. + * A copy of the thread list probably needs to be made. + */ + ConDrvConsoleProcessCtrlEvent(Console, 0, CTRL_CLOSE_EVENT); + + LeaveCriticalSection(&Console->Lock); + return FALSE; +} + +static LRESULT +GuiConsoleHandleNcDestroy(HWND hWnd) +{ + KillTimer(hWnd, CONGUI_UPDATE_TIMER); + GetSystemMenu(hWnd, TRUE); + + /* Free the GuiData registration */ + SetWindowLongPtrW(hWnd, GWLP_USERDATA, (DWORD_PTR)NULL); + + return DefWindowProcW(hWnd, WM_NCDESTROY, 0, 0); +} + +static COORD +PointToCoord(PGUI_CONSOLE_DATA GuiData, LPARAM lParam) +{ + PCONSOLE Console = GuiData->Console; + PCONSOLE_SCREEN_BUFFER Buffer = ConDrvGetActiveScreenBuffer(Console); + COORD Coord; + UINT WidthUnit, HeightUnit; + + GetScreenBufferSizeUnits(Buffer, GuiData, &WidthUnit, &HeightUnit); + + Coord.X = Buffer->ViewOrigin.X + ((SHORT)LOWORD(lParam) / (int)WidthUnit ); + Coord.Y = Buffer->ViewOrigin.Y + ((SHORT)HIWORD(lParam) / (int)HeightUnit); + + /* Clip coordinate to ensure it's inside buffer */ + if (Coord.X < 0) + Coord.X = 0; + else if (Coord.X >= Buffer->ScreenBufferSize.X) + Coord.X = Buffer->ScreenBufferSize.X - 1; + + if (Coord.Y < 0) + Coord.Y = 0; + else if (Coord.Y >= Buffer->ScreenBufferSize.Y) + Coord.Y = Buffer->ScreenBufferSize.Y - 1; + + return Coord; +} + +static LRESULT +GuiConsoleHandleMouse(PGUI_CONSOLE_DATA GuiData, UINT msg, WPARAM wParam, LPARAM lParam) +{ + BOOL Err = FALSE; + PCONSOLE Console = GuiData->Console; + + if (GuiData->IgnoreNextMouseSignal) + { + if (msg != WM_LBUTTONDOWN && + msg != WM_MBUTTONDOWN && + msg != WM_RBUTTONDOWN) + { + /* + * If this mouse signal is not a button-down action, + * then it is the last signal being ignored. + */ + GuiData->IgnoreNextMouseSignal = FALSE; + } + else + { + /* + * This mouse signal is a button-down action. + * Ignore it and perform default action. + */ + Err = TRUE; + } + goto Quit; + } + + if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) + { + Err = TRUE; + goto Quit; + } + + if ( (Console->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) || + (Console->QuickEdit) ) + { + switch (msg) + { + case WM_LBUTTONDOWN: + { + LPWSTR WindowTitle = NULL; + SIZE_T Length = 0; + + Console->Selection.dwSelectionAnchor = PointToCoord(GuiData, lParam); + SetCapture(GuiData->hWindow); + Console->Selection.dwFlags |= CONSOLE_SELECTION_IN_PROGRESS | CONSOLE_MOUSE_SELECTION | CONSOLE_MOUSE_DOWN; + GuiConsoleUpdateSelection(Console, &Console->Selection.dwSelectionAnchor); + + Length = Console->Title.Length + sizeof(L"Selection - ")/sizeof(WCHAR) + 1; + WindowTitle = ConsoleAllocHeap(0, Length * sizeof(WCHAR)); + wcscpy(WindowTitle, L"Selection - "); + wcscat(WindowTitle, Console->Title.Buffer); + SetWindowText(GuiData->hWindow, WindowTitle); + ConsoleFreeHeap(WindowTitle); + + break; + } + + case WM_LBUTTONUP: + { + COORD c; + + if (!(Console->Selection.dwFlags & CONSOLE_MOUSE_DOWN)) break; + + c = PointToCoord(GuiData, lParam); + Console->Selection.dwFlags &= ~CONSOLE_MOUSE_DOWN; + GuiConsoleUpdateSelection(Console, &c); + ReleaseCapture(); + + break; + } + + case WM_LBUTTONDBLCLK: + { + DPRINT1("Handle left-double-click for selecting a word\n"); + break; + } + + case WM_RBUTTONDOWN: + case WM_RBUTTONDBLCLK: + { + if (!(Console->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY)) + { + GuiConsolePaste(GuiData); + } + else + { + GuiConsoleCopy(GuiData); + } + + GuiData->IgnoreNextMouseSignal = TRUE; + break; + } + + case WM_MOUSEMOVE: + { + COORD c; + + if (!(wParam & MK_LBUTTON)) break; + if (!(Console->Selection.dwFlags & CONSOLE_MOUSE_DOWN)) break; + + c = PointToCoord(GuiData, lParam); /* TODO: Scroll buffer to bring c into view */ + GuiConsoleUpdateSelection(Console, &c); + + break; + } + + default: + Err = FALSE; // TRUE; + break; + } + } + else if (Console->InputBuffer.Mode & ENABLE_MOUSE_INPUT) + { + INPUT_RECORD er; + WORD wKeyState = GET_KEYSTATE_WPARAM(wParam); + DWORD dwButtonState = 0; + DWORD dwControlKeyState = 0; + DWORD dwEventFlags = 0; + + switch (msg) + { + case WM_LBUTTONDOWN: + SetCapture(GuiData->hWindow); + dwButtonState = FROM_LEFT_1ST_BUTTON_PRESSED; + dwEventFlags = 0; + break; + + case WM_MBUTTONDOWN: + SetCapture(GuiData->hWindow); + dwButtonState = FROM_LEFT_2ND_BUTTON_PRESSED; + dwEventFlags = 0; + break; + + case WM_RBUTTONDOWN: + SetCapture(GuiData->hWindow); + dwButtonState = RIGHTMOST_BUTTON_PRESSED; + dwEventFlags = 0; + break; + + case WM_LBUTTONUP: + ReleaseCapture(); + dwButtonState = 0; + dwEventFlags = 0; + break; + + case WM_MBUTTONUP: + ReleaseCapture(); + dwButtonState = 0; + dwEventFlags = 0; + break; + + case WM_RBUTTONUP: + ReleaseCapture(); + dwButtonState = 0; + dwEventFlags = 0; + break; + + case WM_LBUTTONDBLCLK: + dwButtonState = FROM_LEFT_1ST_BUTTON_PRESSED; + dwEventFlags = DOUBLE_CLICK; + break; + + case WM_MBUTTONDBLCLK: + dwButtonState = FROM_LEFT_2ND_BUTTON_PRESSED; + dwEventFlags = DOUBLE_CLICK; + break; + + case WM_RBUTTONDBLCLK: + dwButtonState = RIGHTMOST_BUTTON_PRESSED; + dwEventFlags = DOUBLE_CLICK; + break; + + case WM_MOUSEMOVE: + dwButtonState = 0; + dwEventFlags = MOUSE_MOVED; + break; + + case WM_MOUSEWHEEL: + dwButtonState = GET_WHEEL_DELTA_WPARAM(wParam) << 16; + dwEventFlags = MOUSE_WHEELED; + break; + + case WM_MOUSEHWHEEL: + dwButtonState = GET_WHEEL_DELTA_WPARAM(wParam) << 16; + dwEventFlags = MOUSE_HWHEELED; + break; + + default: + Err = TRUE; + break; + } + + if (!Err) + { + if (wKeyState & MK_LBUTTON) + dwButtonState |= FROM_LEFT_1ST_BUTTON_PRESSED; + if (wKeyState & MK_MBUTTON) + dwButtonState |= FROM_LEFT_2ND_BUTTON_PRESSED; + if (wKeyState & MK_RBUTTON) + dwButtonState |= RIGHTMOST_BUTTON_PRESSED; + + if (GetKeyState(VK_RMENU) & 0x8000) + dwControlKeyState |= RIGHT_ALT_PRESSED; + if (GetKeyState(VK_LMENU) & 0x8000) + dwControlKeyState |= LEFT_ALT_PRESSED; + if (GetKeyState(VK_RCONTROL) & 0x8000) + dwControlKeyState |= RIGHT_CTRL_PRESSED; + if (GetKeyState(VK_LCONTROL) & 0x8000) + dwControlKeyState |= LEFT_CTRL_PRESSED; + if (GetKeyState(VK_SHIFT) & 0x8000) + dwControlKeyState |= SHIFT_PRESSED; + if (GetKeyState(VK_NUMLOCK) & 0x0001) + dwControlKeyState |= NUMLOCK_ON; + if (GetKeyState(VK_SCROLL) & 0x0001) + dwControlKeyState |= SCROLLLOCK_ON; + if (GetKeyState(VK_CAPITAL) & 0x0001) + dwControlKeyState |= CAPSLOCK_ON; + /* See WM_CHAR MSDN documentation for instance */ + if (lParam & 0x01000000) + dwControlKeyState |= ENHANCED_KEY; + + er.EventType = MOUSE_EVENT; + er.Event.MouseEvent.dwMousePosition = PointToCoord(GuiData, lParam); + er.Event.MouseEvent.dwButtonState = dwButtonState; + er.Event.MouseEvent.dwControlKeyState = dwControlKeyState; + er.Event.MouseEvent.dwEventFlags = dwEventFlags; + + ConioProcessInputEvent(Console, &er); + } + } + else + { + Err = TRUE; + } + + LeaveCriticalSection(&Console->Lock); + +Quit: + if (Err) + return DefWindowProcW(GuiData->hWindow, msg, wParam, lParam); + else + return 0; +} + +VOID GuiCopyFromTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer); +VOID GuiCopyFromGraphicsBuffer(PGRAPHICS_SCREEN_BUFFER Buffer); + +static VOID +GuiConsoleCopy(PGUI_CONSOLE_DATA GuiData) +{ - if (OpenClipboard(GuiData->hWindow) == TRUE) ++ if (OpenClipboard(GuiData->hWindow)) + { + PCONSOLE Console = GuiData->Console; + PCONSOLE_SCREEN_BUFFER Buffer = ConDrvGetActiveScreenBuffer(Console); + + if (GetType(Buffer) == TEXTMODE_BUFFER) + { + GuiCopyFromTextModeBuffer((PTEXTMODE_SCREEN_BUFFER)Buffer); + } + else /* if (GetType(Buffer) == GRAPHICS_BUFFER) */ + { + GuiCopyFromGraphicsBuffer((PGRAPHICS_SCREEN_BUFFER)Buffer); + } + + CloseClipboard(); + + /* Clear the selection */ + GuiConsoleUpdateSelection(Console, NULL); + SetWindowText(GuiData->hWindow, Console->Title.Buffer); + } +} + +VOID GuiPasteToTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer); +VOID GuiPasteToGraphicsBuffer(PGRAPHICS_SCREEN_BUFFER Buffer); + +static VOID +GuiConsolePaste(PGUI_CONSOLE_DATA GuiData) +{ - if (OpenClipboard(GuiData->hWindow) == TRUE) ++ if (OpenClipboard(GuiData->hWindow)) + { + PCONSOLE Console = GuiData->Console; + PCONSOLE_SCREEN_BUFFER Buffer = ConDrvGetActiveScreenBuffer(Console); + + if (GetType(Buffer) == TEXTMODE_BUFFER) + { + GuiPasteToTextModeBuffer((PTEXTMODE_SCREEN_BUFFER)Buffer); + } + else /* if (GetType(Buffer) == GRAPHICS_BUFFER) */ + { + GuiPasteToGraphicsBuffer((PGRAPHICS_SCREEN_BUFFER)Buffer); + } + + CloseClipboard(); + } +} + +static VOID +GuiConsoleGetMinMaxInfo(PGUI_CONSOLE_DATA GuiData, PMINMAXINFO minMaxInfo) +{ + PCONSOLE Console = GuiData->Console; + PCONSOLE_SCREEN_BUFFER ActiveBuffer; + DWORD windx, windy; + UINT WidthUnit, HeightUnit; + + if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return; + + ActiveBuffer = ConDrvGetActiveScreenBuffer(Console); + + GetScreenBufferSizeUnits(ActiveBuffer, GuiData, &WidthUnit, &HeightUnit); + + windx = CONGUI_MIN_WIDTH * WidthUnit + 2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE)); + windy = CONGUI_MIN_HEIGHT * HeightUnit + 2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION); + + minMaxInfo->ptMinTrackSize.x = windx; + minMaxInfo->ptMinTrackSize.y = windy; + + windx = (ActiveBuffer->ScreenBufferSize.X) * WidthUnit + 2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE)); + windy = (ActiveBuffer->ScreenBufferSize.Y) * HeightUnit + 2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION); + + if (ActiveBuffer->ViewSize.X < ActiveBuffer->ScreenBufferSize.X) windy += GetSystemMetrics(SM_CYHSCROLL); // window currently has a horizontal scrollbar + if (ActiveBuffer->ViewSize.Y < ActiveBuffer->ScreenBufferSize.Y) windx += GetSystemMetrics(SM_CXVSCROLL); // window currently has a vertical scrollbar + + minMaxInfo->ptMaxTrackSize.x = windx; + minMaxInfo->ptMaxTrackSize.y = windy; + + LeaveCriticalSection(&Console->Lock); +} + +static VOID +GuiConsoleResize(PGUI_CONSOLE_DATA GuiData, WPARAM wParam, LPARAM lParam) +{ + PCONSOLE Console = GuiData->Console; + + if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return; + + if ((GuiData->WindowSizeLock == FALSE) && + (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED || wParam == SIZE_MINIMIZED)) + { + PCONSOLE_SCREEN_BUFFER Buff = ConDrvGetActiveScreenBuffer(Console); + DWORD windx, windy, charx, chary; + UINT WidthUnit, HeightUnit; + + GetScreenBufferSizeUnits(Buff, GuiData, &WidthUnit, &HeightUnit); + + GuiData->WindowSizeLock = TRUE; + + windx = LOWORD(lParam); + windy = HIWORD(lParam); + + // Compensate for existing scroll bars (because lParam values do not accommodate scroll bar) + if (Buff->ViewSize.X < Buff->ScreenBufferSize.X) windy += GetSystemMetrics(SM_CYHSCROLL); // window currently has a horizontal scrollbar + if (Buff->ViewSize.Y < Buff->ScreenBufferSize.Y) windx += GetSystemMetrics(SM_CXVSCROLL); // window currently has a vertical scrollbar + + charx = windx / (int)WidthUnit ; + chary = windy / (int)HeightUnit; + + // Character alignment (round size up or down) + if ((windx % WidthUnit ) >= (WidthUnit / 2)) ++charx; + if ((windy % HeightUnit) >= (HeightUnit / 2)) ++chary; + + // Compensate for added scroll bars in new window + if (charx < Buff->ScreenBufferSize.X) windy -= GetSystemMetrics(SM_CYHSCROLL); // new window will have a horizontal scroll bar + if (chary < Buff->ScreenBufferSize.Y) windx -= GetSystemMetrics(SM_CXVSCROLL); // new window will have a vertical scroll bar + + charx = windx / (int)WidthUnit ; + chary = windy / (int)HeightUnit; + + // Character alignment (round size up or down) + if ((windx % WidthUnit ) >= (WidthUnit / 2)) ++charx; + if ((windy % HeightUnit) >= (HeightUnit / 2)) ++chary; + + // Resize window + if ((charx != Buff->ViewSize.X) || (chary != Buff->ViewSize.Y)) + { + Buff->ViewSize.X = (charx <= Buff->ScreenBufferSize.X) ? charx : Buff->ScreenBufferSize.X; + Buff->ViewSize.Y = (chary <= Buff->ScreenBufferSize.Y) ? chary : Buff->ScreenBufferSize.Y; + } + + GuiConsoleResizeWindow(GuiData); + + // Adjust the start of the visible area if we are attempting to show nonexistent areas + if ((Buff->ScreenBufferSize.X - Buff->ViewOrigin.X) < Buff->ViewSize.X) Buff->ViewOrigin.X = Buff->ScreenBufferSize.X - Buff->ViewSize.X; + if ((Buff->ScreenBufferSize.Y - Buff->ViewOrigin.Y) < Buff->ViewSize.Y) Buff->ViewOrigin.Y = Buff->ScreenBufferSize.Y - Buff->ViewSize.Y; + InvalidateRect(GuiData->hWindow, NULL, TRUE); + + GuiData->WindowSizeLock = FALSE; + } + + LeaveCriticalSection(&Console->Lock); +} + +/* +// HACK: This functionality is standard for general scrollbars. Don't add it by hand. + +VOID +FASTCALL +GuiConsoleHandleScrollbarMenu(VOID) +{ + HMENU hMenu; + + hMenu = CreatePopupMenu(); + if (hMenu == NULL) + { + DPRINT("CreatePopupMenu failed\n"); + return; + } + + //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLHERE); + //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1); + //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLTOP); + //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLBOTTOM); + //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1); + //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLPAGE_UP); + //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLPAGE_DOWN); + //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1); + //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLUP); + //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLDOWN); +} +*/ + +static LRESULT +GuiConsoleHandleScroll(PGUI_CONSOLE_DATA GuiData, UINT uMsg, WPARAM wParam) +{ + PCONSOLE Console = GuiData->Console; + PCONSOLE_SCREEN_BUFFER Buff; + SCROLLINFO sInfo; + int fnBar; + int old_pos, Maximum; + PSHORT pShowXY; + + if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return 0; + + Buff = ConDrvGetActiveScreenBuffer(Console); + + if (uMsg == WM_HSCROLL) + { + fnBar = SB_HORZ; + Maximum = Buff->ScreenBufferSize.X - Buff->ViewSize.X; + pShowXY = &Buff->ViewOrigin.X; + } + else + { + fnBar = SB_VERT; + Maximum = Buff->ScreenBufferSize.Y - Buff->ViewSize.Y; + pShowXY = &Buff->ViewOrigin.Y; + } + + /* set scrollbar sizes */ + sInfo.cbSize = sizeof(SCROLLINFO); + sInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE | SIF_TRACKPOS; + + if (!GetScrollInfo(GuiData->hWindow, fnBar, &sInfo)) goto Quit; + + old_pos = sInfo.nPos; + + switch (LOWORD(wParam)) + { + case SB_LINELEFT: + sInfo.nPos -= 1; + break; + + case SB_LINERIGHT: + sInfo.nPos += 1; + break; + + case SB_PAGELEFT: + sInfo.nPos -= sInfo.nPage; + break; + + case SB_PAGERIGHT: + sInfo.nPos += sInfo.nPage; + break; + + case SB_THUMBTRACK: + sInfo.nPos = sInfo.nTrackPos; + ConioPause(Console, PAUSED_FROM_SCROLLBAR); + break; + + case SB_THUMBPOSITION: + ConioUnpause(Console, PAUSED_FROM_SCROLLBAR); + break; + + case SB_TOP: + sInfo.nPos = sInfo.nMin; + break; + + case SB_BOTTOM: + sInfo.nPos = sInfo.nMax; + break; + + default: + break; + } + + sInfo.nPos = max(sInfo.nPos, 0); + sInfo.nPos = min(sInfo.nPos, Maximum); + + if (old_pos != sInfo.nPos) + { + USHORT OldX = Buff->ViewOrigin.X; + USHORT OldY = Buff->ViewOrigin.Y; + UINT WidthUnit, HeightUnit; + + *pShowXY = sInfo.nPos; + + GetScreenBufferSizeUnits(Buff, GuiData, &WidthUnit, &HeightUnit); + + ScrollWindowEx(GuiData->hWindow, + (OldX - Buff->ViewOrigin.X) * WidthUnit , + (OldY - Buff->ViewOrigin.Y) * HeightUnit, + NULL, + NULL, + NULL, + NULL, + SW_INVALIDATE); + + sInfo.fMask = SIF_POS; + SetScrollInfo(GuiData->hWindow, fnBar, &sInfo, TRUE); + + UpdateWindow(GuiData->hWindow); + } + +Quit: + LeaveCriticalSection(&Console->Lock); + return 0; +} + +static LRESULT CALLBACK +GuiConsoleWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + LRESULT Result = 0; + PGUI_CONSOLE_DATA GuiData = NULL; + PCONSOLE Console = NULL; + + /* + * - If it's the first time we create a window for the terminal, + * just initialize it and return. + * + * - If we are destroying the window, just do it and return. + */ + if (msg == WM_NCCREATE) + { + return (LRESULT)GuiConsoleHandleNcCreate(hWnd, (LPCREATESTRUCTW)lParam); + } + else if (msg == WM_NCDESTROY) + { + return GuiConsoleHandleNcDestroy(hWnd); + } + + /* + * Now the terminal window is initialized. + * Get the terminal data via the window's data. + * If there is no data, just go away. + */ + GuiData = GuiGetGuiData(hWnd); + if (GuiData == NULL) return DefWindowProcW(hWnd, msg, wParam, lParam); + + /* + * Just retrieve a pointer to the console in case somebody needs it. + * It is not NULL because it was checked in GuiGetGuiData. + * Each helper function which needs the console has to validate and lock it. + */ + Console = GuiData->Console; + + /* We have a console, start message dispatching */ + switch (msg) + { + case WM_ACTIVATE: + { + if (LOWORD(wParam) == WA_CLICKACTIVE) GuiData->IgnoreNextMouseSignal = TRUE; + break; + } + + case WM_CLOSE: + if (GuiConsoleHandleClose(GuiData)) goto Default; + break; + + case WM_PAINT: + GuiConsoleHandlePaint(GuiData); + break; + + case WM_KEYDOWN: + case WM_KEYUP: + case WM_CHAR: + case WM_DEADCHAR: + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + case WM_SYSCHAR: + case WM_SYSDEADCHAR: + { + /* Detect Alt-Enter presses and switch back and forth to fullscreen mode */ + if (msg == WM_SYSKEYDOWN && (HIWORD(lParam) & KF_ALTDOWN) && wParam == VK_RETURN) + { + /* Switch only at first Alt-Enter press, and ignore subsequent key repetitions */ + if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) != KF_REPEAT) GuiConsoleSwitchFullScreen(GuiData); + break; + } + + GuiConsoleHandleKey(GuiData, msg, wParam, lParam); + break; + } + + case WM_TIMER: + GuiConsoleHandleTimer(GuiData); + break; + + case WM_SETCURSOR: + { + /* + * The message was sent because we are manually triggering a change. + * Check whether the mouse is indeed present on this console window + * and take appropriate decisions. + */ + if (wParam == -1 && lParam == -1) + { + POINT mouseCoords; + HWND hWndHit; + + /* Get the placement of the mouse */ + GetCursorPos(&mouseCoords); + + /* On which window is placed the mouse ? */ + hWndHit = WindowFromPoint(mouseCoords); + + /* It's our window. Perform the hit-test to be used later on. */ + if (hWndHit == hWnd) + { + wParam = (WPARAM)hWnd; + lParam = DefWindowProcW(hWndHit, WM_NCHITTEST, 0, + MAKELPARAM(mouseCoords.x, mouseCoords.y)); + } + } + + /* Set the mouse cursor only when we are in the client area */ + if ((HWND)wParam == hWnd && LOWORD(lParam) == HTCLIENT) + { + if (GuiData->MouseCursorRefCount >= 0) + { + /* Show the cursor */ + SetCursor(GuiData->hCursor); + } + else + { + /* Hide the cursor if the reference count is negative */ + SetCursor(NULL); + } + return TRUE; + } + else + { + goto Default; + } + } + + case WM_LBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_RBUTTONDOWN: + case WM_LBUTTONUP: + case WM_MBUTTONUP: + case WM_RBUTTONUP: + case WM_LBUTTONDBLCLK: + case WM_MBUTTONDBLCLK: + case WM_RBUTTONDBLCLK: + case WM_MOUSEMOVE: + case WM_MOUSEWHEEL: + case WM_MOUSEHWHEEL: + { + Result = GuiConsoleHandleMouse(GuiData, msg, wParam, lParam); + break; + } + + case WM_HSCROLL: + case WM_VSCROLL: + { + Result = GuiConsoleHandleScroll(GuiData, msg, wParam); + break; + } + + case WM_NCRBUTTONDOWN: + { + DPRINT1("WM_NCRBUTTONDOWN\n"); + /* + * HACK: !! Because, when we deal with WM_RBUTTON* and we do not + * call after that DefWindowProc, on ReactOS, right-clicks on the + * (non-client) application title-bar does not display the system + * menu and does not trigger a WM_NCRBUTTONUP message too. + * See: http://git.reactos.org/?p=reactos.git;a=blob;f=reactos/win32ss/user/user32/windows/defwnd.c;hb=HEAD#l1103 + * and line 1135 too. + */ + if (DefWindowProcW(hWnd, WM_NCHITTEST, 0, lParam) == HTCAPTION) + { + /* Call DefWindowProcW with the WM_CONTEXTMENU message */ + msg = WM_CONTEXTMENU; + } + goto Default; + } + // case WM_NCRBUTTONUP: + // DPRINT1("WM_NCRBUTTONUP\n"); + // goto Default; + + case WM_CONTEXTMENU: + { + if (DefWindowProcW(hWnd /*GuiData->hWindow*/, WM_NCHITTEST, 0, lParam) == HTCLIENT) + { + HMENU hMenu = CreatePopupMenu(); + if (hMenu != NULL) + { + GuiConsoleAppendMenuItems(hMenu, GuiConsoleEditMenuItems); + TrackPopupMenuEx(hMenu, + TPM_RIGHTBUTTON, + GET_X_LPARAM(lParam), + GET_Y_LPARAM(lParam), + hWnd, + NULL); + DestroyMenu(hMenu); + } + break; + } + else + { + goto Default; + } + } + + case WM_INITMENU: + { + HMENU hMenu = (HMENU)wParam; + if (hMenu != NULL) + { + /* Enable or disable the Close menu item */ + EnableMenuItem(hMenu, SC_CLOSE, MF_BYCOMMAND | + (GuiData->IsCloseButtonEnabled ? MF_ENABLED : MF_GRAYED)); + + /* Enable or disable the Copy and Paste items */ + EnableMenuItem(hMenu, ID_SYSTEM_EDIT_COPY , MF_BYCOMMAND | + ((Console->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) && + (Console->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY) ? MF_ENABLED : MF_GRAYED)); + EnableMenuItem(hMenu, ID_SYSTEM_EDIT_PASTE, MF_BYCOMMAND | + (!(Console->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) && + IsClipboardFormatAvailable(CF_UNICODETEXT) ? MF_ENABLED : MF_GRAYED)); + } + + if (ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) + { + GuiSendMenuEvent(Console, WM_INITMENU); + LeaveCriticalSection(&Console->Lock); + } + break; + } + + case WM_MENUSELECT: + { + if (HIWORD(wParam) == 0xFFFF) // Allow all the menu flags + { + if (ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) + { + GuiSendMenuEvent(Console, WM_MENUSELECT); + LeaveCriticalSection(&Console->Lock); + } + } + break; + } + + case WM_COMMAND: + case WM_SYSCOMMAND: + { + Result = GuiConsoleHandleSysMenuCommand(GuiData, wParam, lParam); + break; + } + + case WM_SETFOCUS: + case WM_KILLFOCUS: + { + if (ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) + { + INPUT_RECORD er; + er.EventType = FOCUS_EVENT; + er.Event.FocusEvent.bSetFocus = (msg == WM_SETFOCUS); + ConioProcessInputEvent(Console, &er); + + if (msg == WM_SETFOCUS) + DPRINT1("TODO: Create console caret\n"); + else // if (msg == WM_KILLFOCUS) + DPRINT1("TODO: Destroy console caret\n"); + + LeaveCriticalSection(&Console->Lock); + } + break; + } + + case WM_GETMINMAXINFO: + GuiConsoleGetMinMaxInfo(GuiData, (PMINMAXINFO)lParam); + break; + + case WM_SIZE: + GuiConsoleResize(GuiData, wParam, lParam); + break; + + case PM_RESIZE_TERMINAL: + { + /* Resize the window to the user's values */ + GuiData->WindowSizeLock = TRUE; + GuiConsoleResizeWindow(GuiData); + GuiData->WindowSizeLock = FALSE; + break; + } + + case PM_APPLY_CONSOLE_INFO: + { + if (ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) + { + GuiApplyUserSettings(GuiData, (HANDLE)wParam, (BOOL)lParam); + LeaveCriticalSection(&Console->Lock); + } + break; + } + + case PM_CONSOLE_BEEP: + DPRINT1("Beep !!\n"); + Beep(800, 200); + break; + + // case PM_CONSOLE_SET_TITLE: + // SetWindowText(GuiData->hWindow, GuiData->Console->Title.Buffer); + // break; + + default: Default: + Result = DefWindowProcW(hWnd, msg, wParam, lParam); + break; + } + + return Result; +} + + + +/****************************************************************************** + * GUI Terminal Initialization * + ******************************************************************************/ + +static LRESULT CALLBACK +GuiConsoleNotifyWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + HWND NewWindow; + LONG WindowCount; + MSG Msg; + + switch (msg) + { + case WM_CREATE: + { + SetWindowLongW(hWnd, GWL_USERDATA, 0); + return 0; + } + + case PM_CREATE_CONSOLE: + { + PGUI_CONSOLE_DATA GuiData = (PGUI_CONSOLE_DATA)lParam; + PCONSOLE Console = GuiData->Console; + + NewWindow = CreateWindowExW(WS_EX_CLIENTEDGE, + GUI_CONSOLE_WINDOW_CLASS, + Console->Title.Buffer, + WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + NULL, + NULL, + ConSrvDllInstance, + (PVOID)GuiData); + if (NULL != NewWindow) + { + WindowCount = GetWindowLongW(hWnd, GWL_USERDATA); + WindowCount++; + SetWindowLongW(hWnd, GWL_USERDATA, WindowCount); + + DPRINT("Set icons via PM_CREATE_CONSOLE\n"); + if (GuiData->hIcon == NULL) + { + DPRINT("Not really /o\\...\n"); + GuiData->hIcon = ghDefaultIcon; + GuiData->hIconSm = ghDefaultIconSm; + } + else if (GuiData->hIcon != ghDefaultIcon) + { + DPRINT("Yes \\o/\n"); + SendMessageW(GuiData->hWindow, WM_SETICON, ICON_BIG, (LPARAM)GuiData->hIcon); + SendMessageW(GuiData->hWindow, WM_SETICON, ICON_SMALL, (LPARAM)GuiData->hIconSm); + } + + /* Move and resize the window to the user's values */ + /* CAN WE DEADLOCK ?? */ + GuiConsoleMoveWindow(GuiData); + GuiData->WindowSizeLock = TRUE; + GuiConsoleResizeWindow(GuiData); + GuiData->WindowSizeLock = FALSE; + + // ShowWindow(NewWindow, (int)wParam); + ShowWindowAsync(NewWindow, (int)wParam); + DPRINT("Window showed\n"); + } + + return (LRESULT)NewWindow; + } + + case PM_DESTROY_CONSOLE: + { + PGUI_CONSOLE_DATA GuiData = (PGUI_CONSOLE_DATA)lParam; + + /* + * Window creation is done using a PostMessage(), so it's possible + * that the window that we want to destroy doesn't exist yet. + * So first empty the message queue. + */ + /* + while (PeekMessageW(&Msg, NULL, 0, 0, PM_REMOVE)) + { + TranslateMessage(&Msg); + DispatchMessageW(&Msg); + }*/ + while (PeekMessageW(&Msg, NULL, 0, 0, PM_REMOVE)) ; + + if (GuiData->hWindow != NULL) /* && DestroyWindow(GuiData->hWindow) */ + { + DestroyWindow(GuiData->hWindow); + + WindowCount = GetWindowLongW(hWnd, GWL_USERDATA); + WindowCount--; + SetWindowLongW(hWnd, GWL_USERDATA, WindowCount); + if (0 == WindowCount) + { + NotifyWnd = NULL; + DestroyWindow(hWnd); + DPRINT("CONSRV: Going to quit the Gui Thread!!\n"); + PostQuitMessage(0); + } + } + + return 0; + } + + default: + return DefWindowProcW(hWnd, msg, wParam, lParam); + } +} + +static DWORD WINAPI +GuiConsoleGuiThread(PVOID Data) +{ + MSG msg; + PHANDLE GraphicsStartupEvent = (PHANDLE)Data; + + /* + * This thread dispatches all the console notifications to the notify window. + * It is common for all the console windows. + */ + + PrivateCsrssManualGuiCheck(+1); + + NotifyWnd = CreateWindowW(L"ConSrvCreateNotify", + L"", + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + NULL, + NULL, + ConSrvDllInstance, + NULL); + if (NULL == NotifyWnd) + { + PrivateCsrssManualGuiCheck(-1); + SetEvent(*GraphicsStartupEvent); + return 1; + } + + SetEvent(*GraphicsStartupEvent); + + while (GetMessageW(&msg, NULL, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + + DPRINT("CONSRV: Quit the Gui Thread!!\n"); + PrivateCsrssManualGuiCheck(-1); + + return 1; +} + +static BOOL +GuiInit(VOID) +{ + WNDCLASSEXW wc; + ATOM ConsoleClassAtom; + + /* Exit if we were already initialized */ + // if (ConsInitialized) return TRUE; + + /* + * Initialize and register the different window classes, if needed. + */ + if (!ConsInitialized) + { + /* Initialize the notification window class */ + wc.cbSize = sizeof(WNDCLASSEXW); + wc.lpszClassName = L"ConSrvCreateNotify"; + wc.lpfnWndProc = GuiConsoleNotifyWndProc; + wc.style = 0; + wc.hInstance = ConSrvDllInstance; + wc.hIcon = NULL; + wc.hIconSm = NULL; + wc.hCursor = NULL; + wc.hbrBackground = NULL; + wc.lpszMenuName = NULL; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + if (RegisterClassExW(&wc) == 0) + { + DPRINT1("Failed to register GUI notify wndproc\n"); + return FALSE; + } + + /* Initialize the console window class */ + ghDefaultIcon = LoadImageW(ConSrvDllInstance, + MAKEINTRESOURCEW(IDI_TERMINAL), + IMAGE_ICON, + GetSystemMetrics(SM_CXICON), + GetSystemMetrics(SM_CYICON), + LR_SHARED); + ghDefaultIconSm = LoadImageW(ConSrvDllInstance, + MAKEINTRESOURCEW(IDI_TERMINAL), + IMAGE_ICON, + GetSystemMetrics(SM_CXSMICON), + GetSystemMetrics(SM_CYSMICON), + LR_SHARED); + ghDefaultCursor = LoadCursorW(NULL, IDC_ARROW); + wc.cbSize = sizeof(WNDCLASSEXW); + wc.lpszClassName = GUI_CONSOLE_WINDOW_CLASS; + wc.lpfnWndProc = GuiConsoleWndProc; + wc.style = CS_DBLCLKS /* | CS_HREDRAW | CS_VREDRAW */; + wc.hInstance = ConSrvDllInstance; + wc.hIcon = ghDefaultIcon; + wc.hIconSm = ghDefaultIconSm; + wc.hCursor = ghDefaultCursor; + wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); // The color of a terminal when it is switch off. + wc.lpszMenuName = NULL; + wc.cbClsExtra = 0; + wc.cbWndExtra = GWLP_CONSOLEWND_ALLOC; + + ConsoleClassAtom = RegisterClassExW(&wc); + if (ConsoleClassAtom == 0) + { + DPRINT1("Failed to register GUI console wndproc\n"); + return FALSE; + } + else + { + NtUserConsoleControl(GuiConsoleWndClassAtom, &ConsoleClassAtom, sizeof(ATOM)); + } + + ConsInitialized = TRUE; + } + + /* + * Set-up the notification window + */ + if (NULL == NotifyWnd) + { + HANDLE ThreadHandle; + HANDLE GraphicsStartupEvent; + + GraphicsStartupEvent = CreateEventW(NULL, FALSE, FALSE, NULL); + if (NULL == GraphicsStartupEvent) return FALSE; + + ThreadHandle = CreateThread(NULL, + 0, + GuiConsoleGuiThread, + (PVOID)&GraphicsStartupEvent, + 0, + NULL); + if (NULL == ThreadHandle) + { + CloseHandle(GraphicsStartupEvent); + DPRINT1("CONSRV: Failed to create graphics console thread. Expect problems\n"); + return FALSE; + } + SetThreadPriority(ThreadHandle, THREAD_PRIORITY_HIGHEST); + CloseHandle(ThreadHandle); + + WaitForSingleObject(GraphicsStartupEvent, INFINITE); + CloseHandle(GraphicsStartupEvent); + + if (NULL == NotifyWnd) + { + DPRINT1("CONSRV: Failed to create notification window.\n"); + return FALSE; + } + } + + // ConsInitialized = TRUE; + + return TRUE; +} + + + +/****************************************************************************** + * GUI Console Driver * + ******************************************************************************/ + +static VOID WINAPI +GuiDeinitFrontEnd(IN OUT PFRONTEND This); + +NTSTATUS NTAPI +GuiInitFrontEnd(IN OUT PFRONTEND This, + IN PCONSOLE Console) +{ + PGUI_INIT_INFO GuiInitInfo; + PCONSOLE_INFO ConsoleInfo; + PCONSOLE_START_INFO ConsoleStartInfo; + + PGUI_CONSOLE_DATA GuiData; + GUI_CONSOLE_INFO TermInfo; + + SIZE_T Length = 0; + LPWSTR IconPath = NULL; + INT IconIndex = 0; + + if (This == NULL || Console == NULL || This->OldData == NULL) + return STATUS_INVALID_PARAMETER; + + ASSERT(This->Console == Console); + + GuiInitInfo = This->OldData; + + if (GuiInitInfo->ConsoleInfo == NULL || GuiInitInfo->ConsoleStartInfo == NULL) + return STATUS_INVALID_PARAMETER; + + ConsoleInfo = GuiInitInfo->ConsoleInfo; + ConsoleStartInfo = GuiInitInfo->ConsoleStartInfo; + + IconPath = ConsoleStartInfo->IconPath; + IconIndex = ConsoleStartInfo->IconIndex; + + + /* Terminal data allocation */ + GuiData = ConsoleAllocHeap(HEAP_ZERO_MEMORY, sizeof(GUI_CONSOLE_DATA)); + if (!GuiData) + { + DPRINT1("CONSRV: Failed to create GUI_CONSOLE_DATA\n"); + return STATUS_UNSUCCESSFUL; + } + /* HACK */ Console->TermIFace.Data = (PVOID)GuiData; /* HACK */ + GuiData->Console = Console; + GuiData->hWindow = NULL; + + /* The console can be resized */ + Console->FixedSize = FALSE; + + InitializeCriticalSection(&GuiData->Lock); + + + /* + * Load terminal settings + */ + + /* 1. Load the default settings */ + GuiConsoleGetDefaultSettings(&TermInfo, GuiInitInfo->ProcessId); + + /* 3. Load the remaining console settings via the registry. */ + if ((ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME) == 0) + { + /* Load the terminal infos from the registry. */ + GuiConsoleReadUserSettings(&TermInfo, + ConsoleInfo->ConsoleTitle, + GuiInitInfo->ProcessId); + + /* + * Now, update them with the properties the user might gave to us + * via the STARTUPINFO structure before calling CreateProcess + * (and which was transmitted via the ConsoleStartInfo structure). + * We therefore overwrite the values read in the registry. + */ + if (ConsoleStartInfo->dwStartupFlags & STARTF_USESHOWWINDOW) + { + TermInfo.ShowWindow = ConsoleStartInfo->ShowWindow; + } + if (ConsoleStartInfo->dwStartupFlags & STARTF_USEPOSITION) + { + TermInfo.AutoPosition = FALSE; + TermInfo.WindowOrigin = ConsoleStartInfo->ConsoleWindowOrigin; + } + if (ConsoleStartInfo->dwStartupFlags & STARTF_RUNFULLSCREEN) + { + TermInfo.FullScreen = TRUE; + } + } + + + /* + * Set up GUI data + */ + + Length = min(wcslen(TermInfo.FaceName) + 1, LF_FACESIZE); // wcsnlen + wcsncpy(GuiData->GuiInfo.FaceName, TermInfo.FaceName, LF_FACESIZE); + GuiData->GuiInfo.FaceName[Length] = L'\0'; + GuiData->GuiInfo.FontFamily = TermInfo.FontFamily; + GuiData->GuiInfo.FontSize = TermInfo.FontSize; + GuiData->GuiInfo.FontWeight = TermInfo.FontWeight; + GuiData->GuiInfo.UseRasterFonts = TermInfo.UseRasterFonts; + GuiData->GuiInfo.FullScreen = TermInfo.FullScreen; + GuiData->GuiInfo.ShowWindow = TermInfo.ShowWindow; + GuiData->GuiInfo.AutoPosition = TermInfo.AutoPosition; + GuiData->GuiInfo.WindowOrigin = TermInfo.WindowOrigin; + + /* Initialize the icon handles to their default values */ + GuiData->hIcon = ghDefaultIcon; + GuiData->hIconSm = ghDefaultIconSm; + + /* Get the associated icon, if any */ + if (IconPath == NULL || IconPath[0] == L'\0') + { + IconPath = ConsoleStartInfo->AppPath; + IconIndex = 0; + } + DPRINT("IconPath = %S ; IconIndex = %lu\n", (IconPath ? IconPath : L"n/a"), IconIndex); + if (IconPath && IconPath[0] != L'\0') + { + HICON hIcon = NULL, hIconSm = NULL; + PrivateExtractIconExW(IconPath, + IconIndex, + &hIcon, + &hIconSm, + 1); + DPRINT("hIcon = 0x%p ; hIconSm = 0x%p\n", hIcon, hIconSm); + if (hIcon != NULL) + { + DPRINT("Effectively set the icons\n"); + GuiData->hIcon = hIcon; + GuiData->hIconSm = hIconSm; + } + } + + /* Mouse is shown by default with its default cursor shape */ + GuiData->hCursor = ghDefaultCursor; + GuiData->MouseCursorRefCount = 0; + + /* A priori don't ignore mouse signals */ + GuiData->IgnoreNextMouseSignal = FALSE; + + /* Close button and the corresponding system menu item are enabled by default */ + GuiData->IsCloseButtonEnabled = TRUE; + + /* There is no user-reserved menu id range by default */ + GuiData->cmdIdLow = GuiData->cmdIdHigh = 0; + + /* + * We need to wait until the GUI has been fully initialized + * to retrieve custom settings i.e. WindowSize etc... + * Ideally we could use SendNotifyMessage for this but its not + * yet implemented. + */ + GuiData->hGuiInitEvent = CreateEventW(NULL, FALSE, FALSE, NULL); + + DPRINT("GUI - Checkpoint\n"); + + /* Create the terminal window */ + PostMessageW(NotifyWnd, PM_CREATE_CONSOLE, GuiData->GuiInfo.ShowWindow, (LPARAM)GuiData); + + /* Wait until initialization has finished */ + WaitForSingleObject(GuiData->hGuiInitEvent, INFINITE); + DPRINT("OK we created the console window\n"); + CloseHandle(GuiData->hGuiInitEvent); + GuiData->hGuiInitEvent = NULL; + + /* Check whether we really succeeded in initializing the terminal window */ + if (GuiData->hWindow == NULL) + { + DPRINT("GuiInitConsole - We failed at creating a new terminal window\n"); + GuiDeinitFrontEnd(This); + return STATUS_UNSUCCESSFUL; + } + + /* Finally, finish to initialize the frontend structure */ + This->Data = GuiData; + if (This->OldData) ConsoleFreeHeap(This->OldData); + This->OldData = NULL; + + return STATUS_SUCCESS; +} + +static VOID WINAPI +GuiDeinitFrontEnd(IN OUT PFRONTEND This) +{ + PGUI_CONSOLE_DATA GuiData = This->Data; + + SendMessageW(NotifyWnd, PM_DESTROY_CONSOLE, 0, (LPARAM)GuiData); + + DPRINT("Destroying icons !! - GuiData->hIcon = 0x%p ; ghDefaultIcon = 0x%p ; GuiData->hIconSm = 0x%p ; ghDefaultIconSm = 0x%p\n", + GuiData->hIcon, ghDefaultIcon, GuiData->hIconSm, ghDefaultIconSm); + if (GuiData->hIcon != NULL && GuiData->hIcon != ghDefaultIcon) + { + DPRINT("Destroy hIcon\n"); + DestroyIcon(GuiData->hIcon); + } + if (GuiData->hIconSm != NULL && GuiData->hIconSm != ghDefaultIconSm) + { + DPRINT("Destroy hIconSm\n"); + DestroyIcon(GuiData->hIconSm); + } + + This->Data = NULL; + DeleteCriticalSection(&GuiData->Lock); + ConsoleFreeHeap(GuiData); + + DPRINT("Quit GuiDeinitFrontEnd\n"); +} + +static VOID WINAPI +GuiDrawRegion(IN OUT PFRONTEND This, + SMALL_RECT* Region) +{ + PGUI_CONSOLE_DATA GuiData = This->Data; + RECT RegionRect; + + SmallRectToRect(GuiData, &RegionRect, Region); + /* Do not erase the background: it speeds up redrawing and reduce flickering */ + InvalidateRect(GuiData->hWindow, &RegionRect, FALSE); +} + +static VOID WINAPI +GuiWriteStream(IN OUT PFRONTEND This, + SMALL_RECT* Region, + SHORT CursorStartX, + SHORT CursorStartY, + UINT ScrolledLines, + PWCHAR Buffer, + UINT Length) +{ + PGUI_CONSOLE_DATA GuiData = This->Data; + PCONSOLE_SCREEN_BUFFER Buff; + SHORT CursorEndX, CursorEndY; + RECT ScrollRect; + + if (NULL == GuiData || NULL == GuiData->hWindow) return; + + Buff = ConDrvGetActiveScreenBuffer(GuiData->Console); + if (GetType(Buff) != TEXTMODE_BUFFER) return; + + if (0 != ScrolledLines) + { + ScrollRect.left = 0; + ScrollRect.top = 0; + ScrollRect.right = Buff->ViewSize.X * GuiData->CharWidth; + ScrollRect.bottom = Region->Top * GuiData->CharHeight; + + ScrollWindowEx(GuiData->hWindow, + 0, + -(int)(ScrolledLines * GuiData->CharHeight), + &ScrollRect, + NULL, + NULL, + NULL, + SW_INVALIDATE); + } + + GuiDrawRegion(This, Region); + + if (CursorStartX < Region->Left || Region->Right < CursorStartX + || CursorStartY < Region->Top || Region->Bottom < CursorStartY) + { + GuiInvalidateCell(This, CursorStartX, CursorStartY); + } + + CursorEndX = Buff->CursorPosition.X; + CursorEndY = Buff->CursorPosition.Y; + if ((CursorEndX < Region->Left || Region->Right < CursorEndX + || CursorEndY < Region->Top || Region->Bottom < CursorEndY) + && (CursorEndX != CursorStartX || CursorEndY != CursorStartY)) + { + GuiInvalidateCell(This, CursorEndX, CursorEndY); + } + + // Set up the update timer (very short interval) - this is a "hack" for getting the OS to + // repaint the window without having it just freeze up and stay on the screen permanently. + Buff->CursorBlinkOn = TRUE; + SetTimer(GuiData->hWindow, CONGUI_UPDATE_TIMER, CONGUI_UPDATE_TIME, NULL); +} + +static BOOL WINAPI +GuiSetCursorInfo(IN OUT PFRONTEND This, + PCONSOLE_SCREEN_BUFFER Buff) +{ + PGUI_CONSOLE_DATA GuiData = This->Data; + + if (ConDrvGetActiveScreenBuffer(GuiData->Console) == Buff) + { + GuiInvalidateCell(This, Buff->CursorPosition.X, Buff->CursorPosition.Y); + } + + return TRUE; +} + +static BOOL WINAPI +GuiSetScreenInfo(IN OUT PFRONTEND This, + PCONSOLE_SCREEN_BUFFER Buff, + SHORT OldCursorX, + SHORT OldCursorY) +{ + PGUI_CONSOLE_DATA GuiData = This->Data; + + if (ConDrvGetActiveScreenBuffer(GuiData->Console) == Buff) + { + /* Redraw char at old position (remove cursor) */ + GuiInvalidateCell(This, OldCursorX, OldCursorY); + /* Redraw char at new position (show cursor) */ + GuiInvalidateCell(This, Buff->CursorPosition.X, Buff->CursorPosition.Y); + } + + return TRUE; +} + +static VOID WINAPI +GuiResizeTerminal(IN OUT PFRONTEND This) +{ + PGUI_CONSOLE_DATA GuiData = This->Data; + + /* Resize the window to the user's values */ + // GuiData->WindowSizeLock = TRUE; + // GuiConsoleResizeWindow(GuiData); + // GuiData->WindowSizeLock = FALSE; + // NOTE: This code ^^ causes deadlocks... + + PostMessageW(GuiData->hWindow, PM_RESIZE_TERMINAL, 0, 0); +} + +static BOOL WINAPI +GuiProcessKeyCallback(IN OUT PFRONTEND This, + MSG* msg, + BYTE KeyStateMenu, + DWORD ShiftState, + UINT VirtualKeyCode, + BOOL Down) +{ + if ((ShiftState & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED) || KeyStateMenu & 0x80) && + (VirtualKeyCode == VK_ESCAPE || VirtualKeyCode == VK_TAB || VirtualKeyCode == VK_SPACE)) + { + DefWindowProcW(msg->hwnd, msg->message, msg->wParam, msg->lParam); + return TRUE; + } + + return FALSE; +} + +static BOOL WINAPI +GuiSetMouseCursor(IN OUT PFRONTEND This, + HCURSOR hCursor); + +static VOID WINAPI +GuiRefreshInternalInfo(IN OUT PFRONTEND This) +{ + PGUI_CONSOLE_DATA GuiData = This->Data; + + /* Update the console leader information held by the window */ + SetConsoleWndConsoleLeaderCID(GuiData); + + /* + * HACK: + * We reset the cursor here so that, when a console app quits, we reset + * the cursor to the default one. It's quite a hack since it doesn't proceed + * per - console process... This must be fixed. + * + * See GuiInitConsole(...) for more information. + */ + + /* Mouse is shown by default with its default cursor shape */ + GuiData->MouseCursorRefCount = 0; // Reinitialize the reference counter + GuiSetMouseCursor(This, NULL); +} + +static VOID WINAPI +GuiChangeTitle(IN OUT PFRONTEND This) +{ + PGUI_CONSOLE_DATA GuiData = This->Data; + // PostMessageW(GuiData->hWindow, PM_CONSOLE_SET_TITLE, 0, 0); + SetWindowText(GuiData->hWindow, GuiData->Console->Title.Buffer); +} + +static BOOL WINAPI +GuiChangeIcon(IN OUT PFRONTEND This, + HICON hWindowIcon) +{ + PGUI_CONSOLE_DATA GuiData = This->Data; + HICON hIcon, hIconSm; + + if (hWindowIcon == NULL) + { + hIcon = ghDefaultIcon; + hIconSm = ghDefaultIconSm; + } + else + { + hIcon = CopyIcon(hWindowIcon); + hIconSm = CopyIcon(hWindowIcon); + } + + if (hIcon == NULL) + { + return FALSE; + } + + if (hIcon != GuiData->hIcon) + { + if (GuiData->hIcon != NULL && GuiData->hIcon != ghDefaultIcon) + { + DestroyIcon(GuiData->hIcon); + } + if (GuiData->hIconSm != NULL && GuiData->hIconSm != ghDefaultIconSm) + { + DestroyIcon(GuiData->hIconSm); + } + + GuiData->hIcon = hIcon; + GuiData->hIconSm = hIconSm; + + DPRINT("Set icons in GuiChangeIcon\n"); + PostMessageW(GuiData->hWindow, WM_SETICON, ICON_BIG, (LPARAM)GuiData->hIcon); + PostMessageW(GuiData->hWindow, WM_SETICON, ICON_SMALL, (LPARAM)GuiData->hIconSm); + } + + return TRUE; +} + +static HWND WINAPI +GuiGetConsoleWindowHandle(IN OUT PFRONTEND This) +{ + PGUI_CONSOLE_DATA GuiData = This->Data; + return GuiData->hWindow; +} + +static VOID WINAPI +GuiGetLargestConsoleWindowSize(IN OUT PFRONTEND This, + PCOORD pSize) +{ + PGUI_CONSOLE_DATA GuiData = This->Data; + PCONSOLE_SCREEN_BUFFER ActiveBuffer; + RECT WorkArea; + LONG width, height; + UINT WidthUnit, HeightUnit; + + if (!pSize) return; + + if (!SystemParametersInfoW(SPI_GETWORKAREA, 0, &WorkArea, 0)) + { + DPRINT1("SystemParametersInfoW failed - What to do ??\n"); + return; + } + + ActiveBuffer = ConDrvGetActiveScreenBuffer(GuiData->Console); + if (ActiveBuffer) + { + GetScreenBufferSizeUnits(ActiveBuffer, GuiData, &WidthUnit, &HeightUnit); + } + else + { + /* Default: text mode */ + WidthUnit = GuiData->CharWidth ; + HeightUnit = GuiData->CharHeight; + } + + width = WorkArea.right; + height = WorkArea.bottom; + + width -= (2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE))); + height -= (2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION)); + + if (width < 0) width = 0; + if (height < 0) height = 0; + + pSize->X = (SHORT)(width / (int)WidthUnit ) /* HACK */ + 2; + pSize->Y = (SHORT)(height / (int)HeightUnit) /* HACK */ + 1; +} + +static ULONG WINAPI +GuiGetDisplayMode(IN OUT PFRONTEND This) +{ + PGUI_CONSOLE_DATA GuiData = This->Data; + ULONG DisplayMode = 0; + + if (GuiData->GuiInfo.FullScreen) + DisplayMode |= CONSOLE_FULLSCREEN_HARDWARE; // CONSOLE_FULLSCREEN + else + DisplayMode |= CONSOLE_WINDOWED; + + return DisplayMode; +} + +static BOOL WINAPI +GuiSetDisplayMode(IN OUT PFRONTEND This, + ULONG NewMode) +{ + PGUI_CONSOLE_DATA GuiData = This->Data; + + if (NewMode & ~(CONSOLE_FULLSCREEN_MODE | CONSOLE_WINDOWED_MODE)) + return FALSE; + + GuiData->GuiInfo.FullScreen = (NewMode & CONSOLE_FULLSCREEN_MODE); + // TODO: Change the display mode + return TRUE; +} + +static INT WINAPI +GuiShowMouseCursor(IN OUT PFRONTEND This, + BOOL Show) +{ + PGUI_CONSOLE_DATA GuiData = This->Data; + + /* Set the reference count */ + if (Show) ++GuiData->MouseCursorRefCount; + else --GuiData->MouseCursorRefCount; + + /* Effectively show (or hide) the cursor (use special values for (w|l)Param) */ + PostMessageW(GuiData->hWindow, WM_SETCURSOR, -1, -1); + + return GuiData->MouseCursorRefCount; +} + +static BOOL WINAPI +GuiSetMouseCursor(IN OUT PFRONTEND This, + HCURSOR hCursor) +{ + PGUI_CONSOLE_DATA GuiData = This->Data; + + /* + * Set the cursor's handle. If the given handle is NULL, + * then restore the default cursor. + */ + GuiData->hCursor = (hCursor ? hCursor : ghDefaultCursor); + + /* Effectively modify the shape of the cursor (use special values for (w|l)Param) */ + PostMessageW(GuiData->hWindow, WM_SETCURSOR, -1, -1); + + return TRUE; +} + +static HMENU WINAPI +GuiMenuControl(IN OUT PFRONTEND This, + UINT cmdIdLow, + UINT cmdIdHigh) +{ + PGUI_CONSOLE_DATA GuiData = This->Data; + + GuiData->cmdIdLow = cmdIdLow ; + GuiData->cmdIdHigh = cmdIdHigh; + + return GetSystemMenu(GuiData->hWindow, FALSE); +} + +static BOOL WINAPI +GuiSetMenuClose(IN OUT PFRONTEND This, + BOOL Enable) +{ + /* + * NOTE: See http://www.mail-archive.com/harbour@harbour-project.org/msg27509.html + * or http://harbour-devel.1590103.n2.nabble.com/Question-about-hb-gt-win-CtrlHandler-usage-td4670862i20.html + * for more information. + */ + + PGUI_CONSOLE_DATA GuiData = This->Data; + HMENU hSysMenu = GetSystemMenu(GuiData->hWindow, FALSE); + + if (hSysMenu == NULL) return FALSE; + + GuiData->IsCloseButtonEnabled = Enable; + EnableMenuItem(hSysMenu, SC_CLOSE, MF_BYCOMMAND | (Enable ? MF_ENABLED : MF_GRAYED)); + + return TRUE; +} + +static FRONTEND_VTBL GuiVtbl = +{ + GuiInitFrontEnd, + GuiDeinitFrontEnd, + GuiDrawRegion, + GuiWriteStream, + GuiSetCursorInfo, + GuiSetScreenInfo, + GuiResizeTerminal, + GuiProcessKeyCallback, + GuiRefreshInternalInfo, + GuiChangeTitle, + GuiChangeIcon, + GuiGetConsoleWindowHandle, + GuiGetLargestConsoleWindowSize, + GuiGetDisplayMode, + GuiSetDisplayMode, + GuiShowMouseCursor, + GuiSetMouseCursor, + GuiMenuControl, + GuiSetMenuClose, +}; + + +static BOOL +LoadShellLinkConsoleInfo(IN OUT PCONSOLE_START_INFO ConsoleStartInfo, + IN OUT PCONSOLE_INFO ConsoleInfo) +{ +#define PATH_SEPARATOR L'\\' + + BOOL RetVal = FALSE; + HRESULT hRes = S_OK; + LPWSTR LinkName = NULL; + SIZE_T Length = 0; + + if ((ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME) == 0) + return FALSE; + + ConsoleStartInfo->IconPath[0] = L'\0'; + ConsoleStartInfo->IconIndex = 0; + + /* 1- Find the last path separator if any */ + LinkName = wcsrchr(ConsoleStartInfo->ConsoleTitle, PATH_SEPARATOR); + if (LinkName == NULL) + { + LinkName = ConsoleStartInfo->ConsoleTitle; + } + else + { + /* Skip the path separator */ + ++LinkName; + } + + /* 2- Check for the link extension. The name ".lnk" is considered invalid. */ + Length = wcslen(LinkName); + if ( (Length <= 4) || (wcsicmp(LinkName + (Length - 4), L".lnk") != 0) ) + return FALSE; + + /* 3- It may be a link. Try to retrieve some properties */ + hRes = CoInitialize(NULL); + if (SUCCEEDED(hRes)) + { + /* Get a pointer to the IShellLink interface */ + IShellLinkW* pshl = NULL; + hRes = CoCreateInstance(&CLSID_ShellLink, + NULL, + CLSCTX_INPROC_SERVER, + &IID_IShellLinkW, + (LPVOID*)&pshl); + if (SUCCEEDED(hRes)) + { + /* Get a pointer to the IPersistFile interface */ + IPersistFile* ppf = NULL; + hRes = IPersistFile_QueryInterface(pshl, &IID_IPersistFile, (LPVOID*)&ppf); + if (SUCCEEDED(hRes)) + { + /* Load the shortcut */ + hRes = IPersistFile_Load(ppf, ConsoleStartInfo->ConsoleTitle, STGM_READ); + if (SUCCEEDED(hRes)) + { + /* + * Finally we can get the properties ! + * Update the old ones if needed. + */ + INT ShowCmd = 0; + // WORD HotKey = 0; + + /* Reset the name of the console with the name of the shortcut */ + Length = min(/*Length*/ Length - 4, // 4 == len(".lnk") + sizeof(ConsoleInfo->ConsoleTitle) / sizeof(ConsoleInfo->ConsoleTitle[0]) - 1); + wcsncpy(ConsoleInfo->ConsoleTitle, LinkName, Length); + ConsoleInfo->ConsoleTitle[Length] = L'\0'; + + /* Get the window showing command */ + hRes = IShellLinkW_GetShowCmd(pshl, &ShowCmd); + if (SUCCEEDED(hRes)) ConsoleStartInfo->ShowWindow = (WORD)ShowCmd; + + /* Get the hotkey */ + // hRes = pshl->GetHotkey(&ShowCmd); + // if (SUCCEEDED(hRes)) ConsoleStartInfo->HotKey = HotKey; + + /* Get the icon location, if any */ + + hRes = IShellLinkW_GetIconLocation(pshl, + ConsoleStartInfo->IconPath, + sizeof(ConsoleStartInfo->IconPath)/sizeof(ConsoleStartInfo->IconPath[0]) - 1, // == MAX_PATH + &ConsoleStartInfo->IconIndex); + if (!SUCCEEDED(hRes)) + { + ConsoleStartInfo->IconPath[0] = L'\0'; + ConsoleStartInfo->IconIndex = 0; + } + + // FIXME: Since we still don't load console properties from the shortcut, + // return false. When this will be done, we will return true instead. + RetVal = FALSE; + } + IPersistFile_Release(ppf); + } + IShellLinkW_Release(pshl); + } + } + CoUninitialize(); + + return RetVal; +} + +NTSTATUS NTAPI +GuiLoadFrontEnd(IN OUT PFRONTEND FrontEnd, + IN OUT PCONSOLE_INFO ConsoleInfo, + IN OUT PVOID ExtraConsoleInfo, + IN ULONG ProcessId) +{ + PCONSOLE_START_INFO ConsoleStartInfo = ExtraConsoleInfo; + PGUI_INIT_INFO GuiInitInfo; + + if (FrontEnd == NULL || ConsoleInfo == NULL || ConsoleStartInfo == NULL) + return STATUS_INVALID_PARAMETER; + + /* Initialize GUI terminal emulator common functionalities */ + if (!GuiInit()) return STATUS_UNSUCCESSFUL; + + /* + * Load per-application terminal settings. + * + * Check whether the process creating the console was launched via + * a shell-link. ConsoleInfo->ConsoleTitle may be updated with the + * name of the shortcut, and ConsoleStartInfo->Icon[Path|Index] too. + */ + if (ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME) + { + if (!LoadShellLinkConsoleInfo(ConsoleStartInfo, ConsoleInfo)) + { + ConsoleStartInfo->dwStartupFlags &= ~STARTF_TITLEISLINKNAME; + } + } + + /* + * Initialize a private initialization info structure for later use. + * It must be freed by a call to GuiUnloadFrontEnd or GuiInitFrontEnd. + */ + GuiInitInfo = ConsoleAllocHeap(HEAP_ZERO_MEMORY, sizeof(GUI_INIT_INFO)); + if (GuiInitInfo == NULL) return STATUS_NO_MEMORY; + + // HACK: We suppose that the pointers will be valid in GuiInitFrontEnd... + GuiInitInfo->ConsoleInfo = ConsoleInfo; + GuiInitInfo->ConsoleStartInfo = ConsoleStartInfo; + GuiInitInfo->ProcessId = ProcessId; + + /* Finally, initialize the frontend structure */ + FrontEnd->Vtbl = &GuiVtbl; + FrontEnd->Data = NULL; + FrontEnd->OldData = GuiInitInfo; + + return STATUS_SUCCESS; +} + +NTSTATUS NTAPI +GuiUnloadFrontEnd(IN OUT PFRONTEND FrontEnd) +{ + if (FrontEnd == NULL) return STATUS_INVALID_PARAMETER; + + if (FrontEnd->Data) GuiDeinitFrontEnd(FrontEnd); + if (FrontEnd->OldData) ConsoleFreeHeap(FrontEnd->OldData); + + return STATUS_SUCCESS; +} + +/* EOF */ diff --cc win32ss/user/winsrv/usersrv/register.c index 5322075c276,00000000000..e1a7154f712 mode 100644,000000..100644 --- a/win32ss/user/winsrv/usersrv/register.c +++ b/win32ss/user/winsrv/usersrv/register.c @@@ -1,69 -1,0 +1,69 @@@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS User API Server DLL + * FILE: win32ss/user/winsrv/usersrv/register.c + * PURPOSE: Register logon window and services process + * PROGRAMMERS: Eric Kohl + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +/* INCLUDES *******************************************************************/ + +#include "usersrv.h" + +#define NDEBUG +#include + +/* GLOBALS ********************************************************************/ + +static BOOLEAN ServicesProcessIdValid = FALSE; +static ULONG_PTR ServicesProcessId = 0; + +ULONG_PTR LogonProcessId = 0; + +/* PUBLIC SERVER APIS *********************************************************/ + +CSR_API(SrvRegisterLogonProcess) +{ + PUSER_REGISTER_LOGON_PROCESS RegisterLogonProcessRequest = &((PUSER_API_MESSAGE)ApiMessage)->Data.RegisterLogonProcessRequest; + + if (RegisterLogonProcessRequest->Register) + { + if (LogonProcessId != 0) + return STATUS_LOGON_SESSION_EXISTS; + + LogonProcessId = RegisterLogonProcessRequest->ProcessId; + } + else + { + if (ApiMessage->Header.ClientId.UniqueProcess != (HANDLE)LogonProcessId) + { + DPRINT1("Current logon process 0x%x, can't deregister from process 0x%x\n", + LogonProcessId, ApiMessage->Header.ClientId.UniqueProcess); + return STATUS_NOT_LOGON_PROCESS; + } + + LogonProcessId = 0; + } + + return STATUS_SUCCESS; +} + +CSR_API(SrvRegisterServicesProcess) +{ + PUSER_REGISTER_SERVICES_PROCESS RegisterServicesProcessRequest = &((PUSER_API_MESSAGE)ApiMessage)->Data.RegisterServicesProcessRequest; + - if (ServicesProcessIdValid == TRUE) ++ if (ServicesProcessIdValid) + { + /* Only accept a single call */ + return STATUS_INVALID_PARAMETER; + } + else + { + ServicesProcessId = RegisterServicesProcessRequest->ProcessId; + ServicesProcessIdValid = TRUE; + return STATUS_SUCCESS; + } +} + +/* EOF */