summaryrefslogtreecommitdiff
path: root/src/x.cpp
diff options
context:
space:
mode:
authorAntoine Beaupré <anarcat@debian.org>2017-08-23 16:11:28 -0400
committerAntoine Beaupré <anarcat@debian.org>2017-08-23 16:11:28 -0400
commit66e6a9296b651d82a4f46bd381f0edef7bdd84d3 (patch)
tree860d323448ae57a8b9b73e2c5afdf5515673aa35 /src/x.cpp
parent6d36cc6ec5e97c715dc8162fc6efe172133bf268 (diff)
New upstream version 5.4.68
Diffstat (limited to 'src/x.cpp')
-rw-r--r--src/x.cpp370
1 files changed, 298 insertions, 72 deletions
diff --git a/src/x.cpp b/src/x.cpp
index c31cd50..f4168d4 100644
--- a/src/x.cpp
+++ b/src/x.cpp
@@ -1,95 +1,321 @@
-/* x.cpp: Handles starting and managing X
- *
- * Copyright (C) 2014: Dalton Nell, Maim Contributors (https://github.com/naelstrof/maim/graphs/contributors).
- *
- * This file is part of Maim.
- *
- * Maim 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 3 of the License, or
- * (at your option) any later version.
- *
- * Maim 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 Maim. If not, see <http://www.gnu.org/licenses/>.
- */
-
#include "x.hpp"
-maim::XEngine* xengine = new maim::XEngine();
+static char _x_err = 0;
+static int
+TmpXError(Display * d, XErrorEvent * ev) {
+ _x_err = 1;
+ return 0;
+}
+
+glm::ivec4 getWindowGeometry( X11* x11, Window win ) {
+ // First lets check for if we're a window manager frame.
+ Window root, parent;
+ Window* children;
+ unsigned int num_children;
+ XQueryTree( x11->display, win, &root, &parent, &children, &num_children);
+
+ // To do that, we check if our top level child happens to have the _NET_FRAME_EXTENTS atom.
+ unsigned char *data;
+ Atom type_return;
+ unsigned long nitems_return;
+ unsigned long bytes_after_return;
+ int format_return;
+ bool window_frame = false;
+ Window actualWindow = win;
+ if ( num_children > 0 && XGetWindowProperty( x11->display, children[num_children-1],
+ XInternAtom( x11->display, "_NET_FRAME_EXTENTS", False),
+ 0, LONG_MAX, False, XA_CARDINAL, &type_return,
+ &format_return, &nitems_return, &bytes_after_return,
+ &data) == Success ) {
+ if ((type_return == XA_CARDINAL) && (format_return == 32) && (nitems_return == 4) && (data)) {
+ actualWindow = children[num_children-1];
+ window_frame = true;
+ }
+ }
+ XFree( children );
-maim::XEngine::XEngine() {
- m_display = NULL;
- m_visual = NULL;
- m_screen = NULL;
- m_good = false;
+ // If we're a window frame, we actually get the dimensions of the child window, then add the _NET_FRAME_EXTENTS to it.
+ // (then add the border width of the window frame after that.)
+ if ( window_frame ) {
+ // First lets grab the border width.
+ XWindowAttributes frameattr;
+ XGetWindowAttributes( x11->display, win, &frameattr );
+ // Then lets grab the dims of the child window.
+ XWindowAttributes attr;
+ XGetWindowAttributes( x11->display, actualWindow, &attr );
+ unsigned int width = attr.width;
+ unsigned int height = attr.height;
+ // We combine both border widths.
+ unsigned int border = attr.border_width+frameattr.border_width;
+ int x, y;
+ // Gotta translate them into root coords, we can adjust for the border width here.
+ Window junk;
+ XTranslateCoordinates( x11->display, actualWindow, attr.root, -border, -border, &x, &y, &junk );
+ width += border*2;
+ height += border*2;
+ // Now uh, remember that _NET_FRAME_EXTENTS stuff? That's the window frame information.
+ // We HAVE to do this because mutter likes to mess with window sizes with shadows and stuff.
+ unsigned long* ldata = (unsigned long*)data;
+ width += ldata[0] + ldata[1];
+ height += ldata[2] + ldata[3];
+ x -= ldata[0];
+ y -= ldata[2];
+ XFree( data );
+ return glm::vec4( x, y, width, height );
+ } else {
+ // Either the WM is malfunctioning, or the window secified isn't a window manager frame.
+ // so we just rely on X.
+ XWindowAttributes attr;
+ XGetWindowAttributes( x11->display, win, &attr );
+ unsigned int width = attr.width;
+ unsigned int height = attr.height;
+ // We combine both border widths.
+ unsigned int border = attr.border_width;
+ int x, y;
+ // Gotta translate them into root coords, we can adjust for the border width here.
+ Window junk;
+ XTranslateCoordinates( x11->display, win, attr.root, -border, -border, &x, &y, &junk );
+ width += border*2;
+ height += border*2;
+ return glm::vec4( x, y, width, height );
+ }
}
-maim::XEngine::~XEngine() {
- if ( !m_good ) {
- return;
+std::vector<XRRCrtcInfo*> X11::getCRTCS() {
+ std::vector<XRRCrtcInfo*> monitors;
+ if ( !res ) {
+ return monitors;
+ }
+ for ( int i=0;i<res->ncrtc;i++ ) {
+ monitors.push_back( XRRGetCrtcInfo( display, res, res->crtcs[ i ] ) );
+ }
+ return monitors;
+}
+
+void X11::freeCRTCS( std::vector<XRRCrtcInfo*> monitors ) {
+ for ( unsigned int i=0;i<monitors.size();i++ ) {
+ XRRFreeCrtcInfo( monitors[ i ] );
}
- XCloseDisplay( m_display );
}
-int maim::XEngine::init( std::string display ) {
+X11::X11( std::string displayName ) {
// Initialize display
- m_display = XOpenDisplay( display.c_str() );
- if ( !m_display ) {
- fprintf( stderr, "Error: Failed to open X display %s\n", display.c_str() );
- return 1;
- }
- m_screen = ScreenOfDisplay( m_display, DefaultScreen( m_display ) );
- m_visual = DefaultVisual ( m_display, XScreenNumberOfScreen( m_screen ) );
- m_colormap = DefaultColormap( m_display, XScreenNumberOfScreen( m_screen ) );
- m_root = RootWindow ( m_display, XScreenNumberOfScreen( m_screen ) );
- //m_root = DefaultRootWindow( m_display );
-
- // We ignore X errors since we don't care if we fail to get
- // the physical monitor positions.
- XErrorHandler originalHandler = XSetErrorHandler( maim::IgnoreErrorHandler );
- m_res = XRRGetScreenResourcesCurrent( m_display, m_root);
- XSetErrorHandler( originalHandler );
- if ( !m_res ) {
- fprintf( stderr, "Warning: Failed to get screen resources. Multi-monitor X screens won't have garbage visual data removed.\n" );
+ display = XOpenDisplay( displayName.c_str() );
+ if ( !display ) {
+ throw new std::runtime_error(std::string("Error: Failed to open X display: ") + displayName );
}
+ screen = ScreenOfDisplay( display, DefaultScreen( display ) );
+ visual = DefaultVisual( display, XScreenNumberOfScreen( screen ) );
+ root = DefaultRootWindow( display );
+ int major = 0;
+ int minor = 0;
+ Bool pixmaps = true;
+ haveXShm = (True == XShmQueryVersion( display, &major, &minor, &pixmaps ));
+ haveXShm = (haveXShm && pixmaps );
+ major = 2;
+ minor = 0;
+ haveXFixes = (True == XFixesQueryVersion ( display, &major, &minor ));
+ major = 0;
+ minor = 0;
+ haveXRR = (True == XRRQueryVersion( display, &major, &minor ) );
+ major = 0;
+ minor = 2;
+ haveXComposite = (True == XCompositeQueryVersion( display, &major, &minor ));
+ major = 0;
+ minor = 0;
+ haveXRender = (True == XRenderQueryVersion( display, &major, &minor ));
- m_good = true;
- return EXIT_SUCCESS;
+ if ( haveXRR ) {
+ res = XRRGetScreenResourcesCurrent( display, root );
+ }
}
-Window maim::XEngine::getWindowByID( int id ) {
- // There's actually no way to check if the id is valid...
- return (Window)id;
- // The only thing we can do is use it and see if we get a BadWindow error later.
+X11::~X11() {
+ if ( haveXRR ) {
+ XRRFreeScreenResources( res );
+ }
+ XCloseDisplay( display );
}
-// This stuff is used to detect which pixels we can actually see with
-// the physical monitor positions.
-// It's useful for people with multimonitor setups where the monitors
-// don't fit together well since we can black out the pixels that are
-// generally just garbage.
-std::vector<XRRCrtcInfo*> maim::XEngine::getCRTCS() {
- std::vector<XRRCrtcInfo*> monitors;
- if ( !m_res ) {
- return monitors;
+XImage* X11::getImage( Window draw, int x, int y, int w, int h, glm::ivec2& imageloc ) {
+ glm::ivec4 sourceGeo = getWindowGeometry( this, draw );
+ // We need to clamp the selection to fit within the
+ // provided window.
+
+ x = glm::clamp( x, sourceGeo.x, sourceGeo.x+sourceGeo.z );
+ y = glm::clamp( y, sourceGeo.y, sourceGeo.y+sourceGeo.w );
+ w = glm::clamp( w, 1, sourceGeo.x+sourceGeo.z-x );
+ h = glm::clamp( h, 1, sourceGeo.y+sourceGeo.w-y );
+
+ imageloc = glm::ivec2( x, y );
+
+ // Translate the newly clamped selection to local coordinates.
+ int localx, localy;
+ Window junk;
+ XTranslateCoordinates( this->display, this->root, draw, x, y, &localx, &localy, &junk);
+
+ if ( haveXComposite ) {
+ // We redirect all the pixmaps offscreen, so that they won't be corrupted if obscured.
+ for ( int i = 0; i < ScreenCount( display ); i++ ) {
+ XCompositeRedirectSubwindows( display, RootWindow( display, i ), CompositeRedirectAutomatic );
+ }
+ // We don't have to worry about undoing the redirect, since as soon as maim closes X knows to undo it.
}
- for ( int i=0;i<m_res->ncrtc;i++ ) {
- monitors.push_back( XRRGetCrtcInfo( m_display, m_res, m_res->crtcs[ i ] ) );
+ if ( haveXRender && haveXFixes ) {
+ return getImageUsingXRender( draw, localx, localy, w, h );
}
- return monitors;
+ // This stuff doesn't work very well...
+ //if ( haveXShm ) {
+ //XErrorHandler ph = XSetErrorHandler(TmpXError);
+ //XImage* check = getImageUsingXShm( draw, localx, localy, w, h );
+ //XSetErrorHandler(ph);
+ //if ( !_x_err && check != None ) {
+ //return check;
+ //}
+ //}
+ return XGetImage( display, draw, localx, localy, w, h, AllPlanes, ZPixmap );
}
-void maim::XEngine::freeCRTCS( std::vector<XRRCrtcInfo*> monitors ) {
- for ( unsigned int i=0;i<monitors.size();i++ ) {
- XRRFreeCrtcInfo( monitors[ i ] );
+XImage* X11::getImageUsingXRender( Window draw, int localx, int localy, int w, int h ) {
+ // We use XRender to grab the drawable, since it'll save it in a format we like.
+ XWindowAttributes attr;
+ XGetWindowAttributes( display, draw, &attr );
+ XRenderPictFormat *format = XRenderFindVisualFormat( display, attr.visual );
+ bool hasAlpha = ( format->type == PictTypeDirect && format->direct.alphaMask );
+ XRenderPictureAttributes pa;
+ pa.subwindow_mode = IncludeInferiors;
+ Picture picture = XRenderCreatePicture( display, draw, format, CPSubwindowMode, &pa );
+ if ( draw != root ) {
+ XserverRegion region = findRegion( draw );
+ // Also we use XRender because of this neato function here.
+ XFixesSetPictureClipRegion( display, picture, 0, 0, region );
+ XFixesDestroyRegion( display, region );
}
+
+ Pixmap pixmap = XCreatePixmap(display, root, w, h, 32);
+ XRenderPictureAttributes pa2;
+
+ XRenderPictFormat *format2 = XRenderFindStandardFormat(display, PictStandardARGB32);
+ Picture pixmapPicture = XRenderCreatePicture( display, pixmap, format2, 0, &pa2 );
+ XRenderColor c;
+ c.red = 0x0000;
+ c.green = 0x0000;
+ c.blue = 0x0000;
+ c.alpha = 0x0000;
+ XRenderFillRectangle (display, PictOpSrc, pixmapPicture, &c, 0, 0, w, h);
+ XRenderComposite(display, hasAlpha ? PictOpOver : PictOpSrc, picture, 0,
+ pixmapPicture, localx, localy, 0, 0, 0, 0,
+ w, h);
+ XImage* temp = XGetImage( display, pixmap, 0, 0, w, h, AllPlanes, ZPixmap );
+ temp->red_mask = format2->direct.redMask << format2->direct.red;
+ temp->green_mask = format2->direct.greenMask << format2->direct.green;
+ temp->blue_mask = format2->direct.blueMask << format2->direct.blue;
+ temp->depth = format2->depth;
+ return temp;
+}
+
+bool X11::hasClipping( Window d ) {
+ int bShaped, xbs, ybs, cShaped, xcs, ycs;
+ unsigned int wbs, hbs, wcs, hcs;
+ XShapeQueryExtents ( display, d, &bShaped, &xbs, &ybs, &wbs, &hbs, &cShaped, &xcs, &ycs, &wcs, &hcs );
+ return bShaped;
+}
+
+XserverRegion X11::findRegion( Window d ) {
+ XserverRegion rootRegion = XFixesCreateRegionFromWindow( display, d, WindowRegionBounding );
+ glm::vec4 rootgeo = getWindowGeometry( this, d );
+ XFixesTranslateRegion( display, rootRegion, rootgeo.x, rootgeo.y ); // Regions are in respect to the root window by default.
+ unionClippingRegions( rootRegion, d );
+ unionBorderRegions( rootRegion, d );
+ return rootRegion;
+}
+
+void X11::unionBorderRegions( XserverRegion rootRegion, Window d ) {
+ glm::vec4 bordergeo = getWindowGeometry( this, d );
+ XRectangle* rects = new XRectangle[1];
+ rects[0].x = bordergeo.x;
+ rects[0].y = bordergeo.y;
+ rects[0].width = bordergeo.z;
+ rects[0].height = bordergeo.w;
+ XserverRegion borderRegionRect = XFixesCreateRegion( display, rects, 1 );
+ XWindowAttributes attr;
+ XGetWindowAttributes( display, d, &attr );
+ rects[0].x += attr.border_width;
+ rects[0].y += attr.border_width;
+ rects[0].width -= attr.border_width*2;
+ rects[0].height -= attr.border_width*2;
+ XserverRegion regionRect = XFixesCreateRegion( display, rects, 1 );
+ XFixesSubtractRegion( display, regionRect, borderRegionRect, regionRect );
+ delete[] rects;
+ XFixesUnionRegion( display, rootRegion, rootRegion, regionRect );
+ XFixesDestroyRegion( display, regionRect );
+ XFixesDestroyRegion( display, borderRegionRect );
}
-int maim::IgnoreErrorHandler( Display* dpy, XErrorEvent* event ) {
- return EXIT_SUCCESS;
+void X11::unionClippingRegions( XserverRegion rootRegion, Window child ) {
+ Window root, parent;
+ Window* children;
+ unsigned int num_children;
+ XQueryTree( display, child, &root, &parent, &children, &num_children);
+ for ( unsigned int i=0;i<num_children;i++ ) {
+ if ( hasClipping( children[i] ) ) {
+ Window clippingWindow = children[i];
+ glm::vec4 geo = getWindowGeometry( this, clippingWindow );
+ XRectangle* rects = new XRectangle[1];
+ rects[0].x = geo.x;
+ rects[0].y = geo.y;
+ rects[0].width = geo.z;
+ rects[0].height = geo.w;
+ // We have to keep the parent's region from interfering the clipping region.
+ XserverRegion clippingWindowRect = XFixesCreateRegion( display, rects, 1 );
+ delete[] rects;
+ // So we cut a neat hole the size of the child window where the clipping happens.
+ XFixesSubtractRegion( display, rootRegion, rootRegion, clippingWindowRect );
+ XFixesDestroyRegion( display, clippingWindowRect );
+ XserverRegion childRegion = XFixesCreateRegionFromWindow( display, clippingWindow, WindowRegionBounding );
+ XFixesTranslateRegion( display, childRegion, geo.x, geo.y ); // Regions are in respect to the root window by default.
+ XFixesUnionRegion( display, rootRegion, rootRegion, childRegion );
+ XFixesDestroyRegion( display, childRegion );
+ // We don't desend deeper down the tree looking for more clipping regions, I just don't want to support
+ // applications that do that...
+ } else {
+ unionClippingRegions( rootRegion, children[i] );
+ }
+ }
+ XFree( children );
+}
+
+XImage* X11::getImageUsingXShm(Window draw, int localx, int localy, int w, int h) {
+ XImage* xim;
+ XShmSegmentInfo thing;
+
+ XWindowAttributes xattr;
+ Status s = XGetWindowAttributes (display, draw, &xattr);
+
+ /* try create an shm image */
+ xim = XShmCreateImage(display, xattr.visual, xattr.depth, ZPixmap, 0, &thing, w, h);
+ if (!xim) {
+ return None;
+ }
+
+ /* get an shm id of this image */
+ thing.shmid = shmget(IPC_PRIVATE, xim->bytes_per_line * xim->height, IPC_CREAT | 0777);
+ /* if the get succeeds */
+ if (thing.shmid != -1) {
+ /* set the params for the shm segment */
+ thing.readOnly = False;
+ thing.shmaddr = xim->data = (char*)shmat(thing.shmid, 0, 0);
+ /* get the shm addr for this data chunk */
+ if (xim->data != (char *)-1) {
+ XShmAttach(display, &thing);
+ XShmGetImage(display, draw, xim, localx, localy, AllPlanes);
+ return xim;
+ //shmdt(thing.shmaddr);
+ }
+ /* get failed - out of shm id's or shm segment too big ? */
+ /* remove the shm id we created */
+ shmctl(thing.shmid, IPC_RMID, 0);
+ shmdt(thing.shmaddr);
+ }
+ return None;
}