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( 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 }