1 //
2 // Copyright 2016 Pixar
3 //
4 // Licensed under the Apache License, Version 2.0 (the "Apache License")
5 // with the following modification; you may not use this file except in
6 // compliance with the Apache License and the following modification to it:
7 // Section 6. Trademarks. is deleted and replaced with:
8 //
9 // 6. Trademarks. This License does not grant permission to use the trade
10 // names, trademarks, service marks, or product names of the Licensor
11 // and its affiliates, except as required to comply with Section 4(c) of
12 // the License and to reproduce the content of the NOTICE file.
13 //
14 // You may obtain a copy of the Apache License at
15 //
16 // http://www.apache.org/licenses/LICENSE-2.0
17 //
18 // Unless required by applicable law or agreed to in writing, software
19 // distributed under the Apache License with the above modification is
20 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 // KIND, either express or implied. See the Apache License for the specific
22 // language governing permissions and limitations under the Apache License.
23 //
24
25 #include "pxr/pxr.h"
26 #include "pxr/base/tf/type.h"
27 #include "pxr/base/tf/notice.h"
28 #include "pxr/base/tf/pyFunction.h"
29 #include "pxr/base/tf/pyIdentity.h"
30 #include "pxr/base/tf/pyNoticeWrapper.h"
31 #include "pxr/base/tf/pyPtrHelpers.h"
32 #include "pxr/base/tf/pyWeakObject.h"
33 #include "pxr/base/tf/stringUtils.h"
34 #include "pxr/base/tf/type.h"
35
36 #include <boost/noncopyable.hpp>
37 #include <boost/python/class.hpp>
38 #include <boost/python/def.hpp>
39 #include <boost/python/handle.hpp>
40 #include <boost/python/manage_new_object.hpp>
41 #include <boost/python/return_value_policy.hpp>
42 #include <boost/python/scope.hpp>
43
44 #include <functional>
45
46 using std::string;
47
48 using namespace boost::python;
49
50 PXR_NAMESPACE_OPEN_SCOPE
51
52 class Tf_PyNotice {
53 public:
SendWithType(const TfNotice & notice,const TfType & noticeType,const TfWeakBase * sender,const void * senderUniqueId,const std::type_info & senderType)54 static size_t SendWithType(const TfNotice& notice,
55 const TfType & noticeType,
56 const TfWeakBase* sender,
57 const void *senderUniqueId,
58 const std::type_info & senderType)
59 {
60 return notice._SendWithType(noticeType,
61 sender, senderUniqueId, senderType);
62 }
63 };
64
65 PXR_NAMESPACE_CLOSE_SCOPE
66
67 PXR_NAMESPACE_USING_DIRECTIVE
68
69 namespace {
70
71 // TfNotice is passed for both the type and the base to indicate the root of the
72 // hierarchy.
73 TF_INSTANTIATE_NOTICE_WRAPPER(TfNotice, TfNotice);
74
75 class Tf_PyNoticeInternal
76 {
77 public:
78
79 struct Listener : public TfWeakBase, public boost::noncopyable {
80
81 typedef void CallbackSig(object const &, handle<> const &);
82 typedef std::function<CallbackSig> Callback;
83
New__anonacb6c7c40111::Tf_PyNoticeInternal::Listener84 static Listener *New(TfType const ¬iceType,
85 Callback const &callback,
86 TfAnyWeakPtr const &sender)
87 {
88 if (noticeType.IsA<TfNotice>())
89 return new Listener(noticeType, callback, sender);
90
91 // Fail -- unknown notice type.
92 TfPyThrowTypeError("not registering for '" +
93 noticeType.GetTypeName() +
94 "' because it is not a known TfNotice type");
95 // CODE_COVERAGE_OFF The above throws, so this is unreachable.
96 return 0;
97 // CODE_COVERAGE_ON
98 }
99
~Listener__anonacb6c7c40111::Tf_PyNoticeInternal::Listener100 ~Listener() { Revoke(); }
Revoke__anonacb6c7c40111::Tf_PyNoticeInternal::Listener101 void Revoke() { TfNotice::Revoke(_key); }
102
103 private:
104
Listener__anonacb6c7c40111::Tf_PyNoticeInternal::Listener105 Listener(TfType const ¬iceType,
106 Callback const &callback,
107 TfAnyWeakPtr const &sender) :
108 _callback(callback), _noticeType(noticeType) {
109
110 _key = TfNotice::Register
111 (TfCreateWeakPtr(this), &Listener::_HandleNotice,
112 noticeType, sender);
113 }
114
_GetDeliverableNotice__anonacb6c7c40111::Tf_PyNoticeInternal::Listener115 object _GetDeliverableNotice(TfNotice const ¬ice,
116 TfType const ¬iceType) {
117 // If the notice type is not wrapped, return the type name in a
118 // string.
119 TfPyLock lock;
120 /// XXX noticeType is incorrect when the notice is
121 /// python-implemented. We should fix this when TfType optimization
122 /// work is done.
123 object noticeClass = TfPyGetClassObject(typeid(notice));
124 if (TfPyIsNone(noticeClass))
125 return object(TfType::Find(notice).GetTypeName());
126
127 // If it's a python notice, use the embedded python object.
128 if (TfPyNoticeWrapperBase const *pyNotice =
129 TfSafeDynamic_cast<TfPyNoticeWrapperBase const *>(¬ice))
130 return object(pyNotice->GetNoticePythonObject());
131
132 // Otherwise convert the notice to python like normal. We
133 // can't just use object(notice) because that won't produce
134 // a notice of the correct derived type.
135 return Tf_PyNoticeObjectGenerator::Invoke(notice);
136 }
137
_HandleNotice__anonacb6c7c40111::Tf_PyNoticeInternal::Listener138 void _HandleNotice(TfNotice const ¬ice,
139 TfType const &type,
140 TfWeakBase *sender,
141 void const *senderUniqueId,
142 const std::type_info &) {
143 TfPyLock lock;
144 object pyNotice = _GetDeliverableNotice(notice, type);
145 if (!TfPyIsNone(pyNotice)) {
146 // Get the python sender.
147 handle<> pySender = sender ?
148 handle<>(allow_null(Tf_PyIdentityHelper::
149 Get(senderUniqueId))) : handle<>();
150 _callback(pyNotice, pySender);
151 }
152 }
153
154 Callback _callback;
155 TfNotice::Key _key;
156 TfType _noticeType;
157 };
158
159 static Listener *
RegisterWithAnyWeakPtrSender(TfType const & noticeType,Listener::Callback const & cb,TfAnyWeakPtr const & sender)160 RegisterWithAnyWeakPtrSender(TfType const ¬iceType,
161 Listener::Callback const &cb,
162 TfAnyWeakPtr const &sender)
163 {
164 return Listener::New(noticeType, cb, sender);
165 }
166
167 static Listener *
RegisterWithPythonSender(TfType const & noticeType,Listener::Callback const & cb,object const & sender)168 RegisterWithPythonSender(TfType const ¬iceType,
169 Listener::Callback const &cb,
170 object const &sender) {
171 Tf_PyWeakObjectPtr weakSender = Tf_PyWeakObject::GetOrCreate(sender);
172 if (!weakSender)
173 TfPyThrowTypeError("Cannot register to listen to notices from the "
174 "provided sender. The sender must support "
175 "python weak references.");
176
177 TfAnyWeakPtr holder(weakSender);
178 return RegisterWithAnyWeakPtrSender(noticeType, cb, holder);
179 }
180
181 static Listener *
RegisterGlobally(TfType const & noticeType,Listener::Callback const & cb)182 RegisterGlobally(TfType const ¬iceType, Listener::Callback const &cb) {
183 return RegisterWithAnyWeakPtrSender
184 (noticeType, cb, TfAnyWeakPtr());
185 }
186
187 static size_t
SendWithAnyWeakPtrSender(TfNotice const & self,TfAnyWeakPtr const & sender)188 SendWithAnyWeakPtrSender(TfNotice const &self,
189 TfAnyWeakPtr const &sender) {
190 return Tf_PyNotice::SendWithType(self, TfType::Find(&self),
191 sender.GetWeakBase(),
192 sender.GetUniqueIdentifier(),
193 sender.GetTypeInfo());
194 }
195
196 static size_t
SendWithPythonSender(TfNotice const & self,object const & sender)197 SendWithPythonSender(TfNotice const &self, object const &sender) {
198 // Get a "WeakObjectPtr" corresponding to the sender -- this is a
199 // TfWeakPtr to an object which holds a python weak reference. This
200 // object expires when the python object expires. This is what lets us
201 // use arbitrary python objects as senders in the notice system.
202 Tf_PyWeakObjectPtr weakSender = Tf_PyWeakObject::GetOrCreate(sender);
203 if (!weakSender)
204 TfPyThrowTypeError("Cannot send notice from the provided sender. "
205 "Sender must support python weak references.");
206 TfAnyWeakPtr holder(weakSender);
207 return SendWithAnyWeakPtrSender(self, holder);
208 }
209
SendGlobally(TfNotice const & self)210 static size_t SendGlobally(TfNotice const &self) {
211 return Tf_PyNotice::SendWithType(self, TfType::Find(&self),
212 0, 0, typeid(void));
213 }
214
215 };
216
217 } // anonymous namespace
218
wrapNotice()219 void wrapNotice()
220 {
221 // Make sure we can pass callbacks from python.
222 TfPyFunctionFromPython<Tf_PyNoticeInternal::Listener::CallbackSig>();
223
224 // Passing TfNotice for both T and its base indicates that this is the root
225 // of the notice hierarchy.
226 scope notice = TfPyNoticeWrapper<TfNotice, TfNotice>::Wrap("Notice")
227 .def(init<>())
228
229 // We register the method that takes any python object first, as this is
230 // the last overload that will be tried. Thus, it will only be invoked
231 // if the python object is not already weak-pointable.
232 .def("Register",
233 Tf_PyNoticeInternal::RegisterWithPythonSender,
234 return_value_policy<manage_new_object>(),
235 "Register( noticeType, callback, sender ) -> Listener \n\n"
236 "noticeType : Tf.Notice\n"
237 "callback : function\n"
238 "sender : object\n\n"
239 "Register a listener as being interested in a TfNotice "
240 "type from a specific sender. Notice listener will get sender "
241 "as an argument. "
242 " "
243 "Registration of interest in a notice class N automatically "
244 "registers interest in all classes derived from N. When a "
245 "notice of appropriate type is received, the listening object's "
246 " member-function method is called with the notice. "
247 " "
248 " "
249 "To reverse the registration, call Revoke() on the Listener "
250 "object returned by this call. "
251 )
252 .def("Register",
253 Tf_PyNoticeInternal::RegisterWithAnyWeakPtrSender,
254 return_value_policy<manage_new_object>())
255 .staticmethod("Register")
256
257 .def("RegisterGlobally",
258 Tf_PyNoticeInternal::RegisterGlobally,
259 return_value_policy<manage_new_object>(),
260 "RegisterGlobally( noticeType, callback ) -> Listener \n\n"
261 "noticeType : Tf.Notice\n"
262 "callback : function\n\n"
263 "Register a listener as being interested in a TfNotice "
264 "type from any sender. The notice listener does not get sender "
265 "as an argument. "
266 "")
267 .staticmethod("RegisterGlobally")
268
269
270 // We register the method that takes any python object first, as this is
271 // the last overload that will be tried. Thus, it will only be invoked
272 // if the python object is not already weak-pointable.
273 .def("Send", &Tf_PyNoticeInternal::SendWithPythonSender,
274 "Send(sender) \n\n"
275 "sender : object \n\n"
276 "Deliver the notice to interested listeners, returning the number "
277 "of interested listeners. "
278 "This is the recommended form of Send. It takes the sender as an "
279 "argument. "
280 "Listeners that registered for the given sender AND listeners "
281 "that registered globally will get the notice. ")
282 .def("Send", &Tf_PyNoticeInternal::SendWithAnyWeakPtrSender)
283 .def("SendGlobally", &Tf_PyNoticeInternal::SendGlobally,
284 "SendGlobally() \n\n"
285 "Deliver the notice to interested listeners. "
286 "For most clients it is recommended to use the Send(sender) "
287 "version of "
288 "Send() rather than this one. Clients that use this form of Send "
289 "will prevent listeners from being able to register to receive "
290 "notices "
291 "based on the sender of the notice. "
292 "ONLY listeners that registered globally will get the notice. ")
293 ;
294
295 const char* Listener_string = ""
296 "Represents the Notice connection between "
297 "senders and receivers of notices. When a Listener object expires the "
298 "connection is broken. "
299 "You can also use the Revoke() function to break the connection. "
300 "A Listener object is returned from the Register() and "
301 "RegisterGlobally() functions. ";
302 class_<Tf_PyNoticeInternal::Listener,
303 boost::noncopyable>("Listener", Listener_string, no_init)
304 .def("Revoke", &Tf_PyNoticeInternal::Listener::Revoke,
305 "Revoke() \n\n"
306 "Revoke interest by a notice listener. "
307 " "
308 "This function revokes interest in the "
309 "particular notice type and call-back method that its "
310 "Listener object was registered for. "
311 )
312 ;
313 }
314