mirror of
https://github.com/reactos/reactos.git
synced 2024-08-22 03:12:51 +00:00
![Baruch Rutman](/assets/img/avatar_default.png)
Add a mechanism that detects via the version resource whether it's needed to mirror
the application layout or not. This is a "follow up" to commit 5b14b6f
.
This code is taken from the GetProcessDefaultLayout() function from Wine
(called there "WIN_CreateWindowEx").
In Windows the version-resource mirroring happens when the GDI is loading the LPK.
- Implement mirroring via version resource. GDI linking on startup is required.
- GDI32: Add LPK loading on GDI startup.
To enable mirroring via version resource the lpk.dll must be loaded before any windows are created.
This is done through GdiInitializeLanguagePack() function which is called in GdiProcessSetup().
- LPK: Use Wine debug messaging.
428 lines
12 KiB
C
428 lines
12 KiB
C
/*
|
|
* PROJECT: ReactOS LPK
|
|
* LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
|
|
* PURPOSE: Language Pack DLL.
|
|
* PROGRAMMERS: Magnus Olsen (greatlrd)
|
|
* Baruch Rutman (peterooch at gmail dot com)
|
|
*/
|
|
|
|
#include "ros_lpk.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(bidi);
|
|
|
|
LPK_LPEDITCONTROL_LIST LpkEditControl = {EditCreate, EditIchToXY, EditMouseToIch, EditCchInWidth,
|
|
EditGetLineWidth, EditDrawText, EditHScroll, EditMoveSelection,
|
|
EditVerifyText, EditNextWord, EditSetMenu, EditProcessMenu,
|
|
EditCreateCaret, EditAdjustCaret};
|
|
|
|
#define PREFIX 38
|
|
#define ALPHA_PREFIX 30 /* Win16: Alphabet prefix */
|
|
#define KANA_PREFIX 31 /* Win16: Katakana prefix */
|
|
|
|
static int PSM_FindLastPrefix(LPCWSTR str, int count)
|
|
{
|
|
int i, prefix_count = 0, index = -1;
|
|
|
|
for (i = 0; i < count - 1; i++)
|
|
{
|
|
if (str[i] == PREFIX && str[i + 1] != PREFIX)
|
|
{
|
|
index = i - prefix_count;
|
|
prefix_count++;
|
|
}
|
|
else if (str[i] == PREFIX && str[i + 1] == PREFIX)
|
|
{
|
|
i++;
|
|
}
|
|
}
|
|
return index;
|
|
}
|
|
|
|
static void PSM_PrepareToDraw(LPCWSTR str, INT count, LPWSTR new_str, LPINT new_count)
|
|
{
|
|
int len, i = 0, j = 0;
|
|
|
|
while (i < count)
|
|
{
|
|
if (str[i] == PREFIX || (iswspace(str[i]) && str[i] != L' '))
|
|
{
|
|
if(i < count - 1 && str[i + 1] == PREFIX)
|
|
new_str[j++] = str[i++];
|
|
else
|
|
i++;
|
|
}
|
|
else
|
|
{
|
|
new_str[j++] = str[i++];
|
|
}
|
|
}
|
|
|
|
new_str[j] = L'\0';
|
|
len = wcslen(new_str);
|
|
*new_count = len;
|
|
}
|
|
|
|
/* Can be used with also LpkDrawTextEx if it will be implemented */
|
|
static void LPK_DrawUnderscore(HDC hdc, int x, int y, LPCWSTR str, int count, int offset)
|
|
{
|
|
SCRIPT_STRING_ANALYSIS ssa;
|
|
DWORD dwSSAFlags = SSA_GLYPHS;
|
|
int prefix_x;
|
|
int prefix_end;
|
|
int pos;
|
|
SIZE size;
|
|
HPEN hpen;
|
|
HPEN oldPen;
|
|
HRESULT hr = S_FALSE;
|
|
|
|
if (offset == -1)
|
|
return;
|
|
|
|
if (ScriptIsComplex(str, count, SIC_COMPLEX) == S_OK)
|
|
{
|
|
if (GetLayout(hdc) & LAYOUT_RTL || GetTextAlign(hdc) & TA_RTLREADING)
|
|
dwSSAFlags |= SSA_RTL;
|
|
|
|
hr = ScriptStringAnalyse(hdc, str, count, (3 * count / 2 + 16),
|
|
-1, dwSSAFlags, -1, NULL, NULL, NULL, NULL, NULL, &ssa);
|
|
}
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
ScriptStringCPtoX(ssa, offset, FALSE, &pos);
|
|
prefix_x = x + pos;
|
|
ScriptStringCPtoX(ssa, offset, TRUE, &pos);
|
|
prefix_end = x + pos;
|
|
ScriptStringFree(&ssa);
|
|
}
|
|
else
|
|
{
|
|
GetTextExtentPointW(hdc, str, offset, &size);
|
|
prefix_x = x + size.cx;
|
|
GetTextExtentPointW(hdc, str, offset + 1, &size);
|
|
prefix_end = x + size.cx - 1;
|
|
}
|
|
hpen = CreatePen(PS_SOLID, 1, GetTextColor(hdc));
|
|
oldPen = SelectObject(hdc, hpen);
|
|
MoveToEx(hdc, prefix_x, y, NULL);
|
|
LineTo(hdc, prefix_end, y);
|
|
SelectObject(hdc, oldPen);
|
|
DeleteObject(hpen);
|
|
}
|
|
|
|
/* Code taken from the GetProcessDefaultLayout function from Wine's user32
|
|
* Wine version 3.17
|
|
*
|
|
* This function should be called from LpkInitialize,
|
|
* which is in turn called by GdiInitializeLanguagePack (from gdi32).
|
|
* TODO: Move call from LpkDllInitialize to LpkInitialize when latter
|
|
* function is implemented.
|
|
*/
|
|
static void LPK_ApplyMirroring()
|
|
{
|
|
static const WCHAR translationW[] = { '\\','V','a','r','F','i','l','e','I','n','f','o',
|
|
'\\','T','r','a','n','s','l','a','t','i','o','n', 0 };
|
|
static const WCHAR filedescW[] = { '\\','S','t','r','i','n','g','F','i','l','e','I','n','f','o',
|
|
'\\','%','0','4','x','%','0','4','x',
|
|
'\\','F','i','l','e','D','e','s','c','r','i','p','t','i','o','n',0 };
|
|
WCHAR *str, buffer[MAX_PATH];
|
|
#ifdef __REACTOS__
|
|
DWORD i, version_layout = 0;
|
|
UINT len;
|
|
#else
|
|
DWORD i, len, version_layout = 0;
|
|
#endif
|
|
DWORD user_lang = GetUserDefaultLangID();
|
|
DWORD *languages;
|
|
void *data = NULL;
|
|
|
|
GetModuleFileNameW( 0, buffer, MAX_PATH );
|
|
if (!(len = GetFileVersionInfoSizeW( buffer, NULL ))) goto done;
|
|
if (!(data = HeapAlloc( GetProcessHeap(), 0, len ))) goto done;
|
|
if (!GetFileVersionInfoW( buffer, 0, len, data )) goto done;
|
|
if (!VerQueryValueW( data, translationW, (void **)&languages, &len ) || !len) goto done;
|
|
|
|
len /= sizeof(DWORD);
|
|
for (i = 0; i < len; i++) if (LOWORD(languages[i]) == user_lang) break;
|
|
if (i == len) /* try neutral language */
|
|
for (i = 0; i < len; i++)
|
|
if (LOWORD(languages[i]) == MAKELANGID( PRIMARYLANGID(user_lang), SUBLANG_NEUTRAL )) break;
|
|
if (i == len) i = 0; /* default to the first one */
|
|
|
|
sprintfW( buffer, filedescW, LOWORD(languages[i]), HIWORD(languages[i]) );
|
|
if (!VerQueryValueW( data, buffer, (void **)&str, &len )) goto done;
|
|
TRACE( "found description %s\n", debugstr_w( str ));
|
|
if (str[0] == 0x200e && str[1] == 0x200e) version_layout = LAYOUT_RTL;
|
|
|
|
done:
|
|
HeapFree( GetProcessHeap(), 0, data );
|
|
SetProcessDefaultLayout(version_layout);
|
|
}
|
|
|
|
BOOL
|
|
WINAPI
|
|
DllMain(
|
|
HANDLE hDll,
|
|
DWORD dwReason,
|
|
LPVOID lpReserved)
|
|
{
|
|
|
|
return LpkDllInitialize(hDll,dwReason,lpReserved);
|
|
}
|
|
|
|
BOOL
|
|
WINAPI
|
|
LpkDllInitialize(
|
|
HANDLE hDll,
|
|
DWORD dwReason,
|
|
LPVOID lpReserved)
|
|
{
|
|
switch(dwReason)
|
|
{
|
|
case DLL_PROCESS_ATTACH:
|
|
DisableThreadLibraryCalls(hDll);
|
|
/* Tell usp10 it is activated usp10 */
|
|
//LpkPresent();
|
|
LPK_ApplyMirroring();
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
LpkExtTextOut(
|
|
HDC hdc,
|
|
int x,
|
|
int y,
|
|
UINT fuOptions,
|
|
const RECT *lprc,
|
|
LPCWSTR lpString,
|
|
UINT uCount,
|
|
const INT *lpDx,
|
|
INT unknown)
|
|
{
|
|
LPWORD glyphs = NULL;
|
|
LPWSTR reordered_str = NULL;
|
|
INT cGlyphs;
|
|
DWORD dwSICFlags = SIC_COMPLEX;
|
|
BOOL bResult, bReorder;
|
|
|
|
UNREFERENCED_PARAMETER(unknown);
|
|
|
|
fuOptions |= ETO_IGNORELANGUAGE;
|
|
|
|
/* Check text direction */
|
|
if ((GetLayout(hdc) & LAYOUT_RTL) || (GetTextAlign(hdc) & TA_RTLREADING))
|
|
fuOptions |= ETO_RTLREADING;
|
|
|
|
/* If text direction is RTL change flag to account neutral characters */
|
|
if (fuOptions & ETO_RTLREADING)
|
|
dwSICFlags |= SIC_NEUTRAL;
|
|
|
|
/* Check if the string requires complex script processing and not a "glyph indices" array */
|
|
if (ScriptIsComplex(lpString, uCount, dwSICFlags) == S_OK && !(fuOptions & ETO_GLYPH_INDEX))
|
|
{
|
|
/* reordered_str is used as fallback in case the glyphs array fails to generate,
|
|
BIDI_Reorder doesn't attempt to write into reordered_str if memory allocation fails */
|
|
reordered_str = HeapAlloc(GetProcessHeap(), 0, uCount * sizeof(WCHAR));
|
|
|
|
bReorder = BIDI_Reorder(hdc, lpString, uCount, GCP_REORDER,
|
|
(fuOptions & ETO_RTLREADING) ? WINE_GCPW_FORCE_RTL : WINE_GCPW_FORCE_LTR,
|
|
reordered_str, uCount, NULL, &glyphs, &cGlyphs);
|
|
|
|
if (glyphs)
|
|
{
|
|
fuOptions |= ETO_GLYPH_INDEX;
|
|
uCount = cGlyphs;
|
|
}
|
|
|
|
/* Now display the reordered text if any of the arrays is valid and if BIDI_Reorder succeeded */
|
|
if ((glyphs || reordered_str) && bReorder)
|
|
{
|
|
bResult = ExtTextOutW(hdc, x, y, fuOptions, lprc,
|
|
glyphs ? (LPWSTR)glyphs : reordered_str, uCount, lpDx);
|
|
}
|
|
else
|
|
{
|
|
WARN("BIDI_Reorder failed, falling back to original string.\n");
|
|
bResult = ExtTextOutW(hdc, x, y, fuOptions, lprc, lpString, uCount, lpDx);
|
|
}
|
|
|
|
HeapFree(GetProcessHeap(), 0, glyphs);
|
|
HeapFree(GetProcessHeap(), 0, reordered_str);
|
|
|
|
return bResult;
|
|
}
|
|
|
|
return ExtTextOutW(hdc, x, y, fuOptions, lprc, lpString, uCount, lpDx);
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
DWORD
|
|
WINAPI
|
|
LpkGetCharacterPlacement(
|
|
HDC hdc,
|
|
LPCWSTR lpString,
|
|
INT uCount,
|
|
INT nMaxExtent,
|
|
LPGCP_RESULTSW lpResults,
|
|
DWORD dwFlags,
|
|
DWORD dwUnused)
|
|
{
|
|
DWORD ret = 0;
|
|
HRESULT hr;
|
|
SCRIPT_STRING_ANALYSIS ssa;
|
|
LPWORD lpGlyphs = NULL;
|
|
SIZE size;
|
|
UINT nSet, i;
|
|
INT cGlyphs;
|
|
|
|
UNREFERENCED_PARAMETER(dwUnused);
|
|
|
|
/* Sanity check (most likely a direct call) */
|
|
if (!(dwFlags & GCP_REORDER))
|
|
return GetCharacterPlacementW(hdc, lpString, uCount, nMaxExtent, lpResults, dwFlags);
|
|
|
|
nSet = (UINT)uCount;
|
|
if (nSet > lpResults->nGlyphs)
|
|
nSet = lpResults->nGlyphs;
|
|
|
|
BIDI_Reorder(hdc, lpString, uCount, dwFlags, WINE_GCPW_FORCE_LTR, lpResults->lpOutString,
|
|
nSet, lpResults->lpOrder, &lpGlyphs, &cGlyphs);
|
|
|
|
lpResults->nGlyphs = (UINT)cGlyphs;
|
|
|
|
if (lpResults->lpGlyphs)
|
|
{
|
|
if (lpGlyphs)
|
|
StringCchCopyW(lpResults->lpGlyphs, cGlyphs, lpGlyphs);
|
|
else if (lpResults->lpOutString)
|
|
GetGlyphIndicesW(hdc, lpResults->lpOutString, nSet, lpResults->lpGlyphs, 0);
|
|
}
|
|
|
|
if (lpResults->lpDx)
|
|
{
|
|
int c;
|
|
|
|
/* If glyph shaping was requested */
|
|
if (dwFlags & GCP_GLYPHSHAPE)
|
|
{
|
|
if (lpResults->lpGlyphs)
|
|
{
|
|
for (i = 0; i < lpResults->nGlyphs; i++)
|
|
{
|
|
if (GetCharWidthI(hdc, 0, 1, (WORD *)&lpResults->lpGlyphs[i], &c))
|
|
lpResults->lpDx[i] = c;
|
|
}
|
|
}
|
|
}
|
|
|
|
else
|
|
{
|
|
for (i = 0; i < nSet; i++)
|
|
{
|
|
if (GetCharWidth32W(hdc, lpResults->lpOutString[i], lpResults->lpOutString[i], &c))
|
|
lpResults->lpDx[i] = c;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (lpResults->lpCaretPos)
|
|
{
|
|
int pos = 0;
|
|
|
|
hr = ScriptStringAnalyse(hdc, lpString, nSet, (3 * nSet / 2 + 16), -1, SSA_GLYPHS, -1,
|
|
NULL, NULL, NULL, NULL, NULL, &ssa);
|
|
if (hr == S_OK)
|
|
{
|
|
for (i = 0; i < nSet; i++)
|
|
{
|
|
if (ScriptStringCPtoX(ssa, i, FALSE, &pos) == S_OK)
|
|
lpResults->lpCaretPos[i] = pos;
|
|
}
|
|
ScriptStringFree(&ssa);
|
|
}
|
|
else
|
|
{
|
|
lpResults->lpCaretPos[0] = 0;
|
|
for (i = 1; i < nSet; i++)
|
|
{
|
|
if (GetTextExtentPoint32W(hdc, &(lpString[i - 1]), 1, &size))
|
|
lpResults->lpCaretPos[i] = (pos += size.cx);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (GetTextExtentPoint32W(hdc, lpString, uCount, &size))
|
|
ret = MAKELONG(size.cx, size.cy);
|
|
|
|
HeapFree(GetProcessHeap(), 0, lpGlyphs);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Stripped down version of DrawText, can only draw single line text and Prefix underscore
|
|
* (only on the last found amperstand)
|
|
* only flags to be found to be of use in testing:
|
|
*
|
|
* DT_NOPREFIX - Draw the string as is without removal of the amperstands and without underscore
|
|
* DT_HIDEPREFIX - Draw the string without underscore
|
|
* DT_PREFIXONLY - Draw only the underscore
|
|
*
|
|
* without any of these flags the behavior is the string being drawn without the amperstands and
|
|
* with the underscore.
|
|
* user32 has an equivalent function - UserLpkPSMTextOut
|
|
*
|
|
* Note: lpString does not need to be null terminated
|
|
*/
|
|
INT WINAPI LpkPSMTextOut(HDC hdc, int x, int y, LPCWSTR lpString, int cString, DWORD dwFlags)
|
|
{
|
|
SIZE size;
|
|
TEXTMETRICW tm;
|
|
int prefix_offset, len;
|
|
LPWSTR display_str = NULL;
|
|
|
|
if (!lpString || cString <= 0)
|
|
return 0;
|
|
|
|
if (dwFlags & DT_NOPREFIX)
|
|
{
|
|
LpkExtTextOut(hdc, x, y, 0, NULL, lpString, cString, NULL, 0);
|
|
GetTextExtentPointW(hdc, lpString, cString, &size);
|
|
return size.cx;
|
|
}
|
|
|
|
display_str = HeapAlloc(GetProcessHeap(), 0, (cString + 1) * sizeof(WCHAR));
|
|
|
|
if (!display_str)
|
|
return 0;
|
|
|
|
PSM_PrepareToDraw(lpString, cString, display_str, &len);
|
|
|
|
if (!(dwFlags & DT_PREFIXONLY))
|
|
LpkExtTextOut(hdc, x, y, 0, NULL, display_str, len, NULL, 0);
|
|
|
|
if (!(dwFlags & DT_HIDEPREFIX))
|
|
{
|
|
prefix_offset = PSM_FindLastPrefix(lpString, cString);
|
|
GetTextMetricsW(hdc, &tm);
|
|
LPK_DrawUnderscore(hdc, x, y + tm.tmAscent + 1, display_str, len, prefix_offset);
|
|
}
|
|
|
|
GetTextExtentPointW(hdc, display_str, len + 1, &size);
|
|
HeapFree(GetProcessHeap(), 0, display_str);
|
|
|
|
return size.cx;
|
|
}
|