[prev in list] [next in list] [prev in thread] [next in thread]
List: gentoo-user
Subject: Re: [gentoo-user] Soft scrolling on framebuffer consoles - with GPM handling - version of the patch
From: Alan Mackenzie <acm () muc ! de>
Date: 2024-04-04 8:05:29
Message-ID: Zg5fSSnksZ1W2_NV () ACM
[Download RAW message or body]
Hello, Peter.
On Mon, Mar 11, 2024 at 10:47:43 +0000, Peter Humphrey wrote:
> On Wednesday, 24 January 2024 12:20:29 GMT Alan Mackenzie wrote:
> > Hello, Gentoo.
> > On Wed, Jan 24, 2024 at 10:00:37 +0000, Alan Mackenzie wrote:
> > [ .... ]
> > Please note the corrected subject line. This version of the soft
> > scrolling patch is for kernel 6.6.13, or thereabouts.
> It works well here, Alan, up to kernel version 6.7.9, but one of the 15 or so
> new kernel parameters (since 6.7.8) seems to cause it to fail.
> I've attached the reject file, /usr/src/linux-6.8.0-gentoo/drivers/tty/vt/
> vt.c.rej.
Thanks!
I've had a look at this, now. The 6.6.13 patch appears to work on 6.7.x
(I tested it on 6.7.10 this morning).
For 6.8.1, a new patch is needed, see below. Remember, to apply it, you
need smething like:
$ patch -p1 < 6.8.1-GPS.20240402.diff
.. Have fun!
> --
> Regards,
> Peter.
["6.8.1-GPM.20240402.diff" (text/plain)]
diff --git a/drivers/tty/vt/selection.c b/drivers/tty/vt/selection.c
index 8967c3a0d916..f40ebbfb87de 100644
--- a/drivers/tty/vt/selection.c
+++ b/drivers/tty/vt/selection.c
@@ -217,7 +217,8 @@ static int vc_selection_store_chars(struct vc_data *vc, bool unicode)
unless non-space at end of line. */
if (obp != bp) {
bp = obp;
- *bp++ = '\r';
+ if ((i + 2) < vc_sel.end) /* Don't add \r to the last line. */
+ *bp++ = '\r';
}
obp = bp;
}
@@ -263,6 +264,11 @@ static int vc_do_selection(struct vc_data *vc, unsigned short mode, int ps,
new_sel_start = rounddown(ps, vc->vc_size_row);
new_sel_end = rounddown(pe, vc->vc_size_row) +
vc->vc_size_row - 2;
+ while ((new_sel_end > pe)
+ && (is_space_on_vt (sel_pos (new_sel_end, unicode))))
+ new_sel_end -= 2;
+ if (!((new_sel_end) % vc->vc_size_row))
+ new_sel_end += 2;
break;
case TIOCL_SELPOINTER:
highlight_pointer(pe);
diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index 38a765eadbe2..dd012e99e797 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -134,6 +134,11 @@ const struct consw *conswitchp;
#define DEFAULT_BELL_DURATION (HZ/8)
#define DEFAULT_CURSOR_BLINK_MS 200
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+static unsigned int console_soft_scrollback_size =
+ 1024 * CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_SIZE;
+#endif
+
struct vc vc_cons [MAX_NR_CONSOLES];
EXPORT_SYMBOL(vc_cons);
@@ -289,8 +294,31 @@ static inline bool con_should_update(const struct vc_data *vc)
static inline unsigned short *screenpos(const struct vc_data *vc, int offset,
bool viewed)
{
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ unsigned long softback_pos, scrolled_expanse;
+
+ if (vc->vc_softback_curr == vc->vc_origin) /* Should never happen! */
+ return (unsigned short *)(vc->vc_origin + offset);
+ else {
+ if (vc->vc_softback_in >= vc->vc_softback_curr)
+ scrolled_expanse = vc->vc_softback_in - vc->vc_softback_curr;
+ else
+ scrolled_expanse = vc->vc_softback_in - vc->vc_softback_curr
+ + vc->vc_softback_end - vc->vc_softback_buf;
+ if (offset >= scrolled_expanse)
+ return (unsigned short *)(vc->vc_origin
+ + (offset - scrolled_expanse));
+ else {
+ softback_pos = vc->vc_softback_curr + offset;
+ if (softback_pos >= vc->vc_softback_end)
+ softback_pos -= vc->vc_softback_end
+ - vc->vc_softback_buf;
+ }
+ }
+ return (unsigned short *)softback_pos;
+#else
unsigned short *p;
-
+
if (!viewed)
p = (unsigned short *)(vc->vc_origin + offset);
else if (!vc->vc_sw->con_screen_pos)
@@ -298,6 +326,7 @@ static inline unsigned short *screenpos(const struct vc_data *vc, int offset,
else
p = vc->vc_sw->con_screen_pos(vc, offset);
return p;
+#endif
}
/* Called from the keyboard irq path.. */
@@ -319,107 +348,111 @@ void schedule_console_callback(void)
* Code to manage unicode-based screen buffers
*/
-/*
- * Our screen buffer is preceded by an array of line pointers so that
- * scrolling only implies some pointer shuffling.
- */
+#define vc_uniscr_buf_end(vc) (vc->vc_uniscr_buf + vc->vc_uniscr_char_size)
-static u32 **vc_uniscr_alloc(unsigned int cols, unsigned int rows)
+static int vc_uniscr_alloc(struct vc_data *vc, unsigned int cols, unsigned int rows)
{
- u32 **uni_lines;
- void *p;
- unsigned int memsize, i, col_size = cols * sizeof(**uni_lines);
-
- /* allocate everything in one go */
- memsize = col_size * rows;
- memsize += rows * sizeof(*uni_lines);
- uni_lines = vzalloc(memsize);
- if (!uni_lines)
- return NULL;
-
- /* initial line pointers */
- p = uni_lines + rows;
- for (i = 0; i < rows; i++) {
- uni_lines[i] = p;
- p += col_size;
- }
+ uint32_t *p;
+ unsigned int new_size; /* In 32-bit characters */
- return uni_lines;
-}
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM
+ unsigned int num_scrollback_rows;
-static void vc_uniscr_free(u32 **uni_lines)
-{
- vfree(uni_lines);
+ num_scrollback_rows = (console_soft_scrollback_size / 2) / cols;
+ new_size = cols * (num_scrollback_rows + rows + 1);
+#else
+ new_size = cols * (rows + 1); /* 1 row for the circular buffer admin */
+#endif
+ p = (uint32_t *)vzalloc (sizeof (uint32_t) * new_size);
+ if (!p)
+ return -ENOMEM;
+ vc->vc_uniscr_buf = p;
+ vc->vc_uniscr_curr = p;
+ vc->vc_uniscr_char_size = new_size;
+ memset32(p, ' ', new_size); /* Probably redundant. */
+ return 0;
}
-static void vc_uniscr_set(struct vc_data *vc, u32 **new_uni_lines)
+static void vc_uniscr_free(struct vc_data *vc)
{
- vc_uniscr_free(vc->vc_uni_lines);
- vc->vc_uni_lines = new_uni_lines;
+ kvfree(vc->vc_uniscr_buf);
+ vc->vc_uniscr_buf = NULL;
}
static void vc_uniscr_putc(struct vc_data *vc, u32 uc)
{
- if (vc->vc_uni_lines)
- vc->vc_uni_lines[vc->state.y][vc->state.x] = uc;
+ uint32_t *pos;
+
+ if (vc->vc_uniscr_buf) {
+ pos = vc->vc_uniscr_curr;
+ UNISCR_PLUS(pos, vc->state.y * vc->vc_cols + vc->state.x);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM
+ UNISCR_MINUS(pos, vc->vc_cols * (vc->vc_softback_lines + vc->vc_top));
+#endif
+ *pos = uc;
+ }
}
static void vc_uniscr_insert(struct vc_data *vc, unsigned int nr)
{
- if (vc->vc_uni_lines) {
- u32 *ln = vc->vc_uni_lines[vc->state.y];
- unsigned int x = vc->state.x, cols = vc->vc_cols;
+ unsigned int x = vc->state.x, y = vc->state.y, cols = vc->vc_cols;
+ uint32_t *ln = vc->vc_uniscr_curr;
- memmove(&ln[x + nr], &ln[x], (cols - x - nr) * sizeof(*ln));
+ if (vc->vc_uniscr_buf) {
+ UNISCR_PLUS(ln, y * cols);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM
+ UNISCR_MINUS(ln, vc->vc_cols * (vc->vc_softback_lines + vc->vc_top));
+#endif
+ memmove(&ln[x + nr], &ln[x], (cols - x - nr) * sizeof(uint32_t));
memset32(&ln[x], ' ', nr);
}
}
static void vc_uniscr_delete(struct vc_data *vc, unsigned int nr)
{
- if (vc->vc_uni_lines) {
- u32 *ln = vc->vc_uni_lines[vc->state.y];
- unsigned int x = vc->state.x, cols = vc->vc_cols;
+ unsigned int x = vc->state.x, y = vc->state.y, cols = vc->vc_cols;
+ uint32_t *ln = vc->vc_uniscr_curr;
- memmove(&ln[x], &ln[x + nr], (cols - x - nr) * sizeof(*ln));
+ if (vc->vc_uniscr_buf) {
+ UNISCR_PLUS(ln, y * cols);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM
+ UNISCR_MINUS(ln, vc->vc_cols * (vc->vc_softback_lines + vc->vc_top));
+#endif
+ memmove(&ln[x], &ln[x + nr], (cols - x - nr) * sizeof(uint32_t));
memset32(&ln[cols - nr], ' ', nr);
}
}
+/* FIXME!!! We need to check that NR never goes beyond the current line end. !!! */
static void vc_uniscr_clear_line(struct vc_data *vc, unsigned int x,
unsigned int nr)
{
- if (vc->vc_uni_lines)
- memset32(&vc->vc_uni_lines[vc->state.y][x], ' ', nr);
+ if (vc->vc_uniscr_buf) {
+ uint32_t *ln = vc->vc_uniscr_curr;
+
+ UNISCR_PLUS(ln, vc->state.y * vc->vc_cols);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM
+ UNISCR_MINUS(ln, vc->vc_cols * (vc->vc_softback_lines + vc->vc_top));
+#endif
+ memset32(&ln[x], ' ', nr);
+ }
}
static void vc_uniscr_clear_lines(struct vc_data *vc, unsigned int y,
unsigned int nr)
{
- if (vc->vc_uni_lines)
- while (nr--)
- memset32(vc->vc_uni_lines[y++], ' ', vc->vc_cols);
-}
-
-/* juggling array rotation algorithm (complexity O(N), size complexity O(1)) */
-static void juggle_array(u32 **array, unsigned int size, unsigned int nr)
-{
- unsigned int gcd_idx;
-
- for (gcd_idx = 0; gcd_idx < gcd(nr, size); gcd_idx++) {
- u32 *gcd_idx_val = array[gcd_idx];
- unsigned int dst_idx = gcd_idx;
+ if (vc->vc_uniscr_buf) {
+ unsigned int cols = vc->vc_cols;
+ uint32_t *ln = vc->vc_uniscr_curr;
- while (1) {
- unsigned int src_idx = (dst_idx + nr) % size;
- if (src_idx == gcd_idx)
- break;
-
- array[dst_idx] = array[src_idx];
- dst_idx = src_idx;
+ UNISCR_PLUS(ln, y * cols);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM
+ UNISCR_MINUS(ln, vc->vc_cols * (vc->vc_softback_lines + vc->vc_top));
+#endif
+ while (nr--) {
+ memset32(ln, ' ', cols);
+ UNISCR_PLUS(ln, cols);
}
-
- array[dst_idx] = gcd_idx_val;
}
}
@@ -427,49 +460,52 @@ static void vc_uniscr_scroll(struct vc_data *vc, unsigned int top,
unsigned int bottom, enum con_scroll dir,
unsigned int nr)
{
- u32 **uni_lines = vc->vc_uni_lines;
- unsigned int size = bottom - top;
-
- if (!uni_lines)
- return;
-
- if (dir == SM_DOWN) {
- juggle_array(&uni_lines[top], size, size - nr);
- vc_uniscr_clear_lines(vc, top, nr);
- } else {
- juggle_array(&uni_lines[top], size, nr);
- vc_uniscr_clear_lines(vc, bottom - nr, nr);
- }
-}
-
-static void vc_uniscr_copy_area(u32 **dst_lines,
- unsigned int dst_cols,
- unsigned int dst_rows,
- u32 **src_lines,
- unsigned int src_cols,
- unsigned int src_top_row,
- unsigned int src_bot_row)
-{
- unsigned int dst_row = 0;
-
- if (!dst_lines)
- return;
-
- while (src_top_row < src_bot_row) {
- u32 *src_line = src_lines[src_top_row];
- u32 *dst_line = dst_lines[dst_row];
-
- memcpy(dst_line, src_line, src_cols * sizeof(*src_line));
- if (dst_cols - src_cols)
- memset32(dst_line + src_cols, ' ', dst_cols - src_cols);
- src_top_row++;
- dst_row++;
- }
- while (dst_row < dst_rows) {
- u32 *dst_line = dst_lines[dst_row];
-
- memset32(dst_line, ' ', dst_cols);
- dst_row++;
+ if (vc->vc_uniscr_buf) {
+ unsigned int cols = vc->vc_cols;
+ unsigned int sz, /* number of rows being scrolled */
+ d, /* number of rows needing blanking */
+ clear; /* The number of the topmost row needing blanking. */
+ uint32_t *dest, *src;
+ unsigned int i;
+
+ if (dir == SM_UP /* && top == 0 */&& bottom == vc->vc_rows) {
+ UNISCR_PLUS(vc->vc_uniscr_curr, nr * cols);
+ d = nr;
+ clear = vc->vc_rows - nr;
+ } else if (dir == SM_DOWN && top == 0 && bottom == vc->vc_rows - nr) {
+ UNISCR_MINUS(vc->vc_uniscr_curr, nr * cols);
+ d = nr;
+ clear = top;
+ } else if (dir == SM_UP) {
+ sz = bottom - top;
+ src = vc->vc_uniscr_curr;
+ UNISCR_PLUS(src, top * cols);
+ dest = src;
+ UNISCR_MINUS(dest, nr * cols);
+ i = bottom - top;
+ while (i--) {
+ memcpy(dest, src, cols * sizeof(uint32_t));
+ UNISCR_PLUS(src, cols);
+ UNISCR_PLUS(dest, cols);
+ }
+ d = nr;
+ clear = bottom - nr;
+ } else {
+ src = vc->vc_uniscr_curr;
+ UNISCR_PLUS(src, bottom * cols);
+ dest = src;
+ UNISCR_PLUS(dest, nr * cols);
+ i = bottom - top;
+ while (i--) {
+ UNISCR_MINUS(src, cols);
+ UNISCR_MINUS(dest, cols);
+ memcpy(dest, src, cols * sizeof(uint32_t));
+ }
+ d = nr;
+ clear = top;
+ }
+ if (d)
+ vc_uniscr_clear_lines(vc, clear, nr);
}
}
@@ -481,7 +517,6 @@ static void vc_uniscr_copy_area(u32 **dst_lines,
*/
int vc_uniscr_check(struct vc_data *vc)
{
- u32 **uni_lines;
unsigned short *p;
int x, y, mask;
@@ -490,11 +525,10 @@ int vc_uniscr_check(struct vc_data *vc)
if (!vc->vc_utf)
return -ENODATA;
- if (vc->vc_uni_lines)
+ if (vc->vc_uniscr_buf)
return 0;
- uni_lines = vc_uniscr_alloc(vc->vc_cols, vc->vc_rows);
- if (!uni_lines)
+ if (vc_uniscr_alloc (vc, vc->vc_cols, vc->vc_rows))
return -ENOMEM;
/*
@@ -506,15 +540,14 @@ int vc_uniscr_check(struct vc_data *vc)
p = (unsigned short *)vc->vc_origin;
mask = vc->vc_hi_font_mask | 0xff;
for (y = 0; y < vc->vc_rows; y++) {
- u32 *line = uni_lines[y];
+ uint32_t *line = vc->vc_uniscr_curr;
+ UNISCR_PLUS(line, y * vc->vc_cols);
for (x = 0; x < vc->vc_cols; x++) {
u16 glyph = scr_readw(p++) & mask;
line[x] = inverse_translate(vc, glyph, true);
}
}
- vc->vc_uni_lines = uni_lines;
-
return 0;
}
@@ -526,13 +559,24 @@ int vc_uniscr_check(struct vc_data *vc)
void vc_uniscr_copy_line(const struct vc_data *vc, void *dest, bool viewed,
unsigned int row, unsigned int col, unsigned int nr)
{
- u32 **uni_lines = vc->vc_uni_lines;
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ uint32_t *pos; /* Position in the unicode buffer of col/row */
+#else
int offset = row * vc->vc_size_row + col * 2;
- unsigned long pos;
+ unsigned long pos; /* Position in the main screen buffer of col/row */
+#endif
- if (WARN_ON_ONCE(!uni_lines))
+ if (WARN_ON_ONCE(!vc->vc_uniscr_buf))
return;
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ pos = vc->vc_uniscr_curr;
+ UNISCR_PLUS(pos, row * vc->vc_cols + col);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM
+ UNISCR_MINUS(pos, vc->vc_softback_lines * vc->vc_cols);
+#endif
+ memcpy(dest, pos, nr * sizeof(uint32_t));
+#else
pos = (unsigned long)screenpos(vc, offset, viewed);
if (pos >= vc->vc_origin && pos < vc->vc_scr_end) {
/*
@@ -542,24 +586,233 @@ void vc_uniscr_copy_line(const struct vc_data *vc, void *dest, bool viewed,
*/
row = (pos - vc->vc_origin) / vc->vc_size_row;
col = ((pos - vc->vc_origin) % vc->vc_size_row) / 2;
- memcpy(dest, &uni_lines[row][col], nr * sizeof(u32));
+ memcpy(dest,
+ (void *)(vc->vc_uniscr_curr + row * vc->vc_cols + col),
+ nr);
} else {
/*
- * Scrollback is active. For now let's simply backtranslate
- * the screen glyphs until the unicode screen buffer does
- * synchronize with console display drivers for a scrollback
- * buffer of its own.
+ * Scrollback is active. So hoik the unicode characters out
+ * of the unicode circular buffer.
*/
- u16 *p = (u16 *)pos;
- int mask = vc->vc_hi_font_mask | 0xff;
- u32 *uni_buf = dest;
- while (nr--) {
- u16 glyph = scr_readw(p++) & mask;
- *uni_buf++ = inverse_translate(vc, glyph, true);
+ /* CAN'T HAPPEN!!! (Hah hah!) */
+ }
+#endif
+}
+
+
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+static void con_update_softback(struct vc_data *vc)
+{
+ int l = console_soft_scrollback_size / vc->vc_size_row;
+ if (l > 5)
+ {
+ vc->vc_softback_end = vc->vc_softback_buf + l * vc->vc_size_row;
+ vc->vc_softback_top = vc->vc_softback_buf;
+ }
+ else
+ /* Smaller scrollback makes no sense, and 0 would screw
+ the operation totally */
+ vc->vc_softback_top = 0;
+}
+
+static int concon_set_origin(struct vc_data *vc)
+{
+ if (vc->vc_softback_lines)
+ concon_scrolldelta(vc, vc->vc_softback_lines);
+ return 0;
+}
+
+#define advance_row(p, delta) (unsigned short *)((unsigned long)(p) + (delta) * vc->vc_size_row)
+
+static void con_redraw_softback(struct vc_data *vc, /* struct display *p, */
+ long delta)
+{
+ int count = vc->vc_rows;
+ unsigned short *d, *s;
+ unsigned long n;
+ int line = 0;
+
+ if (!vc->vc_softback_lines)
+ vc->vc_char_at_pos = scr_readw((u16 *)vc->vc_pos);
+
+ d = (u16 *) vc->vc_softback_curr;
+ if (d == (u16 *) vc->vc_softback_in)
+ d = (u16 *) vc->vc_origin;
+ n = vc->vc_softback_curr + delta * vc->vc_size_row;
+ vc->vc_softback_lines -= delta;
+ if (delta < 0) {
+ if (vc->vc_softback_curr < vc->vc_softback_top
+ && n < vc->vc_softback_buf) {
+ n += vc->vc_softback_end - vc->vc_softback_buf;
+ if (n < vc->vc_softback_top) {
+ vc->vc_softback_lines -=
+ (vc->vc_softback_top - n) / vc->vc_size_row;
+ n = vc->vc_softback_top;
+ }
+ } else if (vc->vc_softback_curr >= vc->vc_softback_top
+ && n < vc->vc_softback_top) {
+ vc->vc_softback_lines -=
+ (vc->vc_softback_top - n) / vc->vc_size_row;
+ n = vc->vc_softback_top;
+ }
+ } else {
+ if (vc->vc_softback_curr > vc->vc_softback_in
+ && n >= vc->vc_softback_end) {
+ n += vc->vc_softback_buf - vc->vc_softback_end;
+ if (n > vc->vc_softback_in) {
+ n = vc->vc_softback_in;
+ vc->vc_softback_lines = 0;
+ }
+ } else if (vc->vc_softback_curr <= vc->vc_softback_in
+ && n > vc->vc_softback_in) {
+ n = vc->vc_softback_in;
+ vc->vc_softback_lines = 0;
}
}
+ if (n == vc->vc_softback_curr)
+ return;
+ vc->vc_softback_curr = n;
+ /* If we're not scrolled any more, restore the character to the cursor
+ * position */
+ if (!vc->vc_softback_lines)
+ scr_writew(vc->vc_char_at_pos, (u16 *)vc->vc_pos);
+ s = (u16 *) vc->vc_softback_curr;
+ if (s == (u16 *) vc->vc_softback_in)
+ s = (u16 *) vc->vc_origin;
+ while (count--) {
+ unsigned short *start;
+ unsigned short *le;
+ unsigned short c;
+ int x = 0;
+ unsigned short attr = 1;
+
+ start = s;
+ le = advance_row(s, 1);
+ /* Temporarily overwrite the character at the cursor position
+ * with the one we actually want to see on the screen. */
+ if (count == vc->vc_rows - vc->state.y - 1)
+ {
+ c = scr_readw((u16 *)(s + vc->state.x));
+ scr_writew(c, (u16 *)vc->vc_pos);
+ vc->vc_sw->con_putcs
+ (vc, (u16 *)vc->vc_pos, 1, line, vc->state.x);
+ }
+ do {
+ c = scr_readw(s);
+ if (attr != (c & 0xff00)) {
+ attr = c & 0xff00;
+ if (s > start) {
+ vc->vc_sw->con_putcs(
+ vc, start, s - start,
+ line, x);
+ x += s - start;
+ start = s;
+ }
+ }
+ if (c == scr_readw(d)) {
+ if (s > start) {
+ vc->vc_sw->con_putcs(
+ vc, start, s - start,
+ line, x);
+ x += s - start + 1;
+ start = s + 1;
+ } else {
+ x++;
+ start++;
+ }
+ }
+ s++;
+ d++;
+ } while (s < le);
+ if (s > start)
+ vc->vc_sw->con_putcs(vc, start, s - start, line, x);
+ line++;
+ if (d == (u16 *) vc->vc_softback_end)
+ d = (u16 *) vc->vc_softback_buf;
+ if (d == (u16 *) vc->vc_softback_in)
+ d = (u16 *) vc->vc_origin;
+ if (s == (u16 *) vc->vc_softback_end)
+ s = (u16 *) vc->vc_softback_buf;
+ if (s == (u16 *) vc->vc_softback_in)
+ s = (u16 *) vc->vc_origin;
+ }
}
+static inline void con_softback_note(struct vc_data *vc, int t,
+ int count)
+{
+ unsigned short *p;
+
+ if (vc->vc_num != fg_console)
+ return;
+ p = (unsigned short *) (vc->vc_origin + t * vc->vc_size_row);
+
+ while (count) {
+ scr_memcpyw((u16 *) vc->vc_softback_in, p, vc->vc_size_row);
+ count--;
+ p = advance_row(p, 1);
+ vc->vc_softback_in += vc->vc_size_row;
+ if (vc->vc_softback_in == vc->vc_softback_end)
+ vc->vc_softback_in = vc->vc_softback_buf;
+ if (vc->vc_softback_in == vc->vc_softback_top) {
+ vc->vc_softback_top += vc->vc_size_row;
+ if (vc->vc_softback_top == vc->vc_softback_end)
+ vc->vc_softback_top = vc->vc_softback_buf;
+ }
+ }
+ vc->vc_softback_curr = vc->vc_softback_in;
+}
+
+void concon_scrolldelta(struct vc_data *vc, int lines)
+{
+ /* struct display *disp = &fb_display[fg_console]; */
+ /* int offset, limit, scrollback_old; */
+
+ if (vc->vc_softback_top) {
+ if (vc->vc_num != fg_console)
+ return;
+ if (vc->vc_mode != KD_TEXT || !lines)
+ return;
+#if 0
+ if (logo_shown >= 0) {
+ struct vc_data *conp2 = vc_cons[logo_shown].d;
+
+ if (conp2->vc_top == logo_lines
+ && conp2->vc_bottom == conp2->vc_rows)
+ conp2->vc_top = 0;
+ if (logo_shown == vc->vc_num) {
+ unsigned long p, q;
+ int i;
+
+ p = vc->vc_softback_in;
+ q = vc->vc_origin +
+ logo_lines * vc->vc_size_row;
+ for (i = 0; i < logo_lines; i++) {
+ if (p == vc->vc_softback_top)
+ break;
+ if (p == vc->vc_softback_buf)
+ p = vc->vc_softback_end;
+ p -= vc->vc_size_row;
+ q -= vc->vc_size_row;
+ scr_memcpyw((u16 *) q, (u16 *) p,
+ vc->vc_size_row);
+ }
+ vc->vc_softback_in = vc->vc_softback_curr = p;
+ update_region(vc, vc->vc_origin,
+ logo_lines * vc->vc_cols);
+ }
+ logo_shown = FBCON_LOGO_CANSHOW;
+ }
+#endif
+ vc->vc_sw->con_cursor(vc, CM_ERASE /* | CM_SOFTBACK */);
+ con_redraw_softback(vc, /* disp, */ lines);
+ if (!vc->vc_softback_lines)
+ vc->vc_sw->con_cursor(vc, CM_DRAW /* | CM_SOFTBACK */);
+ }
+}
+
+#endif /* CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK */
+
static void con_scroll(struct vc_data *vc, unsigned int top,
unsigned int bottom, enum con_scroll dir,
unsigned int nr)
@@ -571,6 +824,10 @@ static void con_scroll(struct vc_data *vc, unsigned int top,
nr = rows - 1;
if (bottom > vc->vc_rows || top >= bottom || nr < 1)
return;
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ if (dir == SM_UP && vc->vc_softback_top)
+ con_softback_note (vc, top, nr);
+#endif
vc_uniscr_scroll(vc, top, bottom, dir, nr);
if (con_is_visible(vc) &&
@@ -588,6 +845,53 @@ static void con_scroll(struct vc_data *vc, unsigned int top,
scr_memsetw(clear, vc->vc_video_erase_char, vc->vc_size_row * nr);
}
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+static void do_update_region(struct vc_data *vc, unsigned long start, int count)
+{
+ unsigned int xx, yy, offset;
+ u16 *p;
+
+ unsigned long origin =
+ (start >= vc->vc_softback_buf && start < vc->vc_softback_end)
+ ? start >= vc->vc_softback_curr
+ ? vc->vc_softback_curr
+ : vc->vc_softback_curr
+ - (vc->vc_softback_end - vc->vc_softback_buf)
+ : vc->vc_origin - vc->vc_softback_lines * vc->vc_size_row;
+ p = (u16 *) start;
+ offset = (start - origin) / 2;
+ xx = offset % vc->vc_cols;
+ yy = offset / vc->vc_cols;
+
+ for(;;) {
+ u16 attrib = scr_readw(p) & 0xff00;
+ int startx = xx;
+ u16 *q = p;
+ while (xx < vc->vc_cols && count) {
+ if (attrib != (scr_readw(p) & 0xff00)) {
+ if (p > q)
+ vc->vc_sw->con_putcs(vc, q, p-q, yy, startx);
+ startx = xx;
+ q = p;
+ attrib = scr_readw(p) & 0xff00;
+ }
+ p++;
+ xx++;
+ count--;
+ }
+ if (p > q)
+ vc->vc_sw->con_putcs(vc, q, p-q, yy, startx);
+ if (p == (u16 *) vc->vc_softback_end)
+ p = (u16 *)vc->vc_softback_buf;
+ if (p == (u16 *) vc->vc_softback_in)
+ p = (u16 *)vc->vc_origin;
+ if (!count)
+ break;
+ xx = 0;
+ yy++;
+ }
+}
+#else
static void do_update_region(struct vc_data *vc, unsigned long start, int count)
{
unsigned int xx, yy, offset;
@@ -631,6 +935,7 @@ static void do_update_region(struct vc_data *vc, unsigned long start, int count)
}
}
}
+#endif
void update_region(struct vc_data *vc, unsigned long start, int count)
{
@@ -639,7 +944,10 @@ void update_region(struct vc_data *vc, unsigned long start, int count)
if (con_should_update(vc)) {
hide_cursor(vc);
do_update_region(vc, start, count);
- set_cursor(vc);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ if (!vc->vc_softback_lines)
+#endif
+ set_cursor(vc);
}
}
EXPORT_SYMBOL(update_region);
@@ -701,51 +1009,68 @@ static void update_attr(struct vc_data *vc)
}
/* Note: inverting the screen twice should revert to the original state */
+/* OFFSET is the offset in bytes (not 16-bit characters), COUNT is a byte
+ * count (not a character count). */
void invert_screen(struct vc_data *vc, int offset, int count, bool viewed)
{
unsigned short *p;
+ int row_offset, bytes_left_in_row;
+ int row_count;
WARN_CONSOLE_UNLOCKED();
count /= 2;
- p = screenpos(vc, offset, viewed);
- if (vc->vc_sw->con_invert_region) {
- vc->vc_sw->con_invert_region(vc, p, count);
- } else {
- u16 *q = p;
- int cnt = count;
- u16 a;
-
- if (!vc->vc_can_do_color) {
- while (cnt--) {
- a = scr_readw(q);
- a ^= 0x0800;
- scr_writew(a, q);
- q++;
- }
- } else if (vc->vc_hi_font_mask == 0x100) {
- while (cnt--) {
- a = scr_readw(q);
- a = (a & 0x11ff) |
- ((a & 0xe000) >> 4) |
- ((a & 0x0e00) << 4);
- scr_writew(a, q);
- q++;
- }
+ row_offset = offset;
+ bytes_left_in_row = vc->vc_size_row - (row_offset % vc->vc_size_row);
+ row_count = (count < bytes_left_in_row / 2)
+ ? count
+ : bytes_left_in_row / 2;
+
+ while (count) {
+ p = screenpos(vc, row_offset, viewed);
+ if (vc->vc_sw->con_invert_region) {
+ vc->vc_sw->con_invert_region(vc, p, row_count);
} else {
- while (cnt--) {
- a = scr_readw(q);
- a = (a & 0x88ff) |
- ((a & 0x7000) >> 4) |
- ((a & 0x0700) << 4);
- scr_writew(a, q);
- q++;
+ u16 *q = p;
+ int cnt = row_count;
+ u16 a;
+
+ if (!vc->vc_can_do_color) {
+ while (cnt--) {
+ a = scr_readw(q);
+ a ^= 0x0800;
+ scr_writew(a, q);
+ q++;
+ }
+ } else if (vc->vc_hi_font_mask == 0x100) {
+ while (cnt--) {
+ a = scr_readw(q);
+ a = (a & 0x11ff) |
+ ((a & 0xe000) >> 4) |
+ ((a & 0x0e00) << 4);
+ scr_writew(a, q);
+ q++;
+ }
+ } else {
+ while (cnt--) {
+ a = scr_readw(q);
+ a = (a & 0x88ff) |
+ ((a & 0x7000) >> 4) |
+ ((a & 0x0700) << 4);
+ scr_writew(a, q);
+ q++;
+ }
}
}
- }
- if (con_should_update(vc))
- do_update_region(vc, (unsigned long) p, count);
+ if (con_should_update(vc))
+ do_update_region(vc, (unsigned long) p, row_count);
+ row_offset += 2 * row_count;
+ count -= row_count;
+ row_count = (count >= vc->vc_cols)
+ ? vc->vc_cols
+ : count;
+ }
notify_update(vc);
}
@@ -875,8 +1200,17 @@ static void set_origin(struct vc_data *vc)
WARN_CONSOLE_UNLOCKED();
if (!con_is_visible(vc) ||
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ (
+ !concon_set_origin (vc) &&
+ (
+#endif
!vc->vc_sw->con_set_origin ||
- !vc->vc_sw->con_set_origin(vc))
+ !vc->vc_sw->con_set_origin(vc)
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ ))
+#endif
+ )
vc->vc_origin = (unsigned long)vc->vc_screenbuf;
vc->vc_visible_origin = vc->vc_origin;
vc->vc_scr_end = vc->vc_origin + vc->vc_screenbuf_size;
@@ -937,7 +1271,6 @@ void redraw_screen(struct vc_data *vc, int is_switch)
if (!vc) {
/* strange ... */
- /* printk("redraw_screen: tty %d not allocated ??\n", new_console+1); */
return;
}
@@ -952,7 +1285,6 @@ void redraw_screen(struct vc_data *vc, int is_switch)
hide_cursor(old_vc);
if (!con_is_visible(old_vc)) {
save_screen(old_vc);
- set_origin(old_vc);
}
if (tty0dev)
sysfs_notify(&tty0dev->kobj, NULL, "active");
@@ -965,7 +1297,6 @@ void redraw_screen(struct vc_data *vc, int is_switch)
int update;
int old_was_color = vc->vc_can_do_color;
- set_origin(vc);
update = vc->vc_sw->con_switch(vc);
set_palette(vc);
/*
@@ -980,9 +1311,19 @@ void redraw_screen(struct vc_data *vc, int is_switch)
}
if (update && vc->vc_mode != KD_GRAPHICS)
- do_update_region(vc, vc->vc_origin, vc->vc_screenbuf_size / 2);
+ do_update_region(vc,
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ vc->vc_softback_lines
+ ? vc->vc_softback_curr
+ :
+#endif
+ vc->vc_origin,
+ vc->vc_screenbuf_size / 2);
}
- set_cursor(vc);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ if (!vc->vc_softback_lines)
+#endif
+ set_cursor(vc);
if (is_switch) {
vt_set_leds_compute_shiftstate();
notify_update(vc);
@@ -1061,7 +1402,6 @@ int vc_allocate(unsigned int currcons) /* return 0 on success */
int err;
WARN_CONSOLE_UNLOCKED();
-
if (currcons >= MAX_NR_CONSOLES)
return -ENXIO;
@@ -1103,9 +1443,24 @@ int vc_allocate(unsigned int currcons) /* return 0 on success */
global_cursor_default = 1;
vc_init(vc, 1);
+ if (vc_uniscr_alloc (vc, vc->vc_cols, vc->vc_rows) != 0)
+ goto err_free;
vcs_make_sysfs(currcons);
atomic_notifier_call_chain(&vt_notifier_list, VT_ALLOCATE, ¶m);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ vc->vc_softback_size = console_soft_scrollback_size;
+ err = -ENOMEM;
+ vc->vc_softback_buf =
+ (unsigned long)vzalloc(console_soft_scrollback_size);
+ if (!vc->vc_softback_buf)
+ goto err_free;
+ vc->vc_softback_in = vc->vc_softback_top = vc->vc_softback_curr =
+ vc->vc_softback_buf;
+ vc->vc_softback_lines = 0;
+ con_update_softback(vc);
+ vc_uniscr_alloc(vc, vc->vc_cols, vc->vc_rows);
+#endif
return 0;
err_free:
visual_deinit(vc);
@@ -1126,6 +1481,75 @@ static inline int resize_screen(struct vc_data *vc, int width, int height,
return err;
}
+static int vc_copy_uniscr_to_new_area (struct vc_data *vc,
+ unsigned int new_cols,
+ unsigned int new_rows)
+{
+ unsigned int old_rows = vc->vc_rows, old_cols = vc->vc_cols;
+ uint32_t *old_uniscr_curr = vc->vc_uniscr_curr,
+ *old_uniscr_buf = vc->vc_uniscr_buf;
+ unsigned int old_uniscr_char_size = vc->vc_uniscr_char_size;
+ unsigned int new_lines;
+ unsigned int copy_cols;
+ uint32_t *dest, *src;
+ int res;
+
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM
+ unsigned int new_uniscr_rows;
+ unsigned int old_lines;
+ unsigned long tmp;
+
+ if (vc->vc_softback_in >= vc->vc_softback_top)
+ tmp = vc->vc_softback_in - vc->vc_softback_top;
+ else
+ tmp = vc->vc_softback_in - vc->vc_softback_top
+ + vc->vc_softback_end - vc->vc_softback_buf;
+ old_lines = tmp / vc->vc_size_row + old_rows;
+
+ if ((res = vc_uniscr_alloc(vc, new_cols, new_rows)) != 0)
+ return res;
+
+ new_uniscr_rows = vc->vc_uniscr_char_size / new_cols + new_rows;
+ new_lines = min(old_lines, new_uniscr_rows);
+ copy_cols = min(old_cols, new_cols);
+
+ dest = vc->vc_uniscr_curr;
+ if (new_lines > old_rows) {
+ dest -= (new_lines - old_rows) * new_cols;
+ while (dest < vc->vc_uniscr_buf) /* Could happen twice. */
+ dest += vc->vc_uniscr_char_size;
+ }
+ src = old_uniscr_curr;
+ if (new_lines > old_rows) {
+ src -= (new_lines - old_rows) * old_cols;
+ while (src < old_uniscr_buf)
+ src += old_uniscr_char_size;
+ }
+#else
+ if ((res = vc_uniscr_alloc(vc, new_cols, new_rows)) != 0)
+ return res;
+
+ new_lines = min(old_rows, new_rows);
+ copy_cols = min(old_cols, new_cols);
+ dest = vc->vc_uniscr_curr;
+ src = old_uniscr_curr;
+#endif
+ if (old_uniscr_buf) {
+ while (new_lines--) {
+ memcpy(dest, src, copy_cols * sizeof(uint32_t));
+ if (new_cols > old_cols)
+ memset32(dest + old_cols, ' ',
+ new_cols - old_cols);
+ UNISCR_PLUS(dest, new_cols);
+ src += old_cols;
+ if (src >= old_uniscr_buf + old_uniscr_char_size)
+ src -= old_uniscr_char_size;
+ }
+ kvfree(old_uniscr_buf);
+ }
+ return 0;
+}
+
/**
* vc_do_resize - resizing method for the tty
* @tty: tty being resized
@@ -1146,12 +1570,19 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc,
{
unsigned long old_origin, new_origin, new_scr_end, rlth, rrem, err = 0;
unsigned long end;
- unsigned int old_rows, old_row_size, first_copied_row;
- unsigned int new_cols, new_rows, new_row_size, new_screen_size;
+ unsigned int old_rows, old_size_row, first_copied_row;
+ unsigned int new_cols, new_rows, new_size_row, new_screen_size;
unsigned int user;
unsigned short *oldscreen, *newscreen;
- u32 **new_uniscr = NULL;
-
+ uint32_t *old_uniscr = vc->vc_uniscr_buf;
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ unsigned short *d;
+ unsigned long old_softback_buf, new_softback_buf, tmp;
+ unsigned long old_softback_end, new_softback_end;
+ unsigned int old_scrolled_rows, new_scrolled_rows;
+ unsigned int count, copied_scrolled_rows;
+ void *temp_new_softback_buf;
+#endif
WARN_CONSOLE_UNLOCKED();
if (!vc)
@@ -1165,8 +1596,8 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc,
new_cols = (cols ? cols : vc->vc_cols);
new_rows = (lines ? lines : vc->vc_rows);
- new_row_size = new_cols << 1;
- new_screen_size = new_row_size * new_rows;
+ new_size_row = new_cols << 1;
+ new_screen_size = new_size_row * new_rows;
if (new_cols == vc->vc_cols && new_rows == vc->vc_rows) {
/*
@@ -1194,61 +1625,91 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc,
if (!newscreen)
return -ENOMEM;
- if (vc->vc_uni_lines) {
- new_uniscr = vc_uniscr_alloc(new_cols, new_rows);
- if (!new_uniscr) {
- kfree(newscreen);
- return -ENOMEM;
- }
- }
-
if (vc_is_sel(vc))
clear_selection();
old_rows = vc->vc_rows;
- old_row_size = vc->vc_size_row;
+ old_size_row = vc->vc_size_row;
err = resize_screen(vc, new_cols, new_rows, user);
if (err) {
kfree(newscreen);
- vc_uniscr_free(new_uniscr);
+ vc_uniscr_free(vc);
+ vc->vc_uniscr_buf = old_uniscr;
return err;
}
- vc->vc_rows = new_rows;
- vc->vc_cols = new_cols;
- vc->vc_size_row = new_row_size;
- vc->vc_screenbuf_size = new_screen_size;
-
- rlth = min(old_row_size, new_row_size);
- rrem = new_row_size - rlth;
+ rlth = min(old_size_row, new_size_row);
+ rrem = new_size_row - rlth;
old_origin = vc->vc_origin;
new_origin = (long) newscreen;
new_scr_end = new_origin + new_screen_size;
if (vc->state.y > new_rows) {
- if (old_rows - vc->state.y < new_rows) {
+ if (old_rows - new_rows < vc->vc_top + vc->state.y) {
+ if (old_rows - new_rows > vc->vc_top) {
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ con_softback_note(vc, vc->vc_top,
+ old_rows - new_rows - vc->vc_top);
+#endif
+ vc_uniscr_scroll(vc, 0, old_rows, SM_UP,
+ old_rows - new_rows - vc->vc_top);
+ }
/*
* Cursor near the bottom, copy contents from the
* bottom of buffer
*/
first_copied_row = (old_rows - new_rows);
} else {
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ con_softback_note(vc, vc->vc_top,
+ vc->state.y - new_rows/2);
+#endif
+ vc_uniscr_scroll(vc, 0, old_rows, SM_UP,
+ vc->state.y - new_rows/2);
/*
* Cursor is in no man's land, copy 1/2 screenful
* from the top and bottom of cursor position
*/
first_copied_row = (vc->state.y - new_rows/2);
}
- old_origin += first_copied_row * old_row_size;
+ old_origin += first_copied_row * old_size_row;
} else
first_copied_row = 0;
- end = old_origin + old_row_size * min(old_rows, new_rows);
+ end = old_origin + old_size_row * min(old_rows, new_rows);
- vc_uniscr_copy_area(new_uniscr, new_cols, new_rows,
- vc->vc_uni_lines, rlth/2, first_copied_row,
- min(old_rows, new_rows));
- vc_uniscr_set(vc, new_uniscr);
+ if ((err = vc_copy_uniscr_to_new_area(vc, new_cols, new_rows)) != 0)
+ return err;
+
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ concon_set_origin(vc);
+ old_softback_buf = vc->vc_softback_buf;
+ old_softback_end = vc->vc_softback_end;
+ vc->vc_softback_size = console_soft_scrollback_size;
+ temp_new_softback_buf = vzalloc(console_soft_scrollback_size);
+ new_softback_buf = (unsigned long)temp_new_softback_buf;
+ if (!new_softback_buf)
+ return -ENOMEM;
+ new_softback_end = new_softback_buf + console_soft_scrollback_size;
+ d = (unsigned short *)new_softback_buf;
+ while (d != (u16 *)new_softback_end) {
+ scr_writew (0x0020, d);
+ d++;
+ }
+ if (vc->vc_softback_top <= vc->vc_softback_in)
+ tmp = vc->vc_softback_in - vc->vc_softback_top;
+ else
+ tmp = vc->vc_softback_in - vc->vc_softback_top
+ + vc->vc_softback_end - vc->vc_softback_buf;
+ old_scrolled_rows = tmp / vc->vc_size_row;
+ new_scrolled_rows = console_soft_scrollback_size / new_size_row;
+ copied_scrolled_rows = min(old_scrolled_rows, new_scrolled_rows);
+#endif
+
+ vc->vc_cols = new_cols;
+ vc->vc_rows = new_rows;
+ vc->vc_size_row = new_size_row;
+ vc->vc_screenbuf_size = new_screen_size;
update_attr(vc);
@@ -1258,17 +1719,60 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc,
if (rrem)
scr_memsetw((void *)(new_origin + rlth),
vc->vc_video_erase_char, rrem);
- old_origin += old_row_size;
- new_origin += new_row_size;
+ old_origin += old_size_row;
+ new_origin += new_size_row;
}
if (new_scr_end > new_origin)
scr_memsetw((void *)new_origin, vc->vc_video_erase_char,
new_scr_end - new_origin);
+
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ new_origin = new_softback_buf;
+ if (copied_scrolled_rows) {
+ old_origin = vc->vc_softback_in
+ - copied_scrolled_rows * old_size_row;
+ if (old_origin < vc->vc_softback_buf)
+ old_origin += vc->vc_softback_end
+ - vc->vc_softback_buf;
+ count = copied_scrolled_rows;
+
+ while (count--) {
+ scr_memcpyw((unsigned short *) new_origin,
+ (unsigned short *) old_origin, rlth);
+ if (rrem)
+ scr_memsetw((void *)(new_origin + rlth),
+ vc->vc_video_erase_char, rrem);
+ old_origin += old_size_row;
+ if (old_origin >= old_softback_end)
+ old_origin -= old_softback_end
+ - old_softback_buf;
+ new_origin += new_size_row;
+ }
+ }
+#endif
oldscreen = vc->vc_screenbuf;
vc->vc_screenbuf = newscreen;
vc->vc_screenbuf_size = new_screen_size;
set_origin(vc);
kfree(oldscreen);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ vc->vc_softback_buf = new_softback_buf;
+ vc->vc_softback_end = new_softback_buf
+ + new_scrolled_rows * new_size_row;
+ if (copied_scrolled_rows) {
+ if (new_origin >= vc->vc_softback_end)
+ new_origin -= vc->vc_softback_end - vc->vc_softback_buf;
+ vc->vc_softback_in = new_origin;
+ } else
+ vc->vc_softback_in = new_softback_buf;
+ vc->vc_softback_top = new_softback_buf;
+ if (copied_scrolled_rows
+ && (new_origin == new_softback_buf))
+ vc->vc_softback_top += new_size_row;
+ vc->vc_softback_curr = vc->vc_softback_in;
+ vc->vc_softback_lines = 0; /* Probably redundant. */
+ kvfree((void *)old_softback_buf);
+#endif
/* do part of a reset_terminal() */
vc->vc_top = 0;
@@ -1350,8 +1854,8 @@ struct vc_data *vc_deallocate(unsigned int currcons)
visual_deinit(vc);
con_free_unimap(vc);
put_pid(vc->vt_pid);
- vc_uniscr_set(vc, NULL);
kfree(vc->vc_screenbuf);
+ vc->vc_uniscr_buf = NULL;
vc_cons[currcons].d = NULL;
}
return vc;
@@ -1600,7 +2104,7 @@ struct rgb { u8 r; u8 g; u8 b; };
static void rgb_from_256(int i, struct rgb *c)
{
- if (i < 8) { /* Standard colours. */
+ if (i < 8) { /* Standard colours. */
c->r = i&1 ? 0xaa : 0x00;
c->g = i&2 ? 0xaa : 0x00;
c->b = i&4 ? 0xaa : 0x00;
@@ -1612,7 +2116,7 @@ static void rgb_from_256(int i, struct rgb *c)
c->r = (i - 16) / 36 * 85 / 2;
c->g = (i - 16) / 6 % 6 * 85 / 2;
c->b = (i - 16) % 6 * 85 / 2;
- } else /* Grayscale ramp. */
+ } else /* Grayscale ramp. */
c->r = c->g = c->b = i * 10 - 2312;
}
@@ -2882,6 +3386,12 @@ static int do_con_write(struct tty_struct *tty, const u8 *buf, int count)
param.vc = vc;
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ /* Undo any soft scrolling - <Alt><Fn> and <Shift><PgUp/Down> do
+ not pass through this function. */
+ concon_set_origin (vc);
+#endif
+
while (!tty->flow.stopped && count) {
int orig = *buf;
buf++;
@@ -3047,11 +3557,8 @@ static void vt_console_print(struct console *co, const char *b, unsigned count)
if (kmsg_console && vc_cons_allocated(kmsg_console - 1))
vc = vc_cons[kmsg_console - 1].d;
- if (!vc_cons_allocated(fg_console)) {
- /* impossible */
- /* printk("vt_console_print: tty %d not allocated ??\n", currcons+1); */
+ if (!vc_cons_allocated(fg_console))
goto quit;
- }
if (vc->vc_mode != KD_TEXT)
goto quit;
@@ -3096,7 +3603,11 @@ static void vt_console_print(struct console *co, const char *b, unsigned count)
}
if (cnt && con_is_visible(vc))
vc->vc_sw->con_putcs(vc, start, cnt, vc->state.y, start_x);
- set_cursor(vc);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ if (!vc->vc_softback_lines)
+#endif
+ set_cursor(vc);
+
notify_update(vc);
quit:
@@ -3320,7 +3831,11 @@ static void con_flush_chars(struct tty_struct *tty)
/* if we race with con_close(), vt may be null */
console_lock();
vc = tty->driver_data;
- if (vc)
+ if (vc
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ && !vc->vc_softback_lines
+#endif
+ )
set_cursor(vc);
console_unlock();
}
@@ -3341,6 +3856,14 @@ static int con_install(struct tty_driver *driver, struct tty_struct *tty)
vc = vc_cons[currcons].d;
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ con_update_softback(vc);
+ if (!vc->vc_uniscr_buf)
+ ret = vc_uniscr_alloc(vc, vc->vc_cols, vc->vc_rows);
+ if (ret)
+ goto unlock;
+#endif
+
/* Still being freed */
if (vc->port.tty) {
ret = -ERESTARTSYS;
@@ -3396,7 +3919,7 @@ static void con_cleanup(struct tty_struct *tty)
tty_port_put(&vc->port);
}
-static int default_color = 7; /* white */
+static int default_color = 7; /* white */
static int default_italic_color = 2; // green (ASCII)
static int default_underline_color = 3; // cyan (ASCII)
module_param_named(color, default_color, int, S_IRUGO | S_IWUSR);
@@ -3475,6 +3998,7 @@ static int __init con_init(void)
/* Assuming vc->vc_{cols,rows,screenbuf_size} are sane here. */
vc->vc_screenbuf = kzalloc(vc->vc_screenbuf_size, GFP_NOWAIT);
vc_init(vc, currcons || !vc->vc_sw->con_save_screen);
+ vc_uniscr_alloc(vc, vc->vc_cols, vc->vc_rows);
}
currcons = fg_console = 0;
master_display_fg = vc = vc_cons[currcons].d;
@@ -4088,7 +4612,7 @@ static int do_register_con_driver(const struct consw *csw, int first, int last)
con_driver->desc = desc;
con_driver->node = i;
con_driver->flag = CON_DRIVER_FLAG_MODULE |
- CON_DRIVER_FLAG_INIT;
+ CON_DRIVER_FLAG_INIT;
con_driver->first = first;
con_driver->last = last;
retval = 0;
@@ -4385,7 +4909,10 @@ void do_unblank_screen(int leaving_gfx)
if (console_blank_hook)
console_blank_hook(0);
set_palette(vc);
- set_cursor(vc);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ if (!vc->vc_softback_lines)
+#endif
+ set_cursor(vc);
vt_event_post(VT_EVENT_UNBLANK, vc->vc_num, vc->vc_num);
}
EXPORT_SYMBOL(do_unblank_screen);
@@ -4698,12 +5225,17 @@ EXPORT_SYMBOL_GPL(screen_glyph);
u32 screen_glyph_unicode(const struct vc_data *vc, int n)
{
- u32 **uni_lines = vc->vc_uni_lines;
+ int y = n / vc->vc_cols, x = n % vc->vc_cols;
+ uint32_t *ln = vc->vc_uniscr_curr;
- if (uni_lines)
- return uni_lines[n / vc->vc_cols][n % vc->vc_cols];
-
- return inverse_translate(vc, screen_glyph(vc, n * 2), true);
+ if (vc->vc_uniscr_curr) {
+ UNISCR_PLUS(ln, y * vc->vc_cols);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM
+ UNISCR_MINUS(ln, vc->vc_softback_lines * vc->vc_cols);
+#endif
+ return ln[x];
+ }
+ return inverse_translate(vc, screen_glyph(vc, n * 2), 1);
}
EXPORT_SYMBOL_GPL(screen_glyph_unicode);
diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig
index bc31db6ef7d2..dc76e4852347 100644
--- a/drivers/video/console/Kconfig
+++ b/drivers/video/console/Kconfig
@@ -101,6 +101,44 @@ config FRAMEBUFFER_CONSOLE_LEGACY_ACCELERATION
If unsure, select n.
+config FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ bool "Enable Scrollback Buffer in System RAM"
+ depends on FB=y && FRAMEBUFFER_CONSOLE
+ default y
+ help
+ This option creates a scrollback buffer for each framebuffer console.
+ These buffers are allocated dynamically during initialisation.
+
+ If you want this feature, say 'Y' here and enter in
+ FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_SIZE the amount of RAM to
+ allocate for each buffer. If unsure, say 'N'.
+
+config FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_SIZE
+ int "Scrollback Buffer Size (in KB)"
+ depends on FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ range 1 1024
+ default "128"
+ help
+ Enter the amount of System RAM in kilobytes to allocate for the
+ scrollback buffer of each framebuffer console. Each character
+ position on the video takes 2 bytes of storage. 128kB will give you
+ approximately four 240x67 screenfuls of scrollback buffer.
+
+config FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM
+ bool "Enable a working GPM for scrolled back scrollback buffer in System RAM"
+ depends on FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ default y
+ help
+ This option buffers up Unicode characters corresponding to the glyphs
+ displayed by the scrollback buffer. This enables the GPM mouse driver
+ (or similar) to copy characters from a scrolled back buffer.
+
+ A buffer is created for each framebuffer console, this buffer being
+ approximately twice as big as the buffer size specified in
+ FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_SIZE.
+
+ If unsure, say 'Y'.
+
config FRAMEBUFFER_CONSOLE_DETECT_PRIMARY
bool "Map the console to the primary display device"
depends on FRAMEBUFFER_CONSOLE
diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c
index 46823c2e2ba1..e95244e01c94 100644
--- a/drivers/video/fbdev/core/fbcon.c
+++ b/drivers/video/fbdev/core/fbcon.c
@@ -611,6 +611,28 @@ static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info,
erase,
vc->vc_size_row * logo_lines);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM
+ {
+ uint32_t *s = vc->vc_uniscr_curr, *d = vc->vc_uniscr_curr;
+ unsigned int i = vc->vc_rows - logo_lines;
+
+ UNISCR_PLUS(d, vc->vc_rows * vc->vc_cols);
+ UNISCR_PLUS(s, (vc->vc_rows - logo_lines) * vc->vc_cols);
+ while (i--) {
+ UNISCR_MINUS(d, vc->vc_cols);
+ UNISCR_MINUS(s, vc->vc_cols);
+ memcpy(d, s, vc->vc_cols * sizeof (uint32_t));
+ }
+ i = logo_lines;
+ d = vc->vc_uniscr_curr;
+ while (i--) {
+ memset32(d, ' ', vc->vc_cols);
+ UNISCR_PLUS(d, vc->vc_cols);
+ }
+ vc->vc_uniscr_curr = d;
+ }
+#endif
+
if (con_is_visible(vc) && vc->vc_mode == KD_TEXT) {
fbcon_clear_margins(vc, 0);
update_screen(vc);
@@ -1258,6 +1280,25 @@ static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height,
* bitmap stretched into the margin area.
*/
fbcon_clear_margins(vc, 0);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM
+ {
+ uint32_t *s = vc->vc_uniscr_curr, *d = vc->vc_uniscr_curr;
+ unsigned int i = vc->vc_rows - logo_lines;
+
+ UNISCR_PLUS(d, (vc->vc_rows - logo_lines) * vc->vc_cols);
+ UNISCR_PLUS(s, (vc->vc_rows - 2 * logo_lines) * vc->vc_cols);
+ while (i--) {
+ UNISCR_MINUS(d, vc->vc_cols);
+ UNISCR_MINUS(s, vc->vc_cols);
+ memcpy (d, s, vc->vc_cols * sizeof (uint32_t));
+ }
+ i = logo_lines;
+ while (i--) {
+ UNISCR_MINUS(d, vc->vc_cols);
+ memset32(d, ' ', vc->vc_cols);
+ }
+ }
+#endif
}
/* Split blits that cross physical y_wrap boundary */
@@ -2064,6 +2105,7 @@ static int fbcon_switch(struct vc_data *vc)
struct fbcon_ops *ops;
struct fbcon_display *p = &fb_display[vc->vc_num];
struct fb_var_screeninfo var;
+ unsigned short *d, *s;
int i, ret, prev_console;
info = fbcon_info_from_console(vc->vc_num);
@@ -2073,8 +2115,21 @@ static int fbcon_switch(struct vc_data *vc)
struct vc_data *conp2 = vc_cons[logo_shown].d;
if (conp2->vc_top == logo_lines
- && conp2->vc_bottom == conp2->vc_rows)
+ && conp2->vc_bottom == conp2->vc_rows) {
+ /* Scroll the bottom part of the screen up to fill the
+ * logo lines. */
+ i = conp2->vc_bottom - conp2->vc_top;
+ d = (unsigned short *)conp2->vc_origin;
+ s = (unsigned short *)(conp2->vc_origin + logo_lines * conp2->vc_size_row);
+ while (i--) {
+ scr_memcpyw(d, s, conp2->vc_size_row);
+ d += conp2->vc_cols;
+ s += conp2->vc_cols;
+ }
+ scr_memsetw(d, conp2->vc_video_erase_char,
+ conp2->vc_size_row * logo_lines);
conp2->vc_top = 0;
+ }
logo_shown = FBCON_LOGO_CANSHOW;
}
@@ -3162,6 +3217,9 @@ static const struct consw fb_con = {
.con_font_get = fbcon_get_font,
.con_font_default = fbcon_set_def_font,
.con_set_palette = fbcon_set_palette,
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ .con_scrolldelta = concon_scrolldelta,
+#endif
.con_invert_region = fbcon_invert_region,
.con_screen_pos = fbcon_screen_pos,
.con_getxy = fbcon_getxy,
diff --git a/include/linux/console_struct.h b/include/linux/console_struct.h
index 539f1cd45309..9d87a3fa6ed3 100644
--- a/include/linux/console_struct.h
+++ b/include/linux/console_struct.h
@@ -109,6 +109,17 @@ struct vc_data {
unsigned short *vc_screenbuf; /* In-memory character/attribute buffer */
unsigned int vc_screenbuf_size;
unsigned char vc_mode; /* KD_TEXT, ... */
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ unsigned int vc_softback_size; /* Size in bytes of scrollback buffer. */
+ unsigned long vc_softback_buf; /* Address of scrollback buffer. */
+ unsigned long vc_softback_end; /* (Just past) end of buffer. */
+ unsigned long vc_softback_in; /* Head pointer into circular buffer. */
+ unsigned long vc_softback_top; /* Tail pointer into circular buffer. */
+ unsigned long vc_softback_curr; /* Pos in vc_screenbuf or vc_softback_buf
+ corresponding to visible screen. */
+ int vc_softback_lines; /* Number of lines currently scrolled. */
+ unsigned short vc_char_at_pos; /* Char at vc_pos when no soft scroll */
+#endif
/* attributes for all characters on screen */
unsigned char vc_attr; /* Current attributes */
unsigned char vc_def_color; /* Default colors */
@@ -158,7 +169,9 @@ struct vc_data {
struct vc_data **vc_display_fg; /* [!] Ptr to var holding fg console for this display */
struct uni_pagedict *uni_pagedict;
struct uni_pagedict **uni_pagedict_loc; /* [!] Location of uni_pagedict variable for this console */
- u32 **vc_uni_lines; /* unicode screen content */
+ uint32_t *vc_uniscr_buf; /* Address of unicode screen content */
+ unsigned int vc_uniscr_char_size; /* Size of *vc-uniscr_buf in 32-bit chars */
+ uint32_t *vc_uniscr_curr; /* Pos of first char of (unscrolled) screen */
/* additional information is in vt_kern.h */
};
@@ -193,4 +206,22 @@ extern void vc_SAK(struct work_struct *work);
bool con_is_visible(const struct vc_data *vc);
+/* Macros for wraparound in the uniscr buffer. POS must be a uint32_t pointer
+ * variable which is expected to be within the confines of vc->vc_uniscr_buf
+ * and vc->vc_uniscr_buf + vc->vc_uniscr_char_size. OFFSET must be a positive
+ * distance measured in uint32_t's, which when added to or subtracted from POS
+ * won't be too far away from the buffer. */
+#define UNISCR_PLUS(pos,offset) \
+ do { \
+ pos += offset; \
+ if (pos >= vc->vc_uniscr_buf + vc->vc_uniscr_char_size) \
+ pos -= vc->vc_uniscr_char_size; \
+ } while (0)
+#define UNISCR_MINUS(pos,offset) \
+ do { \
+ pos -= offset; \
+ if (pos < vc->vc_uniscr_buf) \
+ pos += vc->vc_uniscr_char_size; \
+ } while (0)
+
#endif /* _LINUX_CONSOLE_STRUCT_H */
diff --git a/include/linux/vt_kern.h b/include/linux/vt_kern.h
index c1f5aebef170..97ecca06eceb 100644
--- a/include/linux/vt_kern.h
+++ b/include/linux/vt_kern.h
@@ -114,6 +114,9 @@ int con_copy_unimap(struct vc_data *dst_vc, struct vc_data *src_vc)
/* vt.c */
void vt_event_post(unsigned int event, unsigned int old, unsigned int new);
int vt_waitactive(int n);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+void concon_scrolldelta(struct vc_data *vc, int lines);
+#endif
void change_console(struct vc_data *new_vc);
void reset_vc(struct vc_data *vc);
int do_unbind_con_driver(const struct consw *csw, int first, int last,
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic