📄 lines.c
字号:
/* lines.c
* Copyright (c) 1997 David Cole
*
* Manage line array for terminal with history. At the moment, only a
* fixed size history is supported.
*/
#include <windows.h>
#include <string.h>
#include "utils.h"
#include "term.h"
#include "termwin.h"
#include "lines.h"
#include "emul.h"
#include "log.h"
/* Return the specified line
*/
Line* linesGetLine(int num)
{
if (num < 0 || num >= term.numLinesUsed)
return NULL;
ASSERT(term.lineList != NULL);
ASSERT(term.lineList[num] != NULL);
return term.lineList[num];
}
/* Allocate a Line.
*
* Return a pointer to the new line, initialised to 0 length.
*/
static Line* linesAlloc(void)
{
Line* line;
if (term.numLinesUsed == term.maxLines) {
/* We have filled our history. We have to remove the oldest
* line to make room for the new line.
*/
ASSERT(term.lineList != NULL);
line = term.lineList[0];
ASSERT(line != NULL);
ASSERT(term.maxLines > 1);
memmove(&term.lineList[0], &term.lineList[1],
(term.maxLines - 1) * sizeof(*term.lineList));
term.numLinesUsed--;
/* Tell the terminal window that the top history line was
* removed. This might cause a scroll.
*/
winTopLineRemoved();
} else
/* Allocate a new line
*/
line = (Line *)xmalloc(sizeof(*line));
/* Initialise the line with zero length, no wrap, and current
* color attributes.
*/
ASSERT(line != NULL);
line->len = 0;
line->wrapped = 0;
line->attr[0] = getCurrAttr();
return line;
}
/* Add a new line to the bottom of the line array
*/
void linesNewLine()
{
Line* line = linesAlloc();
term.lineList[term.numLinesUsed++] = line;
}
/* Make sure that lines exist up to the specified line array index
*/
void linesCreateTo(int idx)
{
if (idx >= term.maxLines)
idx = term.maxLines - 1;
while (term.lineList[idx] == NULL)
term.lineList[term.numLinesUsed++] = linesAlloc();
}
/* Return the line array index of the specified terminal line.
*
* Args:
* posy - offset from the top line of the terminal.
*/
int linesTerminalToLine(int posy)
{
return winTerminalTopLine() + posy;
}
/* Fill a part of a line with an ASCII character + attribute. */
static void linesFill (Line *line, int x, int n, unsigned char c, unsigned char attr)
{
int i;
if (n<=0) return;
line->text[x].cType = DTCHAR_ASCII;
line->text[x].cCode = c;
for (i=1; i<n; ++i) {
line->text[x+i] = line->text[x];
}
memset (&line->attr[x], attr, n);
}
/* Insert a number of lines at the specified terminal line.
*
* Args:
* posy - the terminal line to insert lines before
* bottom - the bottom terminal line (handles scroll regions)
* numLines - the number of lines to insert
*/
void linesInsert(int posy, int bottom, int numLines)
{
/* Convert terminal lines to line array index
*/
int posyIdx = linesTerminalToLine(posy);
int bottomIdx = linesTerminalToLine(bottom);
ASSERT(posy >= 0);
ASSERT(bottom >= 1);
ASSERT(posy < bottom);
ASSERT(bottom <= term.winSize.cy);
ASSERT(numLines > 0);
ASSERT(posyIdx <= term.maxLines);
ASSERT(bottomIdx <= term.maxLines);
/* Make sure that we do not try to insert more lines than will fit
* in the region.
*/
if (posy + numLines > bottom)
numLines = bottom - posy;
/* Make that lines exist to the specified y position
*/
linesCreateTo(posyIdx);
/* Insert the blank lines
*/
while (numLines-- > 0) {
Line* line;
if (term.numLinesUsed >= bottomIdx) {
/* Take the line off the bottom and insert it
* at the current position.
*/
line = term.lineList[bottomIdx - 1];
ASSERT(line != NULL);
line->len = 0;
line->wrapped = 0;
line->attr[0] = getCurrAttr();
} else
/* We need to create a new line to insert it
*/
line = linesAlloc();
/* If there are any lines at or after the insertion position,
* we need to move them down.
*/
if (posyIdx < bottomIdx - 1)
memmove(&term.lineList[posyIdx + 1], &term.lineList[posyIdx],
(bottomIdx - posyIdx - 1) * sizeof(*term.lineList));
term.lineList[posyIdx] = line;
}
}
/* Insert a number of blank characters in the specified terminal line.
*
* Args:
* posy - the terminal line to insert characters into
* posx - the column at which characters will be inserted
* numChars - the number of characters to be inserted
*/
void linesCharsInsert(int posy, int posx, int numChars)
{
/* Convert the terminal line to line array index
*/
int posyIdx = linesTerminalToLine(posy);
Line* line; /* line being modified */
ASSERT(posy >= 0);
ASSERT(posy < term.winSize.cy);
ASSERT(posx >= 0);
ASSERT(numChars > 0);
ASSERT(posyIdx < term.maxLines);
linesCreateTo(posyIdx);
line = term.lineList[posyIdx];
ASSERT(line != NULL);
/* Do not insert any characters if at end of line
*/
if (line == NULL || posx >= line->len)
return;
/* Make sure that we do not insert past the right edge of the
* window
*/
if (numChars + posx > term.winSize.cx)
numChars = term.winSize.cx - posx;
if (numChars + line->len > term.winSize.cx)
line->len = term.winSize.cx - numChars;
/* Now insert the blank characters
*/
memmove(line->text + posx + numChars,
line->text + posx,
sizeof(line->text[0])*(line->len - posx));
memmove(line->attr + posx + numChars,
line->attr + posx,
line->len - posx + 1);
linesFill (line, posx, numChars, ' ', getCurrAttr());
line->len += numChars;
}
/* Delete a number of lines at the specified terminal line.
*
* Args:
* posy - the terminal line to delete lines at
* bottom - the bottom terminal line (handle scroll regions)
* numLines - the number of lines to delete
*/
void linesDelete(int posy, int bottom, int numLines)
{
/* Convert terminal lines to line array index
*/
int posyIdx = linesTerminalToLine(posy);
int bottomIdx = linesTerminalToLine(bottom);
ASSERT(posy >= 0);
ASSERT(bottom >= 1);
ASSERT(posy < bottom);
ASSERT(bottom <= term.winSize.cy);
ASSERT(numLines > 0);
ASSERT(posyIdx <= term.maxLines);
ASSERT(bottomIdx <= term.maxLines);
/* Limit bottom line to the number of lines that have actually
* been used.
*/
if (bottomIdx > term.numLinesUsed)
bottomIdx = term.numLinesUsed;
/* Make sure that we do not try to delete more lines than are in
* the region.
*/
if (posy + numLines > bottom)
numLines = bottom - posy;
/* Make that lines exist to the specified y position
*/
linesCreateTo(posyIdx);
while (numLines--) {
/* Take the top line, clear it, and rotate it to the bottom of
* the delete region.
*/
Line* line = term.lineList[posyIdx];
ASSERT(line != NULL);
line->len = 0;
line->wrapped = 0;
line->attr[0] = getCurrAttr();
/* If there are any lines used after the current point -
* shuffle them up.
*/
if (posyIdx < bottomIdx - 1)
memmove(&term.lineList[posyIdx], &term.lineList[posyIdx + 1],
(bottomIdx - 1 - posyIdx) * sizeof(*term.lineList));
term.lineList[bottomIdx - 1] = line;
}
}
/* Delete a number of characters in the specified terminal line.
*
* Args:
* posy - the terminal line to delete characters from
* posx - the column at which characters will be deleted
* numChars - the number of characters to be deleted
*/
void linesCharsDelete(int posy, int posx, int numChars)
{
/* Convert terminal line to line array index
*/
int posyIdx = linesTerminalToLine(posy);
Line* line; /* the line being modified */
ASSERT(posy >= 0);
ASSERT(posy < term.winSize.cy);
ASSERT(posx >= 0);
ASSERT(numChars > 0);
ASSERT(posyIdx < term.maxLines);
/* Make sure lines exist up to the one being modified
*/
linesCreateTo(posyIdx);
line = term.lineList[posyIdx];
/* Make sure that the line length does not extend past the right
* edge of the window
*/
if (line->len > term.winSize.cx)
line->len = term.winSize.cx;
/* Do not delete any characters if past end of line
*/
if (posx >= line->len)
return;
/* Only delete characters up to the end of the line
*/
if (numChars > line->len - posx)
numChars = line->len - posx;
memmove(line->text + posx,
line->text + posx + numChars,
line->len - posx - numChars);
memmove(line->attr + posx,
line->attr + posx + numChars,
line->len - posx - numChars + 1);
line->len -= numChars;
}
/* Clear all of the text in the terminal between two locations.
*
* Args:
* y1/x1 - terminal line/column to clear from
* y2/x2 - terminal line/column to clear to
*
* The from location is assumed to precede the to location.
*/
void linesClearRange(int y1, int x1, int y2, int x2)
{
int lineIdx; /* iterate over lines to be cleared */
Line* line; /* line currently being cleared */
/* If start of clear operation is off bottom of terminal, we have
* no work to do.
*/
if (y1 >= term.winSize.cy)
return;
/* If end of clear operation is off bottom of terminal, set it to
* the end of the last line on the terminal.
*/
if (y2 >= term.winSize.cy) {
y2 = term.winSize.cy - 1;
x2 = term.winSize.cx;
}
ASSERT(x1 >= 0);
ASSERT(x2 >= 0);
ASSERT(y2 >= y1);
/* Convert the starting line to line array index and make sure
* lines exist to that point.
*/
lineIdx = linesTerminalToLine(y1);
linesCreateTo(linesTerminalToLine(y2));
line = term.lineList[lineIdx];
ASSERT(line != NULL);
if (y1 == y2) {
/* Clearing text on single line
*/
ASSERT(x1 <= x2);
if (x1 <= line->len) {
if (x2 >= line->len) {
/* Cleared to end of line - redefine line length
*/
line->len = x1;
line->wrapped = 0;
line->attr[line->len] = getCurrAttr();
} else {
/* Cleared part of line
*/
linesFill (line, x1, x2-x1, ' ', getCurrAttr());
}
}
return;
}
/* Clearing more than one line - first line is from x1 to end of line
*/
if (x1 < line->len) {
line->len = x1;
line->wrapped = 0;
line->attr[x1] = getCurrAttr();
}
++lineIdx;
++y1;
/* Totally clear all lines until y2
*/
while (y1 < y2) {
ASSERT(term.lineList[lineIdx] != NULL);
term.lineList[lineIdx]->len = 0;
term.lineList[lineIdx]->wrapped = 0;
term.lineList[lineIdx]->attr[0] = getCurrAttr();
++lineIdx;
++y1;
}
/* Clearing last line - clear from start of line to x2
*/
if (y1 == term.winSize.cy)
return;
line = term.lineList[lineIdx];
ASSERT(line != NULL);
if (x2 >= line->len) {
line->len = 0;
line->wrapped = 0;
line->attr[0] = getCurrAttr();
} else {
ASSERT(x2 > 0);
linesFill (line, 0, x2, ' ', getCurrAttr());
}
}
/* Fill the screen with 'E' for alignment test
*/
void linesAlignTest()
{
int y; /* iterate over lines to be cleared */
for (y = 0; y < term.winSize.cy; y++) {
int lineIdx = linesTerminalToLine(y);
Line* line;
linesCreateTo(lineIdx);
line = term.lineList[lineIdx];
ASSERT(line != NULL);
line->len = term.winSize.cx;
line->wrapped = 0;
linesFill (line, 0, term.winSize.cx, 'E', getCurrAttr());
line->attr [term.winSize.cx] = getCurrAttr();
}
}
/* Switch to/from inverse video
*/
void linesInvertAll()
{
int y; /* iterate over lines to be inverted */
for (y = 0; y < term.winSize.cy; y++) {
int lineIdx = linesTerminalToLine(y);
Line* line;
int x;
linesCreateTo(lineIdx);
line = term.lineList[lineIdx];
ASSERT(line != NULL);
for (x = 0; x <= line->len; x++)
line->attr[x] = (unsigned char)REVERSE(line->attr[x]);
}
}
/* Set the character at the current cursor position.
*
* Args:
* c - the character to set at the cursor position.
*/
void linesSetChar(const DtChar *dCh)
{
int lineIdx; /* cursor terminal line as line array index */
Line* line; /* line being modified */
int posy = term.cursor.y;
int posx = term.cursor.x;
int i;
/* If cursor off bottom of terminal, we have nothing to do
*/
if (posy >= term.winSize.cy)
return;
/* Make sure lines up to the cursor location exist
*/
lineIdx = linesTerminalToLine(posy);
ASSERT(lineIdx < term.maxLines);
linesCreateTo(lineIdx);
line = term.lineList[lineIdx];
ASSERT(line != NULL);
ASSERT(posx < sizeof(line->text));
if (posx >= line->len) {
/* Append to the end of the line, retain the end of line
* attribute.
*/
line->attr[posx + 1] = line->attr[line->len];
if (posx > line->len) {
for (i=line->len; i<posx; ++i) {
line->text[i].cType = DTCHAR_ASCII;
line->text[i].cCode = ' ';
line->attr[i] = line->attr[line->len];
}
}
line->len = posx + 1;
}
/* Set the character
*/
line->text[posx] = *dCh;
line->attr[posx] = getCurrAttr();
/* Make sure that the line length does not extend past the right
* edge of the window
*/
if (line->len > term.winSize.cx)
line->len = term.winSize.cx;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -