Compare commits

..

No commits in common. "6681cddf7250d6372392b8a38d3a0bdeb1a9acb3" and "04bf9f64ffafcc285a7523d3e9781321d6b1d802" have entirely different histories.

3 changed files with 121 additions and 113 deletions

View File

@ -1,6 +1,6 @@
@echo off @echo off
set libs=User32.lib Gdi32.lib set libs=User32.lib Dwmapi.lib Gdi32.lib
set opts=-FC -GR- -EHa- -nologo -Zi set opts=-FC -GR- -EHa- -nologo -Zi
set code=%cd% set code=%cd%

View File

@ -12,7 +12,7 @@ blacklist_patterns = {
load_paths_base = { load_paths_base = {
{ ".", .relative = true, .recursive = true, }, { ".", .relative = true, .recursive = true, },
}; };
load_paths = { .win = load_paths_base, }; load_paths = { { load_paths_base, .os = "win", }, };
commands = { commands = {
.build = .build =

View File

@ -1,14 +1,14 @@
/* /*
** Win32 Typing Test Program ** Win32 Typing Test Program
** v1.1.0 - Jan 19th 2024 ** v1.0.0 - Jan 19th 2024
** by Allen Webster allenw@mr4th.com ** by Allen Webster allenw@mr4th.com
** **
** public domain example program ** public domain example program
** NO WARRANTY IMPLIED; USE AT YOUR OWN RISK ** NO WARRANTY IMPLIED; USE AT YOUR OWN RISK
** **
** **
** Tested on: ** Tested on:
** Windows 10 (English, Latvian, Emojis) ** Windows 10 English
** **
** This is an example and a tool for gathering information about ** This is an example and a tool for gathering information about
** Win32 text input APIs. ** Win32 text input APIs.
@ -25,6 +25,9 @@
#include <Windows.h> #include <Windows.h>
// only for vsync in GDI
#include <dwmapi.h>
typedef LONG S32; typedef LONG S32;
typedef USHORT U16; typedef USHORT U16;
typedef ULONG U32; typedef ULONG U32;
@ -44,9 +47,6 @@ static void AppTypeCodepoint(U32 cp);
static BOOL keep_running = 1; static BOOL keep_running = 1;
static WCHAR lo_surrogate = 0;
static WCHAR hi_surrogate = 0;
static LRESULT static LRESULT
WindowProc(HWND wnd, WindowProc(HWND wnd,
UINT msg, UINT msg,
@ -55,53 +55,51 @@ WindowProc(HWND wnd,
LRESULT result = 0; LRESULT result = 0;
switch (msg){ switch (msg){
case WM_PAINT:
{
PAINTSTRUCT ps;
BeginPaint(wnd, &ps);
AppRender(wnd);
EndPaint(wnd, &ps);
}break;
case WM_CHAR: case WM_CHAR:
{ {
// When a WM_CHAR generally represents text input that // When a user uses IME, their input is handled by
// should be inserted, but there are a few twists. // WM_IME_CHAR messages, but additional WM_CHAR messages
// get generated in the process.
// //
// Sometimes WM_CHAR is triggered by controls that are // I'm not entirely sure what the best thing is to do with
// not text input, like Ctrl + Letter. To filter out // them - but they appear to always be messages I want to ignore.
// these cases we ignore values less than ' ' (0x20)
// and the value 0x7F. See WM_KEYDOWN for a little
// more on this issue.
// //
// When the user types a character that does not fit // They also appear to be the only case where a WM_CHAR
// in a single U16, a pair of WM_CHAR messages are sent. // message has a scan code of zero.
// Each WM_CHAR message in the pair contains a
// "surrogate" (defined by UTF16) one "high" surrogate
// followed by a "low" surrogate. A pair of surrogates
// translates into a single typed codepoint.
// //
// (Actually the docs don't clearly say one order // Therefore my best solution so far is to filter out WM_CHAR
// or the other - I just support both) // messages that have a zero scan code.
U32 c = (wparam & 0xFFFF); U32 scan_code = ((lparam >> 16) & 0xFF);
if (c >= ' ' && c != 0x7F){ if (scan_code != 0){
U32 cp16 = (wparam & 0xFFFF);
if (IS_LOW_SURROGATE(c)){ if (cp16 >= ' ' && cp16 != 0x7F){
lo_surrogate = c; AppTypeCodepoint(cp16);
} }
else if (IS_HIGH_SURROGATE(c)){ }
hi_surrogate = c; }break;
}
else{ case WM_IME_CHAR:
AppTypeCodepoint(c); {
lo_surrogate = 0; U32 cp16 = (wparam & 0xFFFF);
hi_surrogate = 0; if (cp16 >= ' ' && cp16 != 0x7F){
} AppTypeCodepoint(cp16);
}
if (hi_surrogate != 0 && lo_surrogate != 0){ }break;
if (IS_SURROGATE_PAIR(hi_surrogate, lo_surrogate)){
U32 cp = (0x10000 + case WM_UNICHAR:
((hi_surrogate & 0x3FF) << 10) + {
(lo_surrogate & 0x3FF) ); U32 cp32 = wparam;
AppTypeCodepoint(cp); if (cp32 >= ' ' && cp32 != 0x7F){
} AppTypeCodepoint(cp32);
hi_surrogate = 0;
lo_surrogate = 0;
}
} }
}break; }break;
@ -145,14 +143,6 @@ WindowProc(HWND wnd,
} }
}break; }break;
case WM_PAINT:
{
PAINTSTRUCT ps;
BeginPaint(wnd, &ps);
AppRender(wnd);
EndPaint(wnd, &ps);
}break;
case WM_CLOSE: case WM_CLOSE:
{ {
keep_running = 0; keep_running = 0;
@ -168,63 +158,6 @@ WindowProc(HWND wnd,
} }
////////////////////////////////
// Main
static BOOL composition_enabled = 0;
int
WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nShowCmd){
WNDCLASSW window_class = {0};
window_class.style = CS_HREDRAW|CS_VREDRAW;
window_class.lpfnWndProc = WindowProc;
window_class.hInstance = hInstance;
window_class.hCursor = LoadCursorA(0, IDC_ARROW);
window_class.lpszClassName = L"WindowClass";
ATOM atom = RegisterClassW(&window_class);
if (atom == 0){
MessageBoxW(0, (WCHAR*)L"RegisterClassW failed\n", (WCHAR*)L"Fatal", MB_OK);
ExitProcess(1);
}
DWORD style = WS_OVERLAPPEDWINDOW;
HWND wnd = CreateWindowW(L"WindowClass", L"Type Test", style,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
0, 0, hInstance, 0);
// init
AppInit(wnd);
// main loop
ShowWindow(wnd, SW_SHOW);
keep_running = 1;
for (;keep_running;){
// Handle Message Queue
BOOL wait = 1;
MSG msg;
for (;(wait?GetMessageW(&msg, 0, 0, 0):
PeekMessageW(&msg, 0, 0, 0, PM_REMOVE));){
wait = 0;
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
// render
AppRender(wnd);
// sleep
Sleep(33);
}
}
//////////////////////////////// ////////////////////////////////
// App Implementation // App Implementation
@ -338,4 +271,79 @@ AppRender(HWND wnd){
} }
////////////////////////////////
// Main
static BOOL composition_enabled = 0;
int
WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nShowCmd){
// window setup
#define WINDOW_CLASS L"InputWindowClass"
WNDCLASSW window_class = {0};
window_class.style = CS_HREDRAW|CS_VREDRAW;
window_class.lpfnWndProc = WindowProc;
window_class.hInstance = hInstance;
window_class.hCursor = LoadCursorA(0, IDC_ARROW);
window_class.lpszClassName = WINDOW_CLASS;
ATOM atom = RegisterClassW(&window_class);
if (atom == 0){
MessageBoxW(0, (WCHAR*)L"RegisterClassW failed\n", (WCHAR*)L"Fatal", MB_OK);
ExitProcess(1);
}
DWORD style = WS_OVERLAPPEDWINDOW;
HWND wnd = CreateWindowW(WINDOW_CLASS, L"Type Test", style,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
0, 0, hInstance, 0);
// setup vsync
if (DwmIsCompositionEnabled(&composition_enabled) != S_OK){
composition_enabled = 0;
}
if (!composition_enabled){
MessageBoxW(0, (WCHAR*)L"No Vsync\n", (WCHAR*)L"Issue", MB_OK);
}
// init
AppInit(wnd);
// main loop
ShowWindow(wnd, SW_SHOW);
keep_running = 1;
for (;keep_running;){
// flush message queue
{
BOOL wait_for_message = 1;
MSG msg;
for (;(wait_for_message?
GetMessageW(&msg, 0, 0, 0):
PeekMessageW(&msg, 0, 0, 0, PM_REMOVE));){
wait_for_message = 0;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
// render
AppRender(wnd);
// vsync or sleep
if (composition_enabled){
DwmFlush();
}
else{
Sleep(33);
}
}
}
//$ graphical // //$ graphical //