Rectangles and SDL_Rect
Learn to create, render, and interact with basic rectangles using the SDL_Rect
and SDL_Color
types.
So far, we've learned how to create a window and fill it with a solid color. While useful, most applications need to draw more specific shapes and images.
One of the most fundamental shapes in 2D graphics is the rectangle. Rectangles are essential for user interfaces (buttons, text boxes), game elements (sprites, collision boxes), and defining areas on the screen (viewports, selection boxes).
SDL provides tools specifically for working with rectangles. We'll learn about the SDL_Rect
type for defining position and size, and the SDL_Color
type for managing colors independently of screen formats. We'll then combine these to draw rectangles and make them respond to basic mouse interaction.
Starting Point
We'll start with the basic SDL application structure we developed previously. This includes initializing SDL, creating a Window class to manage the SDL_Window
and its surface, and running the main event loop.
Creating a Rectangle Class
Let's add a Rectangle
class to manage our rectangles. We want our rectangle to render itself to the screen, so we'll add a Render()
function that accepts an SDL_Surface
pointer. This is the surface that our Rectangle
instance will render itself to:
// Rectangle.h
#pragma once
#include <SDL.h>
class Rectangle {
public:
void Render(SDL_Surface* Surface) const {
// ...
}
};
Over in our main
function, let's construct a Rectangle
and Render()
it at the appropriate time.
As we covered in our double buffering lesson, the rendering process starts with the call to GameWindow.Render()
- which fills the window surface with a solid color, and it ends with the call to GameWindow.Update()
- which triggers the buffer swap.
All of our other objects, such as our Rectangle
, should be rendered between these two steps:
#include <SDL.h>
#include "Window.h"
#include "Rectangle.h"
int main(int argc, char** argv) {
SDL_Init(SDL_INIT_VIDEO);
Window GameWindow;
Rectangle Rect;
SDL_Event E;
while (true) {
while (SDL_PollEvent(&E)) {
if (E.type == SDL_QUIT) {
SDL_Quit();
return 0;
}
}
GameWindow.Render();
Rect.Render(GameWindow.GetSurface());
GameWindow.Update();
}
return 0;
}
Rendering the Rectangle
You may remember from our Window
class that we can draw a rectangle using SDL_FillRect()
, which requires three arguments:
- The surface to draw to. This will be the argument provided to our
Render()
function. - The rectangular area to draw. In our
Window
class, we're passing anullptr
as this argument, indicating we want the color to fill the entire surface. In this lesson, we'll learn how to provide an argument here, in the form of anSDL_Rect
- The color we want to use. Like we did before, we can generate this color using
SDL_MapRGB()
.
// Rectangle.h
#pragma once
#include <SDL.h>
class Rectangle {
public:
void Render(SDL_Surface* Surface) const {
SDL_FillRect(
Surface,
// We'll provide this later
nullptr,
SDL_MapRGB(Surface->format, 255, 0, 0)
);
}
};
The SDL_Rect
Type
To represent a rectangle, SDL provides the SDL_Rect
type. It is a simple struct with four integer members that store the position and dimensions of the rectangle:
- The
x
andy
members represent the position of the top-left corner of the rectangle - The
w
member represents the width of the rectangle - The
h
member represents the height of the rectangle
Visually, it looks like this:

Remember, SDL's coordinate system uses the "y-down" convention, so increasing the y
value corresponds to moving the rectangle down.
Let's add an SDL_Rect
to our Rectangle
class. We'll also add it as a constructor parameter:
// Rectangle.h
#pragma once
#include <SDL.h>
class Rectangle {
public:
Rectangle(const SDL_Rect& Rect)
: Rect{Rect} {}
void Render(SDL_Surface* Surface) {/*...*/}
private:
SDL_Rect Rect;
};
We'll provide the argument to this constructor in our main.cpp
:
#include <SDL.h>
#include "Window.h"
#include "Rectangle.h"
int main(int argc, char** argv) {
SDL_Init(SDL_INIT_VIDEO);
Window GameWindow;
Rectangle Rect{SDL_Rect{50, 50, 50, 50}};
SDL_Event E;
while (true) {/*...*/}
return 0;
}
To use this rectangle for rendering, we provide a pointer to it as our second argument to SDL_FillRect()
:
// Rectangle.h
#pragma once
#include <SDL.h>
class Rectangle {
public:
Rectangle(const SDL_Rect& Rect)
: Rect{Rect} {}
void Render(SDL_Surface* Surface) const {
SDL_FillRect(
Surface,
&Rect,
SDL_MapRGB(Surface->format, 255, 0, 0)
);
}
private:
SDL_Rect Rect;
};
With these changes, we should now see our red rectangle being rendered to the screen:

