Blob Blame History Raw
/*
    Copyright (c) 2017, Lukas Holecek <hluk@email.cz>

    This file is part of CopyQ.

    CopyQ is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    CopyQ is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with CopyQ.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "clipboardmonitor.h"

#include "common/log.h"
#include "common/mimetypes.h"
#include "common/monitormessagecode.h"
#include "item/serialize.h"
#include "platform/platformclipboard.h"
#include "platform/platformwindow.h"

#include <QApplication>

namespace {

bool hasSameData(const QVariantMap &data, const QVariantMap &lastData)
{
    for (const auto &format : lastData.keys()) {
        if ( !format.startsWith(COPYQ_MIME_PREFIX)
             && !data.contains(format) )
        {
            return false;
        }
    }

    for (const auto &format : data.keys()) {
        if ( !format.startsWith(COPYQ_MIME_PREFIX)
             && !data[format].toByteArray().isEmpty()
             && data[format] != lastData.value(format) )
        {
            return false;
        }
    }

    return true;
}

} // namespace

ClipboardMonitor::ClipboardMonitor(int &argc, char **argv, const QString &serverName, const QString &sessionName)
    : Client()
    , App("Monitor", createPlatformNativeInterface()->createMonitorApplication(argc, argv),
          sessionName)
    , m_clipboard(createPlatformNativeInterface()->clipboard())
{
    restoreSettings();

#ifdef HAS_TESTS
    if ( serverName == QString("copyq_TEST") )
        qApp->setProperty("CopyQ_testing", true);
#endif

    startClientSocket(serverName, argc, argv, 0, 0);
}

void ClipboardMonitor::onClipboardChanged(PlatformClipboard::Mode mode)
{
    QVariantMap data = m_clipboard->data(mode, m_formats);
    QVariantMap &lastData = m_lastData[mode];

    if ( hasSameData(data, lastData) ) {
        COPYQ_LOG( QString("Ignoring unchanged %1")
                   .arg(mode == PlatformClipboard::Clipboard ? "clipboard" : "selection") );
        return;
    }

    COPYQ_LOG( QString("%1 changed")
               .arg(mode == PlatformClipboard::Clipboard ? "Clipboard" : "Selection") );

    if (mode != PlatformClipboard::Clipboard) {
        const QString modeName = mode == PlatformClipboard::Selection
                ? "selection"
                : "find buffer";
        data.insert(mimeClipboardMode, modeName);
    }

    // add window title of clipboard owner
    if ( !data.contains(mimeOwner) && !data.contains(mimeWindowTitle) ) {
        PlatformPtr platform = createPlatformNativeInterface();
        PlatformWindowPtr currentWindow = platform->getCurrentWindow();
        if (currentWindow)
            data.insert( mimeWindowTitle, currentWindow->getTitle().toUtf8() );
    }

    sendMessage( serializeData(data), MonitorClipboardChanged );
    lastData = data;
}

void ClipboardMonitor::onMessageReceived(const QByteArray &message, int messageCode)
{
    if (messageCode == MonitorPing) {
        sendMessage( QByteArray(), MonitorPong );
    } else if (messageCode == MonitorSettings) {
        QVariantMap settings;
        QDataStream stream(message);
        stream >> settings;

        if ( hasLogLevel(LogDebug) ) {
            COPYQ_LOG("Loading configuration:");
            for (const auto &key : settings.keys()) {
                const QVariant val = settings[key];
                const QString str = val.canConvert<QStringList>() ? val.toStringList().join(",")
                                                                  : val.toString();
                COPYQ_LOG( QString(" %1=%2").arg(key).arg(str) );
            }
        }

        if ( settings.contains("formats") )
            m_formats = settings["formats"].toStringList();

        connect( m_clipboard.get(), SIGNAL(changed(PlatformClipboard::Mode)),
                 this, SLOT(onClipboardChanged(PlatformClipboard::Mode)),
                 Qt::UniqueConnection );

        m_clipboard->loadSettings(settings);

        COPYQ_LOG("Configured");
    } else if (messageCode == MonitorChangeClipboard
            || messageCode == MonitorChangeSelection)
    {
        COPYQ_LOG( QString("Received change %1 request (%2 KiB)")
                   .arg(messageCode == MonitorChangeClipboard ? "clipboard" : "selection")
                   .arg(message.size() / 1024.0) );

        QVariantMap data;
        deserializeData(&data, message);
        if (messageCode == MonitorChangeClipboard)
            m_clipboard->setData(PlatformClipboard::Clipboard, data);
        if (messageCode == MonitorChangeSelection)
            m_clipboard->setData(PlatformClipboard::Selection, data);
    } else {
        log( QString("Unknown message code %1!").arg(messageCode), LogError );
    }
}

void ClipboardMonitor::onDisconnected()
{
    exit(0);
}

void ClipboardMonitor::onConnectionFailed()
{
    exit(1);
}