Created
December 3, 2023 23:30
-
-
Save delfigamer/6532ce6bc717e1fddb0994519aa8b580 to your computer and use it in GitHub Desktop.
Examples of using WM_CHAR for text input in a Win32 application, a simple and a partially IME-aware version
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define WIN32_LEAN_AND_MEAN | |
#define _CRT_SECURE_NO_WARNINGS | |
#define _SCL_SECURE_NO_WARNINGS | |
#include <Windows.h> | |
#include <cstdio> | |
#include <utility> | |
#include <string> | |
#include <vector> | |
struct TextEdit { | |
std::wstring buffer; | |
int caret = 0; | |
wchar_t const* Buffer() { | |
return buffer.data(); | |
} | |
int Length() { | |
return buffer.size(); | |
} | |
void NormalizeCaret() { | |
if (caret < 0) { | |
caret = 0; | |
} else if (caret > Length()) { | |
caret = buffer.size(); | |
} | |
} | |
void Insert(wchar_t ch) { | |
NormalizeCaret(); | |
buffer.insert(caret, 1, ch); | |
caret += 1; | |
} | |
void Insert(std::wstring const& str) { | |
NormalizeCaret(); | |
if (!str.empty()) { | |
buffer.insert(caret, str); | |
caret += str.size(); | |
} | |
} | |
void EraseLeft() { | |
NormalizeCaret(); | |
if (caret > 0) { | |
buffer.erase(caret - 1, 1); | |
caret -= 1; | |
} | |
} | |
void EraseRight() { | |
NormalizeCaret(); | |
if (caret < Length()) { | |
buffer.erase(caret, 1); | |
} | |
} | |
void MoveLeft() { | |
if (caret > 0) { | |
caret -= 1; | |
} | |
} | |
void MoveRight() { | |
if (caret < Length()) { | |
caret += 1; | |
} | |
} | |
void MoveHome() { | |
caret = 0; | |
} | |
void MoveEnd() { | |
caret = buffer.size(); | |
} | |
}; | |
TextEdit Current; | |
HPEN CaretPen; | |
void WindowPaint(HWND hwnd) { | |
RECT clientrect; | |
GetClientRect(hwnd, &clientrect); | |
SIZE wndsize = {clientrect.right - clientrect.left, clientrect.bottom - clientrect.top}; | |
PAINTSTRUCT ps; | |
HDC wnddc = BeginPaint(hwnd, &ps); | |
HBITMAP bitmap = CreateCompatibleBitmap(wnddc, wndsize.cx, wndsize.cy); | |
HDC dc = CreateCompatibleDC(wnddc); | |
SelectObject(dc, bitmap); | |
RECT wndrect = {0, 0, wndsize.cx, wndsize.cy}; | |
HBRUSH brush = CreateSolidBrush(RGB(255, 255, 255)); | |
FillRect(dc, &wndrect, brush); | |
TEXTMETRICW textmetric; | |
GetTextMetricsW(dc, &textmetric); | |
int currentx = textmetric.tmHeight; | |
int currenty = textmetric.tmHeight; | |
SIZE textsize; | |
TextOutW(dc, currentx, currenty, Current.Buffer(), Current.Length()); | |
GetTextExtentPointW(dc, Current.Buffer(), Current.caret, &textsize); | |
SelectObject(dc, CaretPen); | |
MoveToEx(dc, currentx + textsize.cx, currenty, nullptr); | |
LineTo(dc, currentx + textsize.cx, currenty + textmetric.tmHeight); | |
BitBlt(wnddc, 0, 0, wndsize.cx, wndsize.cy, dc, 0, 0, SRCCOPY); | |
DeleteObject(brush); | |
DeleteDC(dc); | |
DeleteObject(bitmap); | |
EndPaint(hwnd, &ps); | |
} | |
LRESULT CALLBACK WindowProc( | |
HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam | |
) { | |
switch (uMsg) { | |
case WM_CREATE: | |
CaretPen = CreatePen(PS_SOLID, 0, RGB(0, 0, 0)); | |
return 0; | |
case WM_DESTROY: | |
DeleteObject(CaretPen); | |
PostQuitMessage(0); | |
return 0; | |
case WM_CHAR: | |
wprintf(L"WM_CHAR %#4llx'%lc' %3lli\n", wParam, (wchar_t)wParam, (lParam >> 16) & 0xff); | |
if (wParam == 8) { | |
Current.EraseLeft(); | |
} else if (wParam >= 32) { | |
Current.Insert(wParam); | |
} | |
InvalidateRect(hwnd, nullptr, false); | |
return 0; | |
case WM_KEYDOWN: | |
wprintf(L"WM_KEYDOWN %4lli %3lli\n", wParam, (lParam >> 16) & 0xff); | |
if (wParam == VK_LEFT) { | |
Current.MoveLeft(); | |
InvalidateRect(hwnd, nullptr, false); | |
} else if (wParam == VK_RIGHT) { | |
Current.MoveRight(); | |
InvalidateRect(hwnd, nullptr, false); | |
} else if (wParam == VK_HOME || wParam == VK_PRIOR || wParam == VK_UP) { | |
Current.MoveHome(); | |
InvalidateRect(hwnd, nullptr, false); | |
} else if (wParam == VK_END || wParam == VK_NEXT || wParam == VK_DOWN) { | |
Current.MoveEnd(); | |
InvalidateRect(hwnd, nullptr, false); | |
} else if (wParam == VK_DELETE) { | |
Current.EraseRight(); | |
InvalidateRect(hwnd, nullptr, false); | |
} | |
return 0; | |
case WM_KEYUP: | |
return 0; | |
case WM_PAINT: | |
WindowPaint(hwnd); | |
return 0; | |
default: | |
return DefWindowProcW(hwnd, uMsg, wParam, lParam); | |
} | |
} | |
int CALLBACK WinMain( | |
HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { | |
AllocConsole(); | |
freopen("CONOUT$", "w", stdout); | |
const DWORD WindowStyle = WS_OVERLAPPEDWINDOW; | |
WNDCLASSW wndclass; | |
wndclass.style = CS_VREDRAW | CS_HREDRAW; | |
wndclass.lpfnWndProc = &WindowProc; | |
wndclass.cbClsExtra = 8; | |
wndclass.cbWndExtra = 0; | |
wndclass.hInstance = hInstance; | |
wndclass.hIcon = LoadIcon(0, IDI_APPLICATION); | |
wndclass.hCursor = LoadCursor(0, IDC_ARROW); | |
wndclass.hbrBackground = 0; | |
wndclass.lpszMenuName = 0; | |
wndclass.lpszClassName = L"MyWindowClass"; | |
ATOM wndclassatom = RegisterClassW(&wndclass); | |
RECT WindowRect = {0, 0, 800, 600}; | |
AdjustWindowRect( | |
&WindowRect, WindowStyle, false); | |
HWND hwnd = CreateWindowW( | |
(LPCWSTR)wndclassatom, | |
L"window", | |
WindowStyle, | |
20, | |
20, | |
WindowRect.right - WindowRect.left, | |
WindowRect.bottom - WindowRect.top, | |
0, | |
0, | |
hInstance, | |
0); | |
ShowWindow(hwnd, SW_SHOW); | |
UpdateWindow(hwnd); | |
MSG message = {0, 0, 0, 0}; | |
while (GetMessageW(&message, nullptr, 0, 0)) { | |
TranslateMessage(&message); | |
DispatchMessageW(&message); | |
} | |
return 0; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define WIN32_LEAN_AND_MEAN | |
#define _CRT_SECURE_NO_WARNINGS | |
#define _SCL_SECURE_NO_WARNINGS | |
#include <Windows.h> | |
#include <cstdio> | |
#include <utility> | |
#include <string> | |
#include <vector> | |
struct TextEdit | |
{ | |
std::wstring buffer; | |
int caret = 0; | |
wchar_t const* Buffer() | |
{ | |
return buffer.data(); | |
} | |
int Length() | |
{ | |
return buffer.size(); | |
} | |
void NormalizeCaret() | |
{ | |
if (caret < 0) { | |
caret = 0; | |
} else if (caret > Length()) { | |
caret = buffer.size(); | |
} | |
} | |
void Insert(wchar_t ch) | |
{ | |
NormalizeCaret(); | |
buffer.insert(caret, 1, ch); | |
caret += 1; | |
} | |
void Insert(std::wstring const& str) | |
{ | |
NormalizeCaret(); | |
if (!str.empty()) { | |
buffer.insert(caret, str); | |
caret += str.size(); | |
} | |
} | |
void EraseLeft() | |
{ | |
NormalizeCaret(); | |
if (caret > 0) { | |
buffer.erase(caret - 1, 1); | |
caret -= 1; | |
} | |
} | |
void EraseRight() | |
{ | |
NormalizeCaret(); | |
if (caret < Length()) { | |
buffer.erase(caret); | |
} | |
} | |
void MoveLeft() | |
{ | |
if (caret > 0) { | |
caret -= 1; | |
} | |
} | |
void MoveRight() | |
{ | |
if (caret < Length()) { | |
caret += 1; | |
} | |
} | |
void MoveHome() | |
{ | |
caret = 0; | |
} | |
void MoveEnd() | |
{ | |
caret = buffer.size(); | |
} | |
}; | |
enum class CompositionAttribute | |
{ | |
Input, | |
Selected, | |
Converted, | |
}; | |
struct CompositionClause | |
{ | |
std::wstring text; | |
CompositionAttribute attr; | |
}; | |
struct CompositionState | |
{ | |
std::vector<CompositionClause> clauses; | |
int cursorclause; | |
int cursorpos; | |
std::wstring UpdateAndGetResult(HWND hwnd) | |
{ | |
cursorclause = 0; | |
cursorpos = 0; | |
HIMC imc = ImmGetContext(hwnd); | |
std::wstring result; | |
result.resize(ImmGetCompositionStringW(imc, GCS_RESULTSTR, nullptr, 0) / 2); | |
if (!result.empty()) { | |
ImmGetCompositionStringW(imc, GCS_RESULTSTR, (wchar_t*)result.data(), result.size() * 2); | |
} | |
std::vector<wchar_t> str; | |
std::vector<BYTE> attr; | |
std::vector<DWORD> pos; | |
str.resize(ImmGetCompositionStringW(imc, GCS_COMPSTR, nullptr, 0) / 2); | |
if (str.empty()) { | |
clauses.clear(); | |
} | |
ImmGetCompositionStringW(imc, GCS_COMPSTR, str.data(), str.size() * 2); | |
attr.resize(ImmGetCompositionStringW(imc, GCS_COMPATTR, nullptr, 0)); | |
ImmGetCompositionStringW(imc, GCS_COMPATTR, attr.data(), attr.size()); | |
pos.resize(ImmGetCompositionStringW(imc, GCS_COMPCLAUSE, nullptr, 0) / 4); | |
ImmGetCompositionStringW(imc, GCS_COMPCLAUSE, pos.data(), pos.size() * 4); | |
if (pos.size() < 1) { | |
return result; | |
} | |
clauses.resize(pos.size() - 1); | |
for (int i = 0; i < (int)clauses.size(); ++i) { | |
int begin = pos[i]; | |
int end = pos[i + 1]; | |
if (begin >= 0 && end <= (int)str.size() && begin < end) { | |
clauses[i].text.resize(end - begin); | |
std::copy(str.data() + begin, str.data() + end, (wchar_t*)clauses[i].text.data()); | |
} else { | |
clauses[i].text.clear(); | |
} | |
clauses[i].attr = CompositionAttribute::Input; | |
if (begin >= 0 && begin < (int)attr.size()) { | |
switch (attr[begin]) { | |
case ATTR_CONVERTED: | |
clauses[i].attr = CompositionAttribute::Converted; | |
break; | |
case ATTR_TARGET_CONVERTED: | |
clauses[i].attr = CompositionAttribute::Selected; | |
break; | |
} | |
} | |
} | |
int cursor = ImmGetCompositionStringW(imc, GCS_CURSORPOS, nullptr, 0) & 0xffff; | |
for (int i = 0; i < (int)clauses.size(); ++i) { | |
int length = clauses[i].text.size(); | |
if (cursor >= 0 && cursor <= length) { | |
cursorclause = i; | |
cursorpos = cursor; | |
break; | |
} else { | |
cursor -= length; | |
} | |
} | |
ImmReleaseContext(hwnd, imc); | |
return result; | |
} | |
}; | |
TextEdit Current; | |
CompositionState CurrentComposition; | |
HPEN CaretPen; | |
HPEN InputAttrPen; | |
HPEN SelectedAttrPen; | |
HPEN ConvertedAttrPen; | |
void WindowPaint(HWND hwnd) | |
{ | |
RECT clientrect; | |
GetClientRect(hwnd, &clientrect); | |
SIZE wndsize = { clientrect.right - clientrect.left, clientrect.bottom - clientrect.top }; | |
PAINTSTRUCT ps; | |
HDC wnddc = BeginPaint(hwnd, &ps); | |
HBITMAP bitmap = CreateCompatibleBitmap(wnddc, wndsize.cx, wndsize.cy); | |
HDC dc = CreateCompatibleDC(wnddc); | |
SelectObject(dc, bitmap); | |
RECT wndrect = { 0, 0, wndsize.cx, wndsize.cy }; | |
HBRUSH brush = CreateSolidBrush(RGB(255, 255, 255)); | |
FillRect(dc, &wndrect, brush); | |
TEXTMETRICW textmetric; | |
GetTextMetricsW(dc, &textmetric); | |
int currentx = textmetric.tmHeight; | |
int currenty = textmetric.tmHeight; | |
SIZE textsize; | |
if (CurrentComposition.clauses.empty()) { | |
TextOutW(dc, currentx, currenty, Current.Buffer(), Current.Length()); | |
GetTextExtentPointW(dc, Current.Buffer(), Current.caret, &textsize); | |
SelectObject(dc, CaretPen); | |
MoveToEx(dc, currentx + textsize.cx, currenty, nullptr); | |
LineTo(dc, currentx + textsize.cx, currenty + textmetric.tmHeight); | |
} else { | |
TextOutW(dc, currentx, currenty, Current.Buffer(), Current.caret); | |
GetTextExtentPointW(dc, Current.Buffer(), Current.caret, &textsize); | |
currentx += textsize.cx; | |
for (int i = 0; i < (int)CurrentComposition.clauses.size(); ++i) { | |
CompositionClause const& clause = CurrentComposition.clauses[i]; | |
TextOutW(dc, currentx, currenty, clause.text.data(), clause.text.size()); | |
if (CurrentComposition.cursorclause == i) { | |
GetTextExtentPointW(dc, clause.text.data(), CurrentComposition.cursorpos, &textsize); | |
SelectObject(dc, CaretPen); | |
MoveToEx(dc, currentx + textsize.cx, currenty, nullptr); | |
LineTo(dc, currentx + textsize.cx, currenty + textmetric.tmHeight); | |
} | |
GetTextExtentPointW(dc, clause.text.data(), clause.text.size(), &textsize); | |
switch (clause.attr) { | |
case CompositionAttribute::Input: | |
SelectObject(dc, InputAttrPen); | |
break; | |
case CompositionAttribute::Selected: | |
SelectObject(dc, SelectedAttrPen); | |
break; | |
case CompositionAttribute::Converted: | |
SelectObject(dc, ConvertedAttrPen); | |
break; | |
} | |
MoveToEx(dc, currentx + 1, currenty + textmetric.tmHeight + 1, nullptr); | |
LineTo(dc, currentx + textsize.cx - 1, currenty + textmetric.tmHeight + 1); | |
currentx += textsize.cx; | |
} | |
TextOutW(dc, currentx, currenty, Current.Buffer() + Current.caret, Current.Length() - Current.caret); | |
} | |
BitBlt(wnddc, 0, 0, wndsize.cx, wndsize.cy, dc, 0, 0, SRCCOPY); | |
DeleteObject(brush); | |
DeleteDC(dc); | |
DeleteObject(bitmap); | |
EndPaint(hwnd, &ps); | |
} | |
void ImrQueryCharPosition(HWND hwnd, IMECHARPOSITION* chpos) | |
{ | |
int charpos = chpos->dwCharPos; | |
wprintf(L"WM_IME_REQUEST IMR_QUERYCHARPOSITION %i\n", charpos); | |
CurrentComposition.UpdateAndGetResult(hwnd); | |
RECT clientrect; | |
GetClientRect(hwnd, &clientrect); | |
HDC dc = GetDC(hwnd); | |
TEXTMETRICW textmetric; | |
GetTextMetricsW(dc, &textmetric); | |
int currentx = textmetric.tmHeight; | |
int currenty = textmetric.tmHeight; | |
SIZE textsize; | |
GetTextExtentPointW(dc, Current.Buffer(), Current.caret, &textsize); | |
currentx += textsize.cx; | |
for (int i = 0; i < (int)CurrentComposition.clauses.size(); ++i) { | |
CompositionClause const& clause = CurrentComposition.clauses[i]; | |
if (charpos < (int)clause.text.size()) { | |
GetTextExtentPointW(dc, clause.text.data(), charpos, &textsize); | |
currentx += textsize.cx; | |
break; | |
} else { | |
GetTextExtentPointW(dc, clause.text.data(), clause.text.size(), &textsize); | |
currentx += textsize.cx; | |
charpos -= (int)clause.text.size(); | |
} | |
} | |
POINT pt = { currentx, currenty + 5 }; | |
ClientToScreen(hwnd, &pt); | |
chpos->pt = pt; | |
chpos->cLineHeight = textmetric.tmHeight; | |
POINT client1 = { 0, 0 }; | |
POINT client2 = { clientrect.right - clientrect.left, currenty + textmetric.tmHeight }; | |
ClientToScreen(hwnd, &client1); | |
ClientToScreen(hwnd, &client2); | |
chpos->rcDocument = { client1.x, client1.y, client2.x, client2.y }; | |
ReleaseDC(hwnd, dc); | |
} | |
LRESULT CALLBACK WindowProc( | |
HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) | |
{ | |
switch (uMsg) { | |
case WM_CREATE: | |
CaretPen = CreatePen(PS_SOLID, 0, RGB(0, 0, 0)); | |
InputAttrPen = CreatePen(PS_DOT, 0, RGB(0, 0, 0)); | |
SelectedAttrPen = CreatePen(PS_SOLID, 2, RGB(0, 0, 0)); | |
ConvertedAttrPen = CreatePen(PS_SOLID, 0, RGB(0, 0, 0)); | |
return 0; | |
case WM_DESTROY: | |
DeleteObject(CaretPen); | |
DeleteObject(InputAttrPen); | |
DeleteObject(SelectedAttrPen); | |
DeleteObject(ConvertedAttrPen); | |
PostQuitMessage(0); | |
return 0; | |
case WM_CHAR: | |
wprintf(L"WM_CHAR %#4x'%lc' %3i\n", wParam, (wchar_t)wParam, (lParam >> 16) & 0xff); | |
if (wParam == 8) { | |
Current.EraseLeft(); | |
} else if (wParam >= 32) { | |
Current.Insert(wParam); | |
} | |
InvalidateRect(hwnd, nullptr, false); | |
return 0; | |
case WM_KEYDOWN: | |
wprintf(L"WM_KEYDOWN %4i %3i\n", wParam, (lParam >> 16) & 0xff); | |
if (wParam == VK_LEFT) { | |
Current.MoveLeft(); | |
InvalidateRect(hwnd, nullptr, false); | |
} else if (wParam == VK_RIGHT) { | |
Current.MoveRight(); | |
InvalidateRect(hwnd, nullptr, false); | |
} else if (wParam == VK_HOME || wParam == VK_PRIOR || wParam == VK_UP) { | |
Current.MoveHome(); | |
InvalidateRect(hwnd, nullptr, false); | |
} else if (wParam == VK_END || wParam == VK_NEXT || wParam == VK_DOWN) { | |
Current.MoveEnd(); | |
InvalidateRect(hwnd, nullptr, false); | |
} else if (wParam == VK_DELETE) { | |
Current.EraseRight(); | |
InvalidateRect(hwnd, nullptr, false); | |
} | |
return 0; | |
case WM_KEYUP: | |
return 0; | |
case WM_IME_SETCONTEXT: | |
wprintf(L"WM_IME_SETCONTEXT %i\n", wParam); | |
return DefWindowProcW(hwnd, uMsg, wParam, lParam); | |
case WM_IME_STARTCOMPOSITION: | |
wprintf(L"WM_IME_STARTCOMPOSITION\n"); | |
Current.Insert(CurrentComposition.UpdateAndGetResult(hwnd)); | |
InvalidateRect(hwnd, nullptr, false); | |
return 0; | |
case WM_IME_COMPOSITION: | |
wprintf(L"WM_IME_COMPOSITION\n"); | |
Current.Insert(CurrentComposition.UpdateAndGetResult(hwnd)); | |
InvalidateRect(hwnd, nullptr, false); | |
return 0; | |
case WM_IME_ENDCOMPOSITION: | |
wprintf(L"WM_IME_ENDCOMPOSITION\n"); | |
CurrentComposition.clauses.clear(); | |
InvalidateRect(hwnd, nullptr, false); | |
return 0; | |
case WM_IME_SELECT: | |
wprintf(L"WM_IME_SELECT %i\n", wParam); | |
return DefWindowProcW(hwnd, uMsg, wParam, lParam); | |
case WM_IME_REQUEST: | |
switch (wParam) { | |
case IMR_CANDIDATEWINDOW: | |
wprintf(L"WM_IME_REQUEST IMR_CANDIDATEWINDOW\n"); | |
return DefWindowProcW(hwnd, uMsg, wParam, lParam); | |
case IMR_COMPOSITIONFONT: | |
wprintf(L"WM_IME_REQUEST IMR_COMPOSITIONFONT\n"); | |
return DefWindowProcW(hwnd, uMsg, wParam, lParam); | |
case IMR_COMPOSITIONWINDOW: | |
wprintf(L"WM_IME_REQUEST IMR_COMPOSITIONWINDOW\n"); | |
return DefWindowProcW(hwnd, uMsg, wParam, lParam); | |
case IMR_CONFIRMRECONVERTSTRING: | |
wprintf(L"WM_IME_REQUEST IMR_CONFIRMRECONVERTSTRING\n"); | |
return DefWindowProcW(hwnd, uMsg, wParam, lParam); | |
case IMR_DOCUMENTFEED: | |
wprintf(L"WM_IME_REQUEST IMR_DOCUMENTFEED\n"); | |
return 0; | |
case IMR_QUERYCHARPOSITION: | |
ImrQueryCharPosition(hwnd, (IMECHARPOSITION*)lParam); | |
return true; | |
case IMR_RECONVERTSTRING: | |
wprintf(L"WM_IME_REQUEST IMR_RECONVERTSTRING\n"); | |
return DefWindowProcW(hwnd, uMsg, wParam, lParam); | |
default: | |
wprintf(L"WM_IME_REQUEST %i\n", wParam); | |
return DefWindowProcW(hwnd, uMsg, wParam, lParam); | |
} | |
case WM_IME_NOTIFY: | |
switch (wParam) { | |
case IMN_CHANGECANDIDATE: | |
wprintf(L"WM_IME_NOTIFY IMN_CHANGECANDIDATE\n"); | |
return 0; | |
case IMN_CLOSECANDIDATE: | |
wprintf(L"WM_IME_NOTIFY IMN_CLOSECANDIDATE\n"); | |
return 0; | |
case IMN_CLOSESTATUSWINDOW: | |
wprintf(L"WM_IME_NOTIFY IMN_CLOSESTATUSWINDOW\n"); | |
return 0; | |
case IMN_GUIDELINE: | |
wprintf(L"WM_IME_NOTIFY IMN_GUIDELINE\n"); | |
return 0; | |
case IMN_OPENCANDIDATE: | |
wprintf(L"WM_IME_NOTIFY IMN_OPENCANDIDATE\n"); | |
return 0; | |
case IMN_OPENSTATUSWINDOW: | |
wprintf(L"WM_IME_NOTIFY IMN_OPENSTATUSWINDOW\n"); | |
return 0; | |
case IMN_SETCANDIDATEPOS: | |
wprintf(L"WM_IME_NOTIFY IMN_SETCANDIDATEPOS\n"); | |
return 0; | |
case IMN_SETCOMPOSITIONFONT: | |
wprintf(L"WM_IME_NOTIFY IMN_SETCOMPOSITIONFONT\n"); | |
return 0; | |
case IMN_SETCOMPOSITIONWINDOW: | |
wprintf(L"WM_IME_NOTIFY IMN_SETCOMPOSITIONWINDOW\n"); | |
return 0; | |
case IMN_SETCONVERSIONMODE: | |
wprintf(L"WM_IME_NOTIFY IMN_SETCONVERSIONMODE\n"); | |
return 0; | |
case IMN_SETOPENSTATUS: | |
wprintf(L"WM_IME_NOTIFY IMN_SETOPENSTATUS\n"); | |
return 0; | |
case IMN_SETSENTENCEMODE: | |
wprintf(L"WM_IME_NOTIFY IMN_SETSENTENCEMODE\n"); | |
return 0; | |
case IMN_SETSTATUSWINDOWPOS: | |
wprintf(L"WM_IME_NOTIFY IMN_SETSTATUSWINDOWPOS\n"); | |
return 0; | |
default: | |
wprintf(L"WM_IME_NOTIFY %i\n", wParam); | |
return DefWindowProcW(hwnd, uMsg, wParam, lParam); | |
} | |
case WM_PAINT: | |
WindowPaint(hwnd); | |
return 0; | |
default: | |
return DefWindowProcW(hwnd, uMsg, wParam, lParam); | |
} | |
} | |
int CALLBACK WinMain( | |
HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) | |
{ | |
AllocConsole(); | |
freopen("CONOUT$", "w", stdout); | |
const DWORD WindowStyle = WS_OVERLAPPEDWINDOW; | |
WNDCLASSW wndclass; | |
wndclass.style = CS_VREDRAW | CS_HREDRAW; | |
wndclass.lpfnWndProc = &WindowProc; | |
wndclass.cbClsExtra = 8; | |
wndclass.cbWndExtra = 0; | |
wndclass.hInstance = hInstance; | |
wndclass.hIcon = LoadIcon(0, IDI_APPLICATION); | |
wndclass.hCursor = LoadCursor(0, IDC_ARROW); | |
wndclass.hbrBackground = 0; | |
wndclass.lpszMenuName = 0; | |
wndclass.lpszClassName = L"MyWindowClass"; | |
ATOM wndclassatom = RegisterClassW(&wndclass); | |
RECT WindowRect = { 0, 0, 800, 600 }; | |
AdjustWindowRect( | |
&WindowRect, WindowStyle, false); | |
HWND hwnd = CreateWindowW( | |
(LPCWSTR)wndclassatom, | |
L"window", | |
WindowStyle, | |
20, | |
20, | |
WindowRect.right - WindowRect.left, | |
WindowRect.bottom - WindowRect.top, | |
0, | |
0, | |
hInstance, | |
0); | |
ShowWindow(hwnd, SW_SHOW); | |
UpdateWindow(hwnd); | |
MSG message = { 0, 0, 0, 0 }; | |
while (GetMessageW(&message, nullptr, 0, 0)) { | |
TranslateMessage(&message); | |
DispatchMessageW(&message); | |
} | |
return 0; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment