1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
|
//Input Method Functions and Variables
//#include "imtable.h"
#define IM_BUFFER_LEN 32
#define IM_PREVIEW 1
#define IM_PREVIEW_ENTRIES 6
#define IM_PREVIEW_WIDTH (IM_TABLE_ENTRY_WIDTH+2)*IM_PREVIEW_ENTRIES
#define IM_ON_INDICATOR "CN"
#define IM_ON_INDICATOR_LEN 2
#define IM_OFF_INDICATOR "EN"
#define IM_OFF_INDICATOR_LEN 2
#define IM_INDICATOR_WIDTH 2
#include "boshiamy.h"
#define ENTER_KEY 0x0D
//Two different backspace keys are defined, to disable one set both macros to
//the one you desire
#define BACKSPACE_KEY 0x7F
#define BACKSPACE_KEY_2 0x08
//the literal escape char is only fed into the im if it is pressed twice, see
//escape.h
#define ESCAPE_KEY '`'
#define ESCAPE_KEY_2 0x1B
//Vertical Tab Ctrl+K
#define TOGGLE_KEY 0x0B
#define IM_KEY_RANGE_LEN 3
#define IM_SELECTOR_RANGE_LEN 2
//Array of Input method key ranges(inclusive) seperate from the above
char im_key_ranges[IM_KEY_RANGE_LEN][2] = {
{'a','z'},
{',',','},
{'.','.'},
};
//array of selector ranges, these keys never start a sequence
char im_selector_ranges[IM_SELECTOR_RANGE_LEN][2] = {
{'0','9'},
{' ',' '},
};
//if chars = 0, only return im_keys, otherwise iwll return true for im keys and
//selector keys
int is_im_key(char input, unsigned int chars) {
for (int i = 0; i < IM_KEY_RANGE_LEN; i++) {
if (input >= im_key_ranges[i][0] && input <= im_key_ranges[i][1]) {
return 1;
}
}
if (chars > 0) {
for (int j = 0; j < IM_SELECTOR_RANGE_LEN; j++) {
if (input >= im_selector_ranges[j][0] && input <= im_selector_ranges[j][1]) {
return 1;
}
}
}
return 0;
}
char im_buffer[IM_BUFFER_LEN+1];
unsigned int im_buffer_pos = 0;
int im_on = 1;
//clears the im buffer
void clear_im_buffer() {
im_buffer[0] = 0;
im_buffer_pos = 0;
}
//recursive search, returns -1 on failure
//searches from [start, end),
//can limit itself to n bytes, if n=0, use IM_TABLE_ENTRY_LEN
int im_search(char * input, int start, int end, int n) {
if (n == 0) {
n = IM_TABLE_ENTRY_LEN;
}
// 0 width search
if (end <= start) {
return -1;
}
//test for middle, works even for 1 width middle=start
int middle = start + (end-start)/2;
//printf("testing:%s\n",im_table[middle][0]);
int test = strncmp(im_table[middle][0], input, n);
if (test == 0) {
return middle;
}
//special case for width 1,2 region
if ((end - start) == 1) {
if (test == 0) {
return start;
} else {
return -1;
}
}
if (test == 0) {
return 0;
} else {
if (test > 0) {
return im_search(input,start,middle,n);
} else {
return im_search(input,middle,end,n);
}
}
}
//update_im_state is fed input characters 1 at a time, and gives 0 to
//IM_BUFFER_LEN output characters.
//returns output len
int update_im_state(char input, char * output) {
if (input == TOGGLE_KEY) {
im_on = !im_on;
//clears the im buffer if its toggling off
if (!im_on) {
clear_im_buffer();
}
//do not passthrough the toggle
return 0;
}
//just passthrough if the im is off
if (!im_on) {
output[0] = input;
output[1] = 0;
return 1;
}
if ( is_im_key(input, im_buffer_pos) ) {
//check if there's enough room in the im buffer
if ( (im_buffer_pos < IM_BUFFER_LEN) && (im_buffer_pos < IM_TABLE_ENTRY_LEN) ){
im_buffer[im_buffer_pos] = input;
im_buffer_pos++;
im_buffer[im_buffer_pos] = 0;
} else {
//passthrough
output[0] = input;
output[1] = 0;
return 1;
}
//if there's a match return it
int im_entry = im_search(im_buffer, 0, IM_TABLE_LEN, 0);
if (im_entry != -1) {
//clear the buffer
clear_im_buffer();
strncpy(output, im_table[im_entry][1], IM_TABLE_ENTRY_LEN);
return strlen(im_table[im_entry][1]);
} else {
return 0;
}
} else {
//these only do something if there's something in the buffer, otherwise
//passthrough
if (im_buffer_pos > 0) {
switch (input) {
//escape, cancels
case ESCAPE_KEY_2:
case ESCAPE_KEY:
clear_im_buffer();
return 0;
break;
//enter, enters the buffer instead of sending it
//to im
case ENTER_KEY:
strncpy(output, im_buffer, IM_BUFFER_LEN);
int tmp = im_buffer_pos;
clear_im_buffer();
return tmp;
break;
case BACKSPACE_KEY_2:
//backspace
case BACKSPACE_KEY:
if (im_buffer_pos > 0) {
im_buffer_pos--;
im_buffer[im_buffer_pos] = 0;
}
return 0;
break;
}
}
}
//default behavior of passthrough
output[0] = input;
output[1] = 0;
return 1;
}
//prints im preview, assumes im_disp already moved it to the proper location
void im_preview(int fd, int trows, int tcols) {
//searches for any im entries that match the im buffer, up to the
//im_buffer length, because it's branching search it's not guaranteed to
//be the first entry in the table that matches
int first_entry = im_search(im_buffer, 0, IM_TABLE_LEN, im_buffer_pos);
if (first_entry == -1) {
return;
}
//keeps iterating until it finds the first entry in the table that
//matches
for (int i = first_entry; i>0 && i < IM_TABLE_LEN && (!strncmp(im_table[i][0],im_buffer,im_buffer_pos)); i--) {
first_entry = i;
}
//preview buffer, each utf8 char could be up to 4 bytes, each new entry
//catted on could be IM_TABLE_ENTRY_LEN*2 before next detection in loop,
//so that + 1 margin for null byte
char preview_buffer[IM_PREVIEW_ENTRIES*IM_TABLE_ENTRY_LEN*2+1] = "";
//tracks how many entries we've added to the buffer so far
int k = 0;
for (int j = first_entry; j> 0 && j < IM_TABLE_LEN && (!strncmp(im_table[j][0],im_buffer,im_buffer_pos)); j++) {
if (k >= IM_PREVIEW_ENTRIES) {
break;
}
//even if the string is the same as im_buffer_pos, it will point
//to a null pointer, which is treated as empty
strncat(preview_buffer,&(im_table[j][0][im_buffer_pos]),1);
strncat(preview_buffer,":",1);
//assuming that each output is only 2 characters wide display
strncat(preview_buffer,im_table[j][1], IM_TABLE_ENTRY_LEN);
k++;
}
//writes the preview
write(fd,preview_buffer,strlen(preview_buffer));
}
void restrict_scroll(int fd, int trows, int tcols) {
char pos_str[32] = "";
int l = snprintf(pos_str, 32, "\033[0;%dr", trows-1);
write(fd, pos_str,l);
}
void unrestrict_scroll(int fd) {
write(fd,"\033[0;0r", 6);
}
//Displays the IM on the bottom line, input is the number of actual physical
//rows and columns on the terminal descibed by fd
void disp_im(int fd, int trows, int tcols) {
//generates the position escape sequence for the bottom left corner
//2 spaces for the im indicator, 2 for buffer brackets, +1 for automargin
int im_col = tcols - IM_TABLE_ENTRY_LEN - IM_INDICATOR_WIDTH - 2 - 1;
if (IM_PREVIEW) {
im_col = im_col - IM_PREVIEW_WIDTH;
}
char pos_str[32] = "";
int l = snprintf(pos_str, 32, "\033[%d;%df", trows, im_col);
write(fd,"\0337",2); //saves previous cursor position
unrestrict_scroll(fd); //unrestricts the scrolling region
write(fd, pos_str,l);
write(fd,"\033[7m",4); //invert video
//overwrite what was there previously, this works out to leaving 1 space
//at the end for automargin b/c you need (tcols-im_col)+1 spaces, but
//want -1 space so it doesn't write to the last character position
write(fd," ",
tcols-im_col);
write(fd, pos_str,l);
//im mode indicator
if (im_on) {
write(fd,IM_ON_INDICATOR,IM_ON_INDICATOR_LEN);
} else {
write(fd,IM_OFF_INDICATOR,IM_OFF_INDICATOR_LEN);
}
write(fd,"[",1);
//actual buffer
write(fd,im_buffer,im_buffer_pos);
write(fd,"]",1);
//if preview is set do that
if (IM_PREVIEW) {
im_preview(fd, trows, tcols);
}
//restricts scroll region again
restrict_scroll(fd, trows, tcols);
//resets scroll region
//restore cursor pos and attrbutes
write(fd,"\033[0m",4);
write(fd,"\0338",2);
}
|