gitui icon indicating copy to clipboard operation
gitui copied to clipboard

listen for SIGINT signal instead of exit key bind

Open crabdancing opened this issue 2 years ago • 13 comments

Describe the bug A clear and concise description of what the bug is.

To Reproduce Steps to reproduce the behavior: Remap Ctrl+Q to quit, with Ctrl+C being now available for copy/paste keybindings in terminal config:

stty intr ^Q
stty -ixon
stty -ixoff

Expected behavior Ctrl+Q should now be interpreted as SIGINT. This works for most applications, but because GitUI enables raw terminal mode, it is up to GitUI to manually respect system settings.

Context (please complete the following information):

  • OS/Distro + Version: NixOS 23.11
  • GitUI Version: gitui 0.24.3
  • Rust version: unknown

crabdancing avatar Dec 12 '23 01:12 crabdancing

GitUI is hard coded to quit on Ctrl+C

it is not hard coded to do that. you can configure the key bindings. but indeed it would be interesting to honor the signal SIGINT instead of a custom key handler. that should be doable

extrawurst avatar Dec 15 '23 23:12 extrawurst

Ah, my apologies. :)

crabdancing avatar Dec 17 '23 08:12 crabdancing

I’ll try to create a PR in the next couple days!

cruessler avatar Dec 30 '23 19:12 cruessler

@cruessler i am not sure if this is really possible with the terminal mode we use. please checkout my branch signal-handler-to-quit to see how far I got - or not :)

extrawurst avatar Dec 30 '23 20:12 extrawurst

@extrawurst I just checked out your branch and found that gitui indeed responded to SIGINT by quitting and logging signal received. exiting when I sent it a signal using kill -INT <pid>. So the issue as described in the title seems already solved. Am I correct in assuming that this issue is less about responding to SIGINT, but rather about gitui somehow not reacting to an alternative shortcut that is supposed to send SIGINT?

cruessler avatar Feb 23 '24 19:02 cruessler

Really? It’s been a while, no idea anymore why I thought it does not work 😉 Maybe terminal specific 🤔

extrawurst avatar Feb 23 '24 20:02 extrawurst

For context, I'm using Wezterm (20240203-110809-5046fc22). My Ctrl+Q keybindings still do not work, but Ctrl+C does, which is counter to my configuration.

crabdancing avatar Feb 23 '24 23:02 crabdancing

To maybe get inspiration from another program on how to implement this in an elegant way, I first ran stty intr ^Q and stty start ^C, then opened less, man and tig. None of them seemed to close on ^Q, so I’m wondering whether my setup contains anything that prevents ^Q from working.

cruessler avatar Mar 19 '24 19:03 cruessler

I think it's just very common for applications that put the terminal in raw mode to hardcode Ctrl+C and call it a day. It sets a really bad precedent, IMO. Some applications don't even respond to the Ctrl+C standard. On my system, man responds to q, but not Ctrl+C or Ctrl+Q (regardless of stty intr config). And others IIRC respond to Esc, but not q or Ctrl+C or Ctrl+Q. The problem is so irritating that I'm actually considering writing a wrapper to translate the keypresses to misbehaving applications.

crabdancing avatar Mar 23 '24 02:03 crabdancing

It doesn't seem too hard to detect terminal state and then decide which key to interpret as interrupt in raw mode based on that. I could write a mockup program with this behavior, if it helps.

crabdancing avatar Mar 23 '24 02:03 crabdancing

That would help. Or point me to a stackoverflow or any precedence

extrawurst avatar Mar 23 '24 03:03 extrawurst

Here's a minimal example:

use std::io::{self, Read};
use std::os::unix::io::AsRawFd;
use termios::*;

fn main() {
    let stdin_fd = io::stdin().as_raw_fd();
    let original_term_config = Termios::from_fd(stdin_fd).unwrap();

    let interrupt_char = original_term_config.c_cc[VINTR];

    let mut raw_termios = original_term_config;
    cfmakeraw(&mut raw_termios);
    tcsetattr(stdin_fd, TCSANOW, &mut raw_termios).unwrap();

    println!("Press the interrupt character to exit...\r");

    let mut buffer = [0u8; 1];
    loop {
        io::stdin().read_exact(&mut buffer).unwrap();
        eprintln!("Got: {:?}\r", &buffer);
        if buffer[0] == interrupt_char {
            break;
        }
    }

    tcsetattr(stdin_fd, TCSANOW, &original_term_config).unwrap();
}

If you use do stty intr ^Q, it will quit when you hit Ctrl+Q, and if you do stty intr ^C, it will quit when you do Ctrl+C. Edge cases seem to work too -- if you do stty intr q it will quit on q.

crabdancing avatar Mar 24 '24 04:03 crabdancing

Thanks for the example!

termios and crossterm encode keys differently. I don’t know whether there is an obvious way to map c_cc[VINTR] to code in KeyEvent (which is what GitUI reacts to). c_cc[VINTR] seems to map a to 1, b to 2 etc.

https://docs.rs/crossterm/latest/crossterm/event/enum.KeyCode.html

cruessler avatar Mar 25 '24 17:03 cruessler