1 #include <u.h> 2 #include <libc.h> 3 #include <draw.h> 4 #include <thread.h> 5 #include <cursor.h> 6 #include <mouse.h> 7 #include <keyboard.h> 8 #include <frame.h> 9 #include <fcall.h> 10 #include <plumb.h> 11 #include "dat.h" 12 #include "fns.h" 13 #include <ctype.h> 14 15 char Ebadwr[] = "bad rectangle in wctl request"; 16 char Ewalloc[] = "window allocation failed in wctl request"; 17 18 /* >= Top are disallowed if mouse button is pressed */ 19 enum 20 { 21 New, 22 Resize, 23 Move, 24 Scroll, 25 Noscroll, 26 Set, 27 Top, 28 Bottom, 29 Current, 30 Hide, 31 Unhide, 32 Delete, 33 }; 34 35 static char *cmds[] = { 36 [New] = "new", 37 [Resize] = "resize", 38 [Move] = "move", 39 [Scroll] = "scroll", 40 [Noscroll] = "noscroll", 41 [Set] = "set", 42 [Top] = "top", 43 [Bottom] = "bottom", 44 [Current] = "current", 45 [Hide] = "hide", 46 [Unhide] = "unhide", 47 [Delete] = "delete", 48 nil 49 }; 50 51 enum 52 { 53 Cd, 54 Deltax, 55 Deltay, 56 Hidden, 57 Id, 58 Maxx, 59 Maxy, 60 Minx, 61 Miny, 62 PID, 63 R, 64 Scrolling, 65 Noscrolling, 66 }; 67 68 static char *params[] = { 69 [Cd] = "-cd", 70 [Deltax] = "-dx", 71 [Deltay] = "-dy", 72 [Hidden] = "-hide", 73 [Id] = "-id", 74 [Maxx] = "-maxx", 75 [Maxy] = "-maxy", 76 [Minx] = "-minx", 77 [Miny] = "-miny", 78 [PID] = "-pid", 79 [R] = "-r", 80 [Scrolling] = "-scroll", 81 [Noscrolling] = "-noscroll", 82 nil 83 }; 84 85 /* 86 * Check that newly created window will be of manageable size 87 */ 88 int 89 goodrect(Rectangle r) 90 { 91 if(!eqrect(canonrect(r), r)) 92 return 0; 93 if(Dx(r)<100 || Dy(r)<3*font->height) 94 return 0; 95 /* must have some screen and border visible so we can move it out of the way */ 96 if(Dx(r) >= Dx(screen->r) && Dy(r) >= Dy(screen->r)) 97 return 0; 98 /* reasonable sizes only please */ 99 if(Dx(r) > BIG*Dx(screen->r)) 100 return 0; 101 if(Dy(r) > BIG*Dx(screen->r)) 102 return 0; 103 return 1; 104 } 105 106 static 107 int 108 word(char **sp, char *tab[]) 109 { 110 char *s, *t; 111 int i; 112 113 s = *sp; 114 while(isspace(*s)) 115 s++; 116 t = s; 117 while(*s!='\0' && !isspace(*s)) 118 s++; 119 for(i=0; tab[i]!=nil; i++) 120 if(strncmp(tab[i], t, strlen(tab[i])) == 0){ 121 *sp = s; 122 return i; 123 } 124 return -1; 125 } 126 127 int 128 set(int sign, int neg, int abs, int pos) 129 { 130 if(sign < 0) 131 return neg; 132 if(sign > 0) 133 return pos; 134 return abs; 135 } 136 137 Rectangle 138 newrect(void) 139 { 140 static int i = 0; 141 int minx, miny, dx, dy; 142 143 dx = min(600, Dx(screen->r) - 2*Borderwidth); 144 dy = min(400, Dy(screen->r) - 2*Borderwidth); 145 minx = 32 + 16*i; 146 miny = 32 + 16*i; 147 i++; 148 i %= 10; 149 150 return Rect(minx, miny, minx+dx, miny+dy); 151 } 152 153 void 154 shift(int *minp, int *maxp, int min, int max) 155 { 156 if(*minp < min){ 157 *maxp += min-*minp; 158 *minp = min; 159 } 160 if(*maxp > max){ 161 *minp += max-*maxp; 162 *maxp = max; 163 } 164 } 165 166 Rectangle 167 rectonscreen(Rectangle r) 168 { 169 shift(&r.min.x, &r.max.x, screen->r.min.x, screen->r.max.x); 170 shift(&r.min.y, &r.max.y, screen->r.min.y, screen->r.max.y); 171 return r; 172 } 173 174 /* permit square brackets, in the manner of %R */ 175 int 176 riostrtol(char *s, char **t) 177 { 178 int n; 179 180 while(*s!='\0' && (*s==' ' || *s=='\t' || *s=='[')) 181 s++; 182 if(*s == '[') 183 s++; 184 n = strtol(s, t, 10); 185 if(*t != s) 186 while((*t)[0] == ']') 187 (*t)++; 188 return n; 189 } 190 191 192 int 193 parsewctl(char **argp, Rectangle r, Rectangle *rp, int *pidp, int *idp, int *hiddenp, int *scrollingp, char **cdp, char *s, char *err) 194 { 195 int cmd, param, xy, sign; 196 char *t; 197 198 *pidp = 0; 199 *hiddenp = 0; 200 *scrollingp = scrolling; 201 *cdp = nil; 202 cmd = word(&s, cmds); 203 if(cmd < 0){ 204 strcpy(err, "unrecognized wctl command"); 205 return -1; 206 } 207 if(cmd == New) 208 r = newrect(); 209 210 strcpy(err, "missing or bad wctl parameter"); 211 while((param = word(&s, params)) >= 0){ 212 switch(param){ /* special cases */ 213 case Hidden: 214 *hiddenp = 1; 215 continue; 216 case Scrolling: 217 *scrollingp = 1; 218 continue; 219 case Noscrolling: 220 *scrollingp = 0; 221 continue; 222 case R: 223 r.min.x = riostrtol(s, &t); 224 if(t == s) 225 return -1; 226 s = t; 227 r.min.y = riostrtol(s, &t); 228 if(t == s) 229 return -1; 230 s = t; 231 r.max.x = riostrtol(s, &t); 232 if(t == s) 233 return -1; 234 s = t; 235 r.max.y = riostrtol(s, &t); 236 if(t == s) 237 return -1; 238 s = t; 239 continue; 240 } 241 while(isspace(*s)) 242 s++; 243 if(param == Cd){ 244 *cdp = s; 245 while(*s && !isspace(*s)) 246 s++; 247 if(*s != '\0') 248 *s++ = '\0'; 249 continue; 250 } 251 sign = 0; 252 if(*s == '-'){ 253 sign = -1; 254 s++; 255 }else if(*s == '+'){ 256 sign = +1; 257 s++; 258 } 259 if(!isdigit(*s)) 260 return -1; 261 xy = riostrtol(s, &s); 262 switch(param){ 263 case -1: 264 strcpy(err, "unrecognized wctl parameter"); 265 return -1; 266 case Minx: 267 r.min.x = set(sign, r.min.x-xy, xy, r.min.x+xy); 268 break; 269 case Miny: 270 r.min.y = set(sign, r.min.y-xy, xy, r.min.y+xy); 271 break; 272 case Maxx: 273 r.max.x = set(sign, r.max.x-xy, xy, r.max.x+xy); 274 break; 275 case Maxy: 276 r.max.y = set(sign, r.max.y-xy, xy, r.max.y+xy); 277 break; 278 case Deltax: 279 r.max.x = set(sign, r.max.x-xy, r.min.x+xy, r.max.x+xy); 280 break; 281 case Deltay: 282 r.max.y = set(sign, r.max.y-xy, r.min.y+xy, r.max.y+xy); 283 break; 284 case Id: 285 if(idp != nil) 286 *idp = xy; 287 break; 288 case PID: 289 if(pidp != nil) 290 *pidp = xy; 291 break; 292 } 293 } 294 295 *rp = rectonscreen(rectaddpt(r, screen->r.min)); 296 297 while(isspace(*s)) 298 s++; 299 if(cmd!=New && *s!='\0'){ 300 strcpy(err, "extraneous text in wctl message"); 301 return -1; 302 } 303 304 if(argp) 305 *argp = s; 306 307 return cmd; 308 } 309 310 int 311 wctlnew(Rectangle rect, char *arg, int pid, int hideit, int scrollit, char *dir, char *err) 312 { 313 char **argv; 314 Image *i; 315 316 if(!goodrect(rect)){ 317 strcpy(err, Ebadwr); 318 return -1; 319 } 320 argv = emalloc(4*sizeof(char*)); 321 argv[0] = "rc"; 322 argv[1] = "-c"; 323 while(isspace(*arg)) 324 arg++; 325 if(*arg == '\0'){ 326 argv[1] = "-i"; 327 argv[2] = nil; 328 }else{ 329 argv[2] = arg; 330 argv[3] = nil; 331 } 332 if(hideit) 333 i = allocimage(display, rect, screen->chan, 0, DWhite); 334 else 335 i = allocwindow(wscreen, rect, Refbackup, DWhite); 336 if(i == nil){ 337 strcpy(err, Ewalloc); 338 return -1; 339 } 340 border(i, rect, Selborder, red, ZP); 341 342 new(i, hideit, scrollit, pid, dir, "/bin/rc", argv); 343 344 free(argv); /* when new() returns, argv and args have been copied */ 345 return 1; 346 } 347 348 int 349 writewctl(Xfid *x, char *err) 350 { 351 int cnt, cmd, j, id, hideit, scrollit, pid; 352 Image *i; 353 char *arg, *dir; 354 Rectangle rect; 355 Window *w; 356 357 w = x->f->w; 358 cnt = x->count; 359 x->data[cnt] = '\0'; 360 id = 0; 361 362 rect = rectsubpt(w->screenr, screen->r.min); 363 cmd = parsewctl(&arg, rect, &rect, &pid, &id, &hideit, &scrollit, &dir, x->data, err); 364 if(cmd < 0) 365 return -1; 366 367 if(mouse->buttons!=0 && cmd>=Top){ 368 strcpy(err, "action disallowed when mouse active"); 369 return -1; 370 } 371 372 if(id != 0){ 373 for(j=0; j<nwindow; j++) 374 if(window[j]->id == id) 375 break; 376 if(j == nwindow){ 377 strcpy(err, "no such window id"); 378 return -1; 379 } 380 w = window[j]; 381 if(w->deleted || w->i==nil){ 382 strcpy(err, "window deleted"); 383 return -1; 384 } 385 } 386 387 switch(cmd){ 388 case New: 389 return wctlnew(rect, arg, pid, hideit, scrollit, dir, err); 390 case Set: 391 if(pid > 0) 392 wsetpid(w, pid, 0); 393 return 1; 394 case Move: 395 rect = Rect(rect.min.x, rect.min.y, rect.min.x+Dx(w->screenr), rect.min.y+Dy(w->screenr)); 396 rect = rectonscreen(rect); 397 /* fall through */ 398 case Resize: 399 if(!goodrect(rect)){ 400 strcpy(err, Ebadwr); 401 return -1; 402 } 403 if(eqrect(rect, w->screenr)) 404 return 1; 405 i = allocwindow(wscreen, rect, Refbackup, DWhite); 406 if(i == nil){ 407 strcpy(err, Ewalloc); 408 return -1; 409 } 410 border(i, rect, Selborder, red, ZP); 411 wsendctlmesg(w, Reshaped, i->r, i); 412 return 1; 413 case Scroll: 414 w->scrolling = 1; 415 wshow(w, w->nr); 416 wsendctlmesg(w, Wakeup, ZR, nil); 417 return 1; 418 case Noscroll: 419 w->scrolling = 0; 420 wsendctlmesg(w, Wakeup, ZR, nil); 421 return 1; 422 case Top: 423 wtopme(w); 424 return 1; 425 case Bottom: 426 wbottomme(w); 427 return 1; 428 case Current: 429 wcurrent(w); 430 return 1; 431 case Hide: 432 switch(whide(w)){ 433 case -1: 434 strcpy(err, "window already hidden"); 435 return -1; 436 case 0: 437 strcpy(err, "hide failed"); 438 return -1; 439 default: 440 break; 441 } 442 return 1; 443 case Unhide: 444 for(j=0; j<nhidden; j++) 445 if(hidden[j] == w) 446 break; 447 if(j == nhidden){ 448 strcpy(err, "window not hidden"); 449 return -1; 450 } 451 if(wunhide(j) == 0){ 452 strcpy(err, "hide failed"); 453 return -1; 454 } 455 return 1; 456 case Delete: 457 wsendctlmesg(w, Deleted, ZR, nil); 458 return 1; 459 } 460 strcpy(err, "invalid wctl message"); 461 return -1; 462 } 463 464 void 465 wctlthread(void *v) 466 { 467 char *buf, *arg, *dir; 468 int cmd, id, pid, hideit, scrollit; 469 Rectangle rect; 470 char err[ERRMAX]; 471 Channel *c; 472 473 c = v; 474 475 threadsetname("WCTLTHREAD"); 476 477 for(;;){ 478 buf = recvp(c); 479 cmd = parsewctl(&arg, ZR, &rect, &pid, &id, &hideit, &scrollit, &dir, buf, err); 480 481 switch(cmd){ 482 case New: 483 wctlnew(rect, arg, pid, hideit, scrollit, dir, err); 484 } 485 free(buf); 486 } 487 } 488 489 void 490 wctlproc(void *v) 491 { 492 char *buf; 493 int n, eofs; 494 Channel *c; 495 496 threadsetname("WCTLPROC"); 497 c = v; 498 499 eofs = 0; 500 for(;;){ 501 buf = emalloc(messagesize); 502 n = read(wctlfd, buf, messagesize-1); /* room for \0 */ 503 if(n < 0) 504 break; 505 if(n == 0){ 506 if(++eofs > 20) 507 break; 508 continue; 509 } 510 eofs = 0; 511 512 buf[n] = '\0'; 513 sendp(c, buf); 514 } 515 } 516