Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bug when resizing borderless window with SDL2

I'm trying to make an application with a borderless window in SDL2.

I've implemented moving and resizing via drag. Moving works perfectly fine. Resizing by dragging the bottom and right borders also works fine.

Resizing by dragging the top and left borders functions fine, but it has a cosmetic bug.

Basically if I drag from the left border, the right side of the window makes little jumps (maybe 1-2 pixels) as I move it. Dragging from the top border causes the bottom to make little jumps. When I stop dragging the window is always in the right position, but this bug makes it seem very inelegant.

The bug exists on Linux (multiple WMs/DEs) and Windows. I haven't tested on OS X.

I'm using SDL_SetWindowPosition and SDL_SetWindowSize. I've tried bypassing SDL and using XMoveResizeWindow but it causes the same bug.

While I'd strongly prefer not to bypass SDL, I'd be willing to use Xlib and/or WinAPI if I need to.

Here's a snippet of my code:

// mousePos is initialized to current mouse pos
// newWindowSize initilized to current window size
// newWindowPos initialized to current window position
// mWindowResizeOffset variable is where the mouse grabbed the window

// omitted code for right and bottom borders because the bug doesn't exist there

// Logic for the top border is the same
if (mLeftBorderGrabbed)
{
    newWindowPos.x = mousePos.x - mWindowResizeOffset.x;
    newWindowSize.x += windowPos.x - newWindowPos.x;
}

SDL_SetWindowPosition(mInternalWindow, newWindowPos.x, newWindowPos.y);
SDL_SetWindowSize(mInternalWindow, newWindowSize.x, newWindowSize.y);
like image 501
DormoTheNord Avatar asked Mar 29 '14 00:03

DormoTheNord


1 Answers

I picked up SDL2 and spent the last few hours experimenting with different window moving/sizing/redrawing APIs:

SDL_SetWindowPosition(window, newWindowPos.x, newWindowPos.y);
SDL_SetWindowSize(window, newWindowSize.x, newWindowSize.y);

SetWindowPos(windowHandle, nullptr, newWindowPos.x, newWindowPos.y, newWindowSize.x, newWindowSize.y, SWP_SHOWWINDOW);

MoveWindow(windowHandle, newWindowPos.x, newWindowPos.y, newWindowSize.x, newWindowSize.y, TRUE);

and

MoveWindow(windowHandle, newWindowPos.x, newWindowPos.y, newWindowSize.x, newWindowSize.y, TRUE);
InvalidateRect(windowHandle, &windowRect, TRUE);

I've learned that the SDL APIs do okay with slow, small drags, but larger, faster drags trigger the visual glitch you describe.

SetWindowPos and MoveWindow seem almost identical - both work correctly on my system. There is some glitching on the left edge of the window during dragging, but this is consistent with the behavior of other windows on my system. The right edge stays visually fixed in place without glitching. I would probably pick the MoveWindow call for a permanent solution, although that's mostly gut feeling based on how the different API setups ran. YMMV.

InvalidateRect doesn't seem to make any difference in how things are drawn. It does not exacerbate the left-border glitch I mentioned, nor does it alleviate it. I would guess this is because I specified a redrawing flag for the MoveWindow call.

For reference, here's the code I used for testing. I just commented/uncommented the relevant APIs and rebuilt the project as needed. Sorry about the messiness - I was more interested in getting something up and running than making the code look perfect. Let me know if you'd like me to clean it up a bit.

//Using SDL and standard IO
#include <windows.h>
#include <SDL.h>
#include <SDL_syswm.h>
#include <stdio.h>

//Screen dimension constants
const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;

struct Point
{
  int x;
  int y;
};

int main(int argc, char* args[])
{
  bool quit = false, dragging = false;
  //The window we'll be rendering to
  SDL_Window* window = NULL;

  //The surface contained by the window
  SDL_Surface* screenSurface = NULL;

  SDL_Event e;

  SDL_SysWMinfo windowInfo;
  HWND windowHandle;

  Point mousePos, windowPos, newWindowPos, newWindowSize, mWindowResizeOffset;

  //Initialize SDL
  if(SDL_Init(SDL_INIT_VIDEO) < 0)
  {
    printf("SDL could not initialize! SDL_Error: %s\n", SDL_GetError());
  }
  else
  {
    //Create window
    window = SDL_CreateWindow("SDL Tutorial", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN | SDL_WINDOW_BORDERLESS);   // | SDL_WINDOW_RESIZABLE
    if(window == NULL)
    {
      printf("Window could not be created! SDL_Error: %s\n", SDL_GetError());
    }
    else
    {
      //Get window surface
      screenSurface = SDL_GetWindowSurface(window);

      SDL_VERSION(&windowInfo.version);
      SDL_GetWindowWMInfo(window, &windowInfo);
      windowHandle = windowInfo.info.win.window;

      //While application is running
      while(!quit)
      {
        //Handle events on queue
        while(SDL_PollEvent(&e) != 0)
        {
          //process events
          switch(e.type)
          {
          case SDL_QUIT:
            quit = true;
            break;
          case SDL_WINDOWEVENT:
            if(e.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
            {
              screenSurface = SDL_GetWindowSurface(window);
            }
            //Fill the surface blue
            SDL_FillRect(screenSurface, NULL, SDL_MapRGB(screenSurface->format, 0x00, 0xA2, 0xE8));

            //Update the surface
            SDL_UpdateWindowSurface(window);
            break;
          case SDL_MOUSEBUTTONDOWN:
            SDL_GetMouseState(&mWindowResizeOffset.x, &mWindowResizeOffset.y);
            SDL_GetWindowPosition(window, &windowPos.x, &windowPos.y);
            dragging = true;
            break;
          case SDL_MOUSEBUTTONUP:
            dragging = false;
            break;
          case SDL_MOUSEMOTION:
            if(dragging)
            {
              SDL_GetMouseState(&mousePos.x, &mousePos.y);
              SDL_GetWindowPosition(window, &newWindowPos.x, &newWindowPos.y);
              SDL_GetWindowSize(window, &newWindowSize.x, &newWindowSize.y);

              newWindowPos.x = newWindowPos.x + mousePos.x - mWindowResizeOffset.x;
              newWindowSize.x += windowPos.x - newWindowPos.x;

              //SDL_SetWindowPosition(window, newWindowPos.x, newWindowPos.y);
              //SDL_SetWindowSize(window, newWindowSize.x, newWindowSize.y);
              //SetWindowPos(windowHandle, nullptr, newWindowPos.x, newWindowPos.y, newWindowSize.x, newWindowSize.y, SWP_SHOWWINDOW);
              MoveWindow(windowHandle, newWindowPos.x, newWindowPos.y, newWindowSize.x, newWindowSize.y, TRUE);

              /*RECT drawRect;
              drawRect.left = windowPos.x;
              drawRect.top = windowPos.y;
              drawRect.right = windowPos.x + newWindowSize.x;
              drawRect.bottom = windowPos.y + newWindowSize.y;

              InvalidateRect(windowHandle, &drawRect, TRUE);*/

              windowPos = newWindowPos;
            }
            break;
          }
        }

        SDL_Delay(1);
      }
    }
  }

  //Destroy window
  SDL_DestroyWindow(window);

  //Quit SDL subsystems
  SDL_Quit();

  return 0;
}
like image 138
cf stands with Monica Avatar answered Oct 14 '22 02:10

cf stands with Monica