re-implemented keyboard for better performance and keypress detection. there is no longer css customization, but the keyboard is now responsive to the screen width.
This commit is contained in:
Binary file not shown.
Binary file not shown.
+2
-1
@@ -1,3 +1,4 @@
|
|||||||
osk
|
*osk
|
||||||
*.o
|
*.o
|
||||||
*.env
|
*.env
|
||||||
|
build/*
|
||||||
|
|||||||
@@ -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
|
|
||||||
@@ -32,17 +32,17 @@ Run the following commands:
|
|||||||
```
|
```
|
||||||
git clone https://github.com/weave-space/oskeyboard
|
git clone https://github.com/weave-space/oskeyboard
|
||||||
cd oskeyboard
|
cd oskeyboard
|
||||||
make
|
meson compile -C build
|
||||||
./osk
|
./build/osk
|
||||||
```
|
```
|
||||||
|
|
||||||
## Customization
|
## 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.
|
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
|
## TODO
|
||||||
- [ ] Responsive design sizes
|
- [x] Responsive design sizes
|
||||||
- [ ] CLI flags
|
- [ ] CLI flags
|
||||||
- [ ] Man page
|
- [ ] Man page
|
||||||
- [ ] Documentation
|
- [ ] Documentation
|
||||||
|
|||||||
-84
@@ -1,84 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<interface>
|
|
||||||
<object class="GtkWindow" id="main_window">
|
|
||||||
<property name="title">OSKeyboard</property>
|
|
||||||
|
|
||||||
<child>
|
|
||||||
<object class="GtkGrid" id="keyboard_container">
|
|
||||||
<property name="row-spacing">15</property>
|
|
||||||
<property name="halign">center</property>
|
|
||||||
<property name="valign">center</property>
|
|
||||||
|
|
||||||
<child>
|
|
||||||
<object class="GtkGrid" id="kbd_row_0">
|
|
||||||
<style><class name="keyboard_row"></class></style>
|
|
||||||
<layout>
|
|
||||||
<property name="row">0</property>
|
|
||||||
<property name="column">0</property>
|
|
||||||
</layout>
|
|
||||||
<property name="column-spacing">15</property>
|
|
||||||
<!-- <property name="halign">left</property> -->
|
|
||||||
<property name="valign">center</property>
|
|
||||||
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
|
|
||||||
<child>
|
|
||||||
<object class="GtkGrid" id="kbd_row_1">
|
|
||||||
<style><class name="keyboard_row"></class></style>
|
|
||||||
<layout>
|
|
||||||
<property name="row">1</property>
|
|
||||||
<property name="column">0</property>
|
|
||||||
</layout>
|
|
||||||
<property name="column-spacing">15</property>
|
|
||||||
<!-- <property name="halign">left</property> -->
|
|
||||||
<property name="valign">center</property>
|
|
||||||
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
|
|
||||||
<child>
|
|
||||||
<object class="GtkGrid" id="kbd_row_2">
|
|
||||||
<style><class name="keyboard_row"></class></style>
|
|
||||||
<layout>
|
|
||||||
<property name="row">2</property>
|
|
||||||
<property name="column">0</property>
|
|
||||||
</layout>
|
|
||||||
<property name="column-spacing">15</property>
|
|
||||||
<!-- <property name="halign">left</property> -->
|
|
||||||
<property name="valign">center</property>
|
|
||||||
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
|
|
||||||
<child>
|
|
||||||
<object class="GtkGrid" id="kbd_row_3">
|
|
||||||
<style><class name="keyboard_row"></class></style>
|
|
||||||
<layout>
|
|
||||||
<property name="row">3</property>
|
|
||||||
<property name="column">0</property>
|
|
||||||
</layout>
|
|
||||||
<property name="column-spacing">15</property>
|
|
||||||
<!-- <property name="halign">left</property> -->
|
|
||||||
<property name="valign">center</property>
|
|
||||||
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
|
|
||||||
<child>
|
|
||||||
<object class="GtkGrid" id="kbd_row_4">
|
|
||||||
<style><class name="keyboard_row"></class></style>
|
|
||||||
<layout>
|
|
||||||
<property name="row">4</property>
|
|
||||||
<property name="column">0</property>
|
|
||||||
</layout>
|
|
||||||
<property name="column-spacing">15</property>
|
|
||||||
<!-- <property name="halign">left</property> -->
|
|
||||||
<property name="valign">center</property>
|
|
||||||
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</interface>
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
#ifndef KEYMAP_H
|
|
||||||
#define KEYMAP_H
|
|
||||||
|
|
||||||
#include <linux/input-event-codes.h>
|
|
||||||
|
|
||||||
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
|
|
||||||
@@ -1,184 +0,0 @@
|
|||||||
#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;
|
|
||||||
}
|
|
||||||
+12
@@ -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')
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<interface>
|
||||||
|
<object class="GtkWindow" id="main_window">
|
||||||
|
<property name="title">OSKeyboard</property>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<object class="GtkDrawingArea" id="drawing_area">
|
||||||
|
<property name="can-focus">FALSE</property>
|
||||||
|
<property name="hexpand">TRUE</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</interface>
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
#ifndef KEYMAP_H
|
||||||
|
#define KEYMAP_H
|
||||||
|
|
||||||
|
#include <linux/input-event-codes.h>
|
||||||
|
|
||||||
|
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
|
||||||
+235
@@ -0,0 +1,235 @@
|
|||||||
|
#include "cairo.h"
|
||||||
|
#include "glib-object.h"
|
||||||
|
#include "keymap.h"
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
#include <gtk4-layer-shell/gtk4-layer-shell.h>
|
||||||
|
#include <libevdev/libevdev-uinput.h>
|
||||||
|
#include <libevdev/libevdev.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
Reference in New Issue
Block a user