+ {
+ dsc_space = fsize.y;
+ } /* vv-- 10 = max strlen for uint16_t */
+ char scrolldsc[1 + strlen(more) + 1 + 10 + 1 + strlen(unit) + 1 + 1];
+ sprintf(scrolldsc, " %d %s %s ", dist, more, unit);
+
+ /* Decide on offset of the description text inside the scroll hint line. */
+ uint16_t dsc_offset = 1;
+ if (dsc_space > strlen(scrolldsc) + 1)
+ {
+ dsc_offset = (dsc_space - strlen(scrolldsc)) / 2;
+ }
+
+ /* Draw scroll hint line as dir symbols bracketing description text. */
+ uint16_t draw_offset = 0;
+ if ('>' == dir)
+ {
+ draw_offset = fsize.x - 1;
+ }
+ else if ('v' == dir)
+ {
+ draw_offset = fsize.y - 1;
+ }
+ uint16_t q = 0;
+ for (; q < dsc_space; q++)
+ {
+ chtype c = dir | A_REVERSE;
+ if (q >= dsc_offset && q < strlen(scrolldsc) + dsc_offset)
+ {
+ c = scrolldsc[q - dsc_offset] | A_REVERSE;
+ }
+ if ('<' == dir || '>' == dir)
+ {
+ mvwaddch(world.wmeta->pad, start.y + q, start.x + draw_offset, c);
+ }
+ else
+ {
+ mvwaddch(world.wmeta->pad, start.y + draw_offset, start.x + q, c);
+ }
+ }
+}
+
+
+static void padscroll_hint(char dir, uint16_t dist)
+{
+ struct yx_uint16 start;
+ start.y = 0;
+ start.x = world.wmeta->pad_offset;
+ scroll_hint(world.wmeta->padsize, dir, dist, "columns", start);
+}
+
+
+
+static void winscroll_hint(struct Win * w, char dir, uint16_t dist)
+{
+ char * unit = "lines";
+ if ('<' == dir || '>' == dir)
+ {
+ unit = "columns";
+ }
+ struct yx_uint16 start = w->start;
+ scroll_hint(w->framesize, dir, dist, unit, start);
+}
+
+
+
+static void draw_wins(struct Win * w)
+{
+ w->draw(w);
+ uint16_t y, x, size_y, size_x;
+ size_y = w->winmapsize.y;
+ size_x = w->winmapsize.x;
+ uint16_t offset_y = center_offset(w->center.y, size_y, w->framesize.y);
+ uint16_t offset_x = center_offset(w->center.x, size_x, w->framesize.x);
+ for (y = offset_y; y < w->framesize.y + offset_y && y < size_y; y++)
+ {
+ for (x = offset_x; x < w->framesize.x + offset_x && x < size_x; x++)
+ {
+ chtype ch = w->winmap[(y * w->winmapsize.x) + x];
+ mvwaddch(world.wmeta->pad, w->start.y + (y - offset_y),
+ w->start.x + (x - offset_x), ch);
+ }
+ }
+ free(w->winmap);
+ w->winmap = NULL;
+ w->winmapsize.y = 0;
+ w->winmapsize.x = 0;
+ if (offset_y > 0)
+ {
+ winscroll_hint(w, '^', offset_y + 1);
+ }
+ if (size_y > offset_y + w->framesize.y)
+ {
+ winscroll_hint(w, 'v', size_y - ((offset_y + w->framesize.y) - 1));
+ }
+ if (offset_x > 0)
+ {
+ winscroll_hint(w, '<', offset_x + 1);
+ }
+ if (size_x > offset_x + w->framesize.x)
+ {
+ winscroll_hint(w, '>', size_x - ((offset_x + w->framesize.x) - 1));
+ }
+ if (0 != w->next)
+ {
+ return draw_wins(w->next);
+ }
+}
+
+
+
+static void draw_win_borderlines(struct Win * w, char active, WINDOW * pad)
+{
+ /* Draw vertical and horizontal border lines. */
+ uint16_t y, x;
+ for (y = w->start.y; y <= w->start.y + w->framesize.y; y++)
+ {
+ mvwaddch(pad, y, w->start.x - 1, '|');
+ mvwaddch(pad, y, w->start.x + w->framesize.x, '|');
+ }
+ for (x = w->start.x; x <= w->start.x + w->framesize.x; x++)
+ {
+ mvwaddch(pad, w->start.y - 1, x, '-');
+ mvwaddch(pad, w->start.y + w->framesize.y, x, '-');
+ }
+
+ /* Draw as much as possible of the title into center of top border line. */
+ char min_title_length_visible = 3; /* min. 1 char + 2 padding/decoration */
+ if (w->framesize.x >= min_title_length_visible)
+ {
+ uint16_t title_offset = 0;
+ if (w->framesize.x > strlen(w->title) + 2)
+ {
+ title_offset = (w->framesize.x - (strlen(w->title) + 2)) / 2;
+ } /* +2 is for padding/decoration */
+ uint16_t length_visible = strnlen(w->title, w->framesize.x - 2);
+ char title[length_visible + 3];
+ char decoration = ' ';
+ if (1 == active)
+ {
+ decoration = '$';
+ }
+ memcpy(title + 1, w->title, length_visible);
+ title[0] = title[length_visible + 1] = decoration;
+ title[length_visible + 2] = '\0';
+ mvwaddstr(pad, w->start.y - 1, w->start.x + title_offset, title);
+ }
+}
+
+
+
+static void draw_wins_borderlines(struct Win * w, struct Win * w_active,
+ WINDOW * pad)
+{
+ char active = 0;
+ if (w == w_active)
+ {
+ active = 1;
+ }
+ draw_win_borderlines(w, active, pad);
+ if (0 != w->next)
+ {
+ draw_wins_borderlines(w->next, w_active, pad);
+ }
+}
+
+
+
+static void draw_wins_bordercorners(struct Win * w, WINDOW * pad)
+{
+ mvwaddch(pad, w->start.y - 1, w->start.x - 1, '+');
+ mvwaddch(pad, w->start.y - 1, w->start.x + w->framesize.x, '+');
+ mvwaddch(pad, w->start.y + w->framesize.y, w->start.x - 1, '+');
+ mvwaddch(pad, w->start.y + w->framesize.y, w->start.x + w->framesize.x,'+');
+ if (0 != w->next)
+ {
+ draw_wins_bordercorners(w->next, pad);
+ }
+}
+
+
+
+static void shift_win_forward()
+{
+ if (world.wmeta->active == world.wmeta->chain_end)
+ {
+ world.wmeta->chain_end = world.wmeta->active->prev;
+ world.wmeta->chain_end->next = 0;
+ world.wmeta->active->next = world.wmeta->chain_start;
+ world.wmeta->active->next->prev = world.wmeta->active;
+ world.wmeta->chain_start = world.wmeta->active;
+ world.wmeta->chain_start->prev = 0;
+ }
+ else
+ {
+ struct Win * old_prev = world.wmeta->active->prev;
+ struct Win * old_next = world.wmeta->active->next;
+ if (world.wmeta->chain_end == world.wmeta->active->next)
+ {
+ world.wmeta->chain_end = world.wmeta->active;
+ world.wmeta->active->next = 0;
+ }
+ else
+ {
+ world.wmeta->active->next = old_next->next;
+ world.wmeta->active->next->prev = world.wmeta->active;
+ }
+ if (world.wmeta->chain_start == world.wmeta->active)
+ {
+ world.wmeta->chain_start = old_next;
+ }
+ else
+ {
+ old_prev->next = old_next;
+ }
+ old_next->prev = old_prev;
+ old_next->next = world.wmeta->active;
+ world.wmeta->active->prev = old_next;
+ }
+}
+
+
+
+static void shift_win_backward()
+{
+ if (world.wmeta->active == world.wmeta->chain_start)
+ {
+ world.wmeta->chain_start = world.wmeta->active->next;
+ world.wmeta->chain_start->prev = 0;
+ world.wmeta->active->prev = world.wmeta->chain_end;
+ world.wmeta->active->prev->next = world.wmeta->active;
+ world.wmeta->chain_end = world.wmeta->active;
+ world.wmeta->chain_end->next = 0;
+ }
+ else
+ {
+ struct Win * old_prev = world.wmeta->active->prev;
+ struct Win * old_next = world.wmeta->active->next;
+ if (world.wmeta->chain_start == world.wmeta->active->prev)
+ {
+ world.wmeta->chain_start = world.wmeta->active;
+ world.wmeta->active->prev = 0;
+ }
+ else
+ {
+ world.wmeta->active->prev = old_prev->prev;
+ world.wmeta->active->prev->next = world.wmeta->active;
+ }
+ if (world.wmeta->chain_end == world.wmeta->active)
+ {
+ world.wmeta->chain_end = old_prev;
+ }
+ else
+ {
+ old_next->prev = old_prev;
+ }
+ old_prev->next = old_next;
+ old_prev->prev = world.wmeta->active;
+ world.wmeta->active->next = old_prev;
+ }
+}
+
+
+
+extern void init_win_meta(WINDOW * screen)
+{
+ char * f_name = "init_win_meta()";
+ char * err_s = "init_win_meta() creates virtual screen beyond legal size.";
+ char * err_m = "init_win_meta() triggers memory alloc error via newpad().";
+ world.wmeta = try_malloc(sizeof(struct WinMeta), f_name);
+ world.wmeta->screen = screen;
+ uint32_t maxy_test = getmaxy(screen);
+ uint32_t maxx_test = getmaxx(screen);
+ uint8_t test = (maxy_test > UINT16_MAX || maxx_test > UINT16_MAX);
+ exit_err(test, err_s);
+ world.wmeta->padsize.y = maxy_test;
+ world.wmeta->padsize.x = maxx_test;
+ world.wmeta->chain_start = 0;
+ world.wmeta->chain_end = 0;
+ world.wmeta->pad_offset = 0;
+ world.wmeta->pad = newpad(world.wmeta->padsize.y, 1);
+ exit_err(NULL == world.wmeta->pad, err_m);
+ world.wmeta->active = 0;
+}
+
+
+
+extern void init_win(struct Win ** wp, char * title, int16_t height,
+ int16_t width, void * func)
+{
+ char * f_name = "init_win()";
+ struct Win * w = try_malloc(sizeof(struct Win), f_name);
+ w->prev = 0;
+ w->next = 0;
+ w->winmapsize.y = 0;
+ w->winmapsize.x = 0;
+ w->winmap = NULL;
+ w->title = try_malloc(strlen(title) + 1, f_name);
+ sprintf(w->title, "%s", title);
+ w->draw = func;
+ w->center.y = 0;
+ w->center.x = 0;
+ if (0 < width)
+ {
+ w->framesize.x = width;
+ }
+ else if (0 >= width)
+ {
+ w->framesize.x = world.wmeta->padsize.x + width;
+ }
+ if (0 < height && height <= world.wmeta->padsize.y - 1)
+ {
+ w->framesize.y = height;
+ }
+ else if (0 >= height && world.wmeta->padsize.y + (height - 1) > 0)
+ {
+ w->framesize.y = world.wmeta->padsize.y + (height - 1);
+ }
+ *wp = w;
+}
+
+
+
+extern void free_winmeta()
+{
+ delwin(world.wmeta->pad);
+ free(world.wmeta);
+}
+
+
+
+extern void free_win(struct Win * win)
+{
+ free(win->title);
+ free(win);
+}
+
+
+
+extern void append_win(struct Win * w)
+{
+ if (0 != world.wmeta->chain_start)
+ {
+ w->prev = world.wmeta->chain_end;
+ world.wmeta->chain_end->next = w;
+ }