00001 // Filename: clientBase.cxx 00002 // Created by: jason (04Aug00) 00003 // 00004 //////////////////////////////////////////////////////////////////// 00005 // 00006 // PANDA 3D SOFTWARE 00007 // Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved 00008 // 00009 // All use of this software is subject to the terms of the Panda 3d 00010 // Software license. You should have received a copy of this license 00011 // along with this source code; you will also find a current copy of 00012 // the license at http://www.panda3d.org/license.txt . 00013 // 00014 // To contact the maintainers of this program write to 00015 // panda3d@yahoogroups.com . 00016 // 00017 //////////////////////////////////////////////////////////////////// 00018 00019 00020 #include "clientBase.h" 00021 #include "config_device.h" 00022 00023 TypeHandle ClientBase::_type_handle; 00024 00025 //////////////////////////////////////////////////////////////////// 00026 // Function: ClientBase::constructor 00027 // Access: Protected 00028 // Description: 00029 //////////////////////////////////////////////////////////////////// 00030 ClientBase:: 00031 ClientBase() { 00032 _forked = false; 00033 _last_poll_time = 0.0f; 00034 _last_poll_frame = 0; 00035 _cs = CS_default; 00036 00037 #ifdef OLD_HAVE_IPC 00038 _client_thread = (thread *)NULL; 00039 _shutdown = false; 00040 #endif 00041 } 00042 00043 00044 //////////////////////////////////////////////////////////////////// 00045 // Function: ClientBase::destructor 00046 // Access: Public 00047 // Description: 00048 //////////////////////////////////////////////////////////////////// 00049 ClientBase:: 00050 ~ClientBase() { 00051 // We have to disconnect all of our devices before destructing. 00052 Devices::iterator di; 00053 Devices devices_copy = _devices; 00054 for (di = devices_copy.begin(); di != devices_copy.end(); ++di) { 00055 DevicesByName &dbn = (*di).second; 00056 DevicesByName::iterator dbni; 00057 for (dbni = dbn.begin(); dbni != dbn.end(); ++dbni) { 00058 ClientDevice *device = (*dbni).second; 00059 device->disconnect(); 00060 } 00061 } 00062 00063 #ifdef OLD_HAVE_IPC 00064 if (_forked) { 00065 _shutdown = true; 00066 00067 // Join the loader thread - calling process blocks until the loader 00068 // thread returns. 00069 void *ret; 00070 _client_thread->join(&ret); 00071 } 00072 #endif 00073 } 00074 00075 //////////////////////////////////////////////////////////////////// 00076 // Function: ClientBase::fork_asynchronous_thread 00077 // Access: Public 00078 // Description: Forks a separate thread to do all the polling of 00079 // connected devices. The forked thread will poll after 00080 // every poll_time seconds has elapsed. Returns true if 00081 // the fork was successful, or false otherwise (for 00082 // instance, because we were already forked, or because 00083 // asynchronous threads are disabled). 00084 //////////////////////////////////////////////////////////////////// 00085 bool ClientBase:: 00086 fork_asynchronous_thread(double poll_time) { 00087 #ifdef OLD_HAVE_IPC 00088 if (_forked) { 00089 device_cat.error() 00090 << "Attempt to fork client thread twice.\n"; 00091 return false; 00092 } 00093 00094 if (asynchronous_clients) { 00095 _sleep_time = (int)(1000000 * poll_time); 00096 00097 _client_thread = thread::create(&st_callback, this); 00098 _forked = true; 00099 if (device_cat.is_debug()) { 00100 device_cat.debug() 00101 << "fork_asynchronous_thread() - forking client thread" 00102 << endl; 00103 } 00104 return true; 00105 } 00106 #endif 00107 00108 return false; 00109 } 00110 00111 //////////////////////////////////////////////////////////////////// 00112 // Function: ClientBase::get_device 00113 // Access: Public 00114 // Description: Returns a ClientDevice pointer that corresponds to 00115 // the named device of the indicated device type. The 00116 // device_type should be one of ClientTrackerDevice, 00117 // ClientAnalogDevice, etc.; the device_name is 00118 // implementation defined. 00119 // 00120 // Normally, the user does not need to call this 00121 // function directly; it is called automatically by 00122 // creating a TrackerNode or AnalogNode or some such 00123 // data graph node. 00124 // 00125 // The return value is the pointer to the created device 00126 // (which might be the same pointer returned by a 00127 // previous call to this function with the same 00128 // parameters). When the pointer destructs (i.e. its 00129 // reference count reaches zero) it will automatically 00130 // be disconnected. 00131 // 00132 // If the named device does not exist or cannot be 00133 // connected for some reason, NULL is returned. 00134 //////////////////////////////////////////////////////////////////// 00135 PT(ClientDevice) ClientBase:: 00136 get_device(TypeHandle device_type, const string &device_name) { 00137 DevicesByName &dbn = _devices[device_type]; 00138 00139 DevicesByName::iterator dbni; 00140 dbni = dbn.find(device_name); 00141 if (dbni != dbn.end()) { 00142 // This device was previously connected. Return it again. 00143 return (*dbni).second; 00144 } 00145 00146 // We need to create a new device for this name. 00147 PT(ClientDevice) device = make_device(device_type, device_name); 00148 00149 if (device != (ClientDevice *)NULL) { 00150 dbn.insert(DevicesByName::value_type(device_name, device)); 00151 device->_is_connected = true; 00152 } 00153 00154 return device; 00155 } 00156 00157 //////////////////////////////////////////////////////////////////// 00158 // Function: ClientBase::disconnect_device 00159 // Access: Protected, Virtual 00160 // Description: Removes the device, which is presumably about to 00161 // destruct, from the list of connected devices, and 00162 // frees any data required to support it. This device 00163 // will no longer receive automatic updates with each 00164 // poll. 00165 // 00166 // The return value is true if the device was 00167 // disconnected, or false if it was unknown (e.g. it was 00168 // disconnected previously). 00169 //////////////////////////////////////////////////////////////////// 00170 bool ClientBase:: 00171 disconnect_device(TypeHandle device_type, const string &device_name, 00172 ClientDevice *device) { 00173 DevicesByName &dbn = _devices[device_type]; 00174 00175 DevicesByName::iterator dbni; 00176 dbni = dbn.find(device_name); 00177 if (dbni != dbn.end()) { 00178 if ((*dbni).second == device) { 00179 // We found it! 00180 dbn.erase(dbni); 00181 return true; 00182 } 00183 } 00184 00185 // The device was unknown. 00186 return false; 00187 } 00188 00189 //////////////////////////////////////////////////////////////////// 00190 // Function: ClientBase::do_poll 00191 // Access: Protected, Virtual 00192 // Description: Implements the polling and updating of connected 00193 // devices, if the ClientBase requires this. This may 00194 // be called in a sub-thread if 00195 // fork_asynchronous_thread() was called; otherwise, it 00196 // will be called once per frame. 00197 //////////////////////////////////////////////////////////////////// 00198 void ClientBase:: 00199 do_poll() { 00200 ClockObject *global_clock = ClockObject::get_global_clock(); 00201 _last_poll_frame = global_clock->get_frame_count(); 00202 _last_poll_time = global_clock->get_frame_time(); 00203 } 00204 00205 #ifdef OLD_HAVE_IPC 00206 //////////////////////////////////////////////////////////////////// 00207 // Function: ClientBase::st_callback 00208 // Access: Private, static 00209 // Description: Call back function for thread (if thread has been 00210 // spawned). A call back function must be static, so 00211 // this merely calls the non-static member callback In 00212 // addition, the function has a void* return type even 00213 // though we don't actually return anything. This is 00214 // necessary because ipc assumes a function that does 00215 // not return anything indicates that the associated 00216 // thread should be created as unjoinable (detached). 00217 //////////////////////////////////////////////////////////////////// 00218 void *ClientBase:: 00219 st_callback(void *arg) { 00220 nassertr(arg != NULL, NULL); 00221 ((ClientBase *)arg)->callback(); 00222 return NULL; 00223 } 00224 00225 //////////////////////////////////////////////////////////////////// 00226 // Function: ClientBase::callback 00227 // Access: Private 00228 // Description: This is the main body of the sub-thread. It sleeps 00229 // a certain time and then polls all devices currently 00230 // being watched 00231 //////////////////////////////////////////////////////////////////// 00232 void ClientBase:: 00233 callback() { 00234 while (true) { 00235 if (_shutdown) { 00236 break; 00237 } 00238 do_poll(); 00239 ipc_traits::sleep(0, _sleep_time); 00240 } 00241 } 00242 #endif // OLD_HAVE_IPC