Software scrollback for fbcon [PATCH]

Jakub Jelinek (jj@sunsite.ms.mff.cuni.cz)
Thu, 31 Dec 1998 10:49:18 +0100 (CET)


Hi!

The scrollback some fbcon consoles had so far (only those using ypan/ywrap
with at least twice as much memory as current resolution ate) has several
problems, the most important is that you cannot cut and paste in it (I guess
the most important feature one needs from the scrollback).
So, I've implemented software scrollback for fbcon, which should work on all
fbcon consoles and records every line scrolled out in during scrolling up
(unlike VGAcon scrollback, where only lines scrolled out of the top row are
remembered).
By default it eats 32K of your memory, but you can turn it off (by
giving video=scrollback:0 argument to the kernel, or you can play with
larger scrollback, e.g. video=scrollback:64k). The scrollback is shared
among fb consoles and is cleared on every console switch (IMHO it would be a
security problem if it did not work like that). The number of lines
remembered depends on the actual number of columns you have at the current
console, e.g. 32k give me on my 160 column display 102 lines of scrollback
(every character takes 2 bytes).
This patch should not slow down scrolling up noticably, as the cost of a
scroll-up by 1 line is in addition to what has been done so far (ie.
a lot of tampering with hardware) is just one memcpy of the size twice the
number of columns you have.
As a side effect, the patch moves logos back to the left and uses only
redraw when logo is shown, so that no flickering occurs. You don't need to
use any hacks like the one in current kernel, as if you want, Shift+PgUp
will show you anything scrolled away.
Eventually, it would be possible to add support on some framebuffers, so
that this scrollback buffer sits in the video memory, but I don't know if
that's worth of that.
Also, I might try to code support for changing the size of the scrollback
buffer during runtime using fbset.
This patch seems to work for me quite well both on Creator and Mach64.
The patch is against vger CVS, but I hope it will apply cleanly against
2.2.0-pre*, as AFAIK all fbcon stuff goes through vger these days.
Enjoy and if you'll have any problems, let me know.

diff -u -r1.110 -r1.111
--- linux/drivers/char/console.c 1998/11/08 11:14:22 1.110
+++ linux/drivers/char/console.c 1998/12/28 11:19:34 1.111
@@ -203,7 +203,14 @@

static inline unsigned short *screenpos(int currcons, int offset, int viewed)
{
- unsigned short *p = (unsigned short *)(visible_origin + offset);
+ unsigned short *p;
+
+ if (!viewed)
+ p = (unsigned short *)(origin + offset);
+ else if (!sw->con_screen_pos)
+ p = (unsigned short *)(visible_origin + offset);
+ else
+ p = sw->con_screen_pos(vc_cons[currcons].d, offset);
return p;
}

@@ -253,16 +260,16 @@
unsigned int xx, yy, offset;
u16 *p;

- if (start < origin) {
- count -= origin - start;
- start = origin;
- }
- if (count <= 0)
- return;
- offset = (start - origin) / 2;
- xx = offset % video_num_columns;
- yy = offset / video_num_columns;
p = (u16 *) start;
+ if (!sw->con_getxy) {
+ offset = (start - origin) / 2;
+ xx = offset % video_num_columns;
+ yy = offset / video_num_columns;
+ } else {
+ int nxx, nyy;
+ start = sw->con_getxy(vc_cons[currcons].d, start, &nxx, &nyy);
+ xx = nxx; yy = nyy;
+ }
for(;;) {
u16 attrib = scr_readw(p) & 0xff00;
int startx = xx;
@@ -285,6 +292,10 @@
break;
xx = 0;
yy++;
+ if (sw->con_getxy) {
+ p = (u16 *)start;
+ start = sw->con_getxy(vc_cons[currcons].d, start, NULL, NULL);
+ }
}
#endif
}
diff -u -r1.22 -r1.23
--- linux/drivers/char/vc_screen.c 1998/08/26 10:26:59 1.22
+++ linux/drivers/char/vc_screen.c 1998/12/28 11:19:31 1.23
@@ -89,6 +89,7 @@
long p = *ppos;
long viewed, attr, size, read;
char *buf0;
+ int col, maxcol;
unsigned short *org = NULL;

