Codebase list maim / 66e6a92
New upstream version 5.4.68 Antoine Beaupré 6 years ago
30 changed file(s) with 3213 addition(s) and 3147 deletion(s). Raw diff Collapse all Expand all
0 cmake_minimum_required( VERSION 2.8 )
0 cmake_minimum_required( VERSION 3.1.3 )
11
22 project( "maim" )
3 set( maim_VERSION_MAJOR 3 )
4 set( maim_VERSION_MINOR 3 )
5 set( maim_VERSION_PATCH 41 )
63
7 set( BIN_TARGET "${PROJECT_NAME}" )
4 set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build (Debug or Release)")
85
96 if ( NOT CMAKE_INSTALL_PREFIX )
10 set( CMAKE_INSTALL_PREFIX "/usr" )
7 set(CMAKE_INSTALL_PREFIX "/usr")
118 endif()
129
1310 set( CMAKE_INSTALL_MANDIR "${CMAKE_INSTALL_PREFIX}/share/man" CACHE PATH "Directory where man pages reside. (/usr/share/man, /usr/local/share/man, etc.)" )
1512 set( CMAKE_COMPRESS_MAN TRUE CACHE BOOL "Whether or not to compress the man pages for install." )
1613
1714 if ( CMAKE_COMPRESS_MAN )
18 set( MANTARGET "man-src/maim.1.gz" )
15 set( MANTARGET "maim.1.gz" )
1916 else()
20 set( MANTARGET "man-src/maim.1" )
17 set( MANTARGET "maim.1" )
2118 endif()
2219
23 if( NOT CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE )
24 set( CMAKE_BUILD_TYPE RelWithDebInfo )
25 endif()
20 add_definitions(-DMAIM_VERSION="v5.4.68")
2621
27 # Linux compiler initialization.
28 if ( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR
29 "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR
30 "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel" )
31 set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-unused-parameter" )
32 set( CMAKE_CXX_FLAGS_DEBUG "-Wextra -pedantic-errors -O0 -g" )
33 set( CMAKE_CXX_FLAGS_RELEASE "-O2" )
34 set( CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g" )
35 # -Wall: Enable all warnings.
36 # -Wextra: Enable some more warnings.
37 # -Werror: Have errors on warnings.
38 # -pedantic-errors: Even more errors.
39 # -Wno-unused-parameter: Don't error on unused parameters, required since we have function hooks
40 # that have unused parameters.
41 # -O#: Optimization level
42 else()
43 message( FATAL_ERROR "Your operating system isn't supported yet! CMake will now exit." )
44 endif()
45
46 # Add a check target for our makefile.
47 find_program( CPPCHECK_EXECUTABLE cppcheck
48 DOC "A tool for static C/C++ code analysis." )
49 if ( CPPCHECK_EXECUTABLE )
50 add_custom_target( "check"
51 COMMAND "${CPPCHECK_EXECUTABLE}" "--enable=all" "*"
52 WORKING_DIRECTORY src VERBATIM )
53 endif()
54
55 # Add our manpage generator if possible. Not needed as we include the man pages by default.
56 find_program( RONN_EXECUTABLE ronn
57 DOC "A tool for generating our manpages." )
58 find_program( GZIP_EXECUTABLE gzip
59 DOC "A tool for generating our manpages." )
60 if ( RONN_EXECUTABLE AND GZIP_EXECUTABLE )
61 add_custom_target( "man"
62 COMMAND "${RONN_EXECUTABLE}" "-r" "maim.1.ronn"
63 COMMAND "${GZIP_EXECUTABLE}" "-k" "maim.1"
64 WORKING_DIRECTORY man-src VERBATIM )
65 endif()
66
67 # Here we generate some of our code if we can. I package it pre-generated
68 # so nobody has to go find and install gengetopt if they don't want to.
69 find_program( GENGETOPT_EXECUTABLE gengetopt
70 DOC "A tool to generate code to grab command line options." )
71 if ( GENGETOPT_EXECUTABLE )
72 message( "-- Regenerating cmdline.in" )
73 execute_process( COMMAND "${GENGETOPT_EXECUTABLE}" "--input=options.ggo"
74 WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/src" )
75 file( RENAME "${CMAKE_SOURCE_DIR}/src/cmdline.h" "${CMAKE_SOURCE_DIR}/src/cmdline.in" )
76 else()
77 message( "Warning: Command gengetopt not found! Won't regenerate command line code. (If you're just compiling this doesn't matter.)" )
78 endif()
79
80 # By default our src/options.ggo has our cmake versions variables for
81 # the 'version ""' line. We replace them here.
82 # The ${CMAKE_SOURCE_DIR} is there to fix problems with OpenBSD's out-of-source build black magic.
83 configure_file( "src/cmdline.in" "${CMAKE_SOURCE_DIR}/src/cmdline.h" )
84
85 # This allows for "make README.md" to be ran to update the README's help
86 # section automatically. We don't add it to ALL because running arbitrary
87 # scripts is unsafe and I don't know if systems will actually have it
88 # be executbable.
89 add_custom_target( README.md "./generateReadme.sh" DEPENDS "maim" )
22 set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/bin/")
9023
9124 # Sources
9225 set( source
93 src/cmdline.c
94 src/im.cpp
9526 src/x.cpp
27 src/image.cpp
9628 src/main.cpp )
9729
98 # Obtain library paths and make sure they exist.
99 set( CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" "${CMAKE_SOURCE_DIR}/cmakemodules" )
100 find_package( Imlib2 REQUIRED )
101 find_package( X11 REQUIRED )
102 find_package( XRandr REQUIRED )
103 find_package( XFixes REQUIRED )
104
105 set( CMAKE_CXX_FLAGS
106 "${CMAKE_CXX_FLAGS} ${CMAKE_IMLIB2_CXX_FLAGS}" )
107
108 # Includes
109 include_directories( "${IMLIB2_INCLUDE_DIR}"
110 "${XRANDR_INCLUDE_DIR}"
111 "${X11_INCLUDE_DIR}"
112 "${XFIXES_INCLUDE_DIR}" )
30 set( BIN_TARGET "${PROJECT_NAME}" )
11331
11432 # Executable
11533 add_executable( "${BIN_TARGET}" ${source} )
11634
35 # Obtain library paths and make sure they exist.
36 set( CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" "${CMAKE_SOURCE_DIR}/modules" )
37 find_package( PNG REQUIRED )
38 find_package( JPEG REQUIRED )
39 find_package( XRandr REQUIRED )
40 find_package( XRender REQUIRED )
41 find_package( XFixes REQUIRED )
42 find_package( XComposite REQUIRED )
43 find_package( X11 REQUIRED )
44 find_package( SLOP REQUIRED )
45 find_package( Threads REQUIRED )
46
47 set_property(TARGET ${BIN_TARGET} PROPERTY CXX_STANDARD_REQUIRED ON)
48 set_property(TARGET ${BIN_TARGET} PROPERTY CXX_STANDARD 11)
49
50 # Includes
51 include_directories( ${XRANDR_INCLUDE_DIR}
52 ${X11_INCLUDE_DIR}
53 ${SLOP_INCLUDE_DIR}
54 ${XFIXES_INCLUDE_DIR}
55 ${XCOMPOSITE_INCLUDE_DIR}
56 ${JPEG_INCLUDE_DIR}
57 ${XRANDR_INCLUDE_DIR}
58 ${XRENDER_INCLUDE_DIR}
59 ${PNG_INCLUDE_DIRS} )
60
11761 # Libraries
118 target_link_libraries( "${BIN_TARGET}"
119 ${IMLIB2_LIBRARIES}
62 target_link_libraries( ${BIN_TARGET}
63 ${CMAKE_THREAD_LIBS_INIT}
12064 ${X11_LIBRARIES}
121 "${XRANDR_LIBRARY}"
122 "${XFIXES_LIBRARY}" )
65 ${PNG_LIBRARIES}
66 ${XFIXES_LIBRARY}
67 ${XCOMPOSITE_LIBRARY}
68 ${XRANDR_LIBRARY}
69 ${JPEG_LIBRARIES}
70 ${XRENDER_LIBRARY}
71 ${SLOP_LIBRARIES} )
12372
124 install( TARGETS "${BIN_TARGET}"
125 DESTINATION "${CMAKE_INSTALL_PREFIX}/bin" )
73 if( CMAKE<3.7 )
74 message( WARNING "CMake version is below 3.7, CMake version >= 3.7 is required for unicode support." )
75 else()
76 find_package(ICU COMPONENTS uc)
77 set( MAIM_UNICODE TRUE CACHE BOOL "To enable or disable unicode support." )
78 if ( MAIM_UNICODE AND ICU_FOUND )
79 # ICU is required for old nvidia drivers to work for whatever reason.
80 add_definitions(-DCXXOPTS_USE_UNICODE)
81 include_directories( ${ICU_INCLUDE_DIR} )
82 target_link_libraries( ${BIN_TARGET} ${ICU_UC_LIBRARIES} )
83 endif()
84 endif()
12685
127 install( FILES "${CMAKE_SOURCE_DIR}/${MANTARGET}"
128 DESTINATION "${CMAKE_INSTALL_MANDIR}/man1"
129 COMPONENT doc )
86 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g")
87
88 install( TARGETS ${BIN_TARGET} DESTINATION "${CMAKE_INSTALL_PREFIX}/bin" )
89 install( FILES "${CMAKE_SOURCE_DIR}/${MANTARGET}" DESTINATION "${CMAKE_INSTALL_MANDIR}/man1" COMPONENT doc )
00 # maim
1
2 maim (Make Image) is a utility that takes screenshots of your desktop using imlib2. It's meant to overcome shortcomings of scrot and performs better in several ways.
1 maim (Make Image) is a utility that takes screenshots of your desktop. It's meant to overcome shortcomings of scrot and performs better in several ways.
32
43 ## Features
5 * Allows you to take a screenshot of your desktop and save it in any format.
6 * Allows you to take a screenshot of a predetermined region or window of your desktop.
7 * If slop (https://github.com/naelstrof/slop) is installed, it can be used for selecting a region to screenshot.
4 * Takes screenshots of your desktop, and saves it in png or jpg format.
5 * Takes screenshots predetermined regions or windows, useful for automation.
6 * Allows a users to select a region, or window, before taking a screenshot on the fly.
87
9 ![slopgood](http://farmpolice.com/content/images/2014-10-14-12:14:51.png)
10 * Allows you to blend the system cursor to screenshots. (Why don't any other commandline screenshooters do this?)
8 ![screenshot with selection](http://i.imgur.com/ILZKJCT.png)
9 * Blends the system cursor to the screenshot.
10 ![screenshot with cursor](http://i.imgur.com/PD1bgBg.png)
11 * Masks off-screen pixels to be transparent or black.
1112
12 ![screenshot with cursor](http://farmpolice.com/content/images/wow.png)
13 ![screenshot with masked pixels](http://i.imgur.com/kMkcHlZ.png)
14 * Maim cleanly pipes screenshots directly to standard output (unless otherwise specified). Allowing for command chaining.
15 * Maim supports anything slop does, even selection [shaders](https://github.com/naelstrof/slop#shaders)!
1316
14 * Allows you to mask off-screen pixels to be black and transparent in screenshots. (Great for people who use an uneven multi-monitor setup!)
17 ![slop animation](http://i.giphy.com/kfBLafeJfLs2Y.gif)
1518
16 ![screenshot mask comparison](http://farmpolice.com/content/images/mask_compare2.png)
1719
18 ## Why use maim over import or scrot?
19 * Compared to scrot
20 - maim has no --exec or naming features. This is because maim follows the unix philosophy of "do one thing and do it well". These features are things that should be handled by the shell.
21 - scrot has no way to screenshot a predefined region. maim comes equipped with --geometry features that allow for specified region capture.
22 - With [slop](https://github.com/naelstrof/slop) installed, maim's --select option is far superior to scrot's -s option in many ways. See [slop](https://github.com/naelstrof/slop) for more details.
23 - maim will never error with `giblib error: couldn't grab keyboard:Resource temporarily unavailable` as it never grabs the keyboard. (slop does, but it has proper error handling that keeps it from crashing.)
24 * Compared to ImageMagick's import
25 - import doesn't play nicely with compositors; making effects like transparent windows not render properly in the screenshot. maim, like scrot, uses imlib2 which isn't inflicted with this problem.
26 * Compared to either
27 - maim can actually take screenshots with your cursor included in them! It does this using the XFixes extension. To my knowledge, no other commandline screenshot utility does this.
28 - For those of you with multiple monitors, maim is aware of which pixels are visible or not and will make off-screen pixels that are in screenshots black and transparent. Import and scrot both mindlessly include off-screen pixel data in their screenshots which is very often just garbage.
20 ## Installation
2921
30 ## Examples
31 I'm including this section because some people don't see how powerful and flexible their shell can be with simple tools like maim. Remember you can always bind keys to shell commands!
32 The following can be executed in any bash-like shells:
33
34 * Set the screenshot's name to the current time and date:
35 ```bash
36 $ maim ~/Pictures/$(date +%F-%T).png
37 ```
38
39 * Take a screenshot of the active window: (Requires xdotool.)
40 ```bash
41 $ maim -i $(xdotool getactivewindow)
42 ```
43
44 * Custom transparent red selection with 10 pixel padding: (Requires [slop](https://github.com/naelstrof/slop).)
45 ```bash
46 $ maim -s -c 1,0,0,0.6 -p 10
47 ```
48 ![Image of maim selecting a window](http://farmpolice.com/content/images/window_selection.png)
49
50 * Automatically upload selected region to Imgur: (Requires [Bart's Bash Script Imgur Uploader](http://imgur.com/tools/imgurbash.sh), xclip is optional.)
51 ```bash
52 $ maim -s /tmp/screenshot.png; imgurbash.sh /tmp/screenshot.png
53 $ # If xclip is installed, your clipboard should have the online screenshot's URL in it!
54 ```
55
56 In review, maim does one thing and does it well: it takes a screenshot of what you want. :) What you want is up to you, your programming skills, and your imagination.
57
58 ## How to install
59
60 ### Install using your Package Manager (preferred)
61
22 ### Install using your Package Manager (Preferred)
6223 * [Arch Linux: community/maim](https://www.archlinux.org/packages/community/x86_64/maim/)
24 * [Debian: maim](https://tracker.debian.org/pkg/maim)
6325 * [Void Linux: maim](https://github.com/voidlinux/void-packages/blob/24ac22af44018e2598047e5ef7fd3522efa79db5/srcpkgs/maim/template)
6426 * [FreeBSD: graphics/maim](http://www.freshports.org/graphics/maim/)
6527 * [OpenBSD: graphics/maim](http://openports.se/graphics/maim)
28 * [CRUX: 6c37/maim](https://github.com/6c37/crux-ports/tree/3.2/maim)
29 * [Gentoo: media-gfx/maim](https://packages.gentoo.org/packages/media-gfx/maim)
30 * [NixOS: maim](https://github.com/NixOS/nixpkgs/blob/master/pkgs/tools/graphics/maim/default.nix)
31 * [GNU Guix: maim](https://www.gnu.org/software/guix/packages/#maim)
6632 * Please make a package for maim on your favorite system, and make a pull request to add it to this list.
6733
68
69 ### Install using CMake (Requires CMake)
70
71 Note: Dependencies should be installed first: Imlib2, libXrandr, and libXfixes.
72
34 ### Install using CMake (Requires CMake, git, libXrander, libXfixes, libGLM)
7335 ```bash
36 git clone https://github.com/naelstrof/slop.git
37 cd slop
38 cmake -DCMAKE_INSTALL_PREFIX="/usr" ./
39 make && sudo make install
40 cd ..
7441 git clone https://github.com/naelstrof/maim.git
7542 cd maim
76 cmake ./
43 cmake -DCMAKE_INSTALL_PREFIX="/usr" ./
7744 make && sudo make install
7845 ```
7946
80 Make sure to check out and install [slop](https://github.com/naelstrof/slop) too if you want selection capabilities!
47 ## Examples
48 Maim allows for a lot of unique and interesting functionalities. Here's an example of a few interactions.
8149
82 help
83 ----
84 Join us on irc at freenode in *#maim*.
85 ```text
86 maim v3.3.41
50 * This command will allow you to select an area on your screen, then copy the selection to your clipboard. This can be used to easily post images in mumble, discord, gimp-- or any other image supporting application.
51 ```bash
52 $ maim -s | xclip -selection clipboard -t image/png
53 ```
8754
88 Copyright (C) 2014 Dalton Nell, Maim Contributors
89 (https://github.com/naelstrof/maim/graphs/contributors)
55 * This messy command forces a user to select a window to screenshot, then applies a shadow effect using *imagemagick*, then saves it to shadow.png. It looks really nice on windows that support an alpha channel.
56 ```bash
57 $ maim -st 9999999 | convert - \( +clone -background black -shadow 80x3+5+5 \) +swap -background none -layers merge +repage shadow.png
58 ```
9059
91 Takes screenshots.
60 * This command is a particular favorite of mine, invented by a friend. It simply prints the RGB values of the selected pixel. A basic color picker that has the additional ability to average out the pixel values of an area. If used cleverly with the geometry and window flag, the return color might warn you of a found counter-strike match...
61 ```bash
62 $ maim -st 0 | convert - -resize 1x1\! -format '%[pixel:p{0,0}]' info:-
63 ```
9264
93 Usage: maim [options] [file]
65 * This is a basic, but useful command that simply screenshots the current active window.
66 ```bash
67 $ maim -i $(xdotool getactivewindow) ~/mypicture.jpg
68 ```
9469
95 maim (Make Image) is a utility that takes screenshots of your desktop using
96 imlib2. It's meant to overcome shortcomings of scrot and performs better than
97 scrot in several ways.
70 * This is another basic command, but I find it necessary to describe the usefulness of date. This particular command creates a full screenshot, and names it as the number of seconds that passed since 1970. Guaranteed unique, already sorted, and easily read.
71 ```bash
72 $ maim ~/Pictures/$(date +%s).png
73 ```
9874
99 --help Print help and exit
100 -V, --version Print version and exit
101 Options
102 --xdisplay=hostname:number.screen_number
103 Sets the x display.
104 -s, --select Enables user region selection. Requires slop to
105 be installed. (default=off)
106 -x, --x=INT Sets the x coordinate for taking an image
107 -y, --y=INT Sets the y coordinate for taking an image
108 -w, --w=INT Sets the width for taking an image
109 -h, --h=INT Sets the height for taking an image
110 -g, --geometry=WxH+X+Y Set the region to capture
111 -d, --delay=FLOAT Set the amount of time to wait before taking an
112 image. (default=`0.0')
113 -i, --windowid=INT Set the window to capture. Defaults to the root
114 window id.
115 --localize Localizes given geometry to the given window.
116 So "maim -i $ID -g 100x100+0+0 --localize"
117 would screenshot the top-left 100x100 pixels
118 of the given window, rather than the top-left
119 100x100 pixels of the root window.
120 (default=off)
121 --hidecursor Prevents the system cursor from showing up in
122 screenshots. (default=off)
123 -m, --mask=STRING Masks off-screen pixels so they don't show up
124 in screenshots. (possible values="auto",
125 "off", "on" default=`auto')
75 * This one overlays a still of your desktop, then allows you to crop it. Doesn't play well with multiple monitors, but I'm sure if it did it wouldn't look this pretty and simple.
76 ```bash
77 $ maim | feh - -x & maim -s cropped.png
78 ```
12679
127 Slop Options
128 --nokeyboard Disables the ability to cancel selections with
129 the keyboard. (default=off)
130 -b, --bordersize=INT Set the selection rectangle's thickness. Does
131 nothing when --highlight is enabled.
132 (default=`5')
133 -p, --padding=INT Set the padding size of the selection. Can be
134 negative. (default=`0')
135 -t, --tolerance=INT How far in pixels the mouse can move after
136 clicking and still be detected as a normal
137 click instead of a click and drag. Setting
138 this to 0 will disable window selections.
139 (default=`2')
140 --gracetime=FLOAT Set the amount of time before slop will check
141 for keyboard cancellations in seconds.
142 (default=`0.4')
143 -c, --color=FLOAT,FLOAT,FLOAT,FLOAT
144 Set the selection rectangle's color. Supports
145 RGB or RGBA values.
146 (default=`0.5,0.5,0.5,1')
147 -n, --nodecorations Attempt to select child windows in order to
148 avoid window decorations. (default=off)
149 --min=INT Set the minimum output of width or height
150 values. This is useful to avoid outputting 0.
151 Setting min and max to the same value
152 disables drag selections. (default=`0')
153 --max=INT Set the maximum output of width or height
154 values. Setting min and max to the same value
155 disables drag selections. (default=`0')
156 -l, --highlight Instead of outlining selections, slop
157 highlights it. This is only useful when
158 --color is set to a transparent color.
159 (default=off)
160
161 Examples
162 $ # Screenshot the active window
163 $ maim -i $(xdotool getactivewindow)
164
165 $ # Prompt a transparent red selection to screenshot.
166 $ maim -s -c 1,0,0,0.6
167
168 $ # Save a dated screenshot.
169 $ maim ~/$(date +%F-%T).png
80 * Finally with the [help your friendly neighborhood scripter](https://github.com/tremby/imgur.sh), pictures can automatically be uploaded and their URLs copied to the clipboard with this basic command.
81 ```bash
82 $ maim -s /tmp/screenshot.png; imgurbash.sh /tmp/screenshot.png
17083 ```
+0
-91
cmakemodules/FindImlib2.cmake less more
0 #
1 # This module finds if IMLIB2 is available and determines where the
2 # include files and libraries are.
3 # On Unix/Linux it relies on the output of imlib2-config.
4 # This code sets the following variables:
5 #
6 #
7 #
8 # IMLIB2_FOUND = system has IMLIB2 lib
9 #
10 # IMLIB2_LIBRARIES = full path to the libraries
11 # on Unix/Linux with additional linker flags from "imlib2-config --libs"
12 #
13 # CMAKE_IMLIB2_CXX_FLAGS = Unix compiler flags for IMLIB2, essentially "`imlib2-config --cxxflags`"
14 #
15 # IMLIB2_INCLUDE_DIR = where to find headers
16 #
17 # IMLIB2_LINK_DIRECTORIES = link directories, useful for rpath on Unix
18 #
19 #
20 # author Jan Woetzel and Jan-Friso Evers
21 # www.mip.informatik.uni-kiel.de/~jw
22
23 IF(WIN32)
24 MESSAGE("FindIMLIB2.cmake: IMLIB2 not (yet) supported on WIN32")
25 SET(IMLIB2_FOUND OFF )
26 ELSE(WIN32)
27 IF(UNIX)
28 SET(IMLIB2_CONFIG_PREFER_PATH "$ENV{IMLIB2_HOME}/bin" CACHE STRING "preferred path to imlib2")
29 FIND_PROGRAM(IMLIB2_CONFIG imlib2-config
30 ${IMLIB2_CONFIG_PREFER_PATH}
31 /usr/bin/
32 /opt/gnome/bin/)
33
34 IF (IMLIB2_CONFIG)
35 # OK, found imlib2-config.
36 # set CXXFLAGS to be fed into CXX_FLAGS by the user:
37 SET(IMLIB2_CXX_FLAGS "`${IMLIB2_CONFIG} --cflags`")
38
39 # set INCLUDE_DIRS to prefix+include
40 EXEC_PROGRAM(${IMLIB2_CONFIG}
41 ARGS --prefix
42 OUTPUT_VARIABLE IMLIB2_PREFIX)
43 SET(IMLIB2_INCLUDE_DIR ${IMLIB2_PREFIX}/include CACHE STRING INTERNAL)
44
45 # extract link dirs for rpath
46 EXEC_PROGRAM(${IMLIB2_CONFIG}
47 ARGS --libs
48 OUTPUT_VARIABLE IMLIB2_CONFIG_LIBS)
49
50 # set link libraries and link flags
51 #SET(IMLIB2_LIBRARIES "`${IMLIB2_CONFIG} --libs`")
52 SET(IMLIB2_LIBRARIES ${IMLIB2_CONFIG_LIBS})
53
54 # split off the link dirs (for rpath)
55 # use regular expression to match wildcard equivalent "-L*<endchar>"
56 # with <endchar> is a space or a semicolon
57 STRING(REGEX MATCHALL "[-][L]([^ ;])+"
58 IMLIB2_LINK_DIRECTORIES_WITH_PREFIX
59 "${IMLIB2_CONFIG_LIBS}")
60 #MESSAGE("DBG IMLIB2_LINK_DIRECTORIES_WITH_PREFIX=${IMLIB2_LINK_DIRECTORIES_WITH_PREFIX}")
61
62 # remove prefix -L because we need the pure directory for LINK_DIRECTORIES
63 # replace -L by ; because the separator seems to be lost otherwise (bug or feature?)
64 IF (IMLIB2_LINK_DIRECTORIES_WITH_PREFIX)
65 STRING(REGEX REPLACE "[-][L]" ";" IMLIB2_LINK_DIRECTORIES ${IMLIB2_LINK_DIRECTORIES_WITH_PREFIX} )
66 #MESSAGE("DBG IMLIB2_LINK_DIRECTORIES=${IMLIB2_LINK_DIRECTORIES}")
67 ENDIF (IMLIB2_LINK_DIRECTORIES_WITH_PREFIX)
68
69 # replace space separated string by semicolon separated vector to make
70 # it work with LINK_DIRECTORIES
71 SEPARATE_ARGUMENTS(IMLIB2_LINK_DIRECTORIES)
72
73 MARK_AS_ADVANCED(IMLIB2_CXX_FLAGS
74 IMLIB2_INCLUDE_DIR
75 IMLIB2_LIBRARIES
76 IMLIB2_LINK_DIRECTORIES
77 IMLIB2_CONFIG_PREFER_PATH
78 IMLIB2_CONFIG)
79
80 ELSE(IMLIB2_CONFIG)
81 MESSAGE( "FindIMLIB2.cmake: imlib2-config not found. Please set it manually. IMLIB2_CONFIG=${IMLIB2_CONFIG}")
82 ENDIF(IMLIB2_CONFIG)
83 ENDIF(UNIX)
84 ENDIF(WIN32)
85
86 IF(IMLIB2_LIBRARIES)
87 IF(IMLIB2_INCLUDE_DIR OR IMLIB2_CXX_FLAGS)
88 SET(IMLIB2_FOUND 1)
89 ENDIF(IMLIB2_INCLUDE_DIR OR IMLIB2_CXX_FLAGS)
90 ENDIF(IMLIB2_LIBRARIES)
+0
-26
cmakemodules/FindXFixes.cmake less more
0 # - Find XFixes
1 # Find the XFixes libraries
2 #
3 # This module defines the following variables:
4 # XFIXES_FOUND - 1 if XFIXES_INCLUDE_DIR & XFIXES_LIBRARY are found, 0 otherwise
5 # XFIXES_INCLUDE_DIR - where to find Xlib.h, etc.
6 # XFIXES_LIBRARY - the X11 library
7 #
8
9 find_path( XFIXES_INCLUDE_DIR
10 NAMES X11/extensions/Xfixes.h
11 PATH_SUFFIXES X11/extensions
12 DOC "The XFixes include directory" )
13
14 find_library( XFIXES_LIBRARY
15 NAMES Xfixes
16 PATHS /usr/lib /lib
17 DOC "The XFixes library" )
18
19 if( XFIXES_INCLUDE_DIR AND XFIXES_LIBRARY )
20 set( XFIXES_FOUND 1 )
21 else()
22 set( XFIXES_FOUND 0 )
23 endif()
24
25 mark_as_advanced( XFIXES_INCLUDE_DIR XFIXES_LIBRARY )
+0
-26
cmakemodules/FindXRandr.cmake less more
0 # - Find XRandr
1 # Find the XRandr libraries
2 #
3 # This module defines the following variables:
4 # XRANDR_FOUND - 1 if XRANDR_INCLUDE_DIR & XRANDR_LIBRARY are found, 0 otherwise
5 # XRANDR_INCLUDE_DIR - where to find Xlib.h, etc.
6 # XRANDR_LIBRARY - the X11 library
7 #
8
9 find_path( XRANDR_INCLUDE_DIR
10 NAMES X11/extensions/Xrandr.h
11 PATH_SUFFIXES X11/extensions
12 DOC "The XRandr include directory" )
13
14 find_library( XRANDR_LIBRARY
15 NAMES Xrandr
16 PATHS /usr/lib /lib
17 DOC "The XRandr library" )
18
19 if( XRANDR_INCLUDE_DIR AND XRANDR_LIBRARY )
20 set( XRANDR_FOUND 1 )
21 else()
22 set( XRANDR_FOUND 0 )
23 endif()
24
25 mark_as_advanced( XRANDR_INCLUDE_DIR XRANDR_LIBRARY )
+0
-13
generateReadme.sh less more
0 #!/bin/sh
1 # generateReadme.sh: Regenerates the help section of the README.md using output from ./maim --help.
2
3 # Remove help section
4 sed -i '/^help$/,/^```$/d' README.md
5
6 # Add the help section again.
7 echo 'help' >> README.md
8 echo '----' >> README.md
9 echo 'Join us on irc at freenode in *#maim*.' >> README.md
10 echo '```text' >> README.md
11 echo "$(./maim --help)" >> README.md
12 echo '```' >> README.md
0 .\" Manpage for maim.
1 .\" Contact naelstrof@gmail.com to correct errors or typos.
2 .TH maim 1 2017-03-21 Linux "maim man page"
3 .SH NAME
4 maim \- make image
5 .SH SYNOPSIS
6 maim [OPTIONS] [FILEPATH]
7 .SH DESCRIPTION
8 maim (make image) is an utility that takes a screenshot of your desktop, and encodes a png or jpg image of it. By default it outputs the encoded image data directly to standard output.
9 .SH OPTIONS
10 .TP
11 .BR \-h ", " \-\-help
12 Print help and exit.
13 .TP
14 .BR \-v ", " \-\-version
15 Print version and exit.
16 .TP
17 .BR \-x ", " \-\-xdisplay=\fIhostname:number.screen_number\fR
18 Sets the xdisplay to use.
19 .TP
20 .BR \-f ", " \-\-format=\fISTRING\fR
21 Sets the desired output format, by default maim will attempt to determine the desired output format automatically from the output file. If that fails it defaults to a lossless png format. Currently only supports `png` or `jpg`.
22 .TP
23 .BR \-i ", " \-\-window=\fIWINDOW\fR
24 Sets the desired window to capture, defaults to the root window. Allows for an integer, hex, or `root` for input.
25 .TP
26 .BR \-g ", " \-\-geometry=\fIGEOMETRY\fR
27 Sets the region to capture, uses local coordinates from the given window. So -g10x30-5+0 would represent the rectangle wxh+x+y where w=10, h=30, x=-5, and y=0. x and y are the upper left location of this rectangle.
28 .TP
29 .BR \-w ", " \-\-parent=\fIWINDOW\fR
30 By default, maim assumes the --geometry values are in respect to the provided --window (or root if not provided). This parameter overrides this behavior by making the geometry be in respect to whatever window you provide to --parent. Allows for an integer, hex, or `root` for input.
31 .TP
32 .BR \-d ", " \-\-delay=\fIFLOAT\fR
33 Sets the time in seconds to wait before taking a screenshot. Prints a simple message to show how many seconds are left before a screenshot is taken. See \-\-quiet for muting this message.
34 .TP
35 .BR \-u ", " \-\-hidecursor
36 By default maim super-imposes the cursor onto the image, you can disable that behavior with this flag.
37 .TP
38 .BR \-m ", " \-\-quality
39 An integer from 1 to 10 that determines the compression quality. 1 is the highest (and lossiest) compression available for the provided format. For example a setting of `1` with png (a lossless format) would increase filesize and decrease decoding time. While a setting of `1` on a jpeg would create a pixel mush.
40 .TP
41 .BR \-s ", " \-\-select
42 Enables an interactive selection mode where you may select the desired region or window before a screenshot is captured. Uses the settings below to determine the visuals and settings of slop.
43 .SH SLOP OPTIONS
44 .TP
45 .BR \-b ", " \-\-bordersize=\fIFLOAT\fR
46 Sets the selection rectangle's thickness.
47 .TP
48 .BR \-p ", " \-\-padding=\fIFLOAT\fR
49 Sets the padding size for the selection, this can be negative.
50 .TP
51 .BR \-t ", " \-\-tolerance=\fIFLOAT\fR
52 How far in pixels the mouse can move after clicking, and still be detected as a normal click instead of a click-and-drag. Setting this to 0 will disable window selections. Alternatively setting it to 9999999 would force a window selection.
53 .TP
54 .BR \-c ", " \-\-color=\fIFLOAT,FLOAT,FLOAT,FLOAT\fR
55 Sets the selection rectangle's color. Supports RGB or RGBA input. Depending on the system's window manager/OpenGL support, the opacity may be ignored.
56 .TP
57 .BR \-r ", " \-\-shader=\fISTRING\fR
58 This sets the vertex shader, and fragment shader combo to use when drawing the final framebuffer to the screen. This obviously only works when OpenGL is enabled. The shaders are loaded from ~/.config/maim. See https://github.com/naelstrof/slop for more information on how to create your own shaders.
59 .TP
60 .BR \-n ", " \-\-nodecorations=\fIINT\fR
61 Sets the level of aggressiveness when trying to remove window decroations. `0' is off, `1' will try lightly to remove decorations, and `2' will recursively descend into the root tree until it gets the deepest available visible child under the mouse. Defaults to `0'.
62 .TP
63 .BR \-l ", " \-\-highlight
64 Instead of outlining a selection, maim will highlight it instead. This is particularly useful if the color is set to an opacity lower than 1.
65 .TP
66 .BR \-q ", " \-\-quiet
67 Disable any unnecessary cerr output. Any warnings or info simply won't print.
68 .TP
69 .BR \-k ", " \-\-nokeyboard
70 Disables the ability to cancel selections with the keyboard.
71 .TP
72 .BR \-o ", " \-\-noopengl
73 Disables graphics hardware acceleration.
74 .SH EXAMPLES
75 Screenshot the active window and save it to the clipboard for quick pasting.
76 .PP
77 .nf
78 .RS
79 maim -i $(xdotool getactivewindow) | xclip -selection clipboard -t image/png
80 .RE
81 .fi
82 .PP
83 Save a desktop screenshot with a unique ordered timestamp in the Pictures folder.
84 .PP
85 .nf
86 .RS
87 maim ~/Pictures/$(date +%s).png
88 .RE
89 .fi
90 .PP
91 Prompt for a region to screenshot. Add a fancy shadow to it, then save it to shadow.png.
92 .PP
93 .nf
94 .RS
95 maim -s | convert - \\( +clone -background black -shadow 80x3+5+5 \\) +swap -background none -layers merge +repage shadow.png
96 .RE
97 .fi
98 .PP
99 .SH SEE ALSO
100 .BR slop(1)
101 .SH BUGS
102 No known bugs.
103 .SH AUTHOR
104 Dalton Nell (naelstrof@gmail.com)
Binary diff not shown
+0
-157
man-src/maim.1 less more
0 .\" generated with Ronn/v0.7.3
1 .\" http://github.com/rtomayko/ronn/tree/0.7.3
2 .
3 .TH "MAIM" "1" "December 2014" "" ""
4 .
5 .SH "NAME"
6 \fBmaim\fR \- Takes screenshots
7 .
8 .SH "SYNOPSIS"
9 \fBmaim\fR [options] [file]
10 .
11 .SH "DESCRIPTION"
12 maim (MAke IMage) is a utility that takes screenshots of your desktop using imlib2\. It\'s meant to overcome shortcomings of scrot and performs better than scrot in several ways\.
13 .
14 .SH "OPTIONS"
15 .
16 .TP
17 \fB\-\-help\fR
18 Print help and exit
19 .
20 .TP
21 \fB\-V\fR, \fB\-\-version\fR
22 Print version and exit
23 .
24 .SS "Options"
25 .
26 .TP
27 \fB\-\-xdisplay=hostname:number\.screen_number\fR
28 Sets the x display\.
29 .
30 .TP
31 \fB\-s\fR, \fB\-\-select\fR
32 Enables user region selection\. Requires slop to be installed\. (default=off)
33 .
34 .TP
35 \fB\-x\fR, \fB\-\-x=INT\fR
36 Sets the x coordinate for taking an image
37 .
38 .TP
39 \fB\-y\fR, \fB\-\-y=INT\fR
40 Sets the y coordinate for taking an image
41 .
42 .TP
43 \fB\-w\fR, \fB\-\-w=INT\fR
44 Sets the width for taking an image
45 .
46 .TP
47 \fB\-h\fR, \fB\-\-h=INT\fR
48 Sets the height for taking an image
49 .
50 .TP
51 \fB\-g\fR, \fB\-\-geometry=WxH+X+Y\fR
52 Set the region to capture
53 .
54 .TP
55 \fB\-d\fR, \fB\-\-delay=FLOAT\fR
56 Set the amount of time to wait before taking an image\. (default=`0\.0\')
57 .
58 .TP
59 \fB\-i\fR, \fB\-\-windowid=INT\fR
60 Set the window to capture\. Defaults to the root window id\.
61 .
62 .TP
63 \fB\-\-localize\fR
64 Localizes given geometry to the given window\. So \fBmaim \-i $ID \-g 100x100+0+0 \-\-localize\fR would screenshot the top\-left 100x100 pixels of the given window, rather than the top\-left 100x100 pixels of the root window\. (default=off)
65 .
66 .TP
67 \fB\-\-hidecursor\fR
68 Prevents the system cursor from showing up in screenshots\. (default=off)
69 .
70 .TP
71 \fB\-m\fR, \fB\-\-mask=STRING\fR
72 Masks off\-screen pixels so they don\'t show up in screenshots\. (possible values="auto", "off", "on" default=`auto\')
73 .
74 .SS "SLOP OPTIONS"
75 .
76 .TP
77 \fB\-\-nokeyboard\fR
78 Disables the ability to cancel selections with the keyboard\. (default=off)
79 .
80 .TP
81 \fB\-b\fR, \fB\-\-bordersize=INT\fR
82 Set the selection rectangle\'s thickness\. Does nothing when \fB\-\-highlight\fR is enabled\. (default=`5\')
83 .
84 .TP
85 \fB\-p\fR, \fB\-\-padding=INT\fR
86 Set the padding size of the selection\. Can be negative\. (default=`0\')
87 .
88 .TP
89 \fB\-t\fR, \fB\-\-tolerance=INT\fR
90 How far in pixels the mouse can move after clicking and still be detected as a normal click instead of a click and drag\. Setting this to 0 will disable window selections\. (default=`2\')
91 .
92 .TP
93 \fB\-\-gracetime=FLOAT\fR
94 Set the amount of time before slop will check for keyboard cancellations in seconds\. (default=`0\.4\')
95 .
96 .TP
97 \fB\-c\fR, \fB\-\-color=FLOAT,FLOAT,FLOAT,FLOAT\fR
98 Set the selection rectangle\'s color\. Supports RGB or RGBA values\. (default=`0\.5,0\.5,0\.5,1\')
99 .
100 .TP
101 \fB\-n\fR, \fB\-\-nodecorations\fR
102 Attempt to select child windows in order to avoid window decorations\. (default=off)
103 .
104 .TP
105 \fB\-\-min=INT\fR
106 Set the minimum output of width or height values\. This is useful to avoid outputting 0\. Setting min and max to the same value disables drag selections\. (default=`0\')
107 .
108 .TP
109 \fB\-\-max=INT\fR
110 Set the maximum output of width or height values\. Setting min and max to the same value disables drag selections\. (default=`0\')
111 .
112 .TP
113 \fB\-l\fR, \fB\-\-highlight\fR
114 Instead of outlining selections, slop highlights it\. This is only useful when \fB\-\-color\fR is set to a transparent color\. (default=off)
115 .
116 .SH "EXAMPLES"
117 Screenshot the active window
118 .
119 .IP "" 4
120 .
121 .nf
122
123 $ maim \-i $(xdotool getactivewindow)
124 .
125 .fi
126 .
127 .IP "" 0
128 .
129 .P
130 Prompt a transparent red selection to screenshot\.
131 .
132 .IP "" 4
133 .
134 .nf
135
136 $ maim \-s \-c 1,0,0,0\.6
137 .
138 .fi
139 .
140 .IP "" 0
141 .
142 .P
143 Save a dated screenshot\.
144 .
145 .IP "" 4
146 .
147 .nf
148
149 $ maim ~/$(date +%F\-%T)\.png
150 .
151 .fi
152 .
153 .IP "" 0
154 .
155 .SH "COPYRIGHT"
156 Copyright (C) 2014 Dalton Nell \fB<naelstrof@gmail\.com>\fR, Maim Contributors \fB<http://github\.com/naelstrof/maim/graphs/contributors>\fR\.
man-src/maim.1.gz less more
Binary diff not shown
+0
-158
man-src/maim.1.html less more
0 <!DOCTYPE html>
1 <html>
2 <head>
3 <meta http-equiv='content-type' value='text/html;charset=utf8'>
4 <meta name='generator' value='Ronn/v0.7.3 (http://github.com/rtomayko/ronn/tree/0.7.3)'>
5 <title>maim(1) - Takes screenshots</title>
6 <style type='text/css' media='all'>
7 /* style: man */
8 body#manpage {margin:0}
9 .mp {max-width:100ex;padding:0 9ex 1ex 4ex}
10 .mp p,.mp pre,.mp ul,.mp ol,.mp dl {margin:0 0 20px 0}
11 .mp h2 {margin:10px 0 0 0}
12 .mp > p,.mp > pre,.mp > ul,.mp > ol,.mp > dl {margin-left:8ex}
13 .mp h3 {margin:0 0 0 4ex}
14 .mp dt {margin:0;clear:left}
15 .mp dt.flush {float:left;width:8ex}
16 .mp dd {margin:0 0 0 9ex}
17 .mp h1,.mp h2,.mp h3,.mp h4 {clear:left}
18 .mp pre {margin-bottom:20px}
19 .mp pre+h2,.mp pre+h3 {margin-top:22px}
20 .mp h2+pre,.mp h3+pre {margin-top:5px}
21 .mp img {display:block;margin:auto}
22 .mp h1.man-title {display:none}
23 .mp,.mp code,.mp pre,.mp tt,.mp kbd,.mp samp,.mp h3,.mp h4 {font-family:monospace;font-size:14px;line-height:1.42857142857143}
24 .mp h2 {font-size:16px;line-height:1.25}
25 .mp h1 {font-size:20px;line-height:2}
26 .mp {text-align:justify;background:#fff}
27 .mp,.mp code,.mp pre,.mp pre code,.mp tt,.mp kbd,.mp samp {color:#131211}
28 .mp h1,.mp h2,.mp h3,.mp h4 {color:#030201}
29 .mp u {text-decoration:underline}
30 .mp code,.mp strong,.mp b {font-weight:bold;color:#131211}
31 .mp em,.mp var {font-style:italic;color:#232221;text-decoration:none}
32 .mp a,.mp a:link,.mp a:hover,.mp a code,.mp a pre,.mp a tt,.mp a kbd,.mp a samp {color:#0000ff}
33 .mp b.man-ref {font-weight:normal;color:#434241}
34 .mp pre {padding:0 4ex}
35 .mp pre code {font-weight:normal;color:#434241}
36 .mp h2+pre,h3+pre {padding-left:0}
37 ol.man-decor,ol.man-decor li {margin:3px 0 10px 0;padding:0;float:left;width:33%;list-style-type:none;text-transform:uppercase;color:#999;letter-spacing:1px}
38 ol.man-decor {width:100%}
39 ol.man-decor li.tl {text-align:left}
40 ol.man-decor li.tc {text-align:center;letter-spacing:4px}
41 ol.man-decor li.tr {text-align:right;float:right}
42 </style>
43 </head>
44 <!--
45 The following styles are deprecated and will be removed at some point:
46 div#man, div#man ol.man, div#man ol.head, div#man ol.man.
47
48 The .man-page, .man-decor, .man-head, .man-foot, .man-title, and
49 .man-navigation should be used instead.
50 -->
51 <body id='manpage'>
52 <div class='mp' id='man'>
53
54 <div class='man-navigation' style='display:none'>
55 <a href="#NAME">NAME</a>
56 <a href="#SYNOPSIS">SYNOPSIS</a>
57 <a href="#DESCRIPTION">DESCRIPTION</a>
58 <a href="#OPTIONS">OPTIONS</a>
59 <a href="#EXAMPLES">EXAMPLES</a>
60 <a href="#COPYRIGHT">COPYRIGHT</a>
61 </div>
62
63 <ol class='man-decor man-head man head'>
64 <li class='tl'>maim(1)</li>
65 <li class='tc'></li>
66 <li class='tr'>maim(1)</li>
67 </ol>
68
69 <h2 id="NAME">NAME</h2>
70 <p class="man-name">
71 <code>maim</code> - <span class="man-whatis">Takes screenshots</span>
72 </p>
73
74 <h2 id="SYNOPSIS">SYNOPSIS</h2>
75
76 <p><code>maim</code> [options] [file]</p>
77
78 <h2 id="DESCRIPTION">DESCRIPTION</h2>
79
80 <p>maim (MAke IMage) is a utility that takes screenshots of your desktop using
81 imlib2. It's meant to overcome shortcomings of scrot and performs better than
82 scrot in several ways.</p>
83
84 <h2 id="OPTIONS">OPTIONS</h2>
85
86 <dl>
87 <dt class="flush"><code>--help</code></dt><dd><p>Print help and exit</p></dd>
88 <dt><code>-V</code>, <code>--version</code></dt><dd><p>Print version and exit</p></dd>
89 </dl>
90
91
92 <h3 id="Options">Options</h3>
93
94 <dl>
95 <dt><code>--xdisplay=hostname:number.screen_number</code></dt><dd><p>Sets the x display.</p></dd>
96 <dt><code>-s</code>, <code>--select</code></dt><dd><p>Enables user region selection. Requires slop to
97 be installed. (default=off)</p></dd>
98 <dt><code>-x</code>, <code>--x=INT</code></dt><dd><p>Sets the x coordinate for taking an image</p></dd>
99 <dt><code>-y</code>, <code>--y=INT</code></dt><dd><p>Sets the y coordinate for taking an image</p></dd>
100 <dt><code>-w</code>, <code>--w=INT</code></dt><dd><p>Sets the width for taking an image</p></dd>
101 <dt><code>-h</code>, <code>--h=INT</code></dt><dd><p>Sets the height for taking an image</p></dd>
102 <dt><code>-g</code>, <code>--geometry=WxH+X+Y</code></dt><dd><p>Set the region to capture</p></dd>
103 <dt><code>-d</code>, <code>--delay=FLOAT</code></dt><dd><p>Set the amount of time to wait before taking an image. (default=`0.0')</p></dd>
104 <dt><code>-i</code>, <code>--windowid=INT</code></dt><dd><p>Set the window to capture. Defaults to the root window id.</p></dd>
105 <dt><code>--localize</code></dt><dd><p>Localizes given geometry to the given window. So <code>maim -i $ID -g 100x100+0+0 --localize</code> would screenshot the top-left 100x100 pixels of the given window, rather than the top-left 100x100 pixels of the root window. (default=off)</p></dd>
106 <dt><code>--hidecursor</code></dt><dd><p>Prevents the system cursor from showing up in screenshots. (default=off)</p></dd>
107 <dt><code>-m</code>, <code>--mask=STRING</code></dt><dd><p>Masks off-screen pixels so they don't show up in screenshots. (possible values="auto", "off", "on" default=`auto')</p></dd>
108 </dl>
109
110
111 <h3 id="SLOP-OPTIONS">SLOP OPTIONS</h3>
112
113 <dl>
114 <dt><code>--nokeyboard</code></dt><dd><p>Disables the ability to cancel selections with the keyboard. (default=off)</p></dd>
115 <dt><code>-b</code>, <code>--bordersize=INT</code></dt><dd><p>Set the selection rectangle's thickness. Does nothing when <code>--highlight</code> is enabled. (default=`5')</p></dd>
116 <dt><code>-p</code>, <code>--padding=INT</code></dt><dd><p>Set the padding size of the selection. Can be negative. (default=`0')</p></dd>
117 <dt><code>-t</code>, <code>--tolerance=INT</code></dt><dd><p>How far in pixels the mouse can move after clicking and still be detected as a normal click instead of a click and drag. Setting this to 0 will disable window selections. (default=`2')</p></dd>
118 <dt><code>--gracetime=FLOAT</code></dt><dd><p>Set the amount of time before slop will check for keyboard cancellations in seconds. (default=`0.4')</p></dd>
119 <dt><code>-c</code>, <code>--color=FLOAT,FLOAT,FLOAT,FLOAT</code></dt><dd><p>Set the selection rectangle's color. Supports RGB or RGBA values. (default=`0.5,0.5,0.5,1')</p></dd>
120 <dt><code>-n</code>, <code>--nodecorations</code></dt><dd><p>Attempt to select child windows in order to avoid window decorations. (default=off)</p></dd>
121 <dt><code>--min=INT</code></dt><dd><p>Set the minimum output of width or height values. This is useful to avoid outputting 0. Setting min and max to the same value disables drag selections. (default=`0')</p></dd>
122 <dt><code>--max=INT</code></dt><dd><p>Set the maximum output of width or height values. Setting min and max to the same value disables drag selections. (default=`0')</p></dd>
123 <dt><code>-l</code>, <code>--highlight</code></dt><dd><p>Instead of outlining selections, slop highlights it. This is only useful when <code>--color</code> is set to a transparent color. (default=off)</p></dd>
124 </dl>
125
126
127 <h2 id="EXAMPLES">EXAMPLES</h2>
128
129 <p> Screenshot the active window</p>
130
131 <pre><code>$ maim -i $(xdotool getactivewindow)
132 </code></pre>
133
134 <p> Prompt a transparent red selection to screenshot.</p>
135
136 <pre><code>$ maim -s -c 1,0,0,0.6
137 </code></pre>
138
139 <p> Save a dated screenshot.</p>
140
141 <pre><code>$ maim ~/$(date +%F-%T).png
142 </code></pre>
143
144 <h2 id="COPYRIGHT">COPYRIGHT</h2>
145
146 <p>Maim is Copyright (C) 2014 Dalton Nell <code>&lt;naelstrof@gmail.com&gt;</code> and Maim Contributors <code>&lt;http://github.com/naelstrof/maim/graphs/contributors&gt;</code></p>
147
148
149 <ol class='man-decor man-foot man foot'>
150 <li class='tl'></li>
151 <li class='tc'>December 2014</li>
152 <li class='tr'>maim(1)</li>
153 </ol>
154
155 </div>
156 </body>
157 </html>
+0
-107
man-src/maim.1.ronn less more
0 maim(1) -- Takes screenshots
1 ============================
2
3 ## SYNOPSIS
4
5 `maim` [options] [file]
6
7 ## DESCRIPTION
8
9 maim (MAke IMage) is a utility that takes screenshots of your desktop using
10 imlib2. It's meant to overcome shortcomings of scrot and performs better than
11 scrot in several ways.
12
13 ## OPTIONS
14
15 * `--help`:
16 Print help and exit
17
18 * `-V`, `--version`:
19 Print version and exit
20
21 ### Options
22
23 * `--xdisplay=hostname:number.screen_number`:
24 Sets the x display.
25
26 * `-s`, `--select`:
27 Enables user region selection. Requires slop to
28 be installed. (default=off)
29
30 * `-x`, `--x=INT`:
31 Sets the x coordinate for taking an image
32
33 * `-y`, `--y=INT`:
34 Sets the y coordinate for taking an image
35
36 * `-w`, `--w=INT`:
37 Sets the width for taking an image
38
39 * `-h`, `--h=INT`:
40 Sets the height for taking an image
41
42 * `-g`, `--geometry=WxH+X+Y`:
43 Set the region to capture
44
45 * `-d`, `--delay=FLOAT`:
46 Set the amount of time to wait before taking an image. (default=`0.0')
47
48 * `-i`, `--windowid=INT`:
49 Set the window to capture. Defaults to the root window id.
50
51 * `--localize`:
52 Localizes given geometry to the given window. So `maim -i $ID -g 100x100+0+0 --localize` would screenshot the top-left 100x100 pixels of the given window, rather than the top-left 100x100 pixels of the root window. (default=off)
53
54 * `--hidecursor`:
55 Prevents the system cursor from showing up in screenshots. (default=off)
56
57 * `-m`, `--mask=STRING`:
58 Masks off-screen pixels so they don't show up in screenshots. (possible values="auto", "off", "on" default=`auto')
59
60 ### SLOP OPTIONS
61
62 * `--nokeyboard`:
63 Disables the ability to cancel selections with the keyboard. (default=off)
64
65 * `-b`, `--bordersize=INT`:
66 Set the selection rectangle's thickness. Does nothing when `--highlight` is enabled. (default=`5')
67
68 * `-p`, `--padding=INT`:
69 Set the padding size of the selection. Can be negative. (default=`0')
70
71 * `-t`, `--tolerance=INT`:
72 How far in pixels the mouse can move after clicking and still be detected as a normal click instead of a click and drag. Setting this to 0 will disable window selections. (default=`2')
73
74 * `--gracetime=FLOAT`:
75 Set the amount of time before slop will check for keyboard cancellations in seconds. (default=`0.4')
76
77 * `-c`, `--color=FLOAT,FLOAT,FLOAT,FLOAT`:
78 Set the selection rectangle's color. Supports RGB or RGBA values. (default=`0.5,0.5,0.5,1')
79
80 * `-n`, `--nodecorations`:
81 Attempt to select child windows in order to avoid window decorations. (default=off)
82
83 * `--min=INT`:
84 Set the minimum output of width or height values. This is useful to avoid outputting 0. Setting min and max to the same value disables drag selections. (default=`0')
85
86 * `--max=INT`:
87 Set the maximum output of width or height values. Setting min and max to the same value disables drag selections. (default=`0')
88
89 * `-l`, `--highlight`:
90 Instead of outlining selections, slop highlights it. This is only useful when `--color` is set to a transparent color. (default=off)
91
92 ## EXAMPLES
93 Screenshot the active window
94
95 $ maim -i $(xdotool getactivewindow)
96
97 Prompt a transparent red selection to screenshot.
98
99 $ maim -s -c 1,0,0,0.6
100
101 Save a dated screenshot.
102
103 $ maim ~/$(date +%F-%T).png
104 ## COPYRIGHT
105
106 Copyright (C) 2014 Dalton Nell `<naelstrof@gmail.com>`, Maim Contributors `<http://github.com/naelstrof/maim/graphs/contributors>`.
0 # Try to find GLX. Once done, this will define:
1 #
2 # GLX_FOUND - variable which returns the result of the search
3 # GLX_INCLUDE_DIRS - list of include directories
4 # GLX_LIBRARIES - options for the linker
5
6 #=============================================================================
7 # Copyright 2012 Benjamin Eikel
8 #
9 # Distributed under the OSI-approved BSD License (the "License");
10 # see accompanying file Copyright.txt for details.
11 #
12 # This software is distributed WITHOUT ANY WARRANTY; without even the
13 # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 # See the License for more information.
15 #=============================================================================
16 # (To distribute this file outside of CMake, substitute the full
17 # License text for the above reference.)
18
19 find_package(PkgConfig)
20 pkg_check_modules(PC_GLX QUIET glx)
21
22 find_path(GLX_INCLUDE_DIR
23 GL/glx.h
24 HINTS ${PC_GLX_INCLUDEDIR} ${PC_GLX_INCLUDE_DIRS}
25 )
26 find_library(GLX_LIBRARY
27 GL
28 HINTS ${PC_GLX_LIBDIR} ${PC_GLX_LIBRARY_DIRS}
29 )
30
31 set(GLX_INCLUDE_DIRS ${GLX_INCLUDE_DIR})
32 set(GLX_LIBRARIES ${GLX_LIBRARY})
33
34 include(FindPackageHandleStandardArgs)
35 find_package_handle_standard_args(GLX DEFAULT_MSG
36 GLX_INCLUDE_DIR
37 GLX_LIBRARY
38 )
39
40 mark_as_advanced(
41 GLX_INCLUDE_DIR
42 GLX_LIBRARY
43 )
0 # - Find SLOP
1 # Find the SLOP libraries
2 #
3 # This module defines the following variables:
4 # SLOP_FOUND - 1 if SLOP_INCLUDE_DIR & SLOP_LIBRARY are found, 0 otherwise
5 # SLOP_INCLUDE_DIR - where to find Xlib.h, etc.
6 # SLOP_LIBRARY - the X11 library
7 #
8
9
10 find_path( SLOP_INCLUDE_DIRS
11 NAMES slop.hpp
12 PATH_SUFFIXES /usr/include /include
13 DOC "The SLOP include directory" )
14
15 find_library( SLOP_LIBRARIES
16 NAMES slopy slopy.so slop slop.so
17 PATHS /usr/lib /lib
18 DOC "The SLOP library" )
19
20 FIND_PACKAGE(X11 REQUIRED)
21 FIND_PACKAGE(GLX REQUIRED)
22 list(APPEND SLOP_LIBRARIES
23 ${X11_LIBRARIES}
24 ${GLX_LIBRARY}
25 )
26
27 if( SLOP_INCLUDE_DIR AND SLOP_LIBRARY )
28 set( SLOP_FOUND 1 )
29 else()
30 set( SLOP_FOUND 0 )
31 endif()
32
33 mark_as_advanced( SLOP_INCLUDE_DIR SLOP_LIBRARY )
0 # - Find XComposite
1 # Find the XComposite libraries
2 #
3 # This module defines the following variables:
4 # XCOMPOSITE_FOUND - 1 if XCOMPOSITE_INCLUDE_DIR & XCOMPOSITE_LIBRARY are found, 0 otherwise
5 # XCOMPOSITE_INCLUDE_DIR - where to find Xlib.h, etc.
6 # XCOMPOSITE_LIBRARY - the X11 library
7 #
8
9 find_path( XCOMPOSITE_INCLUDE_DIR
10 NAMES X11/extensions/Xcomposite.h
11 PATH_SUFFIXES X11/extensions
12 DOC "The XComposite include directory" )
13
14 find_library( XCOMPOSITE_LIBRARY
15 NAMES Xcomposite
16 PATHS /usr/lib /lib
17 DOC "The XComposite library" )
18
19 if( XCOMPOSITE_INCLUDE_DIR AND XCOMPOSITE_LIBRARY )
20 set( XCOMPOSITE_FOUND 1 )
21 else()
22 set( XCOMPOSITE_FOUND 0 )
23 endif()
24
25 mark_as_advanced( XCOMPOSITE_INCLUDE_DIR XCOMPOSITE_LIBRARY )
0 # - Find XFixes
1 # Find the XFixes libraries
2 #
3 # This module defines the following variables:
4 # XFIXES_FOUND - 1 if XFIXES_INCLUDE_DIR & XFIXES_LIBRARY are found, 0 otherwise
5 # XFIXES_INCLUDE_DIR - where to find Xlib.h, etc.
6 # XFIXES_LIBRARY - the X11 library
7 #
8
9 find_path( XFIXES_INCLUDE_DIR
10 NAMES X11/extensions/Xfixes.h
11 PATH_SUFFIXES X11/extensions
12 DOC "The XFixes include directory" )
13
14 find_library( XFIXES_LIBRARY
15 NAMES Xfixes
16 PATHS /usr/lib /lib
17 DOC "The XFixes library" )
18
19 if( XFIXES_INCLUDE_DIR AND XFIXES_LIBRARY )
20 set( XFIXES_FOUND 1 )
21 else()
22 set( XFIXES_FOUND 0 )
23 endif()
24
25 mark_as_advanced( XFIXES_INCLUDE_DIR XFIXES_LIBRARY )
0 # - Find XRandr
1 # Find the XRandr libraries
2 #
3 # This module defines the following variables:
4 # XRANDR_FOUND - 1 if XRANDR_INCLUDE_DIR & XRANDR_LIBRARY are found, 0 otherwise
5 # XRANDR_INCLUDE_DIR - where to find Xlib.h, etc.
6 # XRANDR_LIBRARY - the X11 library
7 #
8
9 find_path( XRANDR_INCLUDE_DIR
10 NAMES X11/extensions/Xrandr.h
11 PATH_SUFFIXES X11/extensions
12 DOC "The XRandr include directory" )
13
14 find_library( XRANDR_LIBRARY
15 NAMES Xrandr
16 PATHS /usr/lib /lib
17 DOC "The XRandr library" )
18
19 if( XRANDR_INCLUDE_DIR AND XRANDR_LIBRARY )
20 set( XRANDR_FOUND 1 )
21 else()
22 set( XRANDR_FOUND 0 )
23 endif()
24
25 mark_as_advanced( XRANDR_INCLUDE_DIR XRANDR_LIBRARY )
0 # - Find XRender
1 # Find the XRender libraries
2 #
3 # This module defines the following variables:
4 # XRENDER_FOUND - true if XRENDER_INCLUDE_DIR & XRENDER_LIBRARY are found
5 # XRENDER_LIBRARIES - Set when Xrender_LIBRARY is found
6 # XRENDER_INCLUDE_DIRS - Set when Xrender_INCLUDE_DIR is found
7 #
8 # XRENDER_INCLUDE_DIR - where to find Xrender.h, etc.
9 # XRENDER_LIBRARY - the Xrender library
10 #
11
12 #=============================================================================
13 # Copyright 2013 Corey Clayton <can.of.tuna@gmail.com>
14 #
15 # Licensed under the Apache License, Version 2.0 (the "License");
16 # you may not use this file except in compliance with the License.
17 # You may obtain a copy of the License at
18 #
19 # http://www.apache.org/licenses/LICENSE-2.0
20 #
21 # Unless required by applicable law or agreed to in writing, software
22 # distributed under the License is distributed on an "AS IS" BASIS,
23 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24 # See the License for the specific language governing permissions and
25 # limitations under the License.
26 #=============================================================================
27
28 find_path(XRENDER_INCLUDE_DIR NAMES X11/extensions/Xrender.h
29 PATHS /opt/X11/include
30 DOC "The Xrender include directory")
31
32 find_library(XRENDER_LIBRARY NAMES Xrender
33 PATHS /opt/X11/lib
34 DOC "The Xrender library")
35
36 include(FindPackageHandleStandardArgs)
37 FIND_PACKAGE_HANDLE_STANDARD_ARGS(Xrender DEFAULT_MSG XRENDER_LIBRARY XRENDER_INCLUDE_DIR)
38
39 if(XRENDER_FOUND)
40
41 set(XRENDER_LIBRARIES ${XRENDER_LIBRARY})
42 set(XRENDER_INCLUDE_DIRS ${XRENDER_INCLUDE_DIR})
43
44 endif()
45
46 mark_as_advanced(XRENDER_INCLUDE_DIR XRENDER_LIBRARY)
+0
-1045
src/cmdline.c less more
0 /*
1 File autogenerated by gengetopt version 2.22.6
2 generated with the following command:
3 /usr/bin/gengetopt --input=options.ggo --unamed-opts --file-name=cmdline
4
5 The developers of gengetopt consider the fixed text that goes in all
6 gengetopt output files to be in the public domain:
7 we make no copyright claims on it.
8 */
9
10 /* If we use autoconf. */
11 #ifdef HAVE_CONFIG_H
12 #include "config.h"
13 #endif
14
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18
19 #ifndef FIX_UNUSED
20 #define FIX_UNUSED(X) (void) (X) /* avoid warnings for unused params */
21 #endif
22
23 #include <getopt.h>
24
25 #include "cmdline.h"
26
27 const char *gengetopt_args_info_purpose = "Takes screenshots.";
28
29 const char *gengetopt_args_info_usage = "Usage: maim [options] [file]";
30
31 const char *gengetopt_args_info_versiontext = "Copyright (C) 2014 Dalton Nell, Maim Contributors\n(https://github.com/naelstrof/maim/graphs/contributors)";
32
33 const char *gengetopt_args_info_description = "maim (Make Image) is a utility that takes screenshots of your desktop using\nimlib2. It's meant to overcome shortcomings of scrot and performs better than\nscrot in several ways.";
34
35 const char *gengetopt_args_info_help[] = {
36 " --help Print help and exit",
37 " -V, --version Print version and exit",
38 "Options",
39 " --xdisplay=hostname:number.screen_number\n Sets the x display.",
40 " -s, --select Enables user region selection. Requires slop to\n be installed. (default=off)",
41 " -x, --x=INT Sets the x coordinate for taking an image",
42 " -y, --y=INT Sets the y coordinate for taking an image",
43 " -w, --w=INT Sets the width for taking an image",
44 " -h, --h=INT Sets the height for taking an image",
45 " -g, --geometry=WxH+X+Y Set the region to capture",
46 " -d, --delay=FLOAT Set the amount of time to wait before taking an\n image. (default=`0.0')",
47 " -i, --windowid=INT Set the window to capture. Defaults to the root\n window id.",
48 " --localize Localizes given geometry to the given window.\n So \"maim -i $ID -g 100x100+0+0 --localize\"\n would screenshot the top-left 100x100 pixels\n of the given window, rather than the top-left\n 100x100 pixels of the root window.\n (default=off)",
49 " --hidecursor Prevents the system cursor from showing up in\n screenshots. (default=off)",
50 " -m, --mask=STRING Masks off-screen pixels so they don't show up\n in screenshots. (possible values=\"auto\",\n \"off\", \"on\" default=`auto')",
51 "\nSlop Options",
52 " --nokeyboard Disables the ability to cancel selections with\n the keyboard. (default=off)",
53 " -b, --bordersize=INT Set the selection rectangle's thickness. Does\n nothing when --highlight is enabled.\n (default=`5')",
54 " -p, --padding=INT Set the padding size of the selection. Can be\n negative. (default=`0')",
55 " -t, --tolerance=INT How far in pixels the mouse can move after\n clicking and still be detected as a normal\n click instead of a click and drag. Setting\n this to 0 will disable window selections.\n (default=`2')",
56 " --gracetime=FLOAT Set the amount of time before slop will check\n for keyboard cancellations in seconds.\n (default=`0.4')",
57 " -c, --color=FLOAT,FLOAT,FLOAT,FLOAT\n Set the selection rectangle's color. Supports\n RGB or RGBA values.\n (default=`0.5,0.5,0.5,1')",
58 " -n, --nodecorations Attempt to select child windows in order to\n avoid window decorations. (default=off)",
59 " --min=INT Set the minimum output of width or height\n values. This is useful to avoid outputting 0.\n Setting min and max to the same value\n disables drag selections. (default=`0')",
60 " --max=INT Set the maximum output of width or height\n values. Setting min and max to the same value\n disables drag selections. (default=`0')",
61 " -l, --highlight Instead of outlining selections, slop\n highlights it. This is only useful when\n --color is set to a transparent color.\n (default=off)",
62 "\nExamples\n $ # Screenshot the active window\n $ maim -i $(xdotool getactivewindow)\n\n $ # Prompt a transparent red selection to screenshot.\n $ maim -s -c 1,0,0,0.6\n\n $ # Save a dated screenshot.\n $ maim ~/$(date +%F-%T).png\n",
63 0
64 };
65
66 typedef enum {ARG_NO
67 , ARG_FLAG
68 , ARG_STRING
69 , ARG_INT
70 } cmdline_parser_arg_type;
71
72 static
73 void clear_given (struct gengetopt_args_info *args_info);
74 static
75 void clear_args (struct gengetopt_args_info *args_info);
76
77 static int
78 cmdline_parser_internal (int argc, char **argv, struct gengetopt_args_info *args_info,
79 struct cmdline_parser_params *params, const char *additional_error);
80
81
82 const char *cmdline_parser_mask_values[] = {"auto", "off", "on", 0}; /*< Possible values for mask. */
83
84 static char *
85 gengetopt_strdup (const char *s);
86
87 static
88 void clear_given (struct gengetopt_args_info *args_info)
89 {
90 args_info->help_given = 0 ;
91 args_info->version_given = 0 ;
92 args_info->xdisplay_given = 0 ;
93 args_info->select_given = 0 ;
94 args_info->x_given = 0 ;
95 args_info->y_given = 0 ;
96 args_info->w_given = 0 ;
97 args_info->h_given = 0 ;
98 args_info->geometry_given = 0 ;
99 args_info->delay_given = 0 ;
100 args_info->windowid_given = 0 ;
101 args_info->localize_given = 0 ;
102 args_info->hidecursor_given = 0 ;
103 args_info->mask_given = 0 ;
104 args_info->nokeyboard_given = 0 ;
105 args_info->bordersize_given = 0 ;
106 args_info->padding_given = 0 ;
107 args_info->tolerance_given = 0 ;
108 args_info->gracetime_given = 0 ;
109 args_info->color_given = 0 ;
110 args_info->nodecorations_given = 0 ;
111 args_info->min_given = 0 ;
112 args_info->max_given = 0 ;
113 args_info->highlight_given = 0 ;
114 }
115
116 static
117 void clear_args (struct gengetopt_args_info *args_info)
118 {
119 FIX_UNUSED (args_info);
120 args_info->xdisplay_arg = NULL;
121 args_info->xdisplay_orig = NULL;
122 args_info->select_flag = 0;
123 args_info->x_orig = NULL;
124 args_info->y_orig = NULL;
125 args_info->w_orig = NULL;
126 args_info->h_orig = NULL;
127 args_info->geometry_arg = NULL;
128 args_info->geometry_orig = NULL;
129 args_info->delay_arg = gengetopt_strdup ("0.0");
130 args_info->delay_orig = NULL;
131 args_info->windowid_orig = NULL;
132 args_info->localize_flag = 0;
133 args_info->hidecursor_flag = 0;
134 args_info->mask_arg = gengetopt_strdup ("auto");
135 args_info->mask_orig = NULL;
136 args_info->nokeyboard_flag = 0;
137 args_info->bordersize_arg = 5;
138 args_info->bordersize_orig = NULL;
139 args_info->padding_arg = 0;
140 args_info->padding_orig = NULL;
141 args_info->tolerance_arg = 2;
142 args_info->tolerance_orig = NULL;
143 args_info->gracetime_arg = gengetopt_strdup ("0.4");
144 args_info->gracetime_orig = NULL;
145 args_info->color_arg = gengetopt_strdup ("0.5,0.5,0.5,1");
146 args_info->color_orig = NULL;
147 args_info->nodecorations_flag = 0;
148 args_info->min_arg = 0;
149 args_info->min_orig = NULL;
150 args_info->max_arg = 0;
151 args_info->max_orig = NULL;
152 args_info->highlight_flag = 0;
153
154 }
155
156 static
157 void init_args_info(struct gengetopt_args_info *args_info)
158 {
159
160
161 args_info->help_help = gengetopt_args_info_help[0] ;
162 args_info->version_help = gengetopt_args_info_help[1] ;
163 args_info->xdisplay_help = gengetopt_args_info_help[3] ;
164 args_info->select_help = gengetopt_args_info_help[4] ;
165 args_info->x_help = gengetopt_args_info_help[5] ;
166 args_info->y_help = gengetopt_args_info_help[6] ;
167 args_info->w_help = gengetopt_args_info_help[7] ;
168 args_info->h_help = gengetopt_args_info_help[8] ;
169 args_info->geometry_help = gengetopt_args_info_help[9] ;
170 args_info->delay_help = gengetopt_args_info_help[10] ;
171 args_info->windowid_help = gengetopt_args_info_help[11] ;
172 args_info->localize_help = gengetopt_args_info_help[12] ;
173 args_info->hidecursor_help = gengetopt_args_info_help[13] ;
174 args_info->mask_help = gengetopt_args_info_help[14] ;
175 args_info->nokeyboard_help = gengetopt_args_info_help[16] ;
176 args_info->bordersize_help = gengetopt_args_info_help[17] ;
177 args_info->padding_help = gengetopt_args_info_help[18] ;
178 args_info->tolerance_help = gengetopt_args_info_help[19] ;
179 args_info->gracetime_help = gengetopt_args_info_help[20] ;
180 args_info->color_help = gengetopt_args_info_help[21] ;
181 args_info->nodecorations_help = gengetopt_args_info_help[22] ;
182 args_info->min_help = gengetopt_args_info_help[23] ;
183 args_info->max_help = gengetopt_args_info_help[24] ;
184 args_info->highlight_help = gengetopt_args_info_help[25] ;
185
186 }
187
188 void
189 cmdline_parser_print_version (void)
190 {
191 printf ("%s %s\n",
192 (strlen(CMDLINE_PARSER_PACKAGE_NAME) ? CMDLINE_PARSER_PACKAGE_NAME : CMDLINE_PARSER_PACKAGE),
193 CMDLINE_PARSER_VERSION);
194
195 if (strlen(gengetopt_args_info_versiontext) > 0)
196 printf("\n%s\n", gengetopt_args_info_versiontext);
197 }
198
199 static void print_help_common(void) {
200 cmdline_parser_print_version ();
201
202 if (strlen(gengetopt_args_info_purpose) > 0)
203 printf("\n%s\n", gengetopt_args_info_purpose);
204
205 if (strlen(gengetopt_args_info_usage) > 0)
206 printf("\n%s\n", gengetopt_args_info_usage);
207
208 printf("\n");
209
210 if (strlen(gengetopt_args_info_description) > 0)
211 printf("%s\n\n", gengetopt_args_info_description);
212 }
213
214 void
215 cmdline_parser_print_help (void)
216 {
217 int i = 0;
218 print_help_common();
219 while (gengetopt_args_info_help[i])
220 printf("%s\n", gengetopt_args_info_help[i++]);
221 }
222
223 void
224 cmdline_parser_init (struct gengetopt_args_info *args_info)
225 {
226 clear_given (args_info);
227 clear_args (args_info);
228 init_args_info (args_info);
229
230 args_info->inputs = 0;
231 args_info->inputs_num = 0;
232 }
233
234 void
235 cmdline_parser_params_init(struct cmdline_parser_params *params)
236 {
237 if (params)
238 {
239 params->override = 0;
240 params->initialize = 1;
241 params->check_required = 1;
242 params->check_ambiguity = 0;
243 params->print_errors = 1;
244 }
245 }
246
247 struct cmdline_parser_params *
248 cmdline_parser_params_create(void)
249 {
250 struct cmdline_parser_params *params =
251 (struct cmdline_parser_params *)malloc(sizeof(struct cmdline_parser_params));
252 cmdline_parser_params_init(params);
253 return params;
254 }
255
256 static void
257 free_string_field (char **s)
258 {
259 if (*s)
260 {
261 free (*s);
262 *s = 0;
263 }
264 }
265
266
267 static void
268 cmdline_parser_release (struct gengetopt_args_info *args_info)
269 {
270 unsigned int i;
271 free_string_field (&(args_info->xdisplay_arg));
272 free_string_field (&(args_info->xdisplay_orig));
273 free_string_field (&(args_info->x_orig));
274 free_string_field (&(args_info->y_orig));
275 free_string_field (&(args_info->w_orig));
276 free_string_field (&(args_info->h_orig));
277 free_string_field (&(args_info->geometry_arg));
278 free_string_field (&(args_info->geometry_orig));
279 free_string_field (&(args_info->delay_arg));
280 free_string_field (&(args_info->delay_orig));
281 free_string_field (&(args_info->windowid_orig));
282 free_string_field (&(args_info->mask_arg));
283 free_string_field (&(args_info->mask_orig));
284 free_string_field (&(args_info->bordersize_orig));
285 free_string_field (&(args_info->padding_orig));
286 free_string_field (&(args_info->tolerance_orig));
287 free_string_field (&(args_info->gracetime_arg));
288 free_string_field (&(args_info->gracetime_orig));
289 free_string_field (&(args_info->color_arg));
290 free_string_field (&(args_info->color_orig));
291 free_string_field (&(args_info->min_orig));
292 free_string_field (&(args_info->max_orig));
293
294
295 for (i = 0; i < args_info->inputs_num; ++i)
296 free (args_info->inputs [i]);
297
298 if (args_info->inputs_num)
299 free (args_info->inputs);
300
301 clear_given (args_info);
302 }
303
304 /**
305 * @param val the value to check
306 * @param values the possible values
307 * @return the index of the matched value:
308 * -1 if no value matched,
309 * -2 if more than one value has matched
310 */
311 static int
312 check_possible_values(const char *val, const char *values[])
313 {
314 int i, found, last;
315 size_t len;
316
317 if (!val) /* otherwise strlen() crashes below */
318 return -1; /* -1 means no argument for the option */
319
320 found = last = 0;
321
322 for (i = 0, len = strlen(val); values[i]; ++i)
323 {
324 if (strncmp(val, values[i], len) == 0)
325 {
326 ++found;
327 last = i;
328 if (strlen(values[i]) == len)
329 return i; /* exact macth no need to check more */
330 }
331 }
332
333 if (found == 1) /* one match: OK */
334 return last;
335
336 return (found ? -2 : -1); /* return many values or none matched */
337 }
338
339
340 static void
341 write_into_file(FILE *outfile, const char *opt, const char *arg, const char *values[])
342 {
343 int found = -1;
344 if (arg) {
345 if (values) {
346 found = check_possible_values(arg, values);
347 }
348 if (found >= 0)
349 fprintf(outfile, "%s=\"%s\" # %s\n", opt, arg, values[found]);
350 else
351 fprintf(outfile, "%s=\"%s\"\n", opt, arg);
352 } else {
353 fprintf(outfile, "%s\n", opt);
354 }
355 }
356
357
358 int
359 cmdline_parser_dump(FILE *outfile, struct gengetopt_args_info *args_info)
360 {
361 int i = 0;
362
363 if (!outfile)
364 {
365 fprintf (stderr, "%s: cannot dump options to stream\n", CMDLINE_PARSER_PACKAGE);
366 return EXIT_FAILURE;
367 }
368
369 if (args_info->help_given)
370 write_into_file(outfile, "help", 0, 0 );
371 if (args_info->version_given)
372 write_into_file(outfile, "version", 0, 0 );
373 if (args_info->xdisplay_given)
374 write_into_file(outfile, "xdisplay", args_info->xdisplay_orig, 0);
375 if (args_info->select_given)
376 write_into_file(outfile, "select", 0, 0 );
377 if (args_info->x_given)
378 write_into_file(outfile, "x", args_info->x_orig, 0);
379 if (args_info->y_given)
380 write_into_file(outfile, "y", args_info->y_orig, 0);
381 if (args_info->w_given)
382 write_into_file(outfile, "w", args_info->w_orig, 0);
383 if (args_info->h_given)
384 write_into_file(outfile, "h", args_info->h_orig, 0);
385 if (args_info->geometry_given)
386 write_into_file(outfile, "geometry", args_info->geometry_orig, 0);
387 if (args_info->delay_given)
388 write_into_file(outfile, "delay", args_info->delay_orig, 0);
389 if (args_info->windowid_given)
390 write_into_file(outfile, "windowid", args_info->windowid_orig, 0);
391 if (args_info->localize_given)
392 write_into_file(outfile, "localize", 0, 0 );
393 if (args_info->hidecursor_given)
394 write_into_file(outfile, "hidecursor", 0, 0 );
395 if (args_info->mask_given)
396 write_into_file(outfile, "mask", args_info->mask_orig, cmdline_parser_mask_values);
397 if (args_info->nokeyboard_given)
398 write_into_file(outfile, "nokeyboard", 0, 0 );
399 if (args_info->bordersize_given)
400 write_into_file(outfile, "bordersize", args_info->bordersize_orig, 0);
401 if (args_info->padding_given)
402 write_into_file(outfile, "padding", args_info->padding_orig, 0);
403 if (args_info->tolerance_given)
404 write_into_file(outfile, "tolerance", args_info->tolerance_orig, 0);
405 if (args_info->gracetime_given)
406 write_into_file(outfile, "gracetime", args_info->gracetime_orig, 0);
407 if (args_info->color_given)
408 write_into_file(outfile, "color", args_info->color_orig, 0);
409 if (args_info->nodecorations_given)
410 write_into_file(outfile, "nodecorations", 0, 0 );
411 if (args_info->min_given)
412 write_into_file(outfile, "min", args_info->min_orig, 0);
413 if (args_info->max_given)
414 write_into_file(outfile, "max", args_info->max_orig, 0);
415 if (args_info->highlight_given)
416 write_into_file(outfile, "highlight", 0, 0 );
417
418
419 i = EXIT_SUCCESS;
420 return i;
421 }
422
423 int
424 cmdline_parser_file_save(const char *filename, struct gengetopt_args_info *args_info)
425 {
426 FILE *outfile;
427 int i = 0;
428
429 outfile = fopen(filename, "w");
430
431 if (!outfile)
432 {
433 fprintf (stderr, "%s: cannot open file for writing: %s\n", CMDLINE_PARSER_PACKAGE, filename);
434 return EXIT_FAILURE;
435 }
436
437 i = cmdline_parser_dump(outfile, args_info);
438 fclose (outfile);
439
440 return i;
441 }
442
443 void
444 cmdline_parser_free (struct gengetopt_args_info *args_info)
445 {
446 cmdline_parser_release (args_info);
447 }
448
449 /** @brief replacement of strdup, which is not standard */
450 char *
451 gengetopt_strdup (const char *s)
452 {
453 char *result = 0;
454 if (!s)
455 return result;
456
457 result = (char*)malloc(strlen(s) + 1);
458 if (result == (char*)0)
459 return (char*)0;
460 strcpy(result, s);
461 return result;
462 }
463
464 int
465 cmdline_parser (int argc, char **argv, struct gengetopt_args_info *args_info)
466 {
467 return cmdline_parser2 (argc, argv, args_info, 0, 1, 1);
468 }
469
470 int
471 cmdline_parser_ext (int argc, char **argv, struct gengetopt_args_info *args_info,
472 struct cmdline_parser_params *params)
473 {
474 int result;
475 result = cmdline_parser_internal (argc, argv, args_info, params, 0);
476
477 if (result == EXIT_FAILURE)
478 {
479 cmdline_parser_free (args_info);
480 exit (EXIT_FAILURE);
481 }
482
483 return result;
484 }
485
486 int
487 cmdline_parser2 (int argc, char **argv, struct gengetopt_args_info *args_info, int override, int initialize, int check_required)
488 {
489 int result;
490 struct cmdline_parser_params params;
491
492 params.override = override;
493 params.initialize = initialize;
494 params.check_required = check_required;
495 params.check_ambiguity = 0;
496 params.print_errors = 1;
497
498 result = cmdline_parser_internal (argc, argv, args_info, &params, 0);
499
500 if (result == EXIT_FAILURE)
501 {
502 cmdline_parser_free (args_info);
503 exit (EXIT_FAILURE);
504 }
505
506 return result;
507 }
508
509 int
510 cmdline_parser_required (struct gengetopt_args_info *args_info, const char *prog_name)
511 {
512 FIX_UNUSED (args_info);
513 FIX_UNUSED (prog_name);
514 return EXIT_SUCCESS;
515 }
516
517
518 static char *package_name = 0;
519
520 /**
521 * @brief updates an option
522 * @param field the generic pointer to the field to update
523 * @param orig_field the pointer to the orig field
524 * @param field_given the pointer to the number of occurrence of this option
525 * @param prev_given the pointer to the number of occurrence already seen
526 * @param value the argument for this option (if null no arg was specified)
527 * @param possible_values the possible values for this option (if specified)
528 * @param default_value the default value (in case the option only accepts fixed values)
529 * @param arg_type the type of this option
530 * @param check_ambiguity @see cmdline_parser_params.check_ambiguity
531 * @param override @see cmdline_parser_params.override
532 * @param no_free whether to free a possible previous value
533 * @param multiple_option whether this is a multiple option
534 * @param long_opt the corresponding long option
535 * @param short_opt the corresponding short option (or '-' if none)
536 * @param additional_error possible further error specification
537 */
538 static
539 int update_arg(void *field, char **orig_field,
540 unsigned int *field_given, unsigned int *prev_given,
541 char *value, const char *possible_values[],
542 const char *default_value,
543 cmdline_parser_arg_type arg_type,
544 int check_ambiguity, int override,
545 int no_free, int multiple_option,
546 const char *long_opt, char short_opt,
547 const char *additional_error)
548 {
549 char *stop_char = 0;
550 const char *val = value;
551 int found;
552 char **string_field;
553 FIX_UNUSED (field);
554
555 stop_char = 0;
556 found = 0;
557
558 if (!multiple_option && prev_given && (*prev_given || (check_ambiguity && *field_given)))
559 {
560 if (short_opt != '-')
561 fprintf (stderr, "%s: `--%s' (`-%c') option given more than once%s\n",
562 package_name, long_opt, short_opt,
563 (additional_error ? additional_error : ""));
564 else
565 fprintf (stderr, "%s: `--%s' option given more than once%s\n",
566 package_name, long_opt,
567 (additional_error ? additional_error : ""));
568 return 1; /* failure */
569 }
570
571 if (possible_values && (found = check_possible_values((value ? value : default_value), possible_values)) < 0)
572 {
573 if (short_opt != '-')
574 fprintf (stderr, "%s: %s argument, \"%s\", for option `--%s' (`-%c')%s\n",
575 package_name, (found == -2) ? "ambiguous" : "invalid", value, long_opt, short_opt,
576 (additional_error ? additional_error : ""));
577 else
578 fprintf (stderr, "%s: %s argument, \"%s\", for option `--%s'%s\n",
579 package_name, (found == -2) ? "ambiguous" : "invalid", value, long_opt,
580 (additional_error ? additional_error : ""));
581 return 1; /* failure */
582 }
583
584 if (field_given && *field_given && ! override)
585 return 0;
586 if (prev_given)
587 (*prev_given)++;
588 if (field_given)
589 (*field_given)++;
590 if (possible_values)
591 val = possible_values[found];
592
593 switch(arg_type) {
594 case ARG_FLAG:
595 *((int *)field) = !*((int *)field);
596 break;
597 case ARG_INT:
598 if (val) *((int *)field) = strtol (val, &stop_char, 0);
599 break;
600 case ARG_STRING:
601 if (val) {
602 string_field = (char **)field;
603 if (!no_free && *string_field)
604 free (*string_field); /* free previous string */
605 *string_field = gengetopt_strdup (val);
606 }
607 break;
608 default:
609 break;
610 };
611
612 /* check numeric conversion */
613 switch(arg_type) {
614 case ARG_INT:
615 if (val && !(stop_char && *stop_char == '\0')) {
616 fprintf(stderr, "%s: invalid numeric value: %s\n", package_name, val);
617 return 1; /* failure */
618 }
619 break;
620 default:
621 ;
622 };
623
624 /* store the original value */
625 switch(arg_type) {
626 case ARG_NO:
627 case ARG_FLAG:
628 break;
629 default:
630 if (value && orig_field) {
631 if (no_free) {
632 *orig_field = value;
633 } else {
634 if (*orig_field)
635 free (*orig_field); /* free previous string */
636 *orig_field = gengetopt_strdup (value);
637 }
638 }
639 };
640
641 return 0; /* OK */
642 }
643
644
645 int
646 cmdline_parser_internal (
647 int argc, char **argv, struct gengetopt_args_info *args_info,
648 struct cmdline_parser_params *params, const char *additional_error)
649 {
650 int c; /* Character of the parsed option. */
651
652 int error_occurred = 0;
653 struct gengetopt_args_info local_args_info;
654
655 int override;
656 int initialize;
657 int check_required;
658 int check_ambiguity;
659
660 package_name = argv[0];
661
662 override = params->override;
663 initialize = params->initialize;
664 check_required = params->check_required;
665 check_ambiguity = params->check_ambiguity;
666
667 if (initialize)
668 cmdline_parser_init (args_info);
669
670 cmdline_parser_init (&local_args_info);
671
672 optarg = 0;
673 optind = 0;
674 opterr = params->print_errors;
675 optopt = '?';
676
677 while (1)
678 {
679 int option_index = 0;
680
681 static struct option long_options[] = {
682 { "help", 0, NULL, 0 },
683 { "version", 0, NULL, 'V' },
684 { "xdisplay", 1, NULL, 0 },
685 { "select", 0, NULL, 's' },
686 { "x", 1, NULL, 'x' },
687 { "y", 1, NULL, 'y' },
688 { "w", 1, NULL, 'w' },
689 { "h", 1, NULL, 'h' },
690 { "geometry", 1, NULL, 'g' },
691 { "delay", 1, NULL, 'd' },
692 { "windowid", 1, NULL, 'i' },
693 { "localize", 0, NULL, 0 },
694 { "hidecursor", 0, NULL, 0 },
695 { "mask", 1, NULL, 'm' },
696 { "nokeyboard", 0, NULL, 0 },
697 { "bordersize", 1, NULL, 'b' },
698 { "padding", 1, NULL, 'p' },
699 { "tolerance", 1, NULL, 't' },
700 { "gracetime", 1, NULL, 0 },
701 { "color", 1, NULL, 'c' },
702 { "nodecorations", 0, NULL, 'n' },
703 { "min", 1, NULL, 0 },
704 { "max", 1, NULL, 0 },
705 { "highlight", 0, NULL, 'l' },
706 { 0, 0, 0, 0 }
707 };
708
709 c = getopt_long (argc, argv, "Vsx:y:w:h:g:d:i:m:b:p:t:c:nl", long_options, &option_index);
710
711 if (c == -1) break; /* Exit from `while (1)' loop. */
712
713 switch (c)
714 {
715 case 'V': /* Print version and exit. */
716 cmdline_parser_print_version ();
717 cmdline_parser_free (&local_args_info);
718 exit (EXIT_SUCCESS);
719
720 case 's': /* Enables user region selection. Requires slop to be installed.. */
721
722
723 if (update_arg((void *)&(args_info->select_flag), 0, &(args_info->select_given),
724 &(local_args_info.select_given), optarg, 0, 0, ARG_FLAG,
725 check_ambiguity, override, 1, 0, "select", 's',
726 additional_error))
727 goto failure;
728
729 break;
730 case 'x': /* Sets the x coordinate for taking an image. */
731
732
733 if (update_arg( (void *)&(args_info->x_arg),
734 &(args_info->x_orig), &(args_info->x_given),
735 &(local_args_info.x_given), optarg, 0, 0, ARG_INT,
736 check_ambiguity, override, 0, 0,
737 "x", 'x',
738 additional_error))
739 goto failure;
740
741 break;
742 case 'y': /* Sets the y coordinate for taking an image. */
743
744
745 if (update_arg( (void *)&(args_info->y_arg),
746 &(args_info->y_orig), &(args_info->y_given),
747 &(local_args_info.y_given), optarg, 0, 0, ARG_INT,
748 check_ambiguity, override, 0, 0,
749 "y", 'y',
750 additional_error))
751 goto failure;
752
753 break;
754 case 'w': /* Sets the width for taking an image. */
755
756
757 if (update_arg( (void *)&(args_info->w_arg),
758 &(args_info->w_orig), &(args_info->w_given),
759 &(local_args_info.w_given), optarg, 0, 0, ARG_INT,
760 check_ambiguity, override, 0, 0,
761 "w", 'w',
762 additional_error))
763 goto failure;
764
765 break;
766 case 'h': /* Sets the height for taking an image. */
767
768
769 if (update_arg( (void *)&(args_info->h_arg),
770 &(args_info->h_orig), &(args_info->h_given),
771 &(local_args_info.h_given), optarg, 0, 0, ARG_INT,
772 check_ambiguity, override, 0, 0,
773 "h", 'h',
774 additional_error))
775 goto failure;
776
777 break;
778 case 'g': /* Set the region to capture. */
779
780
781 if (update_arg( (void *)&(args_info->geometry_arg),
782 &(args_info->geometry_orig), &(args_info->geometry_given),
783 &(local_args_info.geometry_given), optarg, 0, 0, ARG_STRING,
784 check_ambiguity, override, 0, 0,
785 "geometry", 'g',
786 additional_error))
787 goto failure;
788
789 break;
790 case 'd': /* Set the amount of time to wait before taking an image.. */
791
792
793 if (update_arg( (void *)&(args_info->delay_arg),
794 &(args_info->delay_orig), &(args_info->delay_given),
795 &(local_args_info.delay_given), optarg, 0, "0.0", ARG_STRING,
796 check_ambiguity, override, 0, 0,
797 "delay", 'd',
798 additional_error))
799 goto failure;
800
801 break;
802 case 'i': /* Set the window to capture. Defaults to the root window id.. */
803
804
805 if (update_arg( (void *)&(args_info->windowid_arg),
806 &(args_info->windowid_orig), &(args_info->windowid_given),
807 &(local_args_info.windowid_given), optarg, 0, 0, ARG_INT,
808 check_ambiguity, override, 0, 0,
809 "windowid", 'i',
810 additional_error))
811 goto failure;
812
813 break;
814 case 'm': /* Masks off-screen pixels so they don't show up in screenshots.. */
815
816
817 if (update_arg( (void *)&(args_info->mask_arg),
818 &(args_info->mask_orig), &(args_info->mask_given),
819 &(local_args_info.mask_given), optarg, cmdline_parser_mask_values, "auto", ARG_STRING,
820 check_ambiguity, override, 0, 0,
821 "mask", 'm',
822 additional_error))
823 goto failure;
824
825 break;
826 case 'b': /* Set the selection rectangle's thickness. Does nothing when --highlight is enabled.. */
827
828
829 if (update_arg( (void *)&(args_info->bordersize_arg),
830 &(args_info->bordersize_orig), &(args_info->bordersize_given),
831 &(local_args_info.bordersize_given), optarg, 0, "5", ARG_INT,
832 check_ambiguity, override, 0, 0,
833 "bordersize", 'b',
834 additional_error))
835 goto failure;
836
837 break;
838 case 'p': /* Set the padding size of the selection. Can be negative.. */
839
840
841 if (update_arg( (void *)&(args_info->padding_arg),
842 &(args_info->padding_orig), &(args_info->padding_given),
843 &(local_args_info.padding_given), optarg, 0, "0", ARG_INT,
844 check_ambiguity, override, 0, 0,
845 "padding", 'p',
846 additional_error))
847 goto failure;
848
849 break;
850 case 't': /* How far in pixels the mouse can move after clicking and still be detected as a normal click instead of a click and drag. Setting this to 0 will disable window selections.. */
851
852
853 if (update_arg( (void *)&(args_info->tolerance_arg),
854 &(args_info->tolerance_orig), &(args_info->tolerance_given),
855 &(local_args_info.tolerance_given), optarg, 0, "2", ARG_INT,
856 check_ambiguity, override, 0, 0,
857 "tolerance", 't',
858 additional_error))
859 goto failure;
860
861 break;
862 case 'c': /* Set the selection rectangle's color. Supports RGB or RGBA values.. */
863
864
865 if (update_arg( (void *)&(args_info->color_arg),
866 &(args_info->color_orig), &(args_info->color_given),
867 &(local_args_info.color_given), optarg, 0, "0.5,0.5,0.5,1", ARG_STRING,
868 check_ambiguity, override, 0, 0,
869 "color", 'c',
870 additional_error))
871 goto failure;
872
873 break;
874 case 'n': /* Attempt to select child windows in order to avoid window decorations.. */
875
876
877 if (update_arg((void *)&(args_info->nodecorations_flag), 0, &(args_info->nodecorations_given),
878 &(local_args_info.nodecorations_given), optarg, 0, 0, ARG_FLAG,
879 check_ambiguity, override, 1, 0, "nodecorations", 'n',
880 additional_error))
881 goto failure;
882
883 break;
884 case 'l': /* Instead of outlining selections, slop highlights it. This is only useful when --color is set to a transparent color.. */
885
886
887 if (update_arg((void *)&(args_info->highlight_flag), 0, &(args_info->highlight_given),
888 &(local_args_info.highlight_given), optarg, 0, 0, ARG_FLAG,
889 check_ambiguity, override, 1, 0, "highlight", 'l',
890 additional_error))
891 goto failure;
892
893 break;
894
895 case 0: /* Long option with no short option */
896 if (strcmp (long_options[option_index].name, "help") == 0) {
897 cmdline_parser_print_help ();
898 cmdline_parser_free (&local_args_info);
899 exit (EXIT_SUCCESS);
900 }
901
902 /* Sets the x display.. */
903 if (strcmp (long_options[option_index].name, "xdisplay") == 0)
904 {
905
906
907 if (update_arg( (void *)&(args_info->xdisplay_arg),
908 &(args_info->xdisplay_orig), &(args_info->xdisplay_given),
909 &(local_args_info.xdisplay_given), optarg, 0, 0, ARG_STRING,
910 check_ambiguity, override, 0, 0,
911 "xdisplay", '-',
912 additional_error))
913 goto failure;
914
915 }
916 /* Localizes given geometry to the given window. So \"maim -i $ID -g 100x100+0+0 --localize\" would screenshot the top-left 100x100 pixels of the given window, rather than the top-left 100x100 pixels of the root window.. */
917 else if (strcmp (long_options[option_index].name, "localize") == 0)
918 {
919
920
921 if (update_arg((void *)&(args_info->localize_flag), 0, &(args_info->localize_given),
922 &(local_args_info.localize_given), optarg, 0, 0, ARG_FLAG,
923 check_ambiguity, override, 1, 0, "localize", '-',
924 additional_error))
925 goto failure;
926
927 }
928 /* Prevents the system cursor from showing up in screenshots.. */
929 else if (strcmp (long_options[option_index].name, "hidecursor") == 0)
930 {
931
932
933 if (update_arg((void *)&(args_info->hidecursor_flag), 0, &(args_info->hidecursor_given),
934 &(local_args_info.hidecursor_given), optarg, 0, 0, ARG_FLAG,
935 check_ambiguity, override, 1, 0, "hidecursor", '-',
936 additional_error))
937 goto failure;
938
939 }
940 /* Disables the ability to cancel selections with the keyboard.. */
941 else if (strcmp (long_options[option_index].name, "nokeyboard") == 0)
942 {
943
944
945 if (update_arg((void *)&(args_info->nokeyboard_flag), 0, &(args_info->nokeyboard_given),
946 &(local_args_info.nokeyboard_given), optarg, 0, 0, ARG_FLAG,
947 check_ambiguity, override, 1, 0, "nokeyboard", '-',
948 additional_error))
949 goto failure;
950
951 }
952 /* Set the amount of time before slop will check for keyboard cancellations in seconds.. */
953 else if (strcmp (long_options[option_index].name, "gracetime") == 0)
954 {
955
956
957 if (update_arg( (void *)&(args_info->gracetime_arg),
958 &(args_info->gracetime_orig), &(args_info->gracetime_given),
959 &(local_args_info.gracetime_given), optarg, 0, "0.4", ARG_STRING,
960 check_ambiguity, override, 0, 0,
961 "gracetime", '-',
962 additional_error))
963 goto failure;
964
965 }
966 /* Set the minimum output of width or height values. This is useful to avoid outputting 0. Setting min and max to the same value disables drag selections.. */
967 else if (strcmp (long_options[option_index].name, "min") == 0)
968 {
969
970
971 if (update_arg( (void *)&(args_info->min_arg),
972 &(args_info->min_orig), &(args_info->min_given),
973 &(local_args_info.min_given), optarg, 0, "0", ARG_INT,
974 check_ambiguity, override, 0, 0,
975 "min", '-',
976 additional_error))
977 goto failure;
978
979 }
980 /* Set the maximum output of width or height values. Setting min and max to the same value disables drag selections.. */
981 else if (strcmp (long_options[option_index].name, "max") == 0)
982 {
983
984
985 if (update_arg( (void *)&(args_info->max_arg),
986 &(args_info->max_orig), &(args_info->max_given),
987 &(local_args_info.max_given), optarg, 0, "0", ARG_INT,
988 check_ambiguity, override, 0, 0,
989 "max", '-',
990 additional_error))
991 goto failure;
992
993 }
994
995 break;
996 case '?': /* Invalid option. */
997 /* `getopt_long' already printed an error message. */
998 goto failure;
999
1000 default: /* bug: option not considered. */
1001 fprintf (stderr, "%s: option unknown: %c%s\n", CMDLINE_PARSER_PACKAGE, c, (additional_error ? additional_error : ""));
1002 abort ();
1003 } /* switch */
1004 } /* while */
1005
1006
1007
1008
1009 cmdline_parser_release (&local_args_info);
1010
1011 if ( error_occurred )
1012 return (EXIT_FAILURE);
1013
1014 if (optind < argc)
1015 {
1016 int i = 0 ;
1017 int found_prog_name = 0;
1018 /* whether program name, i.e., argv[0], is in the remaining args
1019 (this may happen with some implementations of getopt,
1020 but surely not with the one included by gengetopt) */
1021
1022 i = optind;
1023 while (i < argc)
1024 if (argv[i++] == argv[0]) {
1025 found_prog_name = 1;
1026 break;
1027 }
1028 i = 0;
1029
1030 args_info->inputs_num = argc - optind - found_prog_name;
1031 args_info->inputs =
1032 (char **)(malloc ((args_info->inputs_num)*sizeof(char *))) ;
1033 while (optind < argc)
1034 if (argv[optind++] != argv[0])
1035 args_info->inputs[ i++ ] = gengetopt_strdup (argv[optind-1]) ;
1036 }
1037
1038 return 0;
1039
1040 failure:
1041
1042 cmdline_parser_release (&local_args_info);
1043 return (EXIT_FAILURE);
1044 }
+0
-259
src/cmdline.in less more
0 /** @file cmdline.h
1 * @brief The header file for the command line option parser
2 * generated by GNU Gengetopt version 2.22.6
3 * http://www.gnu.org/software/gengetopt.
4 * DO NOT modify this file, since it can be overwritten
5 * @author GNU Gengetopt by Lorenzo Bettini */
6
7 #ifndef CMDLINE_H
8 #define CMDLINE_H
9
10 /* If we use autoconf. */
11 #ifdef HAVE_CONFIG_H
12 #include "config.h"
13 #endif
14
15 #include <stdio.h> /* for FILE */
16
17 #ifdef __cplusplus
18 extern "C" {
19 #endif /* __cplusplus */
20
21 #ifndef CMDLINE_PARSER_PACKAGE
22 /** @brief the program name (used for printing errors) */
23 #define CMDLINE_PARSER_PACKAGE "maim"
24 #endif
25
26 #ifndef CMDLINE_PARSER_PACKAGE_NAME
27 /** @brief the complete program name (used for help and version) */
28 #define CMDLINE_PARSER_PACKAGE_NAME "maim"
29 #endif
30
31 #ifndef CMDLINE_PARSER_VERSION
32 /** @brief the program version */
33 #define CMDLINE_PARSER_VERSION "v@maim_VERSION_MAJOR@.@maim_VERSION_MINOR@.@maim_VERSION_PATCH@"
34 #endif
35
36 /** @brief Where the command line options are stored */
37 struct gengetopt_args_info
38 {
39 const char *help_help; /**< @brief Print help and exit help description. */
40 const char *version_help; /**< @brief Print version and exit help description. */
41 char * xdisplay_arg; /**< @brief Sets the x display.. */
42 char * xdisplay_orig; /**< @brief Sets the x display. original value given at command line. */
43 const char *xdisplay_help; /**< @brief Sets the x display. help description. */
44 int select_flag; /**< @brief Enables user region selection. Requires slop to be installed. (default=off). */
45 const char *select_help; /**< @brief Enables user region selection. Requires slop to be installed. help description. */
46 int x_arg; /**< @brief Sets the x coordinate for taking an image. */
47 char * x_orig; /**< @brief Sets the x coordinate for taking an image original value given at command line. */
48 const char *x_help; /**< @brief Sets the x coordinate for taking an image help description. */
49 int y_arg; /**< @brief Sets the y coordinate for taking an image. */
50 char * y_orig; /**< @brief Sets the y coordinate for taking an image original value given at command line. */
51 const char *y_help; /**< @brief Sets the y coordinate for taking an image help description. */
52 int w_arg; /**< @brief Sets the width for taking an image. */
53 char * w_orig; /**< @brief Sets the width for taking an image original value given at command line. */
54 const char *w_help; /**< @brief Sets the width for taking an image help description. */
55 int h_arg; /**< @brief Sets the height for taking an image. */
56 char * h_orig; /**< @brief Sets the height for taking an image original value given at command line. */
57 const char *h_help; /**< @brief Sets the height for taking an image help description. */
58 char * geometry_arg; /**< @brief Set the region to capture. */
59 char * geometry_orig; /**< @brief Set the region to capture original value given at command line. */
60 const char *geometry_help; /**< @brief Set the region to capture help description. */
61 char * delay_arg; /**< @brief Set the amount of time to wait before taking an image. (default='0.0'). */
62 char * delay_orig; /**< @brief Set the amount of time to wait before taking an image. original value given at command line. */
63 const char *delay_help; /**< @brief Set the amount of time to wait before taking an image. help description. */
64 int windowid_arg; /**< @brief Set the window to capture. Defaults to the root window id.. */
65 char * windowid_orig; /**< @brief Set the window to capture. Defaults to the root window id. original value given at command line. */
66 const char *windowid_help; /**< @brief Set the window to capture. Defaults to the root window id. help description. */
67 int localize_flag; /**< @brief Localizes given geometry to the given window. So \"maim -i $ID -g 100x100+0+0 --localize\" would screenshot the top-left 100x100 pixels of the given window, rather than the top-left 100x100 pixels of the root window. (default=off). */
68 const char *localize_help; /**< @brief Localizes given geometry to the given window. So \"maim -i $ID -g 100x100+0+0 --localize\" would screenshot the top-left 100x100 pixels of the given window, rather than the top-left 100x100 pixels of the root window. help description. */
69 int hidecursor_flag; /**< @brief Prevents the system cursor from showing up in screenshots. (default=off). */
70 const char *hidecursor_help; /**< @brief Prevents the system cursor from showing up in screenshots. help description. */
71 char * mask_arg; /**< @brief Masks off-screen pixels so they don't show up in screenshots. (default='auto'). */
72 char * mask_orig; /**< @brief Masks off-screen pixels so they don't show up in screenshots. original value given at command line. */
73 const char *mask_help; /**< @brief Masks off-screen pixels so they don't show up in screenshots. help description. */
74 int nokeyboard_flag; /**< @brief Disables the ability to cancel selections with the keyboard. (default=off). */
75 const char *nokeyboard_help; /**< @brief Disables the ability to cancel selections with the keyboard. help description. */
76 int bordersize_arg; /**< @brief Set the selection rectangle's thickness. Does nothing when --highlight is enabled. (default='5'). */
77 char * bordersize_orig; /**< @brief Set the selection rectangle's thickness. Does nothing when --highlight is enabled. original value given at command line. */
78 const char *bordersize_help; /**< @brief Set the selection rectangle's thickness. Does nothing when --highlight is enabled. help description. */
79 int padding_arg; /**< @brief Set the padding size of the selection. Can be negative. (default='0'). */
80 char * padding_orig; /**< @brief Set the padding size of the selection. Can be negative. original value given at command line. */
81 const char *padding_help; /**< @brief Set the padding size of the selection. Can be negative. help description. */
82 int tolerance_arg; /**< @brief How far in pixels the mouse can move after clicking and still be detected as a normal click instead of a click and drag. Setting this to 0 will disable window selections. (default='2'). */
83 char * tolerance_orig; /**< @brief How far in pixels the mouse can move after clicking and still be detected as a normal click instead of a click and drag. Setting this to 0 will disable window selections. original value given at command line. */
84 const char *tolerance_help; /**< @brief How far in pixels the mouse can move after clicking and still be detected as a normal click instead of a click and drag. Setting this to 0 will disable window selections. help description. */
85 char * gracetime_arg; /**< @brief Set the amount of time before slop will check for keyboard cancellations in seconds. (default='0.4'). */
86 char * gracetime_orig; /**< @brief Set the amount of time before slop will check for keyboard cancellations in seconds. original value given at command line. */
87 const char *gracetime_help; /**< @brief Set the amount of time before slop will check for keyboard cancellations in seconds. help description. */
88 char * color_arg; /**< @brief Set the selection rectangle's color. Supports RGB or RGBA values. (default='0.5,0.5,0.5,1'). */
89 char * color_orig; /**< @brief Set the selection rectangle's color. Supports RGB or RGBA values. original value given at command line. */
90 const char *color_help; /**< @brief Set the selection rectangle's color. Supports RGB or RGBA values. help description. */
91 int nodecorations_flag; /**< @brief Attempt to select child windows in order to avoid window decorations. (default=off). */
92 const char *nodecorations_help; /**< @brief Attempt to select child windows in order to avoid window decorations. help description. */
93 int min_arg; /**< @brief Set the minimum output of width or height values. This is useful to avoid outputting 0. Setting min and max to the same value disables drag selections. (default='0'). */
94 char * min_orig; /**< @brief Set the minimum output of width or height values. This is useful to avoid outputting 0. Setting min and max to the same value disables drag selections. original value given at command line. */
95 const char *min_help; /**< @brief Set the minimum output of width or height values. This is useful to avoid outputting 0. Setting min and max to the same value disables drag selections. help description. */
96 int max_arg; /**< @brief Set the maximum output of width or height values. Setting min and max to the same value disables drag selections. (default='0'). */
97 char * max_orig; /**< @brief Set the maximum output of width or height values. Setting min and max to the same value disables drag selections. original value given at command line. */
98 const char *max_help; /**< @brief Set the maximum output of width or height values. Setting min and max to the same value disables drag selections. help description. */
99 int highlight_flag; /**< @brief Instead of outlining selections, slop highlights it. This is only useful when --color is set to a transparent color. (default=off). */
100 const char *highlight_help; /**< @brief Instead of outlining selections, slop highlights it. This is only useful when --color is set to a transparent color. help description. */
101
102 unsigned int help_given ; /**< @brief Whether help was given. */
103 unsigned int version_given ; /**< @brief Whether version was given. */
104 unsigned int xdisplay_given ; /**< @brief Whether xdisplay was given. */
105 unsigned int select_given ; /**< @brief Whether select was given. */
106 unsigned int x_given ; /**< @brief Whether x was given. */
107 unsigned int y_given ; /**< @brief Whether y was given. */
108 unsigned int w_given ; /**< @brief Whether w was given. */
109 unsigned int h_given ; /**< @brief Whether h was given. */
110 unsigned int geometry_given ; /**< @brief Whether geometry was given. */
111 unsigned int delay_given ; /**< @brief Whether delay was given. */
112 unsigned int windowid_given ; /**< @brief Whether windowid was given. */
113 unsigned int localize_given ; /**< @brief Whether localize was given. */
114 unsigned int hidecursor_given ; /**< @brief Whether hidecursor was given. */
115 unsigned int mask_given ; /**< @brief Whether mask was given. */
116 unsigned int nokeyboard_given ; /**< @brief Whether nokeyboard was given. */
117 unsigned int bordersize_given ; /**< @brief Whether bordersize was given. */
118 unsigned int padding_given ; /**< @brief Whether padding was given. */
119 unsigned int tolerance_given ; /**< @brief Whether tolerance was given. */
120 unsigned int gracetime_given ; /**< @brief Whether gracetime was given. */
121 unsigned int color_given ; /**< @brief Whether color was given. */
122 unsigned int nodecorations_given ; /**< @brief Whether nodecorations was given. */
123 unsigned int min_given ; /**< @brief Whether min was given. */
124 unsigned int max_given ; /**< @brief Whether max was given. */
125 unsigned int highlight_given ; /**< @brief Whether highlight was given. */
126
127 char **inputs ; /**< @brief unamed options (options without names) */
128 unsigned inputs_num ; /**< @brief unamed options number */
129 } ;
130
131 /** @brief The additional parameters to pass to parser functions */
132 struct cmdline_parser_params
133 {
134 int override; /**< @brief whether to override possibly already present options (default 0) */
135 int initialize; /**< @brief whether to initialize the option structure gengetopt_args_info (default 1) */
136 int check_required; /**< @brief whether to check that all required options were provided (default 1) */
137 int check_ambiguity; /**< @brief whether to check for options already specified in the option structure gengetopt_args_info (default 0) */
138 int print_errors; /**< @brief whether getopt_long should print an error message for a bad option (default 1) */
139 } ;
140
141 /** @brief the purpose string of the program */
142 extern const char *gengetopt_args_info_purpose;
143 /** @brief the usage string of the program */
144 extern const char *gengetopt_args_info_usage;
145 /** @brief the description string of the program */
146 extern const char *gengetopt_args_info_description;
147 /** @brief all the lines making the help output */
148 extern const char *gengetopt_args_info_help[];
149
150 /**
151 * The command line parser
152 * @param argc the number of command line options
153 * @param argv the command line options
154 * @param args_info the structure where option information will be stored
155 * @return 0 if everything went fine, NON 0 if an error took place
156 */
157 int cmdline_parser (int argc, char **argv,
158 struct gengetopt_args_info *args_info);
159
160 /**
161 * The command line parser (version with additional parameters - deprecated)
162 * @param argc the number of command line options
163 * @param argv the command line options
164 * @param args_info the structure where option information will be stored
165 * @param override whether to override possibly already present options
166 * @param initialize whether to initialize the option structure my_args_info
167 * @param check_required whether to check that all required options were provided
168 * @return 0 if everything went fine, NON 0 if an error took place
169 * @deprecated use cmdline_parser_ext() instead
170 */
171 int cmdline_parser2 (int argc, char **argv,
172 struct gengetopt_args_info *args_info,
173 int override, int initialize, int check_required);
174
175 /**
176 * The command line parser (version with additional parameters)
177 * @param argc the number of command line options
178 * @param argv the command line options
179 * @param args_info the structure where option information will be stored
180 * @param params additional parameters for the parser
181 * @return 0 if everything went fine, NON 0 if an error took place
182 */
183 int cmdline_parser_ext (int argc, char **argv,
184 struct gengetopt_args_info *args_info,
185 struct cmdline_parser_params *params);
186
187 /**
188 * Save the contents of the option struct into an already open FILE stream.
189 * @param outfile the stream where to dump options
190 * @param args_info the option struct to dump
191 * @return 0 if everything went fine, NON 0 if an error took place
192 */
193 int cmdline_parser_dump(FILE *outfile,
194 struct gengetopt_args_info *args_info);
195
196 /**
197 * Save the contents of the option struct into a (text) file.
198 * This file can be read by the config file parser (if generated by gengetopt)
199 * @param filename the file where to save
200 * @param args_info the option struct to save
201 * @return 0 if everything went fine, NON 0 if an error took place
202 */
203 int cmdline_parser_file_save(const char *filename,
204 struct gengetopt_args_info *args_info);
205
206 /**
207 * Print the help
208 */
209 void cmdline_parser_print_help(void);
210 /**
211 * Print the version
212 */
213 void cmdline_parser_print_version(void);
214
215 /**
216 * Initializes all the fields a cmdline_parser_params structure
217 * to their default values
218 * @param params the structure to initialize
219 */
220 void cmdline_parser_params_init(struct cmdline_parser_params *params);
221
222 /**
223 * Allocates dynamically a cmdline_parser_params structure and initializes
224 * all its fields to their default values
225 * @return the created and initialized cmdline_parser_params structure
226 */
227 struct cmdline_parser_params *cmdline_parser_params_create(void);
228
229 /**
230 * Initializes the passed gengetopt_args_info structure's fields
231 * (also set default values for options that have a default)
232 * @param args_info the structure to initialize
233 */
234 void cmdline_parser_init (struct gengetopt_args_info *args_info);
235 /**
236 * Deallocates the string fields of the gengetopt_args_info structure
237 * (but does not deallocate the structure itself)
238 * @param args_info the structure to deallocate
239 */
240 void cmdline_parser_free (struct gengetopt_args_info *args_info);
241
242 /**
243 * Checks that all the required options were specified
244 * @param args_info the structure to check
245 * @param prog_name the name of the program that will be used to print
246 * possible errors
247 * @return
248 */
249 int cmdline_parser_required (struct gengetopt_args_info *args_info,
250 const char *prog_name);
251
252 extern const char *cmdline_parser_mask_values[]; /**< @brief Possible values for mask. */
253
254
255 #ifdef __cplusplus
256 }
257 #endif /* __cplusplus */
258 #endif /* CMDLINE_H */
0 /*
1
2 Copyright (c) 2014, 2015, 2016 Jarryd Beck
3
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
10
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 THE SOFTWARE.
21
22 */
23
24 #ifndef CXX_OPTS_HPP
25 #define CXX_OPTS_HPP
26
27 #if defined(__GNUC__)
28 #pragma GCC diagnostic push
29 #pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
30 #endif
31
32 #include <cstring>
33 #include <exception>
34 #include <iostream>
35 #include <map>
36 #include <memory>
37 #include <regex>
38 #include <sstream>
39 #include <string>
40 #include <unordered_set>
41 #include <vector>
42
43 //when we ask cxxopts to use Unicode, help strings are processed using ICU,
44 //which results in the correct lengths being computed for strings when they
45 //are formatted for the help output
46 //it is necessary to make sure that <unicode/unistr.h> can be found by the
47 //compiler, and that icu-uc is linked in to the binary.
48
49 #ifdef CXXOPTS_USE_UNICODE
50 #include <unicode/unistr.h>
51
52 namespace cxxopts
53 {
54 typedef icu::UnicodeString String;
55
56 inline
57 String
58 toLocalString(std::string s)
59 {
60 return icu::UnicodeString::fromUTF8(s);
61 }
62
63 class UnicodeStringIterator : public
64 std::iterator<std::forward_iterator_tag, int32_t>
65 {
66 public:
67
68 UnicodeStringIterator(const icu::UnicodeString* s, int32_t pos)
69 : s(s)
70 , i(pos)
71 {
72 }
73
74 value_type
75 operator*() const
76 {
77 return s->char32At(i);
78 }
79
80 bool
81 operator==(const UnicodeStringIterator& rhs) const
82 {
83 return s == rhs.s && i == rhs.i;
84 }
85
86 bool
87 operator!=(const UnicodeStringIterator& rhs) const
88 {
89 return !(*this == rhs);
90 }
91
92 UnicodeStringIterator&
93 operator++()
94 {
95 ++i;
96 return *this;
97 }
98
99 UnicodeStringIterator
100 operator+(int32_t v)
101 {
102 return UnicodeStringIterator(s, i + v);
103 }
104
105 private:
106 const icu::UnicodeString* s;
107 int32_t i;
108 };
109
110 inline
111 String&
112 stringAppend(String&s, String a)
113 {
114 return s.append(std::move(a));
115 }
116
117 inline
118 String&
119 stringAppend(String& s, int n, UChar32 c)
120 {
121 for (int i = 0; i != n; ++i)
122 {
123 s.append(c);
124 }
125
126 return s;
127 }
128
129 template <typename Iterator>
130 String&
131 stringAppend(String& s, Iterator begin, Iterator end)
132 {
133 while (begin != end)
134 {
135 s.append(*begin);
136 ++begin;
137 }
138
139 return s;
140 }
141
142 inline
143 size_t
144 stringLength(const String& s)
145 {
146 return s.length();
147 }
148
149 inline
150 std::string
151 toUTF8String(const String& s)
152 {
153 std::string result;
154 s.toUTF8String(result);
155
156 return result;
157 }
158
159 inline
160 bool
161 empty(const String& s)
162 {
163 return s.isEmpty();
164 }
165 }
166
167 namespace std
168 {
169 cxxopts::UnicodeStringIterator
170 begin(const icu::UnicodeString& s)
171 {
172 return cxxopts::UnicodeStringIterator(&s, 0);
173 }
174
175 cxxopts::UnicodeStringIterator
176 end(const icu::UnicodeString& s)
177 {
178 return cxxopts::UnicodeStringIterator(&s, s.length());
179 }
180 }
181
182 //ifdef CXXOPTS_USE_UNICODE
183 #else
184
185 namespace cxxopts
186 {
187 typedef std::string String;
188
189 template <typename T>
190 T
191 toLocalString(T&& t)
192 {
193 return t;
194 }
195
196 inline
197 size_t
198 stringLength(const String& s)
199 {
200 return s.length();
201 }
202
203 inline
204 String&
205 stringAppend(String&s, String a)
206 {
207 return s.append(std::move(a));
208 }
209
210 inline
211 String&
212 stringAppend(String& s, size_t n, char c)
213 {
214 return s.append(n, c);
215 }
216
217 template <typename Iterator>
218 String&
219 stringAppend(String& s, Iterator begin, Iterator end)
220 {
221 return s.append(begin, end);
222 }
223
224 template <typename T>
225 std::string
226 toUTF8String(T&& t)
227 {
228 return std::forward<T>(t);
229 }
230
231 inline
232 bool
233 empty(const std::string& s)
234 {
235 return s.empty();
236 }
237 }
238
239 //ifdef CXXOPTS_USE_UNICODE
240 #endif
241
242 namespace cxxopts
243 {
244 class Value : public std::enable_shared_from_this<Value>
245 {
246 public:
247
248 virtual void
249 parse(const std::string& text) const = 0;
250
251 virtual void
252 parse() const = 0;
253
254 virtual bool
255 has_arg() const = 0;
256
257 virtual bool
258 has_default() const = 0;
259
260 virtual bool
261 is_container() const = 0;
262
263 virtual bool
264 has_implicit() const = 0;
265
266 virtual std::string
267 get_default_value() const = 0;
268
269 virtual std::string
270 get_implicit_value() const = 0;
271
272 virtual std::shared_ptr<Value>
273 default_value(const std::string& value) = 0;
274
275 virtual std::shared_ptr<Value>
276 implicit_value(const std::string& value) = 0;
277 };
278
279 class OptionException : public std::exception
280 {
281 public:
282 OptionException(const std::string& message)
283 : m_message(message)
284 {
285 }
286
287 virtual const char*
288 what() const noexcept
289 {
290 return m_message.c_str();
291 }
292
293 private:
294 std::string m_message;
295 };
296
297 class OptionSpecException : public OptionException
298 {
299 public:
300
301 OptionSpecException(const std::string& message)
302 : OptionException(message)
303 {
304 }
305 };
306
307 class OptionParseException : public OptionException
308 {
309 public:
310 OptionParseException(const std::string& message)
311 : OptionException(message)
312 {
313 }
314 };
315
316 class option_exists_error : public OptionSpecException
317 {
318 public:
319 option_exists_error(const std::string& option)
320 : OptionSpecException(u8"Option ‘" + option + u8"’ already exists")
321 {
322 }
323 };
324
325 class invalid_option_format_error : public OptionSpecException
326 {
327 public:
328 invalid_option_format_error(const std::string& format)
329 : OptionSpecException(u8"Invalid option format ‘" + format + u8"’")
330 {
331 }
332 };
333
334 class option_not_exists_exception : public OptionParseException
335 {
336 public:
337 option_not_exists_exception(const std::string& option)
338 : OptionParseException(u8"Option ‘" + option + u8"’ does not exist")
339 {
340 }
341 };
342
343 class missing_argument_exception : public OptionParseException
344 {
345 public:
346 missing_argument_exception(const std::string& option)
347 : OptionParseException(u8"Option ‘" + option + u8"’ is missing an argument")
348 {
349 }
350 };
351
352 class option_requires_argument_exception : public OptionParseException
353 {
354 public:
355 option_requires_argument_exception(const std::string& option)
356 : OptionParseException(u8"Option ‘" + option + u8"’ requires an argument")
357 {
358 }
359 };
360
361 class option_not_has_argument_exception : public OptionParseException
362 {
363 public:
364 option_not_has_argument_exception
365 (
366 const std::string& option,
367 const std::string& arg
368 )
369 : OptionParseException(
370 u8"Option ‘" + option + u8"’ does not take an argument, but argument‘"
371 + arg + "’ given")
372 {
373 }
374 };
375
376 class option_not_present_exception : public OptionParseException
377 {
378 public:
379 option_not_present_exception(const std::string& option)
380 : OptionParseException(u8"Option ‘" + option + u8"’ not present")
381 {
382 }
383 };
384
385 class argument_incorrect_type : public OptionParseException
386 {
387 public:
388 argument_incorrect_type
389 (
390 const std::string& arg
391 )
392 : OptionParseException(
393 u8"Argument ‘" + arg + u8"’ failed to parse"
394 )
395 {
396 }
397 };
398
399 namespace values
400 {
401 template <typename T>
402 void
403 parse_value(const std::string& text, T& value)
404 {
405 std::istringstream is(text);
406 if (!(is >> value))
407 {
408 throw argument_incorrect_type(text);
409 }
410
411 if (is.rdbuf()->in_avail() != 0)
412 {
413 throw argument_incorrect_type(text);
414 }
415 }
416
417 inline
418 void
419 parse_value(const std::string& /*text*/, bool& value)
420 {
421 //TODO recognise on, off, yes, no, enable, disable
422 //so that we can write --long=yes explicitly
423 value = true;
424 }
425
426 inline
427 void
428 parse_value(const std::string& text, std::string& value)
429 {
430 value = text;
431 }
432
433 template <typename T>
434 void
435 parse_value(const std::string& text, std::vector<T>& value)
436 {
437 T v;
438 parse_value(text, v);
439 value.push_back(v);
440 }
441
442 template <typename T>
443 struct value_has_arg
444 {
445 static constexpr bool value = true;
446 };
447
448 template <>
449 struct value_has_arg<bool>
450 {
451 static constexpr bool value = false;
452 };
453
454 template <typename T>
455 struct type_is_container
456 {
457 static constexpr bool value = false;
458 };
459
460 template <typename T>
461 struct type_is_container<std::vector<T>>
462 {
463 static constexpr bool value = true;
464 };
465
466 template <typename T>
467 class standard_value : public Value
468 {
469 public:
470 standard_value()
471 : m_result(std::make_shared<T>())
472 , m_store(m_result.get())
473 {
474 }
475
476 standard_value(T* t)
477 : m_store(t)
478 {
479 }
480
481 void
482 parse(const std::string& text) const
483 {
484 parse_value(text, *m_store);
485 }
486
487 bool
488 is_container() const
489 {
490 return type_is_container<T>::value;
491 }
492
493 void
494 parse() const
495 {
496 parse_value(m_default_value, *m_store);
497 }
498
499 bool
500 has_arg() const
501 {
502 return value_has_arg<T>::value;
503 }
504
505 bool
506 has_default() const
507 {
508 return m_default;
509 }
510
511 bool
512 has_implicit() const
513 {
514 return m_implicit;
515 }
516
517 virtual std::shared_ptr<Value>
518 default_value(const std::string& value){
519 m_default = true;
520 m_default_value = value;
521 return shared_from_this();
522 }
523
524 virtual std::shared_ptr<Value>
525 implicit_value(const std::string& value){
526 m_implicit = true;
527 m_implicit_value = value;
528 return shared_from_this();
529 }
530
531 std::string
532 get_default_value() const
533 {
534 return m_default_value;
535 }
536
537 std::string
538 get_implicit_value() const
539 {
540 return m_implicit_value;
541 }
542
543 const T&
544 get() const
545 {
546 if (m_store == nullptr)
547 {
548 return *m_result;
549 }
550 else
551 {
552 return *m_store;
553 }
554 }
555
556 protected:
557 std::shared_ptr<T> m_result;
558 T* m_store;
559 bool m_default = false;
560 std::string m_default_value;
561 bool m_implicit = false;
562 std::string m_implicit_value;
563 };
564 }
565
566 template <typename T>
567 std::shared_ptr<Value>
568 value()
569 {
570 return std::make_shared<values::standard_value<T>>();
571 }
572
573 template <typename T>
574 std::shared_ptr<Value>
575 value(T& t)
576 {
577 return std::make_shared<values::standard_value<T>>(&t);
578 }
579
580 class OptionAdder;
581
582 class OptionDetails
583 {
584 public:
585 OptionDetails
586 (
587 const String& description,
588 std::shared_ptr<const Value> value
589 )
590 : m_desc(description)
591 , m_value(value)
592 , m_count(0)
593 {
594 }
595
596 const String&
597 description() const
598 {
599 return m_desc;
600 }
601
602 bool
603 has_arg() const
604 {
605 return m_value->has_arg();
606 }
607
608 void
609 parse(const std::string& text)
610 {
611 m_value->parse(text);
612 ++m_count;
613 }
614
615 void
616 parse_default()
617 {
618 m_value->parse();
619 }
620
621 int
622 count() const
623 {
624 return m_count;
625 }
626
627 const Value& value() const {
628 return *m_value;
629 }
630
631 template <typename T>
632 const T&
633 as() const
634 {
635 #ifdef CXXOPTS_NO_RTTI
636 return static_cast<const values::standard_value<T>&>(*m_value).get();
637 #else
638 return dynamic_cast<const values::standard_value<T>&>(*m_value).get();
639 #endif
640 }
641
642 private:
643 String m_desc;
644 std::shared_ptr<const Value> m_value;
645 int m_count;
646 };
647
648 struct HelpOptionDetails
649 {
650 std::string s;
651 std::string l;
652 String desc;
653 bool has_arg;
654 bool has_default;
655 std::string default_value;
656 bool has_implicit;
657 std::string implicit_value;
658 std::string arg_help;
659 bool is_container;
660 };
661
662 struct HelpGroupDetails
663 {
664 std::string name;
665 std::string description;
666 std::vector<HelpOptionDetails> options;
667 };
668
669 class Options
670 {
671 public:
672
673 Options(std::string program, std::string help_string = "")
674 : m_program(std::move(program))
675 , m_help_string(toLocalString(std::move(help_string)))
676 , m_positional_help("positional parameters")
677 , m_next_positional(m_positional.end())
678 {
679 }
680
681 inline
682 Options&
683 positional_help(const std::string& help_text)
684 {
685 m_positional_help = std::move(help_text);
686 return *this;
687 }
688
689 inline
690 void
691 parse(int& argc, char**& argv);
692
693 inline
694 OptionAdder
695 add_options(std::string group = "");
696
697 inline
698 void
699 add_option
700 (
701 const std::string& group,
702 const std::string& s,
703 const std::string& l,
704 std::string desc,
705 std::shared_ptr<const Value> value,
706 std::string arg_help
707 );
708
709 int
710 count(const std::string& o) const
711 {
712 auto iter = m_options.find(o);
713 if (iter == m_options.end())
714 {
715 return 0;
716 }
717
718 return iter->second->count();
719 }
720
721 const OptionDetails&
722 operator[](const std::string& option) const
723 {
724 auto iter = m_options.find(option);
725
726 if (iter == m_options.end())
727 {
728 throw option_not_present_exception(option);
729 }
730
731 return *iter->second;
732 }
733
734 //parse positional arguments into the given option
735 inline
736 void
737 parse_positional(std::string option);
738
739 inline
740 void
741 parse_positional(std::vector<std::string> options);
742
743 inline
744 std::string
745 help(const std::vector<std::string>& groups = {""}) const;
746
747 inline
748 const std::vector<std::string>
749 groups() const;
750
751 inline
752 const HelpGroupDetails&
753 group_help(const std::string& group) const;
754
755 private:
756
757 inline
758 void
759 add_one_option
760 (
761 const std::string& option,
762 std::shared_ptr<OptionDetails> details
763 );
764
765 inline
766 bool
767 consume_positional(std::string a);
768
769 inline
770 void
771 add_to_option(const std::string& option, const std::string& arg);
772
773 inline
774 void
775 parse_option
776 (
777 std::shared_ptr<OptionDetails> value,
778 const std::string& name,
779 const std::string& arg = ""
780 );
781
782 inline
783 void
784 checked_parse_arg
785 (
786 int argc,
787 char* argv[],
788 int& current,
789 std::shared_ptr<OptionDetails> value,
790 const std::string& name
791 );
792
793 inline
794 String
795 help_one_group(const std::string& group) const;
796
797 inline
798 void
799 generate_group_help(String& result, const std::vector<std::string>& groups) const;
800
801 inline
802 void
803 generate_all_groups_help(String& result) const;
804
805 std::string m_program;
806 String m_help_string;
807 std::string m_positional_help;
808
809 std::map<std::string, std::shared_ptr<OptionDetails>> m_options;
810 std::vector<std::string> m_positional;
811 std::vector<std::string>::iterator m_next_positional;
812 std::unordered_set<std::string> m_positional_set;
813
814 //mapping from groups to help options
815 std::map<std::string, HelpGroupDetails> m_help;
816 };
817
818 class OptionAdder
819 {
820 public:
821
822 OptionAdder(Options& options, std::string group)
823 : m_options(options), m_group(std::move(group))
824 {
825 }
826
827 inline
828 OptionAdder&
829 operator()
830 (
831 const std::string& opts,
832 const std::string& desc,
833 std::shared_ptr<const Value> value
834 = ::cxxopts::value<bool>(),
835 std::string arg_help = ""
836 );
837
838 private:
839 Options& m_options;
840 std::string m_group;
841 };
842
843 }
844
845 namespace cxxopts
846 {
847
848 namespace
849 {
850
851 constexpr int OPTION_LONGEST = 30;
852 constexpr int OPTION_DESC_GAP = 2;
853
854 std::basic_regex<char> option_matcher
855 ("--([[:alnum:]][-_[:alnum:]]+)(=(.*))?|-([[:alnum:]]+)");
856
857 std::basic_regex<char> option_specifier
858 ("(([[:alnum:]]),)?([[:alnum:]][-_[:alnum:]]+)");
859
860 String
861 format_option
862 (
863 const HelpOptionDetails& o
864 )
865 {
866 auto& s = o.s;
867 auto& l = o.l;
868
869 String result = " ";
870
871 if (s.size() > 0)
872 {
873 result += "-" + toLocalString(s) + ",";
874 }
875 else
876 {
877 result += " ";
878 }
879
880 if (l.size() > 0)
881 {
882 result += " --" + toLocalString(l);
883 }
884
885 if (o.has_arg)
886 {
887 auto arg = o.arg_help.size() > 0 ? toLocalString(o.arg_help) : "arg";
888
889 if (o.has_implicit)
890 {
891 result += " [=" + arg + "(=" + toLocalString(o.implicit_value) + ")]";
892 }
893 else
894 {
895 result += " " + arg;
896 }
897 }
898
899 return result;
900 }
901
902 String
903 format_description
904 (
905 const HelpOptionDetails& o,
906 size_t start,
907 size_t width
908 )
909 {
910 auto desc = o.desc;
911
912 if (o.has_default)
913 {
914 desc += toLocalString(" (default: " + o.default_value + ")");
915 }
916
917 String result;
918
919 auto current = std::begin(desc);
920 auto startLine = current;
921 auto lastSpace = current;
922
923 auto size = size_t{};
924
925 while (current != std::end(desc))
926 {
927 if (*current == ' ')
928 {
929 lastSpace = current;
930 }
931
932 if (size > width)
933 {
934 if (lastSpace == startLine)
935 {
936 stringAppend(result, startLine, current + 1);
937 stringAppend(result, "\n");
938 stringAppend(result, start, ' ');
939 startLine = current + 1;
940 lastSpace = startLine;
941 }
942 else
943 {
944 stringAppend(result, startLine, lastSpace);
945 stringAppend(result, "\n");
946 stringAppend(result, start, ' ');
947 startLine = lastSpace + 1;
948 }
949 size = 0;
950 }
951 else
952 {
953 ++size;
954 }
955
956 ++current;
957 }
958
959 //append whatever is left
960 stringAppend(result, startLine, current);
961
962 return result;
963 }
964 }
965
966 OptionAdder
967 Options::add_options(std::string group)
968 {
969 return OptionAdder(*this, std::move(group));
970 }
971
972 OptionAdder&
973 OptionAdder::operator()
974 (
975 const std::string& opts,
976 const std::string& desc,
977 std::shared_ptr<const Value> value,
978 std::string arg_help
979 )
980 {
981 std::match_results<const char*> result;
982 std::regex_match(opts.c_str(), result, option_specifier);
983
984 if (result.empty())
985 {
986 throw invalid_option_format_error(opts);
987 }
988
989 const auto& s = result[2];
990 const auto& l = result[3];
991
992 m_options.add_option(m_group, s.str(), l.str(), desc, value,
993 std::move(arg_help));
994
995 return *this;
996 }
997
998 void
999 Options::parse_option
1000 (
1001 std::shared_ptr<OptionDetails> value,
1002 const std::string& /*name*/,
1003 const std::string& arg
1004 )
1005 {
1006 value->parse(arg);
1007 }
1008
1009 void
1010 Options::checked_parse_arg
1011 (
1012 int argc,
1013 char* argv[],
1014 int& current,
1015 std::shared_ptr<OptionDetails> value,
1016 const std::string& name
1017 )
1018 {
1019 if (current + 1 >= argc)
1020 {
1021 if (value->value().has_implicit())
1022 {
1023 parse_option(value, name, value->value().get_implicit_value());
1024 }
1025 else
1026 {
1027 throw missing_argument_exception(name);
1028 }
1029 }
1030 else
1031 {
1032 if (argv[current + 1][0] == '-' && value->value().has_implicit())
1033 {
1034 parse_option(value, name, value->value().get_implicit_value());
1035 }
1036 else
1037 {
1038 parse_option(value, name, argv[current + 1]);
1039 ++current;
1040 }
1041 }
1042 }
1043
1044 void
1045 Options::add_to_option(const std::string& option, const std::string& arg)
1046 {
1047 auto iter = m_options.find(option);
1048
1049 if (iter == m_options.end())
1050 {
1051 throw option_not_exists_exception(option);
1052 }
1053
1054 parse_option(iter->second, option, arg);
1055 }
1056
1057 bool
1058 Options::consume_positional(std::string a)
1059 {
1060 while (m_next_positional != m_positional.end())
1061 {
1062 auto iter = m_options.find(*m_next_positional);
1063 if (iter != m_options.end())
1064 {
1065 if (!iter->second->value().is_container())
1066 {
1067 if (iter->second->count() == 0)
1068 {
1069 add_to_option(*m_next_positional, a);
1070 ++m_next_positional;
1071 return true;
1072 }
1073 else
1074 {
1075 ++m_next_positional;
1076 continue;
1077 }
1078 }
1079 else
1080 {
1081 add_to_option(*m_next_positional, a);
1082 return true;
1083 }
1084 }
1085 ++m_next_positional;
1086 }
1087
1088 return false;
1089 }
1090
1091 void
1092 Options::parse_positional(std::string option)
1093 {
1094 parse_positional(std::vector<std::string>{option});
1095 }
1096
1097 void
1098 Options::parse_positional(std::vector<std::string> options)
1099 {
1100 m_positional = std::move(options);
1101 m_next_positional = m_positional.begin();
1102
1103 m_positional_set.insert(m_positional.begin(), m_positional.end());
1104 }
1105
1106 void
1107 Options::parse(int& argc, char**& argv)
1108 {
1109 int current = 1;
1110
1111 int nextKeep = 1;
1112
1113 bool consume_remaining = false;
1114
1115 while (current != argc)
1116 {
1117 if (strcmp(argv[current], "--") == 0)
1118 {
1119 consume_remaining = true;
1120 ++current;
1121 break;
1122 }
1123
1124 std::match_results<const char*> result;
1125 std::regex_match(argv[current], result, option_matcher);
1126
1127 if (result.empty())
1128 {
1129 //not a flag
1130
1131 //if true is returned here then it was consumed, otherwise it is
1132 //ignored
1133 if (consume_positional(argv[current]))
1134 {
1135 }
1136 else
1137 {
1138 argv[nextKeep] = argv[current];
1139 ++nextKeep;
1140 }
1141 //if we return from here then it was parsed successfully, so continue
1142 }
1143 else
1144 {
1145 //short or long option?
1146 if (result[4].length() != 0)
1147 {
1148 const std::string& s = result[4];
1149
1150 for (std::size_t i = 0; i != s.size(); ++i)
1151 {
1152 std::string name(1, s[i]);
1153 auto iter = m_options.find(name);
1154
1155 if (iter == m_options.end())
1156 {
1157 throw option_not_exists_exception(name);
1158 }
1159
1160 auto value = iter->second;
1161
1162 //if no argument then just add it
1163 if (!value->has_arg())
1164 {
1165 parse_option(value, name);
1166 }
1167 else
1168 {
1169 //it must be the last argument
1170 if (i + 1 == s.size())
1171 {
1172 checked_parse_arg(argc, argv, current, value, name);
1173 }
1174 else if (value->value().has_implicit())
1175 {
1176 parse_option(value, name, value->value().get_implicit_value());
1177 }
1178 else
1179 {
1180 //error
1181 throw option_requires_argument_exception(name);
1182 }
1183 }
1184 }
1185 }
1186 else if (result[1].length() != 0)
1187 {
1188 const std::string& name = result[1];
1189
1190 auto iter = m_options.find(name);
1191
1192 if (iter == m_options.end())
1193 {
1194 throw option_not_exists_exception(name);
1195 }
1196
1197 auto opt = iter->second;
1198
1199 //equals provided for long option?
1200 if (result[3].length() != 0)
1201 {
1202 //parse the option given
1203
1204 //but if it doesn't take an argument, this is an error
1205 if (!opt->has_arg())
1206 {
1207 throw option_not_has_argument_exception(name, result[3]);
1208 }
1209
1210 parse_option(opt, name, result[3]);
1211 }
1212 else
1213 {
1214 if (opt->has_arg())
1215 {
1216 //parse the next argument
1217 checked_parse_arg(argc, argv, current, opt, name);
1218 }
1219 else
1220 {
1221 //parse with empty argument
1222 parse_option(opt, name);
1223 }
1224 }
1225 }
1226
1227 }
1228
1229 ++current;
1230 }
1231
1232 for (auto& opt : m_options)
1233 {
1234 auto& detail = opt.second;
1235 auto& value = detail->value();
1236
1237 if(!detail->count() && value.has_default()){
1238 detail->parse_default();
1239 }
1240 }
1241
1242 if (consume_remaining)
1243 {
1244 while (current < argc)
1245 {
1246 if (!consume_positional(argv[current])) {
1247 break;
1248 }
1249 ++current;
1250 }
1251
1252 //adjust argv for any that couldn't be swallowed
1253 while (current != argc) {
1254 argv[nextKeep] = argv[current];
1255 ++nextKeep;
1256 ++current;
1257 }
1258 }
1259
1260 argc = nextKeep;
1261
1262 }
1263
1264 void
1265 Options::add_option
1266 (
1267 const std::string& group,
1268 const std::string& s,
1269 const std::string& l,
1270 std::string desc,
1271 std::shared_ptr<const Value> value,
1272 std::string arg_help
1273 )
1274 {
1275 auto stringDesc = toLocalString(std::move(desc));
1276 auto option = std::make_shared<OptionDetails>(stringDesc, value);
1277
1278 if (s.size() > 0)
1279 {
1280 add_one_option(s, option);
1281 }
1282
1283 if (l.size() > 0)
1284 {
1285 add_one_option(l, option);
1286 }
1287
1288 //add the help details
1289 auto& options = m_help[group];
1290
1291 options.options.emplace_back(HelpOptionDetails{s, l, stringDesc,
1292 value->has_arg(),
1293 value->has_default(), value->get_default_value(),
1294 value->has_implicit(), value->get_implicit_value(),
1295 std::move(arg_help),
1296 value->is_container()});
1297 }
1298
1299 void
1300 Options::add_one_option
1301 (
1302 const std::string& option,
1303 std::shared_ptr<OptionDetails> details
1304 )
1305 {
1306 auto in = m_options.emplace(option, details);
1307
1308 if (!in.second)
1309 {
1310 throw option_exists_error(option);
1311 }
1312 }
1313
1314 String
1315 Options::help_one_group(const std::string& g) const
1316 {
1317 typedef std::vector<std::pair<String, String>> OptionHelp;
1318
1319 auto group = m_help.find(g);
1320 if (group == m_help.end())
1321 {
1322 return "";
1323 }
1324
1325 OptionHelp format;
1326
1327 size_t longest = 0;
1328
1329 String result;
1330
1331 if (!g.empty())
1332 {
1333 result += toLocalString(" " + g + " options:\n");
1334 }
1335
1336 for (const auto& o : group->second.options)
1337 {
1338 if (o.is_container && m_positional_set.find(o.l) != m_positional_set.end())
1339 {
1340 continue;
1341 }
1342
1343 auto s = format_option(o);
1344 longest = std::max(longest, stringLength(s));
1345 format.push_back(std::make_pair(s, String()));
1346 }
1347
1348 longest = std::min(longest, static_cast<size_t>(OPTION_LONGEST));
1349
1350 //widest allowed description
1351 auto allowed = size_t{76} - longest - OPTION_DESC_GAP;
1352
1353 auto fiter = format.begin();
1354 for (const auto& o : group->second.options)
1355 {
1356 if (o.is_container && m_positional_set.find(o.l) != m_positional_set.end())
1357 {
1358 continue;
1359 }
1360
1361 auto d = format_description(o, longest + OPTION_DESC_GAP, allowed);
1362
1363 result += fiter->first;
1364 if (stringLength(fiter->first) > longest)
1365 {
1366 result += '\n';
1367 result += toLocalString(std::string(longest + OPTION_DESC_GAP, ' '));
1368 }
1369 else
1370 {
1371 result += toLocalString(std::string(longest + OPTION_DESC_GAP -
1372 stringLength(fiter->first),
1373 ' '));
1374 }
1375 result += d;
1376 result += '\n';
1377
1378 ++fiter;
1379 }
1380
1381 return result;
1382 }
1383
1384 void
1385 Options::generate_group_help(String& result, const std::vector<std::string>& groups) const
1386 {
1387 for (std::size_t i = 0; i < groups.size(); ++i)
1388 {
1389 String const& group_help = help_one_group(groups[i]);
1390 if (empty(group_help)) continue;
1391 result += group_help;
1392 if (i < groups.size() - 1)
1393 {
1394 result += '\n';
1395 }
1396 }
1397 }
1398
1399 void
1400 Options::generate_all_groups_help(String& result) const
1401 {
1402 std::vector<std::string> groups;
1403 groups.reserve(m_help.size());
1404
1405 for (auto& group : m_help)
1406 {
1407 groups.push_back(group.first);
1408 }
1409
1410 generate_group_help(result, groups);
1411 }
1412
1413 std::string
1414 Options::help(const std::vector<std::string>& groups) const
1415 {
1416 String result = m_help_string + "\nUsage:\n " +
1417 toLocalString(m_program) + " [OPTION...]";
1418
1419 if (m_positional.size() > 0) {
1420 result += " " + toLocalString(m_positional_help);
1421 }
1422
1423 result += "\n\n";
1424
1425 if (groups.size() == 0)
1426 {
1427 generate_all_groups_help(result);
1428 }
1429 else
1430 {
1431 generate_group_help(result, groups);
1432 }
1433
1434 return toUTF8String(result);
1435 }
1436
1437 const std::vector<std::string>
1438 Options::groups() const
1439 {
1440 std::vector<std::string> g;
1441
1442 std::transform(
1443 m_help.begin(),
1444 m_help.end(),
1445 std::back_inserter(g),
1446 [] (const std::map<std::string, HelpGroupDetails>::value_type& pair)
1447 {
1448 return pair.first;
1449 }
1450 );
1451
1452 return g;
1453 }
1454
1455 const HelpGroupDetails&
1456 Options::group_help(const std::string& group) const
1457 {
1458 return m_help.at(group);
1459 }
1460
1461 }
1462
1463 #if defined(__GNU__)
1464 #pragma GCC diagnostic pop
1465 #endif
1466
1467 #endif //CXX_OPTS_HPP
+0
-315
src/im.cpp less more
0 /* im.cpp: Handles starting and managing imlib2.
1 *
2 * Copyright (C) 2014: Dalton Nell, Maim Contributors (https://github.com/naelstrof/maim/graphs/contributors).
3 *
4 * This file is part of Maim.
5 *
6 * Maim is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Maim is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with Maim. If not, see <http://www.gnu.org/licenses/>.
18 */
19 #include "im.hpp"
20
21 maim::IMEngine* imengine = new maim::IMEngine();
22
23 maim::IMEngine::IMEngine() {
24 }
25
26 maim::IMEngine::~IMEngine() {
27 }
28
29 /**
30 * @brief Initializes our imlib context
31 *
32 * @return 0 on success, 1 on failure.
33 */
34 int maim::IMEngine::init() {
35 if ( !xengine->m_good ) {
36 return EXIT_FAILURE;
37 }
38 imlib_set_cache_size( 2048 * 1024 );
39 imlib_context_set_display( xengine->m_display );
40 imlib_context_set_visual( xengine->m_visual );
41 imlib_context_set_colormap( xengine->m_colormap );
42 imlib_context_set_blend( 1 );
43 return EXIT_SUCCESS;
44 }
45
46 /**
47 * @brief Takes a screenshot of the given window, and leaves the allocated image in the imlib context.
48 *
49 * @param id A window ID, for example the root id would take a full screenshot.
50 *
51 * @return 0 on success, 1 on failure.
52 */
53 int maim::IMEngine::screenshot( Window id ) {
54 if ( id == None ) {
55 fprintf( stderr, "Error: Can't screenshot a window with id None!\n" );
56 return EXIT_FAILURE;
57 }
58 // Get the window's dimensions
59 Window root;
60 int x, y;
61 unsigned int w, h, b, d;
62 int status = XGetGeometry( xengine->m_display, id, &root, &x, &y, &w, &h, &b, &d );
63 if ( status == 0 ) {
64 fprintf( stderr, "Error: Failed to grab window geometry of window id: %lu\n", id );
65 return EXIT_FAILURE;
66 }
67 // Create an uninitialized image buffer of the same width and height as the window
68 Imlib_Image buffer = imlib_create_image( w, h );
69 imlib_context_set_image( buffer );
70 // Make sure that imlib knows that it's possible for the image to have alpha
71 // prevents blending issues in the future.
72 imlib_image_set_has_alpha( 1 );
73 imlib_context_set_drawable( id );
74 int destinationx = x < 0 ? -x : 0;
75 int destinationy = y < 0 ? -y : 0;
76 imlib_copy_drawable_to_image( 0, destinationx, destinationy, w, h, 0, 0, 0 );
77 // Screenshot image is now in the imlib context!
78 return EXIT_SUCCESS;
79 }
80
81 /**
82 * @brief Similar to maim::IMEngine::screenshot( Window id ), but also crops the image.
83 *
84 * @param id The window to take a screenshot of.
85 * @param x Starting X position of the crop.
86 * @param y Starting Y position of the crop.
87 * @param w Width of the final cropped image.
88 * @param h Height of the final cropped image.
89 *
90 * @return 0 on success, 1 on failure.
91 */
92 int maim::IMEngine::screenshot( Window id, int x, int y, unsigned int w, unsigned int h ) {
93 if ( id == None ) {
94 fprintf( stderr, "Error: Can't screenshot a window with id None!\n" );
95 return EXIT_FAILURE;
96 }
97 // Create an uninitialized image buffer of the same width and height as our selection.
98 Imlib_Image buffer = imlib_create_image( w, h );
99 imlib_context_set_image( buffer );
100 // Make sure that imlib knows that it's possible for the image to have alpha
101 // prevents blending issues in the future.
102 imlib_image_set_has_alpha( 1 );
103 imlib_context_set_drawable( id );
104 // This make sure negative x or y values actually affect the location
105 // of the drawable. Since asking for it to copy from a negative
106 // x or y position doesn't seem to do anything.
107 // Might be a bug, but if it's fixed it'll break my program as it is now. :v
108 int destinationx = x < 0 ? -x : 0;
109 int destinationy = y < 0 ? -y : 0;
110 imlib_copy_drawable_to_image( 0, x, y, w, h, destinationx, destinationy, 0 );
111 // Screenshot image is now in the imlib context!
112 return EXIT_SUCCESS;
113 }
114
115 /**
116 * @brief Blends the system cursor onto the current image in the imlib context.
117 *
118 * @param id The window used to take the screenshot, this is used to grab the relative cursor position.
119 * @param x X offset of the window to the image
120 * @param y Y offset of the window to the image
121 *
122 * @return 0 on success, 1 on failure.
123 */
124 int maim::IMEngine::blendCursor( Window id, int x, int y ) {
125 if ( id == None ) {
126 fprintf( stderr, "Error: Can't blend the cursor without a valid window (None given)!\n" );
127 return EXIT_FAILURE;
128 }
129 if ( imlib_context_get_image() == NULL ) {
130 fprintf( stderr, "Error: Can't blend the cursor to NULL image!\n" );
131 return EXIT_FAILURE;
132 }
133 // Grab the cursor image with XFixes
134 XFixesCursorImage* xcursor = XFixesGetCursorImage( xengine->m_display );
135 // If we failed don't do anything.
136 if ( !xcursor ) {
137 fprintf( stderr, "Warning: Failed to grab cursor image, it won't appear in screenshots!\n" );
138 return EXIT_FAILURE;
139 }
140 // For whatever reason, XFixes returns 32 bit ARGB colors with 64 bit longs?
141 // I'm guessing this is because some old AMD cpu's longs are actually 32 bits.
142 // Regardless this is how I convert it to the correct bit length.
143 uint32_t* pixels = new uint32_t[ xcursor->width * xcursor->height ];
144 for ( int i=0;i<xcursor->width*xcursor->height;i++ ) {
145 pixels[ i ] = (uint32_t)xcursor->pixels[ i ];
146 }
147 Imlib_Image cursor = imlib_create_image_using_data( xcursor->width, xcursor->height, pixels );
148 // First save the image that we'll be applying the cursor to.
149 Imlib_Image buffer = imlib_context_get_image();
150 // Make sure imlib knows that it has alpha
151 imlib_context_set_image( cursor );
152 imlib_image_set_has_alpha( 1 );
153 // Then we quickly set the old image back.
154 imlib_context_set_image( buffer );
155 // We grab the window's position with this, so we can find where the cursor would be located on our image.
156 Window root, junk;
157 int tx, ty;
158 unsigned int tw, th, tb, td;
159 int status = XGetGeometry( xengine->m_display, id, &root, &tx, &ty, &tw, &th, &tb, &td );
160 if ( status == 0 ) {
161 fprintf( stderr, "Error: Failed to grab window geometry of window id: %lu\n", id );
162 return EXIT_FAILURE;
163 }
164 // Make sure the window's position is in root coordinates
165 XTranslateCoordinates( xengine->m_display, id, root, -tb, -tb, &tx, &ty, &junk );
166 // Finally blend the cursor to the screenshot, we don't have to worry about the cursor not being visible as it would be a non-existant image if it was.
167 imlib_blend_image_onto_image( cursor, 0, 0, 0, xcursor->width, xcursor->height, xcursor->x-tx-xcursor->xhot-x, xcursor->y-ty-xcursor->yhot-y, xcursor->width, xcursor->height );
168 // Free the cursor image and delete its data.
169 imlib_context_set_image( cursor );
170 imlib_free_image();
171 imlib_context_set_image( buffer );
172 free( xcursor );
173 delete[] pixels;
174 return EXIT_SUCCESS;
175 }
176
177 /**
178 * @brief This one is a doozy, it tries to mask off-screen pixels so they don't show up as garbage in screenshots.
179 * It's highly situational in it's usage, and often yields zero noticable results as most people not only
180 * don't have a multi-monitor setup, but the few people that do don't have them unevenly set up.
181 *
182 * @param x X offset of the image in buffer in relation to the physical monitors.
183 * @param y Y offset of the image in buffer in relation to the physical monitors.
184 * @param w Width of the image in buffer.
185 * @param h Height of the image in buffer.
186 *
187 * @return 0 on success, 1 on failure.
188 */
189 int maim::IMEngine::mask( int x, int y, unsigned int w, unsigned int h ) {
190 // If xengine couldn't find any physical screens. We don't do anything.
191 if ( !xengine->m_res ) {
192 return EXIT_FAILURE;
193 }
194 if ( imlib_context_get_image() == NULL ) {
195 fprintf( stderr, "Error: Can't mask a NULL image!\n" );
196 return EXIT_FAILURE;
197 }
198 // If no width or height arguments were given, we grab them ourselves.
199 if ( w == 0 && h == 0 && x == 0 && y == 0 ) {
200 w = imlib_image_get_width();
201 h = imlib_image_get_height();
202 } else if ( w == 0 || h == 0 ) {
203 fprintf( stderr, "Error: Tried to mask an image with 0 width or height!\n" );
204 return EXIT_FAILURE;
205 }
206 // So first we generate an image of the same exact size filled with the color 0,0,0,0
207 Imlib_Image mask = imlib_create_image( w, h );
208 // Save our original image.
209 Imlib_Image buffer = imlib_context_get_image();
210 imlib_context_set_image( mask );
211 imlib_image_set_has_alpha( 1 );
212 imlib_context_set_color( 0, 0, 0, 0 );
213 imlib_image_fill_rectangle( 0, 0, w, h );
214 // Grab our monitor information, (basically get pixel rectangles that are actually displaying).
215 std::vector<XRRCrtcInfo*> monitors = xengine->getCRTCS();
216 imlib_context_set_color( 0, 0, 0, 255 );
217 for ( unsigned int i=0;i<monitors.size();i++ ) {
218 XRRCrtcInfo* cmonitor = monitors[ i ];
219 // Then quickly block in our visible pixels on our mask
220 imlib_image_fill_rectangle( cmonitor->x - x, cmonitor->y - y, cmonitor->width, cmonitor->height );
221 }
222 xengine->freeCRTCS( monitors );
223 imlib_context_set_color( 255, 255, 255, 255 );
224 imlib_context_set_image( buffer );
225 // Then finally apply our mask to the original image, which should remove any garbage pixels that are off-screen.
226 imlib_image_copy_alpha_to_image( mask, 0, 0 );
227 imlib_context_set_image( mask );
228 imlib_free_image();
229 imlib_context_set_image( buffer );
230 // Then finally apply our mask to the original image, which should remove any garbage pixels that are off-screen.
231 // But unfortunately that doesn't actually delete the pixels, so we have to do one more pass.
232 // This whole thing just creates another blank image, and blends the masked image onto it.
233 // This is because all we did was copy the alpha channel, so formats like jpg wouldn't even care that we did all this
234 // work unless I add this extra pass.
235 Imlib_Image finalimage = imlib_create_image( w, h );
236 imlib_context_set_image( finalimage );
237 imlib_image_set_has_alpha( 1 );
238 imlib_context_set_color( 0, 0, 0, 0 );
239 imlib_image_fill_rectangle( 0, 0, w, h );
240 imlib_context_set_color( 255, 255, 255, 255 );
241 imlib_blend_image_onto_image( buffer, 1, 0, 0, w, h, 0, 0, w, h );
242 imlib_context_set_image( buffer );
243 imlib_free_image();
244 imlib_context_set_image( finalimage );
245 // Our final image is in the imlib context!
246 return EXIT_SUCCESS;
247 }
248
249 /**
250 * @brief Simply saves the image in buffer to the given file, then frees the image from memory. Imlib handles formating (.png, .gif, etc).
251 *
252 * @param file The file path to save.
253 *
254 * @return 0 on success, 1 on failure.
255 */
256 int maim::IMEngine::save( std::string file ) {
257 Imlib_Load_Error err;
258 imlib_save_image_with_error_return( file.c_str(), &err );
259 if ( err == IMLIB_LOAD_ERROR_NONE ) {
260 imlib_free_image();
261 return EXIT_SUCCESS;
262 }
263 fprintf( stderr, "Failed to save image %s: ", file.c_str() );
264 switch( err ) {
265 case IMLIB_LOAD_ERROR_UNKNOWN:
266 default: {
267 fprintf( stderr, "unknown error %d\n", (int)err );
268 break;
269 }
270 case IMLIB_LOAD_ERROR_OUT_OF_FILE_DESCRIPTORS:
271 fprintf( stderr, "out of file descriptors\n" );
272 break;
273 case IMLIB_LOAD_ERROR_OUT_OF_MEMORY:
274 fprintf( stderr, "out of memory\n" );
275 break;
276 case IMLIB_LOAD_ERROR_TOO_MANY_SYMBOLIC_LINKS:
277 fprintf( stderr, "path contains too many symbolic links\n" );
278 break;
279 case IMLIB_LOAD_ERROR_PATH_POINTS_OUTSIDE_ADDRESS_SPACE:
280 fprintf( stderr, "path points outside address space\n" );
281 break;
282 case IMLIB_LOAD_ERROR_PATH_COMPONENT_NOT_DIRECTORY:
283 fprintf( stderr, "path component is not a directory\n" );
284 break;
285 case IMLIB_LOAD_ERROR_PATH_COMPONENT_NON_EXISTANT:
286 fprintf( stderr, "path component is non-existant (~ isn't expanded inside quotes!)\n" );
287 break;
288 case IMLIB_LOAD_ERROR_PATH_TOO_LONG:
289 fprintf( stderr, "path is too long\n" );
290 break;
291 case IMLIB_LOAD_ERROR_NO_LOADER_FOR_FILE_FORMAT:
292 fprintf( stderr, "no loader for file format (unsupported format)\n" );
293 break;
294 case IMLIB_LOAD_ERROR_OUT_OF_DISK_SPACE: {
295 fprintf( stderr, "not enough disk space\n" );
296 break;
297 }
298 case IMLIB_LOAD_ERROR_FILE_DOES_NOT_EXIST: {
299 fprintf( stderr, "file does not exist\n" );
300 break;
301 }
302 case IMLIB_LOAD_ERROR_FILE_IS_DIRECTORY: {
303 fprintf( stderr, "file is a directory\n" );
304 break;
305 }
306 case IMLIB_LOAD_ERROR_PERMISSION_DENIED_TO_WRITE:
307 case IMLIB_LOAD_ERROR_PERMISSION_DENIED_TO_READ: {
308 fprintf( stderr, "permission denied\n" );
309 break;
310 }
311 }
312 imlib_free_image();
313 return EXIT_FAILURE;
314 }
+0
-52
src/im.hpp less more
0 /* im.hpp: Handles starting and managing imlib2.
1 *
2 * Copyright (C) 2014: Dalton Nell, Maim Contributors (https://github.com/naelstrof/maim/graphs/contributors).
3 *
4 * This file is part of Maim.
5 *
6 * Maim is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Maim is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with Maim. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #ifndef MAIM_IM_H_
21 #define MAIM_IM_H_
22
23 #include <Imlib2.h>
24 #include <X11/extensions/Xfixes.h>
25 #include <X11/extensions/Xrandr.h>
26 #include <vector>
27 #include <stdlib.h>
28 #include <stdint.h>
29
30 #include "x.hpp"
31
32 namespace maim {
33
34 class IMEngine {
35 public:
36 IMEngine();
37 ~IMEngine();
38 int init();
39 int screenshot( Window id, int x, int y, unsigned int w, unsigned int h );
40 int screenshot( Window id );
41 int blendCursor( Window id, int x = 0, int y = 0 );
42 int mask( int x = 0, int y = 0, unsigned int w = 0, unsigned int h = 0 );
43 int save( std::string filename );
44 private:
45 };
46
47 }
48
49 extern maim::IMEngine* imengine;
50
51 #endif // MAIM_IM_H_
0 #include "image.hpp"
1
2 ARGBImage::~ARGBImage() {
3 delete[] data;
4 }
5
6 ARGBImage::ARGBImage( XImage* image, glm::ivec2 iloc, glm::ivec4 selectionrect, int channels, X11* x11 ) {
7 this->imagex = iloc.x;
8 this->imagey = iloc.y;
9 this->channels = channels;
10 glm::ivec2 spos = glm::ivec2( selectionrect.x, selectionrect.y );
11 offset = spos-iloc;
12 long long int alpha_mask = ~(image->red_mask|image->green_mask|image->blue_mask);
13 long long int roffset = get_shift(image->red_mask);
14 long long int goffset = get_shift(image->green_mask);
15 long long int boffset = get_shift(image->blue_mask);
16 long long int aoffset = get_shift(alpha_mask);
17 width = selectionrect.z;
18 height = selectionrect.w;
19 data = new unsigned char[width*height*channels];
20 // Clear necessary stuff
21 // Top rect
22 for ( int y = 0; y < glm::min(-offset.y,(int)height);y++ ) {
23 for ( int x = 0; x < width;x++ ) {
24 for ( int c = 0; c < channels;c++ ) {
25 data[(y*width+x)*channels+c] = 0;
26 }
27 }
28 }
29 // Left rect
30 for ( int y = 0; y < height;y++ ) {
31 for ( int x = 0; x < glm::min(-offset.x,(int)width);x++ ) {
32 for ( int c = 0; c < channels;c++ ) {
33 data[(y*width+x)*channels+c] = 0;
34 }
35 }
36 }
37 // Bot rect
38 for ( int y=-offset.y+image->height; y<height; y++ ) {
39 for ( int x = 0; x < width;x++ ) {
40 for ( int c = 0; c < channels;c++ ) {
41 data[(y*width+x)*channels+c] = 0;
42 }
43 }
44 }
45 // Right rect
46 for ( int y = 0; y < height;y++ ) {
47 for ( int x = -offset.x+image->width; x<width; x++ ) {
48 for ( int c = 0; c < channels;c++ ) {
49 data[(y*width+x)*channels+c] = 0;
50 }
51 }
52 }
53
54 // Find the intersection of the rectangles.
55 int maxx = glm::max( offset.x, 0 );
56 int maxy = glm::max( offset.y, 0 );
57 int minw = glm::min( (int)(offset.x+width), image->width);
58 int minh = glm::min( (int)(offset.y+height), image->height );
59
60 // Loop only through the intersecting parts, copying everything.
61 // Also check if we have any useful alpha data.
62 switch( channels ) {
63 case 4:
64 if ( aoffset >= image->depth ) {
65 for(int y = maxy; y < minh; y++) {
66 for(int x = maxx; x < minw; x++) {
67 // This is where we just have RGB but require an RGBA image.
68 computeRGBAPixel( data, image, x, y, roffset, goffset, boffset, width, offset );
69 }
70 }
71 } else {
72 for(int y = maxy; y < minh; y++) {
73 for(int x = maxx; x < minw; x++) {
74 computeRGBAPixel( data, image, x, y, roffset, goffset, boffset, aoffset, width, offset );
75 }
76 }
77 }
78 break;
79 case 3:
80 for(int y = maxy; y < minh; y++) {
81 for(int x = maxx; x < minw; x++) {
82 computeRGBPixel( data, image, x, y, roffset, goffset, boffset, width, offset );
83 }
84 }
85 break;
86 default:
87 throw new std::invalid_argument("Invalid number of channels provided to image.");
88 }
89 }
90
91 void png_write_ostream(png_structp png_ptr, png_bytep data, png_size_t length)
92 {
93 std::ostream *stream = (std::ostream*)png_get_io_ptr(png_ptr); //Get pointer to ostream
94 stream->write((char*)data,length); //Write requested amount of data
95 }
96
97 void png_flush_ostream(png_structp png_ptr)
98 {
99 std::ostream *stream = (std::ostream*)png_get_io_ptr(png_ptr); //Get pointer to ostream
100 stream->flush();
101 }
102
103 void user_error_fn(png_structp png_ptr, png_const_charp error_msg)
104 {
105 throw new std::runtime_error(error_msg);
106 }
107
108 void user_warning_fn(png_structp png_ptr, png_const_charp warning_msg)
109 {
110 std::cerr << warning_msg << "\n";
111 }
112
113 void ARGBImage::writePNG( std::ostream& streamout, int quality ) {
114 if ( quality > 10 || quality < 1 ) {
115 throw new std::invalid_argument("Quality argument must be between 1 and 10");
116 }
117 png_structp png = NULL;
118 png_infop info = NULL;
119 png_bytep *rows = new png_bytep[height];
120
121 png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
122 if(!png) throw new std::runtime_error( "Failed to write png image" );
123 info = png_create_info_struct(png);
124 if(!info) throw new std::runtime_error( "Failed to write png image" );
125 png_set_error_fn(png, png_get_error_ptr(png), user_error_fn, user_warning_fn);
126 png_set_write_fn(png, &streamout, png_write_ostream, png_flush_ostream);
127 png_set_compression_level(png, quality-1);
128 if ( channels == 4 ) {
129 png_set_IHDR(png, info, width, height,
130 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
131 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
132 } else {
133 png_set_IHDR(png, info, width, height,
134 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
135 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
136 }
137 for( int i=0;i<height;i++ ) {
138 rows[i] = (png_bytep)(data+(i*width*4));
139 }
140 png_write_info(png, info);
141 png_write_image(png, rows);
142 png_write_end(png, info);
143 png_destroy_write_struct(&png, &info);
144 }
145
146 void init_buffer(jpeg_compress_struct* cinfo) {}
147
148 /* what to do when the buffer is full; this should almost never
149 * happen since we allocated our buffer to be big to start with
150 */
151 boolean empty_buffer(jpeg_compress_struct* cinfo) {
152 return TRUE;
153 }
154
155 /* finalize the buffer and do any cleanup stuff */
156 void term_buffer(jpeg_compress_struct* cinfo) {}
157
158 void ARGBImage::writeJPEG( std::ostream& streamout, int quality ) {
159 if ( channels != 3 ) {
160 throw new std::runtime_error( "JPEG tried to save image with more than 3 channels." );
161 }
162 struct jpeg_compress_struct cinfo;
163 struct jpeg_error_mgr jerr;
164 struct jpeg_destination_mgr dmgr;
165
166 /* create our in-memory output buffer to hold the jpeg */
167 JOCTET * out_buffer = new JOCTET[width * height *3];
168
169 /* here is the magic */
170 dmgr.init_destination = init_buffer;
171 dmgr.empty_output_buffer = empty_buffer;
172 dmgr.term_destination = term_buffer;
173 dmgr.next_output_byte = out_buffer;
174 dmgr.free_in_buffer = width * height *3;
175
176 cinfo.err = jpeg_std_error(&jerr);
177 jpeg_create_compress(&cinfo);
178
179 /* make sure we tell it about our manager */
180 cinfo.dest = &dmgr;
181
182 cinfo.image_width = width;
183 cinfo.image_height = height;
184 cinfo.input_components = 3;
185 cinfo.in_color_space = JCS_RGB;
186
187 jpeg_set_defaults(&cinfo);
188 // Convert quality from scale 1-10 to 0-100
189 jpeg_set_quality (&cinfo, (int)((float)quality-1.f)*(100.f/9.f), true);
190 jpeg_start_compress(&cinfo, true);
191
192 JSAMPROW row_pointer;
193 unsigned char* buffer = (unsigned char*)data;
194
195 /* main code to write jpeg data */
196 while (cinfo.next_scanline < cinfo.image_height) {
197 row_pointer = (JSAMPROW) &buffer[cinfo.next_scanline * 3*width];
198 jpeg_write_scanlines(&cinfo, &row_pointer, 1);
199 }
200 jpeg_finish_compress(&cinfo);
201
202 streamout.write( (const char*)out_buffer, cinfo.dest->next_output_byte - out_buffer );
203 delete[] out_buffer;
204 }
205
206 bool ARGBImage::intersect( XRRCrtcInfo* a, glm::vec4 b ) {
207 if (a->x < b.x + b.z &&
208 a->x + a->width > b.x &&
209 a->y < b.y + b.w &&
210 a->height + a->y > b.y) {
211 return true;
212 }
213 return false;
214 }
215
216 bool ARGBImage::containsCompletely( XRRCrtcInfo* a, glm::vec4 b ) {
217 if ( b.x >= a->x && b.y >= a->y && b.x+b.z <= a->x+a->width && b.y+b.w <= a->y+a->height ) {
218 return true;
219 }
220 return false;
221 }
222
223 void ARGBImage::mask(X11* x11) {
224 if ( !x11->haveXRR ) {
225 return;
226 }
227 std::vector<XRRCrtcInfo*> physicalMonitors = x11->getCRTCS();
228 // Make sure a masking needs to happen, it's not a perfect detection,
229 // but will detect most situations where a masking actually needs to happen.
230 for ( int i=0;i<physicalMonitors.size();i++ ) {
231 XRRCrtcInfo* m = physicalMonitors[i];
232 if ( intersect(m, glm::vec4(imagex, imagey, width, height ) ) ) {
233 if ( containsCompletely(m, glm::vec4( imagex, imagey, width, height ) ) ) {
234 x11->freeCRTCS(physicalMonitors);
235 return;
236 }
237 }
238 }
239 unsigned char* copy = new unsigned char[width*height*channels];
240 // Zero out our copy
241 for ( int y = 0; y < height;y++ ) {
242 for ( int x = 0; x < width;x++ ) {
243 for ( int c = 0; c < channels;c++ ) {
244 copy[(y*width+x)*channels+c] = 0;
245 }
246 }
247 }
248 for ( int i=0;i<physicalMonitors.size();i++ ) {
249 // Make sure we're intersecting
250 XRRCrtcInfo* m = physicalMonitors[i];
251 if ( !intersect(m, glm::vec4(imagex, imagey, width, height ) ) ) {
252 continue;
253 }
254 // Copy over data within the intersecting areas.
255 for ( int y = glm::max(0,m->y-imagey); y<glm::min(height,m->y+m->height-imagey);y++ ) {
256 for ( int x = glm::max(0,m->x-imagex); x < glm::min(width,m->x+m->width-imagex);x++ ) {
257 for ( int c = 0; c < channels;c++ ) {
258 copy[(y*width+x)*channels+c] = data[(y*width+x)*channels+c];
259 }
260 }
261 }
262 }
263 x11->freeCRTCS(physicalMonitors);
264 delete[] data;
265 data = copy;
266 }
267
268 void ARGBImage::blendCursor( X11* x11 ) {
269 if ( !x11->haveXFixes ) {
270 return;
271 }
272 XFixesCursorImage* xcursor = XFixesGetCursorImage( x11->display );
273 if ( !xcursor ) {
274 return;
275 }
276 // 64bit -> 32bit conversion
277 unsigned char pixels[xcursor->width * xcursor->height * 4];
278 for ( unsigned int i=0;i<xcursor->width*xcursor->height;i++ ) {
279 ((unsigned int*)pixels)[ i ] = (unsigned int)xcursor->pixels[ i ];
280 }
281 xcursor->y -= xcursor->yhot + offset.x;
282 xcursor->x -= xcursor->xhot + offset.y;
283 for ( int y = glm::max(0,xcursor->y-imagey); y<glm::min((int)height,xcursor->y+xcursor->height-imagey);y++ ) {
284 for ( int x = glm::max(0,xcursor->x-imagex); x < glm::min((int)width,xcursor->x+xcursor->width-imagex);x++ ) {
285 int cx = x-(xcursor->x-imagex);
286 int cy = y-(xcursor->y-imagey);
287 float alpha = (float)pixels[(cy*xcursor->width+cx)*4+3]/255.f;
288 data[(y*width+x)*channels] = data[(y*width+x)*channels]*(1-alpha) + pixels[(cy*xcursor->width+cx)*4+2]*alpha;
289 data[(y*width+x)*channels+1] = data[(y*width+x)*channels+1]*(1-alpha) + pixels[(cy*xcursor->width+cx)*4+1]*alpha;
290 data[(y*width+x)*channels+2] = data[(y*width+x)*channels+2]*(1-alpha) + pixels[(cy*xcursor->width+cx)*4]*alpha;
291 // If the original image has alpha, we need to override it.
292 if ( channels == 4 ) {
293 data[(y*width+x)*channels+3] = glm::min(data[(y*width+x)*channels+3]+pixels[(cy*xcursor->width+cx)*4+3],255);
294 }
295 }
296 }
297 }
0 /* image.hpp: image helper
1 *
2 * Copyright (C) 2014: Dalton Nell, Maim Contributors (https://github.com/naelstrof/slop/graphs/contributors).
3 *
4 * This file is part of Maim.
5 *
6 * Maim is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Maim is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with Maim. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #ifndef N_IMAGE_H_
21 #define N_IMAGE_H_
22
23 #include <iostream>
24 #include <png.h>
25 #include <jpeglib.h>
26 #include <X11/Xlib.h>
27 #include <X11/Xutil.h>
28 #include <stdexcept>
29 #include <glm/glm.hpp>
30
31 #include "x.hpp"
32
33 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 ) {
34 int curpixel = ((y-offset.y)*width+((x-offset.x)))*3;
35 unsigned int real = XGetPixel(image, x, y);
36 data[curpixel] = (unsigned char)((real & image->red_mask) >> roffset);
37 data[curpixel+1] = (unsigned char)((real & image->green_mask) >> goffset);
38 data[curpixel+2] = (unsigned char)((real & image->blue_mask) >> boffset);
39 }
40
41 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 ) {
42 int curpixel = ((y-offset.y)*width+(x-offset.x))*4;
43 //unsigned int real = ((unsigned int*)image->data)[curpixel/4];
44 unsigned int real = XGetPixel(image, x, y);
45 data[curpixel] = (unsigned char)((real & image->red_mask) >> roffset);
46 data[curpixel+1] = (unsigned char)((real & image->green_mask) >> goffset);
47 data[curpixel+2] = (unsigned char)((real & image->blue_mask) >> boffset);
48 data[curpixel+3] = (unsigned char)(real >> aoffset);
49 }
50
51 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 ) {
52 int curpixel = ((y-offset.y)*width+((x-offset.x)))*4;
53 //unsigned int real = ((unsigned int*)image->data)[curpixel/4];
54 unsigned int real = XGetPixel(image, x, y);
55 data[curpixel] = (unsigned char)((real & image->red_mask) >> roffset);
56 data[curpixel+1] = (unsigned char)((real & image->green_mask) >> goffset);
57 data[curpixel+2] = (unsigned char)((real & image->blue_mask) >> boffset);
58 data[curpixel+3] = 255;
59 }
60
61 static inline int get_shift (int mask) {
62 int shift = 0;
63 while (mask) {
64 if (mask & 1) { break; }
65 shift++;
66 mask >>= 1;
67 }
68 return shift;
69 }
70
71 class ARGBImage {
72 private:
73 unsigned char* data;
74 unsigned int width;
75 unsigned int height;
76 unsigned int channels;
77 int imagex, imagey;
78 glm::ivec2 offset;
79 bool intersect( XRRCrtcInfo* a, glm::vec4 b );
80 bool containsCompletely( XRRCrtcInfo* a, glm::vec4 b );
81 public:
82 void blendCursor( X11* x11 );
83 void mask(X11* x11);
84 ARGBImage( XImage* image, glm::ivec2 imageloc, glm::ivec4 selectionrect, int channels, X11* x11 );
85 ~ARGBImage();
86 void writePNG( std::ostream& streamout, int quality );
87 void writeJPEG( std::ostream& streamout, int quality );
88 };
89
90 #endif
0 /* main.cpp
1 *
2 * Copyright (C) 2014: Dalton Nell, Maim Contributors (https://github.com/naelstrof/maim/graphs/contributors).
3 *
4 * This file is part of Maim.
5 *
6 * Maim is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Maim is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with Maim. If not, see <http://www.gnu.org/licenses/>.
18 */
19 #include <cstdio>
20 #include <cstdlib>
21 #include <cstring>
22 #include <cerrno>
23 #include <stdio.h>
24 #include <unistd.h>
25 #include <sys/types.h>
26 #include <sys/param.h>
27 #include <pwd.h>
28 #include <string>
0 #include <iostream>
1 #include <slop.hpp>
2 #include <glm/glm.hpp>
3 #include <fstream>
294 #include <sstream>
30 #include <time.h>
31
5 #include <thread>
6 #include <X11/extensions/shape.h>
7
8 #include "cxxopts.hpp"
329 #include "x.hpp"
33 #include "im.hpp"
34 #include "cmdline.h"
35
36 // Executes a command and gets its output. Used for executing slop for selection.
37 int exec( std::string cmd, std::string* ret ) {
38 FILE* pipe = popen( cmd.c_str(), "r" );
39 if ( !pipe ) {
40 return EXIT_FAILURE;
41 }
42 // Doesn't matter what size the buffer is, since it's grabbed in chunks.
43 char buffer[255];
44 std::string result = "";
45 while( !feof( pipe ) ) {
46 if( fgets( buffer, 255, pipe ) != NULL ) {
47 result += buffer;
48 }
49 }
50 *ret = result;
51 if ( pclose( pipe ) == -1 ) {
52 return EXIT_FAILURE;
53 }
54 return EXIT_SUCCESS;
55 }
56
57 // Parse geometry from a string, it's pretty simple really.
58 int parseGeometry( std::string arg, int* x, int* y, int* w, int* h ) {
59 std::string copy = arg;
60 // Replace all x's and +'s with spaces. This is so that sscanf works properly, it just doesn't
61 // like using anything but spaces for delimiters.
62 int find = copy.find( "x" );
63 while( find != (int)copy.npos ) {
64 copy.at( find ) = ' ';
65 find = copy.find( "x" );
66 }
67 find = copy.find( "+" );
68 while( find != (int)copy.npos ) {
69 copy.at( find ) = ' ';
70 find = copy.find( "+" );
71 }
72 int num = sscanf( copy.c_str(), "%d %d %d %d", w, h, x, y );
73 if ( num != 4 ) {
74 fprintf( stderr, "Error parsing geometry from %s\n", arg.c_str() );
75 return EXIT_FAILURE;
76 }
77 return EXIT_SUCCESS;
78 }
79
80 // We use this to detect if we should enable masking or not.
81 // This is really important because if a user tries to screenshot a window that's
82 // slightly off-screen he probably wants the whole window, but if a user
83 // takes a full screenshot, then he would most certainly want it masked, but
84 // only if they have pixels that are offscreen.
85 bool checkMask( std::string type, int x, int y, int w, int h, Window id ) {
86 int sw = WidthOfScreen( xengine->m_screen );
87 int sh = HeightOfScreen( xengine->m_screen );
88 if ( type == "auto" ) {
89 // First we check if there's even any offscreen pixels
90 int monitorArea = 0;
91 std::vector<XRRCrtcInfo*> monitors = xengine->getCRTCS();
92 for ( unsigned int i = 0;i<monitors.size();i++ ) {
93 XRRCrtcInfo* cmonitor = monitors[ i ];
94 monitorArea += cmonitor->height * cmonitor->width;
95 }
96 xengine->freeCRTCS( monitors );
97 // If our monitors cover the entire screen, masking won't do anything anyway.
98 if ( monitorArea >= sw * sh ) {
99 return false;
100 }
101 // If we specified an actual window we certainly don't want to mask anything.
102 if ( id != None && id != xengine->m_root ) {
103 return false;
104 }
105 // If our screenshot has > 80% of the screen covered, we probably want it masked by off-screen pixels.
106 if ( abs( (int)( (float)sw - (float)w ) ) / (float)sw < 0.2 &&
107 abs( (int)( (float)sh - (float)h ) ) / (float)sh < 0.2 &&
108 (float)x / (float)sw < 0.2 &&
109 (float)y / (float)sh < 0.2 ) {
110 return true;
111 }
112 // Otherwise we're probably taking a picture of a specific thing on the screen.
113 return false;
114 } else if ( type == "on" ) {
115 return true;
116 }
117 return false;
118 }
119
120 int slop( gengetopt_args_info options, int* x, int* y, int* w, int* h, Window* window ) {
121 std::stringstream slopcommand;
122 slopcommand << "slop";
123 if ( options.nokeyboard_flag ) {
124 slopcommand << " --nokeyboard ";
125 }
126 slopcommand << " -b " << options.bordersize_arg;
127 slopcommand << " -p " << options.padding_arg;
128 slopcommand << " -t " << options.tolerance_arg;
129 slopcommand << " -g " << options.gracetime_arg;
130 slopcommand << " -c " << options.color_arg;
131 if ( options.nodecorations_flag ) {
132 slopcommand << " -n";
133 }
134 slopcommand << " --min=" << options.min_arg;
135 slopcommand << " --max=" << options.max_arg;
136 if ( options.xdisplay_given ) {
137 slopcommand << " --xdisplay=" << options.xdisplay_arg;
138 }
139 if ( options.highlight_flag ) {
140 slopcommand << " -l";
141 }
142 slopcommand << "\n";
143 std::string result;
144 int err = exec( slopcommand.str(), &result );
145 if ( err != EXIT_SUCCESS ) {
146 return EXIT_FAILURE;
147 }
148 // From here we'll just be parsing the output of slop.
149 // Replace all ='s with spaces in the result, this is so sscanf works properly.
150 int find = result.find( "=" );
151 while( find != (int)result.npos ) {
152 result.at( find ) = ' ';
153 find = result.find( "=" );
154 }
155 Window test = None;
156 int num = sscanf( result.c_str(), "X %i\n Y %i\n W %i\n H %i\nG %*s\nID %lu", x, y, w, h, &test );
157 if ( num != 5 || ( *w == 0 && *h == 0 ) ) {
158 return EXIT_FAILURE;
159 }
160 // If we actually have a window selection, set the window and offset the coordinates to be
161 // localized to that particular window.
162 if ( test != None ) {
163 *window = test;
164 // If we get a window, make sure that slop's selection's origin is around it.
165 // Slop's selection's origin defaults to the root window, so we just use XTranslateCoordinates.
10 #include "image.hpp"
11
12 template<typename Out>
13 static void split(const std::string &s, char delim, Out result) {
14 std::stringstream ss;
15 ss.str(s);
16 std::string item;
17 while (std::getline(ss, item, delim)) {
18 *(result++) = item;
19 }
20 }
21 static std::vector<std::string> split(const std::string &s, char delim) {
22 std::vector<std::string> elems;
23 split(s, delim, std::back_inserter(elems));
24 return elems;
25 }
26
27 class MaimOptions {
28 public:
29 MaimOptions();
30 std::string savepath;
31 std::string format;
32 Window window;
33 Window parent;
34 glm::vec4 geometry;
35 float delay;
36 int quality;
37 bool select;
38 bool hideCursor;
39 bool geometryGiven;
40 bool quiet;
41 bool windowGiven;
42 bool parentGiven;
43 bool formatGiven;
44 bool version;
45 bool help;
46 bool savepathGiven;
47 };
48
49 MaimOptions::MaimOptions() {
50 savepath = "";
51 window = None;
52 parent = None;
53 quality = 7;
54 quiet = false;
55 delay = 0;
56 format = "png";
57 version = false;
58 help = false;
59 select = false;
60 parentGiven = false;
61 hideCursor = false;
62 geometryGiven = false;
63 savepathGiven = false;
64 windowGiven = false;
65 formatGiven = false;
66 }
67
68 Window parseWindow( std::string win, X11* x11 ) {
69 if ( win == "root" ) {
70 return x11->root;
71 }
72 Window retwin;
73 std::string::size_type sz;
74 try {
75 retwin = std::stoi(win,&sz);
76 } catch ( ... ) {
77 try {
78 retwin = std::stoul(win,&sz,16);
79 } catch ( ... ) {
80 throw new std::invalid_argument("Unable to parse value " + win + " as a window. Expecting integer, hex, or `root`.");
81 }
82 }
83 return retwin;
84 }
85
86 glm::vec4 parseColor( std::string value ) {
87 std::string valuecopy = value;
88 glm::vec4 found;
89 std::string::size_type sz;
90 try {
91 found[0] = std::stof(value,&sz);
92 value = value.substr(sz+1);
93 found[1] = std::stof(value,&sz);
94 value = value.substr(sz+1);
95 found[2] = std::stof(value,&sz);
96 if ( value.size() != sz ) {
97 value = value.substr(sz+1);
98 found[3] = std::stof(value,&sz);
99 if ( value.size() != sz ) {
100 throw "dur";
101 }
102 } else {
103 found[3] = 1;
104 }
105 } catch ( ... ) {
106 throw new std::invalid_argument("Unable to parse value `" + valuecopy + "` as a color. Should be in the format r,g,b or r,g,b,a. Like 1,1,1,1.");
107 }
108 return found;
109 }
110
111 glm::vec4 parseGeometry( std::string value ) {
112 glm::vec4 found;
113 std::string valuecopy = value;
114 std::string::size_type sz = 0;
115 glm::vec2 dim(0,0);
116 int curpos = 0;
117 glm::vec2 pos(0,0);
118 try {
119 if ( std::count(value.begin(), value.end(), '+') > 2 ) {
120 throw "dur";
121 }
122 if ( std::count(value.begin(), value.end(), '-') > 2 ) {
123 throw "dur";
124 }
125 if ( std::count(value.begin(), value.end(), 'x') > 1 ) {
126 throw "dur";
127 }
128 while( value != "" ) {
129 switch( value[0] ) {
130 case 'x':
131 dim.y = std::stof(value.substr(1),&sz);
132 sz++;
133 break;
134 case '+':
135 pos[curpos++] = std::stof(value.substr(1), &sz);
136 sz++;
137 break;
138 case '-':
139 pos[curpos++] = -std::stof(value.substr(1), &sz);
140 sz++;
141 break;
142 default:
143 dim.x = std::stof(value,&sz);
144 break;
145 }
146 value = value.substr(sz);
147 }
148 } catch ( ... ) {
149 throw new std::invalid_argument("Unable to parse value `" + valuecopy + "` as a geometry. Should be in the format wxh+x+y, +x+y, or wxh. Like 600x400+10+20.");
150 }
151 found.x = pos.x;
152 found.y = pos.y;
153 found.z = dim.x;
154 found.w = dim.y;
155 return found;
156 }
157
158 MaimOptions* getMaimOptions( cxxopts::Options& options, X11* x11 ) {
159 MaimOptions* foo = new MaimOptions();
160 foo->parentGiven = options.count("parent") > 0;
161 if ( foo->parentGiven ) {
162 foo->parent = parseWindow( options["parent"].as<std::string>(), x11 );
163 }
164 foo->windowGiven = options.count("window") > 0;
165 if ( foo->windowGiven ) {
166 foo->window = parseWindow( options["window"].as<std::string>(), x11 );
167 }
168 foo->geometryGiven = options.count("geometry") > 0;
169 if ( foo->geometryGiven ) {
170 foo->geometry = parseGeometry( options["geometry"].as<std::string>() );
171 }
172 if ( options.count( "delay" ) > 0 ) {
173 foo->delay = options["delay"].as<float>();
174 }
175 if ( options.count( "hidecursor" ) > 0 ) {
176 foo->hideCursor = options["hidecursor"].as<bool>();
177 }
178 if ( options.count( "select" ) > 0 ) {
179 foo->select = options["select"].as<bool>();
180 }
181 if ( options.count( "version" ) > 0 ) {
182 foo->version = options["version"].as<bool>();
183 }
184 if ( options.count( "help" ) > 0 ) {
185 foo->help = options["help"].as<bool>();
186 }
187 if ( options.count( "quiet" ) > 0 ) {
188 foo->quiet = options["quiet"].as<bool>();
189 }
190 if ( options.count( "format" ) > 0 ) {
191 foo->quiet = options["quiet"].as<bool>();
192 }
193 foo->formatGiven = options.count("format") > 0;
194 if ( foo->formatGiven ) {
195 foo->format = options["format"].as<std::string>();
196 if ( foo->format != "png" && foo->format != "jpg" && foo->format != "jpeg" ) {
197 throw new std::invalid_argument("Unknown format type: `" + foo->format + "`, only `png` or `jpg` is allowed." );
198 }
199 }
200 if ( options.count( "quality" ) > 0 ) {
201 foo->quality = options["quality"].as<int>();
202 if ( foo->quality > 10 || foo->quality < 1 ) {
203 throw new std::invalid_argument("Quality argument must be between 1 and 10");
204 }
205 }
206 auto& positional = options["positional"].as<std::vector<std::string>>();
207 foo->savepathGiven = positional.size() > 0;
208 //std::cerr << positional[0] << "\n";
209 if ( foo->savepathGiven ) {
210 foo->savepath = positional[0];
211 }
212 return foo;
213 }
214
215 slop::SlopOptions* getSlopOptions( cxxopts::Options& options ) {
216 slop::SlopOptions* foo = new slop::SlopOptions();
217 if ( options.count( "bordersize" ) > 0 ) {
218 foo->border = options["bordersize"].as<float>();
219 }
220 if ( options.count( "padding" ) > 0 ) {
221 foo->padding = options["padding"].as<float>();
222 }
223 if ( options.count( "tolerance" ) > 0 ) {
224 foo->tolerance = options["tolerance"].as<float>();
225 }
226 glm::vec4 color = glm::vec4( foo->r, foo->g, foo->b, foo->a );
227 if ( options.count( "color" ) > 0 ) {
228 color = parseColor( options["color"].as<std::string>() );
229 }
230 foo->r = color.r;
231 foo->g = color.g;
232 foo->b = color.b;
233 foo->a = color.a;
234 if ( options.count( "nokeyboard" ) > 0 ) {
235 foo->nokeyboard = options["nokeyboard"].as<bool>();
236 }
237 if ( options.count( "noopengl" ) > 0 ) {
238 foo->noopengl = options["noopengl"].as<bool>();
239 }
240 if ( options.count( "xdisplay" ) > 0 ) {
241 std::string xdisplay = options["xdisplay"].as<std::string>();
242 char* cxdisplay = new char[xdisplay.length()+1];
243 memcpy( cxdisplay, xdisplay.c_str(), xdisplay.length() );
244 cxdisplay[xdisplay.length()]='\0';
245 foo->xdisplay = cxdisplay;
246 }
247 if ( options.count( "shader" ) > 0 ) {
248 std::string shaders = options["shader"].as<std::string>();
249 char* cshaders = new char[shaders.length()+1];
250 memcpy( cshaders, shaders.c_str(), shaders.length() );
251 cshaders[shaders.length()]='\0';
252 foo->shaders = cshaders;
253 }
254 if ( options.count( "quiet" ) > 0 ) {
255 foo->quiet = options["quiet"].as<bool>();
256 }
257 if ( options.count( "highlight" ) > 0 ) {
258 foo->highlight = options["highlight"].as<bool>();
259 }
260 if ( options.count( "nodecorations" ) > 0 ) {
261 foo->nodecorations = options["nodecorations"].as<int>();
262 if ( foo->nodecorations < 0 || foo->nodecorations > 2 ) {
263 throw new std::invalid_argument( "--nodecorations must be between 0 and 2. Or be used as a flag." );
264 }
265 }
266 return foo;
267 }
268
269 void help() {
270 std::cout << "maim - make image\n";
271 std::cout << "\n";
272 std::cout << "SYNOPSIS\n";
273 std::cout << " maim [OPTIONS] [FILEPATH]\n";
274 std::cout << "\n";
275 std::cout << "DESCRIPTION\n";
276 std::cout << " maim (make image) is an utility that takes a screenshot of your desktop,\n";
277 std::cout << " and encodes a png or jpg image of it. By default it outputs the encoded\n";
278 std::cout << " image data directly to standard output.\n";
279 std::cout << "\n";
280 std::cout << "OPTIONS\n";
281 std::cout << " -h, --help\n";
282 std::cout << " Print help and exit.\n";
283 std::cout << "\n";
284 std::cout << " -v, --version\n";
285 std::cout << " Print version and exit.\n";
286 std::cout << "\n";
287 std::cout << " -x, --xdisplay=hostname:number.screen_number\n";
288 std::cout << " Sets the xdisplay to use.\n";
289 std::cout << "\n";
290 std::cout << " -f, --format=STRING\n";
291 std::cout << " Sets the desired output format, by default maim will attempt to\n";
292 std::cout << " determine the desired output format automatically from the output\n";
293 std::cout << " file. If that fails it defaults to a lossless png format. Cur‐\n";
294 std::cout << " rently only supports `png` or `jpg`.\n";
295 std::cout << "\n";
296 std::cout << " -i, --window=INT\n";
297 std::cout << " Sets the desired window to capture, defaults to the root window.\n";
298 std::cout << "\n";
299 std::cout << " -g, --geometry=GEOMETRY\n";
300 std::cout << " Sets the region to capture, uses local coordinates from the given\n";
301 std::cout << " window. So -g10x30-5+0 would represent the rectangle wxh+x+y where\n";
302 std::cout << " w=10, h=30, x=-5, and y=0. x and y are the upper left location of\n";
303 std::cout << " this rectangle.\n";
304 std::cout << "\n";
305 std::cout << " -d, --delay=FLOAT\n";
306 std::cout << " Sets the time in seconds to wait before taking a screenshot.\n";
307 std::cout << " Prints a simple message to show how many seconds are left before a\n";
308 std::cout << " screenshot is taken. See --quiet for muting this message.\n";
309 std::cout << "\n";
310 std::cout << " -u, --hidecursor\n";
311 std::cout << " By default maim super-imposes the cursor onto the image, you can\n";
312 std::cout << " disable that behavior with this flag.\n";
313 std::cout << "\n";
314 std::cout << " -m, --quality\n";
315 std::cout << " An integer from 1 to 10 that determines the compression quality. 1\n";
316 std::cout << " is the highest (and lossiest) compression available for the pro‐\n";
317 std::cout << " vided format. For example a setting of `1` with png (a lossless\n";
318 std::cout << " format) would increase filesize and speed up encoding dramatical-\n";
319 std::cout << " ly. While a setting of `1` on a jpeg would create a pixel mush.\n";
320 std::cout << "\n";
321 std::cout << " -s, --select\n";
322 std::cout << " Enables an interactive selection mode where you may select the\n";
323 std::cout << " desired region or window before a screenshot is captured. Uses the\n";
324 std::cout << " settings below to determine the visuals and settings of slop.\n";
325 std::cout << "\n";
326 std::cout << " -w, --parent=WINDOW\n";
327 std::cout << " By default, maim assumes the --geometry values are in respect to\n";
328 std::cout << " the provided --window (or root if not provided). This parameter\n";
329 std::cout << " overrides this behavior by making the geometry be in respect to\n";
330 std::cout << " whatever window you provide to --parent. Allows for an integer,\n";
331 std::cout << " hex, or `root` for input.\n";
332 std::cout << "\n";
333 std::cout << "SLOP OPTIONS\n";
334 std::cout << " -b, --bordersize=FLOAT\n";
335 std::cout << " Sets the selection rectangle's thickness.\n";
336 std::cout << "\n";
337 std::cout << " -p, --padding=FLOAT\n";
338 std::cout << " Sets the padding size for the selection, this can be negative.\n";
339 std::cout << "\n";
340 std::cout << " -t, --tolerance=FLOAT\n";
341 std::cout << " How far in pixels the mouse can move after clicking, and still be\n";
342 std::cout << " detected as a normal click instead of a click-and-drag. Setting\n";
343 std::cout << " this to 0 will disable window selections. Alternatively setting it\n";
344 std::cout << " to 9999999 would force a window selection.\n";
345 std::cout << "\n";
346 std::cout << " -c, --color=FLOAT,FLOAT,FLOAT,FLOAT\n";
347 std::cout << " Sets the selection rectangle's color. Supports RGB or RGBA input.\n";
348 std::cout << " Depending on the system's window manager/OpenGL support, the opac‐\n";
349 std::cout << " ity may be ignored.\n";
350 std::cout << "\n";
351 std::cout << " -r, --shader=STRING\n";
352 std::cout << " This sets the vertex shader, and fragment shader combo to use when\n";
353 std::cout << " drawing the final framebuffer to the screen. This obviously only\n";
354 std::cout << " works when OpenGL is enabled. The shaders are loaded from ~/.con‐\n";
355 std::cout << " fig/maim. See https://github.com/naelstrof/slop for more informa‐\n";
356 std::cout << " tion on how to create your own shaders.\n";
357 std::cout << "\n";
358 std::cout << " -n, --nodecorations=INT\n";
359 std::cout << " Sets the level of aggressiveness when trying to remove window\n";
360 std::cout << " decroations. `0' is off, `1' will try lightly to remove decora‐\n";
361 std::cout << " tions, and `2' will recursively descend into the root tree until\n";
362 std::cout << " it gets the deepest available visible child under the mouse.\n";
363 std::cout << " Defaults to `0'.\n";
364 std::cout << "\n";
365 std::cout << " -l, --highlight\n";
366 std::cout << " Instead of outlining a selection, maim will highlight it instead.\n";
367 std::cout << " This is particularly useful if the color is set to an opacity\n";
368 std::cout << " lower than 1.\n";
369 std::cout << "\n";
370 std::cout << " -q, --quiet\n";
371 std::cout << " Disable any unnecessary cerr output. Any warnings or info simply\n";
372 std::cout << " won't print.\n";
373 std::cout << "\n";
374 std::cout << " -k, --nokeyboard\n";
375 std::cout << " Disables the ability to cancel selections with the keyboard.\n";
376 std::cout << "\n";
377 std::cout << " -o, --noopengl\n";
378 std::cout << " Disables graphics hardware acceleration.\n";
379 std::cout << "\n";
380 std::cout << "EXAMPLES\n";
381 std::cout << " Screenshot the active window and save it to the clipboard for quick past‐\n";
382 std::cout << " ing.\n";
383 std::cout << "\n";
384 std::cout << " maim -i $(xdotool getactivewindow) | xclip -selection clipboard -t image/png\n";
385 std::cout << "\n";
386 std::cout << " Save a desktop screenshot with a unique ordered timestamp in the Pictures\n";
387 std::cout << " folder.\n";
388 std::cout << "\n";
389 std::cout << " maim ~/Pictures/$(date +%s).png\n";
390 std::cout << "\n";
391 std::cout << " Prompt for a region to screenshot. Add a fancy shadow to it, then save it\n";
392 std::cout << " to shadow.png.\n";
393 std::cout << "\n";
394 std::cout << " maim -s | convert - \\( +clone -background black -shadow 80x3+5+5 \\) +swap \\\n";
395 std::cout << " -background none -layers merge +repage shadow.png\n";
396 }
397
398 int app( int argc, char** argv ) {
399 // Use cxxopts to parse options, we pass them into a MaimOptions and SlopOptions object so we can swap out cxxopts if it's bad or whatever.
400 cxxopts::Options options("maim", "Screenshot application.");
401 options.add_options()
402 ("h,help", "Print help and exit.")
403 ("v,version", "Print version and exit.")
404 ("x,xdisplay", "Sets the xdisplay to use", cxxopts::value<std::string>())
405 ("f,format", "Sets the desired output format, by default maim will attempt to determine the desired output format automatically from the output file. If that fails it defaults to a lossless png format. Currently only supports `png` or `jpg`.", cxxopts::value<std::string>())
406 ("i,window", "Sets the desired window to capture, defaults to the root window. Allows for an integer, hex, or `root` for input.", cxxopts::value<std::string>())
407 ("g,geometry", "Sets the region to capture, uses local coordinates from the given window. So -g10x30-5+0 would represent the rectangle wxh+x+y where w=10, h=30, x=-5, and y=0. x and y are the upper left location of this rectangle.", cxxopts::value<std::string>())
408 ("w,parent", "By default, maim assumes the --geometry values are in respect to the provided --window (or root if not provided). This parameter overrides this behavior by making the geometry be in respect to whatever window you provide to --parent. Allows for an integer, hex, or `root` for input.", cxxopts::value<std::string>())
409 ("d,delay", "Sets the time in seconds to wait before taking a screenshot. Prints a simple message to show how many seconds are left before a screenshot is taken. See --quiet for muting this message.", cxxopts::value<float>()->implicit_value("5"))
410 ("u,hidecursor", "By default maim super-imposes the cursor onto the image, you can disable that behavior with this flag.")
411 ("m,quality", "An integer from 1 to 10 that determines the compression quality. 1 is the highest (and lossiest) compression available for the provided format. For example a setting of `1` with png (a loss‐ less format) would increase filesize and decrease decoding time. While a setting of `1` on a jpeg would create a pixel mush.", cxxopts::value<int>())
412 ("s,select", "Enables an interactive selection mode where you may select the desired region or window before a screenshot is captured. Uses the settings below to determine the visuals and settings of slop.")
413 ("b,bordersize", "Sets the selection rectangle's thickness.", cxxopts::value<float>())
414 ("p,padding", "Sets the padding size for the selection, this can be negative.", cxxopts::value<float>())
415 ("t,tolerance", "How far in pixels the mouse can move after clicking, and still be detected as a normal click instead of a click-and-drag. Setting this to 0 will disable window selections. Alternatively setting it to 9999999 would force a window selection.", cxxopts::value<float>())
416 ("c,color", "Sets the selection rectangle's color. Supports RGB or RGBA input. Depending on the system's window manager/OpenGL support, the opacity may be ignored.", cxxopts::value<std::string>())
417 ("r,shader", "This sets the vertex shader, and fragment shader combo to use when drawing the final framebuffer to the screen. This obviously only works when OpenGL is enabled. The shaders are loaded from ~/.config/maim. See https://github.com/naelstrof/slop for more information on how to create your own shaders.", cxxopts::value<std::string>())
418 ("n,nodecorations", "Sets the level of aggressiveness when trying to remove window decroations. `0' is off, `1' will try lightly to remove decorations, and `2' will recursively descend into the root tree until it gets the deepest available visible child under the mouse. Defaults to `0'.", cxxopts::value<int>()->implicit_value("1"))
419 ("l,highlight", "Instead of outlining a selection, maim will highlight it instead. This is particularly useful if the color is set to an opacity lower than 1.")
420 ("q,quiet", "Disable any unnecessary cerr output. Any warnings or info simply won't print.")
421 ("k,nokeyboard", "Disables the ability to cancel selections with the keyboard.")
422 ("o,noopengl", "Disables graphics hardware acceleration.")
423 ("positional", "Positional parameters", cxxopts::value<std::vector<std::string>>())
424 ;
425 options.parse_positional("positional");
426 options.parse(argc, argv);
427
428 slop::SlopOptions* slopOptions = getSlopOptions( options );
429 // Boot up x11
430 X11* x11 = new X11(slopOptions->xdisplay);
431 MaimOptions* maimOptions = getMaimOptions( options, x11 );
432 if ( maimOptions->version ) {
433 std::cout << MAIM_VERSION << "\n";
434 return 0;
435 }
436 if ( maimOptions->help ) {
437 help();
438 return 0;
439 }
440 slop::SlopSelection selection(0,0,0,0,0,true);
441
442 if ( maimOptions->select ) {
443 if ( maimOptions->windowGiven || maimOptions->parentGiven || maimOptions->geometryGiven ) {
444 throw new std::invalid_argument( "Interactive mode (--select) doesn't support the following parameters: --window, --parent, --geometry." );
445 }
446 selection = SlopSelect(slopOptions);
447 if ( selection.cancelled ) {
448 if ( !maimOptions->quiet ) {
449 std::cerr << "Selection was cancelled by keystroke or right-click.\n";
450 }
451 return 1;
452 }
453 }
454
455 if ( !maimOptions->formatGiven && maimOptions->savepathGiven && maimOptions->savepath.find_last_of(".") != std::string::npos ) {
456 maimOptions->format = maimOptions->savepath.substr(maimOptions->savepath.find_last_of(".")+1);
457 if ( maimOptions->format != "png" && maimOptions->format != "jpg" && maimOptions->format != "jpeg" ) {
458 throw new std::invalid_argument("Unknown format type: `" + maimOptions->format + "`, only `png` or `jpg` is allowed." );
459 }
460 }
461 if ( !maimOptions->windowGiven ) {
462 maimOptions->window = x11->root;
463 } else {
464 XWindowAttributes attr;
465 XGetWindowAttributes(x11->display, maimOptions->window, &attr);
466 if (attr.backing_store == NotUseful && attr.width == 1 && attr.height == 1) {
467 Window root, parent;
468 parent = None;
469 Window* children;
470 unsigned int nchildren;
471 Window selectedWindow;
472 XQueryTree( x11->display, maimOptions->window, &root, &parent, &children, &nchildren );
473 if ( parent != None ) {
474 maimOptions->window = parent;
475 }
476 }
477 }
478 if ( !maimOptions->parentGiven ) {
479 maimOptions->parent = maimOptions->window;
480 } else if ( !maimOptions->geometryGiven ) {
481 throw new std::invalid_argument( "Relative mode (--parent) requires --geometry." );
482 }
483 if ( !maimOptions->geometryGiven ) {
166484 Window junk;
167 XTranslateCoordinates( xengine->m_display, xengine->m_root, test, *x, *y, x, y, &junk );
168 }
169 return EXIT_SUCCESS;
170 }
171
172 int app( int argc, char** argv ) {
173 // First parse any options and the filename we need.
174 gengetopt_args_info options;
175 int err = cmdline_parser( argc, argv, &options );
176 if ( err != EXIT_SUCCESS ) {
177 return EXIT_FAILURE;
178 }
179 // Then set up the x interface.
180 if ( options.xdisplay_given ) {
181 err = xengine->init( options.xdisplay_arg );
485 glm::ivec4 geometry = getWindowGeometry( x11, maimOptions->window );
486 XTranslateCoordinates(x11->display, x11->root, maimOptions->window, geometry.x, geometry.y, &geometry.x, &geometry.y, &junk);
487 maimOptions->geometry = geometry;
488 }
489
490 if ( !maimOptions->select ) {
491 selection.x = maimOptions->geometry.x;
492 selection.y = maimOptions->geometry.y;
493 selection.w = maimOptions->geometry.z;
494 selection.h = maimOptions->geometry.w;
495 selection.id = maimOptions->window;
496 }
497 std::ostream* out;
498 if ( maimOptions->savepathGiven ) {
499 std::ofstream* file = new std::ofstream();
500 file->open(maimOptions->savepath.c_str());
501 if ( !file->is_open() ) {
502 throw new std::runtime_error( "Failed to open file for writing: `" + maimOptions->savepath + "`." );
503 }
504 out = file;
182505 } else {
183 // If we weren't specifically given a xdisplay, we try
184 // to parse it from environment variables
185 char* display = getenv( "DISPLAY" );
186 if ( display ) {
187 err = xengine->init( display );
188 } else {
189 fprintf( stderr, "Warning: Failed to parse environment variable: DISPLAY. Using \":0\" instead.\n" );
190 err = xengine->init( ":0" );
191 }
192 }
193 if ( err != EXIT_SUCCESS ) {
194 fprintf( stderr, "Failed to grab X display!\n" );
195 return EXIT_FAILURE;
196 }
197 // Then the imlib2 interface
198 err = imengine->init();
199 if ( err != EXIT_SUCCESS ) {
200 fprintf( stderr, "Failed to initialize imlib2!\n" );
201 return EXIT_FAILURE;
202 }
203 // Grab all of our variables from the options.
204 bool gotGeometry = false;
205 bool gotSelectFlag = options.select_flag;
206 int x, y, w, h;
207 float delay;
208 err = sscanf( options.delay_arg, "%f", &delay );
209 if ( err != 1 ) {
210 fprintf( stderr, "Failed to parse %s as a float for delay!\n", options.delay_arg );
211 return EXIT_FAILURE;
212 }
213 struct timespec delayTime;
214 delayTime.tv_sec = delay;
215 delayTime.tv_nsec = 0;
216 // Get our geometry if we have any.
217 if ( options.x_given && options.y_given && options.w_given && options.h_given && !options.geometry_given ) {
218 x = options.x_arg;
219 y = options.y_arg;
220 w = options.w_arg;
221 h = options.h_arg;
222 gotGeometry = true;
223 } else if ( ( options.x_given || options.y_given || options.w_given || options.h_given ) && !options.geometry_given ) {
224 fprintf( stderr, "Partial geometry arguments were set, but it isn't enough data to take a screenshot!\n" );
225 fprintf( stderr, "Please give the geometry argument or give ALL of the following arguments: x, y, w, h.\n" );
226 cmdline_parser_free( &options );
227 return EXIT_FAILURE;
228 } else if ( options.geometry_given ) {
229 err = parseGeometry( options.geometry_arg, &x, &y, &w, &h );
230 if ( err != EXIT_SUCCESS ) {
231 fprintf( stderr, "Failed to parse geometry %s, should be in format WxH+X+Y!\n", options.geometry_arg );
232 cmdline_parser_free( &options );
233 return EXIT_FAILURE;
234 }
235 gotGeometry = true;
236 }
237 // Get our window if we have one, default to the root window.
238 Window window = xengine->m_root;
239 if ( options.windowid_given ) {
240 window = (Window)options.windowid_arg;
241 // Since we have a window we need to turn root coords into our local window coords.
242 // but only if the user wants us to.
243 if ( !options.localize_flag && window != None ) {
244 Window junk;
245 XTranslateCoordinates( xengine->m_display, xengine->m_root, window, x, y, &x, &y, &junk );
246 }
247 }
248 // Get our file name
249 std::string file = "";
250 // If we don't have a file, default to writing to the home directory.
251 if ( options.inputs_num == 0 ) {
252 // Try as hard as we can to get the current directory.
253 int trycount = 0;
254 int length = MAXPATHLEN;
255 char* currentdir = new char[ length ];
256 char* error = getcwd( currentdir, length );
257 while ( error == NULL ) {
258 delete[] currentdir;
259 length *= 2;
260 currentdir = new char[ length ];
261 error = getcwd( currentdir, length );
262 trycount++;
263 // Ok someone's trying to be whacky with the current directory if we're going 8 times over
264 // the max path length.
265 if ( trycount > 3 ) {
266 fprintf( stderr, "Failed to grab the current directory!" );
267 cmdline_parser_free( &options );
268 return EXIT_FAILURE;
506 out = &std::cout;
507 }
508
509 // Then we grab the pixel buffer of the provided window/selection.
510 if ( maimOptions->delay ) {
511 if ( !maimOptions->quiet ) {
512 std::cerr << "Snapshotting in...";
513 }
514 while ( maimOptions->delay > 0 ) {
515 std::this_thread::sleep_for(std::chrono::milliseconds(glm::clamp(glm::min(1000,(int)(maimOptions->delay*1000)),0,1000)));
516 maimOptions->delay-=1;
517 if ( !maimOptions->quiet ) {
518 if ( maimOptions->delay <= 0 ) {
519 std::cerr << "☺";
520 } else {
521 std::cerr << maimOptions->delay << " ";
522 }
269523 }
270524 }
271 file = currentdir;
272 // Get unix timestamp
273 std::stringstream result;
274 result << (int)time( NULL );
275 file += "/" + result.str() + ".png";
276 printf( "No file specified, using %s\n", file.c_str() );
277 free( currentdir );
278 } else if ( options.inputs_num == 1 ) {
279 file = options.inputs[ 0 ];
280 } else {
281 fprintf( stderr, "Unexpected number of output files! There should only be one.\n" );
282 cmdline_parser_free( &options );
283 return EXIT_FAILURE;
284 }
285
286 // Finally we have all our information, now to use it.
287 if ( gotSelectFlag ) {
288 err = slop( options, &x, &y, &w, &h, &window );
289 if ( err != EXIT_SUCCESS ) {
290 fprintf( stderr, "Selection was cancelled or slop failed to run. Make sure it's installed!\n" );
291 cmdline_parser_free( &options );
292 return EXIT_FAILURE;
293 }
294 err = nanosleep( &delayTime, NULL );
295 if ( err != EXIT_SUCCESS ) {
296 fprintf( stderr, "Warning: Failed to delay the screenshot. Continuing anyway..." );
297 }
298 err = imengine->screenshot( window, x, y, w, h );
299 if ( err != EXIT_SUCCESS ) {
300 fprintf( stderr, "Failed to take screenshot.\n" );
301 return EXIT_FAILURE;
302 }
303 if ( !options.hidecursor_flag ) {
304 imengine->blendCursor( window, x, y );
305 }
306 if ( checkMask( options.mask_arg, x, y, w, h, window ) ) {
307 imengine->mask( x, y, w, h );
308 }
309 err = imengine->save( file );
310 cmdline_parser_free( &options );
311 if ( err != EXIT_SUCCESS ) {
312 fprintf( stderr, "Failed to take screenshot.\n" );
313 return EXIT_FAILURE;
314 }
315 return EXIT_SUCCESS;
316 }
317 if ( gotGeometry ) {
318 err = nanosleep( &delayTime, NULL );
319 if ( err != EXIT_SUCCESS ) {
320 fprintf( stderr, "Warning: Failed to delay the screenshot. Continuing anyway..." );
321 }
322 err = imengine->screenshot( window, x, y, w, h );
323 if ( err != EXIT_SUCCESS ) {
324 fprintf( stderr, "Failed to take screenshot.\n" );
325 return EXIT_FAILURE;
326 }
327 if ( !options.hidecursor_flag ) {
328 imengine->blendCursor( window, x, y );
329 }
330 if ( checkMask( options.mask_arg, x, y, w, h, window ) ) {
331 imengine->mask( x, y, w, h );
332 }
333 err = imengine->save( file );
334 cmdline_parser_free( &options );
335 if ( err != EXIT_SUCCESS ) {
336 fprintf( stderr, "Failed to take screenshot.\n" );
337 return EXIT_FAILURE;
338 }
339 return EXIT_SUCCESS;
340 }
341 // If we didn't get any special options, just screenshot the specified window
342 // (Which defaults to the whole screen).
343 err = nanosleep( &delayTime, NULL );
344 if ( err != EXIT_SUCCESS ) {
345 fprintf( stderr, "Warning: Failed to delay the screenshot. Continuing anyway..." );
346 }
347 err = imengine->screenshot( window );
348 if ( err != EXIT_SUCCESS ) {
349 fprintf( stderr, "Failed to take screenshot.\n" );
350 return EXIT_FAILURE;
351 }
352 if ( !options.hidecursor_flag ) {
353 imengine->blendCursor( window );
354 }
355 if ( checkMask( options.mask_arg, 0, 0, WidthOfScreen( xengine->m_screen ), HeightOfScreen( xengine->m_screen ), window ) ) {
356 imengine->mask();
357 }
358 err = imengine->save( file );
359 cmdline_parser_free( &options );
360 if ( err != EXIT_SUCCESS ) {
361 fprintf( stderr, "Failed to take screenshot.\n" );
362 return EXIT_FAILURE;
363 }
364 return EXIT_SUCCESS;
525 if ( !maimOptions->quiet ) {
526 std::cerr << "\n";
527 }
528 }
529 // Localize to our parent
530 int px, py;
531 Window junk;
532 XTranslateCoordinates( x11->display, maimOptions->parent, x11->root, (int)selection.x, (int)selection.y, &px, &py, &junk);
533 glm::ivec2 imageloc;
534 // Snapshot the image
535 XImage* image = x11->getImage( selection.id, px, py, selection.w, selection.h, imageloc);
536 if ( maimOptions->format == "png" ) {
537 // Convert it to an ARGB format, clipping it to the selection.
538 ARGBImage convert(image, imageloc, glm::vec4(px, py, selection.w, selection.h), 4, x11 );
539 if ( !maimOptions->hideCursor ) {
540 convert.blendCursor( x11 );
541 }
542 // Mask it if we're taking a picture of root
543 if ( selection.id == x11->root ) {
544 convert.mask(x11);
545 }
546 // Then output it in the desired format.
547 convert.writePNG(*out, maimOptions->quality );
548 } else if ( maimOptions->format == "jpg" || maimOptions->format == "jpeg" ) {
549 // Convert it to a RGB format, clipping it to the selection.
550 ARGBImage convert(image, imageloc, glm::vec4(px, py, selection.w, selection.h), 3, x11 );
551 if ( !maimOptions->hideCursor ) {
552 convert.blendCursor( x11 );
553 }
554 // Mask it if we're taking a picture of root
555 if ( selection.id == x11->root ) {
556 convert.mask(x11);
557 }
558 // Then output it in the desired format.
559 convert.writeJPEG(*out, maimOptions->quality );
560 }
561 XDestroyImage( image );
562
563 if ( maimOptions->savepathGiven ) {
564 std::ofstream* file = (std::ofstream*)out;
565 file->close();
566 delete (std::ofstream*)out;
567 }
568 delete x11;
569 delete maimOptions;
570 if ( options.count( "xdisplay" ) > 0 ) {
571 delete slopOptions->xdisplay;
572 }
573 if ( options.count( "shader" ) > 0 ) {
574 delete slopOptions->shaders;
575 }
576 delete slopOptions;
577
578 return 0;
365579 }
366580
367581 int main( int argc, char** argv ) {
368582 try {
369583 return app( argc, argv );
370 } catch( std::bad_alloc const& exception ) {
371 fprintf( stderr, "Couldn't allocate enough memory! No space left in RAM." );
372 return EXIT_FAILURE;
373 } catch( std::exception const& exception ) {
374 fprintf( stderr, "Unhandled Exception Thrown: %s\n", exception.what() );
375 return EXIT_FAILURE;
376 } catch( ... ) {
377 fprintf( stderr, "Unknown Exception Thrown!\n" );
378 return EXIT_FAILURE;
379 }
380 }
584 } catch( std::exception* e ) {
585 std::cerr << "Maim encountered an error:\n" << e->what() << "\n";
586 return 1;
587 } // let the operating system handle any other kind of exception.
588 return 1;
589 }
+0
-125
src/options.ggo less more
0 package "maim"
1 version "v@maim_VERSION_MAJOR@.@maim_VERSION_MINOR@.@maim_VERSION_PATCH@"
2 purpose "Takes screenshots."
3 usage "maim [options] [file]"
4 description "maim (Make Image) is a utility that takes screenshots of your desktop using imlib2. It's meant to overcome shortcomings of scrot and performs better than scrot in several ways."
5 versiontext "Copyright (C) 2014 Dalton Nell, Maim Contributors (https://github.com/naelstrof/maim/graphs/contributors)"
6
7 args "--unamed-opts --file-name=cmdline"
8
9 text "Options"
10
11 option "xdisplay" - "Sets the x display."
12 string
13 typestr="hostname:number.screen_number"
14 optional
15
16 option "select" s "Enables user region selection. Requires slop to be installed."
17 flag
18 off
19
20 option "x" x "Sets the x coordinate for taking an image"
21 int
22 optional
23
24 option "y" y "Sets the y coordinate for taking an image"
25 int
26 optional
27
28 option "w" w "Sets the width for taking an image"
29 int
30 optional
31
32 option "h" h "Sets the height for taking an image"
33 int
34 optional
35
36 option "geometry" g "Set the region to capture"
37 string
38 typestr="WxH+X+Y"
39 optional
40
41 option "delay" d "Set the amount of time to wait before taking an image."
42 string
43 typestr="FLOAT"
44 default="0.0"
45 optional
46
47 option "windowid" i "Set the window to capture. Defaults to the root window id."
48 int
49 optional
50
51 option "localize" - "Localizes given geometry to the given window. So \"maim -i $ID -g 100x100+0+0 --localize\" would screenshot the top-left 100x100 pixels of the given window, rather than the top-left 100x100 pixels of the root window."
52 flag
53 off
54
55 option "hidecursor" - "Prevents the system cursor from showing up in screenshots."
56 flag
57 off
58
59 option "mask" m "Masks off-screen pixels so they don't show up in screenshots."
60 string
61 values="auto","off","on"
62 default="auto"
63 optional
64
65 text "\nSlop Options"
66
67 option "nokeyboard" - "Disables the ability to cancel selections with the keyboard."
68 flag
69 off
70
71 option "bordersize" b "Set the selection rectangle's thickness. Does nothing when --highlight is enabled."
72 int
73 default="5"
74 optional
75
76 option "padding" p "Set the padding size of the selection. Can be negative."
77 int
78 default="0"
79 optional
80
81 option "tolerance" t "How far in pixels the mouse can move after clicking and still be detected as a normal click instead of a click and drag. Setting this to 0 will disable window selections."
82 int
83 default="2"
84 optional
85
86 option "gracetime" - "Set the amount of time before slop will check for keyboard cancellations in seconds."
87 string
88 typestr="FLOAT"
89 default="0.4"
90 optional
91
92 option "color" c "Set the selection rectangle's color. Supports RGB or RGBA values."
93 string
94 typestr="FLOAT,FLOAT,FLOAT,FLOAT"
95 default="0.5,0.5,0.5,1"
96 optional
97
98 option "nodecorations" n "Attempt to select child windows in order to avoid window decorations."
99 flag
100 off
101
102 option "min" - "Set the minimum output of width or height values. This is useful to avoid outputting 0. Setting min and max to the same value disables drag selections."
103 int
104 default="0"
105 optional
106
107 option "max" - "Set the maximum output of width or height values. Setting min and max to the same value disables drag selections."
108 int
109 default="0"
110 optional
111
112 option "highlight" l "Instead of outlining selections, slop highlights it. This is only useful when --color is set to a transparent color."
113 flag
114 off
115
116 text "\nExamples\n"
117 text " $ # Screenshot the active window\n"
118 text " $ maim -i $(xdotool getactivewindow)\n"
119 text "\n"
120 text " $ # Prompt a transparent red selection to screenshot.\n"
121 text " $ maim -s -c 1,0,0,0.6\n"
122 text "\n"
123 text " $ # Save a dated screenshot.\n"
124 text " $ maim ~/$(date +%F-%T).png\n"
0 /* x.cpp: Handles starting and managing X
1 *
2 * Copyright (C) 2014: Dalton Nell, Maim Contributors (https://github.com/naelstrof/maim/graphs/contributors).
3 *
4 * This file is part of Maim.
5 *
6 * Maim is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Maim is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with Maim. If not, see <http://www.gnu.org/licenses/>.
18 */
19
200 #include "x.hpp"
211
22 maim::XEngine* xengine = new maim::XEngine();
23
24 maim::XEngine::XEngine() {
25 m_display = NULL;
26 m_visual = NULL;
27 m_screen = NULL;
28 m_good = false;
29 }
30
31 maim::XEngine::~XEngine() {
32 if ( !m_good ) {
33 return;
34 }
35 XCloseDisplay( m_display );
36 }
37
38 int maim::XEngine::init( std::string display ) {
39 // Initialize display
40 m_display = XOpenDisplay( display.c_str() );
41 if ( !m_display ) {
42 fprintf( stderr, "Error: Failed to open X display %s\n", display.c_str() );
43 return 1;
44 }
45 m_screen = ScreenOfDisplay( m_display, DefaultScreen( m_display ) );
46 m_visual = DefaultVisual ( m_display, XScreenNumberOfScreen( m_screen ) );
47 m_colormap = DefaultColormap( m_display, XScreenNumberOfScreen( m_screen ) );
48 m_root = RootWindow ( m_display, XScreenNumberOfScreen( m_screen ) );
49 //m_root = DefaultRootWindow( m_display );
50
51 // We ignore X errors since we don't care if we fail to get
52 // the physical monitor positions.
53 XErrorHandler originalHandler = XSetErrorHandler( maim::IgnoreErrorHandler );
54 m_res = XRRGetScreenResourcesCurrent( m_display, m_root);
55 XSetErrorHandler( originalHandler );
56 if ( !m_res ) {
57 fprintf( stderr, "Warning: Failed to get screen resources. Multi-monitor X screens won't have garbage visual data removed.\n" );
58 }
59
60 m_good = true;
61 return EXIT_SUCCESS;
62 }
63
64 Window maim::XEngine::getWindowByID( int id ) {
65 // There's actually no way to check if the id is valid...
66 return (Window)id;
67 // The only thing we can do is use it and see if we get a BadWindow error later.
68 }
69
70 // This stuff is used to detect which pixels we can actually see with
71 // the physical monitor positions.
72 // It's useful for people with multimonitor setups where the monitors
73 // don't fit together well since we can black out the pixels that are
74 // generally just garbage.
75 std::vector<XRRCrtcInfo*> maim::XEngine::getCRTCS() {
76 std::vector<XRRCrtcInfo*> monitors;
77 if ( !m_res ) {
78 return monitors;
79 }
80 for ( int i=0;i<m_res->ncrtc;i++ ) {
81 monitors.push_back( XRRGetCrtcInfo( m_display, m_res, m_res->crtcs[ i ] ) );
82 }
83 return monitors;
84 }
85
86 void maim::XEngine::freeCRTCS( std::vector<XRRCrtcInfo*> monitors ) {
2 static char _x_err = 0;
3 static int
4 TmpXError(Display * d, XErrorEvent * ev) {
5 _x_err = 1;
6 return 0;
7 }
8
9 glm::ivec4 getWindowGeometry( X11* x11, Window win ) {
10 // First lets check for if we're a window manager frame.
11 Window root, parent;
12 Window* children;
13 unsigned int num_children;
14 XQueryTree( x11->display, win, &root, &parent, &children, &num_children);
15
16 // To do that, we check if our top level child happens to have the _NET_FRAME_EXTENTS atom.
17 unsigned char *data;
18 Atom type_return;
19 unsigned long nitems_return;
20 unsigned long bytes_after_return;
21 int format_return;
22 bool window_frame = false;
23 Window actualWindow = win;
24 if ( num_children > 0 && XGetWindowProperty( x11->display, children[num_children-1],
25 XInternAtom( x11->display, "_NET_FRAME_EXTENTS", False),
26 0, LONG_MAX, False, XA_CARDINAL, &type_return,
27 &format_return, &nitems_return, &bytes_after_return,
28 &data) == Success ) {
29 if ((type_return == XA_CARDINAL) && (format_return == 32) && (nitems_return == 4) && (data)) {
30 actualWindow = children[num_children-1];
31 window_frame = true;
32 }
33 }
34 XFree( children );
35
36 // If we're a window frame, we actually get the dimensions of the child window, then add the _NET_FRAME_EXTENTS to it.
37 // (then add the border width of the window frame after that.)
38 if ( window_frame ) {
39 // First lets grab the border width.
40 XWindowAttributes frameattr;
41 XGetWindowAttributes( x11->display, win, &frameattr );
42 // Then lets grab the dims of the child window.
43 XWindowAttributes attr;
44 XGetWindowAttributes( x11->display, actualWindow, &attr );
45 unsigned int width = attr.width;
46 unsigned int height = attr.height;
47 // We combine both border widths.
48 unsigned int border = attr.border_width+frameattr.border_width;
49 int x, y;
50 // Gotta translate them into root coords, we can adjust for the border width here.
51 Window junk;
52 XTranslateCoordinates( x11->display, actualWindow, attr.root, -border, -border, &x, &y, &junk );
53 width += border*2;
54 height += border*2;
55 // Now uh, remember that _NET_FRAME_EXTENTS stuff? That's the window frame information.
56 // We HAVE to do this because mutter likes to mess with window sizes with shadows and stuff.
57 unsigned long* ldata = (unsigned long*)data;
58 width += ldata[0] + ldata[1];
59 height += ldata[2] + ldata[3];
60 x -= ldata[0];
61 y -= ldata[2];
62 XFree( data );
63 return glm::vec4( x, y, width, height );
64 } else {
65 // Either the WM is malfunctioning, or the window secified isn't a window manager frame.
66 // so we just rely on X.
67 XWindowAttributes attr;
68 XGetWindowAttributes( x11->display, win, &attr );
69 unsigned int width = attr.width;
70 unsigned int height = attr.height;
71 // We combine both border widths.
72 unsigned int border = attr.border_width;
73 int x, y;
74 // Gotta translate them into root coords, we can adjust for the border width here.
75 Window junk;
76 XTranslateCoordinates( x11->display, win, attr.root, -border, -border, &x, &y, &junk );
77 width += border*2;
78 height += border*2;
79 return glm::vec4( x, y, width, height );
80 }
81 }
82
83 std::vector<XRRCrtcInfo*> X11::getCRTCS() {
84 std::vector<XRRCrtcInfo*> monitors;
85 if ( !res ) {
86 return monitors;
87 }
88 for ( int i=0;i<res->ncrtc;i++ ) {
89 monitors.push_back( XRRGetCrtcInfo( display, res, res->crtcs[ i ] ) );
90 }
91 return monitors;
92 }
93
94 void X11::freeCRTCS( std::vector<XRRCrtcInfo*> monitors ) {
8795 for ( unsigned int i=0;i<monitors.size();i++ ) {
8896 XRRFreeCrtcInfo( monitors[ i ] );
8997 }
9098 }
9199
92 int maim::IgnoreErrorHandler( Display* dpy, XErrorEvent* event ) {
93 return EXIT_SUCCESS;
94 }
100 X11::X11( std::string displayName ) {
101 // Initialize display
102 display = XOpenDisplay( displayName.c_str() );
103 if ( !display ) {
104 throw new std::runtime_error(std::string("Error: Failed to open X display: ") + displayName );
105 }
106 screen = ScreenOfDisplay( display, DefaultScreen( display ) );
107 visual = DefaultVisual( display, XScreenNumberOfScreen( screen ) );
108 root = DefaultRootWindow( display );
109 int major = 0;
110 int minor = 0;
111 Bool pixmaps = true;
112 haveXShm = (True == XShmQueryVersion( display, &major, &minor, &pixmaps ));
113 haveXShm = (haveXShm && pixmaps );
114 major = 2;
115 minor = 0;
116 haveXFixes = (True == XFixesQueryVersion ( display, &major, &minor ));
117 major = 0;
118 minor = 0;
119 haveXRR = (True == XRRQueryVersion( display, &major, &minor ) );
120 major = 0;
121 minor = 2;
122 haveXComposite = (True == XCompositeQueryVersion( display, &major, &minor ));
123 major = 0;
124 minor = 0;
125 haveXRender = (True == XRenderQueryVersion( display, &major, &minor ));
126
127 if ( haveXRR ) {
128 res = XRRGetScreenResourcesCurrent( display, root );
129 }
130 }
131
132 X11::~X11() {
133 if ( haveXRR ) {
134 XRRFreeScreenResources( res );
135 }
136 XCloseDisplay( display );
137 }
138
139 XImage* X11::getImage( Window draw, int x, int y, int w, int h, glm::ivec2& imageloc ) {
140 glm::ivec4 sourceGeo = getWindowGeometry( this, draw );
141 // We need to clamp the selection to fit within the
142 // provided window.
143
144 x = glm::clamp( x, sourceGeo.x, sourceGeo.x+sourceGeo.z );
145 y = glm::clamp( y, sourceGeo.y, sourceGeo.y+sourceGeo.w );
146 w = glm::clamp( w, 1, sourceGeo.x+sourceGeo.z-x );
147 h = glm::clamp( h, 1, sourceGeo.y+sourceGeo.w-y );
148
149 imageloc = glm::ivec2( x, y );
150
151 // Translate the newly clamped selection to local coordinates.
152 int localx, localy;
153 Window junk;
154 XTranslateCoordinates( this->display, this->root, draw, x, y, &localx, &localy, &junk);
155
156 if ( haveXComposite ) {
157 // We redirect all the pixmaps offscreen, so that they won't be corrupted if obscured.
158 for ( int i = 0; i < ScreenCount( display ); i++ ) {
159 XCompositeRedirectSubwindows( display, RootWindow( display, i ), CompositeRedirectAutomatic );
160 }
161 // We don't have to worry about undoing the redirect, since as soon as maim closes X knows to undo it.
162 }
163 if ( haveXRender && haveXFixes ) {
164 return getImageUsingXRender( draw, localx, localy, w, h );
165 }
166 // This stuff doesn't work very well...
167 //if ( haveXShm ) {
168 //XErrorHandler ph = XSetErrorHandler(TmpXError);
169 //XImage* check = getImageUsingXShm( draw, localx, localy, w, h );
170 //XSetErrorHandler(ph);
171 //if ( !_x_err && check != None ) {
172 //return check;
173 //}
174 //}
175 return XGetImage( display, draw, localx, localy, w, h, AllPlanes, ZPixmap );
176 }
177
178 XImage* X11::getImageUsingXRender( Window draw, int localx, int localy, int w, int h ) {
179 // We use XRender to grab the drawable, since it'll save it in a format we like.
180 XWindowAttributes attr;
181 XGetWindowAttributes( display, draw, &attr );
182 XRenderPictFormat *format = XRenderFindVisualFormat( display, attr.visual );
183 bool hasAlpha = ( format->type == PictTypeDirect && format->direct.alphaMask );
184 XRenderPictureAttributes pa;
185 pa.subwindow_mode = IncludeInferiors;
186 Picture picture = XRenderCreatePicture( display, draw, format, CPSubwindowMode, &pa );
187 if ( draw != root ) {
188 XserverRegion region = findRegion( draw );
189 // Also we use XRender because of this neato function here.
190 XFixesSetPictureClipRegion( display, picture, 0, 0, region );
191 XFixesDestroyRegion( display, region );
192 }
193
194 Pixmap pixmap = XCreatePixmap(display, root, w, h, 32);
195 XRenderPictureAttributes pa2;
196
197 XRenderPictFormat *format2 = XRenderFindStandardFormat(display, PictStandardARGB32);
198 Picture pixmapPicture = XRenderCreatePicture( display, pixmap, format2, 0, &pa2 );
199 XRenderColor c;
200 c.red = 0x0000;
201 c.green = 0x0000;
202 c.blue = 0x0000;
203 c.alpha = 0x0000;
204 XRenderFillRectangle (display, PictOpSrc, pixmapPicture, &c, 0, 0, w, h);
205 XRenderComposite(display, hasAlpha ? PictOpOver : PictOpSrc, picture, 0,
206 pixmapPicture, localx, localy, 0, 0, 0, 0,
207 w, h);
208 XImage* temp = XGetImage( display, pixmap, 0, 0, w, h, AllPlanes, ZPixmap );
209 temp->red_mask = format2->direct.redMask << format2->direct.red;
210 temp->green_mask = format2->direct.greenMask << format2->direct.green;
211 temp->blue_mask = format2->direct.blueMask << format2->direct.blue;
212 temp->depth = format2->depth;
213 return temp;
214 }
215
216 bool X11::hasClipping( Window d ) {
217 int bShaped, xbs, ybs, cShaped, xcs, ycs;
218 unsigned int wbs, hbs, wcs, hcs;
219 XShapeQueryExtents ( display, d, &bShaped, &xbs, &ybs, &wbs, &hbs, &cShaped, &xcs, &ycs, &wcs, &hcs );
220 return bShaped;
221 }
222
223 XserverRegion X11::findRegion( Window d ) {
224 XserverRegion rootRegion = XFixesCreateRegionFromWindow( display, d, WindowRegionBounding );
225 glm::vec4 rootgeo = getWindowGeometry( this, d );
226 XFixesTranslateRegion( display, rootRegion, rootgeo.x, rootgeo.y ); // Regions are in respect to the root window by default.
227 unionClippingRegions( rootRegion, d );
228 unionBorderRegions( rootRegion, d );
229 return rootRegion;
230 }
231
232 void X11::unionBorderRegions( XserverRegion rootRegion, Window d ) {
233 glm::vec4 bordergeo = getWindowGeometry( this, d );
234 XRectangle* rects = new XRectangle[1];
235 rects[0].x = bordergeo.x;
236 rects[0].y = bordergeo.y;
237 rects[0].width = bordergeo.z;
238 rects[0].height = bordergeo.w;
239 XserverRegion borderRegionRect = XFixesCreateRegion( display, rects, 1 );
240 XWindowAttributes attr;
241 XGetWindowAttributes( display, d, &attr );
242 rects[0].x += attr.border_width;
243 rects[0].y += attr.border_width;
244 rects[0].width -= attr.border_width*2;
245 rects[0].height -= attr.border_width*2;
246 XserverRegion regionRect = XFixesCreateRegion( display, rects, 1 );
247 XFixesSubtractRegion( display, regionRect, borderRegionRect, regionRect );
248 delete[] rects;
249 XFixesUnionRegion( display, rootRegion, rootRegion, regionRect );
250 XFixesDestroyRegion( display, regionRect );
251 XFixesDestroyRegion( display, borderRegionRect );
252 }
253
254 void X11::unionClippingRegions( XserverRegion rootRegion, Window child ) {
255 Window root, parent;
256 Window* children;
257 unsigned int num_children;
258 XQueryTree( display, child, &root, &parent, &children, &num_children);
259 for ( unsigned int i=0;i<num_children;i++ ) {
260 if ( hasClipping( children[i] ) ) {
261 Window clippingWindow = children[i];
262 glm::vec4 geo = getWindowGeometry( this, clippingWindow );
263 XRectangle* rects = new XRectangle[1];
264 rects[0].x = geo.x;
265 rects[0].y = geo.y;
266 rects[0].width = geo.z;
267 rects[0].height = geo.w;
268 // We have to keep the parent's region from interfering the clipping region.
269 XserverRegion clippingWindowRect = XFixesCreateRegion( display, rects, 1 );
270 delete[] rects;
271 // So we cut a neat hole the size of the child window where the clipping happens.
272 XFixesSubtractRegion( display, rootRegion, rootRegion, clippingWindowRect );
273 XFixesDestroyRegion( display, clippingWindowRect );
274 XserverRegion childRegion = XFixesCreateRegionFromWindow( display, clippingWindow, WindowRegionBounding );
275 XFixesTranslateRegion( display, childRegion, geo.x, geo.y ); // Regions are in respect to the root window by default.
276 XFixesUnionRegion( display, rootRegion, rootRegion, childRegion );
277 XFixesDestroyRegion( display, childRegion );
278 // We don't desend deeper down the tree looking for more clipping regions, I just don't want to support
279 // applications that do that...
280 } else {
281 unionClippingRegions( rootRegion, children[i] );
282 }
283 }
284 XFree( children );
285 }
286
287 XImage* X11::getImageUsingXShm(Window draw, int localx, int localy, int w, int h) {
288 XImage* xim;
289 XShmSegmentInfo thing;
290
291 XWindowAttributes xattr;
292 Status s = XGetWindowAttributes (display, draw, &xattr);
293
294 /* try create an shm image */
295 xim = XShmCreateImage(display, xattr.visual, xattr.depth, ZPixmap, 0, &thing, w, h);
296 if (!xim) {
297 return None;
298 }
299
300 /* get an shm id of this image */
301 thing.shmid = shmget(IPC_PRIVATE, xim->bytes_per_line * xim->height, IPC_CREAT | 0777);
302 /* if the get succeeds */
303 if (thing.shmid != -1) {
304 /* set the params for the shm segment */
305 thing.readOnly = False;
306 thing.shmaddr = xim->data = (char*)shmat(thing.shmid, 0, 0);
307 /* get the shm addr for this data chunk */
308 if (xim->data != (char *)-1) {
309 XShmAttach(display, &thing);
310 XShmGetImage(display, draw, xim, localx, localy, AllPlanes);
311 return xim;
312 //shmdt(thing.shmaddr);
313 }
314 /* get failed - out of shm id's or shm segment too big ? */
315 /* remove the shm id we created */
316 shmctl(thing.shmid, IPC_RMID, 0);
317 shmdt(thing.shmaddr);
318 }
319 return None;
320 }
0 /* x.hpp: Handles starting and managing X
0 /* x.hpp: initializes x11
11 *
2 * Copyright (C) 2014: Dalton Nell, Maim Contributors (https://github.com/naelstrof/maim/graphs/contributors).
2 * Copyright (C) 2014: Dalton Nell, Maim Contributors (https://github.com/naelstrof/slop/graphs/contributors).
33 *
44 * This file is part of Maim.
55 *
1717 * along with Maim. If not, see <http://www.gnu.org/licenses/>.
1818 */
1919
20 #ifndef MAIM_X_H_
21 #define MAIM_X_H_
20 #ifndef N_X_H_
21 #define N_X_H_
2222
23 #include <iostream>
2324 #include <X11/Xlib.h>
25 #include <X11/Xatom.h>
26 #include <X11/extensions/XShm.h>
27 #include <X11/extensions/Xrender.h>
28 #include <X11/extensions/shape.h>
29 #include <X11/extensions/Xcomposite.h>
2430 #include <X11/extensions/Xrandr.h>
25 #include <cstdlib>
31 //#include <meta/meta-shadow-factory.h>
32 #include <sys/shm.h>
33 #include <string>
2634 #include <vector>
27 #include <string>
28 #include <cstdio>
35 #include <stdexcept>
36 #include <glm/glm.hpp>
2937
30 namespace maim {
31
32 class XEngine {
38 class X11 {
39 private:
40 bool hasClipping( Window d );
41 XserverRegion findRegion( Window d );
42 void unionClippingRegions( XserverRegion rootRegion, Window child );
43 void unionBorderRegions( XserverRegion rootRegion, Window d );
44 XImage* getImageUsingXRender( Window draw, int localx, int localy, int w, int h );
45 XImage* getImageUsingXShm( Window draw, int localx, int localy, int w, int h );
3346 public:
34 XEngine();
35 ~XEngine();
36 int init( std::string display );
37 Display* m_display;
38 Visual* m_visual;
39 Screen* m_screen;
40 Colormap m_colormap;
41 Window m_root;
42 XRRScreenResources* m_res;
43 bool m_good;
44 Window getWindowByID( int id );
45 std::vector<XRRCrtcInfo*> getCRTCS();
46 void freeCRTCS( std::vector<XRRCrtcInfo*> monitors );
47 bool haveXComposite;
48 bool haveXRender;
49 bool haveXShm;
50 bool haveXFixes;
51 bool haveXRR;
52 X11( std::string displayName );
53 ~X11();
54 Display* display;
55 Visual* visual;
56 Screen* screen;
57 Window root;
58 XImage* getImage( Window d, int x, int y, int w, int h, glm::ivec2& imageloc );
59 XRRScreenResources* res;
60 std::vector<XRRCrtcInfo*> getCRTCS();
61 void freeCRTCS( std::vector<XRRCrtcInfo*> monitors );
4762 };
4863
49 int IgnoreErrorHandler( Display* dpy, XErrorEvent* event );
64 glm::ivec4 getWindowGeometry( X11* x11, Window win );
5065
51 }
52
53 extern maim::XEngine* xengine;
54
55 #endif // MAIM_X_H_
66 #endif
+0
-44
unitTests.sh less more
0 #!/bin/sh
1
2 function test {
3 "$@"
4 local status=$?
5 if [ $status -ne 0 ]; then
6 echo "Error test \"$@\" failed with $status, should be 0!"
7 exit 1
8 fi
9 }
10
11 function test_fail {
12 "$@"
13 local status=$?
14 if [ $status -ne 1 ]; then
15 echo "Error test \"$@\" failed with $status, should be 1!"
16 exit 1
17 fi
18 }
19
20 echo "Starting unit tests..."
21 test ./maim
22 # Variable expansion tests.
23 test ./maim ~/test.png
24 test ./maim "/tmp/should be entire x screen.png"
25
26 # Buffer overflow tests. Lots of characters depending on the username. With naelstrof it's over 2.5k which is over the default MAXPATHLENGTH of 1024.
27 test ./maim ~/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/../$USER/test.png
28 test ./maim "/tmp/should be 1920x1080+0+0.png" -g 1920x1080+0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000+-00000000000000000000000000000000000000000000000000000000000000000000000000000
29 test ./maim "/tmp/should be 1920x1080+0+0 also.png" -x 0000000000000000000000000000000000000000000000000000 -y 0000000000000000000000000000000000000000000 -w 1920 -h 1080
30 test ./maim "/tmp/should be what you selected first.png" -s -c 1.00000000000000000000000000000000000000000000,1.00000000000000000000000000000000000000000000000,1.0000000000000000000000000000000000000000000000000,1.0000000000000000000000000000000000000000000000000
31 # Color RGB test (vs RGBA)
32 test ./maim "/tmp/should be what you selected second.png" -s -c 1,0,0
33
34 # Failure cases.
35 test_fail ./maim "/tmp/$(date +%s-%N).png" "~/whyamihere.png" 2>/dev/null
36 test_fail ./maim "/tmp/$(date +%s-%N).png" -x 0 -y 0 -w 100 2>/dev/null
37 test_fail ./maim "/tmp/$(date +%s-%N).png" -g thisisntageo 2>/dev/null
38 test_fail ./maim "/tmp/should.not.exist.extension" 2>/dev/null
39 test_fail ./maim -d notafloat "/tmp/should.not.exist.png" 2>/dev/null
40
41 rm ~/test.png
42
43 echo "Unit tests finished without error! Check /tmp for any awkward/broken screenshots."