Ошибка при изменении размера окна без полей с помощью SDL2

Я пытаюсь сделать приложение с окном без полей в SDL2.

Я реализовал перемещение и изменение размера с помощью drag. Перемещение работает отлично. Изменение размера путем перетаскивания нижней и правой границ также отлично работает.

Изменение размера, перетаскивая верхнюю и левую границы, отлично работает, но имеет косметическую ошибку.

В принципе, если я перетаскиваю левую границу, правая сторона окна делает небольшие прыжки (возможно, 1-2 пикселя), когда я его перемещаю. Перетаскивание с верхней границы приводит к тому, что дно совершает небольшие прыжки. Когда я останавливаю перетаскивание окна, всегда находится в правильном положении, но эта ошибка делает ее очень неэлегантной.

Ошибка существует в Linux (несколько WM/DE) и Windows. Я не тестировал OS X.

Я использую SDL_SetWindowPosition и SDL_SetWindowSize. Я пробовал обходить SDL и использовать XMoveResizeWindow, но он вызывает ту же ошибку.

Хотя я бы предпочел не обойти SDL, я был бы готов использовать Xlib и/или WinAPI, если мне нужно.

Вот фрагмент моего кода:

// 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);

Ответ 1

Я взял SDL2 и провел последние несколько часов, экспериментируя с различными API-интерфейсами для перемещения/масштабирования/перерисовки окна:

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);

и

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

Я узнал, что API-интерфейсы SDL хорошо справляются с медленными, небольшими перетаскиваниями, но большие, более быстрые перетаскивания запускают визуальный глюк, который вы описываете.

SetWindowPos и MoveWindow кажутся почти идентичны - оба работают правильно в моей системе. При перетаскивании на левом краю окна происходит сбой, но это согласуется с поведением других окон в моей системе. Правый край остается визуально фиксированным на месте без скрещивания. Вероятно, я бы выбрал вызов MoveWindow для постоянного решения, хотя это в основном чувство кишки, основанное на том, как запускались различные настройки API. YMMV.

InvalidateRect, похоже, не имеет никакого отношения к тому, как делаются вещи. Это не усугубляет левый пограничный глюк, о котором я упоминал, и не смягчает его. Я предполагаю, что это потому, что я указал флаг перерисовки для вызова MoveWindow.

Для справки, здесь код, который я использовал для тестирования. Я просто прокомментировал/раскомментировал соответствующие API-интерфейсы и при необходимости восстановил проект. Извините за беспорядок - я больше интересовался тем, что делал что-то и работал, чем улучшал код. Дайте мне знать, если вы хотите, чтобы я немного почистил его.

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