status.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638
  1. /*
  2. * wmfs2 by Martin Duquesnoy <xorg62@gmail.com> { for(i = 2011; i < 2111; ++i) ©(i); }
  3. * For license, see COPYING.
  4. */
  5. #include "status.h"
  6. #include "barwin.h"
  7. #include "config.h"
  8. #include "infobar.h"
  9. #include "util.h"
  10. #include "draw.h"
  11. #include <string.h>
  12. struct status_seq*
  13. status_new_seq(char type, int narg, int minarg, char *args[], int *shift)
  14. {
  15. struct status_seq *sq = xcalloc(1, sizeof(struct status_seq));
  16. SLIST_INIT(&sq->mousebinds);
  17. sq->type = type;
  18. *shift = 0;
  19. if(narg == minarg || !strcmp(args[0], "right") || !strcmp(args[0], "left"))
  20. sq->align = str_to_position(args[0]);
  21. else
  22. {
  23. sq->align = NoAlign;
  24. sq->geo.x = ATOI(args[0]);
  25. sq->geo.y = ATOI(args[1]);
  26. *shift = 1;
  27. }
  28. return sq;
  29. }
  30. struct status_ctx
  31. status_new_ctx(struct barwin *b, struct theme *t)
  32. {
  33. struct status_ctx ctx = { .barwin = b, .theme = t };
  34. SLIST_INIT(&ctx.statushead);
  35. SLIST_INIT(&ctx.gcache);
  36. return ctx;
  37. }
  38. static void
  39. status_gcache_free(struct status_ctx *ctx)
  40. {
  41. struct status_gcache *gc;
  42. while(!SLIST_EMPTY(&ctx->gcache))
  43. {
  44. gc = SLIST_FIRST(&ctx->gcache);
  45. SLIST_REMOVE_HEAD(&ctx->gcache, next);
  46. free(gc->datas);
  47. free(gc->name);
  48. free(gc);
  49. }
  50. }
  51. void
  52. status_free_ctx(struct status_ctx *ctx)
  53. {
  54. free(ctx->status);
  55. status_flush_list(ctx);
  56. status_gcache_free(ctx);
  57. }
  58. static void
  59. status_graph_draw(struct status_ctx *ctx, struct status_seq *sq, struct status_gcache *gc)
  60. {
  61. int i, j, y;
  62. float c;
  63. int ys = sq->geo.y + sq->geo.h - 1;
  64. XSetForeground(W->dpy, W->gc, sq->color2);
  65. for(i = sq->geo.x + sq->geo.w - 1, j = gc->ndata - 1;
  66. j >= 0 && i >= sq->geo.x;
  67. --j, --i)
  68. {
  69. /* You divided by zero didn't you? */
  70. if(gc->datas[j])
  71. {
  72. c = (float)sq->data[2] / (float)gc->datas[j];
  73. y = ys - (sq->geo.h / (c > 1 ? c : 1)) + 1;
  74. draw_line(ctx->barwin->dr, i, y, i, ys);
  75. }
  76. }
  77. }
  78. static void
  79. status_graph_process(struct status_ctx *ctx, struct status_seq *sq, char *name)
  80. {
  81. int j;
  82. struct status_gcache *gc;
  83. /* Graph already exist and have a cache */
  84. SLIST_FOREACH(gc, &ctx->gcache, next)
  85. if(!strcmp(name, gc->name))
  86. {
  87. /* shift buffer to remove unused old value */
  88. if(gc->ndata > (sq->geo.w << 1))
  89. for(gc->ndata /= 2, j = 0;
  90. j < gc->ndata;
  91. gc->datas[j] = gc->datas[j + gc->ndata], ++j);
  92. gc->datas[gc->ndata++] = sq->data[1];
  93. status_graph_draw(ctx, sq, gc);
  94. return;
  95. }
  96. if(sq->data[1] > sq->data[2])
  97. sq->data[1] = sq->data[2];
  98. /* No? Make a cache for it */
  99. gc = xcalloc(1, sizeof(struct status_gcache));
  100. gc->name = xstrdup(name);
  101. gc->ndata = 1;
  102. gc->datas = xcalloc(sq->geo.w << 2, sizeof(int));
  103. gc->datas[0] = sq->data[1];
  104. SLIST_INSERT_HEAD(&ctx->gcache, gc, next);
  105. status_graph_draw(ctx, sq, gc);
  106. }
  107. /* Parse mousebind sequence next normal sequence: \<seq>[](button;func;cmd) */
  108. static char*
  109. status_parse_mouse(struct status_seq *sq, char *str)
  110. {
  111. struct mousebind *m;
  112. char *end, *arg[3] = { NULL };
  113. int i;
  114. if(*str != '(' || !(end = strchr(str, ')')))
  115. return str;
  116. i = parse_args(++str, ';', ')', 3, arg);
  117. m = xcalloc(1, sizeof(struct mousebind));
  118. m->use_area = true;
  119. m->button = ATOI(arg[0]);
  120. m->func = uicb_name_func(arg[1]);
  121. m->cmd = (i > 1 ? xstrdup(arg[2]) : NULL);
  122. SLIST_INSERT_HEAD(&sq->mousebinds, m, snext);
  123. return end + 1;
  124. }
  125. #define STATUS_CHECK_ARGS(i, n1, n2, str, end) \
  126. if(i != n1 && i != n2) \
  127. { \
  128. str = end + 2; \
  129. continue; \
  130. }
  131. void
  132. status_parse(struct status_ctx *ctx)
  133. {
  134. struct status_seq *sq, *prev = NULL;
  135. int i, tmp, shift = 0;
  136. char *dstr = xstrdup(ctx->status), *sauv = dstr;
  137. char type, *p, *pp, *end, *arg[10] = { NULL };
  138. for(; *dstr; ++dstr)
  139. {
  140. /* Check if this is a sequence */
  141. if(*dstr != '^' && *dstr != '\\')
  142. continue;
  143. p = ++dstr;
  144. /* Search for correct end of sequence (] without \ behind) */
  145. if((end = strchr(p, ']')))
  146. while(*(end - 1) == '\\')
  147. end = strchr(end + 1, ']');
  148. if(!(strchr("sRpPig", *p)) || !end)
  149. continue;
  150. /* Then parse & list it */
  151. switch((type = *p))
  152. {
  153. /*
  154. * Text sequence: \s[left/right;#color;text] OR \s[x;y;#color;text]
  155. */
  156. case 's':
  157. i = parse_args(p + 2, ';', ']', 4, arg);
  158. STATUS_CHECK_ARGS(i, 2, 3, dstr, end);
  159. sq = status_new_seq(type, i, 2, arg, &shift);
  160. sq->color = color_atoh(arg[1 + shift]);
  161. sq->str = xstrdup(arg[2 + shift]);
  162. /* Remove \ from string */
  163. for(pp = sq->str; (pp = strchr(sq->str, '\\'));)
  164. memmove(pp, pp + 1, strlen(pp));
  165. break;
  166. /*
  167. * Rectangle sequence: \R[left/right;w;h;#color] OR \R[x;y;w;h;#color]
  168. */
  169. case 'R':
  170. i = parse_args(p + 2, ';', ']', 5, arg);
  171. STATUS_CHECK_ARGS(i, 3, 4, dstr, end);
  172. sq = status_new_seq(type, i, 3, arg, &shift);
  173. sq->geo.w = ATOI(arg[1 + shift]);
  174. sq->geo.h = ATOI(arg[2 + shift]);
  175. sq->color = color_atoh(arg[3 + shift]);
  176. break;
  177. /*
  178. * Progress bar sequence: \p[left/right;w;h;bord;val;valmax;bg;fg] OR x;y
  179. * Position bar sequence: \P[left/right;w;h;tickbord;val;valmax;bg;fg] OR x;y
  180. */
  181. case 'p':
  182. case 'P':
  183. i = parse_args(p + 2, ';', ']', 9, arg);
  184. STATUS_CHECK_ARGS(i, 7, 8, dstr, end);
  185. sq = status_new_seq(type, i, 7, arg, &shift);
  186. sq->geo.w = ATOI(arg[1 + shift]);
  187. sq->geo.h = ATOI(arg[2 + shift]);
  188. sq->data[0] = ATOI(arg[3 + shift]); /* Border */
  189. sq->data[1] = ((tmp = ATOI(arg[4 + shift])) ? tmp : 1); /* Value */
  190. sq->data[2] = ATOI(arg[5 + shift]); /* Value Max */
  191. sq->color = color_atoh(arg[6 + shift]);
  192. sq->color2 = color_atoh(arg[7 + shift]);
  193. break;
  194. /*
  195. * Graph sequence: \g[left/right;w;h;val;valmax;bg;fg;name] OR x;y
  196. */
  197. case 'g':
  198. i = parse_args(p + 2, ';', ']', 9, arg);
  199. STATUS_CHECK_ARGS(i, 7, 8, dstr, end);
  200. sq = status_new_seq(type, i, 7, arg, &shift);
  201. sq->geo.w = ATOI(arg[1 + shift]);
  202. sq->geo.h = ATOI(arg[2 + shift]);
  203. sq->data[1] = ATOI(arg[3 + shift]); /* Value */
  204. sq->data[2] = ATOI(arg[4 + shift]); /* Value Max */
  205. sq->color = color_atoh(arg[5 + shift]);
  206. sq->color2 = color_atoh(arg[6 + shift]);
  207. sq->str = xstrdup(arg[7 + shift]);
  208. break;
  209. /*
  210. * Image sequence: \i[left/right;w;h;/path/img] OR \i[x;y;w;h;/path/img]
  211. */
  212. #ifdef HAVE_IMLIB2
  213. case 'i':
  214. i = parse_args(p + 2, ';', ']', 5, arg);
  215. STATUS_CHECK_ARGS(i, 3, 4, dstr, end);
  216. sq = status_new_seq(type, i, 3, arg, &shift);
  217. sq->geo.w = ATOI(arg[1 + shift]);
  218. sq->geo.h = ATOI(arg[2 + shift]);
  219. sq->str = xstrdup(arg[3 + shift]);
  220. break;
  221. #endif /* HAVE_IMLIB2 */
  222. }
  223. if(sq->align == Right)
  224. SLIST_INSERT_HEAD(&ctx->statushead, sq, next);
  225. else
  226. SLIST_INSERT_TAIL(&ctx->statushead, sq, next, prev);
  227. /*
  228. * Optional mousebind sequence(s) \<seq>[](button;func;cmd)
  229. * Parse it while there is a mousebind sequence.
  230. */
  231. dstr = end + 1;
  232. do
  233. dstr = status_parse_mouse(sq, dstr);
  234. while(*dstr == '(');
  235. --dstr;
  236. prev = sq;
  237. }
  238. free(sauv);
  239. }
  240. #define STATUS_ALIGN(align) \
  241. if(align == Left) \
  242. { \
  243. sq->geo.x = left; \
  244. left += sq->geo.w; \
  245. } \
  246. else if(align == Right) \
  247. { \
  248. sq->geo.x = ctx->barwin->geo.w - right - sq->geo.w; \
  249. right += sq->geo.w; \
  250. }
  251. #define STORE_MOUSEBIND() \
  252. if(!SLIST_EMPTY(&sq->mousebinds)) \
  253. SLIST_FOREACH(m, &sq->mousebinds, snext) \
  254. m->area = sq->geo;
  255. #define NOALIGN_Y() \
  256. if(sq->align != NoAlign) \
  257. sq->geo.y = (ctx->barwin->geo.h >> 1) - (sq->geo.h >> 1);
  258. static void
  259. status_apply_list(struct status_ctx *ctx)
  260. {
  261. struct status_seq *sq;
  262. struct mousebind *m;
  263. struct geo g;
  264. int left = 0, right = 0, w, h;
  265. SLIST_FOREACH(sq, &ctx->statushead, next)
  266. {
  267. switch(sq->type)
  268. {
  269. /* Text */
  270. case 's':
  271. sq->geo.w = draw_textw(ctx->theme, sq->str);
  272. sq->geo.h = ctx->theme->font.height;
  273. if(sq->align != NoAlign)
  274. sq->geo.y = TEXTY(ctx->theme, ctx->barwin->geo.h);
  275. STATUS_ALIGN(sq->align);
  276. draw_text(ctx->barwin->dr, ctx->theme, sq->geo.x, sq->geo.y, sq->color, sq->str);
  277. if(!SLIST_EMPTY(&sq->mousebinds))
  278. SLIST_FOREACH(m, &sq->mousebinds, snext)
  279. {
  280. m->area = sq->geo;
  281. m->area.y -= sq->geo.h;
  282. }
  283. break;
  284. /* Rectangle */
  285. case 'R':
  286. NOALIGN_Y();
  287. STATUS_ALIGN(sq->align);
  288. draw_rect(ctx->barwin->dr, &sq->geo, sq->color);
  289. STORE_MOUSEBIND();
  290. break;
  291. /* Progress */
  292. case 'p':
  293. NOALIGN_Y();
  294. STATUS_ALIGN(sq->align);
  295. draw_rect(ctx->barwin->dr, &sq->geo, sq->color);
  296. /* Progress bar geo */
  297. g.x = sq->geo.x + sq->data[0];
  298. g.y = sq->geo.y + sq->data[0];
  299. g.w = sq->geo.w - sq->data[0] - sq->data[0];
  300. g.h = sq->geo.h - sq->data[0] - sq->data[0];
  301. if(sq->data[1] > sq->data[2])
  302. sq->data[1] = sq->data[2];
  303. if(sq->geo.w > sq->geo.h)
  304. g.w /= ((float)sq->data[2] / (float)sq->data[1]);
  305. else
  306. {
  307. g.y += g.h;
  308. g.h /= ((float)sq->data[2] / (float)sq->data[1]);
  309. g.y -= g.h;
  310. }
  311. draw_rect(ctx->barwin->dr, &g, sq->color2);
  312. STORE_MOUSEBIND();
  313. break;
  314. /* Position */
  315. case 'P':
  316. NOALIGN_Y();
  317. STATUS_ALIGN(sq->align);
  318. draw_rect(ctx->barwin->dr, &sq->geo, sq->color);
  319. if(sq->data[1] > sq->data[2])
  320. sq->data[1] = sq->data[2];
  321. g.x = sq->geo.x + ((sq->geo.w - sq->data[0]) / ((float)sq->data[2] / (float)sq->data[1]));
  322. g.y = sq->geo.y;
  323. g.w = sq->data[0];
  324. g.h = sq->geo.h;
  325. draw_rect(ctx->barwin->dr, &g, sq->color2);
  326. STORE_MOUSEBIND();
  327. break;
  328. /* Graph */
  329. case 'g':
  330. NOALIGN_Y();
  331. STATUS_ALIGN(sq->align);
  332. draw_rect(ctx->barwin->dr, &sq->geo, sq->color);
  333. status_graph_process(ctx, sq, sq->str);
  334. STORE_MOUSEBIND();
  335. break;
  336. /* Image */
  337. #ifdef HAVE_IMLIB2
  338. case 'i':
  339. draw_image_load(sq->str, &w, &h);
  340. if(sq->geo.w <= 0)
  341. sq->geo.w = w;
  342. if(sq->geo.h <= 0)
  343. sq->geo.h = h;
  344. if(sq->align != NoAlign)
  345. sq->geo.y = (ctx->barwin->geo.h >> 1) - (sq->geo.h >> 1);
  346. STATUS_ALIGN(sq->align);
  347. draw_image(ctx->barwin->dr, &sq->geo);
  348. STORE_MOUSEBIND();
  349. break;
  350. #endif /* HAVE_IMLIB2 */
  351. }
  352. }
  353. }
  354. /* Render current statustext of an element */
  355. void
  356. status_render(struct status_ctx *ctx)
  357. {
  358. if(!ctx->status)
  359. return;
  360. if(!(ctx->flags & STATUS_BLOCK_REFRESH))
  361. barwin_refresh_color(ctx->barwin);
  362. /* Use simple text instead sequence if no sequence found */
  363. if(SLIST_EMPTY(&ctx->statushead))
  364. {
  365. int l = draw_textw(ctx->theme, ctx->status);
  366. draw_text(ctx->barwin->dr, ctx->theme, ctx->barwin->geo.w - l,
  367. TEXTY(ctx->theme, ctx->barwin->geo.h), ctx->barwin->fg, ctx->status);
  368. }
  369. else
  370. status_apply_list(ctx);
  371. barwin_refresh(ctx->barwin);
  372. }
  373. void
  374. status_flush_list(struct status_ctx *ctx)
  375. {
  376. struct status_seq *sq;
  377. struct mousebind *m;
  378. /* Flush previous linked list of status sequences */
  379. while(!SLIST_EMPTY(&ctx->statushead))
  380. {
  381. sq = SLIST_FIRST(&ctx->statushead);
  382. SLIST_REMOVE_HEAD(&ctx->statushead, next);
  383. while(!SLIST_EMPTY(&sq->mousebinds))
  384. {
  385. m = SLIST_FIRST(&sq->mousebinds);
  386. SLIST_REMOVE_HEAD(&sq->mousebinds, snext);
  387. free((void*)m->cmd);
  388. free(m);
  389. }
  390. free(sq->str);
  391. free(sq);
  392. }
  393. SLIST_INIT(&ctx->statushead);
  394. }
  395. void
  396. status_copy_mousebind(struct status_ctx *ctx)
  397. {
  398. struct mousebind *m;
  399. struct status_seq *sq;
  400. if(!ctx->barwin)
  401. return;
  402. /* Flush barwin head of status mousebinds */
  403. SLIST_INIT(&ctx->barwin->statusmousebinds);
  404. SLIST_FOREACH(sq, &ctx->statushead, next)
  405. {
  406. SLIST_FOREACH(m, &sq->mousebinds, snext)
  407. SLIST_INSERT_HEAD(&ctx->barwin->statusmousebinds, m, next);
  408. }
  409. }
  410. /* Parse and render statustext */
  411. void
  412. status_manage(struct status_ctx *ctx)
  413. {
  414. if(!ctx->status)
  415. return;
  416. ctx->update = false;
  417. status_flush_list(ctx);
  418. status_parse(ctx);
  419. status_render(ctx);
  420. status_copy_mousebind(ctx);
  421. }
  422. void
  423. status_flush_surface(void)
  424. {
  425. struct barwin *b;
  426. while(!SLIST_EMPTY(&W->h.vbarwin))
  427. {
  428. b = SLIST_FIRST(&W->h.vbarwin);
  429. SLIST_REMOVE_HEAD(&W->h.vbarwin, vnext);
  430. barwin_remove(b);
  431. }
  432. }
  433. static void
  434. status_surface(int x, int y, int w, int h, Color bg, char *status)
  435. {
  436. struct barwin *b;
  437. struct screen *s;
  438. struct status_ctx ctx;
  439. int d;
  440. Window rw;
  441. if(!status)
  442. return;
  443. if(x + y < 0)
  444. XQueryPointer(W->dpy, W->root, &rw, &rw, &x, &y, &d, &d, (unsigned int *)&d);
  445. s = screen_gb_geo(x, y);
  446. if(x + w > s->geo.x + s->geo.w)
  447. x -= w;
  448. if(y + h > s->geo.y + s->geo.h)
  449. y -= h;
  450. b = barwin_new(W->root, x, y, w, h, 0, bg, false);
  451. barwin_map(b);
  452. /* Use client theme */
  453. ctx = status_new_ctx(b, W->ctheme);
  454. ctx.status = xstrdup(status);
  455. SLIST_INSERT_HEAD(&W->h.vbarwin, b, vnext);
  456. status_manage(&ctx);
  457. status_free_ctx(&ctx);
  458. }
  459. void
  460. uicb_status_surface(Uicb cmd)
  461. {
  462. char *p, *ccmd = xstrdup(cmd);
  463. int s, w, h, x = -1, y = -1;
  464. Color bg;
  465. if(!ccmd || !(p = strchr(ccmd, ' ')))
  466. return;
  467. *p = '\0';
  468. ++p;
  469. if(!(((s = sscanf(ccmd, "%d,%d,#%x", &w, &h, &bg)) == 3)
  470. || (s = sscanf(ccmd, "%d,%d,%d,%d,#%x", &x, &y, &w, &h, &bg)) == 5))
  471. {
  472. free(ccmd);
  473. return;
  474. }
  475. status_surface(x, y, w, h, bg, p);
  476. free(ccmd);
  477. }
  478. /* Syntax: "<infobar name> <status string>" */
  479. void
  480. uicb_status(Uicb cmd)
  481. {
  482. struct infobar *ib;
  483. struct screen *s;
  484. char *p;
  485. if(!cmd || !(p = strchr(cmd, ' ')))
  486. return;
  487. /* Get infobar name & status */
  488. *p = '\0';
  489. ++p;
  490. SLIST_FOREACH(s, &W->h.screen, next)
  491. {
  492. SLIST_FOREACH(ib, &s->infobars, next)
  493. if(!strcmp(cmd, ib->name))
  494. {
  495. free(ib->statusctx.status);
  496. ib->statusctx.status = xstrdup(p);
  497. ib->statusctx.update = true;
  498. infobar_elem_screen_update(s, ElemStatus);
  499. }
  500. }
  501. }