1 /* main.cpp: parses options, runs slop, prints results.
2 *
3 * Copyright (C) 2014: Dalton Nell, Slop Contributors (https://github.com/naelstrof/slop/graphs/contributors).
4 *
5 * This file is part of Slop.
6 *
7 * Slop is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * Slop is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with Slop. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include <iostream>
22 #include <sstream>
23 #include <string>
24 #include <vector>
25 #include <glm/glm.hpp>
26
27 #include "slop.hpp"
28 #include "cxxopts.hpp"
29
30 using namespace slop;
31
parseColor(std::string value)32 glm::vec4 parseColor( std::string value ) {
33 std::string valuecopy = value;
34 glm::vec4 found;
35 std::string::size_type sz;
36 try {
37 found[0] = std::stof(value,&sz);
38 value = value.substr(sz+1);
39 found[1] = std::stof(value,&sz);
40 value = value.substr(sz+1);
41 found[2] = std::stof(value,&sz);
42 if ( value.size() != sz ) {
43 value = value.substr(sz+1);
44 found[3] = std::stof(value,&sz);
45 if ( value.size() != sz ) {
46 throw std::runtime_error("dur");
47 }
48 } else {
49 found[3] = 1;
50 }
51 } catch ( ... ) {
52 throw 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.");
53 }
54 return found;
55 }
56
getOptions(cxxopts::Options & options)57 SlopOptions* getOptions( cxxopts::Options& options ) {
58 slop::SlopOptions* foo = new slop::SlopOptions();
59 if ( options.count( "bordersize" ) > 0 ) {
60 foo->border = options["bordersize"].as<float>();
61 }
62 if ( options.count( "padding" ) > 0 ) {
63 foo->padding = options["padding"].as<float>();
64 }
65 if ( options.count( "tolerance" ) > 0 ) {
66 foo->tolerance = options["tolerance"].as<float>();
67 }
68 glm::vec4 color = glm::vec4( foo->r, foo->g, foo->b, foo->a );
69 if ( options.count( "color" ) > 0 ) {
70 color = parseColor( options["color"].as<std::string>() );
71 }
72 foo->r = color.r;
73 foo->g = color.g;
74 foo->b = color.b;
75 foo->a = color.a;
76 if ( options.count( "nokeyboard" ) > 0 ) {
77 foo->nokeyboard = options["nokeyboard"].as<bool>();
78 }
79 if ( options.count( "xdisplay" ) > 0 ) {
80 std::string xdisplay = options["xdisplay"].as<std::string>();
81 char* cxdisplay = new char[xdisplay.length()+1];
82 memcpy( cxdisplay, xdisplay.c_str(), xdisplay.length() );
83 cxdisplay[xdisplay.length()]='\0';
84 foo->xdisplay = cxdisplay;
85 }
86 if ( options.count( "shader" ) > 0 ) {
87 std::string shaders = options["shader"].as<std::string>();
88 char* cshaders = new char[shaders.length()+1];
89 memcpy( cshaders, shaders.c_str(), shaders.length() );
90 cshaders[shaders.length()]='\0';
91 foo->shaders = cshaders;
92 }
93 if ( options.count( "noopengl" ) > 0 ) {
94 foo->noopengl = options["noopengl"].as<bool>();
95 }
96 if ( options.count( "highlight" ) > 0 ) {
97 foo->highlight = options["highlight"].as<bool>();
98 }
99 if ( options.count( "nodrag" ) > 0 ) {
100 foo->nodrag = options["nodrag"].as<bool>();
101 }
102 if ( options.count( "quiet" ) > 0 ) {
103 foo->quiet = options["quiet"].as<bool>();
104 }
105 if ( options.count( "nodecorations" ) > 0 ) {
106 foo->nodecorations = options["nodecorations"].as<int>();
107 if ( foo->nodecorations < 0 || foo->nodecorations > 2 ) {
108 throw std::invalid_argument( "--nodecorations must be between 0 and 2. Or be used as a flag." );
109 }
110 }
111 return foo;
112 }
113
formatOutput(std::string input,SlopSelection selection)114 std::string formatOutput( std::string input, SlopSelection selection ) {
115 std::stringstream output;
116 for( unsigned int i=0;i<input.length();i++) {
117 if ( input[i] == '%' ) {
118 if ( input.length() <= i+1 ) {
119 throw std::invalid_argument( "Expected character after `%`, got END." );
120 }
121 switch( input[i+1] ) {
122 case 'x':
123 case 'X': output << round(selection.x); break;
124 case 'y':
125 case 'Y': output << round(selection.y); break;
126 case 'w':
127 case 'W': output << round(selection.w); break;
128 case 'c':
129 case 'C': output << selection.cancelled; break;
130 case 'h':
131 case 'H': output << round(selection.h); break;
132 case 'g':
133 case 'G': output << round(selection.w) << "x" << round(selection.h)
134 << "+" << round(selection.x) << "+" << round(selection.y); break;
135 case 'i':
136 case 'I': output << selection.id; break;
137 case '%': output << "%"; break;
138 default: throw std::invalid_argument( std::string()+"Expected x, y, w, h, g, i, c, or % after % in format. Got `" + input[i+1] + "`." );
139 }
140 i++;
141 continue;
142 }
143 output << input[i];
144 }
145 return output.str();
146 }
147
printHelp()148 void printHelp() {
149 std::cout << "slop " << SLOP_VERSION << "\n";
150 std::cout << "\n";
151 std::cout << "Copyright (C) 2017 Dalton Nell, Slop Contributors\n";
152 std::cout << "(https://github.com/naelstrof/slop/graphs/contributors)\n";
153 std::cout << "Usage: slop [options]\n";
154 std::cout << "\n";
155 std::cout << "slop (Select Operation) is an application that queries for a selection from the\n";
156 std::cout << "user and prints the region to stdout.\n";
157 std::cout << "\n";
158 std::cout << "-h, --help Print help and exit\n";
159 std::cout << "-v, --version Print version and exit\n";
160 std::cout << "Options\n";
161 std::cout << " -x, --xdisplay=hostname:number.screen_number\n";
162 std::cout << " Sets the x display.\n";
163 std::cout << " -k, --nokeyboard Disables the ability to cancel selections with\n";
164 std::cout << " the keyboard. (default=off)\n";
165 std::cout << " -b, --bordersize=FLOAT Set the selection rectangle's thickness.\n";
166 std::cout << " (default=`1')\n";
167 std::cout << " -p, --padding=FLOAT Set the padding size of the selection. Can be\n";
168 std::cout << " negative. (default=`0')\n";
169 std::cout << " -t, --tolerance=FLOAT How far in pixels the mouse can move after\n";
170 std::cout << " clicking and still be detected as a normal\n";
171 std::cout << " click instead of a click and drag. Setting\n";
172 std::cout << " this to 0 will disable window selections.\n";
173 std::cout << " Alternatively setting it to 999999 would.\n";
174 std::cout << " only allow for window selections.\n";
175 std::cout << " (default=`2')\n";
176 std::cout << " -D, --nodrag Select region with two clicks instead of\n";
177 std::cout << " click and drag\n";
178 std::cout << " -c, --color=FLOAT,FLOAT,FLOAT,FLOAT\n";
179 std::cout << " Set the selection rectangle's color. Supports\n";
180 std::cout << " RGB or RGBA values.\n";
181 std::cout << " (default=`0.5,0.5,0.5,1')\n";
182 std::cout << " -n, --nodecorations=INT Attempt to select child windows in order to\n";
183 std::cout << " avoid window decorations. Setting this to\n";
184 std::cout << " 1 will enable a light attempt to\n";
185 std::cout << " remove decorations. Setting this to 2 will\n";
186 std::cout << " enable aggressive decoration removal.\n";
187 std::cout << " Supplying slop with just `-n` is\n";
188 std::cout << " equivalent to supplying `-n1`.\n";
189 std::cout << " (default=`0')\n";
190 std::cout << " -D, --nodrag Allows you to click twice to indicate a \n";
191 std::cout << " selection, rather than click-dragging.\n";
192 std::cout << " -q, --quiet Disable any unnecessary cerr output. Any\n";
193 std::cout << " warnings simply won't print.\n";
194 std::cout << " -l, --highlight Instead of outlining selections, slop\n";
195 std::cout << " highlights it. This is only useful when\n";
196 std::cout << " --color is set to a transparent color.\n";
197 std::cout << " (default=off)\n";
198 std::cout << " -r, --shader=STRING Sets the shader to load and use from\n";
199 std::cout << " ~/.config/slop/\n";
200 std::cout << " -f, --format=STRING Set the output format string. Format specifiers\n";
201 std::cout << " are %x (x offset), %y (y offset), %w (width),\n";
202 std::cout << " %h (height), %i (window id),\n";
203 std::cout << " %g (geometry - `%wx%h+%x+%y\'),\n";
204 std::cout << " %c (1 if cancelled, 0 otherwise),\n";
205 std::cout << " and %% for a literal percent sign.\n";
206 std::cout << " (default=`%g')\n";
207 std::cout << " -o, --noopengl Disable graphics acceleration.\n";
208 std::cout << "Examples\n";
209 std::cout << " $ # Gray, thick, transparent border for maximum visiblity.\n";
210 std::cout << " $ slop -b 20 -c 0.5,0.5,0.5,0.8\n";
211 std::cout << "\n";
212 std::cout << " $ # Remove window decorations.\n";
213 std::cout << " $ slop --nodecorations\n";
214 std::cout << "\n";
215 std::cout << " $ # Disable window selections. Useful for selecting individual pixels.\n";
216 std::cout << " $ slop -t 0\n";
217 std::cout << "\n";
218 std::cout << " $ # Classic Windows XP selection.\n";
219 std::cout << " $ slop -l -c 0.3,0.4,0.6,0.4\n";
220 std::cout << "\n";
221 std::cout << " $ # Read slop output for use in scripts.\n";
222 std::cout << " $ read -r X Y W H G ID < <(slop -f '%x %y %w %h %g %i')\n";
223 std::cout << "\n";
224 std::cout << "Tips\n";
225 std::cout << " * If you don't like a selection: you can cancel it by right-clicking\n";
226 std::cout << "regardless of which options are enabled or disabled for slop.\n";
227 std::cout << " * You can move the selection by holding down the space bar!\n";
228 std::cout << " * If slop doesn't seem to select a window accurately, the problem could be\n";
229 std::cout << "because of decorations getting in the way. Try enabling the --nodecorations\n";
230 std::cout << "flag.\n";
231 }
232
app(int argc,char ** argv)233 int app( int argc, char** argv ) {
234 cxxopts::Options options("maim", "Screenshot application.");
235 options.add_options()
236 ("h,help", "Print help and exit.")
237 ("v,version", "Print version and exit.")
238 ("x,xdisplay", "Sets the xdisplay to use", cxxopts::value<std::string>())
239 ("f,format", "Sets the output format for slop. Format specifiers are %x, %y, %w, %h, %i, %c, and %g. If actual percentage signs are desired in output, use a double percentage sign like so `%%`.", cxxopts::value<std::string>())
240 ("b,bordersize", "Sets the selection rectangle's thickness.", cxxopts::value<float>())
241 ("p,padding", "Sets the padding size for the selection, this can be negative.", cxxopts::value<float>())
242 ("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>())
243 ("D,nodrag", "Select region with two clicks instead of click and drag")
244 ("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>())
245 ("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>())
246 ("n,nodecorations", "Sets the level of aggressiveness when trying to remove window decorations. `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"))
247 ("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.")
248 ("q,quiet", "Disable any unnecessary cerr output. Any warnings or info simply won't print.")
249 ("k,nokeyboard", "Disables the ability to cancel selections with the keyboard.")
250 ("o,noopengl", "Disables graphics hardware acceleration.")
251 ("positional", "Positional parameters", cxxopts::value<std::vector<std::string>>())
252 ;
253 options.parse_positional("positional");
254 options.parse(argc, argv);
255 // Options just validates all of our input from argv
256 auto& positional = options["positional"].as<std::vector<std::string>>();
257 if ( positional.size() > 0 ) {
258 throw std::invalid_argument("Unexpected positional argument: " + positional[0]);
259 }
260 bool help = false;
261 if ( options.count( "help" ) > 0 ) {
262 help = options["help"].as<bool>();
263 }
264 if ( help ) {
265 printHelp();
266 return 0;
267 }
268 bool version = false;
269 if ( options.count( "version" ) > 0 ) {
270 version = options["version"].as<bool>();
271 }
272 if ( version ) {
273 std::cout << SLOP_VERSION << "\n";
274 return 0;
275 }
276 // We then parse the options into something slop can understand.
277 SlopOptions* parsedOptions = getOptions( options );
278
279 // We want to validate our format option if we got one, we do that by just doing a dry run
280 // on a fake selection.
281 SlopSelection selection(0,0,0,0,0,true);
282 std::string format;
283 bool gotFormat = options.count( "format" ) > 0;
284 if ( gotFormat ) {
285 format = options["format"].as<std::string>();
286 formatOutput( format, selection );
287 }
288
289 // Finally we do the real selection.
290 selection = SlopSelect(parsedOptions);
291
292 bool quiet = parsedOptions->quiet;
293 // Here we're done with the parsed option data.
294 delete parsedOptions;
295 // We know if we cancelled or not
296 if ( selection.cancelled ) {
297 if ( !quiet ) {
298 std::cerr << "Selection was cancelled by keystroke or right-click.\n";
299 }
300 return 1;
301 }
302 // If we recieved a format option, we output the specified output.
303 if ( gotFormat ) {
304 std::cout << formatOutput( format, selection );
305 return 0;
306 }
307 std::cout << formatOutput( "%g\n", selection );
308 return 0;
309 }
310
main(int argc,char ** argv)311 int main( int argc, char** argv ) {
312 try {
313 return app( argc, argv );
314 } catch( std::exception& e ) {
315 std::cerr << "Slop encountered an error:\n" << e.what() << "\n";
316 return 1;
317 } // let the operating system handle any other kind of exception.
318 return 1;
319 }
320