#include "CmCursor.h" #include "CmRenderWindow.h" #include "CmWindowEventUtilities.h" #include "CmPixelUtil.h" #include "CmApplication.h" #include namespace CamelotFramework { struct NativeCursorData::Pimpl { HCURSOR cursor; }; NativeCursorData::NativeCursorData() { data = cm_new(); } NativeCursorData::~NativeCursorData() { cm_delete(data); } bool Cursor::mIsHidden = false; NativeCursorData Cursor::mCursor; bool Cursor::mUsingCustom = false; void Cursor::setPosition(const Int2& screenPos) { SetCursorPos(screenPos.x, screenPos.y); } void Cursor::hide() { mIsHidden = true; // ShowCursor(FALSE) doesn't work. Presumably because we're in the wrong thread, and using // WM_SETCURSOR in message loop to hide the cursor is smarter solution anyway. RenderWindowPtr primaryWindow = gApplication().getPrimaryWindow(); HWND hwnd; primaryWindow->getCustomAttribute("WINDOW", &hwnd); PostMessage(hwnd, WM_SETCURSOR, WPARAM(hwnd), (LPARAM)MAKELONG(HTCLIENT, WM_MOUSEMOVE)); } void Cursor::show() { mIsHidden = false; // ShowCursor(FALSE) doesn't work. Presumably because we're in the wrong thread, and using // WM_SETCURSOR in message loop to hide the cursor is smarter solution anyway. RenderWindowPtr primaryWindow = gApplication().getPrimaryWindow(); HWND hwnd; primaryWindow->getCustomAttribute("WINDOW", &hwnd); PostMessage(hwnd, WM_SETCURSOR, WPARAM(hwnd), (LPARAM)MAKELONG(HTCLIENT, WM_MOUSEMOVE)); } void Cursor::clipToWindow(const RenderWindow& window) { HWND hwnd; window.getCustomAttribute("WINDOW", &hwnd); // Clip cursor to the window RECT clipWindowRect; if(GetWindowRect(hwnd, &clipWindowRect)) { ClipCursor(&clipWindowRect); } } void Cursor::clipToRect(const Rect& screenRect) { RECT clipWindowRect; clipWindowRect.left = screenRect.x; clipWindowRect.top = screenRect.y; clipWindowRect.right = screenRect.x + screenRect.width; clipWindowRect.bottom = screenRect.y + screenRect.height; ClipCursor(&clipWindowRect); } void Cursor::clipDisable() { ClipCursor(NULL); } void Cursor::setCursor(CursorType type) { if(mUsingCustom) { SetCursor(0); DestroyIcon(mCursor.data->cursor); mUsingCustom = false; } switch(type) { case CursorType::Arrow: mCursor.data->cursor = LoadCursor(0, IDC_ARROW); break; case CursorType::Wait: mCursor.data->cursor = LoadCursor(0, IDC_WAIT); break; case CursorType::IBeam: mCursor.data->cursor = LoadCursor(0, IDC_IBEAM); break; case CursorType::Help: mCursor.data->cursor = LoadCursor(0, IDC_HELP); break; case CursorType::Hand: mCursor.data->cursor = LoadCursor(0, IDC_HAND); break; case CursorType::SizeAll: mCursor.data->cursor = LoadCursor(0, IDC_SIZEALL); break; case CursorType::SizeNESW: mCursor.data->cursor = LoadCursor(0, IDC_SIZENESW); break; case CursorType::SizeNS: mCursor.data->cursor = LoadCursor(0, IDC_SIZENS); break; case CursorType::SizeNWSE: mCursor.data->cursor = LoadCursor(0, IDC_SIZENWSE); break; case CursorType::SizeWE: mCursor.data->cursor = LoadCursor(0, IDC_SIZEWE); break; } // Make sure we notify the message loop to perform the actual cursor update RenderWindowPtr primaryWindow = gApplication().getPrimaryWindow(); HWND hwnd; primaryWindow->getCustomAttribute("WINDOW", &hwnd); PostMessage(hwnd, WM_SETCURSOR, WPARAM(hwnd), (LPARAM)MAKELONG(HTCLIENT, WM_MOUSEMOVE)); } // TODO - Add support for animated custom cursor void Cursor::setCustomCursor(PixelData& pixelData, const Int2& hotSpot) { if(mUsingCustom) { SetCursor(0); DestroyIcon(mCursor.data->cursor); } mUsingCustom = true; BITMAPV5HEADER bi; ZeroMemory(&bi,sizeof(BITMAPV5HEADER)); bi.bV5Size = sizeof(BITMAPV5HEADER); bi.bV5Width = pixelData.getWidth(); bi.bV5Height = pixelData.getHeight(); bi.bV5Planes = 1; bi.bV5BitCount = 32; bi.bV5Compression = BI_BITFIELDS; bi.bV5RedMask = 0x00FF0000; bi.bV5GreenMask = 0x0000FF00; bi.bV5BlueMask = 0x000000FF; bi.bV5AlphaMask = 0xFF000000; HDC hDC = GetDC(NULL); void* data = nullptr; HBITMAP hBitmap = CreateDIBSection(hDC, (BITMAPINFO *)&bi, DIB_RGB_COLORS, (void**)&data, NULL, (DWORD)0); HDC hBitmapDC = CreateCompatibleDC(hDC); ReleaseDC(NULL, hDC); // Create an empty mask bitmap. HBITMAP hMonoBitmap = CreateBitmap(pixelData.getWidth(), pixelData.getHeight(), 1, 1, NULL); //Select the bitmaps to DC HBITMAP hOldBitmap = (HBITMAP)SelectObject(hBitmapDC, hBitmap); //Scan each pixel of the source bitmap and create the masks Color pixel; DWORD *dst = (DWORD*)data; for(UINT32 y = 0; y < pixelData.getHeight(); ++y) { for(UINT32 x = 0; x < pixelData.getWidth(); ++x) { pixel = pixelData.getColorAt(x, pixelData.getHeight() - y - 1); *dst = pixel.getAsBGRA(); dst++; } } SelectObject(hBitmapDC, hOldBitmap); DeleteDC(hBitmapDC); ICONINFO iconinfo = {0}; iconinfo.fIcon = FALSE; iconinfo.xHotspot = (DWORD)hotSpot.x; iconinfo.yHotspot = (DWORD)hotSpot.y; iconinfo.hbmMask = hMonoBitmap; iconinfo.hbmColor = hBitmap; mCursor.data->cursor = CreateIconIndirect(&iconinfo); DeleteObject(hBitmap); DeleteObject(hMonoBitmap); // Make sure we notify the message loop to perform the actual cursor update RenderWindowPtr primaryWindow = gApplication().getPrimaryWindow(); HWND hwnd; primaryWindow->getCustomAttribute("WINDOW", &hwnd); PostMessage(hwnd, WM_SETCURSOR, WPARAM(hwnd), (LPARAM)MAKELONG(HTCLIENT, WM_MOUSEMOVE)); } void Cursor::_win32ShowCursor() { SetCursor(mCursor.data->cursor); } void Cursor::_win32HideCursor() { SetCursor(nullptr); } }