1 /*
  2  * winrules plugin for compiz
  3  *
  4  * Copyright (C) 2007 Bellegarde Cedric (gnumdk (at) gmail.com)
  5  * This program is free software; you can redistribute it and/or
  6  * modify it under the terms of the GNU General Public License
  7  * as published by the Free Software Foundation; either version 2
  8  * of the License, or (at your option) any later version.
  9  *
 10  * This program is distributed in the hope that it will be useful,
 11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 13  * GNU General Public License for more details.
 14  *
 15  * You should have received a copy of the GNU General Public License
 16  * along with this program; if not, write to the
 17  * Free Software Foundation, Inc.,
 18  * 51 Franklin Street, Fifth Floor,
 19  * Boston, MA  02110-1301, USA.
 20  */
 21 
 22 #include "winrules.h"
 23 
 24 COMPIZ_PLUGIN_20090315 (winrules, WinrulesPluginVTable);
 25 
 26 void
 27 WinrulesScreen::setProtocols (unsigned int protocols,
 28 		      	      Window       id)
 29 {
 30     Atom protocol[4];
 31     int  count = 0;
 32 
 33     if (protocols & CompWindowProtocolDeleteMask)
 34 	protocol[count++] = Atoms::wmDeleteWindow;
 35     if (protocols & CompWindowProtocolTakeFocusMask)
 36 	protocol[count++] = Atoms::wmTakeFocus;
 37     if (protocols & CompWindowProtocolPingMask)
 38 	protocol[count++] = Atoms::wmPing;
 39     if (protocols & CompWindowProtocolSyncRequestMask)
 40 	protocol[count++] = Atoms::wmSyncRequest;
 41 
 42     XSetWMProtocols (screen->dpy (), id, protocol, count);
 43 }
 44 
 45 bool
 46 WinrulesWindow::is ()
 47 {
 48     if (window->overrideRedirect ())
 49 	return false;
 50 
 51     if (window->wmType () & CompWindowTypeDesktopMask)
 52 	return false;
 53 
 54     return true;
 55 }
 56 
 57 bool
 58 WinrulesWindow::isFocussable () const
 59 {
 60     window->isFocussable ();
 61 
 62     return false; // We only want to return false else where we are not wrapped
 63 }
 64 
 65 bool
 66 WinrulesWindow::focus ()
 67 {
 68     window->focus ();
 69 
 70     return false; // We only want to return false for the window we are wrapped
 71 }
 72 
 73 bool
 74 WinrulesWindow::alpha () const
 75 {
 76     window->alpha ();
 77 
 78     return false; // We only want to return false else where we are not wrapped
 79 }
 80 
 81 void
 82 WinrulesWindow::setNoFocus (int        optNum)
 83 {
 84     unsigned int newProtocol = window->protocols ();
 85 
 86     WINRULES_SCREEN (screen);
 87 
 88     if (!is ())
 89 	return;
 90 
 91     if (ws->getOptions ().at (optNum). value ().match ().evaluate (window))
 92     {
 93 	if (window->protocols () & CompWindowProtocolTakeFocusMask)
 94 	{
 95 	    protocolSetMask |= (window->protocols () &
 96 				CompWindowProtocolTakeFocusMask);
 97 	    newProtocol = window->protocols () & ~CompWindowProtocolTakeFocusMask;
 98 	}
 99 	window->isFocussableSetEnabled (this, true);// causes w->isFocussable ()
100 						    // to return false
101 	window->focusSetEnabled (this, true); // causes w->focus () to return
102 					      // false for this window
103     }
104     else if ((protocolSetMask & CompWindowProtocolTakeFocusMask))
105     {
106 	newProtocol = window->protocols () |
107 	              (protocolSetMask & CompWindowProtocolTakeFocusMask);
108 	protocolSetMask &= ~CompWindowProtocolTakeFocusMask;
109 	window->isFocussableSetEnabled (this, false);
110 	window->focusSetEnabled (this, false);
111     }
112 
113     if (newProtocol != window->protocols ())
114     {
115 	ws->setProtocols (newProtocol, window->id ());
116     }
117 }
118 
119 void
120 WinrulesWindow::setNoAlpha (int        optNum)
121 {
122     WINRULES_SCREEN (screen);
123 
124     if (!is ())
125 	return;
126 
127     if (ws->getOptions ().at (optNum). value ().match ().evaluate (window))
128     {
129 	window->alphaSetEnabled (this, true); // Causes w->alpha () to return
130 					      // false
131     }
132     else
133     {
134 	window->alphaSetEnabled (this, false);
135     }
136 }
137 
138 void
139 WinrulesWindow::updateState (int        optNum,
140 		             int        mask)
141 {
142     unsigned int newState = window->state ();
143 
144     WINRULES_SCREEN (screen);
145 
146     if (!is ())
147 	return;
148 
149     if (ws->getOptions ().at (optNum). value ().match ().evaluate (window))
150     {
151 	newState |= mask;
152 	newState = window->constrainWindowState (newState, window->actions ());
153 	stateSetMask |= (newState & mask);
154     }
155     else if (stateSetMask & mask)
156     {
157 	newState &= ~mask;
158 	stateSetMask &= ~mask;
159     }
160 
161     if (newState != window->state ())
162     {
163 	window->changeState (newState);
164 
165 	if (mask & (CompWindowStateFullscreenMask |
166 		    CompWindowStateAboveMask      |
167 		    CompWindowStateBelowMask       ))
168 	    window->updateAttributes (CompStackingUpdateModeNormal);
169 	else
170 	    window->updateAttributes (CompStackingUpdateModeNone);
171     }
172 }
173 
174 void
175 WinrulesWindow::setAllowedActions (int        optNum,
176 			   	  int        action)
177 {
178     WINRULES_SCREEN (screen);
179 
180     if (!is ())
181 	return;
182 
183     if (ws->getOptions ().at (optNum). value ().match ().evaluate (window))
184 	allowedActions &= ~action;
185     else if (!(allowedActions & action))
186 	allowedActions |= action;
187 
188     window->recalcActions ();
189 }
190 
191 bool
192 WinrulesWindow::matchSizeValue (CompOption::Value::Vector matches,
193 				CompOption::Value::Vector widthValues,
194 				CompOption::Value::Vector heightValues,
195 				int	   *width,
196 				int	   *height)
197 {
198     int min;
199 
200     if (!is ())
201 	return false;
202 
203     if (window->type () & CompWindowTypeDesktopMask)
204 	return false;
205 
206     min = MIN (matches.size (), widthValues.size ());
207     min = MIN ((unsigned int) min, heightValues.size ());
208     min = MIN ((unsigned int) min, matches.size ());
209 
210     for (int i = 0; i < min; i++)
211     {
212 	if ((matches.at (i).match ().evaluate (window)))
213 	{
214 	    *width = widthValues.at (i).i ();
215 	    *height = heightValues.at (i).i ();
216 	
217 	    return true;
218 	}
219     }
220 
221     return false;
222 }
223 
224 bool
225 WinrulesWindow::matchSize (int	      *width,
226 			   int	      *height)
227 {
228     WINRULES_SCREEN (screen);
229 
230     return matchSizeValue (ws->optionGetSizeMatches (),
231 			   ws->optionGetSizeWidthValues (),
232 			   ws->optionGetSizeHeightValues (),
233 			   width, height);
234 }
235 
236 void
237 WinrulesWindow::updateWindowSize (int        width,
238 				  int        height)
239 {
240     XWindowChanges xwc;
241     unsigned int   xwcm = 0;
242 
243     if (width != window->serverWidth ())
244 	xwcm |= CWWidth;
245     if (height != window->serverHeight ())
246 	xwcm |= CWHeight;
247 
248     xwc.width = width;
249     xwc.height = height;
250 
251     if (window->mapNum () && xwcm)
252 	window->sendSyncRequest ();
253 
254     window->configureXWindow (xwcm, &xwc);
255 }
256 
257 void
258 WinrulesScreen::optionChanged (CompOption	       *option,
259 			       WinrulesOptions::Options num)
260 {
261 
262     unsigned int updateStateMask = 0, updateActionsMask = 0;
263 
264     switch (num)
265     {
266 	case WinrulesOptions::SkiptaskbarMatch:
267 	    updateStateMask = CompWindowStateSkipTaskbarMask;
268 	    break;
269 	case WinrulesOptions::SkippagerMatch:
270 	    updateStateMask = CompWindowStateSkipPagerMask;
271 	    break;
272 	case WinrulesOptions::AboveMatch:
273 	    updateStateMask = CompWindowStateAboveMask;
274 	break;
275 	case WinrulesOptions::BelowMatch:
276 	    updateStateMask = CompWindowStateBelowMask;
277 	break;
278 	case WinrulesOptions::StickyMatch:
279 	    updateStateMask = CompWindowStateStickyMask;
280 	break;
281 	case WinrulesOptions::FullscreenMatch:
282 	    updateStateMask = CompWindowStateFullscreenMask;
283 	break;
284 	case WinrulesOptions::MaximizeMatch:
285 	    updateStateMask = CompWindowStateMaximizedHorzMask |
286 			      CompWindowStateMaximizedVertMask;
287 	break;
288 	case WinrulesOptions::NoMoveMatch:
CID 12431 - DEADCODE
Assigning: "updateActionsMask" = "1U".
289 	    updateActionsMask = CompWindowActionMoveMask;
290 	break;
291 	case WinrulesOptions::NoResizeMatch:
CID 12431 - DEADCODE
Assigning: "updateActionsMask" = "2U".
292 	    updateActionsMask = CompWindowActionResizeMask;
293 	break;
294 	case WinrulesOptions::NoMinimizeMatch:
CID 12431 - DEADCODE
Assigning: "updateActionsMask" = "8U".
295 	    updateActionsMask = CompWindowActionMinimizeMask;
296 	break;
297 	case WinrulesOptions::NoMaximizeMatch:
CID 12431 - DEADCODE
Assigning: "updateActionsMask" = "48U".
298 	    updateActionsMask = CompWindowActionMaximizeVertMask |
299 		                CompWindowActionMaximizeHorzMask;
300 	break;
301 	case WinrulesOptions::NoCloseMatch:
CID 12431 - DEADCODE
Assigning: "updateActionsMask" = "128U".
302 	    updateActionsMask = CompWindowActionCloseMask;
303 	break;
304 	case WinrulesOptions::NoArgbMatch:
305 	    foreach (CompWindow *w, screen->windows ())
306 	    {
307 		WINRULES_WINDOW (w);
308 		ww->setNoAlpha (num);
309 	    }
310 
311 	    return;
312 	break;
313 	case WinrulesOptions::SizeMatches:
314 	    foreach (CompOption::Value &v, option->value ().list ())
315 	    {
316 	        CompMatch &m = v.match ();
317 		m.update ();
318 	    }
319 	    return;
320 	break;
321 	default:
322 	    return;
323 	break;
324     }
325 
326     if (updateStateMask)
327     {
328 	/* We traverse a copy of the list here because windows can be unhooked
329 	 * on state change rather than the delayed unhook that happens in <0.8.x
330 	 */
331 
332 	CompWindowList windows = screen->windows ();
333 
334 	foreach (CompWindow *w, windows)
335 	{
336 	    WINRULES_WINDOW (w);
337 	    ww->updateState (num, updateStateMask);
338 	}
339 
340 	return;
341     }
342 
CID 12431 - DEADCODE
At condition "updateActionsMask", the value of "updateActionsMask" must be in one of the following intervals: {[1,2], [8,8], [48,48], [128,128]}.
The condition "updateActionsMask" must be true.
343     if (updateActionsMask)
344     {
345 	foreach (CompWindow *w, screen->windows ())
346 	{
347 	    WINRULES_WINDOW (w);
348 	    ww->setAllowedActions (num, updateActionsMask);
349 	}
350 
351 	return;
352     }
353 
CID 12431 - DEADCODE
Execution cannot reach this statement "return;".
354     return;
355 }
356 
357 
358 bool
359 WinrulesWindow::applyRules ()
360 {
361     int        width, height;
362 
363     updateState (WinrulesOptions::SkiptaskbarMatch,
364 		 CompWindowStateSkipTaskbarMask);
365 
366     updateState (WinrulesOptions::SkippagerMatch,
367 		 CompWindowStateSkipPagerMask);
368 
369     updateState (WinrulesOptions::AboveMatch,
370 		 CompWindowStateAboveMask);
371 
372     updateState (WinrulesOptions::BelowMatch,
373 		 CompWindowStateBelowMask);
374 
375     updateState (WinrulesOptions::StickyMatch,
376 		 CompWindowStateStickyMask);
377 
378     updateState (WinrulesOptions::FullscreenMatch,
379 		 CompWindowStateFullscreenMask);
380 
381     updateState (WinrulesOptions::MaximizeMatch,
382 		 CompWindowStateMaximizedHorzMask |
383 		 CompWindowStateMaximizedVertMask);
384 
385     setAllowedActions (WinrulesOptions::NoMoveMatch,
386 		       CompWindowActionMoveMask);
387 
388     setAllowedActions (WinrulesOptions::NoResizeMatch,
389 		       CompWindowActionResizeMask);
390 
391     setAllowedActions (WinrulesOptions::NoMinimizeMatch,
392 		       CompWindowActionMinimizeMask);
393 
394     setAllowedActions (WinrulesOptions::NoMaximizeMatch,
395 		       CompWindowActionMaximizeVertMask |
396 		       CompWindowActionMaximizeHorzMask);
397 
398     setAllowedActions (WinrulesOptions::NoCloseMatch,
399 		       CompWindowActionCloseMask);
400 
401     setNoAlpha (WinrulesOptions::NoArgbMatch);
402 
403     if (matchSize (&width, &height))
404 	updateWindowSize (width, height);
405 
406     return false;
407 }
408 
409 
410 void
411 WinrulesScreen::handleEvent (XEvent *event)
412 {
413     if (event->type == MapRequest)
414     {
415 	CompWindow *w = screen->findWindow (event->xmap.window);
416 	if (w)
417 	{
418 	    WINRULES_WINDOW (w);
419 	    ww->setNoFocus (WinrulesOptions::NoFocusMatch);
420 	    ww->applyRules ();
421 	}
422     }
423 
424     screen->handleEvent (event);
425 
426 }
427 
428 void
429 WinrulesWindow::getAllowedActions (unsigned int &setActions,
430 				   unsigned int &clearActions)
431 {
432     window->getAllowedActions (setActions, clearActions);
433 
434     clearActions |= ~allowedActions;
435 }
436 
437 void
438 WinrulesScreen::matchExpHandlerChanged ()
439 {
440     screen->matchExpHandlerChanged ();
441 
442     /* match options are up to date after the call to matchExpHandlerChanged */
443     foreach (CompWindow *w, screen->windows ())
444     {
445 	WINRULES_WINDOW (w);
446 	ww->applyRules ();
447     }
448 }
449 
450 void
451 WinrulesScreen::matchPropertyChanged (CompWindow *w)
452 {
453     WINRULES_WINDOW (w);
454 
455     /* Re-apply rules on match property change */
456     ww->applyRules ();
457 
458     screen->matchPropertyChanged (w);
459 }
460 
461 WinrulesScreen::WinrulesScreen (CompScreen *screen) :
462     PluginClassHandler <WinrulesScreen, CompScreen> (screen)
463 {
464     ScreenInterface::setHandler (screen);
465 
466     optionSetSkiptaskbarMatchNotify (boost::bind
467 					(&WinrulesScreen::optionChanged, this,
468 					 _1, _2));
469 
470     optionSetSkippagerMatchNotify (boost::bind
471 				   (&WinrulesScreen::optionChanged, this,
472 				    _1, _2));
473 
474     optionSetAboveMatchNotify (boost::bind
475 				(&WinrulesScreen::optionChanged, this,
476 				 _1, _2));
477 
478     optionSetBelowMatchNotify (boost::bind
479 				(&WinrulesScreen::optionChanged, this,
480 				 _1, _2));
481 
482     optionSetFullscreenMatchNotify (boost::bind
483 				    (&WinrulesScreen::optionChanged, this,
484 				     _1, _2));
485 
486     optionSetStickyMatchNotify (boost::bind
487 				(&WinrulesScreen::optionChanged, this,
488 				_1, _2));
489 
490     optionSetMaximizeMatchNotify (boost::bind
491 				  (&WinrulesScreen::optionChanged, this,
492 				   _1, _2));
493 
494     optionSetNoArgbMatchNotify (boost::bind
495 				(&WinrulesScreen::optionChanged, this,
496 				 _1, _2));
497 
498     optionSetNoMoveMatchNotify (boost::bind
499 				(&WinrulesScreen::optionChanged, this,
500 				 _1, _2));
501 
502     optionSetNoResizeMatchNotify (boost::bind
503 				  (&WinrulesScreen::optionChanged, this,
504 				   _1, _2));
505 
506     optionSetNoMinimizeMatchNotify (boost::bind
507 				    (&WinrulesScreen::optionChanged, this,
508 				     _1, _2));
509 
510     optionSetNoMaximizeMatchNotify (boost::bind
511 				    (&WinrulesScreen::optionChanged, this,
512 				     _1, _2));
513 
514     optionSetNoCloseMatchNotify (boost::bind
515 				 (&WinrulesScreen::optionChanged, this,
516 				  _1, _2));
517 
518     optionSetNoFocusMatchNotify (boost::bind
519 				 (&WinrulesScreen::optionChanged, this,
520 				  _1, _2));
521 
522 }
523 
524 WinrulesWindow::WinrulesWindow (CompWindow *window) :
525     PluginClassHandler <WinrulesWindow, CompWindow> (window),
526     window (window),
527     allowedActions (~0),
528     stateSetMask (0),
529     protocolSetMask (0)
530 {
531     CompTimer timer;
532 
533     WindowInterface::setHandler (window);
534 
535     window->isFocussableSetEnabled (this, false);
536     window->alphaSetEnabled (this, false);
537     window->focusSetEnabled (this, false);
538 
539     timer.setCallback (boost::bind(&WinrulesWindow::applyRules, this));
540     timer.setTimes (0, 0);
541 
542     timer.start ();
543 
544 }
545 
546 bool
547 WinrulesPluginVTable::init ()
548 {
549     if (!CompPlugin::checkPluginABI ("core", CORE_ABIVERSION))
550 	return false;
551 
552     return true;
553 }