From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id 7TqgFpg/xmDAJAAAWB0awg (envelope-from ) for ; Sun, 13 Jun 2021 13:25:44 -0400 Received: by simark.ca (Postfix, from userid 112) id 52BAC1F163; Sun, 13 Jun 2021 13:25:44 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-1.0 required=5.0 tests=MAILING_LIST_MULTI, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.2 Received: from sourceware.org (server2.sourceware.org [8.43.85.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id 1B46B1E01F for ; Sun, 13 Jun 2021 13:25:42 -0400 (EDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 91A24388C008 for ; Sun, 13 Jun 2021 17:25:41 +0000 (GMT) Received: from mail-wr1-f51.google.com (mail-wr1-f51.google.com [209.85.221.51]) by sourceware.org (Postfix) with ESMTPS id 8FCC23857004 for ; Sun, 13 Jun 2021 17:25:29 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 8FCC23857004 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=palves.net Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-wr1-f51.google.com with SMTP id y7so11779291wrh.7 for ; Sun, 13 Jun 2021 10:25:29 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:subject:from:to:cc:references:message-id:date :user-agent:mime-version:in-reply-to:content-language :content-transfer-encoding; bh=dsp/kVBQrJLUScxbJwezua4dP3S+ST07a7Zy0YM4IUs=; b=qJMupJlEzNr2z4SiTf7feIAQY+PZT9lapTnzOaaPpvDElUdc9+TeDy8EDgLUjU9Xw+ bWU15cg3/V1a/KhcXnFWlqObDH+261uN8WUn63RicK6nJohxc34yxvoFthb3lhhcRAPL gi1yHKnAKFPS+XpsKh6XK5ZyhtZWCzPoqw/pQnkGOZ1WBkBIeGsAU2s1F1iSNwUDFMAA TCxt+XaCuP5e1+tXpdbzRlDUd/eAMJPvfyW1hy1YIm4xWcLdrbJd29xf9xQTOtmFtZvA S87f+8bSuk8yf0NyihKfkRda4/LPfwJCuLRLMv3IKgAGztSzhtbnflME9CkHkSVjFdZY X1jg== X-Gm-Message-State: AOAM530qxyJ93iIJSxGEiqxTSsf67wHOO7pYo3h/lv0BDoC8GE5QmalG yxuzyK0/jL5nbfqanF2ouQQ= X-Google-Smtp-Source: ABdhPJzTTKLXBhaOQB01+azO+FE8y4592aFs1AxAS2jzd78KQ4WJ4FQBi28OP1shJRbHTmz4+a4Z5w== X-Received: by 2002:adf:fb92:: with SMTP id a18mr14512804wrr.182.1623605128316; Sun, 13 Jun 2021 10:25:28 -0700 (PDT) Received: from ?IPv6:2001:8a0:f932:6a00:6b6e:c7b6:c5a7:aac3? ([2001:8a0:f932:6a00:6b6e:c7b6:c5a7:aac3]) by smtp.gmail.com with ESMTPSA id o3sm17004429wrc.0.2021.06.13.10.25.27 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Sun, 13 Jun 2021 10:25:27 -0700 (PDT) Subject: [PATCH v3] Make the TUI command window support the mouse From: Pedro Alves To: Hannes Domani , Hannes Domani via Gdb-patches , Tom Tromey References: <20210603151453.15248-1-ssbssa.ref@yahoo.de> <20210603151453.15248-1-ssbssa@yahoo.de> <87r1hhoi03.fsf@tromey.com> <1760297979.5212997.1622816480782@mail.yahoo.com> <0936428e-80c4-c192-cdfd-817ff9bcfab0@palves.net> <877dj9o4ch.fsf@tromey.com> <3facd73a-bfea-d46c-85a7-101ea893998f@palves.net> <8cfc78b7-ecdc-b435-5c1f-f027c8704f9e@palves.net> <1231900744.5510610.1622904017896@mail.yahoo.com> <87wnr1h80t.fsf@tromey.com> <287323544.8784317.1623409369460@mail.yahoo.com> <1761936332.9225994.1623501145444@mail.yahoo.com> <6afbe909-91d6-af32-d496-ca112dcfb43a@palves.net> <455337ea-65c3-42b5-0ec0-3c85b85ba054@palves.net> <1500249367.9517443.1623589447782@mail.yahoo.com> Message-ID: <25580f20-76bc-4fd0-8426-5474d5ed5d22@palves.net> Date: Sun, 13 Jun 2021 18:25:26 +0100 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.11.0 MIME-Version: 1.0 In-Reply-To: <1500249367.9517443.1623589447782@mail.yahoo.com> Content-Type: text/plain; charset=utf-8 Content-Language: en-US Content-Transfer-Encoding: 8bit X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Joel Brobecker Errors-To: gdb-patches-bounces+public-inbox=simark.ca@sourceware.org Sender: "Gdb-patches" On 2021-06-13 2:04 p.m., Hannes Domani wrote: > Am Sonntag, 13. Juni 2021, 04:46:15 MESZ hat Pedro Alves Folgendes geschrieben: > > This is great, thank you for doing this. > > I just tried it, and I had just one problem, see below. > >> +      /* For the standard keys, we can find them efficiently in the >> +    key_xxx macros, defined by ncurses.  We could also hardcode >> +    sequences readline understands, and/or use rl_get_termcap. >> +    See readline/readline.c:bind_arrow_keys_internal for >> +    hardcoded sequences.  */ >> +      switch (ch) >> +    { >> +    case KEY_NPAGE: /* page down */ >> +      return start_sequence (key_npage); >> +    case KEY_PPAGE: /* page up */ >> +      return start_sequence (key_ppage); >> +    case KEY_DOWN: >> +      return start_sequence (key_down); >> +    case KEY_UP: >> +      return start_sequence (key_up); >> +    case KEY_RIGHT: >> +      return start_sequence (key_right); >> +    case KEY_LEFT: >> +      return start_sequence (key_left); >> +    case KEY_HOME: >> +      return start_sequence (key_home); >> +    case KEY_END: >> +      return start_sequence (key_end); >> +    case KEY_DC: /* del */ >> +      return start_sequence (key_dc); >> +    case KEY_IC: /* ins */ >> +      return start_sequence (key_ic); >> +    } > > PDcurses doesn't have these key_npage/key_ppage/key_down/... variables, > so I had to use the hardcoded sequences as you described. > This is working for the arrow keys and home/end, and for del I used '\004' > instead, but I didn't see any for page up/down and ins. Ack. \004 is ctrl-d ('d' & 0x1f). I'm a bit wary of hardcoding it, since I imagine there might be some users out where that have bound ctrl-d to some other action in their inputrc. readline's bind_arrow_keys_internal has: #if defined (__MINGW32__) rl_bind_keyseq_if_unbound ("\340H", rl_get_previous_history); rl_bind_keyseq_if_unbound ("\340P", rl_get_next_history); rl_bind_keyseq_if_unbound ("\340M", rl_forward_char); rl_bind_keyseq_if_unbound ("\340K", rl_backward_char); rl_bind_keyseq_if_unbound ("\340G", rl_beg_of_line); rl_bind_keyseq_if_unbound ("\340O", rl_end_of_line); rl_bind_keyseq_if_unbound ("\340S", rl_delete); rl_bind_keyseq_if_unbound ("\340R", rl_overwrite_mode); /* These may or may not work because of the embedded NUL. */ rl_bind_keyseq_if_unbound ("\\000H", rl_get_previous_history); rl_bind_keyseq_if_unbound ("\\000P", rl_get_next_history); rl_bind_keyseq_if_unbound ("\\000M", rl_forward_char); rl_bind_keyseq_if_unbound ("\\000K", rl_backward_char); rl_bind_keyseq_if_unbound ("\\000G", rl_beg_of_line); rl_bind_keyseq_if_unbound ("\\000O", rl_end_of_line); rl_bind_keyseq_if_unbound ("\\000S", rl_delete); rl_bind_keyseq_if_unbound ("\\000R", rl_overwrite_mode); #endif So would "\340S" and "\340R" work instead? See attached patch. > > I'm not sure if it's even a problem for page up/down, they seem to not be used > by readline, but I couldn't make ins work. Ins works for me on konsole, and on a linux virtual console. It does not work on xterm and rxvt. Nor on konsole + ssh. Nor under screen. I confirmed that I get the same key sequence for the insert key on all those cases (run "cat" + press the key). It's the same with bash and gdb in non-tui mode. It's not specific to this patch. I would guess readline is somehow finding that the terminal does not support overwrite mode. > > And rl_get_termcap returned for everything except "le" NULL, but I assume > that's just how it is on Windows. Unfortunate. Here's a new version. Does this one work for you? >From dca7d16331980842ec022ed83003fbf7cb90575e Mon Sep 17 00:00:00 2001 From: Pedro Alves Date: Sat, 5 Jun 2021 19:11:09 +0100 Subject: [PATCH v3] Make the TUI command window support the mouse In v3: - hardcode sequences instead of using ncurses's key_up etc. which don't exist on pdcurses. - drop page up/ page down and ctrl combinations - never pass non-ascii characters to readline - add missing NCURSES_MOUSE_VERSION check in tui_dispatch_mouse_event. - misc minor comment tweaks Currently, when the focus is on the command window, we disable the keypad. This means that when the command window has the focus, keys such as up/down/home/end etc. are not processed by curses, and their escape sequences go straight to readline. A side effect of disabling keypad mode is that wgetch no longer processes mouse escape sequences, with the end result being the mouse doesn't work, and worse, the raw mouse escape sequences are printed on the terminal. This commit makes the TUI command window support the mouse as well, by always enabling the keypad, and then to avoid losing support for up/down browsing the command history, home/end/left/right moving the cursor position, etc., we forward those keys as raw escape sequences to readline. Note we don't make an effort to pass down to readline all keys returned by curses, only the common ones that readline understands by default. Given users can specify their own readline bindings (inputrc file, bind utility), this approach is good in practice, though not 100% transparent or perfect. Note that the patch makes it so that CTLC-L is always passed to readline even if the command window does not have the focus. It was simpler to implement that way, and it just seems correct to me. I don't know of a reason we shouldn't do that. The patch improves the TUI behavior in a related way. Now we can pass special keys to readline irrespective of which window has the focus. First, we try to dispatch the key to a window, via tui_displatch_ctrl_char. If the key is dispatched, then we don't pass it to readline. E.g., pressing "up" when you have the source window in focus results in scrolling the source window, and nothing else. If however, you press ctrl-del instead, that results in killing the next word in the command window, no matter which window has has focus. Before, it would only work if you had the command window in focus. Similarly, ctrl-left/ctrl-right to move between words, etc. Similarly, the previous spot where we handled mouse events was incorrect. It was never reached if the window with focus can't scroll, which is the case for the command window. Mouse scrolling affects the window under the mouse cursor, not the window in focus. We now always try to dispatch mouse events. One last bit in the patch -- now if we don't recognize the non-ascii curses key, then we don't pass it down to readline at all. Before that would result in bogus characters in the input line. gdb/ChangeLog: yyyy-mm-dd Pedro Alves * tui/tui-io.c (tui_dispatch_mouse_event): New, factored out from ... (tui_dispatch_ctrl_char): ... this. Move CTRL-L handling to tui_getc_1. (cur_seq, start_sequence): New. (tui_getc_1): Pass key escape sequences for curses control keys to readline. Handle mouse and ctrl-l here. (tui_resize_all): Disable/reenable the keypad if the command window has the focus too. * tui/tui-win.c (tui_set_focus_command): Don't change keypad setting. * tui/tui.c (tui_rl_other_window): Don't change keypad setting. Change-Id: Ie0a7d849943cfb47f4a6589e1c73341563740fa9 --- gdb/tui/tui-io.c | 204 +++++++++++++++++++++++++++++++++++++--------- gdb/tui/tui-win.c | 12 +-- gdb/tui/tui.c | 5 +- 3 files changed, 169 insertions(+), 52 deletions(-) diff --git a/gdb/tui/tui-io.c b/gdb/tui/tui-io.c index 7df0e2f1bd3..d16a121401c 100644 --- a/gdb/tui/tui-io.c +++ b/gdb/tui/tui-io.c @@ -946,6 +946,40 @@ tui_initialize_io (void) #endif } +/* Dispatch the correct tui function based upon the mouse event. */ + +static void +tui_dispatch_mouse_event () +{ +#ifdef NCURSES_MOUSE_VERSION + MEVENT mev; + if (getmouse (&mev) != OK) + return; + + for (tui_win_info *wi : all_tui_windows ()) + if (mev.x > wi->x && mev.x < wi->x + wi->width - 1 + && mev.y > wi->y && mev.y < wi->y + wi->height - 1) + { + if ((mev.bstate & BUTTON1_CLICKED) != 0 + || (mev.bstate & BUTTON2_CLICKED) != 0 + || (mev.bstate & BUTTON3_CLICKED) != 0) + { + int button = (mev.bstate & BUTTON1_CLICKED) != 0 ? 1 + : ((mev.bstate & BUTTON2_CLICKED) != 0 ? 2 + : 3); + wi->click (mev.x - wi->x - 1, mev.y - wi->y - 1, button); + } +#ifdef BUTTON5_PRESSED + else if ((mev.bstate & BUTTON4_PRESSED) != 0) + wi->backward_scroll (3); + else if ((mev.bstate & BUTTON5_PRESSED) != 0) + wi->forward_scroll (3); +#endif + break; + } +#endif +} + /* Dispatch the correct tui function based upon the control character. */ static unsigned int @@ -953,10 +987,6 @@ tui_dispatch_ctrl_char (unsigned int ch) { struct tui_win_info *win_info = tui_win_with_focus (); - /* Handle the CTRL-L refresh for each window. */ - if (ch == '\f') - tui_refresh_all_win (); - /* If no window has the focus, or if the focus window can't scroll, just pass the character through. */ if (win_info == NULL || !win_info->can_scroll ()) @@ -984,39 +1014,6 @@ tui_dispatch_ctrl_char (unsigned int ch) case KEY_LEFT: win_info->right_scroll (1); break; -#ifdef NCURSES_MOUSE_VERSION - case KEY_MOUSE: - { - MEVENT mev; - if (getmouse (&mev) != OK) - break; - - for (tui_win_info *wi : all_tui_windows ()) - if (mev.x > wi->x && mev.x < wi->x + wi->width - 1 - && mev.y > wi->y && mev.y < wi->y + wi->height - 1) - { - if ((mev.bstate & BUTTON1_CLICKED) != 0 - || (mev.bstate & BUTTON2_CLICKED) != 0 - || (mev.bstate & BUTTON3_CLICKED) != 0) - { - int button = (mev.bstate & BUTTON1_CLICKED) != 0 ? 1 - : ((mev.bstate & BUTTON2_CLICKED) != 0 ? 2 - : 3); - wi->click (mev.x - wi->x - 1, mev.y - wi->y - 1, button); - } -#ifdef BUTTON5_PRESSED - else if ((mev.bstate & BUTTON4_PRESSED) != 0) - wi->backward_scroll (3); - else if ((mev.bstate & BUTTON5_PRESSED) != 0) - wi->forward_scroll (3); -#endif - break; - } - } - break; -#endif - case '\f': - break; default: /* We didn't recognize the character as a control character, so pass it through. */ @@ -1067,6 +1064,24 @@ tui_inject_newline_into_command_window () } } +/* If we're passing an escape sequence to readline, this points to a + string holding the remaining characters of the sequence to pass. + We advance the pointer one character at a time until '\0' is + reached. */ +static const char *cur_seq = nullptr; + +/* Set CUR_SEQ to point at the current sequence to pass to readline, + setup to call the input handler again so we complete the sequence + shortly, and return the first character to start the sequence. */ + +static int +start_sequence (const char *seq) +{ + call_stdin_event_handler_again_p = 1; + cur_seq = seq + 1; + return seq[0]; +} + /* Main worker for tui_getc. Get a character from the command window. This is called from the readline package, but wrapped in a try/catch by tui_getc. */ @@ -1084,11 +1099,115 @@ tui_getc_1 (FILE *fp) tui_readline_output (0, 0); #endif - ch = gdb_wgetch (w); + /* We enable keypad mode so that curses's wgetch processes mouse + escape sequences. In keypad mode, wgetch also processes the + escape sequences for keys such as up/down etc. and returns KEY_UP + / KEY_DOWN etc. When we have the focus on the command window + though, we want to pass the raw up/down etc. escape codes to + readline so readline understands them. */ + if (cur_seq != nullptr) + { + ch = *cur_seq++; + + /* If we've reached the end of the string, we're done with the + sequence. Otherwise, setup to get back here again for + another character. */ + if (*cur_seq == '\0') + cur_seq = nullptr; + else + call_stdin_event_handler_again_p = 1; + return ch; + } + else + ch = gdb_wgetch (w); /* Handle prev/next/up/down here. */ ch = tui_dispatch_ctrl_char (ch); - + +#ifdef NCURSES_MOUSE_VERSION + if (ch == KEY_MOUSE) + { + tui_dispatch_mouse_event (); + return 0; + } +#endif + + /* Translate curses keys back to escape sequences so that readline + can understand them. We do this irrespective of which window has + the focus. If e.g., we're focused on a non-command window, then + the up/down keys will already have been filtered by + tui_dispatch_ctrl_char. Keys that haven't been intercepted will + be passed down to readline. */ + if (current_ui->command_editing) + { + /* For the standard arrow keys + home/end, hardcode sequences + readline understands. See bind_arrow_keys_internal in + readline/readline.c. */ + switch (ch) + { + case KEY_UP: + return start_sequence ("\033[A"); + case KEY_DOWN: + return start_sequence ("\033[B"); + case KEY_RIGHT: + return start_sequence ("\033[C"); + case KEY_LEFT: + return start_sequence ("\033[D"); + case KEY_HOME: + return start_sequence ("\033[H"); + case KEY_END: + return start_sequence ("\033[F"); + + /* del and ins are unfortunately not hardcoded in readline for + all systems. */ + + case KEY_DC: /* del */ +#ifdef __MINGW32__ + return start_sequence ("\340S"); +#else + return start_sequence ("\e[3~"); +#endif + + case KEY_IC: /* ins */ +#if defined __MINGW32__ + return start_sequence ("\340R"); +#else + return start_sequence ("\e[2~"); +#endif + } + + /* Keycodes above KEY_MAX are not garanteed to be stable. + Compare keyname instead. */ + if (ch >= KEY_MAX) + { + auto name = gdb::string_view (keyname (ch)); + + /* The following sequences are hardcoded in readline as + well. */ + + /* ctrl-arrow keys */ + if (name == "kLFT5") /* ctrl-left */ + return start_sequence ("\033[1;5D"); + else if (name == "kRIT5") /* ctrl-right */ + return start_sequence ("\033[1;5C"); + else if (name == "kDC5") /* ctrl-del */ + return start_sequence ("\033[3;5~"); + + /* alt-arrow keys */ + else if (name == "kLFT3") /* alt-left */ + return start_sequence ("\033[1;3D"); + else if (name == "kRIT3") /* alt-right */ + return start_sequence ("\033[1;3C"); + } + } + + /* Handle the CTRL-L refresh for each window. */ + if (ch == '\f') + { + tui_refresh_all_win (); + return ch; + } + if (ch == KEY_BACKSPACE) return '\b'; @@ -1118,6 +1237,13 @@ tui_getc_1 (FILE *fp) } } + if (ch >= 255) + { + /* Readline doesn't understand non-ascii curses keys, filter + them out. */ + return 0; + } + return ch; } diff --git a/gdb/tui/tui-win.c b/gdb/tui/tui-win.c index 4e75a66a00e..a51e7b9f6da 100644 --- a/gdb/tui/tui-win.c +++ b/gdb/tui/tui-win.c @@ -498,14 +498,11 @@ tui_resize_all (void) height_diff = screenheight - tui_term_height (); if (height_diff || width_diff) { - struct tui_win_info *win_with_focus = tui_win_with_focus (); - #ifdef HAVE_RESIZE_TERM resize_term (screenheight, screenwidth); #endif /* Turn keypad off while we resize. */ - if (win_with_focus != TUI_CMD_WIN) - keypad (TUI_CMD_WIN->handle.get (), FALSE); + keypad (TUI_CMD_WIN->handle.get (), FALSE); tui_update_gdb_sizes (); tui_set_term_height_to (screenheight); tui_set_term_width_to (screenwidth); @@ -515,10 +512,8 @@ tui_resize_all (void) erase (); clearok (curscr, TRUE); tui_apply_current_layout (); - /* Turn keypad back on, unless focus is in the command - window. */ - if (win_with_focus != TUI_CMD_WIN) - keypad (TUI_CMD_WIN->handle.get (), TRUE); + /* Turn keypad back on. */ + keypad (TUI_CMD_WIN->handle.get (), TRUE); } } @@ -703,7 +698,6 @@ tui_set_focus_command (const char *arg, int from_tty) error (_("Window \"%s\" is not visible"), arg); tui_set_win_focus_to (win_info); - keypad (TUI_CMD_WIN->handle.get (), win_info != TUI_CMD_WIN); printf_filtered (_("Focus set to %s window.\n"), tui_win_with_focus ()->name ()); } diff --git a/gdb/tui/tui.c b/gdb/tui/tui.c index 529fc62c9ac..5f0c87c05e1 100644 --- a/gdb/tui/tui.c +++ b/gdb/tui/tui.c @@ -179,10 +179,7 @@ tui_rl_other_window (int count, int key) win_info = tui_next_win (tui_win_with_focus ()); if (win_info) - { - tui_set_win_focus_to (win_info); - keypad (TUI_CMD_WIN->handle.get (), win_info != TUI_CMD_WIN); - } + tui_set_win_focus_to (win_info); return 0; } base-commit: ad9cc2097049a04dc6fa0a593a59f4a3b4807c6f -- 2.26.2