From 810bf794566c626e8f89e1bd0b867844fb5ffec4 Mon Sep 17 00:00:00 2001 From: Vaughan Rouesnel Date: Thu, 31 Mar 2022 16:02:31 +0200 Subject: [PATCH] Multi-monitor working for up/down directions to/from macOS. Very rough implementation. #1112 --- src/lib/barrier/Display.h | 12 +++ src/lib/barrier/IScreen.h | 3 + src/lib/barrier/PlatformScreen.h | 6 +- src/lib/barrier/Screen.cpp | 5 + src/lib/barrier/Screen.h | 1 + src/lib/client/Client.cpp | 6 ++ src/lib/client/Client.h | 1 + src/lib/platform/OSXScreen.h | 5 + src/lib/platform/OSXScreen.mm | 23 +++++ src/lib/server/BaseClientProxy.h | 1 + src/lib/server/ClientProxy1_0.cpp | 9 ++ src/lib/server/ClientProxy1_0.h | 1 + src/lib/server/ClientProxy1_6.cpp | 8 ++ src/lib/server/ClientProxy1_6.h | 1 + src/lib/server/PrimaryClient.cpp | 12 +++ src/lib/server/PrimaryClient.h | 1 + src/lib/server/Server.cpp | 163 +++++++++++++++++++++++++----- src/lib/server/Server.h | 2 +- 18 files changed, 231 insertions(+), 29 deletions(-) create mode 100644 src/lib/barrier/Display.h diff --git a/src/lib/barrier/Display.h b/src/lib/barrier/Display.h new file mode 100644 index 00000000..995c54f9 --- /dev/null +++ b/src/lib/barrier/Display.h @@ -0,0 +1,12 @@ +#include +/** + * Platform-independent display. + */ +class Display { + public: + UInt32 displayId; + SInt32 x; + SInt32 y; + SInt32 width; + SInt32 height; +}; diff --git a/src/lib/barrier/IScreen.h b/src/lib/barrier/IScreen.h index a1e7c47f..4185d39c 100644 --- a/src/lib/barrier/IScreen.h +++ b/src/lib/barrier/IScreen.h @@ -22,6 +22,7 @@ #include "base/Event.h" #include "base/EventTypes.h" #include "common/IInterface.h" +#include "Display.h" class IClipboard; @@ -67,5 +68,7 @@ public: */ virtual void getCursorPos(SInt32& x, SInt32& y) const = 0; + virtual std::vector getDisplays() const = 0; + //@} }; diff --git a/src/lib/barrier/PlatformScreen.h b/src/lib/barrier/PlatformScreen.h index 19a3da10..60439715 100644 --- a/src/lib/barrier/PlatformScreen.h +++ b/src/lib/barrier/PlatformScreen.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package 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 @@ -97,7 +97,7 @@ public: virtual void setOptions(const OptionsList& options) = 0; virtual void setSequenceNumber(UInt32) = 0; virtual bool isPrimary() const = 0; - + virtual void fakeDraggingFiles(DragFileList fileList) { throw std::runtime_error("fakeDraggingFiles not implemented"); } virtual const String& getDropTarget() const { throw std::runtime_error("getDropTarget not implemented"); } diff --git a/src/lib/barrier/Screen.cpp b/src/lib/barrier/Screen.cpp index 2a2c8776..ab4a9405 100644 --- a/src/lib/barrier/Screen.cpp +++ b/src/lib/barrier/Screen.cpp @@ -562,4 +562,9 @@ Screen::leaveSecondary() m_screen->fakeAllKeysUp(); } +std::vector +Screen::getDisplays() const { + return m_screen->getDisplays(); +} + } diff --git a/src/lib/barrier/Screen.h b/src/lib/barrier/Screen.h index 1c8e7dec..b1976da6 100644 --- a/src/lib/barrier/Screen.h +++ b/src/lib/barrier/Screen.h @@ -301,6 +301,7 @@ public: virtual void getShape(SInt32& x, SInt32& y, SInt32& width, SInt32& height) const; virtual void getCursorPos(SInt32& x, SInt32& y) const; + virtual std::vector getDisplays() const; IPlatformScreen* getPlatformScreen() { return m_screen; } diff --git a/src/lib/client/Client.cpp b/src/lib/client/Client.cpp index a7b15cf5..48552beb 100644 --- a/src/lib/client/Client.cpp +++ b/src/lib/client/Client.cpp @@ -234,6 +234,12 @@ Client::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const m_screen->getShape(x, y, w, h); } +std::vector +Client::getDisplays() const +{ + // TODO(vjpr): +} + void Client::getCursorPos(SInt32& x, SInt32& y) const { diff --git a/src/lib/client/Client.h b/src/lib/client/Client.h index c172af22..0bae527c 100644 --- a/src/lib/client/Client.h +++ b/src/lib/client/Client.h @@ -139,6 +139,7 @@ public: virtual void getShape(SInt32& x, SInt32& y, SInt32& width, SInt32& height) const; virtual void getCursorPos(SInt32& x, SInt32& y) const; + virtual std::vector getDisplays() const; // IClient overrides virtual void enter(SInt32 xAbs, SInt32 yAbs, diff --git a/src/lib/platform/OSXScreen.h b/src/lib/platform/OSXScreen.h index 691b74c4..6f7c14d4 100644 --- a/src/lib/platform/OSXScreen.h +++ b/src/lib/platform/OSXScreen.h @@ -24,6 +24,7 @@ #include "base/EventTypes.h" #include "common/stdmap.h" #include "common/stdvector.h" +#import "barrier/Display.h" #include #include @@ -53,6 +54,9 @@ class Mutex; //! Implementation of IPlatformScreen for OS X class OSXScreen : public PlatformScreen { public: + // TODO(vjpr): make private. + std::vector m_displays; + OSXScreen(IEventQueue* events, bool isPrimary, bool autoShowHideCursor=true); virtual ~OSXScreen(); @@ -64,6 +68,7 @@ public: virtual void getShape(SInt32& x, SInt32& y, SInt32& width, SInt32& height) const; virtual void getCursorPos(SInt32& x, SInt32& y) const; + virtual std::vector getDisplays() const; // IPrimaryScreen overrides virtual void reconfigure(UInt32 activeSides); diff --git a/src/lib/platform/OSXScreen.mm b/src/lib/platform/OSXScreen.mm index d41e321b..1bb4b171 100644 --- a/src/lib/platform/OSXScreen.mm +++ b/src/lib/platform/OSXScreen.mm @@ -38,6 +38,7 @@ #include "base/Log.h" #include "base/IEventQueue.h" #include "base/TMethodEventJob.h" +#import "barrier/Display.h" #include #include @@ -244,6 +245,11 @@ OSXScreen::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const h = m_h; } +std::vector +OSXScreen::getDisplays() const { + return m_displays; +} + void OSXScreen::getCursorPos(SInt32& x, SInt32& y) const { @@ -270,6 +276,7 @@ OSXScreen::warpCursor(SInt32 x, SInt32 y) CGPoint pos; pos.x = x; pos.y = y; + // NOTE: If y is invalid, it sets it to 0. CGWarpMouseCursorPosition(pos); // save new cursor position @@ -1546,6 +1553,21 @@ OSXScreen::updateScreenShape() return; } + // save displays as generic displays. + for (CGDisplayCount i = 0; i < displayCount; ++i) { + CGRect bounds = CGDisplayBounds(displays[i]); + Display* display = new Display{ + // TODO(vjpr): Display numbers are simply the enumeration order. This is not stable between reboots I don't think. + i, + // -- + (SInt32)bounds.origin.x, + (SInt32)bounds.origin.y, + (SInt32)bounds.size.width, + (SInt32)bounds.size.height}; + m_displays.push_back(display); + } + + // get smallest rect enclosing all display rects CGRect totalBounds = CGRectZero; for (CGDisplayCount i = 0; i < displayCount; ++i) { @@ -1566,6 +1588,7 @@ OSXScreen::updateScreenShape() m_yCenter = (rect.origin.y + rect.size.height) / 2; delete[] displays; + // We want to notify the peer screen whether we are primary screen or not sendEvent(m_events->forIScreen().shapeChanged()); diff --git a/src/lib/server/BaseClientProxy.h b/src/lib/server/BaseClientProxy.h index 5d5dc06e..192b07c7 100644 --- a/src/lib/server/BaseClientProxy.h +++ b/src/lib/server/BaseClientProxy.h @@ -63,6 +63,7 @@ public: virtual bool getClipboard(ClipboardID id, IClipboard*) const = 0; virtual void getShape(SInt32& x, SInt32& y, SInt32& width, SInt32& height) const = 0; + virtual std::vector getDisplays() const = 0; virtual void getCursorPos(SInt32& x, SInt32& y) const = 0; // IClient overrides diff --git a/src/lib/server/ClientProxy1_0.cpp b/src/lib/server/ClientProxy1_0.cpp index 33b0f159..52b854be 100644 --- a/src/lib/server/ClientProxy1_0.cpp +++ b/src/lib/server/ClientProxy1_0.cpp @@ -500,3 +500,12 @@ ClientProxy1_0::ClientClipboard::ClientClipboard() : { // do nothing } + +std::vector +ClientProxy1_0::getDisplays() const { + + // TODO(vjpr): + std::vector vec; + return vec; + +} diff --git a/src/lib/server/ClientProxy1_0.h b/src/lib/server/ClientProxy1_0.h index 45d75413..5e5a9ef3 100644 --- a/src/lib/server/ClientProxy1_0.h +++ b/src/lib/server/ClientProxy1_0.h @@ -37,6 +37,7 @@ public: virtual void getShape(SInt32& x, SInt32& y, SInt32& width, SInt32& height) const; virtual void getCursorPos(SInt32& x, SInt32& y) const; + virtual std::vector getDisplays() const; // IClient overrides virtual void enter(SInt32 xAbs, SInt32 yAbs, diff --git a/src/lib/server/ClientProxy1_6.cpp b/src/lib/server/ClientProxy1_6.cpp index 29f3ce4d..7a16a241 100644 --- a/src/lib/server/ClientProxy1_6.cpp +++ b/src/lib/server/ClientProxy1_6.cpp @@ -99,3 +99,11 @@ ClientProxy1_6::recvClipboard() return true; } + +std::vector getDisplays() { + + // TODO(vjpr): + std::vector vec; + return vec; + +} diff --git a/src/lib/server/ClientProxy1_6.h b/src/lib/server/ClientProxy1_6.h index a4c2e5d8..0433d20f 100644 --- a/src/lib/server/ClientProxy1_6.h +++ b/src/lib/server/ClientProxy1_6.h @@ -31,6 +31,7 @@ public: virtual void setClipboard(ClipboardID id, const IClipboard* clipboard); virtual bool recvClipboard(); + //virtual std::vector getDisplays(); private: void handleClipboardSendingEvent(const Event&, void*); diff --git a/src/lib/server/PrimaryClient.cpp b/src/lib/server/PrimaryClient.cpp index 6583e5d2..a87cbf37 100644 --- a/src/lib/server/PrimaryClient.cpp +++ b/src/lib/server/PrimaryClient.cpp @@ -21,6 +21,7 @@ #include "barrier/Screen.h" #include "barrier/Clipboard.h" #include "base/Log.h" +#include "platform/OSXScreen.h" // // PrimaryClient @@ -272,3 +273,14 @@ PrimaryClient::setOptions(const OptionsList& options) { m_screen->setOptions(options); } + +std::vector +PrimaryClient::getDisplays() const { + + // TODO(vjpr): Only works for osx! + IPlatformScreen* screen = m_screen->getPlatformScreen(); + OSXScreen* platformScreen = dynamic_cast(screen); + std::vector displays = platformScreen->m_displays; + return displays; + +} diff --git a/src/lib/server/PrimaryClient.h b/src/lib/server/PrimaryClient.h index 13be8389..27a64c45 100644 --- a/src/lib/server/PrimaryClient.h +++ b/src/lib/server/PrimaryClient.h @@ -122,6 +122,7 @@ public: virtual void getShape(SInt32& x, SInt32& y, SInt32& width, SInt32& height) const; virtual void getCursorPos(SInt32& x, SInt32& y) const; + virtual std::vector getDisplays() const; // IClient overrides virtual void enter(SInt32 xAbs, SInt32 yAbs, diff --git a/src/lib/server/Server.cpp b/src/lib/server/Server.cpp index a169db16..513baf61 100644 --- a/src/lib/server/Server.cpp +++ b/src/lib/server/Server.cpp @@ -42,6 +42,7 @@ #include "base/IEventQueue.h" #include "base/Log.h" #include "base/TMethodEventJob.h" +#include "platform/OSXScreen.h" #include #include @@ -456,7 +457,8 @@ Server::switchScreen(BaseClientProxy* dst, { SInt32 dx, dy, dw, dh; dst->getShape(dx, dy, dw, dh); - assert(x >= dx && y >= dy && x < dx + dw && y < dy + dh); + // TODO(vjpr): This doesn't make sense anymore when we use current display bounds. + //assert(x >= dx && y >= dy && x < dx + dw && y < dy + dh); } #endif assert(m_active != NULL); @@ -640,9 +642,38 @@ Server::getNeighbor(BaseClientProxy* src, } } +// TODO(vjpr): For now its just bottom-most. +// For a given direction, find the display furthest in that direction. +Display* findDisplay(BaseClientProxy* dst, SInt32& x, SInt32& y) { + + Display* found = NULL; + + std::vector displays = dst->getDisplays(); + + LOG((CLOG_DEBUG1 "findDisplay - mouse pos %d,%d", x, y)); + + // Get display that mouse is on. + for (Display* display : displays) { + //LOG((CLOG_DEBUG1 "check x %d <= %d <= %d true=%d", display->x, x, display->x + display->width, display->x <= x <= (display->x + display->width))); + if (display->x <= x && x <= (display->x + display->width)) { + //LOG((CLOG_DEBUG1 "check y %d <= %d <= %d", display->y, y, display->y + display->width)); + if (found) { + if (display->y < found->y) { + found = display; + } + } else { + found = display; + } + } + } + + return found; + +} + BaseClientProxy* Server::mapToNeighbor(BaseClientProxy* src, - EDirection srcSide, SInt32& x, SInt32& y) const + EDirection srcSide, SInt32& x, SInt32& y, Display* currentDisplay) const { // note -- must be locked on entry @@ -698,8 +729,18 @@ Server::mapToNeighbor(BaseClientProxy* src, break; case kTop: - y -= dy; - while (dst != NULL) { + // vjpr + // Initially: + // y = mouse cursor position relative to primary display top-left corner. + // dy = highest display's top-left corner relative to primary display top-left corner. That is, (0,0) in the "canonical screen space". + //y -= dy; + // vjpr: We need to get y relative to our screen instead. + if (currentDisplay) { + y = y - currentDisplay->y; + } else { + y -= dy; + } + while (dst != NULL) { lastGoodScreen = dst; lastGoodScreen->getShape(dx, dy, dw, dh); y += dh; @@ -713,21 +754,36 @@ Server::mapToNeighbor(BaseClientProxy* src, y += dy; break; - case kBottom: - y -= dy; - while (dst != NULL) { - y -= dh; - lastGoodScreen = dst; - lastGoodScreen->getShape(dx, dy, dw, dh); - if (y < dh) { - break; - } - LOG((CLOG_DEBUG2 "skipping over screen %s", getName(dst).c_str())); - dst = getNeighbor(lastGoodScreen, srcSide, x, y); - } - assert(lastGoodScreen != NULL); - y += dy; - break; + case kBottom: { + y -= dy; + while (dst != NULL) { + // vjpr + // y = pixels we need to position cursor in dest screen. + y -= dh; + lastGoodScreen = dst; + lastGoodScreen->getShape(dx, dy, dw, dh); + if (y < dh) { + break; + } + LOG((CLOG_DEBUG2 "skipping over screen %s", getName(dst).c_str())); + dst = getNeighbor(lastGoodScreen, srcSide, x, y); + } + assert(lastGoodScreen != NULL); + // vjpr: `dy` is the top-left of all merged display bounds. + // Instead, we want to look at `x`, find the top-most dest display, then offset `y` from this display's origin. + + // y += dy; + + // TODO(vjpr): Have to add ability to read displays from dest. + auto display = findDisplay(dst, x, y); + if (!display) { + // TODO(vjpr): Shouldn't happen. + y += dy; + } + y = y + display->y; + + break; + } case kNoDirection: assert(0 && "bad direction"); @@ -1733,6 +1789,9 @@ Server::onMouseUp(ButtonID id) } } +#import +#import + bool Server::onMouseMovePrimary(SInt32 x, SInt32 y) { @@ -1776,6 +1835,43 @@ Server::onMouseMovePrimary(SInt32 x, SInt32 y) yc = ay + ah - 1; } + // -------------------- + // TODO(vjpr): + + // TODO(vjpr): This CG function call is too slow! Adds lag. + //uint32_t displayCount = 0; + //uint32_t displayID; + //CGGetDisplaysWithPoint(CGPointMake(x, y), 1, &displayID, &displayCount); + //LOG((CLOG_DEBUG4 "onMouseMovePrimary displayID %d", displayID)); + // --- + + Display* currentDisplay = NULL; + + // TODO(vjpr): Assumes OSX is always primary screen. Maybe this will break things. + OSXScreen* screen = dynamic_cast(m_screen->getPlatformScreen()); + std::vector displays = screen->m_displays; + + //LOG((CLOG_DEBUG1 "mouse pos %d,%d", x, y)); + + // Get display that mouse is on. + for (Display* display : displays) { + //LOG((CLOG_DEBUG1 "check x %d <= %d <= %d true=%d", display->x, x, display->x + display->width, display->x <= x <= (display->x + display->width))); + if (display->x <= x && x <= (display->x + display->width)) { + //LOG((CLOG_DEBUG1 "check y %d <= %d <= %d", display->y, y, display->y + display->width)); + if (display->y <= y && y <= (display->y + display->height)) { + //LOG((CLOG_DEBUG1 "match x=%d y=%d w=%d h=%d", display->x, display->y, display->width, display->height)); + currentDisplay = display; + } + } + } + + //LOG((CLOG_DEBUG1 "currentDisplay x=%d,y=%d,w=%d,h=%d", currentDisplay->x, currentDisplay->y, currentDisplay->width, currentDisplay->height)); + + ax = currentDisplay->x; + ay = currentDisplay->y; + + // -------------------- + // see if we should change screens // when the cursor is in a corner, there may be a screen either // horizontally or vertically. check both directions. @@ -1783,19 +1879,36 @@ Server::onMouseMovePrimary(SInt32 x, SInt32 y) SInt32 xh = x, yv = y; if (x < ax + zoneSize) { xh -= zoneSize; - dirh = kLeft; + //dirh = kLeft; } else if (x >= ax + aw - zoneSize) { xh += zoneSize; - dirh = kRight; + //dirh = kRight; } if (y < ay + zoneSize) { yv -= zoneSize; - dirv = kTop; + + // Is another display above? + for (Display* display : displays) { + if (display->displayId == currentDisplay->displayId) continue; // Skip current monitor. + SInt32 x0 = display->x; + SInt32 y0 = display->y + display->height; + SInt32 x1 = display->x + display->width; + SInt32 y1 = display->y; + if (y0 - 1 == display->y) { + // This display is directly above. Don't switch screen. + break; + } + if (x0 < x && x > x1) { + // The cursor is within the x bounds. + dirv = kTop; + } + } + } else if (y >= ay + ah - zoneSize) { yv += zoneSize; - dirv = kBottom; + //dirv = kBottom; } if (dirh == kNoDirection && dirv == kNoDirection) { // still on local screen @@ -1814,7 +1927,7 @@ Server::onMouseMovePrimary(SInt32 x, SInt32 y) x = xs[i], y = ys[i]; // get jump destination - BaseClientProxy* newScreen = mapToNeighbor(m_active, dir, x, y); + BaseClientProxy* newScreen = mapToNeighbor(m_active, dir, x, y, currentDisplay); // should we switch or not? if (isSwitchOkay(newScreen, dir, x, y, xc, yc)) { @@ -2005,7 +2118,7 @@ Server::onMouseMoveSecondary(SInt32 dx, SInt32 dy) } // try to switch screen. get the neighbor. - newScreen = mapToNeighbor(m_active, dir, m_x, m_y); + newScreen = mapToNeighbor(m_active, dir, m_x, m_y, NULL); // see if we should switch if (!isSwitchOkay(newScreen, dir, m_x, m_y, xc, yc)) { diff --git a/src/lib/server/Server.h b/src/lib/server/Server.h index ae8b2bd5..da3b83e6 100644 --- a/src/lib/server/Server.h +++ b/src/lib/server/Server.h @@ -232,7 +232,7 @@ private: // cross multiple screens. if there is no suitable screen then // return NULL and x,y are not modified. BaseClientProxy* mapToNeighbor(BaseClientProxy*, EDirection, - SInt32& x, SInt32& y) const; + SInt32& x, SInt32& y, Display*) const; // adjusts x and y or neither to avoid ending up in a jump zone // after entering the client in the given direction.