]>
Commit | Line | Data |
---|---|---|
56339ab6 | 1 | /* |
2 | (C) 2016 Tobias Girstmair, released under the GNU GPL | |
3 | ||
4 | description: sends a sequence of keystrokes provided from stdin to the hid | |
5 | device. | |
6 | stops typing at: control characters (including newline), chars not in table, EOF | |
7 | parameters: | |
8 | device file (e.g. /dev/hidg0) | |
9 | keyboard layout (1=en_us, 2=de_at, 3=de_at-nodeadkeys) | |
10 | unicode method: 1=gtk_holddown, 2=gtk_spaceend, 3=windows | |
11 | */ | |
12 | #include <stdio.h> | |
13 | #include <stdlib.h> | |
14 | #include <string.h> | |
15 | #include "scancodes.h" | |
16 | ||
17 | #define TEXT_LEN 256 //max length of piped text | |
18 | enum params {//argv-indices: | |
19 | P_EXE, //executable name | |
20 | P_DEV, //device file | |
21 | P_LAY, //layout | |
22 | P_UNI, //unicode method | |
23 | NUM_P //number of parameters | |
24 | }; | |
25 | enum uni_m {//unicode methods: | |
26 | SKIP, //ignore any keys not on the layout | |
27 | GTK_HOLD, //hold ctrl and shift while entering hex values | |
28 | GTK_SPACE, //end hex sequence with spacebar | |
29 | WINDOWS //use alt+numpad | |
30 | }; | |
31 | enum errors { | |
32 | ERR_SUCCESS, //no error | |
33 | ERR_ARGCOUNT, //wrong number of arguments | |
34 | ERR_SYMBOL, //symbol not in look up table | |
35 | ERR_LAYOUT, //parameter P_LAY does not contain a correct keyboard layout | |
36 | ERR_LAZY //i haven't done this | |
37 | }; | |
38 | ||
39 | void send_key (FILE* hid_dev, unsigned short key, unsigned short mod); | |
40 | enum errors send_unicode (FILE* hid_dev, unsigned int unicode, enum uni_m method, enum kbdl layout); | |
41 | ||
42 | int main (int argc, char** argv) { | |
43 | if (argc != NUM_P) { | |
44 | fprintf (stderr, "Usage: %s <device file> <layout> <unicode>\n", argv[P_EXE]); | |
45 | fprintf (stderr, "Takes string to type from stdin\n"); | |
46 | fprintf (stderr, "<device file>:\ton the Raspberry Pi usually /dev/hidg0\n"); | |
47 | fprintf (stderr, "<layout>:\n\t%d\t%s\n\t%d\t%s\n\t%d\t%s\n", | |
48 | en_US, "en_US", | |
49 | de_AT, "de_AT (w/ dead keys)", | |
50 | de_ND, "de_AT-nodeadkeys"); | |
51 | fprintf (stderr, "<unicode>:\n\t%d\t%s\n\t%d\t%s\n\t%d\t%s\n\t%d\t%s\n", | |
52 | SKIP, "skip over unicode characters", | |
53 | GTK_HOLD, "X11 Holddown: CTRL+SHIFT+[u, hex]", | |
54 | GTK_SPACE, "X11 Space: CTRL+SHIFT+u, hex, SPACE", | |
55 | WINDOWS, "Windows: Alt+[Numpad]"); | |
56 | return ERR_ARGCOUNT; | |
57 | } | |
d6e251a2 | 58 | FILE* hid_dev = fopen (argv[P_DEV], "w"); |
56339ab6 | 59 | char in_string[TEXT_LEN]; |
60 | fgets(in_string, TEXT_LEN, stdin); | |
61 | for (int i = 0; i < strlen (in_string); i++) { | |
62 | /* if (in_string[i] < 32) { | |
63 | if (in_string[i] != '\n') fprintf (stderr, "Cannot print control characters!\n(Is there a newline at the end of your string?\n"); | |
64 | return ERR_SYMBOL; | |
65 | } | |
66 | */ | |
67 | char tmp[UTF8_MAX_LENGTH] = {in_string[i], in_string[i+1], in_string[i+2], '\0'}; | |
68 | //TODO: replace by something less stupid | |
69 | if (in_string[i] < 128) { // not multi byte | |
70 | tmp[1] = '\0'; | |
71 | } else { // is multi byte | |
72 | if (in_string[i] < 0xe0) { | |
73 | i++; //skip next thing | |
74 | tmp[2] = 0; | |
75 | } else { | |
76 | i+=2; //WARN: fails on utf8 > 3 byte | |
77 | } | |
78 | } | |
79 | ||
80 | struct keysym* s = toscan (tmp); | |
81 | if (s == NULL) { | |
82 | fprintf (stderr, "Key Symbol not found.\n"); | |
83 | fclose (hid_dev); | |
84 | return ERR_SYMBOL; | |
85 | } | |
86 | struct layout* l = tolay (s, atoi (argv[P_LAY])); | |
87 | if (l == NULL) { | |
88 | fprintf (stderr, "Unrecognised keyboard layout.\n"); | |
89 | fclose (hid_dev); | |
90 | return ERR_LAYOUT; | |
91 | } | |
92 | if (l->key != 0x00) { | |
93 | send_key(hid_dev, l->key, l->mod); | |
94 | send_key(hid_dev, '\0', '\0'); //release all keys | |
95 | if (l->is_dead) { | |
96 | //dead keys need to be pressed twice to show up | |
97 | send_key(hid_dev, l->key, l->mod); | |
98 | send_key(hid_dev, '\0', '\0'); //release all keys | |
99 | } | |
100 | } else { | |
101 | //key does not exist in this layout, use unicode method | |
102 | //fprintf (stderr, "Warning: Key '%s'(0x%x) not in this layout!\n", s->sym, s->unicode); | |
103 | send_unicode (hid_dev, s->unicode, atoi (argv[P_UNI]), atoi(argv[P_LAY])); | |
104 | } | |
105 | } | |
106 | fclose (hid_dev); | |
107 | ||
108 | return ERR_SUCCESS; | |
109 | } | |
110 | ||
111 | void send_key (FILE* hid_dev, unsigned short key, unsigned short mod) { | |
112 | fprintf (hid_dev, "%c%c%c%c%c%c%c%c", mod, '\0', key, '\0', '\0', '\0', '\0', '\0'); | |
113 | } | |
114 | ||
115 | enum errors send_unicode (FILE* hid_dev, unsigned int unicode, enum uni_m method, enum kbdl layout) { | |
116 | char buf[10]; | |
117 | struct keysym* s; | |
118 | struct layout* l; | |
119 | ||
120 | if (unicode == 0x00) { | |
121 | fprintf (stderr, "Symbol not in lookup table!\n"); | |
122 | return ERR_SYMBOL; | |
123 | } | |
124 | ||
125 | switch (method) { | |
126 | case SKIP: | |
127 | break; | |
128 | case GTK_HOLD: | |
129 | sprintf (buf, "%x", unicode); | |
130 | s = toscan ("u"); | |
131 | l = tolay (s, layout); | |
132 | send_key (hid_dev, l->key, MOD_LCTRL | MOD_LSHIFT); | |
133 | for (int i = 0; i < strlen (buf); i++) { | |
134 | s = toscan ((char[2]){buf[i], '\0'}); | |
135 | l = tolay (s, layout); | |
136 | send_key (hid_dev, l->key, MOD_LCTRL | MOD_LSHIFT); | |
137 | } | |
138 | send_key (hid_dev, '\0', '\0'); | |
139 | break; | |
140 | case GTK_SPACE: | |
141 | sprintf (buf, "%x ", unicode); | |
142 | s = toscan ("u"); | |
143 | l = tolay (s, layout); | |
144 | send_key (hid_dev, l->key, MOD_LCTRL | MOD_LSHIFT); | |
145 | for (int i = 0; i < strlen (buf); i++) { | |
146 | s = toscan ((char[2]){buf[i], '\0'}); | |
147 | l = tolay (s, layout); | |
148 | send_key (hid_dev, l->key, MOD_NONE); | |
149 | } | |
150 | send_key (hid_dev, '\0', '\0'); | |
151 | break; | |
152 | case WINDOWS: | |
153 | fprintf (stderr, "windows method not implemented!\n"); | |
154 | return ERR_LAZY; | |
155 | default: | |
156 | fprintf (stderr, "unknown unicode method!\n"); | |
157 | return ERR_LAYOUT; //TODO: better error code | |
158 | } | |
159 | return ERR_SUCCESS; | |
160 | } |