wctl.c 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551
  1. /*
  2. * This file is part of the UCB release of Plan 9. It is subject to the license
  3. * terms in the LICENSE file found in the top-level directory of this
  4. * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
  5. * part of the UCB release of Plan 9, including this file, may be copied,
  6. * modified, propagated, or distributed except according to the terms contained
  7. * in the LICENSE file.
  8. */
  9. /* Portions of this file are Copyright (C) 2015-2018 Giacomo Tesio <giacomo@tesio.it>
  10. * See /doc/license/gpl-2.0.txt for details about the licensing.
  11. */
  12. /* Portions of this file are Copyright (C) 9front's team.
  13. * See /doc/license/9front-mit for details about the licensing.
  14. * See http://code.9front.org/hg/plan9front/ for a list of authors.
  15. */
  16. #include <u.h>
  17. #include <lib9.h>
  18. #include <draw.h>
  19. #include <thread.h>
  20. #include <cursor.h>
  21. #include <mouse.h>
  22. #include <keyboard.h>
  23. #include <frame.h>
  24. #include <9P2000.h>
  25. #include <plumb.h>
  26. #include "dat.h"
  27. #include "fns.h"
  28. #include <chartypes.h>
  29. char Ebadwr[] = "bad rectangle in wctl request";
  30. char Ewalloc[] = "window allocation failed in wctl request";
  31. /* >= Top are disallowed if mouse button is pressed */
  32. enum
  33. {
  34. New,
  35. Resize,
  36. Move,
  37. Scroll,
  38. Noscroll,
  39. Set,
  40. Top,
  41. Bottom,
  42. Current,
  43. Hide,
  44. Unhide,
  45. Delete,
  46. };
  47. static char *cmds[] = {
  48. [New] = "new",
  49. [Resize] = "resize",
  50. [Move] = "move",
  51. [Scroll] = "scroll",
  52. [Noscroll] = "noscroll",
  53. [Set] = "set",
  54. [Top] = "top",
  55. [Bottom] = "bottom",
  56. [Current] = "current",
  57. [Hide] = "hide",
  58. [Unhide] = "unhide",
  59. [Delete] = "delete",
  60. nil
  61. };
  62. enum
  63. {
  64. Cd,
  65. Deltax,
  66. Deltay,
  67. Hidden,
  68. Id,
  69. Maxx,
  70. Maxy,
  71. Minx,
  72. Miny,
  73. PID,
  74. R,
  75. Scrolling,
  76. Noscrolling,
  77. };
  78. static char *params[] = {
  79. [Cd] = "-cd",
  80. [Deltax] = "-dx",
  81. [Deltay] = "-dy",
  82. [Hidden] = "-hide",
  83. [Id] = "-id",
  84. [Maxx] = "-maxx",
  85. [Maxy] = "-maxy",
  86. [Minx] = "-minx",
  87. [Miny] = "-miny",
  88. [PID] = "-pid",
  89. [R] = "-r",
  90. [Scrolling] = "-scroll",
  91. [Noscrolling] = "-noscroll",
  92. nil
  93. };
  94. /*
  95. * Check that newly created window will be of manageable size
  96. */
  97. int
  98. goodrect(Rectangle r)
  99. {
  100. if(!eqrect(canonrect(r), r))
  101. return 0;
  102. /* reasonable sizes only please */
  103. if(Dx(r) > BIG*Dx(screen->r))
  104. return 0;
  105. if(Dy(r) > BIG*Dx(screen->r))
  106. return 0;
  107. if(Dx(r) < 100 || Dy(r) < 3*font->height)
  108. return 0;
  109. /* window must be on screen */
  110. if(!rectXrect(screen->r, r))
  111. return 0;
  112. /* must have some screen and border visible so we can move it out of the way */
  113. if(rectinrect(screen->r, insetrect(r, Borderwidth)))
  114. return 0;
  115. return 1;
  116. }
  117. static
  118. int
  119. word(char **sp, char *tab[])
  120. {
  121. char *s, *t;
  122. int i;
  123. s = *sp;
  124. while(isspace(*s))
  125. s++;
  126. t = s;
  127. while(*s!='\0' && !isspace(*s))
  128. s++;
  129. for(i=0; tab[i]!=nil; i++)
  130. if(strncmp(tab[i], t, strlen(tab[i])) == 0){
  131. *sp = s;
  132. return i;
  133. }
  134. return -1;
  135. }
  136. int
  137. set(int sign, int neg, int abs, int pos)
  138. {
  139. if(sign < 0)
  140. return neg;
  141. if(sign > 0)
  142. return pos;
  143. return abs;
  144. }
  145. Rectangle
  146. newrect(void)
  147. {
  148. static int i = 0;
  149. int minx, miny, dx, dy;
  150. dx = min(600, Dx(screen->r) - 2*Borderwidth);
  151. dy = min(400, Dy(screen->r) - 2*Borderwidth);
  152. minx = 32 + 16*i;
  153. miny = 32 + 16*i;
  154. i++;
  155. i %= 10;
  156. return Rect(minx, miny, minx+dx, miny+dy);
  157. }
  158. void
  159. shift(int *minp, int *maxp, int min, int max)
  160. {
  161. if(*maxp > max){
  162. *minp += max-*maxp;
  163. *maxp = max;
  164. }
  165. if(*minp < min){
  166. *maxp += min-*minp;
  167. if(*maxp > max)
  168. *maxp = max;
  169. *minp = min;
  170. }
  171. }
  172. Rectangle
  173. rectonscreen(Rectangle r)
  174. {
  175. shift(&r.min.x, &r.max.x, screen->r.min.x, screen->r.max.x);
  176. shift(&r.min.y, &r.max.y, screen->r.min.y, screen->r.max.y);
  177. return r;
  178. }
  179. /* permit square brackets, in the manner of %R */
  180. int
  181. riostrtol(char *s, char **t)
  182. {
  183. int n;
  184. while(*s!='\0' && (*s==' ' || *s=='\t' || *s=='['))
  185. s++;
  186. if(*s == '[')
  187. s++;
  188. n = strtol(s, t, 10);
  189. if(*t != s)
  190. while((*t)[0] == ']')
  191. (*t)++;
  192. return n;
  193. }
  194. int
  195. parsewctl(char **argp, Rectangle r, Rectangle *rp, int *pidp, int *idp, int *hiddenp, int *scrollingp, char **cdp, char *s, char *err)
  196. {
  197. int cmd, param, xy, sign;
  198. char *t;
  199. *pidp = 0;
  200. *hiddenp = 0;
  201. *scrollingp = scrolling;
  202. *cdp = nil;
  203. cmd = word(&s, cmds);
  204. if(cmd < 0){
  205. strcpy(err, "unrecognized wctl command");
  206. return -1;
  207. }
  208. if(cmd == New)
  209. r = newrect();
  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. *rp = rectonscreen(rectaddpt(r, screen->r.min));
  295. while(isspace(*s))
  296. s++;
  297. if(cmd!=New && *s!='\0'){
  298. strcpy(err, "extraneous text in wctl message");
  299. return -1;
  300. }
  301. if(argp)
  302. *argp = s;
  303. return cmd;
  304. }
  305. int
  306. wctlnew(Rectangle rect, char *arg, int pid, int hideit, int scrollit, char *dir, char *err)
  307. {
  308. char **argv;
  309. Image *i;
  310. if(!goodrect(rect)){
  311. strcpy(err, Ebadwr);
  312. return -1;
  313. }
  314. argv = emalloc(4*sizeof(char*));
  315. argv[0] = "rc";
  316. argv[1] = "-c";
  317. while(isspace(*arg))
  318. arg++;
  319. if(*arg == '\0'){
  320. argv[1] = "-i";
  321. argv[2] = nil;
  322. }else{
  323. argv[2] = arg;
  324. argv[3] = nil;
  325. }
  326. if(hideit)
  327. i = allocimage(display, rect, screen->chan, 0, DNofill);
  328. else
  329. i = allocwindow(wscreen, rect, Refbackup, DNofill);
  330. if(i == nil){
  331. strcpy(err, Ewalloc);
  332. return -1;
  333. }
  334. new(i, hideit, scrollit, pid, dir, "/cmd/rc", argv);
  335. free(argv); /* when new() returns, argv and args have been copied */
  336. return 1;
  337. }
  338. int
  339. wctlcmd(Window *w, Rectangle r, int cmd, char *err)
  340. {
  341. Image *i;
  342. switch(cmd){
  343. case Move:
  344. r = Rect(r.min.x, r.min.y, r.min.x+Dx(w->screenr), r.min.y+Dy(w->screenr));
  345. r = rectonscreen(r);
  346. /* fall through */
  347. case Resize:
  348. if(!goodrect(r)){
  349. strcpy(err, Ebadwr);
  350. return -1;
  351. }
  352. if(Dx(w->screenr) > 0){
  353. if(eqrect(r, w->screenr))
  354. return 1;
  355. if(w != input){
  356. strcpy(err, "window not current");
  357. return -1;
  358. }
  359. i = allocwindow(wscreen, r, Refbackup, DNofill);
  360. } else { /* hidden */
  361. if(eqrect(r, w->i->r))
  362. return 1;
  363. i = allocimage(display, r, w->i->chan, 0, DNofill);
  364. r = ZR;
  365. }
  366. if(i == nil){
  367. strcpy(err, Ewalloc);
  368. return -1;
  369. }
  370. wsendctlmesg(w, Reshaped, r, i);
  371. return 1;
  372. case Scroll:
  373. w->scrolling = 1;
  374. wshow(w, w->nr);
  375. wsendctlmesg(w, Wakeup, ZR, nil);
  376. return 1;
  377. case Noscroll:
  378. w->scrolling = 0;
  379. wsendctlmesg(w, Wakeup, ZR, nil);
  380. return 1;
  381. case Top:
  382. wtopme(w);
  383. return 1;
  384. case Bottom:
  385. wbottomme(w);
  386. return 1;
  387. case Current:
  388. if(Dx(w->screenr)<=0){
  389. strcpy(err, "window is hidden");
  390. return -1;
  391. }
  392. wtopme(w);
  393. wcurrent(w);
  394. return 1;
  395. case Hide:
  396. switch(whide(w)){
  397. case -1:
  398. strcpy(err, "window already hidden");
  399. return -1;
  400. case 0:
  401. strcpy(err, "hide failed");
  402. return -1;
  403. default:
  404. break;
  405. }
  406. return 1;
  407. case Unhide:
  408. switch(wunhide(w)){
  409. case -1:
  410. strcpy(err, "window not hidden");
  411. return -1;
  412. case 0:
  413. strcpy(err, "hide failed");
  414. return -1;
  415. default:
  416. break;
  417. }
  418. return 1;
  419. case Delete:
  420. wsendctlmesg(w, Deleted, ZR, nil);
  421. return 1;
  422. }
  423. strcpy(err, "invalid wctl message");
  424. return -1;
  425. }
  426. int
  427. writewctl(Xfid *x, char *err)
  428. {
  429. int cnt, cmd, id, hideit, scrollit, pid;
  430. char *arg, *dir;
  431. Rectangle r;
  432. Window *w;
  433. w = x->f->w;
  434. cnt = x->count;
  435. x->data[cnt] = '\0';
  436. id = 0;
  437. r = rectsubpt(w->screenr, screen->r.min);
  438. cmd = parsewctl(&arg, r, &r, &pid, &id, &hideit, &scrollit, &dir, x->data, err);
  439. if(cmd < 0)
  440. return -1;
  441. if(id != 0){
  442. w = wlookid(id);
  443. if(w == 0){
  444. strcpy(err, "no such window id");
  445. return -1;
  446. }
  447. }
  448. switch(cmd){
  449. case New:
  450. return wctlnew(r, arg, pid, hideit, scrollit, dir, err);
  451. case Set:
  452. if(pid > 0)
  453. wsetpid(w, pid, 0);
  454. return 1;
  455. }
  456. incref(&w->ref);
  457. id = wctlcmd(w, r, cmd, err);
  458. wclose(w);
  459. return id;
  460. }
  461. void
  462. wctlthread(void *v)
  463. {
  464. char *buf, *arg, *dir;
  465. int cmd, id, pid, hideit, scrollit;
  466. Rectangle rect;
  467. char err[ERRMAX];
  468. Channel *c;
  469. c = v;
  470. threadsetname("WCTLTHREAD");
  471. for(;;){
  472. buf = recvp(c);
  473. cmd = parsewctl(&arg, ZR, &rect, &pid, &id, &hideit, &scrollit, &dir, buf, err);
  474. switch(cmd){
  475. case New:
  476. wctlnew(rect, arg, pid, hideit, scrollit, dir, err);
  477. }
  478. free(buf);
  479. }
  480. }
  481. void
  482. wctlproc(void *v)
  483. {
  484. char *buf;
  485. int n, eofs;
  486. Channel *c;
  487. threadsetname("WCTLPROC");
  488. c = v;
  489. eofs = 0;
  490. for(;;){
  491. buf = emalloc(messagesize);
  492. n = jehanne_read(wctlfd, buf, messagesize-1); /* room for \0 */
  493. if(n < 0)
  494. break;
  495. if(n == 0){
  496. if(++eofs > 20)
  497. break;
  498. continue;
  499. }
  500. eofs = 0;
  501. buf[n] = '\0';
  502. sendp(c, buf);
  503. }
  504. }