1 // Scintilla source code edit control
3 ** Implementation of platform facilities on Windows.
5 // Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
6 // The License.txt file describes the conditions under which this software may be distributed.
21 #define _WIN32_WINNT 0x0500
29 #if defined(NTDDI_WIN7) && !defined(DISABLE_D2D)
39 #include "StringCopy.h"
41 #include "UniConversion.h"
42 #include "FontQuality.h"
45 #define IDC_HAND MAKEINTRESOURCE(32649)
48 #ifndef SPI_GETFONTSMOOTHINGCONTRAST
49 #define SPI_GETFONTSMOOTHINGCONTRAST 0x200C
52 static void *PointerFromWindow(HWND hWnd
) {
53 return reinterpret_cast<void *>(::GetWindowLongPtr(hWnd
, 0));
56 static void SetWindowPointer(HWND hWnd
, void *ptr
) {
57 ::SetWindowLongPtr(hWnd
, 0, reinterpret_cast<LONG_PTR
>(ptr
));
60 extern UINT
CodePageFromCharSet(DWORD characterSet
, UINT documentCodePage
);
62 // Declarations needed for functions dynamically loaded as not available on all Windows versions.
63 typedef BOOL (WINAPI
*AlphaBlendSig
)(HDC
, int, int, int, int, HDC
, int, int, int, int, BLENDFUNCTION
);
64 typedef HMONITOR (WINAPI
*MonitorFromPointSig
)(POINT
, DWORD
);
65 typedef HMONITOR (WINAPI
*MonitorFromRectSig
)(LPCRECT
, DWORD
);
66 typedef BOOL (WINAPI
*GetMonitorInfoSig
)(HMONITOR
, LPMONITORINFO
);
68 static CRITICAL_SECTION crPlatformLock
;
69 static HINSTANCE hinstPlatformRes
= 0;
71 static HMODULE hDLLImage
= 0;
72 static AlphaBlendSig AlphaBlendFn
= 0;
74 static HMODULE hDLLUser32
= 0;
75 static HMONITOR (WINAPI
*MonitorFromPointFn
)(POINT
, DWORD
) = 0;
76 static HMONITOR (WINAPI
*MonitorFromRectFn
)(LPCRECT
, DWORD
) = 0;
77 static BOOL (WINAPI
*GetMonitorInfoFn
)(HMONITOR
, LPMONITORINFO
) = 0;
79 static HCURSOR reverseArrowCursor
= NULL
;
85 Point
Point::FromLong(long lpoint
) {
86 return Point(static_cast<short>(LOWORD(lpoint
)), static_cast<short>(HIWORD(lpoint
)));
89 static RECT
RectFromPRectangle(PRectangle prc
) {
90 RECT rc
= {static_cast<LONG
>(prc
.left
), static_cast<LONG
>(prc
.top
),
91 static_cast<LONG
>(prc
.right
), static_cast<LONG
>(prc
.bottom
)};
96 IDWriteFactory
*pIDWriteFactory
= 0;
97 ID2D1Factory
*pD2DFactory
= 0;
98 IDWriteRenderingParams
*defaultRenderingParams
= 0;
99 IDWriteRenderingParams
*customClearTypeRenderingParams
= 0;
101 static HMODULE hDLLD2D
= NULL
;
102 static HMODULE hDLLDWrite
= NULL
;
105 static bool triedLoadingD2D
= false;
106 if (!triedLoadingD2D
) {
107 typedef HRESULT (WINAPI
*D2D1CFSig
)(D2D1_FACTORY_TYPE factoryType
, REFIID riid
,
108 CONST D2D1_FACTORY_OPTIONS
*pFactoryOptions
, IUnknown
**factory
);
109 typedef HRESULT (WINAPI
*DWriteCFSig
)(DWRITE_FACTORY_TYPE factoryType
, REFIID iid
,
112 hDLLD2D
= ::LoadLibraryEx(TEXT("D2D1.DLL"), 0, 0x00000800 /*LOAD_LIBRARY_SEARCH_SYSTEM32*/);
114 D2D1CFSig fnD2DCF
= (D2D1CFSig
)::GetProcAddress(hDLLD2D
, "D2D1CreateFactory");
116 // A single threaded factory as Scintilla always draw on the GUI thread
117 fnD2DCF(D2D1_FACTORY_TYPE_SINGLE_THREADED
,
118 __uuidof(ID2D1Factory
),
120 reinterpret_cast<IUnknown
**>(&pD2DFactory
));
123 hDLLDWrite
= ::LoadLibraryEx(TEXT("DWRITE.DLL"), 0, 0x00000800 /*LOAD_LIBRARY_SEARCH_SYSTEM32*/);
125 DWriteCFSig fnDWCF
= (DWriteCFSig
)::GetProcAddress(hDLLDWrite
, "DWriteCreateFactory");
127 fnDWCF(DWRITE_FACTORY_TYPE_SHARED
,
128 __uuidof(IDWriteFactory
),
129 reinterpret_cast<IUnknown
**>(&pIDWriteFactory
));
133 if (pIDWriteFactory
) {
134 HRESULT hr
= pIDWriteFactory
->CreateRenderingParams(&defaultRenderingParams
);
136 unsigned int clearTypeContrast
;
137 if (::SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST
, 0, &clearTypeContrast
, 0)) {
140 if (clearTypeContrast
>= 1000 && clearTypeContrast
<= 2200)
141 gamma
= static_cast<FLOAT
>(clearTypeContrast
) / 1000.0f
;
143 gamma
= defaultRenderingParams
->GetGamma();
145 pIDWriteFactory
->CreateCustomRenderingParams(gamma
, defaultRenderingParams
->GetEnhancedContrast(), defaultRenderingParams
->GetClearTypeLevel(),
146 defaultRenderingParams
->GetPixelGeometry(), defaultRenderingParams
->GetRenderingMode(), &customClearTypeRenderingParams
);
152 triedLoadingD2D
= true;
153 return pIDWriteFactory
&& pD2DFactory
;
157 struct FormatAndMetrics
{
161 IDWriteTextFormat
*pTextFormat
;
167 FLOAT yInternalLeading
;
168 FormatAndMetrics(HFONT hfont_
, int extraFontFlag_
, int characterSet_
) :
169 technology(SCWIN_TECH_GDI
), hfont(hfont_
),
173 extraFontFlag(extraFontFlag_
), characterSet(characterSet_
), yAscent(2), yDescent(1), yInternalLeading(0) {
176 FormatAndMetrics(IDWriteTextFormat
*pTextFormat_
,
181 FLOAT yInternalLeading_
) :
182 technology(SCWIN_TECH_DIRECTWRITE
),
184 pTextFormat(pTextFormat_
),
185 extraFontFlag(extraFontFlag_
),
186 characterSet(characterSet_
),
189 yInternalLeading(yInternalLeading_
) {
192 ~FormatAndMetrics() {
194 ::DeleteObject(hfont
);
197 pTextFormat
->Release();
204 yInternalLeading
= 0;
209 HFONT
FormatAndMetrics::HFont() {
212 if (technology
== SCWIN_TECH_GDI
) {
213 if (0 == ::GetObjectW(hfont
, sizeof(lf
), &lf
)) {
217 HRESULT hr
= pTextFormat
->GetFontFamilyName(lf
.lfFaceName
, LF_FACESIZE
);
218 if (!SUCCEEDED(hr
)) {
221 lf
.lfWeight
= pTextFormat
->GetFontWeight();
222 lf
.lfItalic
= pTextFormat
->GetFontStyle() == DWRITE_FONT_STYLE_ITALIC
;
223 lf
.lfHeight
= -static_cast<int>(pTextFormat
->GetFontSize());
226 if (0 == ::GetObjectW(hfont
, sizeof(lf
), &lf
)) {
230 return ::CreateFontIndirectW(&lf
);
233 #ifndef CLEARTYPE_QUALITY
234 #define CLEARTYPE_QUALITY 5
237 static BYTE
Win32MapFontQuality(int extraFontFlag
) {
238 switch (extraFontFlag
& SC_EFF_QUALITY_MASK
) {
240 case SC_EFF_QUALITY_NON_ANTIALIASED
:
241 return NONANTIALIASED_QUALITY
;
243 case SC_EFF_QUALITY_ANTIALIASED
:
244 return ANTIALIASED_QUALITY
;
246 case SC_EFF_QUALITY_LCD_OPTIMIZED
:
247 return CLEARTYPE_QUALITY
;
250 return SC_EFF_QUALITY_DEFAULT
;
255 static D2D1_TEXT_ANTIALIAS_MODE
DWriteMapFontQuality(int extraFontFlag
) {
256 switch (extraFontFlag
& SC_EFF_QUALITY_MASK
) {
258 case SC_EFF_QUALITY_NON_ANTIALIASED
:
259 return D2D1_TEXT_ANTIALIAS_MODE_ALIASED
;
261 case SC_EFF_QUALITY_ANTIALIASED
:
262 return D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE
;
264 case SC_EFF_QUALITY_LCD_OPTIMIZED
:
265 return D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE
;
268 return D2D1_TEXT_ANTIALIAS_MODE_DEFAULT
;
273 static void SetLogFont(LOGFONTW
&lf
, const char *faceName
, int characterSet
, float size
, int weight
, bool italic
, int extraFontFlag
) {
275 // The negative is to allow for leading
276 lf
.lfHeight
= -(abs(static_cast<int>(size
+ 0.5)));
277 lf
.lfWeight
= weight
;
278 lf
.lfItalic
= static_cast<BYTE
>(italic
? 1 : 0);
279 lf
.lfCharSet
= static_cast<BYTE
>(characterSet
);
280 lf
.lfQuality
= Win32MapFontQuality(extraFontFlag
);
281 UTF16FromUTF8(faceName
, strlen(faceName
)+1, lf
.lfFaceName
, LF_FACESIZE
);
285 * Create a hash from the parameters for a font to allow easy checking for identity.
286 * If one font is the same as another, its hash will be the same, but if the hash is the
287 * same then they may still be different.
289 static int HashFont(const FontParameters
&fp
) {
291 static_cast<int>(fp
.size
) ^
292 (fp
.characterSet
<< 10) ^
293 ((fp
.extraFontFlag
& SC_EFF_QUALITY_MASK
) << 9) ^
294 ((fp
.weight
/100) << 12) ^
295 (fp
.italic
? 0x20000000 : 0) ^
296 (fp
.technology
<< 15) ^
300 class FontCached
: Font
{
307 explicit FontCached(const FontParameters
&fp
);
309 bool SameAs(const FontParameters
&fp
);
310 virtual void Release();
312 static FontCached
*first
;
314 static FontID
FindOrCreate(const FontParameters
&fp
);
315 static void ReleaseId(FontID fid_
);
318 FontCached
*FontCached::first
= 0;
320 FontCached::FontCached(const FontParameters
&fp
) :
321 next(0), usage(0), size(1.0), hash(0) {
322 SetLogFont(lf
, fp
.faceName
, fp
.characterSet
, fp
.size
, fp
.weight
, fp
.italic
, fp
.extraFontFlag
);
323 technology
= fp
.technology
;
326 if (technology
== SCWIN_TECH_GDI
) {
327 HFONT hfont
= ::CreateFontIndirectW(&lf
);
328 fid
= reinterpret_cast<void *>(new FormatAndMetrics(hfont
, fp
.extraFontFlag
, fp
.characterSet
));
331 IDWriteTextFormat
*pTextFormat
;
332 const int faceSize
= 200;
333 WCHAR wszFace
[faceSize
];
334 UTF16FromUTF8(fp
.faceName
, strlen(fp
.faceName
)+1, wszFace
, faceSize
);
335 FLOAT fHeight
= fp
.size
;
336 DWRITE_FONT_STYLE style
= fp
.italic
? DWRITE_FONT_STYLE_ITALIC
: DWRITE_FONT_STYLE_NORMAL
;
337 HRESULT hr
= pIDWriteFactory
->CreateTextFormat(wszFace
, NULL
,
338 static_cast<DWRITE_FONT_WEIGHT
>(fp
.weight
),
340 DWRITE_FONT_STRETCH_NORMAL
, fHeight
, L
"en-us", &pTextFormat
);
342 pTextFormat
->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP
);
344 const int maxLines
= 2;
345 DWRITE_LINE_METRICS lineMetrics
[maxLines
];
346 UINT32 lineCount
= 0;
347 FLOAT yAscent
= 1.0f
;
348 FLOAT yDescent
= 1.0f
;
349 FLOAT yInternalLeading
= 0.0f
;
350 IDWriteTextLayout
*pTextLayout
= 0;
351 hr
= pIDWriteFactory
->CreateTextLayout(L
"X", 1, pTextFormat
,
352 100.0f
, 100.0f
, &pTextLayout
);
354 hr
= pTextLayout
->GetLineMetrics(lineMetrics
, maxLines
, &lineCount
);
356 yAscent
= lineMetrics
[0].baseline
;
357 yDescent
= lineMetrics
[0].height
- lineMetrics
[0].baseline
;
360 hr
= pTextLayout
->GetFontSize(0, &emHeight
);
362 yInternalLeading
= lineMetrics
[0].height
- emHeight
;
365 pTextLayout
->Release();
366 pTextFormat
->SetLineSpacing(DWRITE_LINE_SPACING_METHOD_UNIFORM
, lineMetrics
[0].height
, lineMetrics
[0].baseline
);
368 fid
= reinterpret_cast<void *>(new FormatAndMetrics(pTextFormat
, fp
.extraFontFlag
, fp
.characterSet
, yAscent
, yDescent
, yInternalLeading
));
375 bool FontCached::SameAs(const FontParameters
&fp
) {
378 (lf
.lfWeight
== fp
.weight
) &&
379 (lf
.lfItalic
== static_cast<BYTE
>(fp
.italic
? 1 : 0)) &&
380 (lf
.lfCharSet
== fp
.characterSet
) &&
381 (lf
.lfQuality
== Win32MapFontQuality(fp
.extraFontFlag
)) &&
382 (technology
== fp
.technology
)) {
383 wchar_t wszFace
[LF_FACESIZE
];
384 UTF16FromUTF8(fp
.faceName
, strlen(fp
.faceName
)+1, wszFace
, LF_FACESIZE
);
385 return 0 == wcscmp(lf
.lfFaceName
,wszFace
);
390 void FontCached::Release() {
391 delete reinterpret_cast<FormatAndMetrics
*>(fid
);
395 FontID
FontCached::FindOrCreate(const FontParameters
&fp
) {
397 ::EnterCriticalSection(&crPlatformLock
);
398 int hashFind
= HashFont(fp
);
399 for (FontCached
*cur
=first
; cur
; cur
=cur
->next
) {
400 if ((cur
->hash
== hashFind
) &&
407 FontCached
*fc
= new FontCached(fp
);
412 ::LeaveCriticalSection(&crPlatformLock
);
416 void FontCached::ReleaseId(FontID fid_
) {
417 ::EnterCriticalSection(&crPlatformLock
);
418 FontCached
**pcur
=&first
;
419 for (FontCached
*cur
=first
; cur
; cur
=cur
->next
) {
420 if (cur
->fid
== fid_
) {
422 if (cur
->usage
== 0) {
432 ::LeaveCriticalSection(&crPlatformLock
);
444 void Font::Create(const FontParameters
&fp
) {
447 fid
= FontCached::FindOrCreate(fp
);
450 void Font::Release() {
452 FontCached::ReleaseId(fid
);
456 // Buffer to hold strings and string position arrays without always allocating on heap.
457 // May sometimes have string too long to allocate on stack. So use a fixed stack-allocated buffer
458 // when less than safe size otherwise allocate on heap and free automatically.
459 template<typename T
, int lengthStandard
>
461 T bufferStandard
[lengthStandard
];
462 // Private so VarBuffer objects can not be copied
463 VarBuffer(const VarBuffer
&);
464 VarBuffer
&operator=(const VarBuffer
&);
467 explicit VarBuffer(size_t length
) : buffer(0) {
468 if (length
> lengthStandard
) {
469 buffer
= new T
[length
];
471 buffer
= bufferStandard
;
475 if (buffer
!= bufferStandard
) {
482 const int stackBufferLength
= 1000;
483 class TextWide
: public VarBuffer
<wchar_t, stackBufferLength
> {
486 TextWide(const char *s
, int len
, bool unicodeMode
, int codePage
=0) :
487 VarBuffer
<wchar_t, stackBufferLength
>(len
) {
489 tlen
= static_cast<int>(UTF16FromUTF8(s
, len
, buffer
, len
));
491 // Support Asian string display in 9x English
492 tlen
= ::MultiByteToWideChar(codePage
, 0, s
, len
, buffer
, len
);
496 typedef VarBuffer
<XYPOSITION
, stackBufferLength
> TextPositions
;
498 class SurfaceGDI
: public Surface
{
515 void BrushColor(ColourDesired back
);
516 void SetFont(Font
&font_
);
518 // Private so SurfaceGDI objects can not be copied
519 SurfaceGDI(const SurfaceGDI
&);
520 SurfaceGDI
&operator=(const SurfaceGDI
&);
523 virtual ~SurfaceGDI();
525 void Init(WindowID wid
);
526 void Init(SurfaceID sid
, WindowID wid
);
527 void InitPixMap(int width
, int height
, Surface
*surface_
, WindowID wid
);
531 void PenColour(ColourDesired fore
);
533 int DeviceHeightFont(int points
);
534 void MoveTo(int x_
, int y_
);
535 void LineTo(int x_
, int y_
);
536 void Polygon(Point
*pts
, int npts
, ColourDesired fore
, ColourDesired back
);
537 void RectangleDraw(PRectangle rc
, ColourDesired fore
, ColourDesired back
);
538 void FillRectangle(PRectangle rc
, ColourDesired back
);
539 void FillRectangle(PRectangle rc
, Surface
&surfacePattern
);
540 void RoundedRectangle(PRectangle rc
, ColourDesired fore
, ColourDesired back
);
541 void AlphaRectangle(PRectangle rc
, int cornerSize
, ColourDesired fill
, int alphaFill
,
542 ColourDesired outline
, int alphaOutline
, int flags
);
543 void DrawRGBAImage(PRectangle rc
, int width
, int height
, const unsigned char *pixelsImage
);
544 void Ellipse(PRectangle rc
, ColourDesired fore
, ColourDesired back
);
545 void Copy(PRectangle rc
, Point from
, Surface
&surfaceSource
);
547 void DrawTextCommon(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, UINT fuOptions
);
548 void DrawTextNoClip(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, ColourDesired fore
, ColourDesired back
);
549 void DrawTextClipped(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, ColourDesired fore
, ColourDesired back
);
550 void DrawTextTransparent(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, ColourDesired fore
);
551 void MeasureWidths(Font
&font_
, const char *s
, int len
, XYPOSITION
*positions
);
552 XYPOSITION
WidthText(Font
&font_
, const char *s
, int len
);
553 XYPOSITION
WidthChar(Font
&font_
, char ch
);
554 XYPOSITION
Ascent(Font
&font_
);
555 XYPOSITION
Descent(Font
&font_
);
556 XYPOSITION
InternalLeading(Font
&font_
);
557 XYPOSITION
ExternalLeading(Font
&font_
);
558 XYPOSITION
Height(Font
&font_
);
559 XYPOSITION
AverageCharWidth(Font
&font_
);
561 void SetClip(PRectangle rc
);
562 void FlushCachedState();
564 void SetUnicodeMode(bool unicodeMode_
);
565 void SetDBCSMode(int codePage_
);
568 SurfaceGDI::SurfaceGDI() :
570 hdc(0), hdcOwned(false),
572 brush(0), brushOld(0),
574 bitmap(0), bitmapOld(0) {
575 maxWidthMeasure
= INT_MAX
;
576 // There appears to be a 16 bit string length limit in GDI on NT.
582 SurfaceGDI::~SurfaceGDI() {
586 void SurfaceGDI::Release() {
588 ::SelectObject(reinterpret_cast<HDC
>(hdc
), penOld
);
594 ::SelectObject(reinterpret_cast<HDC
>(hdc
), brushOld
);
595 ::DeleteObject(brush
);
600 // Fonts are not deleted as they are owned by a Font object
601 ::SelectObject(reinterpret_cast<HDC
>(hdc
), fontOld
);
606 ::SelectObject(reinterpret_cast<HDC
>(hdc
), bitmapOld
);
607 ::DeleteObject(bitmap
);
612 ::DeleteDC(reinterpret_cast<HDC
>(hdc
));
618 bool SurfaceGDI::Initialised() {
622 void SurfaceGDI::Init(WindowID
) {
624 hdc
= ::CreateCompatibleDC(NULL
);
626 ::SetTextAlign(reinterpret_cast<HDC
>(hdc
), TA_BASELINE
);
629 void SurfaceGDI::Init(SurfaceID sid
, WindowID
) {
631 hdc
= reinterpret_cast<HDC
>(sid
);
632 ::SetTextAlign(reinterpret_cast<HDC
>(hdc
), TA_BASELINE
);
635 void SurfaceGDI::InitPixMap(int width
, int height
, Surface
*surface_
, WindowID
) {
637 SurfaceGDI
*psurfOther
= static_cast<SurfaceGDI
*>(surface_
);
638 hdc
= ::CreateCompatibleDC(psurfOther
->hdc
);
640 bitmap
= ::CreateCompatibleBitmap(psurfOther
->hdc
, width
, height
);
641 bitmapOld
= static_cast<HBITMAP
>(::SelectObject(hdc
, bitmap
));
642 ::SetTextAlign(reinterpret_cast<HDC
>(hdc
), TA_BASELINE
);
643 SetUnicodeMode(psurfOther
->unicodeMode
);
644 SetDBCSMode(psurfOther
->codePage
);
647 void SurfaceGDI::PenColour(ColourDesired fore
) {
649 ::SelectObject(hdc
, penOld
);
654 pen
= ::CreatePen(0,1,fore
.AsLong());
655 penOld
= static_cast<HPEN
>(::SelectObject(reinterpret_cast<HDC
>(hdc
), pen
));
658 void SurfaceGDI::BrushColor(ColourDesired back
) {
660 ::SelectObject(hdc
, brushOld
);
661 ::DeleteObject(brush
);
665 // Only ever want pure, non-dithered brushes
666 ColourDesired colourNearest
= ::GetNearestColor(hdc
, back
.AsLong());
667 brush
= ::CreateSolidBrush(colourNearest
.AsLong());
668 brushOld
= static_cast<HBRUSH
>(::SelectObject(hdc
, brush
));
671 void SurfaceGDI::SetFont(Font
&font_
) {
672 if (font_
.GetID() != font
) {
673 FormatAndMetrics
*pfm
= reinterpret_cast<FormatAndMetrics
*>(font_
.GetID());
674 PLATFORM_ASSERT(pfm
->technology
== SCWIN_TECH_GDI
);
676 ::SelectObject(hdc
, pfm
->hfont
);
678 fontOld
= static_cast<HFONT
>(::SelectObject(hdc
, pfm
->hfont
));
680 font
= reinterpret_cast<HFONT
>(pfm
->hfont
);
684 int SurfaceGDI::LogPixelsY() {
685 return ::GetDeviceCaps(hdc
, LOGPIXELSY
);
688 int SurfaceGDI::DeviceHeightFont(int points
) {
689 return ::MulDiv(points
, LogPixelsY(), 72);
692 void SurfaceGDI::MoveTo(int x_
, int y_
) {
693 ::MoveToEx(hdc
, x_
, y_
, 0);
696 void SurfaceGDI::LineTo(int x_
, int y_
) {
697 ::LineTo(hdc
, x_
, y_
);
700 void SurfaceGDI::Polygon(Point
*pts
, int npts
, ColourDesired fore
, ColourDesired back
) {
703 std::vector
<POINT
> outline
;
704 for (int i
=0; i
<npts
; i
++) {
705 POINT pt
= {static_cast<LONG
>(pts
[i
].x
), static_cast<LONG
>(pts
[i
].y
)};
706 outline
.push_back(pt
);
708 ::Polygon(hdc
, &outline
[0], npts
);
711 void SurfaceGDI::RectangleDraw(PRectangle rc
, ColourDesired fore
, ColourDesired back
) {
714 const RECT rcw
= RectFromPRectangle(rc
);
715 ::Rectangle(hdc
, rcw
.left
, rcw
.top
, rcw
.right
, rcw
.bottom
);
718 void SurfaceGDI::FillRectangle(PRectangle rc
, ColourDesired back
) {
719 // Using ExtTextOut rather than a FillRect ensures that no dithering occurs.
720 // There is no need to allocate a brush either.
721 RECT rcw
= RectFromPRectangle(rc
);
722 ::SetBkColor(hdc
, back
.AsLong());
723 ::ExtTextOut(hdc
, rcw
.left
, rcw
.top
, ETO_OPAQUE
, &rcw
, TEXT(""), 0, NULL
);
726 void SurfaceGDI::FillRectangle(PRectangle rc
, Surface
&surfacePattern
) {
728 if (static_cast<SurfaceGDI
&>(surfacePattern
).bitmap
)
729 br
= ::CreatePatternBrush(static_cast<SurfaceGDI
&>(surfacePattern
).bitmap
);
730 else // Something is wrong so display in red
731 br
= ::CreateSolidBrush(RGB(0xff, 0, 0));
732 RECT rcw
= RectFromPRectangle(rc
);
733 ::FillRect(hdc
, &rcw
, br
);
737 void SurfaceGDI::RoundedRectangle(PRectangle rc
, ColourDesired fore
, ColourDesired back
) {
740 const RECT rcw
= RectFromPRectangle(rc
);
742 rcw
.left
+ 1, rcw
.top
,
743 rcw
.right
- 1, rcw
.bottom
,
747 // Plot a point into a DWORD buffer symmetrically to all 4 quadrants
748 static void AllFour(DWORD
*pixels
, int width
, int height
, int x
, int y
, DWORD val
) {
749 pixels
[y
*width
+x
] = val
;
750 pixels
[y
*width
+width
-1-x
] = val
;
751 pixels
[(height
-1-y
)*width
+x
] = val
;
752 pixels
[(height
-1-y
)*width
+width
-1-x
] = val
;
756 #define AC_SRC_OVER 0x00
759 #define AC_SRC_ALPHA 0x01
762 static DWORD
dwordFromBGRA(byte b
, byte g
, byte r
, byte a
) {
767 converter
.pixVal
[0] = b
;
768 converter
.pixVal
[1] = g
;
769 converter
.pixVal
[2] = r
;
770 converter
.pixVal
[3] = a
;
771 return converter
.val
;
774 void SurfaceGDI::AlphaRectangle(PRectangle rc
, int cornerSize
, ColourDesired fill
, int alphaFill
,
775 ColourDesired outline
, int alphaOutline
, int /* flags*/ ) {
776 const RECT rcw
= RectFromPRectangle(rc
);
777 if (AlphaBlendFn
&& rc
.Width() > 0) {
778 HDC hMemDC
= ::CreateCompatibleDC(reinterpret_cast<HDC
>(hdc
));
779 int width
= static_cast<int>(rc
.Width());
780 int height
= static_cast<int>(rc
.Height());
781 // Ensure not distorted too much by corners when small
782 cornerSize
= Platform::Minimum(cornerSize
, (Platform::Minimum(width
, height
) / 2) - 2);
783 BITMAPINFO bpih
= {{sizeof(BITMAPINFOHEADER
), width
, height
, 1, 32, BI_RGB
, 0, 0, 0, 0, 0}};
785 HBITMAP hbmMem
= CreateDIBSection(reinterpret_cast<HDC
>(hMemDC
), &bpih
,
786 DIB_RGB_COLORS
, &image
, NULL
, 0);
789 HBITMAP hbmOld
= SelectBitmap(hMemDC
, hbmMem
);
791 DWORD valEmpty
= dwordFromBGRA(0,0,0,0);
792 DWORD valFill
= dwordFromBGRA(
793 static_cast<byte
>(GetBValue(fill
.AsLong()) * alphaFill
/ 255),
794 static_cast<byte
>(GetGValue(fill
.AsLong()) * alphaFill
/ 255),
795 static_cast<byte
>(GetRValue(fill
.AsLong()) * alphaFill
/ 255),
796 static_cast<byte
>(alphaFill
));
797 DWORD valOutline
= dwordFromBGRA(
798 static_cast<byte
>(GetBValue(outline
.AsLong()) * alphaOutline
/ 255),
799 static_cast<byte
>(GetGValue(outline
.AsLong()) * alphaOutline
/ 255),
800 static_cast<byte
>(GetRValue(outline
.AsLong()) * alphaOutline
/ 255),
801 static_cast<byte
>(alphaOutline
));
802 DWORD
*pixels
= reinterpret_cast<DWORD
*>(image
);
803 for (int y
=0; y
<height
; y
++) {
804 for (int x
=0; x
<width
; x
++) {
805 if ((x
==0) || (x
==width
-1) || (y
== 0) || (y
== height
-1)) {
806 pixels
[y
*width
+x
] = valOutline
;
808 pixels
[y
*width
+x
] = valFill
;
812 for (int c
=0; c
<cornerSize
; c
++) {
813 for (int x
=0; x
<c
+1; x
++) {
814 AllFour(pixels
, width
, height
, x
, c
-x
, valEmpty
);
817 for (int x
=1; x
<cornerSize
; x
++) {
818 AllFour(pixels
, width
, height
, x
, cornerSize
-x
, valOutline
);
821 BLENDFUNCTION merge
= { AC_SRC_OVER
, 0, 255, AC_SRC_ALPHA
};
823 AlphaBlendFn(reinterpret_cast<HDC
>(hdc
), rcw
.left
, rcw
.top
, width
, height
, hMemDC
, 0, 0, width
, height
, merge
);
825 SelectBitmap(hMemDC
, hbmOld
);
826 ::DeleteObject(hbmMem
);
831 FrameRect(hdc
, &rcw
, brush
);
835 void SurfaceGDI::DrawRGBAImage(PRectangle rc
, int width
, int height
, const unsigned char *pixelsImage
) {
836 if (AlphaBlendFn
&& rc
.Width() > 0) {
837 HDC hMemDC
= ::CreateCompatibleDC(reinterpret_cast<HDC
>(hdc
));
838 if (rc
.Width() > width
)
839 rc
.left
+= static_cast<int>((rc
.Width() - width
) / 2);
840 rc
.right
= rc
.left
+ width
;
841 if (rc
.Height() > height
)
842 rc
.top
+= static_cast<int>((rc
.Height() - height
) / 2);
843 rc
.bottom
= rc
.top
+ height
;
845 BITMAPINFO bpih
= {{sizeof(BITMAPINFOHEADER
), width
, height
, 1, 32, BI_RGB
, 0, 0, 0, 0, 0}};
846 unsigned char *image
= 0;
847 HBITMAP hbmMem
= CreateDIBSection(reinterpret_cast<HDC
>(hMemDC
), &bpih
,
848 DIB_RGB_COLORS
, reinterpret_cast<void **>(&image
), NULL
, 0);
850 HBITMAP hbmOld
= SelectBitmap(hMemDC
, hbmMem
);
852 for (int y
=height
-1; y
>=0; y
--) {
853 for (int x
=0; x
<width
; x
++) {
854 unsigned char *pixel
= image
+ (y
*width
+x
) * 4;
855 unsigned char alpha
= pixelsImage
[3];
856 // Input is RGBA, output is BGRA with premultiplied alpha
857 pixel
[2] = static_cast<unsigned char>((*pixelsImage
++) * alpha
/ 255);
858 pixel
[1] = static_cast<unsigned char>((*pixelsImage
++) * alpha
/ 255);
859 pixel
[0] = static_cast<unsigned char>((*pixelsImage
++) * alpha
/ 255);
860 pixel
[3] = static_cast<unsigned char>(*pixelsImage
++);
864 BLENDFUNCTION merge
= { AC_SRC_OVER
, 0, 255, AC_SRC_ALPHA
};
866 AlphaBlendFn(reinterpret_cast<HDC
>(hdc
), static_cast<int>(rc
.left
), static_cast<int>(rc
.top
),
867 static_cast<int>(rc
.Width()), static_cast<int>(rc
.Height()), hMemDC
, 0, 0, width
, height
, merge
);
869 SelectBitmap(hMemDC
, hbmOld
);
870 ::DeleteObject(hbmMem
);
877 void SurfaceGDI::Ellipse(PRectangle rc
, ColourDesired fore
, ColourDesired back
) {
880 const RECT rcw
= RectFromPRectangle(rc
);
881 ::Ellipse(hdc
, rcw
.left
, rcw
.top
, rcw
.right
, rcw
.bottom
);
884 void SurfaceGDI::Copy(PRectangle rc
, Point from
, Surface
&surfaceSource
) {
886 static_cast<int>(rc
.left
), static_cast<int>(rc
.top
),
887 static_cast<int>(rc
.Width()), static_cast<int>(rc
.Height()),
888 static_cast<SurfaceGDI
&>(surfaceSource
).hdc
,
889 static_cast<int>(from
.x
), static_cast<int>(from
.y
), SRCCOPY
);
892 typedef VarBuffer
<int, stackBufferLength
> TextPositionsI
;
894 void SurfaceGDI::DrawTextCommon(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, UINT fuOptions
) {
896 RECT rcw
= RectFromPRectangle(rc
);
899 int x
= static_cast<int>(rc
.left
);
900 const int yBaseInt
= static_cast<int>(ybase
);
902 // Text drawing may fail if the text is too big.
903 // If it does fail, slice up into segments and draw each segment.
904 const int maxSegmentLength
= 0x200;
908 int lenDraw
= Platform::Minimum(len
, maxLenText
);
909 if (!::ExtTextOutA(hdc
, x
, yBaseInt
, fuOptions
, &rcw
, s
, lenDraw
, NULL
)) {
910 while (lenDraw
> pos
) {
911 int seglen
= Platform::Minimum(maxSegmentLength
, lenDraw
- pos
);
912 if (!::ExtTextOutA(hdc
, x
, yBaseInt
, fuOptions
, &rcw
, s
+ pos
, seglen
, NULL
)) {
913 PLATFORM_ASSERT(false);
916 ::GetTextExtentPoint32A(hdc
, s
+pos
, seglen
, &sz
);
923 const TextWide
tbuf(s
, len
, unicodeMode
, codePage
);
924 if (!::ExtTextOutW(hdc
, x
, yBaseInt
, fuOptions
, &rcw
, tbuf
.buffer
, tbuf
.tlen
, NULL
)) {
925 while (tbuf
.tlen
> pos
) {
926 int seglen
= Platform::Minimum(maxSegmentLength
, tbuf
.tlen
- pos
);
927 if (!::ExtTextOutW(hdc
, x
, yBaseInt
, fuOptions
, &rcw
, tbuf
.buffer
+ pos
, seglen
, NULL
)) {
928 PLATFORM_ASSERT(false);
931 ::GetTextExtentPoint32W(hdc
, tbuf
.buffer
+pos
, seglen
, &sz
);
939 void SurfaceGDI::DrawTextNoClip(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
,
940 ColourDesired fore
, ColourDesired back
) {
941 ::SetTextColor(hdc
, fore
.AsLong());
942 ::SetBkColor(hdc
, back
.AsLong());
943 DrawTextCommon(rc
, font_
, ybase
, s
, len
, ETO_OPAQUE
);
946 void SurfaceGDI::DrawTextClipped(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
,
947 ColourDesired fore
, ColourDesired back
) {
948 ::SetTextColor(hdc
, fore
.AsLong());
949 ::SetBkColor(hdc
, back
.AsLong());
950 DrawTextCommon(rc
, font_
, ybase
, s
, len
, ETO_OPAQUE
| ETO_CLIPPED
);
953 void SurfaceGDI::DrawTextTransparent(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
,
954 ColourDesired fore
) {
955 // Avoid drawing spaces in transparent mode
956 for (int i
=0; i
<len
; i
++) {
958 ::SetTextColor(hdc
, fore
.AsLong());
959 ::SetBkMode(hdc
, TRANSPARENT
);
960 DrawTextCommon(rc
, font_
, ybase
, s
, len
, 0);
961 ::SetBkMode(hdc
, OPAQUE
);
967 XYPOSITION
SurfaceGDI::WidthText(Font
&font_
, const char *s
, int len
) {
971 ::GetTextExtentPoint32A(hdc
, s
, Platform::Minimum(len
, maxLenText
), &sz
);
973 const TextWide
tbuf(s
, len
, unicodeMode
, codePage
);
974 ::GetTextExtentPoint32W(hdc
, tbuf
.buffer
, tbuf
.tlen
, &sz
);
976 return static_cast<XYPOSITION
>(sz
.cx
);
979 void SurfaceGDI::MeasureWidths(Font
&font_
, const char *s
, int len
, XYPOSITION
*positions
) {
984 const TextWide
tbuf(s
, len
, unicodeMode
, codePage
);
985 TextPositionsI
poses(tbuf
.tlen
);
987 if (!::GetTextExtentExPointW(hdc
, tbuf
.buffer
, tbuf
.tlen
, maxWidthMeasure
, &fit
, poses
.buffer
, &sz
)) {
988 // Likely to have failed because on Windows 9x where function not available
989 // So measure the character widths by measuring each initial substring
990 // Turns a linear operation into a quadratic but seems fast enough on test files
991 for (int widthSS
=0; widthSS
< tbuf
.tlen
; widthSS
++) {
992 ::GetTextExtentPoint32W(hdc
, tbuf
.buffer
, widthSS
+1, &sz
);
993 poses
.buffer
[widthSS
] = sz
.cx
;
996 // Map the widths given for UTF-16 characters back onto the UTF-8 input string
998 const unsigned char *us
= reinterpret_cast<const unsigned char *>(s
);
1001 unsigned char uch
= us
[i
];
1002 unsigned int lenChar
= 1;
1003 if (uch
>= (0x80 + 0x40 + 0x20 + 0x10)) {
1006 } else if (uch
>= (0x80 + 0x40 + 0x20)) {
1008 } else if (uch
>= (0x80)) {
1011 for (unsigned int bytePos
=0; (bytePos
<lenChar
) && (i
<len
); bytePos
++) {
1012 positions
[i
++] = static_cast<XYPOSITION
>(poses
.buffer
[ui
]);
1016 XYPOSITION lastPos
= 0.0f
;
1018 lastPos
= positions
[i
-1];
1020 positions
[i
++] = lastPos
;
1023 // Zero positions to avoid random behaviour on failure.
1024 std::fill(positions
, positions
+ len
, 0.0f
);
1025 // len may be larger than platform supports so loop over segments small enough for platform
1026 int startOffset
= 0;
1028 int lenBlock
= Platform::Minimum(len
, maxLenText
);
1029 TextPositionsI
poses(len
);
1030 if (!::GetTextExtentExPointA(hdc
, s
, lenBlock
, maxWidthMeasure
, &fit
, poses
.buffer
, &sz
)) {
1031 // Eeek - a NULL DC or other foolishness could cause this.
1033 } else if (fit
< lenBlock
) {
1034 // For some reason, such as an incomplete DBCS character
1035 // Not all the positions are filled in so make them equal to end.
1037 poses
.buffer
[fit
++] = 0;
1038 for (int i
= fit
; i
<lenBlock
; i
++)
1039 poses
.buffer
[i
] = poses
.buffer
[fit
-1];
1041 for (int i
=0; i
<lenBlock
; i
++)
1042 positions
[i
] = static_cast<XYPOSITION
>(poses
.buffer
[i
] + startOffset
);
1043 startOffset
= poses
.buffer
[lenBlock
-1];
1045 positions
+= lenBlock
;
1051 XYPOSITION
SurfaceGDI::WidthChar(Font
&font_
, char ch
) {
1054 ::GetTextExtentPoint32A(hdc
, &ch
, 1, &sz
);
1055 return static_cast<XYPOSITION
>(sz
.cx
);
1058 XYPOSITION
SurfaceGDI::Ascent(Font
&font_
) {
1061 ::GetTextMetrics(hdc
, &tm
);
1062 return static_cast<XYPOSITION
>(tm
.tmAscent
);
1065 XYPOSITION
SurfaceGDI::Descent(Font
&font_
) {
1068 ::GetTextMetrics(hdc
, &tm
);
1069 return static_cast<XYPOSITION
>(tm
.tmDescent
);
1072 XYPOSITION
SurfaceGDI::InternalLeading(Font
&font_
) {
1075 ::GetTextMetrics(hdc
, &tm
);
1076 return static_cast<XYPOSITION
>(tm
.tmInternalLeading
);
1079 XYPOSITION
SurfaceGDI::ExternalLeading(Font
&font_
) {
1082 ::GetTextMetrics(hdc
, &tm
);
1083 return static_cast<XYPOSITION
>(tm
.tmExternalLeading
);
1086 XYPOSITION
SurfaceGDI::Height(Font
&font_
) {
1089 ::GetTextMetrics(hdc
, &tm
);
1090 return static_cast<XYPOSITION
>(tm
.tmHeight
);
1093 XYPOSITION
SurfaceGDI::AverageCharWidth(Font
&font_
) {
1096 ::GetTextMetrics(hdc
, &tm
);
1097 return static_cast<XYPOSITION
>(tm
.tmAveCharWidth
);
1100 void SurfaceGDI::SetClip(PRectangle rc
) {
1101 ::IntersectClipRect(hdc
, static_cast<int>(rc
.left
), static_cast<int>(rc
.top
),
1102 static_cast<int>(rc
.right
), static_cast<int>(rc
.bottom
));
1105 void SurfaceGDI::FlushCachedState() {
1111 void SurfaceGDI::SetUnicodeMode(bool unicodeMode_
) {
1112 unicodeMode
=unicodeMode_
;
1115 void SurfaceGDI::SetDBCSMode(int codePage_
) {
1116 // No action on window as automatically handled by system.
1117 codePage
= codePage_
;
1120 #if defined(USE_D2D)
1122 class SurfaceD2D
: public Surface
{
1129 ID2D1RenderTarget
*pRenderTarget
;
1130 bool ownRenderTarget
;
1133 IDWriteTextFormat
*pTextFormat
;
1136 FLOAT yInternalLeading
;
1138 ID2D1SolidColorBrush
*pBrush
;
1144 void SetFont(Font
&font_
);
1146 // Private so SurfaceD2D objects can not be copied
1147 SurfaceD2D(const SurfaceD2D
&);
1148 SurfaceD2D
&operator=(const SurfaceD2D
&);
1151 virtual ~SurfaceD2D();
1154 void Init(WindowID wid
);
1155 void Init(SurfaceID sid
, WindowID wid
);
1156 void InitPixMap(int width
, int height
, Surface
*surface_
, WindowID wid
);
1161 HRESULT
FlushDrawing();
1163 void PenColour(ColourDesired fore
);
1164 void D2DPenColour(ColourDesired fore
, int alpha
=255);
1166 int DeviceHeightFont(int points
);
1167 void MoveTo(int x_
, int y_
);
1168 void LineTo(int x_
, int y_
);
1169 void Polygon(Point
*pts
, int npts
, ColourDesired fore
, ColourDesired back
);
1170 void RectangleDraw(PRectangle rc
, ColourDesired fore
, ColourDesired back
);
1171 void FillRectangle(PRectangle rc
, ColourDesired back
);
1172 void FillRectangle(PRectangle rc
, Surface
&surfacePattern
);
1173 void RoundedRectangle(PRectangle rc
, ColourDesired fore
, ColourDesired back
);
1174 void AlphaRectangle(PRectangle rc
, int cornerSize
, ColourDesired fill
, int alphaFill
,
1175 ColourDesired outline
, int alphaOutline
, int flags
);
1176 void DrawRGBAImage(PRectangle rc
, int width
, int height
, const unsigned char *pixelsImage
);
1177 void Ellipse(PRectangle rc
, ColourDesired fore
, ColourDesired back
);
1178 void Copy(PRectangle rc
, Point from
, Surface
&surfaceSource
);
1180 void DrawTextCommon(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, UINT fuOptions
);
1181 void DrawTextNoClip(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, ColourDesired fore
, ColourDesired back
);
1182 void DrawTextClipped(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, ColourDesired fore
, ColourDesired back
);
1183 void DrawTextTransparent(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, ColourDesired fore
);
1184 void MeasureWidths(Font
&font_
, const char *s
, int len
, XYPOSITION
*positions
);
1185 XYPOSITION
WidthText(Font
&font_
, const char *s
, int len
);
1186 XYPOSITION
WidthChar(Font
&font_
, char ch
);
1187 XYPOSITION
Ascent(Font
&font_
);
1188 XYPOSITION
Descent(Font
&font_
);
1189 XYPOSITION
InternalLeading(Font
&font_
);
1190 XYPOSITION
ExternalLeading(Font
&font_
);
1191 XYPOSITION
Height(Font
&font_
);
1192 XYPOSITION
AverageCharWidth(Font
&font_
);
1194 void SetClip(PRectangle rc
);
1195 void FlushCachedState();
1197 void SetUnicodeMode(bool unicodeMode_
);
1198 void SetDBCSMode(int codePage_
);
1201 SurfaceD2D::SurfaceD2D() :
1208 pRenderTarget
= NULL
;
1209 ownRenderTarget
= false;
1212 // From selected font
1216 yInternalLeading
= 0;
1225 SurfaceD2D::~SurfaceD2D() {
1229 void SurfaceD2D::Release() {
1234 if (pRenderTarget
) {
1235 while (clipsActive
) {
1236 pRenderTarget
->PopAxisAlignedClip();
1239 if (ownRenderTarget
) {
1240 pRenderTarget
->Release();
1246 void SurfaceD2D::SetScale() {
1247 HDC hdcMeasure
= ::CreateCompatibleDC(NULL
);
1248 logPixelsY
= ::GetDeviceCaps(hdcMeasure
, LOGPIXELSY
);
1249 dpiScaleX
= ::GetDeviceCaps(hdcMeasure
, LOGPIXELSX
) / 96.0f
;
1250 dpiScaleY
= logPixelsY
/ 96.0f
;
1251 ::DeleteDC(hdcMeasure
);
1254 bool SurfaceD2D::Initialised() {
1255 return pRenderTarget
!= 0;
1258 HRESULT
SurfaceD2D::FlushDrawing() {
1259 return pRenderTarget
->Flush();
1262 void SurfaceD2D::Init(WindowID
/* wid */) {
1267 void SurfaceD2D::Init(SurfaceID sid
, WindowID
) {
1270 pRenderTarget
= reinterpret_cast<ID2D1RenderTarget
*>(sid
);
1273 void SurfaceD2D::InitPixMap(int width
, int height
, Surface
*surface_
, WindowID
) {
1276 SurfaceD2D
*psurfOther
= static_cast<SurfaceD2D
*>(surface_
);
1277 ID2D1BitmapRenderTarget
*pCompatibleRenderTarget
= NULL
;
1278 D2D1_SIZE_F desiredSize
= D2D1::SizeF(static_cast<float>(width
), static_cast<float>(height
));
1279 D2D1_PIXEL_FORMAT desiredFormat
;
1281 desiredFormat
.format
= DXGI_FORMAT_UNKNOWN
;
1283 desiredFormat
= psurfOther
->pRenderTarget
->GetPixelFormat();
1285 desiredFormat
.alphaMode
= D2D1_ALPHA_MODE_IGNORE
;
1286 HRESULT hr
= psurfOther
->pRenderTarget
->CreateCompatibleRenderTarget(
1287 &desiredSize
, NULL
, &desiredFormat
, D2D1_COMPATIBLE_RENDER_TARGET_OPTIONS_NONE
, &pCompatibleRenderTarget
);
1288 if (SUCCEEDED(hr
)) {
1289 pRenderTarget
= pCompatibleRenderTarget
;
1290 pRenderTarget
->BeginDraw();
1291 ownRenderTarget
= true;
1293 SetUnicodeMode(psurfOther
->unicodeMode
);
1294 SetDBCSMode(psurfOther
->codePage
);
1297 void SurfaceD2D::PenColour(ColourDesired fore
) {
1301 void SurfaceD2D::D2DPenColour(ColourDesired fore
, int alpha
) {
1302 if (pRenderTarget
) {
1304 col
.r
= (fore
.AsLong() & 0xff) / 255.0f
;
1305 col
.g
= ((fore
.AsLong() & 0xff00) >> 8) / 255.0f
;
1306 col
.b
= (fore
.AsLong() >> 16) / 255.0f
;
1307 col
.a
= alpha
/ 255.0f
;
1309 pBrush
->SetColor(col
);
1311 HRESULT hr
= pRenderTarget
->CreateSolidColorBrush(col
, &pBrush
);
1312 if (!SUCCEEDED(hr
) && pBrush
) {
1320 void SurfaceD2D::SetFont(Font
&font_
) {
1321 FormatAndMetrics
*pfm
= reinterpret_cast<FormatAndMetrics
*>(font_
.GetID());
1322 PLATFORM_ASSERT(pfm
->technology
== SCWIN_TECH_DIRECTWRITE
);
1323 pTextFormat
= pfm
->pTextFormat
;
1324 yAscent
= pfm
->yAscent
;
1325 yDescent
= pfm
->yDescent
;
1326 yInternalLeading
= pfm
->yInternalLeading
;
1327 codePageText
= codePage
;
1328 if (pfm
->characterSet
) {
1329 codePageText
= CodePageFromCharSet(pfm
->characterSet
, codePage
);
1331 if (pRenderTarget
) {
1332 D2D1_TEXT_ANTIALIAS_MODE aaMode
;
1333 aaMode
= DWriteMapFontQuality(pfm
->extraFontFlag
);
1335 if (aaMode
== D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE
&& customClearTypeRenderingParams
)
1336 pRenderTarget
->SetTextRenderingParams(customClearTypeRenderingParams
);
1337 else if (defaultRenderingParams
)
1338 pRenderTarget
->SetTextRenderingParams(defaultRenderingParams
);
1340 pRenderTarget
->SetTextAntialiasMode(aaMode
);
1344 int SurfaceD2D::LogPixelsY() {
1348 int SurfaceD2D::DeviceHeightFont(int points
) {
1349 return ::MulDiv(points
, LogPixelsY(), 72);
1352 void SurfaceD2D::MoveTo(int x_
, int y_
) {
1357 static int Delta(int difference
) {
1360 else if (difference
> 0)
1366 static float RoundFloat(float f
) {
1367 return float(int(f
+0.5f
));
1370 void SurfaceD2D::LineTo(int x_
, int y_
) {
1371 if (pRenderTarget
) {
1373 int xDelta
= Delta(xDiff
);
1375 int yDelta
= Delta(yDiff
);
1376 if ((xDiff
== 0) || (yDiff
== 0)) {
1377 // Horizontal or vertical lines can be more precisely drawn as a filled rectangle
1378 int xEnd
= x_
- xDelta
;
1379 int left
= Platform::Minimum(x
, xEnd
);
1380 int width
= abs(x
- xEnd
) + 1;
1381 int yEnd
= y_
- yDelta
;
1382 int top
= Platform::Minimum(y
, yEnd
);
1383 int height
= abs(y
- yEnd
) + 1;
1384 D2D1_RECT_F rectangle1
= D2D1::RectF(static_cast<float>(left
), static_cast<float>(top
),
1385 static_cast<float>(left
+width
), static_cast<float>(top
+height
));
1386 pRenderTarget
->FillRectangle(&rectangle1
, pBrush
);
1387 } else if ((abs(xDiff
) == abs(yDiff
))) {
1389 pRenderTarget
->DrawLine(D2D1::Point2F(x
+ 0.5f
, y
+ 0.5f
),
1390 D2D1::Point2F(x_
+ 0.5f
- xDelta
, y_
+ 0.5f
- yDelta
), pBrush
);
1392 // Line has a different slope so difficult to avoid last pixel
1393 pRenderTarget
->DrawLine(D2D1::Point2F(x
+ 0.5f
, y
+ 0.5f
),
1394 D2D1::Point2F(x_
+ 0.5f
, y_
+ 0.5f
), pBrush
);
1401 void SurfaceD2D::Polygon(Point
*pts
, int npts
, ColourDesired fore
, ColourDesired back
) {
1402 if (pRenderTarget
) {
1403 ID2D1Factory
*pFactory
= 0;
1404 pRenderTarget
->GetFactory(&pFactory
);
1405 ID2D1PathGeometry
*geometry
=0;
1406 HRESULT hr
= pFactory
->CreatePathGeometry(&geometry
);
1407 if (SUCCEEDED(hr
)) {
1408 ID2D1GeometrySink
*sink
= 0;
1409 hr
= geometry
->Open(&sink
);
1410 if (SUCCEEDED(hr
)) {
1411 sink
->BeginFigure(D2D1::Point2F(pts
[0].x
+ 0.5f
, pts
[0].y
+ 0.5f
), D2D1_FIGURE_BEGIN_FILLED
);
1412 for (size_t i
=1; i
<static_cast<size_t>(npts
); i
++) {
1413 sink
->AddLine(D2D1::Point2F(pts
[i
].x
+ 0.5f
, pts
[i
].y
+ 0.5f
));
1415 sink
->EndFigure(D2D1_FIGURE_END_CLOSED
);
1420 pRenderTarget
->FillGeometry(geometry
,pBrush
);
1422 pRenderTarget
->DrawGeometry(geometry
,pBrush
);
1425 geometry
->Release();
1430 void SurfaceD2D::RectangleDraw(PRectangle rc
, ColourDesired fore
, ColourDesired back
) {
1431 if (pRenderTarget
) {
1432 D2D1_RECT_F rectangle1
= D2D1::RectF(RoundFloat(rc
.left
) + 0.5f
, rc
.top
+0.5f
, RoundFloat(rc
.right
) - 0.5f
, rc
.bottom
-0.5f
);
1434 pRenderTarget
->FillRectangle(&rectangle1
, pBrush
);
1436 pRenderTarget
->DrawRectangle(&rectangle1
, pBrush
);
1440 void SurfaceD2D::FillRectangle(PRectangle rc
, ColourDesired back
) {
1441 if (pRenderTarget
) {
1443 D2D1_RECT_F rectangle1
= D2D1::RectF(RoundFloat(rc
.left
), rc
.top
, RoundFloat(rc
.right
), rc
.bottom
);
1444 pRenderTarget
->FillRectangle(&rectangle1
, pBrush
);
1448 void SurfaceD2D::FillRectangle(PRectangle rc
, Surface
&surfacePattern
) {
1449 SurfaceD2D
&surfOther
= static_cast<SurfaceD2D
&>(surfacePattern
);
1450 surfOther
.FlushDrawing();
1451 ID2D1Bitmap
*pBitmap
= NULL
;
1452 ID2D1BitmapRenderTarget
*pCompatibleRenderTarget
= reinterpret_cast<ID2D1BitmapRenderTarget
*>(
1453 surfOther
.pRenderTarget
);
1454 HRESULT hr
= pCompatibleRenderTarget
->GetBitmap(&pBitmap
);
1455 if (SUCCEEDED(hr
)) {
1456 ID2D1BitmapBrush
*pBitmapBrush
= NULL
;
1457 D2D1_BITMAP_BRUSH_PROPERTIES brushProperties
=
1458 D2D1::BitmapBrushProperties(D2D1_EXTEND_MODE_WRAP
, D2D1_EXTEND_MODE_WRAP
,
1459 D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR
);
1460 // Create the bitmap brush.
1461 hr
= pRenderTarget
->CreateBitmapBrush(pBitmap
, brushProperties
, &pBitmapBrush
);
1463 if (SUCCEEDED(hr
)) {
1464 pRenderTarget
->FillRectangle(
1465 D2D1::RectF(rc
.left
, rc
.top
, rc
.right
, rc
.bottom
),
1467 pBitmapBrush
->Release();
1472 void SurfaceD2D::RoundedRectangle(PRectangle rc
, ColourDesired fore
, ColourDesired back
) {
1473 if (pRenderTarget
) {
1474 D2D1_ROUNDED_RECT roundedRectFill
= {
1475 D2D1::RectF(rc
.left
+1.0f
, rc
.top
+1.0f
, rc
.right
-1.0f
, rc
.bottom
-1.0f
),
1478 pRenderTarget
->FillRoundedRectangle(roundedRectFill
, pBrush
);
1480 D2D1_ROUNDED_RECT roundedRect
= {
1481 D2D1::RectF(rc
.left
+ 0.5f
, rc
.top
+0.5f
, rc
.right
- 0.5f
, rc
.bottom
-0.5f
),
1484 pRenderTarget
->DrawRoundedRectangle(roundedRect
, pBrush
);
1488 void SurfaceD2D::AlphaRectangle(PRectangle rc
, int cornerSize
, ColourDesired fill
, int alphaFill
,
1489 ColourDesired outline
, int alphaOutline
, int /* flags*/ ) {
1490 if (pRenderTarget
) {
1491 if (cornerSize
== 0) {
1492 // When corner size is zero, draw square rectangle to prevent blurry pixels at corners
1493 D2D1_RECT_F rectFill
= D2D1::RectF(RoundFloat(rc
.left
) + 1.0f
, rc
.top
+ 1.0f
, RoundFloat(rc
.right
) - 1.0f
, rc
.bottom
- 1.0f
);
1494 D2DPenColour(fill
, alphaFill
);
1495 pRenderTarget
->FillRectangle(rectFill
, pBrush
);
1497 D2D1_RECT_F rectOutline
= D2D1::RectF(RoundFloat(rc
.left
) + 0.5f
, rc
.top
+ 0.5f
, RoundFloat(rc
.right
) - 0.5f
, rc
.bottom
- 0.5f
);
1498 D2DPenColour(outline
, alphaOutline
);
1499 pRenderTarget
->DrawRectangle(rectOutline
, pBrush
);
1501 const float cornerSizeF
= static_cast<float>(cornerSize
);
1502 D2D1_ROUNDED_RECT roundedRectFill
= {
1503 D2D1::RectF(RoundFloat(rc
.left
) + 1.0f
, rc
.top
+ 1.0f
, RoundFloat(rc
.right
) - 1.0f
, rc
.bottom
- 1.0f
),
1504 cornerSizeF
, cornerSizeF
};
1505 D2DPenColour(fill
, alphaFill
);
1506 pRenderTarget
->FillRoundedRectangle(roundedRectFill
, pBrush
);
1508 D2D1_ROUNDED_RECT roundedRect
= {
1509 D2D1::RectF(RoundFloat(rc
.left
) + 0.5f
, rc
.top
+ 0.5f
, RoundFloat(rc
.right
) - 0.5f
, rc
.bottom
- 0.5f
),
1510 cornerSizeF
, cornerSizeF
};
1511 D2DPenColour(outline
, alphaOutline
);
1512 pRenderTarget
->DrawRoundedRectangle(roundedRect
, pBrush
);
1517 void SurfaceD2D::DrawRGBAImage(PRectangle rc
, int width
, int height
, const unsigned char *pixelsImage
) {
1518 if (pRenderTarget
) {
1519 if (rc
.Width() > width
)
1520 rc
.left
+= static_cast<int>((rc
.Width() - width
) / 2);
1521 rc
.right
= rc
.left
+ width
;
1522 if (rc
.Height() > height
)
1523 rc
.top
+= static_cast<int>((rc
.Height() - height
) / 2);
1524 rc
.bottom
= rc
.top
+ height
;
1526 std::vector
<unsigned char> image(height
* width
* 4);
1527 for (int yPixel
=0; yPixel
<height
; yPixel
++) {
1528 for (int xPixel
= 0; xPixel
<width
; xPixel
++) {
1529 unsigned char *pixel
= &image
[0] + (yPixel
*width
+ xPixel
) * 4;
1530 unsigned char alpha
= pixelsImage
[3];
1531 // Input is RGBA, output is BGRA with premultiplied alpha
1532 pixel
[2] = (*pixelsImage
++) * alpha
/ 255;
1533 pixel
[1] = (*pixelsImage
++) * alpha
/ 255;
1534 pixel
[0] = (*pixelsImage
++) * alpha
/ 255;
1535 pixel
[3] = *pixelsImage
++;
1539 ID2D1Bitmap
*bitmap
= 0;
1540 D2D1_SIZE_U size
= D2D1::SizeU(width
, height
);
1541 D2D1_BITMAP_PROPERTIES props
= {{DXGI_FORMAT_B8G8R8A8_UNORM
,
1542 D2D1_ALPHA_MODE_PREMULTIPLIED
}, 72.0, 72.0};
1543 HRESULT hr
= pRenderTarget
->CreateBitmap(size
, &image
[0],
1544 width
* 4, &props
, &bitmap
);
1545 if (SUCCEEDED(hr
)) {
1546 D2D1_RECT_F rcDestination
= {rc
.left
, rc
.top
, rc
.right
, rc
.bottom
};
1547 pRenderTarget
->DrawBitmap(bitmap
, rcDestination
);
1553 void SurfaceD2D::Ellipse(PRectangle rc
, ColourDesired fore
, ColourDesired back
) {
1554 if (pRenderTarget
) {
1555 FLOAT radius
= rc
.Width() / 2.0f
;
1556 D2D1_ELLIPSE ellipse
= {
1557 D2D1::Point2F((rc
.left
+ rc
.right
) / 2.0f
, (rc
.top
+ rc
.bottom
) / 2.0f
),
1561 pRenderTarget
->FillEllipse(ellipse
, pBrush
);
1563 pRenderTarget
->DrawEllipse(ellipse
, pBrush
);
1567 void SurfaceD2D::Copy(PRectangle rc
, Point from
, Surface
&surfaceSource
) {
1568 SurfaceD2D
&surfOther
= static_cast<SurfaceD2D
&>(surfaceSource
);
1569 surfOther
.FlushDrawing();
1570 ID2D1BitmapRenderTarget
*pCompatibleRenderTarget
= reinterpret_cast<ID2D1BitmapRenderTarget
*>(
1571 surfOther
.pRenderTarget
);
1572 ID2D1Bitmap
*pBitmap
= NULL
;
1573 HRESULT hr
= pCompatibleRenderTarget
->GetBitmap(&pBitmap
);
1574 if (SUCCEEDED(hr
)) {
1575 D2D1_RECT_F rcDestination
= {rc
.left
, rc
.top
, rc
.right
, rc
.bottom
};
1576 D2D1_RECT_F rcSource
= {from
.x
, from
.y
, from
.x
+ rc
.Width(), from
.y
+ rc
.Height()};
1577 pRenderTarget
->DrawBitmap(pBitmap
, rcDestination
, 1.0f
,
1578 D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR
, rcSource
);
1579 hr
= pRenderTarget
->Flush();
1581 Platform::DebugPrintf("Failed Flush 0x%x\n", hr
);
1587 void SurfaceD2D::DrawTextCommon(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, UINT fuOptions
) {
1590 // Use Unicode calls
1591 const TextWide
tbuf(s
, len
, unicodeMode
, codePageText
);
1592 if (pRenderTarget
&& pTextFormat
&& pBrush
) {
1593 if (fuOptions
& ETO_CLIPPED
) {
1594 D2D1_RECT_F rcClip
= {rc
.left
, rc
.top
, rc
.right
, rc
.bottom
};
1595 pRenderTarget
->PushAxisAlignedClip(rcClip
, D2D1_ANTIALIAS_MODE_ALIASED
);
1598 // Explicitly creating a text layout appears a little faster
1599 IDWriteTextLayout
*pTextLayout
;
1600 HRESULT hr
= pIDWriteFactory
->CreateTextLayout(tbuf
.buffer
, tbuf
.tlen
, pTextFormat
,
1601 rc
.Width(), rc
.Height(), &pTextLayout
);
1602 if (SUCCEEDED(hr
)) {
1603 D2D1_POINT_2F origin
= {rc
.left
, ybase
-yAscent
};
1604 pRenderTarget
->DrawTextLayout(origin
, pTextLayout
, pBrush
, D2D1_DRAW_TEXT_OPTIONS_NONE
);
1605 pTextLayout
->Release();
1608 if (fuOptions
& ETO_CLIPPED
) {
1609 pRenderTarget
->PopAxisAlignedClip();
1614 void SurfaceD2D::DrawTextNoClip(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
,
1615 ColourDesired fore
, ColourDesired back
) {
1616 if (pRenderTarget
) {
1617 FillRectangle(rc
, back
);
1619 DrawTextCommon(rc
, font_
, ybase
, s
, len
, ETO_OPAQUE
);
1623 void SurfaceD2D::DrawTextClipped(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
,
1624 ColourDesired fore
, ColourDesired back
) {
1625 if (pRenderTarget
) {
1626 FillRectangle(rc
, back
);
1628 DrawTextCommon(rc
, font_
, ybase
, s
, len
, ETO_OPAQUE
| ETO_CLIPPED
);
1632 void SurfaceD2D::DrawTextTransparent(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
,
1633 ColourDesired fore
) {
1634 // Avoid drawing spaces in transparent mode
1635 for (int i
=0; i
<len
; i
++) {
1637 if (pRenderTarget
) {
1639 DrawTextCommon(rc
, font_
, ybase
, s
, len
, 0);
1646 XYPOSITION
SurfaceD2D::WidthText(Font
&font_
, const char *s
, int len
) {
1649 const TextWide
tbuf(s
, len
, unicodeMode
, codePageText
);
1650 if (pIDWriteFactory
&& pTextFormat
) {
1652 IDWriteTextLayout
*pTextLayout
= 0;
1653 HRESULT hr
= pIDWriteFactory
->CreateTextLayout(tbuf
.buffer
, tbuf
.tlen
, pTextFormat
, 1000.0, 1000.0, &pTextLayout
);
1654 if (SUCCEEDED(hr
)) {
1655 DWRITE_TEXT_METRICS textMetrics
;
1656 if (SUCCEEDED(pTextLayout
->GetMetrics(&textMetrics
)))
1657 width
= textMetrics
.widthIncludingTrailingWhitespace
;
1658 pTextLayout
->Release();
1664 void SurfaceD2D::MeasureWidths(Font
&font_
, const char *s
, int len
, XYPOSITION
*positions
) {
1667 const TextWide
tbuf(s
, len
, unicodeMode
, codePageText
);
1668 TextPositions
poses(tbuf
.tlen
);
1670 const int clusters
= 1000;
1671 DWRITE_CLUSTER_METRICS clusterMetrics
[clusters
];
1673 if (pIDWriteFactory
&& pTextFormat
) {
1676 IDWriteTextLayout
*pTextLayout
= 0;
1677 HRESULT hr
= pIDWriteFactory
->CreateTextLayout(tbuf
.buffer
, tbuf
.tlen
, pTextFormat
, 10000.0, 1000.0, &pTextLayout
);
1680 // For now, assuming WCHAR == cluster
1681 if (!SUCCEEDED(pTextLayout
->GetClusterMetrics(clusterMetrics
, clusters
, &count
)))
1683 FLOAT position
= 0.0f
;
1685 for (size_t ci
=0; ci
<count
; ci
++) {
1686 position
+= clusterMetrics
[ci
].width
;
1687 for (size_t inCluster
=0; inCluster
<clusterMetrics
[ci
].length
; inCluster
++) {
1688 //poses.buffer[ti++] = int(position + 0.5);
1689 poses
.buffer
[ti
++] = position
;
1692 PLATFORM_ASSERT(ti
== static_cast<size_t>(tbuf
.tlen
));
1693 pTextLayout
->Release();
1696 // Map the widths given for UTF-16 characters back onto the UTF-8 input string
1698 const unsigned char *us
= reinterpret_cast<const unsigned char *>(s
);
1701 unsigned char uch
= us
[i
];
1702 unsigned int lenChar
= 1;
1703 if (uch
>= (0x80 + 0x40 + 0x20 + 0x10)) {
1706 } else if (uch
>= (0x80 + 0x40 + 0x20)) {
1708 } else if (uch
>= (0x80)) {
1711 for (unsigned int bytePos
=0; (bytePos
<lenChar
) && (i
<len
); bytePos
++) {
1712 positions
[i
++] = poses
.buffer
[ui
];
1716 XYPOSITION lastPos
= 0.0f
;
1718 lastPos
= positions
[i
-1];
1720 positions
[i
++] = lastPos
;
1722 } else if (codePageText
== 0) {
1724 // One character per position
1725 PLATFORM_ASSERT(len
== tbuf
.tlen
);
1726 for (size_t kk
=0; kk
<static_cast<size_t>(len
); kk
++) {
1727 positions
[kk
] = poses
.buffer
[kk
];
1732 // May be more than one byte per position
1733 unsigned int ui
= 0;
1734 FLOAT position
= 0.0f
;
1735 for (int i
=0; i
<len
;) {
1737 position
= poses
.buffer
[ui
];
1738 if (Platform::IsDBCSLeadByte(codePageText
, s
[i
])) {
1739 positions
[i
] = position
;
1740 positions
[i
+1] = position
;
1743 positions
[i
] = position
;
1752 XYPOSITION
SurfaceD2D::WidthChar(Font
&font_
, char ch
) {
1755 if (pIDWriteFactory
&& pTextFormat
) {
1757 IDWriteTextLayout
*pTextLayout
= 0;
1758 const WCHAR wch
= ch
;
1759 HRESULT hr
= pIDWriteFactory
->CreateTextLayout(&wch
, 1, pTextFormat
, 1000.0, 1000.0, &pTextLayout
);
1760 if (SUCCEEDED(hr
)) {
1761 DWRITE_TEXT_METRICS textMetrics
;
1762 if (SUCCEEDED(pTextLayout
->GetMetrics(&textMetrics
)))
1763 width
= textMetrics
.widthIncludingTrailingWhitespace
;
1764 pTextLayout
->Release();
1770 XYPOSITION
SurfaceD2D::Ascent(Font
&font_
) {
1772 return ceil(yAscent
);
1775 XYPOSITION
SurfaceD2D::Descent(Font
&font_
) {
1777 return ceil(yDescent
);
1780 XYPOSITION
SurfaceD2D::InternalLeading(Font
&font_
) {
1782 return floor(yInternalLeading
);
1785 XYPOSITION
SurfaceD2D::ExternalLeading(Font
&) {
1786 // Not implemented, always return one
1790 XYPOSITION
SurfaceD2D::Height(Font
&font_
) {
1791 return Ascent(font_
) + Descent(font_
);
1794 XYPOSITION
SurfaceD2D::AverageCharWidth(Font
&font_
) {
1797 if (pIDWriteFactory
&& pTextFormat
) {
1799 IDWriteTextLayout
*pTextLayout
= 0;
1800 const WCHAR wszAllAlpha
[] = L
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1801 const size_t lenAllAlpha
= wcslen(wszAllAlpha
);
1802 HRESULT hr
= pIDWriteFactory
->CreateTextLayout(wszAllAlpha
, static_cast<UINT32
>(lenAllAlpha
),
1803 pTextFormat
, 1000.0, 1000.0, &pTextLayout
);
1804 if (SUCCEEDED(hr
)) {
1805 DWRITE_TEXT_METRICS textMetrics
;
1806 if (SUCCEEDED(pTextLayout
->GetMetrics(&textMetrics
)))
1807 width
= textMetrics
.width
/ lenAllAlpha
;
1808 pTextLayout
->Release();
1814 void SurfaceD2D::SetClip(PRectangle rc
) {
1815 if (pRenderTarget
) {
1816 D2D1_RECT_F rcClip
= {rc
.left
, rc
.top
, rc
.right
, rc
.bottom
};
1817 pRenderTarget
->PushAxisAlignedClip(rcClip
, D2D1_ANTIALIAS_MODE_ALIASED
);
1822 void SurfaceD2D::FlushCachedState() {
1825 void SurfaceD2D::SetUnicodeMode(bool unicodeMode_
) {
1826 unicodeMode
=unicodeMode_
;
1829 void SurfaceD2D::SetDBCSMode(int codePage_
) {
1830 // No action on window as automatically handled by system.
1831 codePage
= codePage_
;
1835 Surface
*Surface::Allocate(int technology
) {
1836 #if defined(USE_D2D)
1837 if (technology
== SCWIN_TECH_GDI
)
1838 return new SurfaceGDI
;
1840 return new SurfaceD2D
;
1842 return new SurfaceGDI
;
1849 void Window::Destroy() {
1851 ::DestroyWindow(reinterpret_cast<HWND
>(wid
));
1855 bool Window::HasFocus() {
1856 return ::GetFocus() == wid
;
1859 PRectangle
Window::GetPosition() {
1861 ::GetWindowRect(reinterpret_cast<HWND
>(wid
), &rc
);
1862 return PRectangle::FromInts(rc
.left
, rc
.top
, rc
.right
, rc
.bottom
);
1865 void Window::SetPosition(PRectangle rc
) {
1866 ::SetWindowPos(reinterpret_cast<HWND
>(wid
),
1867 0, static_cast<int>(rc
.left
), static_cast<int>(rc
.top
),
1868 static_cast<int>(rc
.Width()), static_cast<int>(rc
.Height()), SWP_NOZORDER
| SWP_NOACTIVATE
);
1871 static RECT
RectFromMonitor(HMONITOR hMonitor
) {
1872 if (GetMonitorInfoFn
) {
1873 MONITORINFO mi
= {0};
1874 mi
.cbSize
= sizeof(mi
);
1875 if (GetMonitorInfoFn(hMonitor
, &mi
)) {
1879 RECT rc
= {0, 0, 0, 0};
1880 if (::SystemParametersInfoA(SPI_GETWORKAREA
, 0, &rc
, 0) == 0) {
1889 void Window::SetPositionRelative(PRectangle rc
, Window w
) {
1890 LONG style
= ::GetWindowLong(reinterpret_cast<HWND
>(wid
), GWL_STYLE
);
1891 if (style
& WS_POPUP
) {
1892 POINT ptOther
= {0, 0};
1893 ::ClientToScreen(reinterpret_cast<HWND
>(w
.GetID()), &ptOther
);
1894 rc
.Move(static_cast<XYPOSITION
>(ptOther
.x
), static_cast<XYPOSITION
>(ptOther
.y
));
1896 RECT rcMonitor
= RectFromPRectangle(rc
);
1898 HMONITOR hMonitor
= NULL
;
1899 if (MonitorFromRectFn
)
1900 hMonitor
= MonitorFromRectFn(&rcMonitor
, MONITOR_DEFAULTTONEAREST
);
1901 // If hMonitor is NULL, that's just the main screen anyways.
1902 //::GetMonitorInfo(hMonitor, &mi);
1903 RECT rcWork
= RectFromMonitor(hMonitor
);
1905 if (rcWork
.left
< rcWork
.right
) {
1906 // Now clamp our desired rectangle to fit inside the work area
1907 // This way, the menu will fit wholly on one screen. An improvement even
1908 // if you don't have a second monitor on the left... Menu's appears half on
1909 // one screen and half on the other are just U.G.L.Y.!
1910 if (rc
.right
> rcWork
.right
)
1911 rc
.Move(rcWork
.right
- rc
.right
, 0);
1912 if (rc
.bottom
> rcWork
.bottom
)
1913 rc
.Move(0, rcWork
.bottom
- rc
.bottom
);
1914 if (rc
.left
< rcWork
.left
)
1915 rc
.Move(rcWork
.left
- rc
.left
, 0);
1916 if (rc
.top
< rcWork
.top
)
1917 rc
.Move(0, rcWork
.top
- rc
.top
);
1923 PRectangle
Window::GetClientPosition() {
1926 ::GetClientRect(reinterpret_cast<HWND
>(wid
), &rc
);
1927 return PRectangle::FromInts(rc
.left
, rc
.top
, rc
.right
, rc
.bottom
);
1930 void Window::Show(bool show
) {
1932 ::ShowWindow(reinterpret_cast<HWND
>(wid
), SW_SHOWNOACTIVATE
);
1934 ::ShowWindow(reinterpret_cast<HWND
>(wid
), SW_HIDE
);
1937 void Window::InvalidateAll() {
1938 ::InvalidateRect(reinterpret_cast<HWND
>(wid
), NULL
, FALSE
);
1941 void Window::InvalidateRectangle(PRectangle rc
) {
1942 RECT rcw
= RectFromPRectangle(rc
);
1943 ::InvalidateRect(reinterpret_cast<HWND
>(wid
), &rcw
, FALSE
);
1946 static LRESULT
Window_SendMessage(Window
*w
, UINT msg
, WPARAM wParam
=0, LPARAM lParam
=0) {
1947 return ::SendMessage(reinterpret_cast<HWND
>(w
->GetID()), msg
, wParam
, lParam
);
1950 void Window::SetFont(Font
&font
) {
1951 Window_SendMessage(this, WM_SETFONT
,
1952 reinterpret_cast<WPARAM
>(font
.GetID()), 0);
1955 static void FlipBitmap(HBITMAP bitmap
, int width
, int height
) {
1956 HDC hdc
= ::CreateCompatibleDC(NULL
);
1958 HGDIOBJ prevBmp
= ::SelectObject(hdc
, bitmap
);
1959 ::StretchBlt(hdc
, width
- 1, 0, -width
, height
, hdc
, 0, 0, width
, height
, SRCCOPY
);
1960 ::SelectObject(hdc
, prevBmp
);
1965 static HCURSOR
GetReverseArrowCursor() {
1966 if (reverseArrowCursor
!= NULL
)
1967 return reverseArrowCursor
;
1969 ::EnterCriticalSection(&crPlatformLock
);
1970 HCURSOR cursor
= reverseArrowCursor
;
1971 if (cursor
== NULL
) {
1972 cursor
= ::LoadCursor(NULL
, IDC_ARROW
);
1974 if (::GetIconInfo(cursor
, &info
)) {
1976 if (::GetObject(info
.hbmMask
, sizeof(bmp
), &bmp
)) {
1977 FlipBitmap(info
.hbmMask
, bmp
.bmWidth
, bmp
.bmHeight
);
1978 if (info
.hbmColor
!= NULL
)
1979 FlipBitmap(info
.hbmColor
, bmp
.bmWidth
, bmp
.bmHeight
);
1980 info
.xHotspot
= (DWORD
)bmp
.bmWidth
- 1 - info
.xHotspot
;
1982 reverseArrowCursor
= ::CreateIconIndirect(&info
);
1983 if (reverseArrowCursor
!= NULL
)
1984 cursor
= reverseArrowCursor
;
1987 ::DeleteObject(info
.hbmMask
);
1988 if (info
.hbmColor
!= NULL
)
1989 ::DeleteObject(info
.hbmColor
);
1992 ::LeaveCriticalSection(&crPlatformLock
);
1996 void Window::SetCursor(Cursor curs
) {
1999 ::SetCursor(::LoadCursor(NULL
,IDC_IBEAM
));
2002 ::SetCursor(::LoadCursor(NULL
,IDC_UPARROW
));
2005 ::SetCursor(::LoadCursor(NULL
,IDC_WAIT
));
2008 ::SetCursor(::LoadCursor(NULL
,IDC_SIZEWE
));
2011 ::SetCursor(::LoadCursor(NULL
,IDC_SIZENS
));
2014 ::SetCursor(::LoadCursor(NULL
,IDC_HAND
));
2016 case cursorReverseArrow
:
2017 ::SetCursor(GetReverseArrowCursor());
2020 case cursorInvalid
: // Should not occur, but just in case.
2021 ::SetCursor(::LoadCursor(NULL
,IDC_ARROW
));
2026 void Window::SetTitle(const char *s
) {
2027 ::SetWindowTextA(reinterpret_cast<HWND
>(wid
), s
);
2030 /* Returns rectangle of monitor pt is on, both rect and pt are in Window's
2032 PRectangle
Window::GetMonitorRect(Point pt
) {
2033 // MonitorFromPoint and GetMonitorInfo are not available on Windows 95 and NT 4.
2034 PRectangle rcPosition
= GetPosition();
2035 POINT ptDesktop
= {static_cast<LONG
>(pt
.x
+ rcPosition
.left
),
2036 static_cast<LONG
>(pt
.y
+ rcPosition
.top
)};
2037 HMONITOR hMonitor
= NULL
;
2038 if (MonitorFromPointFn
)
2039 hMonitor
= MonitorFromPointFn(ptDesktop
, MONITOR_DEFAULTTONEAREST
);
2041 RECT rcWork
= RectFromMonitor(hMonitor
);
2042 if (rcWork
.left
< rcWork
.right
) {
2043 PRectangle
rcMonitor(
2044 rcWork
.left
- rcPosition
.left
,
2045 rcWork
.top
- rcPosition
.top
,
2046 rcWork
.right
- rcPosition
.left
,
2047 rcWork
.bottom
- rcPosition
.top
);
2050 return PRectangle();
2054 struct ListItemData
{
2060 std::vector
<char> words
;
2062 std::vector
<ListItemData
> data
;
2075 ListItemData
Get(int index
) const {
2076 if (index
>= 0 && index
< static_cast<int>(data
.size())) {
2079 ListItemData missing
= {"", -1};
2084 return static_cast<int>(data
.size());
2087 void AllocItem(const char *text
, int pixId
) {
2088 ListItemData lid
= { text
, pixId
};
2089 data
.push_back(lid
);
2092 char *SetWords(const char *s
) {
2093 words
= std::vector
<char>(s
, s
+strlen(s
)+1);
2098 const TCHAR ListBoxX_ClassName
[] = TEXT("ListBoxX");
2100 ListBox::ListBox() {
2103 ListBox::~ListBox() {
2106 class ListBoxX
: public ListBox
{
2110 RGBAImageSet images
;
2114 int desiredVisibleRows
;
2115 unsigned int maxItemCharacters
;
2116 unsigned int aveCharWidth
;
2119 CallBackAction doubleClickAction
;
2120 void *doubleClickActionData
;
2121 const char *widestItem
;
2122 unsigned int maxCharWidth
;
2124 PRectangle rcPreSize
;
2126 Point location
; // Caret location at which the list is opened
2127 int wheelDelta
; // mouse wheel residue
2129 HWND
GetHWND() const;
2130 void AppendListItem(const char *text
, const char *numword
);
2131 static void AdjustWindowRect(PRectangle
*rc
);
2132 int ItemHeight() const;
2133 int MinClientWidth() const;
2134 int TextOffset() const;
2135 POINT
GetClientExtent() const;
2136 POINT
MinTrackSize() const;
2137 POINT
MaxTrackSize() const;
2138 void SetRedraw(bool on
);
2139 void OnDoubleClick();
2140 void ResizeToCursor();
2141 void StartResize(WPARAM
);
2142 LRESULT
NcHitTest(WPARAM
, LPARAM
) const;
2143 void CentreItem(int n
);
2145 static LRESULT PASCAL
ControlWndProc(HWND hWnd
, UINT iMessage
, WPARAM wParam
, LPARAM lParam
);
2147 static const Point ItemInset
; // Padding around whole item
2148 static const Point TextInset
; // Padding around text
2149 static const Point ImageInset
; // Padding around image
2152 ListBoxX() : lineHeight(10), fontCopy(0), technology(0), lb(0), unicodeMode(false),
2153 desiredVisibleRows(5), maxItemCharacters(0), aveCharWidth(8),
2154 parent(NULL
), ctrlID(0), doubleClickAction(NULL
), doubleClickActionData(NULL
),
2155 widestItem(NULL
), maxCharWidth(1), resizeHit(0), wheelDelta(0) {
2157 virtual ~ListBoxX() {
2159 ::DeleteObject(fontCopy
);
2163 virtual void SetFont(Font
&font
);
2164 virtual void Create(Window
&parent_
, int ctrlID_
, Point location_
, int lineHeight_
, bool unicodeMode_
, int technology_
);
2165 virtual void SetAverageCharWidth(int width
);
2166 virtual void SetVisibleRows(int rows
);
2167 virtual int GetVisibleRows() const;
2168 virtual PRectangle
GetDesiredRect();
2169 virtual int CaretFromEdge();
2170 virtual void Clear();
2171 virtual void Append(char *s
, int type
= -1);
2172 virtual int Length();
2173 virtual void Select(int n
);
2174 virtual int GetSelection();
2175 virtual int Find(const char *prefix
);
2176 virtual void GetValue(int n
, char *value
, int len
);
2177 virtual void RegisterImage(int type
, const char *xpm_data
);
2178 virtual void RegisterRGBAImage(int type
, int width
, int height
, const unsigned char *pixelsImage
);
2179 virtual void ClearRegisteredImages();
2180 virtual void SetDoubleClickAction(CallBackAction action
, void *data
) {
2181 doubleClickAction
= action
;
2182 doubleClickActionData
= data
;
2184 virtual void SetList(const char *list
, char separator
, char typesep
);
2185 void Draw(DRAWITEMSTRUCT
*pDrawItem
);
2186 LRESULT
WndProc(HWND hWnd
, UINT iMessage
, WPARAM wParam
, LPARAM lParam
);
2187 static LRESULT PASCAL
StaticWndProc(HWND hWnd
, UINT iMessage
, WPARAM wParam
, LPARAM lParam
);
2190 const Point
ListBoxX::ItemInset(0, 0);
2191 const Point
ListBoxX::TextInset(2, 0);
2192 const Point
ListBoxX::ImageInset(1, 0);
2194 ListBox
*ListBox::Allocate() {
2195 ListBoxX
*lb
= new ListBoxX();
2199 void ListBoxX::Create(Window
&parent_
, int ctrlID_
, Point location_
, int lineHeight_
, bool unicodeMode_
, int technology_
) {
2202 location
= location_
;
2203 lineHeight
= lineHeight_
;
2204 unicodeMode
= unicodeMode_
;
2205 technology
= technology_
;
2206 HWND hwndParent
= reinterpret_cast<HWND
>(parent
->GetID());
2207 HINSTANCE hinstanceParent
= GetWindowInstance(hwndParent
);
2208 // Window created as popup so not clipped within parent client area
2209 wid
= ::CreateWindowEx(
2210 WS_EX_WINDOWEDGE
, ListBoxX_ClassName
, TEXT(""),
2211 WS_POPUP
| WS_THICKFRAME
,
2212 100,100, 150,80, hwndParent
,
2217 POINT locationw
= {static_cast<LONG
>(location
.x
), static_cast<LONG
>(location
.y
)};
2218 ::MapWindowPoints(hwndParent
, NULL
, &locationw
, 1);
2219 location
= Point::FromInts(locationw
.x
, locationw
.y
);
2222 void ListBoxX::SetFont(Font
&font
) {
2225 ::DeleteObject(fontCopy
);
2228 FormatAndMetrics
*pfm
= reinterpret_cast<FormatAndMetrics
*>(font
.GetID());
2229 fontCopy
= pfm
->HFont();
2230 ::SendMessage(lb
, WM_SETFONT
, reinterpret_cast<WPARAM
>(fontCopy
), 0);
2234 void ListBoxX::SetAverageCharWidth(int width
) {
2235 aveCharWidth
= width
;
2238 void ListBoxX::SetVisibleRows(int rows
) {
2239 desiredVisibleRows
= rows
;
2242 int ListBoxX::GetVisibleRows() const {
2243 return desiredVisibleRows
;
2246 HWND
ListBoxX::GetHWND() const {
2247 return reinterpret_cast<HWND
>(GetID());
2250 PRectangle
ListBoxX::GetDesiredRect() {
2251 PRectangle rcDesired
= GetPosition();
2253 int rows
= Length();
2254 if ((rows
== 0) || (rows
> desiredVisibleRows
))
2255 rows
= desiredVisibleRows
;
2256 rcDesired
.bottom
= rcDesired
.top
+ ItemHeight() * rows
;
2258 int width
= MinClientWidth();
2259 HDC hdc
= ::GetDC(lb
);
2260 HFONT oldFont
= SelectFont(hdc
, fontCopy
);
2261 SIZE textSize
= {0, 0};
2264 len
= static_cast<int>(strlen(widestItem
));
2266 const TextWide
tbuf(widestItem
, len
, unicodeMode
);
2267 ::GetTextExtentPoint32W(hdc
, tbuf
.buffer
, tbuf
.tlen
, &textSize
);
2269 ::GetTextExtentPoint32A(hdc
, widestItem
, len
, &textSize
);
2273 ::GetTextMetrics(hdc
, &tm
);
2274 maxCharWidth
= tm
.tmMaxCharWidth
;
2275 SelectFont(hdc
, oldFont
);
2276 ::ReleaseDC(lb
, hdc
);
2278 int widthDesired
= Platform::Maximum(textSize
.cx
, (len
+ 1) * tm
.tmAveCharWidth
);
2279 if (width
< widthDesired
)
2280 width
= widthDesired
;
2282 rcDesired
.right
= rcDesired
.left
+ TextOffset() + width
+ (TextInset
.x
* 2);
2283 if (Length() > rows
)
2284 rcDesired
.right
+= ::GetSystemMetrics(SM_CXVSCROLL
);
2286 AdjustWindowRect(&rcDesired
);
2290 int ListBoxX::TextOffset() const {
2291 int pixWidth
= images
.GetWidth();
2292 return static_cast<int>(pixWidth
== 0 ? ItemInset
.x
: ItemInset
.x
+ pixWidth
+ (ImageInset
.x
* 2));
2295 int ListBoxX::CaretFromEdge() {
2297 AdjustWindowRect(&rc
);
2298 return TextOffset() + static_cast<int>(TextInset
.x
+ (0 - rc
.left
) - 1);
2301 void ListBoxX::Clear() {
2302 ::SendMessage(lb
, LB_RESETCONTENT
, 0, 0);
2303 maxItemCharacters
= 0;
2308 void ListBoxX::Append(char *, int) {
2309 // This method is no longer called in Scintilla
2310 PLATFORM_ASSERT(false);
2313 int ListBoxX::Length() {
2317 void ListBoxX::Select(int n
) {
2318 // We are going to scroll to centre on the new selection and then select it, so disable
2319 // redraw to avoid flicker caused by a painting new selection twice in unselected and then
2323 ::SendMessage(lb
, LB_SETCURSEL
, n
, 0);
2327 int ListBoxX::GetSelection() {
2328 return static_cast<int>(::SendMessage(lb
, LB_GETCURSEL
, 0, 0));
2331 // This is not actually called at present
2332 int ListBoxX::Find(const char *) {
2336 void ListBoxX::GetValue(int n
, char *value
, int len
) {
2337 ListItemData item
= lti
.Get(n
);
2338 strncpy(value
, item
.text
, len
);
2339 value
[len
-1] = '\0';
2342 void ListBoxX::RegisterImage(int type
, const char *xpm_data
) {
2343 XPM
xpmImage(xpm_data
);
2344 images
.Add(type
, new RGBAImage(xpmImage
));
2347 void ListBoxX::RegisterRGBAImage(int type
, int width
, int height
, const unsigned char *pixelsImage
) {
2348 images
.Add(type
, new RGBAImage(width
, height
, 1.0, pixelsImage
));
2351 void ListBoxX::ClearRegisteredImages() {
2355 void ListBoxX::Draw(DRAWITEMSTRUCT
*pDrawItem
) {
2356 if ((pDrawItem
->itemAction
== ODA_SELECT
) || (pDrawItem
->itemAction
== ODA_DRAWENTIRE
)) {
2357 RECT rcBox
= pDrawItem
->rcItem
;
2358 rcBox
.left
+= TextOffset();
2359 if (pDrawItem
->itemState
& ODS_SELECTED
) {
2360 RECT rcImage
= pDrawItem
->rcItem
;
2361 rcImage
.right
= rcBox
.left
;
2362 // The image is not highlighted
2363 ::FillRect(pDrawItem
->hDC
, &rcImage
, reinterpret_cast<HBRUSH
>(COLOR_WINDOW
+1));
2364 ::FillRect(pDrawItem
->hDC
, &rcBox
, reinterpret_cast<HBRUSH
>(COLOR_HIGHLIGHT
+1));
2365 ::SetBkColor(pDrawItem
->hDC
, ::GetSysColor(COLOR_HIGHLIGHT
));
2366 ::SetTextColor(pDrawItem
->hDC
, ::GetSysColor(COLOR_HIGHLIGHTTEXT
));
2368 ::FillRect(pDrawItem
->hDC
, &pDrawItem
->rcItem
, reinterpret_cast<HBRUSH
>(COLOR_WINDOW
+1));
2369 ::SetBkColor(pDrawItem
->hDC
, ::GetSysColor(COLOR_WINDOW
));
2370 ::SetTextColor(pDrawItem
->hDC
, ::GetSysColor(COLOR_WINDOWTEXT
));
2373 ListItemData item
= lti
.Get(pDrawItem
->itemID
);
2374 int pixId
= item
.pixId
;
2375 const char *text
= item
.text
;
2376 int len
= static_cast<int>(strlen(text
));
2378 RECT rcText
= rcBox
;
2379 ::InsetRect(&rcText
, static_cast<int>(TextInset
.x
), static_cast<int>(TextInset
.y
));
2382 const TextWide
tbuf(text
, len
, unicodeMode
);
2383 ::DrawTextW(pDrawItem
->hDC
, tbuf
.buffer
, tbuf
.tlen
, &rcText
, DT_NOPREFIX
|DT_END_ELLIPSIS
|DT_SINGLELINE
|DT_NOCLIP
);
2385 ::DrawTextA(pDrawItem
->hDC
, text
, len
, &rcText
, DT_NOPREFIX
|DT_END_ELLIPSIS
|DT_SINGLELINE
|DT_NOCLIP
);
2387 if (pDrawItem
->itemState
& ODS_SELECTED
) {
2388 ::DrawFocusRect(pDrawItem
->hDC
, &rcBox
);
2391 // Draw the image, if any
2392 RGBAImage
*pimage
= images
.Get(pixId
);
2394 Surface
*surfaceItem
= Surface::Allocate(technology
);
2396 if (technology
== SCWIN_TECH_GDI
) {
2397 surfaceItem
->Init(pDrawItem
->hDC
, pDrawItem
->hwndItem
);
2398 long left
= pDrawItem
->rcItem
.left
+ static_cast<int>(ItemInset
.x
+ ImageInset
.x
);
2399 PRectangle rcImage
= PRectangle::FromInts(left
, pDrawItem
->rcItem
.top
,
2400 left
+ images
.GetWidth(), pDrawItem
->rcItem
.bottom
);
2401 surfaceItem
->DrawRGBAImage(rcImage
,
2402 pimage
->GetWidth(), pimage
->GetHeight(), pimage
->Pixels());
2404 ::SetTextAlign(pDrawItem
->hDC
, TA_TOP
);
2406 #if defined(USE_D2D)
2407 D2D1_RENDER_TARGET_PROPERTIES props
= D2D1::RenderTargetProperties(
2408 D2D1_RENDER_TARGET_TYPE_DEFAULT
,
2410 DXGI_FORMAT_B8G8R8A8_UNORM
,
2411 D2D1_ALPHA_MODE_IGNORE
),
2414 D2D1_RENDER_TARGET_USAGE_NONE
,
2415 D2D1_FEATURE_LEVEL_DEFAULT
2417 ID2D1DCRenderTarget
*pDCRT
= 0;
2418 HRESULT hr
= pD2DFactory
->CreateDCRenderTarget(&props
, &pDCRT
);
2419 if (SUCCEEDED(hr
)) {
2421 GetClientRect(pDrawItem
->hwndItem
, &rcWindow
);
2422 hr
= pDCRT
->BindDC(pDrawItem
->hDC
, &rcWindow
);
2423 if (SUCCEEDED(hr
)) {
2424 surfaceItem
->Init(pDCRT
, pDrawItem
->hwndItem
);
2426 long left
= pDrawItem
->rcItem
.left
+ static_cast<long>(ItemInset
.x
+ ImageInset
.x
);
2427 PRectangle rcImage
= PRectangle::FromInts(left
, pDrawItem
->rcItem
.top
,
2428 left
+ images
.GetWidth(), pDrawItem
->rcItem
.bottom
);
2429 surfaceItem
->DrawRGBAImage(rcImage
,
2430 pimage
->GetWidth(), pimage
->GetHeight(), pimage
->Pixels());
2447 void ListBoxX::AppendListItem(const char *text
, const char *numword
) {
2452 while ((ch
= *++numword
) != '\0') {
2453 pixId
= 10 * pixId
+ (ch
- '0');
2457 lti
.AllocItem(text
, pixId
);
2458 unsigned int len
= static_cast<unsigned int>(strlen(text
));
2459 if (maxItemCharacters
< len
) {
2460 maxItemCharacters
= len
;
2465 void ListBoxX::SetList(const char *list
, char separator
, char typesep
) {
2466 // Turn off redraw while populating the list - this has a significant effect, even if
2467 // the listbox is not visible.
2470 size_t size
= strlen(list
);
2471 char *words
= lti
.SetWords(list
);
2472 char *startword
= words
;
2473 char *numword
= NULL
;
2474 for (size_t i
=0; i
< size
; i
++) {
2475 if (words
[i
] == separator
) {
2479 AppendListItem(startword
, numword
);
2480 startword
= words
+ i
+ 1;
2482 } else if (words
[i
] == typesep
) {
2483 numword
= words
+ i
;
2489 AppendListItem(startword
, numword
);
2492 // Finally populate the listbox itself with the correct number of items
2493 int count
= lti
.Count();
2494 ::SendMessage(lb
, LB_INITSTORAGE
, count
, 0);
2495 for (int j
=0; j
<count
; j
++) {
2496 ::SendMessage(lb
, LB_ADDSTRING
, 0, j
+1);
2501 void ListBoxX::AdjustWindowRect(PRectangle
*rc
) {
2502 RECT rcw
= RectFromPRectangle(*rc
);
2503 ::AdjustWindowRectEx(&rcw
, WS_THICKFRAME
, false, WS_EX_WINDOWEDGE
);
2504 *rc
= PRectangle::FromInts(rcw
.left
, rcw
.top
, rcw
.right
, rcw
.bottom
);
2507 int ListBoxX::ItemHeight() const {
2508 int itemHeight
= lineHeight
+ (static_cast<int>(TextInset
.y
) * 2);
2509 int pixHeight
= images
.GetHeight() + (static_cast<int>(ImageInset
.y
) * 2);
2510 if (itemHeight
< pixHeight
) {
2511 itemHeight
= pixHeight
;
2516 int ListBoxX::MinClientWidth() const {
2517 return 12 * (aveCharWidth
+aveCharWidth
/3);
2520 POINT
ListBoxX::MinTrackSize() const {
2521 PRectangle rc
= PRectangle::FromInts(0, 0, MinClientWidth(), ItemHeight());
2522 AdjustWindowRect(&rc
);
2523 POINT ret
= {static_cast<LONG
>(rc
.Width()), static_cast<LONG
>(rc
.Height())};
2527 POINT
ListBoxX::MaxTrackSize() const {
2528 PRectangle rc
= PRectangle::FromInts(0, 0,
2529 Platform::Maximum(MinClientWidth(),
2530 maxCharWidth
* maxItemCharacters
+ static_cast<int>(TextInset
.x
) * 2 +
2531 TextOffset() + ::GetSystemMetrics(SM_CXVSCROLL
)),
2532 ItemHeight() * lti
.Count());
2533 AdjustWindowRect(&rc
);
2534 POINT ret
= {static_cast<LONG
>(rc
.Width()), static_cast<LONG
>(rc
.Height())};
2538 void ListBoxX::SetRedraw(bool on
) {
2539 ::SendMessage(lb
, WM_SETREDRAW
, static_cast<BOOL
>(on
), 0);
2541 ::InvalidateRect(lb
, NULL
, TRUE
);
2544 static XYPOSITION
XYMinimum(XYPOSITION a
, XYPOSITION b
) {
2551 static XYPOSITION
XYMaximum(XYPOSITION a
, XYPOSITION b
) {
2558 void ListBoxX::ResizeToCursor() {
2559 PRectangle rc
= GetPosition();
2561 ::GetCursorPos(&ptw
);
2562 Point pt
= Point::FromInts(ptw
.x
, ptw
.y
);
2563 pt
.x
+= dragOffset
.x
;
2564 pt
.y
+= dragOffset
.y
;
2566 switch (resizeHit
) {
2597 POINT ptMin
= MinTrackSize();
2598 POINT ptMax
= MaxTrackSize();
2599 // We don't allow the left edge to move at present, but just in case
2600 rc
.left
= XYMaximum(XYMinimum(rc
.left
, rcPreSize
.right
- ptMin
.x
), rcPreSize
.right
- ptMax
.x
);
2601 rc
.top
= XYMaximum(XYMinimum(rc
.top
, rcPreSize
.bottom
- ptMin
.y
), rcPreSize
.bottom
- ptMax
.y
);
2602 rc
.right
= XYMaximum(XYMinimum(rc
.right
, rcPreSize
.left
+ ptMax
.x
), rcPreSize
.left
+ ptMin
.x
);
2603 rc
.bottom
= XYMaximum(XYMinimum(rc
.bottom
, rcPreSize
.top
+ ptMax
.y
), rcPreSize
.top
+ ptMin
.y
);
2608 void ListBoxX::StartResize(WPARAM hitCode
) {
2609 rcPreSize
= GetPosition();
2611 ::GetCursorPos(&cursorPos
);
2617 dragOffset
.x
= rcPreSize
.right
- cursorPos
.x
;
2618 dragOffset
.y
= rcPreSize
.bottom
- cursorPos
.y
;
2622 dragOffset
.x
= rcPreSize
.right
- cursorPos
.x
;
2623 dragOffset
.y
= rcPreSize
.top
- cursorPos
.y
;
2626 // Note that the current hit test code prevents the left edge cases ever firing
2627 // as we don't want the left edge to be moveable
2631 dragOffset
.x
= rcPreSize
.left
- cursorPos
.x
;
2632 dragOffset
.y
= rcPreSize
.top
- cursorPos
.y
;
2635 dragOffset
.x
= rcPreSize
.left
- cursorPos
.x
;
2636 dragOffset
.y
= rcPreSize
.bottom
- cursorPos
.y
;
2643 ::SetCapture(GetHWND());
2644 resizeHit
= static_cast<int>(hitCode
);
2647 LRESULT
ListBoxX::NcHitTest(WPARAM wParam
, LPARAM lParam
) const {
2648 LRESULT hit
= ::DefWindowProc(GetHWND(), WM_NCHITTEST
, wParam
, lParam
);
2649 // There is an apparent bug in the DefWindowProc hit test code whereby it will
2650 // return HTTOPXXX if the window in question is shorter than the default
2651 // window caption height + frame, even if one is hovering over the bottom edge of
2652 // the frame, so workaround that here
2653 if (hit
>= HTTOP
&& hit
<= HTTOPRIGHT
) {
2654 int minHeight
= GetSystemMetrics(SM_CYMINTRACK
);
2655 PRectangle rc
= const_cast<ListBoxX
*>(this)->GetPosition();
2656 int yPos
= GET_Y_LPARAM(lParam
);
2657 if ((rc
.Height() < minHeight
) && (yPos
> ((rc
.top
+ rc
.bottom
)/2))) {
2658 hit
+= HTBOTTOM
- HTTOP
;
2662 // Nerver permit resizing that moves the left edge. Allow movement of top or bottom edge
2663 // depending on whether the list is above or below the caret
2673 PRectangle rc
= const_cast<ListBoxX
*>(this)->GetPosition();
2674 // Valid only if caret below list
2675 if (location
.y
< rc
.top
)
2681 case HTBOTTOMRIGHT
: {
2682 PRectangle rc
= const_cast<ListBoxX
*>(this)->GetPosition();
2683 // Valid only if caret above list
2684 if (rc
.bottom
< location
.y
)
2693 void ListBoxX::OnDoubleClick() {
2695 if (doubleClickAction
!= NULL
) {
2696 doubleClickAction(doubleClickActionData
);
2700 POINT
ListBoxX::GetClientExtent() const {
2701 PRectangle rc
= const_cast<ListBoxX
*>(this)->GetClientPosition();
2703 ret
.x
= static_cast<LONG
>(rc
.Width());
2704 ret
.y
= static_cast<LONG
>(rc
.Height());
2708 void ListBoxX::CentreItem(int n
) {
2709 // If below mid point, scroll up to centre, but with more items below if uneven
2711 POINT extent
= GetClientExtent();
2712 int visible
= extent
.y
/ItemHeight();
2713 if (visible
< Length()) {
2714 LRESULT top
= ::SendMessage(lb
, LB_GETTOPINDEX
, 0, 0);
2715 int half
= (visible
- 1) / 2;
2716 if (n
> (top
+ half
))
2717 ::SendMessage(lb
, LB_SETTOPINDEX
, n
- half
, 0);
2722 // Performs a double-buffered paint operation to avoid flicker
2723 void ListBoxX::Paint(HDC hDC
) {
2724 POINT extent
= GetClientExtent();
2725 HBITMAP hBitmap
= ::CreateCompatibleBitmap(hDC
, extent
.x
, extent
.y
);
2726 HDC bitmapDC
= ::CreateCompatibleDC(hDC
);
2727 HBITMAP hBitmapOld
= SelectBitmap(bitmapDC
, hBitmap
);
2728 // The list background is mainly erased during painting, but can be a small
2729 // unpainted area when at the end of a non-integrally sized list with a
2730 // vertical scroll bar
2731 RECT rc
= { 0, 0, extent
.x
, extent
.y
};
2732 ::FillRect(bitmapDC
, &rc
, reinterpret_cast<HBRUSH
>(COLOR_WINDOW
+1));
2733 // Paint the entire client area and vertical scrollbar
2734 ::SendMessage(lb
, WM_PRINT
, reinterpret_cast<WPARAM
>(bitmapDC
), PRF_CLIENT
|PRF_NONCLIENT
);
2735 ::BitBlt(hDC
, 0, 0, extent
.x
, extent
.y
, bitmapDC
, 0, 0, SRCCOPY
);
2736 // Select a stock brush to prevent warnings from BoundsChecker
2737 ::SelectObject(bitmapDC
, GetStockFont(WHITE_BRUSH
));
2738 SelectBitmap(bitmapDC
, hBitmapOld
);
2739 ::DeleteDC(bitmapDC
);
2740 ::DeleteObject(hBitmap
);
2743 LRESULT PASCAL
ListBoxX::ControlWndProc(HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
) {
2751 HDC hDC
= ::BeginPaint(hWnd
, &ps
);
2752 ListBoxX
*lbx
= reinterpret_cast<ListBoxX
*>(PointerFromWindow(::GetParent(hWnd
)));
2755 ::EndPaint(hWnd
, &ps
);
2759 case WM_MOUSEACTIVATE
:
2760 // This prevents the view activating when the scrollbar is clicked
2761 return MA_NOACTIVATE
;
2763 case WM_LBUTTONDOWN
: {
2764 // We must take control of selection to prevent the ListBox activating
2766 LRESULT lResult
= ::SendMessage(hWnd
, LB_ITEMFROMPOINT
, 0, lParam
);
2767 int item
= LOWORD(lResult
);
2768 if (HIWORD(lResult
) == 0 && item
>= 0) {
2769 ::SendMessage(hWnd
, LB_SETCURSEL
, item
, 0);
2777 case WM_LBUTTONDBLCLK
: {
2778 ListBoxX
*lbx
= reinterpret_cast<ListBoxX
*>(PointerFromWindow(::GetParent(hWnd
)));
2780 lbx
->OnDoubleClick();
2785 case WM_MBUTTONDOWN
:
2786 // disable the scroll wheel button click action
2790 WNDPROC prevWndProc
= reinterpret_cast<WNDPROC
>(GetWindowLongPtr(hWnd
, GWLP_USERDATA
));
2792 return ::CallWindowProc(prevWndProc
, hWnd
, uMsg
, wParam
, lParam
);
2794 return ::DefWindowProc(hWnd
, uMsg
, wParam
, lParam
);
2798 return ::DefWindowProc(hWnd
, uMsg
, wParam
, lParam
);
2801 LRESULT
ListBoxX::WndProc(HWND hWnd
, UINT iMessage
, WPARAM wParam
, LPARAM lParam
) {
2804 HINSTANCE hinstanceParent
= GetWindowInstance(reinterpret_cast<HWND
>(parent
->GetID()));
2805 // Note that LBS_NOINTEGRALHEIGHT is specified to fix cosmetic issue when resizing the list
2806 // but has useful side effect of speeding up list population significantly
2807 lb
= ::CreateWindowEx(
2808 0, TEXT("listbox"), TEXT(""),
2809 WS_CHILD
| WS_VSCROLL
| WS_VISIBLE
|
2810 LBS_OWNERDRAWFIXED
| LBS_NODATA
| LBS_NOINTEGRALHEIGHT
,
2812 reinterpret_cast<HMENU
>(ctrlID
),
2815 WNDPROC prevWndProc
= reinterpret_cast<WNDPROC
>(::SetWindowLongPtr(lb
, GWLP_WNDPROC
, reinterpret_cast<LONG_PTR
>(ControlWndProc
)));
2816 ::SetWindowLongPtr(lb
, GWLP_USERDATA
, reinterpret_cast<LONG_PTR
>(prevWndProc
));
2823 ::SetWindowPos(lb
, 0, 0,0, LOWORD(lParam
), HIWORD(lParam
), SWP_NOZORDER
|SWP_NOACTIVATE
|SWP_NOMOVE
);
2824 // Ensure the selection remains visible
2825 CentreItem(GetSelection());
2832 ::BeginPaint(hWnd
, &ps
);
2833 ::EndPaint(hWnd
, &ps
);
2838 // This is not actually needed now - the registered double click action is used
2839 // directly to action a choice from the list.
2840 ::SendMessage(reinterpret_cast<HWND
>(parent
->GetID()), iMessage
, wParam
, lParam
);
2843 case WM_MEASUREITEM
: {
2844 MEASUREITEMSTRUCT
*pMeasureItem
= reinterpret_cast<MEASUREITEMSTRUCT
*>(lParam
);
2845 pMeasureItem
->itemHeight
= static_cast<unsigned int>(ItemHeight());
2850 Draw(reinterpret_cast<DRAWITEMSTRUCT
*>(lParam
));
2855 ::SetWindowLong(hWnd
, 0, 0);
2856 return ::DefWindowProc(hWnd
, iMessage
, wParam
, lParam
);
2859 // To reduce flicker we can elide background erasure since this window is
2860 // completely covered by its child.
2863 case WM_GETMINMAXINFO
: {
2864 MINMAXINFO
*minMax
= reinterpret_cast<MINMAXINFO
*>(lParam
);
2865 minMax
->ptMaxTrackSize
= MaxTrackSize();
2866 minMax
->ptMinTrackSize
= MinTrackSize();
2870 case WM_MOUSEACTIVATE
:
2871 return MA_NOACTIVATE
;
2874 return NcHitTest(wParam
, lParam
);
2876 case WM_NCLBUTTONDOWN
:
2877 // We have to implement our own window resizing because the DefWindowProc
2878 // implementation insists on activating the resized window
2879 StartResize(wParam
);
2882 case WM_MOUSEMOVE
: {
2883 if (resizeHit
== 0) {
2884 return ::DefWindowProc(hWnd
, iMessage
, wParam
, lParam
);
2893 if (resizeHit
!= 0) {
2897 return ::DefWindowProc(hWnd
, iMessage
, wParam
, lParam
);
2900 wheelDelta
-= static_cast<short>(HIWORD(wParam
));
2901 if (abs(wheelDelta
) >= WHEEL_DELTA
) {
2902 int nRows
= GetVisibleRows();
2903 int linesToScroll
= 1;
2905 linesToScroll
= nRows
- 1;
2907 if (linesToScroll
> 3) {
2910 linesToScroll
*= (wheelDelta
/ WHEEL_DELTA
);
2911 LRESULT top
= ::SendMessage(lb
, LB_GETTOPINDEX
, 0, 0) + linesToScroll
;
2915 ::SendMessage(lb
, LB_SETTOPINDEX
, top
, 0);
2916 // update wheel delta residue
2917 if (wheelDelta
>= 0)
2918 wheelDelta
= wheelDelta
% WHEEL_DELTA
;
2920 wheelDelta
= - (-wheelDelta
% WHEEL_DELTA
);
2925 return ::DefWindowProc(hWnd
, iMessage
, wParam
, lParam
);
2931 LRESULT PASCAL
ListBoxX::StaticWndProc(
2932 HWND hWnd
, UINT iMessage
, WPARAM wParam
, LPARAM lParam
) {
2933 if (iMessage
== WM_CREATE
) {
2934 CREATESTRUCT
*pCreate
= reinterpret_cast<CREATESTRUCT
*>(lParam
);
2935 SetWindowPointer(hWnd
, pCreate
->lpCreateParams
);
2937 // Find C++ object associated with window.
2938 ListBoxX
*lbx
= reinterpret_cast<ListBoxX
*>(PointerFromWindow(hWnd
));
2940 return lbx
->WndProc(hWnd
, iMessage
, wParam
, lParam
);
2942 return ::DefWindowProc(hWnd
, iMessage
, wParam
, lParam
);
2946 static bool ListBoxX_Register() {
2947 WNDCLASSEX wndclassc
;
2948 wndclassc
.cbSize
= sizeof(wndclassc
);
2949 // We need CS_HREDRAW and CS_VREDRAW because of the ellipsis that might be drawn for
2950 // truncated items in the list and the appearance/disappearance of the vertical scroll bar.
2951 // The list repaint is double-buffered to avoid the flicker this would otherwise cause.
2952 wndclassc
.style
= CS_GLOBALCLASS
| CS_HREDRAW
| CS_VREDRAW
;
2953 wndclassc
.cbClsExtra
= 0;
2954 wndclassc
.cbWndExtra
= sizeof(ListBoxX
*);
2955 wndclassc
.hInstance
= hinstPlatformRes
;
2956 wndclassc
.hIcon
= NULL
;
2957 wndclassc
.hbrBackground
= NULL
;
2958 wndclassc
.lpszMenuName
= NULL
;
2959 wndclassc
.lpfnWndProc
= ListBoxX::StaticWndProc
;
2960 wndclassc
.hCursor
= ::LoadCursor(NULL
, IDC_ARROW
);
2961 wndclassc
.lpszClassName
= ListBoxX_ClassName
;
2962 wndclassc
.hIconSm
= 0;
2964 return ::RegisterClassEx(&wndclassc
) != 0;
2967 bool ListBoxX_Unregister() {
2968 return ::UnregisterClass(ListBoxX_ClassName
, hinstPlatformRes
) != 0;
2971 Menu::Menu() : mid(0) {
2974 void Menu::CreatePopUp() {
2976 mid
= ::CreatePopupMenu();
2979 void Menu::Destroy() {
2981 ::DestroyMenu(reinterpret_cast<HMENU
>(mid
));
2985 void Menu::Show(Point pt
, Window
&w
) {
2986 ::TrackPopupMenu(reinterpret_cast<HMENU
>(mid
),
2987 TPM_RIGHTBUTTON
, static_cast<int>(pt
.x
- 4), static_cast<int>(pt
.y
), 0,
2988 reinterpret_cast<HWND
>(w
.GetID()), NULL
);
2992 static bool initialisedET
= false;
2993 static bool usePerformanceCounter
= false;
2994 static LARGE_INTEGER frequency
;
2996 ElapsedTime::ElapsedTime() {
2997 if (!initialisedET
) {
2998 usePerformanceCounter
= ::QueryPerformanceFrequency(&frequency
) != 0;
2999 initialisedET
= true;
3001 if (usePerformanceCounter
) {
3002 LARGE_INTEGER timeVal
;
3003 ::QueryPerformanceCounter(&timeVal
);
3004 bigBit
= timeVal
.HighPart
;
3005 littleBit
= timeVal
.LowPart
;
3012 double ElapsedTime::Duration(bool reset
) {
3017 if (usePerformanceCounter
) {
3019 ::QueryPerformanceCounter(&lEnd
);
3020 endBigBit
= lEnd
.HighPart
;
3021 endLittleBit
= lEnd
.LowPart
;
3022 LARGE_INTEGER lBegin
;
3023 lBegin
.HighPart
= bigBit
;
3024 lBegin
.LowPart
= littleBit
;
3025 double elapsed
= static_cast<double>(lEnd
.QuadPart
- lBegin
.QuadPart
);
3026 result
= elapsed
/ static_cast<double>(frequency
.QuadPart
);
3028 endBigBit
= clock();
3030 double elapsed
= endBigBit
- bigBit
;
3031 result
= elapsed
/ CLOCKS_PER_SEC
;
3035 littleBit
= endLittleBit
;
3040 class DynamicLibraryImpl
: public DynamicLibrary
{
3044 explicit DynamicLibraryImpl(const char *modulePath
) {
3045 h
= ::LoadLibraryA(modulePath
);
3048 virtual ~DynamicLibraryImpl() {
3053 // Use GetProcAddress to get a pointer to the relevant function.
3054 virtual Function
FindFunction(const char *name
) {
3056 // C++ standard doesn't like casts between function pointers and void pointers so use a union
3061 fnConv
.fp
= ::GetProcAddress(h
, name
);
3068 virtual bool IsValid() {
3073 DynamicLibrary
*DynamicLibrary::Load(const char *modulePath
) {
3074 return static_cast<DynamicLibrary
*>(new DynamicLibraryImpl(modulePath
));
3077 ColourDesired
Platform::Chrome() {
3078 return ::GetSysColor(COLOR_3DFACE
);
3081 ColourDesired
Platform::ChromeHighlight() {
3082 return ::GetSysColor(COLOR_3DHIGHLIGHT
);
3085 const char *Platform::DefaultFont() {
3089 int Platform::DefaultFontSize() {
3093 unsigned int Platform::DoubleClickTime() {
3094 return ::GetDoubleClickTime();
3097 bool Platform::MouseButtonBounce() {
3101 void Platform::DebugDisplay(const char *s
) {
3102 ::OutputDebugStringA(s
);
3105 bool Platform::IsKeyDown(int key
) {
3106 return (::GetKeyState(key
) & 0x80000000) != 0;
3109 long Platform::SendScintilla(WindowID w
, unsigned int msg
, unsigned long wParam
, long lParam
) {
3110 // This should never be called - its here to satisfy an old interface
3111 return static_cast<long>(::SendMessage(reinterpret_cast<HWND
>(w
), msg
, wParam
, lParam
));
3114 long Platform::SendScintillaPointer(WindowID w
, unsigned int msg
, unsigned long wParam
, void *lParam
) {
3115 // This should never be called - its here to satisfy an old interface
3116 return static_cast<long>(::SendMessage(reinterpret_cast<HWND
>(w
), msg
, wParam
,
3117 reinterpret_cast<LPARAM
>(lParam
)));
3120 bool Platform::IsDBCSLeadByte(int codePage
, char ch
) {
3121 // Byte ranges found in Wikipedia articles with relevant search strings in each case
3122 unsigned char uch
= static_cast<unsigned char>(ch
);
3126 return ((uch
>= 0x81) && (uch
<= 0x9F)) ||
3127 ((uch
>= 0xE0) && (uch
<= 0xEF));
3130 return (uch
>= 0x81) && (uch
<= 0xFE);
3132 // Korean Wansung KS C-5601-1987
3133 return (uch
>= 0x81) && (uch
<= 0xFE);
3136 return (uch
>= 0x81) && (uch
<= 0xFE);
3138 // Korean Johab KS C-5601-1992
3140 ((uch
>= 0x84) && (uch
<= 0xD3)) ||
3141 ((uch
>= 0xD8) && (uch
<= 0xDE)) ||
3142 ((uch
>= 0xE0) && (uch
<= 0xF9));
3147 int Platform::DBCSCharLength(int codePage
, const char *s
) {
3148 if (codePage
== 932 || codePage
== 936 || codePage
== 949 ||
3149 codePage
== 950 || codePage
== 1361) {
3150 return Platform::IsDBCSLeadByte(codePage
, s
[0]) ? 2 : 1;
3156 int Platform::DBCSCharMaxLength() {
3160 // These are utility functions not really tied to a platform
3162 int Platform::Minimum(int a
, int b
) {
3169 int Platform::Maximum(int a
, int b
) {
3179 void Platform::DebugPrintf(const char *format
, ...) {
3182 va_start(pArguments
, format
);
3183 vsprintf(buffer
,format
,pArguments
);
3185 Platform::DebugDisplay(buffer
);
3188 void Platform::DebugPrintf(const char *, ...) {
3192 static bool assertionPopUps
= true;
3194 bool Platform::ShowAssertionPopUps(bool assertionPopUps_
) {
3195 bool ret
= assertionPopUps
;
3196 assertionPopUps
= assertionPopUps_
;
3200 void Platform::Assert(const char *c
, const char *file
, int line
) {
3202 sprintf(buffer
, "Assertion [%s] failed at %s %d%s", c
, file
, line
, assertionPopUps
? "" : "\r\n");
3203 if (assertionPopUps
) {
3204 int idButton
= ::MessageBoxA(0, buffer
, "Assertion failure",
3205 MB_ABORTRETRYIGNORE
|MB_ICONHAND
|MB_SETFOREGROUND
|MB_TASKMODAL
);
3206 if (idButton
== IDRETRY
) {
3208 } else if (idButton
== IDIGNORE
) {
3214 Platform::DebugDisplay(buffer
);
3220 int Platform::Clamp(int val
, int minVal
, int maxVal
) {
3229 // GetVersionEx has been deprecated fro Windows 8.1 but called here to determine if Windows 9x.
3230 // Too dangerous to find alternate check.
3231 #pragma warning(disable: 4996)
3234 void Platform_Initialise(void *hInstance
) {
3235 ::InitializeCriticalSection(&crPlatformLock
);
3236 hinstPlatformRes
= reinterpret_cast<HINSTANCE
>(hInstance
);
3237 // This may be called from DllMain, in which case the call to LoadLibrary
3238 // is bad because it can upset the DLL load order.
3240 hDLLImage
= ::LoadLibrary(TEXT("Msimg32"));
3243 AlphaBlendFn
= (AlphaBlendSig
)::GetProcAddress(hDLLImage
, "AlphaBlend");
3246 hDLLUser32
= ::LoadLibrary(TEXT("User32"));
3249 MonitorFromPointFn
= (MonitorFromPointSig
)::GetProcAddress(hDLLUser32
, "MonitorFromPoint");
3250 MonitorFromRectFn
= (MonitorFromRectSig
)::GetProcAddress(hDLLUser32
, "MonitorFromRect");
3251 GetMonitorInfoFn
= (GetMonitorInfoSig
)::GetProcAddress(hDLLUser32
, "GetMonitorInfoA");
3254 ListBoxX_Register();
3258 #pragma warning(default: 4996)
3261 void Platform_Finalise(bool fromDllMain
) {
3262 #if defined(USE_D2D)
3264 if (defaultRenderingParams
) {
3265 defaultRenderingParams
->Release();
3266 defaultRenderingParams
= 0;
3268 if (customClearTypeRenderingParams
) {
3269 customClearTypeRenderingParams
->Release();
3270 customClearTypeRenderingParams
= 0;
3272 if (pIDWriteFactory
) {
3273 pIDWriteFactory
->Release();
3274 pIDWriteFactory
= 0;
3277 pD2DFactory
->Release();
3281 FreeLibrary(hDLLDWrite
);
3285 FreeLibrary(hDLLD2D
);
3290 if (reverseArrowCursor
!= NULL
)
3291 ::DestroyCursor(reverseArrowCursor
);
3292 ListBoxX_Unregister();
3293 ::DeleteCriticalSection(&crPlatformLock
);
3295 FreeLibrary(hDLLUser32
);
3299 FreeLibrary(hDLLImage
);
3304 #ifdef SCI_NAMESPACE