diff --git a/autotests/server/CMakeLists.txt b/autotests/server/CMakeLists.txt --- a/autotests/server/CMakeLists.txt +++ b/autotests/server/CMakeLists.txt @@ -57,3 +57,15 @@ target_link_libraries( testTabletInterface Qt5::Test KF5::WaylandServer KF5::WaylandClient Wayland::Client) add_test(NAME kwayland-testTabletInterface COMMAND testTabletInterface) ecm_mark_as_test(testTabletInterface) + +######################################################## +# Test Keyboard Shortcuts Inhibitor Interface +######################################################## +ecm_add_qtwayland_client_protocol(KEYBOARD_SHORTCUTS_INHIBITOR_SRCS + PROTOCOL ${WaylandProtocols_DATADIR}/unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml + BASENAME keyboard-shortcuts-inhibit-unstable-v1 + ) +add_executable(testKeyboardShortcutsInhibitorInterface test_keyboard_shortcuts_inhibitor_interface.cpp ${KEYBOARD_SHORTCUTS_INHIBITOR_SRCS}) +target_link_libraries(testKeyboardShortcutsInhibitorInterface Qt5::Test KF5::WaylandServer KF5::WaylandClient Wayland::Client) +add_test(NAME kwayland-testKeyboardShortcutsInhibitorInterface COMMAND testKeyboardShortcutsInhibitorInterface) +ecm_mark_as_test(testKeyboardShortcutsInhibitorInterface) diff --git a/autotests/server/test_keyboard_shortcuts_inhibitor_interface.cpp b/autotests/server/test_keyboard_shortcuts_inhibitor_interface.cpp new file mode 100644 --- /dev/null +++ b/autotests/server/test_keyboard_shortcuts_inhibitor_interface.cpp @@ -0,0 +1,217 @@ +/* + SPDX-FileCopyrightText: 2020 Benjamin Port + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +// Qt +#include +#include +#include +// WaylandServer +#include "../../src/server/compositor_interface.h" +#include "../../src/server/display.h" +#include "../../src/server/keyboard_shortcuts_inhibit_interface.h" +#include "../../src/server/seat_interface.h" + +#include "../../src/client/compositor.h" +#include "../../src/client/connection_thread.h" +#include "../../src/client/event_queue.h" +#include "../../src/client/registry.h" +#include "../../src/client/seat.h" +#include "../../src/client/surface.h" + +#include "qwayland-keyboard-shortcuts-inhibit-unstable-v1.h" + +using namespace KWayland::Server; + +class KeyboardShortcutsInhibitManager : public QObject, public QtWayland::zwp_keyboard_shortcuts_inhibit_manager_v1 +{ + Q_OBJECT +public: + KeyboardShortcutsInhibitManager(wl_registry *registry, quint32 id, quint32 version) + : QtWayland::zwp_keyboard_shortcuts_inhibit_manager_v1(registry, id, version) + { + } + +}; + +class KeyboardShortcutsInhibitor : public QObject, public QtWayland::zwp_keyboard_shortcuts_inhibitor_v1 +{ + Q_OBJECT +public: + KeyboardShortcutsInhibitor(::zwp_keyboard_shortcuts_inhibitor_v1 *inhibitorV1) + : QtWayland::zwp_keyboard_shortcuts_inhibitor_v1(inhibitorV1) + { + } + + void zwp_keyboard_shortcuts_inhibitor_v1_active() override + { + emit inhibitorActive(); + } + + void zwp_keyboard_shortcuts_inhibitor_v1_inactive() override + { + emit inhibitorInactive(); + } + +Q_SIGNALS: + void inhibitorActive(); + void inhibitorInactive(); +}; + + +class TestKeyboardShortcutsInhibitorInterface : public QObject +{ + Q_OBJECT +public: + TestKeyboardShortcutsInhibitorInterface() + { + } + ~TestKeyboardShortcutsInhibitorInterface() override; + +private Q_SLOTS: + void initTestCase(); + void testKeyboardShortcuts(); + +private: + KWayland::Client::ConnectionThread *m_connection; + KWayland::Client::EventQueue *m_queue; + KWayland::Client::Compositor *m_clientCompositor; + KWayland::Client::Seat *m_clientSeat = nullptr; + + QThread *m_thread; + Display m_display; + SeatInterface *m_seat; + CompositorInterface *m_serverCompositor; + + KeyboardShortcutsInhibitManagerInterface *m_manager; + QVector m_surfaces; + QVector m_clientSurfaces; + KeyboardShortcutsInhibitManager *m_inhibitManagerClient = nullptr; + QVector *m_inhibitors; +}; + +static const QString s_socketName = QStringLiteral("kwin-wayland-server-keyboard-shortcuts-inhibitor-test-0"); + +void TestKeyboardShortcutsInhibitorInterface::initTestCase() +{ + m_display.setSocketName(s_socketName); + m_display.start(); + QVERIFY(m_display.isRunning()); + + m_seat = m_display.createSeat(this); + m_seat->create(); + m_serverCompositor = m_display.createCompositor(this); + m_serverCompositor->create(); + + m_manager = m_display.createKeyboardShortcutsInhibitManager(this); + QVERIFY(m_serverCompositor->isValid()); + connect(m_serverCompositor, &CompositorInterface::surfaceCreated, this, [this](SurfaceInterface *surface) { + m_surfaces += surface; + }); + + // setup connection + m_connection = new KWayland::Client::ConnectionThread; + QSignalSpy connectedSpy(m_connection, &KWayland::Client::ConnectionThread::connected); + m_connection->setSocketName(s_socketName); + + m_thread = new QThread(this); + m_connection->moveToThread(m_thread); + m_thread->start(); + + m_connection->initConnection(); + QVERIFY(connectedSpy.wait()); + QVERIFY(!m_connection->connections().isEmpty()); + + m_queue = new KWayland::Client::EventQueue(this); + QVERIFY(!m_queue->isValid()); + m_queue->setup(m_connection); + QVERIFY(m_queue->isValid()); + + auto registry = new KWayland::Client::Registry(this); + connect(registry, &KWayland::Client::Registry::interfaceAnnounced, this, [this, registry](const QByteArray &interface, quint32 id, quint32 version) { + if (interface == "zwp_keyboard_shortcuts_inhibit_manager_v1") { + m_inhibitManagerClient = new KeyboardShortcutsInhibitManager(registry->registry(), id, version); + } + }); + connect(registry, &KWayland::Client::Registry::seatAnnounced, this, [this, registry](quint32 name, quint32 version) { + m_clientSeat = registry->createSeat(name, version); + }); + registry->setEventQueue(m_queue); + QSignalSpy compositorSpy(registry, &KWayland::Client::Registry::compositorAnnounced); + registry->create(m_connection->display()); + QVERIFY(registry->isValid()); + registry->setup(); + wl_display_flush(m_connection->display()); + + QVERIFY(compositorSpy.wait()); + m_clientCompositor = registry->createCompositor(compositorSpy.first().first().value(), compositorSpy.first().last().value(), this); + QVERIFY(m_clientCompositor->isValid()); + + QSignalSpy surfaceSpy(m_serverCompositor, &CompositorInterface::surfaceCreated); + for (int i = 0; i < 3; ++i) { + KWayland::Client::Surface *s =m_clientCompositor->createSurface(this); + m_clientSurfaces += s->operator wl_surface *(); + } + QVERIFY(surfaceSpy.count() < 3 && surfaceSpy.wait(200)); + QVERIFY(m_surfaces.count() == 3); + QVERIFY(m_inhibitManagerClient); +} + +TestKeyboardShortcutsInhibitorInterface::~TestKeyboardShortcutsInhibitorInterface() +{ + if (m_queue) { + delete m_queue; + m_queue = nullptr; + } + if (m_thread) { + m_thread->quit(); + m_thread->wait(); + delete m_thread; + m_thread = nullptr; + } + m_connection->deleteLater(); + m_connection = nullptr; +} + +void TestKeyboardShortcutsInhibitorInterface::testKeyboardShortcuts() +{ + auto clientSurface = m_clientSurfaces[0]; + auto surface = m_surfaces[0]; + + // Test creation + auto inhibitorClientV1 = m_inhibitManagerClient->inhibit_shortcuts(clientSurface, m_clientSeat->operator wl_seat *()); + auto inhibitorClient = new KeyboardShortcutsInhibitor(inhibitorClientV1); + QSignalSpy inhibitorActiveSpy(inhibitorClient, &KeyboardShortcutsInhibitor::inhibitorActive); + QSignalSpy inhibitorInactiveSpy(inhibitorClient, &KeyboardShortcutsInhibitor::inhibitorInactive); + QSignalSpy inhibitorCreatedSpy(m_manager, &KeyboardShortcutsInhibitManagerInterface::inhibitorCreated); + QVERIFY(inhibitorCreatedSpy.wait() || inhibitorCreatedSpy.count() == 1); + auto inhibitorServer = m_manager->getShortcutsInhibitor(surface, m_seat); + + // Test deactivate + inhibitorServer->setActive(false); + QVERIFY(inhibitorInactiveSpy.wait() || inhibitorInactiveSpy.count() == 1); + + // Test activate + inhibitorServer->setActive(true); + QVERIFY(inhibitorInactiveSpy.wait() || inhibitorInactiveSpy.count() == 1); + + // Test creating for another surface + m_inhibitManagerClient->inhibit_shortcuts(m_clientSurfaces[1], m_clientSeat->operator wl_seat *()); + QVERIFY(inhibitorCreatedSpy.wait() || inhibitorCreatedSpy.count() == 2); + + // Test destroy is working + inhibitorClient->destroy(); + m_inhibitManagerClient->inhibit_shortcuts(clientSurface, m_clientSeat->operator wl_seat *()); + QVERIFY(inhibitorCreatedSpy.wait() || inhibitorCreatedSpy.count() == 3); + + // Test creating with same surface / seat (expect error) + QSignalSpy errorOccured(m_connection, &KWayland::Client::ConnectionThread::errorOccurred); + m_inhibitManagerClient->inhibit_shortcuts(m_clientSurfaces[0], m_clientSeat->operator wl_seat *()); + QVERIFY(errorOccured.wait() || errorOccured.count() == 1); +} + +QTEST_GUILESS_MAIN(TestKeyboardShortcutsInhibitorInterface) + +#include "test_keyboard_shortcuts_inhibitor_interface.moc" diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt --- a/src/server/CMakeLists.txt +++ b/src/server/CMakeLists.txt @@ -20,6 +20,7 @@ idleinhibit_interface.cpp idleinhibit_interface_v1.cpp keyboard_interface.cpp + keyboard_shortcuts_inhibit_interface.cpp keystate_interface.cpp linuxdmabuf_v1_interface.cpp output_interface.cpp @@ -234,6 +235,11 @@ BASENAME tablet-unstable-v2 ) +ecm_add_qtwayland_server_protocol(SERVER_LIB_SRCS + PROTOCOL ${WaylandProtocols_DATADIR}/unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml + BASENAME keyboard-shortcuts-inhibit-unstable-v1 +) + set(SERVER_GENERATED_SRCS ${CMAKE_CURRENT_BINARY_DIR}/wayland-blur-client-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-blur-server-protocol.h @@ -293,6 +299,7 @@ ${CMAKE_CURRENT_BINARY_DIR}/wayland-xdg-shell-v6-server-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/qwayland-server-tablet-unstable-v2.h ${CMAKE_CURRENT_BINARY_DIR}/qwayland-server-tablet-unstable-v2.cpp + ${CMAKE_CURRENT_BINARY_DIR}/qwayland-server-keyboard-shortcuts-inhibit-unstable-v1.h ) set_source_files_properties(${SERVER_GENERATED_SRCS} PROPERTIES SKIP_AUTOMOC ON) @@ -351,6 +358,7 @@ idle_interface.h idleinhibit_interface.h keyboard_interface.h + keyboard_shortcuts_inhibit_interface.h keystate_interface.h linuxdmabuf_v1_interface.h output_interface.h diff --git a/src/server/display.h b/src/server/display.h --- a/src/server/display.h +++ b/src/server/display.h @@ -13,6 +13,7 @@ #include #include "clientconnection.h" +#include "keyboard_shortcuts_inhibit_interface.h" struct wl_client; struct wl_display; @@ -80,6 +81,7 @@ class KeyStateInterface; class LinuxDmabufUnstableV1Interface; class TabletManagerInterface; +class KeyboardShortcutsInhibitManagerInterface; /** * @brief Class holding the Wayland server display loop. @@ -318,6 +320,13 @@ */ TabletManagerInterface *createTabletManagerInterface(QObject *parent = nullptr); + /** + * Creates the TestKeyboardShortcutsInhibitorInterface + * + * @since 5.71 + */ + KeyboardShortcutsInhibitManagerInterface *createKeyboardShortcutsInhibitManager(QObject *object); + /** * Gets the ClientConnection for the given @p client. * If there is no ClientConnection yet for the given @p client, it will be created. diff --git a/src/server/display.cpp b/src/server/display.cpp --- a/src/server/display.cpp +++ b/src/server/display.cpp @@ -15,6 +15,7 @@ #include "fakeinput_interface.h" #include "idle_interface.h" #include "idleinhibit_interface_p.h" +#include "keyboard_shortcuts_inhibit_interface.h" #include "keystate_interface.h" #include "linuxdmabuf_v1_interface.h" #include "logging.h" @@ -531,6 +532,13 @@ return d; } +KeyboardShortcutsInhibitManagerInterface *Display::createKeyboardShortcutsInhibitManager(QObject *parent) +{ + auto d = new KeyboardShortcutsInhibitManagerInterface(this, parent); + connect(this, &Display::aboutToTerminate, d, [d] { delete d; }); + return d; +} + void Display::createShm() { Q_ASSERT(d->display); diff --git a/src/server/keyboard_shortcuts_inhibit_interface.h b/src/server/keyboard_shortcuts_inhibit_interface.h new file mode 100644 --- /dev/null +++ b/src/server/keyboard_shortcuts_inhibit_interface.h @@ -0,0 +1,78 @@ +/* + SPDX-FileCopyrightText: 2020 Benjamin Port + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#ifndef WAYLAND_SERVER_KEYBOARD_SHORTCUTS_INHIBIT_INTERFACE_H +#define WAYLAND_SERVER_KEYBOARD_SHORTCUTS_INHIBIT_INTERFACE_H + +#include +#include +#include "seat_interface.h" + +namespace KWayland +{ +namespace Server +{ +/** + * This is an implementation of wayland-protocols/unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml + * + * This class is just the means to get a @class KeyboardShortcutsInhibitorInterface, which is + * the class that will have all of the information we need. + * + * @since 5.71 + */ + +class KeyboardShortcutsInhibitManagerInterface; + +class KWAYLANDSERVER_EXPORT KeyboardShortcutsInhibitorInterface : public QObject +{ + Q_OBJECT + +public: + ~KeyboardShortcutsInhibitorInterface() override; + + SurfaceInterface *surface() const; + SeatInterface *seat() const; + void setActive(bool active); + bool isActive() const; +private: + friend class KeyboardShortcutsInhibitManagerInterface; + explicit KeyboardShortcutsInhibitorInterface(wl_resource *resource, int id, SurfaceInterface *surface, SeatInterface *seat, KeyboardShortcutsInhibitManagerInterface *manager); + class Private; + QScopedPointer d; +}; + + +class KWAYLANDSERVER_EXPORT KeyboardShortcutsInhibitManagerInterface : public QObject +{ + Q_OBJECT + +public: + ~KeyboardShortcutsInhibitManagerInterface() override; + + /** + * return shortucts inhibitor associated with surface and seat, if no shortcut are associated, return nullptr + * @param surface + * @param seat + * @return + */ + KeyboardShortcutsInhibitorInterface *getShortcutsInhibitor(SurfaceInterface *surface, SeatInterface *seat) const; + +Q_SIGNALS: + void inhibitorCreated(KeyboardShortcutsInhibitorInterface *inhibitor); + +private: + friend class Display; + friend class KeyboardShortcutsInhibitorInterface; + explicit KeyboardShortcutsInhibitManagerInterface(Display *d, QObject *parent); + void removeInhibitor(SurfaceInterface *const surface, SeatInterface *const seat); + class Private; + QScopedPointer d; +}; + +} +} + +#endif diff --git a/src/server/keyboard_shortcuts_inhibit_interface.cpp b/src/server/keyboard_shortcuts_inhibit_interface.cpp new file mode 100644 --- /dev/null +++ b/src/server/keyboard_shortcuts_inhibit_interface.cpp @@ -0,0 +1,180 @@ +/* + SPDX-FileCopyrightText: 2020 Benjamin Port + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#include "keyboard_shortcuts_inhibit_interface.h" + +#include + +#include "display.h" +#include "seat_interface.h" +#include "surface_interface.h" +#include "tablet_interface.h" + +using namespace KWayland; +using namespace Server; + +static int s_version = 1; + +class KeyboardShortcutsInhibitorInterface::Private : public QtWaylandServer::zwp_keyboard_shortcuts_inhibitor_v1 +{ +public: + explicit Private(wl_resource *resource, int id, SurfaceInterface *surface, SeatInterface *seat, KeyboardShortcutsInhibitManagerInterface *manager, KeyboardShortcutsInhibitorInterface* q); + void zwp_keyboard_shortcuts_inhibitor_v1_destroy_resource(Resource *resource) override; + void zwp_keyboard_shortcuts_inhibitor_v1_destroy(Resource *resource) override; + + KeyboardShortcutsInhibitorInterface *const q; + KeyboardShortcutsInhibitManagerInterface *m_manager; + SurfaceInterface *const m_surface; + SeatInterface *const m_seat; + bool m_active; +}; + +KeyboardShortcutsInhibitorInterface::Private::Private(wl_resource *resource, int id, SurfaceInterface *surface, + SeatInterface *seat, + KeyboardShortcutsInhibitManagerInterface *manager, + KeyboardShortcutsInhibitorInterface *q) + : zwp_keyboard_shortcuts_inhibitor_v1() + , q(q) + , m_manager(manager) + , m_surface(surface) + , m_seat(seat) + , m_active(false) +{ + int version = wl_resource_get_version(resource); + init(resource->client, id, version); +} + +void KeyboardShortcutsInhibitorInterface::Private::zwp_keyboard_shortcuts_inhibitor_v1_destroy(QtWaylandServer::zwp_keyboard_shortcuts_inhibitor_v1::Resource *resource) +{ + // Ensure manager don't track anymore this inhibitor + m_manager->removeInhibitor(m_surface, m_seat); + wl_resource_destroy(resource->handle); +} + +void KeyboardShortcutsInhibitorInterface::Private::zwp_keyboard_shortcuts_inhibitor_v1_destroy_resource(QtWaylandServer::zwp_keyboard_shortcuts_inhibitor_v1::Resource *resource) +{ + Q_UNUSED(resource) + delete q; +} + +KeyboardShortcutsInhibitorInterface::KeyboardShortcutsInhibitorInterface(wl_resource *resource, int id, SurfaceInterface *surface, SeatInterface *seat, KeyboardShortcutsInhibitManagerInterface *manager) + : QObject(nullptr) + , d(new Private(resource, id, surface, seat, manager, this)) +{ +} + +KeyboardShortcutsInhibitorInterface::~KeyboardShortcutsInhibitorInterface() = default; + +void KeyboardShortcutsInhibitorInterface::setActive(bool active) +{ + if (d->m_active == active) { + return; + } + d->m_active = active; + if (active) { + d->send_active(); + } else { + d->send_inactive(); + } +} + +bool KeyboardShortcutsInhibitorInterface::isActive() const +{ + return d->m_active; +} + +SeatInterface *KeyboardShortcutsInhibitorInterface::seat() const +{ + return d->m_seat; +} + +SurfaceInterface *KeyboardShortcutsInhibitorInterface::surface() const +{ + return d->m_surface; +} + +class KeyboardShortcutsInhibitManagerInterface::Private : public QtWaylandServer::zwp_keyboard_shortcuts_inhibit_manager_v1 +{ +public: + explicit Private(Display *display, KeyboardShortcutsInhibitManagerInterface *q); + void zwp_keyboard_shortcuts_inhibit_manager_v1_inhibit_shortcuts(Resource *resource, uint32_t id, struct ::wl_resource *surface_resource, struct ::wl_resource *seat_resource) override; + KeyboardShortcutsInhibitorInterface *getShortcutsInhibitor(SurfaceInterface *surface, SeatInterface *seat) const; + void zwp_keyboard_shortcuts_inhibit_manager_v1_destroy_resource(Resource *resource) override; + void zwp_keyboard_shortcuts_inhibit_manager_v1_destroy(Resource *resource) override; + void removeInhibitor(SurfaceInterface *const surface, SeatInterface *const seat); + + KeyboardShortcutsInhibitManagerInterface *const q; + Display *const m_display; + QHash, KeyboardShortcutsInhibitorInterface *> m_inhibitors; + +}; + +KeyboardShortcutsInhibitManagerInterface::Private::Private(Display *display, KeyboardShortcutsInhibitManagerInterface *q) + : zwp_keyboard_shortcuts_inhibit_manager_v1(*display, s_version) + , q(q) + , m_display(display) +{ +} + +void KeyboardShortcutsInhibitManagerInterface::Private::zwp_keyboard_shortcuts_inhibit_manager_v1_inhibit_shortcuts( + QtWaylandServer::zwp_keyboard_shortcuts_inhibit_manager_v1::Resource *resource, uint32_t id, + wl_resource *surface_resource, wl_resource *seat_resource) +{ + SeatInterface* seat = SeatInterface::get(seat_resource); + SurfaceInterface* surface = SurfaceInterface::get(surface_resource); + if (m_inhibitors.contains({ surface, seat })) { + wl_resource_post_error(resource->handle, ZWP_KEYBOARD_SHORTCUTS_INHIBIT_MANAGER_V1_ERROR_ALREADY_INHIBITED, + "the shortcuts are already inhibited for this surface and seat"); + return; + } + auto inhibitor = new KeyboardShortcutsInhibitorInterface(resource->handle, id, surface, seat, q); + m_inhibitors[{ surface, seat }] = inhibitor; + Q_EMIT q->inhibitorCreated(inhibitor); + inhibitor->setActive(true); +} + +KeyboardShortcutsInhibitorInterface *KeyboardShortcutsInhibitManagerInterface::Private::getShortcutsInhibitor(SurfaceInterface *surface, + SeatInterface *seat) const +{ + return m_inhibitors.value({ surface, seat }); +} + +void KeyboardShortcutsInhibitManagerInterface::Private::zwp_keyboard_shortcuts_inhibit_manager_v1_destroy_resource( + QtWaylandServer::zwp_keyboard_shortcuts_inhibit_manager_v1::Resource *resource) +{ + Q_UNUSED(resource) + delete q; +} + +void KeyboardShortcutsInhibitManagerInterface::Private::zwp_keyboard_shortcuts_inhibit_manager_v1_destroy( + QtWaylandServer::zwp_keyboard_shortcuts_inhibit_manager_v1::Resource *resource) +{ + wl_resource_destroy(resource->handle); +} + +void KeyboardShortcutsInhibitManagerInterface::Private::removeInhibitor(SurfaceInterface *const surface, SeatInterface *const seat) +{ + m_inhibitors.remove({ surface, seat }); +} + +KeyboardShortcutsInhibitManagerInterface::KeyboardShortcutsInhibitManagerInterface(Display *display, QObject *parent) + : QObject(parent) + , d(new Private(display, this)) +{ +} + +KeyboardShortcutsInhibitManagerInterface::~KeyboardShortcutsInhibitManagerInterface() = default; + +KeyboardShortcutsInhibitorInterface *Server::KeyboardShortcutsInhibitManagerInterface::getShortcutsInhibitor(SurfaceInterface *surface, SeatInterface *seat) const +{ + return d->getShortcutsInhibitor(surface, seat); +} + +void KeyboardShortcutsInhibitManagerInterface::removeInhibitor(SurfaceInterface *const surface, + SeatInterface *const seat) +{ + d->removeInhibitor(surface, seat); +}