herbe.c 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. #include <X11/Xlib.h>
  2. #include <X11/Xft/Xft.h>
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <signal.h>
  6. #include <unistd.h>
  7. #include <string.h>
  8. #include <stdarg.h>
  9. #include <fcntl.h>
  10. #include <semaphore.h>
  11. #include "config.h"
  12. Display *display;
  13. Window window;
  14. static void die(const char *format, ...)
  15. {
  16. va_list ap;
  17. va_start(ap, format);
  18. vfprintf(stderr, format, ap);
  19. fprintf(stderr, "\n");
  20. va_end(ap);
  21. exit(EXIT_FAILURE);
  22. }
  23. int get_max_len(char *body, XftFont *font, int max_text_width)
  24. {
  25. int eol = strlen(body);
  26. XGlyphInfo info;
  27. XftTextExtentsUtf8(display, font, (FcChar8 *)body, eol, &info);
  28. if (info.width > max_text_width)
  29. {
  30. eol = max_text_width / font->max_advance_width;
  31. info.width = 0;
  32. while (info.width < max_text_width)
  33. {
  34. eol++;
  35. XftTextExtentsUtf8(display, font, (FcChar8 *)body, eol, &info);
  36. }
  37. eol--;
  38. }
  39. for (int i = 0; i < eol; i++)
  40. if (body[i] == '\n')
  41. return ++i;
  42. if (info.width < max_text_width)
  43. return eol;
  44. int temp = eol;
  45. while (body[eol] != ' ' && eol)
  46. --eol;
  47. if (eol == 0)
  48. return temp;
  49. else
  50. return ++eol;
  51. }
  52. void expire()
  53. {
  54. XEvent event;
  55. event.type = ButtonPress;
  56. XSendEvent(display, window, 0, 0, &event);
  57. XFlush(display);
  58. }
  59. int main(int argc, char *argv[])
  60. {
  61. if (argc == 1)
  62. die("Usage: %s body", argv[0]);
  63. signal(SIGALRM, expire);
  64. signal(SIGTERM, expire);
  65. signal(SIGINT, expire);
  66. display = XOpenDisplay(0);
  67. if (display == 0)
  68. die("Cannot open display");
  69. int screen = DefaultScreen(display);
  70. Visual *visual = DefaultVisual(display, screen);
  71. Colormap colormap = DefaultColormap(display, screen);
  72. int screen_width = DisplayWidth(display, screen);
  73. int screen_height = DisplayHeight(display, screen);
  74. XftColor color;
  75. XSetWindowAttributes attributes;
  76. attributes.override_redirect = True;
  77. XftColorAllocName(display, visual, colormap, background_color, &color);
  78. attributes.background_pixel = color.pixel;
  79. XftColorAllocName(display, visual, colormap, border_color, &color);
  80. attributes.border_pixel = color.pixel;
  81. XftFont *font = XftFontOpenName(display, screen, font_pattern);
  82. int num_of_lines = 0;
  83. int max_text_width = width - 2 * padding;
  84. int words_size = 5;
  85. char **words = malloc(words_size * sizeof(char *));
  86. if (!words)
  87. die("malloc failed");
  88. for (int i = 1; i < argc; i++)
  89. {
  90. char *body = argv[i];
  91. for (unsigned int eol = get_max_len(body, font, max_text_width); eol <= strlen(body) && eol; body += eol, num_of_lines++, eol = get_max_len(body, font, max_text_width))
  92. {
  93. if (words_size <= num_of_lines)
  94. {
  95. words = realloc(words, (words_size += 5) * sizeof(char *));
  96. if (!words)
  97. die("malloc failed");
  98. }
  99. words[num_of_lines] = malloc((eol + 1) * sizeof(char));
  100. if (!words[num_of_lines])
  101. die("malloc failed");
  102. strncpy(words[num_of_lines], body, eol);
  103. words[num_of_lines][eol] = '\0';
  104. }
  105. }
  106. unsigned int x = pos_x;
  107. unsigned int y = pos_y;
  108. unsigned int text_height = font->ascent - font->descent;
  109. unsigned int height = (num_of_lines - 1) * line_spacing + num_of_lines * text_height + 2 * padding;
  110. if (corner == TOP_RIGHT || corner == BOTTOM_RIGHT)
  111. x = screen_width - width - border_size * 2 - pos_x;
  112. if (corner == BOTTOM_LEFT || corner == BOTTOM_RIGHT)
  113. y = screen_height - height - border_size * 2 - pos_y;
  114. window = XCreateWindow(display, RootWindow(display, screen), x, y, width, height, border_size, DefaultDepth(display, screen), CopyFromParent, visual,
  115. CWOverrideRedirect | CWBackPixel | CWBorderPixel, &attributes);
  116. XftDraw *draw = XftDrawCreate(display, window, visual, colormap);
  117. XftColorAllocName(display, visual, colormap, font_color, &color);
  118. XSelectInput(display, window, ExposureMask | ButtonPress);
  119. XMapWindow(display, window);
  120. sem_t *mutex = sem_open("/herbe", O_CREAT, 0644, 1);
  121. sem_wait(mutex);
  122. if (duration != 0)
  123. alarm(duration);
  124. while (1)
  125. {
  126. XEvent event;
  127. XNextEvent(display, &event);
  128. if (event.type == Expose)
  129. {
  130. XClearWindow(display, window);
  131. for (int i = 0; i < num_of_lines; i++)
  132. XftDrawStringUtf8(draw, &color, font, padding, line_spacing * i + text_height * (i + 1) + padding, (FcChar8 *)words[i], strlen(words[i]));
  133. }
  134. if (event.type == ButtonPress)
  135. break;
  136. }
  137. sem_post(mutex);
  138. sem_close(mutex);
  139. for (int i = 0; i < num_of_lines; i++)
  140. free(words[i]);
  141. free(words);
  142. XftDrawDestroy(draw);
  143. XftColorFree(display, visual, colormap, &color);
  144. XftFontClose(display, font);
  145. XCloseDisplay(display);
  146. exit(EXIT_SUCCESS);
  147. }