3344 lines
93 KiB
C
3344 lines
93 KiB
C
/*
|
|
* File: screen.c
|
|
*
|
|
*/
|
|
|
|
static const char cvs_ident[] = "$Id$";
|
|
|
|
#include "config.h"
|
|
#include "feature.h"
|
|
|
|
/* includes */
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#ifdef HAVE_SYS_TIME_H
|
|
# include <sys/time.h>
|
|
#endif
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#ifdef HAVE_UNISTD_H
|
|
# include <unistd.h>
|
|
#endif
|
|
#ifdef HAVE_SYS_IOCTL_H
|
|
# include <sys/ioctl.h>
|
|
#endif
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
#include <X11/Xatom.h>
|
|
#include <X11/Xmd.h> /* CARD32 */
|
|
#ifdef HAVE_X11_XMU_ATOMS_H
|
|
# include <X11/Xmu/Atoms.h>
|
|
#endif
|
|
|
|
#include "buttons.h"
|
|
#include "command.h"
|
|
#include "font.h"
|
|
#include "startup.h"
|
|
#include "screen.h"
|
|
#include "scrollbar.h"
|
|
#include "options.h"
|
|
#include "pixmap.h"
|
|
#include "profile.h"
|
|
#include "term.h"
|
|
|
|
/* These arrays store the text and rendering info that were last drawn to the screen. */
|
|
static text_t **drawn_text = NULL;
|
|
static rend_t **drawn_rend = NULL;
|
|
|
|
/* These are used for buffering during text scrolls. */
|
|
static text_t **buf_text = NULL;
|
|
static rend_t **buf_rend = NULL;
|
|
|
|
/* Tab stop locations */
|
|
static char *tabs = NULL;
|
|
|
|
static screen_t screen =
|
|
{
|
|
NULL, NULL, 0, 0, 0, 0, 0, Screen_DefaultFlags
|
|
};
|
|
|
|
static screen_t swap =
|
|
{
|
|
NULL, NULL, 0, 0, 0, 0, 0, Screen_DefaultFlags
|
|
};
|
|
|
|
static save_t save =
|
|
{
|
|
0, 0, 0, 'B', DEFAULT_RSTYLE
|
|
};
|
|
|
|
static selection_t selection =
|
|
{
|
|
NULL, 0, SELECTION_CLEAR, PRIMARY, 0,
|
|
{0, 0},
|
|
{0, 0},
|
|
{0, 0}
|
|
};
|
|
|
|
static char charsets[4] =
|
|
{
|
|
'B', 'B', 'B', 'B' /* all ascii */
|
|
};
|
|
|
|
static short current_screen = PRIMARY;
|
|
static rend_t rstyle = DEFAULT_RSTYLE;
|
|
static short rvideo = 0;
|
|
int prev_nrow = -1, prev_ncol = -1;
|
|
unsigned char refresh_all = 0;
|
|
|
|
#ifdef MULTI_CHARSET
|
|
static short multi_byte = 0;
|
|
static short lost_multi = 0;
|
|
static enum {
|
|
SBYTE, WBYTE
|
|
} chstat = SBYTE;
|
|
encoding_t encoding_method = LATIN1;
|
|
|
|
#define RESET_CHSTAT if (chstat == WBYTE) chstat = SBYTE, lost_multi = 1
|
|
#else
|
|
#define RESET_CHSTAT
|
|
#endif
|
|
|
|
/* Fill part/all of a drawn line with blanks. */
|
|
inline void blank_line(text_t *, rend_t *, int, rend_t);
|
|
inline void
|
|
blank_line(text_t *et, rend_t *er, int width, rend_t efs)
|
|
{
|
|
/* int i = width; */
|
|
register unsigned int i = width;
|
|
rend_t *r = er, fs = efs;
|
|
|
|
MEMSET(et, ' ', i);
|
|
for (; i--;)
|
|
*r++ = fs;
|
|
}
|
|
|
|
/* Create a new row in the screen buffer and initialize it. */
|
|
inline void blank_screen_mem(text_t **, rend_t **, int, rend_t);
|
|
inline void
|
|
blank_screen_mem(text_t **tp, rend_t **rp, int row, rend_t efs)
|
|
{
|
|
register unsigned int i = TermWin.ncol;
|
|
rend_t *r, fs = efs;
|
|
|
|
if (tp[row] == NULL) {
|
|
tp[row] = MALLOC(sizeof(text_t) * (TermWin.ncol + 1));
|
|
rp[row] = MALLOC(sizeof(rend_t) * TermWin.ncol);
|
|
}
|
|
MEMSET(tp[row], ' ', i);
|
|
tp[row][i] = 0;
|
|
for (r = rp[row]; i--;)
|
|
*r++ = fs;
|
|
}
|
|
|
|
void
|
|
scr_reset(void)
|
|
{
|
|
int total_rows, prev_total_rows, chscr = 0;
|
|
register int i, j, k;
|
|
text_t tc;
|
|
|
|
D_SCREEN(("scr_reset()\n"));
|
|
|
|
TermWin.view_start = 0;
|
|
RESET_CHSTAT;
|
|
|
|
if (TermWin.ncol == prev_ncol && TermWin.nrow == prev_nrow)
|
|
return;
|
|
|
|
if (current_screen != PRIMARY) {
|
|
short tmp = TermWin.nrow;
|
|
|
|
TermWin.nrow = prev_nrow;
|
|
chscr = scr_change_screen(PRIMARY);
|
|
TermWin.nrow = tmp;
|
|
}
|
|
if (TermWin.ncol <= 0)
|
|
TermWin.ncol = 80;
|
|
if (TermWin.nrow <= 0)
|
|
TermWin.nrow = 24;
|
|
if (TermWin.saveLines <= 0)
|
|
TermWin.saveLines = 0;
|
|
|
|
total_rows = TermWin.nrow + TermWin.saveLines;
|
|
prev_total_rows = prev_nrow + TermWin.saveLines;
|
|
|
|
screen.tscroll = 0;
|
|
screen.bscroll = (TermWin.nrow - 1);
|
|
if (prev_nrow == -1) {
|
|
/*
|
|
* A: first time called so just malloc everything : don't rely on realloc
|
|
* Note: this is still needed so that all the scrollback lines are NULL
|
|
*/
|
|
screen.text = CALLOC(text_t *, total_rows);
|
|
buf_text = CALLOC(text_t *, total_rows);
|
|
drawn_text = CALLOC(text_t *, TermWin.nrow);
|
|
swap.text = CALLOC(text_t *, TermWin.nrow);
|
|
|
|
screen.rend = CALLOC(rend_t *, total_rows);
|
|
buf_rend = CALLOC(rend_t *, total_rows);
|
|
drawn_rend = CALLOC(rend_t *, TermWin.nrow);
|
|
swap.rend = CALLOC(rend_t *, TermWin.nrow);
|
|
D_SCREEN(("screen.text == %8p, screen.rend == %8p, swap.text == %8p, swap.rend == %8p\n", screen.text, screen.rend, swap.text, swap.rend));
|
|
|
|
for (i = 0; i < TermWin.nrow; i++) {
|
|
j = i + TermWin.saveLines;
|
|
blank_screen_mem(screen.text, screen.rend, j, DEFAULT_RSTYLE);
|
|
blank_screen_mem(swap.text, swap.rend, i, DEFAULT_RSTYLE);
|
|
blank_screen_mem(drawn_text, drawn_rend, i, DEFAULT_RSTYLE);
|
|
}
|
|
TermWin.nscrolled = 0; /* no saved lines */
|
|
|
|
} else {
|
|
/*
|
|
* B1: add or delete rows as appropriate
|
|
*/
|
|
if (TermWin.nrow < prev_nrow) {
|
|
/* delete rows */
|
|
k = MIN(TermWin.nscrolled, prev_nrow - TermWin.nrow);
|
|
scroll_text(0, prev_nrow - 1, k, 1);
|
|
|
|
for (i = TermWin.nrow; i < prev_nrow; i++) {
|
|
j = i + TermWin.saveLines;
|
|
if (screen.text[j]) {
|
|
FREE(screen.text[j]);
|
|
FREE(screen.rend[j]);
|
|
}
|
|
if (swap.text[i]) {
|
|
FREE(swap.text[i]);
|
|
FREE(swap.rend[i]);
|
|
}
|
|
if (drawn_text[i]) {
|
|
FREE(drawn_text[i]);
|
|
FREE(drawn_rend[i]);
|
|
}
|
|
}
|
|
screen.text = REALLOC(screen.text, total_rows * sizeof(text_t*));
|
|
buf_text = REALLOC(buf_text , total_rows * sizeof(text_t*));
|
|
drawn_text = REALLOC(drawn_text , TermWin.nrow * sizeof(text_t*));
|
|
swap.text = REALLOC(swap.text , TermWin.nrow * sizeof(text_t*));
|
|
|
|
screen.rend = REALLOC(screen.rend, total_rows * sizeof(rend_t*));
|
|
buf_rend = REALLOC(buf_rend , total_rows * sizeof(rend_t*));
|
|
drawn_rend = REALLOC(drawn_rend , TermWin.nrow * sizeof(rend_t*));
|
|
swap.rend = REALLOC(swap.rend , TermWin.nrow * sizeof(rend_t*));
|
|
D_SCREEN(("screen.text == %8p, screen.rend == %8p, swap.text == %8p, swap.rend == %8p\n", screen.text, screen.rend, swap.text, swap.rend));
|
|
|
|
/* we have fewer rows so fix up number of scrolled lines */
|
|
UPPER_BOUND(screen.row, TermWin.nrow - 1);
|
|
|
|
} else if (TermWin.nrow > prev_nrow) {
|
|
/* add rows */
|
|
screen.text = REALLOC(screen.text, total_rows * sizeof(text_t*));
|
|
buf_text = REALLOC(buf_text , total_rows * sizeof(text_t*));
|
|
drawn_text = REALLOC(drawn_text , TermWin.nrow * sizeof(text_t*));
|
|
swap.text = REALLOC(swap.text , TermWin.nrow * sizeof(text_t*));
|
|
|
|
screen.rend = REALLOC(screen.rend, total_rows * sizeof(rend_t*));
|
|
buf_rend = REALLOC(buf_rend , total_rows * sizeof(rend_t*));
|
|
drawn_rend = REALLOC(drawn_rend , TermWin.nrow * sizeof(rend_t*));
|
|
swap.rend = REALLOC(swap.rend , TermWin.nrow * sizeof(rend_t*));
|
|
D_SCREEN(("screen.text == %8p, screen.rend == %8p, swap.text == %8p, swap.rend == %8p\n", screen.text, screen.rend, swap.text, swap.rend));
|
|
|
|
k = MIN(TermWin.nscrolled, TermWin.nrow - prev_nrow);
|
|
for (i = prev_total_rows; i < total_rows - k; i++) {
|
|
screen.text[i] = NULL;
|
|
blank_screen_mem(screen.text, screen.rend, i, DEFAULT_RSTYLE);
|
|
}
|
|
for ( /* i = total_rows - k */ ; i < total_rows; i++) {
|
|
screen.text[i] = NULL;
|
|
screen.rend[i] = NULL;
|
|
}
|
|
for (i = prev_nrow; i < TermWin.nrow; i++) {
|
|
swap.text[i] = NULL;
|
|
drawn_text[i] = NULL;
|
|
blank_screen_mem(swap.text, swap.rend, i, DEFAULT_RSTYLE);
|
|
blank_screen_mem(drawn_text, drawn_rend, i, DEFAULT_RSTYLE);
|
|
}
|
|
if (k > 0) {
|
|
scroll_text(0, TermWin.nrow - 1, -k, 1);
|
|
screen.row += k;
|
|
TermWin.nscrolled -= k;
|
|
for (i = TermWin.saveLines - TermWin.nscrolled; k--; i--) {
|
|
if (screen.text[i] == NULL) {
|
|
blank_screen_mem(screen.text, screen.rend, i, DEFAULT_RSTYLE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* B2: resize columns */
|
|
if (TermWin.ncol != prev_ncol) {
|
|
for (i = 0; i < total_rows; i++) {
|
|
if (screen.text[i]) {
|
|
tc = screen.text[i][prev_ncol];
|
|
screen.text[i] = REALLOC(screen.text[i], (TermWin.ncol + 1) * sizeof(text_t));
|
|
screen.rend[i] = REALLOC(screen.rend[i], TermWin.ncol * sizeof(rend_t));
|
|
screen.text[i][TermWin.ncol] = MIN(tc, TermWin.ncol);
|
|
if (TermWin.ncol > prev_ncol)
|
|
blank_line(&(screen.text[i][prev_ncol]),
|
|
&(screen.rend[i][prev_ncol]),
|
|
TermWin.ncol - prev_ncol, DEFAULT_RSTYLE);
|
|
}
|
|
}
|
|
for (i = 0; i < TermWin.nrow; i++) {
|
|
drawn_text[i] = REALLOC(drawn_text[i], (TermWin.ncol + 1) * sizeof(text_t));
|
|
drawn_rend[i] = REALLOC(drawn_rend[i], TermWin.ncol * sizeof(rend_t));
|
|
if (swap.text[i]) {
|
|
tc = swap.text[i][prev_ncol];
|
|
swap.text[i] = REALLOC(swap.text[i], (TermWin.ncol + 1) * sizeof(text_t));
|
|
swap.rend[i] = REALLOC(swap.rend[i], TermWin.ncol * sizeof(rend_t));
|
|
swap.text[i][TermWin.ncol] = MIN(tc, TermWin.ncol);
|
|
if (TermWin.ncol > prev_ncol)
|
|
blank_line(&(swap.text[i][prev_ncol]),
|
|
&(swap.rend[i][prev_ncol]),
|
|
TermWin.ncol - prev_ncol, DEFAULT_RSTYLE);
|
|
}
|
|
if (TermWin.ncol > prev_ncol)
|
|
blank_line(&(drawn_text[i][prev_ncol]),
|
|
&(drawn_rend[i][prev_ncol]),
|
|
TermWin.ncol - prev_ncol, DEFAULT_RSTYLE);
|
|
}
|
|
}
|
|
if (tabs)
|
|
FREE(tabs);
|
|
}
|
|
tabs = MALLOC(TermWin.ncol);
|
|
|
|
for (i = 0; i < TermWin.ncol; i++)
|
|
tabs[i] = (i % TABSIZE == 0) ? 1 : 0;
|
|
|
|
prev_nrow = TermWin.nrow;
|
|
prev_ncol = TermWin.ncol;
|
|
|
|
tt_resize();
|
|
if (chscr) {
|
|
scr_change_screen(chscr);
|
|
}
|
|
}
|
|
|
|
/* Release all screen memory. */
|
|
void
|
|
scr_release(void)
|
|
{
|
|
int total_rows;
|
|
register int i;
|
|
|
|
total_rows = TermWin.nrow + TermWin.saveLines;
|
|
for (i = 0; i < total_rows; i++) {
|
|
if (screen.text[i]) {
|
|
FREE(screen.text[i]);
|
|
FREE(screen.rend[i]);
|
|
}
|
|
}
|
|
for (i = 0; i < TermWin.nrow; i++) {
|
|
FREE(drawn_text[i]);
|
|
FREE(drawn_rend[i]);
|
|
FREE(swap.text[i]);
|
|
FREE(swap.rend[i]);
|
|
}
|
|
FREE(screen.text);
|
|
FREE(screen.rend);
|
|
FREE(drawn_text);
|
|
FREE(drawn_rend);
|
|
FREE(swap.text);
|
|
FREE(swap.rend);
|
|
FREE(buf_text);
|
|
FREE(buf_rend);
|
|
FREE(tabs);
|
|
}
|
|
|
|
/* Perform a full reset on the terminal. Called by the "\ec" sequence or by an xterm color change. */
|
|
void
|
|
scr_poweron(void)
|
|
{
|
|
D_SCREEN(("scr_poweron()\n"));
|
|
|
|
/* Reset all character sets to Latin1 */
|
|
MEMSET(charsets, 'B', sizeof(charsets));
|
|
rvideo = 0;
|
|
/* Reset the rendering style to the default colors/style */
|
|
scr_rendition(0, ~RS_None);
|
|
#if NSCREENS
|
|
/* Reset the secondary screen */
|
|
scr_change_screen(SECONDARY);
|
|
scr_erase_screen(2);
|
|
swap.tscroll = 0;
|
|
swap.bscroll = TermWin.nrow - 1;
|
|
swap.row = swap.col = 0;
|
|
swap.charset = 0;
|
|
swap.flags = Screen_DefaultFlags;
|
|
#endif
|
|
/* Reset the primary screen */
|
|
scr_change_screen(PRIMARY);
|
|
scr_erase_screen(2);
|
|
screen.row = screen.col = 0;
|
|
screen.charset = 0;
|
|
screen.flags = Screen_DefaultFlags;
|
|
|
|
scr_cursor(SAVE);
|
|
scr_reset();
|
|
scr_refresh(SLOW_REFRESH);
|
|
}
|
|
|
|
/*
|
|
* Save and Restore cursor
|
|
* XTERM_SEQ: Save cursor : ESC 7
|
|
* XTERM_SEQ: Restore cursor: ESC 8
|
|
*/
|
|
void
|
|
scr_cursor(int mode)
|
|
{
|
|
D_SCREEN(("scr_cursor(%s)\n", (mode == SAVE ? "SAVE" : "RESTORE")));
|
|
|
|
switch (mode) {
|
|
case SAVE:
|
|
save.row = screen.row;
|
|
save.col = screen.col;
|
|
save.rstyle = rstyle;
|
|
save.charset = screen.charset;
|
|
save.charset_char = charsets[screen.charset];
|
|
break;
|
|
case RESTORE:
|
|
screen.row = save.row;
|
|
screen.col = save.col;
|
|
rstyle = save.rstyle;
|
|
screen.charset = save.charset;
|
|
charsets[screen.charset] = save.charset_char;
|
|
set_font_style();
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Swap between primary and secondary screens
|
|
* XTERM_SEQ: Primary screen : ESC [ ? 4 7 h
|
|
* XTERM_SEQ: Secondary screen: ESC [ ? 4 7 l
|
|
*/
|
|
int
|
|
scr_change_screen(int scrn)
|
|
{
|
|
int i, offset, tmp;
|
|
text_t *t0;
|
|
rend_t *r0;
|
|
|
|
D_SCREEN(("scr_change_screen(%d)\n", scrn));
|
|
|
|
TermWin.view_start = 0;
|
|
RESET_CHSTAT;
|
|
|
|
if (current_screen == scrn)
|
|
return current_screen;
|
|
|
|
SWAP_IT(current_screen, scrn, tmp);
|
|
#if NSCREENS
|
|
offset = TermWin.saveLines;
|
|
if (!screen.text || !screen.rend)
|
|
return (current_screen);
|
|
for (i = TermWin.nrow; i--;) {
|
|
SWAP_IT(screen.text[i + offset], swap.text[i], t0);
|
|
SWAP_IT(screen.rend[i + offset], swap.rend[i], r0);
|
|
}
|
|
SWAP_IT(screen.row, swap.row, tmp);
|
|
SWAP_IT(screen.col, swap.col, tmp);
|
|
SWAP_IT(screen.charset, swap.charset, tmp);
|
|
SWAP_IT(screen.flags, swap.flags, tmp);
|
|
screen.flags |= Screen_VisibleCursor;
|
|
swap.flags |= Screen_VisibleCursor;
|
|
|
|
#else
|
|
# ifndef DONT_SCROLL_ME
|
|
if (current_screen == PRIMARY)
|
|
if (current_screen == PRIMARY) {
|
|
scroll_text(0, (TermWin.nrow - 1), TermWin.nrow, 0);
|
|
for (i = TermWin.saveLines; i < TermWin.nrow + TermWin.saveLines; i++)
|
|
if (screen.text[i] == NULL) {
|
|
blank_screen_mem(screen.text, screen.rend, i, DEFAULT_RSTYLE);
|
|
}
|
|
}
|
|
# endif
|
|
#endif
|
|
|
|
return scrn;
|
|
}
|
|
|
|
/*
|
|
* Change the color for following text
|
|
*/
|
|
void
|
|
scr_color(unsigned int color, unsigned int Intensity)
|
|
{
|
|
|
|
D_SCREEN(("scr_color(%u, %u) called.\n", color, Intensity));
|
|
if (color == restoreFG)
|
|
color = fgColor;
|
|
else if (color == restoreBG)
|
|
color = bgColor;
|
|
else {
|
|
if (Xdepth <= 2) { /* Monochrome - ignore color changes */
|
|
switch (Intensity) {
|
|
case RS_Bold:
|
|
color = fgColor;
|
|
break;
|
|
case RS_Blink:
|
|
color = bgColor;
|
|
break;
|
|
}
|
|
} else {
|
|
#ifndef NO_BRIGHTCOLOR
|
|
if ((rstyle & Intensity) && color >= minColor && color <= maxColor)
|
|
color += (minBright - minColor);
|
|
else if (color >= minBright && color <= maxBright) {
|
|
if (rstyle & Intensity)
|
|
return;
|
|
color -= (minBright - minColor);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
switch (Intensity) {
|
|
case RS_Bold:
|
|
rstyle = SET_FGCOLOR(rstyle, color);
|
|
break;
|
|
case RS_Blink:
|
|
rstyle = SET_BGCOLOR(rstyle, color);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Change the rendition style for following text
|
|
*/
|
|
void
|
|
scr_rendition(int set, int style)
|
|
{
|
|
unsigned int color;
|
|
int old_style = rstyle;
|
|
|
|
D_SCREEN(("scr_rendition(%d, %d) called.\n", set, style));
|
|
if (set) {
|
|
/* A: Set style */
|
|
rstyle |= style;
|
|
switch (style) {
|
|
case RS_RVid:
|
|
if (rvideo)
|
|
rstyle &= ~RS_RVid;
|
|
break;
|
|
#ifndef NO_BRIGHTCOLOR
|
|
case RS_Bold:
|
|
color = GET_FGCOLOR(rstyle);
|
|
scr_color((color == fgColor ? GET_FGCOLOR(colorfgbg) : color),
|
|
RS_Bold);
|
|
break;
|
|
case RS_Blink:
|
|
color = GET_BGCOLOR(rstyle);
|
|
scr_color((color == bgColor ? GET_BGCOLOR(colorfgbg) : color),
|
|
RS_Blink);
|
|
break;
|
|
#endif
|
|
}
|
|
} else {
|
|
/* B: Unset style */
|
|
rstyle &= ~style;
|
|
|
|
switch (style) {
|
|
case ~RS_None: /* default fg/bg colors */
|
|
rstyle = DEFAULT_RSTYLE | (old_style & RS_fontMask);
|
|
/* FALLTHROUGH */
|
|
case RS_RVid:
|
|
if (rvideo)
|
|
rstyle |= RS_RVid;
|
|
break;
|
|
#ifndef NO_BRIGHTCOLOR
|
|
case RS_Bold:
|
|
color = GET_FGCOLOR(rstyle);
|
|
if (color >= minBright && color <= maxBright) {
|
|
scr_color(color, RS_Bold);
|
|
if ((rstyle & RS_fgMask) == (colorfgbg & RS_fgMask))
|
|
scr_color(restoreFG, RS_Bold);
|
|
}
|
|
break;
|
|
case RS_Blink:
|
|
color = GET_BGCOLOR(rstyle);
|
|
if (color >= minBright && color <= maxBright) {
|
|
scr_color(color, RS_Blink);
|
|
if ((rstyle & RS_bgMask) == (colorfgbg & RS_bgMask))
|
|
scr_color(restoreBG, RS_Blink);
|
|
}
|
|
break;
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Scroll text region from <row1> through <row2> by <count> lines */
|
|
int
|
|
scroll_text(int row1, int row2, int count, int spec)
|
|
{
|
|
register int i, j;
|
|
|
|
PROF_INIT(scroll_text);
|
|
D_SCREEN(("scroll_text(%d,%d,%d,%d): %s\n", row1, row2, count, spec, (current_screen == PRIMARY) ? "Primary" : "Secondary"));
|
|
|
|
if (count == 0 || (row1 > row2))
|
|
return 0;
|
|
if ((count > 0) && (row1 == 0) && (current_screen == PRIMARY)) {
|
|
TermWin.nscrolled += count;
|
|
UPPER_BOUND(TermWin.nscrolled, TermWin.saveLines);
|
|
} else if (!spec)
|
|
row1 += TermWin.saveLines;
|
|
row2 += TermWin.saveLines;
|
|
|
|
if (selection.op && current_screen == selection.screen) {
|
|
i = selection.beg.row + TermWin.saveLines;
|
|
j = selection.end.row + TermWin.saveLines;
|
|
if ((i < row1 && j > row1)
|
|
|| (i < row2 && j > row2)
|
|
|| (i - count < row1 && i >= row1)
|
|
|| (i - count > row2 && i <= row2)
|
|
|| (j - count < row1 && j >= row1)
|
|
|| (j - count > row2 && j <= row2)) {
|
|
CLEAR_ALL_SELECTION;
|
|
selection.op = SELECTION_CLEAR;
|
|
} else if (j >= row1 && j <= row2) {
|
|
/* move selected region too */
|
|
selection.beg.row -= count;
|
|
selection.end.row -= count;
|
|
selection.mark.row -= count;
|
|
}
|
|
}
|
|
CHECK_SELECTION;
|
|
|
|
if (count > 0) {
|
|
|
|
/* A: scroll up */
|
|
|
|
UPPER_BOUND(count, row2 - row1 + 1);
|
|
|
|
/* A1: Copy and blank out lines that will get clobbered by the rotation */
|
|
for (i = 0, j = row1; i < count; i++, j++) {
|
|
buf_text[i] = screen.text[j];
|
|
buf_rend[i] = screen.rend[j];
|
|
if (buf_text[i] == NULL) {
|
|
/* A new ALLOC is done with size ncol and
|
|
blankline with size prev_ncol -- Sebastien van K */
|
|
buf_text[i] = MALLOC(sizeof(text_t) * (prev_ncol + 1));
|
|
buf_rend[i] = MALLOC(sizeof(rend_t) * prev_ncol);
|
|
}
|
|
blank_line(buf_text[i], buf_rend[i], prev_ncol, DEFAULT_RSTYLE);
|
|
buf_text[i][prev_ncol] = 0;
|
|
}
|
|
/* A2: Rotate lines */
|
|
for (j = row1; (j + count) <= row2; j++) {
|
|
screen.text[j] = screen.text[j + count];
|
|
screen.rend[j] = screen.rend[j + count];
|
|
}
|
|
/* A3: Resurrect lines */
|
|
for (i = 0; i < count; i++, j++) {
|
|
screen.text[j] = buf_text[i];
|
|
screen.rend[j] = buf_rend[i];
|
|
}
|
|
} else if (count < 0) {
|
|
/* B: scroll down */
|
|
|
|
count = MIN(-count, row2 - row1 + 1);
|
|
/* B1: Copy and blank out lines that will get clobbered by the rotation */
|
|
for (i = 0, j = row2; i < count; i++, j--) {
|
|
buf_text[i] = screen.text[j];
|
|
buf_rend[i] = screen.rend[j];
|
|
if (buf_text[i] == NULL) {
|
|
/* A new ALLOC is done with size ncol and
|
|
blankline with size prev_ncol -- Sebastien van K */
|
|
buf_text[i] = MALLOC(sizeof(text_t) * (prev_ncol + 1));
|
|
buf_rend[i] = MALLOC(sizeof(rend_t) * prev_ncol);
|
|
}
|
|
blank_line(buf_text[i], buf_rend[i], prev_ncol, DEFAULT_RSTYLE);
|
|
buf_text[i][prev_ncol] = 0;
|
|
}
|
|
/* B2: Rotate lines */
|
|
for (j = row2; (j - count) >= row1; j--) {
|
|
screen.text[j] = screen.text[j - count];
|
|
screen.rend[j] = screen.rend[j - count];
|
|
}
|
|
/* B3: Resurrect lines */
|
|
for (i = 0, j = row1; i < count; i++, j++) {
|
|
screen.text[j] = buf_text[i];
|
|
screen.rend[j] = buf_rend[i];
|
|
}
|
|
count = -count;
|
|
}
|
|
PROF_DONE(scroll_text);
|
|
PROF_TIME(scroll_text);
|
|
return count;
|
|
}
|
|
|
|
/*
|
|
* Add text given in <str> of length <len> to screen struct
|
|
*/
|
|
void
|
|
scr_add_lines(const unsigned char *str, int nlines, int len)
|
|
{
|
|
/* char c; */
|
|
register char c;
|
|
|
|
/* int i, j, row, last_col; */
|
|
int last_col;
|
|
register int i, j, row;
|
|
text_t *stp;
|
|
rend_t *srp;
|
|
row_col_t beg, end;
|
|
|
|
if (len <= 0) /* sanity */
|
|
return;
|
|
|
|
last_col = TermWin.ncol;
|
|
|
|
D_SCREEN(("scr_add_lines(*,%d,%d)\n", nlines, len));
|
|
ZERO_SCROLLBACK;
|
|
if (nlines > 0) {
|
|
nlines += (screen.row - screen.bscroll);
|
|
D_SCREEN((" -> screen.row == %d, screen.bscroll == %d, new nlines == %d\n", screen.row, screen.bscroll, nlines));
|
|
if ((nlines > 0) && (screen.tscroll == 0) && (screen.bscroll == (TermWin.nrow - 1))) {
|
|
/* _at least_ this many lines need to be scrolled */
|
|
scroll_text(screen.tscroll, screen.bscroll, nlines, 0);
|
|
for (i = nlines, row = screen.bscroll + TermWin.saveLines + 1; row > 0 && i--;) {
|
|
/* Move row-- to beginning of loop to avoid segfault. -- added by Sebastien van K */
|
|
row--;
|
|
blank_screen_mem(screen.text, screen.rend, row, rstyle);
|
|
}
|
|
screen.row -= nlines;
|
|
}
|
|
}
|
|
UPPER_BOUND(screen.col, last_col - 1);
|
|
BOUND(screen.row, -TermWin.nscrolled, TermWin.nrow - 1);
|
|
|
|
row = screen.row + TermWin.saveLines;
|
|
if (screen.text[row] == NULL) {
|
|
blank_screen_mem(screen.text, screen.rend, row, DEFAULT_RSTYLE);
|
|
} /* avoid segfault -- added by Sebastien van K */
|
|
beg.row = screen.row;
|
|
beg.col = screen.col;
|
|
stp = screen.text[row];
|
|
srp = screen.rend[row];
|
|
|
|
#ifdef MULTI_CHARSET
|
|
if (lost_multi && screen.col > 0
|
|
&& ((srp[screen.col - 1] & RS_multiMask) == RS_multi1)
|
|
&& *str != '\n' && *str != '\r' && *str != '\t')
|
|
chstat = WBYTE;
|
|
#endif
|
|
|
|
for (i = 0; i < len;) {
|
|
c = str[i++];
|
|
#ifdef MULTI_CHARSET
|
|
if ((encoding_method != LATIN1) && (chstat == WBYTE)) {
|
|
rstyle |= RS_multiMask; /* multibyte 2nd byte */
|
|
chstat = SBYTE;
|
|
if (encoding_method == EUCJ) {
|
|
c |= 0x80; /* maybe overkill, but makes it selectable */
|
|
}
|
|
} else if (chstat == SBYTE) {
|
|
if ((encoding_method != LATIN1) && (multi_byte || (c & 0x80))) { /* multibyte 1st byte */
|
|
rstyle &= ~RS_multiMask;
|
|
rstyle |= RS_multi1;
|
|
chstat = WBYTE;
|
|
if (encoding_method == EUCJ) {
|
|
c |= 0x80; /* maybe overkill, but makes it selectable */
|
|
}
|
|
} else
|
|
#endif
|
|
switch (c) {
|
|
case 127:
|
|
continue; /* ummmm..... */
|
|
case '\t':
|
|
scr_tab(1);
|
|
continue;
|
|
case '\n':
|
|
LOWER_BOUND(stp[last_col], screen.col);
|
|
screen.flags &= ~Screen_WrapNext;
|
|
if (screen.row == screen.bscroll) {
|
|
scroll_text(screen.tscroll, screen.bscroll, 1, 0);
|
|
j = screen.bscroll + TermWin.saveLines;
|
|
blank_screen_mem(screen.text, screen.rend, j, rstyle & ~RS_Uline);
|
|
} else if (screen.row < (TermWin.nrow - 1)) {
|
|
screen.row++;
|
|
row = screen.row + TermWin.saveLines;
|
|
}
|
|
stp = screen.text[row]; /* _must_ refresh */
|
|
srp = screen.rend[row]; /* _must_ refresh */
|
|
continue;
|
|
case '\r':
|
|
LOWER_BOUND(stp[last_col], screen.col);
|
|
screen.flags &= ~Screen_WrapNext;
|
|
screen.col = 0;
|
|
continue;
|
|
default:
|
|
#ifdef MULTI_CHARSET
|
|
rstyle &= ~RS_multiMask;
|
|
#endif
|
|
break;
|
|
}
|
|
#ifdef MULTI_CHARSET
|
|
}
|
|
#endif
|
|
if (screen.flags & Screen_WrapNext) {
|
|
stp[last_col] = WRAP_CHAR;
|
|
if (screen.row == screen.bscroll) {
|
|
scroll_text(screen.tscroll, screen.bscroll, 1, 0);
|
|
j = screen.bscroll + TermWin.saveLines;
|
|
/* blank_line(screen.text[j], screen.rend[j], TermWin.ncol,
|
|
rstyle); Bug fix from John Ellison - need to reset rstyle */
|
|
blank_screen_mem(screen.text, screen.rend, j, rstyle & ~RS_Uline);
|
|
} else if (screen.row < (TermWin.nrow - 1)) {
|
|
screen.row++;
|
|
row = screen.row + TermWin.saveLines;
|
|
}
|
|
stp = screen.text[row]; /* _must_ refresh */
|
|
srp = screen.rend[row]; /* _must_ refresh */
|
|
screen.col = 0;
|
|
screen.flags &= ~Screen_WrapNext;
|
|
}
|
|
if (screen.flags & Screen_Insert)
|
|
scr_insdel_chars(1, INSERT);
|
|
stp[screen.col] = c;
|
|
srp[screen.col] = rstyle;
|
|
if (screen.col < (last_col - 1))
|
|
screen.col++;
|
|
else {
|
|
stp[last_col] = last_col;
|
|
if (screen.flags & Screen_Autowrap)
|
|
screen.flags |= Screen_WrapNext;
|
|
else
|
|
screen.flags &= ~Screen_WrapNext;
|
|
}
|
|
}
|
|
LOWER_BOUND(stp[last_col], screen.col);
|
|
if (screen.col == 0) {
|
|
end.col = last_col - 1;
|
|
end.row = screen.row - 1;
|
|
} else {
|
|
end.col = screen.col - 1;
|
|
end.row = screen.row;
|
|
}
|
|
if (((selection.end.row > beg.row)
|
|
|| (selection.end.row == beg.row
|
|
&& selection.end.col >= beg.col))
|
|
&& ((selection.beg.row < end.row)
|
|
|| (selection.beg.row == end.row
|
|
&& selection.beg.col <= end.col)))
|
|
selection_reset();
|
|
}
|
|
|
|
/*
|
|
* Process Backspace. Move back the cursor back a position, wrap if have to
|
|
* XTERM_SEQ: CTRL-H
|
|
*/
|
|
void
|
|
scr_backspace(void)
|
|
{
|
|
|
|
RESET_CHSTAT;
|
|
if (screen.col == 0 && screen.row > 0) {
|
|
screen.col = TermWin.ncol - 1;
|
|
screen.row--;
|
|
} else if (screen.flags & Screen_WrapNext) {
|
|
screen.flags &= ~Screen_WrapNext;
|
|
} else
|
|
scr_gotorc(0, -1, RELATIVE);
|
|
}
|
|
|
|
/*
|
|
* Process Horizontal Tab
|
|
* count: +ve = forward; -ve = backwards
|
|
* XTERM_SEQ: CTRL-I
|
|
*/
|
|
void
|
|
scr_tab(int count)
|
|
{
|
|
int i, x;
|
|
|
|
RESET_CHSTAT;
|
|
x = screen.col;
|
|
if (count == 0)
|
|
return;
|
|
else if (count > 0) {
|
|
for (i = x + 1; i < TermWin.ncol; i++) {
|
|
if (tabs[i]) {
|
|
x = i;
|
|
if (!--count)
|
|
break;
|
|
}
|
|
}
|
|
} else if (count < 0) {
|
|
for (i = x - 1; i >= 0; i--) {
|
|
if (tabs[i]) {
|
|
x = i;
|
|
if (!++count)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (x != screen.col)
|
|
scr_gotorc(0, x, R_RELATIVE);
|
|
}
|
|
|
|
/*
|
|
* Goto Row/Column
|
|
*/
|
|
void
|
|
scr_gotorc(int row, int col, int relative)
|
|
{
|
|
ZERO_SCROLLBACK;
|
|
RESET_CHSTAT;
|
|
|
|
screen.col = ((relative & C_RELATIVE) ? (screen.col + col) : col);
|
|
BOUND(screen.col, 0, TermWin.ncol - 1);
|
|
|
|
if (screen.flags & Screen_WrapNext) {
|
|
screen.flags &= ~Screen_WrapNext;
|
|
}
|
|
if (relative & R_RELATIVE) {
|
|
if (row > 0) {
|
|
if (screen.row <= screen.bscroll
|
|
&& (screen.row + row) > screen.bscroll)
|
|
screen.row = screen.bscroll;
|
|
else
|
|
screen.row += row;
|
|
} else if (row < 0) {
|
|
if (screen.row >= screen.tscroll
|
|
&& (screen.row + row) < screen.tscroll)
|
|
screen.row = screen.tscroll;
|
|
else
|
|
screen.row += row;
|
|
}
|
|
} else {
|
|
if (screen.flags & Screen_Relative) { /* relative origin mode */
|
|
screen.row = row + screen.tscroll;
|
|
UPPER_BOUND(screen.row, screen.bscroll);
|
|
} else
|
|
screen.row = row;
|
|
}
|
|
BOUND(screen.row, 0, TermWin.nrow - 1);
|
|
}
|
|
|
|
/*
|
|
* direction should be UP or DN
|
|
*/
|
|
void
|
|
scr_index(int direction)
|
|
{
|
|
int dirn;
|
|
|
|
dirn = ((direction == UP) ? 1 : -1);
|
|
D_SCREEN(("scr_index(%d)\n", dirn));
|
|
|
|
ZERO_SCROLLBACK;
|
|
RESET_CHSTAT;
|
|
|
|
if (screen.flags & Screen_WrapNext) {
|
|
screen.flags &= ~Screen_WrapNext;
|
|
}
|
|
if ((screen.row == screen.bscroll && direction == UP)
|
|
|| (screen.row == screen.tscroll && direction == DN)) {
|
|
scroll_text(screen.tscroll, screen.bscroll, dirn, 0);
|
|
if (direction == UP)
|
|
dirn = screen.bscroll + TermWin.saveLines;
|
|
else
|
|
dirn = screen.tscroll + TermWin.saveLines;
|
|
blank_screen_mem(screen.text, screen.rend, dirn, rstyle);
|
|
} else
|
|
screen.row += dirn;
|
|
BOUND(screen.row, 0, TermWin.nrow - 1);
|
|
CHECK_SELECTION;
|
|
}
|
|
|
|
/*
|
|
* Erase part or whole of a line
|
|
* XTERM_SEQ: Clear line to right: ESC [ 0 K
|
|
* XTERM_SEQ: Clear line to left : ESC [ 1 K
|
|
* XTERM_SEQ: Clear whole line : ESC [ 2 K
|
|
*/
|
|
void
|
|
scr_erase_line(int mode)
|
|
{
|
|
int row, col, num;
|
|
|
|
D_SCREEN(("scr_erase_line(%d) at screen row: %d\n", mode, screen.row));
|
|
ZERO_SCROLLBACK;
|
|
RESET_CHSTAT;
|
|
|
|
if (screen.flags & Screen_WrapNext)
|
|
screen.flags &= ~Screen_WrapNext;
|
|
|
|
row = TermWin.saveLines + screen.row;
|
|
if (screen.text[row]) {
|
|
switch (mode) {
|
|
case 0: /* erase to end of line */
|
|
col = screen.col;
|
|
num = TermWin.ncol - col;
|
|
UPPER_BOUND(screen.text[row][TermWin.ncol], col);
|
|
break;
|
|
case 1: /* erase to beginning of line */
|
|
col = 0;
|
|
num = screen.col + 1;
|
|
break;
|
|
case 2: /* erase whole line */
|
|
col = 0;
|
|
num = TermWin.ncol;
|
|
screen.text[row][TermWin.ncol] = 0;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
blank_line(&(screen.text[row][col]), &(screen.rend[row][col]), num, rstyle & ~RS_Uline);
|
|
} else {
|
|
blank_screen_mem(screen.text, screen.rend, row, rstyle & ~RS_Uline);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Erase part of whole of the screen
|
|
* XTERM_SEQ: Clear screen after cursor : ESC [ 0 J
|
|
* XTERM_SEQ: Clear screen before cursor: ESC [ 1 J
|
|
* XTERM_SEQ: Clear whole screen : ESC [ 2 J
|
|
*/
|
|
void
|
|
scr_erase_screen(int mode)
|
|
{
|
|
int row, num, row_offset;
|
|
rend_t ren;
|
|
long gcmask;
|
|
XGCValues gcvalue;
|
|
Pixmap pmap = None;
|
|
Drawable draw_buffer;
|
|
|
|
if (buffer_pixmap) {
|
|
draw_buffer = buffer_pixmap;
|
|
pmap = images[image_bg].current->pmap->pixmap;
|
|
} else {
|
|
draw_buffer = TermWin.vt;
|
|
}
|
|
|
|
D_SCREEN(("scr_erase_screen(%d) at screen row: %d\n", mode, screen.row));
|
|
REFRESH_ZERO_SCROLLBACK;
|
|
RESET_CHSTAT;
|
|
row_offset = TermWin.saveLines;
|
|
|
|
|
|
switch (mode) {
|
|
case 0: /* erase to end of screen */
|
|
scr_erase_line(0);
|
|
row = screen.row + 1; /* possible OOB */
|
|
num = TermWin.nrow - row;
|
|
break;
|
|
case 1: /* erase to beginning of screen */
|
|
scr_erase_line(1);
|
|
row = 0; /* possible OOB */
|
|
num = screen.row;
|
|
break;
|
|
case 2: /* erase whole screen */
|
|
row = 0;
|
|
num = TermWin.nrow;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
if (row >= 0 && row <= TermWin.nrow) { /* check OOB */
|
|
UPPER_BOUND(num, (TermWin.nrow - row));
|
|
if (rstyle & RS_RVid || rstyle & RS_Uline)
|
|
ren = -1;
|
|
else {
|
|
if (GET_BGCOLOR(rstyle) == bgColor) {
|
|
ren = DEFAULT_RSTYLE;
|
|
CLEAR_ROWS(row, num);
|
|
} else {
|
|
ren = (rstyle & (RS_fgMask | RS_bgMask));
|
|
gcvalue.foreground = PixColors[GET_BGCOLOR(ren)];
|
|
gcmask = GCForeground;
|
|
XChangeGC(Xdisplay, TermWin.gc, gcmask, &gcvalue);
|
|
ERASE_ROWS(row, num);
|
|
gcvalue.foreground = PixColors[fgColor];
|
|
XChangeGC(Xdisplay, TermWin.gc, gcmask, &gcvalue);
|
|
}
|
|
}
|
|
for (; num--; row++) {
|
|
blank_screen_mem(screen.text, screen.rend, row + row_offset, rstyle & ~(RS_RVid | RS_Uline));
|
|
blank_screen_mem(drawn_text, drawn_rend, row, ren);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Fill the screen with `E's
|
|
* XTERM_SEQ: Screen Alignment Test: ESC # 8
|
|
*/
|
|
void
|
|
scr_E(void)
|
|
{
|
|
int i, j;
|
|
text_t *t;
|
|
rend_t *r, fs;
|
|
|
|
ZERO_SCROLLBACK;
|
|
RESET_CHSTAT;
|
|
|
|
fs = rstyle;
|
|
for (i = TermWin.saveLines; i < TermWin.nrow + TermWin.saveLines; i++) {
|
|
t = screen.text[i];
|
|
r = screen.rend[i];
|
|
for (j = 0; j < TermWin.ncol; j++) {
|
|
*t++ = 'E';
|
|
*r++ = fs;
|
|
}
|
|
*t = '\0';
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Insert/Delete <count> lines
|
|
*/
|
|
void
|
|
scr_insdel_lines(int count, int insdel)
|
|
{
|
|
int end;
|
|
|
|
ZERO_SCROLLBACK;
|
|
RESET_CHSTAT;
|
|
|
|
if (screen.row > screen.bscroll)
|
|
return;
|
|
|
|
end = screen.bscroll - screen.row + 1;
|
|
if (count > end) {
|
|
if (insdel == DELETE)
|
|
return;
|
|
else if (insdel == INSERT)
|
|
count = end;
|
|
}
|
|
if (screen.flags & Screen_WrapNext) {
|
|
screen.flags &= ~Screen_WrapNext;
|
|
}
|
|
scroll_text(screen.row, screen.bscroll, insdel * count, 0);
|
|
|
|
/* fill the inserted or new lines with rstyle. TODO: correct for delete? */
|
|
if (insdel == DELETE) {
|
|
end = screen.bscroll + TermWin.saveLines;
|
|
} else if (insdel == INSERT) {
|
|
end = screen.row + count - 1 + TermWin.saveLines;
|
|
}
|
|
for (; count--; end--) {
|
|
blank_screen_mem(screen.text, screen.rend, end, rstyle);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Insert/Delete <count> characters from the current position
|
|
*/
|
|
void
|
|
scr_insdel_chars(int count, int insdel)
|
|
{
|
|
int col, row;
|
|
|
|
ZERO_SCROLLBACK;
|
|
RESET_CHSTAT;
|
|
|
|
if (count <= 0)
|
|
return;
|
|
|
|
CHECK_SELECTION;
|
|
UPPER_BOUND(count, (TermWin.ncol - screen.col));
|
|
|
|
row = screen.row + TermWin.saveLines;
|
|
screen.flags &= ~Screen_WrapNext;
|
|
|
|
switch (insdel) {
|
|
case INSERT:
|
|
for (col = TermWin.ncol - 1; (col - count) >= screen.col; col--) {
|
|
screen.text[row][col] = screen.text[row][col - count];
|
|
screen.rend[row][col] = screen.rend[row][col - count];
|
|
}
|
|
screen.text[row][TermWin.ncol] += count;
|
|
UPPER_BOUND(screen.text[row][TermWin.ncol], TermWin.ncol);
|
|
/* FALLTHROUGH */
|
|
case ERASE:
|
|
blank_line(&(screen.text[row][screen.col]),
|
|
&(screen.rend[row][screen.col]),
|
|
count, rstyle);
|
|
break;
|
|
case DELETE:
|
|
for (col = screen.col; (col + count) < TermWin.ncol; col++) {
|
|
screen.text[row][col] = screen.text[row][col + count];
|
|
screen.rend[row][col] = screen.rend[row][col + count];
|
|
}
|
|
blank_line(&(screen.text[row][TermWin.ncol - count]),
|
|
&(screen.rend[row][TermWin.ncol - count]),
|
|
count, rstyle);
|
|
screen.text[row][TermWin.ncol] -= count;
|
|
if (((int) (char) screen.text[row][TermWin.ncol]) < 0)
|
|
screen.text[row][TermWin.ncol] = 0;
|
|
break;
|
|
}
|
|
#ifdef MULTI_CHARSET
|
|
if ((screen.rend[row][0] & RS_multiMask) == RS_multi2) {
|
|
screen.rend[row][0] &= ~RS_multiMask;
|
|
screen.text[row][0] = ' ';
|
|
}
|
|
if ((screen.rend[row][TermWin.ncol - 1] & RS_multiMask) == RS_multi1) {
|
|
screen.rend[row][TermWin.ncol - 1] &= ~RS_multiMask;
|
|
screen.text[row][TermWin.ncol - 1] = ' ';
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Set the scrolling region
|
|
* XTERM_SEQ: Set region <top> - <bot> inclusive: ESC [ <top> ; <bot> r
|
|
*/
|
|
void
|
|
scr_scroll_region(int top, int bot)
|
|
{
|
|
LOWER_BOUND(top, 0);
|
|
UPPER_BOUND(bot, TermWin.nrow - 1);
|
|
if (top > bot)
|
|
return;
|
|
screen.tscroll = top;
|
|
screen.bscroll = bot;
|
|
scr_gotorc(0, 0, 0);
|
|
}
|
|
|
|
/*
|
|
* Make the cursor visible/invisible
|
|
* XTERM_SEQ: Make cursor visible : ESC [ ? 25 h
|
|
* XTERM_SEQ: Make cursor invisible: ESC [ ? 25 l
|
|
*/
|
|
void
|
|
scr_cursor_visible(int mode)
|
|
{
|
|
if (mode)
|
|
screen.flags |= Screen_VisibleCursor;
|
|
else
|
|
screen.flags &= ~Screen_VisibleCursor;
|
|
}
|
|
|
|
/*
|
|
* Set/unset automatic wrapping
|
|
* XTERM_SEQ: Set Wraparound : ESC [ ? 7 h
|
|
* XTERM_SEQ: Unset Wraparound: ESC [ ? 7 l
|
|
*/
|
|
void
|
|
scr_autowrap(int mode)
|
|
{
|
|
if (mode)
|
|
screen.flags |= Screen_Autowrap;
|
|
else
|
|
screen.flags &= ~Screen_Autowrap;
|
|
}
|
|
|
|
/*
|
|
* Set/unset margin origin mode
|
|
* Absolute mode: line numbers are counted relative to top margin of screen
|
|
* and the cursor can be moved outside the scrolling region.
|
|
* Relative mode: line numbers are relative to top margin of scrolling region
|
|
* and the cursor cannot be moved outside.
|
|
* XTERM_SEQ: Set Absolute: ESC [ ? 6 h
|
|
* XTERM_SEQ: Set Relative: ESC [ ? 6 l
|
|
*/
|
|
void
|
|
scr_relative_origin(int mode)
|
|
{
|
|
if (mode)
|
|
screen.flags |= Screen_Relative;
|
|
else
|
|
screen.flags &= ~Screen_Relative;
|
|
scr_gotorc(0, 0, 0);
|
|
}
|
|
|
|
/*
|
|
* Set insert/replace mode
|
|
* XTERM_SEQ: Set Insert mode : ESC [ ? 4 h
|
|
* XTERM_SEQ: Set Replace mode: ESC [ ? 4 l
|
|
*/
|
|
void
|
|
scr_insert_mode(int mode)
|
|
{
|
|
if (mode)
|
|
screen.flags |= Screen_Insert;
|
|
else
|
|
screen.flags &= ~Screen_Insert;
|
|
}
|
|
|
|
/*
|
|
* Set/Unset tabs
|
|
* XTERM_SEQ: Set tab at current column : ESC H
|
|
* XTERM_SEQ: Clear tab at current column: ESC [ 0 g
|
|
* XTERM_SEQ: Clear all tabs : ESC [ 3 g
|
|
*/
|
|
void
|
|
scr_set_tab(int mode)
|
|
{
|
|
if (mode < 0)
|
|
MEMSET(tabs, 0, (unsigned int) TermWin.ncol);
|
|
|
|
else if (screen.col < TermWin.ncol)
|
|
tabs[screen.col] = (mode ? 1 : 0);
|
|
}
|
|
|
|
/*
|
|
* Set reverse/normal video
|
|
* XTERM_SEQ: Reverse video: ESC [ ? 5 h
|
|
* XTERM_SEQ: Normal video : ESC [ ? 5 l
|
|
*/
|
|
void
|
|
scr_rvideo_mode(int mode)
|
|
{
|
|
int i, j, maxlines;
|
|
|
|
if (rvideo != mode) {
|
|
rvideo = mode;
|
|
rstyle ^= RS_RVid;
|
|
|
|
maxlines = TermWin.saveLines + TermWin.nrow;
|
|
for (i = TermWin.saveLines; i < maxlines; i++)
|
|
for (j = 0; j < TermWin.ncol + 1; j++)
|
|
screen.rend[i][j] ^= RS_RVid;
|
|
scr_refresh(SLOW_REFRESH);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Report current cursor position
|
|
* XTERM_SEQ: Report position: ESC [ 6 n
|
|
*/
|
|
void
|
|
scr_report_position(void)
|
|
{
|
|
tt_printf((unsigned char *) "\033[%d;%dR", screen.row + 1, screen.col + 1);
|
|
}
|
|
|
|
/* Set font style */
|
|
void
|
|
set_font_style(void)
|
|
{
|
|
rstyle &= ~RS_fontMask;
|
|
switch (charsets[screen.charset]) {
|
|
case '0': /* DEC Special Character & Line Drawing Set */
|
|
rstyle |= RS_acsFont;
|
|
break;
|
|
case 'A': /* United Kingdom (UK) */
|
|
rstyle |= RS_ukFont;
|
|
break;
|
|
case 'B': /* United States (USASCII) */
|
|
break;
|
|
case '<': /* Multinational character set */
|
|
break;
|
|
case '5': /* Finnish character set */
|
|
break;
|
|
case 'C': /* Finnish character set */
|
|
break;
|
|
case 'K': /* German character set */
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Choose a font
|
|
* XTERM_SEQ: Invoke G0 character set: CTRL-O
|
|
* XTERM_SEQ: Invoke G1 character set: CTRL-N
|
|
* XTERM_SEQ: Invoke G2 character set: ESC N
|
|
* XTERM_SEQ: Invoke G3 character set: ESC O
|
|
*/
|
|
void
|
|
scr_charset_choose(int set)
|
|
{
|
|
screen.charset = set;
|
|
set_font_style();
|
|
}
|
|
|
|
/*
|
|
* Set a font
|
|
* XTERM_SEQ: Set G0 character set: ESC ( <C>
|
|
* XTERM_SEQ: Set G1 character set: ESC ) <C>
|
|
* XTERM_SEQ: Set G2 character set: ESC * <C>
|
|
* XTERM_SEQ: Set G3 character set: ESC + <C>
|
|
* See set_font_style for possible values for <C>
|
|
*/
|
|
void
|
|
scr_charset_set(int set, unsigned int ch)
|
|
{
|
|
#ifdef MULTI_CHARSET
|
|
multi_byte = (set < 0);
|
|
set = abs(set);
|
|
#endif
|
|
charsets[set] = (unsigned char) ch;
|
|
set_font_style();
|
|
}
|
|
|
|
#ifdef MULTI_CHARSET
|
|
|
|
static void latin1(unsigned char *str, int len);
|
|
static void eucj2jis(unsigned char *str, int len);
|
|
static void sjis2jis(unsigned char *str, int len);
|
|
static void big5dummy(unsigned char *str, int len);
|
|
|
|
static void (*multichar_decode) (unsigned char *str, int len) = latin1;
|
|
|
|
static void
|
|
latin1(unsigned char *str, int len)
|
|
{
|
|
return;
|
|
str = NULL;
|
|
len = 0;
|
|
}
|
|
|
|
static void
|
|
eucj2jis(unsigned char *str, int len)
|
|
{
|
|
register int i;
|
|
|
|
for (i = 0; i < len; i++)
|
|
str[i] &= 0x7F;
|
|
}
|
|
|
|
static void
|
|
sjis2jis(unsigned char *str, int len)
|
|
{
|
|
register int i;
|
|
unsigned char *high, *low;
|
|
|
|
for (i = 0; i < len; i += 2, str += 2) {
|
|
high = str;
|
|
low = str + 1;
|
|
(*high) -= (*high > 0x9F ? 0xB1 : 0x71);
|
|
*high = (*high) * 2 + 1;
|
|
if (*low > 0x9E) {
|
|
*low -= 0x7E;
|
|
(*high)++;
|
|
} else {
|
|
if (*low > 0x7E)
|
|
(*low)--;
|
|
*low -= 0x1F;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
big5dummy(unsigned char *str, int len)
|
|
{
|
|
str = NULL;
|
|
len = 0;
|
|
}
|
|
#endif
|
|
|
|
void
|
|
set_multichar_encoding(const char *str)
|
|
{
|
|
#ifdef MULTI_CHARSET
|
|
if (str && *str) {
|
|
if (!strcasecmp(str, "sjis")) {
|
|
encoding_method = SJIS;
|
|
multichar_decode = sjis2jis;
|
|
} else if (!strcasecmp(str, "eucj") || !strcasecmp(str, "euckr") || !strcasecmp(str, "gb")) {
|
|
encoding_method = EUCJ;
|
|
multichar_decode = eucj2jis;
|
|
} else if (!strcasecmp(str, "big5")) {
|
|
encoding_method = BIG5;
|
|
multichar_decode = big5dummy;
|
|
} else {
|
|
encoding_method = LATIN1;
|
|
multichar_decode = latin1;
|
|
}
|
|
}
|
|
#else
|
|
return;
|
|
str = NULL;
|
|
#endif /* MULTI_CHARSET */
|
|
}
|
|
|
|
/* Refresh an area */
|
|
void
|
|
scr_expose(int x, int y, int width, int height)
|
|
{
|
|
int i;
|
|
register short nc, nr;
|
|
row_col_t rect_beg, rect_end;
|
|
|
|
REQUIRE(drawn_text != NULL);
|
|
|
|
nc = TermWin.ncol - 1;
|
|
nr = TermWin.nrow - 1;
|
|
|
|
rect_beg.col = Pixel2Col(x);
|
|
BOUND(rect_beg.col, 0, nc);
|
|
rect_beg.row = Pixel2Row(y);
|
|
BOUND(rect_beg.row, 0, nr);
|
|
rect_end.col = Pixel2Width(x + width + TermWin.fwidth - 1);
|
|
BOUND(rect_end.col, 0, nc);
|
|
rect_end.row = Pixel2Row(y + height + TermWin.fheight - 1);
|
|
BOUND(rect_end.row, 0, nr);
|
|
|
|
D_SCREEN(("scr_expose(x:%d, y:%d, w:%d, h:%d) area (c:%d,r:%d)-(c:%d,r:%d)\n", x, y, width, height, rect_beg.col, rect_beg.row,
|
|
rect_end.col, rect_end.row));
|
|
|
|
for (i = rect_beg.row; i <= rect_end.row; i++) {
|
|
MEMSET(&(drawn_text[i][rect_beg.col]), 0, rect_end.col - rect_beg.col + 1);
|
|
}
|
|
}
|
|
|
|
/* Move the display so that the line represented by scrollbar value Y is at
|
|
the top of the screen */
|
|
int
|
|
scr_move_to(int y, int len)
|
|
{
|
|
int start;
|
|
|
|
start = TermWin.view_start;
|
|
TermWin.view_start = ((len - y) * (TermWin.nrow - 1 + TermWin.nscrolled)
|
|
/ (len)) - (TermWin.nrow - 1);
|
|
D_SCREEN(("scr_move_to(%d, %d) view_start:%d\n", y, len, TermWin.view_start));
|
|
|
|
BOUND(TermWin.view_start, 0, TermWin.nscrolled);
|
|
|
|
return (TermWin.view_start - start);
|
|
}
|
|
|
|
/* Scroll the visible region up/down by <nlines> lines */
|
|
int
|
|
scr_page(int direction, int nlines)
|
|
{
|
|
int start;
|
|
|
|
D_SCREEN(("scr_page(%s, %d) view_start:%d\n", ((direction == UP) ? "UP" : "DN"), nlines, TermWin.view_start));
|
|
|
|
start = TermWin.view_start;
|
|
BOUND(nlines, 1, TermWin.nrow);
|
|
TermWin.view_start += ((direction == UP) ? nlines : (-nlines));
|
|
BOUND(TermWin.view_start, 0, TermWin.nscrolled);
|
|
return (TermWin.view_start - start);
|
|
}
|
|
|
|
void
|
|
scr_bell(void)
|
|
{
|
|
#ifndef NO_MAPALERT
|
|
#ifdef MAPALERT_OPTION
|
|
if (Options & Opt_map_alert)
|
|
#endif
|
|
XMapWindow(Xdisplay, TermWin.parent);
|
|
#endif
|
|
if (Options & Opt_visual_bell) {
|
|
scr_rvideo_mode(!rvideo);
|
|
scr_rvideo_mode(!rvideo);
|
|
} else
|
|
XBell(Xdisplay, 0);
|
|
}
|
|
|
|
void
|
|
scr_printscreen(int fullhist)
|
|
{
|
|
#ifdef PRINTPIPE
|
|
int i, r, nrows, row_offset;
|
|
text_t *t;
|
|
FILE *fd;
|
|
|
|
if ((fd = popen_printer()) == NULL)
|
|
return;
|
|
nrows = TermWin.nrow;
|
|
if (fullhist) {
|
|
/* Print the entire scrollback buffer. Always start from the top and go all the way to the bottom. */
|
|
nrows += TermWin.nscrolled;
|
|
row_offset = TermWin.saveLines - TermWin.nscrolled;
|
|
} else {
|
|
/* Just print what's on the screen. */
|
|
row_offset = TermWin.saveLines - TermWin.view_start;
|
|
}
|
|
|
|
for (r = 0; r < nrows; r++) {
|
|
t = screen.text[r + row_offset];
|
|
for (i = TermWin.ncol - 1; i >= 0; i--)
|
|
if (!isspace(t[i]))
|
|
break;
|
|
fprintf(fd, "%.*s\n", (i + 1), t);
|
|
}
|
|
pclose_printer(fd);
|
|
#endif
|
|
}
|
|
|
|
#ifdef MULTI_CHARSET
|
|
int
|
|
scr_multi1(void)
|
|
{
|
|
rend_t rend;
|
|
|
|
rend = screen.rend[screen.row + TermWin.saveLines][screen.col];
|
|
return ((rend & RS_multiMask) == RS_multi1);
|
|
}
|
|
|
|
int
|
|
scr_multi2(void)
|
|
{
|
|
rend_t rend;
|
|
|
|
if (screen.col == 0)
|
|
return 0;
|
|
rend = screen.rend[screen.row + TermWin.saveLines][screen.col - 1];
|
|
return ((rend & RS_multiMask) == RS_multi2);
|
|
}
|
|
#endif /* MULTI_CHARSET */
|
|
|
|
/*
|
|
* Refresh the screen
|
|
* drawn_text/drawn_rend contain the screen information before the update.
|
|
* screen.text/screen.rend contain what the screen will change to.
|
|
*/
|
|
|
|
void
|
|
scr_refresh(int type)
|
|
{
|
|
int i, /* tmp */
|
|
scrrow, /* screen row offset */
|
|
row_offset, /* basic offset in screen structure */
|
|
boldlast = 0, /* last character in some row was bold */
|
|
len, wlen, /* text length screen/buffer */
|
|
fprop, /* proportional font used */
|
|
is_cursor, /* cursor this position */
|
|
rvid, /* reverse video this position */
|
|
fore, back, /* desired foreground/background */
|
|
wbyte, /* we're in multibyte */
|
|
xpixel, /* x offset for start of drawing (font) */
|
|
ypixel; /* y offset for start of drawing (font) */
|
|
register int col, row, /* column/row we're processing */
|
|
rend; /* rendition */
|
|
static int focus = -1; /* screen in focus? */
|
|
long gcmask; /* Graphics Context mask */
|
|
unsigned long ltmp;
|
|
rend_t rt1, rt2, /* tmp rend values */
|
|
lastrend; /* rend type of last char in drawing set */
|
|
text_t lasttext; /* last char being replaced in drawing set */
|
|
rend_t *drp, *srp; /* drawn-rend-pointer, screen-rend-pointer */
|
|
text_t *dtp, *stp; /* drawn-text-pointer, screen-text-pointer */
|
|
XGCValues gcvalue; /* Graphics Context values */
|
|
char buf[MAX_COLS + 1];
|
|
register char *buffer = buf;
|
|
Pixmap pmap = images[image_bg].current->pmap->pixmap;
|
|
int (*draw_string) (), (*draw_image_string) ();
|
|
register int low_x = 99999, low_y = 99999, high_x = 0, high_y = 0;
|
|
Drawable draw_buffer;
|
|
#ifndef NO_BOLDFONT
|
|
int bfont = 0; /* we've changed font to bold font */
|
|
#endif
|
|
#ifdef OPTIMIZE_HACKS
|
|
register int nrows = TermWin.nrow;
|
|
register int ncols = TermWin.ncol;
|
|
#endif
|
|
int ascent, descent;
|
|
|
|
PROF_INIT(scr_refresh);
|
|
|
|
switch (type) {
|
|
case NO_REFRESH:
|
|
D_SCREEN(("scr_refresh(NO_REFRESH) called.\n"));
|
|
break;
|
|
case SLOW_REFRESH:
|
|
D_SCREEN(("scr_refresh(SLOW_REFRESH) called.\n"));
|
|
break;
|
|
case FAST_REFRESH:
|
|
D_SCREEN(("scr_refresh(FAST_REFRESH) called.\n"));
|
|
break;
|
|
}
|
|
if (type == NO_REFRESH)
|
|
return;
|
|
|
|
if (buffer_pixmap) {
|
|
draw_buffer = buffer_pixmap;
|
|
} else {
|
|
draw_buffer = TermWin.vt;
|
|
}
|
|
|
|
row_offset = TermWin.saveLines - TermWin.view_start;
|
|
fprop = TermWin.fprop;
|
|
|
|
gcvalue.foreground = PixColors[fgColor];
|
|
gcvalue.background = PixColors[bgColor];
|
|
wbyte = 0;
|
|
|
|
XSetFont(Xdisplay, TermWin.gc, TermWin.font->fid);
|
|
|
|
draw_string = XDrawString;
|
|
draw_image_string = XDrawImageString;
|
|
|
|
BOUND(screen.row, 0, TermWin.nrow - 1);
|
|
BOUND(screen.col, 0, TermWin.ncol - 1);
|
|
|
|
row = screen.row + TermWin.saveLines;
|
|
col = screen.col;
|
|
if (screen.flags & Screen_VisibleCursor) {
|
|
screen.rend[row][col] |= RS_Cursor;
|
|
#ifdef MULTI_CHARSET
|
|
srp = &screen.rend[row][col];
|
|
if ((col < ncols - 1) && ((srp[0] & RS_multiMask) == RS_multi1)
|
|
&& ((srp[1] & RS_multiMask) == RS_multi2)) {
|
|
screen.rend[row][col + 1] |= RS_Cursor;
|
|
} else if ((col > 0) && ((srp[0] & RS_multiMask) == RS_multi2)
|
|
&& ((srp[-1] & RS_multiMask) == RS_multi1)) {
|
|
screen.rend[row][col - 1] |= RS_Cursor;
|
|
}
|
|
#endif
|
|
if (focus != TermWin.focus) {
|
|
focus = TermWin.focus;
|
|
if ((i = screen.row - TermWin.view_start) >= 0) {
|
|
drawn_rend[i][col] = RS_attrMask;
|
|
#ifdef MULTI_CHARSET
|
|
if ((col < ncols - 1) && ((srp[1] & RS_multiMask) == RS_multi2)) {
|
|
drawn_rend[i][col + 1] = RS_attrMask;
|
|
} else if ((col > 0) && ((srp[-1] & RS_multiMask) == RS_multi1)) {
|
|
drawn_rend[i][col - 1] = RS_attrMask;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
for (row = 0; row < nrows; row++) {
|
|
scrrow = row + row_offset;
|
|
stp = screen.text[scrrow];
|
|
srp = screen.rend[scrrow];
|
|
dtp = drawn_text[row];
|
|
drp = drawn_rend[row];
|
|
|
|
for (col = 0; col < ncols; col++) {
|
|
if (!refresh_all) {
|
|
/* compare new text with old - if exactly the same then continue */
|
|
rt1 = srp[col];
|
|
rt2 = drp[col];
|
|
if ((stp[col] == dtp[col]) /* must match characters to skip */
|
|
&& ((rt1 == rt2) /* either rendition the same or */
|
|
|| ((stp[col] == ' ') /* space w/ no bg change */
|
|
&& (GET_BGATTR(rt1) == GET_BGATTR(rt2))))) {
|
|
#ifdef MULTI_CHARSET
|
|
/* if first byte is multibyte then compare second bytes */
|
|
if ((rt1 & RS_multiMask) != RS_multi1)
|
|
continue;
|
|
else if (stp[col + 1] == dtp[col + 1]) {
|
|
/* assume no corrupt characters on the screen */
|
|
col++;
|
|
continue;
|
|
}
|
|
#else
|
|
continue;
|
|
#endif
|
|
}
|
|
}
|
|
lasttext = dtp[col];
|
|
lastrend = drp[col];
|
|
/* redraw one or more characters */
|
|
dtp[col] = stp[col];
|
|
rend = drp[col] = srp[col];
|
|
|
|
len = 0;
|
|
buffer[len++] = stp[col];
|
|
xpixel = Col2Pixel(col);
|
|
wlen = 1;
|
|
|
|
/*
|
|
* Find out the longest string we can write out at once
|
|
*/
|
|
if (fprop == 0) { /* Fixed width font */
|
|
#ifdef MULTI_CHARSET
|
|
if (((rend & RS_multiMask) == RS_multi1) && (col < ncols - 1)
|
|
&& ((srp[col + 1]) & RS_multiMask) == RS_multi2) {
|
|
if (!wbyte) {
|
|
wbyte = 1;
|
|
XSetFont(Xdisplay, TermWin.gc, TermWin.mfont->fid);
|
|
draw_string = XDrawString16;
|
|
draw_image_string = XDrawImageString16;
|
|
}
|
|
/* double stepping - we're in Multibyte mode */
|
|
for (; ++col < ncols;) {
|
|
/* XXX: could check sanity on 2nd byte */
|
|
dtp[col] = stp[col];
|
|
drp[col] = srp[col];
|
|
buffer[len++] = stp[col];
|
|
col++;
|
|
if ((col == ncols) || (srp[col] != (unsigned int) rend))
|
|
break;
|
|
if ((stp[col] == dtp[col])
|
|
&& (srp[col] == drp[col])
|
|
&& (stp[col + 1] == dtp[col + 1]))
|
|
break;
|
|
if (len == MAX_COLS)
|
|
break;
|
|
dtp[col] = stp[col];
|
|
drp[col] = srp[col];
|
|
buffer[len++] = stp[col];
|
|
}
|
|
col--;
|
|
if (buffer[0] & 0x80)
|
|
multichar_decode(buffer, len);
|
|
wlen = len / 2;
|
|
} else {
|
|
if ((rend & RS_multiMask) == RS_multi1) {
|
|
/* XXX : maybe do the same thing for RS_multi2 */
|
|
/* corrupt character - you're outta there */
|
|
rend &= ~RS_multiMask;
|
|
drp[col] = rend; /* TODO check: may also want */
|
|
dtp[col] = ' '; /* to poke into stp/srp */
|
|
buffer[0] = ' ';
|
|
}
|
|
if (wbyte) {
|
|
wbyte = 0;
|
|
XSetFont(Xdisplay, TermWin.gc, TermWin.font->fid);
|
|
draw_string = XDrawString;
|
|
draw_image_string = XDrawImageString;
|
|
}
|
|
#endif
|
|
/* single stepping - `normal' mode */
|
|
for (; ++col < ncols - 1;) {
|
|
if ((unsigned int) rend != srp[col])
|
|
break;
|
|
if ((stp[col] == dtp[col]) && (srp[col] == drp[col]))
|
|
break;
|
|
if (len == MAX_COLS)
|
|
break;
|
|
lasttext = dtp[col];
|
|
lastrend = drp[col];
|
|
dtp[col] = stp[col];
|
|
drp[col] = srp[col];
|
|
buffer[len++] = stp[col];
|
|
}
|
|
col--;
|
|
wlen = len;
|
|
#ifdef MULTI_CHARSET
|
|
}
|
|
#endif
|
|
}
|
|
buffer[len] = '\0';
|
|
|
|
/* Determine the attributes for the string */
|
|
fore = GET_FGCOLOR(rend);
|
|
back = GET_BGCOLOR(rend);
|
|
rend = GET_ATTR(rend);
|
|
gcmask = 0;
|
|
rvid = (rend & RS_RVid) ? 1 : 0;
|
|
if (rend & RS_Select)
|
|
rvid = !rvid;
|
|
if (rend & RS_Cursor) {
|
|
if (focus) {
|
|
is_cursor = 2; /* normal cursor */
|
|
rvid = !rvid;
|
|
} else {
|
|
is_cursor = 1; /* outline cursor */
|
|
rend &= ~RS_Cursor;
|
|
}
|
|
srp[col] &= ~RS_Cursor;
|
|
} else
|
|
is_cursor = 0;
|
|
switch (rend & RS_fontMask) {
|
|
case RS_acsFont:
|
|
for (i = 0; i < len; i++)
|
|
if (buffer[i] == 0x5f)
|
|
buffer[i] = 0x7f;
|
|
else if (buffer[i] > 0x5f && buffer[i] < 0x7f)
|
|
buffer[i] -= 0x5f;
|
|
break;
|
|
case RS_ukFont:
|
|
for (i = 0; i < len; i++)
|
|
if (buffer[i] == '#')
|
|
buffer[i] = 0x1e;
|
|
break;
|
|
}
|
|
if (rvid)
|
|
SWAP_IT(fore, back, i);
|
|
if (back != bgColor) {
|
|
gcvalue.background = PixColors[back];
|
|
gcmask |= GCBackground;
|
|
}
|
|
if (fore != fgColor) {
|
|
gcvalue.foreground = PixColors[fore];
|
|
gcmask |= GCForeground;
|
|
}
|
|
#ifndef NO_BOLDUNDERLINE
|
|
else if (rend & RS_Bold) {
|
|
if (PixColors[fore] != PixColors[colorBD]
|
|
&& PixColors[back] != PixColors[colorBD]) {
|
|
gcvalue.foreground = PixColors[colorBD];
|
|
gcmask |= GCForeground;
|
|
}
|
|
} else if (rend & RS_Uline) {
|
|
if (PixColors[fore] != PixColors[colorUL]
|
|
&& PixColors[back] != PixColors[colorUL]) {
|
|
gcvalue.foreground = PixColors[colorUL];
|
|
gcmask |= GCForeground;
|
|
}
|
|
}
|
|
#endif
|
|
#ifndef NO_CURSORCOLOR
|
|
if (rend & RS_Cursor) {
|
|
if (PixColors[cursorColor] != PixColors[bgColor]) {
|
|
gcvalue.background = PixColors[cursorColor];
|
|
back = cursorColor;
|
|
gcmask |= GCBackground;
|
|
}
|
|
if (PixColors[cursorColor2] != PixColors[fgColor]) {
|
|
gcvalue.foreground = PixColors[cursorColor2];
|
|
gcmask |= GCForeground;
|
|
}
|
|
}
|
|
#endif
|
|
if (gcmask) {
|
|
XChangeGC(Xdisplay, TermWin.gc, gcmask, &gcvalue);
|
|
}
|
|
#ifndef NO_BOLDFONT
|
|
if (!wbyte && MONO_BOLD(rend) && TermWin.boldFont != NULL) {
|
|
XSetFont(Xdisplay, TermWin.gc, TermWin.boldFont->fid);
|
|
bfont = 1;
|
|
} else if (bfont) {
|
|
bfont = 0;
|
|
XSetFont(Xdisplay, TermWin.gc, TermWin.font->fid);
|
|
}
|
|
#endif
|
|
|
|
#ifdef MULTI_CHARSET
|
|
ascent = MAX((encoding_method == LATIN1 ? 0 : TermWin.mfont->ascent), TermWin.font->ascent);
|
|
descent = MAX((encoding_method == LATIN1 ? 0 : TermWin.mfont->descent), TermWin.font->descent);
|
|
#else
|
|
ascent = TermWin.font->ascent;
|
|
descent = TermWin.font->descent;
|
|
#endif
|
|
ypixel = ascent + Row2Pixel(row);
|
|
|
|
|
|
/* The actual drawing of the string is done here. */
|
|
if (fprop) {
|
|
if (back != bgColor) {
|
|
SWAP_IT(gcvalue.foreground, gcvalue.background, ltmp);
|
|
gcmask |= (GCForeground | GCBackground);
|
|
XChangeGC(Xdisplay, TermWin.gc, gcmask, &gcvalue);
|
|
XFillRectangle(Xdisplay, draw_buffer, TermWin.gc, xpixel, ypixel - ascent, Width2Pixel(1), Height2Pixel(1));
|
|
SWAP_IT(gcvalue.foreground, gcvalue.background, ltmp);
|
|
XChangeGC(Xdisplay, TermWin.gc, gcmask, &gcvalue);
|
|
} else {
|
|
CLEAR_CHARS(xpixel, ypixel - ascent, 1);
|
|
}
|
|
if (TermWin.font->per_char) {
|
|
int fw, cw;
|
|
|
|
fw = TermWin.fwidth;
|
|
cw = TermWin.font->per_char[((int) (*buffer))].width;
|
|
if (cw > 0 && cw < TermWin.font->max_bounds.width) {
|
|
if (fw > cw) {
|
|
xpixel += ((fw - cw) >> 1);
|
|
} else {
|
|
xpixel -= ((cw - fw) >> 1);
|
|
if (col < ncols - 1) {
|
|
dtp[col + 1] = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
DRAW_STRING(draw_string, xpixel, ypixel, buffer, 1);
|
|
UPDATE_BOX(xpixel, ypixel - ascent, xpixel + Width2Pixel(1), ypixel + Height2Pixel(1));
|
|
#ifndef NO_BOLDOVERSTRIKE
|
|
if (MONO_BOLD(rend)) {
|
|
DRAW_STRING(draw_string, xpixel + 1, ypixel, buffer, 1);
|
|
UPDATE_BOX(xpixel + 1, ypixel - ascent, xpixel + 1 + Width2Pixel(1), ypixel + Height2Pixel(1));
|
|
}
|
|
#endif
|
|
} else {
|
|
#ifdef PIXMAP_SUPPORT
|
|
if (background_is_pixmap() && (back == bgColor)) {
|
|
if (fshadow.do_shadow) {
|
|
Pixel tmp;
|
|
int xx, yy, ww, hh;
|
|
|
|
tmp = gcvalue.foreground;
|
|
xx = xpixel;
|
|
yy = ypixel - ascent;
|
|
ww = Width2Pixel(wlen);
|
|
hh = Height2Pixel(1);
|
|
CLEAR_CHARS(xpixel, ypixel - ascent, len);
|
|
if (fshadow.shadow[SHADOW_TOP_LEFT] || fshadow.shadow[SHADOW_TOP_RIGHT]) {
|
|
yy--;
|
|
hh++;
|
|
}
|
|
if (fshadow.shadow[SHADOW_BOTTOM_LEFT] || fshadow.shadow[SHADOW_BOTTOM_RIGHT]) {
|
|
hh++;
|
|
if (row < nrows - 1) {
|
|
int ii;
|
|
|
|
for (ii = col - len + 1; ii <= col; ii++) {
|
|
drawn_text[row + 1][ii] = 0;
|
|
}
|
|
}
|
|
}
|
|
if (fshadow.shadow[SHADOW_TOP_LEFT]) {
|
|
XSetForeground(Xdisplay, TermWin.gc, fshadow.color[SHADOW_TOP_LEFT]);
|
|
DRAW_STRING(draw_string, xpixel - 1, ypixel - 1, buffer, wlen);
|
|
if (col) {
|
|
dtp[col - 1] = 0;
|
|
}
|
|
}
|
|
if (fshadow.shadow[SHADOW_TOP_RIGHT]) {
|
|
XSetForeground(Xdisplay, TermWin.gc, fshadow.color[SHADOW_TOP_RIGHT]);
|
|
DRAW_STRING(draw_string, xpixel + 1, ypixel - 1, buffer, wlen);
|
|
if (col < ncols - 1) {
|
|
dtp[col + 1] = 0;
|
|
}
|
|
}
|
|
if (fshadow.shadow[SHADOW_BOTTOM_LEFT]) {
|
|
XSetForeground(Xdisplay, TermWin.gc, fshadow.color[SHADOW_BOTTOM_LEFT]);
|
|
DRAW_STRING(draw_string, xpixel - 1, ypixel + 1, buffer, wlen);
|
|
if (col) {
|
|
dtp[col - 1] = 0;
|
|
}
|
|
}
|
|
if (fshadow.shadow[SHADOW_BOTTOM_RIGHT]) {
|
|
XSetForeground(Xdisplay, TermWin.gc, fshadow.color[SHADOW_BOTTOM_RIGHT]);
|
|
DRAW_STRING(draw_string, xpixel + 1, ypixel + 1, buffer, wlen);
|
|
if (col < ncols - 1) {
|
|
dtp[col + 1] = 0;
|
|
}
|
|
}
|
|
XSetForeground(Xdisplay, TermWin.gc, tmp);
|
|
DRAW_STRING(draw_string, xpixel, ypixel, buffer, wlen);
|
|
UPDATE_BOX(xx, yy, xx + ww, yy + hh);
|
|
} else {
|
|
CLEAR_CHARS(xpixel, ypixel - ascent, len);
|
|
DRAW_STRING(draw_string, xpixel, ypixel, buffer, wlen);
|
|
UPDATE_BOX(xpixel, ypixel - ascent, xpixel + Width2Pixel(wlen), ypixel + Height2Pixel(1));
|
|
}
|
|
} else
|
|
#endif
|
|
{
|
|
#ifdef FORCE_CLEAR_CHARS
|
|
CLEAR_CHARS(xpixel, ypixel - ascent, len);
|
|
#endif
|
|
DRAW_STRING(draw_image_string, xpixel, ypixel, buffer, wlen);
|
|
#ifdef MULTI_CHARSET
|
|
{
|
|
XFontStruct *font = wbyte? TermWin.mfont: TermWin.font;
|
|
|
|
if (font->ascent < ascent || font->descent < descent) {
|
|
SWAP_IT(gcvalue.foreground, gcvalue.background, ltmp);
|
|
gcmask |= (GCForeground | GCBackground);
|
|
XChangeGC(Xdisplay, TermWin.gc, gcmask, &gcvalue);
|
|
if (font->ascent < ascent) {
|
|
XFillRectangle(Xdisplay, draw_buffer, TermWin.gc, xpixel,
|
|
Row2Pixel(row),
|
|
Width2Pixel(len),
|
|
ascent - font->ascent);
|
|
}
|
|
if (font->descent < descent) {
|
|
XFillRectangle(Xdisplay, draw_buffer, TermWin.gc, xpixel,
|
|
Row2Pixel(row) + ascent + font->descent,
|
|
Width2Pixel(len),
|
|
descent - font->descent);
|
|
}
|
|
SWAP_IT(gcvalue.foreground, gcvalue.background, ltmp);
|
|
XChangeGC(Xdisplay, TermWin.gc, gcmask, &gcvalue);
|
|
}
|
|
}
|
|
#endif
|
|
UPDATE_BOX(xpixel, ypixel - ascent, xpixel + Width2Pixel(wlen), ypixel + Height2Pixel(1));
|
|
}
|
|
}
|
|
|
|
/* do the convoluted bold overstrike */
|
|
#ifndef NO_BOLDOVERSTRIKE
|
|
if (MONO_BOLD(rend)) {
|
|
DRAW_STRING(draw_string, xpixel + 1, ypixel, buffer, wlen);
|
|
UPDATE_BOX(xpixel + 1, ypixel - ascent, xpixel + 1 + Width2Pixel(wlen), ypixel + Height2Pixel(1));
|
|
}
|
|
#endif
|
|
|
|
if ((rend & RS_Uline) && (descent > 1)) {
|
|
XDrawLine(Xdisplay, draw_buffer, TermWin.gc, xpixel, ypixel + 1, xpixel + Width2Pixel(wlen) - 1, ypixel + 1);
|
|
UPDATE_BOX(xpixel, ypixel + 1, xpixel + Width2Pixel(wlen) - 1, ypixel + 1);
|
|
}
|
|
if (is_cursor == 1) {
|
|
#ifndef NO_CURSORCOLOR
|
|
if (PixColors[cursorColor] != PixColors[bgColor]) {
|
|
XSetForeground(Xdisplay, TermWin.gc, PixColors[cursorColor]);
|
|
}
|
|
#endif
|
|
XDrawRectangle(Xdisplay, draw_buffer, TermWin.gc, xpixel, ypixel - ascent, Width2Pixel(1 + wbyte) - 1, Height2Pixel(1) - 1);
|
|
UPDATE_BOX(xpixel, ypixel - ascent, Width2Pixel(1 + wbyte) - 1, Height2Pixel(1) - 1);
|
|
XSetForeground(Xdisplay, TermWin.gc, PixColors[fgColor]);
|
|
}
|
|
if (gcmask) { /* restore normal colors */
|
|
gcvalue.foreground = PixColors[fgColor];
|
|
gcvalue.background = PixColors[bgColor];
|
|
XChangeGC(Xdisplay, TermWin.gc, gcmask, &gcvalue);
|
|
}
|
|
if (MONO_BOLD(lastrend)) {
|
|
if (col < ncols - 1) {
|
|
dtp[col + 1] = 0;
|
|
} else {
|
|
boldlast = 1;
|
|
}
|
|
}
|
|
} /* for (col = 0; col < TermWin.ncol; col++) */
|
|
} /* for (row = 0; row < TermWin.nrow; row++) */
|
|
|
|
row = screen.row + TermWin.saveLines;
|
|
col = screen.col;
|
|
if (screen.flags & Screen_VisibleCursor) {
|
|
screen.rend[row][col] &= ~RS_Cursor;
|
|
#ifdef MULTI_CHARSET
|
|
/* very low overhead so don't check properly, just wipe it all out */
|
|
if (screen.col < ncols - 1)
|
|
screen.rend[row][col + 1] &= ~RS_Cursor;
|
|
if (screen.col > 0)
|
|
screen.rend[row][col - 1] &= ~RS_Cursor;
|
|
#endif
|
|
}
|
|
if (buffer_pixmap) {
|
|
D_SCREEN(("Update box dimensions: from (%d, %d) to (%d, %d). Dimensions %dx%d\n", low_x, low_y, high_x, high_y,
|
|
high_x - low_x + 1, high_y - low_y + 1));
|
|
XClearArea(Xdisplay, TermWin.vt, low_x, low_y, high_x - low_x + 1, high_y - low_y + 1, False);
|
|
if (fshadow.shadow[SHADOW_TOP_LEFT] || fshadow.shadow[SHADOW_BOTTOM_LEFT]) {
|
|
XCopyArea(Xdisplay, pmap, buffer_pixmap, TermWin.gc, TermWin.internalBorder - 1, 0,
|
|
1, TermWin_TotalHeight() - 1, TermWin.internalBorder - 1, 0);
|
|
XClearArea(Xdisplay, TermWin.vt, TermWin.internalBorder - 1, 0, 1, TermWin_TotalHeight() - 1, False);
|
|
}
|
|
if (fshadow.shadow[SHADOW_TOP_RIGHT] || fshadow.shadow[SHADOW_BOTTOM_RIGHT] || boldlast) {
|
|
XCopyArea(Xdisplay, pmap, buffer_pixmap, TermWin.gc, TermWin_TotalWidth() - 2, 0,
|
|
1, TermWin_TotalHeight() - 1, TermWin_TotalWidth() - 2, 0);
|
|
XClearArea(Xdisplay, TermWin.vt, TermWin_TotalWidth() - 2, 0, 1, TermWin_TotalHeight() - 1, False);
|
|
}
|
|
if (fshadow.shadow[SHADOW_TOP_LEFT] || fshadow.shadow[SHADOW_TOP_RIGHT]) {
|
|
XCopyArea(Xdisplay, pmap, buffer_pixmap, TermWin.gc, 0, TermWin.internalBorder - 1,
|
|
TermWin_TotalWidth() - 1, 1, 0, TermWin.internalBorder - 1);
|
|
XClearArea(Xdisplay, TermWin.vt, 0, TermWin.internalBorder - 1, TermWin_TotalWidth() - 1, 1, False);
|
|
}
|
|
if (fshadow.shadow[SHADOW_BOTTOM_LEFT] || fshadow.shadow[SHADOW_BOTTOM_RIGHT]) {
|
|
XCopyArea(Xdisplay, pmap, buffer_pixmap, TermWin.gc, 0, TermWin_TotalHeight() - TermWin.internalBorder,
|
|
TermWin_TotalWidth() - 1, 1, 0, TermWin_TotalHeight() - TermWin.internalBorder);
|
|
XClearArea(Xdisplay, TermWin.vt, 0, TermWin_TotalHeight() - TermWin.internalBorder, TermWin_TotalWidth() - 1, 1, False);
|
|
}
|
|
} else {
|
|
if (fshadow.shadow[SHADOW_TOP_LEFT] || fshadow.shadow[SHADOW_BOTTOM_LEFT]) {
|
|
XClearArea(Xdisplay, TermWin.vt, TermWin.internalBorder - 1, 0, 1, TermWin_TotalHeight() - 1, False);
|
|
}
|
|
if ((fshadow.shadow[SHADOW_TOP_RIGHT] || fshadow.shadow[SHADOW_BOTTOM_RIGHT] || boldlast) && TermWin.internalBorder) {
|
|
XClearArea(Xdisplay, TermWin.vt, TermWin_TotalWidth() - 2, 0, 1, TermWin_TotalHeight() - 1, False);
|
|
}
|
|
if (fshadow.shadow[SHADOW_TOP_LEFT] || fshadow.shadow[SHADOW_TOP_RIGHT]) {
|
|
XClearArea(Xdisplay, TermWin.vt, 0, TermWin.internalBorder - 1, TermWin_TotalWidth() - 1, 1, False);
|
|
}
|
|
if (fshadow.shadow[SHADOW_BOTTOM_LEFT] || fshadow.shadow[SHADOW_BOTTOM_RIGHT]) {
|
|
XClearArea(Xdisplay, TermWin.vt, 0, TermWin_TotalHeight() - TermWin.internalBorder, TermWin_TotalWidth() - 1, 1, False);
|
|
}
|
|
}
|
|
if (type == SLOW_REFRESH) {
|
|
XSync(Xdisplay, False);
|
|
}
|
|
refresh_all = 0;
|
|
D_SCREEN(("Exiting.\n"));
|
|
|
|
PROF_DONE(scr_refresh);
|
|
PROF_TIME(scr_refresh);
|
|
}
|
|
|
|
int
|
|
scr_strmatch(unsigned long row, unsigned long col, const char *str)
|
|
{
|
|
unsigned char c;
|
|
const char *s;
|
|
|
|
for (c = screen.text[row][col], s = str; s; s++) {
|
|
if (c != *s) {
|
|
return (0);
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/* Find and highlight all occurances of "str" in the scrollback. */
|
|
void
|
|
scr_search_scrollback(char *str)
|
|
{
|
|
unsigned char *c;
|
|
char *s;
|
|
static char *last_str = NULL;
|
|
unsigned int *i;
|
|
unsigned long row, lrow, col, rows, cols, len, k;
|
|
|
|
if (str == NULL) {
|
|
if ((str = last_str) == NULL) {
|
|
return;
|
|
}
|
|
} else {
|
|
last_str = STRDUP(str);
|
|
}
|
|
lrow = rows = TermWin.nrow + TermWin.saveLines;
|
|
cols = TermWin.ncol;
|
|
len = strlen(str);
|
|
|
|
D_SCREEN(("%d, %d\n", rows, cols));
|
|
for (row = 0; row < rows; row++) {
|
|
if (screen.text[row]) {
|
|
c = screen.text[row];
|
|
for (s = strstr(c, str); s; s = strstr(s + 1, str)) {
|
|
unsigned long j;
|
|
|
|
col = (int) s - (int) c;
|
|
for (i = screen.rend[row] + col, j = 0; j < len; i++, j++) {
|
|
if (*i & RS_RVid) {
|
|
*i &= ~RS_RVid;
|
|
} else {
|
|
*i |= RS_RVid;
|
|
}
|
|
}
|
|
if ((long) row <= TermWin.saveLines) {
|
|
lrow = row;
|
|
}
|
|
}
|
|
for (s = screen.text[row] + cols - len + 1, k = len - 1; k; s++, k--) {
|
|
unsigned long j;
|
|
|
|
if ((row < rows - 1) && !strncasecmp(s, str, k) && screen.text[row + 1] && !strncasecmp(screen.text[row + 1], str + k, len - k)) {
|
|
col = (int) s - (int) c;
|
|
for (i = &(screen.rend[row][cols - k]), j = 0; j < k; i++, j++) {
|
|
(*i & RS_RVid) ? (*i &= ~RS_RVid) : (*i |= RS_RVid);
|
|
}
|
|
for (i = screen.rend[row + 1], j = 0, k = len - k; j < k; i++, j++) {
|
|
(*i & RS_RVid) ? (*i &= ~RS_RVid) : (*i |= RS_RVid);
|
|
}
|
|
if ((long) row <= TermWin.saveLines) {
|
|
lrow = row;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (last_str == str) {
|
|
FREE(last_str);
|
|
} else {
|
|
if (lrow != rows) {
|
|
TermWin.view_start = rows - lrow - TermWin.nrow;
|
|
BOUND(TermWin.view_start, 0, TermWin.nscrolled);
|
|
D_SCREEN(("New view start is %d\n", TermWin.view_start));
|
|
}
|
|
}
|
|
scr_refresh(refresh_type);
|
|
}
|
|
|
|
/* Dump the entire contents of the scrollback buffer to stderr in hex and ASCII */
|
|
void
|
|
scr_dump(void)
|
|
{
|
|
unsigned char *c;
|
|
unsigned int *i;
|
|
unsigned long row, col, rows, cols;
|
|
|
|
rows = TermWin.nrow + TermWin.saveLines;
|
|
cols = TermWin.ncol;
|
|
|
|
D_SCREEN(("%d, %d\n", rows, cols));
|
|
for (row = 0; row < rows; row++) {
|
|
fprintf(stderr, "%lu: ", row);
|
|
if (screen.text[row]) {
|
|
for (col = 0, c = screen.text[row]; col < cols; c++, col++) {
|
|
fprintf(stderr, "%02x ", *c);
|
|
}
|
|
fprintf(stderr, "\"");
|
|
for (col = 0, c = screen.text[row]; col < cols; c++, col++) {
|
|
fprintf(stderr, "%c", ((isprint(*c)) ? (*c) : '.'));
|
|
}
|
|
fprintf(stderr, "\"");
|
|
for (col = 0, i = screen.rend[row]; col < cols; i++, col++) {
|
|
fprintf(stderr, " %08x", *i);
|
|
}
|
|
} else {
|
|
fprintf(stderr, "NULL");
|
|
}
|
|
fprintf(stderr, "\n");
|
|
fflush(stderr);
|
|
}
|
|
}
|
|
|
|
/* Dump the entire contents of the scrollback buffer to a file */
|
|
void
|
|
scr_dump_to_file(const char *fname)
|
|
{
|
|
int outfd;
|
|
char *buff, *src, *dest;
|
|
unsigned long row, col, rows, cols;
|
|
|
|
REQUIRE(fname != NULL);
|
|
|
|
rows = TermWin.nrow + TermWin.saveLines;
|
|
cols = TermWin.ncol;
|
|
D_SCREEN(("Dumping to file \"%s\". %d rows, %d cols\n", fname, rows, cols));
|
|
|
|
/* Remove it if it's there. If this fails, we don't
|
|
care, because open() will do the right thing. */
|
|
unlink(fname);
|
|
|
|
/* Only open if it's a new file, and open with permissions 0600 */
|
|
outfd = open(fname, O_CREAT | O_EXCL | O_NDELAY | O_WRONLY, S_IRUSR | S_IWUSR);
|
|
if (outfd < 0) {
|
|
D_SCREEN(("Unable to open \"%s\" for writing -- %s\n", fname, strerror(errno)));
|
|
return;
|
|
}
|
|
buff = MALLOC(cols + 1);
|
|
for (row = 0; row < rows; row++) {
|
|
if (screen.text[row]) {
|
|
for (src = screen.text[row], dest = buff, col = 0; col < cols; col++) *dest++ = *src++;
|
|
*dest++ = '\n';
|
|
*dest = 0;
|
|
write(outfd, buff, dest - buff);
|
|
}
|
|
}
|
|
close(outfd);
|
|
FREE(buff);
|
|
}
|
|
|
|
/*
|
|
* If (row,col) is within a selected region of text, remove the selection
|
|
* -TermWin.nscrolled <= (selection row) <= TermWin.nrow - 1
|
|
*/
|
|
void
|
|
selection_check(void)
|
|
{
|
|
int c1, c2, r1, r2;
|
|
|
|
if (current_screen != selection.screen)
|
|
return;
|
|
|
|
if ((selection.mark.row < -TermWin.nscrolled)
|
|
|| (selection.mark.row >= TermWin.nrow)
|
|
|| (selection.beg.row < -TermWin.nscrolled)
|
|
|| (selection.beg.row >= TermWin.nrow)
|
|
|| (selection.end.row < -TermWin.nscrolled)
|
|
|| (selection.end.row >= TermWin.nrow)) {
|
|
selection_reset();
|
|
return;
|
|
}
|
|
r1 = (screen.row - TermWin.view_start);
|
|
|
|
c1 = ((r1 - selection.mark.row) * (r1 - selection.end.row));
|
|
|
|
/*
|
|
* selection.mark.row > screen.row - TermWin.view_start
|
|
* or
|
|
* selection.end.row > screen.row - TermWin.view_start
|
|
*/
|
|
if (c1 < 0)
|
|
selection_reset();
|
|
else if (c1 == 0) {
|
|
if ((selection.mark.row < selection.end.row)
|
|
|| ((selection.mark.row == selection.end.row)
|
|
&& (selection.mark.col < selection.end.col))) {
|
|
r1 = selection.mark.row;
|
|
c1 = selection.mark.col;
|
|
r2 = selection.end.row;
|
|
c2 = selection.end.col;
|
|
} else {
|
|
r1 = selection.end.row;
|
|
c1 = selection.end.col;
|
|
r2 = selection.mark.row;
|
|
c2 = selection.mark.col;
|
|
}
|
|
if ((screen.row == r1) && (screen.row == r2)) {
|
|
if ((screen.col >= c1) && (screen.col <= c2))
|
|
selection_reset();
|
|
} else if (((screen.row == r1) && (screen.col >= c1))
|
|
|| ((screen.row == r2) && (screen.col <= c2)))
|
|
selection_reset();
|
|
}
|
|
}
|
|
|
|
/* Write the selection out to the tty. */
|
|
void
|
|
selection_write(unsigned char *data, size_t len)
|
|
{
|
|
size_t num;
|
|
unsigned char *p, *cr = "\r";
|
|
|
|
D_SELECT(("Writing %lu characters of selection data to tty.\n", len));
|
|
D_SELECT(("\n%s\n\n", safe_print_string((char *) data, len)));
|
|
for (p = data, num = 0; len--; p++) {
|
|
/* Write out each line, replacing newlines with carriage returns. */
|
|
if (*p != '\n') {
|
|
num++;
|
|
} else {
|
|
tt_write(data, num);
|
|
tt_write(cr, 1);
|
|
data += num + 1;
|
|
num = 0;
|
|
}
|
|
}
|
|
/* If there's anything left, write it out too. */
|
|
if (num) {
|
|
tt_write(data, num);
|
|
}
|
|
}
|
|
|
|
/* Fetch the selection from the specified property and write it to the tty. */
|
|
void
|
|
selection_fetch(Window win, unsigned prop, int delete)
|
|
{
|
|
long nread;
|
|
unsigned long bytes_after, nitems;
|
|
unsigned char *data;
|
|
Atom actual_type;
|
|
int actual_fmt;
|
|
|
|
D_SELECT(("Fetching selection in property %d from window 0x%08x\n", (int) prop, (int) win));
|
|
if (prop == None) {
|
|
return;
|
|
}
|
|
for (nread = 0, bytes_after = 1; bytes_after > 0;) {
|
|
if ((XGetWindowProperty(Xdisplay, win, prop, (nread / 4), PROP_SIZE, delete, AnyPropertyType, &actual_type, &actual_fmt, &nitems, &bytes_after, &data) != Success)
|
|
|| (actual_type == None) || (data == NULL)) {
|
|
D_SELECT(("Unable to fetch the value of property %d from window 0x%08x\n", (int) prop, (int) win));
|
|
if (data != NULL) {
|
|
XFree(data);
|
|
}
|
|
return;
|
|
}
|
|
nread += nitems;
|
|
D_SELECT(("Got selection info: Actual type %d (format %d), %lu items at 0x%08x, %lu bytes left over.\n",
|
|
(int) actual_type, actual_fmt, nitems, data, bytes_after));
|
|
|
|
if (nitems == 0) {
|
|
D_SELECT(("Retrieval of incremental selection complete.\n"));
|
|
TermWin.mask &= ~(PropertyChangeMask);
|
|
XSelectInput(Xdisplay, TermWin.vt, TermWin.mask);
|
|
return;
|
|
}
|
|
if (actual_type == XA_STRING) {
|
|
/* We can handle strings directly. */
|
|
selection_write(data, nitems);
|
|
} else if (actual_type == props[PROP_SELECTION_INCR]) {
|
|
D_SELECT(("Incremental selection transfer initiated. Length is at least %u bytes.\n", (unsigned) *((unsigned *) data)));
|
|
TermWin.mask |= PropertyChangeMask;
|
|
XSelectInput(Xdisplay, TermWin.vt, TermWin.mask);
|
|
} else {
|
|
int size, i;
|
|
XTextProperty xtextp;
|
|
char **cl = NULL;
|
|
|
|
/* It's not a string, so convert it to one (or more). */
|
|
D_SELECT(("Selection is not a string. Converting.\n"));
|
|
xtextp.value = data;
|
|
xtextp.encoding = actual_type;
|
|
xtextp.format = actual_fmt;
|
|
xtextp.nitems = nitems;
|
|
XmbTextPropertyToTextList(Xdisplay, &xtextp, &cl, &size);
|
|
|
|
if (cl) {
|
|
D_SELECT(("Got string list 0x%08x with %d strings.\n", cl, size));
|
|
for (i = 0; i < size; i++) {
|
|
if (cl[i]) {
|
|
selection_write(cl[i], strlen(cl[i]));
|
|
}
|
|
}
|
|
XFreeStringList(cl);
|
|
}
|
|
}
|
|
if (data) {
|
|
XFree(data);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Copy a specific string of a given length to the buffer specified. */
|
|
void
|
|
selection_copy_string(Atom sel, char *str, size_t len)
|
|
{
|
|
if (str == NULL || len == 0) {
|
|
return;
|
|
}
|
|
if (IS_SELECTION(sel)) {
|
|
D_SELECT(("Copying selection to selection %d\n", (int) sel));
|
|
XSetSelectionOwner(Xdisplay, sel, TermWin.vt, CurrentTime);
|
|
if (XGetSelectionOwner(Xdisplay, sel) != TermWin.vt) {
|
|
print_error("Can't take ownership of selection\n");
|
|
}
|
|
} else {
|
|
D_SELECT(("Copying selection to cut buffer %d\n", (int) sel));
|
|
XChangeProperty(Xdisplay, Xroot, sel, XA_STRING, 8, PropModeReplace, str, len);
|
|
}
|
|
}
|
|
|
|
/* Copy the currently-selected text to the buffer specified. */
|
|
void
|
|
selection_copy(Atom sel)
|
|
{
|
|
selection_copy_string(sel, selection.text, selection.len);
|
|
}
|
|
|
|
/* Paste the specified selection from the specified buffer. */
|
|
void
|
|
selection_paste(Atom sel)
|
|
{
|
|
if (selection.text != NULL) {
|
|
/* If we have a selection of our own, paste it. */
|
|
D_SELECT(("Pasting my current selection of length %lu\n", selection.len));
|
|
selection_write(selection.text, selection.len);
|
|
} else if (IS_SELECTION(sel)) {
|
|
if (XGetSelectionOwner(Xdisplay, sel) == None) {
|
|
/* If nobody owns the current selection, just try to paste it ourselves. */
|
|
D_SELECT(("Current selection %d unowned. Attempting to paste the default cut buffer.\n", (int) sel));
|
|
selection_fetch(Xroot, XA_CUT_BUFFER0, False);
|
|
} else {
|
|
/* If someone owns the current selection, send a request to that client to
|
|
convert the selection to the appropriate form (usually XA_STRING) and
|
|
save it for us in the VT_SELECTION property. We'll then get a SelectionNotify. */
|
|
D_SELECT(("Requesting current selection (%d) -> VT_SELECTION (%d)\n", sel, props[PROP_SELECTION_DEST]));
|
|
#if defined(MULTI_CHARSET) && defined(HAVE_X11_XMU_ATOMS_H)
|
|
if (encoding_method != LATIN1) {
|
|
XConvertSelection(Xdisplay, sel, XA_COMPOUND_TEXT(Xdisplay), props[PROP_SELECTION_DEST], TermWin.vt, CurrentTime);
|
|
} else {
|
|
XConvertSelection(Xdisplay, sel, XA_STRING, props[PROP_SELECTION_DEST], TermWin.vt, CurrentTime);
|
|
}
|
|
#else
|
|
XConvertSelection(Xdisplay, sel, XA_STRING, props[PROP_SELECTION_DEST], TermWin.vt, CurrentTime);
|
|
#endif
|
|
}
|
|
} else {
|
|
D_SELECT(("Pasting cut buffer %d.\n", (int) sel));
|
|
selection_fetch(Xroot, sel, False);
|
|
}
|
|
}
|
|
|
|
/* Clear the selected state of all selected text. */
|
|
void
|
|
selection_reset(void)
|
|
{
|
|
int i, j, lrow, lcol;
|
|
|
|
D_SELECT(("selection_reset()\n"));
|
|
|
|
lrow = TermWin.nrow + TermWin.saveLines;
|
|
lcol = TermWin.ncol;
|
|
selection.op = SELECTION_CLEAR;
|
|
|
|
i = (current_screen == PRIMARY) ? 0 : TermWin.saveLines;
|
|
for (; i < lrow; i++) {
|
|
if (screen.text[i]) {
|
|
for (j = 0; j < lcol; j++) {
|
|
screen.rend[i][j] &= ~RS_Select;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* Delete the current selection. */
|
|
void
|
|
selection_clear(void)
|
|
{
|
|
D_SELECT(("selection_clear()\n"));
|
|
|
|
if (selection.text) {
|
|
FREE(selection.text);
|
|
}
|
|
selection.len = 0;
|
|
selection_reset();
|
|
}
|
|
|
|
/* Set or clear between selected points (inclusive) */
|
|
void
|
|
selection_setclr(int set, int startr, int startc, int endr, int endc)
|
|
{
|
|
int row, col, last_col;
|
|
rend_t *rend;
|
|
|
|
D_SELECT(("selection_setclr(%d) %s (%d,%d)-(%d,%d)\n", set, (set ? "set " : "clear"), startc, startr, endc, endr));
|
|
|
|
if ((startr < -TermWin.nscrolled) || (endr >= TermWin.nrow)) {
|
|
selection_reset();
|
|
return;
|
|
}
|
|
last_col = TermWin.ncol - 1;
|
|
|
|
LOWER_BOUND(startc, 0);
|
|
UPPER_BOUND(endc, last_col);
|
|
BOUND(startr, -TermWin.nscrolled, TermWin.nrow - 1);
|
|
BOUND(endr, -TermWin.nscrolled, TermWin.nrow - 1);
|
|
|
|
startr += TermWin.saveLines;
|
|
endr += TermWin.saveLines;
|
|
|
|
col = startc;
|
|
if (set) {
|
|
for (row = startr; row < endr; row++) {
|
|
rend = &(screen.rend[row][col]);
|
|
for (; col <= last_col; col++, rend++)
|
|
*rend |= RS_Select;
|
|
col = 0;
|
|
}
|
|
rend = &(screen.rend[row][col]);
|
|
for (; col <= endc; col++, rend++)
|
|
*rend |= RS_Select;
|
|
} else {
|
|
for (row = startr; row < endr; row++) {
|
|
rend = &(screen.rend[row][col]);
|
|
for (; col <= last_col; col++, rend++)
|
|
*rend &= ~RS_Select;
|
|
col = 0;
|
|
}
|
|
rend = &(screen.rend[row][col]);
|
|
for (; col <= endc; col++, rend++)
|
|
*rend &= ~RS_Select;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Mark a selection at the specified x/y pixel location
|
|
*/
|
|
void
|
|
selection_start(int x, int y)
|
|
{
|
|
D_SELECT(("selection_start(%d, %d)\n", x, y));
|
|
selection_start_colrow(Pixel2Col(x), Pixel2Row(y));
|
|
}
|
|
|
|
/*
|
|
* Mark a selection at the specified col/row
|
|
*/
|
|
void
|
|
selection_start_colrow(int col, int row)
|
|
{
|
|
int end_col;
|
|
|
|
D_SELECT(("selection_start_colrow(%d, %d)\n", col, row));
|
|
|
|
if (selection.op) {
|
|
/* clear the old selection */
|
|
|
|
if (selection.beg.row < -TermWin.nscrolled)
|
|
selection_reset();
|
|
else
|
|
selection_setclr(0,
|
|
selection.beg.row, selection.beg.col,
|
|
selection.end.row, selection.end.col);
|
|
}
|
|
selection.op = SELECTION_INIT;
|
|
BOUND(row, 0, TermWin.nrow - 1);
|
|
|
|
row -= TermWin.view_start;
|
|
end_col = screen.text[row + TermWin.saveLines][TermWin.ncol];
|
|
if (end_col != WRAP_CHAR && col > end_col)
|
|
col = TermWin.ncol;
|
|
selection.mark.col = col;
|
|
selection.mark.row = row;
|
|
}
|
|
|
|
/*
|
|
* Copy a selection into the cut buffer
|
|
* EXT: button 1 or 3 release
|
|
*/
|
|
void
|
|
selection_make(Time tm)
|
|
{
|
|
int i, col, end_col, row, end_row;
|
|
text_t *new_selection_text;
|
|
char *str;
|
|
text_t *t;
|
|
|
|
D_SELECT(("selection.op=%d, selection.clicks=%d\n", selection.op, selection.clicks));
|
|
switch (selection.op) {
|
|
case SELECTION_CONT:
|
|
break;
|
|
case SELECTION_INIT:
|
|
selection_reset();
|
|
selection.end.row = selection.beg.row = selection.mark.row;
|
|
selection.end.col = selection.beg.col = selection.mark.col;
|
|
/* FALLTHROUGH */
|
|
case SELECTION_BEGIN:
|
|
selection.op = SELECTION_DONE;
|
|
/* FALLTHROUGH */
|
|
default:
|
|
return;
|
|
}
|
|
selection.op = SELECTION_DONE;
|
|
|
|
if (selection.clicks == 4)
|
|
return; /* nothing selected, go away */
|
|
|
|
if (selection.beg.row < -TermWin.nscrolled
|
|
|| selection.end.row >= TermWin.nrow) {
|
|
selection_reset();
|
|
return;
|
|
}
|
|
i = (selection.end.row - selection.beg.row + 1) * (TermWin.ncol + 1) + 1;
|
|
str = MALLOC(i * sizeof(char));
|
|
new_selection_text = (unsigned char *) str;
|
|
|
|
col = MAX(selection.beg.col, 0);
|
|
row = selection.beg.row + TermWin.saveLines;
|
|
end_row = selection.end.row + TermWin.saveLines;
|
|
/*
|
|
* A: rows before end row
|
|
*/
|
|
for (; row < end_row; row++) {
|
|
t = &(screen.text[row][col]);
|
|
if ((end_col = screen.text[row][TermWin.ncol]) == WRAP_CHAR)
|
|
end_col = TermWin.ncol;
|
|
for (; col < end_col; col++)
|
|
*str++ = *t++;
|
|
col = 0;
|
|
if (screen.text[row][TermWin.ncol] != WRAP_CHAR) {
|
|
if (!(Options & Opt_select_trailing_spaces)) {
|
|
for (str--; *str == ' ' || *str == '\t'; str--);
|
|
str++;
|
|
}
|
|
*str++ = '\n';
|
|
}
|
|
}
|
|
/*
|
|
* B: end row
|
|
*/
|
|
t = &(screen.text[row][col]);
|
|
end_col = screen.text[row][TermWin.ncol];
|
|
if (end_col == WRAP_CHAR || selection.end.col <= end_col) {
|
|
i = 0;
|
|
end_col = selection.end.col + 1;
|
|
} else
|
|
i = 1;
|
|
UPPER_BOUND(end_col, TermWin.ncol);
|
|
for (; col < end_col; col++)
|
|
*str++ = *t++;
|
|
if (!(Options & Opt_select_trailing_spaces)) {
|
|
for (str--; *str == ' ' || *str == '\t'; str--);
|
|
str++;
|
|
}
|
|
if (i)
|
|
*str++ = '\n';
|
|
*str = '\0';
|
|
if ((i = strlen((char *) new_selection_text)) == 0) {
|
|
FREE(new_selection_text);
|
|
return;
|
|
}
|
|
selection.len = i;
|
|
if (selection.text)
|
|
FREE(selection.text);
|
|
selection.text = new_selection_text;
|
|
selection.screen = current_screen;
|
|
|
|
selection_copy(XA_PRIMARY);
|
|
D_SELECT(("selection.len=%d\n", selection.len));
|
|
return;
|
|
tm = 0;
|
|
}
|
|
|
|
/*
|
|
* Mark or select text based upon number of clicks: 1, 2, or 3
|
|
* EXT: button 1 press
|
|
*/
|
|
void
|
|
selection_click(int clicks, int x, int y)
|
|
{
|
|
|
|
/*
|
|
* int r, c;
|
|
* row_col_t ext_beg, ext_end;
|
|
*/
|
|
|
|
D_SELECT(("selection_click(%d, %d, %d)\n", clicks, x, y));
|
|
|
|
clicks = ((clicks - 1) % 3) + 1;
|
|
selection.clicks = clicks; /* save clicks so extend will work */
|
|
|
|
selection_start(x, y); /* adjusts for scroll offset */
|
|
if (clicks == 2 || clicks == 3)
|
|
selection_extend_colrow(selection.mark.col,
|
|
selection.mark.row + TermWin.view_start, 0, 1);
|
|
}
|
|
|
|
/*
|
|
* Select text for 2 clicks
|
|
* row is given as a normal selection row value
|
|
* beg.row, end.row are returned as normal selection row values
|
|
*/
|
|
|
|
/* what do we want: spaces/tabs are delimiters or cutchars or non-cutchars */
|
|
#ifdef CUTCHAR_OPTION
|
|
# define DELIMIT_TEXT(x) (strchr((rs_cutchars?rs_cutchars:CUTCHARS), (x)) != NULL)
|
|
#else
|
|
# define DELIMIT_TEXT(x) (strchr(CUTCHARS, (x)) != NULL)
|
|
#endif
|
|
#ifdef MULTI_CHARSET
|
|
#define DELIMIT_REND(x) (((x) & RS_multiMask) ? 1 : 0)
|
|
#endif
|
|
|
|
void
|
|
selection_delimit_word(int col, int row, row_col_t *beg, row_col_t *end)
|
|
{
|
|
int beg_col, beg_row, end_col, end_row, last_col;
|
|
int row_offset, w1;
|
|
text_t *stp, *stp1, t;
|
|
|
|
#ifdef MULTI_CHARSET
|
|
int w2;
|
|
rend_t *srp, r;
|
|
|
|
#endif
|
|
|
|
if (selection.clicks != 2) /* We only handle double clicks: go away */
|
|
return;
|
|
|
|
if (!screen.text || !screen.rend)
|
|
return;
|
|
|
|
last_col = TermWin.ncol - 1;
|
|
|
|
if (row >= TermWin.nrow) {
|
|
row = TermWin.nrow - 1;
|
|
col = last_col;
|
|
} else if (row < -TermWin.saveLines) {
|
|
row = -TermWin.saveLines;
|
|
col = 0;
|
|
}
|
|
beg_col = end_col = col;
|
|
beg_row = end_row = row;
|
|
|
|
row_offset = TermWin.saveLines;
|
|
|
|
/* A: find the beginning of the word */
|
|
|
|
if (!screen.text[beg_row + row_offset] || !screen.rend[beg_row + row_offset])
|
|
return;
|
|
if (!screen.text[end_row + row_offset] || !screen.rend[end_row + row_offset])
|
|
return;
|
|
#if 0
|
|
if (!screen.text[beg_row + row_offset - 1] || !screen.rend[beg_row + row_offset - 1])
|
|
return;
|
|
if (!screen.text[end_row + row_offset + 1] || !screen.rend[end_row + row_offset + 1])
|
|
return;
|
|
#endif
|
|
|
|
stp1 = stp = &(screen.text[beg_row + row_offset][beg_col]);
|
|
w1 = DELIMIT_TEXT(*stp);
|
|
if (w1 == 2)
|
|
w1 = 0;
|
|
#ifdef MULTI_CHARSET
|
|
srp = &(screen.rend[beg_row + row_offset][beg_col]);
|
|
w2 = DELIMIT_REND(*srp);
|
|
#endif
|
|
|
|
for (;;) {
|
|
for (; beg_col > 0; beg_col--) {
|
|
t = *--stp;
|
|
if (DELIMIT_TEXT(t) != w1 || (w1 && *stp1 != t && Options & Opt_xterm_select))
|
|
break;
|
|
#ifdef MULTI_CHARSET
|
|
r = *--srp;
|
|
if (DELIMIT_REND(r) != w2)
|
|
break;
|
|
#endif
|
|
}
|
|
if (!(Options & Opt_xterm_select)) {
|
|
if (beg_col == col && beg_col > 0) {
|
|
if (DELIMIT_TEXT(*stp)) /* space or tab or cutchar */
|
|
break;
|
|
#ifdef MULTI_CHARSET
|
|
srp = &(screen.rend[beg_row + row_offset][beg_col - 1]);
|
|
#endif
|
|
for (; --beg_col > 0;) {
|
|
t = *--stp;
|
|
if (DELIMIT_TEXT(t))
|
|
break;
|
|
#ifdef MULTI_CHARSET
|
|
r = *--srp;
|
|
if (DELIMIT_REND(r) != w2)
|
|
break;
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
if (beg_col == 0 && (beg_row > -TermWin.nscrolled)) {
|
|
stp = &(screen.text[beg_row + row_offset - 1][last_col + 1]);
|
|
if (*stp == WRAP_CHAR) {
|
|
t = *(stp - 1);
|
|
#ifdef MULTI_CHARSET
|
|
srp = &(screen.rend[beg_row + row_offset - 1][last_col + 1]);
|
|
r = *(srp - 1);
|
|
if (DELIMIT_TEXT(t) == w1 && (!w1 || *stp == t || !(Options & Opt_xterm_select)) && DELIMIT_REND(r) == w2) {
|
|
srp--;
|
|
#else
|
|
if (DELIMIT_TEXT(t) == w1 && (!w1 || *stp == t || !(Options & Opt_xterm_select))) {
|
|
#endif
|
|
stp--;
|
|
beg_row--;
|
|
beg_col = last_col;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* B: find the end of the word */
|
|
|
|
# ifdef OPTIMIZE_HACKS
|
|
stp = stp1;
|
|
# else
|
|
stp1 = stp = &(screen.text[end_row + row_offset][end_col]);
|
|
# endif
|
|
|
|
#ifdef MULTI_CHARSET
|
|
srp = &(screen.rend[end_row + row_offset][end_col]);
|
|
#endif
|
|
for (;;) {
|
|
for (; end_col < last_col; end_col++) {
|
|
t = *++stp;
|
|
if (DELIMIT_TEXT(t) != w1 || (w1 && *stp1 != t && Options & Opt_xterm_select))
|
|
break;
|
|
#ifdef MULTI_CHARSET
|
|
r = *++srp;
|
|
if (DELIMIT_REND(r) != w2)
|
|
break;
|
|
#endif
|
|
}
|
|
if (!(Options & Opt_xterm_select)) {
|
|
if (end_col == col && end_col < last_col) {
|
|
if (DELIMIT_TEXT(*stp)) /* space or tab or cutchar */
|
|
break;
|
|
#ifdef MULTI_CHARSET
|
|
srp = &(screen.rend[end_row + row_offset][end_col + 1]);
|
|
#endif
|
|
for (; ++end_col < last_col;) {
|
|
t = *++stp;
|
|
if (DELIMIT_TEXT(t))
|
|
break;
|
|
#ifdef MULTI_CHARSET
|
|
r = *++srp;
|
|
if (DELIMIT_REND(r) != w2)
|
|
break;
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
if (end_col == last_col
|
|
&& (end_row < (TermWin.nrow - 1))) {
|
|
if (*++stp == WRAP_CHAR) {
|
|
stp = screen.text[end_row + row_offset + 1];
|
|
#ifdef MULTI_CHARSET
|
|
srp = screen.rend[end_row + row_offset + 1];
|
|
if (DELIMIT_TEXT(*stp) == w1 && (!w1 || *stp1 == *stp || !(Options & Opt_xterm_select)) && DELIMIT_REND(*srp) == w2) {
|
|
#else
|
|
if (DELIMIT_TEXT(*stp) == w1 && (!w1 || *stp1 == *stp || !(Options & Opt_xterm_select))) {
|
|
#endif
|
|
end_row++;
|
|
end_col = 0;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
D_SELECT(("selection_delimit_word(%d, %d) says (%d,%d)->(%d,%d)\n", col, row, beg_col, beg_row, end_col, end_row));
|
|
|
|
/* Poke the values back in */
|
|
beg->col = beg_col;
|
|
beg->row = beg_row;
|
|
end->col = end_col;
|
|
end->row = end_row;
|
|
}
|
|
|
|
/*
|
|
* Extend the selection to the specified x/y pixel location
|
|
* EXT: button 3 press; button 1 or 3 drag
|
|
* flag == 0 ==> button 1
|
|
* flag != 0 ==> button 3
|
|
*/
|
|
void
|
|
selection_extend(int x, int y, int flag)
|
|
{
|
|
int col, row;
|
|
|
|
/*
|
|
* If we're selecting characters (single click) then we must check first
|
|
* if we are at the same place as the original mark. If we are then
|
|
* select nothing. Otherwise, if we're to the right of the mark, you have to
|
|
* be _past_ a character for it to be selected.
|
|
*/
|
|
col = Pixel2Col(x);
|
|
row = Pixel2Row(y);
|
|
BOUND(row, 0, TermWin.nrow - 1);
|
|
if (((selection.clicks % 3) == 1) && !flag && (col == selection.mark.col && (row == selection.mark.row + TermWin.view_start))) {
|
|
/* select nothing */
|
|
selection_setclr(0, selection.beg.row, selection.beg.col,
|
|
selection.end.row, selection.end.col);
|
|
selection.beg.row = selection.end.row = selection.mark.row;
|
|
selection.beg.col = selection.end.col = selection.mark.col;
|
|
selection.clicks = 4;
|
|
D_SELECT(("selection.clicks = 4\n"));
|
|
return;
|
|
}
|
|
if (selection.clicks == 4)
|
|
selection.clicks = 1;
|
|
selection_extend_colrow(col, row, flag, 0);
|
|
}
|
|
|
|
/*
|
|
* Extend the selection to the specified col/row
|
|
*/
|
|
void
|
|
selection_extend_colrow(int col, int row, int flag, int cont)
|
|
{
|
|
int old_col, end_col;
|
|
row_col_t new_beg1, new_beg2, new_end1, new_end2, old_beg, old_end;
|
|
enum {
|
|
LEFT, RIGHT
|
|
} closeto = RIGHT;
|
|
|
|
#ifdef MULTI_CHARSET
|
|
int r;
|
|
|
|
#endif
|
|
|
|
D_SELECT(("selection_extend_colrow(%d, %d, %d, %d) clicks:%d\n", col, row, flag, cont, selection.clicks));
|
|
|
|
switch (selection.op) {
|
|
case SELECTION_INIT:
|
|
selection_reset();
|
|
selection.end.col = selection.beg.col = selection.mark.col;
|
|
selection.end.row = selection.beg.row = selection.mark.row;
|
|
selection.op = SELECTION_BEGIN;
|
|
/* FALLTHROUGH */
|
|
case SELECTION_BEGIN:
|
|
break;
|
|
case SELECTION_DONE:
|
|
selection.op = SELECTION_CONT;
|
|
/* FALLTHROUGH */
|
|
case SELECTION_CONT:
|
|
break;
|
|
case SELECTION_CLEAR:
|
|
selection_start_colrow(col, row);
|
|
/* FALLTHROUGH */
|
|
default:
|
|
return;
|
|
}
|
|
if ((selection.beg.row < -TermWin.nscrolled)
|
|
|| (selection.end.row < -TermWin.nscrolled)) {
|
|
selection_reset();
|
|
return;
|
|
}
|
|
old_col = col;
|
|
BOUND(col, -1, TermWin.ncol);
|
|
old_beg.col = selection.beg.col;
|
|
old_beg.row = selection.beg.row;
|
|
old_end.col = selection.end.col;
|
|
old_end.row = selection.end.row;
|
|
|
|
if ((selection.op == SELECTION_BEGIN) && (cont || (row != selection.mark.row || col != selection.mark.col)))
|
|
selection.op = SELECTION_CONT;
|
|
|
|
row -= TermWin.view_start; /* adjust for scroll */
|
|
|
|
if (flag) {
|
|
if (row < selection.beg.row
|
|
|| (row == selection.beg.row && col < selection.beg.col))
|
|
closeto = LEFT;
|
|
else if (row > selection.end.row
|
|
|| (row == selection.end.row && col >= selection.end.col)) {
|
|
/* */ ;
|
|
} else if (((col - selection.beg.col)
|
|
+ ((row - selection.beg.row) * TermWin.ncol))
|
|
< ((selection.end.col - col)
|
|
+ ((selection.end.row - row) * TermWin.ncol)))
|
|
closeto = LEFT;
|
|
}
|
|
if (selection.clicks == 1) {
|
|
/*
|
|
* A1: extension on single click - selection between points
|
|
*/
|
|
if (flag) { /* button 3 extension */
|
|
if (closeto == LEFT) {
|
|
selection.beg.row = row;
|
|
selection.beg.col = col;
|
|
end_col = screen.text[row + TermWin.saveLines][TermWin.ncol];
|
|
if (end_col != WRAP_CHAR && selection.beg.col > end_col) {
|
|
if (selection.beg.row < selection.end.row) {
|
|
selection.beg.col = -1;
|
|
selection.beg.row++;
|
|
} else {
|
|
selection.beg.col = selection.mark.col;
|
|
selection.beg.row = selection.mark.row;
|
|
}
|
|
}
|
|
} else {
|
|
selection.end.row = row;
|
|
selection.end.col = col - 1;
|
|
end_col = screen.text[row + TermWin.saveLines][TermWin.ncol];
|
|
if (end_col != WRAP_CHAR && selection.end.col >= end_col)
|
|
selection.end.col = TermWin.ncol - 1;
|
|
}
|
|
} else if ((row < selection.mark.row)
|
|
|| (row == selection.mark.row && col < selection.mark.col)) {
|
|
/* select left of mark character excluding mark */
|
|
selection.beg.row = row;
|
|
selection.beg.col = col;
|
|
selection.end.row = selection.mark.row;
|
|
selection.end.col = selection.mark.col - 1;
|
|
if (selection.end.col >= 0) {
|
|
end_col = screen.text[row + TermWin.saveLines][TermWin.ncol];
|
|
if (end_col != WRAP_CHAR && selection.beg.col > end_col) {
|
|
if (selection.beg.row < selection.end.row) {
|
|
selection.beg.col = -1;
|
|
selection.beg.row++;
|
|
} else {
|
|
selection.beg.col = selection.mark.col;
|
|
selection.beg.row = selection.mark.row;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
/* select right of mark character including mark */
|
|
selection.beg.row = selection.mark.row;
|
|
selection.beg.col = selection.mark.col;
|
|
selection.end.row = row;
|
|
selection.end.col = col - 1;
|
|
if (old_col >= 0) {
|
|
end_col = screen.text[row + TermWin.saveLines][TermWin.ncol];
|
|
if (end_col != WRAP_CHAR && selection.end.col >= end_col)
|
|
selection.end.col = TermWin.ncol - 1;
|
|
}
|
|
}
|
|
#ifdef MULTI_CHARSET
|
|
if ((selection.beg.col > 0) && (selection.beg.col < TermWin.ncol)) {
|
|
r = selection.beg.row + TermWin.saveLines;
|
|
if (((screen.rend[r][selection.beg.col] & RS_multiMask) == RS_multi2)
|
|
&& ((screen.rend[r][selection.beg.col - 1] & RS_multiMask) == RS_multi1))
|
|
selection.beg.col--;
|
|
}
|
|
if ((selection.end.col > 0) && (selection.end.col < (TermWin.ncol - 1))) {
|
|
r = selection.end.row + TermWin.saveLines;
|
|
if (((screen.rend[r][selection.end.col] & RS_multiMask) == RS_multi1)
|
|
&& ((screen.rend[r][selection.end.col + 1] & RS_multiMask) == RS_multi2))
|
|
selection.end.col++;
|
|
}
|
|
#endif
|
|
} else if (selection.clicks == 2) {
|
|
/*
|
|
* A2: extension on double click - selection between words
|
|
*/
|
|
selection_delimit_word(col, row, &new_beg2, &new_end2);
|
|
if (flag && closeto == LEFT)
|
|
selection_delimit_word(selection.end.col, selection.end.row, &new_beg1, &new_end1);
|
|
else if (flag && closeto == RIGHT)
|
|
selection_delimit_word(selection.beg.col, selection.beg.row, &new_beg1, &new_end1);
|
|
else
|
|
selection_delimit_word(selection.mark.col, selection.mark.row, &new_beg1, &new_end1);
|
|
if ((!flag && (selection.mark.row < row || (selection.mark.row == row && selection.mark.col <= col)))
|
|
|| (flag && closeto == RIGHT)) {
|
|
selection.beg.col = new_beg1.col;
|
|
selection.beg.row = new_beg1.row;
|
|
selection.end.col = new_end2.col;
|
|
selection.end.row = new_end2.row;
|
|
} else {
|
|
selection.beg.col = new_beg2.col;
|
|
selection.beg.row = new_beg2.row;
|
|
selection.end.col = new_end1.col;
|
|
selection.end.row = new_end1.row;
|
|
}
|
|
} else if (selection.clicks == 3) {
|
|
/*
|
|
* A3: extension on triple click - selection between lines
|
|
*/
|
|
if (flag) {
|
|
if (closeto == LEFT)
|
|
selection.beg.row = row;
|
|
else
|
|
selection.end.row = row;
|
|
} else if (row <= selection.mark.row) {
|
|
selection.beg.row = row;
|
|
selection.end.row = selection.mark.row;
|
|
} else {
|
|
selection.beg.row = selection.mark.row;
|
|
selection.end.row = row;
|
|
}
|
|
if (Options & Opt_select_whole_line) {
|
|
selection.beg.col = 0;
|
|
} else {
|
|
selection.clicks = 2;
|
|
selection_delimit_word(col, row, &new_beg2, &new_end2);
|
|
selection.beg.col = new_beg2.col;
|
|
selection.clicks = 3;
|
|
}
|
|
selection.end.col = TermWin.ncol - 1;
|
|
}
|
|
D_SELECT(("(c:%d,r:%d)-(c:%d,r:%d) old (c:%d,r:%d)-(c:%d,r:%d)\n", selection.beg.col, selection.beg.row,
|
|
selection.end.col, selection.end.row, old_beg.col, old_beg.row, old_end.col, old_end.row));
|
|
|
|
/*
|
|
* B1: clear anything before the current selection
|
|
*/
|
|
if ((old_beg.row < selection.beg.row) || (old_beg.row == selection.beg.row && old_beg.col < selection.beg.col)) {
|
|
if (selection.beg.col < TermWin.ncol - 1) {
|
|
row = selection.beg.row;
|
|
col = selection.beg.col + 1;
|
|
} else {
|
|
row = selection.beg.row + 1;
|
|
col = 0;
|
|
}
|
|
selection_setclr(0, old_beg.row, old_beg.col, row, col);
|
|
}
|
|
/*
|
|
* B2: clear anything after the current selection
|
|
*/
|
|
if ((old_end.row > selection.end.row) || (old_end.row == selection.end.row && old_end.col > selection.end.col)) {
|
|
if (selection.end.col > 0) {
|
|
row = selection.end.row;
|
|
col = selection.end.col - 1;
|
|
} else {
|
|
row = selection.end.row - 1;
|
|
col = TermWin.ncol - 1;
|
|
}
|
|
selection_setclr(0, row, col, old_end.row, old_end.col);
|
|
}
|
|
/*
|
|
* B3: set everything
|
|
*/
|
|
/* TODO: optimise this */
|
|
selection_setclr(1, selection.beg.row, selection.beg.col, selection.end.row, selection.end.col);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Double click on button 3 when already selected
|
|
* EXT: button 3 double click
|
|
*/
|
|
void
|
|
selection_rotate(int x, int y)
|
|
{
|
|
int col, row;
|
|
|
|
col = Pixel2Col(x);
|
|
row = Pixel2Row(y);
|
|
selection.clicks = selection.clicks % 3 + 1;
|
|
selection_extend_colrow(col, row, 1, 0);
|
|
}
|
|
|
|
/*
|
|
* On some systems, the Atom typedef is 64 bits wide. We need to have a type
|
|
* that is exactly 32 bits wide, because a format of 64 is not allowed by
|
|
* the X11 protocol.
|
|
*/
|
|
typedef CARD32 Atom32;
|
|
|
|
/*
|
|
* Respond to a request for our current selection
|
|
* EXT: SelectionRequest
|
|
*/
|
|
void
|
|
selection_send(XSelectionRequestEvent * rq)
|
|
{
|
|
XEvent ev;
|
|
Atom32 target_list[2];
|
|
|
|
ev.xselection.type = SelectionNotify;
|
|
ev.xselection.property = None;
|
|
ev.xselection.display = rq->display;
|
|
ev.xselection.requestor = rq->requestor;
|
|
ev.xselection.selection = rq->selection;
|
|
ev.xselection.target = rq->target;
|
|
ev.xselection.time = rq->time;
|
|
|
|
if (rq->target == props[PROP_SELECTION_TARGETS]) {
|
|
target_list[0] = (Atom32) props[PROP_SELECTION_TARGETS];
|
|
target_list[1] = (Atom32) XA_STRING;
|
|
XChangeProperty(Xdisplay, rq->requestor, rq->property, rq->target,
|
|
(8 * sizeof(target_list[0])), PropModeReplace,
|
|
(unsigned char *) target_list,
|
|
(sizeof(target_list) / sizeof(target_list[0])));
|
|
ev.xselection.property = rq->property;
|
|
#if defined(MULTI_CHARSET) && defined(HAVE_X11_XMU_ATOMS_H)
|
|
} else if (rq->target == XA_TEXT(Xdisplay) || rq->target == XA_COMPOUND_TEXT(Xdisplay)) {
|
|
XTextProperty xtextp;
|
|
char *l[1];
|
|
|
|
*l = selection.text;
|
|
xtextp.value = NULL;
|
|
xtextp.nitems = 0;
|
|
if (XmbTextListToTextProperty(Xdisplay, l, 1, XCompoundTextStyle, &xtextp) == Success) {
|
|
if (xtextp.nitems > 0 && xtextp.value != NULL) {
|
|
XChangeProperty(Xdisplay, rq->requestor, rq->property, XA_COMPOUND_TEXT(Xdisplay), 8, PropModeReplace, xtextp.value, xtextp.nitems);
|
|
ev.xselection.property = rq->property;
|
|
}
|
|
}
|
|
#endif
|
|
} else if (rq->target == XA_STRING) {
|
|
XChangeProperty(Xdisplay, rq->requestor, rq->property, rq->target, 8, PropModeReplace, selection.text, selection.len);
|
|
ev.xselection.property = rq->property;
|
|
}
|
|
XSendEvent(Xdisplay, rq->requestor, False, 0, &ev);
|
|
}
|
|
|
|
void
|
|
mouse_report(XButtonEvent * ev)
|
|
{
|
|
int button_number, key_state;
|
|
|
|
button_number = ((ev->button == AnyButton) ? 3 : (ev->button - Button1));
|
|
key_state = ((ev->state & (ShiftMask | ControlMask))
|
|
+ ((ev->state & Mod1Mask) ? 2 : 0));
|
|
tt_printf((unsigned char *) "\033[M%c%c%c",
|
|
(32 + button_number + (key_state << 2)),
|
|
(32 + Pixel2Col(ev->x) + 1),
|
|
(32 + Pixel2Row(ev->y) + 1));
|
|
}
|
|
|
|
void
|
|
debug_colors(void)
|
|
{
|
|
int color;
|
|
char *name[] =
|
|
{
|
|
"fg", "bg",
|
|
"black", "red", "green", "yellow", "blue", "magenta", "cyan", "white"
|
|
};
|
|
|
|
fprintf(stderr, "Color ( ");
|
|
if (rstyle & RS_RVid)
|
|
fprintf(stderr, "rvid ");
|
|
if (rstyle & RS_Bold)
|
|
fprintf(stderr, "bold ");
|
|
if (rstyle & RS_Blink)
|
|
fprintf(stderr, "blink ");
|
|
if (rstyle & RS_Uline)
|
|
fprintf(stderr, "uline ");
|
|
fprintf(stderr, "): ");
|
|
|
|
color = GET_FGCOLOR(rstyle);
|
|
#ifndef NO_BRIGHTCOLOR
|
|
if (color >= minBright && color <= maxBright) {
|
|
color -= (minBright - minColor);
|
|
fprintf(stderr, "bright ");
|
|
}
|
|
#endif
|
|
fprintf(stderr, "%s on ", name[color]);
|
|
|
|
color = GET_BGCOLOR(rstyle);
|
|
#ifndef NO_BRIGHTCOLOR
|
|
if (color >= minBright && color <= maxBright) {
|
|
color -= (minBright - minColor);
|
|
fprintf(stderr, "bright ");
|
|
}
|
|
#endif
|
|
fprintf(stderr, "%s\n", name[color]);
|
|
}
|
|
|
|
#ifdef USE_XIM
|
|
void xim_get_position(XPoint *pos)
|
|
{
|
|
pos->x = Col2Pixel(screen.col);
|
|
if (scrollbar_is_visible() && !(Options & Opt_scrollbar_right)) {
|
|
pos->x += scrollbar_trough_width();
|
|
}
|
|
pos->y = (Height2Pixel(screen.row)
|
|
# ifdef MULTI_CHARSET
|
|
+ MAX((encoding_method == LATIN1 ? 0 : TermWin.mfont->ascent), TermWin.font->ascent)
|
|
# else
|
|
+ TermWin.font->ascent
|
|
# endif
|
|
+ TermWin.internalBorder
|
|
+ bbar_calc_docked_height(BBAR_DOCKED_TOP));
|
|
}
|
|
#endif
|