+
+/*
+ * Physical-scrolling support
+ *
+ * This code was adapted from Keith Bostic's hardware scrolling
+ * support for 4.4BSD curses. I (esr) translated it to use terminfo
+ * capabilities, narrowed the call interface slightly, and cleaned
+ * up some convoluted tests. I also added support for the memory_above
+ * memory_below, and non_dest_scroll_region capabilities.
+ *
+ * For this code to work, we must have either
+ * change_scroll_region and scroll forward/reverse commands, or
+ * insert and delete line capabilities.
+ * When the scrolling region has been set, the cursor has to
+ * be at the last line of the region to make the scroll up
+ * happen, or on the first line of region to scroll down.
+ *
+ * This code makes one aesthetic decision in the opposite way from
+ * BSD curses. BSD curses preferred pairs of il/dl operations
+ * over scrolls, allegedly because il/dl looked faster. We, on
+ * the other hand, prefer scrolls because (a) they're just as fast
+ * on many terminals and (b) using them avoids bouncing an
+ * unchanged bottom section of the screen up and down, which is
+ * visually nasty.
+ *
+ * (lav): added more cases, used dl/il when bot==maxy and in csr case.
+ *
+ * I used assumption that capabilities il/il1/dl/dl1 work inside
+ * changed scroll region not shifting screen contents outside of it.
+ * If there are any terminals behaving different way, it would be
+ * necessary to add some conditions to scroll_csr_forward/backward.
+ */
+
+/* Try to scroll up assuming given csr (miny, maxy). Returns ERR on failure */
+static int scroll_csr_forward(int n, int top, int bot, int miny, int maxy, chtype blank)
+{
+ int i;
+
+ if (n == 1 && scroll_forward && top == miny && bot == maxy)
+ {
+ GoTo(bot, 0);
+ UpdateAttrs(blank);
+ TPUTS_TRACE("scroll_forward");
+ tputs(scroll_forward, 0, _nc_outch);
+ }
+ else if (n == 1 && delete_line && bot == maxy)
+ {
+ GoTo(top, 0);
+ UpdateAttrs(blank);
+ TPUTS_TRACE("delete_line");
+ tputs(delete_line, 0, _nc_outch);
+ }
+ else if (parm_index && top == miny && bot == maxy)
+ {
+ GoTo(bot, 0);
+ UpdateAttrs(blank);
+ TPUTS_TRACE("parm_index");
+ tputs(tparm(parm_index, n, 0), n, _nc_outch);
+ }
+ else if (parm_delete_line && bot == maxy)
+ {
+ GoTo(top, 0);
+ UpdateAttrs(blank);
+ TPUTS_TRACE("parm_delete_line");
+ tputs(tparm(parm_delete_line, n, 0), n, _nc_outch);
+ }
+ else if (scroll_forward && top == miny && bot == maxy)
+ {
+ GoTo(bot, 0);
+ UpdateAttrs(blank);
+ for (i = 0; i < n; i++)
+ {
+ TPUTS_TRACE("scroll_forward");
+ tputs(scroll_forward, 0, _nc_outch);
+ }
+ }
+ else if (delete_line && bot == maxy)
+ {
+ GoTo(top, 0);
+ UpdateAttrs(blank);
+ for (i = 0; i < n; i++)
+ {
+ TPUTS_TRACE("delete_line");
+ tputs(delete_line, 0, _nc_outch);
+ }
+ }
+ else
+ return ERR;
+
+ return OK;
+}
+
+/* Try to scroll down assuming given csr (miny, maxy). Returns ERR on failure */
+/* n > 0 */
+static int scroll_csr_backward(int n, int top, int bot, int miny, int maxy, chtype blank)
+{
+ int i;
+
+ if (n == 1 && scroll_reverse && top == miny && bot == maxy)
+ {
+ GoTo(top, 0);
+ UpdateAttrs(blank);
+ TPUTS_TRACE("scroll_reverse");
+ tputs(scroll_reverse, 0, _nc_outch);
+ }
+ else if (n == 1 && insert_line && bot == maxy)
+ {
+ GoTo(top, 0);
+ UpdateAttrs(blank);
+ TPUTS_TRACE("insert_line");
+ tputs(insert_line, 0, _nc_outch);
+ }
+ else if (parm_rindex && top == miny && bot == maxy)
+ {
+ GoTo(top, 0);
+ UpdateAttrs(blank);
+ TPUTS_TRACE("parm_rindex");
+ tputs(tparm(parm_rindex, n, 0), n, _nc_outch);
+ }
+ else if (parm_insert_line && bot == maxy)
+ {
+ GoTo(top, 0);
+ UpdateAttrs(blank);
+ TPUTS_TRACE("parm_insert_line");
+ tputs(tparm(parm_insert_line, n, 0), n, _nc_outch);
+ }
+ else if (scroll_reverse && top == miny && bot == maxy)
+ {
+ GoTo(top, 0);
+ UpdateAttrs(blank);
+ for (i = 0; i < n; i++)
+ {
+ TPUTS_TRACE("scroll_reverse");
+ tputs(scroll_reverse, 0, _nc_outch);
+ }
+ }
+ else if (insert_line && bot == maxy)
+ {
+ GoTo(top, 0);
+ UpdateAttrs(blank);
+ for (i = 0; i < n; i++)
+ {
+ TPUTS_TRACE("insert_line");
+ tputs(insert_line, 0, _nc_outch);
+ }
+ }
+ else
+ return ERR;
+
+ return OK;
+}
+
+/* scroll by using delete_line at del and insert_line at ins */
+/* n > 0 */
+static int scroll_idl(int n, int del, int ins, chtype blank)
+{
+ int i;
+
+ if(!((parm_delete_line || delete_line) && (parm_insert_line || insert_line)))
+ return ERR;
+
+ GoTo(del, 0);
+ UpdateAttrs(blank);
+ if (n == 1 && delete_line)
+ {
+ TPUTS_TRACE("delete_line");
+ tputs(delete_line, 0, _nc_outch);
+ }
+ else if (parm_delete_line)
+ {
+ TPUTS_TRACE("parm_delete_line");
+ tputs(tparm(parm_delete_line, n, 0), n, _nc_outch);
+ }
+ else /* if (delete_line) */
+ {
+ for (i = 0; i < n; i++)
+ {
+ TPUTS_TRACE("delete_line");
+ tputs(delete_line, 0, _nc_outch);
+ }
+ }
+
+ GoTo(ins, 0);
+ UpdateAttrs(blank);
+ if (n == 1 && insert_line)
+ {
+ TPUTS_TRACE("insert_line");
+ tputs(insert_line, 0, _nc_outch);
+ }
+ else if (parm_insert_line)
+ {
+ TPUTS_TRACE("parm_insert_line");
+ tputs(tparm(parm_insert_line, n, 0), n, _nc_outch);
+ }
+ else /* if (insert_line) */
+ {
+ for (i = 0; i < n; i++)
+ {
+ TPUTS_TRACE("insert_line");
+ tputs(insert_line, 0, _nc_outch);
+ }
+ }
+
+ return OK;
+}
+
+int _nc_scrolln(int n, int top, int bot, int maxy)
+/* scroll region from top to bot by n lines */
+{
+ chtype blank=ClrBlank(stdscr);
+ int i;
+ bool cursor_saved=FALSE;
+ int res;
+
+ TR(TRACE_MOVE, ("mvcur_scrolln(%d, %d, %d, %d)", n, top, bot, maxy));
+
+#if USE_XMC_SUPPORT
+ /*
+ * If we scroll, we might remove a cookie.
+ */
+ if (magic_cookie_glitch > 0) {
+ return (ERR);
+ }
+#endif
+
+ if (n > 0) /* scroll up (forward) */
+ {
+ /*
+ * Explicitly clear if stuff pushed off top of region might
+ * be saved by the terminal.
+ */
+ if (non_dest_scroll_region || (memory_above && top == 0)) {
+ for (i = 0; i < n; i++)
+ {
+ GoTo(i, 0);
+ ClrToEOL(BLANK);
+ }
+ }
+
+ res = scroll_csr_forward(n, top, bot, 0, maxy, blank);
+
+ if (res == ERR && change_scroll_region)
+ {
+ if ((((n==1 && scroll_forward) || parm_index)
+ && (SP->_cursrow == bot || SP->_cursrow == bot-1))
+ && save_cursor && restore_cursor)
+ {
+ cursor_saved=TRUE;
+ TPUTS_TRACE("save_cursor");
+ tputs(save_cursor, 0, _nc_outch);
+ }
+ TPUTS_TRACE("change_scroll_region");
+ tputs(tparm(change_scroll_region, top, bot), 0, _nc_outch);
+ if (cursor_saved)
+ {
+ TPUTS_TRACE("restore_cursor");
+ tputs(restore_cursor, 0, _nc_outch);
+ }
+ else
+ {
+ SP->_cursrow = SP->_curscol = -1;
+ }
+
+ res = scroll_csr_forward(n, top, bot, top, bot, blank);
+
+ TPUTS_TRACE("change_scroll_region");
+ tputs(tparm(change_scroll_region, 0, maxy), 0, _nc_outch);
+ SP->_cursrow = SP->_curscol = -1;
+ }
+
+ if (res == ERR && _nc_idlok)
+ res = scroll_idl(n, top, bot-n+1, blank);
+ }
+ else /* (n < 0) - scroll down (backward) */
+ {
+ /*
+ * Do explicit clear to end of region if it's possible that the
+ * terminal might hold on to stuff we push off the end.
+ */
+ if (non_dest_scroll_region || (memory_below && bot == maxy))
+ {
+ if (bot == maxy && clr_eos)
+ {
+ GoTo(maxy + n, 0);
+ ClrToEOS(BLANK);
+ }
+ else if (clr_eol)
+ {
+ for (i = 0; i < -n; i++)
+ {
+ GoTo(maxy + n + i, 0);
+ ClrToEOL(BLANK);
+ }
+ }
+ }
+
+ res = scroll_csr_backward(-n, top, bot, 0, maxy, blank);
+
+ if (res == ERR && change_scroll_region)
+ {
+ if (top != 0 && (SP->_cursrow == top || SP->_cursrow == top-1)
+ && save_cursor && restore_cursor)
+ {
+ cursor_saved=TRUE;
+ TPUTS_TRACE("save_cursor");
+ tputs(save_cursor, 0, _nc_outch);
+ }
+ TPUTS_TRACE("change_scroll_region");
+ tputs(tparm(change_scroll_region, top, bot), 0, _nc_outch);
+ if (cursor_saved)
+ {
+ TPUTS_TRACE("restore_cursor");
+ tputs(restore_cursor, 0, _nc_outch);
+ }
+ else
+ {
+ SP->_cursrow = SP->_curscol = -1;
+ }
+
+ res = scroll_csr_backward(-n, top, bot, top, bot, blank);
+
+ TPUTS_TRACE("change_scroll_region");
+ tputs(tparm(change_scroll_region, 0, maxy), 0, _nc_outch);
+ SP->_cursrow = SP->_curscol = -1;
+ }
+
+ if (res == ERR && _nc_idlok)
+ res = scroll_idl(-n, bot+n+1, top, blank);
+ }
+
+ if (res == ERR)
+ return(ERR);
+
+ _nc_scroll_window(curscr, n, top, bot, blank);
+
+ return(OK);
+}
+
+
+void _nc_screen_resume()
+{
+ /* make sure terminal is in a sane known state */
+ SP->_current_attr = A_NORMAL;
+ newscr->_clear = TRUE;
+
+ if (SP->_coloron == TRUE && orig_pair)
+ putp(orig_pair);
+ if (exit_attribute_mode)
+ putp(exit_attribute_mode);
+ else
+ {
+ /* turn off attributes */
+ if (exit_alt_charset_mode)
+ putp(exit_alt_charset_mode);
+ if (exit_standout_mode)
+ putp(exit_standout_mode);
+ if (exit_underline_mode)
+ putp(exit_underline_mode);
+ }
+ if (exit_insert_mode)
+ putp(exit_insert_mode);
+ if (enter_am_mode && exit_am_mode)
+ putp(auto_right_margin ? enter_am_mode : exit_am_mode);
+}
+
+void _nc_screen_init()
+{
+ _nc_screen_resume();
+}
+
+/* wrap up screen handling */
+void _nc_screen_wrap()
+{
+ UpdateAttrs(A_NORMAL);
+}
+
+#if USE_XMC_SUPPORT
+void _nc_do_xmc_glitch(attr_t previous)
+{
+ attr_t chg = XMC_CHANGES(previous ^ SP->_current_attr);
+
+ while (chg != 0) {
+ if (chg & 1) {
+ SP->_curscol += magic_cookie_glitch;
+ if (SP->_curscol >= SP->_columns)
+ wrap_cursor();
+ T(("bumped to %d,%d after cookie", SP->_cursrow, SP->_curscol));
+ }
+ chg >>= 1;
+ }
+}
+#endif /* USE_XMC_SUPPORT */