Multi-monitor working for up/down directions to/from macOS.
Very rough implementation. #1112
This commit is contained in:
parent
653e4badeb
commit
810bf79456
|
@ -0,0 +1,12 @@
|
|||
#include <Carbon/Carbon.h>
|
||||
/**
|
||||
* Platform-independent display.
|
||||
*/
|
||||
class Display {
|
||||
public:
|
||||
UInt32 displayId;
|
||||
SInt32 x;
|
||||
SInt32 y;
|
||||
SInt32 width;
|
||||
SInt32 height;
|
||||
};
|
|
@ -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<Display*> getDisplays() const = 0;
|
||||
|
||||
//@}
|
||||
};
|
||||
|
|
|
@ -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"); }
|
||||
|
|
|
@ -562,4 +562,9 @@ Screen::leaveSecondary()
|
|||
m_screen->fakeAllKeysUp();
|
||||
}
|
||||
|
||||
std::vector<Display*>
|
||||
Screen::getDisplays() const {
|
||||
return m_screen->getDisplays();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<Display*> getDisplays() const;
|
||||
|
||||
IPlatformScreen* getPlatformScreen() { return m_screen; }
|
||||
|
||||
|
|
|
@ -234,6 +234,12 @@ Client::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const
|
|||
m_screen->getShape(x, y, w, h);
|
||||
}
|
||||
|
||||
std::vector<Display*>
|
||||
Client::getDisplays() const
|
||||
{
|
||||
// TODO(vjpr):
|
||||
}
|
||||
|
||||
void
|
||||
Client::getCursorPos(SInt32& x, SInt32& y) const
|
||||
{
|
||||
|
|
|
@ -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<Display*> getDisplays() const;
|
||||
|
||||
// IClient overrides
|
||||
virtual void enter(SInt32 xAbs, SInt32 yAbs,
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "base/EventTypes.h"
|
||||
#include "common/stdmap.h"
|
||||
#include "common/stdvector.h"
|
||||
#import "barrier/Display.h"
|
||||
|
||||
#include <bitset>
|
||||
#include <Carbon/Carbon.h>
|
||||
|
@ -53,6 +54,9 @@ class Mutex;
|
|||
//! Implementation of IPlatformScreen for OS X
|
||||
class OSXScreen : public PlatformScreen {
|
||||
public:
|
||||
// TODO(vjpr): make private.
|
||||
std::vector<Display*> 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<Display*> getDisplays() const;
|
||||
|
||||
// IPrimaryScreen overrides
|
||||
virtual void reconfigure(UInt32 activeSides);
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#include "base/Log.h"
|
||||
#include "base/IEventQueue.h"
|
||||
#include "base/TMethodEventJob.h"
|
||||
#import "barrier/Display.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <mach-o/dyld.h>
|
||||
|
@ -244,6 +245,11 @@ OSXScreen::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const
|
|||
h = m_h;
|
||||
}
|
||||
|
||||
std::vector<Display*>
|
||||
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());
|
||||
|
||||
|
|
|
@ -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<Display*> getDisplays() const = 0;
|
||||
virtual void getCursorPos(SInt32& x, SInt32& y) const = 0;
|
||||
|
||||
// IClient overrides
|
||||
|
|
|
@ -500,3 +500,12 @@ ClientProxy1_0::ClientClipboard::ClientClipboard() :
|
|||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
std::vector<Display*>
|
||||
ClientProxy1_0::getDisplays() const {
|
||||
|
||||
// TODO(vjpr):
|
||||
std::vector<Display*> vec;
|
||||
return vec;
|
||||
|
||||
}
|
||||
|
|
|
@ -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<Display*> getDisplays() const;
|
||||
|
||||
// IClient overrides
|
||||
virtual void enter(SInt32 xAbs, SInt32 yAbs,
|
||||
|
|
|
@ -99,3 +99,11 @@ ClientProxy1_6::recvClipboard()
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<Display*> getDisplays() {
|
||||
|
||||
// TODO(vjpr):
|
||||
std::vector<Display*> vec;
|
||||
return vec;
|
||||
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ public:
|
|||
|
||||
virtual void setClipboard(ClipboardID id, const IClipboard* clipboard);
|
||||
virtual bool recvClipboard();
|
||||
//virtual std::vector<Display*> getDisplays();
|
||||
|
||||
private:
|
||||
void handleClipboardSendingEvent(const Event&, void*);
|
||||
|
|
|
@ -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<Display*>
|
||||
PrimaryClient::getDisplays() const {
|
||||
|
||||
// TODO(vjpr): Only works for osx!
|
||||
IPlatformScreen* screen = m_screen->getPlatformScreen();
|
||||
OSXScreen* platformScreen = dynamic_cast<OSXScreen*>(screen);
|
||||
std::vector<Display*> displays = platformScreen->m_displays;
|
||||
return displays;
|
||||
|
||||
}
|
||||
|
|
|
@ -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<Display*> getDisplays() const;
|
||||
|
||||
// IClient overrides
|
||||
virtual void enter(SInt32 xAbs, SInt32 yAbs,
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
#include "base/IEventQueue.h"
|
||||
#include "base/Log.h"
|
||||
#include "base/TMethodEventJob.h"
|
||||
#include "platform/OSXScreen.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
|
@ -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<Display*> 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 <CoreGraphics/CGGeometry.h>
|
||||
#import <CoreGraphics/CGDirectDisplay.h>
|
||||
|
||||
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<OSXScreen*>(m_screen->getPlatformScreen());
|
||||
std::vector<Display*> 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)) {
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue