mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-01 01:07:51 -04:00
interactive_console
This commit is contained in:
parent
365f35a4f2
commit
7b34234edc
@ -29,7 +29,7 @@ else:
|
|||||||
from direct.showbase import VFSImporter
|
from direct.showbase import VFSImporter
|
||||||
|
|
||||||
from direct.showbase.DirectObject import DirectObject
|
from direct.showbase.DirectObject import DirectObject
|
||||||
from pandac.PandaModules import VirtualFileSystem, Filename, Multifile, loadPrcFileData, unloadPrcFile, getModelPath, HTTPClient, Thread, WindowProperties, readXmlStream, ExecutionEnvironment, PandaSystem, URLSpec
|
from pandac.PandaModules import VirtualFileSystem, Filename, Multifile, loadPrcFileData, unloadPrcFile, getModelPath, HTTPClient, Thread, WindowProperties, readXmlStream, ExecutionEnvironment, PandaSystem, URLSpec, Notify, StreamWriter, ConfigVariableString
|
||||||
from direct.stdpy import file
|
from direct.stdpy import file
|
||||||
from direct.task.TaskManagerGlobal import taskMgr
|
from direct.task.TaskManagerGlobal import taskMgr
|
||||||
from direct.showbase.MessengerGlobal import messenger
|
from direct.showbase.MessengerGlobal import messenger
|
||||||
@ -62,15 +62,21 @@ class AppRunner(DirectObject):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
DirectObject.__init__(self)
|
DirectObject.__init__(self)
|
||||||
|
|
||||||
# We need to make sure sys.stdout maps to sys.stderr instead,
|
# We direct both our stdout and stderr objects onto Panda's
|
||||||
# so if someone makes an unadorned print command within Python
|
# Notify stream. This ensures that unadorned print statements
|
||||||
# code, it won't muck up the data stream between parent and
|
# made within Python will get routed into the log properly.
|
||||||
# child.
|
stream = StreamWriter(Notify.out(), False)
|
||||||
sys.stdout = sys.stderr
|
sys.stdout = stream
|
||||||
|
sys.stderr = stream
|
||||||
|
|
||||||
# This is set true by dummyAppRunner(), below.
|
# This is set true by dummyAppRunner(), below.
|
||||||
self.dummy = False
|
self.dummy = False
|
||||||
|
|
||||||
|
# These will be set from the application flags when
|
||||||
|
# setP3DFilename() is called.
|
||||||
|
self.allowPythonDev = False
|
||||||
|
self.interactiveConsole = False
|
||||||
|
|
||||||
self.sessionId = 0
|
self.sessionId = 0
|
||||||
self.packedAppEnvironmentInitialized = False
|
self.packedAppEnvironmentInitialized = False
|
||||||
self.gotWindow = False
|
self.gotWindow = False
|
||||||
@ -303,6 +309,12 @@ class AppRunner(DirectObject):
|
|||||||
if hasattr(main, 'main') and callable(main.main):
|
if hasattr(main, 'main') and callable(main.main):
|
||||||
main.main(self)
|
main.main(self)
|
||||||
|
|
||||||
|
if self.interactiveConsole:
|
||||||
|
# At this point, we have successfully loaded the app.
|
||||||
|
# If the interactive_console flag is enabled, stop the
|
||||||
|
# main loop now and give the user a Python prompt.
|
||||||
|
taskMgr.stop()
|
||||||
|
|
||||||
def getPandaScriptObject(self):
|
def getPandaScriptObject(self):
|
||||||
""" Called by the browser to query the Panda instance's
|
""" Called by the browser to query the Panda instance's
|
||||||
toplevel scripting object, for querying properties in the
|
toplevel scripting object, for querying properties in the
|
||||||
@ -363,8 +375,8 @@ class AppRunner(DirectObject):
|
|||||||
print "%s %s is not preloaded." % (
|
print "%s %s is not preloaded." % (
|
||||||
package.packageName, package.packageVersion)
|
package.packageName, package.packageVersion)
|
||||||
|
|
||||||
def setP3DFilename(self, p3dFilename, tokens = [], argv = [],
|
def setP3DFilename(self, p3dFilename, tokens, argv, instanceId,
|
||||||
instanceId = None):
|
interactiveConsole):
|
||||||
""" Called by the browser to specify the p3d file that
|
""" Called by the browser to specify the p3d file that
|
||||||
contains the application itself, along with the web tokens
|
contains the application itself, along with the web tokens
|
||||||
and/or command-line arguments. Once this method has been
|
and/or command-line arguments. Once this method has been
|
||||||
@ -402,6 +414,9 @@ class AppRunner(DirectObject):
|
|||||||
# Now load the p3dInfo file.
|
# Now load the p3dInfo file.
|
||||||
self.p3dInfo = None
|
self.p3dInfo = None
|
||||||
self.p3dPackage = None
|
self.p3dPackage = None
|
||||||
|
self.p3dConfig = None
|
||||||
|
self.allowPythonDev = False
|
||||||
|
|
||||||
i = mf.findSubfile('p3d_info.xml')
|
i = mf.findSubfile('p3d_info.xml')
|
||||||
if i >= 0:
|
if i >= 0:
|
||||||
stream = mf.openReadSubfile(i)
|
stream = mf.openReadSubfile(i)
|
||||||
@ -409,6 +424,24 @@ class AppRunner(DirectObject):
|
|||||||
mf.closeReadSubfile(stream)
|
mf.closeReadSubfile(stream)
|
||||||
if self.p3dInfo:
|
if self.p3dInfo:
|
||||||
self.p3dPackage = self.p3dInfo.FirstChildElement('package')
|
self.p3dPackage = self.p3dInfo.FirstChildElement('package')
|
||||||
|
if self.p3dPackage:
|
||||||
|
self.p3dConfig = self.p3dPackage.FirstChildElement('config')
|
||||||
|
|
||||||
|
if self.p3dConfig:
|
||||||
|
allowPythonDev = self.p3dConfig.Attribute('allow_python_dev')
|
||||||
|
if allowPythonDev:
|
||||||
|
self.allowPythonDev = int(allowPythonDev)
|
||||||
|
|
||||||
|
# The interactiveConsole flag can only be set true if the
|
||||||
|
# application has allow_python_dev set.
|
||||||
|
if not self.allowPythonDev and interactiveConsole:
|
||||||
|
raise StandardError, "Impossible, interactive_console set without allow_python_dev."
|
||||||
|
self.interactiveConsole = interactiveConsole
|
||||||
|
|
||||||
|
if self.allowPythonDev:
|
||||||
|
# Set the fps text to remind the user that
|
||||||
|
# allow_python_dev is enabled.
|
||||||
|
ConfigVariableString('frame-rate-meter-text-pattern').setValue('allow_python_dev %0.1f fps')
|
||||||
|
|
||||||
self.initPackedAppEnvironment()
|
self.initPackedAppEnvironment()
|
||||||
|
|
||||||
|
@ -41,13 +41,12 @@ Options:
|
|||||||
also be specified with the pdef-path Config.prc variable.
|
also be specified with the pdef-path Config.prc variable.
|
||||||
|
|
||||||
-D
|
-D
|
||||||
Allow the application to be run with -D on the panda3d command
|
Sets the allow_python_dev flag in the application. This enables
|
||||||
line, or equivalently, "python_dev=1" in the web tokens. If this
|
additional runtime debug operations, particularly the -i option
|
||||||
is allowed, then it will preserve the PYTHONPATH environment
|
to the panda3d command, which enables a live Python prompt within
|
||||||
variable from the user's environment, allowing Python files on
|
the application's environment. Setting this flag may be useful
|
||||||
disk to shadow the same-named Python files within the p3d file,
|
to develop an application initially, but should not be set on an
|
||||||
for rapid iteration on the Python code. If the appliation is not
|
application intended for secure deployment.
|
||||||
built with -D here, this option does nothing at runtime.
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -115,7 +115,7 @@
|
|||||||
p3dCInstance.cxx \
|
p3dCInstance.cxx \
|
||||||
p3dCInstance.h p3dCInstance.I \
|
p3dCInstance.h p3dCInstance.I \
|
||||||
p3dPythonRun.cxx p3dPythonRun.h p3dPythonRun.I \
|
p3dPythonRun.cxx p3dPythonRun.h p3dPythonRun.I \
|
||||||
run_p3dpython.h
|
run_p3dpython.h run_p3dpython.cxx
|
||||||
|
|
||||||
#define WIN_SYS_LIBS user32.lib
|
#define WIN_SYS_LIBS user32.lib
|
||||||
|
|
||||||
|
@ -39,23 +39,6 @@ static const size_t length_nonce2 = 612811373;
|
|||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
static void
|
static void
|
||||||
write_xml_node(ostream &out, TiXmlNode *xnode) {
|
write_xml_node(ostream &out, TiXmlNode *xnode) {
|
||||||
NodeType type = NT_element;
|
|
||||||
if (xnode->ToDocument() != NULL) {
|
|
||||||
type = NT_document;
|
|
||||||
} else if (xnode->ToElement() != NULL) {
|
|
||||||
type = NT_element;
|
|
||||||
} else if (xnode->ToText() != NULL) {
|
|
||||||
type = NT_text;
|
|
||||||
} else {
|
|
||||||
type = NT_unknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
out.put((char)type);
|
|
||||||
// We don't bother to write any data for the unknown types.
|
|
||||||
if (type == NT_unknown) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const string &value = xnode->ValueStr();
|
const string &value = xnode->ValueStr();
|
||||||
size_t value_length = value.length();
|
size_t value_length = value.length();
|
||||||
size_t value_proof = (value_length + length_nonce1) * length_nonce2;
|
size_t value_proof = (value_length + length_nonce1) * length_nonce2;
|
||||||
@ -69,6 +52,24 @@ write_xml_node(ostream &out, TiXmlNode *xnode) {
|
|||||||
out.write((char *)&value_proof, sizeof(value_proof));
|
out.write((char *)&value_proof, sizeof(value_proof));
|
||||||
out.write(value.data(), value_length);
|
out.write(value.data(), value_length);
|
||||||
|
|
||||||
|
// Now write out the node type.
|
||||||
|
NodeType type = NT_element;
|
||||||
|
if (xnode->ToDocument() != NULL) {
|
||||||
|
type = NT_document;
|
||||||
|
} else if (xnode->ToElement() != NULL) {
|
||||||
|
type = NT_element;
|
||||||
|
} else if (xnode->ToText() != NULL) {
|
||||||
|
type = NT_text;
|
||||||
|
} else {
|
||||||
|
type = NT_unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
out.put((char)type);
|
||||||
|
// We don't bother to write any further data for the unknown types.
|
||||||
|
if (type == NT_unknown) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (type == NT_element) {
|
if (type == NT_element) {
|
||||||
// Write the element attributes.
|
// Write the element attributes.
|
||||||
TiXmlElement *xelement = xnode->ToElement();
|
TiXmlElement *xelement = xnode->ToElement();
|
||||||
@ -119,11 +120,6 @@ write_xml_node(ostream &out, TiXmlNode *xnode) {
|
|||||||
static TiXmlNode *
|
static TiXmlNode *
|
||||||
read_xml_node(istream &in, char *&buffer, size_t &buffer_length,
|
read_xml_node(istream &in, char *&buffer, size_t &buffer_length,
|
||||||
ostream &logfile) {
|
ostream &logfile) {
|
||||||
NodeType type = (NodeType)in.get();
|
|
||||||
if (type == NT_unknown) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t value_length;
|
size_t value_length;
|
||||||
in.read((char *)&value_length, sizeof(value_length));
|
in.read((char *)&value_length, sizeof(value_length));
|
||||||
if (in.gcount() != sizeof(value_length)) {
|
if (in.gcount() != sizeof(value_length)) {
|
||||||
@ -167,6 +163,12 @@ read_xml_node(istream &in, char *&buffer, size_t &buffer_length,
|
|||||||
in.read(buffer, value_length);
|
in.read(buffer, value_length);
|
||||||
string value(buffer, value_length);
|
string value(buffer, value_length);
|
||||||
|
|
||||||
|
// Read the node type.
|
||||||
|
NodeType type = (NodeType)in.get();
|
||||||
|
if (type == NT_unknown) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
TiXmlNode *xnode = NULL;
|
TiXmlNode *xnode = NULL;
|
||||||
if (type == NT_element) {
|
if (type == NT_element) {
|
||||||
xnode = new TiXmlElement(value);
|
xnode = new TiXmlElement(value);
|
||||||
|
@ -21,9 +21,11 @@
|
|||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
typedef HANDLE FHandle;
|
typedef HANDLE FHandle;
|
||||||
|
static const HANDLE invalid_fhandle = INVALID_HANDLE_VALUE;
|
||||||
#else
|
#else
|
||||||
// On POSIX, we use a file descriptor as a "handle".
|
// On POSIX, we use a file descriptor as a "handle".
|
||||||
typedef int FHandle;
|
typedef int FHandle;
|
||||||
|
static const int invalid_fhandle = -1;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -164,7 +164,7 @@ load_plugin(const string &p3d_plugin_filename,
|
|||||||
#else // _WIN32
|
#else // _WIN32
|
||||||
// Posix case.
|
// Posix case.
|
||||||
assert(module == NULL);
|
assert(module == NULL);
|
||||||
module = dlopen(filename.c_str(), RTLD_NOW | RTLD_LOCAL);
|
module = dlopen(filename.c_str(), RTLD_LOCAL);
|
||||||
if (module == NULL) {
|
if (module == NULL) {
|
||||||
// Couldn't load the .so.
|
// Couldn't load the .so.
|
||||||
return false;
|
return false;
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
#include <shlobj.h>
|
#include <shlobj.h>
|
||||||
#else
|
#else
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <signal.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static ofstream logfile;
|
static ofstream logfile;
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include "run_p3dpython.h"
|
#include "run_p3dpython.h"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
@ -41,12 +42,29 @@ main(int argc, char *argv[]) {
|
|||||||
const char *program_name = argv[0];
|
const char *program_name = argv[0];
|
||||||
const char *dll_file = NULL;
|
const char *dll_file = NULL;
|
||||||
const char *archive_file = NULL;
|
const char *archive_file = NULL;
|
||||||
|
const char *input_handle_str = NULL;
|
||||||
|
const char *output_handle_str = NULL;
|
||||||
|
const char *error_handle_str = NULL;
|
||||||
|
const char *interactive_console_str = NULL;
|
||||||
|
|
||||||
if (argc > 1) {
|
if (argc > 1) {
|
||||||
dll_file = argv[1];
|
dll_file = argv[1];
|
||||||
}
|
}
|
||||||
if (argc > 2) {
|
if (argc > 2) {
|
||||||
archive_file = argv[2];
|
archive_file = argv[2];
|
||||||
}
|
}
|
||||||
|
if (argc > 3) {
|
||||||
|
input_handle_str = argv[3];
|
||||||
|
}
|
||||||
|
if (argc > 4) {
|
||||||
|
output_handle_str = argv[4];
|
||||||
|
}
|
||||||
|
if (argc > 5) {
|
||||||
|
error_handle_str = argv[5];
|
||||||
|
}
|
||||||
|
if (argc > 6) {
|
||||||
|
interactive_console_str = argv[6];
|
||||||
|
}
|
||||||
|
|
||||||
if (dll_file == NULL || *dll_file == '\0') {
|
if (dll_file == NULL || *dll_file == '\0') {
|
||||||
cerr << "No libp3dpython filename specified on command line.\n";
|
cerr << "No libp3dpython filename specified on command line.\n";
|
||||||
@ -58,6 +76,47 @@ main(int argc, char *argv[]) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FHandle input_handle = invalid_fhandle;
|
||||||
|
if (input_handle_str != NULL && *input_handle_str) {
|
||||||
|
stringstream stream(input_handle_str);
|
||||||
|
stream >> input_handle;
|
||||||
|
if (!stream) {
|
||||||
|
input_handle = invalid_fhandle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FHandle output_handle = invalid_fhandle;
|
||||||
|
if (output_handle_str != NULL && *output_handle_str) {
|
||||||
|
stringstream stream(output_handle_str);
|
||||||
|
stream >> output_handle;
|
||||||
|
if (!stream) {
|
||||||
|
output_handle = invalid_fhandle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FHandle error_handle = invalid_fhandle;
|
||||||
|
if (error_handle_str != NULL && *error_handle_str) {
|
||||||
|
stringstream stream(error_handle_str);
|
||||||
|
stream >> error_handle;
|
||||||
|
if (!stream) {
|
||||||
|
error_handle = invalid_fhandle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool interactive_console = false;
|
||||||
|
if (interactive_console_str != NULL && *interactive_console_str) {
|
||||||
|
stringstream stream(interactive_console_str);
|
||||||
|
int flag;
|
||||||
|
stream >> flag;
|
||||||
|
if (stream) {
|
||||||
|
interactive_console = (flag != 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cerr << "handles: " << input_handle << ", " << output_handle
|
||||||
|
<< ", " << error_handle << "\n";
|
||||||
|
cerr << "interactive_console = " << interactive_console << "\n";
|
||||||
|
|
||||||
// For some vague idea of security, we insist that this program can
|
// For some vague idea of security, we insist that this program can
|
||||||
// only run libp3dpython.dll: you can't use it to load just any
|
// only run libp3dpython.dll: you can't use it to load just any
|
||||||
// arbitrary DLL on the system. Of course, if you're successfully
|
// arbitrary DLL on the system. Of course, if you're successfully
|
||||||
@ -72,15 +131,16 @@ main(int argc, char *argv[]) {
|
|||||||
slash = backslash;
|
slash = backslash;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
string basename;
|
||||||
if (slash == NULL) {
|
if (slash == NULL) {
|
||||||
slash = dll_file;
|
basename = dll_file;
|
||||||
} else {
|
} else {
|
||||||
++slash;
|
//dirname = string(dll_file, slash - dll_file);
|
||||||
|
basename = (slash + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
string expected_basename = "libp3dpython" + dll_ext;
|
string expected_basename = "libp3dpython" + dll_ext;
|
||||||
|
if (basename != expected_basename) {
|
||||||
if (memcmp(slash, expected_basename.data(), expected_basename.size()) != 0) {
|
|
||||||
cerr << dll_file << " does not name " << expected_basename << "\n";
|
cerr << dll_file << " does not name " << expected_basename << "\n";
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -97,18 +157,40 @@ main(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#define get_func GetProcAddress
|
#define get_func GetProcAddress
|
||||||
FHandle input = GetStdHandle(STD_INPUT_HANDLE);
|
|
||||||
FHandle output = GetStdHandle(STD_OUTPUT_HANDLE);
|
// Get the default values for the communication handles, if we
|
||||||
if (!SetStdHandle(STD_INPUT_HANDLE, INVALID_HANDLE_VALUE)) {
|
// weren't given specific handles.
|
||||||
cerr << "unable to reset input handle\n";
|
if (input_handle == invalid_fhandle) {
|
||||||
}
|
input_handle = GetStdHandle(STD_INPUT_HANDLE);
|
||||||
if (!SetStdHandle(STD_OUTPUT_HANDLE, INVALID_HANDLE_VALUE)) {
|
|
||||||
cerr << "unable to reset input handle\n";
|
// Close the system input handle, so application code won't
|
||||||
|
// accidentally read from our private input stream.
|
||||||
|
if (!SetStdHandle(STD_INPUT_HANDLE, INVALID_HANDLE_VALUE)) {
|
||||||
|
cerr << "unable to reset input handle\n";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (output_handle == invalid_fhandle) {
|
||||||
|
output_handle = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||||
|
|
||||||
|
// Close the system output handle, so application code won't
|
||||||
|
// accidentally write to our private output stream.
|
||||||
|
if (!SetStdHandle(STD_OUTPUT_HANDLE, INVALID_HANDLE_VALUE)) {
|
||||||
|
cerr << "unable to reset input handle\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No matter what error handle we were given, make it
|
||||||
|
// STD_ERROR_HANDLE.
|
||||||
|
if (error_handle == invalid_fhandle) {
|
||||||
|
error_handle = GetStdHandle(STD_ERROR_HANDLE);
|
||||||
|
} else {
|
||||||
|
SetStdHandle(STD_ERROR_HANDLE, error_handle);
|
||||||
|
}
|
||||||
|
|
||||||
#else // _WIN32
|
#else // _WIN32
|
||||||
// Posix case.
|
// Posix case.
|
||||||
void *module = dlopen(dll_file, RTLD_GLOBAL);
|
void *module = dlopen(dll_file, RTLD_LOCAL);
|
||||||
if (module == NULL) {
|
if (module == NULL) {
|
||||||
// Couldn't load the .so.
|
// Couldn't load the .so.
|
||||||
cerr << "Couldn't load " << dll_file << "\n";
|
cerr << "Couldn't load " << dll_file << "\n";
|
||||||
@ -116,8 +198,25 @@ main(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#define get_func dlsym
|
#define get_func dlsym
|
||||||
FHandle input = STDIN_FILENO;
|
|
||||||
FHandle output = STDOUT_FILENO;
|
// Get the default values for the communication handles, if we
|
||||||
|
// weren't given specific handles.
|
||||||
|
if (input_handle == invalid_fhandle) {
|
||||||
|
input_handle = STDIN_FILENO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (output_handle == invalid_fhandle) {
|
||||||
|
output_handle = STDOUT_FILENO;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No matter what error handle we were given, make it STDERR_FILENO.
|
||||||
|
if (error_handle == invalid_fhandle) {
|
||||||
|
error_handle = STDERR_FILENO;
|
||||||
|
} else if (error_handle != STDERR_FILENO) {
|
||||||
|
dup2(error_handle, STDERR_FILENO);
|
||||||
|
close(error_handle);
|
||||||
|
error_handle = STDERR_FILENO;
|
||||||
|
}
|
||||||
|
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
|
|
||||||
@ -127,7 +226,8 @@ main(int argc, char *argv[]) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!run_p3dpython(program_name, archive_file, input, output)) {
|
if (!run_p3dpython(program_name, archive_file, input_handle, output_handle,
|
||||||
|
error_handle, interactive_console)) {
|
||||||
cerr << "Failure on startup.\n";
|
cerr << "Failure on startup.\n";
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,8 @@ P3DPythonRun *P3DPythonRun::_global_ptr = NULL;
|
|||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
P3DPythonRun::
|
P3DPythonRun::
|
||||||
P3DPythonRun(const char *program_name, const char *archive_file,
|
P3DPythonRun(const char *program_name, const char *archive_file,
|
||||||
FHandle input, FHandle output) {
|
FHandle input_handle, FHandle output_handle,
|
||||||
|
FHandle error_handle, bool interactive_console) {
|
||||||
_read_thread_continue = false;
|
_read_thread_continue = false;
|
||||||
_program_continue = true;
|
_program_continue = true;
|
||||||
_session_terminated = false;
|
_session_terminated = false;
|
||||||
@ -40,6 +41,8 @@ P3DPythonRun(const char *program_name, const char *archive_file,
|
|||||||
_session_id = 0;
|
_session_id = 0;
|
||||||
_next_sent_id = 0;
|
_next_sent_id = 0;
|
||||||
|
|
||||||
|
_interactive_console = interactive_console;
|
||||||
|
|
||||||
if (program_name != NULL) {
|
if (program_name != NULL) {
|
||||||
_program_name = program_name;
|
_program_name = program_name;
|
||||||
}
|
}
|
||||||
@ -70,10 +73,18 @@ P3DPythonRun(const char *program_name, const char *archive_file,
|
|||||||
Py_Initialize();
|
Py_Initialize();
|
||||||
PySys_SetArgv(_py_argc, _py_argv);
|
PySys_SetArgv(_py_argc, _py_argv);
|
||||||
|
|
||||||
|
// Open the error output before we do too much more.
|
||||||
|
_error_log.open_write(error_handle);
|
||||||
|
if (_error_log) {
|
||||||
|
// Set up the indicated error log as the Notify output.
|
||||||
|
_error_log.setf(ios::unitbuf);
|
||||||
|
Notify::ptr()->set_ostream_ptr(&_error_log, false);
|
||||||
|
}
|
||||||
|
|
||||||
// Open the pipe streams with the input and output handles from the
|
// Open the pipe streams with the input and output handles from the
|
||||||
// parent.
|
// parent.
|
||||||
_pipe_read.open_read(input);
|
_pipe_read.open_read(input_handle);
|
||||||
_pipe_write.open_write(output);
|
_pipe_write.open_write(output_handle);
|
||||||
|
|
||||||
if (!_pipe_read) {
|
if (!_pipe_read) {
|
||||||
nout << "unable to open read pipe\n";
|
nout << "unable to open read pipe\n";
|
||||||
@ -96,6 +107,10 @@ P3DPythonRun::
|
|||||||
|
|
||||||
join_read_thread();
|
join_read_thread();
|
||||||
DESTROY_LOCK(_commands_lock);
|
DESTROY_LOCK(_commands_lock);
|
||||||
|
|
||||||
|
// Restore the notify stream in case it tries to write to anything
|
||||||
|
// else after our shutdown.
|
||||||
|
Notify::ptr()->set_ostream_ptr(&cerr, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
@ -301,14 +316,45 @@ run_python() {
|
|||||||
Py_DECREF(check_comm);
|
Py_DECREF(check_comm);
|
||||||
|
|
||||||
// Finally, get lost in taskMgr.run().
|
// Finally, get lost in taskMgr.run().
|
||||||
|
bool okflag = true;
|
||||||
PyObject *done = PyObject_CallMethod(_taskMgr, (char *)"run", (char *)"");
|
PyObject *done = PyObject_CallMethod(_taskMgr, (char *)"run", (char *)"");
|
||||||
if (done == NULL) {
|
if (done == NULL) {
|
||||||
PyErr_Print();
|
PyErr_Print();
|
||||||
return false;
|
okflag = false;
|
||||||
|
} else {
|
||||||
|
Py_DECREF(done);
|
||||||
}
|
}
|
||||||
Py_DECREF(done);
|
|
||||||
|
|
||||||
return true;
|
if (_interactive_console) {
|
||||||
|
run_interactive_console();
|
||||||
|
okflag = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return okflag;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: P3DPythonRun::run_interactive_console
|
||||||
|
// Access: Private
|
||||||
|
// Description: Gives the user a chance to type interactive Python
|
||||||
|
// commands, for easy development of a p3d application.
|
||||||
|
// This method is only called if "interactive_console=1"
|
||||||
|
// is set as a web token, and "allow_python_dev" is set
|
||||||
|
// within the application itself.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
void P3DPythonRun::
|
||||||
|
run_interactive_console() {
|
||||||
|
// The "readline" module makes the Python prompt friendlier, with
|
||||||
|
// command history and everything. Simply importing it is
|
||||||
|
// sufficient.
|
||||||
|
PyObject *readline_module = PyImport_ImportModule("readline");
|
||||||
|
if (readline_module == NULL) {
|
||||||
|
PyErr_Print();
|
||||||
|
} else {
|
||||||
|
Py_DECREF(readline_module);
|
||||||
|
}
|
||||||
|
|
||||||
|
PyRun_InteractiveLoop(stdin, "<stdin>");
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
@ -1117,8 +1163,8 @@ set_p3d_filename(P3DCInstance *inst, TiXmlElement *xfparams) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
PyObject *result = PyObject_CallMethod
|
PyObject *result = PyObject_CallMethod
|
||||||
(_runner, (char *)"setP3DFilename", (char *)"sOOi", p3d_filename.c_str(),
|
(_runner, (char *)"setP3DFilename", (char *)"sOOii", p3d_filename.c_str(),
|
||||||
token_list, arg_list, inst->get_instance_id());
|
token_list, arg_list, inst->get_instance_id(), _interactive_console);
|
||||||
Py_DECREF(token_list);
|
Py_DECREF(token_list);
|
||||||
Py_DECREF(arg_list);
|
Py_DECREF(arg_list);
|
||||||
|
|
||||||
@ -1552,20 +1598,3 @@ rt_thread_run() {
|
|||||||
RELEASE_LOCK(_commands_lock);
|
RELEASE_LOCK(_commands_lock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
|
||||||
// Function: run_p3dpython
|
|
||||||
// Description: This externally-visible function is the main entry
|
|
||||||
// point to this DLL, and it starts the whole thing
|
|
||||||
// running. Returns true on success, false on failure.
|
|
||||||
////////////////////////////////////////////////////////////////////
|
|
||||||
bool
|
|
||||||
run_p3dpython(const char *program_name, const char *archive_file,
|
|
||||||
FHandle input, FHandle output) {
|
|
||||||
P3DPythonRun::_global_ptr =
|
|
||||||
new P3DPythonRun(program_name, archive_file, input, output);
|
|
||||||
bool result = P3DPythonRun::_global_ptr->run_python();
|
|
||||||
delete P3DPythonRun::_global_ptr;
|
|
||||||
P3DPythonRun::_global_ptr = NULL;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
@ -67,12 +67,14 @@ using namespace std;
|
|||||||
class P3DPythonRun {
|
class P3DPythonRun {
|
||||||
public:
|
public:
|
||||||
P3DPythonRun(const char *program_name, const char *archive_file,
|
P3DPythonRun(const char *program_name, const char *archive_file,
|
||||||
FHandle input, FHandle output);
|
FHandle input_handle, FHandle output_handle,
|
||||||
|
FHandle error_handle, bool interactive_console);
|
||||||
~P3DPythonRun();
|
~P3DPythonRun();
|
||||||
|
|
||||||
bool run_python();
|
bool run_python();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void run_interactive_console();
|
||||||
void handle_command(TiXmlDocument *doc);
|
void handle_command(TiXmlDocument *doc);
|
||||||
void handle_pyobj_command(TiXmlElement *xcommand, bool needs_response,
|
void handle_pyobj_command(TiXmlElement *xcommand, bool needs_response,
|
||||||
int want_response_id);
|
int want_response_id);
|
||||||
@ -117,6 +119,7 @@ private:
|
|||||||
Filename _archive_file;
|
Filename _archive_file;
|
||||||
int _py_argc;
|
int _py_argc;
|
||||||
char **_py_argv;
|
char **_py_argv;
|
||||||
|
bool _interactive_console;
|
||||||
|
|
||||||
PyObject *_runner;
|
PyObject *_runner;
|
||||||
PyObject *_undefined_object_class;
|
PyObject *_undefined_object_class;
|
||||||
@ -152,6 +155,7 @@ private:
|
|||||||
|
|
||||||
HandleStream _pipe_read;
|
HandleStream _pipe_read;
|
||||||
HandleStream _pipe_write;
|
HandleStream _pipe_write;
|
||||||
|
HandleStream _error_log;
|
||||||
|
|
||||||
bool _read_thread_continue;
|
bool _read_thread_continue;
|
||||||
bool _program_continue;
|
bool _program_continue;
|
||||||
|
@ -706,20 +706,21 @@ start_p3dpython(P3DInstance *inst) {
|
|||||||
|
|
||||||
nout << "Search path is " << search_path << "\n";
|
nout << "Search path is " << search_path << "\n";
|
||||||
|
|
||||||
bool python_dev = false;
|
bool keep_pythonpath = false;
|
||||||
if (inst->_allow_python_dev) {
|
if (inst->_allow_python_dev) {
|
||||||
// If "allow_python_dev" is set in the instance's p3d_info.xml,
|
// If "allow_python_dev" is set in the instance's p3d_info.xml,
|
||||||
// *and* we have python_dev in the tokens, then we set python_dev
|
// *and* we have keep_pythonpath in the tokens, then we set
|
||||||
// true.
|
// keep_pythonpath true.
|
||||||
python_dev = (inst->get_fparams().lookup_token_int("python_dev") != 0);
|
keep_pythonpath = (inst->get_fparams().lookup_token_int("keep_pythonpath") != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string dyld_path = search_path;
|
||||||
string python_path = search_path;
|
string python_path = search_path;
|
||||||
string prc_path = search_path;
|
string prc_path = search_path;
|
||||||
|
|
||||||
if (python_dev) {
|
if (keep_pythonpath) {
|
||||||
// With python_dev true, we preserve the PYTHONPATH setting from
|
// With keep_pythonpath true, we preserve the PYTHONPATH setting
|
||||||
// the caller's environment; in fact, we put it in the front.
|
// from the caller's environment; in fact, we put it in the front.
|
||||||
// This allows the caller's on-disk Python files to shadow the
|
// This allows the caller's on-disk Python files to shadow the
|
||||||
// similar-named files in the p3d file, allowing easy iteration on
|
// similar-named files in the p3d file, allowing easy iteration on
|
||||||
// the code in the p3d file.
|
// the code in the p3d file.
|
||||||
@ -741,7 +742,7 @@ start_p3dpython(P3DInstance *inst) {
|
|||||||
prc_path += search_path;
|
prc_path += search_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
nout << "python_dev is true\n"
|
nout << "keep_pythonpath is true\n"
|
||||||
<< "PYTHONPATH set to: " << python_path << "\n"
|
<< "PYTHONPATH set to: " << python_path << "\n"
|
||||||
<< "PRC_PATH set to: " << prc_path << "\n";
|
<< "PRC_PATH set to: " << prc_path << "\n";
|
||||||
}
|
}
|
||||||
@ -766,6 +767,31 @@ start_p3dpython(P3DInstance *inst) {
|
|||||||
#else
|
#else
|
||||||
_p3dpython_dll += ".so";
|
_p3dpython_dll += ".so";
|
||||||
#endif
|
#endif
|
||||||
|
} else {
|
||||||
|
// We have a custom path to libp3dpython.dylib etc., for
|
||||||
|
// development.
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
// For some bizarre reason, Apple's dlopen() goes out of its way to
|
||||||
|
// ignore whatever full path you specify, and always searches for
|
||||||
|
// the file's basename along $DYLD_LIBRARY_PATH. Weird. To work
|
||||||
|
// around this and load the full path we're actually asking for, we
|
||||||
|
// have to ensure that our desired path appears first on
|
||||||
|
// $DYLD_LIBRARY_PATH.
|
||||||
|
|
||||||
|
// This may also inadvertently put other (incorrect) files first
|
||||||
|
// on the path, but presumably this won't cause too much trouble,
|
||||||
|
// since the user is in development mode anyway and maybe won't
|
||||||
|
// mind.
|
||||||
|
size_t slash = _p3dpython_dll.rfind('/');
|
||||||
|
if (slash != string::npos) {
|
||||||
|
string dirname = _p3dpython_dll.substr(0, slash);
|
||||||
|
cerr << "dirname is " << dirname << "\n";
|
||||||
|
|
||||||
|
dyld_path = dirname + ":" + dyld_path;
|
||||||
|
cerr << "dyld_path is " << dyld_path << "\n";
|
||||||
|
}
|
||||||
|
#endif // __APPLE__
|
||||||
}
|
}
|
||||||
|
|
||||||
// Populate the new process' environment.
|
// Populate the new process' environment.
|
||||||
@ -800,7 +826,7 @@ start_p3dpython(P3DInstance *inst) {
|
|||||||
_env += '\0';
|
_env += '\0';
|
||||||
|
|
||||||
_env += "DYLD_LIBRARY_PATH=";
|
_env += "DYLD_LIBRARY_PATH=";
|
||||||
_env += search_path;
|
_env += dyld_path;
|
||||||
_env += '\0';
|
_env += '\0';
|
||||||
|
|
||||||
_env += "PYTHONPATH=";
|
_env += "PYTHONPATH=";
|
||||||
@ -835,6 +861,23 @@ start_p3dpython(P3DInstance *inst) {
|
|||||||
_env += '\0';
|
_env += '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for a few tokens that have special meaning at this level.
|
||||||
|
bool console_output = (inst->get_fparams().lookup_token_int("console_output") != 0);
|
||||||
|
bool one_process = (inst->get_fparams().lookup_token_int("one_process") != 0);
|
||||||
|
_interactive_console = (inst->get_fparams().lookup_token_int("interactive_console") != 0);
|
||||||
|
|
||||||
|
if (!inst->_allow_python_dev) {
|
||||||
|
// interactive_console is only allowed to be enabled if
|
||||||
|
// allow_python_dev is also set within the p3d file.
|
||||||
|
_interactive_console = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_interactive_console) {
|
||||||
|
// If we have interactive_console set, it follows we also need
|
||||||
|
// console_output.
|
||||||
|
console_output = true;
|
||||||
|
}
|
||||||
|
|
||||||
// Get the log filename from the p3d_info.xml file.
|
// Get the log filename from the p3d_info.xml file.
|
||||||
string log_basename = inst->_log_basename;
|
string log_basename = inst->_log_basename;
|
||||||
|
|
||||||
@ -843,9 +886,6 @@ start_p3dpython(P3DInstance *inst) {
|
|||||||
log_basename = inst->get_fparams().lookup_token("log_basename");
|
log_basename = inst->get_fparams().lookup_token("log_basename");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool console_output = (inst->get_fparams().lookup_token_int("console_output") != 0);
|
|
||||||
bool one_process = (inst->get_fparams().lookup_token_int("one_process") != 0);
|
|
||||||
|
|
||||||
#ifdef P3D_PLUGIN_LOG_BASENAME3
|
#ifdef P3D_PLUGIN_LOG_BASENAME3
|
||||||
if (log_basename.empty()) {
|
if (log_basename.empty()) {
|
||||||
// No log_basename specified for the app; use the compiled-in
|
// No log_basename specified for the app; use the compiled-in
|
||||||
@ -899,8 +939,8 @@ start_p3dpython(P3DInstance *inst) {
|
|||||||
SetHandleInformation(r_from, HANDLE_FLAG_INHERIT, 0);
|
SetHandleInformation(r_from, HANDLE_FLAG_INHERIT, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
_output = w_from;
|
_output_handle = w_from;
|
||||||
_input = r_to;
|
_input_handle = r_to;
|
||||||
_pipe_read.open_read(r_from);
|
_pipe_read.open_read(r_from);
|
||||||
_pipe_write.open_write(w_to);
|
_pipe_write.open_write(w_to);
|
||||||
|
|
||||||
@ -915,23 +955,57 @@ start_p3dpython(P3DInstance *inst) {
|
|||||||
perror("failed to create pipe");
|
perror("failed to create pipe");
|
||||||
}
|
}
|
||||||
|
|
||||||
_input = to_fd[0];
|
_input_handle = to_fd[0];
|
||||||
_output = from_fd[1];
|
_output_handle = from_fd[1];
|
||||||
_pipe_read.open_read(from_fd[0]);
|
_pipe_read.open_read(from_fd[0]);
|
||||||
_pipe_write.open_write(to_fd[1]);
|
_pipe_write.open_write(to_fd[1]);
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
|
|
||||||
// Get the filename to the Panda3D multifile. We need to pass this
|
// Create the error stream for log output. This means opening the
|
||||||
|
// logfile, if we have one, or keeping the standard error if we
|
||||||
|
// don't.
|
||||||
|
_got_error_handle = false;
|
||||||
|
#ifdef _WIN32
|
||||||
|
_error_handle = GetStdHandle(STD_ERROR_HANDLE);
|
||||||
|
if (!_log_pathname.empty()) {
|
||||||
|
HANDLE handle = CreateFile
|
||||||
|
(_log_pathname.c_str(), GENERIC_WRITE,
|
||||||
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||||
|
NULL, CREATE_ALWAYS, 0, NULL);
|
||||||
|
if (handle != INVALID_HANDLE_VALUE) {
|
||||||
|
_error_handle = handle;
|
||||||
|
SetHandleInformation(error_handle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
|
||||||
|
_got_error_handle = true;
|
||||||
|
} else {
|
||||||
|
nout << "Unable to open " << _log_pathname << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else // _WIN32
|
||||||
|
_error_handle = STDERR_FILENO;
|
||||||
|
if (!_log_pathname.empty()) {
|
||||||
|
int logfile_fd = open(_log_pathname.c_str(),
|
||||||
|
O_WRONLY | O_CREAT | O_TRUNC, 0666);
|
||||||
|
if (logfile_fd < 0) {
|
||||||
|
nout << "Unable to open " << _log_pathname << "\n";
|
||||||
|
} else {
|
||||||
|
_error_handle = logfile_fd;
|
||||||
|
_got_error_handle = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // _WIN32
|
||||||
|
|
||||||
|
// Get the filename of the Panda3D multifile. We need to pass this
|
||||||
// to p3dpython.
|
// to p3dpython.
|
||||||
_mf_filename = inst->_panda3d->get_archive_file_pathname();
|
_mf_filename = inst->_panda3d->get_archive_file_pathname();
|
||||||
|
|
||||||
|
nout << "Attempting to start python from " << _p3dpython_exe
|
||||||
|
<< " and " << _p3dpython_dll << "\n";
|
||||||
|
|
||||||
bool started_p3dpython;
|
bool started_p3dpython;
|
||||||
if (one_process) {
|
if (one_process) {
|
||||||
nout << "one_process is set; running Python within parent process.\n";
|
nout << "one_process is set; running Python within parent process.\n";
|
||||||
started_p3dpython = false;
|
started_p3dpython = false;
|
||||||
} else {
|
} else {
|
||||||
nout << "Attempting to start python from " << _p3dpython_exe
|
|
||||||
<< " and " << _p3dpython_dll << "\n";
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
_p3dpython_handle = win_create_process();
|
_p3dpython_handle = win_create_process();
|
||||||
started_p3dpython = (_p3dpython_handle != INVALID_HANDLE_VALUE);
|
started_p3dpython = (_p3dpython_handle != INVALID_HANDLE_VALUE);
|
||||||
@ -1130,42 +1204,19 @@ rt_terminate() {
|
|||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
HANDLE P3DSession::
|
HANDLE P3DSession::
|
||||||
win_create_process() {
|
win_create_process() {
|
||||||
HANDLE error_handle = GetStdHandle(STD_ERROR_HANDLE);
|
|
||||||
bool got_log_pathname = !_log_pathname.empty();
|
|
||||||
if (got_log_pathname) {
|
|
||||||
// Open the named file for output and redirect the child's stderr
|
|
||||||
// into it.
|
|
||||||
HANDLE handle = CreateFile
|
|
||||||
(_log_pathname.c_str(), GENERIC_WRITE,
|
|
||||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
||||||
NULL, CREATE_ALWAYS, 0, NULL);
|
|
||||||
if (handle != INVALID_HANDLE_VALUE) {
|
|
||||||
error_handle = handle;
|
|
||||||
SetHandleInformation(error_handle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
|
|
||||||
} else {
|
|
||||||
nout << "Unable to open " << _log_pathname << "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure we see an error dialog if there is a missing DLL.
|
// Make sure we see an error dialog if there is a missing DLL.
|
||||||
SetErrorMode(0);
|
SetErrorMode(0);
|
||||||
|
|
||||||
// Pass the appropriate ends of the bi-directional pipe as the
|
|
||||||
// standard input and standard output of the child process.
|
|
||||||
STARTUPINFO startup_info;
|
STARTUPINFO startup_info;
|
||||||
ZeroMemory(&startup_info, sizeof(STARTUPINFO));
|
ZeroMemory(&startup_info, sizeof(STARTUPINFO));
|
||||||
startup_info.cb = sizeof(startup_info);
|
startup_info.cb = sizeof(startup_info);
|
||||||
startup_info.hStdError = error_handle;
|
|
||||||
startup_info.hStdOutput = _output;
|
|
||||||
startup_info.hStdInput = _input;
|
|
||||||
startup_info.dwFlags |= STARTF_USESTDHANDLES;
|
|
||||||
|
|
||||||
// Make sure the "python" console window is hidden.
|
// Make sure the "python" console window is hidden.
|
||||||
startup_info.wShowWindow = SW_HIDE;
|
startup_info.wShowWindow = SW_HIDE;
|
||||||
startup_info.dwFlags |= STARTF_USESHOWWINDOW;
|
startup_info.dwFlags |= STARTF_USESHOWWINDOW;
|
||||||
|
|
||||||
// If the start directory is empty, meaning not to change the
|
// If _use_start_dir is false, meaning not to change the current
|
||||||
// current directory, then pass NULL in to CreateProcess().
|
// directory, then pass NULL in to CreateProcess().
|
||||||
const char *start_dir_cstr = NULL;
|
const char *start_dir_cstr = NULL;
|
||||||
if (_use_start_dir) {
|
if (_use_start_dir) {
|
||||||
start_dir_cstr = _start_dir.c_str();
|
start_dir_cstr = _start_dir.c_str();
|
||||||
@ -1175,7 +1226,9 @@ win_create_process() {
|
|||||||
// command-line arguments.
|
// command-line arguments.
|
||||||
ostringstream stream;
|
ostringstream stream;
|
||||||
stream << "\"" << _p3dpython_exe << "\" \"" << _p3dpython_dll
|
stream << "\"" << _p3dpython_exe << "\" \"" << _p3dpython_dll
|
||||||
<< "\" \"" << _mf_filename << "\"";
|
<< "\" \"" << _mf_filename << "\" \"" << _input_handle
|
||||||
|
<< "\" \"" << _output_handle << "\" \"" << _error_handle
|
||||||
|
<< "\" \"" << _interactive_console << "\"";
|
||||||
|
|
||||||
// I'm not sure why CreateProcess wants a non-const char pointer for
|
// I'm not sure why CreateProcess wants a non-const char pointer for
|
||||||
// its command-line string, but I'm not taking chances. It gets a
|
// its command-line string, but I'm not taking chances. It gets a
|
||||||
@ -1194,10 +1247,10 @@ win_create_process() {
|
|||||||
delete[] command_line;
|
delete[] command_line;
|
||||||
|
|
||||||
// Close the pipe handles that are now owned by the child.
|
// Close the pipe handles that are now owned by the child.
|
||||||
CloseHandle(_output);
|
CloseHandle(_output_handle);
|
||||||
CloseHandle(_input);
|
CloseHandle(_input_handle);
|
||||||
if (got_log_pathname) {
|
if (_got_error_handle) {
|
||||||
CloseHandle(error_handle);
|
CloseHandle(_error_handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!started_program) {
|
if (!started_program) {
|
||||||
@ -1227,8 +1280,8 @@ win_create_process() {
|
|||||||
// process's standard output and standard input,
|
// process's standard output and standard input,
|
||||||
// respectively.
|
// respectively.
|
||||||
//
|
//
|
||||||
// Returns the handle to the created process on success,
|
// Returns the pid of the created process on success, or
|
||||||
// or INVALID_HANDLE_VALUE on falure.
|
// -1 on falure.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
int P3DSession::
|
int P3DSession::
|
||||||
posix_create_process() {
|
posix_create_process() {
|
||||||
@ -1241,24 +1294,8 @@ posix_create_process() {
|
|||||||
|
|
||||||
if (child == 0) {
|
if (child == 0) {
|
||||||
// Here we are in the child process.
|
// Here we are in the child process.
|
||||||
bool got_log_pathname = !_log_pathname.empty();
|
|
||||||
if (got_log_pathname) {
|
|
||||||
// Open the named file for output and redirect the child's stderr
|
|
||||||
// into it.
|
|
||||||
int logfile_fd = open(_log_pathname.c_str(),
|
|
||||||
O_WRONLY | O_CREAT | O_TRUNC, 0666);
|
|
||||||
if (logfile_fd < 0) {
|
|
||||||
nout << "Unable to open " << _log_pathname << "\n";
|
|
||||||
} else {
|
|
||||||
dup2(logfile_fd, STDERR_FILENO);
|
|
||||||
close(logfile_fd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the appropriate ends of the bi-directional pipe as the
|
// Close the parent's ends of the pipes.
|
||||||
// standard input and standard output of the child process.
|
|
||||||
dup2(_input, STDIN_FILENO);
|
|
||||||
dup2(_output, STDOUT_FILENO);
|
|
||||||
_pipe_read.close();
|
_pipe_read.close();
|
||||||
_pipe_write.close();
|
_pipe_write.close();
|
||||||
|
|
||||||
@ -1281,15 +1318,33 @@ posix_create_process() {
|
|||||||
}
|
}
|
||||||
ptrs.push_back((char *)NULL);
|
ptrs.push_back((char *)NULL);
|
||||||
|
|
||||||
|
stringstream input_handle_stream;
|
||||||
|
input_handle_stream << _input_handle;
|
||||||
|
string input_handle_str = input_handle_stream.str();
|
||||||
|
|
||||||
|
stringstream output_handle_stream;
|
||||||
|
output_handle_stream << _output_handle;
|
||||||
|
string output_handle_str = output_handle_stream.str();
|
||||||
|
|
||||||
|
stringstream error_handle_stream;
|
||||||
|
error_handle_stream << _error_handle;
|
||||||
|
string error_handle_str = error_handle_stream.str();
|
||||||
|
|
||||||
execle(_p3dpython_exe.c_str(),
|
execle(_p3dpython_exe.c_str(),
|
||||||
_p3dpython_exe.c_str(), _p3dpython_dll.c_str(), _mf_filename.c_str(), (char *)0,
|
_p3dpython_exe.c_str(), _p3dpython_dll.c_str(),
|
||||||
&ptrs[0]);
|
_mf_filename.c_str(), input_handle_str.c_str(),
|
||||||
|
output_handle_str.c_str(), error_handle_str.c_str(),
|
||||||
|
_interactive_console ? "1" : "0", (char *)0, &ptrs[0]);
|
||||||
nout << "Failed to exec " << _p3dpython_exe << "\n";
|
nout << "Failed to exec " << _p3dpython_exe << "\n";
|
||||||
_exit(1);
|
_exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
close(_input);
|
// Close the handles that are now owned by the child.
|
||||||
close(_output);
|
close(_input_handle);
|
||||||
|
close(_output_handle);
|
||||||
|
if (_got_error_handle) {
|
||||||
|
close(_error_handle);
|
||||||
|
}
|
||||||
|
|
||||||
return child;
|
return child;
|
||||||
}
|
}
|
||||||
@ -1307,8 +1362,12 @@ p3dpython_thread_run() {
|
|||||||
nout << "running p3dpython_thread_run()\n";
|
nout << "running p3dpython_thread_run()\n";
|
||||||
|
|
||||||
// Set the environment. Hopefully this won't be too destructive to
|
// Set the environment. Hopefully this won't be too destructive to
|
||||||
// the current process, and hopefully these changes will be read
|
// the current process.
|
||||||
// properly by Python.
|
|
||||||
|
// Note that on OSX at least, changing the DYLD_LIBRARY_PATH after
|
||||||
|
// the process has started has no effect (and furthermore you can't
|
||||||
|
// specify a full path to dlopen() calls), so this whole one-process
|
||||||
|
// approach is fatally flawed on OSX.
|
||||||
size_t p = 0;
|
size_t p = 0;
|
||||||
size_t zero = _env.find('\0', p);
|
size_t zero = _env.find('\0', p);
|
||||||
while (zero != string::npos) {
|
while (zero != string::npos) {
|
||||||
@ -1340,7 +1399,7 @@ p3dpython_thread_run() {
|
|||||||
|
|
||||||
#else // _WIN32
|
#else // _WIN32
|
||||||
// Posix case.
|
// Posix case.
|
||||||
void *module = dlopen(_p3dpython_dll.c_str(), RTLD_GLOBAL);
|
void *module = dlopen(_p3dpython_dll.c_str(), RTLD_LOCAL);
|
||||||
if (module == NULL) {
|
if (module == NULL) {
|
||||||
// Couldn't load the .so.
|
// Couldn't load the .so.
|
||||||
nout << "Couldn't load " << _p3dpython_dll << "\n";
|
nout << "Couldn't load " << _p3dpython_dll << "\n";
|
||||||
@ -1358,7 +1417,8 @@ p3dpython_thread_run() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!run_p3dpython(_p3dpython_dll.c_str(), _mf_filename.c_str(),
|
if (!run_p3dpython(_p3dpython_dll.c_str(), _mf_filename.c_str(),
|
||||||
_input, _output)) {
|
_input_handle, _output_handle, _error_handle,
|
||||||
|
_interactive_console)) {
|
||||||
nout << "Failure on startup.\n";
|
nout << "Failure on startup.\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -100,7 +100,9 @@ private:
|
|||||||
string _p3dpython_dll;
|
string _p3dpython_dll;
|
||||||
string _mf_filename;
|
string _mf_filename;
|
||||||
string _env;
|
string _env;
|
||||||
FHandle _input, _output;
|
FHandle _input_handle, _output_handle, _error_handle;
|
||||||
|
bool _got_error_handle;
|
||||||
|
bool _interactive_console;
|
||||||
|
|
||||||
typedef map<int, P3DInstance *> Instances;
|
typedef map<int, P3DInstance *> Instances;
|
||||||
Instances _instances;
|
Instances _instances;
|
||||||
|
35
direct/src/plugin/run_p3dpython.cxx
Normal file
35
direct/src/plugin/run_p3dpython.cxx
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Filename: run_p3dpython.cxx
|
||||||
|
// Created by: drose (29Aug09)
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// 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."
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "run_p3dpython.h"
|
||||||
|
#include "p3dPythonRun.h"
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: run_p3dpython
|
||||||
|
// Description: This externally-visible function is the main entry
|
||||||
|
// point to this DLL, and it starts the whole thing
|
||||||
|
// running. Returns true on success, false on failure.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
bool
|
||||||
|
run_p3dpython(const char *program_name, const char *archive_file,
|
||||||
|
FHandle input_handle, FHandle output_handle,
|
||||||
|
FHandle error_handle, bool interactive_console) {
|
||||||
|
P3DPythonRun::_global_ptr =
|
||||||
|
new P3DPythonRun(program_name, archive_file, input_handle, output_handle,
|
||||||
|
error_handle, interactive_console);
|
||||||
|
bool result = P3DPythonRun::_global_ptr->run_python();
|
||||||
|
delete P3DPythonRun::_global_ptr;
|
||||||
|
P3DPythonRun::_global_ptr = NULL;
|
||||||
|
return result;
|
||||||
|
}
|
@ -26,12 +26,15 @@
|
|||||||
#define EXPCL_P3DPYTHON
|
#define EXPCL_P3DPYTHON
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef bool run_p3dpython_func(const char *program_name, const char *archive_file,
|
typedef bool
|
||||||
FHandle input, FHandle output);
|
run_p3dpython_func(const char *program_name, const char *archive_file,
|
||||||
|
FHandle input_handle, FHandle output_handle,
|
||||||
|
FHandle error_handle, bool interactive_console);
|
||||||
|
|
||||||
EXPCL_P3DPYTHON extern "C" bool
|
EXPCL_P3DPYTHON extern "C" bool
|
||||||
run_p3dpython(const char *program_name, const char *archive_file,
|
run_p3dpython(const char *program_name, const char *archive_file,
|
||||||
FHandle input, FHandle output);
|
FHandle input_handle, FHandle output_handle,
|
||||||
|
FHandle error_handle, bool interactive_console);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -25,6 +25,8 @@
|
|||||||
#include <sstream>
|
#include <sstream>
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
#else
|
||||||
|
#include <signal.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef HAVE_GETOPT
|
#ifndef HAVE_GETOPT
|
||||||
@ -64,7 +66,7 @@ run(int argc, char *argv[]) {
|
|||||||
// We prefix a "+" sign to tell gnu getopt not to parse options
|
// We prefix a "+" sign to tell gnu getopt not to parse options
|
||||||
// following the first not-option parameter. (These will be passed
|
// following the first not-option parameter. (These will be passed
|
||||||
// into the sub-process.)
|
// into the sub-process.)
|
||||||
const char *optstr = "+mu:p:fw:t:s:o:l:Dh";
|
const char *optstr = "+mu:p:fw:t:s:o:l:ih";
|
||||||
|
|
||||||
bool allow_multiple = false;
|
bool allow_multiple = false;
|
||||||
string download_url = PANDA_PACKAGE_HOST_URL;
|
string download_url = PANDA_PACKAGE_HOST_URL;
|
||||||
@ -136,12 +138,26 @@ run(int argc, char *argv[]) {
|
|||||||
_log_basename = "panda3d";
|
_log_basename = "panda3d";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'D':
|
case 'i':
|
||||||
{
|
{
|
||||||
P3D_token token;
|
P3D_token token;
|
||||||
token._keyword = "python_dev";
|
token._keyword = "keep_pythonpath";
|
||||||
token._value = "1";
|
token._value = "1";
|
||||||
_tokens.push_back(token);
|
_tokens.push_back(token);
|
||||||
|
token._keyword = "interactive_console";
|
||||||
|
token._value = "1";
|
||||||
|
_tokens.push_back(token);
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
// We should also ignore SIGINT in this case, so that a
|
||||||
|
// control-C operation will be delivered to the subordinate
|
||||||
|
// Python process and return to a command shell, and won't
|
||||||
|
// just kill the panda3d process.
|
||||||
|
struct sigaction ignore;
|
||||||
|
memset(&ignore, 0, sizeof(ignore));
|
||||||
|
ignore.sa_handler = SIG_IGN;
|
||||||
|
sigaction(SIGINT, &ignore, NULL);
|
||||||
|
#endif // _WIN32
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -789,10 +805,11 @@ usage() {
|
|||||||
<< " if a new version is available. Normally, this is done only\n"
|
<< " if a new version is available. Normally, this is done only\n"
|
||||||
<< " if contents.xml cannot be read.\n\n"
|
<< " if contents.xml cannot be read.\n\n"
|
||||||
|
|
||||||
<< " -D\n"
|
<< " -i\n"
|
||||||
<< " Request python_dev mode. This requires that the application was\n"
|
<< " Runs the application interactively. This requires that the application\n"
|
||||||
<< " also built with -D on the packp3d command line. If so, this will\n"
|
<< " was built with -D on the packp3d command line. If so, this option will\n"
|
||||||
<< " preserve the PYTHONPATH environment variable from the user's\n"
|
<< " create an interactive Python prompt after the application has loaded.\n"
|
||||||
|
<< " It will also the PYTHONPATH environment variable from the user's\n"
|
||||||
<< " environment, allowing Python files on disk to shadow the same-named\n"
|
<< " environment, allowing Python files on disk to shadow the same-named\n"
|
||||||
<< " Python files within the p3d file, for rapid iteration on the Python\n"
|
<< " Python files within the p3d file, for rapid iteration on the Python\n"
|
||||||
<< " code.\n\n"
|
<< " code.\n\n"
|
||||||
|
@ -832,8 +832,14 @@ class ShowBase(DirectObject.DirectObject):
|
|||||||
self.win.setClearStencilActive(oldClearStencilActive)
|
self.win.setClearStencilActive(oldClearStencilActive)
|
||||||
self.win.setClearStencil(oldClearStencil)
|
self.win.setClearStencil(oldClearStencil)
|
||||||
|
|
||||||
self.setFrameRateMeter(self.config.GetBool(
|
flag = self.config.GetBool('show-frame-rate-meter', False)
|
||||||
'show-frame-rate-meter', 0))
|
if self.appRunner is not None and self.appRunner.allowPythonDev:
|
||||||
|
# In an allow_python_dev p3d application, we always
|
||||||
|
# start up with the frame rate meter enabled, to
|
||||||
|
# provide a visual reminder that this flag has been
|
||||||
|
# set.
|
||||||
|
flag = True
|
||||||
|
self.setFrameRateMeter(flag)
|
||||||
return success
|
return success
|
||||||
|
|
||||||
def setSleep(self, amount):
|
def setSleep(self, amount):
|
||||||
@ -2477,13 +2483,12 @@ class ShowBase(DirectObject.DirectObject):
|
|||||||
self.startDirect(fWantDirect = fDirect, fWantTk = fTk, fWantWx = fWx)
|
self.startDirect(fWantDirect = fDirect, fWantTk = fTk, fWantWx = fWx)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
# This method only does anything when self.appRunner is None,
|
# This method runs the TaskManager when self.appRunner is
|
||||||
# which is to say, when we are not running from within a p3d
|
# None, which is to say, when we are not running from within a
|
||||||
# file. When we *are* within a p3d file, the Panda runtime
|
# p3d file. When we *are* within a p3d file, the Panda
|
||||||
# has to be responsible for running the main loop, so we can't
|
# runtime has to be responsible for running the main loop, so
|
||||||
# allow the application to do it. This is a minor hack, but
|
# we can't allow the application to do it.
|
||||||
# should work for 99% of the cases.
|
if self.appRunner is None or self.appRunner.dummy or self.appRunner.interactiveConsole:
|
||||||
if self.appRunner is None or self.appRunner.dummy:
|
|
||||||
self.taskMgr.run()
|
self.taskMgr.run()
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user