diff --git a/.cache/clangd/index/keymap.h.ABBD7372264C183C.idx b/.cache/clangd/index/keymap.h.ABBD7372264C183C.idx
new file mode 100644
index 0000000..ea89c80
Binary files /dev/null and b/.cache/clangd/index/keymap.h.ABBD7372264C183C.idx differ
diff --git a/.cache/clangd/index/main.c.8CF723096C569150.idx b/.cache/clangd/index/main.c.8CF723096C569150.idx
new file mode 100644
index 0000000..3193f7c
Binary files /dev/null and b/.cache/clangd/index/main.c.8CF723096C569150.idx differ
diff --git a/.gitignore b/.gitignore
index f603704..ddb306f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
-osk
+*osk
*.o
*.env
+build/*
diff --git a/Makefile b/Makefile
deleted file mode 100644
index 8323e4f..0000000
--- a/Makefile
+++ /dev/null
@@ -1,36 +0,0 @@
-# Variables for compiler and flags
-CC = gcc
-PKG_CONFIG = pkg-config
-PACKAGES = gtk4 libevdev gtk4-layer-shell-0
-
-# Fetch flags and libs using pkg-config
-CFLAGS = $(shell $(PKG_CONFIG) --cflags $(PACKAGES))
-LIBS = $(shell $(PKG_CONFIG) --libs $(PACKAGES))
-
-# Target executable name
-TARGET = osk
-
-# Source files
-SRC = main.c resources.c
-OBJ = $(SRC:.c=.o)
-
-# Default rule: build the target
-all: $(TARGET)
-
-# Rule to link the final executable
-$(TARGET): resources.c $(OBJ)
- $(CC) -o $(TARGET) $(OBJ) $(LIBS)
-
-# Rule to compile .c files to .o files
-%.o: %.c
- $(CC) $(CFLAGS) -c $< -o $@
-
-# Rule to generate resources.c from XML
-resources.c: resources.xml
- glib-compile-resources resources.xml --target=resources.c --generate-source
-
-# Clean up generated files
-clean:
- rm -f $(TARGET) $(OBJ) resources.c
-
-.PHONY: all clean
diff --git a/README.md b/README.md
index 8650548..4ffb073 100644
--- a/README.md
+++ b/README.md
@@ -32,17 +32,17 @@ Run the following commands:
```
git clone https://github.com/weave-space/oskeyboard
cd oskeyboard
-make
-./osk
+meson compile -C build
+./build/osk
```
-## Customization
-There are two ways to customize `oskeyboard`: editing `keymap.h` and `styles.css`. Currently there is a limitation of custom layouts, as the number of rows and the number of keys per row are hardcoded in `main.c`. That will be remedied in the near future.
+## Customization
+There is now only one way to customize `oskeyboard`: editing `keymap.h`. Currently there is a limitation of custom layouts, as the number of rows and the number of keys per row are hardcoded in `main.c`. That will be remedied in the near future.
-Any changes to `keymap.h` or `styles.css` require you to recompile, simply run `make` to do so.
+Any changes to `keymap.h` require you to recompile, simply run `meson compile -C build` to do so.
## TODO
-- [ ] Responsive design sizes
+- [x] Responsive design sizes
- [ ] CLI flags
- [ ] Man page
- [ ] Documentation
diff --git a/keyboard.ui b/keyboard.ui
deleted file mode 100644
index 55c8b36..0000000
--- a/keyboard.ui
+++ /dev/null
@@ -1,84 +0,0 @@
-
-
-
-
diff --git a/keymap.h b/keymap.h
deleted file mode 100644
index 459133b..0000000
--- a/keymap.h
+++ /dev/null
@@ -1,40 +0,0 @@
-#ifndef KEYMAP_H
-#define KEYMAP_H
-
-#include
-
-typedef struct {
-const char *label;
-const char *shift_label;
-int key_code;
-} KeyMapping;
-
-static const KeyMapping key_map[] = {
- /* Row 0 */
- {"`", "~", KEY_GRAVE}, {"1", "!", KEY_1}, {"2", "#", KEY_2}, {"3", "#", KEY_3}, {"4", "$", KEY_4},
- {"5", "%", KEY_5}, {"6", "^", KEY_6}, {"7", "&", KEY_7}, {"8", "*", KEY_8}, {"9", "(", KEY_9},
- {"0", ")", KEY_0}, {"-", "_", KEY_MINUS}, {"=", "+", KEY_EQUAL}, {"Backspace", "Delete", KEY_BACKSPACE},
-
- /* Row 1 */
- {"Tab", "Tab", KEY_TAB}, {"q", "Q", KEY_Q}, {"w", "W", KEY_W}, {"e", "E", KEY_E}, {"r", "R", KEY_R},
- {"t", "T", KEY_T}, {"y", "Y", KEY_Y}, {"u", "U", KEY_U}, {"i", "I", KEY_I}, {"o", "O", KEY_O},
- {"p", "P", KEY_P}, {"[", "{", KEY_LEFTBRACE}, {"]", "}", KEY_RIGHTBRACE}, {"\\", "|", KEY_BACKSLASH},
-
- /* Row 2 */
- {"Escape", "Escape", KEY_ESC}, {"a", "A", KEY_A}, {"s", "S", KEY_S}, {"d", "D", KEY_D},
- {"f", "F", KEY_F}, {"g", "G", KEY_G}, {"h", "H", KEY_H}, {"j", "J", KEY_J}, {"k", "K", KEY_K},
- {"l", "L", KEY_L}, {";", ":", KEY_SEMICOLON}, {"'", "\"", KEY_APOSTROPHE}, {"Enter", "Enter", KEY_ENTER},
-
- /* Row 3 */
- {"Shift", "Shift", KEY_LEFTSHIFT}, {"z", "Z", KEY_Z}, {"x", "X", KEY_X}, {"c", "C", KEY_C},
- {"v", "V", KEY_V}, {"b", "B", KEY_B}, {"n", "N", KEY_N}, {"m", "M", KEY_M}, {",", "<", KEY_COMMA},
- {".", ">", KEY_DOT}, {"/", "?", KEY_SLASH}, {"Shift", "Shift", KEY_RIGHTSHIFT},
-
- /* Row 4 */
- {"Ctrl", "Ctrl", KEY_LEFTCTRL}, {"Super", "Super", KEY_LEFTMETA}, {"Alt", "Alt", KEY_LEFTALT},
- {"Space", "Space", KEY_SPACE}, {"Alt", "Alt", KEY_RIGHTALT}, {"Ctrl", "Ctrl", KEY_RIGHTCTRL}
-};
-
-#define KEY_MAP_SIZE (sizeof(key_map) / sizeof(KeyMapping))
-
-#endif
diff --git a/main.c b/main.c
deleted file mode 100644
index 293b91b..0000000
--- a/main.c
+++ /dev/null
@@ -1,184 +0,0 @@
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include "keymap.h"
-
-GtkWidget *window = NULL;
-struct libevdev *dev;
-struct libevdev_uinput *uidev;
-bool shift_active = false;
-GList *key_buttons = NULL;
-
-static void apply_css() {
- GtkCssProvider *provider = gtk_css_provider_new();
- gtk_css_provider_load_from_resource(provider, "/org/spaces/oskeyboard/style.css");
- gtk_style_context_add_provider_for_display(
- gdk_display_get_default(),
- GTK_STYLE_PROVIDER(provider),
- GTK_STYLE_PROVIDER_PRIORITY_APPLICATION
- );
- g_object_unref(provider);
-}
-
-void press_key(int key_code) {
- if (!uidev) return;
- libevdev_uinput_write_event(uidev, EV_KEY, key_code, 1);
- libevdev_uinput_write_event(uidev, EV_SYN, SYN_REPORT, 0);
- libevdev_uinput_write_event(uidev, EV_KEY, key_code, 0);
- libevdev_uinput_write_event(uidev, EV_SYN, SYN_REPORT, 0);
-}
-
-static void update_labels() {
- for (GList *l = key_buttons; l != NULL; l = l->next) {
- GtkButton *btn = GTK_BUTTON(l->data);
- const char *current = gtk_button_get_label(btn);
-
- for (int i = 0; i < KEY_MAP_SIZE; i++) {
- if (shift_active && g_strcmp0(current, key_map[i].label) == 0) {
- gtk_button_set_label(btn, key_map[i].shift_label);
- break;
- } else if (!shift_active && g_strcmp0(current, key_map[i].shift_label) == 0) {
- gtk_button_set_label(btn, key_map[i].label);
- break;
- }
- }
- }
-}
-
-static void on_key_clicked(GtkButton *button, gpointer user_data) {
- const char *label = gtk_button_get_label(button);
-
- if (g_strcmp0(label, "Shift") == 0) {
- shift_active = !shift_active;
- update_labels();
- return;
- }
-
- for (int i = 0; i < KEY_MAP_SIZE; i++) {
- if (g_strcmp0(label, key_map[i].label) == 0 || g_strcmp0(label, key_map[i].shift_label) == 0) {
- if (shift_active) {
- libevdev_uinput_write_event(uidev, EV_KEY, KEY_LEFTSHIFT, 1);
- libevdev_uinput_write_event(uidev, EV_SYN, SYN_REPORT, 0);
- }
-
- press_key(key_map[i].key_code);
-
- if (shift_active) {
- libevdev_uinput_write_event(uidev, EV_KEY, KEY_LEFTSHIFT, 0);
- libevdev_uinput_write_event(uidev, EV_SYN, SYN_REPORT, 0);
- shift_active = false;
- update_labels();
- }
- return;
- }
- }
-}
-
-void add_key(GtkGrid *grid, const char *label, int col, int width) {
- GtkWidget *button = gtk_button_new_with_label(label);
- gtk_widget_set_hexpand(button, TRUE);
- gtk_widget_set_halign(button, GTK_ALIGN_FILL);
- gtk_widget_set_size_request(button, 94 * width, 94);
-
- g_signal_connect(button, "clicked", G_CALLBACK(on_key_clicked), NULL);
- gtk_grid_attach(grid, button, col, 0, width, 1);
- key_buttons = g_list_append(key_buttons, button);
-}
-
-void init_evdev_keyboard() {
- dev = libevdev_new();
- libevdev_set_name(dev, "OSKeyboard");
- libevdev_enable_event_type(dev, EV_KEY);
-
- for (int i = 0; i < KEY_MAP_SIZE; i++) {
- libevdev_enable_event_code(dev, EV_KEY, key_map[i].key_code, NULL);
- }
-
- int fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
- if (fd < 0) {
- fprintf(stderr, "Failed to open /dev/uinput: %s\n", strerror(errno));
- return;
- }
-
- int err = libevdev_uinput_create_from_device(dev, fd, &uidev);
- if (err != 0) {
- fprintf(stderr, "Failed to create uinput device: %s\n", strerror(-err));
- close(fd);
- return;
- }
-}
-
-static void activate(GtkApplication *app, gpointer user_data) {
- if (window) {
- if (gtk_widget_get_visible(window)) {
- gtk_widget_set_visible(window, FALSE);
- } else {
- gtk_window_present(GTK_WINDOW(window));
- }
- return;
- }
-
- apply_css();
-
- GtkBuilder *builder = gtk_builder_new_from_resource("/org/spaces/oskeyboard/keyboard.ui");
- window = GTK_WIDGET(gtk_builder_get_object(builder, "main_window"));
-
- gtk_layer_init_for_window(GTK_WINDOW(window));
- gtk_layer_set_layer(GTK_WINDOW(window), GTK_LAYER_SHELL_EDGE_BOTTOM);
- gtk_layer_auto_exclusive_zone_enable(GTK_WINDOW(window));
- gtk_layer_set_keyboard_mode(GTK_WINDOW(window), FALSE);
- gtk_layer_set_anchor(GTK_WINDOW(window), GTK_LAYER_SHELL_EDGE_BOTTOM, TRUE);
- gtk_layer_set_anchor(GTK_WINDOW(window), GTK_LAYER_SHELL_EDGE_LEFT, TRUE);
- gtk_layer_set_anchor(GTK_WINDOW(window), GTK_LAYER_SHELL_EDGE_RIGHT, TRUE);
-
- gtk_window_set_application(GTK_WINDOW(window), app);
-
- GtkGrid *rows[] = {
- GTK_GRID(gtk_builder_get_object(builder, "kbd_row_0")),
- GTK_GRID(gtk_builder_get_object(builder, "kbd_row_1")),
- GTK_GRID(gtk_builder_get_object(builder, "kbd_row_2")),
- GTK_GRID(gtk_builder_get_object(builder, "kbd_row_3")),
- GTK_GRID(gtk_builder_get_object(builder, "kbd_row_4"))
- };
-
- int current_key = 0;
- int row_counts[] = {14, 14, 13, 12, 6};
-
- for (int r = 0; r < 5; r++) {
- int current_col = 0;
- for (int c = 0; c < row_counts[r]; c++) {
- if (current_key >= KEY_MAP_SIZE) break;
-
- const char *label = key_map[current_key].label;
- int width = 1;
-
- if (g_strcmp0(label, "Space") == 0) width = 7;
- else if (g_strcmp0(label, "Backspace") == 0) width = 2;
- else if (g_strcmp0(label, "Enter") == 0) width = 3;
- else if (g_strcmp0(label, "Shift") == 0) width = 3;
- else if (g_strcmp0(label, "Alt") == 0) width = 2;
- else if (g_strcmp0(label, "Ctrl") == 0) width = 3;
- else if (g_strcmp0(label, "Caps Lock") == 0) width = 2;
-
- add_key(rows[r], label, current_col, width);
- current_col += width;
- current_key++;
- }
- }
-
- init_evdev_keyboard();
- gtk_window_present(GTK_WINDOW(window));
- g_object_unref(builder);
-}
-
-int main(int argc, char **argv) {
- GtkApplication *app = gtk_application_new("org.spaces.oskeyboard", G_APPLICATION_DEFAULT_FLAGS);
- g_signal_connect(app, "activate", G_CALLBACK(activate), NULL);
- int status = g_application_run(G_APPLICATION(app), argc, argv);
- g_object_unref(app);
- return status;
-}
diff --git a/meson.build b/meson.build
new file mode 100644
index 0000000..4ed821e
--- /dev/null
+++ b/meson.build
@@ -0,0 +1,12 @@
+project('oskeyboard', 'c',
+ version : '0.1',
+ default_options : ['warning_level=0'])
+
+# Import the GNOME module to handle GResources
+gnome = import('gnome')
+
+gtk_dep = dependency('gtk4')
+libevdev_dep = dependency('libevdev')
+layer_shell_dep = dependency('gtk4-layer-shell-0')
+
+subdir('src')
diff --git a/src/keyboard.ui b/src/keyboard.ui
new file mode 100644
index 0000000..2d58fd9
--- /dev/null
+++ b/src/keyboard.ui
@@ -0,0 +1,13 @@
+
+
+
+ OSKeyboard
+
+
+
+ FALSE
+ TRUE
+
+
+
+
diff --git a/src/keymap.h b/src/keymap.h
new file mode 100644
index 0000000..842e896
--- /dev/null
+++ b/src/keymap.h
@@ -0,0 +1,97 @@
+#ifndef KEYMAP_H
+#define KEYMAP_H
+
+#include
+
+typedef struct {
+ const char *label;
+ const char *shift_label;
+ int key_code;
+ float weight;
+} KeyDef;
+
+typedef struct {
+ KeyDef *keys;
+ int key_count;
+ float total_weight;
+} RowDef;
+
+static const RowDef keys[] = {
+ // Top Row (0)
+ {(KeyDef[]){{"`", "~", KEY_GRAVE, 1.0},
+ {"1", "!", KEY_1, 1.0},
+ {"2", "@", KEY_2, 1.0},
+ {"3", "#", KEY_3, 1.0},
+ {"4", "$", KEY_4, 1.0},
+ {"5", "%", KEY_5, 1.0},
+ {"6", "^", KEY_6, 1.0},
+ {"7", "&", KEY_7, 1.0},
+ {"8", "*", KEY_8, 1.0},
+ {"9", "(", KEY_9, 1.0},
+ {"0", ")", KEY_0, 1.0},
+ {"-", "_", KEY_MINUS, 1.0},
+ {"=", "+", KEY_EQUAL, 1.0},
+ {"Backspace", "Backspace", KEY_BACKSPACE, 1.5}},
+ 14, 14.5},
+
+ // Row 1
+ {(KeyDef[]){{"Tab", "Tab", KEY_TAB, 1.5},
+ {"q", "Q", KEY_Q, 1.0},
+ {"w", "W", KEY_W, 1.0},
+ {"e", "E", KEY_E, 1.0},
+ {"r", "R", KEY_R, 1.0},
+ {"t", "T", KEY_T, 1.0},
+ {"y", "Y", KEY_Y, 1.0},
+ {"u", "U", KEY_U, 1.0},
+ {"i", "I", KEY_I, 1.0},
+ {"o", "O", KEY_O, 1.0},
+ {"p", "P", KEY_P, 1.0},
+ {"[", "{", KEY_LEFTBRACE, 1.0},
+ {"]", "}", KEY_RIGHTBRACE, 1.0},
+ {"\\", "|", KEY_BACKSLASH, 1.0}},
+ 14, 14.5},
+
+ // Row 2
+ {(KeyDef[]){{"Escape", "Escape", KEY_ESC, 2.0},
+ {"a", "A", KEY_A, 1.0},
+ {"s", "S", KEY_S, 1.0},
+ {"d", "D", KEY_D, 1.0},
+ {"f", "F", KEY_F, 1.0},
+ {"g", "G", KEY_G, 1.0},
+ {"h", "H", KEY_H, 1.0},
+ {"j", "J", KEY_J, 1.0},
+ {"k", "K", KEY_K, 1.0},
+ {"l", "L", KEY_L, 1.0},
+ {";", ":", KEY_SEMICOLON, 1.0},
+ {"'", "\"", KEY_APOSTROPHE, 1.0},
+ {"Enter", "Enter", KEY_ENTER, 2.0}},
+ 13, 15.0},
+
+ // Row 3
+ {(KeyDef[]){{"Shift", "Shift", KEY_LEFTSHIFT, 2.5},
+ {"z", "Z", KEY_Z, 1.0},
+ {"x", "X", KEY_X, 1.0},
+ {"c", "C", KEY_C, 1.0},
+ {"v", "V", KEY_V, 1.0},
+ {"b", "B", KEY_B, 1.0},
+ {"n", "N", KEY_N, 1.0},
+ {"m", "M", KEY_M, 1.0},
+ {",", "<", KEY_COMMA, 1.0},
+ {".", ">", KEY_DOT, 1.0},
+ {"/", "?", KEY_SLASH, 1.0},
+ {"Shift", "Shift", KEY_RIGHTSHIFT, 2.0}},
+ 12, 14.5},
+
+ // Row 4
+ {(KeyDef[]){{"Ctrl", "Ctrl", KEY_LEFTCTRL, 2.0},
+ {"Super", "Super", KEY_LEFTMETA, 1.0},
+ {"Alt", "Alt", KEY_LEFTALT, 1.0},
+ {"Space", "Space", KEY_SPACE, 6.0},
+ {"Alt", "Alt", KEY_RIGHTALT, 1.0},
+ {"Ctrl", "Ctrl", KEY_RIGHTCTRL, 1.0}},
+ 6, 12.0},
+};
+
+#define KEY_MAP_SIZE (sizeof(keys) / sizeof(RowDef))
+
+#endif
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..4006d7a
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,235 @@
+#include "cairo.h"
+#include "glib-object.h"
+#include "keymap.h"
+#include
+#include
+#include
+#include
+#include
+#include
+
+GtkWidget *window = NULL;
+GtkDrawingArea *area = NULL;
+
+struct libevdev *dev;
+struct libevdev_uinput *uidev;
+
+static gboolean shift_active = FALSE;
+static gboolean alt_active = FALSE;
+static gboolean ctrl_active = FALSE;
+
+void send_keypress(int key_code) {
+ if (!uidev)
+ return;
+ libevdev_uinput_write_event(uidev, EV_KEY, key_code, 1);
+ libevdev_uinput_write_event(uidev, EV_SYN, SYN_REPORT, 0);
+ libevdev_uinput_write_event(uidev, EV_KEY, key_code, 0);
+ libevdev_uinput_write_event(uidev, EV_SYN, SYN_REPORT, 0);
+}
+
+static void on_click_pressed(GtkGestureClick *gesture, int n_press, double x,
+ double y, gpointer data) {
+ GtkWidget *widget =
+ gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(gesture));
+ int width = gtk_widget_get_width(widget);
+ int height = gtk_widget_get_height(widget);
+
+ int row_height = height / 5;
+ int row_index = (int)(y / row_height);
+ if (row_index < 0 || row_index >= 5)
+ return;
+
+ RowDef row = keys[row_index];
+ double unit_width = (double)width / row.total_weight;
+
+ double current_x = 0;
+ for (int i = 0; i < row.key_count; i++) {
+ double next_x = current_x + (row.keys[i].weight * unit_width);
+
+ if (x >= current_x && x <= next_x) {
+ KeyDef *clicked_key = &row.keys[i];
+
+ if (clicked_key->key_code == KEY_LEFTSHIFT ||
+ clicked_key->key_code == KEY_RIGHTSHIFT) {
+ shift_active = !shift_active;
+ gtk_widget_queue_draw(GTK_WIDGET(area));
+ return;
+ }
+
+ if (clicked_key->key_code == KEY_LEFTALT ||
+ clicked_key->key_code == KEY_RIGHTALT) {
+ alt_active = !alt_active;
+ return;
+ }
+
+ if (clicked_key->key_code == KEY_LEFTCTRL ||
+ clicked_key->key_code == KEY_RIGHTCTRL) {
+ ctrl_active = !ctrl_active;
+ return;
+ }
+
+ if (shift_active) {
+ libevdev_uinput_write_event(uidev, EV_KEY, KEY_LEFTSHIFT, 1);
+ libevdev_uinput_write_event(uidev, EV_SYN, SYN_REPORT, 0);
+ }
+
+ if (alt_active) {
+ libevdev_uinput_write_event(uidev, EV_KEY, KEY_LEFTALT, 1);
+ libevdev_uinput_write_event(uidev, EV_SYN, SYN_REPORT, 0);
+ }
+
+ if (ctrl_active) {
+ libevdev_uinput_write_event(uidev, EV_KEY, KEY_LEFTCTRL, 1);
+ libevdev_uinput_write_event(uidev, EV_SYN, SYN_REPORT, 0);
+ }
+
+ send_keypress(clicked_key->key_code);
+
+ if (shift_active) {
+ libevdev_uinput_write_event(uidev, EV_KEY, KEY_LEFTSHIFT, 0);
+ libevdev_uinput_write_event(uidev, EV_SYN, SYN_REPORT, 0);
+
+ shift_active = FALSE;
+ gtk_widget_queue_draw(GTK_WIDGET(area));
+ }
+
+ if (alt_active) {
+ libevdev_uinput_write_event(uidev, EV_KEY, KEY_LEFTALT, 0);
+ libevdev_uinput_write_event(uidev, EV_SYN, SYN_REPORT, 0);
+
+ alt_active = FALSE;
+ }
+
+ if (ctrl_active) {
+ libevdev_uinput_write_event(uidev, EV_KEY, KEY_LEFTCTRL, 0);
+ libevdev_uinput_write_event(uidev, EV_SYN, SYN_REPORT, 0);
+
+ ctrl_active = FALSE;
+ }
+
+ break;
+ }
+ current_x = next_x;
+ }
+}
+
+static void draw_key(cairo_t *ct, KeyDef key, int x, int y, int key_width,
+ int key_height) {
+ cairo_set_source_rgba(ct, 1.0, 1.0, 1.0, 1.0);
+ cairo_set_line_width(ct, 3.0);
+ cairo_rectangle(ct, x, y, key_width, key_height);
+ cairo_stroke(ct);
+
+ cairo_select_font_face(ct, "Hack Nerd Font Mono", CAIRO_FONT_SLANT_NORMAL,
+ CAIRO_FONT_WEIGHT_NORMAL);
+ cairo_set_font_size(ct, key_height * 0.17);
+
+ const char *label_text = shift_active ? key.shift_label : key.label;
+
+ cairo_text_extents_t extents;
+ cairo_text_extents(ct, label_text, &extents);
+
+ double tx = x + (key_width - extents.width) / 2.0 - extents.x_bearing;
+ double ty = y + (key_height - extents.height) / 2.0 - extents.y_bearing;
+
+ cairo_set_source_rgba(ct, 1.0, 1.0, 1.0, 1.0);
+ cairo_move_to(ct, tx, ty);
+ cairo_show_text(ct, label_text);
+}
+
+static void draw_row(cairo_t *ct, RowDef row, int row_i, int width,
+ int height) {
+ int key_count = row.key_count;
+ float total_weight = row.total_weight;
+
+ double key_width = width / total_weight;
+ int key_height = height / 5;
+ double y = key_height * row_i;
+
+ double x = 0;
+ for (int i = 0; i < key_count; i++) {
+ double weighted_key_width = key_width * row.keys[i].weight;
+ draw_key(ct, row.keys[i], x, y, weighted_key_width, key_height);
+ x += weighted_key_width;
+ }
+}
+
+static void draw_keyboard(GtkDrawingArea *area, cairo_t *ct, int width,
+ int height, gpointer data) {
+ cairo_set_source_rgba(ct, 0.0, 0.0, 0.0, 1.0);
+ cairo_paint(ct);
+ for (int i = 0; i < 5; i++) {
+ draw_row(ct, keys[i], i, width, height);
+ }
+}
+
+void init_evdev_keyboard() {
+ dev = libevdev_new();
+ libevdev_set_name(dev, "OSKeyboard");
+ libevdev_enable_event_type(dev, EV_KEY);
+
+ for (int r = 0; r < 5; r++) {
+ for (int k = 0; k < keys[r].key_count; k++) {
+ libevdev_enable_event_code(dev, EV_KEY, keys[r].keys[k].key_code, NULL);
+ }
+ }
+
+ int fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
+ if (fd < 0) {
+ fprintf(stderr, "Failed to open /dev/uinput: %s\n", strerror(errno));
+ return;
+ }
+
+ int err = libevdev_uinput_create_from_device(dev, fd, &uidev);
+ if (err != 0) {
+ fprintf(stderr, "Failed to create uinput device: %s\n", strerror(-err));
+ close(fd);
+ return;
+ }
+}
+
+static void activate(GtkApplication *app, gpointer user_data) {
+ if (window) {
+ if (gtk_widget_get_visible(window)) {
+ gtk_widget_set_visible(window, FALSE);
+ } else {
+ gtk_window_present(GTK_WINDOW(window));
+ }
+ return;
+ }
+
+ GtkBuilder *builder =
+ gtk_builder_new_from_resource("/org/spaces/oskeyboard/keyboard.ui");
+ window = GTK_WIDGET(gtk_builder_get_object(builder, "main_window"));
+
+ gtk_layer_init_for_window(GTK_WINDOW(window));
+ gtk_layer_set_layer(GTK_WINDOW(window), GTK_LAYER_SHELL_EDGE_BOTTOM);
+ gtk_layer_auto_exclusive_zone_enable(GTK_WINDOW(window));
+ gtk_layer_set_keyboard_mode(GTK_WINDOW(window), FALSE);
+ gtk_layer_set_anchor(GTK_WINDOW(window), GTK_LAYER_SHELL_EDGE_BOTTOM, TRUE);
+ gtk_layer_set_anchor(GTK_WINDOW(window), GTK_LAYER_SHELL_EDGE_LEFT, TRUE);
+ gtk_layer_set_anchor(GTK_WINDOW(window), GTK_LAYER_SHELL_EDGE_RIGHT, TRUE);
+ gtk_widget_set_size_request(window, 1000, 500);
+ gtk_window_set_application(GTK_WINDOW(window), app);
+
+ area = GTK_DRAWING_AREA(gtk_builder_get_object(builder, "drawing_area"));
+ gtk_drawing_area_set_draw_func(area, draw_keyboard, NULL, NULL);
+
+ GtkGesture *gesture = gtk_gesture_click_new();
+ g_signal_connect(gesture, "pressed", G_CALLBACK(on_click_pressed), NULL);
+ gtk_widget_add_controller(GTK_WIDGET(area), GTK_EVENT_CONTROLLER(gesture));
+
+ init_evdev_keyboard();
+ gtk_window_present(GTK_WINDOW(window));
+ g_object_unref(builder);
+}
+
+int main(int argc, char **argv) {
+ GtkApplication *app =
+ gtk_application_new("org.spaces.oskeyboard", G_APPLICATION_DEFAULT_FLAGS);
+ g_signal_connect(app, "activate", G_CALLBACK(activate), NULL);
+
+ int status = g_application_run(G_APPLICATION(app), argc, argv);
+ g_object_unref(app);
+ return status;
+}
diff --git a/src/meson.build b/src/meson.build
new file mode 100644
index 0000000..4fb6466
--- /dev/null
+++ b/src/meson.build
@@ -0,0 +1,12 @@
+# Compile the resources into a C file
+res = gnome.compile_resources(
+ 'oskeyboard-resources',
+ 'resources.xml',
+ source_dir: '.', # Look for .ui and .css in the current directory
+)
+
+executable('osk',
+ 'main.c',
+ res, # Add the compiled resource file to the sources
+ dependencies : [gtk_dep, libevdev_dep, layer_shell_dep],
+ install : true)
diff --git a/resources.xml b/src/resources.xml
similarity index 100%
rename from resources.xml
rename to src/resources.xml
diff --git a/style.css b/src/style.css
similarity index 100%
rename from style.css
rename to src/style.css