/* * Deskflow -- mouse and keyboard sharing utility * SPDX-FileCopyrightText: (C) 2012 - 2016 Symless Ltd. * SPDX-FileCopyrightText: (C) 2003 Chris Schoeneman * SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception */ #include "deskflow/Screen.h" #include "base/IEventQueue.h" #include "base/Log.h" #include "deskflow/IPlatformScreen.h" #include "deskflow/ProtocolTypes.h" #include "server/ClientProxy.h" namespace deskflow { // // Screen // Screen::Screen(IPlatformScreen *platformScreen, IEventQueue *events) : m_screen(platformScreen), m_isPrimary(platformScreen->isPrimary()), m_entered(m_isPrimary), m_events(events) { assert(m_screen != nullptr); // reset options resetOptions(); LOG_DEBUG("opened display"); } Screen::~Screen() { if (m_enabled) { disable(); } assert(!m_enabled); // Originally there was an assert here added before 2009 (history not in // tact). This condition seems to occur on a Windows client when the process // is shut down to make way for a new elevated process (e.g. at login screen). // The reason why this assert was originally added is unclear, and was causing // pain when using debug builds; you lose control of the client when it's at // the login screen. Therefore it has been converted to a warning so that we // can still see when it happens but it won't cause the process to pause. This // also gives us the added benefit of seeing when it happens in production. // Perhaps it indicates that the cursor is still being controlled on the // client while it's shutting down? i.e. the screen is entered and is not the // server, or the screen is not entered and is the server. if (m_entered == m_isPrimary) { LOG( (CLOG_DEBUG "current screen: entered=%s, primary=%s", // m_entered ? "yes" : "no", m_isPrimary ? "yes" : "no") ); if (m_isPrimary) { LOG_WARN("current primary screen is not entered on shutdown"); } else { LOG_WARN("current secondary screen is entered on shutdown"); } } delete m_screen; LOG_DEBUG("closed display"); } void Screen::enable() { assert(!m_enabled); m_screen->updateKeyMap(); m_screen->updateKeyState(); m_screen->enable(); if (m_isPrimary) { enablePrimary(); } else { enableSecondary(); } // note activation m_enabled = true; } void Screen::disable() { assert(m_enabled); if (!m_isPrimary && m_entered) { leave(); } else if (m_isPrimary && !m_entered) { enter(0); } m_screen->disable(); if (m_isPrimary) { disablePrimary(); } else { disableSecondary(); } // note deactivation m_enabled = false; } void Screen::enter(KeyModifierMask toggleMask) { LOG_INFO("entering screen"); if (m_entered) { LOG_WARN("screen already entered"); } // now on screen m_entered = true; m_screen->enter(); if (m_isPrimary) { enterPrimary(); } else { enterSecondary(toggleMask); } } bool Screen::leave() { LOG_INFO("leaving screen"); if (!m_entered) { LOG_WARN("screen already left"); } if (!m_screen->canLeave()) { return false; } if (m_isPrimary) { leavePrimary(); } else { leaveSecondary(); } m_screen->leave(); // make sure our idea of clipboard ownership is correct m_screen->checkClipboards(); // now not on screen m_entered = false; return true; } void Screen::reconfigure(uint32_t activeSides) { assert(m_isPrimary); m_screen->reconfigure(activeSides); } void Screen::warpCursor(int32_t x, int32_t y) { assert(m_isPrimary); m_screen->warpCursor(x, y); } void Screen::setClipboard(ClipboardID id, const IClipboard *clipboard) { m_screen->setClipboard(id, clipboard); } void Screen::grabClipboard(ClipboardID id) { m_screen->setClipboard(id, nullptr); } void Screen::screensaver(bool) const { // do nothing } void Screen::keyDown(KeyID id, KeyModifierMask mask, KeyButton button, const std::string &lang) { // check for ctrl+alt+del emulation if (id == kKeyDelete && (mask & (KeyModifierControl | KeyModifierAlt)) == (KeyModifierControl | KeyModifierAlt)) { LOG_DEBUG("emulating ctrl+alt+del press"); if (m_screen->fakeCtrlAltDel()) { return; } } m_screen->fakeKeyDown(id, mask, button, lang); } void Screen::keyRepeat(KeyID id, KeyModifierMask mask, int32_t count, KeyButton button, const std::string &lang) { assert(!m_isPrimary); m_screen->fakeKeyRepeat(id, mask, count, button, lang); } void Screen::keyUp(KeyID, KeyModifierMask, KeyButton button) { m_screen->fakeKeyUp(button); } void Screen::mouseDown(ButtonID button) { m_screen->fakeMouseButton(button, true); } void Screen::mouseUp(ButtonID button) { m_screen->fakeMouseButton(button, false); } void Screen::mouseMove(int32_t x, int32_t y) { assert(!m_isPrimary); m_screen->fakeMouseMove(x, y); } void Screen::mouseRelativeMove(int32_t dx, int32_t dy) const { assert(!m_isPrimary); m_screen->fakeMouseRelativeMove(dx, dy); } void Screen::mouseWheel(int32_t xDelta, int32_t yDelta) const { assert(!m_isPrimary); m_screen->fakeMouseWheel(xDelta, yDelta); } void Screen::resetOptions() { // reset options m_halfDuplex = 0; // let screen handle its own options m_screen->resetOptions(); } void Screen::setOptions(const OptionsList &options) { // update options for (uint32_t i = 0, n = (uint32_t)options.size(); i < n; i += 2) { if (options[i] == kOptionHalfDuplexCapsLock) { if (options[i + 1] != 0) { m_halfDuplex |= KeyModifierCapsLock; } else { m_halfDuplex &= ~KeyModifierCapsLock; } LOG_DEBUG1("half-duplex caps-lock %s", ((m_halfDuplex & KeyModifierCapsLock) != 0) ? "on" : "off"); } else if (options[i] == kOptionHalfDuplexNumLock) { if (options[i + 1] != 0) { m_halfDuplex |= KeyModifierNumLock; } else { m_halfDuplex &= ~KeyModifierNumLock; } LOG_DEBUG1("half-duplex num-lock %s", ((m_halfDuplex & KeyModifierNumLock) != 0) ? "on" : "off"); } else if (options[i] == kOptionHalfDuplexScrollLock) { if (options[i + 1] != 0) { m_halfDuplex |= KeyModifierScrollLock; } else { m_halfDuplex &= ~KeyModifierScrollLock; } LOG_DEBUG1("half-duplex scroll-lock %s", ((m_halfDuplex & KeyModifierScrollLock) != 0) ? "on" : "off"); } } // update half-duplex options m_screen->setHalfDuplexMask(m_halfDuplex); // let screen handle its own options m_screen->setOptions(options); } void Screen::setSequenceNumber(uint32_t seqNum) { m_screen->setSequenceNumber(seqNum); } uint32_t Screen::registerHotKey(KeyID key, KeyModifierMask mask) { return m_screen->registerHotKey(key, mask); } void Screen::unregisterHotKey(uint32_t id) { m_screen->unregisterHotKey(id); } void Screen::fakeInputBegin() { assert(!m_fakeInput); m_fakeInput = true; m_screen->fakeInputBegin(); } void Screen::fakeInputEnd() { assert(m_fakeInput); m_fakeInput = false; m_screen->fakeInputEnd(); } bool Screen::isOnScreen() const { return m_entered; } bool Screen::isLockedToScreen() const { if (uint32_t buttonID = 0; m_screen->isAnyMouseButtonDown(buttonID)) { LOG_DEBUG("locked by mouse buttonID: %d", buttonID); return true; } // not locked return false; } int32_t Screen::getJumpZoneSize() const { if (!m_isPrimary) { return 0; } else { return m_screen->getJumpZoneSize(); } } void Screen::getCursorCenter(int32_t &x, int32_t &y) const { m_screen->getCursorCenter(x, y); } KeyModifierMask Screen::getActiveModifiers() const { return m_screen->getActiveModifiers(); } KeyModifierMask Screen::pollActiveModifiers() const { return m_screen->pollActiveModifiers(); } void *Screen::getEventTarget() const { return m_screen; } bool Screen::getClipboard(ClipboardID id, IClipboard *clipboard) const { return m_screen->getClipboard(id, clipboard); } void Screen::getShape(int32_t &x, int32_t &y, int32_t &w, int32_t &h) const { m_screen->getShape(x, y, w, h); } void Screen::getCursorPos(int32_t &x, int32_t &y) const { m_screen->getCursorPos(x, y); } void Screen::enablePrimary() { // get notified of screen saver activation/deactivation m_screen->openScreensaver(true); // claim screen changed size m_events->addEvent(Event(EventTypes::ScreenShapeChanged, getEventTarget())); } void Screen::enableSecondary() { // assume primary has all clipboards for (ClipboardID id = 0; id < kClipboardEnd; ++id) { grabClipboard(id); } } void Screen::disablePrimary() { // done with screen saver m_screen->closeScreensaver(); } void Screen::disableSecondary() { // done with screen saver m_screen->closeScreensaver(); } void Screen::enterPrimary() const { // do nothing } void Screen::enterSecondary(KeyModifierMask) const { // do nothing } void Screen::leavePrimary() { // we don't track keys while on the primary screen so update our // idea of them now. this is particularly to update the state of // the toggle modifiers. m_screen->updateKeyState(); } void Screen::leaveSecondary() { // release any keys we think are still down m_screen->fakeAllKeysUp(); } std::string Screen::getSecureInputApp() const { return m_screen->getSecureInputApp(); } } // namespace deskflow