#include #include "WinBFApp.h" #include "DXRenderDevice.h" #include #include "../../util/BeefPerf.h" #include "DSoundManager.h" #include "DInputManager.h" #include #pragma comment(lib, "dwmapi.lib") #include "util/AllocDebug.h" USING_NS_BF; int Beefy::WinBFMenu::mMenuCount = 0; int PrintStuff() { OutputDebugStringA("Hey!\n"); return 123; } WinBFMenu::WinBFMenu() { mIsPlaceholder = false; mMenu = NULL; mParent = NULL; mMenuId = 0; } /// static HICON gMainIcon = NULL; static BOOL CALLBACK BFEnumResNameProc( HMODULE hModule, LPCWSTR lpszType, LPWSTR lpszName, LONG_PTR lParam ) { gMainIcon = ::LoadIconW(hModule, lpszName); return FALSE; } struct AdjustedMonRect { int mMonCount; int mX; int mY; int mWidth; int mHeight; }; static BOOL ClipToMonitor(HMONITOR mon, HDC hdc, LPRECT monRect, LPARAM userArg) { AdjustedMonRect* outRect = (AdjustedMonRect*)userArg; MONITORINFO monitorInfo = { sizeof(MONITORINFO) }; if (::GetMonitorInfo(mon, &monitorInfo) == 0) return TRUE; outRect->mMonCount++; if (outRect->mX < monitorInfo.rcWork.left) outRect->mX = monitorInfo.rcWork.left; else if (outRect->mX + outRect->mWidth >= monitorInfo.rcWork.right) outRect->mX = BF_MAX((int)monitorInfo.rcWork.left, monitorInfo.rcWork.right - outRect->mWidth); if (outRect->mY < monitorInfo.rcWork.top) outRect->mY = monitorInfo.rcWork.top; else if (outRect->mY + outRect->mHeight >= monitorInfo.rcWork.bottom) outRect->mY = BF_MAX((int)monitorInfo.rcWork.top, monitorInfo.rcWork.bottom - outRect->mHeight); return TRUE; } static BOOL KeyboardLayoutHasAltGr(HKL layout) { BOOL hasAltGr = FALSE; int scancode; for (WORD i = 32; i < 256; ++i) { scancode = VkKeyScanEx((TCHAR)i, layout); if ((scancode != -1) && ((scancode & 0x600) == 0x600)) { hasAltGr = TRUE; break; } } return hasAltGr; } WinBFWindow::WinBFWindow(BFWindow* parent, const StringImpl& title, int x, int y, int width, int height, int windowFlags) { //OutputDebugStrF("Wnd %p Create\n", this); HINSTANCE hInstance = GetModuleHandle(NULL); mMinWidth = 128; mMinHeight = 128; WNDCLASSW wc; wc.style = 0; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hbrBackground = NULL; //wc.hbrBackground = ::CreateSolidBrush(::GetSysColor(COLOR_BTNFACE)); wc.hCursor = NULL; //wc.hIcon = (HICON) ::LoadImageA(hInstance, "MainIcon", IMAGE_ICON, 0, 0, 0); //wc.hIcon = (HICON) ::LoadImageA(hInstance, MAKEINTRESOURCEA(32512), IMAGE_ICON, 0, 0, 0); if (gMainIcon != NULL) { wc.hIcon = gMainIcon; } else { wc.hIcon = (HICON) ::LoadIconA(hInstance, "MainIcon"); if (wc.hIcon == NULL) { EnumResourceNamesW(hInstance, (LPCWSTR)RT_GROUP_ICON, BFEnumResNameProc, 0); wc.hIcon = gMainIcon; } } wc.hInstance = hInstance; wc.lpfnWndProc = WindowProcStub; wc.lpszClassName = L"BFWindow"; wc.lpszMenuName = NULL; RegisterClassW(&wc); int requestedX = x; int requestedY = y; int aWindowFlags = 0; bool hasDestAlpha = (windowFlags & BFWINDOW_DEST_ALPHA) != 0; if (windowFlags & BFWINDOW_MENU) { WinBFMenu* aMenu = new WinBFMenu(); aMenu->mMenu = ::CreateMenu(); mHMenuMap[aMenu->mMenu] = aMenu; mMenu = aMenu; } int windowFlagsEx = /*WS_EX_COMPOSITED |*/ WS_EX_LAYERED; if (windowFlags & BFWINDOW_TOOLWINDOW) windowFlagsEx |= WS_EX_TOOLWINDOW; if (windowFlags & BFWINDOW_BORDER) aWindowFlags |= WS_BORDER; if (windowFlags & BFWINDOW_THICKFRAME) aWindowFlags |= WS_THICKFRAME; if ((windowFlags & BFWINDOW_RESIZABLE) && (!hasDestAlpha)) aWindowFlags |= WS_SIZEBOX; if (windowFlags & BFWINDOW_SYSMENU) aWindowFlags |= WS_SYSMENU; if (windowFlags & BFWINDOW_CAPTION) aWindowFlags |= WS_CAPTION; else aWindowFlags |= WS_POPUP; if (windowFlags & BFWINDOW_MINIMIZE) aWindowFlags |= WS_MINIMIZEBOX; if (windowFlags & BFWINDOW_MAXIMIZE) aWindowFlags |= WS_MAXIMIZEBOX; if ((windowFlags & BFWINDOW_TOPMOST) && (parent == NULL)) windowFlagsEx |= WS_EX_TOPMOST; if ((windowFlags & BFWINDOW_ACCEPTFILES)) windowFlagsEx |= WS_EX_ACCEPTFILES; if ((windowFlags & BFWINDOW_NO_MOUSE)) windowFlagsEx |= WS_EX_TRANSPARENT | WS_EX_LAYERED; if (windowFlags & BFWINDOW_CLIENT_SIZED) { RECT rect = {0, 0, width, height}; AdjustWindowRectEx(&rect, aWindowFlags, mMenu != NULL, windowFlagsEx); x += rect.left; y += rect.top; width = rect.right - rect.left; height = rect.bottom - rect.top; } if (windowFlags & BFWINDOW_POPUP_POSITION) { AdjustedMonRect adjustRect = { 0, x, y, width, height }; RECT wantRect = { x, y, x + width, y + height }; EnumDisplayMonitors(NULL, &wantRect, ClipToMonitor, (LPARAM)&adjustRect); if (adjustRect.mMonCount == 0) EnumDisplayMonitors(NULL, NULL, ClipToMonitor, (LPARAM)&adjustRect); x = adjustRect.mX; y = adjustRect.mY; width = adjustRect.mWidth; height = adjustRect.mHeight; } mFlags = windowFlags; mMouseVisible = true; mParent = parent; HWND parentHWnd = NULL; if (parent != NULL) parentHWnd = ((WinBFWindow*) parent)->mHWnd; if (mMenu != NULL) { WinBFMenu* placeholderMenu = (WinBFMenu*) AddMenuItem(mMenu, 0, "", NULL, NULL, false, -1, false); placeholderMenu->mIsPlaceholder = true; } mHWnd = CreateWindowExW(windowFlagsEx, L"BFWindow", UTF8Decode(title).c_str(), aWindowFlags, x, y, width, height, parentHWnd, (mMenu != NULL) ? ((WinBFMenu*) mMenu)->mMenu : NULL, hInstance, 0); if ((windowFlags & BFWINDOW_ALPHA_MASK) == 0) SetLayeredWindowAttributes(mHWnd, 0, 255, 0); HWND relativeWindow = NULL; if ((windowFlags & BFWINDOW_TOPMOST) && (parent == NULL)) relativeWindow = HWND_TOP; int showFlags = SWP_SHOWWINDOW; // if (windowFlags & BFWINDOW_SHOWMINIMIZED) // showFlags = SWP_ mHasFocus = true; mSoftHasFocus = true; if (windowFlags & BFWINDOW_FAKEFOCUS) { showFlags |= SWP_NOACTIVATE; if (windowFlags & BFWINDOW_NO_ACTIVATE) { mHasFocus = false; mSoftHasFocus = false; } } else if (windowFlags & BFWINDOW_NO_ACTIVATE) { showFlags |= SWP_NOACTIVATE; mHasFocus = false; mSoftHasFocus = false; } if ((windowFlags & BFWINDOW_NOSHOW)) showFlags = SWP_HIDEWINDOW; if (windowFlags & (BFWINDOW_SHOWMINIMIZED | BFWINDOW_SHOWMAXIMIZED)) { WINDOWPLACEMENT wndPlacement = { sizeof(WINDOWPLACEMENT), 0 }; ::GetWindowPlacement(mHWnd, &wndPlacement); if (windowFlags & BFWINDOW_SHOWMINIMIZED) wndPlacement.showCmd = SW_SHOWMINIMIZED; else if (windowFlags & BFWINDOW_SHOWMAXIMIZED) wndPlacement.showCmd = SW_SHOWMAXIMIZED; else wndPlacement.showCmd = SW_SHOWNORMAL; wndPlacement.rcNormalPosition.left = x; wndPlacement.rcNormalPosition.top = y; wndPlacement.rcNormalPosition.right = x + width; wndPlacement.rcNormalPosition.bottom = y + height; ::SetWindowPlacement(mHWnd, &wndPlacement); } else { SetWindowPos(mHWnd, relativeWindow, x, y, width, height, showFlags); } SetTimer(mHWnd, 0, 10, NULL); mIsMouseInside = false; mRenderWindow = new DXRenderWindow((DXRenderDevice*) gBFApp->mRenderDevice, this, (windowFlags & BFWINDOW_FULLSCREEN) == 0); gBFApp->mRenderDevice->AddRenderWindow(mRenderWindow); SetWindowLongPtr(mHWnd, GWLP_USERDATA, (LONG_PTR)this); mIsMenuKeyHandled = false; mAlphaMaskBitmap = NULL; mAlphaMaskDC = NULL; mAlphaMaskPixels = NULL; mAlphaMaskWidth = 0; mAlphaMaskHeight = 0; mNeedsStateReset = false; mAwaitKeyReleases = false; mAwaitKeyReleasesEventTick = 0; mAwaitKeyReleasesCheckIdx = 0; mFocusLostTick = ::GetTickCount(); if (windowFlags & BFWINDOW_DEST_ALPHA) { MARGINS dWMMargins = {-1, -1, -1, -1}; DwmExtendFrameIntoClientArea(mHWnd, &dWMMargins); } if (windowFlags & BFWINDOW_MODAL) { EnableWindow(parentHWnd, FALSE); } if (parent != NULL) { auto winParent = (WinBFWindow*)parent; BF_ASSERT(winParent->mHWnd); parent->mChildren.push_back(this); } HKL layout = GetKeyboardLayout(0); mKeyLayoutHasAltGr = (KeyboardLayoutHasAltGr(layout) == TRUE); } WinBFWindow::~WinBFWindow() { //OutputDebugStrF("Wnd %p Destroyed\n", this); if (mHWnd != NULL) Destroy(); } void* WinBFWindow::GetUnderlying() { return mHWnd; } void WinBFWindow::Destroy() { if (mAlphaMaskDC != NULL) DeleteDC(mAlphaMaskDC); mAlphaMaskDC = NULL; if (mAlphaMaskBitmap != NULL) DeleteObject(mAlphaMaskBitmap); mAlphaMaskBitmap = NULL; for (auto& menu : mMenuIDMap) { delete menu.mValue; } mMenuIDMap.Clear(); ::DestroyWindow(mHWnd); BF_ASSERT(mHWnd == NULL); } bool WinBFWindow::TryClose() { SendMessage(mHWnd, WM_CLOSE, 0, 0); return mHWnd == NULL; } void WinBFWindow::SetTitle(const char* title) { SetWindowTextA(mHWnd, title); } static int ToWShow(BFWindow::ShowKind showKind) { switch (showKind) { case BFWindow::ShowKind_Hide: return SW_HIDE; case BFWindow::ShowKind_Normal: return SW_NORMAL; case BFWindow::ShowKind_Minimized: return SW_MINIMIZE; case BFWindow::ShowKind_Maximized: return SW_MAXIMIZE; case BFWindow::ShowKind_Show: return SW_SHOW; case BFWindow::ShowKind_ShowNormal: return SW_SHOWNORMAL; case BFWindow::ShowKind_ShowMinimized: return SW_SHOWMINIMIZED; case BFWindow::ShowKind_ShowMaximized: return SW_SHOWMAXIMIZED; } return SW_SHOW; } void WinBFWindow::Show(ShowKind showKind) { ::ShowWindow(mHWnd, ToWShow(showKind)); } void WinBFWindow::LostFocus(BFWindow* newFocus) { ///OutputDebugStrF("Lost focus\n"); mFocusLostTick = ::GetTickCount(); WinBFWindow* bfNewFocus = (WinBFWindow*)newFocus; mSoftHasFocus = false; for (int i = 0; i < KEYCODE_MAX; i++) { // Only transfer mode keys if (mIsKeyDown[i]) { mIsKeyDown[i] = false; mKeyUpFunc(this, i); if ((newFocus != NULL) && ((i == VK_SHIFT) || (i == VK_CONTROL) || (i == VK_MENU))) { newFocus->mIsKeyDown[i] = true; newFocus->mKeyDownFunc(newFocus, i, 0); } } } } void WinBFWindow::GotFocus() { DWORD tickNow = ::GetTickCount(); //OutputDebugStrF("GotFocus since lost %d\n", tickNow - mFocusLostTick); if (tickNow - mFocusLostTick >= 1000) { mAwaitKeyReleases = true; mAwaitKeyReleasesCheckIdx = 0; mAwaitKeyReleasesEventTick = ::GetTickCount(); } } void WinBFWindow::SetForeground() { bool hadFocus = mHasFocus; DWORD prevFocusLostTick = mFocusLostTick; if (mFlags & BFWINDOW_FAKEFOCUS) { mHasFocus = true; mSoftHasFocus = true; return; } ::SetFocus(mHWnd); ::SetForegroundWindow(mHWnd); //OutputDebugStrF("SetForeground %p %d %d %d\n", mHWnd, hadFocus, ::GetTickCount() - prevFocusLostTick, mAwaitKeyReleases); } static POINT gLastScreenMouseCoords = { -1, -1 }; void WinBFWindow::RehupMouseOver(bool isMouseOver) { if ((!mIsMouseInside) && (isMouseOver)) { TRACKMOUSEEVENT tme; tme.cbSize = sizeof(TRACKMOUSEEVENT); tme.dwFlags = TME_LEAVE; tme.hwndTrack = mHWnd; TrackMouseEvent(&tme); mIsMouseInside = true; } if ((mIsMouseInside) && (!isMouseOver)) { mIsMouseInside = false; mMouseLeaveFunc(this); } } bool WinBFWindow::CheckKeyReleases(bool isKeyDown) { if (!mHasFocus) GotFocus(); if (!mAwaitKeyReleases) return true; // Time expired with no key presses if ((mAwaitKeyReleasesEventTick != 0) && (mAwaitKeyReleasesCheckIdx == 0) && (::GetTickCount() - mAwaitKeyReleasesEventTick > 150)) { //OutputDebugStrF("CheckKeyReleases no initial key press\n"); mAwaitKeyReleases = false; return true; } mAwaitKeyReleasesCheckIdx++; bool hasKeyDown = false; uint8 keysDown[256] = { 0 }; ::GetKeyboardState((PBYTE)&keysDown); for (int i = 1; i < 256; i++) if (keysDown[i] & 0x80) hasKeyDown = true; if ((hasKeyDown) && (::GetTickCount() - mAwaitKeyReleasesEventTick >= 600)) { // String dbgStr = "CheckKeyReleases timeout. Keys down:"; // for (int i = 1; i < 256; i++) // if (keysDown[i] & 0x80) // dbgStr += StrFormat(" %2X", i); // dbgStr += "\n"; // OutputDebugStr(dbgStr); hasKeyDown = false; } if (!hasKeyDown) { mAwaitKeyReleases = false; mAwaitKeyReleasesCheckIdx = 0; mAwaitKeyReleasesEventTick = 0; } return !mAwaitKeyReleases; } LRESULT WinBFWindow::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { WinBFApp* app = (WinBFApp*) gBFApp; if (app == NULL) { PrintStuff(); } switch (uMsg) { case WM_KEYDOWN: case WM_KEYUP: case WM_SYSKEYDOWN: case WM_CHAR: for (auto child : mChildren) { auto childWindow = (WinBFWindow*)child; if ((childWindow->mSoftHasFocus) && (childWindow->mFlags & BFWINDOW_FAKEFOCUS)) { return childWindow->WindowProc(hWnd, uMsg, wParam, lParam); } } break; } switch (uMsg) { case WM_CLOSE: { //OutputDebugStrF("WM_CLOSE %08X NewFocus:%08X\n", hWnd, GetFocus()); if (mCloseQueryFunc(this) != 0) gBFApp->RemoveWindow(this); HWND newFocus = GetFocus(); for (auto window : gBFApp->mWindowList) { WinBFWindow* winWindow = (WinBFWindow*)window; if (winWindow->mHWnd == newFocus) { while (true) { WinBFWindow* altFocusWindow = NULL; for (auto checkChild : winWindow->mChildren) { auto checkWinChild = (WinBFWindow*)checkChild; if (checkWinChild->mFlags & BFWINDOW_FAKEFOCUS) altFocusWindow = checkWinChild; } if (altFocusWindow == NULL) break; winWindow = altFocusWindow; } winWindow->mHasFocus = true; winWindow->mSoftHasFocus = true; winWindow->mGotFocusFunc(winWindow); } } return 0; } break; case WM_DESTROY: /*if (mFlags & BFWINDOW_QUIT_ON_CLOSE) { gBFApp->mRunning = false; }*/ SetWindowLongPtr(mHWnd, GWLP_USERDATA, (LONG_PTR)0); mHWnd = NULL; if (!mChildren.IsEmpty()) { NOP; } break; } LRESULT result = 0; bool doResult = false; if (!app->mInMsgProc) { if (mNeedsStateReset) { for (int i = 0; i < KEYCODE_MAX; i++) { if (mIsKeyDown[i]) { mIsKeyDown[i] = false; mKeyUpFunc(this, i); } } POINT mousePoint; ::GetCursorPos(&mousePoint); ::ScreenToClient(hWnd, &mousePoint); for (int i = 0; i < MOUSEBUTTON_MAX; i++) { if (mIsMouseDown[i]) { mMouseUpFunc(this, mousePoint.x, mousePoint.y, i); mIsMouseDown[i] = false; //OutputDebugStrF("Wnd %p mNeedsStateReset MouseUp %d\n", this, i); } } //OutputDebugStrF("Rehup ReleaseCapture()\n"); ReleaseCapture(); mNeedsStateReset = false; mIsMouseInside = false; } WinBFWindow* menuTarget = this; if (mFlags & BFWINDOW_USE_PARENT_MENU) menuTarget = ((WinBFWindow*)mParent); auto* menuIDMap = &menuTarget->mMenuIDMap; auto* hMenuMap = &menuTarget->mHMenuMap; app->mInMsgProc = true; switch (uMsg) { case WM_DISPLAYCHANGE: ((DXRenderWindow*)mRenderWindow)->mRefreshRate = 0; break; case WM_SIZE: mRenderWindow->Resized(); if (mMovedFunc != NULL) mMovedFunc(this); break; case WM_PAINT: break; case WM_NCHITTEST: { //OutputDebugStrF("WM_NCHITTEST %X\n", mHWnd); int x = (short)LOWORD(lParam); int y = (short)HIWORD(lParam); result = mHitTestFunc(this, x, y); doResult = (result != -3); } break; case WM_LBUTTONDOWN: case WM_RBUTTONDOWN: case WM_MBUTTONDOWN: case WM_XBUTTONDOWN: case WM_LBUTTONDBLCLK: case WM_RBUTTONDBLCLK: case WM_LBUTTONUP: case WM_RBUTTONUP: case WM_MBUTTONUP: case WM_XBUTTONUP: case WM_MOUSEWHEEL: case WM_MOUSEHWHEEL: case WM_MOUSEMOVE: { int x = (short)LOWORD(lParam); int y = (short)HIWORD(lParam); bool releaseCapture = false; POINT point = {x, y}; if ((uMsg != WM_MOUSEWHEEL) && (uMsg != WM_MOUSEHWHEEL)) ::ClientToScreen(hWnd, &point); if ((uMsg == WM_MOUSEMOVE) && (point.x == gLastScreenMouseCoords.x) && (point.y == gLastScreenMouseCoords.y)) { // Don't process a WM_MOUSEMOVE if it's at the same point at the last mouse event // This keeps us from getting a WM_MOUSEMOVE when we popup a window under the current cursor location. // This is important for keyboard cursor control - so the first down arrow keypress selects the first item in the list // irregardless of the mouse cursor position. break; } gLastScreenMouseCoords.x = point.x; gLastScreenMouseCoords.y = point.y; HWND windowAtPoint = ::WindowFromPoint(point); bool isMouseOver = windowAtPoint == hWnd; RehupMouseOver(isMouseOver); //OutputDebugStrF("HWnd: %X Focus Window: %X Capture: %X\n", hWnd, windowAtPoint, ::GetCapture()); bool checkNonTransparentMousePosition = mNonExclusiveMouseCapture; auto _BtnDown = [&](int btn) { BF_ASSERT(btn < MOUSEBUTTON_MAX); if (btn >= MOUSEBUTTON_MAX) return; //OutputDebugStrF("Wnd %p BtnDown %d\n", this, btn); DWORD tickNow = BFTickCount(); if (::SetCapture(hWnd) != hWnd) { // Not captured, no buttons were down for (int i = 0; i < MOUSEBUTTON_MAX; i++) { if (mIsMouseDown[i]) { mMouseUpFunc(this, x, y, i); mIsMouseDown[i] = false; //OutputDebugStrF("Wnd %p BtnDown MouseUp %d\n", this, i); } } } mIsMouseDown[btn] = true; BFCoord mouseCoords = { x, y }; if ((mouseCoords.mX != mMouseDownCoords[btn].mX) || (mouseCoords.mY != mMouseDownCoords[btn].mY) || (tickNow - mMouseDownTicks[btn] > ::GetDoubleClickTime())) mMouseClickCount[btn] = 0; mMouseDownCoords[btn] = mouseCoords; mMouseClickCount[btn]++; mMouseDownTicks[btn] = tickNow; mMouseDownFunc(this, x, y, btn, mMouseClickCount[btn]); }; auto _BtnUp = [&](int btn) { BF_ASSERT(btn < MOUSEBUTTON_MAX); if (btn >= MOUSEBUTTON_MAX) return; //OutputDebugStrF("Wnd %p BtnUp %d\n", this, btn); releaseCapture = true; mIsMouseDown[btn] = false; mMouseUpFunc(this, x, y, btn); }; switch (uMsg) { case WM_LBUTTONDOWN: //OutputDebugStrF("WM_LBUTTONDOWN Capture HWnd: %X\n", hWnd); _BtnDown(0); break; case WM_RBUTTONDOWN: //OutputDebugStrF("WM_RBUTTONDOWN Capture HWnd: %X\n", hWnd); _BtnDown(1); break; case WM_MBUTTONDOWN: _BtnDown(2); break; case WM_XBUTTONDOWN: _BtnDown((int)(wParam >> 16) + 2); break; case WM_LBUTTONUP: _BtnUp(0); break; case WM_RBUTTONUP: _BtnUp(1); break; case WM_MBUTTONUP: _BtnUp(2); break; case WM_XBUTTONUP: _BtnUp((int)(wParam >> 16) + 2); break; case WM_MOUSEWHEEL: case WM_MOUSEHWHEEL: { WinBFWindow* cursorWindow = this; if ((gBFApp->mWindowList.size() > 1) && (GetCapture() == NULL)) { // See if our mouse is down and has entered into another window's space POINT point = { x, y }; HWND windowAtPoint = ::WindowFromPoint(point); BFWindowList::iterator itr = gBFApp->mWindowList.begin(); while (itr != gBFApp->mWindowList.end()) { WinBFWindow* aWindow = (WinBFWindow*) *itr; LONG targetStyle = ::GetWindowLong(aWindow->mHWnd, GWL_EXSTYLE); if ((::IsWindowEnabled(aWindow->mHWnd)) && ((targetStyle & WS_EX_TRANSPARENT) == 0)) { if (aWindow->mHWnd == windowAtPoint) { aWindow->mIsMouseInside = true; cursorWindow = aWindow; } } ++itr; } } UINT ucNumLines = 0; SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &ucNumLines, 0); if (ucNumLines == 0) ucNumLines = 3; // Default if ((cursorWindow != this) && (mIsMouseInside)) { mMouseLeaveFunc(this); mIsMouseInside = false; } POINT pt = {x, y}; ScreenToClient(cursorWindow->mHWnd, &pt); if (uMsg == WM_MOUSEWHEEL) { float delta = ((int16)HIWORD(wParam)) / 120.0f * (float)ucNumLines; mMouseWheelFunc(cursorWindow, pt.x, pt.y, 0, delta); } else { float delta = ((int16)HIWORD(wParam)) / 120.0f; mMouseWheelFunc(cursorWindow, pt.x, pt.y, delta, 0); } } break; case WM_MOUSEMOVE: { //OutputDebugStrF("WM_MOUSEMOVE %d\n", hWnd); mMouseMoveFunc(this, x, y); // If we are dragging a transparent window then check for mouse positions under cursor HWND captureWindow = GetCapture(); if (captureWindow != NULL) { LONG captureStyle = ::GetWindowLong(captureWindow, GWL_EXSTYLE); if ((captureStyle & WS_EX_TRANSPARENT) != 0) checkNonTransparentMousePosition = true; } } break; } if ((checkNonTransparentMousePosition) && (gBFApp->mWindowList.size() > 1)) { // See if our mouse is down and has entered into another window's space POINT point = { x, y }; ::ClientToScreen(hWnd, &point); HWND windowAtPoint = ::WindowFromPoint(point); BFWindowList::iterator itr = gBFApp->mWindowList.begin(); while (itr != gBFApp->mWindowList.end()) { WinBFWindow* aWindow = (WinBFWindow*) *itr; if (aWindow != this) { LONG myStyle = ::GetWindowLong(mHWnd, GWL_EXSTYLE); LONG targetStyle = ::GetWindowLong(aWindow->mHWnd, GWL_EXSTYLE); if ((targetStyle & WS_EX_TRANSPARENT) == 0) { if (aWindow->mHWnd == windowAtPoint) { POINT clientPt = point; ::ScreenToClient(aWindow->mHWnd, &clientPt); aWindow->mMouseProxyMoveFunc(aWindow, clientPt.x, clientPt.y); aWindow->mIsMouseInside = true; } else if (aWindow->mIsMouseInside) { aWindow->mMouseLeaveFunc(aWindow); aWindow->mIsMouseInside = false; } } } ++itr; } } if (releaseCapture) { for (int i = 0; i < MOUSEBUTTON_MAX; i++) if (mIsMouseDown[i]) releaseCapture = false; } if (releaseCapture) { //OutputDebugStrF("Wnd %p Release Capture\n", this); //OutputDebugStrF("ReleaseCapture\n"); ReleaseCapture(); BFWindowList::iterator itr = gBFApp->mWindowList.begin(); while (itr != gBFApp->mWindowList.end()) { WinBFWindow* aWindow = (WinBFWindow*) *itr; if ((aWindow != this) && (aWindow->mIsMouseInside)) { aWindow->mMouseLeaveFunc(aWindow); aWindow->mIsMouseInside = false; } ++itr; } } } break; case WM_COMMAND: { WinBFMenu* aMenu = (*menuIDMap)[(uint32)wParam]; if (aMenu != NULL) menuTarget->mMenuItemSelectedFunc(menuTarget, aMenu); } break; case WM_APPCOMMAND: { if ((mFlags & BFWINDOW_CAPTURE_MEDIA_KEYS) != 0) { int cmd = GET_APPCOMMAND_LPARAM(lParam); int uDevice = GET_DEVICE_LPARAM(lParam); int dwKeys = GET_KEYSTATE_LPARAM(lParam); int key = cmd | 0x1000; mKeyDownFunc(this, key, false); result = TRUE; doResult = true; } } break; case WM_INITMENUPOPUP: { if (mIsKeyDown[VK_MENU]) { mKeyUpFunc(this, (int)VK_MENU); mIsKeyDown[VK_MENU] = false; } HMENU hMenu = (HMENU) wParam; WinBFMenu* aMenu = (*hMenuMap)[hMenu]; if (aMenu != NULL) menuTarget->mMenuItemSelectedFunc(menuTarget, aMenu); } break; case WM_MOUSEACTIVATE: if (mFlags & BFWINDOW_NO_MOUSE_ACTIVATE) { doResult = true; result = MA_NOACTIVATE; } else if (mFlags & BFWINDOW_FAKEFOCUS) { doResult = true; result = MA_NOACTIVATE; SetForeground(); } break; case WM_ACTIVATE: //OutputDebugStrF("WM_ACTIVATE %p\n", hWnd); break; case WM_KILLFOCUS: //OutputDebugStrF("WM_KILLFOCUS %p\n", hWnd); mHasFocus = false; mSoftHasFocus = false; LostFocus(NULL); mLostFocusFunc(this); break; case WM_SETFOCUS: //OutputDebugStrF("WM_SETFOCUS %p\n", hWnd); if (!mHasFocus) GotFocus(); mHasFocus = true; mSoftHasFocus = true; mGotFocusFunc(this); break; case WM_ENTERMENULOOP: //OutputDebugStrF("WM_ENTERMENULOOP %08X\n", hWnd); if (mMenu != NULL) mNeedsStateReset = true; break; case WM_EXITMENULOOP: //OutputDebugStrF("WM_EXITMENULOOP %08X\n", hWnd); if (mMenu != NULL) mNeedsStateReset = true; break; case WM_NCMOUSELEAVE: mIsMouseInside = false; mMouseLeaveFunc(this); break; case WM_CHAR: { /*if (wParam == 'z') { ID3D11Debug* pD3DDebug = NULL; HRESULT hr = ((DXRenderDevice*)mRenderWindow->mRenderDevice)->mD3DDevice->QueryInterface(__uuidof(ID3D11Debug), (void**)&pD3DDebug); if (pD3DDebug != NULL) { pD3DDebug->ReportLiveDeviceObjects(D3D11_RLDO_DETAIL); pD3DDebug->Release(); } }*/ //NOTE: This line broke Alt+Gr for braces and such. Determine why this was needed. //if ((!mIsKeyDown[VK_MENU]) && (!mIsKeyDown[VK_CONTROL])) if (CheckKeyReleases(true)) { for (int i = 0; i < (lParam & 0x7FFF); i++) mKeyCharFunc(this, (WCHAR)wParam); } } break; case WM_MENUCHAR: if (mIsMenuKeyHandled) { result = MNC_CLOSE << 16; doResult = true; } break; case WM_SYSKEYDOWN: case WM_KEYDOWN: { mIsMenuKeyHandled = false; int keyCode = (int) wParam; if (keyCode == VK_APPS) break; // This is handled in WM_CONTEXTMENU if ((mKeyLayoutHasAltGr) && (keyCode == VK_MENU) && ((lParam & 0x01000000) != 0)) keyCode = VK_RMENU; mIsKeyDown[keyCode] = true; // if ((keyCode == 192) && (mIsKeyDown[VK_MENU])) // { // ((DXRenderDevice*)mRenderWindow->mRenderDevice)->mNeedsReinitNative = true; // } for (auto kv : *menuIDMap) { WinBFMenu* aMenu = kv.mValue; if ((aMenu->mKeyCode == keyCode) && (aMenu->mKeyShift == mIsKeyDown[VK_SHIFT]) && (aMenu->mKeyCtrl == mIsKeyDown[VK_CONTROL]) && (aMenu->mKeyAlt == mIsKeyDown[VK_MENU])) { mIsMenuKeyHandled = true; menuTarget->mMenuItemSelectedFunc(menuTarget, aMenu); doResult = true; break; } } if (!mIsMenuKeyHandled) { if ((CheckKeyReleases(true)) && (mKeyDownFunc(this, keyCode, (lParam & 0x7FFF) != 0))) { mIsMenuKeyHandled = true; doResult = true; } } } break; case WM_SYSCHAR: { int keyCode = toupper((int) wParam); for (auto& menuKV : *menuIDMap) { WinBFMenu* aMenu = menuKV.mValue; if ((aMenu->mKeyCode == keyCode) && (aMenu->mKeyShift == mIsKeyDown[VK_SHIFT]) && (aMenu->mKeyCtrl == mIsKeyDown[VK_CONTROL]) && (aMenu->mKeyAlt == mIsKeyDown[VK_MENU])) { if (CheckKeyReleases(true)) doResult = true; break; } } if (!mIsKeyDown[VK_MENU]) { // If we don't have the alt key down then we assume we must have // had keyups forced by losing focus from the mMenuItemSelectedFunc doResult = true; } } break; case WM_SYSKEYUP: case WM_KEYUP: { int keyCode = (int) wParam; if ((mKeyLayoutHasAltGr) && (keyCode == VK_MENU) && ((lParam & 0x01000000) != 0)) keyCode = VK_RMENU; if (mIsKeyDown[keyCode]) { mKeyUpFunc(this, keyCode); mIsKeyDown[keyCode] = false; } CheckKeyReleases(false); } break; case WM_SYSCOMMAND: // Ignore F10 if ((wParam == SC_KEYMENU) && (lParam == 0)) { doResult = true; result = 0; } break; case WM_CONTEXTMENU: { int x = (short)LOWORD(lParam); int y = (short)HIWORD(lParam); if ((x == -1) && (y == -1)) { mKeyDownFunc(this, VK_APPS, false); mKeyUpFunc(this, VK_APPS); } } break; case WM_TIMER: if (gBFApp->mSysDialogCnt == 0) { auto checkNonFake = this; while (checkNonFake->mFlags & BFWINDOW_FAKEFOCUS) { checkNonFake = (WinBFWindow*)checkNonFake->mParent; } bool isFocused = GetForegroundWindow() == checkNonFake->mHWnd; if ((!isFocused) && (mHasFocus)) { mSoftHasFocus = false; mHasFocus = false; LostFocus(NULL); mLostFocusFunc(this); //OutputDebugStrF("Timer detected lost focus %p\r\n", hWnd); } else if ((isFocused) && (!mHasFocus) && (checkNonFake == this)) { //OutputDebugStrF("Timer detected got focus %p\r\n", hWnd); GotFocus(); mHasFocus = true; mSoftHasFocus = true; mGotFocusFunc(this); } mSoftHasFocus = mHasFocus; gBFApp->Process(); // Don't do anything with 'this' after Process, we may be deleted now doResult = true; result = 0; } break; case WM_SETCURSOR: gBFApp->PhysSetCursor(); break; case WM_GETMINMAXINFO: { MINMAXINFO* minMaxInfo = (MINMAXINFO*)lParam; minMaxInfo->ptMinTrackSize.x = mMinWidth; minMaxInfo->ptMinTrackSize.y = mMinHeight; result = 0; doResult = true; } break; case WM_MOVE: case WM_MOVING: if (mMovedFunc != NULL) mMovedFunc(this); break; case WM_SIZING: mRenderWindow->Resized(); if (mMovedFunc != NULL) mMovedFunc(this); if (gBFApp->mSysDialogCnt == 0) gBFApp->Process(); break; case WM_INPUTLANGCHANGE: mKeyLayoutHasAltGr = (KeyboardLayoutHasAltGr((HKL)lParam) == TRUE); break; case WM_DROPFILES: { HDROP hDropInfo = (HDROP)wParam; char sItem[MAX_PATH]; for(int i = 0; DragQueryFileA(hDropInfo, i, (LPSTR)sItem, sizeof(sItem)); i++) mDragDropFileFunc(this, sItem); DragFinish(hDropInfo); } break; } app->mInMsgProc = false; } else { // We got messages we couldn't process (due to reentrancy) switch (uMsg) { case WM_LBUTTONUP: case WM_RBUTTONUP: case WM_MBUTTONUP: case WM_MOUSEMOVE: case WM_KEYUP: case WM_MOUSELEAVE: mNeedsStateReset = true; break; } } if (doResult) return result; return DefWindowProc(hWnd, uMsg, wParam, lParam); } LRESULT CALLBACK WinBFWindow::WindowProcStub(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { WinBFWindow* aWindow = (WinBFWindow*) GetWindowLongPtr(hWnd, GWLP_USERDATA); if (aWindow != NULL) return aWindow->WindowProc(hWnd, Msg, wParam, lParam); else return DefWindowProc(hWnd, Msg, wParam, lParam); } // static int WinBFReportHook( int reportType, char *message, int *returnValue ) { if (reportType == 0) return 0; //__crtMessageWindowW(nRptType, szFile, (nLine ? szLineMessage : NULL), szModule, szUserMessage);; if (gBFApp) ((WinBFApp*) gBFApp)->mInMsgProc = true; int nCode = ::MessageBoxA(NULL, message, "Microsoft Visual C++ Debug Library", MB_TASKMODAL|MB_ICONHAND|MB_ABORTRETRYIGNORE|MB_SETFOREGROUND); if (gBFApp) ((WinBFApp*) gBFApp)->mInMsgProc = false; /* Abort: abort the program */ if (IDABORT == nCode) { /* note that it is better NOT to call abort() here, because the * default implementation of abort() will call Watson */ /* raise abort signal */ raise(SIGABRT); /* We usually won't get here, but it's possible that SIGABRT was ignored. So exit the program anyway. */ _exit(3); } /* Retry: return 1 to call the debugger */ if (IDRETRY == nCode) return 1; /* Ignore: continue execution */ return 0; return 1; } extern HINSTANCE gDLLInstance; typedef UINT(NTAPI *GetDpiForWindow_t)(HWND); static GetDpiForWindow_t gGetDpiForWindow = NULL; static HMODULE gUserDll = NULL; WinBFApp::WinBFApp() { #ifndef BF_MINGW //_CrtSetReportHook(WinBFReportHook); #endif if (gUserDll == NULL) { gUserDll = ::LoadLibraryA("user32.dll"); gGetDpiForWindow = (GetDpiForWindow_t)::GetProcAddress(gUserDll, "GetDpiForWindow"); } mRunning = false; mRenderDevice = NULL; mInstallDir = "Hey"; WCHAR aStr[MAX_PATH]; GetModuleFileNameW(gDLLInstance, aStr, MAX_PATH); mInstallDir = UTF8Encode(aStr); int a2DArray[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}}; int lastSlash = (int) mInstallDir.LastIndexOf(L'\\'); if (lastSlash != -1) mInstallDir = mInstallDir.Substring(0, lastSlash + 1); mDataDir = mInstallDir; mInMsgProc = false; mDSoundManager = NULL; mDInputManager = NULL; mVSyncThreadId = 0; mClosing = false; mVSyncActive = false; mVSyncThread = BfpThread_Create(VSyncThreadProcThunk, (void*)this, 128 * 1024, BfpThreadCreateFlag_StackSizeReserve, &mVSyncThreadId); BfpThread_SetPriority(mVSyncThread, BfpThreadPriority_High, NULL); } void BFP_CALLTYPE WinBFApp::VSyncThreadProcThunk(void* ptr) { ((WinBFApp*)ptr)->VSyncThreadProc(); } void WinBFApp::VSyncThreadProc() { DWORD lastBlankFinish = GetTickCount(); Array waitTimes; while (!mClosing) { bool didWait = false; IDXGIOutput* output = NULL; // { AutoCrit autoCrit(mCritSect); if ((mRenderDevice != NULL) && (!mRenderDevice->mRenderWindowList.IsEmpty())) { auto renderWindow = (DXRenderWindow*)mRenderDevice->mRenderWindowList[0]; renderWindow->mDXSwapChain->GetContainingOutput(&output); } } if (output != NULL) { DWORD startTick = GetTickCount(); bool success = output->WaitForVBlank() == 0; DWORD endTick = GetTickCount(); if (success) { int elapsed = (int)(endTick - startTick); waitTimes.Add(elapsed); if (waitTimes.mSize > 8) waitTimes.RemoveAt(0); if (elapsed <= 1) { bool hadNonZero = false; for (auto waitTime : waitTimes) { if (waitTime > 1) hadNonZero = true; } if (!hadNonZero) success = false; } } if (success) { didWait = true; mVSyncActive = true; mVSyncEvent.Set(); } output->Release(); } if (!didWait) { mVSyncActive = false; BfpThread_Sleep(20); } } } WinBFApp::~WinBFApp() { mClosing = true; BfpThread_WaitFor(mVSyncThread, -1); BfpThread_Release(mVSyncThread); delete mRenderDevice; delete mDSoundManager; delete mDInputManager; } void WinBFApp::Init() { BP_ZONE("WinBFApp::Init"); AutoCrit autoCrit(mCritSect); mRunning = true; mInMsgProc = false; mRenderDevice = new DXRenderDevice(); mRenderDevice->Init(this); } void WinBFApp::Run() { MSG msg; while (mRunning) { while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } if (mRunning) Process(); } } void WinBFApp::Process() { BFApp::Process(); auto dxRenderDevice = (DXRenderDevice*)mRenderDevice; if (dxRenderDevice->mNeedsReinitNative) { dxRenderDevice->mNeedsReinitNative = false; dxRenderDevice->ReinitNative(); mForceNextDraw = true; } } void WinBFApp::Draw() { mRenderDevice->FrameStart(); BFApp::Draw(); mRenderDevice->FrameEnd(); } void WinBFApp::GetDesktopResolution(int& width, int& height) { width = ::GetSystemMetrics(SM_CXSCREEN); height = ::GetSystemMetrics(SM_CYSCREEN); } static BOOL InflateRectToMonitor(HMONITOR mon, HDC hdc, LPRECT monRect, LPARAM userArg) { AdjustedMonRect* inflatedRect = (AdjustedMonRect*)userArg; MONITORINFO monitorInfo = { sizeof(MONITORINFO) }; if (::GetMonitorInfo(mon, &monitorInfo) == 0) return TRUE; inflatedRect->mMonCount++; if (inflatedRect->mMonCount == 1) { inflatedRect->mX = monitorInfo.rcWork.left; inflatedRect->mY = monitorInfo.rcWork.top; inflatedRect->mWidth = monitorInfo.rcWork.right - monitorInfo.rcWork.left; inflatedRect->mHeight = monitorInfo.rcWork.bottom - monitorInfo.rcWork.top; } else { int minLeft = BF_MIN(inflatedRect->mX, monitorInfo.rcWork.left); int minTop = BF_MIN(inflatedRect->mY, monitorInfo.rcWork.top); int maxRight = BF_MAX(inflatedRect->mX + inflatedRect->mWidth, monitorInfo.rcWork.right); int maxBottom = BF_MAX(inflatedRect->mY + inflatedRect->mHeight, monitorInfo.rcWork.bottom); inflatedRect->mX = minLeft; inflatedRect->mY = minTop; inflatedRect->mWidth = maxRight - minLeft; inflatedRect->mHeight = maxBottom - minTop; } return TRUE; } void WinBFApp::GetWorkspaceRect(int& x, int& y, int& width, int& height) { AdjustedMonRect inflateRect = { 0 }; EnumDisplayMonitors(NULL, NULL, InflateRectToMonitor, (LPARAM)&inflateRect); x = inflateRect.mX; y = inflateRect.mY; width = inflateRect.mWidth; height = inflateRect.mHeight; } void WinBFApp::GetWorkspaceRectFrom(int fromX, int fromY, int fromWidth, int fromHeight, int & outX, int & outY, int & outWidth, int & outHeight) { AdjustedMonRect inflateRect = { 0 }; RECT wantRect; wantRect.left = fromX; wantRect.top = fromY; wantRect.right = fromX + BF_MAX(fromWidth, 1); wantRect.bottom = fromY + BF_MAX(fromHeight, 1); EnumDisplayMonitors(NULL, &wantRect, InflateRectToMonitor, (LPARAM)&inflateRect); if (inflateRect.mMonCount == 0) { GetWorkspaceRect(outX, outY, outWidth, outHeight); return; } outX = inflateRect.mX; outY = inflateRect.mY; outWidth = inflateRect.mWidth; outHeight = inflateRect.mHeight; } BFWindow* WinBFApp::CreateNewWindow(BFWindow* parent, const StringImpl& title, int x, int y, int width, int height, int windowFlags) { AutoCrit autoCrit(mCritSect); BFWindow* aWindow = new WinBFWindow(parent, title, x, y, width, height, windowFlags); mWindowList.push_back(aWindow); return aWindow; } void WinBFApp::RehupMouse() { HWND windowAtPoint = ::WindowFromPoint(gLastScreenMouseCoords); for (auto window : mWindowList) { auto winWindow = (WinBFWindow*)window; winWindow->RehupMouseOver(winWindow->mHWnd == windowAtPoint); } } String WinBFApp::EnumerateInputDevices() { if (mDInputManager == NULL) mDInputManager = new DInputManager(); return mDInputManager->EnumerateDevices(); } BFInputDevice* WinBFApp::CreateInputDevice(const StringImpl& guid) { if (mDInputManager == NULL) mDInputManager = new DInputManager(); return mDInputManager->CreateInputDevice(guid); } void WinBFWindow::SetMinimumSize(int minWidth, int minHeight, bool clientSized) { if (clientSized) { DWORD windowFlags = ::GetWindowLong(mHWnd, GWL_STYLE); DWORD windowFlagsEx = ::GetWindowLong(mHWnd, GWL_EXSTYLE); RECT rect = { 0, 0, minWidth, minHeight }; AdjustWindowRectEx(&rect, windowFlags, mMenu != NULL, windowFlagsEx); minWidth = rect.right - rect.left; minHeight = rect.bottom - rect.top; } mMinWidth = minWidth; mMinHeight = minHeight; if (mHWnd != NULL) { RECT windowRect; ::GetWindowRect(mHWnd, &windowRect); bool resized = false; if (windowRect.right - windowRect.left < minWidth) { windowRect.right = windowRect.left + minWidth; resized = true; } if (windowRect.bottom - windowRect.top < minHeight) { windowRect.bottom = windowRect.top + minHeight; resized = true; } if (resized) { ::MoveWindow(mHWnd, windowRect.left, windowRect.top, windowRect.right - windowRect.left, windowRect.bottom - windowRect.top, TRUE); mRenderWindow->Resized(); if (mMovedFunc != NULL) mMovedFunc(this); } } } void WinBFWindow::GetPosition(int* x, int* y, int* width, int* height, int* clientX, int* clientY, int* clientWidth, int* clientHeight) { RECT windowRect; ::GetWindowRect(mHWnd, &windowRect); RECT clientRect; ::GetClientRect(mHWnd, &clientRect); if (clientRect.right <= clientRect.left) return; // TODO: return failure? *x = windowRect.left; *y = windowRect.top; *width = windowRect.right - windowRect.left; *height = windowRect.bottom - windowRect.top; *clientWidth = clientRect.right - clientRect.left; *clientHeight = clientRect.bottom - clientRect.top; POINT startPt = {0, 0}; ::ClientToScreen(mHWnd, &startPt); *clientX = startPt.x; *clientY = startPt.y; } void WinBFWindow::GetPlacement(int* normX, int* normY, int* normWidth, int* normHeight, int* showKind) { WINDOWPLACEMENT wndPlacement = { sizeof(WINDOWPLACEMENT), 0 }; ::GetWindowPlacement(mHWnd, &wndPlacement); *normX = wndPlacement.rcNormalPosition.left; *normY = wndPlacement.rcNormalPosition.top; *normWidth = wndPlacement.rcNormalPosition.right - wndPlacement.rcNormalPosition.left; *normHeight = wndPlacement.rcNormalPosition.bottom - wndPlacement.rcNormalPosition.top; switch (wndPlacement.showCmd) { case SW_SHOWNORMAL: *showKind = ShowKind_ShowNormal; break; case SW_SHOWMINIMIZED: *showKind = ShowKind_ShowMinimized; break; case SW_SHOWMAXIMIZED: *showKind = ShowKind_ShowMaximized; break; default: *showKind = ShowKind_Hide; break; } } void WinBFWindow::Resize(int x, int y, int width, int height, ShowKind showKind) { WINDOWPLACEMENT wndPlacement = { sizeof(WINDOWPLACEMENT), 0 }; ::GetWindowPlacement(mHWnd, &wndPlacement); wndPlacement.showCmd = ToWShow(showKind); wndPlacement.rcNormalPosition.left = x; wndPlacement.rcNormalPosition.top = y; wndPlacement.rcNormalPosition.right = x + width; wndPlacement.rcNormalPosition.bottom = y + height; ::SetWindowPlacement(mHWnd, &wndPlacement); //::MoveWindow(mHWnd, x, y, width, height, FALSE); mRenderWindow->Resized(); if (mMovedFunc != NULL) mMovedFunc(this); } void WinBFApp::PhysSetCursor() { static HCURSOR cursors [] = { ::LoadCursor(NULL, IDC_ARROW), //TODO: mApp->mHandCursor); ::LoadCursor(NULL, IDC_HAND), //TODO: mApp->mDraggingCursor); ::LoadCursor(NULL, IDC_SIZEALL), ::LoadCursor(NULL, IDC_IBEAM), ::LoadCursor(NULL, IDC_NO), ::LoadCursor(NULL, IDC_SIZEALL), ::LoadCursor(NULL, IDC_SIZENESW), ::LoadCursor(NULL, IDC_SIZENS), ::LoadCursor(NULL, IDC_SIZENWSE), ::LoadCursor(NULL, IDC_SIZEWE), ::LoadCursor(NULL, IDC_WAIT), NULL }; ::SetCursor(cursors[mCursor]); } void WinBFWindow::SetClientPosition(int x, int y) { RECT rect; ::GetClientRect(mHWnd, &rect); ::OffsetRect(&rect, x, y); LONG aStyle = ::GetWindowLong(mHWnd, GWL_STYLE); LONG exStyle = ::GetWindowLong(mHWnd, GWL_EXSTYLE); ::AdjustWindowRectEx(&rect, aStyle, mMenu != NULL, exStyle); ::MoveWindow(mHWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, FALSE); if (mMovedFunc != NULL) mMovedFunc(this); } void WinBFWindow::SetMouseVisible(bool isMouseVisible) { mMouseVisible = isMouseVisible; LONG aStyle = ::GetWindowLong(mHWnd, GWL_EXSTYLE); if (!isMouseVisible) aStyle |= WS_EX_TRANSPARENT; else aStyle &= ~WS_EX_TRANSPARENT; ::SetWindowLong(mHWnd, GWL_EXSTYLE, aStyle); } void WinBFWindow::SetAlpha(float alpha, uint32 destAlphaSrcMask, bool isMouseVisible) { if (destAlphaSrcMask != 0) { if (mAlphaMaskBitmap == NULL) { RECT clientRect; GetClientRect(mHWnd, &clientRect); RECT windowRect; GetWindowRect(mHWnd, &windowRect); int aWidth = clientRect.right - clientRect.left; int aHeight = clientRect.bottom - clientRect.top; mAlphaMaskWidth = aWidth; mAlphaMaskHeight = aHeight; mAlphaMaskDC = CreateCompatibleDC(NULL); BITMAPINFO bi = {}; bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bi.bmiHeader.biBitCount = 32; bi.bmiHeader.biWidth = aWidth; bi.bmiHeader.biHeight = -aHeight; bi.bmiHeader.biCompression = BI_RGB; bi.bmiHeader.biPlanes = 1; mAlphaMaskBitmap = CreateDIBSection(mAlphaMaskDC, &bi, DIB_RGB_COLORS, (void**)&mAlphaMaskPixels, NULL, 0); GdiFlush(); } HDC hdc = GetDC(mHWnd); if (hdc) { DXRenderWindow* renderWindow = (DXRenderWindow*) mRenderWindow; renderWindow->CopyBitsTo(mAlphaMaskPixels, mAlphaMaskWidth, mAlphaMaskHeight); //for (int i = 0; i < mAlphaMaskWidth*mAlphaMaskHeight/2; i++) //mAlphaMaskPixels[i] = 0x80FF8000; HGDIOBJ hPrevObj = 0; POINT ptDest = {0, 0}; POINT ptSrc = {0, 0}; SIZE client = {mAlphaMaskWidth, mAlphaMaskHeight}; BLENDFUNCTION blendFunc = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA}; hPrevObj = SelectObject(mAlphaMaskDC, mAlphaMaskBitmap); ClientToScreen(mHWnd, &ptDest); BOOL worked = ::UpdateLayeredWindow(mHWnd, hdc, NULL, &client, mAlphaMaskDC, &ptSrc, 0, &blendFunc, ULW_ALPHA); DWORD error = GetLastError(); SelectObject(mAlphaMaskDC, hPrevObj); ReleaseDC(mHWnd, hdc); } } else { ::SetLayeredWindowAttributes(mHWnd, 0, (int) (alpha * 255), LWA_ALPHA); } SetMouseVisible(isMouseVisible); } void WinBFWindow::CaptureMouse() { //OutputDebugStrF("Wnd %p CaptureMouse", this); ::SetCapture(mHWnd); for (auto window : gBFApp->mWindowList) { if (window == this) continue; for (int i = 0; i < MOUSEBUTTON_MAX; i++) { if (window->mIsMouseDown[i]) { window->mMouseUpFunc(window, -1, -1, i); window->mIsMouseDown[i] = false; } } } } bool WinBFWindow::IsMouseCaptured() { return (mHWnd != NULL) && (GetCapture() == mHWnd); } int WinBFWindow::GetDPI() { if (gGetDpiForWindow != NULL) return (int)gGetDpiForWindow(mHWnd); return 96; // Default DPI } uint32 WinBFApp::GetClipboardFormat(const StringImpl& format) { if (format == "text") return CF_UNICODETEXT; else if (format == "atext") return CF_TEXT; uint32 aFormat; if (mClipboardFormatMap.TryGetValue(format, &aFormat)) return aFormat; aFormat = ::RegisterClipboardFormatA(format.c_str()); mClipboardFormatMap[format] = aFormat; return aFormat; } static String gClipboardData; void* WinBFApp::GetClipboardData(const StringImpl& format, int* size) { HWND aWindow = NULL; if (!mWindowList.empty()) aWindow = ((WinBFWindow*)mWindowList.front())->mHWnd; uint32 aFormat = GetClipboardFormat(format); if (aFormat != 0) { if (OpenClipboard(aWindow)) { HGLOBAL globalHandle = ::GetClipboardData(aFormat); if (globalHandle == NULL) { if (aFormat == CF_UNICODETEXT) { CloseClipboard(); // Return ascii text return (char*)GetClipboardData("atext", size); } CloseClipboard(); *size = 0; return NULL; } *size = (int)::GlobalSize(globalHandle); void* aPtr = ::GlobalLock(globalHandle); if (aFormat == CF_UNICODETEXT) { gClipboardData = UTF8Encode((WCHAR*)aPtr); *size = (int)gClipboardData.length() + 1; } else { gClipboardData.Clear(); gClipboardData.Insert(0, (char*)aPtr, *size); } ::GlobalUnlock(globalHandle); CloseClipboard(); return (void*)gClipboardData.c_str(); } else { *size = -1; return NULL; } } *size = 0; return NULL; } void WinBFApp::ReleaseClipboardData(void* ptr) { } void WinBFApp::SetClipboardData(const StringImpl& format, const void* ptr, int size, bool resetClipboard) { BP_ZONE("WinBFApp::SetClipboardData"); HWND aWindow = NULL; if (!mWindowList.empty()) aWindow = ((WinBFWindow*) mWindowList.front())->mHWnd; uint32 aFormat = GetClipboardFormat(format); if (aFormat != 0) { if (OpenClipboard(aWindow)) { if (resetClipboard) { BP_ZONE("WinBFApp::SetClipboardData:empty"); EmptyClipboard(); } if (format == "text") { BP_ZONE("WinBFApp::SetClipboardData:text"); HGLOBAL globalHandle = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, size * 2); char* data = (char*)GlobalLock(globalHandle); UTF16String wString; // { BP_ZONE("WinBFApp::SetClipboardData:utf8decode"); wString = UTF8Decode((char*)ptr); } memcpy(data, wString.c_str(), size * 2); GlobalUnlock(globalHandle); ::SetClipboardData(CF_UNICODETEXT, globalHandle); } else { HGLOBAL globalHandle = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, size); char* data = (char*)GlobalLock(globalHandle); memcpy(data, ptr, size); GlobalUnlock(globalHandle); ::SetClipboardData(aFormat, globalHandle); } CloseClipboard(); } } } BFMenu* WinBFWindow::AddMenuItem(BFMenu* parent, int insertIdx, const char* text, const char* hotKey, BFSysBitmap* sysBitmap, bool enabled, int checkState, bool radioCheck) { UTF16String lText; if (text != NULL) lText = UTF8Decode(text); UTF16String lHotKey; if (hotKey != NULL) lHotKey = UTF8Decode(hotKey); bool wasEmpty = mMenu->mBFMenuList.size() == 0; if (parent == NULL) parent = mMenu; if ((parent->mBFMenuList.size() == 1) && (((WinBFMenu*) parent->mBFMenuList.front())->mIsPlaceholder)) { // Get rid of placeholder menu item auto placeholderMenuItem = parent->mBFMenuList.front(); RemoveMenuItem(placeholderMenuItem); delete placeholderMenuItem; } WinBFMenu* winBFMenuParent = (WinBFMenu*) parent; WinBFMenu* newMenu = new WinBFMenu(); newMenu->mMenuId = ++WinBFMenu::mMenuCount; newMenu->mParent = parent; //static int allocIdx = 0; //allocIdx++; //OutputDebugStrF("MenuIdx %d %@\n", allocIdx, newMenu); if ((winBFMenuParent != NULL) && (winBFMenuParent->mMenu == NULL)) { winBFMenuParent->mMenu = ::CreateMenu(); mHMenuMap[winBFMenuParent->mMenu] = winBFMenuParent; MENUITEMINFO menuItem; memset(&menuItem, 0, sizeof(MENUITEMINFO)); menuItem.cbSize = sizeof(MENUITEMINFO); menuItem.fMask = MIIM_SUBMENU; menuItem.hSubMenu = winBFMenuParent->mMenu; ::SetMenuItemInfo(((WinBFMenu*) winBFMenuParent->mParent)->mMenu, winBFMenuParent->mMenuId, FALSE, &menuItem); } mMenuIDMap[newMenu->mMenuId] = newMenu; BF_ASSERT(insertIdx <= (int) parent->mBFMenuList.size()); //// UTF16String lCombinedName; MENUITEMINFOW menuItem; memset(&menuItem, 0, sizeof(MENUITEMINFO)); menuItem.cbSize = sizeof(MENUITEMINFO); menuItem.fMask = MIIM_FTYPE | MIIM_ID; if (text != NULL) { menuItem.fMask |= MIIM_STRING; menuItem.fType = MFT_STRING; } else menuItem.fType = MFT_SEPARATOR; menuItem.fState = enabled ? MFS_DEFAULT : MFS_GRAYED; if (checkState == 0) menuItem.fState |= MFS_UNCHECKED; if (checkState == 1) menuItem.fState |= MFS_CHECKED; if (radioCheck) menuItem.fType = MFT_RADIOCHECK; menuItem.wID = newMenu->mMenuId; if (text != NULL) { menuItem.dwTypeData = (WCHAR*)lText.c_str(); } if (hotKey != NULL) { String combinedName = String(text); combinedName += "\t"; if (hotKey[0] == '#') { combinedName += hotKey + 1; } else { combinedName += hotKey; newMenu->ParseHotKey(hotKey); } lCombinedName = UTF8Decode(combinedName); menuItem.dwTypeData = (WCHAR*)lCombinedName.c_str(); } ::InsertMenuItemW(winBFMenuParent->mMenu, insertIdx, TRUE, &menuItem); //////// /*MENUITEMINFO menuItem; memset(&menuItem, 0, sizeof(MENUITEMINFO)); menuItem.cbSize = sizeof(MENUITEMINFO); menuItem.fMask = MIIM_ID; menuItem.wID = newMenu->mMenuId; ::InsertMenuItem(winBFMenuParent->mMenu, insertIdx, TRUE, &menuItem);*/ ModifyMenuItem(newMenu, text, hotKey, sysBitmap, enabled, checkState, radioCheck); parent->mBFMenuList.push_back(newMenu); return newMenu; } void WinBFWindow::ModifyMenuItem(BFMenu* item, const char* text, const char* hotKey, BFSysBitmap* sysBitmap, bool enabled, int checkState, bool radioCheck) { UTF16String lText; if (text != NULL) lText = UTF8Decode(text); UTF16String lHotKey; if (hotKey != NULL) lHotKey = UTF8Decode(hotKey); WinBFMenu* aMenu = (WinBFMenu*)item; WinBFMenu* parentMenu = (WinBFMenu*) item->mParent; UTF16String lCombinedName; MENUITEMINFOW menuItem; memset(&menuItem, 0, sizeof(MENUITEMINFO)); menuItem.cbSize = sizeof(MENUITEMINFO); menuItem.fMask = MIIM_FTYPE | MIIM_ID; ::GetMenuItemInfoW(parentMenu->mMenu, aMenu->mMenuId, FALSE, &menuItem); menuItem.fMask |= MIIM_STATE; if (text != NULL) { menuItem.fMask |= MIIM_STRING; //menuItem.fType = MFT_STRING; } /*else menuItem.fType = MFT_SEPARATOR;*/ menuItem.fState = enabled ? /*MFS_DEFAULT*/0 : MFS_GRAYED; if (checkState != -1) { menuItem.fMask |= MIIM_CHECKMARKS; } if (checkState == 0) menuItem.fState |= MFS_UNCHECKED; if (checkState == 1) menuItem.fState |= MFS_CHECKED; if (radioCheck) menuItem.fType = MFT_RADIOCHECK; menuItem.wID = aMenu->mMenuId; menuItem.dwTypeData = (WCHAR*)lText.c_str(); if (hotKey != NULL) { menuItem.fMask |= MIIM_DATA; String combinedName = String(text); combinedName += "\t"; if (hotKey[0] == '#') { combinedName += hotKey + 1; } else { combinedName += hotKey; aMenu->ParseHotKey(hotKey); } lCombinedName = UTF8Decode(combinedName); menuItem.dwTypeData = (WCHAR*)lCombinedName.c_str(); } ::SetMenuItemInfoW(parentMenu->mMenu, aMenu->mMenuId, FALSE, &menuItem); } void WinBFWindow::RemoveMenuItem(BFMenu* item) { WinBFMenu* aMenu = (WinBFMenu*) item; WinBFMenu* parentMenu = (WinBFMenu*) item->mParent; if (parentMenu != NULL) parentMenu->mBFMenuList.Remove(item); mMenuIDMap.Remove(aMenu->mMenuId); ::RemoveMenu(parentMenu->mMenu, aMenu->mMenuId, MF_BYCOMMAND); } BFSysBitmap* WinBFApp::LoadSysBitmap(const WCHAR* fileName) { return NULL; } BFSoundManager* WinBFApp::GetSoundManager() { if (mDSoundManager == NULL) mDSoundManager = new DSoundManager(NULL); return mDSoundManager; } intptr WinBFApp::GetCriticalThreadId(int idx) { if (idx == 0) return mVSyncThreadId; return 0; } void WinBFWindow::ModalsRemoved() { ::EnableWindow(mHWnd, TRUE); if (mChildren.empty()) ::SetFocus(mHWnd); } DrawLayer* WinBFApp::CreateDrawLayer(BFWindow* window) { DXDrawLayer* drawLayer = new DXDrawLayer(); if (window != NULL) { drawLayer->mRenderWindow = window->mRenderWindow; window->mRenderWindow->mDrawLayerList.push_back(drawLayer); } drawLayer->mRenderDevice = mRenderDevice; return drawLayer; }