1 /**************************************************************************
2 *
3 * Tint2 panel
4 *
5 * Copyright (C) 2007 Pål Staurland (staura@gmail.com)
6 * Modified (C) 2008 thierry lorthiois (lorthiois@bbsoft.fr)
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License version 2
10 * as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19 **************************************************************************/
20 
21 #include <X11/extensions/Xdamage.h>
22 #include <X11/extensions/Xrandr.h>
23 #include <X11/extensions/Xrender.h>
24 
25 #include <fcntl.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30 
31 #include "common.h"
32 #include "config.h"
33 #include "server.h"
34 #include "signals.h"
35 #include "window.h"
36 
37 Server server;
38 
server_catch_error(Display * d,XErrorEvent * ev)39 int server_catch_error(Display *d, XErrorEvent *ev)
40 {
41     return 0;
42 }
43 
server_init_atoms()44 void server_init_atoms()
45 {
46     server.atom._XROOTPMAP_ID = XInternAtom(server.display, "_XROOTPMAP_ID", False);
47     server.atom._XROOTMAP_ID = XInternAtom(server.display, "_XROOTMAP_ID", False);
48     server.atom._NET_CURRENT_DESKTOP = XInternAtom(server.display, "_NET_CURRENT_DESKTOP", False);
49     server.atom._NET_NUMBER_OF_DESKTOPS = XInternAtom(server.display, "_NET_NUMBER_OF_DESKTOPS", False);
50     server.atom._NET_DESKTOP_NAMES = XInternAtom(server.display, "_NET_DESKTOP_NAMES", False);
51     server.atom._NET_DESKTOP_GEOMETRY = XInternAtom(server.display, "_NET_DESKTOP_GEOMETRY", False);
52     server.atom._NET_DESKTOP_VIEWPORT = XInternAtom(server.display, "_NET_DESKTOP_VIEWPORT", False);
53     server.atom._NET_WORKAREA = XInternAtom(server.display, "_NET_WORKAREA", False);
54     server.atom._NET_ACTIVE_WINDOW = XInternAtom(server.display, "_NET_ACTIVE_WINDOW", False);
55     server.atom._NET_WM_WINDOW_TYPE = XInternAtom(server.display, "_NET_WM_WINDOW_TYPE", False);
56     server.atom._NET_WM_STATE_SKIP_PAGER = XInternAtom(server.display, "_NET_WM_STATE_SKIP_PAGER", False);
57     server.atom._NET_WM_STATE_SKIP_TASKBAR = XInternAtom(server.display, "_NET_WM_STATE_SKIP_TASKBAR", False);
58     server.atom._NET_WM_STATE_STICKY = XInternAtom(server.display, "_NET_WM_STATE_STICKY", False);
59     server.atom._NET_WM_STATE_DEMANDS_ATTENTION = XInternAtom(server.display, "_NET_WM_STATE_DEMANDS_ATTENTION", False);
60     server.atom._NET_WM_WINDOW_TYPE_DOCK = XInternAtom(server.display, "_NET_WM_WINDOW_TYPE_DOCK", False);
61     server.atom._NET_WM_WINDOW_TYPE_DESKTOP = XInternAtom(server.display, "_NET_WM_WINDOW_TYPE_DESKTOP", False);
62     server.atom._NET_WM_WINDOW_TYPE_TOOLBAR = XInternAtom(server.display, "_NET_WM_WINDOW_TYPE_TOOLBAR", False);
63     server.atom._NET_WM_WINDOW_TYPE_MENU = XInternAtom(server.display, "_NET_WM_WINDOW_TYPE_MENU", False);
64     server.atom._NET_WM_WINDOW_TYPE_SPLASH = XInternAtom(server.display, "_NET_WM_WINDOW_TYPE_SPLASH", False);
65     server.atom._NET_WM_WINDOW_TYPE_DIALOG = XInternAtom(server.display, "_NET_WM_WINDOW_TYPE_DIALOG", False);
66     server.atom._NET_WM_WINDOW_TYPE_NORMAL = XInternAtom(server.display, "_NET_WM_WINDOW_TYPE_NORMAL", False);
67     server.atom._NET_WM_DESKTOP = XInternAtom(server.display, "_NET_WM_DESKTOP", False);
68     server.atom.WM_STATE = XInternAtom(server.display, "WM_STATE", False);
69     server.atom._NET_WM_STATE = XInternAtom(server.display, "_NET_WM_STATE", False);
70     server.atom._NET_WM_STATE_MAXIMIZED_VERT = XInternAtom(server.display, "_NET_WM_STATE_MAXIMIZED_VERT", False);
71     server.atom._NET_WM_STATE_MAXIMIZED_HORZ = XInternAtom(server.display, "_NET_WM_STATE_MAXIMIZED_HORZ", False);
72     server.atom._NET_WM_STATE_SHADED = XInternAtom(server.display, "_NET_WM_STATE_SHADED", False);
73     server.atom._NET_WM_STATE_HIDDEN = XInternAtom(server.display, "_NET_WM_STATE_HIDDEN", False);
74     server.atom._NET_WM_STATE_BELOW = XInternAtom(server.display, "_NET_WM_STATE_BELOW", False);
75     server.atom._NET_WM_STATE_ABOVE = XInternAtom(server.display, "_NET_WM_STATE_ABOVE", False);
76     server.atom._NET_WM_STATE_MODAL = XInternAtom(server.display, "_NET_WM_STATE_MODAL", False);
77     server.atom._NET_CLIENT_LIST = XInternAtom(server.display, "_NET_CLIENT_LIST", False);
78     server.atom._NET_WM_VISIBLE_NAME = XInternAtom(server.display, "_NET_WM_VISIBLE_NAME", False);
79     server.atom._NET_WM_NAME = XInternAtom(server.display, "_NET_WM_NAME", False);
80     server.atom._NET_WM_STRUT = XInternAtom(server.display, "_NET_WM_STRUT", False);
81     server.atom._NET_WM_ICON = XInternAtom(server.display, "_NET_WM_ICON", False);
82     server.atom._NET_WM_ICON_GEOMETRY = XInternAtom(server.display, "_NET_WM_ICON_GEOMETRY", False);
83     server.atom._NET_WM_ICON_NAME = XInternAtom(server.display, "_NET_WM_ICON_NAME", False);
84     server.atom._NET_CLOSE_WINDOW = XInternAtom(server.display, "_NET_CLOSE_WINDOW", False);
85     server.atom.UTF8_STRING = XInternAtom(server.display, "UTF8_STRING", False);
86     server.atom._NET_SUPPORTING_WM_CHECK = XInternAtom(server.display, "_NET_SUPPORTING_WM_CHECK", False);
87     server.atom._NET_WM_CM_S0 = XInternAtom(server.display, "_NET_WM_CM_S0", False);
88     server.atom._NET_SUPPORTING_WM_CHECK = XInternAtom(server.display, "_NET_WM_NAME", False);
89     server.atom._NET_WM_STRUT_PARTIAL = XInternAtom(server.display, "_NET_WM_STRUT_PARTIAL", False);
90     server.atom.WM_NAME = XInternAtom(server.display, "WM_NAME", False);
91     server.atom.__SWM_VROOT = XInternAtom(server.display, "__SWM_VROOT", False);
92     server.atom._MOTIF_WM_HINTS = XInternAtom(server.display, "_MOTIF_WM_HINTS", False);
93     server.atom.WM_HINTS = XInternAtom(server.display, "WM_HINTS", False);
94     gchar *name = g_strdup_printf("_XSETTINGS_S%d", DefaultScreen(server.display));
95     server.atom._XSETTINGS_SCREEN = XInternAtom(server.display, name, False);
96     g_free(name);
97     server.atom._XSETTINGS_SETTINGS = XInternAtom(server.display, "_XSETTINGS_SETTINGS", False);
98 
99     // systray protocol
100     name = g_strdup_printf("_NET_SYSTEM_TRAY_S%d", DefaultScreen(server.display));
101     server.atom._NET_SYSTEM_TRAY_SCREEN = XInternAtom(server.display, name, False);
102     g_free(name);
103     server.atom._NET_SYSTEM_TRAY_OPCODE = XInternAtom(server.display, "_NET_SYSTEM_TRAY_OPCODE", False);
104     server.atom.MANAGER = XInternAtom(server.display, "MANAGER", False);
105     server.atom._NET_SYSTEM_TRAY_MESSAGE_DATA = XInternAtom(server.display, "_NET_SYSTEM_TRAY_MESSAGE_DATA", False);
106     server.atom._NET_SYSTEM_TRAY_ORIENTATION = XInternAtom(server.display, "_NET_SYSTEM_TRAY_ORIENTATION", False);
107     server.atom._NET_SYSTEM_TRAY_ICON_SIZE = XInternAtom(server.display, "_NET_SYSTEM_TRAY_ICON_SIZE", False);
108     server.atom._NET_SYSTEM_TRAY_PADDING = XInternAtom(server.display, "_NET_SYSTEM_TRAY_PADDING", False);
109     server.atom._XEMBED = XInternAtom(server.display, "_XEMBED", False);
110     server.atom._XEMBED_INFO = XInternAtom(server.display, "_XEMBED_INFO", False);
111     server.atom._NET_WM_PID = XInternAtom(server.display, "_NET_WM_PID", True);
112 
113     // drag 'n' drop
114     server.atom.XdndAware = XInternAtom(server.display, "XdndAware", False);
115     server.atom.XdndEnter = XInternAtom(server.display, "XdndEnter", False);
116     server.atom.XdndPosition = XInternAtom(server.display, "XdndPosition", False);
117     server.atom.XdndStatus = XInternAtom(server.display, "XdndStatus", False);
118     server.atom.XdndDrop = XInternAtom(server.display, "XdndDrop", False);
119     server.atom.XdndLeave = XInternAtom(server.display, "XdndLeave", False);
120     server.atom.XdndSelection = XInternAtom(server.display, "XdndSelection", False);
121     server.atom.XdndTypeList = XInternAtom(server.display, "XdndTypeList", False);
122     server.atom.XdndActionCopy = XInternAtom(server.display, "XdndActionCopy", False);
123     server.atom.XdndFinished = XInternAtom(server.display, "XdndFinished", False);
124     server.atom.TARGETS = XInternAtom(server.display, "TARGETS", False);
125 }
126 
GetAtomName(Display * disp,Atom a)127 const char *GetAtomName(Display *disp, Atom a)
128 {
129     if (a == None)
130         return "None";
131     else
132         return XGetAtomName(disp, a);
133 }
134 
cleanup_server()135 void cleanup_server()
136 {
137     if (server.colormap)
138         XFreeColormap(server.display, server.colormap);
139     server.colormap = 0;
140     if (server.colormap32)
141         XFreeColormap(server.display, server.colormap32);
142     server.colormap32 = 0;
143     if (server.monitors) {
144         for (int i = 0; i < server.num_monitors; ++i) {
145             g_strfreev(server.monitors[i].names);
146             server.monitors[i].names = NULL;
147         }
148         free(server.monitors);
149         server.monitors = NULL;
150     }
151     if (server.gc)
152         XFreeGC(server.display, server.gc);
153     server.gc = NULL;
154     server.disable_transparency = FALSE;
155 #ifdef HAVE_SN
156     if (server.pids)
157         g_tree_destroy(server.pids);
158     server.pids = NULL;
159 #endif
160 }
161 
send_event32(Window win,Atom at,long data1,long data2,long data3)162 void send_event32(Window win, Atom at, long data1, long data2, long data3)
163 {
164     XEvent event;
165 
166     event.xclient.type = ClientMessage;
167     event.xclient.serial = 0;
168     event.xclient.send_event = True;
169     event.xclient.display = server.display;
170     event.xclient.window = win;
171     event.xclient.message_type = at;
172 
173     event.xclient.format = 32;
174     event.xclient.data.l[0] = data1;
175     event.xclient.data.l[1] = data2;
176     event.xclient.data.l[2] = data3;
177     event.xclient.data.l[3] = 0;
178     event.xclient.data.l[4] = 0;
179 
180     XSendEvent(server.display, server.root_win, False, SubstructureRedirectMask | SubstructureNotifyMask, &event);
181 }
182 
get_property32(Window win,Atom at,Atom type)183 int get_property32(Window win, Atom at, Atom type)
184 {
185     Atom type_ret;
186     int format_ret = 0, data = 0;
187     unsigned long nitems_ret = 0;
188     unsigned long bafter_ret = 0;
189     unsigned char *prop_value = 0;
190     int result;
191 
192     if (!win)
193         return 0;
194 
195     result = XGetWindowProperty(server.display,
196                                 win,
197                                 at,
198                                 0,
199                                 0x7fffffff,
200                                 False,
201                                 type,
202                                 &type_ret,
203                                 &format_ret,
204                                 &nitems_ret,
205                                 &bafter_ret,
206                                 &prop_value);
207 
208     if (result == Success && prop_value) {
209         data = ((gulong *)prop_value)[0];
210         XFree(prop_value);
211     }
212     return data;
213 }
214 
server_get_property(Window win,Atom at,Atom type,int * num_results)215 void *server_get_property(Window win, Atom at, Atom type, int *num_results)
216 {
217     Atom type_ret;
218     int format_ret = 0;
219     unsigned long nitems_ret = 0;
220     unsigned long bafter_ret = 0;
221     unsigned char *prop_value;
222 
223     if (!win)
224         return NULL;
225 
226     int result = XGetWindowProperty(server.display,
227                                     win,
228                                     at,
229                                     0,
230                                     0x7fffffff,
231                                     False,
232                                     type,
233                                     &type_ret,
234                                     &format_ret,
235                                     &nitems_ret,
236                                     &bafter_ret,
237                                     &prop_value);
238 
239     // Send fill_color resultcount
240     if (num_results)
241         *num_results = (int)nitems_ret;
242 
243     if (result == Success && prop_value)
244         return prop_value;
245     else
246         return NULL;
247 }
248 
get_root_pixmap()249 void get_root_pixmap()
250 {
251     Pixmap ret = None;
252 
253     Atom pixmap_atoms[] = {server.atom._XROOTPMAP_ID, server.atom._XROOTMAP_ID};
254     for (int i = 0; i < sizeof(pixmap_atoms) / sizeof(Atom); ++i) {
255         unsigned long *res = (unsigned long *)server_get_property(server.root_win, pixmap_atoms[i], XA_PIXMAP, NULL);
256         if (res) {
257             ret = *((Pixmap *)res);
258             XFree(res);
259             break;
260         }
261     }
262     server.root_pmap = ret;
263 
264     if (server.root_pmap == None) {
265         fprintf(stderr, "tint2: pixmap background detection failed\n");
266     } else {
267         XGCValues gcv;
268         gcv.ts_x_origin = 0;
269         gcv.ts_y_origin = 0;
270         gcv.fill_style = FillTiled;
271         unsigned mask = GCTileStipXOrigin | GCTileStipYOrigin | GCFillStyle | GCTile;
272 
273         gcv.tile = server.root_pmap;
274         XChangeGC(server.display, server.gc, mask, &gcv);
275     }
276 }
277 
compare_monitor_pos(const void * monitor1,const void * monitor2)278 int compare_monitor_pos(const void *monitor1, const void *monitor2)
279 {
280     const Monitor *m1 = (const Monitor *)monitor1;
281     const Monitor *m2 = (const Monitor *)monitor2;
282 
283     if (m1->x < m2->x) {
284         return -1;
285     } else if (m1->x > m2->x) {
286         return 1;
287     } else if (m1->y < m2->y) {
288         return -1;
289     } else if (m1->y > m2->y) {
290         return 1;
291     } else {
292         return 0;
293     }
294 }
295 
monitor_includes_monitor(const void * monitor1,const void * monitor2)296 int monitor_includes_monitor(const void *monitor1, const void *monitor2)
297 {
298     const Monitor *m1 = (const Monitor *)monitor1;
299     const Monitor *m2 = (const Monitor *)monitor2;
300 
301     if (m1->x >= m2->x && m1->y >= m2->y && (m1->x + m1->width) <= (m2->x + m2->width) &&
302         (m1->y + m1->height) <= (m2->y + m2->height)) {
303         // m1 included inside m2
304         return 1;
305     } else {
306         return -1;
307     }
308 }
309 
sort_monitors()310 void sort_monitors()
311 {
312     qsort(server.monitors, server.num_monitors, sizeof(Monitor), compare_monitor_pos);
313 }
314 
compute_dpi(XRRCrtcInfo * crtc,XRROutputInfo * output)315 int compute_dpi(XRRCrtcInfo *crtc, XRROutputInfo *output)
316 {
317     double width = output->mm_width;
318     double height = output->mm_height;
319     double x_res = crtc->width;
320     double y_res = crtc->height;
321 
322     if (width > 0 && height > 0) {
323         int dpi_x = x_res / width * 25.4;
324         int dpi_y = y_res / height * 25.4;
325         return MAX(dpi_x, dpi_y);
326     }
327     return 0;
328 }
329 
get_monitors()330 void get_monitors()
331 {
332     if (XineramaIsActive(server.display)) {
333         int num_monitors;
334         XineramaScreenInfo *info = XineramaQueryScreens(server.display, &num_monitors);
335         XRRScreenResources *res = XRRGetScreenResourcesCurrent(server.display, server.root_win);
336         RROutput primary_output = XRRGetOutputPrimary(server.display, server.root_win);
337 
338         if (res && res->ncrtc >= num_monitors) {
339             // use xrandr to identify monitors (does not work with proprietery nvidia drivers)
340             fprintf(stderr, "tint2: xRandr: Found crtc's: %d\n", res->ncrtc);
341             server.monitors = calloc(res->ncrtc, sizeof(Monitor));
342             num_monitors = 0;
343             for (int i = 0; i < res->ncrtc; ++i) {
344                 XRRCrtcInfo *crtc_info = XRRGetCrtcInfo(server.display, res, res->crtcs[i]);
345                 // Ignore empty crtc
346                 if (!crtc_info->width || !crtc_info->height) {
347                     fprintf(stderr, "tint2: xRandr: crtc %d seems disabled\n", i);
348                     XRRFreeCrtcInfo(crtc_info);
349                     continue;
350                 }
351                 int i_monitor = num_monitors;
352                 num_monitors++;
353                 server.monitors[i_monitor].x = crtc_info->x;
354                 server.monitors[i_monitor].y = crtc_info->y;
355                 server.monitors[i_monitor].width = crtc_info->width;
356                 server.monitors[i_monitor].height = crtc_info->height;
357                 server.monitors[i_monitor].names = calloc((crtc_info->noutput + 1), sizeof(gchar *));
358                 server.monitors[i_monitor].dpi = 96;
359                 for (int j = 0; j < crtc_info->noutput; ++j) {
360                     XRROutputInfo *output_info = XRRGetOutputInfo(server.display, res, crtc_info->outputs[j]);
361                     server.monitors[i_monitor].names[j] = g_strdup(output_info->name);
362                     server.monitors[i_monitor].primary = crtc_info->outputs[j] == primary_output;
363                     int dpi = compute_dpi(crtc_info, output_info);
364                     if (dpi)
365                         server.monitors[i_monitor].dpi = dpi;
366                     fprintf(stderr,
367                             BLUE "tint2: xRandr: Linking output %s with crtc %d, resolution %dx%d, DPI %d" RESET "\n",
368                             output_info->name,
369                             i,
370                             server.monitors[i_monitor].width,
371                             server.monitors[i_monitor].height,
372                             server.monitors[i_monitor].dpi);
373                     XRRFreeOutputInfo(output_info);
374                 }
375                 server.monitors[i_monitor].names[crtc_info->noutput] = NULL;
376                 XRRFreeCrtcInfo(crtc_info);
377             }
378         } else if (info && num_monitors > 0) {
379             server.monitors = calloc(num_monitors, sizeof(Monitor));
380             for (int i = 0; i < num_monitors; i++) {
381                 server.monitors[i].x = info[i].x_org;
382                 server.monitors[i].y = info[i].y_org;
383                 server.monitors[i].width = info[i].width;
384                 server.monitors[i].height = info[i].height;
385                 server.monitors[i].names = NULL;
386                 server.monitors[i].dpi = 96;
387             }
388         }
389 
390         // Sort monitors by inclusion
391         qsort(server.monitors, num_monitors, sizeof(Monitor), monitor_includes_monitor);
392 
393         // Remove monitors included in other ones
394         int i = 0;
395         while (i < num_monitors) {
396             for (int j = 0; j < i; j++) {
397                 if (monitor_includes_monitor(&server.monitors[i], &server.monitors[j]) > 0) {
398                     goto next;
399                 }
400             }
401             i++;
402         }
403     next:
404         for (int j = i; j < num_monitors; ++j)
405             if (server.monitors[j].names)
406                 g_strfreev(server.monitors[j].names);
407         server.num_monitors = i;
408         server.monitors = realloc(server.monitors, server.num_monitors * sizeof(Monitor));
409         qsort(server.monitors, server.num_monitors, sizeof(Monitor), compare_monitor_pos);
410 
411         if (res)
412             XRRFreeScreenResources(res);
413         XFree(info);
414     }
415 
416     if (!server.num_monitors) {
417         server.num_monitors = 1;
418         server.monitors = calloc(1, sizeof(Monitor));
419         server.monitors[0].x = server.monitors[0].y = 0;
420         server.monitors[0].width = DisplayWidth(server.display, server.screen);
421         server.monitors[0].height = DisplayHeight(server.display, server.screen);
422         server.monitors[0].names = 0;
423         server.monitors[0].dpi = 96;
424     }
425 }
426 
print_monitors()427 void print_monitors()
428 {
429     fprintf(stderr, "tint2: Number of monitors: %d\n", server.num_monitors);
430     for (int i = 0; i < server.num_monitors; i++) {
431         fprintf(stderr,
432                 "Monitor %d: x = %d, y = %d, w = %d, h = %d\n",
433                 i + 1,
434                 server.monitors[i].x,
435                 server.monitors[i].y,
436                 server.monitors[i].width,
437                 server.monitors[i].height);
438     }
439 }
440 
server_get_number_of_desktops()441 void server_get_number_of_desktops()
442 {
443     if (server.viewports) {
444         free(server.viewports);
445         server.viewports = NULL;
446     }
447 
448     server.num_desktops = get_property32(server.root_win, server.atom._NET_NUMBER_OF_DESKTOPS, XA_CARDINAL);
449     if (server.num_desktops > 1)
450         return;
451 
452     int num_results;
453     long *work_area_size = server_get_property(server.root_win, server.atom._NET_WORKAREA, XA_CARDINAL, &num_results);
454     if (!work_area_size)
455         return;
456     int work_area_width = work_area_size[0] + work_area_size[2];
457     int work_area_height = work_area_size[1] + work_area_size[3];
458     XFree(work_area_size);
459 
460     long *x_screen_size =
461         server_get_property(server.root_win, server.atom._NET_DESKTOP_GEOMETRY, XA_CARDINAL, &num_results);
462     if (!x_screen_size)
463         return;
464     int x_screen_width = x_screen_size[0];
465     int x_screen_height = x_screen_size[1];
466     XFree(x_screen_size);
467 
468     int num_viewports = MAX(x_screen_width / work_area_width, 1) * MAX(x_screen_height / work_area_height, 1);
469     if (num_viewports <= 1) {
470         server.num_desktops = 1;
471         return;
472     }
473 
474     server.viewports = calloc(num_viewports, sizeof(Viewport));
475     int k = 0;
476     for (int i = 0; i < MAX(x_screen_height / work_area_height, 1); i++) {
477         for (int j = 0; j < MAX(x_screen_width / work_area_width, 1); j++) {
478             server.viewports[k].x = j * work_area_width;
479             server.viewports[k].y = i * work_area_height;
480             server.viewports[k].width = work_area_width;
481             server.viewports[k].height = work_area_height;
482             k++;
483         }
484     }
485 
486     server.num_desktops = num_viewports;
487 }
488 
get_desktop_names()489 GSList *get_desktop_names()
490 {
491     if (server.viewports) {
492         GSList *list = NULL;
493         for (int j = 0; j < server.num_desktops; j++) {
494             list = g_slist_append(list, g_strdup_printf("%d", j + 1));
495         }
496         return list;
497     }
498 
499     int count;
500     GSList *list = NULL;
501     gchar *data_ptr =
502         server_get_property(server.root_win, server.atom._NET_DESKTOP_NAMES, server.atom.UTF8_STRING, &count);
503     if (data_ptr) {
504         list = g_slist_append(list, g_strdup(data_ptr));
505         for (int j = 0; j < count - 1; j++) {
506             if (*(data_ptr + j) == '\0') {
507                 gchar *ptr = (gchar *)data_ptr + j + 1;
508                 list = g_slist_append(list, g_strdup(ptr));
509             }
510         }
511         XFree(data_ptr);
512     }
513     return list;
514 }
515 
get_current_desktop()516 int get_current_desktop()
517 {
518     if (!server.viewports) {
519         return MAX(0,
520                    MIN(server.num_desktops - 1,
521                        get_property32(server.root_win, server.atom._NET_CURRENT_DESKTOP, XA_CARDINAL)));
522     }
523 
524     int num_results;
525     long *work_area_size = server_get_property(server.root_win, server.atom._NET_WORKAREA, XA_CARDINAL, &num_results);
526     if (!work_area_size)
527         return 0;
528     int work_area_width = work_area_size[0] + work_area_size[2];
529     int work_area_height = work_area_size[1] + work_area_size[3];
530     XFree(work_area_size);
531 
532     if (work_area_width <= 0 || work_area_height <= 0)
533         return 0;
534 
535     long *viewport = server_get_property(server.root_win, server.atom._NET_DESKTOP_VIEWPORT, XA_CARDINAL, &num_results);
536     if (!viewport)
537         return 0;
538     int viewport_x = viewport[0];
539     int viewport_y = viewport[1];
540     XFree(viewport);
541 
542     long *x_screen_size =
543         server_get_property(server.root_win, server.atom._NET_DESKTOP_GEOMETRY, XA_CARDINAL, &num_results);
544     if (!x_screen_size)
545         return 0;
546     int x_screen_width = x_screen_size[0];
547     XFree(x_screen_size);
548 
549     int ncols = x_screen_width / work_area_width;
550 
551     //	fprintf(stderr, "tint2: \n");
552     //	fprintf(stderr, "tint2: Work area size: %d x %d\n", work_area_width, work_area_height);
553     //	fprintf(stderr, "tint2: Viewport pos: %d x %d\n", viewport_x, viewport_y);
554     //	fprintf(stderr, "tint2: Viewport i: %d\n", (viewport_y / work_area_height) * ncols + viewport_x /
555     //work_area_width);
556 
557     int result = (viewport_y / work_area_height) * ncols + viewport_x / work_area_width;
558     return MAX(0, MIN(server.num_desktops - 1, result));
559 }
560 
change_desktop(int desktop)561 void change_desktop(int desktop)
562 {
563     if (!server.viewports) {
564         send_event32(server.root_win, server.atom._NET_CURRENT_DESKTOP, desktop, 0, 0);
565     } else {
566         send_event32(server.root_win,
567                      server.atom._NET_DESKTOP_VIEWPORT,
568                      server.viewports[desktop].x,
569                      server.viewports[desktop].y,
570                      0);
571     }
572 }
573 
get_desktops()574 void get_desktops()
575 {
576     // detect number of desktops
577     // wait 15s to leave some time for window manager startup
578     for (int i = 0; i < 15; i++) {
579         server_get_number_of_desktops();
580         if (server.num_desktops > 0)
581             break;
582         sleep(1);
583     }
584     if (server.num_desktops == 0) {
585         server.num_desktops = 1;
586         fprintf(stderr, "tint2: warning : WM doesn't respect NETWM specs. tint2 default to 1 desktop.\n");
587     }
588 }
589 
server_init_visual()590 void server_init_visual()
591 {
592     // inspired by freedesktops fdclock ;)
593     XVisualInfo templ = {.screen = server.screen, .depth = 32, .class = TrueColor};
594     int nvi;
595     XVisualInfo *xvi =
596         XGetVisualInfo(server.display, VisualScreenMask | VisualDepthMask | VisualClassMask, &templ, &nvi);
597 
598     Visual *visual = NULL;
599     if (xvi) {
600         XRenderPictFormat *format;
601         for (int i = 0; i < nvi; i++) {
602             format = XRenderFindVisualFormat(server.display, xvi[i].visual);
603             if (format->type == PictTypeDirect && format->direct.alphaMask) {
604                 visual = xvi[i].visual;
605                 break;
606             }
607         }
608     }
609     XFree(xvi);
610 
611     // check composite manager
612     server.composite_manager = XGetSelectionOwner(server.display, server.atom._NET_WM_CM_S0);
613     if (server.colormap)
614         XFreeColormap(server.display, server.colormap);
615     if (server.colormap32)
616         XFreeColormap(server.display, server.colormap32);
617 
618     if (visual) {
619         server.visual32 = visual;
620         server.colormap32 = XCreateColormap(server.display, server.root_win, visual, AllocNone);
621     }
622 
623     if (!server.disable_transparency && visual && server.composite_manager != None && !snapshot_path) {
624         XSetWindowAttributes attrs;
625         attrs.event_mask = StructureNotifyMask;
626         XChangeWindowAttributes(server.display, server.composite_manager, CWEventMask, &attrs);
627 
628         server.real_transparency = TRUE;
629         server.depth = 32;
630         fprintf(stderr, "tint2: real transparency on... depth: %d\n", server.depth);
631         server.colormap = XCreateColormap(server.display, server.root_win, visual, AllocNone);
632         server.visual = visual;
633     } else {
634         // no composite manager or snapshot mode => fake transparency
635         server.real_transparency = FALSE;
636         server.depth = DefaultDepth(server.display, server.screen);
637         fprintf(stderr, "tint2: real transparency off.... depth: %d\n", server.depth);
638         server.colormap = DefaultColormap(server.display, server.screen);
639         server.visual = DefaultVisual(server.display, server.screen);
640     }
641 }
642 
server_init_xdamage()643 void server_init_xdamage()
644 {
645     XDamageQueryExtension(server.display, &server.xdamage_event_type, &server.xdamage_event_error_type);
646     server.xdamage_event_type += XDamageNotify;
647     server.xdamage_event_error_type += XDamageNotify;
648 }
649 
650 // Forward mouse click to the desktop window
forward_click(XEvent * e)651 void forward_click(XEvent *e)
652 {
653     // forward the click to the desktop window (thanks conky)
654     XUngrabPointer(server.display, e->xbutton.time);
655     e->xbutton.window = server.root_win;
656     // icewm doesn't open under the mouse.
657     // and xfce doesn't open at all.
658     e->xbutton.x = e->xbutton.x_root;
659     e->xbutton.y = e->xbutton.y_root;
660     // fprintf(stderr, "tint2: **** %d, %d\n", e->xbutton.x, e->xbutton.y);
661     // XSetInputFocus(server.display, e->xbutton.window, RevertToParent, e->xbutton.time);
662     XSendEvent(server.display, e->xbutton.window, False, ButtonPressMask, e);
663 }
664 
handle_crash(const char * reason)665 void handle_crash(const char *reason)
666 {
667 #ifndef DISABLE_BACKTRACE
668     char path[4096];
669     sprintf(path, "%s/.tint2-crash.log", get_home_dir());
670     int log_fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
671     log_string(log_fd, RED "tint2: crashed, reason: ");
672     log_string(log_fd, reason);
673     log_string(log_fd, RESET "\n");
674     dump_backtrace(log_fd);
675     log_string(log_fd, RED "Please create a bug report with this log output." RESET "\n");
676     close(log_fd);
677 #endif
678 }
679 
x11_io_error(Display * display)680 int x11_io_error(Display *display)
681 {
682     handle_crash("X11 I/O error");
683     return 0;
684 }
685 
686 #ifdef HAVE_SN
687 static int error_trap_depth = 0;
688 
error_trap_push(SnDisplay * display,Display * xdisplay)689 void error_trap_push(SnDisplay *display, Display *xdisplay)
690 {
691     ++error_trap_depth;
692 }
693 
error_trap_pop(SnDisplay * display,Display * xdisplay)694 void error_trap_pop(SnDisplay *display, Display *xdisplay)
695 {
696     if (error_trap_depth == 0) {
697         fprintf(stderr, "tint2: Error trap underflow!\n");
698         return;
699     }
700 
701     XSync(xdisplay, False); /* get all errors out of the queue */
702     --error_trap_depth;
703 }
704 #endif // HAVE_SN
705