I seem to be having a bit of trouble when managing multiple windows in SFML C++. When I try to manage multiple windows, they both open correctly and I can interact in the larger one, however, the smaller one, which upon creation is overlapping the larger window, I can not interact with until I move the large window away. An image is below to help with the visual. Also below is the main loop for my code.
The main loop of the code is as follows:
while (MainWin.isOpen() || F1Menu.isOpen())
{
sf::Event Event;
if (MainWin.pollEvent(Event))
{
switch (Event.type)
{
case sf::Event::Closed:
MainWin.close();
if (F1Menu.isOpen())
F1Menu.close();
break;
case sf::Event::Resized:
MainView.reset(sf::FloatRect(0.f, 0.f, (MainWin.getSize().x*0.9f), (MainWin.getSize().y*0.9)));
MainWin.setView(MainView);
break;
case sf::Event::KeyPressed:
if (Event.key.code == sf::Keyboard::F1)
F1Menu.create(sf::VideoMode(200, 500), "SFML 2D - F1 Menu");
else if (Event.key.code == sf::Keyboard::Escape)
{
MainWin.close();
if (F1Menu.isOpen())
F1Menu.close();
}
break;
}
}
if (F1Menu.pollEvent(Event))
{
switch (Event.type)
{
case sf::Event::Closed:
F1Menu.close();
break;
case sf::Event::MouseButtonReleased:
if (Event.mouseButton.button == sf::Mouse::Left)
if (LMButton.mouseIn(F1Menu))
LoadMap("MapA.dat");
break;
case sf::Event::MouseMoved:
if (LMButton.mouseIn(F1Menu))
LMButton.setColor(sf::Color::Yellow);
else
LMButton.setColor(sf::Color::White);
break;
}
}
moveClock.restart();
if (sf::Keyboard::isKeyPressed(sf::Keyboard::W))
{
player.move(0, -4 * time);
player.setDirection(sfm::Direction::Up);
}
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::S))
{
player.move(0, 4 * time);
player.setDirection(sfm::Direction::Down);
}
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::A))
{
player.move(-4 * time, 0);
player.setDirection(sfm::Direction::Left);
}
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::D))
{
player.move(4 * time, 0);
player.setDirection(sfm::Direction::Right);
}
if (F1Menu.isOpen())
{
F1Menu.clear();
F1Menu.draw(LMButton);
F1Menu.display();
}
if (MainWin.isOpen())
{
MainWin.clear();
if (SplashScreen.didAnimate)
SplashScreen.Animate(MainWin, sfg::Animation::FadeIn);
if (inMenu)
{
}
if (isPlaying)
{
DrawMap(MainWin);
MainWin.draw(player);
}
MainWin.display();
}
}
In my knowledge, you aren't able to poll two separate windows with one sf::Event object. this is because when polling events, you are essentially popping off of the event stack and handling each one. The signature of pollEvent is
bool pollEvent(sf::Event &event);
note that there is no const qualifier here, because each processed event gets popped off the event stack. By the time you finish polling your main window there are no events left for your other window. This could be the cause of your window being unable to focus. Your second window should use it's own separate sf::Event
On a side note I would highly recommend encapsulating your data inside classes. You can find a great reference here on how to go about doing so. It's great coding practice and helps minimize confusion when bug hunting
Related
I'm experiencing some problem to getting working touch events with CEF in offscreen rendering using SDL2 in some situation:
Im writing an SDL-CEF application that need to execute some online game into a videolottery. so I start the application from tty using startx and not in DE. Morever, I can't use the mouse but is only possible use touchscreen to interact with the machine.The touch event work pretty well in the most os cases, but with some game the click event is not recognized by the web application.
For pass the correct event to the CEF browser I have written some specialized handler functions that catchs the SDL_Event and converts their to CEF_Event. Fo example for processing tauch events I have a function called handleFingerEvent(SDL_Event, CEF_Browser*). This specialized function is called from a generic handleEvents(SDL_Event, VEF_Browser*) handler function of highter level .
// The Touch Event Hendler function
void handleFingerEvent(SDL_Event &e, CefBrowser* browser) {
CefMouseEvent event;
switch (e.type) {
case SDL_FINGERDOWN:
// W_SIZE and H_SIZE are the dimension of the screen in px
if (e.tfinger.pressure > 0.9) {
event.x = static_cast<int>(e.tfinger.x*W_SIZE);
event.y = static_cast<int>(e.tfinger.y*H_SIZE);
browser->GetHost()->SendMouseClickEvent(event,
MBT_LEFT,
false, 1);
}
break;
case SDL_FINGERUP:
event.x = static_cast<int>(e.tfinger.x*W_SIZE);
event.y = static_cast<int>(e.tfinger.y*H_SIZE);
browser->GetHost()->SendMouseClickEvent(event, MBT_LEFT, true, 1);
break;
case SDL_FINGERMOTION: {
int scrollX = static_cast<int>(e.tfinger.dx);
int scrollY = static_cast<int>(e.tfinger.dy);
event.x = static_cast<int>(e.tfinger.x*W_SIZE);
event.y = static_cast<int>(e.tfinger.y*H_SIZE);
browser->GetHost()->SendMouseWheelEvent(event, scrollX, scrollY);
break;
}
default:
break;
}
}
void handleEvents(SDL_Event &e, CefBrowser *browser) {
switch (e.type) {
// Key events
case SDL_KEYDOWN:
case SDL_KEYUP:
handleKeyEvent(e, browser);
break;
// Window events
case SDL_WINDOWEVENT:
handleWindowEvent(e, browser);
break;
//Mouse events
case SDL_MOUSEMOTION:
case SDL_MOUSEBUTTONUP:
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEWHEEL:
handleMouseEvent(e, browser);
break;
// Touch events
case SDL_FINGERDOWN:
case SDL_FINGERMOTION:
case SDL_FINGERUP:
handleFingerEvent(e, browser);
break;
default:
break;
}
}
I call it from a SDL_PollEvent() while loop like this:
browser = CefBrowserHost::CreateBrowserSync(window_info,
browserClient, //My BrowserClient implementation
"https://www.something.sometother",
browserSettings, // The cef setting
nullptr, nullptr);
while(!browserClient->closeAllowed()) {
//Send events to the browser
SDL_Event e;
while(SDL_PollEvent(&e) != 0){
//Quit and window resize
if(e.type == SDL_QUIT) {
browser->GetHost()->CloseBrowser(false);
}
else if (e.type == SDL_WINDOWEVENT && e.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
renderHandler->resize(e.window.data1, e.window.data2);
browser->GetHost()->WasResized();
}
else {
handleEvents(e, browser.get());
}
}
// Cef main work loop
cefApp->doCefWork();
//Set background color
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
//Clear window/renderer
SDL_RenderClear(renderer);
renderHandler->render();
//Update screen contents
SDL_RenderPresent(renderer);
}
Now I really don't know why in some game the event isn't caught by the browser. Morever if I open the game in a machine with mouse connected and a DM, and so I use the mouse pointer for interact with the game, it works without any problem. Maybe is there a better approach to send a click event to CefBrowser?
I've been working on a little 3D engine trying to fix up the camera and make it less jerky. I've been using SDL for the input and while it works it's doing this thing where if I press and hold a button it will instantly move once then pause and then start moving properly making the movement feel unresponsive.
I recorded a GIF of it and while it may be slightly hard to see what's happening hopefully it'll give some idea:
Moving forward and then right would be like:
w wwwwwwwwwwwwwwww a aaaaaaaaaaaaaaaaaaaaa
The important code is here but feel free to ask for more if necessary:
//Poll events
SDL_Event event;
while (m_EngineState != EngineState::EXIT)
{
m_last = m_current;
m_current = SDL_GetPerformanceCounter();
deltaTime = (double)((m_current - m_last) * 1000 / SDL_GetPerformanceFrequency());
while (SDL_PollEvent(&event))
{
switch (event.type) {
case SDL_QUIT:
m_EngineState = EngineState::EXIT;
break;
case SDL_MOUSEMOTION:
break;
case SDL_KEYDOWN:
m_Keys[event.key.keysym.sym] = true;
break;
case SDL_KEYUP:
m_Keys[event.key.keysym.sym] = false;
break;
}
ProcessEvents();
}
void Engine::ProcessEvents()
{
if (m_Keys[SDLK_w])
{
m_Camera->MoveForward(5.0f*(deltaTime*0.001));
}
if (m_Keys[SDLK_s])
{
m_Camera->MoveForward(-5.0f*(deltaTime*0.001));
}
if (m_Keys[SDLK_d])
{
m_Camera->MoveRight(5.0f*(deltaTime*0.001));
}
if (m_Keys[SDLK_a])
{
m_Camera->MoveRight(-5.0f*(deltaTime*0.001));
}
}
void Camera::MoveForward(float amount)
{
m_pos += m_forward * amount;
}
void Camera::MoveRight(float amount)
{
m_pos += glm::cross(m_forward, m_up) * amount;
}
Do not use SDL_PollEvent with the SDL_KEYDOWN and SDL_KEYUP events, it is subject to OS keyboard repeat rates. Which is great for typing, but not for camera/player controls. Use SDL_GetKeyboardState(NULL) in stead to query the current state of the keys.
For example:
Uint8* keystate = SDL_GetKeyboardState(NULL);
if (keystate[SDL_SCANCODE_W])
{
m_Camera->MoveForward(5.0f*(deltaTime*0.001));
}
if (keystate[SDL_SCANCODE_S])
{
m_Camera->MoveForward(-5.0f*(deltaTime*0.001));
}
I'm making a basic breakout game using SFML in order to familiarize myself with C++ (moving up from C). Inside the game loop, I've placed an event poll that leads to a switch statement. When the mouse is moved, the switch-case will retrieve the mouse xy change and move the paddle accordingly, like so:
while (window.isOpen())
{
while (window.pollEvent(event))
{
switch (event.type)
{
case sf::Event::Closed:
window.close();
break;
case sf::Event::KeyPressed:
switch (event.key.code)
{
case sf::Keyboard::Escape:
window.close();
break;
}
break;
case sf::Event::MouseMoved:
paddle.move(sf::Vector2f((sf::Mouse::getPosition(window).x - windowOffset.x) * sensitivity, 0));
break;
}
}
sf::Mouse::setPosition(windowOffset, window);
window.clear();
window.draw(paddle);
window.display();
}
Now, that works just fine and dandy, but I'm resetting the mouse position to the center of the window on every loop, which isn't necessary if the mouse hasn't moved. So, I tried moving sf::Mouse::setPosition() into the switch statement, underneath paddle.move, like so:
case sf::Event::MouseMoved:
paddle.move(sf::Vector2f((sf::Mouse::getPosition(window).x - windowOffset.x) * sensitivity, 0));
sf::Mouse::setPosition(windowOffset, window);
break;
However, when I rebuild the project, I now get a blank white window.
So, my question is: What's causing this to happen?
edit(minimal complete code)
int WinMain()
{
sf::Vector2i windowRes(700, 900);
sf::Vector2i windowOffset(350, 450);
sf::ContextSettings settings;
sf::RectangleShape paddle;
paddle.setSize(sf::Vector2f(150, 20));
paddle.setOrigin(350, 0);
paddle.setPosition(350, 825);
sf::RenderWindow window(sf::VideoMode(windowRes.x, windowRes.y), "Breakout", sf::Style::Default, settings);
window.setMouseCursorVisible(false);
sf::Mouse::setPosition(windowOffset, window);
sf::Event event;
while (window.isOpen())
{
while (window.pollEvent(event))
{
switch (event.type)
{
case sf::Event::Closed:
window.close();
break;
case sf::Event::KeyPressed:
switch (event.key.code)
{
case sf::Keyboard::Escape:
window.close();
break;
}
break;
case sf::Event::MouseMoved:
paddle.move(sf::Vector2f((sf::Mouse::getPosition(window).x - windowOffset.x), 0));
//sf::Mouse::setPosition(windowOffset, window);
break;
}
}
sf::Mouse::setPosition(windowOffset, window);
window.clear();
window.draw(paddle);
window.display();
}
return 0;
}
So, through some tinkering, it seems that sf::MOUSE::setposition() generates an event.type of sf::Event::MouseMoved, which would be added to the event queue, causing an infinite loop.
I'm new to programming and, since I find it easier to learn doing, I was playing around with C++ and SFML. I was trying to get a sprite to move, but for some reason, I can't do it. I've tried everything I found, but no luck. Anyone know why this isn't working? I'm using Visual Studio 2012 Express and SFML 2.1, by the way.
int main()
{
sf::RenderWindow window(sf::VideoMode(1000, 800), "Project");
window.setFramerateLimit(30);
glEnable(GL_TEXTURE_2D);
window.clear(sf::Color::White);
window.display();
sf::Texture charMain;
if (!charMain.loadFromFile("Images/playerFrontSprite.png"))
{
return 1;
}
sf::Sprite charaMain;
charaMain.setPosition(500.f , 500.f);
charaMain.setTexture(charMain);
window.draw(charaMain);
window.display();
sf::Font cavefont;
sf::Font::Font(cavefont);
if (!cavefont.loadFromFile("Cave-Story.ttf"))
return 1;
while (window.isOpen())
{
sf::Event playerAction;
while (window.pollEvent(playerAction))
{
switch(playerAction.type)
{
case sf::Event::Closed:
window.close();
break;
case sf::Event::KeyPressed:
std::cout << "sf::Event::KeyPressed" << std::endl;
switch(playerAction.key.code)
{
case sf::Keyboard::W:
charaMain.move(0 , 1);
break;
case sf::Keyboard::A:
charaMain.move(-1 , 0);
break;
case sf::Keyboard::S:
charaMain.move(0 , -1);
break;
case sf::Keyboard::D:
charaMain.move(1 , 0);
break;
}
break;
}
}
}
}
Your problem is that you're moving the object, but you're not drawing it on the screen at its new position. Your drawing code is outside the main loop, so it never gets called after you move the sprite, so you never see the effects.
You basically need to pull the rendering code (ie. window.draw(charaMain) and window.display()) inside the while (window.isOpen()) loop, outside the event handling loop.
First of all I'd like to say that I'm not sure if the title is appropriate but it's the only logical explanation I could find.
What I'm trying to do is move my window by clicking a sprite (sort of like simulating a titlebar).
All working fine until I let go of the mouse button the rate new mouse events are emitted is way lower and with huge 1.5~2s pauses between them.
Is it possible that sf::Mouse::isBUttonPressed is filling the queue or is it another issue?
Edit: The window class has a sf::Event object and passes it to each object's event handler.
The sprite class has an event handler of this form:
bool object::handleEvents(sf::Event& event)
{
switch(event.type)
{
case sf::Event::MouseMoved:
case sf::Event::MouseButtonPressed:
case sf::Event::MouseButtonReleased:
{
auto mouse_pos = sf::Mouse::getPosition(*this->parent);
if(this->isPointInside(mouse_pos))
{
if(event.type == sf::Event::MouseMoved)
{
this->hovering = true;
if(this->callback["onHover"])
this->callback["onHover"](this, nullptr);
return true;
}
else if(event.type == sf::Event::MouseButtonPressed)
{
this->clicked = true;
this->focused = true;
if(event.mouseButton.button == sf::Mouse::Left)
if(this->callback["onLClick"])
this->callback["onLClick"](this, ref(mouse_pos));
if(event.mouseButton.button == sf::Mouse::Right)
if(this->callback["onRClick"])
this->callback["onRClick"](this, ref(mouse_pos));
return true;
}
else if(event.type == sf::Event::MouseButtonReleased && this->clicked)
{
this->clicked = false;
if(event.mouseButton.button == sf::Mouse::Left)
if(this->callback["onLClickReleased"])
this->callback["onLClickReleased"](this, ref(mouse_pos));
if(event.mouseButton.button == sf::Mouse::Right)
if(this->callback["onRClickReleased"])
this->callback["onRClickReleased"](this, ref(mouse_pos));
return true;
}
}
else
{
if(this->hovering)
{
if(this->callback["onHoverLost"])
this->callback["onHoverLost"](this, nullptr);
this->hovering = false;
}
}
}break;
default: ;
}
return false;
}
and the code responsible for moving the window:
titlebar->callback["onLClick"] = [&](object* obj, void* data)
{
sf::Vector2i* relpos = (sf::Vector2i*)(data);
while(sf::Mouse::isButtonPressed(sf::Mouse::Left))
{
sf::Vector2i abspos = sf::Mouse::getPosition();
window.setPosition(sf::Vector2i((abspos.x - relpos->x),(abspos.y - relpos->y)));
}
titlebar->clicked = false;
};
The sf::Mouse class as well as sf::Keyboard and sf::Joystick aren't connected to the event system, but are completely independent, thus it's impossible that isButtonPressed would've any influence on the events.
The real problem in your code is the 'infinite' loop when the left mouse button is pressed. If the left mouse button is pressed, everything that happens in your application is, that the window gets moved around. There won't be any event dispatches (= processing) and every event that happens within that time, will get pilled in the event queue. Thus when you return to process the events, you'll have a longer queue than usual and will start dispatching with the oldest event.
So if you now move your window around for 2 seconds, you'll get a filled queue worth 2 seconds which can delay the further processing.
To solve this problem, you'll most probably have to dispatch all the events while you're moving the window.