~kris/suckless

dmenu

ref: a5b5760f9b34518858a787463fe51308aa90d23f dmenu/patch/inputmethod.c -rw-r--r-- 5.0 KiB
a5b5760f — Kris Yotam chore: sync local state after restore (push updates, no pull) a month ago
                                                                                
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
static size_t nextrunetext(const char *text, size_t position, int inc)
{
	ssize_t n;

	/* return location of next utf8 rune in the given direction (+1 or -1) */
	for (n = position + inc; n + inc >= 0 && (text[n] & 0xc0) == 0x80; n += inc)
		;
	return n;
}

/* return bytes from beginning of text to nth utf8 rune to the right */
static size_t runebytes(const char *text, size_t n)
{
	size_t ret;

	ret = 0;
	while (n-- > 0)
		ret += nextrunetext(text + ret, 0, 1);
	return ret;
}

/* return number of characters from beginning of text to nth byte to the right
 */
static size_t runechars(const char *text, size_t n)
{
	size_t ret, i;

	ret = i = 0;
	while (i < n) {
		i += nextrunetext(text + i, 0, 1);
		ret++;
	}
	return ret;
}

/* move caret on pre-edit text */
static void preeditcaret(XIC xic, XPointer clientdata, XPointer calldata)
{
	XIMPreeditCaretCallbackStruct *pcaret;

	(void)xic;
	pcaret = (XIMPreeditCaretCallbackStruct *)calldata;
	if (!pcaret)
		return;
	switch (pcaret->direction) {
	case XIMForwardChar:
		cursor = nextrune(+1);
		break;
	case XIMBackwardChar:
		cursor = nextrune(-1);
		break;
	case XIMForwardWord:
		movewordedge(+1);
		break;
	case XIMBackwardWord:
		movewordedge(-1);
		break;
	case XIMLineStart:
		cursor = 0;
		break;
	case XIMLineEnd:
		if (preview[cursor] != '\0')
			cursor = strlen(preview);
		break;
	case XIMAbsolutePosition:
		cursor = runebytes(text, pcaret->position);
		break;
	case XIMDontChange:
		/* do nothing */
		break;
	case XIMCaretUp:
	case XIMCaretDown:
	case XIMNextLine:
	case XIMPreviousLine:
		/* not implemented */
		break;
	}
	pcaret->position = runechars(preview, cursor);
}

/* start input method pre-editing */
static int preeditstart(XIC xic, XPointer clientdata, XPointer calldata)
{
	(void)xic;
	(void)calldata;
	(void)clientdata;
	composing = 1;
	printf("PREEDIT\n");
	return BUFSIZ;
}

/* end input method pre-editing */
static void preeditdone(XIC xic, XPointer clientdata, XPointer calldata)
{
	(void)xic;
	(void)clientdata;
	(void)calldata;
	printf("DONE\n");

	composing = 0;
}

/* draw input method pre-edit text */
static void preeditdraw(XIC xic, XPointer clientdata, XPointer calldata)
{
	XIMPreeditDrawCallbackStruct *pdraw;
	size_t beg, dellen, inslen, endlen;

	printf("DRAW\n");

	(void)xic;
	pdraw = (XIMPreeditDrawCallbackStruct *)calldata;
	if (!pdraw)
		return;

	/* we do not support wide characters */
	if (pdraw->text && pdraw->text->encoding_is_wchar == True) {
		fputs("warning: wchar is not supportecd; use utf8", stderr);
		return;
	}

	beg = runebytes(text, pdraw->chg_first);
	dellen = runebytes(preview + beg, pdraw->chg_length);
	inslen = pdraw->text ? runebytes(pdraw->text->string.multi_byte, pdraw->text->length) : 0;
	endlen = 0;
	if (beg + dellen < strlen(preview))
		endlen = strlen(preview + beg + dellen);

	/* we cannot change text past the end of our pre-edit string */

	if (beg + dellen >= BUFSIZ || beg + inslen >= BUFSIZ)
		return;

	/* get space for text to be copied, and copy it */
	memmove(preview + beg + inslen, preview + beg + dellen, endlen + 1);
	if (pdraw->text && pdraw->text->length)
		memcpy(preview + beg, pdraw->text->string.multi_byte, inslen);
	(preview + beg + inslen + endlen)[0] = '\0';

	/* get caret position */
	cursor = runebytes(text, pdraw->caret);
}

static void init_input_method(XIM xim)
{
	XVaNestedList preedit = NULL;
	XICCallback start, done, draw, caret;
	XIMStyle preeditstyle;
	XIMStyle statusstyle;
	XIMStyles *imstyles;
	int i;

	/* get styles supported by input method */
	if (XGetIMValues(xim, XNQueryInputStyle, &imstyles, NULL) != NULL)
		fputs("XGetIMValues: could not obtain input method values", stderr);

	/* check whether input method support on-the-spot pre-editing */
	preeditstyle = XIMPreeditNothing;
	statusstyle = XIMStatusNothing;
	for (i = 0; i < imstyles->count_styles; i++) {
		if (imstyles->supported_styles[i] & XIMPreeditCallbacks) {
			preeditstyle = XIMPreeditCallbacks;
			break;
		}
	}

	/* create callbacks for the input context */
	start.client_data = NULL;
	done.client_data = NULL;
	draw.client_data = (XPointer)text;
	caret.client_data = (XPointer)text;
	start.callback = (XICProc)preeditstart;
	done.callback = (XICProc)preeditdone;
	draw.callback = (XICProc)preeditdraw;
	caret.callback = (XICProc)preeditcaret;

	/* create list of values for input context */
	preedit = XVaCreateNestedList(0, XNPreeditStartCallback, &start, XNPreeditDoneCallback,
				      &done, XNPreeditDrawCallback, &draw, XNPreeditCaretCallback,
				      &caret, NULL);
	if (preedit == NULL)
		fputs("XVaCreateNestedList: could not create nested list", stderr);

	xic = XCreateIC(xim, XNInputStyle, preeditstyle | statusstyle, XNPreeditAttributes, preedit,
			XNClientWindow, win, XNFocusWindow, win, NULL);
	XFree(preedit);

	long eventmask;
	/* get events the input method is interested in */
	if (XGetICValues(xic, XNFilterEvents, &eventmask, NULL))
		fputs("XGetICValues: could not obtain input context values", stderr);

	XSelectInput(dpy, win,
		     ExposureMask | KeyPressMask | VisibilityChangeMask | ButtonPressMask |
			     PointerMotionMask | eventmask);
}