status.c 17 KB

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