/* * ReactOS kernel * Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * PROJECT: ReactOS user32.dll * FILE: win32ss/user/user32/windows/accel.c * PURPOSE: Accelerator tables * PROGRAMMER: KJK::Hyperion * UPDATE HISTORY: * 09/05/2001 CSH Created * 08/07/2003 KJK Fully implemented */ #include /* this is the 8 byte accel struct used in Win32 resources (internal only) */ typedef struct { BYTE fVirt; BYTE pad0; WORD key; WORD cmd; WORD pad1; } PE_ACCEL, *LPPE_ACCEL; /* Cache entry */ typedef struct _USER_ACCEL_CACHE_ENTRY { struct _USER_ACCEL_CACHE_ENTRY * Next; ULONG_PTR Usage; /* how many times the table has been loaded */ HACCEL Object; /* handle to the NtUser accelerator table object */ HGLOBAL Data; /* base address of the resource data */ } U32_ACCEL_CACHE_ENTRY; /* FUNCTIONS *****************************************************************/ /* Lock guarding the cache */ CRITICAL_SECTION U32AccelCacheLock; /* Cache */ U32_ACCEL_CACHE_ENTRY * U32AccelCache = NULL; /* Look up a handle or resource address in the cache */ U32_ACCEL_CACHE_ENTRY ** WINAPI U32AccelCacheFind(HANDLE Object, HGLOBAL Data) { /* to avoid using a double-link list and still allow elements to be removed, return a pointer to the list link that points to the desired entry */ U32_ACCEL_CACHE_ENTRY ** ppEntry = &U32AccelCache; for(; *ppEntry; ppEntry = &((*ppEntry)->Next)) if((*ppEntry)->Object == Object || (*ppEntry)->Data == Data) break; return ppEntry; } /* Allocate an entry and insert it into the cache */ void WINAPI U32AccelCacheAdd(HACCEL Object, HGLOBAL Data) { U32_ACCEL_CACHE_ENTRY * pEntry = LocalAlloc(LMEM_FIXED, sizeof(U32_ACCEL_CACHE_ENTRY)); /* failed to allocate an entry - not critical */ if(pEntry == NULL) return; /* initialize the entry */ pEntry->Usage = 1; pEntry->Object = Object; pEntry->Data = Data; /* insert the entry into the cache */ pEntry->Next = U32AccelCache; U32AccelCache = pEntry; } /* Create an accelerator table from a loaded resource */ HACCEL WINAPI U32LoadAccelerators(HINSTANCE hInstance, HRSRC hTableRes) { HGLOBAL hAccTableData; HACCEL hAccTable = NULL; U32_ACCEL_CACHE_ENTRY * pEntry; PE_ACCEL * pAccTableResData; SIZE_T i = 0; SIZE_T j = 0; ACCEL * pAccTableData; /* load the accelerator table */ hAccTableData = LoadResource(hInstance, hTableRes); /* failure */ if(hAccTableData == NULL) return NULL; EnterCriticalSection(&U32AccelCacheLock); /* see if this accelerator table has already been loaded */ pEntry = *U32AccelCacheFind(NULL, hAccTableData); /* accelerator table already loaded */ if(pEntry) { /* increment the reference count */ ++ pEntry->Usage; /* return the existing object */ hAccTable = pEntry->Object; /* success */ goto l_Leave; } /* determine the number of entries in the table */ i = SizeofResource(hInstance, hTableRes) / sizeof(PE_ACCEL); /* allocate the buffer for the table to be passed to Win32K */ pAccTableData = LocalAlloc(LMEM_FIXED, i * sizeof(ACCEL)); /* failure */ if(pAccTableData == NULL) goto l_Leave; pAccTableResData = (PE_ACCEL *)hAccTableData; /* copy the table */ for(j = 0; j < i; ++ j) { pAccTableData[j].fVirt = pAccTableResData[j].fVirt; pAccTableData[j].key = pAccTableResData[j].key; pAccTableData[j].cmd = pAccTableResData[j].cmd; } pAccTableData[i - 1].fVirt |= 0x80; /* create a new accelerator table object */ hAccTable = NtUserCreateAcceleratorTable(pAccTableData, i); /* free the buffer */ LocalFree(pAccTableData); /* failure */ if(hAccTable == NULL) goto l_Leave; /* success - cache the object */ U32AccelCacheAdd(hAccTable, pAccTableResData); l_Leave: LeaveCriticalSection(&U32AccelCacheLock); return hAccTable; } /* Checks if a message can be translated through an accelerator table */ BOOL WINAPI U32IsValidAccelMessage(UINT uMsg) { switch(uMsg) { case WM_KEYDOWN: case WM_KEYUP: case WM_CHAR: case WM_SYSCHAR: case WM_SYSKEYDOWN: case WM_SYSKEYUP: return TRUE; default: return FALSE; } } /* WIN32 FUNCTIONS ***********************************************************/ /* * Dereference the specified accelerator table, removing it from the cache and * deleting the associated NtUser object as appropriate * * @implemented */ BOOL WINAPI DestroyAcceleratorTable(HACCEL hAccel) { U32_ACCEL_CACHE_ENTRY ** ppEntry; ULONG_PTR nUsage = 0; if (!hAccel) return FALSE; EnterCriticalSection(&U32AccelCacheLock); /* see if this accelerator table has been cached */ ppEntry = U32AccelCacheFind(hAccel, NULL); /* accelerator table cached */ if(*ppEntry) { U32_ACCEL_CACHE_ENTRY * pEntry = *ppEntry; /* decrement the reference count */ nUsage = pEntry->Usage = pEntry->Usage - 1; /* reference count now zero: destroy the cache entry */ if(nUsage == 0) { /* unlink the cache entry */ *ppEntry = pEntry->Next; /* free the cache entry */ LocalFree(pEntry); } } LeaveCriticalSection(&U32AccelCacheLock); if(nUsage > 0) return FALSE; /* destroy the object */ return NtUserDestroyAcceleratorTable(hAccel); } /* * Create an accelerator table from a named resource * * @implemented */ HACCEL WINAPI LoadAcceleratorsW(HINSTANCE hInstance, LPCWSTR lpTableName) { return U32LoadAccelerators ( hInstance, FindResourceExW(hInstance, (LPCWSTR) RT_ACCELERATOR, lpTableName, 0) ); } /* * @implemented */ HACCEL WINAPI LoadAcceleratorsA(HINSTANCE hInstance, LPCSTR lpTableName) { HRSRC Accel; Accel = FindResourceExA(hInstance, (LPCSTR) RT_ACCELERATOR, lpTableName, 0); if (NULL == Accel) { return NULL; } return U32LoadAccelerators(hInstance, Accel); } /* * Translate a key press into a WM_COMMAND message * * @implemented */ int WINAPI TranslateAcceleratorW(HWND hWnd, HACCEL hAccTable, LPMSG lpMsg) { if(!U32IsValidAccelMessage(lpMsg->message)) return 0; return NtUserTranslateAccelerator(hWnd, hAccTable, lpMsg); } /* * @implemented */ int WINAPI CopyAcceleratorTableA ( HACCEL hAccelSrc, LPACCEL lpAccelDst, /* can be NULL */ int cAccelEntries ) { int i; cAccelEntries = CopyAcceleratorTableW(hAccelSrc, lpAccelDst, cAccelEntries); if (lpAccelDst == NULL) return cAccelEntries; for(i = 0; i < cAccelEntries; ++ i) if(!(lpAccelDst[i].fVirt & FVIRTKEY)) { NTSTATUS nErrCode = RtlUnicodeToMultiByteN( (PCHAR)&lpAccelDst[i].key, sizeof(lpAccelDst[i].key), NULL, (PWCHAR)&lpAccelDst[i].key, sizeof(lpAccelDst[i].key) ); if(!NT_SUCCESS(nErrCode)) lpAccelDst[i].key = 0; } return cAccelEntries; } /* * @implemented */ HACCEL WINAPI CreateAcceleratorTableA(LPACCEL lpaccl, int cEntries) { int i; if (!cEntries || !lpaccl) return (HACCEL)0; for(i = 0; i < cEntries; ++ i) if(!lpaccl[i].fVirt) { NTSTATUS nErrCode = RtlMultiByteToUnicodeN ( (PWCHAR)&lpaccl[i].key, sizeof(lpaccl[i].key), NULL, (PCHAR)&lpaccl[i].key, sizeof(lpaccl[i].key) ); if(!NT_SUCCESS(nErrCode)) lpaccl[i].key = -1; } return CreateAcceleratorTableW(lpaccl, cEntries); } /* * @implemented */ int WINAPI TranslateAcceleratorA(HWND hWnd, HACCEL hAccTable, LPMSG lpMsg) { switch (lpMsg->message) { case WM_KEYDOWN: case WM_SYSKEYDOWN: return TranslateAcceleratorW( hWnd, hAccTable, lpMsg ); case WM_CHAR: case WM_SYSCHAR: { MSG msgW = *lpMsg; char ch = LOWORD(lpMsg->wParam); WCHAR wch; MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1); msgW.wParam = MAKEWPARAM(wch, HIWORD(lpMsg->wParam)); return TranslateAcceleratorW( hWnd, hAccTable, &msgW ); } default: return 0; } } /* EOF */