1/* 2 * Support for callback functions/structs in the FSEvents frameework. 3 */ 4#include <Python.h> 5#include "pyobjc-api.h" 6 7#import <CoreServices/CoreServices.h> 8 9static const void* 10m_retain_python(const void* value) 11{ 12 PyGILState_STATE state = PyGILState_Ensure(); 13 14 Py_XINCREF((PyObject*)value); 15 16 PyGILState_Release(state); 17 18 return value; 19} 20 21static void 22m_release_python(const void* value) 23{ 24 PyGILState_STATE state = PyGILState_Ensure(); 25 26 Py_XDECREF((PyObject*)value); 27 28 PyGILState_Release(state); 29} 30 31static CFStringRef 32m_copyDescription_python(const void* value) 33{ 34 CFStringRef result; 35 PyObject* description; 36 int r; 37 38 PyGILState_STATE state = PyGILState_Ensure(); 39 40 description = PyObject_Repr((PyObject*)value); 41 if (description == NULL) { 42 PyObjCErr_ToObjCWithGILState(&state); 43 } 44 45 r = PyObjC_PythonToObjC(@encode(CFStringRef), description, &result); 46 Py_DECREF(description); 47 if (r == -1) { 48 PyObjCErr_ToObjCWithGILState(&state); 49 } 50 51 /* description is autoreleased, we should donate a reference to 52 * our caller 53 */ 54 CFRetain(result); 55 56 PyGILState_Release(state); 57 return result; 58} 59 60static FSEventStreamContext m_python_context_template = { 61 0, 62 NULL, 63 m_retain_python, 64 m_release_python, 65 m_copyDescription_python 66}; 67 68static void 69m_FSEVentStreamCallback( 70 ConstFSEventStreamRef streamRef, 71 void* clientCallbackInfo, 72 size_t numEvents, 73 void* eventPaths, 74 const FSEventStreamEventFlags eventFlags[], 75 const FSEventStreamEventId eventIds[]) 76{ 77 PyGILState_STATE state = PyGILState_Ensure(); 78 FSEventStreamCreateFlags flags; 79 PyObject* callback; 80 PyObject* info; 81 PyObject* v; 82 PyObject* paths; 83 84 v = PyTuple_GET_ITEM((PyObject*)clientCallbackInfo, 0); 85 if (PyObjC_PythonToObjC( 86 @encode(FSEventStreamCreateFlags), v, &flags) < 0) { 87 PyObjCErr_ToObjCWithGILState(&state); 88 } 89 90 info = PyTuple_GET_ITEM((PyObject*)clientCallbackInfo, 1); 91 callback = PyTuple_GET_ITEM((PyObject*)clientCallbackInfo, 2); 92 93 if (flags & kFSEventStreamCreateFlagUseCFTypes) { 94 /* The evenPaths are an CFArray */ 95 paths = PyObjC_ObjCToPython(@encode(CFArrayRef), &eventPaths); 96 if (paths == NULL) { 97 PyObjCErr_ToObjCWithGILState(&state); 98 } 99 } else { 100 /* The evenPaths are a CArray of C strings */ 101 paths = PyObjC_CArrayToPython(@encode(char*), 102 eventPaths, numEvents); 103 if (paths == NULL) { 104 PyObjCErr_ToObjCWithGILState(&state); 105 } 106 } 107 108 PyObject* py_streamRef = PyObjC_ObjCToPython( 109 @encode(ConstFSEventStreamRef), 110 &streamRef); 111 if (py_streamRef == NULL) { 112 Py_DECREF(paths); 113 PyObjCErr_ToObjCWithGILState(&state); 114 } 115 PyObject* py_eventFlags = PyObjC_CArrayToPython( 116 @encode(FSEventStreamCreateFlags), 117 (void*)eventFlags, numEvents); 118 if (py_eventFlags == NULL) { 119 Py_DECREF(paths); 120 Py_DECREF(py_streamRef); 121 PyObjCErr_ToObjCWithGILState(&state); 122 } 123 PyObject* py_eventIds = PyObjC_CArrayToPython( 124 @encode(FSEventStreamEventId), 125 (void*)eventIds, numEvents); 126 if (py_eventIds == NULL) { 127 Py_DECREF(paths); 128 Py_DECREF(py_streamRef); 129 Py_DECREF(py_eventFlags); 130 PyObjCErr_ToObjCWithGILState(&state); 131 } 132 133 PyObject* result = PyObject_CallFunction(callback, 134 "OO" Py_ARG_SIZE_T "OOO", 135 py_streamRef, info, numEvents, paths, 136 py_eventFlags, 137 py_eventIds); 138 Py_DECREF(paths); 139 Py_DECREF(py_streamRef); 140 Py_DECREF(py_eventFlags); 141 Py_DECREF(py_eventIds); 142 if (result == NULL) { 143 PyObjCErr_ToObjCWithGILState(&state); 144 } 145 Py_DECREF(result); 146 147 PyGILState_Release(state); 148} 149 150PyDoc_STRVAR(m_FSEventStreamCreate_doc, 151 "FSEventStreamCreate(allocator, callback, callback_info, \n" 152 " pathsToWatch, sinceWhen, latency, flags) -> stream\n" 153 "\n" 154 "NOTE: the callback info is passed directly, it is not a structure as\n" 155 "it is in C"); 156static PyObject* 157m_FSEventStreamCreate(PyObject* self __attribute__((__unused__)), 158 PyObject* args) 159{ 160 PyObject* py_allocator; 161 PyObject* py_callback; 162 PyObject* py_callback_info; 163 PyObject* py_pathsToWatch; 164 PyObject* py_sinceWhen; 165 PyObject* py_latency; 166 PyObject* py_flags; 167 168 if (!PyArg_ParseTuple(args, "OOOOOOO", 169 &py_allocator, &py_callback, &py_callback_info, 170 &py_pathsToWatch, &py_sinceWhen, &py_latency, &py_flags)) { 171 172 return NULL; 173 } 174 175 CFAllocatorRef allocator; 176 if (PyObjC_PythonToObjC(@encode(CFAllocatorRef), py_allocator, &allocator) < 0) { 177 return NULL; 178 } 179 180 CFArrayRef pathsToWatch; 181 if (PyObjC_PythonToObjC(@encode(CFArrayRef), py_pathsToWatch, &pathsToWatch) < 0) { 182 return NULL; 183 } 184 185 FSEventStreamEventId sinceWhen; 186 if (PyObjC_PythonToObjC(@encode(FSEventStreamEventId), py_sinceWhen, &sinceWhen) < 0) { 187 return NULL; 188 } 189 190 CFTimeInterval latency; 191 if (PyObjC_PythonToObjC(@encode(CFTimeInterval), py_latency, &latency) < 0) { 192 return NULL; 193 } 194 195 FSEventStreamCreateFlags flags; 196 if (PyObjC_PythonToObjC(@encode(FSEventStreamCreateFlags), py_flags, &flags) < 0) { 197 return NULL; 198 } 199 200 /* 201 * Build the actual callback info, which includes the flags because 202 * the arguments passed to the callback vary based on the value of 203 * flags. 204 */ 205 PyObject* info = Py_BuildValue("OOO", 206 py_flags, py_callback_info, py_callback); 207 if (info == NULL) { 208 return NULL; 209 } 210 211 FSEventStreamContext context = m_python_context_template; 212 context.info = info; 213 214 FSEventStreamRef stream = NULL; 215 216 PyObjC_DURING 217 stream = FSEventStreamCreate( 218 allocator, 219 m_FSEVentStreamCallback, 220 &context, 221 pathsToWatch, 222 sinceWhen, 223 latency, 224 flags); 225 226 PyObjC_HANDLER 227 stream = NULL; 228 PyObjCErr_FromObjC(localException); 229 230 PyObjC_ENDHANDLER 231 232 Py_DECREF(info); 233 234 if (stream == NULL && PyErr_Occurred()) { 235 return NULL; 236 } 237 238 if (stream == NULL) { 239 Py_INCREF(Py_None); 240 return Py_None; 241 } 242 243 PyObject* result = PyObjC_ObjCToPython(@encode(FSEventStreamRef), &stream); 244 // FSEventStreamRef is not a CF type (AFAIK), hence the user is 245 // responsible for maintaining the refcount. 246 //FSEventStreamRelease(stream); 247 return result; 248} 249 250 251PyDoc_STRVAR(m_FSEventStreamCreateRelativeToDevice_doc, 252 "FSEventStreamCreate(allocator, callback, callback_info, \n" 253 " deviceToWatch, pathsToWatchRelativeToDevice, sinceWhen, \n" 254 " latency, flags) -> stream\n" 255 "\n" 256 "NOTE: the callback info is passed directly, it is not a structure as\n" 257 "it is in C"); 258static PyObject* 259m_FSEventStreamCreateRelativeToDevice(PyObject* self __attribute__((__unused__)), 260 PyObject* args) 261{ 262 PyObject* py_allocator; 263 PyObject* py_callback; 264 PyObject* py_callback_info; 265 PyObject* py_pathsToWatch; 266 PyObject* py_sinceWhen; 267 PyObject* py_latency; 268 PyObject* py_flags; 269 PyObject* py_deviceToWatch; 270 271 if (!PyArg_ParseTuple(args, "OOOOOOOO", 272 &py_allocator, &py_callback, &py_callback_info, 273 &py_deviceToWatch, &py_pathsToWatch, &py_sinceWhen, 274 &py_latency, &py_flags)) { 275 276 return NULL; 277 } 278 279 CFAllocatorRef allocator; 280 if (PyObjC_PythonToObjC(@encode(CFAllocatorRef), py_allocator, &allocator) < 0) { 281 return NULL; 282 } 283 284 dev_t deviceToWatch; 285 if (PyObjC_PythonToObjC(@encode(dev_t), py_deviceToWatch, &deviceToWatch) < 0) { 286 return NULL; 287 } 288 289 CFArrayRef pathsToWatch; 290 if (PyObjC_PythonToObjC(@encode(CFArrayRef), py_pathsToWatch, &pathsToWatch) < 0) { 291 return NULL; 292 } 293 294 FSEventStreamEventId sinceWhen; 295 if (PyObjC_PythonToObjC(@encode(FSEventStreamEventId), py_sinceWhen, &sinceWhen) < 0) { 296 return NULL; 297 } 298 299 CFTimeInterval latency; 300 if (PyObjC_PythonToObjC(@encode(CFTimeInterval), py_latency, &latency) < 0) { 301 return NULL; 302 } 303 304 FSEventStreamCreateFlags flags; 305 if (PyObjC_PythonToObjC(@encode(FSEventStreamCreateFlags), py_flags, &flags) < 0) { 306 return NULL; 307 } 308 309 /* 310 * Build the actual callback info, which includes the flags because 311 * the arguments passed to the callback vary based on the value of 312 * flags. 313 */ 314 PyObject* info = Py_BuildValue("OOO", 315 py_flags, py_callback_info, py_callback); 316 if (info == NULL) { 317 return NULL; 318 } 319 320 FSEventStreamContext context = m_python_context_template; 321 context.info = info; 322 323 FSEventStreamRef stream = NULL; 324 325 PyObjC_DURING 326 stream = FSEventStreamCreateRelativeToDevice( 327 allocator, 328 m_FSEVentStreamCallback, 329 &context, 330 deviceToWatch, 331 pathsToWatch, 332 sinceWhen, 333 latency, 334 flags); 335 PyObjC_HANDLER 336 stream = NULL; 337 PyObjCErr_FromObjC(localException); 338 339 PyObjC_ENDHANDLER 340 341 Py_DECREF(info); 342 343 if (stream == NULL && PyErr_Occurred()) { 344 return NULL; 345 } 346 347 if (stream == NULL) { 348 Py_INCREF(Py_None); 349 return Py_None; 350 } 351 352 PyObject* result = PyObjC_ObjCToPython( 353 @encode(FSEventStreamRef), &stream); 354 // FSEventStreamRef is not a CF type (AFAIK), hence the user is 355 // responsible for maintaining the refcount. 356 //FSEventStreamRelease(stream); 357 return result; 358} 359 360 361static PyMethodDef m_methods[] = { 362 { 363 "FSEventStreamCreate", 364 (PyCFunction)m_FSEventStreamCreate, 365 METH_VARARGS, 366 m_FSEventStreamCreate_doc 367 }, 368 369 { 370 "FSEventStreamCreateRelativeToDevice", 371 (PyCFunction)m_FSEventStreamCreateRelativeToDevice, 372 METH_VARARGS, 373 m_FSEventStreamCreateRelativeToDevice_doc 374 }, 375 376 377 { 0, 0, 0, } 378}; 379 380void init_callbacks(void); 381void init_callbacks(void) 382{ 383 PyObject* m = Py_InitModule4("_callbacks", m_methods, 384 NULL, NULL, PYTHON_API_VERSION); 385 386 if (PyObjC_ImportAPI(m) < 0) { return; } 387} 388