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