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 }