Client: Add safer fromObject function to scanner

Makes the scanner produce generated static functions such as

QtWaylandClient::wl_surface *wl_surface::fromObject(struct ::wl_surface *object);

Which casts from the wayland-scanner generated types, such as struct ::wl_surface *,
to types types generated by qtwaylandscanner, but performs a check to see if
the listener is set to the wrapper class first (at least for interfaces with events).

This lets us easily fix crashes in a couple of places where we receive events
with wayland objects that we didn't create.

Also adds nullptr checks whenever we use the fromWlSurface() and fromWlOutput()
functions to handle failed conversions.

Task-number: QTBUG-73801
Fixes: QTBUG-74085
Change-Id: I9f33c31c7d1a939ccb3ebbbcb0eb67af10037237
Reviewed-by: Jaroslaw Kubik <jarek@froglogic.com>
Reviewed-by: Paul Olav Tvete <paul.tvete@qt.io>
This commit is contained in:
Johan Klokkhammer Helsing 2019-04-08 13:51:28 +02:00 committed by Aapo Keskimolo
parent 7c682530d8
commit c6476ae915
6 changed files with 48 additions and 10 deletions

View File

@ -151,9 +151,13 @@ void QWaylandDataDevice::data_device_drop()
void QWaylandDataDevice::data_device_enter(uint32_t serial, wl_surface *surface, wl_fixed_t x, wl_fixed_t y, wl_data_offer *id)
{
m_enterSerial = serial;
m_dragWindow = QWaylandWindow::fromWlSurface(surface)->window();
auto *dragWaylandWindow = QWaylandWindow::fromWlSurface(surface);
if (!dragWaylandWindow)
return; // Ignore foreign surfaces
m_dragWindow = dragWaylandWindow->window();
m_dragPoint = calculateDragPosition(x, y, m_dragWindow);
m_enterSerial = serial;
QMimeData *dragData = nullptr;
Qt::DropActions supportedActions;

View File

@ -555,6 +555,9 @@ void QWaylandInputDevice::Pointer::pointer_enter(uint32_t serial, struct wl_surf
QWaylandWindow *window = QWaylandWindow::fromWlSurface(surface);
if (!window)
return; // Ignore foreign surfaces
if (mFocus) {
qCWarning(lcQpaWayland) << "The compositor sent a wl_pointer.enter event before sending a"
<< "leave event first, this is not allowed by the wayland protocol"
@ -596,10 +599,12 @@ void QWaylandInputDevice::Pointer::pointer_leave(uint32_t time, struct wl_surfac
if (!surface)
return;
if (!QWaylandWindow::mouseGrab()) {
QWaylandWindow *window = QWaylandWindow::fromWlSurface(surface);
auto *window = QWaylandWindow::fromWlSurface(surface);
if (!window)
return; // Ignore foreign surfaces
if (!QWaylandWindow::mouseGrab())
setFrameEvent(new LeaveEvent(window, mSurfacePos, mGlobalPos));
}
invalidateFocus();
mButtons = Qt::NoButton;
@ -1264,9 +1269,13 @@ void QWaylandInputDevice::Touch::touch_down(uint32_t serial,
if (!surface)
return;
auto *window = QWaylandWindow::fromWlSurface(surface);
if (!window)
return; // Ignore foreign surfaces
mParent->mTime = time;
mParent->mSerial = serial;
mFocus = QWaylandWindow::fromWlSurface(surface);
mFocus = window;
mParent->mQDisplay->setLastInputDevice(mParent, serial, mFocus);
mParent->handleTouchPoint(id, wl_fixed_to_double(x), wl_fixed_to_double(y), Qt::TouchPointPressed);
}

View File

@ -70,7 +70,11 @@ void QWaylandQtKeyExtension::zqt_key_v1_key(struct wl_surface *surface,
}
QWaylandInputDevice *dev = inputDevices.first();
QWaylandWindow *win = surface ? QWaylandWindow::fromWlSurface(surface) : dev->keyboardFocus();
auto *win = surface ? QWaylandWindow::fromWlSurface(surface) : nullptr;
if (!win)
win = dev->keyboardFocus();
if (!win || !win->window()) {
qWarning("qt_key_extension: handle_qtkey: No keyboard focus");

View File

@ -190,8 +190,9 @@ QWaylandScreen * QWaylandScreen::waylandScreenFromWindow(QWindow *window)
QWaylandScreen *QWaylandScreen::fromWlOutput(::wl_output *output)
{
auto wlOutput = static_cast<QtWayland::wl_output *>(wl_output_get_user_data(output));
return static_cast<QWaylandScreen *>(wlOutput);
if (auto *o = QtWayland::wl_output::fromObject(output))
return static_cast<QWaylandScreen *>(o);
return nullptr;
}
void QWaylandScreen::output_mode(uint32_t flags, int width, int height, int refresh)

View File

@ -65,7 +65,9 @@ QWaylandScreen *QWaylandSurface::oldestEnteredScreen()
QWaylandSurface *QWaylandSurface::fromWlSurface(::wl_surface *surface)
{
return static_cast<QWaylandSurface *>(static_cast<QtWayland::wl_surface *>(wl_surface_get_user_data(surface)));
if (auto *s = QtWayland::wl_surface::fromObject(surface))
return static_cast<QWaylandSurface *>(s);
return nullptr;
}
void QWaylandSurface::handleScreenRemoved(QScreen *qScreen)
@ -79,6 +81,9 @@ void QWaylandSurface::surface_enter(wl_output *output)
{
auto addedScreen = QWaylandScreen::fromWlOutput(output);
if (!addedScreen)
return;
if (m_screens.contains(addedScreen)) {
qCWarning(lcQpaWayland)
<< "Ignoring unexpected wl_surface.enter received for output with id:"
@ -95,6 +100,10 @@ void QWaylandSurface::surface_enter(wl_output *output)
void QWaylandSurface::surface_leave(wl_output *output)
{
auto *removedScreen = QWaylandScreen::fromWlOutput(output);
if (!removedScreen)
return;
bool wasRemoved = m_screens.removeOne(removedScreen);
if (!wasRemoved) {
qCWarning(lcQpaWayland)

View File

@ -986,6 +986,7 @@ bool Scanner::process()
printf("\n");
printf(" struct ::%s *object() { return m_%s; }\n", interfaceName, interfaceName);
printf(" const struct ::%s *object() const { return m_%s; }\n", interfaceName, interfaceName);
printf(" static %s *fromObject(struct ::%s *object);\n", interfaceName, interfaceName);
printf("\n");
printf(" bool isInitialized() const;\n");
printf("\n");
@ -1130,6 +1131,16 @@ bool Scanner::process()
printf(" }\n");
printf("\n");
printf(" %s *%s::fromObject(struct ::%s *object)\n", interfaceName, interfaceName, interfaceName);
printf(" {\n");
if (hasEvents) {
printf(" if (wl_proxy_get_listener((struct ::wl_proxy *)object) != (void *)&m_%s_listener)\n", interfaceName);
printf(" return nullptr;\n");
}
printf(" return static_cast<%s *>(%s_get_user_data(object));\n", interfaceName, interfaceName);
printf(" }\n");
printf("\n");
printf(" bool %s::isInitialized() const\n", interfaceName);
printf(" {\n");
printf(" return m_%s != nullptr;\n", interfaceName);