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