From 726bf58428f937b9ef40684f2a5f38c590ce738b Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Tue, 15 Aug 2017 19:17:40 +0200 Subject: Handle multiscreen properly, make background persistent --- CMakeLists.txt | 8 +++++- src/loginform.cpp | 1 + src/main.cpp | 74 ++++++++++++++++++++++++++++++++++++++++++++++++------ src/mainwindow.cpp | 38 +++++++++++++++++----------- src/mainwindow.h | 10 ++++++-- src/x11util.cpp | 62 +++++++++++++++++++++++++++++++++++++++++++++ src/x11util.h | 9 +++++++ 7 files changed, 177 insertions(+), 25 deletions(-) create mode 100644 src/x11util.cpp create mode 100644 src/x11util.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 2fd0179..55282d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,7 @@ file(GLOB_RECURSE UIS src/*.ui) find_package(Qt5Widgets REQUIRED) find_package(Qt5Svg REQUIRED) +FIND_PACKAGE(X11 REQUIRED) QT5_ADD_RESOURCES(RSCS qt-lightdm-greeter.qrc) @@ -29,7 +30,12 @@ include_directories ( ${CMAKE_CURRENT_SOURCE_DIR} add_executable ( qt-lightdm-greeter ${SRCS} ${RSCS} ${UI_HEADERS} ) -target_link_libraries ( qt-lightdm-greeter Qt5::Widgets Qt5::Svg ${LIGHTDM_QT_LIBRARIES} ) +target_link_libraries ( qt-lightdm-greeter + Qt5::Widgets + Qt5::Svg + ${LIGHTDM_QT_LIBRARIES} + ${X11_LIBRARIES} +) install(TARGETS ${PROJECT} RUNTIME DESTINATION bin) diff --git a/src/loginform.cpp b/src/loginform.cpp index ced5f1e..930c7a6 100644 --- a/src/loginform.cpp +++ b/src/loginform.cpp @@ -37,6 +37,7 @@ LoginForm::LoginForm(QWidget *parent) : LoginForm::~LoginForm() { + delete ui; } diff --git a/src/main.cpp b/src/main.cpp index c0ce7bc..50a8d38 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -11,18 +11,23 @@ #include #include #include +#include +#include #include #include "settings.h" #include "mainwindow.h" +#include "x11util.h" -QFile logfile; -QTextStream ts; +static void messageHandler(QtMsgType type, const QMessageLogContext&, const QString& msg) +{ + std::cerr << type << ": " << msg.toUtf8().constData() << '\n'; +} -void messageHandler(QtMsgType type, const QMessageLogContext&, const QString& msg) +static inline int size(const QRect& r) { - std::cerr << type << ": " << msg.toLatin1().data() << "\n"; + return r.width() * r.height(); } int main(int argc, char *argv[]) @@ -38,12 +43,66 @@ int main(int argc, char *argv[]) QIcon::setThemeName(Settings().iconThemeName()); } + // Build background for X server, in case we start a session that + // doesn't set one on its own this will make sure it's not simply + // black + QImage entire; + QSize desktopSize = QApplication::desktop()->size(); + qDebug() << "Desktop full size is " << desktopSize; + entire = QImage(desktopSize, QImage::Format_RGB32); + QPainter painter(&entire); + + // Get a list of non-overlapping screens, as this might lead to a broken + // greeter with main windows covering other login forms + QMap screens; + for (int i = 0; i < QApplication::desktop()->screenCount(); ++i) { + QRect r = QApplication::desktop()->screenGeometry(i); + QMutableMapIteratorit(screens); + while (it.hasNext()) { + it.next(); + if (!it.value().intersects(r)) + continue; + // Overlap, bigger wins + if (size(it.value()) >= size(r)) { + // Existing is bigger + goto skip_rect; + } + // New is bigger, remove existing and keep going + it.remove(); + } + // We reached here, so add new window + screens.insert(i, r); + skip_rect: ; // Do nothing + } + // Determine primary screen + int primary; + if (screens.contains(QApplication::desktop()->primaryScreen())) { + // Primary screen still in selection + primary = QApplication::desktop()->primaryScreen(); + } else { + // Fallback to first one + primary = screens.begin().key(); + } + + // Now set up all the screens MainWindow *focusWindow = 0; - for (int i = 0; i < QApplication::desktop()->screenCount(); ++i) { - MainWindow *w = new MainWindow(i); + QMapIterator it(screens); + while (it.hasNext()) { + it.next(); + MainWindow *w = new MainWindow(primary == it.key(), it.key(), it.value()); w->show(); - if (w->showLoginForm()) + if (w->showLoginForm()) { focusWindow = w; + } + if (!entire.isNull()) { + QPoint p = it.value().topLeft(); + painter.drawImage(p, w->getBackground()); + } + } + + if (!entire.isNull()) { + qDebug() << "Setting x background"; + AddPixmapToBackground(entire.constBits(), entire.width(), entire.height(), 24, entire.bytesPerLine(), entire.byteCount()); } // Ensure we set the primary screen's widget as active when there @@ -55,3 +114,4 @@ int main(int argc, char *argv[]) return a.exec(); } + diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 56f927f..314493c 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -16,14 +16,13 @@ #include "loginform.h" #include "settings.h" -MainWindow::MainWindow(int screen, QWidget *parent) : +MainWindow::MainWindow(bool primary, int screen, const QRect &screenRect, QWidget *parent) : QWidget(parent), - m_Screen(screen) + m_Screen(screen), + m_Primary(primary) { setObjectName(QString("MainWindow_%1").arg(screen)); - - QRect screenRect = QApplication::desktop()->screenGeometry(screen); setGeometry(screenRect); setBackground(); @@ -59,7 +58,7 @@ MainWindow::~MainWindow() bool MainWindow::showLoginForm() { - return m_Screen == QApplication::desktop()->primaryScreen(); + return m_Primary; } void MainWindow::setFocus(Qt::FocusReason reason) @@ -98,29 +97,38 @@ int MainWindow::getOffset(QString settingsOffset, int maxVal, int defaultVal) void MainWindow::setBackground() { - QImage backgroundImage; QSettings greeterSettings(CONFIG_FILE, QSettings::IniFormat); if (greeterSettings.contains(BACKGROUND_IMAGE_KEY)) { - QString pathToBackgroundImage = greeterSettings.value(BACKGROUND_IMAGE_KEY).toString(); + QString pathTom_background = greeterSettings.value(BACKGROUND_IMAGE_KEY).toString(); - backgroundImage = QImage(pathToBackgroundImage); - if (backgroundImage.isNull()) { - qWarning() << "Not able to read" << pathToBackgroundImage << "as image"; + m_background = QImage(pathTom_background); + if (m_background.isNull()) { + qWarning() << "Not able to read" << pathTom_background << "as image"; } } + if (m_background.isNull()) { + m_background = QImage(2, 2, QImage::Format_RGB32); + m_background.setPixel(0, 0, 0xffffffff); + m_background.setPixel(1, 0, 0xff888687); + m_background.setPixel(0, 1, 0xff888687); + m_background.setPixel(1, 1, 0xfff9a72b); + } QPalette palette; QRect rect = QApplication::desktop()->screenGeometry(m_Screen); - if (backgroundImage.isNull()) { + if (m_background.isNull()) { palette.setColor(QPalette::Background, Qt::black); } else { - backgroundImage = backgroundImage.scaled(rect.width(), rect.height(), Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); - int xoff = (backgroundImage.width() - rect.width()) / 2; - int yoff = (backgroundImage.height() - rect.height()) / 2; - QBrush brush(backgroundImage.copy(xoff, yoff, backgroundImage.width(), backgroundImage.height())); + m_background = m_background.scaled(rect.width(), rect.height(), Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); + int xoff = (m_background.width() - rect.width()) / 2; + int yoff = (m_background.height() - rect.height()) / 2; + if (xoff != 0 || yoff != 0) { + m_background = m_background.copy(xoff, yoff, m_background.width(), m_background.height()); + } + QBrush brush(m_background); palette.setBrush(this->backgroundRole(), brush); } this->setPalette(palette); diff --git a/src/mainwindow.h b/src/mainwindow.h index 738d423..7d20a6e 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -11,6 +11,8 @@ #include #include +#include +#include #include "loginform.h" @@ -23,19 +25,23 @@ class MainWindow : public QWidget Q_OBJECT public: - explicit MainWindow(int screen, QWidget *parent = 0); + explicit MainWindow(bool primary, int screen, const QRect &rect, QWidget *parent = 0); ~MainWindow(); void setFocus(Qt::FocusReason reason); bool showLoginForm(); - LoginForm* loginForm() { return m_LoginForm;} + QImage& getBackground() { return m_background; } + + LoginForm* loginForm() { return m_LoginForm; } private: int getOffset(QString offset, int maxVal, int defaultVal); void setBackground(); int m_Screen; + bool m_Primary; LoginForm* m_LoginForm; + QImage m_background; }; #endif // MAINWINDOW_H diff --git a/src/x11util.cpp b/src/x11util.cpp new file mode 100644 index 0000000..f56d31e --- /dev/null +++ b/src/x11util.cpp @@ -0,0 +1,62 @@ +#include "x11util.h" +extern "C" { + #include + #include +} +#include +#include +#include + +static int eHandler(Display* dpy, XErrorEvent* e) +{ + char buf[1000]; + XGetErrorText(dpy, e->error_code, buf, 1000); + fprintf(stderr, "X ERROR %d: %s\n", (int)e->error_code, buf); + return 0; +} + +extern "C" +void AddPixmapToBackground(unsigned const char* imgData, int width, int height, int depth, int bytesPerLine, int byteCount) +{ + Pixmap pix = 0; + GC gc = NULL; + XImage *xi = NULL; + XGCValues gc_init; + memset(&gc_init, 0, sizeof(gc_init)); + Display* dpy = XOpenDisplay(NULL); + if (dpy == NULL) + return; + XSetErrorHandler(&eHandler); + int screen = DefaultScreen(dpy); + Window root = RootWindow(dpy, screen); + char *data = (char*)malloc(byteCount); + memcpy(data, imgData, byteCount); + xi = XCreateImage(dpy, CopyFromParent, depth, ZPixmap, 0, data, width, height, 32, bytesPerLine); + if (xi == NULL) + goto cleanup; + pix = XCreatePixmap(dpy, root, width, height, (unsigned int)DefaultDepth(dpy, screen)); + if (pix == 0) + goto cleanup; + gc_init.foreground = BlackPixel(dpy, screen); + gc_init.background = WhitePixel(dpy, screen); + gc = XCreateGC(dpy, pix, GCForeground|GCBackground, &gc_init); + if (gc == NULL) + goto cleanup; + int res1, res2, res3; + res1 = XPutImage(dpy, pix, gc, xi, 0, 0, 0, 0, width, height); + res2 = XSetWindowBackgroundPixmap(dpy, root, pix); + res3 = XClearWindow(dpy, root); +cleanup: + if (gc != NULL) { + XFreeGC(dpy, gc); + } + if (pix != 0) { + XFreePixmap(dpy, pix); + } + if (xi != NULL) { + XDestroyImage(xi); + } + if (dpy != NULL) { + XCloseDisplay(dpy); + } +} diff --git a/src/x11util.h b/src/x11util.h new file mode 100644 index 0000000..81ac0c0 --- /dev/null +++ b/src/x11util.h @@ -0,0 +1,9 @@ +#ifndef X11UTIL_H_ +#define X11UTIL_H_ + +extern "C" { + #include + void AddPixmapToBackground(unsigned const char* imgData, int width, int height, int depth, int bytesPerLine, int byteCount); +} + +#endif /* X11UTIL_H_ */ -- cgit v1.2.3-55-g7522