📄 terminal.c
字号:
////////////////////////////////////////////////////////////////////////////
//
// TERMINAL.C - Written by Mike Sax for Dr. Dobb's Journal
//
// This file implements a terminal window class "TERMINAL" which you can
// use to emulate a small tty terminal in a window. If your application
// has windows of class "TERMINAL", you should call the InitTerminal
// function at the beginning of your program (not just for the first
// instance).
//
// This file contains one public function:
//
// BOOL InitTerminal(HANDLE hInstance); // Return TRUE if success
//
// To send characters to the terminal window, you can send a TW_SENDCHAR
// and TW_SENDSTRING messages. For TW_SENDCHAR, the loword of lParam should
// contain the character you want to send to the terminal window. For
// TW_SENDSTRING lParam should be a long pointer to a null-terminated
// character-string.
////////////////////////////////////////////////////////////////////////////
#include <windows.h>
#include <string.h>
#include "terminal.h"
// Exported functions:
LONG FAR PASCAL _export TerminalWndProc(HANDLE hWnd, WORD wMessage,
WORD wParam, DWORD lParam);
// Static functions:
void static InitFont(void);
void static PositionCaret(HWND hWnd);
void static SendChar(HWND hWnd, int nChar);
int static Handle(WORD wParam, int nOldValue, int maxValue,
int nTrackPosition);
// Global variables:
static char *gszClass = "TERMINAL";
static HFONT ghFont; // Handle of terminal font
static int gcxFont; // Width of terminal font
static int gcyFont; // Height of terminal font
// Set "OEM-font" global variables: ghFont, gxFont, gyFont
void static InitFont(void)
{
HDC hDC;
TEXTMETRIC tm;
ghFont = GetStockObject(OEM_FIXED_FONT);
hDC = GetDC(NULL);
SelectObject(hDC, ghFont);
GetTextMetrics(hDC, &tm);
ReleaseDC(NULL, hDC);
gcxFont = tm.tmMaxCharWidth;
gcyFont = tm.tmHeight;
}
// This function should be called once for every instance in your program
// Return TRUE if success
BOOL InitTerminal(HANDLE hInstance)
{
WNDCLASS wc;
InitFont();
// If the terminal class was registered by a previous instance, we can
// return success
if (GetClassInfo(hInstance, gszClass, &wc))
return TRUE;
wc.style = 0;
wc.lpfnWndProc = TerminalWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = sizeof(HTERMINAL);
wc.hInstance = hInstance;
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = GetStockObject(BLACK_BRUSH);
wc.lpszMenuName = NULL;
wc.lpszClassName = gszClass;
return RegisterClass(&wc);
}
// Position the caret in the window IMPORTANT: This function should only be
// called when the window has the input focus!
void static PositionCaret(HWND hWnd)
{
HTERMINAL hTerminal = GetWindowWord(hWnd, 0);
NPTERMINAL npTerminal = (NPTERMINAL)LocalLock(hTerminal);
if (NULL == npTerminal)
return; // Abort function
SetCaretPos(gcxFont * (npTerminal->xCursor - npTerminal->xOffset),
gcyFont * (npTerminal->yCursor - npTerminal->yOffset + 1) - 1);
LocalUnlock(hTerminal);
}
// Send a character to a terminal window (internal function)
void static SendChar(HWND hWnd, int nChar)
{
HTERMINAL hTerminal = GetWindowWord(hWnd, 0);
NPTERMINAL npTerminal = (NPTERMINAL)LocalLock(hTerminal);
if (NULL == npTerminal)
return; // Abort function
switch(nChar)
{
case '\t': // Tab
npTerminal->xCursor += TABSIZE - (npTerminal->xCursor % TABSIZE);
break;
case '\r': // Return
npTerminal->xCursor = 0;
break;
case '\n': // New line
npTerminal->yCursor++;
break;
case '\a': // Beep
MessageBeep(0);
break;
case '\xC': // Clear screen
memset(npTerminal->achBuffer[0], ' ', ROWS * COLUMNS);
npTerminal->xCursor = npTerminal->yCursor = 0;
InvalidateRect(hWnd, NULL, TRUE);
break;
case '\b': // Backspace
if (npTerminal->xCursor)
npTerminal->xCursor--;
break;
default:
{
HDC hDC;
npTerminal->achBuffer[npTerminal->yCursor]
[npTerminal->xCursor] = (char)nChar;
if (hDC = GetDC(hWnd))
{
SelectObject(hDC, ghFont);
SetBkColor(hDC, 0l);
SetTextColor(hDC, RGB(255, 128, 128));
HideCaret(hWnd); // Don't paint over the caret
TextOut(hDC,
gcxFont * (npTerminal->xCursor - npTerminal->xOffset),
gcyFont * (npTerminal->yCursor - npTerminal->yOffset),
(LPSTR)&nChar, 1);
ShowCaret(hWnd);
ReleaseDC(hWnd, hDC);
}
npTerminal->xCursor++;
}
}
if (npTerminal->xCursor >= COLUMNS)
{
npTerminal->xCursor = 0;
npTerminal->yCursor++;
}
if (npTerminal->yCursor >= ROWS)
{
npTerminal->yCursor = ROWS - 1;
memmove(npTerminal->achBuffer[0], npTerminal->achBuffer[1],
(ROWS - 1) * COLUMNS);
memset(npTerminal->achBuffer[ROWS - 1], ' ', COLUMNS);
ScrollWindow(hWnd, 0, -gcyFont, NULL, NULL);
UpdateWindow(hWnd); // Send WM_PAINT message now
}
if (hWnd == GetFocus())
PositionCaret(hWnd);
LocalUnlock(hTerminal);
}
// Handle WM_VSCROLL or WM_HSCROLL message.
// Returns the new scrollbar position
int static HandleScroll(WORD wParam, int nOldValue, int maxValue,
int nTrackPosition)
{
int nNewValue = nOldValue;
switch(wParam)
{
case SB_BOTTOM:
nNewValue = maxValue - 1;
break;
case SB_LINEUP:
--nNewValue;
break;
case SB_LINEDOWN:
++nNewValue;
break;
case SB_PAGEUP:
nNewValue -= maxValue / 5;
break;
case SB_PAGEDOWN:
nNewValue += maxValue / 5;
break;
case SB_TOP:
nNewValue = 0;
case SB_THUMBPOSITION:
nNewValue = nTrackPosition;
break;
}
if (nNewValue < 0)
nNewValue = 0;
if (nNewValue > maxValue)
nNewValue = maxValue;
return nNewValue;
}
// This is the window function of the terminal class
LONG FAR PASCAL _export TerminalWndProc(HANDLE hWnd, WORD wMessage,
WORD wParam, DWORD lParam)
{
switch(wMessage)
{
case WM_CREATE:
{
HTERMINAL hTerminal = LocalAlloc(LHND, sizeof(TERMINAL));
NPTERMINAL npTerminal;
if (!hTerminal)
return -1; // Fail CreateWindow
npTerminal = (NPTERMINAL)LocalLock(hTerminal);
if (npTerminal == NULL)
{
LocalFree(hTerminal);
return -1;
}
memset((PSTR)npTerminal->achBuffer, ' ', ROWS * COLUMNS);
SetWindowWord(hWnd, 0, (WORD)hTerminal);
LocalUnlock(hTerminal);
}
break;
case WM_HSCROLL:
{
HTERMINAL hTerminal = GetWindowWord(hWnd, 0);
NPTERMINAL npTerminal = (NPTERMINAL)LocalLock(hTerminal);
int xNewOffset;
if (NULL == npTerminal)
return 0l; // abort
xNewOffset = HandleScroll(wParam, npTerminal->xOffset,
max(0, COLUMNS - npTerminal->cxWindow), LOWORD(lParam));
if (xNewOffset != npTerminal->xOffset)
{
ScrollWindow(hWnd, gcxFont * (npTerminal->xOffset -
xNewOffset), 0, NULL, NULL);
npTerminal->xOffset = xNewOffset;
SetScrollPos(hWnd, SB_HORZ, xNewOffset, TRUE);
}
LocalUnlock(hTerminal);
}
break;
case WM_VSCROLL:
{
HTERMINAL hTerminal = GetWindowWord(hWnd, 0);
NPTERMINAL npTerminal = (NPTERMINAL)LocalLock(hTerminal);
int yNewOffset;
if (NULL == npTerminal)
return 0l; // abort
yNewOffset = HandleScroll(wParam, npTerminal->yOffset,
max(0, ROWS - npTerminal->cyWindow), LOWORD(lParam));
if (yNewOffset != npTerminal->yOffset)
{
ScrollWindow(hWnd, 0, gcyFont * (npTerminal->yOffset -
yNewOffset), NULL, NULL);
npTerminal->yOffset = yNewOffset;
SetScrollPos(hWnd, SB_VERT, yNewOffset, TRUE);
}
LocalUnlock(hTerminal);
}
break;
case WM_DESTROY:
LocalFree(GetWindowWord(hWnd, 0));
break;
case WM_PAINT:
{
int i;
PAINTSTRUCT ps;
HDC hDC;
HTERMINAL hTerminal = GetWindowWord(hWnd, 0);
NPTERMINAL npTerminal = (NPTERMINAL)LocalLock(hTerminal);
if (NULL == npTerminal)
return 0l; // Can't access our parameters->abort paint
hDC = BeginPaint(hWnd, &ps);
SelectObject(hDC, ghFont);
SetBkColor(hDC, 0l);
SetTextColor(hDC, RGB(255, 128, 128));
for(i = 0 ; i < ROWS ; ++i)
TextOut(hDC, - (npTerminal->xOffset * gcxFont),
gcyFont * (i - npTerminal->yOffset),
npTerminal->achBuffer[i], COLUMNS);
EndPaint(hWnd, &ps);
LocalUnlock(hTerminal);
}
break;
case WM_SIZE:
{
HTERMINAL hTerminal = GetWindowWord(hWnd, 0);
NPTERMINAL npTerminal = (NPTERMINAL)LocalLock(hTerminal);
RECT rect;
if (NULL == npTerminal)
return 0l;
// Get client dimensions without scroll bars (we have no border)
GetWindowRect(hWnd, &rect);
// Set max rows and columns that can be displayed
npTerminal->cxWindow = (rect.right - rect.left) / gcxFont;
npTerminal->cyWindow = (rect.bottom - rect.top) / gcyFont;
SetScrollRange(hWnd, SB_HORZ, 0,
max(0, COLUMNS - npTerminal->cxWindow), TRUE);
SetScrollRange(hWnd, SB_VERT, 0,
max(0, ROWS - npTerminal->cyWindow), TRUE);
LocalUnlock(hTerminal);
}
break;
case TW_SENDCHAR:
SendChar(hWnd, wParam);
break;
case TW_SENDSTRING:
while ((LPSTR)(lParam))
SendChar(hWnd, *((LPSTR)(lParam++)));
break;
case WM_CHAR:
// Send a notification message to our parent
SendMessage(GetParent(hWnd), WM_COMMAND,
GetWindowWord(hWnd, GWW_ID), MAKELONG(hWnd, wParam));
break;
case WM_LBUTTONDOWN:
SetFocus(hWnd);
case WM_SETFOCUS:
CreateCaret(hWnd, NULL, gcxFont, 0);
PositionCaret(hWnd);
ShowCaret(hWnd);
break;
case WM_KILLFOCUS:
DestroyCaret();
break;
case WM_GETDLGCODE:
return DLGC_WANTALLKEYS; // We also process enter, tab, ...
default:
return DefWindowProc(hWnd, wMessage, wParam, lParam);
}
return 0l;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -