/*
* Copyright (C) 2007 Andrew Riedi <andrewriedi@gmail.com>
*
* Sticky window handling by Dennis Kasprzyk <onestone@opencompositing.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* This plug-in for Metacity-like workarounds.
*/
#include <string.h>
#include <limits.h>
#include <compiz.h>
#include <X11/Xatom.h>
#include <workarounds_options.h>
static int displayPrivateIndex;
typedef struct _WorkaroundsDisplay {
int screenPrivateIndex;
HandleEventProc handleEvent;
Atom roleAtom;
} WorkaroundsDisplay;
typedef struct _WorkaroundsScreen {
int windowPrivateIndex;
WindowResizeNotifyProc windowResizeNotify;
GetAllowedActionsForWindowProc getAllowedActionsForWindow;
} WorkaroundsScreen;
typedef struct _WorkaroundsWindow {
Bool adjustedWinType;
Bool madeSticky;
Bool madeFullscreen;
Bool isFullscreen;
} WorkaroundsWindow;
#define GET_WORKAROUNDS_DISPLAY(d) \
((WorkaroundsDisplay *) (d)->privates[displayPrivateIndex].ptr)
#define WORKAROUNDS_DISPLAY(d) \
WorkaroundsDisplay *wd = GET_WORKAROUNDS_DISPLAY (d)
#define GET_WORKAROUNDS_SCREEN(s, wd) \
((WorkaroundsScreen *) (s)->privates[(wd)->screenPrivateIndex].ptr)
#define WORKAROUNDS_SCREEN(s) \
WorkaroundsScreen *ws = GET_WORKAROUNDS_SCREEN (s, \
GET_WORKAROUNDS_DISPLAY (s->display))
#define GET_WORKAROUNDS_WINDOW(w, ns) \
((WorkaroundsWindow *) (w)->privates[(ns)->windowPrivateIndex].ptr)
#define WORKAROUNDS_WINDOW(w) \
WorkaroundsWindow *ww = GET_WORKAROUNDS_WINDOW (w, \
GET_WORKAROUNDS_SCREEN (w->screen, \
GET_WORKAROUNDS_DISPLAY (w->screen->display)))
static char *
workaroundsGetWindowRoleAtom (CompWindow *w)
{
CompDisplay *d = w->screen->display;
Atom type;
unsigned long nItems;
unsigned long bytesAfter;
unsigned char *str = NULL;
int format, result;
char *retval;
WORKAROUNDS_DISPLAY (d);
result = XGetWindowProperty (d->display, w->id, wd->roleAtom,
0, LONG_MAX, FALSE, XA_STRING,
&type, &format, &nItems, &bytesAfter,
(unsigned char **) &str);
if (result != Success)
return NULL;
if (type != XA_STRING)
{
XFree (str);
return NULL;
}
retval = strdup ((char *) str);
XFree (str);
return retval;
}
static void
workaroundsRemoveSticky (CompWindow *w)
{
WORKAROUNDS_WINDOW (w);
if (w->state & CompWindowStateStickyMask && ww->madeSticky)
changeWindowState (w, w->state & ~CompWindowStateStickyMask);
ww->madeSticky = FALSE;
}
static void
workaroundsUpdateSticky (CompWindow *w)
{
WORKAROUNDS_WINDOW (w);
CompDisplay *d = w->screen->display;
Bool makeSticky = FALSE;
if (workaroundsGetStickyAlldesktops (d) && w->desktop == 0xffffffff &&
matchEval (workaroundsGetAlldesktopStickyMatch (d), w))
makeSticky = TRUE;
if (makeSticky)
{
if (!(w->state & CompWindowStateStickyMask))
{
ww->madeSticky = TRUE;
changeWindowState (w, w->state | CompWindowStateStickyMask);
}
}
else
workaroundsRemoveSticky (w);
}
static void
workaroundsGetAllowedActionsForWindow (CompWindow *w,
unsigned int *setActions,
unsigned int *clearActions)
{
CompScreen *s = w->screen;
WORKAROUNDS_SCREEN (s);
WORKAROUNDS_WINDOW (w);
UNWRAP (ws, s, getAllowedActionsForWindow);
(*s->getAllowedActionsForWindow) (w, setActions, clearActions);
WRAP (ws, s, getAllowedActionsForWindow,
workaroundsGetAllowedActionsForWindow);
if (ww->isFullscreen)
*setActions |= CompWindowActionFullscreenMask;
}
static void
workaroundsFixupFullscreen (CompWindow *w)
{
Bool isFullSize;
int output;
BoxPtr box;
WORKAROUNDS_WINDOW (w);
if (w->wmType & CompWindowTypeDesktopMask)
{
/* desktop windows are implicitly fullscreen */
isFullSize = FALSE;
}
else
{
/* get output region for window */
output = outputDeviceForWindow (w);
box = &w->screen->outputDev[output].region.extents;
/* does the size match the output rectangle? */
isFullSize = (w->serverX == box->x1) && (w->serverY == box->y1) &&
(w->serverWidth == (box->x2 - box->x1)) &&
(w->serverHeight == (box->y2 - box->y1));
/* if not, check if it matches the whole screen */
if (!isFullSize)
{
if ((w->serverX == 0) && (w->serverY == 0) &&
(w->serverWidth == w->screen->width) &&
(w->serverHeight == w->screen->height))
{
isFullSize = TRUE;
}
}
}
ww->isFullscreen = isFullSize;
if ((isFullSize && !(w->state & CompWindowStateFullscreenMask)) ||
(!isFullSize && (w->state & CompWindowStateFullscreenMask)))
{
unsigned int state = w->state & ~CompWindowStateFullscreenMask;
if (isFullSize)
state |= CompWindowStateFullscreenMask;
ww->madeFullscreen = isFullSize;
if (state != w->state)
{
changeWindowState (w, state);
recalcWindowType (w);
recalcWindowActions (w);
updateWindowAttributes (w, CompStackingUpdateModeNormal);
}
}
}
static void
workaroundsDoFixes (CompWindow *w)
{
CompDisplay *d = w->screen->display;
unsigned int newWmType;
newWmType = getWindowType (d, w->id);
/* FIXME: Is this the best way to detect a notification type window? */
if (workaroundsGetNotificationDaemonFix (d))
{
if (w->wmType == CompWindowTypeNormalMask &&
w->attrib.override_redirect && w->resName &&
strcmp (w->resName, "notification-daemon") == 0)
{
newWmType = CompWindowTypeNotificationMask;
}
}
if ((w->wmType == newWmType) && workaroundsGetFirefoxMenuFix (d))
{
if (w->wmType == CompWindowTypeNormalMask &&
w->attrib.override_redirect && w->resName)
{
if ((strcasecmp (w->resName, "gecko") == 0) ||
(strcasecmp (w->resName, "popup") == 0))
{
newWmType = CompWindowTypeDropdownMenuMask;
}
}
}
/* FIXME: Basic hack to get Java windows working correctly. */
if ((w->wmType == newWmType) && workaroundsGetJavaFix (d) && w->resName)
{
if ((strcmp (w->resName, "sun-awt-X11-XMenuWindow") == 0) ||
(strcmp (w->resName, "sun-awt-X11-XWindowPeer") == 0))
{
newWmType = CompWindowTypeDropdownMenuMask;
}
else if (strcmp (w->resName, "sun-awt-X11-XDialogPeer") == 0)
{
newWmType = CompWindowTypeDialogMask;
}
else if (strcmp (w->resName, "sun-awt-X11-XFramePeer") == 0)
{
newWmType = CompWindowTypeNormalMask;
}
}
if ((newWmType == w->wmType) && workaroundsGetQtFix (d))
{
char *windowRole;
/* fix tooltips */
windowRole = workaroundsGetWindowRoleAtom (w);
if (windowRole)
{
if ((strcmp (windowRole, "toolTipTip") == 0) ||
(strcmp (windowRole, "qtooltip_label") == 0))
{
newWmType = CompWindowTypeTooltipMask;
}
free (windowRole);
}
/* fix Qt transients - FIXME: is there a better way to detect them? */
if (w->wmType == newWmType)
{
if (!w->resName && w->attrib.override_redirect &&
(w->attrib.class == InputOutput) &&
(w->wmType == CompWindowTypeUnknownMask))
{
newWmType = CompWindowTypeDropdownMenuMask;
}
}
}
if (newWmType != w->wmType)
{
WORKAROUNDS_WINDOW (w);
ww->adjustedWinType = TRUE;
w->wmType = newWmType;
recalcWindowType (w);
recalcWindowActions (w);
(*d->matchPropertyChanged) (d, w);
}
if (workaroundsGetLegacyFullscreen (w->screen->display))
workaroundsFixupFullscreen (w);
}
static void
workaroundsDisplayOptionChanged (CompDisplay *d,
CompOption *opt,
WorkaroundsDisplayOptions num)
{
CompScreen *s;
CompWindow *w;
for (s = d->screens; s; s = s->next)
for (w = s->windows; w; w = w->next)
workaroundsUpdateSticky (w);
}
static void
workaroundsHandleEvent (CompDisplay *d,
XEvent *event)
{
CompWindow *w;
WORKAROUNDS_DISPLAY (d);
switch (event->type) {
case ConfigureRequest:
w = findWindowAtDisplay (d, event->xconfigurerequest.window);
if (w)
{
WORKAROUNDS_WINDOW (w);
if (ww->madeFullscreen)
w->state &= ~CompWindowStateFullscreenMask;
}
break;
case MapRequest:
w = findWindowAtDisplay (d, event->xmaprequest.window);
if (w)
{
workaroundsUpdateSticky (w);
workaroundsDoFixes (w);
}
break;
case MapNotify:
w = findWindowAtDisplay (d, event->xmap.window);
if (w && w->attrib.override_redirect)
workaroundsDoFixes (w);
break;
}
UNWRAP (wd, d, handleEvent);
(*d->handleEvent) (d, event);
WRAP (wd, d, handleEvent, workaroundsHandleEvent);
switch (event->type) {
case ConfigureRequest:
w = findWindowAtDisplay (d, event->xconfigurerequest.window);
if (w)
{
WORKAROUNDS_WINDOW (w);
if (ww->madeFullscreen)
w->state |= CompWindowStateFullscreenMask;
}
break;
case ClientMessage:
if (event->xclient.message_type == d->winDesktopAtom)
{
w = findWindowAtDisplay (d, event->xclient.window);
if (w)
workaroundsUpdateSticky (w);
}
break;
case PropertyNotify:
if ((event->xproperty.atom == XA_WM_CLASS) ||
(event->xproperty.atom == d->winTypeAtom))
{
w = findWindowAtDisplay (d, event->xproperty.window);
if (w)
workaroundsDoFixes (w);
}
break;
default:
break;
}
}
static void
workaroundsWindowResizeNotify (CompWindow *w, int dx, int dy,
int dwidth, int dheight)
{
WORKAROUNDS_SCREEN (w->screen);
if (workaroundsGetLegacyFullscreen (w->screen->display))
workaroundsFixupFullscreen (w);
UNWRAP (ws, w->screen, windowResizeNotify);
(*w->screen->windowResizeNotify) (w, dx, dy, dwidth, dheight);
WRAP (ws, w->screen, windowResizeNotify, workaroundsWindowResizeNotify);
}
static Bool
workaroundsInitDisplay (CompPlugin *plugin, CompDisplay *d)
{
WorkaroundsDisplay *wd;
wd = malloc (sizeof (WorkaroundsDisplay));
if (!wd)
return FALSE;
wd->screenPrivateIndex = allocateScreenPrivateIndex( d );
if (wd->screenPrivateIndex < 0)
{
free (wd);
return FALSE;
}
wd->roleAtom = XInternAtom (d->display, "WM_WINDOW_ROLE", 0);
workaroundsSetStickyAlldesktopsNotify (d, workaroundsDisplayOptionChanged);
workaroundsSetAlldesktopStickyMatchNotify (d,
workaroundsDisplayOptionChanged);
d->privates[displayPrivateIndex].ptr = wd;
WRAP (wd, d, handleEvent, workaroundsHandleEvent);
return TRUE;
}
static void
workaroundsFiniDisplay (CompPlugin *plugin, CompDisplay *d)
{
WORKAROUNDS_DISPLAY (d);
freeScreenPrivateIndex (d, wd->screenPrivateIndex);
UNWRAP (wd, d, handleEvent);
free (wd);
}
static Bool
workaroundsInitScreen (CompPlugin *plugin, CompScreen *s)
{
WorkaroundsScreen *ws;
WORKAROUNDS_DISPLAY (s->display);
ws = malloc (sizeof (WorkaroundsScreen));
if (!ws)
return FALSE;
ws->windowPrivateIndex = allocateWindowPrivateIndex (s);
if (ws->windowPrivateIndex < 0)
{
free (ws);
return FALSE;
}
WRAP (ws, s, windowResizeNotify, workaroundsWindowResizeNotify);
WRAP (ws, s, getAllowedActionsForWindow,
workaroundsGetAllowedActionsForWindow);
s->privates[wd->screenPrivateIndex].ptr = ws;
return TRUE;
}
static void
workaroundsFiniScreen (CompPlugin *plugin, CompScreen *s)
{
WORKAROUNDS_SCREEN (s);
freeWindowPrivateIndex (s, ws->windowPrivateIndex);
UNWRAP (ws, s, windowResizeNotify);
UNWRAP (ws, s, getAllowedActionsForWindow);
free (ws);
}
static Bool
workaroundsInitWindow (CompPlugin *plugin, CompWindow *w)
{
WORKAROUNDS_SCREEN (w->screen);
WorkaroundsWindow *ww;
ww = malloc (sizeof (WorkaroundsWindow));
if (!ww)
return FALSE;
ww->madeSticky = FALSE;
ww->adjustedWinType = FALSE;
ww->isFullscreen = FALSE;
ww->madeFullscreen = FALSE;
w->privates[ws->windowPrivateIndex].ptr = ww;
return TRUE;
}
static void
workaroundsFiniWindow (CompPlugin *plugin, CompWindow *w)
{
WORKAROUNDS_WINDOW (w);
if (ww->adjustedWinType)
{
w->wmType = getWindowType (w->screen->display, w->id);
recalcWindowType (w);
recalcWindowActions (w);
}
if (w->state & CompWindowStateStickyMask && ww->madeSticky)
setWindowState (w->screen->display,
w->state & ~CompWindowStateStickyMask, w->id);
free (ww);
}
static Bool
workaroundsInit (CompPlugin *plugin)
{
displayPrivateIndex = allocateDisplayPrivateIndex ();
if (displayPrivateIndex < 0)
return FALSE;
return TRUE;
}
static void
workaroundsFini (CompPlugin *plugin)
{
freeDisplayPrivateIndex (displayPrivateIndex);
}
static int
workaroundsGetVersion (CompPlugin *plugin, int version)
{
return ABIVERSION;
}
CompPluginVTable workaroundsVTable =
{
"workarounds",
workaroundsGetVersion,
0,
workaroundsInit,
workaroundsFini,
workaroundsInitDisplay,
workaroundsFiniDisplay,
workaroundsInitScreen,
workaroundsFiniScreen,
workaroundsInitWindow,
workaroundsFiniWindow,
0, /* GetDisplayOptions */
0, /* SetDisplayOption */
0, /* GetScreenOptions */
0 /* SetScreenOption */
};
CompPluginVTable *
getCompPluginInfo (void)
{
return &workaroundsVTable;
}
syntax highlighted by Code2HTML, v. 0.9.1