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 5D8EF15838C for ; Wed, 24 Jan 2024 10:00:48 +0000 (UTC) Received: from pigeon.gentoo.org (localhost [127.0.0.1]) by pigeon.gentoo.org (Postfix) with SMTP id 4ECFA2BC03B; Wed, 24 Jan 2024 10:00:42 +0000 (UTC) Received: from mail.muc.de (mail.muc.de [193.149.48.3]) (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 C09022BC01C for ; Wed, 24 Jan 2024 10:00:39 +0000 (UTC) Received: (qmail 41284 invoked by uid 3782); 24 Jan 2024 11:00:38 +0100 Received: from acm.muc.de (p4fe15939.dip0.t-ipconnect.de [79.225.89.57]) (using STARTTLS) by colin.muc.de (tmda-ofmipd) with ESMTP; Wed, 24 Jan 2024 11:00:37 +0100 Received: (qmail 3274 invoked by uid 1000); 24 Jan 2024 10:00:37 -0000 Date: Wed, 24 Jan 2024 10:00:37 +0000 To: gentoo-user@lists.gentoo.org Subject: Re: [gentoo-user] Soft scrolling on framebuffer consoles - with GPM handling - version of the patch for kernel 6.3 onwards. Message-ID: References: <4776889.GXAFRqVoOG@wstn> <12167623.O9o76ZdvQC@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="gDAVLxOU+RzwZmri" 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: 3b53f2ed-2716-4585-acf6-41811c46e9c7 X-Archives-Hash: 11e34dbc0902fd6d556e2c8b878fbbdf --gDAVLxOU+RzwZmri Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Hello, Gentoo. [ First an older post, but without quoting >s on the lines. ] On Fri, Feb 03, 2023 at 18:56:27 +0000, Alan Mackenzie wrote: The topic of this post is my kernel patch which enables soft scrolling on Linux tty's with and , and also enables the GPM mouse utility on those scrolled regions. Currently, the patch I posted some months ago works on all 6.1.x kernels, and very likely works on 6.2.x, too. In kernel 6.3.1, some significant refactoring was done by the kernel people, necessitating a new version of the patch, called 6.3.11-GPM.20231004.diff. I've tested this on 6.3.11 and 6.5.5. Just a quick reminder of how to use these files for anybody else who might be interested: (i) cd /usr/src/linux-6.3.11-gentoo, or similar. (Or ...-6.1.x-gentoo). (ii) patch -p1 < 6.3.11-GPM.20231004.diff (or the other one). (iii) Configure the kernel as normal. Additionally, under Device drivers/Graphic Support/Console display driver support, enable CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK, set the buffer size to taste (it's default is 128 kB) and accept the default enablement of CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM. (iv) Build the kernel and install it into your boot manager. (v) Reboot and enjoy! You can now use GPM in scrolled consoles. Just a warning: if you copy your kernel .config from a 6.1.x kernel, and use make oldconfig to generate a new kernel for 6.3.x etc., you will have to enable CONFIG_HID_SUPPORT, otherwise your USB keyboard and mouse will be dead on bootup. ;-( The usual disclaimers apply, here. If this patch breaks anything for you, you get to join the pieces back together again. But if this does happen, please let me know, so that I can try to fix the bug. My only promise is that there's nothing malicious in the patch. As well as 6.3.11-GPM.20231004.diff, I'm reposting 6.1.8-GPM.20230203.diff for anybody new here who wants to try it on the current Gentoo 6.1.x kernel. -- Alan Mackenzie (Nuremberg, Germany). I've adapted this patch for kernel 6.6.13. All of the parent post still applies, with the exception of the version numbers. The main patch for the new kernel is 6.6.13-GPM.20240123.diff. Additionally I've attached a smaller patch which fixes what to me was a little glitch in the gpm utility. This was that on a triple click to select a whole line (or sequence of lines), the (last) line got a linefeed appended to it. The effect was that if you wanted to hoik a line out of some screen output and execute that from the bash command line, you couldn't edit it first. So with this patch, you no longer get that annoying linefeed, and the highlighting is adjusted accordingly. This patch is (as far as I'm aware, without testing) independent of the main GPM patch. If anybody still needs them, I've still got the patches for the 6.1 kernel, and the 6.3 and 6.5 kernels, and one or two earlier kernels too. Have fun! -- Alan Mackenzie (Nuremberg, Germany). --gDAVLxOU+RzwZmri Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="6.6.13-GPM.20240123.diff" diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 5c47f77804f0..3a901537d5b6 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; - memcpy(&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 + 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) { - 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 - and 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: @@ -3314,7 +3825,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(); } @@ -3335,6 +3850,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; @@ -3390,7 +3913,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); @@ -3469,6 +3992,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; @@ -4080,7 +4604,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; @@ -4380,7 +4904,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); @@ -4693,12 +5220,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 30577b1d3de5..96530aeb7434 100644 --- a/drivers/video/console/Kconfig +++ b/drivers/video/console/Kconfig @@ -100,6 +100,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 f157a5a1dffc..92985a585add 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -609,6 +609,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); @@ -1257,6 +1279,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 */ @@ -2063,6 +2104,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); @@ -2072,8 +2114,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; } @@ -3163,6 +3218,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, --gDAVLxOU+RzwZmri Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="6.6.13-TRIPLE.20240123.diff" 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); --gDAVLxOU+RzwZmri--