stew

a monorepo of some sort
git clone git://git.nsmpr.xyz/stew.git
Log | Files | Refs

knob.c (6129B)


      1 #include <u.h>
      2 #include <libc.h>
      3 #include <plumb.h>
      4 #include <thread.h>
      5 #include <draw.h>
      6 #include <mouse.h>
      7 #include <bio.h>
      8 
      9 
     10 enum {
     11 	KnobSize = (100 - 8),
     12 };
     13 
     14 
     15 typedef struct Knob Knob;
     16 typedef struct Group Group;
     17 
     18 struct Knob {
     19 	char *name;
     20 	double min;
     21 	double max;
     22 	double val;
     23 	Rectangle r;
     24 };
     25 
     26 struct Group {
     27 	char *name;
     28 	Knob *knobs;
     29 	Rectangle r;
     30 };
     31 
     32 Mousectl *mctl;
     33 Font *microfont;
     34 Image *bg, *groupbg, *fg, *hotfg;
     35 char *plumbport = "widget";
     36 
     37 Knob *hotknob;
     38 Group g;
     39 
     40 int
     41 knobslen(Knob *k)
     42 {
     43 	int n;
     44 	if (k == nil) return 0;
     45 	for (n = 0; k[n].name != nil; n++);
     46 	return n;
     47 }
     48 
     49 void
     50 appendknob(Group *g, Knob *k)
     51 {
     52 	int n = knobslen(g->knobs);
     53 	g->knobs = realloc(g->knobs, sizeof(Knob) * (n + 2));
     54 	g->knobs[n] = *k;
     55 	g->knobs[n + 1].name = nil;
     56 }
     57 
     58 void
     59 config(int fd)
     60 {
     61 	Biobuf *bp = Bfdopen(fd, OREAD);
     62 	char *str;
     63 	while ((str = Brdstr(bp, '\n', 0)) != nil) {
     64 		char *args[4];
     65 		int n = tokenize(str, args, 4);
     66 		if (n > 0) {
     67 			Knob k;
     68 
     69 			k.name = args[0];
     70 
     71 			if (n >= 3) {
     72 				k.min = strtod(args[1], nil);
     73 				k.max = strtod(args[2], nil);
     74 			}
     75 			if (n == 4) k.val = strtod(args[3], nil);
     76 		
     77 			appendknob(&g, &k);
     78 		}
     79 	}
     80 	if (fd != 0) close(fd);
     81 }
     82 
     83 void
     84 _drawknob(Rectangle r, double min, double max, double val, Image *src, char *name)
     85 {
     86 	int d, w;
     87 	Point C;
     88 	char *s;
     89 	char buf[64];
     90 
     91 	C = divpt(addpt(r.min, r.max), 2);
     92 	s = nil;
     93 
     94 	w = stringwidth(microfont, name);
     95 	string(
     96 	  screen,
     97 	  addpt(r.min, Pt((Dx(r) - w)/2, 0)),
     98 	  src, ZP, microfont, name
     99 	);
    100 
    101 	d = (Dx(r) < Dy(r) - microfont->height * 2) ?
    102 		Dx(r) : Dy(r) - microfont->height * 2;
    103 
    104 	ellipse(
    105 	  screen, C,
    106 	  d / 2, d / 2, 0, src, ZP
    107 	);
    108 	if (val > max) {
    109 		s = ">";
    110 	}
    111 	else if (val < min) {
    112 		s = "<";
    113 	}
    114 
    115 	if (s != nil) {
    116 		w = stringwidth(microfont, s);
    117 		string(
    118 		  screen,
    119 		  subpt(C, Pt(w/2, microfont->height/2)),
    120 		  src, ZP, microfont, s
    121 		);
    122 	} else {
    123 		double p;
    124 		int cosp, sinp;
    125 		p = (max != min) ? (val - min) / (max - min) : 0;
    126 		p = (max != -min) ? p * 360 : p * 270 - 135;
    127 		icossin(p, &cosp, &sinp);
    128 		line(
    129 		  screen,
    130 		  addpt(C, Pt(sinp * d / 4096, - cosp * d / 4096)),
    131 		  addpt(C, Pt(sinp * d / 2048, - cosp * d / 2048)),
    132 		  0, 0, 0,
    133 		  src, ZP
    134 		);
    135 	}
    136 
    137 	snprint(buf, 64, "%g", val);
    138 
    139 	w = stringwidth(microfont, buf);
    140 	string(
    141 	  screen,
    142 	  Pt(r.min.x + (Dx(r) - w) / 2, r.max.y - microfont->height),
    143 	  src, ZP, microfont, buf
    144 	);
    145 }
    146 
    147 void
    148 drawknob(Knob *k)
    149 {
    150 	Image *src;
    151 	src = (hotknob == k) ? hotfg : fg;
    152 	draw(screen, k->r, bg, nil, ZP);
    153 	_drawknob(k->r, k->min, k->max, k->val, src, k->name);
    154 }
    155 
    156 void
    157 drawgroup(Group *g)
    158 {
    159 	draw(screen,
    160 	  Rpt(g->r.min, Pt(g->r.max.x, g->r.min.y + microfont->height)),
    161 	  groupbg, nil, ZP
    162 	);
    163 	string(
    164 	  screen,
    165 	  g->r.min,
    166 	  fg, ZP, microfont, g->name
    167 	);
    168 }
    169 
    170 void
    171 resize(void)
    172 {
    173 	draw(screen, screen->r, bg, nil, ZP);
    174 
    175 	g.r = screen->r;
    176 	drawgroup(&g);
    177 
    178 	Rectangle r = rectaddpt(
    179 	  Rect(0, 0, KnobSize, KnobSize),
    180 	  addpt(screen->r.min, Pt(0, microfont->height))
    181 	);
    182 
    183 	Knob *k;
    184 
    185 	for (k = g.knobs; k->name != nil; k++) {
    186 		k->r = r;
    187 		drawknob(k);
    188 
    189 		r = rectaddpt(r, Pt(KnobSize, 0));
    190 		if (r.max.x > screen->r.max.x) {
    191 			r = rectaddpt(
    192 			  Rect(0, 0, KnobSize, KnobSize),
    193 			  Pt(screen->r.min.x, r.max.y)
    194 			);
    195 		}
    196 	}
    197 }
    198 
    199 Knob *
    200 findknob(char *name)
    201 {
    202 	if (name == nil) return nil;
    203 	if (g.knobs == nil) return nil;
    204 
    205 	Knob *k;
    206 
    207 	for (k = g.knobs; k->name != nil; k++) {
    208 		if (strcmp(name, k->name) == 0) {
    209 			return k;
    210 		}
    211 	}
    212 	return nil;
    213 }
    214 
    215 Knob *
    216 findhot(Point xy)
    217 {
    218 
    219 	if (g.knobs == nil) return nil;
    220 
    221 	Knob *k;
    222 
    223 	for (k = g.knobs; k->name != nil; k++) {
    224 		if (ptinrect(xy, k->r) != 0) return k;
    225 	}
    226 	return nil;
    227 }
    228 
    229 void
    230 threadmouse(void *)
    231 {
    232 	int fd;
    233 	fd = plumbopen("send", OWRITE);
    234 	if (fd <= 0) sysfatal("threadmouse: %r");
    235 
    236 	Mouse m;
    237 	double startval;
    238 	Point startxy;
    239 
    240 	while(recv(mctl->c, &m) != -1){
    241 		if (m.buttons == 0) {
    242 			Knob *k = hotknob;
    243 			hotknob = nil;
    244 			if (k != nil) {
    245 				lockdisplay(display);
    246 				drawknob(k);
    247 				flushimage(display, 1);
    248 				unlockdisplay(display);
    249 			}
    250 			continue;
    251 		}
    252 		if (hotknob == nil) {
    253 			hotknob = findhot(m.xy);
    254 			if (hotknob != nil) {
    255 				startval = hotknob->val;
    256 				startxy = m.xy;
    257 			}
    258 		}
    259 		if (hotknob != nil) {
    260 			double val;
    261 			val = startval + (m.xy.y - startxy.y) * (hotknob->max - hotknob->min) / 1000;
    262 			if (val > hotknob->max) val = hotknob->max;
    263 			if (val < hotknob->min) val = hotknob->min;
    264 
    265 			Plumbmsg *msg;
    266 			msg = mallocz(sizeof(Plumbmsg), 1);
    267 			msg->src = strdup("knob");
    268 			msg->dst = strdup(plumbport);
    269 			msg->type = strdup("text");
    270 			msg->data = smprint("%g\n", val);
    271 			msg->ndata = strlen(msg->data);
    272 
    273 			msg->attr = mallocz(sizeof(Plumbattr), 1);
    274 			msg->attr->name = strdup("name");
    275 			msg->attr->value = strdup(hotknob->name);
    276 			msg->attr->next = nil;
    277 
    278 			plumbsend(fd, msg);
    279 			plumbfree(msg);
    280 		}
    281 	}
    282 }
    283 
    284 void
    285 threaddraw(void *)
    286 {
    287 
    288 	int fd;
    289 	Plumbmsg *msg;
    290 	fd = plumbopen(plumbport, OREAD);
    291 	if (fd <= 0) sysfatal("threaddraw: %r");
    292 
    293 	while((msg = plumbrecv(fd)) != nil){
    294 		char *name = plumblookup(msg->attr, "name");
    295 		plumbfree(msg);
    296 		Knob *k = findknob(name);
    297 		if (k != nil) {
    298 			k->val = strtod(msg->data, nil);
    299 			lockdisplay(display);
    300 			drawknob(k);
    301 			flushimage(display, 1);
    302 			unlockdisplay(display);
    303 		}
    304 	}
    305 }
    306 
    307 void
    308 threadmain(int argc, char **argv)
    309 {
    310 
    311 	g.name = "global";
    312 
    313 	config(0);
    314 
    315 	initdraw(nil, nil, "knob");
    316 	display->locking = 1;
    317 
    318 	if ((mctl = initmouse(0, screen)) == nil) sysfatal("%r");
    319 
    320 	microfont = openfont(display, "/lib/font/bit/terminus/unicode.12.font");
    321 
    322 	groupbg = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DMedblue);
    323 	bg      = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DBlack);
    324 	fg      = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DWhite);
    325 	hotfg   = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DRed);
    326 
    327 	resize();
    328 	flushimage(display, 1);
    329 
    330 	unlockdisplay(display);
    331 
    332 	proccreate(threaddraw, nil, 4096 * 1024);
    333 	threadcreate(threadmouse, nil, 4096 * 1024);
    334 
    335 	for (;;) {
    336 		if (recv(mctl->resizec, nil) > 0) {
    337 			if (getwindow(display, Refnone) < 0)
    338 				sysfatal("resize failed: %r");
    339 
    340 			lockdisplay(display);
    341 			resize();
    342 			flushimage(display, 1);
    343 			unlockdisplay(display);
    344 		}
    345 	}
    346 }