/* Copyright 1992 John Bovey, University of Kent at Canterbury.
*
* You can do what you like with this source code as long as
* you don't try to make money out of it and you include an
* unaltered copy of this message (including the copyright).
*/
char xvt_screen_c_sccsid[] = "@(#)screen.c 1.3 18/9/92 (UKC)";
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include "rvt.h"
#include "screen.h"
#include "command.h"
#include "xsetup.h"
#include "sbar.h"
#define MAX_WIDTH 250 /* max width of selected lines */
#define PROP_SIZE 1024 /* chunk size for retrieving the selection
* property */
/* Character classes used when selecting words with a double click.
*/
static int char_class[128] = {
32, 1, 1, 1, 1, 1, 1, 1,
1, 32, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
32, 33, 34, 35, 36, 37, 38, 39,
40, 41, 42, 43, 44, 45, 46, 47,
48, 48, 48, 48, 48, 48, 48, 48,
48, 48, 58, 59, 60, 61, 62, 63,
64, 48, 48, 48, 48, 48, 48, 48,
48, 48, 48, 48, 48, 48, 48, 48,
48, 48, 48, 48, 48, 48, 48, 48,
48, 48, 48, 91, 92, 93, 94, 48,
96, 48, 48, 48, 48, 48, 48, 48,
48, 48, 48, 48, 48, 48, 48, 48,
48, 48, 48, 48, 48, 48, 48, 48,
48, 48, 48, 123, 124, 125, 126, 1
};
/* External global variables that are initialised at startup.
*/
extern Display *display;
extern Window vt_win;
extern Window main_win;
extern Colormap colormap;
extern XFontStruct *mainfont; /* main font structure */
extern XFontStruct *boldfont; /* bold font structure */
extern GC gc; /* GC for drawing text */
extern GC negc; /* GC without graphics exposures */
extern GC hlgc; /* GC used for highlighting text cursor */
extern unsigned long foreground;/* foreground pixel value */
extern unsigned long background;/* background pixel value */
extern int reverse_wrap; /* reverse wrap allowed */
/* Structure describing the current state of the screen.
*/
struct screenst {
char **text; /* backup copy of screen->text */
char **rend; /* character rendition styles etc. */
int row; /* cursor position */
int col; /* ditto */
int tmargin; /* top scroll margin */
int bmargin; /* bottom scroll margin */
int decom; /* origin mode flag */
int wrap; /* auto-wrap flag */
int wrap_next; /* wrap before the next printed character */
int insert; /* insert mode flag */
};
/* structure describing a saved line
*/
struct slinest {
char *sl_text; /* the text of the line */
char *sl_rend; /* the rendition style */
int sl_length; /* length of the line */
};
/* selection endpoint types.
*/
enum seltype {
SCREEN,
SAVED,
NOSEL
};
/* structure describing a selection endpoint.
*/
struct selst {
enum seltype se_type;
int se_index; /* index into the sline or screen array */
int se_col; /* column of the character */
};
static struct selst selend1, selend2; /* the selection endpoints */
static struct selst selanchor; /* the selection anchor */
static char *selection_text = NULL; /* text version of the current
* selection */
static int selection_length; /* length of selection text */
static enum selunit selection_unit; /* current unit of selection */
/* Screen state variables that are the same for both screens.
*/
static int pwidth; /* width in pixels */
static int pheight; /* height in pixels */
static int cwidth = 0; /* width in characters */
static int cheight = 0; /* height in characters */
static int focus = 0; /* window has the keyboard focus */
static int rstyle; /* rendition style */
static int save_rstyle; /* when it needs to be saved */
/* screen state variables
*/
static struct screenst screen1; /* main screen */
static struct screenst screen2; /* second screen */
static struct screenst *screen = NULL; /* current of screen1 and screen2 */
static struct screenst save_screen;
static int sline_top; /* high water mark of saved scrolled lines */
static int sline_max; /* Max number of saved lines */
static struct slinest **sline; /* main array of saved lines */
static int offset = 0; /* current vertical offset for displaying
* saved lines */
static int fheight; /* height of a character in the font */
static int fwidth; /* width of a font character */
static void repair_damage(void);
static Bool grexornoex(Display *, XEvent *, char *);
static void change_offset(int);
static void paint_rval_text(char *, int, int, int, int);
static void paint_rvec_text(char *, char *, int, int, int);
static void refresh(int, int, int, int);
static void show_selection(int, int, int, int);
static void scroll(int, int, int);
static void scroll1(int);
static void home_screen(void);
static void cursor(void);
static void rc_to_selend(int, int, struct selst *);
static void fix_rc(int *, int *);
static void selend_to_rc(int *, int *, struct selst *);
static void change_selection(struct selst *, struct selst *);
static char *convert_line(char *, int *, int, int);
static int selcmp(struct selst *, struct selst *);
static void adjust_selection(struct selst *);
static int save_selection(void);
static void check_selection(int, int);
static int cclass(int);
/* Perform any initialisation on the screen data structures. Called just once
* at startup. saved_lines is the number of saved lines.
*/
void
scr_init(saved_lines)
int saved_lines;
{
int i;
/* Initialise the array of lines that have scrolled of the top. */
sline_max = saved_lines;
if (sline_max < MAX_SCROLL)
sline_max = MAX_SCROLL;
sline = (struct slinest **) cmalloc(sline_max * sizeof(struct slinest *));
for (i = 0; i < sline_max; i++)
sline[i] = NULL;
sline_top = 0;
screen1.text = NULL;
screen1.rend = NULL;
screen1.row = 0;
screen1.col = 0;
screen1.wrap = 1;
screen1.decom = 0;
screen1.insert = 0;
screen2.text = NULL;
screen2.rend = NULL;
screen2.row = 0;
screen2.col = 0;
screen2.wrap = 1;
screen2.decom = 0;
screen2.insert = 0;
save_screen.row = 0;
save_screen.col = 0;
screen = &screen1;
rstyle = 0;
fwidth = XTextWidth(mainfont, "M", 1);
fheight = mainfont->ascent + mainfont->descent;
scr_reset();
}
/* Reset the screen - called whenever the screen needs to be repaired.
*/
void
scr_reset()
{
Window root;
int x, y, n, i, j, onscreen;
unsigned int width, height, border_width, depth;
int cw, ch;
char **r1, **r2, **s1, **s2;
struct slinest *sl;
XGetGeometry(display, vt_win, &root, &x, &y, &width, &height, &border_width, &depth);
cw = (width - 2 * MARGIN) / fwidth;
ch = (height - 2 * MARGIN) / fheight;
if (screen->text == NULL || cw != cwidth || ch != cheight) {
offset = 0;
/* Recreate the screen backup arrays. The screen arrays are
* one byte wider than the screen and the last byte is used as
* a flag which is non-zero of the line wrapped automatically. */
s1 = (char **) cmalloc(ch * sizeof(char *));
s2 = (char **) cmalloc(ch * sizeof(char *));
r1 = (char **) cmalloc(ch * sizeof(char *));
r2 = (char **) cmalloc(ch * sizeof(char *));
for (y = 0; y < ch; y++) {
s1[y] = (char *) cmalloc(cw + 1);
s2[y] = (char *) cmalloc(cw + 1);
r1[y] = (char *) cmalloc(cw + 1);
r2[y] = (char *) cmalloc(cw + 1);
memset(s1[y], 0, cw + 1);
memset(s2[y], 0, cw + 1);
memset(r1[y], 0, cw + 1);
memset(r2[y], 0, cw + 1);
}
if (screen1.text != NULL) {
/* Now fill up the screen from the old screen and
* saved lines. */
if (screen1.row >= ch) {
/* scroll up to save any lines that will be
* lost. */
scroll1(screen1.row - ch + 1);
screen1.row = ch - 1;
}
/* calculate working no. of lines. */
i = sline_top + screen1.row + 1;
j = i > ch ? ch - 1 : i - 1;
i = screen1.row;
screen1.row = j;
onscreen = 1;
for (; j >= 0; j--) {
if (onscreen) {
n = cw < cwidth ? cw : cwidth;
memcpy(s1[j], screen1.text[i], n);
memcpy(s2[j], screen2.text[i], n);
memcpy(r1[j], screen1.rend[i], n);
memcpy(r2[j], screen2.rend[i], n);
s1[j][cw] = screen1.text[i][cwidth];
s2[j][cw] = screen2.text[i][cwidth];
r1[j][cw] = screen1.rend[i][cwidth];
r2[j][cw] = screen2.rend[i][cwidth];
i--;
if (i < 0) {
onscreen = 0;
i = 0;
}
} else {
if (i >= sline_top)
break;
sl = sline[i];
n = cw < sl->sl_length ? cw : sl->sl_length;
memcpy(s1[j], sl->sl_text, n);
free(sl->sl_text);
if (sl->sl_rend != NULL) {
memcpy(r1[j], sl->sl_rend, n);
r1[j][cw] = sl->sl_rend[sl->sl_length];
free(sl->sl_rend);
}
i++;
}
}
if (onscreen)
abort();
for (j = i; j < sline_top; j++)
sline[j - i] = sline[j];
for (j = sline_top - i; j < sline_top; j++)
sline[j] = NULL;
sline_top -= i;
for (y = 0; y < cheight; y++) {
free(screen1.text[y]);
free(screen2.text[y]);
free(screen1.rend[y]);
free(screen2.rend[y]);
}
free((char *) screen1.text);
free((char *) screen2.text);
free((char *) screen1.rend);
free((char *) screen2.rend);
}
screen1.text = s1;
screen2.text = s2;
screen1.rend = r1;
screen2.rend = r2;
cwidth = cw;
cheight = ch;
pwidth = width;
pheight = height;
screen1.tmargin = 0;
screen1.bmargin = cheight - 1;
screen1.decom = 0;
screen1.wrap_next = 0;
screen2.tmargin = 0;
screen2.bmargin = cheight - 1;
screen2.decom = 0;
screen2.wrap_next = 0;
tty_set_size(cwidth, cheight);
scr_start_selection(0, 0, CHAR);
}
if (screen->col >= cwidth)
screen->col = cwidth - 1;
if (screen->row >= cheight)
screen->row = cheight - 1;
sbar_show(cheight + sline_top - 1, offset, offset + cheight - 1);
refresh(0, cheight - 1, 0, cwidth - 1);
cursor();
}
/* Parse the string as a sequence of character classes and use it to
* modify the char_class table.
*/
void
scr_char_class(s)
char *s;
{
int first, last, value, i;
while (isdigit(*s)) {
first = last = value = 0;
while (isdigit(*s)) {
first = first * 10 + *s - '0';
s++;
}
if (*s == '-') {
s++;
while (isdigit(*s)) {
last = last * 10 + *s - '0';
s++;
}
} else
last = first;
if (*s == ':')
s++;
while (isdigit(*s)) {
value = value * 10 + *s - '0';
s++;
}
if (*s == ',')
s++;
if (last > 127)
last = 127;
for (i = first; i <= last; i++)
char_class[i] = value;
}
}
/* Handle a backspace
*/
void
scr_backspace()
{
if (screen->col == 0 && reverse_wrap && screen->row > 0) {
cursor();
screen->row--;
screen->col = cwidth - 1;
cursor();
} else if (screen->wrap_next && reverse_wrap)
screen->wrap_next = 0;
else
scr_move(-1, 0, COL_RELATIVE | ROW_RELATIVE);
}
/* Ring the bell
*/
void
scr_bell()
{
XBell(display, 0);
}
/* Change between the alternate and the main screens
*/
void
scr_change_screen(direction)
int direction;
{
home_screen();
screen = (direction == HIGH) ? &screen2 : &screen1;
selend2.se_type = NOSEL;
refresh(0, cheight - 1, 0, cwidth - 1);
cursor();
}
/* Change the rendition style.
*/
void
scr_change_rendition(style)
int style;
{
if (style == 0)
rstyle = 0;
else
rstyle |= style;
}
/* Return the width and height of the screen.
*/
void
scr_get_size(width_p, height_p)
int *width_p, *height_p;
{
*width_p = cwidth;
*height_p = cheight;
}
/* Indicate a change of keyboard focus. type is 1 for entry events and 2 for
* focus events.
*/
void
scr_focus(type, is_in)
int type, is_in;
{
cursor();
if (is_in)
focus |= type;
else
focus &= ~type;
cursor();
}
/* Display the string at the current position. nlcount is the number of new lines
* in the string.
*/
void
scr_string(str, len, nlcount)
char *str;
int len, nlcount;
{
int x, x2, y, n, i, width;
char *s, *r;
home_screen();
cursor();
if (nlcount > 0) {
if (screen->row > screen->bmargin)
nlcount = 0;
else
nlcount -= screen->bmargin - screen->row;
if (nlcount < 0)
nlcount = 0;
else if (nlcount > screen->row - screen->tmargin)
nlcount = screen->row - screen->tmargin;
if (nlcount > MAX_SCROLL)
nlcount = MAX_SCROLL;
scroll(screen->tmargin, screen->bmargin, nlcount);
screen->row -= nlcount;
}
while (len > 0) {
if (*str == '\n') {
if (screen->row == screen->bmargin)
scroll(screen->tmargin, screen->bmargin, 1);
else if (screen->row < cheight - 1)
screen->row++;
check_selection(screen->row, screen->row);
screen->wrap_next = 0;
len--;
str++;
continue;
}
if (*str == '\r') {
screen->col = 0;
screen->wrap_next = 0;
len--;
str++;
continue;
}
if (*str == '\t') {
if (screen->col < cwidth - 1) {
s = screen->text[screen->row];
if (s[screen->col] == 0)
s[screen->col] = '\t';
screen->col++;
while (screen->col % 8 != 0 && screen->col < cwidth - 1)
screen->col++;
}
len--;
str++;
continue;
}
if (screen->wrap_next) {
screen->text[screen->row][cwidth] = 1;
if (screen->row == screen->bmargin)
scroll(screen->tmargin, screen->bmargin, 1);
else if (screen->row < cheight - 1)
screen->row++;
screen->col = 0;
screen->wrap_next = 0;
}
check_selection(screen->row, screen->row);
x = MARGIN + fwidth * screen->col;
y = MARGIN + fheight * screen->row;
for (n = 0; str[n] >= ' '; n++);
n = n + screen->col > cwidth ? cwidth - screen->col : n;
if (screen->insert) {
s = screen->text[screen->row];
r = screen->rend[screen->row];
for (i = cwidth - 1; i >= screen->col + n; i--) {
s[i] = s[i - n];
r[i] = r[i - n];
}
width = (cwidth - screen->col - n) * fwidth;
x2 = x + n * fwidth;
if (width > 0) {
XCopyArea(display, vt_win, vt_win, gc, x, y, width, fheight, x2, y);
repair_damage();
}
}
paint_rval_text(str, rstyle, n, x, y);
memcpy(screen->text[screen->row] + screen->col, str, n);
if (rstyle == 0)
memset(screen->rend[screen->row] + screen->col, 0, n);
else {
for (i = 0; i < n; i++)
screen->rend[screen->row][screen->col + i] = rstyle;
screen->rend[screen->row][cwidth] = 1;
}
len -= n;
str += n;
screen->col += n;
if (len > 0 && screen->col == cwidth && *str >= ' ') {
if (screen->wrap) {
screen->text[screen->row][cwidth] = 1;
if (screen->row == screen->bmargin)
scroll(screen->tmargin, screen->bmargin, 1);
else
screen->row++;
screen->col = 0;
} else {
screen->col = cwidth - 1;
cursor();
return;
}
}
}
if (screen->col == cwidth) {
screen->col = cwidth - 1;
screen->wrap_next = screen->wrap;
}
cursor();
}
/* Move the cursor to a new position. The relative argument is a pair of
* flags that specify relative rather than absolute motion.
*/
void
scr_move(x, y, relative)
int x, y, relative;
{
home_screen();
cursor();
screen->col = (relative & COL_RELATIVE) ? screen->col + x : x;
if (screen->col < 0)
screen->col = 0;
if (screen->col >= cwidth)
screen->col = cwidth - 1;
if (relative & ROW_RELATIVE) {
if (y > 0) {
if (screen->row <= screen->bmargin && screen->row + y > screen->bmargin)
screen->row = screen->bmargin;
else
screen->row += y;
} else if (y < 0) {
if (screen->row >= screen->tmargin && screen->row + y < screen->tmargin)
screen->row = screen->tmargin;
else
screen->row += y;
}
} else {
if (screen->decom) {
screen->row = y + screen->tmargin;
if (screen->row > screen->bmargin)
screen->row = screen->bmargin;
} else
screen->row = y;
}
if (screen->row < 0)
screen->row = 0;
if (screen->row >= cheight)
screen->row = cheight - 1;
screen->wrap_next = 0;
check_selection(screen->row, screen->row);
cursor();
}
/* Move the cursor down one line and scroll if necessary.
*/
void
scr_index()
{
home_screen();
cursor();
if (screen->row == screen->bmargin)
scroll(screen->tmargin, screen->bmargin, 1);
else
screen->row++;
screen->wrap_next = 0;
check_selection(screen->row, screen->row);
cursor();
}
/* Move the cursor up one line and scroll if necessary.
*/
void
scr_rindex()
{
home_screen();
cursor();
if (screen->row == screen->tmargin)
scroll(screen->tmargin, screen->bmargin, -1);
else
screen->row--;
screen->wrap_next = 0;
check_selection(screen->row, screen->row);
cursor();
}
/* Save the cursor position and rendition style.
*/
void
scr_save_cursor()
{
save_screen.row = screen->row;
save_screen.col = screen->col;
save_rstyle = rstyle;
}
/* Restore the cursor position and rendition style.
*/
void
scr_restore_cursor()
{
cursor();
screen->row = save_screen.row;
if (screen->row >= cheight)
screen->row = cheight - 1;
screen->col = save_screen.col;
if (screen->col >= cwidth)
screen->col = cwidth - 1;
scr_change_rendition(save_rstyle);
cursor();
}
/* erase part or the whole of a line
*/
void
scr_erase_line(mode)
int mode;
{
int i, x, y, width, height;
char *r, *s;
home_screen();
y = MARGIN + screen->row * fheight;
height = fheight;
s = screen->text[screen->row];
r = screen->rend[screen->row];
switch (mode) {
case START:
x = MARGIN;
width = (screen->col + 1) * fwidth;
memset(s, 0, screen->col + 1);
memset(r, 0, screen->col + 1);
break;
case END:
x = MARGIN + screen->col * fwidth;
width = (cwidth - screen->col) * fwidth;
memset(s + screen->col, 0, cwidth - screen->col + 1);
memset(r + screen->col, 0, cwidth - screen->col);
break;
case ENTIRE:
x = MARGIN;
width = cwidth * fwidth;
memset(s, 0, cwidth + 1);
memset(r, 0, cwidth);
break;
default:
return;
}
/* patch in the final rendition flag if there is any non-zero
* rendition. */
r[cwidth] = 0;
for (i = 0; i < cwidth; i++)
if (r[i] != 0) {
r[cwidth] = 1;
break;
}
cursor();
check_selection(screen->row, screen->row);
XClearArea(display, vt_win, x, y, width, height, False);
screen->wrap_next = 0;
cursor();
}
/* erase part or the whole of the screen
*/
void
scr_erase_screen(mode)
int mode;
{
int x, y, width, height;
int i;
home_screen();
screen->wrap_next = 0;
x = MARGIN;
width = fwidth * cwidth;
switch (mode) {
case START:
y = MARGIN;
height = screen->row * fheight;
for (i = 0; i < screen->row; i++) {
memset(screen->text[i], 0, cwidth + 1);
memset(screen->rend[i], 0, cwidth + 1);
}
check_selection(0, screen->row - 1);
if (height > 0)
XClearArea(display, vt_win, x, y, width, height, False);
scr_erase_line(mode);
break;
case END:
y = MARGIN + (screen->row + 1) * fheight;
height = (cheight - screen->row - 1) * fheight;
for (i = screen->row + 1; i < cheight; i++) {
memset(screen->text[i], 0, cwidth + 1);
memset(screen->rend[i], 0, cwidth + 1);
}
check_selection(screen->row + 1, cheight - 1);
if (height > 0)
XClearArea(display, vt_win, x, y, width, height, False);
scr_erase_line(mode);
break;
case ENTIRE:
y = MARGIN;
height = cheight * fheight;
for (i = 0; i < cheight; i++) {
memset(screen->text[i], 0, cwidth + 1);
memset(screen->rend[i], 0, cwidth + 1);
}
cursor();
check_selection(0, cheight - 1);
XClearArea(display, vt_win, x, y, width, height, False);
cursor();
break;
default:
return;
}
}
/* Delete count lines and scroll up the bottom of the screen to fill the gap
*/
void
scr_delete_lines(count)
int count;
{
if (count > screen->bmargin - screen->row + 1)
return;
home_screen();
cursor();
while (count > MAX_SCROLL) {
scroll(screen->row, screen->bmargin, MAX_SCROLL);
count -= MAX_SCROLL;
}
scroll(screen->row, screen->bmargin, count);
screen->wrap_next = 0;
cursor();
}
/* Insert count blank lines at the current position and scroll the lower lines
* down.
*/
void
scr_insert_lines(count)
int count;
{
if (screen->row > screen->bmargin)
return;
if (count > screen->bmargin - screen->row + 1)
count = screen->bmargin - screen->row + 1;
home_screen();
cursor();
while (count > MAX_SCROLL) {
scroll(screen->row, screen->bmargin, -MAX_SCROLL);
count -= MAX_SCROLL;
}
scroll(screen->row, screen->bmargin, -count);
screen->wrap_next = 0;
cursor();
}
/* Delete count characters from the current position.
*/
void
scr_delete_characters(count)
int count;
{
int x1, x2, y, width, i;
char *r, *s;
if (count > cwidth - screen->col)
count = cwidth - screen->col;
if (count <= 0)
return;
home_screen();
cursor();
check_selection(screen->row, screen->row);
s = screen->text[screen->row];
r = screen->rend[screen->row];
for (i = screen->col + count; i < cwidth; i++) {
s[i - count] = s[i];
r[i - count] = r[i];
}
memset(s + cwidth - count, 0, count);
memset(r + cwidth - count, 0, count);
y = MARGIN + screen->row * fheight;
x2 = MARGIN + screen->col * fwidth;
x1 = x2 + count * fwidth;
width = (cwidth - count - screen->col) * fwidth;
if (width > 0) {
XCopyArea(display, vt_win, vt_win, gc, x1, y, width, fheight, x2, y);
repair_damage();
}
x1 = x2 + width;
width = count * fwidth;
XClearArea(display, vt_win, x1, y, width, fheight, False);
screen->wrap_next = 0;
cursor();
}
/* Insert count spaces from the current position.
*/
void
scr_insert_characters(count)
int count;
{
int x1, x2, y, width, i;
char *r, *s;
if (count > cwidth - screen->col)
count = cwidth - screen->col;
if (count <= 0)
return;
home_screen();
cursor();
check_selection(screen->row, screen->row);
s = screen->text[screen->row];
r = screen->rend[screen->row];
for (i = cwidth - 1; i >= screen->col + count; i--) {
s[i] = s[i - count];
r[i] = r[i - count];
}
memset(s + screen->col, 0, count);
memset(r + screen->col, 0, count);
y = MARGIN + screen->row * fheight;
x1 = MARGIN + screen->col * fwidth;
x2 = x1 + count * fwidth;
width = (cwidth - count - screen->col) * fwidth;
if (width > 0)
XCopyArea(display, vt_win, vt_win, negc, x1, y, width, fheight, x2, y);
x1 = MARGIN + screen->col * fwidth;
width = count * fwidth;
XClearArea(display, vt_win, x1, y, width, fheight, False);
screen->wrap_next = 0;
cursor();
}
/* Tab to the next tab_stop.
*/
void
scr_tab()
{
home_screen();
if (screen->col == cwidth - 1)
return;
cursor();
check_selection(screen->row, screen->row);
if (screen->text[screen->row][screen->col] == 0)
screen->text[screen->row][screen->col] = '\t';
screen->col++;
while (screen->col % 8 != 0 && screen->col < cwidth - 1)
screen->col++;
cursor();
}
/* Attempt to set the top ans bottom scroll margins.
*/
void
scr_set_margins(top, bottom)
int top, bottom;
{
if (top < 0)
top = 0;
if (bottom >= cheight)
bottom = cheight - 1;
if (top > bottom)
return;
screen->tmargin = top;
screen->bmargin = bottom;
scr_move(0, 0, 0);
}
/* Set or unset automatic wrapping.
*/
void
scr_set_wrap(mode)
int mode;
{
screen->wrap = mode == HIGH;
}
/* Set or unset margin origin mode.
*/
void
scr_set_decom(mode)
int mode;
{
screen->decom = mode == HIGH;
scr_move(0, 0, 0);
}
/* Set or unset automatic insert mode.
*/
void
scr_set_insert(mode)
int mode;
{
screen->insert = mode == HIGH;
}
/* Fill the screen with 'E's - useful for testing.
*/
void
scr_efill()
{
int x, y;
for (y = 0; y < cheight; y++)
for (x = 0; x < cwidth; x++) {
screen->text[y][x] = 'E';
screen->rend[y][x] = 0;
}
home_screen();
check_selection(0, cheight - 1);
refresh(0, cheight - 1, 0, cwidth - 1);
cursor();
}
/* Move the display so that line represented by scrollbar value y is at the top
* of the screen.
*/
void
scr_move_to(y)
int y;
{
int n, lnum;
y = pheight - 1 - y;
lnum = y * (cheight + sline_top - 1) / (pheight - 1);
n = lnum - cheight + 1;
change_offset(n);
}
/* Move the display by a distance represented by the value.
*/
void
scr_move_by(y)
int y;
{
int n;
if (y >= 0) {
y = (y - MARGIN) / fheight;
n = offset - y;
} else {
y = (-y - MARGIN) / fheight;
n = offset + y;
}
change_offset(n);
}
/* Make the selection currently delimited by the selection end markers.
*/
void
scr_make_selection(time)
int time;
{
if (save_selection() < 0)
return;
XSetSelectionOwner(display, XA_PRIMARY, vt_win, (Time) time);
if (XGetSelectionOwner(display, XA_PRIMARY) != vt_win)
error("Could not get primary selection");
/* Place in CUT_BUFFER0 for backup. */
XChangeProperty(display, DefaultRootWindow(display), XA_CUT_BUFFER0,
XA_STRING, 8, PropModeReplace, selection_text, selection_length);
}
/* respond to a request for our current selection.
*/
void
scr_send_selection(time, requestor, target, property)
int time, requestor, target, property;
{
XEvent event;
int status;
event.xselection.type = SelectionNotify;
event.xselection.selection = XA_PRIMARY;
event.xselection.target = XA_STRING;
event.xselection.requestor = requestor;
event.xselection.time = time;
if (target == XA_STRING) {
XChangeProperty(display, requestor, property, XA_STRING, 8, PropModeReplace,
selection_text, selection_length);
event.xselection.property = property;
} else
event.xselection.property = None;
status = XSendEvent(display, requestor, False, 0, &event);
}
/* Request the current primary selection
*/
void
scr_request_selection(time, x, y)
int time;
int x, y;
{
Atom sel_property;
/* First check that the release is within the window. */
if (x < 0 || x >= pwidth || y < 0 || y >= pheight)
return;
if (selection_text != NULL) {
/* The selection is internal */
send_string(selection_text, selection_length);
return;
}
if (XGetSelectionOwner(display, XA_PRIMARY) == None) {
/* No primary selection so use the cut buffer. */
Atom actual_type;
int actual_format;
unsigned long nitems, bytes_after, nread;
unsigned char *data;
nread = 0;
do {
if (XGetWindowProperty(display, DefaultRootWindow(display),
XA_CUT_BUFFER0, nread / 4, PROP_SIZE, False,
XA_STRING, &actual_type, &actual_format,
&nitems, &bytes_after, &data) != Success)
return;
send_string(data, nitems);
nread += nitems;
XFree(data);
} while (bytes_after > 0);
return;
}
sel_property = XInternAtom(display, "VT_SELECTION", False);
XConvertSelection(display, XA_PRIMARY, XA_STRING, sel_property, vt_win, time);
}
/* Respond to a notification that a primary selection has been sent
*/
void
scr_paste_primary(time, window, property)
int time, window, property;
{
Atom actual_type;
int actual_format;
unsigned long nitems, bytes_after, nread;
unsigned char *data;
nread = 0;
do {
if (XGetWindowProperty(display, window, property, nread / 4, PROP_SIZE, True,
AnyPropertyType, &actual_type, &actual_format,
&nitems, &bytes_after, &data) != Success)
return;
if (actual_type != XA_STRING)
return;
send_string(data, nitems);
nread += nitems;
XFree(data);
} while (bytes_after > 0);
}
/* Clear the current selection.
*/
void
scr_clear_selection()
{
if (selection_text != NULL) {
free(selection_text);
selection_text = NULL;
selection_length = 0;
}
show_selection(0, cheight - 1, 0, cwidth - 1);
selend1.se_type = selend2.se_type = NOSEL;
}
/* Extend the selection.
*/
void
scr_extend_selection(x, y, drag)
int x, y;
int drag;
{
int row, col, r1, r2, c1, c2;
struct selst sesave1, sesave2;
struct selst *se;
if (selend1.se_type == NOSEL)
return;
col = (x - MARGIN) / fwidth;
row = (y - MARGIN) / fheight;
fix_rc(&row, &col);
if (selend2.se_type == NOSEL) {
rc_to_selend(row, col, &selend2);
show_selection(0, cheight - 1, 0, cwidth - 1);
return;
}
sesave1 = selend1;
sesave2 = selend2;
if (drag) {
/* Anchor the start end. */
selend1 = selanchor;
rc_to_selend(row, col, &selend2);
adjust_selection(&selend2);
} else {
selend_to_rc(&r1, &c1, &selend1);
selend_to_rc(&r2, &c2, &selend2);
/* Determine which is the nearest endpoint. */
if (abs(r1 - row) < abs(r2 - row))
se = &selend1;
else if (abs(r2 - row) < abs(r1 - row))
se = &selend2;
else if (r1 == r2) {
if (row < r1)
se = (c1 < c2) ? &selend1 : &selend2;
else if (row > r1)
se = (c1 > c2) ? &selend1 : &selend2;
else
se = abs(c1 - col) < abs(c2 - col) ? &selend1 : &selend2;
} else
se = &selend2;
rc_to_selend(row, col, se);
adjust_selection(se);
}
change_selection(&sesave1, &sesave2);
}
/* start a selection using the specified unit.
*/
void
scr_start_selection(x, y, unit)
int x, y;
enum selunit unit;
{
int row, col;
show_selection(0, cheight - 1, 0, cwidth - 1);
col = (x - MARGIN) / fwidth;
row = (y - MARGIN) / fheight;
selection_unit = unit;
fix_rc(&row, &col);
rc_to_selend(row, col, &selanchor);
selend2 = selend1 = selanchor;
adjust_selection(&selend2);
show_selection(0, cheight - 1, 0, cwidth - 1);
}
/* Send the name of the current display to the command.
*/
void
scr_report_display()
{
char *dname;
static char hostname[32 + 6];
dname = DisplayString(display);
if (strncmp(dname, "unix:", 5) == 0) {
gethostname(hostname, sizeof(hostname));
cprintf("%s%s\n", hostname, dname + 4);
} else
cprintf("%s\n", dname);
}
/* Report the current cursor position.
*/
void
scr_report_position()
{
cprintf("\033[%d;%dR", screen->row + 1, screen->col + 1);
}
/* Check for and repair any damage after copying an area of the window.
*/
static void
repair_damage(void)
{
XEvent event;
int row1, row2, col1, col2;
do {
/* Get the next graphics exposure or noexposure event. */
XIfEvent(display, &event, grexornoex, NULL);
if (event.type == NoExpose)
return;
row1 = (event.xgraphicsexpose.y - MARGIN) / fheight;
if (row1 < 0)
row1 = 0;
row2 = (event.xgraphicsexpose.y + event.xgraphicsexpose.height - MARGIN) / fheight;
if (row2 >= cheight)
row2 = cheight - 1;
col1 = (event.xgraphicsexpose.x - MARGIN) / fwidth;
if (col1 < 0)
col1 = 0;
col2 = (event.xgraphicsexpose.x + event.xgraphicsexpose.width - MARGIN) / fwidth;
if (col2 >= cwidth)
col2 = cwidth - 1;
refresh(row1, row2, col1, col2);
} while (event.xgraphicsexpose.count > 0);
}
/* Return true if the event is a graphics exposure or noexposure.
*/
static Bool
grexornoex(dpy, ev, arg)
Display *dpy;
XEvent *ev;
char *arg;
{
return (ev->type == GraphicsExpose || ev->type == NoExpose);
}
/* Change the value of the scrolled screen offset and repaint the screen
*/
static void
change_offset(n)
int n;
{
int y1, y2, height, d;
if (n > sline_top)
n = sline_top;
if (n < 0)
n = 0;
if (n == offset)
return;
cursor();
d = n - offset;
offset = n;
if (d > 0 && d < cheight) {
/* Text has moved down by less than a screen so raster the
* lines that did not move off. */
y1 = MARGIN;
y2 = y1 + d * fheight;
height = (cheight - d) * fheight;
XCopyArea(display, vt_win, vt_win, gc, 0, y1, pwidth, height, 0, y2);
refresh(0, d - 1, 0, cwidth - 1);
repair_damage();
} else if (d < 0 && -d < cheight) {
/* Text has moved down by less than a screen. */
d = -d;
y2 = MARGIN;
y1 = y2 + d * fheight;
height = (cheight - d) * fheight;
XCopyArea(display, vt_win, vt_win, gc, 0, y1, pwidth, height, 0, y2);
refresh(cheight - d, cheight - 1, 0, cwidth - 1);
repair_damage();
} else
refresh(0, cheight - 1, 0, cwidth - 1);
cursor();
sbar_show(cheight + sline_top - 1, offset, offset + cheight - 1);
}
/* Paint the text using the rendition value at the screen position.
*/
static void
paint_rval_text(str, rval, len, x, y)
char *str;
int rval;
int len, x, y;
{
if (rval & RS_RVID) {
XSetForeground(display, gc, background);
XSetBackground(display, gc, foreground);
}
if (rval & RS_BOLD && boldfont != NULL) {
XSetFont(display, gc, boldfont->fid);
y += boldfont->ascent;
} else
y += mainfont->ascent;
XDrawImageString(display, vt_win, gc, x, y, str, len);
y++;
if (rval & RS_ULINE)
XDrawLine(display, vt_win, gc, x, y, x + len * fwidth, y);
if (rval & RS_RVID) {
XSetForeground(display, gc, foreground);
XSetBackground(display, gc, background);
}
if (rval & RS_BOLD)
XSetFont(display, gc, mainfont->fid);
}
/* Display the string using the rendition vector at the screen coordinates
*/
static void
paint_rvec_text(str, rvec, len, x, y)
char *str, *rvec;
int len, x, y;
{
int i;
if (rvec == NULL) {
paint_rval_text(str, 0, len, x, y);
return;
}
while (len > 0) {
for (i = 0; i < len; i++)
if (rvec[i] != rvec[0])
break;
paint_rval_text(str, rvec[0], i, x, y);
str += i;
rvec += i;
len -= i;
x += i * fwidth;
}
}
/* Repaint the box delimited by row1 to row2 and col1 to col2 of the displayed
* screen from the backup screen.
*/
static void
refresh(row1, row2, col1, col2)
int row1, row2, col1, col2;
{
char *s, *str, *r;
int x, y, x1, y1, x2, width, i, m;
struct slinest *sl;
str = (char *) cmalloc(cwidth + 1);
y = row1;
x1 = MARGIN + col1 * fwidth;
y1 = MARGIN + row1 * fheight;
/* First do any 'scrolled off' lines that are visible. */
for (i = offset - 1 - row1; y <= row2 && i >= 0; y++, i--) {
sl = sline[i];
m = (col2 + 1) < sl->sl_length ? (col2 + 1) : sl->sl_length;
s = sl->sl_text;
m -= col1;
for (x = 0; x < m; x++)
str[x] = s[x + col1] < ' ' ? ' ' : s[x + col1];
r = sl->sl_rend == NULL ? NULL : sl->sl_rend + col1;
paint_rvec_text(str, r, m, x1, y1);
x2 = x1 + m * fwidth;
width = (col2 - col1 + 1 - m) * fwidth;
if (width > 0)
XClearArea(display, vt_win, x2, y1, width, fheight, False);
y1 += fheight;
}
/* Now do the remainder from the current screen */
i = offset > row1 ? 0 : row1 - offset;
for (; y <= row2; y++, i++) {
s = screen->text[i];
m = col1 - 1;
for (x = col1; x <= col2; x++)
if (s[x] < ' ')
str[x - col1] = ' ';
else {
str[x - col1] = s[x];
m = x;
}
m++;
m -= col1;
r = screen->rend[i][cwidth] == 0 ? NULL : screen->rend[i] + col1;
paint_rvec_text(str, r, m, x1, y1);
x2 = x1 + m * fwidth;
width = (col2 - col1 + 1 - m) * fwidth;
if (width > 0)
XClearArea(display, vt_win, x2, y1, width, fheight, False);
y1 += fheight;
}
free(str);
show_selection(row1, row2, col1, col2);
}
/* Paint any part of the selection that is between rows row1 and row2 inclusive
* and between cols col1 and col2 inclusive.
*/
static void
show_selection(row1, row2, col1, col2)
int row1, row2, col1, col2;
{
int r1, c1, r2, c2, sr, sc, er, ec;
int x1, x2, y, row;
if (selend1.se_type == NOSEL || selend2.se_type == NOSEL)
return;
if (selcmp(&selend1, &selend2) == 0)
return;
selend_to_rc(&r1, &c1, &selend1);
selend_to_rc(&r2, &c2, &selend2);
col2++;
/* Obtain initial and final endpoints for the selection. */
if (r1 < r2 || (r1 == r2 && c1 <= c2)) {
sr = r1;
sc = c1;
er = r2;
ec = c2;
} else {
sr = r2;
sc = c2;
er = r1;
ec = c1;
}
if (sr < row1) {
sr = row1;
sc = col1;
}
if (sc < col1)
sc = col1;
if (er > row2) {
er = row2;
ec = col2;
}
if (ec > col2)
ec = col2;
if (sr > er)
return;
/* Paint in the reverse video. */
for (row = sr; row <= er; row++) {
y = MARGIN + row * fheight;
x1 = MARGIN + (row == sr ? sc : col1) * fwidth;
x2 = MARGIN + ((row == er) ? ec : col2) * fwidth;
if (x2 > x1)
XFillRectangle(display, vt_win, hlgc, x1, y, x2 - x1, fheight);
}
}
/* Scroll count lines from row1 to row2 inclusive. row1 should be <= row1.
* scrolling is up for a +ve count and down for a -ve count.
* count is limited to a maximum of MAX_SCROLL lines.
*/
static void
scroll(row1, row2, count)
int row1, row2, count;
{
int y1, y2, height, i, j;
char *save[MAX_SCROLL], *rend[MAX_SCROLL];
struct slinest *sl;
char *r, *s;
row2++;
if (row1 == 0 && screen == &screen1 && count > 0) {
/* Save lines that scroll of the top of the screen. */
for (i = 1; i <= count; i++) {
if ((sl = sline[sline_max - i]) != NULL) {
free(sl->sl_text);
if (sl->sl_rend != NULL)
free(sl->sl_rend);
free((char *) sl);
}
if (selend1.se_type == SAVED && selend1.se_index == sline_max - i) {
show_selection(0, cheight - 1, 0, cwidth - 1);
selend1.se_type = NOSEL;
}
if (selend2.se_type == SAVED && selend2.se_index == sline_max - i) {
show_selection(0, cheight - 1, 0, cwidth - 1);
selend2.se_type = NOSEL;
}
}
for (i = sline_max - count - 1; i >= 0; i--) {
sline[i + count] = sline[i];
if (selend1.se_type == SAVED && selend1.se_index == i)
selend1.se_index = i + count;
if (selend2.se_type == SAVED && selend2.se_index == i)
selend2.se_index = i + count;
}
for (i = 0; i < count; i++) {
s = screen->text[i];
r = screen->rend[i];
for (j = cwidth - 1; j >= 0 && s[j] == 0; j--);
j++;
sl = (struct slinest *) cmalloc(sizeof(struct slinest));
sl->sl_text = (char *) cmalloc(j + 1);
memcpy(sl->sl_text, s, j);
sl->sl_text[j] = s[cwidth];
if (r[cwidth] == 0)
sl->sl_rend = NULL;
else {
sl->sl_rend = (char *) cmalloc(j + 1);
memcpy(sl->sl_rend, r, j);
}
sl->sl_length = j;
sline[count - i - 1] = sl;
if (selend1.se_type == SCREEN && selend1.se_index == i) {
selend1.se_type = SAVED;
selend1.se_index = count - i - 1;
}
if (selend2.se_type == SCREEN && selend2.se_index == i) {
selend2.se_type = SAVED;
selend2.se_index = count - i - 1;
}
}
sline_top += count;
if (sline_top > sline_max)
sline_top = sline_max;
sbar_show(cheight + sline_top - 1, offset, offset + cheight - 1);
}
if (count > 0) {
j = row1;
for (i = 0; i < count; i++, j++) {
save[i] = screen->text[j];
rend[i] = screen->rend[j];
if (selend1.se_type == SCREEN && selend1.se_index == j) {
show_selection(0, cheight - 1, 0, cwidth - 1);
selend1.se_type = NOSEL;
}
if (selend2.se_type == SCREEN && selend2.se_index == j) {
show_selection(0, cheight - 1, 0, cwidth - 1);
selend2.se_type = NOSEL;
}
}
for (; j < row2; j++) {
screen->text[j - count] = screen->text[j];
screen->rend[j - count] = screen->rend[j];
if (selend1.se_type == SCREEN && selend1.se_index == j)
selend1.se_index = j - count;
if (selend2.se_type == SCREEN && selend2.se_index == j)
selend2.se_index = j - count;
}
for (i = 0; i < count; i++) {
memset(save[i], 0, cwidth + 1);
screen->text[row2 - i - 1] = save[i];
memset(rend[i], 0, cwidth + 1);
screen->rend[row2 - i - 1] = rend[i];
}
if (count < row2 - row1) {
y2 = MARGIN + row1 * fheight;
y1 = y2 + count * fheight;
height = (row2 - row1 - count) * fheight;
XCopyArea(display, vt_win, vt_win, gc, 0, y1, pwidth, height, 0, y2);
repair_damage();
}
height = count * fheight;
y1 = MARGIN + (row2 - count) * fheight;
XClearArea(display, vt_win, 0, y1, pwidth, height, False);
}
if (count < 0) {
count = -count;
j = row2 - 1;
for (i = 0; i < count; i++, j--) {
save[i] = screen->text[j];
rend[i] = screen->rend[j];
if (selend1.se_type == SCREEN && selend1.se_index == j) {
show_selection(0, cheight - 1, 0, cwidth - 1);
selend1.se_type = NOSEL;
}
if (selend2.se_type == SCREEN && selend2.se_index == j) {
show_selection(0, cheight - 1, 0, cwidth - 1);
selend2.se_type = NOSEL;
}
}
for (; j >= row1; j--) {
screen->text[j + count] = screen->text[j];
screen->rend[j + count] = screen->rend[j];
if (selend1.se_type == SCREEN && selend1.se_index == j)
selend1.se_index = j + count;
if (selend2.se_type == SCREEN && selend2.se_index == j)
selend2.se_index = j + count;
}
for (i = 0; i < count; i++) {
memset(save[i], 0, cwidth + 1);
screen->text[row1 + i] = save[i];
memset(rend[i], 0, cwidth + 1);
screen->rend[row1 + i] = rend[i];
}
if (count < row2 - row1) {
y1 = MARGIN + row1 * fheight;
y2 = y1 + count * fheight;
height = (row2 - row1 - count) * fheight;
XCopyArea(display, vt_win, vt_win, gc, 0, y1, pwidth, height, 0, y2);
repair_damage();
}
height = count * fheight;
y1 = MARGIN + row1 * fheight;
XClearArea(display, vt_win, 0, y1, pwidth, height, False);
}
}
/* Scroll screen1 up by count lines saving lines as needed. This is used
* after the screen size is reduced.
*/
static void
scroll1(count)
int count;
{
int i, j, n;
char *save[MAX_SCROLL], *rend[MAX_SCROLL];
struct slinest *sl;
char *r, *s;
while (count > 0) {
/* If count is greater than MAX_SCROLL then scroll in
* installements. */
n = count > MAX_SCROLL ? MAX_SCROLL : count;
count -= n;
/* Save lines that scroll of the top of the screen. */
for (i = 1; i <= n; i++) {
if ((sl = sline[sline_max - i]) != NULL) {
free(sl->sl_text);
if (sl->sl_rend != NULL)
free(sl->sl_rend);
free((char *) sl);
}
}
for (i = sline_max - n - 1; i >= 0; i--) {
sline[i + n] = sline[i];
}
for (i = 0; i < n; i++) {
s = screen1.text[i];
r = screen1.rend[i];
for (j = cwidth - 1; j >= 0 && s[j] == 0; j--);
j++;
sl = (struct slinest *) cmalloc(sizeof(struct slinest));
sl->sl_text = (char *) cmalloc(j + 1);
memcpy(sl->sl_text, s, j);
sl->sl_text[j] = s[cwidth];
if (r[cwidth] == 0)
sl->sl_rend = NULL;
else {
sl->sl_rend = (char *) cmalloc(j + 1);
memcpy(sl->sl_rend, r, j);
}
sl->sl_length = j;
sline[n - i - 1] = sl;
}
sline_top += n;
if (sline_top > sline_max)
sline_top = sline_max;
j = 0;
for (i = 0; i < n; i++, j++) {
save[i] = screen1.text[j];
rend[i] = screen1.rend[j];
}
for (; j < cheight; j++) {
screen1.text[j - n] = screen1.text[j];
screen1.rend[j - n] = screen1.rend[j];
}
for (i = 0; i < n; i++) {
memset(save[i], 0, cwidth + 1);
screen1.text[cheight - i - 1] = save[i];
memset(rend[i], 0, cwidth + 1);
screen1.rend[cheight - i - 1] = rend[i];
}
}
}
/* Reposition the scrolled text so that the scrollbar is at the bottom.
*/
static void
home_screen(void)
{
if (offset > 0) {
offset = 0;
refresh(0, cheight - 1, 0, cwidth - 1);
cursor();
sbar_show(cheight + sline_top - 1, 0, cheight - 1);
}
}
/* Draw the cursor at the current position.
*/
static void
cursor(void)
{
int x, y;
if (offset > 0)
return;
x = MARGIN + fwidth * screen->col;
y = MARGIN + fheight * screen->row;
XFillRectangle(display, vt_win, hlgc, x, y, fwidth, fheight);
if (focus == 0)
XFillRectangle(display, vt_win, hlgc, x + 1, y + 1, fwidth - 2, fheight - 2);
}
/* Convert a row and column coordinates into a selection endpoint.
*/
static void
rc_to_selend(row, col, se)
int row, col;
struct selst *se;
{
int i;
i = (row - offset);
if (i >= 0)
se->se_type = SCREEN;
else {
se->se_type = SAVED;
i = -1 - i;
}
se->se_index = i;
se->se_col = col;
}
/* Fix the coordinates so that they are within the screen and do not lie within
* empty space.
*/
static void
fix_rc(rowp, colp)
int *rowp, *colp;
{
int i, len, row, col;
char *s;
col = *colp;
if (col < 0)
col = 0;
if (col > cwidth)
col = cwidth;
row = *rowp;
if (row < 0)
row = 0;
if (row >= cheight)
row = cheight - 1;
if (selection_unit == CHAR) {
i = (row - offset);
if (i >= 0) {
s = screen->text[i];
if (col > 0 && s[col - 1] < ' ')
while (col < cwidth && s[col] < ' ')
col++;
} else {
i = -1 - i;
len = sline[i]->sl_length;
s = sline[i]->sl_text;
if (col > 0 && s[col - 1] < ' ')
while (col <= len && s[col] < ' ')
col++;
if (col > len)
col = cwidth;
}
}
*colp = col;
*rowp = row;
}
/* Convert the selection into a row and column.
*/
static void
selend_to_rc(rowp, colp, se)
int *rowp, *colp;
struct selst *se;
{
if (se->se_type == NOSEL)
return;
*colp = se->se_col;
if (se->se_type == SCREEN)
*rowp = se->se_index + offset;
else
*rowp = offset - se->se_index - 1;
}
/* Repaint the displayed selection to reflect the new value. ose1 and ose2
* are assumed to represent the currently displayed selection endpoints.
*/
static void
change_selection(ose1, ose2)
struct selst *ose1, *ose2;
{
int rs, cs, re, ce, n;
int row;
int row1, row2;
int x1, x2, y;
struct selst *se, *se1, *se2;
if (selcmp(ose1, ose2) > 0) {
se = ose1;
ose1 = ose2;
ose2 = se;
}
if (selcmp(&selend1, &selend2) <= 0) {
se1 = &selend1;
se2 = &selend2;
} else {
se1 = &selend2;
se2 = &selend1;
}
if ((n = selcmp(se1, ose1)) != 0) {
/* repaint the start. */
if (n < 0) {
selend_to_rc(&rs, &cs, se1);
selend_to_rc(&re, &ce, ose1);
} else {
selend_to_rc(&rs, &cs, ose1);
selend_to_rc(&re, &ce, se1);
}
row1 = rs < 0 ? 0 : rs;
row2 = re >= cheight ? cheight - 1 : re;
/* Invert the changed area */
for (row = row1; row <= row2; row++) {
y = MARGIN + row * fheight;
x1 = MARGIN + (row == rs ? cs * fwidth : 0);
x2 = MARGIN + ((row == re) ? ce : cwidth) * fwidth;
XFillRectangle(display, vt_win, hlgc, x1, y, x2 - x1, fheight);
}
}
if ((n = selcmp(se2, ose2)) != 0) {
/* repaint the end. */
if (n < 0) {
selend_to_rc(&rs, &cs, se2);
selend_to_rc(&re, &ce, ose2);
} else {
selend_to_rc(&rs, &cs, ose2);
selend_to_rc(&re, &ce, se2);
}
row1 = rs < 0 ? 0 : rs;
row2 = re >= cheight ? cheight - 1 : re;
/* Invert the changed area */
for (row = row1; row <= row2; row++) {
y = MARGIN + row * fheight;
x1 = MARGIN + (row == rs ? cs * fwidth : 0);
x2 = MARGIN + ((row == re) ? ce : cwidth) * fwidth;
XFillRectangle(display, vt_win, hlgc, x1, y, x2 - x1, fheight);
}
}
}
/* Convert a section of displayed text line into a text string suitable for pasting.
* *lenp is the length of the input string, i1 is index of the first character to
* convert and i2 is the last. The length of the returned string is returned
* in *lenp;
*/
static char *
convert_line(str, lenp, i1, i2)
char *str;
int *lenp;
int i1, i2;
{
static char buf[MAX_WIDTH + 3];
char *s;
int i;
int newline;
newline = (i2 + 1 == cwidth) && (str[*lenp] == 0);
if (i2 >= *lenp)
i2 = *lenp - 1;
if (i2 - i1 >= MAX_WIDTH)
i2 = i1 + MAX_WIDTH;
while (i2 >= i1 && str[i2] == 0)
i2--;
s = buf;
for (i = i1; i <= i2; i++) {
if (str[i] >= ' ')
*s++ = str[i];
else if (str[i] == '\t') {
*s++ = '\t';
while (i < i2 && str[i + 1] == 0)
i++;
} else
*s++ = ' ';
}
if (newline)
*s++ = '\n';
*s = 0;
*lenp = s - buf;
return (buf);
}
/* Compare the two selections and return -1, 0 or 1 depending on
* whether se2 is after, equal to or before se1.
*/
static int
selcmp(se1, se2)
struct selst *se1, *se2;
{
if (se1->se_type == SAVED && se2->se_type == SAVED) {
if (se1->se_index > se2->se_index)
return (-1);
if (se1->se_index < se2->se_index)
return (1);
if (se1->se_col < se2->se_col)
return (-1);
if (se2->se_col < se1->se_col)
return (1);
return (0);
}
if (se1->se_type == SCREEN && se2->se_type == SCREEN) {
if (se1->se_index < se2->se_index)
return (-1);
if (se1->se_index > se2->se_index)
return (1);
if (se1->se_col < se2->se_col)
return (-1);
if (se2->se_col < se1->se_col)
return (1);
return (0);
}
if (se1->se_type == SAVED)
return (-1);
return (1);
}
/* Adjust the selection to a word or line boundary. If the include endpoint is
* non NULL then the selection is forced to be large enough to include it.
*/
static void
adjust_selection(include)
struct selst *include;
{
struct selst *se1, *se2;
int i, len;
char *s;
if (selection_unit == CHAR)
return;
if (selcmp(&selend1, &selend2) <= 0) {
se1 = &selend1;
se2 = &selend2;
} else {
se2 = &selend1;
se1 = &selend2;
}
if (selection_unit == WORD) {
i = se1->se_col;
s = se1->se_type == SCREEN
? screen->text[se1->se_index]
: sline[se1->se_index]->sl_text;
while (i > 0 && cclass(s[i]) == cclass(s[i - 1]))
i--;
se1->se_col = i;
i = se2->se_col;
if (se2 == include || selcmp(se2, &selanchor) == 0)
i++;
if (se2->se_type == SCREEN) {
s = screen->text[se2->se_index];
len = cwidth;
} else {
s = sline[se2->se_index]->sl_text;
len = sline[se2->se_index]->sl_length;
}
while (i < len && cclass(s[i]) == cclass(s[i - 1]))
i++;
se2->se_col = (i > len) ? cwidth : i;
} else if (selection_unit == LINE) {
se1->se_col = 0;
se2->se_col = cwidth;
}
}
/* Convert the currently marked screen selection as a text string and save it
* as the current saved selection. 0 is returned for a success, -1 for a failure.
*/
static int
save_selection(void)
{
char *str, *s;
int i, len, total, col1, col2;
struct selst *se1, *se2;
struct slinest *sl;
if (selend1.se_type == NOSEL || selend2.se_type == NOSEL)
return (-1);
if (selend1.se_type == selend2.se_type
&& selend1.se_index == selend2.se_index
&& selend1.se_col == selend2.se_col)
return (-1);
if (selection_text != NULL)
free(selection_text);
/* Set se1 and se2 to point to the first and second selection
* endpoints. */
if (selcmp(&selend1, &selend2) <= 0) {
se1 = &selend1;
se2 = &selend2;
} else {
se2 = &selend1;
se1 = &selend2;
}
str = (char *) cmalloc(total = 1);
if (se1->se_type == SAVED) {
col1 = se1->se_col;
for (i = se1->se_index; i >= 0; i--) {
sl = sline[i];
if (se2->se_type == SAVED && se2->se_index == i) {
col2 = se2->se_col - 1;
i = 0; /* force loop exit */
} else
col2 = cwidth - 1;
len = sl->sl_length;
s = convert_line(sl->sl_text, &len, col1, col2);
str = realloc(str, total + len);
strncpy(str + total - 1, s, len);
total += len;
col1 = 0;
}
}
if (se2->se_type == SCREEN) {
if (se1->se_type == SCREEN) {
i = se1->se_index;
col1 = se1->se_col;
} else {
i = 0;
col1 = 0;
}
for (; i <= se2->se_index; i++) {
col2 = i == se2->se_index ? se2->se_col : cwidth;
if (--col2 < 0)
break;
len = cwidth;
s = convert_line(screen->text[i], &len, col1, col2);
str = realloc(str, total + len);
strncpy(str + total - 1, s, len);
total += len;
col1 = 0;
}
}
str[total - 1] = 0;
selection_text = str;
selection_length = total - 1;
return (0);
}
/* Determine if the current selection overlaps row1-row2 and if it does then
* remove it from the screen.
*/
static void
check_selection(row1, row2)
int row1, row2;
{
int r1, r2, x;
if (selend1.se_type == NOSEL || selend2.se_type == NOSEL)
return;
r1 = selend1.se_type == SCREEN ? selend1.se_index : -1;
r2 = selend2.se_type == SCREEN ? selend2.se_index : -1;
if (r1 > r2) {
x = r1;
r1 = r2;
r2 = x;
}
if (row2 < r1 || row1 > r2)
return;
show_selection(0, cheight - 1, 0, cwidth - 1);
selend2.se_type = NOSEL;
}
/* Return a character class for selecting words
*/
static int
cclass(c)
int c;
{
return (char_class[c]);
}
syntax highlighted by Code2HTML, v. 0.9.1