简体   繁体   English

SDL2 无法捕获控制台键盘事件?

[英]SDL2 cannot capture console keyboard events?

TL;DR TL; 博士

I am trying to capture keyboard events (more specifically, the Ctrl+c command) in my own C++ program.我试图在我自己的 C++ 程序中捕获键盘事件(更具体地说,Ctrl+c 命令)。 I am attempting this through generic keyboard presses in SDL2.我正在通过 SDL2 中的通用键盘按键尝试此操作。

END TL;DR结束 TL;DR

I have found links on SO and the internet that cover the subject of handling keyboard events with SDL2.我在 SO 和互联网上找到了涵盖使用 SDL2 处理键盘事件主题的链接。 I have a few of them listed here.我在这里列出了其中的一些。

https://stackoverflow.com/questions/28105533/sdl2-joystick-dont-capture-pressed-event
https://lazyfoo.net/tutorials/SDL/04_key_presses/index.php
http://www.cplusplus.com/forum/windows/182214/
http://gigi.nullneuron.net/gigilabs/handling-keyboard-and-mouse-events-in-sdl2/

The major issue I think is causing the problem is that I am also using an Xbox-style joystick at the same time.我认为导致问题的主要问题是我同时也在使用 Xbox 风格的操纵杆。 I have had no issues whatsoever with capturing joystick events.我在捕获操纵杆事件方面没有任何问题。 I have been doing that for a long time now.我已经这样做了很长时间了。 I am having issues trying to get anything with the keyboard to throw an event.我在尝试使用键盘获取任何内容以引发事件时遇到问题。 I have tried if(event.type == SDL_KEYDOWN) and then checking which key it was, but that appears to return nothing.我试过if(event.type == SDL_KEYDOWN)然后检查它是哪个键,但似乎没有返回任何内容。 I feel like there is some macro that I need to define to allow this since I keep finding the same solutions on the internet.我觉得我需要定义一些宏来允许这样做,因为我一直在互联网上找到相同的解决方案。

I have included the entire script that I am running at the moment.我已经包含了我目前正在运行的整个脚本。


#include <boost/thread.hpp>
// Time library
#include <chrono>
// vector data structure
#include <vector>
// Thread-safe base variables
#include <atomic>
// std::cout
#include <iostream>
// Joystick library
#include <SDL2/SDL.h>
// Counters for printing
std::atomic_int printcounter{ 0 };
// This is every 3 * 1000 milliseconds
const int printer = 300;
// If an event is found, allow for printing.
std::atomic_bool eventupdate{ false };
// This function converts the raw joystick axis from the SDL library to proper double precision floating-point values.
double intToDouble(int input)
{
    return (double) input / 32767.0 ;
}
// Prevent joystick values from going outside the physical limit
double clamp(double input)
{
    return (input < -1.0) ? -1.0 : ( (input > 1.0) ? 1.0 : input);
}
// SDL library joystick deadband
const int JOYSTICK_DEAD_ZONE = 5000;
// These are the raw read in values from the joystick in XInput (XBox) mode.
//Normalized direction
int leftX = 0;
int leftY = 0;
int rightX = 0;
int rightY = 0;
int leftTrigger  = -32768;
int rightTrigger = -32768;
// Button array
uint buttons[11] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
// Tbe pov hat is only 4 bits - 1, 2, 4, 8
int povhat = 0;
// These are the rectified joystick values
double leftstickx = 0;
double leftsticky = 0;
double rightstickx = 0;
double rightsticky = 0;
double lefttrigger = 0;
double righttrigger = 0;
// These are the rectified boolean buttons
bool leftstickbut = false;
bool rightstickbut = false;
bool xbutton = false;
bool ybutton = false;
bool abutton = false;
bool bbutton = false;
bool rightbut = false;
bool leftbut = false;
bool startbut = false;
bool backbut = false;
bool centbut = false;

