SDL2

SDL2 SDL2

May 3, 2025

Суброзділ SDL2

Заняття №2

01_hello_SDL

01_hello_SDL.cpp

/**
 * @file 01_hello_SDL.cpp
 * @author Sam4uk (sam4uk.site@gmail.com)
 * @date 2025-05-03
 * 
 * @copyright Copyright © Sam4uk 2025 (Sam4uk.site@gmail.com) 
 * 
 */
#if defined(__linux)
#include <SDL2/SDL.h>
#elif defined(WIN32)
#include <SDL.h>
#endif

#include <exception>
#include <iostream>
#include <memory>
#include <stdexcept>

/** Screen dimension constants */
const int SCREEN_WIDTH{640}, SCREEN_HEIGHT{480};

int main(int argc, char* args[]) try {
  /** Initialize SDL */
  struct SDL_Guard {
    SDL_Guard(Uint32 flags) {
      if (0 > SDL_Init(flags))
        throw std::runtime_error("SDL could not initialize! SDL_Error:" +
                                 std::string(SDL_GetError()));
    }
    ~SDL_Guard() { SDL_Quit(); }
  } sdl_guart(SDL_INIT_VIDEO);

  /** The window we'll be rendering to */
  std::unique_ptr<SDL_Window, void (*)(SDL_Window*)> window{
      SDL_CreateWindow("C++ SDL blank by Sam4uk", SDL_WINDOWPOS_UNDEFINED,
                       SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT,
                       SDL_WINDOW_SHOWN),
      [](SDL_Window* w) {
        if (w) SDL_DestroyWindow(w);
      }};

  if (!window)
    throw std::runtime_error("Window could not be created! SDL_Error:\n" +
                             std::string(SDL_GetError()));

  /** The surface contained by the window */
  SDL_Surface* screenSurface{SDL_GetWindowSurface(window.get())};

  /** Fill the surface white */
  SDL_FillRect(screenSurface, NULL,
               SDL_MapRGB(screenSurface->format, 0xFF, 0xFF, 0xFF));

  SDL_Event e;

  bool quit{false};

  do {
    // Update the surface
    SDL_UpdateWindowSurface(window.get());
    while (SDL_PollEvent(&e)) {
      switch (e.type) {
        case SDL_QUIT:
          quit = true;
          break;
        default:
          break;
      }
    }
  } while (!quit);

  return EXIT_SUCCESS;
}

catch (const std::exception& e) {
  std::cout << e.what() << std::endl;
  return EXIT_FAILURE;
}

catch (...) {
  std::cout << "Something wrong\n";
  return -EXIT_FAILURE;
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.11)
project(01_hello_SDL)

add_executable(${PROJECT_NAME} 01_hello_SDL.cpp)

target_link_libraries(${PROJECT_NAME} SDL2)

Збирання проекту

cmake -B build -G="Unix Makefiles"
cmake --build ./build -j$(nproc)

Заняття №1 Налаштування та робота з вікнами

Заняття №1

01_hello_SDL.cpp

#if defined(__linux)
#include <SDL2/SDL.h>

#elif defined(WIN32)
#include <SDL.h>
#endif

#include <iostream>

Буду старатися використовувати розумний менеджмент пам’яті. Тому підключаємо необхідні для цього заголовки.

#include <memory>

Створимо структуру яка при вихоід з області видимоті автоматично викличе SDL_Quit(). Це обов’язково. А для початку треба ініціалізувати все, що нам потрібно функцією SDL_Init(Uint32), яка проініціалізує потрібні нам системи.

SDL_INIT_TIMERtimer subsystem
SDL_INIT_AUDIOaudio subsystem
SDL_INIT_VIDEOvideo subsystem; automatically initializes the events subsystem
SDL_INIT_JOYSTICKjoystick subsystem; automatically initializes the events subsystem
SDL_INIT_HAPTIChaptic (force feedback) subsystem
SDL_INIT_GAMECONTROLLERcontroller subsystem; automatically initializes the joystick subsystem
SDL_INIT_EVENTSevents subsystem
SDL_INIT_EVERYTHINGall of the above subsystems

Можна ініціалізувати всі потрібні системи одразу, або згодом при необхідності SDL_InitSubSystem(Uint32) ініціалізувати конкретну систему, або зупинити SDL_QuitSubSystem(Uint32) вже не потрібну систему. При виклику SDL_Quit() зупиняються всі системи.

struct SDL_Guard {
  SDL_Guard(Uint32 flags) {
    if (0 > SDL_Init(flags))
      throw std::runtime_error("SDL could not initialize! SDL_Error:" +
                               std::string(SDL_GetError()));
  }
  ~SDL_Guard() { SDL_Quit(); }
};

Для вікна ми використаємо розумний вказівник який розглянемо трохи пізніше.

