git home / emma home
logo

twn

Minimal terminal multiplexer without curses.
git clone https://git.y1.nz/archives/twn.tar.gz
README | Files | Log | Refs | LICENSE

window.c


      1 #include <stdio.h> /* for stdin/out, fprintf, etc */
      2 #include <stdlib.h> /* for malloc, free */
      3 
      4 #include "config.h"
      5 #include "window.h"
      6 #include "direction.h"
      7 
      8 /* interior bounds, inclusive. */
      9 /* min and max cursor positions in the text area of the given subterm */
     10 #define IN_L(win_ptr)  ((win_ptr)->l + (win_ptr)->pl)
     11 #define IN_U(win_ptr)  ((win_ptr)->u + (win_ptr)->pu)
     12 #define IN_R(win_ptr)  ((win_ptr)->r - (win_ptr)->pr)
     13 #define IN_D(win_ptr)  ((win_ptr)->d - (win_ptr)->pd)
     14 
     15 /* collision check */
     16 #define IN_WINDOW(w, x, y)  (w->l <= x && x <= w->r && w->u <= y && y <= w->d)
     17 
     18 /* get cursor position within window, in base-terminal coordinates */
     19 // TODO use actual cursor instead of top-left corner
     20 #define ABS_CURS_X(w)  (w)->l
     21 #define ABS_CURS_Y(w)  (w)->u
     22 
     23 
     24 /* recursive helper for close_window */
     25 void
     26 resize_others(Window *w, Window *other)
     27 {
     28 	if (other->l == w->r+1) other->l = w->l;
     29 	if (other->r == w->l-1) other->r = w->r;
     30 	if (other->u == w->d+1) other->u = w->u;
     31 	if (other->d == w->u-1) other->d = w->d;
     32 	if (other->child1 != NULL) {
     33 		resize_others(w, other->child1);
     34 		resize_others(w, other->child2);
     35 	}
     36 }
     37 
     38 /* FOCUS MUST BE A LEAF */
     39 /* returns the new focus */
     40 Window *
     41 close_window(Window *root, Window *focus) {
     42 	Window *parent = focus->parent;
     43 	Window *sibling;
     44 	Window *new_focus;
     45 
     46 	if (parent == NULL) return NULL;
     47 
     48 	sibling = focus == parent->child1 ?
     49 		parent->child2 : parent->child1;
     50 
     51 	/* merge sibling and parent */
     52 	parent->child1 = sibling->child1;
     53 	parent->child2 = sibling->child2;
     54 	if (parent->child1 != NULL) {
     55 		parent->child1->parent = parent;
     56 		parent->child2->parent = parent;
     57 	}
     58 	// TODO set parent's subterm, cursor, etc to sibling's
     59 	resize_others(focus, parent);
     60 	new_focus = find_window(root, ABS_CURS_X(focus), ABS_CURS_Y(focus));
     61 	free(focus);
     62 	free(sibling);
     63 
     64 	return new_focus;
     65 }
     66 
     67 Window *
     68 clone_window(Window *a)
     69 {
     70 	Window *b = (Window *) malloc(sizeof(Window));
     71 	b->l = a->l; b->u = a->u;
     72 	b->r = a->r; b->d = a->d;
     73 	b->pl = a->pl; b->pu = a->pu;
     74 	b->pr = a->pr; b->pd = a->pd;
     75 	b->parent = NULL;
     76 	b->child1 = NULL;
     77 	b->child2 = NULL;
     78 	b->t = NULL;
     79 	return b;
     80 }
     81 
     82 /* Shrink the given window and create a new one. */
     83 /* Returns the pointer to the new focused window. */
     84 Window *
     85 split_window(Window *parent, int direction)
     86 {
     87 	Window *c1 = clone_window(parent), *c2 = clone_window(parent);
     88 
     89 	/* calculate the new windows' geometries */
     90 	int xmid = (parent->l + parent->r) / 2;
     91 	int ymid = (parent->u + parent->d) / 2;
     92 	switch (direction) {
     93 	case down:
     94 		c1->d = ymid; c1->pd = SPLIT_DOWN_PAD;
     95 		c2->u = ymid+1; c2->pu = SPLIT_UP_PAD;
     96 		break;
     97 	case up:
     98 		c1->u = ymid; c1->pu = SPLIT_UP_PAD;
     99 		c2->d = ymid-1; c2->pd = SPLIT_DOWN_PAD;
    100 		break;
    101 	case right:
    102 		c1->r = xmid; c1->pr = SPLIT_RIGHT_PAD;
    103 		c2->l = xmid+1; c2->pl = SPLIT_LEFT_PAD;
    104 		break;
    105 	case left:
    106 		c1->l = xmid; c1->pl = SPLIT_LEFT_PAD;
    107 		c2->r = xmid-1; c2->pr = SPLIT_RIGHT_PAD;
    108 		break;
    109 	}
    110 
    111 	/* update the window tree */
    112 	parent->t = NULL;
    113 	c1->parent = parent; c2->parent = parent;
    114 	parent->child1 = c1; parent->child2 = c2;
    115 
    116 	// TODO open a new subterm for c2
    117 	// c2->t = ...
    118 
    119 	return c2;
    120 }
    121 
    122 /* Find the leaf window containing the given point, or else NULL */
    123 /* Recursing helper method for find_window_in_direction */
    124 Window *
    125 find_window(Window *w, int x, int y)
    126 {
    127 	// TODO may be unnecessary: only check root window?
    128 	if (!IN_WINDOW(w, x, y)) return NULL;
    129 	
    130 	/* no children */
    131 	if (w->child1 == NULL) return w;
    132 	
    133 	return find_window(IN_WINDOW(w->child1, x, y) ? w->child1 : w->child2, x, y);
    134 }
    135 
    136 /* Find the next window in a given direction */
    137 Window *
    138 find_next_window(Window *root, Window *focus, int direction)
    139 {
    140 	int x, y; /* target position */
    141 	switch(direction) {
    142 	case left: x = focus->l - 1; y = ABS_CURS_Y(focus); break;
    143 	case up: y = focus->u - 1; x = ABS_CURS_X(focus); break;
    144 	case right: x = focus->r + 1; y = ABS_CURS_Y(focus); break;
    145 	case down: y = focus->d + 1; x = ABS_CURS_X(focus); break;
    146 	}
    147 	return find_window(root, x, y);
    148 }
    149 
    150 void
    151 fill_rect(int l, int u, int r, int d, char c)
    152 {
    153 	int x = l, y = u;
    154 	for (;;) {
    155 		/* CSI sequence HVP: move cursor to row,col */
    156 		printf("\033[%d;%df", y, x);
    157 
    158 		putchar(c);
    159 
    160 		x++;
    161 		if (x > r) {
    162 			x = l;
    163 			y++;
    164 		}
    165 		if (y > d) break;
    166 	}
    167 }
    168 
    169 void
    170 fill_window_pad(Window *w)
    171 {
    172 	if (w->pu > 0) fill_rect(IN_L(w), w->u, IN_R(w), IN_U(w) - 1, V_PAD_CHAR);
    173 	if (w->pd > 0) fill_rect(IN_L(w), IN_D(w) + 1, IN_R(w), w->d, V_PAD_CHAR);
    174 
    175 	if (w->pl > 0) fill_rect(w->l, IN_U(w), IN_L(w) - 1, IN_D(w), H_PAD_CHAR);
    176 	if (w->pr > 0) fill_rect(IN_R(w) + 1, IN_U(w), w->r, IN_D(w), H_PAD_CHAR);
    177 
    178 	if (w->pl > 0 && w->pu > 0) fill_rect(w->l, w->u, IN_L(w) - 1, IN_U(w) - 1, CORNER_PAD_CHAR);
    179 	if (w->pl > 0 && w->pd > 0) fill_rect(w->l, IN_D(w)+1, IN_L(w) - 1, w->d, CORNER_PAD_CHAR);
    180 	if (w->pr > 0 && w->pu > 0) fill_rect(IN_R(w) + 1, w->u, w->r, IN_U(w) - 1, CORNER_PAD_CHAR);
    181 	if (w->pr > 0 && w->pd > 0) fill_rect(IN_R(w) + 1, IN_D(w)+1, w->r, w->d, CORNER_PAD_CHAR);
    182 	// TODO draw corners...
    183 }
    184 
    185 void
    186 fill_window(Window *w, char c)
    187 {
    188 	fill_rect(IN_L(w), IN_U(w), IN_R(w), IN_D(w), c);
    189 }
    190 
    191 void
    192 render(Window *w, Window *focus)
    193 {
    194 	int r;
    195 	if (w == NULL) return;
    196 
    197 	if (w->child1 != NULL) {
    198 		render(w->child1, focus);
    199 		render(w->child2, focus);
    200 		return;
    201 	}
    202 
    203 	/* reset bg */
    204 	fprintf(stdout, "\033[49m");
    205 
    206 	fill_window(w, ' ');
    207 
    208 	/* show border in a color... */
    209 	if (w == focus) fprintf(stdout, "\033[41m");
    210 
    211 	fill_window_pad(w);
    212 	fflush(stdout);
    213 }
    214 

This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.