├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── ROADMAP.md ├── dub.json ├── dub.selections.json ├── libcobox-wm.a ├── screenshots ├── cobox.png └── cobox2.png └── src ├── app.d ├── cboxapp.d ├── cli └── options.d ├── config.d ├── events ├── handler.d ├── interfaces.d ├── keyboard.d └── mouse.d ├── gui ├── bar.d ├── cursor.d ├── drawer.d └── font.d ├── helper ├── process.d └── x11.d ├── kernel.d ├── legacy.d ├── monitor.d ├── old.d ├── theme ├── layout.d └── manager.d ├── types.d ├── utils.d ├── window.d └── xinerama.d /.gitignore: -------------------------------------------------------------------------------- 1 | .dub 2 | docs.json 3 | __dummy.html 4 | *.o 5 | *.obj 6 | cobox-wm -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Jorge Meireles 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | cbox: 2 | dub build --compiler=dmd --build=release-nobounds --force 3 | all: cbox 4 | 5 | clean: 6 | rm cobox-wm 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cobox-wm - Dlang Window Manager 2 | CteamBox Window Manager in D programming Language. 3 | Thanks to DWM, DDWM project which alot of code has been forked and of course to fluxbox. 4 | It's still in a heavy development and alot of changes and features will come in a near future. If you feel like joining let me know. 5 | 6 | ## Requirements 7 | Dlang 8 | DUB 9 | 10 | ## Installation 11 | #### Download libX11 from Deimos 12 | 1. git clone https://github.com/D-Programming-Deimos/libX11 13 | 2. dub add-local libX11 0.0.1 14 | 3. 15 | 16 | #### Download Cobox 17 | 1. git clone https://github.com/jmeireles/cobox-wm 18 | 2. cd cobox-wm 19 | 3. dub build 20 | 21 | 22 | ### Testing with Xephyr 23 | Xephyr -ac -br -noreset -screen 800x600 :1 24 | 25 | DISPLAY=:1 ./cobox-wm 26 | 27 | 28 | ## Screen Shots 29 | ![ScreenShot](https://raw.githubusercontent.com/Faianca/cobox-wm/master/screenshots/cobox.png) 30 | 31 | ![ScreenShot](https://raw.githubusercontent.com/Faianca/cobox-wm/master/screenshots/cobox2.png) 32 | -------------------------------------------------------------------------------- /ROADMAP.md: -------------------------------------------------------------------------------- 1 | # Roadmap 2 | 3 | 1. Configs from file cfg 4 | 2. Keyshortcuts from file cfg 5 | 3. Use rc files 6 | 4. Window Decorations 7 | 5. ThemeManager 8 | 6. Menu 9 | 7. Better panel, with tray icons ( now its only supported by third party ) 10 | -------------------------------------------------------------------------------- /dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cobox-wm", 3 | "description": "Window Manager fork from DDWM and Fluxbox.", 4 | "copyright": "Copyright © 2015, root", 5 | "authors": ["faianca"], 6 | "license": "MIT-License", 7 | "dependencies":{ 8 | "x11": "~>1.0.13" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /dub.selections.json: -------------------------------------------------------------------------------- 1 | { 2 | "fileVersion": 1, 3 | "versions": { 4 | "dyaml": "0.5.1", 5 | "tinyendian": "0.1.2", 6 | "x11": "1.0.13" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /libcobox-wm.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Faianca/cobox-wm/212911301ae3a1951d853dab2f24e77cea082c4d/libcobox-wm.a -------------------------------------------------------------------------------- /screenshots/cobox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Faianca/cobox-wm/212911301ae3a1951d853dab2f24e77cea082c4d/screenshots/cobox.png -------------------------------------------------------------------------------- /screenshots/cobox2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Faianca/cobox-wm/212911301ae3a1951d853dab2f24e77cea082c4d/screenshots/cobox2.png -------------------------------------------------------------------------------- /src/app.d: -------------------------------------------------------------------------------- 1 | import kernel; 2 | import types; 3 | import cboxapp; 4 | import cli.options; 5 | import config; 6 | 7 | import std.stdio; 8 | import x11.X; 9 | import x11.Xlib; 10 | import std.datetime; 11 | 12 | int main(string[] args) 13 | { 14 | CboxOptions opts = new CboxOptions(); 15 | int exitcode = opts.parse(args); 16 | 17 | if (exitcode == -1) { 18 | return exitcode; 19 | } 20 | 21 | opts.update(); 22 | 23 | if(AppDisplay.instance().dpy is null) { 24 | stderr.writeln("cbox: cannot open display"); 25 | return -1; 26 | } 27 | 28 | writeln("Codename: Nikola 0.2"); 29 | 30 | Kernel kernel = new Kernel(); 31 | int response = kernel.boot(); 32 | 33 | writeln("cbox-"~VERSION~" end"); 34 | return response; 35 | } 36 | -------------------------------------------------------------------------------- /src/cboxapp.d: -------------------------------------------------------------------------------- 1 | module cboxapp; 2 | 3 | import std.stdio; 4 | import x11.X; 5 | import x11.Xlib; 6 | import x11.keysymdef; 7 | import x11.Xutil; 8 | import x11.Xatom; 9 | import window; 10 | import types; 11 | import theme.layout; 12 | import monitor; 13 | 14 | alias DGC = core.memory.GC; 15 | alias XGC = x11.Xlib.GC; 16 | 17 | import core.sys.posix.signal; 18 | import core.sys.posix.sys.wait; 19 | import core.sys.posix.unistd; 20 | 21 | /** 22 | * Singleton to hold our main Display 23 | **/ 24 | class AppDisplay 25 | { 26 | 27 | Display *dpy; 28 | bool running = true; 29 | 30 | static AppDisplay instance() 31 | { 32 | if (!instantiated_) { 33 | synchronized { 34 | if (instance_ is null) { 35 | instance_ = new AppDisplay; 36 | instance_.dpy = XOpenDisplay(null); 37 | } 38 | instantiated_ = true; 39 | } 40 | } 41 | return instance_; 42 | } 43 | 44 | void quit() 45 | { 46 | this.running = false; 47 | } 48 | 49 | private: 50 | this() {} 51 | static bool instantiated_; // Thread local 52 | __gshared AppDisplay instance_; 53 | 54 | } 55 | 56 | struct Client 57 | { 58 | string name; 59 | float mina, maxa; 60 | int x, y, w, h; 61 | int oldx, oldy, oldw, oldh; 62 | int basew, baseh, incw, inch, maxw, maxh, minw, minh; 63 | int bw, oldbw; 64 | uint tags; 65 | bool isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; 66 | Client *next; 67 | Client *snext; 68 | Monitor *mon; 69 | Window win; 70 | 71 | /** 72 | * A range to iterate over the client list via 'next' or 'snext', 73 | * as specified by the template string. 74 | * Example: 75 | * --- 76 | * auto r = ClientRange!"next"(clientPtr); 77 | * auto sr = ClientRange!"snext"(clientPtr); 78 | * --- 79 | */ 80 | struct ClientRange(string NextField) 81 | { 82 | Client* client; 83 | @property bool empty() 84 | { 85 | return client is null; 86 | } 87 | 88 | @property auto front() 89 | { 90 | return client; 91 | } 92 | 93 | auto popFront() 94 | { 95 | mixin(`client = client.`~NextField~`;`); 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/cli/options.d: -------------------------------------------------------------------------------- 1 | module cli.options; 2 | 3 | import std.string; 4 | import std.stdio; 5 | import config; 6 | import std.process; 7 | import std.file; 8 | 9 | struct CFInfo 10 | { 11 | bool create_file; 12 | immutable string default_name; 13 | immutable string filename; 14 | immutable string fullName; 15 | 16 | this(bool cf, string dn, string fn) 17 | { 18 | create_file = cf; 19 | default_name = dn; 20 | filename = fn; 21 | fullName = dn ~ fn; 22 | } 23 | } 24 | 25 | class CboxOptions 26 | { 27 | string cboxhome = "/home/jmeireles/.cbox"; 28 | CFInfo *keys; 29 | CFInfo *startup; 30 | 31 | this() 32 | { 33 | keys = new CFInfo(false, this.cboxhome, "/keys"); 34 | startup = new CFInfo(false, this.cboxhome, "/startup"); 35 | } 36 | 37 | void update() 38 | { 39 | if (exists(this.startup.fullName)) { 40 | spawnProcess(this.startup.fullName); 41 | } 42 | } 43 | 44 | /** 45 | * setup the configutation files in 46 | * home directory 47 | */ 48 | void setupConfigFiles() 49 | { 50 | 51 | } 52 | 53 | int parse(string[] args) 54 | { 55 | if (args.length > 1) { 56 | foreach(string arg; args) { 57 | if(arg == "-v" || arg == "-version") { 58 | return this.versions(); 59 | } else if(arg == "-h" || arg == "-help") { 60 | return this.help(); 61 | } 62 | } 63 | } 64 | 65 | return 1; 66 | } 67 | 68 | private: 69 | int versions() 70 | { 71 | writeln( 72 | "Cobox-wm-"~VERSION~"\n"~ 73 | "The Cteam Window Manager.\n"~ 74 | "© 2015 Cbox\n" 75 | ); 76 | return -1; 77 | } 78 | 79 | int help() 80 | { 81 | writeln( 82 | "Cobox-wm "~VERSION~" : (c) Cobox Team\n" ~ 83 | "Website: http://www.fluxbox.org/\n\n" ~ 84 | "-display \t\tuse display connection.\n" ~ 85 | "-screen \trun on specified screens only.\n" ~ 86 | "-rc \t\t\tuse alternate resource file.\n" ~ 87 | "-no-slit\t\t\tdo not provide a slit.\n" ~ 88 | "-no-toolbar\t\t\tdo not provide a toolbar.\n" ~ 89 | "-version\t\t\tdisplay version and exit.\n" ~ 90 | "-info\t\t\t\tdisplay some useful information.\n" ~ 91 | "-list-commands\t\t\tlist all valid key commands.\n" ~ 92 | "-sync\t\t\t\tsynchronize with X server for debugging.\n" ~ 93 | "-log \t\t\tlog output to file.\n" ~ 94 | "-help\t\t\t\tdisplay this help text and exit.\n\n" 95 | ); 96 | return -1; 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /src/config.d: -------------------------------------------------------------------------------- 1 | module config; 2 | import x11.X; 3 | import types; 4 | import old; 5 | import theme.layout; 6 | 7 | static immutable string VERSION = "0.1 Cobox"; 8 | enum MODKEY = Mod1Mask; 9 | 10 | static immutable Layout[] layouts = [ 11 | /* symbol arrange function */ 12 | { symbol:"><>", arrange:null }, /* no layout function means floating behavior */ 13 | /* { symbol:"[]=", arrange:&tile }, first entry is default 14 | { symbol:"[M]", arrange:&monocle },*/ 15 | ]; 16 | 17 | static immutable Rule[] rules = [ 18 | /* xprop(1): 19 | * WM_CLASS(STRING) = instance, klass 20 | * WM_NAME(STRING) = title 21 | */ 22 | /* klass instance title tags mask isfloating monitor */ 23 | { "xterm", null, null, 1, false, -1 }, 24 | //{ "Firefox", null, null, 1 << 8, false, -1 }, 25 | ]; 26 | 27 | static immutable string normbordercolor = "#cccccc"; 28 | static immutable string normbgcolor = "#000000"; 29 | static immutable string normfgcolor = "#cccccc"; 30 | static immutable string selbordercolor = "#cccccc"; 31 | static immutable string selbgcolor = "#FF0000"; 32 | static immutable string selfgcolor = "#eeeeee"; 33 | 34 | static immutable uint borderpx = 0; /* border pixel of windows */ 35 | static immutable uint snap = 32; /* snap pixel */ 36 | static immutable bool showbar = true; /* false means no bar */ 37 | static immutable bool topbar = true; /* false means bottom bar */ 38 | static bool running = true; 39 | 40 | immutable string broken = "broken"; 41 | immutable string[] tags = [ "asterix", "obelix", "idefix", "avoranfix", "logs" ]; 42 | static immutable string font = "-*-terminus-medium-r-*-*-16-*-*-*-*-*-*-*"; 43 | 44 | /* commands */ 45 | static char[2] dmenumon = "0"; /* component of dmenucmd, manipulated in spawn() */ 46 | static immutable string[] dmenucmd = [ "dmenu_run", "-fn", font, "-nb", normbgcolor, "-nf", normfgcolor, "-sb", selbgcolor, "-sf", selfgcolor]; 47 | static immutable string[] termcmd = ["uxterm"]; 48 | 49 | /* layout(s) */ 50 | static immutable float mfact = 0.55; /* factor of master area size [0.05..0.95] */ 51 | static immutable int nmaster = 1; /* number of clients in master area */ 52 | static immutable bool resizehints = true; /* true means respect size hints in tiled resizals */ -------------------------------------------------------------------------------- /src/events/handler.d: -------------------------------------------------------------------------------- 1 | module events.handler; 2 | 3 | import events.keyboard; 4 | import events.mouse; 5 | import kernel; 6 | import old; 7 | import config; 8 | import types; 9 | import gui.bar; 10 | import cboxapp; 11 | import window; 12 | import monitor; 13 | 14 | import x11.X; 15 | import x11.Xlib; 16 | import x11.keysymdef; 17 | import x11.Xutil; 18 | import x11.Xatom; 19 | import std.stdio; 20 | import std.c.locale; 21 | import std.c.string; 22 | import std.c.stdlib; 23 | 24 | class EventHandler 25 | { 26 | KeyboardEvents keyboard; 27 | MouseEvents mouse; 28 | 29 | this(KeyboardEvents keyboardEvents, MouseEvents mouseEvents) 30 | { 31 | keyboard = keyboardEvents; 32 | mouse = mouseEvents; 33 | 34 | handler[ClientMessage] = &clientmessage; 35 | handler[ConfigureRequest] = &configurerequest; 36 | handler[ConfigureNotify] = &configurenotify; 37 | handler[DestroyNotify] = &destroynotify; 38 | handler[Expose] = &expose; 39 | handler[FocusIn] = &focusin; 40 | handler[MappingNotify] = &mappingnotify; 41 | handler[MapRequest] = &maprequest; 42 | handler[MotionNotify] = &motionnotify; 43 | handler[PropertyNotify] = &propertynotify; 44 | handler[UnmapNotify] = &unmapnotify; 45 | } 46 | 47 | void function(XEvent*)[LASTEvent] handler; 48 | 49 | void listen(XEvent* ev) 50 | { 51 | switch (ev.type) { 52 | case KeyPress: 53 | this.keyboard.listen(ev); 54 | break; 55 | 56 | case ButtonPress: 57 | this.mouse.listen(ev); 58 | break; 59 | 60 | default: { } 61 | } 62 | 63 | if(handler[ev.type]) { 64 | handler[ev.type](ev); /* call handler */ 65 | } 66 | } 67 | } 68 | 69 | void maprequest(XEvent *e) 70 | { 71 | static XWindowAttributes wa; 72 | XMapRequestEvent *ev = &e.xmaprequest; 73 | 74 | if(!XGetWindowAttributes(AppDisplay.instance().dpy, ev.window, &wa)) 75 | return; 76 | if(wa.override_redirect) 77 | return; 78 | if(!wintoclient(ev.window)) 79 | windowManager.manage(ev.window, &wa); 80 | } 81 | 82 | void propertynotify(XEvent *e) 83 | { 84 | 85 | Client *c; 86 | Window trans; 87 | XPropertyEvent *ev = &e.xproperty; 88 | if((ev.window == rootWin) && (ev.atom == XA_WM_NAME)) 89 | updatestatus(); 90 | else if(ev.state == PropertyDelete) 91 | return; /* ignore */ 92 | else { 93 | c = wintoclient(ev.window); 94 | if(c) { 95 | switch(ev.atom) { 96 | default: 97 | break; 98 | case XA_WM_TRANSIENT_FOR: 99 | if(!c.isfloating && (XGetTransientForHint(AppDisplay.instance().dpy, c.win, &trans))) { 100 | c.isfloating = (wintoclient(trans) !is null); 101 | if(c.isfloating) { 102 | arrange(c.mon); 103 | } 104 | } 105 | break; 106 | case XA_WM_NORMAL_HINTS: 107 | updatesizehints(c); 108 | break; 109 | case XA_WM_HINTS: 110 | updatewmhints(c); 111 | drawbars(); 112 | break; 113 | } 114 | 115 | if(ev.atom == XA_WM_NAME || ev.atom == netatom[NetWMName]) { 116 | windowManager.updatetitle(c); 117 | if(c == c.mon.sel) 118 | drawbar(c.mon); 119 | } 120 | 121 | if(ev.atom == netatom[NetWMWindowType]) 122 | windowManager.updatewindowtype(c); 123 | } 124 | } 125 | } 126 | 127 | void focusin(XEvent *e) 128 | { 129 | /* there are some broken focus acquiring clients */ 130 | XFocusChangeEvent *ev = &e.xfocus; 131 | if(selmon.sel && ev.window != selmon.sel.win) { 132 | setfocus(selmon.sel); 133 | } 134 | } 135 | 136 | void unmapnotify(XEvent *e) 137 | { 138 | 139 | Client *c; 140 | XUnmapEvent *ev = &e.xunmap; 141 | 142 | c= wintoclient(ev.window); 143 | if(c) { 144 | if(ev.send_event) 145 | setclientstate(c, WithdrawnState); 146 | else 147 | unmanage(c, false); 148 | } 149 | } 150 | 151 | void destroynotify(XEvent *e) 152 | { 153 | Client *c; 154 | XDestroyWindowEvent *ev = &e.xdestroywindow; 155 | 156 | c = wintoclient(ev.window); 157 | if(c !is null) { 158 | unmanage(c, true); 159 | } 160 | } 161 | 162 | void enternotify(XEvent *e) 163 | { 164 | Client *c; 165 | Monitor *m; 166 | XCrossingEvent *ev = &e.xcrossing; 167 | 168 | if((ev.mode != NotifyNormal || ev.detail == NotifyInferior) && ev.window != rootWin) 169 | return; 170 | c = wintoclient(ev.window); 171 | m = c ? c.mon : wintomon(ev.window); 172 | if(m != selmon) { 173 | unfocus(selmon.sel, true); 174 | selmon = m; 175 | } else if(!c || c == selmon.sel) { 176 | return; 177 | } 178 | focus(c); 179 | } 180 | 181 | void mappingnotify(XEvent *e) 182 | { 183 | XMappingEvent *ev = &e.xmapping; 184 | 185 | XRefreshKeyboardMapping(ev); 186 | if(ev.request == MappingKeyboard) 187 | keyboardEventHandler.grabkeys(); 188 | } 189 | 190 | 191 | void expose(XEvent *e) 192 | { 193 | Monitor *m; 194 | XExposeEvent *ev = &e.xexpose; 195 | 196 | if(ev.count == 0) { 197 | m = wintomon(ev.window); 198 | if(m !is null) { 199 | drawbar(m); 200 | } 201 | } 202 | } 203 | 204 | void configurerequest(XEvent *e) 205 | { 206 | Client *c; 207 | Monitor *m; 208 | XConfigureRequestEvent *ev = &e.xconfigurerequest; 209 | XWindowChanges wc; 210 | c = wintoclient(ev.window); 211 | if(c) { 212 | if(ev.value_mask & CWBorderWidth) { 213 | c.bw = ev.border_width; 214 | } else if(c.isfloating || !selmon.lt[selmon.sellt].arrange) { 215 | m = c.mon; 216 | if(ev.value_mask & CWX) { 217 | c.oldx = c.x; 218 | c.x = m.mx + ev.x; 219 | } 220 | if(ev.value_mask & CWY) { 221 | c.oldy = c.y; 222 | c.y = m.my + ev.y; 223 | } 224 | if(ev.value_mask & CWWidth) { 225 | c.oldw = c.w; 226 | c.w = ev.width; 227 | } 228 | if(ev.value_mask & CWHeight) { 229 | c.oldh = c.h; 230 | c.h = ev.height; 231 | } 232 | if((c.x + c.w) > m.mx + m.mw && c.isfloating) 233 | c.x = m.mx + (m.mw / 2 - WIDTH(c) / 2); /* center in x direction */ 234 | if((c.y + c.h) > m.my + m.mh && c.isfloating) 235 | c.y = m.my + (m.mh / 2 - HEIGHT(c) / 2); /* center in y direction */ 236 | if((ev.value_mask & (CWX|CWY)) && !(ev.value_mask & (CWWidth|CWHeight))) 237 | configure(c); 238 | if(ISVISIBLE(c)) 239 | XMoveResizeWindow(AppDisplay.instance().dpy, c.win, c.x, c.y, c.w, c.h); 240 | } else { 241 | configure(c); 242 | } 243 | } else { 244 | wc.x = ev.x; 245 | wc.y = ev.y; 246 | wc.width = ev.width; 247 | wc.height = ev.height; 248 | wc.border_width = ev.border_width; 249 | wc.sibling = ev.above; 250 | wc.stack_mode = ev.detail; 251 | 252 | // HACK to fix the slowdown XError issue. value_mask recieved is 36 but needs to be 12 253 | // 36 ==> b2:width, b5:sibling 254 | // 12 ==> b2:width, b3:height 255 | ev.value_mask = 12; 256 | XConfigureWindow(AppDisplay.instance().dpy, ev.window, ev.value_mask, &wc); 257 | } 258 | XSync(AppDisplay.instance().dpy, false); 259 | } 260 | 261 | void configurenotify(XEvent *e) 262 | { 263 | XConfigureEvent *ev = &e.xconfigure; 264 | bool dirty; 265 | 266 | // TODO: updategeom handling sucks, needs to be simplified 267 | if(ev.window == rootWin) { 268 | dirty = (sw != ev.width || sh != ev.height); 269 | sw = ev.width; 270 | sh = ev.height; 271 | if(updategeom() || dirty) { 272 | drw.resize(sw, bh); 273 | updatebars(); 274 | foreach(m; mons.range) { 275 | XMoveResizeWindow(AppDisplay.instance().dpy, m.barwin, m.wx, m.by, m.ww, bh); 276 | } 277 | focus(null); 278 | arrange(null); 279 | } 280 | } 281 | } 282 | 283 | void motionnotify(XEvent *e) 284 | { 285 | static Monitor *mon = null; 286 | Monitor *m; 287 | XMotionEvent *ev = &e.xmotion; 288 | 289 | if(ev.window != rootWin) 290 | return; 291 | if((m = recttomon(ev.x_root, ev.y_root, 1, 1)) != mon && mon) { 292 | unfocus(selmon.sel, true); 293 | selmon = m; 294 | focus(null); 295 | } 296 | mon = m; 297 | } 298 | -------------------------------------------------------------------------------- /src/events/interfaces.d: -------------------------------------------------------------------------------- 1 | module events.interfaces; 2 | 3 | import x11.Xlib; 4 | 5 | interface EventInterface 6 | { 7 | void listen(XEvent *e); 8 | //void addEvent(); 9 | } 10 | -------------------------------------------------------------------------------- /src/events/keyboard.d: -------------------------------------------------------------------------------- 1 | module events.keyboard; 2 | 3 | import kernel; 4 | import old; 5 | import config; 6 | import types; 7 | import cboxapp; 8 | import gui.bar; 9 | import helper.process; 10 | 11 | import x11.X; 12 | import x11.Xlib; 13 | import x11.keysymdef; 14 | import x11.Xutil; 15 | import x11.Xatom; 16 | import std.stdio; 17 | import events.interfaces; 18 | 19 | struct Key { 20 | uint mod; 21 | KeySym keysym; 22 | void function(const Arg* a) func; 23 | const Arg arg; 24 | 25 | this(uint mod, KeySym keysym, void function(const Arg* a) func) { 26 | this(mod, keysym, func, null); 27 | } 28 | this(T)(uint mod, KeySym keysym, void function(const Arg* a) func, T arg) { 29 | this.mod = mod; 30 | this.keysym = keysym; 31 | this.func = func; 32 | this.arg = makeArg(arg); 33 | } 34 | } 35 | 36 | class KeyboardEvents : EventInterface 37 | { 38 | Key[] keys; 39 | 40 | this() 41 | { 42 | keys = [ 43 | Key( MODKEY|ShiftMask, XK_Return, &spawn, termcmd ), // termcmd 44 | Key( MODKEY, XK_b, &togglebar ), // TopBar.instance().togglebar 45 | Key( MODKEY, XK_j, &focusstack, +1 ), 46 | Key( MODKEY, XK_k, &focusstack, -1 ), 47 | Key( MODKEY, XK_i, &incnmaster, +1 ), 48 | Key( MODKEY, XK_d, &incnmaster, -1 ), 49 | Key( MODKEY, XK_h, &setmfact, -0.05 ), 50 | Key( MODKEY, XK_l, &setmfact, +0.05 ), 51 | Key( MODKEY, XK_Return, &zoom ), 52 | Key( MODKEY, XK_Tab, &view ), 53 | Key( MODKEY|ShiftMask, XK_c, &killclient ), 54 | Key( MODKEY|ShiftMask, XK_space, &togglefloating, 0 ), 55 | Key( MODKEY, XK_0, &view, ~0 ), 56 | Key( MODKEY|ShiftMask, XK_0, &tag, ~0 ), 57 | Key( MODKEY, XK_comma, &focusmon, -1 ), 58 | Key( MODKEY, XK_period, &focusmon, +1 ), 59 | Key( MODKEY|ShiftMask, XK_comma, &tagmon, -1 ), 60 | Key( MODKEY|ShiftMask, XK_period, &tagmon, +1 ), 61 | Key( MODKEY, XK_1, &view, 1<<0 ), 62 | Key( MODKEY|ControlMask, XK_1, &toggleview, 1<<0 ), 63 | Key( MODKEY|ShiftMask, XK_1, &tag, 1<<0 ), 64 | Key( MODKEY|ControlMask|ShiftMask, XK_1, &toggletag, 1<<0 ), 65 | Key( MODKEY, XK_2, &view, 1<<1 ), 66 | Key( MODKEY|ControlMask, XK_2, &toggleview, 1<<1 ), 67 | Key( MODKEY|ShiftMask, XK_2, &tag, 1<<1 ), 68 | Key( MODKEY|ControlMask|ShiftMask, XK_2, &toggletag, 1<<1 ), 69 | Key( MODKEY, XK_3, &view, 1<<2 ), 70 | Key( MODKEY|ControlMask, XK_3, &toggleview, 1<<2 ), 71 | Key( MODKEY|ShiftMask, XK_3, &tag, 1<<2 ), 72 | Key( MODKEY|ControlMask|ShiftMask, XK_3, &toggletag, 1<<2 ), 73 | Key( MODKEY, XK_4, &view, 1<<3 ), 74 | Key( MODKEY|ControlMask, XK_4, &toggleview, 1<<3 ), 75 | Key( MODKEY|ShiftMask, XK_4, &tag, 1<<3 ), 76 | Key( MODKEY|ControlMask|ShiftMask, XK_4, &toggletag, 1<<3 ), 77 | Key( MODKEY, XK_5, &view, 1<<4 ), 78 | Key( MODKEY|ControlMask, XK_5, &toggleview, 1<<4 ), 79 | Key( MODKEY|ShiftMask, XK_5, &tag, 1<<4 ), 80 | Key( MODKEY|ControlMask|ShiftMask, XK_5, &toggletag, 1<<4 ), 81 | Key( MODKEY, XK_6, &view, 1<<5 ), 82 | Key( MODKEY|ControlMask, XK_6, &toggleview, 1<<5 ), 83 | Key( MODKEY|ShiftMask, XK_6, &tag, 1<<5 ), 84 | Key( MODKEY|ControlMask|ShiftMask, XK_6, &toggletag, 1<<5 ), 85 | Key( MODKEY, XK_7, &view, 1<<6 ), 86 | Key( MODKEY|ControlMask, XK_7, &toggleview, 1<<6 ), 87 | Key( MODKEY|ShiftMask, XK_7, &tag, 1<<6 ), 88 | Key( MODKEY|ControlMask|ShiftMask, XK_7, &toggletag, 1<<6 ), 89 | Key( MODKEY, XK_8, &view, 1<<7 ), 90 | Key( MODKEY|ControlMask, XK_8, &toggleview, 1<<7 ), 91 | Key( MODKEY|ShiftMask, XK_8, &tag, 1<<7 ), 92 | Key( MODKEY|ControlMask|ShiftMask, XK_8, &toggletag, 1<<7 ), 93 | Key( MODKEY, XK_9, &view, 1<<8 ), 94 | Key( MODKEY|ControlMask, XK_9, &toggleview, 1<<8 ), 95 | Key( MODKEY|ShiftMask, XK_9, &tag, 1<<8 ), 96 | Key( MODKEY|ControlMask|ShiftMask, XK_9, &toggletag, 1<<8 ) 97 | ]; 98 | } 99 | 100 | void addEvent(uint keyMod, const(int) keySymbol, void function(const Arg* a) dg) 101 | { 102 | this.keys ~= Key(keyMod, keySymbol, dg); 103 | } 104 | 105 | void addEvent(T)(uint keyMod, const(int) keySymbol, void function(const Arg* a) dg, T arg) 106 | { 107 | this.keys ~= Key(keyMod, keySymbol, dg, arg); 108 | } 109 | 110 | void listen(XEvent *e) 111 | { 112 | this.keypress(e); 113 | } 114 | 115 | void keypress(XEvent *e) 116 | { 117 | uint i; 118 | KeySym keysym; 119 | XKeyEvent *ev; 120 | 121 | ev = &e.xkey; 122 | keysym = XKeycodeToKeysym(AppDisplay.instance().dpy, cast(KeyCode)ev.keycode, 0); 123 | 124 | foreach(ref const key; keys) { 125 | if(keysym == key.keysym 126 | && CLEANMASK(key.mod) == CLEANMASK(ev.state) 127 | && key.func) { 128 | key.func( &(key.arg) ); 129 | } 130 | } 131 | } 132 | 133 | void grabkeys() 134 | { 135 | updatenumlockmask(); 136 | { 137 | uint i, j; 138 | uint[] modifiers = [ 0, LockMask, numlockmask, numlockmask|LockMask ]; 139 | KeyCode code; 140 | 141 | XUngrabKey(AppDisplay.instance().dpy, AnyKey, AnyModifier, rootWin); 142 | foreach(ref const key; keys) { 143 | code = XKeysymToKeycode(AppDisplay.instance().dpy, key.keysym); 144 | if(code) { 145 | foreach(ref const mod; modifiers) { 146 | XGrabKey(AppDisplay.instance().dpy, code, key.mod | mod, rootWin, 147 | True, GrabModeAsync, GrabModeAsync); 148 | } 149 | } 150 | } 151 | } 152 | } 153 | 154 | Key[] getKeys() 155 | { 156 | return this.keys; 157 | } 158 | } -------------------------------------------------------------------------------- /src/events/mouse.d: -------------------------------------------------------------------------------- 1 | module events.mouse; 2 | 3 | 4 | import events.interfaces; 5 | import kernel; 6 | import old; 7 | import config; 8 | import types; 9 | import cboxapp; 10 | import gui.cursor; 11 | import monitor; 12 | 13 | import x11.X; 14 | import x11.Xlib; 15 | import x11.keysymdef; 16 | import x11.Xutil; 17 | import x11.Xatom; 18 | 19 | import std.stdio; 20 | import std.math; 21 | import std.algorithm; 22 | 23 | enum BUTTONMASK = ButtonPressMask | ButtonReleaseMask; 24 | enum MOUSEMASK = ButtonPressMask | ButtonReleaseMask | PointerMotionMask; 25 | 26 | struct Button 27 | { 28 | uint click; 29 | uint mask; 30 | uint button; 31 | 32 | void function(const Arg* a) func; 33 | const Arg arg; 34 | 35 | this(uint click, uint mask, uint button, void function(const Arg* a) func) { 36 | this(click, mask, button, func, 0); 37 | } 38 | this(T)(uint click, uint mask, uint button, void function(const Arg* a) func, T arg) { 39 | this.click = click; 40 | this.mask = mask; 41 | this.button = button; 42 | this.func = func; 43 | this.arg = makeArg(arg); 44 | } 45 | }; 46 | 47 | class MouseEvents : EventInterface 48 | { 49 | Button[] buttons; 50 | 51 | this() 52 | { 53 | buttons = [ 54 | /* click event mask button function argument */ 55 | Button( ClkWinTitle, 0, Button2, &zoom ), 56 | //Button( ClkStatusText, 0, Button2, &spawn, termcmd ), // &termcmd 57 | Button( ClkClientWin, MODKEY, Button1, &movemouse ), 58 | Button( ClkClientWin, MODKEY, Button2, &togglefloating ), 59 | Button( ClkClientWin, MODKEY, Button3, &resizemouse ), 60 | Button( ClkTagBar, 0, Button1, &view ), 61 | Button( ClkTagBar, 0, Button3, &toggleview ), 62 | Button( ClkTagBar, MODKEY, Button1, &tag ), 63 | Button( ClkTagBar, MODKEY, Button3, &toggletag ) 64 | ]; 65 | } 66 | 67 | Button[] getButtons() 68 | { 69 | return this.buttons; 70 | } 71 | 72 | void addEvent() 73 | { 74 | 75 | } 76 | 77 | void listen(XEvent *e) 78 | { 79 | this.buttonpress(e); 80 | } 81 | 82 | void grabbuttons(Client *c, bool focused) 83 | { 84 | updatenumlockmask(); 85 | uint i, j; 86 | uint[] modifiers = [ 0, LockMask, numlockmask, numlockmask|LockMask ]; 87 | XUngrabButton(AppDisplay.instance().dpy, AnyButton, AnyModifier, c.win); 88 | 89 | if(focused) { 90 | foreach(ref const but; this.buttons) { 91 | if(but.click == ClkClientWin) { 92 | foreach(ref const mod; modifiers) { 93 | XGrabButton(AppDisplay.instance().dpy, but.button, 94 | but.mask | mod, 95 | c.win, false, BUTTONMASK, 96 | GrabModeAsync, GrabModeSync, 97 | cast(ulong)None, cast(ulong)None); 98 | } 99 | } 100 | } 101 | } else { 102 | XGrabButton(AppDisplay.instance().dpy, AnyButton, AnyModifier, c.win, false, 103 | BUTTONMASK, GrabModeAsync, GrabModeSync, None, None); 104 | } 105 | } 106 | 107 | void buttonpress(XEvent *e) 108 | { 109 | uint i, x, click; 110 | auto arg = Arg(0); 111 | Client *c; 112 | Monitor *m; 113 | XButtonPressedEvent *ev = &e.xbutton; 114 | 115 | click = ClkRootWin; 116 | 117 | /* focus monitor if necessary */ 118 | m = cast(Monitor*)wintomon(ev.window); 119 | 120 | if(ev.window == selmon.barwin) { 121 | 122 | i = x = 0; 123 | do { 124 | x += TEXTW(tags[i]); 125 | } while(ev.x >= x && ++i < LENGTH(tags)); 126 | 127 | if(i < LENGTH(tags)) { 128 | click = ClkTagBar; 129 | arg.ui = 1 << i; 130 | } else if(ev.x < x + blw) 131 | click = ClkLtSymbol; 132 | else if(ev.x > selmon.ww - TEXTW(stext)) 133 | click = ClkStatusText; 134 | else 135 | click = ClkWinTitle; 136 | } else { 137 | c = wintoclient(ev.window); 138 | if(c !is null) { 139 | focus(c); 140 | click = ClkClientWin; 141 | } 142 | } 143 | 144 | foreach(ref const but; buttons) { 145 | if(click == but.click && 146 | but.func !is null && 147 | but.button == ev.button && 148 | CLEANMASK(but.mask) == CLEANMASK(ev.state)) { 149 | but.func(click == ClkTagBar && but.arg.i == 0 ? &arg : &but.arg); 150 | } 151 | } 152 | } 153 | } 154 | 155 | void movemouse(const Arg *arg) 156 | { 157 | int x, y, ocx, ocy, nx, ny; 158 | Client *c; 159 | Monitor *m; 160 | XEvent ev; 161 | Time lasttime = 0; 162 | 163 | c = selmon.sel; 164 | 165 | if(!c) { 166 | return; 167 | } 168 | 169 | if(c.isfullscreen) /* no support moving fullscreen windows by mouse */ 170 | return; 171 | 172 | restack(selmon); 173 | ocx = c.x; 174 | ocy = c.y; 175 | if(XGrabPointer(AppDisplay.instance().dpy, 176 | rootWin, 177 | false, 178 | MOUSEMASK, 179 | GrabModeAsync, 180 | GrabModeAsync, 181 | None, 182 | cursor[CurMove].cursor, 183 | CurrentTime) != GrabSuccess) { 184 | return; 185 | } 186 | 187 | if(!getrootptr(&x, &y)) { 188 | return; 189 | } 190 | 191 | do { 192 | XMaskEvent(AppDisplay.instance().dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); 193 | switch(ev.type) { 194 | case ConfigureRequest: 195 | case Expose: 196 | case MapRequest: 197 | writeln(ev.type); 198 | //handler[ev.type](&ev); 199 | break; 200 | case MotionNotify: 201 | 202 | if ((ev.xmotion.time - lasttime) <= (1000 / 60)) 203 | continue; 204 | lasttime = ev.xmotion.time; 205 | 206 | nx = ocx + (ev.xmotion.x - x); 207 | ny = ocy + (ev.xmotion.y - y); 208 | if(nx >= selmon.wx && nx <= selmon.wx + selmon.ww 209 | && ny >= selmon.wy && ny <= selmon.wy + selmon.wh) { 210 | if(abs(selmon.wx - nx) < snap) 211 | nx = selmon.wx; 212 | else if(abs((selmon.wx + selmon.ww) - (nx + WIDTH(c))) < snap) 213 | nx = selmon.wx + selmon.ww - WIDTH(c); 214 | if(abs(selmon.wy - ny) < snap) 215 | ny = selmon.wy; 216 | else if(abs((selmon.wy + selmon.wh) - (ny + HEIGHT(c))) < snap) 217 | ny = selmon.wy + selmon.wh - HEIGHT(c); 218 | if(!c.isfloating && selmon.lt[selmon.sellt].arrange 219 | && (abs(nx - c.x) > snap || abs(ny - c.y) > snap)) 220 | togglefloating(null); 221 | } 222 | 223 | if(!selmon.lt[selmon.sellt].arrange || c.isfloating) 224 | resize(c, nx, ny, c.w, c.h, true); 225 | break; 226 | default : 227 | break; 228 | } 229 | } while(ev.type != ButtonRelease); 230 | XUngrabPointer(AppDisplay.instance().dpy, CurrentTime); 231 | 232 | if((m = recttomon(c.x, c.y, c.w, c.h)) != selmon) { 233 | sendmon(c, m); 234 | selmon = m; 235 | focus(null); 236 | } 237 | } 238 | 239 | void resizemouse(const Arg *arg) 240 | { 241 | int ocx, ocy, nw, nh; 242 | Client *c; 243 | Monitor *m; 244 | XEvent ev; 245 | Time lasttime = 0; 246 | 247 | c = selmon.sel; 248 | if(!c) { 249 | return; 250 | } 251 | 252 | if(c.isfullscreen) /* no support resizing fullscreen windows by mouse */ 253 | return; 254 | 255 | restack(selmon); 256 | ocx = c.x; 257 | ocy = c.y; 258 | 259 | if(XGrabPointer(AppDisplay.instance().dpy, rootWin, false, MOUSEMASK, GrabModeAsync, GrabModeAsync, 260 | None, cursor[CurResize].cursor, CurrentTime) != GrabSuccess) 261 | return; 262 | 263 | XWarpPointer(AppDisplay.instance().dpy, None, c.win, 0, 0, 0, 0, c.w + c.bw - 1, c.h + c.bw - 1); 264 | do { 265 | XMaskEvent(AppDisplay.instance().dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); 266 | switch(ev.type) { 267 | case ConfigureRequest: 268 | case Expose: 269 | case MapRequest: 270 | handler[ev.type](&ev); 271 | break; 272 | case MotionNotify: 273 | if ((ev.xmotion.time - lasttime) <= (1000 / 60)) 274 | continue; 275 | lasttime = ev.xmotion.time; 276 | 277 | nw = max(ev.xmotion.x - ocx - 2 * c.bw + 1, 1); 278 | nh = max(ev.xmotion.y - ocy - 2 * c.bw + 1, 1); 279 | if(c.mon.wx + nw >= selmon.wx && c.mon.wx + nw <= selmon.wx + selmon.ww 280 | && c.mon.wy + nh >= selmon.wy && c.mon.wy + nh <= selmon.wy + selmon.wh) { 281 | if(!c.isfloating && selmon.lt[selmon.sellt].arrange 282 | && (abs(nw - c.w) > snap || abs(nh - c.h) > snap)) 283 | togglefloating(null); 284 | } 285 | if(!selmon.lt[selmon.sellt].arrange || c.isfloating) 286 | resize(c, c.x, c.y, nw, nh, true); 287 | break; 288 | default : 289 | break; 290 | } 291 | } while(ev.type != ButtonRelease); 292 | XWarpPointer(AppDisplay.instance().dpy, None, c.win, 0, 0, 0, 0, c.w + c.bw - 1, c.h + c.bw - 1); 293 | XUngrabPointer(AppDisplay.instance().dpy, CurrentTime); 294 | while(XCheckMaskEvent(AppDisplay.instance().dpy, EnterWindowMask, &ev)) {} 295 | m = recttomon(c.x, c.y, c.w, c.h); 296 | if(m != selmon) { 297 | sendmon(c, m); 298 | selmon = m; 299 | focus(null); 300 | } 301 | } -------------------------------------------------------------------------------- /src/gui/bar.d: -------------------------------------------------------------------------------- 1 | module gui.bar; 2 | 3 | import cboxapp; 4 | import types; 5 | import gui.cursor; 6 | import window; 7 | import old; 8 | import legacy; 9 | import utils; 10 | import config; 11 | import kernel; 12 | import helper.x11; 13 | import monitor; 14 | import std.stdio; 15 | import theme.manager; 16 | 17 | import x11.X; 18 | import x11.Xlib; 19 | import x11.keysymdef; 20 | import x11.Xutil; 21 | import x11.Xatom; 22 | 23 | void drawbar(Monitor *m) 24 | { 25 | uint occ = 0, urg = 0; 26 | 27 | foreach(c; m.clients.range!"next") { 28 | occ |= c.tags; 29 | if(c.isurgent) { 30 | urg |= c.tags; 31 | } 32 | } 33 | 34 | int x = 0, w; 35 | 36 | ClrScheme sel = ThemeManager.instance().getScheme(SchemeSel); 37 | ClrScheme norm = ThemeManager.instance().getScheme(SchemeNorm); 38 | 39 | foreach(i, tag; tags) { 40 | w = TEXTW(tag); 41 | drw.setscheme((m.tagset[m.seltags] & (1 << i)) 42 | ? &sel 43 | : &norm); 44 | drw.text(x, 0, w, bh, tag, urg & 1 << i); 45 | drw.rect(x, 0, w, bh, m == selmon && selmon.sel && selmon.sel.tags & 1 << i, 46 | occ & 1 << i, urg & 1 << i); 47 | x += w; 48 | } 49 | 50 | int xx = x; 51 | 52 | if(m == selmon) { /* status is only drawn on selected monitor */ 53 | w = TEXTW(stext); 54 | x = m.mw - w; 55 | 56 | if(x < xx) { 57 | x = xx; 58 | w = m.mw - xx; 59 | } 60 | 61 | drw.setscheme(&norm); 62 | drw.text(x, 0, w, bh, stext, 0); 63 | } else { 64 | x = m.mw; 65 | } 66 | 67 | if((w = x - xx) > bh) { 68 | x = xx; 69 | if(m.sel) { 70 | drw.setscheme(m == selmon ? &sel : &norm); 71 | drw.text(x, 0, w, bh, m.sel.name, 0); 72 | drw.rect(x, 0, w, bh, m.sel.isfixed, m.sel.isfloating, 0); 73 | } else { 74 | drw.setscheme(&norm); 75 | drw.text(x, 0, w, bh, null, 0); 76 | } 77 | } 78 | 79 | drw.map(m.barwin, 0, 0, m.mw, bh); 80 | } 81 | 82 | void drawbars() 83 | { 84 | foreach(m; mons.range) { 85 | drawbar(m); 86 | } 87 | } 88 | 89 | void updatebars() 90 | { 91 | Client *c; 92 | XSetWindowAttributes wa = { 93 | override_redirect : True, 94 | background_pixmap : ParentRelative, 95 | event_mask : ButtonPressMask|ExposureMask 96 | }; 97 | 98 | foreach(m; mons.range) { 99 | if (m.barwin) 100 | continue; 101 | 102 | m.barwin = XCreateWindow( 103 | AppDisplay.instance().dpy, 104 | rootWin, 105 | m.wx, 106 | m.by, 107 | m.ww, 108 | bh, 109 | 0, 110 | DefaultDepth(AppDisplay.instance().dpy, screen), 111 | CopyFromParent, 112 | DefaultVisual(AppDisplay.instance().dpy, screen), 113 | CWOverrideRedirect|CWBackPixmap|CWEventMask, 114 | &wa 115 | ); 116 | 117 | XDefineCursor(AppDisplay.instance().dpy, m.barwin, cursor[CurNormal].cursor); 118 | 119 | //sendevent(c, XInternAtom(AppDisplay.instance().dpy, cast(char*)("_NET_WM_STATE_ABOVE"), false)); 120 | XMapRaised(AppDisplay.instance().dpy, m.barwin); 121 | //sendevent(c, XInternAtom(AppDisplay.instance().dpy, cast(char*)("_NET_WM_STATE_ABOVE"), false)); 122 | } 123 | } 124 | 125 | void updatebarpos(Monitor *m) 126 | { 127 | 128 | m.wy = m.my; 129 | m.wh = m.mh; 130 | if(m.showbar) { 131 | m.wh -= bh; 132 | m.by = m.topbar ? m.wy : m.wy + m.wh; 133 | m.wy = m.topbar ? m.wy + bh : m.wy; 134 | } else 135 | m.by = -bh; 136 | } 137 | 138 | void togglebar(const Arg *arg) 139 | { 140 | selmon.showbar = !selmon.showbar; 141 | updatebarpos(selmon); 142 | XMoveResizeWindow(AppDisplay.instance().dpy, selmon.barwin, selmon.wx, selmon.by, selmon.ww, bh); 143 | arrange(selmon); 144 | } 145 | 146 | void updatestatus() 147 | { 148 | if(!X11Helper.gettextprop(rootWin, XA_WM_NAME, stext)) { 149 | stext = "ddwm-"~VERSION; 150 | } 151 | drawbar(selmon); 152 | } 153 | 154 | 155 | -------------------------------------------------------------------------------- /src/gui/cursor.d: -------------------------------------------------------------------------------- 1 | module gui.cursor; 2 | 3 | import utils; 4 | import cboxapp; 5 | import x11.X; 6 | import x11.Xlib; 7 | import x11.keysymdef; 8 | import x11.Xutil; 9 | import x11.Xatom; 10 | import std.c.stdlib; 11 | static Cur*[CurLast] cursor; 12 | 13 | enum 14 | { 15 | CurNormal, 16 | CurResize, 17 | CurMove, 18 | CurLast 19 | }; /* cursor */ 20 | 21 | enum CursorFont : int 22 | { 23 | XC_num_glyphs = 154, 24 | XC_X_cursor = 0, 25 | XC_arrow = 2, 26 | XC_based_arrow_down = 4, 27 | XC_based_arrow_up = 6, 28 | XC_boat = 8, 29 | XC_bogosity = 10, 30 | XC_bottom_left_corner = 12, 31 | XC_bottom_right_corner = 14, 32 | XC_bottom_side = 16, 33 | XC_bottom_tee = 18, 34 | XC_box_spiral = 20, 35 | XC_center_ptr = 22, 36 | XC_circle = 24, 37 | XC_clock = 26, 38 | XC_coffee_mug = 28, 39 | XC_cross = 30, 40 | XC_cross_reverse = 32, 41 | XC_crosshair = 34, 42 | XC_diamond_cross = 36, 43 | XC_dot = 38, 44 | XC_dotbox = 40, 45 | XC_double_arrow = 42, 46 | XC_draft_large = 44, 47 | XC_draft_small = 46, 48 | XC_draped_box = 48, 49 | XC_exchange = 50, 50 | XC_fleur = 52, 51 | XC_gobbler = 54, 52 | XC_gumby = 56, 53 | XC_hand1 = 58, 54 | XC_hand2 = 60, 55 | XC_heart = 62, 56 | XC_icon = 64, 57 | XC_iron_cross = 66, 58 | XC_left_ptr = 68, 59 | XC_left_side = 70, 60 | XC_left_tee = 72, 61 | XC_leftbutton = 74, 62 | XC_ll_angle = 76, 63 | XC_lr_angle = 78, 64 | XC_man = 80, 65 | XC_middlebutton = 82, 66 | XC_mouse = 84, 67 | XC_pencil = 86, 68 | XC_pirate = 88, 69 | XC_plus = 90, 70 | XC_question_arrow = 92, 71 | XC_right_ptr = 94, 72 | XC_right_side = 96, 73 | XC_right_tee = 98, 74 | XC_rightbutton = 100, 75 | XC_rtl_logo = 102, 76 | XC_sailboat = 104, 77 | XC_sb_down_arrow = 106, 78 | XC_sb_h_double_arrow = 108, 79 | XC_sb_left_arrow = 110, 80 | XC_sb_right_arrow = 112, 81 | XC_sb_up_arrow = 114, 82 | XC_sb_v_double_arrow = 116, 83 | XC_shuttle = 118, 84 | XC_sizing = 120, 85 | XC_spider = 122, 86 | XC_spraycan = 124, 87 | XC_star = 126, 88 | XC_target = 128, 89 | XC_tcross = 130, 90 | XC_top_left_arrow = 132, 91 | XC_top_left_corner = 134, 92 | XC_top_right_corner = 136, 93 | XC_top_side = 138, 94 | XC_top_tee = 140, 95 | XC_trek = 142, 96 | XC_ul_angle = 144, 97 | XC_umbrella = 146, 98 | XC_ur_angle = 148, 99 | XC_watch = 150, 100 | XC_xterm = 152 101 | } 102 | 103 | /** 104 | * Wraps a X cursor. 105 | */ 106 | struct Cur 107 | { 108 | Cursor cursor; 109 | Display* dpy; 110 | 111 | /** 112 | * Ctor constructing a Cursor with a given display object. 113 | * Params: 114 | * dpy= Display object 115 | * shape= X cursor shape 116 | */ 117 | this(Display* dpy, CursorFont shape) 118 | { 119 | if(dpy is null) { 120 | // lout(__FUNCTION__~"\n\t--> NULL Display* parm"); 121 | exit(EXIT_FAILURE); 122 | } 123 | this.dpy = dpy; 124 | this.cursor = XCreateFontCursor(this.dpy, shape); 125 | } 126 | 127 | private void destroy() 128 | { 129 | XFreeCursor(this.dpy, this.cursor); 130 | } 131 | 132 | static void free(Cur* c) 133 | { 134 | if(c is null) { 135 | // lout(__FUNCTION__~"\n\t--> NULL Cur* parm"); 136 | exit(EXIT_FAILURE); 137 | } 138 | c.destroy(); 139 | DGC.free(c); 140 | } 141 | 142 | } -------------------------------------------------------------------------------- /src/gui/drawer.d: -------------------------------------------------------------------------------- 1 | module drawer; 2 | 3 | -------------------------------------------------------------------------------- /src/gui/font.d: -------------------------------------------------------------------------------- 1 | module gui.font; 2 | 3 | import x11.X; 4 | import x11.Xlib; 5 | import x11.Xutil; 6 | import std.c.stdlib; 7 | import std.string; 8 | import std.algorithm; 9 | 10 | import cboxapp; 11 | import types; 12 | 13 | /** 14 | * Font object to encapsulate the X font. 15 | */ 16 | struct Fnt 17 | { 18 | int ascent; /// Ascent of the font 19 | int descent;/// Descent of the font 20 | uint h; /// Height of the font. This equates to ascent + descent. 21 | XFontSet set; // Font set to use 22 | XFontStruct *xfont; /// The X font we're covering. 23 | Display* dpy; 24 | 25 | /** 26 | * Ctor. Creates a Fnt object wrapping the specified font for a given display. 27 | * Params: 28 | * dpy= X display 29 | * fontname= Name of the font to wrap (X font name) 30 | * Example: 31 | * --- 32 | * auto f = Fnt(display, "-*-terminus-medium-r-*-*-16-*-*-*-*-*-*-*"); 33 | * --- 34 | */ 35 | this(Display *dpy, in string fontname) 36 | { 37 | if(AppDisplay.instance().dpy is null) { 38 | exit(EXIT_FAILURE); 39 | } 40 | 41 | this.dpy = dpy; 42 | char *def; 43 | char **missing; 44 | int n; 45 | 46 | this.set = XCreateFontSet(AppDisplay.instance().dpy, cast(char*)fontname.toStringz, &missing, &n, &def); 47 | 48 | if(missing) { 49 | while(n--) { 50 | //lout("drw: missing fontset: %s", missing[n].fromStringz); 51 | } 52 | XFreeStringList(missing); 53 | } 54 | 55 | if(this.set) { 56 | XFontStruct **xfonts; 57 | char **font_names; 58 | XExtentsOfFontSet(this.set); 59 | n = XFontsOfFontSet(this.set, &xfonts, &font_names); 60 | while(n--) { 61 | this.ascent = max(this.ascent, (*xfonts).ascent); 62 | this.descent = max(this.descent,(*xfonts).descent); 63 | xfonts++; 64 | } 65 | } 66 | 67 | else { 68 | this.xfont = XLoadQueryFont(AppDisplay.instance().dpy, cast(char*)(fontname.toStringz)); 69 | if(this.xfont is null) { 70 | this.xfont = XLoadQueryFont(AppDisplay.instance().dpy, cast(char*)("fixed".toStringz)); 71 | if(this.xfont is null) { 72 | //lout("error, cannot load font: %s", fontname); 73 | exit(EXIT_FAILURE); 74 | } 75 | } 76 | this.ascent = this.xfont.ascent; 77 | this.descent = this.xfont.descent; 78 | } 79 | 80 | this.h = this.ascent + this.descent; 81 | } 82 | 83 | /** 84 | * Destroy the X resources for this font. 85 | */ 86 | private void destroy(Display* dpy) 87 | { 88 | if(this.set) { 89 | XFreeFontSet(dpy, this.set); 90 | } 91 | else if(this.xfont) { 92 | XFreeFont(dpy, this.xfont); 93 | } 94 | this.set = null; 95 | this.xfont = null; 96 | } 97 | 98 | /** 99 | * Free the given font object associated with a display. This will release 100 | * the GC allocated memory. 101 | * Params: 102 | * fnt= Pointer to the font to destroy. 103 | * dpy= Display associated with the font to destroy 104 | * Example: 105 | * --- 106 | * auto f = Fnt(display, "-*-terminus-medium-r-*-*-16-*-*-*-*-*-*-*"); 107 | * 108 | * Fnt.free(f) 109 | * --- 110 | */ 111 | static void free(Display* dpy, Fnt* fnt) 112 | { 113 | fnt.destroy(AppDisplay.instance().dpy); 114 | DGC.free(fnt); 115 | } 116 | 117 | /** 118 | * Get the font extents for a given string. 119 | * Params: 120 | * text= Text to get the extents 121 | * tex= Extents struct to fill in with the font information 122 | */ 123 | void getexts(in string text, Extnts *tex) 124 | { 125 | XRectangle r; 126 | 127 | if(text.length == 0) { 128 | return; 129 | } 130 | if(this.set) { 131 | XmbTextExtents(this.set, cast(char*)text.ptr, cast(int)text.length, null, &r); 132 | tex.w = r.width; 133 | tex.h = r.height; 134 | } 135 | else { 136 | tex.h = this.ascent + this.descent; 137 | tex.w = XTextWidth(this.xfont, cast(char*)text.ptr, cast(int)text.length); 138 | } 139 | } 140 | 141 | /** 142 | * Get the rendered width of a string for the wrapped font. 143 | * Params: 144 | * text= Text to get the width for 145 | * Returns: 146 | * Width of the text for the wrapped font. 147 | */ 148 | uint getexts_width(in string text) 149 | { 150 | Extnts tex; 151 | 152 | this.getexts(text, &tex); 153 | return tex.w; 154 | } 155 | } -------------------------------------------------------------------------------- /src/helper/process.d: -------------------------------------------------------------------------------- 1 | module helper.process; 2 | 3 | import std.conv; 4 | import std.process; 5 | import std.string; 6 | import std.stdio; 7 | import types; 8 | import config; 9 | import utils; 10 | 11 | /** 12 | * Runs command 13 | **/ 14 | void spawn(const Arg *arg) 15 | { 16 | import std.variant; 17 | Variant v = arg.val; 18 | const(string[]) args = arg.s; 19 | 20 | if(args[0] == dmenucmd[0]) { 21 | dmenumon[0] = cast(char)('0' + selmon.num); 22 | } 23 | 24 | try { 25 | auto pid = spawnProcess(args); 26 | } catch { 27 | die("Failed to spawn '%s'", args); 28 | } 29 | } 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/helper/x11.d: -------------------------------------------------------------------------------- 1 | module helper.x11; 2 | 3 | import cboxapp; 4 | import window; 5 | 6 | import std.conv; 7 | import std.string; 8 | import x11.X; 9 | import x11.Xlib; 10 | import x11.keysymdef; 11 | import x11.Xutil; 12 | import x11.Xatom; 13 | 14 | class X11Helper 15 | { 16 | static bool gettextprop(Window w, Atom atom, out string text) 17 | { 18 | static immutable size_t MAX_TEXT_LENGTH = 256; 19 | XTextProperty name; 20 | XGetTextProperty(AppDisplay.instance().dpy, w, &name, atom); 21 | 22 | if(!name.nitems) 23 | return false; 24 | 25 | if(name.encoding == XA_STRING) { 26 | text = (cast(char*)(name.value)).fromStringz.to!string; 27 | } else { 28 | char **list = null; 29 | int n; 30 | if(XmbTextPropertyToTextList(AppDisplay.instance().dpy, &name, &list, &n) >= XErrorCode.Success && 31 | n > 0 && 32 | *list) { 33 | text = (*list).fromStringz.to!string; 34 | XFreeStringList(list); 35 | } 36 | } 37 | 38 | XFree(name.value); 39 | return true; 40 | } 41 | } 42 | 43 | -------------------------------------------------------------------------------- /src/kernel.d: -------------------------------------------------------------------------------- 1 | module kernel; 2 | 3 | import std.c.locale; 4 | import std.c.string; 5 | import std.c.stdlib; 6 | 7 | import std.stdio; 8 | import std.string; 9 | import std.algorithm; 10 | import std.conv; 11 | import std.process; 12 | import std.traits; 13 | import helper.process; 14 | 15 | import core.sys.posix.signal; 16 | import core.sys.posix.sys.wait; 17 | import core.sys.posix.unistd; 18 | import core.memory; 19 | 20 | import x11.X; 21 | import x11.Xlib; 22 | import x11.keysymdef; 23 | import x11.Xutil; 24 | import x11.Xatom; 25 | 26 | import cboxapp; 27 | import types; 28 | import utils; 29 | import legacy; 30 | import old; 31 | import config; 32 | import events.handler; 33 | import events.keyboard; 34 | import events.mouse; 35 | import window; 36 | import helper.x11; 37 | import gui.cursor; 38 | import gui.font; 39 | import gui.bar; 40 | import theme.layout; 41 | import theme.manager; 42 | import monitor; 43 | 44 | static Drw *drw; 45 | static Fnt *fnt; 46 | static Key[] keys; 47 | 48 | /* button definitions */ 49 | /* click can be ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */ 50 | static Button[] buttons; 51 | static void function(XEvent*)[LASTEvent] handler; 52 | 53 | 54 | EventHandler eventManager; 55 | KeyboardEvents keyboardEventHandler; 56 | MouseEvents mouseEventHandler; 57 | WindowManager windowManager; 58 | 59 | static Atom[WMLast] wmatom; 60 | static Atom[NetLast] netatom; 61 | 62 | void quit(const Arg *arg) 63 | { 64 | AppDisplay.instance().running = false; 65 | } 66 | 67 | class Kernel 68 | { 69 | this() 70 | { 71 | keyboardEventHandler = new KeyboardEvents(); 72 | keyboardEventHandler.addEvent(MODKEY|ShiftMask, XK_q, &quit); 73 | keyboardEventHandler.addEvent(MODKEY, XK_p, &spawn, dmenucmd); 74 | 75 | mouseEventHandler = new MouseEvents(); 76 | eventManager = new EventHandler(keyboardEventHandler, mouseEventHandler); 77 | windowManager = new WindowManager(); 78 | 79 | wmatom = windowManager.getAllAtoms("WMLast"); 80 | netatom = windowManager.getAllAtoms("NetLast"); 81 | } 82 | 83 | int boot() 84 | { 85 | keys = keyboardEventHandler.getKeys(); 86 | buttons = mouseEventHandler.getButtons(); 87 | 88 | this.checkotherwm(); 89 | this.setup(); 90 | this.scan(); 91 | this.run(); 92 | this.close(); 93 | 94 | return 0; 95 | } 96 | 97 | void checkotherwm() 98 | { 99 | xerrorxlib = XSetErrorHandler(&xerrorstart); 100 | /* this causes an error if some other window manager is running */ 101 | XSelectInput(AppDisplay.instance().dpy, DefaultRootWindow(AppDisplay.instance().dpy), SubstructureRedirectMask); 102 | XSync(AppDisplay.instance().dpy, false); 103 | XSetErrorHandler(&xerror); 104 | XSync(AppDisplay.instance().dpy, false); 105 | } 106 | 107 | void setup() 108 | { 109 | XSetWindowAttributes wa; 110 | 111 | /* clean up any zombies immediately */ 112 | sigchld(0); 113 | 114 | /* init screen */ 115 | screen = DefaultScreen(AppDisplay.instance().dpy); 116 | rootWin = RootWindow(AppDisplay.instance().dpy, screen); 117 | 118 | fnt = new Fnt(AppDisplay.instance().dpy, font); 119 | sw = DisplayWidth(AppDisplay.instance().dpy, screen); 120 | sh = DisplayHeight(AppDisplay.instance().dpy, screen); 121 | bh = fnt.h + 2; 122 | 123 | drw = new Drw(AppDisplay.instance().dpy, screen, rootWin, sw, sh); 124 | drw.setfont(fnt); 125 | updategeom(); 126 | 127 | /* init cursors */ 128 | cursor[CurNormal] = new Cur(drw.dpy, CursorFont.XC_left_ptr); 129 | cursor[CurResize] = new Cur(drw.dpy, CursorFont.XC_sizing); 130 | cursor[CurMove] = new Cur(drw.dpy, CursorFont.XC_fleur); 131 | 132 | 133 | /* init bars */ 134 | updatebars(); 135 | updatestatus(); 136 | 137 | /* EWMH support per view */ 138 | XChangeProperty( 139 | AppDisplay.instance().dpy, 140 | rootWin, 141 | windowManager.getAtom("NetLast",NetSupported), 142 | XA_ATOM, 32, 143 | PropModeReplace, 144 | cast(ubyte*) windowManager.getAllAtoms("NetLast"), 145 | NetLast 146 | ); 147 | 148 | XDeleteProperty( 149 | AppDisplay.instance().dpy, 150 | rootWin, 151 | windowManager.getAtom("NetLast", NetClientList) 152 | ); 153 | 154 | /* select for events */ 155 | wa.cursor = cursor[CurNormal].cursor; 156 | wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask|ButtonPressMask|PointerMotionMask 157 | |EnterWindowMask|LeaveWindowMask|StructureNotifyMask|PropertyChangeMask; 158 | 159 | XChangeWindowAttributes(AppDisplay.instance().dpy, rootWin, CWEventMask|CWCursor, &wa); 160 | XSelectInput(AppDisplay.instance().dpy, rootWin, wa.event_mask); 161 | keyboardEventHandler.grabkeys(); 162 | 163 | focus(null); 164 | } 165 | 166 | void scan() 167 | { 168 | uint i, num; 169 | Window d1, d2; 170 | Window* wins = null; 171 | XWindowAttributes wa; 172 | 173 | if(XQueryTree(AppDisplay.instance().dpy, rootWin, &d1, &d2, &wins, &num)) { 174 | for(i = 0; i < num; i++) { 175 | if(!XGetWindowAttributes(AppDisplay.instance().dpy, wins[i], &wa) 176 | || wa.override_redirect || XGetTransientForHint(AppDisplay.instance().dpy, wins[i], &d1)) 177 | continue; 178 | if(wa.map_state == IsViewable || this.getstate(wins[i]) == IconicState) 179 | windowManager.manage(wins[i], &wa); 180 | } 181 | for(i = 0; i < num; i++) { /* now the transients */ 182 | if(!XGetWindowAttributes(AppDisplay.instance().dpy, wins[i], &wa)) 183 | continue; 184 | if(XGetTransientForHint(AppDisplay.instance().dpy, wins[i], &d1) 185 | && (wa.map_state == IsViewable || this.getstate(wins[i]) == IconicState)) 186 | windowManager.manage(wins[i], &wa); 187 | } 188 | if(wins) 189 | XFree(wins); 190 | } 191 | } 192 | 193 | void run() 194 | { 195 | extern(C) __gshared XEvent ev; 196 | 197 | /* main event loop */ 198 | XSync(AppDisplay.instance().dpy, false); 199 | while(AppDisplay.instance().running && !XNextEvent(AppDisplay.instance().dpy, &ev)) { 200 | eventManager.listen(&ev); 201 | } 202 | } 203 | 204 | void cleanup() 205 | { 206 | auto a = Arg(-1); 207 | Layout foo = { "", null }; 208 | 209 | view(&a); 210 | selmon.lt[selmon.sellt] = &foo; 211 | foreach(m; mons.range) { 212 | while(m.stack) { 213 | unmanage(m.stack, false); 214 | } 215 | } 216 | XUngrabKey(AppDisplay.instance().dpy, AnyKey, AnyModifier, rootWin); 217 | while(mons) { 218 | cleanupmon(mons); 219 | } 220 | 221 | Cur.free(cursor[CurNormal]); 222 | Cur.free(cursor[CurResize]); 223 | Cur.free(cursor[CurMove]); 224 | Fnt.free(AppDisplay.instance().dpy, fnt); 225 | Clr.free(ThemeManager.instance().getScheme(SchemeNorm).border); 226 | Clr.free(ThemeManager.instance().getScheme(SchemeNorm).bg); 227 | Clr.free(ThemeManager.instance().getScheme(SchemeNorm).fg); 228 | Clr.free(ThemeManager.instance().getScheme(SchemeSel).border); 229 | Clr.free(ThemeManager.instance().getScheme(SchemeSel).bg); 230 | Clr.free(ThemeManager.instance().getScheme(SchemeSel).fg); 231 | 232 | 233 | Drw.free(drw); 234 | XSync(AppDisplay.instance().dpy, false); 235 | XSetInputFocus(AppDisplay.instance().dpy, PointerRoot, RevertToPointerRoot, CurrentTime); 236 | XDeleteProperty(AppDisplay.instance().dpy, rootWin, netatom[NetActiveWindow]); 237 | } 238 | 239 | void close() 240 | { 241 | this.cleanup(); 242 | XCloseDisplay(AppDisplay.instance().dpy); 243 | } 244 | 245 | /** 246 | * Get window State 247 | **/ 248 | long getstate(Window w) 249 | { 250 | int format; 251 | long result = -1; 252 | ubyte *p = null; 253 | ulong n, extra; 254 | Atom realVal; 255 | 256 | if(XGetWindowProperty(AppDisplay.instance().dpy, w, wmatom[WMState], 0L, 2L, false, wmatom[WMState], 257 | &realVal, &format, &n, &extra, cast(ubyte **)&p) != XErrorCode.Success) { 258 | return -1; 259 | } 260 | 261 | if(n != 0) { 262 | result = *p; 263 | } 264 | 265 | XFree(p); 266 | return result; 267 | } 268 | 269 | } 270 | -------------------------------------------------------------------------------- /src/legacy.d: -------------------------------------------------------------------------------- 1 | module legacy; 2 | 3 | import core.sys.posix.signal; 4 | import core.sys.posix.sys.wait; 5 | import core.sys.posix.unistd; 6 | import core.memory; 7 | import std.stdio; 8 | 9 | import x11.X; 10 | import x11.Xlib; 11 | import x11.keysymdef; 12 | import x11.Xutil; 13 | import x11.Xatom; 14 | 15 | import utils; 16 | 17 | extern(C) int xerrordummy(Display *dpy, XErrorEvent *ee) nothrow { 18 | return 0; 19 | } 20 | 21 | // From core.sys.posix.sys.wait because waitpid is not nothrow @nogc @system 22 | extern(C) pid_t waitpid(pid_t, int*, int) nothrow @nogc @system; 23 | 24 | // From core.stdc.stdio due to conflict with std.stdio over stderr 25 | extern(C) int close(int fd) @trusted; 26 | 27 | extern(C) void sigchldImpl(int unused) nothrow @nogc @system 28 | { 29 | // wait for all child processes to exit. 30 | while(0 < waitpid(-1, null, WNOHANG)) {} 31 | } 32 | 33 | extern(C) int xerrorstart(Display *dpy, XErrorEvent *ee) nothrow 34 | { 35 | die("cobox: another window manager is already running"); 36 | return -1; 37 | } 38 | 39 | extern(C) int xerror(Display *dpy, XErrorEvent *ee) nothrow { 40 | import x11.Xproto : 41 | X_SetInputFocus, X_PolyText8, X_PolyFillRectangle, X_PolySegment, 42 | X_ConfigureWindow, X_GrabButton, X_GrabKey, X_CopyArea; 43 | 44 | if(ee.error_code == XErrorCode.BadWindow 45 | || (ee.request_code == X_SetInputFocus && ee.error_code == XErrorCode.BadMatch) 46 | || (ee.request_code == X_PolyText8 && ee.error_code == XErrorCode.BadDrawable) 47 | || (ee.request_code == X_PolyFillRectangle && ee.error_code == XErrorCode.BadDrawable) 48 | || (ee.request_code == X_PolySegment && ee.error_code == XErrorCode.BadDrawable) 49 | || (ee.request_code == X_ConfigureWindow && ee.error_code == XErrorCode.BadMatch) 50 | || (ee.request_code == X_GrabButton && ee.error_code == XErrorCode.BadAccess) 51 | || (ee.request_code == X_GrabKey && ee.error_code == XErrorCode.BadAccess) 52 | || (ee.request_code == X_CopyArea && ee.error_code == XErrorCode.BadDrawable) 53 | ) { 54 | printf(">>>> XERROR: %d", ee.error_code); 55 | return 0; 56 | } 57 | 58 | printf("cobox: fatal error: request code=%d, error code=%d", ee.request_code, ee.error_code); 59 | 60 | return xerrorxlib(dpy, ee); 61 | } 62 | 63 | extern(C) static int function(Display*, XErrorEvent*) nothrow xerrorxlib; 64 | 65 | -------------------------------------------------------------------------------- /src/monitor.d: -------------------------------------------------------------------------------- 1 | module monitor; 2 | 3 | import std.stdio; 4 | import std.string; 5 | import std.algorithm; 6 | 7 | import x11.X; 8 | 9 | import cboxapp; 10 | import window; 11 | import theme.layout; 12 | import config; 13 | import types; 14 | import kernel; 15 | import theme.manager; 16 | import old; 17 | import utils; 18 | 19 | struct Monitor 20 | { 21 | string ltsymbol; 22 | float mfact; 23 | int nmaster; 24 | int num; 25 | int by; /* bar geometry */ 26 | int mx, my, mw, mh; /* screen size */ 27 | int wx, wy, ww, wh; /* window area */ 28 | 29 | uint seltags; 30 | uint sellt; 31 | uint[2] tagset; 32 | bool showbar; 33 | bool topbar; 34 | Client *clients; 35 | Client *sel; 36 | Client *stack; 37 | Monitor *next; 38 | Window barwin; 39 | const(Layout)*[2] lt; 40 | 41 | struct MonitorRange 42 | { 43 | Monitor* monitor; 44 | 45 | @property empty() 46 | { 47 | return monitor is null; 48 | } 49 | 50 | @property auto front() 51 | { 52 | return monitor; 53 | } 54 | 55 | auto popFront() 56 | { 57 | monitor = monitor.next; 58 | } 59 | 60 | int opApply(int delegate(Monitor*) dg) 61 | { 62 | int result = 0; 63 | while(!this.empty) 64 | { 65 | result = dg(this.front); 66 | if(result) 67 | { 68 | break; 69 | } 70 | this.popFront; 71 | } 72 | return result; 73 | } 74 | 75 | int opApply(int delegate(size_t, Monitor*) dg) 76 | { 77 | int result = 0; 78 | size_t ii = 0; 79 | while(!this.empty) 80 | { 81 | result = dg(ii++, this.front); 82 | if(result) 83 | { 84 | break; 85 | } 86 | this.popFront; 87 | } 88 | return result; 89 | } 90 | } 91 | } 92 | 93 | Monitor* recttomon(int x, int y, int w, int h) 94 | { 95 | auto r = selmon; 96 | int a, area = 0; 97 | 98 | foreach(m; mons.range) { 99 | a = INTERSECT(x, y, w, h, m); 100 | if(a > area) { 101 | area = a; 102 | r = m; 103 | } 104 | } 105 | return r; 106 | } 107 | 108 | Monitor* wintomon(Window w) 109 | { 110 | int x, y; 111 | 112 | if(w == rootWin && getrootptr(&x, &y)) { 113 | return recttomon(x, y, 1, 1); 114 | } 115 | auto m = mons.range.find!(mon => mon.barwin == w).front; 116 | if(m) { 117 | return m; 118 | } 119 | 120 | auto c = wintoclient(w); 121 | if(c) { 122 | return c.mon; 123 | } 124 | return selmon; 125 | } 126 | 127 | Monitor* dirtomon(int dir) 128 | { 129 | Monitor *m = null; 130 | 131 | if(dir > 0) { 132 | m = selmon.next; 133 | if(m is null) { 134 | m = mons; 135 | } 136 | } else if(selmon == mons) { 137 | m = mons.range.find!"a.next is null".front; 138 | } else { 139 | m = mons.range.find!(a => a.next == selmon).front; 140 | } 141 | return m; 142 | } 143 | 144 | Monitor* createmon() 145 | { 146 | Monitor* m = new Monitor(); 147 | 148 | if(m is null) { 149 | die("fatal: could not malloc() %s bytes\n", Monitor.sizeof); 150 | } 151 | 152 | m.tagset[0] = m.tagset[1] = 1; 153 | m.mfact = mfact; 154 | m.nmaster = nmaster; 155 | m.showbar = showbar; 156 | m.topbar = topbar; 157 | m.lt[0] = &layouts[0]; 158 | m.lt[1] = &layouts[1 % LENGTH(layouts)]; 159 | m.ltsymbol = layouts[0].symbol; 160 | 161 | return m; 162 | } 163 | 164 | void arrange(Monitor *m) 165 | { 166 | if(m) { 167 | windowManager.showhide(m.stack); 168 | } else foreach(m; mons.range) { 169 | windowManager.showhide(m.stack); 170 | } 171 | if(m) { 172 | arrangemon(m); 173 | restack(m); 174 | } else foreach(m; mons.range) { 175 | arrangemon(m); 176 | } 177 | } 178 | 179 | void arrangemon(Monitor *m) { 180 | 181 | m.ltsymbol = m.lt[m.sellt].symbol; 182 | 183 | if(m.lt[m.sellt].arrange) 184 | m.lt[m.sellt].arrange(m); 185 | } 186 | 187 | void attach(Client *c) { 188 | 189 | c.next = c.mon.clients; 190 | c.mon.clients = c; 191 | } 192 | 193 | void attachstack(Client *c) { 194 | 195 | c.snext = c.mon.stack; 196 | c.mon.stack = c; 197 | } -------------------------------------------------------------------------------- /src/old.d: -------------------------------------------------------------------------------- 1 | module old; 2 | import core.memory; 3 | 4 | import core.sys.posix.signal; 5 | import core.sys.posix.sys.wait; 6 | import core.sys.posix.unistd; 7 | 8 | import std.c.locale; 9 | import std.c.string; 10 | import std.c.stdlib; 11 | 12 | import std.stdio; 13 | import std.string; 14 | import std.algorithm; 15 | import std.conv; 16 | import std.process; 17 | import std.traits; 18 | 19 | import x11.X; 20 | import x11.Xlib; 21 | import x11.keysymdef; 22 | import x11.Xutil; 23 | import x11.Xatom; 24 | 25 | import types; 26 | import kernel; 27 | import legacy; 28 | import utils; 29 | import config; 30 | import cboxapp; 31 | import window; 32 | import helper.x11; 33 | import gui.cursor; 34 | import gui.font; 35 | import theme.layout; 36 | import theme.manager; 37 | import gui.bar; 38 | import monitor; 39 | 40 | void updatenumlockmask() 41 | { 42 | XModifierKeymap *modmap; 43 | 44 | numlockmask = 0; 45 | modmap = XGetModifierMapping(AppDisplay.instance().dpy); 46 | foreach_reverse(i; 0..7) { 47 | if(numlockmask == 0) { 48 | break; 49 | } 50 | foreach_reverse(j; 0..modmap.max_keypermod-1) { 51 | if(modmap.modifiermap[i * modmap.max_keypermod + j] == 52 | XKeysymToKeycode(AppDisplay.instance().dpy, XK_Num_Lock)) { 53 | numlockmask = (1 << i); 54 | break; 55 | } 56 | } 57 | } 58 | XFreeModifiermap(modmap); 59 | } 60 | 61 | void updateclientlist() 62 | { 63 | 64 | Client *c; 65 | Monitor *m; 66 | 67 | XDeleteProperty(AppDisplay.instance().dpy, rootWin, netatom[NetClientList]); 68 | for(m = mons; m; m = m.next) 69 | for(c = m.clients; c; c = c.next) 70 | XChangeProperty(AppDisplay.instance().dpy, rootWin, netatom[NetClientList], 71 | XA_WINDOW, 32, PropModeAppend, 72 | cast(ubyte *)&(c.win), 1); 73 | } 74 | 75 | 76 | bool getrootptr(int *x, int *y) 77 | { 78 | int di; 79 | uint dui; 80 | Window dummy; 81 | 82 | return XQueryPointer(AppDisplay.instance().dpy, rootWin, &dummy, &dummy, x, y, &di, &di, &dui) != 0; 83 | } 84 | 85 | void setfocus(Client *c) 86 | { 87 | 88 | if(!c.neverfocus) { 89 | XSetInputFocus(AppDisplay.instance().dpy, c.win, RevertToPointerRoot, CurrentTime); 90 | XChangeProperty(AppDisplay.instance().dpy, rootWin, netatom[NetActiveWindow], 91 | XA_WINDOW, 32, PropModeReplace, 92 | cast(ubyte *) &(c.win), 1); 93 | } 94 | sendevent(c, wmatom[WMTakeFocus]); 95 | } 96 | 97 | 98 | bool sendevent(Client *c, Atom proto) 99 | { 100 | int n; 101 | Atom *protocols; 102 | bool exists = false; 103 | XEvent ev; 104 | 105 | if(XGetWMProtocols(AppDisplay.instance().dpy, c.win, &protocols, &n)) { 106 | while(!exists && n--) 107 | exists = protocols[n] == proto; 108 | XFree(protocols); 109 | } 110 | if(exists) { 111 | ev.type = ClientMessage; 112 | ev.xclient.window = c.win; 113 | ev.xclient.message_type = wmatom[WMProtocols]; 114 | ev.xclient.format = 32; 115 | ev.xclient.data.l[0] = proto; 116 | ev.xclient.data.l[1] = CurrentTime; 117 | XSendEvent(AppDisplay.instance().dpy, c.win, false, NoEventMask, &ev); 118 | } 119 | return exists; 120 | } 121 | 122 | Client* nexttiled(Client *c) 123 | { 124 | return c.range!"next".find!(a => !a.isfloating && ISVISIBLE(a)).front; 125 | } 126 | 127 | void pop(Client *c) 128 | { 129 | detach(c); 130 | attach(c); 131 | focus(c); 132 | arrange(c.mon); 133 | } 134 | 135 | void setfullscreen(Client *c, bool fullscreen) 136 | { 137 | if(fullscreen) { 138 | XChangeProperty(AppDisplay.instance().dpy, c.win, netatom[NetWMState], XA_ATOM, 32, 139 | PropModeReplace, cast(ubyte*)&netatom[NetWMFullscreen], 1); 140 | c.isfullscreen = true; 141 | c.oldstate = c.isfloating; 142 | c.oldbw = c.bw; 143 | c.bw = 0; 144 | c.isfloating = true; 145 | resizeclient(c, c.mon.mx, c.mon.my, c.mon.mw, c.mon.mh); 146 | XRaiseWindow(AppDisplay.instance().dpy, c.win); 147 | } else { 148 | XChangeProperty(AppDisplay.instance().dpy, c.win, netatom[NetWMState], XA_ATOM, 32, 149 | PropModeReplace, cast(ubyte*)0, 0); 150 | c.isfullscreen = false; 151 | c.isfloating = c.oldstate; 152 | c.bw = c.oldbw; 153 | c.x = c.oldx; 154 | c.y = c.oldy; 155 | c.w = c.oldw; 156 | c.h = c.oldh; 157 | resizeclient(c, c.x, c.y, c.w, c.h); 158 | arrange(c.mon); 159 | } 160 | } 161 | 162 | void resize(Client *c, int x, int y, int w, int h, bool interact) 163 | { 164 | if(applysizehints(c, x, y, w, h, interact)) 165 | resizeclient(c, x, y, w, h); 166 | } 167 | 168 | void resizeclient(Client *c, int x, int y, int w, int h) 169 | { 170 | XWindowChanges wc; 171 | 172 | c.oldx = c.x; 173 | c.x = wc.x = x; 174 | c.oldy = c.y; 175 | c.y = wc.y = y; 176 | c.oldw = c.w; 177 | c.w = wc.width = w; 178 | c.oldh = c.h; 179 | c.h = wc.height = h; 180 | wc.border_width = c.bw; 181 | XConfigureWindow(AppDisplay.instance().dpy, c.win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc); 182 | configure(c); 183 | XSync(AppDisplay.instance().dpy, false); 184 | } 185 | 186 | void sendmon(Client *c, Monitor *m) 187 | { 188 | if(c.mon == m) 189 | return; 190 | unfocus(c, true); 191 | detach(c); 192 | detachstack(c); 193 | c.mon = m; 194 | c.tags = m.tagset[m.seltags]; /* assign tags of target monitor */ 195 | attach(c); 196 | attachstack(c); 197 | focus(null); 198 | arrange(null); 199 | } 200 | 201 | 202 | bool applysizehints(Client *c, ref int x, ref int y, ref int w, ref int h, bool interact) 203 | { 204 | bool baseismin; 205 | Monitor *m = c.mon; 206 | 207 | /* set minimum possible */ 208 | w = max(1, w); 209 | h = max(1, h); 210 | if(interact) { 211 | if(x > sw) 212 | x = sw - WIDTH(c); 213 | if(y > sh) 214 | y = sh - HEIGHT(c); 215 | if(x + w + 2 * c.bw < 0) 216 | x = 0; 217 | if(y + h + 2 * c.bw < 0) 218 | y = 0; 219 | } else { 220 | if(x >= m.wx + m.ww) 221 | x = m.wx + m.ww - WIDTH(c); 222 | if(y >= m.wy + m.wh) 223 | y = m.wy + m.wh - HEIGHT(c); 224 | if(x + w + 2 * c.bw <= m.wx) 225 | x = m.wx; 226 | if(y + h + 2 * c.bw <= m.wy) 227 | y = m.wy; 228 | } 229 | if(h < bh) 230 | h = bh; 231 | if(w < bh) 232 | w = bh; 233 | if(resizehints || c.isfloating || !c.mon.lt[c.mon.sellt].arrange) { 234 | /* see last two sentences in ICCCM 4.1.2.3 */ 235 | baseismin = c.basew == c.minw && c.baseh == c.minh; 236 | if(!baseismin) { /* temporarily remove base dimensions */ 237 | w -= c.basew; 238 | h -= c.baseh; 239 | } 240 | import std.math : 241 | nearbyint; 242 | /* adjust for aspect limits */ 243 | if(c.mina > 0 && c.maxa > 0) { 244 | if(c.maxa < float(w) / h) 245 | w = cast(int)(h * c.maxa + 0.5); 246 | else if(c.mina < float(h) / w) 247 | h = cast(int)(w * c.mina + 0.5); 248 | } 249 | if(baseismin) { /* increment calculation requires this */ 250 | w -= c.basew; 251 | h -= c.baseh; 252 | } 253 | /* adjust for increment value */ 254 | if(c.incw) 255 | w -= w % c.incw; 256 | if(c.inch) 257 | h -= h % c.inch; 258 | /* restore base dimensions */ 259 | w = max(w + c.basew, c.minw); 260 | h = max(h + c.baseh, c.minh); 261 | if(c.maxw) 262 | w = min(w, c.maxw); 263 | if(c.maxh) 264 | h = min(h, c.maxh); 265 | } 266 | return x != c.x || y != c.y || w != c.w || h != c.h; 267 | } 268 | 269 | void clientmessage(XEvent *e) 270 | { 271 | XClientMessageEvent *cme = &e.xclient; 272 | Client *c = wintoclient(cme.window); 273 | 274 | if(!c) 275 | return; 276 | if(cme.message_type == netatom[NetWMState]) { 277 | if(cme.data.l[1] == netatom[NetWMFullscreen] || cme.data.l[2] == netatom[NetWMFullscreen]) { 278 | setfullscreen(c, (cme.data.l[0] == 1 /* _NET_WM_STATE_ADD */ 279 | || (cme.data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c.isfullscreen))); 280 | } 281 | } else if(cme.message_type == netatom[NetActiveWindow]) { 282 | if(!ISVISIBLE(c)) { 283 | c.mon.seltags ^= 1; 284 | c.mon.tagset[c.mon.seltags] = c.tags; 285 | } 286 | pop(c); 287 | } 288 | } 289 | 290 | void togglefloating(const Arg *arg) 291 | { 292 | if(!selmon.sel) 293 | return; 294 | if(selmon.sel.isfullscreen) /* no support for fullscreen windows */ 295 | return; 296 | selmon.sel.isfloating = !selmon.sel.isfloating || selmon.sel.isfixed; 297 | if(selmon.sel.isfloating) 298 | resize(selmon.sel, selmon.sel.x, selmon.sel.y, 299 | selmon.sel.w, selmon.sel.h, false); 300 | arrange(selmon); 301 | } 302 | 303 | void tag(const Arg *arg) { 304 | 305 | if(selmon.sel && arg.ui & TAGMASK) { 306 | selmon.sel.tags = arg.ui & TAGMASK; 307 | focus(null); 308 | arrange(selmon); 309 | } 310 | } 311 | 312 | void tagmon(const Arg *arg) { 313 | 314 | if(!selmon.sel || !mons.next) 315 | return; 316 | sendmon(selmon.sel, dirtomon(arg.i)); 317 | } 318 | 319 | void toggletag(const Arg *arg) { 320 | 321 | uint newtags; 322 | 323 | if(!selmon.sel) 324 | return; 325 | newtags = selmon.sel.tags ^ (arg.ui & TAGMASK); 326 | if(newtags) { 327 | selmon.sel.tags = newtags; 328 | focus(null); 329 | arrange(selmon); 330 | } 331 | } 332 | 333 | void toggleview(const Arg *arg) { 334 | 335 | uint newtagset = selmon.tagset[selmon.seltags] ^ (arg.ui & TAGMASK); 336 | 337 | if(newtagset) { 338 | selmon.tagset[selmon.seltags] = newtagset; 339 | focus(null); 340 | arrange(selmon); 341 | } 342 | } 343 | 344 | void zoom(const Arg *arg) { 345 | 346 | Client *c = selmon.sel; 347 | 348 | if(!selmon.lt[selmon.sellt].arrange || 349 | (selmon.sel && selmon.sel.isfloating)) { 350 | return; 351 | } 352 | if(c == nexttiled(selmon.clients)) { 353 | if(c) { 354 | c = nexttiled(c.next); 355 | } 356 | if(!c) { 357 | return; 358 | } 359 | } 360 | pop(c); 361 | } 362 | 363 | 364 | void view(const Arg *arg) { 365 | 366 | if((arg.ui & TAGMASK) == selmon.tagset[selmon.seltags]) { 367 | return; 368 | } 369 | selmon.seltags ^= 1; /* toggle sel tagset */ 370 | if(arg.ui & TAGMASK) { 371 | selmon.tagset[selmon.seltags] = arg.ui & TAGMASK; 372 | } 373 | focus(null); 374 | arrange(selmon); 375 | } 376 | 377 | void killclient(const Arg *arg) { 378 | 379 | if(!selmon.sel) 380 | return; 381 | 382 | if(!sendevent(selmon.sel, wmatom[WMDelete])) { 383 | XGrabServer(AppDisplay.instance().dpy); 384 | XSetErrorHandler(&xerrordummy); 385 | XSetCloseDownMode(AppDisplay.instance().dpy, CloseDownMode.DestroyAll); 386 | XKillClient(AppDisplay.instance().dpy, selmon.sel.win); 387 | XSync(AppDisplay.instance().dpy, false); 388 | XSetErrorHandler(&xerror); 389 | XUngrabServer(AppDisplay.instance().dpy); 390 | } 391 | } 392 | 393 | void incnmaster(const Arg *arg) { 394 | 395 | selmon.nmaster = max(selmon.nmaster + arg.i, 0); 396 | arrange(selmon); 397 | } 398 | 399 | void setmfact(const Arg *arg) { 400 | 401 | float f; 402 | 403 | if(!arg || !selmon.lt[selmon.sellt].arrange) 404 | return; 405 | f = arg.f < 1.0 ? arg.f + selmon.mfact : arg.f - 1.0; 406 | if(f < 0.1 || f > 0.9) 407 | return; 408 | selmon.mfact = f; 409 | arrange(selmon); 410 | } 411 | 412 | 413 | bool updategeom() 414 | { 415 | 416 | bool dirty = false; 417 | 418 | Bool isXineramaActive = false; 419 | 420 | version(XINERAMA) { 421 | isXineramaActive = XineramaIsActive(AppDisplay.instance().dpy); 422 | } 423 | 424 | if(isXineramaActive) { 425 | version(XINERAMA) { 426 | import std.range; 427 | int nn; 428 | Client *c; 429 | XineramaScreenInfo *info = XineramaQueryScreens(AppDisplay.instance().dpy, &nn); 430 | auto n = mons.range.walkLength; 431 | 432 | XineramaScreenInfo[] unique = new XineramaScreenInfo[nn]; 433 | if(!unique.length) { 434 | die("fatal: could not malloc() %u bytes\n", XineramaScreenInfo.sizeof * nn); 435 | } 436 | 437 | /* only consider unique geometries as separate screens */ 438 | int j=0; 439 | foreach(i; 0..nn) { 440 | if(isuniquegeom(&unique[j], j, &info[i])) { 441 | unique[j++] = info[i]; 442 | } 443 | } 444 | 445 | XFree(info); 446 | nn = j; 447 | 448 | if(n <= nn) { 449 | foreach(i; 0..(nn-n)) { /* new monitors available */ 450 | auto m = mons.range.find!"a.next is null".front; 451 | if(m) { 452 | m.next = createmon(); 453 | } else { 454 | mons = createmon(); 455 | } 456 | } 457 | foreach(i, m; iota(nn).lockstep(mons.range)) { 458 | if(i >= n || 459 | (unique[i].x_org != m.mx || unique[i].y_org != m.my || 460 | unique[i].width != m.mw || unique[i].height != m.mh)) { 461 | dirty = true; 462 | m.num = i; 463 | m.mx = m.wx = unique[i].x_org; 464 | m.my = m.wy = unique[i].y_org; 465 | m.mw = m.ww = unique[i].width; 466 | m.mh = m.wh = unique[i].height; 467 | updatebarpos(m); 468 | } 469 | } 470 | } else { /* less monitors available nn < n */ 471 | foreach(i; nn..n) { 472 | auto m = mons.range.find!"a.next is null".front; 473 | if(m) { 474 | while(m.clients) { 475 | dirty = true; 476 | c = m.clients; 477 | m.clients = c.next; 478 | detachstack(c); 479 | c.mon = mons; 480 | attach(c); 481 | attachstack(c); 482 | } 483 | if(m == selmon) 484 | selmon = mons; 485 | cleanupmon(m); 486 | } 487 | } 488 | } 489 | unique = null; 490 | } 491 | } else { 492 | /* default monitor setup */ 493 | if(!mons) { 494 | mons = createmon(); 495 | } 496 | 497 | if(mons.mw != sw || mons.mh != sh) { 498 | dirty = true; 499 | mons.mw = mons.ww = sw; 500 | mons.mh = mons.wh = sh; 501 | updatebarpos(mons); 502 | } 503 | } 504 | if(dirty) { 505 | selmon = mons; 506 | selmon = wintomon(rootWin); 507 | } 508 | return dirty; 509 | } 510 | 511 | void updatewmhints(Client *c) { 512 | 513 | XWMHints *wmh; 514 | wmh = XGetWMHints(AppDisplay.instance().dpy, c.win); 515 | if(wmh) { 516 | if(c == selmon.sel && wmh.flags & XUrgencyHint) { 517 | wmh.flags &= ~XUrgencyHint; 518 | XSetWMHints(AppDisplay.instance().dpy, c.win, wmh); 519 | } else 520 | c.isurgent = (wmh.flags & XUrgencyHint) ? true : false; 521 | if(wmh.flags & InputHint) 522 | c.neverfocus = !wmh.input; 523 | else 524 | c.neverfocus = false; 525 | XFree(wmh); 526 | } 527 | } 528 | 529 | void setclientstate(Client *c, long state) { 530 | 531 | long[] data = [ state, None ]; 532 | 533 | XChangeProperty(AppDisplay.instance().dpy, c.win, wmatom[WMState], wmatom[WMState], 32, 534 | PropModeReplace, cast(ubyte *)data, 2); 535 | } 536 | 537 | void configure(Client *c) { 538 | 539 | XConfigureEvent ce; 540 | 541 | ce.type = ConfigureNotify; 542 | ce.display = AppDisplay.instance().dpy; 543 | ce.event = c.win; 544 | ce.window = c.win; 545 | ce.x = c.x; 546 | ce.y = c.y; 547 | ce.width = c.w; 548 | ce.height = c.h; 549 | ce.border_width = c.bw; 550 | ce.above = None; 551 | ce.override_redirect = false; 552 | XSendEvent(AppDisplay.instance().dpy, c.win, false, StructureNotifyMask, cast(XEvent *)&ce); 553 | } 554 | 555 | 556 | 557 | 558 | void updatesizehints(Client *c) { 559 | 560 | long msize; 561 | XSizeHints size; 562 | 563 | if(!XGetWMNormalHints(AppDisplay.instance().dpy, c.win, &size, &msize)) { 564 | /* size is uninitialized, ensure that size.flags aren't used */ 565 | size.flags = PSize; 566 | } 567 | if(size.flags & PBaseSize) { 568 | c.basew = size.base_width; 569 | c.baseh = size.base_height; 570 | } else if(size.flags & PMinSize) { 571 | c.basew = size.min_width; 572 | c.baseh = size.min_height; 573 | } else { 574 | c.basew = c.baseh = 0; 575 | } 576 | 577 | if(size.flags & PResizeInc) { 578 | c.incw = size.width_inc; 579 | c.inch = size.height_inc; 580 | } else { 581 | c.incw = c.inch = 0; 582 | } 583 | if(size.flags & PMaxSize) { 584 | c.maxw = size.max_width; 585 | c.maxh = size.max_height; 586 | } else { 587 | c.maxw = c.maxh = 0; 588 | } 589 | if(size.flags & PMinSize) { 590 | c.minw = size.min_width; 591 | c.minh = size.min_height; 592 | } else if(size.flags & PBaseSize) { 593 | c.minw = size.base_width; 594 | c.minh = size.base_height; 595 | } else { 596 | c.minw = c.minh = 0; 597 | } 598 | if(size.flags & PAspect) { 599 | c.mina = cast(float)size.min_aspect.y / size.min_aspect.x; 600 | c.maxa = cast(float)size.max_aspect.x / size.max_aspect.y; 601 | } else { 602 | c.maxa = c.mina = 0.0; 603 | } 604 | c.isfixed = (c.maxw && c.minw && c.maxh && c.minh 605 | && c.maxw == c.minw && c.maxh == c.minh); 606 | } 607 | 608 | void focus(Client *c) { 609 | if(!c || !ISVISIBLE(c)) { 610 | c = selmon.stack.range!"snext".find!(a => ISVISIBLE(a)).front; 611 | } 612 | /* was if(selmon.sel) */ 613 | if(selmon.sel && selmon.sel != c) 614 | unfocus(selmon.sel, false); 615 | if(c) { 616 | if(c.mon != selmon) 617 | selmon = c.mon; 618 | if(c.isurgent) 619 | clearurgent(c); 620 | detachstack(c); 621 | attachstack(c); 622 | mouseEventHandler.grabbuttons(c, true); 623 | XSetWindowBorder(AppDisplay.instance().dpy, c.win, ThemeManager.instance().getScheme(SchemeSel).border.rgb); 624 | setfocus(c); 625 | } else { 626 | XSetInputFocus(AppDisplay.instance().dpy, rootWin, RevertToPointerRoot, CurrentTime); 627 | XDeleteProperty(AppDisplay.instance().dpy, rootWin, netatom[NetActiveWindow]); 628 | } 629 | selmon.sel = c; 630 | drawbars(); 631 | } 632 | 633 | void focusmon(const Arg *arg) { 634 | 635 | Monitor *m; 636 | 637 | if(mons && !mons.next) { 638 | return; 639 | } 640 | m = dirtomon(arg.i); 641 | if(m == selmon) { 642 | return; 643 | } 644 | unfocus(selmon.sel, false); /* s/true/false/ fixes input focus issues 645 | in gedit and anjuta */ 646 | selmon = m; 647 | focus(null); 648 | } 649 | 650 | void focusstack(const Arg *arg) { 651 | 652 | Client *c = null, i; 653 | 654 | if(!selmon.sel) 655 | return; 656 | if(arg.i > 0) { 657 | c = selmon.sel.range!"next".find!(a => ISVISIBLE(a)).front; 658 | if(!c) { 659 | c = selmon.clients.range!"next".find!(a => ISVISIBLE(a)).front; 660 | } 661 | } else { 662 | for(i = selmon.clients; i != selmon.sel; i = i.next) { 663 | if(ISVISIBLE(i)) { 664 | c = i; 665 | } 666 | } 667 | if(!c) { 668 | for(; i; i = i.next) { 669 | if(ISVISIBLE(i)) { 670 | c = i; 671 | } 672 | } 673 | } 674 | } 675 | if(c) { 676 | focus(c); 677 | restack(selmon); 678 | } 679 | } 680 | 681 | Client* wintoclient(Window w) { 682 | 683 | foreach(m; mons.range) { 684 | auto c = m.clients.range!"next".find!(client => client.win == w).front; 685 | if(c) { 686 | return c; 687 | } 688 | } 689 | return null; 690 | } 691 | 692 | void unfocus(Client *c, bool setfocus) { 693 | 694 | if(!c) 695 | return; 696 | mouseEventHandler.grabbuttons(c, false); 697 | XSetWindowBorder(AppDisplay.instance().dpy, c.win, ThemeManager.instance().getScheme(SchemeNorm).border.rgb); 698 | if(setfocus) { 699 | XSetInputFocus(AppDisplay.instance().dpy, rootWin, RevertToPointerRoot, CurrentTime); 700 | XDeleteProperty(AppDisplay.instance().dpy, rootWin, netatom[NetActiveWindow]); 701 | } 702 | } 703 | 704 | 705 | 706 | void restack(Monitor *m) 707 | { 708 | XEvent ev; 709 | XWindowChanges wc; 710 | 711 | drawbar(m); 712 | if(!m.sel) 713 | return; 714 | if(m.sel.isfloating || !m.lt[m.sellt].arrange) 715 | XRaiseWindow(AppDisplay.instance().dpy, m.sel.win); 716 | if(m.lt[m.sellt].arrange) { 717 | wc.stack_mode = Below; 718 | wc.sibling = m.barwin; 719 | auto stacks = m.stack.range!"snext".filter!(a => !a.isfloating && ISVISIBLE(a)); 720 | foreach(c; stacks) { 721 | XConfigureWindow(AppDisplay.instance().dpy, c.win, CWSibling|CWStackMode, &wc); 722 | wc.sibling = c.win; 723 | } 724 | } 725 | XSync(AppDisplay.instance().dpy, false); 726 | while(XCheckMaskEvent(AppDisplay.instance().dpy, EnterWindowMask, &ev)) {} 727 | } 728 | 729 | auto TEXTW(X)(auto ref in X x) 730 | if(isSomeString!X) { 731 | return drw.font.getexts_width(x) + drw.font.h; 732 | } 733 | 734 | void cleanupmon(Monitor *mon) 735 | { 736 | if(mon && mon == mons) { 737 | mons = mons.next; 738 | } else { 739 | auto m = mons.range.find!(a => a.next == mon).front; 740 | if(m) { 741 | m.next = mon.next; 742 | } 743 | } 744 | XUnmapWindow(AppDisplay.instance().dpy, mon.barwin); 745 | XDestroyWindow(AppDisplay.instance().dpy, mon.barwin); 746 | DGC.free(mon); 747 | } 748 | 749 | void clearurgent(Client *c) { 750 | 751 | XWMHints *wmh; 752 | 753 | c.isurgent = false; 754 | wmh = XGetWMHints(AppDisplay.instance().dpy, c.win); 755 | if(wmh is null) { 756 | return; 757 | } 758 | wmh.flags &= ~XUrgencyHint; 759 | XSetWMHints(AppDisplay.instance().dpy, c.win, wmh); 760 | XFree(wmh); 761 | } 762 | 763 | void detachstack(Client *c) 764 | { 765 | Client **tc; 766 | 767 | 768 | for(tc = &c.mon.stack; *tc && *tc != c; tc = &(*tc).snext) {} 769 | *tc = c.snext; 770 | 771 | if(c && c == c.mon.sel) { 772 | auto t = c.mon.stack.range!"snext".find!(a=>ISVISIBLE(a)).front; 773 | c.mon.sel = t; 774 | } 775 | } 776 | 777 | void detach(Client *c) 778 | { 779 | Client **tc; 780 | for(tc = &c.mon.clients; *tc && *tc != c; tc = &(*tc).next) {} 781 | *tc = c.next; 782 | } 783 | 784 | -------------------------------------------------------------------------------- /src/theme/layout.d: -------------------------------------------------------------------------------- 1 | module theme.layout; 2 | 3 | import kernel; 4 | import cboxapp; 5 | import old; 6 | import types; 7 | import utils; 8 | import theme.manager; 9 | import window; 10 | import monitor; 11 | 12 | import std.c.stdlib; 13 | import x11.X; 14 | import x11.Xlib; 15 | import x11.keysymdef; 16 | import x11.Xutil; 17 | import x11.Xatom; 18 | import std.stdio; 19 | import std.string; 20 | import std.algorithm; 21 | 22 | 23 | struct Layout 24 | { 25 | const string symbol; 26 | void function(Monitor* m) arrange; 27 | } 28 | 29 | struct Clr 30 | { 31 | ulong rgb; 32 | 33 | this(Drw* drw, in string clrname) 34 | { 35 | if(drw is null) { 36 | // lout(__FUNCTION__~"\n\t--> NULL Drw* parm"); 37 | exit(EXIT_FAILURE); 38 | } 39 | 40 | Colormap cmap; 41 | XColor color; 42 | 43 | cmap = DefaultColormap(drw.dpy, drw.screen); 44 | if(!XAllocNamedColor(drw.dpy, cmap, cast(char*)clrname.toStringz, &color, &color)) { 45 | // lout("error, cannot allocate color '%s'", clrname); 46 | exit(EXIT_FAILURE); 47 | } 48 | this.rgb = color.pixel; 49 | } 50 | 51 | static void free(Clr *clr) 52 | { 53 | if(clr) { 54 | DGC.free(clr); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/theme/manager.d: -------------------------------------------------------------------------------- 1 | module theme.manager; 2 | 3 | import theme.layout; 4 | import gui.font; 5 | import config; 6 | import cboxapp; 7 | import window; 8 | import types; 9 | import gui.bar; 10 | import kernel; 11 | import monitor; 12 | 13 | import std.algorithm; 14 | import std.c.string; 15 | import x11.X; 16 | import x11.Xlib; 17 | import x11.keysymdef; 18 | import x11.Xutil; 19 | import x11.Xatom; 20 | 21 | /* color schemes */ 22 | enum 23 | { 24 | SchemeNorm, 25 | SchemeSel, 26 | SchemeLast 27 | }; 28 | 29 | /// The colour scheme to use 30 | struct ClrScheme 31 | { 32 | Clr *fg; 33 | Clr *bg; 34 | Clr *border; 35 | } 36 | 37 | class ThemeManager 38 | { 39 | 40 | ClrScheme[SchemeLast] scheme; 41 | 42 | static ThemeManager instance() 43 | { 44 | if (!instantiated_) { 45 | synchronized { 46 | if (instance_ is null) { 47 | instance_ = new ThemeManager; 48 | } 49 | instantiated_ = true; 50 | } 51 | } 52 | return instance_; 53 | } 54 | 55 | ClrScheme getScheme(int type) 56 | { 57 | return this.scheme[type]; 58 | } 59 | 60 | 61 | private: 62 | this() 63 | { 64 | /* init appearance */ 65 | scheme[SchemeNorm].border = new Clr(drw, normbordercolor); 66 | scheme[SchemeNorm].bg = new Clr(drw, normbgcolor); 67 | scheme[SchemeNorm].fg = new Clr(drw, normfgcolor); 68 | scheme[SchemeSel].border = new Clr(drw, selbordercolor); 69 | scheme[SchemeSel].bg = new Clr(drw, selbgcolor); 70 | scheme[SchemeSel].fg = new Clr(drw, selfgcolor); 71 | } 72 | 73 | static bool instantiated_; // Thread local 74 | __gshared ThemeManager instance_; 75 | 76 | } 77 | 78 | /** 79 | * Drw provides an interface for working with drawable surfaces. 80 | */ 81 | struct Drw 82 | { 83 | uint w, h; /// The width and height of the drawable area 84 | Display *dpy; /// The X display 85 | int screen; /// The X screen ID 86 | Window root; /// The root window for this drawable 87 | Drawable drawable; /// The X drawable encapsulated by this. 88 | XGC gc; /// The X graphic context 89 | ClrScheme *scheme; /// The colour scheme to use 90 | Fnt *font; /// The X font to use for rendering text. 91 | 92 | /** 93 | * Ctor to initialise the draw object 94 | * Params: 95 | * dpy= X display to render with. 96 | * screen= X screen id 97 | * root= Root X window for this drawable 98 | * w= Width of the drawable 99 | * h= Height of the drawable 100 | * Example: 101 | * --- 102 | * drw = new Drw(AppDisplay.instance().dpy, screen, root, sw, sh); 103 | * --- 104 | */ 105 | this(Display* dpy, int screen, Window root, uint w, uint h) 106 | { 107 | this.dpy = dpy; 108 | this.screen = screen; 109 | this.root = root; 110 | this.w = w; 111 | this.h = h; 112 | this.drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen)); 113 | this.gc = XCreateGC(dpy, root, 0, null); 114 | XSetLineAttributes(dpy, this.gc, 1, LineSolid, CapButt, JoinMiter); 115 | 116 | } 117 | 118 | /** 119 | * Resize the drawable to a new width and height 120 | * Params: 121 | * w= Width 122 | * h= Height 123 | * Example: 124 | * --- 125 | * drw.resize(100, 100); 126 | * --- 127 | */ 128 | void resize(uint w, uint h) 129 | { 130 | this.w = w; 131 | this.h = h; 132 | if(this.drawable != 0) { 133 | XFreePixmap(this.dpy, this.drawable); 134 | } 135 | this.drawable = XCreatePixmap(this.dpy, this.root, w, h, DefaultDepth(this.dpy, this.screen)); 136 | } 137 | 138 | /** 139 | * Set the font to use for rendering. 140 | * Params: 141 | * font= Pointer to the font to use. 142 | */ 143 | void setfont(Fnt *font) 144 | { 145 | this.font = font; 146 | } 147 | 148 | /** 149 | * Set the scheme for this drawable 150 | * Params: 151 | * scheme = Pointer to the scheme to use 152 | */ 153 | void setscheme(ClrScheme *scheme) 154 | { 155 | if(scheme) 156 | this.scheme = scheme; 157 | } 158 | 159 | /** 160 | * Draw a rectangle to the X display using the current settings. Note that 161 | * filled and empty are not mutually exclusive. 162 | * Params: 163 | * x= Left edge of the rect 164 | * y= Top edge of the rect 165 | * w= Width of the rect 166 | * h= Height of the rect 167 | * filled= If true the rect will be filled 168 | * empty= If true the rect will be empty 169 | * invert= If true the colours will be inverted. 170 | * Example: 171 | * --- 172 | * drw.rect(10, 10, 90, 90, true, false, false); 173 | * --- 174 | */ 175 | void rect(int x, int y, uint w, uint h, int filled, int empty, int invert) 176 | { 177 | int dx; 178 | 179 | if(!this.font || !this.scheme) 180 | return; 181 | XSetForeground(this.dpy, this.gc, invert ? this.scheme.bg.rgb : this.scheme.fg.rgb); 182 | dx = (this.font.ascent + this.font.descent + 2) / 4; 183 | if(filled) 184 | XFillRectangle(this.dpy, this.drawable, this.gc, x+1, y+1, dx+1, dx+1); 185 | else if(empty) 186 | XDrawRectangle(this.dpy, this.drawable, this.gc, x+1, y+1, dx, dx); 187 | } 188 | 189 | /** 190 | * Render some text to the display using the current font. 191 | * Params: 192 | * x= Left edge of the text area 193 | * y= Top of the text area 194 | * w= Width of the text area 195 | * h= Height of the text area 196 | * text= Text to write 197 | * invert= true the text bg/fg coluors will be inverted 198 | * Example: 199 | * --- 200 | * drw.text(10, 10, 100, 100, "this is a test", false); 201 | * --- 202 | */ 203 | void text(int x, int y, uint w, uint h, in string text, int invert) 204 | { 205 | char[256] buf; 206 | Extnts tex; 207 | 208 | if(!this.scheme) { 209 | return; 210 | } 211 | XSetForeground(this.dpy, this.gc, invert ? this.scheme.fg.rgb : this.scheme.bg.rgb); 212 | XFillRectangle(this.dpy, this.drawable, this.gc, x, y, w, h); 213 | if(!text || !this.font) { 214 | return; 215 | } 216 | this.font.getexts(text, &tex); 217 | auto th = this.font.ascent + this.font.descent; 218 | auto ty = y + (h / 2) - (th / 2) + this.font.ascent; 219 | auto tx = x + (h / 2); 220 | /* shorten text if necessary */ 221 | auto len = min(text.length, buf.sizeof); 222 | for(; len && (tex.w > w - tex.h || w < tex.h); len--) { 223 | this.font.getexts(text[0..len], &tex); 224 | } 225 | if(!len) { 226 | return; 227 | } 228 | memcpy(buf.ptr, text.ptr, len); 229 | 230 | XSetForeground(this.dpy, this.gc, invert ? this.scheme.bg.rgb : this.scheme.fg.rgb); 231 | if(this.font.set) 232 | XmbDrawString(this.dpy, this.drawable, this.font.set, this.gc, tx, ty, buf.ptr, cast(uint)(len)); 233 | else 234 | XDrawString(this.dpy, this.drawable, this.gc, tx, ty, buf.ptr, cast(int)len); 235 | } 236 | 237 | /** 238 | * Copy the drawable area to a window. 239 | * Params: 240 | * win= Destination to copy to. 241 | * x= Left edge of area to copy 242 | * y= Top of area to copy 243 | * w= Width of area to copy 244 | * h= Height of area to copy 245 | * Example: 246 | * --- 247 | * drw.map(win, 10, 10, 100, 100); 248 | * --- 249 | */ 250 | void map(Window win, int x, int y, uint w, uint h) 251 | { 252 | XCopyArea(this.dpy, this.drawable, win, this.gc, x, y, w, h, x, y); 253 | XSync(this.dpy, false); 254 | } 255 | 256 | /** 257 | * Release the GC memory used for the Drw object 258 | * Params: 259 | * drw = Drw object to release 260 | */ 261 | static void free(Drw* drw) 262 | { 263 | drw.destroy; 264 | DGC.free(drw); 265 | } 266 | 267 | /** 268 | * Destroy the X resources used by this drawable. 269 | */ 270 | void destroy() 271 | { 272 | XFreePixmap(this.dpy, this.drawable); 273 | XFreeGC(this.dpy, this.gc); 274 | } 275 | 276 | } -------------------------------------------------------------------------------- /src/types.d: -------------------------------------------------------------------------------- 1 | module types; 2 | 3 | import x11.X; 4 | import x11.Xlib; 5 | import x11.keysymdef; 6 | import x11.Xutil; 7 | import x11.Xatom; 8 | import std.conv; 9 | import cboxapp; 10 | import config; 11 | import old; 12 | import monitor; 13 | 14 | import std.traits; 15 | 16 | static string stext; 17 | 18 | struct Extnts 19 | { 20 | uint w; 21 | uint h; 22 | } 23 | 24 | enum Keys 25 | { 26 | MOD1 = Mod1Mask, 27 | MOD4 = Mod4Mask, 28 | CONTROL = ControlMask, 29 | SHIFT = ShiftMask 30 | }; 31 | 32 | 33 | 34 | enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, 35 | ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ 36 | 37 | static int screen; 38 | static int sw, sh; /* X display screen geometry width, height */ 39 | static int bh, blw = 0; /* bar geometry */ 40 | static uint numlockmask = 0; 41 | static Monitor *mons, selmon; 42 | static Window rootWin; 43 | 44 | auto range(string NextField)(Client* head) 45 | { 46 | return Client.ClientRange!NextField(head); 47 | } 48 | 49 | auto range(Monitor* head) 50 | { 51 | return Monitor.MonitorRange(head); 52 | } 53 | 54 | auto makeArg(TIN)(TIN val) 55 | { 56 | alias T = Unqual!TIN; 57 | return Arg(val); 58 | } 59 | 60 | auto CLEANMASK(M)(auto ref in M mask) 61 | { 62 | return (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)); 63 | } 64 | 65 | auto INTERSECT(T, M)(T x, T y, T w, T h, M m) 66 | { 67 | import std.algorithm; 68 | return max(0, min(x + w, m.wx + m.ww) - max(x, m.wx)) * max(0, min(y + h, m.wy + m.wh) - max(y, m.wy)); 69 | } 70 | 71 | auto ISVISIBLE(C)(auto ref in C c) pure @safe @nogc nothrow 72 | { 73 | return c.tags & c.mon.tagset[c.mon.seltags]; 74 | } 75 | 76 | auto LENGTH(X)(auto ref in X x) 77 | { 78 | return x.length; 79 | } 80 | 81 | auto WIDTH(X)(auto ref in X x) 82 | { 83 | return x.w + 2 * x.bw; 84 | } 85 | 86 | auto HEIGHT(X)(auto ref in X x) 87 | { 88 | return x.h + 2 * x.bw; 89 | } 90 | 91 | enum TAGMASK = ((1 << tags.length) - 1); 92 | 93 | struct Rule 94 | { 95 | string klass; 96 | string instance; 97 | string title; 98 | uint tags; 99 | bool isfloating; 100 | int monitor; 101 | } 102 | 103 | struct Arg 104 | { 105 | union Vals 106 | { 107 | int ival; 108 | uint uival; 109 | float fval; 110 | string[] sval; 111 | void* vptr; 112 | } 113 | 114 | Vals val; 115 | 116 | //Variant val; 117 | this(TIN)(TIN val) 118 | { 119 | alias T = Unqual!TIN; 120 | static if(isIntegral!T) { 121 | this.val.ival = cast(int)(val); 122 | } else static if(isFloatingPoint!T) { 123 | this.val.fval = cast(float)(val); 124 | } else static if(is(TIN == immutable(immutable(char)[])[])) { 125 | this.val.sval = cast(string[])val; 126 | } else { 127 | this.val.vptr = cast(void*)(val); 128 | } 129 | } 130 | 131 | @property int i() const 132 | { 133 | return this.val.ival; 134 | } 135 | 136 | @property void i(int ival) 137 | { 138 | val.ival = cast(int)(ival); 139 | } 140 | 141 | @property uint ui() const 142 | { 143 | return this.val.uival; 144 | } 145 | 146 | @property void ui(uint ival) 147 | { 148 | val.uival = cast(uint)(ival); 149 | } 150 | 151 | @property float f() const 152 | { 153 | return this.val.fval; 154 | } 155 | 156 | @property void f(float ival) 157 | { 158 | val.fval = cast(float)(ival); 159 | } 160 | 161 | @property const(string[]) s() const 162 | { 163 | return this.val.sval; 164 | } 165 | 166 | @property void s(string[] ival) 167 | { 168 | val.sval = cast(string[])(ival); 169 | } 170 | 171 | @property const(void*) v() const 172 | { 173 | return this.val.vptr; 174 | } 175 | 176 | @property void v(void* ival) 177 | { 178 | val.vptr = cast(void*)(ival); 179 | } 180 | } 181 | 182 | 183 | 184 | -------------------------------------------------------------------------------- /src/utils.d: -------------------------------------------------------------------------------- 1 | module utils; 2 | 3 | import legacy; 4 | import std.stdio; 5 | import core.sys.posix.signal; 6 | 7 | import x11.X; 8 | import x11.Xlib; 9 | import x11.keysymdef; 10 | import x11.Xutil; 11 | import x11.Xatom; 12 | 13 | void sigchld(int unused) nothrow 14 | { 15 | if(signal(SIGCHLD, &sigchldImpl) == SIG_ERR) { 16 | die("Can't install SIGCHLD handler"); 17 | } 18 | sigchldImpl(unused); 19 | } 20 | 21 | auto die(F, A...)(lazy F fmt, lazy A args) nothrow 22 | { 23 | import std.c.stdlib; 24 | try { 25 | std.stdio.stderr.writefln("\n\n"~fmt~"\n\n", args); 26 | } catch {} 27 | exit(EXIT_FAILURE); 28 | } 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/window.d: -------------------------------------------------------------------------------- 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 x11.X; 18 | import x11.Xlib; 19 | import x11.keysymdef; 20 | import x11.Xutil; 21 | import 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 | this.showhide(c.snext); 114 | } else { /* hide clients bottom up */ 115 | this.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 | this.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 unmanage(Client *c, bool destroyed) 271 | { 272 | Monitor *m = c.mon; 273 | XWindowChanges wc; 274 | 275 | /* The server grab construct avoids race conditions. */ 276 | detach(c); 277 | detachstack(c); 278 | 279 | if(!destroyed) { 280 | wc.border_width = c.oldbw; 281 | XGrabServer(AppDisplay.instance().dpy); 282 | XSetErrorHandler(&xerrordummy); 283 | XConfigureWindow(AppDisplay.instance().dpy, c.win, CWBorderWidth, &wc); /* restore border */ 284 | XUngrabButton(AppDisplay.instance().dpy, AnyButton, AnyModifier, c.win); 285 | setclientstate(c, WithdrawnState); 286 | XSync(AppDisplay.instance().dpy, false); 287 | XSetErrorHandler(&xerror); 288 | XUngrabServer(AppDisplay.instance().dpy); 289 | } 290 | 291 | DGC.free(c); 292 | focus(null); 293 | updateclientlist(); 294 | arrange(m); 295 | } 296 | 297 | 298 | -------------------------------------------------------------------------------- /src/xinerama.d: -------------------------------------------------------------------------------- 1 | module xinerama; 2 | 3 | import x11.Xlib; 4 | 5 | struct XineramaScreenInfo 6 | { 7 | int screen_number; 8 | short x_org; 9 | short y_org; 10 | short width; 11 | short height; 12 | } 13 | 14 | // _XFUNCPROTOBEGIN 15 | 16 | extern(C) Bool XineramaQueryExtension ( 17 | Display *dpy, 18 | int *event_base, 19 | int *error_base 20 | ) nothrow @nogc @system; 21 | 22 | extern(C) Status XineramaQueryVersion( 23 | Display *dpy, 24 | int *major_versionp, 25 | int *minor_versionp 26 | ) nothrow @nogc @system; 27 | 28 | extern(C) Bool XineramaIsActive(Display *dpy) nothrow @nogc @system; 29 | 30 | /* 31 | Returns the number of heads and a pointer to an array of 32 | structures describing the position and size of the individual 33 | heads. Returns NULL and number = 0 if Xinerama is not active. 34 | 35 | Returned array should be freed with XFree(). 36 | */ 37 | 38 | extern(C) XineramaScreenInfo * 39 | XineramaQueryScreens( 40 | Display *dpy, 41 | int *number 42 | ) nothrow @nogc @system; --------------------------------------------------------------------------------