/* screen.c * * Copyright (c) 1999 Chris Smith * * This file is distributed under the GPL, version 2 or at your option any * later version. See COPYING for details. */ #include "options.h" #include #include #include #include #include #include #include SLANG_H #include "lpe.h" #include "lpecomm.h" #include "screen.h" #include "buffer.h" #include "minibuf.h" #include "input.h" #include "help.h" #include "cfg.h" /* Definitions of screen update flags. See screen.h for explanation. */ volatile sig_atomic_t refresh_complete; volatile sig_atomic_t refresh_text; volatile sig_atomic_t refresh_banner; /* A global variable used to determine the starting column for the minibuf... * this is in turn returned by get_minibuf_col, which is used in minibuf.c to * decide when to start scrolling the minibuf for mbuf_ask(). */ int minibuf_col = 0; /* Initialize the S-Lang libraries for use in lpe. This sets up the screen and * does other various cleanup needed for screen management. */ void init_slang (void) { SLtt_get_terminfo (); #ifdef TIOCGWINSZ { struct winsize ws; /* Try to get a better screen size. However, some terminals * (Microsoft * Windows bundled telnet client, for example) will * report 0x0 as the * size, so check for that first. */ if (!ioctl (1, TIOCGWINSZ, &ws) && (ws.ws_row != 0) && (ws.ws_col != 0)) { SLtt_Screen_Rows = ws.ws_row; SLtt_Screen_Cols = ws.ws_col; } } #endif /* defined(TIOCGWINSZ) */ SLang_init_tty (-1, 1, 0); SLang_set_abort_signal (NULL); SLsmg_init_smg (); refresh_text = 1; refresh_banner = 1; refresh_complete = 0; } /* Initialize key bindings for S-Lang. This is separated because it need not * be done again when the screen is completely refreshed. In fact, binding * these keys several times seems to result in some type of memory leak, so it * is really important that this only gets called once. */ void init_slang_keys (void) { char *term; SLkp_init (); /* dumb hacks to fix some terminal weirdness */ term = getenv ("TERM"); if (!strcmp (term, "xterm") || !strcmp (term, "xterm-debian")) { SLkp_define_keysym ("\010", LPE_BACKSPACE_KEY); SLkp_define_keysym ("\177", LPE_DELETE_KEY); } if (!strcmp (term, "rxvt")) { SLkp_define_keysym ("\177", LPE_BACKSPACE_KEY); SLkp_define_keysym ("\033\133\063\176", LPE_DELETE_KEY); } if (!strcmp (term, "mach") || !strcmp (term, "mach-color")) { SLkp_define_keysym ("\177", LPE_BACKSPACE_KEY); } SLkp_define_keysym ("\030", LPE_EXIT_KEY); /* Ctrl-X */ SLkp_define_keysym ("\032", LPE_SUSPEND_KEY); /* Ctrl-Z */ SLkp_define_keysym ("\014", LPE_REFRESH_KEY); /* Ctrl-L */ SLkp_define_keysym ("\022", LPE_PGUP_KEY); /* Ctrl-R */ SLkp_define_keysym ("\024", LPE_PGDN_KEY); /* Ctrl-T */ SLkp_define_keysym ("\017", LPE_PREV_WORD_KEY); /* Ctrl-O */ SLkp_define_keysym ("\020", LPE_NEXT_WORD_KEY); /* Ctrl-P */ SLkp_define_keysym ("\013", LPE_KILL_KEY); /* Ctrl-K */ SLkp_define_keysym ("\025", LPE_YANK_KEY); /* Ctrl-U */ SLkp_define_keysym ("\031", LPE_YANK_KEY); /* Ctrl-Y */ SLkp_define_keysym ("\023", LPE_SEARCH_KEY); /* Ctrl-S */ SLkp_define_keysym ("\001", LPE_FIND_NEXT_KEY); /* Ctrl-A */ SLkp_define_keysym ("\006\017", LPE_OPEN_KEY); /* Ctrl-F Ctrl-O */ SLkp_define_keysym ("\006\023", LPE_SAVE_KEY); /* Ctrl-F Ctrl-S */ SLkp_define_keysym ("\006\001", LPE_SAVE_ALT_KEY); /* Ctrl-F Ctrl-A */ SLkp_define_keysym ("\006\022", LPE_READF_KEY); /* Ctrl-F Ctrl-R */ SLkp_define_keysym ("\006\005", LPE_CLEARMOD_KEY); /* Ctrl-F Ctrl-E */ SLkp_define_keysym ("\006\030", LPE_NEXTBUF_KEY); /* Ctrl-F Ctrl-X */ SLkp_define_keysym ("\006\032", LPE_PREVBUF_KEY); /* Ctrl-F Ctrl-Z */ SLkp_define_keysym ("\006\016", LPE_OPENBUF_KEY); /* Ctrl-F Ctrl-N */ SLkp_define_keysym ("\006\014", LPE_CLOSEBUF_KEY); /* Ctrl-F Ctrl-L */ SLkp_define_keysym ("\002\023", LPE_SET_MODE_KEY); /* Ctrl-B Ctrl-S */ SLkp_define_keysym ("\002\024", LPE_TAB_SWAP_KEY); /* Ctrl-B Ctrl-T */ SLkp_define_keysym ("\002\001", LPE_AI_TOGGLE_KEY); /* Ctrl-B Ctrl-A */ SLkp_define_keysym ("\007\021", LPE_HOME_KEY); /* Ctrl-G Ctrl-Q */ SLkp_define_keysym ("\007\027", LPE_END_KEY); /* Ctrl-G Ctrl-W */ SLkp_define_keysym ("\007\001", LPE_BUF_START_KEY); /* Ctrl-G Ctrl-A */ SLkp_define_keysym ("\007\023", LPE_BUF_END_KEY); /* Ctrl-G Ctrl-S */ SLkp_define_keysym ("\007\007", LPE_GOTOLN_KEY); /* Ctrl-G Ctrl-G */ SLkp_define_keysym ("\016\022", LPE_REP_KEY); /* Ctrl-N Ctrl-R */ SLkp_define_keysym ("\016\024", LPE_REP_QUAD_KEY); /* Ctrl-N Ctrl-T */ SLkp_define_keysym ("\016\017", LPE_RECORDER_KEY); /* Ctrl-N Ctrl-O */ SLkp_define_keysym ("\016\020", LPE_PLAYBACK_KEY); /* Ctrl-N Ctrl-P */ SLkp_define_keysym ("\026\026", LPE_SHELL_KEY); /* Ctrl-V Ctrl-V */ SLkp_define_keysym ("\026\001", LPE_AWK_KEY); /* Ctrl-V Ctrl-A */ SLkp_define_keysym ("\026\023", LPE_SED_KEY); /* Ctrl-V Ctrl-S */ SLkp_define_keysym ("\026\002", LPE_SHELL_LN_KEY); /* Ctrl-V Ctrl-B */ SLkp_define_keysym ("\026\004", LPE_AWK_LN_KEY); /* Ctrl-V Ctrl-D */ SLkp_define_keysym ("\026\006", LPE_SED_LN_KEY); /* Ctrl-V Ctrl-F */ SLkp_define_keysym ("\005", LPE_HELP_KEY); /* Ctrl-E */ SLkp_define_keysym ("\004\004", LPE_DEBUG_KEY); /* Ctrl-D Ctrl-D */ SLkp_define_keysym ("\004\023", LPE_SLANG_KEY); /* Ctrl-D Ctrl-S */ } /* Get out of S-Lang's raw terminal mode */ void cleanup_slang (void) { SLsmg_gotorc (SLtt_Screen_Rows - 1, 0); SLsmg_refresh (); SLsmg_reset_smg (); SLang_reset_tty (); } /* Draw the banner at the bottom of the screen */ static void draw_banner (buffer * buf) { char banner[1024]; char tmp[20], *ptmp; int pos, t; if (SLtt_Use_Ansi_Colors) SLsmg_set_color (0); memset (banner, '-', SLtt_Screen_Cols); memcpy (banner + 2, "lpe", 3); if (buf->rdonly) banner[7] = '%'; if (buf->modified) banner[7] = '*'; pos = 10; if (buf->offerhelp && !is_help) { ptmp = _("Ctrl-E for Help"); t = strlen (ptmp); memcpy (banner + pos, ptmp, t); pos += t + 2; } t = strlen (buf->name); memcpy (banner + pos, buf->name, t); pos += t + 2; if (buf->mode_name != NULL) { t = strlen (buf->mode_name); memcpy (banner + pos, "<", 1); memcpy (banner + pos + 1, buf->mode_name, t); memcpy (banner + pos + t + 1, ">", 1); pos += t + 4; } sprintf (tmp, "L%u", buf->linenum + 1); t = strlen (tmp); memcpy (banner + pos, tmp, t); pos += t + 2; if (public_repeat_count > 1) { sprintf (tmp, "(%u)", public_repeat_count); t = strlen (tmp); memcpy (banner + pos, tmp, t); pos += t + 2; } minibuf_col = pos; if (the_mbuf.vis) { int i = 0; t = strlen (the_mbuf.label); memcpy (banner + pos, the_mbuf.label, t); pos += t; t = strlen (the_mbuf.text + the_mbuf.scroll); if (t > the_mbuf.width) t = the_mbuf.width; else if (t < the_mbuf.width) i = the_mbuf.width - t; memcpy (banner + pos, the_mbuf.text + the_mbuf.scroll, t); pos += t; while (i > 0) { banner[pos++] = ' '; i--; } } SLsmg_gotorc (SLtt_Screen_Rows - 1, 0); SLsmg_write_nchars (banner, SLtt_Screen_Cols); } /* Move the cursor to the place it should be on the screen. This should be * called between any drawing activity and a refresh, because drawing changes * the cursor position. */ static void position_cursor (buffer * buf) { if (the_mbuf.focus) { SLsmg_gotorc (SLtt_Screen_Rows - 1, minibuf_col + strlen (the_mbuf.label) + the_mbuf.pos - the_mbuf.scroll); } else { SLsmg_gotorc (buf->linenum - buf->scrollnum, buf->scr_col - buf->scrollcol); } } /* Writes a representation for this control character into the buffer to print, * but backwards so as to coincide with my nifty little write_seg's strange * behavior. */ static void control_rep (char c, char *rep) { unsigned char ch = c; rep[2] = '\0'; if (ch < 32) { rep[1] = '^'; rep[0] = '@' + ch; } else if (ch == '\177') { rep[1] = '^'; rep[0] = '?'; } else if ((ch > 127) && (ch < 160)) { rep[1] = '~'; rep[0] = '@' + (ch - 128); } else { /* this shouldn't be happening -- so I print something obviously * * wrong so that people will notice if it does! :) */ rep[1] = '^'; rep[0] = '!'; } } /* Write a segment of text to the screen, starting at start_col within the * line, and writing at most width characters. Also takes into account tabs * in the line. */ static int write_seg (char *txt, int chars, int max_col, int start_col, int vis_col) { /* This is ugly, but at least it works */ char *p = txt; char *pending; int npending; int col = start_col; int htw = cfg_get_global_int_with_default ("hard_tab_width", DEF_HARD_TAB_WIDTH); // int htw= DEF_HARD_TAB_WIDTH; char c; npending = 0; pending = (char *) malloc (htw + 1); while (((p - txt) < chars) || (npending > 0)) { if (npending > 0) { c = pending[--npending]; } else if (*p == '\t') { int i, tab_spcs; tab_spcs = htw - (col - ((col / htw) * htw)) - 1; for (i = 0; i < tab_spcs; i++) pending[i] = ' '; npending = i; c = ' '; p++; } else if (is_control (*p)) { control_rep (*p, pending); c = pending[1]; npending = 1; p++; } else { c = *p; p++; } if ((col >= vis_col) && (col < max_col)) SLsmg_write_char (c); col++; } free (pending); return col; } static void write_line (buffer * buf, buf_line * line, int lnum, int vis_col) { char *txt; int i = 0; txt = line->txt; if ((SLtt_Use_Ansi_Colors) && (buf->highlight) && (buf->mode.highlight != NULL)) { int idx, start, state, len; idx = 0; state = -1; i = 0; len = strlen (txt); do { int color; start = idx; color = (*buf->mode.highlight) (buf, line, lnum, &idx, &state); SLsmg_set_color (color); i = write_seg (txt + start, idx - start, SLtt_Screen_Cols + vis_col - 1, i, vis_col); } while (idx < len); if (line->next != NULL) { line->next->start_state = state; buf->state_valid = line->next; buf->state_valid_num = lnum + 1; } /* back to a normal color for extra space and/or $ */ SLsmg_set_color (0); } else { int txtlen; txtlen = strlen (txt); i = write_seg (txt, txtlen, SLtt_Screen_Cols + vis_col - 1, 0, vis_col); } if (i - vis_col >= SLtt_Screen_Cols) { SLsmg_write_char ('$'); } else while (i - vis_col < SLtt_Screen_Cols) { if (i >= vis_col) SLsmg_write_char (' '); i++; } } /* Draw the current screenful of text to the screen */ static void draw_text (buffer * buf) { int i; buf_line *curline; curline = buf->scrollpos; for (i = 0; (i < SLtt_Screen_Rows - 1) && (curline != NULL); i++) { SLsmg_gotorc (i, 0); write_line (buf, curline, buf->scrollnum + i, buf->scrollcol); curline = curline->next; } while (i < SLtt_Screen_Rows - 1) { SLsmg_gotorc (i, 0); SLsmg_write_nstring (cfg_get_global_string_with_default ("eof_fill", DEF_EOF_FILL), SLtt_Screen_Cols); i++; } } /* Completely refresh the screen, re-initializing S-Lang and everything. Used * in case something gets screwed up, or when (as with Ctrl-Z) the editor gets * suspended and then resumed, and needs to be restored. */ static void refresh_screen (buffer * buf) { SLsmg_reset_smg (); SLang_reset_tty (); init_slang (); draw_text (buf); draw_banner (buf); position_cursor (buf); SLsmg_refresh (); } void draw_screen (buffer * buf) { if (refresh_complete) { refresh_screen (buf); refresh_complete = 0; refresh_text = 0; refresh_banner = 0; return; } if (refresh_text || refresh_banner) { if (refresh_text) { draw_text (buf); refresh_text = 0; } if (refresh_banner) { draw_banner (buf); refresh_banner = 0; } } position_cursor (buf); SLsmg_refresh (); } int get_minibuf_col (void) { return minibuf_col; }