Files
oskeyboard/main.c
T

185 lines
5.8 KiB
C

#include <libevdev/libevdev.h>
#include <libevdev/libevdev-uinput.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <gtk/gtk.h>
#include <gtk4-layer-shell/gtk4-layer-shell.h>
#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;
}