1 /*
  2  * Copyright ���� 2005 Novell, Inc.
  3  *
  4  * Permission to use, copy, modify, distribute, and sell this software
  5  * and its documentation for any purpose is hereby granted without
  6  * fee, provided that the above copyright notice appear in all copies
  7  * and that both that copyright notice and this permission notice
  8  * appear in supporting documentation, and that the name of
  9  * Novell, Inc. not be used in advertising or publicity pertaining to
 10  * distribution of the software without specific, written prior permission.
 11  * Novell, Inc. makes no representations about the suitability of this
 12  * software for any purpose. It is provided "as is" without express or
 13  * implied warranty.
 14  *
 15  * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 16  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
 17  * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 18  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
 19  * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
 20  * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 21  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 22  *
 23  * Author: David Reveman <davidr@novell.com>
 24  */
 25 
 26 #include <compiztoolbox/compiztoolbox.h>
 27 #include "compiztoolbox_options.h"
 28 
 29 #include <core/abiversion.h>
 30 #include <core/propertywriter.h>
 31 
 32 const unsigned short ICON_SIZE = 48;
 33 const unsigned int MAX_ICON_SIZE = 256;
 34 
 35 bool openGLAvailable;
 36 
 37 class CompizToolboxScreen :
 38     public PluginClassHandler <CompizToolboxScreen, CompScreen>,
 39     public CompiztoolboxOptions
 40 {
 41     public:
 42 	CompizToolboxScreen (CompScreen *);
 43 };
 44 
 45 class CompizToolboxPluginVTable :
 46     public CompPlugin::VTableForScreen <CompizToolboxScreen>
 47 {
 48     public:
 49 	bool init ();
 50 	void fini ();
 51 };
 52 
 53 COMPIZ_PLUGIN_20090315 (compiztoolbox, CompizToolboxPluginVTable)
 54 
 55 CompString
 56 getXDGUserDir (XDGUserDir userDir)
 57 {
 58     std::ifstream userDirsFile;
 59     CompString userDirsFilePath;
 60     const char *userDirsPathSuffix = "/user-dirs.dirs";
 61     const char *varNames[8] =
 62     {
 63 	"XDG_DESKTOP_DIR",
 64 	"XDG_DOWNLOAD_DIR",
 65 	"XDG_TEMPLATES_DIR",
 66 	"XDG_PUBLICSHARE_DIR",
 67 	"XDG_DOCUMENTS_DIR",
 68 	"XDG_MUSIC_DIR",
 69 	"XDG_PICTURES_DIR",
 70 	"XDG_VIDEOS_DIR"
 71     };
 72     const char *varName = varNames[userDir];
 73     size_t varLength = strlen (varName);
 74 
 75     char *home = getenv ("HOME");
 76     if (!(home && strlen (home)))
 77 	return "";
 78 
 79     char *configHome = getenv ("XDG_CONFIG_HOME");
 80     if (configHome && strlen (configHome))
 81     {
 82 	userDirsFilePath = configHome;
 83 	userDirsFilePath += userDirsPathSuffix;
 84     }
 85     else
 86     {
 87 	userDirsFilePath = home;
 88 	userDirsFilePath =
 89 	    userDirsFilePath + "/.config" + userDirsPathSuffix;
 90     }
 91     userDirsFile.open (userDirsFilePath.c_str (), std::ifstream::in);
 92     if (!userDirsFile.is_open ())
 93 	return "";
 94 
 95     // The user-dirs file has lines like:
 96     // XDG_DESKTOP_DIR="$HOME/Desktop"
 97     // Read it line by line until the desired directory is found.
 98     while (!userDirsFile.eof())
 99     {
100 	CompString line;
101 	getline (userDirsFile, line);
102 
103 	size_t varPos = line.find (varName);
104 	if (varPos != CompString::npos) // if found
105 	{
106 	    userDirsFile.close ();
107 
108 	    // Skip the =" part
109 	    size_t valueStartPos = varPos + varLength + 2;
110 
111 	    // Ignore the " at the end
112 	    CompString value = line.substr (valueStartPos,
113 					    line.length () - valueStartPos - 1);
114 
115 	    if (value.substr (0, 5) == "$HOME")
116 		return CompString (home) + value.substr (5);
117 	    else if (value.substr (0, 7) == "${HOME}")
118 		return CompString (home) + value.substr (7);
119 	    else
120 		return value;
121 	}
122     }
123     userDirsFile.close ();
124     return "";
125 }
126 
127 
128 void
129 BaseSwitchScreen::setSelectedWindowHint (bool focus)
130 {
131     Window selectedWindowId = None;
132     CompOption::Vector opts;
133     CompOption::Value  v;
134 
135     if (selectedWindow && !selectedWindow->destroyed ())
136     {
137 	selectedWindowId = selectedWindow->id ();
138 
139 	/* FIXME: Changing the input focus here will
140 	 * screw up the ordering of windows in
141 	 * the switcher, so we probably want to avoid that
142 	 */
143 	if (focus)
144 	    selectedWindow->moveInputFocusTo ();
145     }
146 
147     v = CompOption::Value ((int) selectedWindowId);
148     opts = selectWinAtom.getReadTemplate ();
149     opts.at (0).set (v);
150 
151     selectWinAtom.updateProperty (popupWindow, opts, XA_WINDOW);
152 }
153 
154 void
155 BaseSwitchScreen::getMinimizedAndMatch (bool &minimizedOption,
156 					CompMatch *&matchOption)
157 {
158     minimizedOption = false;
159     matchOption = NULL;
160 }
161 
162 bool
163 BaseSwitchWindow::isSwitchWin (bool removing)
164 {
165     bool minimizedOption;
166     CompMatch *matchOption;
167     baseScreen->getMinimizedAndMatch (minimizedOption, matchOption);
168 
169     if (!removing && window->destroyed ())
170 	return false;
171 
172     if (!removing && (!window->isViewable () || !window->isMapped ()))
173     {
174 	if (minimizedOption)
175 	{
176 	    if (!window->minimized () && !window->inShowDesktopMode () &&
177 		!window->shaded ())
178 		return false;
179 	}
180 	else
181 	{
182 	    return false;
183 	}
184     }
185 
186     if (!window->isFocussable ())
187 	return false;
188 
189     if (window->overrideRedirect ())
190 	return false;
191 
192     if (baseScreen->selection == Panels)
193     {
194 	if (!(window->type () &
195 	      (CompWindowTypeDockMask | CompWindowTypeDesktopMask)))
196 	    return false;
197     }
198     else
199     {
200 	if (window->wmType () &
201 	    (CompWindowTypeDockMask | CompWindowTypeDesktopMask))
202 	    return false;
203 
204 	if (window->state () & CompWindowStateSkipTaskbarMask)
205 	    return false;
206 
207 	if (matchOption && !matchOption->evaluate (window))
208 	    return false;
209     }
210 
211     if (!removing && baseScreen->selection == CurrentViewport)
212     {
213 	if (!window->mapNum () || !window->isViewable ())
214 	{
215 	    const CompWindow::Geometry &sg = window->serverGeometry ();
216 	    if (sg.x () + sg.width ()  <= 0    ||
217 		sg.y () + sg.height () <= 0    ||
218 		sg.x () >= (int) ::screen->width () ||
219 		sg.y () >= (int) ::screen->height ())
220 		return false;
221 	}
222 	else
223 	{
224 	    if (!window->focus ())
225 		return false;
226 	}
227     }
228 
229     return true;
230 }
231 
232 void
233 BaseSwitchScreen::activateEvent (bool activating)
234 {
235     CompOption::Vector o (0);
236 
237     o.push_back (CompOption ("root", CompOption::TypeInt));
238     o.push_back (CompOption ("active", CompOption::TypeBool));
239 
240     o[0].value ().set ((int) ::screen->root ());
241     o[1].value ().set (activating);
242 
243     ::screen->handleCompizEvent ("switcher", "activate", o);
244 }
245 
246 bool
247 BaseSwitchScreen::compareWindows (CompWindow *w1,
248 				  CompWindow *w2)
249 {
250     if (w1->mapNum () && !w2->mapNum ())
251 	return true;
252 
253     if (w2->mapNum () && !w1->mapNum ())
254 	return false;
255 
256     return w2->activeNum () < w1->activeNum ();
257 }
258 
259 CompWindow *
260 BaseSwitchScreen::switchToWindow (bool toNext,
261 				  bool autoChangeVPOption,
262 				  bool focus)
263 {
264     CompWindow               *w = NULL;
265     CompWindowList::iterator it;
266 
267     int cur = 0;
268     int nextIdx = 0;
269 
270     if (!grabIndex)
271 	return NULL;
272 
273     for (it = windows.begin (); it != windows.end (); ++it, ++cur)
274     {
275 	if (*it == selectedWindow)
276 	    break;
277     }
278 
279     if (it == windows.end ())
280 	return NULL;
281 
282     if (toNext)
283     {
284 	++it;
285 	if (it == windows.end ())
286 	    w = windows.front ();
287 	else
288 	    w = *it;
289 	nextIdx = (cur + 1) % windows.size ();
290     }
291     else
292     {
293 	if (it == windows.begin ())
294 	    w = windows.back ();
295 	else
296 	    w = *--it;
297 	nextIdx = (cur + windows.size () - 1) % windows.size ();
298     }
299 
300     if (w)
301     {
302 	CompWindow *old = selectedWindow;
303 
304 	if (selection == AllViewports && autoChangeVPOption)
305 	{
306 	    XEvent xev;
307 	    CompPoint pnt = w->defaultViewport ();
308 
309 	    xev.xclient.type = ClientMessage;
310 	    xev.xclient.display = ::screen->dpy ();
311 	    xev.xclient.format = 32;
312 
313 	    xev.xclient.message_type = Atoms::desktopViewport;
314 	    xev.xclient.window = ::screen->root ();
315 
316 	    xev.xclient.data.l[0] = pnt.x () * ::screen->width ();
317 	    xev.xclient.data.l[1] = pnt.y () * ::screen->height ();
318 	    xev.xclient.data.l[2] = 0;
319 	    xev.xclient.data.l[3] = 0;
320 	    xev.xclient.data.l[4] = 0;
321 
322 	    XSendEvent (::screen->dpy (), ::screen->root (), false,
323 			SubstructureRedirectMask | SubstructureNotifyMask,
324 			&xev);
325 	}
326 
327 	lastActiveNum  = w->activeNum ();
328 	selectedWindow = w;
329 
330 	if (old != w)
331 	    handleSelectionChange (toNext, nextIdx);
332 
333 	if (popupWindow)
334 	{
335 	    CompWindow *popup;
336 
337 	    popup = ::screen->findWindow (popupWindow);
338 	    if (popup)
339 		CompositeWindow::get (popup)->addDamage ();
340 
341 	    setSelectedWindowHint (focus);
342 	}
343 
344 	doWindowDamage (w);
345 
346 	if (old && !old->destroyed ())
347 	    doWindowDamage (old);
348     }
349 
350     return w;
351 }
352 
353 void
354 BaseSwitchScreen::doWindowDamage (CompWindow *w)
355 {
356     CompositeWindow::get (w)->addDamage ();
357 }
358 
359 Visual *
360 BaseSwitchScreen::findArgbVisual (Display *dpy, int scr)
361 {
362     XVisualInfo		*xvi;
363     XVisualInfo		temp;
364     int			nvi;
365     int			i;
366     XRenderPictFormat	*format;
367     Visual		*visual;
368 
369     temp.screen  = scr;
370     temp.depth   = 32;
371     temp.c_class = TrueColor;
372 
373     xvi = XGetVisualInfo (dpy,
374 			  VisualScreenMask |
375 			  VisualDepthMask  |
376 			  VisualClassMask,
377 			  &temp,
378 			  &nvi);
379     if (!xvi)
380 	return 0;
381 
382     visual = 0;
383     for (i = 0; i < nvi; i++)
384     {
385 	format = XRenderFindVisualFormat (dpy, xvi[i].visual);
386 	if (format->type == PictTypeDirect && format->direct.alphaMask)
387 	{
388 	    visual = xvi[i].visual;
389 	    break;
390 	}
391     }
392 
393     XFree (xvi);
394 
395     return visual;
396 }
397 
398 void
399 BaseSwitchWindow::paintThumb (const GLWindowPaintAttrib &attrib,
400 			      const GLMatrix            &transform,
401 			      unsigned int              mask,
402 			      int                       x,
403 			      int                       y,
404 			      int                       width1,
405 			      int                       height1,
406 			      int                       width2,
407 			      int                       height2)
408 {
409     if (!openGLAvailable)
410 	return;
411 
412     GLWindowPaintAttrib  sAttrib (attrib);
413     IconMode             iconMode;
414     int                  wx, wy;
415     float                width, height;
416     GLTexture            *icon = NULL;
417     const CompWindow::Geometry &g = window->geometry ();
418 
419     mask |= PAINT_WINDOW_TRANSFORMED_MASK;
420 
421     if (gWindow->textures ().empty ())
422 	iconMode = ShowIconOnly;
423     else
424 	iconMode = getIconMode ();
425 
426     if (window->mapNum ())
427     {
428 	if (gWindow->textures ().empty ())
429 	    gWindow->bind ();
430     }
431 
432     if (iconMode != ShowIconOnly)
433     {
434 	GLenum   filter;
435 	GLMatrix wTransform (transform);
436 	int      ww, wh;
437 	int      addWindowGeometryIndex =
438 	    gWindow->glAddGeometryGetCurrentIndex ();
439 
440 	width  = width1;
441 	height = height1;
442 
443 	ww = window->borderRect ().width ();
444 	wh = window->borderRect ().height ();
445 
446 	if (ww > width)
447 	    sAttrib.xScale = width / ww;
448 	else
449 	    sAttrib.xScale = 1.0f;
450 
451 	if (wh > height)
452 	    sAttrib.yScale = height / wh;
453 	else
454 	    sAttrib.yScale = 1.0f;
455 
456 	if (sAttrib.xScale < sAttrib.yScale)
457 	    sAttrib.yScale = sAttrib.xScale;
458 	else
459 	    sAttrib.xScale = sAttrib.yScale;
460 
461 	width  = ww * sAttrib.xScale;
462 	height = wh * sAttrib.yScale;
463 
464 	updateIconPos (wx, wy, x, y, width, height);
465 
466 	sAttrib.xTranslate = wx - g.x () +
467 			     window->border ().left * sAttrib.xScale;
468 	sAttrib.yTranslate = wy - g.y () +
469 			     window->border ().top  * sAttrib.yScale;
470 
471 	if (window->alpha () || sAttrib.opacity != OPAQUE)
472 	    mask |= PAINT_WINDOW_TRANSLUCENT_MASK;
473 
474 	wTransform.translate (g.x (), g.y (), 0.0f);
475 	wTransform.scale (sAttrib.xScale, sAttrib.yScale, 1.0f);
476 	wTransform.translate (sAttrib.xTranslate / sAttrib.xScale - g.x (),
477 			      sAttrib.yTranslate / sAttrib.yScale - g.y (),
478 			      0.0f);
479 
480 	filter = gScreen->textureFilter ();
481 
482 	if (baseScreen->getMipmap ())
483 	    gScreen->setTextureFilter (GL_LINEAR_MIPMAP_LINEAR);
484 
485 	/* XXX: replacing the addWindowGeometry function like this is
486 	   very ugly but necessary until the vertex stage has been made
487 	   fully pluggable. */
488 	gWindow->glAddGeometrySetCurrentIndex (MAXSHORT);
489 	gWindow->glDraw (wTransform, sAttrib, infiniteRegion, mask);
490 	gWindow->glAddGeometrySetCurrentIndex (addWindowGeometryIndex);
491 
492 	gScreen->setTextureFilter (filter);
493 
494 	if (iconMode != HideIcon)
495 	{
496 	    icon = gWindow->getIcon (MAX_ICON_SIZE, MAX_ICON_SIZE);
497 	    if (icon)
498 		updateIconTexturedWindow (sAttrib, wx, wy, x, y, icon);
499 	}
500     }
501     else
502     {
503 	width  = width2;
504 	height = height2;
505 
506 	/* try to get a matching icon first */
507 	icon = gWindow->getIcon (width, height);
508 	/* if none found, try a large one */
509 	if (!icon)
510 	    icon = gWindow->getIcon (MAX_ICON_SIZE, MAX_ICON_SIZE);
511 	if (!icon)
512 	    icon = gScreen->defaultIcon ();
513 
514 	if (icon)
515 	    updateIconNontexturedWindow (sAttrib, wx, wy,
516 					 width, height, x, y, icon);
517     }
518 
519     if (icon)
520     {
521 	CompRegion iconReg (g.x (), g.y (), icon->width (), icon->height ());
522 	GLTexture::MatrixList matrix (1);
523 	int addWindowGeometryIndex = gWindow->glAddGeometryGetCurrentIndex ();
524 
525 	mask |= PAINT_WINDOW_BLEND_MASK;
526 
527 	matrix[0] = icon->matrix ();
528 	matrix[0].x0 -= (g.x () * matrix[0].xx);
529 	matrix[0].y0 -= (g.y () * matrix[0].yy);
530 
531 	sAttrib.xTranslate = wx - g.x ();
532 	sAttrib.yTranslate = wy - g.y ();
533 
534 	gWindow->vertexBuffer ()->begin ();
535 
536 	gWindow->glAddGeometrySetCurrentIndex (MAXSHORT);
537 	gWindow->glAddGeometry (matrix, iconReg, infiniteRegion);
538 	gWindow->glAddGeometrySetCurrentIndex (addWindowGeometryIndex);
539 
540 	if (gWindow->vertexBuffer ()->end ())
541 	{
542 	    GLMatrix           wTransform (transform);
543 
544 	    wTransform.translate (g.x (), g.y (), 0.0f);
545 	    wTransform.scale (sAttrib.xScale, sAttrib.yScale, 1.0f);
546 	    wTransform.translate (sAttrib.xTranslate / sAttrib.xScale - g.x (),
547 				  sAttrib.yTranslate / sAttrib.yScale - g.y (),
548 				  0.0f);
549 
550 	    gWindow->glDrawTexture (icon, wTransform, sAttrib, mask);
551 	}
552     }
553 }
554 
555 bool
556 BaseSwitchWindow::damageRect (bool initial, const CompRect &rect)
557 {
558     if (!openGLAvailable)
559 	return true;
560 
561     if (baseScreen->grabIndex)
562     {
563 	CompWindow *popup;
564 
565 	popup = ::screen->findWindow (baseScreen->popupWindow);
566 	if (popup)
567 	{
568 	    foreach (CompWindow *w, baseScreen->windows)
569 	    {
570 		if (window == w)
571 		{
572 		    CompositeWindow::get (popup)->addDamage ();
573 		    break;
574 		}
575 	    }
576 	}
577     }
578 
579     return cWindow->damageRect (initial, rect);
580 }
581 
582 void
583 BaseSwitchScreen::updateForegroundColor ()
584 {
585     Atom	  actual;
586     int		  result, format;
587     unsigned long n, left;
588     unsigned char *propData;
589 
590     if (!popupWindow)
591 	return;
592 
593     result = XGetWindowProperty (::screen->dpy (), popupWindow,
594 				 selectFgColorAtom, 0L, 4L, false,
595 				 XA_INTEGER, &actual, &format,
596 				 &n, &left, &propData);
597 
598     if (result == Success && n && propData)
599     {
600 	if (n == 3 || n == 4)
601 	{
602 	    long *data = (long *) propData;
603 
604 	    fgColor[0] = MIN (0xffff, data[0]);
605 	    fgColor[1] = MIN (0xffff, data[1]);
606 	    fgColor[2] = MIN (0xffff, data[2]);
607 
608 	    if (n == 4)
609 		fgColor[3] = MIN (0xffff, data[3]);
610 	}
611 
612 	XFree (propData);
613     }
614     else
615     {
616 	fgColor[0] = 0;
617 	fgColor[1] = 0;
618 	fgColor[2] = 0;
619 	fgColor[3] = 0xffff;
620     }
621 }
622 
623 void
624 BaseSwitchScreen::handleEvent (XEvent *event)
625 {
626     CompWindow *w = NULL;
627 
628     switch (event->type) {
629 	case DestroyNotify:
630 	    /* We need to get the CompWindow * for event->xdestroywindow.window
631 	       here because in the ::screen->handleEvent call below, that
632 	       CompWindow's id will become 1, so findWindowAtDisplay won't be
633 	       able to find the CompWindow after that. */
634 	       w = ::screen->findWindow (event->xdestroywindow.window);
635 	    break;
636 	default:
637 	    break;
638     }
639 
640     ::screen->handleEvent (event);
641 
642     switch (event->type) {
643 	case UnmapNotify:
644 	    w = ::screen->findWindow (event->xunmap.window);
645 	    windowRemove (w);
646 	    break;
647 	case DestroyNotify:
648 	    windowRemove (w);
649 	    break;
650 	case PropertyNotify:
651 	    if (event->xproperty.atom == selectFgColorAtom)
652 	    {
653 		if (event->xproperty.window == popupWindow)
654 		    updateForegroundColor ();
655 	    }
656 	    break;
657 	default:
658 	    break;
659     }
660 }
661 
662 BaseSwitchScreen::BaseSwitchScreen (CompScreen *screen) :
663     popupWindow (None),
664     selectedWindow (NULL),
665     lastActiveNum (0),
666     grabIndex (NULL),
667     moreAdjust (false),
668     selection (CurrentViewport),
669     ignoreSwitcher (false)
670 {
671     CompOption::Vector atomTemplate;
672     CompOption::Value v;
673     CompOption	      o;
674 
Condition "openGLAvailable", taking false branch
675     if (openGLAvailable)
676     {
677 	cScreen = CompositeScreen::get (screen);
678 	gScreen = GLScreen::get (screen);
End of if statement
679     }
680 
681     o.setName ("id", CompOption::TypeInt);
682     atomTemplate.push_back (o);
683 
684     selectWinAtom = PropertyWriter (CompString (DECOR_SWITCH_WINDOW_ATOM_NAME),
685     				    atomTemplate);
686 
687     selectFgColorAtom =
688     	XInternAtom (::screen->dpy (),
689     		     DECOR_SWITCH_FOREGROUND_COLOR_ATOM_NAME, 0);
690 
691     fgColor[0] = 0;
692     fgColor[1] = 0;
693     fgColor[2] = 0;
694     fgColor[3] = 0xffff;
CID 12567 - UNINIT_CTOR
Non-static class member "cScreen" is not initialized in this constructor nor in any functions that it calls.
Non-static class member "gScreen" is not initialized in this constructor nor in any functions that it calls.
695 }
696 
697 BaseSwitchWindow::BaseSwitchWindow (BaseSwitchScreen *ss, CompWindow *w) :
698     baseScreen (ss),
699     window (w)
700 {
701     if (openGLAvailable)
702     {
703 	gWindow = GLWindow::get (w);
704 	cWindow = CompositeWindow::get (w);
705 	gScreen = GLScreen::get (screen);
706     }
707 
708 }
709 
710 CompizToolboxScreen::CompizToolboxScreen (CompScreen *screen) :
711     PluginClassHandler <CompizToolboxScreen, CompScreen> (screen)
712 {
713 }
714 
715 bool
716 CompizToolboxPluginVTable::init ()
717 {
718     openGLAvailable = true;
719 
720     if (!CompPlugin::checkPluginABI ("core", CORE_ABIVERSION))
721 	return false;
722 
723     if (!CompPlugin::checkPluginABI ("composite", COMPIZ_COMPOSITE_ABI) ||
724         !CompPlugin::checkPluginABI ("opengl", COMPIZ_OPENGL_ABI))
725 	openGLAvailable = false;
726 
727     CompPrivate p;
728     p.uval = COMPIZ_COMPIZTOOLBOX_ABI;
729     screen->storeValue ("compiztoolbox_ABI", p);
730 
731     return true;
732 }
733 
734 void
735 CompizToolboxPluginVTable::fini ()
736 {
737     screen->eraseValue ("compiztoolbox_ABI");
738 }