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

标签 c++ linux sdl-2

TL;博士
我正在尝试在我自己的 C++ 程序中捕获键盘事件(更具体地说,是 Ctrl+c 命令)。我正在通过 SDL2 中的通用键盘按键尝试此操作。
结束 TL;DR
我在 SO 和 Internet 上找到了涵盖使用 SDL2 处理键盘事件的主题的链接。我在这里列出了其中的一些。

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/
我认为导致问题的主要问题是我同时也在使用 Xbox 风格的操纵杆。我在捕获操纵杆事件方面没有任何问题。我已经这样做了很长时间了。我在尝试用键盘获取任何东西来引发事件时遇到问题。我试过if(event.type == SDL_KEYDOWN)然后检查它是哪个键,但这似乎没有返回任何内容。我觉得我需要定义一些宏来允许这样做,因为我一直在互联网上找到相同的解决方案。
我已经包含了我目前正在运行的整个脚本。

#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;
}



那个巨大的代码块中最重要的部分是第 129 到 179 行。我正在做更多的鬼混,试图让关键捕获工作,但我无法得到回应。其他任何地方都是操纵杆阅读的逻辑(这对我来说完美无缺)。我一直在指这个link对于程序员可用的所有宏。我无法捕获左控制按钮或右控制按钮。我也一直在尝试使用箭头键来踢球,但这些也不起作用。我知道在我测试的时候还有其他代码片段的残余。鉴于我的所有测试,我只是不确定如何捕获任何键盘键,更不用说 Ctrl+c . 我想要的打印语句都没有打印。
我可以使用股票 GUI 管理器和窗口管理器在 Ubuntu 1804 LTS 上运行代码。我觉得这个问题也可能与操作系统不让 SDL2 捕获键盘有关,但我不知道该怎么做才能只允许 SDL2 使用键盘或某些键。
我试图不使用特定于平台的代码,因为我已经成功使用了特定于平台的信号中断。我的目标是简单地使按下的键的某种组合导致程序终止。我想,由于 SDL2 可以访问键盘上的所有键,我应该能够简单地

最佳答案

除非您想从标准输入读取键盘输入,否则您需要打开一个窗口并将其聚焦以获取 SDL 中的关键事件。这是一个示例(注意对 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;
}

键事件被发送到当前聚焦的窗口,SDL 使用底层操作系统来处理它。例如。在 Linux 中,这意味着 SDL 调用 X11 函数。

编辑:
详见this question看来您还可以获得按键状态的快照。在任何一种情况下,我都认为需要打开一个窗口来接收事件,即使我已经多次编辑它来得出这个结论(如果造成任何混淆,请道歉)。您的操作系统可能会提供无需使用事件或窗口即可轮询键盘状态的函数,例如 Windows 中的 GetAsyncKeyState。

关于c++ - SDL2 无法捕获控制台键盘事件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59516089/

相关文章:

c++ - 为什么 TTF_RenderText_Blended 需要一个常量字符?

c++ - 在运算符中构造结果与在默认构造的对象上操作

python - 新的 python virtualenv 应该用新的 linux 用户帐户创建吗?

c++ - 是什么让 EXE 变大?

c - 这些 fork() 语句的进程树是什么样子的?

linux - 利用格式字符串漏洞/攻击是什么?我的猜测是 "printf(nameBuf); and that it should be "printf ("%s", nameBuf);

c++ - “IMG_Load”未在此范围 SDL 2 中声明

c - 表面和纹理的区别(SDL/通用)

c++ - C++的本地持续集成系统?

c++ - 帮忙解决这个选角问题