1 /* 2 * This file is part of gtkD. 3 * 4 * gtkD is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU Lesser General Public License 6 * as published by the Free Software Foundation; either version 3 7 * of the License, or (at your option) any later version, with 8 * some exceptions, please read the COPYING file. 9 * 10 * gtkD is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU Lesser General Public License for more details. 14 * 15 * You should have received a copy of the GNU Lesser General Public License 16 * along with gtkD; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA 18 */ 19 20 // generated automatically - do not change 21 // find conversion definition on APILookup.txt 22 // implement new conversion functionalities on the wrap.utils pakage 23 24 25 module gstreamer.ObjectGst; 26 27 private import glib.ErrorG; 28 private import glib.ListG; 29 private import glib.Str; 30 private import gobject.ObjectG; 31 private import gobject.ParamSpec; 32 private import gobject.Signals; 33 private import gobject.Value; 34 private import gstreamer.ControlBinding; 35 private import gstreamerc.gstreamer; 36 public import gstreamerc.gstreamertypes; 37 public import gtkc.gdktypes; 38 private import std.algorithm; 39 40 41 /** 42 * #GstObject provides a root for the object hierarchy tree filed in by the 43 * GStreamer library. It is currently a thin wrapper on top of 44 * #GInitiallyUnowned. It is an abstract class that is not very usable on its own. 45 * 46 * #GstObject gives us basic refcounting, parenting functionality and locking. 47 * Most of the functions are just extended for special GStreamer needs and can be 48 * found under the same name in the base class of #GstObject which is #GObject 49 * (e.g. g_object_ref() becomes gst_object_ref()). 50 * 51 * Since #GstObject derives from #GInitiallyUnowned, it also inherits the 52 * floating reference. Be aware that functions such as gst_bin_add() and 53 * gst_element_add_pad() take ownership of the floating reference. 54 * 55 * In contrast to #GObject instances, #GstObject adds a name property. The functions 56 * gst_object_set_name() and gst_object_get_name() are used to set/get the name 57 * of the object. 58 * 59 * <refsect2> 60 * <title>controlled properties</title> 61 * <para> 62 * Controlled properties offers a lightweight way to adjust gobject properties 63 * over stream-time. It works by using time-stamped value pairs that are queued 64 * for element-properties. At run-time the elements continuously pull value 65 * changes for the current stream-time. 66 * 67 * What needs to be changed in a #GstElement? 68 * Very little - it is just two steps to make a plugin controllable! 69 * <orderedlist> 70 * <listitem><para> 71 * mark gobject-properties paramspecs that make sense to be controlled, 72 * by GST_PARAM_CONTROLLABLE. 73 * </para></listitem> 74 * <listitem><para> 75 * when processing data (get, chain, loop function) at the beginning call 76 * gst_object_sync_values(element,timestamp). 77 * This will make the controller update all GObject properties that are 78 * under its control with the current values based on the timestamp. 79 * </para></listitem> 80 * </orderedlist> 81 * 82 * What needs to be done in applications? 83 * Again it's not a lot to change. 84 * <orderedlist> 85 * <listitem><para> 86 * create a #GstControlSource. 87 * csource = gst_interpolation_control_source_new (); 88 * g_object_set (csource, "mode", GST_INTERPOLATION_MODE_LINEAR, NULL); 89 * </para></listitem> 90 * <listitem><para> 91 * Attach the #GstControlSource on the controller to a property. 92 * gst_object_add_control_binding (object, gst_direct_control_binding_new (object, "prop1", csource)); 93 * </para></listitem> 94 * <listitem><para> 95 * Set the control values 96 * gst_timed_value_control_source_set ((GstTimedValueControlSource *)csource,0 * GST_SECOND, value1); 97 * gst_timed_value_control_source_set ((GstTimedValueControlSource *)csource,1 * GST_SECOND, value2); 98 * </para></listitem> 99 * <listitem><para> 100 * start your pipeline 101 * </para></listitem> 102 * </orderedlist> 103 * </para> 104 * </refsect2> 105 */ 106 public class ObjectGst : ObjectG 107 { 108 /** the main Gtk struct */ 109 protected GstObject* gstObject; 110 111 /** Get the main Gtk struct */ 112 public GstObject* getObjectGstStruct() 113 { 114 return gstObject; 115 } 116 117 /** the main Gtk struct as a void* */ 118 protected override void* getStruct() 119 { 120 return cast(void*)gstObject; 121 } 122 123 protected override void setStruct(GObject* obj) 124 { 125 gstObject = cast(GstObject*)obj; 126 super.setStruct(obj); 127 } 128 129 /** 130 * Sets our main struct and passes it to the parent class. 131 */ 132 public this (GstObject* gstObject, bool ownedRef = false) 133 { 134 this.gstObject = gstObject; 135 super(cast(GObject*)gstObject, ownedRef); 136 } 137 138 139 /** */ 140 public static GType getType() 141 { 142 return gst_object_get_type(); 143 } 144 145 /** 146 * Checks to see if there is any object named @name in @list. This function 147 * does not do any locking of any kind. You might want to protect the 148 * provided list with the lock of the owner of the list. This function 149 * will lock each #GstObject in the list to compare the name, so be 150 * careful when passing a list with a locked object. 151 * 152 * Params: 153 * list = a list of #GstObject to 154 * check through 155 * name = the name to search for 156 * 157 * Return: %TRUE if a #GstObject named @name does not appear in @list, 158 * %FALSE if it does. 159 * 160 * MT safe. Grabs and releases the LOCK of each object in the list. 161 */ 162 public static bool checkUniqueness(ListG list, string name) 163 { 164 return gst_object_check_uniqueness((list is null) ? null : list.getListGStruct(), Str.toStringz(name)) != 0; 165 } 166 167 /** 168 * A default deep_notify signal callback for an object. The user data 169 * should contain a pointer to an array of strings that should be excluded 170 * from the notify. The default handler will print the new value of the property 171 * using g_print. 172 * 173 * MT safe. This function grabs and releases @object's LOCK for getting its 174 * path string. 175 * 176 * Params: 177 * object = the #GObject that signalled the notify. 178 * orig = a #GstObject that initiated the notify. 179 * pspec = a #GParamSpec of the property. 180 * excludedProps = a set of user-specified properties to exclude or %NULL to show 181 * all changes. 182 */ 183 public static void defaultDeepNotify(ObjectG object, ObjectGst orig, ParamSpec pspec, string[] excludedProps) 184 { 185 gst_object_default_deep_notify((object is null) ? null : object.getObjectGStruct(), (orig is null) ? null : orig.getObjectGstStruct(), (pspec is null) ? null : pspec.getParamSpecStruct(), Str.toStringzArray(excludedProps)); 186 } 187 188 /** 189 * Increase the reference count of @object, and possibly remove the floating 190 * reference, if @object has a floating reference. 191 * 192 * In other words, if the object is floating, then this call "assumes ownership" 193 * of the floating reference, converting it to a normal reference by clearing 194 * the floating flag while leaving the reference count unchanged. If the object 195 * is not floating, then this call adds a new normal reference increasing the 196 * reference count by one. 197 * 198 * Params: 199 * object = a #GstObject to sink 200 */ 201 public static void* refSink(void* object) 202 { 203 return gst_object_ref_sink(object); 204 } 205 206 /** 207 * Atomically modifies a pointer to point to a new object. 208 * The reference count of @oldobj is decreased and the reference count of 209 * @newobj is increased. 210 * 211 * Either @newobj and the value pointed to by @oldobj may be %NULL. 212 * 213 * Params: 214 * oldobj = pointer to a place of 215 * a #GstObject to replace 216 * newobj = a new #GstObject 217 * 218 * Return: %TRUE if @newobj was different from @oldobj 219 */ 220 public static bool replace(ref ObjectGst oldobj, ObjectGst newobj) 221 { 222 GstObject* outoldobj = oldobj.getObjectGstStruct(); 223 224 auto p = gst_object_replace(&outoldobj, (newobj is null) ? null : newobj.getObjectGstStruct()) != 0; 225 226 oldobj = ObjectG.getDObject!(ObjectGst)(outoldobj); 227 228 return p; 229 } 230 231 /** 232 * Attach the #GstControlBinding to the object. If there already was a 233 * #GstControlBinding for this property it will be replaced. 234 * 235 * The @object will take ownership of the @binding. 236 * 237 * Params: 238 * binding = the #GstControlBinding that should be used 239 * 240 * Return: %FALSE if the given @binding has not been setup for this object or 241 * has been setup for a non suitable property, %TRUE otherwise. 242 */ 243 public bool addControlBinding(ControlBinding binding) 244 { 245 return gst_object_add_control_binding(gstObject, (binding is null) ? null : binding.getControlBindingStruct()) != 0; 246 } 247 248 /** 249 * A default error function that uses g_printerr() to display the error message 250 * and the optional debug sting.. 251 * 252 * The default handler will simply print the error string using g_print. 253 * 254 * Params: 255 * error = the GError. 256 * dbg = an additional debug information string, or %NULL 257 */ 258 public void defaultError(ErrorG error, string dbg) 259 { 260 gst_object_default_error(gstObject, (error is null) ? null : error.getErrorGStruct(), Str.toStringz(dbg)); 261 } 262 263 /** 264 * Gets the corresponding #GstControlBinding for the property. This should be 265 * unreferenced again after use. 266 * 267 * Params: 268 * propertyName = name of the property 269 * 270 * Return: the #GstControlBinding for 271 * @property_name or %NULL if the property is not controlled. 272 */ 273 public ControlBinding getControlBinding(string propertyName) 274 { 275 auto p = gst_object_get_control_binding(gstObject, Str.toStringz(propertyName)); 276 277 if(p is null) 278 { 279 return null; 280 } 281 282 return ObjectG.getDObject!(ControlBinding)(cast(GstControlBinding*) p, true); 283 } 284 285 /** 286 * Obtain the control-rate for this @object. Audio processing #GstElement 287 * objects will use this rate to sub-divide their processing loop and call 288 * gst_object_sync_values() inbetween. The length of the processing segment 289 * should be up to @control-rate nanoseconds. 290 * 291 * If the @object is not under property control, this will return 292 * %GST_CLOCK_TIME_NONE. This allows the element to avoid the sub-dividing. 293 * 294 * The control-rate is not expected to change if the element is in 295 * %GST_STATE_PAUSED or %GST_STATE_PLAYING. 296 * 297 * Return: the control rate in nanoseconds 298 */ 299 public GstClockTime getControlRate() 300 { 301 return gst_object_get_control_rate(gstObject); 302 } 303 304 /** 305 * Gets a number of #GValues for the given controlled property starting at the 306 * requested time. The array @values need to hold enough space for @n_values of 307 * #GValue. 308 * 309 * This function is useful if one wants to e.g. draw a graph of the control 310 * curve or apply a control curve sample by sample. 311 * 312 * Params: 313 * propertyName = the name of the property to get 314 * timestamp = the time that should be processed 315 * interval = the time spacing between subsequent values 316 * nValues = the number of values 317 * values = array to put control-values in 318 * 319 * Return: %TRUE if the given array could be filled, %FALSE otherwise 320 */ 321 public bool getGValueArray(string propertyName, GstClockTime timestamp, GstClockTime interval, uint nValues, Value values) 322 { 323 return gst_object_get_g_value_array(gstObject, Str.toStringz(propertyName), timestamp, interval, nValues, (values is null) ? null : values.getValueStruct()) != 0; 324 } 325 326 /** 327 * Returns a copy of the name of @object. 328 * Caller should g_free() the return value after usage. 329 * For a nameless object, this returns %NULL, which you can safely g_free() 330 * as well. 331 * 332 * Free-function: g_free 333 * 334 * Return: the name of @object. g_free() 335 * after usage. 336 * 337 * MT safe. This function grabs and releases @object's LOCK. 338 */ 339 public string getName() 340 { 341 auto retStr = gst_object_get_name(gstObject); 342 343 scope(exit) Str.freeString(retStr); 344 return Str.toString(retStr); 345 } 346 347 /** 348 * Returns the parent of @object. This function increases the refcount 349 * of the parent object so you should gst_object_unref() it after usage. 350 * 351 * Return: parent of @object, this can be 352 * %NULL if @object has no parent. unref after usage. 353 * 354 * MT safe. Grabs and releases @object's LOCK. 355 */ 356 public ObjectGst getParent() 357 { 358 auto p = gst_object_get_parent(gstObject); 359 360 if(p is null) 361 { 362 return null; 363 } 364 365 return ObjectG.getDObject!(ObjectGst)(cast(GstObject*) p, true); 366 } 367 368 /** 369 * Generates a string describing the path of @object in 370 * the object hierarchy. Only useful (or used) for debugging. 371 * 372 * Free-function: g_free 373 * 374 * Return: a string describing the path of @object. You must 375 * g_free() the string after usage. 376 * 377 * MT safe. Grabs and releases the #GstObject's LOCK for all objects 378 * in the hierarchy. 379 */ 380 public string getPathString() 381 { 382 auto retStr = gst_object_get_path_string(gstObject); 383 384 scope(exit) Str.freeString(retStr); 385 return Str.toString(retStr); 386 } 387 388 /** 389 * Gets the value for the given controlled property at the requested time. 390 * 391 * Params: 392 * propertyName = the name of the property to get 393 * timestamp = the time the control-change should be read from 394 * 395 * Return: the GValue of the property at the given time, 396 * or %NULL if the property isn't controlled. 397 */ 398 public Value getValue(string propertyName, GstClockTime timestamp) 399 { 400 auto p = gst_object_get_value(gstObject, Str.toStringz(propertyName), timestamp); 401 402 if(p is null) 403 { 404 return null; 405 } 406 407 return ObjectG.getDObject!(Value)(cast(GValue*) p, true); 408 } 409 410 /** 411 * Gets a number of values for the given controlled property starting at the 412 * requested time. The array @values need to hold enough space for @n_values of 413 * the same type as the objects property's type. 414 * 415 * This function is useful if one wants to e.g. draw a graph of the control 416 * curve or apply a control curve sample by sample. 417 * 418 * The values are unboxed and ready to be used. The similar function 419 * gst_object_get_g_value_array() returns the array as #GValues and is 420 * better suites for bindings. 421 * 422 * Params: 423 * propertyName = the name of the property to get 424 * timestamp = the time that should be processed 425 * interval = the time spacing between subsequent values 426 * nValues = the number of values 427 * values = array to put control-values in 428 * 429 * Return: %TRUE if the given array could be filled, %FALSE otherwise 430 */ 431 public bool getValueArray(string propertyName, GstClockTime timestamp, GstClockTime interval, uint nValues, void* values) 432 { 433 return gst_object_get_value_array(gstObject, Str.toStringz(propertyName), timestamp, interval, nValues, values) != 0; 434 } 435 436 /** 437 * Check if the @object has active controlled properties. 438 * 439 * Return: %TRUE if the object has active controlled properties 440 */ 441 public bool hasActiveControlBindings() 442 { 443 return gst_object_has_active_control_bindings(gstObject) != 0; 444 } 445 446 /** 447 * Check if @object has an ancestor @ancestor somewhere up in 448 * the hierarchy. One can e.g. check if a #GstElement is inside a #GstPipeline. 449 * 450 * Deprecated: Use gst_object_has_as_ancestor() instead. 451 * 452 * MT safe. Grabs and releases @object's locks. 453 * 454 * Params: 455 * ancestor = a #GstObject to check as ancestor 456 * 457 * Return: %TRUE if @ancestor is an ancestor of @object. 458 */ 459 public bool hasAncestor(ObjectGst ancestor) 460 { 461 return gst_object_has_ancestor(gstObject, (ancestor is null) ? null : ancestor.getObjectGstStruct()) != 0; 462 } 463 464 /** 465 * Check if @object has an ancestor @ancestor somewhere up in 466 * the hierarchy. One can e.g. check if a #GstElement is inside a #GstPipeline. 467 * 468 * Params: 469 * ancestor = a #GstObject to check as ancestor 470 * 471 * Return: %TRUE if @ancestor is an ancestor of @object. 472 * 473 * MT safe. Grabs and releases @object's locks. 474 */ 475 public bool hasAsAncestor(ObjectGst ancestor) 476 { 477 return gst_object_has_as_ancestor(gstObject, (ancestor is null) ? null : ancestor.getObjectGstStruct()) != 0; 478 } 479 480 /** 481 * Check if @parent is the parent of @object. 482 * E.g. a #GstElement can check if it owns a given #GstPad. 483 * 484 * Params: 485 * parent = a #GstObject to check as parent 486 * 487 * Return: %FALSE if either @object or @parent is %NULL. %TRUE if @parent is 488 * the parent of @object. Otherwise %FALSE. 489 * 490 * MT safe. Grabs and releases @object's locks. 491 * 492 * Since: 1.6 493 */ 494 public bool hasAsParent(ObjectGst parent) 495 { 496 return gst_object_has_as_parent(gstObject, (parent is null) ? null : parent.getObjectGstStruct()) != 0; 497 } 498 499 /** 500 * Increments the reference count on @object. This function 501 * does not take the lock on @object because it relies on 502 * atomic refcounting. 503 * 504 * This object returns the input parameter to ease writing 505 * constructs like : 506 * result = gst_object_ref (object->parent); 507 * 508 * Return: A pointer to @object 509 */ 510 public override ObjectGst doref() 511 { 512 auto p = gst_object_ref(gstObject); 513 514 if(p is null) 515 { 516 return null; 517 } 518 519 return ObjectG.getDObject!(ObjectGst)(cast(GstObject*) p, true); 520 } 521 522 /** 523 * Removes the corresponding #GstControlBinding. If it was the 524 * last ref of the binding, it will be disposed. 525 * 526 * Params: 527 * binding = the binding 528 * 529 * Return: %TRUE if the binding could be removed. 530 */ 531 public bool removeControlBinding(ControlBinding binding) 532 { 533 return gst_object_remove_control_binding(gstObject, (binding is null) ? null : binding.getControlBindingStruct()) != 0; 534 } 535 536 /** 537 * This function is used to disable the control bindings on a property for 538 * some time, i.e. gst_object_sync_values() will do nothing for the 539 * property. 540 * 541 * Params: 542 * propertyName = property to disable 543 * disabled = boolean that specifies whether to disable the controller 544 * or not. 545 */ 546 public void setControlBindingDisabled(string propertyName, bool disabled) 547 { 548 gst_object_set_control_binding_disabled(gstObject, Str.toStringz(propertyName), disabled); 549 } 550 551 /** 552 * This function is used to disable all controlled properties of the @object for 553 * some time, i.e. gst_object_sync_values() will do nothing. 554 * 555 * Params: 556 * disabled = boolean that specifies whether to disable the controller 557 * or not. 558 */ 559 public void setControlBindingsDisabled(bool disabled) 560 { 561 gst_object_set_control_bindings_disabled(gstObject, disabled); 562 } 563 564 /** 565 * Change the control-rate for this @object. Audio processing #GstElement 566 * objects will use this rate to sub-divide their processing loop and call 567 * gst_object_sync_values() inbetween. The length of the processing segment 568 * should be up to @control-rate nanoseconds. 569 * 570 * The control-rate should not change if the element is in %GST_STATE_PAUSED or 571 * %GST_STATE_PLAYING. 572 * 573 * Params: 574 * controlRate = the new control-rate in nanoseconds. 575 */ 576 public void setControlRate(GstClockTime controlRate) 577 { 578 gst_object_set_control_rate(gstObject, controlRate); 579 } 580 581 /** 582 * Sets the name of @object, or gives @object a guaranteed unique 583 * name (if @name is %NULL). 584 * This function makes a copy of the provided name, so the caller 585 * retains ownership of the name it sent. 586 * 587 * Params: 588 * name = new name of object 589 * 590 * Return: %TRUE if the name could be set. Since Objects that have 591 * a parent cannot be renamed, this function returns %FALSE in those 592 * cases. 593 * 594 * MT safe. This function grabs and releases @object's LOCK. 595 */ 596 public bool setName(string name) 597 { 598 return gst_object_set_name(gstObject, Str.toStringz(name)) != 0; 599 } 600 601 /** 602 * Sets the parent of @object to @parent. The object's reference count will 603 * be incremented, and any floating reference will be removed (see gst_object_ref_sink()). 604 * 605 * Params: 606 * parent = new parent of object 607 * 608 * Return: %TRUE if @parent could be set or %FALSE when @object 609 * already had a parent or @object and @parent are the same. 610 * 611 * MT safe. Grabs and releases @object's LOCK. 612 */ 613 public bool setParent(ObjectGst parent) 614 { 615 return gst_object_set_parent(gstObject, (parent is null) ? null : parent.getObjectGstStruct()) != 0; 616 } 617 618 /** 619 * Returns a suggestion for timestamps where buffers should be split 620 * to get best controller results. 621 * 622 * Return: Returns the suggested timestamp or %GST_CLOCK_TIME_NONE 623 * if no control-rate was set. 624 */ 625 public GstClockTime suggestNextSync() 626 { 627 return gst_object_suggest_next_sync(gstObject); 628 } 629 630 /** 631 * Sets the properties of the object, according to the #GstControlSources that 632 * (maybe) handle them and for the given timestamp. 633 * 634 * If this function fails, it is most likely the application developers fault. 635 * Most probably the control sources are not setup correctly. 636 * 637 * Params: 638 * timestamp = the time that should be processed 639 * 640 * Return: %TRUE if the controller values could be applied to the object 641 * properties, %FALSE otherwise 642 */ 643 public bool syncValues(GstClockTime timestamp) 644 { 645 return gst_object_sync_values(gstObject, timestamp) != 0; 646 } 647 648 /** 649 * Clear the parent of @object, removing the associated reference. 650 * This function decreases the refcount of @object. 651 * 652 * MT safe. Grabs and releases @object's lock. 653 */ 654 public void unparent() 655 { 656 gst_object_unparent(gstObject); 657 } 658 659 /** 660 * Decrements the reference count on @object. If reference count hits 661 * zero, destroy @object. This function does not take the lock 662 * on @object as it relies on atomic refcounting. 663 * 664 * The unref method should never be called with the LOCK held since 665 * this might deadlock the dispose function. 666 */ 667 public override void unref() 668 { 669 gst_object_unref(gstObject); 670 } 671 672 protected class OnDeepNotifyDelegateWrapper 673 { 674 void delegate(ObjectGst, ParamSpec, ObjectGst) dlg; 675 gulong handlerId; 676 ConnectFlags flags; 677 this(void delegate(ObjectGst, ParamSpec, ObjectGst) dlg, gulong handlerId, ConnectFlags flags) 678 { 679 this.dlg = dlg; 680 this.handlerId = handlerId; 681 this.flags = flags; 682 } 683 } 684 protected OnDeepNotifyDelegateWrapper[] onDeepNotifyListeners; 685 686 /** 687 * The deep notify signal is used to be notified of property changes. It is 688 * typically attached to the toplevel bin to receive notifications from all 689 * the elements contained in that bin. 690 * 691 * Params: 692 * propObject = the object that originated the signal 693 * prop = the property that changed 694 */ 695 gulong addOnDeepNotify(void delegate(ObjectGst, ParamSpec, ObjectGst) dlg, ConnectFlags connectFlags=cast(ConnectFlags)0) 696 { 697 onDeepNotifyListeners ~= new OnDeepNotifyDelegateWrapper(dlg, 0, connectFlags); 698 onDeepNotifyListeners[onDeepNotifyListeners.length - 1].handlerId = Signals.connectData( 699 this, 700 "deep-notify", 701 cast(GCallback)&callBackDeepNotify, 702 cast(void*)onDeepNotifyListeners[onDeepNotifyListeners.length - 1], 703 cast(GClosureNotify)&callBackDeepNotifyDestroy, 704 connectFlags); 705 return onDeepNotifyListeners[onDeepNotifyListeners.length - 1].handlerId; 706 } 707 708 extern(C) static void callBackDeepNotify(GstObject* objectgstStruct, GstObject* propObject, GParamSpec* prop,OnDeepNotifyDelegateWrapper wrapper) 709 { 710 wrapper.dlg(ObjectG.getDObject!(ObjectGst)(propObject), ObjectG.getDObject!(ParamSpec)(prop), wrapper.outer); 711 } 712 713 extern(C) static void callBackDeepNotifyDestroy(OnDeepNotifyDelegateWrapper wrapper, GClosure* closure) 714 { 715 wrapper.outer.internalRemoveOnDeepNotify(wrapper); 716 } 717 718 protected void internalRemoveOnDeepNotify(OnDeepNotifyDelegateWrapper source) 719 { 720 foreach(index, wrapper; onDeepNotifyListeners) 721 { 722 if (wrapper.dlg == source.dlg && wrapper.flags == source.flags && wrapper.handlerId == source.handlerId) 723 { 724 onDeepNotifyListeners[index] = null; 725 onDeepNotifyListeners = std.algorithm.remove(onDeepNotifyListeners, index); 726 break; 727 } 728 } 729 } 730 731 }