The SDL_Color
Type
In our Render()
function, we're currently generating a red color for our rectangle. However, what if we want to store this color as a member variable, such that we change it from other functions in our Rectangle
class?
// Rectangle.h
#pragma once
#include <SDL.h>
class Rectangle {
public:
// ...
void SetColor(/* ??? */) {
// ???
}
// ...
};
Previously, we saw how we could store a specific color in a specific pixel format using a 32 bit unsigned integer. This is typically the value returned by a function such as SDL_MapRGB()
:
Uint32 Red{SDL_MapRGB(Surface->format, 255, 0, 0)};
However, storing our color as a Uint32
in the correct pixel format may not be easy.
In our simple program, we know we're only ever rendering to one surface - the window surface - so, we could design the Rectangle
class on that basis. However, this violates the design principle of encapsulation.
Ideally, our Rectangle
should be a self-contained component that does not know or care about what is going on elsewhere in our code. In that context, our Rectangle
doesn't know what surface it will be rendering to until Render()
is invoked. At that point, the SDL_Surface
is available as a parameter, and we can retrieve it's pixel format from the format
member variable.
A simple option for storing a color in a way that lets us apply the pixel format later would be to use three integers - one for each of the red, green, and blue channels:
// Rectangle.h
#pragma once
#include <SDL.h>
class Rectangle {
public:
Rectangle(const SDL_Rect& Rect)
: Rect{Rect} {}
void Render(SDL_Surface* Surface) const {
SDL_FillRect(
Surface,
&Rect,
SDL_MapRGB(
Surface->format,
Red, Green, Blue
)
);
}
void SetColor(int R, int G, int B) {
Red = R;
Green = G;
Blue = B;
}
private:
SDL_Rect Rect;
int Red, Green, Blue;
};
This works, but is a little messy, and it makes writting the getter more difficult. We'd prefer to have a type that can represent a color as a single value. To help with this, SDL provides the SDL_Color
type, which is useful for storing a color without locking that color down to a specific pixel format.
SDL_Color
is a basic struct with 4 members - r
, g
, b
, and a
, representing the red, green, blue, and alpha channels respectively. Each variable is an 8-bit unsigned integer, meaning they can store values from 0 to 255:
SDL_Color Black { 0, 0, 0, 255};
SDL_Color Gray {100, 100, 100, 255};
SDL_Color White {255, 255, 255, 255};
SDL_Color Red {255, 0, 0, 255};
SDL_Color Green { 0, 255, 0, 255};
SDL_Color Blue { 0, 0, 255, 255};
SDL_Color Orange {255, 165, 0, 255};
Let's update our Rectangle
to store its color as this type, alongside a getter and setter:
// Rectangle.h
#pragma once
#include <SDL.h>
class Rectangle {
public:
Rectangle(const SDL_Rect& Rect)
: Rect{Rect} {}
void Render(SDL_Surface* Surface) const {
SDL_FillRect(
Surface,
&Rect,
SDL_MapRGB(
Surface->format,
Color.r, Color.g, Color.b
)
);
}
void SetColor(const SDL_Color& NewColor) {
Color = NewColor;
}
SDL_Color GetColor() const {
return Color;
}
private:
SDL_Rect Rect;
// Default to red
SDL_Color Color{255, 0, 0, 255};
};
Reacting to Mouse Motion Events
Let's give our Rectangle
the ability to react to events. To set this up, we'll add a HandleEvent()
function to our class:
// Rectangle.h
// ...
class Rectangle {
public:
// ...
void HandleEvent(SDL_Event& E) {
// We'll implement this next
}
// ...
};
We'll also forward events to this function from our event loop:
// main.cpp
#include <SDL.h>
#include "Window.h"
#include "Rectangle.h"
int main(int argc, char** argv) {
SDL_Init(SDL_INIT_VIDEO);
Window GameWindow;
Rectangle Rect{SDL_Rect{50, 50, 50, 50}};
SDL_Event E;
while (true) {
while (SDL_PollEvent(&E)) {
Rect.HandleEvent(E);
if (E.type == SDL_QUIT) {
SDL_Quit();
return 0;
}
}
GameWindow.Render();
Rect.Render(GameWindow.GetSurface());
GameWindow.Update();
}
return 0;
}
To detect when the user hovers their mouse over the rectangle, we'll apply the techniques we introduced earlier in the chapter. We'll check for SDL_MOUSEMOTION
events and, when they occur, we'll update an isPointerHovering
boolean to true
if the cursor was moved over our rectangle:
// Rectangle.h
// ...
class Rectangle {
public:
// ...
void HandleEvent(SDL_Event& E) {
if (E.type == SDL_MOUSEMOTION) {
isPointerHovering = isWithinRect(
E.motion.x, E.motion.y
);
}
}
// ...
private:
// ...
bool isPointerHovering{false};
bool isWithinRect(int x, int y) {
// We'll implement this next
return false;
}
};
Detecting if a Point is within an SDL_Rect
Our isWithinRect()
function needs to return true
if the x
and y
coordinates are within the bounds of our Rect
variable. There are a few ways of determining this this. One approach is to check if the point is not outside the bounds in all 4 directions:

