This forum has been archived. All content is frozen. Please use KDE Discuss instead.

Moving windows without decoration outside of the screen

Tags: None
(comma "," separated)
vpalancher
Registered Member
Posts
5
Karma
0
Hello,

I'm working on a project developped using Qt 5.3 under KDE and Kwin. We have a widget on which we remove the decoration, but we still want the user to be able to drag the widget around by pressing and moving the mouse on it (meaning without using Alt+click).

To achieve that, we have reimplemented events methods of QWidget to process mouse events (like button press, button release or mouse move), and accordingly move the widget around. It used to work well under Kwin 3.0 (KDE 3.3.0-5, Qt3.3), but we've since changed the system to a more recent one (using KDE 4.14.3 and Kwin 4.11.14) and we've noticed that a window without decoration cannot be moved outside of the screen unless with Alt+click. This is a problem for us because the user needs to be able to move that window at the right border of the screen (meaning most of the window should be outside of the screen). We've tried using MWM instead of Kwin and we could not reprocuce the problem so we've assumed Kwin was restricting the window to the screen (probably to make sure the user doesn't lose a window by mistake I suppose).

Is there a flag we should add to the window in Qt ? Our project is an entire system and we can even control its configuration, so is there a configuration variable we could change to get the same behavior as before (in kwinrc or kwinrulesrc maybe) ?

I've been looking for an issue similar to this one, and so far the only one I could find was this one viewtopic.php?f=305&t=34410 from 2009, and I'm not sure how the solution could be applied in our case.

Thank your for your help
luebking
Karma
0
To achieve that, we have reimplemented events methods of QWidget to process mouse events (like button press, button release or mouse move), and accordingly move the widget around.


Don't. Seriously, it's wrong. This is proper Xlib code, ask if you need xcb code.

Code: Select all
    Atom netMoveResize = XInternAtom(QX11Info::display(), "_NET_WM_MOVERESIZE", False);
    QX11Info info;
    XEvent xev;
    xev.xclient.type = ClientMessage;
    xev.xclient.message_type = netMoveResize;
    xev.xclient.display = QX11Info::display();
    xev.xclient.window = id;
    xev.xclient.format = 32;
    xev.xclient.data.l[0] = p.x();
    xev.xclient.data.l[1] = p.y();
    xev.xclient.data.l[2] = d;
    xev.xclient.data.l[3] = Button1;
    xev.xclient.data.l[4] = 0;
    XUngrabPointer(QX11Info::display(), QX11Info::appTime());
    XSendEvent(QX11Info::display(), QX11Info::appRootWindow(info.screen()), False,
                SubstructureRedirectMask | SubstructureNotifyMask, &xev);



If you would insist on doing what you do at the moment (repeat after me: "it is wrong!"), you'll have to either
a) hint the "NET::FromTool" flag (KWindowSystem has a convenience function to to this, otherwise you'd have to build anX11 configure request)
b) ensure your window is of dock type (Qt "docks" are /not/ - this refers to panels) - most other and notably all "normal" windows are not permitted to move themselves out of screen bounds.

I'm not 100% sure but think to recall that this has not been different in KDE3 (I think I asked Lubos back then because I wanted to move a window off-screen, though w/o the user interacting)

Again: what you do is wrong and terribly inefficient. Just don't ;-)
vpalancher
Registered Member
Posts
5
Karma
0
I'm not sure I understand why you believe it's wrong :P , aren't QWidget protected events methods meant to be reimplemented in subclasses ?

When I say moving window outside of the screen, I mean being able to drag the window until the cursor hits the boundaries of the screen (at which point part of the window is outside the screen, but some of it is still visible). Currently, frameless windows collide with the boundary of the screen, before the cursor itself collides with it. I'm not trying to move a window completely out of the screen (which I admit seems very wrong), just allow my frameless window to be dragged at the border of the screen without colliding as soon as one of its border hits the border of the screen, like any other decorated window. For information, the user can only drag the window when he clicks in a specific region of the widget/window.

I don't know if I make sense, and I'm sorry if I don't :P

Here is a small example that reproduces our problem :

Code: Select all
#include <QtWidgets/QWidget>
#include <QtWidgets/QFrame>
#include <QtWidgets/QApplication>
#include <QtWidgets/QVBoxLayout>
#include <QtGui/QMouseEvent>
#include <QtCore/QPoint>
#include <iostream>

class CWidget : public QFrame
{
    public:
        CWidget(QWidget* parent)
            : QFrame(parent)
            , widgetPressed(false)
        {
            setFrameStyle(QFrame::Panel | QFrame::Raised);
            setFixedSize(100, 100);
        }

    protected:
        virtual void mousePressEvent(QMouseEvent* e)
        {
            if(e->button() == Qt::LeftButton)
            {
                widgetPressed = true;
                mouseMove_parent = e->globalPos();
                firstPos_parent = static_cast<QWidget*>(parent())->pos();
            }
            else if (e->button() == Qt::RightButton)
            {
                if(widgetPressed)
                {
                    widgetPressed = false;
                    static_cast<QWidget*>(parent())->move(firstPos_parent);
                }
            }
        }

