I have made another delayed NMU for the 5.4.68 release, because the latest slop upload breaks it as well...
A.
diff -Nru maim-5.4.64/CMakeLists.txt maim-5.4.68/CMakeLists.txt --- maim-5.4.64/CMakeLists.txt 2017-07-20 10:05:29.000000000 -0400 +++ maim-5.4.68/CMakeLists.txt 2017-08-15 21:35:19.000000000 -0400 @@ -18,7 +18,7 @@ set( MANTARGET "maim.1" ) endif() -add_definitions(-DMAIM_VERSION="v5.4.64") +add_definitions(-DMAIM_VERSION="v5.4.68") set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/bin/") @@ -35,13 +35,15 @@ # Obtain library paths and make sure they exist. set( CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" "${CMAKE_SOURCE_DIR}/modules" ) -find_package( PNG REQUIRED ) -find_package( JPEG REQUIRED ) -find_package( XRandr REQUIRED ) -find_package( XFixes REQUIRED ) -find_package( X11 REQUIRED ) -find_package( SLOP REQUIRED ) -find_package( Threads REQUIRED ) +find_package( PNG REQUIRED ) +find_package( JPEG REQUIRED ) +find_package( XRandr REQUIRED ) +find_package( XRender REQUIRED ) +find_package( XFixes REQUIRED ) +find_package( XComposite REQUIRED ) +find_package( X11 REQUIRED ) +find_package( SLOP REQUIRED ) +find_package( Threads REQUIRED ) set_property(TARGET ${BIN_TARGET} PROPERTY CXX_STANDARD_REQUIRED ON) set_property(TARGET ${BIN_TARGET} PROPERTY CXX_STANDARD 11) @@ -51,8 +53,10 @@ ${X11_INCLUDE_DIR} ${SLOP_INCLUDE_DIR} ${XFIXES_INCLUDE_DIR} + ${XCOMPOSITE_INCLUDE_DIR} ${JPEG_INCLUDE_DIR} ${XRANDR_INCLUDE_DIR} + ${XRENDER_INCLUDE_DIR} ${PNG_INCLUDE_DIRS} ) # Libraries @@ -61,10 +65,25 @@ ${X11_LIBRARIES} ${PNG_LIBRARIES} ${XFIXES_LIBRARY} + ${XCOMPOSITE_LIBRARY} ${XRANDR_LIBRARY} ${JPEG_LIBRARIES} + ${XRENDER_LIBRARY} ${SLOP_LIBRARIES} ) +if( CMAKE<3.7 ) + message( WARNING "CMake version is below 3.7, CMake version >= 3.7 is required for unicode support." ) +else() + find_package(ICU COMPONENTS uc) + set( MAIM_UNICODE TRUE CACHE BOOL "To enable or disable unicode support." ) + if ( MAIM_UNICODE AND ICU_FOUND ) + # ICU is required for old nvidia drivers to work for whatever reason. + add_definitions(-DCXXOPTS_USE_UNICODE) + include_directories( ${ICU_INCLUDE_DIR} ) + target_link_libraries( ${BIN_TARGET} ${ICU_UC_LIBRARIES} ) + endif() +endif() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g") install( TARGETS ${BIN_TARGET} DESTINATION "${CMAKE_INSTALL_PREFIX}/bin" ) diff -Nru maim-5.4.64/debian/changelog maim-5.4.68/debian/changelog --- maim-5.4.64/debian/changelog 2017-07-29 10:45:07.000000000 -0400 +++ maim-5.4.68/debian/changelog 2017-08-23 16:14:51.000000000 -0400 @@ -1,3 +1,10 @@ +maim (5.4.68-1.1) unstable; urgency=medium + + * Non-maintainer upload. + * New upstream release + + -- Antoine Beaupré <anar...@debian.org> Wed, 23 Aug 2017 16:14:51 -0400 + maim (5.4.64-1.1) unstable; urgency=medium * Non-maintainer upload. diff -Nru maim-5.4.64/modules/FindXComposite.cmake maim-5.4.68/modules/FindXComposite.cmake --- maim-5.4.64/modules/FindXComposite.cmake 1969-12-31 19:00:00.000000000 -0500 +++ maim-5.4.68/modules/FindXComposite.cmake 2017-08-15 21:35:19.000000000 -0400 @@ -0,0 +1,26 @@ +# - Find XComposite +# Find the XComposite libraries +# +# This module defines the following variables: +# XCOMPOSITE_FOUND - 1 if XCOMPOSITE_INCLUDE_DIR & XCOMPOSITE_LIBRARY are found, 0 otherwise +# XCOMPOSITE_INCLUDE_DIR - where to find Xlib.h, etc. +# XCOMPOSITE_LIBRARY - the X11 library +# + +find_path( XCOMPOSITE_INCLUDE_DIR + NAMES X11/extensions/Xcomposite.h + PATH_SUFFIXES X11/extensions + DOC "The XComposite include directory" ) + +find_library( XCOMPOSITE_LIBRARY + NAMES Xcomposite + PATHS /usr/lib /lib + DOC "The XComposite library" ) + +if( XCOMPOSITE_INCLUDE_DIR AND XCOMPOSITE_LIBRARY ) + set( XCOMPOSITE_FOUND 1 ) +else() + set( XCOMPOSITE_FOUND 0 ) +endif() + +mark_as_advanced( XCOMPOSITE_INCLUDE_DIR XCOMPOSITE_LIBRARY ) diff -Nru maim-5.4.64/modules/FindXRender.cmake maim-5.4.68/modules/FindXRender.cmake --- maim-5.4.64/modules/FindXRender.cmake 1969-12-31 19:00:00.000000000 -0500 +++ maim-5.4.68/modules/FindXRender.cmake 2017-08-15 21:35:19.000000000 -0400 @@ -0,0 +1,47 @@ +# - Find XRender +# Find the XRender libraries +# +# This module defines the following variables: +# XRENDER_FOUND - true if XRENDER_INCLUDE_DIR & XRENDER_LIBRARY are found +# XRENDER_LIBRARIES - Set when Xrender_LIBRARY is found +# XRENDER_INCLUDE_DIRS - Set when Xrender_INCLUDE_DIR is found +# +# XRENDER_INCLUDE_DIR - where to find Xrender.h, etc. +# XRENDER_LIBRARY - the Xrender library +# + +#============================================================================= +# Copyright 2013 Corey Clayton <can.of.t...@gmail.com> +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#============================================================================= + +find_path(XRENDER_INCLUDE_DIR NAMES X11/extensions/Xrender.h + PATHS /opt/X11/include + DOC "The Xrender include directory") + +find_library(XRENDER_LIBRARY NAMES Xrender + PATHS /opt/X11/lib + DOC "The Xrender library") + +include(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(Xrender DEFAULT_MSG XRENDER_LIBRARY XRENDER_INCLUDE_DIR) + +if(XRENDER_FOUND) + + set(XRENDER_LIBRARIES ${XRENDER_LIBRARY}) + set(XRENDER_INCLUDE_DIRS ${XRENDER_INCLUDE_DIR}) + +endif() + +mark_as_advanced(XRENDER_INCLUDE_DIR XRENDER_LIBRARY) diff -Nru maim-5.4.64/src/image.cpp maim-5.4.68/src/image.cpp --- maim-5.4.64/src/image.cpp 2017-07-20 10:05:29.000000000 -0400 +++ maim-5.4.68/src/image.cpp 2017-08-15 21:35:19.000000000 -0400 @@ -9,7 +9,7 @@ this->imagey = iloc.y; this->channels = channels; glm::ivec2 spos = glm::ivec2( selectionrect.x, selectionrect.y ); - glm::ivec2 offset = spos-iloc; + offset = spos-iloc; long long int alpha_mask = ~(image->red_mask|image->green_mask|image->blue_mask); long long int roffset = get_shift(image->red_mask); long long int goffset = get_shift(image->green_mask); @@ -116,16 +116,16 @@ throw new std::invalid_argument("Quality argument must be between 1 and 10"); } png_structp png = NULL; - png_infop info = NULL; - png_bytep *rows = new png_bytep[height]; + png_infop info = NULL; + png_bytep *rows = new png_bytep[height]; - png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if(!png) throw new std::runtime_error( "Failed to write png image" ); - info = png_create_info_struct(png); - if(!info) throw new std::runtime_error( "Failed to write png image" ); + png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if(!png) throw new std::runtime_error( "Failed to write png image" ); + info = png_create_info_struct(png); + if(!info) throw new std::runtime_error( "Failed to write png image" ); png_set_error_fn(png, png_get_error_ptr(png), user_error_fn, user_warning_fn); png_set_write_fn(png, &streamout, png_write_ostream, png_flush_ostream); - png_set_compression_level(png, quality-1); + png_set_compression_level(png, quality-1); if ( channels == 4 ) { png_set_IHDR(png, info, width, height, 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, @@ -150,7 +150,7 @@ * happen since we allocated our buffer to be big to start with */ boolean empty_buffer(jpeg_compress_struct* cinfo) { - return TRUE; + return TRUE; } /* finalize the buffer and do any cleanup stuff */ @@ -160,45 +160,45 @@ if ( channels != 3 ) { throw new std::runtime_error( "JPEG tried to save image with more than 3 channels." ); } - struct jpeg_compress_struct cinfo; - struct jpeg_error_mgr jerr; - struct jpeg_destination_mgr dmgr; - - /* create our in-memory output buffer to hold the jpeg */ - JOCTET * out_buffer = new JOCTET[width * height *3]; - - /* here is the magic */ - dmgr.init_destination = init_buffer; - dmgr.empty_output_buffer = empty_buffer; - dmgr.term_destination = term_buffer; - dmgr.next_output_byte = out_buffer; - dmgr.free_in_buffer = width * height *3; - - cinfo.err = jpeg_std_error(&jerr); - jpeg_create_compress(&cinfo); - - /* make sure we tell it about our manager */ - cinfo.dest = &dmgr; - - cinfo.image_width = width; - cinfo.image_height = height; - cinfo.input_components = 3; - cinfo.in_color_space = JCS_RGB; + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + struct jpeg_destination_mgr dmgr; + + /* create our in-memory output buffer to hold the jpeg */ + JOCTET * out_buffer = new JOCTET[width * height *3]; + + /* here is the magic */ + dmgr.init_destination = init_buffer; + dmgr.empty_output_buffer = empty_buffer; + dmgr.term_destination = term_buffer; + dmgr.next_output_byte = out_buffer; + dmgr.free_in_buffer = width * height *3; + + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_compress(&cinfo); + + /* make sure we tell it about our manager */ + cinfo.dest = &dmgr; + + cinfo.image_width = width; + cinfo.image_height = height; + cinfo.input_components = 3; + cinfo.in_color_space = JCS_RGB; - jpeg_set_defaults(&cinfo); + jpeg_set_defaults(&cinfo); // Convert quality from scale 1-10 to 0-100 - jpeg_set_quality (&cinfo, (int)((float)quality-1.f)*(100.f/9.f), true); - jpeg_start_compress(&cinfo, true); + jpeg_set_quality (&cinfo, (int)((float)quality-1.f)*(100.f/9.f), true); + jpeg_start_compress(&cinfo, true); - JSAMPROW row_pointer; - unsigned char* buffer = (unsigned char*)data; + JSAMPROW row_pointer; + unsigned char* buffer = (unsigned char*)data; - /* main code to write jpeg data */ - while (cinfo.next_scanline < cinfo.image_height) { - row_pointer = (JSAMPROW) &buffer[cinfo.next_scanline * 3*width]; - jpeg_write_scanlines(&cinfo, &row_pointer, 1); - } - jpeg_finish_compress(&cinfo); + /* main code to write jpeg data */ + while (cinfo.next_scanline < cinfo.image_height) { + row_pointer = (JSAMPROW) &buffer[cinfo.next_scanline * 3*width]; + jpeg_write_scanlines(&cinfo, &row_pointer, 1); + } + jpeg_finish_compress(&cinfo); streamout.write( (const char*)out_buffer, cinfo.dest->next_output_byte - out_buffer ); delete[] out_buffer; @@ -267,6 +267,9 @@ } void ARGBImage::blendCursor( X11* x11 ) { + if ( !x11->haveXFixes ) { + return; + } XFixesCursorImage* xcursor = XFixesGetCursorImage( x11->display ); if ( !xcursor ) { return; @@ -276,8 +279,8 @@ for ( unsigned int i=0;i<xcursor->width*xcursor->height;i++ ) { ((unsigned int*)pixels)[ i ] = (unsigned int)xcursor->pixels[ i ]; } - xcursor->y -= xcursor->yhot; - xcursor->x -= xcursor->xhot; + xcursor->y -= xcursor->yhot + offset.x; + xcursor->x -= xcursor->xhot + offset.y; for ( int y = glm::max(0,xcursor->y-imagey); y<glm::min((int)height,xcursor->y+xcursor->height-imagey);y++ ) { for ( int x = glm::max(0,xcursor->x-imagex); x < glm::min((int)width,xcursor->x+xcursor->width-imagex);x++ ) { int cx = x-(xcursor->x-imagex); @@ -286,6 +289,10 @@ data[(y*width+x)*channels] = data[(y*width+x)*channels]*(1-alpha) + pixels[(cy*xcursor->width+cx)*4+2]*alpha; data[(y*width+x)*channels+1] = data[(y*width+x)*channels+1]*(1-alpha) + pixels[(cy*xcursor->width+cx)*4+1]*alpha; data[(y*width+x)*channels+2] = data[(y*width+x)*channels+2]*(1-alpha) + pixels[(cy*xcursor->width+cx)*4]*alpha; + // If the original image has alpha, we need to override it. + if ( channels == 4 ) { + data[(y*width+x)*channels+3] = glm::min(data[(y*width+x)*channels+3]+pixels[(cy*xcursor->width+cx)*4+3],255); + } } } } diff -Nru maim-5.4.64/src/image.hpp maim-5.4.68/src/image.hpp --- maim-5.4.64/src/image.hpp 2017-07-20 10:05:29.000000000 -0400 +++ maim-5.4.68/src/image.hpp 2017-08-15 21:35:19.000000000 -0400 @@ -32,16 +32,17 @@ #include "x.hpp" static inline unsigned char computeRGBPixel(unsigned char* data, XImage* image, int x, int y, int roffset, int goffset, int boffset, int width, glm::ivec2 offset ) { - unsigned int real = XGetPixel(image, x, y); int curpixel = ((y-offset.y)*width+((x-offset.x)))*3; + unsigned int real = XGetPixel(image, x, y); data[curpixel] = (unsigned char)((real & image->red_mask) >> roffset); data[curpixel+1] = (unsigned char)((real & image->green_mask) >> goffset); data[curpixel+2] = (unsigned char)((real & image->blue_mask) >> boffset); } static inline unsigned char computeRGBAPixel(unsigned char* data, XImage* image, int x, int y, int roffset, int goffset, int boffset, int aoffset, int width, glm::ivec2 offset ) { + int curpixel = ((y-offset.y)*width+(x-offset.x))*4; + //unsigned int real = ((unsigned int*)image->data)[curpixel/4]; unsigned int real = XGetPixel(image, x, y); - int curpixel = ((y-offset.y)*width+((x-offset.x)))*4; data[curpixel] = (unsigned char)((real & image->red_mask) >> roffset); data[curpixel+1] = (unsigned char)((real & image->green_mask) >> goffset); data[curpixel+2] = (unsigned char)((real & image->blue_mask) >> boffset); @@ -49,8 +50,9 @@ } static inline unsigned char computeRGBAPixel(unsigned char* data, XImage* image, int x, int y, int roffset, int goffset, int boffset, int width, glm::ivec2 offset ) { - unsigned int real = XGetPixel(image, x, y); int curpixel = ((y-offset.y)*width+((x-offset.x)))*4; + //unsigned int real = ((unsigned int*)image->data)[curpixel/4]; + unsigned int real = XGetPixel(image, x, y); data[curpixel] = (unsigned char)((real & image->red_mask) >> roffset); data[curpixel+1] = (unsigned char)((real & image->green_mask) >> goffset); data[curpixel+2] = (unsigned char)((real & image->blue_mask) >> boffset); @@ -74,6 +76,7 @@ unsigned int height; unsigned int channels; int imagex, imagey; + glm::ivec2 offset; bool intersect( XRRCrtcInfo* a, glm::vec4 b ); bool containsCompletely( XRRCrtcInfo* a, glm::vec4 b ); public: diff -Nru maim-5.4.64/src/main.cpp maim-5.4.68/src/main.cpp --- maim-5.4.64/src/main.cpp 2017-07-20 10:05:29.000000000 -0400 +++ maim-5.4.68/src/main.cpp 2017-08-15 21:35:19.000000000 -0400 @@ -216,7 +216,7 @@ slop::SlopOptions* getSlopOptions( cxxopts::Options& options ) { slop::SlopOptions* foo = new slop::SlopOptions(); if ( options.count( "bordersize" ) > 0 ) { - foo->borderSize = options["bordersize"].as<float>(); + foo->border = options["bordersize"].as<float>(); } if ( options.count( "padding" ) > 0 ) { foo->padding = options["padding"].as<float>(); @@ -235,16 +235,25 @@ if ( options.count( "nokeyboard" ) > 0 ) { foo->nokeyboard = options["nokeyboard"].as<bool>(); } + if ( options.count( "noopengl" ) > 0 ) { + foo->noopengl = options["noopengl"].as<bool>(); + } if ( options.count( "xdisplay" ) > 0 ) { - foo->xdisplay = options["xdisplay"].as<std::string>(); + std::string xdisplay = options["xdisplay"].as<std::string>(); + char* cxdisplay = new char[xdisplay.length()+1]; + memcpy( cxdisplay, xdisplay.c_str(), xdisplay.length() ); + cxdisplay[xdisplay.length()]='\0'; + foo->xdisplay = cxdisplay; } - std::string shaders = "textured"; if ( options.count( "shader" ) > 0 ) { - shaders = options["shader"].as<std::string>(); + std::string shaders = options["shader"].as<std::string>(); + char* cshaders = new char[shaders.length()+1]; + memcpy( cshaders, shaders.c_str(), shaders.length() ); + cshaders[shaders.length()]='\0'; + foo->shaders = cshaders; } - foo->shaders = split( shaders, ',' ); - if ( options.count( "noopengl" ) > 0 ) { - foo->noopengl = options["noopengl"].as<bool>(); + if ( options.count( "quiet" ) > 0 ) { + foo->quiet = options["quiet"].as<bool>(); } if ( options.count( "highlight" ) > 0 ) { foo->highlight = options["highlight"].as<bool>(); @@ -429,15 +438,14 @@ help(); return 0; } - bool cancelled = false; - slop::SlopSelection selection(0,0,0,0,0); + slop::SlopSelection selection(0,0,0,0,0,true); if ( maimOptions->select ) { if ( maimOptions->windowGiven || maimOptions->parentGiven || maimOptions->geometryGiven ) { throw new std::invalid_argument( "Interactive mode (--select) doesn't support the following parameters: --window, --parent, --geometry." ); } - selection = SlopSelect(slopOptions, &cancelled, maimOptions->quiet); - if ( cancelled ) { + selection = SlopSelect(slopOptions); + if ( selection.cancelled ) { if ( !maimOptions->quiet ) { std::cerr << "Selection was cancelled by keystroke or right-click.\n"; } @@ -551,6 +559,7 @@ // Then output it in the desired format. convert.writeJPEG(*out, maimOptions->quality ); } + XDestroyImage( image ); if ( maimOptions->savepathGiven ) { std::ofstream* file = (std::ofstream*)out; @@ -559,9 +568,15 @@ } delete x11; delete maimOptions; + if ( options.count( "xdisplay" ) > 0 ) { + delete slopOptions->xdisplay; + } + if ( options.count( "shader" ) > 0 ) { + delete slopOptions->shaders; + } delete slopOptions; - return 0; + return 0; } int main( int argc, char** argv ) { diff -Nru maim-5.4.64/src/x.cpp maim-5.4.68/src/x.cpp --- maim-5.4.64/src/x.cpp 2017-07-20 10:05:29.000000000 -0400 +++ maim-5.4.68/src/x.cpp 2017-08-15 21:35:19.000000000 -0400 @@ -6,31 +6,79 @@ _x_err = 1; return 0; } -/* -glm::ivec4 getWindowGeometry( X11* x11, Window win ) { - XWindowAttributes attr; - XGetWindowAttributes( x11->display, win, &attr ); - unsigned int width = attr.width; - unsigned int height = attr.height; - unsigned int border = attr.border_width; - int x, y; - Window junk; - XTranslateCoordinates( x11->display, win, attr.root, 0, 0, &x, &y, &junk ); - return glm::vec4( x, y, width, height ); -} -*/ + glm::ivec4 getWindowGeometry( X11* x11, Window win ) { - XWindowAttributes attr; - XGetWindowAttributes( x11->display, win, &attr ); - unsigned int width = attr.width; - unsigned int height = attr.height; - unsigned int border = attr.border_width; - int x, y; - Window junk; - XTranslateCoordinates( x11->display, win, attr.root, -attr.border_width, -attr.border_width, &x, &y, &junk ); - width += border*2; - height += border*2; - return glm::vec4( x, y, width, height ); + // 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 ); + + // 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 ); + } } std::vector<XRRCrtcInfo*> X11::getCRTCS() { @@ -51,35 +99,49 @@ } X11::X11( std::string displayName ) { - // Initialize display - 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 ); + // Initialize display + 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 = XShmQueryVersion( display, &major, &minor, &pixmaps ); + 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 = XRRQueryVersion( display, &major, &minor ); + 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 )); + if ( haveXRR ) { res = XRRGetScreenResourcesCurrent( display, root ); } } X11::~X11() { - XCloseDisplay( display ); + if ( haveXRR ) { + XRRFreeScreenResources( res ); + } + XCloseDisplay( display ); } 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 ); @@ -87,57 +149,173 @@ 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); - // This is a HUGE no-no, but it keeps the image from changing under our feet. - XGrabServer(display); - if ( haveXShm ) { - // Try to grab the image through shared memory, if we fail try another method. - XErrorHandler ph = XSetErrorHandler(TmpXError); - XImage* image = getImageShm( draw, x, y, w, h ); - XSetErrorHandler(ph); - if ( !_x_err && image != None ) { - XUngrabServer(display); - return image; + 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. } - Window junk; - XTranslateCoordinates( this->display, this->root, draw, x, y, &x, &y, &junk); - XUngrabServer(display); - return XGetImage( display, draw, x, y, w, h, AllPlanes, ZPixmap ); -} - -// Basically a faster image grabber. -XImage* X11::getImageShm(Window draw, int x, int y, 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; - } + if ( haveXRender && haveXFixes ) { + return getImageUsingXRender( draw, localx, localy, w, h ); + } + // 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 ); +} - /* get an shm id of this image */ - thing.shmid = shmget(IPC_PRIVATE, xim->bytes_per_line * xim->height, IPC_CREAT | 0666); - /* 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, x, y, 0xffffffff); - 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; +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 ); +} + +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; } diff -Nru maim-5.4.64/src/x.hpp maim-5.4.68/src/x.hpp --- maim-5.4.64/src/x.hpp 2017-07-20 10:05:29.000000000 -0400 +++ maim-5.4.68/src/x.hpp 2017-08-15 21:35:19.000000000 -0400 @@ -23,10 +23,13 @@ #include <iostream> #include <X11/Xlib.h> +#include <X11/Xatom.h> #include <X11/extensions/XShm.h> #include <X11/extensions/Xrender.h> +#include <X11/extensions/shape.h> #include <X11/extensions/Xcomposite.h> #include <X11/extensions/Xrandr.h> +//#include <meta/meta-shadow-factory.h> #include <sys/shm.h> #include <string> #include <vector> @@ -35,9 +38,17 @@ class X11 { private: - bool haveXShm; - XImage* getImageShm( Window d, int x, int y, int w, int h ); + bool hasClipping( Window d ); + XserverRegion findRegion( Window d ); + void unionClippingRegions( XserverRegion rootRegion, Window child ); + void unionBorderRegions( XserverRegion rootRegion, Window d ); + XImage* getImageUsingXRender( Window draw, int localx, int localy, int w, int h ); + XImage* getImageUsingXShm( Window draw, int localx, int localy, int w, int h ); public: + bool haveXComposite; + bool haveXRender; + bool haveXShm; + bool haveXFixes; bool haveXRR; X11( std::string displayName ); ~X11(); @@ -52,6 +63,5 @@ }; glm::ivec4 getWindowGeometry( X11* x11, Window win ); -//glm::ivec4 getWindowGeometryWithoutBorder( X11* x11, Window win ); #endif
signature.asc
Description: PGP signature