New upstream version 5.4.68
Antoine Beaupré
6 years ago
0 | cmake_minimum_required( VERSION 2.8 ) | |
0 | cmake_minimum_required( VERSION 3.1.3 ) | |
1 | 1 | |
2 | 2 | project( "maim" ) |
3 | set( maim_VERSION_MAJOR 3 ) | |
4 | set( maim_VERSION_MINOR 3 ) | |
5 | set( maim_VERSION_PATCH 41 ) | |
6 | 3 | |
7 | set( BIN_TARGET "${PROJECT_NAME}" ) | |
4 | set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build (Debug or Release)") | |
8 | 5 | |
9 | 6 | if ( NOT CMAKE_INSTALL_PREFIX ) |
10 | set( CMAKE_INSTALL_PREFIX "/usr" ) | |
7 | set(CMAKE_INSTALL_PREFIX "/usr") | |
11 | 8 | endif() |
12 | 9 | |
13 | 10 | set( CMAKE_INSTALL_MANDIR "${CMAKE_INSTALL_PREFIX}/share/man" CACHE PATH "Directory where man pages reside. (/usr/share/man, /usr/local/share/man, etc.)" ) |
15 | 12 | set( CMAKE_COMPRESS_MAN TRUE CACHE BOOL "Whether or not to compress the man pages for install." ) |
16 | 13 | |
17 | 14 | if ( CMAKE_COMPRESS_MAN ) |
18 | set( MANTARGET "man-src/maim.1.gz" ) | |
15 | set( MANTARGET "maim.1.gz" ) | |
19 | 16 | else() |
20 | set( MANTARGET "man-src/maim.1" ) | |
17 | set( MANTARGET "maim.1" ) | |
21 | 18 | endif() |
22 | 19 | |
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") | |
26 | 21 | |
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/") | |
90 | 23 | |
91 | 24 | # Sources |
92 | 25 | set( source |
93 | src/cmdline.c | |
94 | src/im.cpp | |
95 | 26 | src/x.cpp |
27 | src/image.cpp | |
96 | 28 | src/main.cpp ) |
97 | 29 | |
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}" ) | |
113 | 31 | |
114 | 32 | # Executable |
115 | 33 | add_executable( "${BIN_TARGET}" ${source} ) |
116 | 34 | |
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 | ||
117 | 61 | # Libraries |
118 | target_link_libraries( "${BIN_TARGET}" | |
119 | ${IMLIB2_LIBRARIES} | |
62 | target_link_libraries( ${BIN_TARGET} | |
63 | ${CMAKE_THREAD_LIBS_INIT} | |
120 | 64 | ${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} ) | |
123 | 72 | |
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() | |
126 | 85 | |
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 ) |
0 | 0 | # 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. | |
3 | 2 | |
4 | 3 | ## 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. | |
8 | 7 | |
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. | |
11 | 12 | |
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)! | |
13 | 16 | |
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) | |
15 | 18 | |
16 | ![screenshot mask comparison](http://farmpolice.com/content/images/mask_compare2.png) | |
17 | 19 | |
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 | |
29 | 21 | |
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) | |
62 | 23 | * [Arch Linux: community/maim](https://www.archlinux.org/packages/community/x86_64/maim/) |
24 | * [Debian: maim](https://tracker.debian.org/pkg/maim) | |
63 | 25 | * [Void Linux: maim](https://github.com/voidlinux/void-packages/blob/24ac22af44018e2598047e5ef7fd3522efa79db5/srcpkgs/maim/template) |
64 | 26 | * [FreeBSD: graphics/maim](http://www.freshports.org/graphics/maim/) |
65 | 27 | * [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) | |
66 | 32 | * Please make a package for maim on your favorite system, and make a pull request to add it to this list. |
67 | 33 | |
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) | |
73 | 35 | ```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 .. | |
74 | 41 | git clone https://github.com/naelstrof/maim.git |
75 | 42 | cd maim |
76 | cmake ./ | |
43 | cmake -DCMAKE_INSTALL_PREFIX="/usr" ./ | |
77 | 44 | make && sudo make install |
78 | 45 | ``` |
79 | 46 | |
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. | |
81 | 49 | |
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 | ``` | |
87 | 54 | |
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 | ``` | |
90 | 59 | |
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 | ``` | |
92 | 64 | |
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 | ``` | |
94 | 69 | |
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 | ``` | |
98 | 74 | |
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 | ``` | |
126 | 79 | |
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 | |
170 | 83 | ``` |
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 | # - 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 | #!/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) |
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\. |
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><naelstrof@gmail.com></code> and Maim Contributors <code><http://github.com/naelstrof/maim/graphs/contributors></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 | 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 | /* | |
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, ¶ms, 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 | /** @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 | /* 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 | /* 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> | |
29 | 4 | #include <sstream> |
30 | #include <time.h> | |
31 | ||
5 | #include <thread> | |
6 | #include <X11/extensions/shape.h> | |
7 | ||
8 | #include "cxxopts.hpp" | |
32 | 9 | #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 ) { | |
166 | 484 | 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; | |
182 | 505 | } 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 | } | |
269 | 523 | } |
270 | 524 | } |
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; | |
365 | 579 | } |
366 | 580 | |
367 | 581 | int main( int argc, char** argv ) { |
368 | 582 | try { |
369 | 583 | 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 | 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 | ||
20 | 0 | #include "x.hpp" |
21 | 1 | |
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 ) { | |
87 | 95 | for ( unsigned int i=0;i<monitors.size();i++ ) { |
88 | 96 | XRRFreeCrtcInfo( monitors[ i ] ); |
89 | 97 | } |
90 | 98 | } |
91 | 99 | |
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 | |
1 | 1 | * |
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). | |
3 | 3 | * |
4 | 4 | * This file is part of Maim. |
5 | 5 | * |
17 | 17 | * along with Maim. If not, see <http://www.gnu.org/licenses/>. |
18 | 18 | */ |
19 | 19 | |
20 | #ifndef MAIM_X_H_ | |
21 | #define MAIM_X_H_ | |
20 | #ifndef N_X_H_ | |
21 | #define N_X_H_ | |
22 | 22 | |
23 | #include <iostream> | |
23 | 24 | #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> | |
24 | 30 | #include <X11/extensions/Xrandr.h> |
25 | #include <cstdlib> | |
31 | //#include <meta/meta-shadow-factory.h> | |
32 | #include <sys/shm.h> | |
33 | #include <string> | |
26 | 34 | #include <vector> |
27 | #include <string> | |
28 | #include <cstdio> | |
35 | #include <stdexcept> | |
36 | #include <glm/glm.hpp> | |
29 | 37 | |
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 ); | |
33 | 46 | 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 ); | |
47 | 62 | }; |
48 | 63 | |
49 | int IgnoreErrorHandler( Display* dpy, XErrorEvent* event ); | |
64 | glm::ivec4 getWindowGeometry( X11* x11, Window win ); | |
50 | 65 | |
51 | } | |
52 | ||
53 | extern maim::XEngine* xengine; | |
54 | ||
55 | #endif // MAIM_X_H_ | |
66 | #endif |
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." |