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