/* * Copyright (C) 2007 Andrew Riedi * * Sticky window handling by Dennis Kasprzyk * * 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 #include #include #include #include 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; }