Allow screen switching on local XInput
Allows to switch from client to server and vice versa if local input is detected, e.g. a touchscreen input or a mouse click if clients also have input devices
This commit is contained in:
parent
5a02070b54
commit
b891b1e630
|
@ -29,6 +29,7 @@ const char* kMsgCScreenSaver = "CSEC%1i";
|
|||
const char* kMsgCResetOptions = "CROP";
|
||||
const char* kMsgCInfoAck = "CIAK";
|
||||
const char* kMsgCKeepAlive = "CALV";
|
||||
const char* kMsgCLocalInput = "CLIN%2i%2i";
|
||||
const char* kMsgDKeyDown = "DKDN%2i%2i%2i";
|
||||
const char* kMsgDKeyDown1_0 = "DKDN%2i%2i";
|
||||
const char* kMsgDKeyRepeat = "DKRP%2i%2i%2i%2i";
|
||||
|
|
|
@ -29,9 +29,10 @@
|
|||
// 1.4: adds crypto support
|
||||
// 1.5: adds file transfer and removes home brew crypto
|
||||
// 1.6: adds clipboard streaming
|
||||
// 1.7: adds focus/screen switch on local input
|
||||
// NOTE: with new version, barrier minor version should increment
|
||||
static const SInt16 kProtocolMajorVersion = 1;
|
||||
static const SInt16 kProtocolMinorVersion = 6;
|
||||
static const SInt16 kProtocolMinorVersion = 7;
|
||||
|
||||
// default contact port number
|
||||
static const UInt16 kDefaultPort = 24800;
|
||||
|
@ -172,6 +173,10 @@ extern const char* kMsgCInfoAck;
|
|||
// defined by an option.
|
||||
extern const char* kMsgCKeepAlive;
|
||||
|
||||
// Local input: secondary -> primary
|
||||
// Inform primary about local input to request focus
|
||||
extern const char* kMsgCLocalInput;
|
||||
|
||||
//
|
||||
// data codes
|
||||
//
|
||||
|
|
|
@ -177,6 +177,7 @@ REGISTER_EVENT(IPrimaryScreen, fakeInputEnd)
|
|||
|
||||
REGISTER_EVENT(IScreen, error)
|
||||
REGISTER_EVENT(IScreen, shapeChanged)
|
||||
REGISTER_EVENT(IScreen, localInput)
|
||||
REGISTER_EVENT(IScreen, suspend)
|
||||
REGISTER_EVENT(IScreen, resume)
|
||||
|
||||
|
|
|
@ -651,6 +651,7 @@ public:
|
|||
IScreenEvents() :
|
||||
m_error(Event::kUnknown),
|
||||
m_shapeChanged(Event::kUnknown),
|
||||
m_localInput(Event::kUnknown),
|
||||
m_suspend(Event::kUnknown),
|
||||
m_resume(Event::kUnknown) { }
|
||||
|
||||
|
@ -671,6 +672,14 @@ public:
|
|||
*/
|
||||
Event::Type shapeChanged();
|
||||
|
||||
//! Get local input event type
|
||||
/*!
|
||||
Returns the local input event type. This is sent when the cursor
|
||||
is not on the current screen but a local input is detected (e.g.
|
||||
via touchscreen inputs or mouse movements).
|
||||
*/
|
||||
Event::Type localInput();
|
||||
|
||||
//! Get suspend event type
|
||||
/*!
|
||||
Returns the suspend event type. This is sent whenever the system goes
|
||||
|
@ -690,6 +699,7 @@ public:
|
|||
private:
|
||||
Event::Type m_error;
|
||||
Event::Type m_shapeChanged;
|
||||
Event::Type m_localInput;
|
||||
Event::Type m_suspend;
|
||||
Event::Type m_resume;
|
||||
};
|
||||
|
|
|
@ -271,7 +271,7 @@ Client::leave()
|
|||
void
|
||||
Client::setClipboard(ClipboardID id, const IClipboard* clipboard)
|
||||
{
|
||||
m_screen->setClipboard(id, clipboard);
|
||||
m_screen->setClipboard(id, clipboard);
|
||||
m_ownClipboard[id] = false;
|
||||
m_sentClipboard[id] = false;
|
||||
}
|
||||
|
@ -508,6 +508,10 @@ Client::setupScreen()
|
|||
getEventTarget(),
|
||||
new TMethodEventJob<Client>(this,
|
||||
&Client::handleClipboardGrabbed));
|
||||
m_events->adoptHandler(m_events->forIScreen().localInput(),
|
||||
getEventTarget(),
|
||||
new TMethodEventJob<Client>(this,
|
||||
&Client::handleLocalInputEvent));
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -564,6 +568,8 @@ Client::cleanupScreen()
|
|||
getEventTarget());
|
||||
m_events->removeHandler(m_events->forClipboard().clipboardGrabbed(),
|
||||
getEventTarget());
|
||||
m_events->removeHandler(m_events->forIScreen().localInput(),
|
||||
getEventTarget());
|
||||
delete m_server;
|
||||
m_server = NULL;
|
||||
}
|
||||
|
@ -767,6 +773,14 @@ Client::handleStopRetry(const Event&, void*)
|
|||
m_args.m_restartable = false;
|
||||
}
|
||||
|
||||
void
|
||||
Client::handleLocalInputEvent(const Event& event, void*)
|
||||
{
|
||||
IPlatformScreen::MotionInfo* info = static_cast<IPlatformScreen::MotionInfo*>(event.getData());
|
||||
LOG((CLOG_DEBUG "Trigger screen switching caused by local input, screen coordinates (%d, %d)", info->m_x, info->m_y));
|
||||
m_server->onLocalInput(info->m_x, info->m_y);
|
||||
}
|
||||
|
||||
void
|
||||
Client::writeToDropDirThread(void*)
|
||||
{
|
||||
|
|
|
@ -191,6 +191,7 @@ private:
|
|||
void handleFileChunkSending(const Event&, void*);
|
||||
void handleFileRecieveCompleted(const Event&, void*);
|
||||
void handleStopRetry(const Event&, void*);
|
||||
void handleLocalInputEvent(const Event&, void*);
|
||||
void onFileRecieveCompleted();
|
||||
void sendClipboardThread(void*);
|
||||
|
||||
|
|
|
@ -367,6 +367,14 @@ ServerProxy::onClipboardChanged(ClipboardID id, const IClipboard* clipboard)
|
|||
StreamChunker::sendClipboard(data, data.size(), id, m_seqNum, m_events, this);
|
||||
}
|
||||
|
||||
void
|
||||
ServerProxy::onLocalInput(SInt32 x, SInt32 y)
|
||||
{
|
||||
// Coordinates as signed 32 bit integers don't make a lot of sense
|
||||
// but are used for compatibility with e.g. IPlatformScreen::MotionInfo
|
||||
ProtocolUtil::writef(m_stream, kMsgCLocalInput, x, y);
|
||||
}
|
||||
|
||||
void
|
||||
ServerProxy::flushCompressedMouse()
|
||||
{
|
||||
|
|
|
@ -50,6 +50,7 @@ public:
|
|||
void onInfoChanged();
|
||||
bool onGrabClipboard(ClipboardID);
|
||||
void onClipboardChanged(ClipboardID, const IClipboard*);
|
||||
void onLocalInput(SInt32 x, SInt32 y);
|
||||
|
||||
//@}
|
||||
|
||||
|
|
|
@ -133,7 +133,7 @@ XWindowsScreen::XWindowsScreen(
|
|||
#ifdef HAVE_XI2
|
||||
m_xi2detected = detectXI2();
|
||||
if (m_xi2detected) {
|
||||
selectXIRawMotion();
|
||||
selectXIRawEventsPrimary();
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
|
@ -145,6 +145,12 @@ XWindowsScreen::XWindowsScreen(
|
|||
openIM();
|
||||
}
|
||||
else {
|
||||
#ifdef HAVE_XI2
|
||||
m_xi2detected = detectXI2();
|
||||
if (m_xi2detected) {
|
||||
selectXIRawEventsSecondary();
|
||||
}
|
||||
#endif
|
||||
// become impervious to server grabs
|
||||
m_impl->XTestGrabControl(m_display, True);
|
||||
}
|
||||
|
@ -1218,22 +1224,41 @@ XWindowsScreen::handleSystemEvent(const Event& event, void*)
|
|||
|
||||
#ifdef HAVE_XI2
|
||||
if (m_xi2detected) {
|
||||
// Process RawMotion
|
||||
// Process RawEvents
|
||||
XGenericEventCookie *cookie = (XGenericEventCookie*)&xevent->xcookie;
|
||||
if (m_impl->XGetEventData(m_display, cookie) &&
|
||||
cookie->type == GenericEvent &&
|
||||
cookie->extension == xi_opcode) {
|
||||
if (cookie->evtype == XI_RawMotion) {
|
||||
// Get current pointer's position
|
||||
Window root, child;
|
||||
XMotionEvent xmotion;
|
||||
xmotion.type = MotionNotify;
|
||||
xmotion.send_event = False; // Raw motion
|
||||
xmotion.display = m_display;
|
||||
xmotion.window = m_window;
|
||||
/* xmotion's time, state and is_hint are not used */
|
||||
unsigned int msk;
|
||||
xmotion.same_screen = m_impl->XQueryPointer(
|
||||
if (m_impl->XGetEventData(m_display, cookie) &&
|
||||
cookie->type == GenericEvent &&
|
||||
cookie->extension == xi_opcode) {
|
||||
LOGC(cookie->evtype == XI_RawTouchBegin, (CLOG_DEBUG1 "Touch begin on %s, onScreen = %s",
|
||||
m_isPrimary ? "server" : "client",
|
||||
m_isOnScreen ? "true" : "false"));
|
||||
|
||||
if (!m_isOnScreen && (cookie->evtype == XI_RawTouchBegin || cookie->evtype == XI_RawButtonPress )) {
|
||||
// Touch or button press detected on local screen but cursor is not on screen => request focus from server
|
||||
// Note: focus switching works on touch from client to server and vice versa, on mouse button press due
|
||||
// to the nature of the shared server mouse only from server to client
|
||||
SInt32 x, y;
|
||||
// On touch event, getCursorPos sets x and y to the actual event coordinates. On mouse button press, x and
|
||||
// y are set to the coordinates of the center of the screen.
|
||||
// In the latter case, reusing m_x, m_y or m_xCursor, m_yCursor failed as they are not set on the client
|
||||
// and thus trigger unexpected behavior (unexpected jumps to (0, 0) on button press)
|
||||
getCursorPos(x, y);
|
||||
sendEvent(m_events->forIScreen().localInput(), MotionInfo::alloc(x, y));
|
||||
m_impl->XFreeEventData(m_display, cookie);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cookie->evtype == XI_RawMotion) {
|
||||
// Mouse motion detected on server => handle mouse motion
|
||||
// Get current pointer's position
|
||||
XMotionEvent xmotion;
|
||||
xmotion.type = MotionNotify;
|
||||
xmotion.send_event = False; // Raw motion
|
||||
xmotion.display = m_display;
|
||||
xmotion.window = m_window;
|
||||
/* xmotion's time, state and is_hint are not used */
|
||||
unsigned int msk;
|
||||
xmotion.same_screen = m_impl->XQueryPointer(
|
||||
m_display, m_root, &xmotion.root, &xmotion.subwindow,
|
||||
&xmotion.x_root,
|
||||
&xmotion.y_root,
|
||||
|
@ -1241,10 +1266,10 @@ XWindowsScreen::handleSystemEvent(const Event& event, void*)
|
|||
&xmotion.y,
|
||||
&msk);
|
||||
onMouseMove(xmotion);
|
||||
m_impl->XFreeEventData(m_display, cookie);
|
||||
m_impl->XFreeEventData(m_display, cookie);
|
||||
return;
|
||||
}
|
||||
m_impl->XFreeEventData(m_display, cookie);
|
||||
}
|
||||
m_impl->XFreeEventData(m_display, cookie);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -2062,17 +2087,43 @@ XWindowsScreen::detectXI2()
|
|||
|
||||
#ifdef HAVE_XI2
|
||||
void
|
||||
XWindowsScreen::selectXIRawMotion()
|
||||
XWindowsScreen::selectXIRawEventsPrimary()
|
||||
{
|
||||
XIEventMask mask;
|
||||
|
||||
mask.deviceid = XIAllDevices;
|
||||
mask.mask_len = XIMaskLen(XI_RawMotion);
|
||||
mask.mask = (unsigned char*)calloc(mask.mask_len, sizeof(char));
|
||||
mask.deviceid = XIAllMasterDevices;
|
||||
memset(mask.mask, 0, 2);
|
||||
XISetMask(mask.mask, XI_RawKeyRelease);
|
||||
mask.mask_len = XIMaskLen(XI_LASTEVENT);
|
||||
mask.mask = (unsigned char*) calloc(mask.mask_len, sizeof(char));
|
||||
LOGC((mask.mask == nullptr), (CLOG_ERR "Cannot listen on XI2 events due to memory error"));
|
||||
|
||||
XISetMask(mask.mask, XI_RawKeyRelease);
|
||||
XISetMask(mask.mask, XI_RawMotion);
|
||||
// Detect touchscreen events on primary screen (= server)
|
||||
XISetMask(mask.mask, XI_RawTouchBegin);
|
||||
XISetMask(mask.mask, XI_RawTouchUpdate);
|
||||
XISetMask(mask.mask, XI_RawTouchEnd);
|
||||
|
||||
m_impl->XISelectEvents(m_display, DefaultRootWindow(m_display), &mask, 1);
|
||||
free(mask.mask);
|
||||
}
|
||||
|
||||
void
|
||||
XWindowsScreen::selectXIRawEventsSecondary()
|
||||
{
|
||||
XIEventMask mask;
|
||||
|
||||
mask.deviceid = XIAllMasterDevices;
|
||||
mask.mask_len = XIMaskLen(XI_LASTEVENT);
|
||||
mask.mask = (unsigned char*) calloc(mask.mask_len, sizeof(char));
|
||||
LOGC((mask.mask == nullptr), (CLOG_ERR "Cannot listen on XI2 events due to memory error"));
|
||||
|
||||
// Detect mouse button press events on secondary screens (= clients)
|
||||
XISetMask(mask.mask, XI_RawButtonPress);
|
||||
// Detect touchscreen events on secondary screens (= clients)
|
||||
XISetMask(mask.mask, XI_RawTouchBegin);
|
||||
XISetMask(mask.mask, XI_RawTouchUpdate);
|
||||
XISetMask(mask.mask, XI_RawTouchEnd);
|
||||
|
||||
m_impl->XISelectEvents(m_display, DefaultRootWindow(m_display), &mask, 1);
|
||||
free(mask.mask);
|
||||
}
|
||||
|
|
|
@ -144,7 +144,8 @@ private:
|
|||
|
||||
bool detectXI2();
|
||||
#ifdef HAVE_XI2
|
||||
void selectXIRawMotion();
|
||||
void selectXIRawEventsPrimary();
|
||||
void selectXIRawEventsSecondary();
|
||||
#endif
|
||||
void selectEvents(Window) const;
|
||||
void doSelectEvents(Window) const;
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* barrier -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2015-2016 Symless Ltd.
|
||||
*
|
||||
* 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
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "server/ClientProxy1_7.h"
|
||||
|
||||
#include "server/Server.h"
|
||||
#include "barrier/ProtocolUtil.h"
|
||||
#include "barrier/StreamChunker.h"
|
||||
#include "barrier/ClipboardChunk.h"
|
||||
#include "io/IStream.h"
|
||||
#include "base/TMethodEventJob.h"
|
||||
#include "base/Log.h"
|
||||
|
||||
//
|
||||
// ClientProxy1_7
|
||||
//
|
||||
|
||||
ClientProxy1_7::ClientProxy1_7(const String& name, barrier::IStream* stream, Server* server, IEventQueue* events) :
|
||||
ClientProxy1_6(name, stream, server, events),
|
||||
m_events(events)
|
||||
{
|
||||
}
|
||||
|
||||
ClientProxy1_7::~ClientProxy1_7()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
ClientProxy1_7::parseMessage(const UInt8* code)
|
||||
{
|
||||
if (memcmp(code, kMsgCLocalInput, 4) == 0) {
|
||||
SInt16 x, y;
|
||||
ProtocolUtil::readf(getStream(), kMsgCLocalInput + 4, &x, &y);
|
||||
|
||||
// Set up container for x and y coordinates
|
||||
IPlatformScreen::MotionInfo* info = IPlatformScreen::MotionInfo::alloc(x, y);
|
||||
|
||||
m_events->addEvent(Event(m_events->forIScreen().localInput(), getEventTarget(), info));
|
||||
}
|
||||
else {
|
||||
return ClientProxy1_6::parseMessage(code);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* barrier -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2015-2016 Symless Ltd.
|
||||
*
|
||||
* 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
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "server/ClientProxy1_6.h"
|
||||
|
||||
class Server;
|
||||
class IEventQueue;
|
||||
|
||||
//! Proxy for client implementing protocol version 1.7
|
||||
class ClientProxy1_7 : public ClientProxy1_6 {
|
||||
public:
|
||||
ClientProxy1_7(const String& name, barrier::IStream* adoptedStream, Server* server, IEventQueue* events);
|
||||
~ClientProxy1_7();
|
||||
|
||||
virtual bool parseMessage(const UInt8* code);
|
||||
|
||||
private:
|
||||
void handleClipboardSendingEvent(const Event&, void*);
|
||||
|
||||
private:
|
||||
IEventQueue* m_events;
|
||||
};
|
|
@ -26,6 +26,7 @@
|
|||
#include "server/ClientProxy1_4.h"
|
||||
#include "server/ClientProxy1_5.h"
|
||||
#include "server/ClientProxy1_6.h"
|
||||
#include "server/ClientProxy1_7.h"
|
||||
#include "barrier/protocol_types.h"
|
||||
#include "barrier/ProtocolUtil.h"
|
||||
#include "barrier/XBarrier.h"
|
||||
|
@ -231,6 +232,10 @@ ClientProxyUnknown::handleData(const Event&, void*)
|
|||
case 6:
|
||||
m_proxy = new ClientProxy1_6(name, m_stream, m_server, m_events);
|
||||
break;
|
||||
|
||||
case 7:
|
||||
m_proxy = new ClientProxy1_7(name, m_stream, m_server, m_events);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -243,6 +243,14 @@ Server::~Server()
|
|||
m_primaryClient->getEventTarget());
|
||||
m_events->removeHandler(m_events->forIPrimaryScreen().screensaverDeactivated(),
|
||||
m_primaryClient->getEventTarget());
|
||||
m_events->removeHandler(m_events->forServer().switchToScreen(),
|
||||
m_inputFilter);
|
||||
m_events->removeHandler(m_events->forServer().switchInDirection(),
|
||||
m_inputFilter);
|
||||
m_events->removeHandler(m_events->forServer().keyboardBroadcast(),
|
||||
m_inputFilter);
|
||||
m_events->removeHandler(m_events->forServer().lockCursorToScreen(),
|
||||
m_inputFilter);
|
||||
m_events->removeHandler(m_events->forIPrimaryScreen().fakeInputBegin(),
|
||||
m_inputFilter);
|
||||
m_events->removeHandler(m_events->forIPrimaryScreen().fakeInputEnd(),
|
||||
|
@ -1426,6 +1434,20 @@ Server::handleToggleScreenEvent(const Event& event, void*)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
Server::handleLocalInputEvent(const Event& event, void* vclient)
|
||||
{
|
||||
BaseClientProxy* client = static_cast<BaseClientProxy*>(vclient);
|
||||
IPlatformScreen::MotionInfo* info = static_cast<IPlatformScreen::MotionInfo*>(event.getData());
|
||||
info = (info == nullptr) ? IPlatformScreen::MotionInfo::alloc(0, 0) : info;
|
||||
LOG((CLOG_DEBUG "Trigger screen switching caused by local input on screen \"%s\", screen coordinates (%d, %d)", client->getName().c_str(), info->m_x, info->m_y));
|
||||
|
||||
// Record current cursor position on active screen
|
||||
m_active->setJumpCursorPos(m_x, m_y);
|
||||
|
||||
// Do actual screen switching
|
||||
switchScreen(client, info->m_x, info->m_y, false);
|
||||
}
|
||||
|
||||
void
|
||||
Server::handleSwitchInDirectionEvent(const Event& event, void*)
|
||||
|
@ -2127,6 +2149,10 @@ Server::addClient(BaseClientProxy* client)
|
|||
client->getEventTarget(),
|
||||
new TMethodEventJob<Server>(this,
|
||||
&Server::handleClipboardChanged, client));
|
||||
m_events->adoptHandler(m_events->forIScreen().localInput(),
|
||||
client->getEventTarget(),
|
||||
new TMethodEventJob<Server>(this,
|
||||
&Server::handleLocalInputEvent, client));
|
||||
|
||||
// add to list
|
||||
m_clientSet.insert(client);
|
||||
|
@ -2159,6 +2185,8 @@ Server::removeClient(BaseClientProxy* client)
|
|||
client->getEventTarget());
|
||||
m_events->removeHandler(m_events->forClipboard().clipboardChanged(),
|
||||
client->getEventTarget());
|
||||
m_events->removeHandler(m_events->forIScreen().localInput(),
|
||||
client->getEventTarget());
|
||||
|
||||
// remove from list
|
||||
m_clients.erase(getName(client));
|
||||
|
|
|
@ -309,6 +309,7 @@ private:
|
|||
void handleClientCloseTimeout(const Event&, void*);
|
||||
void handleSwitchToScreenEvent(const Event&, void*);
|
||||
void handleToggleScreenEvent(const Event&, void*);
|
||||
void handleLocalInputEvent(const Event&, void*);
|
||||
void handleSwitchInDirectionEvent(const Event&, void*);
|
||||
void handleKeyboardBroadcastEvent(const Event&,void*);
|
||||
void handleLockCursorToScreenEvent(const Event&, void*);
|
||||
|
|
Loading…
Reference in New Issue