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