4 #include <stdint.h> /* for uint8_t, uint16_t, uint32_t, UINT16_MAX */
5 #include <ncurses.h> /* for typedefs chtype, wresize(), getmaxx(), getmaxy(),
6 * delwin(), mvwaddch(), mvwaddstr(), newpad(), erase(),
7 * wnoutrefresh(), werase(), pnoutrefresh(), doupdate()
9 #include <stdlib.h> /* for 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 /* Make virtual screen just wide enough to contain all visible windows. */
19 static void refit_pad();
21 /* Update geometry (sizes, positions) of window "w" and its successors in the
22 * window chain. Use place_win() for the positioning algorithm.
24 static void update_wins(struct Win * w);
25 static void place_win(struct Win * w);
27 /* Draw scroll hint (a line saying that there are "dist" more elements of "unit"
28 * further into the direction symbolized by "dir") into virtual screen, onto an
29 * appropriate edge of a window or the screen; the left/right edge if "dir" is
30 * "<"/">", or the top/bottom edge if it is "^"/"v". "start" be either the start
31 * coordinate of a window's frame, or .y=0, .x=wm->pad_offset for the virtual
32 * screen. winscroll_hint() and padscroll_hint() are wrappers to both cases.
34 static void scroll_hint(struct yx_uint16 fsize, char dir, uint16_t dist,
35 char * unit, struct yx_uint16 start);
36 static void winscroll_hint(struct Win * w, char dir, uint16_t dist);
37 static void padscroll_hint(char dir, uint16_t dist);
39 /* Draw contents of all windows in window chain from window "w" onwards. */
40 static void draw_wins(struct Win * w);
42 /* draw_win_borderlines() draws vertical/horizontal borders of window "w" sans
43 * corners into the virtual screen. It draws the top border line as the windows'
44 * title bar (highlighted if the window is selected as active). It is called
45 * recursively by draw_wins_borderlines() on all windows from "w" on.
46 * draw_wins_bordercorners() draws the border corners of "w" and its successors.
48 static void draw_win_borderlines(struct Win * w);
49 static void draw_wins_borderlines(struct Win * w);
50 static void draw_wins_bordercorners(struct Win * w);
52 /* Shift active window forwards / backwards in window chain. */
53 static void shift_win_forward();
54 static void shift_win_backward();
58 static void refit_pad()
60 /* Determine rightmost window column. */
61 uint32_t lastwcol = 0;
62 struct Win * wp = world.wmeta->chain_start;
65 if ((uint32_t) wp->start.x + (uint32_t) wp->framesize.x > lastwcol + 1)
67 lastwcol = (uint32_t) wp->start.x + (uint32_t) wp->framesize.x - 1;
72 /* Only resize the pad if the rightmost window column has changed. */
73 char * err_s = "refit_pad() extends virtual screen beyond legal sizes.";
74 char * err_m = "refit_pad() triggers memory alloc error via wresize().";
75 if (getmaxx(world.wmeta->pad) + 1 != lastwcol)
77 uint8_t t = (lastwcol + 2 > UINT16_MAX);
79 t = wresize(world.wmeta->pad, getmaxy(world.wmeta->pad), lastwcol + 2);
86 static void update_wins(struct Win * w)
98 static void place_win(struct Win * w)
100 /* If w is first window, it goes into the top left corner. */
102 w->start.y = 1; /* Leave space for title bar. */
106 /* If not, fit w's top left to top right of last top predecessor. */
107 struct Win * w_top = w->prev;
108 while (w_top->start.y != 1)
112 w->start.x = w_top->start.x + w_top->framesize.x + 1;
114 /* Fit w's top left to bottom left of its ->prev if enough space. */
115 uint16_t w_prev_maxy = w->prev->start.y + w->prev->framesize.y;
116 if ( w->framesize.x <= w->prev->framesize.x
117 && w->framesize.y < world.wmeta->padsize.y - w_prev_maxy)
119 w->start.x = w->prev->start.x;
120 w->start.y = w_prev_maxy + 1;
123 /* Failing that, try to fit w' top left to the top right of the last
124 * predecessor w_test 1) not followed by windows with a left corner
125 * further rightwards than its own 2) with enough space rightwards for w
126 * until the bottom right of w_thr directly throning over it 3) and with
127 * this same space extending far enough to the bottom for fitting in w.
131 struct Win * w_test = w->prev;
133 while (w_test != w_top)
135 w_thr = w_test->prev;
136 for (; w_test->start.y <= w_thr->start.y; w_thr = w_thr->prev);
137 uint16_t w_thr_bottom = w_thr->start.y + w_thr->framesize.y;
138 uint16_t free_width = (w_thr->start.x + w_thr->framesize.x)
139 - (w_test->start.x + w_test->framesize.x);
140 if ( w->framesize.y < world.wmeta->padsize.y - w_thr_bottom
141 && w->framesize.x < free_width)
143 w->start.x = w_test->start.x + w_test->framesize.x + 1;
144 w->start.y = w_thr_bottom + 1;
155 static void scroll_hint(struct yx_uint16 fsize, char dir, uint16_t dist,
156 char * unit, struct yx_uint16 start)
158 /* Decide on alignment (vertical/horizontal?), thereby hint text space. */
159 char * more = "more";
160 uint16_t dsc_space = fsize.x;
161 if ('<' == dir || '>' == dir)
164 } /* vv-- 10 = max strlen for uint16_t */
165 char scrolldsc[1 + strlen(more) + 1 + 10 + 1 + strlen(unit) + 1 + 1];
166 sprintf(scrolldsc, " %d %s %s ", dist, more, unit);
168 /* Decide on offset of the description text inside the scroll hint line. */
169 uint16_t dsc_offset = 1;
170 if (dsc_space > strlen(scrolldsc) + 1)
172 dsc_offset = (dsc_space - strlen(scrolldsc)) / 2;
175 /* Draw scroll hint line as dir symbols bracketing description text. */
176 uint16_t draw_offset = 0;
179 draw_offset = fsize.x - 1;
183 draw_offset = fsize.y - 1;
186 for (; q < dsc_space; q++)
188 chtype c = dir | A_REVERSE;
189 if (q >= dsc_offset && q < strlen(scrolldsc) + dsc_offset)
191 c = scrolldsc[q - dsc_offset] | A_REVERSE;
193 if ('<' == dir || '>' == dir)
195 mvwaddch(world.wmeta->pad, start.y + q, start.x + draw_offset, c);
198 mvwaddch(world.wmeta->pad, start.y + draw_offset, start.x + q, c);
203 static void padscroll_hint(char dir, uint16_t dist)
205 struct yx_uint16 start;
207 start.x = world.wmeta->pad_offset;
208 scroll_hint(world.wmeta->padsize, dir, dist, "columns", start);
213 static void winscroll_hint(struct Win * w, char dir, uint16_t dist)
215 char * unit = "lines";
216 if ('<' == dir || '>' == dir)
220 struct yx_uint16 start = w->start;
221 scroll_hint(w->framesize, dir, dist, unit, start);
226 static void draw_wins(struct Win * w)
229 uint16_t size_y = w->winmapsize.y;
230 uint16_t size_x = w->winmapsize.x;
231 uint16_t offset_y = center_offset(w->center.y, size_y, w->framesize.y);
232 uint16_t offset_x = center_offset(w->center.x, size_x, w->framesize.x);
234 for (y = offset_y; y < w->framesize.y + offset_y && y < size_y; y++)
236 for (x = offset_x; x < w->framesize.x + offset_x && x < size_x; x++)
238 chtype ch = w->winmap[(y * w->winmapsize.x) + x];
239 mvwaddch(world.wmeta->pad, w->start.y + (y - offset_y),
240 w->start.x + (x - offset_x), ch);
249 winscroll_hint(w, '^', offset_y + 1);
251 if (size_y > offset_y + w->framesize.y)
253 winscroll_hint(w, 'v', size_y - ((offset_y + w->framesize.y) - 1));
257 winscroll_hint(w, '<', offset_x + 1);
259 if (size_x > offset_x + w->framesize.x)
261 winscroll_hint(w, '>', size_x - ((offset_x + w->framesize.x) - 1));
265 return draw_wins(w->next);
271 static void draw_win_borderlines(struct Win * w)
273 /* Draw vertical and horizontal border lines. */
275 for (y = w->start.y; y <= w->start.y + w->framesize.y; y++)
277 mvwaddch(world.wmeta->pad, y, w->start.x - 1, '|');
278 mvwaddch(world.wmeta->pad, y, w->start.x + w->framesize.x, '|');
280 for (x = w->start.x; x <= w->start.x + w->framesize.x; x++)
282 mvwaddch(world.wmeta->pad, w->start.y - 1, x, '-');
283 mvwaddch(world.wmeta->pad, w->start.y + w->framesize.y, x, '-');
286 /* Draw as much as possible of the title into center of top border line. */
287 char min_title_length_visible = 3; /* min. 1 char + 2 padding/decoration */
288 if (w->framesize.x >= min_title_length_visible)
290 uint16_t title_offset = 0;
291 if (w->framesize.x > strlen(w->title) + 2)
293 title_offset = (w->framesize.x - (strlen(w->title) + 2)) / 2;
294 } /* +2 is for padding/decoration */
295 uint16_t length_visible = strnlen(w->title, w->framesize.x - 2);
296 char title[length_visible + 3];
297 char decoration = ' ';
298 if (w == world.wmeta->active)
302 memcpy(title + 1, w->title, length_visible);
303 title[0] = title[length_visible + 1] = decoration;
304 title[length_visible + 2] = '\0';
305 mvwaddstr(world.wmeta->pad,
306 w->start.y - 1, w->start.x + title_offset, title);
312 static void draw_wins_borderlines(struct Win * w)
314 draw_win_borderlines(w);
317 draw_wins_borderlines(w->next);
323 static void draw_wins_bordercorners(struct Win * w)
325 mvwaddch(world.wmeta->pad, w->start.y - 1, w->start.x - 1, '+');
326 mvwaddch(world.wmeta->pad, w->start.y - 1, w->start.x + w->framesize.x,'+');
327 mvwaddch(world.wmeta->pad, w->start.y + w->framesize.y, w->start.x - 1,'+');
328 mvwaddch(world.wmeta->pad, w->start.y + w->framesize.y,
329 w->start.x + w->framesize.x, '+');
332 draw_wins_bordercorners(w->next);
338 static void shift_win_forward()
340 if (world.wmeta->active == world.wmeta->chain_end)
342 world.wmeta->chain_end = world.wmeta->active->prev;
343 world.wmeta->chain_end->next = 0;
344 world.wmeta->active->next = world.wmeta->chain_start;
345 world.wmeta->active->next->prev = world.wmeta->active;
346 world.wmeta->chain_start = world.wmeta->active;
347 world.wmeta->chain_start->prev = 0;
351 struct Win * old_prev = world.wmeta->active->prev;
352 struct Win * old_next = world.wmeta->active->next;
353 if (world.wmeta->chain_end == world.wmeta->active->next)
355 world.wmeta->chain_end = world.wmeta->active;
356 world.wmeta->active->next = 0;
360 world.wmeta->active->next = old_next->next;
361 world.wmeta->active->next->prev = world.wmeta->active;
363 if (world.wmeta->chain_start == world.wmeta->active)
365 world.wmeta->chain_start = old_next;
369 old_prev->next = old_next;
371 old_next->prev = old_prev;
372 old_next->next = world.wmeta->active;
373 world.wmeta->active->prev = old_next;
379 static void shift_win_backward()
381 if (world.wmeta->active == world.wmeta->chain_start)
383 world.wmeta->chain_start = world.wmeta->active->next;
384 world.wmeta->chain_start->prev = 0;
385 world.wmeta->active->prev = world.wmeta->chain_end;
386 world.wmeta->active->prev->next = world.wmeta->active;
387 world.wmeta->chain_end = world.wmeta->active;
388 world.wmeta->chain_end->next = 0;
392 struct Win * old_prev = world.wmeta->active->prev;
393 struct Win * old_next = world.wmeta->active->next;
394 if (world.wmeta->chain_start == world.wmeta->active->prev)
396 world.wmeta->chain_start = world.wmeta->active;
397 world.wmeta->active->prev = 0;
401 world.wmeta->active->prev = old_prev->prev;
402 world.wmeta->active->prev->next = world.wmeta->active;
404 if (world.wmeta->chain_end == world.wmeta->active)
406 world.wmeta->chain_end = old_prev;
410 old_next->prev = old_prev;
412 old_prev->next = old_next;
413 old_prev->prev = world.wmeta->active;
414 world.wmeta->active->next = old_prev;
420 extern void init_win_meta()
422 char * f_name = "init_win_meta()";
423 char * err_s = "init_win_meta() creates virtual screen beyond legal size.";
424 char * err_m = "init_win_meta() triggers memory alloc error via newpad().";
425 world.wmeta = try_malloc(sizeof(struct WinMeta), f_name);
426 world.wmeta->screen = initscr();
427 uint32_t maxy_test = getmaxy(world.wmeta->screen);
428 uint32_t maxx_test = getmaxx(world.wmeta->screen);
429 exit_err(maxy_test > UINT16_MAX || maxx_test > UINT16_MAX, err_s);
430 world.wmeta->padsize.y = maxy_test;
431 world.wmeta->padsize.x = maxx_test;
432 world.wmeta->chain_start = 0;
433 world.wmeta->chain_end = 0;
434 world.wmeta->pad_offset = 0;
435 world.wmeta->pad = newpad(world.wmeta->padsize.y, 1);
436 exit_err(NULL == world.wmeta->pad, err_m);
437 world.wmeta->active = 0;
442 extern void init_win(struct Win ** wp, char * title, int16_t height,
443 int16_t width, void * func)
445 char * f_name = "init_win()";
446 struct Win * w = try_malloc(sizeof(struct Win), f_name);
452 w->title = try_malloc(strlen(title) + 1, f_name);
453 sprintf(w->title, "%s", title);
459 w->framesize.x = width;
463 w->framesize.x = world.wmeta->padsize.x + width;
465 if (0 < height && height <= world.wmeta->padsize.y - 1)
467 w->framesize.y = height;
469 else if (0 >= height && world.wmeta->padsize.y + (height - 1) > 0)
471 w->framesize.y = world.wmeta->padsize.y + (height - 1);
478 extern void free_winmeta_and_endwin()
480 delwin(world.wmeta->pad);
487 extern void free_win(struct Win * win)
495 extern void append_win(struct Win * w)
497 if (0 != world.wmeta->chain_start)
499 w->prev = world.wmeta->chain_end;
500 world.wmeta->chain_end->next = w;
504 world.wmeta->active = w;
505 world.wmeta->chain_start = w;
507 world.wmeta->chain_end = w;
513 extern void suspend_win(struct Win * w)
515 if (world.wmeta->chain_start != w)
517 w->prev->next = w->next;
521 world.wmeta->chain_start = w->next;
523 uint8_t pad_refitted = 0;
524 if (world.wmeta->chain_end != w)
526 w->next->prev = w->prev;
527 if (world.wmeta->active == w)
529 world.wmeta->active = w->next;
531 update_wins(w->next); /* Positioning of successor windows may be */
532 pad_refitted = 1; /* affected / need correction. Note that */
533 } /* update_wins() already refits the pad, */
534 else /* voiding later need for that. */
536 world.wmeta->chain_end = w->prev;
537 if (world.wmeta->active == w)
539 world.wmeta->active = w->prev;
544 if (0 == pad_refitted)
552 extern void reset_pad_offset(uint16_t new_offset)
555 && (new_offset < world.wmeta->pad_offset
556 || new_offset + world.wmeta->padsize.x < getmaxx(world.wmeta->pad)))
558 world.wmeta->pad_offset = new_offset;
564 extern void resize_active_win(struct yx_uint16 size)
566 if (0 != world.wmeta->active
567 && size.x > 0 && size.y > 0 && size.y < world.wmeta->padsize.y)
569 world.wmeta->active->framesize = size;
570 update_wins(world.wmeta->active); /* Positioning of following */
571 } /* windows may be affected. */
576 extern void cycle_active_win(char dir)
578 if (0 != world.wmeta->active)
582 if (world.wmeta->active->next != 0)
584 world.wmeta->active = world.wmeta->active->next;
588 world.wmeta->active = world.wmeta->chain_start;
593 if (world.wmeta->active->prev != 0)
595 world.wmeta->active = world.wmeta->active->prev;
599 world.wmeta->active = world.wmeta->chain_end;
607 extern void shift_active_win(char dir)
609 if ( 0 == world.wmeta->active /* No shifting with < 2 windows visible. */
610 || world.wmeta->chain_start == world.wmeta->chain_end)
617 update_wins(world.wmeta->chain_start);
620 shift_win_backward();
621 update_wins(world.wmeta->chain_start);
626 extern void draw_all_wins()
628 /* Empty everything before filling it a-new. */
630 wnoutrefresh(world.wmeta->screen);
631 werase(world.wmeta->pad);
632 if (world.wmeta->chain_start)
635 /* Draw windows' borders first, then windows. */
636 draw_wins_borderlines(world.wmeta->chain_start);
637 draw_wins_bordercorners(world.wmeta->chain_start);
638 draw_wins(world.wmeta->chain_start);
640 /* Draw virtual screen scroll hints. */
641 if (world.wmeta->pad_offset > 0)
643 padscroll_hint('<', world.wmeta->pad_offset + 1);
645 uint16_t size_x = getmaxx(world.wmeta->pad);
646 uint16_t right_edge = world.wmeta->pad_offset + world.wmeta->padsize.x;
647 if (right_edge < size_x - 1)
649 padscroll_hint('>', size_x - right_edge);
652 /* Write pad segment to be shown on physical screen to screen buffer. */
653 pnoutrefresh(world.wmeta->pad, 0, world.wmeta->pad_offset, 0, 0,
654 world.wmeta->padsize.y, world.wmeta->padsize.x - 1);
657 /* Only at the end write accumulated changes to the physical screen. */