--- /dev/null
+#ifndef LIBDS_UTIL_BOX_H
+#define LIBDS_UTIL_BOX_H
+
+#include <stdbool.h>
+#include <wayland-server.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * A box representing a rectangle region in a 2D space.
+ *
+ * The x and y coordinates are inclusive, and the width and height lengths are
+ * exclusive. In other words, the box starts from the coordinates (x, y), and
+ * goes up to but not including (x + width, y + height)
+ */
+struct ds_box {
+ int x, y;
+ int width, height;
+};
+
+/**
+ * A floating-point box representing a rectangle region in a 2D space.
+ *
+ * ds_fbox has the same semantics as ds_box
+ */
+struct ds_fbox {
+ double x, y;
+ double width, height;
+};
+
+/**
+ * Finds the closest point within the box bounds
+ *
+ * Returns false if the box is empty
+ */
+bool ds_box_closest_point(const struct ds_box *box, double x, double y,
+ double *dest_x, double *dest_y);
+
+/**
+ * Gives the intersecting box between two ds_box.
+ *
+ * Returns an empty ds_box if the provided ds_box don't intersect.
+ */
+bool ds_box_intersection(struct ds_box *dest, const struct ds_box *box_a,
+ const struct ds_box *box_b);
+
+/**
+ * Verifies if a point is contained within the bounds of a given ds_box.
+ *
+ * For example:
+ * - A point at (100, 50) is not contained in the box (0, 0, 100, 50).
+ * - A point at (10, 10) is contained in the box (10, 0, 50, 50).
+ */
+bool ds_box_contains_point(const struct ds_box *box, double x, double y);
+
+/**
+ * Checks whether a box is empty or not.
+ *
+ * A ds_box is considered empty if its width and/or height is zero or negative.
+ */
+bool ds_box_empty(const struct ds_box *box);
+
+/**
+ * Transforms a box inside a (0, 0, width, height) box.
+ */
+void ds_box_transform(struct ds_box *dest, const struct ds_box *box,
+ enum wl_output_transform transform, int width, int height);
+
+/**
+ * Checks whether a box is empty or not.
+ *
+ * A ds_box is considered empty if its width and/or height is zero or negative.
+ */
+bool ds_fbox_empty(const struct ds_fbox *box);
+
+/**
+ * Transforms a floating-point box inside a (0, 0, width, height) box.
+ */
+void ds_fbox_transform(struct ds_fbox *dest, const struct ds_fbox *box,
+ enum wl_output_transform transform, double width, double height);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+#include "libds/util/box.h"
+
+WL_EXPORT bool
+ds_box_closest_point(const struct ds_box *box, double x, double y,
+ double *dest_x, double *dest_y)
+{
+ // if box is empty, then it contains no points, so no closest point either
+ if (box->width <= 0 || box->height <= 0)
+ return false;
+
+ // find the closest x point
+ if (x < box->x) {
+ *dest_x = box->x;
+ } else if (x >= box->x + box->width) {
+ *dest_x = box->x + box->width - 1;
+ } else {
+ *dest_x = x;
+ }
+
+ // find closest y point
+ if (y < box->y) {
+ *dest_y = box->y;
+ } else if (y >= box->y + box->height) {
+ *dest_y = box->y + box->height - 1;
+ } else {
+ *dest_y = y;
+ }
+
+ return true;
+}
+
+WL_EXPORT bool
+ds_box_empty(const struct ds_box *box)
+{
+ return box == NULL || box->width <= 0 || box->height <= 0;
+}
+
+WL_EXPORT bool
+ds_box_intersection(struct ds_box *dest,
+ const struct ds_box *box_a, const struct ds_box *box_b)
+{
+ bool a_empty = ds_box_empty(box_a);
+ bool b_empty = ds_box_empty(box_b);
+
+ if (a_empty || b_empty) {
+ dest->x = 0;
+ dest->y = 0;
+ dest->width = -100;
+ dest->height = -100;
+ return false;
+ }
+
+ int x1 = fmax(box_a->x, box_b->x);
+ int y1 = fmax(box_a->y, box_b->y);
+ int x2 = fmin(box_a->x + box_a->width, box_b->x + box_b->width);
+ int y2 = fmin(box_a->y + box_a->height, box_b->y + box_b->height);
+
+ dest->x = x1;
+ dest->y = y1;
+ dest->width = x2 - x1;
+ dest->height = y2 - y1;
+
+ return !ds_box_empty(dest);
+}
+
+WL_EXPORT bool
+ds_box_contains_point(const struct ds_box *box, double x, double y)
+{
+ if (ds_box_empty(box)) {
+ return false;
+ } else {
+ return x >= box->x && x < box->x + box->width &&
+ y >= box->y && y < box->y + box->height;
+ }
+}
+
+WL_EXPORT void
+ds_box_transform(struct ds_box *dest, const struct ds_box *box,
+ enum wl_output_transform transform, int width, int height) {
+ struct ds_box src = *box;
+
+ if (transform % 2 == 0) {
+ dest->width = src.width;
+ dest->height = src.height;
+ } else {
+ dest->width = src.height;
+ dest->height = src.width;
+ }
+
+ switch (transform) {
+ case WL_OUTPUT_TRANSFORM_NORMAL:
+ dest->x = src.x;
+ dest->y = src.y;
+ break;
+ case WL_OUTPUT_TRANSFORM_90:
+ dest->x = height - src.y - src.height;
+ dest->y = src.x;
+ break;
+ case WL_OUTPUT_TRANSFORM_180:
+ dest->x = width - src.x - src.width;
+ dest->y = height - src.y - src.height;
+ break;
+ case WL_OUTPUT_TRANSFORM_270:
+ dest->x = src.y;
+ dest->y = width - src.x - src.width;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED:
+ dest->x = width - src.x - src.width;
+ dest->y = src.y;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+ dest->x = src.y;
+ dest->y = src.x;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+ dest->x = src.x;
+ dest->y = height - src.y - src.height;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+ dest->x = height - src.y - src.height;
+ dest->y = width - src.x - src.width;
+ break;
+ }
+}
+
+WL_EXPORT bool
+ds_fbox_empty(const struct ds_fbox *box)
+{
+ return box == NULL || box->width <= 0 || box->height <= 0;
+}
+
+WL_EXPORT void
+ds_fbox_transform(struct ds_fbox *dest, const struct ds_fbox *box,
+ enum wl_output_transform transform, double width, double height)
+{
+ struct ds_fbox src = *box;
+
+ if (transform % 2 == 0) {
+ dest->width = src.width;
+ dest->height = src.height;
+ } else {
+ dest->width = src.height;
+ dest->height = src.width;
+ }
+
+ switch (transform) {
+ case WL_OUTPUT_TRANSFORM_NORMAL:
+ dest->x = src.x;
+ dest->y = src.y;
+ break;
+ case WL_OUTPUT_TRANSFORM_90:
+ dest->x = height - src.y - src.height;
+ dest->y = src.x;
+ break;
+ case WL_OUTPUT_TRANSFORM_180:
+ dest->x = width - src.x - src.width;
+ dest->y = height - src.y - src.height;
+ break;
+ case WL_OUTPUT_TRANSFORM_270:
+ dest->x = src.y;
+ dest->y = width - src.x - src.width;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED:
+ dest->x = width - src.x - src.width;
+ dest->y = src.y;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+ dest->x = src.y;
+ dest->y = src.x;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+ dest->x = src.x;
+ dest->y = height - src.y - src.height;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+ dest->x = height - src.y - src.height;
+ dest->y = width - src.x - src.width;
+ break;
+ }
+}