// This is the boolean that controls running the robot.
std::atomic_bool quitrobot{false};
// Joystick values
static double joyvalues[6] = { 0, 0, 0, 0, 0, 0};
static bool joybuttons[11] = { false };
// Sleep function
void wait(int milliseconds)
{
    boost::this_thread::sleep_for(boost::chrono::milliseconds{milliseconds});
}
// Now the main code
int main(int argc, char** argv)
{
    // Now the robot goes through the looping code until a quit flag is set to true
    while ( ! quitrobot)
    {
        // Now we look for an Xbox-style joystick
        std::cout << "Looking for gamepad..." << std::endl;
        while(true)
        {
            // Now the program waits until an Xbox-style joystick is plugged in.
            // resetting SDL makes things more stable
            SDL_Quit();
            // restart SDL with the expectation that a jostick is required.
            SDL_Init(SDL_INIT_JOYSTICK);
            // SDL_HINT_GRAB_KEYBOARD
            // check for a joystick
            int res = SDL_NumJoysticks();
            if (res > 0) { break; } // Here a joystick has been detected.
            if (res < 0)
            {
                std::cout << "Joystick detection error: " << std::to_string(res) << std::endl;
            }
            // we don't want the program running super fast when detecting hardware.
            wait(20);
        }
        // Now we check to make sure that the joystick is valid.
        // Open the joystick for reading and store its handle in the joy variable
        SDL_Joystick *joy = SDL_JoystickOpen(0);
        if (joy == NULL) {
            /* back to top of while loop */
            continue;
        }
        // Get information about the joystick
        const char *name = SDL_JoystickName(joy);
        const int num_axes = SDL_JoystickNumAxes(joy);
        const int num_buttons = SDL_JoystickNumButtons(joy);
        const int num_hats = SDL_JoystickNumHats(joy);
        printf("Now reading from joystick '%s' with:\n"
            "%d axes\n"
            "%d buttons\n"
            "%d hats\n\n",
            name,
            num_axes,
            num_buttons,
            num_hats);
        /* I'm using a logitech F350 wireless in X mode.
        If num axis is 4, then gamepad is in D mode, so neutral drive and wait for X mode.
        [SAFETY] This means 'D' becomes our robot-disable button.
        This can be removed if that's not the goal. */
        if (num_axes < 5) {
            /* back to top of while loop */
            continue;
        }
        // This is the read joystick and drive robot loop.
        while(true)
        {
            // poll for disconnects or bad things
            SDL_Event e;
            if (SDL_PollEvent(&e)) {
                // SDL generated quit command
                if (e.type == SDL_QUIT) { break; }
                // Checking for Ctrl+c on the keyboard
                // SDL_Keymod modstates = SDL_GetModState();
                // if (modstates & KMOD_CTRL)
                // {
                    // One of the Ctrl keys are being held down
                    // std::cout << "Pressed Ctrl key." << std::endl;
                // }
                if(e.key.keysym.scancode == SDLK_RCTRL || e.key.keysym.scancode == SDLK_LCTRL || SDL_SCANCODE_RCTRL == e.key.keysym.scancode || e.key.keysym.scancode == SDL_SCANCODE_LCTRL)
                {
                    std::cout << "Pressed QQQQ." << std::endl;
                }
                if (e.type == SDL_KEYDOWN)
                {
                    switch(e.key.keysym.sym){ 
                        case SDLK_UP: 
                            std::cout << "Pressed up." << std::endl;
                            break; 
                        case SDLK_RCTRL: 
                            std::cout << "Pressed up." << std::endl;
                            break; 
                        case SDLK_LCTRL: 
                            std::cout << "Pressed up." << std::endl;
                            break; 
                    }
                    // Select surfaces based on key press
                    switch( e.key.keysym.sym )
                    {
                        case SDLK_UP:
                            std::cout << "Pressed Up." << std::endl;
                        break;

                        case SDLK_DOWN:
                            std::cout << "Pressed Up." << std::endl;
                        break;

                        case SDLK_LEFT:
                            std::cout << "Pressed Up." << std::endl;
                        break;

                        case SDLK_RIGHT:
                            std::cout << "Pressed Up." << std::endl;
                        break;
                    }
                    std::cout << "Pressed blah di blah blah please print me." << std::endl;
                }
                // Checking which joystick event occured
                if (e.jdevice.type == SDL_JOYDEVICEREMOVED) { break; }
                // Since joystick is not erroring out, we can 
                else if( e.type == SDL_JOYAXISMOTION )
                {
                    //Motion on controller 0
                    if( e.jaxis.which == 0 )
                    {
                        // event happened
                        eventupdate = true;
                        // Left X axis 
                        if( e.jaxis.axis == 0 )
                        {
                            // dead zone check
                            if( e.jaxis.value > -JOYSTICK_DEAD_ZONE && e.jaxis.value < JOYSTICK_DEAD_ZONE)
                            {
                                leftX = 0;
                            }
                            else
                            {
                                leftX = e.jaxis.value;
                            }
                        }
                        // Right Y axis
                        else if( e.jaxis.axis == 1 )
                        {
                            // dead zone check
                            if( e.jaxis.value > -JOYSTICK_DEAD_ZONE && e.jaxis.value < JOYSTICK_DEAD_ZONE)
                            {
                                leftY = 0;
                            }
                            else
                            {
                                leftY = e.jaxis.value;
                            }
                        }
                        // Left trigger
                        else if ( e.jaxis.axis == 2 )
                        {
                            // dead zone check
                            if( e.jaxis.value > -JOYSTICK_DEAD_ZONE && e.jaxis.value < JOYSTICK_DEAD_ZONE)
                            {
                                leftTrigger = 0;
                            }
                            else
                            {
                                leftTrigger = e.jaxis.value;
                            }
                        }
                        // Right X axis
                        else if( e.jaxis.axis == 3 )
                        {
                            // dead zone check
                            if( e.jaxis.value > -JOYSTICK_DEAD_ZONE && e.jaxis.value < JOYSTICK_DEAD_ZONE)
                            {
                                rightX = 0;
                            }
                            else
                            {
                                rightX = e.jaxis.value;
                            }
                        }
                        // Right Y axis
                        else if( e.jaxis.axis == 4 )
                        {
                            // dead zone check
                            if( e.jaxis.value > -JOYSTICK_DEAD_ZONE && e.jaxis.value < JOYSTICK_DEAD_ZONE)
                            {
                                rightY = 0;
                            }
                            else
                            {
                                rightY = e.jaxis.value;
                            }
                        }
                        // Right trigger
                        else if( e.jaxis.axis == 5 )
                        {
                            // dead zone check
                            if( e.jaxis.value > -JOYSTICK_DEAD_ZONE && e.jaxis.value < JOYSTICK_DEAD_ZONE)
                            {
                                rightTrigger = 0;
                            }
                            else
                            {
                                rightTrigger = e.jaxis.value;
                            }
                        }
                    }
                }
                else if ( e.type == SDL_JOYBUTTONUP || e.type == SDL_JOYBUTTONDOWN )
                {
                    
                    // now we are looking for button events.
                    if (e.jbutton.which == 0)
                    {
                        // event happened
                        eventupdate = true;
                        // buttons[e.jbutton.button] = e.jbutton.state;
                        if (e.jbutton.button == 0)
                        {
                            buttons[0] = e.jbutton.state;
                        }
                        if (e.jbutton.button == 1)
                        {
                            buttons[1] = e.jbutton.state;
                        }
                        if (e.jbutton.button == 2)
                        {
                            buttons[2] = e.jbutton.state;
                        }
                        if (e.jbutton.button == 3)
                        {
                            buttons[3] = e.jbutton.state;
                        }
                        if (e.jbutton.button == 4)
                        {
                            buttons[4] = e.jbutton.state;
                        }
                        if (e.jbutton.button == 5)
                        {
                            buttons[5] = e.jbutton.state;
                        }
                        if (e.jbutton.button == 6)
                        {
                            buttons[6] = e.jbutton.state;
                        }
                        if (e.jbutton.button == 7)
                        {
                            buttons[7] = e.jbutton.state;
                        }
                        if (e.jbutton.button == 8)
                        {
                            buttons[8] = e.jbutton.state;
                        }
                        if (e.jbutton.button == 9)
                        {
                            buttons[9] = e.jbutton.state;
                        }
                        if (e.jbutton.button == 10)
                        {
                            buttons[10] = e.jbutton.state;
                        }
                        if (e.jbutton.button == 11)
                        {
                            buttons[11] = e.jbutton.state;
                        }
                    }
                }
                else if ( e.type == SDL_JOYHATMOTION)
                {
                    if (e.jhat.which == 0)
                    {
                        // event happened
                        eventupdate = true;
                        povhat = e.jhat.value;
                    }
                }
            }

            // Now that we have read in the values directly from the joystick we need top convert the values properly.
            leftstickx = clamp(intToDouble(leftX));
            leftsticky = clamp(intToDouble(leftY));
            rightstickx = clamp(intToDouble(rightX));
            rightsticky = clamp(intToDouble(rightY));
            lefttrigger = clamp(intToDouble(leftTrigger));
            righttrigger = clamp(intToDouble(rightTrigger));
            // rectify the buttons to become boolean values instead of integers.
            abutton = buttons[0] > 0;
            bbutton = buttons[1] > 0;
            xbutton = buttons[2] > 0;
            ybutton = buttons[3] > 0;
            //
            rightbut = buttons[4] > 0;
            leftbut  = buttons[5] > 0;
            //
            centbut = buttons[8] > 0;
            startbut = buttons[7] > 0;
            backbut = buttons[6] > 0;
            //
            leftstickbut = buttons[9] > 0;
            rightstickbut = buttons[10] > 0;

            // Transfer axis to the array.
            joyvalues[0] = leftstickx;
            joyvalues[1] = leftsticky;
            joyvalues[2] = rightstickx;
            joyvalues[3] = rightsticky;
            joyvalues[4] = lefttrigger;
            joyvalues[5] = righttrigger;
            // We are using the "B" button to quit the program
            if (bbutton)
            {
                quitrobot = true;
                std::cout << "Shutting down program." << std::endl;
                break;
            }
            if (eventupdate)                    
            {
                // This section of code is meant for running code that happens when SDL has detected an event.
                // This code section can be used for something else as well.
                if (e.key.keysym.sym == SDL_SCANCODE_RCTRL || e.key.keysym.sym == SDL_SCANCODE_LCTRL || e.key.keysym.sym == SDLK_LCTRL || e.key.keysym.sym == SDLK_RCTRL)
                {
                    std::cout << "SDL Event: Ctrl pressed.\n" << std::endl;
                }
                // Simply print the event
                eventupdate = false;
            } else {}
            if ( ! (printcounter = ((printcounter + 1) % printer)))
            {
                // const Uint8 *state = SDL_GetKeyboardState(NULL);
                // if (state[SDL_SCANCODE_RETURN]) {
                //     printf("<RETURN> is pressed.\n");
                // }
            }
            // Sleep the program for a short bit
            wait(5);
        }
        // Reset SDL since the robot is no longer being actuated.
        SDL_JoystickClose(joy);
        // We get here only if the joystick has been disconnected.
        std::cout << "Gamepad disconnected.\n" << std::endl;
    }
    // The program then completes. 
    return 0;
}



