Panda3D
 All Classes Functions Variables Enumerations
clientBase.cxx
1 // Filename: clientBase.cxx
2 // Created by: jason (04Aug00)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 
16 #include "clientBase.h"
17 #include "config_device.h"
18 
19 TypeHandle ClientBase::_type_handle;
20 
21 ////////////////////////////////////////////////////////////////////
22 // Function: ClientBase::constructor
23 // Access: Protected
24 // Description:
25 ////////////////////////////////////////////////////////////////////
26 ClientBase::
27 ClientBase() {
28  _forked = false;
29  _last_poll_time = 0.0f;
30  _last_poll_frame = 0;
31  _cs = CS_default;
32 
33 #ifdef OLD_HAVE_IPC
34  _client_thread = (thread *)NULL;
35  _shutdown = false;
36 #endif
37 }
38 
39 
40 ////////////////////////////////////////////////////////////////////
41 // Function: ClientBase::destructor
42 // Access: Public
43 // Description:
44 ////////////////////////////////////////////////////////////////////
45 ClientBase::
46 ~ClientBase() {
47  // We have to disconnect all of our devices before destructing.
48  Devices::iterator di;
49  Devices devices_copy = _devices;
50  for (di = devices_copy.begin(); di != devices_copy.end(); ++di) {
51  DevicesByName &dbn = (*di).second;
52  DevicesByName::iterator dbni;
53  for (dbni = dbn.begin(); dbni != dbn.end(); ++dbni) {
54  ClientDevice *device = (*dbni).second;
55  device->disconnect();
56  }
57  }
58 
59 #ifdef OLD_HAVE_IPC
60  if (_forked) {
61  _shutdown = true;
62 
63  // Join the loader thread - calling process blocks until the loader
64  // thread returns.
65  void *ret;
66  _client_thread->join(&ret);
67  }
68 #endif
69 }
70 
71 ////////////////////////////////////////////////////////////////////
72 // Function: ClientBase::fork_asynchronous_thread
73 // Access: Public
74 // Description: Forks a separate thread to do all the polling of
75 // connected devices. The forked thread will poll after
76 // every poll_time seconds has elapsed. Returns true if
77 // the fork was successful, or false otherwise (for
78 // instance, because we were already forked, or because
79 // asynchronous threads are disabled).
80 ////////////////////////////////////////////////////////////////////
81 bool ClientBase::
82 fork_asynchronous_thread(double poll_time) {
83 #ifdef OLD_HAVE_IPC
84  if (_forked) {
85  device_cat.error()
86  << "Attempt to fork client thread twice.\n";
87  return false;
88  }
89 
90  if (asynchronous_clients) {
91  _sleep_time = (int)(1000000 * poll_time);
92 
93  _client_thread = thread::create(&st_callback, this);
94  _forked = true;
95  if (device_cat.is_debug()) {
96  device_cat.debug()
97  << "fork_asynchronous_thread() - forking client thread"
98  << endl;
99  }
100  return true;
101  }
102 #endif
103 
104  return false;
105 }
106 
107 ////////////////////////////////////////////////////////////////////
108 // Function: ClientBase::get_device
109 // Access: Public
110 // Description: Returns a ClientDevice pointer that corresponds to
111 // the named device of the indicated device type. The
112 // device_type should be one of ClientTrackerDevice,
113 // ClientAnalogDevice, etc.; the device_name is
114 // implementation defined.
115 //
116 // Normally, the user does not need to call this
117 // function directly; it is called automatically by
118 // creating a TrackerNode or AnalogNode or some such
119 // data graph node.
120 //
121 // The return value is the pointer to the created device
122 // (which might be the same pointer returned by a
123 // previous call to this function with the same
124 // parameters). When the pointer destructs (i.e. its
125 // reference count reaches zero) it will automatically
126 // be disconnected.
127 //
128 // If the named device does not exist or cannot be
129 // connected for some reason, NULL is returned.
130 ////////////////////////////////////////////////////////////////////
132 get_device(TypeHandle device_type, const string &device_name) {
133  DevicesByName &dbn = _devices[device_type];
134 
135  DevicesByName::iterator dbni;
136  dbni = dbn.find(device_name);
137  if (dbni != dbn.end()) {
138  // This device was previously connected. Return it again.
139  return (*dbni).second;
140  }
141 
142  // We need to create a new device for this name.
143  PT(ClientDevice) device = make_device(device_type, device_name);
144 
145  if (device != (ClientDevice *)NULL) {
146  dbn.insert(DevicesByName::value_type(device_name, device));
147  device->_is_connected = true;
148  }
149 
150  return device;
151 }
152 
153 ////////////////////////////////////////////////////////////////////
154 // Function: ClientBase::disconnect_device
155 // Access: Protected, Virtual
156 // Description: Removes the device, which is presumably about to
157 // destruct, from the list of connected devices, and
158 // frees any data required to support it. This device
159 // will no longer receive automatic updates with each
160 // poll.
161 //
162 // The return value is true if the device was
163 // disconnected, or false if it was unknown (e.g. it was
164 // disconnected previously).
165 ////////////////////////////////////////////////////////////////////
166 bool ClientBase::
167 disconnect_device(TypeHandle device_type, const string &device_name,
168  ClientDevice *device) {
169  DevicesByName &dbn = _devices[device_type];
170 
171  DevicesByName::iterator dbni;
172  dbni = dbn.find(device_name);
173  if (dbni != dbn.end()) {
174  if ((*dbni).second == device) {
175  // We found it!
176  dbn.erase(dbni);
177  return true;
178  }
179  }
180 
181  // The device was unknown.
182  return false;
183 }
184 
185 ////////////////////////////////////////////////////////////////////
186 // Function: ClientBase::do_poll
187 // Access: Protected, Virtual
188 // Description: Implements the polling and updating of connected
189 // devices, if the ClientBase requires this. This may
190 // be called in a sub-thread if
191 // fork_asynchronous_thread() was called; otherwise, it
192 // will be called once per frame.
193 ////////////////////////////////////////////////////////////////////
194 void ClientBase::
195 do_poll() {
196  ClockObject *global_clock = ClockObject::get_global_clock();
197  _last_poll_frame = global_clock->get_frame_count();
198  _last_poll_time = global_clock->get_frame_time();
199 }
200 
201 #ifdef OLD_HAVE_IPC
202 ////////////////////////////////////////////////////////////////////
203 // Function: ClientBase::st_callback
204 // Access: Private, static
205 // Description: Call back function for thread (if thread has been
206 // spawned). A call back function must be static, so
207 // this merely calls the non-static member callback In
208 // addition, the function has a void* return type even
209 // though we don't actually return anything. This is
210 // necessary because ipc assumes a function that does
211 // not return anything indicates that the associated
212 // thread should be created as unjoinable (detached).
213 ////////////////////////////////////////////////////////////////////
214 void *ClientBase::
215 st_callback(void *arg) {
216  nassertr(arg != NULL, NULL);
217  ((ClientBase *)arg)->callback();
218  return NULL;
219 }
220 
221 ////////////////////////////////////////////////////////////////////
222 // Function: ClientBase::callback
223 // Access: Private
224 // Description: This is the main body of the sub-thread. It sleeps
225 // a certain time and then polls all devices currently
226 // being watched
227 ////////////////////////////////////////////////////////////////////
228 void ClientBase::
229 callback() {
230  while (true) {
231  if (_shutdown) {
232  break;
233  }
234  do_poll();
235  ipc_traits::sleep(0, _sleep_time);
236  }
237 }
238 #endif // OLD_HAVE_IPC
static ClockObject * get_global_clock()
Returns a pointer to the global ClockObject.
Definition: clockObject.I:271
void disconnect()
Disconnects the ClientDevice from its ClientBase object.
double get_frame_time(Thread *current_thread=Thread::get_current_thread()) const
Returns the time in seconds as of the last time tick() was called (typically, this will be as of the ...
Definition: clockObject.I:48
A ClockObject keeps track of elapsed real time and discrete time.
Definition: clockObject.h:66
int get_frame_count(Thread *current_thread=Thread::get_current_thread()) const
Returns the number of times tick() has been called since the ClockObject was created, or since it was last reset.
Definition: clockObject.I:113
An abstract base class for a family of client device interfaces–including trackers, buttons, dials, and other analog inputs.
Definition: clientBase.h:47
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85
bool fork_asynchronous_thread(double poll_time)
Forks a separate thread to do all the polling of connected devices.
Definition: clientBase.cxx:82
Any of a number of different devices that might be attached to a ClientBase, including trackers...
Definition: clientDevice.h:35