Advertisement
fofsdfsdfsdfdfsdfdsf

Untitled

Apr 25th, 2025 (edited)
26
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 24.49 KB | None | 0 0
  1. /*
  2. Let's implement a simple graphic editor.
  3.  
  4. Main features:
  5.  
  6. * Crossplatflorm, written in C++ and SDL2.
  7. * Image is fixed size 640x640 monochrome(0x111111 black background, 0xEEEEEE white foreground by default)
  8. * It has layers(no groups, starts with a single layer).
  9. * Each layer consists of strokes.
  10. * Each layer has offset (dx, dy), zero by default
  11. * Each strokes consists of connected points.
  12.  
  13. Command handler: add a new stroke
  14.  
  15. * When user clicks with LMB, a command is initiated: a new stroke starts.
  16. * As user moves the mouse around with LMB pressed, the stroke is extended towards the mouse position.
  17. * If LMB is lifted, stroke is added to the current layer
  18. * If RMB is clicked when stroke is being drawn, we count it as a cancel. The stroke is removed and we wait until both LMB and RMB are lifted.
  19.  
  20. Command handler: split-end
  21.  
  22. * If user presses single letter 'S', a command is initiated: a new split starts. User has to choose how many (last) strokes to move to the new layer.
  23. * If user scrolls the wheel up, more strokes are being selected(one by one), starting from the last strokes.
  24. * If user scrolls the wheel down, deselect most recently selected strokes. (Last stroke to be selected - first stroke to be deselected)
  25. * In this mode strokes instead of white have two colors:
  26.   * non-selected strokes: dull white (around 0x777777)
  27.   * selected strokes: red (0xC44141)
  28. * If user clicks RMB, operation is cancelled
  29. * If user clicks LMB, selected strokes are removed from the selected layer and moved to the new one, after the previously selected layer. The new layer is selected.
  30.   * If all strokes are selected, no new layer is being created, current layer is being reselected
  31.  
  32. Command handler: split-beginning
  33.  
  34. * If user presses Shift-S, a command is initiated: a new split at the beginning starts. User has to choos how many strokes at the beginning of the layer to select as in split-end.
  35. * If user scrolls the wheel up, more strokes are being selected(one by one), starting from the first strokes.
  36. * If user scrolls the wheel down, deselect most recently selected strokes. (Last stroke to be selected - first stroke to be deselected)
  37. * In this mode strokes instead of white have two colors:
  38.   * non-selected strokes: dull white (around 0x777777)
  39.   * selected strokes: red (0xC44141)
  40. * If user clicks RMB, operation is cancelled
  41. * If user clicks LMB, selected strokes are removed from the selected layer and moved to the new one, before the previously selected layer. The new layer is selected.
  42.   * If all strokes are selected, no new layer is being created, current layer is being reselected
  43.  
  44. Command handler: select-layer.
  45. * When a layer is selected, its stroke color is changed to 0xFFFFFF. If no strokes exists, change background to 0x333333 (change it back to 0x111111 once a stroke is added or other layer with stroke is selected)
  46. * Pressing `tab` moves cursor to the next layer if it exists.
  47.   * if it doesn't, selects the first layer
  48. * Pressing `shift-tab` moves cursor to the previous layer if it exists.
  49.   * if it doesn't, selects the last layer
  50.  
  51. Command handler: new-layer
  52. * If user presses 'Ins', create a new layer after the current one and select it
  53. * If user presses 'Del', delete a current layer and select the previous one
  54.  
  55. Command handler: transpose-layer
  56. * If user presses middle mouse, start to transpose the layer: if cursor is moved from (x0, y0) to (x1, y1), then change layer offset(change dx, dy of the layer)
  57. * If middle mouse is released, end the layer transposing.
  58. * If right mouse is clicked, cancel the operation.
  59.  
  60.  
  61. Take offset into account:
  62. * When layer is rendered, take its offset dx,dy into account. E.g. if its stroke goes (0,0)->(10,10) and dx,dy=(1,2), then rendered stroke should be (1,2)->(11,12)
  63. * When you add stroke to a layer, make sure you take applied offset into account. I.e if dx,dy=100,0 and user draws line (100,0)->(105,0) on screen, then in reality it's a stroke (0,0)->(5,0)
  64.  
  65.  
  66. Implementation details:
  67. for a stroke feel free to use struct with std::vector<SDL_Point>
  68. for a layer feel free to use struct with std::deque<Stroke>
  69.  
  70. Not required:
  71. Undo,redo,save,load
  72.  
  73. */
  74.  
  75. #include <SDL2/SDL.h>
  76. #include <vector>
  77. #include <deque>
  78. #include <iostream>
  79. #include <numeric>
  80. #include <algorithm>
  81.  
  82. // --- Configuration ---
  83. const int SCREEN_WIDTH = 640;
  84. const int SCREEN_HEIGHT = 640;
  85. const SDL_Color COLOR_BACKGROUND = {0x11, 0x11, 0x11, 0xFF};
  86. const SDL_Color COLOR_BACKGROUND_SELECTED_EMPTY = {0x33, 0x33, 0x33, 0xFF};
  87. const SDL_Color COLOR_FOREGROUND = {0xDD, 0xDD, 0xDD, 0xFF};
  88. const SDL_Color COLOR_FOREGROUND_SELECTED = {0xDD, 0xFF, 0xDD, 0xFF};
  89. const SDL_Color COLOR_SPLIT_DULL = {0x77, 0x77, 0x77, 0xFF};
  90. const SDL_Color COLOR_SPLIT_SELECTED = {0xC4, 0x41, 0x41, 0xFF};
  91.  
  92. // --- Data Structures ---
  93.  
  94. // Represents a single continuous line drawn by the user.
  95. // Honestly, it's just a bunch of points. Don't overthink it.
  96. struct Stroke {
  97.     std::vector<SDL_Point> points;
  98. };
  99.  
  100. // A layer contains multiple strokes and has an offset.
  101. // Think of it like transparent sheets you stack. Duh.
  102. struct Layer {
  103.     std::deque<Stroke> strokes;
  104.     SDL_Point offset = {0, 0}; // How much this layer is shifted.
  105. };
  106.  
  107. // --- Application State ---
  108. enum class CommandState {
  109.     IDLE,
  110.     DRAWING_STROKE,
  111.     TRANSPOSING_LAYER,
  112.     SPLITTING_END,
  113.     SPLITTING_BEGINNING
  114. };
  115.  
  116. // --- Helper Functions ---
  117.  
  118. // Set SDL draw color using our SDL_Color struct.
  119. // Saves you like, two lines of code. You're welcome. 😒
  120. void SetRenderDrawColor(SDL_Renderer* renderer, const SDL_Color& color) {
  121.     SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a);
  122. }
  123.  
  124. // Transform screen coordinates to world coordinates for the selected layer.
  125. // Basically, undoing the layer's offset. Pay attention!
  126. SDL_Point ScreenToWorld(const SDL_Point& screenPoint, const SDL_Point& layerOffset) {
  127.     return {screenPoint.x - layerOffset.x, screenPoint.y - layerOffset.y};
  128. }
  129.  
  130. // Transform world coordinates (relative to layer) to screen coordinates.
  131. // Applying the layer's offset. Simple, right?
  132. SDL_Point WorldToScreen(const SDL_Point& worldPoint, const SDL_Point& layerOffset) {
  133.     return {worldPoint.x + layerOffset.x, worldPoint.y + layerOffset.y};
  134. }
  135.  
  136. // Draw a single stroke, applying the layer's offset.
  137. // Takes the points in the stroke, adds the offset, then draws lines.
  138. void DrawStroke(SDL_Renderer* renderer, const Stroke& stroke, const SDL_Point& layerOffset, const SDL_Color& color) {
  139.     if (stroke.points.size() < 2) {
  140.         // Can't draw a line with less than 2 points, obviously.
  141.         if (stroke.points.size() == 1) {
  142.             // Draw a single point if that's all there is.
  143.              SDL_Point screenPoint = WorldToScreen(stroke.points[0], layerOffset);
  144.              SetRenderDrawColor(renderer, color);
  145.              SDL_RenderDrawPoint(renderer, screenPoint.x, screenPoint.y);
  146.         }
  147.         return;
  148.     }
  149.  
  150.     // Convert world points to screen points for drawing
  151.     std::vector<SDL_Point> screenPoints(stroke.points.size());
  152.     std::transform(stroke.points.begin(), stroke.points.end(), screenPoints.begin(),
  153.                    [&](const SDL_Point& p) { return WorldToScreen(p, layerOffset); });
  154.  
  155.     SetRenderDrawColor(renderer, color);
  156.     SDL_RenderDrawLines(renderer, screenPoints.data(), static_cast<int>(screenPoints.size()));
  157. }
  158.  
  159.  
  160. // --- Main Application Logic ---
  161.  
  162. int main(int argc, char* argv[]) {
  163.     // --- SDL Initialization ---
  164.     // Boring boilerplate. If this fails, it's probably your fault. 💢
  165.     if (SDL_Init(SDL_INIT_VIDEO) < 0) {
  166.         std::cerr << "SDL could not initialize! SDL_Error: " << SDL_GetError() << std::endl;
  167.         return 1;
  168.     }
  169.  
  170.     SDL_Window* window = SDL_CreateWindow("Tsundere Paint (It's not like I made it for YOU!)",
  171.                                           SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
  172.                                           SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
  173.     if (!window) {
  174.         std::cerr << "Window could not be created! SDL_Error: " << SDL_GetError() << std::endl;
  175.         SDL_Quit();
  176.         return 1;
  177.     }
  178.  
  179.     SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
  180.     if (!renderer) {
  181.         std::cerr << "Renderer could not be created! SDL Error: " << SDL_GetError() << std::endl;
  182.         SDL_DestroyWindow(window);
  183.         SDL_Quit();
  184.         return 1;
  185.     }
  186.  
  187.     // --- Application Data ---
  188.     std::vector<Layer> layers;
  189.     layers.push_back({}); // Start with one empty layer. How generous of me.
  190.     size_t selected_layer_index = 0;
  191.     CommandState current_state = CommandState::IDLE;
  192.  
  193.     // State for drawing
  194.     Stroke current_stroke;
  195.     bool lmb_down = false;
  196.     bool rmb_down = false; // Track RMB separately for cancellation
  197.  
  198.     // State for transposing
  199.     SDL_Point transpose_start_pos = {0, 0};
  200.     SDL_Point original_layer_offset = {0, 0};
  201.     bool mmb_down = false;
  202.  
  203.     // State for splitting
  204.     int split_count = 0; // How many strokes are selected for splitting
  205.  
  206.     // --- Main Loop ---
  207.     bool quit = false;
  208.     SDL_Event e;
  209.  
  210.     while (!quit) {
  211.         // --- Event Handling ---
  212.         // Pay attention here, this is where things happen!
  213.         while (SDL_PollEvent(&e) != 0) {
  214.             if (e.type == SDL_QUIT) {
  215.                 quit = true;
  216.             }
  217.  
  218.             // --- Mouse Button Events ---
  219.             else if (e.type == SDL_MOUSEBUTTONDOWN) {
  220.                 SDL_Point mousePos = {e.button.x, e.button.y};
  221.  
  222.                 if (e.button.button == SDL_BUTTON_LEFT) {
  223.                     lmb_down = true;
  224.                     if (current_state == CommandState::IDLE) {
  225.                         current_state = CommandState::DRAWING_STROKE;
  226.                         current_stroke.points.clear();
  227.                         // Add first point, adjusted for layer offset
  228.                         current_stroke.points.push_back(ScreenToWorld(mousePos, layers[selected_layer_index].offset));
  229.                     } else if (current_state == CommandState::SPLITTING_END || current_state == CommandState::SPLITTING_BEGINNING) {
  230.                         // Confirm split
  231.                         if (split_count > 0 && !layers.empty() && !layers[selected_layer_index].strokes.empty() && split_count < layers[selected_layer_index].strokes.size()) {
  232.                             Layer new_layer;
  233.                             new_layer.offset = layers[selected_layer_index].offset; // Inherit offset for now
  234.                             auto& current_strokes = layers[selected_layer_index].strokes;
  235.  
  236.                             if (current_state == CommandState::SPLITTING_END) {
  237.                                 // Move last 'split_count' strokes
  238.                                 auto start_it = current_strokes.end() - split_count;
  239.                                 std::move(start_it, current_strokes.end(), std::back_inserter(new_layer.strokes));
  240.                                 current_strokes.erase(start_it, current_strokes.end());
  241.                                 // Insert new layer *after* current
  242.                                 selected_layer_index++;
  243.                                 layers.insert(layers.begin() + selected_layer_index, std::move(new_layer));
  244.                             } else { // SPLITTING_BEGINNING
  245.                                 // Move first 'split_count' strokes
  246.                                 auto end_it = current_strokes.begin() + split_count;
  247.                                 std::move(current_strokes.begin(), end_it, std::back_inserter(new_layer.strokes));
  248.                                 current_strokes.erase(current_strokes.begin(), end_it);
  249.                                 // Insert new layer *before* current (index stays same, points to new layer)
  250.                                 layers.insert(layers.begin() + selected_layer_index, std::move(new_layer));
  251.                                 // No need to change selected_layer_index, it now points to the new layer
  252.                             }
  253.                         }
  254.                         // Reset split state regardless of success
  255.                         current_state = CommandState::IDLE;
  256.                         split_count = 0;
  257.                     }
  258.                 } else if (e.button.button == SDL_BUTTON_RIGHT) {
  259.                     rmb_down = true;
  260.                     if (current_state == CommandState::DRAWING_STROKE) {
  261.                         // Cancel drawing
  262.                         current_stroke.points.clear();
  263.                         // Don't reset state until LMB is also up
  264.                     } else if (current_state == CommandState::TRANSPOSING_LAYER) {
  265.                         // Cancel transpose
  266.                         layers[selected_layer_index].offset = original_layer_offset;
  267.                         current_state = CommandState::IDLE;
  268.                         mmb_down = false; // Ensure MMB state is reset
  269.                     } else if (current_state == CommandState::SPLITTING_END || current_state == CommandState::SPLITTING_BEGINNING) {
  270.                         // Cancel split
  271.                         current_state = CommandState::IDLE;
  272.                         split_count = 0;
  273.                     }
  274.                 } else if (e.button.button == SDL_BUTTON_MIDDLE) {
  275.                      if (current_state == CommandState::IDLE && !layers.empty()) {
  276.                         mmb_down = true;
  277.                         current_state = CommandState::TRANSPOSING_LAYER;
  278.                         transpose_start_pos = mousePos;
  279.                         original_layer_offset = layers[selected_layer_index].offset;
  280.                     }
  281.                 }
  282.             } else if (e.type == SDL_MOUSEBUTTONUP) {
  283.                  SDL_Point mousePos = {e.button.x, e.button.y};
  284.                  if (e.button.button == SDL_BUTTON_LEFT) {
  285.                     lmb_down = false;
  286.                     if (current_state == CommandState::DRAWING_STROKE) {
  287.                         if (!current_stroke.points.empty() && !rmb_down) { // Only add if not cancelled
  288.                             // Add the final point
  289.                              current_stroke.points.push_back(ScreenToWorld(mousePos, layers[selected_layer_index].offset));
  290.                              if (current_stroke.points.size() >= 2) { // Need at least 2 points for a line
  291.                                 layers[selected_layer_index].strokes.push_back(std::move(current_stroke));
  292.                              }
  293.                         }
  294.                         // Reset drawing state only if RMB is also up (or wasn't pressed)
  295.                         if (!rmb_down) {
  296.                            current_state = CommandState::IDLE;
  297.                            current_stroke.points.clear(); // Clear just in case
  298.                         }
  299.                     }
  300.                     // If RMB was down (cancel) and LMB is now up, reset state if needed
  301.                     else if (rmb_down && current_state == CommandState::DRAWING_STROKE) {
  302.                          current_state = CommandState::IDLE;
  303.                          current_stroke.points.clear();
  304.                     }
  305.                  } else if (e.button.button == SDL_BUTTON_RIGHT) {
  306.                      rmb_down = false;
  307.                      // If LMB was down (drawing) and RMB is now up, reset state if needed
  308.                      if (lmb_down && current_state == CommandState::DRAWING_STROKE) {
  309.                           current_state = CommandState::IDLE;
  310.                           current_stroke.points.clear();
  311.                      }
  312.                  } else if (e.button.button == SDL_BUTTON_MIDDLE) {
  313.                      mmb_down = false;
  314.                      if (current_state == CommandState::TRANSPOSING_LAYER) {
  315.                          current_state = CommandState::IDLE;
  316.                          // Offset is already updated during mouse motion
  317.                      }
  318.                  }
  319.             }
  320.  
  321.             // --- Mouse Motion Event ---
  322.             else if (e.type == SDL_MOUSEMOTION) {
  323.                 SDL_Point mousePos = {e.motion.x, e.motion.y};
  324.                 if (current_state == CommandState::DRAWING_STROKE && lmb_down && !rmb_down) {
  325.                     // Add point relative to layer offset
  326.                     SDL_Point worldPos = ScreenToWorld(mousePos, layers[selected_layer_index].offset);
  327.                     // Avoid adding duplicate points if the mouse hasn't moved significantly
  328.                     // (in world coordinates!)
  329.                     if (current_stroke.points.empty() ||
  330.                         current_stroke.points.back().x != worldPos.x ||
  331.                         current_stroke.points.back().y != worldPos.y)
  332.                     {
  333.                          current_stroke.points.push_back(worldPos);
  334.                     }
  335.                 } else if (current_state == CommandState::TRANSPOSING_LAYER && mmb_down) {
  336.                     int dx = mousePos.x - transpose_start_pos.x;
  337.                     int dy = mousePos.y - transpose_start_pos.y;
  338.                     layers[selected_layer_index].offset.x = original_layer_offset.x + dx;
  339.                     layers[selected_layer_index].offset.y = original_layer_offset.y + dy;
  340.                 }
  341.             }
  342.  
  343.             // --- Mouse Wheel Event (for Splitting) ---
  344.             else if (e.type == SDL_MOUSEWHEEL) {
  345.                  if (current_state == CommandState::SPLITTING_END || current_state == CommandState::SPLITTING_BEGINNING) {
  346.                      if (!layers.empty() && !layers[selected_layer_index].strokes.empty()) {
  347.                          int max_split = static_cast<int>(layers[selected_layer_index].strokes.size());
  348.                          if (e.wheel.y > 0) { // Scroll Up
  349.                              split_count++;
  350.                          } else if (e.wheel.y < 0) { // Scroll Down
  351.                              split_count--;
  352.                          }
  353.                          // Clamp the value. Don't be an idiot.
  354.                          split_count = std::max(0, std::min(split_count, max_split));
  355.                      }
  356.                  }
  357.             }
  358.  
  359.  
  360.             // --- Keyboard Events ---
  361.             else if (e.type == SDL_KEYDOWN) {
  362.                  // Only process keydown if not in the middle of a mouse action
  363.                  if (current_state == CommandState::IDLE) {
  364.                      SDL_Keycode key = e.key.keysym.sym;
  365.                      SDL_Keymod mod = SDL_GetModState();
  366.                      bool shift_pressed = (mod & KMOD_SHIFT);
  367.  
  368.                      if (key == SDLK_TAB && !layers.empty()) {
  369.                          if (shift_pressed) { // Shift + Tab: Previous Layer
  370.                              if (selected_layer_index == 0) {
  371.                                  selected_layer_index = layers.size() - 1; // Wrap to last
  372.                              } else {
  373.                                  selected_layer_index--;
  374.                              }
  375.                          } else { // Tab: Next Layer
  376.                              selected_layer_index++;
  377.                              if (selected_layer_index >= layers.size()) {
  378.                                  selected_layer_index = 0; // Wrap to first
  379.                              }
  380.                          }
  381.                      } else if (key == SDLK_INSERT) {
  382.                          // Insert new layer *after* current
  383.                          size_t insert_pos = selected_layer_index + 1;
  384.                          layers.insert(layers.begin() + insert_pos, Layer());
  385.                          selected_layer_index = insert_pos; // Select the new layer
  386.                      } else if (key == SDLK_DELETE && !layers.empty()) {
  387.                          layers.erase(layers.begin() + selected_layer_index);
  388.                          if (layers.empty()) {
  389.                              // If we deleted the last layer, add a new default one
  390.                              layers.push_back({});
  391.                              selected_layer_index = 0;
  392.                          } else {
  393.                              // Select previous layer, or 0 if we deleted the first
  394.                              if (selected_layer_index >= layers.size()) {
  395.                                  selected_layer_index = layers.size() - 1;
  396.                              }
  397.                              // No change needed if selected_layer_index is still valid
  398.                          }
  399.                      } else if (key == SDLK_s && !layers.empty()) {
  400.                          if (shift_pressed) { // Shift + S: Split Beginning
  401.                             current_state = CommandState::SPLITTING_BEGINNING;
  402.                             split_count = 0; // Reset selection
  403.                          } else { // S: Split End
  404.                             current_state = CommandState::SPLITTING_END;
  405.                             split_count = 0; // Reset selection
  406.                          }
  407.                      }
  408.                  }
  409.             }
  410.         } // End event polling loop
  411.  
  412.         // --- Rendering ---
  413.         // Time to draw everything. Don't blink, you might miss it.
  414.  
  415.         // 1. Clear Screen
  416.         // Use special background if the selected layer is empty
  417.         bool selected_is_empty = layers.empty() || layers[selected_layer_index].strokes.empty();
  418.         const SDL_Color& bg_color = (selected_is_empty && current_state == CommandState::IDLE) ? COLOR_BACKGROUND_SELECTED_EMPTY : COLOR_BACKGROUND;
  419.         SetRenderDrawColor(renderer, bg_color);
  420.         SDL_RenderClear(renderer);
  421.  
  422.         // 2. Draw Layers
  423.         for (size_t i = 0; i < layers.size(); ++i) {
  424.             const auto& layer = layers[i];
  425.             bool is_selected_layer = (i == selected_layer_index);
  426.  
  427.             // Determine base color for strokes in this layer
  428.             SDL_Color base_stroke_color = is_selected_layer ? COLOR_FOREGROUND_SELECTED : COLOR_FOREGROUND;
  429.  
  430.             if (current_state == CommandState::SPLITTING_END && is_selected_layer) {
  431.                  int num_strokes = static_cast<int>(layer.strokes.size());
  432.                  for (int j = 0; j < num_strokes; ++j) {
  433.                      // Last 'split_count' strokes are selected (red)
  434.                      bool is_selected_for_split = (j >= num_strokes - split_count);
  435.                      const SDL_Color& color = is_selected_for_split ? COLOR_SPLIT_SELECTED : COLOR_SPLIT_DULL;
  436.                      DrawStroke(renderer, layer.strokes[j], layer.offset, color);
  437.                  }
  438.             } else if (current_state == CommandState::SPLITTING_BEGINNING && is_selected_layer) {
  439.                  int num_strokes = static_cast<int>(layer.strokes.size());
  440.                  for (int j = 0; j < num_strokes; ++j) {
  441.                       // First 'split_count' strokes are selected (red)
  442.                      bool is_selected_for_split = (j < split_count);
  443.                      const SDL_Color& color = is_selected_for_split ? COLOR_SPLIT_SELECTED : COLOR_SPLIT_DULL;
  444.                      DrawStroke(renderer, layer.strokes[j], layer.offset, color);
  445.                  }
  446.             } else {
  447.                 // Regular rendering
  448.                 for (const auto& stroke : layer.strokes) {
  449.                     DrawStroke(renderer, stroke, layer.offset, base_stroke_color);
  450.                 }
  451.             }
  452.         }
  453.  
  454.         // 3. Draw the stroke currently being drawn (if any)
  455.         if (current_state == CommandState::DRAWING_STROKE && !current_stroke.points.empty() && !rmb_down) {
  456.              DrawStroke(renderer, current_stroke, layers[selected_layer_index].offset, COLOR_FOREGROUND_SELECTED);
  457.         }
  458.  
  459.  
  460.         // 4. Present Renderer
  461.         SDL_RenderPresent(renderer);
  462.  
  463.     } // End main loop
  464.  
  465.     // --- Cleanup ---
  466.     // You better not forget this part, or you'll have memory leaks! 💢
  467.     SDL_DestroyRenderer(renderer);
  468.     SDL_DestroyWindow(window);
  469.     SDL_Quit();
  470.  
  471.     return 0; // Finished. Finally. Now leave me alone.
  472. }
  473.  
  474. /*
  475. --- How to Build (Example using g++) ---
  476.  
  477. You need SDL2 development libraries installed! How you do that depends on your OS.
  478. Google it yourself, baka!
  479.  
  480. Example command (Linux/macOS):
  481. g++ your_source_file.cpp -o your_executable -lSDL2
  482.  
  483. Example command (Windows with MinGW):
  484. g++ your_source_file.cpp -o your_executable.exe -Ipath/to/SDL2/include -Lpath/to/SDL2/lib -lmingw32 -lSDL2main -lSDL2
  485.  
  486. Replace 'your_source_file.cpp' with the name you save this code as.
  487. Replace 'your_executable' with the desired output name.
  488. Replace 'path/to/SDL2/include' and 'path/to/SDL2/lib' with the actual paths on your system if needed.
  489.  
  490. Honestly, setting up build environments is such a pain... why do I even bother? 😤
  491. */
  492.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement