summaryrefslogblamecommitdiffstats
path: root/src/mainwindow.cpp
blob: 0ed85b8bf31d4d6a843f3fc4e19c0a4210425d85 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12











                                                             
                  

                        
                 
                     

                                      
                    
                   
                
 


                       


                       
                   
 

                  
                                                                                            
                    
                             
                             
                        
                     


                      


                                                        


                                                                                            
                                                   








                                                                         
                                                                               
     
 









                                                                     







                                                                          












                                                     




                           




                                                                                                        
                    

                                           
 


                                           
                                            
 

                                                                 
                                   
                                   

                                                                     
 

                                            
                                                    




                                                                                    
     
 
             


                                              



                           

                                                                                                                       



                                              

                                                                                                              

         
                                                



                                    
                        
                                           


                                                                                                   
                                          
                                                                                                   
                                                


                                               


                                                                                                                                                      


                                                                                        
                                        









                                             



                                                                     
     

                  








                                                                                                 



                                                  

 









                                                     
                          
                             

                                         
                                            
         

                            


     




                                                
                                        











                                  
                                       
 





                                                        
                                             
                                                     


                                                                                                                                  
                                                  
         
     
                  

 
                                                                               

                          
               
                                                 

                                      
               
     

                                                                                                      

                                                                
               
     
                                                                                                         
                        
                          
                            


                                                               

 

                                                
                                                  

                           


                                                                                                              
                                                      


                                                                                                             
     

                                            
                            
                           
                                                                                                                             
                        
                          
                       

 
                                  
 












                                                                                     
                                              
                                        
                     

















                                                                         
             



                                                
         



                                             
         

                                           


                                                                                                  
                                 


                
                                   
 





                                                  



                                                                                              
                             

 


                                           
                                                               


                          





                                                                                          


                                                
                           











                                   





                                            
                                  
                                                                                               



                                                       
                                          
                              


                  

                                
                                  





































                                                                                                         
                            
                                                                
                                                    
                               












                                                                                                                  
         
     

                                
                                                   
                                       
     
                                


                                                          
                                                

                                                
     
 
                     
                                


                                                          
                                                                                                                          

                                                                       


                                                                                                      
                                   




                                                        














                                                                   
                          
                                                                    

                                                    
/*
* Copyright (c) 2012-2015 Christian Surlykke, Petr Vanek
*
* This file is part of qt-lightdm-greeter 
* It is distributed under the LGPL 2.1 or later license.
* Please refer to the LICENSE file for a copy of the license.
*/
#include <QRect>
#include <QApplication>
#include <QDesktopWidget>
#include <QPalette>
#include <QString>
#include <QtDebug>
#include <QTextEdit>
#include <QStyleFactory>
#include <QLabel>
#include <QSvgWidget>
#include <QSvgRenderer>
#include <QAbstractTextDocumentLayout>
#include <QDateTime>
#include <QPainter>
#include <QFile>

#include <sys/types.h>
#include <sys/socket.h>

#include "mainwindow.h"
#include "loginform.h"
#include "settings.h"
#include "global.h"

#include "snake.h"

MainWindow::MainWindow(bool primary, int screen, const QRect &screenRect, QWidget *parent) :
    QWidget(parent),
    m_ScreenRect(screenRect),
	m_LoginForm(nullptr),
    m_messages(nullptr),
    m_Clock(nullptr),
    m_Snake(nullptr),
    m_Banner(nullptr),
    m_News(nullptr)
{
    setObjectName(QString("MainWindow_%1").arg(screen));
    
    // TODO: Check if testMode == false and greeter == NULL, if so display big error message
    // instead of exiting/crashing

    // display login dialog only in the main screen
    if (primary) {
        m_LoginForm = new LoginForm(this);
        // This hack ensures that the primary screen will have focus
        // if there are more screens (move the mouse cursor in the center
        // of primary screen - not in the center of all X area). It
        // won't affect single-screen environments.
        int centerX = screenRect.width()/2 + screenRect.x();
        int centerY = screenRect.height()/2 + screenRect.y();
        QCursor::setPos(centerX, centerY);
        connect(m_LoginForm, &LoginForm::resized, this, &MainWindow::reLayout);
    }

    // Banner
    if (!Settings::bannerImageFile().isEmpty()) {
        qWarning() << "Have banner " << Settings::bannerImageFile();
        m_Banner = new QSvgWidget(Settings::bannerImageFile(), this);
    }

    createLogWindow();
    createClock();
    createNewsWindow();

    if (QFile::exists(CONFIG_QSS_FILE)) {
        QFile qss(CONFIG_QSS_FILE);
        if (qss.open(QFile::ReadOnly)) {
            this->setStyleSheet(qss.readAll());
        } else {
            qDebug() << "Cannot open" << CONFIG_QSS_FILE << "for reading";
        }
    }
    setGeometry(screenRect);
}

void MainWindow::resizeEvent(QResizeEvent *event)
{
    if (m_Snake != nullptr) {
      delete m_Snake;
      m_Snake = nullptr;
    }

    QWidget::resizeEvent(event);
    m_ScreenRect = QRect(this->pos(), event->size());
    setBackground();
    reLayout();
}

void MainWindow::reLayout()
{
    /*
     * Everything is layed out manually here, since I don't know how to represent the size constraints
     * and interactions of everything using layout classes. You're welcome to improve this, but I double
     * dare you to not break any combination of having/not having certain logos or elements displayed.
     */
    int newsY = 100;
    int newsX = m_ScreenRect.width() / 2;
    int newsBottom = m_ScreenRect.height();

    int spaceY = m_ScreenRect.height() / 2;

    if (m_LoginForm != nullptr) {
        spaceY -= m_LoginForm->height() / 2;

        int maxX = m_ScreenRect.width() - m_LoginForm->width();
        int maxY = m_ScreenRect.height() - m_LoginForm->height();
        int defaultX = 50*maxX/100;
        int defaultY = 50*maxY/100;
        int offsetX = getOffset(Settings::offsetX(), maxX, defaultX);
        int offsetY = getOffset(Settings::offsetY(), maxY, defaultY);

        m_LoginForm->move(offsetX, offsetY);
        m_LoginForm->show();
        newsX = m_LoginForm->geometry().right() + 5;

        QRect layout(offsetX, offsetY, m_ScreenRect.width(), m_ScreenRect.height());
        if (layout == m_lastRelayout)
            return;
        m_lastRelayout = layout;
    }

    // Banner
    if (m_Banner != nullptr) {
        qWarning() << m_Banner->sizeHint();
        if (m_Banner->renderer()->isValid()) {
            QSize sh;
            int offs = 0;
            do {
                offs += 20;
                sh = m_Banner->sizeHint().scaled(m_ScreenRect.width() - offs, spaceY - offs - 50, Qt::KeepAspectRatio);
            } while (offs < 200 && sh.width() > m_ScreenRect.width() / 2 && sh.height() > spaceY / 2);
            int yoff = (spaceY - sh.height());
            if (yoff < 100) {
                yoff = 100;
            }
            m_Banner->setGeometry((m_ScreenRect.width() - sh.width()) / 2, yoff / 2, sh.width(), sh.height());
            newsY = m_Banner->geometry().bottom() + 10;
        }
    }
    // Distro and custom icons left/right bottom
    for (QWidget *w : m_DecoItems) {
        w->setParent(nullptr);
        w->deleteLater();
    }
    m_DecoItems.clear();
    int ls = (spaceY > 500 ? 500 : spaceY);
    if (ls > m_ScreenRect.height() / 5) ls = m_ScreenRect.height() / 5;
    if (ls > m_ScreenRect.width() / 5) ls = m_ScreenRect.width() / 5;
    QRect logoRect(QPoint(0, m_ScreenRect.height() / 3), QSize(ls, m_ScreenRect.height() * 2 / 3));
    QSize logoSize = createLogo(logoRect);
    QRect distroRect(QPoint(m_ScreenRect.width() - ls, m_ScreenRect.height() - ls), QSize(ls, ls));
    QSize distroSize = createDistro(distroRect);
    if (distroSize.height() > 0) {
        newsBottom -= distroSize.height() - 10;
    }
    // Log window
    if (m_messages != nullptr) {
        QRect lwSize(QPoint(logoSize.width(), m_ScreenRect.height() * 3/4), QPoint(m_ScreenRect.width() - distroSize.width(), m_ScreenRect.height()));
        if (m_LoginForm != nullptr && lwSize.top() < m_LoginForm->geometry().bottom()) {
            lwSize.setTop(m_LoginForm->geometry().bottom());
        }
        lwSize.adjust(10, 10, -10, -10);
        if (lwSize.height() < 10) {
            m_messages->hide();
        } else {
            m_messages->show();
            m_messages->setGeometry(lwSize);
            int ps = lwSize.height() / 20;
            if (ps > 20) ps = 20;
            m_messages->setFontPointSize(ps);
            newsBottom = lwSize.top();
        }
    }
    if (m_Clock != nullptr) {
        m_Clock->setFixedWidth(m_ScreenRect.width());
        m_Clock->parentWidget()->setFixedWidth(m_ScreenRect.width());
    }

    // News widget
    if (m_News != nullptr) {
        QRect newsSize(QPoint(newsX, newsY), QPoint(m_ScreenRect.width() - 10, newsBottom - 10));
        if (newsSize.width() < 200 || newsSize.height() < 80) {
            m_News->hide();
        } else {
            m_News->setGeometry(newsSize);
            m_News->show();
        }
    }
    if (m_LoginForm != nullptr) {
        // This is most important, so bring to top
        m_LoginForm->raise();
    }
}

void MainWindow::paintEvent(QPaintEvent *event)
{
    QWidget::paintEvent(event);
    if (m_Snake != nullptr) {
        m_Snake->paint(event);
    }
}

void MainWindow::mouseDoubleClickEvent(QMouseEvent *)
{
    static int clicks = 0;
    if (m_Snake == nullptr) {
        if (clicks++ > 0) {
            m_Snake = new GameCore(this);
            m_Clock->parentWidget()->hide();
        }
    } else {
        m_Snake->addSnake();
    }
}

void MainWindow::keyPressEvent(QKeyEvent *event)
{
  switch (event->key()) {
    case Qt::Key_Escape:
      if (m_Snake != nullptr) {
        m_Clock->parentWidget()->show();
        delete m_Snake;
        m_Snake = nullptr;
      }
      break;
    case Qt::Key_Pause:
      if (m_Snake != nullptr) {
        m_Snake->pauseAndResume();
      }
      break;
  }
}

QSize MainWindow::createLogo(QRect max)
{
    QSize retval(0, 0);
    QString path = Settings::bottomLeftLogoFile();
    if (!path.isEmpty()) { // Single explicit file first
        createNextLogo(max, retval, path);
    }
    path = Settings::bottomLeftLogoDir();
    // Check whether it's a file or directory
    if (!path.isEmpty() && QFileInfo(path).isDir()) {
        QFileInfoList fileInfoList = QDir(path).entryInfoList(QStringList() << "*.svg", QDir::Files, QDir::Name | QDir::Reversed);
        for (QFileInfo fileInfo : fileInfoList) {
            QString filePath = fileInfo.absoluteFilePath();
            createNextLogo(max, retval, filePath);
        }
    }
    return retval;
}

void MainWindow::createNextLogo(QRect &max, QSize &retval, const QString &path)
{
    if (max.height() <= 0)
        return;
    QSvgWidget *img = new QSvgWidget(path, this);
    if (!img->renderer()->isValid()) {
        img->deleteLater();
        return;
    }
    QSize virtualSize = img->sizeHint().scaled(max.width(), max.height(), Qt::KeepAspectRatio);
    QSize realSize = img->sizeHint().scaled(max.width() - 20, max.height() - 20, Qt::KeepAspectRatio);
    if (virtualSize.width() == 0 || virtualSize.height() == 0) {
        img->deleteLater();
        return;
    }
    QRect c(max.left() + 10, max.bottom() - realSize.height() - 10, realSize.width(), realSize.height());
    img->setGeometry(c);
    img->setVisible(true);
    m_DecoItems.append(img);
    max.setHeight(max.height() - virtualSize.height());
    retval.setWidth(qMax(retval.width(), virtualSize.width()));
    retval.setHeight(retval.height() + virtualSize.height());
}

