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 deimos.X11.X; 14 import deimos.X11.Xlib; 15 import deimos.X11.keysymdef; 16 import deimos.X11.Xutil; 17 import deimos.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( ClkLtSymbol, 0, Button1, &setlayout ), 56 Button( ClkLtSymbol, 0, Button3, &setlayout, &layouts[2] ), 57 Button( ClkWinTitle, 0, Button2, &zoom ), 58 Button( ClkStatusText, 0, Button2, &spawn, termcmd ), // &termcmd 59 Button( ClkClientWin, MODKEY, Button1, &movemouse ), 60 Button( ClkClientWin, MODKEY, Button2, &togglefloating ), 61 Button( ClkClientWin, MODKEY, Button3, &resizemouse ), 62 Button( ClkTagBar, 0, Button1, &view ), 63 Button( ClkTagBar, 0, Button3, &toggleview ), 64 Button( ClkTagBar, MODKEY, Button1, &tag ), 65 Button( ClkTagBar, MODKEY, Button3, &toggletag ) 66 ]; 67 } 68 69 Button[] getButtons() 70 { 71 return this.buttons; 72 } 73 74 void addEvent() 75 { 76 77 } 78 79 void listen(XEvent *e) 80 { 81 switch (e.type) { 82 case ButtonPress: 83 this.buttonpress(e); 84 break; 85 default: { } 86 } 87 } 88 89 void grabbuttons(Client *c, bool focused) 90 { 91 updatenumlockmask(); 92 uint i, j; 93 uint[] modifiers = [ 0, LockMask, numlockmask, numlockmask|LockMask ]; 94 XUngrabButton(AppDisplay.instance().dpy, AnyButton, AnyModifier, c.win); 95 if(focused) { 96 foreach(ref const but; this.buttons) { 97 if(but.click == ClkClientWin) { 98 foreach(ref const mod; modifiers) { 99 XGrabButton(AppDisplay.instance().dpy, but.button, 100 but.mask | mod, 101 c.win, false, BUTTONMASK, 102 GrabModeAsync, GrabModeSync, 103 cast(ulong)None, cast(ulong)None); 104 } 105 } 106 } 107 } else { 108 XGrabButton(AppDisplay.instance().dpy, AnyButton, AnyModifier, c.win, false, 109 BUTTONMASK, GrabModeAsync, GrabModeSync, None, None); 110 } 111 } 112 113 void buttonpress(XEvent *e) 114 { 115 uint i, x, click; 116 auto arg = Arg(0); 117 Client *c; 118 Monitor *m; 119 XButtonPressedEvent *ev = &e.xbutton; 120 121 click = ClkRootWin; 122 123 /* focus monitor if necessary */ 124 m = cast(Monitor*)wintomon(ev.window); 125 126 if(ev.window == selmon.barwin) { 127 i = x = 0; 128 do { 129 x += TEXTW(tags[i]); 130 } while(ev.x >= x && ++i < LENGTH(tags)); 131 if(i < LENGTH(tags)) { 132 click = ClkTagBar; 133 arg.ui = 1 << i; 134 } else if(ev.x < x + blw) 135 click = ClkLtSymbol; 136 else if(ev.x > selmon.ww - TEXTW(stext)) 137 click = ClkStatusText; 138 else 139 click = ClkWinTitle; 140 } else { 141 c = wintoclient(ev.window); 142 if(c !is null) { 143 focus(c); 144 click = ClkClientWin; 145 } 146 } 147 foreach(ref const but; buttons) { 148 if(click == but.click && 149 but.func !is null && 150 but.button == ev.button && 151 CLEANMASK(but.mask) == CLEANMASK(ev.state)) { 152 but.func(click == ClkTagBar && but.arg.i == 0 ? &arg : &but.arg); 153 } 154 } 155 } 156 } 157 158 void movemouse(const Arg *arg) 159 { 160 int x, y, ocx, ocy, nx, ny; 161 Client *c; 162 Monitor *m; 163 XEvent ev; 164 Time lasttime = 0; 165 166 c = selmon.sel; 167 168 if(!c) { 169 return; 170 } 171 172 if(c.isfullscreen) /* no support moving fullscreen windows by mouse */ 173 return; 174 175 restack(selmon); 176 ocx = c.x; 177 ocy = c.y; 178 if(XGrabPointer(AppDisplay.instance().dpy, 179 rootWin, 180 false, 181 MOUSEMASK, 182 GrabModeAsync, 183 GrabModeAsync, 184 None, 185 cursor[CurMove].cursor, 186 CurrentTime) != GrabSuccess) { 187 return; 188 } 189 190 if(!getrootptr(&x, &y)) { 191 return; 192 } 193 194 do { 195 XMaskEvent(AppDisplay.instance().dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); 196 switch(ev.type) { 197 case ConfigureRequest: 198 case Expose: 199 case MapRequest: 200 writeln(ev.type); 201 //handler[ev.type](&ev); 202 break; 203 case MotionNotify: 204 205 if ((ev.xmotion.time - lasttime) <= (1000 / 60)) 206 continue; 207 lasttime = ev.xmotion.time; 208 209 nx = ocx + (ev.xmotion.x - x); 210 ny = ocy + (ev.xmotion.y - y); 211 if(nx >= selmon.wx && nx <= selmon.wx + selmon.ww 212 && ny >= selmon.wy && ny <= selmon.wy + selmon.wh) { 213 if(abs(selmon.wx - nx) < snap) 214 nx = selmon.wx; 215 else if(abs((selmon.wx + selmon.ww) - (nx + WIDTH(c))) < snap) 216 nx = selmon.wx + selmon.ww - WIDTH(c); 217 if(abs(selmon.wy - ny) < snap) 218 ny = selmon.wy; 219 else if(abs((selmon.wy + selmon.wh) - (ny + HEIGHT(c))) < snap) 220 ny = selmon.wy + selmon.wh - HEIGHT(c); 221 if(!c.isfloating && selmon.lt[selmon.sellt].arrange 222 && (abs(nx - c.x) > snap || abs(ny - c.y) > snap)) 223 togglefloating(null); 224 } 225 226 if(!selmon.lt[selmon.sellt].arrange || c.isfloating) 227 resize(c, nx, ny, c.w, c.h, true); 228 break; 229 default : 230 break; 231 } 232 } while(ev.type != ButtonRelease); 233 XUngrabPointer(AppDisplay.instance().dpy, CurrentTime); 234 235 if((m = recttomon(c.x, c.y, c.w, c.h)) != selmon) { 236 sendmon(c, m); 237 selmon = m; 238 focus(null); 239 } 240 } 241 242 void resizemouse(const Arg *arg) 243 { 244 int ocx, ocy, nw, nh; 245 Client *c; 246 Monitor *m; 247 XEvent ev; 248 Time lasttime = 0; 249 250 c = selmon.sel; 251 if(!c) { 252 return; 253 } 254 255 if(c.isfullscreen) /* no support resizing fullscreen windows by mouse */ 256 return; 257 258 restack(selmon); 259 ocx = c.x; 260 ocy = c.y; 261 262 if(XGrabPointer(AppDisplay.instance().dpy, rootWin, false, MOUSEMASK, GrabModeAsync, GrabModeAsync, 263 None, cursor[CurResize].cursor, CurrentTime) != GrabSuccess) 264 return; 265 266 XWarpPointer(AppDisplay.instance().dpy, None, c.win, 0, 0, 0, 0, c.w + c.bw - 1, c.h + c.bw - 1); 267 do { 268 XMaskEvent(AppDisplay.instance().dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); 269 switch(ev.type) { 270 case ConfigureRequest: 271 case Expose: 272 case MapRequest: 273 handler[ev.type](&ev); 274 break; 275 case MotionNotify: 276 if ((ev.xmotion.time - lasttime) <= (1000 / 60)) 277 continue; 278 lasttime = ev.xmotion.time; 279 280 nw = max(ev.xmotion.x - ocx - 2 * c.bw + 1, 1); 281 nh = max(ev.xmotion.y - ocy - 2 * c.bw + 1, 1); 282 if(c.mon.wx + nw >= selmon.wx && c.mon.wx + nw <= selmon.wx + selmon.ww 283 && c.mon.wy + nh >= selmon.wy && c.mon.wy + nh <= selmon.wy + selmon.wh) { 284 if(!c.isfloating && selmon.lt[selmon.sellt].arrange 285 && (abs(nw - c.w) > snap || abs(nh - c.h) > snap)) 286 togglefloating(null); 287 } 288 if(!selmon.lt[selmon.sellt].arrange || c.isfloating) 289 resize(c, c.x, c.y, nw, nh, true); 290 break; 291 default : 292 break; 293 } 294 } while(ev.type != ButtonRelease); 295 XWarpPointer(AppDisplay.instance().dpy, None, c.win, 0, 0, 0, 0, c.w + c.bw - 1, c.h + c.bw - 1); 296 XUngrabPointer(AppDisplay.instance().dpy, CurrentTime); 297 while(XCheckMaskEvent(AppDisplay.instance().dpy, EnterWindowMask, &ev)) {} 298 m = recttomon(c.x, c.y, c.w, c.h); 299 if(m != selmon) { 300 sendmon(c, m); 301 selmon = m; 302 focus(null); 303 } 304 }