If the point is not in any of the areas we marked in red, we can deduce that it must be within the rectangle. In code, we can implement the check like this:
// Rectangle.h
// ...
class Rectangle {
// ...
private:
// ...
bool isWithinRect(int x, int y) {
// Too far left
if (x < Rect.x) return false;
// Too far right
if (x > Rect.x + Rect.w) return false;
// Too far up
if (y < Rect.y) return false;
// Too far down
if (y > Rect.y + Rect.h) return false;
// If we got this far, that means the
// point is within the rectangle
return true;
}
};
Changing Color on Hover
Now that our Rectangle
is keeping track of whether the user's cursor is hovering over it, other functions in our class can react to it. For example, let's have our rectangle change color when hovered. We'll add a new member variable, getter, and setter:
// Rectangle.h
// ...
class Rectangle {
public:
// ...
void SetHoverColor(const SDL_Color& NewColor) {
HoverColor = NewColor;
}
SDL_Color GetHoverColor() const {
return HoverColor;
}
private:
// ...
SDL_Color HoverColor{0, 0, 255, 255};
};
Let's update our Render()
function to select a color based on the isPointerHovering
state. This is an ideal place to use structured binding:
// Rectangle.h
// ...
class Rectangle {
public:
// ...
void Render(SDL_Surface* Surface) const {
auto [r, g, b, a]{
isPointerHovering ? HoverColor : Color
};
SDL_FillRect(
Surface, &Rect,
SDL_MapRGB(Surface->format, r, g, b)
);
}
// ...
};
Note that, to use structured binding, we need to use all the variables of the SDL_Color
type, even though we don't need the a
(alpha) channel in this scenario.
Structured Binding
This lesson introduces Structured Binding, a handy tool for unpacking simple data structures
Now, when we hover our rectangle, it should change color from red to blue:

Reacting to Window Events
By default, SDL stops sending us mouse motion events once the pointer leaves our window.
If the rectangle is at the edge of the window, or the mouse is moving quickly, this means we may not be notified that the cursor has left our rectangle through leaving the window entirely.
To address this, we can additionally keep track of SDL_WINDOWEVENT_LEAVE
events, using the techniques we covered in the previous lesson:
// Rectangle.h
// ...
class Rectangle {
public:
// ...
void HandleEvent(SDL_Event& E) {
if (E.type == SDL_MOUSEMOTION) {
isPointerHovering = isWithinRect(
E.motion.x, E.motion.y
);
} else if (
E.type == SDL_WINDOWEVENT &&
E.window.event == SDL_WINDOWEVENT_LEAVE
) {
isPointerHovering = false;
}
}
// ...
};
Reacting to Mouse Click Events
Let's also allow our Rectangle
objects to react to mouse click events - that is, events with a type
of SDL_MOUSEBUTTONDOWN
:
// Rectangle.h
// ...
class Rectangle {
public:
// ...
void HandleEvent(SDL_Event& E) {
if (E.type == SDL_MOUSEMOTION) {
isPointerHovering = isWithinRect(
E.motion.x, E.motion.y
);
} else if (
E.type == SDL_WINDOWEVENT &&
E.window.event == SDL_WINDOWEVENT_LEAVE
) {
isPointerHovering = false;
} else if (E.type == SDL_MOUSEBUTTONDOWN) {
if (E.button.button == SDL_BUTTON_LEFT) {
std::cout << "A left-click happened "
"somewhere in the window\n";
}
}
}
// ...
};
The click events include x
and y
members, informing us of where the cursor was when the user clicked on our window. We can use this to determine whether they specifically clicked on the Rectangle
instance:
// Rectangle.h
// ...
class Rectangle {
public:
// ...
void HandleEvent(SDL_Event& E) {
if (E.type == SDL_MOUSEMOTION) {
isPointerHovering = isWithinRect(
E.motion.x, E.motion.y
);
} else if (
E.type == SDL_WINDOWEVENT &&
E.window.event == SDL_WINDOWEVENT_LEAVE
) {
isPointerHovering = false;
} else if (E.type == SDL_MOUSEBUTTONDOWN) {
if (isWithinRect(E.button.x, E.button.y)
&& E.button.button == SDL_BUTTON_LEFT
) {
std::cout << "A left-click happened "
"on me!\n";
}
}
}
// ...
};
Alternatively, we can use the isHovering
boolean we added in the previous section:
// Rectangle.h
// ...
class Rectangle {
public:
// ...
void HandleEvent(SDL_Event& E) {
if (E.type == SDL_MOUSEMOTION) {
isPointerHovering = isWithinRect(
E.motion.x, E.motion.y
);
} else if (
E.type == SDL_WINDOWEVENT &&
E.window.event == SDL_WINDOWEVENT_LEAVE
) {
isPointerHovering = false;
} else if (E.type == SDL_MOUSEBUTTONDOWN) {
if (isPointerHovering &&
E.button.button == SDL_BUTTON_LEFT
) {
std::cout << "A left-click happened "
"on me!\n";
}
}
}
// ...
};
Complete Code
Our program now includes a dedicated Rectangle.h
file defining our Rectangle
class. The main.cpp file creates an instance of this class and integrates its rendering and event handling into the main loop. This setup provides a good starting point for adding more complex features through the rest of this chapter.
Summary
In this lesson, we learned how to define, render, and interact with rectangles in SDL2. We created a Rectangle
class encapsulating an SDL_Rect
for geometry and an SDL_Color
for appearance, allowing us to draw shapes and respond to mouse input.
Key Takeaways:
SDL_Rect
defines a rectangle usingx
,y
(top-left corner),w
(width), andh
(height).SDL_Color
stores RGBA color values (0-255) independently of pixel formats.SDL_FillRect()
draws a filled rectangle onto anSDL_Surface
.SDL_MapRGB()
converts RGBA values into a surface-specific color format.- We can check if a point is inside an
SDL_Rect
by comparing its coordinates against the rectangle's boundaries. - Mouse motion (
SDL_MOUSEMOTION
) and click (SDL_MOUSEBUTTONDOWN
) events provide cursor coordinates. - Window events (
SDL_WINDOWEVENT_LEAVE
) help track when the cursor leaves the window.
Structuring SDL Programs
Discover how to organize SDL components using manager classes, inheritance, and polymorphism for cleaner code.