status.c 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  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. return ctx;
  36. }
  37. void
  38. status_free_ctx(struct status_ctx *ctx)
  39. {
  40. free(ctx->status);
  41. if(ctx->barwin)
  42. SLIST_INIT(&ctx->barwin->statusmousebinds);
  43. status_flush_list(ctx);
  44. }
  45. /* Parse mousebind sequence next normal sequence: \<seq>[](button;func;cmd) */
  46. static char*
  47. status_parse_mouse(struct status_seq *sq, char *str)
  48. {
  49. struct mousebind *m;
  50. char *end, *arg[3] = { NULL };
  51. int i;
  52. if(*str != '(' || !(end = strchr(str, ')')))
  53. return str;
  54. i = parse_args(++str, ';', ')', 3, arg);
  55. m = xcalloc(1, sizeof(struct mousebind));
  56. m->use_area = true;
  57. m->button = ATOI(arg[0]);
  58. m->func = uicb_name_func(arg[1]);
  59. m->cmd = (i > 1 ? xstrdup(arg[2]) : NULL);
  60. SLIST_INSERT_HEAD(&sq->mousebinds, m, snext);
  61. return end + 1;
  62. }
  63. #define STATUS_CHECK_ARGS(i, n1, n2, str, end) \
  64. if(i != n1 && i != n2) \
  65. { \
  66. str = end + 2; \
  67. continue; \
  68. }
  69. void
  70. status_parse(struct status_ctx *ctx)
  71. {
  72. struct status_seq *sq, *prev = NULL;
  73. int i, shift = 0;
  74. char *dstr = xstrdup(ctx->status), *sauv = dstr;
  75. char type, *p, *end, *arg[6] = { NULL };
  76. for(; *dstr; ++dstr)
  77. {
  78. /* Check if this is a sequence */
  79. if(*dstr != '^' && *dstr != '\\')
  80. continue;
  81. p = ++dstr;
  82. if(!(strchr("sRi", *p)) || !(end = strchr(p, ']')))
  83. continue;
  84. /* Then parse & list it */
  85. switch((type = *p))
  86. {
  87. /*
  88. * Text sequence: \s[left/right;#color;text] OR \s[x;y;#color;text]
  89. */
  90. case 's':
  91. i = parse_args(p + 2, ';', ']', 4, arg);
  92. STATUS_CHECK_ARGS(i, 2, 3, dstr, end);
  93. sq = status_new_seq(type, i, 2, arg, &shift);
  94. sq->color = color_atoh(arg[1 + shift]);
  95. sq->str = xstrdup(arg[2 + shift]);
  96. break;
  97. /*
  98. * Rectangle sequence: \R[left/right;w;h;#color] OR \R[x;y;w;h;#color]
  99. */
  100. case 'R':
  101. i = parse_args(p + 2, ';', ']', 5, arg);
  102. STATUS_CHECK_ARGS(i, 3, 4, dstr, end);
  103. sq = status_new_seq(type, i, 3, arg, &shift);
  104. sq->geo.w = ATOI(arg[1 + shift]);
  105. sq->geo.h = ATOI(arg[2 + shift]);
  106. sq->color = color_atoh(arg[3 + shift]);
  107. break;
  108. /*
  109. * Image sequence: \i[left/right;w;h;/path/img] OR \i[x;y;w;h;/path/img]
  110. */
  111. #ifdef HAVE_IMLIB2
  112. case 'i':
  113. i = parse_args(p + 2, ';', ']', 5, arg);
  114. STATUS_CHECK_ARGS(i, 3, 4, dstr, end);
  115. sq = status_new_seq(type, i, 3, arg, &shift);
  116. sq->geo.w = ATOI(arg[1 + shift]);
  117. sq->geo.h = ATOI(arg[2 + shift]);
  118. sq->str = xstrdup(arg[3 + shift]);
  119. break;
  120. #endif /* HAVE_IMLIB2 */
  121. }
  122. SLIST_INSERT_TAIL(&ctx->statushead, sq, next, prev);
  123. /*
  124. * Optional mousebind sequence(s) \<seq>[](button;func;cmd)
  125. * Parse it while there is a mousebind sequence.
  126. */
  127. dstr = end + 1;
  128. while((*(dstr = status_parse_mouse(sq, dstr)) == '('));
  129. --dstr;
  130. prev = sq;
  131. }
  132. free(sauv);
  133. }
  134. #define STATUS_ALIGN(align) \
  135. if(align == Left) \
  136. { \
  137. sq->geo.x = left; \
  138. left += sq->geo.w; \
  139. } \
  140. else if(align == Right) \
  141. { \
  142. sq->geo.x = ctx->barwin->geo.w - right - sq->geo.w; \
  143. right += sq->geo.w; \
  144. }
  145. static void
  146. status_apply_list(struct status_ctx *ctx)
  147. {
  148. struct status_seq *sq;
  149. struct mousebind *m;
  150. int left = 0, right = 0, w, h;
  151. SLIST_FOREACH(sq, &ctx->statushead, next)
  152. {
  153. switch(sq->type)
  154. {
  155. /* Text */
  156. case 's':
  157. sq->geo.w = draw_textw(ctx->theme, sq->str);
  158. sq->geo.h = ctx->theme->font.height;
  159. if(sq->align != NoAlign)
  160. sq->geo.y = TEXTY(ctx->theme, ctx->barwin->geo.h);
  161. STATUS_ALIGN(sq->align);
  162. draw_text(ctx->barwin->dr, ctx->theme, sq->geo.x, sq->geo.y, sq->color, sq->str);
  163. if(!SLIST_EMPTY(&sq->mousebinds))
  164. SLIST_FOREACH(m, &sq->mousebinds, snext)
  165. {
  166. m->area = sq->geo;
  167. m->area.y -= sq->geo.h;
  168. }
  169. break;
  170. /* Rectangle */
  171. case 'R':
  172. if(sq->align != NoAlign)
  173. sq->geo.y = (ctx->barwin->geo.h >> 1) - (sq->geo.h >> 1);
  174. STATUS_ALIGN(sq->align);
  175. draw_rect(ctx->barwin->dr, &sq->geo, sq->color);
  176. if(!SLIST_EMPTY(&sq->mousebinds))
  177. SLIST_FOREACH(m, &sq->mousebinds, snext)
  178. m->area = sq->geo;
  179. break;
  180. /* Image */
  181. #ifdef HAVE_IMLIB2
  182. case 'i':
  183. draw_image_load(sq->str, &w, &h);
  184. if(sq->geo.w <= 0)
  185. sq->geo.w = w;
  186. if(sq->geo.h <= 0)
  187. sq->geo.h = h;
  188. if(sq->align != NoAlign)
  189. sq->geo.y = (ctx->barwin->geo.h >> 1) - (sq->geo.h >> 1);
  190. STATUS_ALIGN(sq->align);
  191. draw_image(ctx->barwin->dr, &sq->geo);
  192. if(!SLIST_EMPTY(&sq->mousebinds))
  193. SLIST_FOREACH(m, &sq->mousebinds, snext)
  194. m->area = sq->geo;
  195. break;
  196. #endif /* HAVE_IMLIB2 */
  197. }
  198. }
  199. }
  200. /* Render current statustext of an element */
  201. void
  202. status_render(struct status_ctx *ctx)
  203. {
  204. if(!ctx->status)
  205. return;
  206. barwin_refresh_color(ctx->barwin);
  207. /* Use simple text instead sequence if no sequence found */
  208. if(SLIST_EMPTY(&ctx->statushead))
  209. {
  210. int l = draw_textw(ctx->theme, ctx->status);
  211. draw_text(ctx->barwin->dr, ctx->theme, ctx->barwin->geo.w - l,
  212. TEXTY(ctx->theme, ctx->barwin->geo.h), ctx->barwin->fg, ctx->status);
  213. }
  214. else
  215. status_apply_list(ctx);
  216. barwin_refresh(ctx->barwin);
  217. }
  218. void
  219. status_flush_list(struct status_ctx *ctx)
  220. {
  221. struct status_seq *sq;
  222. struct mousebind *m;
  223. /* Flush previous linked list of status sequences */
  224. while(!SLIST_EMPTY(&ctx->statushead))
  225. {
  226. sq = SLIST_FIRST(&ctx->statushead);
  227. SLIST_REMOVE_HEAD(&ctx->statushead, next);
  228. while(!SLIST_EMPTY(&sq->mousebinds))
  229. {
  230. m = SLIST_FIRST(&sq->mousebinds);
  231. SLIST_REMOVE_HEAD(&sq->mousebinds, snext);
  232. free((void*)m->cmd);
  233. free(m);
  234. }
  235. free(sq->str);
  236. free(sq);
  237. }
  238. }
  239. void
  240. status_copy_mousebind(struct status_ctx *ctx)
  241. {
  242. struct mousebind *m;
  243. struct status_seq *sq;
  244. if(!ctx->barwin)
  245. return;
  246. /* Flush barwin head of status mousebinds */
  247. SLIST_INIT(&ctx->barwin->statusmousebinds);
  248. SLIST_FOREACH(sq, &ctx->statushead, next)
  249. {
  250. SLIST_FOREACH(m, &sq->mousebinds, snext)
  251. SLIST_INSERT_HEAD(&ctx->barwin->statusmousebinds, m, next);
  252. }
  253. }
  254. /* Parse and render statustext */
  255. void
  256. status_manage(struct status_ctx *ctx)
  257. {
  258. if(!ctx->status)
  259. return;
  260. ctx->update = false;
  261. status_flush_list(ctx);
  262. status_parse(ctx);
  263. status_render(ctx);
  264. status_copy_mousebind(ctx);
  265. }
  266. /* Syntax: "<infobar name> <status string>" */
  267. void
  268. uicb_status(Uicb cmd)
  269. {
  270. struct infobar *ib;
  271. struct screen *s;
  272. char *p;
  273. if(!cmd || !(p = strchr(cmd, ' ')))
  274. return;
  275. /* Get infobar name & status */
  276. *p = '\0';
  277. ++p;
  278. SLIST_FOREACH(s, &W->h.screen, next)
  279. {
  280. SLIST_FOREACH(ib, &s->infobars, next)
  281. if(!strcmp(cmd, ib->name))
  282. {
  283. free(ib->statusctx.status);
  284. ib->statusctx.status = xstrdup(p);
  285. ib->statusctx.update = true;
  286. infobar_elem_screen_update(s, ElemStatus);
  287. }
  288. }
  289. }