Added support for a user option to require hitting the edge of a
screen twice within a specified amount of time in order to switch screens. This can help prevent unintended switching.
This commit is contained in:
parent
7bbd33d787
commit
f411df65fb
|
@ -79,7 +79,7 @@ CConfig::renameScreen(const CString& oldName,
|
||||||
|
|
||||||
// update connections
|
// update connections
|
||||||
for (index = m_map.begin(); index != m_map.end(); ++index) {
|
for (index = m_map.begin(); index != m_map.end(); ++index) {
|
||||||
for (UInt32 i = 0; i <= kLastDirection - kFirstDirection; ++i) {
|
for (UInt32 i = 0; i < kNumDirections; ++i) {
|
||||||
if (CStringUtil::CaselessCmp::equal(getCanonicalName(
|
if (CStringUtil::CaselessCmp::equal(getCanonicalName(
|
||||||
index->second.m_neighbor[i]), oldCanonical)) {
|
index->second.m_neighbor[i]), oldCanonical)) {
|
||||||
index->second.m_neighbor[i] = newName;
|
index->second.m_neighbor[i] = newName;
|
||||||
|
@ -117,7 +117,7 @@ CConfig::removeScreen(const CString& name)
|
||||||
// disconnect
|
// disconnect
|
||||||
for (index = m_map.begin(); index != m_map.end(); ++index) {
|
for (index = m_map.begin(); index != m_map.end(); ++index) {
|
||||||
CCell& cell = index->second;
|
CCell& cell = index->second;
|
||||||
for (UInt32 i = 0; i <= kLastDirection - kFirstDirection; ++i) {
|
for (UInt32 i = 0; i < kNumDirections; ++i) {
|
||||||
if (getCanonicalName(cell.m_neighbor[i]) == canonical) {
|
if (getCanonicalName(cell.m_neighbor[i]) == canonical) {
|
||||||
cell.m_neighbor[i].erase();
|
cell.m_neighbor[i].erase();
|
||||||
}
|
}
|
||||||
|
@ -200,6 +200,8 @@ bool
|
||||||
CConfig::connect(const CString& srcName,
|
CConfig::connect(const CString& srcName,
|
||||||
EDirection srcSide, const CString& dstName)
|
EDirection srcSide, const CString& dstName)
|
||||||
{
|
{
|
||||||
|
assert(srcSide >= kFirstDirection && srcSide <= kLastDirection);
|
||||||
|
|
||||||
// find source cell
|
// find source cell
|
||||||
CCellMap::iterator index = m_map.find(getCanonicalName(srcName));
|
CCellMap::iterator index = m_map.find(getCanonicalName(srcName));
|
||||||
if (index == m_map.end()) {
|
if (index == m_map.end()) {
|
||||||
|
@ -217,6 +219,8 @@ CConfig::connect(const CString& srcName,
|
||||||
bool
|
bool
|
||||||
CConfig::disconnect(const CString& srcName, EDirection srcSide)
|
CConfig::disconnect(const CString& srcName, EDirection srcSide)
|
||||||
{
|
{
|
||||||
|
assert(srcSide >= kFirstDirection && srcSide <= kLastDirection);
|
||||||
|
|
||||||
// find source cell
|
// find source cell
|
||||||
CCellMap::iterator index = m_map.find(srcName);
|
CCellMap::iterator index = m_map.find(srcName);
|
||||||
if (index == m_map.end()) {
|
if (index == m_map.end()) {
|
||||||
|
@ -406,6 +410,8 @@ CConfig::getCanonicalName(const CString& name) const
|
||||||
CString
|
CString
|
||||||
CConfig::getNeighbor(const CString& srcName, EDirection srcSide) const
|
CConfig::getNeighbor(const CString& srcName, EDirection srcSide) const
|
||||||
{
|
{
|
||||||
|
assert(srcSide >= kFirstDirection && srcSide <= kLastDirection);
|
||||||
|
|
||||||
// find source cell
|
// find source cell
|
||||||
CCellMap::const_iterator index = m_map.find(getCanonicalName(srcName));
|
CCellMap::const_iterator index = m_map.find(getCanonicalName(srcName));
|
||||||
if (index == m_map.end()) {
|
if (index == m_map.end()) {
|
||||||
|
@ -485,7 +491,7 @@ CConfig::operator==(const CConfig& x) const
|
||||||
}
|
}
|
||||||
|
|
||||||
// compare neighbors
|
// compare neighbors
|
||||||
for (UInt32 i = 0; i <= kLastDirection - kFirstDirection; ++i) {
|
for (UInt32 i = 0; i < kNumDirections; ++i) {
|
||||||
if (!CStringUtil::CaselessCmp::equal(index1->second.m_neighbor[i],
|
if (!CStringUtil::CaselessCmp::equal(index1->second.m_neighbor[i],
|
||||||
index2->second.m_neighbor[i])) {
|
index2->second.m_neighbor[i])) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -516,6 +522,9 @@ const char*
|
||||||
CConfig::dirName(EDirection dir)
|
CConfig::dirName(EDirection dir)
|
||||||
{
|
{
|
||||||
static const char* s_name[] = { "left", "right", "top", "bottom" };
|
static const char* s_name[] = { "left", "right", "top", "bottom" };
|
||||||
|
|
||||||
|
assert(dir >= kFirstDirection && dir <= kLastDirection);
|
||||||
|
|
||||||
return s_name[dir - kFirstDirection];
|
return s_name[dir - kFirstDirection];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -627,6 +636,9 @@ CConfig::getOptionName(OptionID id)
|
||||||
if (id == kOptionScreenSwitchDelay) {
|
if (id == kOptionScreenSwitchDelay) {
|
||||||
return "switchDelay";
|
return "switchDelay";
|
||||||
}
|
}
|
||||||
|
if (id == kOptionScreenSwitchTwoTap) {
|
||||||
|
return "switchDoubleTap";
|
||||||
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -663,7 +675,8 @@ CConfig::getOptionValue(OptionID id, OptionValue value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (id == kOptionHeartbeat ||
|
if (id == kOptionHeartbeat ||
|
||||||
id == kOptionScreenSwitchDelay) {
|
id == kOptionScreenSwitchDelay ||
|
||||||
|
id == kOptionScreenSwitchTwoTap) {
|
||||||
return CStringUtil::print("%d", value);
|
return CStringUtil::print("%d", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -774,6 +787,9 @@ CConfig::readSectionOptions(std::istream& s)
|
||||||
else if (name == "switchDelay") {
|
else if (name == "switchDelay") {
|
||||||
addOption("", kOptionScreenSwitchDelay, parseInt(value));
|
addOption("", kOptionScreenSwitchDelay, parseInt(value));
|
||||||
}
|
}
|
||||||
|
else if (name == "switchDoubleTap") {
|
||||||
|
addOption("", kOptionScreenSwitchTwoTap, parseInt(value));
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
throw XConfigRead("unknown argument");
|
throw XConfigRead("unknown argument");
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,8 +60,13 @@ CServer::CServer(const CString& serverName) :
|
||||||
m_activeSaver(NULL),
|
m_activeSaver(NULL),
|
||||||
m_httpServer(NULL),
|
m_httpServer(NULL),
|
||||||
m_httpAvailable(&m_mutex, s_httpMaxSimultaneousRequests),
|
m_httpAvailable(&m_mutex, s_httpMaxSimultaneousRequests),
|
||||||
|
m_switchDir(kNoDirection),
|
||||||
|
m_switchScreen(NULL),
|
||||||
m_switchWaitDelay(0.0),
|
m_switchWaitDelay(0.0),
|
||||||
m_switchWaitScreen(NULL)
|
m_switchWaitEngaged(false),
|
||||||
|
m_switchTwoTapDelay(0.0),
|
||||||
|
m_switchTwoTapEngaged(false),
|
||||||
|
m_switchTwoTapArmed(false)
|
||||||
{
|
{
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
|
@ -231,7 +236,14 @@ CServer::setConfig(const CConfig& config)
|
||||||
if (m_switchWaitDelay < 0.0) {
|
if (m_switchWaitDelay < 0.0) {
|
||||||
m_switchWaitDelay = 0.0;
|
m_switchWaitDelay = 0.0;
|
||||||
}
|
}
|
||||||
clearSwitchWait();
|
m_switchWaitEngaged = false;
|
||||||
|
}
|
||||||
|
else if (id == kOptionScreenSwitchTwoTap) {
|
||||||
|
m_switchTwoTapDelay = 1.0e-3 * static_cast<double>(value);
|
||||||
|
if (m_switchTwoTapDelay < 0.0) {
|
||||||
|
m_switchTwoTapDelay = 0.0;
|
||||||
|
}
|
||||||
|
m_switchTwoTapEngaged = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -508,29 +520,20 @@ CServer::onOneShotTimerExpired(UInt32 id)
|
||||||
{
|
{
|
||||||
CLock lock(&m_mutex);
|
CLock lock(&m_mutex);
|
||||||
|
|
||||||
// ignore old timer or if there's no jump screen anymore
|
// ignore if it's an old timer or if switch wait isn't engaged anymore
|
||||||
if (m_switchWaitScreen == NULL || id != m_switchWaitTimer) {
|
if (!m_switchWaitEngaged || id != m_switchWaitTimer) {
|
||||||
clearSwitchWait();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ignore if mouse is locked to screen
|
// ignore if mouse is locked to screen
|
||||||
if (isLockedToScreenNoLock()) {
|
if (isLockedToScreenNoLock()) {
|
||||||
clearSwitchWait();
|
LOG((CLOG_DEBUG1 "locked to screen"));
|
||||||
|
clearSwitchState();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// switch screen
|
// switch screen
|
||||||
switchScreen(m_switchWaitScreen, m_switchWaitX, m_switchWaitY, false);
|
switchScreen(m_switchScreen, m_switchWaitX, m_switchWaitY, false);
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
CServer::clearSwitchWait()
|
|
||||||
{
|
|
||||||
if (m_switchWaitScreen != NULL) {
|
|
||||||
LOG((CLOG_DEBUG1 "cancel switch wait"));
|
|
||||||
m_switchWaitScreen = NULL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -629,61 +632,38 @@ CServer::onMouseMovePrimaryNoLock(SInt32 x, SInt32 y)
|
||||||
if (x < ax + zoneSize) {
|
if (x < ax + zoneSize) {
|
||||||
x -= zoneSize;
|
x -= zoneSize;
|
||||||
dir = kLeft;
|
dir = kLeft;
|
||||||
LOG((CLOG_DEBUG1 "switch to left"));
|
|
||||||
}
|
}
|
||||||
else if (x >= ax + aw - zoneSize) {
|
else if (x >= ax + aw - zoneSize) {
|
||||||
x += zoneSize;
|
x += zoneSize;
|
||||||
dir = kRight;
|
dir = kRight;
|
||||||
LOG((CLOG_DEBUG1 "switch to right"));
|
|
||||||
}
|
}
|
||||||
else if (y < ay + zoneSize) {
|
else if (y < ay + zoneSize) {
|
||||||
y -= zoneSize;
|
y -= zoneSize;
|
||||||
dir = kTop;
|
dir = kTop;
|
||||||
LOG((CLOG_DEBUG1 "switch to top"));
|
|
||||||
}
|
}
|
||||||
else if (y >= ay + ah - zoneSize) {
|
else if (y >= ay + ah - zoneSize) {
|
||||||
y += zoneSize;
|
y += zoneSize;
|
||||||
dir = kBottom;
|
dir = kBottom;
|
||||||
LOG((CLOG_DEBUG1 "switch to bottom"));
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// still on local screen
|
// still on local screen
|
||||||
clearSwitchWait();
|
onNoSwitch();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get jump destination and, if no screen in jump direction,
|
// get jump destination
|
||||||
// then ignore the move.
|
|
||||||
IClient* newScreen = getNeighbor(m_active, dir, x, y);
|
IClient* newScreen = getNeighbor(m_active, dir, x, y);
|
||||||
if (newScreen == NULL) {
|
|
||||||
clearSwitchWait();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if waiting before a switch then prepare to switch later
|
|
||||||
if (m_switchWaitDelay > 0.0) {
|
|
||||||
if (m_switchWaitScreen == NULL || dir != m_switchWaitDir) {
|
|
||||||
m_switchWaitDir = dir;
|
|
||||||
m_switchWaitScreen = newScreen;
|
|
||||||
m_switchWaitX = x;
|
|
||||||
m_switchWaitY = y;
|
|
||||||
m_switchWaitTimer = m_primaryClient->addOneShotTimer(
|
|
||||||
m_switchWaitDelay);
|
|
||||||
LOG((CLOG_DEBUG1 "waiting to switch"));
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ignore if mouse is locked to screen
|
|
||||||
if (isLockedToScreenNoLock()) {
|
|
||||||
LOG((CLOG_DEBUG1 "locked to screen"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// should we switch or not?
|
||||||
|
if (isSwitchOkay(newScreen, dir, x, y)) {
|
||||||
// switch screen
|
// switch screen
|
||||||
switchScreen(newScreen, x, y, false);
|
switchScreen(newScreen, x, y, false);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy)
|
CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy)
|
||||||
|
@ -721,9 +701,10 @@ CServer::onMouseMoveSecondaryNoLock(SInt32 dx, SInt32 dy)
|
||||||
SInt32 ax, ay, aw, ah;
|
SInt32 ax, ay, aw, ah;
|
||||||
m_active->getShape(ax, ay, aw, ah);
|
m_active->getShape(ax, ay, aw, ah);
|
||||||
|
|
||||||
// find direction of neighbor
|
// find direction of neighbor and get the neighbor
|
||||||
|
IClient* newScreen;
|
||||||
|
do {
|
||||||
EDirection dir;
|
EDirection dir;
|
||||||
IClient* newScreen = NULL;
|
|
||||||
if (m_x < ax) {
|
if (m_x < ax) {
|
||||||
dir = kLeft;
|
dir = kLeft;
|
||||||
}
|
}
|
||||||
|
@ -737,53 +718,16 @@ CServer::onMouseMoveSecondaryNoLock(SInt32 dx, SInt32 dy)
|
||||||
dir = kBottom;
|
dir = kBottom;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
// we haven't left the screen
|
||||||
newScreen = m_active;
|
newScreen = m_active;
|
||||||
|
|
||||||
// keep compiler quiet about unset variable
|
// if waiting and mouse is not on the border we're waiting
|
||||||
dir = kLeft;
|
// on then stop waiting. also if it's not on the border
|
||||||
}
|
// then arm the double tap.
|
||||||
|
if (m_switchScreen != NULL) {
|
||||||
// switch screens if the mouse is outside the screen and not
|
|
||||||
// locked to the screen
|
|
||||||
bool clamp = false;
|
|
||||||
if (newScreen == NULL) {
|
|
||||||
// get neighbor we should switch to
|
|
||||||
newScreen = getNeighbor(m_active, dir, m_x, m_y);
|
|
||||||
LOG((CLOG_DEBUG1 "leave \"%s\" on %s", m_active->getName().c_str(), CConfig::dirName(dir)));
|
|
||||||
if (newScreen == NULL) {
|
|
||||||
LOG((CLOG_DEBUG1 "no neighbor %s", CConfig::dirName(dir)));
|
|
||||||
clamp = true;
|
|
||||||
}
|
|
||||||
else if (m_switchWaitDelay > 0.0) {
|
|
||||||
// wait to switch; prepare to switch later
|
|
||||||
if (m_switchWaitScreen == NULL || dir != m_switchWaitDir) {
|
|
||||||
m_switchWaitDir = dir;
|
|
||||||
m_switchWaitScreen = newScreen;
|
|
||||||
m_switchWaitX = m_x;
|
|
||||||
m_switchWaitY = m_y;
|
|
||||||
m_switchWaitTimer = m_primaryClient->addOneShotTimer(
|
|
||||||
m_switchWaitDelay);
|
|
||||||
LOG((CLOG_DEBUG1 "waiting to switch"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// don't try to switch screen now
|
|
||||||
m_x = xOld + dx;
|
|
||||||
m_y = yOld + dy;
|
|
||||||
clamp = true;
|
|
||||||
}
|
|
||||||
else if (isLockedToScreenNoLock()) {
|
|
||||||
// clamp to edge when locked to screen
|
|
||||||
LOG((CLOG_DEBUG1 "locked to screen"));
|
|
||||||
clamp = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// on same screen. if waiting and mouse is not on the border
|
|
||||||
// we're waiting on then stop waiting.
|
|
||||||
if (m_switchWaitScreen != NULL) {
|
|
||||||
bool clearWait;
|
bool clearWait;
|
||||||
SInt32 zoneSize = m_primaryClient->getJumpZoneSize();
|
SInt32 zoneSize = m_primaryClient->getJumpZoneSize();
|
||||||
switch (m_switchWaitDir) {
|
switch (m_switchDir) {
|
||||||
case kLeft:
|
case kLeft:
|
||||||
clearWait = (m_x >= ax + zoneSize);
|
clearWait = (m_x >= ax + zoneSize);
|
||||||
break;
|
break;
|
||||||
|
@ -801,13 +745,31 @@ CServer::onMouseMoveSecondaryNoLock(SInt32 dx, SInt32 dy)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (clearWait) {
|
if (clearWait) {
|
||||||
clearSwitchWait();
|
onNoSwitch();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// clamp mouse to edge
|
// skip rest of block
|
||||||
if (clamp) {
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to switch screen. get the neighbor.
|
||||||
|
newScreen = getNeighbor(m_active, dir, m_x, m_y);
|
||||||
|
|
||||||
|
// see if we should switch
|
||||||
|
if (!isSwitchOkay(newScreen, dir, m_x, m_y)) {
|
||||||
|
newScreen = m_active;
|
||||||
|
}
|
||||||
|
} while (false);
|
||||||
|
|
||||||
|
if (newScreen != m_active) {
|
||||||
|
// switch screens
|
||||||
|
switchScreen(newScreen, m_x, m_y, false);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// same screen. clamp mouse to edge.
|
||||||
|
m_x = xOld + dx;
|
||||||
|
m_y = yOld + dy;
|
||||||
if (m_x < ax) {
|
if (m_x < ax) {
|
||||||
m_x = ax;
|
m_x = ax;
|
||||||
LOG((CLOG_DEBUG2 "clamp to left of \"%s\"", m_active->getName().c_str()));
|
LOG((CLOG_DEBUG2 "clamp to left of \"%s\"", m_active->getName().c_str()));
|
||||||
|
@ -824,22 +786,13 @@ CServer::onMouseMoveSecondaryNoLock(SInt32 dx, SInt32 dy)
|
||||||
m_y = ay + ah - 1;
|
m_y = ay + ah - 1;
|
||||||
LOG((CLOG_DEBUG2 "clamp to bottom of \"%s\"", m_active->getName().c_str()));
|
LOG((CLOG_DEBUG2 "clamp to bottom of \"%s\"", m_active->getName().c_str()));
|
||||||
}
|
}
|
||||||
newScreen = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// warp cursor if on same screen
|
// warp cursor if it moved.
|
||||||
if (newScreen == NULL || newScreen == m_active) {
|
|
||||||
// do nothing if mouse didn't move
|
|
||||||
if (m_x != xOld || m_y != yOld) {
|
if (m_x != xOld || m_y != yOld) {
|
||||||
LOG((CLOG_DEBUG2 "move on %s to %d,%d", m_active->getName().c_str(), m_x, m_y));
|
LOG((CLOG_DEBUG2 "move on %s to %d,%d", m_active->getName().c_str(), m_x, m_y));
|
||||||
m_active->mouseMove(m_x, m_y);
|
m_active->mouseMove(m_x, m_y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// otherwise screen screens
|
|
||||||
else {
|
|
||||||
switchScreen(newScreen, m_x, m_y, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -894,7 +847,7 @@ CServer::switchScreen(IClient* dst, SInt32 x, SInt32 y, bool forScreensaver)
|
||||||
LOG((CLOG_INFO "switch from \"%s\" to \"%s\" at %d,%d", m_active->getName().c_str(), dst->getName().c_str(), x, y));
|
LOG((CLOG_INFO "switch from \"%s\" to \"%s\" at %d,%d", m_active->getName().c_str(), dst->getName().c_str(), x, y));
|
||||||
|
|
||||||
// stop waiting to switch
|
// stop waiting to switch
|
||||||
clearSwitchWait();
|
clearSwitchState();
|
||||||
|
|
||||||
// record new position
|
// record new position
|
||||||
m_x = x;
|
m_x = x;
|
||||||
|
@ -1166,6 +1119,120 @@ CServer::getNeighbor(IClient* src,
|
||||||
return dst;
|
return dst;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
CServer::isSwitchOkay(IClient* newScreen, EDirection dir, SInt32 x, SInt32 y)
|
||||||
|
{
|
||||||
|
LOG((CLOG_DEBUG1 "try to leave \"%s\" on %s", m_active->getName().c_str(), CConfig::dirName(dir)));
|
||||||
|
|
||||||
|
// is there a neighbor?
|
||||||
|
if (newScreen == NULL) {
|
||||||
|
// there's no neighbor. we don't want to switch and we don't
|
||||||
|
// want to try to switch later.
|
||||||
|
LOG((CLOG_DEBUG1 "no neighbor %s", CConfig::dirName(dir)));
|
||||||
|
clearSwitchState();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// should we switch or not?
|
||||||
|
bool preventSwitch = false;
|
||||||
|
bool allowSwitch = false;
|
||||||
|
|
||||||
|
// note if the switch direction has changed. save the new
|
||||||
|
// direction and screen if so.
|
||||||
|
bool isNewDirection = (dir != m_switchDir);
|
||||||
|
if (isNewDirection || m_switchScreen == NULL) {
|
||||||
|
m_switchDir = dir;
|
||||||
|
m_switchScreen = newScreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
// is this a double tap and do we care?
|
||||||
|
if (!allowSwitch && m_switchTwoTapDelay > 0.0) {
|
||||||
|
if (isNewDirection || !m_switchTwoTapEngaged) {
|
||||||
|
// tapping a different or new edge. prepare for second tap.
|
||||||
|
preventSwitch = true;
|
||||||
|
m_switchTwoTapEngaged = true;
|
||||||
|
m_switchTwoTapArmed = false;
|
||||||
|
m_switchTwoTapTimer.reset();
|
||||||
|
LOG((CLOG_DEBUG1 "waiting for second tap"));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// second tap if we were armed. if soon enough then switch.
|
||||||
|
if (m_switchTwoTapArmed &&
|
||||||
|
m_switchTwoTapTimer.getTime() <= m_switchTwoTapDelay) {
|
||||||
|
allowSwitch = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// not fast enough. reset the clock.
|
||||||
|
preventSwitch = true;
|
||||||
|
m_switchTwoTapEngaged = true;
|
||||||
|
m_switchTwoTapArmed = false;
|
||||||
|
m_switchTwoTapTimer.reset();
|
||||||
|
LOG((CLOG_DEBUG1 "waiting for second tap"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if waiting before a switch then prepare to switch later
|
||||||
|
if (!allowSwitch && m_switchWaitDelay > 0.0) {
|
||||||
|
if (isNewDirection) {
|
||||||
|
m_switchWaitEngaged = true;
|
||||||
|
m_switchWaitX = x;
|
||||||
|
m_switchWaitY = y;
|
||||||
|
m_switchWaitTimer = m_primaryClient->addOneShotTimer(
|
||||||
|
m_switchWaitDelay);
|
||||||
|
LOG((CLOG_DEBUG1 "waiting to switch"));
|
||||||
|
}
|
||||||
|
preventSwitch = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ignore if mouse is locked to screen
|
||||||
|
if (!preventSwitch && isLockedToScreenNoLock()) {
|
||||||
|
LOG((CLOG_DEBUG1 "locked to screen"));
|
||||||
|
preventSwitch = true;
|
||||||
|
|
||||||
|
// don't try to switch later. it's possible that we might
|
||||||
|
// not be locked to the screen when the wait delay expires
|
||||||
|
// and could switch then but we'll base the decision on
|
||||||
|
// when the user first attempts the switch. this also
|
||||||
|
// ensures that all switch tests are using the same
|
||||||
|
clearSwitchState();
|
||||||
|
}
|
||||||
|
|
||||||
|
return !preventSwitch;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CServer::onNoSwitch()
|
||||||
|
{
|
||||||
|
if (m_switchTwoTapEngaged) {
|
||||||
|
if (m_switchTwoTapTimer.getTime() > m_switchTwoTapDelay) {
|
||||||
|
// second tap took too long. disengage.
|
||||||
|
m_switchTwoTapEngaged = false;
|
||||||
|
m_switchTwoTapArmed = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// we've moved away from the edge and there's still
|
||||||
|
// time to get back for a double tap.
|
||||||
|
m_switchTwoTapArmed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// once the mouse moves away from the edge we no longer want to
|
||||||
|
// switch after a delay.
|
||||||
|
m_switchWaitEngaged = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CServer::clearSwitchState()
|
||||||
|
{
|
||||||
|
if (m_switchScreen != NULL) {
|
||||||
|
m_switchDir = kNoDirection;
|
||||||
|
m_switchScreen = NULL;
|
||||||
|
m_switchWaitEngaged = false;
|
||||||
|
m_switchTwoTapEngaged = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CServer::closeClients(const CConfig& config)
|
CServer::closeClients(const CConfig& config)
|
||||||
{
|
{
|
||||||
|
@ -1196,8 +1263,8 @@ CServer::closeClients(const CConfig& config)
|
||||||
index2->second->close();
|
index2->second->close();
|
||||||
|
|
||||||
// don't switch to it if we planned to
|
// don't switch to it if we planned to
|
||||||
if (index2->second == m_switchWaitScreen) {
|
if (index2->second == m_switchScreen) {
|
||||||
clearSwitchWait();
|
clearSwitchState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -1890,7 +1957,9 @@ CServer::removeConnection(const CString& name)
|
||||||
m_primaryClient->getCursorCenter(m_x, m_y);
|
m_primaryClient->getCursorCenter(m_x, m_y);
|
||||||
|
|
||||||
// stop waiting to switch if we were
|
// stop waiting to switch if we were
|
||||||
clearSwitchWait();
|
if (active == m_switchScreen) {
|
||||||
|
clearSwitchState();
|
||||||
|
}
|
||||||
|
|
||||||
// don't notify active screen since it probably already disconnected
|
// don't notify active screen since it probably already disconnected
|
||||||
LOG((CLOG_INFO "jump from \"%s\" to \"%s\" at %d,%d", active->getName().c_str(), m_primaryClient->getName().c_str(), m_x, m_y));
|
LOG((CLOG_INFO "jump from \"%s\" to \"%s\" at %d,%d", active->getName().c_str(), m_primaryClient->getName().c_str(), m_x, m_y));
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "CCondVar.h"
|
#include "CCondVar.h"
|
||||||
#include "CMutex.h"
|
#include "CMutex.h"
|
||||||
#include "CThread.h"
|
#include "CThread.h"
|
||||||
|
#include "CStopwatch.h"
|
||||||
#include "stdlist.h"
|
#include "stdlist.h"
|
||||||
#include "stdmap.h"
|
#include "stdmap.h"
|
||||||
|
|
||||||
|
@ -198,8 +199,18 @@ private:
|
||||||
IClient* getNeighbor(IClient*, EDirection,
|
IClient* getNeighbor(IClient*, EDirection,
|
||||||
SInt32& x, SInt32& y) const;
|
SInt32& x, SInt32& y) const;
|
||||||
|
|
||||||
|
// test if a switch is permitted. this includes testing user
|
||||||
|
// options like switch delay and tracking any state required to
|
||||||
|
// implement them. returns true iff a switch is permitted.
|
||||||
|
bool isSwitchOkay(IClient* dst, EDirection,
|
||||||
|
SInt32 x, SInt32 y);
|
||||||
|
|
||||||
|
// update switch state due to a mouse move that doesn't try to
|
||||||
|
// switch screens.
|
||||||
|
void onNoSwitch();
|
||||||
|
|
||||||
// reset switch wait state
|
// reset switch wait state
|
||||||
void clearSwitchWait();
|
void clearSwitchState();
|
||||||
|
|
||||||
// send screen options to \c client
|
// send screen options to \c client
|
||||||
void sendOptions(IClient* client) const;
|
void sendOptions(IClient* client) const;
|
||||||
|
@ -325,11 +336,22 @@ private:
|
||||||
CCondVar<SInt32> m_httpAvailable;
|
CCondVar<SInt32> m_httpAvailable;
|
||||||
static const SInt32 s_httpMaxSimultaneousRequests;
|
static const SInt32 s_httpMaxSimultaneousRequests;
|
||||||
|
|
||||||
|
// common state for screen switch tests. all tests are always
|
||||||
|
// trying to reach the same screen in the same direction.
|
||||||
|
EDirection m_switchDir;
|
||||||
|
IClient* m_switchScreen;
|
||||||
|
|
||||||
|
// state for delayed screen switching
|
||||||
double m_switchWaitDelay;
|
double m_switchWaitDelay;
|
||||||
EDirection m_switchWaitDir;
|
|
||||||
UInt32 m_switchWaitTimer;
|
UInt32 m_switchWaitTimer;
|
||||||
IClient* m_switchWaitScreen;
|
bool m_switchWaitEngaged;
|
||||||
SInt32 m_switchWaitX, m_switchWaitY;
|
SInt32 m_switchWaitX, m_switchWaitY;
|
||||||
|
|
||||||
|
// state for double-tap screen switching
|
||||||
|
double m_switchTwoTapDelay;
|
||||||
|
CStopwatch m_switchTwoTapTimer;
|
||||||
|
bool m_switchTwoTapEngaged;
|
||||||
|
bool m_switchTwoTapArmed;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -52,6 +52,7 @@ static const OptionID kOptionModifierMapForMeta = OPTION_CODE("MMFM");
|
||||||
static const OptionID kOptionModifierMapForSuper = OPTION_CODE("MMFR");
|
static const OptionID kOptionModifierMapForSuper = OPTION_CODE("MMFR");
|
||||||
static const OptionID kOptionHeartbeat = OPTION_CODE("HART");
|
static const OptionID kOptionHeartbeat = OPTION_CODE("HART");
|
||||||
static const OptionID kOptionScreenSwitchDelay = OPTION_CODE("SSWT");
|
static const OptionID kOptionScreenSwitchDelay = OPTION_CODE("SSWT");
|
||||||
|
static const OptionID kOptionScreenSwitchTwoTap = OPTION_CODE("SSTT");
|
||||||
//@}
|
//@}
|
||||||
|
|
||||||
#undef OPTION_CODE
|
#undef OPTION_CODE
|
||||||
|
|
|
@ -36,14 +36,17 @@ static const double kHeartBeatsUntilDeath = 3.0;
|
||||||
|
|
||||||
// direction constants
|
// direction constants
|
||||||
enum EDirection {
|
enum EDirection {
|
||||||
|
kNoDirection,
|
||||||
kLeft,
|
kLeft,
|
||||||
kRight,
|
kRight,
|
||||||
kTop,
|
kTop,
|
||||||
kBottom,
|
kBottom,
|
||||||
kFirstDirection = kLeft,
|
kFirstDirection = kLeft,
|
||||||
kLastDirection = kBottom
|
kLastDirection = kBottom,
|
||||||
|
kNumDirections = kLastDirection - kFirstDirection + 1
|
||||||
};
|
};
|
||||||
enum EDirectionMask {
|
enum EDirectionMask {
|
||||||
|
kNoDirMask = 0,
|
||||||
kLeftMask = 1 << kLeft,
|
kLeftMask = 1 << kLeft,
|
||||||
kRightMask = 1 << kRight,
|
kRightMask = 1 << kRight,
|
||||||
kTopMask = 1 << kTop,
|
kTopMask = 1 << kTop,
|
||||||
|
|
Loading…
Reference in New Issue