1 /**
2  *
3  * Compiz group plugin
4  *
5  * selection.c
6  *
7  * Copyright : (C) 2006-2007 by Patrick Niklaus, Roi Cohen, Danny Baumann
8  * Authors: Patrick Niklaus <patrick.niklaus@googlemail.com>
9  *          Roi Cohen       <roico.beryl@gmail.com>
10  *          Danny Baumann   <maniac@opencompositing.org>
11  *
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  **/
24 
25 #include "group-internal.h"
26 
27 /*
28  * groupWindowInRegion
29  *
30  */
31 static Bool
groupWindowInRegion(CompWindow * w,Region src,float precision)32 groupWindowInRegion (CompWindow *w,
33 		     Region     src,
34 		     float      precision)
35 {
36     Region buf;
37     int    i;
38     int    area = 0;
39     BOX    *box;
40 
41     buf = XCreateRegion ();
42     if (!buf)
43 	return FALSE;
44 
45     XIntersectRegion (w->region, src, buf);
46 
47     /* buf area */
48     for (i = 0; i < buf->numRects; i++)
49     {
50 	box = &buf->rects[i];
51 	area += (box->x2 - box->x1) * (box->y2 - box->y1); /* width * height */
52     }
53 
54     XDestroyRegion (buf);
55 
56     if (area >= WIN_WIDTH (w) * WIN_HEIGHT (w) * precision)
57     {
58 	XSubtractRegion (src, w->region, src);
59 	return TRUE;
60     }
61 
62     return FALSE;
63 }
64 
65 /*
66  * groupFindGroupInWindows
67  *
68  */
69 static Bool
groupFindGroupInWindows(GroupSelection * group,CompWindow ** windows,int nWins)70 groupFindGroupInWindows (GroupSelection *group,
71 			 CompWindow     **windows,
72 			 int            nWins)
73 {
74     int i;
75 
76     for (i = 0; i < nWins; i++)
77     {
78 	CompWindow *cw = windows[i];
79 	GROUP_WINDOW (cw);
80 
81 	if (gw->group == group)
82 	    return TRUE;
83     }
84 
85     return FALSE;
86 }
87 
88 /*
89  * groupFindWindowsInRegion
90  *
91  */
92 static CompWindow**
groupFindWindowsInRegion(CompScreen * s,Region reg,int * c)93 groupFindWindowsInRegion (CompScreen *s,
94 			  Region     reg,
95 			  int        *c)
96 {
97     float      precision = groupGetSelectPrecision (s) / 100.0f;
98     CompWindow **ret = NULL;
99     int        count = 0;
100     CompWindow *w;
101 
102     for (w = s->reverseWindows; w; w = w->prev)
103     {
104 	if (groupIsGroupWindow (w) &&
105 	    groupWindowInRegion (w, reg, precision))
106 	{
107 	    GROUP_WINDOW (w);
108 	    if (gw->group && groupFindGroupInWindows (gw->group, ret, count))
109 		continue;
110 
111 	    ret = realloc (ret, sizeof (CompWindow) * (count + 1));
112 	    ret[count] = w;
113 
114 	    count++;
115 	}
116     }
117 
118     (*c) = count;
119     return ret;
120 }
121 
122 /*
123  * groupDeleteSelectionWindow
124  *
125  */
126 static void
groupDeleteSelectionWindow(CompWindow * w)127 groupDeleteSelectionWindow (CompWindow *w)
128 {
129     GROUP_SCREEN (w->screen);
130     GROUP_WINDOW (w);
131 
132     if (gs->tmpSel.nWins > 0 && gs->tmpSel.windows)
133     {
134 	CompWindow **buf = gs->tmpSel.windows;
135 	int        counter = 0;
136 	int        i;
137 
138 	gs->tmpSel.windows = calloc (gs->tmpSel.nWins - 1,
139 				     sizeof (CompWindow *));
140 
141 	for (i = 0; i < gs->tmpSel.nWins; i++)
142 	{
143 	    if (buf[i]->id == w->id)
144 		continue;
145 
146 	    gs->tmpSel.windows[counter++] = buf[i];
147 	}
148 
149 	gs->tmpSel.nWins = counter;
150 	free (buf);
151     }
152 
153     gw->inSelection = FALSE;
154 }
155 
156 /*
157  * groupAddWindowToSelection
158  *
159  */
160 static void
groupAddWindowToSelection(CompWindow * w)161 groupAddWindowToSelection (CompWindow *w)
162 {
163     GROUP_SCREEN (w->screen);
164     GROUP_WINDOW (w);
165 
166     gs->tmpSel.windows = realloc (gs->tmpSel.windows,
167 				  sizeof (CompWindow *) *
168 				  (gs->tmpSel.nWins + 1));
169 
170     gs->tmpSel.windows[gs->tmpSel.nWins] = w;
171     gs->tmpSel.nWins++;
172 
173     gw->inSelection = TRUE;
174 }
175 
176 /*
177  * groupSelectWindow
178  *
179  */
180 static void
groupSelectWindow(CompWindow * w)181 groupSelectWindow (CompWindow *w)
182 {
183     GROUP_SCREEN (w->screen);
184     GROUP_WINDOW (w);
185 
186     /* filter out windows we don't want to be groupable */
187     if (!groupIsGroupWindow (w))
188 	return;
189 
190     if (gw->inSelection)
191     {
192 	if (gw->group)
193 	{
194 	    /* unselect group */
195 	    GroupSelection *group = gw->group;
196 	    CompWindow     **buf = gs->tmpSel.windows;
197 	    int            i, counter = 0;
198 
199 	    /* Faster than doing groupDeleteSelectionWindow
200 	       for each window in this group. */
201 	    gs->tmpSel.windows = calloc (gs->tmpSel.nWins - gw->group->nWins,
202 					 sizeof (CompWindow *));
203 
204 	    for (i = 0; i < gs->tmpSel.nWins; i++)
205 	    {
206 		CompWindow *cw = buf[i];
207 		GROUP_WINDOW (cw);
208 
209 		if (gw->group == group)
210 		{
211 		    gw->inSelection = FALSE;
212 		    addWindowDamage (cw);
213 		    continue;
214 		}
215 
216 		gs->tmpSel.windows[counter++] = buf[i];
217 	    }
218 	    gs->tmpSel.nWins = counter;
219 	    free (buf);
220 	}
221 	else
222 	{
223 	    /* unselect single window */
224 	    groupDeleteSelectionWindow (w);
225 	    addWindowDamage (w);
226 	}
227     }
228     else
229     {
230 	if (gw->group)
231 	{
232 	    /* select group */
233 	    int i;
234 	    for (i = 0; i < gw->group->nWins; i++)
235 	    {
236 		CompWindow *cw = gw->group->windows[i];
237 
238 		groupAddWindowToSelection (cw);
239 		addWindowDamage (cw);
240 	    }
241 	}
242 	else
243 	{
244 	    /* select single window */
245 	    groupAddWindowToSelection (w);
246 	    addWindowDamage (w);
247 	}
248     }
249 }
250 
251 /*
252  * groupSelectSingle
253  *
254  */
255 Bool
groupSelectSingle(CompDisplay * d,CompAction * action,CompActionState state,CompOption * option,int nOption)256 groupSelectSingle (CompDisplay     *d,
257 		   CompAction      *action,
258 		   CompActionState state,
259 		   CompOption      *option,
260 		   int             nOption)
261 {
262     Window     xid;
263     CompWindow *w;
264 
265     xid = getIntOptionNamed (option, nOption, "window", 0);
266     w   = findWindowAtDisplay (d, xid);
267     if (w)
268 	groupSelectWindow (w);
269 
270     return TRUE;
271 }
272 
273 /*
274  * groupSelect
275  *
276  */
277 Bool
groupSelect(CompDisplay * d,CompAction * action,CompActionState state,CompOption * option,int nOption)278 groupSelect (CompDisplay     *d,
279 	     CompAction      *action,
280 	     CompActionState state,
281 	     CompOption      *option,
282 	     int             nOption)
283 {
284     Window     xid;
285     CompWindow *w;
286 
287     xid = getIntOptionNamed (option, nOption, "window", 0);
288     w   = findWindowAtDisplay (d, xid);
289     if (w)
290     {
291 	GROUP_SCREEN (w->screen);
292 
293 	if (gs->grabState == ScreenGrabNone)
294 	{
295 	    groupGrabScreen (w->screen, ScreenGrabSelect);
296 
297 	    if (state & CompActionStateInitKey)
298 		action->state |= CompActionStateTermKey;
299 
300 	    if (state & CompActionStateInitButton)
301 		action->state |= CompActionStateTermButton;
302 
303 	    gs->x1 = gs->x2 = pointerX;
304 	    gs->y1 = gs->y2 = pointerY;
305 	}
306 
307 	return TRUE;
308     }
309 
310     return FALSE;
311 }
312 
313 /*
314  * groupSelectTerminate
315  *
316  */
317 Bool
groupSelectTerminate(CompDisplay * d,CompAction * action,CompActionState state,CompOption * option,int nOption)318 groupSelectTerminate (CompDisplay     *d,
319 		      CompAction      *action,
320 		      CompActionState state,
321 		      CompOption      *option,
322 		      int             nOption)
323 {
324     CompScreen *s;
325     Window     xid;
326 
327     xid = getIntOptionNamed(option, nOption, "root", 0);
328     s = findScreenAtDisplay (d, xid);
329     if (s)
330     {
331 	GROUP_SCREEN (s);
332 
333 	if (gs->grabState == ScreenGrabSelect)
334 	{
335 	    groupGrabScreen (s, ScreenGrabNone);
336 
337 	    if (gs->x1 != gs->x2 && gs->y1 != gs->y2)
338 	    {
339 		Region     reg;
340 		XRectangle rect;
341 		int        count;
342 		CompWindow **ws;
343 
344 		reg = XCreateRegion ();
345 		if (reg)
346 		{
347 		    rect.x      = MIN (gs->x1, gs->x2) - 2;
348 		    rect.y      = MIN (gs->y1, gs->y2) - 2;
349 		    rect.width  = MAX (gs->x1, gs->x2) -
350 			          MIN (gs->x1, gs->x2) + 4;
351 		    rect.height = MAX (gs->y1, gs->y2) -
352 			          MIN (gs->y1, gs->y2) + 4;
353 		    XUnionRectWithRegion (&rect, reg, reg);
354 
355 		    damageScreenRegion (s, reg);
356 
357 		    ws = groupFindWindowsInRegion (s, reg, &count);
358 		    if (ws)
359 		    {
360 			/* select windows */
361 			int i;
362 			for (i = 0; i < count; i++)
363 			    groupSelectWindow (ws[i]);
364 
365 			if (groupGetAutoGroup(s))
366 			    groupGroupWindows (d, NULL, 0, NULL, 0);
367 
368 			free (ws);
369 		    }
370 		    XDestroyRegion (reg);
371 		}
372 	    }
373 	}
374     }
375 
376     action->state &= ~(CompActionStateTermKey | CompActionStateTermButton);
377 
378     return FALSE;
379 }
380 
381 /*
382  * groupDamageSelectionRect
383  *
384  */
385 void
groupDamageSelectionRect(CompScreen * s,int xRoot,int yRoot)386 groupDamageSelectionRect (CompScreen *s,
387 			  int        xRoot,
388 			  int        yRoot)
389 {
390     REGION reg;
391 
392     GROUP_SCREEN (s);
393 
394     reg.rects = &reg.extents;
395     reg.numRects = 1;
396 
397     reg.extents.x1 = MIN (gs->x1, gs->x2) - 5;
398     reg.extents.y1 = MIN (gs->y1, gs->y2) - 5;
399     reg.extents.x2 = MAX (gs->x1, gs->x2) + 5;
400     reg.extents.y2 = MAX (gs->y1, gs->y2) + 5;
401     damageScreenRegion (s, &reg);
402 
403     gs->x2 = xRoot;
404     gs->y2 = yRoot;
405 
406     reg.extents.x1 = MIN (gs->x1, gs->x2) - 5;
407     reg.extents.y1 = MIN (gs->y1, gs->y2) - 5;
408     reg.extents.x2 = MAX (gs->x1, gs->x2) + 5;
409     reg.extents.y2 = MAX (gs->y1, gs->y2) + 5;
410     damageScreenRegion (s, &reg);
411 }
412