Back to index

unity  6.0.0
inputremover.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2011 Canonical Ltd.
00003  *
00004  * This program is free software; you can redistribute it and/or modify
00005  * it under the terms of the GNU General Public License as published by
00006  * the Free Software Foundation; either version 2 of the License, or
00007  * (at your option) any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00017  *
00018  * Authored By:
00019  * Sam Spilsbury <sam.spilsbury@canonical.com>
00020  */
00021 
00022 #include "inputremover.h"
00023 #include <X11/Xregion.h>
00024 #include <cstdio>
00025 #include <cstring>
00026 
00027 compiz::WindowInputRemoverInterface::~WindowInputRemoverInterface ()
00028 {
00029 }
00030 
00031 compiz::WindowInputRemover::WindowInputRemover (Display *dpy,
00032                                                 Window xid) :
00033   mDpy (dpy),
00034   mShapeWindow (xid),
00035   mShapeMask (0),
00036   mInputRects (NULL),
00037   mNInputRects (0),
00038   mInputRectOrdering (0),
00039   mBoundingRects (NULL),
00040   mNBoundingRects (0),
00041   mBoundingRectOrdering (0),
00042   mRemoved (false)
00043 {
00044   /* FIXME: roundtrip */
00045   XShapeQueryExtension (mDpy, &mShapeEvent, &mShapeError);
00046 }
00047 
00048 compiz::WindowInputRemover::~WindowInputRemover ()
00049 {
00050   if (mRemoved)
00051     restore ();
00052 }
00053 
00054 void
00055 compiz::WindowInputRemover::sendShapeNotify ()
00056 {
00057   /* Send a synthetic ShapeNotify event to the client and parent window
00058    * since we ignored shape events when setting visibility
00059    * in order to avoid cycling in the shape handling code -
00060    * ignore the sent shape notify event since that will
00061    * be send_event = true
00062    *
00063    * NB: We must send ShapeNotify events to both the client
00064    * window and to the root window with SubstructureRedirectMask
00065    * since NoEventMask will only deliver the event to the client
00066    * (see xserver/dix/events.c on the handling of CantBeFiltered
00067    *  messages)
00068    *
00069    * NB: This code will break if you don't have this patch on the
00070    * X Server since XSendEvent for non-core events are broken.
00071    *
00072    * http://lists.x.org/archives/xorg-devel/2011-September/024996.html
00073    */
00074 
00075   XShapeEvent  xsev;
00076   XEvent       *xev = (XEvent *) &xsev;
00077   Window       rootReturn, parentReturn;
00078   Window       childReturn;
00079   Window       *children;
00080   int          x, y, xOffset, yOffset;
00081   unsigned int width, height, depth, border, nchildren;
00082 
00083   memset (&xsev, 0, sizeof (XShapeEvent));
00084 
00085   /* XXX: libXShape is weird and range checks the event
00086    * type on event_to_wire so ensure that we are setting
00087    * the event type on the right range */
00088   xsev.type = (mShapeEvent - ShapeNotify) & 0x7f;
00089   /* We must explicitly fill in these values to avoid padding errors */
00090   xsev.serial = 0L;
00091   xsev.send_event = TRUE;
00092   xsev.display = mDpy;
00093   xsev.window = mShapeWindow;
00094 
00095   if (!mRemoved)
00096   {
00097     /* FIXME: these roundtrips suck */
00098     if (!XGetGeometry (mDpy, mShapeWindow, &rootReturn, &x, &y, &width, &height, &depth, &border))
00099       return;
00100     if (!XQueryTree (mDpy, mShapeWindow, &rootReturn, &parentReturn, &children, &nchildren))
00101       return;
00102 
00103     /* We need to translate the co-ordinates of the origin to the
00104      * client window to its parent to find out the offset of its
00105      * position so that we can subtract that from the final bounding
00106      * rect of the window shape according to the Shape extension
00107      * specification */
00108 
00109     XTranslateCoordinates (mDpy, mShapeWindow, parentReturn, 0, 0,
00110                         &xOffset, &yOffset, &childReturn);
00111 
00112     xsev.kind = ShapeBounding;
00113 
00114     /* Calculate extents of the bounding shape */
00115     if (!mNBoundingRects)
00116     {
00117       /* No set input shape, we must use the client geometry */
00118       xsev.x = x - xOffset;
00119       xsev.y = y - yOffset;
00120       xsev.width = width; 
00121       xsev.height = height;
00122       xsev.shaped = false;
00123     }
00124     else
00125     {
00126       Region      boundingRegion = XCreateRegion ();
00127 
00128       for (int i = 0; i < mNBoundingRects; i++)
00129         XUnionRectWithRegion (&(mBoundingRects[i]), boundingRegion, boundingRegion);
00130 
00131       xsev.x = boundingRegion->extents.x1 - xOffset;
00132       xsev.y = boundingRegion->extents.y1 - yOffset;
00133       xsev.width = boundingRegion->extents.x2 - boundingRegion->extents.x1;
00134       xsev.height = boundingRegion->extents.y2 - boundingRegion->extents.y1;
00135       xsev.shaped = true;
00136 
00137       XDestroyRegion (boundingRegion);
00138     }
00139 
00140     xsev.time = CurrentTime;
00141 
00142     XSendEvent (mDpy, mShapeWindow, FALSE, NoEventMask, xev);
00143     XSendEvent (mDpy, parentReturn, FALSE, NoEventMask, xev);
00144     xsev.kind = ShapeInput;
00145 
00146     /* Calculate extents of the bounding shape */
00147     if (!mNInputRects)
00148     {
00149       /* No set input shape, we must use the client geometry */
00150       xsev.x = x - xOffset;
00151       xsev.y = y - yOffset;
00152       xsev.width = width; 
00153       xsev.height = height;
00154       xsev.shaped = false;
00155     }
00156     else
00157     {
00158       Region      inputRegion = XCreateRegion ();
00159 
00160       for (int i = 0; i < mNInputRects; i++)
00161         XUnionRectWithRegion (&(mInputRects[i]), inputRegion, inputRegion);
00162 
00163       xsev.x = inputRegion->extents.x1 - xOffset;
00164       xsev.y = inputRegion->extents.y1 - yOffset;
00165       xsev.width = inputRegion->extents.x2 - inputRegion->extents.x1;
00166       xsev.height = inputRegion->extents.y2 - inputRegion->extents.y1;
00167       xsev.shaped = true;
00168 
00169       XDestroyRegion (inputRegion);
00170     }
00171 
00172     xsev.time = CurrentTime;
00173 
00174     XSendEvent (mDpy, mShapeWindow, FALSE, NoEventMask, xev);
00175     XSendEvent (mDpy, parentReturn, FALSE, NoEventMask, xev);
00176 
00177     if (children)
00178       XFree (children);
00179   }
00180   else
00181   {
00182     XQueryTree (mDpy, mShapeWindow, &rootReturn, &parentReturn, &children, &nchildren);
00183 
00184     xsev.kind = ShapeBounding;
00185 
00186     xsev.x = 0;
00187     xsev.y = 0;
00188     xsev.width = 0;
00189     xsev.height = 0;
00190     xsev.shaped = true;
00191 
00192     xsev.time = CurrentTime;
00193     XSendEvent (mDpy, mShapeWindow, FALSE, NoEventMask, xev);
00194     XSendEvent (mDpy, parentReturn, FALSE, NoEventMask, xev);
00195 
00196     xsev.kind = ShapeInput;
00197 
00198     /* Both ShapeBounding and ShapeInput are null */
00199 
00200     xsev.time = CurrentTime;
00201 
00202     XSendEvent (mDpy, mShapeWindow, FALSE, NoEventMask, xev);
00203     XSendEvent (mDpy, parentReturn, FALSE, NoEventMask, xev);
00204 
00205   }
00206 
00207 }
00208 
00209 bool
00210 compiz::WindowInputRemover::saveInput ()
00211 {
00212   XRectangle   *rects;
00213   int          count = 0, ordering;
00214   Window       root;
00215   int          x, y;
00216   unsigned int width, height, border, depth;
00217 
00218   /* FIXME: There should be a generic GetGeometry request we can
00219      * use here in order to avoid a round-trip */
00220   if (!XGetGeometry (mDpy, mShapeWindow, &root, &x, &y, &width, &height,
00221                      &border, &depth))
00222   {
00223     return false;
00224   }
00225 
00226   rects = XShapeGetRectangles (mDpy, mShapeWindow, ShapeInput,
00227                                &count, &ordering);
00228 
00229   /* check if the returned shape exactly matches the window shape -
00230    * if that is true, the window currently has no set input shape */
00231   if ((count == 1) &&
00232       (rects[0].x == -((int) border)) &&
00233       (rects[0].y == -((int) border)) &&
00234       (rects[0].width == (width + border)) &&
00235       (rects[0].height == (height + border)))
00236   {
00237     count = 0;
00238   }
00239 
00240   if (mInputRects)
00241     XFree (mInputRects);
00242 
00243   mInputRects = rects;
00244   mNInputRects = count;
00245   mInputRectOrdering = ordering;
00246 
00247   rects = XShapeGetRectangles (mDpy, mShapeWindow, ShapeBounding,
00248                                &count, &ordering);
00249 
00250   /* check if the returned shape exactly matches the window shape -
00251    * if that is true, the window currently has no set bounding shape */
00252   if ((count == 1) &&
00253       (rects[0].x == -((int) border)) &&
00254       (rects[0].y == -((int) border)) &&
00255       (rects[0].width == (width + border)) &&
00256       (rects[0].height == (height + border)))
00257   {
00258     count = 0;
00259   }
00260 
00261   if (mBoundingRects)
00262     XFree (mBoundingRects);
00263 
00264   mBoundingRects = rects;
00265   mNBoundingRects = count;
00266   mBoundingRectOrdering = ordering;
00267 
00268   mShapeMask = XShapeInputSelected (mDpy, mShapeWindow);
00269 
00270   return true;
00271 }
00272 
00273 bool
00274 compiz::WindowInputRemover::removeInput ()
00275 {
00276   if (!mNInputRects)
00277     if (!save ())
00278       return false;
00279 
00280   XShapeSelectInput (mDpy, mShapeWindow, NoEventMask);
00281 
00282   XShapeCombineRectangles (mDpy, mShapeWindow, ShapeInput, 0, 0,
00283                            NULL, 0, ShapeSet, 0);
00284   XShapeCombineRectangles (mDpy, mShapeWindow, ShapeBounding, 0, 0,
00285                            NULL, 0, ShapeSet, 0);
00286 
00287   XShapeSelectInput (mDpy, mShapeWindow, mShapeMask);
00288 
00289   mRemoved = true;
00290 
00291   sendShapeNotify ();
00292 
00293   return true;
00294 }
00295 
00296 bool
00297 compiz::WindowInputRemover::restoreInput ()
00298 {
00299   XShapeSelectInput (mDpy, mShapeWindow, NoEventMask);
00300 
00301   if (mRemoved)
00302   {
00303     if (mNInputRects)
00304     {
00305       XShapeCombineRectangles (mDpy, mShapeWindow, ShapeInput, 0, 0,
00306                               mInputRects, mNInputRects,
00307                               ShapeSet, mInputRectOrdering);
00308 
00309     }
00310     else
00311     {
00312       XShapeCombineMask (mDpy, mShapeWindow, ShapeInput,
00313                       0, 0, None, ShapeSet);
00314     }
00315 
00316     if (mInputRects)
00317     {
00318       XFree (mInputRects);
00319       mInputRects = NULL;
00320       mNInputRects = 0;
00321     }
00322 
00323     if (mNBoundingRects)
00324     {
00325       XShapeCombineRectangles (mDpy, mShapeWindow, ShapeBounding, 0, 0,
00326                          mBoundingRects, mNBoundingRects,
00327                          ShapeSet, mBoundingRectOrdering);
00328     }
00329     else
00330     {
00331       XShapeCombineMask (mDpy, mShapeWindow, ShapeBounding,
00332                    0, 0, None, ShapeSet);
00333     }
00334 
00335     if (mBoundingRects)
00336     {
00337       XFree (mBoundingRects);
00338       mBoundingRects = NULL;
00339       mNBoundingRects = 0;
00340     }
00341   }
00342 
00343   XShapeSelectInput (mDpy, mShapeWindow, mShapeMask);
00344 
00345   mRemoved = false;
00346 
00347   sendShapeNotify ();
00348 
00349   return true;
00350 }