static LRESULT WindowProcedure(WindowHandle window, WindowMessage message, WPARAM wParam, LPARAM lParam) { switch (message) { case WindowMessage.InputLanguageChange: dwCharSet = (CharacterSet)(uint)wParam; goto case WindowMessage.Create; case WindowMessage.Create: using (DeviceContext dc = window.GetDeviceContext()) { using (FontHandle font = Windows.CreateFont(0, 0, 0, 0, FontWeight.DoNotCare, false, false, false, dwCharSet, OutputPrecision.Default, ClippingPrecision.Default, Quality.Default, FontPitch.FixedPitch, FontFamily.DoNotCare, null)) { dc.SelectObject(font); dc.GetTextMetrics(out TEXTMETRIC tm); cxChar = tm.tmAveCharWidth; cyChar = tm.tmHeight; dc.SelectObject(StockFont.System); } } goto CalculateSize; case WindowMessage.Size: cxClient = lParam.LowWord; cyClient = lParam.HighWord; CalculateSize: // calculate window size in characters cxBuffer = Math.Max(1, cxClient / cxChar); cyBuffer = Math.Max(1, cyClient / cyChar); // allocate memory for buffer and clear it pBuffer = new char[cxBuffer, cyBuffer]; // set caret to upper left corner xCaret = 0; yCaret = 0; if (window == Windows.GetFocus()) { Windows.SetCaretPosition(xCaret * cxChar, yCaret * cyChar); } window.Invalidate(true); return(0); case WindowMessage.SetFocus: // create and show the caret window.CreateCaret(null, cxChar, cyChar); Windows.SetCaretPosition(xCaret * cxChar, yCaret * cyChar); window.ShowCaret(); return(0); case WindowMessage.KillFocus: // hide and destroy the caret window.HideCaret(); Windows.DestroyCaret(); return(0); case WindowMessage.KeyDown: switch ((VirtualKey)wParam) { case VirtualKey.Home: xCaret = 0; break; case VirtualKey.End: xCaret = cxBuffer - 1; break; case VirtualKey.Prior: yCaret = 0; break; case VirtualKey.Next: yCaret = cyBuffer - 1; break; case VirtualKey.Left: xCaret = Math.Max(xCaret - 1, 0); break; case VirtualKey.Right: xCaret = Math.Min(xCaret + 1, cxBuffer - 1); break; case VirtualKey.Up: yCaret = Math.Max(yCaret - 1, 0); break; case VirtualKey.Down: yCaret = Math.Min(yCaret + 1, cyBuffer - 1); break; case VirtualKey.Delete: for (int x = xCaret; x < cxBuffer - 1; x++) { pBuffer[x, yCaret] = pBuffer[x + 1, yCaret]; } pBuffer[cxBuffer - 1, yCaret] = ' '; window.HideCaret(); using (DeviceContext dc = window.GetDeviceContext()) { using (FontHandle font = Windows.CreateFont(0, 0, 0, 0, FontWeight.DoNotCare, false, false, false, dwCharSet, OutputPrecision.Default, ClippingPrecision.Default, Quality.Default, FontPitch.FixedPitch, FontFamily.DoNotCare, null)) { dc.SelectObject(font); unsafe { fixed(char *c = &pBuffer[xCaret, yCaret]) dc.TextOut(xCaret * cxChar, yCaret * cyChar, c, cxBuffer - xCaret); } dc.SelectObject(StockFont.System); } window.ShowCaret(); } break; } Windows.SetCaretPosition(xCaret * cxChar, yCaret * cyChar); return(0); case WindowMessage.Char: for (int i = 0; i < lParam.LowWord; i++) { switch ((char)wParam) { case '\b': // backspace if (xCaret > 0) { xCaret--; window.SendMessage(WindowMessage.KeyDown, (uint)VirtualKey.Delete, 1); } break; case '\t': // tab do { window.SendMessage(WindowMessage.Char, ' ', 1); } while (xCaret % 8 != 0); break; case '\n': // line feed if (++yCaret == cyBuffer) { yCaret = 0; } break; case '\r': // carriage return xCaret = 0; if (++yCaret == cyBuffer) { yCaret = 0; } break; case '\x1B': // escape for (int y = 0; y < cyBuffer; y++) { for (int x = 0; x < cxBuffer; x++) { pBuffer[x, y] = ' '; } } xCaret = 0; yCaret = 0; window.Invalidate(false); break; default: // character codes pBuffer[xCaret, yCaret] = (char)wParam; window.HideCaret(); using (DeviceContext dc = window.GetDeviceContext()) { using (FontHandle font = Windows.CreateFont(0, 0, 0, 0, FontWeight.DoNotCare, false, false, false, dwCharSet, OutputPrecision.Default, ClippingPrecision.Default, Quality.Default, FontPitch.FixedPitch, FontFamily.DoNotCare, null)) { dc.SelectObject(font); unsafe { fixed(char *c = &pBuffer[xCaret, yCaret]) dc.TextOut(xCaret * cxChar, yCaret * cyChar, c, 1); } dc.SelectObject(StockFont.System); } window.ShowCaret(); } if (++xCaret == cxBuffer) { xCaret = 0; if (++yCaret == cyBuffer) { yCaret = 0; } } break; } } Windows.SetCaretPosition(xCaret * cxChar, yCaret * cyChar); return(0); case WindowMessage.Paint: using (DeviceContext dc = window.BeginPaint()) { using (FontHandle font = Windows.CreateFont(0, 0, 0, 0, FontWeight.DoNotCare, false, false, false, dwCharSet, OutputPrecision.Default, ClippingPrecision.Default, Quality.Default, FontPitch.FixedPitch, FontFamily.DoNotCare, null)) { dc.SelectObject(font); unsafe { for (int y = 0; y < cyBuffer; y++) fixed(char *c = &pBuffer[0, y]) dc.TextOut(0, y * cyChar, c, cxBuffer); } dc.SelectObject(StockFont.System); } } return(0); case WindowMessage.Destroy: Windows.PostQuitMessage(0); return(0); } return(Windows.DefaultWindowProcedure(window, message, wParam, lParam)); }