Merge branch 'main/rendor-staging' into fixes
[ryzomcore.git] / nel / src / 3d / driver / direct3d / driver_direct3d_inputs.cpp
bloba18bb3a6e90273861824240278b7fdd2dcaeba50
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010-2020 Winch Gate Property Limited
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17 #include "stddirect3d.h"
18 #include "driver_direct3d.h"
20 #ifdef DEBUG_NEW
21 #define new DEBUG_NEW
22 #endif
24 using namespace std;
25 using namespace NLMISC;
27 namespace NL3D
30 // *************************************************************************************
31 CDriverD3D::CCursor::CCursor() : ColorDepth(CDriverD3D::ColorDepth32),
32 OrigHeight(32),
33 HotspotScale(1.f),
34 HotspotOffsetX(0),
35 HotspotOffsetY(0),
36 HotSpotX(0),
37 HotSpotY(0),
38 Cursor(EmptyCursor),
39 Col(CRGBA::White),
40 Rot(0)
44 // *************************************************************************************
45 CDriverD3D::CCursor::~CCursor()
47 reset();
50 // *************************************************************************************
51 void CDriverD3D::CCursor::reset()
53 if (Cursor != EmptyCursor)
55 DestroyIcon(Cursor);
59 // *************************************************************************************
60 CDriverD3D::CCursor& CDriverD3D::CCursor::operator= (const CDriverD3D::CCursor& from)
62 if (&from == this)
63 return *this;
64 Src = from.Src; // requires more than a surface copy
65 OrigHeight = from.OrigHeight;
66 HotspotScale = from.HotspotScale;
67 HotspotOffsetX = from.HotspotOffsetX;
68 HotspotOffsetY = from.HotspotOffsetY;
69 HotSpotX = from.HotSpotX;
70 HotSpotY = from.HotSpotY;
71 Cursor = from.Cursor;
72 Col = from.Col;
73 Rot = from.Rot;
74 return *this;
77 // *************************************************************************************
78 bool CDriverD3D::isAlphaBlendedCursorSupported()
80 if (!_AlphaBlendedCursorSupportRetrieved)
82 // Support starts with windows 2000 (not only from XP as seen in most docs)
83 // NB : Additionnaly, could query D3D caps to know if
84 // color hardware cursor is supported, not only emulated,
85 // but can't be sure that using the win32 api 'SetCursor' uses the same resources
86 // So far, seems to be supported on any modern card used by the game anyway ...
87 OSVERSIONINFO osvi;
88 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
89 if (GetVersionEx(&osvi))
91 _AlphaBlendedCursorSupported = (osvi.dwMajorVersion >= 5);
94 _AlphaBlendedCursorSupportRetrieved = true;
97 return _AlphaBlendedCursorSupported;
100 // *************************************************************************************
101 void CDriverD3D::addCursor(const std::string &name, const NLMISC::CBitmap &cursorBitmap)
103 if (!isAlphaBlendedCursorSupported()) return;
105 nlassert(cursorBitmap.getWidth() != 0);
106 nlassert(cursorBitmap.getHeight() != 0);
108 // find used part base on alpha, to avoid too much shrinking
109 const CRGBA *pixels = (const CRGBA *) &cursorBitmap.getPixels()[0];
110 uint minX, maxX, minY, maxY;
111 uint width = cursorBitmap.getWidth();
112 uint height = cursorBitmap.getHeight();
114 minX = 0;
115 for (uint x = 0; x < width; ++x)
117 bool stop = false;
118 minX = x;
119 for (uint y = 0; y < height; ++y)
121 if(pixels[x + y * width].A != 0)
123 stop = true;
124 break;
127 if (stop) break;
130 maxX = width - 1;
131 for (sint x = width - 1; x >= 0; --x)
133 bool stop = false;
134 maxX = (uint) x;
135 for (uint y = 0; y < height; ++y)
137 if(pixels[x + y * width].A != 0)
139 stop = true;
140 break;
143 if (stop) break;
146 minY = 0;
147 for (uint y = 0; y < height; ++y)
149 bool stop = false;
150 minY = y;
151 for (uint x = 0; x < width; ++x)
153 if(pixels[x + y * width].A != 0)
155 stop = true;
156 break;
159 if (stop) break;
162 maxY = height - 1;
163 for (sint y = height - 1; y >= 0; --y)
165 bool stop = false;
166 maxY = (uint) y;
167 for (uint x = 0; x < width; ++x)
169 if(pixels[x + y * width].A != 0)
171 stop = true;
172 break;
175 if (stop) break;
178 CCursor &curs = _Cursors[name];
179 curs = CCursor(); // erase possible previous cursor
181 uint destWidth = GetSystemMetrics(SM_CXCURSOR);
182 uint destHeight = GetSystemMetrics(SM_CYCURSOR);
184 // build a square bitmap
185 uint tmpSize = std::max(maxX - minX + 1, maxY - minY + 1);
186 curs.Src.resize(tmpSize, tmpSize);
187 // blit at top left corner
188 curs.Src.blit(cursorBitmap, minX, minY, maxX - minX + 1, maxY - minY + 1, 0, 0);
190 curs.OrigHeight = cursorBitmap.getHeight();
191 curs.HotspotOffsetX = minX;
192 curs.HotspotOffsetY = minY;
194 curs.HotspotScale = _CursorScale;
195 clamp(curs.HotspotScale, 0.f, 1.f);
196 // first resampling, same for all cursors
197 tmpSize = (uint) (tmpSize * curs.HotspotScale);
198 if (tmpSize == 0) tmpSize = 1;
200 if (curs.HotspotScale < 1.f)
202 curs.Src.resample(tmpSize, tmpSize);
205 // shrink if necessary
206 if (tmpSize > destWidth || tmpSize > destHeight) // need to shrink ?
208 // constraint proportions
209 curs.HotspotScale *= std::min(float(destWidth) / tmpSize, float(destHeight) / tmpSize);
210 curs.Src.resample(destWidth, destHeight);
212 else
214 CBitmap final;
215 final.resize(destWidth, destHeight);
216 final.blit(&curs.Src, 0, 0);
217 curs.Src.swap(final);
220 if (name == _CurrName)
222 updateCursor();
226 // *************************************************************************************
227 void CDriverD3D::createCursors()
229 _DefaultCursor = LoadCursor(NULL, IDC_ARROW);
232 // *************************************************************************************
233 void CDriverD3D::releaseCursors()
235 SetClassLongPtr(_HWnd, GCLP_HCURSOR, 0);
237 _Cursors.clear();
240 // *************************************************************************************
241 void CDriverD3D::updateCursor(bool forceRebuild)
243 setCursor(_CurrName, _CurrCol, _CurrRot, _CurrHotSpotX, _CurrHotSpotY, forceRebuild);
246 // *************************************************************************************
247 void CDriverD3D::setCursor(const std::string &name, NLMISC::CRGBA col, uint8 rot, sint hotSpotX, sint hotSpotY, bool forceRebuild)
249 // don't update cursor if it's hidden or if custom cursors are not suppported
250 if (!isAlphaBlendedCursorSupported() || _CurrName == "none") return;
252 _CurrName = name;
253 _CurrCol = col;
254 _CurrRot = rot;
255 _CurrHotSpotX = hotSpotX;
256 _CurrHotSpotY = hotSpotY;
258 // cursor has to be changed next time
259 if (_CurrName.empty()) return;
261 if (rot > 3) rot = 3; // same than 'CViewRenderer::drawRotFlipBitmapTiled
263 TCursorMap::iterator it = _Cursors.find(name);
265 nlCursor cursorHandle = _DefaultCursor;
267 if (it != _Cursors.end())
269 // Update cursor if modified or not already built
270 CCursor &curs = it->second;
271 hotSpotX = (sint) (curs.HotspotScale * (hotSpotX - curs.HotspotOffsetX));
272 hotSpotY = (sint) (curs.HotspotScale * ((curs.OrigHeight - hotSpotY) - curs.HotspotOffsetY));
273 if (curs.Cursor == EmptyCursor ||
274 curs.HotSpotX != hotSpotX ||
275 curs.HotSpotY != hotSpotY ||
276 curs.Col != col ||
277 curs.Rot != rot ||
278 curs.ColorDepth != _ColorDepth ||
279 forceRebuild
282 curs.reset();
283 curs.Cursor = buildCursor(curs.Src, col, rot, hotSpotX, hotSpotY);
284 curs.Col = col;
285 curs.Rot = rot;
286 curs.HotSpotX = hotSpotX;
287 curs.HotSpotY = hotSpotY;
288 curs.ColorDepth = _ColorDepth;
290 cursorHandle = curs.Cursor ? curs.Cursor : _DefaultCursor;
293 if (isSystemCursorInClientArea() || isSystemCursorCaptured() || forceRebuild)
295 // if (CInputHandlerManager::getInstance()->hasFocus())
297 ::SetCursor(cursorHandle);
298 SetClassLongPtr(_HWnd, GCLP_HCURSOR, (LONG_PTR) cursorHandle); // set default mouse icon to the last one
304 // *************************************************************************************
305 void CDriverD3D::setCursorScale(float scale)
307 _CursorScale = scale;
310 // *************************************************************************************
311 nlCursor CDriverD3D::buildCursor(const CBitmap &src, NLMISC::CRGBA col, uint8 rot, sint hotSpotX, sint hotSpotY)
313 nlassert(isAlphaBlendedCursorSupported());
315 uint mouseW = GetSystemMetrics(SM_CXCURSOR);
316 uint mouseH = GetSystemMetrics(SM_CYCURSOR);
318 CBitmap rotSrc = src;
319 if (rot > 3) rot = 3; // mimic behavior of 'CViewRenderer::drawRotFlipBitmapTiled' (why not rot & 3 ??? ...)
320 switch(rot)
322 case 0: break;
323 case 1: rotSrc.rot90CW(); break;
324 case 2: rotSrc.rot90CW(); rotSrc.rot90CW(); break;
325 case 3: rotSrc.rot90CCW(); break;
328 // create a cursor from bitmap
329 nlCursor result = NULL;
330 convertBitmapToCursor(rotSrc, result, mouseW, mouseH, _ColorDepth == ColorDepth16 ? 16:32, col, hotSpotX, hotSpotY);
331 return result;
335 // *************************************************************************************
336 void CDriverD3D::setSystemArrow()
338 H_AUTO_D3D(CDriverD3D_setSystemArrow);
340 if (isSystemCursorInClientArea() || isSystemCursorCaptured())
342 SetCursor(_DefaultCursor);
345 // set default mouse icon to the default one
346 SetClassLongPtr(_HWnd, GCLP_HCURSOR, (LONG_PTR) _DefaultCursor);
349 // ***************************************************************************
350 void CDriverD3D::showCursor(bool b)
352 H_AUTO_D3D(CDriverD3D_showCursor);
354 if (_HWnd == EmptyWindow)
355 return;
357 if (b)
359 // update current hardware icon to avoid to have the plain arrow
360 updateCursor(true);
362 while (ShowCursor(b) < 0)
365 else
367 while (ShowCursor(b) >= 0)
372 // ***************************************************************************
373 void CDriverD3D::setMousePos(float x, float y)
375 H_AUTO_D3D(CDriverD3D_setMousePos);
377 if (_HWnd == EmptyWindow || !_WindowFocus)
378 return;
380 // convert position size from float to pixels
381 sint x1 = (sint)((float)_CurrentMode.Width*x);
382 sint y1 = (sint)((float)_CurrentMode.Height*(1.0f-y));
384 // NeL window coordinate to MSWindows coordinates
385 POINT pt;
386 pt.x = x1;
387 pt.y = y1;
388 ClientToScreen (_HWnd, &pt);
389 SetCursorPos(pt.x, pt.y);
392 // ***************************************************************************
393 void CDriverD3D::setCapture (bool b)
395 H_AUTO_D3D(CDriverD3D_setCapture);
397 if (b && isSystemCursorInClientArea() && !isSystemCursorCaptured())
399 SetCapture(_HWnd);
401 else if (!b && isSystemCursorCaptured())
403 // if hardware mouse and not in client area, then force to update its aspect by updating its pos
404 if (!isSystemCursorInClientArea())
406 // force update
407 showCursor(true);
410 ReleaseCapture();
414 // ***************************************************************************
415 bool CDriverD3D::isSystemCursorInClientArea()
417 if (_FullScreen /* || !IsMouseCursorHardware() */)
419 return IsWindowVisible(_HWnd) != FALSE;
421 else
423 POINT cursPos;
424 // the mouse should be in the client area of the window
425 if (!GetCursorPos(&cursPos))
427 return false;
429 HWND wnd = WindowFromPoint(cursPos);
430 if (wnd != _HWnd)
432 return false; // not the same window
434 // want that the mouse be in the client area
435 RECT clientRect;
436 if (!GetClientRect(_HWnd, &clientRect))
438 return false;
440 POINT tl, br;
441 tl.x = clientRect.left;
442 tl.y = clientRect.top;
443 br.x = clientRect.right;
444 br.y = clientRect.bottom;
445 if (!ClientToScreen(_HWnd, &tl))
447 return false;
449 if (!ClientToScreen(_HWnd, &br))
451 return false;
453 if ((cursPos.x < tl.x) || (cursPos.x >= br.x) || (cursPos.y < tl.y) || (cursPos.y >= br.y))
455 return false;
459 return true;
462 // ***************************************************************************
463 bool CDriverD3D::isSystemCursorCaptured()
465 H_AUTO_D3D(CDriverD3D_isSystemCursorCaptured);
467 return GetCapture() == _HWnd;
470 bool CDriverD3D::convertBitmapToCursor(const NLMISC::CBitmap &bitmap, nlCursor &cursor, uint iconWidth, uint iconHeight, uint iconDepth, const NLMISC::CRGBA &col, sint hotSpotX, sint hotSpotY)
472 return convertBitmapToIcon(bitmap, cursor, iconWidth, iconHeight, iconDepth, col, hotSpotX, hotSpotY, true);
475 } // NL3D