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