Windows-Server-2003/shell/ext/keyremap/mapps.c

536 lines
14 KiB
C

/*****************************************************************************
*
* mapps.c - Property sheet handler
*
*****************************************************************************/
#include "map.h"
/*****************************************************************************
*
* The sqiffle for this file.
*
*****************************************************************************/
#define sqfl sqflPs
/*****************************************************************************
*
* Strings.
*
* The Scancode map registry value looks like this:
*
* DWORD dwVersion; // Must be zero
* DWORD dwFlags; // Must be zero
* DWORD dwNumRemaps; // Number of remaps, including terminating 0
* REMAPENTRY rgRemap[...]; // dwNumRemaps remap entries
*
* The last remap entry must be all-zero.
*
*
* Each remap entry looks like this:
*
* WORD wTo;
* WORD wFrom;
*
* where wFrom is the source scancode and wTo is the target scancode.
* If the key being remapped is an extended key, then the high word
* of the scancode is 0xE0. Otherwise, the high word is zero.
*
* NOTE! When we load the scancode map into memory, we make
* dwNumRemaps *not* include the terminating zero. When we write
* it out, we re-adjust it back. This is to avoid off-by-one errors
* in the code.
*
*****************************************************************************/
typedef union REMAPENTRY {
union {
DWORD dw; /* Accessed as a dword */
};
struct {
WORD wTo; /* Accessed as two words */
WORD wFrom;
};
} REMAPENTRY, *PREMAPENTRY;
#define MAX_REMAPENTRY (IDS_NUMKEYS+1)
typedef struct SCANCODEMAP {
DWORD dwVersion;
DWORD dwFlags;
DWORD dwNumRemaps;
REMAPENTRY rgRemap[MAX_REMAPENTRY];
} SCANCODEMAP, *PSCANCODEMAP;
#pragma BEGIN_CONST_DATA
TCHAR c_tszKeyboard[] = TEXT("SYSTEM\\CurrentControlSet\\Control\\")
TEXT("Keyboard Layout");
TCHAR c_tszMapping[] = TEXT("Scancode Map");
#pragma END_CONST_DATA
/*****************************************************************************
*
* rgwRemap
*
* Maps each key to its scancode. This must match the list of strings.
*
*****************************************************************************/
#pragma BEGIN_CONST_DATA
WORD rgwRemap[] = {
0x003A, // IDS_CAPSLOCK
0x001D, // IDS_LCTRL
0xE01D, // IDS_RCTRL
0x0038, // IDS_LALT
0xE038, // IDS_RALT
0x002A, // IDS_LSHIFT
0x0036, // IDS_RSHIFT
0xE05B, // IDS_LWIN
0xE05C, // IDS_RWIN
0xE05D, // IDS_APPS
};
#pragma END_CONST_DATA
/*****************************************************************************
*
* KEYMAPDATA
*
* Instance data for the property sheet.
*
*****************************************************************************/
typedef struct KEYMAPDATA {
SCANCODEMAP map; /* The mapping to apply */
int ilbFrom; /* What's in ID_FROM? */
int ilbTo; /* What's in ID_TO? */
} KMD, *PKMD;
#define pkmdHdlg(hdlg) (PKMD)GetWindowPointer(hdlg, DWLP_USER)
/*****************************************************************************
*
* MapPs_GetLbCurSel
*
* Get the current selection from a listbox.
*
*****************************************************************************/
int PASCAL
MapPs_GetLbCurSel(HWND hdlg, UINT idc)
{
return (int)SendDlgItemMessage(hdlg, idc, LB_GETCURSEL, 0, 0);
}
/*****************************************************************************
*
* MapPs_FindEntry
*
* Locate a mapping table entry, or -1 if not found.
*
*****************************************************************************/
int PASCAL
MapPs_FindEntry(PKMD pkmd, WORD wFrom)
{
DWORD iMap;
for (iMap = 0; iMap < pkmd->map.dwNumRemaps; iMap++) {
if (pkmd->map.rgRemap[iMap].wFrom == wFrom) {
return (int)iMap;
}
}
return -1;
}
/*****************************************************************************
*
* MapPs_WordToIndex
*
* Given a mapping in the form of a word (rgwRemap), convert it back
* to the index that it came from. This is the reverse of the rgwRemap
* array.
*
*****************************************************************************/
int PASCAL
MapPs_WordToIndex(WORD w)
{
int i;
for (i = 0; i < IDS_NUMKEYS; i++) {
if (rgwRemap[i] == w) {
return i;
}
}
return -1;
}
/*****************************************************************************
*
* MapPs_SaveCurSel
*
* Stash what's in the current selection.
*
*****************************************************************************/
void PASCAL
MapPs_SaveCurSel(HWND hdlg, PKMD pkmd)
{
int iTo = MapPs_GetLbCurSel(hdlg, IDC_TO);
int iMap;
WORD wFrom = rgwRemap[pkmd->ilbFrom];
WORD wTo = rgwRemap[iTo];
iMap = MapPs_FindEntry(pkmd, wFrom);
if (iMap < 0) {
/*
* Not found; must allocate. Note that we check against
* MAX_REMAPENTRY-1 because the trailing null eats one slot.
*/
if (pkmd->map.dwNumRemaps < MAX_REMAPENTRY - 1) {
iMap = (int)pkmd->map.dwNumRemaps++;
} else {
/*
* No room in the table. Oh well.
*/
return;
}
}
/*
* If the item is mapping to itself, then delete it entirely.
*/
if (wFrom == wTo) {
pkmd->map.dwNumRemaps--;
pkmd->map.rgRemap[iMap].dw =
pkmd->map.rgRemap[pkmd->map.dwNumRemaps].dw;
} else {
pkmd->map.rgRemap[iMap].wFrom = wFrom;
pkmd->map.rgRemap[iMap].wTo = wTo;
}
}
/*****************************************************************************
*
* MapPs_TrackSel
*
* Select the corresponding item in idcTo given what's in idcFrom.
*
*****************************************************************************/
void PASCAL
MapPs_TrackSel(HWND hdlg, PKMD pkmd)
{
int iFrom = pkmd->ilbFrom;
int iMap, iTo;
iMap = MapPs_FindEntry(pkmd, rgwRemap[iFrom]);
if (iMap >= 0) {
iTo = MapPs_WordToIndex(pkmd->map.rgRemap[iMap].wTo);
if (iTo < 0) {
/*
* Target not recognized; just map it to itself.
*/
iTo = iFrom;
}
} else {
/*
* Key not mapped. Therefore, it maps to itself.
*/
iTo = iFrom;
}
pkmd->ilbTo = iTo;
SendDlgItemMessage(hdlg, IDC_TO, LB_SETCURSEL, iTo, 0);
}
/*****************************************************************************
*
* MapPs_OnInitDialog
*
* Read the current scancode mapping and fill in the dialog box.
*
*****************************************************************************/
BOOL NEAR PASCAL
MapPs_OnInitDialog(HWND hdlg)
{
PKMD pkmd = LocalAlloc(LPTR, cbX(KMD));
HKEY hk;
LONG lRc;
DWORD dwDisp;
SetWindowPointer(hdlg, DWLP_USER, pkmd);
lRc = RegCreateKeyEx(HKEY_LOCAL_MACHINE, c_tszKeyboard, 0,
TEXT(""), REG_OPTION_NON_VOLATILE,
KEY_QUERY_VALUE | KEY_SET_VALUE,
NULL, &hk, &dwDisp);
if (lRc == ERROR_SUCCESS) {
DWORD dwType;
DWORD cb;
int dids;
cb = sizeof(pkmd->map);
lRc = RegQueryValueEx(hk, c_tszMapping, NULL, &dwType,
(LPBYTE)&pkmd->map, &cb);
RegCloseKey(hk);
/*
* Note that ERROR_MORE_DATA is an error here.
* But ERROR_FILE_NOT_FOUND is okay.
*/
if (lRc == ERROR_SUCCESS) {
/*
* Sanity-check all the data.
*/
if (
/* Must be binary data */
dwType == REG_BINARY &&
/* Version zero */
pkmd->map.dwVersion == 0 &&
/* No flags */
pkmd->map.dwFlags == 0 &&
/* Sane number of remaps */
pkmd->map.dwNumRemaps > 0 &&
pkmd->map.dwNumRemaps <= MAX_REMAPENTRY &&
/* Structure is the correct size */
cb == (DWORD)FIELD_OFFSET(SCANCODEMAP,
rgRemap[pkmd->map.dwNumRemaps]) &&
/* Last remap must be zero */
pkmd->map.rgRemap[pkmd->map.dwNumRemaps - 1].dw == 0
) {
} else {
goto fail;
}
pkmd->map.dwNumRemaps--; /* Don't count the trailing null */
} else if (lRc == ERROR_FILE_NOT_FOUND) {
/*
* Set it up for a null mapping.
*/
ZeroMemory(&pkmd->map, sizeof(pkmd->map));
} else {
goto fail;
}
/*
* Now init the dialog items.
*/
for (dids = 0; dids < IDS_NUMKEYS; dids++) {
TCHAR tsz[256];
LoadString(g_hinst, IDS_KEYFIRST + dids, tsz, cA(tsz));
SendDlgItemMessage(hdlg, IDC_FROM,
LB_ADDSTRING, 0, (LPARAM)tsz);
SendDlgItemMessage(hdlg, IDC_TO,
LB_ADDSTRING, 0, (LPARAM)tsz);
}
} else {
fail:;
/*
* User does not have permission to remap keys, or the key
* contents aren't something we like. Gray the controls.
*/
EnableWindow(GetDlgItem(hdlg, IDC_TO), FALSE);
}
SendDlgItemMessage(hdlg, IDC_FROM, LB_SETCURSEL, 0, 0);
MapPs_TrackSel(hdlg, pkmd);
return 1;
}
/*****************************************************************************
*
* MapPs_OnSelChange
*
* Somebody changed a selection. Save the selection and set
* the new one.
*
*****************************************************************************/
void PASCAL
MapPs_OnSelChange(HWND hdlg, PKMD pkmd)
{
MapPs_SaveCurSel(hdlg, pkmd); /* Save it */
pkmd->ilbFrom = MapPs_GetLbCurSel(hdlg, IDC_FROM);
MapPs_TrackSel(hdlg, pkmd); /* And update for the new one */
}
/*****************************************************************************
*
* MapPs_OnCommand
*
* Ooh, we got a command.
*
*****************************************************************************/
BOOL PASCAL
MapPs_OnCommand(HWND hdlg, int id, UINT codeNotify)
{
PKMD pkmd = pkmdHdlg(hdlg);
switch (id) {
case IDC_FROM:
switch (codeNotify) {
case LBN_SELCHANGE:
MapPs_OnSelChange(hdlg, pkmd);
break;
}
break;
case IDC_TO:
switch (codeNotify) {
case LBN_SELCHANGE:
if (MapPs_GetLbCurSel(hdlg, IDC_TO) != pkmd->ilbTo) {
PropSheet_Changed(GetParent(hdlg), hdlg);
}
break;
}
break;
}
return 0;
}
/*****************************************************************************
*
* MapPs_Apply
*
* Write the changes to the registry and nudge the VxD. We might have
* to load the VxD if the user is playing with KeyRemap immediately
* after installing, without rebooting in the interim.
*
*****************************************************************************/
BOOL PASCAL
MapPs_Apply(HWND hdlg)
{
PKMD pkmd = pkmdHdlg(hdlg);
MapPs_SaveCurSel(hdlg, pkmd);
if (IsWindowEnabled(GetDlgItem(hdlg, IDC_TO))) {
LONG lRc;
HKEY hk;
lRc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, c_tszKeyboard, 0,
KEY_SET_VALUE, &hk);
if (lRc == ERROR_SUCCESS) {
DWORD cb;
/*
* Count the trailing null again. And make sure
* it's a trailing null!
*/
pkmd->map.rgRemap[pkmd->map.dwNumRemaps].dw = 0;
pkmd->map.dwNumRemaps++;
cb = (DWORD)FIELD_OFFSET(SCANCODEMAP,
rgRemap[pkmd->map.dwNumRemaps]);
lRc = RegSetValueEx(hk, c_tszMapping, 0, REG_BINARY,
(LPBYTE)&pkmd->map, cb);
pkmd->map.dwNumRemaps--;
RegCloseKey(hk);
}
if (lRc == ERROR_SUCCESS) {
PropSheet_RebootSystem(GetParent(hdlg));
}
}
return 1;
}
/*****************************************************************************
*
* MapPs_OnNotify
*
* Ooh, we got a notification.
*
*****************************************************************************/
BOOL PASCAL
MapPs_OnNotify(HWND hdlg, NMHDR FAR *pnm)
{
switch (pnm->code) {
case PSN_APPLY:
MapPs_Apply(hdlg);
break;
}
return 0;
}
/*****************************************************************************
*
* MapPs_OnDestroy
*
* Clean up.
*
*****************************************************************************/
BOOL PASCAL
MapPs_OnDestroy(HWND hdlg)
{
PKMD pkmd = pkmdHdlg(hdlg);
FreePv(pkmd);
return 1;
}
/*****************************************************************************
*
* MapPs_DlgProc
*
* Our property sheet dialog procedure.
*
*****************************************************************************/
INT_PTR CALLBACK
MapPs_DlgProc(HWND hdlg, UINT wm, WPARAM wParam, LPARAM lParam)
{
switch (wm) {
case WM_INITDIALOG:
return MapPs_OnInitDialog(hdlg);
case WM_COMMAND:
return MapPs_OnCommand(hdlg,
(int)GET_WM_COMMAND_ID(wParam, lParam),
(UINT)GET_WM_COMMAND_CMD(wParam, lParam));
case WM_NOTIFY:
return MapPs_OnNotify(hdlg, (NMHDR FAR *)lParam);
case WM_DESTROY:
return MapPs_OnDestroy(hdlg);
default: return 0; /* Unhandled */
}
return 1; /* Handled */
}