        virtual void mouseReleaseEvent(QMouseEvent* )
        {
            widgetPressed = false;
        }

        virtual void mouseMoveEvent(QMouseEvent* e)
        {
            if(widgetPressed)
            {
                QWidget* p = static_cast<QWidget*>(parent());
                p->move(p->pos() + e->globalPos() - mouseMove_parent);
                mouseMove_parent = e->globalPos();
            }
        }

    private:
        bool widgetPressed;
        QPoint mouseMove_parent;
        QPoint firstPos_parent;
};

int main(int argc, char**argv)
{
    QApplication app(argc, argv);

    // Main widget
    QWidget w(nullptr, Qt::FramelessWindowHint);
    w.setFixedSize(200, 200);
    w.move(200, 200);

    CWidget* w2 = new CWidget(&w);
    QVBoxLayout* lay = new QVBoxLayout;
    lay->addWidget(w2, 0, Qt::AlignHCenter);
    w.setLayout(lay);

    w.show();
    return app.exec();
}
luebking
Karma
0
I know what you want to do. It's not "wrong" codewise, but the approach is.

You instead want to tell the WM "please start moving around this window", what
a) tells the WM that a user is acting now, whatever he wants is ok (so no need to perform sanity checks)
b) makes the WM aware of the ungoing process (allowing such things as wobbly windows, snapping, quicktiling etc.)
c) spares you a hell lot of roundtrips. Currently the server need to sync all clients with every pixel you drag the window, leaving aside the communication overhead. And if you're using the composited mode, KWin won't even actually move the window (yet another roundtrip) until the user is done, but only reposition the texture in the compositors scene (that's for free)
d) avoids Qt event translation and compression (on slow systems, moving might otherwise lag behind the mouse)

So when you want to start the window being moved, you just signal the code snippet *once*, from that moment on the WM will move the window as if you had pressed Alt+LMB.

Client side moving is only required if there is no window manager (or the window is override_redirect, but then neither will appear in Alt+Tab or the taskbar, but instead be on top of everything)
vpalancher
Registered Member
Posts
5
Karma
0
I think I'm starting to understand what you mean.
The thing is, we don't directly discuss with the WM, we only work with Qt and we try to keep it that way. We may have a few files that do xcb related stuff but we want to try to keep it to a minimum.

We've been looking for a way to do this through Qt. I'm assuming the QWidget::move method does not bypass the WM, but you're telling me I should instead instruct Qt to notify the WM that we are initiating a move and only when we are done moving, instruct Qt to notify the WM of the move, is that right ? I'm sorry if I'm being annoying but I'm not entirely familiar with the way X11, Qt and the WM work together.

If so, as a side question, should we avoid ever moving a QWidget using the method move ? Or is it fine as long as we are not dragging it (and thus asking multiple times and in a short period to move the window) ?
luebking
Karma
0
If you want to restrict to Qt, you'll have to move the window by configure events (moving the widget) - and live with all drawbacks.

As long as the window is managed, Qt will use configure requests for this and those are *requests* to the WM; whatever that does in return (in this case constraining the window to the workarea) is undefined:

http://www.x.org/releases/X11R7.6/doc/x ... icccm.html
Client configure requests are interpreted by the window manager in the same manner as the initial window geometry mapped from the Withdrawn state, as described in the section called “WM_NORMAL_HINTS Property” Clients must be aware that there is no guarantee that the window manager will allocate them the requested size or location and must be prepared to deal with any size and location.


As an alternative, you can explicitly except the window from being managed by passing Qt::BypassWindowManagerHint to the constructor.
As mentioned this implies that the window is not part of the regular window stack (but above everything) nor focus chain. It will not be activated by the WM when eg. clicking it etc.

Any other solution will require patches to Qt or diving into X11 code (though we could abstract this in KWindowSystem)
vpalancher
Registered Member
Posts
5
Karma
0
We tried using the ByPassWindowManagerHint and it had several side effects that we don't want.
We are going to try out a few things, and I'll come back to give you our results, and hopefully accept your answers :)

Thank you for your help
luebking
Karma
0
In case you're using KF5 in addition to Qt, feel free to ask for an abstraction of the feature in KWindowSystem (sth. like "KWindowSystem::initiateMove(window()->winId());")
vpalancher
Registered Member
Posts
5
Karma
0
We got it working by sending NET_WM_MOVERESIZE events through xcb, the same way you posted it with Xlib, and the way kruler works.
We still have a small issue to fix but it seems to be working the way we want it to.
Thank you again for your help :)


Bookmarks



Who is online

Registered users: bartoloni, Bing [Bot], Evergrowing, Google [Bot], ourcraft