summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.build18
-rw-r--r--.gitignore3
-rw-r--r--chisel/.build12
-rw-r--r--chisel/attribute.hpp17
-rw-r--r--chisel/box.cpp285
-rw-r--r--chisel/box.hpp75
-rw-r--r--chisel/button.cpp42
-rw-r--r--chisel/button.hpp19
-rw-r--r--chisel/kiosk.cpp30
-rw-r--r--chisel/kiosk.hpp13
-rw-r--r--chisel/sdl_window.cpp141
-rw-r--r--chisel/sdl_window.hpp32
-rw-r--r--chisel/vertical_box.cpp27
-rw-r--r--chisel/vertical_box.hpp15
-rw-r--r--chisel/window.hpp17
-rw-r--r--main.cpp112
-rw-r--r--socket/readme6
-rw-r--r--socket/socket.hpp68
18 files changed, 932 insertions, 0 deletions
diff --git a/.build b/.build
new file mode 100644
index 0000000..4c3789e
--- /dev/null
+++ b/.build
@@ -0,0 +1,18 @@
+this.include_dirs = [Path('.').resolve()]
+this.objects = []
+this.deps = []
+
+this('chisel')
+
+this.objects += this.cpp.cc(
+ 'main.cpp',
+
+ include_dirs = this.include_dirs,
+ dependencies = this.deps
+)
+
+chisel = this.cpp.exe('chtest',
+ this.objects,
+
+ dependencies = this.deps
+)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..5f7172c
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+/bld/
+.ninja_*
+build.ninja
diff --git a/chisel/.build b/chisel/.build
new file mode 100644
index 0000000..4480210
--- /dev/null
+++ b/chisel/.build
@@ -0,0 +1,12 @@
+this.deps += CDependency(['sdl2'])
+
+this.objects += this.cpp.cc(
+ 'box.cpp',
+ 'button.cpp',
+ 'kiosk.cpp',
+ 'sdl_window.cpp',
+ 'vertical_box.cpp',
+
+ include_dirs = this.include_dirs,
+ dependencies = this.deps
+)
diff --git a/chisel/attribute.hpp b/chisel/attribute.hpp
new file mode 100644
index 0000000..2f94b9f
--- /dev/null
+++ b/chisel/attribute.hpp
@@ -0,0 +1,17 @@
+#pragma once
+
+namespace chisel {
+ enum AttributeType {
+ PX,
+ PR,
+ AUTO
+ };
+
+ struct Attribute {
+ enum AttributeType type;
+ union {
+ int px;
+ float pr;
+ };
+ };
+}
diff --git a/chisel/box.cpp b/chisel/box.cpp
new file mode 100644
index 0000000..3215c24
--- /dev/null
+++ b/chisel/box.cpp
@@ -0,0 +1,285 @@
+#include "box.hpp"
+#include "window.hpp"
+
+#include <algorithm>
+#include <iostream>
+
+namespace chisel {
+ Box::Box() {}
+ Box::~Box() {
+ for (auto &child : children)
+ delete child;
+ }
+
+ void Box::damage() {
+ if (window->damaged_set.count(this) > 0)
+ return;
+
+ window->damaged_set.insert(this);
+ window->damaged_deque.push_back(this);
+
+ for (auto& child : children)
+ child->damage();
+ }
+
+ void Box::draw() {
+ fill_rect(0, 0, width, height, width % 256, height % 256, (width + height) % 256);
+ }
+
+ void Box::fill_rect(int x0, int y0, int w, int h, int r, int g, int b) {
+ if (!parent)
+ return;
+
+ if (x0 < 0)
+ x0 = 0;
+ if (y0 < 0)
+ y0 = 0;
+ if (x0 + w >= width)
+ w = width - x0;
+ if (y0 + h >= height)
+ h = height - x0;
+
+ parent->fill_rect(x + x0, y + y0, w, h, r, g, b);
+ }
+
+ void Box::add_child(Box *child) {
+ child->window = window;
+ child->parent = this;
+ children.push_back(child);
+ }
+
+ void Box::event_on_mouse_button_up(int x, int y, int button) {
+ mouse_button_up(x, y, button);
+
+ for (auto& child : children)
+ if (x >= child->x && x < child->x + child->width && y >= child->y && y < child->y + child->height)
+ child->event_on_mouse_button_up(x - child->x, y - child->y, button);
+ }
+
+ void Box::event_on_mouse_button_down(int x, int y, int button) {
+ mouse_button_down(x, y, button);
+
+ for (auto& child : children)
+ if (x >= child->x && x < child->x + child->width && y >= child->y && y < child->y + child->height)
+ child->event_on_mouse_button_down(x - child->x, y - child->y, button);
+ }
+
+ void Box::event_on_mouse_motion(int x, int y) {
+ mouse_motion(x, y);
+
+ for (auto& child : children)
+ if (x >= child->x && x < child->x + child->width && y >= child->y && y < child->y + child->height) {
+ child->event_on_mouse_motion(x - child->x, y - child->y);
+
+ if (!child->hovered)
+ child->event_on_mouse_enter(x - child->x, y - child->y);
+ } else if (child->hovered)
+ child->event_on_mouse_exit(x - child->x, y - child->y);
+ }
+
+ void Box::event_on_mouse_enter(int x, int y) {
+ hovered = true;
+ mouse_enter(x, y);
+ }
+
+ void Box::event_on_mouse_exit(int x, int y) {
+ hovered = false;
+ mouse_exit(x, y);
+
+ for (auto &child : children)
+ if (child->hovered)
+ child->event_on_mouse_exit(x - child->x, y - child->y);
+ }
+
+ void Box::event_on_resize() {
+ arrange_pass0();
+ //arrange_pass1();
+ //arrange_pass2();
+ // Broadcast resize event on socket
+ //resize();
+
+ // Make sure everything respects padding
+ /*int padding_width = width - padding_left - padding_right;
+ int padding_height = height - padding_top - padding_bottom;
+
+ for (auto &child : children) {
+ int cx = child->x;
+ int cy = child->y;
+ int cw = child->width;
+ int ch = child->height;
+
+ if (cx < padding_left)
+ cx = padding_left;
+ if (cy < padding_top)
+ cy = padding_top;
+ if (cx - padding_left + cw >= padding_width)
+ cw = padding_width + padding_left - cx;
+ if (cy - padding_top + ch >= padding_height)
+ ch = padding_height + padding_top - cy;
+
+ if (child->x != cx || child->y != cy || child->width != cw || child->height != ch) {
+ damage();
+
+ child->x = cx;
+ child->y = cy;
+ child->width = cw;
+ child->height = ch;
+ child->event_on_resize();
+ }
+ }*/
+ }
+
+ void Box::arrange_pass0() {
+ virtual_width = 0;
+ virtual_height = 0;
+
+ int parent_padding_width = width;
+ int parent_padding_height = height;
+ int parent_padding_left = 0;
+ int parent_padding_top = 0;
+ if (parent) {
+ parent_padding_width = std::max(0, parent->virtual_width - parent->padding_left - parent->padding_right);
+ parent_padding_height = std::max(0, parent->virtual_height - parent->padding_top - parent->padding_bottom);
+ parent_padding_left = parent->padding_left;
+ parent_padding_top = parent->padding_top;
+ }
+
+ if (width_attr.type == PX)
+ virtual_width = width_attr.px;
+ if (height_attr.type == PX)
+ virtual_height = height_attr.px;
+
+ if (parent && width_attr.type == PR)
+ virtual_width = (int)((float)parent_padding_width * width_attr.pr);
+ if (parent && height_attr.type == PR)
+ virtual_height = (int)((float)parent_padding_height * height_attr.pr);
+
+ // FIXME: can do weird stuff with multiplication for small values
+ for (auto &child : children)
+ child->arrange_pass0();
+
+ arrange_pass1();
+
+ int min_x = -1, min_y = -1;
+ int max_x = -1, max_y = -1;
+ for (auto &child : children) {
+ if (min_x == -1) {
+ min_x = child->x;
+ min_y = child->y;
+ }
+
+ min_x = std::min(min_x, child->x);
+ min_y = std::min(min_y, child->y);
+ max_x = std::max(max_x, child->x + child->virtual_width);
+ max_y = std::max(max_y, child->y + child->virtual_height);
+ }
+
+ if (virtual_width == 0)
+ virtual_width = max_x - min_x + padding_left + padding_right;
+ if (virtual_height == 0)
+ virtual_height = max_y - min_y + padding_top + padding_bottom;
+
+ bool parent_known_width = true;
+ bool parent_known_height = true;
+ if (parent) {
+ parent_known_width = parent->virtual_width != 0;
+ parent_known_height = parent->virtual_height != 0;
+ }
+
+ int cx = x;
+ int cy = y;
+ int cw = virtual_width;
+ int ch = virtual_height;
+
+ if (cx < parent_padding_left)
+ cx = parent_padding_left;
+ if (cy < parent_padding_top)
+ cy = parent_padding_top;
+ if (parent_known_width && cx - parent_padding_top + cw >= parent_padding_width)
+ cw = parent_padding_width + parent_padding_top - cx;
+ if (parent_known_height && cy - parent_padding_top + ch >= parent_padding_height)
+ ch = parent_padding_height + parent_padding_top - cy;
+
+ virtual_width = cw;
+ virtual_height = ch;
+
+ for (auto &child : children)
+ child->arrange_pass0();
+
+ arrange_pass1();
+ }
+
+ void Box::arrange_pass1() {
+ //std::cout << "noice" << std::endl;
+ resize();
+ }
+
+ void Box::arrange_pass2() {
+ int parent_padding_width = virtual_width;
+ int parent_padding_height = virtual_height;
+ int parent_padding_left = 0;
+ int parent_padding_top = 0;
+ if (parent) {
+ parent_padding_width = std::max(0, parent->virtual_width - parent->padding_left - parent->padding_right);
+ parent_padding_height = std::max(0, parent->virtual_height - parent->padding_top - parent->padding_bottom);
+ parent_padding_left = parent->padding_left;
+ parent_padding_top = parent->padding_top;
+ }
+
+ // FIXME
+ int min_x = -1, min_y = -1;
+ int max_x = -1, max_y = -1;
+ for (auto &child : children) {
+ if (min_x == -1) {
+ min_x = child->x;
+ min_y = child->y;
+ }
+
+ min_x = std::min(min_x, child->x);
+ min_y = std::min(min_y, child->y);
+ max_x = std::max(max_x, child->x + child->virtual_width);
+ max_y = std::max(max_y, child->y + child->virtual_height);
+ }
+
+ if (virtual_width == 0)
+ virtual_width = max_x - min_x + padding_left + padding_right;
+ if (virtual_height == 0)
+ virtual_height = max_y - min_y + padding_top + padding_bottom;
+
+ int cx = x;
+ int cy = y;
+ int cw = virtual_width;
+ int ch = virtual_height;
+
+ if (cx < parent_padding_left)
+ cx = parent_padding_left;
+ if (cy < parent_padding_top)
+ cy = parent_padding_top;
+ if (cx - parent_padding_top + cw >= parent_padding_width)
+ cw = parent_padding_width + parent_padding_top - cx;
+ if (cy - parent_padding_top + ch >= parent_padding_height)
+ ch = parent_padding_height + parent_padding_top - cy;
+
+ std::cout << "parent be like: " << parent_padding_width << "x" << parent_padding_height << std::endl;
+ if (children.empty()) {
+ std::cout << "hi, i am innermost component, my dims are: " << virtual_width << "x" << virtual_height << std::endl;
+ }
+
+ if (virtual_width != cw || virtual_height != ch) {
+ virtual_width = cw;
+ virtual_height = ch;
+ width = cw;
+ height = ch;
+
+ // Should i do this? perhaps in layouting or something
+
+ arrange_pass1();
+
+ // ?
+ damage();
+ }
+
+ for (auto &child : children)
+ child->arrange_pass2();
+ }
+}
diff --git a/chisel/box.hpp b/chisel/box.hpp
new file mode 100644
index 0000000..b9787b8
--- /dev/null
+++ b/chisel/box.hpp
@@ -0,0 +1,75 @@
+#pragma once
+
+#include <socket/socket.hpp>
+#include "attribute.hpp"
+
+#include <string>
+#include <vector>
+
+namespace chisel {
+ class Window;
+
+ class Box {
+ public:
+ Box();
+ virtual ~Box();
+
+ std::string id;
+
+ socket::Socket<void(int, int, int)> mouse_button_down;
+ socket::Socket<void(int, int, int)> mouse_button_up;
+ socket::Socket<void(int, int)> mouse_motion;
+ socket::Socket<void(int, int)> mouse_enter;
+ socket::Socket<void(int, int)> mouse_exit;
+ socket::Socket<void(void)> resize;
+
+ Window *window;
+ Box *parent = nullptr;
+ std::vector<Box*> children;
+
+ /* Real position and dimension after layout-ing */
+ int x = 0;
+ int y = 0;
+ int width = 0;
+ int height = 0;
+
+ /* Virtual dimensions used for bounding box calculation */
+ int virtual_width = 0;
+ int virtual_height = 0;
+
+ /* Width and height attributes as configured by user */
+ Attribute width_attr = { AUTO };
+ Attribute height_attr = { AUTO };
+
+ /* Padding in px (for now) */
+ int padding_left = 0;
+ int padding_right = 0;
+ int padding_top = 0;
+ int padding_bottom = 0;
+
+ bool hovered = false;
+
+ void damage();
+ virtual void draw();
+
+ virtual void fill_rect(int x0, int y0, int w, int h, int r, int g, int b);
+
+ void add_child(Box *child);
+
+ void event_on_mouse_button_up(int x, int y, int button);
+ void event_on_mouse_button_down(int x, int y, int button);
+ void event_on_mouse_motion(int x, int y);
+ void event_on_mouse_enter(int x, int y);
+ void event_on_mouse_exit(int x, int y);
+ void event_on_resize();
+
+ // Self determination of current and children dims
+ void arrange_pass0();
+
+ // Layoutting of current
+ void arrange_pass1();
+
+ // Constraining the children
+ void arrange_pass2();
+ };
+}
diff --git a/chisel/button.cpp b/chisel/button.cpp
new file mode 100644
index 0000000..fa90713
--- /dev/null
+++ b/chisel/button.cpp
@@ -0,0 +1,42 @@
+#include "button.hpp"
+
+namespace chisel {
+ Button::Button() {
+ mouse_enter.connect(this, &Button::hover);
+ mouse_exit.connect(this, &Button::hover);
+ mouse_button_down.connect(this, &Button::press);
+ mouse_button_up.connect(this, &Button::depress);
+ pressed = false;
+ r = 255;
+ g = 255;
+ b = 255;
+ }
+ Button::~Button() {}
+
+ void Button::hover(int x, int y) {
+ if (!hovered)
+ pressed = false;
+ damage();
+ }
+
+ void Button::press(int x, int y, int b) {
+ pressed = true;
+ damage();
+ }
+
+ void Button::depress(int x, int y, int b) {
+ pressed = false;
+ damage();
+ }
+
+ void Button::draw() {
+ if (pressed)
+ r = g = b = 0;
+ else if (hovered)
+ r = g = b = 255/2;
+ else
+ r = g = b = 255;
+
+ fill_rect(0, 0, width, height, r, g, b);
+ }
+}
diff --git a/chisel/button.hpp b/chisel/button.hpp
new file mode 100644
index 0000000..477d013
--- /dev/null
+++ b/chisel/button.hpp
@@ -0,0 +1,19 @@
+#pragma once
+
+#include "box.hpp"
+
+namespace chisel {
+ class Button : public Box {
+ public:
+ Button();
+ virtual ~Button();
+
+ int r, g, b;
+ bool pressed;
+
+ void hover(int x, int y);
+ void press(int x, int y, int b);
+ void depress(int x, int y, int b);
+ void draw() override;
+ };
+}
diff --git a/chisel/kiosk.cpp b/chisel/kiosk.cpp
new file mode 100644
index 0000000..0323822
--- /dev/null
+++ b/chisel/kiosk.cpp
@@ -0,0 +1,30 @@
+#include "kiosk.hpp"
+#include <iostream>
+
+namespace chisel {
+ Kiosk::Kiosk() {
+ resize.connect(this, &Kiosk::layout);
+ }
+
+ Kiosk::~Kiosk() {}
+
+ void Kiosk::layout() {
+ for (auto &child : children) {
+ int cx = padding_left;
+ int cy = padding_top;
+ int cw = child->virtual_width;
+ int ch = child->virtual_height;
+
+ if (child->x != cx || child->y != cy || child->width != cw || child->height != ch) {
+ child->x = cx;
+ child->y = cy;
+ child->width = cw;
+ child->height = ch;
+
+ // FIXME: is this needed ?
+ child->arrange_pass0();
+ damage();
+ }
+ }
+ }
+}
diff --git a/chisel/kiosk.hpp b/chisel/kiosk.hpp
new file mode 100644
index 0000000..6d6f944
--- /dev/null
+++ b/chisel/kiosk.hpp
@@ -0,0 +1,13 @@
+#pragma once
+
+#include "box.hpp"
+
+namespace chisel {
+ class Kiosk : public Box {
+ public:
+ Kiosk();
+ virtual ~Kiosk();
+
+ void layout();
+ };
+}
diff --git a/chisel/sdl_window.cpp b/chisel/sdl_window.cpp
new file mode 100644
index 0000000..a969767
--- /dev/null
+++ b/chisel/sdl_window.cpp
@@ -0,0 +1,141 @@
+#include "sdl_window.hpp"
+
+#include <iostream>
+#include <functional>
+#include <sstream>
+
+namespace chisel {
+ int SDLWindow::count = 0;
+
+ SDLWindow::SDLWindow(std::string title) {
+ title_ = title;
+ window = this;
+ width_attr.type = PX;
+ height_attr.type = PX;
+ padding_left = 0;
+ padding_right = 0;
+ padding_top = 0;
+ padding_bottom = 0;
+
+ if (SDLWindow::count++ == 0) {
+ if (SDL_Init(SDL_INIT_VIDEO) != 0)
+ std::cerr << "could not initiate SDL" << std::endl;
+ }
+
+ sdl_window = SDL_CreateWindow(title_.c_str(),
+ SDL_WINDOWPOS_UNDEFINED,
+ SDL_WINDOWPOS_UNDEFINED,
+ 640, 480,
+ SDL_WINDOW_RESIZABLE
+ );
+
+ if (!sdl_window)
+ std::cerr << "could not initiate SDL" << std::endl;
+
+ sdl_renderer = SDL_CreateRenderer(
+ sdl_window,
+ -1,
+ SDL_RENDERER_SOFTWARE
+ );
+
+ SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
+ SDL_RenderClear(sdl_renderer);
+ SDL_RenderPresent(sdl_renderer);
+ }
+
+ SDLWindow::~SDLWindow() {
+ SDL_DestroyWindow(sdl_window);
+
+ if (--SDLWindow::count == 0)
+ SDL_Quit();
+ }
+
+ void SDLWindow::on_sdl_event(SDL_Event *event) {
+ // If the event does not relate to window, exit
+ if (SDL_GetWindowFromID(event->window.windowID) != sdl_window)
+ return;
+
+ // If quitting event, stop running
+ if (event->type == SDL_QUIT)
+ running = false;
+
+ // If resize event, handle it
+ if (event->type == SDL_WINDOWEVENT && event->window.event == SDL_WINDOWEVENT_RESIZED) {
+ // Update the width and height
+ SDL_GetWindowSize(sdl_window, &width, &height);
+ width_attr.px = width;
+ height_attr.px = height;
+
+ // FIXME: temporary way to check if resize handling works
+ std::stringstream s;
+ s << width << "x" << height;
+ title(s.str());
+
+ damage();
+ event_on_resize();
+ }
+
+ // If mouse button up event
+ if (event->type == SDL_MOUSEBUTTONUP)
+ event_on_mouse_button_up(event->button.x, event->button.y, event->button.button);
+
+ // If mouse button down event
+ if (event->type == SDL_MOUSEBUTTONDOWN)
+ event_on_mouse_button_down(event->button.x, event->button.y, event->button.button);
+
+ // If mouse motion
+ if (event->type == SDL_MOUSEMOTION)
+ event_on_mouse_motion(event->motion.x, event->motion.y);
+ }
+
+ void SDLWindow::run() {
+ running = true;
+
+ SDL_GetWindowSize(sdl_window, &width, &height);
+ event_on_resize();
+
+ while (running) {
+ if (damaged_set.empty()) {
+ SDL_Event event;
+ SDL_WaitEvent(&event);
+ on_sdl_event(&event);
+ }
+
+ // Redraw damaged stuff
+ for (auto &damage : damaged_deque)
+ damage->draw();
+
+ // Present the buffer
+ if (damaged_set.size() > 0)
+ SDL_RenderPresent(sdl_renderer);
+
+ // Empty the damaged boxes list
+ damaged_set.clear();
+ damaged_deque.clear();
+ }
+ }
+
+ void SDLWindow::title(std::string title) {
+ title_ = title;
+ SDL_SetWindowTitle(sdl_window, title_.c_str());
+ }
+
+ std::string& SDLWindow::title() {
+ return title_;
+ }
+
+ void SDLWindow::draw() {
+ SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 0xFF);
+ SDL_RenderClear(sdl_renderer);
+ }
+
+ void SDLWindow::fill_rect(int x0, int y0, int w, int h, int r, int g, int b) {
+ SDL_Rect rect;
+ rect.x = x0;
+ rect.y = y0;
+ rect.w = w;
+ rect.h = h;
+ SDL_SetRenderDrawColor(sdl_renderer, r, g, b, 0xFF);
+ SDL_RenderFillRect(sdl_renderer, &rect);
+ }
+}
diff --git a/chisel/sdl_window.hpp b/chisel/sdl_window.hpp
new file mode 100644
index 0000000..d809f54
--- /dev/null
+++ b/chisel/sdl_window.hpp
@@ -0,0 +1,32 @@
+#pragma once
+
+#include <SDL2/SDL.h>
+
+#include "window.hpp"
+#include <string>
+
+namespace chisel {
+ class SDLWindow : public Window {
+ SDL_Window *sdl_window;
+ SDL_Renderer *sdl_renderer;
+
+ static int count;
+
+ bool running;
+
+ void on_sdl_event(SDL_Event* event);
+ public:
+ SDLWindow(std::string title);
+ virtual ~SDLWindow();
+
+ void run();
+
+ // Implements Window
+ void title(std::string title) override;
+ std::string& title() override;
+
+ // Implements Box
+ void draw() override;
+ void fill_rect(int x0, int y0, int w, int h, int r, int g, int b) override;
+ };
+}
diff --git a/chisel/vertical_box.cpp b/chisel/vertical_box.cpp
new file mode 100644
index 0000000..0dd474a
--- /dev/null
+++ b/chisel/vertical_box.cpp
@@ -0,0 +1,27 @@
+#include "vertical_box.hpp"
+
+
+namespace chisel {
+ VerticalBox::VerticalBox() {
+ resize.connect(this, &VerticalBox::layout);
+ }
+
+ VerticalBox::~VerticalBox() {}
+
+ void VerticalBox::layout() {
+ int pos = padding_top;
+
+ for (auto &child : children) {
+ int cx = padding_left;
+ int cy = pos;
+
+ child->x = cx;
+ child->y = cy;
+ child->width = child->virtual_width;
+ child->height = child->virtual_height;
+ damage();
+
+ pos += child->virtual_height + margin;
+ }
+ }
+}
diff --git a/chisel/vertical_box.hpp b/chisel/vertical_box.hpp
new file mode 100644
index 0000000..8f20da3
--- /dev/null
+++ b/chisel/vertical_box.hpp
@@ -0,0 +1,15 @@
+#pragma once
+
+#include "box.hpp"
+
+namespace chisel {
+ class VerticalBox : public Box {
+ public:
+ VerticalBox();
+ virtual ~VerticalBox();
+
+ int margin;
+
+ void layout();
+ };
+}
diff --git a/chisel/window.hpp b/chisel/window.hpp
new file mode 100644
index 0000000..058f8a4
--- /dev/null
+++ b/chisel/window.hpp
@@ -0,0 +1,17 @@
+#pragma once
+
+#include <string>
+#include <set>
+#include <deque>
+#include "kiosk.hpp"
+
+namespace chisel {
+ struct Window : public Kiosk {
+ std::set<Box*> damaged_set;
+ std::deque<Box*> damaged_deque;
+ std::string title_;
+
+ virtual void title(std::string name) = 0;
+ virtual std::string& title() = 0;
+ };
+}
diff --git a/main.cpp b/main.cpp
new file mode 100644
index 0000000..c1c14ca
--- /dev/null
+++ b/main.cpp
@@ -0,0 +1,112 @@
+#include <iostream>
+
+#include <string>
+#include <chisel/box.hpp>
+#include <chisel/sdl_window.hpp>
+#include <chisel/vertical_box.hpp>
+#include <chisel/button.hpp>
+#include <chisel/attribute.hpp>
+
+#include <socket/socket.hpp>
+
+void mouse_button_up(int x, int y, int button) {
+ std::cout << "mouse button up " << x << " " << y << " " << button << std::endl;
+}
+
+void mouse_button_down(int x, int y, int button) {
+ std::cout << "mouse button down " << x << " " << y << " " << button << std::endl;
+}
+
+void mouse_enter(int x, int y) {
+ std::cout << "mouse entered from " << x << " " << y << std::endl;
+}
+
+void mouse_exit(int x, int y) {
+ std::cout << "mouse exitted on " << x << " " << y << std::endl;
+}
+
+int main() {
+ std::cout << "chisel toolkit" << std::endl;
+ chisel::SDLWindow window("hai");
+ chisel::Kiosk *a = new chisel::Kiosk();
+ a->padding_top = 10;
+ a->padding_bottom = 10;
+ a->padding_left = 10;
+ a->padding_right = 10;
+ a->width_attr = {
+ .type = chisel::PR,
+ .pr = 1.0
+ };
+ a->height_attr = {
+ .type = chisel::PR,
+ .pr = 1.0
+ };
+ a->mouse_button_up.connect(mouse_button_up);
+ a->mouse_button_down.connect(mouse_button_down);
+ a->mouse_enter.connect(mouse_enter);
+ a->mouse_exit.connect(mouse_exit);
+ window.add_child(a);
+ chisel::VerticalBox *b = new chisel::VerticalBox();
+ b->id = "B";
+ b->margin = 8;
+ b->padding_top = 15;
+ b->padding_bottom = 15;
+ b->padding_left = 15;
+ b->padding_right = 15;
+ b->width_attr = {
+ .type = chisel::PR,
+ .pr = 1.0
+ };
+ b->height_attr = {
+ .type = chisel::PR,
+ .pr = 1.0
+ };
+ b->mouse_button_up.connect(mouse_button_up);
+ b->mouse_button_down.connect(mouse_button_down);
+ b->mouse_enter.connect(mouse_enter);
+ b->mouse_exit.connect(mouse_exit);
+ a->add_child(b);
+
+ for (int i = 0; i < 5; i++) {
+ chisel::Button *c = new chisel::Button();
+ c->width_attr = {
+ .type = chisel::PR,
+ .pr = 1.0
+ };
+ c->height_attr = {
+ .type = chisel::PR,
+ .pr = 0.08
+ };
+ c->mouse_button_up.connect(mouse_button_up);
+ c->mouse_button_down.connect(mouse_button_down);
+ c->mouse_enter.connect(mouse_enter);
+ c->mouse_exit.connect(mouse_exit);
+ b->add_child(c);
+ }
+
+/*
+ chisel::Button *c = new chisel::Button();
+ c->x = 0;
+ c->y = 0;
+ c->width_attr = {
+ .type = chisel::PX,
+ .px = 30
+ };
+ c->height_attr = {
+ .type = chisel::PX,
+ .px = 25
+ };
+ c->parent = b;
+ c->window = &window;
+ c->mouse_button_up.connect(mouse_button_up);
+ c->mouse_button_down.connect(mouse_button_down);
+ c->mouse_enter.connect(mouse_enter);
+ c->mouse_exit.connect(mouse_exit);
+ b->add_child(c);*/
+
+ window.run();
+
+ chisel::AttributeType attr = chisel::AttributeType::AUTO;
+
+ return 0;
+}
diff --git a/socket/readme b/socket/readme
new file mode 100644
index 0000000..0046460
--- /dev/null
+++ b/socket/readme
@@ -0,0 +1,6 @@
+The [[socket]] module introduces a structure
+called a socket, whose purpose is to be able
+to dispatch a function call to an arbitrary
+number of handlers. Those handlers can be
+connected and disconnected from the socket
+dynamically.
diff --git a/socket/socket.hpp b/socket/socket.hpp
new file mode 100644
index 0000000..dd9b2a3
--- /dev/null
+++ b/socket/socket.hpp
@@ -0,0 +1,68 @@
+#pragma once
+
+#include <functional>
+#include <map>
+
+namespace socket {
+ /* Allows casting R(Args...) */
+ template <typename T>
+ class Socket;
+
+ /* A class that represents a socket that can
+ be used to dispatch a function call to different
+ handlers. Handlers can be connected and disconnected
+ from the socket at runtime. */
+ template <typename R, typename... Args>
+ class Socket<R(Args...)> {
+ /* The type of handler function or methods */
+ using HandlerType = std::function<R(Args...)>;
+
+
+ /* The next handler descriptor to be assigned
+ the descriptor can be used to disconnect
+ a descriptor */
+ int descriptor;
+
+ /* Map between descriptors and handlers */
+ std::map<int, HandlerType> handlers;
+ public:
+ /* Connect a handler to the socket
+ handler: the handler to connect to
+ Returns the handler descriptor */
+ int connect(HandlerType handler) {
+ // Get the handler descriptor of next handler
+ int hd = descriptor++;
+
+ // Store the handler
+ handlers[hd] = handler;
+
+ return hd;
+ }
+
+ /* Connect a handler to the socket
+ and bind the call to an object, this
+ can be useful if the handler is a method */
+ template <typename T, typename V>
+ int connect(T *object, R (V::*handler)(Args...)) {
+ auto f = [=](Args... args) {
+ return std::invoke(handler, object, args...);
+ };
+
+ return connect(f);
+ }
+
+ /* Disconnect a handler from the socket
+ hd: the handle descriptor of the handler
+ to disconnect */
+ void disconnect(int hd) {
+ // Remove the handler from the map
+ handlers.erase(hd);
+ }
+
+ /* Dispatch a call to all handlers */
+ void operator()(Args... args) {
+ for (auto &handler : handlers)
+ handler.second(args...);
+ }
+ };
+}