1 module window;
2 
3 import cboxapp;
4 import config;
5 import types;
6 import std.string;
7 import std.conv;
8 import kernel;
9 import old;
10 import utils;
11 import legacy;
12 import monitor;
13 import helper.x11;
14 import theme.manager;
15 
16 import std.algorithm;
17 import deimos.X11.X;
18 import deimos.X11.Xlib;
19 import deimos.X11.keysymdef;
20 import deimos.X11.Xutil;
21 import deimos.X11.Xatom;
22 import std.algorithm;
23 
24 enum { 
25 	NetSupported, 
26 	NetWMName, 
27 	NetWMState,
28     NetWMFullscreen, 
29     NetActiveWindow,
30     NetWMWindowType,
31     NetWMWindowTypeDialog, 
32     NetWMWindowOnTop,
33     NetClientList, 
34     NetLast 
35 }; /* EWMH atoms */
36 
37 enum { 
38 	WMProtocols, 
39 	WMDelete, 
40 	WMState, 
41 	WMTakeFocus, 
42 	WMLast 
43 }; /* default atoms */
44 
45 class WindowManager
46 {
47 	Atom[WMLast] wmatom;
48 	Atom[NetLast] netatom;
49 
50 	this()
51 	{
52 		/* init atoms */
53         wmatom[WMProtocols] = XInternAtom(AppDisplay.instance().dpy, cast(char*)("WM_PROTOCOLS".toStringz), false);
54         wmatom[WMDelete] = XInternAtom(AppDisplay.instance().dpy, cast(char*)("WM_DELETE_WINDOW".toStringz), false);
55         wmatom[WMState] = XInternAtom(AppDisplay.instance().dpy, cast(char*)("WM_STATE".toStringz), false);
56         wmatom[WMTakeFocus] = XInternAtom(AppDisplay.instance().dpy, cast(char*)("WM_TAKE_FOCUS".toStringz), false);
57 
58         netatom[NetActiveWindow] = XInternAtom(AppDisplay.instance().dpy, cast(char*)("_NET_ACTIVE_WINDOW".toStringz), false);
59         netatom[NetSupported] = XInternAtom(AppDisplay.instance().dpy, cast(char*)("_NET_SUPPORTED".toStringz), false);
60         netatom[NetWMName] = XInternAtom(AppDisplay.instance().dpy, cast(char*)("_NET_WM_NAME".toStringz), false);
61         netatom[NetWMState] = XInternAtom(AppDisplay.instance().dpy, cast(char*)("_NET_WM_STATE".toStringz), false);
62         netatom[NetWMFullscreen] = XInternAtom(AppDisplay.instance().dpy, cast(char*)("_NET_WM_STATE_FULLSCREEN".toStringz), false);
63         netatom[NetWMWindowType] = XInternAtom(AppDisplay.instance().dpy, cast(char*)("_NET_WM_WINDOW_TYPE".toStringz), false);
64         netatom[NetWMWindowTypeDialog] = XInternAtom(AppDisplay.instance().dpy, cast(char*)("_NET_WM_WINDOW_TYPE_DIALOG".toStringz), false);
65         netatom[NetClientList] = XInternAtom(AppDisplay.instance().dpy, cast(char*)("_NET_CLIENT_LIST".toStringz), false);	
66 	    netatom[NetWMWindowOnTop] = XInternAtom(AppDisplay.instance().dpy, cast(char*)("_NET_WM_STATE_ABOVE".toStringz), false);	
67 	}
68 
69 	Atom[] getAllAtoms(string atomType = "NetLast")
70 	{
71 		Atom[] tmp;
72 
73 		switch (atomType) {
74 			case "NetLast": 
75 				tmp = this.netatom;
76 				break;
77 			case "WMLast":
78 				tmp = this.wmatom;
79 				break;
80 			default: 
81 				throw new Exception(format("Only option NetLast and WMLast are available. You : %s", atomType));
82 		}
83 
84 		return tmp;
85 	}
86 
87 	Atom getAtom(string atomType, int atomId)
88 	{
89 		Atom tmp;
90 
91 		switch (atomType) {
92 			case "NetLast":
93 				tmp = this.netatom[atomId];
94 				break;
95 			case "WMLast":
96 				tmp = this.wmatom[atomId];
97 				break;
98 			default: 
99 				throw new Exception(format("Only option NetLast and WMLast are available. You : %s", atomType));
100 		}
101 
102 		return tmp;
103 	}
104 
105 	void showhide(Client *c) 
106 	{
107 	    if(!c)
108 	        return;
109 	    if(ISVISIBLE(c)) { /* show clients top down */
110 	        XMoveWindow(AppDisplay.instance().dpy, c.win, c.x, c.y);
111 	        if((!c.mon.lt[c.mon.sellt].arrange || c.isfloating) && !c.isfullscreen)
112 	            resize(c, c.x, c.y, c.w, c.h, false);
113 	        showhide(c.snext);
114 	    } else { /* hide clients bottom up */
115 	        showhide(c.snext);
116 	        XMoveWindow(AppDisplay.instance().dpy, c.win, WIDTH(c) * -2, c.y);
117 	    }
118 	}
119 
120 	void updatetitle(Client *c) 
121 	{
122 	    if(!X11Helper.gettextprop(c.win, netatom[NetWMName], c.name)) {
123 	        X11Helper.gettextprop(c.win, XA_WM_NAME, c.name);
124 	    }
125 
126 	    if(c.name.length == 0) { /* hack to mark broken clients */
127 	        c.name = broken;
128 	    }
129 	}
130 
131 	void applyrules(Client *c) 
132 	{
133 	    XClassHint ch = { null, null };
134 	    /* rule matching */
135 	    c.isfloating = false;
136 	    c.tags = 0;
137 	    XGetClassHint(AppDisplay.instance().dpy, c.win, &ch);
138 	    immutable auto klass    = ch.res_class ? ch.res_class.to!string : broken;
139 	    immutable auto instance = ch.res_name  ? ch.res_name.to!string : broken;
140 	    foreach(immutable r; rules) {
141 	        if( (r.title.length==0 || r.title.indexOf(c.name) >= 0) &&
142 	                (r.klass.length==0 || r.klass.indexOf(klass) >= 0) &&
143 	                (r.instance.length==0 || r.instance.indexOf(instance) >= 0)) {
144 	            c.isfloating = r.isfloating;
145 	            c.tags |= r.tags;
146 
147 	            auto m = mons.range.find!(mon => mon.num == r.monitor).front;
148 	            if(m) {
149 	                c.mon = m;
150 	            }
151 	        }
152 	    }
153 	    if(ch.res_class)
154 	        XFree(ch.res_class);
155 	    if(ch.res_name)
156 	        XFree(ch.res_name);
157 	    c.tags = c.tags & TAGMASK ? c.tags & TAGMASK : c.mon.tagset[c.mon.seltags];
158 	}
159 
160 	void manage(Window w, XWindowAttributes *wa) 
161 	{
162 	    Client *c, t = null;
163 	    Window trans = None;
164 	    XWindowChanges wc;
165 
166 	    c = new Client();
167 	    if(c is null) {
168 	        die("fatal: could not malloc() %u bytes\n", Client.sizeof);
169 	    }
170 	    c.win = w;
171 	    updatetitle(c);
172 
173 	    c.mon = null;
174 	    if(XGetTransientForHint(AppDisplay.instance().dpy, w, &trans)) {
175 	        t = wintoclient(trans);
176 	        if(t) {
177 	            c.mon = t.mon;
178 	            c.tags = t.tags;
179 	        }
180 	    } 
181 
182 	    if(!c.mon) {
183 	        c.mon = selmon;
184 	        this.applyrules(c);
185 	    }
186 
187 	    /* geometry */
188 	    c.x = c.oldx = wa.x;
189 	    c.y = c.oldy = wa.y;
190 	    c.w = c.oldw = wa.width;
191 	    c.h = c.oldh = wa.height;
192 	    c.oldbw = wa.border_width;
193 
194 	    if(c.x + WIDTH(c) > c.mon.mx + c.mon.mw)
195 	        c.x = c.mon.mx + c.mon.mw - WIDTH(c);
196 	    if(c.y + HEIGHT(c) > c.mon.my + c.mon.mh)
197 	        c.y = c.mon.my + c.mon.mh - HEIGHT(c);
198 	    c.x = max(c.x, c.mon.mx);
199 	    /* only fix client y-offset, if the client center might cover the bar */
200 	    c.y = max(c.y, ((c.mon.by == c.mon.my) && (c.x + (c.w / 2) >= c.mon.wx)
201 	                    && (c.x + (c.w / 2) < c.mon.wx + c.mon.ww)) ? bh : c.mon.my);
202 	    c.bw = borderpx;
203 
204 	    wc.border_width = c.bw;
205 
206 	    XConfigureWindow(AppDisplay.instance().dpy, w, CWBorderWidth, &wc);
207 	    XSetWindowBorder(AppDisplay.instance().dpy, w, ThemeManager.instance().getScheme(SchemeNorm).border.rgb);
208 
209 	    configure(c); /* propagates border_width, if size doesn't change */
210 	    updatewindowtype(c);
211 	    updatesizehints(c);
212 	    updatewmhints(c);
213 
214 	    XSelectInput(AppDisplay.instance().dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask);
215 	    mouseEventHandler.grabbuttons(c, false);
216 	    
217 	    if(!c.isfloating)
218 	        c.isfloating = c.oldstate = trans != None || c.isfixed;
219 
220 	    if(c.isfloating)
221 	        XRaiseWindow(AppDisplay.instance().dpy, c.win);
222 
223 	    attach(c);
224 	    attachstack(c);
225 
226 	    XChangeProperty(AppDisplay.instance().dpy, rootWin, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend,
227 	                    cast(ubyte*)(&(c.win)), 1);
228 	    XMoveResizeWindow(AppDisplay.instance().dpy, c.win, c.x + 2 * sw, c.y, c.w, c.h); /* some windows require this */
229 	    
230 	    setclientstate(c, NormalState);
231 
232 	    if (c.mon == selmon)
233 	        unfocus(selmon.sel, false);
234 	    c.mon.sel = c;
235 
236 	    arrange(c.mon);
237 	    XMapWindow(AppDisplay.instance().dpy, c.win);
238 	    focus(null);
239 	}
240 
241 
242 	Atom getatomprop(Client *c, Atom prop) 
243 	{
244 	    int di;
245 	    ulong dl;
246 	    ubyte* p = null;
247 	    Atom da, atom = None;
248 
249 	    if(XGetWindowProperty(AppDisplay.instance().dpy, c.win, prop, 0L, atom.sizeof, false, XA_ATOM,
250 	                          &da, &di, &dl, &dl, &p) == XErrorCode.Success && p) {
251 	        atom = *cast(Atom *)(p);
252 	        XFree(p);
253 	    }
254 	    return atom;
255 	}
256 
257 	void updatewindowtype(Client *c) 
258 	{
259 	    
260 	    Atom state = getatomprop(c, netatom[NetWMState]);
261 	    Atom wtype = getatomprop(c, netatom[NetWMWindowType]);
262 
263 	    if(state == netatom[NetWMFullscreen])
264 	        setfullscreen(c, true);
265 	    if(wtype == netatom[NetWMWindowTypeDialog])
266 	        c.isfloating = true;
267 	}
268 }
269 
270 void updatetitle(Client *c) 
271 {
272     if(!X11Helper.gettextprop(c.win, netatom[NetWMName], c.name)) {
273         X11Helper.gettextprop(c.win, XA_WM_NAME, c.name);
274     }
275     
276     /* hack to mark broken clients */
277     if(c.name.length == 0) { 
278         c.name = broken;
279     }
280 }
281 
282 void unmanage(Client *c, bool destroyed) 
283 {
284     Monitor *m = c.mon;
285     XWindowChanges wc;
286 
287     /* The server grab construct avoids race conditions. */
288     detach(c);
289     detachstack(c);
290 
291     if(!destroyed) {
292         wc.border_width = c.oldbw;
293         XGrabServer(AppDisplay.instance().dpy);
294         XSetErrorHandler(&xerrordummy);
295         XConfigureWindow(AppDisplay.instance().dpy, c.win, CWBorderWidth, &wc); /* restore border */
296         XUngrabButton(AppDisplay.instance().dpy, AnyButton, AnyModifier, c.win);
297         setclientstate(c, WithdrawnState);
298         XSync(AppDisplay.instance().dpy, false);
299         XSetErrorHandler(&xerror);
300         XUngrabServer(AppDisplay.instance().dpy);
301     }
302     
303     DGC.free(c);
304     focus(null);
305     updateclientlist();
306     arrange(m);
307 }
308 
309