/* * Copyright (C) 2013-2015 Canonical, Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License version 3, as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "ubuntukeyboardinfo.h" #include namespace qtmir { namespace { const int gConnectionAttemptIntervalMs = 5000; const int gMaxConsecutiveAttempts = 10; const char gServerName[] = "ubuntu-keyboard-info"; } UbuntuKeyboardInfo* UbuntuKeyboardInfo::m_instance = nullptr; UbuntuKeyboardInfo* UbuntuKeyboardInfo::instance() { return m_instance; } UbuntuKeyboardInfo::UbuntuKeyboardInfo(QObject *parent) : QObject(parent), m_consecutiveAttempts(0), m_lastWidth(0), m_lastHeight(0) { if (m_instance) { qFatal("Cannot have more than one instance of UbuntuKeyboardInfo simultaneously."); } m_instance = this; connect(&m_socket, &QLocalSocket::stateChanged, this, &UbuntuKeyboardInfo::onSocketStateChanged); connect(&m_socket, &QIODevice::readyRead, this, &UbuntuKeyboardInfo::readAllBytesFromSocket); buildSocketFilePath(); typedef void (QLocalSocket::*MemberFunctionType)(QLocalSocket::LocalSocketError); MemberFunctionType funcPointer = &QLocalSocket::error; connect(&m_socket, funcPointer, this, &UbuntuKeyboardInfo::onSocketError); m_connectionRetryTimer.setInterval(gConnectionAttemptIntervalMs); m_connectionRetryTimer.setSingleShot(true); connect(&m_connectionRetryTimer, &QTimer::timeout, this, &UbuntuKeyboardInfo::tryConnectingToServer); tryConnectingToServer(); } UbuntuKeyboardInfo::~UbuntuKeyboardInfo() { // Make sure we don't get onSocketStateChanged() called during // destruction. m_socket.disconnect(this); Q_ASSERT(m_instance); m_instance = nullptr; } void UbuntuKeyboardInfo::tryConnectingToServer() { ++m_consecutiveAttempts; Q_ASSERT(!m_socketFilePath.isEmpty()); m_socket.connectToServer(m_socketFilePath, QIODevice::ReadOnly); } void UbuntuKeyboardInfo::onSocketStateChanged(QLocalSocket::LocalSocketState socketState) { switch (socketState) { case QLocalSocket::UnconnectedState: retryConnection(); break; case QLocalSocket::ConnectedState: m_consecutiveAttempts = 0; break; default: break; } } void UbuntuKeyboardInfo::onSocketError(QLocalSocket::LocalSocketError socketError) { Q_UNUSED(socketError); qWarning() << "UbuntuKeyboardInfo - socket error:" << m_socket.errorString(); } void UbuntuKeyboardInfo::retryConnection() { // Polling every gConnectionAttemptIntervalMs. Not the best approach but could be worse. if (m_consecutiveAttempts < gMaxConsecutiveAttempts) { if (!m_connectionRetryTimer.isActive()) { m_connectionRetryTimer.start(); } } else { qCritical() << "Failed to connect to" << m_socketFilePath << "after" << m_consecutiveAttempts << "failed attempts"; // it shouldn't be running, but just to be sure. m_connectionRetryTimer.stop(); } } void UbuntuKeyboardInfo::readAllBytesFromSocket() { while (m_socket.bytesAvailable() > 0) { readInfoFromSocket(); } } void UbuntuKeyboardInfo::readInfoFromSocket() { const size_t sharedInfoSize = sizeof(struct SharedInfo); QByteArray bytes = m_socket.read(sharedInfoSize); if (bytes.size() != sharedInfoSize) { qWarning() << "UbuntuKeyboardInfo: expected to receive" << sharedInfoSize << "but got" << bytes.size(); return; } { struct SharedInfo *sharedInfo = reinterpret_cast(bytes.data()); if (m_lastX != sharedInfo->keyboardX) { m_lastX = sharedInfo->keyboardX; Q_EMIT xChanged(m_lastX); } if (m_lastY != sharedInfo->keyboardY) { m_lastY = sharedInfo->keyboardY; Q_EMIT yChanged(m_lastY); } if (m_lastWidth != sharedInfo->keyboardWidth) { m_lastWidth = sharedInfo->keyboardWidth; Q_EMIT widthChanged(m_lastWidth); } if (m_lastHeight != sharedInfo->keyboardHeight) { m_lastHeight = sharedInfo->keyboardHeight; Q_EMIT heightChanged(m_lastHeight); } } } void UbuntuKeyboardInfo::buildSocketFilePath() { char *xdgRuntimeDir = getenv("XDG_RUNTIME_DIR"); if (xdgRuntimeDir) { m_socketFilePath = QDir(xdgRuntimeDir).filePath(gServerName); } else { m_socketFilePath = QDir("/tmp").filePath(gServerName); } } } // namespace qtmir