diff --git a/direct/src/interval/CMakeLists.txt b/direct/src/interval/CMakeLists.txt index 2eba8a917a..e3053b4369 100644 --- a/direct/src/interval/CMakeLists.txt +++ b/direct/src/interval/CMakeLists.txt @@ -37,11 +37,16 @@ set(P3INTERVAL_SOURCES waitInterval.cxx ) +set(P3INTERVAL_IGATEEXT + cInterval_ext.cxx + cInterval_ext.h +) + composite_sources(p3interval P3INTERVAL_SOURCES) add_component_library(p3interval SYMBOL BUILDING_DIRECT_INTERVAL ${P3INTERVAL_HEADERS} ${P3INTERVAL_SOURCES}) target_link_libraries(p3interval p3directbase panda) -target_interrogate(p3interval ALL) +target_interrogate(p3interval ALL EXTENSIONS ${P3INTERVAL_IGATEEXT}) if(NOT BUILD_METALIBS) install(TARGETS p3interval diff --git a/direct/src/interval/cInterval.h b/direct/src/interval/cInterval.h index 979dba4547..3c4617a801 100644 --- a/direct/src/interval/cInterval.h +++ b/direct/src/interval/cInterval.h @@ -19,6 +19,7 @@ #include "pvector.h" #include "config_interval.h" #include "pStatCollector.h" +#include "extension.h" class CIntervalManager; @@ -120,6 +121,8 @@ PUBLISHED: bool step_play(); PUBLISHED: + EXTENSION(PyObject *__await__(PyObject *self)); + MAKE_PROPERTY(name, get_name); MAKE_PROPERTY(duration, get_duration); MAKE_PROPERTY(open_ended, get_open_ended); diff --git a/direct/src/interval/cInterval_ext.cxx b/direct/src/interval/cInterval_ext.cxx new file mode 100644 index 0000000000..9604f8003b --- /dev/null +++ b/direct/src/interval/cInterval_ext.cxx @@ -0,0 +1,58 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file cInterval_ext.cxx + * @author rdb + * @date 2020-10-17 + */ + +#include "cInterval_ext.h" +#include "cIntervalManager.h" +#include "asyncFuture.h" + +#ifdef HAVE_PYTHON + +#ifndef CPPPARSER +extern struct Dtool_PyTypedObject Dtool_CInterval; +#endif + +/** + * Yields continuously until the interval is done. + */ +static PyObject *gen_next(PyObject *self) { + const CInterval *ival; + if (!Dtool_Call_ExtractThisPointer(self, Dtool_CInterval, (void **)&ival)) { + return nullptr; + } + + if (ival->get_state() != CInterval::S_final) { + // Try again next frame. + Py_INCREF(Py_None); + return Py_None; + } + else { + PyErr_SetNone(PyExc_StopIteration); + return nullptr; + } +} + +/** + * Awaiting an interval starts it and yields a future until it is done. + */ +PyObject *Extension:: +__await__(PyObject *self) { + if (_this->get_state() != CInterval::S_initial) { + PyErr_SetString(PyExc_RuntimeError, "Can only await an interval that is in the initial state."); + return nullptr; + } + + _this->start(); + return Dtool_NewGenerator(self, &gen_next); +} + +#endif // HAVE_PYTHON diff --git a/direct/src/interval/cInterval_ext.h b/direct/src/interval/cInterval_ext.h new file mode 100644 index 0000000000..edec0cffd0 --- /dev/null +++ b/direct/src/interval/cInterval_ext.h @@ -0,0 +1,37 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file cInterval_ext.h + * @author rdb + * @date 2020-10-17 + */ + +#ifndef CINTERVAL_EXT_H +#define CINTERVAL_EXT_H + +#include "dtoolbase.h" + +#ifdef HAVE_PYTHON + +#include "extension.h" +#include "cInterval.h" +#include "py_panda.h" + +/** + * This class defines the extension methods for CInterval, which are called + * instead of any C++ methods with the same prototype. + */ +template<> +class Extension : public ExtensionBase { +public: + PyObject *__await__(PyObject *self); +}; + +#endif // HAVE_PYTHON + +#endif // CINTERVAL_EXT_H diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index c7df213179..9153adfc20 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -4978,6 +4978,7 @@ if not PkgSkip("DIRECT"): IGATEFILES=GetDirectoryContents('direct/src/interval', ["*.h", "*_composite*.cxx"]) TargetAdd('libp3interval.in', opts=OPTS, input=IGATEFILES) TargetAdd('libp3interval.in', opts=['IMOD:panda3d.direct', 'ILIB:libp3interval', 'SRCDIR:direct/src/interval']) + PyTargetAdd('p3interval_cInterval_ext.obj', opts=OPTS, input='cInterval_ext.cxx') # # DIRECTORY: direct/src/showbase/ @@ -5041,6 +5042,7 @@ if not PkgSkip("DIRECT"): PyTargetAdd('direct.pyd', input='libp3showbase_igate.obj') PyTargetAdd('direct.pyd', input='libp3deadrec_igate.obj') PyTargetAdd('direct.pyd', input='libp3interval_igate.obj') + PyTargetAdd('direct.pyd', input='p3interval_cInterval_ext.obj') PyTargetAdd('direct.pyd', input='libp3distributed_igate.obj') PyTargetAdd('direct.pyd', input='libp3motiontrail_igate.obj') diff --git a/panda/src/event/pythonTask.cxx b/panda/src/event/pythonTask.cxx index 6796940d6f..8d7e351394 100644 --- a/panda/src/event/pythonTask.cxx +++ b/panda/src/event/pythonTask.cxx @@ -624,6 +624,11 @@ do_python_task() { return DS_done; } + } else if (result == Py_None && PyCoro_CheckExact(_generator)) { + // Bare yield from a coroutine means to try again next frame. + Py_DECREF(result); + return DS_cont; + } else if (DtoolInstance_Check(result)) { // We are waiting for an AsyncFuture (eg. other task) to finish. AsyncFuture *fut = (AsyncFuture *)DtoolInstance_UPCAST(result, Dtool_AsyncFuture);