VideoTools
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
gvviewerevent.cpp
1 //
2 // Copyright (C) 2017 Graeme Walker
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 // ===
17 //
18 // gvviewerevent.cpp
19 //
20 
21 #include "gdef.h"
22 #include "gnet.h"
23 #include "gtimer.h"
24 #include "gvviewerevent.h"
25 #include "geventloop.h"
26 #include "gstr.h"
27 #include "gassert.h"
28 #include <string>
29 #include <sstream>
30 
31 Gv::ViewerEvent::ViewerEvent( const std::string &channel_name ) :
32  m_channel(channel_name,true) ,
33  m_button_down(false)
34 {
35 }
36 
37 std::string Gv::ViewerEvent::channelName() const
38 {
39  return m_channel.name() ;
40 }
41 
43 {
44  return m_channel.open() ;
45 }
46 
48 {
49  return m_channel.fd() ;
50 }
51 
52 bool Gv::ViewerEvent::receive( std::vector<char> & buffer , std::string * type_p )
53 {
54  return m_channel.receive( buffer , type_p ) ;
55 }
56 
57 int Gv::ViewerEvent::parse( const std::string & event , const std::string & key , int default_ )
58 {
59  typedef std::string::size_type pos_t ;
60  const pos_t npos = std::string::npos ;
61 
62  pos_t pos1 = event.find( "'"+key+"':" ) ;
63  if( pos1 == npos ) return default_ ;
64  pos_t pos2 = pos1 + key.length() + 3U ;
65  pos_t pos3 = event.find( "," , pos2 ) ;
66  if( pos3 == npos ) return default_ ;
67  std::string value = event.substr( pos2 , pos3-pos2 ) ;
68  G::Str::trim( value , G::Str::ws() ) ;
69  if( !G::Str::isInt(value) ) return default_ ;
70  return G::Str::toInt(value) ;
71 }
72 
73 Gv::ViewerEvent::Info Gv::ViewerEvent::apply( const std::vector<char> & event )
74 {
75  return apply( std::string(&event[0],event.size()) ) ;
76 }
77 
78 Gv::ViewerEvent::Info Gv::ViewerEvent::apply( const std::string & event )
79 {
80  Info info ;
81  info.type = Invalid ;
82  info.x = parse( event , "x" ) ;
83  info.y = parse( event , "y" ) ;
84  info.x_down = parse( event , "x0" ) ;
85  info.y_down = parse( event , "y0" ) ;
86  info.dx = parse( event , "dx" ) ;
87  info.dy = parse( event , "dy" ) ;
88  info.shift = parse( event , "shift" ) ;
89  info.control = parse( event , "control" ) ;
90 
91  // messaging is not reliable, so button-down messages especially
92  // can get lost -- therefore we synthesise our own button-up
93  // and button-down events depending on the button state
94  // information that appears in all three message types
95 
96  if( event.find("'button': 'down'") != std::string::npos )
97  {
98  if( !m_button_down )
99  {
100  m_button_down = true ;
101  info.type = Down ;
102  }
103  }
104  if( event.find("'event': 'move'") != std::string::npos )
105  {
106  info.type = m_button_down ? Drag : Move ;
107  }
108  if( event.find("'button': 'up'") != std::string::npos )
109  {
110  if( m_button_down )
111  {
112  m_button_down = false ;
113  info.type = Up ;
114  }
115  }
116  return info ;
117 }
118 
119 // ==
120 
121 /// \class Gv::ViewerEventMixinImp
122 /// A pimple-pattern implementation class for Gv::ViewerEventMixin.
123 ///
125 {
126 public:
127  ViewerEventMixinImp( ViewerEventMixin & outer , const std::string & channel ) ;
128  virtual ~ViewerEventMixinImp() ;
129 
130 private:
131  virtual void readEvent() override ;
132  virtual void onException( std::exception & ) override ;
133  void onTimeout() ;
134 
135 private:
136  ViewerEventMixin & m_outer ;
137  ViewerEvent m_viewer_event ;
139  int m_fd ;
140 } ;
141 
142 Gv::ViewerEventMixinImp::ViewerEventMixinImp( ViewerEventMixin & outer , const std::string & channel ) :
143  m_outer(outer) ,
144  m_viewer_event(channel) ,
145  m_timer(*this,&ViewerEventMixinImp::onTimeout,*this) ,
146  m_fd(-1)
147 {
148  if( !channel.empty() )
149  m_timer.startTimer( 1 ) ;
150 }
151 
152 Gv::ViewerEventMixinImp::~ViewerEventMixinImp()
153 {
154  if( m_fd != -1 )
155  GNet::EventLoop::instance().dropRead( GNet::Descriptor(m_viewer_event.fd()) ) ;
156 }
157 
158 void Gv::ViewerEventMixinImp::onTimeout()
159 {
160  if( m_viewer_event.init() )
161  {
162  G_ASSERT( m_fd == -1 ) ;
163  if( m_fd == -1 )
164  {
165  m_fd = m_viewer_event.fd() ;
167  }
168  }
169  else
170  {
171  G_DEBUG( "Gv::ViewerEventMixinImp::onTimeout: viewer event channel not ready: " << m_viewer_event.channelName() ) ;
172  m_timer.startTimer( 1 ) ;
173  }
174 }
175 
176 void Gv::ViewerEventMixinImp::readEvent()
177 {
178  std::vector<char> buffer ;
179  if( !m_viewer_event.receive( buffer ) )
180  throw std::runtime_error( "viewer has gone away" ) ;
181 
182  m_outer.onViewerEvent( m_viewer_event.apply(buffer) ) ;
183 }
184 
185 void Gv::ViewerEventMixinImp::onException( std::exception & )
186 {
187  throw ;
188 }
189 
190 // ==
191 
192 Gv::ViewerEventMixin::ViewerEventMixin( const std::string & channel_name ) :
193  m_imp(new ViewerEventMixinImp(*this,channel_name))
194 {
195 }
196 
198 {
199  delete m_imp ;
200 }
201 
202 // ==
203 
204 Gv::ViewerEvent::Info::Info() :
205  type(Invalid) ,
206  x(-1) ,
207  y(-1) ,
208  dx(0) ,
209  dy(0) ,
210  x_down(-1) ,
211  y_down(-1) ,
212  shift(false) ,
213  control(false)
214 {
215 }
216 
217 std::string Gv::ViewerEvent::Info::str() const
218 {
219  std::ostringstream ss ;
220  ss
221  << type << ","
222  << x << "," << y << ","
223  << dx << "," << dy << ","
224  << x_down << "," << y_down << ","
225  << shift << "," << control ;
226  return ss.str() ;
227 }
228 
A structure representing an interaction event received from a viewer process.
Definition: gvviewerevent.h:43
bool receive(std::vector< char > &, std::string *type_p=nullptr)
Receives the payload for an event on fd().
A class for receiving and handling events published from a viewer process.
Definition: gvviewerevent.h:39
static int toInt(const std::string &s)
Converts string 's' to an int.
Definition: gstr.cpp:368
bool init()
Tries to subscribe to the viewer's event channel.
ViewerEvent(const std::string &channel_name=std::string())
Constructor.
virtual void dropRead(Descriptor fd)=0
Removes the given event source descriptor from the list of read sources.
ViewerEventMixin(const std::string &channel)
Constructor.
A class that encapsulates a network file descriptor and hides knowledge of its o/s-spefific error val...
Definition: gdescriptor.h:37
A pimple-pattern implementation class for Gv::ViewerEventMixin.
static void trim(std::string &s, const std::string &ws)
Trims both ends of s, taking off any of the 'ws' characters.
Definition: gstr.cpp:208
virtual void addRead(Descriptor fd, EventHandler &handler)=0
Adds the given event source descriptor and associated handler to the read list.
static bool isInt(const std::string &s)
Returns true if the string can be converted into an integer without throwing an exception.
Definition: gstr.cpp:250
A base class for classes that handle asynchronous events from the event loop.
Definition: geventhandler.h:78
virtual ~ViewerEventMixin()
Destructor.
std::string channelName() const
Returns the channel name, as passed to the ctor.
int fd() const
Returns the subsription file descriptor, or minus one if not init()isalised.
Info apply(const std::string &)
Parses the event string and maintains an internal button state.
A timer class template in which the timeout is delivered to the specified method. ...
Definition: gtimer.h:110
A mixin base-class containing a ViewerEvent object that integrates with the GNet::EventLoop.
Definition: gvviewerevent.h:97
static std::string ws()
A convenience function returning standard whitespace characters.
Definition: gstr.cpp:1027
static EventLoop & instance()
Returns a reference to an instance of the class, if any.
Definition: geventloop.cpp:43