#include "rotator.h" #include #include #include #include #include #include #include #include #include #include const QPointF Rotator::Gravity(0, 50); static QPointF rotate(QPointF point, double angle) { const double c = qCos(qDegreesToRadians(angle)); const double s = qSin(qDegreesToRadians(angle)); return {point.x() * c - point.y() * s, point.x() * s + point.y() * c}; } static double lengthOfPoint(QPointF point) { return std::sqrt((point.x() * point.x()) + (point.y() * point.y())); } static double clampDegrees(double deg) { while(deg < 0) { deg += 360.0; } while(deg > 360) { deg -= 360.0; } return deg; } static double clampAngleDiff(double diff) { diff = fmod(diff + 180.0, 360.0); if (diff < 0) { diff += 360.0; } return diff - 180.0; } static double pointToAngle(QPointF point) { return clampDegrees(-qRadiansToDegrees(qAtan2(point.x(), point.y()))); } Rotator::Rotator(QObject* parent) : QObject(parent), disp(XOpenDisplay(nullptr)), renderer(new QSvgRenderer(QString("dmz3.svg"), this)), loopTimer(new QTimer(nullptr)), lastTimePoint(QDateTime::currentMSecsSinceEpoch()), lastPosition(-1, -1), angle(0), angleVelocity(0) { QObject::connect(loopTimer, &QTimer::timeout, this, &Rotator::loop); loopTimer->setInterval(std::chrono::milliseconds(10)); loopTimer->start(); // test /* for(int i = -360; i < 360; ++i) { qDebug() << i << clampAngleDiff(i); } throw std::exception(); */ } Rotator::~Rotator() { if(disp) { XCloseDisplay(disp); } } void Rotator::forAllWindows(std::function func) { forAllWindows(func, DefaultRootWindow(disp)); } void Rotator::reset() { angle = 0; angleVelocity = 0; } void Rotator::loop() { int x = 0, y = 0; unsigned int mask = 0; Window rootRet; Window childRet; int xChild = 0, yChild = 0; if(XQueryPointer(disp, DefaultRootWindow(disp), &rootRet, &childRet, &x, &y, &xChild, &yChild, &mask)) { recalculateCursor({(double) x, (double) y}); } } void Rotator::recalculateCursor(QPointF newPos) { quint64 currentTimePoint = QDateTime::currentMSecsSinceEpoch(); if(lastPosition.x() == -1) { lastPosition = newPos; lastTimePoint = currentTimePoint; return; } const double timeDiff = (currentTimePoint - lastTimePoint) / 1000.0; QPointF mouseForceVector = lastPosition - newPos; QPointF forceVector = ((mouseForceVector * mouseForceStrength) + (Gravity * gravityStrength)) * timeDiff; emit this->force(forceVector); // has position 0,0 (need soffset) QPointF rotatedEndCap = rotate(QPointF(0, length), angle); QPointF forcedCap = rotatedEndCap + forceVector; // fix the distance to the origin (should be exactly length) //double forcedCapLength = lengthOfPoint(forcedCap); //forcedCap *= length / forcedCapLength; //qDebug() << "normalized:" << forcedCap; // calculate angle double newAngle = clampDegrees(pointToAngle(forcedCap)); // qDebug() << "force:" << forceVector << "forcedCap:" << forcedCap << "angle:" << newAngle; double angleDiff = clampAngleDiff(newAngle - angle); if(qAbs(angleDiff) < 0.01 * timeDiff) { lastPosition = newPos; lastTimePoint = currentTimePoint; return; } newAngle += angleVelocity * timeDiff; angleVelocity += angleDiff / timeDiff; qDebug() << "angleDiff:" << angleDiff << "angleVelocity:" << angleVelocity << "angle:" << newAngle; angleVelocity *= 0.95; if(qAbs(angleVelocity) < 0.001) { angleVelocity = 0.0; } // calculate offset ( is this required? ) // QPointF realCap = forcedCap + newPos; angle = clampDegrees(newAngle); // just rotate // angle += 300 * timeDiff; emit this->angleUpdated(angle); lastPosition = newPos; lastTimePoint = currentTimePoint; rerender(); } void Rotator::rerender() { int size = 100; QPixmap px(size, size); px.fill(QColorConstants::Transparent); QPainter painter(&px); QTransform transform; qreal scaleF = 0.7; qreal tX = 16 * (size/32.0); qreal tY = 12 * (size/32.0); qreal lateTranslate = size/2.0; transform.translate(lateTranslate, lateTranslate).scale(scaleF, scaleF).rotate(angle).translate(-tX, -tY); painter.setTransform(transform, false); renderer->render(&painter); painter.setPen({QColorConstants::Red}); painter.drawLine(tX-10, tY, tX+10, tY); painter.drawLine(tX, tY-10, tX, tY+10); painter.setTransform(QTransform(), false); painter.drawRect(0, 0, size-1, size-1); emit this->newImage(px); // setCursor(px, {50, 50}); } void Rotator::setCursor(const QPixmap& px, const QPointF& hotspot) { // Get the raw XImage from the QPixmap QImage image = px.toImage(); uint32_t imageSize = image.width() * image.depth() * image.height(); char* imageData = (char*) malloc(imageSize); memcpy(imageData, image.bits(), imageSize); XImage* xImage = XCreateImage( disp, DefaultVisual(disp, DefaultScreen(disp)), DefaultDepth(disp, DefaultScreen(disp)), ZPixmap, 0, imageData, image.width(), image.height(), image.depth(), 0 ); // Create the XcursorImages XcursorImage* cursorImage = XcursorImageCreate(image.width(), image.height()); cursorImage->xhot = hotspot.x(); cursorImage->yhot = hotspot.y(); // Copy the image data to XcursorImage cursorImage->pixels = (XcursorPixel*) xImage->data; cursorImage->delay = 0; // Create the cursor Cursor cursor = XcursorImageLoadCursor(disp, cursorImage); // Set the cursor XDefineCursor(disp, DefaultRootWindow(disp), cursor); // Clean up resources // XFreeCursor(disp, cursor); // XDestroyImage(xImage); } void Rotator::forAllWindows(std::function func, Window root) { func(root); Window* children = nullptr; Window dummy; unsigned int nchildren; if (!XQueryTree(disp, root, &dummy, &dummy, &children, &nchildren)) { // no children return; } for (unsigned int i = 0; i < nchildren; i++) { forAllWindows(func, children[i]); } if (children) { XFree((char*) children); } }