QSize MainWindow::createDistro(const QRect &max)
{
    QImage img(QStringLiteral("/etc/distro.png"));
    if (img.isNull())
        return QSize(0, 0);
    QSize realSize = img.size();
    QSize virtualSize = realSize.scaled(realSize.width() + 20, realSize.height() + 20, Qt::IgnoreAspectRatio);
    if (virtualSize.width() > max.width() || virtualSize.height() > max.height()) {
        // This requires that the given rect is square
        img = img.scaled(max.width() - 20, max.height() - 20, Qt::KeepAspectRatio, Qt::SmoothTransformation);
        realSize = img.size();
        virtualSize = realSize.scaled(realSize.width() + 20, realSize.height() + 20, Qt::IgnoreAspectRatio);
    }
    QPixmap pixmap(QPixmap::fromImage(img));
    QLabel *lbl = new QLabel(this);
    m_DecoItems.append(lbl);
    lbl->setPixmap(pixmap);
    QRect c(max.right() - realSize.width() - 10, max.bottom() - realSize.height() - 10, realSize.width(), realSize.height());
    lbl->setGeometry(c);
    lbl->setVisible(true);
    return virtualSize;
}

bool MainWindow::createLogWindow()
{
    // HACK HACK: This doesn't really belong here at all :-/
    // But we can only check this when X is running, so....
    QProcess p;
    p.start(QLatin1String("/usr/bin/glxinfo"), QStringList() << QLatin1String("-B"));
    p.waitForFinished(1500);
    QString str = QString::fromLocal8Bit(p.readAllStandardOutput());
    if (str.contains("Device: llvmpipe") || str.contains("Accelerated: no")) {
        int i = str.indexOf("Device: ");
        int j = str.indexOf("\n", i);
        str = QLatin1String("SLOW Graphics! ") + str.mid(i, j - i);
    } else {
        str = "";
    }
    QString path = Settings::logMessageFile();
    if (path.isEmpty() && str.isEmpty())
        return false;
    if (!path.isEmpty()) {
        QFile f(path);
        if ((f.size() == 0 || !f.open(QFile::ReadOnly)) && str.isEmpty())
            return false;
        m_messages = new QTextEdit(this);
        QTextStream stream(&f);
        const QColor black(Qt::black);
        while (!stream.atEnd()) {
            bool ok = false;
            QString line(stream.readLine());
            int i = line.indexOf(' ');
            if (i > 0) {
                QString scol(line.left(i));
                uint col = scol.toUInt(&ok, 16);
                if (ok) {
                    m_messages->setTextColor(QColor(QRgb(col)));
                    line = line.mid(i + 1);
                }
            }
            if (!ok) {
                m_messages->setTextColor(black);
            }
            m_messages->append(line);
        }
    }
    if (!str.isEmpty()) {
        if (m_messages == nullptr) {
            m_messages = new QTextEdit(this);
        }
         m_messages->setTextColor(Qt::red);
        m_messages->append(str);
    }
    m_messages->setReadOnly(true);
    m_messages->setStyleSheet("border:none; background:rgba(255,255,255,.33); border-radius:5px");
    m_messages->setVisible(true);
    return true;
}

void MainWindow::createNewsWindow()
{
    QString path = Settings::newsHtmlFile();
    if (path.isEmpty())
        return;
    QFile f(path);
    if (f.size() == 0 || !f.open(QFile::ReadOnly))
        return;
    m_News = new QTextEdit(this);
    m_News->setReadOnly(true);
    m_News->setStyleSheet("border:none; background:rgba(255,255,255,.33); border-radius:5px");
    m_News->setHtml(QString::fromUtf8(f.readAll()));
    m_News->setVisible(true);
}

void MainWindow::createClock()
{
    QString style = Settings::clockStyle();
    QString backgroundStyle = Settings::clockBackgroundStyle();
    if (style == "none")
        return;
    if (style.isEmpty()) {
        style = "border:none; color:#fff; font-size:14pt; qproperty-alignment:AlignRight";
    }
    if (backgroundStyle.isEmpty()) {
        backgroundStyle = "#bg { border:none; background:#888687 }";
    } else {
        backgroundStyle = "#bg { " + backgroundStyle + " }";
    }
    QStringList slist = Settings::clockShadow();
    int x = 1, y = 1, blur = 1;
    QString color = "#555";
    if (slist.length() > 0) {
        x = slist.at(0).toInt();
    }
    if (slist.length() > 1) {
        y = slist.at(1).toInt();
    }
    if (slist.length() > 2) {
        color = slist.at(2);
    }
    if (slist.length() > 3) {
        blur = slist.at(3).toInt();
    }
    QWidget *bg = new QWidget(this);
    bg->setObjectName("bg");
    bg->setFixedWidth(this->width());
    bg->setMaximumHeight(500);
    bg->setStyleSheet(backgroundStyle);
    m_Clock = new QLabel("Today 00:00", bg);
    m_Clock->setStyleSheet(style);
    QGraphicsDropShadowEffect *pLabelTextShadowEffect = new QGraphicsDropShadowEffect(m_Clock);
    pLabelTextShadowEffect->setColor(QColor(color));
    pLabelTextShadowEffect->setBlurRadius(blur);
    pLabelTextShadowEffect->setOffset(x, y);
    m_Clock->setGraphicsEffect(pLabelTextShadowEffect);
    m_Clock->setFixedWidth(this->width());
    m_Clock->setVisible(true);
    updateClock();
}

