1 /* -*- c-basic-offset:2; tab-width:2; indent-tabs-mode:nil -*- */
2 
3 #include "vt_term.h"
4 
5 #include <pobl/bl_mem.h> /* malloc/free */
6 #include <pobl/bl_debug.h>
7 #include <pobl/bl_str.h> /* strdup */
8 #include <pobl/bl_sig_child.h>
9 #include <pobl/bl_path.h> /* bl_parse_uri */
10 #include <pobl/bl_dialog.h>
11 
12 #include "vt_pty.h"
13 #include "vt_parser.h"
14 #include "vt_screen.h"
15 
16 #ifdef OPEN_PTY_ASYNC
17 
18 #ifdef USE_WIN32API
19 #include <windows.h>
20 #include <process.h> /* _beginthreadex */
21 #else
22 #include <pthread.h>
23 #endif
24 
25 typedef struct {
26   vt_term_t *term;
27   char *cmd_path;
28   char **argv;
29   char **env;
30   char *host;
31   char *work_dir;
32   char *pass;
33   char *pubkey;
34   char *privkey;
35   u_int width_pix;
36   u_int height_pix;
37 
38 } pty_args_t;
39 
40 #endif
41 
42 /* --- global variables --- */
43 
44 #ifndef NO_IMAGE
45 /* XXX */
46 void (*vt_term_pty_closed_event)(vt_term_t *);
47 #endif
48 
49 #if defined(__ANDROID__) && defined(USE_LIBSSH2)
50 /* XXX */
51 int start_with_local_pty = 0;
52 #endif
53 
54 /* --- static functions --- */
55 
56 #ifdef OPEN_PTY_ASYNC
57 
pty_args_destroy(pty_args_t * args)58 static void pty_args_destroy(pty_args_t *args) {
59   int count;
60 
61   free(args->cmd_path);
62   free(args->host);
63   free(args->work_dir);
64   free(args->pass);
65   free(args->pubkey);
66   free(args->privkey);
67 
68   if (args->argv) {
69     for (count = 0; args->argv[count]; count++) {
70       free(args->argv[count]);
71     }
72     free(args->argv);
73   }
74 
75   if (args->env) {
76     for (count = 0; args->env[count]; count++) {
77       free(args->env[count]);
78     }
79     free(args->env);
80   }
81 
82   free(args);
83 }
84 
pty_args_new(vt_term_t * term,const char * cmd_path,char ** argv,char ** env,const char * host,const char * work_dir,const char * pass,const char * pubkey,const char * privkey,u_int width_pix,u_int height_pix)85 static pty_args_t *pty_args_new(vt_term_t *term, const char *cmd_path, char **argv, char **env,
86                                 const char *host, const char *work_dir, const char *pass,
87                                 const char *pubkey, const char *privkey, u_int width_pix,
88                                 u_int height_pix) {
89   pty_args_t *args;
90   u_int num;
91   u_int count;
92 
93   if (!(args = calloc(1, sizeof(pty_args_t)))) {
94     return NULL;
95   }
96 
97   args->term = term;
98 
99   if (cmd_path) {
100     args->cmd_path = strdup(cmd_path);
101   }
102 
103   if (host) {
104     args->host = strdup(host);
105   }
106 
107   if (work_dir) {
108     args->work_dir = strdup(work_dir);
109   }
110 
111   if (pass) {
112     args->pass = strdup(pass);
113   }
114 
115   if (pubkey) {
116     args->pubkey = strdup(pubkey);
117   }
118 
119   if (privkey) {
120     args->privkey = strdup(privkey);
121   }
122 
123   args->width_pix = width_pix;
124   args->height_pix = height_pix;
125 
126   if (argv) {
127     for (num = 0; argv[num]; num++)
128       ;
129 
130     if ((args->argv = malloc(sizeof(char *) * (num + 1)))) {
131       for (count = 0; count < num; count++) {
132         args->argv[count] = strdup(argv[count]);
133       }
134       args->argv[count] = NULL;
135     }
136   } else {
137     args->argv = NULL;
138   }
139 
140   if (env) {
141     for (num = 0; env[num]; num++)
142       ;
143 
144     if ((args->env = malloc(sizeof(char *) * (num + 1)))) {
145       for (count = 0; count < num; count++) {
146         args->env[count] = strdup(env[count]);
147       }
148       args->env[count] = NULL;
149     }
150   } else {
151     args->env = NULL;
152   }
153 
154   return args;
155 }
156 
157 #ifdef USE_WIN32API
158 static u_int __stdcall
159 #else
160 static void *
161 #endif
open_pty(void * p)162     open_pty(void *p) {
163   pty_args_t *args;
164   vt_pty_t *pty;
165 #ifdef USE_WIN32API
166   static HANDLE mutex;
167 
168   if (!mutex) {
169     mutex = CreateMutex(NULL, FALSE, NULL);
170   }
171 
172   WaitForSingleObject(mutex, INFINITE);
173 #else
174   static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
175 
176   pthread_detach(pthread_self());
177   pthread_mutex_lock(&mutex);
178 #endif
179 
180   args = p;
181 
182   pty =
183       vt_pty_new(args->cmd_path, args->argv, args->env, args->host, args->work_dir, args->pass,
184                  args->pubkey, args->privkey, vt_screen_get_logical_cols(args->term->screen),
185                  vt_screen_get_logical_rows(args->term->screen), args->width_pix, args->height_pix);
186 
187   if (pty) {
188     if (args->pass) {
189       args->term->uri = strdup(args->host);
190     }
191 
192     vt_term_plug_pty(args->term, pty);
193   } else {
194     bl_dialog(BL_DIALOG_ALERT, "Failed to open pty");
195 
196     args->term->return_special_pid = 1;
197     bl_trigger_sig_child(-10);
198     args->term->return_special_pid = 0;
199   }
200 
201   pty_args_destroy(args);
202 
203 #ifdef USE_WIN32API
204   ReleaseMutex(mutex);
205 #else
206   pthread_mutex_unlock(&mutex);
207 #endif
208 
209   return 0;
210 }
211 
212 #endif
213 
214 /* --- global functions --- */
215 
vt_term_final(void)216 void vt_term_final(void) {
217   vt_parser_final();
218   vt_termcap_final();
219 }
220 
vt_term_new(const char * term_type,u_int cols,u_int rows,u_int tab_size,u_int log_size,vt_char_encoding_t encoding,int is_auto_encoding,int use_auto_detect,int logging_vt_seq,vt_unicode_policy_t policy,u_int col_size_a,int use_char_combining,int use_multi_col_char,int use_ctl,vt_bidi_mode_t bidi_mode,const char * bidi_separators,int use_dynamic_comb,vt_bs_mode_t bs_mode,vt_vertical_mode_t vertical_mode,int use_local_echo,const char * win_name,const char * icon_name,int use_ansi_colors,vt_alt_color_mode_t alt_color_mode,int use_ot_layout,vt_cursor_style_t cursor_style,int ignore_broadcasted_chars)221 vt_term_t *vt_term_new(const char *term_type, u_int cols, u_int rows, u_int tab_size,
222                        u_int log_size, vt_char_encoding_t encoding, int is_auto_encoding,
223                        int use_auto_detect, int logging_vt_seq, vt_unicode_policy_t policy,
224                        u_int col_size_a, int use_char_combining, int use_multi_col_char,
225                        int use_ctl, vt_bidi_mode_t bidi_mode, const char *bidi_separators,
226                        int use_dynamic_comb, vt_bs_mode_t bs_mode, vt_vertical_mode_t vertical_mode,
227                        int use_local_echo, const char *win_name, const char *icon_name,
228                        int use_ansi_colors, vt_alt_color_mode_t alt_color_mode, int use_ot_layout,
229                        vt_cursor_style_t cursor_style, int ignore_broadcasted_chars) {
230   vt_termcap_ptr_t termcap;
231   vt_term_t *term;
232 
233   if (!(termcap = vt_termcap_get(term_type))) {
234     return NULL;
235   }
236 
237   if ((term = calloc(1, sizeof(vt_term_t))) == NULL) {
238 #ifdef DEBUG
239     bl_warn_printf(BL_DEBUG_TAG " malloc failed.\n");
240 #endif
241 
242     return NULL;
243   }
244 
245   if (!(term->screen = vt_screen_new(cols, rows, tab_size, log_size,
246                                      vt_termcap_bce_is_enabled(termcap), bs_mode))) {
247 #ifdef DEBUG
248     bl_warn_printf(BL_DEBUG_TAG " vt_screen_new failed.\n");
249 #endif
250 
251     goto error;
252   }
253 
254   term->use_ot_layout = use_ot_layout;
255 
256 #ifndef NOT_CONVERT_TO_ISCII
257 #if defined(USE_HARFBUZZ) || defined(USE_UNISCRIBE)
258   if (!term->use_ot_layout)
259 #endif
260   {
261     policy |= CONVERT_UNICODE_TO_ISCII;
262   }
263 #endif
264 
265   if (!(term->parser = vt_parser_new(term->screen, termcap, encoding, is_auto_encoding,
266                                      use_auto_detect, logging_vt_seq, policy, col_size_a,
267                                      use_char_combining, use_multi_col_char, win_name, icon_name,
268                                      use_ansi_colors, alt_color_mode, cursor_style,
269                                      ignore_broadcasted_chars, use_local_echo))) {
270 #ifdef DEBUG
271     bl_warn_printf(BL_DEBUG_TAG " vt_parser_new failed.\n");
272 #endif
273 
274     goto error;
275   }
276 
277   if (bidi_separators) {
278     term->bidi_separators = bl_str_unescape(bidi_separators);
279   }
280 
281   term->vertical_mode = vertical_mode;
282   term->bidi_mode = bidi_mode;
283   term->use_ctl = use_ctl;
284   term->use_dynamic_comb = use_dynamic_comb;
285 
286   return term;
287 
288 error:
289   if (term->screen) {
290     vt_screen_destroy(term->screen);
291   }
292 
293   if (term->parser) {
294     vt_parser_destroy(term->parser);
295   }
296 
297   free(term);
298 
299   return NULL;
300 }
301 
vt_term_destroy(vt_term_t * term)302 void vt_term_destroy(vt_term_t *term) {
303 #ifndef NO_IMAGE
304   if (vt_term_pty_closed_event) {
305     (*vt_term_pty_closed_event)(term);
306   }
307 #endif
308 
309   free(term->user_data);
310 
311   if (term->pty) {
312     vt_pty_destroy(term->pty);
313   } else if (term->pty_listener) {
314     (*term->pty_listener->closed)(term->pty_listener->self);
315   }
316 
317   free(term->uri);
318   free(term->icon_path);
319   free(term->bidi_separators);
320 
321   vt_screen_destroy(term->screen);
322   vt_parser_destroy(term->parser);
323 
324   free(term);
325 }
326 
vt_term_zombie(vt_term_t * term)327 void vt_term_zombie(vt_term_t *term) {
328   if (term->pty) {
329     vt_pty_t *pty;
330 
331     pty = term->pty;
332 
333     /* Should be NULL because vt_pty_destroy calls term->pty_listener->closed. */
334     term->pty = NULL;
335 
336     vt_pty_destroy(pty);
337   }
338 #ifdef DEBUG
339   else {
340     bl_debug_printf(BL_DEBUG_TAG " term is already zombie.\n");
341   }
342 #endif
343 }
344 
345 /* The caller should swap width_pix and height_pix in vertical mode. */
vt_term_open_pty(vt_term_t * term,const char * cmd_path,char ** argv,char ** env,const char * host,const char * work_dir,const char * pass,const char * pubkey,const char * privkey,u_int width_pix,u_int height_pix)346 int vt_term_open_pty(vt_term_t *term, const char *cmd_path, char **argv, char **env,
347                      const char *host, const char *work_dir, const char *pass, const char *pubkey,
348                      const char *privkey, u_int width_pix, u_int height_pix) {
349   if (!term->pty) {
350 #ifdef OPEN_PTY_ASYNC
351     char *host_dup;
352     char *user;
353     char *server;
354     char *port;
355 
356     if (pass && (host_dup = alloca(strlen(host) + 1)) &&
357         bl_parse_uri(NULL, &user, &server, &port, NULL, NULL, strcpy(host_dup, host))
358 #ifdef USE_LIBSSH2
359         && !vt_search_ssh_session(server, port, user)
360 #endif
361         ) {
362       pty_args_t *args;
363 
364       if (!(args = pty_args_new(term, cmd_path, argv, env, host, work_dir, pass, pubkey, privkey,
365                                 width_pix, height_pix))) {
366         return 0;
367       }
368 
369 #ifdef USE_WIN32API
370       {
371         HANDLE thrd;
372         u_int tid;
373 
374         if ((thrd = _beginthreadex(NULL, 0, open_pty, args, 0, &tid))) {
375           CloseHandle(thrd);
376 
377           return 1;
378         }
379 
380         return 0;
381       }
382 #else
383       {
384         pthread_t thrd;
385 
386         if (pthread_create(&thrd, NULL, open_pty, args) == 0) {
387           return 1;
388         } else {
389           return 0;
390         }
391       }
392 #endif
393     } else
394 #endif /* OPEN_PTY_ASYNC */
395     {
396       vt_pty_t *pty;
397 
398       if (!(pty = vt_pty_new(cmd_path, argv, env, host, work_dir, pass, pubkey, privkey,
399                              vt_screen_get_logical_cols(term->screen),
400                              vt_screen_get_logical_rows(term->screen), width_pix, height_pix))) {
401         bl_dialog(BL_DIALOG_ALERT, "Failed to open pty");
402 
403         return 0;
404       }
405 
406       if (pass) {
407         term->uri = strdup(host);
408       }
409 
410       vt_term_plug_pty(term, pty);
411     }
412   }
413 
414   return 1;
415 }
416 
vt_term_plug_pty(vt_term_t * term,vt_pty_t * pty)417 int vt_term_plug_pty(vt_term_t *term, vt_pty_t *pty /* Not NULL */) {
418   if (!term->pty) {
419     if (term->pty_listener) {
420       vt_pty_set_listener(pty, term->pty_listener);
421       term->pty_listener = NULL;
422     }
423 
424     vt_parser_set_pty(term->parser, pty);
425 
426     term->pty = pty;
427   }
428 
429   return 1;
430 }
431 
vt_term_attach(vt_term_t * term,vt_xterm_event_listener_t * xterm_listener,vt_config_event_listener_t * config_listener,vt_screen_event_listener_t * screen_listener,vt_pty_event_listener_t * pty_listener)432 int vt_term_attach(vt_term_t *term, vt_xterm_event_listener_t *xterm_listener,
433                    vt_config_event_listener_t *config_listener,
434                    vt_screen_event_listener_t *screen_listener,
435                    vt_pty_event_listener_t *pty_listener) {
436   if (term->is_attached) {
437     /* already attached */
438     return 0;
439   }
440 
441   vt_parser_set_xterm_listener(term->parser, xterm_listener);
442   vt_parser_set_config_listener(term->parser, config_listener);
443   vt_screen_set_listener(term->screen, screen_listener);
444 
445   if (term->pty) {
446     vt_pty_set_listener(term->pty, pty_listener);
447   } else {
448     term->pty_listener = pty_listener;
449   }
450 
451   term->is_attached = 1;
452 
453   return 1;
454 }
455 
vt_term_detach(vt_term_t * term)456 int vt_term_detach(vt_term_t *term) {
457   if (!term->is_attached) {
458     /* already detached. */
459     return 0;
460   }
461 
462   vt_parser_set_xterm_listener(term->parser, NULL);
463   vt_parser_set_config_listener(term->parser, NULL);
464   vt_screen_set_listener(term->screen, NULL);
465 
466   if (term->pty) {
467     vt_pty_set_listener(term->pty, NULL);
468   } else {
469     term->pty_listener = NULL;
470   }
471 
472   term->is_attached = 0;
473 
474   return 1;
475 }
476 
vt_term_set_use_ot_layout(vt_term_t * term,int flag)477 void vt_term_set_use_ot_layout(vt_term_t *term, int flag) {
478 #if (defined(USE_HARFBUZZ) || defined(USE_UNISCRIBE)) && !defined(NOT_CONVERT_TO_ISCII)
479   vt_unicode_policy_t policy;
480 
481   policy = vt_parser_get_unicode_policy(term->parser);
482 
483   if (flag) {
484     policy &= ~CONVERT_UNICODE_TO_ISCII;
485   } else {
486     policy |= CONVERT_UNICODE_TO_ISCII;
487   }
488 
489   vt_parser_set_unicode_policy(term->parser, policy);
490 #endif
491 
492   term->use_ot_layout = flag;
493 }
494 
vt_term_get_master_fd(vt_term_t * term)495 int vt_term_get_master_fd(vt_term_t *term) {
496   if (term->pty == NULL) {
497     return -1;
498   }
499 
500   return vt_pty_get_master_fd(term->pty);
501 }
502 
vt_term_get_slave_fd(vt_term_t * term)503 int vt_term_get_slave_fd(vt_term_t *term) {
504   if (term->pty == NULL) {
505     return -1;
506   }
507 
508   return vt_pty_get_slave_fd(term->pty);
509 }
510 
511 /*
512  * Always return non-NULL value.
513  * XXX Static data can be returned. (Not reentrant)
514  */
vt_term_get_slave_name(vt_term_t * term)515 char *vt_term_get_slave_name(vt_term_t *term) {
516   if (term->pty == NULL) {
517     return "/dev/zombie";
518   }
519 
520   return vt_pty_get_slave_name(term->pty);
521 }
522 
vt_term_get_child_pid(vt_term_t * term)523 pid_t vt_term_get_child_pid(vt_term_t *term) {
524   if (term->pty == NULL) {
525 #ifdef OPEN_PTY_ASYNC
526     return term->return_special_pid ? -10 : -1;
527 #else
528     return -1;
529 #endif
530   }
531 
532   return vt_pty_get_pid(term->pty);
533 }
534 
vt_term_get_pty_mode(vt_term_t * term)535 pid_t vt_term_get_pty_mode(vt_term_t *term) {
536   if (term->pty == NULL) {
537     return PTY_NONE;
538   }
539 
540   return vt_pty_get_mode(term->pty);
541 }
542 
vt_term_write(vt_term_t * term,u_char * buf,size_t len)543 size_t vt_term_write(vt_term_t *term, u_char *buf, size_t len) {
544   if (term->pty == NULL) {
545     return 0;
546   }
547 
548   return vt_parser_write(term->parser, buf, len);
549 }
550 
551 /* The caller should swap width_pix and height_pix in vertical mode. */
vt_term_resize(vt_term_t * term,u_int cols,u_int rows,u_int width_pix,u_int height_pix)552 int vt_term_resize(vt_term_t *term, u_int cols, u_int rows, u_int width_pix, u_int height_pix) {
553   int ret;
554 
555   vt_screen_logical(term->screen);
556   ret = vt_screen_resize(term->screen, cols, rows);
557   vt_screen_render(term->screen);
558   vt_screen_visual(term->screen);
559 
560   if (term->pty) {
561     /* Don't use cols and rows because status line might change rows in vt_screen_resize(). */
562     vt_set_pty_winsize(term->pty, vt_screen_get_logical_cols(term->screen),
563                        vt_screen_get_logical_rows(term->screen), width_pix, height_pix);
564   }
565 
566   return ret;
567 }
568 
vt_term_unhighlight_cursor(vt_term_t * term,int revert_visual)569 int vt_term_unhighlight_cursor(vt_term_t *term, int revert_visual) {
570   vt_line_t *line;
571   int ret;
572 
573 #ifdef DEBUG
574   if (term->screen->logvis && !term->screen->logvis->is_visual) {
575     bl_debug_printf(BL_DEBUG_TAG
576                     " vt_term_unhighlight_cursor() should be called in visual context but"
577                     " is called in logical context.\n");
578   }
579 #endif
580 
581   vt_screen_logical(term->screen);
582 
583   if ((line = vt_screen_get_cursor_line(term->screen)) == NULL || vt_line_is_empty(line)) {
584     ret = 0;
585   } else {
586     vt_line_set_modified(line, vt_screen_cursor_char_index(term->screen),
587                          vt_screen_cursor_char_index(term->screen));
588 
589     ret = 1;
590   }
591 
592   if (revert_visual) {
593     /* vt_screen_render(term->screen); */
594     vt_screen_visual(term->screen);
595   }
596 
597   return ret;
598 }
599 
600 /*
601  * Not implemented yet.
602  */
603 #if 0
604 void vt_term_set_modified_region(vt_term_t *term, int beg_char_index, int beg_row, u_int nchars,
605                                  u_int nrows) {
606   return;
607 }
608 #endif
609 
610 /*
611  * Not used.
612  */
613 #if 0
614 void vt_term_set_modified_region_in_screen(vt_term_t *term, int beg_char_index, int beg_row,
615                                            u_int nchars, u_int nrows) {
616   int row;
617   vt_line_t *line;
618   int revert_to_visual;
619 
620   /*
621    * This function is usually called in visual context, and sometimes
622    * called in logical context. (see flush_scroll_cache() in x_screen.c)
623    */
624   if (!vt_screen_logical_visual_is_reversible(term->screen) && vt_screen_logical(term->screen)) {
625     revert_to_visual = 1;
626   } else {
627     revert_to_visual = 0;
628   }
629 
630   for (row = beg_row; row < beg_row + nrows; row++) {
631     if ((line = vt_screen_get_line_in_screen(term->screen, row))) {
632       vt_line_set_modified(line, beg_char_index, beg_char_index + nchars - 1);
633     }
634   }
635 
636   if (revert_to_visual) {
637     /* vt_screen_render(term->screen); */
638     vt_screen_visual(term->screen);
639   }
640 }
641 #endif
642 
vt_term_set_modified_lines(vt_term_t * term,int beg,int end)643 void vt_term_set_modified_lines(vt_term_t *term, int beg, int end) {
644   int row;
645   vt_line_t *line;
646   int revert_to_visual;
647 
648   /*
649    * This function is usually called in visual context, and sometimes
650    * called in logical context. (see flush_scroll_cache() in x_screen.c)
651    */
652   if (!vt_screen_logical_visual_is_reversible(term->screen) && vt_screen_logical(term->screen)) {
653     revert_to_visual = 1;
654   } else {
655     revert_to_visual = 0;
656   }
657 
658   for (row = beg; row <= end; row++) {
659     if ((line = vt_screen_get_line(term->screen, row))) {
660       vt_line_set_modified_all(line);
661     }
662   }
663 
664   if (revert_to_visual) {
665     /* vt_screen_render(term->screen); */
666     vt_screen_visual(term->screen);
667   }
668 }
669 
vt_term_set_modified_lines_in_screen(vt_term_t * term,int beg,int end)670 void vt_term_set_modified_lines_in_screen(vt_term_t *term, int beg, int end) {
671   int row;
672   vt_line_t *line;
673   int revert_to_visual;
674 
675   /*
676    * This function is usually called in visual context, and sometimes
677    * called in logical context. (see flush_scroll_cache() in x_screen.c)
678    */
679   if (!vt_screen_logical_visual_is_reversible(term->screen) && vt_screen_logical(term->screen)) {
680     revert_to_visual = 1;
681   } else {
682     revert_to_visual = 0;
683   }
684 
685   for (row = beg; row <= end; row++) {
686     if ((line = vt_screen_get_line_in_screen(term->screen, row))) {
687       vt_line_set_modified_all(line);
688     }
689   }
690 
691   if (revert_to_visual) {
692     /* vt_screen_render(term->screen); */
693     vt_screen_visual(term->screen);
694   }
695 }
696 
vt_term_set_modified_all_lines_in_screen(vt_term_t * term)697 void vt_term_set_modified_all_lines_in_screen(vt_term_t *term) {
698   int revert_to_visual;
699 
700   /*
701    * This function is usually called in visual context, and sometimes
702    * called in logical context. (see flush_scroll_cache() in x_screen.c)
703    */
704   if (!vt_screen_logical_visual_is_reversible(term->screen) && vt_screen_logical(term->screen)) {
705     revert_to_visual = 1;
706   } else {
707     revert_to_visual = 0;
708   }
709 
710   vt_screen_set_modified_all(term->screen);
711 
712   if (revert_to_visual) {
713     /* vt_screen_render(term->screen); */
714     vt_screen_visual(term->screen);
715   }
716 }
717 
vt_term_updated_all(vt_term_t * term)718 void vt_term_updated_all(vt_term_t *term) {
719   int row;
720   vt_line_t *line;
721 
722 #ifdef DEBUG
723   if (term->screen->logvis && !term->screen->logvis->is_visual) {
724     bl_debug_printf(BL_DEBUG_TAG
725                     " vt_term_updated_all() should be called in visual context but"
726                     " is called in logical context.\n");
727   }
728 #endif
729 
730   if (!vt_screen_logical_visual_is_reversible(term->screen)) {
731     vt_screen_logical(term->screen);
732   }
733 
734   for (row = 0; row < vt_screen_get_rows(term->screen); row++) {
735     line = vt_screen_get_line_in_screen(term->screen, row); /* Always non-NULL */
736     vt_line_set_updated(line);
737   }
738 
739   if (!vt_screen_logical_visual_is_reversible(term->screen)) {
740     /* vt_screen_render(term->screen); */
741     vt_screen_visual(term->screen);
742   }
743 }
744 
745 /*
746  * Return value:
747  *  1 => Updated
748  *  0 => Not updated(== not necessary to redraw)
749  */
vt_term_update_special_visual(vt_term_t * term)750 int vt_term_update_special_visual(vt_term_t *term) {
751   vt_logical_visual_t *logvis;
752   int had_logvis = 0;
753   int has_logvis = 0;
754 
755   had_logvis = vt_screen_destroy_logical_visual(term->screen);
756 
757   if (term->use_dynamic_comb) {
758     if ((logvis = vt_logvis_comb_new())) {
759       if (vt_screen_add_logical_visual(term->screen, logvis)) {
760         has_logvis = 1;
761 
762         if (vt_parser_is_using_char_combining(term->parser)) {
763           bl_msg_printf(
764               "Set use_combining=false forcibly "
765               "to enable use_dynamic_comb.\n");
766           vt_parser_set_use_char_combining(term->parser, 0);
767         }
768       } else {
769 #ifdef DEBUG
770         bl_warn_printf(BL_DEBUG_TAG " vt_screen_add_logical_visual failed.\n");
771 #endif
772 
773         (*logvis->destroy)(logvis);
774       }
775     }
776 #ifdef DEBUG
777     else {
778       bl_warn_printf(BL_DEBUG_TAG " vt_logvis_comb_new() failed.\n");
779     }
780 #endif
781   }
782 
783   /* Vertical mode, BiDi and ISCII can't coexist. */
784 
785   /* Similar if-else conditions exist in update_special_visual in x_screen.c. */
786   if (term->vertical_mode) {
787     if ((logvis = vt_logvis_vert_new(term->vertical_mode))) {
788       if (vt_screen_add_logical_visual(term->screen, logvis)) {
789         has_logvis = 1;
790       } else {
791 #ifdef DEBUG
792         bl_warn_printf(BL_DEBUG_TAG " vt_screen_add_logical_visual failed.\n");
793 #endif
794 
795         (*logvis->destroy)(logvis);
796       }
797     }
798 #ifdef DEBUG
799     else {
800       bl_warn_printf(BL_DEBUG_TAG " vt_logvis_vert_new() failed.\n");
801     }
802 #endif
803   } else if (term->use_ctl && (vt_term_get_encoding(term) == VT_UTF8 ||
804                                IS_ISCII_ENCODING(vt_term_get_encoding(term)))) {
805     if ((logvis = vt_logvis_ctl_new(term->bidi_mode, term->bidi_separators,
806                                     term->use_ot_layout ? term : NULL))) {
807       if (vt_screen_add_logical_visual(term->screen, logvis)) {
808         has_logvis = 1;
809       } else {
810 #ifdef DEBUG
811         bl_warn_printf(BL_DEBUG_TAG " vt_screen_add_logical_visual failed.\n");
812 #endif
813 
814         (*logvis->destroy)(logvis);
815       }
816     }
817 #ifdef DEBUG
818     else {
819       bl_warn_printf(BL_DEBUG_TAG " vt_logvis_ctl_new() failed.\n");
820     }
821 #endif
822   }
823 
824   if (had_logvis || has_logvis) {
825     vt_screen_render(term->screen);
826     vt_screen_visual(term->screen);
827 
828     return 1;
829   } else {
830     return 0;
831   }
832 }
833 
vt_term_enter_backscroll_mode(vt_term_t * term)834 int vt_term_enter_backscroll_mode(vt_term_t *term) {
835   /* XXX */
836   if (term->vertical_mode) {
837     bl_msg_printf("Not supported backscrolling in vertical mode.\n");
838 
839     return 0;
840   }
841 
842   return vt_enter_backscroll_mode(term->screen);
843 }
844 
vt_term_set_icon_path(vt_term_t * term,const char * path)845 void vt_term_set_icon_path(vt_term_t *term, const char *path) {
846   free(term->icon_path);
847 
848   if (path && *path) {
849     term->icon_path = strdup(path);
850   } else {
851     term->icon_path = NULL;
852   }
853 }
854 
vt_term_set_bidi_separators(vt_term_t * term,const char * bidi_separators)855 void vt_term_set_bidi_separators(vt_term_t *term, const char *bidi_separators) {
856   free(term->bidi_separators);
857 
858   if (bidi_separators && *bidi_separators) {
859     term->bidi_separators = bl_str_unescape(bidi_separators);
860   } else {
861     term->bidi_separators = NULL;
862   }
863 }
864 
vt_term_get_config(vt_term_t * term,vt_term_t * output,char * key,int to_menu,int * flag)865 int vt_term_get_config(vt_term_t *term, vt_term_t *output, /* if term == output, NULL is set */
866                        char *key, int to_menu, int *flag) {
867   char *value;
868 
869   if (vt_parser_get_config(term->parser, output ? output->pty : NULL, key, to_menu, flag)) {
870     return 1;
871   }
872 
873   if (strcmp(key, "vertical_mode") == 0) {
874     value = vt_get_vertical_mode_name(term->vertical_mode);
875   } else if (strcmp(key, "use_dynamic_comb") == 0) {
876     if (term->use_dynamic_comb) {
877       value = "true";
878     } else {
879       value = "false";
880     }
881   } else if (strcmp(key, "use_ctl") == 0) {
882     if (term->use_ctl) {
883       value = "true";
884     } else {
885       value = "false";
886     }
887   } else if (strcmp(key, "bidi_mode") == 0) {
888     value = vt_get_bidi_mode_name(term->bidi_mode);
889   } else if (strcmp(key, "bidi_separators") == 0) {
890     if ((value = term->bidi_separators) == NULL) {
891       value = "";
892     }
893   }
894 #ifdef USE_OT_LAYOUT
895   else if (strcmp(key, "use_ot_layout") == 0) {
896     if (term->use_ot_layout) {
897       value = "true";
898     } else {
899       value = "false";
900     }
901   } else if (strcmp(key, "ot_features") == 0) {
902     value = vt_get_ot_layout_attr(OT_FEATURES);
903   } else if (strcmp(key, "ot_script") == 0) {
904     value = vt_get_ot_layout_attr(OT_SCRIPT);
905   }
906 #endif
907   else if (strcmp(key, "pty_name") == 0) {
908     if (output) {
909       if ((value = vt_get_window_name(term->parser)) == NULL) {
910         value = "";
911       }
912     } else {
913       value = vt_term_get_slave_name(term);
914     }
915   } else if (strcmp(key, "icon_path") == 0) {
916     if ((value = term->icon_path) == NULL) {
917       value = "";
918     }
919   }
920 #if defined(__ANDROID__) && defined(USE_LIBSSH2)
921   else if (strcmp(key, "start_with_local_pty") == 0) {
922     value = start_with_local_pty ? "true" : "false";
923   }
924 #endif
925   else {
926     /* Continue to process it in x_screen.c */
927     return 0;
928   }
929 
930   if (!output) {
931     output = term;
932   }
933 
934 /* value is never set NULL above. */
935 #if 0
936   if (!value) {
937     vt_response_config(output->pty, "error", NULL, to_menu);
938   }
939 #endif
940 
941   if (flag) {
942     *flag = value ? true_or_false(value) : -1;
943   } else {
944     vt_response_config(output->pty, key, value, to_menu);
945   }
946 
947   return 1;
948 }
949 
950 /* Called in visual context */
vt_term_set_config(vt_term_t * term,char * key,char * value)951 int vt_term_set_config(vt_term_t *term, char *key, char *value) {
952   if (vt_parser_set_config(term->parser, key, value)) {
953     /* do nothing */
954   }
955 #ifdef USE_OT_LAYOUT
956   else if (strcmp(key, "ot_script") == 0) {
957     vt_set_ot_layout_attr(value, OT_SCRIPT);
958   } else if (strcmp(key, "ot_features") == 0) {
959     vt_set_ot_layout_attr(value, OT_FEATURES);
960   }
961 #endif
962   else {
963     /* Continue to process it in x_screen.c */
964     return 0;
965   }
966 
967   return 1;
968 }
969