1 #include "utils.h"
2
3 #include <X11/Xlib.h>
4 #include <X11/Xutil.h>
5 #include <cstdlib>
6 #include <cstring>
7 #include <string>
8 #include <vector>
9
10 #include "globals.h"
11 #include "settings.h"
12
13 #if defined(__MACH__) && ! defined(CLOCK_REALTIME)
14 #include <mach/clock.h>
15 #include <mach/mach.h>
16 #endif
17
18 using std::shared_ptr;
19 using std::string;
20 using std::vector;
21
get_monotonic_timestamp()22 time_t get_monotonic_timestamp() {
23 struct timespec ts{};
24 #if defined(__MACH__) && ! defined(CLOCK_REALTIME) // OS X does not have clock_gettime, use clock_get_time
25 clock_serv_t cclock;
26 mach_timespec_t mts;
27 host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
28 clock_get_time(cclock, &mts);
29 mach_port_deallocate(mach_task_self(), cclock);
30 ts.tv_sec = mts.tv_sec;
31 ts.tv_nsec = mts.tv_nsec;
32 #else
33 clock_gettime(CLOCK_REALTIME, &ts);
34 #endif
35 return ts.tv_sec;
36 }
37
MOD(int x,int n)38 int MOD(int x, int n) {
39 // for the tests of this, see tests of cycle_monitor in test_monitor.py
40 if (n > 0) {
41 while (x < 0) {
42 x += n;
43 }
44 }
45 return (((x % n) + n) % n);
46 }
47
window_class_to_string(Display * dpy,Window window)48 string window_class_to_string(Display* dpy, Window window) {
49 XClassHint hint;
50 if (0 == XGetClassHint(dpy, window, &hint)) {
51 return "";
52 }
53 string str = hint.res_class ? hint.res_class : "";
54 if (hint.res_name) {
55 XFree(hint.res_name);
56 }
57 if (hint.res_class) {
58 XFree(hint.res_class);
59 }
60 return str;
61 }
62
is_herbstluft_window(Display * dpy,Window window)63 bool is_herbstluft_window(Display* dpy, Window window) {
64 auto str = window_class_to_string(dpy, window);
65 return str == HERBST_FRAME_CLASS || str == HERBST_DECORATION_CLASS;
66 }
67
68 // duplicates an argument-vector
argv_duplicate(int argc,char ** argv)69 char** argv_duplicate(int argc, char** argv) {
70 if (argc <= 0) {
71 return nullptr;
72 }
73 char** new_argv = new char*[argc];
74 int i;
75 for (i = 0; i < argc; i++) {
76 new_argv[i] = strdup(argv[i]);
77 }
78 return new_argv;
79 }
80
81 // tells if the intervals [a_left, a_right) [b_left, b_right) intersect
intervals_intersect(int a_left,int a_right,int b_left,int b_right)82 bool intervals_intersect(int a_left, int a_right, int b_left, int b_right) {
83 return (b_left < a_right) && (a_left < b_right);
84 }
85
utf8_string_length(const string & str)86 size_t utf8_string_length(const string& str) {
87 // utf-strlen from stackoverflow:
88 // http://stackoverflow.com/questions/5117393/utf-8-strings-length-in-linux-c
89 size_t i = 0, j = 0;
90 while (str[i]) {
91 if ((str[i] & 0xc0) != 0x80) {
92 j++;
93 }
94 i++;
95 }
96 return j;
97 }
98
utf8_string_at(const string & str,size_t offset)99 string utf8_string_at(const string& str, size_t offset) {
100 // utf-strlen from stackoverflow:
101 // http://stackoverflow.com/questions/5117393/utf-8-strings-length-in-linux-c
102 //
103 // int i = 0, j = 0;
104 // while (s[i]) {
105 // if ((s[i] & 0xc0) != 0x80) j++;
106 // i++;
107 // }
108 // return j;
109 //for (char ch : str) {
110 // std::cout << "\'"<< ch << "\' -> " << ((ch&0xc0) == 0x80) << endl;
111 //}
112 size_t i = 0, byte_offset = 0;
113 string result;
114 // find the beginning of the n'th character
115 // find the n'th character ch, with (ch & 0xc0) == 0x80
116 while (i < offset) {
117 // we are at some byte with (ch & 0xc0) != 0x80
118 byte_offset++;
119 while ((str[byte_offset] & 0xc0) == 0x80) {
120 // if its a continuation byte, skip it
121 byte_offset++;
122 }
123 // we are at some byte with (ch & 0xc0) != 0x80 again
124 // and its the first byte of the (i+1)'th character
125 i++;
126 }
127 result += str[byte_offset]; // add its first char
128 // and add all continuation bytes
129 while ((str[++byte_offset] & 0xc0) == 0x80) {
130 result += str[byte_offset];
131 }
132 return result;
133 }
134
135 /**
136 * \brief emulates a double window border through the border pixmap mechanism
137 */
set_window_double_border(Display * dpy,Window win,int ibw,unsigned long inner_color,unsigned long outer_color)138 void set_window_double_border(Display *dpy, Window win, int ibw,
139 unsigned long inner_color,
140 unsigned long outer_color)
141 {
142 XWindowAttributes wa;
143
144 if (!XGetWindowAttributes(dpy, win, &wa)) {
145 return;
146 }
147
148 int bw = wa.border_width;
149
150 if (bw < 2 || ibw >= bw || ibw < 1) {
151 return;
152 }
153
154 int width = wa.width;
155 int height = wa.height;
156
157 auto depth = (unsigned)wa.depth;
158
159 int full_width = width + 2 * bw;
160 int full_height = height + 2 * bw;
161
162 // the inner border is represented through the following pattern:
163 //
164 // ██ ██
165 // ██ ██
166 // ██ ██
167 // ██ ██
168 // ██ ██
169 // ██ ██
170 // ██ ██
171 // ██ ██
172 // ██ ██
173 // ██ ██
174 // ██ ██
175 // ██████████████████████████ ██
176 //
177 // ██████████████████████████ ██
178
179 // use intermediates for casting (to avoid narrowing)
180 short fw_ibw = full_width - ibw, fh_ibw = full_height - ibw;
181 unsigned short uibw = ibw, h_ibw = height + ibw, w_ibw = width + ibw;
182 vector<XRectangle> rectangles{
183 { (short)width, 0, uibw, h_ibw },
184 { fw_ibw, 0, uibw, h_ibw },
185 { 0, (short)height, w_ibw, uibw },
186 { 0, fh_ibw, w_ibw, uibw },
187 { fw_ibw, fh_ibw, uibw, uibw }
188 };
189
190 Pixmap pix = XCreatePixmap(dpy, win, full_width, full_height, depth);
191 GC gc = XCreateGC(dpy, pix, 0, nullptr);
192
193 /* outer border */
194 XSetForeground(dpy, gc, outer_color);
195 XFillRectangle(dpy, pix, gc, 0, 0, full_width, full_height);
196
197 /* inner border */
198 XSetForeground(dpy, gc, inner_color);
199 XFillRectangles(dpy, pix, gc, &rectangles.front(), rectangles.size());
200
201 XSetWindowBorderPixmap(dpy, win, pix);
202 XFreeGC(dpy, gc);
203 XFreePixmap(dpy, pix);
204 }
205
subtree_print_to(shared_ptr<TreeInterface> intface,const string & indent,const string & rootprefix,Output output)206 static void subtree_print_to(shared_ptr<TreeInterface> intface, const string& indent,
207 const string& rootprefix, Output output) {
208 size_t child_count = intface->childCount();
209 string tree_style = g_settings->tree_style();
210 if (child_count == 0) {
211 output << rootprefix;
212 output << utf8_string_at(tree_style, 6);
213 output << utf8_string_at(tree_style, 5);
214 intface->appendCaption(output);
215 output << "\n";
216 } else {
217 output << rootprefix;
218 output << utf8_string_at(tree_style, 6);
219 output << utf8_string_at(tree_style, 7);
220 intface->appendCaption(output);
221 output << '\n';
222 // append children
223 string child_indent;
224 string child_prefix;
225 for (size_t i = 0; i < child_count; i++) {
226 bool last = (i == child_count - 1);
227 child_indent = indent + " ";
228 child_indent += utf8_string_at(tree_style, last ? 2 : 1);
229 child_prefix = indent + " ";
230 child_prefix += utf8_string_at(tree_style, last ? 4 : 3);
231 shared_ptr<TreeInterface> child = intface->nthChild(i);
232 subtree_print_to(child, child_indent,
233 child_prefix, output);
234 }
235 }
236 }
237
tree_print_to(shared_ptr<TreeInterface> intface,Output output)238 void tree_print_to(shared_ptr<TreeInterface> intface, Output output) {
239 string rootIndicator;
240 rootIndicator += utf8_string_at(g_settings->tree_style(), 0);
241 subtree_print_to(intface, " ", rootIndicator, output);
242 }
243
posix_sh_escape(const char * source)244 char* posix_sh_escape(const char* source) {
245 size_t count = 0;
246 int i;
247 for (i = 0; source[i]; i++) {
248 int j = LENGTH(ESCAPE_CHARACTERS) - 1; // = strlen(ESCAPE_CHARACTERS)
249 slow_assert(j == strlen(ESCAPE_CHARACTERS));
250 while (j--) {
251 slow_assert(0 <= j && j < strlen(ESCAPE_CHARACTERS));
252 if (source[i] == ESCAPE_CHARACTERS[j]) {
253 count++;
254 break;
255 }
256 }
257 }
258 auto source_len = (size_t)i;
259 // special chars:
260 if (source[0] == '~') {
261 count++;
262 }
263 // if there is nothing to escape
264 if (count == 0) {
265 return nullptr;
266 }
267 // TODO migrate to new
268 char* target = (char*)malloc(sizeof(char) * (count + source_len + 1));
269
270 // do the actual escaping
271 // special chars:
272 int s = 0; // position in the source
273 int t = 0; // position in the target
274 slow_assert(s < strlen(source));
275 slow_assert(t < (count + source_len));
276 if (source[0] == '~') {
277 target[t++] = '\\';
278 target[t++] = source[s++];
279 }
280 slow_assert(s < strlen(source));
281 slow_assert(t < (count + source_len));
282 while (source[s]) {
283 // check if we need to escape the next char
284 int j = LENGTH(ESCAPE_CHARACTERS) - 1; // = strlen(ESCAPE_CHARACTERS)
285 slow_assert(s < strlen(source));
286 slow_assert(t < (count + source_len));
287 while (j--) {
288 if (source[s] == ESCAPE_CHARACTERS[j]) {
289 // if source[s] needs to be escape, then put an backslash first
290 target[t++] = '\\';
291 break;
292 }
293 }
294 slow_assert(s < strlen(source));
295 slow_assert(t < (count + source_len));
296 // put the actual character
297 target[t++] = source[s++];
298 }
299 slow_assert(s == strlen(source));
300 slow_assert(t == (count + source_len));
301 // terminate the string
302 target[t] = '\0';
303 return target;
304 }
305
posix_sh_compress_inplace(char * str)306 void posix_sh_compress_inplace(char* str) {
307 int offset = 0;
308 for (int i = 0; true ; i++) {
309 if (str[i] == '\\' && str[i + 1] ) {
310 str[i + offset] = str[i + 1];
311 i++;
312 offset --;
313 } else {
314 str[i + offset] = str[i];
315 }
316 if (!str[i]) {
317 break;
318 }
319 }
320 }
321
322