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