Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ctrl + I and Tab #267

Open
TobiasWallner opened this issue Jun 27, 2023 · 6 comments
Open

Ctrl + I and Tab #267

TobiasWallner opened this issue Jun 27, 2023 · 6 comments

Comments

@TobiasWallner
Copy link
Contributor

TobiasWallner commented Jun 27, 2023

In my editor I wanted to use the key combinations Ctrl + J/I/L/K to move the cursor left/up/right/down. Kind of like WASD in computer games but with the other hand.

I noticed that when pressing the TAB key, Ctrl + I will be sent to the terminal instead.

Is this a bug? I am not sure, because in some forums I read that those two are sometimes mapped over ontop of each other. I really want to be able to use Ctrl + I and TAB.

-------- from this point on I start to get ahead of my own question -------

Furthermore I would also like to be able to detect Ctrl + Shift + S or even Ctrl + Shift + Alt + O, or other arbitrary key kombinations. As I understand the current state of cpp-terminal, this is not possible, right - telll me if I am wrong?

I don't know about linux, but for windows I found: GetAsyncKeyState

And kind of started to write my own Keyboard-Scanner that can differentiate between TAB and Ctrl + I.
Before, I spend any more time developing in this direction I wanted to ask you, if you have already considered something like this in the past? or are developing on this? or did and found that it does not work or that there does not exist an equivalent for linux or other target platforms? Or too much complexity and lack of testing capacity with different keybord layouts and languages? Or anything else I cannot think of right now?

@TobiasWallner
Copy link
Contributor Author

TobiasWallner commented Jun 28, 2023

In your cpp-terminal/platform/input.cpp, I just tried to retrieve the key press status of the controll keys (CTRL, ALT, SHIFT, CAPSLOCK) in addition to the message that is being sent to the terminal, by inserting the following code down below. Note that they have to be read before the messages are removed from the input stream.

It works quite nicely. I would propose to add the bolean flags: isCTRL, isALT, isSHIFT, isCAPSLOCK to the Term::Key class and pass them to the Key event and prepozess the value of the key event so that it only contains the utf8 code of the key press. For example when CTRL+I has been pressed. CTRL will only be saved in the isCTRL flag and the Key::Value would only store the 'I'. That way we would be able to easily distinguish between CTRL+I and Tab or CTRL+M and Enter. It would also allow us to make more complex KeyPress combinations, for example CTRL+SHIFT+S which is commonly used to save all open documents.

I would like to hear what you think about this.

// ++++++++++ new code here ++++++++++
        const short down_mask = 0x8000;
	const short toggle_mask = 0x00001;
	const bool isCTRL = (GetKeyState(VK_CONTROL) & down_mask) != 0; 
	const bool isALT = (GetKeyState(VK_MENU) & down_mask) != 0; 
	const bool isSHIFT = (GetKeyState(VK_SHIFT) & down_mask) != 0; 
	const bool isCAPSLOCK = (GetKeyState(VK_CAPITAL) & toggle_mask) != 0; 
