Add support for 'actions'
[herbe.git] / herbe.c
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
12 #include "config.h"
13
14 #define EXIT_ACTION 3
15
16 Display *display;
17 Window window;
18 int exit_code = EXIT_SUCCESS;
19
20 static void die(const char *format, ...)
21 {
22 va_list ap;
23 va_start(ap, format);
24 vfprintf(stderr, format, ap);
25 fprintf(stderr, "\n");
26 va_end(ap);
27 exit(EXIT_FAILURE);
28 }
29
30 int get_max_len(char *body, XftFont *font, int max_text_width)
31 {
32 int eol = strlen(body);
33 XGlyphInfo info;
34 XftTextExtentsUtf8(display, font, (FcChar8 *)body, eol, &info);
35
36 if (info.width > max_text_width)
37 {
38
39 eol = max_text_width / font->max_advance_width;
40 info.width = 0;
41
42 while (info.width < max_text_width)
43 {
44 eol++;
45 XftTextExtentsUtf8(display, font, (FcChar8 *)body, eol, &info);
46 }
47
48 eol--;
49 }
50
51 for (int i = 0; i < eol; i++)
52 if (body[i] == '\n')
53 {
54 body[i] = ' ';
55 return ++i;
56 }
57
58 if (info.width < max_text_width)
59 return eol;
60
61 int temp = eol;
62
63 while (body[eol] != ' ' && eol)
64 --eol;
65
66 if (eol == 0)
67 return temp;
68 else
69 return ++eol;
70 }
71
72 void expire()
73 {
74 XEvent event;
75 event.type = ButtonPress;
76 XSendEvent(display, window, 0, 0, &event);
77 XFlush(display);
78 }
79
80 void action()
81 {
82 exit_code = EXIT_ACTION;
83 expire();
84 }
85
86 int main(int argc, char *argv[])
87 {
88 if (argc == 1)
89 die("Usage: %s body", argv[0]);
90
91 signal(SIGALRM, expire);
92 signal(SIGTERM, expire);
93 signal(SIGINT, expire);
94 signal(SIGUSR1, SIG_IGN);
95 signal(SIGUSR2, SIG_IGN);
96
97 display = XOpenDisplay(0);
98
99 if (display == 0)
100 die("Cannot open display");
101
102 int screen = DefaultScreen(display);
103 Visual *visual = DefaultVisual(display, screen);
104 Colormap colormap = DefaultColormap(display, screen);
105
106 int screen_width = DisplayWidth(display, screen);
107 int screen_height = DisplayHeight(display, screen);
108
109 XftColor color;
110
111 XSetWindowAttributes attributes;
112 attributes.override_redirect = True;
113 XftColorAllocName(display, visual, colormap, background_color, &color);
114 attributes.background_pixel = color.pixel;
115 XftColorAllocName(display, visual, colormap, border_color, &color);
116 attributes.border_pixel = color.pixel;
117
118 XftFont *font = XftFontOpenName(display, screen, font_pattern);
119
120 int num_of_lines = 0;
121 int max_text_width = width - 2 * padding;
122 int words_size = 5;
123 char **words = malloc(words_size * sizeof(char *));
124 if (!words)
125 die("malloc failed");
126
127 for (int i = 1; i < argc; i++)
128 {
129 char *body = argv[i];
130
131 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))
132 {
133 if (words_size <= num_of_lines)
134 {
135 words = realloc(words, (words_size += 5) * sizeof(char *));
136 if (!words)
137 die("malloc failed");
138 }
139 words[num_of_lines] = malloc((eol + 1) * sizeof(char));
140 if (!words[num_of_lines])
141 die("malloc failed");
142 strncpy(words[num_of_lines], body, eol);
143 words[num_of_lines][eol] = '\0';
144 }
145 }
146
147 unsigned int x = pos_x;
148 unsigned int y = pos_y;
149 unsigned int text_height = font->ascent - font->descent;
150 unsigned int height = (num_of_lines - 1) * line_spacing + num_of_lines * text_height + 2 * padding;
151
152 if (corner == TOP_RIGHT || corner == BOTTOM_RIGHT)
153 x = screen_width - width - border_size * 2 - pos_x;
154
155 if (corner == BOTTOM_LEFT || corner == BOTTOM_RIGHT)
156 y = screen_height - height - border_size * 2 - pos_y;
157
158 window = XCreateWindow(display, RootWindow(display, screen), x, y, width, height, border_size, DefaultDepth(display, screen), CopyFromParent, visual,
159 CWOverrideRedirect | CWBackPixel | CWBorderPixel, &attributes);
160
161 XftDraw *draw = XftDrawCreate(display, window, visual, colormap);
162 XftColorAllocName(display, visual, colormap, font_color, &color);
163
164 XSelectInput(display, window, ExposureMask | ButtonPress);
165
166 XMapWindow(display, window);
167
168 sem_t *mutex = sem_open("/herbe", O_CREAT, 0644, 1);
169 sem_wait(mutex);
170
171 signal(SIGUSR1, expire);
172 signal(SIGUSR2, action);
173
174 if (duration != 0)
175 alarm(duration);
176
177 while (1)
178 {
179 XEvent event;
180 XNextEvent(display, &event);
181
182 if (event.type == Expose)
183 {
184 XClearWindow(display, window);
185 for (int i = 0; i < num_of_lines; i++)
186 XftDrawStringUtf8(draw, &color, font, padding, line_spacing * i + text_height * (i + 1) + padding, (FcChar8 *)words[i], strlen(words[i]));
187 }
188 if (event.type == ButtonPress)
189 {
190 if (event.xbutton.button == DISMISS_BUTTON)
191 break;
192 else if (event.xbutton.button == ACTION_BUTTON)
193 {
194 exit_code = EXIT_ACTION;
195 break;
196 }
197 }
198 }
199
200 sem_post(mutex);
201 sem_close(mutex);
202
203 for (int i = 0; i < num_of_lines; i++)
204 free(words[i]);
205
206 free(words);
207 XftDrawDestroy(draw);
208 XftColorFree(display, visual, colormap, &color);
209 XftFontClose(display, font);
210 XCloseDisplay(display);
211
212 exit(exit_code);
213 }