Differentiate clipboard event when using read() function in c from stdin

Viewed 89

I am working on terminal based text editor and stuck at a point where I need to differentiate whether the input text from read() function is a clipboard paste text or a keyboard text input.

#include <unistd.h>

char read_key_input() {
    char ch;
    int read_count;

    while ((read_count = read(STDIN_FILENO, &ch, 1)) != 1)
    {
        // Handle error if any
    }
    
    return ch;
}

...

Edit: Here's what I ended up doing.

void read_key_input(void (*callback)(int)) {
    char* text = malloc(UINT8_MAX);
    int read_count;

    while ((read_count = read(STDIN_FILENO, text, UINT8_MAX)) == -1)
    {
        // Handle error if any
    }

    // Handle escape sequences if found and return early
    ...

    if (read_count > 1) {
        // It's probably a clipboard text. So change the editor mode to input and loop through all the characters one by one.
    else {
        // It's a user keyboard text input
    }

    // Revert back to the original editor mode if changed
}

I updated the code to retrieve more than one byte at a time (as suggested by @AnttiHaapala) and process each byte. Seems to be sufficient for my text editor's need for now. Will post back if I update.

3 Answers

Usually you can differentiate this by counting the number of characters you've received in rapid succession. So if you get the keypresses more rapidly than say 1000 characters per minute, then it is likely a clipboard paste... or nonsense.

Furthermore, if you've set the terminal to raw mode, then you can easily monitor individual keypresses. Also make read accept more than one byte at once - with read that is the maximum number of bytes to receive without blocking when available anyway.


One example of such an interactive terminal program would be IPython - here two lines typed separately:

In [1]: print("Hello")
Hello

In [2]: print("World")
World

And here pasted in one go:

In [3]: print("Hello")
   ...: print("World")
   ...:
Hello
World

Notice how the prompt is different, and the program runs only after there has been a separate Enter key hit after a small delay.

AFAIK, you cannot do what you want (reliably).

The clipboard is related (usually) to some display server, e.g. Xorg or Wayland server (Weston). And X11 might have distant clients (hence, a clipboard operation could be slow, if crossing some ocean).

Some Linux machines (perhaps the web server at Stackoverflow) do not run any display server.

You could code a GUI application, e.g. using GTK or Qt.

You could test if your standard input is a terminal with termios(3) functions or isatty(3) (i.e. with isatty(STDIN_FILENO) or isatty(STDOUT_FILENO) for standard output)

If your program is run inside a crontab(1) job, or a unix pipeline, the standard input won't be a terminal, and there might not even be any display server running.

You could take inspiration from the source code of GNU emacs, which does detect when a display server is available (probably using environ(7), e.g. getenv(3) of "DISPLAY"...)

On Linux you might open(2) /dev/tty (see tty(4)) to access your terminal. In some cases, it does not exist.

Hey @jiten not sure if you checked like its key input detect and check wether it's input is one by one key or its instant bulk input.

Related