attr = (currcons & 128);
@@ -111,10 +112,19 @@
count = size - p;

buf0 = buf;
+ maxcol = video_num_columns;
if (!attr) {
org = screen_pos(currcons, p, viewed);
- while (count-- > 0)
+ col = p % maxcol;
+ p += maxcol - col;
+ while (count-- > 0) {
put_user(vcs_scr_readw(currcons, org++) & 0xff, buf++);
+ if (++col == maxcol) {
+ org = screen_pos(currcons, p, viewed);
+ col = 0;
+ p += maxcol;
+ }
+ }
} else {
if (p < HEADER_SIZE) {
char header[HEADER_SIZE];
@@ -124,20 +134,35 @@
while (p < HEADER_SIZE && count > 0)
{ count--; put_user(header[p++], buf++); }
}
+ p -= HEADER_SIZE;
+ col = (p/2) % maxcol;
if (count > 0) {
- p -= HEADER_SIZE;
- org = screen_pos(currcons, p/2, viewed);
- if ((p & 1) && count > 0)
+ org = screen_pos(currcons, p/2, viewed);
+ if ((p & 1) && count > 0) {
+ count--;
#ifdef __BIG_ENDIAN
- { count--; put_user(vcs_scr_readw(currcons, org++) & 0xff, buf++); }
+ put_user(vcs_scr_readw(currcons, org++) & 0xff, buf++);
#else
- { count--; put_user(vcs_scr_readw(currcons, org++) >> 8, buf++); }
+ put_user(vcs_scr_readw(currcons, org++) >> 8, buf++);
#endif
+ p++;
+ if (++col == maxcol) {
+ org = screen_pos(currcons, p/2, viewed);
+ col = 0;
+ }
+ }
+ p /= 2;
+ p += maxcol - col;
}
while (count > 1) {
put_user(vcs_scr_readw(currcons, org++), (unsigned short *) buf);
buf += 2;
count -= 2;
+ if (++col == maxcol) {
+ org = screen_pos(currcons, p, viewed);
+ col = 0;
+ p += maxcol;
+ }
}
if (count > 0)
#ifdef __BIG_ENDIAN
@@ -159,6 +184,7 @@
long p = *ppos;
long viewed, attr, size, written;
const char *buf0;
+ int col, maxcol;
u16 *org0 = NULL, *org = NULL;

attr = (currcons & 128);
@@ -181,14 +207,22 @@
count = size - p;

buf0 = buf;
+ maxcol = video_num_columns;
if (!attr) {
org0 = org = screen_pos(currcons, p, viewed);
+ col = p % maxcol;
+ p += maxcol - col;
while (count > 0) {
unsigned char c;
count--;
get_user(c, (const unsigned char*)buf++);
vcs_scr_writew(currcons, (vcs_scr_readw(currcons, org) & 0xff00) | c, org);
org++;
+ if (++col == maxcol) {
+ org = screen_pos(currcons, p, viewed);
+ col = 0;
+ p += maxcol;
+ }
}
} else {
if (p < HEADER_SIZE) {
@@ -199,8 +233,9 @@
if (!viewed)
putconsxy(currcons, header+2);
}
+ p -= HEADER_SIZE;
+ col = (p/2) % maxcol;
if (count > 0) {
- p -= HEADER_SIZE;
org0 = org = screen_pos(currcons, p/2, viewed);
if ((p & 1) && count > 0) {
char c;
@@ -214,7 +249,14 @@
(vcs_scr_readw(currcons, org) & 0xff), org);
#endif
org++;
+ p++;
+ if (++col == maxcol) {
+ org = screen_pos(currcons, p/2, viewed);
+ col = 0;
+ }
}
+ p /= 2;
+ p += maxcol - col;
}
while (count > 1) {
unsigned short w;
@@ -222,6 +264,11 @@
vcs_scr_writew(currcons, w, org++);
buf += 2;
count -= 2;
+ if (++col == maxcol) {
+ org = screen_pos(currcons, p, viewed);
+ col = 0;
+ p += maxcol;
+ }
}
if (count > 0) {
unsigned char c;
diff -u -r1.87 -r1.90
--- linux/drivers/video/fbcon.c 1998/12/15 08:36:17 1.87
+++ linux/drivers/video/fbcon.c 1998/12/30 14:50:36 1.90
@@ -26,7 +26,7 @@
*
* Hardware cursor support added by Emmanuel Marty (core@ggi-project.org)
* Smart redraw scrolling, arbitrary font width support, 512char font support
- * added by
+ * and software scrollback added by
* Jakub Jelinek (jj@ultra.linux.cz)
*
* Random hacking by Martin Mares <mj@ucw.cz>
@@ -111,6 +111,12 @@
struct display fb_display[MAX_NR_CONSOLES];
static int logo_lines;
static int logo_shown = -1;
+/* Software scrollback */
+extern int fbcon_softback_size;
+static unsigned long softback_buf, softback_curr;
+static unsigned long softback_in;
+static unsigned long softback_top, softback_end;
+static int softback_lines;

#define REFCOUNT(fd) (((int *)(fd))[-1])
#define FNTSIZE(fd) (((int *)(fd))[-2])
@@ -118,7 +124,12 @@
#define FNTSUM(fd) (((int *)(fd))[-4])
#define FONT_EXTRA_WORDS 4

-static void fbcon_free_font(struct display *p);
+#define CM_SOFTBACK (8)
+
+#define advance_row(p, delta) (unsigned short *)((unsigned long)(p) + (delta) * conp->vc_size_row)
+
+static void fbcon_free_font(struct display *);
+static int fbcon_set_origin(struct vc_data *);

/*
* Emmanuel: fbcon will now use a hardware cursor if the
@@ -422,6 +433,27 @@
logo = 0;

p->var.xoffset = p->var.yoffset = p->yscroll = 0; /* reset wrap/pan */
+
+ if (con == fg_console && p->type != FB_TYPE_TEXT) {
+ if (fbcon_softback_size) {
+ if (!softback_buf) {
+ softback_buf = (unsigned long)kmalloc(fbcon_softback_size, GFP_KERNEL);
+ if (!softback_buf) {
+ fbcon_softback_size = 0;
+ softback_top = 0;
+ }
+ }
+ } else {
+ if (softback_buf) {
+ kfree((void *)softback_buf);
+ softback_buf = 0;
+ softback_top = 0;
+ }
+ }
+ if (softback_buf)
+ softback_in = softback_top = softback_curr = softback_buf;
+ softback_lines = 0;
+ }

for (i = 0; i < MAX_NR_CONSOLES; i++)
if (i != con && fb_display[i].fb_info == p->fb_info &&
@@ -578,6 +610,17 @@
logo_shown = -2;
conp->vc_top = logo_lines;
}
+
+ if (con == fg_console && softback_buf) {
+ int l = fbcon_softback_size / conp->vc_size_row;
+ if (l > 5)
+ softback_end = softback_buf + l * conp->vc_size_row;
+ else {
+ /* Smaller scrollback makes no sense, and 0 would screw
+ the operation totally */
+ softback_top = 0;
+ }
+ }
}


@@ -702,17 +745,29 @@
{
int unit = conp->vc_num;
struct display *p = &fb_display[unit];
+ int y = conp->vc_y;
+
+ if (mode & CM_SOFTBACK) {
+ mode &= ~CM_SOFTBACK;
+ if (softback_lines) {
+ if (y + softback_lines >= conp->vc_rows)
+ mode = CM_ERASE;
+ else
+ y += softback_lines;
+ }
+ } else if (softback_lines)
+ fbcon_set_origin(conp);

/* do we have a hardware cursor ? */
if (p->dispsw->cursor) {
p->cursor_x = conp->vc_x;
- p->cursor_y = conp->vc_y;
+ p->cursor_y = y;
p->dispsw->cursor(p, mode, p->cursor_x, real_y(p, p->cursor_y));
return;
}

/* Avoid flickering if there's no real change. */
- if (p->cursor_x == conp->vc_x && p->cursor_y == conp->vc_y &&
+ if (p->cursor_x == conp->vc_x && p->cursor_y == y &&
(mode == CM_ERASE) == !cursor_on)
return;

@@ -721,7 +776,7 @@
p->dispsw->revc(p, p->cursor_x, real_y(p, p->cursor_y));

p->cursor_x = conp->vc_x;
- p->cursor_y = conp->vc_y;
+ p->cursor_y = y;

switch (mode) {
case CM_ERASE:
@@ -835,6 +890,94 @@
scrollback_current = 0;
}

+static void fbcon_redraw_softback(struct vc_data *conp, struct display *p, long delta)
+{
+ unsigned short *d, *s;
+ unsigned long n;
+ int line = 0;
+ int count = conp->vc_rows;
+
+ d = (u16 *)softback_curr;
+ if (d == (u16 *)softback_in)
+ d = (u16 *)conp->vc_origin;
+ n = softback_curr + delta * conp->vc_size_row;
+ softback_lines -= delta;
+ if (delta < 0) {
+ if (softback_curr < softback_top && n < softback_buf) {
+ n += softback_end - softback_buf;
+ if (n < softback_top) {
+ softback_lines -= (softback_top - n) / conp->vc_size_row;
+ n = softback_top;
+ }
+ } else if (softback_curr >= softback_top && n < softback_top) {
+ softback_lines -= (softback_top - n) / conp->vc_size_row;
+ n = softback_top;
+ }
+ } else {
+ if (softback_curr > softback_in && n >= softback_end) {
+ n += softback_buf - softback_end;
+ if (n > softback_in) {
+ n = softback_in;
+ softback_lines = 0;
+ }
+ } else if (softback_curr <= softback_in && n > softback_in) {
+ n = softback_in;
+ softback_lines = 0;
+ }
+ }
+ if (n == softback_curr)
+ return;
+ softback_curr = n;
+ s = (u16 *)softback_curr;
+ if (s == (u16 *)softback_in)
+ s = (u16 *)conp->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);
+ do {
+ c = scr_readw(s);
+ if (attr != (c & 0xff00)) {
+ attr = c & 0xff00;
+ if (s > start) {
+ p->dispsw->putcs(conp, p, start, s - start,
+ real_y(p, line), x);
+ x += s - start;
+ start = s;
+ }
+ }
+ if (c == scr_readw(d)) {
+ if (s > start) {
+ p->dispsw->putcs(conp, p, start, s - start,
+ real_y(p, line), x);
+ x += s - start + 1;
+ start = s + 1;
+ } else {
+ x++;
+ start++;
+ }
+ }
+ s++;
+ d++;
+ } while (s < le);
+ if (s > start)
+ p->dispsw->putcs(conp, p, start, s - start, real_y(p, line), x);
+ line++;
+ if (d == (u16 *)softback_end)
+ d = (u16 *)softback_buf;
+ if (d == (u16 *)softback_in)
+ d = (u16 *)conp->vc_origin;
+ if (s == (u16 *)softback_end)
+ s = (u16 *)softback_buf;
+ if (s == (u16 *)softback_in)
+ s = (u16 *)conp->vc_origin;
+ }
+}

static void fbcon_redraw(struct vc_data *conp, struct display *p,
int line, int count, int offset)
@@ -845,8 +988,7 @@

while (count--) {
unsigned short *start = s;
- unsigned short *le = (unsigned short *)
- ((unsigned long)s + conp->vc_size_row);
+ unsigned short *le = advance_row(s, 1);
unsigned short c;
int x = 0;
unsigned short attr = 1;
@@ -939,13 +1081,32 @@
}
}

