--- /dev/null
+/* VR Keyboard
+ *
+ * (C) 2018 by Andreas Eversberg <jolly@eversberg.eu>
+ * All Rights Reserved
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+#include <math.h>
+#include "../../include/keycodes.h"
+#include "../libsdl/print.h"
+#include "keyboard.h"
+#define GL3_PROTOTYPES 1
+#include <GL/glew.h>
+
+#define MAX_KEYBOARDS 2 /* maximum number of keyboards to we have */
+#define MAX_KEYS 64 /* maximum number of keys per keyboard */
+#define MAX_KEY_LINES 16 /* maximum number of lines on each key */
+#define TILT_KEYBOARD /* keyboard is tilted up from horzontal x-z-plane to vertical x-y-plane */
+
+/*
+ * The keys are defined by coordinates that are described by the following
+ * digits:
+ *
+ * a b c d e
+ *
+ * f g h i j
+ *
+ * k l m n o
+ *
+ * p q r s t
+ *
+ * u v w x y
+ *
+ * The lines are defined from digit to digit to digit.... To start with a
+ * new line (not connected to the previous one, add ' ' (space).
+ *
+ */
+
+static struct key_font {
+ char key;
+ const char *font;
+} key_font[] = {
+ { '0', "bdjtxvpfb qi" },
+ { '1', "gcw vx" },
+ { '2', "fbdjnlpuy" },
+ { '3', "fbdjntxvp ln" },
+ { '4', "ako hw" },
+ { '5', "eakntxu" },
+ { '6', "jdbfpvxtnk" },
+ { '7', "aemw" },
+ { '8', "lfbdjntxvpln" },
+ { '9', "jnlfbdjtxvp" },
+ { 'A', "ukoy kco" },
+ { 'B', "knjdauxtn" },
+ { 'C', "jdbfpvxt" },
+ { 'D', "adjtxua" },
+ { 'E', "eauy km" },
+ { 'F', "eau km" },
+ { 'G', "jdbfpvxt yom" },
+ { 'H', "au ey ko" },
+ { 'I', "bd vx cw" },
+ { 'J', "aetxvpk" },
+ { 'K', "au emy km" },
+ { 'L', "auy" },
+ { 'M', "uamey" },
+ { 'N', "uaye" },
+ { 'O', "bdjtxvpfb" },
+ { 'P', "uadjnk" },
+ { 'Q', "bdjtxvpfb ys" },
+ { 'R', "uadjnk my" },
+ { 'S', "jdbflntxvp" },
+ { 'T', "ae cw" },
+ { 'U', "apvxte" },
+ { 'V', "akwoe" },
+ { 'W', "aumye" },
+ { 'X', "ay ue" },
+ { 'Y', "ame mw" },
+ { 'Z', "aeuy" },
+ { 'u', "cw kco" },
+ { 'd', "cw kwo" },
+ { 'l', "ko ckw" },
+ { 'r', "ko cow" },
+ { '.', "rr" },
+ { ',', "rv" },
+ { '+', "cw ko" },
+ { '-', "ko" },
+ { '*', "ko ay ue" },
+ { 's', "" }, /* space */
+ { 'b', "beyvkb gs qi" },
+ { 'e', "dsp pl pv" },
+ { ' ', "" },
+ { 0, NULL }
+};
+
+/*
+ * f-keys are 25 percent larger, so 4 f-keys are as wide as 5 regular keys.
+ * This is why f-keys use 5 steps instead of four (regular keys).
+ */
+
+static const char *keyboard_layout[] = {
+ "1 2 3 4 5 6 7 8 9 0 G",
+ " fh e ",
+ " f1 f2 f0 7 8 9 * ",
+ " 4 5 6 - ",
+ " u 1 2 3 + ",
+ " l d r 02 e2 ",
+ NULL, /* end of keyboard */
+ "1 2 3 4 5 6 7 8 9 0 b",
+ " Q W E R T Y U I O P ",
+ " A S D F G H J K L e ",
+ " Z X C V B N M , . ",
+ " s6 ",
+ NULL, /* end of keyboard */
+ NULL, /* end of all keyboards */
+};
+
+static enum keycode keyboard_codes_1[] = {
+ KEYCODE_1, KEYCODE_2, KEYCODE_3, KEYCODE_4, KEYCODE_5, KEYCODE_6, KEYCODE_7, KEYCODE_8, KEYCODE_9, KEYCODE_0, KEYCODE_g,
+ KEYCODE_HELP, KEYCODE_RETURN,
+ KEYCODE_F1, KEYCODE_F2, KEYCODE_F10, KEYCODE_KP_7, KEYCODE_KP_8, KEYCODE_KP_9, KEYCODE_KP_MULTIPLY,
+ KEYCODE_KP_4, KEYCODE_KP_5, KEYCODE_KP_6, KEYCODE_KP_MINUS,
+ KEYCODE_UP, KEYCODE_KP_1, KEYCODE_KP_2, KEYCODE_KP_3, KEYCODE_KP_PLUS,
+ KEYCODE_LEFT, KEYCODE_DOWN, KEYCODE_RIGHT, KEYCODE_KP_0, KEYCODE_KP_ENTER,
+};
+
+static enum keycode keyboard_codes_2[] = {
+ KEYCODE_1, KEYCODE_2, KEYCODE_3, KEYCODE_4, KEYCODE_5, KEYCODE_6, KEYCODE_7, KEYCODE_8, KEYCODE_9, KEYCODE_0, KEYCODE_BACKSPACE,
+ KEYCODE_q, KEYCODE_w, KEYCODE_e, KEYCODE_r, KEYCODE_t, KEYCODE_y, KEYCODE_u, KEYCODE_i, KEYCODE_o, KEYCODE_p,
+ KEYCODE_a, KEYCODE_s, KEYCODE_d, KEYCODE_f, KEYCODE_g, KEYCODE_h, KEYCODE_j, KEYCODE_k, KEYCODE_l, KEYCODE_RETURN,
+ KEYCODE_z, KEYCODE_x, KEYCODE_c, KEYCODE_v, KEYCODE_b, KEYCODE_n, KEYCODE_m, KEYCODE_COMMA, KEYCODE_PERIOD,
+ KEYCODE_SPACE,
+};
+
+static enum keycode *keyboard_codes[] = {
+ keyboard_codes_1, keyboard_codes_2,
+};
+
+/* definition of keys with coordinates for rendering */
+
+struct key_def {
+ char key;
+ int fkey;
+ double left_top_x, right_top_x;
+ double top_y;
+ double front_top_z, back_top_z;
+ double left_bottom_x, right_bottom_x;
+ double bottom_y;
+ double front_bottom_z, back_bottom_z;
+ double x1[MAX_KEY_LINES], y1[MAX_KEY_LINES], z1[MAX_KEY_LINES];
+ double x2[MAX_KEY_LINES], y2[MAX_KEY_LINES], z2[MAX_KEY_LINES];
+ int lines;
+};
+
+/* definition for each keyboard */
+static struct keyboard_def {
+ struct key_def key[MAX_KEYS];
+ int keys;
+} keyboard_def[MAX_KEYBOARDS];
+
+static int keyboards = 0;
+
+/*
+ * add key to keyboard_def[].key structure
+ *
+ *-4 -2 0 2 3 4
+ * +-------------------+ 4
+ * | +---------------+ | 3
+ * | | a b c d e | | 2
+ * | | f g h i j | | 1
+ * | | k l m n o | | 0
+ * | | p q r s t | | -1
+ * | | u v w x y | | -2
+ * | +---------------+ | -3
+ * +-------------------+ -4
+ * (front)
+ */
+
+static int add_key(char key, int fkey, double width, struct key_def *key_def)
+{
+ int d, l, f, i;
+ double font_scale = 1.0, font_offset = 0.0;
+
+ memset(key_def, 0, sizeof(*key_def));
+
+ key_def->key = key;
+ key_def->fkey = fkey;
+
+ /* key case (see layout in the description above) */
+ key_def->left_top_x = -3.0;
+ key_def->right_top_x = 3.0 + (width - 1.0) * 9.0;
+ key_def->top_y = 0;
+ key_def->front_top_z = 3.0;
+ key_def->back_top_z = -3.0;
+ key_def->left_bottom_x = -4.0;
+ key_def->right_bottom_x = 4.0 + (width - 1.0) * 9.0;
+ key_def->bottom_y = -4.0;
+ key_def->front_bottom_z = 4.0;
+ key_def->back_bottom_z = -4.0;
+
+ /* lines to be drawn ontop of the key (see layout in the description above) */
+ l = 0;
+ /* up to 4 letters/digits are displayed on key */
+ for (d = 0; d < 4; d++) {
+ if (!key_def->fkey) {
+ /* render regular key */
+ if (d == 0)
+ key = key_def->key;
+ else
+ break;
+ } else {
+ /* render F-key */
+ if (d == 0) {
+ font_scale = 0.5;
+ font_offset = -0.25;
+ if (key_def->key == 'h')
+ key = 'H';
+ else
+ key = 'F';
+ }
+ else if (d == 1) {
+ font_offset += 2.5;
+ if (key_def->key == 'h')
+ key = 'E';
+ else if (key_def->key != '0')
+ key = key_def->key;
+ else
+ key = '1';
+ }
+ else if (d == 2) {
+ font_offset += 2.5;
+ if (key_def->key == 'h')
+ key = 'L';
+ else if (key_def->key != '0')
+ break;
+ else
+ key = '0';
+ }
+ else if (d == 3) {
+ font_offset += 2.5;
+ if (key_def->key == 'h')
+ key = 'P';
+ else
+ break;
+ }
+ }
+ /* search for key font */
+ for (f = 0; key_font[f].key; f++) {
+ if (key_font[f].key == key)
+ break;
+ }
+ if (!key_font[f].key) {
+ print_error("Failed to find keyboard character '%c' in key_font, please fix\n", key);
+ return -EINVAL;
+ }
+ /* generate lines */
+ i = 0;
+ while (key_font[f].font[i] && key_font[f].font[i + 1]) {
+ /* skip spaces, but this should not happen */
+ if (key_font[f].font[i] == ' ') {
+ i++;
+ continue;
+ }
+ /* the second character is space, so we start over with a new line sequence */
+ if (key_font[f].font[i + 1] == ' ') {
+ i += 2;
+ continue;
+ }
+ /* add line */
+ if (l == MAX_KEY_LINES) {
+ print_error("Failed to add key definition, maximum number of lines exeeded, please fix\n");
+ return -EINVAL;
+ }
+ key_def->x1[l] = (double)(((key_font[f].font[i] - 'a') % 5) - 2) * font_scale + font_offset;
+ key_def->y1[l] = 0;
+ key_def->z1[l] = (double)((key_font[f].font[i] - 'a') / 5) - 2;
+ key_def->x2[l] = (double)(((key_font[f].font[i + 1] - 'a') % 5) - 2) * font_scale + font_offset;
+ key_def->y2[l] = 0;
+ key_def->z2[l] = (double)((key_font[f].font[i + 1] - 'a') / 5) - 2;
+ l++;
+ i++;
+ }
+ }
+ key_def->lines = l;
+
+ return 0;
+}
+
+static void scale_key(double px, double py, double pz, double scale, struct key_def *key_def)
+{
+ int l;
+
+ /* scale and translate key case */
+ key_def->left_top_x = key_def->left_top_x * scale + px;
+ key_def->right_top_x = key_def->right_top_x * scale + px;
+ key_def->top_y = key_def->top_y * scale + py;
+ key_def->front_top_z = key_def->front_top_z * scale + pz;
+ key_def->back_top_z = key_def->back_top_z * scale + pz;
+ key_def->left_bottom_x = key_def->left_bottom_x * scale + px;
+ key_def->right_bottom_x = key_def->right_bottom_x * scale + px;
+ key_def->bottom_y = key_def->bottom_y * scale + py;
+ key_def->front_bottom_z = key_def->front_bottom_z * scale + pz;
+ key_def->back_bottom_z = key_def->back_bottom_z * scale + pz;
+
+ /* scale and translate line ontop of keys */
+ for (l = 0; l < key_def->lines; l++) {
+ key_def->x1[l] = key_def->x1[l] * scale + px;
+ key_def->y1[l] = key_def->y1[l] * scale + py;
+ key_def->z1[l] = key_def->z1[l] * scale + pz;
+ key_def->x2[l] = key_def->x2[l] * scale + px;
+ key_def->y2[l] = key_def->y2[l] * scale + py;
+ key_def->z2[l] = key_def->z2[l] * scale + pz;
+ }
+}
+
+#define KEY_SIZE_INCH 3.0
+
+int init_vr_keyboard(double keyboard_height, double keyboard_distance)
+{
+ double width;
+ int l, k, y, x;
+ int rc;
+ int key, fkey;
+
+ l = 0;
+ /* count all keyboards */
+ while (keyboard_layout[l]) {
+ k = 0;
+ /* for each row in keyboard */
+ for (y = 0; keyboard_layout[l]; y++, l++) {
+ /* for each key in row */
+ for (x = 0; keyboard_layout[l][x]; x++) {
+ /* skip spaces, because this is no key */
+ if (keyboard_layout[l][x] == ' ')
+ continue;
+ width = 1;
+ key = keyboard_layout[l][x];
+ fkey = 0;
+ /* if we have an F-key, this means size * 1.5 */
+ if (keyboard_layout[l][x] == 'f') {
+ if (keyboard_layout[l][x + 1] == 'h')
+ width = 2.0; /* help key */
+ else
+ width = 1.5; /* f-key */
+ key = keyboard_layout[l][x + 1];
+ fkey = 1;
+ } else
+ /* if we have a digit after the key, this means different size */
+ if (keyboard_layout[l][x + 1] > '0')
+ width = (double)(keyboard_layout[l][x + 1] - '0');
+ rc = add_key(key, fkey, width, &keyboard_def[keyboards].key[k]);
+ if (rc < 0)
+ return -EINVAL;
+#ifdef TILT_KEYBOARD
+ scale_key(((double)x / 4.0 - 5.0) * KEY_SIZE_INCH, -keyboard_distance, (double)y * KEY_SIZE_INCH - keyboard_height, KEY_SIZE_INCH / 9, &keyboard_def[keyboards].key[k]);
+#else
+ scale_key(((double)x / 4.0 - 5.0) * KEY_SIZE_INCH, keyboard_height, (double)y * KEY_SIZE_INCH - keyboard_distance, KEY_SIZE_INCH / 9, &keyboard_def[keyboards].key[k]);
+#endif
+ k++;
+ /* skip size information, if any */
+ if (keyboard_layout[l][x + 1] > ' ')
+ x++;
+ }
+ }
+ keyboard_def[keyboards].keys = k;
+ keyboards++;
+ l++;
+ }
+
+ return 0;
+}
+
+static int highlight_k;
+static double from_x, from_y, from_z;
+static double to_x, to_y, to_z;
+
+/* calculate pointer and to what key it points to */
+void handle_vr_keyboard(int keyboard, double pointer_x, double pointer_y, double pointer_z, double pointer_yaw, double pointer_pitch, enum keycode *key)
+{
+ int k;
+ struct key_def *key_def;
+#ifdef TILT_KEYBOARD
+ double keyboard_dist = keyboard_def[keyboard].key[0].y1[0]; /* just get the dist from first line of first key */
+#else
+ double keyboard_height = keyboard_def[keyboard].key[0].y1[0]; /* just get the height from first line of first key */
+#endif
+
+ /* generate poiting vector */
+ from_x = pointer_x;
+ from_y = pointer_y;
+ from_z = pointer_z;
+ to_x = from_x - sin(pointer_yaw) * cos(pointer_pitch) * 20;
+ to_y = from_y + sin(pointer_pitch) * 20;
+ to_z = from_z - cos(pointer_yaw) * cos(pointer_pitch) * 20;
+
+ /* if pointer points down, intersect with keyboard */
+ highlight_k = -1;
+#ifdef TILT_KEYBOARD
+ if (from_z - 1.0 > keyboard_dist && to_z < from_z - 1.0) {
+ to_x = (to_x - from_x) / (from_z - to_z) * (from_z - keyboard_dist) + from_x;
+ to_y = (to_y - from_y) / (from_z - to_z) * (from_z - keyboard_dist) + from_y;
+ to_z = keyboard_dist;
+
+ /* find key that we intersect with */
+ for (k = 0; k < keyboard_def[keyboard].keys; k++) {
+ key_def = &keyboard_def[keyboard].key[k];
+ if (to_x < key_def->left_top_x)
+ continue;
+ if (to_x > key_def->right_top_x)
+ continue;
+ if (-to_y < key_def->back_top_z)
+ continue;
+ if (-to_y > key_def->front_top_z)
+ continue;
+ highlight_k = k;
+ *key = keyboard_codes[keyboard][k];
+ break;
+ }
+ }
+#else
+ if (from_y - 1.0 > keyboard_height && to_y < from_y - 1.0) {
+ to_x = (to_x - from_x) / (from_y - to_y) * (from_y - keyboard_height) + from_x;
+ to_z = (to_z - from_z) / (from_y - to_y) * (from_y - keyboard_height) + from_z;
+ to_y = keyboard_height;
+
+ /* find key that we intersect with */
+ for (k = 0; k < keyboard_def[keyboard].keys; k++) {
+ key_def = &keyboard_def[keyboard].key[k];
+ if (to_x < key_def->left_top_x)
+ continue;
+ if (to_x > key_def->right_top_x)
+ continue;
+ if (to_z < key_def->back_top_z)
+ continue;
+ if (to_z > key_def->front_top_z)
+ continue;
+ highlight_k = k;
+ *key = keyboard_codes[keyboard][k];
+ break;
+ }
+ }
+#endif
+}
+
+#ifdef TILT_KEYBOARD
+#define FLIP_YZ(y, z) -z, y
+#else
+#define FLIP_YZ(y, z) y, z
+#endif
+
+/* render keyboard per eye */
+void render_vr_keyboard(int keyboard, double camera_x, double camera_y)
+{
+ int k, l, distant_k;
+ int rendered[MAX_KEYS];
+ double dists[MAX_KEYS], dist_x, dist_y, dist;
+ struct key_def *key_def;
+
+ glEnable(GL_CULL_FACE);
+ glFrontFace(GL_CW);
+ glCullFace(GL_FRONT);
+
+ memset(rendered, 0, sizeof(rendered));
+
+ /* calculate distances to keys */
+ for (k = 0; k < keyboard_def[keyboard].keys; k++) {
+ key_def = &keyboard_def[keyboard].key[k];
+#ifdef TILT_KEYBOARD
+ dist_x = (key_def->left_top_x + key_def->right_top_x) / 2.0 - camera_x;
+ dist_y = (key_def->front_top_z + key_def->back_top_z) / 2.0 + camera_y;
+#else
+ dist_x = (key_def->left_top_x + key_def->right_top_x) / 2.0 - camera_x;
+ dist_y = (key_def->front_top_z + key_def->back_top_z) / 2.0;
+#endif
+ dists[k] = sqrt(dist_x * dist_x + dist_y * dist_y);
+ }
+
+ while (1) {
+ key_def = NULL;
+ /* search for most distant key */
+ for (k = 0; k < keyboard_def[keyboard].keys; k++) {
+ /* skip keys that have been already rendered */
+ if (rendered[k])
+ continue;
+ if (!key_def || dists[k] > dist) {
+ dist = dists[k];
+ key_def = &keyboard_def[keyboard].key[k];
+ distant_k = k;
+ }
+ }
+ /* no unrendered key, we are done */
+ if (!key_def)
+ break;
+ rendered[distant_k] = 1;
+
+ /* render case of key */
+ glBegin(GL_QUADS);
+ /* top plane */
+ if (distant_k == highlight_k)
+ glColor4f(0.0, 0.7, 0.7, 1.0);
+ else
+ glColor4f(0.5, 0.5, 0.5, 1.0);
+ glVertex3f(key_def->left_top_x, FLIP_YZ(key_def->top_y, key_def->back_top_z));
+ glVertex3f(key_def->right_top_x, FLIP_YZ(key_def->top_y, key_def->back_top_z));
+ glVertex3f(key_def->right_top_x, FLIP_YZ(key_def->top_y, key_def->front_top_z));
+ glVertex3f(key_def->left_top_x, FLIP_YZ(key_def->top_y, key_def->front_top_z));
+ /* front plane (in front of player) */
+ glColor4f(0.1, 0.1, 0.1, 1.0);
+ glVertex3f(key_def->left_top_x, FLIP_YZ(key_def->top_y, key_def->front_top_z));
+ glVertex3f(key_def->right_top_x, FLIP_YZ(key_def->top_y, key_def->front_top_z));
+ glVertex3f(key_def->right_bottom_x, FLIP_YZ(key_def->bottom_y, key_def->front_bottom_z));
+ glVertex3f(key_def->left_bottom_x, FLIP_YZ(key_def->bottom_y, key_def->front_bottom_z));
+ /* back plane (away from player) */
+ glColor4f(0.3, 0.3, 0.3, 1.0);
+ glVertex3f(key_def->right_top_x, FLIP_YZ(key_def->top_y, key_def->back_top_z));
+ glVertex3f(key_def->left_top_x, FLIP_YZ(key_def->top_y, key_def->back_top_z));
+ glVertex3f(key_def->left_bottom_x, FLIP_YZ(key_def->bottom_y, key_def->back_bottom_z));
+ glVertex3f(key_def->right_bottom_x, FLIP_YZ(key_def->bottom_y, key_def->back_bottom_z));
+ /* left plane */
+ glColor4f(0.2, 0.2, 0.2, 1.0);
+ glVertex3f(key_def->left_top_x, FLIP_YZ(key_def->top_y, key_def->back_top_z));
+ glVertex3f(key_def->left_top_x, FLIP_YZ(key_def->top_y, key_def->front_top_z));
+ glVertex3f(key_def->left_bottom_x, FLIP_YZ(key_def->bottom_y, key_def->front_bottom_z));
+ glVertex3f(key_def->left_bottom_x, FLIP_YZ(key_def->bottom_y, key_def->back_bottom_z));
+ /* right plane */
+ glVertex3f(key_def->right_top_x, FLIP_YZ(key_def->top_y, key_def->front_top_z));
+ glVertex3f(key_def->right_top_x, FLIP_YZ(key_def->top_y, key_def->back_top_z));
+ glVertex3f(key_def->right_bottom_x, FLIP_YZ(key_def->bottom_y, key_def->back_bottom_z));
+ glVertex3f(key_def->right_bottom_x, FLIP_YZ(key_def->bottom_y, key_def->front_bottom_z));
+ /* no bottom plane, because it is unlikely visible */
+ glEnd();
+
+ /* render lines of key */
+ glColor4f(1.0, 1.0, 1.0, 1.0);
+ for (l = 0; l < key_def->lines; l++) {
+ if (key_def->x1[l] == key_def->x2[l] && key_def->z1[l] == key_def->z2[l]) {
+ /* just a point */
+ glBegin(GL_POINTS);
+ glVertex3f(key_def->x1[l], FLIP_YZ(key_def->y1[l], key_def->z1[l]));
+ glEnd();
+ } else {
+ glBegin(GL_LINES);
+ glVertex3f(key_def->x1[l], FLIP_YZ(key_def->y1[l], key_def->z1[l]));
+ glVertex3f(key_def->x2[l], FLIP_YZ(key_def->y2[l], key_def->z2[l]));
+ glEnd();
+ }
+ }
+ }
+
+ glDisable(GL_CULL_FACE);
+
+ /* render pointer */
+ glColor4f(1.0, 0.0, 0.0, 1.0);
+ glBegin(GL_LINES);
+ glVertex3f(from_x, from_y, from_z);
+ glVertex3f(to_x, to_y, to_z);
+ glEnd();
+}
+