Release v0.6.57: multi-PC input, shared clipboard and resilient Settings Save
This commit is contained in:
1 parent
82292c8a7e
commit
5de010ee44
39 files changed
+1707
-312
No files matched your search
@@ -0,0 +1,82 @@
|
||||
#pragma once
|
||||
#include "lan_input.h"
|
||||
#include <map>
|
||||
namespace ks::lan
|
||||
{
|
||||
// The physical input source routes directly to the current destination. A
|
||||
// destination only reports edge crossings; it never relays injected key events.
|
||||
class InputMesh
|
||||
{
|
||||
std::string local,active;
|
||||
InputEnvironment env;
|
||||
std::map<std::string,POINT> positions;
|
||||
std::map<std::string,uint64_t> ready;
|
||||
std::function<bool(const std::string &,InputFrame)> send;
|
||||
uint64_t activeGeneration{},routeRevision{};
|
||||
InputRouter router;
|
||||
std::string neighbor(const std::string &from,int side)const
|
||||
{
|
||||
POINT p{};if(from!=local){auto found=positions.find(from);if(found==positions.end())return {};p=found->second;}
|
||||
if(side==0)--p.x;else if(side==1)++p.x;else if(side==2)--p.y;else if(side==3)++p.y;else return {};
|
||||
if(!p.x&&!p.y)return local;
|
||||
for(auto &[id,cell]:positions)if(cell.x==p.x&&cell.y==p.y)return id;
|
||||
return {};
|
||||
}
|
||||
void select(const std::string &peer,int side,uint64_t now)
|
||||
{
|
||||
if(active!=peer){active=peer;++routeRevision;}
|
||||
activeGeneration=ready.at(peer);router.selectEdge(side,local<peer);router.tick(true,now);
|
||||
}
|
||||
public:
|
||||
InputMesh(std::string id,InputEnvironment environment,std::map<std::string,POINT> layout,
|
||||
std::function<bool(const std::string &,InputFrame)> sender)
|
||||
:local(std::move(id)),env(std::move(environment)),positions(std::move(layout)),send(std::move(sender)),
|
||||
router(env,[this](InputFrame f){return !active.empty()&&ready.contains(active)&&send(active,f);},1,true,true){}
|
||||
ControlMode mode()const{return router.mode();}
|
||||
uint64_t revision()const{return router.revision()+routeRevision;}
|
||||
const std::string &peer()const{return active;}
|
||||
void cancel(uint64_t now){router.cancel(now);}
|
||||
void tick(std::map<std::string,uint64_t> connections,uint64_t now)
|
||||
{
|
||||
ready=std::move(connections);
|
||||
if(!active.empty()){
|
||||
bool connected=ready.contains(active);
|
||||
if(connected&&activeGeneration!=ready.at(active)){router.cancel(now,"peer reconnected");activeGeneration=ready.at(active);}
|
||||
router.tick(connected,now);
|
||||
}
|
||||
}
|
||||
void receive(const std::string &peer,const InputFrame &f,uint64_t now,bool allowEnter=true)
|
||||
{
|
||||
if(!ready.contains(peer)||!positions.contains(peer))return;
|
||||
if(f.type==InputType::Enter){
|
||||
if(!allowEnter||f.a<0||f.a>3||(router.mode()!=ControlMode::Local&&active!=peer)){
|
||||
send(peer,{InputType::Return,f.token,32768});return;
|
||||
}
|
||||
if(router.mode()==ControlMode::Local)select(peer,f.a^1,now);
|
||||
}
|
||||
if(active!=peer)return;
|
||||
if(f.type==InputType::Cross){
|
||||
if(f.token!=router.sessionToken()||!router.canHandoff()||f.a<0||f.a>3||f.b<0||f.b>65535)return;
|
||||
auto target=neighbor(peer,f.a);
|
||||
if(target.empty()||(target!=local&&!ready.contains(target)))return;
|
||||
if(!send(peer,{InputType::Return,f.token,f.b})){router.cancel(now,"cannot release previous computer");return;}
|
||||
if(target==local){router.returnAt(f.a^1,f.b,now);++routeRevision;return;}
|
||||
active=target;activeGeneration=ready.at(target);++routeRevision;
|
||||
router.handoff(f.a,f.b,now);return;
|
||||
}
|
||||
router.receive(f,now);
|
||||
}
|
||||
bool keyboard(const KBDLLHOOKSTRUCT &key,bool down,uint64_t now){return router.keyboard(key,down,now);}
|
||||
bool mouse(UINT message,const MSLLHOOKSTRUCT &mouse,uint64_t now,bool allowEnter=true)
|
||||
{
|
||||
bool enter=false;
|
||||
if(router.mode()==ControlMode::Local&&allowEnter&&message==WM_MOUSEMOVE&&!(mouse.flags&LLMHF_INJECTED)){
|
||||
for(int side=0;side<4;++side)if(atEdge(mouse.pt,env.bounds(),side)){
|
||||
auto target=neighbor(local,side);
|
||||
if(!target.empty()&&ready.contains(target)){select(target,side,now);enter=true;break;}
|
||||
}
|
||||
}
|
||||
return router.mouse(message,mouse,now,enter);
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user