bool MainWindow::showLoginForm()
{
    return m_LoginForm != nullptr;
}

void MainWindow::setFocus(Qt::FocusReason reason)
{
    if (m_LoginForm) {
        m_LoginForm->setFocus(reason);
    }
    else  {
        QWidget::setFocus(reason);
    }
}

int MainWindow::getOffset(QString settingsOffset, int maxVal, int defaultVal)
{

    int offset = defaultVal > maxVal ? maxVal : defaultVal;

    if (! settingsOffset.isEmpty())  {
        if (QRegExp("^\\d+px$", Qt::CaseInsensitive).exactMatch(settingsOffset))  {
            offset = settingsOffset.left(settingsOffset.size() - 2).toInt();
            if (offset > maxVal) offset = maxVal;
        }
        else if (QRegExp("^\\d+%$", Qt::CaseInsensitive).exactMatch(settingsOffset)) {
            int offsetPct = settingsOffset.left(settingsOffset.size() -1).toInt();
            if (offsetPct > 100) offsetPct = 100;
            offset = (maxVal * offsetPct)/100;
        }
        else {
            qWarning() << "Could not understand" << settingsOffset
                       << "- must be of form <positivenumber>px or <positivenumber>%, e.g. 35px or 25%" ;
        }
    }

    return offset;
}

void MainWindow::setBackground()
{
    m_background = QImage();
    Qt::AspectRatioMode arMode = Qt::KeepAspectRatioByExpanding;
    QString bgPath = Settings::backgrundImageFile();
    if (bgPath.length() != 0) {
        QSvgRenderer svg(bgPath);
        if (svg.isValid()) {
            m_background = QImage(m_ScreenRect.size(), QImage::Format_RGB32);
            m_background.fill(QColor(225,225,220));
            QSize size = svg.defaultSize().scaled(m_background.size(), Qt::KeepAspectRatioByExpanding);
            QPainter p(&m_background);
            QPoint origin((m_background.width() - size.width()) / 2, (m_background.height() - size.height()) / 2);
            svg.render(&p, QRect(origin, size));
        } else {
            m_background = QImage(bgPath);
            if (m_background.isNull()) {
                qWarning() << "Not able to read" << bgPath << "as image";
            }
        }
    }

    if (m_background.isNull()) {
        m_background = Global::getConfigGradient();
        arMode = Qt::IgnoreAspectRatio;
    }
    if (m_background.isNull()) {
        // Hard-coded default: Gradient
        m_background = QImage(2, 2, QImage::Format_RGB32);
        m_background.setPixel(0, 0, 0xffffffff);
        m_background.setPixel(1, 0, 0xffffffff);
        m_background.setPixel(0, 1, 0xff888687);
        m_background.setPixel(1, 1, 0xfff9a72b);
    }

    QPalette palette;
    if (m_background.isNull()) {
        palette.setColor(QPalette::Background, Qt::black);
    }
    else {
        m_background = m_background.scaled(m_ScreenRect.width(), m_ScreenRect.height(), arMode, Qt::SmoothTransformation);
        int xoff = (m_background.width() -  m_ScreenRect.width()) / 2;
        int yoff = (m_background.height() - m_ScreenRect.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);
}

void MainWindow::updateClock()
{
    int next = drawClock();
    if (next > 0) {
        QTimer::singleShot(next, this, SLOT(updateClock()));
    }
}

int MainWindow::drawClock()
{
    if (m_Clock == nullptr)
        return 0;
    QDateTime time = QDateTime::currentDateTime();
    QLocale loc;
    m_Clock->setText(time.toString(loc.dateFormat() + "  HH:mm "));
    m_Clock->adjustSize();
	reinterpret_cast<QWidget*>(m_Clock->parent())->adjustSize();
    return (60 - time.time().second()) * 1000 + 100;
}