4 #include <stdint.h> /* for uint8_t, uint16_t, uint32_t, UINT16_MAX */
5 #include <ncurses.h> /* for typedefs WINDOW, chtype, wresize(), getmaxx(), */
6 /* getmaxy(), delwin(), mvwaddch(), mvwaddstr(), */
7 /* newpad(), wnoutrefres(), erase(), werase(), */
8 /* pnoutrefresh(), doupdate(), getmaxyx() */
9 #include <stdlib.h> /* for malloc(), free() */
10 #include <string.h> /* for strlen(), strnlen(), memcpy() */
11 #include "yx_uint16.h" /* for struct yx_uint16 */
12 #include "misc.h" /* for center_offset(), try_malloc() */
13 #include "main.h" /* for world global */
14 #include "rexit.h" /* for exit_err() */
18 /* Fit virtual screen's width to minimum width demanded by current windows'
21 static void refit_pad();
23 /* Update geometry (sizes, positions) of window "w" and its successors in the
24 * window chain. For the positioning algorithm place_win() is used.
26 static void update_wins(struct Win * w);
27 static void place_win(struct Win * w);
29 /* Draw scroll hint (a line saying that there are "dist" more elements of
30 * "unit" further into the direction symbolized by the "dir" char) into virtual
31 * screen pad, onto an appropriate edge of either a window or the screen; the
32 * left or right edge if "dir" is "<" or ">", or the upper or lower edge if it
33 * is "^" or "v". "start" should be either the start coordinate of a window's
34 * frame or .y=0, .x=wm->pad_offset if it describes the virtual screen pad.
35 * winscroll_hint() and padscroll_hint() are wrappers to simplify these uses.
37 static void scroll_hint(struct yx_uint16 fsize, char dir, uint16_t dist,
38 char * unit, struct yx_uint16 start);
39 static void winscroll_hint(struct Win * w, char dir, uint16_t dist);
40 static void padscroll_hint(char dir, uint16_t dist);
42 /* Draw contents of all windows in window chain from window "w" onwards. */
43 static void draw_wins(struct Win * w);
45 /* draw_win_borderlines() draws the vertical and horizontal borders of window
46 * "w" sans corners into the virtual screen "pad", and draws the top border
47 * line as the windows' title bar (highlighted if the window is described
48 * active by "active" being == 1).
50 * draw_wins_borderlines() calls draw_win_borderlines() recursively on all
51 * windows from "w" on. "w_active" is a pointer to the one window that
52 * draw_win_borderlines() is supposed to handle as the active window.
54 * Finally, draw_wins_bordercorners() draws into "pad" the borders of window "w"
55 * and all its successors.
57 static void draw_win_borderlines(struct Win * w, char active, WINDOW * pad);
58 static void draw_wins_borderlines(struct Win * w, struct Win * w_active,
60 static void draw_wins_bordercorners(struct Win * w, WINDOW * pad);
62 /* Shift active window forwards / backwards in window chain. */
63 static void shift_win_forward();
64 static void shift_win_backward();
68 static void refit_pad()
70 /* Determine rightmost window column. */
71 uint32_t lastwcol = 0;
72 struct Win * wp = world.wmeta->chain_start;
75 if ((uint32_t) wp->start.x + (uint32_t) wp->framesize.x > lastwcol + 1)
77 lastwcol = (uint32_t) wp->start.x + (uint32_t) wp->framesize.x - 1;
82 /* Only resize the pad if the rightmost window column has changed. */
83 char * err_s = "refit_pad() extends virtual screen beyond legal sizes.";
84 char * err_m = "refit_pad() triggers memory alloc error via wresize().";
85 if (getmaxx(world.wmeta->pad) + 1 != lastwcol)
87 uint8_t t = (lastwcol + 2 > UINT16_MAX);
89 t = wresize(world.wmeta->pad, getmaxy(world.wmeta->pad), lastwcol + 2);
96 static void update_wins(struct Win * w)
102 update_wins(w->next);
108 static void place_win(struct Win * w)
110 /* First window goes into the upper-left corner. */
112 w->start.y = 1; /* Leave space for title bar. */
116 /* Non-first window fallbacks to: fit rightwards of rightmost border. */
117 struct Win * w_top = w->prev;
118 while (w_top->start.y != 1)
122 w->start.x = w_top->start.x + w_top->framesize.x + 1;
124 /* Fit window below its predecessor if that one directly thrones over
125 * empty space wide and high enough.
127 uint16_t w_prev_maxy = w->prev->start.y + w->prev->framesize.y;
128 if ( w->framesize.x <= w->prev->framesize.x
129 && w->framesize.y < world.wmeta->padsize.y - w_prev_maxy)
131 w->start.x = w->prev->start.x;
132 w->start.y = w_prev_maxy + 1;
135 /* Failing that, try to open a new sub column below the nearest
136 * predecessor window that thrones over enough empty space.
140 struct Win * w_up = w->prev;
141 struct Win * w_upup = w_up;
143 while (w_up != w_top)
148 if (w_up->start.y != w_upup->start.y)
152 w_upup = w_upup->prev;
154 w_prev_maxy = w_upup->start.y + w_upup->framesize.y;
155 widthdiff = (w_upup->start.x + w_upup->framesize.x)
156 - (w_up->start.x + w_up->framesize.x);
157 if ( w->framesize.y < world.wmeta->padsize.y - w_prev_maxy
158 && w->framesize.x < widthdiff)
160 w->start.x = w_up->start.x + w_up->framesize.x + 1 ;
161 w->start.y = w_prev_maxy + 1;
172 static void scroll_hint(struct yx_uint16 fsize, char dir, uint16_t dist,
173 char * unit, struct yx_uint16 start)
175 /* Decide on alignment (vertical/horizontal?), thereby hint text space. */
176 char * more = "more";
177 uint16_t dsc_space = fsize.x;
178 if ('<' == dir || '>' == dir)
181 } /* vv-- 10 = max strlen for uint16_t */
182 char scrolldsc[1 + strlen(more) + 1 + 10 + 1 + strlen(unit) + 1 + 1];
183 sprintf(scrolldsc, " %d %s %s ", dist, more, unit);
185 /* Decide on offset of the description text inside the scroll hint line. */
186 uint16_t dsc_offset = 1;
187 if (dsc_space > strlen(scrolldsc) + 1)
189 dsc_offset = (dsc_space - strlen(scrolldsc)) / 2;
192 /* Draw scroll hint line as dir symbols bracketing description text. */
193 uint16_t draw_offset = 0;
196 draw_offset = fsize.x - 1;
200 draw_offset = fsize.y - 1;
203 for (; q < dsc_space; q++)
205 chtype c = dir | A_REVERSE;
206 if (q >= dsc_offset && q < strlen(scrolldsc) + dsc_offset)
208 c = scrolldsc[q - dsc_offset] | A_REVERSE;
210 if ('<' == dir || '>' == dir)
212 mvwaddch(world.wmeta->pad, start.y + q, start.x + draw_offset, c);
216 mvwaddch(world.wmeta->pad, start.y + draw_offset, start.x + q, c);
222 static void padscroll_hint(char dir, uint16_t dist)
224 struct yx_uint16 start;
226 start.x = world.wmeta->pad_offset;
227 scroll_hint(world.wmeta->padsize, dir, dist, "columns", start);
232 static void winscroll_hint(struct Win * w, char dir, uint16_t dist)
234 char * unit = "lines";
235 if ('<' == dir || '>' == dir)
239 struct yx_uint16 start = w->start;
240 scroll_hint(w->framesize, dir, dist, unit, start);
245 static void draw_wins(struct Win * w)
248 uint16_t y, x, size_y, size_x;
249 size_y = w->winmapsize.y;
250 size_x = w->winmapsize.x;
251 uint16_t offset_y = center_offset(w->center.y, size_y, w->framesize.y);
252 uint16_t offset_x = center_offset(w->center.x, size_x, w->framesize.x);
253 for (y = offset_y; y < w->framesize.y + offset_y && y < size_y; y++)
255 for (x = offset_x; x < w->framesize.x + offset_x && x < size_x; x++)
257 chtype ch = w->winmap[(y * w->winmapsize.x) + x];
258 mvwaddch(world.wmeta->pad, w->start.y + (y - offset_y),
259 w->start.x + (x - offset_x), ch);
268 winscroll_hint(w, '^', offset_y + 1);
270 if (size_y > offset_y + w->framesize.y)
272 winscroll_hint(w, 'v', size_y - ((offset_y + w->framesize.y) - 1));
276 winscroll_hint(w, '<', offset_x + 1);
278 if (size_x > offset_x + w->framesize.x)
280 winscroll_hint(w, '>', size_x - ((offset_x + w->framesize.x) - 1));
284 return draw_wins(w->next);
290 static void draw_win_borderlines(struct Win * w, char active, WINDOW * pad)
292 /* Draw vertical and horizontal border lines. */
294 for (y = w->start.y; y <= w->start.y + w->framesize.y; y++)
296 mvwaddch(pad, y, w->start.x - 1, '|');
297 mvwaddch(pad, y, w->start.x + w->framesize.x, '|');
299 for (x = w->start.x; x <= w->start.x + w->framesize.x; x++)
301 mvwaddch(pad, w->start.y - 1, x, '-');
302 mvwaddch(pad, w->start.y + w->framesize.y, x, '-');
305 /* Draw as much as possible of the title into center of top border line. */
306 char min_title_length_visible = 3; /* min. 1 char + 2 padding/decoration */
307 if (w->framesize.x >= min_title_length_visible)
309 uint16_t title_offset = 0;
310 if (w->framesize.x > strlen(w->title) + 2)
312 title_offset = (w->framesize.x - (strlen(w->title) + 2)) / 2;
313 } /* +2 is for padding/decoration */
314 uint16_t length_visible = strnlen(w->title, w->framesize.x - 2);
315 char title[length_visible + 3];
316 char decoration = ' ';
321 memcpy(title + 1, w->title, length_visible);
322 title[0] = title[length_visible + 1] = decoration;
323 title[length_visible + 2] = '\0';
324 mvwaddstr(pad, w->start.y - 1, w->start.x + title_offset, title);
330 static void draw_wins_borderlines(struct Win * w, struct Win * w_active,
338 draw_win_borderlines(w, active, pad);
341 draw_wins_borderlines(w->next, w_active, pad);
347 static void draw_wins_bordercorners(struct Win * w, WINDOW * pad)
349 mvwaddch(pad, w->start.y - 1, w->start.x - 1, '+');
350 mvwaddch(pad, w->start.y - 1, w->start.x + w->framesize.x, '+');
351 mvwaddch(pad, w->start.y + w->framesize.y, w->start.x - 1, '+');
352 mvwaddch(pad, w->start.y + w->framesize.y, w->start.x + w->framesize.x,'+');
355 draw_wins_bordercorners(w->next, pad);
361 static void shift_win_forward()
363 if (world.wmeta->active == world.wmeta->chain_end)
365 world.wmeta->chain_end = world.wmeta->active->prev;
366 world.wmeta->chain_end->next = 0;
367 world.wmeta->active->next = world.wmeta->chain_start;
368 world.wmeta->active->next->prev = world.wmeta->active;
369 world.wmeta->chain_start = world.wmeta->active;
370 world.wmeta->chain_start->prev = 0;
374 struct Win * old_prev = world.wmeta->active->prev;
375 struct Win * old_next = world.wmeta->active->next;
376 if (world.wmeta->chain_end == world.wmeta->active->next)
378 world.wmeta->chain_end = world.wmeta->active;
379 world.wmeta->active->next = 0;
383 world.wmeta->active->next = old_next->next;
384 world.wmeta->active->next->prev = world.wmeta->active;
386 if (world.wmeta->chain_start == world.wmeta->active)
388 world.wmeta->chain_start = old_next;
392 old_prev->next = old_next;
394 old_next->prev = old_prev;
395 old_next->next = world.wmeta->active;
396 world.wmeta->active->prev = old_next;
402 static void shift_win_backward()
404 if (world.wmeta->active == world.wmeta->chain_start)
406 world.wmeta->chain_start = world.wmeta->active->next;
407 world.wmeta->chain_start->prev = 0;
408 world.wmeta->active->prev = world.wmeta->chain_end;
409 world.wmeta->active->prev->next = world.wmeta->active;
410 world.wmeta->chain_end = world.wmeta->active;
411 world.wmeta->chain_end->next = 0;
415 struct Win * old_prev = world.wmeta->active->prev;
416 struct Win * old_next = world.wmeta->active->next;
417 if (world.wmeta->chain_start == world.wmeta->active->prev)
419 world.wmeta->chain_start = world.wmeta->active;
420 world.wmeta->active->prev = 0;
424 world.wmeta->active->prev = old_prev->prev;
425 world.wmeta->active->prev->next = world.wmeta->active;
427 if (world.wmeta->chain_end == world.wmeta->active)
429 world.wmeta->chain_end = old_prev;
433 old_next->prev = old_prev;
435 old_prev->next = old_next;
436 old_prev->prev = world.wmeta->active;
437 world.wmeta->active->next = old_prev;
443 extern void init_win_meta(WINDOW * screen)
445 char * f_name = "init_win_meta()";
446 char * err_s = "init_win_meta() creates virtual screen beyond legal size.";
447 char * err_m = "init_win_meta() triggers memory alloc error via newpad().";
448 world.wmeta = try_malloc(sizeof(struct WinMeta), f_name);
449 world.wmeta->screen = screen;
450 uint32_t maxy_test = getmaxy(screen);
451 uint32_t maxx_test = getmaxx(screen);
452 uint8_t test = (maxy_test > UINT16_MAX || maxx_test > UINT16_MAX);
453 exit_err(test, err_s);
454 world.wmeta->padsize.y = maxy_test;
455 world.wmeta->padsize.x = maxx_test;
456 world.wmeta->chain_start = 0;
457 world.wmeta->chain_end = 0;
458 world.wmeta->pad_offset = 0;
459 world.wmeta->pad = newpad(world.wmeta->padsize.y, 1);
460 exit_err(NULL == world.wmeta->pad, err_m);
461 world.wmeta->active = 0;
466 extern void init_win(struct Win ** wp, char * title, int16_t height,
467 int16_t width, void * func)
469 char * f_name = "init_win()";
470 struct Win * w = try_malloc(sizeof(struct Win), f_name);
476 w->title = try_malloc(strlen(title) + 1, f_name);
477 sprintf(w->title, "%s", title);
483 w->framesize.x = width;
487 w->framesize.x = world.wmeta->padsize.x + width;
489 if (0 < height && height <= world.wmeta->padsize.y - 1)
491 w->framesize.y = height;
493 else if (0 >= height && world.wmeta->padsize.y + (height - 1) > 0)
495 w->framesize.y = world.wmeta->padsize.y + (height - 1);
502 extern void free_winmeta()
504 delwin(world.wmeta->pad);
510 extern void free_win(struct Win * win)
518 extern void append_win(struct Win * w)
520 if (0 != world.wmeta->chain_start)
522 w->prev = world.wmeta->chain_end;
523 world.wmeta->chain_end->next = w;
527 world.wmeta->active = w;
528 world.wmeta->chain_start = w;
530 world.wmeta->chain_end = w;
536 extern void suspend_win(struct Win * w)
538 if (world.wmeta->chain_start != w)
540 w->prev->next = w->next;
544 world.wmeta->chain_start = w->next;
546 char pad_refitted = 0;
547 if (world.wmeta->chain_end != w)
549 w->next->prev = w->prev;
550 if (world.wmeta->active == w)
552 world.wmeta->active = w->next;
554 update_wins(w->next); /* Positioning of successor windows may be */
555 pad_refitted = 1; /* affected / need correction. Note that */
556 } /* update_wins() already refits the pad, */
557 else /* voiding later need for that. */
559 world.wmeta->chain_end = w->prev;
560 if (world.wmeta->active == w)
562 world.wmeta->active = w->prev;
569 if (0 == pad_refitted)
577 extern void reset_pad_offset(uint16_t new_offset)
580 && (new_offset < world.wmeta->pad_offset
581 || new_offset + world.wmeta->padsize.x < getmaxx(world.wmeta->pad)))
583 world.wmeta->pad_offset = new_offset;
589 extern void resize_active_win(struct yx_uint16 size)
591 if (0 != world.wmeta->active
592 && size.x > 0 && size.y > 0 && size.y < world.wmeta->padsize.y)
594 world.wmeta->active->framesize = size;
595 update_wins(world.wmeta->active); /* Positioning of following */
596 } /* windows may be affected. */
601 extern void cycle_active_win(char dir)
603 if (0 != world.wmeta->active)
607 if (world.wmeta->active->next != 0)
609 world.wmeta->active = world.wmeta->active->next;
613 world.wmeta->active = world.wmeta->chain_start;
618 if (world.wmeta->active->prev != 0)
620 world.wmeta->active = world.wmeta->active->prev;
624 world.wmeta->active = world.wmeta->chain_end;
632 extern void shift_active_win(char dir)
634 if ( 0 == world.wmeta->active /* No shifting with < 2 windows visible. */
635 || world.wmeta->chain_start == world.wmeta->chain_end)
645 shift_win_backward();
647 update_wins(world.wmeta->chain_start);
652 extern void draw_all_wins()
654 /* Empty everything before filling it a-new. */
656 wnoutrefresh(world.wmeta->screen);
657 werase(world.wmeta->pad);
658 if (world.wmeta->chain_start)
661 /* Draw windows' borders first, then windows. */
662 draw_wins_borderlines(world.wmeta->chain_start, world.wmeta->active,
664 draw_wins_bordercorners(world.wmeta->chain_start, world.wmeta->pad);
665 draw_wins(world.wmeta->chain_start);
667 /* Draw virtual screen scroll hints. */
668 if (world.wmeta->pad_offset > 0)
670 padscroll_hint('<', world.wmeta->pad_offset + 1);
672 uint16_t size_x = getmaxx(world.wmeta->pad);
673 uint16_t right_edge = world.wmeta->pad_offset + world.wmeta->padsize.x;
674 if (right_edge < size_x - 1)
676 padscroll_hint('>', size_x - right_edge);
679 /* Write pad segment to be shown on physical screen to screen buffer. */
680 pnoutrefresh(world.wmeta->pad, 0, world.wmeta->pad_offset, 0, 0,
681 world.wmeta->padsize.y, world.wmeta->padsize.x - 1);
684 /* Only at the end write accumulated changes to the physical screen. */