status.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619
  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. if(ctx->barwin)
  56. SLIST_INIT(&ctx->barwin->statusmousebinds);
  57. status_flush_list(ctx);
  58. status_gcache_free(ctx);
  59. }
  60. static void
  61. status_graph_draw(struct status_ctx *ctx, struct status_seq *sq, struct status_gcache *gc)
  62. {
  63. int i, j, y;
  64. int ys = sq->geo.y + sq->geo.h - 1;
  65. XSetForeground(W->dpy, W->gc, sq->color2);
  66. for(i = sq->geo.x + sq->geo.w - 1, j = gc->ndata - 1;
  67. j >= 0 && i >= sq->geo.x;
  68. --j, --i)
  69. {
  70. /* You divided by zero didn't you? */
  71. if(gc->datas[j])
  72. {
  73. y = ys - (sq->geo.h / ((float)sq->data[2] / (float)gc->datas[j])) + 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. /* 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 + sq->geo.w, 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->geo.w > sq->geo.h)
  300. g.w /= ((float)sq->data[2] / (float)sq->data[1]);
  301. else
  302. {
  303. g.y += g.h;
  304. g.h /= ((float)sq->data[2] / (float)sq->data[1]);
  305. g.y -= g.h;
  306. }
  307. draw_rect(ctx->barwin->dr, &g, sq->color2);
  308. STORE_MOUSEBIND();
  309. break;
  310. /* Position */
  311. case 'P':
  312. NOALIGN_Y();
  313. STATUS_ALIGN(sq->align);
  314. draw_rect(ctx->barwin->dr, &sq->geo, sq->color);
  315. g.x = sq->geo.x + ((sq->geo.w - sq->data[0]) / ((float)sq->data[2] / (float)sq->data[1]));
  316. g.y = sq->geo.y;
  317. g.w = sq->data[0];
  318. g.h = sq->geo.h;
  319. draw_rect(ctx->barwin->dr, &g, sq->color2);
  320. STORE_MOUSEBIND();
  321. break;
  322. /* Graph */
  323. case 'g':
  324. NOALIGN_Y();
  325. STATUS_ALIGN(sq->align);
  326. draw_rect(ctx->barwin->dr, &sq->geo, sq->color);
  327. status_graph_process(ctx, sq, sq->str);
  328. STORE_MOUSEBIND();
  329. break;
  330. /* Image */
  331. #ifdef HAVE_IMLIB2
  332. case 'i':
  333. draw_image_load(sq->str, &w, &h);
  334. if(sq->geo.w <= 0)
  335. sq->geo.w = w;
  336. if(sq->geo.h <= 0)
  337. sq->geo.h = h;
  338. if(sq->align != NoAlign)
  339. sq->geo.y = (ctx->barwin->geo.h >> 1) - (sq->geo.h >> 1);
  340. STATUS_ALIGN(sq->align);
  341. draw_image(ctx->barwin->dr, &sq->geo);
  342. STORE_MOUSEBIND();
  343. break;
  344. #endif /* HAVE_IMLIB2 */
  345. }
  346. }
  347. }
  348. /* Render current statustext of an element */
  349. void
  350. status_render(struct status_ctx *ctx)
  351. {
  352. if(!ctx->status)
  353. return;
  354. if(!(ctx->flags & STATUS_BLOCK_REFRESH))
  355. barwin_refresh_color(ctx->barwin);
  356. /* Use simple text instead sequence if no sequence found */
  357. if(SLIST_EMPTY(&ctx->statushead))
  358. {
  359. int l = draw_textw(ctx->theme, ctx->status);
  360. draw_text(ctx->barwin->dr, ctx->theme, ctx->barwin->geo.w - l,
  361. TEXTY(ctx->theme, ctx->barwin->geo.h), ctx->barwin->fg, ctx->status);
  362. }
  363. else
  364. status_apply_list(ctx);
  365. barwin_refresh(ctx->barwin);
  366. }
  367. void
  368. status_flush_list(struct status_ctx *ctx)
  369. {
  370. struct status_seq *sq;
  371. struct mousebind *m;
  372. /* Flush previous linked list of status sequences */
  373. while(!SLIST_EMPTY(&ctx->statushead))
  374. {
  375. sq = SLIST_FIRST(&ctx->statushead);
  376. SLIST_REMOVE_HEAD(&ctx->statushead, next);
  377. while(!SLIST_EMPTY(&sq->mousebinds))
  378. {
  379. m = SLIST_FIRST(&sq->mousebinds);
  380. SLIST_REMOVE_HEAD(&sq->mousebinds, snext);
  381. free((void*)m->cmd);
  382. free(m);
  383. }
  384. free(sq->str);
  385. free(sq);
  386. }
  387. }
  388. void
  389. status_copy_mousebind(struct status_ctx *ctx)
  390. {
  391. struct mousebind *m;
  392. struct status_seq *sq;
  393. if(!ctx->barwin)
  394. return;
  395. /* Flush barwin head of status mousebinds */
  396. SLIST_INIT(&ctx->barwin->statusmousebinds);
  397. SLIST_FOREACH(sq, &ctx->statushead, next)
  398. {
  399. SLIST_FOREACH(m, &sq->mousebinds, snext)
  400. SLIST_INSERT_HEAD(&ctx->barwin->statusmousebinds, m, next);
  401. }
  402. }
  403. /* Parse and render statustext */
  404. void
  405. status_manage(struct status_ctx *ctx)
  406. {
  407. if(!ctx->status)
  408. return;
  409. ctx->update = false;
  410. status_flush_list(ctx);
  411. status_parse(ctx);
  412. status_render(ctx);
  413. status_copy_mousebind(ctx);
  414. }
  415. void
  416. status_flush_surface(void)
  417. {
  418. struct barwin *b;
  419. while(!SLIST_EMPTY(&W->h.vbarwin))
  420. {
  421. b = SLIST_FIRST(&W->h.vbarwin);
  422. SLIST_REMOVE_HEAD(&W->h.vbarwin, vnext);
  423. barwin_remove(b);
  424. }
  425. }
  426. static void
  427. status_surface(int w, int h, Color bg, char *status)
  428. {
  429. struct barwin *b;
  430. struct status_ctx ctx;
  431. int d, x, y;
  432. if(!status)
  433. return;
  434. XQueryPointer(W->dpy, W->root, (Window*)&d, (Window*)&d, &x, &y, &d, &d, (unsigned int *)&d);
  435. b = barwin_new(W->root, x, y, w, h, 0, bg, false);
  436. barwin_map(b);
  437. ctx = status_new_ctx(b, SLIST_FIRST(&W->h.theme));
  438. ctx.status = xstrdup(status);
  439. SLIST_INSERT_HEAD(&W->h.vbarwin, b, vnext);
  440. status_manage(&ctx);
  441. status_free_ctx(&ctx);
  442. }
  443. void
  444. uicb_status_surface(Uicb cmd)
  445. {
  446. char *p, *ccmd = xstrdup(cmd);
  447. int w, h;
  448. Color bg;
  449. if(!ccmd || !(p = strchr(ccmd, ' ')))
  450. return;
  451. if(sscanf(ccmd, "%d,%d,#%x", &w, &h, &bg) != 3
  452. || !w || !h)
  453. {
  454. free(ccmd);
  455. return;
  456. }
  457. *p = '\0';
  458. ++p;
  459. status_surface(w, h, bg, p);
  460. free(ccmd);
  461. }
  462. /* Syntax: "<infobar name> <status string>" */
  463. void
  464. uicb_status(Uicb cmd)
  465. {
  466. struct infobar *ib;
  467. struct screen *s;
  468. char *p;
  469. if(!cmd || !(p = strchr(cmd, ' ')))
  470. return;
  471. /* Get infobar name & status */
  472. *p = '\0';
  473. ++p;
  474. SLIST_FOREACH(s, &W->h.screen, next)
  475. {
  476. SLIST_FOREACH(ib, &s->infobars, next)
  477. if(!strcmp(cmd, ib->name))
  478. {
  479. free(ib->statusctx.status);
  480. ib->statusctx.status = xstrdup(p);
  481. ib->statusctx.update = true;
  482. infobar_elem_screen_update(s, ElemStatus);
  483. }
  484. }
  485. }