1 /* -*- c-basic-offset:2; tab-width:2; indent-tabs-mode:nil -*- */
2 
3 #include "ui_layout.h"
4 
5 #include <pobl/bl_types.h> /* u_int */
6 #include <pobl/bl_str.h>
7 
8 #ifdef USE_BEOS
9 void view_reset_uiwindow(ui_window_t *uiwindow);
10 #endif
11 
12 #ifdef USE_CONSOLE
13 static u_int dummy_arg;
14 #define SEPARATOR_WIDTH ui_get_opened_displays(&dummy_arg)[0]->display->col_width
15 #define SEPARATOR_HEIGHT ui_get_opened_displays(&dummy_arg)[0]->display->line_height
16 #else
17 #define SEPARATOR_WIDTH 1
18 #define SEPARATOR_HEIGHT 1
19 #endif
20 #define SCROLLBAR_WIDTH(scrollbar) (ACTUAL_WIDTH(&(scrollbar).window) + SEPARATOR_WIDTH)
21 #define HAS_MULTI_CHILDREN(layout) ((layout)->term.next[0] || (layout)->term.next[1])
22 
23 /* --- static variables --- */
24 
25 static void (*orig_window_focused)(ui_window_t *);
26 static void (*orig_xterm_set_window_name)(void *, u_char *);
27 static void (*orig_xterm_set_icon_name)(void *, u_char *);
28 
29 /* --- static functions --- */
30 
update_title(ui_screen_t * screen)31 static void update_title(ui_screen_t *screen) {
32   ui_set_window_name(&screen->window, vt_term_window_name(screen->term));
33   ui_set_icon_name(&screen->window, vt_term_icon_name(screen->term));
34 }
35 
window_focused(ui_window_t * win)36 static void window_focused(ui_window_t *win) {
37   ui_layout_t *layout = (ui_layout_t*)win->parent;
38 
39   (*orig_window_focused)(win);
40 
41   if (HAS_MULTI_CHILDREN(layout)) {
42     /*
43      * If two screens in layout and one of them is removed, update_title() is
44      * not called here. It is called at the end of ui_layout_remove_child().
45      */
46     update_title((ui_screen_t*)win);
47   }
48 }
49 
xterm_set_window_name(void * p,u_char * name)50 static void xterm_set_window_name(void *p, u_char *name) {
51   ui_window_t *win = p;
52   ui_layout_t *layout = (ui_layout_t*)win->parent;
53 
54   if (!HAS_MULTI_CHILDREN(layout) || win->inputtable > 0) {
55     (*orig_xterm_set_window_name)(p, name);
56   }
57 }
58 
xterm_set_icon_name(void * p,u_char * name)59 static void xterm_set_icon_name(void *p, u_char *name) {
60   ui_window_t *win = p;
61   ui_layout_t *layout = (ui_layout_t*)win->parent;
62 
63   if (!HAS_MULTI_CHILDREN(layout) || win->inputtable > 0) {
64     (*orig_xterm_set_icon_name)(p, name);
65   }
66 }
67 
modify_separator(u_int separator,u_int total,u_int next_min,u_int unit,u_int fixed,u_int margin)68 static u_int modify_separator(u_int separator, u_int total, u_int next_min, u_int unit, u_int fixed,
69                               u_int margin) {
70   u_int surplus;
71   int dont_round_up;
72 
73   if (total < separator + margin + next_min) {
74 #if 0
75     separator = total - next_min - margin;
76 #else
77     separator = total / 2;
78 #endif
79     dont_round_up = 1;
80   } else {
81     dont_round_up = 0;
82   }
83 
84   if ((surplus = (separator - fixed) % unit) > 0) {
85     if (!dont_round_up && surplus > unit / 2) {
86       separator += (unit - surplus);
87     } else if (separator - fixed > surplus) {
88       separator -= surplus;
89     } else {
90       separator = fixed + unit;
91     }
92   }
93 
94   return separator;
95 }
96 
reset_layout(struct terminal * term,int x,int y,u_int width,u_int height)97 static void reset_layout(struct terminal *term, int x, int y, u_int width, u_int height) {
98   u_int child_width;
99   u_int child_height;
100   int screen_moved;
101   int screen_resized;
102 
103   if (term->separator_x > 0) {
104     term->separator_x = child_width = modify_separator(
105         term->separator_x, width,
106         term->next[0]->screen->window.hmargin * 2 + ui_col_width(term->next[0]->screen) +
107             (term->next[0]->sb_mode != SBM_NONE ? SCROLLBAR_WIDTH(term->next[0]->scrollbar) : 0),
108         ui_col_width(term->screen),
109         term->screen->window.hmargin * 2 +
110             (term->sb_mode != SBM_NONE ? SCROLLBAR_WIDTH(term->scrollbar) : 0),
111         SEPARATOR_WIDTH);
112   } else {
113     child_width = width;
114   }
115 
116   if (term->separator_y > 0) {
117     term->separator_y = child_height = modify_separator(
118         term->separator_y, height,
119         term->next[1]->screen->window.vmargin * 2 + ui_line_height(term->next[1]->screen),
120         ui_line_height(term->screen), term->screen->window.vmargin * 2, SEPARATOR_HEIGHT);
121   } else {
122     child_height = height;
123   }
124 
125   if (term->sb_mode != SBM_NONE) {
126     int sep_x;
127     int sb_moved;
128     int sb_resized;
129 
130     if (term->sb_mode == SBM_RIGHT) {
131       screen_moved = ui_window_move(&term->screen->window, x, y);
132       sb_moved = ui_window_move(&term->scrollbar.window,
133                                  x + child_width - ACTUAL_WIDTH(&term->scrollbar.window), y);
134       sep_x = x + child_width - SCROLLBAR_WIDTH(term->scrollbar);
135     } else {
136       screen_moved = ui_window_move(&term->screen->window, x + SCROLLBAR_WIDTH(term->scrollbar), y);
137       sb_moved = ui_window_move(&term->scrollbar.window, x, y);
138       sep_x = x + ACTUAL_WIDTH(&term->scrollbar.window);
139     }
140 
141     /*
142      * The order of resizing: ui_scrollbar_t -> ui_screen_t
143      *
144      * ui_window_resize_with_margin(screen) may call ui_scrollbar_line_is_added()
145      * which redraws screen by ui_window_update(), so you should resize ui_scrollbar_t
146      * before ui_screen_t.
147      */
148     sb_resized = ui_window_resize_with_margin(&term->scrollbar.window,
149                                               ACTUAL_WIDTH(&term->scrollbar.window),
150                                               child_height, NOTIFY_TO_MYSELF);
151     screen_resized = ui_window_resize_with_margin(&term->screen->window,
152                                                   child_width - SCROLLBAR_WIDTH(term->scrollbar),
153                                                   child_height, NOTIFY_TO_MYSELF);
154 
155 #ifdef MANAGE_SUB_WINDOWS_BY_MYSELF
156     ui_window_fill_with(&UI_SCREEN_TO_LAYOUT(term->screen)->window,
157                         &UI_SCREEN_TO_LAYOUT(term->screen)->window.fg_color, sep_x, y,
158                         SEPARATOR_WIDTH, child_height);
159 #else
160     if (
161 #ifdef USE_WIN32GUI
162     /*
163      * Scrollbar 3 isn't clearly redrawn without this.
164      *
165      *  1              2       3
166      * +-+------------+-+-----+-+----+
167      * | |$ hresize_sc| |$    | |$   |
168      * | |reen +1     | |     | |    |
169      * | |            | |     | |    |
170      */
171         sb_moved
172 #else
173         sb_moved && !sb_resized && UI_SCREEN_TO_LAYOUT(term->screen)->bg_pic
174 #endif
175         ) {
176       ui_window_update_all(&term->scrollbar.window);
177     }
178 #endif
179   } else {
180     screen_moved = ui_window_move(&term->screen->window, x, y);
181     screen_resized = ui_window_resize_with_margin(&term->screen->window, child_width, child_height,
182                                                   NOTIFY_TO_MYSELF);
183   }
184 
185 #ifndef MANAGE_SUB_WINDOWS_BY_MYSELF
186   if (
187 #ifdef USE_QUARTZ
188       /* ui_window_move() clears screen in true transparency on MacOSX/Cocoa. */
189       term->screen->color_man->alpha < 255 ||
190 #endif
191       (screen_moved && !screen_resized && UI_SCREEN_TO_LAYOUT(term->screen)->bg_pic)) {
192     ui_window_update_all(&term->screen->window);
193   }
194 #endif
195 
196   if (term->yfirst) {
197     if (term->next[1] && term->separator_y > 0) {
198       reset_layout(term->next[1], x, y + term->separator_y + SEPARATOR_HEIGHT, width,
199                    height - term->separator_y - SEPARATOR_HEIGHT);
200 #ifdef MANAGE_SUB_WINDOWS_BY_MYSELF
201       ui_window_fill_with(&UI_SCREEN_TO_LAYOUT(term->screen)->window,
202                           &UI_SCREEN_TO_LAYOUT(term->screen)->window.fg_color, x,
203                           y + term->separator_y, width, SEPARATOR_HEIGHT);
204 #endif
205     }
206 
207     if (term->next[0] && term->separator_x > 0) {
208       reset_layout(term->next[0], x + term->separator_x + SEPARATOR_WIDTH, y,
209                    width - term->separator_x - SEPARATOR_WIDTH, child_height);
210 #ifdef MANAGE_SUB_WINDOWS_BY_MYSELF
211       ui_window_fill_with(&UI_SCREEN_TO_LAYOUT(term->screen)->window,
212                           &UI_SCREEN_TO_LAYOUT(term->screen)->window.fg_color,
213                           x + term->separator_x, y, SEPARATOR_WIDTH, child_height);
214 #endif
215     }
216   } else {
217     if (term->next[0] && term->separator_x > 0) {
218       reset_layout(term->next[0], x + term->separator_x + SEPARATOR_WIDTH, y,
219                    width - term->separator_x - SEPARATOR_WIDTH, height);
220 #ifdef MANAGE_SUB_WINDOWS_BY_MYSELF
221       ui_window_fill_with(&UI_SCREEN_TO_LAYOUT(term->screen)->window,
222                           &UI_SCREEN_TO_LAYOUT(term->screen)->window.fg_color,
223                           x + term->separator_x, y, SEPARATOR_WIDTH, height);
224 #endif
225     }
226 
227     if (term->next[1] && term->separator_y > 0) {
228       reset_layout(term->next[1], x, y + term->separator_y + SEPARATOR_HEIGHT, child_width,
229                    height - term->separator_y - SEPARATOR_HEIGHT);
230 #ifdef MANAGE_SUB_WINDOWS_BY_MYSELF
231       ui_window_fill_with(&UI_SCREEN_TO_LAYOUT(term->screen)->window,
232                           &UI_SCREEN_TO_LAYOUT(term->screen)->window.fg_color, x,
233                           y + term->separator_y, child_width, SEPARATOR_HEIGHT);
234 #endif
235     }
236   }
237 }
238 
search_term(struct terminal * term,ui_screen_t * screen)239 static struct terminal *search_term(struct terminal *term, ui_screen_t *screen) {
240   struct terminal *hit;
241 
242   if (term->screen == screen) {
243     return term;
244   }
245 
246   if ((term->next[0] && (hit = search_term(term->next[0], screen))) ||
247       (term->next[1] && (hit = search_term(term->next[1], screen)))) {
248     return hit;
249   }
250 
251   return NULL;
252 }
253 
search_parent_term(struct terminal * term,struct terminal * child)254 static struct terminal *search_parent_term(struct terminal *term, struct terminal *child) {
255   struct terminal *parent;
256 
257   if (term->next[0] == child || term->next[1] == child) {
258     return term;
259   }
260 
261   if ((term->next[0] && (parent = search_parent_term(term->next[0], child))) ||
262       (term->next[1] && (parent = search_parent_term(term->next[1], child)))) {
263     return parent;
264   }
265 
266   return NULL;
267 }
268 
search_next_screen(struct terminal * term,ui_screen_t * screen,int * found)269 static ui_screen_t *search_next_screen(struct terminal *term, ui_screen_t *screen, int *found) {
270   ui_screen_t *hit;
271   int idx;
272   int count;
273 
274   if (term->screen == screen) {
275     *found = 1;
276   }
277 
278   if (term->yfirst) {
279     idx = 0;
280   } else {
281     idx = 1;
282   }
283 
284   for (count = 0; count < 2; count++) {
285     if (term->next[idx]) {
286       if (*found) {
287         return term->next[idx]->screen;
288       } else if ((hit = search_next_screen(term->next[idx], screen, found))) {
289         return hit;
290       }
291     }
292 
293     idx = !idx;
294   }
295 
296   return NULL;
297 }
298 
search_prev_screen(struct terminal * term,ui_screen_t * screen,ui_screen_t ** prev)299 static ui_screen_t *search_prev_screen(struct terminal *term, ui_screen_t *screen,
300                                        ui_screen_t **prev) {
301   int idx;
302   int count;
303 
304   if (*prev && term->screen == screen) {
305     return *prev;
306   }
307 
308   *prev = term->screen;
309 
310   if (term->yfirst) {
311     idx = 0;
312   } else {
313     idx = 1;
314   }
315 
316   for (count = 0; count < 2; count++) {
317     if (term->next[idx] && search_prev_screen(term->next[idx], screen, prev)) {
318       return *prev;
319     }
320 
321     idx = !idx;
322   }
323 
324   return NULL;
325 }
326 
get_current_term(struct terminal * term)327 static struct terminal *get_current_term(struct terminal *term) {
328   struct terminal *current;
329 
330   if (term->screen->window.inputtable > 0) {
331     return term;
332   }
333 
334   if ((term->next[0] && (current = get_current_term(term->next[0]))) ||
335       (term->next[1] && (current = get_current_term(term->next[1])))) {
336     return current;
337   }
338 
339   return NULL;
340 }
341 
get_current_window(ui_layout_t * layout)342 static ui_window_t *get_current_window(ui_layout_t *layout) {
343   struct terminal *term;
344 
345   if ((term = get_current_term(&layout->term))) {
346     return &term->screen->window;
347   } else {
348     return &layout->term.screen->window;
349   }
350 }
351 
get_separator_x(ui_screen_t * screen,u_int sb_width,u_int percent)352 static u_int get_separator_x(ui_screen_t *screen, u_int sb_width, u_int percent /* < 100 */
353                              ) {
354   u_int width;
355   u_int fixed_width;
356   u_int sep_x;
357 
358   width = screen->window.width;
359   fixed_width = screen->window.hmargin * 2 + sb_width;
360 
361   sep_x = (width + fixed_width) * percent / 100;
362   if (sep_x < fixed_width + ui_col_width(screen)) {
363     return 0;
364   }
365 
366 #ifdef PSEUDO_COLOR_DISPLAY
367   if (screen->window.disp->display->pixels_per_byte > 1) {
368     return sep_x - sep_x % screen->window.disp->display->pixels_per_byte - SEPARATOR_WIDTH;
369   } else
370 #endif
371   {
372     return sep_x - (sep_x - fixed_width) % ui_col_width(screen);
373   }
374 }
375 
get_separator_y(ui_screen_t * screen,u_int percent)376 static u_int get_separator_y(ui_screen_t *screen, u_int percent /* < 100 */
377                              ) {
378   u_int height;
379   u_int fixed_height;
380   u_int sep_y;
381 
382   height = screen->window.height;
383   fixed_height = screen->window.vmargin * 2;
384 
385   sep_y = (height + fixed_height) * percent / 100;
386   if (sep_y < fixed_height + ui_line_height(screen)) {
387     return 0;
388   }
389 
390   return sep_y - (sep_y - fixed_height) % ui_line_height(screen);
391 }
392 
393 /*
394  * callbacks of ui_window_t events.
395  */
396 
window_finalized(ui_window_t * win)397 static void window_finalized(ui_window_t *win) {
398   ui_layout_t *layout;
399 
400   layout = (ui_layout_t *)win;
401 
402   ui_layout_destroy(layout);
403 }
404 
405 #ifdef WALL_PICTURE_SIXEL_REPLACES_SYSTEM_PALETTE
reload_color_cache(struct terminal * term,int do_unload)406 static void reload_color_cache(struct terminal *term, int do_unload) {
407   ui_screen_reload_color_cache(term->screen, do_unload);
408 
409   if (term->next[0]) {
410     reload_color_cache(term->next[0], 0);
411   }
412 
413   if (term->next[1]) {
414     reload_color_cache(term->next[1], 0);
415   }
416 }
417 #endif
418 
window_resized(ui_window_t * win)419 static void window_resized(ui_window_t *win) {
420   ui_layout_t *layout;
421   ui_picture_t *pic;
422 
423   layout = (ui_layout_t *)win;
424 
425   if (layout->bg_pic &&
426       (pic = ui_acquire_bg_picture(&layout->window, &layout->pic_mod, layout->pic_file_path))) {
427     ui_window_set_wall_picture(&layout->window, pic->pixmap, 0);
428     ui_release_picture(layout->bg_pic);
429     layout->bg_pic = pic;
430 
431 #ifdef WALL_PICTURE_SIXEL_REPLACES_SYSTEM_PALETTE
432     if (layout->window.disp->depth == 4 && strstr(layout->pic_file_path, "six")) {
433       /*
434        * Color pallette of ui_display can be changed by ui_acquire_bg_picture().
435        * (see ui_display_set_cmap() called from fb/ui_imagelib.c.)
436        */
437       reload_color_cache(&layout->term, 1);
438     }
439 #endif
440   }
441 
442   reset_layout(&layout->term, 0, 0, win->width, win->height);
443 }
444 
child_window_resized(ui_window_t * win,ui_window_t * child)445 static void child_window_resized(ui_window_t *win, ui_window_t *child) {
446   ui_layout_t *layout;
447   u_int actual_width;
448 
449   layout = (ui_layout_t *)win;
450 
451   if (HAS_MULTI_CHILDREN(layout)) {
452     reset_layout(&layout->term, 0, 0, layout->window.width, layout->window.height);
453 
454     return;
455   }
456 
457   if (&layout->term.screen->window == child) {
458     if (layout->term.sb_mode == SBM_NONE) {
459       actual_width = ACTUAL_WIDTH(child);
460     } else {
461       actual_width =
462           ACTUAL_WIDTH(child) + ACTUAL_WIDTH(&layout->term.scrollbar.window) + SEPARATOR_WIDTH;
463     }
464 
465     ui_window_resize_with_margin(&layout->window, actual_width, ACTUAL_HEIGHT(child),
466                                  NOTIFY_TO_NONE);
467 
468     ui_window_resize_with_margin(&layout->term.scrollbar.window,
469                                  ACTUAL_WIDTH(&layout->term.scrollbar.window), ACTUAL_HEIGHT(child),
470                                  NOTIFY_TO_MYSELF);
471 
472     if (layout->term.sb_mode == SBM_RIGHT) {
473       ui_window_move(&layout->term.scrollbar.window,
474                      ACTUAL_WIDTH(&layout->term.screen->window) + SEPARATOR_WIDTH, 0);
475     }
476   } else if (&layout->term.scrollbar.window == child) {
477     if (layout->term.sb_mode == SBM_NONE) {
478       return;
479     }
480 
481     ui_window_resize_with_margin(
482         &layout->window,
483         ACTUAL_WIDTH(child) + ACTUAL_WIDTH(&layout->term.screen->window) + SEPARATOR_WIDTH,
484         ACTUAL_HEIGHT(child), NOTIFY_TO_NONE);
485 
486     if (layout->term.sb_mode == SBM_LEFT) {
487       ui_window_move(&layout->term.screen->window,
488                      ACTUAL_WIDTH(&layout->term.scrollbar.window) + SEPARATOR_WIDTH, 0);
489     }
490   } else {
491 #ifdef DEBUG
492     bl_warn_printf(BL_DEBUG_TAG " illegal child. this event should be invoken only by screen.\n");
493 #endif
494   }
495 }
496 
window_exposed(ui_window_t * win,int x,int y,u_int width,u_int height)497 static void window_exposed(ui_window_t *win, int x, int y, u_int width, u_int height) {
498   ui_window_fill_with(win, &win->fg_color, x, y, width,
499                       height);
500 }
501 
key_pressed(ui_window_t * win,XKeyEvent * event)502 static void key_pressed(ui_window_t *win, XKeyEvent *event) {
503   ui_layout_t *layout;
504   ui_window_t *child;
505 
506   layout = (ui_layout_t *)win;
507   child = get_current_window(layout);
508 
509   /* key_pressed of ui_screen_t can be NULL. (see xterm_lock_keyboard in ui_screen.c) */
510   if (child->key_pressed) {
511     /* dispatch to screen */
512     (*child->key_pressed)(child, event);
513   }
514 }
515 
utf_selection_notified(ui_window_t * win,u_char * buf,size_t len)516 static void utf_selection_notified(ui_window_t *win, u_char *buf, size_t len) {
517   ui_layout_t *layout;
518   ui_window_t *child;
519 
520   layout = (ui_layout_t *)win;
521   child = get_current_window(layout);
522 
523   /* dispatch to screen */
524   (*child->utf_selection_notified)(child, buf, len);
525 }
526 
xct_selection_notified(ui_window_t * win,u_char * buf,size_t len)527 static void xct_selection_notified(ui_window_t *win, u_char *buf, size_t len) {
528   ui_layout_t *layout;
529   ui_window_t *child;
530 
531   layout = (ui_layout_t *)win;
532   child = get_current_window(layout);
533 
534   /* dispatch to screen */
535   (*child->xct_selection_notified)(child, buf, len);
536 }
537 
538 #ifndef DISABLE_XDND
set_xdnd_config(ui_window_t * win,char * dev,char * buf,char * value)539 static void set_xdnd_config(ui_window_t *win, char *dev, char *buf, char *value) {
540   ui_layout_t *layout;
541   ui_window_t *child;
542 
543   layout = (ui_layout_t *)win;
544   child = get_current_window(layout);
545 
546   /* dispatch to screen */
547   (*child->set_xdnd_config)(child, dev, buf, value);
548 }
549 #endif
550 
window_destroyed_intern(struct terminal * term)551 static void window_destroyed_intern(struct terminal *term) {
552   if (term->next[0]) {
553     window_destroyed_intern(term->next[0]);
554   }
555 
556   if (term->next[1]) {
557     window_destroyed_intern(term->next[1]);
558   }
559 
560   /* dispatch to screen */
561   (*term->screen->window.window_destroyed)(&term->screen->window);
562 }
563 
window_destroyed(ui_window_t * win)564 static void window_destroyed(ui_window_t *win) {
565   ui_layout_t *layout;
566 
567   layout = (ui_layout_t *)win;
568 
569   window_destroyed_intern(&layout->term);
570 }
571 
572 #ifndef USE_QUARTZ
573 static void change_sb_mode_intern(struct terminal *term, ui_sb_mode_t new_mode, int dynamic_change);
574 
check_scrollbar_visible(struct terminal * term)575 static void check_scrollbar_visible(struct terminal *term) {
576   if (term->autohide_scrollbar && term->sb_mode == SBM_RIGHT) {
577     if (term->scrollbar.window.button_is_pressing) {
578       term->idling_count = 0;
579     } else if (++term->idling_count == 30) {
580       term->idling_count = 0;
581       change_sb_mode_intern(term, SBM_NONE, 1);
582     }
583   }
584 
585   if (term->next[0]) {
586     check_scrollbar_visible(term->next[0]);
587   }
588 
589   if (term->next[1]) {
590     check_scrollbar_visible(term->next[1]);
591   }
592 }
593 
window_idling(ui_window_t * win)594 static void window_idling(ui_window_t *win) {
595   ui_layout_t *layout;
596 
597   layout = (ui_layout_t *)win;
598 
599   check_scrollbar_visible(&layout->term);
600 }
601 #endif
602 
603 #if 0
604 /*
605  * Overriding methods of ui_scrollbar_t.
606  */
607 
608 static void sb_key_pressed(ui_window_t *win, XKeyEvent *event) {
609   struct terminal term;
610   ui_screen_t **screen;
611 
612   screen = ((void *)win) - (((void *)&term.scrollbar) - ((void *)&term.screen));
613 
614   /* dispatch to screen */
615   (*(*screen)->window.key_pressed)(&(*screen)->window, event);
616 }
617 #endif
618 
619 /*
620  * Overriding methods of vt_screen_listener_t of ui_screen_t.
621  */
622 
line_scrolled_out(void * p)623 static void line_scrolled_out(void *p /* must be ui_screen_t(, or child of ui_layout_t) */
624                               ) {
625   ui_layout_t *layout;
626   struct terminal *term;
627 
628   layout = UI_SCREEN_TO_LAYOUT((ui_screen_t *)p);
629 
630   (*layout->line_scrolled_out)(p);
631 
632   if ((term = search_term(&layout->term, p))) {
633     if (vt_term_log_size_is_unlimited(((ui_screen_t *)p)->term)) {
634       ui_scrollbar_set_num_log_lines(&term->scrollbar,
635                                      vt_term_get_log_size(((ui_screen_t *)p)->term));
636     }
637 
638     ui_scrollbar_line_is_added(&term->scrollbar);
639   }
640 }
641 
642 /*
643  * callbacks of ui_sb_event_listener_t events.
644  */
645 
screen_scroll_to(void * p,int row)646 static void screen_scroll_to(void *p, int row) {
647   struct terminal *term;
648 
649   term = p;
650 
651   ui_screen_scroll_to(term->screen, row);
652 }
653 
screen_scroll_upward(void * p,u_int size)654 static void screen_scroll_upward(void *p, u_int size) {
655   struct terminal *term;
656 
657   term = p;
658 
659   ui_screen_scroll_upward(term->screen, size);
660 }
661 
screen_scroll_downward(void * p,u_int size)662 static void screen_scroll_downward(void *p, u_int size) {
663   struct terminal *term;
664 
665   term = p;
666 
667   ui_screen_scroll_downward(term->screen, size);
668 }
669 
screen_is_static(void * p)670 static int screen_is_static(void *p) {
671   struct terminal *term;
672 
673   term = p;
674 
675   if (vt_term_is_backscrolling(term->screen->term) == BSM_STATIC) {
676     return 1;
677   } else {
678     return 0;
679   }
680 }
681 
682 /*
683  * callbacks of ui_screen_scroll_event_listener_t events.
684  */
685 
bs_mode_exited(void * p)686 static void bs_mode_exited(void *p) {
687   struct terminal *term;
688 
689   term = p;
690 
691   ui_scrollbar_reset(&term->scrollbar);
692 }
693 
scrolled_upward(void * p,u_int size)694 static void scrolled_upward(void *p, u_int size) {
695   struct terminal *term;
696 
697   term = p;
698 
699   ui_scrollbar_move_downward(&term->scrollbar, size);
700 }
701 
scrolled_downward(void * p,u_int size)702 static void scrolled_downward(void *p, u_int size) {
703   struct terminal *term;
704 
705   term = p;
706 
707   ui_scrollbar_move_upward(&term->scrollbar, size);
708 }
709 
scrolled_to(void * p,int row)710 static void scrolled_to(void *p, int row) {
711   struct terminal *term;
712 
713   term = p;
714 
715   ui_scrollbar_move(&term->scrollbar, row);
716 }
717 
log_size_changed(void * p,u_int log_size)718 static void log_size_changed(void *p, u_int log_size) {
719   struct terminal *term;
720 
721   term = p;
722 
723   ui_scrollbar_set_num_log_lines(&term->scrollbar, log_size);
724 }
725 
line_height_changed(void * p,u_int line_height)726 static void line_height_changed(void *p, u_int line_height) {
727   struct terminal *term;
728 
729   term = p;
730 
731   ui_scrollbar_set_line_height(&term->scrollbar, line_height);
732 }
733 
change_fg_color(void * p,char * color)734 static void change_fg_color(void *p, char *color) {
735   struct terminal *term;
736 
737   term = p;
738 
739   ui_scrollbar_set_fg_color(&term->scrollbar, color);
740 }
741 
get_fg_color(void * p)742 static char *get_fg_color(void *p) {
743   struct terminal *term;
744 
745   term = p;
746 
747   return term->scrollbar.fg_color;
748 }
749 
change_bg_color(void * p,char * color)750 static void change_bg_color(void *p, char *color) {
751   struct terminal *term;
752 
753   term = p;
754 
755   ui_scrollbar_set_bg_color(&term->scrollbar, color);
756 }
757 
get_bg_color(void * p)758 static char *get_bg_color(void *p) {
759   struct terminal *term;
760 
761   term = p;
762 
763   return term->scrollbar.bg_color;
764 }
765 
change_view(void * p,char * name)766 static void change_view(void *p, char *name) {
767   struct terminal *term;
768 
769   term = p;
770 
771   ui_scrollbar_change_view(&term->scrollbar, name);
772 }
773 
get_view_name(void * p)774 static char *get_view_name(void *p) {
775   struct terminal *term;
776 
777   term = p;
778 
779   return term->scrollbar.view_name;
780 }
781 
transparent_state_changed(void * p,int is_transparent,ui_picture_modifier_t * pic_mod)782 static void transparent_state_changed(void *p, int is_transparent, ui_picture_modifier_t *pic_mod) {
783   struct terminal *term;
784 
785   term = p;
786 
787   if (is_transparent == 1) {
788     ui_scrollbar_set_transparent(&term->scrollbar, pic_mod, 1);
789   } else {
790     ui_scrollbar_unset_transparent(&term->scrollbar);
791   }
792 }
793 
sb_mode(void * p)794 static ui_sb_mode_t sb_mode(void *p) {
795   struct terminal *term;
796 
797   term = p;
798 
799   if (term->autohide_scrollbar) {
800     return SBM_AUTOHIDE;
801   } else {
802     return term->sb_mode;
803   }
804 }
805 
screen_color_changed(void * p)806 static void screen_color_changed(void *p) {
807   ui_layout_t *layout;
808 
809   layout = UI_SCREEN_TO_LAYOUT(((struct terminal *)p)->screen);
810 
811   if (&layout->term == p) {
812     ui_window_set_fg_color(&layout->window, &layout->term.screen->window.fg_color);
813     ui_window_set_bg_color(&layout->window, &layout->term.screen->window.bg_color);
814   }
815 }
816 
total_width(struct terminal * term)817 static u_int total_width(struct terminal *term) {
818   u_int width;
819 
820   width = ACTUAL_WIDTH(&term->screen->window);
821 
822   if (term->sb_mode != SBM_NONE) {
823     width += (ACTUAL_WIDTH(&term->scrollbar.window) + SEPARATOR_WIDTH);
824   }
825 
826   if (term->separator_x > 0 && term->next[0]) {
827     width += (total_width(term->next[0]) + SEPARATOR_WIDTH);
828   }
829 
830   return width;
831 }
832 
total_height(struct terminal * term)833 static u_int total_height(struct terminal *term) {
834   u_int height;
835 
836   height = ACTUAL_HEIGHT(&term->screen->window);
837 
838   if (term->separator_y > 0 && term->next[1]) {
839     height += (total_height(term->next[1]) + SEPARATOR_HEIGHT);
840   }
841 
842   return height;
843 }
844 
clear_hint_size(struct terminal * term)845 static void clear_hint_size(struct terminal *term) {
846   int count;
847 
848   for (count = 0; count < 2; count++) {
849     if (term->next[count]) {
850       clear_hint_size(term->next[count]);
851     }
852   }
853 
854   term->screen->window.sizehint_flag = 0;
855   term->scrollbar.window.sizehint_flag = 0;
856 }
857 
total_hint_width(struct terminal * term,u_int * width)858 static void total_hint_width(struct terminal *term, u_int *width) {
859   if (term->sb_mode != SBM_NONE) {
860     *width += SEPARATOR_WIDTH;
861   }
862 
863   if (term->next[0]) {
864     *width += SEPARATOR_WIDTH;
865     total_hint_width(term->next[0], width);
866   }
867 
868   if (term->next[1]) {
869     clear_hint_size(term->next[1]);
870   }
871 
872   term->screen->window.sizehint_flag = SIZEHINT_WIDTH;
873   term->scrollbar.window.sizehint_flag = SIZEHINT_WIDTH;
874 }
875 
total_hint_height(struct terminal * term,u_int * height)876 static void total_hint_height(struct terminal *term, u_int *height) {
877   if (term->next[0]) {
878     clear_hint_size(term->next[0]);
879   }
880 
881   if (term->next[1]) {
882     *height += SEPARATOR_HEIGHT;
883     total_hint_height(term->next[1], height);
884   }
885 
886   term->screen->window.sizehint_flag = SIZEHINT_HEIGHT;
887   term->scrollbar.window.sizehint_flag = 0;
888 }
889 
total_hint_size(struct terminal * term,u_int * width,u_int * height)890 static void total_hint_size(struct terminal *term, u_int *width, u_int *height) {
891   if (term->sb_mode != SBM_NONE) {
892     *width += SEPARATOR_WIDTH;
893   }
894 
895   if (term->next[0]) {
896     *width += SEPARATOR_WIDTH;
897     total_hint_width(term->next[0], width);
898   }
899 
900   if (term->next[1]) {
901     *height += SEPARATOR_HEIGHT;
902     total_hint_height(term->next[1], height);
903   }
904 
905   term->screen->window.sizehint_flag = SIZEHINT_HEIGHT|SIZEHINT_WIDTH;
906   term->scrollbar.window.sizehint_flag = SIZEHINT_WIDTH;
907 }
908 
update_normal_hints(ui_layout_t * layout)909 static void update_normal_hints(ui_layout_t *layout) {
910   u_int min_width = 0;
911   u_int min_height = 0;
912 
913   total_hint_size(&layout->term, &min_width, &min_height);
914   ui_window_set_normal_hints(&layout->window, min_width, min_height, 0, 0);
915 }
916 
917 #ifndef USE_QUARTZ
need_idling_event(struct terminal * term)918 static int need_idling_event(struct terminal *term) {
919   if (!term->autohide_scrollbar && (!term->next[0] || !need_idling_event(term->next[0])) &&
920       (!term->next[1] || !need_idling_event(term->next[1]))) {
921     return 0;
922   } else {
923     return 1;
924   }
925 }
926 #endif
927 
change_sb_mode_intern(struct terminal * term,ui_sb_mode_t new_mode,int dynamic_change)928 static void change_sb_mode_intern(struct terminal *term, ui_sb_mode_t new_mode,
929                                   int dynamic_change) {
930   ui_layout_t *layout;
931   ui_sb_mode_t old_mode;
932 
933   layout = UI_SCREEN_TO_LAYOUT(term->screen);
934 
935   if (new_mode == SBM_AUTOHIDE) {
936     if (term->autohide_scrollbar) {
937       return;
938     }
939 
940     new_mode = SBM_NONE;
941     term->autohide_scrollbar = 1;
942     ui_window_add_event_mask(&term->screen->window, PointerMotionMask);
943 #ifndef USE_QUARTZ
944     layout->window.idling = window_idling;
945 #endif
946   } else if (!dynamic_change) {
947     term->autohide_scrollbar = 0;
948     ui_window_remove_event_mask(&term->screen->window, PointerMotionMask);
949     (*term->screen->xterm_listener.set_mouse_report)(term->screen->xterm_listener.self);
950 
951 #ifndef USE_QUARTZ
952     if (!need_idling_event(&layout->term)) {
953       layout->window.idling = NULL;
954     }
955 #endif
956   }
957 
958   if ((old_mode = term->sb_mode) == new_mode) {
959     return;
960   }
961 
962   /*
963    * term->sb_mode should be changed before ui_window_unmap/ui_window_map
964    * for framebuffer. (see fb/ui_window.c)
965    */
966   term->sb_mode = new_mode;
967 
968   if (new_mode == SBM_NONE) {
969     ui_window_unmap(&term->scrollbar.window);
970   }
971 #ifdef MANAGE_ROOT_WINDOWS_BY_MYSELF
972   else if (old_mode == SBM_NONE) {
973     /* scrollbar's height may have been changed. */
974     ui_window_resize_with_margin(&term->scrollbar.window, ACTUAL_WIDTH(&term->scrollbar.window),
975                                  ACTUAL_HEIGHT(&term->screen->window), NOTIFY_TO_MYSELF);
976 
977     /* Don't call ui_window_map() because scrollbar's position may be
978      * inappropriate. */
979     term->scrollbar.window.is_mapped = 1;
980     term->scrollbar.window.x = -1; /* To redraw scrollbar in ui_window_move() */
981 
982     if (new_mode == SBM_LEFT) {
983       /* Shrink window size before ui_window_move() in reset_layout(). */
984       ui_window_resize_with_margin(&term->screen->window, ACTUAL_WIDTH(&term->screen->window) -
985                                                               SCROLLBAR_WIDTH(term->scrollbar),
986                                    ACTUAL_HEIGHT(&term->screen->window), 0);
987     }
988   }
989 
990   reset_layout(&layout->term, 0, 0, layout->window.width, layout->window.height);
991 
992   if (new_mode == SBM_NONE) {
993     /* Window size was enlarged but not exposed. */
994     (*term->screen->window.window_exposed)(
995         &term->screen->window,
996         ACTUAL_WIDTH(&term->screen->window) - SCROLLBAR_WIDTH(term->scrollbar), 0,
997         SCROLLBAR_WIDTH(term->scrollbar), ACTUAL_HEIGHT(&term->screen->window));
998   }
999 #else
1000   else if (old_mode == SBM_NONE) {
1001     ui_window_map(&term->scrollbar.window);
1002 #ifdef MANAGE_SUB_WINDOWS_BY_MYSELF
1003     /* To show hidden scrollbar whose size and position aren't changed. (for wayland) */
1004     term->scrollbar.window.x = -1; /* To redraw scrollbar in ui_window_move() */
1005 #endif
1006   } else {
1007     goto noresize;
1008   }
1009 
1010   if (term->screen->window.x + ACTUAL_WIDTH(&term->screen->window) +
1011         (old_mode != SBM_NONE ? SCROLLBAR_WIDTH(term->scrollbar) : 0) >=
1012       ACTUAL_WIDTH(&layout->window)) {
1013     /* This should be called before ui_window_resize(&layout->window) */
1014     update_normal_hints(layout);
1015     if (ui_window_resize(&layout->window, total_width(&layout->term), total_height(&layout->term),
1016                          NOTIFY_TO_MYSELF)) {
1017       return;
1018     }
1019   }
1020 
1021 noresize:
1022   reset_layout(&layout->term, 0, 0, layout->window.width, layout->window.height);
1023 #endif
1024 
1025   update_normal_hints(layout);
1026 }
1027 
change_sb_mode(void * p,ui_sb_mode_t new_mode)1028 static void change_sb_mode(void *p, ui_sb_mode_t new_mode) {
1029   change_sb_mode_intern(p, new_mode, 0);
1030 }
1031 
term_changed(void * p,u_int log_size,u_int logged_lines)1032 static void term_changed(void *p, u_int log_size, u_int logged_lines) {
1033   struct terminal *term;
1034 
1035   term = p;
1036 
1037   ui_scrollbar_set_num_log_lines(&term->scrollbar, log_size);
1038   ui_scrollbar_set_num_filled_log_lines(&term->scrollbar, logged_lines);
1039 }
1040 
screen_pointer_motion(ui_window_t * win,XMotionEvent * event)1041 static void screen_pointer_motion(ui_window_t *win, XMotionEvent *event) {
1042   struct terminal *term;
1043   ui_layout_t *layout;
1044 
1045   layout = UI_SCREEN_TO_LAYOUT((ui_screen_t *)win);
1046   (*layout->pointer_motion)(win, event);
1047 
1048   term = search_term(&layout->term, (ui_screen_t *)win);
1049 
1050   if (term->autohide_scrollbar) {
1051     ui_sb_mode_t mode;
1052 
1053     if (((int)win->width) - 2 <= event->x && 0 <= event->y && event->y < win->height) {
1054       if (term->scrollbar.window.is_mapped) {
1055         return;
1056       }
1057 
1058       mode = SBM_RIGHT;
1059     } else {
1060       if (!term->scrollbar.window.is_mapped) {
1061         return;
1062       }
1063 
1064       mode = SBM_NONE;
1065     }
1066 
1067     change_sb_mode_intern(term, mode, 1);
1068   }
1069 }
1070 
destroy_term(struct terminal * term)1071 static void destroy_term(struct terminal *term) {
1072   ui_scrollbar_final(&term->scrollbar);
1073 
1074   if (term->next[0]) {
1075     destroy_term(term->next[0]);
1076     free(term->next[0]);
1077   }
1078 
1079   if (term->next[1]) {
1080     destroy_term(term->next[1]);
1081     free(term->next[1]);
1082   }
1083 }
1084 
destroy_screen_bg_pic(ui_screen_t * screen)1085 static void destroy_screen_bg_pic(ui_screen_t *screen) {
1086   free(screen->pic_file_path);
1087   screen->pic_file_path = NULL;
1088   ui_release_picture(screen->bg_pic);
1089   screen->bg_pic = NULL;
1090 }
1091 
destroy_layout_bg_pic(ui_layout_t * layout)1092 static void destroy_layout_bg_pic(ui_layout_t *layout) {
1093   free(layout->pic_file_path);
1094   layout->pic_file_path = NULL;
1095   ui_release_picture(layout->bg_pic);
1096   layout->bg_pic = NULL;
1097 }
1098 
save_screen_bg_pic(ui_layout_t * layout)1099 static void save_screen_bg_pic(ui_layout_t *layout) {
1100   /* layout->bg_pic has been already set. */
1101   ui_release_picture(layout->term.screen->bg_pic);
1102   layout->term.screen->bg_pic = NULL;
1103 
1104   layout->pic_file_path = layout->term.screen->pic_file_path;
1105   layout->term.screen->pic_file_path = NULL;
1106 
1107   layout->pic_mod = layout->term.screen->pic_mod;
1108 }
1109 
restore_screen_bg_pic(ui_layout_t * layout)1110 static void restore_screen_bg_pic(ui_layout_t *layout) {
1111   layout->term.screen->pic_file_path = layout->pic_file_path;
1112   layout->pic_file_path = NULL;
1113   layout->term.screen->bg_pic = layout->bg_pic;
1114   layout->bg_pic = NULL;
1115   layout->term.screen->pic_mod = layout->pic_mod;
1116 }
1117 
1118 /* --- global functions --- */
1119 
ui_layout_new(ui_screen_t * screen,char * view_name,char * fg_color,char * bg_color,ui_sb_mode_t mode,u_int hmargin,u_int vmargin)1120 ui_layout_t *ui_layout_new(ui_screen_t *screen, char *view_name, char *fg_color, char *bg_color,
1121                            ui_sb_mode_t mode, u_int hmargin, u_int vmargin) {
1122   ui_layout_t *layout;
1123   u_int actual_width;
1124   u_int min_width;
1125   int screen_x;
1126   int sb_x;
1127   int sb_map;
1128 
1129   if ((layout = calloc(1, sizeof(ui_layout_t))) == NULL) {
1130     return NULL;
1131   }
1132 
1133   /*
1134    * event callbacks.
1135    */
1136   layout->term.sb_listener.self = &layout->term;
1137   layout->term.sb_listener.screen_scroll_to = screen_scroll_to;
1138   layout->term.sb_listener.screen_scroll_upward = screen_scroll_upward;
1139   layout->term.sb_listener.screen_scroll_downward = screen_scroll_downward;
1140   layout->term.sb_listener.screen_is_static = screen_is_static;
1141 
1142   if (ui_scrollbar_init(
1143           &layout->term.scrollbar, &layout->term.sb_listener, view_name, fg_color, bg_color,
1144           ACTUAL_HEIGHT(&screen->window), ui_line_height(screen),
1145           vt_term_get_log_size(screen->term), vt_term_get_num_logged_lines(screen->term),
1146           screen->window.is_transparent, ui_screen_get_picture_modifier(screen)) == 0) {
1147 #ifdef DEBUG
1148     bl_warn_printf(BL_DEBUG_TAG " ui_scrollbar_init() failed.\n");
1149 #endif
1150 
1151     goto error;
1152   }
1153 #if 0
1154   layout->term.scrollbar.window.key_pressed = sb_key_pressed;
1155 #endif
1156   layout->term.screen = screen;
1157 
1158   /*
1159    * event callbacks.
1160    */
1161   layout->term.screen_scroll_listener.self = &layout->term;
1162   layout->term.screen_scroll_listener.bs_mode_entered = NULL;
1163   layout->term.screen_scroll_listener.bs_mode_exited = bs_mode_exited;
1164   layout->term.screen_scroll_listener.scrolled_upward = scrolled_upward;
1165   layout->term.screen_scroll_listener.scrolled_downward = scrolled_downward;
1166   layout->term.screen_scroll_listener.scrolled_to = scrolled_to;
1167   layout->term.screen_scroll_listener.log_size_changed = log_size_changed;
1168   layout->term.screen_scroll_listener.line_height_changed = line_height_changed;
1169   layout->term.screen_scroll_listener.change_fg_color = change_fg_color;
1170   layout->term.screen_scroll_listener.fg_color = get_fg_color;
1171   layout->term.screen_scroll_listener.change_bg_color = change_bg_color;
1172   layout->term.screen_scroll_listener.bg_color = get_bg_color;
1173   layout->term.screen_scroll_listener.change_view = change_view;
1174   layout->term.screen_scroll_listener.view_name = get_view_name;
1175   layout->term.screen_scroll_listener.transparent_state_changed = transparent_state_changed;
1176   layout->term.screen_scroll_listener.sb_mode = sb_mode;
1177   layout->term.screen_scroll_listener.change_sb_mode = change_sb_mode;
1178   layout->term.screen_scroll_listener.term_changed = term_changed;
1179   layout->term.screen_scroll_listener.screen_color_changed = screen_color_changed;
1180 
1181   ui_set_screen_scroll_listener(screen, &layout->term.screen_scroll_listener);
1182 
1183   layout->line_scrolled_out = screen->screen_listener.line_scrolled_out;
1184   screen->screen_listener.line_scrolled_out = line_scrolled_out;
1185 
1186   if (mode == SBM_AUTOHIDE) {
1187     layout->term.sb_mode = SBM_NONE;
1188     layout->term.autohide_scrollbar = 1;
1189   } else {
1190     layout->term.sb_mode = mode;
1191   }
1192   layout->pointer_motion = screen->window.pointer_motion;
1193   screen->window.pointer_motion = screen_pointer_motion;
1194 
1195   if (layout->term.sb_mode == SBM_NONE) {
1196     actual_width = ACTUAL_WIDTH(&screen->window);
1197 
1198     min_width = 0;
1199   } else {
1200     actual_width = ACTUAL_WIDTH(&screen->window) + SCROLLBAR_WIDTH(layout->term.scrollbar);
1201 
1202     min_width = SEPARATOR_WIDTH;
1203   }
1204 
1205   if (ui_window_init(&layout->window, actual_width, ACTUAL_HEIGHT(&screen->window), min_width, 0, 0,
1206                      0, hmargin, vmargin, 0, 0) == 0) {
1207 #ifdef DEBUG
1208     bl_warn_printf(BL_DEBUG_TAG " ui_window_init() failed.\n");
1209 #endif
1210 
1211     goto error;
1212   }
1213 
1214   if (layout->term.sb_mode == SBM_RIGHT) {
1215     screen_x = 0;
1216     sb_x = ACTUAL_WIDTH(&screen->window) + SEPARATOR_WIDTH;
1217     sb_map = 1;
1218   } else if (layout->term.sb_mode == SBM_LEFT) {
1219     screen_x = ACTUAL_WIDTH(&layout->term.scrollbar.window) + SEPARATOR_WIDTH;
1220     sb_x = 0;
1221     sb_map = 1;
1222   } else /* if( layout->term.sb_mode == SBM_NONE) */
1223   {
1224     screen_x = sb_x = 0; /* overlaying scrollbar window */
1225     sb_map = 0;
1226   }
1227 
1228   orig_window_focused = screen->window.window_focused;
1229   orig_xterm_set_window_name = screen->xterm_listener.set_window_name;
1230   orig_xterm_set_icon_name = screen->xterm_listener.set_icon_name;
1231   screen->window.window_focused = window_focused;
1232   screen->xterm_listener.set_window_name = xterm_set_window_name;
1233   screen->xterm_listener.set_icon_name = xterm_set_icon_name;
1234 
1235   if (!ui_window_add_child(&layout->window, &layout->term.scrollbar.window, sb_x, 0, sb_map) ||
1236       !ui_window_add_child(&layout->window, &screen->window, screen_x, 0, 1)) {
1237     goto error;
1238   }
1239 
1240   /*
1241    * event call backs.
1242    */
1243   ui_window_add_event_mask(&layout->window, KeyPressMask);
1244   layout->window.window_finalized = window_finalized;
1245   layout->window.window_resized = window_resized;
1246   layout->window.child_window_resized = child_window_resized;
1247   layout->window.window_exposed = window_exposed;
1248   layout->window.key_pressed = key_pressed;
1249   layout->window.utf_selection_notified = utf_selection_notified;
1250   layout->window.xct_selection_notified = xct_selection_notified;
1251   layout->window.window_destroyed = window_destroyed;
1252 #ifndef DISABLE_XDND
1253   layout->window.set_xdnd_config = set_xdnd_config;
1254 #endif
1255 
1256   if (layout->term.autohide_scrollbar) {
1257 #ifndef USE_QUARTZ
1258     layout->window.idling = window_idling;
1259 #endif
1260     ui_window_add_event_mask(&screen->window, PointerMotionMask);
1261   }
1262 
1263   return layout;
1264 
1265 error:
1266   free(layout);
1267 
1268   return NULL;
1269 }
1270 
ui_layout_destroy(ui_layout_t * layout)1271 void ui_layout_destroy(ui_layout_t *layout) {
1272   if (layout->bg_pic) {
1273     destroy_layout_bg_pic(layout);
1274   }
1275 
1276   destroy_term(&layout->term);
1277   free(layout);
1278 }
1279 
ui_layout_add_child(ui_layout_t * layout,ui_screen_t * screen,int horizontal,const char * sep_str)1280 int ui_layout_add_child(ui_layout_t *layout, ui_screen_t *screen, int horizontal,
1281                         const char *sep_str /* "XX%" or "XX" */
1282                         ) {
1283   struct terminal *next;
1284   struct terminal *term;
1285   u_int sep = 50;
1286   int is_percent = 1;
1287 
1288   if (sep_str) {
1289     char *p;
1290 
1291     if ((p = strchr(sep_str, '%'))) {
1292       if (!bl_str_n_to_uint(&sep, sep_str, p - sep_str) || sep >= 100 || sep == 0) {
1293         return 0;
1294       }
1295     } else {
1296       if (!bl_str_to_uint(&sep, sep_str) || sep == 0) {
1297         return 0;
1298       }
1299 
1300       is_percent = 0;
1301     }
1302   }
1303 
1304   if (!(next = calloc(1, sizeof(struct terminal)))) {
1305     return 0;
1306   }
1307 
1308   if (!(term = get_current_term(&layout->term))) {
1309     term = &layout->term;
1310   }
1311 
1312   if (horizontal) {
1313     u_int orig_separator_x;
1314     u_int sb_width;
1315 
1316     orig_separator_x = term->separator_x;
1317 
1318     if (term->sb_mode != SBM_NONE) {
1319       sb_width = SCROLLBAR_WIDTH(term->scrollbar);
1320     } else {
1321       sb_width = 0;
1322     }
1323 
1324     if (is_percent ? ((term->separator_x = get_separator_x(term->screen, sb_width, sep)) == 0)
1325                    : ((term->separator_x = screen->window.hmargin * 2 +
1326                                            ui_col_width(term->screen) * sep + sb_width) >=
1327                       ACTUAL_WIDTH(&term->screen->window) + sb_width)) {
1328       term->separator_x = orig_separator_x;
1329       free(next);
1330 
1331       return 0;
1332     }
1333 
1334     next->next[0] = term->next[0];
1335     term->next[0] = next;
1336 
1337     if (orig_separator_x > 0) {
1338       next->separator_x = orig_separator_x - term->separator_x;
1339     }
1340   } else {
1341     u_int orig_separator_y;
1342 
1343     orig_separator_y = term->separator_y;
1344 
1345     if (is_percent ? ((term->separator_y = get_separator_y(term->screen, sep)) == 0)
1346                    : ((term->separator_y = screen->window.vmargin * 2 +
1347                                            ui_line_height(term->screen) * sep) >=
1348                       ACTUAL_HEIGHT(&term->screen->window))) {
1349       term->separator_y = orig_separator_y;
1350       free(next);
1351 
1352       return 0;
1353     }
1354 
1355     if (term->separator_x == 0) {
1356       term->yfirst = 1;
1357     }
1358 
1359     next->next[1] = term->next[1];
1360     term->next[1] = next;
1361 
1362     if (orig_separator_y > 0) {
1363       next->separator_y = orig_separator_y - term->separator_y;
1364     }
1365   }
1366 
1367   next->sb_listener = term->sb_listener;
1368   next->sb_listener.self = next;
1369 
1370   ui_scrollbar_init(
1371       &next->scrollbar, &next->sb_listener, term->scrollbar.view_name, term->scrollbar.fg_color,
1372       term->scrollbar.bg_color, ACTUAL_HEIGHT(&screen->window), ui_line_height(screen),
1373       vt_term_get_log_size(screen->term), vt_term_get_num_logged_lines(screen->term),
1374       screen->window.is_transparent, ui_screen_get_picture_modifier(screen));
1375 #if 0
1376   next->scrollbar.window.key_pressed = sb_key_pressed;
1377 #endif
1378   ui_window_add_child(&layout->window, &next->scrollbar.window, 0, 0, 0);
1379   ui_window_show(&next->scrollbar.window, 0);
1380 
1381   next->screen_scroll_listener = term->screen_scroll_listener;
1382   next->screen_scroll_listener.self = next;
1383 
1384   next->screen = screen;
1385   ui_set_screen_scroll_listener(screen, &next->screen_scroll_listener);
1386   screen->screen_listener.line_scrolled_out = line_scrolled_out;
1387   screen->window.window_focused = window_focused;
1388   screen->xterm_listener.set_window_name = xterm_set_window_name;
1389   screen->xterm_listener.set_icon_name = xterm_set_icon_name;
1390   ui_window_add_child(&layout->window, &screen->window, 0, 0, 0);
1391 
1392   if (screen->pic_file_path) {
1393     destroy_screen_bg_pic(screen);
1394   }
1395 
1396   ui_window_show(&screen->window, 0);
1397 
1398   if (!ui_window_has_wall_picture(&layout->window) && layout->term.screen->pic_file_path) {
1399     if ((layout->bg_pic = ui_acquire_bg_picture(&layout->window, &layout->term.screen->pic_mod,
1400                                                 layout->term.screen->pic_file_path))) {
1401       save_screen_bg_pic(layout);
1402       ui_window_set_wall_picture(&layout->window, layout->bg_pic->pixmap, 0);
1403 
1404 #ifdef WALL_PICTURE_SIXEL_REPLACES_SYSTEM_PALETTE
1405       if (layout->window.disp->depth == 4 && strstr(layout->pic_file_path, "six")) {
1406         /*
1407          * Color pallette of ui_display can be changed by
1408          * ui_acquire_bg_picture().
1409          * (see ui_display_set_cmap() called from fb/ui_imagelib.c.)
1410          */
1411         reload_color_cache(&layout->term, 1);
1412       }
1413 #endif
1414     }
1415   }
1416 
1417   next->sb_mode = term->sb_mode;
1418   if ((next->autohide_scrollbar = term->autohide_scrollbar)) {
1419     ui_window_add_event_mask(&screen->window, PointerMotionMask);
1420   }
1421   screen->window.pointer_motion = screen_pointer_motion;
1422 
1423   reset_layout(&layout->term, 0, 0, layout->window.width, layout->window.height);
1424 
1425   if (term->sb_mode != SBM_NONE) {
1426     ui_window_map(&next->scrollbar.window);
1427   }
1428   ui_window_map(&screen->window);
1429 
1430   ui_window_set_input_focus(&term->screen->window);
1431 
1432   update_normal_hints(layout);
1433 
1434   return 1;
1435 }
1436 
ui_layout_remove_child(ui_layout_t * layout,ui_screen_t * screen)1437 int ui_layout_remove_child(ui_layout_t *layout, ui_screen_t *screen) {
1438   struct terminal *term;
1439   int idx2;
1440 #ifndef MANAGE_SUB_WINDOWS_BY_MYSELF
1441   u_int w_surplus;
1442   u_int h_surplus;
1443 #endif
1444 
1445   if (!HAS_MULTI_CHILDREN(layout)) {
1446     return 0;
1447   }
1448 
1449   term = search_term(&layout->term, screen);
1450 
1451   ui_scrollbar_final(&term->scrollbar);
1452   ui_window_unmap(&term->scrollbar.window);
1453   ui_window_final(&term->scrollbar.window);
1454 
1455   if (term->yfirst) {
1456     idx2 = 0;
1457   } else {
1458     idx2 = 1;
1459   }
1460 
1461   if (!term->next[idx2]) {
1462     idx2 = !idx2;
1463   }
1464 
1465   if (term == &layout->term) {
1466     struct terminal *next;
1467 
1468     if (idx2 == 0) {
1469       if (term->separator_y > 0) {
1470         term->next[0]->separator_y = term->separator_y;
1471       }
1472     } else {
1473       if (term->separator_x > 0) {
1474         term->next[1]->separator_x = term->separator_x;
1475       }
1476     }
1477 
1478     term->next[idx2]->yfirst = term->yfirst;
1479 
1480     next = term->next[idx2];
1481 
1482     if (term->next[!idx2]) {
1483       struct terminal *parent;
1484       int empty_idx;
1485 
1486       /* Search from 'next' for a term whose child is NULL. */
1487       parent = search_parent_term(next, NULL);
1488       if (parent->next[!idx2] == NULL) {
1489         empty_idx = !idx2;
1490       } else {
1491         empty_idx = idx2;
1492       }
1493 
1494       parent->next[empty_idx] = term->next[!idx2];
1495       if (empty_idx == 0) {
1496         parent->separator_x =
1497           get_separator_x(parent->screen,
1498                           parent->sb_mode == SBM_NONE ? 0 : SCROLLBAR_WIDTH(parent->scrollbar),
1499                           100);
1500       } else {
1501         parent->separator_y = get_separator_y(parent->screen, 100);
1502       }
1503     }
1504 
1505     *term = *next;
1506     term->screen_scroll_listener.self = term;
1507     ui_set_screen_scroll_listener(term->screen, &term->screen_scroll_listener);
1508     term->sb_listener.self = term;
1509     term->scrollbar.sb_listener = &term->sb_listener;
1510 #ifndef USE_QUARTZ
1511     term->scrollbar.view->win = &term->scrollbar.window;
1512 #ifdef USE_BEOS
1513     view_reset_uiwindow(&term->scrollbar.window);
1514 #endif
1515 #endif
1516 
1517     term = next;
1518   } else {
1519     struct terminal *parent;
1520     int idx;
1521 
1522     parent = search_parent_term(&layout->term, term);
1523 
1524     if (parent->next[0] == term) {
1525       idx = 0;
1526     } else {
1527       idx = 1;
1528     }
1529 
1530     if (term->next[idx2]) {
1531       if (idx2 == 0) {
1532         if (term->separator_y > 0) {
1533           term->next[0]->separator_y = term->separator_y;
1534         }
1535       } else {
1536         if (term->separator_x > 0) {
1537           term->next[1]->separator_x = term->separator_x;
1538         }
1539       }
1540       term->next[idx2]->yfirst = term->yfirst;
1541 
1542       parent->next[idx] = term->next[idx2];
1543 
1544       if (term->next[!idx2]) {
1545         parent = search_parent_term(parent->next[idx], NULL);
1546         if (parent->next[!idx2] == NULL) {
1547           parent->next[!idx2] = term->next[!idx2];
1548         } else {
1549           parent->next[idx2] = term->next[!idx2];
1550         }
1551       }
1552     } else {
1553       parent->next[idx] = NULL;
1554 
1555       if (idx == 0) {
1556         parent->separator_x = 0;
1557       } else {
1558         parent->separator_y = 0;
1559       }
1560     }
1561   }
1562 
1563   ui_window_remove_child(&layout->window, &term->scrollbar.window);
1564   ui_window_remove_child(&layout->window, &screen->window);
1565   screen->screen_listener.line_scrolled_out = layout->line_scrolled_out;
1566   ui_window_unmap(&screen->window);
1567 
1568   if (layout->bg_pic && !HAS_MULTI_CHILDREN(layout)) {
1569     restore_screen_bg_pic(layout);
1570     ui_window_unset_wall_picture(&layout->window, 0);
1571   }
1572 
1573 #ifndef MANAGE_SUB_WINDOWS_BY_MYSELF
1574   if (!HAS_MULTI_CHILDREN(layout)) {
1575     w_surplus = (layout->window.width - layout->term.screen->window.hmargin * 2 -
1576                  (layout->term.sb_mode != SBM_NONE ? SCROLLBAR_WIDTH(layout->term.scrollbar) : 0)) %
1577                 ui_col_width(layout->term.screen);
1578     h_surplus = (layout->window.height - layout->term.screen->window.vmargin * 2) %
1579                 ui_line_height(layout->term.screen);
1580   } else {
1581     w_surplus = h_surplus = 0;
1582   }
1583 
1584   if (w_surplus > 0 || h_surplus > 0) {
1585     ui_window_resize(&layout->window, layout->window.width - w_surplus,
1586                      layout->window.height - h_surplus, NOTIFY_TO_MYSELF);
1587   } else
1588 #endif
1589   {
1590     reset_layout(&layout->term, 0, 0, layout->window.width, layout->window.height);
1591   }
1592 
1593   update_normal_hints(layout);
1594 
1595 #ifndef MANAGE_SUB_WINDOWS_BY_MYSELF
1596   if (ui_screen_attached(screen)) {
1597     /* Revert the size of vt_term to the original one. */
1598     ui_window_resize_with_margin(
1599         &screen->window, ACTUAL_WIDTH(&layout->window) -
1600                              (term->sb_mode != SBM_NONE ? SCROLLBAR_WIDTH(term->scrollbar) : 0),
1601         ACTUAL_HEIGHT(&layout->window), NOTIFY_TO_MYSELF);
1602   }
1603 #endif
1604 
1605   free(term);
1606 
1607   ui_window_set_input_focus(&layout->term.screen->window);
1608 
1609   /* See window_focused() above */
1610   if (!HAS_MULTI_CHILDREN(layout)) {
1611     update_title(layout->term.screen);
1612   }
1613 
1614 #ifndef USE_QUARTZ
1615   if (layout->window.idling && !need_idling_event(&layout->term)) {
1616     layout->window.idling = NULL;
1617   }
1618 #endif
1619 
1620   return 1;
1621 }
1622 
ui_layout_switch_screen(ui_layout_t * layout,int prev)1623 int ui_layout_switch_screen(ui_layout_t *layout, int prev) {
1624   ui_screen_t *screen;
1625 
1626   if (!HAS_MULTI_CHILDREN(layout)) {
1627     return 0;
1628   }
1629 
1630   if (prev) {
1631     screen = NULL;
1632 
1633     search_prev_screen(&layout->term, (ui_screen_t *)get_current_window(layout), &screen);
1634   } else {
1635     int found = 0;
1636 
1637     if (!(screen = search_next_screen(&layout->term, (ui_screen_t *)get_current_window(layout),
1638                                       &found))) {
1639       screen = layout->term.screen;
1640     }
1641   }
1642 
1643   ui_window_set_input_focus(&screen->window);
1644 
1645   return 1;
1646 }
1647 
ui_layout_resize(ui_layout_t * layout,ui_screen_t * screen,int horizontal,const char * size_str)1648 int ui_layout_resize(ui_layout_t *layout, ui_screen_t *screen, int horizontal,
1649                      const char *size_str /* "N", "+N", "-N" or "N%" */) {
1650   struct terminal *term;
1651   struct terminal *child = NULL;
1652   int size;
1653   int is_percent;
1654   char *p;
1655 
1656   if ((p = strchr(size_str, '%'))) {
1657     if (!bl_str_n_to_int(&size, size_str, p - size_str) || size <= 0) {
1658       return 0;
1659     }
1660     is_percent = 1;
1661   } else {
1662     if (!bl_str_to_int(&size, *size_str == '+' ? size_str + 1: size_str) || size == 0) {
1663       return 0;
1664     }
1665     is_percent = 0;
1666   }
1667 
1668   if (!(term = search_term(&layout->term, screen))) {
1669     term = &layout->term;
1670   }
1671 
1672   if (horizontal) {
1673     if (is_percent) {
1674       if (term->separator_x > 0) {
1675         term->separator_x = total_width(term) * size / 100;
1676       }
1677     } else {
1678       int step;
1679 
1680       if (*size_str == '+' || *size_str == '-') {
1681         step = size;
1682       } else {
1683         step = size - vt_term_get_cols(screen->term);
1684       }
1685 
1686       while (term->separator_x == 0) {
1687         if (!(term = search_parent_term(&layout->term, (child = term)))) {
1688           return 0;
1689         }
1690       }
1691 
1692       if (term->next[0] == child) {
1693         step = -step;
1694       }
1695 
1696       step *= ui_col_width(term->screen);
1697 
1698       if (step < 0 && term->separator_x < abs(step)) {
1699         return 0;
1700       }
1701 
1702       term->separator_x += step;
1703     }
1704   } else {
1705     if (is_percent) {
1706       if (term->separator_y > 0) {
1707         term->separator_y = total_height(term) * size / 100;
1708       }
1709     } else {
1710       int step;
1711 
1712       if (*size_str == '+' || *size_str == '-') {
1713         step = size;
1714       } else {
1715         step = size - vt_term_get_rows(screen->term);
1716       }
1717 
1718       while (term->separator_y == 0) {
1719         if (!(term = search_parent_term(&layout->term, (child = term)))) {
1720           return 0;
1721         }
1722       }
1723 
1724       if (term->next[1] == child) {
1725         step = -step;
1726       }
1727 
1728       step *= ui_line_height(term->screen);
1729 
1730       if (step < 0 && term->separator_y < abs(step)) {
1731         return 0;
1732       }
1733 
1734       term->separator_y += step;
1735     }
1736   }
1737 
1738   reset_layout(&layout->term, 0, 0, layout->window.width, layout->window.height);
1739 
1740   return 1;
1741 }
1742