📄 term.c
字号:
/* term.c
* Copyright (c) 1997 David Cole
*
* Control and manage the terminal window
*/
#include <windows.h>
#include <shellapi.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <limits.h>
#include <ctype.h>
#include "platform.h"
#include "utils.h"
#include "term.h"
#include "termwin.h"
#include "lines.h"
#include "dtelnet.h"
#include "socket.h"
#include "raw.h"
#include "font.h"
#include "status.h"
#include "emul.h"
#include "log.h"
/* Fred. We need the bs2Del flag of the current session
*/
#include "connect.h"
static char* termWinClass = "DTermWClass";
Terminal term; /* The terminal */
static HWND termWnd; /* terminal window handle */
static BOOL termHasPalette; /* can we support palettes? */
static HPALETTE termPalette; /* the palette for the window */
static UINT termTimerId; /* selection scrolling timer */
static BOOL haveCaret; /* do we have the Windows Caret? */
static RECT update; /* current update rectangle (before scroll) */
static BOOL haveUpdate; /* do we have an update rectangle? */
static BOOL bottomOnOutput; /* do we scroll to bottom on output? */
static BOOL autoCopy = TRUE; /* do we automatically copy selection
* to clipboard? */
static CursorStyle cursorStyle; /* terminal cursor style */
static BOOL useIniGeometry = TRUE; /* should we update .INI file */
static int vscrollWidth; /* width of a vertical scrollbar */
/* .INI File Strings
*/
static char termStr[] = "Terminal";
static char sizeStr[] = "Size";
static char autoCopyStr[] = "Auto Copy";
static char bottomOnOutputStr[] = "Bottom On Output";
static char cursorStyleStr[] = "Cursor Style";
static char attachPrinterStr[] = "Attached Printer";
static char printScreenStr[] = "Enable PrintScreen";
/* static unsigned char* termCaretBits; -- not used ?! LZS*/
/* bits that make up the caret bitmap */
static COLORREF colors[] = {
RGB(0,0,0), /* Black */
RGB(200,0,0), /* Red */
RGB(0,200,0), /* Green */
RGB(200,200,0), /* Yellow */
RGB(0,0,200), /* Blue */
RGB(200,0,200), /* Magenta */
RGB(0,200,200), /* Cyan */
RGB(200,200,200), /* White */
RGB(100,100,100), /* Bold Black */
RGB(255,0,0), /* Bold Red */
RGB(0,255,0), /* Bold Green */
RGB(255,255,0), /* Bold Yellow */
RGB(30,144,255), /* Bold Blue */
RGB(255,0,255), /* Bold Magenta */
RGB(0,255,255), /* Bold Cyan */
RGB(255,255,255) /* Bold White */
};
#define PaletteSize numElem(colors)
static void termFuncKeyDown(BOOL shiftPressed, int nFkey);
static void selectCheckOverlap(int y1, int x1, int y2, int x2);
#ifdef WIN32
static UINT WM_TERM_WHEEL = 0;
#endif
/*********************
* WINDOW MANAGEMENT *
*********************/
/* Return the line array index of the line at the top of the terminal
* (unscrolled window)
*/
int winTerminalTopLine()
{
return term.numLinesUsed <= term.winSize.cy
? 0 : term.numLinesUsed - term.winSize.cy;
}
/* Return a terminal line converted to a window line index, taking
* account of scrolling backwards in history.
*
* Args:
* ypos - cursor position to convert to window line index
*/
int winTerminalToWindow(int ypos)
{
return ypos + winTerminalTopLine() - term.topVisibleLine;
}
/* Move the caret to the specifed character position
*
* Args:
* x - the terminal column to move the caret to
* y - the terminal line to move the caret to
*/
void winCaretPos(int x, int y)
{
y = winTerminalToWindow(y);
SetCaretPos(
x * term.charSize.cx + term.cursorRect.left,
y * term.charSize.cy + term.cursorRect.top
);
}
/* Set the history scrollbar to indicate the visible part of the
* line array
*/
void winSetScrollbar()
{
#ifdef WIN32
int range = (term.numLinesUsed <= term.winSize.cy)
? term.winSize.cy : term.numLinesUsed;
SCROLLINFO scrollInfo;
scrollInfo.cbSize = sizeof(scrollInfo);
scrollInfo.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
scrollInfo.nMin = 0;
scrollInfo.nMax = range - 1;
scrollInfo.nPage = term.winSize.cy;
scrollInfo.nPos = term.topVisibleLine;
scrollInfo.nTrackPos = 0;
SetScrollInfo(termWnd, SB_VERT, &scrollInfo, TRUE);
#else
int range = (term.numLinesUsed <= term.winSize.cy)
? 1 : term.numLinesUsed - term.winSize.cy;
SetScrollRange(termWnd, SB_VERT, 0, range, TRUE);
SetScrollPos(termWnd, SB_VERT, term.topVisibleLine, TRUE);
#endif
}
/* Force windows to update the part of the window we have changed
*/
void winUpdate()
{
if (haveUpdate) {
/* We have accumulated some changes to the terminal window in
* the update rect. Convert the top and bottom from terminal
* to window ordinates so we can check if the updates are
* visible.
*/
update.top = winTerminalToWindow(update.top);
update.bottom = winTerminalToWindow(update.bottom);
/* Since the terminal is always at the end of the line array,
* it is not possible to scroll down past the terminal. We
* can only scroll up. We only have to check if the top
* updated line is still visible.
*/
if (update.top < term.winSize.cy) {
/* The update rect is visible, build a window rect that
* encloses the terminal update rect
*/
RECT rect;
if (update.top < 0)
update.top = 0;
if (update.bottom >= term.winSize.cy)
update.bottom = term.winSize.cy - 1;
rect.top = update.top * term.charSize.cy;
rect.left = update.left * term.charSize.cx;
rect.bottom = (update.bottom + 1) * term.charSize.cy;
rect.right = (update.right + 1) * term.charSize.cx;
/* Invalidate the window rect
*/
InvalidateRect(termWnd, &rect, FALSE);
}
haveUpdate = FALSE;
}
/* Force windows to perform any pending updates
*/
UpdateWindow(termWnd);
}
/* Scroll the region a number of lines. Positive values scroll lines
* up.
*
* Args:
* numLines - the number of lines to scroll the scroll region.
*/
void winScrollRegion(int numLines)
{
RECT rect; /* build window rect to scroll */
rect.left = 0;
rect.right = term.winSize.cx * term.charSize.cx;
if (numLines < 0) {
/* Scroll lines down
*/
rect.top = winTerminalToWindow(term.scrollTop) * term.charSize.cy;
rect.bottom = (winTerminalToWindow(term.scrollBottom) + numLines)
* term.charSize.cy;
} else {
/* Scroll lines up
*/
rect.top = (winTerminalToWindow(term.scrollTop) + numLines)
* term.charSize.cy;
rect.bottom = winTerminalToWindow(term.scrollBottom) * term.charSize.cy;
}
/* Force pending updates - be paranoid to prevent windows getting
* confused about what needs to be updated.
*/
winUpdate();
/* Scroll the region
*/
ScrollWindow(termWnd, 0, -numLines * term.charSize.cy, &rect, NULL);
}
/* Scroll the entire window a number of lines. Positive values scroll
* lines up.
*
* Args:
* numLines - the number of lines to scroll
*/
void winScrollWindow(int numLines)
{
ScrollWindow(termWnd, 0, -numLines * term.charSize.cy, NULL, NULL);
}
/* The oldest line in the history buffer has been removed
*/
void winTopLineRemoved()
{
if (term.topVisibleLine == 0)
winScrollWindow(1);
else
term.topVisibleLine--;
if (term.haveSelection) {
/* We have a selection, make sure we adjust the selection
* range to account for the top line being removed.
*/
term.selectFrom.y--;
if (term.selectFrom.y < 0) {
term.selectFrom.y = 0;
term.selectFrom.x = 0;
}
term.selectTo.y--;
if (term.selectTo.y < 0)
term.haveSelection = FALSE;
}
}
/* Add an area to the terminal window update rect
*
* Args:
* y1 - top terminal line modified
* x1 - left column modified
* y2 - bottom terminal line modified
* x2 - right column modified
*/
void winModifyRange(int y1, int x1, int y2, int x2)
{
/* Hide the selection if it overlaps with the modified area.
*/
selectCheckOverlap(linesTerminalToLine(y1), x1,
linesTerminalToLine(y2), x2);
if (!haveUpdate) {
/* No previous update rect. The modified area is the update
* rect
*/
update.top = y1;
update.left = x1;
update.bottom = y2;
update.right = x2;
haveUpdate = TRUE;
return;
}
/* Merge the modified area with the update rect
*/
if (y1 < update.top)
update.top = y1;
if (x1 < update.left)
update.left = x1;
if (y2 > update.bottom)
update.bottom = y2;
if (x2 > update.right)
update.right = x2;
}
/* A number of lines were inserted into the terminal.
*
* Args:
* posy - terminal line to insert lines at
* bottom - bottom line affected by scrolling
* numLines - number of lines to insert
*/
void winLinesInsert(int posy, int bottom, int numLines)
{
RECT rect; /* build window rect to scroll */
RECT client; /* query client window size */
if (posy >= bottom)
/* No lines to scroll
*/
return;
/* Hide the selection if it overlaps the inserted lines
*/
selectCheckOverlap(linesTerminalToLine(posy), 0,
linesTerminalToLine(bottom), term.winSize.cx);
/* Convert terminal line position to window line.
*/
posy = winTerminalToWindow(posy);
if (posy >= term.winSize.cy)
/* Cannot see the insertion point - nothing to do
*/
return;
bottom = winTerminalToWindow(bottom);
/* Do not bother scrolling a part of the window we will not see
*/
if (posy + numLines > bottom)
numLines = bottom - posy;
/* Build scroll rect
*/
GetClientRect(termWnd, &client);
rect.left = 0;
rect.top = posy * term.charSize.cy;
rect.right = client.right;
rect.bottom = (bottom - numLines) * term.charSize.cy;
/* Force an update before we scroll - windows bug avoidance
*/
winUpdate();
ScrollWindow(termWnd, 0, term.charSize.cy * numLines, &rect, NULL);
/* Invalidate the part of the window exposed
*/
rect.left = 0;
rect.top = posy * term.charSize.cy;
rect.right = client.right;
rect.bottom = numLines * term.charSize.cy;
InvalidateRect(termWnd, &rect, FALSE);
}
/* Some characters were inserted into a line.
*
* Args:
* posy - line that characters were inserted into
* posx - column at which characters were inserted
*/
void winCharsInsert(int posy, int posx)
{
int posyIdx = linesTerminalToLine(posy);
/* Hide the selection if it overlaps the inserted characters
*/
selectCheckOverlap(posyIdx, posx, posyIdx, term.winSize.cx);
/* Add the affected line, from the insert column to the end to the
* update rect.
*/
winModifyRange(posy, posx, posy, term.winSize.cx);
}
/* A number of lines were deleted from the terminal
*
* Args:
* posy - terminal line to insert lines at
* bottom - bottom line affected by scrolling
* numLines - number of lines to delete
*/
void winLinesDelete(int posy, int bottom, int numLines)
{
RECT rect; /* build window rect to scroll */
RECT client; /* query client window size */
if (posy >= bottom)
/* No lines to scroll
*/
return;
/* Hide the selection if it overlaps the inserted lines
*/
selectCheckOverlap(linesTerminalToLine(posy), 0,
linesTerminalToLine(bottom), term.winSize.cx);
/* Convert the terminal line position to window line.
*/
posy = winTerminalToWindow(posy);
if (posy >= term.winSize.cy)
/* We cannot see the affected portion of the screen - nothing
* to do
*/
return;
bottom = winTerminalToWindow(bottom);
/* Do not scroll lines we cannot see
*/
if (posy + numLines > bottom)
numLines = bottom - posy;
/* Build scroll rect
*/
GetClientRect(termWnd, &client);
rect.left = 0;
rect.top = (posy + numLines) * term.charSize.cy;
rect.right = client.right;
rect.bottom = bottom * term.charSize.cy;
/* Force an update before we scroll - windows bug avoidance
*/
winUpdate();
ScrollWindow(termWnd, 0, -term.charSize.cy * numLines, &rect, NULL);
/* Invalidate the part of the window exposed
*/
rect.left = 0;
rect.top = (bottom - numLines) * term.charSize.cy;
rect.right = client.right;
rect.bottom = bottom * term.charSize.cy;
InvalidateRect(termWnd, &rect, FALSE);
}
/* Some characters were deleted from a line
*
* Args:
* posy - line that characters were deleted from
* posx - column at which characters were deleted
*/
void winCharsDelete(int posy, int posx)
{
int posyIdx = linesTerminalToLine(posy);
/* Hide the selection if it overlaps the inserted characters
*/
selectCheckOverlap(posyIdx, posx, posyIdx, term.winSize.cx);
/* Add the affected line, from the delete column to the end to the
* update rect.
*/
winModifyRange(posy, posx, posy, term.winSize.cx);
}
/************************
* SELECTION MANAGEMENT *
************************/
/* Return the start and end of the selection (start < end)
*
* Args:
* start - returns the start of the selection
* end - returns the end of the selection
*
* The POINT.y ordinate is a line array index
*/
static void termSelectGetRange(POINT* start, POINT* end)
{
if (term.selectFrom.y > term.selectTo.y
|| term.selectFrom.y == term.selectTo.y
&& term.selectFrom.x > term.selectTo.x) {
*start = term.selectTo;
*end = term.selectFrom;
} else {
*start = term.selectFrom;
*end = term.selectTo;
}
}
/* Invalidate the area of the window that contains the selection
*
* Args:
* y1 - line index of selection start
* x1 - column number of selection start
* y2 - line index of selection end
* x2 - column number of selection end
* LZS: x1 and x2 are indifferent
*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -