//----------------------------------------------------------------------------- // File: configwnd.cpp // // Desc: CConfigWnd is derived from CFlexWnd. It implements the top-level // UI window which all other windows are descendents of. // // Functionalities handled by CConfigWnd are device tabs, Reset, Ok, // and Cancel buttons. // // Copyright (C) 1999-2000 Microsoft Corporation. All Rights Reserved. //----------------------------------------------------------------------------- #include "common.hpp" LPCTSTR g_tszAppWindowName = _T("DINPUT Default Mapper UI"); const int WINDOW_WIDTH = 640; const int WINDOW_HEIGHT = 480; const int TABTEXTMARGINLEFT = 7; const int TABTEXTMARGINTOP = 3; const int TABTEXTMARGINRIGHT = 7; const int TABTEXTMARGINBOTTOM = 4; const int BUTTONTEXTMARGINLEFT = 7; const int BUTTONTEXTMARGINTOP = 3; const int BUTTONTEXTMARGINRIGHT = 7; const int BUTTONTEXTMARGINBOTTOM = 4; const int BARBUTTONMARGINLEFT = 9; const int BARBUTTONMARGINTOP = 4; const int BARBUTTONMARGINRIGHT = 9; const int BARBUTTONMARGINBOTTOM = 5; const int BARBUTTONSPACING = 4; //#define WM_QUERYACTIONASSIGNEDANYWHERE (WM_USER + 4) CConfigWnd::CConfigWnd(CUIGlobals &uig) : m_uig(uig), m_bCreated(FALSE), m_pPageFactory(NULL), m_hPageFactoryInst(NULL), m_pSurface(NULL), m_pSurface3D(NULL), //@@BEGIN_MSINTERNAL #ifdef DDKBUILD m_bEditLayout(uig.QueryAllowEditLayout()), #endif //@@END_MSINTERNAL m_CurSel(-1), m_nCurGenre(0), m_pbmTopGradient(NULL), m_pbmBottomGradient(NULL), m_pbmPointerEraser(NULL), m_pbm3D(NULL), m_p3DBits(NULL), m_SurfFormat(D3DFMT_UNKNOWN), m_uiPixelSize(4), m_bBitmapsMapped(FALSE), m_lpDI(NULL), m_bScrollTabs(FALSE), m_bScrollTabsLeft(FALSE), m_bScrollTabsRight(FALSE), m_nLeftTab(0), m_dwInitFlags(0), m_bHourGlass(FALSE), m_bNeedRedraw(FALSE) { tracescope(__ts, _T("CConfigWnd::CConfigWnd()\n")); m_lpDI = m_uig.GetDI(); m_pSurface = m_uig.GetSurface(); m_pSurface3D = m_uig.GetSurface3D(); if (m_pSurface != NULL || m_pSurface3D != NULL) { if (m_pSurface != NULL && m_pSurface3D != NULL) { etrace(_T("Both Surface and Surface3D are non-NULL, will use only Surface3D\n")); m_pSurface->Release(); m_pSurface = NULL; assert(m_pSurface3D != NULL); assert(m_pSurface == NULL); } assert(m_pSurface != NULL || m_pSurface3D != NULL); assert(!(m_pSurface != NULL && m_pSurface3D != NULL)); m_bRender3D = (m_pSurface3D != NULL); SetRenderMode(); trace(_T("RenderMode set\n")); traceBOOL(m_bRender3D); if (m_bRender3D) Create3DBitmap(); HDC hDC = GetRenderDC(); if (hDC != NULL) { m_pbmPointerEraser = CBitmap::Create( GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), hDC); ReleaseRenderDC(hDC); } else etrace(_T("Failed to get Render DC")); } } CConfigWnd::~CConfigWnd() { tracescope(__ts, _T("CConfigWnd::~CConfigWnd()\n")); ClearList(); if (m_lpDI != NULL) m_lpDI->Release(); m_lpDI = NULL; if (m_pSurface != NULL) m_pSurface->Release(); m_pSurface = NULL; if (m_pSurface3D != NULL) m_pSurface3D->Release(); m_pSurface3D = NULL; if (m_pPageFactory != NULL) m_pPageFactory->Release(); m_pPageFactory = NULL; if (m_hPageFactoryInst != NULL) FreeLibrary(m_hPageFactoryInst); m_hPageFactoryInst = NULL; if (m_pbmPointerEraser != NULL) delete m_pbmPointerEraser; m_pbmPointerEraser = NULL; if (m_pbm3D != NULL) delete m_pbm3D; m_pbm3D = NULL; if (m_pbmTopGradient != NULL) delete m_pbmTopGradient; m_pbmTopGradient = NULL; if (m_pbmBottomGradient != NULL) delete m_pbmBottomGradient; m_pbmBottomGradient = NULL; } HWND CMouseTrap::Create(HWND hParent, BOOL bInRenderMode) { if (m_hWnd) return m_hWnd; m_hParent = hParent; int sx = GetSystemMetrics(SM_CXSCREEN); int sy = GetSystemMetrics(SM_CYSCREEN); RECT rect = {0, 0, sx, sy}; // If we are not in render mode, the trap window is exactly the same as the parent window if (!bInRenderMode) GetWindowRect(hParent, &rect); return CFlexWnd::Create( hParent, NULL, WS_EX_TOPMOST, WS_POPUP | WS_VISIBLE, rect); } BOOL CConfigWnd::Create(HWND hParent) { tracescope(__ts, _T("CConfigWnd::Create()\n")); traceHEX(hParent); HRESULT hresult = PrivGetClassObject(CLSID_CDIDeviceActionConfigPage, CLSCTX_INPROC_SERVER, NULL, IID_IClassFactory, (LPVOID*) &m_pPageFactory, &m_hPageFactoryInst); if (FAILED(hresult)) { // TODO: indicate failure to create page factory m_pPageFactory = NULL; m_hPageFactoryInst = NULL; etrace1(_T("Failed to create page classfactory, PrivGetClassObject() returned 0x%08x\n"), hresult); return FALSE; } int sx = GetSystemMetrics(SM_CXSCREEN); int sy = GetSystemMetrics(SM_CYSCREEN); int w = WINDOW_WIDTH; int h = WINDOW_HEIGHT; int rx = sx - w; int ry = sy - h; RECT rect = {rx / 2, ry / 2, 0, 0}; rect.right = rect.left + w; rect.bottom = rect.top + h; HWND hConfigParent = hParent; if (InRenderMode()) { hConfigParent = m_MouseTrap.Create(hParent, InRenderMode()); if (hConfigParent == NULL) hConfigParent = hParent; } HWND hRet = CFlexWnd::Create( hConfigParent, g_tszAppWindowName, 0, WS_POPUP | WS_VISIBLE | WS_CLIPCHILDREN, rect); if (hRet == NULL) etrace(_T("CFlexWnd::Create() failed!\n")); // Set the cursor extent to this window if we are in render mode (full-screen) if (InRenderMode()) { RECT rc; GetWindowRect(m_hWnd, &rc); ClipCursor(&rc); } return NULL != hRet; } void CConfigWnd::SetForegroundWindow() { // find the window HWND hWnd = FindWindow(GetDefaultClassName(), g_tszAppWindowName); // activate it if found if (NULL != hWnd) ::SetForegroundWindow(hWnd); } void CConfigWnd::OnPaint(HDC hDC) { if (hDC == NULL) return; SIZE topsize = GetRectSize(m_rectTopGradient); SIZE bottomsize = GetRectSize(m_rectBottomGradient); SIZE bsize = {max(topsize.cx, bottomsize.cx), max(topsize.cy, bottomsize.cy)}; CBitmap *pbm = CBitmap::Create(bsize, hDC); if (pbm == NULL) return; HDC hBDC = NULL, hODC = hDC; if (m_bHourGlass) { if (!InRenderMode()) { // If not in fullscreen mode, change cursor to hourglass HCURSOR hCursor; hCursor = LoadCursor(NULL, IDC_WAIT); SetCursor(hCursor); } else { // If in fullscreen mode, hide the cursor during reset process. SetCursor(NULL); } } hBDC = pbm->BeginPaintInto(hDC); if (hBDC == NULL) { delete pbm; return; } hDC = hBDC; if (m_pbmTopGradient != NULL) m_pbmTopGradient->Draw(hDC); { CPaintHelper ph(m_uig, hDC); ph.SetElement(UIE_BORDER); ph.MoveTo(0, m_rectTopGradient.bottom - 1); ph.LineTo(WINDOW_WIDTH, m_rectTopGradient.bottom - 1); int i; for (i = 0; i < GetNumElements(); i++) { const ELEMENT &e = GetElement(i); BOOL bSel = i == m_CurSel; ph.SetElement(bSel ? UIE_SELTAB : UIE_TAB); ph.Rectangle(e.rect); RECT trect = e.textrect; DrawText(hDC, e.tszCaption, -1, &trect, DT_NOCLIP | DT_NOPREFIX); if (bSel) { ph.SetPen(UIP_BLACK); ph.MoveTo(e.rect.left + 1, e.rect.bottom - 1); ph.LineTo(e.rect.right - 1, e.rect.bottom - 1); } } if (m_bScrollTabs && GetNumElements() > 0) { ph.SetElement(UIE_TABARROW); const ELEMENT &e = GetElement(0); int h = e.rect.bottom - e.rect.top; for (i = 0; i < 2; i++) { RECT &rect = i == 0 ? m_rectSTRight : m_rectSTLeft; BOOL bDraw = i ? m_bScrollTabsLeft : m_bScrollTabsRight; ph.Rectangle(rect); if (!bDraw) continue; int d,l,r,m,t,b, f = !i, w; w = rect.right - rect.left; l = f ? w / 4 : 3 * w / 8; r = f ? 5 * w / 8 : 3 * w / 4; d = r - l; m = w / 2; t = m - d; b = m + d; l += rect.left; r += rect.left; POINT p[4]; p[3].x = p[0].x = f ? l : r; p[2].x = p[1].x = f ? r : l; p[3].y = p[0].y = m; p[1].y = t; p[2].y = b; Polyline(hDC, p, 4); } } } pbm->Draw(hODC, topsize); m_pbmBottomGradient->Draw(hDC); { CPaintHelper ph(m_uig, hDC); ph.SetElement(UIE_BORDER); Rectangle(hDC, 0, -1, WINDOW_WIDTH, GetRectSize(m_rectBottomGradient).cy); for (int i = 0; i < NUMBUTTONS; i++) { BOOL bOkOnly = !m_uig.InEditMode(); const BUTTON &b = m_Button[i]; //@@BEGIN_MSINTERNAL #ifdef DDKBUILD BOOL bLayoutButton = i == BUTTON_LAYOUT; if (!m_uig.QueryAllowEditLayout() && bLayoutButton) continue; #endif //@@END_MSINTERNAL if ( bOkOnly && i != BUTTON_CANCEL //@@BEGIN_MSINTERNAL #ifdef DDKBUILD && !bLayoutButton #endif //@@END_MSINTERNAL ) continue; if (i == BUTTON_OK || bOkOnly) ph.SetElement(UIE_DEFBUTTON); else ph.SetElement(UIE_BUTTON); int ay = m_rectBottomGradient.top; ph.Rectangle(b.rect.left, b.rect.top - ay, b.rect.right, b.rect.bottom - ay); RECT trect = b.textrect; OffsetRect(&trect, 0, -ay); DrawText(hDC, b.tszCaption, -1, &trect, DT_NOCLIP | DT_NOPREFIX); } } pbm->Draw(hODC, m_rectBottomGradient.left, m_rectBottomGradient.top, bottomsize); pbm->EndPaintInto(hBDC); delete pbm; hDC = hODC; { CPaintHelper ph(m_uig, hDC); ph.SetElement(UIE_BORDER); ph.MoveTo(0, m_rectTopGradient.bottom); ph.LineTo(0, m_rectBottomGradient.top); ph.MoveTo(WINDOW_WIDTH - 1, m_rectTopGradient.bottom); ph.LineTo(WINDOW_WIDTH - 1, m_rectBottomGradient.top); } } void CConfigWnd::OnClick(POINT point, WPARAM fwKeys, BOOL bLeft) { int i; // Un-highlight the current callout SendMessage(CFlexWnd::s_CurrPageHwnd, WM_UNHIGHLIGHT, 0, 0); // check scroll tab buttons if (m_bScrollTabs) for (i = 0; i < 2; i++) { RECT &r = !i ? m_rectSTRight : m_rectSTLeft; BOOL b = !i ? m_bScrollTabsRight : m_bScrollTabsLeft; if (PtInRect(&r, point)) { if (b) ScrollTabs(!i ? -1 : 1); return; } } // check tabs for (i = 0; i < GetNumElements(); i++) if (PtInRect(&(GetElement(i).rect), point)) { // Check if the tab is partially obscured. If so we scroll the tab so it becomes completely visible. POINT pt = {m_rectSTLeft.left, m_rectSTLeft.top}; if (m_bScrollTabsRight || m_bScrollTabsLeft) { while (PtInRect(&(GetElement(i).rect), pt)) ScrollTabs(1); } SelTab(i); return; } // check buttons for (i = 0; i < NUMBUTTONS; i++) if (PtInRect(&(m_Button[i].rect), point)) { FireButton(i); return; } } void CConfigWnd::ScrollTabs(int by) { m_nLeftTab += by; if (m_nLeftTab < 0) m_nLeftTab = 0; if (m_nLeftTab >= GetNumElements()) m_nLeftTab = GetNumElements() - 1; CalcTabs(); Invalidate(); } void CConfigWnd::OnDestroy() { tracescope(__ts, _T("CConfigWnd::OnDestroy()\n")); ClipCursor(NULL); // Set cursor extent to entire desktop. if (m_bCreated) PostQuitMessage(EXIT_SUCCESS); } LRESULT CConfigWnd::OnCreate(LPCREATESTRUCT lpCreateStruct) { tracescope(__ts, _T("CConfigWnd::OnCreate()\n")); if (!Init()) { etrace(_T("CConfigWnd::Init() failed\n")); return -1; } else m_bCreated = TRUE; return 0; } BOOL CALLBACK EnumDeviceCallback(const DIDEVICEINSTANCEW *lpdidi, LPDIRECTINPUTDEVICE8W pdiDev8W, DWORD dwFlags, DWORD dwDeviceRemaining, LPVOID pvRef) { if (pvRef != NULL) return ((CConfigWnd *)pvRef)->EnumDeviceCallback(lpdidi); else return DIENUM_STOP; } BOOL CConfigWnd::EnumDeviceCallback(const DIDEVICEINSTANCEW *lpdidi) { DIDEVICEINSTANCEW didi; didi.dwSize = sizeof(DIDEVICEINSTANCEW); didi.guidInstance = lpdidi->guidInstance; didi.guidProduct = lpdidi->guidProduct; didi.dwDevType = lpdidi->dwDevType; CopyStr(didi.tszInstanceName, lpdidi->tszInstanceName, MAX_PATH); CopyStr(didi.tszProductName, lpdidi->tszProductName, MAX_PATH); didi.guidFFDriver = lpdidi->guidFFDriver; didi.wUsagePage = lpdidi->wUsagePage; didi.wUsage = lpdidi->wUsage; AddToList(&didi); return DIENUM_CONTINUE; } // show any error message here if returning false BOOL CConfigWnd::Init(DWORD dwInitFlags) { tracescope(__ts, _T("CConfigWnd::Init()\n")); HRESULT hr = S_OK; BOOL bReInit = !!(dwInitFlags & CFGWND_INIT_REINIT); m_dwInitFlags = dwInitFlags; SetOnFunctionExit _set_m_dwInitFlags(m_dwInitFlags, 0); // make sure we have DI assert(m_lpDI != NULL); if (m_lpDI == NULL) { etrace(_T("NULL m_lpDI\n")); return FALSE; } if (!(dwInitFlags & CFGWND_INIT_RESET)) { // If we are not doing reset, clear device list then re-enumerate and rebuild. // clear list ClearList(); // enum devices { tracescope(ts, _T("Enumerating Devices...\n\n")); DWORD dwFlags = DIEDBSFL_ATTACHEDONLY; hr = m_lpDI->EnumDevicesBySemantics(NULL, (LPDIACTIONFORMATW)&m_uig.RefMasterAcFor(m_nCurGenre), ::EnumDeviceCallback, (LPVOID)this, dwFlags); trace(_T("\n")); } } else { DIDEVICEINSTANCEW didiCopy; // Saves a copy of device instance as the current ELEMENT will be freed by AddToList(). CopyMemory(&didiCopy, &GetElement(m_CurSel).didi, sizeof(didiCopy)); // If resetting, call AddToList with bReset as TRUE to just get default mappings. AddToList(&didiCopy, TRUE); } // handle potential enum failure if (FAILED(hr)) { etrace1(_T("EnumDevicesBySemantics() failed, returning 0x%08x\n"), hr); return FALSE; } // if there are no elements, fail if (GetNumElements() < 1) { etrace(_T("No devices\n")); return FALSE; } // calculate tabs, buttons, init gradients CalcTabs(); if (!bReInit) { CalcButtons(); InitGradients(); // set the timer if (InRenderMode()) { if (g_fptimeSetEvent) g_fptimeSetEvent(20, 20, CConfigWnd::TimerProc, (DWORD_PTR)m_hWnd, TIME_ONESHOT); Render(); } } // make sure all the pages are in the right place PlacePages(); // show the first page if we are not resetting. Show current page if we are. int CurSel = (dwInitFlags & CFGWND_INIT_RESET) ? m_CurSel : 0; m_CurSel = -1; SelTab(CurSel); // if we're already editting the layout, set it. // KLUDGE, set false and toggle to set if (m_bEditLayout) { m_bEditLayout = FALSE; ToggleLayoutEditting(); } trace(_T("\n")); return TRUE; } // This is called once for each device that will get configured. int CConfigWnd::AddToList(const DIDEVICEINSTANCEW *lpdidi, BOOL bReset) { if (lpdidi == NULL) { etrace(_T("NULL lpdidi")); assert(0); return GetNumElements(); } int i; tracescope(ts, _T("Adding Device ")); trace(QSAFESTR(lpdidi->tszInstanceName)); trace(_T("\n\n")); // add an element and get it if we are not doing reset (adding new device) if (!bReset) { i = GetNumElements(); m_Element.SetSize(i + 1); } else { i = m_CurSel; ClearElement(m_CurSel, bReset); // If resetting, clear the current ELEMENT as we will populate it below. } // If we are doing reset, then we use the existing ELEMENT that this device is already using. ELEMENT &e = bReset ? GetElement(m_CurSel) : GetElement(i); // set various needed variables e.didi = *lpdidi; e.bCalc = FALSE; e.pUIGlobals = &m_uig; // create and set the device if (m_lpDI == NULL) { e.lpDID = NULL; etrace(_T("m_lpDI NULL! Can't create this device.\n")); } else { e.lpDID = CreateDevice(e.didi.guidInstance); if (!e.lpDID) etrace(_T("Failed to create device!\n")); } if (!bReset) { // Find owner of device only if we are not doing reset. // set starting current user index DIPROPSTRING dips; dips.diph.dwSize = sizeof(DIPROPSTRING); dips.diph.dwHeaderSize = sizeof(DIPROPHEADER); dips.diph.dwObj = DIPH_DEVICE; dips.diph.dwHow = 0; CopyStr(dips.wsz, "", MAX_PATH); if (!e.lpDID) { etrace(_T("no lpDID, assuming device unassigned\n")); e.nCurUser = -1; } //@@BEGIN_MSINTERNAL #ifdef DDKBUILD else if (m_uig.QueryAllowEditLayout()) { trace(_T("In DDK mode. Set user to 0 automatically.\n")); e.nCurUser = 0; } #endif //@@END_MSINTERNAL else { HRESULT hr = e.lpDID->GetProperty(DIPROP_USERNAME, (LPDIPROPHEADER)&dips); e.nCurUser = -1; // unassigned unless getusernameindex below works if (FAILED(hr)) etrace(_T("GetProperty(DIPROP_USERNAME,...) failed\n")); else if (hr == S_FALSE) trace(_T("GetProperty(DIPROP_USERNAME,...) returned S_FALSE\n")); else if (StrLen(dips.wsz) < 1) trace(_T("GetProperty(DIPROP_USERNAME,...) returned empty string\n")); else { trace(_T("Getting user name index for ")); traceWSTR(dips.wsz); e.nCurUser = m_uig.GetUserNameIndex(dips.wsz); trace(_T("Result: ")); traceLONG(e.nCurUser); if (e.nCurUser == -1) etrace(_T("Device assigned to user not passed to ConfigureDevices()\nConsidering unassigned now\n")); } } } // create and set the page object HWND hwndChild = NULL; e.pPage = CreatePageObject(i, e, hwndChild); if (e.pPage == NULL) etrace(_T("Failed to create page object!\n")); e.hWnd = hwndChild; if (e.hWnd == NULL) etrace(_T("CreatePageObject() returned NULL hwnd!\n")); // create/test the first acfor for this device with cur genre/user traceLONG(m_nCurGenre); traceLONG(e.nCurUser); LPDIACTIONFORMATW lpAcFor = NULL; if (e.nCurUser != -1) { lpAcFor = e.GetAcFor(m_nCurGenre, e.nCurUser, bReset); if (lpAcFor != NULL) TraceActionFormat(_T("Starting Device ActionFormat:"), *lpAcFor); else etrace(_T("Failed to create starting ActionFormat\n")); } else trace(_T("Device unassigned\n")); // check if anything was unsuccessful if ((lpAcFor == NULL && e.nCurUser != -1) || e.lpDID == NULL || e.pPage == NULL || e.hWnd == NULL) { // clear what was successful, set the size back (remove element), // and indicate error ClearElement(e); m_Element.SetSize(i); etrace(_T("Can't add this device - Element removed\n")); } trace(_T("\n")); return GetNumElements(); } LPDIRECTINPUTDEVICE8W CConfigWnd::CreateDevice(GUID &guid) { LPDIRECTINPUTDEVICE8W lpDID; HRESULT hr = m_lpDI->CreateDevice(guid, &lpDID, NULL); if (FAILED(hr) || lpDID == NULL) { etrace2(_T("Could not create device (guid %s), CreateDevice() returned 0x%08x\n"), GUIDSTR(guid), hr); return NULL; } return lpDID; } void CConfigWnd::ClearElement(int i, BOOL bReset) { ELEMENT &e = GetElement(i); ClearElement(e, bReset); } void CConfigWnd::ClearElement(ELEMENT &e, BOOL bReset) { if (e.pPage != NULL) DestroyPageObject(e.pPage); if (e.lpDID != NULL) { e.lpDID->Release(); e.lpDID = NULL; } e.pPage = NULL; e.lpDID = NULL; e.hWnd = NULL; e.pUIGlobals = NULL; // not freed if (!bReset) // Free map only if we are not resetting (delete permanently). e.FreeMap(); } void CConfigWnd::ClearList() { int i; for (i = 0; i < GetNumElements(); i++) ClearElement(i); m_Element.RemoveAll(); assert(!GetNumElements()); } void CConfigWnd::PlacePages() { RECT rect; GetPageRect(rect); for (int i = 0; i < GetNumElements(); i++) { DWORD flags = SWP_NOZORDER | SWP_NOACTIVATE; SetWindowPos(GetElement(i).hWnd, NULL, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, flags); } } SIZE CConfigWnd::GetTextSize(LPCTSTR tszText) { RECT trect = {0, 0, 1, 1}; HDC hDC = CreateCompatibleDC(NULL); if (hDC != NULL) { { CPaintHelper ph(m_uig, hDC); ph.SetFont(UIF_FRAME); DrawText(hDC, tszText, -1, &trect, DT_CALCRECT | DT_NOPREFIX); } DeleteDC(hDC); } SIZE size = {trect.right - trect.left, trect.bottom - trect.top}; return size; } void CConfigWnd::InitGradients() { if (m_pbmTopGradient == NULL) m_pbmTopGradient = CBitmap::CreateHorzGradient(m_rectTopGradient, m_uig.GetColor(UIC_CONTROLFILL), m_uig.GetColor(UIC_CONTROLFILL)); if (m_pbmBottomGradient == NULL) m_pbmBottomGradient = CBitmap::CreateHorzGradient(m_rectBottomGradient, m_uig.GetColor(UIC_CONTROLFILL), m_uig.GetColor(UIC_CONTROLFILL)); } void CConfigWnd::CalcTabs() { int i, maxh = 0, lastx = 0; for (i = 0; i < GetNumElements(); i++) { ELEMENT &e = GetElement(i); CopyStr(e.tszCaption, e.didi.tszInstanceName, MAX_PATH); e.rect.left = i > 0 ? GetElement(i - 1).rect.right - 1 : 0; e.rect.top = 0; SIZE tsize = GetTextSize(e.tszCaption); e.textrect.left = e.textrect.top = 0; e.textrect.right = tsize.cx; e.textrect.bottom = tsize.cy; OffsetRect(&e.textrect, e.rect.left + TABTEXTMARGINLEFT, e.rect.top + TABTEXTMARGINTOP); int w = tsize.cx; int h = tsize.cy; e.rect.right = e.rect.left + TABTEXTMARGINLEFT + w + TABTEXTMARGINRIGHT + 1; e.rect.bottom = e.rect.top + TABTEXTMARGINTOP + h + TABTEXTMARGINBOTTOM; h = e.rect.bottom - e.rect.top; if (h > maxh) maxh = h; e.bCalc = TRUE; } for (i = 0; i < GetNumElements(); i++) { ELEMENT &e = GetElement(i); e.rect.bottom = e.rect.top + maxh; lastx = e.rect.right; } if (lastx > WINDOW_WIDTH) { if (!m_bScrollTabs) m_nLeftTab = 0; m_bScrollTabs = TRUE; } else { m_bScrollTabs = FALSE; m_nLeftTab = 0; } int cutoff = WINDOW_WIDTH; if (m_bScrollTabs) { cutoff = WINDOW_WIDTH - maxh * 2; RECT r = {WINDOW_WIDTH - maxh, 0, WINDOW_WIDTH, maxh}; m_rectSTLeft = r; OffsetRect(&r, -(maxh - 1), 0); m_rectSTRight = r; } if (m_bScrollTabs && m_nLeftTab > 0) { int left = GetElement(m_nLeftTab).rect.left, right = 0; for (i = 0; i < GetNumElements(); i++) { ELEMENT &e = GetElement(i); OffsetRect(&e.rect, -left, 0); OffsetRect(&e.textrect, -left, 0); if (e.rect.right > right) right = e.rect.right; } lastx = right; } if (m_bScrollTabs) { m_bScrollTabsLeft = lastx > cutoff && m_nLeftTab < GetNumElements() - 1; m_bScrollTabsRight = m_nLeftTab > 0; } RECT t = {0/*lastx*/, 0, WINDOW_WIDTH, maxh}; m_rectTopGradient = t; } void CConfigWnd::CalcButtons() { SIZE max = {0, 0}; int i; for (i = 0; i < NUMBUTTONS; i++) { BUTTON &b = m_Button[i]; if (!StrLen(b.tszCaption)) { switch (i) { case BUTTON_RESET: LoadString(g_hModule, IDS_BUTTON_RESET, b.tszCaption, MAX_PATH); break; //@@BEGIN_MSINTERNAL #ifdef DDKBUILD case BUTTON_LAYOUT: LoadString(g_hModule, IDS_BUTTON_LAYOUT, b.tszCaption, MAX_PATH); break; #endif //@@END_MSINTERNAL case BUTTON_CANCEL: if (m_uig.InEditMode()) { LoadString(g_hModule, IDS_BUTTON_CANCEL, b.tszCaption, MAX_PATH); break; } // else, intentional fallthrough case BUTTON_OK: LoadString(g_hModule, IDS_BUTTON_OK, b.tszCaption, MAX_PATH); break; } } b.textsize = GetTextSize(b.tszCaption); if (b.textsize.cx > max.cx) max.cx = b.textsize.cx; if (b.textsize.cy > max.cy) max.cy = b.textsize.cy; } max.cx += BUTTONTEXTMARGINLEFT + BUTTONTEXTMARGINRIGHT; max.cy += BUTTONTEXTMARGINTOP + BUTTONTEXTMARGINBOTTOM; m_rectBottomGradient.bottom = WINDOW_HEIGHT; m_rectBottomGradient.top = m_rectBottomGradient.bottom - max.cy - BARBUTTONMARGINTOP - BARBUTTONMARGINBOTTOM; m_rectBottomGradient.left = 0; m_rectBottomGradient.right = WINDOW_WIDTH; for (i = 0; i < NUMBUTTONS; i++) { BUTTON &b = m_Button[i]; RECT z = {0,0,0,0}; b.rect = z; b.rect.right = max.cx; b.rect.bottom = max.cy; int by = m_rectBottomGradient.top + BARBUTTONMARGINTOP; switch (i) { case BUTTON_RESET: OffsetRect(&b.rect, BARBUTTONMARGINLEFT, by); break; //@@BEGIN_MSINTERNAL #ifdef DDKBUILD case BUTTON_LAYOUT: OffsetRect(&b.rect, WINDOW_WIDTH / 2 - max.cx / 2, by); break; #endif //@@END_MSINTERNAL case BUTTON_CANCEL: OffsetRect(&b.rect, m_rectBottomGradient.right - BARBUTTONMARGINRIGHT - max.cx, by); break; case BUTTON_OK: OffsetRect(&b.rect, m_rectBottomGradient.right - BARBUTTONMARGINRIGHT - max.cx - max.cx - BARBUTTONSPACING, by); break; } POINT m = {(b.rect.right + b.rect.left) / 2, (b.rect.bottom + b.rect.top) / 2}; b.textrect.left = m.x - b.textsize.cx / 2; b.textrect.top = m.y - b.textsize.cy / 2; b.textrect.right = b.textrect.left + b.textsize.cx; b.textrect.bottom = b.textrect.top + b.textsize.cy; } } void CConfigWnd::GetPageRect(RECT &rect, BOOL bTemp) { if (bTemp) { rect.left = 1; rect.right = WINDOW_WIDTH - 1; rect.top = 40; rect.bottom = WINDOW_HEIGHT - 40; } else { rect.left = 1; rect.right = WINDOW_WIDTH - 1; rect.top = m_rectTopGradient.bottom; rect.bottom = m_rectBottomGradient.top; } } void CConfigWnd::ToggleLayoutEditting() { m_bEditLayout = !m_bEditLayout; for (int i = 0; i < GetNumElements(); i++) { ELEMENT &e = GetElement(i); if (e.pPage) e.pPage->SetEditLayout(m_bEditLayout); } } void CConfigWnd::FireButton(int b) { switch(b) { case BUTTON_OK: if (!m_uig.InEditMode()) break; // If not in edit mode, Ok button doesn't not exist so we shouldn't do anything. //@@BEGIN_MSINTERNAL #ifdef DDKBUILD if (m_uig.QueryAllowEditLayout()) { int MsgBoxRet = MessageBox(m_hWnd, _T("Do you wish to save layout information?"), _T("Save"), MB_YESNOCANCEL); if (MsgBoxRet == IDYES) { // Write IHV settings for all devices. for (int i = 0; i < GetNumElements(); i++) GetElement(i).pPage->WriteIHVSetting(); } else if (MsgBoxRet == IDCANCEL) break; } else #endif //@@END_MSINTERNAL Apply(); // If we are in Edit Layout mode, do not call Apply() to save to user setting. // intentional fallthrough case BUTTON_CANCEL: Destroy(); break; case BUTTON_RESET: if (m_uig.InEditMode()) // Only reset if in edit mode. Do nothing in view mode. Reset(); break; //@@BEGIN_MSINTERNAL #ifdef DDKBUILD case BUTTON_LAYOUT: if (m_uig.QueryAllowEditLayout()) ToggleLayoutEditting(); break; #endif //@@END_MSINTERNAL default: assert(0); break; } } void CConfigWnd::SelTab(int i) { if (i >= 0 && i < GetNumElements()) { if (i == m_CurSel) return; ShowPage(i); HidePage(m_CurSel); m_CurSel = i; Invalidate(); } } PAGETYPE *CConfigWnd::CreatePageObject(int nPage, const ELEMENT &e, HWND &refhChildWnd) { if (m_pPageFactory == NULL) return NULL; PAGETYPE *pPage = NULL; HRESULT hresult = m_pPageFactory->CreateInstance(NULL, IID_IDIDeviceActionConfigPage, (LPVOID*) &pPage); if (FAILED(hresult) || pPage == NULL) return NULL; DICFGPAGECREATESTRUCT cs; cs.dwSize = sizeof(DICFGPAGECREATESTRUCT); cs.nPage = nPage; cs.hParentWnd = m_hWnd; GetPageRect(cs.rect, TRUE); cs.hPageWnd = NULL; cs.didi = e.didi; cs.lpDID = e.lpDID; cs.pUIGlobals = &m_uig; cs.pUIFrame = dynamic_cast(this); hresult = pPage->Create(&cs); if (FAILED(hresult)) { etrace1(_T("pPage->Create() failed, returning 0x%08x\n"), hresult); pPage->Release(); return NULL; } refhChildWnd = cs.hPageWnd; return pPage; } void CConfigWnd::DestroyPageObject(PAGETYPE *&pPage) { if (pPage != NULL) pPage->Release(); pPage = NULL; } void CConfigWnd::ShowPage(int i) { if (i == -1) return; if (i < 0 || i >= GetNumElements()) { assert(0); return; } ELEMENT &e = GetElement(i); PAGETYPE *pPage = e.pPage; if (pPage == NULL) { assert(0); return; } pPage->Show(e.GetAcFor(m_nCurGenre, e.nCurUser)); } void CConfigWnd::HidePage(int i) { if (i == -1) return; if (i < 0 || i >= GetNumElements()) { assert(0); return; } PAGETYPE *pPage = GetElement(i).pPage; if (pPage == NULL) { assert(0); return; } pPage->Hide(); } void CConfigWnd::OnMouseOver(POINT point, WPARAM fwKeys) { int i; CFlexWnd::s_ToolTip.SetEnable(FALSE); // check scroll tab buttons if (m_bScrollTabs) for (i = 0; i < 2; i++) { RECT &r = !i ? m_rectSTRight : m_rectSTLeft; BOOL b = !i ? m_bScrollTabsRight : m_bScrollTabsLeft; if (PtInRect(&r, point)) { if (b) GetElement(m_CurSel).pPage->SetInfoText(m_uig.InEditMode() ? IDS_INFOMSG_EDIT_TABSCROLL : IDS_INFOMSG_VIEW_TABSCROLL); return; } } // check tabs for (i = 0; i < GetNumElements(); i++) if (PtInRect(&(GetElement(i).rect), point)) { GetElement(m_CurSel).pPage->SetInfoText(m_uig.InEditMode() ? IDS_INFOMSG_EDIT_TAB : IDS_INFOMSG_VIEW_TAB); return; } // check buttons for (i = 0; i < NUMBUTTONS; i++) if (PtInRect(&(m_Button[i].rect), point)) { switch(i) { case BUTTON_OK: if (m_uig.InEditMode()) GetElement(m_CurSel).pPage->SetInfoText(IDS_INFOMSG_EDIT_OK); break; case BUTTON_CANCEL: if (m_uig.InEditMode()) GetElement(m_CurSel).pPage->SetInfoText(IDS_INFOMSG_EDIT_CANCEL); else GetElement(m_CurSel).pPage->SetInfoText(IDS_INFOMSG_VIEW_OK); break; case BUTTON_RESET: if (m_uig.InEditMode()) // Only reset if in edit mode. Do nothing in view mode. GetElement(m_CurSel).pPage->SetInfoText(IDS_INFOMSG_EDIT_RESET); break; } return; } GetElement(m_CurSel).pPage->SetInfoText(-1); } void CALLBACK CConfigWnd::TimerProc(UINT uID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2) { if (!IsWindow((HWND)dwUser)) return; // Verify that dwUser is a valid window handle CConfigWnd *pCfgWnd = (CConfigWnd *)GetFlexWnd((HWND)dwUser); // Get flex object // We use PostMessage instead of calling Render() so we stay synchronized. PostMessage((HWND)dwUser, WM_DIRENDER, 0, 0); } void CConfigWnd::MapBitmaps(HDC hDC) { if (m_bBitmapsMapped) return; if (m_pbmTopGradient) m_pbmTopGradient->MapToDC(hDC); if (m_pbmBottomGradient) m_pbmBottomGradient->MapToDC(hDC); m_bBitmapsMapped = TRUE; } LPDIACTIONFORMATW CConfigWnd::GetCurAcFor(ELEMENT &e) { return e.GetAcFor(m_nCurGenre, e.nCurUser); } BOOL CConfigWnd::IsActionAssignedAnywhere(GUID GuidInstance, int nActionIndex) { // Find out which user owns the device in question first. int nUser = 0; for (int ii = 0; ii < GetNumElements(); ii++) { ELEMENT &e = GetElement(ii); if (IsEqualGUID(e.didi.guidInstance, GuidInstance)) { nUser = e.nCurUser; break; } } // Now we check the actions against this user. for (int i = 0; i < GetNumElements(); i++) { ELEMENT &e = GetElement(i); const LPDIACTIONFORMATW &lpAcFor = e.GetAcFor(m_nCurGenre, nUser); if (lpAcFor == NULL) continue; if (nActionIndex < 0 || nActionIndex > int(lpAcFor->dwNumActions)) continue; // If this device is not owned by this user, don't need to check. if (e.nCurUser != nUser) continue; const DIACTIONW &a = lpAcFor->rgoAction[nActionIndex]; if (!IsEqualGUID(a.guidInstance, GUID_NULL)) return TRUE; } return FALSE; } LRESULT CConfigWnd::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_ACTIVATE: switch(wParam) { case WA_ACTIVE: case WA_CLICKACTIVE: // Set the cursor extent to this window if we are in render mode because the // cursor can't be drawn by us when it's not above us. if (InRenderMode()) { RECT rc; GetWindowRect(m_hWnd, &rc); ClipCursor(&rc); } // Reacquire current device if (GetNumElements() && m_CurSel >= 0) GetElement(m_CurSel).pPage->Reacquire(); break; case WA_INACTIVE: // Unacquire current device if (GetNumElements() && m_CurSel >= 0) GetElement(m_CurSel).pPage->Unacquire(); break; } break; case WM_DIRENDER: // Render message, sent by TimerProc() earlier. // The timer proc has request a render operation. Render(m_bNeedRedraw); // Set the next timer event. if (g_fptimeSetEvent) g_fptimeSetEvent(20, 20, CConfigWnd::TimerProc, (DWORD_PTR)m_hWnd, TIME_ONESHOT); return 0; case WM_SETFOCUS: // Set the keyboard focus to the current page window. ShowPage(m_CurSel); // Call Show() on current page so it can get keyboard focus. return 0; // WM_NCHITTEST handler is added to support moving window when in GDI mode. case WM_NCHITTEST: { if (InRenderMode()) break; BOOL bHitCaption = TRUE; POINT point = {(short)LOWORD(lParam), (short)HIWORD(lParam)}; int i; ScreenToClient(m_hWnd, &point); // check scroll tab buttons if (m_bScrollTabs) for (i = 0; i < 2; i++) { RECT &r = !i ? m_rectSTRight : m_rectSTLeft; BOOL b = !i ? m_bScrollTabsRight : m_bScrollTabsLeft; if (PtInRect(&r, point)) { if (b) bHitCaption = FALSE; break; } } // check tabs for (i = 0; i < GetNumElements(); i++) if (PtInRect(&(GetElement(i).rect), point)) { bHitCaption = FALSE; break; } // check buttons for (i = 0; i < NUMBUTTONS; i++) if (PtInRect(&(m_Button[i].rect), point)) { if ((i == BUTTON_RESET || i == BUTTON_OK) && !m_uig.InEditMode()) continue; //@@BEGIN_MSINTERNAL #ifdef DDKBUILD if (i == BUTTON_LAYOUT && !m_uig.QueryAllowEditLayout()) continue; #endif //@@END_MSINTERNAL bHitCaption = FALSE; break; } // Check Y coordinate to see if it is within the caption bar. if ((point.y < GetElement(0).rect.top || point.y > GetElement(0).rect.bottom) && (point.y < m_rectBottomGradient.top || point.y > m_rectBottomGradient.bottom)) bHitCaption = FALSE; if (bHitCaption) { // If we are returning HTCAPTION, clear the page's info box. GetElement(m_CurSel).pPage->SetInfoText(-1); return HTCAPTION; } break; } case WM_CFGUIRESET: { CFlexWnd::s_ToolTip.SetEnable(FALSE); m_bHourGlass = TRUE; // Set the flag so Render() will draw hourglass instead of arrow Invalidate(); SendMessage(this->m_hWnd, WM_PAINT, 0, 0); if (InRenderMode()) // If in render mode, need to specifically call OnRender as sending WM_PAINT merely changes flag. Render(TRUE); if (!Init(CFGWND_INIT_REINIT | CFGWND_INIT_RESET)) { m_uig.SetFinalResult(E_FAIL); Destroy(); } m_bHourGlass = FALSE; // Change cursor back to arrow m_MsgBox.Destroy(); Invalidate(); return TRUE; } case WM_SETCURSOR: { static HCURSOR hCursor = LoadCursor(NULL, IDC_ARROW); ::SetCursor(InRenderMode() ? NULL : hCursor); } return TRUE; // case WM_QUERYACTIONASSIGNEDANYWHERE: // return IsActionAssignedAnywhere(int(wParam), int(lParam)); } return CFlexWnd::WndProc(hWnd, msg, wParam, lParam); } HRESULT CConfigWnd::Apply() { tracescope(ts, _T("\n\nApplying Changes to All Devices...\n")); // Devices need to be in the unaquired state when SetActionMap is called. Unacquire(); for (int i = 0; i < GetNumElements(); i++) GetElement(i).Apply(); Reacquire(); trace(_T("\n\n")); return S_OK; } int CConfigWnd::GetNumElements() { return m_Element.GetSize(); } ELEMENT &CConfigWnd::GetElement(int i) { if (i < 0 || i >= GetNumElements()) { assert(0); etrace1(_T("Tried to get invalid element %d\n"), i); return m_InvalidElement; } return m_Element[i]; } // This function returns a pointer to the action format of the device that has the given GUID HRESULT CConfigWnd::GetActionFormatFromInstanceGuid(LPDIACTIONFORMATW *lplpAcFor, REFGUID Guid) { if (!lplpAcFor) return E_INVALIDARG; for (int i = 0; i < GetNumElements(); i++) { ELEMENT &e = m_Element[i]; if (e.didi.guidInstance == Guid) { *lplpAcFor = GetCurAcFor(e); return S_OK; } } return E_INVALIDARG; } HDC CConfigWnd::GetRenderDC() { assert(InRenderMode()); if (m_bRender3D) return m_pbm3D == NULL ? NULL : m_pbm3D->BeginPaintInto(); else { if (m_pSurface == NULL) return NULL; HDC hDC = NULL; HRESULT hr = m_pSurface->GetDC(&hDC); if (FAILED(hr)) if (hr == DDERR_SURFACELOST) { m_pSurface->Restore(); // Restore the surface hr = m_pSurface->GetDC(&hDC); // Retry if (FAILED(hr)) return NULL; } else return NULL; return hDC; } } void CConfigWnd::ReleaseRenderDC(HDC &phDC) { assert(InRenderMode()); HDC hDC = phDC; phDC = NULL; if (m_bRender3D) { if (m_pbm3D == NULL) return; m_pbm3D->EndPaintInto(hDC); } else { if (m_pSurface == NULL) return; m_pSurface->ReleaseDC(hDC); } } struct BITMAPINFO_3MASKS { BITMAPINFOHEADER bmiHeader; RGBQUAD bmiColors[3]; }; void CConfigWnd::Create3DBitmap() { HDC hDC = CreateCompatibleDC(NULL); BITMAPINFO_3MASKS bmi3mask; // BITMAPINFO with 3 DWORDs for bmiColors BITMAPINFO *pbmi = (BITMAPINFO*)&bmi3mask; BITMAPINFOHEADER &h = pbmi->bmiHeader; h.biSize = sizeof(BITMAPINFOHEADER); h.biWidth = WINDOW_WIDTH; h.biHeight = -WINDOW_HEIGHT; h.biPlanes = 1; h.biSizeImage = 0; h.biXPelsPerMeter = 100; h.biYPelsPerMeter = 100; h.biClrImportant = 0; // Get the surface's pixel format D3DSURFACE_DESC d3dsd; ZeroMemory(&d3dsd, sizeof(d3dsd)); m_pSurface3D->GetDesc(&d3dsd); m_SurfFormat = d3dsd.Format; switch(d3dsd.Format) { case D3DFMT_R5G6B5: h.biClrUsed = 3; h.biBitCount = 16; m_uiPixelSize = 2; h.biCompression = BI_BITFIELDS; *((LPDWORD)pbmi->bmiColors) = 0xF800; *((LPDWORD)pbmi->bmiColors+1) = 0x07E0; *((LPDWORD)pbmi->bmiColors+2) = 0x001F; break; case D3DFMT_X1R5G5B5: case D3DFMT_A1R5G5B5: h.biClrUsed = 3; h.biBitCount = 16; m_uiPixelSize = 2; h.biCompression = BI_BITFIELDS; *((LPDWORD)pbmi->bmiColors) = 0x7C00; *((LPDWORD)pbmi->bmiColors+1) = 0x03E0; *((LPDWORD)pbmi->bmiColors+2) = 0x001F; break; case D3DFMT_R8G8B8: h.biClrUsed = 0; h.biBitCount = 24; m_uiPixelSize = 3; h.biCompression = BI_RGB; break; case D3DFMT_A8R8G8B8: case D3DFMT_X8R8G8B8: default: // Use 32 bits for all other formats h.biClrUsed = 0; h.biBitCount = 32; m_uiPixelSize = 4; h.biCompression = BI_RGB; break; } HBITMAP hbm = CreateDIBSection( hDC, pbmi, DIB_RGB_COLORS, &m_p3DBits, NULL, 0); DeleteDC(hDC); hDC = NULL; if (hbm != NULL) m_pbm3D = CBitmap::StealToCreate(hbm); if (hbm != NULL) DeleteObject((HGDIOBJ)hbm); hbm = NULL; } void CConfigWnd::Copy3DBitmapToSurface3D() { assert(m_bRender3D); if (m_p3DBits == NULL || m_pbm3D == NULL || m_pSurface3D == NULL) { etrace(_T("One or more of the vars required for Copy3DBitmapToSurface() was NULL!\n")); return; } RECT rect = {0, 0, WINDOW_WIDTH, WINDOW_HEIGHT}; HRESULT hr = D3DXLoadSurfaceFromMemory( m_pSurface3D, NULL, NULL,//&rect, m_p3DBits, m_SurfFormat, WINDOW_WIDTH * m_uiPixelSize, NULL, &rect, D3DX_FILTER_POINT, 0); // Disable Color Key } void CConfigWnd::CallRenderCallback() { LPDICONFIGUREDEVICESCALLBACK pCallback = m_uig.GetCallback(); LPVOID pvRefData = m_uig.GetRefData(); if (pCallback == NULL) return; if (m_bRender3D) { Copy3DBitmapToSurface3D(); pCallback(m_pSurface3D, pvRefData); } else { pCallback(m_pSurface, pvRefData); } } void CConfigWnd::OnRender(BOOL bInternalCall) { m_bNeedRedraw = TRUE; } void CConfigWnd::Render(BOOL bInternalCall) { tracescope(__ts, _T("CConfigWnd::Render() ")); traceBOOL(bInternalCall); m_bNeedRedraw = FALSE; ValidateRect(m_hWnd, NULL); if (m_hWnd == NULL) return; HDC hDC = GetRenderDC(); if (hDC == NULL) return; if (bInternalCall) RenderInto(hDC); static ICONINFO IconInfo; static HCURSOR hOldCursor = NULL; static HCURSOR hCursor; if (m_bHourGlass) hCursor = LoadCursor(NULL, IDC_WAIT); else hCursor = LoadCursor(NULL, IDC_ARROW); if (hCursor == NULL) return; if (hOldCursor != hCursor) { hOldCursor = hCursor; GetIconInfo(hCursor, &IconInfo); if (IconInfo.hbmMask) DeleteObject(IconInfo.hbmMask); if (IconInfo.hbmColor) DeleteObject(IconInfo.hbmColor); } POINT pt; GetCursorPos(&pt); ScreenToClient(m_hWnd, &pt); pt.x -= IconInfo.xHotspot; pt.y -= IconInfo.yHotspot; if (m_pbmPointerEraser) m_pbmPointerEraser->Get(hDC, pt); // If m_bHourGlass is true, we are resetting, so we don't draw mouse cursor. if (hCursor && !m_bHourGlass) DrawIcon(hDC, pt.x, pt.y, hCursor); ReleaseRenderDC(hDC); CallRenderCallback(); hDC = GetRenderDC(); if (hDC == NULL) return; if (m_pbmPointerEraser) m_pbmPointerEraser->Draw(hDC, pt); ReleaseRenderDC(hDC); } void CConfigWnd::Unacquire() { for (int i = 0; i < GetNumElements(); i++) { ELEMENT &e = m_Element[i]; if (e.pPage != NULL) e.pPage->Unacquire(); } } void CConfigWnd::Reacquire() { for (int i = 0; i < GetNumElements(); i++) { ELEMENT &e = m_Element[i]; if (e.pPage != NULL) e.pPage->Reacquire(); } } HRESULT CConfigWnd::Reset() { RECT rect; int iCenterX, iCenterY; GetClientRect(&rect); iCenterX = (rect.left + rect.right) >> 1; iCenterY = (rect.top + rect.bottom) >> 1; rect.left = rect.right = iCenterX; rect.top = rect.bottom = iCenterY; InflateRect(&rect, g_iResetMsgBoxWidth >> 1, g_iResetMsgBoxHeight >> 1); m_MsgBox.Create(m_hWnd, rect, FALSE); m_MsgBox.SetNotify(m_hWnd); m_MsgBox.SetFont((HFONT)m_uig.GetFont(UIE_USERNAMES)); m_MsgBox.SetColors(m_uig.GetTextColor(UIE_USERNAMES), m_uig.GetBkColor(UIE_USERNAMES), m_uig.GetTextColor(UIE_USERNAMESEL), m_uig.GetBkColor(UIE_USERNAMESEL), m_uig.GetBrushColor(UIE_USERNAMES), m_uig.GetPenColor(UIE_USERNAMES)); TCHAR tszResourceString[MAX_PATH]; LoadString(g_hModule, IDS_RESETMSG, tszResourceString, MAX_PATH); m_MsgBox.SetText(tszResourceString); ::ShowWindow(m_MsgBox.m_hWnd, SW_SHOW); ::SetWindowPos(m_MsgBox.m_hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_SHOWWINDOW); m_MsgBox.Invalidate(); return S_OK; } HRESULT CConfigWnd::QueryActionAssignedAnywhere(GUID GuidInstance, int i) { return IsActionAssignedAnywhere(GuidInstance, i) ? S_OK : S_FALSE; } int CConfigWnd::GetNumGenres() { return m_uig.GetNumMasterAcFors(); } HRESULT CConfigWnd::SetCurUser(int nPage, int nUser) { // make sure we're using a valid element index if (nPage < 0 || nPage >= GetNumElements()) { assert(0); return E_FAIL; } // get the element ELEMENT &e = GetElement(nPage); // don't do anything if we're already set to this user if (e.nCurUser == nUser) return S_OK; // store new curuser e.nCurUser = nUser; // if this page isn't the one currently shown, do nothing // (it'll get the new acfor when it's shown) if (m_CurSel != nPage) return S_OK; // otherwised, cycle the page to reflect change if (e.pPage) e.pPage->Unacquire(); HidePage(nPage); ShowPage(nPage); if (e.pPage) e.pPage->Reacquire(); return S_OK; } HRESULT CConfigWnd::SetCurGenre(int NewGenre) { // if no change, do nothing if (NewGenre == m_nCurGenre) return S_OK; // make sure genre index is in range if (NewGenre < 0 || NewGenre >= GetNumGenres()) return E_INVALIDARG; // set genre m_nCurGenre = NewGenre; // store which page is currently up int iOldPage = m_CurSel; // for each page... BOOL bShown = FALSE; for (int i = 0; i < GetNumElements(); i++) { ELEMENT &e = GetElement(i); // hide the page and unacquire its device if (e.pPage) { e.pPage->Unacquire(); HidePage(i); } // show page if it was the old cur page if (i == iOldPage && e.pPage && GetCurAcFor(e)) { ShowPage(i); bShown = TRUE; } // reacquire device if (e.pPage) e.pPage->Reacquire(); } // if nothing was shown, show something if (!bShown && GetNumElements() > 0) { m_CurSel = -1; SelTab(0); } // if we showed the one we expected to show, we succeeded return bShown ? S_OK : E_FAIL; } int CConfigWnd::GetCurGenre() { return m_nCurGenre; } HWND CConfigWnd::GetMainHWND() { return m_hWnd; } // This is called by CDIDeviceActionConfigPage::DeviceUINotify. // We scan the ELEMENT array and when we find a match, destroy and recreate the device // object, then return it back to CDIDeviceActionConfigPage so it can update its pointer. LPDIRECTINPUTDEVICE8W CConfigWnd::RenewDevice(GUID &GuidInstance) { for (int i = 0; i < GetNumElements(); i++) { ELEMENT &e = GetElement(i); if (e.didi.guidInstance == GuidInstance) { // Releaes the instance we have if (e.lpDID) { e.lpDID->Release(); e.lpDID = NULL; } // Recreate the device e.lpDID = CreateDevice(e.didi.guidInstance); return e.lpDID; } } return NULL; } LPDIACTIONFORMATW ELEMENT::GetAcFor(int nGenre, int nUser, BOOL bHwDefault) { // return null if requesting for unassigned user if (nUser == -1) return NULL; // validate params if (!lpDID || !pUIGlobals || nGenre < 0 || nGenre >= pUIGlobals->GetNumMasterAcFors() || nUser < 0 || nUser >= pUIGlobals->GetNumUserNames()) { etrace(_T("ELEMENT::GetAcFor(): Invalid params\n")); return NULL; } // generate dword id for map entry DWORD dwMap = GENREUSER2MAP(nGenre, nUser); // try to get that acfor LPDIACTIONFORMATW lpAcFor = NULL; BOOL bFound = AcForMap.Lookup(dwMap, lpAcFor); // if we found it and its not null and we are asked for hardware default setting, return it if (bFound && lpAcFor && !bHwDefault) return lpAcFor; // otherwise... we gotta make it tracescope(__ts, _T("ELEMENT::GetAcFor")); trace2(_T("(%d, %d)\n"), nGenre, nUser); trace1(_T("Building map entry 0x%08x...\n"), dwMap); // copy it from the masteracfor for the genre lpAcFor = DupActionFormat(&(pUIGlobals->RefMasterAcFor(nGenre))); if (!lpAcFor) { etrace(_T("DupActionFormat() failed\n")); return NULL; } // build it for the user DWORD dwFlags = 0; if (bHwDefault //@@BEGIN_MSINTERNAL #ifdef DDKBUILD || pUIGlobals->QueryAllowEditLayout() #endif //@@END_MSINTERNAL ) dwFlags |= DIDBAM_HWDEFAULTS; LPCWSTR wszUserName = pUIGlobals->GetUserName(nUser); HRESULT hr = lpDID->BuildActionMap(lpAcFor, wszUserName, dwFlags); if (FAILED(hr)) { etrace4(_T("BuildActionMap(0x%p, %s, 0x%08x) failed, returning 0x%08x\n"), lpAcFor, QSAFESTR(wszUserName), dwFlags, hr); FreeActionFormatDup(lpAcFor); lpAcFor = NULL; return NULL; } else { trace3(_T("BuildActionMap(0x%p, %s, 0x%08x) succeeded\n"), lpAcFor, QSAFESTR(wszUserName), dwFlags); // Now we check if the return code is DI_WRITEPROTECT. If so, device can't be remapped. if (hr == DI_WRITEPROTECT) { // The way we disable mapping is to add DIA_APPFIXED flag to all actions. for (DWORD i = 0; i < lpAcFor->dwNumActions; ++i) lpAcFor->rgoAction[i].dwFlags |= DIA_APPFIXED; } } // Here we copy the DIA_APPFIXED flag back to our action format for all DIACTION that had this. const DIACTIONFORMATW &MasterAcFor = pUIGlobals->RefMasterAcFor(nGenre); for (DWORD i = 0; i < MasterAcFor.dwNumActions; ++i) if (MasterAcFor.rgoAction[i].dwFlags & DIA_APPFIXED) lpAcFor->rgoAction[i].dwFlags |= DIA_APPFIXED; // set it in the map assert(lpAcFor != NULL); AcForMap.SetAt(dwMap, lpAcFor); // return it return lpAcFor; } void ELEMENT::FreeMap() { POSITION pos = AcForMap.GetStartPosition(); while (pos != NULL) { DWORD dwMap = (DWORD)-1; LPDIACTIONFORMATW lpAcFor = NULL; AcForMap.GetNextAssoc(pos, dwMap, lpAcFor); if (lpAcFor) FreeActionFormatDup(lpAcFor); } AcForMap.RemoveAll(); } void ELEMENT::Apply() { tracescope(tsa, _T("\nELEMENT::Apply()\n")); trace1(_T("Applying Changes to Device %s\n"), QSAFESTR(didi.tszInstanceName)); if (lpDID == NULL) { etrace(_T("NULL lpDID, can't apply\n")); return; } if (pUIGlobals == NULL) { etrace(_T("NULL pUIGlobals, can't apply\n")); return; } LPDIACTIONFORMATW lpAcFor = NULL; // go through the map and add the map keys to last if the user // is the current user assignment, or to first if not CList first, last; POSITION pos = AcForMap.GetStartPosition(); while (pos != NULL) { DWORD dwMap = (DWORD)-1; lpAcFor = NULL; AcForMap.GetNextAssoc(pos, dwMap, lpAcFor); if (MAP2USER(dwMap) == nCurUser) last.AddTail(dwMap); else first.AddTail(dwMap); } // concatenate lists first.AddTail(&last); // now go through the resulting list (so that the current // assignments are set last) if this device is assigned. if (nCurUser != -1) { pos = first.GetHeadPosition(); while (pos != NULL) { DWORD dwMap = first.GetNext(pos); lpAcFor = AcForMap[dwMap]; tracescope(tsa2, _T("Applying lpAcFor at AcForMap[")); trace1(_T("0x%08x]...\n"), dwMap); if (lpAcFor == NULL) { etrace(_T("NULL lpAcFor, can't apply\n")); continue; } int nGenre = MAP2GENRE(dwMap); int nUser = MAP2USER(dwMap); LPCWSTR wszUserName = pUIGlobals->GetUserName(nUser); traceLONG(nGenre); traceLONG(nUser); traceWSTR(wszUserName); TraceActionFormat(_T("Final Device ActionFormat:"), *lpAcFor); for (DWORD j = 0; j < lpAcFor->dwNumActions; ++j) { if( lpAcFor->rgoAction[j].dwObjID == (DWORD)-1 || IsEqualGUID(lpAcFor->rgoAction[j].guidInstance, GUID_NULL)) { lpAcFor->rgoAction[j].dwHow = DIAH_UNMAPPED; } else if( lpAcFor->rgoAction[j].dwHow & ( DIAH_USERCONFIG | DIAH_APPREQUESTED | DIAH_HWAPP | DIAH_HWDEFAULT | DIAH_DEFAULT ) ) { //@@BEGIN_MSINTERNAL // ISSUE-2001/03/27-MarcAnd should look at doing this less destructively // that is if everything is defaults then leave them alon //@@END_MSINTERNAL lpAcFor->rgoAction[j].dwHow = DIAH_USERCONFIG; } else if(IsEqualGUID(didi.guidInstance,lpAcFor->rgoAction[j].guidInstance)) { lpAcFor->rgoAction[j].dwHow = DIAH_USERCONFIG; } } HRESULT hr; hr = lpDID->SetActionMap(lpAcFor, wszUserName, DIDSAM_FORCESAVE|DIDSAM_DEFAULT); if (FAILED(hr)) etrace1(_T("SetActionMap() failed, returning 0x%08x\n"), hr); else trace(_T("SetActionMap() succeeded\n")); } } // if (nCurUser != -1) else { // we're unassigned, set null trace(_T("Unassigning...\n")); // we need an acfor to unassign, so get one if don't have // one left over from what we just did if (!lpAcFor) lpAcFor = GetAcFor(0, 0); if (!lpAcFor) etrace(_T("Couldn't get an acfor for unassignment\n")); HRESULT hr; hr = lpDID->SetActionMap(lpAcFor, NULL, DIDSAM_NOUSER); if (FAILED(hr)) etrace1(_T("SetActionMap() failed, returning 0x%08x\n"), hr); else trace(_T("SetActionMap() succeeded\n")); } } int CConfigWnd::GetNumUsers() { return m_uig.GetNumUserNames(); } int CConfigWnd::GetCurUser(int nPage) { return GetElement(nPage).nCurUser; }