1 /**************************************************************************
5 * Copyright (C) 2007 Pål Staurland (staura@gmail.com)
6 * Modified (C) 2008 thierry lorthiois (lorthiois@bbsoft.fr)
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.
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 **************************************************************************/
21 #include <X11/extensions/Xrender.h>
22 #include <X11/extensions/Xrandr.h>
34 void server_catch_error (Display
*d
, XErrorEvent
*ev
){}
36 void server_init_atoms ()
38 server
.atom
._XROOTPMAP_ID
= XInternAtom (server
.dsp
, "_XROOTPMAP_ID", False
);
39 server
.atom
._XROOTMAP_ID
= XInternAtom (server
.dsp
, "_XROOTMAP_ID", False
);
40 server
.atom
._NET_CURRENT_DESKTOP
= XInternAtom (server
.dsp
, "_NET_CURRENT_DESKTOP", False
);
41 server
.atom
._NET_NUMBER_OF_DESKTOPS
= XInternAtom (server
.dsp
, "_NET_NUMBER_OF_DESKTOPS", False
);
42 server
.atom
._NET_DESKTOP_NAMES
= XInternAtom (server
.dsp
, "_NET_DESKTOP_NAMES", False
);
43 server
.atom
._NET_DESKTOP_GEOMETRY
= XInternAtom (server
.dsp
, "_NET_DESKTOP_GEOMETRY", False
);
44 server
.atom
._NET_DESKTOP_VIEWPORT
= XInternAtom (server
.dsp
, "_NET_DESKTOP_VIEWPORT", False
);
45 server
.atom
._NET_ACTIVE_WINDOW
= XInternAtom (server
.dsp
, "_NET_ACTIVE_WINDOW", False
);
46 server
.atom
._NET_WM_WINDOW_TYPE
= XInternAtom (server
.dsp
, "_NET_WM_WINDOW_TYPE", False
);
47 server
.atom
._NET_WM_STATE_SKIP_PAGER
= XInternAtom (server
.dsp
, "_NET_WM_STATE_SKIP_PAGER", False
);
48 server
.atom
._NET_WM_STATE_SKIP_TASKBAR
= XInternAtom (server
.dsp
, "_NET_WM_STATE_SKIP_TASKBAR", False
);
49 server
.atom
._NET_WM_STATE_STICKY
= XInternAtom (server
.dsp
, "_NET_WM_STATE_STICKY", False
);
50 server
.atom
._NET_WM_STATE_DEMANDS_ATTENTION
= XInternAtom (server
.dsp
, "_NET_WM_STATE_DEMANDS_ATTENTION", False
);
51 server
.atom
._NET_WM_WINDOW_TYPE_DOCK
= XInternAtom (server
.dsp
, "_NET_WM_WINDOW_TYPE_DOCK", False
);
52 server
.atom
._NET_WM_WINDOW_TYPE_DESKTOP
= XInternAtom (server
.dsp
, "_NET_WM_WINDOW_TYPE_DESKTOP", False
);
53 server
.atom
._NET_WM_WINDOW_TYPE_TOOLBAR
= XInternAtom (server
.dsp
, "_NET_WM_WINDOW_TYPE_TOOLBAR", False
);
54 server
.atom
._NET_WM_WINDOW_TYPE_MENU
= XInternAtom (server
.dsp
, "_NET_WM_WINDOW_TYPE_MENU", False
);
55 server
.atom
._NET_WM_WINDOW_TYPE_SPLASH
= XInternAtom (server
.dsp
, "_NET_WM_WINDOW_TYPE_SPLASH", False
);
56 server
.atom
._NET_WM_WINDOW_TYPE_DIALOG
= XInternAtom (server
.dsp
, "_NET_WM_WINDOW_TYPE_DIALOG", False
);
57 server
.atom
._NET_WM_WINDOW_TYPE_NORMAL
= XInternAtom (server
.dsp
, "_NET_WM_WINDOW_TYPE_NORMAL", False
);
58 server
.atom
._NET_WM_DESKTOP
= XInternAtom (server
.dsp
, "_NET_WM_DESKTOP", False
);
59 server
.atom
.WM_STATE
= XInternAtom (server
.dsp
, "WM_STATE", False
);
60 server
.atom
._NET_WM_STATE
= XInternAtom (server
.dsp
, "_NET_WM_STATE", False
);
61 server
.atom
._NET_WM_STATE_MAXIMIZED_VERT
= XInternAtom (server
.dsp
, "_NET_WM_STATE_MAXIMIZED_VERT", False
);
62 server
.atom
._NET_WM_STATE_MAXIMIZED_HORZ
= XInternAtom (server
.dsp
, "_NET_WM_STATE_MAXIMIZED_HORZ", False
);
63 server
.atom
._NET_WM_STATE_SHADED
= XInternAtom (server
.dsp
, "_NET_WM_STATE_SHADED", False
);
64 server
.atom
._NET_WM_STATE_HIDDEN
= XInternAtom (server
.dsp
, "_NET_WM_STATE_HIDDEN", False
);
65 server
.atom
._NET_WM_STATE_BELOW
= XInternAtom (server
.dsp
, "_NET_WM_STATE_BELOW", False
);
66 server
.atom
._NET_WM_STATE_ABOVE
= XInternAtom (server
.dsp
, "_NET_WM_STATE_ABOVE", False
);
67 server
.atom
._NET_WM_STATE_MODAL
= XInternAtom (server
.dsp
, "_NET_WM_STATE_MODAL", False
);
68 server
.atom
._NET_CLIENT_LIST
= XInternAtom (server
.dsp
, "_NET_CLIENT_LIST", False
);
69 server
.atom
._NET_WM_VISIBLE_NAME
= XInternAtom (server
.dsp
, "_NET_WM_VISIBLE_NAME", False
);
70 server
.atom
._NET_WM_NAME
= XInternAtom (server
.dsp
, "_NET_WM_NAME", False
);
71 server
.atom
._NET_WM_STRUT
= XInternAtom (server
.dsp
, "_NET_WM_STRUT", False
);
72 server
.atom
._NET_WM_ICON
= XInternAtom (server
.dsp
, "_NET_WM_ICON", False
);
73 server
.atom
._NET_WM_ICON_GEOMETRY
= XInternAtom(server
.dsp
, "_NET_WM_ICON_GEOMETRY", False
);
74 server
.atom
._NET_CLOSE_WINDOW
= XInternAtom (server
.dsp
, "_NET_CLOSE_WINDOW", False
);
75 server
.atom
.UTF8_STRING
= XInternAtom (server
.dsp
, "UTF8_STRING", False
);
76 server
.atom
._NET_SUPPORTING_WM_CHECK
= XInternAtom (server
.dsp
, "_NET_SUPPORTING_WM_CHECK", False
);
77 server
.atom
._NET_WM_CM_S0
= XInternAtom (server
.dsp
, "_NET_WM_CM_S0", False
);
78 server
.atom
._NET_SUPPORTING_WM_CHECK
= XInternAtom (server
.dsp
, "_NET_WM_NAME", False
);
79 server
.atom
._NET_WM_STRUT_PARTIAL
= XInternAtom (server
.dsp
, "_NET_WM_STRUT_PARTIAL", False
);
80 server
.atom
.WM_NAME
= XInternAtom(server
.dsp
, "WM_NAME", False
);
81 server
.atom
.__SWM_VROOT
= XInternAtom(server
.dsp
, "__SWM_VROOT", False
);
82 server
.atom
._MOTIF_WM_HINTS
= XInternAtom(server
.dsp
, "_MOTIF_WM_HINTS", False
);
83 server
.atom
.WM_HINTS
= XInternAtom(server
.dsp
, "WM_HINTS", False
);
84 char *name
= g_strdup_printf("_XSETTINGS_S%d", DefaultScreen(server
.dsp
));
85 server
.atom
._XSETTINGS_SCREEN
= XInternAtom(server
.dsp
, name
, False
);
87 server
.atom
._XSETTINGS_SETTINGS
= XInternAtom(server
.dsp
, "_XSETTINGS_SETTINGS", False
);
90 name
= g_strdup_printf("_NET_SYSTEM_TRAY_S%d", DefaultScreen(server
.dsp
));
91 server
.atom
._NET_SYSTEM_TRAY_SCREEN
= XInternAtom(server
.dsp
, name
, False
);
93 server
.atom
._NET_SYSTEM_TRAY_OPCODE
= XInternAtom(server
.dsp
, "_NET_SYSTEM_TRAY_OPCODE", False
);
94 server
.atom
.MANAGER
= XInternAtom(server
.dsp
, "MANAGER", False
);
95 server
.atom
._NET_SYSTEM_TRAY_MESSAGE_DATA
= XInternAtom(server
.dsp
, "_NET_SYSTEM_TRAY_MESSAGE_DATA", False
);
96 server
.atom
._NET_SYSTEM_TRAY_ORIENTATION
= XInternAtom(server
.dsp
, "_NET_SYSTEM_TRAY_ORIENTATION", False
);
97 server
.atom
._XEMBED
= XInternAtom(server
.dsp
, "_XEMBED", False
);
98 server
.atom
._XEMBED_INFO
= XInternAtom(server
.dsp
, "_XEMBED_INFO", False
);
101 server
.atom
.XdndAware
= XInternAtom(server
.dsp
, "XdndAware", False
);
102 server
.atom
.XdndPosition
= XInternAtom(server
.dsp
, "XdndPosition", False
);
103 server
.atom
.XdndStatus
= XInternAtom(server
.dsp
, "XdndStatus", False
);
104 server
.atom
.XdndLeave
= XInternAtom(server
.dsp
, "XdndLeave", False
);
108 void cleanup_server()
110 if (server
.colormap
) XFreeColormap(server
.dsp
, server
.colormap
);
111 if (server
.colormap32
) XFreeColormap(server
.dsp
, server
.colormap32
);
112 if (server
.monitor
) {
114 for (i
=0; i
<server
.nb_monitor
; ++i
)
115 if (server
.monitor
[i
].names
)
116 g_strfreev(server
.monitor
[i
].names
);
117 free(server
.monitor
);
119 if (server
.gc
) XFreeGC(server
.dsp
, server
.gc
);
123 void send_event32 (Window win
, Atom at
, long data1
, long data2
, long data3
)
127 event
.xclient
.type
= ClientMessage
;
128 event
.xclient
.serial
= 0;
129 event
.xclient
.send_event
= True
;
130 event
.xclient
.display
= server
.dsp
;
131 event
.xclient
.window
= win
;
132 event
.xclient
.message_type
= at
;
134 event
.xclient
.format
= 32;
135 event
.xclient
.data
.l
[0] = data1
;
136 event
.xclient
.data
.l
[1] = data2
;
137 event
.xclient
.data
.l
[2] = data3
;
138 event
.xclient
.data
.l
[3] = 0;
139 event
.xclient
.data
.l
[4] = 0;
141 XSendEvent(server
.dsp
, server
.root_win
, False
, SubstructureRedirectMask
|SubstructureNotifyMask
, &event
);
145 int get_property32 (Window win
, Atom at
, Atom type
)
148 int format_ret
= 0, data
= 0;
149 unsigned long nitems_ret
= 0;
150 unsigned long bafter_ret
= 0;
151 unsigned char *prop_value
= 0;
156 result
= XGetWindowProperty(server
.dsp
, win
, at
, 0, 0x7fffffff, False
, type
, &type_ret
, &format_ret
, &nitems_ret
, &bafter_ret
, &prop_value
);
158 if (result
== Success
&& prop_value
) {
159 data
= ((gulong
*)prop_value
)[0];
166 void *server_get_property (Window win
, Atom at
, Atom type
, int *num_results
)
170 unsigned long nitems_ret
= 0;
171 unsigned long bafter_ret
= 0;
172 unsigned char *prop_value
;
177 result
= XGetWindowProperty(server
.dsp
, win
, at
, 0, 0x7fffffff, False
, type
, &type_ret
, &format_ret
, &nitems_ret
, &bafter_ret
, &prop_value
);
179 // Send back resultcount
180 if (num_results
) *num_results
= (int)nitems_ret
;
182 if (result
== Success
&& prop_value
) return prop_value
;
187 void get_root_pixmap()
192 Atom pixmap_atoms
[] = { server
.atom
._XROOTPMAP_ID
, server
.atom
._XROOTMAP_ID
};
195 for (i
=0; i
<sizeof(pixmap_atoms
)/sizeof(Atom
); ++i
) {
196 res
= server_get_property (server
.root_win
, pixmap_atoms
[i
], XA_PIXMAP
, 0);
198 ret
= *((Pixmap
*)res
);
203 server
.root_pmap
= ret
;
205 if (server
.root_pmap
== None
)
206 fprintf(stderr
, "tint2 : pixmap background detection failed\n");
211 gcv
.fill_style
= FillTiled
;
212 uint mask
= GCTileStipXOrigin
| GCTileStipYOrigin
| GCFillStyle
| GCTile
;
214 gcv
.tile
= server
.root_pmap
;
215 XChangeGC(server
.dsp
, server
.gc
, mask
, &gcv
);
220 int compareMonitorPos(const void *monitor1
, const void *monitor2
)
222 Monitor
*m1
= (Monitor
*)monitor1
;
223 Monitor
*m2
= (Monitor
*)monitor2
;
228 else if (m1
->x
> m2
->x
) {
231 else if (m1
->y
< m2
->y
) {
234 else if (m1
->y
> m2
->y
) {
243 int compareMonitorIncluded(const void *monitor1
, const void *monitor2
)
245 Monitor
*m1
= (Monitor
*)monitor1
;
246 Monitor
*m2
= (Monitor
*)monitor2
;
248 if (m1
->x
>= m2
->x
&& m1
->y
>= m2
->y
&& (m1
->x
+m1
->width
) <= (m2
->x
+m2
->width
) && (m1
->y
+m1
->height
) <= (m2
->y
+m2
->height
)) {
249 // m1 included inside m2
261 if (XineramaIsActive(server
.dsp
)) {
262 XineramaScreenInfo
*info
= XineramaQueryScreens(server
.dsp
, &nbmonitor
);
263 XRRScreenResources
*res
= XRRGetScreenResourcesCurrent(server
.dsp
, server
.root_win
);
265 if (res
&& res
->ncrtc
>= nbmonitor
) {
266 // use xrandr to identify monitors (does not work with proprietery nvidia drivers)
267 printf("xRandr: Found crtc's: %d\n", res
->ncrtc
);
268 server
.monitor
= malloc(res
->ncrtc
* sizeof(Monitor
));
269 for (i
=0; i
<res
->ncrtc
; ++i
) {
270 XRRCrtcInfo
* crtc_info
= XRRGetCrtcInfo(server
.dsp
, res
, res
->crtcs
[i
]);
271 server
.monitor
[i
].x
= crtc_info
->x
;
272 server
.monitor
[i
].y
= crtc_info
->y
;
273 server
.monitor
[i
].width
= crtc_info
->width
;
274 server
.monitor
[i
].height
= crtc_info
->height
;
275 server
.monitor
[i
].names
= malloc((crtc_info
->noutput
+1) * sizeof(char*));
276 for (j
=0; j
<crtc_info
->noutput
; ++j
) {
277 XRROutputInfo
* output_info
= XRRGetOutputInfo(server
.dsp
, res
, crtc_info
->outputs
[j
]);
278 printf("xRandr: Linking output %s with crtc %d\n", output_info
->name
, i
);
279 server
.monitor
[i
].names
[j
] = g_strdup(output_info
->name
);
280 XRRFreeOutputInfo(output_info
);
282 server
.monitor
[i
].names
[j
] = 0;
283 XRRFreeCrtcInfo(crtc_info
);
285 nbmonitor
= res
->ncrtc
;
287 else if (info
&& nbmonitor
> 0) {
288 server
.monitor
= malloc(nbmonitor
* sizeof(Monitor
));
289 for (i
=0 ; i
< nbmonitor
; i
++) {
290 server
.monitor
[i
].x
= info
[i
].x_org
;
291 server
.monitor
[i
].y
= info
[i
].y_org
;
292 server
.monitor
[i
].width
= info
[i
].width
;
293 server
.monitor
[i
].height
= info
[i
].height
;
294 server
.monitor
[i
].names
= 0;
299 qsort(server
.monitor
, nbmonitor
, sizeof(Monitor
), compareMonitorIncluded
);
301 // remove monitor included into another one
303 while (i
< nbmonitor
) {
304 for (j
=0; j
< i
; j
++) {
305 if (compareMonitorIncluded(&server
.monitor
[i
], &server
.monitor
[j
]) > 0) {
312 for (j
=i
; j
<nbmonitor
; ++j
)
313 if (server
.monitor
[j
].names
)
314 g_strfreev(server
.monitor
[j
].names
);
315 server
.nb_monitor
= i
;
316 server
.monitor
= realloc(server
.monitor
, server
.nb_monitor
* sizeof(Monitor
));
317 qsort(server
.monitor
, server
.nb_monitor
, sizeof(Monitor
), compareMonitorPos
);
320 XRRFreeScreenResources(res
);
324 if (!server
.nb_monitor
) {
325 server
.nb_monitor
= 1;
326 server
.monitor
= malloc(sizeof(Monitor
));
327 server
.monitor
[0].x
= server
.monitor
[0].y
= 0;
328 server
.monitor
[0].width
= DisplayWidth (server
.dsp
, server
.screen
);
329 server
.monitor
[0].height
= DisplayHeight (server
.dsp
, server
.screen
);
330 server
.monitor
[0].names
= 0;
339 // detect number of desktops
340 // wait 15s to leave some time for window manager startup
341 for (i
=0 ; i
< 15 ; i
++) {
342 server
.nb_desktop
= server_get_number_of_desktop ();
343 if (server
.nb_desktop
> 0) break;
346 if (server
.nb_desktop
== 0) {
347 server
.nb_desktop
= 1;
348 fprintf(stderr
, "warning : WM doesn't respect NETWM specs. tint2 default to 1 desktop.\n");
353 void server_init_visual()
355 // inspired by freedesktops fdclock ;)
357 XVisualInfo templ
= { .screen
=server
.screen
, .depth
=32, .class=TrueColor
};
359 xvi
= XGetVisualInfo(server
.dsp
, VisualScreenMask
|VisualDepthMask
|VisualClassMask
, &templ
, &nvi
);
364 XRenderPictFormat
*format
;
365 for (i
= 0; i
< nvi
; i
++) {
366 format
= XRenderFindVisualFormat(server
.dsp
, xvi
[i
].visual
);
367 if (format
->type
== PictTypeDirect
&& format
->direct
.alphaMask
) {
368 visual
= xvi
[i
].visual
;
375 // check composite manager
376 server
.composite_manager
= XGetSelectionOwner(server
.dsp
, server
.atom
._NET_WM_CM_S0
);
378 XFreeColormap(server
.dsp
, server
.colormap
);
379 if (server
.colormap32
)
380 XFreeColormap(server
.dsp
, server
.colormap32
);
383 server
.visual32
= visual
;
384 server
.colormap32
= XCreateColormap(server
.dsp
, server
.root_win
, visual
, AllocNone
);
387 if (visual
&& server
.composite_manager
!= None
&& snapshot_path
== 0) {
388 XSetWindowAttributes attrs
;
389 attrs
.event_mask
= StructureNotifyMask
;
390 XChangeWindowAttributes (server
.dsp
, server
.composite_manager
, CWEventMask
, &attrs
);
392 server
.real_transparency
= 1;
394 printf("real transparency on... depth: %d\n", server
.depth
);
395 server
.colormap
= XCreateColormap(server
.dsp
, server
.root_win
, visual
, AllocNone
);
396 server
.visual
= visual
;
399 // no composite manager or snapshot mode => fake transparency
400 server
.real_transparency
= 0;
401 server
.depth
= DefaultDepth(server
.dsp
, server
.screen
);
402 printf("real transparency off.... depth: %d\n", server
.depth
);
403 server
.colormap
= DefaultColormap(server
.dsp
, server
.screen
);
404 server
.visual
= DefaultVisual(server
.dsp
, server
.screen
);