c++ - 来自多线程 SDL 应用程序的 InvalidCursor 错误

标签 c++ sdl xorg

我重写了 Lode's raycasting tutorial代码使其在单独的线程中处理事件。我发现任何调用 xlib 函数的 SDL 调用都需要是主线程,所以在这段代码中所有依赖 xlib 的函数都在主线程中。

这是我仍然随机从应用程序中得到的错误:

X Error of failed request:  BadCursor (invalid Cursor parameter)
  Major opcode of failed request:  95 (X_FreeCursor)
  Resource id in failed request:  0x4a0000b
  Serial number of failed request:  108
  Current serial number in output stream:  107

有时当我运行它时我会得到这个错误,但如果我再次运行它就会工作。

我不确定我还需要如何更改代码,因为所有图形处理都在主线程中进行,单独的线程只处理事件处理。有人知道我做错了什么吗?

raycaster.cpp

#include <iostream>
#include <cmath>

#include "SDL.h"
#include "SDL/SDL_image.h"
#include "SDL/SDL_ttf.h"

#include "game.hpp"

using std::cout;

static int SCREENW = 500;
static int SCREENH = 500;
static int BPP = 32;

int events_loop(void* data);

int main(int argc, char** argv) {
    SDL_Init(SDL_INIT_EVERYTHING | SDL_INIT_EVENTTHREAD);
    TTF_Init();

    SDL_Thread* events;
    Game_state* gs = new Game_state();
    events = SDL_CreateThread(events_loop, (void*)gs);

    SDL_Surface* screen = SDL_SetVideoMode(SCREENW, SCREENH, BPP, SDL_SWSURFACE);
    SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
    SDL_WM_SetCaption("Raycaster (non-textured)", NULL);

    Game* game = new Game(screen, SCREENW, SCREENH, BPP);

    //BEGIN GAME VARIABLES
    //game map
    int map[Game::MAP_WIDTH][Game::MAP_WIDTH] = {
    { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
    { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
    { 1, 0, 0, 4, 0, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
    { 1, 0, 3, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
    { 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
    { 1, 0, 0, 0, 0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
    { 1, 0, 4, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
    { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
    { 1, 0, 2, 0, 0, 2, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 1 },
    { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 0, 0, 0, 0, 0, 0, 1 },
    { 1, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
    { 1, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
    { 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
    { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
    { 1, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
    { 1, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 1 },
    { 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 1 },
    { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 0, 0, 0, 1 },
    { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
    { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }
    };

    //direction variables
    double pos_x = Game::PLAYER_START_X;
    double pos_y = Game::PLAYER_START_Y;
    double dir_x = -1; double old_dir_x;
    double dir_y = 0;
    int map_x, map_y;

    //timing variables
    double start_ticks = 0;
    double end_ticks = 0;
    double frame_time = 0;

    //camera varibales
    double camera_x;
    double ray_pos_x, ray_pos_y;
    double ray_dir_x, ray_dir_y;
    double plane_x = 0; double plane_y = Game::FOV; double old_plane_x;
    int line_height;

    //DDA variables
    double side_dist_x, side_dist_y;
    double delta_dist_x, delta_dist_y;
    double perpen_wall_dist;
    int step_x, step_y;
    bool EW_side; //east west side hit, negative implies north south side
    bool hit = false;

    //drawing variables
    int draw_low_y, draw_high_y;
    int r, g, b;

    //movement variables
    double move_speed, rotation_speed;

    //BEGIN RENDERING LOGIC
    while(gs->over == false) {
        start_ticks = SDL_GetTicks();

        //lock screen to modify its pixels
        /*if(SDL_MUSTLOCK(screen)) {
            SDL_LockSurface(screen);
        }*/

        game->clear_screen();

        //BEGIN DRAWING PIXELS
        for(int x = 0; x < SCREENW; x++) {
            //set up camera
            camera_x = 2 * x / (double(SCREENW) - 1);
            ray_pos_x = pos_x; ray_pos_y = pos_y;

            ray_dir_x = dir_x + plane_x * camera_x;
            ray_dir_y = dir_y + plane_y * camera_x;

            delta_dist_x = sqrt(1 + (ray_dir_y * ray_dir_y) / (ray_dir_x * ray_dir_x));
            delta_dist_y = sqrt(1 + (ray_dir_x * ray_dir_x) / (ray_dir_y * ray_dir_y));

            //what box are we in?
            map_x = int(ray_pos_x); map_y = int(ray_pos_y);

            //calculate step and side_dist
            if(ray_dir_x < 0) {
                step_x = -1;
                side_dist_x = (ray_pos_x - map_x) * delta_dist_x;
            }
            else {
                step_x = 1;
                side_dist_x = (map_x + 1.0 - ray_pos_x) * delta_dist_x;
            }

            if(ray_dir_y < 0) {
                step_y = -1;
                side_dist_y = (ray_pos_y - map_y) * delta_dist_y;
            }
            else {
                step_y = 1;
                side_dist_y = (map_y + 1.0 - ray_pos_y) * delta_dist_y;
            }

            //step using DDA until a wall is hit
            hit = false;
            while(hit == false) {
                if(side_dist_x < side_dist_y) {
                    side_dist_x += delta_dist_x;
                    map_x += step_x;
                    EW_side = false;
                }
                else {
                    side_dist_y += delta_dist_y;
                    map_y += step_y;
                    EW_side = true;
                }

                if(map[map_x][map_y] > 0) { hit = true; }
            }

            //calculate dist from camera to wall that was hit
            if(EW_side == false) {
                perpen_wall_dist = fabs((map_x - ray_pos_x + (1 - step_x) / 2) / ray_dir_x);
            }
            else {
                perpen_wall_dist = fabs((map_y - ray_pos_y + (1 - step_y) / 2) / ray_dir_y);
            }

            //calculate line height from perpendicular wall distance
            line_height = abs(int(SCREENH / perpen_wall_dist));

            //calculate how high to draw the line
            draw_high_y = -line_height / 2 + SCREENH / 2;
            if(draw_high_y < 0) { draw_high_y = 0; }

            draw_low_y = line_height / 2 + SCREENH / 2;
            if(draw_low_y >= SCREENH) { draw_low_y = SCREENH - 1; }
            if(draw_low_y < 0) { draw_low_y = 0; } //added (shouldn't need to be here)

            //finally draw the line
            game->draw_line(x, draw_low_y, draw_high_y, map[map_x][map_y], EW_side);
        }

        //unlock screen for blitting
        /*if(SDL_MUSTLOCK(screen)) {
            SDL_UnlockSurface(screen);
        }*/

        //calculate timing and print the FPS
        end_ticks = SDL_GetTicks();
        frame_time = (end_ticks - start_ticks) / 1000.0;

        game->blit_fps(frame_time);
        game->blit_location(map_x, map_y);

        if(SDL_Flip(screen) != 0) {
            cout << "ERROR: couldn't draw to the screen <" << SDL_GetError() << ">\n";
        }

        //BEGIN CALCULATING NEXT STEP
        //calculate new direction based on frames drawn
        move_speed = frame_time * Game_state::MOVEMENT_MULTIPLIER;
        rotation_speed = frame_time * Game_state::ROTATION_MULTIPLIER;

        //process movement for next frame
        if(gs->movement_forward == Game_state::MOVE_UP) {
            if(map[int(pos_x + dir_x * move_speed)][int(pos_y)] == 0) { pos_x += dir_x * move_speed; }
            if(map[int(pos_x)][int(pos_y + dir_y * move_speed)] == 0) { pos_y += dir_y * move_speed; }
        }
        else if(gs->movement_forward == Game_state::MOVE_DOWN) {
            if(map[int(pos_x - dir_x * move_speed)][int(pos_y)] == 0) { pos_x -= dir_x * move_speed; }
            if(map[int(pos_x)][int(pos_y - dir_y * move_speed)] == 0) { pos_y -= dir_y * move_speed; }
        }

        if(gs->movement_side == Game_state::MOVE_RIGHT) {
            old_dir_x = dir_x;
            dir_x = dir_x * cos(-rotation_speed) - dir_y * sin(-rotation_speed);
            dir_y = old_dir_x * sin(-rotation_speed) + dir_y * cos(-rotation_speed);

            old_plane_x = plane_x;
            plane_x = plane_x * cos(-rotation_speed) - plane_y * sin(-rotation_speed);
            plane_y = old_plane_x * sin(-rotation_speed) + plane_y * cos(-rotation_speed);
        }
        else if(gs->movement_side == Game_state::MOVE_LEFT) {
            old_dir_x = dir_x;
            dir_x = dir_x * cos(rotation_speed) - dir_y * sin(rotation_speed);
            dir_y = old_dir_x * sin(rotation_speed) + dir_y * cos(rotation_speed);

            old_plane_x = plane_x;
            plane_x = plane_x * cos(rotation_speed) - plane_y * sin(rotation_speed);
            plane_y = old_plane_x * sin(rotation_speed) + plane_y * cos(rotation_speed);
        }
    }

    delete gs;
    delete game;

    return 0;
}

int events_loop(void* data) {
    Game_state* gs = (Game_state*)data;

    SDL_Event evt;
    while(1) {
        while(SDL_PollEvent(&evt)) {
            if(evt.type == SDL_QUIT) { gs->over = true; cout << "quit\n"; return 0; }
            else if(evt.type == SDL_KEYDOWN) {
                if(evt.key.keysym.sym == SDLK_w) {
                    gs->move(Game_state::MOVE_UP);
                }
                else if(evt.key.keysym.sym == SDLK_s) {
                    gs->move(Game_state::MOVE_DOWN);
                }
                else if(evt.key.keysym.sym == SDLK_a) {
                    gs->move(Game_state::MOVE_LEFT);
                }
                else if(evt.key.keysym.sym == SDLK_d) {
                    gs->move(Game_state::MOVE_RIGHT);
                }
                else if(evt.key.keysym.sym == SDLK_ESCAPE) { gs->over = true; cout << "escape\n"; return 0; }
            }
            else if(evt.type == SDL_KEYUP) {
                if(evt.key.keysym.sym == SDLK_w) {
                    gs->stop_move(Game_state::MOVE_UP);
                }
                else if(evt.key.keysym.sym == SDLK_s) {
                    gs->stop_move(Game_state::MOVE_DOWN);
                }
                else if(evt.key.keysym.sym == SDLK_a) {
                    gs->stop_move(Game_state::MOVE_LEFT);
                }
                else if(evt.key.keysym.sym == SDLK_d) {
                    gs->stop_move(Game_state::MOVE_LEFT);
                }
            }
            else { /* ignore */ }
        }
    }
}

游戏.cpp

#include <iostream>

#include "game.hpp"

using std::cout; using std::endl;

Game_state::Game_state() {
    movement_forward = NO_MOVE;
    movement_side = NO_MOVE;
    over = false;
}

void Game_state::move(int direction) {
    switch(direction) {
        case NO_MOVE:
        break;

        case MOVE_UP:
        if(movement_forward == MOVE_DOWN) { movement_forward = NO_MOVE; }
        else { movement_forward = MOVE_UP; }
        break;

        case MOVE_DOWN:
        if(movement_forward == MOVE_UP) { movement_forward = NO_MOVE; }
        else { movement_forward = MOVE_DOWN; }
        break;

        case MOVE_LEFT:
        if(movement_side == MOVE_RIGHT) { movement_side = NO_MOVE; }
        else { movement_side = MOVE_LEFT; }
        break;

        case MOVE_RIGHT:
        if(movement_side == MOVE_LEFT) { movement_side = NO_MOVE; }
        else { movement_side = MOVE_RIGHT; }
        break;

        default:
        cout << "ERROR: invalid movement in Game_state::move() at time " << SDL_GetTicks() << endl;
        break;
    }
}

void Game_state::stop_move(int direction) {
    switch(direction) {
        case NO_MOVE:
        break;

        case MOVE_UP:
        case MOVE_DOWN:
        movement_forward = NO_MOVE;
        break;

        case MOVE_RIGHT:
        case MOVE_LEFT:
        movement_side = NO_MOVE;
        break;

        default:
        cout << "ERROR: invalid movement in Game_state::stop_move() at time " << SDL_GetTicks() << endl;
        break;
    }
}

Game::Game(SDL_Surface* scr, int w, int h, int b) {
    screen = scr;
    scr_w = w; scr_h = h; bpp = b;

    //fps printing vars
    fps_location.x = 0; fps_location.y = 0;
    fps_font = TTF_OpenFont("/usr/share/fonts/truetype/ttf-liberation/LiberationMono-Regular.ttf", 24);
    fps_color.r = 0; fps_color.g = 0; fps_color.b = 255; //blue

    //location printing vars
    location_font = TTF_OpenFont("/usr/share/fonts/truetype/ttf-liberation/LiberationMono-Regular.ttf", 18);

    //determine how high the font surface should be from the bottom
    location_color.r = 0; location_color.g = 90; location_color.b = 240;
    location_surface = TTF_RenderText_Solid(location_font, location_buffer, location_color);
    location_location.x = 0; location_location.y = scr_h - location_surface->clip_rect.h;

    //set up the wall colors
    wall_color[OUTSIDE_WALL].r = 255; wall_color[OUTSIDE_WALL].g = 255; wall_color[OUTSIDE_WALL].b = 255;
    wall_color[RED_WALL].r = 255; wall_color[RED_WALL].g = 0; wall_color[RED_WALL].b = 0;
    wall_color[GRAY_WALL].r = 160; wall_color[GRAY_WALL].g = 160; wall_color[GRAY_WALL].b = 160;
    wall_color[GOLD_WALL].r = 232; wall_color[GOLD_WALL].g = 211; wall_color[GOLD_WALL].g = 34;
}

void Game::clear_screen() {
    SDL_FillRect(screen, &screen->clip_rect, SDL_MapRGB(screen->format, 0, 0, 0));
}

void Game::blit_fps(double frame_time) {
    sprintf(fps_buffer, "FPS: %3.3f", 1.0 / frame_time);
    fps_surface = TTF_RenderText_Solid(fps_font, fps_buffer, fps_color);
    if(SDL_BlitSurface(fps_surface, NULL, screen, &fps_location) != 0) {
        cout << "ERROR: couldn't blit the FPS surface <" << SDL_GetError() << ">\n";
    }
}

void Game::blit_location(int x, int y) {
    sprintf(location_buffer, "location: %d, %d", x, y);
    location_surface = TTF_RenderText_Solid(location_font, location_buffer, location_color);
    if(SDL_BlitSurface(location_surface, NULL, screen, &location_location) != 0) {
        cout << "ERROR: couldn't blit the location surface <" << SDL_GetError() << ">\n";
    }
}

//high_y means the y coord closest to the top of the screen
void Game::draw_line(int x, int low_y, int high_y, int wall_type, bool EW_side) {
    //cout << "high_y = " << high_y << " low_y = " << low_y << endl;
    int r = wall_color[wall_type].r;
    int g = wall_color[wall_type].g;
    int b = wall_color[wall_type].b;
    if(EW_side == true) { r /= 2; g /= 2; b /= 2; }
    //cout << "r = " << r << " g = " << g << " b = " << b << "\n";

    //draw ceiling
    /*for(int y = 0; y < high_y - 1; y++) {
        put_pixel(x, y, 0, 255, 90);
    }*/

    for(int y = high_y; y <= low_y; y++) {
        put_pixel(x, y, r, g, b);
    }

    //draw floor (checkered)
    /*for(int y = low_y + 1; y <= scr_h; y++) {
        if(x % 20 > 10 && y % 20 > 10) {
            put_pixel(x, y, 255, 255, 255);
        }
    }*/
}

void Game::put_pixel(int x, int y, int r, int g, int b) {
    int bpp = screen->format->BytesPerPixel;
    Uint8* p = (Uint8*)screen->pixels + y * screen->pitch + x * bpp;
    Uint32 pixel = SDL_MapRGB(screen->format, r, g, b);

    switch(bpp) {
        case 1:
        *p = pixel;
        break;

        case 2:
        *(Uint16*)p = pixel;
        break;

        case 3:
        if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
            p[0] = (pixel >> 16) & 0xff;
            p[1] = (pixel >> 8) & 0xff;
            p[2] = pixel & 0xff;
        }
        else {
            p[0] = pixel & 0xff;
            p[1] = (pixel >> 8) & 0xff;
            p[2] = (pixel >> 16) & 0xff;
        }
        break;

        case 4:
        *(Uint32*)p = pixel;
        break;
    }
}

游戏.hpp

#include "SDL.h"
#include "SDL/SDL_image.h"
#include "SDL/SDL_ttf.h"

class Game_state {
    public:
    //movement statics
    static const int NO_MOVE = 0;
    static const int MOVE_UP = 1;
    static const int MOVE_DOWN = 2;
    static const int MOVE_LEFT = 3;
    static const int MOVE_RIGHT = 4;
    static const double MOVEMENT_MULTIPLIER = 5.0;
    static const double ROTATION_MULTIPLIER = 3.0;

    int movement_forward;
    int movement_side;
    bool over;

    Game_state();
    void move(int direction);
    void stop_move(int direction);
};

class Game {
    private:
    //fps vars
    char fps_buffer[50];
    TTF_Font* fps_font;
    SDL_Surface* fps_surface;
    SDL_Rect fps_location;
    SDL_Color fps_color;

    //location vars
    char location_buffer[24];
    TTF_Font* location_font;
    SDL_Surface* location_surface;
    SDL_Rect location_location;
    SDL_Color location_color;

    void put_pixel(int x, int y, int r, int g, int b);

    public:
    //game statics
    static const int MAP_WIDTH = 20;
    static const int MAP_HEIGHT = 20;
    static const double FOV = 0.66;
    static const int PLAYER_START_X = 1;
    static const int PLAYER_START_Y = 1;

    //wall options
    static const int FLOOR = 0;
    static const int OUTSIDE_WALL = 1;
    static const int RED_WALL = 2;
    static const int GRAY_WALL = 3;
    static const int GOLD_WALL = 4;

    //game variables
    SDL_Surface* screen;
    int scr_w;
    int scr_h;
    int bpp;

    SDL_Color wall_color[5];

    Game(SDL_Surface* scr, int w, int h, int b);
    void clear_screen();
    void blit_fps(double frame_time);
    void blit_location(int x, int y);
    void draw_line(int x, int low_y, int high_y, int wall_type, bool EW_side);
};

最佳答案

SDL_PollEvent documentation states (至少对于 SDL 1.2 来说很明显)它应该只从设置视频模式的同一线程调用。

关于c++ - 来自多线程 SDL 应用程序的 InvalidCursor 错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7155276/

相关文章:

c++ - 通过引用传递,仅限子范围?

c++ - netbeans C++ 调试中的信号是什么

c++ - 指向 SDL 音频回调的成员函数的指针?

linux - 一台机器上的多个离散键盘输入

centos - Cutycapt、wkhtmltopdf、wkhtmltoimage、phantomjs 无法正确显示字符

c++ - PThread 堆栈上的互斥锁定和解锁

C++ - 符号+括号数组语法?

c++ - SDL2 键盘检测不起作用

linux - 将帧缓冲区映射到 X 窗口

c++ - 这里使用的是拷贝构造函数、赋值运算符,还是两者都用了?