// +++++++++++ end +++++++++++

	if(!ReadConsoleInputW(Private::std_cin.getHandler(), &buf[0], buf.size(), &nre)) { Term::Exception("ReadFile() failed"); }
    for(std::size_t i = 0; i != nre; ++i)
    {
      switch(buf[i].EventType)
      {
        case KEY_EVENT:
        {
          //if(buf[i].Event.KeyEvent.wVirtualKeyCode != 0) break;  //skip them for now
          if(buf[i].Event.KeyEvent.bKeyDown)
          {
            std::size_t size_needed = WideCharToMultiByte(CP_UTF8, 0, &buf[i].Event.KeyEvent.uChar.UnicodeChar, -1, NULL, 0, NULL, NULL);
            std::string strTo(size_needed, '\0');
            WideCharToMultiByte(CP_UTF8, 0, &buf[i].Event.KeyEvent.uChar.UnicodeChar, 1, &strTo[0], size_needed, NULL, NULL);
            ret += strTo.c_str();
			
// +++++++++++++ new code here +++++++++++
           std::cout << "isCTRL: " << isCTRL << std::endl;
	   std::cout << "isALT: " << isALT << std::endl;
	   std::cout << "isSHIFT: " << isSHIFT << std::endl;
	   std::cout << "isCAPSLOCK: " << isCAPSLOCK << std::endl;
	   std::cout << "key: " << strTo.c_str() << std::endl;
// +++++++++++++++ end ++++++++++++++

sry the indentation is a little out of place

@TobiasWallner
Copy link
Contributor Author

That could be an alternative for the Linux side of the code: getKeyState
(But I am not as knowledgable, so I do not know if this is useful)

@flagarde
Copy link
Collaborator

@TobiasWallner https://linux.die.net/man/3/wx_misc is not included in all Linux system I think. For windows it is possible to obtain this information directly in KEY_EVENT_RECORD but then the library would not be cross-platform because the user could ask some key combination that can be split in two distinct cases (TAB and CTRL_I) while in Linux it is not possible for now. Maybe reading keycode directly but this need to be investigate

@TobiasWallner
Copy link
Contributor Author

@flagarde I know StackOverflow is not always the best ressource, but I found this for linux control keys and it sounds promising.
https://stackoverflow.com/a/20946151

#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <linux/input.h>
#include <string.h>
#include <stdio.h>

static const char *const evval[3] = {
    "RELEASED",
    "PRESSED ",
    "REPEATED"
};

int main(void)
{
    const char *dev = "/dev/input/by-path/platform-i8042-serio-0-event-kbd";
    struct input_event ev;
    ssize_t n;
    int fd;

    fd = open(dev, O_RDONLY);
    if (fd == -1) {
        fprintf(stderr, "Cannot open %s: %s.\n", dev, strerror(errno));
        return EXIT_FAILURE;
    }
    while (1) {
        n = read(fd, &ev, sizeof ev);
        if (n == (ssize_t)-1) {
            if (errno == EINTR)
                continue;
            else
                break;
        } else
        if (n != sizeof ev) {
            errno = EIO;
            break;
        }
        if (ev.type == EV_KEY && ev.value >= 0 && ev.value <= 2)
            printf("%s 0x%04x (%d)\n", evval[ev.value], (int)ev.code, (int)ev.code);
    }
    fflush(stdout);
    fprintf(stderr, "%s.\n", strerror(errno));
    return EXIT_FAILURE;
}
        

And I found this for wayland. Although complicated looking the C code is fairly small https://wayland-book.com/seat/keyboard.html

#include <sys/mman.h>
// ...

static void wl_keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard,
        uint32_t format, int32_t fd, uint32_t size) {
    assert(format == WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1);
    struct my_state *state = (struct my_state *)data;

    char *map_shm = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
    assert(map_shm != MAP_FAILED);

    struct xkb_keymap *keymap = xkb_keymap_new_from_string(
        state->xkb_context, map_shm, XKB_KEYMAP_FORMAT_TEXT_V1,
        XKB_KEYMAP_COMPILE_NO_FLAGS);
    munmap(map_shm, size);
    close(fd);

    // ...do something with keymap...
}

@flagarde
Copy link
Collaborator

@TobiasWallner yes I have look into it but it seems you need to be sudo to read from the fd

@TobiasWallner
Copy link
Contributor Author

TobiasWallner commented Jul 2, 2023

@flagarde I found a project, logkeys, that may can do on linux something similar to what windows does with KEY_EVENT_RECORD. It is a key logger and can distinguish between different key presses and controll keys. Especially this structure key_state_t looks very simmilar to the one that windows uses with KEY_EVENT_RECORD . Do you think that this could help us?

or maybe the following:

Maybe reading keycode directly but this need to be investigate

Grab Raw Keyboard Input from Event Device Node (/dev/input/event)
crossed it out because I think this also needs root priveleges

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants