summaryrefslogtreecommitdiff
path: root/im.h
blob: e3b2b6526e1c2fd9171dcd728eaba27919a3365a (plain)
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
//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
//setting it to the actual escape character interferes with arrow keys and
//whatnot
//#define ESCAPE_KEY 0x1B
#define ESCAPE_KEY '`'
//Vertical Tab Ctrl+K
#define TOGGLE_KEY 0x0B
#define IM_KEY_RANGE_LEN 5

//Array of Input method key ranges(inclusive) seperate from the above
char im_key_ranges[IM_KEY_RANGE_LEN][2] = {
	{'a','z'},
	{'0','9'},
	{' ',' '},
	{',',','},
	{'.','.'},
};
int is_im_key(char input) {
	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;
		}
	}
	return 0;
}
char im_buffer[IM_BUFFER_LEN+1];
char 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) ) {
		//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:
					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));
}
//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
	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);
	}
	//restore cursor pos and attrbutes
	write(fd,"\033[0m",4);
	write(fd,"\0338",2);
}