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 }