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 }