Compare commits

...

5 Commits

Author SHA1 Message Date
Bram Moolenaar
a3571ebef5 patch 8.0.1346: crash when passing 50 char string to balloon_split()
Problem:    Crash when passing 50 char string to balloon_split().
Solution:   Fix off-by-one error.
2017-11-26 16:53:16 +01:00
Bram Moolenaar
c41838aa01 patch 8.0.1345: race condition between stat() and open() for viminfo
Problem:    Race condition between stat() and open() for the viminfo temp
            file. (Simon Ruderich)
Solution:   use open() with O_EXCL to atomically check if the file exists.
            Don't try using a temp file, renaming it will fail anyway.
2017-11-26 16:50:41 +01:00
Bram Moolenaar
2877d334ad patch 8.0.1344: using 'imactivatefunc' in the GUI does not work
Problem:    Using 'imactivatefunc' in the GUI does not work.
Solution:   Do not use 'imactivatefunc' and 'imstatusfunc' in the GUI.
2017-11-26 14:56:16 +01:00
Bram Moolenaar
d7ccc4d81d patch 8.0.1343: MS-Windows: does not show colored emojis
Problem:    MS-Windows: does not show colored emojis.
Solution:   Implement colored emojis. Improve drawing speed. Make 'taamode'
            work. (Taro Muraoka, Yasuhiro Matsumoto, Ken Takata, close #2375)
2017-11-26 14:29:32 +01:00
Bram Moolenaar
fb1db0e355 patch 8.0.1342: cannot build with Motif and multi-byte
Problem:    Cannot build with Motif and multi-byte. (Mohamed Boughaba)
Solution:   Use the right input method status flag. (closes #2374)
2017-11-25 21:07:46 +01:00
12 changed files with 729 additions and 432 deletions

View File

@@ -16,6 +16,7 @@ matrix:
before_build: before_build:
- '"C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.cmd" /x64 /release' - '"C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.cmd" /x64 /release'
- 'set INCLUDE=%INCLUDE%C:\Program Files (x86)\Windows Kits\8.1\Include\um'
build_script: build_script:
- src/appveyor.bat - src/appveyor.bat

View File

@@ -4258,8 +4258,8 @@ A jump table for the options with a short description can be found at |Q_op|.
{not in Vi} {not in Vi}
{only available when compiled with |+mbyte|} {only available when compiled with |+mbyte|}
This option specifies a function that will be called to This option specifies a function that will be called to
activate/inactivate Input Method. activate or deactivate the Input Method.
Does not work in the MS-Windows GUI version. It is not used in the GUI.
Example: > Example: >
function ImActivateFunc(active) function ImActivateFunc(active)
@@ -4375,7 +4375,7 @@ A jump table for the options with a short description can be found at |Q_op|.
{only available when compiled with |+mbyte|} {only available when compiled with |+mbyte|}
This option specifies a function that is called to obtain the status This option specifies a function that is called to obtain the status
of Input Method. It must return a positive number when IME is active. of Input Method. It must return a positive number when IME is active.
Does not work in the MS-Windows GUI version. It is not used in the GUI.
Example: > Example: >
function ImStatusFunc() function ImStatusFunc()
@@ -6154,11 +6154,34 @@ A jump table for the options with a short description can be found at |Q_op|.
Example: > Example: >
set encoding=utf-8 set encoding=utf-8
set gfn=Ricty_Diminished:h12:cSHIFTJIS set gfn=Ricty_Diminished:h12
set rop=type:directx set rop=type:directx
< <
If select a raster font (Courier, Terminal or FixedSys) to If select a raster font (Courier, Terminal or FixedSys which
'guifont', it fallbacks to be drawn by GDI automatically. have ".fon" extension in file name) to 'guifont', it will be
drawn by GDI as a fallback. This fallback will cause
significant slow down on drawing.
NOTE: It is known that some fonts and options combination
causes trouble on drawing glyphs.
- 'rendmode:5' and 'renmode:6' will not work with some
special made fonts (True-Type fonts which includes only
bitmap glyphs).
- 'taamode:3' will not work with some vector fonts.
NOTE: With this option, you can display colored emoji
(emoticon) in Windows 8.1 or later. To display colored emoji,
there are some conditions which you should notice.
- If your font includes non-colored emoji already, it will
be used.
- If your font doesn't have emoji, the system chooses an
alternative symbol font. On Windows 10, "Segoe UI Emoji"
will be used.
- When this alternative font didn't have fixed width glyph,
emoji might be rendered beyond the bounding box of drawing
cell.
Other render types are currently not supported. Other render types are currently not supported.

View File

@@ -1825,7 +1825,6 @@ write_viminfo(char_u *file, int forceit)
FILE *fp_out = NULL; /* output viminfo file */ FILE *fp_out = NULL; /* output viminfo file */
char_u *tempname = NULL; /* name of temp viminfo file */ char_u *tempname = NULL; /* name of temp viminfo file */
stat_T st_new; /* mch_stat() of potential new file */ stat_T st_new; /* mch_stat() of potential new file */
char_u *wp;
#if defined(UNIX) || defined(VMS) #if defined(UNIX) || defined(VMS)
mode_t umask_save; mode_t umask_save;
#endif #endif
@@ -1847,27 +1846,29 @@ write_viminfo(char_u *file, int forceit)
fp_in = mch_fopen((char *)fname, READBIN); fp_in = mch_fopen((char *)fname, READBIN);
if (fp_in == NULL) if (fp_in == NULL)
{ {
int fd;
/* if it does exist, but we can't read it, don't try writing */ /* if it does exist, but we can't read it, don't try writing */
if (mch_stat((char *)fname, &st_new) == 0) if (mch_stat((char *)fname, &st_new) == 0)
goto end; goto end;
#if defined(UNIX) || defined(VMS)
/* /* Create the new .viminfo non-accessible for others, because it may
* For Unix we create the .viminfo non-accessible for others, * contain text from non-accessible documents. It is up to the user to
* because it may contain text from non-accessible documents. * widen access (e.g. to a group). This may also fail if there is a
*/ * race condition, then just give up. */
umask_save = umask(077); fd = mch_open((char *)fname,
#endif O_CREAT|O_EXTRA|O_EXCL|O_WRONLY|O_NOFOLLOW, 0600);
fp_out = mch_fopen((char *)fname, WRITEBIN); if (fd < 0)
#if defined(UNIX) || defined(VMS) goto end;
(void)umask(umask_save); fp_out = fdopen(fd, WRITEBIN);
#endif
} }
else else
{ {
/* /*
* There is an existing viminfo file. Create a temporary file to * There is an existing viminfo file. Create a temporary file to
* write the new viminfo into, in the same directory as the * write the new viminfo into, in the same directory as the
* existing viminfo file, which will be renamed later. * existing viminfo file, which will be renamed once all writing is
* successful.
*/ */
#ifdef UNIX #ifdef UNIX
/* /*
@@ -1901,12 +1902,18 @@ write_viminfo(char_u *file, int forceit)
#endif #endif
/* /*
* Make tempname. * Make tempname, find one that does not exist yet.
* Beware of a race condition: If someone logs out and all Vim
* instances exit at the same time a temp file might be created between
* stat() and open(). Use mch_open() with O_EXCL to avoid that.
* May try twice: Once normal and once with shortname set, just in * May try twice: Once normal and once with shortname set, just in
* case somebody puts his viminfo file in an 8.3 filesystem. * case somebody puts his viminfo file in an 8.3 filesystem.
*/ */
for (;;) for (;;)
{ {
int next_char = 'z';
char_u *wp;
tempname = buf_modname( tempname = buf_modname(
#ifdef UNIX #ifdef UNIX
shortname, shortname,
@@ -1924,127 +1931,129 @@ write_viminfo(char_u *file, int forceit)
break; break;
/* /*
* Check if tempfile already exists. Never overwrite an * Try a series of names. Change one character, just before
* existing file! * the extension. This should also work for an 8.3
* file name, when after adding the extension it still is
* the same file as the original.
*/ */
if (mch_stat((char *)tempname, &st_new) == 0) wp = tempname + STRLEN(tempname) - 5;
if (wp < gettail(tempname)) /* empty file name? */
wp = gettail(tempname);
for (;;)
{ {
/*
* Check if tempfile already exists. Never overwrite an
* existing file!
*/
if (mch_stat((char *)tempname, &st_new) == 0)
{
#ifdef UNIX #ifdef UNIX
/*
* Check if tempfile is same as original file. May happen
* when modname() gave the same file back. E.g. silly
* link, or file name-length reached. Try again with
* shortname set.
*/
if (!shortname && st_new.st_dev == st_old.st_dev
&& st_new.st_ino == st_old.st_ino)
{
vim_free(tempname);
tempname = NULL;
shortname = TRUE;
continue;
}
#endif
/*
* Try another name. Change one character, just before
* the extension. This should also work for an 8.3
* file name, when after adding the extension it still is
* the same file as the original.
*/
wp = tempname + STRLEN(tempname) - 5;
if (wp < gettail(tempname)) /* empty file name? */
wp = gettail(tempname);
for (*wp = 'z'; mch_stat((char *)tempname, &st_new) == 0;
--*wp)
{
/* /*
* They all exist? Must be something wrong! Don't * Check if tempfile is same as original file. May happen
* write the viminfo file then. * when modname() gave the same file back. E.g. silly
* link, or file name-length reached. Try again with
* shortname set.
*/ */
if (*wp == 'a') if (!shortname && st_new.st_dev == st_old.st_dev
&& st_new.st_ino == st_old.st_ino)
{ {
EMSG2(_("E929: Too many viminfo temp files, like %s!"),
tempname);
vim_free(tempname); vim_free(tempname);
tempname = NULL; tempname = NULL;
shortname = TRUE;
break; break;
} }
#endif
} }
else
{
/* Try creating the file exclusively. This may fail if
* another Vim tries to do it at the same time. */
#ifdef VMS
/* fdopen() fails for some reason */
umask_save = umask(077);
fp_out = mch_fopen((char *)tempname, WRITEBIN);
(void)umask(umask_save);
#else
int fd;
/* Use mch_open() to be able to use O_NOFOLLOW and set file
* protection:
* Unix: same as original file, but strip s-bit. Reset
* umask to avoid it getting in the way.
* Others: r&w for user only. */
# ifdef UNIX
umask_save = umask(0);
fd = mch_open((char *)tempname,
O_CREAT|O_EXTRA|O_EXCL|O_WRONLY|O_NOFOLLOW,
(int)((st_old.st_mode & 0777) | 0600));
(void)umask(umask_save);
# else
fd = mch_open((char *)tempname,
O_CREAT|O_EXTRA|O_EXCL|O_WRONLY|O_NOFOLLOW, 0600);
# endif
if (fd < 0)
{
fp_out = NULL;
# ifdef EEXIST
/* Avoid trying lots of names while the problem is lack
* of premission, only retry if the file already
* exists. */
if (errno != EEXIST)
break;
# endif
}
else
fp_out = fdopen(fd, WRITEBIN);
#endif /* VMS */
if (fp_out != NULL)
break;
}
/* Assume file exists, try again with another name. */
if (next_char == 'a' - 1)
{
/* They all exist? Must be something wrong! Don't write
* the viminfo file then. */
EMSG2(_("E929: Too many viminfo temp files, like %s!"),
tempname);
break;
}
*wp = next_char;
--next_char;
} }
break;
if (tempname != NULL)
break;
/* continue if shortname was set */
} }
if (tempname != NULL)
{
#ifdef VMS
/* fdopen() fails for some reason */
umask_save = umask(077);
fp_out = mch_fopen((char *)tempname, WRITEBIN);
(void)umask(umask_save);
#else
int fd;
/* Use mch_open() to be able to use O_NOFOLLOW and set file
* protection:
* Unix: same as original file, but strip s-bit. Reset umask to
* avoid it getting in the way.
* Others: r&w for user only. */
# ifdef UNIX
umask_save = umask(0);
fd = mch_open((char *)tempname,
O_CREAT|O_EXTRA|O_EXCL|O_WRONLY|O_NOFOLLOW,
(int)((st_old.st_mode & 0777) | 0600));
(void)umask(umask_save);
# else
fd = mch_open((char *)tempname,
O_CREAT|O_EXTRA|O_EXCL|O_WRONLY|O_NOFOLLOW, 0600);
# endif
if (fd < 0)
fp_out = NULL;
else
fp_out = fdopen(fd, WRITEBIN);
#endif /* VMS */
/*
* If we can't create in the same directory, try creating a
* "normal" temp file. This is just an attempt, renaming the temp
* file might fail as well.
*/
if (fp_out == NULL)
{
vim_free(tempname);
if ((tempname = vim_tempname('o', TRUE)) != NULL)
fp_out = mch_fopen((char *)tempname, WRITEBIN);
}
#if defined(UNIX) && defined(HAVE_FCHOWN) #if defined(UNIX) && defined(HAVE_FCHOWN)
if (tempname != NULL && fp_out != NULL)
{
stat_T tmp_st;
/* /*
* Make sure the original owner can read/write the tempfile and * Make sure the original owner can read/write the tempfile and
* otherwise preserve permissions, making sure the group matches. * otherwise preserve permissions, making sure the group matches.
*/ */
if (fp_out != NULL) if (mch_stat((char *)tempname, &tmp_st) >= 0)
{ {
stat_T tmp_st; if (st_old.st_uid != tmp_st.st_uid)
/* Changing the owner might fail, in which case the
if (mch_stat((char *)tempname, &tmp_st) >= 0) * file will now owned by the current user, oh well. */
{ ignored = fchown(fileno(fp_out), st_old.st_uid, -1);
if (st_old.st_uid != tmp_st.st_uid) if (st_old.st_gid != tmp_st.st_gid
/* Changing the owner might fail, in which case the && fchown(fileno(fp_out), -1, st_old.st_gid) == -1)
* file will now owned by the current user, oh well. */ /* can't set the group to what it should be, remove
ignored = fchown(fileno(fp_out), st_old.st_uid, -1); * group permissions */
if (st_old.st_gid != tmp_st.st_gid
&& fchown(fileno(fp_out), -1, st_old.st_gid) == -1)
/* can't set the group to what it should be, remove
* group permissions */
(void)mch_setperm(tempname, 0600);
}
else
/* can't stat the file, set conservative permissions */
(void)mch_setperm(tempname, 0600); (void)mch_setperm(tempname, 0600);
} }
#endif else
/* can't stat the file, set conservative permissions */
(void)mch_setperm(tempname, 0600);
} }
} }
#endif
/* /*
* Check if the new viminfo file can be written to. * Check if the new viminfo file can be written to.

View File

@@ -4,6 +4,7 @@
* *
* Contributors: * Contributors:
* - Ken Takata * - Ken Takata
* - Yasuhiro Matsumoto
* *
* Copyright (C) 2013 MURAOKA Taro <koron.kaoriya@gmail.com> * Copyright (C) 2013 MURAOKA Taro <koron.kaoriya@gmail.com>
* THIS FILE IS DISTRIBUTED UNDER THE VIM LICENSE. * THIS FILE IS DISTRIBUTED UNDER THE VIM LICENSE.
@@ -23,7 +24,21 @@
#include <math.h> #include <math.h>
#include <d2d1.h> #include <d2d1.h>
#include <d2d1helper.h> #include <d2d1helper.h>
#include <dwrite.h>
// Disable these macros to compile with old VC and newer SDK (V8.1 or later).
#if defined(_MSC_VER) && (_MSC_VER < 1700)
# define _COM_Outptr_ __out
# define _In_reads_(s)
# define _In_reads_opt_(s)
# define _Maybenull_
# define _Out_writes_(s)
# define _Out_writes_opt_(s)
# define _Out_writes_to_(x, y)
# define _Out_writes_to_opt_(x, y)
# define _Outptr_
#endif
#include <dwrite_2.h>
#include "gui_dwrite.h" #include "gui_dwrite.h"
@@ -79,16 +94,6 @@ template <class T> inline void SafeRelease(T **ppT)
} }
} }
struct GdiTextRendererContext
{
// const fields.
COLORREF color;
FLOAT cellWidth;
// working fields.
FLOAT offsetX;
};
static DWRITE_PIXEL_GEOMETRY static DWRITE_PIXEL_GEOMETRY
ToPixelGeometry(int value) ToPixelGeometry(int value)
{ {
@@ -184,17 +189,151 @@ ToInt(DWRITE_RENDERING_MODE value)
} }
} }
class FontCache {
public:
struct Item {
HFONT hFont;
IDWriteTextFormat* pTextFormat;
DWRITE_FONT_WEIGHT fontWeight;
DWRITE_FONT_STYLE fontStyle;
Item() : hFont(NULL), pTextFormat(NULL) {}
};
private:
int mSize;
Item *mItems;
public:
FontCache(int size = 2) :
mSize(size),
mItems(new Item[size])
{
}
~FontCache()
{
for (int i = 0; i < mSize; ++i)
SafeRelease(&mItems[i].pTextFormat);
delete[] mItems;
}
bool get(HFONT hFont, Item &item)
{
int n = find(hFont);
if (n < 0)
return false;
item = mItems[n];
slide(n);
return true;
}
void put(const Item& item)
{
int n = find(item.hFont);
if (n < 0)
n = mSize - 1;
if (mItems[n].pTextFormat != item.pTextFormat)
{
SafeRelease(&mItems[n].pTextFormat);
item.pTextFormat->AddRef();
}
mItems[n] = item;
slide(n);
}
private:
int find(HFONT hFont)
{
for (int i = 0; i < mSize; ++i)
{
if (mItems[i].hFont == hFont)
return i;
}
return -1;
}
void slide(int nextTop)
{
if (nextTop == 0)
return;
Item tmp = mItems[nextTop];
for (int i = nextTop - 1; i >= 0; --i)
mItems[i + 1] = mItems[i];
mItems[0] = tmp;
}
};
struct DWriteContext {
HDC mHDC;
bool mDrawing;
bool mFallbackDC;
ID2D1Factory *mD2D1Factory;
ID2D1DCRenderTarget *mRT;
ID2D1SolidColorBrush *mBrush;
IDWriteFactory *mDWriteFactory;
IDWriteFactory2 *mDWriteFactory2;
IDWriteGdiInterop *mGdiInterop;
IDWriteRenderingParams *mRenderingParams;
FontCache mFontCache;
IDWriteTextFormat *mTextFormat;
DWRITE_FONT_WEIGHT mFontWeight;
DWRITE_FONT_STYLE mFontStyle;
D2D1_TEXT_ANTIALIAS_MODE mTextAntialiasMode;
// METHODS
DWriteContext();
virtual ~DWriteContext();
HRESULT CreateTextFormatFromLOGFONT(const LOGFONTW &logFont,
IDWriteTextFormat **ppTextFormat);
HRESULT SetFontByLOGFONT(const LOGFONTW &logFont);
void SetFont(HFONT hFont);
void BindDC(HDC hdc, RECT *rect);
void AssureDrawing();
ID2D1Brush* SolidBrush(COLORREF color);
void DrawText(const WCHAR* text, int len,
int x, int y, int w, int h, int cellWidth, COLORREF color,
UINT fuOptions, CONST RECT *lprc, CONST INT * lpDx);
void FillRect(RECT *rc, COLORREF color);
void Flush();
void SetRenderingParams(
const DWriteRenderingParams *params);
DWriteRenderingParams *GetRenderingParams(
DWriteRenderingParams *params);
};
class AdjustedGlyphRun : public DWRITE_GLYPH_RUN class AdjustedGlyphRun : public DWRITE_GLYPH_RUN
{ {
private: private:
FLOAT &mAccum;
FLOAT mDelta; FLOAT mDelta;
FLOAT *mAdjustedAdvances; FLOAT *mAdjustedAdvances;
public: public:
AdjustedGlyphRun( AdjustedGlyphRun(
const DWRITE_GLYPH_RUN *glyphRun, const DWRITE_GLYPH_RUN *glyphRun,
FLOAT cellWidth) : FLOAT cellWidth,
FLOAT &accum) :
DWRITE_GLYPH_RUN(*glyphRun), DWRITE_GLYPH_RUN(*glyphRun),
mAccum(accum),
mDelta(0.0f), mDelta(0.0f),
mAdjustedAdvances(new FLOAT[glyphRun->glyphCount]) mAdjustedAdvances(new FLOAT[glyphRun->glyphCount])
{ {
@@ -209,45 +348,44 @@ public:
glyphAdvances = mAdjustedAdvances; glyphAdvances = mAdjustedAdvances;
} }
~AdjustedGlyphRun(void) ~AdjustedGlyphRun()
{ {
mAccum += mDelta;
delete[] mAdjustedAdvances; delete[] mAdjustedAdvances;
} }
FLOAT getDelta(void) const
{
return mDelta;
}
static FLOAT adjustToCell(FLOAT value, FLOAT cellWidth) static FLOAT adjustToCell(FLOAT value, FLOAT cellWidth)
{ {
int cellCount = (int)floor(value / cellWidth + 0.5f); int cellCount = int(floor(value / cellWidth + 0.5f));
if (cellCount < 1) if (cellCount < 1)
cellCount = 1; cellCount = 1;
return cellCount * cellWidth; return cellCount * cellWidth;
} }
}; };
class GdiTextRenderer FINAL : public IDWriteTextRenderer struct TextRendererContext {
// const fields.
COLORREF color;
FLOAT cellWidth;
// working fields.
FLOAT offsetX;
};
class TextRenderer FINAL : public IDWriteTextRenderer
{ {
public: public:
GdiTextRenderer( TextRenderer(
IDWriteBitmapRenderTarget* bitmapRenderTarget, DWriteContext* pDWC) :
IDWriteRenderingParams* renderingParams) :
cRefCount_(0), cRefCount_(0),
pRenderTarget_(bitmapRenderTarget), pDWC_(pDWC)
pRenderingParams_(renderingParams)
{ {
pRenderTarget_->AddRef();
pRenderingParams_->AddRef();
AddRef(); AddRef();
} }
// add "virtual" to avoid a compiler warning // add "virtual" to avoid a compiler warning
virtual ~GdiTextRenderer() virtual ~TextRenderer()
{ {
SafeRelease(&pRenderTarget_);
SafeRelease(&pRenderingParams_);
} }
IFACEMETHOD(IsPixelSnappingDisabled)( IFACEMETHOD(IsPixelSnappingDisabled)(
@@ -263,7 +401,8 @@ public:
__out DWRITE_MATRIX* transform) __out DWRITE_MATRIX* transform)
{ {
// forward the render target's transform // forward the render target's transform
pRenderTarget_->GetCurrentTransform(transform); pDWC_->mRT->GetTransform(
reinterpret_cast<D2D1_MATRIX_3X2_F*>(transform));
return S_OK; return S_OK;
} }
@@ -271,43 +410,12 @@ public:
__maybenull void* clientDrawingContext, __maybenull void* clientDrawingContext,
__out FLOAT* pixelsPerDip) __out FLOAT* pixelsPerDip)
{ {
*pixelsPerDip = pRenderTarget_->GetPixelsPerDip(); float dpiX, unused;
pDWC_->mRT->GetDpi(&dpiX, &unused);
*pixelsPerDip = dpiX / 96.0f;
return S_OK; return S_OK;
} }
IFACEMETHOD(DrawGlyphRun)(
__maybenull void* clientDrawingContext,
FLOAT baselineOriginX,
FLOAT baselineOriginY,
DWRITE_MEASURING_MODE measuringMode,
__in DWRITE_GLYPH_RUN const* glyphRun,
__in DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription,
IUnknown* clientDrawingEffect)
{
HRESULT hr = S_OK;
GdiTextRendererContext *context =
reinterpret_cast<GdiTextRendererContext*>(clientDrawingContext);
AdjustedGlyphRun adjustedGlyphRun(glyphRun, context->cellWidth);
// Pass on the drawing call to the render target to do the real work.
RECT dirtyRect = {0};
hr = pRenderTarget_->DrawGlyphRun(
baselineOriginX + context->offsetX,
baselineOriginY,
measuringMode,
&adjustedGlyphRun,
pRenderingParams_,
context->color,
&dirtyRect);
context->offsetX += adjustedGlyphRun.getDelta();
return hr;
}
IFACEMETHOD(DrawUnderline)( IFACEMETHOD(DrawUnderline)(
__maybenull void* clientDrawingContext, __maybenull void* clientDrawingContext,
FLOAT baselineOriginX, FLOAT baselineOriginX,
@@ -340,6 +448,69 @@ public:
return E_NOTIMPL; return E_NOTIMPL;
} }
IFACEMETHOD(DrawGlyphRun)(
__maybenull void* clientDrawingContext,
FLOAT baselineOriginX,
FLOAT baselineOriginY,
DWRITE_MEASURING_MODE measuringMode,
__in DWRITE_GLYPH_RUN const* glyphRun,
__in DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription,
IUnknown* clientDrawingEffect)
{
TextRendererContext *context =
reinterpret_cast<TextRendererContext*>(clientDrawingContext);
AdjustedGlyphRun adjustedGlyphRun(glyphRun, context->cellWidth,
context->offsetX);
if (pDWC_->mDWriteFactory2 != NULL)
{
IDWriteColorGlyphRunEnumerator *enumerator = NULL;
HRESULT hr = pDWC_->mDWriteFactory2->TranslateColorGlyphRun(
baselineOriginX + context->offsetX,
baselineOriginY,
&adjustedGlyphRun,
NULL,
DWRITE_MEASURING_MODE_GDI_NATURAL,
NULL,
0,
&enumerator);
if (SUCCEEDED(hr))
{
// Draw by IDWriteFactory2 for color emoji
BOOL hasRun = TRUE;
enumerator->MoveNext(&hasRun);
while (hasRun)
{
const DWRITE_COLOR_GLYPH_RUN* colorGlyphRun;
enumerator->GetCurrentRun(&colorGlyphRun);
pDWC_->mBrush->SetColor(colorGlyphRun->runColor);
pDWC_->mRT->DrawGlyphRun(
D2D1::Point2F(
colorGlyphRun->baselineOriginX,
colorGlyphRun->baselineOriginY),
&colorGlyphRun->glyphRun,
pDWC_->mBrush,
DWRITE_MEASURING_MODE_NATURAL);
enumerator->MoveNext(&hasRun);
}
SafeRelease(&enumerator);
return S_OK;
}
}
// Draw by IDWriteFactory (without color emoji)
pDWC_->mRT->DrawGlyphRun(
D2D1::Point2F(
baselineOriginX + context->offsetX,
baselineOriginY),
&adjustedGlyphRun,
pDWC_->SolidBrush(context->color),
DWRITE_MEASURING_MODE_NATURAL);
return S_OK;
}
public: public:
IFACEMETHOD_(unsigned long, AddRef) () IFACEMETHOD_(unsigned long, AddRef) ()
{ {
@@ -385,80 +556,28 @@ public:
private: private:
long cRefCount_; long cRefCount_;
IDWriteBitmapRenderTarget* pRenderTarget_; DWriteContext* pDWC_;
IDWriteRenderingParams* pRenderingParams_;
};
struct DWriteContext {
FLOAT mDpiScaleX;
FLOAT mDpiScaleY;
bool mDrawing;
ID2D1Factory *mD2D1Factory;
ID2D1DCRenderTarget *mRT;
ID2D1SolidColorBrush *mBrush;
IDWriteFactory *mDWriteFactory;
IDWriteGdiInterop *mGdiInterop;
IDWriteRenderingParams *mRenderingParams;
IDWriteTextFormat *mTextFormat;
HFONT mLastHFont;
DWRITE_FONT_WEIGHT mFontWeight;
DWRITE_FONT_STYLE mFontStyle;
D2D1_TEXT_ANTIALIAS_MODE mTextAntialiasMode;
// METHODS
DWriteContext();
virtual ~DWriteContext();
HRESULT SetLOGFONT(const LOGFONTW &logFont, float fontSize);
void SetFont(HFONT hFont);
void SetFont(const LOGFONTW &logFont);
void DrawText(HDC hdc, const WCHAR* text, int len,
int x, int y, int w, int h, int cellWidth, COLORREF color);
float PixelsToDipsX(int x);
float PixelsToDipsY(int y);
void SetRenderingParams(
const DWriteRenderingParams *params);
DWriteRenderingParams *GetRenderingParams(
DWriteRenderingParams *params);
}; };
DWriteContext::DWriteContext() : DWriteContext::DWriteContext() :
mDpiScaleX(1.f), mHDC(NULL),
mDpiScaleY(1.f),
mDrawing(false), mDrawing(false),
mFallbackDC(false),
mD2D1Factory(NULL), mD2D1Factory(NULL),
mRT(NULL), mRT(NULL),
mBrush(NULL), mBrush(NULL),
mDWriteFactory(NULL), mDWriteFactory(NULL),
mDWriteFactory2(NULL),
mGdiInterop(NULL), mGdiInterop(NULL),
mRenderingParams(NULL), mRenderingParams(NULL),
mFontCache(8),
mTextFormat(NULL), mTextFormat(NULL),
mLastHFont(NULL),
mFontWeight(DWRITE_FONT_WEIGHT_NORMAL), mFontWeight(DWRITE_FONT_WEIGHT_NORMAL),
mFontStyle(DWRITE_FONT_STYLE_NORMAL), mFontStyle(DWRITE_FONT_STYLE_NORMAL),
mTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_DEFAULT) mTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_DEFAULT)
{ {
HRESULT hr; HRESULT hr;
HDC screen = ::GetDC(0);
mDpiScaleX = ::GetDeviceCaps(screen, LOGPIXELSX) / 96.0f;
mDpiScaleY = ::GetDeviceCaps(screen, LOGPIXELSY) / 96.0f;
::ReleaseDC(0, screen);
hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED,
__uuidof(ID2D1Factory), NULL, __uuidof(ID2D1Factory), NULL,
reinterpret_cast<void**>(&mD2D1Factory)); reinterpret_cast<void**>(&mD2D1Factory));
@@ -495,6 +614,15 @@ DWriteContext::DWriteContext() :
mDWriteFactory); mDWriteFactory);
} }
if (SUCCEEDED(hr))
{
DWriteCreateFactory(
DWRITE_FACTORY_TYPE_SHARED,
__uuidof(IDWriteFactory2),
reinterpret_cast<IUnknown**>(&mDWriteFactory2));
_RPT1(_CRT_WARN, "IDWriteFactory2: %s\n", SUCCEEDED(hr) ? "available" : "not available");
}
if (SUCCEEDED(hr)) if (SUCCEEDED(hr))
{ {
hr = mDWriteFactory->GetGdiInterop(&mGdiInterop); hr = mDWriteFactory->GetGdiInterop(&mGdiInterop);
@@ -515,20 +643,24 @@ DWriteContext::~DWriteContext()
SafeRelease(&mRenderingParams); SafeRelease(&mRenderingParams);
SafeRelease(&mGdiInterop); SafeRelease(&mGdiInterop);
SafeRelease(&mDWriteFactory); SafeRelease(&mDWriteFactory);
SafeRelease(&mDWriteFactory2);
SafeRelease(&mBrush); SafeRelease(&mBrush);
SafeRelease(&mRT); SafeRelease(&mRT);
SafeRelease(&mD2D1Factory); SafeRelease(&mD2D1Factory);
} }
HRESULT HRESULT
DWriteContext::SetLOGFONT(const LOGFONTW &logFont, float fontSize) DWriteContext::CreateTextFormatFromLOGFONT(const LOGFONTW &logFont,
IDWriteTextFormat **ppTextFormat)
{ {
// Most of this function is copy from: http://msdn.microsoft.com/en-us/library/windows/desktop/dd941783(v=vs.85).aspx // Most of this function is copied from: https://github.com/Microsoft/Windows-classic-samples/blob/master/Samples/Win7Samples/multimedia/DirectWrite/RenderTest/TextHelpers.cpp
HRESULT hr = S_OK; HRESULT hr = S_OK;
IDWriteTextFormat *pTextFormat = NULL;
IDWriteFont *font = NULL; IDWriteFont *font = NULL;
IDWriteFontFamily *fontFamily = NULL; IDWriteFontFamily *fontFamily = NULL;
IDWriteLocalizedStrings *localizedFamilyNames = NULL; IDWriteLocalizedStrings *localizedFamilyNames = NULL;
float fontSize = 0;
if (SUCCEEDED(hr)) if (SUCCEEDED(hr))
{ {
@@ -561,33 +693,30 @@ DWriteContext::SetLOGFONT(const LOGFONTW &logFont, float fontSize)
if (SUCCEEDED(hr)) if (SUCCEEDED(hr))
{ {
// If no font size was passed in use the lfHeight of the LOGFONT. // Use lfHeight of the LOGFONT as font size.
if (fontSize == 0) fontSize = float(logFont.lfHeight);
if (fontSize < 0)
{ {
// Convert from pixels to DIPs. // Negative lfHeight represents the size of the em unit.
fontSize = PixelsToDipsY(logFont.lfHeight); fontSize = -fontSize;
if (fontSize < 0) }
{ else
// Negative lfHeight represents the size of the em unit. {
fontSize = -fontSize; // Positive lfHeight represents the cell height (ascent +
} // descent).
else DWRITE_FONT_METRICS fontMetrics;
{ font->GetMetrics(&fontMetrics);
// Positive lfHeight represents the cell height (ascent +
// descent).
DWRITE_FONT_METRICS fontMetrics;
font->GetMetrics(&fontMetrics);
// Convert the cell height (ascent + descent) from design units // Convert the cell height (ascent + descent) from design units
// to ems. // to ems.
float cellHeight = static_cast<float>( float cellHeight = static_cast<float>(
fontMetrics.ascent + fontMetrics.descent) fontMetrics.ascent + fontMetrics.descent)
/ fontMetrics.designUnitsPerEm; / fontMetrics.designUnitsPerEm;
// Divide the font size by the cell height to get the font em // Divide the font size by the cell height to get the font em
// size. // size.
fontSize /= cellHeight; fontSize /= cellHeight;
}
} }
} }
@@ -612,123 +741,165 @@ DWriteContext::SetLOGFONT(const LOGFONTW &logFont, float fontSize)
font->GetStretch(), font->GetStretch(),
fontSize, fontSize,
localeName, localeName,
&mTextFormat); &pTextFormat);
} }
if (SUCCEEDED(hr)) if (SUCCEEDED(hr))
{ hr = pTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING);
mFontWeight = static_cast<DWRITE_FONT_WEIGHT>(logFont.lfWeight);
mFontStyle = logFont.lfItalic ? DWRITE_FONT_STYLE_ITALIC if (SUCCEEDED(hr))
: DWRITE_FONT_STYLE_NORMAL; hr = pTextFormat->SetParagraphAlignment(
} DWRITE_PARAGRAPH_ALIGNMENT_CENTER);
if (SUCCEEDED(hr))
hr = pTextFormat->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP);
SafeRelease(&localizedFamilyNames); SafeRelease(&localizedFamilyNames);
SafeRelease(&fontFamily); SafeRelease(&fontFamily);
SafeRelease(&font); SafeRelease(&font);
if (SUCCEEDED(hr))
*ppTextFormat = pTextFormat;
else
SafeRelease(&pTextFormat);
return hr;
}
HRESULT
DWriteContext::SetFontByLOGFONT(const LOGFONTW &logFont)
{
HRESULT hr = S_OK;
IDWriteTextFormat *pTextFormat = NULL;
hr = CreateTextFormatFromLOGFONT(logFont, &pTextFormat);
if (SUCCEEDED(hr))
{
SafeRelease(&mTextFormat);
mTextFormat = pTextFormat;
mFontWeight = static_cast<DWRITE_FONT_WEIGHT>(logFont.lfWeight);
mFontStyle = logFont.lfItalic ? DWRITE_FONT_STYLE_ITALIC
: DWRITE_FONT_STYLE_NORMAL;
}
return hr; return hr;
} }
void void
DWriteContext::SetFont(HFONT hFont) DWriteContext::SetFont(HFONT hFont)
{ {
if (mLastHFont != hFont) FontCache::Item item;
if (mFontCache.get(hFont, item))
{ {
LOGFONTW lf; if (item.pTextFormat != NULL)
if (GetObjectW(hFont, sizeof(lf), &lf))
{ {
SetFont(lf); item.pTextFormat->AddRef();
mLastHFont = hFont; SafeRelease(&mTextFormat);
mTextFormat = item.pTextFormat;
mFontWeight = item.fontWeight;
mFontStyle = item.fontStyle;
mFallbackDC = false;
} }
else
mFallbackDC = true;
return;
} }
HRESULT hr = E_FAIL;
LOGFONTW lf;
if (GetObjectW(hFont, sizeof(lf), &lf))
hr = SetFontByLOGFONT(lf);
item.hFont = hFont;
if (SUCCEEDED(hr))
{
item.pTextFormat = mTextFormat;
item.fontWeight = mFontWeight;
item.fontStyle = mFontStyle;
}
mFontCache.put(item);
} }
void void
DWriteContext::SetFont(const LOGFONTW &logFont) DWriteContext::BindDC(HDC hdc, RECT *rect)
{ {
SafeRelease(&mTextFormat); Flush();
mLastHFont = NULL; mRT->BindDC(hdc, rect);
mRT->SetTransform(D2D1::IdentityMatrix());
HRESULT hr = SetLOGFONT(logFont, 0.f); mHDC = hdc;
if (SUCCEEDED(hr))
hr = mTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING);
if (SUCCEEDED(hr))
hr = mTextFormat->SetParagraphAlignment(
DWRITE_PARAGRAPH_ALIGNMENT_CENTER);
if (SUCCEEDED(hr))
hr = mTextFormat->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP);
} }
void void
DWriteContext::DrawText(HDC hdc, const WCHAR* text, int len, DWriteContext::AssureDrawing()
int x, int y, int w, int h, int cellWidth, COLORREF color)
{ {
HRESULT hr = S_OK; if (mDrawing == false)
IDWriteBitmapRenderTarget *bmpRT = NULL; {
mRT->BeginDraw();
mDrawing = true;
}
}
// Skip when any fonts are not set. ID2D1Brush*
if (mTextFormat == NULL) DWriteContext::SolidBrush(COLORREF color)
{
mBrush->SetColor(D2D1::ColorF(UINT32(GetRValue(color)) << 16 |
UINT32(GetGValue(color)) << 8 | UINT32(GetBValue(color))));
return mBrush;
}
void
DWriteContext::DrawText(const WCHAR* text, int len,
int x, int y, int w, int h, int cellWidth, COLORREF color,
UINT fuOptions, CONST RECT *lprc, CONST INT * lpDx)
{
if (mFallbackDC)
{
Flush();
ExtTextOutW(mHDC, x, y, fuOptions, lprc, text, len, lpDx);
return; return;
}
// Check possibility of zero divided error. AssureDrawing();
if (cellWidth == 0 || mDpiScaleX == 0.0f || mDpiScaleY == 0.0f)
return;
if (SUCCEEDED(hr)) HRESULT hr;
hr = mGdiInterop->CreateBitmapRenderTarget(hdc, w, h, &bmpRT); IDWriteTextLayout *textLayout = NULL;
hr = mDWriteFactory->CreateTextLayout(text, len, mTextFormat,
FLOAT(w), FLOAT(h), &textLayout);
if (SUCCEEDED(hr)) if (SUCCEEDED(hr))
{ {
IDWriteTextLayout *textLayout = NULL; DWRITE_TEXT_RANGE textRange = { 0, UINT32(len) };
textLayout->SetFontWeight(mFontWeight, textRange);
textLayout->SetFontStyle(mFontStyle, textRange);
HDC memdc = bmpRT->GetMemoryDC(); TextRenderer renderer(this);
BitBlt(memdc, 0, 0, w, h, hdc, x, y, SRCCOPY); TextRendererContext context = { color, FLOAT(cellWidth), 0.0f };
textLayout->Draw(&context, &renderer, FLOAT(x), FLOAT(y));
hr = mDWriteFactory->CreateGdiCompatibleTextLayout(
text, len, mTextFormat, PixelsToDipsX(w),
PixelsToDipsY(h), mDpiScaleX, NULL, TRUE, &textLayout);
if (SUCCEEDED(hr))
{
DWRITE_TEXT_RANGE textRange = { 0, (UINT32)len };
textLayout->SetFontWeight(mFontWeight, textRange);
textLayout->SetFontStyle(mFontStyle, textRange);
}
if (SUCCEEDED(hr))
{
GdiTextRenderer *renderer = new GdiTextRenderer(bmpRT,
mRenderingParams);
GdiTextRendererContext data = {
color,
PixelsToDipsX(cellWidth),
0.0f
};
textLayout->Draw(&data, renderer, 0, 0);
SafeRelease(&renderer);
}
BitBlt(hdc, x, y, w, h, memdc, 0, 0, SRCCOPY);
SafeRelease(&textLayout);
} }
SafeRelease(&bmpRT); SafeRelease(&textLayout);
} }
float void
DWriteContext::PixelsToDipsX(int x) DWriteContext::FillRect(RECT *rc, COLORREF color)
{ {
return x / mDpiScaleX; AssureDrawing();
mRT->FillRectangle(
D2D1::RectF(FLOAT(rc->left), FLOAT(rc->top),
FLOAT(rc->right), FLOAT(rc->bottom)),
SolidBrush(color));
} }
float void
DWriteContext::PixelsToDipsY(int y) DWriteContext::Flush()
{ {
return y / mDpiScaleY; if (mDrawing)
{
mRT->EndDraw();
mDrawing = false;
}
} }
void void
@@ -757,6 +928,10 @@ DWriteContext::SetRenderingParams(
SafeRelease(&mRenderingParams); SafeRelease(&mRenderingParams);
mRenderingParams = renderingParams; mRenderingParams = renderingParams;
mTextAntialiasMode = textAntialiasMode; mTextAntialiasMode = textAntialiasMode;
Flush();
mRT->SetTextRenderingParams(mRenderingParams);
mRT->SetTextAntialiasMode(mTextAntialiasMode);
} }
} }
@@ -824,40 +999,23 @@ DWriteContext_Open(void)
return new DWriteContext(); return new DWriteContext();
} }
void
DWriteContext_BeginDraw(DWriteContext *ctx)
{
if (ctx != NULL && ctx->mRT != NULL)
{
ctx->mRT->BeginDraw();
ctx->mRT->SetTransform(D2D1::IdentityMatrix());
ctx->mDrawing = true;
}
}
void void
DWriteContext_BindDC(DWriteContext *ctx, HDC hdc, RECT *rect) DWriteContext_BindDC(DWriteContext *ctx, HDC hdc, RECT *rect)
{ {
if (ctx != NULL && ctx->mRT != NULL) if (ctx != NULL)
{ ctx->BindDC(hdc, rect);
ctx->mRT->BindDC(hdc, rect);
ctx->mRT->SetTextAntialiasMode(ctx->mTextAntialiasMode);
}
} }
void void
DWriteContext_SetFont(DWriteContext *ctx, HFONT hFont) DWriteContext_SetFont(DWriteContext *ctx, HFONT hFont)
{ {
if (ctx != NULL) if (ctx != NULL)
{
ctx->SetFont(hFont); ctx->SetFont(hFont);
}
} }
void void
DWriteContext_DrawText( DWriteContext_DrawText(
DWriteContext *ctx, DWriteContext *ctx,
HDC hdc,
const WCHAR* text, const WCHAR* text,
int len, int len,
int x, int x,
@@ -865,20 +1023,28 @@ DWriteContext_DrawText(
int w, int w,
int h, int h,
int cellWidth, int cellWidth,
COLORREF color) COLORREF color,
UINT fuOptions,
CONST RECT *lprc,
CONST INT * lpDx)
{ {
if (ctx != NULL) if (ctx != NULL)
ctx->DrawText(hdc, text, len, x, y, w, h, cellWidth, color); ctx->DrawText(text, len, x, y, w, h, cellWidth, color,
fuOptions, lprc, lpDx);
} }
void void
DWriteContext_EndDraw(DWriteContext *ctx) DWriteContext_FillRect(DWriteContext *ctx, RECT *rc, COLORREF color)
{ {
if (ctx != NULL && ctx->mRT != NULL) if (ctx != NULL)
{ ctx->FillRect(rc, color);
ctx->mRT->EndDraw(); }
ctx->mDrawing = false;
} void
DWriteContext_Flush(DWriteContext *ctx)
{
if (ctx != NULL)
ctx->Flush();
} }
void void

View File

@@ -4,6 +4,7 @@
* *
* Contributors: * Contributors:
* - Ken Takata * - Ken Takata
* - Yasuhiro Matsumoto
* *
* Copyright (C) 2013 MURAOKA Taro <koron.kaoriya@gmail.com> * Copyright (C) 2013 MURAOKA Taro <koron.kaoriya@gmail.com>
* THIS FILE IS DISTRIBUTED UNDER THE VIM LICENSE. * THIS FILE IS DISTRIBUTED UNDER THE VIM LICENSE.
@@ -54,12 +55,10 @@ void DWrite_Init(void);
void DWrite_Final(void); void DWrite_Final(void);
DWriteContext *DWriteContext_Open(void); DWriteContext *DWriteContext_Open(void);
void DWriteContext_BeginDraw(DWriteContext *ctx);
void DWriteContext_BindDC(DWriteContext *ctx, HDC hdc, RECT *rect); void DWriteContext_BindDC(DWriteContext *ctx, HDC hdc, RECT *rect);
void DWriteContext_SetFont(DWriteContext *ctx, HFONT hFont); void DWriteContext_SetFont(DWriteContext *ctx, HFONT hFont);
void DWriteContext_DrawText( void DWriteContext_DrawText(
DWriteContext *ctx, DWriteContext *ctx,
HDC hdc,
const WCHAR* text, const WCHAR* text,
int len, int len,
int x, int x,
@@ -67,8 +66,12 @@ void DWriteContext_DrawText(
int w, int w,
int h, int h,
int cellWidth, int cellWidth,
COLORREF color); COLORREF color,
void DWriteContext_EndDraw(DWriteContext *ctx); UINT fuOptions,
CONST RECT *lprc,
CONST INT * lpDx);
void DWriteContext_FillRect(DWriteContext *ctx, RECT *rc, COLORREF color);
void DWriteContext_Flush(DWriteContext *ctx);
void DWriteContext_Close(DWriteContext *ctx); void DWriteContext_Close(DWriteContext *ctx);
void DWriteContext_SetRenderingParams( void DWriteContext_SetRenderingParams(

View File

@@ -34,28 +34,14 @@ static DWriteContext *s_dwc = NULL;
static int s_directx_enabled = 0; static int s_directx_enabled = 0;
static int s_directx_load_attempted = 0; static int s_directx_load_attempted = 0;
# define IS_ENABLE_DIRECTX() (s_directx_enabled && s_dwc != NULL) # define IS_ENABLE_DIRECTX() (s_directx_enabled && s_dwc != NULL)
static int directx_enabled(void);
static void directx_binddc(void);
#endif #endif
#ifdef FEAT_MENU #ifdef FEAT_MENU
static int gui_mswin_get_menu_height(int fix_window); static int gui_mswin_get_menu_height(int fix_window);
#endif #endif
#if defined(FEAT_DIRECTX) || defined(PROTO)
int
directx_enabled(void)
{
if (s_dwc != NULL)
return 1;
else if (s_directx_load_attempted)
return 0;
/* load DirectX */
DWrite_Init();
s_directx_load_attempted = 1;
s_dwc = DWriteContext_Open();
return s_dwc != NULL ? 1 : 0;
}
#endif
#if defined(FEAT_RENDER_OPTIONS) || defined(PROTO) #if defined(FEAT_RENDER_OPTIONS) || defined(PROTO)
int int
gui_mch_set_rendering_options(char_u *s) gui_mch_set_rendering_options(char_u *s)
@@ -369,6 +355,34 @@ static int allow_scrollbar = FALSE;
# define MyTranslateMessage(x) TranslateMessage(x) # define MyTranslateMessage(x) TranslateMessage(x)
#endif #endif
#if defined(FEAT_DIRECTX)
static int
directx_enabled(void)
{
if (s_dwc != NULL)
return 1;
else if (s_directx_load_attempted)
return 0;
/* load DirectX */
DWrite_Init();
s_directx_load_attempted = 1;
s_dwc = DWriteContext_Open();
directx_binddc();
return s_dwc != NULL ? 1 : 0;
}
static void
directx_binddc(void)
{
if (s_textArea != NULL)
{
RECT rect;
GetClientRect(s_textArea, &rect);
DWriteContext_BindDC(s_dwc, s_hdc, &rect);
}
}
#endif
#if defined(FEAT_MBYTE) || defined(GLOBAL_IME) #if defined(FEAT_MBYTE) || defined(GLOBAL_IME)
/* use of WindowProc depends on wide_WindowProc */ /* use of WindowProc depends on wide_WindowProc */
# define MyWindowProc vim_WindowProc # define MyWindowProc vim_WindowProc
@@ -589,6 +603,10 @@ _OnBlinkTimer(
blink_timer = (UINT) SetTimer(NULL, 0, (UINT)blink_ontime, blink_timer = (UINT) SetTimer(NULL, 0, (UINT)blink_ontime,
(TIMERPROC)_OnBlinkTimer); (TIMERPROC)_OnBlinkTimer);
} }
#if defined(FEAT_DIRECTX)
if (IS_ENABLE_DIRECTX())
DWriteContext_Flush(s_dwc);
#endif
} }
static void static void
@@ -1000,6 +1018,19 @@ _OnMouseMoveOrRelease(
_OnMouseEvent(button, x, y, FALSE, keyFlags); _OnMouseEvent(button, x, y, FALSE, keyFlags);
} }
static void
_OnSizeTextArea(
HWND hwnd UNUSED,
UINT state UNUSED,
int cx UNUSED,
int cy UNUSED)
{
#if defined(FEAT_DIRECTX)
if (IS_ENABLE_DIRECTX())
directx_binddc();
#endif
}
#ifdef FEAT_MENU #ifdef FEAT_MENU
/* /*
* Find the vimmenu_T with the given id * Find the vimmenu_T with the given id
@@ -1234,6 +1265,7 @@ _TextAreaWndProc(
HANDLE_MSG(hwnd, WM_XBUTTONDBLCLK,_OnMouseButtonDown); HANDLE_MSG(hwnd, WM_XBUTTONDBLCLK,_OnMouseButtonDown);
HANDLE_MSG(hwnd, WM_XBUTTONDOWN,_OnMouseButtonDown); HANDLE_MSG(hwnd, WM_XBUTTONDOWN,_OnMouseButtonDown);
HANDLE_MSG(hwnd, WM_XBUTTONUP, _OnMouseMoveOrRelease); HANDLE_MSG(hwnd, WM_XBUTTONUP, _OnMouseMoveOrRelease);
HANDLE_MSG(hwnd, WM_SIZE, _OnSizeTextArea);
#ifdef FEAT_BEVAL_GUI #ifdef FEAT_BEVAL_GUI
case WM_NOTIFY: Handle_WM_Notify(hwnd, (LPNMHDR)lParam); case WM_NOTIFY: Handle_WM_Notify(hwnd, (LPNMHDR)lParam);
@@ -1633,6 +1665,11 @@ gui_mch_invert_rectangle(
{ {
RECT rc; RECT rc;
#if defined(FEAT_DIRECTX)
if (IS_ENABLE_DIRECTX())
DWriteContext_Flush(s_dwc);
#endif
/* /*
* Note: InvertRect() excludes right and bottom of rectangle. * Note: InvertRect() excludes right and bottom of rectangle.
*/ */
@@ -1661,6 +1698,11 @@ gui_mch_draw_hollow_cursor(guicolor_T color)
HBRUSH hbr; HBRUSH hbr;
RECT rc; RECT rc;
#if defined(FEAT_DIRECTX)
if (IS_ENABLE_DIRECTX())
DWriteContext_Flush(s_dwc);
#endif
/* /*
* Note: FrameRect() excludes right and bottom of rectangle. * Note: FrameRect() excludes right and bottom of rectangle.
*/ */
@@ -1701,6 +1743,12 @@ gui_mch_draw_part_cursor(
rc.top = FILL_Y(gui.row) + gui.char_height - h; rc.top = FILL_Y(gui.row) + gui.char_height - h;
rc.right = rc.left + w; rc.right = rc.left + w;
rc.bottom = rc.top + h; rc.bottom = rc.top + h;
#if defined(FEAT_DIRECTX)
if (IS_ENABLE_DIRECTX())
DWriteContext_Flush(s_dwc);
#endif
hbr = CreateSolidBrush(color); hbr = CreateSolidBrush(color);
FillRect(s_hdc, &rc, hbr); FillRect(s_hdc, &rc, hbr);
DeleteBrush(hbr); DeleteBrush(hbr);
@@ -2856,10 +2904,6 @@ _OnPaint(
out_flush(); /* make sure all output has been processed */ out_flush(); /* make sure all output has been processed */
(void)BeginPaint(hwnd, &ps); (void)BeginPaint(hwnd, &ps);
#if defined(FEAT_DIRECTX)
if (IS_ENABLE_DIRECTX())
DWriteContext_BeginDraw(s_dwc);
#endif
#ifdef FEAT_MBYTE #ifdef FEAT_MBYTE
/* prevent multi-byte characters from misprinting on an invalid /* prevent multi-byte characters from misprinting on an invalid
@@ -2876,19 +2920,11 @@ _OnPaint(
if (!IsRectEmpty(&ps.rcPaint)) if (!IsRectEmpty(&ps.rcPaint))
{ {
#if defined(FEAT_DIRECTX)
if (IS_ENABLE_DIRECTX())
DWriteContext_BindDC(s_dwc, s_hdc, &ps.rcPaint);
#endif
gui_redraw(ps.rcPaint.left, ps.rcPaint.top, gui_redraw(ps.rcPaint.left, ps.rcPaint.top,
ps.rcPaint.right - ps.rcPaint.left + 1, ps.rcPaint.right - ps.rcPaint.left + 1,
ps.rcPaint.bottom - ps.rcPaint.top + 1); ps.rcPaint.bottom - ps.rcPaint.top + 1);
} }
#if defined(FEAT_DIRECTX)
if (IS_ENABLE_DIRECTX())
DWriteContext_EndDraw(s_dwc);
#endif
EndPaint(hwnd, &ps); EndPaint(hwnd, &ps);
} }
} }
@@ -3010,6 +3046,11 @@ gui_mch_flash(int msec)
{ {
RECT rc; RECT rc;
#if defined(FEAT_DIRECTX)
if (IS_ENABLE_DIRECTX())
DWriteContext_Flush(s_dwc);
#endif
/* /*
* Note: InvertRect() excludes right and bottom of rectangle. * Note: InvertRect() excludes right and bottom of rectangle.
*/ */
@@ -3082,6 +3123,12 @@ gui_mch_delete_lines(
intel_gpu_workaround(); intel_gpu_workaround();
#if defined(FEAT_DIRECTX)
// Commit drawing queue before ScrollWindowEx.
if (IS_ENABLE_DIRECTX())
DWriteContext_Flush(s_dwc);
#endif
rc.left = FILL_X(gui.scroll_region_left); rc.left = FILL_X(gui.scroll_region_left);
rc.right = FILL_X(gui.scroll_region_right + 1); rc.right = FILL_X(gui.scroll_region_right + 1);
rc.top = FILL_Y(row); rc.top = FILL_Y(row);
@@ -3115,6 +3162,12 @@ gui_mch_insert_lines(
intel_gpu_workaround(); intel_gpu_workaround();
#if defined(FEAT_DIRECTX)
// Commit drawing queue before ScrollWindowEx.
if (IS_ENABLE_DIRECTX())
DWriteContext_Flush(s_dwc);
#endif
rc.left = FILL_X(gui.scroll_region_left); rc.left = FILL_X(gui.scroll_region_left);
rc.right = FILL_X(gui.scroll_region_right + 1); rc.right = FILL_X(gui.scroll_region_right + 1);
rc.top = FILL_Y(row); rc.top = FILL_Y(row);
@@ -6145,9 +6198,6 @@ gui_mch_draw_string(
#endif #endif
HPEN hpen, old_pen; HPEN hpen, old_pen;
int y; int y;
#ifdef FEAT_DIRECTX
int font_is_ttf_or_vector = 0;
#endif
/* /*
* Italic and bold text seems to have an extra row of pixels at the bottom * Italic and bold text seems to have an extra row of pixels at the bottom
@@ -6208,6 +6258,11 @@ gui_mch_draw_string(
hbr = hbr_cache[brush_lru]; hbr = hbr_cache[brush_lru];
brush_lru = !brush_lru; brush_lru = !brush_lru;
} }
#if defined(FEAT_DIRECTX)
if (IS_ENABLE_DIRECTX())
DWriteContext_FillRect(s_dwc, &rc, gui.currBgColor);
#endif
FillRect(s_hdc, &rc, hbr); FillRect(s_hdc, &rc, hbr);
SetBkMode(s_hdc, TRANSPARENT); SetBkMode(s_hdc, TRANSPARENT);
@@ -6227,16 +6282,7 @@ gui_mch_draw_string(
#ifdef FEAT_DIRECTX #ifdef FEAT_DIRECTX
if (IS_ENABLE_DIRECTX()) if (IS_ENABLE_DIRECTX())
{ DWriteContext_SetFont(s_dwc, (HFONT)gui.currFont);
TEXTMETRIC tm;
GetTextMetrics(s_hdc, &tm);
if (tm.tmPitchAndFamily & (TMPF_TRUETYPE | TMPF_VECTOR))
{
font_is_ttf_or_vector = 1;
DWriteContext_SetFont(s_dwc, (HFONT)gui.currFont);
}
}
#endif #endif
if (pad_size != Columns || padding == NULL || padding[0] != gui.char_width) if (pad_size != Columns || padding == NULL || padding[0] != gui.char_width)
@@ -6347,12 +6393,13 @@ gui_mch_draw_string(
++clen; ++clen;
} }
#if defined(FEAT_DIRECTX) #if defined(FEAT_DIRECTX)
if (IS_ENABLE_DIRECTX() && font_is_ttf_or_vector) if (IS_ENABLE_DIRECTX())
{ {
/* Add one to "cells" for italics. */ /* Add one to "cells" for italics. */
DWriteContext_DrawText(s_dwc, s_hdc, unicodebuf, wlen, DWriteContext_DrawText(s_dwc, unicodebuf, wlen,
TEXT_X(col), TEXT_Y(row), FILL_X(cells + 1), FILL_Y(1), TEXT_X(col), TEXT_Y(row), FILL_X(cells + 1), FILL_Y(1),
gui.char_width, gui.currFgColor); gui.char_width, gui.currFgColor,
foptions, pcliprect, unicodepdy);
} }
else else
#endif #endif
@@ -6411,6 +6458,12 @@ gui_mch_draw_string(
foptions, pcliprect, (char *)text, len, padding); foptions, pcliprect, (char *)text, len, padding);
} }
#if defined(FEAT_DIRECTX)
if (IS_ENABLE_DIRECTX() &&
(flags & (DRAW_UNDERL | DRAW_STRIKE | DRAW_UNDERC | DRAW_CURSOR)))
DWriteContext_Flush(s_dwc);
#endif
/* Underline */ /* Underline */
if (flags & DRAW_UNDERL) if (flags & DRAW_UNDERL)
{ {
@@ -6473,6 +6526,11 @@ gui_mch_flush(void)
BOOL __stdcall GdiFlush(void); BOOL __stdcall GdiFlush(void);
# endif # endif
#if defined(FEAT_DIRECTX)
if (IS_ENABLE_DIRECTX())
DWriteContext_Flush(s_dwc);
#endif
GdiFlush(); GdiFlush();
} }
@@ -6481,6 +6539,14 @@ clear_rect(RECT *rcp)
{ {
HBRUSH hbr; HBRUSH hbr;
#if defined(FEAT_DIRECTX)
if (IS_ENABLE_DIRECTX())
{
DWriteContext_FillRect(s_dwc, rcp, gui.back_pixel);
return;
}
#endif
hbr = CreateSolidBrush(gui.back_pixel); hbr = CreateSolidBrush(gui.back_pixel);
FillRect(s_hdc, rcp, hbr); FillRect(s_hdc, rcp, hbr);
DeleteBrush(hbr); DeleteBrush(hbr);
@@ -8386,6 +8452,11 @@ gui_mch_drawsign(int row, int col, int typenr)
if (!gui.in_use || (sign = (signicon_t *)sign_get_image(typenr)) == NULL) if (!gui.in_use || (sign = (signicon_t *)sign_get_image(typenr)) == NULL)
return; return;
#if defined(FEAT_DIRECTX)
if (IS_ENABLE_DIRECTX())
DWriteContext_Flush(s_dwc);
#endif
x = TEXT_X(col); x = TEXT_X(col);
y = TEXT_Y(row); y = TEXT_Y(row);
w = gui.char_width * 2; w = gui.char_width * 2;
@@ -8865,6 +8936,11 @@ netbeans_draw_multisign_indicator(int row)
x = 0; x = 0;
y = TEXT_Y(row); y = TEXT_Y(row);
#if defined(FEAT_DIRECTX)
if (IS_ENABLE_DIRECTX())
DWriteContext_Flush(s_dwc);
#endif
for (i = 0; i < gui.char_height - 3; i++) for (i = 0; i < gui.char_height - 3; i++)
SetPixel(s_hdc, x+2, y++, gui.currFgColor); SetPixel(s_hdc, x+2, y++, gui.currFgColor);

View File

@@ -4782,6 +4782,14 @@ iconv_end(void)
#endif /* FEAT_MBYTE */ #endif /* FEAT_MBYTE */
#ifdef FEAT_GUI
# define USE_IMACTIVATEFUNC (!gui.in_use && *p_imaf != NUL)
# define USE_IMSTATUSFUNC (!gui.in_use && *p_imsf != NUL)
#else
# define USE_IMACTIVATEFUNC (*p_imaf != NUL)
# define USE_IMSTATUSFUNC (*p_imsf != NUL)
#endif
#ifdef FEAT_EVAL #ifdef FEAT_EVAL
static void static void
call_imactivatefunc(int active) call_imactivatefunc(int active)
@@ -5689,7 +5697,7 @@ im_synthesize_keypress(unsigned int keyval, unsigned int state)
xim_reset(void) xim_reset(void)
{ {
#ifdef FEAT_EVAL #ifdef FEAT_EVAL
if (p_imaf[0] != NUL) if (USE_IMACTIVATEFUNC)
call_imactivatefunc(im_is_active); call_imactivatefunc(im_is_active);
else else
#endif #endif
@@ -5868,7 +5876,7 @@ xim_queue_key_press_event(GdkEventKey *event, int down)
im_get_status(void) im_get_status(void)
{ {
# ifdef FEAT_EVAL # ifdef FEAT_EVAL
if (p_imsf[0] != NUL) if (USE_IMSTATUSFUNC)
return call_imstatusfunc(); return call_imstatusfunc();
# endif # endif
return im_is_active; return im_is_active;
@@ -5908,21 +5916,19 @@ im_set_active(int active_arg)
/* If 'imdisable' is set, XIM is never active. */ /* If 'imdisable' is set, XIM is never active. */
if (p_imdisable) if (p_imdisable)
active = FALSE; active = FALSE;
# if !defined(FEAT_GUI_GTK)
else if (input_style & XIMPreeditPosition) else if (input_style & XIMPreeditPosition)
/* There is a problem in switching XIM off when preediting is used, /* There is a problem in switching XIM off when preediting is used,
* and it is not clear how this can be solved. For now, keep XIM on * and it is not clear how this can be solved. For now, keep XIM on
* all the time, like it was done in Vim 5.8. */ * all the time, like it was done in Vim 5.8. */
active = TRUE; active = TRUE;
# endif
# if defined(FEAT_EVAL) # if defined(FEAT_EVAL)
if (p_imaf[0] != NUL) if (USE_IMACTIVATEFUNC)
{ {
if (active != im_get_status()) if (active != im_get_status())
{ {
call_imactivatefunc(active); call_imactivatefunc(active);
im_is_active = active; xim_has_focus = active;
} }
return; return;
} }
@@ -6328,7 +6334,8 @@ xim_real_init(Window x11_window, Display *x11_display)
} }
else else
{ {
EMSG(_(e_xim)); if (!is_not_a_term())
EMSG(_(e_xim));
XCloseIM(xim); XCloseIM(xim);
return FALSE; return FALSE;
} }
@@ -6348,7 +6355,7 @@ xim_real_init(Window x11_window, Display *x11_display)
im_get_status(void) im_get_status(void)
{ {
# ifdef FEAT_EVAL # ifdef FEAT_EVAL
if (p_imsf[0] != NUL) if (USE_IMSTATUSFUNC)
return call_imstatusfunc(); return call_imstatusfunc();
# endif # endif
return xim_has_focus; return xim_has_focus;
@@ -6480,7 +6487,7 @@ static int im_was_set_active = FALSE;
im_get_status() im_get_status()
{ {
# ifdef FEAT_EVAL # ifdef FEAT_EVAL
if (p_imsf[0] != NUL) if (USE_IMSTATUSFUNC)
return call_imstatusfunc(); return call_imstatusfunc();
# endif # endif
return im_was_set_active; return im_was_set_active;
@@ -6492,7 +6499,7 @@ im_set_active(int active_arg)
# if defined(FEAT_MBYTE) && defined(FEAT_EVAL) # if defined(FEAT_MBYTE) && defined(FEAT_EVAL)
int active = !p_imdisable && active_arg; int active = !p_imdisable && active_arg;
if (p_imaf[0] != NUL && active != im_get_status()) if (USE_IMACTIVATEFUNC && active != im_get_status())
{ {
call_imactivatefunc(active); call_imactivatefunc(active);
im_was_set_active = active; im_was_set_active = active;

View File

@@ -836,7 +836,7 @@ split_message(char_u *mesg, pumitem_T **array)
item->bytelen = p - item->start; item->bytelen = p - item->start;
if (item->cells > max_cells) if (item->cells > max_cells)
max_cells = item->cells; max_cells = item->cells;
long_item_count += item->cells / BALLOON_MIN_WIDTH; long_item_count += (item->cells - 1) / BALLOON_MIN_WIDTH;
} }
height = 2 + ga.ga_len; height = 2 + ga.ga_len;

View File

@@ -1,5 +1,4 @@
/* gui_w32.c */ /* gui_w32.c */
int directx_enabled(void);
int gui_mch_set_rendering_options(char_u *s); int gui_mch_set_rendering_options(char_u *s);
int gui_mch_is_blinking(void); int gui_mch_is_blinking(void);
int gui_mch_is_blink_off(void); int gui_mch_is_blink_off(void);

View File

@@ -17,9 +17,6 @@ func IM_statusfunc()
endfunc endfunc
func Test_iminsert2() func Test_iminsert2()
if has('gui_win32')
return
endif
set imactivatefunc=IM_activatefunc set imactivatefunc=IM_activatefunc
set imstatusfunc=IM_statusfunc set imstatusfunc=IM_statusfunc
set iminsert=2 set iminsert=2
@@ -27,6 +24,8 @@ func Test_iminsert2()
set iminsert=0 set iminsert=0
set imactivatefunc= set imactivatefunc=
set imstatusfunc= set imstatusfunc=
call assert_equal(1, s:imactivatefunc_called)
call assert_equal(1, s:imstatusfunc_called) let expected = has('gui_running') ? 0 : 1
call assert_equal(expected, s:imactivatefunc_called)
call assert_equal(expected, s:imstatusfunc_called)
endfunc endfunc

View File

@@ -707,6 +707,10 @@ func Test_balloon_split()
if !exists('*balloon_split') if !exists('*balloon_split')
return return
endif endif
call assert_equal([
\ 'tempname: 0x555555e380a0 "/home/mool/.viminfz.tmp"',
\ ], balloon_split(
\ 'tempname: 0x555555e380a0 "/home/mool/.viminfz.tmp"'))
call assert_equal([ call assert_equal([
\ 'one two three four one two three four one two thre', \ 'one two three four one two three four one two thre',
\ 'e four', \ 'e four',

View File

@@ -771,6 +771,16 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
1346,
/**/
1345,
/**/
1344,
/**/
1343,
/**/
1342,
/**/ /**/
1341, 1341,
/**/ /**/