// Ryzom - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #include "stdpch.h" #include "custom_mouse.h" // #include "nel/misc/file.h" #include "../global.h" #include "../input.h" #include "input_handler_manager.h" CCustomMouse CustomMouse; using namespace NLMISC; using namespace NL3D; #ifdef NL_OS_WINDOWS //************************************************************************************* CCustomMouse::CCursor::CCursor() : ColorDepth(CCustomMouse::ColorDepth32), OrigHeight(32), HotspotScale(1.f), HotspotOffsetX(0), HotspotOffsetY(0), HotSpotX(0), HotSpotY(0), Icon(0), Col(CRGBA::White), Rot(0) { } //************************************************************************************* CCustomMouse::CCursor::~CCursor() { if (Icon) { DestroyIcon(Icon); } } //************************************************************************************* CCustomMouse::CCustomMouse() { _ColorDepth = CCustomMouse::ColorDepth32; _DefaultCursor = LoadCursor(NULL, IDC_ARROW); _AlphaBlendedCursorSupported = false; _AlphaBlendedCursorSupportRetrieved = false; _CurrCol = CRGBA::White; _CurrRot = 0; _CurrHotSpotX = 0; _CurrHotSpotY = 0; } //************************************************************************************* bool CCustomMouse::isAlphaBlendedCursorSupported() { if (!_AlphaBlendedCursorSupportRetrieved) { // Support starts with windows 2000 (not only from XP as seen in most docs) // NB : Additionnaly, could query D3D caps to know if // color hardware cursor is supported, not only emulated, // but can't be sure that using the win32 api 'SetCursor' uses the same resources // So far, seems to be supported on any modern card used by the game anyway ... OSVERSIONINFO osvi; osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); if (GetVersionEx(&osvi)) { _AlphaBlendedCursorSupported = (osvi.dwMajorVersion >= 5); } _AlphaBlendedCursorSupportRetrieved = true; } return _AlphaBlendedCursorSupported; } bool VerboseCursorRT12516 = true; namespace NLMISC { extern bool TempMaxVerboseResample; } //************************************************************************************* void CCustomMouse::addCursor(const std::string &name, const NLMISC::CBitmap &cursorBitmap) { if (!isAlphaBlendedCursorSupported()) return; nlassert(cursorBitmap.getWidth() != 0); nlassert(cursorBitmap.getHeight() != 0); // find used part base on alpha, to avoid too much shrinking const CRGBA *pixels = (const CRGBA *) &cursorBitmap.getPixels()[0]; uint minX, maxX, minY, maxY; uint width = cursorBitmap.getWidth(); uint height = cursorBitmap.getHeight(); // minX = 0; for (uint x = 0; x < width; ++x) { bool stop = false; minX = x; for (uint y = 0; y < height; ++y) { if(pixels[x + y * width].A != 0) { stop = true; break; } } if (stop) break; } // maxX = width - 1; for (sint x = width - 1; x >= 0; --x) { bool stop = false; maxX = (uint) x; for (uint y = 0; y < height; ++y) { if(pixels[x + y * width].A != 0) { stop = true; break; } } if (stop) break; } // minY = 0; for (uint y = 0; y < height; ++y) { bool stop = false; minY = y; for (uint x = 0; x < width; ++x) { if(pixels[x + y * width].A != 0) { stop = true; break; } } if (stop) break; } // maxY = height - 1; for (sint y = height - 1; y >= 0; --y) { bool stop = false; maxY = (uint) y; for (uint x = 0; x < width; ++x) { if(pixels[x + y * width].A != 0) { stop = true; break; } } if (stop) break; } // CCursor &curs = _Cursors[name]; curs = CCursor(); // erase possible previous cursor uint destWidth = GetSystemMetrics(SM_CXCURSOR); uint destHeight = GetSystemMetrics(SM_CYCURSOR); // build a square bitmap uint tmpSize = std::max(maxX - minX + 1, maxY - minY + 1); curs.Src.resize(tmpSize, tmpSize), // blit at top left corner curs.Src.blit(cursorBitmap, minX, minY, maxX - minX + 1, maxY - minY + 1, 0, 0); curs.OrigHeight = cursorBitmap.getHeight(); curs.HotspotOffsetX = minX; curs.HotspotOffsetY = minY; // curs.HotspotScale = ClientCfg.HardwareCursorScale; clamp(curs.HotspotScale, 0.f, 1.f); // first resampling, same for all cursors tmpSize = (uint) (tmpSize * curs.HotspotScale); if (tmpSize == 0) tmpSize = 1; if (VerboseCursorRT12516 && ((name == "curs_stop.tga") || (name == "curs_pick_dup.tga"))) TempMaxVerboseResample = true; if (TempMaxVerboseResample) { try { //nldebug("RT12516: BEFORE FIRST RESAMPLE"); //nldebug("RT12516: %s: curs=%p curs.Src=%p curs.Src.PixelPtr=%p", name.c_str(), &curs, &(curs.Src), &curs.Src.getPixels(0)[0]); //nldebug("RT12516: %s: curs.Src.PixelSize=%u", name.c_str(), curs.Src.getPixels(0).size()); } catch (...) { //nldebug("RT12516: An exception occurred!"); } } // TMP for RT 12406 /* nlwarning("Resampling mouse %s cursor : initial size = %d x %d, new size = %d x %d", name.c_str(), curs.Src.getWidth(), curs.Src.getHeight(), tmpSize, tmpSize );*/ curs.Src.resample(tmpSize, tmpSize); if (TempMaxVerboseResample) { try { //nldebug("RT12516: AFTER FIRST RESAMPLE"); //nldebug("RT12516: %s: curs=%p curs.Src=%p curs.Src.PixelPtr=%p", name.c_str(), &curs, &(curs.Src), &curs.Src.getPixels(0)[0]); //nldebug("RT12516: %s: curs.Src.PixelSize=%u", name.c_str(), curs.Src.getPixels(0).size()); } catch (...) { //nldebug("RT12516: An exception occurred!"); } } // shrink if necessary if (tmpSize > destWidth || tmpSize > destHeight) // need to shrink ? { // constraint proportions curs.HotspotScale *= std::min(float(destWidth) / tmpSize, float(destHeight) / tmpSize); // TMP for RT 12406 /* nlwarning("Resampling mouse %s cursor : initial size = %d x %d, new size = %d x %d", name.c_str(), curs.Src.getWidth(), curs.Src.getHeight(), destWidth, destHeight );*/ curs.Src.resample(destWidth, destHeight); } else { CBitmap final; final.resize(destWidth, destHeight); final.blit(&curs.Src, 0, 0); curs.Src.swap(final); } if (TempMaxVerboseResample) { try { //nldebug("RT12516: AFTER SECOND RESAMPLE"); //nldebug("RT12516: %s: curs=%p curs.Src=%p curs.Src.PixelPtr=%p", name.c_str(), &curs, &(curs.Src), &curs.Src.getPixels(0)[0]); //nldebug("RT12516: %s: curs.Src.PixelSize=%u", name.c_str(), curs.Src.getPixels(0).size()); } catch (...) { //nldebug("RT12516: An exception occurred!"); } } if (name == _CurrName) { updateCursor(); } TempMaxVerboseResample = false; } //************************************************************************************* void CCustomMouse::release() { if (!isAlphaBlendedCursorSupported()) return; nlassert(Driver); HWND drvWnd = Driver->getDisplay(); if (drvWnd) { SetClassLongPtr(drvWnd, GCLP_HCURSOR, 0); } _Cursors.clear(); } //************************************************************************************* void CCustomMouse::setColorDepth(TColorDepth colorDepth) { if (colorDepth == _ColorDepth) return; _ColorDepth = colorDepth; updateCursor(true); } //************************************************************************************* void CCustomMouse::updateCursor(bool forceRebuild) { if (!Driver) return; setCursor(_CurrName, _CurrCol, _CurrRot, _CurrHotSpotX, _CurrHotSpotY, forceRebuild); } //************************************************************************************* void CCustomMouse::setCursor(const std::string &name, NLMISC::CRGBA col, uint8 rot, sint hotSpotX, sint hotSpotY, bool forceRebuild) { if (!isAlphaBlendedCursorSupported()) return; _CurrName = name; _CurrCol = col; _CurrRot = rot; _CurrHotSpotX = hotSpotX; _CurrHotSpotY = hotSpotY; // if (rot > 3) rot = 3; // same than 'CViewRenderer::drawRotFlipBitmapTiled TIconMap::iterator it = _Cursors.find(name); HCURSOR cursorHandle = _DefaultCursor; if (it != _Cursors.end()) { // Update cursor if modified or not already built CCursor &curs = it->second; hotSpotX = (sint) (curs.HotspotScale * (hotSpotX - curs.HotspotOffsetX)); hotSpotY = (sint) (curs.HotspotScale * ((curs.OrigHeight - hotSpotY) - curs.HotspotOffsetY)); if (curs.Icon == 0 || curs.HotSpotX != hotSpotX || curs.HotSpotY != hotSpotY || curs.Col != col || curs.Rot != rot || curs.ColorDepth != _ColorDepth || forceRebuild ) { if (curs.Icon != 0) { DestroyIcon(curs.Icon); } curs.Icon = buildCursor(curs.Src, col, rot, hotSpotX, hotSpotY); curs.Col = col; curs.Rot = rot; curs.HotSpotX = hotSpotX; curs.HotSpotY = hotSpotY; curs.ColorDepth = _ColorDepth; } cursorHandle = curs.Icon ? (HCURSOR) curs.Icon : _DefaultCursor; } if (IsSystemCursorInClientArea() || IsSystemCursorCaptured() || forceRebuild) { if (CInputHandlerManager::getInstance()->hasFocus()) { ::SetCursor(cursorHandle); HWND drvWnd = Driver->getDisplay(); if (drvWnd) { SetClassLongPtr(drvWnd, GCLP_HCURSOR, (LONG_PTR) cursorHandle); // set default mouse icon to the last one } } } } //************************************************************************************* HICON CCustomMouse::buildCursor(const CBitmap &src, NLMISC::CRGBA col, uint8 rot, sint hotSpotX, sint hotSpotY) { nlassert(isAlphaBlendedCursorSupported()); uint mouseW = GetSystemMetrics(SM_CXCURSOR); uint mouseH = GetSystemMetrics(SM_CYCURSOR); nlassert(src.getWidth() == mouseW); nlassert(src.getHeight() == mouseH); HICON result = 0; CBitmap rotSrc = src; if (rot > 3) rot = 3; // mimic behavior of 'CViewRenderer::drawRotFlipBitmapTiled' (why not rot & 3 ??? ...) switch(rot) { case 0: break; case 1: rotSrc.rot90CW(); break; case 2: rotSrc.rot90CW(); rotSrc.rot90CW(); break; case 3: rotSrc.rot90CCW(); break; } CBitmap colorBm; colorBm.resize(mouseW, mouseH, CBitmap::RGBA); const CRGBA *srcColorPtr = (CRGBA *) &(rotSrc.getPixels()[0]); const CRGBA *srcColorPtrLast = srcColorPtr + (mouseW * mouseH); CRGBA *destColorPtr = (CRGBA *) &(colorBm.getPixels()[0]); static volatile uint8 alphaThreshold = 127; do { destColorPtr->modulateFromColor(*srcColorPtr, col); std::swap(destColorPtr->R, destColorPtr->B); ++ srcColorPtr; ++ destColorPtr; } while (srcColorPtr != srcColorPtrLast); // HBITMAP colorHbm = 0; HBITMAP maskHbm = 0; // if (_ColorDepth == ColorDepth16) { std::vector colorBm16(colorBm.getWidth() * colorBm.getHeight()); const CRGBA *src32 = (const CRGBA *) &colorBm.getPixels(0)[0]; for (uint k = 0;k < colorBm16.size(); ++k) { colorBm16[k] = ((uint16)(src32[k].R&0xf8)>>3) | ((uint16)(src32[k].G&0xfc)<<3) | ((uint16)(src32[k].B & 0xf8)<<8); } colorHbm = CreateBitmap(mouseW, mouseH, 1, 16, &colorBm16[0]); std::vector bitMask((colorBm.getWidth() * colorBm.getHeight() + 7) / 8, 0); for (uint k = 0;k < colorBm16.size(); ++k) { if (src32[k].A <= 120) { bitMask[k / 8] |= (0x80 >> (k & 7)); } } maskHbm = CreateBitmap(mouseW, mouseH, 1, 1, &bitMask[0]); } else { colorHbm = CreateBitmap(mouseW, mouseH, 1, 32, &colorBm.getPixels(0)[0]); maskHbm = CreateBitmap(mouseW, mouseH, 1, 32, &colorBm.getPixels(0)[0]); } ICONINFO iconInfo; iconInfo.fIcon = FALSE; iconInfo.xHotspot = (DWORD) hotSpotX; iconInfo.yHotspot = (DWORD) hotSpotY; iconInfo.hbmMask = maskHbm; iconInfo.hbmColor = colorHbm; if (colorHbm && maskHbm) { result = CreateIconIndirect(&iconInfo); } // if (colorHbm) DeleteObject(colorHbm); if (maskHbm) DeleteObject(maskHbm); return result; } //************************************************************************************* void CCustomMouse::setSystemArrow() { extern HINSTANCE HInstance; HCURSOR arrow = LoadCursor(NULL, IDC_ARROW); if (IsSystemCursorInClientArea() || IsSystemCursorCaptured()) { ::SetCursor(arrow); } HWND drvWnd = Driver->getDisplay(); if (drvWnd) { SetClassLongPtr(drvWnd, GCLP_HCURSOR, (LONG_PTR) arrow); // set default mouse icon to the last one } } #else // not implemented yet for other OS //************************************************************************************* CCustomMouse::CCustomMouse() { // NOT IMPLEMENTED } //************************************************************************************* void CCustomMouse::setCursor(const std::string &name, NLMISC::CRGBA col, uint8 rot, sint hotSpotX, sint hotSpotY, bool forceRebuild) { // NOT IMPLEMENTED } //************************************************************************************* void CCustomMouse::release() { // NOT IMPLEMENTED } //************************************************************************************* bool CCustomMouse::isAlphaBlendedCursorSupported() { return false; } //************************************************************************************* void CCustomMouse::setSystemArrow() { // } void CCustomMouse::addCursor(const std::string &name, const NLMISC::CBitmap &cursorBitmap) { // TODO for Linux } //************************************************************************************* void CCustomMouse::setColorDepth(TColorDepth colorDepth) { // TODO for Linux } //************************************************************************************* void CCustomMouse::updateCursor(bool forceRebuild) { // TODO for Linux } #endif // NL_OS_WINDOWS