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 }