using UniqueWindow = std::unique_ptr<SDL_Window, void (*)(SDL_Window*)>;

Оголосимо main функцію для різних операційних систем

#if defined(_WIN32)
#include <windows.h>
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine, int nCmdShow)
#elif defined(__APPLE__)
int main(int argc, char** argv)

#elif defined(__linux)
int main(int argc, char** argv, char** env)

#else
int main(int argc, char** argv)
#endif
    try {

Нарешті тут ініціалізуємо всі системи у “розумні структурі”

  SDL_Guard sdl_guart(SDL_INIT_EVERYTHING);

  const int WINDOW_WIDTH{680}, WINDOWS_HEIGHT{480};

Створюємо вікно функцією SDL_CreateWindow(const char *title, int x, int y, int w, int h, Uint32 flags). Перший параметр це заголовок (назва) вікна і є собою c-string параметром. Інші параметри вказують на позицію та розміри вікна.

--
--
--
--
--
SDL_WINDOW_FULLSCREENfullscreen window
SDL_WINDOW_OPENGLwindow usable with OpenGL context
SDL_WINDOW_SHOWNwindow is visible
SDL_WINDOW_HIDDENwindow is not visible
SDL_WINDOW_BORDERLESSno window decoration
SDL_WINDOW_RESIZABLEwindow can be resized
SDL_WINDOW_MINIMIZEDwindow is minimized
SDL_WINDOW_MAXIMIZEDwindow is maximized
SDL_WINDOW_MOUSE_GRABBEDwindow has grabbed mouse input
SDL_WINDOW_INPUT_FOCUSwindow has input focus
SDL_WINDOW_MOUSE_FOCUSwindow has mouse focus
SDL_WINDOW_FULLSCREEN_DESKTOP
SDL_WINDOW_FOREIGNwindow not created by SDL
SDL_WINDOW_ALLOW_HIGHDPIwindow should be created in high-DPI mode if supported. On macOS NSHighResolutionCapable must be set true in the application’s Info.plist for this to have any effect.
SDL_WINDOW_MOUSE_CAPTUREwindow has mouse captured (unrelated to MOUSE_GRABBED)
SDL_WINDOW_ALWAYS_ON_TOPwindow should always be above others
SDL_WINDOW_SKIP_TASKBARwindow should not be added to the taskbar
SDL_WINDOW_UTILITYwindow should be treated as a utility window
SDL_WINDOW_TOOLTIPwindow should be treated as a tooltip
SDL_WINDOW_POPUP_MENUwindow should be treated as a popup menu
SDL_WINDOW_KEYBOARD_GRABBEDwindow has grabbed keyboard input
SDL_WINDOW_VULKANwindow usable for Vulkan surface
SDL_WINDOW_METALwindow usable for Metal view
SDL_WINDOW_INPUT_GRABBEDequivalent to SDL_WINDOW_MOUSE_GRABBED for compatibility
  UniqueWindow the_window{
      SDL_CreateWindow("Example", SDL_WINDOWPOS_UNDEFINED,
                       SDL_WINDOWPOS_UNDEFINED, WINDOW_WIDTH, WINDOWS_HEIGHT,
                       SDL_WINDOW_SHOWN),

Завжди створене вікно по завершеннютреба знищувати SDL_DestroyWindow(SDL_Window*)

      SDL_DestroyWindow};

Переред початком роботи з вікном потрібно перевірити чи вдалося його створити.

  if (!the_window)
    throw std::runtime_error("Error creating window: " +
                             std::string(SDL_GetError()));

Отримуємо вкзівник на поверхню по якій бужемо малювати

  SDL_Surface* winSurface{SDL_GetWindowSurface(the_window.get())};

Перевіряємо чи вдалося отримати вказіник на поверхню

  if (!winSurface)
    throw std::runtime_error("Error getting surface: " +
                             std::string(SDL_GetError()));

  SDL_FillRect(winSurface, NULL,
               SDL_MapRGB(winSurface->format, 0xFF, 0xFF, 0xFF));

  SDL_Event e;

  bool quit{false};

  do {
    SDL_UpdateWindowSurface(the_window.get());
    while (SDL_PollEvent(&e)) {
      switch (e.type) {
        case SDL_QUIT:
          quit = true;
          break;
        default:
          break;
      }
    }
  } while (!quit);

  return EXIT_SUCCESS;
}

catch (const std::exception& e) {
  std::cout << e.what() << std::endl;
  return EXIT_FAILURE;
}

catch (...) {
  std::cout << "Something wrong\n";
  return -EXIT_FAILURE;
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.11)

project(Lesson01)

add_executable(${PROJECT_NAME} main.cpp)

target_link_libraries(${PROJECT_NAME} SDL2)

Збирання проекту

cmake -B build -G="Unix Makefiles"
cmake --build ./build -j$(nproc)
./Lesson01

Заняття №2