1 /* -*- c-basic-offset:2; tab-width:2; indent-tabs-mode:nil -*- */
2
3 #include "vt_line_bidi.h"
4
5 #include <string.h> /* memset */
6 #include <pobl/bl_debug.h>
7 #include <pobl/bl_mem.h> /* alloca */
8
9 #define MSB32 0x80000000
10
11 /* --- static functions --- */
12
copy_char_with_mirror_check(vt_char_t * dst,vt_char_t * src,u_int16_t * visual_order,u_int16_t visual_order_size,int pos)13 static void copy_char_with_mirror_check(vt_char_t *dst, vt_char_t *src, u_int16_t *visual_order,
14 u_int16_t visual_order_size, int pos) {
15 vt_char_copy(dst, src);
16
17 if (((pos > 0 && visual_order[pos - 1] == visual_order[pos] + 1) ||
18 (pos + 1 < visual_order_size && visual_order[pos] == visual_order[pos + 1] + 1))) {
19 /*
20 * Pos is RTL character.
21 * => XXX It is assumed that pos is always US_ASCII or ISO10646_UCS4_1.
22 */
23 #if 0
24 ef_charset_t cs;
25
26 if ((cs = vt_char_cs(dst)) == US_ASCII || cs == ISO10646_UCS4_1)
27 #endif
28 {
29 u_int mirror;
30
31 if ((mirror = vt_bidi_get_mirror_char(vt_char_code(dst)))) {
32 vt_char_set_code(dst, mirror);
33 }
34 }
35 }
36 }
37
set_visual_modified(vt_line_t * line,int logical_mod_beg,int logical_mod_end)38 static void set_visual_modified(vt_line_t *line, int logical_mod_beg, int logical_mod_end) {
39 int log_pos;
40 int visual_mod_beg;
41 int visual_mod_end;
42
43 /* same as 0 <= char_index < size */
44 if (((u_int)logical_mod_beg) >= line->ctl_info.bidi->size ||
45 ((u_int)logical_mod_end) >= line->ctl_info.bidi->size) {
46 vt_line_set_modified_all(line);
47
48 return;
49 }
50
51 visual_mod_beg = vt_line_end_char_index(line);
52 visual_mod_end = 0;
53 for (log_pos = logical_mod_beg; log_pos <= logical_mod_end; log_pos++) {
54 int vis_pos = line->ctl_info.bidi->visual_order[log_pos];
55
56 if (vis_pos < visual_mod_beg) {
57 visual_mod_beg = vis_pos;
58 }
59 if (vis_pos > visual_mod_end) {
60 visual_mod_end = vis_pos;
61 }
62 }
63
64 #if 0
65 bl_debug_printf("%p %d %d -> %d %d\n", line, logical_mod_beg, logical_mod_end, visual_mod_beg,
66 visual_mod_end);
67 #endif
68
69 vt_line_set_updated(line);
70 vt_line_set_modified(line, visual_mod_beg, visual_mod_end);
71 }
72
73 /* --- global functions --- */
74
vt_line_set_use_bidi(vt_line_t * line,int flag)75 int vt_line_set_use_bidi(vt_line_t *line, int flag) {
76 if (flag) {
77 if (vt_line_is_using_bidi(line)) {
78 return 1;
79 } else if (line->ctl_info_type != 0) {
80 return 0;
81 }
82
83 if ((line->ctl_info.bidi = vt_bidi_new()) == NULL) {
84 return 0;
85 }
86
87 line->ctl_info_type = VINFO_BIDI;
88 } else {
89 if (vt_line_is_using_bidi(line)) {
90 vt_bidi_destroy(line->ctl_info.bidi);
91 line->ctl_info_type = 0;
92 }
93 }
94
95 return 1;
96 }
97
98 /* The caller should check vt_line_is_using_bidi() in advance. */
vt_line_bidi_render(vt_line_t * line,vt_bidi_mode_t bidi_mode,const char * separators)99 int vt_line_bidi_render(vt_line_t *line, /* is always modified */
100 vt_bidi_mode_t bidi_mode, const char *separators) {
101 int ret;
102
103 if (vt_line_is_real_modified(line)) {
104 int base_was_rtl;
105
106 base_was_rtl = BASE_IS_RTL(line->ctl_info.bidi);
107
108 if ((ret = vt_bidi(line->ctl_info.bidi, line->chars, line->num_filled_chars, bidi_mode,
109 separators)) <= 0) {
110 if (base_was_rtl) {
111 /* shifting RTL-base to LTR-base (which requires redrawing line all) */
112 vt_line_set_modified_all(line);
113 }
114
115 return ret;
116 }
117
118 /* Conforming line->change_{beg|end}_col to visual mode. */
119 if (base_was_rtl != BASE_IS_RTL(line->ctl_info.bidi)) {
120 /*
121 * shifting RTL-base to LTR-base or LTR-base to RTL-base.
122 * (which requires redrawing line all)
123 */
124 vt_line_set_modified_all(line);
125
126 return 1;
127 }
128 } else {
129 ret = 1; /* order is not changed */
130 }
131
132 if (ret == 2) {
133 /* order is changed => force to redraw all */
134 if (vt_line_get_end_of_modified(line) > vt_line_end_char_index(line)) {
135 vt_line_set_modified_all(line);
136 } else {
137 vt_line_set_modified(line, 0, vt_line_end_char_index(line));
138 }
139 } else if (HAS_RTL(line->ctl_info.bidi)) {
140 set_visual_modified(line, vt_line_get_beg_of_modified(line), vt_line_get_end_of_modified(line));
141 }
142
143 return 1;
144 }
145
146 /* The caller should check vt_line_is_using_bidi() in advance. */
vt_line_bidi_visual(vt_line_t * line)147 int vt_line_bidi_visual(vt_line_t *line) {
148 int count;
149 vt_char_t *src;
150
151 if (line->ctl_info.bidi->size == 0 || !HAS_RTL(line->ctl_info.bidi)) {
152 #ifdef __DEBUG
153 bl_debug_printf(BL_DEBUG_TAG " Not need to visualize.\n");
154 #endif
155
156 return 1;
157 }
158
159 if ((src = vt_str_alloca(line->ctl_info.bidi->size)) == NULL) {
160 return 0;
161 }
162 vt_str_init(src, line->ctl_info.bidi->size);
163 vt_str_copy(src, line->chars, line->ctl_info.bidi->size);
164
165 for (count = 0; count < line->ctl_info.bidi->size; count++) {
166 copy_char_with_mirror_check(line->chars + line->ctl_info.bidi->visual_order[count], src + count,
167 line->ctl_info.bidi->visual_order, line->ctl_info.bidi->size,
168 count);
169 }
170
171 vt_str_final(src, line->ctl_info.bidi->size);
172
173 return 1;
174 }
175
176 /* The caller should check vt_line_is_using_bidi() in advance. */
vt_line_bidi_logical(vt_line_t * line)177 int vt_line_bidi_logical(vt_line_t *line) {
178 int count;
179 vt_char_t *src;
180
181 if (line->ctl_info.bidi->size == 0 || !HAS_RTL(line->ctl_info.bidi)) {
182 #ifdef __DEBUG
183 bl_debug_printf(BL_DEBUG_TAG " Not need to logicalize.\n");
184 #endif
185
186 return 0;
187 }
188
189 if ((src = vt_str_alloca(line->ctl_info.bidi->size)) == NULL) {
190 return 0;
191 }
192 vt_str_init(src, line->ctl_info.bidi->size);
193 vt_str_copy(src, line->chars, line->ctl_info.bidi->size);
194
195 for (count = 0; count < line->ctl_info.bidi->size; count++) {
196 copy_char_with_mirror_check(line->chars + count, src + line->ctl_info.bidi->visual_order[count],
197 line->ctl_info.bidi->visual_order, line->ctl_info.bidi->size,
198 count);
199 }
200
201 vt_str_final(src, line->ctl_info.bidi->size);
202
203 /*
204 * !! Notice !!
205 * is_modified is as it is , which should not be touched here.
206 */
207
208 return 1;
209 }
210
211 /* The caller should check vt_line_is_using_bidi() in advance. */
vt_line_bidi_convert_logical_char_index_to_visual(vt_line_t * line,int char_index,u_int32_t * meet_pos_info)212 int vt_line_bidi_convert_logical_char_index_to_visual(vt_line_t *line, int char_index,
213 u_int32_t *meet_pos_info) {
214 if (((u_int)char_index) < line->ctl_info.bidi->size && /* same as 0 <= char_index < size */
215 HAS_RTL(line->ctl_info.bidi)) {
216 if (meet_pos_info) {
217 int count;
218
219 *meet_pos_info &= ~MSB32;
220
221 if (!BASE_IS_RTL(line->ctl_info.bidi) && char_index >= 1) {
222 for (count = char_index - 2; count >= -1; count--) {
223 /*
224 * visual order -> 1 2 4 3 5
225 * ^ ^ ^- char index
226 * | |
227 * cursor position --+ +-- meet position
228 *
229 * visual order -> 1 2*5*4 3 6
230 * ^ ^ ^ ^- char index
231 * | |
232 * cursor position --+ +-- meet position
233 *
234 * visual order -> 1 2 3 6 5 4 7
235 * ^ ^ ^ ^- char index
236 * | |
237 * cursor position --+ +-- meet position
238 */
239 #if 0
240 bl_debug_printf(" Normal pos %d - Current pos %d %d %d - Meet position info %d\n",
241 line->ctl_info.bidi->visual_order[char_index],
242 count >= 0 ? line->ctl_info.bidi->visual_order[count] : 0,
243 line->ctl_info.bidi->visual_order[count + 1],
244 line->ctl_info.bidi->visual_order[count + 2], *meet_pos_info);
245 #endif
246 if ((count < 0 ||
247 line->ctl_info.bidi->visual_order[count] <
248 line->ctl_info.bidi->visual_order[count + 1]) &&
249 line->ctl_info.bidi->visual_order[count + 1] + 1 <
250 line->ctl_info.bidi->visual_order[count + 2]) {
251 /*
252 * If meet position is not changed, text isn't changed
253 * but cursor is moved. In this case cursor position should
254 * not be fixed to visual_order[count + 1].
255 */
256 if (((*meet_pos_info) & ~MSB32) !=
257 line->ctl_info.bidi->visual_order[count + 1] +
258 line->ctl_info.bidi->visual_order[count + 2]) {
259 *meet_pos_info = line->ctl_info.bidi->visual_order[count + 1] +
260 line->ctl_info.bidi->visual_order[count + 2];
261
262 if (line->ctl_info.bidi->visual_order[char_index] ==
263 line->ctl_info.bidi->visual_order[count + 2] + 1) {
264 *meet_pos_info |= MSB32;
265 return line->ctl_info.bidi->visual_order[count + 1];
266 }
267 }
268
269 break;
270 }
271 }
272
273 if (count == 0) {
274 *meet_pos_info = 0;
275 }
276 } else if (BASE_IS_RTL(line->ctl_info.bidi) && char_index >= 1) {
277 for (count = char_index - 2; count >= -1; count--) {
278 /*
279 * visual order -> 6 5 4 2 3 1
280 * ^ ^ ^ ^- char index
281 * |
282 * +-- meet position & cursor position
283 * visual order -> 7 6 5 2 3*4*1
284 * ^ ^ ^ ^- char index
285 * |
286 * +-- meet position & cursor position
287 *
288 * visual order -> 7 6 4 5 3 2 1
289 * ^ ^ ^- char index
290 * | |
291 * cursor position --+ +-- meet position
292 * visual order -> 7 6 3 4*5*2 1
293 * ^ ^ ^- char index
294 * | |
295 * cursor position --+ +-- meet position
296 */
297 #if 0
298 bl_debug_printf(" Normal pos %d - Current pos %d %d %d - Meet position info %d\n",
299 line->ctl_info.bidi->visual_order[char_index],
300 count >= 0 ? line->ctl_info.bidi->visual_order[count] : 0,
301 line->ctl_info.bidi->visual_order[count + 1],
302 line->ctl_info.bidi->visual_order[count + 2], *meet_pos_info);
303 #endif
304
305 if ((count < 0 ||
306 line->ctl_info.bidi->visual_order[count] >
307 line->ctl_info.bidi->visual_order[count + 1]) &&
308 line->ctl_info.bidi->visual_order[count + 1] >
309 line->ctl_info.bidi->visual_order[count + 2] + 1) {
310 /*
311 * If meet position is not changed, text isn't changed
312 * but cursor is moved. In this case cursor position should
313 * not be fixed to visual_order[count + 1].
314 */
315 if (((*meet_pos_info) & ~MSB32) !=
316 line->ctl_info.bidi->visual_order[count + 1] +
317 line->ctl_info.bidi->visual_order[count + 2]) {
318 *meet_pos_info = line->ctl_info.bidi->visual_order[count + 1] +
319 line->ctl_info.bidi->visual_order[count + 2];
320
321 if (line->ctl_info.bidi->visual_order[char_index] + 1 ==
322 line->ctl_info.bidi->visual_order[count + 2]) {
323 *meet_pos_info |= MSB32;
324 return line->ctl_info.bidi->visual_order[count + 1];
325 }
326 }
327
328 break;
329 }
330 }
331
332 if (count == 0) {
333 *meet_pos_info = 0;
334 }
335 } else {
336 *meet_pos_info = 0;
337 }
338 }
339
340 return line->ctl_info.bidi->visual_order[char_index];
341 } else {
342 if (meet_pos_info) {
343 *meet_pos_info = 0;
344 }
345
346 return char_index;
347 }
348 }
349
350 /*
351 * This function is used only by a loader of this module (not used inside this
352 * module),
353 * so it is assumed that vt_line_is_using_bidi() was already checked (otherwise
354 * this
355 * module can be loaded unnecessarily).
356 */
vt_line_bidi_convert_visual_char_index_to_logical(vt_line_t * line,int char_index)357 int vt_line_bidi_convert_visual_char_index_to_logical(vt_line_t *line, int char_index) {
358 u_int count;
359
360 for (count = 0; count < line->ctl_info.bidi->size; count++) {
361 if (line->ctl_info.bidi->visual_order[count] == char_index) {
362 return count;
363 }
364 }
365
366 return char_index;
367 }
368
369 /*
370 * This function is used only by a loader of this module (not used inside this
371 * module),
372 * so it is assumed that vt_line_is_using_bidi() was already checked (otherwise
373 * this
374 * module can be loaded unnecessarily).
375 */
vt_line_bidi_is_rtl(vt_line_t * line)376 int vt_line_bidi_is_rtl(vt_line_t *line) { return BASE_IS_RTL(line->ctl_info.bidi); }
377
vt_line_bidi_need_shape(vt_line_t * line)378 int vt_line_bidi_need_shape(vt_line_t *line) { return HAS_RTL(line->ctl_info.bidi); }
379
380 /*
381 * This function is used only by a loader of this module (not used inside this
382 * module),
383 * so it is assumed that vt_line_is_using_bidi() was already checked.
384 */
vt_line_bidi_copy_logical_str(vt_line_t * line,vt_char_t * dst,int beg,u_int len)385 int vt_line_bidi_copy_logical_str(vt_line_t *line, vt_char_t *dst, int beg, /* visual position */
386 u_int len) {
387 /*
388 * XXX
389 * adhoc implementation.
390 */
391
392 int *flags;
393 int bidi_pos;
394 int norm_pos;
395 int dst_pos;
396
397 #ifdef DEBUG
398 if (((int)len) < 0) {
399 abort();
400 }
401 #endif
402
403 if (line->ctl_info.bidi->size == 0) {
404 return 0;
405 }
406
407 if ((flags = alloca(sizeof(int) * line->ctl_info.bidi->size)) == NULL) {
408 return 0;
409 }
410
411 memset(flags, 0, sizeof(int) * line->ctl_info.bidi->size);
412
413 for (bidi_pos = beg; bidi_pos < beg + len; bidi_pos++) {
414 for (norm_pos = 0; norm_pos < line->ctl_info.bidi->size; norm_pos++) {
415 if (line->ctl_info.bidi->visual_order[norm_pos] == bidi_pos) {
416 flags[norm_pos] = 1;
417 }
418 }
419 }
420
421 for (dst_pos = norm_pos = 0; norm_pos < line->ctl_info.bidi->size; norm_pos++) {
422 if (flags[norm_pos]) {
423 copy_char_with_mirror_check(
424 &dst[dst_pos++], line->chars + line->ctl_info.bidi->visual_order[norm_pos],
425 line->ctl_info.bidi->visual_order, line->ctl_info.bidi->size, norm_pos);
426 }
427 }
428
429 return 1;
430 }
431