3
* Compiz session plugin
7
* Copyright (c) 2008 Travis Watkins <amaranth@ubuntu.com>
8
* Copyright (c) 2008 Danny Baumann <maniac@opencompositing.org>
9
* Copyright (c) 2006 Patrick Niklaus
12
* This program is free software; you can redistribute it and/or
13
* modify it under the terms of the GNU General Public License
14
* as published by the Free Software Foundation; either version 2
15
* of the License, or (at your option) any later version.
17
* This program is distributed in the hope that it will be useful,
18
* but WITHOUT ANY WARRANTY; without even the implied warranty of
19
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
* GNU General Public License for more details.
22
* Authors: Travis Watkins <amaranth@ubuntu.com>
27
#include <core/atoms.h>
28
#include <core/session.h>
34
COMPIZ_PLUGIN_20090315 (session, SessionPluginVTable);
37
SessionScreen::getUtf8Property (Window id,
43
unsigned long nItems, bytesAfter;
47
result = XGetWindowProperty (screen->dpy (), id, atom, 0L, 65536, False,
48
Atoms::utf8String, &type, &format, &nItems,
49
&bytesAfter, (unsigned char **) &val);
51
if (result != Success)
54
if (type == Atoms::utf8String && format != 8 && nItems == 0)
56
char valueString[nItems + 1];
58
strncpy (valueString, val, nItems);
59
valueString[nItems] = 0;
72
SessionScreen::getTextProperty (Window id,
80
if (XGetTextProperty (screen->dpy (), id, &text, atom))
84
char valueString[text.nitems + 1];
86
strncpy (valueString, (char *) text.value, text.nitems);
87
valueString[text.nitems] = 0;
100
SessionScreen::getWindowTitle (Window id,
103
if (getUtf8Property (id, visibleNameAtom, string))
106
if (getUtf8Property (id, Atoms::wmName, string))
109
if (getTextProperty (id, XA_WM_NAME, string))
116
SessionScreen::getWindowClass (Window id,
118
CompString& resClass)
120
XClassHint classHint;
125
if (!XGetClassHint (screen->dpy (), id, &classHint) != Success)
128
if (classHint.res_name)
130
resName = classHint.res_name;
131
XFree (classHint.res_name);
134
if (classHint.res_class)
136
resClass = classHint.res_class;
137
XFree (classHint.res_class);
144
SessionScreen::getIsEmbedded (Window id)
148
unsigned long nitems, bytesAfter;
151
result = XGetWindowProperty (screen->dpy (), id, embedInfoAtom, 0L, 65536,
152
false, XA_CARDINAL, &type, &format, &nitems,
155
if (result != Success)
165
SessionScreen::getClientLeaderProperty (CompWindow *w,
171
clientLeader = w->clientLeader ();
173
/* try to find clientLeader on transient parents */
176
CompWindow *window = w;
178
while (window && window->transientFor ())
180
if (window->transientFor () == window->id ())
183
window = screen->findWindow (window->transientFor ());
184
if (window && window->clientLeader ())
186
clientLeader = window->clientLeader ();
193
clientLeader = w->id ();
195
return getTextProperty (clientLeader, atom, string);
199
SessionScreen::getIntForProp (xmlNodePtr node,
205
temp = xmlGetProp (node, BAD_CAST prop);
208
num = xmlXPathCastStringToNumber (temp);
218
SessionScreen::getStringForProp (xmlNodePtr node,
224
text = xmlGetProp (node, BAD_CAST prop);
227
retval = (char *) text;
235
SessionScreen::isSessionWindow (CompWindow *w)
237
if (w->overrideRedirect ())
240
/* filter out embedded windows (notification icons) */
241
if (getIsEmbedded (w->id ()))
244
if (optionGetIgnoreMatch ().evaluate (w))
251
addIntegerPropToNode (xmlNodePtr node,
255
xmlChar *string = xmlXPathCastNumberToString (value);
260
xmlNewProp (node, BAD_CAST name, string);
265
SessionScreen::addWindowNode (CompWindow *w,
268
CompString clientId, command, string;
269
CompString resName, resClass;
270
int x, y, width, height;
271
xmlNodePtr node, childNode;
273
if (!getClientLeaderProperty (w, clientIdAtom, clientId) &&
274
!optionGetSaveLegacy ())
279
getClientLeaderProperty (w, commandAtom, command);
280
if (clientId.empty () && command.empty ())
283
node = xmlNewChild (rootNode, NULL, BAD_CAST "window", NULL);
287
if (!clientId.empty ())
288
xmlNewProp (node, BAD_CAST "id", BAD_CAST clientId.c_str ());
290
if (getWindowTitle (w->id (), string))
291
xmlNewProp (node, BAD_CAST "title", BAD_CAST string.c_str ());
293
if (getWindowClass (w->id (), resName, resClass))
295
if (!resClass.empty ())
296
xmlNewProp (node, BAD_CAST "class", BAD_CAST resClass.c_str ());
297
if (!resName.empty ())
298
xmlNewProp (node, BAD_CAST "name", BAD_CAST resName.c_str ());
301
if (getTextProperty (w->id (), roleAtom, string))
302
xmlNewProp (node, BAD_CAST "role", BAD_CAST string.c_str ());
304
if (!command.empty ())
305
xmlNewProp (node, BAD_CAST "command", BAD_CAST command.c_str ());
307
/* save geometry, relative to viewport 0, 0 */
308
childNode = xmlNewChild (node, NULL, BAD_CAST "geometry", NULL);
311
x = (w->saveMask () & CWX) ? w->saveWc ().x : w->serverX ();
312
y = (w->saveMask () & CWY) ? w->saveWc ().y : w->serverY ();
313
if (!w->onAllViewports ())
315
x += screen->vp ().x () * screen->width ();
316
y += screen->vp ().y () * screen->height ();
319
x -= w->border ().left;
320
y -= w->border ().top;
322
width = (w->saveMask () & CWWidth) ? w->saveWc ().width :
324
height = (w->saveMask () & CWHeight) ? w->saveWc ().height :
327
addIntegerPropToNode (childNode, "x", x);
328
addIntegerPropToNode (childNode, "y", y);
329
addIntegerPropToNode (childNode, "width", width);
330
addIntegerPropToNode (childNode, "height", height);
333
/* save various window states */
334
if (w->state () & CompWindowStateShadedMask)
335
xmlNewChild (node, NULL, BAD_CAST "shaded", NULL);
336
if (w->state () & CompWindowStateStickyMask)
337
xmlNewChild (node, NULL, BAD_CAST "sticky", NULL);
338
if (w->state () & CompWindowStateFullscreenMask)
339
xmlNewChild (node, NULL, BAD_CAST "fullscreen", NULL);
341
xmlNewChild (node, NULL, BAD_CAST "minimized", NULL);
342
if (w->state () & MAXIMIZE_STATE)
344
childNode = xmlNewChild (node, NULL, BAD_CAST "maximized", NULL);
347
if (w->state () & CompWindowStateMaximizedVertMask)
348
xmlNewProp (childNode, BAD_CAST "vert", BAD_CAST "yes");
349
if (w->state () & CompWindowStateMaximizedHorzMask)
350
xmlNewProp (childNode, BAD_CAST "horz", BAD_CAST "yes");
355
if (!(w->type () & (CompWindowTypeDesktopMask | CompWindowTypeDockMask)))
357
childNode = xmlNewChild (node, NULL, BAD_CAST "workspace", NULL);
359
addIntegerPropToNode (childNode, "index", w->desktop ());
364
SessionScreen::getFileName (const CompString& clientId)
367
struct passwd *p = getpwuid (geteuid ());
369
fileName = p->pw_dir;
370
fileName += "/.compiz/session/";
371
fileName += clientId;
377
SessionScreen::createDir (const CompString& path)
381
if (mkdir (path.c_str (), 0700) == 0)
384
/* did it already exist? */
388
/* was parent present? if yes, fail */
392
pos = path.rfind ('/', path.size () - 1);
393
if (pos == CompString::npos)
396
if (!createDir (path.substr (0, pos)))
399
return (mkdir (path.c_str (), 0700) == 0);
403
SessionScreen::saveState (const CompString& clientId)
405
CompString fileName = getFileName (clientId);
406
xmlDocPtr doc = NULL;
407
xmlSaveCtxtPtr ctx = NULL;
409
if (!createDir (fileName.substr (0, fileName.rfind ('/'))))
412
ctx = xmlSaveToFilename (fileName.c_str (), NULL, XML_SAVE_FORMAT);
416
/* write out all windows on this screen */
417
doc = xmlNewDoc (BAD_CAST "1.0");
421
rootNode = xmlNewNode (NULL, BAD_CAST "compiz_session");
424
xmlNewProp (rootNode, BAD_CAST "id", BAD_CAST clientId.c_str ());
425
xmlDocSetRootElement (doc, rootNode);
427
foreach (CompWindow *w, screen->windows ())
429
if (!isSessionWindow (w))
435
addWindowNode (w, rootNode);
438
xmlSaveDoc (ctx, doc);
448
SessionScreen::matchWindowClass (CompWindow *w,
449
const SessionItem& info)
451
CompString resName, resClass;
453
if (!getWindowClass (w->id (), resName, resClass))
456
if (resName != info.resName)
459
if (resClass != info.resClass)
466
SessionWindow::place (CompPoint& pos)
476
return window->place (pos);
480
SessionScreen::readWindow (CompWindow *w)
483
unsigned int xwcm = 0;
484
CompString title, role, clientId, command;
485
ItemList::iterator item;
487
/* optimization: don't mess around with getting X properties
488
if there is nothing to match */
492
if (!isSessionWindow (w))
495
if (!getClientLeaderProperty (w, clientIdAtom, clientId) &&
496
!optionGetSaveLegacy ())
501
getClientLeaderProperty (w, commandAtom, command);
502
getWindowTitle (w->id (), title);
503
getTextProperty (w->id (), roleAtom, role);
505
for (item = items.begin (); item != items.end (); item++)
507
if (!clientId.empty () && clientId == item->clientId)
509
/* try to match role as well if possible (see ICCCM 5.1) */
510
if (!role.empty () && !item->role.empty ())
512
if (role == item->role)
517
if (matchWindowClass (w, *item))
521
else if (optionGetSaveLegacy ())
523
if (!command.empty () && !item->command.empty () &&
524
matchWindowClass (w, *item))
526
/* match by command, class and name as second try */
529
else if (!title.empty () && title == item->title)
531
/* last resort: match by window title */
537
if (item == items.end ())
541
if (item->geometrySet)
543
SessionWindow *sw = SessionWindow::get (w);
547
xwc.x = item->geometry.x () + w->border ().left;
548
xwc.y = item->geometry.y () + w->border ().top;
550
if (!w->onAllViewports ())
552
xwc.x -= (screen->vp ().x () * screen->width ());
553
xwc.y -= (screen->vp ().y () * screen->height ());
556
if (item->geometry.width () != w->serverWidth ())
558
xwc.width = item->geometry.width ();
561
if (item->geometry.height () != w->serverHeight ())
563
xwc.height = item->geometry.height ();
567
if (w->mapNum () && (xwcm & (CWWidth | CWHeight)))
568
w->sendSyncRequest ();
570
w->configureXWindow (xwcm, &xwc);
572
sw->positionSet = true;
573
sw->position.set (xwc.x, xwc.y);
579
if (item->workspace != -1)
580
w->setDesktop (item->workspace);
584
w->changeState (w->state () | item->state);
585
w->updateAttributes (CompStackingUpdateModeNone);
588
/* remove item from list */
595
SessionScreen::readState (xmlNodePtr root)
597
xmlNodePtr cur, attrib;
599
for (cur = root->xmlChildrenNode; cur; cur = cur->next)
603
item.geometrySet = false;
605
if (xmlStrcmp (cur->name, BAD_CAST "window") == 0)
607
item.clientId = getStringForProp (cur, "id");
608
item.title = getStringForProp (cur, "title");
609
item.resName = getStringForProp (cur, "name");
610
item.resClass = getStringForProp (cur, "class");
611
item.role = getStringForProp (cur, "role");
612
item.command = getStringForProp (cur, "command");
615
if (item.clientId.empty () && item.title.empty () &&
616
item.resName.empty () && item.resClass.empty ())
621
for (attrib = cur->xmlChildrenNode; attrib; attrib = attrib->next)
623
if (xmlStrcmp (attrib->name, BAD_CAST "geometry") == 0)
625
int x, y, width, height;
627
x = getIntForProp (attrib, "x");
628
y = getIntForProp (attrib, "y");
629
width = getIntForProp (attrib, "width");
630
height = getIntForProp (attrib, "height");
632
item.geometrySet = true;
633
item.geometry.setGeometry (x, x + width, y, y + height);
636
if (xmlStrcmp (attrib->name, BAD_CAST "shaded") == 0)
637
item.state |= CompWindowStateShadedMask;
638
if (xmlStrcmp (attrib->name, BAD_CAST "sticky") == 0)
639
item.state |= CompWindowStateStickyMask;
640
if (xmlStrcmp (attrib->name, BAD_CAST "fullscreen") == 0)
641
item.state |= CompWindowStateFullscreenMask;
642
if (xmlStrcmp (attrib->name, BAD_CAST "minimized") == 0)
643
item.minimized = true;
645
if (xmlStrcmp (attrib->name, BAD_CAST "maximized") == 0)
647
xmlChar *vert, *horiz;
648
vert = xmlGetProp (attrib, BAD_CAST "vert");
651
item.state |= CompWindowStateMaximizedVertMask;
655
horiz = xmlGetProp (attrib, BAD_CAST "horiz");
658
item.state |= CompWindowStateMaximizedHorzMask;
663
if (xmlStrcmp (attrib->name, BAD_CAST "workspace") == 0)
664
item.workspace = getIntForProp (attrib, "index");
667
items.push_back (item);
672
SessionScreen::loadState (const CompString& previousId)
676
CompString fileName = getFileName (previousId);
678
doc = xmlParseFile (fileName.c_str ());
682
root = xmlDocGetRootElement (doc);
683
if (root && xmlStrcmp (root->name, BAD_CAST "compiz_session") == 0)
691
SessionScreen::handleEvent (XEvent *event)
696
if (event->type == MapRequest)
698
w = screen->findWindow (event->xmaprequest.window);
707
screen->handleEvent (event);
709
if (event->type == MapRequest)
711
if (w && !(state & CompWindowStateDemandsAttentionMask))
713
state = w->state () & ~CompWindowStateDemandsAttentionMask;
714
w->changeState (state);
720
SessionScreen::sessionEvent (CompSession::Event event,
721
CompOption::Vector& arguments)
723
if (event == CompSession::EventSaveYourself)
725
bool shutdown, fast, saveSession;
726
int saveType, interactStyle;
729
shutdown = CompOption::getBoolOptionNamed (arguments,
731
saveType = CompOption::getIntOptionNamed (arguments,
732
"save_type", SmSaveLocal);
733
interactStyle = CompOption::getIntOptionNamed (arguments,
735
SmInteractStyleNone);
736
fast = CompOption::getBoolOptionNamed (arguments, "fast", false);
738
/* ignore saveYourself after registering for the first time
739
(SM specification 7.2) */
740
saveSession = shutdown || fast ||
741
(saveType != SmSaveLocal) ||
742
(interactStyle != SmInteractStyleNone);
745
clientId = CompSession::getClientId (CompSession::ClientId);
747
if (!clientId.empty ())
748
saveState (clientId);
751
screen->sessionEvent (event, arguments);
754
SessionScreen::SessionScreen (CompScreen *s) :
755
PluginClassHandler<SessionScreen, CompScreen> (s)
757
CompString prevClientId;
759
visibleNameAtom = XInternAtom (s->dpy (), "_NET_WM_VISIBLE_NAME", 0);
760
clientIdAtom = XInternAtom (s->dpy (), "SM_CLIENT_ID", 0);
761
embedInfoAtom = XInternAtom (s->dpy (), "_XEMBED_INFO", 0);
762
roleAtom = XInternAtom (s->dpy (), "WM_WINDOW_ROLE", 0);
763
commandAtom = XInternAtom (s->dpy (), "WM_COMMAND", 0);
765
prevClientId = CompSession::getClientId (CompSession::PrevClientId);
766
if (!prevClientId.empty ())
767
loadState (prevClientId);
769
ScreenInterface::setHandler (s);
772
SessionWindow::SessionWindow (CompWindow *w) :
773
PluginClassHandler<SessionWindow, CompWindow> (w),
777
WindowInterface::setHandler (w);
779
if (!w->overrideRedirect () && w->isViewable ())
780
SessionScreen::get (screen)->readWindow (w);
784
SessionPluginVTable::init ()
786
if (!CompPlugin::checkPluginABI ("core", CORE_ABIVERSION))