+static inline void fbcon_softback_note(struct vc_data *conp, int t, int count)
+{
+ unsigned short *p = (unsigned short *)
+ (conp->vc_origin + t * conp->vc_size_row);
+ while (count) {
+ scr_memcpyw((u16 *)softback_in, p, conp->vc_size_row);
+ count--;
+ p = advance_row(p, 1);
+ softback_in += conp->vc_size_row;
+ if (softback_in == softback_end)
+ softback_in = softback_buf;
+ if (softback_in == softback_top) {
+ softback_top += conp->vc_size_row;
+ if (softback_top == softback_end)
+ softback_top = softback_buf;
+ }
+ }
+ softback_curr = softback_in;
+}
+
static int fbcon_scroll(struct vc_data *conp, int t, int b, int dir,
int count)
{
int unit = conp->vc_num;
struct display *p = &fb_display[unit];
int scroll_partial = !(p->scrollmode & __SCROLL_YNOPARTIAL);
- int logos_left = 0; int logos_width = conp->vc_cols;

if (!p->can_soft_blank && console_blanked)
return 0;
@@ -954,7 +1115,7 @@
return 0;

fbcon_cursor(conp, CM_ERASE);
-
+
/*
* ++Geert: Only use ywrap/ypan if the console is in text mode
* ++Andrew: Only use ypan on hardware text mode when scrolling the
@@ -963,34 +1124,25 @@

switch (dir) {
case SM_UP:
- /* K.Garloff@ping.de, 98/10/21: If logo is diplayed, only save logo
- * and not the hole top region. In combination with the logo being
- * displayed on the right side, this allows scrollback feature
- * when bootlogo is displayed. */
- if (t != 0 && logo_shown == fg_console) {
- struct display *p = &fb_display[unit];
- int lw = (smp_num_cpus * (LOGO_W + 8) - 7) / fontwidth(p) + 1;
- logos_left = conp->vc_cols - lw;
- while (logos_left < 0) logos_left += (LOGO_W + 8 - 7) / fontwidth(p);
- logos_width = conp->vc_cols - logos_left;
- }
if (count > conp->vc_rows) /* Maximum realistic size */
count = conp->vc_rows;
+ if (softback_top)
+ fbcon_softback_note(conp, t, count);
+ if (logo_shown >= 0) goto redraw_up;
switch (p->scrollmode & __SCROLL_YMASK) {
case __SCROLL_YMOVE:
- if (t > 0) p->dispsw->bmove(p, 0, logos_left, count,
- logos_left, t, logos_width);
- p->dispsw->bmove(p, count, 0, 0, 0, b-count,
+ p->dispsw->bmove(p, t+count, 0, t, 0, b-t-count,
conp->vc_cols);
- p->dispsw->clear(conp, p, b-count, 0, count,
+ p->dispsw->clear(conp, p, b-count, 0, count,
conp->vc_cols);
break;

case __SCROLL_YWRAP:
- if (b-t-count > 2*conp->vc_rows/3) {
+ if (b-t-count > 3*conp->vc_rows>>2) {
if (t > 0)
- fbcon_bmove(conp, 0, logos_left, count, logos_left, t, logos_width);
- ywrap_up(unit, conp, p, count);
+ fbcon_bmove(conp, 0, 0, count, 0, t,
+ conp->vc_cols);
+ ywrap_up(unit, conp, p, count);
if (conp->vc_rows-b > 0)
fbcon_bmove(conp, b-count, 0, b, 0,
conp->vc_rows-b, conp->vc_cols);
@@ -1006,8 +1158,8 @@
if (( !scroll_partial && (b-t == conp->vc_rows)) ||
( scroll_partial && (b-t-count > 3*conp->vc_rows>>2))) {
if (t > 0)
- fbcon_bmove(conp, 0, logos_left, count, logos_left, t,
- logos_width);
+ fbcon_bmove(conp, 0, 0, count, 0, t,
+ conp->vc_cols);
ypan_up(unit, conp, p, count);
if (conp->vc_rows-b > 0)
fbcon_bmove(conp, b-count, 0, b, 0,
@@ -1111,7 +1263,7 @@
(sx <= p->cursor_x) && (p->cursor_x < sx+width)) ||
((dy <= p->cursor_y) && (p->cursor_y < dy+height) &&
(dx <= p->cursor_x) && (p->cursor_x < dx+width)))
- fbcon_cursor(conp, CM_ERASE);
+ fbcon_cursor(conp, CM_ERASE|CM_SOFTBACK);

/* Split blits that cross physical y_wrap case.
* Pathological case involves 4 blits, better to use recursive
@@ -1161,6 +1313,21 @@
struct display *p = &fb_display[unit];
struct fb_info *info = p->fb_info;

+ if (softback_top) {
+ int l = fbcon_softback_size / conp->vc_size_row;
+ if (softback_lines)
+ fbcon_set_origin(conp);
+ softback_top = softback_curr = softback_in = softback_buf;
+ softback_lines = 0;
+
+ if (l > 5)
+ softback_end = softback_buf + l * conp->vc_size_row;
+ else {
+ /* Smaller scrollback makes no sense, and 0 would screw
+ the operation totally */
+ softback_top = 0;
+ }
+ }
if (logo_shown >= 0) {
struct vc_data *conp2 = vc_cons[logo_shown].d;

@@ -1319,6 +1486,9 @@
return -ENXIO;
}

+ if (CON_IS_VISIBLE(p->conp) && softback_lines)
+ fbcon_set_origin(p->conp);
+
resize = (w != fontwidth(p)) || (h != fontheight(p));
if (p->userfont)
old_data = p->fontdata;
@@ -1379,6 +1549,7 @@
fbcon_font_widths(p);

if (resize) {
+ struct vc_data *conp = p->conp;
/* reset wrap/pan */
p->var.xoffset = p->var.yoffset = p->yscroll = 0;
p->vrows = p->var.yres_virtual/h;
@@ -1386,6 +1557,16 @@
p->vrows--;
updatescrollmode(p);
vc_resize_con( p->var.yres/h, p->var.xres/w, unit );
+ if (CON_IS_VISIBLE(conp) && softback_buf) {
+ int l = fbcon_softback_size / conp->vc_size_row;
+ if (l > 5)
+ softback_end += l * conp->vc_size_row;
+ else {
+ /* Smaller scrollback makes no sense, and 0 would screw
+ the operation totally */
+ softback_top = 0;
+ }
+ }
} else if (CON_IS_VISIBLE(p->conp) && vt_cons[unit]->vc_mode == KD_TEXT) {
if (p->dispsw->clear_margins)
p->dispsw->clear_margins(p->conp, p, 0);
@@ -1420,7 +1601,7 @@
int w = op->width;
int h = op->height;
int size = h;
- int i, j, k;
+ int i, k;
u8 *new_data, *data = op->data, *p;

#ifdef CONFIG_FBCON_FONTWIDTH8_ONLY
@@ -1462,6 +1643,7 @@
}
} else if (w <= 24) {
for (i = 0; i < op->charcount; i++) {
+ int j;
for (j = 0; j < h; j++) {
memcpy(p, data, 3);
p[3] = 0;
@@ -1572,10 +1754,119 @@
return p->fb_info->fbops->fb_set_cmap(&palette_cmap, 1, unit, p->fb_info);
}

+static u16 *fbcon_screen_pos(struct vc_data *conp, int offset)
+{
+ int line;
+ unsigned long p;
+
+ if (conp->vc_num != fg_console || !softback_lines)
+ return (u16 *)(conp->vc_origin + offset);
+ line = offset / conp->vc_size_row;
+ if (line >= softback_lines)
+ return (u16 *)(conp->vc_origin + offset - softback_lines * conp->vc_size_row);
+ p = softback_curr + offset;
+ if (p >= softback_end)
+ p += softback_buf - softback_end;
+ return (u16 *)p;
+}
+
+static unsigned long fbcon_getxy(struct vc_data *conp, unsigned long pos, int *px, int *py)
+{
+ int x, y;
+ unsigned long ret;
+ if (pos >= conp->vc_origin && pos < conp->vc_scr_end) {
+ unsigned long offset = (pos - conp->vc_origin) / 2;
+
+ x = offset % conp->vc_cols;
+ y = offset / conp->vc_cols;
+ if (conp->vc_num == fg_console)
+ y += softback_lines;
+ ret = pos + (conp->vc_cols - x) * 2;
+ } else if (conp->vc_num == fg_console && softback_lines) {
+ unsigned long offset = (pos - softback_curr) / 2;
+
+ x = offset % conp->vc_cols;
+ y = offset / conp->vc_cols;
+ if (pos < softback_curr)
+ y += (softback_end - softback_buf) / conp->vc_size_row;
+ ret = pos + (conp->vc_cols - x) * 2;
+ if (ret == softback_end)
+ ret = softback_buf;
+ if (ret == softback_in)
+ ret = conp->vc_origin;
+ } else {
+ /* Should not happen */
+ x = y = 0;
+ ret = conp->vc_origin;
+ }
+ if (px) *px = x;
+ if (py) *py = y;
+ return ret;
+}
+
+/* As we might be inside of softback, we may work with non-contiguous buffer,
+ that's why we have to use a separate routine. */
+static void fbcon_invert_region(struct vc_data *conp, u16 *p, int cnt)
+{
+ while (cnt--) {
+ if (!conp->vc_can_do_color)
+ *p++ ^= 0x0800;
+ else if (conp->vc_hi_font_mask == 0x100) {
+ u16 a = *p;
+ a = ((a) & 0x11ff) | (((a) & 0xe000) >> 4) | (((a) & 0x0e00) << 4);
+ *p++ = a;
+ } else {
+ u16 a = *p;
+ a = ((a) & 0x88ff) | (((a) & 0x7000) >> 4) | (((a) & 0x0700) << 4);
+ *p++ = a;
+ }
+ if (p == (u16 *)softback_end)
+ p = (u16 *)softback_buf;
+ if (p == (u16 *)softback_in)
+ p = (u16 *)conp->vc_origin;
+ }
+}
+
static int fbcon_scrolldelta(struct vc_data *conp, int lines)
{
int unit, offset, limit, scrollback_old;
struct display *p;
+
+ unit = fg_console;
+ p = &fb_display[unit];
+ if (softback_top) {
+ if (conp->vc_num != unit)
+ return 0;
+ if (vt_cons[unit]->vc_mode != KD_TEXT || !lines)
+ return 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 == unit) {
+ unsigned long p, q;
+ int i;
+
+ p = softback_in;
+ q = conp->vc_origin + logo_lines * conp->vc_size_row;
+ for (i = 0; i < logo_lines; i++) {
+ if (p == softback_top) break;
+ if (p == softback_buf) p = softback_end;
+ p -= conp->vc_size_row;
+ q -= conp->vc_size_row;
+ scr_memcpyw((u16 *)q, (u16 *)p, conp->vc_size_row);
+ }
+ softback_in = p;
+ update_region(unit, conp->vc_origin, logo_lines * conp->vc_cols);
+ }
+ logo_shown = -1;
+ }
+ fbcon_cursor(conp, CM_ERASE|CM_SOFTBACK);
+ fbcon_redraw_softback(conp, p, lines);
+ fbcon_cursor(conp, CM_DRAW|CM_SOFTBACK);
+ return 0;
+ }

if (!scrollback_phys_max)
return -ENOSYS;
@@ -1589,8 +1880,6 @@
if (scrollback_current == scrollback_old)
return 0;

- unit = fg_console;
- p = &fb_display[unit];
if (!p->can_soft_blank &&
(console_blanked || vt_cons[unit]->vc_mode != KD_TEXT || !lines))
return 0;
@@ -1619,6 +1908,13 @@
return 0;
}

+static int fbcon_set_origin(struct vc_data *conp)
+{
+ if (softback_lines && !console_blanked)
+ fbcon_scrolldelta(conp, softback_lines);
+ return 0;
+}
+
static inline unsigned safe_shift(unsigned d,int n)
{
return n<0 ? d>>-n : d<<n;
@@ -1691,8 +1987,8 @@
logo_depth = 1;
}

- for (x = p->var.xres - LOGO_W; x > 0 && x > (int)p->var.xres
- - smp_num_cpus * (LOGO_W + 8); x -= (LOGO_W + 8)) {
+ for (x = 0; x < smp_num_cpus * (LOGO_W + 8) &&
+ x < p->var.xres - (LOGO_W + 8); x += (LOGO_W + 8)) {

#if defined(CONFIG_FBCON_CFB16) || defined(CONFIG_FBCON_CFB24) || \
defined(CONFIG_FBCON_CFB32) || defined(CONFIG_FB_SBUS)
@@ -1925,10 +2221,12 @@
con_font_op: fbcon_font_op,
con_set_palette: fbcon_set_palette,
con_scrolldelta: fbcon_scrolldelta,
- con_set_origin: NULL,
+ con_set_origin: fbcon_set_origin,
con_save_screen: NULL,
con_build_attr: NULL,
- con_invert_region: NULL,
+ con_invert_region: fbcon_invert_region,
+ con_screen_pos: fbcon_screen_pos,
+ con_getxy: fbcon_getxy,
};


diff -u -r1.14 -r1.15
--- linux/drivers/video/fbmem.c 1998/11/20 12:17:35 1.14
+++ linux/drivers/video/fbmem.c 1998/12/28 11:19:40 1.15
@@ -183,6 +183,7 @@

struct fb_info *registered_fb[FB_MAX];
int num_registered_fb = 0;
+int fbcon_softback_size = 32768;

char con2fb_map[MAX_NR_CONSOLES];

@@ -656,6 +657,21 @@
if (!options || !*options)
return;

+ if (!strncmp(options, "scrollback:", 11)) {
+ options += 11;
+ if (*options) {
+ fbcon_softback_size = simple_strtoul(options, &options, 0);
+ if (*options == 'k' || *options == 'K') {
+ fbcon_softback_size *= 1024;
+ options++;
+ }
+ if (*options != ',')
+ return;
+ options++;
+ } else
+ return;
+ }
+
if (!strncmp(options, "map:", 4)) {
options += 4;
if (*options)
@@ -666,7 +682,7 @@
}
return;
}
-
+
if (!strncmp(options, "vc:", 3)) {
options += 3;
if (*options)
diff -u -r1.27 -r1.28
--- linux/include/linux/console.h 1998/11/22 19:55:42 1.27
+++ linux/include/linux/console.h 1998/12/28 11:19:44 1.28
@@ -42,6 +42,8 @@
void (*con_save_screen)(struct vc_data *);
u8 (*con_build_attr)(struct vc_data *, u8, u8, u8, u8, u8);
void (*con_invert_region)(struct vc_data *, u16 *, int);
+ u16 *(*con_screen_pos)(struct vc_data *, int);
+ unsigned long (*con_getxy)(struct vc_data *, unsigned long, int *, int *);
};

extern struct consw *conswitchp;

Cheers,
Jakub
___________________________________________________________________
Jakub Jelinek | jj@sunsite.mff.cuni.cz | http://sunsite.mff.cuni.cz
Administrator of SunSITE Czech Republic, MFF, Charles University
___________________________________________________________________
UltraLinux | http://ultra.linux.cz/ | http://ultra.penguin.cz/
Linux version 2.1.130 on a sparc64 machine (3958.37 BogoMips)
___________________________________________________________________

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/