diff --git a/applications/display-server/dbusinterface.cpp b/applications/display-server/dbusinterface.cpp index f05866f5e..4e4c46931 100644 --- a/applications/display-server/dbusinterface.cpp +++ b/applications/display-server/dbusinterface.cpp @@ -20,7 +20,8 @@ DbusInterface::DbusInterface(QObject* parent) : QObject(parent), m_focused(nullptr), - buttonsDevNumber{QFileInfo(deviceSettings.getButtonsDevicePath()).baseName().midRef(5).toInt()} + m_buttonsDevNumber{QFileInfo(deviceSettings.getButtonsDevicePath()).baseName().midRef(5).toInt()}, + m_exlusiveMode{false} { engine.load(QUrl(QStringLiteral("qrc:/Workspace.qml"))); if(engine.rootObjects().isEmpty()){ @@ -280,7 +281,7 @@ QStringList DbusInterface::getSurfaces(QDBusMessage message){ QDBusUnixFileDescriptor DbusInterface::frameBuffer(QDBusMessage message){ Q_UNUSED(message); // TODO - only allow tarnish to make this call - return QDBusUnixFileDescriptor(::open("/dev/fb0", O_RDWR)); + return QDBusUnixFileDescriptor(guiThread->framebuffer()); } void DbusInterface::lower(QString identifier, QDBusMessage message){ @@ -346,9 +347,49 @@ void DbusInterface::focus(QString identifier, QDBusMessage message){ void DbusInterface::waitForNoRepaints(QDBusMessage message){ Q_UNUSED(message); // TODO - only allow tarnish to make this call +#ifdef EPAPER QEventLoop loop; connect(guiThread, &GUIThread::settled, &loop, &QEventLoop::quit); loop.exec(); +#endif +} + +void DbusInterface::enterExclusiveMode(QDBusMessage message){ + Q_UNUSED(message); + // TODO - only allow tarnish to make this call + O_INFO("Entering exclusive mode"); + m_exlusiveMode = true; + waitForNoRepaints(message); + evdevHandler->clear_buffers(); +} + +void DbusInterface::exitExclusiveMode(QDBusMessage message){ + Q_UNUSED(message); + // TODO - only allow tarnish to make this call + O_INFO("Exiting exclusive mode"); + m_exlusiveMode = false; +#ifdef EPAPER + guiThread->enqueue( + nullptr, + EPFrameBuffer::instance()->framebuffer()->rect(), + EPFrameBuffer::HighQualityGrayscale, + 0, + true + ); +#endif + waitForNoRepaints(message); +} + +void DbusInterface::exclusiveModeRepaint(QDBusMessage message){ + Q_UNUSED(message); + // TODO - only allow tarnish to make this call +#ifdef EPAPER + guiThread->sendUpdate( + EPFrameBuffer::instance()->framebuffer()->rect(), + EPFrameBuffer::HighQualityGrayscale, + 0 + ); +#endif } Connection* DbusInterface::focused(){ return m_focused; } @@ -372,7 +413,7 @@ void DbusInterface::serviceOwnerChanged(const QString& name, const QString& oldO } void DbusInterface::inputEvents(unsigned int device, const std::vector& events){ - if(m_focused != nullptr && device != buttonsDevNumber){ + if(!inExclusiveMode() && m_focused != nullptr && device != m_buttonsDevNumber){ m_focused->inputEvents(device, events); } for(auto connection : qAsConst(connections)){ @@ -382,6 +423,8 @@ void DbusInterface::inputEvents(unsigned int device, const std::vectorservicePid(message.service());; for(auto connection : qAsConst(connections)){ diff --git a/applications/display-server/dbusinterface.h b/applications/display-server/dbusinterface.h index 4a6d952eb..56fd84da9 100644 --- a/applications/display-server/dbusinterface.h +++ b/applications/display-server/dbusinterface.h @@ -40,6 +40,7 @@ class DbusInterface : public QObject, public QDBusContext { Connection* focused(); void setFocus(Connection* connection); void inputEvents(unsigned int device, const std::vector& events); + bool inExclusiveMode(); // Property getter/setters const QByteArray& clipboard(); @@ -71,6 +72,9 @@ public slots: void raise(QString identifier, QDBusMessage message); void focus(QString identifier, QDBusMessage message); void waitForNoRepaints(QDBusMessage message); + void enterExclusiveMode(QDBusMessage message); + void exitExclusiveMode(QDBusMessage message); + void exclusiveModeRepaint(QDBusMessage message); signals: void clipboardChanged(const QByteArray& data); @@ -92,7 +96,8 @@ private slots: QByteArray selection; QByteArray secondary; } clipboards; - unsigned int buttonsDevNumber; + unsigned int m_buttonsDevNumber; + bool m_exlusiveMode; Connection* getConnection(QDBusMessage message); Connection* getConnection(QString identifier); diff --git a/applications/display-server/evdevdevice.cpp b/applications/display-server/evdevdevice.cpp index f3bcb5ab1..cfc25a23e 100644 --- a/applications/display-server/evdevdevice.cpp +++ b/applications/display-server/evdevdevice.cpp @@ -49,6 +49,12 @@ void EvDevDevice::lock(){ exists() && device.lock(); } void EvDevDevice::unlock(){ exists() && device.locked && device.unlock(); } +void EvDevDevice::clear_buffer(){ + auto flood = build_flood(); + ::write(device.fd, flood, 512 * 8 * 4 * sizeof(input_event)); + delete flood; +} + void EvDevDevice::readEvents(){ Oxide::dispatchToThread(thread(), [this]{ notifier->setEnabled(false); @@ -70,9 +76,6 @@ void EvDevDevice::readEvents(){ emit inputEvents(events); events.clear(); } -// if(events.size() > 16){ -// emitSomeEvents(); -// } }while(res == LIBEVDEV_READ_STATUS_SUCCESS || res == LIBEVDEV_READ_STATUS_SYNC); emitSomeEvents(); if(res == -ENODEV){ @@ -102,3 +105,26 @@ void EvDevDevice::emitSomeEvents(){ } } } + +input_event EvDevDevice::createEvent(ushort type, ushort code, int value){ + struct input_event event; + event.type = type; + event.code = code; + event.value = value; + return event; +} + +input_event* EvDevDevice::build_flood(){ + auto n = 512 * 8; + auto num_inst = 4; + input_event* ev = (input_event*)malloc(sizeof(struct input_event) * n * num_inst); + memset(ev, 0, sizeof(input_event) * n * num_inst); + auto i = 0; + while (i < n) { + ev[i++] = createEvent(EV_ABS, ABS_DISTANCE, 1); + ev[i++] = createEvent(EV_SYN, 0, 0); + ev[i++] = createEvent(EV_ABS, ABS_DISTANCE, 2); + ev[i++] = createEvent(EV_SYN, 0, 0); + } + return ev; +} diff --git a/applications/display-server/evdevdevice.h b/applications/display-server/evdevdevice.h index 6f68309db..6d196502b 100644 --- a/applications/display-server/evdevdevice.h +++ b/applications/display-server/evdevdevice.h @@ -22,6 +22,7 @@ class EvDevDevice : public QObject{ bool exists(); void lock(); void unlock(); + void clear_buffer(); signals: void inputEvents(const std::vector events); @@ -38,4 +39,6 @@ public slots: libevdev* dev; void emitSomeEvents(); + input_event createEvent(ushort type, ushort code, int value); + input_event* build_flood(); }; diff --git a/applications/display-server/evdevhandler.cpp b/applications/display-server/evdevhandler.cpp index bddc3d0cb..ab57a5439 100644 --- a/applications/display-server/evdevhandler.cpp +++ b/applications/display-server/evdevhandler.cpp @@ -23,7 +23,8 @@ EvDevHandler* EvDevHandler::init(){ } EvDevHandler::EvDevHandler() -: QThread() +: QThread(), + m_clearing{false} { setObjectName("EvDevHandler"); reloadDevices(); @@ -34,6 +35,14 @@ EvDevHandler::EvDevHandler() EvDevHandler::~EvDevHandler(){} +void EvDevHandler::clear_buffers(){ + m_clearing = true; + for(auto input : qAsConst(devices)){ + input->clear_buffer(); + } + m_clearing = false; +} + bool EvDevHandler::hasDevice(event_device device){ for(auto input : qAsConst(devices)){ if(device.device.c_str() == input->path()){ @@ -49,7 +58,9 @@ void EvDevHandler::reloadDevices(){ if(!hasDevice(device) && device.fd > 0){ auto input = new EvDevDevice(this, device); connect(input, &EvDevDevice::inputEvents, this, [this, input](auto events){ - dbusInterface->inputEvents(input->number(), events); + if(!m_clearing){ + dbusInterface->inputEvents(input->number(), events); + } }, Qt::QueuedConnection); O_DEBUG(input->name() << "added"); devices.append(input); diff --git a/applications/display-server/evdevhandler.h b/applications/display-server/evdevhandler.h index 6109aec54..9264905e8 100644 --- a/applications/display-server/evdevhandler.h +++ b/applications/display-server/evdevhandler.h @@ -17,9 +17,11 @@ class EvDevHandler : public QThread{ static EvDevHandler* init(); EvDevHandler(); ~EvDevHandler(); + void clear_buffers(); private: QList devices; + bool m_clearing; bool hasDevice(event_device device); void reloadDevices(); }; diff --git a/applications/display-server/guithread.cpp b/applications/display-server/guithread.cpp index 28802660e..748dc474a 100644 --- a/applications/display-server/guithread.cpp +++ b/applications/display-server/guithread.cpp @@ -95,7 +95,7 @@ void GUIThread::enqueue( bool global, std::function callback ){ - if(isInterruptionRequested()){ + if(isInterruptionRequested() || dbusInterface->inExclusiveMode()){ if(callback != nullptr){ callback(); } @@ -165,7 +165,11 @@ void GUIThread::enqueue( notify(); } -void GUIThread::notify(){ m_repaintWait.notify_one(); } +void GUIThread::notify(){ + if(!dbusInterface->inExclusiveMode()){ + m_repaintWait.notify_one(); + } +} void GUIThread::clearFrameBuffer(){ EPFrameBuffer::instance()->framebuffer()->fill(Qt::white); @@ -203,6 +207,10 @@ void GUIThread::repaintSurface(QPainter* painter, QRect* rect, std::shared_ptrinExclusiveMode()){ + O_DEBUG("In exclusive mode, skipping redraw"); + return; + } if(!event.global){ if(event.surface == nullptr){ O_WARNING("surface missing"); diff --git a/applications/display-server/guithread.h b/applications/display-server/guithread.h index 59e4d78d0..5655c7d5c 100644 --- a/applications/display-server/guithread.h +++ b/applications/display-server/guithread.h @@ -44,7 +44,6 @@ public slots: std::function callback = nullptr ); void notify(); - void clearFrameBuffer(); int framebuffer(); void sendUpdate(const QRect& rect, EPFrameBuffer::WaveformMode waveform, unsigned int marker); @@ -59,9 +58,9 @@ public slots: QPoint m_screenOffset; QRect m_screenRect; + void clearFrameBuffer(); void repaintSurface(QPainter* painter, QRect* rect, std::shared_ptr surface); void redraw(RepaintRequest& event); - void scheduleUpdate(); QList> visibleSurfaces(); }; #endif diff --git a/applications/system-service/apibase.cpp b/applications/system-service/apibase.cpp index b0fead85b..bb2f056fd 100644 --- a/applications/system-service/apibase.cpp +++ b/applications/system-service/apibase.cpp @@ -51,7 +51,7 @@ QImage* getFrameBuffer(){ return nullptr; } file = new QFile(); - file->open(dup(qfd.fileDescriptor()), QFile::ReadOnly); + file->open(dup(qfd.fileDescriptor()), QFile::ReadWrite); uchar* data = file->map(0, file->size()); image = new QImage(data, 1404, 1872, 2808, QImage::Format_RGB16); } diff --git a/applications/system-service/application.cpp b/applications/system-service/application.cpp index f859c3c14..b2e3343a2 100644 --- a/applications/system-service/application.cpp +++ b/applications/system-service/application.cpp @@ -59,6 +59,15 @@ void Application::launchNoSecurityCheck(){ ); m_notification->display(); } + if(flags().contains("exclusive")){ + auto compositor = getCompositorDBus(); + compositor->enterExclusiveMode().waitForFinished(); + auto frameBuffer = getFrameBuffer(); + QPainter p(frameBuffer); + p.fillRect(frameBuffer->rect(), Qt::white); + p.end(); + compositor->exclusiveModeRepaint().waitForFinished(); + } if(m_process->program() != bin()){ m_process->setProgram(bin()); } @@ -155,6 +164,9 @@ void Application::interruptApplication(){ #else Q_UNUSED(t); #endif + if(flags().contains("exclusive")){ + m_backup = new QImage(getFrameBuffer()->copy()); + } if(!onPause().isEmpty()){ Oxide::Sentry::sentry_span(t, "onPause", "Run onPause action", [this](){ system(onPause().toStdString().c_str()); @@ -193,6 +205,9 @@ void Application::interruptApplication(){ startSpan("stopped", "Application is stopped"); } }); + if(flags().contains("exclusive")){ + getCompositorDBus()->exitExclusiveMode().waitForFinished(); + } }); } void Application::waitForPause(){ @@ -261,6 +276,21 @@ void Application::uninterruptApplication(){ return; } Oxide::Sentry::sentry_transaction("application", "uninterrupt", [this](Oxide::Sentry::Transaction* t){ + if(flags().contains("exclusive")){ + auto compositor = getCompositorDBus(); + compositor->enterExclusiveMode().waitForFinished(); + auto frameBuffer = getFrameBuffer(); + QPainter p(frameBuffer); + if(m_backup == nullptr){ + p.fillRect(frameBuffer->rect(), Qt::white); + }else{ + p.drawImage(frameBuffer->rect(), *m_backup, m_backup->rect()); + delete m_backup; + m_backup = nullptr; + } + p.end(); + compositor->exclusiveModeRepaint().waitForFinished(); + } #ifdef SENTRY if(t != nullptr){ sentry_transaction_set_tag(t->inner, "application", name().toStdString().c_str()); @@ -532,6 +562,10 @@ void Application::finished(int exitCode){ if(transient()){ unregister(); } + if(m_backup != nullptr){ + delete m_backup; + m_backup = nullptr; + } } void Application::readyReadStandardError(){ @@ -652,7 +686,7 @@ void Application::updateEnvironment(){ preload.append(sysfs_preload); } } - if(!flags().contains("nopreload") && !flags().contains("nopreload.compositor")){ + if(!flags().contains("exclusive") && !flags().contains("nopreload") && !flags().contains("nopreload.compositor")){ QString blight_client("/opt/lib/libblight_client.so"); if(!preload.contains(blight_client)){ preload.append(blight_client); diff --git a/applications/system-service/application.h b/applications/system-service/application.h index 8d7a97487..b844c82c4 100644 --- a/applications/system-service/application.h +++ b/applications/system-service/application.h @@ -202,6 +202,7 @@ private slots: int p_stderr_fd = -1; QTextStream* p_stderr = nullptr; Notification* m_notification = nullptr; + QImage* m_backup = nullptr; bool hasPermission(QString permission, const char* sender = __builtin_FUNCTION()); void delayUpTo(int milliseconds); diff --git a/assets/opt/usr/share/applications/xochitl.oxide b/assets/opt/usr/share/applications/xochitl.oxide index 7d090ee1b..5336c2eb6 100644 --- a/assets/opt/usr/share/applications/xochitl.oxide +++ b/assets/opt/usr/share/applications/xochitl.oxide @@ -3,8 +3,5 @@ "description": "Read documents and take notes", "bin": "/opt/bin/xochitl", "icon": "oxide:xochitl-48", - "flags": ["nosplash"], - "environment": { - "OXIDE_PRELOAD_FORCE_QT": "1" - } + "flags": ["nosplash", "exclusive"] } diff --git a/interfaces/blight.xml b/interfaces/blight.xml index b6fc0aac9..08b3f55ce 100644 --- a/interfaces/blight.xml +++ b/interfaces/blight.xml @@ -58,5 +58,11 @@ + + + + + + diff --git a/shared/libblight_client/main.cpp b/shared/libblight_client/main.cpp index ffbffe34d..a3ebec8f0 100644 --- a/shared/libblight_client/main.cpp +++ b/shared/libblight_client/main.cpp @@ -1028,7 +1028,7 @@ extern "C" { setenv("OXIDE_PRELOAD", std::to_string(getpgrp()).c_str(), true); setenv("RM2FB_ACTIVE", "1", true); setenv("RM2FB_SHIM", "1", true); - if(path != "/usr/bin/xochitl"){ + if(path != "/usr/bin/xochitl" && getenv("OXIDE_PRELOAD_ALLOW_RM2FB") == nullptr){ setenv("RM2FB_DISABLE", "1", true); }else{ unsetenv("RM2FB_DISABLE"); diff --git a/shared/liboxide/signalhandler.cpp b/shared/liboxide/signalhandler.cpp index 00084e3c0..6acf96bc9 100644 --- a/shared/liboxide/signalhandler.cpp +++ b/shared/liboxide/signalhandler.cpp @@ -48,7 +48,9 @@ namespace Oxide { static SignalHandler* instance; if(self != nullptr){ instance = self; - setup_unix_signal_handlers(); + QTimer::singleShot(0, qApp, []{ + setup_unix_signal_handlers(); + }); } return instance; } @@ -60,6 +62,9 @@ namespace Oxide { if(::socketpair(AF_UNIX, SOCK_STREAM, 0, sigUsr2Fd)){ qFatal("Couldn't create USR2 socketpair"); } + if(::socketpair(AF_UNIX, SOCK_STREAM, 0, sigContFd)){ + qFatal("Couldn't create CONT socketpair"); + } snUsr1 = new QSocketNotifier(sigUsr1Fd[1], QSocketNotifier::Read, this); connect(snUsr1, &QSocketNotifier::activated, this, &SignalHandler::handleSigUsr1, Qt::QueuedConnection);