The most important part of that huge block of code is lines 129 to 179. I was doing more fooling around trying to get key capture to work but I could not get a response.那个巨大的代码块中最重要的部分是第 129 到 179 行。我正在做更多的鬼混试图让密钥捕获工作,但我无法得到响应。 Everywhere else is logic for the joystick reading (which has worked for me flawlessly).其他地方都是操纵杆阅读的逻辑(这对我来说完美无缺)。 I have been referring to this link for all of the macros available to the programmer.对于程序员可用的所有宏,我一直在参考这个链接 I have not been able to capture the left control button or the right control button.我一直无法捕获左控制按钮或右控制按钮。 I have also been trying the arrow keys for kicks as well and those are not working either.我也一直在尝试使用箭头键进行踢腿,但这些也不起作用。 I know there are remnants of other code snippets thrown in there as I was testing.我知道在我测试时还有其他代码片段的残留物。 Given all of my testing, I am just not sure how to capture any keyboard keys, let alone Ctrl+c .鉴于我的所有测试,我只是不确定如何捕获任何键盘键,更不用说Ctrl+c None of my desired print statements print.我想要的打印语句都没有打印出来。

I am able to run the code on Ubuntu 1804 LTS with the stock GUI manager and window manager.我能够使用股票 GUI 管理器和窗口管理器在 Ubuntu 1804 LTS 上运行代码。 I have a feeling the problem might also have something to do with the operating system not letting SDL2 capture the keyboard, but I don't know what to do to allow only the keyboard or certain keys to be consumed by SDL2.我有一种感觉,这个问题也可能与操作系统不让 SDL2 捕获键盘有关,但我不知道该怎么做才能让 SDL2 只使用键盘或某些键。

