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 = ®.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, ®);
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, ®);
411 }
412