1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010-2020 Winch Gate Property Limited
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.
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"
25 using namespace NLMISC
;
30 // *************************************************************************************
31 CDriverD3D::CCursor::CCursor() : ColorDepth(CDriverD3D::ColorDepth32
),
44 // *************************************************************************************
45 CDriverD3D::CCursor::~CCursor()
50 // *************************************************************************************
51 void CDriverD3D::CCursor::reset()
53 if (Cursor
!= EmptyCursor
)
59 // *************************************************************************************
60 CDriverD3D::CCursor
& CDriverD3D::CCursor::operator= (const CDriverD3D::CCursor
& from
)
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
;
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 ...
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();
115 for (uint x
= 0; x
< width
; ++x
)
119 for (uint y
= 0; y
< height
; ++y
)
121 if(pixels
[x
+ y
* width
].A
!= 0)
131 for (sint x
= width
- 1; x
>= 0; --x
)
135 for (uint y
= 0; y
< height
; ++y
)
137 if(pixels
[x
+ y
* width
].A
!= 0)
147 for (uint y
= 0; y
< height
; ++y
)
151 for (uint x
= 0; x
< width
; ++x
)
153 if(pixels
[x
+ y
* width
].A
!= 0)
163 for (sint y
= height
- 1; y
>= 0; --y
)
167 for (uint x
= 0; x
< width
; ++x
)
169 if(pixels
[x
+ y
* width
].A
!= 0)
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
);
215 final
.resize(destWidth
, destHeight
);
216 final
.blit(&curs
.Src
, 0, 0);
217 curs
.Src
.swap(final
);
220 if (name
== _CurrName
)
226 // *************************************************************************************
227 void CDriverD3D::createCursors()
229 _DefaultCursor
= LoadCursor(NULL
, IDC_ARROW
);
232 // *************************************************************************************
233 void CDriverD3D::releaseCursors()
235 SetClassLongPtr(_HWnd
, GCLP_HCURSOR
, 0);
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;
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
||
278 curs
.ColorDepth
!= _ColorDepth
||
283 curs
.Cursor
= buildCursor(curs
.Src
, col
, rot
, hotSpotX
, hotSpotY
);
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 ??? ...)
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
);
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
)
359 // update current hardware icon to avoid to have the plain arrow
362 while (ShowCursor(b
) < 0)
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
)
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
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())
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())
414 // ***************************************************************************
415 bool CDriverD3D::isSystemCursorInClientArea()
417 if (_FullScreen
/* || !IsMouseCursorHardware() */)
419 return IsWindowVisible(_HWnd
) != FALSE
;
424 // the mouse should be in the client area of the window
425 if (!GetCursorPos(&cursPos
))
429 HWND wnd
= WindowFromPoint(cursPos
);
432 return false; // not the same window
434 // want that the mouse be in the client area
436 if (!GetClientRect(_HWnd
, &clientRect
))
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
))
449 if (!ClientToScreen(_HWnd
, &br
))
453 if ((cursPos
.x
< tl
.x
) || (cursPos
.x
>= br
.x
) || (cursPos
.y
< tl
.y
) || (cursPos
.y
>= br
.y
))
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);