diff --git a/slop.1 b/slop.1 index 6931895..4f0d722 100644 --- a/slop.1 +++ b/slop.1 @@ -27,6 +27,9 @@ Sets the padding size for the selection, this can be negative. .BR \-t ", " \-\-tolerance=\fIFLOAT\fR 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. .TP +.BR \-a ", " \-\-aspectratio=\fIFLOAT,FLOAT\fR +Set the selection rectangle's aspect ratio, for example `16,9'. +.TP .BR \-c ", " \-\-color=\fIFLOAT,FLOAT,FLOAT,FLOAT\fR 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. .TP diff --git a/src/main.cpp b/src/main.cpp index 4bcb4ec..03c6361 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -29,6 +29,23 @@ using namespace slop; +glm::vec2 parseAspectRatio( std::string value ) { + std::string valuecopy = value; + glm::vec2 found; + std::string::size_type sz; + try { + found[0] = std::stof(value,&sz); + value = value.substr(sz+1); + found[1] = std::stof(value,&sz); + if (value.size() != sz) { + throw std::runtime_error("dur"); + } + } catch ( ... ) { + throw std::invalid_argument("Unable to parse value `" + valuecopy + "` as a aspect ratio. Should be in the format width,height. Like 16,9."); + } + return found; +} + glm::vec4 parseColor( std::string value ) { std::string valuecopy = value; glm::vec4 found; @@ -65,6 +82,15 @@ SlopOptions* getOptions( cxxopts::Options& options ) { if ( options.count( "tolerance" ) > 0 ) { foo->tolerance = options["tolerance"].as(); } + glm::vec2 aspectRatio = glm::vec2(foo->x_ratio, foo->y_ratio); + if ( options.count( "aspectratio" ) > 0) { + aspectRatio = parseAspectRatio( options["aspectratio"].as() ); + if (aspectRatio.x == 0 || aspectRatio.y == 0) { + throw std::invalid_argument("--aspectratio must not contain zero values"); + } + } + foo->x_ratio = aspectRatio.x; + foo->y_ratio = aspectRatio.y; glm::vec4 color = glm::vec4( foo->r, foo->g, foo->b, foo->a ); if ( options.count( "color" ) > 0 ) { color = parseColor( options["color"].as() ); @@ -173,6 +199,7 @@ void printHelp() { std::cout << " Alternatively setting it to 999999 would.\n"; std::cout << " only allow for window selections.\n"; std::cout << " (default=`2')\n"; + std::cout << " -a, --aspectratio=FLOAT,FLOAT Set the selection rectangle's aspect ratio.\n"; std::cout << " -D, --nodrag Select region with two clicks instead of\n"; std::cout << " click and drag\n"; std::cout << " -c, --color=FLOAT,FLOAT,FLOAT,FLOAT\n"; @@ -238,6 +265,7 @@ int app( int argc, char** argv ) { ("b,bordersize", "Sets the selection rectangle's thickness.", cxxopts::value()) ("p,padding", "Sets the padding size for the selection, this can be negative.", cxxopts::value()) ("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()) + ("a,aspectratio", "Sets the selection rectangle's aspect ratio.", cxxopts::value()) ("D,nodrag", "Select region with two clicks instead of click and drag") ("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()) ("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()) diff --git a/src/slop.cpp b/src/slop.cpp index 1181f81..1177d7d 100644 --- a/src/slop.cpp +++ b/src/slop.cpp @@ -76,6 +76,8 @@ slop::SlopOptions::SlopOptions() { padding = 0; shaders = slop_default_shaders; highlight = false; + x_ratio = 0; + y_ratio = 0; r = 0.5; g = 0.5; b = 0.5; @@ -354,6 +356,8 @@ extern "C" struct slop_options slop_options_default() { options.padding = 0; options.shaders = slop_default_shaders; options.highlight = false; + options.x_ratio = 0; + options.y_ratio = 0; options.r = 0.5; options.g = 0.5; options.b = 0.5; @@ -380,6 +384,8 @@ extern "C" struct slop_selection slop_select( struct slop_options* options ) { realOptions.padding = options->padding; realOptions.shaders = options->shaders; realOptions.highlight = options->highlight; + realOptions.x_ratio = options->x_ratio; + realOptions.y_ratio = options->y_ratio; realOptions.r = options->r; realOptions.g = options->g; realOptions.b = options->b; diff --git a/src/slop.hpp b/src/slop.hpp index 6b40946..550043d 100644 --- a/src/slop.hpp +++ b/src/slop.hpp @@ -35,6 +35,8 @@ struct slop_options { int nokeyboard; int nodecorations; char* shaders; + float x_ratio; + float y_ratio; float r; float g; float b; @@ -69,6 +71,8 @@ class SlopOptions { bool nokeyboard; bool nodecorations; char* shaders; + float x_ratio; + float y_ratio; float r; float g; float b; diff --git a/src/slopstates.cpp b/src/slopstates.cpp index 503d38d..1029ed5 100644 --- a/src/slopstates.cpp +++ b/src/slopstates.cpp @@ -8,6 +8,7 @@ slop::SlopMemory::SlopMemory( SlopOptions* options, Rectangle* rect ) { state = (SlopState*)new SlopStart(); nextState = NULL; tolerance = options->tolerance; + aspectRatio = glm::vec2(options->x_ratio, options->y_ratio); nodrag = options->nodrag; nodecorations = options->nodecorations; rectangle = rect; @@ -103,24 +104,47 @@ void slop::SlopStartDrag::update( SlopMemory& memory, double dt ) { memory.setState( (SlopState*)new SlopEndDrag() ); } + // Query X server once only during update function + glm::vec2 mousePos = mouse->getMousePos(); + // Determine which cursor to use - char a = startPoint.y > mouse->getMousePos().y; - char b = startPoint.x > mouse->getMousePos().x; + char a = startPoint.y > mousePos.y; + char b = startPoint.x > mousePos.x; char c = (a << 1) | b; + + // -1 to invert, 1 to leave as is + glm::vec2 invert; switch ( c ) { case 0: mouse->setCursor( XC_lr_angle ); + invert = glm::vec2(1, 1); break; case 1: mouse->setCursor( XC_ll_angle ); + invert = glm::vec2(-1, 1); break; case 2: mouse->setCursor( XC_ur_angle ); + invert = glm::vec2(1, -1); break; case 3: mouse->setCursor( XC_ul_angle ); + invert = glm::vec2(-1, -1); break; } + + glm::vec2 widthHeight = mousePos - startPoint; + float scale = glm::max(glm::abs(widthHeight.x) / memory.aspectRatio.x, + glm::abs(widthHeight.y) / memory.aspectRatio.y); + + glm::vec2 rectEndPoint; + if (memory.aspectRatio == glm::vec2(0, 0)) { + rectEndPoint = glm::vec2(mousePos); + } else { + rectEndPoint = glm::vec2(startPoint + memory.aspectRatio*scale*invert); + } + // Compensate for edges of screen, depending on the mouse position in relation to the start point. - int lx = mouse->getMousePos().x < startPoint.x; - int ly = mouse->getMousePos().y < startPoint.y; - memory.rectangle->setPoints(startPoint+glm::vec2(1*lx,1*ly), mouse->getMousePos()+glm::vec2(1*(!lx), 1*(!ly))); + int lx = mousePos.x < startPoint.x; + int ly = mousePos.y < startPoint.y; + + memory.rectangle->setPoints(startPoint+glm::vec2(1*lx,1*ly), rectEndPoint+glm::vec2(1*(!lx), 1*(!ly))); if ( !memory.nodrag && !mouse->getButton( 1 ) ) { memory.setState( (SlopState*)new SlopEndDrag() ); @@ -129,7 +153,7 @@ void slop::SlopStartDrag::update( SlopMemory& memory, double dt ) { if ( keyboard ) { if ( keyboard->getKey(XK_space) ) { - memory.setState( (SlopState*)new SlopStartMove( startPoint, mouse->getMousePos() ) ); + memory.setState( (SlopState*)new SlopStartMove(startPoint, rectEndPoint) ); return; } int arrows[2]; @@ -152,6 +176,18 @@ void slop::SlopStartDrag::update( SlopMemory& memory, double dt ) { } void slop::SlopEndDrag::onEnter( SlopMemory& memory ) { + // Clip rectangle on edges of screen. + // p1(x,y), p2(z,w) with default format as p2_x x p2_y+p1_x+p1_y + glm::vec4 rect = memory.rectangle->getRect(); + glm::vec2 p1, p2; + p1.x = glm::min(int(rect.x), WidthOfScreen(x11->screen)); + p1.x = glm::max(int(rect.x), 0); + p1.y = glm::min(int(rect.y), HeightOfScreen(x11->screen)); + p1.y = glm::max(int(rect.y), 0); + p2.x = glm::min(int(rect.x + rect.z), WidthOfScreen(x11->screen)); + p2.y = glm::min(int(rect.y + rect.w), HeightOfScreen(x11->screen)); + memory.rectangle->setPoints(p1, p2); + memory.running = false; } @@ -159,8 +195,11 @@ slop::SlopStartMove::SlopStartMove( glm::vec2 oldPoint, glm::vec2 newPoint ) { // oldPoint is where drag was started and newPoint where move was startPoint = oldPoint; // This vector is the diagonal of the rectangle - // it will be used to move the startPoint along with mousePos diagonal = newPoint - oldPoint; + // This vector is the diagonal of the rectangle between mouse and oldPoint. + // It will be used to move the startPoint along with mousePos. + // Cause of aspect ratio feature, it isn't necessarily the same as diagonal. + mouseDiagonal = mouse->getMousePos() - oldPoint; } void slop::SlopStartMove::onEnter( SlopMemory& memory ) { // redundant because of update() @@ -170,21 +209,24 @@ void slop::SlopStartMove::onEnter( SlopMemory& memory ) { mouse->setCursor( XC_fleur ); } void slop::SlopStartMove::update( SlopMemory& memory, double dt ) { - // Unclear why it has to be - and not + - startPoint = mouse->getMousePos() - diagonal; - - int lx = mouse->getMousePos().x < startPoint.x; - int ly = mouse->getMousePos().y < startPoint.y; - memory.rectangle->setPoints(startPoint+glm::vec2(1*lx,1*ly), mouse->getMousePos()+glm::vec2(1*(!lx), 1*(!ly))); + glm::vec2 mousePos = mouse->getMousePos(); + + // It needs to be - instead + because: + // - left upper corner of screen is (x,y) = (0,0); + // - y axis is inverted (0,0) x + // +----> + // | + (4,1) + // y v + startPoint = mousePos - mouseDiagonal; + + int lx = mousePos.x < startPoint.x; + int ly = mousePos.y < startPoint.y; + memory.rectangle->setPoints(startPoint+glm::vec2(1*lx,1*ly), + startPoint+diagonal+glm::vec2(1*(!lx), 1*(!ly))); // space or mouse1 released, return to drag // if mouse1 is released then drag will end also if ( !keyboard->getKey(XK_space) or (!mouse->getButton( 1 ) && !memory.nodrag) ) { - // clip rectangle on edges of screen. - startPoint.x = glm::min((int)startPoint.x, WidthOfScreen(x11->screen)); - startPoint.x = glm::max((int)startPoint.x, 0); - startPoint.y = glm::min((int)startPoint.y, HeightOfScreen(x11->screen)); - startPoint.y = glm::max((int)startPoint.y, 0); memory.setState( (SlopState*) new SlopStartDrag(startPoint) ); } } diff --git a/src/slopstates.hpp b/src/slopstates.hpp index 4e2ce4f..91726ad 100644 --- a/src/slopstates.hpp +++ b/src/slopstates.hpp @@ -70,6 +70,7 @@ class SlopEndDrag : SlopState { class SlopStartMove : SlopState { private: glm::vec2 diagonal; + glm::vec2 mouseDiagonal; public: SlopStartMove( glm::vec2 oldPoint, glm::vec2 newPoint ); virtual void onEnter( SlopMemory& memory ); @@ -84,6 +85,7 @@ class SlopMemory { SlopMemory( SlopOptions* options, Rectangle* rect ); ~SlopMemory(); Window selectedWindow; + glm::vec2 aspectRatio; bool running; float tolerance; bool nodecorations;