bool CUnixEventEmitter::processMessage (XEvent &event, CEventServer *server)
{
	if (!server)
		server=&_InternalServer;

	XWindowAttributes xwa;
	XGetWindowAttributes (_dpy, _win, &xwa);

	switch (event.type)
	{
	case ButtonPress:
	{
		//nlinfo("%d %d %d", event.xbutton.button, event.xbutton.x, event.xbutton.y);
		float fX = (float) event.xbutton.x / (float) xwa.width;
		float fY = 1.0f - (float) event.xbutton.y / (float) xwa.height;
		TMouseButton button=getMouseButton(event.xbutton.state);
		switch(event.xbutton.button)
		{
		case Button1:
			server->postEvent(new CEventMouseDown(fX, fY, (TMouseButton)(leftButton|(button&~(leftButton|middleButton|rightButton))), this));
			break;
		case Button2:
			server->postEvent(new CEventMouseDown(fX, fY, (TMouseButton)(middleButton|(button&~(leftButton|middleButton|rightButton))), this));
			break;
		case Button3:
			server->postEvent(new CEventMouseDown(fX, fY, (TMouseButton)(rightButton|(button&~(leftButton|middleButton|rightButton))), this));
			break;
		case Button4:
			server->postEvent(new CEventMouseWheel(fX, fY, button, true, this));
			break;
		case Button5:
			server->postEvent(new CEventMouseWheel(fX, fY, button, false, this));
			break;
		}
		break;
	}
	case ButtonRelease:
	{
		//nlinfo("%d %d %d", event.xbutton.button, event.xbutton.x, event.xbutton.y);
		float fX = (float) event.xbutton.x / (float) xwa.width;
		float fY = 1.0f - (float) event.xbutton.y / (float) xwa.height;
		switch(event.xbutton.button)
		{
		case Button1:
			server->postEvent(new CEventMouseUp(fX, fY, leftButton, this));
			break;
		case Button2:
			server->postEvent(new CEventMouseUp(fX, fY, middleButton, this));
			break;
		case Button3:
			server->postEvent(new CEventMouseUp(fX, fY, rightButton, this));
			break;
		}
		break;
	}
	case MotionNotify:
	{
		TMouseButton button=getMouseButton (event.xbutton.state);

		// if raw mode should be emulated
		if(_emulateRawMode)
		{
			// when we just wrapped back the pointer to 0.5 / 0.5, ignore event
			if(event.xbutton.x == xwa.width / 2 && event.xbutton.y == xwa.height / 2)
				break;

			// post a CGDMouseMove with the movement delta to the event server
			server->postEvent(
				new CGDMouseMove(this, NULL /* no mouse device */,
					event.xbutton.x - (xwa.width / 2),
					(xwa.height / 2) - event.xbutton.y));

			// move the pointer back to the center of the window
			XWarpPointer(_dpy, None, _win, None, None, None, None,
				(xwa.width / 2), (xwa.height / 2));
		}
		// if in normal mouse mode
		else
		{
			// get the relative mouse position
			float fX = (float) event.xbutton.x / (float) xwa.width;
			float fY = 1.0f - (float) event.xbutton.y / (float) xwa.height;

			// post a normal mouse move event to the event server
			server->postEvent (new CEventMouseMove (fX, fY, button, this));
		}
		break;
	}
	case KeyPress:
	{
		// save keycode because XFilterEvent could set it to 0
		uint keyCode = event.xkey.keycode;
		KeySym k;
		static char Text[256];
		int c = 0;

		// check if event is filtered
		bool filtered = XFilterEvent(&event, _win);

		// if key event is filtered, we shouldn't use XLookupString to retrieve KeySym
		if (!filtered)
		{
			Status status = XLookupNone;

#ifdef X_HAVE_UTF8_STRING
			if (_ic)
				c = Xutf8LookupString(_ic, &event.xkey, Text, sizeof(Text), &k, &status);
#endif

			if (status == XLookupNone)
				c = XLookupString(&event.xkey, Text, sizeof(Text), &k, NULL);
		}
		else
		{
			k = XKeycodeToKeysym(_dpy, keyCode, 0);
		}

		// send CEventKeyDown event only if keyCode is defined
		if (keyCode)
		{
			TKey key = getKeyFromKeySym(k);
			if(key == KeyNOKEY)
				key = getKeyFromKeycode(keyCode);

			// search for key in map
			std::map<TKey, bool>::const_iterator it = _PressedKeys.find(key);

			// if key is not found or value is false, that's the first time
			bool firstTime = (it == _PressedKeys.end()) || !it->second;

			server->postEvent (new CEventKeyDown (key, getKeyButton(event.xbutton.state), firstTime, this));
			_PressedKeys[key] = true;

			// don't send a control character when deleting
			if (key == KeyDELETE)
				c = 0;
		}

		Text[c] = '\0';
		if(c>0)
		{
#ifdef X_HAVE_UTF8_STRING
			ucstring ucstr;
			ucstr.fromUtf8(Text);

			CEventChar *charEvent = new CEventChar (ucstr[0], getKeyButton(event.xbutton.state), this);

			// raw if not processed by IME
			charEvent->setRaw(keyCode != 0);

			server->postEvent (charEvent);
#else
			for (int i = 0; i < c; i++)
			{
				CEventChar *charEvent = new CEventChar ((ucchar)(unsigned char)Text[i], getKeyButton(event.xbutton.state), this);

				// raw if not processed by IME
				charEvent->setRaw(keyCode != 0);

				server->postEvent (charEvent);
			}
#endif
		}
		break;
	}
	case KeyRelease:
	{
		if (!keyRepeat(_dpy, &event))
		{
			KeySym k;
			// only need to get correct KeySym
			int c = XLookupString(&event.xkey, NULL, 0, &k, NULL);

			TKey key = getKeyFromKeySym(k);
			if(key == KeyNOKEY)
				key = getKeyFromKeycode(event.xkey.keycode);

			server->postEvent (new CEventKeyUp (key, getKeyButton(event.xbutton.state), this));
			_PressedKeys[key] = false;
		}
		break;
	}
	case SelectionRequest:
	{
		XEvent respond;
		XSelectionRequestEvent req = event.xselectionrequest;

		respond.xselection.type= SelectionNotify;
		respond.xselection.display= req.display;
		respond.xselection.requestor= req.requestor;
		respond.xselection.selection=req.selection;
		respond.xselection.target= req.target;
		respond.xselection.time = req.time;
		respond.xselection.property = req.property;

		if (req.property == None)
		{
			respond.xselection.property = req.target;
		}
		if (req.target == XA_TARGETS)
		{
			Atom targets[] =
			{
				XA_TARGETS,
				XA_STRING,
				XA_UTF8_STRING
			};

			respond.xselection.property = req.property;

			XChangeProperty(req.display, req.requestor, req.property, XA_ATOM, 32, PropModeReplace, (unsigned char *)targets, 3 /* number of element */);
		}
		else if (req.target == XA_STRING)
		{
			respond.xselection.property = req.property;
			std::string str = _CopiedString.toString();
			XChangeProperty(req.display, req.requestor, req.property, XA_STRING, 8, PropModeReplace, (const unsigned char*)str.c_str(), str.length());
		}
		else if (req.target == XA_UTF8_STRING)
		{
			respond.xselection.property = req.property;
			std::string str = _CopiedString.toUtf8();
			XChangeProperty(req.display, req.requestor, respond.xselection.property, XA_UTF8_STRING, 8, PropModeReplace, (const unsigned char*)str.c_str(), str.length());
		}
		else
		{
			// Note: Calling XGetAtomName with arbitrary value crash the client, maybe req.target have been sanitized by X11 server
			respond.xselection.property = None;
		}

		XSendEvent (_dpy, req.requestor, 0, 0, &respond);

		break;
	}
	case SelectionClear:
		_SelectionOwned = false;
		_CopiedString = "";
		break;
	case SelectionNotify:
	{
		Atom target = event.xselection.target;

		Atom actualType = 0;
		int actualFormat = 0;
		unsigned long nitems = 0, bytesLeft = 0;

		// some applications are sending ATOM and other TARGETS
		if (target == XA_TARGETS || target == XA_ATOM)
		{
			Atom *supportedTargets = NULL;

			// list NeL selection properties
			if (XGetWindowProperty(_dpy, _win, XA_NEL_SEL, 0, XMaxRequestSize(_dpy), False, AnyPropertyType, &actualType, &actualFormat, &nitems, &bytesLeft, (unsigned char**)&supportedTargets) != Success)
				return false;

			if (bytesLeft > 0)
			{
				nlwarning("Paste: Supported TARGETS list too long.");
			}

			Atom bestTarget = 0;
			sint bestTargetElect = 0;

			// Elect best type
			for (uint i=0; i < nitems; i++)
			{
				// nlwarning(" - Type=%s (%u)", XGetAtomName(_dpy, supportedTargets[i]), (uint)supportedTargets[i]);
				if (supportedTargets[i] == XA_UTF8_STRING )
				{
					if (bestTargetElect < 2)
					{
						bestTarget = XA_UTF8_STRING;
						bestTargetElect = 2;
					}
				}
				else if (supportedTargets[i] == XA_STRING )
				{
					if (bestTargetElect < 1)
					{
						bestTarget = XA_STRING;
						bestTargetElect = 1;
					}
				}
			}

			XFree(supportedTargets);

			if (!bestTargetElect)
			{
				nlwarning("Paste buffer is not a text buffer.");
				return false;
			}

			// request string conversion
			XConvertSelection(_dpy, XA_CLIPBOARD, bestTarget, XA_NEL_SEL, _win, CurrentTime);
		}
		else if (target == XA_UTF8_STRING || target == XA_STRING)
		{
			uint8 *data = NULL;

			// get selection
			if (XGetWindowProperty(_dpy, _win, XA_NEL_SEL, 0, XMaxRequestSize(_dpy), False, AnyPropertyType, &actualType, &actualFormat, &nitems, &bytesLeft, (unsigned char**)&data) != Success)
				return false;

			ucstring text;
			std::string tmpData = (const char*)data;
			XFree(data);

			// convert buffer to ucstring
			if (target == XA_UTF8_STRING)
			{
				text = ucstring::makeFromUtf8(tmpData);
			}
			else if (target == XA_STRING)
			{
				text = tmpData;
			}
			else
			{
				nlwarning("Unknow format %u", (uint)target);
			}

			// sent string event to event server
			server->postEvent (new CEventString (text, this));
		}
		else
		{
			nlwarning("Unknow target %u", (uint)target);
		}

		break;
	}
	case FocusIn:
		// keyboard focus
//		server->postEvent (new CEventSetFocus (true, this));
		if (_ic) XSetICFocus(_ic);
		break;
	case FocusOut:
		// keyboard focus
//		server->postEvent (new CEventSetFocus (false, this));
		if (_ic) XUnsetICFocus(_ic);
		break;
	case KeymapNotify:
		break;
	case MappingNotify:
		// update keymap
		XRefreshKeyboardMapping((XMappingEvent *)&event);
		break;
	case DestroyNotify:
		// XIM server has crashed
		createIM();
		break;
	case ClientMessage:
		if ((event.xclient.format == 32) && ((Atom)event.xclient.data.l[0] == XA_WM_DELETE_WINDOW))
		{
			server->postEvent(new CEventDestroyWindow(this));
		}
		break;
	default:
		//	nlinfo("UnknownEvent");
		//	XtDispatchEvent(&event);
		return false;
	}

	return true;
}
Ejemplo n.º 2
0
ServerProxy::EResult
ServerProxy::parseMessage(const UInt8* code)
{
	if (memcmp(code, kMsgDMouseMove, 4) == 0) {
		mouseMove();
	}

	else if (memcmp(code, kMsgDMouseRelMove, 4) == 0) {
		mouseRelativeMove();
	}

	else if (memcmp(code, kMsgDMouseWheel, 4) == 0) {
		mouseWheel();
	}

	else if (memcmp(code, kMsgDKeyDown, 4) == 0) {
		keyDown();
	}

	else if (memcmp(code, kMsgDKeyUp, 4) == 0) {
		keyUp();
	}

	else if (memcmp(code, kMsgDMouseDown, 4) == 0) {
		mouseDown();
	}

	else if (memcmp(code, kMsgDMouseUp, 4) == 0) {
		mouseUp();
	}

	else if (memcmp(code, kMsgDKeyRepeat, 4) == 0) {
		keyRepeat();
	}

	else if (memcmp(code, kMsgCKeepAlive, 4) == 0) {
		// echo keep alives and reset alarm
		ProtocolUtil::writef(m_stream, kMsgCKeepAlive);
		resetKeepAliveAlarm();
	}

	else if (memcmp(code, kMsgCNoop, 4) == 0) {
		// accept and discard no-op
	}

	else if (memcmp(code, kMsgCEnter, 4) == 0) {
		enter();
	}

	else if (memcmp(code, kMsgCLeave, 4) == 0) {
		leave();
	}

	else if (memcmp(code, kMsgCClipboard, 4) == 0) {
		grabClipboard();
	}

	else if (memcmp(code, kMsgCScreenSaver, 4) == 0) {
		screensaver();
	}

	else if (memcmp(code, kMsgQInfo, 4) == 0) {
		queryInfo();
	}

	else if (memcmp(code, kMsgCInfoAck, 4) == 0) {
		infoAcknowledgment();
	}

	else if (memcmp(code, kMsgDClipboard, 4) == 0) {
		setClipboard();
	}

	else if (memcmp(code, kMsgCResetOptions, 4) == 0) {
		resetOptions();
	}

	else if (memcmp(code, kMsgDSetOptions, 4) == 0) {
		setOptions();
	}

	else if (memcmp(code, kMsgDFileTransfer, 4) == 0) {
		fileChunkReceived();
	}
	else if (memcmp(code, kMsgDDragInfo, 4) == 0) {
		dragInfoReceived();
	}

	else if (memcmp(code, kMsgCClose, 4) == 0) {
		// server wants us to hangup
		LOG((CLOG_DEBUG1 "recv close"));
		m_client->disconnect(NULL);
		return kDisconnect;
	}
	else if (memcmp(code, kMsgEBad, 4) == 0) {
		LOG((CLOG_ERR "server disconnected due to a protocol error"));
		m_client->disconnect("server reported a protocol error");
		return kDisconnect;
	}
	else {
		return kUnknown;
	}

	// send a reply.  this is intended to work around a delay when
	// running a linux server and an OS X (any BSD?) client.  the
	// client waits to send an ACK (if the system control flag
	// net.inet.tcp.delayed_ack is 1) in hopes of piggybacking it
	// on a data packet.  we provide that packet here.  i don't
	// know why a delayed ACK should cause the server to wait since
	// TCP_NODELAY is enabled.
	ProtocolUtil::writef(m_stream, kMsgCNoop);

	return kOkay;
}
Ejemplo n.º 3
0
int app( int argc, char** argv ) {
    gengetopt_args_info options;
    int err = cmdline_parser( argc, argv, &options );
    if ( err != EXIT_SUCCESS ) {
        return EXIT_FAILURE;
    }
    int state = 0;
    bool running = true;
    bool opengl = options.opengl_flag;
    slop::SelectRectangle* selection = NULL;
    Window window = None;
    Window windowmemory = None;
    std::string xdisplay;
    if ( options.xdisplay_given ) {
        xdisplay = options.xdisplay_arg;
    } else {
        // If we weren't specifically given a xdisplay, we try
        // to parse it from environment variables
        char* display = getenv( "DISPLAY" );
        if ( display ) {
            xdisplay = display;
        } else {
            fprintf( stderr, "Warning: Failed to parse environment variable: DISPLAY. Using \":0\" instead.\n" );
            xdisplay = ":0";
        }
    }
    int padding = options.padding_arg;
    int borderSize = options.bordersize_arg;
    int tolerance = options.tolerance_arg;
    float r, g, b, a;
    err = parseColor( options.color_arg, &r, &g, &b, &a );
    if ( err != EXIT_SUCCESS ) {
        fprintf( stderr, "Error parsing color %s\n", options.color_arg );
        return EXIT_FAILURE;
    }
    float gracetime;
    err = sscanf( options.gracetime_arg, "%f", &gracetime );
    if ( err != 1 ) {
        fprintf( stderr, "Error parsing %s as a float for gracetime!\n", options.gracetime_arg );
        return EXIT_FAILURE;
    }
    bool highlight = options.highlight_flag;
    bool keyboard = !options.nokeyboard_flag;
    bool decorations = !options.nodecorations_flag;
    bool themeon = (bool)options.theme_given;
    std::string theme = options.theme_arg;
#ifdef OPENGL_ENABLED
    bool shadergiven = (bool)options.shader_given;
#endif
    std::string shader = options.shader_arg;
    struct timespec start, time;
    int xoffset = 0;
    int yoffset = 0;
    int cx = 0;
    int cy = 0;
    int xmem = 0;
    int ymem = 0;
    int wmem = 0;
    int hmem = 0;
    int minimumsize = options.min_arg;
    int maximumsize = options.max_arg;
    bool pressedMemory[4];
    double pressedTime[4];
    for ( int i=0;i<4;i++ ) {
        pressedMemory[ i ] = false;
        pressedTime[ i ] = 0;
    }
    std::string format = options.format_arg;
    bool magenabled = options.magnify_flag;
#ifdef OPENGL_ENABLED
    float magstrength = options.magstrength_arg;
    if ( options.magpixels_arg < 0 ) {
        fprintf( stderr, "Error: --magpixels < 0, it's an unsigned integer you twat. Stop trying to underflow me!\n" );
        return EXIT_FAILURE;
    }
    unsigned int magpixels = (unsigned int)options.magpixels_arg;
#endif
    cmdline_parser_free( &options );
#ifndef OPENGL_ENABLED
    if ( opengl || themeon || magenabled ) {
        throw std::runtime_error( "Slop wasn't compiled with OpenGL support, so themes, magnifications, and shaders are disabled! Try compiling it with the CMAKE_OPENGL_SUPPORT set to true." );
    }
#else // OPENGL_ENABLED
    if ( ( themeon || magenabled || shadergiven ) && !opengl ) {
        throw std::runtime_error( "Slop needs --opengl enabled to use themes, shaders, or magnifications." );
    }
#endif

    // First we set up the x interface and grab the mouse,
    // if we fail for either we exit immediately.
    err = xengine->init( xdisplay.c_str() );
    if ( err != EXIT_SUCCESS ) {
        printSelection( format, true, 0, 0, 0, 0, None );
        return EXIT_FAILURE;
    }
    if ( !slop::isSelectRectangleSupported() ) {
        fprintf( stderr, "Error: Your X server doesn't support the XShape extension. There's nothing slop can do about this!\n" );
        fprintf( stderr, "  Try updating X and making sure you have XExtensions installed. (/usr/lib/libXext.so, /usr/include/X11/extensions/shape.h)\n" );
        return EXIT_FAILURE;
    }
    err = xengine->grabCursor( slop::Cross, gracetime );
    if ( err != EXIT_SUCCESS ) {
        printSelection( format, true, 0, 0, 0, 0, None );
        return EXIT_FAILURE;
    }
    if ( keyboard ) {
        err = xengine->grabKeyboard();
        if ( err ) {
            fprintf( stderr, "Warning: Failed to grab the keyboard. This is non-fatal, keyboard presses might fall through to other applications.\n" );
        }
    }
    current_utc_time( &start );
    double deltatime = 0;
    double curtime = double( start.tv_sec*1000000000L + start.tv_nsec )/1000000000.f;
    while ( running ) {
        current_utc_time( &time );
        // "ticking" the xengine makes it process all queued events.
        xengine->tick();
        // If the user presses any key on the keyboard, exit the application.
        // Make sure at least gracetime has passed before allowing canceling
        double newtime = double( time.tv_sec*1000000000L + time.tv_nsec )/1000000000.f;
        deltatime = newtime-curtime;
        curtime = newtime;
        double starttime = double( start.tv_sec*1000000000L + start.tv_nsec )/1000000000.f;
        if ( curtime - starttime > gracetime ) {
            if ( keyRepeat( XK_Up, curtime, 0.5, &pressedTime[ 0 ], &pressedMemory[ 0 ] ) ) {
                yoffset -= 1;
            }
            if ( keyRepeat( XK_Down, curtime, 0.5, &pressedTime[ 1 ], &pressedMemory[ 1 ] ) ) {
                yoffset += 1;
            }
            if ( keyRepeat( XK_Left, curtime, 0.5, &pressedTime[ 2 ], &pressedMemory[ 2 ] ) ) {
                xoffset -= 1;
            }
            if ( keyRepeat( XK_Right, curtime, 0.5, &pressedTime[ 3 ], &pressedMemory[ 3 ] ) ) {
                xoffset += 1;
            }
            // If we pressed enter we move the state onward.
            if ( xengine->keyPressed( XK_Return ) ) {
                // If we're highlight windows, just select the active window.
                if ( state == 0 ) {
                    state = 1;
                // If we're making a custom selection, select the custom selection.
                } else if ( state == 2 ) {
                    state = 3;
                }
            }
            // If we press any key other than the arrow keys or enter key, we shut down!
            if ( !( xengine->keyPressed( XK_Up ) || xengine->keyPressed( XK_Down ) || xengine->keyPressed( XK_Left ) || xengine->keyPressed( XK_Right ) ) &&
                !( xengine->keyPressed( XK_Return ) ) &&
                ( ( xengine->anyKeyPressed() && keyboard ) || xengine->mouseDown( 3 ) ) ) {
                printSelection( format, true, 0, 0, 0, 0, None );
                fprintf( stderr, "User pressed key. Canceled selection.\n" );
                state = -1;
                running = false;
            }
        }
        // Our adorable little state manager will handle what state we're in.
        switch ( state ) {
            default: {
                break;
            }
            case 0: {
                // If xengine has found a window we're hovering over (or if it changed)
                // create a rectangle around it so the user knows he/she can click on it.
                // --but only if the user wants us to
                if ( window != xengine->m_hoverWindow && tolerance > 0 ) {
                    slop::WindowRectangle t;
                    t.setGeometry( xengine->m_hoverWindow, decorations );
                    t.applyPadding( padding );
                    t.applyMinMaxSize( minimumsize, maximumsize );
                    // Make sure we only apply offsets to windows that we've forcibly removed decorations on.
                    if ( !selection ) {
#ifdef OPENGL_ENABLED
                        if ( opengl ) {
                            selection = new slop::GLSelectRectangle( t.m_x, t.m_y,
                                                                     t.m_x + t.m_width,
                                                                     t.m_y + t.m_height,
                                                                     borderSize,
                                                                     highlight,
                                                                     r, g, b, a );
                            // Haha why is this so hard to cast?
                            ((slop::GLSelectRectangle*)(selection))->setMagnifySettings( magenabled, magstrength, magpixels );
                            ((slop::GLSelectRectangle*)(selection))->setTheme( themeon, theme );
                            ((slop::GLSelectRectangle*)(selection))->setShader( shader );
                        } else {
#endif // OPENGL_ENABLED
                            selection = new slop::XSelectRectangle( t.m_x, t.m_y,
                                                                    t.m_x + t.m_width,
                                                                    t.m_y + t.m_height,
                                                                    borderSize,
                                                                    highlight,
                                                                    r, g, b, a );
#ifdef OPENGL_ENABLED
                        }
#endif // OPENGL_ENABLED
                    } else {
                        selection->setGeo( t.m_x, t.m_y, t.m_x + t.m_width, t.m_y + t.m_height );
                    }
                    //window = xengine->m_hoverWindow;
                    // Since WindowRectangle can select different windows depending on click location...
                    window = t.getWindow();
                }
                if ( selection ) {
                    selection->update( deltatime );
                }
                // If the user clicked we move on to the next state.
                if ( xengine->mouseDown( 1 ) ) {
                    state++;
                }
                break;
            }
            case 1: {
                // Set the mouse position of where we clicked, used so that click tolerance doesn't affect the rectangle's position.
                cx = xengine->m_mousex;
                cy = xengine->m_mousey;
                // Make sure we don't have un-seen applied offsets.
                xoffset = 0;
                yoffset = 0;
                // Also remember where the original selection was
                if ( selection ) {
                    xmem = selection->m_x;
                    ymem = selection->m_y;
                    wmem = selection->m_width;
                    hmem = selection->m_height;
                } else {
                    xmem = cx;
                    ymem = cy;
                }
                state++;
                break;
            }
            case 2: {
                // It's possible that our selection doesn't exist still, lets make sure it actually gets created here.
                if ( !selection ) {
                    int sx, sy, ex, ey;
                    constrain( cx, cy, xengine->m_mousex, xengine->m_mousey, padding, minimumsize, maximumsize, &sx, &sy, &ex, &ey );
#ifdef OPENGL_ENABLED
                    if ( opengl ) {
                        selection = new slop::GLSelectRectangle( sx, sy,
                                                                 ex, ey,
                                                                 borderSize,
                                                                 highlight,
                                                                 r, g, b, a );
                        // Haha why is this so hard to cast?
                        ((slop::GLSelectRectangle*)(selection))->setMagnifySettings( magenabled, magstrength, magpixels );
                        ((slop::GLSelectRectangle*)(selection))->setTheme( themeon, theme );
                        ((slop::GLSelectRectangle*)(selection))->setShader( shader );
                    } else {
#endif // OPENGL_ENABLED
                        selection = new slop::XSelectRectangle( sx, sy,
                                                                ex, ey,
                                                                borderSize,
                                                                highlight,
                                                                r, g, b, a );
#ifdef OPENGL_ENABLED
                    }
#endif // OPENGL_ENABLED
                }
                windowmemory = window;
                // If the user has let go of the mouse button, we'll just
                // continue to the next state.
                if ( !xengine->mouseDown( 1 ) ) {
                    state++;
                    break;
                }
                // Check to make sure the user actually wants to drag for a selection before moving things around.
                int w = xengine->m_mousex - cx;
                int h = xengine->m_mousey - cy;
                if ( ( std::abs( w ) < tolerance && std::abs( h ) < tolerance ) ) {
                    // We make sure the selection rectangle stays on the window we had selected
                    selection->setGeo( xmem, ymem, xmem + wmem, ymem + hmem );
                    selection->update( deltatime );
                    xengine->setCursor( slop::Left );
                    // Make sure
                    window = windowmemory;
                    continue;
                }
                // If we're not selecting a window.
                windowmemory = window;
                window = None;
                // We also detect which way the user is pulling and set the mouse icon accordingly.
                bool x = cx > xengine->m_mousex;
                bool y = cy > xengine->m_mousey;
                if ( ( selection->m_width <= 1 && selection->m_height <= 1 ) || ( minimumsize == maximumsize && minimumsize != 0 && maximumsize != 0 ) ) {
                    xengine->setCursor( slop::Cross );
                } else if ( !x && !y ) {
                    xengine->setCursor( slop::LowerRightCorner );
                } else if ( x && !y ) {
                    xengine->setCursor( slop::LowerLeftCorner );
                } else if ( !x && y ) {
                    xengine->setCursor( slop::UpperRightCorner );
                } else if ( x && y ) {
                    xengine->setCursor( slop::UpperLeftCorner );
                }
                // Apply padding and minimum size adjustments.
                int sx, sy, ex, ey;
                constrain( cx, cy, xengine->m_mousex, xengine->m_mousey, padding, minimumsize, maximumsize, &sx, &sy, &ex, &ey );
                // Set the selection rectangle's dimensions to mouse movement.
                selection->setGeo( sx + xoffset, sy + yoffset, ex, ey );
                selection->update( deltatime );
                break;
            }
            case 3: {
                int x, y, w, h;
                // Exit the utility after this state runs once.
                running = false;
                // We pull the dimensions and positions from the selection rectangle.
                // The selection rectangle automatically converts the positions and
                // dimensions to absolute coordinates when we set them earilier.
                x = selection->m_x;
                y = selection->m_y;
                w = selection->m_width;
                h = selection->m_height;
                // Delete the rectangle, which will remove it from the screen.
                delete selection;
                // Make sure if no window was specifically specified, that we output the root window.
                Window temp = window;
                if ( temp == None ) {
                    temp = xengine->m_root;
                }
                // Print the selection :)
                printSelection( format, false, x, y, w, h, temp );
                break;
            }
        }
        // This sleep is required because drawing the rectangles is a very expensive task that acts really weird with Xorg when called as fast as possible.
        // 0.01 seconds
        usleep( 10000 );
    }
    xengine->releaseCursor();
    xengine->releaseKeyboard();
    // Try to process any last-second requests.
    //xengine->tick();
    // Clean up global classes.
    delete xengine;
    // Sleep for 0.05 seconds to ensure everything was cleaned up. (Without this, slop's window often shows up in screenshots.)
    usleep( 50000 );
    // If we canceled the selection, return error.
    if ( state == -1 ) {
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}