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 (4096 bits)) (No client certificate requested) by finch.gentoo.org (Postfix) with ESMTPS id BBA2F15802C for ; Sun, 15 Dec 2024 11:09:22 +0000 (UTC) Received: from pigeon.gentoo.org (localhost [127.0.0.1]) by pigeon.gentoo.org (Postfix) with SMTP id 48BB3E092D; Sun, 15 Dec 2024 11:09:14 +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 22C2DE08F3 for ; Sun, 15 Dec 2024 11:09:11 +0000 (UTC) Received: (qmail 13688 invoked by uid 3782); 15 Dec 2024 12:09:08 +0100 Received: from muc.de (pd953a38d.dip0.t-ipconnect.de [217.83.163.141]) (using STARTTLS) by colin.muc.de (tmda-ofmipd) with ESMTP; Sun, 15 Dec 2024 12:09:08 +0100 Received: (qmail 3212 invoked by uid 1000); 15 Dec 2024 11:09:07 -0000 Date: Sun, 15 Dec 2024 11:09:07 +0000 To: gentoo-user@lists.gentoo.org Subject: [gentoo-user] A fix for irritations with GPM Message-ID: 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="gBzMzaA8WCbtah/s" Content-Disposition: inline X-Submission-Agent: TMDA/1.3.x (Ph3nix) From: Alan Mackenzie X-Primary-Address: acm@muc.de X-Archives-Salt: ddefd1e7-8921-432d-9962-99a2f070d338 X-Archives-Hash: ba3898e087e5d49586949c3cdb7953df --gBzMzaA8WCbtah/s Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Hello, Gentoo. As a long time user of the Linux console, two things about the GPM mouse utility get on my nerves. The first is that on pasting a selection, a carriage return is appended to the line (or the last line). This means that, for example, on copying a bash command from one tty to another, there is no opportunity to edit it before it gets run. The second is that while selecting, the highlighting on the screen is done crudely to the end of each line in the selection, rather than to the end of the text in that line. I have a fix for these, now. The pertinent source file is drivers/tty/vt/selection.c in the kernel. The patch is in the attached file 6.8.1-SELECTION.20241215.diff. To use this patch (but see below, first), extract the patch to your home directory, cd to /usr/src/linux-6.6.62-gentoo (or later version of the kernel), then issue the command: $ patch --dry-run -p1 < ~/6.8.1-SELECTION.20241215.diff .. This is just a dry run which checks the patch applies cleanly rather than actually applying it. If everything is OK, apply it for real with: $ patch -p1 < ~/6.8.1-SELECTION.20241215.diff .. If, on the other hand, you get an error saying Hunk #7 failed, you've probably already applied my earlier patch to enable scrolling on the console, which included a patch for problem 1 above. You will need to undo that patch before applying the main one. Extract the attached patch 6.6.13-TRIPLE.20240123.diff, and apply it reversed as follows: $ patch -p1 -R < ~/6.6.13-TRIPLE.20240123.diff .. Then apply the main patch, as above. Having done that, rebuild your kernel, and install it to the /boot partition in the normal (for you) way. Reboot and enjoy! The usual reservations apply, of course. There's nothing malicious in the patches, but if it breaks for you, I'll be sorry and will handle bug reports. Nothing more. -- Alan Mackenzie (Nuremberg, Germany). --gBzMzaA8WCbtah/s Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="6.8.1-SELECTION.20241215.diff" diff --git a/drivers/tty/vt/selection.c b/drivers/tty/vt/selection.c index 8967c3a0d916..545ca051635a 100644 --- a/drivers/tty/vt/selection.c +++ b/drivers/tty/vt/selection.c @@ -42,21 +42,20 @@ static struct vc_selection { char *buffer; unsigned int buf_len; volatile int start; /* cleared by clear_selection */ - int end; + int end; /* Note: this points to the last char, not to after it. */ + bool mouse_at_e; + unsigned short prev_x; } vc_sel = { .lock = __MUTEX_INITIALIZER(vc_sel.lock), .start = -1, }; +static bool unicode; +static unsigned int size_row; + /* clear_selection, highlight and highlight_pointer can be called from interrupt (via scrollback/front) */ -/* set reverse video on characters s-e of console with selection. */ -static inline void highlight(const int s, const int e) -{ - invert_screen(vc_sel.cons, s, e-s+2, true); -} - /* use complementary color to show the pointer */ static inline void highlight_pointer(const int where) { @@ -64,7 +63,7 @@ static inline void highlight_pointer(const int where) } static u32 -sel_pos(int n, bool unicode) +sel_pos(int n) { if (unicode) return screen_glyph_unicode(vc_sel.cons, n / 2); @@ -72,6 +71,61 @@ sel_pos(int n, bool unicode) false); } +static int last_char_pos(int line_end, int limit) +{ + int pos = line_end; + while ((pos >= limit) && is_space_on_vt(sel_pos(pos))) + pos -= 2; + return pos; +} + +/* set reverse video on characters s-e of console with selection. */ +static void highlight(const int s, const int e) +{ + bool mouse_at_e = (vc_sel.start != -1) && vc_sel.mouse_at_e; + int bol = s; + int eol = rounddown(s, size_row) + size_row - 2; + int pos; + + if (mouse_at_e) { + int prev_e = s - 2; /* For the + 2 in vc_do_selection. */ + int low_mouse_beg = rounddown(prev_e, size_row); + int high_mouse_beg = rounddown(e, size_row); + if (low_mouse_beg != high_mouse_beg) { + /* Unhighlight any trailing space on the previous line + * (moving down), or rehighlight it (moving up). */ + int low_mouse_end = low_mouse_beg + size_row - 2; + + pos = last_char_pos(low_mouse_end, + max(vc_sel.start, low_mouse_beg)) + 2; + if (prev_e >= pos) + invert_screen(vc_sel.cons, pos, + prev_e - pos + 2, true); + } + } + + while (eol < e) { + if ((pos = last_char_pos(eol, bol)) >= bol) + invert_screen(vc_sel.cons, bol, pos - bol + 2, true); + bol = eol + 2; + eol += size_row; + } + /* Last line: Firstly, are we (un)highlighting the entire selection? */ + if ((vc_sel.start == -1) || ((s == vc_sel.start && e == vc_sel.end)) || + /* .... or trailing space at the end of the selection? */ + (eol >= vc_sel.end)) + /* YES: so highlight the entire last line up to E. */ + invert_screen(vc_sel.cons, bol, e - bol + 2, true); + else { + /* NO: Don't highlight the trailing spaces. */ + pos = last_char_pos(eol, bol); + if (pos > e) + pos = e; + if (pos >= bol) + invert_screen(vc_sel.cons, bol, pos - bol + 2, true); + } +} + /** * clear_selection - remove current selection * @@ -186,7 +240,7 @@ int set_selection_user(const struct tiocl_selection __user *sel, return set_selection_kernel(&v, tty); } -static int vc_selection_store_chars(struct vc_data *vc, bool unicode) +static int vc_selection_store_chars(void) { char *bp, *obp; unsigned int i; @@ -205,16 +259,17 @@ static int vc_selection_store_chars(struct vc_data *vc, bool unicode) obp = bp; for (i = vc_sel.start; i <= vc_sel.end; i += 2) { - u32 c = sel_pos(i, unicode); + u32 c = sel_pos(i); if (unicode) bp += store_utf8(c, bp); else *bp++ = c; if (!is_space_on_vt(c)) obp = bp; - if (!((i + 2) % vc->vc_size_row)) { + if (!((i + 2) % size_row) && + (i + 2) < vc_sel.end) { /* strip trailing blanks from line and add newline, - unless non-space at end of line. */ + unless non-space at end of line or on last line. */ if (obp != bp) { bp = obp; *bp++ = '\r'; @@ -227,11 +282,10 @@ static int vc_selection_store_chars(struct vc_data *vc, bool unicode) return 0; } -static int vc_do_selection(struct vc_data *vc, unsigned short mode, int ps, - int pe) +static int vc_do_selection(unsigned short mode, int ps, int pe) { int new_sel_start, new_sel_end, spc; - bool unicode = vt_do_kdgkbmode(fg_console) == K_UNICODE; + int pe_line_start, term_space; switch (mode) { case TIOCL_SELCHAR: /* character-by-character selection */ @@ -239,30 +293,37 @@ static int vc_do_selection(struct vc_data *vc, unsigned short mode, int ps, new_sel_end = pe; break; case TIOCL_SELWORD: /* word-by-word selection */ - spc = is_space_on_vt(sel_pos(ps, unicode)); + spc = is_space_on_vt(sel_pos(ps)); for (new_sel_start = ps; ; ps -= 2) { - if ((spc && !is_space_on_vt(sel_pos(ps, unicode))) || - (!spc && !inword(sel_pos(ps, unicode)))) + if ((spc && !is_space_on_vt(sel_pos(ps))) || + (!spc && !inword(sel_pos(ps)))) break; new_sel_start = ps; - if (!(ps % vc->vc_size_row)) + if (!(ps % size_row)) break; } - spc = is_space_on_vt(sel_pos(pe, unicode)); + spc = is_space_on_vt(sel_pos(pe)); + if (spc) term_space = pe; for (new_sel_end = pe; ; pe += 2) { - if ((spc && !is_space_on_vt(sel_pos(pe, unicode))) || - (!spc && !inword(sel_pos(pe, unicode)))) + if ((spc && !is_space_on_vt(sel_pos(pe))) || + (!spc && !inword(sel_pos(pe)))) break; new_sel_end = pe; - if (!((pe + 2) % vc->vc_size_row)) + if (!((pe + 2) % size_row)) break; } + /* Don't highlight trailing space after the mouse on the last + * line. */ + if (spc && + !((pe + 2) % size_row)) + new_sel_end = last_char_pos(term_space, ps); break; case TIOCL_SELLINE: /* line-by-line selection */ - new_sel_start = rounddown(ps, vc->vc_size_row); - new_sel_end = rounddown(pe, vc->vc_size_row) + - vc->vc_size_row - 2; + new_sel_start = rounddown(ps, size_row); + pe_line_start = rounddown(pe, size_row); + new_sel_end = last_char_pos(pe_line_start + size_row - 2, + ps); /* Can be before BOL. */ break; case TIOCL_SELPOINTER: highlight_pointer(pe); @@ -274,17 +335,6 @@ static int vc_do_selection(struct vc_data *vc, unsigned short mode, int ps, /* remove the pointer */ highlight_pointer(-1); - /* select to end of line if on trailing space */ - if (new_sel_end > new_sel_start && - !atedge(new_sel_end, vc->vc_size_row) && - is_space_on_vt(sel_pos(new_sel_end, unicode))) { - for (pe = new_sel_end + 2; ; pe += 2) - if (!is_space_on_vt(sel_pos(pe, unicode)) || - atedge(pe, vc->vc_size_row)) - break; - if (is_space_on_vt(sel_pos(pe, unicode))) - new_sel_end = pe; - } if (vc_sel.start == -1) /* no current selection */ highlight(new_sel_start, new_sel_end); else if (new_sel_start == vc_sel.start) @@ -303,22 +353,32 @@ static int vc_do_selection(struct vc_data *vc, unsigned short mode, int ps, else /* contract from left */ highlight(vc_sel.start, new_sel_start - 2); } - else /* some other case; start selection from scratch */ + else /* The mouse has been moved through the starting point or we + * have word or line selection. */ { - clear_selection(); + /* Restore vc_sel.mouse_at_e to its previous value to + * unhighlight the previous selection. */ + vc_sel.mouse_at_e = !vc_sel.mouse_at_e; + highlight(vc_sel.start, vc_sel.end); + vc_sel.mouse_at_e = !vc_sel.mouse_at_e; + + vc_sel.start = new_sel_start; + vc_sel.end = new_sel_end; highlight(new_sel_start, new_sel_end); } vc_sel.start = new_sel_start; vc_sel.end = new_sel_end; - return vc_selection_store_chars(vc, unicode); + return vc_selection_store_chars(); } static int vc_selection(struct vc_data *vc, struct tiocl_selection *v, struct tty_struct *tty) { int ps, pe; + int res; + size_row = vc->vc_size_row; poke_blanked_console(); if (v->sel_mode == TIOCL_SELCLEAR) { @@ -327,6 +387,20 @@ static int vc_selection(struct vc_data *vc, struct tiocl_selection *v, return 0; } + /* Heuristically correct any strange values from GPM. When the mouse + * is dragged off the left hand edge, GPM reports it as being at the + * end of the previous line. When it is dragged off the bottom edge, + * it is reported as being at the end of the last line. */ + if ((v->xe == vc->vc_cols) && (vc_sel.start != -1)) { + if (v->ye == vc->vc_rows) { + if (vc_sel.prev_x <= (vc->vc_cols - 20)) + v->xe = vc_sel.prev_x; + } else if (vc_sel.prev_x <= (vc->vc_cols >> 1)) { + v->xe = 1; + v->ye++; + } + } + v->xs = min_t(u16, v->xs - 1, vc->vc_cols - 1); v->ys = min_t(u16, v->ys - 1, vc->vc_rows - 1); v->xe = min_t(u16, v->xe - 1, vc->vc_cols - 1); @@ -338,17 +412,27 @@ static int vc_selection(struct vc_data *vc, struct tiocl_selection *v, return 0; } - ps = v->ys * vc->vc_size_row + (v->xs << 1); - pe = v->ye * vc->vc_size_row + (v->xe << 1); - if (ps > pe) /* make vc_sel.start <= vc_sel.end */ + vc_sel.mouse_at_e = true; + ps = v->ys * size_row + (v->xs << 1); + pe = v->ye * size_row + (v->xe << 1); + if (ps > pe) { /* make vc_sel.start <= vc_sel.end */ swap(ps, pe); + vc_sel.mouse_at_e = false; + } if (vc_sel.cons != vc) { clear_selection(); vc_sel.cons = vc; } - return vc_do_selection(vc, v->sel_mode, ps, pe); + unicode = vt_do_kdgkbmode(fg_console) == K_UNICODE; + + res = vc_do_selection(v->sel_mode, ps, pe); + if ((vc_sel.start != -1) && + ((v->sel_mode == TIOCL_SELCHAR) || (v->sel_mode == TIOCL_SELWORD) || + (v->sel_mode == TIOCL_SELLINE))) + vc_sel.prev_x = v->xe + 1; /* Convert back to 1-based. */ + return res; } int set_selection_kernel(struct tiocl_selection *v, struct tty_struct *tty) --gBzMzaA8WCbtah/s 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); --gBzMzaA8WCbtah/s--