From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from lists.gentoo.org (pigeon.gentoo.org [208.92.234.80]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by finch.gentoo.org (Postfix) with ESMTPS id BA11915800F for ; Thu, 26 Jan 2023 20:28:47 +0000 (UTC) Received: from pigeon.gentoo.org (localhost [127.0.0.1]) by pigeon.gentoo.org (Postfix) with SMTP id 125EEE07A5; Thu, 26 Jan 2023 20:28:41 +0000 (UTC) Received: from mx3.muc.de (mx3.muc.de [193.149.48.5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by pigeon.gentoo.org (Postfix) with ESMTPS id 1CC1AE077F for ; Thu, 26 Jan 2023 20:28:39 +0000 (UTC) Received: (qmail 57528 invoked by uid 3782); 26 Jan 2023 21:28:37 +0100 Received: from acm.muc.de (p4fe15d7e.dip0.t-ipconnect.de [79.225.93.126]) (using STARTTLS) by colin.muc.de (tmda-ofmipd) with ESMTP; Thu, 26 Jan 2023 21:28:37 +0100 Received: (qmail 4443 invoked by uid 1000); 26 Jan 2023 20:28:36 -0000 Date: Thu, 26 Jan 2023 20:28:36 +0000 To: gentoo-user@lists.gentoo.org Subject: Re: [gentoo-user] Soft scrolling on framebuffer consoles - New version of the patch - with GPM handling. Message-ID: References: <5647862.DvuYhMxLoT@wstn> <5662152.DvuYhMxLoT@wstn> Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: List-Id: Gentoo Linux mail X-BeenThere: gentoo-user@lists.gentoo.org Reply-to: gentoo-user@lists.gentoo.org X-Auto-Response-Suppress: DR, RN, NRN, OOF, AutoReply MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="8L3PnhVWMIwd8d2Y" Content-Disposition: inline In-Reply-To: X-Submission-Agent: TMDA/1.3.x (Ph3nix) From: Alan Mackenzie X-Primary-Address: acm@muc.de X-Archives-Salt: 6bd4b349-dc9a-4f2b-b99c-2e162969bb11 X-Archives-Hash: 5b3093c6c1724c52a950d31bce20b63b --8L3PnhVWMIwd8d2Y Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Hello, Peter. On Sun, Jan 01, 2023 at 15:13:02 +0000, Alan Mackenzie wrote: > On Sat, Dec 31, 2022 at 16:13:23 +0000, Alan Mackenzie wrote: > > On Sat, Dec 31, 2022 at 15:47:01 +0000, Peter Humphrey wrote: > > > Hello Alan, > > > On Saturday, 31 December 2022 14:08:43 GMT you wrote: > [ .... ] > > > I think you've put your finger on it: > > > $ file /lib/rc/console/font > > > /lib/rc/console/font: Linux/i386 PC Screen Font v2 data, 256 characters, > > > Unicode directory, 22x11 > > > I use consolefont="ter-122n" from the terminus-font package. It's a long time > > > since I was able to read a high-resolution screen in its native resolution. > That's a nice font. I could get used to it if I wasn't so attached to > the 8x16 font. [ .... ] > The included patch is still imperfect. When booting in 11x22, it doesn't > handle the early boot messages at all well. Also, I'm a little confused > by what a low-level scroll function is meant to do - sometimes, scrolling > happens when you type a CR, and want a line on the screen to be space > filled. Other times, you type and don't want any space > filling to happen. So I'm not convinced that scrolling, invoked by, say, > an editor program, will work correctly. OK, I'm now including a revised patch which is less imperfect. ;-) To use it, please apply it to the vanilla gentoo-sources 5.15.88 (or similar) with the usual: patch -p1 < 5.15.80-GPM.20230126.diff , followed by configuring in CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK and CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM, and setting an appropriate scrollback buffer size as already done. The ..._GPM is new, but is enabled by default. Build and install the kernel, then reboot into it, to try it out. I think it will work better than the previous patch. > >>>> To use it, please apply the supplied patch ON TOP OF the patch I > >>>> posted here on 12th December. Proceed as documented in that post, > >>>> up until configuring the kernel - in Device drivers/Graphic > >>>> support/Console display driver support, there's an extra item > >>>> "Enable a working GPM for scrolled back scrollback buffer in System > >>>> RAM" which should be enabled by default. Check this is set up > >>>> properly. Then build and install the kernel. Then reboot into it > >>>> and try it out. Again, on any problems please let me know and I'll try to fix them. As ever, there are no guarantees, etc., etc., etc. My only promise is that there's no malicious code in the patch. > > > -- > > > Regards, > > > Peter. -- Alan Mackenzie (Nuremberg, Germany). --8L3PnhVWMIwd8d2Y Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="5.15.80-GPM.20230126.diff" diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index b8f5bc19416d..58332d1dd923 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]; #ifndef VT_SINGLE_DRIVER @@ -286,8 +291,38 @@ 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 { + /* OLD STOUGH, 2023-01-26 */ + /* scrolled_expanse = vc->vc_softback_in - vc->vc_softback_curr; */ + /* if (scrolled_expanse < 0) */ + /* scrolled_expanse += vc->vc_softback_end */ + /* - vc->vc_softback_buf; */ + /* NEW STOUGH, 2023-01-26 */ + 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; + /* END OF NEW STOUGH */ + 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) @@ -295,6 +330,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.. */ @@ -316,101 +352,110 @@ void schedule_console_callback(void) * Code to manage unicode-based screen buffers */ -#ifdef NO_VC_UNI_SCREEN -/* this disables and optimizes related code away at compile time */ -#define get_vc_uniscr(vc) NULL -#else -#define get_vc_uniscr(vc) vc->vc_uni_screen -#endif - #define VC_UNI_SCREEN_DEBUG 0 typedef uint32_t char32_t; -/* - * Our screen buffer is preceded by an array of line pointers so that - * scrolling only implies some pointer shuffling. - */ -struct uni_screen { - char32_t *lines[0]; -}; +#define vc_uniscr_buf_end(vc) (vc->vc_uniscr_buf + vc->vc_uniscr_char_size) -static struct uni_screen *vc_uniscr_alloc(unsigned int cols, unsigned int rows) +static int vc_uniscr_alloc(struct vc_data *vc, unsigned int cols, unsigned int rows) { - struct uni_screen *uniscr; - void *p; - unsigned int memsize, i; + uint32_t *p; + unsigned int new_size; /* In 32-bit characters */ - /* allocate everything in one go */ - memsize = cols * rows * sizeof(char32_t); - memsize += rows * sizeof(char32_t *); - p = vzalloc(memsize); - if (!p) - return NULL; - - /* initial line pointers */ - uniscr = p; - p = uniscr->lines + rows; - for (i = 0; i < rows; i++) { - uniscr->lines[i] = p; - p += cols * sizeof(char32_t); - } - return uniscr; -} +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM + unsigned int num_scrollback_rows; -static void vc_uniscr_free(struct uni_screen *uniscr) -{ - vfree(uniscr); + 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, struct uni_screen *new_uniscr) +static void vc_uniscr_free(struct vc_data *vc) { - vc_uniscr_free(vc->vc_uni_screen); - vc->vc_uni_screen = new_uniscr; + kvfree(vc->vc_uniscr_buf); + vc->vc_uniscr_buf = NULL; } static void vc_uniscr_putc(struct vc_data *vc, char32_t uc) { - struct uni_screen *uniscr = get_vc_uniscr(vc); - - if (uniscr) - uniscr->lines[vc->state.y][vc->state.x] = uc; + uint32_t *pos; + + if (vc->vc_uniscr_buf) { + pos = vc->vc_uniscr_curr + + vc->state.y * vc->vc_cols + + vc->state.x; + if (pos >= vc_uniscr_buf_end(vc)) + pos -= vc->vc_uniscr_char_size; +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM + pos -= vc->vc_softback_lines * vc->vc_cols; + if (pos < vc->vc_uniscr_buf) + pos += vc->vc_uniscr_char_size; +#endif + *pos = uc; + } } static void vc_uniscr_insert(struct vc_data *vc, unsigned int nr) { - struct uni_screen *uniscr = get_vc_uniscr(vc); + unsigned int x = vc->state.x, y = vc->state.y, cols = vc->vc_cols; + uint32_t *ln = vc->vc_uniscr_curr + y * cols; - if (uniscr) { - char32_t *ln = uniscr->lines[vc->state.y]; - unsigned int x = vc->state.x, cols = vc->vc_cols; - - memmove(&ln[x + nr], &ln[x], (cols - x - nr) * sizeof(*ln)); + if (vc->vc_uniscr_buf) { + if (ln >= vc_uniscr_buf_end(vc)) + ln -= vc->vc_uniscr_char_size; +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM + ln -= vc->vc_softback_lines * vc->vc_cols; + if (ln < vc->vc_uniscr_buf) + ln += vc->vc_uniscr_char_size; +#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) { - struct uni_screen *uniscr = get_vc_uniscr(vc); - - if (uniscr) { - char32_t *ln = uniscr->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 + y * cols; - memcpy(&ln[x], &ln[x + nr], (cols - x - nr) * sizeof(*ln)); + if (vc->vc_uniscr_buf) { + if (ln >= vc_uniscr_buf_end(vc)) + ln -= vc->vc_uniscr_char_size; +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM + ln -= vc->vc_softback_lines * vc->vc_cols; + if (ln < vc->vc_uniscr_buf) + ln += vc->vc_uniscr_char_size; +#endif + memcpy(&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) { - struct uni_screen *uniscr = get_vc_uniscr(vc); - - if (uniscr) { - char32_t *ln = uniscr->lines[vc->state.y]; + if (vc->vc_uniscr_buf) { + uint32_t *ln = vc->vc_uniscr_curr + vc->state.y * vc->vc_cols; + if (ln >= vc_uniscr_buf_end(vc)) + ln -= vc->vc_uniscr_char_size; +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM + ln -= vc->vc_softback_lines * vc->vc_cols; + if (ln < vc->vc_uniscr_buf) + ln += vc->vc_uniscr_char_size; +#endif memset32(&ln[x], ' ', nr); } } @@ -418,77 +463,88 @@ static void vc_uniscr_clear_line(struct vc_data *vc, unsigned int x, static void vc_uniscr_clear_lines(struct vc_data *vc, unsigned int y, unsigned int nr) { - struct uni_screen *uniscr = get_vc_uniscr(vc); - - if (uniscr) { + if (vc->vc_uniscr_buf) { unsigned int cols = vc->vc_cols; + uint32_t *ln = vc->vc_uniscr_curr + y * cols; - while (nr--) - memset32(uniscr->lines[y++], ' ', cols); +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM + ln -= vc->vc_softback_lines * cols; + if (ln < vc->vc_uniscr_buf) + ln += vc->vc_uniscr_char_size; +#endif + while (nr--) { + if (ln >= vc_uniscr_buf_end(vc)) + ln -= vc->vc_uniscr_char_size; + memset32(ln, ' ', cols); + ln += cols; + } } } static void vc_uniscr_scroll(struct vc_data *vc, unsigned int t, unsigned int b, enum con_scroll dir, unsigned int nr) { - struct uni_screen *uniscr = get_vc_uniscr(vc); - - if (uniscr) { - unsigned int i, j, k, sz, d, clear; - - sz = b - t; - clear = b - nr; - d = nr; - if (dir == SM_DOWN) { - clear = t; - d = sz - nr; - } - for (i = 0; i < gcd(d, sz); i++) { - char32_t *tmp = uniscr->lines[t + i]; - j = i; - while (1) { - k = j + d; - if (k >= sz) - k -= sz; - if (k == i) - break; - uniscr->lines[t + j] = uniscr->lines[t + k]; - j = k; + 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 && t == 0 && b == vc->vc_rows) { + vc->vc_uniscr_curr += nr * cols; + if (vc->vc_uniscr_curr >= vc_uniscr_buf_end(vc)) + vc->vc_uniscr_curr -= vc->vc_uniscr_char_size; + d = nr; + clear = vc->vc_rows - nr; + } else if (dir == SM_DOWN && t == 0 && b == vc->vc_rows - nr) { + vc->vc_uniscr_curr -= nr * cols; + if (vc->vc_uniscr_curr < vc->vc_uniscr_buf) + vc->vc_uniscr_curr += vc->vc_uniscr_char_size; + d = 0; + } else if (dir == SM_UP) { + sz = b - t; + src = vc->vc_uniscr_curr + t * cols; + if (src >= vc_uniscr_buf_end(vc)) + src -= vc->vc_uniscr_char_size; + dest = src - nr * cols; + if (dest < vc->vc_uniscr_buf) + dest += vc->vc_uniscr_char_size; + i = b - t; + while (i--) { + memcpy(dest, src, cols * sizeof(uint32_t)); + src += cols; + if (src >= vc_uniscr_buf_end(vc)) + src -= vc->vc_uniscr_char_size; + dest += cols; + if (dest >= vc_uniscr_buf_end(vc)) + dest -= vc->vc_uniscr_char_size; } - uniscr->lines[t + j] = tmp; + d = nr; + clear = b - nr; + } else { + src = vc->vc_uniscr_curr + b * cols; + if (src >= vc_uniscr_buf_end(vc)) + src -= vc->vc_uniscr_char_size; + dest = src + nr * cols; + if (dest >= vc_uniscr_buf_end(vc)) + dest -= vc->vc_uniscr_char_size; + i = b - t; + while (i--) { + src -= cols; + if (src < vc->vc_uniscr_buf) + src += vc->vc_uniscr_char_size; + dest -= cols; + if (dest < vc->vc_uniscr_buf) + dest += vc->vc_uniscr_char_size; + memcpy(dest, src, cols * sizeof(uint32_t)); + } + d = nr; + clear = t; } - vc_uniscr_clear_lines(vc, clear, nr); - } -} - -static void vc_uniscr_copy_area(struct uni_screen *dst, - unsigned int dst_cols, - unsigned int dst_rows, - struct uni_screen *src, - unsigned int src_cols, - unsigned int src_top_row, - unsigned int src_bot_row) -{ - unsigned int dst_row = 0; - - if (!dst) - return; - - while (src_top_row < src_bot_row) { - char32_t *src_line = src->lines[src_top_row]; - char32_t *dst_line = dst->lines[dst_row]; - - memcpy(dst_line, src_line, src_cols * sizeof(char32_t)); - if (dst_cols - src_cols) - memset32(dst_line + src_cols, ' ', dst_cols - src_cols); - src_top_row++; - dst_row++; - } - while (dst_row < dst_rows) { - char32_t *dst_line = dst->lines[dst_row]; - - memset32(dst_line, ' ', dst_cols); - dst_row++; + if (d) + vc_uniscr_clear_lines(vc, clear, nr); } } @@ -500,7 +556,6 @@ static void vc_uniscr_copy_area(struct uni_screen *dst, */ int vc_uniscr_check(struct vc_data *vc) { - struct uni_screen *uniscr; unsigned short *p; int x, y, mask; @@ -512,11 +567,10 @@ int vc_uniscr_check(struct vc_data *vc) if (!vc->vc_utf) return -ENODATA; - if (vc->vc_uni_screen) + if (vc->vc_uniscr_buf) return 0; - uniscr = vc_uniscr_alloc(vc->vc_cols, vc->vc_rows); - if (!uniscr) + if (vc_uniscr_alloc (vc, vc->vc_cols, vc->vc_rows)) return -ENOMEM; /* @@ -528,14 +582,15 @@ 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++) { - char32_t *line = uniscr->lines[y]; + uint32_t *line = vc->vc_uniscr_curr + y * vc->vc_cols; + if (line >= vc_uniscr_buf_end(vc)) + line -= vc->vc_uniscr_char_size; for (x = 0; x < vc->vc_cols; x++) { u16 glyph = scr_readw(p++) & mask; line[x] = inverse_translate(vc, glyph, true); } } - vc->vc_uni_screen = uniscr; return 0; } @@ -547,12 +602,26 @@ 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) { - struct uni_screen *uniscr = get_vc_uniscr(vc); +#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 - BUG_ON(!uniscr); + BUG_ON(!vc->vc_uniscr_buf); +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK + pos = (vc->vc_uniscr_curr + row * vc->vc_cols + col); + if (pos >= vc_uniscr_buf_end(vc)) + pos -= vc->vc_uniscr_char_size; +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM + pos -= vc->vc_softback_lines * vc->vc_cols; + if (pos < vc->vc_uniscr_buf) + pos += vc->vc_uniscr_char_size; +#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) { /* @@ -562,60 +631,271 @@ 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, &uniscr->lines[row][col], nr * sizeof(char32_t)); + 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; - char32_t *uni_buf = dest; - while (nr--) { - u16 glyph = scr_readw(p++) & mask; - *uni_buf++ = inverse_translate(vc, glyph, true); - } + /* CAN'T HAPPEN!!! (Hah hah!) */ } +#endif } /* this is for validation and debugging only */ static void vc_uniscr_debug_check(struct vc_data *vc) { - struct uni_screen *uniscr = get_vc_uniscr(vc); - unsigned short *p; - int x, y, mask; +#ifndef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK + /* struct uni_screen *uniscr = get_vc_uniscr(vc); */ + /* unsigned short *p; */ + /* int x, y, mask; */ +#endif - if (!VC_UNI_SCREEN_DEBUG || !uniscr) + if (!VC_UNI_SCREEN_DEBUG || !vc->vc_uniscr_buf) return; WARN_CONSOLE_UNLOCKED(); +#ifndef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK /* * Make sure our unicode screen translates into the same glyphs * as the actual screen. This is brutal indeed. */ - p = (unsigned short *)vc->vc_origin; - mask = vc->vc_hi_font_mask | 0xff; - for (y = 0; y < vc->vc_rows; y++) { - char32_t *line = uniscr->lines[y]; - for (x = 0; x < vc->vc_cols; x++) { - u16 glyph = scr_readw(p++) & mask; - char32_t uc = line[x]; - int tc = conv_uni_to_pc(vc, uc); - if (tc == -4) - tc = conv_uni_to_pc(vc, 0xfffd); - if (tc == -4) - tc = conv_uni_to_pc(vc, '?'); - if (tc != glyph) - pr_err_ratelimited( - "%s: mismatch at %d,%d: glyph=%#x tc=%#x\n", - __func__, x, y, glyph, tc); + /* p = (unsigned short *)vc->vc_origin; */ + /* mask = vc->vc_hi_font_mask | 0xff; */ + /* for (y = 0; y < vc->vc_rows; y++) { */ + /* char32_t *line = uniscr->lines[y]; */ + /* for (x = 0; x < vc->vc_cols; x++) { */ + /* u16 glyph = scr_readw(p++) & mask; */ + /* char32_t uc = line[x]; */ + /* int tc = conv_uni_to_pc(vc, uc); */ + /* if (tc == -4) */ + /* tc = conv_uni_to_pc(vc, 0xfffd); */ + /* if (tc == -4) */ + /* tc = conv_uni_to_pc(vc, '?'); */ + /* if (tc != glyph) */ + /* pr_err_ratelimited( */ + /* "%s: mismatch at %d,%d: glyph=%#x tc=%#x\n", */ + /* __func__, x, y, glyph, tc); */ + /* } */ + /* } */ +#endif +} + +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK +static void con_update_softback(struct vc_data *vc) +{ + int l = vc->vc_softback_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 t, unsigned int b, enum con_scroll dir, unsigned int nr) @@ -626,6 +906,10 @@ static void con_scroll(struct vc_data *vc, unsigned int t, unsigned int b, nr = b - t - 1; if (b > vc->vc_rows || t >= b || nr < 1) return; +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK + if (dir == SM_UP && vc->vc_softback_top) + con_softback_note (vc, t, nr); +#endif vc_uniscr_scroll(vc, t, b, dir, nr); if (con_is_visible(vc) && vc->vc_sw->con_scroll(vc, t, b, dir, nr)) return; @@ -641,6 +925,53 @@ static void con_scroll(struct vc_data *vc, unsigned int t, unsigned int b, 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; @@ -684,6 +1015,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) { @@ -692,7 +1024,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); } } @@ -753,51 +1088,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); } @@ -927,8 +1279,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; @@ -989,7 +1350,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; } @@ -1004,7 +1364,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"); @@ -1017,7 +1376,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); /* @@ -1032,9 +1390,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); @@ -1110,14 +1478,70 @@ int vc_allocate(unsigned int currcons) /* return 0 on success */ struct vt_notifier_param param; struct vc_data *vc; int err; +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK + unsigned long new_end; + unsigned long new_in, new_top; + unsigned long in_residue; + unsigned short *d; +#endif WARN_CONSOLE_UNLOCKED(); - if (currcons >= MAX_NR_CONSOLES) return -ENXIO; if (vc_cons[currcons].d) + { +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK + vc = vc_cons[currcons].d; + if (!vc->vc_softback_size) { + /* vc was partially initialized by __init. */ + vc->vc_softback_size = console_soft_scrollback_size; + vc->vc_softback_buf = + (unsigned long)/*k*/vzalloc(vc->vc_softback_size/*, GFP_KERNEL*/); + if (vc->vc_softback_buf) { + 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); + } + } + if (vc->vc_softback_buf + && (vc->vc_softback_end - vc->vc_softback_buf) % vc->vc_size_row) { + new_end = vc->vc_softback_buf + vc->vc_softback_size + - (vc->vc_softback_size % vc->vc_size_row); + new_top = vc->vc_softback_top + - (vc->vc_softback_top - vc->vc_softback_buf) + % vc->vc_size_row; + in_residue = (vc->vc_softback_in - vc->vc_softback_buf) + % vc->vc_size_row; + new_in = vc->vc_softback_in + + (in_residue ? vc->vc_size_row - in_residue : 0); + if (new_in == new_end) + new_in = vc->vc_softback_buf; + if (new_top == new_in) + new_top += vc->vc_size_row; + else if ((new_in > new_top + && (new_in - new_top <= vc->vc_size_row))) + new_top += 2 * vc->vc_size_row; + if (new_top >= new_end) + new_top -= (new_end - vc->vc_softback_buf); + vc->vc_softback_lines = 0; + vc->vc_softback_curr = new_in; + + d = (u16 *)vc->vc_softback_in; + vc->vc_softback_end = new_end; + vc->vc_softback_in = new_in; + vc->vc_softback_top = new_top; + while (d != (u16 *)new_in) { + scr_writew (0x0020, d); /* How about non-VGA? */ + if (++d == (u16 *)new_end) + d = (u16 *)vc->vc_softback_buf; + } + } +#endif return 0; + } /* due to the granularity of kmalloc, we waste some memory here */ /* the alloc is done in two steps, to optimize the common situation @@ -1157,6 +1581,19 @@ int vc_allocate(unsigned int currcons) /* return 0 on success */ 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)/*k*/vzalloc(vc->vc_softback_size/*, GFP_KERNEL*/); + 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); @@ -1177,6 +1614,78 @@ 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 > new_rows) { + dest -= (new_lines - new_rows) * new_cols; + if (dest < vc->vc_uniscr_buf) + dest += vc->vc_uniscr_char_size; + } + src = old_uniscr_curr; + if (new_lines > new_rows) { + src -= (new_lines - new_rows) * old_cols; + if (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); + dest += new_cols; + if (dest >= vc->vc_uniscr_buf + + vc->vc_uniscr_char_size) + dest -= vc->vc_uniscr_char_size; + 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 @@ -1197,12 +1706,18 @@ 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; - struct uni_screen *new_uniscr = NULL; - + unsigned short *oldscreen, *newscreen, *d; + uint32_t *old_uniscr = vc->vc_uniscr_buf; +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK + 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) @@ -1216,8 +1731,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) { /* @@ -1245,61 +1760,93 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc, if (!newscreen) return -ENOMEM; - if (get_vc_uniscr(vc)) { - new_uniscr = vc_uniscr_alloc(new_cols, new_rows); - if (!new_uniscr) { - kfree(newscreen); - return -ENOMEM; - } +#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(vc->vc_softback_size); + new_softback_buf = (unsigned long)temp_new_softback_buf; + if (!new_softback_buf) + return -ENOMEM; + new_softback_end = new_softback_buf + vc->vc_softback_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 = vc->vc_softback_size / new_size_row; + copied_scrolled_rows = min(old_scrolled_rows, new_scrolled_rows); +#endif 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) { +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK + con_softback_note(vc, 0, + old_rows - new_rows); +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM + vc_uniscr_scroll(vc, 0, old_rows, SM_UP, + old_rows - new_rows); +#endif +#endif /* * 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, 0, + vc->state.y - new_rows/2); +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM + vc_uniscr_scroll(vc, 0, old_rows, SM_UP, + vc->state.y - new_rows/2); +#endif +#endif /* * 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, - get_vc_uniscr(vc), 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; + + 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); @@ -1309,17 +1856,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 /* (unsigned long)newscreen STOUGH, 2023-01-26 */; + vc->vc_softback_lines = 0; /* Probably redundant. */ + kvfree((void *)old_softback_buf); +#endif /* do part of a reset_terminal() */ vc->vc_top = 0; @@ -1400,8 +1990,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; @@ -1646,7 +2236,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; @@ -1658,7 +2248,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; } @@ -2928,6 +3518,12 @@ static int do_con_write(struct tty_struct *tty, const unsigned char *buf, int co param.vc = vc; +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK + /* Undo any soft scrolling - and do + not pass through this function. */ + concon_set_origin (vc); +#endif + while (!tty->flow.stopped && count) { int orig = *buf; buf++; @@ -3094,11 +3690,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; @@ -3143,7 +3736,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: @@ -3364,7 +3961,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(); } @@ -3385,6 +3986,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; @@ -3440,7 +4049,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); @@ -4140,7 +4749,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; @@ -4441,7 +5050,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); @@ -4741,10 +5353,19 @@ EXPORT_SYMBOL_GPL(screen_glyph); u32 screen_glyph_unicode(const struct vc_data *vc, int n) { - struct uni_screen *uniscr = get_vc_uniscr(vc); + int y = n / vc->vc_cols, x = n % vc->vc_cols; + uint32_t *ln = vc->vc_uniscr_curr + y * vc->vc_cols; - if (uniscr) - return uniscr->lines[n / vc->vc_cols][n % vc->vc_cols]; + if (vc->vc_uniscr_curr) { + if (ln >= vc_uniscr_buf_end(vc)) + ln -= vc->vc_uniscr_char_size; +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM + ln -= vc->vc_softback_lines * vc->vc_cols; + if (ln < vc->vc_uniscr_buf) + ln += vc->vc_uniscr_char_size; +#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 fcc46380e7c9..1abba103d7da 100644 --- a/drivers/video/console/Kconfig +++ b/drivers/video/console/Kconfig @@ -98,6 +98,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 e035a63bbe5b..a4fab71c5853 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -3152,6 +3152,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 d5b9c8d40c18..281da9b3797f 100644 --- a/include/linux/console_struct.h +++ b/include/linux/console_struct.h @@ -110,6 +110,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 */ @@ -159,7 +170,11 @@ struct vc_data { struct vc_data **vc_display_fg; /* [!] Ptr to var holding fg console for this display */ struct uni_pagedir *vc_uni_pagedir; struct uni_pagedir **vc_uni_pagedir_loc; /* [!] Location of uni_pagedir variable for this console */ - struct uni_screen *vc_uni_screen; /* unicode screen content */ +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM + 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 */ +#endif /* additional information is in vt_kern.h */ }; diff --git a/include/linux/vt_kern.h b/include/linux/vt_kern.h index b5ab452fca5b..1a11c9868f8e 100644 --- a/include/linux/vt_kern.h +++ b/include/linux/vt_kern.h @@ -115,6 +115,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, --8L3PnhVWMIwd8d2Y--