I am trying to not use platform-specific code since I already have successfully used platform-specific signal interrupts.我试图不使用特定于平台的代码,因为我已经成功地使用了特定于平台的信号中断。 My goal is to simply make a certain combination of depressed keys result in a program terminating.我的目标是简单地使按下的键的某种组合导致程序终止。 I figured that, since SDL2 can access all keys on a keyboard, that I should be able to simply我想,由于 SDL2 可以访问键盘上的所有键,所以我应该能够简单地

Unless you want to read keyboard input from stdin you need to open a window and focus it to get key events in SDL.除非您想从 stdin 读取键盘输入,否则您需要打开一个窗口并将其聚焦以获取 SDL 中的关键事件。 Here's an example (note the call to SDL_Init uses SDL_INIT_VIDEO and there's some code in there for rendering a background and handling resize events).这是一个示例(注意对 SDL_Init 的调用使用 SDL_INIT_VIDEO 并且其中有一些代码用于呈现背景和处理调整大小事件)。

#include <iostream>
#include <SDL2/SDL.h>


int main(int argc, char** argv)
{
    if (SDL_Init(SDL_INIT_VIDEO) < 0) { // also initialises the events subsystem
        std::cout << "Failed to init SDL.\n";
        return -1;
    }

    SDL_Window *window = SDL_CreateWindow(
        "Window", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 
        680, 480, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);

    if(!window) {
        std::cout << "Failed to create window.\n";
        return -1;
    }

    // Create renderer and select the color for drawing. 
    SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, 0);
    SDL_SetRenderDrawColor(renderer, 200, 200, 200, 255);

    while(true)
    {
        // Clear the entire screen and present.
        SDL_RenderClear(renderer);
        SDL_RenderPresent(renderer);

        SDL_Event event;
        while (SDL_PollEvent(&event))
        {
            if (event.type == SDL_QUIT) {
                SDL_Quit();
                return 0;
            }

            if(event.type == SDL_WINDOWEVENT) {
                if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
                    int width = event.window.data1;
                    int height = event.window.data2;
                    std::cout << "resize event: " << width << "," << height << std::endl;
                }
            }            

            if (event.type == SDL_KEYDOWN) {
                int key = event.key.keysym.sym;
                if (key == SDLK_ESCAPE) {
                    SDL_Quit();
                    return 0;
                }

                std::cout << "key event: " << key << std::endl; 
            }
        }
    }

    return 0;
}

Key events are sent to the currently focused window and SDL uses the underlying OS to handle this.关键事件被发送到当前聚焦的窗口,SDL 使用底层操作系统来处理这个。 Eg In Linux this means SDL calls X11 functions.例如,在 Linux 中,这意味着 SDL 调用 X11 函数。

edit: As detailed in this question it appears you can also get a snapshot of the state of the keys.编辑:正如在这个问题中所详述的那样,您似乎还可以获得键状态的快照。 In either case I think a window needs to be opened to receive events even though I've edited this multiple times to come to that conclusion (apologies if that caused any confusion).在任何一种情况下,我都认为需要打开一个窗口来接收事件,即使我已经多次编辑它来得出这个结论(如果这引起了任何混淆,我深表歉意)。 Your OS may provide functions for polling the state of the keyboard without using events or windows, such as GetAsyncKeyState in Windows.您的操作系统可能会提供无需使用事件或窗口即可轮询键盘状态的功能,例如 Windows 中的 GetAsyncKeyState。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM