1 /* -*- c-basic-offset:2; tab-width:2; indent-tabs-mode:nil -*- */
2 
3 #include "ui_screen_manager.h"
4 
5 #include <stdio.h>  /* sprintf */
6 #include <string.h> /* memset/memcpy */
7 #include <stdlib.h> /* getenv */
8 #include <unistd.h> /* getuid */
9 
10 #include <pobl/bl_def.h> /* USE_WIN32API */
11 #ifndef USE_WIN32API
12 #include <pwd.h> /* getpwuid */
13 #endif
14 
15 #include <pobl/bl_debug.h>
16 #include <pobl/bl_str.h>  /* bl_str_sep/bl_str_to_int/strdup */
17 #include <pobl/bl_path.h> /* bl_basename */
18 #include <pobl/bl_util.h> /* DIGIT_STR_LEN */
19 #include <pobl/bl_mem.h>  /* alloca/malloc/free */
20 #include <pobl/bl_conf.h>
21 #include <pobl/bl_conf_io.h>
22 #include <pobl/bl_types.h> /* u_int */
23 #include <pobl/bl_args.h>  /* bl_arg_str_to_array */
24 #include <pobl/bl_sig_child.h>
25 #include <pobl/bl_dialog.h>
26 #include <vt_term_manager.h>
27 #include <vt_char_encoding.h>
28 
29 #include "ui_layout.h"
30 #include "ui_display.h"
31 
32 #if defined(USE_WIN32API) || defined(USE_LIBSSH2)
33 #include "ui_connect_dialog.h"
34 #endif
35 
36 #define MAX_SCREENS (MSU * max_screens_multiple) /* Default MAX_SCREENS is 32. */
37 #define MSU (8 * sizeof(dead_mask[0]))           /* MAX_SCREENS_UNIT */
38 
39 #if 0
40 #define __DEBUG
41 #endif
42 
43 /* --- static variables --- */
44 
45 static char *mlterm_version;
46 
47 static u_int max_screens_multiple;
48 static u_int32_t *dead_mask;
49 
50 static ui_screen_t **screens;
51 static u_int num_screens;
52 
53 static u_int depth;
54 
55 static u_int num_startup_screens;
56 
57 static ui_system_event_listener_t system_listener;
58 
59 static ui_main_config_t main_config;
60 
61 static ui_shortcut_t shortcut;
62 
63 /* --- static functions --- */
64 
65 /*
66  * Callbacks of vt_config_event_listener_t events.
67  */
68 
69 /*
70  * Reload mlterm/main file and reset main_config.
71  * Notice: Saved changes are not applied to the screens already opened.
72  */
config_saved(void)73 static void config_saved(void) {
74   bl_conf_t *conf;
75   char *argv[] = {"mlterm", NULL};
76 
77   ui_main_config_final(&main_config);
78 
79   if ((conf = bl_conf_new()) == NULL) {
80     return;
81   }
82 
83   ui_prepare_for_main_config(conf);
84   ui_main_config_init(&main_config, conf, 1, argv);
85 
86   bl_conf_destroy(conf);
87 }
88 
font_config_updated(void)89 static void font_config_updated(void) {
90   u_int count;
91 
92   ui_font_cache_unload_all();
93 
94   for (count = 0; count < num_screens; count++) {
95     ui_screen_reset_view(screens[count]);
96   }
97 }
98 
color_config_updated(void)99 static void color_config_updated(void) {
100   u_int count;
101 
102   ui_color_cache_unload_all();
103 
104   ui_display_reset_cmap();
105 
106   for (count = 0; count < num_screens; count++) {
107     ui_screen_reset_view(screens[count]);
108   }
109 }
110 
create_term_intern(void)111 static vt_term_t *create_term_intern(void) {
112   vt_term_t *term;
113 
114   if ((term = vt_create_term(
115            main_config.term_type, main_config.cols, main_config.rows, main_config.tab_size,
116            main_config.num_log_lines, main_config.encoding, main_config.is_auto_encoding,
117            main_config.use_auto_detect, main_config.logging_vt_seq, main_config.unicode_policy,
118            main_config.col_size_of_width_a, main_config.use_char_combining,
119            main_config.use_multi_col_char, main_config.use_ctl, main_config.bidi_mode,
120            main_config.bidi_separators, main_config.use_dynamic_comb, main_config.bs_mode,
121            main_config.vertical_mode, main_config.use_local_echo, main_config.title,
122            main_config.icon_name, main_config.use_ansi_colors, main_config.alt_color_mode,
123            main_config.use_ot_layout, main_config.blink_cursor ? CS_BLINK|CS_BLOCK : CS_BLOCK,
124            main_config.ignore_broadcasted_chars)) == NULL) {
125     return NULL;
126   }
127 
128   if (main_config.icon_path) {
129     vt_term_set_icon_path(term, main_config.icon_path);
130   }
131 
132   if (main_config.unlimit_log_size) {
133     vt_term_unlimit_log_size(term);
134   }
135 
136   return term;
137 }
138 
open_pty_intern(vt_term_t * term,char * cmd_path,char ** cmd_argv,ui_window_t * win,int show_dialog)139 static int open_pty_intern(vt_term_t *term, char *cmd_path, char **cmd_argv,
140                            ui_window_t *win, int show_dialog) {
141   char *display;
142   Window window;
143   u_int width_pix;
144   u_int height_pix;
145   char *env[7]; /* MLTERM,TERM,WINDOWID,WAYLAND_DISPLAY,DISPLAY,COLORFGBG,NULL */
146   char **env_p;
147   char wid_env[9 + DIGIT_STR_LEN(Window) + 1]; /* "WINDOWID="(9) + [32bit digit] + NULL(1) */
148   char *disp_env;
149   char *term_env;
150   char *uri;
151   char *pass;
152 #ifdef USE_LIBSSH2
153   char *privkey;
154 #endif
155   int ret;
156 
157   display = win->disp->name;
158   window = win->my_window;
159 
160   if (vt_term_get_vertical_mode(term)) {
161     width_pix = win->height * 100 / ((ui_screen_t*)win)->screen_width_ratio;
162     height_pix = win->width;
163   } else {
164     width_pix = win->width * 100 / ((ui_screen_t*)win)->screen_width_ratio;
165     height_pix = win->height;
166   }
167 
168   env_p = env;
169 
170   *(env_p++) = mlterm_version;
171 
172 #if defined(USE_XLIB)
173   sprintf(wid_env, "WINDOWID=%ld", window);
174   *(env_p++) = wid_env;
175 #endif
176 
177 #if defined(USE_WAYLAND)
178   /* "WAYLAND_DISPLAY="(16) + NULL(1) */
179   if (display && (disp_env = alloca(16 + strlen(display) + 1))) {
180     sprintf(disp_env, "WAYLAND_DISPLAY=%s", display);
181     *(env_p++) = disp_env;
182   }
183   *(env_p++) = "DISPLAY=:0.0";
184 #elif defined(USE_XLIB)
185   /* "DISPLAY="(8) + NULL(1) */
186   if (display && (disp_env = alloca(8 + strlen(display) + 1))) {
187     sprintf(disp_env, "DISPLAY=%s", display);
188     *(env_p++) = disp_env;
189   }
190 #endif
191 
192   /* "TERM="(5) + NULL(1) */
193   if (main_config.term_type && (term_env = alloca(5 + strlen(main_config.term_type) + 1))) {
194     sprintf(term_env, "TERM=%s", main_config.term_type);
195     *(env_p++) = term_env;
196   }
197 
198   *(env_p++) = "COLORFGBG=default;default";
199 
200   /* NULL terminator */
201   *env_p = NULL;
202 
203   uri = NULL;
204   pass = NULL;
205 #ifdef USE_LIBSSH2
206   privkey = main_config.private_key;
207 #endif
208 
209 #if defined(USE_WIN32API) || defined(USE_LIBSSH2)
210   if (show_dialog || main_config.default_server) {
211     char *default_server_dup;
212     char *uri_dup;
213     char *user;
214     char *host;
215     char *port;
216     char *encoding = NULL;
217     char *exec_cmd = NULL;
218     int x11_fwd;
219     void *session;
220 
221     x11_fwd = main_config.use_x11_forwarding;
222 
223 #ifdef USE_LIBSSH2
224     if (!show_dialog && main_config.default_server &&
225         (default_server_dup = alloca(strlen(main_config.default_server) + 1)) &&
226         bl_parse_uri(NULL, &user, &host, &port, NULL, &encoding,
227                      strcpy(default_server_dup, main_config.default_server)) &&
228         (session = vt_search_ssh_session(host, port, user))) {
229       uri = strdup(main_config.default_server);
230       pass = strdup("");
231 
232       if (x11_fwd) {
233         vt_pty_ssh_set_use_x11_forwarding(session, x11_fwd);
234       }
235     } else
236 #endif
237     if (!ui_connect_dialog(&uri, &pass, &exec_cmd, &privkey, &x11_fwd, display, window,
238                            main_config.default_server)) {
239 #ifdef DEBUG
240       bl_debug_printf(BL_DEBUG_TAG " Connect dialog is canceled.\n");
241 #endif
242 
243       /*
244        * open_screen_intern() calls close_screen_intern() if this fuction
245        * returns 0, then this thread will be terminated on Haiku.
246        * (If mlclient --serv ... is executed and the connect dialog is cancelled,
247        *  the screen where mlclient is executed also exits.)
248        * So don't return 0 even if the connect dialog is cancelled.
249        *
250        * (vt_term_open_pty() below never returns 0 because OPEN_PTY_ASYNC
251        *  is defined on Haiku.)
252        */
253 #ifndef USE_BEOS
254       if (vt_get_all_terms(NULL) > 1) {
255         return 0;
256       }
257 #endif
258     } else if ((uri_dup = alloca(strlen(uri) + 1))) {
259       bl_parse_uri(NULL, &user, &host, &port, NULL, &encoding, strcpy(uri_dup, uri));
260 
261 #ifdef USE_LIBSSH2
262       vt_pty_ssh_set_use_x11_forwarding(vt_search_ssh_session(host, port, user), x11_fwd);
263 #endif
264     } else {
265       /* XXX Not recovering error. */
266       return 0;
267     }
268 
269 #ifdef __DEBUG
270     bl_debug_printf("Connect dialog: URI %s pass %s\n", uri, pass);
271 #endif
272 
273     if (encoding) {
274       if (vt_term_is_attached(term)) {
275         /*
276          * Don't use vt_term_change_encoding() here because
277          * encoding change could cause special visual change
278          * which should update the state of ui_screen_t.
279          */
280         char *seq;
281         size_t len;
282 
283         if ((seq = alloca((len = 16 + strlen(encoding) + 2)))) {
284           sprintf(seq, "\x1b]5379;encoding=%s\x07", encoding);
285           vt_term_write_loopback(term, seq, len - 1);
286         }
287       } else {
288         vt_term_change_encoding(term, vt_get_char_encoding(encoding));
289       }
290     }
291 
292     if (exec_cmd) {
293       char *tmp = exec_cmd;
294 
295       if ((exec_cmd = alloca(strlen(exec_cmd) + 1))) {
296         strcpy(exec_cmd, tmp);
297       }
298       free(tmp);
299 
300       if ((tmp = bl_argv_alloca(exec_cmd))) {
301         int argc;
302 
303         if (bl_arg_str_to_array((char**)tmp, &argc, exec_cmd)) {
304           cmd_argv = (char**)tmp;
305           cmd_path = cmd_argv[0];
306         }
307       }
308     }
309   }
310 #endif
311 
312 #if 0
313   if (cmd_argv) {
314     char **p;
315 
316     bl_debug_printf(BL_DEBUG_TAG " %s", cmd_path);
317     p = cmd_argv;
318     while (*p) {
319       bl_msg_printf(" %s", *p);
320       p++;
321     }
322     bl_msg_printf("\n");
323   }
324 #endif
325 
326   /*
327    * If cmd_path and pass are NULL, set default shell as cmd_path.
328    * If uri is not NULL (= connecting to ssh/telnet/rlogin etc servers),
329    * cmd_path is not changed.
330    */
331   if (!uri && !cmd_path) {
332 #ifdef __APPLE__
333     char *user;
334 
335     if ((user = getenv("USER")) && (cmd_argv = alloca(sizeof(char *) * 4))) {
336       cmd_argv[0] = cmd_path = "login";
337       cmd_argv[1] = "-fp";
338       cmd_argv[2] = user;
339       cmd_argv[3] = NULL;
340     } else
341 #endif
342     {
343       /*
344        * SHELL env var -> /etc/passwd -> /bin/sh
345        */
346       if ((cmd_path = getenv("SHELL")) == NULL || *cmd_path == '\0') {
347 #ifndef USE_WIN32API
348         struct passwd *pw;
349 
350         if ((pw = getpwuid(getuid())) == NULL || *(cmd_path = pw->pw_shell) == '\0')
351 #endif
352         {
353           cmd_path = "/bin/sh";
354         }
355       }
356     }
357   }
358 
359   /*
360    * Set cmd_argv by cmd_path.
361    */
362   if (cmd_path && !cmd_argv) {
363     char *cmd_file;
364 
365     cmd_file = bl_basename(cmd_path);
366 
367     if ((cmd_argv = alloca(sizeof(char *) * 2)) == NULL) {
368       return 0;
369     }
370 
371     /* 2 = `-' and NULL */
372     if ((cmd_argv[0] = alloca(strlen(cmd_file) + 2)) == NULL) {
373       return 0;
374     }
375 
376     if (main_config.use_login_shell) {
377       sprintf(cmd_argv[0], "-%s", cmd_file);
378     } else {
379       strcpy(cmd_argv[0], cmd_file);
380     }
381 
382     cmd_argv[1] = NULL;
383   }
384 
385 #ifdef USE_LIBSSH2
386   ret = vt_term_open_pty(term, cmd_path, cmd_argv, env, uri ? uri : display, main_config.work_dir,
387                          pass, main_config.public_key, privkey, width_pix, height_pix);
388 #else
389   ret = vt_term_open_pty(term, cmd_path, cmd_argv, env, uri ? uri : display, main_config.work_dir,
390                          pass, NULL, NULL, width_pix, height_pix);
391 #endif
392 
393 #if defined(USE_WIN32API) || defined(USE_LIBSSH2)
394   if (uri) {
395     if (ret && bl_compare_str(uri, main_config.default_server) != 0) {
396       free(main_config.default_server);
397       main_config.default_server = uri;
398     } else {
399       free(uri);
400     }
401 
402     free(pass);
403 
404     if (privkey != main_config.private_key) {
405       free(privkey);
406     }
407   }
408 #endif
409 
410   return ret;
411 }
412 
413 #ifndef NO_IMAGE
414 
get_picture_data(void * p,char * file_path,int * num_cols,int * num_rows,int * num_cols_small,int * num_rows_small,u_int32_t ** sixel_palette,int keep_aspect,int drcs_sixel)415 static vt_char_t *get_picture_data(void *p, char *file_path, int *num_cols /* can be 0 */,
416                                    int *num_rows /* can be 0 */,
417                                    int *num_cols_small /* set only if drcs_sixel is 1. */,
418                                    int *num_rows_small /* set only if drcs_sixel is 1. */,
419                                    u_int32_t **sixel_palette, int keep_aspect, int drcs_sixel) {
420   vt_char_t *data;
421 
422   if (num_screens > 0) {
423     vt_term_t *orig_term;
424 
425     orig_term = screens[0]->term;
426     screens[0]->term = p; /* XXX */
427     data = (*screens[0]->xterm_listener.get_picture_data)(screens[0]->xterm_listener.self,
428                                                           file_path, num_cols, num_rows,
429                                                           num_cols_small, num_rows_small,
430                                                           sixel_palette, 0, drcs_sixel);
431     screens[0]->term = orig_term;
432   } else {
433     data = NULL;
434   }
435 
436   return data;
437 }
438 
detach_screen(ui_screen_t * screen)439 static vt_term_t *detach_screen(ui_screen_t *screen) {
440   vt_term_t *term;
441 
442   if ((term = ui_screen_detach(screen))) {
443     vt_xterm_event_listener_t *listener;
444 
445     if (!(listener = vt_term_get_user_data(term, term))) {
446       if (!(listener = calloc(1, sizeof(vt_xterm_event_listener_t)))) {
447         return term;
448       }
449 
450       listener->self = term;
451       listener->get_picture_data = get_picture_data;
452 
453       vt_term_set_user_data(term, term, listener);
454     }
455 
456     /* XXX */
457     term->parser->xterm_listener = listener;
458   }
459 
460   return term;
461 }
462 
463 #define ui_screen_detach(screen) detach_screen(screen)
464 
465 #endif
466 
467 #ifdef USE_WIN32GUI
close_screen_win32(ui_screen_t * screen)468 static void close_screen_win32(ui_screen_t *screen) {
469   int is_orphan;
470 
471   if (UI_SCREEN_TO_LAYOUT(screen) && ui_layout_remove_child(UI_SCREEN_TO_LAYOUT(screen), screen)) {
472     is_orphan = 1;
473   } else {
474     is_orphan = 0;
475 
476     /*
477      * XXX Hack
478      * In case SendMessage(WM_CLOSE) causes WM_KILLFOCUS
479      * and operates screen->term which was already destroyed.
480      * (see window_unfocused())
481      */
482     screen->window.window_unfocused = NULL;
483   }
484 
485   SendMessage(ui_get_root_window(&screen->window)->my_window, WM_CLOSE, 0, 0);
486 
487   if (is_orphan && screen->window.window_destroyed) {
488     (*screen->window.window_destroyed)(&screen->window);
489   }
490 }
491 #endif
492 
close_screen_intern(ui_screen_t * screen)493 static void close_screen_intern(ui_screen_t *screen) {
494   ui_window_t *root;
495   ui_display_t *disp;
496 
497   if (UI_SCREEN_TO_LAYOUT(screen)) {
498     ui_layout_remove_child(UI_SCREEN_TO_LAYOUT(screen), screen);
499     /* ui_get_root_window() below doesn't return ui_layout. */
500   }
501 
502   ui_screen_detach(screen);
503   ui_font_manager_destroy(screen->font_man);
504   ui_color_manager_destroy(screen->color_man);
505 
506   root = ui_get_root_window(&screen->window);
507   disp = root->disp;
508 
509   if (!ui_display_remove_root(disp, root)) {
510     ui_window_unmap(root);
511     ui_window_final(root);
512   } else if (disp->num_roots == 0) {
513     ui_display_close(disp);
514   }
515 }
516 
open_screen_intern(char * disp_name,vt_term_t * term,ui_layout_t * layout,int horizontal,const char * sep,int show_dialog)517 static ui_screen_t *open_screen_intern(char *disp_name, vt_term_t *term, ui_layout_t *layout,
518                                        int horizontal, const char *sep, int show_dialog) {
519   ui_display_t *disp;
520   ui_screen_t *screen;
521   ui_font_manager_t *font_man;
522   ui_color_manager_t *color_man;
523   ui_window_t *root;
524   ef_charset_t usascii_font_cs;
525   void *p;
526 
527   /*
528    * these are dynamically allocated.
529    */
530   disp = NULL;
531   font_man = NULL;
532   color_man = NULL;
533   screen = NULL;
534   root = NULL;
535 
536   if (MAX_SCREENS <= num_screens) {
537     return NULL;
538   }
539 
540   if (!term) {
541     if ((!layout || (term = vt_get_detached_term(NULL)) == NULL) &&
542         (term = create_term_intern()) == NULL) {
543       return NULL;
544     }
545   }
546 
547   if (layout) {
548     disp = layout->window.disp;
549   } else if ((disp = ui_display_open(disp_name, depth)) == NULL) {
550 #ifdef DEBUG
551     bl_warn_printf(BL_DEBUG_TAG " ui_display_open failed.\n");
552 #endif
553 
554     goto error;
555   }
556 
557   if (main_config.unicode_policy & NOT_USE_UNICODE_FONT || main_config.iso88591_font_for_usascii) {
558     usascii_font_cs = ui_get_usascii_font_cs(VT_ISO8859_1);
559   } else if (main_config.unicode_policy & ONLY_USE_UNICODE_FONT) {
560     usascii_font_cs = ui_get_usascii_font_cs(VT_UTF8);
561   } else {
562     usascii_font_cs = ui_get_usascii_font_cs(vt_term_get_encoding(term));
563   }
564 
565   if ((font_man = ui_font_manager_new(disp->display, main_config.type_engine,
566                                       main_config.font_present, main_config.font_size,
567                                       usascii_font_cs, main_config.step_in_changing_font_size,
568                                       main_config.letter_space, main_config.use_bold_font,
569                                       main_config.use_italic_font)) == NULL) {
570     const char *msg_fmt = "No fonts for %s";
571     char *msg;
572     char *name;
573 
574     if (!(name = ui_get_charset_name(usascii_font_cs))) {
575       name = "US-ASCII";
576     }
577 
578     if ((msg = alloca(13 + strlen(name) + 1))) {
579       sprintf(msg, msg_fmt, name);
580       bl_dialog(BL_DIALOG_ALERT, msg);
581     }
582 
583     goto error;
584   }
585 
586   if ((color_man = ui_color_manager_new(
587            disp, main_config.fg_color, main_config.bg_color, main_config.cursor_fg_color,
588            main_config.cursor_bg_color, main_config.bd_color, main_config.ul_color,
589            main_config.bl_color, main_config.rv_color, main_config.it_color,
590            main_config.co_color)) == NULL) {
591     goto error;
592   }
593 
594   if ((screen = ui_screen_new(term, font_man, color_man, main_config.brightness,
595                               main_config.contrast, main_config.gamma, main_config.alpha,
596                               main_config.fade_ratio, &shortcut, main_config.screen_width_ratio,
597                               main_config.mod_meta_key, main_config.mod_meta_mode,
598                               main_config.bel_mode, main_config.receive_string_via_ucs,
599                               main_config.pic_file_path, main_config.use_transbg,
600                               main_config.use_vertical_cursor, main_config.borderless,
601                               main_config.line_space, main_config.input_method,
602                               main_config.allow_osc52, main_config.hmargin, main_config.vmargin,
603                               main_config.hide_underline, main_config.underline_offset,
604                               main_config.baseline_offset)) == NULL) {
605 #ifdef DEBUG
606     bl_warn_printf(BL_DEBUG_TAG " ui_screen_new() failed.\n");
607 #endif
608 
609     goto error;
610   }
611 
612   /* Override config event listener. */
613   screen->config_listener.saved = config_saved;
614 
615   ui_set_system_listener(screen, &system_listener);
616 
617   if (layout) {
618     if (!ui_layout_add_child(layout, screen, horizontal, sep)) {
619       layout = NULL;
620 
621       goto error;
622     }
623 
624     root = &layout->window;
625   } else {
626     if (main_config.use_mdi &&
627         (layout = ui_layout_new(screen, main_config.scrollbar_view_name, main_config.sb_fg_color,
628                                 main_config.sb_bg_color, main_config.sb_mode,
629                                 main_config.layout_hmargin, main_config.layout_vmargin))) {
630       root = &layout->window;
631     } else {
632       root = &screen->window;
633     }
634 
635     if (!ui_display_show_root(disp, root, main_config.x, main_config.y, main_config.geom_hint,
636                               main_config.app_name, main_config.parent_window)) {
637 #ifdef DEBUG
638       bl_warn_printf(BL_DEBUG_TAG " ui_display_show_root() failed.\n");
639 #endif
640 
641       goto error;
642     }
643   }
644 
645   if ((p = realloc(screens, sizeof(ui_screen_t *) * (num_screens + 1))) == NULL) {
646     /*
647      * XXX
648      * After ui_display_show_root() screen is not destroyed correctly by
649      * 'goto error'(see following error handling in open_pty_intern),
650      * but I don't know how to do.
651      */
652     goto error;
653   }
654 
655   screens = p;
656 
657   /*
658    * New screen is successfully created here except vt_pty.
659    */
660 
661   if (vt_term_pty_is_opened(term)) {
662 #if 0
663     /* mlclient /dev/... -e foo */
664     if (main_config.cmd_argv) {
665       int count;
666       for (count = 0; main_config.cmd_argv[count]; count++) {
667         vt_term_write(term, main_config.cmd_argv[count], strlen(main_config.cmd_argv[count]), 0);
668         vt_term_write(term, " ", 1, 0);
669       }
670 
671       vt_term_write(term, "\n", 1, 0);
672     }
673 #endif
674   } else {
675     if (!open_pty_intern(term, main_config.cmd_path, main_config.cmd_argv,
676                          &screen->window, show_dialog)) {
677       ui_screen_detach(screen);
678       vt_destroy_term(term);
679 
680 #ifdef USE_WIN32GUI
681       screens[num_screens++] = screen;
682       close_screen_win32(screen);
683 #else
684       close_screen_intern(screen);
685 #endif
686 
687       return NULL;
688     }
689 
690     if (main_config.init_str) {
691       vt_term_write(term, main_config.init_str, strlen(main_config.init_str));
692     }
693   }
694 
695   /* Don't add screen to screens before "return NULL" above unless USE_WIN32GUI. */
696   screens[num_screens++] = screen;
697 
698   return screen;
699 
700 error:
701   if (font_man) {
702     ui_font_manager_destroy(font_man);
703   }
704 
705   if (color_man) {
706     ui_color_manager_destroy(color_man);
707   }
708 
709   if (!root || !ui_display_remove_root(disp, root)) {
710     /*
711      * If root is still NULL or is not registered to disp yet.
712      */
713 
714     if (screen) {
715       ui_screen_destroy(screen);
716     }
717 
718     if (layout) {
719       ui_layout_destroy(layout);
720     }
721   }
722 
723   if (disp && disp->num_roots == 0) {
724     ui_display_close(disp);
725   }
726 
727   vt_destroy_term(term);
728 
729   return NULL;
730 }
731 
732 /*
733  * callbacks of ui_system_event_listener_t
734  */
735 
736 /*
737  * EXIT_PROGRAM shortcut calls this at last.
738  * this is for debugging.
739  */
740 #ifdef DEBUG
741 #include "../main/main_loop.h"
742 
__exit(void * p,int status)743 static void __exit(void *p, int status) {
744 #ifdef USE_WIN32GUI
745   u_int count;
746 
747   for (count = 0; count < num_screens; count++) {
748     SendMessage(ui_get_root_window(&screens[count]->window), WM_CLOSE, 0, 0);
749   }
750 #endif
751 #if 1
752   bl_mem_dump_all();
753 #endif
754 
755   main_loop_final();
756 
757 #if defined(USE_WIN32API) && defined(USE_LIBSSH2)
758   WSACleanup();
759 #endif
760 
761   bl_msg_printf("reporting unfreed memories --->\n");
762 
763   bl_mem_free_all();
764 
765   bl_dl_close_all();
766 
767   exit(status);
768 }
769 #endif
770 
open_pty(void * p,ui_screen_t * screen,char * dev)771 static void open_pty(void *p, ui_screen_t *screen, char *dev) {
772   vt_term_t *new;
773 
774   if (dev) {
775     if ((new = vt_get_detached_term(dev)) == NULL) {
776       return;
777     }
778   } else {
779     vt_char_encoding_t encoding;
780 #if defined(USE_WIN32API) || defined(USE_LIBSSH2)
781     char *default_server;
782     char *new_cmd_line;
783     char *new_cmd_line_dup;
784     char *cmd_path;
785     char **cmd_argv;
786 #endif
787     int ret;
788 
789     encoding = main_config.encoding;
790     main_config.encoding = vt_term_get_encoding(screen->term);
791 
792     if ((new = create_term_intern()) == NULL) {
793       main_config.encoding = encoding;
794 
795       return;
796     }
797 
798 #if defined(USE_WIN32API) || defined(USE_LIBSSH2)
799     if (!main_config.show_dialog) {
800       default_server = main_config.default_server;
801       main_config.default_server = vt_term_get_uri(screen->term);
802     }
803 
804     if ((new_cmd_line = vt_term_get_cmd_line(screen->term)) &&
805         (new_cmd_line_dup = alloca(strlen(new_cmd_line) + 1))) {
806       int argc;
807 
808       cmd_path = main_config.cmd_path;
809       cmd_argv = main_config.cmd_argv;
810 
811       if ((main_config.cmd_argv = bl_argv_alloca(new_cmd_line)) &&
812           bl_arg_str_to_array(main_config.cmd_argv, &argc,
813                               strcpy(new_cmd_line_dup, new_cmd_line))) {
814         main_config.cmd_path = main_config.cmd_argv[0];
815       } else {
816         main_config.cmd_argv = cmd_argv;
817       }
818     }
819 #endif
820 
821 #if defined(USE_WIN32API) || defined(USE_LIBSSH2)
822     ret = open_pty_intern(new, main_config.cmd_path, main_config.cmd_argv, &screen->window,
823                           main_config.show_dialog);
824 #else
825     ret = open_pty_intern(new, main_config.cmd_path, main_config.cmd_argv, &screen->window, 0);
826 #endif
827 
828     main_config.encoding = encoding;
829 #if defined(USE_WIN32API) || defined(USE_LIBSSH2)
830     if (!main_config.show_dialog) {
831       main_config.default_server = default_server;
832     }
833 
834     if (new_cmd_line) {
835       main_config.cmd_path = cmd_path;
836       main_config.cmd_argv = cmd_argv;
837     }
838 #endif
839 
840     if (!ret) {
841       vt_destroy_term(new);
842 
843       return;
844     }
845   }
846 
847   ui_screen_detach(screen);
848   ui_screen_attach(screen, new);
849 }
850 
next_pty(void * p,ui_screen_t * screen)851 static void next_pty(void *p, ui_screen_t *screen) {
852   vt_term_t *old;
853   vt_term_t *new;
854 
855   if ((old = ui_screen_detach(screen)) == NULL) {
856     return;
857   }
858 
859   if ((new = vt_next_term(old)) == NULL) {
860     ui_screen_attach(screen, old);
861   } else {
862     ui_screen_attach(screen, new);
863   }
864 }
865 
prev_pty(void * p,ui_screen_t * screen)866 static void prev_pty(void *p, ui_screen_t *screen) {
867   vt_term_t *old;
868   vt_term_t *new;
869 
870   if ((old = ui_screen_detach(screen)) == NULL) {
871     return;
872   }
873 
874   if ((new = vt_prev_term(old)) == NULL) {
875     ui_screen_attach(screen, old);
876   } else {
877     ui_screen_attach(screen, new);
878   }
879 }
880 
close_pty(void * p,ui_screen_t * screen,char * dev)881 static void close_pty(void *p, ui_screen_t *screen, char *dev) {
882   vt_term_t *term;
883 
884   if (dev) {
885     if ((term = vt_get_term(dev)) == NULL) {
886       return;
887     }
888   } else {
889     term = screen->term;
890   }
891 
892   /*
893    * Don't call vt_destroy_term directly, because close_pty() can be called
894    * in the context of parsing vt100 sequence.
895    */
896   bl_trigger_sig_child(vt_term_get_child_pid(term));
897 }
898 
pty_closed(void * p,ui_screen_t * screen)899 static void pty_closed(void *p, ui_screen_t *screen /* screen->term was already destroyed. */
900                        ) {
901   int count;
902 
903 #ifdef DEBUG
904   bl_debug_printf(BL_DEBUG_TAG " pty which is attached to screen %p is closed.\n", screen);
905 #endif
906 
907   for (count = num_screens - 1; count >= 0; count--) {
908     if (screen == screens[count]) {
909       vt_term_t *term;
910 
911       if ((term = vt_get_detached_term(NULL)) == NULL) {
912 #ifdef COCOA_TOUCH
913         if (vt_get_all_terms(NULL) == 0 && (term = create_term_intern())) {
914 #ifdef USE_LIBSSH2
915           if (open_pty_intern(term, main_config.cmd_path, main_config.cmd_argv,
916                               &screen->window, main_config.show_dialog))
917 #else
918           if (open_pty_intern(term, main_config.cmd_path, main_config.cmd_argv,
919                               &screen->window, 0))
920 #endif
921           {
922             ui_screen_attach(screen, term);
923 
924             return;
925           } else {
926             vt_destroy_term(term);
927           }
928         }
929 #endif
930 
931 #ifdef __DEBUG
932         bl_debug_printf(" no detached term. closing screen.\n");
933 #endif
934 
935 #ifdef USE_WIN32GUI
936         close_screen_win32(screen);
937 #else
938         screens[count] = screens[--num_screens];
939         close_screen_intern(screen);
940 #endif
941       } else {
942 #ifdef __DEBUG
943         bl_debug_printf(" using detached term.\n");
944 #endif
945 
946         ui_screen_attach(screen, term);
947       }
948 
949       return;
950     }
951   }
952 }
953 
open_cloned_screen(ui_screen_t * cur_screen,ui_layout_t * layout,int horizontal,const char * sep,int show_dialog)954 static void open_cloned_screen(ui_screen_t *cur_screen, ui_layout_t *layout, int horizontal,
955                                const char *sep, int show_dialog) {
956   vt_char_encoding_t encoding;
957 #if defined(USE_WIN32API) || defined(USE_LIBSSH2)
958   char *default_server;
959   char *new_cmd_line;
960   char *new_cmd_line_dup;
961   char *cmd_path;
962   char **cmd_argv;
963 #endif
964 
965   encoding = main_config.encoding;
966   main_config.encoding = vt_term_get_encoding(cur_screen->term);
967 #if defined(USE_WIN32API) || defined(USE_LIBSSH2)
968   if (!show_dialog) {
969     default_server = main_config.default_server;
970     main_config.default_server = vt_term_get_uri(cur_screen->term);
971   }
972 
973   if ((new_cmd_line = vt_term_get_cmd_line(cur_screen->term)) &&
974       (new_cmd_line_dup = alloca(strlen(new_cmd_line) + 1))) {
975     int argc;
976 
977     cmd_path = main_config.cmd_path;
978     cmd_argv = main_config.cmd_argv;
979 
980     if ((main_config.cmd_argv = bl_argv_alloca(new_cmd_line)) &&
981         bl_arg_str_to_array(main_config.cmd_argv, &argc,
982                             strcpy(new_cmd_line_dup, new_cmd_line))) {
983       main_config.cmd_path = main_config.cmd_argv[0];
984     } else {
985       main_config.cmd_argv = cmd_argv;
986     }
987   }
988 #endif
989 
990 #if defined(USE_WIN32API) || defined(USE_LIBSSH2)
991   open_screen_intern(cur_screen->window.disp->name, NULL, layout, horizontal, sep,
992                      main_config.show_dialog);
993 #else
994   open_screen_intern(cur_screen->window.disp->name, NULL, layout, horizontal, sep, 0);
995 #endif
996 
997   main_config.encoding = encoding;
998 #if defined(USE_WIN32API) || defined(USE_LIBSSH2)
999   if (!show_dialog) {
1000     main_config.default_server = default_server;
1001   }
1002 
1003   if (new_cmd_line) {
1004     main_config.cmd_path = cmd_path;
1005     main_config.cmd_argv = cmd_argv;
1006   }
1007 #endif
1008 }
1009 
open_screen(void * p,ui_screen_t * screen)1010 static void open_screen(void *p, ui_screen_t *screen /* Screen which triggers this event. */
1011                         ) {
1012 #if defined(USE_WIN32API) || defined(USE_LIBSSH2)
1013   open_cloned_screen(screen, NULL, 0, NULL, main_config.show_dialog);
1014 #else
1015   open_cloned_screen(screen, NULL, 0, NULL, 0);
1016 #endif
1017 }
1018 
split_screen(void * p,ui_screen_t * screen,int horizontal,const char * sep)1019 static void split_screen(void *p, ui_screen_t *screen, /* Screen which triggers this event. */
1020                          int horizontal, const char *sep) {
1021   if (UI_SCREEN_TO_LAYOUT(screen)) {
1022 #if defined(USE_WIN32API) || defined(USE_LIBSSH2)
1023     open_cloned_screen(screen, UI_SCREEN_TO_LAYOUT(screen), horizontal, sep,
1024                        main_config.show_dialog);
1025 #else
1026     open_cloned_screen(screen, UI_SCREEN_TO_LAYOUT(screen), horizontal, sep, 0);
1027 #endif
1028   }
1029 }
1030 
close_screen(void * p,ui_screen_t * screen,int force)1031 static int close_screen(void *p, ui_screen_t *screen, /* Screen which triggers this event. */
1032                         int force) {
1033   u_int count;
1034 
1035   if (!force &&
1036       (!UI_SCREEN_TO_LAYOUT(screen) || ui_layout_has_one_child(UI_SCREEN_TO_LAYOUT(screen)))) {
1037     return 0;
1038   }
1039 
1040   for (count = 0; count < num_screens; count++) {
1041     u_int idx;
1042 
1043     if (screen != screens[count]) {
1044       continue;
1045     }
1046 
1047 #ifdef __DEBUG
1048     bl_debug_printf(BL_DEBUG_TAG " screen %p is registered to be closed.\n", screen);
1049 #endif
1050 
1051 #ifdef USE_BEOS
1052     /*
1053      * Don't use ui_close_dead_screens().
1054      * It doesn't clear dead_mask because BWindow::Quit() called from
1055      * close_screen_intern() exists the current thread.
1056      */
1057     screens[count] = screens[--num_screens];
1058     close_screen_intern(screen);
1059 #else
1060     idx = count / MSU; /* count / 8 */
1061     dead_mask[idx] |= (1 << (count - MSU * idx));
1062 #endif
1063 
1064     break;
1065   }
1066 
1067   return 1;
1068 }
1069 
next_screen(void * self,ui_screen_t * screen)1070 static int next_screen(void *self, ui_screen_t *screen) {
1071   if (UI_SCREEN_TO_LAYOUT(screen)) {
1072     return ui_layout_switch_screen(UI_SCREEN_TO_LAYOUT(screen), 0);
1073   } else {
1074     return 0;
1075   }
1076 }
1077 
prev_screen(void * self,ui_screen_t * screen)1078 static int prev_screen(void *self, ui_screen_t *screen) {
1079   if (UI_SCREEN_TO_LAYOUT(screen)) {
1080     return ui_layout_switch_screen(UI_SCREEN_TO_LAYOUT(screen), 1);
1081   } else {
1082     return 0;
1083   }
1084 }
1085 
resize_screen(void * self,ui_screen_t * screen,int horizontal,const char * size)1086 static int resize_screen(void *self, ui_screen_t *screen, int horizontal, const char *size) {
1087   if (UI_SCREEN_TO_LAYOUT(screen)) {
1088     return ui_layout_resize(UI_SCREEN_TO_LAYOUT(screen), screen, horizontal, size);
1089   } else {
1090     return 0;
1091   }
1092 }
1093 
mlclient(void * self,ui_screen_t * screen,char * args,FILE * fp)1094 static int mlclient(void *self, ui_screen_t *screen, char *args,
1095                     FILE *fp /* Stream to output response of mlclient. */
1096                     ) {
1097   char **argv;
1098   int argc;
1099 
1100   if (!(argv = bl_argv_alloca(args)) || !bl_arg_str_to_array(argv, &argc, args)) {
1101     return 0;
1102   }
1103 
1104 #ifdef __DEBUG
1105   {
1106     int i;
1107 
1108     for (i = 0; i < argc; i++) {
1109       bl_msg_printf("%s\n", argv[i]);
1110     }
1111   }
1112 #endif
1113 
1114   if (argc == 0
1115 #if defined(USE_FRAMEBUFFER)
1116       || screen == NULL
1117 #endif
1118       ) {
1119     return 0;
1120   }
1121 
1122   if (argc == 2 && (strcmp(argv[1], "-P") == 0 || strcmp(argv[1], "--ptylist") == 0)) {
1123     /*
1124      * mlclient -P or mlclient --ptylist
1125      */
1126 
1127     vt_term_t **terms;
1128     u_int num;
1129     int count;
1130 
1131     num = vt_get_all_terms(&terms);
1132     for (count = 0; count < num; count++) {
1133       fprintf(fp, "#%s", vt_term_get_slave_name(terms[count]));
1134       if (vt_term_window_name(terms[count])) {
1135         fprintf(fp, "(whose title is %s)", vt_term_window_name(terms[count]));
1136       }
1137       if (vt_term_is_attached(terms[count])) {
1138         fprintf(fp, " is active:)\n");
1139       } else {
1140         fprintf(fp, " is sleeping.zZ\n");
1141       }
1142     }
1143   } else {
1144     bl_conf_t *conf;
1145     ui_main_config_t orig_conf;
1146     char *pty;
1147     int horizontal;
1148     char *sep;
1149 
1150     if (argc >= 2 && *(argv[1]) != '-') {
1151       /*
1152        * mlclient [dev] [options...]
1153        */
1154 
1155       pty = argv[1];
1156       argv[1] = argv[0];
1157       argv = &argv[1];
1158       argc--;
1159     } else {
1160       pty = NULL;
1161     }
1162 
1163     if ((conf = bl_conf_new()) == NULL) {
1164       return 0;
1165     }
1166 
1167     ui_prepare_for_main_config(conf);
1168 
1169     bl_conf_add_opt(conf, '\0', "hsep", 0, "hsep", "");
1170     bl_conf_add_opt(conf, '\0', "vsep", 0, "vsep", "");
1171 
1172     if (!bl_conf_parse_args(conf, &argc, &argv, 1)) {
1173       bl_conf_destroy(conf);
1174 
1175       return 0;
1176     }
1177 
1178     if (screen && UI_SCREEN_TO_LAYOUT(screen)) {
1179       char *p;
1180 
1181       if ((p = bl_conf_get_value(conf, "hsep"))) {
1182         horizontal = 1;
1183       } else if ((p = bl_conf_get_value(conf, "vsep"))) {
1184         horizontal = 0;
1185       } else {
1186         sep = NULL;
1187         goto end_check_sep;
1188       }
1189       if ((sep = alloca(strlen(p) + 1))) {
1190         strcpy(sep, p);
1191       }
1192     } else {
1193       sep = NULL;
1194     }
1195 
1196   end_check_sep:
1197     orig_conf = main_config;
1198 
1199     ui_main_config_init(&main_config, conf, argc, argv);
1200 
1201     bl_conf_destroy(conf);
1202 
1203     if (screen) {
1204       if (sep) {
1205 #if defined(USE_WIN32API) || defined(USE_LIBSSH2)
1206         open_screen_intern(screen->window.disp->name, NULL, UI_SCREEN_TO_LAYOUT(screen),
1207                            horizontal, sep, main_config.show_dialog);
1208 #else
1209         open_screen_intern(screen->window.disp->name, NULL, UI_SCREEN_TO_LAYOUT(screen),
1210                            horizontal, sep, 0);
1211 #endif
1212       } else {
1213         vt_term_t *term;
1214 
1215         if ((term = create_term_intern())) {
1216 #if defined(USE_WIN32API) || defined(USE_LIBSSH2)
1217           if (!open_pty_intern(term, main_config.cmd_path, main_config.cmd_argv,
1218                                &screen->window, main_config.show_dialog))
1219 #else
1220           if (!open_pty_intern(term, main_config.cmd_path, main_config.cmd_argv,
1221                                &screen->window, 0))
1222 #endif
1223           {
1224             vt_destroy_term(term);
1225           } else {
1226             ui_screen_detach(screen);
1227             ui_screen_attach(screen, term);
1228           }
1229         }
1230       }
1231     } else {
1232       vt_term_t *term = NULL;
1233 
1234 #ifdef USE_CONSOLE
1235       if (*main_config.disp_name)
1236 #endif
1237       {
1238         if ((pty && !(term = vt_get_detached_term(pty))) ||
1239             !open_screen_intern(main_config.disp_name, term, NULL, 0, 0, 0)) {
1240 #ifdef DEBUG
1241           bl_warn_printf(BL_DEBUG_TAG " open_screen_intern() failed.\n");
1242 #endif
1243         }
1244       }
1245     }
1246 
1247     ui_main_config_final(&main_config);
1248 
1249     main_config = orig_conf;
1250   }
1251 
1252   /* Flush fp stream because write(2) is called after this function is called.
1253    */
1254   fflush(fp);
1255 
1256   return 1;
1257 }
1258 
1259 /* --- global functions --- */
1260 
ui_screen_manager_init(char * _mlterm_version,u_int _depth,u_int _max_screens_multiple,u_int _num_startup_screens,ui_main_config_t * _main_config)1261 int ui_screen_manager_init(char *_mlterm_version, u_int _depth, u_int _max_screens_multiple,
1262                            u_int _num_startup_screens, ui_main_config_t *_main_config) {
1263   mlterm_version = _mlterm_version;
1264 
1265   depth = _depth;
1266 
1267   main_config = *_main_config;
1268 
1269   max_screens_multiple = _max_screens_multiple;
1270 
1271   if ((dead_mask = calloc(sizeof(*dead_mask), max_screens_multiple)) == NULL) {
1272     return 0;
1273   }
1274 
1275   if (_num_startup_screens > MAX_SCREENS) {
1276     num_startup_screens = MAX_SCREENS;
1277   } else {
1278     num_startup_screens = _num_startup_screens;
1279   }
1280 
1281   if (!vt_term_manager_init(max_screens_multiple)) {
1282     free(dead_mask);
1283 
1284     return 0;
1285   }
1286 
1287   vt_color_config_init();
1288 
1289   ui_shortcut_init(&shortcut);
1290 
1291 /* BACKWARD COMPAT (3.1.7 or before) */
1292 #if 1
1293   {
1294     size_t count;
1295     char key0[] = "Control+Button1";
1296     char key1[] = "Control+Button2";
1297     char key2[] = "Control+Button3";
1298     char key3[] = "Button3";
1299     char *keys[] = {key0, key1, key2, key3};
1300 
1301     for (count = 0; count < sizeof(keys) / sizeof(keys[0]); count++) {
1302       if (main_config.shortcut_strs[count]) {
1303         ui_shortcut_parse(&shortcut, keys[count], main_config.shortcut_strs[count]);
1304       }
1305     }
1306   }
1307 #endif
1308 
1309   if (*main_config.disp_name) {
1310     /*
1311      * setting DISPLAY environment variable to match "--display" option.
1312      */
1313 
1314     char *env;
1315 
1316 #ifdef USE_WAYLAND
1317     if ((env = malloc(16 + strlen(main_config.disp_name) + 1))) {
1318       sprintf(env, "WAYLAND_DISPLAY=%s", main_config.disp_name);
1319       putenv(env);
1320     }
1321 #else
1322     if ((env = malloc(8 + strlen(main_config.disp_name) + 1))) {
1323       sprintf(env, "DISPLAY=%s", main_config.disp_name);
1324       putenv(env);
1325     }
1326 #endif
1327   }
1328 
1329   system_listener.self = NULL;
1330 #ifdef DEBUG
1331   system_listener.exit = __exit;
1332 #else
1333   system_listener.exit = NULL;
1334 #endif
1335 #ifdef MANAGE_ROOT_WINDOWS_BY_MYSELF
1336   system_listener.open_screen = NULL;
1337 #else
1338   system_listener.open_screen = open_screen;
1339 #endif
1340   system_listener.split_screen = split_screen;
1341   system_listener.close_screen = close_screen;
1342   system_listener.next_screen = next_screen;
1343   system_listener.prev_screen = prev_screen;
1344   system_listener.resize_screen = resize_screen;
1345   system_listener.open_pty = open_pty;
1346   system_listener.next_pty = next_pty;
1347   system_listener.prev_pty = prev_pty;
1348   system_listener.close_pty = close_pty;
1349   system_listener.pty_closed = pty_closed;
1350   system_listener.mlclient = mlclient;
1351   system_listener.font_config_updated = font_config_updated;
1352   system_listener.color_config_updated = color_config_updated;
1353 
1354   return 1;
1355 }
1356 
ui_screen_manager_final(void)1357 void ui_screen_manager_final(void) {
1358   u_int count;
1359 
1360   ui_main_config_final(&main_config);
1361 
1362   for (count = 0; count < num_screens; count++) {
1363     close_screen_intern(screens[count]);
1364   }
1365 
1366   free(screens);
1367   free(dead_mask);
1368 
1369   vt_term_manager_final();
1370 
1371   ui_display_close_all();
1372 
1373   vt_color_config_final();
1374   ui_shortcut_final(&shortcut);
1375 }
1376 
1377 #ifdef __ANDROID__
1378 static int suspended;
1379 
1380 void ui_event_source_final(void); /* ui_event_source.h */
1381 
ui_screen_manager_suspend(void)1382 int ui_screen_manager_suspend(void) {
1383   u_int count;
1384 
1385   ui_close_dead_screens();
1386 
1387   for (count = 0; count < num_screens; count++) {
1388     close_screen_intern(screens[count]);
1389   }
1390 
1391   free(screens);
1392   screens = NULL;
1393   num_screens = 0;
1394 
1395   ui_display_close_all();
1396 
1397   ui_event_source_final();
1398 
1399   suspended = 1;
1400 
1401   return 1;
1402 }
1403 #endif
1404 
ui_screen_manager_startup(void)1405 u_int ui_screen_manager_startup(void) {
1406   u_int count;
1407   u_int num_started;
1408 
1409   num_started = 0;
1410 
1411 #ifdef __ANDROID__
1412   if (suspended) {
1413     /* reload ~/.mlterm/main. */
1414     config_saved();
1415     ui_shortcut_final(&shortcut);
1416     ui_shortcut_init(&shortcut);
1417     vt_color_config_final();
1418     vt_color_config_init();
1419   }
1420 #endif
1421 
1422   for (count = 0; count < num_startup_screens; count++) {
1423     if (!open_screen_intern(main_config.disp_name, vt_get_detached_term(NULL), NULL, 0, 0,
1424 #if defined(USE_LIBSSH2) && defined(__ANDROID__)
1425                             !start_with_local_pty
1426 #elif defined(USE_WIN32API)
1427                             1 /* show dialog */
1428 #else
1429                             0
1430 #endif
1431         )) {
1432 #ifdef DEBUG
1433       bl_warn_printf(BL_DEBUG_TAG " open_screen_intern() failed.\n");
1434 #endif
1435     } else {
1436       num_started++;
1437     }
1438   }
1439 
1440   return num_started;
1441 }
1442 
ui_close_dead_screens(void)1443 void ui_close_dead_screens(void) {
1444   if (num_screens > 0) {
1445     int idx;
1446 
1447     for (idx = (num_screens - 1) / MSU; idx >= 0; idx--) {
1448       if (dead_mask[idx]) {
1449         int count;
1450 
1451         for (count = MSU - 1; count >= 0; count--) {
1452           if (dead_mask[idx] & (0x1 << count)) {
1453             ui_screen_t *screen;
1454 
1455 #ifdef __DEBUG
1456             bl_debug_printf(BL_DEBUG_TAG " closing screen %d-%d.", idx, count);
1457 #endif
1458 
1459             screen = screens[idx * MSU + count];
1460             screens[idx * MSU + count] = screens[--num_screens];
1461             close_screen_intern(screen);
1462 
1463 #ifdef __DEBUG
1464             bl_msg_printf(" => Finished. Rest %d\n", num_screens);
1465 #endif
1466           }
1467         }
1468 
1469         memset(&dead_mask[idx], 0, sizeof(dead_mask[idx]));
1470       }
1471     }
1472   }
1473 }
1474 
ui_get_all_screens(ui_screen_t *** _screens)1475 u_int ui_get_all_screens(ui_screen_t ***_screens) {
1476   if (_screens) {
1477     *_screens = screens;
1478   }
1479 
1480   return num_screens;
1481 }
1482 
ui_mlclient(char * args,FILE * fp)1483 int ui_mlclient(char *args, FILE *fp) { return mlclient(NULL, NULL, args, fp); }
1484