PDA

View Full Version : Resize cursor doesn't change back (qx11embedcontainer in qmdisubwindow in qmdiarea)



bartsch
28th June 2012, 10:29
Hi,

I'm writing some kind of a window manager. Therefor I'm embedding windows
within a QX11EmbedContainer in a QMdiSubWindow in a QMdiArea. That part
is working fine.

The problem is the resizing cursor, which doesn't change back into a normal
pointer when moving it from the edge of a QMdiSubWindow to the center,
where the QX11EmbedContainer is. When moving the cursor from the edge of
the QMdiSubWindow to the QMdiArea, the resize cursor changes back to a
normal pointer.

I think QX11EmbedContainer eats some events meant for the
QMdiSubWindow. So, the QMdiSubWindow doesn't notice when to reset the
cursor. But, i haven't figured out how to deal with that.

Here's a stripped down example showing the effect. You'll need to start an
application (i.e. I tested with `evince`) and pass the window id as argument
to the test application.


#include <QApplication>
#include <QDebug>
#include <QMdiArea>
#include <QMdiSubWindow>
#include <QObject>
#include <QTimer>
#include <QX11EmbedContainer>

class Container : public QX11EmbedContainer
{
Q_OBJECT
public:
Container(QMdiArea *_area, int client)
:QX11EmbedContainer(),
area(_area)
{
QObject::connect
(this, SIGNAL(clientClosed()),
SLOT(slot_client_closed()));
QObject::connect
(this, SIGNAL(clientIsEmbedded()),
SLOT(slot_client_is_embedded()));
QObject::connect
(this, SIGNAL(error(QX11EmbedContainer::Error)),
SLOT(slot_client_error(QX11EmbedContainer::Error)) );

frame = new QMdiSubWindow();
frame->setWidget(this);

embedClient(client);
}

private:
QMdiArea *area;
QMdiSubWindow *frame;
void exit(void) {QTimer::singleShot(250, qApp, SLOT(quit()));}

public slots:
void slot_client_closed(void) {exit();}
void slot_client_is_embedded(void)
{
area->addSubWindow(frame);
frame->show();
}
void slot_client_error(QX11EmbedContainer::Error error)
{
qDebug() << "error #" << error;
exit();
}
};

/* ugly hack to prevent: undefined reference to `vtable... */
#include "main.moc"

int main(int argc, char *argv[])
{
QApplication *app = new QApplication(argc, argv);

if (app->arguments().count() != 2) {
qFatal("Error - expected window id as argument");
return 1;
}

QMdiArea *area = new QMdiArea();
area->show();

new Container(area, app->arguments()[1].toInt(0, 16));

return app->exec();
}

bartsch
28th June 2012, 14:29
Just found a solution - raw XEvents:
- register an event filter
- search for an EnterNotify with a matching subwindow id
- set the desired cursor


#include <QApplication>
#include <QDebug>
#include <QMdiArea>
#include <QMdiSubWindow>
#include <QObject>
#include <QTimer>
#include <QX11EmbedContainer>
#include <QX11Info>

#include <X11/cursorfont.h>
#include <X11/Xlib.h>

class Container : public QX11EmbedContainer
{
Q_OBJECT
public:
Container(QMdiArea *_area, Window _client)
:QX11EmbedContainer(),
area(_area)
{
QObject::connect
(this, SIGNAL(clientClosed()),
SLOT(slot_client_closed()));
QObject::connect
(this, SIGNAL(clientIsEmbedded()),
SLOT(slot_client_is_embedded()));
QObject::connect
(this, SIGNAL(error(QX11EmbedContainer::Error)),
SLOT(slot_client_error(QX11EmbedContainer::Error)) );

frame = new QMdiSubWindow();
frame->setWidget(this);

embedClient(_client);
}

private:
QMdiArea *area;
QMdiSubWindow *frame;
void exit(void) {QTimer::singleShot(250, qApp, SLOT(quit()));}

public slots:
void slot_client_closed(void) {exit();}
void slot_client_is_embedded(void)
{
area->addSubWindow(frame);
frame->show();
}
void slot_client_error(QX11EmbedContainer::Error error)
{
qDebug() << "error #" << error;
exit();
}
};

class X11EventFilter
{
public:
static bool x11_event_filter(void *msg, long *res)
{
XEvent *ev = reinterpret_cast<XEvent*>(msg);
if (ev->xany.type != EnterNotify) return NULL;

if (X11EventFilter::client == ev->xcrossing.subwindow) {
XDefineCursor
(QX11Info::display(), X11EventFilter::client,
XCreateFontCursor
(QX11Info::display(), XC_left_ptr));
}

return false;
}

static Window client;
};
Window X11EventFilter::client = 0;

/* ugly hack to prevent: undefined reference to `vtable... */
#include "main.moc"

int main(int argc, char *argv[])
{
QApplication *app = new QApplication(argc, argv);

if (app->arguments().count() != 2) {
qFatal("Error - expected window id as argument");
return 1;
}

app->setEventFilter(X11EventFilter::x11_event_filter);

QMdiArea *area = new QMdiArea();
area->show();

X11EventFilter::client = app->arguments()[1].toInt(0, 16);
new Container(area, X11EventFilter::client);

return app->exec();
}


Well, it works as I want. But, it doesn't look like the best solution to me.
Any suggestions for improvements? A plain Qt solution?

Added after 1 26 minutes:

The solution with XDefineCursor() was not that perfect - as expected. Now, instead of
using XDefineCursor(), I "find" and call unsetCursor() of the underlying QMdiSubWindow.

If I don't report back. I haven't found any other issues with that solution.