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;
70 static bool onNT
= false;
72 static HMODULE hDLLImage
= 0;
73 static AlphaBlendSig AlphaBlendFn
= 0;
75 static HMODULE hDLLUser32
= 0;
76 static HMONITOR (WINAPI
*MonitorFromPointFn
)(POINT
, DWORD
) = 0;
77 static HMONITOR (WINAPI
*MonitorFromRectFn
)(LPCRECT
, DWORD
) = 0;
78 static BOOL (WINAPI
*GetMonitorInfoFn
)(HMONITOR
, LPMONITORINFO
) = 0;
80 static HCURSOR reverseArrowCursor
= NULL
;
90 Point
Point::FromLong(long lpoint
) {
91 return Point(static_cast<short>(LOWORD(lpoint
)), static_cast<short>(HIWORD(lpoint
)));
94 static RECT
RectFromPRectangle(PRectangle prc
) {
95 RECT rc
= {static_cast<LONG
>(prc
.left
), static_cast<LONG
>(prc
.top
),
96 static_cast<LONG
>(prc
.right
), static_cast<LONG
>(prc
.bottom
)};
101 IDWriteFactory
*pIDWriteFactory
= 0;
102 ID2D1Factory
*pD2DFactory
= 0;
103 IDWriteRenderingParams
*defaultRenderingParams
= 0;
104 IDWriteRenderingParams
*customClearTypeRenderingParams
= 0;
106 static HMODULE hDLLD2D
= NULL
;
107 static HMODULE hDLLDWrite
= NULL
;
110 static bool triedLoadingD2D
= false;
111 if (!triedLoadingD2D
) {
112 typedef HRESULT (WINAPI
*D2D1CFSig
)(D2D1_FACTORY_TYPE factoryType
, REFIID riid
,
113 CONST D2D1_FACTORY_OPTIONS
*pFactoryOptions
, IUnknown
**factory
);
114 typedef HRESULT (WINAPI
*DWriteCFSig
)(DWRITE_FACTORY_TYPE factoryType
, REFIID iid
,
117 hDLLD2D
= ::LoadLibraryEx(TEXT("D2D1.DLL"), 0, 0x00000800 /*LOAD_LIBRARY_SEARCH_SYSTEM32*/);
119 D2D1CFSig fnD2DCF
= (D2D1CFSig
)::GetProcAddress(hDLLD2D
, "D2D1CreateFactory");
121 // A single threaded factory as Scintilla always draw on the GUI thread
122 fnD2DCF(D2D1_FACTORY_TYPE_SINGLE_THREADED
,
123 __uuidof(ID2D1Factory
),
125 reinterpret_cast<IUnknown
**>(&pD2DFactory
));
128 hDLLDWrite
= ::LoadLibraryEx(TEXT("DWRITE.DLL"), 0, 0x00000800 /*LOAD_LIBRARY_SEARCH_SYSTEM32*/);
130 DWriteCFSig fnDWCF
= (DWriteCFSig
)::GetProcAddress(hDLLDWrite
, "DWriteCreateFactory");
132 fnDWCF(DWRITE_FACTORY_TYPE_SHARED
,
133 __uuidof(IDWriteFactory
),
134 reinterpret_cast<IUnknown
**>(&pIDWriteFactory
));
138 if (pIDWriteFactory
) {
139 HRESULT hr
= pIDWriteFactory
->CreateRenderingParams(&defaultRenderingParams
);
141 unsigned int clearTypeContrast
;
142 ::SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST
, 0, &clearTypeContrast
, 0);
145 if (clearTypeContrast
>= 1000 && clearTypeContrast
<= 2200)
146 gamma
= static_cast<FLOAT
>(clearTypeContrast
) / 1000.0f
;
148 gamma
= defaultRenderingParams
->GetGamma();
150 pIDWriteFactory
->CreateCustomRenderingParams(gamma
, defaultRenderingParams
->GetEnhancedContrast(), defaultRenderingParams
->GetClearTypeLevel(),
151 defaultRenderingParams
->GetPixelGeometry(), defaultRenderingParams
->GetRenderingMode(), &customClearTypeRenderingParams
);
156 triedLoadingD2D
= true;
157 return pIDWriteFactory
&& pD2DFactory
;
161 struct FormatAndMetrics
{
165 IDWriteTextFormat
*pTextFormat
;
171 FLOAT yInternalLeading
;
172 FormatAndMetrics(HFONT hfont_
, int extraFontFlag_
, int characterSet_
) :
173 technology(SCWIN_TECH_GDI
), hfont(hfont_
),
177 extraFontFlag(extraFontFlag_
), characterSet(characterSet_
), yAscent(2), yDescent(1), yInternalLeading(0) {
180 FormatAndMetrics(IDWriteTextFormat
*pTextFormat_
,
185 FLOAT yInternalLeading_
) :
186 technology(SCWIN_TECH_DIRECTWRITE
),
188 pTextFormat(pTextFormat_
),
189 extraFontFlag(extraFontFlag_
),
190 characterSet(characterSet_
),
193 yInternalLeading(yInternalLeading_
) {
196 ~FormatAndMetrics() {
198 ::DeleteObject(hfont
);
201 pTextFormat
->Release();
208 yInternalLeading
= 0;
213 HFONT
FormatAndMetrics::HFont() {
216 if (technology
== SCWIN_TECH_GDI
) {
217 if (0 == ::GetObjectW(hfont
, sizeof(lf
), &lf
)) {
221 HRESULT hr
= pTextFormat
->GetFontFamilyName(lf
.lfFaceName
, LF_FACESIZE
);
222 if (!SUCCEEDED(hr
)) {
225 lf
.lfWeight
= pTextFormat
->GetFontWeight();
226 lf
.lfItalic
= pTextFormat
->GetFontStyle() == DWRITE_FONT_STYLE_ITALIC
;
227 lf
.lfHeight
= -static_cast<int>(pTextFormat
->GetFontSize());
230 if (0 == ::GetObjectW(hfont
, sizeof(lf
), &lf
)) {
234 return ::CreateFontIndirectW(&lf
);
237 #ifndef CLEARTYPE_QUALITY
238 #define CLEARTYPE_QUALITY 5
241 static BYTE
Win32MapFontQuality(int extraFontFlag
) {
242 switch (extraFontFlag
& SC_EFF_QUALITY_MASK
) {
244 case SC_EFF_QUALITY_NON_ANTIALIASED
:
245 return NONANTIALIASED_QUALITY
;
247 case SC_EFF_QUALITY_ANTIALIASED
:
248 return ANTIALIASED_QUALITY
;
250 case SC_EFF_QUALITY_LCD_OPTIMIZED
:
251 return CLEARTYPE_QUALITY
;
254 return SC_EFF_QUALITY_DEFAULT
;
259 static D2D1_TEXT_ANTIALIAS_MODE
DWriteMapFontQuality(int extraFontFlag
) {
260 switch (extraFontFlag
& SC_EFF_QUALITY_MASK
) {
262 case SC_EFF_QUALITY_NON_ANTIALIASED
:
263 return D2D1_TEXT_ANTIALIAS_MODE_ALIASED
;
265 case SC_EFF_QUALITY_ANTIALIASED
:
266 return D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE
;
268 case SC_EFF_QUALITY_LCD_OPTIMIZED
:
269 return D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE
;
272 return D2D1_TEXT_ANTIALIAS_MODE_DEFAULT
;
277 static void SetLogFont(LOGFONTA
&lf
, const char *faceName
, int characterSet
, float size
, int weight
, bool italic
, int extraFontFlag
) {
279 // The negative is to allow for leading
280 lf
.lfHeight
= -(abs(static_cast<int>(size
+ 0.5)));
281 lf
.lfWeight
= weight
;
282 lf
.lfItalic
= static_cast<BYTE
>(italic
? 1 : 0);
283 lf
.lfCharSet
= static_cast<BYTE
>(characterSet
);
284 lf
.lfQuality
= Win32MapFontQuality(extraFontFlag
);
285 StringCopy(lf
.lfFaceName
, faceName
);
289 * Create a hash from the parameters for a font to allow easy checking for identity.
290 * If one font is the same as another, its hash will be the same, but if the hash is the
291 * same then they may still be different.
293 static int HashFont(const FontParameters
&fp
) {
295 static_cast<int>(fp
.size
) ^
296 (fp
.characterSet
<< 10) ^
297 ((fp
.extraFontFlag
& SC_EFF_QUALITY_MASK
) << 9) ^
298 ((fp
.weight
/100) << 12) ^
299 (fp
.italic
? 0x20000000 : 0) ^
300 (fp
.technology
<< 15) ^
304 class FontCached
: Font
{
311 explicit FontCached(const FontParameters
&fp
);
313 bool SameAs(const FontParameters
&fp
);
314 virtual void Release();
316 static FontCached
*first
;
318 static FontID
FindOrCreate(const FontParameters
&fp
);
319 static void ReleaseId(FontID fid_
);
322 FontCached
*FontCached::first
= 0;
324 FontCached::FontCached(const FontParameters
&fp
) :
325 next(0), usage(0), size(1.0), hash(0) {
326 SetLogFont(lf
, fp
.faceName
, fp
.characterSet
, fp
.size
, fp
.weight
, fp
.italic
, fp
.extraFontFlag
);
327 technology
= fp
.technology
;
330 if (technology
== SCWIN_TECH_GDI
) {
331 HFONT hfont
= ::CreateFontIndirectA(&lf
);
332 fid
= reinterpret_cast<void *>(new FormatAndMetrics(hfont
, fp
.extraFontFlag
, fp
.characterSet
));
335 IDWriteTextFormat
*pTextFormat
;
336 const int faceSize
= 200;
337 WCHAR wszFace
[faceSize
];
338 UTF16FromUTF8(fp
.faceName
, static_cast<unsigned int>(strlen(fp
.faceName
))+1, wszFace
, faceSize
);
339 FLOAT fHeight
= fp
.size
;
340 DWRITE_FONT_STYLE style
= fp
.italic
? DWRITE_FONT_STYLE_ITALIC
: DWRITE_FONT_STYLE_NORMAL
;
341 HRESULT hr
= pIDWriteFactory
->CreateTextFormat(wszFace
, NULL
,
342 static_cast<DWRITE_FONT_WEIGHT
>(fp
.weight
),
344 DWRITE_FONT_STRETCH_NORMAL
, fHeight
, L
"en-us", &pTextFormat
);
346 pTextFormat
->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP
);
348 const int maxLines
= 2;
349 DWRITE_LINE_METRICS lineMetrics
[maxLines
];
350 UINT32 lineCount
= 0;
351 FLOAT yAscent
= 1.0f
;
352 FLOAT yDescent
= 1.0f
;
353 FLOAT yInternalLeading
= 0.0f
;
354 IDWriteTextLayout
*pTextLayout
= 0;
355 hr
= pIDWriteFactory
->CreateTextLayout(L
"X", 1, pTextFormat
,
356 100.0f
, 100.0f
, &pTextLayout
);
358 hr
= pTextLayout
->GetLineMetrics(lineMetrics
, maxLines
, &lineCount
);
360 yAscent
= lineMetrics
[0].baseline
;
361 yDescent
= lineMetrics
[0].height
- lineMetrics
[0].baseline
;
364 hr
= pTextLayout
->GetFontSize(0, &emHeight
);
366 yInternalLeading
= lineMetrics
[0].height
- emHeight
;
369 pTextLayout
->Release();
370 pTextFormat
->SetLineSpacing(DWRITE_LINE_SPACING_METHOD_UNIFORM
, lineMetrics
[0].height
, lineMetrics
[0].baseline
);
372 fid
= reinterpret_cast<void *>(new FormatAndMetrics(pTextFormat
, fp
.extraFontFlag
, fp
.characterSet
, yAscent
, yDescent
, yInternalLeading
));
379 bool FontCached::SameAs(const FontParameters
&fp
) {
382 (lf
.lfWeight
== fp
.weight
) &&
383 (lf
.lfItalic
== static_cast<BYTE
>(fp
.italic
? 1 : 0)) &&
384 (lf
.lfCharSet
== fp
.characterSet
) &&
385 (lf
.lfQuality
== Win32MapFontQuality(fp
.extraFontFlag
)) &&
386 (technology
== fp
.technology
) &&
387 0 == strcmp(lf
.lfFaceName
,fp
.faceName
);
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
= 10000;
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
= 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
{
514 // If 9x OS and current code page is same as ANSI code page.
517 void BrushColor(ColourDesired back
);
518 void SetFont(Font
&font_
);
520 // Private so SurfaceGDI objects can not be copied
521 SurfaceGDI(const SurfaceGDI
&);
522 SurfaceGDI
&operator=(const SurfaceGDI
&);
525 virtual ~SurfaceGDI();
527 void Init(WindowID wid
);
528 void Init(SurfaceID sid
, WindowID wid
);
529 void InitPixMap(int width
, int height
, Surface
*surface_
, WindowID wid
);
533 void PenColour(ColourDesired fore
);
535 int DeviceHeightFont(int points
);
536 void MoveTo(int x_
, int y_
);
537 void LineTo(int x_
, int y_
);
538 void Polygon(Point
*pts
, int npts
, ColourDesired fore
, ColourDesired back
);
539 void RectangleDraw(PRectangle rc
, ColourDesired fore
, ColourDesired back
);
540 void FillRectangle(PRectangle rc
, ColourDesired back
);
541 void FillRectangle(PRectangle rc
, Surface
&surfacePattern
);
542 void RoundedRectangle(PRectangle rc
, ColourDesired fore
, ColourDesired back
);
543 void AlphaRectangle(PRectangle rc
, int cornerSize
, ColourDesired fill
, int alphaFill
,
544 ColourDesired outline
, int alphaOutline
, int flags
);
545 void DrawRGBAImage(PRectangle rc
, int width
, int height
, const unsigned char *pixelsImage
);
546 void Ellipse(PRectangle rc
, ColourDesired fore
, ColourDesired back
);
547 void Copy(PRectangle rc
, Point from
, Surface
&surfaceSource
);
549 void DrawTextCommon(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, UINT fuOptions
);
550 void DrawTextNoClip(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, ColourDesired fore
, ColourDesired back
);
551 void DrawTextClipped(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, ColourDesired fore
, ColourDesired back
);
552 void DrawTextTransparent(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, ColourDesired fore
);
553 void MeasureWidths(Font
&font_
, const char *s
, int len
, XYPOSITION
*positions
);
554 XYPOSITION
WidthText(Font
&font_
, const char *s
, int len
);
555 XYPOSITION
WidthChar(Font
&font_
, char ch
);
556 XYPOSITION
Ascent(Font
&font_
);
557 XYPOSITION
Descent(Font
&font_
);
558 XYPOSITION
InternalLeading(Font
&font_
);
559 XYPOSITION
ExternalLeading(Font
&font_
);
560 XYPOSITION
Height(Font
&font_
);
561 XYPOSITION
AverageCharWidth(Font
&font_
);
563 void SetClip(PRectangle rc
);
564 void FlushCachedState();
566 void SetUnicodeMode(bool unicodeMode_
);
567 void SetDBCSMode(int codePage_
);
570 SurfaceGDI::SurfaceGDI() :
572 hdc(0), hdcOwned(false),
574 brush(0), brushOld(0),
576 bitmap(0), bitmapOld(0) {
577 // Windows 9x has only a 16 bit coordinate system so break after 30000 pixels
578 maxWidthMeasure
= IsNT() ? INT_MAX
: 30000;
579 // There appears to be a 16 bit string length limit in GDI on NT and a limit of
580 // 8192 characters on Windows 95.
581 maxLenText
= IsNT() ? 65535 : 8192;
584 win9xACPSame
= false;
587 SurfaceGDI::~SurfaceGDI() {
591 void SurfaceGDI::Release() {
593 ::SelectObject(reinterpret_cast<HDC
>(hdc
), penOld
);
599 ::SelectObject(reinterpret_cast<HDC
>(hdc
), brushOld
);
600 ::DeleteObject(brush
);
605 // Fonts are not deleted as they are owned by a Font object
606 ::SelectObject(reinterpret_cast<HDC
>(hdc
), fontOld
);
611 ::SelectObject(reinterpret_cast<HDC
>(hdc
), bitmapOld
);
612 ::DeleteObject(bitmap
);
617 ::DeleteDC(reinterpret_cast<HDC
>(hdc
));
623 bool SurfaceGDI::Initialised() {
627 void SurfaceGDI::Init(WindowID
) {
629 hdc
= ::CreateCompatibleDC(NULL
);
631 ::SetTextAlign(reinterpret_cast<HDC
>(hdc
), TA_BASELINE
);
634 void SurfaceGDI::Init(SurfaceID sid
, WindowID
) {
636 hdc
= reinterpret_cast<HDC
>(sid
);
637 ::SetTextAlign(reinterpret_cast<HDC
>(hdc
), TA_BASELINE
);
640 void SurfaceGDI::InitPixMap(int width
, int height
, Surface
*surface_
, WindowID
) {
642 hdc
= ::CreateCompatibleDC(static_cast<SurfaceGDI
*>(surface_
)->hdc
);
644 bitmap
= ::CreateCompatibleBitmap(static_cast<SurfaceGDI
*>(surface_
)->hdc
, width
, height
);
645 bitmapOld
= static_cast<HBITMAP
>(::SelectObject(hdc
, bitmap
));
646 ::SetTextAlign(reinterpret_cast<HDC
>(hdc
), TA_BASELINE
);
649 void SurfaceGDI::PenColour(ColourDesired fore
) {
651 ::SelectObject(hdc
, penOld
);
656 pen
= ::CreatePen(0,1,fore
.AsLong());
657 penOld
= static_cast<HPEN
>(::SelectObject(reinterpret_cast<HDC
>(hdc
), pen
));
660 void SurfaceGDI::BrushColor(ColourDesired back
) {
662 ::SelectObject(hdc
, brushOld
);
663 ::DeleteObject(brush
);
667 // Only ever want pure, non-dithered brushes
668 ColourDesired colourNearest
= ::GetNearestColor(hdc
, back
.AsLong());
669 brush
= ::CreateSolidBrush(colourNearest
.AsLong());
670 brushOld
= static_cast<HBRUSH
>(::SelectObject(hdc
, brush
));
673 void SurfaceGDI::SetFont(Font
&font_
) {
674 if (font_
.GetID() != font
) {
675 FormatAndMetrics
*pfm
= reinterpret_cast<FormatAndMetrics
*>(font_
.GetID());
676 PLATFORM_ASSERT(pfm
->technology
== SCWIN_TECH_GDI
);
678 ::SelectObject(hdc
, pfm
->hfont
);
680 fontOld
= static_cast<HFONT
>(::SelectObject(hdc
, pfm
->hfont
));
682 font
= reinterpret_cast<HFONT
>(pfm
->hfont
);
686 int SurfaceGDI::LogPixelsY() {
687 return ::GetDeviceCaps(hdc
, LOGPIXELSY
);
690 int SurfaceGDI::DeviceHeightFont(int points
) {
691 return ::MulDiv(points
, LogPixelsY(), 72);
694 void SurfaceGDI::MoveTo(int x_
, int y_
) {
695 ::MoveToEx(hdc
, x_
, y_
, 0);
698 void SurfaceGDI::LineTo(int x_
, int y_
) {
699 ::LineTo(hdc
, x_
, y_
);
702 void SurfaceGDI::Polygon(Point
*pts
, int npts
, ColourDesired fore
, ColourDesired back
) {
705 std::vector
<POINT
> outline
;
706 for (int i
=0; i
<npts
; i
++) {
707 POINT pt
= {static_cast<LONG
>(pts
[i
].x
), static_cast<LONG
>(pts
[i
].y
)};
708 outline
.push_back(pt
);
710 ::Polygon(hdc
, &outline
[0], npts
);
713 void SurfaceGDI::RectangleDraw(PRectangle rc
, ColourDesired fore
, ColourDesired back
) {
716 const RECT rcw
= RectFromPRectangle(rc
);
717 ::Rectangle(hdc
, rcw
.left
, rcw
.top
, rcw
.right
, rcw
.bottom
);
720 void SurfaceGDI::FillRectangle(PRectangle rc
, ColourDesired back
) {
721 // Using ExtTextOut rather than a FillRect ensures that no dithering occurs.
722 // There is no need to allocate a brush either.
723 RECT rcw
= RectFromPRectangle(rc
);
724 ::SetBkColor(hdc
, back
.AsLong());
725 ::ExtTextOut(hdc
, rcw
.left
, rcw
.top
, ETO_OPAQUE
, &rcw
, TEXT(""), 0, NULL
);
728 void SurfaceGDI::FillRectangle(PRectangle rc
, Surface
&surfacePattern
) {
730 if (static_cast<SurfaceGDI
&>(surfacePattern
).bitmap
)
731 br
= ::CreatePatternBrush(static_cast<SurfaceGDI
&>(surfacePattern
).bitmap
);
732 else // Something is wrong so display in red
733 br
= ::CreateSolidBrush(RGB(0xff, 0, 0));
734 RECT rcw
= RectFromPRectangle(rc
);
735 ::FillRect(hdc
, &rcw
, br
);
739 void SurfaceGDI::RoundedRectangle(PRectangle rc
, ColourDesired fore
, ColourDesired back
) {
742 const RECT rcw
= RectFromPRectangle(rc
);
744 rcw
.left
+ 1, rcw
.top
,
745 rcw
.right
- 1, rcw
.bottom
,
749 // Plot a point into a DWORD buffer symetrically to all 4 qudrants
750 static void AllFour(DWORD
*pixels
, int width
, int height
, int x
, int y
, DWORD val
) {
751 pixels
[y
*width
+x
] = val
;
752 pixels
[y
*width
+width
-1-x
] = val
;
753 pixels
[(height
-1-y
)*width
+x
] = val
;
754 pixels
[(height
-1-y
)*width
+width
-1-x
] = val
;
758 #define AC_SRC_OVER 0x00
761 #define AC_SRC_ALPHA 0x01
764 static DWORD
dwordFromBGRA(byte b
, byte g
, byte r
, byte a
) {
769 converter
.pixVal
[0] = b
;
770 converter
.pixVal
[1] = g
;
771 converter
.pixVal
[2] = r
;
772 converter
.pixVal
[3] = a
;
773 return converter
.val
;
776 void SurfaceGDI::AlphaRectangle(PRectangle rc
, int cornerSize
, ColourDesired fill
, int alphaFill
,
777 ColourDesired outline
, int alphaOutline
, int /* flags*/ ) {
778 const RECT rcw
= RectFromPRectangle(rc
);
779 if (AlphaBlendFn
&& rc
.Width() > 0) {
780 HDC hMemDC
= ::CreateCompatibleDC(reinterpret_cast<HDC
>(hdc
));
781 int width
= static_cast<int>(rc
.Width());
782 int height
= static_cast<int>(rc
.Height());
783 // Ensure not distorted too much by corners when small
784 cornerSize
= Platform::Minimum(cornerSize
, (Platform::Minimum(width
, height
) / 2) - 2);
785 BITMAPINFO bpih
= {{sizeof(BITMAPINFOHEADER
), width
, height
, 1, 32, BI_RGB
, 0, 0, 0, 0, 0}};
787 HBITMAP hbmMem
= CreateDIBSection(reinterpret_cast<HDC
>(hMemDC
), &bpih
,
788 DIB_RGB_COLORS
, &image
, NULL
, 0);
791 HBITMAP hbmOld
= SelectBitmap(hMemDC
, hbmMem
);
793 DWORD valEmpty
= dwordFromBGRA(0,0,0,0);
794 DWORD valFill
= dwordFromBGRA(
795 static_cast<byte
>(GetBValue(fill
.AsLong()) * alphaFill
/ 255),
796 static_cast<byte
>(GetGValue(fill
.AsLong()) * alphaFill
/ 255),
797 static_cast<byte
>(GetRValue(fill
.AsLong()) * alphaFill
/ 255),
798 static_cast<byte
>(alphaFill
));
799 DWORD valOutline
= dwordFromBGRA(
800 static_cast<byte
>(GetBValue(outline
.AsLong()) * alphaOutline
/ 255),
801 static_cast<byte
>(GetGValue(outline
.AsLong()) * alphaOutline
/ 255),
802 static_cast<byte
>(GetRValue(outline
.AsLong()) * alphaOutline
/ 255),
803 static_cast<byte
>(alphaOutline
));
804 DWORD
*pixels
= reinterpret_cast<DWORD
*>(image
);
805 for (int y
=0; y
<height
; y
++) {
806 for (int x
=0; x
<width
; x
++) {
807 if ((x
==0) || (x
==width
-1) || (y
== 0) || (y
== height
-1)) {
808 pixels
[y
*width
+x
] = valOutline
;
810 pixels
[y
*width
+x
] = valFill
;
814 for (int c
=0; c
<cornerSize
; c
++) {
815 for (int x
=0; x
<c
+1; x
++) {
816 AllFour(pixels
, width
, height
, x
, c
-x
, valEmpty
);
819 for (int x
=1; x
<cornerSize
; x
++) {
820 AllFour(pixels
, width
, height
, x
, cornerSize
-x
, valOutline
);
823 BLENDFUNCTION merge
= { AC_SRC_OVER
, 0, 255, AC_SRC_ALPHA
};
825 AlphaBlendFn(reinterpret_cast<HDC
>(hdc
), rcw
.left
, rcw
.top
, width
, height
, hMemDC
, 0, 0, width
, height
, merge
);
827 SelectBitmap(hMemDC
, hbmOld
);
828 ::DeleteObject(hbmMem
);
833 FrameRect(hdc
, &rcw
, brush
);
837 void SurfaceGDI::DrawRGBAImage(PRectangle rc
, int width
, int height
, const unsigned char *pixelsImage
) {
838 if (AlphaBlendFn
&& rc
.Width() > 0) {
839 HDC hMemDC
= ::CreateCompatibleDC(reinterpret_cast<HDC
>(hdc
));
840 if (rc
.Width() > width
)
841 rc
.left
+= static_cast<int>((rc
.Width() - width
) / 2);
842 rc
.right
= rc
.left
+ width
;
843 if (rc
.Height() > height
)
844 rc
.top
+= static_cast<int>((rc
.Height() - height
) / 2);
845 rc
.bottom
= rc
.top
+ height
;
847 BITMAPINFO bpih
= {{sizeof(BITMAPINFOHEADER
), width
, height
, 1, 32, BI_RGB
, 0, 0, 0, 0, 0}};
848 unsigned char *image
= 0;
849 HBITMAP hbmMem
= CreateDIBSection(reinterpret_cast<HDC
>(hMemDC
), &bpih
,
850 DIB_RGB_COLORS
, reinterpret_cast<void **>(&image
), NULL
, 0);
852 HBITMAP hbmOld
= SelectBitmap(hMemDC
, hbmMem
);
854 for (int y
=height
-1; y
>=0; y
--) {
855 for (int x
=0; x
<width
; x
++) {
856 unsigned char *pixel
= image
+ (y
*width
+x
) * 4;
857 unsigned char alpha
= pixelsImage
[3];
858 // Input is RGBA, output is BGRA with premultiplied alpha
859 pixel
[2] = static_cast<unsigned char>((*pixelsImage
++) * alpha
/ 255);
860 pixel
[1] = static_cast<unsigned char>((*pixelsImage
++) * alpha
/ 255);
861 pixel
[0] = static_cast<unsigned char>((*pixelsImage
++) * alpha
/ 255);
862 pixel
[3] = static_cast<unsigned char>(*pixelsImage
++);
866 BLENDFUNCTION merge
= { AC_SRC_OVER
, 0, 255, AC_SRC_ALPHA
};
868 AlphaBlendFn(reinterpret_cast<HDC
>(hdc
), static_cast<int>(rc
.left
), static_cast<int>(rc
.top
),
869 static_cast<int>(rc
.Width()), static_cast<int>(rc
.Height()), hMemDC
, 0, 0, width
, height
, merge
);
871 SelectBitmap(hMemDC
, hbmOld
);
872 ::DeleteObject(hbmMem
);
879 void SurfaceGDI::Ellipse(PRectangle rc
, ColourDesired fore
, ColourDesired back
) {
882 const RECT rcw
= RectFromPRectangle(rc
);
883 ::Ellipse(hdc
, rcw
.left
, rcw
.top
, rcw
.right
, rcw
.bottom
);
886 void SurfaceGDI::Copy(PRectangle rc
, Point from
, Surface
&surfaceSource
) {
888 static_cast<int>(rc
.left
), static_cast<int>(rc
.top
),
889 static_cast<int>(rc
.Width()), static_cast<int>(rc
.Height()),
890 static_cast<SurfaceGDI
&>(surfaceSource
).hdc
,
891 static_cast<int>(from
.x
), static_cast<int>(from
.y
), SRCCOPY
);
894 typedef VarBuffer
<int, stackBufferLength
> TextPositionsI
;
896 void SurfaceGDI::DrawTextCommon(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, UINT fuOptions
) {
898 RECT rcw
= RectFromPRectangle(rc
);
901 int x
= static_cast<int>(rc
.left
);
902 const int yBaseInt
= static_cast<int>(ybase
);
904 // Text drawing may fail if the text is too big.
905 // If it does fail, slice up into segments and draw each segment.
906 const int maxSegmentLength
= 0x200;
908 if ((!unicodeMode
) && (IsNT() || (codePage
==0) || win9xACPSame
)) {
910 int lenDraw
= Platform::Minimum(len
, maxLenText
);
911 if (!::ExtTextOutA(hdc
, x
, yBaseInt
, fuOptions
, &rcw
, s
, lenDraw
, NULL
)) {
912 while (lenDraw
> pos
) {
913 int seglen
= Platform::Minimum(maxSegmentLength
, lenDraw
- pos
);
914 if (!::ExtTextOutA(hdc
, x
, yBaseInt
, fuOptions
, &rcw
, s
+ pos
, seglen
, NULL
)) {
915 PLATFORM_ASSERT(false);
918 ::GetTextExtentPoint32A(hdc
, s
+pos
, seglen
, &sz
);
925 const TextWide
tbuf(s
, len
, unicodeMode
, codePage
);
926 if (!::ExtTextOutW(hdc
, x
, yBaseInt
, fuOptions
, &rcw
, tbuf
.buffer
, tbuf
.tlen
, NULL
)) {
927 while (tbuf
.tlen
> pos
) {
928 int seglen
= Platform::Minimum(maxSegmentLength
, tbuf
.tlen
- pos
);
929 if (!::ExtTextOutW(hdc
, x
, yBaseInt
, fuOptions
, &rcw
, tbuf
.buffer
+ pos
, seglen
, NULL
)) {
930 PLATFORM_ASSERT(false);
933 ::GetTextExtentPoint32W(hdc
, tbuf
.buffer
+pos
, seglen
, &sz
);
941 void SurfaceGDI::DrawTextNoClip(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
,
942 ColourDesired fore
, ColourDesired back
) {
943 ::SetTextColor(hdc
, fore
.AsLong());
944 ::SetBkColor(hdc
, back
.AsLong());
945 DrawTextCommon(rc
, font_
, ybase
, s
, len
, ETO_OPAQUE
);
948 void SurfaceGDI::DrawTextClipped(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
,
949 ColourDesired fore
, ColourDesired back
) {
950 ::SetTextColor(hdc
, fore
.AsLong());
951 ::SetBkColor(hdc
, back
.AsLong());
952 DrawTextCommon(rc
, font_
, ybase
, s
, len
, ETO_OPAQUE
| ETO_CLIPPED
);
955 void SurfaceGDI::DrawTextTransparent(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
,
956 ColourDesired fore
) {
957 // Avoid drawing spaces in transparent mode
958 for (int i
=0; i
<len
; i
++) {
960 ::SetTextColor(hdc
, fore
.AsLong());
961 ::SetBkMode(hdc
, TRANSPARENT
);
962 DrawTextCommon(rc
, font_
, ybase
, s
, len
, 0);
963 ::SetBkMode(hdc
, OPAQUE
);
969 XYPOSITION
SurfaceGDI::WidthText(Font
&font_
, const char *s
, int len
) {
972 if ((!unicodeMode
) && (IsNT() || (codePage
==0) || win9xACPSame
)) {
973 ::GetTextExtentPoint32A(hdc
, s
, Platform::Minimum(len
, maxLenText
), &sz
);
975 const TextWide
tbuf(s
, len
, unicodeMode
, codePage
);
976 ::GetTextExtentPoint32W(hdc
, tbuf
.buffer
, tbuf
.tlen
, &sz
);
978 return static_cast<XYPOSITION
>(sz
.cx
);
981 void SurfaceGDI::MeasureWidths(Font
&font_
, const char *s
, int len
, XYPOSITION
*positions
) {
986 const TextWide
tbuf(s
, len
, unicodeMode
, codePage
);
987 TextPositionsI
poses(tbuf
.tlen
);
989 if (!::GetTextExtentExPointW(hdc
, tbuf
.buffer
, tbuf
.tlen
, maxWidthMeasure
, &fit
, poses
.buffer
, &sz
)) {
990 // Likely to have failed because on Windows 9x where function not available
991 // So measure the character widths by measuring each initial substring
992 // Turns a linear operation into a qudratic but seems fast enough on test files
993 for (int widthSS
=0; widthSS
< tbuf
.tlen
; widthSS
++) {
994 ::GetTextExtentPoint32W(hdc
, tbuf
.buffer
, widthSS
+1, &sz
);
995 poses
.buffer
[widthSS
] = sz
.cx
;
998 // Map the widths given for UTF-16 characters back onto the UTF-8 input string
1000 const unsigned char *us
= reinterpret_cast<const unsigned char *>(s
);
1003 unsigned char uch
= us
[i
];
1004 unsigned int lenChar
= 1;
1005 if (uch
>= (0x80 + 0x40 + 0x20 + 0x10)) {
1008 } else if (uch
>= (0x80 + 0x40 + 0x20)) {
1010 } else if (uch
>= (0x80)) {
1013 for (unsigned int bytePos
=0; (bytePos
<lenChar
) && (i
<len
); bytePos
++) {
1014 positions
[i
++] = static_cast<XYPOSITION
>(poses
.buffer
[ui
]);
1018 XYPOSITION lastPos
= 0.0f
;
1020 lastPos
= positions
[i
-1];
1022 positions
[i
++] = lastPos
;
1024 } else if (IsNT() || (codePage
==0) || win9xACPSame
) {
1025 // Zero positions to avoid random behaviour on failure.
1026 std::fill(positions
, positions
+ len
, 0.0f
);
1027 // len may be larger than platform supports so loop over segments small enough for platform
1028 int startOffset
= 0;
1030 int lenBlock
= Platform::Minimum(len
, maxLenText
);
1031 TextPositionsI
poses(len
);
1032 if (!::GetTextExtentExPointA(hdc
, s
, lenBlock
, maxWidthMeasure
, &fit
, poses
.buffer
, &sz
)) {
1033 // Eeek - a NULL DC or other foolishness could cause this.
1035 } else if (fit
< lenBlock
) {
1036 // For some reason, such as an incomplete DBCS character
1037 // Not all the positions are filled in so make them equal to end.
1039 poses
.buffer
[fit
++] = 0;
1040 for (int i
= fit
; i
<lenBlock
; i
++)
1041 poses
.buffer
[i
] = poses
.buffer
[fit
-1];
1043 for (int i
=0; i
<lenBlock
; i
++)
1044 positions
[i
] = static_cast<XYPOSITION
>(poses
.buffer
[i
] + startOffset
);
1045 startOffset
= poses
.buffer
[lenBlock
-1];
1047 positions
+= lenBlock
;
1051 // Support Asian string display in 9x English
1052 const TextWide
tbuf(s
, len
, unicodeMode
, codePage
);
1053 TextPositionsI
poses(tbuf
.tlen
);
1054 for (int widthSS
=0; widthSS
<tbuf
.tlen
; widthSS
++) {
1055 ::GetTextExtentPoint32W(hdc
, tbuf
.buffer
, widthSS
+1, &sz
);
1056 poses
.buffer
[widthSS
] = sz
.cx
;
1060 for (int i
=0; i
<len
;) {
1061 if (Platform::IsDBCSLeadByte(codePage
, s
[i
])) {
1062 positions
[i
] = static_cast<XYPOSITION
>(poses
.buffer
[ui
]);
1063 positions
[i
+ 1] = static_cast<XYPOSITION
>(poses
.buffer
[ui
]);
1066 positions
[i
] = static_cast<XYPOSITION
>(poses
.buffer
[ui
]);
1075 XYPOSITION
SurfaceGDI::WidthChar(Font
&font_
, char ch
) {
1078 ::GetTextExtentPoint32A(hdc
, &ch
, 1, &sz
);
1079 return static_cast<XYPOSITION
>(sz
.cx
);
1082 XYPOSITION
SurfaceGDI::Ascent(Font
&font_
) {
1085 ::GetTextMetrics(hdc
, &tm
);
1086 return static_cast<XYPOSITION
>(tm
.tmAscent
);
1089 XYPOSITION
SurfaceGDI::Descent(Font
&font_
) {
1092 ::GetTextMetrics(hdc
, &tm
);
1093 return static_cast<XYPOSITION
>(tm
.tmDescent
);
1096 XYPOSITION
SurfaceGDI::InternalLeading(Font
&font_
) {
1099 ::GetTextMetrics(hdc
, &tm
);
1100 return static_cast<XYPOSITION
>(tm
.tmInternalLeading
);
1103 XYPOSITION
SurfaceGDI::ExternalLeading(Font
&font_
) {
1106 ::GetTextMetrics(hdc
, &tm
);
1107 return static_cast<XYPOSITION
>(tm
.tmExternalLeading
);
1110 XYPOSITION
SurfaceGDI::Height(Font
&font_
) {
1113 ::GetTextMetrics(hdc
, &tm
);
1114 return static_cast<XYPOSITION
>(tm
.tmHeight
);
1117 XYPOSITION
SurfaceGDI::AverageCharWidth(Font
&font_
) {
1120 ::GetTextMetrics(hdc
, &tm
);
1121 return static_cast<XYPOSITION
>(tm
.tmAveCharWidth
);
1124 void SurfaceGDI::SetClip(PRectangle rc
) {
1125 ::IntersectClipRect(hdc
, static_cast<int>(rc
.left
), static_cast<int>(rc
.top
),
1126 static_cast<int>(rc
.right
), static_cast<int>(rc
.bottom
));
1129 void SurfaceGDI::FlushCachedState() {
1135 void SurfaceGDI::SetUnicodeMode(bool unicodeMode_
) {
1136 unicodeMode
=unicodeMode_
;
1139 void SurfaceGDI::SetDBCSMode(int codePage_
) {
1140 // No action on window as automatically handled by system.
1141 codePage
= codePage_
;
1142 win9xACPSame
= !IsNT() && ((unsigned int)codePage
== ::GetACP());
1145 #if defined(USE_D2D)
1147 class SurfaceD2D
: public Surface
{
1154 ID2D1RenderTarget
*pRenderTarget
;
1155 bool ownRenderTarget
;
1158 IDWriteTextFormat
*pTextFormat
;
1161 FLOAT yInternalLeading
;
1163 ID2D1SolidColorBrush
*pBrush
;
1169 void SetFont(Font
&font_
);
1171 // Private so SurfaceD2D objects can not be copied
1172 SurfaceD2D(const SurfaceD2D
&);
1173 SurfaceD2D
&operator=(const SurfaceD2D
&);
1176 virtual ~SurfaceD2D();
1179 void Init(WindowID wid
);
1180 void Init(SurfaceID sid
, WindowID wid
);
1181 void InitPixMap(int width
, int height
, Surface
*surface_
, WindowID wid
);
1186 HRESULT
FlushDrawing();
1188 void PenColour(ColourDesired fore
);
1189 void D2DPenColour(ColourDesired fore
, int alpha
=255);
1191 int DeviceHeightFont(int points
);
1192 void MoveTo(int x_
, int y_
);
1193 void LineTo(int x_
, int y_
);
1194 void Polygon(Point
*pts
, int npts
, ColourDesired fore
, ColourDesired back
);
1195 void RectangleDraw(PRectangle rc
, ColourDesired fore
, ColourDesired back
);
1196 void FillRectangle(PRectangle rc
, ColourDesired back
);
1197 void FillRectangle(PRectangle rc
, Surface
&surfacePattern
);
1198 void RoundedRectangle(PRectangle rc
, ColourDesired fore
, ColourDesired back
);
1199 void AlphaRectangle(PRectangle rc
, int cornerSize
, ColourDesired fill
, int alphaFill
,
1200 ColourDesired outline
, int alphaOutline
, int flags
);
1201 void DrawRGBAImage(PRectangle rc
, int width
, int height
, const unsigned char *pixelsImage
);
1202 void Ellipse(PRectangle rc
, ColourDesired fore
, ColourDesired back
);
1203 void Copy(PRectangle rc
, Point from
, Surface
&surfaceSource
);
1205 void DrawTextCommon(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, UINT fuOptions
);
1206 void DrawTextNoClip(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, ColourDesired fore
, ColourDesired back
);
1207 void DrawTextClipped(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, ColourDesired fore
, ColourDesired back
);
1208 void DrawTextTransparent(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, ColourDesired fore
);
1209 void MeasureWidths(Font
&font_
, const char *s
, int len
, XYPOSITION
*positions
);
1210 XYPOSITION
WidthText(Font
&font_
, const char *s
, int len
);
1211 XYPOSITION
WidthChar(Font
&font_
, char ch
);
1212 XYPOSITION
Ascent(Font
&font_
);
1213 XYPOSITION
Descent(Font
&font_
);
1214 XYPOSITION
InternalLeading(Font
&font_
);
1215 XYPOSITION
ExternalLeading(Font
&font_
);
1216 XYPOSITION
Height(Font
&font_
);
1217 XYPOSITION
AverageCharWidth(Font
&font_
);
1219 void SetClip(PRectangle rc
);
1220 void FlushCachedState();
1222 void SetUnicodeMode(bool unicodeMode_
);
1223 void SetDBCSMode(int codePage_
);
1226 SurfaceD2D::SurfaceD2D() :
1233 pRenderTarget
= NULL
;
1234 ownRenderTarget
= false;
1237 // From selected font
1241 yInternalLeading
= 0;
1250 SurfaceD2D::~SurfaceD2D() {
1254 void SurfaceD2D::Release() {
1259 if (pRenderTarget
) {
1260 while (clipsActive
) {
1261 pRenderTarget
->PopAxisAlignedClip();
1264 if (ownRenderTarget
) {
1265 pRenderTarget
->Release();
1271 void SurfaceD2D::SetScale() {
1272 HDC hdcMeasure
= ::CreateCompatibleDC(NULL
);
1273 logPixelsY
= ::GetDeviceCaps(hdcMeasure
, LOGPIXELSY
);
1274 dpiScaleX
= ::GetDeviceCaps(hdcMeasure
, LOGPIXELSX
) / 96.0f
;
1275 dpiScaleY
= logPixelsY
/ 96.0f
;
1276 ::DeleteDC(hdcMeasure
);
1279 bool SurfaceD2D::Initialised() {
1280 return pRenderTarget
!= 0;
1283 HRESULT
SurfaceD2D::FlushDrawing() {
1284 return pRenderTarget
->Flush();
1287 void SurfaceD2D::Init(WindowID
/* wid */) {
1292 void SurfaceD2D::Init(SurfaceID sid
, WindowID
) {
1295 pRenderTarget
= reinterpret_cast<ID2D1RenderTarget
*>(sid
);
1298 void SurfaceD2D::InitPixMap(int width
, int height
, Surface
*surface_
, WindowID
) {
1301 SurfaceD2D
*psurfOther
= static_cast<SurfaceD2D
*>(surface_
);
1302 ID2D1BitmapRenderTarget
*pCompatibleRenderTarget
= NULL
;
1303 D2D1_SIZE_F desiredSize
= D2D1::SizeF(static_cast<float>(width
), static_cast<float>(height
));
1304 D2D1_PIXEL_FORMAT desiredFormat
;
1306 desiredFormat
.format
= DXGI_FORMAT_UNKNOWN
;
1308 desiredFormat
= psurfOther
->pRenderTarget
->GetPixelFormat();
1310 desiredFormat
.alphaMode
= D2D1_ALPHA_MODE_IGNORE
;
1311 HRESULT hr
= psurfOther
->pRenderTarget
->CreateCompatibleRenderTarget(
1312 &desiredSize
, NULL
, &desiredFormat
, D2D1_COMPATIBLE_RENDER_TARGET_OPTIONS_NONE
, &pCompatibleRenderTarget
);
1313 if (SUCCEEDED(hr
)) {
1314 pRenderTarget
= pCompatibleRenderTarget
;
1315 pRenderTarget
->BeginDraw();
1316 ownRenderTarget
= true;
1320 void SurfaceD2D::PenColour(ColourDesired fore
) {
1324 void SurfaceD2D::D2DPenColour(ColourDesired fore
, int alpha
) {
1325 if (pRenderTarget
) {
1327 col
.r
= (fore
.AsLong() & 0xff) / 255.0f
;
1328 col
.g
= ((fore
.AsLong() & 0xff00) >> 8) / 255.0f
;
1329 col
.b
= (fore
.AsLong() >> 16) / 255.0f
;
1330 col
.a
= alpha
/ 255.0f
;
1332 pBrush
->SetColor(col
);
1334 HRESULT hr
= pRenderTarget
->CreateSolidColorBrush(col
, &pBrush
);
1335 if (!SUCCEEDED(hr
) && pBrush
) {
1343 void SurfaceD2D::SetFont(Font
&font_
) {
1344 FormatAndMetrics
*pfm
= reinterpret_cast<FormatAndMetrics
*>(font_
.GetID());
1345 PLATFORM_ASSERT(pfm
->technology
== SCWIN_TECH_DIRECTWRITE
);
1346 pTextFormat
= pfm
->pTextFormat
;
1347 yAscent
= pfm
->yAscent
;
1348 yDescent
= pfm
->yDescent
;
1349 yInternalLeading
= pfm
->yInternalLeading
;
1350 codePageText
= codePage
;
1351 if (pfm
->characterSet
) {
1352 codePageText
= CodePageFromCharSet(pfm
->characterSet
, codePage
);
1354 if (pRenderTarget
) {
1355 D2D1_TEXT_ANTIALIAS_MODE aaMode
;
1356 aaMode
= DWriteMapFontQuality(pfm
->extraFontFlag
);
1358 if (aaMode
== D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE
&& customClearTypeRenderingParams
)
1359 pRenderTarget
->SetTextRenderingParams(customClearTypeRenderingParams
);
1360 else if (defaultRenderingParams
)
1361 pRenderTarget
->SetTextRenderingParams(defaultRenderingParams
);
1363 pRenderTarget
->SetTextAntialiasMode(aaMode
);
1367 int SurfaceD2D::LogPixelsY() {
1371 int SurfaceD2D::DeviceHeightFont(int points
) {
1372 return ::MulDiv(points
, LogPixelsY(), 72);
1375 void SurfaceD2D::MoveTo(int x_
, int y_
) {
1380 static int Delta(int difference
) {
1383 else if (difference
> 0)
1389 static float RoundFloat(float f
) {
1390 return float(int(f
+0.5f
));
1393 void SurfaceD2D::LineTo(int x_
, int y_
) {
1394 if (pRenderTarget
) {
1396 int xDelta
= Delta(xDiff
);
1398 int yDelta
= Delta(yDiff
);
1399 if ((xDiff
== 0) || (yDiff
== 0)) {
1400 // Horizontal or vertical lines can be more precisely drawn as a filled rectangle
1401 int xEnd
= x_
- xDelta
;
1402 int left
= Platform::Minimum(x
, xEnd
);
1403 int width
= abs(x
- xEnd
) + 1;
1404 int yEnd
= y_
- yDelta
;
1405 int top
= Platform::Minimum(y
, yEnd
);
1406 int height
= abs(y
- yEnd
) + 1;
1407 D2D1_RECT_F rectangle1
= D2D1::RectF(static_cast<float>(left
), static_cast<float>(top
),
1408 static_cast<float>(left
+width
), static_cast<float>(top
+height
));
1409 pRenderTarget
->FillRectangle(&rectangle1
, pBrush
);
1410 } else if ((abs(xDiff
) == abs(yDiff
))) {
1412 pRenderTarget
->DrawLine(D2D1::Point2F(x
+ 0.5f
, y
+ 0.5f
),
1413 D2D1::Point2F(x_
+ 0.5f
- xDelta
, y_
+ 0.5f
- yDelta
), pBrush
);
1415 // Line has a different slope so difficult to avoid last pixel
1416 pRenderTarget
->DrawLine(D2D1::Point2F(x
+ 0.5f
, y
+ 0.5f
),
1417 D2D1::Point2F(x_
+ 0.5f
, y_
+ 0.5f
), pBrush
);
1424 void SurfaceD2D::Polygon(Point
*pts
, int npts
, ColourDesired fore
, ColourDesired back
) {
1425 if (pRenderTarget
) {
1426 ID2D1Factory
*pFactory
= 0;
1427 pRenderTarget
->GetFactory(&pFactory
);
1428 ID2D1PathGeometry
*geometry
=0;
1429 HRESULT hr
= pFactory
->CreatePathGeometry(&geometry
);
1430 if (SUCCEEDED(hr
)) {
1431 ID2D1GeometrySink
*sink
= 0;
1432 hr
= geometry
->Open(&sink
);
1433 if (SUCCEEDED(hr
)) {
1434 sink
->BeginFigure(D2D1::Point2F(pts
[0].x
+ 0.5f
, pts
[0].y
+ 0.5f
), D2D1_FIGURE_BEGIN_FILLED
);
1435 for (size_t i
=1; i
<static_cast<size_t>(npts
); i
++) {
1436 sink
->AddLine(D2D1::Point2F(pts
[i
].x
+ 0.5f
, pts
[i
].y
+ 0.5f
));
1438 sink
->EndFigure(D2D1_FIGURE_END_CLOSED
);
1443 pRenderTarget
->FillGeometry(geometry
,pBrush
);
1445 pRenderTarget
->DrawGeometry(geometry
,pBrush
);
1448 geometry
->Release();
1453 void SurfaceD2D::RectangleDraw(PRectangle rc
, ColourDesired fore
, ColourDesired back
) {
1454 if (pRenderTarget
) {
1455 D2D1_RECT_F rectangle1
= D2D1::RectF(RoundFloat(rc
.left
) + 0.5f
, rc
.top
+0.5f
, RoundFloat(rc
.right
) - 0.5f
, rc
.bottom
-0.5f
);
1457 pRenderTarget
->FillRectangle(&rectangle1
, pBrush
);
1459 pRenderTarget
->DrawRectangle(&rectangle1
, pBrush
);
1463 void SurfaceD2D::FillRectangle(PRectangle rc
, ColourDesired back
) {
1464 if (pRenderTarget
) {
1466 D2D1_RECT_F rectangle1
= D2D1::RectF(RoundFloat(rc
.left
), rc
.top
, RoundFloat(rc
.right
), rc
.bottom
);
1467 pRenderTarget
->FillRectangle(&rectangle1
, pBrush
);
1471 void SurfaceD2D::FillRectangle(PRectangle rc
, Surface
&surfacePattern
) {
1472 SurfaceD2D
&surfOther
= static_cast<SurfaceD2D
&>(surfacePattern
);
1473 surfOther
.FlushDrawing();
1474 ID2D1Bitmap
*pBitmap
= NULL
;
1475 ID2D1BitmapRenderTarget
*pCompatibleRenderTarget
= reinterpret_cast<ID2D1BitmapRenderTarget
*>(
1476 surfOther
.pRenderTarget
);
1477 HRESULT hr
= pCompatibleRenderTarget
->GetBitmap(&pBitmap
);
1478 if (SUCCEEDED(hr
)) {
1479 ID2D1BitmapBrush
*pBitmapBrush
= NULL
;
1480 D2D1_BITMAP_BRUSH_PROPERTIES brushProperties
=
1481 D2D1::BitmapBrushProperties(D2D1_EXTEND_MODE_WRAP
, D2D1_EXTEND_MODE_WRAP
,
1482 D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR
);
1483 // Create the bitmap brush.
1484 hr
= pRenderTarget
->CreateBitmapBrush(pBitmap
, brushProperties
, &pBitmapBrush
);
1486 if (SUCCEEDED(hr
)) {
1487 pRenderTarget
->FillRectangle(
1488 D2D1::RectF(rc
.left
, rc
.top
, rc
.right
, rc
.bottom
),
1490 pBitmapBrush
->Release();
1495 void SurfaceD2D::RoundedRectangle(PRectangle rc
, ColourDesired fore
, ColourDesired back
) {
1496 if (pRenderTarget
) {
1497 D2D1_ROUNDED_RECT roundedRectFill
= {
1498 D2D1::RectF(rc
.left
+1.0f
, rc
.top
+1.0f
, rc
.right
-1.0f
, rc
.bottom
-1.0f
),
1501 pRenderTarget
->FillRoundedRectangle(roundedRectFill
, pBrush
);
1503 D2D1_ROUNDED_RECT roundedRect
= {
1504 D2D1::RectF(rc
.left
+ 0.5f
, rc
.top
+0.5f
, rc
.right
- 0.5f
, rc
.bottom
-0.5f
),
1507 pRenderTarget
->DrawRoundedRectangle(roundedRect
, pBrush
);
1511 void SurfaceD2D::AlphaRectangle(PRectangle rc
, int cornerSize
, ColourDesired fill
, int alphaFill
,
1512 ColourDesired outline
, int alphaOutline
, int /* flags*/ ) {
1513 if (pRenderTarget
) {
1514 if (cornerSize
== 0) {
1515 // When corner size is zero, draw square rectangle to prevent blurry pixels at corners
1516 D2D1_RECT_F rectFill
= D2D1::RectF(RoundFloat(rc
.left
) + 1.0f
, rc
.top
+ 1.0f
, RoundFloat(rc
.right
) - 1.0f
, rc
.bottom
- 1.0f
);
1517 D2DPenColour(fill
, alphaFill
);
1518 pRenderTarget
->FillRectangle(rectFill
, pBrush
);
1520 D2D1_RECT_F rectOutline
= D2D1::RectF(RoundFloat(rc
.left
) + 0.5f
, rc
.top
+ 0.5f
, RoundFloat(rc
.right
) - 0.5f
, rc
.bottom
- 0.5f
);
1521 D2DPenColour(outline
, alphaOutline
);
1522 pRenderTarget
->DrawRectangle(rectOutline
, pBrush
);
1524 const float cornerSizeF
= static_cast<float>(cornerSize
);
1525 D2D1_ROUNDED_RECT roundedRectFill
= {
1526 D2D1::RectF(RoundFloat(rc
.left
) + 1.0f
, rc
.top
+ 1.0f
, RoundFloat(rc
.right
) - 1.0f
, rc
.bottom
- 1.0f
),
1527 cornerSizeF
, cornerSizeF
};
1528 D2DPenColour(fill
, alphaFill
);
1529 pRenderTarget
->FillRoundedRectangle(roundedRectFill
, pBrush
);
1531 D2D1_ROUNDED_RECT roundedRect
= {
1532 D2D1::RectF(RoundFloat(rc
.left
) + 0.5f
, rc
.top
+ 0.5f
, RoundFloat(rc
.right
) - 0.5f
, rc
.bottom
- 0.5f
),
1533 cornerSizeF
, cornerSizeF
};
1534 D2DPenColour(outline
, alphaOutline
);
1535 pRenderTarget
->DrawRoundedRectangle(roundedRect
, pBrush
);
1540 void SurfaceD2D::DrawRGBAImage(PRectangle rc
, int width
, int height
, const unsigned char *pixelsImage
) {
1541 if (pRenderTarget
) {
1542 if (rc
.Width() > width
)
1543 rc
.left
+= static_cast<int>((rc
.Width() - width
) / 2);
1544 rc
.right
= rc
.left
+ width
;
1545 if (rc
.Height() > height
)
1546 rc
.top
+= static_cast<int>((rc
.Height() - height
) / 2);
1547 rc
.bottom
= rc
.top
+ height
;
1549 std::vector
<unsigned char> image(height
* width
* 4);
1550 for (int yPixel
=0; yPixel
<height
; yPixel
++) {
1551 for (int xPixel
= 0; xPixel
<width
; xPixel
++) {
1552 unsigned char *pixel
= &image
[0] + (yPixel
*width
+ xPixel
) * 4;
1553 unsigned char alpha
= pixelsImage
[3];
1554 // Input is RGBA, output is BGRA with premultiplied alpha
1555 pixel
[2] = (*pixelsImage
++) * alpha
/ 255;
1556 pixel
[1] = (*pixelsImage
++) * alpha
/ 255;
1557 pixel
[0] = (*pixelsImage
++) * alpha
/ 255;
1558 pixel
[3] = *pixelsImage
++;
1562 ID2D1Bitmap
*bitmap
= 0;
1563 D2D1_SIZE_U size
= D2D1::SizeU(width
, height
);
1564 D2D1_BITMAP_PROPERTIES props
= {{DXGI_FORMAT_B8G8R8A8_UNORM
,
1565 D2D1_ALPHA_MODE_PREMULTIPLIED
}, 72.0, 72.0};
1566 HRESULT hr
= pRenderTarget
->CreateBitmap(size
, &image
[0],
1567 width
* 4, &props
, &bitmap
);
1568 if (SUCCEEDED(hr
)) {
1569 D2D1_RECT_F rcDestination
= {rc
.left
, rc
.top
, rc
.right
, rc
.bottom
};
1570 pRenderTarget
->DrawBitmap(bitmap
, rcDestination
);
1576 void SurfaceD2D::Ellipse(PRectangle rc
, ColourDesired fore
, ColourDesired back
) {
1577 if (pRenderTarget
) {
1578 FLOAT radius
= rc
.Width() / 2.0f
;
1579 D2D1_ELLIPSE ellipse
= {
1580 D2D1::Point2F((rc
.left
+ rc
.right
) / 2.0f
, (rc
.top
+ rc
.bottom
) / 2.0f
),
1584 pRenderTarget
->FillEllipse(ellipse
, pBrush
);
1586 pRenderTarget
->DrawEllipse(ellipse
, pBrush
);
1590 void SurfaceD2D::Copy(PRectangle rc
, Point from
, Surface
&surfaceSource
) {
1591 SurfaceD2D
&surfOther
= static_cast<SurfaceD2D
&>(surfaceSource
);
1592 surfOther
.FlushDrawing();
1593 ID2D1BitmapRenderTarget
*pCompatibleRenderTarget
= reinterpret_cast<ID2D1BitmapRenderTarget
*>(
1594 surfOther
.pRenderTarget
);
1595 ID2D1Bitmap
*pBitmap
= NULL
;
1596 HRESULT hr
= pCompatibleRenderTarget
->GetBitmap(&pBitmap
);
1597 if (SUCCEEDED(hr
)) {
1598 D2D1_RECT_F rcDestination
= {rc
.left
, rc
.top
, rc
.right
, rc
.bottom
};
1599 D2D1_RECT_F rcSource
= {from
.x
, from
.y
, from
.x
+ rc
.Width(), from
.y
+ rc
.Height()};
1600 pRenderTarget
->DrawBitmap(pBitmap
, rcDestination
, 1.0f
,
1601 D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR
, rcSource
);
1602 pRenderTarget
->Flush();
1607 void SurfaceD2D::DrawTextCommon(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, UINT fuOptions
) {
1610 // Use Unicode calls
1611 const TextWide
tbuf(s
, len
, unicodeMode
, codePageText
);
1612 if (pRenderTarget
&& pTextFormat
&& pBrush
) {
1613 if (fuOptions
& ETO_CLIPPED
) {
1614 D2D1_RECT_F rcClip
= {rc
.left
, rc
.top
, rc
.right
, rc
.bottom
};
1615 pRenderTarget
->PushAxisAlignedClip(rcClip
, D2D1_ANTIALIAS_MODE_ALIASED
);
1618 // Explicitly creating a text layout appears a little faster
1619 IDWriteTextLayout
*pTextLayout
;
1620 HRESULT hr
= pIDWriteFactory
->CreateTextLayout(tbuf
.buffer
, tbuf
.tlen
, pTextFormat
,
1621 rc
.Width(), rc
.Height(), &pTextLayout
);
1622 if (SUCCEEDED(hr
)) {
1623 D2D1_POINT_2F origin
= {rc
.left
, ybase
-yAscent
};
1624 pRenderTarget
->DrawTextLayout(origin
, pTextLayout
, pBrush
, D2D1_DRAW_TEXT_OPTIONS_NONE
);
1625 pTextLayout
->Release();
1628 if (fuOptions
& ETO_CLIPPED
) {
1629 pRenderTarget
->PopAxisAlignedClip();
1634 void SurfaceD2D::DrawTextNoClip(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
,
1635 ColourDesired fore
, ColourDesired back
) {
1636 if (pRenderTarget
) {
1637 FillRectangle(rc
, back
);
1639 DrawTextCommon(rc
, font_
, ybase
, s
, len
, ETO_OPAQUE
);
1643 void SurfaceD2D::DrawTextClipped(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
,
1644 ColourDesired fore
, ColourDesired back
) {
1645 if (pRenderTarget
) {
1646 FillRectangle(rc
, back
);
1648 DrawTextCommon(rc
, font_
, ybase
, s
, len
, ETO_OPAQUE
| ETO_CLIPPED
);
1652 void SurfaceD2D::DrawTextTransparent(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
,
1653 ColourDesired fore
) {
1654 // Avoid drawing spaces in transparent mode
1655 for (int i
=0; i
<len
; i
++) {
1657 if (pRenderTarget
) {
1659 DrawTextCommon(rc
, font_
, ybase
, s
, len
, 0);
1666 XYPOSITION
SurfaceD2D::WidthText(Font
&font_
, const char *s
, int len
) {
1669 const TextWide
tbuf(s
, len
, unicodeMode
, codePageText
);
1670 if (pIDWriteFactory
&& pTextFormat
) {
1672 IDWriteTextLayout
*pTextLayout
= 0;
1673 HRESULT hr
= pIDWriteFactory
->CreateTextLayout(tbuf
.buffer
, tbuf
.tlen
, pTextFormat
, 1000.0, 1000.0, &pTextLayout
);
1674 if (SUCCEEDED(hr
)) {
1675 DWRITE_TEXT_METRICS textMetrics
;
1676 if (SUCCEEDED(pTextLayout
->GetMetrics(&textMetrics
)))
1677 width
= textMetrics
.widthIncludingTrailingWhitespace
;
1678 pTextLayout
->Release();
1684 void SurfaceD2D::MeasureWidths(Font
&font_
, const char *s
, int len
, XYPOSITION
*positions
) {
1687 const TextWide
tbuf(s
, len
, unicodeMode
, codePageText
);
1688 TextPositions
poses(tbuf
.tlen
);
1690 const int clusters
= 1000;
1691 DWRITE_CLUSTER_METRICS clusterMetrics
[clusters
];
1693 if (pIDWriteFactory
&& pTextFormat
) {
1696 IDWriteTextLayout
*pTextLayout
= 0;
1697 HRESULT hr
= pIDWriteFactory
->CreateTextLayout(tbuf
.buffer
, tbuf
.tlen
, pTextFormat
, 10000.0, 1000.0, &pTextLayout
);
1700 // For now, assuming WCHAR == cluster
1701 if (!SUCCEEDED(pTextLayout
->GetClusterMetrics(clusterMetrics
, clusters
, &count
)))
1703 FLOAT position
= 0.0f
;
1705 for (size_t ci
=0; ci
<count
; ci
++) {
1706 position
+= clusterMetrics
[ci
].width
;
1707 for (size_t inCluster
=0; inCluster
<clusterMetrics
[ci
].length
; inCluster
++) {
1708 //poses.buffer[ti++] = int(position + 0.5);
1709 poses
.buffer
[ti
++] = position
;
1712 PLATFORM_ASSERT(ti
== static_cast<size_t>(tbuf
.tlen
));
1713 pTextLayout
->Release();
1716 // Map the widths given for UTF-16 characters back onto the UTF-8 input string
1718 const unsigned char *us
= reinterpret_cast<const unsigned char *>(s
);
1721 unsigned char uch
= us
[i
];
1722 unsigned int lenChar
= 1;
1723 if (uch
>= (0x80 + 0x40 + 0x20 + 0x10)) {
1726 } else if (uch
>= (0x80 + 0x40 + 0x20)) {
1728 } else if (uch
>= (0x80)) {
1731 for (unsigned int bytePos
=0; (bytePos
<lenChar
) && (i
<len
); bytePos
++) {
1732 positions
[i
++] = poses
.buffer
[ui
];
1736 XYPOSITION lastPos
= 0.0f
;
1738 lastPos
= positions
[i
-1];
1740 positions
[i
++] = lastPos
;
1742 } else if (codePageText
== 0) {
1744 // One character per position
1745 PLATFORM_ASSERT(len
== tbuf
.tlen
);
1746 for (size_t kk
=0; kk
<static_cast<size_t>(len
); kk
++) {
1747 positions
[kk
] = poses
.buffer
[kk
];
1752 // May be more than one byte per position
1753 unsigned int ui
= 0;
1754 FLOAT position
= 0.0f
;
1755 for (int i
=0; i
<len
;) {
1757 position
= poses
.buffer
[ui
];
1758 if (Platform::IsDBCSLeadByte(codePageText
, s
[i
])) {
1759 positions
[i
] = position
;
1760 positions
[i
+1] = position
;
1763 positions
[i
] = position
;
1772 XYPOSITION
SurfaceD2D::WidthChar(Font
&font_
, char ch
) {
1775 if (pIDWriteFactory
&& pTextFormat
) {
1777 IDWriteTextLayout
*pTextLayout
= 0;
1778 const WCHAR wch
= ch
;
1779 HRESULT hr
= pIDWriteFactory
->CreateTextLayout(&wch
, 1, pTextFormat
, 1000.0, 1000.0, &pTextLayout
);
1780 if (SUCCEEDED(hr
)) {
1781 DWRITE_TEXT_METRICS textMetrics
;
1782 if (SUCCEEDED(pTextLayout
->GetMetrics(&textMetrics
)))
1783 width
= textMetrics
.widthIncludingTrailingWhitespace
;
1784 pTextLayout
->Release();
1790 XYPOSITION
SurfaceD2D::Ascent(Font
&font_
) {
1792 return ceil(yAscent
);
1795 XYPOSITION
SurfaceD2D::Descent(Font
&font_
) {
1797 return ceil(yDescent
);
1800 XYPOSITION
SurfaceD2D::InternalLeading(Font
&font_
) {
1802 return floor(yInternalLeading
);
1805 XYPOSITION
SurfaceD2D::ExternalLeading(Font
&) {
1806 // Not implemented, always return one
1810 XYPOSITION
SurfaceD2D::Height(Font
&font_
) {
1811 return Ascent(font_
) + Descent(font_
);
1814 XYPOSITION
SurfaceD2D::AverageCharWidth(Font
&font_
) {
1817 if (pIDWriteFactory
&& pTextFormat
) {
1819 IDWriteTextLayout
*pTextLayout
= 0;
1820 const WCHAR wszAllAlpha
[] = L
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1821 HRESULT hr
= pIDWriteFactory
->CreateTextLayout(wszAllAlpha
, static_cast<UINT32
>(wcslen(wszAllAlpha
)),
1822 pTextFormat
, 1000.0, 1000.0, &pTextLayout
);
1823 if (SUCCEEDED(hr
)) {
1824 DWRITE_TEXT_METRICS textMetrics
;
1825 if (SUCCEEDED(pTextLayout
->GetMetrics(&textMetrics
)))
1826 width
= textMetrics
.width
/ wcslen(wszAllAlpha
);
1827 pTextLayout
->Release();
1833 void SurfaceD2D::SetClip(PRectangle rc
) {
1834 if (pRenderTarget
) {
1835 D2D1_RECT_F rcClip
= {rc
.left
, rc
.top
, rc
.right
, rc
.bottom
};
1836 pRenderTarget
->PushAxisAlignedClip(rcClip
, D2D1_ANTIALIAS_MODE_ALIASED
);
1841 void SurfaceD2D::FlushCachedState() {
1844 void SurfaceD2D::SetUnicodeMode(bool unicodeMode_
) {
1845 unicodeMode
=unicodeMode_
;
1848 void SurfaceD2D::SetDBCSMode(int codePage_
) {
1849 // No action on window as automatically handled by system.
1850 codePage
= codePage_
;
1854 Surface
*Surface::Allocate(int technology
) {
1855 #if defined(USE_D2D)
1856 if (technology
== SCWIN_TECH_GDI
)
1857 return new SurfaceGDI
;
1859 return new SurfaceD2D
;
1861 return new SurfaceGDI
;
1868 void Window::Destroy() {
1870 ::DestroyWindow(reinterpret_cast<HWND
>(wid
));
1874 bool Window::HasFocus() {
1875 return ::GetFocus() == wid
;
1878 PRectangle
Window::GetPosition() {
1880 ::GetWindowRect(reinterpret_cast<HWND
>(wid
), &rc
);
1881 return PRectangle::FromInts(rc
.left
, rc
.top
, rc
.right
, rc
.bottom
);
1884 void Window::SetPosition(PRectangle rc
) {
1885 ::SetWindowPos(reinterpret_cast<HWND
>(wid
),
1886 0, static_cast<int>(rc
.left
), static_cast<int>(rc
.top
),
1887 static_cast<int>(rc
.Width()), static_cast<int>(rc
.Height()), SWP_NOZORDER
| SWP_NOACTIVATE
);
1890 static RECT
RectFromMonitor(HMONITOR hMonitor
) {
1891 if (GetMonitorInfoFn
) {
1892 MONITORINFO mi
= {0};
1893 mi
.cbSize
= sizeof(mi
);
1894 if (GetMonitorInfoFn(hMonitor
, &mi
)) {
1898 RECT rc
= {0, 0, 0, 0};
1899 if (::SystemParametersInfoA(SPI_GETWORKAREA
, 0, &rc
, 0) == 0) {
1908 void Window::SetPositionRelative(PRectangle rc
, Window w
) {
1909 LONG style
= ::GetWindowLong(reinterpret_cast<HWND
>(wid
), GWL_STYLE
);
1910 if (style
& WS_POPUP
) {
1911 POINT ptOther
= {0, 0};
1912 ::ClientToScreen(reinterpret_cast<HWND
>(w
.GetID()), &ptOther
);
1913 rc
.Move(static_cast<XYPOSITION
>(ptOther
.x
), static_cast<XYPOSITION
>(ptOther
.y
));
1915 RECT rcMonitor
= RectFromPRectangle(rc
);
1917 HMONITOR hMonitor
= NULL
;
1918 if (MonitorFromRectFn
)
1919 hMonitor
= MonitorFromRectFn(&rcMonitor
, MONITOR_DEFAULTTONEAREST
);
1920 // If hMonitor is NULL, that's just the main screen anyways.
1921 //::GetMonitorInfo(hMonitor, &mi);
1922 RECT rcWork
= RectFromMonitor(hMonitor
);
1924 if (rcWork
.left
< rcWork
.right
) {
1925 // Now clamp our desired rectangle to fit inside the work area
1926 // This way, the menu will fit wholly on one screen. An improvement even
1927 // if you don't have a second monitor on the left... Menu's appears half on
1928 // one screen and half on the other are just U.G.L.Y.!
1929 if (rc
.right
> rcWork
.right
)
1930 rc
.Move(rcWork
.right
- rc
.right
, 0);
1931 if (rc
.bottom
> rcWork
.bottom
)
1932 rc
.Move(0, rcWork
.bottom
- rc
.bottom
);
1933 if (rc
.left
< rcWork
.left
)
1934 rc
.Move(rcWork
.left
- rc
.left
, 0);
1935 if (rc
.top
< rcWork
.top
)
1936 rc
.Move(0, rcWork
.top
- rc
.top
);
1942 PRectangle
Window::GetClientPosition() {
1945 ::GetClientRect(reinterpret_cast<HWND
>(wid
), &rc
);
1946 return PRectangle::FromInts(rc
.left
, rc
.top
, rc
.right
, rc
.bottom
);
1949 void Window::Show(bool show
) {
1951 ::ShowWindow(reinterpret_cast<HWND
>(wid
), SW_SHOWNOACTIVATE
);
1953 ::ShowWindow(reinterpret_cast<HWND
>(wid
), SW_HIDE
);
1956 void Window::InvalidateAll() {
1957 ::InvalidateRect(reinterpret_cast<HWND
>(wid
), NULL
, FALSE
);
1960 void Window::InvalidateRectangle(PRectangle rc
) {
1961 RECT rcw
= RectFromPRectangle(rc
);
1962 ::InvalidateRect(reinterpret_cast<HWND
>(wid
), &rcw
, FALSE
);
1965 static LRESULT
Window_SendMessage(Window
*w
, UINT msg
, WPARAM wParam
=0, LPARAM lParam
=0) {
1966 return ::SendMessage(reinterpret_cast<HWND
>(w
->GetID()), msg
, wParam
, lParam
);
1969 void Window::SetFont(Font
&font
) {
1970 Window_SendMessage(this, WM_SETFONT
,
1971 reinterpret_cast<WPARAM
>(font
.GetID()), 0);
1974 static void FlipBitmap(HBITMAP bitmap
, int width
, int height
) {
1975 HDC hdc
= ::CreateCompatibleDC(NULL
);
1977 HGDIOBJ prevBmp
= ::SelectObject(hdc
, bitmap
);
1978 ::StretchBlt(hdc
, width
- 1, 0, -width
, height
, hdc
, 0, 0, width
, height
, SRCCOPY
);
1979 ::SelectObject(hdc
, prevBmp
);
1984 static HCURSOR
GetReverseArrowCursor() {
1985 if (reverseArrowCursor
!= NULL
)
1986 return reverseArrowCursor
;
1988 ::EnterCriticalSection(&crPlatformLock
);
1989 HCURSOR cursor
= reverseArrowCursor
;
1990 if (cursor
== NULL
) {
1991 cursor
= ::LoadCursor(NULL
, IDC_ARROW
);
1993 if (::GetIconInfo(cursor
, &info
)) {
1995 if (::GetObject(info
.hbmMask
, sizeof(bmp
), &bmp
)) {
1996 FlipBitmap(info
.hbmMask
, bmp
.bmWidth
, bmp
.bmHeight
);
1997 if (info
.hbmColor
!= NULL
)
1998 FlipBitmap(info
.hbmColor
, bmp
.bmWidth
, bmp
.bmHeight
);
1999 info
.xHotspot
= (DWORD
)bmp
.bmWidth
- 1 - info
.xHotspot
;
2001 reverseArrowCursor
= ::CreateIconIndirect(&info
);
2002 if (reverseArrowCursor
!= NULL
)
2003 cursor
= reverseArrowCursor
;
2006 ::DeleteObject(info
.hbmMask
);
2007 if (info
.hbmColor
!= NULL
)
2008 ::DeleteObject(info
.hbmColor
);
2011 ::LeaveCriticalSection(&crPlatformLock
);
2015 void Window::SetCursor(Cursor curs
) {
2018 ::SetCursor(::LoadCursor(NULL
,IDC_IBEAM
));
2021 ::SetCursor(::LoadCursor(NULL
,IDC_UPARROW
));
2024 ::SetCursor(::LoadCursor(NULL
,IDC_WAIT
));
2027 ::SetCursor(::LoadCursor(NULL
,IDC_SIZEWE
));
2030 ::SetCursor(::LoadCursor(NULL
,IDC_SIZENS
));
2033 ::SetCursor(::LoadCursor(NULL
,IDC_HAND
));
2035 case cursorReverseArrow
:
2036 ::SetCursor(GetReverseArrowCursor());
2039 case cursorInvalid
: // Should not occur, but just in case.
2040 ::SetCursor(::LoadCursor(NULL
,IDC_ARROW
));
2045 void Window::SetTitle(const char *s
) {
2046 ::SetWindowTextA(reinterpret_cast<HWND
>(wid
), s
);
2049 /* Returns rectangle of monitor pt is on, both rect and pt are in Window's
2051 PRectangle
Window::GetMonitorRect(Point pt
) {
2052 // MonitorFromPoint and GetMonitorInfo are not available on Windows 95 and NT 4.
2053 PRectangle rcPosition
= GetPosition();
2054 POINT ptDesktop
= {static_cast<LONG
>(pt
.x
+ rcPosition
.left
),
2055 static_cast<LONG
>(pt
.y
+ rcPosition
.top
)};
2056 HMONITOR hMonitor
= NULL
;
2057 if (MonitorFromPointFn
)
2058 hMonitor
= MonitorFromPointFn(ptDesktop
, MONITOR_DEFAULTTONEAREST
);
2060 RECT rcWork
= RectFromMonitor(hMonitor
);
2061 if (rcWork
.left
< rcWork
.right
) {
2062 PRectangle
rcMonitor(
2063 rcWork
.left
- rcPosition
.left
,
2064 rcWork
.top
- rcPosition
.top
,
2065 rcWork
.right
- rcPosition
.left
,
2066 rcWork
.bottom
- rcPosition
.top
);
2069 return PRectangle();
2073 struct ListItemData
{
2079 std::vector
<char> words
;
2081 std::vector
<ListItemData
> data
;
2094 ListItemData
Get(int index
) const {
2095 if (index
>= 0 && index
< static_cast<int>(data
.size())) {
2098 ListItemData missing
= {"", -1};
2103 return static_cast<int>(data
.size());
2106 void AllocItem(const char *text
, int pixId
) {
2107 ListItemData lid
= { text
, pixId
};
2108 data
.push_back(lid
);
2111 char *SetWords(const char *s
) {
2112 words
= std::vector
<char>(s
, s
+strlen(s
)+1);
2117 const TCHAR ListBoxX_ClassName
[] = TEXT("ListBoxX");
2119 ListBox::ListBox() {
2122 ListBox::~ListBox() {
2125 class ListBoxX
: public ListBox
{
2129 RGBAImageSet images
;
2133 int desiredVisibleRows
;
2134 unsigned int maxItemCharacters
;
2135 unsigned int aveCharWidth
;
2138 CallBackAction doubleClickAction
;
2139 void *doubleClickActionData
;
2140 const char *widestItem
;
2141 unsigned int maxCharWidth
;
2143 PRectangle rcPreSize
;
2145 Point location
; // Caret location at which the list is opened
2146 int wheelDelta
; // mouse wheel residue
2148 HWND
GetHWND() const;
2149 void AppendListItem(const char *text
, const char *numword
);
2150 static void AdjustWindowRect(PRectangle
*rc
);
2151 int ItemHeight() const;
2152 int MinClientWidth() const;
2153 int TextOffset() const;
2154 POINT
GetClientExtent() const;
2155 POINT
MinTrackSize() const;
2156 POINT
MaxTrackSize() const;
2157 void SetRedraw(bool on
);
2158 void OnDoubleClick();
2159 void ResizeToCursor();
2160 void StartResize(WPARAM
);
2161 LRESULT
NcHitTest(WPARAM
, LPARAM
) const;
2162 void CentreItem(int n
);
2164 static LRESULT PASCAL
ControlWndProc(HWND hWnd
, UINT iMessage
, WPARAM wParam
, LPARAM lParam
);
2166 static const Point ItemInset
; // Padding around whole item
2167 static const Point TextInset
; // Padding around text
2168 static const Point ImageInset
; // Padding around image
2171 ListBoxX() : lineHeight(10), fontCopy(0), technology(0), lb(0), unicodeMode(false),
2172 desiredVisibleRows(5), maxItemCharacters(0), aveCharWidth(8),
2173 parent(NULL
), ctrlID(0), doubleClickAction(NULL
), doubleClickActionData(NULL
),
2174 widestItem(NULL
), maxCharWidth(1), resizeHit(0), wheelDelta(0) {
2176 virtual ~ListBoxX() {
2178 ::DeleteObject(fontCopy
);
2182 virtual void SetFont(Font
&font
);
2183 virtual void Create(Window
&parent_
, int ctrlID_
, Point location_
, int lineHeight_
, bool unicodeMode_
, int technology_
);
2184 virtual void SetAverageCharWidth(int width
);
2185 virtual void SetVisibleRows(int rows
);
2186 virtual int GetVisibleRows() const;
2187 virtual PRectangle
GetDesiredRect();
2188 virtual int CaretFromEdge();
2189 virtual void Clear();
2190 virtual void Append(char *s
, int type
= -1);
2191 virtual int Length();
2192 virtual void Select(int n
);
2193 virtual int GetSelection();
2194 virtual int Find(const char *prefix
);
2195 virtual void GetValue(int n
, char *value
, int len
);
2196 virtual void RegisterImage(int type
, const char *xpm_data
);
2197 virtual void RegisterRGBAImage(int type
, int width
, int height
, const unsigned char *pixelsImage
);
2198 virtual void ClearRegisteredImages();
2199 virtual void SetDoubleClickAction(CallBackAction action
, void *data
) {
2200 doubleClickAction
= action
;
2201 doubleClickActionData
= data
;
2203 virtual void SetList(const char *list
, char separator
, char typesep
);
2204 void Draw(DRAWITEMSTRUCT
*pDrawItem
);
2205 LRESULT
WndProc(HWND hWnd
, UINT iMessage
, WPARAM wParam
, LPARAM lParam
);
2206 static LRESULT PASCAL
StaticWndProc(HWND hWnd
, UINT iMessage
, WPARAM wParam
, LPARAM lParam
);
2209 const Point
ListBoxX::ItemInset(0, 0);
2210 const Point
ListBoxX::TextInset(2, 0);
2211 const Point
ListBoxX::ImageInset(1, 0);
2213 ListBox
*ListBox::Allocate() {
2214 ListBoxX
*lb
= new ListBoxX();
2218 void ListBoxX::Create(Window
&parent_
, int ctrlID_
, Point location_
, int lineHeight_
, bool unicodeMode_
, int technology_
) {
2221 location
= location_
;
2222 lineHeight
= lineHeight_
;
2223 unicodeMode
= unicodeMode_
;
2224 technology
= technology_
;
2225 HWND hwndParent
= reinterpret_cast<HWND
>(parent
->GetID());
2226 HINSTANCE hinstanceParent
= GetWindowInstance(hwndParent
);
2227 // Window created as popup so not clipped within parent client area
2228 wid
= ::CreateWindowEx(
2229 WS_EX_WINDOWEDGE
, ListBoxX_ClassName
, TEXT(""),
2230 WS_POPUP
| WS_THICKFRAME
,
2231 100,100, 150,80, hwndParent
,
2236 POINT locationw
= {static_cast<LONG
>(location
.x
), static_cast<LONG
>(location
.y
)};
2237 ::MapWindowPoints(hwndParent
, NULL
, &locationw
, 1);
2238 location
= Point::FromInts(locationw
.x
, locationw
.y
);
2241 void ListBoxX::SetFont(Font
&font
) {
2244 ::DeleteObject(fontCopy
);
2247 FormatAndMetrics
*pfm
= reinterpret_cast<FormatAndMetrics
*>(font
.GetID());
2248 fontCopy
= pfm
->HFont();
2249 ::SendMessage(lb
, WM_SETFONT
, reinterpret_cast<WPARAM
>(fontCopy
), 0);
2253 void ListBoxX::SetAverageCharWidth(int width
) {
2254 aveCharWidth
= width
;
2257 void ListBoxX::SetVisibleRows(int rows
) {
2258 desiredVisibleRows
= rows
;
2261 int ListBoxX::GetVisibleRows() const {
2262 return desiredVisibleRows
;
2265 HWND
ListBoxX::GetHWND() const {
2266 return reinterpret_cast<HWND
>(GetID());
2269 PRectangle
ListBoxX::GetDesiredRect() {
2270 PRectangle rcDesired
= GetPosition();
2272 int rows
= Length();
2273 if ((rows
== 0) || (rows
> desiredVisibleRows
))
2274 rows
= desiredVisibleRows
;
2275 rcDesired
.bottom
= rcDesired
.top
+ ItemHeight() * rows
;
2277 int width
= MinClientWidth();
2278 HDC hdc
= ::GetDC(lb
);
2279 HFONT oldFont
= SelectFont(hdc
, fontCopy
);
2280 SIZE textSize
= {0, 0};
2283 len
= static_cast<int>(strlen(widestItem
));
2285 const TextWide
tbuf(widestItem
, len
, unicodeMode
);
2286 ::GetTextExtentPoint32W(hdc
, tbuf
.buffer
, tbuf
.tlen
, &textSize
);
2288 ::GetTextExtentPoint32A(hdc
, widestItem
, len
, &textSize
);
2292 ::GetTextMetrics(hdc
, &tm
);
2293 maxCharWidth
= tm
.tmMaxCharWidth
;
2294 SelectFont(hdc
, oldFont
);
2295 ::ReleaseDC(lb
, hdc
);
2297 int widthDesired
= Platform::Maximum(textSize
.cx
, (len
+ 1) * tm
.tmAveCharWidth
);
2298 if (width
< widthDesired
)
2299 width
= widthDesired
;
2301 rcDesired
.right
= rcDesired
.left
+ TextOffset() + width
+ (TextInset
.x
* 2);
2302 if (Length() > rows
)
2303 rcDesired
.right
+= ::GetSystemMetrics(SM_CXVSCROLL
);
2305 AdjustWindowRect(&rcDesired
);
2309 int ListBoxX::TextOffset() const {
2310 int pixWidth
= images
.GetWidth();
2311 return static_cast<int>(pixWidth
== 0 ? ItemInset
.x
: ItemInset
.x
+ pixWidth
+ (ImageInset
.x
* 2));
2314 int ListBoxX::CaretFromEdge() {
2316 AdjustWindowRect(&rc
);
2317 return TextOffset() + static_cast<int>(TextInset
.x
+ (0 - rc
.left
) - 1);
2320 void ListBoxX::Clear() {
2321 ::SendMessage(lb
, LB_RESETCONTENT
, 0, 0);
2322 maxItemCharacters
= 0;
2327 void ListBoxX::Append(char *, int) {
2328 // This method is no longer called in Scintilla
2329 PLATFORM_ASSERT(false);
2332 int ListBoxX::Length() {
2336 void ListBoxX::Select(int n
) {
2337 // We are going to scroll to centre on the new selection and then select it, so disable
2338 // redraw to avoid flicker caused by a painting new selection twice in unselected and then
2342 ::SendMessage(lb
, LB_SETCURSEL
, n
, 0);
2346 int ListBoxX::GetSelection() {
2347 return static_cast<int>(::SendMessage(lb
, LB_GETCURSEL
, 0, 0));
2350 // This is not actually called at present
2351 int ListBoxX::Find(const char *) {
2355 void ListBoxX::GetValue(int n
, char *value
, int len
) {
2356 ListItemData item
= lti
.Get(n
);
2357 strncpy(value
, item
.text
, len
);
2358 value
[len
-1] = '\0';
2361 void ListBoxX::RegisterImage(int type
, const char *xpm_data
) {
2362 XPM
xpmImage(xpm_data
);
2363 images
.Add(type
, new RGBAImage(xpmImage
));
2366 void ListBoxX::RegisterRGBAImage(int type
, int width
, int height
, const unsigned char *pixelsImage
) {
2367 images
.Add(type
, new RGBAImage(width
, height
, 1.0, pixelsImage
));
2370 void ListBoxX::ClearRegisteredImages() {
2374 void ListBoxX::Draw(DRAWITEMSTRUCT
*pDrawItem
) {
2375 if ((pDrawItem
->itemAction
== ODA_SELECT
) || (pDrawItem
->itemAction
== ODA_DRAWENTIRE
)) {
2376 RECT rcBox
= pDrawItem
->rcItem
;
2377 rcBox
.left
+= TextOffset();
2378 if (pDrawItem
->itemState
& ODS_SELECTED
) {
2379 RECT rcImage
= pDrawItem
->rcItem
;
2380 rcImage
.right
= rcBox
.left
;
2381 // The image is not highlighted
2382 ::FillRect(pDrawItem
->hDC
, &rcImage
, reinterpret_cast<HBRUSH
>(COLOR_WINDOW
+1));
2383 ::FillRect(pDrawItem
->hDC
, &rcBox
, reinterpret_cast<HBRUSH
>(COLOR_HIGHLIGHT
+1));
2384 ::SetBkColor(pDrawItem
->hDC
, ::GetSysColor(COLOR_HIGHLIGHT
));
2385 ::SetTextColor(pDrawItem
->hDC
, ::GetSysColor(COLOR_HIGHLIGHTTEXT
));
2387 ::FillRect(pDrawItem
->hDC
, &pDrawItem
->rcItem
, reinterpret_cast<HBRUSH
>(COLOR_WINDOW
+1));
2388 ::SetBkColor(pDrawItem
->hDC
, ::GetSysColor(COLOR_WINDOW
));
2389 ::SetTextColor(pDrawItem
->hDC
, ::GetSysColor(COLOR_WINDOWTEXT
));
2392 ListItemData item
= lti
.Get(pDrawItem
->itemID
);
2393 int pixId
= item
.pixId
;
2394 const char *text
= item
.text
;
2395 int len
= static_cast<int>(strlen(text
));
2397 RECT rcText
= rcBox
;
2398 ::InsetRect(&rcText
, static_cast<int>(TextInset
.x
), static_cast<int>(TextInset
.y
));
2401 const TextWide
tbuf(text
, len
, unicodeMode
);
2402 ::DrawTextW(pDrawItem
->hDC
, tbuf
.buffer
, tbuf
.tlen
, &rcText
, DT_NOPREFIX
|DT_END_ELLIPSIS
|DT_SINGLELINE
|DT_NOCLIP
);
2404 ::DrawTextA(pDrawItem
->hDC
, text
, len
, &rcText
, DT_NOPREFIX
|DT_END_ELLIPSIS
|DT_SINGLELINE
|DT_NOCLIP
);
2406 if (pDrawItem
->itemState
& ODS_SELECTED
) {
2407 ::DrawFocusRect(pDrawItem
->hDC
, &rcBox
);
2410 // Draw the image, if any
2411 RGBAImage
*pimage
= images
.Get(pixId
);
2413 Surface
*surfaceItem
= Surface::Allocate(technology
);
2415 if (technology
== SCWIN_TECH_GDI
) {
2416 surfaceItem
->Init(pDrawItem
->hDC
, pDrawItem
->hwndItem
);
2417 long left
= pDrawItem
->rcItem
.left
+ static_cast<int>(ItemInset
.x
+ ImageInset
.x
);
2418 PRectangle rcImage
= PRectangle::FromInts(left
, pDrawItem
->rcItem
.top
,
2419 left
+ images
.GetWidth(), pDrawItem
->rcItem
.bottom
);
2420 surfaceItem
->DrawRGBAImage(rcImage
,
2421 pimage
->GetWidth(), pimage
->GetHeight(), pimage
->Pixels());
2423 ::SetTextAlign(pDrawItem
->hDC
, TA_TOP
);
2425 #if defined(USE_D2D)
2426 D2D1_RENDER_TARGET_PROPERTIES props
= D2D1::RenderTargetProperties(
2427 D2D1_RENDER_TARGET_TYPE_DEFAULT
,
2429 DXGI_FORMAT_B8G8R8A8_UNORM
,
2430 D2D1_ALPHA_MODE_IGNORE
),
2433 D2D1_RENDER_TARGET_USAGE_NONE
,
2434 D2D1_FEATURE_LEVEL_DEFAULT
2436 ID2D1DCRenderTarget
*pDCRT
= 0;
2437 HRESULT hr
= pD2DFactory
->CreateDCRenderTarget(&props
, &pDCRT
);
2438 if (SUCCEEDED(hr
)) {
2440 GetClientRect(pDrawItem
->hwndItem
, &rcWindow
);
2441 hr
= pDCRT
->BindDC(pDrawItem
->hDC
, &rcWindow
);
2442 if (SUCCEEDED(hr
)) {
2443 surfaceItem
->Init(pDCRT
, pDrawItem
->hwndItem
);
2445 long left
= pDrawItem
->rcItem
.left
+ static_cast<long>(ItemInset
.x
+ ImageInset
.x
);
2446 PRectangle rcImage
= PRectangle::FromInts(left
, pDrawItem
->rcItem
.top
,
2447 left
+ images
.GetWidth(), pDrawItem
->rcItem
.bottom
);
2448 surfaceItem
->DrawRGBAImage(rcImage
,
2449 pimage
->GetWidth(), pimage
->GetHeight(), pimage
->Pixels());
2466 void ListBoxX::AppendListItem(const char *text
, const char *numword
) {
2471 while ((ch
= *++numword
) != '\0') {
2472 pixId
= 10 * pixId
+ (ch
- '0');
2476 lti
.AllocItem(text
, pixId
);
2477 unsigned int len
= static_cast<unsigned int>(strlen(text
));
2478 if (maxItemCharacters
< len
) {
2479 maxItemCharacters
= len
;
2484 void ListBoxX::SetList(const char *list
, char separator
, char typesep
) {
2485 // Turn off redraw while populating the list - this has a significant effect, even if
2486 // the listbox is not visible.
2489 size_t size
= strlen(list
);
2490 char *words
= lti
.SetWords(list
);
2491 char *startword
= words
;
2492 char *numword
= NULL
;
2493 for (size_t i
=0; i
< size
; i
++) {
2494 if (words
[i
] == separator
) {
2498 AppendListItem(startword
, numword
);
2499 startword
= words
+ i
+ 1;
2501 } else if (words
[i
] == typesep
) {
2502 numword
= words
+ i
;
2508 AppendListItem(startword
, numword
);
2511 // Finally populate the listbox itself with the correct number of items
2512 int count
= lti
.Count();
2513 ::SendMessage(lb
, LB_INITSTORAGE
, count
, 0);
2514 for (int j
=0; j
<count
; j
++) {
2515 ::SendMessage(lb
, LB_ADDSTRING
, 0, j
+1);
2520 void ListBoxX::AdjustWindowRect(PRectangle
*rc
) {
2521 RECT rcw
= RectFromPRectangle(*rc
);
2522 ::AdjustWindowRectEx(&rcw
, WS_THICKFRAME
, false, WS_EX_WINDOWEDGE
);
2523 *rc
= PRectangle::FromInts(rcw
.left
, rcw
.top
, rcw
.right
, rcw
.bottom
);
2526 int ListBoxX::ItemHeight() const {
2527 int itemHeight
= lineHeight
+ (static_cast<int>(TextInset
.y
) * 2);
2528 int pixHeight
= images
.GetHeight() + (static_cast<int>(ImageInset
.y
) * 2);
2529 if (itemHeight
< pixHeight
) {
2530 itemHeight
= pixHeight
;
2535 int ListBoxX::MinClientWidth() const {
2536 return 12 * (aveCharWidth
+aveCharWidth
/3);
2539 POINT
ListBoxX::MinTrackSize() const {
2540 PRectangle rc
= PRectangle::FromInts(0, 0, MinClientWidth(), ItemHeight());
2541 AdjustWindowRect(&rc
);
2542 POINT ret
= {static_cast<LONG
>(rc
.Width()), static_cast<LONG
>(rc
.Height())};
2546 POINT
ListBoxX::MaxTrackSize() const {
2547 PRectangle rc
= PRectangle::FromInts(0, 0,
2548 Platform::Maximum(MinClientWidth(),
2549 maxCharWidth
* maxItemCharacters
+ static_cast<int>(TextInset
.x
) * 2 +
2550 TextOffset() + ::GetSystemMetrics(SM_CXVSCROLL
)),
2551 ItemHeight() * lti
.Count());
2552 AdjustWindowRect(&rc
);
2553 POINT ret
= {static_cast<LONG
>(rc
.Width()), static_cast<LONG
>(rc
.Height())};
2557 void ListBoxX::SetRedraw(bool on
) {
2558 ::SendMessage(lb
, WM_SETREDRAW
, static_cast<BOOL
>(on
), 0);
2560 ::InvalidateRect(lb
, NULL
, TRUE
);
2563 static XYPOSITION
XYMinimum(XYPOSITION a
, XYPOSITION b
) {
2570 static XYPOSITION
XYMaximum(XYPOSITION a
, XYPOSITION b
) {
2577 void ListBoxX::ResizeToCursor() {
2578 PRectangle rc
= GetPosition();
2580 ::GetCursorPos(&ptw
);
2581 Point pt
= Point::FromInts(ptw
.x
, ptw
.y
);
2582 pt
.x
+= dragOffset
.x
;
2583 pt
.y
+= dragOffset
.y
;
2585 switch (resizeHit
) {
2616 POINT ptMin
= MinTrackSize();
2617 POINT ptMax
= MaxTrackSize();
2618 // We don't allow the left edge to move at present, but just in case
2619 rc
.left
= XYMaximum(XYMinimum(rc
.left
, rcPreSize
.right
- ptMin
.x
), rcPreSize
.right
- ptMax
.x
);
2620 rc
.top
= XYMaximum(XYMinimum(rc
.top
, rcPreSize
.bottom
- ptMin
.y
), rcPreSize
.bottom
- ptMax
.y
);
2621 rc
.right
= XYMaximum(XYMinimum(rc
.right
, rcPreSize
.left
+ ptMax
.x
), rcPreSize
.left
+ ptMin
.x
);
2622 rc
.bottom
= XYMaximum(XYMinimum(rc
.bottom
, rcPreSize
.top
+ ptMax
.y
), rcPreSize
.top
+ ptMin
.y
);
2627 void ListBoxX::StartResize(WPARAM hitCode
) {
2628 rcPreSize
= GetPosition();
2630 ::GetCursorPos(&cursorPos
);
2636 dragOffset
.x
= rcPreSize
.right
- cursorPos
.x
;
2637 dragOffset
.y
= rcPreSize
.bottom
- cursorPos
.y
;
2641 dragOffset
.x
= rcPreSize
.right
- cursorPos
.x
;
2642 dragOffset
.y
= rcPreSize
.top
- cursorPos
.y
;
2645 // Note that the current hit test code prevents the left edge cases ever firing
2646 // as we don't want the left edge to be moveable
2650 dragOffset
.x
= rcPreSize
.left
- cursorPos
.x
;
2651 dragOffset
.y
= rcPreSize
.top
- cursorPos
.y
;
2654 dragOffset
.x
= rcPreSize
.left
- cursorPos
.x
;
2655 dragOffset
.y
= rcPreSize
.bottom
- cursorPos
.y
;
2662 ::SetCapture(GetHWND());
2663 resizeHit
= static_cast<int>(hitCode
);
2666 LRESULT
ListBoxX::NcHitTest(WPARAM wParam
, LPARAM lParam
) const {
2667 LRESULT hit
= ::DefWindowProc(GetHWND(), WM_NCHITTEST
, wParam
, lParam
);
2668 // There is an apparent bug in the DefWindowProc hit test code whereby it will
2669 // return HTTOPXXX if the window in question is shorter than the default
2670 // window caption height + frame, even if one is hovering over the bottom edge of
2671 // the frame, so workaround that here
2672 if (hit
>= HTTOP
&& hit
<= HTTOPRIGHT
) {
2673 int minHeight
= GetSystemMetrics(SM_CYMINTRACK
);
2674 PRectangle rc
= const_cast<ListBoxX
*>(this)->GetPosition();
2675 int yPos
= GET_Y_LPARAM(lParam
);
2676 if ((rc
.Height() < minHeight
) && (yPos
> ((rc
.top
+ rc
.bottom
)/2))) {
2677 hit
+= HTBOTTOM
- HTTOP
;
2681 // Nerver permit resizing that moves the left edge. Allow movement of top or bottom edge
2682 // depending on whether the list is above or below the caret
2692 PRectangle rc
= const_cast<ListBoxX
*>(this)->GetPosition();
2693 // Valid only if caret below list
2694 if (location
.y
< rc
.top
)
2700 case HTBOTTOMRIGHT
: {
2701 PRectangle rc
= const_cast<ListBoxX
*>(this)->GetPosition();
2702 // Valid only if caret above list
2703 if (rc
.bottom
< location
.y
)
2712 void ListBoxX::OnDoubleClick() {
2714 if (doubleClickAction
!= NULL
) {
2715 doubleClickAction(doubleClickActionData
);
2719 POINT
ListBoxX::GetClientExtent() const {
2720 PRectangle rc
= const_cast<ListBoxX
*>(this)->GetClientPosition();
2722 ret
.x
= static_cast<LONG
>(rc
.Width());
2723 ret
.y
= static_cast<LONG
>(rc
.Height());
2727 void ListBoxX::CentreItem(int n
) {
2728 // If below mid point, scroll up to centre, but with more items below if uneven
2730 POINT extent
= GetClientExtent();
2731 int visible
= extent
.y
/ItemHeight();
2732 if (visible
< Length()) {
2733 LRESULT top
= ::SendMessage(lb
, LB_GETTOPINDEX
, 0, 0);
2734 int half
= (visible
- 1) / 2;
2735 if (n
> (top
+ half
))
2736 ::SendMessage(lb
, LB_SETTOPINDEX
, n
- half
, 0);
2741 // Performs a double-buffered paint operation to avoid flicker
2742 void ListBoxX::Paint(HDC hDC
) {
2743 POINT extent
= GetClientExtent();
2744 HBITMAP hBitmap
= ::CreateCompatibleBitmap(hDC
, extent
.x
, extent
.y
);
2745 HDC bitmapDC
= ::CreateCompatibleDC(hDC
);
2746 HBITMAP hBitmapOld
= SelectBitmap(bitmapDC
, hBitmap
);
2747 // The list background is mainly erased during painting, but can be a small
2748 // unpainted area when at the end of a non-integrally sized list with a
2749 // vertical scroll bar
2750 RECT rc
= { 0, 0, extent
.x
, extent
.y
};
2751 ::FillRect(bitmapDC
, &rc
, reinterpret_cast<HBRUSH
>(COLOR_WINDOW
+1));
2752 // Paint the entire client area and vertical scrollbar
2753 ::SendMessage(lb
, WM_PRINT
, reinterpret_cast<WPARAM
>(bitmapDC
), PRF_CLIENT
|PRF_NONCLIENT
);
2754 ::BitBlt(hDC
, 0, 0, extent
.x
, extent
.y
, bitmapDC
, 0, 0, SRCCOPY
);
2755 // Select a stock brush to prevent warnings from BoundsChecker
2756 ::SelectObject(bitmapDC
, GetStockFont(WHITE_BRUSH
));
2757 SelectBitmap(bitmapDC
, hBitmapOld
);
2758 ::DeleteDC(bitmapDC
);
2759 ::DeleteObject(hBitmap
);
2762 LRESULT PASCAL
ListBoxX::ControlWndProc(HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
) {
2770 HDC hDC
= ::BeginPaint(hWnd
, &ps
);
2771 ListBoxX
*lbx
= reinterpret_cast<ListBoxX
*>(PointerFromWindow(::GetParent(hWnd
)));
2774 ::EndPaint(hWnd
, &ps
);
2778 case WM_MOUSEACTIVATE
:
2779 // This prevents the view activating when the scrollbar is clicked
2780 return MA_NOACTIVATE
;
2782 case WM_LBUTTONDOWN
: {
2783 // We must take control of selection to prevent the ListBox activating
2785 LRESULT lResult
= ::SendMessage(hWnd
, LB_ITEMFROMPOINT
, 0, lParam
);
2786 int item
= LOWORD(lResult
);
2787 if (HIWORD(lResult
) == 0 && item
>= 0) {
2788 ::SendMessage(hWnd
, LB_SETCURSEL
, item
, 0);
2796 case WM_LBUTTONDBLCLK
: {
2797 ListBoxX
*lbx
= reinterpret_cast<ListBoxX
*>(PointerFromWindow(::GetParent(hWnd
)));
2799 lbx
->OnDoubleClick();
2804 case WM_MBUTTONDOWN
:
2805 // disable the scroll wheel button click action
2809 WNDPROC prevWndProc
= reinterpret_cast<WNDPROC
>(GetWindowLongPtr(hWnd
, GWLP_USERDATA
));
2811 return ::CallWindowProc(prevWndProc
, hWnd
, uMsg
, wParam
, lParam
);
2813 return ::DefWindowProc(hWnd
, uMsg
, wParam
, lParam
);
2817 return ::DefWindowProc(hWnd
, uMsg
, wParam
, lParam
);
2820 LRESULT
ListBoxX::WndProc(HWND hWnd
, UINT iMessage
, WPARAM wParam
, LPARAM lParam
) {
2823 HINSTANCE hinstanceParent
= GetWindowInstance(reinterpret_cast<HWND
>(parent
->GetID()));
2824 // Note that LBS_NOINTEGRALHEIGHT is specified to fix cosmetic issue when resizing the list
2825 // but has useful side effect of speeding up list population significantly
2826 lb
= ::CreateWindowEx(
2827 0, TEXT("listbox"), TEXT(""),
2828 WS_CHILD
| WS_VSCROLL
| WS_VISIBLE
|
2829 LBS_OWNERDRAWFIXED
| LBS_NODATA
| LBS_NOINTEGRALHEIGHT
,
2831 reinterpret_cast<HMENU
>(ctrlID
),
2834 WNDPROC prevWndProc
= reinterpret_cast<WNDPROC
>(::SetWindowLongPtr(lb
, GWLP_WNDPROC
, reinterpret_cast<LONG_PTR
>(ControlWndProc
)));
2835 ::SetWindowLongPtr(lb
, GWLP_USERDATA
, reinterpret_cast<LONG_PTR
>(prevWndProc
));
2842 ::SetWindowPos(lb
, 0, 0,0, LOWORD(lParam
), HIWORD(lParam
), SWP_NOZORDER
|SWP_NOACTIVATE
|SWP_NOMOVE
);
2843 // Ensure the selection remains visible
2844 CentreItem(GetSelection());
2851 ::BeginPaint(hWnd
, &ps
);
2852 ::EndPaint(hWnd
, &ps
);
2857 // This is not actually needed now - the registered double click action is used
2858 // directly to action a choice from the list.
2859 ::SendMessage(reinterpret_cast<HWND
>(parent
->GetID()), iMessage
, wParam
, lParam
);
2862 case WM_MEASUREITEM
: {
2863 MEASUREITEMSTRUCT
*pMeasureItem
= reinterpret_cast<MEASUREITEMSTRUCT
*>(lParam
);
2864 pMeasureItem
->itemHeight
= static_cast<unsigned int>(ItemHeight());
2869 Draw(reinterpret_cast<DRAWITEMSTRUCT
*>(lParam
));
2874 ::SetWindowLong(hWnd
, 0, 0);
2875 return ::DefWindowProc(hWnd
, iMessage
, wParam
, lParam
);
2878 // To reduce flicker we can elide background erasure since this window is
2879 // completely covered by its child.
2882 case WM_GETMINMAXINFO
: {
2883 MINMAXINFO
*minMax
= reinterpret_cast<MINMAXINFO
*>(lParam
);
2884 minMax
->ptMaxTrackSize
= MaxTrackSize();
2885 minMax
->ptMinTrackSize
= MinTrackSize();
2889 case WM_MOUSEACTIVATE
:
2890 return MA_NOACTIVATE
;
2893 return NcHitTest(wParam
, lParam
);
2895 case WM_NCLBUTTONDOWN
:
2896 // We have to implement our own window resizing because the DefWindowProc
2897 // implementation insists on activating the resized window
2898 StartResize(wParam
);
2901 case WM_MOUSEMOVE
: {
2902 if (resizeHit
== 0) {
2903 return ::DefWindowProc(hWnd
, iMessage
, wParam
, lParam
);
2912 if (resizeHit
!= 0) {
2916 return ::DefWindowProc(hWnd
, iMessage
, wParam
, lParam
);
2919 wheelDelta
-= static_cast<short>(HIWORD(wParam
));
2920 if (abs(wheelDelta
) >= WHEEL_DELTA
) {
2921 int nRows
= GetVisibleRows();
2922 int linesToScroll
= 1;
2924 linesToScroll
= nRows
- 1;
2926 if (linesToScroll
> 3) {
2929 linesToScroll
*= (wheelDelta
/ WHEEL_DELTA
);
2930 LRESULT top
= ::SendMessage(lb
, LB_GETTOPINDEX
, 0, 0) + linesToScroll
;
2934 ::SendMessage(lb
, LB_SETTOPINDEX
, top
, 0);
2935 // update wheel delta residue
2936 if (wheelDelta
>= 0)
2937 wheelDelta
= wheelDelta
% WHEEL_DELTA
;
2939 wheelDelta
= - (-wheelDelta
% WHEEL_DELTA
);
2944 return ::DefWindowProc(hWnd
, iMessage
, wParam
, lParam
);
2950 LRESULT PASCAL
ListBoxX::StaticWndProc(
2951 HWND hWnd
, UINT iMessage
, WPARAM wParam
, LPARAM lParam
) {
2952 if (iMessage
== WM_CREATE
) {
2953 CREATESTRUCT
*pCreate
= reinterpret_cast<CREATESTRUCT
*>(lParam
);
2954 SetWindowPointer(hWnd
, pCreate
->lpCreateParams
);
2956 // Find C++ object associated with window.
2957 ListBoxX
*lbx
= reinterpret_cast<ListBoxX
*>(PointerFromWindow(hWnd
));
2959 return lbx
->WndProc(hWnd
, iMessage
, wParam
, lParam
);
2961 return ::DefWindowProc(hWnd
, iMessage
, wParam
, lParam
);
2965 static bool ListBoxX_Register() {
2966 WNDCLASSEX wndclassc
;
2967 wndclassc
.cbSize
= sizeof(wndclassc
);
2968 // We need CS_HREDRAW and CS_VREDRAW because of the ellipsis that might be drawn for
2969 // truncated items in the list and the appearance/disappearance of the vertical scroll bar.
2970 // The list repaint is double-buffered to avoid the flicker this would otherwise cause.
2971 wndclassc
.style
= CS_GLOBALCLASS
| CS_HREDRAW
| CS_VREDRAW
;
2972 wndclassc
.cbClsExtra
= 0;
2973 wndclassc
.cbWndExtra
= sizeof(ListBoxX
*);
2974 wndclassc
.hInstance
= hinstPlatformRes
;
2975 wndclassc
.hIcon
= NULL
;
2976 wndclassc
.hbrBackground
= NULL
;
2977 wndclassc
.lpszMenuName
= NULL
;
2978 wndclassc
.lpfnWndProc
= ListBoxX::StaticWndProc
;
2979 wndclassc
.hCursor
= ::LoadCursor(NULL
, IDC_ARROW
);
2980 wndclassc
.lpszClassName
= ListBoxX_ClassName
;
2981 wndclassc
.hIconSm
= 0;
2983 return ::RegisterClassEx(&wndclassc
) != 0;
2986 bool ListBoxX_Unregister() {
2987 return ::UnregisterClass(ListBoxX_ClassName
, hinstPlatformRes
) != 0;
2990 Menu::Menu() : mid(0) {
2993 void Menu::CreatePopUp() {
2995 mid
= ::CreatePopupMenu();
2998 void Menu::Destroy() {
3000 ::DestroyMenu(reinterpret_cast<HMENU
>(mid
));
3004 void Menu::Show(Point pt
, Window
&w
) {
3005 ::TrackPopupMenu(reinterpret_cast<HMENU
>(mid
),
3006 0, static_cast<int>(pt
.x
- 4), static_cast<int>(pt
.y
), 0,
3007 reinterpret_cast<HWND
>(w
.GetID()), NULL
);
3011 static bool initialisedET
= false;
3012 static bool usePerformanceCounter
= false;
3013 static LARGE_INTEGER frequency
;
3015 ElapsedTime::ElapsedTime() {
3016 if (!initialisedET
) {
3017 usePerformanceCounter
= ::QueryPerformanceFrequency(&frequency
) != 0;
3018 initialisedET
= true;
3020 if (usePerformanceCounter
) {
3021 LARGE_INTEGER timeVal
;
3022 ::QueryPerformanceCounter(&timeVal
);
3023 bigBit
= timeVal
.HighPart
;
3024 littleBit
= timeVal
.LowPart
;
3031 double ElapsedTime::Duration(bool reset
) {
3036 if (usePerformanceCounter
) {
3038 ::QueryPerformanceCounter(&lEnd
);
3039 endBigBit
= lEnd
.HighPart
;
3040 endLittleBit
= lEnd
.LowPart
;
3041 LARGE_INTEGER lBegin
;
3042 lBegin
.HighPart
= bigBit
;
3043 lBegin
.LowPart
= littleBit
;
3044 double elapsed
= static_cast<double>(lEnd
.QuadPart
- lBegin
.QuadPart
);
3045 result
= elapsed
/ static_cast<double>(frequency
.QuadPart
);
3047 endBigBit
= clock();
3049 double elapsed
= endBigBit
- bigBit
;
3050 result
= elapsed
/ CLOCKS_PER_SEC
;
3054 littleBit
= endLittleBit
;
3059 class DynamicLibraryImpl
: public DynamicLibrary
{
3063 explicit DynamicLibraryImpl(const char *modulePath
) {
3064 h
= ::LoadLibraryA(modulePath
);
3067 virtual ~DynamicLibraryImpl() {
3072 // Use GetProcAddress to get a pointer to the relevant function.
3073 virtual Function
FindFunction(const char *name
) {
3075 // C++ standard doesn't like casts betwen function pointers and void pointers so use a union
3080 fnConv
.fp
= ::GetProcAddress(h
, name
);
3087 virtual bool IsValid() {
3092 DynamicLibrary
*DynamicLibrary::Load(const char *modulePath
) {
3093 return static_cast<DynamicLibrary
*>(new DynamicLibraryImpl(modulePath
));
3096 ColourDesired
Platform::Chrome() {
3097 return ::GetSysColor(COLOR_3DFACE
);
3100 ColourDesired
Platform::ChromeHighlight() {
3101 return ::GetSysColor(COLOR_3DHIGHLIGHT
);
3104 const char *Platform::DefaultFont() {
3108 int Platform::DefaultFontSize() {
3112 unsigned int Platform::DoubleClickTime() {
3113 return ::GetDoubleClickTime();
3116 bool Platform::MouseButtonBounce() {
3120 void Platform::DebugDisplay(const char *s
) {
3121 ::OutputDebugStringA(s
);
3124 bool Platform::IsKeyDown(int key
) {
3125 return (::GetKeyState(key
) & 0x80000000) != 0;
3128 long Platform::SendScintilla(WindowID w
, unsigned int msg
, unsigned long wParam
, long lParam
) {
3129 // This should never be called - its here to satisfy an old interface
3130 return static_cast<long>(::SendMessage(reinterpret_cast<HWND
>(w
), msg
, wParam
, lParam
));
3133 long Platform::SendScintillaPointer(WindowID w
, unsigned int msg
, unsigned long wParam
, void *lParam
) {
3134 // This should never be called - its here to satisfy an old interface
3135 return static_cast<long>(::SendMessage(reinterpret_cast<HWND
>(w
), msg
, wParam
,
3136 reinterpret_cast<LPARAM
>(lParam
)));
3139 bool Platform::IsDBCSLeadByte(int codePage
, char ch
) {
3140 // Byte ranges found in Wikipedia articles with relevant search strings in each case
3141 unsigned char uch
= static_cast<unsigned char>(ch
);
3145 return ((uch
>= 0x81) && (uch
<= 0x9F)) ||
3146 ((uch
>= 0xE0) && (uch
<= 0xEF));
3149 return (uch
>= 0x81) && (uch
<= 0xFE);
3151 // Korean Wansung KS C-5601-1987
3152 return (uch
>= 0x81) && (uch
<= 0xFE);
3155 return (uch
>= 0x81) && (uch
<= 0xFE);
3157 // Korean Johab KS C-5601-1992
3159 ((uch
>= 0x84) && (uch
<= 0xD3)) ||
3160 ((uch
>= 0xD8) && (uch
<= 0xDE)) ||
3161 ((uch
>= 0xE0) && (uch
<= 0xF9));
3166 int Platform::DBCSCharLength(int codePage
, const char *s
) {
3167 if (codePage
== 932 || codePage
== 936 || codePage
== 949 ||
3168 codePage
== 950 || codePage
== 1361) {
3169 return Platform::IsDBCSLeadByte(codePage
, s
[0]) ? 2 : 1;
3175 int Platform::DBCSCharMaxLength() {
3179 // These are utility functions not really tied to a platform
3181 int Platform::Minimum(int a
, int b
) {
3188 int Platform::Maximum(int a
, int b
) {
3198 void Platform::DebugPrintf(const char *format
, ...) {
3201 va_start(pArguments
, format
);
3202 vsprintf(buffer
,format
,pArguments
);
3204 Platform::DebugDisplay(buffer
);
3207 void Platform::DebugPrintf(const char *, ...) {
3211 static bool assertionPopUps
= true;
3213 bool Platform::ShowAssertionPopUps(bool assertionPopUps_
) {
3214 bool ret
= assertionPopUps
;
3215 assertionPopUps
= assertionPopUps_
;
3219 void Platform::Assert(const char *c
, const char *file
, int line
) {
3221 sprintf(buffer
, "Assertion [%s] failed at %s %d%s", c
, file
, line
, assertionPopUps
? "" : "\r\n");
3222 if (assertionPopUps
) {
3223 int idButton
= ::MessageBoxA(0, buffer
, "Assertion failure",
3224 MB_ABORTRETRYIGNORE
|MB_ICONHAND
|MB_SETFOREGROUND
|MB_TASKMODAL
);
3225 if (idButton
== IDRETRY
) {
3227 } else if (idButton
== IDIGNORE
) {
3233 Platform::DebugDisplay(buffer
);
3239 int Platform::Clamp(int val
, int minVal
, int maxVal
) {
3248 // GetVersionEx has been deprecated fro Windows 8.1 but called here to determine if Windows 9x.
3249 // Too dangerous to find alternate check.
3250 #pragma warning(disable: 4996)
3253 void Platform_Initialise(void *hInstance
) {
3254 OSVERSIONINFO osv
= {sizeof(OSVERSIONINFO
),0,0,0,0,TEXT("")};
3255 ::GetVersionEx(&osv
);
3256 onNT
= osv
.dwPlatformId
== VER_PLATFORM_WIN32_NT
;
3257 ::InitializeCriticalSection(&crPlatformLock
);
3258 hinstPlatformRes
= reinterpret_cast<HINSTANCE
>(hInstance
);
3259 // This may be called from DllMain, in which case the call to LoadLibrary
3260 // is bad because it can upset the DLL load order.
3262 hDLLImage
= ::LoadLibrary(TEXT("Msimg32"));
3265 AlphaBlendFn
= (AlphaBlendSig
)::GetProcAddress(hDLLImage
, "AlphaBlend");
3268 hDLLUser32
= ::LoadLibrary(TEXT("User32"));
3271 MonitorFromPointFn
= (MonitorFromPointSig
)::GetProcAddress(hDLLUser32
, "MonitorFromPoint");
3272 MonitorFromRectFn
= (MonitorFromRectSig
)::GetProcAddress(hDLLUser32
, "MonitorFromRect");
3273 GetMonitorInfoFn
= (GetMonitorInfoSig
)::GetProcAddress(hDLLUser32
, "GetMonitorInfoA");
3276 ListBoxX_Register();
3280 #pragma warning(default: 4996)
3283 void Platform_Finalise(bool fromDllMain
) {
3284 #if defined(USE_D2D)
3286 if (defaultRenderingParams
) {
3287 defaultRenderingParams
->Release();
3288 defaultRenderingParams
= 0;
3290 if (customClearTypeRenderingParams
) {
3291 customClearTypeRenderingParams
->Release();
3292 customClearTypeRenderingParams
= 0;
3294 if (pIDWriteFactory
) {
3295 pIDWriteFactory
->Release();
3296 pIDWriteFactory
= 0;
3299 pD2DFactory
->Release();
3303 FreeLibrary(hDLLDWrite
);
3307 FreeLibrary(hDLLD2D
);
3312 if (reverseArrowCursor
!= NULL
)
3313 ::DestroyCursor(reverseArrowCursor
);
3314 ListBoxX_Unregister();
3315 ::DeleteCriticalSection(&crPlatformLock
);
3317 FreeLibrary(hDLLUser32
);
3321 FreeLibrary(hDLLImage
);
3326 #ifdef SCI_NAMESPACE