diff --git a/.travis.yml b/.travis.yml index 64cc557d5b..2c4efe8a65 100644 --- a/.travis.yml +++ b/.travis.yml @@ -49,7 +49,7 @@ script: notifications: irc: channels: - - "chat.freenode.net#panda3d" + - secure: "jfwHT9RHAVOGRGTMY8TpYKJI6rq8nFoIj41Y0soZdJQNWtSSFEK9AyzZeMY+2dHga7cR/X+/0NWZ2ehhedTnd9FvlzOnMWWC3K0I/b3XWbEdVEqIZnggFkKGqs82Gy3omguRC63yWupeJCcSCckIhoWbLzWy6xV8lF5WC80iXi8=" on_success: change on_failure: always use_notice: true diff --git a/README.md b/README.md index 43feb9b19c..a9de69e51b 100644 --- a/README.md +++ b/README.md @@ -55,8 +55,8 @@ depending on whether you are on a 32-bit or 64-bit system, or you can [click here](https://github.com/rdb/panda3d-thirdparty) for instructions on building them from source. -http://rdb.name/thirdparty-vc14-x64.7z -http://rdb.name/thirdparty-vc14.7z +https://www.panda3d.org/download/panda3d-1.10.0/panda3d-1.10.0-tools-win64.zip +https://www.panda3d.org/download/panda3d-1.10.0/panda3d-1.10.0-tools-win32.zip After acquiring these dependencies, you may simply build Panda3D from the command prompt using the following command. (Change `14.1` to `14` if you are diff --git a/contrib/src/panda3dtoolsgui/pandaIcon.ico b/contrib/src/panda3dtoolsgui/pandaIcon.ico index cd6f45759f..3a2c1c33d4 100644 Binary files a/contrib/src/panda3dtoolsgui/pandaIcon.ico and b/contrib/src/panda3dtoolsgui/pandaIcon.ico differ diff --git a/direct/src/actor/Actor.py b/direct/src/actor/Actor.py index b9ba299aaa..b7d48f2d87 100644 --- a/direct/src/actor/Actor.py +++ b/direct/src/actor/Actor.py @@ -653,15 +653,11 @@ class Actor(DirectObject, NodePath): """ # make sure we don't call this twice in a row # and pollute the the switches dictionary -## sortedKeys = list(self.switches.keys()) -## sortedKeys.sort() child = self.__LODNode.find(str(lodName)) index = self.__LODNode.node().findChild(child.node()) self.__LODNode.node().forceSwitch(index) def printLOD(self): -## sortedKeys = list(self.switches.keys()) -## sortedKeys.sort() sortedKeys = self.__sortedLODNames for eachLod in sortedKeys: print("python switches for %s: in: %d, out %d" % (eachLod, @@ -679,12 +675,6 @@ class Actor(DirectObject, NodePath): """ Restore all switch distance info (usually after a useLOD call)""" self.__LODNode.node().clearForceSwitch() -## sortedKeys = list(self.switches.keys()) -## sortedKeys.sort() -## for eachLod in sortedKeys: -## index = sortedKeys.index(eachLod) -## self.__LODNode.node().setSwitch(index, self.switches[eachLod][0], -## self.switches[eachLod][1]) def addLOD(self, lodName, inDist=0, outDist=0, center=None): """addLOD(self, string) @@ -706,8 +696,6 @@ class Actor(DirectObject, NodePath): # save the switch distance info self.switches[lodName] = [inDist, outDist] # add the switch distance info -## sortedKeys = list(self.switches.keys()) -## sortedKeys.sort() self.__LODNode.node().setSwitch(self.getLODIndex(lodName), inDist, outDist) def getLODIndex(self, lodName): diff --git a/direct/src/dcparser/dcArrayParameter.cxx b/direct/src/dcparser/dcArrayParameter.cxx index 2f2ea69932..c2c73635a2 100644 --- a/direct/src/dcparser/dcArrayParameter.cxx +++ b/direct/src/dcparser/dcArrayParameter.cxx @@ -262,6 +262,38 @@ pack_string(DCPackData &pack_data, const string &value, } } +/** + * Packs the indicated numeric or string value into the stream. + */ +void DCArrayParameter:: +pack_blob(DCPackData &pack_data, const vector_uchar &value, + bool &pack_error, bool &range_error) const { + // We can only pack a string if the array element type is char or int8. + DCSimpleParameter *simple_type = _element_type->as_simple_parameter(); + if (simple_type == nullptr) { + pack_error = true; + return; + } + + size_t blob_size = value.size(); + + switch (simple_type->get_type()) { + case ST_char: + case ST_uint8: + case ST_int8: + _array_size_range.validate(blob_size, range_error); + if (_num_length_bytes != 0) { + nassertv(_num_length_bytes == 2); + do_pack_uint16(pack_data.get_write_pointer(2), blob_size); + } + pack_data.append_data((const char *)value.data(), blob_size); + break; + + default: + pack_error = true; + } +} + /** * Packs the arrayParameter's specified default value (or a sensible default * if no value is specified) into the stream. Returns true if the default @@ -339,6 +371,46 @@ unpack_string(const char *data, size_t length, size_t &p, string &value, } } +/** + * Unpacks the current numeric or string value from the stream. + */ +void DCArrayParameter:: +unpack_blob(const char *data, size_t length, size_t &p, vector_uchar &value, + bool &pack_error, bool &range_error) const { + // We can only unpack a string if the array element type is char or int8. + DCSimpleParameter *simple_type = _element_type->as_simple_parameter(); + if (simple_type == nullptr) { + pack_error = true; + return; + } + + size_t blob_size; + + switch (simple_type->get_type()) { + case ST_char: + case ST_uint8: + case ST_int8: + if (_num_length_bytes != 0) { + blob_size = do_unpack_uint16(data + p); + p += 2; + } else { + nassertv(_array_size >= 0); + blob_size = _array_size; + } + if (p + blob_size > length) { + pack_error = true; + return; + } + value = vector_uchar((const unsigned char *)data + p, + (const unsigned char *)data + p + blob_size); + p += blob_size; + break; + + default: + pack_error = true; + } +} + /** * Returns true if the other interface is bitwise the same as this one--that * is, a uint32 only matches a uint32, etc. Names of components, and range diff --git a/direct/src/dcparser/dcArrayParameter.h b/direct/src/dcparser/dcArrayParameter.h index 97fb32bdf0..d81707a9e6 100644 --- a/direct/src/dcparser/dcArrayParameter.h +++ b/direct/src/dcparser/dcArrayParameter.h @@ -51,9 +51,13 @@ public: virtual void generate_hash(HashGenerator &hashgen) const; virtual void pack_string(DCPackData &pack_data, const std::string &value, bool &pack_error, bool &range_error) const; + virtual void pack_blob(DCPackData &pack_data, const vector_uchar &value, + bool &pack_error, bool &range_error) const; virtual bool pack_default_value(DCPackData &pack_data, bool &pack_error) const; virtual void unpack_string(const char *data, size_t length, size_t &p, std::string &value, bool &pack_error, bool &range_error) const; + virtual void unpack_blob(const char *data, size_t length, size_t &p, + vector_uchar &value, bool &pack_error, bool &range_error) const; protected: virtual bool do_check_match(const DCPackerInterface *other) const; diff --git a/direct/src/dcparser/dcAtomicField.cxx b/direct/src/dcparser/dcAtomicField.cxx index 3efebfa2d6..eb5607e55c 100644 --- a/direct/src/dcparser/dcAtomicField.cxx +++ b/direct/src/dcparser/dcAtomicField.cxx @@ -88,11 +88,11 @@ get_element(int n) const { * If the element is an array-type element, the returned value will include * the two-byte length preceding the array data. * - * This is deprecated; use get_element() instead. + * @deprecated use get_element() instead. */ -string DCAtomicField:: +vector_uchar DCAtomicField:: get_element_default(int n) const { - nassertr(n >= 0 && n < (int)_elements.size(), string()); + nassertr(n >= 0 && n < (int)_elements.size(), vector_uchar()); return _elements[n]->get_default_value(); } @@ -100,7 +100,7 @@ get_element_default(int n) const { * Returns true if the nth element of the field has a default value specified, * false otherwise. * - * This is deprecated; use get_element() instead. + * @deprecated use get_element() instead. */ bool DCAtomicField:: has_element_default(int n) const { @@ -113,7 +113,7 @@ has_element_default(int n) const { * for documentary purposes; it does not generally affect operation. If a * name is not specified, this will be the empty string. * - * This method is deprecated; use get_element()->get_name() instead. + * @deprecated use get_element()->get_name() instead. */ string DCAtomicField:: get_element_name(int n) const { diff --git a/direct/src/dcparser/dcAtomicField.h b/direct/src/dcparser/dcAtomicField.h index 9eb92e2527..418b1b768f 100644 --- a/direct/src/dcparser/dcAtomicField.h +++ b/direct/src/dcparser/dcAtomicField.h @@ -40,7 +40,7 @@ PUBLISHED: DCParameter *get_element(int n) const; // These five methods are deprecated and will be removed soon. - std::string get_element_default(int n) const; + vector_uchar get_element_default(int n) const; bool has_element_default(int n) const; std::string get_element_name(int n) const; DCSubatomicType get_element_type(int n) const; diff --git a/direct/src/dcparser/dcClass.cxx b/direct/src/dcparser/dcClass.cxx index 374ed47d3c..9487c5e323 100644 --- a/direct/src/dcparser/dcClass.cxx +++ b/direct/src/dcparser/dcClass.cxx @@ -587,7 +587,7 @@ receive_update_other(PyObject *distobj, DatagramIterator &di) const { */ void DCClass:: direct_update(PyObject *distobj, const string &field_name, - const string &value_blob) { + const vector_uchar &value_blob) { DCField *field = get_field_by_name(field_name); nassertv_always(field != nullptr); @@ -606,7 +606,14 @@ direct_update(PyObject *distobj, const string &field_name, void DCClass:: direct_update(PyObject *distobj, const string &field_name, const Datagram &datagram) { - direct_update(distobj, field_name, datagram.get_message()); + DCField *field = get_field_by_name(field_name); + nassertv_always(field != nullptr); + + DCPacker packer; + packer.set_unpack_data((const char *)datagram.get_data(), datagram.get_length(), false); + packer.begin_unpack(field); + field->receive_update(packer, distobj); + packer.end_unpack(); } #endif // HAVE_PYTHON diff --git a/direct/src/dcparser/dcClass.h b/direct/src/dcparser/dcClass.h index cd69716470..94feae1c19 100644 --- a/direct/src/dcparser/dcClass.h +++ b/direct/src/dcparser/dcClass.h @@ -95,7 +95,7 @@ PUBLISHED: void receive_update_other(PyObject *distobj, DatagramIterator &di) const; void direct_update(PyObject *distobj, const std::string &field_name, - const std::string &value_blob); + const vector_uchar &value_blob); void direct_update(PyObject *distobj, const std::string &field_name, const Datagram &datagram); bool pack_required_field(Datagram &datagram, PyObject *distobj, diff --git a/direct/src/dcparser/dcField.I b/direct/src/dcparser/dcField.I index aabf5cf114..e6aa4a4672 100644 --- a/direct/src/dcparser/dcField.I +++ b/direct/src/dcparser/dcField.I @@ -42,7 +42,7 @@ has_default_value() const { * explicitly set (e.g. has_default_value() returns true), returns that * value; otherwise, returns an implicit default for the field. */ -INLINE const std::string &DCField:: +INLINE const vector_uchar &DCField:: get_default_value() const { if (_default_value_stale) { ((DCField *)this)->refresh_default_value(); @@ -172,8 +172,8 @@ set_class(DCClass *dclass) { * Establishes a default value for this field. */ INLINE void DCField:: -set_default_value(const std::string &default_value) { - _default_value = default_value; +set_default_value(vector_uchar default_value) { + _default_value = std::move(default_value); _has_default_value = true; _default_value_stale = false; } diff --git a/direct/src/dcparser/dcField.cxx b/direct/src/dcparser/dcField.cxx index 4f255dea7d..5880fd5097 100644 --- a/direct/src/dcparser/dcField.cxx +++ b/direct/src/dcparser/dcField.cxx @@ -162,7 +162,7 @@ as_parameter() const { * is an error. */ string DCField:: -format_data(const string &packed_data, bool show_field_names) { +format_data(const vector_uchar &packed_data, bool show_field_names) { DCPacker packer; packer.set_unpack_data(packed_data); packer.begin_unpack(this); @@ -178,20 +178,20 @@ format_data(const string &packed_data, bool show_field_names) { * above) that represents the value of this field, parse the string and return * the corresponding packed data. Returns empty string if there is an error. */ -string DCField:: +vector_uchar DCField:: parse_string(const string &formatted_string) { DCPacker packer; packer.begin_pack(this); if (!packer.parse_and_pack(formatted_string)) { // Parse error. - return string(); + return vector_uchar(); } if (!packer.end_pack()) { // Data type mismatch. - return string(); + return vector_uchar(); } - return packer.get_string(); + return packer.get_bytes(); } /** @@ -200,7 +200,7 @@ parse_string(const string &formatted_string) { * record. Returns true if all fields are valid, false otherwise. */ bool DCField:: -validate_ranges(const string &packed_data) const { +validate_ranges(const vector_uchar &packed_data) const { DCPacker packer; packer.set_unpack_data(packed_data); packer.begin_unpack(this); @@ -209,7 +209,7 @@ validate_ranges(const string &packed_data) const { return false; } - return (packer.get_num_unpacked_bytes() == packed_data.length()); + return (packer.get_num_unpacked_bytes() == packed_data.size()); } #ifdef HAVE_PYTHON @@ -488,7 +488,7 @@ pack_default_value(DCPackData &pack_data, bool &) const { // The default behavior is to pack the default value if we got it; // otherwise, to return false and let the packer visit our nested elements. if (!_default_value_stale) { - pack_data.append_data(_default_value.data(), _default_value.length()); + pack_data.append_data((const char *)_default_value.data(), _default_value.size()); return true; } @@ -566,7 +566,8 @@ refresh_default_value() { if (!packer.end_pack()) { std::cerr << "Error while packing default value for " << get_name() << "\n"; } else { - _default_value.assign(packer.get_data(), packer.get_length()); + const unsigned char *data = (const unsigned char *)packer.get_data(); + _default_value = vector_uchar(data, data + packer.get_length()); } _default_value_stale = false; } diff --git a/direct/src/dcparser/dcField.h b/direct/src/dcparser/dcField.h index dc06ff76f2..96b7da4487 100644 --- a/direct/src/dcparser/dcField.h +++ b/direct/src/dcparser/dcField.h @@ -53,13 +53,13 @@ PUBLISHED: virtual DCParameter *as_parameter(); virtual const DCParameter *as_parameter() const; - std::string format_data(const std::string &packed_data, bool show_field_names = true); - std::string parse_string(const std::string &formatted_string); + std::string format_data(const vector_uchar &packed_data, bool show_field_names = true); + vector_uchar parse_string(const std::string &formatted_string); - bool validate_ranges(const std::string &packed_data) const; + bool validate_ranges(const vector_uchar &packed_data) const; INLINE bool has_default_value() const; - INLINE const std::string &get_default_value() const; + INLINE const vector_uchar &get_default_value() const; INLINE bool is_bogus_field() const; @@ -98,7 +98,7 @@ public: INLINE void set_number(int number); INLINE void set_class(DCClass *dclass); - INLINE void set_default_value(const std::string &default_value); + INLINE void set_default_value(vector_uchar default_value); #ifdef HAVE_PYTHON static std::string get_pystr(PyObject *value); @@ -115,7 +115,7 @@ protected: bool _bogus_field; private: - std::string _default_value; + vector_uchar _default_value; #ifdef WITHIN_PANDA PStatCollector _field_update_pcollector; diff --git a/direct/src/dcparser/dcLexer.cxx.prebuilt b/direct/src/dcparser/dcLexer.cxx.prebuilt index 660f96b5cb..fece97f2ae 100644 --- a/direct/src/dcparser/dcLexer.cxx.prebuilt +++ b/direct/src/dcparser/dcLexer.cxx.prebuilt @@ -1,6 +1,6 @@ -#line 2 "lex.yy.c" +#line 2 "built/tmp/dcLexer.lxx.cxx" -#line 4 "lex.yy.c" +#line 4 "built/tmp/dcLexer.lxx.cxx" #define YY_INT_ALIGNED short int @@ -28,13 +28,23 @@ #define FLEX_SCANNER #define YY_FLEX_MAJOR_VERSION 2 #define YY_FLEX_MINOR_VERSION 5 -#define YY_FLEX_SUBMINOR_VERSION 35 +#define YY_FLEX_SUBMINOR_VERSION 37 #if YY_FLEX_SUBMINOR_VERSION > 0 #define FLEX_BETA #endif /* First, we deal with platform-specific or compiler-specific issues. */ +#if defined(__FreeBSD__) +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS +#endif +#include +#include +#else +#define __dead2 +#endif + /* begin standard C headers. */ #include #include @@ -50,10 +60,11 @@ /* C99 systems have . Non-C99 systems may or may not. */ -#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#if defined(__FreeBSD__) || \ + (defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) /* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, - * if you want the limit (max/min) macros for int types. + * if you want the limit (max/min) macros for int types. */ #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS 1 @@ -70,10 +81,9 @@ typedef uint32_t flex_uint32_t; typedef signed char flex_int8_t; typedef short int flex_int16_t; typedef int flex_int32_t; -typedef unsigned char flex_uint8_t; +typedef unsigned char flex_uint8_t; typedef unsigned short int flex_uint16_t; typedef unsigned int flex_uint32_t; -#endif /* ! C99 */ /* Limits of integral types. */ #ifndef INT8_MIN @@ -104,6 +114,8 @@ typedef unsigned int flex_uint32_t; #define UINT32_MAX (4294967295U) #endif +#endif /* ! C99 */ + #endif /* ! FLEXINT_H */ #ifdef __cplusplus @@ -172,7 +184,12 @@ typedef unsigned int flex_uint32_t; typedef struct yy_buffer_state *YY_BUFFER_STATE; #endif -extern int dcyyleng; +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +extern yy_size_t dcyyleng; extern FILE *dcyyin, *dcyyout; @@ -181,7 +198,7 @@ extern FILE *dcyyin, *dcyyout; #define EOB_ACT_LAST_MATCH 2 #define YY_LESS_LINENO(n) - + /* Return all but the first "n" matched characters back to the input stream. */ #define yyless(n) \ do \ @@ -198,11 +215,6 @@ extern FILE *dcyyin, *dcyyout; #define unput(c) yyunput( c, (yytext_ptr) ) -#ifndef YY_TYPEDEF_YY_SIZE_T -#define YY_TYPEDEF_YY_SIZE_T -typedef size_t yy_size_t; -#endif - #ifndef YY_STRUCT_YY_BUFFER_STATE #define YY_STRUCT_YY_BUFFER_STATE struct yy_buffer_state @@ -220,7 +232,7 @@ struct yy_buffer_state /* Number of characters read into yy_ch_buf, not including EOB * characters. */ - int yy_n_chars; + yy_size_t yy_n_chars; /* Whether we "own" the buffer - i.e., we know we created it, * and can realloc() it to grow it, and should free() it to @@ -243,7 +255,7 @@ struct yy_buffer_state int yy_bs_lineno; /**< The line count. */ int yy_bs_column; /**< The column count. */ - + /* Whether to try to fill the input buffer when we reach the * end of it. */ @@ -282,6 +294,7 @@ static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */ #define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ : NULL) +#define yy_current_buffer YY_CURRENT_BUFFER /* Same as previous macro, but useful when we know that the buffer stack is not * NULL or when we need an lvalue. For internal use only. @@ -290,8 +303,8 @@ static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */ /* yy_hold_char holds the character lost when dcyytext is formed. */ static char yy_hold_char; -static int yy_n_chars; /* number of characters read into yy_ch_buf */ -int dcyyleng; +static yy_size_t yy_n_chars; /* number of characters read into yy_ch_buf */ +yy_size_t dcyyleng; /* Points to current character in buffer. */ static char *yy_c_buf_p = (char *) 0; @@ -319,7 +332,7 @@ static void dcyy_init_buffer (YY_BUFFER_STATE b,FILE *file ); YY_BUFFER_STATE dcyy_scan_buffer (char *base,yy_size_t size ); YY_BUFFER_STATE dcyy_scan_string (yyconst char *yy_str ); -YY_BUFFER_STATE dcyy_scan_bytes (yyconst char *bytes,int len ); +YY_BUFFER_STATE dcyy_scan_bytes (yyconst char *bytes,yy_size_t len ); void *dcyyalloc (yy_size_t ); void *dcyyrealloc (void *,yy_size_t ); @@ -365,7 +378,7 @@ extern char *dcyytext; static yy_state_type yy_get_previous_state (void ); static yy_state_type yy_try_NUL_trans (yy_state_type current_state ); static int yy_get_next_buffer (void ); -static void yy_fatal_error (yyconst char msg[] ); +static void yy_fatal_error (yyconst char msg[] ) __dead2; /* Done after the current pattern has been matched and before the * corresponding action - sets up dcyytext. @@ -573,14 +586,14 @@ int dcyy_flex_debug = 0; #define YY_MORE_ADJ 0 #define YY_RESTORE_YY_MORE_OFFSET char *dcyytext; -#line 1 "dcLexer.lxx" +#line 1 "direct/src/dcparser/dcLexer.lxx" /* // Filename: dcLexer.lxx // Created by: drose (05Oct00) -// +// //////////////////////////////////////////////////////////////////// */ -#line 9 "dcLexer.lxx" +#line 9 "direct/src/dcparser/dcLexer.lxx" #include "dcLexerDefs.h" #include "dcParserDefs.h" #include "dcParser.h" @@ -678,12 +691,12 @@ dcyyerror(const std::string &msg) { if (!dc_filename.empty()) { cerr << " in " << dc_filename; } - cerr + cerr << " at line " << line_number << ", column " << col_number << ":\n" << current_line << "\n"; - indent(cerr, col_number-1) + indent(cerr, col_number-1) << "^\n" << msg << "\n\n"; - + error_count++; } @@ -695,10 +708,10 @@ dcyywarning(const std::string &msg) { if (!dc_filename.empty()) { cerr << " in " << dc_filename; } - cerr + cerr << " at line " << line_number << ", column " << col_number << ":\n" << current_line << "\n"; - indent(cerr, col_number-1) + indent(cerr, col_number-1) << "^\n" << msg << "\n\n"; warning_count++; @@ -708,7 +721,7 @@ dcyywarning(const std::string &msg) { // stdio FILE pointer. This is flex-specific. static void input_chars(char *buffer, int &result, int max_size) { - nassertv(input_p != NULL); + nassertv(input_p != nullptr); if (*input_p) { input_p->read(buffer, max_size); result = input_p->gcount(); @@ -729,7 +742,7 @@ input_chars(char *buffer, int &result, int max_size) { // Truncate it at the newline. char *end = strchr(current_line, '\n'); - if (end != NULL) { + if (end != nullptr) { *end = '\0'; } } @@ -888,9 +901,9 @@ scan_quoted_string(char quote_mark) { // scan_hex_string reads a string of hexadecimal digits delimited by // angle brackets and returns the representative string. -static std::string +static vector_uchar scan_hex_string() { - std::string result; + vector_uchar result; // We don't touch the current line number and column number during // scanning, so that if we detect an error while scanning the string @@ -920,24 +933,24 @@ scan_hex_string() { line_number = line; col_number = col; dcyyerror("Invalid hex digit."); - return std::string(); + return vector_uchar(); } odd = !odd; if (odd) { last = value; } else { - result += (char)((last << 4) | value); + result.push_back((unsigned char)((last << 4) | value)); } c = read_char(line, col); } if (c == EOF) { dcyyerror("This hex string is unterminated."); - return std::string(); + return vector_uchar(); } else if (odd) { dcyyerror("Odd number of hex digits."); - return std::string(); + return vector_uchar(); } line_number = line; @@ -957,7 +970,7 @@ eat_c_comment() { int col = col_number; int c, last_c; - + last_c = '\0'; c = read_char(line, col); while (c != EOF && !(last_c == '*' && c == '/')) { @@ -984,7 +997,7 @@ inline void accept() { col_number += dcyyleng; } -#line 984 "lex.yy.c" +#line 1001 "built/tmp/dcLexer.lxx.cxx" #define INITIAL 0 @@ -993,6 +1006,7 @@ inline void accept() { * down here because we want the user's section 1 to have been scanned first. * The user has a chance to override it with an option. */ +#include #endif #ifndef YY_EXTRA_TYPE @@ -1022,7 +1036,7 @@ FILE *dcyyget_out (void ); void dcyyset_out (FILE * out_str ); -int dcyyget_leng (void ); +yy_size_t dcyyget_leng (void ); char *dcyyget_text (void ); @@ -1042,8 +1056,10 @@ extern int dcyywrap (void ); #endif #endif +#ifndef YY_NO_UNPUT static void yyunput (int c,char *buf_ptr ); - +#endif + #ifndef yytext_ptr static void yy_flex_strncpy (char *,yyconst char *,int ); #endif @@ -1072,7 +1088,7 @@ static int input (void ); /* This used to be an fputs(), but since the string might contain NUL's, * we now use fwrite(). */ -#define ECHO fwrite( dcyytext, dcyyleng, 1, dcyyout ) +#define ECHO do { if (fwrite( dcyytext, dcyyleng, 1, dcyyout )) {} } while (0) #endif /* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, @@ -1083,7 +1099,7 @@ static int input (void ); if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ { \ int c = '*'; \ - int n; \ + size_t n; \ for ( n = 0; n < max_size && \ (c = getc( dcyyin )) != EOF && c != '\n'; ++n ) \ buf[n] = (char) c; \ @@ -1161,11 +1177,11 @@ extern int dcyylex (void); */ YY_DECL { - register yy_state_type yy_current_state; - register char *yy_cp, *yy_bp; - register int yy_act; - -#line 415 "dcLexer.lxx" + yy_state_type yy_current_state; + char *yy_cp, *yy_bp; + int yy_act; + +#line 419 "direct/src/dcparser/dcLexer.lxx" @@ -1176,7 +1192,7 @@ YY_DECL } -#line 1177 "lex.yy.c" +#line 1196 "built/tmp/dcLexer.lxx.cxx" if ( !(yy_init) ) { @@ -1220,7 +1236,7 @@ YY_DECL yy_match: do { - register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; + YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ; if ( yy_accept[yy_current_state] ) { (yy_last_accepting_state) = yy_current_state; @@ -1262,7 +1278,7 @@ do_action: /* This label is used only to access EOF actions. */ case 1: /* rule 1 can match eol */ YY_RULE_SETUP -#line 425 "dcLexer.lxx" +#line 429 "direct/src/dcparser/dcLexer.lxx" { // New line. Save a copy of the line so we can print it out for the // benefit of the user in case we get an error. @@ -1279,32 +1295,32 @@ YY_RULE_SETUP YY_BREAK case 2: YY_RULE_SETUP -#line 439 "dcLexer.lxx" -{ +#line 443 "direct/src/dcparser/dcLexer.lxx" +{ // Eat whitespace. accept(); } YY_BREAK case 3: YY_RULE_SETUP -#line 444 "dcLexer.lxx" -{ +#line 448 "direct/src/dcparser/dcLexer.lxx" +{ // Eat C++-style comments. accept(); } YY_BREAK case 4: YY_RULE_SETUP -#line 449 "dcLexer.lxx" +#line 453 "direct/src/dcparser/dcLexer.lxx" { // Eat C-style comments. accept(); - eat_c_comment(); + eat_c_comment(); } YY_BREAK case 5: YY_RULE_SETUP -#line 456 "dcLexer.lxx" +#line 460 "direct/src/dcparser/dcLexer.lxx" { accept(); return KW_DCLASS; @@ -1312,7 +1328,7 @@ YY_RULE_SETUP YY_BREAK case 6: YY_RULE_SETUP -#line 461 "dcLexer.lxx" +#line 465 "direct/src/dcparser/dcLexer.lxx" { accept(); return KW_STRUCT; @@ -1320,7 +1336,7 @@ YY_RULE_SETUP YY_BREAK case 7: YY_RULE_SETUP -#line 466 "dcLexer.lxx" +#line 470 "direct/src/dcparser/dcLexer.lxx" { accept(); return KW_FROM; @@ -1328,7 +1344,7 @@ YY_RULE_SETUP YY_BREAK case 8: YY_RULE_SETUP -#line 471 "dcLexer.lxx" +#line 475 "direct/src/dcparser/dcLexer.lxx" { accept(); return KW_IMPORT; @@ -1336,7 +1352,7 @@ YY_RULE_SETUP YY_BREAK case 9: YY_RULE_SETUP -#line 476 "dcLexer.lxx" +#line 480 "direct/src/dcparser/dcLexer.lxx" { accept(); return KW_KEYWORD; @@ -1344,7 +1360,7 @@ YY_RULE_SETUP YY_BREAK case 10: YY_RULE_SETUP -#line 481 "dcLexer.lxx" +#line 485 "direct/src/dcparser/dcLexer.lxx" { accept(); return KW_TYPEDEF; @@ -1352,7 +1368,7 @@ YY_RULE_SETUP YY_BREAK case 11: YY_RULE_SETUP -#line 486 "dcLexer.lxx" +#line 490 "direct/src/dcparser/dcLexer.lxx" { accept(); return KW_SWITCH; @@ -1360,7 +1376,7 @@ YY_RULE_SETUP YY_BREAK case 12: YY_RULE_SETUP -#line 491 "dcLexer.lxx" +#line 495 "direct/src/dcparser/dcLexer.lxx" { accept(); return KW_CASE; @@ -1368,7 +1384,7 @@ YY_RULE_SETUP YY_BREAK case 13: YY_RULE_SETUP -#line 496 "dcLexer.lxx" +#line 500 "direct/src/dcparser/dcLexer.lxx" { accept(); return KW_DEFAULT; @@ -1376,7 +1392,7 @@ YY_RULE_SETUP YY_BREAK case 14: YY_RULE_SETUP -#line 501 "dcLexer.lxx" +#line 505 "direct/src/dcparser/dcLexer.lxx" { accept(); return KW_BREAK; @@ -1384,7 +1400,7 @@ YY_RULE_SETUP YY_BREAK case 15: YY_RULE_SETUP -#line 506 "dcLexer.lxx" +#line 510 "direct/src/dcparser/dcLexer.lxx" { accept(); return KW_INT8; @@ -1392,7 +1408,7 @@ YY_RULE_SETUP YY_BREAK case 16: YY_RULE_SETUP -#line 511 "dcLexer.lxx" +#line 515 "direct/src/dcparser/dcLexer.lxx" { accept(); return KW_INT16; @@ -1400,7 +1416,7 @@ YY_RULE_SETUP YY_BREAK case 17: YY_RULE_SETUP -#line 516 "dcLexer.lxx" +#line 520 "direct/src/dcparser/dcLexer.lxx" { accept(); return KW_INT32; @@ -1408,7 +1424,7 @@ YY_RULE_SETUP YY_BREAK case 18: YY_RULE_SETUP -#line 521 "dcLexer.lxx" +#line 525 "direct/src/dcparser/dcLexer.lxx" { accept(); return KW_INT64; @@ -1416,7 +1432,7 @@ YY_RULE_SETUP YY_BREAK case 19: YY_RULE_SETUP -#line 526 "dcLexer.lxx" +#line 530 "direct/src/dcparser/dcLexer.lxx" { accept(); return KW_UINT8; @@ -1424,7 +1440,7 @@ YY_RULE_SETUP YY_BREAK case 20: YY_RULE_SETUP -#line 531 "dcLexer.lxx" +#line 535 "direct/src/dcparser/dcLexer.lxx" { accept(); return KW_UINT16; @@ -1432,7 +1448,7 @@ YY_RULE_SETUP YY_BREAK case 21: YY_RULE_SETUP -#line 536 "dcLexer.lxx" +#line 540 "direct/src/dcparser/dcLexer.lxx" { accept(); return KW_UINT32; @@ -1440,7 +1456,7 @@ YY_RULE_SETUP YY_BREAK case 22: YY_RULE_SETUP -#line 541 "dcLexer.lxx" +#line 545 "direct/src/dcparser/dcLexer.lxx" { accept(); return KW_UINT64; @@ -1448,7 +1464,7 @@ YY_RULE_SETUP YY_BREAK case 23: YY_RULE_SETUP -#line 546 "dcLexer.lxx" +#line 550 "direct/src/dcparser/dcLexer.lxx" { accept(); return KW_FLOAT64; @@ -1456,7 +1472,7 @@ YY_RULE_SETUP YY_BREAK case 24: YY_RULE_SETUP -#line 551 "dcLexer.lxx" +#line 555 "direct/src/dcparser/dcLexer.lxx" { accept(); return KW_STRING; @@ -1464,7 +1480,7 @@ YY_RULE_SETUP YY_BREAK case 25: YY_RULE_SETUP -#line 556 "dcLexer.lxx" +#line 560 "direct/src/dcparser/dcLexer.lxx" { accept(); return KW_BLOB; @@ -1472,7 +1488,7 @@ YY_RULE_SETUP YY_BREAK case 26: YY_RULE_SETUP -#line 561 "dcLexer.lxx" +#line 565 "direct/src/dcparser/dcLexer.lxx" { accept(); return KW_BLOB32; @@ -1480,7 +1496,7 @@ YY_RULE_SETUP YY_BREAK case 27: YY_RULE_SETUP -#line 566 "dcLexer.lxx" +#line 570 "direct/src/dcparser/dcLexer.lxx" { accept(); return KW_INT8ARRAY; @@ -1488,7 +1504,7 @@ YY_RULE_SETUP YY_BREAK case 28: YY_RULE_SETUP -#line 571 "dcLexer.lxx" +#line 575 "direct/src/dcparser/dcLexer.lxx" { accept(); return KW_INT16ARRAY; @@ -1496,7 +1512,7 @@ YY_RULE_SETUP YY_BREAK case 29: YY_RULE_SETUP -#line 576 "dcLexer.lxx" +#line 580 "direct/src/dcparser/dcLexer.lxx" { accept(); return KW_INT32ARRAY; @@ -1504,7 +1520,7 @@ YY_RULE_SETUP YY_BREAK case 30: YY_RULE_SETUP -#line 581 "dcLexer.lxx" +#line 585 "direct/src/dcparser/dcLexer.lxx" { accept(); return KW_UINT8ARRAY; @@ -1512,7 +1528,7 @@ YY_RULE_SETUP YY_BREAK case 31: YY_RULE_SETUP -#line 586 "dcLexer.lxx" +#line 590 "direct/src/dcparser/dcLexer.lxx" { accept(); return KW_UINT16ARRAY; @@ -1520,7 +1536,7 @@ YY_RULE_SETUP YY_BREAK case 32: YY_RULE_SETUP -#line 591 "dcLexer.lxx" +#line 595 "direct/src/dcparser/dcLexer.lxx" { accept(); return KW_UINT32ARRAY; @@ -1528,7 +1544,7 @@ YY_RULE_SETUP YY_BREAK case 33: YY_RULE_SETUP -#line 596 "dcLexer.lxx" +#line 600 "direct/src/dcparser/dcLexer.lxx" { accept(); return KW_UINT32UINT8ARRAY; @@ -1536,7 +1552,7 @@ YY_RULE_SETUP YY_BREAK case 34: YY_RULE_SETUP -#line 601 "dcLexer.lxx" +#line 605 "direct/src/dcparser/dcLexer.lxx" { accept(); return KW_CHAR; @@ -1544,8 +1560,8 @@ YY_RULE_SETUP YY_BREAK case 35: YY_RULE_SETUP -#line 606 "dcLexer.lxx" -{ +#line 610 "direct/src/dcparser/dcLexer.lxx" +{ // An unsigned integer number. accept(); @@ -1564,14 +1580,14 @@ YY_RULE_SETUP dcyylval.u.uint64 = next_value + (*p - '0'); ++p; } - + return UNSIGNED_INTEGER; } YY_BREAK case 36: YY_RULE_SETUP -#line 629 "dcLexer.lxx" -{ +#line 633 "direct/src/dcparser/dcLexer.lxx" +{ // A signed integer number. accept(); @@ -1612,17 +1628,17 @@ YY_RULE_SETUP dcyyerror("Number out of range."); dcyylval.u.int64 = 1; } - } - + } + return SIGNED_INTEGER; } YY_BREAK case 37: YY_RULE_SETUP -#line 675 "dcLexer.lxx" +#line 679 "direct/src/dcparser/dcLexer.lxx" { // A hexadecimal integer number. - accept(); + accept(); // As above, we'll decode the hex string by hand. dcyylval.str = dcyytext; @@ -1644,23 +1660,23 @@ YY_RULE_SETUP ++p; } - return UNSIGNED_INTEGER; + return UNSIGNED_INTEGER; } YY_BREAK case 38: YY_RULE_SETUP -#line 702 "dcLexer.lxx" -{ +#line 706 "direct/src/dcparser/dcLexer.lxx" +{ // A floating-point number. - accept(); - dcyylval.u.real = patof(dcyytext); + accept(); + dcyylval.u.real = patof(dcyytext); dcyylval.str = dcyytext; - return REAL; + return REAL; } YY_BREAK case 39: YY_RULE_SETUP -#line 710 "dcLexer.lxx" +#line 714 "direct/src/dcparser/dcLexer.lxx" { // Quoted string. accept(); @@ -1670,7 +1686,7 @@ YY_RULE_SETUP YY_BREAK case 40: YY_RULE_SETUP -#line 717 "dcLexer.lxx" +#line 721 "direct/src/dcparser/dcLexer.lxx" { // Single-quoted string. accept(); @@ -1680,25 +1696,25 @@ YY_RULE_SETUP YY_BREAK case 41: YY_RULE_SETUP -#line 724 "dcLexer.lxx" +#line 728 "direct/src/dcparser/dcLexer.lxx" { // Long hex string. accept(); - dcyylval.str = scan_hex_string(); + dcyylval.bytes = scan_hex_string(); return HEX_STRING; } YY_BREAK case 42: YY_RULE_SETUP -#line 731 "dcLexer.lxx" -{ +#line 735 "direct/src/dcparser/dcLexer.lxx" +{ // Identifier or keyword. accept(); dcyylval.str = dcyytext; - if (dc_file != (DCFile *)NULL) { + if (dc_file != nullptr) { const DCKeyword *keyword = dc_file->get_keyword_by_name(dcyylval.str); - if (keyword != (DCKeyword *)NULL) { + if (keyword != nullptr) { dcyylval.u.keyword = keyword; return KEYWORD; } @@ -1708,19 +1724,19 @@ YY_RULE_SETUP YY_BREAK case 43: YY_RULE_SETUP -#line 747 "dcLexer.lxx" +#line 751 "direct/src/dcparser/dcLexer.lxx" { // Send any other printable character as itself. - accept(); + accept(); return dcyytext[0]; } YY_BREAK case 44: YY_RULE_SETUP -#line 753 "dcLexer.lxx" +#line 757 "direct/src/dcparser/dcLexer.lxx" ECHO; YY_BREAK -#line 1721 "lex.yy.c" +#line 1740 "built/tmp/dcLexer.lxx.cxx" case YY_STATE_EOF(INITIAL): yyterminate(); @@ -1862,9 +1878,9 @@ case YY_STATE_EOF(INITIAL): */ static int yy_get_next_buffer (void) { - register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; - register char *source = (yytext_ptr); - register int number_to_move, i; + char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + char *source = (yytext_ptr); + int number_to_move, i; int ret_val; if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) @@ -1906,21 +1922,21 @@ static int yy_get_next_buffer (void) else { - int num_to_read = + yy_size_t num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; while ( num_to_read <= 0 ) { /* Not enough room in the buffer - grow it. */ /* just a shorter name for the current buffer */ - YY_BUFFER_STATE b = YY_CURRENT_BUFFER; + YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; int yy_c_buf_p_offset = (int) ((yy_c_buf_p) - b->yy_ch_buf); if ( b->yy_is_our_buffer ) { - int new_size = b->yy_buf_size * 2; + yy_size_t new_size = b->yy_buf_size * 2; if ( new_size <= 0 ) b->yy_buf_size += b->yy_buf_size / 8; @@ -1951,7 +1967,7 @@ static int yy_get_next_buffer (void) /* Read in more data. */ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), - (yy_n_chars), (size_t) num_to_read ); + (yy_n_chars), num_to_read ); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); } @@ -1996,14 +2012,14 @@ static int yy_get_next_buffer (void) static yy_state_type yy_get_previous_state (void) { - register yy_state_type yy_current_state; - register char *yy_cp; - + yy_state_type yy_current_state; + char *yy_cp; + yy_current_state = (yy_start); for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) { - register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); + YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); if ( yy_accept[yy_current_state] ) { (yy_last_accepting_state) = yy_current_state; @@ -2028,10 +2044,10 @@ static int yy_get_next_buffer (void) */ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) { - register int yy_is_jam; - register char *yy_cp = (yy_c_buf_p); + int yy_is_jam; + char *yy_cp = (yy_c_buf_p); - register YY_CHAR yy_c = 1; + YY_CHAR yy_c = 1; if ( yy_accept[yy_current_state] ) { (yy_last_accepting_state) = yy_current_state; @@ -2046,13 +2062,14 @@ static int yy_get_next_buffer (void) yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; yy_is_jam = (yy_current_state == 164); - return yy_is_jam ? 0 : yy_current_state; + return yy_is_jam ? 0 : yy_current_state; } - static void yyunput (int c, register char * yy_bp ) +#ifndef YY_NO_UNPUT + static void yyunput (int c, char * yy_bp ) { - register char *yy_cp; - + char *yy_cp; + yy_cp = (yy_c_buf_p); /* undo effects of setting up dcyytext */ @@ -2061,10 +2078,10 @@ static int yy_get_next_buffer (void) if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) { /* need to shift things up to make room */ /* +2 for EOB chars. */ - register int number_to_move = (yy_n_chars) + 2; - register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[ + yy_size_t number_to_move = (yy_n_chars) + 2; + char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[ YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2]; - register char *source = + char *source = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]; while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) @@ -2085,6 +2102,7 @@ static int yy_get_next_buffer (void) (yy_hold_char) = *yy_cp; (yy_c_buf_p) = yy_cp; } +#endif /* ifndef YY_NO_UNPUT */ #ifndef YY_NO_INPUT #ifdef __cplusplus @@ -2095,7 +2113,7 @@ static int yy_get_next_buffer (void) { int c; - + *(yy_c_buf_p) = (yy_hold_char); if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) @@ -2110,7 +2128,7 @@ static int yy_get_next_buffer (void) else { /* need more input */ - int offset = (yy_c_buf_p) - (yytext_ptr); + yy_size_t offset = (yy_c_buf_p) - (yytext_ptr); ++(yy_c_buf_p); switch ( yy_get_next_buffer( ) ) @@ -2162,12 +2180,12 @@ static int yy_get_next_buffer (void) /** Immediately switch to a different input stream. * @param input_file A readable stream. - * + * * @note This function does not reset the start condition to @c INITIAL . */ void dcyyrestart (FILE * input_file ) { - + if ( ! YY_CURRENT_BUFFER ){ dcyyensure_buffer_stack (); YY_CURRENT_BUFFER_LVALUE = @@ -2180,11 +2198,11 @@ static int yy_get_next_buffer (void) /** Switch to a different input buffer. * @param new_buffer The new input buffer. - * + * */ void dcyy_switch_to_buffer (YY_BUFFER_STATE new_buffer ) { - + /* TODO. We should be able to replace this entire function body * with * dcyypop_buffer_state(); @@ -2224,13 +2242,13 @@ static void dcyy_load_buffer_state (void) /** Allocate and initialize an input buffer state. * @param file A readable stream. * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. - * + * * @return the allocated buffer state. */ YY_BUFFER_STATE dcyy_create_buffer (FILE * file, int size ) { YY_BUFFER_STATE b; - + b = (YY_BUFFER_STATE) dcyyalloc(sizeof( struct yy_buffer_state ) ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in dcyy_create_buffer()" ); @@ -2253,11 +2271,11 @@ static void dcyy_load_buffer_state (void) /** Destroy the buffer. * @param b a buffer created with dcyy_create_buffer() - * + * */ void dcyy_delete_buffer (YY_BUFFER_STATE b ) { - + if ( ! b ) return; @@ -2270,10 +2288,6 @@ static void dcyy_load_buffer_state (void) dcyyfree((void *) b ); } -#ifndef __cplusplus -extern int isatty (int ); -#endif /* __cplusplus */ - /* Initializes or reinitializes a buffer. * This function is sometimes called more than once on the same buffer, * such as during a dcyyrestart() or at EOF. @@ -2282,7 +2296,7 @@ extern int isatty (int ); { int oerrno = errno; - + dcyy_flush_buffer(b ); b->yy_input_file = file; @@ -2298,13 +2312,13 @@ extern int isatty (int ); } b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; - + errno = oerrno; } /** Discard all buffered characters. On the next scan, YY_INPUT will be called. * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. - * + * */ void dcyy_flush_buffer (YY_BUFFER_STATE b ) { @@ -2333,7 +2347,7 @@ extern int isatty (int ); * the current state. This function will allocate the stack * if necessary. * @param new_buffer The new state. - * + * */ void dcyypush_buffer_state (YY_BUFFER_STATE new_buffer ) { @@ -2363,7 +2377,7 @@ void dcyypush_buffer_state (YY_BUFFER_STATE new_buffer ) /** Removes and deletes the top of the stack, if present. * The next element becomes the new top. - * + * */ void dcyypop_buffer_state (void) { @@ -2386,8 +2400,8 @@ void dcyypop_buffer_state (void) */ static void dcyyensure_buffer_stack (void) { - int num_to_alloc; - + yy_size_t num_to_alloc; + if (!(yy_buffer_stack)) { /* First allocation is just for 2 elements, since we don't know if this @@ -2400,9 +2414,9 @@ static void dcyyensure_buffer_stack (void) ); if ( ! (yy_buffer_stack) ) YY_FATAL_ERROR( "out of dynamic memory in dcyyensure_buffer_stack()" ); - + memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); - + (yy_buffer_stack_max) = num_to_alloc; (yy_buffer_stack_top) = 0; return; @@ -2430,13 +2444,13 @@ static void dcyyensure_buffer_stack (void) /** Setup the input buffer state to scan directly from a user-specified character buffer. * @param base the character buffer * @param size the size in bytes of the character buffer - * - * @return the newly allocated buffer state object. + * + * @return the newly allocated buffer state object. */ YY_BUFFER_STATE dcyy_scan_buffer (char * base, yy_size_t size ) { YY_BUFFER_STATE b; - + if ( size < 2 || base[size-2] != YY_END_OF_BUFFER_CHAR || base[size-1] != YY_END_OF_BUFFER_CHAR ) @@ -2465,31 +2479,31 @@ YY_BUFFER_STATE dcyy_scan_buffer (char * base, yy_size_t size ) /** Setup the input buffer state to scan a string. The next call to dcyylex() will * scan from a @e copy of @a str. * @param yystr a NUL-terminated string to scan - * + * * @return the newly allocated buffer state object. * @note If you want to scan bytes that may contain NUL values, then use * dcyy_scan_bytes() instead. */ YY_BUFFER_STATE dcyy_scan_string (yyconst char * yystr ) { - + return dcyy_scan_bytes(yystr,strlen(yystr) ); } /** Setup the input buffer state to scan the given bytes. The next call to dcyylex() will * scan from a @e copy of @a bytes. - * @param bytes the byte buffer to scan - * @param len the number of bytes in the buffer pointed to by @a bytes. - * + * @param yybytes the byte buffer to scan + * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. + * * @return the newly allocated buffer state object. */ -YY_BUFFER_STATE dcyy_scan_bytes (yyconst char * yybytes, int _yybytes_len ) +YY_BUFFER_STATE dcyy_scan_bytes (yyconst char * yybytes, yy_size_t _yybytes_len ) { YY_BUFFER_STATE b; char *buf; yy_size_t n; - int i; - + yy_size_t i; + /* Get memory for full buffer, including space for trailing EOB's. */ n = _yybytes_len + 2; buf = (char *) dcyyalloc(n ); @@ -2543,16 +2557,16 @@ static void yy_fatal_error (yyconst char* msg ) /* Accessor methods (get/set functions) to struct members. */ /** Get the current line number. - * + * */ int dcyyget_lineno (void) { - + return dcyylineno; } /** Get the input stream. - * + * */ FILE *dcyyget_in (void) { @@ -2560,7 +2574,7 @@ FILE *dcyyget_in (void) } /** Get the output stream. - * + * */ FILE *dcyyget_out (void) { @@ -2568,15 +2582,15 @@ FILE *dcyyget_out (void) } /** Get the length of the current token. - * + * */ -int dcyyget_leng (void) +yy_size_t dcyyget_leng (void) { return dcyyleng; } /** Get the current token. - * + * */ char *dcyyget_text (void) @@ -2586,18 +2600,18 @@ char *dcyyget_text (void) /** Set the current line number. * @param line_number - * + * */ void dcyyset_lineno (int line_number ) { - + dcyylineno = line_number; } /** Set the input stream. This does not discard the current * input buffer. * @param in_str A readable stream. - * + * * @see dcyy_switch_to_buffer */ void dcyyset_in (FILE * in_str ) @@ -2651,7 +2665,7 @@ static int yy_init_globals (void) /* dcyylex_destroy is for both reentrant and non-reentrant scanners. */ int dcyylex_destroy (void) { - + /* Pop the buffer stack, destroying each element. */ while(YY_CURRENT_BUFFER){ dcyy_delete_buffer(YY_CURRENT_BUFFER ); @@ -2677,7 +2691,7 @@ int dcyylex_destroy (void) #ifndef yytext_ptr static void yy_flex_strncpy (char* s1, yyconst char * s2, int n ) { - register int i; + int i; for ( i = 0; i < n; ++i ) s1[i] = s2[i]; } @@ -2686,7 +2700,7 @@ static void yy_flex_strncpy (char* s1, yyconst char * s2, int n ) #ifdef YY_NEED_STRLEN static int yy_flex_strlen (yyconst char * s ) { - register int n; + int n; for ( n = 0; s[n]; ++n ) ; @@ -2718,4 +2732,4 @@ void dcyyfree (void * ptr ) #define YYTABLES_NAME "yytables" -#line 753 "dcLexer.lxx" +#line 757 "direct/src/dcparser/dcLexer.lxx" diff --git a/direct/src/dcparser/dcLexer.lxx b/direct/src/dcparser/dcLexer.lxx index 3c20a69b5f..3a8156a4f3 100644 --- a/direct/src/dcparser/dcLexer.lxx +++ b/direct/src/dcparser/dcLexer.lxx @@ -1,7 +1,7 @@ /* // Filename: dcLexer.lxx // Created by: drose (05Oct00) -// +// //////////////////////////////////////////////////////////////////// */ @@ -103,12 +103,12 @@ dcyyerror(const std::string &msg) { if (!dc_filename.empty()) { cerr << " in " << dc_filename; } - cerr + cerr << " at line " << line_number << ", column " << col_number << ":\n" << current_line << "\n"; - indent(cerr, col_number-1) + indent(cerr, col_number-1) << "^\n" << msg << "\n\n"; - + error_count++; } @@ -120,10 +120,10 @@ dcyywarning(const std::string &msg) { if (!dc_filename.empty()) { cerr << " in " << dc_filename; } - cerr + cerr << " at line " << line_number << ", column " << col_number << ":\n" << current_line << "\n"; - indent(cerr, col_number-1) + indent(cerr, col_number-1) << "^\n" << msg << "\n\n"; warning_count++; @@ -313,9 +313,9 @@ scan_quoted_string(char quote_mark) { // scan_hex_string reads a string of hexadecimal digits delimited by // angle brackets and returns the representative string. -static std::string +static vector_uchar scan_hex_string() { - std::string result; + vector_uchar result; // We don't touch the current line number and column number during // scanning, so that if we detect an error while scanning the string @@ -345,24 +345,24 @@ scan_hex_string() { line_number = line; col_number = col; dcyyerror("Invalid hex digit."); - return std::string(); + return vector_uchar(); } odd = !odd; if (odd) { last = value; } else { - result += (char)((last << 4) | value); + result.push_back((unsigned char)((last << 4) | value)); } c = read_char(line, col); } if (c == EOF) { dcyyerror("This hex string is unterminated."); - return std::string(); + return vector_uchar(); } else if (odd) { dcyyerror("Odd number of hex digits."); - return std::string(); + return vector_uchar(); } line_number = line; @@ -382,7 +382,7 @@ eat_c_comment() { int col = col_number; int c, last_c; - + last_c = '\0'; c = read_char(line, col); while (c != EOF && !(last_c == '*' && c == '/')) { @@ -440,12 +440,12 @@ REALNUM ([+-]?(([0-9]+[.])|([0-9]*[.][0-9]+))([eE][+-]?[0-9]+)?) yyless(1); } -[ \t\r] { +[ \t\r] { // Eat whitespace. accept(); } -"//".* { +"//".* { // Eat C++-style comments. accept(); } @@ -453,7 +453,7 @@ REALNUM ([+-]?(([0-9]+[.])|([0-9]*[.][0-9]+))([eE][+-]?[0-9]+)?) "/*" { // Eat C-style comments. accept(); - eat_c_comment(); + eat_c_comment(); } @@ -607,7 +607,7 @@ REALNUM ([+-]?(([0-9]+[.])|([0-9]*[.][0-9]+))([eE][+-]?[0-9]+)?) return KW_CHAR; } -{UNSIGNED_INTEGERNUM} { +{UNSIGNED_INTEGERNUM} { // An unsigned integer number. accept(); @@ -626,11 +626,11 @@ REALNUM ([+-]?(([0-9]+[.])|([0-9]*[.][0-9]+))([eE][+-]?[0-9]+)?) dcyylval.u.uint64 = next_value + (*p - '0'); ++p; } - + return UNSIGNED_INTEGER; } -{SIGNED_INTEGERNUM} { +{SIGNED_INTEGERNUM} { // A signed integer number. accept(); @@ -671,14 +671,14 @@ REALNUM ([+-]?(([0-9]+[.])|([0-9]*[.][0-9]+))([eE][+-]?[0-9]+)?) dcyyerror("Number out of range."); dcyylval.u.int64 = 1; } - } - + } + return SIGNED_INTEGER; } {UNSIGNED_HEXNUM} { // A hexadecimal integer number. - accept(); + accept(); // As above, we'll decode the hex string by hand. dcyylval.str = dcyytext; @@ -700,15 +700,15 @@ REALNUM ([+-]?(([0-9]+[.])|([0-9]*[.][0-9]+))([eE][+-]?[0-9]+)?) ++p; } - return UNSIGNED_INTEGER; + return UNSIGNED_INTEGER; } -{REALNUM} { +{REALNUM} { // A floating-point number. - accept(); - dcyylval.u.real = patof(dcyytext); + accept(); + dcyylval.u.real = patof(dcyytext); dcyylval.str = dcyytext; - return REAL; + return REAL; } ["] { @@ -728,11 +728,11 @@ REALNUM ([+-]?(([0-9]+[.])|([0-9]*[.][0-9]+))([eE][+-]?[0-9]+)?) [<] { // Long hex string. accept(); - dcyylval.str = scan_hex_string(); + dcyylval.bytes = scan_hex_string(); return HEX_STRING; } -[A-Za-z_][A-Za-z_0-9]* { +[A-Za-z_][A-Za-z_0-9]* { // Identifier or keyword. accept(); dcyylval.str = dcyytext; @@ -750,7 +750,7 @@ REALNUM ([+-]?(([0-9]+[.])|([0-9]*[.][0-9]+))([eE][+-]?[0-9]+)?) . { // Send any other printable character as itself. - accept(); + accept(); return dcyytext[0]; } - + diff --git a/direct/src/dcparser/dcPacker.I b/direct/src/dcparser/dcPacker.I index 8c640d77b1..6b6ef79ee5 100644 --- a/direct/src/dcparser/dcPacker.I +++ b/direct/src/dcparser/dcPacker.I @@ -217,16 +217,30 @@ pack_string(const std::string &value) { } /** - * Adds the indicated string value into the stream, representing a single pre- - * packed field element, or a whole group of field elements at once. + * Packs the indicated numeric or string value into the stream. */ INLINE void DCPacker:: -pack_literal_value(const std::string &value) { +pack_blob(const vector_uchar &value) { nassertv(_mode == M_pack || _mode == M_repack); if (_current_field == nullptr) { _pack_error = true; } else { - _pack_data.append_data(value.data(), value.length()); + _current_field->pack_blob(_pack_data, value, _pack_error, _range_error); + advance(); + } +} + +/** + * Adds the indicated string value into the stream, representing a single pre- + * packed field element, or a whole group of field elements at once. + */ +INLINE void DCPacker:: +pack_literal_value(const vector_uchar &value) { + nassertv(_mode == M_pack || _mode == M_repack); + if (_current_field == nullptr) { + _pack_error = true; + } else { + _pack_data.append_data((const char *)value.data(), value.size()); advance(); } } @@ -345,16 +359,36 @@ unpack_string() { return value; } +/** + * Unpacks the current binary data value from the stream. + */ +INLINE vector_uchar DCPacker:: +unpack_blob() { + vector_uchar value; + nassertr(_mode == M_unpack, value); + if (_current_field == nullptr) { + _pack_error = true; + + } else { + _current_field->unpack_blob(_unpack_data, _unpack_length, _unpack_p, + value, _pack_error, _range_error); + advance(); + } + + return value; +} + /** * Returns the literal string that represents the packed value of the current * field, and advances the field pointer. */ -INLINE std::string DCPacker:: +INLINE vector_uchar DCPacker:: unpack_literal_value() { size_t start = _unpack_p; unpack_skip(); - nassertr(_unpack_p >= start, std::string()); - return std::string(_unpack_data + start, _unpack_p - start); + nassertr(_unpack_p >= start, vector_uchar()); + return vector_uchar((const unsigned char *)_unpack_data + start, + (const unsigned char *)_unpack_data + _unpack_p); } /** @@ -453,16 +487,33 @@ unpack_string(std::string &value) { } } +/** + * Unpacks the current numeric or string value from the stream. + */ +INLINE void DCPacker:: +unpack_blob(vector_uchar &value) { + nassertv(_mode == M_unpack); + if (_current_field == nullptr) { + _pack_error = true; + + } else { + _current_field->unpack_blob(_unpack_data, _unpack_length, _unpack_p, + value, _pack_error, _range_error); + advance(); + } +} + /** * Returns the literal string that represents the packed value of the current * field, and advances the field pointer. */ INLINE void DCPacker:: -unpack_literal_value(std::string &value) { +unpack_literal_value(vector_uchar &value) { size_t start = _unpack_p; unpack_skip(); nassertv(_unpack_p >= start); - value.assign(_unpack_data + start, _unpack_p - start); + value = vector_uchar((const unsigned char *)_unpack_data + start, + (const unsigned char *)_unpack_data + _unpack_p); } /** @@ -541,6 +592,15 @@ get_string() const { return _pack_data.get_string(); } +/** + * Returns the packed data buffer as a bytes object. Also see get_data(). + */ +INLINE vector_uchar DCPacker:: +get_bytes() const { + const unsigned char *p = (const unsigned char *)_pack_data.get_data(); + return vector_uchar(p, p + _pack_data.get_length()); +} + /** * Returns the total number of bytes in the unpack data buffer. This is the * buffer used when unpacking; it is separate from the pack data returned by @@ -601,9 +661,9 @@ take_data() { * between packing sessions. */ INLINE void DCPacker:: -append_data(const char *buffer, size_t size) { +append_data(const unsigned char *buffer, size_t size) { nassertv(_mode == M_idle); - _pack_data.append_data(buffer, size); + _pack_data.append_data((const char *)buffer, size); } /** @@ -728,6 +788,16 @@ raw_pack_string(const std::string &value) { _pack_data.append_data(value.data(), value.length()); } +/** + * Packs the data into the buffer between packing sessions. + */ +INLINE void DCPacker:: +raw_pack_blob(const vector_uchar &value) { + nassertv(_mode == M_idle); + DCPackerInterface::do_pack_uint16(_pack_data.get_write_pointer(2), value.size()); + _pack_data.append_data((const char *)value.data(), value.size()); +} + /** * Unpacks the data from the buffer between unpacking sessions. */ @@ -870,6 +940,16 @@ raw_unpack_string() { return value; } +/** + * Unpacks the data from the buffer between unpacking sessions. + */ +INLINE vector_uchar DCPacker:: +raw_unpack_blob() { + vector_uchar value; + raw_unpack_blob(value); + return value; +} + /** * Unpacks the data from the buffer between unpacking sessions. */ @@ -971,6 +1051,24 @@ raw_unpack_string(std::string &value) { _unpack_p += string_length; } +/** + * Unpacks the data from the buffer between unpacking sessions. + */ +INLINE void DCPacker:: +raw_unpack_blob(vector_uchar &value) { + nassertv(_mode == M_idle && _unpack_data != nullptr); + unsigned int blob_size = raw_unpack_uint16(); + + if (_unpack_p + blob_size > _unpack_length) { + _pack_error = true; + return; + } + + const unsigned char *p = (const unsigned char *)_unpack_data + _unpack_p; + value = vector_uchar(p, p + blob_size); + _unpack_p += blob_size; +} + /** * Advances to the next field after a call to pack_value() or pop(). */ diff --git a/direct/src/dcparser/dcPacker.cxx b/direct/src/dcparser/dcPacker.cxx index ebab348f6f..b6dbd60855 100644 --- a/direct/src/dcparser/dcPacker.cxx +++ b/direct/src/dcparser/dcPacker.cxx @@ -114,12 +114,12 @@ end_pack() { * version of begin_unpack() that takes only one parameter. */ void DCPacker:: -set_unpack_data(const string &data) { +set_unpack_data(const vector_uchar &data) { nassertv(_mode == M_idle); - char *buffer = new char[data.length()]; - memcpy(buffer, data.data(), data.length()); - set_unpack_data(buffer, data.length(), true); + char *buffer = new char[data.size()]; + memcpy(buffer, data.data(), data.size()); + set_unpack_data(buffer, data.size(), true); } /** @@ -721,11 +721,11 @@ pack_object(PyObject *object) { pack_string(string(buffer, length)); } } else if (PyBytes_Check(object)) { - char *buffer; + const unsigned char *buffer; Py_ssize_t length; - PyBytes_AsStringAndSize(object, &buffer, &length); + PyBytes_AsStringAndSize(object, (char **)&buffer, &length); if (buffer) { - pack_string(string(buffer, length)); + pack_blob(vector_uchar(buffer, buffer + length)); } #else } else if (PyString_Check(object) || PyUnicode_Check(object)) { @@ -1118,9 +1118,9 @@ enquote_string(ostream &out, char quote_mark, const string &str) { * Outputs the indicated string as a hex constant. */ void DCPacker:: -output_hex_string(ostream &out, const string &str) { +output_hex_string(ostream &out, const vector_uchar &str) { out << '<'; - for (string::const_iterator pi = str.begin(); + for (vector_uchar::const_iterator pi = str.begin(); pi != str.end(); ++pi) { char buffer[10]; diff --git a/direct/src/dcparser/dcPacker.h b/direct/src/dcparser/dcPacker.h index 68a01a5999..dce28dbc30 100644 --- a/direct/src/dcparser/dcPacker.h +++ b/direct/src/dcparser/dcPacker.h @@ -41,7 +41,7 @@ PUBLISHED: void begin_pack(const DCPackerInterface *root); bool end_pack(); - void set_unpack_data(const std::string &data); + void set_unpack_data(const vector_uchar &data); public: void set_unpack_data(const char *unpack_data, size_t unpack_length, bool owns_unpack_data); @@ -75,7 +75,8 @@ PUBLISHED: INLINE void pack_int64(int64_t value); INLINE void pack_uint64(uint64_t value); INLINE void pack_string(const std::string &value); - INLINE void pack_literal_value(const std::string &value); + INLINE void pack_blob(const vector_uchar &value); + INLINE void pack_literal_value(const vector_uchar &value); void pack_default_value(); INLINE double unpack_double(); @@ -84,7 +85,8 @@ PUBLISHED: INLINE int64_t unpack_int64(); INLINE uint64_t unpack_uint64(); INLINE std::string unpack_string(); - INLINE std::string unpack_literal_value(); + INLINE vector_uchar unpack_blob(); + INLINE vector_uchar unpack_literal_value(); void unpack_validate(); void unpack_skip(); @@ -97,7 +99,8 @@ public: INLINE void unpack_int64(int64_t &value); INLINE void unpack_uint64(uint64_t &value); INLINE void unpack_string(std::string &value); - INLINE void unpack_literal_value(std::string &value); + INLINE void unpack_blob(vector_uchar &value); + INLINE void unpack_literal_value(vector_uchar &value); PUBLISHED: @@ -119,6 +122,7 @@ PUBLISHED: INLINE size_t get_length() const; INLINE std::string get_string() const; + INLINE vector_uchar get_bytes() const; INLINE size_t get_unpack_length() const; INLINE std::string get_unpack_string() const; public: @@ -126,7 +130,7 @@ public: INLINE const char *get_data() const; INLINE char *take_data(); - INLINE void append_data(const char *buffer, size_t size); + INLINE void append_data(const unsigned char *buffer, size_t size); INLINE char *get_write_pointer(size_t size); INLINE const char *get_unpack_data() const; @@ -148,6 +152,7 @@ PUBLISHED: INLINE void raw_pack_uint64(uint64_t value); INLINE void raw_pack_float64(double value); INLINE void raw_pack_string(const std::string &value); + INLINE void raw_pack_blob(const vector_uchar &value); // this is a hack to allw me to get in and out of 32bit Mode Faster need to // agree with channel_type in dcbase.h @@ -165,6 +170,7 @@ PUBLISHED: INLINE uint64_t raw_unpack_uint64(); INLINE double raw_unpack_float64(); INLINE std::string raw_unpack_string(); + INLINE vector_uchar raw_unpack_blob(); public: INLINE void raw_unpack_int8(int &value); @@ -177,10 +183,11 @@ public: INLINE void raw_unpack_uint64(uint64_t &value); INLINE void raw_unpack_float64(double &value); INLINE void raw_unpack_string(std::string &value); + INLINE void raw_unpack_blob(vector_uchar &value); public: static void enquote_string(std::ostream &out, char quote_mark, const std::string &str); - static void output_hex_string(std::ostream &out, const std::string &str); + static void output_hex_string(std::ostream &out, const vector_uchar &str); private: INLINE void advance(); diff --git a/direct/src/dcparser/dcPackerInterface.cxx b/direct/src/dcparser/dcPackerInterface.cxx index d17ff1f7b7..db51d2712c 100644 --- a/direct/src/dcparser/dcPackerInterface.cxx +++ b/direct/src/dcparser/dcPackerInterface.cxx @@ -248,6 +248,14 @@ pack_string(DCPackData &, const string &, bool &pack_error, bool &) const { pack_error = true; } +/** + * Packs the indicated numeric or string value into the stream. + */ +void DCPackerInterface:: +pack_blob(DCPackData &, const vector_uchar &, bool &pack_error, bool &) const { + pack_error = true; +} + /** * Packs the field's specified default value (or a sensible default if no * value is specified) into the stream. Returns true if the default value is @@ -306,6 +314,14 @@ unpack_string(const char *, size_t, size_t &, string &, bool &pack_error, bool & pack_error = true; } +/** + * Unpacks the current numeric or string value from the stream. + */ +void DCPackerInterface:: +unpack_blob(const char *, size_t, size_t &, vector_uchar &, bool &pack_error, bool &) const { + pack_error = true; +} + /** * Internally unpacks the current numeric or string value and validates it * against the type range limits, but does not return the value. Returns true diff --git a/direct/src/dcparser/dcPackerInterface.h b/direct/src/dcparser/dcPackerInterface.h index ef28b85647..0b2f81d2fe 100644 --- a/direct/src/dcparser/dcPackerInterface.h +++ b/direct/src/dcparser/dcPackerInterface.h @@ -16,6 +16,7 @@ #include "dcbase.h" #include "dcSubatomicType.h" +#include "vector_uchar.h" class DCFile; class DCField; @@ -37,8 +38,8 @@ enum DCPackType { // These PackTypes are all fundamental types, and should be packed (or // unpacked) with the corresponding call to pack_double(), pack_int(), etc. - // PT_blob is the same as PT_string, but implies that the string contains - // binary data. + // PT_blob is similar to PT_string, except that it contains arbitrary binary + // data instead of just UTF-8 text. PT_double, PT_int, PT_uint, @@ -113,6 +114,8 @@ public: bool &pack_error, bool &range_error) const; virtual void pack_string(DCPackData &pack_data, const std::string &value, bool &pack_error, bool &range_error) const; + virtual void pack_blob(DCPackData &pack_data, const vector_uchar &value, + bool &pack_error, bool &range_error) const; virtual bool pack_default_value(DCPackData &pack_data, bool &pack_error) const; virtual void unpack_double(const char *data, size_t length, size_t &p, @@ -127,6 +130,8 @@ public: uint64_t &value, bool &pack_error, bool &range_error) const; virtual void unpack_string(const char *data, size_t length, size_t &p, std::string &value, bool &pack_error, bool &range_error) const; + virtual void unpack_blob(const char *data, size_t length, size_t &p, + vector_uchar &value, bool &pack_error, bool &range_error) const; virtual bool unpack_validate(const char *data, size_t length, size_t &p, bool &pack_error, bool &range_error) const; virtual bool unpack_skip(const char *data, size_t length, size_t &p, diff --git a/direct/src/dcparser/dcParser.cxx.prebuilt b/direct/src/dcparser/dcParser.cxx.prebuilt index 80df4b2b09..399e4af2f9 100644 --- a/direct/src/dcparser/dcParser.cxx.prebuilt +++ b/direct/src/dcparser/dcParser.cxx.prebuilt @@ -1,20 +1,19 @@ -/* A Bison parser, made by GNU Bison 2.4.2. */ +/* A Bison parser, made by GNU Bison 3.1. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018 Free Software Foundation, Inc. -/* Skeleton implementation for Bison's Yacc-like parsers in C - - Copyright (C) 1984, 1989-1990, 2000-2006, 2009-2010 Free Software - Foundation, Inc. - This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program. If not, see . */ @@ -27,7 +26,7 @@ special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. - + This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ @@ -45,7 +44,7 @@ #define YYBISON 1 /* Bison version. */ -#define YYBISON_VERSION "2.4.2" +#define YYBISON_VERSION "3.1" /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" @@ -59,23 +58,19 @@ /* Pull parsers. */ #define YYPULL 1 -/* Using locations. */ -#define YYLSP_NEEDED 0 /* Substitute the variable and function names. */ #define yyparse dcyyparse #define yylex dcyylex #define yyerror dcyyerror -#define yylval dcyylval -#define yychar dcyychar #define yydebug dcyydebug #define yynerrs dcyynerrs +#define yylval dcyylval +#define yychar dcyychar /* Copy the first part of user declarations. */ - -/* Line 189 of yacc.c */ -#line 6 "dcParser.yxx" +#line 7 "direct/src/dcparser/dcParser.yxx" /* yacc.c:339 */ #include "dcLexerDefs.h" #include "dcParserDefs.h" @@ -105,18 +100,18 @@ using std::istream; using std::ostringstream; using std::string; -DCFile *dc_file = (DCFile *)NULL; -static DCClass *current_class = (DCClass *)NULL; -static DCSwitch *current_switch = (DCSwitch *)NULL; -static DCAtomicField *current_atomic = (DCAtomicField *)NULL; -static DCMolecularField *current_molecular = (DCMolecularField *)NULL; -static DCParameter *current_parameter = (DCParameter *)NULL; +DCFile *dc_file = nullptr; +static DCClass *current_class = nullptr; +static DCSwitch *current_switch = nullptr; +static DCAtomicField *current_atomic = nullptr; +static DCMolecularField *current_molecular = nullptr; +static DCParameter *current_parameter = nullptr; static DCKeywordList current_keyword_list; static DCPacker default_packer; static DCPacker *current_packer; static DCDoubleRange double_range; static DCUnsignedIntRange uint_range; -static DCField *parameter_description = (DCField *)NULL; +static DCField *parameter_description = nullptr; //////////////////////////////////////////////////////////////////// // Defining the interface to the parser. @@ -129,9 +124,9 @@ dc_init_parser(istream &in, const string &filename, DCFile &file) { } void -dc_init_parser_parameter_value(istream &in, const string &filename, +dc_init_parser_parameter_value(istream &in, const string &filename, DCPacker &packer) { - dc_file = NULL; + dc_file = nullptr; current_packer = &packer; dc_init_lexer(in, filename); dc_start_parameter_value(); @@ -142,7 +137,7 @@ dc_init_parser_parameter_description(istream &in, const string &filename, DCFile *file) { dc_file = file; dc_init_lexer(in, filename); - parameter_description = NULL; + parameter_description = nullptr; dc_start_parameter_description(); } @@ -153,18 +148,19 @@ dc_get_parameter_description() { void dc_cleanup_parser() { - dc_file = (DCFile *)NULL; + dc_file = nullptr; } +#line 156 "built/tmp/dcParser.yxx.c" /* yacc.c:339 */ -/* Line 189 of yacc.c */ -#line 159 "y.tab.c" - -/* Enabling traces. */ -#ifndef YYDEBUG -# define YYDEBUG 0 -#endif +# ifndef YY_NULLPTR +# if defined __cplusplus && 201103L <= __cplusplus +# define YY_NULLPTR nullptr +# else +# define YY_NULLPTR 0 +# endif +# endif /* Enabling verbose error messages. */ #ifdef YYERROR_VERBOSE @@ -174,67 +170,72 @@ dc_cleanup_parser() { # define YYERROR_VERBOSE 0 #endif -/* Enabling the token table. */ -#ifndef YYTOKEN_TABLE -# define YYTOKEN_TABLE 0 +/* In a future release of Bison, this section will be replaced + by #include "dcParser.yxx.h". */ +#ifndef YY_DCYY_BUILT_TMP_DCPARSER_YXX_H_INCLUDED +# define YY_DCYY_BUILT_TMP_DCPARSER_YXX_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int dcyydebug; #endif - -/* Tokens. */ +/* Token type. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE - /* Put the tokens into the symbol table, so that GDB and other debuggers - know about them. */ - enum yytokentype { - UNSIGNED_INTEGER = 258, - SIGNED_INTEGER = 259, - REAL = 260, - STRING = 261, - HEX_STRING = 262, - IDENTIFIER = 263, - KEYWORD = 264, - KW_DCLASS = 265, - KW_STRUCT = 266, - KW_FROM = 267, - KW_IMPORT = 268, - KW_TYPEDEF = 269, - KW_KEYWORD = 270, - KW_SWITCH = 271, - KW_CASE = 272, - KW_DEFAULT = 273, - KW_BREAK = 274, - KW_INT8 = 275, - KW_INT16 = 276, - KW_INT32 = 277, - KW_INT64 = 278, - KW_UINT8 = 279, - KW_UINT16 = 280, - KW_UINT32 = 281, - KW_UINT64 = 282, - KW_FLOAT64 = 283, - KW_STRING = 284, - KW_BLOB = 285, - KW_BLOB32 = 286, - KW_INT8ARRAY = 287, - KW_INT16ARRAY = 288, - KW_INT32ARRAY = 289, - KW_UINT8ARRAY = 290, - KW_UINT16ARRAY = 291, - KW_UINT32ARRAY = 292, - KW_UINT32UINT8ARRAY = 293, - KW_CHAR = 294, - START_DC = 295, - START_PARAMETER_VALUE = 296, - START_PARAMETER_DESCRIPTION = 297 - }; + enum yytokentype + { + UNSIGNED_INTEGER = 258, + SIGNED_INTEGER = 259, + REAL = 260, + STRING = 261, + IDENTIFIER = 262, + HEX_STRING = 263, + KEYWORD = 264, + KW_DCLASS = 265, + KW_STRUCT = 266, + KW_FROM = 267, + KW_IMPORT = 268, + KW_TYPEDEF = 269, + KW_KEYWORD = 270, + KW_SWITCH = 271, + KW_CASE = 272, + KW_DEFAULT = 273, + KW_BREAK = 274, + KW_INT8 = 275, + KW_INT16 = 276, + KW_INT32 = 277, + KW_INT64 = 278, + KW_UINT8 = 279, + KW_UINT16 = 280, + KW_UINT32 = 281, + KW_UINT64 = 282, + KW_FLOAT64 = 283, + KW_STRING = 284, + KW_BLOB = 285, + KW_BLOB32 = 286, + KW_INT8ARRAY = 287, + KW_INT16ARRAY = 288, + KW_INT32ARRAY = 289, + KW_UINT8ARRAY = 290, + KW_UINT16ARRAY = 291, + KW_UINT32ARRAY = 292, + KW_UINT32UINT8ARRAY = 293, + KW_CHAR = 294, + START_DC = 295, + START_PARAMETER_VALUE = 296, + START_PARAMETER_DESCRIPTION = 297 + }; #endif /* Tokens. */ #define UNSIGNED_INTEGER 258 #define SIGNED_INTEGER 259 #define REAL 260 #define STRING 261 -#define HEX_STRING 262 -#define IDENTIFIER 263 +#define IDENTIFIER 262 +#define HEX_STRING 263 #define KEYWORD 264 #define KW_DCLASS 265 #define KW_STRUCT 266 @@ -270,21 +271,18 @@ dc_cleanup_parser() { #define START_PARAMETER_VALUE 296 #define START_PARAMETER_DESCRIPTION 297 +/* Value type. */ +extern YYSTYPE dcyylval; -#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED - -# define yystype YYSTYPE /* obsolescent; will be withdrawn */ -# define YYSTYPE_IS_DECLARED 1 -#endif +int dcyyparse (void); +#endif /* !YY_DCYY_BUILT_TMP_DCPARSER_YXX_H_INCLUDED */ /* Copy the second part of user declarations. */ - -/* Line 264 of yacc.c */ -#line 284 "y.tab.c" +#line 286 "built/tmp/dcParser.yxx.c" /* yacc.c:358 */ #ifdef short # undef short @@ -298,23 +296,20 @@ typedef unsigned char yytype_uint8; #ifdef YYTYPE_INT8 typedef YYTYPE_INT8 yytype_int8; -#elif (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -typedef signed char yytype_int8; #else -typedef short int yytype_int8; +typedef signed char yytype_int8; #endif #ifdef YYTYPE_UINT16 typedef YYTYPE_UINT16 yytype_uint16; #else -typedef unsigned short int yytype_uint16; +typedef unsigned short yytype_uint16; #endif #ifdef YYTYPE_INT16 typedef YYTYPE_INT16 yytype_int16; #else -typedef short int yytype_int16; +typedef short yytype_int16; #endif #ifndef YYSIZE_T @@ -322,12 +317,11 @@ typedef short int yytype_int16; # define YYSIZE_T __SIZE_TYPE__ # elif defined size_t # define YYSIZE_T size_t -# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) +# elif ! defined YYSIZE_T # include /* INFRINGES ON USER NAME SPACE */ # define YYSIZE_T size_t # else -# define YYSIZE_T unsigned int +# define YYSIZE_T unsigned # endif #endif @@ -337,38 +331,67 @@ typedef short int yytype_int16; # if defined YYENABLE_NLS && YYENABLE_NLS # if ENABLE_NLS # include /* INFRINGES ON USER NAME SPACE */ -# define YY_(msgid) dgettext ("bison-runtime", msgid) +# define YY_(Msgid) dgettext ("bison-runtime", Msgid) # endif # endif # ifndef YY_ -# define YY_(msgid) msgid +# define YY_(Msgid) Msgid +# endif +#endif + +#ifndef YY_ATTRIBUTE +# if (defined __GNUC__ \ + && (2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__))) \ + || defined __SUNPRO_C && 0x5110 <= __SUNPRO_C +# define YY_ATTRIBUTE(Spec) __attribute__(Spec) +# else +# define YY_ATTRIBUTE(Spec) /* empty */ +# endif +#endif + +#ifndef YY_ATTRIBUTE_PURE +# define YY_ATTRIBUTE_PURE YY_ATTRIBUTE ((__pure__)) +#endif + +#ifndef YY_ATTRIBUTE_UNUSED +# define YY_ATTRIBUTE_UNUSED YY_ATTRIBUTE ((__unused__)) +#endif + +#if !defined _Noreturn \ + && (!defined __STDC_VERSION__ || __STDC_VERSION__ < 201112) +# if defined _MSC_VER && 1200 <= _MSC_VER +# define _Noreturn __declspec (noreturn) +# else +# define _Noreturn YY_ATTRIBUTE ((__noreturn__)) # endif #endif /* Suppress unused-variable warnings by "using" E. */ #if ! defined lint || defined __GNUC__ -# define YYUSE(e) ((void) (e)) +# define YYUSE(E) ((void) (E)) #else -# define YYUSE(e) /* empty */ +# define YYUSE(E) /* empty */ #endif -/* Identity function, used to suppress warnings about constant conditions. */ -#ifndef lint -# define YYID(n) (n) +#if defined __GNUC__ && ! defined __ICC && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ +/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")\ + _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ + _Pragma ("GCC diagnostic pop") #else -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -static int -YYID (int yyi) -#else -static int -YYID (yyi) - int yyi; +# define YY_INITIAL_VALUE(Value) Value #endif -{ - return yyi; -} +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END #endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + #if ! defined yyoverflow || YYERROR_VERBOSE @@ -387,11 +410,11 @@ YYID (yyi) # define alloca _alloca # else # define YYSTACK_ALLOC alloca -# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS # include /* INFRINGES ON USER NAME SPACE */ -# ifndef _STDLIB_H -# define _STDLIB_H 1 + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 # endif # endif # endif @@ -399,8 +422,8 @@ YYID (yyi) # endif # ifdef YYSTACK_ALLOC - /* Pacify GCC's `empty if-body' warning. */ -# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) + /* Pacify GCC's 'empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) # ifndef YYSTACK_ALLOC_MAXIMUM /* The OS might guarantee only one guard page at the bottom of the stack, and a page size can be as small as 4096 bytes. So we cannot safely @@ -414,25 +437,23 @@ YYID (yyi) # ifndef YYSTACK_ALLOC_MAXIMUM # define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM # endif -# if (defined __cplusplus && ! defined _STDLIB_H \ +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ && ! ((defined YYMALLOC || defined malloc) \ - && (defined YYFREE || defined free))) + && (defined YYFREE || defined free))) # include /* INFRINGES ON USER NAME SPACE */ -# ifndef _STDLIB_H -# define _STDLIB_H 1 +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 # endif # endif # ifndef YYMALLOC # define YYMALLOC malloc -# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) +# if ! defined malloc && ! defined EXIT_SUCCESS void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ # endif # endif # ifndef YYFREE # define YYFREE free -# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) +# if ! defined free && ! defined EXIT_SUCCESS void free (void *); /* INFRINGES ON USER NAME SPACE */ # endif # endif @@ -442,7 +463,7 @@ void free (void *); /* INFRINGES ON USER NAME SPACE */ #if (! defined yyoverflow \ && (! defined __cplusplus \ - || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) /* A type that is properly aligned for any stack member. */ union yyalloc @@ -460,46 +481,50 @@ union yyalloc ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + YYSTACK_GAP_MAXIMUM) -/* Copy COUNT objects from FROM to TO. The source and destination do - not overlap. */ -# ifndef YYCOPY -# if defined __GNUC__ && 1 < __GNUC__ -# define YYCOPY(To, From, Count) \ - __builtin_memcpy (To, From, (Count) * sizeof (*(From))) -# else -# define YYCOPY(To, From, Count) \ - do \ - { \ - YYSIZE_T yyi; \ - for (yyi = 0; yyi < (Count); yyi++) \ - (To)[yyi] = (From)[yyi]; \ - } \ - while (YYID (0)) -# endif -# endif +# define YYCOPY_NEEDED 1 /* Relocate STACK from its old location to the new one. The local variables YYSIZE and YYSTACKSIZE give the old and new number of elements in the stack, and YYPTR gives the new location of the stack. Advance YYPTR to a properly aligned location for the next stack. */ -# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ - do \ - { \ - YYSIZE_T yynewbytes; \ - YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ - Stack = &yyptr->Stack_alloc; \ - yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ - yyptr += yynewbytes / sizeof (*yyptr); \ - } \ - while (YYID (0)) +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (0) #endif +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (0) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + /* YYFINAL -- State number of the termination state. */ #define YYFINAL 57 /* YYLAST -- Last index in YYTABLE. */ -#define YYLAST 430 +#define YYLAST 431 /* YYNTOKENS -- Number of terminals. */ #define YYNTOKENS 58 @@ -507,17 +532,19 @@ union yyalloc #define YYNNTS 82 /* YYNRULES -- Number of rules. */ #define YYNRULES 190 -/* YYNRULES -- Number of states. */ +/* YYNSTATES -- Number of states. */ #define YYNSTATES 282 -/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ +/* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned + by yylex, with out-of-bounds checking. */ #define YYUNDEFTOK 2 #define YYMAXUTOK 297 -#define YYTRANSLATE(YYX) \ - ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) +#define YYTRANSLATE(YYX) \ + ((unsigned) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) -/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ +/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM + as returned by yylex, without out-of-bounds checking. */ static const yytype_uint8 yytranslate[] = { 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, @@ -553,123 +580,39 @@ static const yytype_uint8 yytranslate[] = }; #if YYDEBUG -/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in - YYRHS. */ -static const yytype_uint16 yyprhs[] = -{ - 0, 0, 3, 6, 9, 12, 14, 17, 20, 23, - 26, 29, 32, 34, 38, 40, 44, 47, 48, 54, - 56, 58, 60, 64, 67, 70, 72, 75, 78, 80, - 82, 83, 91, 93, 95, 98, 100, 104, 106, 109, - 113, 116, 119, 122, 125, 126, 134, 136, 138, 141, - 143, 147, 149, 152, 156, 159, 162, 165, 168, 169, - 175, 177, 179, 181, 185, 187, 188, 192, 194, 196, - 197, 202, 204, 205, 210, 212, 214, 216, 218, 220, - 222, 225, 228, 231, 233, 238, 242, 246, 248, 250, - 252, 254, 256, 258, 262, 265, 269, 275, 280, 282, - 284, 288, 291, 295, 301, 306, 308, 313, 315, 319, - 323, 328, 330, 332, 334, 336, 338, 340, 342, 344, - 346, 348, 350, 352, 353, 358, 360, 362, 364, 366, - 368, 369, 374, 375, 380, 381, 386, 390, 394, 398, - 402, 404, 407, 409, 411, 413, 417, 419, 421, 423, - 425, 427, 429, 431, 433, 435, 437, 439, 441, 443, - 445, 447, 449, 451, 453, 455, 457, 459, 462, 464, - 465, 470, 472, 474, 478, 480, 482, 483, 493, 495, - 498, 501, 504, 508, 512, 513, 518, 521, 523, 525, - 527 -}; - -/* YYRHS -- A `-1'-separated list of the rules' RHS. */ -static const yytype_int16 yyrhs[] = -{ - 59, 0, -1, 40, 60, -1, 41, 114, -1, 42, - 100, -1, 139, -1, 60, 43, -1, 60, 70, -1, - 60, 131, -1, 60, 63, -1, 60, 67, -1, 60, - 68, -1, 8, -1, 61, 44, 8, -1, 61, -1, - 62, 45, 61, -1, 13, 62, -1, -1, 12, 62, - 13, 64, 65, -1, 66, -1, 46, -1, 61, -1, - 66, 47, 61, -1, 14, 98, -1, 15, 69, -1, - 139, -1, 69, 8, -1, 69, 9, -1, 71, -1, - 78, -1, -1, 10, 130, 72, 74, 48, 76, 49, - -1, 8, -1, 139, -1, 50, 75, -1, 73, -1, - 75, 47, 73, -1, 139, -1, 76, 43, -1, 76, - 77, 43, -1, 85, 124, -1, 126, 125, -1, 95, - 124, -1, 93, 124, -1, -1, 11, 130, 79, 81, - 48, 83, 49, -1, 8, -1, 139, -1, 50, 82, - -1, 80, -1, 82, 47, 80, -1, 139, -1, 83, - 43, -1, 83, 84, 43, -1, 85, 125, -1, 126, - 125, -1, 95, 125, -1, 93, 125, -1, -1, 130, - 51, 86, 87, 52, -1, 139, -1, 88, -1, 89, - -1, 88, 47, 89, -1, 98, -1, -1, 105, 91, - 106, -1, 105, -1, 90, -1, -1, 90, 53, 94, - 114, -1, 92, -1, -1, 92, 53, 96, 114, -1, - 90, -1, 92, -1, 93, -1, 95, -1, 97, -1, - 85, -1, 85, 125, -1, 95, 125, -1, 93, 125, - -1, 123, -1, 101, 51, 103, 52, -1, 101, 44, - 108, -1, 101, 54, 112, -1, 101, -1, 8, -1, - 78, -1, 131, -1, 139, -1, 113, -1, 113, 55, - 113, -1, 113, 112, -1, 103, 47, 113, -1, 103, - 47, 113, 55, 113, -1, 103, 47, 113, 112, -1, - 139, -1, 107, -1, 107, 55, 107, -1, 107, 109, - -1, 104, 47, 107, -1, 104, 47, 107, 55, 107, - -1, 104, 47, 107, 109, -1, 102, -1, 105, 56, - 104, 57, -1, 8, -1, 106, 44, 108, -1, 106, - 54, 112, -1, 106, 56, 104, 57, -1, 6, -1, - 108, -1, 3, -1, 4, -1, 4, -1, 3, -1, - 111, -1, 110, -1, 5, -1, 6, -1, 112, -1, - 116, -1, -1, 8, 53, 115, 116, -1, 110, -1, - 111, -1, 5, -1, 6, -1, 7, -1, -1, 48, - 117, 120, 49, -1, -1, 56, 118, 120, 57, -1, - -1, 51, 119, 120, 52, -1, 110, 46, 108, -1, - 111, 46, 108, -1, 5, 46, 108, -1, 7, 46, - 108, -1, 121, -1, 122, 121, -1, 139, -1, 47, - -1, 114, -1, 122, 47, 114, -1, 20, -1, 21, - -1, 22, -1, 23, -1, 24, -1, 25, -1, 26, - -1, 27, -1, 28, -1, 29, -1, 30, -1, 31, - -1, 32, -1, 33, -1, 34, -1, 35, -1, 36, - -1, 37, -1, 38, -1, 39, -1, 139, -1, 124, - 9, -1, 124, -1, -1, 8, 50, 127, 129, -1, - 8, -1, 128, -1, 129, 47, 128, -1, 139, -1, - 8, -1, -1, 16, 130, 51, 99, 52, 48, 132, - 133, 49, -1, 139, -1, 133, 43, -1, 133, 134, - -1, 133, 136, -1, 133, 137, 43, -1, 133, 138, - 43, -1, -1, 17, 135, 114, 50, -1, 18, 50, - -1, 19, -1, 95, -1, 93, -1, -1 -}; - -/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ + /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ static const yytype_uint16 yyrline[] = { - 0, 168, 168, 169, 170, 177, 178, 179, 190, 196, - 197, 198, 202, 203, 210, 211, 218, 223, 222, 230, - 231, 238, 242, 249, 267, 271, 272, 276, 287, 288, - 293, 292, 304, 327, 328, 332, 338, 352, 353, 354, - 367, 377, 378, 386, 397, 396, 408, 431, 432, 436, - 442, 451, 452, 453, 464, 471, 472, 476, 484, 483, - 501, 502, 506, 507, 511, 521, 520, 531, 535, 537, - 536, 565, 567, 566, 595, 596, 600, 601, 605, 609, - 616, 620, 624, 631, 635, 644, 660, 675, 676, 708, - 724, 743, 747, 754, 761, 770, 776, 782, 793, 797, - 804, 811, 818, 824, 830, 839, 840, 851, 856, 871, - 886, 893, 902, 906, 917, 931, 935, 939, 943, 947, - 951, 960, 965, 969, 968, 983, 987, 991, 995, 999, - 1004, 1003, 1012, 1011, 1020, 1019, 1027, 1033, 1039, 1045, - 1054, 1055, 1059, 1060, 1064, 1065, 1069, 1073, 1077, 1081, - 1085, 1089, 1093, 1097, 1101, 1105, 1109, 1113, 1117, 1121, - 1125, 1129, 1133, 1137, 1141, 1145, 1152, 1156, 1163, 1173, - 1172, 1183, 1210, 1216, 1230, 1234, 1239, 1238, 1251, 1252, - 1253, 1254, 1255, 1256, 1270, 1269, 1290, 1299, 1306, 1310, - 1316 + 0, 174, 174, 175, 176, 183, 184, 185, 196, 202, + 203, 204, 208, 209, 216, 217, 224, 229, 228, 236, + 237, 244, 248, 255, 273, 277, 278, 282, 293, 294, + 299, 298, 310, 333, 334, 338, 344, 358, 359, 360, + 373, 383, 384, 392, 403, 402, 414, 437, 438, 442, + 448, 457, 458, 459, 470, 477, 478, 482, 490, 489, + 507, 508, 512, 513, 517, 527, 526, 537, 541, 543, + 542, 571, 573, 572, 601, 602, 606, 607, 611, 615, + 622, 626, 630, 637, 641, 650, 666, 681, 682, 714, + 730, 749, 753, 760, 767, 776, 782, 788, 799, 803, + 810, 817, 824, 830, 836, 845, 846, 857, 862, 877, + 892, 899, 908, 912, 923, 937, 941, 945, 949, 953, + 957, 966, 971, 975, 974, 989, 993, 997, 1001, 1005, + 1010, 1009, 1018, 1017, 1026, 1025, 1033, 1039, 1045, 1051, + 1060, 1061, 1065, 1066, 1070, 1071, 1075, 1079, 1083, 1087, + 1091, 1095, 1099, 1103, 1107, 1111, 1115, 1119, 1123, 1127, + 1131, 1135, 1139, 1143, 1147, 1151, 1158, 1162, 1169, 1179, + 1178, 1189, 1216, 1222, 1236, 1240, 1245, 1244, 1257, 1258, + 1259, 1260, 1261, 1262, 1276, 1275, 1296, 1305, 1312, 1316, + 1322 }; #endif -#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE +#if YYDEBUG || YYERROR_VERBOSE || 0 /* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. First, the terminals, then, starting at YYNTOKENS, nonterminals. */ static const char *const yytname[] = { "$end", "error", "$undefined", "UNSIGNED_INTEGER", "SIGNED_INTEGER", - "REAL", "STRING", "HEX_STRING", "IDENTIFIER", "KEYWORD", "KW_DCLASS", + "REAL", "STRING", "IDENTIFIER", "HEX_STRING", "KEYWORD", "KW_DCLASS", "KW_STRUCT", "KW_FROM", "KW_IMPORT", "KW_TYPEDEF", "KW_KEYWORD", "KW_SWITCH", "KW_CASE", "KW_DEFAULT", "KW_BREAK", "KW_INT8", "KW_INT16", "KW_INT32", "KW_INT64", "KW_UINT8", "KW_UINT16", "KW_UINT32", @@ -697,13 +640,13 @@ static const char *const yytname[] = "array_def", "type_token", "keyword_list", "no_keyword_list", "molecular_field", "$@12", "atomic_name", "molecular_atom_list", "optional_name", "switch", "@13", "switch_fields", "switch_case", "$@14", - "switch_default", "switch_break", "switch_field", "empty", 0 + "switch_default", "switch_break", "switch_field", "empty", YY_NULLPTR }; #endif # ifdef YYPRINT -/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to - token YYLEX-NUM. */ +/* YYTOKNUM[NUM] -- (External) token number corresponding to the + (internal) symbol number NUM (which must be that of a token). */ static const yytype_uint16 yytoknum[] = { 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, @@ -715,7 +658,250 @@ static const yytype_uint16 yytoknum[] = }; # endif -/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +#define YYPACT_NINF -127 + +#define yypact_value_is_default(Yystate) \ + (!!((Yystate) == (-127))) + +#define YYTABLE_NINF -176 + +#define yytable_value_is_error(Yytable_value) \ + 0 + + /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +static const yytype_int16 yypact[] = +{ + 61, -127, 104, 359, 19, 128, -127, -127, -127, -23, + -127, -27, 17, -127, -127, -127, 44, 69, -127, -127, + 15, 42, 42, -127, -127, -127, -127, -127, -127, -127, + -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, + -127, -127, -127, -127, -127, 46, 76, -127, -127, -127, + 4, -127, 27, -127, 22, -127, -127, -127, 42, 63, + 63, 392, -127, -127, -127, -127, -127, -127, -127, -127, + -127, 117, -127, 117, 24, 24, 24, 117, 117, -127, + -127, 96, 142, -127, -127, -127, -127, -127, -127, 117, + 153, 158, 53, 146, -127, -127, -127, 110, 1, 119, + -127, -127, -127, -127, 141, -127, -127, -127, 122, -127, + -127, -127, 116, -127, 120, -127, 114, 111, -127, -127, + 124, 359, -127, 104, 104, -127, -127, -127, 67, -127, + -127, -127, 33, -127, -127, -127, -12, 7, -127, -127, + -127, 20, 392, 125, 162, -127, 63, -127, -127, -127, + -127, 104, -127, -127, -127, 165, 129, -127, -127, -127, + -127, -127, 130, -127, -127, 153, -127, 153, -127, 53, + -127, -127, 53, -127, 117, 158, 53, 131, 133, -127, + -127, -127, 169, 136, -127, -127, 11, 110, -127, -127, + -127, 134, -127, 137, 36, -127, 12, -127, -127, -127, + -4, -127, 392, -127, -127, 139, -127, -127, 110, -127, + 140, 165, 291, -127, -127, 153, -127, 53, -127, -127, + -127, 169, 325, -127, 63, -127, 31, -127, -127, 145, + -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, + 151, -127, -127, -127, -127, 110, -127, -127, -127, -127, + -127, -127, 257, -127, -127, 142, 142, 142, -127, 182, + -127, 147, -127, -127, -127, -127, -127, -127, -127, 155, + 156, -127, -127, 149, 104, -127, -127, -127, 182, 150, + -127, -127 +}; + + /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE does not specify something else to do. Zero + means the default is an error. */ +static const yytype_uint8 yydefact[] = +{ + 0, 190, 0, 190, 0, 2, 5, 116, 115, 127, + 128, 0, 129, 130, 134, 132, 125, 126, 3, 122, + 88, 190, 190, 146, 147, 148, 149, 150, 151, 152, + 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, + 163, 164, 165, 89, 190, 68, 71, 190, 190, 4, + 87, 105, 67, 83, 0, 90, 174, 1, 190, 0, + 0, 0, 190, 6, 9, 10, 11, 7, 28, 29, + 8, 0, 123, 0, 190, 190, 190, 0, 0, 175, + 44, 0, 168, 80, 166, 69, 72, 82, 81, 0, + 190, 0, 190, 0, 58, 30, 12, 14, 0, 16, + 88, 76, 77, 23, 24, 25, 113, 138, 0, 139, + 143, 144, 0, 140, 190, 142, 0, 0, 136, 137, + 190, 190, 167, 0, 0, 85, 119, 120, 0, 118, + 117, 121, 92, 91, 86, 111, 0, 99, 112, 98, + 107, 66, 190, 190, 0, 17, 0, 26, 27, 124, + 131, 143, 141, 135, 133, 0, 0, 47, 79, 74, + 75, 78, 0, 70, 73, 0, 84, 0, 94, 0, + 106, 114, 0, 101, 0, 0, 190, 0, 61, 62, + 64, 60, 0, 0, 33, 13, 0, 15, 145, 46, + 49, 48, 190, 0, 95, 93, 102, 100, 108, 109, + 0, 59, 0, 32, 35, 34, 190, 20, 21, 18, + 19, 0, 190, 51, 176, 0, 97, 0, 104, 110, + 63, 0, 190, 37, 0, 50, 88, 52, 45, 0, + 190, 190, 190, 190, 190, 96, 103, 36, 38, 31, + 0, 190, 190, 190, 190, 22, 169, 53, 54, 57, + 56, 55, 0, 178, 39, 40, 43, 42, 41, 0, + 184, 0, 187, 179, 177, 189, 188, 180, 181, 0, + 0, 171, 172, 170, 0, 186, 182, 183, 0, 0, + 173, 185 +}; + + /* YYPGOTO[NTERM-NUM]. */ +static const yytype_int16 yypgoto[] = +{ + -127, -127, -127, -126, 143, -127, -127, -127, -127, -127, + -127, -127, -127, -127, -127, -20, -127, -127, -127, -127, + 197, -127, -6, -127, -127, -127, -127, -116, -127, -127, + -127, 5, 85, -127, 87, -1, -127, 0, -127, -127, + 152, -127, -127, -127, -127, -127, 34, -127, -127, -125, + -56, 18, -81, -78, -90, -115, -2, -127, 108, -127, + -127, -127, 60, 103, -127, -127, -110, -40, -3, -127, + -60, -127, 47, 215, -127, -127, -127, -127, -127, -127, + -127, 3 +}; + + /* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int16 yydefgoto[] = +{ + -1, 4, 5, 97, 98, 64, 186, 209, 210, 65, + 66, 104, 67, 68, 143, 204, 183, 205, 222, 240, + 43, 120, 190, 156, 191, 212, 229, 44, 142, 177, + 178, 179, 45, 93, 46, 101, 123, 102, 124, 161, + 180, 162, 49, 50, 51, 128, 136, 52, 141, 137, + 138, 173, 16, 17, 131, 132, 111, 108, 19, 74, + 76, 75, 112, 113, 114, 53, 82, 83, 233, 259, + 272, 273, 54, 55, 234, 252, 267, 274, 268, 269, + 270, 84 +}; + + /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule whose + number is the opposite. If YYTABLE_NINF, syntax error. */ +static const yytype_int16 yytable[] = +{ + 18, 134, 47, 48, 6, 158, 56, 87, 88, 129, + 129, 171, 130, 130, 145, 107, 171, 109, 96, 57, + 187, 118, 119, 71, 56, 56, 72, 7, 8, 9, + 10, 11, 12, 125, -65, 169, 7, 8, 126, 7, + 8, 126, 168, 169, 196, 170, 146, 197, 89, 79, + 194, 129, 195, 219, 130, 90, 106, 207, 91, 135, + 208, 56, 172, 73, 174, 105, -175, 217, 80, 81, + 96, 110, 13, 94, 175, 14, 176, 115, 115, 115, + 15, 246, -175, 92, 129, 199, 129, 130, 167, 130, + 77, 215, 236, 133, 129, 139, 230, 130, 245, 85, + 235, 1, 2, 3, 216, 95, 241, 7, 8, 9, + 10, 11, 12, 129, 165, 78, 130, 115, 198, 166, + 106, 163, 164, 157, 56, 7, 8, 9, 10, 86, + 12, 255, 256, 257, 129, 116, 117, 130, 58, 21, + 59, 60, 61, 62, 22, 181, 184, 121, 147, 188, + 148, 122, 13, 140, 144, 14, 7, 8, 126, 127, + 15, 7, 8, 126, 146, 150, 153, 151, 154, 185, + 13, 63, 189, 14, 155, 182, 203, 192, 15, 139, + 202, 211, 193, 201, 206, 214, 221, 224, 247, 271, + 248, 249, 250, 251, 254, 213, 278, 275, 276, 277, + 281, 237, 69, 99, 258, 225, 159, 220, 160, 223, + 200, 231, 232, 103, 218, 56, 149, 152, 280, 244, + 70, 242, 243, 0, 0, 56, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 253, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 265, 266, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 100, 0, 0, 0, 21, 0, + 0, 0, 279, 22, 260, 261, 262, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 0, 226, 0, + 263, 0, 21, 0, 0, 0, 264, 22, 0, 0, + 0, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 42, 0, 226, 0, 227, 0, 21, 0, 0, 0, + 228, 22, 0, 0, 0, 23, 24, 25, 26, 27, + 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, + 38, 39, 40, 41, 42, 0, 20, 0, 238, 0, + 21, 0, 0, 0, 239, 22, 0, 0, 0, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 100, + 0, 0, 0, 21, 0, 0, 0, 0, 22, 0, + 0, 0, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42 +}; + +static const yytype_int16 yycheck[] = +{ + 2, 91, 3, 3, 1, 121, 3, 47, 48, 90, + 91, 4, 90, 91, 13, 71, 4, 73, 7, 0, + 146, 77, 78, 46, 21, 22, 53, 3, 4, 5, + 6, 7, 8, 89, 7, 47, 3, 4, 5, 3, + 4, 5, 132, 47, 169, 57, 45, 172, 44, 7, + 165, 132, 167, 57, 132, 51, 3, 46, 54, 6, + 186, 58, 55, 46, 44, 62, 51, 55, 21, 22, + 7, 47, 48, 51, 54, 51, 56, 74, 75, 76, + 56, 50, 51, 56, 165, 175, 167, 165, 55, 167, + 46, 55, 217, 90, 175, 92, 212, 175, 224, 53, + 215, 40, 41, 42, 194, 58, 222, 3, 4, 5, + 6, 7, 8, 194, 47, 46, 194, 114, 174, 52, + 3, 123, 124, 120, 121, 3, 4, 5, 6, 53, + 8, 241, 242, 243, 215, 75, 76, 215, 10, 11, + 12, 13, 14, 15, 16, 142, 143, 51, 7, 151, + 9, 9, 48, 7, 44, 51, 3, 4, 5, 6, + 56, 3, 4, 5, 45, 49, 52, 47, 57, 7, + 48, 43, 7, 51, 50, 50, 7, 48, 56, 176, + 47, 47, 52, 52, 48, 48, 47, 47, 43, 7, + 230, 231, 232, 233, 43, 192, 47, 50, 43, 43, + 50, 221, 5, 60, 244, 211, 121, 202, 121, 206, + 176, 212, 212, 61, 196, 212, 108, 114, 278, 222, + 5, 222, 222, -1, -1, 222, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 234, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 252, 252, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 7, -1, -1, -1, 11, -1, + -1, -1, 274, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39, -1, 7, -1, + 43, -1, 11, -1, -1, -1, 49, 16, -1, -1, + -1, 20, 21, 22, 23, 24, 25, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, -1, 7, -1, 43, -1, 11, -1, -1, -1, + 49, 16, -1, -1, -1, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, -1, 7, -1, 43, -1, + 11, -1, -1, -1, 49, 16, -1, -1, -1, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32, 33, 34, 35, 36, 37, 38, 39, 7, + -1, -1, -1, 11, -1, -1, -1, -1, 16, -1, + -1, -1, 20, 21, 22, 23, 24, 25, 26, 27, + 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, + 38, 39 +}; + + /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_uint8 yystos[] = +{ + 0, 40, 41, 42, 59, 60, 139, 3, 4, 5, + 6, 7, 8, 48, 51, 56, 110, 111, 114, 116, + 7, 11, 16, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, + 37, 38, 39, 78, 85, 90, 92, 93, 95, 100, + 101, 102, 105, 123, 130, 131, 139, 0, 10, 12, + 13, 14, 15, 43, 63, 67, 68, 70, 71, 78, + 131, 46, 53, 46, 117, 119, 118, 46, 46, 7, + 130, 130, 124, 125, 139, 53, 53, 125, 125, 44, + 51, 54, 56, 91, 51, 130, 7, 61, 62, 62, + 7, 93, 95, 98, 69, 139, 3, 108, 115, 108, + 47, 114, 120, 121, 122, 139, 120, 120, 108, 108, + 79, 51, 9, 94, 96, 108, 5, 6, 103, 110, + 111, 112, 113, 139, 112, 6, 104, 107, 108, 139, + 7, 106, 86, 72, 44, 13, 45, 7, 9, 116, + 49, 47, 121, 52, 57, 50, 81, 139, 85, 90, + 92, 97, 99, 114, 114, 47, 52, 55, 112, 47, + 57, 4, 55, 109, 44, 54, 56, 87, 88, 89, + 98, 139, 50, 74, 139, 7, 64, 61, 114, 7, + 80, 82, 48, 52, 113, 113, 107, 107, 108, 112, + 104, 52, 47, 7, 73, 75, 48, 46, 61, 65, + 66, 47, 83, 139, 48, 55, 112, 55, 109, 57, + 89, 47, 76, 139, 47, 80, 7, 43, 49, 84, + 85, 93, 95, 126, 132, 113, 107, 73, 43, 49, + 77, 85, 93, 95, 126, 61, 50, 43, 125, 125, + 125, 125, 133, 139, 43, 124, 124, 124, 125, 127, + 17, 18, 19, 43, 49, 93, 95, 134, 136, 137, + 138, 7, 128, 129, 135, 50, 43, 43, 47, 114, + 128, 50 +}; + + /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ static const yytype_uint8 yyr1[] = { 0, 58, 59, 59, 59, 60, 60, 60, 60, 60, @@ -740,7 +926,7 @@ static const yytype_uint8 yyr1[] = 139 }; -/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ + /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ static const yytype_uint8 yyr2[] = { 0, 2, 2, 2, 2, 1, 2, 2, 2, 2, @@ -765,340 +951,41 @@ static const yytype_uint8 yyr2[] = 0 }; -/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state - STATE-NUM when YYTABLE doesn't specify something else to do. Zero - means the default is an error. */ -static const yytype_uint8 yydefact[] = -{ - 0, 190, 0, 190, 0, 2, 5, 116, 115, 127, - 128, 129, 0, 130, 134, 132, 125, 126, 3, 122, - 88, 190, 190, 146, 147, 148, 149, 150, 151, 152, - 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, - 163, 164, 165, 89, 190, 68, 71, 190, 190, 4, - 87, 105, 67, 83, 0, 90, 174, 1, 190, 0, - 0, 0, 190, 6, 9, 10, 11, 7, 28, 29, - 8, 0, 0, 123, 190, 190, 190, 0, 0, 175, - 44, 0, 168, 80, 166, 69, 72, 82, 81, 0, - 190, 0, 190, 0, 58, 30, 12, 14, 0, 16, - 88, 76, 77, 23, 24, 25, 113, 138, 139, 0, - 143, 144, 0, 140, 190, 142, 0, 0, 136, 137, - 190, 190, 167, 0, 0, 85, 119, 120, 0, 118, - 117, 121, 92, 91, 86, 111, 0, 99, 112, 98, - 107, 66, 190, 190, 0, 17, 0, 26, 27, 124, - 131, 143, 141, 135, 133, 0, 0, 47, 79, 74, - 75, 78, 0, 70, 73, 0, 84, 0, 94, 0, - 106, 114, 0, 101, 0, 0, 190, 0, 61, 62, - 64, 60, 0, 0, 33, 13, 0, 15, 145, 46, - 49, 48, 190, 0, 95, 93, 102, 100, 108, 109, - 0, 59, 0, 32, 35, 34, 190, 20, 21, 18, - 19, 0, 190, 51, 176, 0, 97, 0, 104, 110, - 63, 0, 190, 37, 0, 50, 88, 52, 45, 0, - 190, 190, 190, 190, 190, 96, 103, 36, 38, 31, - 0, 190, 190, 190, 190, 22, 169, 53, 54, 57, - 56, 55, 0, 178, 39, 40, 43, 42, 41, 0, - 184, 0, 187, 179, 177, 189, 188, 180, 181, 0, - 0, 171, 172, 170, 0, 186, 182, 183, 0, 0, - 173, 185 -}; -/* YYDEFGOTO[NTERM-NUM]. */ -static const yytype_int16 yydefgoto[] = -{ - -1, 4, 5, 97, 98, 64, 186, 209, 210, 65, - 66, 104, 67, 68, 143, 204, 183, 205, 222, 240, - 43, 120, 190, 156, 191, 212, 229, 44, 142, 177, - 178, 179, 45, 93, 46, 101, 123, 102, 124, 161, - 180, 162, 49, 50, 51, 128, 136, 52, 141, 137, - 138, 173, 16, 17, 131, 132, 111, 109, 19, 74, - 76, 75, 112, 113, 114, 53, 82, 83, 233, 259, - 272, 273, 54, 55, 234, 252, 267, 274, 268, 269, - 270, 84 -}; +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 -/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing - STATE-NUM. */ -#define YYPACT_NINF -127 -static const yytype_int16 yypact[] = -{ - 8, -127, 104, 359, 35, 128, -127, -127, -127, 20, - -127, 22, 28, -127, -127, -127, 37, 53, -127, -127, - 54, 139, 139, -127, -127, -127, -127, -127, -127, -127, - -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, - -127, -127, -127, -127, -127, 95, 97, -127, -127, -127, - 2, -127, 26, -127, 100, -127, -127, -127, 139, 145, - 145, 391, -127, -127, -127, -127, -127, -127, -127, -127, - -127, 151, 151, -127, 24, 24, 24, 151, 151, -127, - -127, 108, 152, -127, -127, -127, -127, -127, -127, 151, - 127, 98, 64, 154, -127, -127, -127, 119, 13, 120, - -127, -127, -127, -127, 106, -127, -127, -127, -127, 122, - -127, -127, 115, -127, 121, -127, 114, 110, -127, -127, - 124, 359, -127, 104, 104, -127, -127, -127, 12, -127, - -127, -127, 14, -127, -127, -127, 16, 7, -127, -127, - -127, 1, 391, 125, 161, -127, 145, -127, -127, -127, - -127, 104, -127, -127, -127, 164, 129, -127, -127, -127, - -127, -127, 130, -127, -127, 127, -127, 127, -127, 64, - -127, -127, 64, -127, 151, 98, 64, 131, 133, -127, - -127, -127, 168, 136, -127, -127, 6, 119, -127, -127, - -127, 134, -127, 137, 33, -127, 19, -127, -127, -127, - 43, -127, 391, -127, -127, 140, -127, -127, 119, -127, - 141, 164, 291, -127, -127, 127, -127, 64, -127, -127, - -127, 168, 325, -127, 145, -127, 69, -127, -127, 143, - -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, - 146, -127, -127, -127, -127, 119, -127, -127, -127, -127, - -127, -127, 257, -127, -127, 152, 152, 152, -127, 186, - -127, 147, -127, -127, -127, -127, -127, -127, -127, 153, - 155, -127, -127, 156, 104, -127, -127, -127, 186, 149, - -127, -127 -}; +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab -/* YYPGOTO[NTERM-NUM]. */ -static const yytype_int16 yypgoto[] = -{ - -127, -127, -127, -126, 142, -127, -127, -127, -127, -127, - -127, -127, -127, -127, -127, -21, -127, -127, -127, -127, - 196, -127, -6, -127, -127, -127, -127, -116, -127, -127, - -127, 4, 86, -127, 87, -1, -127, 0, -127, -127, - 157, -127, -127, -127, -127, -127, 34, -127, -127, -125, - -56, 17, -81, -78, -90, -124, -2, -127, 105, -127, - -127, -127, 60, 102, -127, -127, -85, -40, -5, -127, - -59, -127, 18, 215, -127, -127, -127, -127, -127, -127, - -127, 3 -}; - -/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If - positive, shift that token. If negative, reduce the rule which - number is the opposite. If zero, do what YYDEFACT says. - If YYTABLE_NINF, syntax error. */ -#define YYTABLE_NINF -176 -static const yytype_int16 yytable[] = -{ - 18, 134, 47, 48, 6, 158, 56, 87, 88, 129, - 129, 171, 130, 130, 96, 107, 108, 7, 8, 126, - 187, 118, 119, 171, 56, 56, 145, 7, 8, 9, - 10, 11, 12, 125, -65, 57, 7, 8, 126, 80, - 81, 194, 168, 195, 196, 174, 89, 197, 1, 2, - 3, 129, 207, 90, 130, 175, 91, 176, 146, 165, - 208, 56, 172, 169, 166, 105, 71, 106, 72, 167, - 135, 110, 13, 170, 217, 14, 95, 115, 115, 115, - 15, 73, 92, 77, 129, 199, 129, 130, 215, 130, - 169, 235, 236, 133, 129, 139, 230, 130, 245, 78, - 219, 7, 8, 126, 216, -175, 241, 7, 8, 9, - 10, 11, 12, 129, 147, 148, 130, 115, 198, 246, - -175, 163, 164, 157, 56, 7, 8, 9, 10, 11, - 7, 8, 126, 127, 129, 116, 117, 130, 58, 21, - 59, 60, 61, 62, 22, 181, 184, 79, 85, 188, - 86, 94, 13, 96, 106, 14, 255, 256, 257, 121, - 15, 122, 140, 144, 150, 146, 153, 154, 151, 185, - 13, 63, 189, 14, 155, 182, 203, 192, 15, 139, - 202, 211, 193, 201, 206, 214, 247, 221, 224, 254, - 248, 249, 250, 251, 271, 213, 276, 275, 277, 281, - 237, 69, 99, 278, 258, 225, 220, 159, 160, 223, - 200, 231, 232, 218, 149, 56, 152, 244, 103, 280, - 70, 242, 243, 0, 0, 56, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 253, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 265, 266, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 100, 0, 0, 21, 0, - 0, 0, 279, 22, 260, 261, 262, 23, 24, 25, - 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, - 36, 37, 38, 39, 40, 41, 42, 0, 0, 226, - 263, 0, 21, 0, 0, 0, 264, 22, 0, 0, - 0, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, - 42, 0, 0, 226, 227, 0, 21, 0, 0, 0, - 228, 22, 0, 0, 0, 23, 24, 25, 26, 27, - 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, - 38, 39, 40, 41, 42, 0, 0, 20, 238, 0, - 21, 0, 0, 0, 239, 22, 0, 0, 0, 23, - 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, - 34, 35, 36, 37, 38, 39, 40, 41, 42, 100, - 0, 0, 21, 0, 0, 0, 0, 22, 0, 0, - 0, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, - 42 -}; - -static const yytype_int16 yycheck[] = -{ - 2, 91, 3, 3, 1, 121, 3, 47, 48, 90, - 91, 4, 90, 91, 8, 71, 72, 3, 4, 5, - 146, 77, 78, 4, 21, 22, 13, 3, 4, 5, - 6, 7, 8, 89, 8, 0, 3, 4, 5, 21, - 22, 165, 132, 167, 169, 44, 44, 172, 40, 41, - 42, 132, 46, 51, 132, 54, 54, 56, 45, 47, - 186, 58, 55, 47, 52, 62, 46, 3, 46, 55, - 6, 47, 48, 57, 55, 51, 58, 74, 75, 76, - 56, 53, 56, 46, 165, 175, 167, 165, 55, 167, - 47, 215, 217, 90, 175, 92, 212, 175, 224, 46, - 57, 3, 4, 5, 194, 51, 222, 3, 4, 5, - 6, 7, 8, 194, 8, 9, 194, 114, 174, 50, - 51, 123, 124, 120, 121, 3, 4, 5, 6, 7, - 3, 4, 5, 6, 215, 75, 76, 215, 10, 11, - 12, 13, 14, 15, 16, 142, 143, 8, 53, 151, - 53, 51, 48, 8, 3, 51, 241, 242, 243, 51, - 56, 9, 8, 44, 49, 45, 52, 57, 47, 8, - 48, 43, 8, 51, 50, 50, 8, 48, 56, 176, - 47, 47, 52, 52, 48, 48, 43, 47, 47, 43, - 230, 231, 232, 233, 8, 192, 43, 50, 43, 50, - 221, 5, 60, 47, 244, 211, 202, 121, 121, 206, - 176, 212, 212, 196, 109, 212, 114, 222, 61, 278, - 5, 222, 222, -1, -1, 222, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, 234, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 252, 252, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, 8, -1, -1, 11, -1, - -1, -1, 274, 16, 17, 18, 19, 20, 21, 22, - 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, - 33, 34, 35, 36, 37, 38, 39, -1, -1, 8, - 43, -1, 11, -1, -1, -1, 49, 16, -1, -1, - -1, 20, 21, 22, 23, 24, 25, 26, 27, 28, - 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, - 39, -1, -1, 8, 43, -1, 11, -1, -1, -1, - 49, 16, -1, -1, -1, 20, 21, 22, 23, 24, - 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, - 35, 36, 37, 38, 39, -1, -1, 8, 43, -1, - 11, -1, -1, -1, 49, 16, -1, -1, -1, 20, - 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, - 31, 32, 33, 34, 35, 36, 37, 38, 39, 8, - -1, -1, 11, -1, -1, -1, -1, 16, -1, -1, - -1, 20, 21, 22, 23, 24, 25, 26, 27, 28, - 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, - 39 -}; - -/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing - symbol of state STATE-NUM. */ -static const yytype_uint8 yystos[] = -{ - 0, 40, 41, 42, 59, 60, 139, 3, 4, 5, - 6, 7, 8, 48, 51, 56, 110, 111, 114, 116, - 8, 11, 16, 20, 21, 22, 23, 24, 25, 26, - 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, - 37, 38, 39, 78, 85, 90, 92, 93, 95, 100, - 101, 102, 105, 123, 130, 131, 139, 0, 10, 12, - 13, 14, 15, 43, 63, 67, 68, 70, 71, 78, - 131, 46, 46, 53, 117, 119, 118, 46, 46, 8, - 130, 130, 124, 125, 139, 53, 53, 125, 125, 44, - 51, 54, 56, 91, 51, 130, 8, 61, 62, 62, - 8, 93, 95, 98, 69, 139, 3, 108, 108, 115, - 47, 114, 120, 121, 122, 139, 120, 120, 108, 108, - 79, 51, 9, 94, 96, 108, 5, 6, 103, 110, - 111, 112, 113, 139, 112, 6, 104, 107, 108, 139, - 8, 106, 86, 72, 44, 13, 45, 8, 9, 116, - 49, 47, 121, 52, 57, 50, 81, 139, 85, 90, - 92, 97, 99, 114, 114, 47, 52, 55, 112, 47, - 57, 4, 55, 109, 44, 54, 56, 87, 88, 89, - 98, 139, 50, 74, 139, 8, 64, 61, 114, 8, - 80, 82, 48, 52, 113, 113, 107, 107, 108, 112, - 104, 52, 47, 8, 73, 75, 48, 46, 61, 65, - 66, 47, 83, 139, 48, 55, 112, 55, 109, 57, - 89, 47, 76, 139, 47, 80, 8, 43, 49, 84, - 85, 93, 95, 126, 132, 113, 107, 73, 43, 49, - 77, 85, 93, 95, 126, 61, 50, 43, 125, 125, - 125, 125, 133, 139, 43, 124, 124, 124, 125, 127, - 17, 18, 19, 43, 49, 93, 95, 134, 136, 137, - 138, 8, 128, 129, 135, 50, 43, 43, 47, 114, - 128, 50 -}; - -#define yyerrok (yyerrstatus = 0) -#define yyclearin (yychar = YYEMPTY) -#define YYEMPTY (-2) -#define YYEOF 0 - -#define YYACCEPT goto yyacceptlab -#define YYABORT goto yyabortlab -#define YYERROR goto yyerrorlab - - -/* Like YYERROR except do call yyerror. This remains here temporarily - to ease the transition to the new meaning of YYERROR, for GCC. - Once GCC version 2 has supplanted version 1, this can go. However, - YYFAIL appears to be in use. Nevertheless, it is formally deprecated - in Bison 2.4.2's NEWS entry, where a plan to phase it out is - discussed. */ - -#define YYFAIL goto yyerrlab -#if defined YYFAIL - /* This is here to suppress warnings from the GCC cpp's - -Wunused-macros. Normally we don't worry about that warning, but - some users do, and we want to make it easy for users to remove - YYFAIL uses, which will produce warnings from Bison 2.5. */ -#endif #define YYRECOVERING() (!!yyerrstatus) -#define YYBACKUP(Token, Value) \ -do \ - if (yychar == YYEMPTY && yylen == 1) \ - { \ - yychar = (Token); \ - yylval = (Value); \ - yytoken = YYTRANSLATE (yychar); \ - YYPOPSTACK (1); \ - goto yybackup; \ - } \ - else \ - { \ +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ yyerror (YY_("syntax error: cannot back up")); \ - YYERROR; \ - } \ -while (YYID (0)) + YYERROR; \ + } \ +while (0) + +/* Error token number */ +#define YYTERROR 1 +#define YYERRCODE 256 -#define YYTERROR 1 -#define YYERRCODE 256 - - -/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. - If N is 0, then set CURRENT to the empty location which ends - the previous symbol: RHS[0] (always defined). */ - -#define YYRHSLOC(Rhs, K) ((Rhs)[K]) -#ifndef YYLLOC_DEFAULT -# define YYLLOC_DEFAULT(Current, Rhs, N) \ - do \ - if (YYID (N)) \ - { \ - (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ - (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ - (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ - (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ - } \ - else \ - { \ - (Current).first_line = (Current).last_line = \ - YYRHSLOC (Rhs, 0).last_line; \ - (Current).first_column = (Current).last_column = \ - YYRHSLOC (Rhs, 0).last_column; \ - } \ - while (YYID (0)) -#endif - - -/* YY_LOCATION_PRINT -- Print the location on the stream. - This macro was not mandated originally: define only if we know - we won't break user code: when these are the locations we know. */ - -#ifndef YY_LOCATION_PRINT -# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL -# define YY_LOCATION_PRINT(File, Loc) \ - fprintf (File, "%d.%d-%d.%d", \ - (Loc).first_line, (Loc).first_column, \ - (Loc).last_line, (Loc).last_column) -# else -# define YY_LOCATION_PRINT(File, Loc) ((void) 0) -# endif -#endif - - -/* YYLEX -- calling `yylex' with the right arguments. */ - -#ifdef YYLEX_PARAM -# define YYLEX yylex (YYLEX_PARAM) -#else -# define YYLEX yylex () -#endif /* Enable debugging if requested. */ #if YYDEBUG @@ -1108,54 +995,46 @@ while (YYID (0)) # define YYFPRINTF fprintf # endif -# define YYDPRINTF(Args) \ -do { \ - if (yydebug) \ - YYFPRINTF Args; \ -} while (YYID (0)) +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) -# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ -do { \ - if (yydebug) \ - { \ - YYFPRINTF (stderr, "%s ", Title); \ - yy_symbol_print (stderr, \ - Type, Value); \ - YYFPRINTF (stderr, "\n"); \ - } \ -} while (YYID (0)) +/* This macro is provided for backward compatibility. */ +#ifndef YY_LOCATION_PRINT +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +#endif -/*--------------------------------. -| Print this symbol on YYOUTPUT. | -`--------------------------------*/ +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + + +/*----------------------------------------. +| Print this symbol's value on YYOUTPUT. | +`----------------------------------------*/ -/*ARGSUSED*/ -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) static void yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) -#else -static void -yy_symbol_value_print (yyoutput, yytype, yyvaluep) - FILE *yyoutput; - int yytype; - YYSTYPE const * const yyvaluep; -#endif { + FILE *yyo = yyoutput; + YYUSE (yyo); if (!yyvaluep) return; # ifdef YYPRINT if (yytype < YYNTOKENS) YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); -# else - YYUSE (yyoutput); # endif - switch (yytype) - { - default: - break; - } + YYUSE (yytype); } @@ -1163,22 +1042,11 @@ yy_symbol_value_print (yyoutput, yytype, yyvaluep) | Print this symbol on YYOUTPUT. | `--------------------------------*/ -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) static void yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) -#else -static void -yy_symbol_print (yyoutput, yytype, yyvaluep) - FILE *yyoutput; - int yytype; - YYSTYPE const * const yyvaluep; -#endif { - if (yytype < YYNTOKENS) - YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); - else - YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); + YYFPRINTF (yyoutput, "%s %s (", + yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]); yy_symbol_value_print (yyoutput, yytype, yyvaluep); YYFPRINTF (yyoutput, ")"); @@ -1189,16 +1057,8 @@ yy_symbol_print (yyoutput, yytype, yyvaluep) | TOP (included). | `------------------------------------------------------------------*/ -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) static void yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) -#else -static void -yy_stack_print (yybottom, yytop) - yytype_int16 *yybottom; - yytype_int16 *yytop; -#endif { YYFPRINTF (stderr, "Stack now"); for (; yybottom <= yytop; yybottom++) @@ -1209,49 +1069,42 @@ yy_stack_print (yybottom, yytop) YYFPRINTF (stderr, "\n"); } -# define YY_STACK_PRINT(Bottom, Top) \ -do { \ - if (yydebug) \ - yy_stack_print ((Bottom), (Top)); \ -} while (YYID (0)) +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) /*------------------------------------------------. | Report that the YYRULE is going to be reduced. | `------------------------------------------------*/ -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) static void -yy_reduce_print (YYSTYPE *yyvsp, int yyrule) -#else -static void -yy_reduce_print (yyvsp, yyrule) - YYSTYPE *yyvsp; - int yyrule; -#endif +yy_reduce_print (yytype_int16 *yyssp, YYSTYPE *yyvsp, int yyrule) { + unsigned long yylno = yyrline[yyrule]; int yynrhs = yyr2[yyrule]; int yyi; - unsigned long int yylno = yyrline[yyrule]; YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", - yyrule - 1, yylno); + yyrule - 1, yylno); /* The symbols being reduced. */ for (yyi = 0; yyi < yynrhs; yyi++) { YYFPRINTF (stderr, " $%d = ", yyi + 1); - yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], - &(yyvsp[(yyi + 1) - (yynrhs)]) - ); + yy_symbol_print (stderr, + yystos[yyssp[yyi + 1 - yynrhs]], + &(yyvsp[(yyi + 1) - (yynrhs)]) + ); YYFPRINTF (stderr, "\n"); } } -# define YY_REDUCE_PRINT(Rule) \ -do { \ - if (yydebug) \ - yy_reduce_print (yyvsp, Rule); \ -} while (YYID (0)) +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyssp, yyvsp, Rule); \ +} while (0) /* Nonzero means print parse trace. It is left uninitialized so that multiple parsers can coexist. */ @@ -1265,7 +1118,7 @@ int yydebug; /* YYINITDEPTH -- initial size of the parser's stacks. */ -#ifndef YYINITDEPTH +#ifndef YYINITDEPTH # define YYINITDEPTH 200 #endif @@ -1280,7 +1133,6 @@ int yydebug; # define YYMAXDEPTH 10000 #endif - #if YYERROR_VERBOSE @@ -1289,15 +1141,8 @@ int yydebug; # define yystrlen strlen # else /* Return the length of YYSTR. */ -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) static YYSIZE_T yystrlen (const char *yystr) -#else -static YYSIZE_T -yystrlen (yystr) - const char *yystr; -#endif { YYSIZE_T yylen; for (yylen = 0; yystr[yylen]; yylen++) @@ -1313,16 +1158,8 @@ yystrlen (yystr) # else /* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in YYDEST. */ -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) static char * yystpcpy (char *yydest, const char *yysrc) -#else -static char * -yystpcpy (yydest, yysrc) - char *yydest; - const char *yysrc; -#endif { char *yyd = yydest; const char *yys = yysrc; @@ -1352,27 +1189,27 @@ yytnamerr (char *yyres, const char *yystr) char const *yyp = yystr; for (;;) - switch (*++yyp) - { - case '\'': - case ',': - goto do_not_strip_quotes; + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; - case '\\': - if (*++yyp != '\\') - goto do_not_strip_quotes; - /* Fall through. */ - default: - if (yyres) - yyres[yyn] = *yyp; - yyn++; - break; + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + /* Fall through. */ + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; - case '"': - if (yyres) - yyres[yyn] = '\0'; - return yyn; - } + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } do_not_strip_quotes: ; } @@ -1383,161 +1220,161 @@ yytnamerr (char *yyres, const char *yystr) } # endif -/* Copy into YYRESULT an error message about the unexpected token - YYCHAR while in state YYSTATE. Return the number of bytes copied, - including the terminating null byte. If YYRESULT is null, do not - copy anything; just return the number of bytes that would be - copied. As a special case, return 0 if an ordinary "syntax error" - message will do. Return YYSIZE_MAXIMUM if overflow occurs during - size calculation. */ -static YYSIZE_T -yysyntax_error (char *yyresult, int yystate, int yychar) +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. + + Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return 2 if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg, + yytype_int16 *yyssp, int yytoken) { - int yyn = yypact[yystate]; + YYSIZE_T yysize0 = yytnamerr (YY_NULLPTR, yytname[yytoken]); + YYSIZE_T yysize = yysize0; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + /* Internationalized format string. */ + const char *yyformat = YY_NULLPTR; + /* Arguments of yyformat. */ + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + /* Number of reported tokens (one for the "unexpected", one per + "expected"). */ + int yycount = 0; - if (! (YYPACT_NINF < yyn && yyn <= YYLAST)) - return 0; - else + /* There are many possibilities here to consider: + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yytoken != YYEMPTY) { - int yytype = YYTRANSLATE (yychar); - YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); - YYSIZE_T yysize = yysize0; - YYSIZE_T yysize1; - int yysize_overflow = 0; - enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; - char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; - int yyx; + int yyn = yypact[*yyssp]; + yyarg[yycount++] = yytname[yytoken]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yyx; -# if 0 - /* This is so xgettext sees the translatable formats that are - constructed on the fly. */ - YY_("syntax error, unexpected %s"); - YY_("syntax error, unexpected %s, expecting %s"); - YY_("syntax error, unexpected %s, expecting %s or %s"); - YY_("syntax error, unexpected %s, expecting %s or %s or %s"); - YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); -# endif - char *yyfmt; - char const *yyf; - static char const yyunexpected[] = "syntax error, unexpected %s"; - static char const yyexpecting[] = ", expecting %s"; - static char const yyor[] = " or %s"; - char yyformat[sizeof yyunexpected - + sizeof yyexpecting - 1 - + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) - * (sizeof yyor - 1))]; - char const *yyprefix = yyexpecting; - - /* Start YYX at -YYN if negative to avoid negative indexes in - YYCHECK. */ - int yyxbegin = yyn < 0 ? -yyn : 0; - - /* Stay within bounds of both yycheck and yytname. */ - int yychecklim = YYLAST - yyn + 1; - int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; - int yycount = 1; - - yyarg[0] = yytname[yytype]; - yyfmt = yystpcpy (yyformat, yyunexpected); - - for (yyx = yyxbegin; yyx < yyxend; ++yyx) - if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) - { - if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) - { - yycount = 1; - yysize = yysize0; - yyformat[sizeof yyunexpected - 1] = '\0'; - break; - } - yyarg[yycount++] = yytname[yyx]; - yysize1 = yysize + yytnamerr (0, yytname[yyx]); - yysize_overflow |= (yysize1 < yysize); - yysize = yysize1; - yyfmt = yystpcpy (yyfmt, yyprefix); - yyprefix = yyor; - } - - yyf = YY_(yyformat); - yysize1 = yysize + yystrlen (yyf); - yysize_overflow |= (yysize1 < yysize); - yysize = yysize1; - - if (yysize_overflow) - return YYSIZE_MAXIMUM; - - if (yyresult) - { - /* Avoid sprintf, as that infringes on the user's name space. - Don't have undefined behavior even if the translation - produced a string with the wrong number of "%s"s. */ - char *yyp = yyresult; - int yyi = 0; - while ((*yyp = *yyf) != '\0') - { - if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) - { - yyp += yytnamerr (yyp, yyarg[yyi++]); - yyf += 2; - } - else - { - yyp++; - yyf++; - } - } - } - return yysize; + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + break; + } + yyarg[yycount++] = yytname[yyx]; + { + YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]); + if (! (yysize <= yysize1 + && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; + } + } + } } + + switch (yycount) + { +# define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + default: /* Avoid compiler warnings. */ + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +# undef YYCASE_ + } + + { + YYSIZE_T yysize1 = yysize + yystrlen (yyformat); + if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; + } + + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return 1; + } + + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + { + char *yyp = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyformat += 2; + } + else + { + yyp++; + yyformat++; + } + } + return 0; } #endif /* YYERROR_VERBOSE */ - /*-----------------------------------------------. | Release the memory associated to this symbol. | `-----------------------------------------------*/ -/*ARGSUSED*/ -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) static void yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep) -#else -static void -yydestruct (yymsg, yytype, yyvaluep) - const char *yymsg; - int yytype; - YYSTYPE *yyvaluep; -#endif { YYUSE (yyvaluep); - if (!yymsg) yymsg = "Deleting"; YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); - switch (yytype) - { - - default: - break; - } + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YYUSE (yytype); + YY_IGNORE_MAYBE_UNINITIALIZED_END } -/* Prevent warnings from -Wmissing-prototypes. */ -#ifdef YYPARSE_PARAM -#if defined __STDC__ || defined __cplusplus -int yyparse (void *YYPARSE_PARAM); -#else -int yyparse (); -#endif -#else /* ! YYPARSE_PARAM */ -#if defined __STDC__ || defined __cplusplus -int yyparse (void); -#else -int yyparse (); -#endif -#endif /* ! YYPARSE_PARAM */ + /* The lookahead symbol. */ @@ -1545,49 +1382,26 @@ int yychar; /* The semantic value of the lookahead symbol. */ YYSTYPE yylval; - /* Number of syntax errors so far. */ int yynerrs; +/*----------. +| yyparse. | +`----------*/ -/*-------------------------. -| yyparse or yypush_parse. | -`-------------------------*/ - -#ifdef YYPARSE_PARAM -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -int -yyparse (void *YYPARSE_PARAM) -#else -int -yyparse (YYPARSE_PARAM) - void *YYPARSE_PARAM; -#endif -#else /* ! YYPARSE_PARAM */ -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) int yyparse (void) -#else -int -yyparse () - -#endif -#endif { - - int yystate; /* Number of tokens to shift before error messages enabled. */ int yyerrstatus; /* The stacks and their tools: - `yyss': related to states. - `yyvs': related to semantic values. + 'yyss': related to states. + 'yyvs': related to semantic values. - Refer to the stacks thru separate pointers, to allow yyoverflow + Refer to the stacks through separate pointers, to allow yyoverflow to reallocate them elsewhere. */ /* The state stack. */ @@ -1605,7 +1419,7 @@ yyparse () int yyn; int yyresult; /* Lookahead token as an internal (translated) token number. */ - int yytoken; + int yytoken = 0; /* The variables used to return semantic value and location from the action routines. */ YYSTYPE yyval; @@ -1623,9 +1437,8 @@ yyparse () Keep to zero when no symbol should be popped. */ int yylen = 0; - yytoken = 0; - yyss = yyssa; - yyvs = yyvsa; + yyssp = yyss = yyssa; + yyvsp = yyvs = yyvsa; yystacksize = YYINITDEPTH; YYDPRINTF ((stderr, "Starting parse\n")); @@ -1634,14 +1447,6 @@ yyparse () yyerrstatus = 0; yynerrs = 0; yychar = YYEMPTY; /* Cause a token to be read. */ - - /* Initialize stack pointers. - Waste one element of value and location stack - so that they stay on the same level as the state stack. - The wasted elements are never initialized. */ - yyssp = yyss; - yyvsp = yyvs; - goto yysetstate; /*------------------------------------------------------------. @@ -1662,23 +1467,23 @@ yyparse () #ifdef yyoverflow { - /* Give user a chance to reallocate the stack. Use copies of - these so that the &'s don't force the real ones into - memory. */ - YYSTYPE *yyvs1 = yyvs; - yytype_int16 *yyss1 = yyss; + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + yytype_int16 *yyss1 = yyss; - /* Each stack pointer address is followed by the size of the - data in use in that stack, in bytes. This used to be a - conditional around just the two extra args, but that might - be undefined if yyoverflow is a macro. */ - yyoverflow (YY_("memory exhausted"), - &yyss1, yysize * sizeof (*yyssp), - &yyvs1, yysize * sizeof (*yyvsp), - &yystacksize); + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + &yystacksize); - yyss = yyss1; - yyvs = yyvs1; + yyss = yyss1; + yyvs = yyvs1; } #else /* no yyoverflow */ # ifndef YYSTACK_RELOCATE @@ -1686,22 +1491,22 @@ yyparse () # else /* Extend the stack our own way. */ if (YYMAXDEPTH <= yystacksize) - goto yyexhaustedlab; + goto yyexhaustedlab; yystacksize *= 2; if (YYMAXDEPTH < yystacksize) - yystacksize = YYMAXDEPTH; + yystacksize = YYMAXDEPTH; { - yytype_int16 *yyss1 = yyss; - union yyalloc *yyptr = - (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); - if (! yyptr) - goto yyexhaustedlab; - YYSTACK_RELOCATE (yyss_alloc, yyss); - YYSTACK_RELOCATE (yyvs_alloc, yyvs); + yytype_int16 *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); # undef YYSTACK_RELOCATE - if (yyss1 != yyssa) - YYSTACK_FREE (yyss1); + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); } # endif #endif /* no yyoverflow */ @@ -1710,10 +1515,10 @@ yyparse () yyvsp = yyvs + yysize - 1; YYDPRINTF ((stderr, "Stack size increased to %lu\n", - (unsigned long int) yystacksize)); + (unsigned long) yystacksize)); if (yyss + yystacksize - 1 <= yyssp) - YYABORT; + YYABORT; } YYDPRINTF ((stderr, "Entering state %d\n", yystate)); @@ -1733,7 +1538,7 @@ yybackup: /* First try to decide what to do without reference to lookahead token. */ yyn = yypact[yystate]; - if (yyn == YYPACT_NINF) + if (yypact_value_is_default (yyn)) goto yydefault; /* Not known => get a lookahead token if don't already have one. */ @@ -1742,7 +1547,7 @@ yybackup: if (yychar == YYEMPTY) { YYDPRINTF ((stderr, "Reading a token: ")); - yychar = YYLEX; + yychar = yylex (); } if (yychar <= YYEOF) @@ -1764,8 +1569,8 @@ yybackup: yyn = yytable[yyn]; if (yyn <= 0) { - if (yyn == 0 || yyn == YYTABLE_NINF) - goto yyerrlab; + if (yytable_value_is_error (yyn)) + goto yyerrlab; yyn = -yyn; goto yyreduce; } @@ -1782,7 +1587,9 @@ yybackup: yychar = YYEMPTY; yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END goto yynewstate; @@ -1805,7 +1612,7 @@ yyreduce: yylen = yyr2[yyn]; /* If YYLEN is nonzero, implement the default value of the action: - `$$ = $1'. + '$$ = $1'. Otherwise, the following line sets YYVAL to garbage. This behavior is undocumented and Bison @@ -1819,112 +1626,100 @@ yyreduce: switch (yyn) { case 4: - -/* Line 1464 of yacc.c */ -#line 171 "dcParser.yxx" +#line 177 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - parameter_description = (yyvsp[(2) - (2)].u.field); + parameter_description = (yyvsp[0].u.field); } +#line 1634 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 7: - -/* Line 1464 of yacc.c */ -#line 180 "dcParser.yxx" +#line 186 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - if (!dc_file->add_class((yyvsp[(2) - (2)].u.dclass))) { - DCClass *old_class = dc_file->get_class_by_name((yyvsp[(2) - (2)].u.dclass)->get_name()); - if (old_class != (DCClass *)NULL && old_class->is_bogus_class()) { - yyerror("Base class defined after its first reference: " + (yyvsp[(2) - (2)].u.dclass)->get_name()); + if (!dc_file->add_class((yyvsp[0].u.dclass))) { + DCClass *old_class = dc_file->get_class_by_name((yyvsp[0].u.dclass)->get_name()); + if (old_class != nullptr && old_class->is_bogus_class()) { + yyerror("Base class defined after its first reference: " + (yyvsp[0].u.dclass)->get_name()); } else { - yyerror("Duplicate class name: " + (yyvsp[(2) - (2)].u.dclass)->get_name()); + yyerror("Duplicate class name: " + (yyvsp[0].u.dclass)->get_name()); } } } +#line 1649 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 8: - -/* Line 1464 of yacc.c */ -#line 191 "dcParser.yxx" +#line 197 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - if (!dc_file->add_switch((yyvsp[(2) - (2)].u.dswitch))) { - yyerror("Duplicate class name: " + (yyvsp[(2) - (2)].u.dswitch)->get_name()); + if (!dc_file->add_switch((yyvsp[0].u.dswitch))) { + yyerror("Duplicate class name: " + (yyvsp[0].u.dswitch)->get_name()); } } +#line 1659 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 13: - -/* Line 1464 of yacc.c */ -#line 204 "dcParser.yxx" +#line 210 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - (yyval.str) = (yyvsp[(1) - (3)].str) + string("/") + (yyvsp[(3) - (3)].str); + (yyval.str) = (yyvsp[-2].str) + string("/") + (yyvsp[0].str); } +#line 1667 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 15: - -/* Line 1464 of yacc.c */ -#line 212 "dcParser.yxx" +#line 218 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - (yyval.str) = (yyvsp[(1) - (3)].str) + string(".") + (yyvsp[(3) - (3)].str); + (yyval.str) = (yyvsp[-2].str) + string(".") + (yyvsp[0].str); } +#line 1675 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 16: - -/* Line 1464 of yacc.c */ -#line 219 "dcParser.yxx" +#line 225 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - dc_file->add_import_module((yyvsp[(2) - (2)].str)); + dc_file->add_import_module((yyvsp[0].str)); } +#line 1683 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 17: - -/* Line 1464 of yacc.c */ -#line 223 "dcParser.yxx" +#line 229 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - dc_file->add_import_module((yyvsp[(2) - (3)].str)); + dc_file->add_import_module((yyvsp[-1].str)); } +#line 1691 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 20: - -/* Line 1464 of yacc.c */ -#line 232 "dcParser.yxx" +#line 238 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { dc_file->add_import_symbol("*"); } +#line 1699 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 21: - -/* Line 1464 of yacc.c */ -#line 239 "dcParser.yxx" +#line 245 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - dc_file->add_import_symbol((yyvsp[(1) - (1)].str)); + dc_file->add_import_symbol((yyvsp[0].str)); } +#line 1707 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 22: - -/* Line 1464 of yacc.c */ -#line 243 "dcParser.yxx" +#line 249 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - dc_file->add_import_symbol((yyvsp[(3) - (3)].str)); + dc_file->add_import_symbol((yyvsp[0].str)); } +#line 1715 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 23: - -/* Line 1464 of yacc.c */ -#line 250 "dcParser.yxx" +#line 256 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - if ((yyvsp[(2) - (2)].u.parameter) != (DCParameter *)NULL) { - DCTypedef *dtypedef = new DCTypedef((yyvsp[(2) - (2)].u.parameter)); - + if ((yyvsp[0].u.parameter) != nullptr) { + DCTypedef *dtypedef = new DCTypedef((yyvsp[0].u.parameter)); + if (!dc_file->add_typedef(dtypedef)) { DCTypedef *old_typedef = dc_file->get_typedef_by_name(dtypedef->get_name()); if (old_typedef->is_bogus_typedef()) { @@ -1935,343 +1730,316 @@ yyreduce: } } } +#line 1734 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 26: - -/* Line 1464 of yacc.c */ -#line 273 "dcParser.yxx" +#line 279 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - dc_file->add_keyword((yyvsp[(2) - (2)].str)); + dc_file->add_keyword((yyvsp[0].str)); } +#line 1742 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 27: - -/* Line 1464 of yacc.c */ -#line 277 "dcParser.yxx" +#line 283 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { // This keyword has already been defined. But since we are now // explicitly defining it, clear its bitmask, so that we will have a // new hash code--doing this will allow us to phase out the // historical hash code support later. - ((DCKeyword *)(yyvsp[(2) - (2)].u.keyword))->clear_historical_flag(); + ((DCKeyword *)(yyvsp[0].u.keyword))->clear_historical_flag(); } +#line 1754 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 30: - -/* Line 1464 of yacc.c */ -#line 293 "dcParser.yxx" +#line 299 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - current_class = new DCClass(dc_file, (yyvsp[(2) - (2)].str), false, false); + current_class = new DCClass(dc_file, (yyvsp[0].str), false, false); } +#line 1762 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 31: - -/* Line 1464 of yacc.c */ -#line 297 "dcParser.yxx" +#line 303 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { (yyval.u.dclass) = current_class; - current_class = (yyvsp[(3) - (7)].u.dclass); + current_class = (yyvsp[-4].u.dclass); } +#line 1771 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 32: - -/* Line 1464 of yacc.c */ -#line 305 "dcParser.yxx" +#line 311 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - if (dc_file == (DCFile *)NULL) { + if (dc_file == nullptr) { yyerror("No DCFile available, so no class names are predefined."); - (yyval.u.dclass) = NULL; + (yyval.u.dclass) = nullptr; } else { - DCClass *dclass = dc_file->get_class_by_name((yyvsp[(1) - (1)].str)); - if (dclass == (DCClass *)NULL) { + DCClass *dclass = dc_file->get_class_by_name((yyvsp[0].str)); + if (dclass == nullptr) { // Create a bogus class as a forward reference. - dclass = new DCClass(dc_file, (yyvsp[(1) - (1)].str), false, true); + dclass = new DCClass(dc_file, (yyvsp[0].str), false, true); dc_file->add_class(dclass); } if (dclass->is_struct()) { yyerror("struct name not allowed"); } - + (yyval.u.dclass) = dclass; } } +#line 1795 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 35: - -/* Line 1464 of yacc.c */ -#line 333 "dcParser.yxx" +#line 339 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - if ((yyvsp[(1) - (1)].u.dclass) != (DCClass *)NULL) { - current_class->add_parent((yyvsp[(1) - (1)].u.dclass)); + if ((yyvsp[0].u.dclass) != nullptr) { + current_class->add_parent((yyvsp[0].u.dclass)); } } +#line 1805 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 36: - -/* Line 1464 of yacc.c */ -#line 339 "dcParser.yxx" +#line 345 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { if (!dc_multiple_inheritance) { yyerror("Multiple inheritance is not supported without \"dc-multiple-inheritance 1\" in your Config.prc file."); } else { - if ((yyvsp[(3) - (3)].u.dclass) != (DCClass *)NULL) { - current_class->add_parent((yyvsp[(3) - (3)].u.dclass)); + if ((yyvsp[0].u.dclass) != nullptr) { + current_class->add_parent((yyvsp[0].u.dclass)); } } } +#line 1820 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 39: - -/* Line 1464 of yacc.c */ -#line 355 "dcParser.yxx" +#line 361 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - if ((yyvsp[(2) - (3)].u.field) == (DCField *)NULL) { + if ((yyvsp[-1].u.field) == nullptr) { // Pass this error up. - } else if (!current_class->add_field((yyvsp[(2) - (3)].u.field))) { - yyerror("Duplicate field name: " + (yyvsp[(2) - (3)].u.field)->get_name()); - } else if ((yyvsp[(2) - (3)].u.field)->get_number() < 0) { + } else if (!current_class->add_field((yyvsp[-1].u.field))) { + yyerror("Duplicate field name: " + (yyvsp[-1].u.field)->get_name()); + } else if ((yyvsp[-1].u.field)->get_number() < 0) { yyerror("A non-network field cannot be stored on a dclass"); } } +#line 1834 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 40: - -/* Line 1464 of yacc.c */ -#line 368 "dcParser.yxx" +#line 374 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - if ((yyvsp[(1) - (2)].u.field) != (DCField *)NULL) { - if ((yyvsp[(1) - (2)].u.field)->get_name().empty()) { + if ((yyvsp[-1].u.field) != nullptr) { + if ((yyvsp[-1].u.field)->get_name().empty()) { yyerror("Field name required."); } - (yyvsp[(1) - (2)].u.field)->copy_keywords(current_keyword_list); + (yyvsp[-1].u.field)->copy_keywords(current_keyword_list); } - (yyval.u.field) = (yyvsp[(1) - (2)].u.field); + (yyval.u.field) = (yyvsp[-1].u.field); } +#line 1848 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 42: - -/* Line 1464 of yacc.c */ -#line 379 "dcParser.yxx" +#line 385 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { yyerror("Unnamed parameters are not allowed on a dclass"); - if ((yyvsp[(1) - (2)].u.parameter) != (DCField *)NULL) { - (yyvsp[(1) - (2)].u.parameter)->copy_keywords(current_keyword_list); + if ((yyvsp[-1].u.parameter) != nullptr) { + (yyvsp[-1].u.parameter)->copy_keywords(current_keyword_list); } - (yyval.u.field) = (yyvsp[(1) - (2)].u.parameter); + (yyval.u.field) = (yyvsp[-1].u.parameter); } +#line 1860 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 43: - -/* Line 1464 of yacc.c */ -#line 387 "dcParser.yxx" +#line 393 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - if ((yyvsp[(1) - (2)].u.parameter) != (DCField *)NULL) { - (yyvsp[(1) - (2)].u.parameter)->copy_keywords(current_keyword_list); + if ((yyvsp[-1].u.parameter) != nullptr) { + (yyvsp[-1].u.parameter)->copy_keywords(current_keyword_list); } - (yyval.u.field) = (yyvsp[(1) - (2)].u.parameter); + (yyval.u.field) = (yyvsp[-1].u.parameter); } +#line 1871 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 44: - -/* Line 1464 of yacc.c */ -#line 397 "dcParser.yxx" +#line 403 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - current_class = new DCClass(dc_file, (yyvsp[(2) - (2)].str), true, false); + current_class = new DCClass(dc_file, (yyvsp[0].str), true, false); } +#line 1879 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 45: - -/* Line 1464 of yacc.c */ -#line 401 "dcParser.yxx" +#line 407 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { (yyval.u.dclass) = current_class; - current_class = (yyvsp[(3) - (7)].u.dclass); + current_class = (yyvsp[-4].u.dclass); } +#line 1888 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 46: - -/* Line 1464 of yacc.c */ -#line 409 "dcParser.yxx" +#line 415 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - if (dc_file == (DCFile *)NULL) { + if (dc_file == nullptr) { yyerror("No DCFile available, so no struct names are predefined."); - (yyval.u.dclass) = NULL; + (yyval.u.dclass) = nullptr; } else { - DCClass *dstruct = dc_file->get_class_by_name((yyvsp[(1) - (1)].str)); - if (dstruct == (DCClass *)NULL) { + DCClass *dstruct = dc_file->get_class_by_name((yyvsp[0].str)); + if (dstruct == nullptr) { // Create a bogus class as a forward reference. - dstruct = new DCClass(dc_file, (yyvsp[(1) - (1)].str), false, true); + dstruct = new DCClass(dc_file, (yyvsp[0].str), false, true); dc_file->add_class(dstruct); } if (!dstruct->is_struct()) { yyerror("struct name required"); } - + (yyval.u.dclass) = dstruct; } } +#line 1912 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 49: - -/* Line 1464 of yacc.c */ -#line 437 "dcParser.yxx" +#line 443 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - if ((yyvsp[(1) - (1)].u.dclass) != (DCClass *)NULL) { - current_class->add_parent((yyvsp[(1) - (1)].u.dclass)); + if ((yyvsp[0].u.dclass) != nullptr) { + current_class->add_parent((yyvsp[0].u.dclass)); } } +#line 1922 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 50: - -/* Line 1464 of yacc.c */ -#line 443 "dcParser.yxx" +#line 449 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - if ((yyvsp[(3) - (3)].u.dclass) != (DCClass *)NULL) { - current_class->add_parent((yyvsp[(3) - (3)].u.dclass)); + if ((yyvsp[0].u.dclass) != nullptr) { + current_class->add_parent((yyvsp[0].u.dclass)); } } +#line 1932 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 53: - -/* Line 1464 of yacc.c */ -#line 454 "dcParser.yxx" +#line 460 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - if ((yyvsp[(2) - (3)].u.field) == (DCField *)NULL) { + if ((yyvsp[-1].u.field) == nullptr) { // Pass this error up. - } else if (!current_class->add_field((yyvsp[(2) - (3)].u.field))) { - yyerror("Duplicate field name: " + (yyvsp[(2) - (3)].u.field)->get_name()); + } else if (!current_class->add_field((yyvsp[-1].u.field))) { + yyerror("Duplicate field name: " + (yyvsp[-1].u.field)->get_name()); } } +#line 1944 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 54: - -/* Line 1464 of yacc.c */ -#line 465 "dcParser.yxx" +#line 471 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - if ((yyvsp[(1) - (2)].u.field)->get_name().empty()) { + if ((yyvsp[-1].u.field)->get_name().empty()) { yyerror("Field name required."); } - (yyval.u.field) = (yyvsp[(1) - (2)].u.field); + (yyval.u.field) = (yyvsp[-1].u.field); } +#line 1955 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 56: - -/* Line 1464 of yacc.c */ -#line 473 "dcParser.yxx" +#line 479 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - (yyval.u.field) = (yyvsp[(1) - (2)].u.parameter); + (yyval.u.field) = (yyvsp[-1].u.parameter); } +#line 1963 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 57: - -/* Line 1464 of yacc.c */ -#line 477 "dcParser.yxx" +#line 483 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - (yyval.u.field) = (yyvsp[(1) - (2)].u.parameter); + (yyval.u.field) = (yyvsp[-1].u.parameter); } +#line 1971 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 58: - -/* Line 1464 of yacc.c */ -#line 484 "dcParser.yxx" +#line 490 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - if (current_class == (DCClass *)NULL) { + if (current_class == nullptr) { yyerror("Cannot define a method outside of a struct or class."); DCClass *temp_class = new DCClass(dc_file, "temp", false, false); // memory leak. - current_atomic = new DCAtomicField((yyvsp[(1) - (2)].str), temp_class, false); + current_atomic = new DCAtomicField((yyvsp[-1].str), temp_class, false); } else { - current_atomic = new DCAtomicField((yyvsp[(1) - (2)].str), current_class, false); + current_atomic = new DCAtomicField((yyvsp[-1].str), current_class, false); } } +#line 1985 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 59: - -/* Line 1464 of yacc.c */ -#line 494 "dcParser.yxx" +#line 500 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { (yyval.u.field) = current_atomic; - current_atomic = (yyvsp[(3) - (5)].u.atomic); + current_atomic = (yyvsp[-2].u.atomic); } +#line 1994 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 64: - -/* Line 1464 of yacc.c */ -#line 512 "dcParser.yxx" +#line 518 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - if ((yyvsp[(1) - (1)].u.parameter) != (DCParameter *)NULL) { - current_atomic->add_element((yyvsp[(1) - (1)].u.parameter)); + if ((yyvsp[0].u.parameter) != nullptr) { + current_atomic->add_element((yyvsp[0].u.parameter)); } } +#line 2004 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 65: - -/* Line 1464 of yacc.c */ -#line 521 "dcParser.yxx" +#line 527 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - current_parameter = (yyvsp[(1) - (1)].u.parameter); + current_parameter = (yyvsp[0].u.parameter); } +#line 2012 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 66: - -/* Line 1464 of yacc.c */ -#line 525 "dcParser.yxx" +#line 531 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - (yyval.u.parameter) = (yyvsp[(3) - (3)].u.parameter); + (yyval.u.parameter) = (yyvsp[0].u.parameter); } +#line 2020 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 69: - -/* Line 1464 of yacc.c */ -#line 537 "dcParser.yxx" +#line 543 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { current_packer = &default_packer; current_packer->clear_data(); - if ((yyvsp[(1) - (2)].u.parameter) != (DCField *)NULL) { - current_packer->begin_pack((yyvsp[(1) - (2)].u.parameter)); + if ((yyvsp[-1].u.parameter) != nullptr) { + current_packer->begin_pack((yyvsp[-1].u.parameter)); } } +#line 2032 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 70: - -/* Line 1464 of yacc.c */ -#line 545 "dcParser.yxx" +#line 551 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { bool is_valid = false; - if ((yyvsp[(1) - (4)].u.parameter) != (DCField *)NULL) { - is_valid = (yyvsp[(1) - (4)].u.parameter)->is_valid(); + if ((yyvsp[-3].u.parameter) != nullptr) { + is_valid = (yyvsp[-3].u.parameter)->is_valid(); } if (current_packer->end_pack()) { - (yyvsp[(1) - (4)].u.parameter)->set_default_value(current_packer->get_string()); + (yyvsp[-3].u.parameter)->set_default_value(current_packer->get_bytes()); } else { if (is_valid) { @@ -2282,32 +2050,30 @@ yyreduce: // just ignore the default value in this case. } } +#line 2054 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 72: - -/* Line 1464 of yacc.c */ -#line 567 "dcParser.yxx" +#line 573 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { current_packer = &default_packer; current_packer->clear_data(); - if ((yyvsp[(1) - (2)].u.parameter) != (DCField *)NULL) { - current_packer->begin_pack((yyvsp[(1) - (2)].u.parameter)); + if ((yyvsp[-1].u.parameter) != nullptr) { + current_packer->begin_pack((yyvsp[-1].u.parameter)); } } +#line 2066 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 73: - -/* Line 1464 of yacc.c */ -#line 575 "dcParser.yxx" +#line 581 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { bool is_valid = false; - if ((yyvsp[(1) - (4)].u.parameter) != (DCField *)NULL) { - is_valid = (yyvsp[(1) - (4)].u.parameter)->is_valid(); + if ((yyvsp[-3].u.parameter) != nullptr) { + is_valid = (yyvsp[-3].u.parameter)->is_valid(); } if (current_packer->end_pack()) { - (yyvsp[(1) - (4)].u.parameter)->set_default_value(current_packer->get_string()); + (yyvsp[-3].u.parameter)->set_default_value(current_packer->get_bytes()); } else { if (is_valid) { @@ -2318,87 +2084,79 @@ yyreduce: // just ignore the default value in this case. } } +#line 2088 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 78: - -/* Line 1464 of yacc.c */ -#line 606 "dcParser.yxx" +#line 612 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - (yyval.u.field) = (yyvsp[(1) - (1)].u.parameter); + (yyval.u.field) = (yyvsp[0].u.parameter); } +#line 2096 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 79: - -/* Line 1464 of yacc.c */ -#line 610 "dcParser.yxx" +#line 616 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - (yyval.u.field) = (yyvsp[(1) - (1)].u.field); + (yyval.u.field) = (yyvsp[0].u.field); } +#line 2104 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 80: - -/* Line 1464 of yacc.c */ -#line 617 "dcParser.yxx" +#line 623 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - (yyval.u.field) = (yyvsp[(1) - (2)].u.field); + (yyval.u.field) = (yyvsp[-1].u.field); } +#line 2112 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 81: - -/* Line 1464 of yacc.c */ -#line 621 "dcParser.yxx" +#line 627 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - (yyval.u.field) = (yyvsp[(1) - (2)].u.parameter); + (yyval.u.field) = (yyvsp[-1].u.parameter); } +#line 2120 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 82: - -/* Line 1464 of yacc.c */ -#line 625 "dcParser.yxx" +#line 631 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - (yyval.u.field) = (yyvsp[(1) - (2)].u.parameter); + (yyval.u.field) = (yyvsp[-1].u.parameter); } +#line 2128 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 83: - -/* Line 1464 of yacc.c */ -#line 632 "dcParser.yxx" +#line 638 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - (yyval.u.parameter) = new DCSimpleParameter((yyvsp[(1) - (1)].u.subatomic)); + (yyval.u.parameter) = new DCSimpleParameter((yyvsp[0].u.subatomic)); } +#line 2136 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 84: - -/* Line 1464 of yacc.c */ -#line 636 "dcParser.yxx" +#line 642 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - DCSimpleParameter *simple_param = (yyvsp[(1) - (4)].u.parameter)->as_simple_parameter(); - nassertr(simple_param != (DCSimpleParameter *)NULL, 0); + DCSimpleParameter *simple_param = (yyvsp[-3].u.parameter)->as_simple_parameter(); + nassertr(simple_param != nullptr, 0); if (!simple_param->set_range(double_range)) { yyerror("Inappropriate range for type"); } (yyval.u.parameter) = simple_param; } +#line 2149 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 85: - -/* Line 1464 of yacc.c */ -#line 645 "dcParser.yxx" +#line 651 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - DCSimpleParameter *simple_param = (yyvsp[(1) - (3)].u.parameter)->as_simple_parameter(); - nassertr(simple_param != (DCSimpleParameter *)NULL, 0); + DCSimpleParameter *simple_param = (yyvsp[-2].u.parameter)->as_simple_parameter(); + nassertr(simple_param != nullptr, 0); if (!simple_param->is_numeric_type()) { yyerror("A divisor is only valid on a numeric type."); - } else if (!simple_param->set_divisor((yyvsp[(3) - (3)].u.s_uint))) { + } else if (!simple_param->set_divisor((yyvsp[0].u.s_uint))) { yyerror("Invalid divisor."); } else if (simple_param->has_modulus() && !simple_param->set_modulus(simple_param->get_modulus())) { @@ -2407,982 +2165,907 @@ yyreduce: } (yyval.u.parameter) = simple_param; } +#line 2169 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 86: - -/* Line 1464 of yacc.c */ -#line 661 "dcParser.yxx" - { - DCSimpleParameter *simple_param = (yyvsp[(1) - (3)].u.parameter)->as_simple_parameter(); - nassertr(simple_param != (DCSimpleParameter *)NULL, 0); +#line 667 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ + { + DCSimpleParameter *simple_param = (yyvsp[-2].u.parameter)->as_simple_parameter(); + nassertr(simple_param != nullptr, 0); if (!simple_param->is_numeric_type()) { yyerror("A divisor is only valid on a numeric type."); - } else if (!simple_param->set_modulus((yyvsp[(3) - (3)].u.real))) { + } else if (!simple_param->set_modulus((yyvsp[0].u.real))) { yyerror("Invalid modulus."); } (yyval.u.parameter) = simple_param; } +#line 2185 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 88: - -/* Line 1464 of yacc.c */ -#line 677 "dcParser.yxx" +#line 683 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - if (dc_file == (DCFile *)NULL) { + if (dc_file == nullptr) { yyerror("Invalid type."); - (yyval.u.parameter) = NULL; + (yyval.u.parameter) = nullptr; } else { - DCTypedef *dtypedef = dc_file->get_typedef_by_name((yyvsp[(1) - (1)].str)); - if (dtypedef == (DCTypedef *)NULL) { + DCTypedef *dtypedef = dc_file->get_typedef_by_name((yyvsp[0].str)); + if (dtypedef == nullptr) { // Maybe it's a class name. - DCClass *dclass = dc_file->get_class_by_name((yyvsp[(1) - (1)].str)); - if (dclass != (DCClass *)NULL) { + DCClass *dclass = dc_file->get_class_by_name((yyvsp[0].str)); + if (dclass != nullptr) { // Create an implicit typedef for this. dtypedef = new DCTypedef(new DCClassParameter(dclass), true); } else { // Maybe it's a switch name. - DCSwitch *dswitch = dc_file->get_switch_by_name((yyvsp[(1) - (1)].str)); - if (dswitch != (DCSwitch *)NULL) { + DCSwitch *dswitch = dc_file->get_switch_by_name((yyvsp[0].str)); + if (dswitch != nullptr) { // This also gets an implicit typedef. dtypedef = new DCTypedef(new DCSwitchParameter(dswitch), true); } else { // It's an undefined typedef. Create a bogus forward reference. - dtypedef = new DCTypedef((yyvsp[(1) - (1)].str)); + dtypedef = new DCTypedef((yyvsp[0].str)); } } - + dc_file->add_typedef(dtypedef); } - + (yyval.u.parameter) = dtypedef->make_new_parameter(); } } +#line 2221 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 89: - -/* Line 1464 of yacc.c */ -#line 709 "dcParser.yxx" +#line 715 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { // This is an inline struct definition. - if ((yyvsp[(1) - (1)].u.dclass) == (DCClass *)NULL) { - (yyval.u.parameter) = NULL; + if ((yyvsp[0].u.dclass) == nullptr) { + (yyval.u.parameter) = nullptr; } else { - if (dc_file != (DCFile *)NULL) { - dc_file->add_thing_to_delete((yyvsp[(1) - (1)].u.dclass)); + if (dc_file != nullptr) { + dc_file->add_thing_to_delete((yyvsp[0].u.dclass)); } else { // This is a memory leak--this happens when we put an anonymous // struct reference within the string passed to // DCPackerInterface::check_match(). Maybe it doesn't really matter. } - (yyval.u.parameter) = new DCClassParameter((yyvsp[(1) - (1)].u.dclass)); + (yyval.u.parameter) = new DCClassParameter((yyvsp[0].u.dclass)); } } +#line 2241 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 90: - -/* Line 1464 of yacc.c */ -#line 725 "dcParser.yxx" +#line 731 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { // This is an inline switch definition. - if ((yyvsp[(1) - (1)].u.dswitch) == (DCSwitch *)NULL) { - (yyval.u.parameter) = NULL; + if ((yyvsp[0].u.dswitch) == nullptr) { + (yyval.u.parameter) = nullptr; } else { - if (dc_file != (DCFile *)NULL) { - dc_file->add_thing_to_delete((yyvsp[(1) - (1)].u.dswitch)); + if (dc_file != nullptr) { + dc_file->add_thing_to_delete((yyvsp[0].u.dswitch)); } else { // This is a memory leak--this happens when we put an anonymous // switch reference within the string passed to // DCPackerInterface::check_match(). Maybe it doesn't really matter. } - (yyval.u.parameter) = new DCSwitchParameter((yyvsp[(1) - (1)].u.dswitch)); + (yyval.u.parameter) = new DCSwitchParameter((yyvsp[0].u.dswitch)); } } +#line 2261 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 91: - -/* Line 1464 of yacc.c */ -#line 744 "dcParser.yxx" +#line 750 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { double_range.clear(); } +#line 2269 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 92: - -/* Line 1464 of yacc.c */ -#line 748 "dcParser.yxx" +#line 754 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { double_range.clear(); - if (!double_range.add_range((yyvsp[(1) - (1)].u.real), (yyvsp[(1) - (1)].u.real))) { + if (!double_range.add_range((yyvsp[0].u.real), (yyvsp[0].u.real))) { yyerror("Overlapping range"); } } +#line 2280 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 93: - -/* Line 1464 of yacc.c */ -#line 755 "dcParser.yxx" +#line 761 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { double_range.clear(); - if (!double_range.add_range((yyvsp[(1) - (3)].u.real), (yyvsp[(3) - (3)].u.real))) { + if (!double_range.add_range((yyvsp[-2].u.real), (yyvsp[0].u.real))) { yyerror("Overlapping range"); } } +#line 2291 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 94: - -/* Line 1464 of yacc.c */ -#line 762 "dcParser.yxx" +#line 768 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { double_range.clear(); - if ((yyvsp[(2) - (2)].u.real) >= 0) { + if ((yyvsp[0].u.real) >= 0) { yyerror("Syntax error"); - } else if (!double_range.add_range((yyvsp[(1) - (2)].u.real), -(yyvsp[(2) - (2)].u.real))) { + } else if (!double_range.add_range((yyvsp[-1].u.real), -(yyvsp[0].u.real))) { yyerror("Overlapping range"); } } +#line 2304 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 95: - -/* Line 1464 of yacc.c */ -#line 771 "dcParser.yxx" +#line 777 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - if (!double_range.add_range((yyvsp[(3) - (3)].u.real), (yyvsp[(3) - (3)].u.real))) { + if (!double_range.add_range((yyvsp[0].u.real), (yyvsp[0].u.real))) { yyerror("Overlapping range"); } } +#line 2314 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 96: - -/* Line 1464 of yacc.c */ -#line 777 "dcParser.yxx" +#line 783 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - if (!double_range.add_range((yyvsp[(3) - (5)].u.real), (yyvsp[(5) - (5)].u.real))) { + if (!double_range.add_range((yyvsp[-2].u.real), (yyvsp[0].u.real))) { yyerror("Overlapping range"); } } +#line 2324 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 97: - -/* Line 1464 of yacc.c */ -#line 783 "dcParser.yxx" +#line 789 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - if ((yyvsp[(4) - (4)].u.real) >= 0) { + if ((yyvsp[0].u.real) >= 0) { yyerror("Syntax error"); - } else if (!double_range.add_range((yyvsp[(3) - (4)].u.real), -(yyvsp[(4) - (4)].u.real))) { + } else if (!double_range.add_range((yyvsp[-1].u.real), -(yyvsp[0].u.real))) { yyerror("Overlapping range"); } } +#line 2336 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 98: - -/* Line 1464 of yacc.c */ -#line 794 "dcParser.yxx" +#line 800 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { uint_range.clear(); } +#line 2344 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 99: - -/* Line 1464 of yacc.c */ -#line 798 "dcParser.yxx" +#line 804 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { uint_range.clear(); - if (!uint_range.add_range((yyvsp[(1) - (1)].u.s_uint), (yyvsp[(1) - (1)].u.s_uint))) { + if (!uint_range.add_range((yyvsp[0].u.s_uint), (yyvsp[0].u.s_uint))) { yyerror("Overlapping range"); } } +#line 2355 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 100: - -/* Line 1464 of yacc.c */ -#line 805 "dcParser.yxx" +#line 811 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { uint_range.clear(); - if (!uint_range.add_range((yyvsp[(1) - (3)].u.s_uint), (yyvsp[(3) - (3)].u.s_uint))) { + if (!uint_range.add_range((yyvsp[-2].u.s_uint), (yyvsp[0].u.s_uint))) { yyerror("Overlapping range"); } } +#line 2366 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 101: - -/* Line 1464 of yacc.c */ -#line 812 "dcParser.yxx" +#line 818 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { uint_range.clear(); - if (!uint_range.add_range((yyvsp[(1) - (2)].u.s_uint), (yyvsp[(2) - (2)].u.s_uint))) { + if (!uint_range.add_range((yyvsp[-1].u.s_uint), (yyvsp[0].u.s_uint))) { yyerror("Overlapping range"); } } +#line 2377 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 102: - -/* Line 1464 of yacc.c */ -#line 819 "dcParser.yxx" +#line 825 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - if (!uint_range.add_range((yyvsp[(3) - (3)].u.s_uint), (yyvsp[(3) - (3)].u.s_uint))) { + if (!uint_range.add_range((yyvsp[0].u.s_uint), (yyvsp[0].u.s_uint))) { yyerror("Overlapping range"); } } +#line 2387 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 103: - -/* Line 1464 of yacc.c */ -#line 825 "dcParser.yxx" +#line 831 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - if (!uint_range.add_range((yyvsp[(3) - (5)].u.s_uint), (yyvsp[(5) - (5)].u.s_uint))) { + if (!uint_range.add_range((yyvsp[-2].u.s_uint), (yyvsp[0].u.s_uint))) { yyerror("Overlapping range"); } } +#line 2397 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 104: - -/* Line 1464 of yacc.c */ -#line 831 "dcParser.yxx" +#line 837 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - if (!uint_range.add_range((yyvsp[(3) - (4)].u.s_uint), (yyvsp[(4) - (4)].u.s_uint))) { + if (!uint_range.add_range((yyvsp[-1].u.s_uint), (yyvsp[0].u.s_uint))) { yyerror("Overlapping range"); } } +#line 2407 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 106: - -/* Line 1464 of yacc.c */ -#line 841 "dcParser.yxx" +#line 847 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - if ((yyvsp[(1) - (4)].u.parameter) == (DCParameter *)NULL) { - (yyval.u.parameter) = NULL; + if ((yyvsp[-3].u.parameter) == nullptr) { + (yyval.u.parameter) = nullptr; } else { - (yyval.u.parameter) = (yyvsp[(1) - (4)].u.parameter)->append_array_specification(uint_range); + (yyval.u.parameter) = (yyvsp[-3].u.parameter)->append_array_specification(uint_range); } } +#line 2419 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 107: - -/* Line 1464 of yacc.c */ -#line 852 "dcParser.yxx" +#line 858 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - current_parameter->set_name((yyvsp[(1) - (1)].str)); + current_parameter->set_name((yyvsp[0].str)); (yyval.u.parameter) = current_parameter; } +#line 2428 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 108: - -/* Line 1464 of yacc.c */ -#line 857 "dcParser.yxx" +#line 863 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - DCSimpleParameter *simple_param = (yyvsp[(1) - (3)].u.parameter)->as_simple_parameter(); - if (simple_param == NULL || simple_param->get_typedef() != (DCTypedef *)NULL) { + DCSimpleParameter *simple_param = (yyvsp[-2].u.parameter)->as_simple_parameter(); + if (simple_param == nullptr || simple_param->get_typedef() != nullptr) { yyerror("A divisor is only allowed on a primitive type."); } else if (!simple_param->is_numeric_type()) { yyerror("A divisor is only valid on a numeric type."); } else { - if (!simple_param->set_divisor((yyvsp[(3) - (3)].u.s_uint))) { + if (!simple_param->set_divisor((yyvsp[0].u.s_uint))) { yyerror("Invalid divisor."); } } } +#line 2447 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 109: - -/* Line 1464 of yacc.c */ -#line 872 "dcParser.yxx" +#line 878 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - DCSimpleParameter *simple_param = (yyvsp[(1) - (3)].u.parameter)->as_simple_parameter(); - if (simple_param == NULL || simple_param->get_typedef() != (DCTypedef *)NULL) { + DCSimpleParameter *simple_param = (yyvsp[-2].u.parameter)->as_simple_parameter(); + if (simple_param == nullptr || simple_param->get_typedef() != nullptr) { yyerror("A modulus is only allowed on a primitive type."); } else if (!simple_param->is_numeric_type()) { yyerror("A modulus is only valid on a numeric type."); } else { - if (!simple_param->set_modulus((yyvsp[(3) - (3)].u.real))) { + if (!simple_param->set_modulus((yyvsp[0].u.real))) { yyerror("Invalid modulus."); } } } +#line 2466 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 110: - -/* Line 1464 of yacc.c */ -#line 887 "dcParser.yxx" +#line 893 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - (yyval.u.parameter) = (yyvsp[(1) - (4)].u.parameter)->append_array_specification(uint_range); + (yyval.u.parameter) = (yyvsp[-3].u.parameter)->append_array_specification(uint_range); } +#line 2474 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 111: - -/* Line 1464 of yacc.c */ -#line 894 "dcParser.yxx" +#line 900 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - if ((yyvsp[(1) - (1)].str).length() != 1) { + if ((yyvsp[0].str).length() != 1) { yyerror("Single character required."); (yyval.u.s_uint) = 0; } else { - (yyval.u.s_uint) = (unsigned char)(yyvsp[(1) - (1)].str)[0]; + (yyval.u.s_uint) = (unsigned char)(yyvsp[0].str)[0]; } } +#line 2487 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 113: - -/* Line 1464 of yacc.c */ -#line 907 "dcParser.yxx" +#line 913 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - (yyval.u.s_uint) = (unsigned int)(yyvsp[(1) - (1)].u.uint64); - if ((yyval.u.s_uint) != (yyvsp[(1) - (1)].u.uint64)) { + (yyval.u.s_uint) = (unsigned int)(yyvsp[0].u.uint64); + if ((yyval.u.s_uint) != (yyvsp[0].u.uint64)) { yyerror("Number out of range."); (yyval.u.s_uint) = 1; } } +#line 2499 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 114: - -/* Line 1464 of yacc.c */ -#line 918 "dcParser.yxx" +#line 924 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - (yyval.u.s_uint) = (unsigned int)-(yyvsp[(1) - (1)].u.int64); - if ((yyvsp[(1) - (1)].u.int64) >= 0) { + (yyval.u.s_uint) = (unsigned int)-(yyvsp[0].u.int64); + if ((yyvsp[0].u.int64) >= 0) { yyerror("Syntax error."); - } else if ((yyval.u.s_uint) != -(yyvsp[(1) - (1)].u.int64)) { + } else if ((yyval.u.s_uint) != -(yyvsp[0].u.int64)) { yyerror("Number out of range."); (yyval.u.s_uint) = 1; } } +#line 2514 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 117: - -/* Line 1464 of yacc.c */ -#line 940 "dcParser.yxx" +#line 946 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - (yyval.u.real) = (double)(yyvsp[(1) - (1)].u.uint64); + (yyval.u.real) = (double)(yyvsp[0].u.uint64); } +#line 2522 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 118: - -/* Line 1464 of yacc.c */ -#line 944 "dcParser.yxx" +#line 950 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - (yyval.u.real) = (double)(yyvsp[(1) - (1)].u.int64); + (yyval.u.real) = (double)(yyvsp[0].u.int64); } +#line 2530 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 120: - -/* Line 1464 of yacc.c */ -#line 952 "dcParser.yxx" +#line 958 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - if ((yyvsp[(1) - (1)].str).length() != 1) { + if ((yyvsp[0].str).length() != 1) { yyerror("Single character required."); (yyval.u.real) = 0; } else { - (yyval.u.real) = (double)(unsigned char)(yyvsp[(1) - (1)].str)[0]; + (yyval.u.real) = (double)(unsigned char)(yyvsp[0].str)[0]; } } +#line 2543 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 122: - -/* Line 1464 of yacc.c */ -#line 966 "dcParser.yxx" +#line 972 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { } +#line 2550 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 123: - -/* Line 1464 of yacc.c */ -#line 969 "dcParser.yxx" +#line 975 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - if ((yyvsp[(1) - (2)].str) != current_packer->get_current_field_name()) { + if ((yyvsp[-1].str) != current_packer->get_current_field_name()) { ostringstream strm; - strm << "Got '" << (yyvsp[(1) - (2)].str) << "', expected '" + strm << "Got '" << (yyvsp[-1].str) << "', expected '" << current_packer->get_current_field_name() << "'"; yyerror(strm.str()); } } +#line 2563 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 124: - -/* Line 1464 of yacc.c */ -#line 978 "dcParser.yxx" +#line 984 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { } +#line 2570 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 125: - -/* Line 1464 of yacc.c */ -#line 984 "dcParser.yxx" +#line 990 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - current_packer->pack_int64((yyvsp[(1) - (1)].u.int64)); + current_packer->pack_int64((yyvsp[0].u.int64)); } +#line 2578 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 126: - -/* Line 1464 of yacc.c */ -#line 988 "dcParser.yxx" +#line 994 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - current_packer->pack_uint64((yyvsp[(1) - (1)].u.uint64)); + current_packer->pack_uint64((yyvsp[0].u.uint64)); } +#line 2586 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 127: - -/* Line 1464 of yacc.c */ -#line 992 "dcParser.yxx" +#line 998 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - current_packer->pack_double((yyvsp[(1) - (1)].u.real)); + current_packer->pack_double((yyvsp[0].u.real)); } +#line 2594 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 128: - -/* Line 1464 of yacc.c */ -#line 996 "dcParser.yxx" +#line 1002 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - current_packer->pack_string((yyvsp[(1) - (1)].str)); + current_packer->pack_string((yyvsp[0].str)); } +#line 2602 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 129: - -/* Line 1464 of yacc.c */ -#line 1000 "dcParser.yxx" +#line 1006 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - current_packer->pack_literal_value((yyvsp[(1) - (1)].str)); + current_packer->pack_literal_value((yyvsp[0].bytes)); } +#line 2610 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 130: - -/* Line 1464 of yacc.c */ -#line 1004 "dcParser.yxx" +#line 1010 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { current_packer->push(); } +#line 2618 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 131: - -/* Line 1464 of yacc.c */ -#line 1008 "dcParser.yxx" +#line 1014 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { current_packer->pop(); } +#line 2626 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 132: - -/* Line 1464 of yacc.c */ -#line 1012 "dcParser.yxx" +#line 1018 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { current_packer->push(); } +#line 2634 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 133: - -/* Line 1464 of yacc.c */ -#line 1016 "dcParser.yxx" +#line 1022 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { current_packer->pop(); } +#line 2642 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 134: - -/* Line 1464 of yacc.c */ -#line 1020 "dcParser.yxx" +#line 1026 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { current_packer->push(); } +#line 2650 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 135: - -/* Line 1464 of yacc.c */ -#line 1024 "dcParser.yxx" +#line 1030 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { current_packer->pop(); } +#line 2658 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 136: - -/* Line 1464 of yacc.c */ -#line 1028 "dcParser.yxx" +#line 1034 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - for (unsigned int i = 0; i < (yyvsp[(3) - (3)].u.s_uint); i++) { - current_packer->pack_int64((yyvsp[(1) - (3)].u.int64)); + for (unsigned int i = 0; i < (yyvsp[0].u.s_uint); i++) { + current_packer->pack_int64((yyvsp[-2].u.int64)); } } +#line 2668 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 137: - -/* Line 1464 of yacc.c */ -#line 1034 "dcParser.yxx" +#line 1040 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - for (unsigned int i = 0; i < (yyvsp[(3) - (3)].u.s_uint); i++) { - current_packer->pack_uint64((yyvsp[(1) - (3)].u.uint64)); + for (unsigned int i = 0; i < (yyvsp[0].u.s_uint); i++) { + current_packer->pack_uint64((yyvsp[-2].u.uint64)); } } +#line 2678 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 138: - -/* Line 1464 of yacc.c */ -#line 1040 "dcParser.yxx" +#line 1046 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - for (unsigned int i = 0; i < (yyvsp[(3) - (3)].u.s_uint); i++) { - current_packer->pack_double((yyvsp[(1) - (3)].u.real)); + for (unsigned int i = 0; i < (yyvsp[0].u.s_uint); i++) { + current_packer->pack_double((yyvsp[-2].u.real)); } } +#line 2688 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 139: - -/* Line 1464 of yacc.c */ -#line 1046 "dcParser.yxx" +#line 1052 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - for (unsigned int i = 0; i < (yyvsp[(3) - (3)].u.s_uint); i++) { - current_packer->pack_literal_value((yyvsp[(1) - (3)].str)); + for (unsigned int i = 0; i < (yyvsp[0].u.s_uint); i++) { + current_packer->pack_literal_value((yyvsp[-2].bytes)); } } +#line 2698 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 146: - -/* Line 1464 of yacc.c */ -#line 1070 "dcParser.yxx" +#line 1076 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { (yyval.u.subatomic) = ST_int8; } +#line 2706 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 147: - -/* Line 1464 of yacc.c */ -#line 1074 "dcParser.yxx" +#line 1080 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { (yyval.u.subatomic) = ST_int16; } +#line 2714 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 148: - -/* Line 1464 of yacc.c */ -#line 1078 "dcParser.yxx" +#line 1084 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { (yyval.u.subatomic) = ST_int32; } +#line 2722 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 149: - -/* Line 1464 of yacc.c */ -#line 1082 "dcParser.yxx" +#line 1088 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { (yyval.u.subatomic) = ST_int64; } +#line 2730 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 150: - -/* Line 1464 of yacc.c */ -#line 1086 "dcParser.yxx" +#line 1092 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { (yyval.u.subatomic) = ST_uint8; } +#line 2738 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 151: - -/* Line 1464 of yacc.c */ -#line 1090 "dcParser.yxx" +#line 1096 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { (yyval.u.subatomic) = ST_uint16; } +#line 2746 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 152: - -/* Line 1464 of yacc.c */ -#line 1094 "dcParser.yxx" +#line 1100 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { (yyval.u.subatomic) = ST_uint32; } +#line 2754 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 153: - -/* Line 1464 of yacc.c */ -#line 1098 "dcParser.yxx" +#line 1104 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { (yyval.u.subatomic) = ST_uint64; } +#line 2762 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 154: - -/* Line 1464 of yacc.c */ -#line 1102 "dcParser.yxx" +#line 1108 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { (yyval.u.subatomic) = ST_float64; } +#line 2770 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 155: - -/* Line 1464 of yacc.c */ -#line 1106 "dcParser.yxx" +#line 1112 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { (yyval.u.subatomic) = ST_string; } +#line 2778 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 156: - -/* Line 1464 of yacc.c */ -#line 1110 "dcParser.yxx" +#line 1116 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { (yyval.u.subatomic) = ST_blob; } +#line 2786 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 157: - -/* Line 1464 of yacc.c */ -#line 1114 "dcParser.yxx" +#line 1120 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { (yyval.u.subatomic) = ST_blob32; } +#line 2794 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 158: - -/* Line 1464 of yacc.c */ -#line 1118 "dcParser.yxx" +#line 1124 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { (yyval.u.subatomic) = ST_int8array; } +#line 2802 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 159: - -/* Line 1464 of yacc.c */ -#line 1122 "dcParser.yxx" +#line 1128 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { (yyval.u.subatomic) = ST_int16array; } +#line 2810 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 160: - -/* Line 1464 of yacc.c */ -#line 1126 "dcParser.yxx" +#line 1132 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { (yyval.u.subatomic) = ST_int32array; } +#line 2818 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 161: - -/* Line 1464 of yacc.c */ -#line 1130 "dcParser.yxx" +#line 1136 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { (yyval.u.subatomic) = ST_uint8array; } +#line 2826 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 162: - -/* Line 1464 of yacc.c */ -#line 1134 "dcParser.yxx" +#line 1140 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { (yyval.u.subatomic) = ST_uint16array; } +#line 2834 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 163: - -/* Line 1464 of yacc.c */ -#line 1138 "dcParser.yxx" +#line 1144 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { (yyval.u.subatomic) = ST_uint32array; } +#line 2842 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 164: - -/* Line 1464 of yacc.c */ -#line 1142 "dcParser.yxx" +#line 1148 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { (yyval.u.subatomic) = ST_uint32uint8array; } +#line 2850 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 165: - -/* Line 1464 of yacc.c */ -#line 1146 "dcParser.yxx" +#line 1152 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { (yyval.u.subatomic) = ST_char; } +#line 2858 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 166: - -/* Line 1464 of yacc.c */ -#line 1153 "dcParser.yxx" +#line 1159 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { current_keyword_list.clear_keywords(); } +#line 2866 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 167: - -/* Line 1464 of yacc.c */ -#line 1157 "dcParser.yxx" +#line 1163 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - current_keyword_list.add_keyword((yyvsp[(2) - (2)].u.keyword)); + current_keyword_list.add_keyword((yyvsp[0].u.keyword)); } +#line 2874 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 168: - -/* Line 1464 of yacc.c */ -#line 1164 "dcParser.yxx" +#line 1170 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { if (current_keyword_list.get_num_keywords() != 0) { yyerror("Communication keywords are not allowed here."); } } +#line 2884 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 169: - -/* Line 1464 of yacc.c */ -#line 1173 "dcParser.yxx" +#line 1179 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - current_molecular = new DCMolecularField((yyvsp[(1) - (2)].str), current_class); + current_molecular = new DCMolecularField((yyvsp[-1].str), current_class); } +#line 2892 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 170: - -/* Line 1464 of yacc.c */ -#line 1177 "dcParser.yxx" +#line 1183 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { (yyval.u.field) = current_molecular; } +#line 2900 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 171: - -/* Line 1464 of yacc.c */ -#line 1184 "dcParser.yxx" +#line 1190 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - DCField *field = current_class->get_field_by_name((yyvsp[(1) - (1)].str)); - (yyval.u.atomic) = (DCAtomicField *)NULL; - if (field == (DCField *)NULL) { + DCField *field = current_class->get_field_by_name((yyvsp[0].str)); + (yyval.u.atomic) = nullptr; + if (field == nullptr) { // Maybe the field is unknown because the class is partially // bogus. In that case, allow it for now; create a bogus field as // a placeholder. if (current_class->inherits_from_bogus_class()) { - (yyval.u.atomic) = new DCAtomicField((yyvsp[(1) - (1)].str), current_class, true); + (yyval.u.atomic) = new DCAtomicField((yyvsp[0].str), current_class, true); current_class->add_field((yyval.u.atomic)); } else { // Nope, it's a fully-defined class, so this is a real error. - yyerror("Unknown field: " + (yyvsp[(1) - (1)].str)); + yyerror("Unknown field: " + (yyvsp[0].str)); } } else { (yyval.u.atomic) = field->as_atomic_field(); - if ((yyval.u.atomic) == (DCAtomicField *)NULL) { - yyerror("Not an atomic field: " + (yyvsp[(1) - (1)].str)); + if ((yyval.u.atomic) == nullptr) { + yyerror("Not an atomic field: " + (yyvsp[0].str)); } } } +#line 2928 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 172: - -/* Line 1464 of yacc.c */ -#line 1211 "dcParser.yxx" +#line 1217 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - if ((yyvsp[(1) - (1)].u.atomic) != (DCAtomicField *)NULL) { - current_molecular->add_atomic((yyvsp[(1) - (1)].u.atomic)); + if ((yyvsp[0].u.atomic) != nullptr) { + current_molecular->add_atomic((yyvsp[0].u.atomic)); } } +#line 2938 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 173: - -/* Line 1464 of yacc.c */ -#line 1217 "dcParser.yxx" +#line 1223 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - if ((yyvsp[(3) - (3)].u.atomic) != (DCAtomicField *)NULL) { - current_molecular->add_atomic((yyvsp[(3) - (3)].u.atomic)); - if (!(yyvsp[(3) - (3)].u.atomic)->is_bogus_field() && !current_molecular->compare_keywords(*(yyvsp[(3) - (3)].u.atomic))) { - yyerror("Mismatched keywords in molecule between " + + if ((yyvsp[0].u.atomic) != nullptr) { + current_molecular->add_atomic((yyvsp[0].u.atomic)); + if (!(yyvsp[0].u.atomic)->is_bogus_field() && !current_molecular->compare_keywords(*(yyvsp[0].u.atomic))) { + yyerror("Mismatched keywords in molecule between " + current_molecular->get_atomic(0)->get_name() + " and " + - (yyvsp[(3) - (3)].u.atomic)->get_name()); + (yyvsp[0].u.atomic)->get_name()); } } } +#line 2953 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 174: - -/* Line 1464 of yacc.c */ -#line 1231 "dcParser.yxx" +#line 1237 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { (yyval.str) = ""; } +#line 2961 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 176: - -/* Line 1464 of yacc.c */ -#line 1239 "dcParser.yxx" +#line 1245 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - current_switch = new DCSwitch((yyvsp[(2) - (6)].str), (yyvsp[(4) - (6)].u.field)); + current_switch = new DCSwitch((yyvsp[-4].str), (yyvsp[-2].u.field)); } +#line 2969 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 177: - -/* Line 1464 of yacc.c */ -#line 1243 "dcParser.yxx" +#line 1249 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { (yyval.u.dswitch) = current_switch; - current_switch = (DCSwitch *)(yyvsp[(7) - (9)].u.parameter); + current_switch = (DCSwitch *)(yyvsp[-2].u.parameter); } +#line 2978 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 183: - -/* Line 1464 of yacc.c */ -#line 1257 "dcParser.yxx" +#line 1263 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { if (!current_switch->is_field_valid()) { yyerror("case declaration required before first element"); - } else if ((yyvsp[(2) - (3)].u.field) != (DCField *)NULL) { - if (!current_switch->add_field((yyvsp[(2) - (3)].u.field))) { - yyerror("Duplicate field name: " + (yyvsp[(2) - (3)].u.field)->get_name()); + } else if ((yyvsp[-1].u.field) != nullptr) { + if (!current_switch->add_field((yyvsp[-1].u.field))) { + yyerror("Duplicate field name: " + (yyvsp[-1].u.field)->get_name()); } } } +#line 2992 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 184: - -/* Line 1464 of yacc.c */ -#line 1270 "dcParser.yxx" +#line 1276 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { current_packer = &default_packer; current_packer->clear_data(); current_packer->begin_pack(current_switch->get_key_parameter()); } +#line 3002 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 185: - -/* Line 1464 of yacc.c */ -#line 1276 "dcParser.yxx" +#line 1282 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { if (!current_packer->end_pack()) { yyerror("Invalid value for switch parameter"); current_switch->add_invalid_case(); } else { - int case_index = current_switch->add_case(current_packer->get_string()); + int case_index = current_switch->add_case(current_packer->get_bytes()); if (case_index == -1) { yyerror("Duplicate case value"); } } } +#line 3018 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 186: - -/* Line 1464 of yacc.c */ -#line 1291 "dcParser.yxx" +#line 1297 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { if (!current_switch->add_default()) { yyerror("Default case already defined"); } } +#line 3028 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 187: - -/* Line 1464 of yacc.c */ -#line 1300 "dcParser.yxx" +#line 1306 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { current_switch->add_break(); } +#line 3036 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 188: - -/* Line 1464 of yacc.c */ -#line 1307 "dcParser.yxx" +#line 1313 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - (yyval.u.field) = (yyvsp[(1) - (1)].u.parameter); + (yyval.u.field) = (yyvsp[0].u.parameter); } +#line 3044 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; case 189: - -/* Line 1464 of yacc.c */ -#line 1311 "dcParser.yxx" +#line 1317 "direct/src/dcparser/dcParser.yxx" /* yacc.c:1651 */ { - (yyval.u.field) = (yyvsp[(1) - (1)].u.parameter); + (yyval.u.field) = (yyvsp[0].u.parameter); } +#line 3052 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ break; - -/* Line 1464 of yacc.c */ -#line 3380 "y.tab.c" +#line 3056 "built/tmp/dcParser.yxx.c" /* yacc.c:1651 */ default: break; } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); YYPOPSTACK (yylen); @@ -3391,7 +3074,7 @@ yyreduce: *++yyvsp = yyval; - /* Now `shift' the result of the reduction. Determine what state + /* Now 'shift' the result of the reduction. Determine what state that goes to, based on the state we popped back to and the rule number reduced by. */ @@ -3406,10 +3089,14 @@ yyreduce: goto yynewstate; -/*------------------------------------. -| yyerrlab -- here on detecting error | -`------------------------------------*/ +/*--------------------------------------. +| yyerrlab -- here on detecting error. | +`--------------------------------------*/ yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); + /* If not already recovering from an error, report this error. */ if (!yyerrstatus) { @@ -3417,37 +3104,36 @@ yyerrlab: #if ! YYERROR_VERBOSE yyerror (YY_("syntax error")); #else +# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ + yyssp, yytoken) { - YYSIZE_T yysize = yysyntax_error (0, yystate, yychar); - if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) - { - YYSIZE_T yyalloc = 2 * yysize; - if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM)) - yyalloc = YYSTACK_ALLOC_MAXIMUM; - if (yymsg != yymsgbuf) - YYSTACK_FREE (yymsg); - yymsg = (char *) YYSTACK_ALLOC (yyalloc); - if (yymsg) - yymsg_alloc = yyalloc; - else - { - yymsg = yymsgbuf; - yymsg_alloc = sizeof yymsgbuf; - } - } - - if (0 < yysize && yysize <= yymsg_alloc) - { - (void) yysyntax_error (yymsg, yystate, yychar); - yyerror (yymsg); - } - else - { - yyerror (YY_("syntax error")); - if (yysize != 0) - goto yyexhaustedlab; - } + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = YYSYNTAX_ERROR; + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == 1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc); + if (!yymsg) + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = 2; + } + else + { + yysyntax_error_status = YYSYNTAX_ERROR; + yymsgp = yymsg; + } + } + yyerror (yymsgp); + if (yysyntax_error_status == 2) + goto yyexhaustedlab; } +# undef YYSYNTAX_ERROR #endif } @@ -3456,20 +3142,20 @@ yyerrlab: if (yyerrstatus == 3) { /* If just tried and failed to reuse lookahead token after an - error, discard it. */ + error, discard it. */ if (yychar <= YYEOF) - { - /* Return failure if at end of input. */ - if (yychar == YYEOF) - YYABORT; - } + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } else - { - yydestruct ("Error: discarding", - yytoken, &yylval); - yychar = YYEMPTY; - } + { + yydestruct ("Error: discarding", + yytoken, &yylval); + yychar = YYEMPTY; + } } /* Else will try to reuse lookahead token after shifting the error @@ -3488,7 +3174,7 @@ yyerrorlab: if (/*CONSTCOND*/ 0) goto yyerrorlab; - /* Do not reclaim the symbols of the rule which action triggered + /* Do not reclaim the symbols of the rule whose action triggered this YYERROR. */ YYPOPSTACK (yylen); yylen = 0; @@ -3501,35 +3187,37 @@ yyerrorlab: | yyerrlab1 -- common code for both syntax error and YYERROR. | `-------------------------------------------------------------*/ yyerrlab1: - yyerrstatus = 3; /* Each real token shifted decrements this. */ + yyerrstatus = 3; /* Each real token shifted decrements this. */ for (;;) { yyn = yypact[yystate]; - if (yyn != YYPACT_NINF) - { - yyn += YYTERROR; - if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) - { - yyn = yytable[yyn]; - if (0 < yyn) - break; - } - } + if (!yypact_value_is_default (yyn)) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } /* Pop the current state because it cannot handle the error token. */ if (yyssp == yyss) - YYABORT; + YYABORT; yydestruct ("Error: popping", - yystos[yystate], yyvsp); + yystos[yystate], yyvsp); YYPOPSTACK (1); yystate = *yyssp; YY_STACK_PRINT (yyss, yyssp); } + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END /* Shift the error token. */ @@ -3553,7 +3241,7 @@ yyabortlab: yyresult = 1; goto yyreturn; -#if !defined(yyoverflow) || YYERROR_VERBOSE +#if !defined yyoverflow || YYERROR_VERBOSE /*-------------------------------------------------. | yyexhaustedlab -- memory exhaustion comes here. | `-------------------------------------------------*/ @@ -3565,16 +3253,21 @@ yyexhaustedlab: yyreturn: if (yychar != YYEMPTY) - yydestruct ("Cleanup: discarding lookahead", - yytoken, &yylval); - /* Do not reclaim the symbols of the rule which action triggered + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval); + } + /* Do not reclaim the symbols of the rule whose action triggered this YYABORT or YYACCEPT. */ YYPOPSTACK (yylen); YY_STACK_PRINT (yyss, yyssp); while (yyssp != yyss) { yydestruct ("Cleanup: popping", - yystos[*yyssp], yyvsp); + yystos[*yyssp], yyvsp); YYPOPSTACK (1); } #ifndef yyoverflow @@ -3585,9 +3278,5 @@ yyreturn: if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); #endif - /* Make sure YYID is used. */ - return YYID (yyresult); + return yyresult; } - - - diff --git a/direct/src/dcparser/dcParser.h.prebuilt b/direct/src/dcparser/dcParser.h.prebuilt index 680eeff59c..8880652bb0 100644 --- a/direct/src/dcparser/dcParser.h.prebuilt +++ b/direct/src/dcparser/dcParser.h.prebuilt @@ -1,20 +1,19 @@ -/* A Bison parser, made by GNU Bison 2.4.2. */ +/* A Bison parser, made by GNU Bison 3.1. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018 Free Software Foundation, Inc. -/* Skeleton interface for Bison's Yacc-like parsers in C - - Copyright (C) 1984, 1989-1990, 2000-2006, 2009-2010 Free Software - Foundation, Inc. - This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program. If not, see . */ @@ -27,66 +26,74 @@ special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. - + This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ +#ifndef YY_DCYY_BUILT_TMP_DCPARSER_YXX_H_INCLUDED +# define YY_DCYY_BUILT_TMP_DCPARSER_YXX_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int dcyydebug; +#endif -/* Tokens. */ +/* Token type. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE - /* Put the tokens into the symbol table, so that GDB and other debuggers - know about them. */ - enum yytokentype { - UNSIGNED_INTEGER = 258, - SIGNED_INTEGER = 259, - REAL = 260, - STRING = 261, - HEX_STRING = 262, - IDENTIFIER = 263, - KEYWORD = 264, - KW_DCLASS = 265, - KW_STRUCT = 266, - KW_FROM = 267, - KW_IMPORT = 268, - KW_TYPEDEF = 269, - KW_KEYWORD = 270, - KW_SWITCH = 271, - KW_CASE = 272, - KW_DEFAULT = 273, - KW_BREAK = 274, - KW_INT8 = 275, - KW_INT16 = 276, - KW_INT32 = 277, - KW_INT64 = 278, - KW_UINT8 = 279, - KW_UINT16 = 280, - KW_UINT32 = 281, - KW_UINT64 = 282, - KW_FLOAT64 = 283, - KW_STRING = 284, - KW_BLOB = 285, - KW_BLOB32 = 286, - KW_INT8ARRAY = 287, - KW_INT16ARRAY = 288, - KW_INT32ARRAY = 289, - KW_UINT8ARRAY = 290, - KW_UINT16ARRAY = 291, - KW_UINT32ARRAY = 292, - KW_UINT32UINT8ARRAY = 293, - KW_CHAR = 294, - START_DC = 295, - START_PARAMETER_VALUE = 296, - START_PARAMETER_DESCRIPTION = 297 - }; + enum yytokentype + { + UNSIGNED_INTEGER = 258, + SIGNED_INTEGER = 259, + REAL = 260, + STRING = 261, + IDENTIFIER = 262, + HEX_STRING = 263, + KEYWORD = 264, + KW_DCLASS = 265, + KW_STRUCT = 266, + KW_FROM = 267, + KW_IMPORT = 268, + KW_TYPEDEF = 269, + KW_KEYWORD = 270, + KW_SWITCH = 271, + KW_CASE = 272, + KW_DEFAULT = 273, + KW_BREAK = 274, + KW_INT8 = 275, + KW_INT16 = 276, + KW_INT32 = 277, + KW_INT64 = 278, + KW_UINT8 = 279, + KW_UINT16 = 280, + KW_UINT32 = 281, + KW_UINT64 = 282, + KW_FLOAT64 = 283, + KW_STRING = 284, + KW_BLOB = 285, + KW_BLOB32 = 286, + KW_INT8ARRAY = 287, + KW_INT16ARRAY = 288, + KW_INT32ARRAY = 289, + KW_UINT8ARRAY = 290, + KW_UINT16ARRAY = 291, + KW_UINT32ARRAY = 292, + KW_UINT32UINT8ARRAY = 293, + KW_CHAR = 294, + START_DC = 295, + START_PARAMETER_VALUE = 296, + START_PARAMETER_DESCRIPTION = 297 + }; #endif /* Tokens. */ #define UNSIGNED_INTEGER 258 #define SIGNED_INTEGER 259 #define REAL 260 #define STRING 261 -#define HEX_STRING 262 -#define IDENTIFIER 263 +#define IDENTIFIER 262 +#define HEX_STRING 263 #define KEYWORD 264 #define KW_DCLASS 265 #define KW_STRUCT 266 @@ -122,15 +129,11 @@ #define START_PARAMETER_VALUE 296 #define START_PARAMETER_DESCRIPTION 297 +/* Value type. */ - -#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED - -# define yystype YYSTYPE /* obsolescent; will be withdrawn */ -# define YYSTYPE_IS_DECLARED 1 -#endif - extern YYSTYPE dcyylval; +int dcyyparse (void); +#endif /* !YY_DCYY_BUILT_TMP_DCPARSER_YXX_H_INCLUDED */ diff --git a/direct/src/dcparser/dcParser.yxx b/direct/src/dcparser/dcParser.yxx index 0a5c3d6aa0..b283c3314b 100644 --- a/direct/src/dcparser/dcParser.yxx +++ b/direct/src/dcparser/dcParser.yxx @@ -57,7 +57,7 @@ dc_init_parser(istream &in, const string &filename, DCFile &file) { } void -dc_init_parser_parameter_value(istream &in, const string &filename, +dc_init_parser_parameter_value(istream &in, const string &filename, DCPacker &packer) { dc_file = nullptr; current_packer = &packer; @@ -89,15 +89,16 @@ dc_cleanup_parser() { %token UNSIGNED_INTEGER %token SIGNED_INTEGER %token REAL -%token STRING HEX_STRING IDENTIFIER +%token STRING IDENTIFIER +%token HEX_STRING %token KEYWORD -%token KW_DCLASS -%token KW_STRUCT -%token KW_FROM -%token KW_IMPORT -%token KW_TYPEDEF -%token KW_KEYWORD +%token KW_DCLASS +%token KW_STRUCT +%token KW_FROM +%token KW_IMPORT +%token KW_TYPEDEF +%token KW_KEYWORD %token KW_SWITCH %token KW_CASE %token KW_DEFAULT @@ -224,7 +225,7 @@ import: { dc_file->add_import_module($2); } - | KW_FROM import_identifier KW_IMPORT + | KW_FROM import_identifier KW_IMPORT { dc_file->add_import_module($2); } @@ -255,7 +256,7 @@ typedef_decl: { if ($2 != nullptr) { DCTypedef *dtypedef = new DCTypedef($2); - + if (!dc_file->add_typedef(dtypedef)) { DCTypedef *old_typedef = dc_file->get_typedef_by_name(dtypedef->get_name()); if (old_typedef->is_bogus_typedef()) { @@ -294,7 +295,7 @@ dclass_or_struct: ; dclass: - KW_DCLASS optional_name + KW_DCLASS optional_name { current_class = new DCClass(dc_file, $2, false, false); } @@ -322,7 +323,7 @@ dclass_name: if (dclass->is_struct()) { yyerror("struct name not allowed"); } - + $$ = dclass; } } @@ -398,7 +399,7 @@ dclass_field: ; struct: - KW_STRUCT optional_name + KW_STRUCT optional_name { current_class = new DCClass(dc_file, $2, true, false); } @@ -426,7 +427,7 @@ struct_name: if (!dstruct->is_struct()) { yyerror("struct name required"); } - + $$ = dstruct; } } @@ -538,7 +539,7 @@ unnamed_parameter: named_parameter_with_default: named_parameter - | named_parameter '=' + | named_parameter '=' { current_packer = &default_packer; current_packer->clear_data(); @@ -553,7 +554,7 @@ named_parameter_with_default: is_valid = $1->is_valid(); } if (current_packer->end_pack()) { - $1->set_default_value(current_packer->get_string()); + $1->set_default_value(current_packer->get_bytes()); } else { if (is_valid) { @@ -568,7 +569,7 @@ named_parameter_with_default: unnamed_parameter_with_default: unnamed_parameter - | unnamed_parameter '=' + | unnamed_parameter '=' { current_packer = &default_packer; current_packer->clear_data(); @@ -583,7 +584,7 @@ unnamed_parameter_with_default: is_valid = $1->is_valid(); } if (current_packer->end_pack()) { - $1->set_default_value(current_packer->get_string()); + $1->set_default_value(current_packer->get_bytes()); } else { if (is_valid) { @@ -663,7 +664,7 @@ simple_type_name: $$ = simple_param; } | simple_type_name '%' number -{ +{ DCSimpleParameter *simple_param = $1->as_simple_parameter(); nassertr(simple_param != nullptr, 0); if (!simple_param->is_numeric_type()) { @@ -703,10 +704,10 @@ type_name: dtypedef = new DCTypedef($1); } } - + dc_file->add_typedef(dtypedef); } - + $$ = dtypedef->make_new_parameter(); } } @@ -974,7 +975,7 @@ parameter_value: { if ($1 != current_packer->get_current_field_name()) { ostringstream strm; - strm << "Got '" << $1 << "', expected '" + strm << "Got '" << $1 << "', expected '" << current_packer->get_current_field_name() << "'"; yyerror(strm.str()); } @@ -1005,7 +1006,7 @@ parameter_actual_value: { current_packer->pack_literal_value($1); } - | '{' + | '{' { current_packer->push(); } @@ -1013,7 +1014,7 @@ parameter_actual_value: { current_packer->pop(); } - | '[' + | '[' { current_packer->push(); } @@ -1021,7 +1022,7 @@ parameter_actual_value: { current_packer->pop(); } - | '(' + | '(' { current_packer->push(); } @@ -1223,7 +1224,7 @@ molecular_atom_list: if ($3 != nullptr) { current_molecular->add_atomic($3); if (!$3->is_bogus_field() && !current_molecular->compare_keywords(*$3)) { - yyerror("Mismatched keywords in molecule between " + + yyerror("Mismatched keywords in molecule between " + current_molecular->get_atomic(0)->get_name() + " and " + $3->get_name()); } @@ -1283,7 +1284,7 @@ switch_case: yyerror("Invalid value for switch parameter"); current_switch->add_invalid_case(); } else { - int case_index = current_switch->add_case(current_packer->get_string()); + int case_index = current_switch->add_case(current_packer->get_bytes()); if (case_index == -1) { yyerror("Duplicate case value"); } diff --git a/direct/src/dcparser/dcParserDefs.h b/direct/src/dcparser/dcParserDefs.h index 28659a37e3..f6f7aacd71 100644 --- a/direct/src/dcparser/dcParserDefs.h +++ b/direct/src/dcparser/dcParserDefs.h @@ -16,6 +16,7 @@ #include "dcbase.h" #include "dcSubatomicType.h" +#include "vector_uchar.h" class DCFile; class DCClass; @@ -61,6 +62,7 @@ public: const DCKeyword *keyword; } u; std::string str; + vector_uchar bytes; }; // The yacc-generated code expects to use the symbol 'YYSTYPE' to refer to the diff --git a/direct/src/dcparser/dcSimpleParameter.cxx b/direct/src/dcparser/dcSimpleParameter.cxx index bd2cb05534..5043307a4f 100644 --- a/direct/src/dcparser/dcSimpleParameter.cxx +++ b/direct/src/dcparser/dcSimpleParameter.cxx @@ -1069,6 +1069,52 @@ pack_string(DCPackData &pack_data, const string &value, } } +/** + * Packs the indicated numeric or string value into the stream. + */ +void DCSimpleParameter:: +pack_blob(DCPackData &pack_data, const vector_uchar &value, + bool &pack_error, bool &range_error) const { + size_t blob_size = value.size(); + + switch (_type) { + case ST_char: + case ST_uint8: + case ST_int8: + if (blob_size == 0) { + pack_error = true; + } else { + if (blob_size != 1) { + range_error = true; + } + _uint_range.validate((unsigned int)value[0], range_error); + do_pack_uint8(pack_data.get_write_pointer(1), (unsigned int)value[0]); + } + break; + + case ST_string: + case ST_blob: + _uint_range.validate(blob_size, range_error); + validate_uint_limits(blob_size, 16, range_error); + if (_num_length_bytes != 0) { + do_pack_uint16(pack_data.get_write_pointer(2), blob_size); + } + pack_data.append_data((const char *)value.data(), blob_size); + break; + + case ST_blob32: + _uint_range.validate(blob_size, range_error); + if (_num_length_bytes != 0) { + do_pack_uint32(pack_data.get_write_pointer(4), blob_size); + } + pack_data.append_data((const char *)value.data(), blob_size); + break; + + default: + pack_error = true; + } +} + /** * Packs the simpleParameter's specified default value (or a sensible default * if no value is specified) into the stream. Returns true if the default @@ -1937,6 +1983,79 @@ unpack_string(const char *data, size_t length, size_t &p, string &value, return; } +/** + * Unpacks the current numeric or string value from the stream. + */ +void DCSimpleParameter:: +unpack_blob(const char *data, size_t length, size_t &p, vector_uchar &value, + bool &pack_error, bool &range_error) const { + // If the type is a single byte, unpack it into a string of length 1. + switch (_type) { + case ST_char: + case ST_int8: + case ST_uint8: + { + if (p + 1 > length) { + pack_error = true; + return; + } + unsigned int int_value = do_unpack_uint8(data + p); + _uint_range.validate(int_value, range_error); + value.resize(1); + value[0] = int_value; + p++; + } + return; + + default: + break; + } + + size_t blob_size; + + if (_num_length_bytes == 0) { + blob_size = _fixed_byte_size; + + } else { + switch (_type) { + case ST_string: + case ST_blob: + if (p + 2 > length) { + pack_error = true; + return; + } + blob_size = do_unpack_uint16(data + p); + p += 2; + break; + + case ST_blob32: + if (p + 4 > length) { + pack_error = true; + return; + } + blob_size = do_unpack_uint32(data + p); + p += 4; + break; + + default: + pack_error = true; + return; + } + } + + _uint_range.validate(blob_size, range_error); + + if (p + blob_size > length) { + pack_error = true; + return; + } + value = vector_uchar((const unsigned char *)data + p, + (const unsigned char *)data + p + blob_size); + p += blob_size; + + return; +} + /** * Internally unpacks the current numeric or string value and validates it * against the type range limits, but does not return the value. Returns true diff --git a/direct/src/dcparser/dcSimpleParameter.h b/direct/src/dcparser/dcSimpleParameter.h index 7b47773a3d..cafd246d62 100644 --- a/direct/src/dcparser/dcSimpleParameter.h +++ b/direct/src/dcparser/dcSimpleParameter.h @@ -62,6 +62,8 @@ public: bool &pack_error, bool &range_error) const; virtual void pack_string(DCPackData &pack_data, const std::string &value, bool &pack_error, bool &range_error) const; + virtual void pack_blob(DCPackData &pack_data, const vector_uchar &value, + bool &pack_error, bool &range_error) const; virtual bool pack_default_value(DCPackData &pack_data, bool &pack_error) const; virtual void unpack_double(const char *data, size_t length, size_t &p, @@ -76,6 +78,8 @@ public: uint64_t &value, bool &pack_error, bool &range_error) const; virtual void unpack_string(const char *data, size_t length, size_t &p, std::string &value, bool &pack_error, bool &range_error) const; + virtual void unpack_blob(const char *data, size_t length, size_t &p, + vector_uchar &value, bool &pack_error, bool &range_error) const; virtual bool unpack_validate(const char *data, size_t length, size_t &p, bool &pack_error, bool &range_error) const; virtual bool unpack_skip(const char *data, size_t length, size_t &p, diff --git a/direct/src/dcparser/dcSwitch.cxx b/direct/src/dcparser/dcSwitch.cxx index 5b23505209..041533efbe 100644 --- a/direct/src/dcparser/dcSwitch.cxx +++ b/direct/src/dcparser/dcSwitch.cxx @@ -109,7 +109,7 @@ get_num_cases() const { * if no case has this value. */ int DCSwitch:: -get_case_by_value(const string &case_value) const { +get_case_by_value(const vector_uchar &case_value) const { CasesByValue::const_iterator vi; vi = _cases_by_value.find(case_value); if (vi != _cases_by_value.end()) { @@ -140,9 +140,9 @@ get_default_case() const { /** * Returns the packed value associated with the indicated case. */ -string DCSwitch:: +vector_uchar DCSwitch:: get_value(int case_index) const { - nassertr(case_index >= 0 && case_index < (int)_cases.size(), string()); + nassertr(case_index >= 0 && case_index < (int)_cases.size(), vector_uchar()); return _cases[case_index]->_value; } @@ -198,7 +198,7 @@ is_field_valid() const { * -1. This is normally called only by the parser. */ int DCSwitch:: -add_case(const string &value) { +add_case(const vector_uchar &value) { int case_index = (int)_cases.size(); if (!_cases_by_value.insert(CasesByValue::value_type(value, case_index)).second) { add_invalid_case(); @@ -283,7 +283,8 @@ add_break() { const DCPackerInterface *DCSwitch:: apply_switch(const char *value_data, size_t length) const { CasesByValue::const_iterator vi; - vi = _cases_by_value.find(string(value_data, length)); + vi = _cases_by_value.find(vector_uchar((const unsigned char *)value_data, + (const unsigned char *)value_data + length)); if (vi != _cases_by_value.end()) { return _cases[(*vi).second]->_fields; } @@ -421,7 +422,7 @@ generate_hash(HashGenerator &hashgen) const { Cases::const_iterator ci; for (ci = _cases.begin(); ci != _cases.end(); ++ci) { const SwitchCase *dcase = (*ci); - hashgen.add_string(dcase->_value); + hashgen.add_blob(dcase->_value); const SwitchFields *fields = dcase->_fields; hashgen.add_int(fields->_fields.size()); @@ -702,7 +703,7 @@ do_check_match(const DCPackerInterface *) const { * */ DCSwitch::SwitchCase:: -SwitchCase(const string &value, DCSwitch::SwitchFields *fields) : +SwitchCase(const vector_uchar &value, DCSwitch::SwitchFields *fields) : _value(value), _fields(fields) { diff --git a/direct/src/dcparser/dcSwitch.h b/direct/src/dcparser/dcSwitch.h index ac58f5439d..31e69b452b 100644 --- a/direct/src/dcparser/dcSwitch.h +++ b/direct/src/dcparser/dcSwitch.h @@ -40,18 +40,18 @@ PUBLISHED: DCField *get_key_parameter() const; int get_num_cases() const; - int get_case_by_value(const std::string &case_value) const; + int get_case_by_value(const vector_uchar &case_value) const; DCPackerInterface *get_case(int n) const; DCPackerInterface *get_default_case() const; - std::string get_value(int case_index) const; + vector_uchar get_value(int case_index) const; int get_num_fields(int case_index) const; DCField *get_field(int case_index, int n) const; DCField *get_field_by_name(int case_index, const std::string &name) const; public: bool is_field_valid() const; - int add_case(const std::string &value); + int add_case(const vector_uchar &value); void add_invalid_case(); bool add_default(); bool add_field(DCField *field); @@ -98,13 +98,13 @@ public: class SwitchCase { public: - SwitchCase(const std::string &value, SwitchFields *fields); + SwitchCase(const vector_uchar &value, SwitchFields *fields); ~SwitchCase(); bool do_check_match_switch_case(const SwitchCase *other) const; public: - std::string _value; + vector_uchar _value; SwitchFields *_fields; }; @@ -137,7 +137,7 @@ private: bool _fields_added; // This map indexes into the _cases vector, above. - typedef pmap CasesByValue; + typedef pmap CasesByValue; CasesByValue _cases_by_value; }; diff --git a/direct/src/dcparser/hashGenerator.cxx b/direct/src/dcparser/hashGenerator.cxx index 900fd4bf42..bd3a6553b4 100644 --- a/direct/src/dcparser/hashGenerator.cxx +++ b/direct/src/dcparser/hashGenerator.cxx @@ -56,6 +56,18 @@ add_string(const std::string &str) { } } +/** + * Adds a blob to the hash, by breaking it down into a sequence of integers. + */ +void HashGenerator:: +add_blob(const vector_uchar &bytes) { + add_int(bytes.size()); + vector_uchar::const_iterator bi; + for (bi = bytes.begin(); bi != bytes.end(); ++bi) { + add_int(*bi); + } +} + /** * Returns the hash number generated. */ diff --git a/direct/src/dcparser/hashGenerator.h b/direct/src/dcparser/hashGenerator.h index 29ae1a6609..472e858cb0 100644 --- a/direct/src/dcparser/hashGenerator.h +++ b/direct/src/dcparser/hashGenerator.h @@ -16,6 +16,7 @@ #include "dcbase.h" #include "primeNumberGenerator.h" +#include "vector_uchar.h" /** * This class generates an arbitrary hash number from a sequence of ints. @@ -26,6 +27,7 @@ public: void add_int(int num); void add_string(const std::string &str); + void add_blob(const vector_uchar &bytes); unsigned long get_hash() const; diff --git a/direct/src/directbase/ThreeUpStart.py b/direct/src/directbase/ThreeUpStart.py index b0a78fcef9..87158c937a 100644 --- a/direct/src/directbase/ThreeUpStart.py +++ b/direct/src/directbase/ThreeUpStart.py @@ -1,7 +1,7 @@ print('ThreeUpStart: Starting up environment.') -from pandac.PandaModules import * +from panda3d.core import * from direct.showbase.PythonUtil import * from direct.showbase import ThreeUpShow diff --git a/direct/src/directscripts/Doxyfile.cxx b/direct/src/directscripts/Doxyfile.cxx index 8b4fda2230..6c81025960 100644 --- a/direct/src/directscripts/Doxyfile.cxx +++ b/direct/src/directscripts/Doxyfile.cxx @@ -797,7 +797,15 @@ EXCLUDE = dtool/src/parser-inc \ panda/src/linmath/fltnames.h \ panda/src/linmath/dblnames.h \ panda/src/linmath/dbl2fltnames.h \ - panda/src/linmath/flt2dblnames.h + panda/src/linmath/flt2dblnames.h \ + panda/src/android \ + panda/src/iphone \ + panda/src/tinydisplay \ + panda/src/movies/dr_flac.h \ + dtool/src/dtoolbase/pdtoa.cxx \ + dtool/src/dtoolutil/panda_getopt_long.h \ + dtool/src/dtoolutil/panda_getopt_impl.h \ + dtool/src/dtoolutil/panda_getopt_impl.cxx # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded @@ -1116,7 +1124,7 @@ HTML_EXTRA_FILES = # Minimum value: 0, maximum value: 359, default value: 220. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_COLORSTYLE_HUE = 228 +HTML_COLORSTYLE_HUE = 251 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors # in the HTML output. For a value of 0 the output will use grayscales only. A @@ -1124,7 +1132,7 @@ HTML_COLORSTYLE_HUE = 228 # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_COLORSTYLE_SAT = 99 +HTML_COLORSTYLE_SAT = 150 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the # luminance component of the colors in the HTML output. Values below 100 @@ -1135,7 +1143,7 @@ HTML_COLORSTYLE_SAT = 99 # Minimum value: 40, maximum value: 240, default value: 80. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_COLORSTYLE_GAMMA = 56 +HTML_COLORSTYLE_GAMMA = 39 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting this @@ -1972,7 +1980,13 @@ PREDEFINED = TVOLATILE= \ EXTEND= \ ALLOC_DELETED_CHAIN(x)= \ BLOCKING= \ - TYPENAME=typename + TYPENAME=typename \ + MAKE_PROPERTY(x)= \ + MAKE_PROPERTY2(x)= \ + MAKE_SEQ(x)= \ + MAKE_SEQ_PROPERTY(x)= \ + MAKE_MAP_PROPERTY(x)= \ + MAKE_MAP_KEYS_SEQ(x)= # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this # tag can be used to specify a list of macro names that should be expanded. The diff --git a/direct/src/directscripts/Doxyfile.python b/direct/src/directscripts/Doxyfile.python index 4bcfb58727..2119f7b321 100644 --- a/direct/src/directscripts/Doxyfile.python +++ b/direct/src/directscripts/Doxyfile.python @@ -665,7 +665,9 @@ EXCLUDE_SYMLINKS = NO # for example use the pattern */test/* EXCLUDE_PATTERNS = */Opt*-*/* \ - */CVS/* + */CVS/* \ + */.git/* \ + */__pycache__/* # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the @@ -862,13 +864,13 @@ HTML_STYLESHEET = $(DOXYGEN_HTML_STYLESHEET) # 180 is cyan, 240 is blue, 300 purple, and 360 is red again. # The allowed range is 0 to 359. -HTML_COLORSTYLE_HUE = 228 +HTML_COLORSTYLE_HUE = 251 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of # the colors in the HTML output. For a value of 0 the output will use # grayscales only. A value of 255 will produce the most vivid colors. -HTML_COLORSTYLE_SAT = 99 +HTML_COLORSTYLE_SAT = 150 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to # the luminance component of the colors in the HTML output. Values below @@ -877,7 +879,7 @@ HTML_COLORSTYLE_SAT = 99 # so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, # and 100 does not change the gamma. -HTML_COLORSTYLE_GAMMA = 56 +HTML_COLORSTYLE_GAMMA = 39 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting diff --git a/direct/src/directscripts/extract_docs.py b/direct/src/directscripts/extract_docs.py index b6ad69e946..caead7e4dc 100644 --- a/direct/src/directscripts/extract_docs.py +++ b/direct/src/directscripts/extract_docs.py @@ -9,9 +9,20 @@ from __future__ import print_function __all__ = [] -import os +import os, sys +from distutils import sysconfig import panda3d, pandac -from panda3d.dtoolconfig import * +from panda3d.interrogatedb import * + + +if 'interrogate_element_is_sequence' not in globals(): + def interrogate_element_is_sequence(element): + return False + +if 'interrogate_element_is_mapping' not in globals(): + def interrogate_element_is_mapping(element): + return False + LICENSE = """PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University. All rights reserved. @@ -19,6 +30,16 @@ 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.\"""".split("\n") +MAINPAGE = """@mainpage Panda3D Python API Reference +Welcome to the Panda3D API reference. + +Use the links at the top of this page to browse through the list of modules or +the list of classes. + +This reference is automatically generated from comments in the source code. +""" + + def comment(code): if not code: return "" @@ -45,49 +66,16 @@ def comment(code): return '' def block_comment(code): - if not code: + code = code.strip() + + if not code.startswith('///<') and '@verbatim' not in code: + code = code.replace('<', '\\<').replace('>', '\\>') + + if not code or code[0] != '/': + # Not really a comment; get rid of it. return "" - lines = code.split("\n") - newlines = [] - indent = 0 - reading_desc = False - - for line in lines: - if line.startswith("////"): - continue - - line = line.rstrip() - strline = line.lstrip('/ \t') - - if ':' in strline: - pre, post = strline.split(':', 1) - pre = pre.rstrip() - if pre == "Description": - strline = post.lstrip() - elif pre in ("Class", "Access", "Function", "Created by", "Enum"): - continue - - if strline or len(newlines) > 0: - newlines.append('/// ' + strline) - - #if reading_desc: - # newlines.append('/// ' + line[min(indent, len(line) - len(strline)):]) - #else: - # # A "Description:" text starts the description. - # if strline.startswith("Description"): - # strline = strline[11:].lstrip(': \t') - # indent = len(line) - len(strline) - # reading_desc = True - # newlines.append('/// ' + strline) - # else: - # print line - - newcode = '\n'.join(newlines) - if len(newcode) > 0: - return newcode - else: - return "" + return code def translateFunctionName(name): if name.startswith("__"): @@ -139,11 +127,17 @@ def translated_type_name(type, scoped=True): return "object" elif typename == "PN_stdfloat": return "float" + elif typename == "size_t": + return "int" if interrogate_type_is_atomic(type): token = interrogate_type_atomic_token(type) if token == 7: return 'str' + elif token == 8: + return 'long' + elif token == 9: + return 'NoneType' else: return typename @@ -156,12 +150,25 @@ def translated_type_name(type, scoped=True): else: return typename + def processElement(handle, element): if interrogate_element_has_comment(element): print(comment(interrogate_element_comment(element)), file=handle) + elif interrogate_element_has_getter(element): + # If the property has no comment, use the comment of the getter. + getter = interrogate_element_getter(element) + if interrogate_function_has_comment(getter): + print(block_comment(interrogate_function_comment(getter)), file=handle) + + if interrogate_element_is_mapping(element) or \ + interrogate_element_is_sequence(element): + suffix = "[]" + else: + suffix = "" print(translated_type_name(interrogate_element_type(element)), end=' ', file=handle) - print(interrogate_element_name(element) + ';', file=handle) + print(interrogate_element_name(element) + suffix + ';', file=handle) + def processFunction(handle, function, isConstructor = False): for i_wrapper in range(interrogate_function_number_of_python_wrappers(function)): @@ -195,6 +202,7 @@ def processFunction(handle, function, isConstructor = False): print(");", file=handle) + def processType(handle, type): typename = translated_type_name(type, scoped=False) derivations = [ translated_type_name(interrogate_type_get_derivation(type, n)) for n in range(interrogate_type_number_of_derivations(type)) ] @@ -211,8 +219,10 @@ def processType(handle, type): print(interrogate_type_enum_value_name(type, i_value), "=", interrogate_type_enum_value(type, i_value), ",", file=handle) elif interrogate_type_is_typedef(type): - wrapped_type = translated_type_name(interrogate_type_wrapped_type(type)) - print("typedef %s %s;" % (wrapped_type, typename), file=handle) + wrapped_type = interrogate_type_wrapped_type(type) + if interrogate_type_is_global(wrapped_type): + wrapped_type_name = translated_type_name(wrapped_type) + print("typedef %s %s;" % (wrapped_type_name, typename), file=handle) return else: if interrogate_type_is_struct(type): @@ -249,6 +259,7 @@ def processType(handle, type): print("};", file=handle) def processModule(handle, package): + print("Processing module %s" % (package)) print("namespace %s {" % package, file=handle) if package != "core": @@ -280,22 +291,39 @@ def processModule(handle, package): if __name__ == "__main__": handle = open("pandadoc.hpp", "w") + mainpage = MAINPAGE.strip() + if mainpage: + print("/**\n * " + mainpage.replace('\n', '\n * ') + '\n */', file=handle) + print(comment("Panda3D modules that are implemented in C++."), file=handle) print("namespace panda3d {", file=handle) # Determine the path to the interrogatedb files - interrogate_add_search_directory(os.path.join(os.path.dirname(pandac.__file__), "..", "..", "etc")) - interrogate_add_search_directory(os.path.join(os.path.dirname(pandac.__file__), "input")) + pandac = os.path.dirname(pandac.__file__) + interrogate_add_search_directory(os.path.join(pandac, "..", "..", "etc")) + interrogate_add_search_directory(os.path.join(pandac, "input")) import panda3d.core processModule(handle, "core") + # Determine the suffix for the extension modules. + if sys.version_info >= (3, 0): + import _imp + ext_suffix = _imp.extension_suffixes()[0] + elif sys.platform == "win32": + ext_suffix = ".pyd" + else: + ext_suffix = ".so" + for lib in os.listdir(os.path.dirname(panda3d.__file__)): - if lib.endswith(('.pyd', '.so')) and not lib.startswith('core.'): - module_name = os.path.splitext(lib)[0] + if lib.endswith(ext_suffix) and not lib.startswith('core.'): + module_name = lib[:-len(ext_suffix)] __import__("panda3d." + module_name) processModule(handle, module_name) - print("}", file=handle) handle.close() + + print("Wrote output to pandadoc.hpp. You can now run:") + print() + print(" doxygen built/direct/directscripts/Doxyfile.python") diff --git a/direct/src/showutil/FreezeTool.py b/direct/src/dist/FreezeTool.py similarity index 62% rename from direct/src/showutil/FreezeTool.py rename to direct/src/dist/FreezeTool.py index aafbc24124..5b2cd99043 100644 --- a/direct/src/showutil/FreezeTool.py +++ b/direct/src/dist/FreezeTool.py @@ -7,8 +7,12 @@ import os import marshal import imp import platform -from io import StringIO +import struct +import io import distutils.sysconfig as sysconf +import zipfile + +from . import pefile # Temporary (?) try..except to protect against unbuilt p3extend_frozen. try: @@ -28,11 +32,17 @@ isDebugBuild = (python.lower().endswith('_d')) # These are modules that Python always tries to import up-front. They # must be frozen in any main.exe. +# NB. if encodings are removed, be sure to remove them from the shortcut in +# deploy-stub.c. startupModules = [ - 'encodings.cp1252', 'encodings.latin_1', 'encodings.utf_8', - ] + 'imp', 'encodings', 'encodings.*', +] if sys.version_info >= (3, 0): + # Modules specific to Python 3 startupModules += ['io', 'marshal', 'importlib.machinery', 'importlib.util'] +else: + # Modules specific to Python 2 + startupModules += [] # These are some special init functions for some built-in Python modules that # deviate from the standard naming convention. A value of None means that a @@ -42,10 +52,72 @@ builtinInitFuncs = { '__builtin__': None, 'sys': None, 'exceptions': None, - '_imp': 'PyInit_imp', '_warnings': '_PyWarnings_Init', 'marshal': 'PyMarshal_Init', } +if sys.version_info < (3, 7): + builtinInitFuncs['_imp'] = 'PyInit_imp' + +# These are modules that are not found normally for these modules. Add them +# to an include list so users do not have to do this manually. +try: + from pytest import freeze_includes as pytest_imports +except ImportError: + def pytest_imports(): + return [] + +hiddenImports = { + 'pytest': pytest_imports(), + 'pkg_resources': [ + 'pkg_resources.*.*', + ], + 'xml.etree.cElementTree': ['xml.etree.ElementTree'], + 'datetime': ['_strptime'], + 'keyring.backends': ['keyring.backends.*'], + 'matplotlib.font_manager': ['encodings.mac_roman'], + 'direct.particles': ['direct.particles.ParticleManagerGlobal'], + 'numpy.core._multiarray_umath': [ + 'numpy.core._internal', + 'numpy.core._dtype_ctypes', + 'numpy.core._methods', + ], +} + +if sys.version_info >= (3,): + hiddenImports['matplotlib.backends._backend_tk'] = ['tkinter'] +else: + hiddenImports['matplotlib.backends._backend_tk'] = ['Tkinter'] + + +# These are overrides for specific modules. +overrideModules = { + # Used by the warnings module, among others, to get line numbers. Since + # we set __file__, this would cause it to try and extract Python code + # lines from the main executable, which we don't want. + 'linecache': """__all__ = ["getline", "clearcache", "checkcache"] + +cache = {} + +def getline(filename, lineno, module_globals=None): + return '' + +def clearcache(): + global cache + cache = {} + +def getlines(filename, module_globals=None): + return [] + +def checkcache(filename=None): + pass + +def updatecache(filename, module_globals=None): + pass + +def lazycache(filename, module_globals): + pass +""", +} # These are missing modules that we've reported already this session. reportedMissing = {} @@ -250,13 +322,17 @@ frozenMainCode = """ #if PY_MAJOR_VERSION >= 3 #include + +#if PY_MINOR_VERSION < 5 +#define Py_DecodeLocale _Py_char2wchar +#endif #endif #ifdef MS_WINDOWS extern void PyWinFreeze_ExeInit(void); extern void PyWinFreeze_ExeTerm(void); -extern DL_IMPORT(int) PyImport_ExtendInittab(struct _inittab *newtab); +extern PyAPI_FUNC(int) PyImport_ExtendInittab(struct _inittab *newtab); #endif /* Main program */ @@ -271,18 +347,14 @@ Py_FrozenMain(int argc, char **argv) #if PY_MAJOR_VERSION >= 3 int i; - char *oldloc = NULL; + char *oldloc; wchar_t **argv_copy = NULL; /* We need a second copies, as Python might modify the first one. */ wchar_t **argv_copy2 = NULL; if (argc > 0) { - argv_copy = PyMem_RawMalloc(sizeof(wchar_t*) * argc); - argv_copy2 = PyMem_RawMalloc(sizeof(wchar_t*) * argc); - if (!argv_copy || !argv_copy2) { - fprintf(stderr, \"out of memory\\n\"); - goto error; - } + argv_copy = (wchar_t **)alloca(sizeof(wchar_t *) * argc); + argv_copy2 = (wchar_t **)alloca(sizeof(wchar_t *) * argc); } #endif @@ -302,12 +374,7 @@ Py_FrozenMain(int argc, char **argv) } #if PY_MAJOR_VERSION >= 3 - oldloc = _PyMem_RawStrdup(setlocale(LC_ALL, NULL)); - if (!oldloc) { - fprintf(stderr, \"out of memory\\n\"); - goto error; - } - + oldloc = setlocale(LC_ALL, NULL); setlocale(LC_ALL, \"\"); for (i = 0; i < argc; i++) { argv_copy[i] = Py_DecodeLocale(argv[i], NULL); @@ -320,8 +387,6 @@ Py_FrozenMain(int argc, char **argv) } } setlocale(LC_ALL, oldloc); - PyMem_RawFree(oldloc); - oldloc = NULL; #endif #ifdef MS_WINDOWS @@ -371,13 +436,15 @@ Py_FrozenMain(int argc, char **argv) #if PY_MAJOR_VERSION >= 3 error: - PyMem_RawFree(argv_copy); if (argv_copy2) { - for (i = 0; i < argc; i++) + for (i = 0; i < argc; i++) { +#if PY_MINOR_VERSION >= 4 PyMem_RawFree(argv_copy2[i]); - PyMem_RawFree(argv_copy2); +#else + PyMem_Free(argv_copy2[i]); +#endif + } } - PyMem_RawFree(oldloc); #endif return sts; } @@ -649,7 +716,7 @@ class Freezer: return 'ModuleDef(%s)' % (', '.join(args)) def __init__(self, previous = None, debugLevel = 0, - platform = None): + platform = None, path=None): # Normally, we are freezing for our own platform. Change this # if untrue. self.platform = platform or PandaSystem.getPlatform() @@ -660,6 +727,10 @@ class Freezer: # default object will be created when it is needed. self.cenv = None + # This is the search path to use for Python modules. Leave it + # to the default value of None to use sys.path. + self.path = path + # The filename extension to append to the source file before # compiling. self.sourceExtension = '.c' @@ -715,10 +786,46 @@ class Freezer: # special path mangling.) for moduleName, module in list(sys.modules.items()): if module and hasattr(module, '__path__'): - path = getattr(module, '__path__') + path = list(getattr(module, '__path__')) if path: modulefinder.AddPackagePath(moduleName, path[0]) + # Suffix/extension for Python C extension modules + if self.platform == PandaSystem.getPlatform(): + self.moduleSuffixes = imp.get_suffixes() + + # Set extension for Python files to binary mode + for i, suffix in enumerate(self.moduleSuffixes): + if suffix[2] == imp.PY_SOURCE: + self.moduleSuffixes[i] = (suffix[0], 'rb', imp.PY_SOURCE) + else: + self.moduleSuffixes = [('.py', 'rb', 1), ('.pyc', 'rb', 2)] + if 'linux' in self.platform: + self.moduleSuffixes += [ + ('.cpython-{0}{1}m-x86_64-linux-gnu.so'.format(*sys.version_info), 'rb', 3), + ('.cpython-{0}{1}m-i686-linux-gnu.so'.format(*sys.version_info), 'rb', 3), + ('.abi{0}.so'.format(sys.version_info[0]), 'rb', 3), + ('.so', 'rb', 3), + ] + elif 'win' in self.platform: + self.moduleSuffixes += [ + ('.cp{0}{1}-win_amd64.pyd'.format(*sys.version_info), 'rb', 3), + ('.cp{0}{1}-win32.pyd'.format(*sys.version_info), 'rb', 3), + ('.pyd', 'rb', 3), + ] + elif 'mac' in self.platform: + self.moduleSuffixes += [ + ('.cpython-{0}{1}m-darwin.so'.format(*sys.version_info), 'rb', 3), + ('.abi{0}.so'.format(sys.version_info[0]), 'rb', 3), + ('.so', 'rb', 3), + ] + else: # FreeBSD et al. + self.moduleSuffixes += [ + ('.cpython-{0}{1}m.so'.format(*sys.version_info), 'rb', 3), + ('.abi{0}.so'.format(*sys.version_info), 'rb', 3), + ('.so', 'rb', 3), + ] + def excludeFrom(self, freezer): """ Excludes all modules that have already been processed by the indicated FreezeTool. This is equivalent to passing the @@ -843,6 +950,60 @@ class Freezer: return modules + def _gatherSubmodules(self, moduleName, implicit = False, newName = None, + filename = None, guess = False, fromSource = None, + text = None): + if not newName: + newName = moduleName + + assert(moduleName.endswith('.*')) + assert(newName.endswith('.*')) + + mdefs = {} + + # Find the parent module, so we can get its directory. + parentName = moduleName[:-2] + newParentName = newName[:-2] + parentNames = [(parentName, newParentName)] + + if parentName.endswith('.*'): + assert(newParentName.endswith('.*')) + # Another special case. The parent name "*" means to + # return all possible directories within a particular + # directory. + + topName = parentName[:-2] + newTopName = newParentName[:-2] + parentNames = [] + modulePath = self.getModulePath(topName) + if modulePath: + for dirname in modulePath: + for basename in os.listdir(dirname): + if os.path.exists(os.path.join(dirname, basename, '__init__.py')): + parentName = '%s.%s' % (topName, basename) + newParentName = '%s.%s' % (newTopName, basename) + if self.getModulePath(parentName): + parentNames.append((parentName, newParentName)) + + for parentName, newParentName in parentNames: + modules = self.getModuleStar(parentName) + + if modules == None: + # It's actually a regular module. + mdef[newParentName] = self.ModuleDef( + parentName, implicit = implicit, guess = guess, + fromSource = fromSource, text = text) + + else: + # Now get all the py files in the parent directory. + for basename in modules: + moduleName = '%s.%s' % (parentName, basename) + newName = '%s.%s' % (newParentName, basename) + mdefs[newName] = self.ModuleDef( + moduleName, implicit = implicit, guess = True, + fromSource = fromSource) + return mdefs + def addModule(self, moduleName, implicit = False, newName = None, filename = None, guess = False, fromSource = None, text = None): @@ -869,49 +1030,9 @@ class Freezer: newName = moduleName if moduleName.endswith('.*'): - assert(newName.endswith('.*')) - # Find the parent module, so we can get its directory. - parentName = moduleName[:-2] - newParentName = newName[:-2] - parentNames = [(parentName, newParentName)] - - if parentName.endswith('.*'): - assert(newParentName.endswith('.*')) - # Another special case. The parent name "*" means to - # return all possible directories within a particular - # directory. - - topName = parentName[:-2] - newTopName = newParentName[:-2] - parentNames = [] - modulePath = self.getModulePath(topName) - if modulePath: - for dirname in modulePath: - for basename in os.listdir(dirname): - if os.path.exists(os.path.join(dirname, basename, '__init__.py')): - parentName = '%s.%s' % (topName, basename) - newParentName = '%s.%s' % (newTopName, basename) - if self.getModulePath(parentName): - parentNames.append((parentName, newParentName)) - - for parentName, newParentName in parentNames: - modules = self.getModuleStar(parentName) - - if modules == None: - # It's actually a regular module. - self.modules[newParentName] = self.ModuleDef( - parentName, implicit = implicit, guess = guess, - fromSource = fromSource, text = text) - - else: - # Now get all the py files in the parent directory. - for basename in modules: - moduleName = '%s.%s' % (parentName, basename) - newName = '%s.%s' % (newParentName, basename) - mdef = self.ModuleDef( - moduleName, implicit = implicit, guess = True, - fromSource = fromSource) - self.modules[newName] = mdef + self.modules.update(self._gatherSubmodules( + moduleName, implicit, newName, filename, + guess, fromSource, text)) else: # A normal, explicit module name. self.modules[newName] = self.ModuleDef( @@ -935,7 +1056,7 @@ class Freezer: for moduleName in startupModules: if moduleName not in self.modules: - self.modules[moduleName] = self.ModuleDef(moduleName, implicit = True) + self.addModule(moduleName, implicit = True) # Excluding a parent module also excludes all its # (non-explicit) children, unless the parent has allowChildren @@ -968,7 +1089,7 @@ class Freezer: else: includes.append(mdef) - self.mf = PandaModuleFinder(excludes = list(excludeDict.keys())) + self.mf = PandaModuleFinder(excludes=list(excludeDict.keys()), suffixes=self.moduleSuffixes, path=self.path) # Attempt to import the explicit modules into the modulefinder. @@ -1001,6 +1122,20 @@ class Freezer: # module. pass + # Check if any new modules we found have "hidden" imports + for origName in list(self.mf.modules.keys()): + hidden = hiddenImports.get(origName, []) + for modname in hidden: + if modname.endswith('.*'): + mdefs = self._gatherSubmodules(modname, implicit = True) + for mdef in mdefs.values(): + try: + self.__loadModule(mdef) + except ImportError: + pass + else: + self.__loadModule(self.ModuleDef(modname, implicit = True)) + # Now, any new modules we found get added to the export list. for origName in list(self.mf.modules.keys()): if origName not in origToNewName: @@ -1073,11 +1208,11 @@ class Freezer: stuff = ("", "rb", imp.PY_COMPILED) self.mf.load_module(mdef.moduleName, fp, pathname, stuff) else: + stuff = ("", "rb", imp.PY_SOURCE) if mdef.text: - fp = StringIO(mdef.text) + fp = io.StringIO(mdef.text) else: - fp = open(pathname, 'U') - stuff = ("", "r", imp.PY_SOURCE) + fp = open(pathname, 'rb') self.mf.load_module(mdef.moduleName, fp, pathname, stuff) if tempPath: @@ -1394,7 +1529,7 @@ class Freezer: libName = module.split('.')[-1] initFunc = builtinInitFuncs.get(module, 'PyInit_' + libName) if initFunc: - text += 'extern DL_IMPORT(PyObject) *%s(void);\n' % (initFunc) + text += 'extern PyAPI_FUNC(PyObject) *%s(void);\n' % (initFunc) text += '\n' if sys.platform == "win32": @@ -1417,7 +1552,7 @@ class Freezer: libName = module.split('.')[-1] initFunc = builtinInitFuncs.get(module, 'init' + libName) if initFunc: - text += 'extern DL_IMPORT(void) %s(void);\n' % (initFunc) + text += 'extern PyAPI_FUNC(void) %s(void);\n' % (initFunc) text += '\n' if sys.platform == "win32": @@ -1549,6 +1684,498 @@ class Freezer: return target + def generateRuntimeFromStub(self, target, stub_file, use_console, fields={}, + log_append=False): + # We must have a __main__ module to make an exe file. + if not self.__writingModule('__main__'): + message = "Can't generate an executable without a __main__ module." + raise Exception(message) + + if self.platform.startswith('win'): + modext = '.pyd' + else: + modext = '.so' + + # First gather up the strings and code for all the module names, and + # put those in a string pool. + pool = b"" + strings = set() + + for moduleName, mdef in self.getModuleDefs(): + strings.add(moduleName.encode('ascii')) + + for value in fields.values(): + if value is not None: + strings.add(value.encode('utf-8')) + + # Sort by length descending, allowing reuse of partial strings. + strings = sorted(strings, key=lambda str:-len(str)) + string_offsets = {} + + # Now add the strings to the pool, and collect the offsets relative to + # the beginning of the pool. + for string in strings: + # First check whether it's already in there; it could be part of + # a longer string. + offset = pool.find(string + b'\0') + if offset < 0: + offset = len(pool) + pool += string + b'\0' + string_offsets[string] = offset + + # Now go through the modules and add them to the pool as well. These + # are not 0-terminated, but we later record their sizes and names in + # a table after the blob header. + moduleList = [] + + for moduleName, mdef in self.getModuleDefs(): + origName = mdef.moduleName + if mdef.forbid: + # Explicitly disallow importing this module. + moduleList.append((moduleName, 0, 0)) + continue + + # For whatever it's worth, align the code blocks. + if len(pool) & 3 != 0: + pad = (4 - (len(pool) & 3)) + pool += b'\0' * pad + + assert not mdef.exclude + # Allow importing this module. + module = self.mf.modules.get(origName, None) + code = getattr(module, "__code__", None) + if code: + code = marshal.dumps(code) + size = len(code) + if getattr(module, "__path__", None): + # Indicate package by negative size + size = -size + moduleList.append((moduleName, len(pool), size)) + pool += code + continue + + # This is a module with no associated Python code. It is either + # an extension module or a builtin module. Get the filename, if + # it is the former. + extensionFilename = getattr(module, '__file__', None) + + if extensionFilename: + self.extras.append((moduleName, extensionFilename)) + + # If it is a submodule of a frozen module, Python will have + # trouble importing it as a builtin module. Synthesize a frozen + # module that loads it dynamically. + if '.' in moduleName: + if self.platform.startswith("macosx") and not use_console: + # We write the Frameworks directory to sys.path[0]. + code = 'import sys;del sys.modules["%s"];import sys,os,imp;imp.load_dynamic("%s",os.path.join(sys.path[0], "%s%s"))' % (moduleName, moduleName, moduleName, modext) + else: + code = 'import sys;del sys.modules["%s"];import sys,os,imp;imp.load_dynamic("%s",os.path.join(os.path.dirname(sys.executable), "%s%s"))' % (moduleName, moduleName, moduleName, modext) + if sys.version_info >= (3, 2): + code = compile(code, moduleName, 'exec', optimize=2) + else: + code = compile(code, moduleName, 'exec') + code = marshal.dumps(code) + moduleList.append((moduleName, len(pool), len(code))) + pool += code + + # Determine the format of the header and module list entries depending + # on the platform. + num_pointers = 12 + stub_data = bytearray(stub_file.read()) + bitnesses = self._get_executable_bitnesses(stub_data) + + header_layouts = { + 32: 'I', data, 4) + bitnesses = set() + ptr = 8 + for i in range(num_fat): + cputype, cpusubtype, offset, size, align = \ + struct.unpack_from('>IIIII', data, ptr) + ptr += 20 + + if (cputype & 0x1000000) != 0: + bitnesses.add(64) + else: + bitnesses.add(32) + return tuple(bitnesses) + + elif data[:4] in (b'\xCA\xFE\xBA\xBF', b'\xBF\xBA\xFE\xCA'): + # Universal binary with 64-bit offsets. + num_fat, = struct.unpack_from('>I', data, 4) + bitnesses = set() + ptr = 8 + for i in range(num_fat): + cputype, cpusubtype, offset, size, align = \ + struct.unpack_from('>QQQQQ', data, ptr) + ptr += 40 + + if (cputype & 0x1000000) != 0: + bitnesses.add(64) + else: + bitnesses.add(32) + return tuple(bitnesses) + + def _replace_symbol(self, data, symbol_name, replacement, bitness=None): + """We store a custom section in the binary file containing a header + containing offsets to the binary data. + If bitness is set, and the binary in question is a macOS universal + binary, it only replaces for binaries with the given bitness. """ + + if data.startswith(b'MZ'): + # A Windows PE file. + pe = pefile.PEFile() + pe.read(io.BytesIO(data)) + addr = pe.get_export_address(symbol_name) + if addr is not None: + # We found it, return its offset in the file. + offset = pe.get_address_offset(addr) + if offset is not None: + data[offset:offset+len(replacement)] = replacement + return True + + elif data.startswith(b"\177ELF"): + return self._replace_symbol_elf(data, symbol_name, replacement) + + elif data[:4] in (b'\xFE\xED\xFA\xCE', b'\xCE\xFA\xED\xFE', + b'\xFE\xED\xFA\xCF', b'\xCF\xFA\xED\xFE'): + off = self._find_symbol_macho(data, symbol_name) + if off is not None: + data[off:off+len(replacement)] = replacement + return True + return False + + elif data[:4] in (b'\xCA\xFE\xBA\xBE', b'\xBE\xBA\xFE\xCA'): + # Universal binary with 32-bit offsets. + num_fat, = struct.unpack_from('>I', data, 4) + replaced = False + ptr = 8 + for i in range(num_fat): + cputype, cpusubtype, offset, size, align = \ + struct.unpack_from('>IIIII', data, ptr) + ptr += 20 + + # Does this match the requested bitness? + if bitness is not None and ((cputype & 0x1000000) != 0) != (bitness == 64): + continue + + macho_data = data[offset:offset+size] + off = self._find_symbol_macho(macho_data, symbol_name) + if off is not None: + off += offset + data[off:off+len(replacement)] = replacement + replaced = True + + return replaced + + elif data[:4] in (b'\xCA\xFE\xBA\xBF', b'\xBF\xBA\xFE\xCA'): + # Universal binary with 64-bit offsets. + num_fat, = struct.unpack_from('>I', data, 4) + replaced = False + ptr = 8 + for i in range(num_fat): + cputype, cpusubtype, offset, size, align = \ + struct.unpack_from('>QQQQQ', data, ptr) + ptr += 40 + + # Does this match the requested bitness? + if bitness is not None and ((cputype & 0x1000000) != 0) != (bitness == 64): + continue + + macho_data = data[offset:offset+size] + off = self._find_symbol_macho(macho_data, symbol_name) + if off is not None: + off += offset + data[off:off+len(replacement)] = replacement + replaced = True + + return replaced + + # We don't know what kind of file this is. + return False + + def _replace_symbol_elf(self, elf_data, symbol_name, replacement): + """ The Linux/FreeBSD implementation of _replace_symbol. """ + + replaced = False + + # Make sure we read in the correct endianness and integer size + endian = "<>"[ord(elf_data[5:6]) - 1] + is_64bit = ord(elf_data[4:5]) - 1 # 0 = 32-bits, 1 = 64-bits + header_struct = endian + ("HHIIIIIHHHHHH", "HHIQQQIHHHHHH")[is_64bit] + section_struct = endian + ("4xI4xIIII8xI", "4xI8xQQQI12xQ")[is_64bit] + symbol_struct = endian + ("IIIBBH", "IBBHQQ")[is_64bit] + + header_size = struct.calcsize(header_struct) + type, machine, version, entry, phoff, shoff, flags, ehsize, phentsize, phnum, shentsize, shnum, shstrndx \ + = struct.unpack_from(header_struct, elf_data, 16) + section_offsets = [] + symbol_tables = [] + string_tables = {} + + # Seek to the section header table and find the symbol tables. + ptr = shoff + for i in range(shnum): + type, addr, offset, size, link, entsize = struct.unpack_from(section_struct, elf_data[ptr:ptr+shentsize]) + ptr += shentsize + section_offsets.append(offset - addr) + if type == 0x0B and link != 0: # SHT_DYNSYM, links to string table + symbol_tables.append((offset, size, link, entsize)) + string_tables[link] = None + + # Read the relevant string tables. + for idx in list(string_tables.keys()): + ptr = shoff + idx * shentsize + type, addr, offset, size, link, entsize = struct.unpack_from(section_struct, elf_data[ptr:ptr+shentsize]) + if type == 3: + string_tables[idx] = elf_data[offset:offset+size] + + # Loop through to find the offset of the "blobinfo" symbol. + for offset, size, link, entsize in symbol_tables: + entries = size // entsize + for i in range(entries): + ptr = offset + i * entsize + fields = struct.unpack_from(symbol_struct, elf_data[ptr:ptr+entsize]) + if is_64bit: + name, info, other, shndx, value, size = fields + else: + name, value, size, info, other, shndx = fields + + if not name: + continue + + name = string_tables[link][name : string_tables[link].find(b'\0', name)] + if name == symbol_name: + if shndx == 0: # SHN_UNDEF + continue + elif shndx >= 0xff00 and shndx <= 0xffff: + assert False + else: + # Got it. Make the replacement. + off = section_offsets[shndx] + value + elf_data[off:off+len(replacement)] = replacement + replaced = True + + return replaced + + def _find_symbol_macho(self, macho_data, symbol_name): + """ Returns the offset of the given symbol in the binary file. """ + + if macho_data[:4] in (b'\xCE\xFA\xED\xFE', b'\xCF\xFA\xED\xFE'): + endian = '<' + else: + endian = '>' + + cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags = \ + struct.unpack_from(endian + 'IIIIII', macho_data, 4) + + is_64bit = (cputype & 0x1000000) != 0 + segments = [] + + cmd_ptr = 28 + nlist_struct = endian + 'IBBHI' + if is_64bit: + nlist_struct = endian + 'IBBHQ' + cmd_ptr += 4 + nlist_size = struct.calcsize(nlist_struct) + + for i in range(ncmds): + cmd, cmd_size = struct.unpack_from(endian + 'II', macho_data, cmd_ptr) + cmd_data = macho_data[cmd_ptr+8:cmd_ptr+cmd_size] + cmd_ptr += cmd_size + + cmd &= ~0x80000000 + + if cmd == 0x01: # LC_SEGMENT + segname, vmaddr, vmsize, fileoff, filesize, maxprot, initprot, nsects, flags = \ + struct.unpack_from(endian + '16sIIIIIIII', cmd_data) + segments.append((vmaddr, vmsize, fileoff)) + + elif cmd == 0x19: # LC_SEGMENT_64 + segname, vmaddr, vmsize, fileoff, filesize, maxprot, initprot, nsects, flags = \ + struct.unpack_from(endian + '16sQQQQIIII', cmd_data) + segments.append((vmaddr, vmsize, fileoff)) + + elif cmd == 0x2: # LC_SYMTAB + symoff, nsyms, stroff, strsize = \ + struct.unpack_from(endian + 'IIII', cmd_data) + + strings = macho_data[stroff:stroff+strsize] + + for i in range(nsyms): + strx, type, sect, desc, value = struct.unpack_from(nlist_struct, macho_data, symoff) + symoff += nlist_size + name = strings[strx : strings.find(b'\0', strx)] + + if name == b'_' + symbol_name: + # Find out in which segment this is. + for vmaddr, vmsize, fileoff in segments: + # Is it defined in this segment? + rel = value - vmaddr + if rel >= 0 and rel < vmsize: + # Yes, so return the symbol offset. + return fileoff + rel + print("Could not find memory address for symbol %s" % (symbol_name)) + def makeModuleDef(self, mangledName, code): result = '' result += 'static unsigned char %s[] = {' % (mangledName) @@ -1588,51 +2215,201 @@ class Freezer: return True class PandaModuleFinder(modulefinder.ModuleFinder): - """ We subclass ModuleFinder here, to add functionality for - finding the libpandaexpress etc. modules that interrogate - produces. """ def __init__(self, *args, **kw): + """ + :param path: search path to look on, defaults to sys.path + :param suffixes: defaults to imp.get_suffixes() + :param excludes: a list of modules to exclude + :param debug: an integer indicating the level of verbosity + """ + + self.suffixes = kw.pop('suffixes', imp.get_suffixes()) + modulefinder.ModuleFinder.__init__(self, *args, **kw) - def find_module(self, name, path, *args, **kwargs): - if imp.is_frozen(name): - # Don't pick up modules that are frozen into p3dpython. - raise ImportError("'%s' is a frozen module" % (name)) + # Make sure we don't open a .whl/.zip file more than once. + self._zip_files = {} - try: - return modulefinder.ModuleFinder.find_module(self, name, path, *args, **kwargs) - except ImportError: - # It wasn't found through the normal channels. Maybe it's - # one of ours, or maybe it's frozen? - if path: - # Only if we're not looking on a particular path, - # though. - raise + def _open_file(self, path, mode): + """ Opens a module at the given path, which may contain a zip file. + Returns None if the module could not be found. """ - if p3extend_frozen and p3extend_frozen.is_frozen_module(name): - # It's a frozen module. - return (None, name, ('', '', imp.PY_FROZEN)) + if os.path.isfile(path): + if 'b' not in mode: + return io.open(path, mode, encoding='utf8') + else: + return open(path, mode) - message = "DLL loader cannot find %s." % (name) - raise ImportError(message) + # Is there a zip file along the path? + dir, dirname = os.path.split(path) + fn = dirname + while dirname: + if os.path.isfile(dir): + # Okay, this is actually a file. Is it a zip file? + if dir in self._zip_files: + # Yes, and we've previously opened this. + zip = self._zip_files[dir] + elif zipfile.is_zipfile(dir): + zip = zipfile.ZipFile(dir) + self._zip_files[dir] = zip + else: + # It's a different kind of file. Stop looking. + return None + + try: + fp = zip.open(fn.replace(os.path.sep, '/'), 'r') + except KeyError: + return None + + if sys.version_info >= (3, 0) and 'b' not in mode: + return io.TextIOWrapper(fp, encoding='utf8') + return fp + + # Look at the parent directory. + dir, dirname = os.path.split(dir) + fn = os.path.join(dirname, fn) + + return None def load_module(self, fqname, fp, pathname, file_info): - suffix, mode, type = file_info + """Copied from ModuleFinder.load_module with fixes to handle sending bytes + to compile() for PY_SOURCE types. Sending bytes to compile allows it to + handle file encodings.""" - if type == imp.PY_FROZEN: - # It's a frozen module. - co, isPackage = p3extend_frozen.get_frozen_module_code(pathname) - m = self.add_module(fqname) - m.__file__ = '' - if isPackage: - m.__path__ = [pathname] - co = marshal.loads(co) + suffix, mode, type = file_info + self.msgin(2, "load_module", fqname, fp and "fp", pathname) + if type == imp.PKG_DIRECTORY: + m = self.load_package(fqname, pathname) + self.msgout(2, "load_module ->", m) + return m + + if type == imp.PY_SOURCE: + if fqname in overrideModules: + # This module has a custom override. + code = overrideModules[fqname] + else: + code = fp.read() + + code += b'\n' if isinstance(code, bytes) else '\n' + co = compile(code, pathname, 'exec') + elif type == imp.PY_COMPILED: + try: + marshal_data = importlib._bootstrap_external._validate_bytecode_header(fp.read()) + except ImportError as exc: + self.msgout(2, "raise ImportError: " + str(exc), pathname) + raise + co = marshal.loads(marshal_data) + else: + co = None + + m = self.add_module(fqname) + m.__file__ = pathname + if co: if self.replace_paths: co = self.replace_paths_in_code(co) m.__code__ = co self.scan_code(co, m) - self.msgout(2, "load_module ->", m) - return m + self.msgout(2, "load_module ->", m) + return m - return modulefinder.ModuleFinder.load_module(self, fqname, fp, pathname, (suffix, mode, type)) + # This function is provided here since the Python library version has a bug + # (see bpo-35376) + def _safe_import_hook(self, name, caller, fromlist, level=-1): + # wrapper for self.import_hook() that won't raise ImportError + if name in self.badmodules: + self._add_badmodule(name, caller) + return + try: + self.import_hook(name, caller, level=level) + except ImportError as msg: + self.msg(2, "ImportError:", str(msg)) + self._add_badmodule(name, caller) + else: + if fromlist: + for sub in fromlist: + fullname = name + "." + sub + if fullname in self.badmodules: + self._add_badmodule(fullname, caller) + continue + try: + self.import_hook(name, caller, [sub], level=level) + except ImportError as msg: + self.msg(2, "ImportError:", str(msg)) + self._add_badmodule(fullname, caller) + + def find_module(self, name, path=None, parent=None): + """ Finds a module with the indicated name on the given search path + (or self.path if None). Returns a tuple like (fp, path, stuff), where + stuff is a tuple like (suffix, mode, type). """ + + if imp.is_frozen(name): + # Don't pick up modules that are frozen into p3dpython. + raise ImportError("'%s' is a frozen module" % (name)) + + if parent is not None: + fullname = parent.__name__+'.'+name + else: + fullname = name + if fullname in self.excludes: + raise ImportError(name) + + # If we have a custom override for this module, we know we have it. + if fullname in overrideModules: + return (None, '', ('.py', 'r', imp.PY_SOURCE)) + + # If no search path is given, look for a built-in module. + if path is None: + if name in sys.builtin_module_names: + return (None, None, ('', '', imp.C_BUILTIN)) + + path = self.path + + # Look for the module on the search path. + for dir_path in path: + basename = os.path.join(dir_path, name.split('.')[-1]) + + # Look for recognized extensions. + for stuff in self.suffixes: + suffix, mode, _ = stuff + fp = self._open_file(basename + suffix, mode) + if fp: + return (fp, basename + suffix, stuff) + + # Consider a package, i.e. a directory containing __init__.py. + for suffix, mode, _ in self.suffixes: + init = os.path.join(basename, '__init__' + suffix) + if self._open_file(init, mode): + return (None, basename, ('', '', imp.PKG_DIRECTORY)) + + # It wasn't found through the normal channels. Maybe it's one of + # ours, or maybe it's frozen? + if not path: + # Only if we're not looking on a particular path, though. + if p3extend_frozen and p3extend_frozen.is_frozen_module(name): + # It's a frozen module. + return (None, name, ('', '', imp.PY_FROZEN)) + + raise ImportError(name) + + def find_all_submodules(self, m): + # Overridden so that we can define our own suffixes. + if not m.__path__: + return + modules = {} + for dir in m.__path__: + try: + names = os.listdir(dir) + except OSError: + self.msg(2, "can't list directory", dir) + continue + for name in names: + mod = None + for suff in self.suffixes: + n = len(suff) + if name[-n:] == suff: + mod = name[:-n] + break + if mod and mod != "__init__": + modules[mod] = mod + return modules.keys() diff --git a/direct/src/dist/__init__.py b/direct/src/dist/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/direct/src/dist/commands.py b/direct/src/dist/commands.py new file mode 100644 index 0000000000..2254515b2f --- /dev/null +++ b/direct/src/dist/commands.py @@ -0,0 +1,1388 @@ +from __future__ import print_function + +import collections +import os +import pip +import plistlib +import sys +import subprocess +import zipfile +import re +import shutil +import stat +import struct +import imp +import string + +import setuptools +import distutils.log + +from . import FreezeTool +from . import pefile +import panda3d.core as p3d + + +if 'basestring' not in globals(): + basestring = str + + +if sys.version_info < (3, 0): + # Python 3 defines these subtypes of IOError, but Python 2 doesn't. + FileNotFoundError = IOError + + +def _parse_list(input): + if isinstance(input, basestring): + input = input.strip().replace(',', '\n') + if input: + return [item.strip() for item in input.split('\n') if item.strip()] + else: + return [] + else: + return input + + +def _parse_dict(input): + if isinstance(input, dict): + return input + d = {} + for item in _parse_list(input): + key, sep, value = item.partition('=') + d[key.strip()] = value.strip() + return d + + + +def egg2bam(_build_cmd, srcpath, dstpath): + dstpath = dstpath + '.bam' + try: + subprocess.check_call([ + 'egg2bam', + '-o', dstpath, + '-pd', os.path.dirname(os.path.abspath(srcpath)), + '-ps', 'rel', + srcpath + ]) + except FileNotFoundError: + raise RuntimeError('egg2bam failed: egg2bam was not found in the PATH') + except (subprocess.CalledProcessError, OSError) as err: + raise RuntimeError('egg2bam failed: {}'.format(err)) + return dstpath + +macosx_binary_magics = ( + b'\xFE\xED\xFA\xCE', b'\xCE\xFA\xED\xFE', + b'\xFE\xED\xFA\xCF', b'\xCF\xFA\xED\xFE', + b'\xCA\xFE\xBA\xBE', b'\xBE\xBA\xFE\xCA', + b'\xCA\xFE\xBA\xBF', b'\xBF\xBA\xFE\xCA') + +# Some dependencies need data directories to be extracted. This dictionary maps +# modules with data to extract. The values are lists of tuples of the form +# (source_pattern, destination_pattern, flags). The flags is a set of strings. + +PACKAGE_DATA_DIRS = { + 'matplotlib': [('matplotlib/mpl-data/*', 'mpl-data', {})], + 'jsonschema': [('jsonschema/schemas/*', 'schemas', {})], + 'cefpython3': [ + ('cefpython3/*.pak', '', {}), + ('cefpython3/*.dat', '', {}), + ('cefpython3/*.bin', '', {}), + ('cefpython3/*.dll', '', {}), + ('cefpython3/libcef.so', '', {}), + ('cefpython3/LICENSE.txt', '', {}), + ('cefpython3/License', '', {}), + ('cefpython3/subprocess*', '', {'PKG_DATA_MAKE_EXECUTABLE'}), + ('cefpython3/locals/*', 'locals', {}), + ('cefpython3/Chromium Embedded Framework.framework/Resources', 'Chromium Embedded Framework.framework/Resources', {}), + ], +} + +# Some dependencies have extra directories that need to be scanned for DLLs. +# This dictionary maps wheel basenames (ie. the part of the .whl basename +# before the first hyphen) to a list of directories inside the .whl. + +PACKAGE_LIB_DIRS = { + 'scipy': ['scipy/extra-dll'], +} + +# site.py for Python 2. +SITE_PY2 = u""" +import sys + +sys.frozen = True + +# Override __import__ to set __file__ for frozen modules. +prev_import = __import__ +def __import__(*args, **kwargs): + mod = prev_import(*args, **kwargs) + if mod: + mod.__file__ = sys.executable + return mod + +# Add our custom __import__ version to the global scope, as well as a builtin +# definition for __file__ so that it is available in the module itself. +import __builtin__ +__builtin__.__import__ = __import__ +__builtin__.__file__ = sys.executable +del __builtin__ + +# Set the TCL_LIBRARY directory to the location of the Tcl/Tk/Tix files. +import os +tcl_dir = os.path.join(os.path.dirname(sys.executable), 'tcl') +if os.path.isdir(tcl_dir): + for dir in os.listdir(tcl_dir): + sub_dir = os.path.join(tcl_dir, dir) + if os.path.isdir(sub_dir): + if dir.startswith('tcl'): + os.environ['TCL_LIBRARY'] = sub_dir + if dir.startswith('tk'): + os.environ['TK_LIBRARY'] = sub_dir + if dir.startswith('tix'): + os.environ['TIX_LIBRARY'] = sub_dir +del os +""" + +# site.py for Python 3. +SITE_PY3 = u""" +import sys +from _frozen_importlib import _imp, FrozenImporter + +sys.frozen = True + +if sys.platform == 'win32': + # Make sure the preferred encoding is something we actually support. + import _bootlocale + enc = _bootlocale.getpreferredencoding().lower() + if enc != 'utf-8' and not _imp.is_frozen('encodings.%s' % (enc)): + def getpreferredencoding(do_setlocale=True): + return 'mbcs' + _bootlocale.getpreferredencoding = getpreferredencoding + +# Alter FrozenImporter to give a __file__ property to frozen modules. +_find_spec = FrozenImporter.find_spec + +def find_spec(fullname, path=None, target=None): + spec = _find_spec(fullname, path=path, target=target) + if spec: + spec.has_location = True + spec.origin = sys.executable + return spec + +def get_data(path): + with open(path, 'rb') as fp: + return fp.read() + +FrozenImporter.find_spec = find_spec +FrozenImporter.get_data = get_data + +# Set the TCL_LIBRARY directory to the location of the Tcl/Tk/Tix files. +import os +tcl_dir = os.path.join(os.path.dirname(sys.executable), 'tcl') +if os.path.isdir(tcl_dir): + for dir in os.listdir(tcl_dir): + sub_dir = os.path.join(tcl_dir, dir) + if os.path.isdir(sub_dir): + if dir.startswith('tcl'): + os.environ['TCL_LIBRARY'] = sub_dir + if dir.startswith('tk'): + os.environ['TK_LIBRARY'] = sub_dir + if dir.startswith('tix'): + os.environ['TIX_LIBRARY'] = sub_dir +del os +""" + +SITE_PY = SITE_PY3 if sys.version_info >= (3,) else SITE_PY2 + + +class build_apps(setuptools.Command): + description = 'build Panda3D applications' + user_options = [ + ('build-base=', None, 'directory to build applications in'), + ('requirements-path=', None, 'path to requirements.txt file for pip'), + ('platforms=', 'p', 'a list of platforms to build for'), + ] + default_file_handlers = { + '.egg': egg2bam, + } + + def initialize_options(self): + self.build_base = os.path.join(os.getcwd(), 'build') + self.gui_apps = {} + self.console_apps = {} + self.macos_main_app = None + self.rename_paths = {} + self.include_patterns = [] + self.exclude_patterns = [] + self.include_modules = {} + self.exclude_modules = {} + self.platforms = [ + 'manylinux1_x86_64', + 'macosx_10_6_x86_64', + 'win_amd64', + ] + self.plugins = [] + self.embed_prc_data = True + self.extra_prc_files = [] + self.extra_prc_data = '' + self.default_prc_dir = None + self.log_filename = None + self.log_append = False + self.requirements_path = os.path.join(os.getcwd(), 'requirements.txt') + self.use_optimized_wheels = True + self.optimized_wheel_index = '' + self.pypi_extra_indexes = [] + self.file_handlers = {} + self.exclude_dependencies = [ + # Windows + 'kernel32.dll', 'user32.dll', 'wsock32.dll', 'ws2_32.dll', + 'advapi32.dll', 'opengl32.dll', 'glu32.dll', 'gdi32.dll', + 'shell32.dll', 'ntdll.dll', 'ws2help.dll', 'rpcrt4.dll', + 'imm32.dll', 'ddraw.dll', 'shlwapi.dll', 'secur32.dll', + 'dciman32.dll', 'comdlg32.dll', 'comctl32.dll', 'ole32.dll', + 'oleaut32.dll', 'gdiplus.dll', 'winmm.dll', 'iphlpapi.dll', + 'msvcrt.dll', 'kernelbase.dll', 'msimg32.dll', 'msacm32.dll', + + # manylinux1/linux + 'libdl.so.*', 'libstdc++.so.*', 'libm.so.*', 'libgcc_s.so.*', + 'libpthread.so.*', 'libc.so.*', 'ld-linux-x86-64.so.*', + 'libgl.so.*', 'libx11.so.*', 'libreadline.so.*', 'libncursesw.so.*', + 'libbz2.so.*', 'libz.so.*', 'liblzma.so.*', 'librt.so.*', 'libutil.so.*', + + # macOS + '/usr/lib/libstdc++.*.dylib', + '/usr/lib/libz.*.dylib', + '/usr/lib/libobjc.*.dylib', + '/usr/lib/libSystem.*.dylib', + '/usr/lib/libbz2.*.dylib', + '/usr/lib/libedit.*.dylib', + '/System/Library/**', + ] + self.package_data_dirs = {} + + # We keep track of the zip files we've opened. + self._zip_files = {} + + def _get_zip_file(self, path): + if path in self._zip_files: + return self._zip_files[path] + + zip = zipfile.ZipFile(path) + self._zip_files[path] = zip + return zip + + def finalize_options(self): + # We need to massage the inputs a bit in case they came from a + # setup.cfg file. + self.gui_apps = _parse_dict(self.gui_apps) + self.console_apps = _parse_dict(self.console_apps) + + self.rename_paths = _parse_dict(self.rename_paths) + self.include_patterns = _parse_list(self.include_patterns) + self.exclude_patterns = _parse_list(self.exclude_patterns) + self.include_modules = { + key: _parse_list(value) + for key, value in _parse_dict(self.include_modules).items() + } + self.exclude_modules = { + key: _parse_list(value) + for key, value in _parse_dict(self.exclude_modules).items() + } + self.platforms = _parse_list(self.platforms) + self.plugins = _parse_list(self.plugins) + self.extra_prc_files = _parse_list(self.extra_prc_files) + + if self.default_prc_dir is None: + self.default_prc_dir = 'etc' if not self.embed_prc_data else '' + + num_gui_apps = len(self.gui_apps) + num_console_apps = len(self.console_apps) + + if not self.macos_main_app: + if num_gui_apps > 1: + assert False, 'macos_main_app must be defined if more than one gui_app is defined' + elif num_gui_apps == 1: + self.macos_main_app = list(self.gui_apps.keys())[0] + + use_pipenv = ( + 'Pipfile' in os.path.basename(self.requirements_path) or + not os.path.exists(self.requirements_path) and os.path.exists('Pipfile') + ) + if use_pipenv: + reqspath = os.path.join(self.build_base, 'requirements.txt') + with open(reqspath, 'w') as reqsfile: + subprocess.check_call(['pipenv', 'lock', '--requirements'], stdout=reqsfile) + self.requirements_path = reqspath + + if self.use_optimized_wheels: + if not self.optimized_wheel_index: + # Try to find an appropriate wheel index + + # Start with the release index + self.optimized_wheel_index = 'https://archive.panda3d.org/simple/opt' + + # See if a buildbot build is being used + with open(self.requirements_path) as reqsfile: + reqsdata = reqsfile.read() + matches = re.search(r'--extra-index-url (https*://archive.panda3d.org/.*\b)', reqsdata) + if matches and matches.group(1): + self.optimized_wheel_index = matches.group(1) + if not matches.group(1).endswith('opt'): + self.optimized_wheel_index += '/opt' + + assert self.optimized_wheel_index, 'An index for optimized wheels must be defined if use_optimized_wheels is set' + + assert os.path.exists(self.requirements_path), 'Requirements.txt path does not exist: {}'.format(self.requirements_path) + assert num_gui_apps + num_console_apps != 0, 'Must specify at least one app in either gui_apps or console_apps' + + self.exclude_dependencies = [p3d.GlobPattern(i) for i in self.exclude_dependencies] + for glob in self.exclude_dependencies: + glob.case_sensitive = False + + tmp = self.default_file_handlers.copy() + tmp.update(self.file_handlers) + self.file_handlers = tmp + + tmp = self.package_data_dirs.copy() + tmp.update(self.package_data_dirs) + self.package_data_dirs = tmp + + def run(self): + self.announce('Building platforms: {0}'.format(','.join(self.platforms)), distutils.log.INFO) + + for platform in self.platforms: + self.build_runtimes(platform, True) + + def download_wheels(self, platform): + """ Downloads wheels for the given platform using pip. This includes panda3d + wheels. These are special wheels that are expected to contain a deploy_libs + directory containing the Python runtime libraries, which will be added + to sys.path.""" + + self.announce('Gathering wheels for platform: {}'.format(platform), distutils.log.INFO) + + whldir = os.path.join(self.build_base, '__whl_cache__') + + #TODO find a better way to get abi tag than from internal/private pip APIs + if hasattr(pip, 'pep425tags'): + pep425tags = pip.pep425tags + wheel = pip.wheel + else: + from pip._internal import pep425tags, wheel + + abi_tag = pep425tags.get_abi_tag() + + if 'u' in abi_tag and (platform.startswith('win') or platform.startswith('macosx')): + abi_tag = abi_tag.replace('u', '') + + # For these distributions, we need to append 'u' on Linux + if abi_tag in ('cp26m', 'cp27m', 'cp32m') and not platform.startswith('win') and not platform.startswith('macosx'): + abi_tag += 'u' + + pip_version = pip.__version__.split('.') + if int(pip_version[0]) < 9: + raise RuntimeError("pip 9.0 or greater is required, but found {}".format(pip.__version__)) + + # Remove any .zip files. These are built from a VCS and block for an + # interactive prompt on subsequent downloads. + if os.path.exists(whldir): + for whl in os.listdir(whldir): + if whl.endswith('.zip'): + os.remove(os.path.join(whldir, whl)) + + pip_args = [ + 'download', + '-d', whldir, + '-r', self.requirements_path, + '--only-binary', ':all:', + '--platform', platform, + '--abi', abi_tag + ] + + if self.use_optimized_wheels: + pip_args += [ + '--extra-index-url', self.optimized_wheel_index + ] + + for index in self.pypi_extra_indexes: + pip_args += ['--extra-index-url', index] + + subprocess.check_call([sys.executable, '-m', 'pip'] + pip_args) + + # Now figure out which of the downloaded wheels are relevant to us. + tags = pep425tags.get_supported(platform=platform, abi=abi_tag) + wheelpaths = [] + for filename in os.listdir(whldir): + try: + whl = wheel.Wheel(filename) + except wheel.InvalidWheelFilename: + continue + + if whl.supported(tags): + wheelpaths.append(os.path.join(whldir, filename)) + + return wheelpaths + + def bundle_macos_app(self, builddir): + """Bundle built runtime into a .app for macOS""" + + appname = '{}.app'.format(self.macos_main_app) + appdir = os.path.join(builddir, appname) + contentsdir = os.path.join(appdir, 'Contents') + macosdir = os.path.join(contentsdir, 'MacOS') + fwdir = os.path.join(contentsdir, 'Frameworks') + resdir = os.path.join(contentsdir, 'Resources') + + self.announce('Bundling macOS app into {}'.format(appdir), distutils.log.INFO) + + # Create initial directory structure + os.makedirs(macosdir) + os.makedirs(fwdir) + os.makedirs(resdir) + + # Move files over + for fname in os.listdir(builddir): + src = os.path.join(builddir, fname) + if appdir in src: + continue + + if fname in self.gui_apps or self.console_apps: + dst = macosdir + elif os.path.isfile(src) and open(src, 'rb').read(4) in macosx_binary_magics: + dst = fwdir + else: + dst = resdir + shutil.move(src, dst) + + # Write out Info.plist + plist = { + 'CFBundleName': appname, + 'CFBundleDisplayName': appname, #TODO use name from setup.py/cfg + 'CFBundleIdentifier': '', #TODO + 'CFBundleVersion': '0.0.0', #TODO get from setup.py + 'CFBundlePackageType': 'APPL', + 'CFBundleSignature': '', #TODO + 'CFBundleExecutable': self.macos_main_app, + } + with open(os.path.join(contentsdir, 'Info.plist'), 'wb') as f: + if hasattr(plistlib, 'dump'): + plistlib.dump(plist, f) + else: + plistlib.writePlist(plist, f) + + + def build_runtimes(self, platform, use_wheels): + """ Builds the distributions for the given platform. """ + + builddir = os.path.join(self.build_base, platform) + + if os.path.exists(builddir): + shutil.rmtree(builddir) + os.makedirs(builddir) + + path = sys.path[:] + p3dwhl = None + + if use_wheels: + wheelpaths = self.download_wheels(platform) + + for whl in wheelpaths: + if os.path.basename(whl).startswith('panda3d-'): + p3dwhlfn = whl + p3dwhl = self._get_zip_file(p3dwhlfn) + break + else: + raise RuntimeError("Missing panda3d wheel for platform: {}".format(platform)) + + if self.use_optimized_wheels: + # Check to see if we have an optimized wheel + localtag = p3dwhlfn.split('+')[1].split('-')[0] if '+' in p3dwhlfn else '' + if not localtag.endswith('opt'): + self.announce( + 'Could not find an optimized wheel (using index {}) for platform: {}'.format(self.optimized_wheel_index, platform), + distutils.log.WARN + ) + + #whlfiles = {whl: self._get_zip_file(whl) for whl in wheelpaths} + + # Add whl files to the path so they are picked up by modulefinder + for whl in wheelpaths: + path.insert(0, whl) + + # Add deploy_libs from panda3d whl to the path + path.insert(0, os.path.join(p3dwhlfn, 'deploy_libs')) + + + self.announce('Building runtime for platform: {}'.format(platform), distutils.log.INFO) + + # Gather PRC data + prcstring = '' + if not use_wheels: + dtool_fn = p3d.Filename(p3d.ExecutionEnvironment.get_dtool_name()) + libdir = os.path.dirname(dtool_fn.to_os_specific()) + etcdir = os.path.join(libdir, '..', 'etc') + + for fn in os.listdir(etcdir): + if fn.lower().endswith('.prc'): + with open(os.path.join(etcdir, fn)) as f: + prcstring += f.read() + else: + etcfiles = [i for i in p3dwhl.namelist() if i.endswith('.prc')] + for fn in etcfiles: + with p3dwhl.open(fn) as f: + prcstring += f.read().decode('utf8') + user_prcstring = self.extra_prc_data + for fn in self.extra_prc_files: + with open(fn) as f: + user_prcstring += f.read() + + # Clenup PRC data + check_plugins = [ + #TODO find a better way to get this list + 'pandaegg', + 'p3ffmpeg', + 'p3ptloader', + 'p3assimp', + ] + def parse_prc(prcstr, warn_on_missing_plugin): + out = [] + for ln in prcstr.split('\n'): + ln = ln.strip() + useline = True + if ln.startswith('#') or not ln: + continue + if 'model-cache-dir' in ln: + ln = ln.replace('/panda3d', '/{}'.format(self.distribution.get_name())) + for plugin in check_plugins: + if plugin in ln and plugin not in self.plugins: + useline = False + if warn_on_missing_plugin: + self.warn( + "Missing plugin ({0}) referenced in user PRC data".format(plugin) + ) + break + if useline: + out.append(ln) + return out + prcexport = parse_prc(prcstring, 0) + parse_prc(user_prcstring, 1) + + # Export PRC data + prcexport = '\n'.join(prcexport) + if not self.embed_prc_data: + prcdir = self.default_prc_dir.replace('', '') + prcdir = os.path.join(builddir, prcdir) + os.makedirs(prcdir) + with open (os.path.join(prcdir, '00-panda3d.prc'), 'w') as f: + f.write(prcexport) + + # Create runtimes + freezer_extras = set() + freezer_modules = set() + freezer_modpaths = set() + ext_suffixes = set() + def create_runtime(appname, mainscript, use_console): + freezer = FreezeTool.Freezer(platform=platform, path=path) + freezer.addModule('__main__', filename=mainscript) + freezer.addModule('site', filename='site.py', text=SITE_PY) + for incmod in self.include_modules.get(appname, []) + self.include_modules.get('*', []): + freezer.addModule(incmod) + for exmod in self.exclude_modules.get(appname, []) + self.exclude_modules.get('*', []): + freezer.excludeModule(exmod) + freezer.done(addStartupModules=True) + + target_path = os.path.join(builddir, appname) + + stub_name = 'deploy-stub' + if platform.startswith('win') or 'macosx' in platform: + if not use_console: + stub_name = 'deploy-stubw' + + if platform.startswith('win'): + stub_name += '.exe' + target_path += '.exe' + + if use_wheels: + stub_file = p3dwhl.open('panda3d_tools/{0}'.format(stub_name)) + else: + dtool_path = p3d.Filename(p3d.ExecutionEnvironment.get_dtool_name()).to_os_specific() + stub_path = os.path.join(os.path.dirname(dtool_path), '..', 'bin', stub_name) + stub_file = open(stub_path, 'rb') + + freezer.generateRuntimeFromStub(target_path, stub_file, use_console, { + 'prc_data': prcexport if self.embed_prc_data else None, + 'default_prc_dir': self.default_prc_dir, + 'prc_dir_envvars': None, + 'prc_path_envvars': None, + 'prc_patterns': None, + 'prc_encrypted_patterns': None, + 'prc_encryption_key': None, + 'prc_executable_patterns': None, + 'prc_executable_args_envvar': None, + 'main_dir': None, + 'log_filename': self.expand_path(self.log_filename, platform), + }, self.log_append) + stub_file.close() + + # Copy the dependencies. + search_path = [builddir] + if use_wheels: + search_path.append(os.path.join(p3dwhlfn, 'deploy_libs')) + self.copy_dependencies(target_path, builddir, search_path, stub_name) + + freezer_extras.update(freezer.extras) + freezer_modules.update(freezer.getAllModuleNames()) + freezer_modpaths.update({ + mod[1].filename.to_os_specific() + for mod in freezer.getModuleDefs() if mod[1].filename + }) + for suffix in freezer.moduleSuffixes: + if suffix[2] == imp.C_EXTENSION: + ext_suffixes.add(suffix[0]) + + for appname, scriptname in self.gui_apps.items(): + create_runtime(appname, scriptname, False) + + for appname, scriptname in self.console_apps.items(): + create_runtime(appname, scriptname, True) + + # Copy extension modules + whl_modules = [] + whl_modules_ext = '' + if use_wheels: + # Get the module libs + whl_modules = [] + + for i in p3dwhl.namelist(): + if not i.startswith('deploy_libs/'): + continue + + if not any(i.endswith(suffix) for suffix in ext_suffixes): + continue + + base = os.path.basename(i) + module, _, ext = base.partition('.') + whl_modules.append(module) + whl_modules_ext = ext + + # Make sure to copy any builtins that have shared objects in the + # deploy libs, assuming they are not already in freezer_extras. + for mod, source_path in freezer_extras: + freezer_modules.discard(mod) + + for mod in freezer_modules: + if mod in whl_modules: + freezer_extras.add((mod, None)) + + # Copy over necessary plugins + plugin_list = ['panda3d/lib{}'.format(i) for i in self.plugins] + for lib in p3dwhl.namelist(): + plugname = lib.split('.', 1)[0] + if plugname in plugin_list: + source_path = os.path.join(p3dwhlfn, lib) + target_path = os.path.join(builddir, os.path.basename(lib)) + search_path = [os.path.dirname(source_path)] + self.copy_with_dependencies(source_path, target_path, search_path) + + # Copy any shared objects we need + for module, source_path in freezer_extras: + if source_path is not None: + # Rename panda3d/core.pyd to panda3d.core.pyd + basename = os.path.basename(source_path) + if '.' in module: + basename = module.rsplit('.', 1)[0] + '.' + basename + + # Remove python version string + if sys.version_info >= (3, 0): + parts = basename.split('.') + if len(parts) >= 3 and '-' in parts[-2]: + parts = parts[:-2] + parts[-1:] + basename = '.'.join(parts) + else: + # Builtin module, but might not be builtin in wheel libs, so double check + if module in whl_modules: + source_path = os.path.join(p3dwhlfn, 'deploy_libs/{}.{}'.format(module, whl_modules_ext))#'{0}/deploy_libs/{1}.{2}'.format(p3dwhlfn, module, whl_modules_ext) + basename = os.path.basename(source_path) + #XXX should we remove python version string here too? + else: + continue + + # If this is a dynamic library, search for dependencies. + search_path = [os.path.dirname(source_path)] + if use_wheels: + search_path.append(os.path.join(p3dwhlfn, 'deploy_libs')) + + # If the .whl containing this file has a .libs directory, add + # it to the path. This is an auditwheel/numpy convention. + if '.whl' + os.sep in source_path: + whl, wf = source_path.split('.whl' + os.path.sep) + whl += '.whl' + rootdir = wf.split(os.path.sep, 1)[0] + search_path.append(os.path.join(whl, rootdir, '.libs')) + + # Also look for more specific per-package cases, defined in + # PACKAGE_LIB_DIRS at the top of this file. + whl_name = os.path.basename(whl).split('-', 1)[0] + extra_dirs = PACKAGE_LIB_DIRS.get(whl_name, []) + for extra_dir in extra_dirs: + search_path.append(os.path.join(whl, extra_dir.replace('/', os.path.sep))) + + target_path = os.path.join(builddir, basename) + self.copy_with_dependencies(source_path, target_path, search_path) + + # Copy over the tcl directory. + #TODO: get this to work on non-Windows platforms. + if sys.platform == "win32" and platform.startswith('win'): + tcl_dir = os.path.join(sys.prefix, 'tcl') + tkinter_name = 'tkinter' if sys.version_info >= (3, 0) else 'Tkinter' + + if os.path.isdir(tcl_dir) and tkinter_name in freezer_modules: + self.announce('Copying Tcl files', distutils.log.INFO) + os.makedirs(os.path.join(builddir, 'tcl')) + + for dir in os.listdir(tcl_dir): + sub_dir = os.path.join(tcl_dir, dir) + if os.path.isdir(sub_dir): + target_dir = os.path.join(builddir, 'tcl', dir) + self.announce('copying {0} -> {1}'.format(sub_dir, target_dir)) + shutil.copytree(sub_dir, target_dir) + + # Extract any other data files from dependency packages. + for module, datadesc in self.package_data_dirs.items(): + if module not in freezer_modules: + continue + + self.announce('Copying data files for module: {}'.format(module), distutils.log.INFO) + + # OK, find out in which .whl this occurs. + for whl in wheelpaths: + whlfile = self._get_zip_file(whl) + filenames = whlfile.namelist() + for source_pattern, target_dir, flags in datadesc: + srcglob = p3d.GlobPattern(source_pattern) + source_dir = os.path.dirname(source_pattern) + # Relocate the target dir to the build directory. + target_dir = target_dir.replace('/', os.sep) + target_dir = os.path.join(builddir, target_dir) + + for wf in filenames: + if wf.lower().startswith(source_dir.lower() + '/'): + if not srcglob.matches(wf.lower()): + continue + wf = wf.replace('/', os.sep) + relpath = wf[len(source_dir) + 1:] + source_path = os.path.join(whl, wf) + target_path = os.path.join(target_dir, relpath) + self.copy(source_path, target_path) + + if 'PKG_DATA_MAKE_EXECUTABLE' in flags: + mode = os.stat(target_path).st_mode + mode |= stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH + os.chmod(target_path, mode) + + # Copy Game Files + self.announce('Copying game files for platform: {}'.format(platform), distutils.log.INFO) + ignore_copy_list = [ + '**/__pycache__/**', + '**/*.pyc', + '{}/**'.format(self.build_base), + ] + ignore_copy_list += self.exclude_patterns + ignore_copy_list += freezer_modpaths + ignore_copy_list += self.extra_prc_files + ignore_copy_list = [p3d.GlobPattern(p3d.Filename.from_os_specific(i).get_fullpath()) for i in ignore_copy_list] + + include_copy_list = [p3d.GlobPattern(i) for i in self.include_patterns] + + def check_pattern(src, pattern_list): + # Normalize file paths across platforms + fn = p3d.Filename.from_os_specific(os.path.normpath(src)) + path = fn.get_fullpath() + fn.make_absolute() + abspath = fn.get_fullpath() + + for pattern in pattern_list: + # If the pattern is absolute, match against the absolute filename. + if pattern.pattern[0] == '/': + #print('check ignore: {} {} {}'.format(pattern, src, pattern.matches_file(abspath))) + if pattern.matches_file(abspath): + return True + else: + #print('check ignore: {} {} {}'.format(pattern, src, pattern.matches_file(path))) + if pattern.matches_file(path): + return True + return False + + def check_file(fname): + return check_pattern(fname, include_copy_list) and \ + not check_pattern(fname, ignore_copy_list) + + def copy_file(src, dst): + src = os.path.normpath(src) + dst = os.path.normpath(dst) + + if not check_file(src): + self.announce('skipping file {}'.format(src)) + return + + dst_dir = os.path.dirname(dst) + if not os.path.exists(dst_dir): + os.makedirs(dst_dir) + + ext = os.path.splitext(src)[1] + if not ext: + ext = os.path.basename(src) + + if ext in self.file_handlers: + buildscript = self.file_handlers[ext] + self.announce('running {} on src ({})'.format(buildscript.__name__, src)) + try: + dst = self.file_handlers[ext](self, src, dst) + except Exception as err: + self.announce('{}'.format(err), distutils.log.ERROR) + else: + self.announce('copying {0} -> {1}'.format(src, dst)) + shutil.copyfile(src, dst) + + def update_path(path): + normpath = p3d.Filename.from_os_specific(os.path.normpath(src)).c_str() + for inputpath, outputpath in self.rename_paths.items(): + if normpath.startswith(inputpath): + normpath = normpath.replace(inputpath, outputpath, 1) + return p3d.Filename(normpath).to_os_specific() + + rootdir = os.getcwd() + for dirname, subdirlist, filelist in os.walk(rootdir): + dirpath = os.path.relpath(dirname, rootdir) + for fname in filelist: + src = os.path.join(dirpath, fname) + dst = os.path.join(builddir, update_path(src)) + + copy_file(src, dst) + + # Bundle into an .app on macOS + if self.macos_main_app and 'macosx' in platform: + self.bundle_macos_app(builddir) + + def add_dependency(self, name, target_dir, search_path, referenced_by): + """ Searches for the given DLL on the search path. If it exists, + copies it to the target_dir. """ + + if os.path.exists(os.path.join(target_dir, name)): + # We've already added it earlier. + return + + for dep in self.exclude_dependencies: + if dep.matches_file(name): + return + + for dir in search_path: + source_path = os.path.join(dir, name) + + if os.path.isfile(source_path): + target_path = os.path.join(target_dir, name) + self.copy_with_dependencies(source_path, target_path, search_path) + return + + elif '.whl' in source_path: + # Check whether the file exists inside the wheel. + whl, wf = source_path.split('.whl' + os.path.sep) + whl += '.whl' + whlfile = self._get_zip_file(whl) + + # Normalize the path separator + wf = os.path.normpath(wf).replace(os.path.sep, '/') + + # Look case-insensitively. + namelist = whlfile.namelist() + namelist_lower = [file.lower() for file in namelist] + + if wf.lower() in namelist_lower: + # We have a match. Change it to the correct case. + wf = namelist[namelist_lower.index(wf.lower())] + source_path = os.path.join(whl, wf) + target_path = os.path.join(target_dir, os.path.basename(wf)) + self.copy_with_dependencies(source_path, target_path, search_path) + return + + # If we didn't find it, look again, but case-insensitively. + name_lower = name.lower() + + for dir in search_path: + if os.path.isdir(dir): + files = os.listdir(dir) + files_lower = [file.lower() for file in files] + + if name_lower in files_lower: + name = files[files_lower.index(name_lower)] + source_path = os.path.join(dir, name) + target_path = os.path.join(target_dir, name) + self.copy_with_dependencies(source_path, target_path, search_path) + + # Warn if we can't find it, but only once. + self.warn("could not find dependency {0} (referenced by {1})".format(name, referenced_by)) + self.exclude_dependencies.append(p3d.GlobPattern(name.lower())) + + def copy(self, source_path, target_path): + """ Copies source_path to target_path. + + source_path may be located inside a .whl file. """ + + try: + self.announce('copying {0} -> {1}'.format(os.path.relpath(source_path, self.build_base), os.path.relpath(target_path, self.build_base))) + except ValueError: + # No relative path (e.g., files on different drives in Windows), just print absolute paths instead + self.announce('copying {0} -> {1}'.format(source_path, target_path)) + + # Make the directory if it does not yet exist. + target_dir = os.path.dirname(target_path) + if not os.path.isdir(target_dir): + os.makedirs(target_dir) + + # Copy the file, and open it for analysis. + if '.whl' in source_path: + # This was found in a wheel, extract it + whl, wf = source_path.split('.whl' + os.path.sep) + whl += '.whl' + whlfile = self._get_zip_file(whl) + data = whlfile.read(wf.replace(os.path.sep, '/')) + with open(target_path, 'wb') as f: + f.write(data) + else: + # Regular file, copy it + shutil.copyfile(source_path, target_path) + + def copy_with_dependencies(self, source_path, target_path, search_path): + """ Copies source_path to target_path. It also scans source_path for + any dependencies, which are located along the given search_path and + copied to the same directory as target_path. + + source_path may be located inside a .whl file. """ + + self.copy(source_path, target_path) + + source_dir = os.path.dirname(source_path) + target_dir = os.path.dirname(target_path) + base = os.path.basename(target_path) + self.copy_dependencies(target_path, target_dir, search_path + [source_dir], base) + + def copy_dependencies(self, target_path, target_dir, search_path, referenced_by): + """ Copies the dependencies of target_path into target_dir. """ + + fp = open(target_path, 'rb+') + + # What kind of magic does the file contain? + deps = [] + magic = fp.read(4) + if magic.startswith(b'MZ'): + # It's a Windows DLL or EXE file. + pe = pefile.PEFile() + pe.read(fp) + for lib in pe.imports: + if not lib.lower().startswith('api-ms-win-'): + deps.append(lib) + + elif magic == b'\x7FELF': + # Elf magic. Used on (among others) Linux and FreeBSD. + deps = self._read_dependencies_elf(fp, target_dir, search_path) + + elif magic in (b'\xCE\xFA\xED\xFE', b'\xCF\xFA\xED\xFE'): + # A Mach-O file, as used on macOS. + deps = self._read_dependencies_macho(fp, '<', flatten=True) + + elif magic in (b'\xFE\xED\xFA\xCE', b'\xFE\xED\xFA\xCF'): + rel_dir = os.path.relpath(target_dir, os.path.dirname(target_path)) + deps = self._read_dependencies_macho(fp, '>', flatten=True) + + elif magic in (b'\xCA\xFE\xBA\xBE', b'\xBE\xBA\xFE\xCA'): + # A fat file, containing multiple Mach-O binaries. In the future, + # we may want to extract the one containing the architecture we + # are building for. + deps = self._read_dependencies_fat(fp, False, flatten=True) + + elif magic in (b'\xCA\xFE\xBA\xBF', b'\xBF\xBA\xFE\xCA'): + # A 64-bit fat file. + deps = self._read_dependencies_fat(fp, True, flatten=True) + + # If we discovered any dependencies, recursively add those. + for dep in deps: + self.add_dependency(dep, target_dir, search_path, referenced_by) + + def _read_dependencies_elf(self, elf, origin, search_path): + """ Having read the first 4 bytes of the ELF file, fetches the + dependent libraries and returns those as a list. """ + + ident = elf.read(12) + + # Make sure we read in the correct endianness and integer size + byte_order = "<>"[ord(ident[1:2]) - 1] + elf_class = ord(ident[0:1]) - 1 # 0 = 32-bits, 1 = 64-bits + header_struct = byte_order + ("HHIIIIIHHHHHH", "HHIQQQIHHHHHH")[elf_class] + section_struct = byte_order + ("4xI8xIII8xI", "4xI16xQQI12xQ")[elf_class] + dynamic_struct = byte_order + ("iI", "qQ")[elf_class] + + type, machine, version, entry, phoff, shoff, flags, ehsize, phentsize, phnum, shentsize, shnum, shstrndx \ + = struct.unpack(header_struct, elf.read(struct.calcsize(header_struct))) + dynamic_sections = [] + string_tables = {} + + # Seek to the section header table and find the .dynamic section. + elf.seek(shoff) + for i in range(shnum): + type, offset, size, link, entsize = struct.unpack_from(section_struct, elf.read(shentsize)) + if type == 6 and link != 0: # DYNAMIC type, links to string table + dynamic_sections.append((offset, size, link, entsize)) + string_tables[link] = None + + # Read the relevant string tables. + for idx in string_tables.keys(): + elf.seek(shoff + idx * shentsize) + type, offset, size, link, entsize = struct.unpack_from(section_struct, elf.read(shentsize)) + if type != 3: continue + elf.seek(offset) + string_tables[idx] = elf.read(size) + + # Loop through the dynamic sections and rewrite it if it has an rpath/runpath. + needed = [] + rpath = [] + for offset, size, link, entsize in dynamic_sections: + elf.seek(offset) + data = elf.read(entsize) + tag, val = struct.unpack_from(dynamic_struct, data) + + # Read tags until we find a NULL tag. + while tag != 0: + if tag == 1: # A NEEDED entry. Read it from the string table. + string = string_tables[link][val : string_tables[link].find(b'\0', val)] + needed.append(string.decode('utf-8')) + + elif tag == 15 or tag == 29: + # An RPATH or RUNPATH entry. + string = string_tables[link][val : string_tables[link].find(b'\0', val)] + rpath += [ + os.path.normpath(i.decode('utf-8').replace('$ORIGIN', origin)) + for i in string.split(b':') + ] + + data = elf.read(entsize) + tag, val = struct.unpack_from(dynamic_struct, data) + elf.close() + + search_path += rpath + return needed + + def _read_dependencies_macho(self, fp, endian, flatten=False): + """ Having read the first 4 bytes of the Mach-O file, fetches the + dependent libraries and returns those as a list. + + If flatten is True, if the dependencies contain paths like + @loader_path/../.dylibs/libsomething.dylib, it will rewrite them to + instead contain @loader_path/libsomething.dylib if possible. + This requires the file pointer to be opened in rb+ mode. """ + + cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags = \ + struct.unpack(endian + 'IIIIII', fp.read(24)) + + is_64bit = (cputype & 0x1000000) != 0 + if is_64bit: + fp.read(4) + + # After the header, we get a series of linker commands. We just + # iterate through them and gather up the LC_LOAD_DYLIB commands. + load_dylibs = [] + for i in range(ncmds): + cmd, cmd_size = struct.unpack(endian + 'II', fp.read(8)) + cmd_data = fp.read(cmd_size - 8) + cmd &= ~0x80000000 + + if cmd == 0x0c: # LC_LOAD_DYLIB + dylib = cmd_data[16:].decode('ascii').split('\x00', 1)[0] + orig = dylib + + if dylib.startswith('@loader_path/../Frameworks/'): + dylib = dylib.replace('@loader_path/../Frameworks/', '') + elif dylib.startswith('@executable_path/../Frameworks/'): + dylib = dylib.replace('@executable_path/../Frameworks/', '') + elif dylib.startswith('@loader_path/'): + dylib = dylib.replace('@loader_path/', '') + + # Do we need to flatten the relative reference? + if '/' in dylib and flatten: + new_dylib = '@loader_path/' + os.path.basename(dylib) + str_size = len(cmd_data) - 16 + if len(new_dylib) < str_size: + fp.seek(-str_size, os.SEEK_CUR) + fp.write(new_dylib.encode('ascii').ljust(str_size, b'\0')) + else: + self.warn('Unable to rewrite dependency {}'.format(orig)) + + load_dylibs.append(dylib) + + return load_dylibs + + def _read_dependencies_fat(self, fp, is_64bit, flatten=False): + num_fat, = struct.unpack('>I', fp.read(4)) + + # After the header we get a table of executables in this fat file, + # each one with a corresponding offset into the file. + offsets = [] + for i in range(num_fat): + if is_64bit: + cputype, cpusubtype, offset, size, align = \ + struct.unpack('>QQQQQ', fp.read(40)) + else: + cputype, cpusubtype, offset, size, align = \ + struct.unpack('>IIIII', fp.read(20)) + offsets.append(offset) + + # Go through each of the binaries in the fat file. + deps = [] + for offset in offsets: + # Add 4, since it expects we've already read the magic. + fp.seek(offset) + magic = fp.read(4) + + if magic in (b'\xCE\xFA\xED\xFE', b'\xCF\xFA\xED\xFE'): + endian = '<' + elif magic in (b'\xFE\xED\xFA\xCE', b'\xFE\xED\xFA\xCF'): + endian = '>' + else: + # Not a Mach-O file we can read. + continue + + for dep in self._read_dependencies_macho(fp, endian, flatten=flatten): + if dep not in deps: + deps.append(dep) + + return deps + + def expand_path(self, path, platform): + "Substitutes variables in the given path string." + + if path is None: + return None + + t = string.Template(path) + if platform.startswith('win'): + return t.substitute(HOME='~', USER_APPDATA='~/AppData/Local') + elif platform.startswith('macosx'): + return t.substitute(HOME='~', USER_APPDATA='~/Documents') + else: + return t.substitute(HOME='~', USER_APPDATA='~/.local/share') + + +class bdist_apps(setuptools.Command): + DEFAULT_INSTALLERS = { + 'manylinux1_x86_64': ['gztar'], + 'manylinux1_i686': ['gztar'], + # Everything else defaults to ['zip'] + } + + description = 'bundle built Panda3D applications into distributable forms' + user_options = build_apps.user_options + [ + ('dist-dir=', 'd', 'directory to put final built distributions in'), + ('skip-build', None, 'skip rebuilding everything (for testing/debugging)'), + ] + + def _build_apps_options(self): + return [opt[0].replace('-', '_').replace('=', '') for opt in build_apps.user_options] + + def initialize_options(self): + self.installers = {} + self.dist_dir = os.path.join(os.getcwd(), 'dist') + self.skip_build = False + for opt in self._build_apps_options(): + setattr(self, opt, None) + + def finalize_options(self): + # We need to massage the inputs a bit in case they came from a + # setup.cfg file. + self.installers = { + key: _parse_list(value) + for key, value in _parse_dict(self.installers).items() + } + + def _get_archive_basedir(self): + return self.distribution.get_name() + + def create_zip(self, basename, build_dir): + import zipfile + + base_dir = self._get_archive_basedir() + + with zipfile.ZipFile(basename+'.zip', 'w', compression=zipfile.ZIP_DEFLATED) as zf: + zf.write(build_dir, base_dir) + + for dirpath, dirnames, filenames in os.walk(build_dir): + for name in sorted(dirnames): + path = os.path.normpath(os.path.join(dirpath, name)) + zf.write(path, path.replace(build_dir, base_dir, 1)) + for name in filenames: + path = os.path.normpath(os.path.join(dirpath, name)) + if os.path.isfile(path): + zf.write(path, path.replace(build_dir, base_dir, 1)) + + def create_tarball(self, basename, build_dir, tar_compression): + import tarfile + + base_dir = self._get_archive_basedir() + build_cmd = self.get_finalized_command('build_apps') + binary_names = list(build_cmd.console_apps.keys()) + list(build_cmd.gui_apps.keys()) + + def tarfilter(tarinfo): + if tarinfo.isdir() or os.path.basename(tarinfo.name) in binary_names: + tarinfo.mode = 0o755 + else: + tarinfo.mode = 0o644 + return tarinfo + + with tarfile.open('{}.tar.{}'.format(basename, tar_compression), 'w|{}'.format(tar_compression)) as tf: + tf.add(build_dir, base_dir, filter=tarfilter) + + def create_nsis(self, basename, build_dir, is_64bit): + # Get a list of build applications + build_cmd = self.get_finalized_command('build_apps') + apps = build_cmd.gui_apps.copy() + apps.update(build_cmd.console_apps) + apps = [ + '{}.exe'.format(i) + for i in apps + ] + + fullname = self.distribution.get_fullname() + shortname = self.distribution.get_name() + + # Create the .nsi installer script + nsifile = p3d.Filename(build_cmd.build_base, shortname + ".nsi") + nsifile.unlink() + nsi = open(nsifile.to_os_specific(), "w") + + # Some global info + nsi.write('Name "%s"\n' % shortname) + nsi.write('OutFile "%s"\n' % (fullname+'.exe')) + if is_64bit: + nsi.write('InstallDir "$PROGRAMFILES64\\%s"\n' % shortname) + else: + nsi.write('InstallDir "$PROGRAMFILES\\%s"\n' % shortname) + nsi.write('SetCompress auto\n') + nsi.write('SetCompressor lzma\n') + nsi.write('ShowInstDetails nevershow\n') + nsi.write('ShowUninstDetails nevershow\n') + nsi.write('InstType "Typical"\n') + + # Tell Vista that we require admin rights + nsi.write('RequestExecutionLevel admin\n') + nsi.write('\n') + + # TODO offer run and desktop shortcut after we figure out how to deal + # with multiple apps + + nsi.write('!include "MUI2.nsh"\n') + nsi.write('!define MUI_ABORTWARNING\n') + nsi.write('\n') + nsi.write('Var StartMenuFolder\n') + nsi.write('!insertmacro MUI_PAGE_WELCOME\n') + # TODO license file + nsi.write('!insertmacro MUI_PAGE_DIRECTORY\n') + nsi.write('!insertmacro MUI_PAGE_STARTMENU Application $StartMenuFolder\n') + nsi.write('!insertmacro MUI_PAGE_INSTFILES\n') + nsi.write('!insertmacro MUI_PAGE_FINISH\n') + nsi.write('!insertmacro MUI_UNPAGE_WELCOME\n') + nsi.write('!insertmacro MUI_UNPAGE_CONFIRM\n') + nsi.write('!insertmacro MUI_UNPAGE_INSTFILES\n') + nsi.write('!insertmacro MUI_UNPAGE_FINISH\n') + nsi.write('!insertmacro MUI_LANGUAGE "English"\n') + + # This section defines the installer. + nsi.write('Section "" SecCore\n') + nsi.write(' SetOutPath "$INSTDIR"\n') + curdir = "" + for root, dirs, files in os.walk(build_dir): + for name in files: + basefile = p3d.Filename.fromOsSpecific(os.path.join(root, name)) + file = p3d.Filename(basefile) + file.makeAbsolute() + file.makeRelativeTo(build_dir) + outdir = file.getDirname().replace('/', '\\') + if curdir != outdir: + nsi.write(' SetOutPath "$INSTDIR\\%s"\n' % outdir) + curdir = outdir + nsi.write(' File "%s"\n' % (basefile.toOsSpecific())) + nsi.write(' SetOutPath "$INSTDIR"\n') + nsi.write(' WriteUninstaller "$INSTDIR\\Uninstall.exe"\n') + nsi.write(' ; Start menu items\n') + nsi.write(' !insertmacro MUI_STARTMENU_WRITE_BEGIN Application\n') + nsi.write(' CreateDirectory "$SMPROGRAMS\\$StartMenuFolder"\n') + for app in apps: + nsi.write(' CreateShortCut "$SMPROGRAMS\\$StartMenuFolder\\%s.lnk" "$INSTDIR\\%s"\n' % (shortname, app)) + nsi.write(' CreateShortCut "$SMPROGRAMS\\$StartMenuFolder\\Uninstall.lnk" "$INSTDIR\\Uninstall.exe"\n') + nsi.write(' !insertmacro MUI_STARTMENU_WRITE_END\n') + nsi.write('SectionEnd\n') + + # This section defines the uninstaller. + nsi.write('Section Uninstall\n') + nsi.write(' RMDir /r "$INSTDIR"\n') + nsi.write(' ; Desktop icon\n') + nsi.write(' Delete "$DESKTOP\\%s.lnk"\n' % shortname) + nsi.write(' ; Start menu items\n') + nsi.write(' !insertmacro MUI_STARTMENU_GETFOLDER Application $StartMenuFolder\n') + nsi.write(' RMDir /r "$SMPROGRAMS\\$StartMenuFolder"\n') + nsi.write('SectionEnd\n') + nsi.close() + + cmd = ['makensis'] + for flag in ["V2"]: + cmd.append( + '{}{}'.format('/' if sys.platform.startswith('win') else '-', flag) + ) + cmd.append(nsifile.to_os_specific()) + subprocess.check_call(cmd) + + def run(self): + build_cmd = self.distribution.get_command_obj('build_apps') + for opt in self._build_apps_options(): + optval = getattr(self, opt) + if optval is not None: + setattr(build_cmd, opt, optval) + build_cmd.finalize_options() + if not self.skip_build: + self.run_command('build_apps') + + platforms = build_cmd.platforms + build_base = build_cmd.build_base + os.makedirs(self.dist_dir, exist_ok=True) + os.chdir(self.dist_dir) + + for platform in platforms: + build_dir = os.path.join(build_base, platform) + basename = '{}_{}'.format(self.distribution.get_fullname(), platform) + installers = self.installers.get(platform, self.DEFAULT_INSTALLERS.get(platform, ['zip'])) + + for installer in installers: + self.announce('\nBuilding {} for platform: {}'.format(installer, platform), distutils.log.INFO) + + if installer == 'zip': + self.create_zip(basename, build_dir) + elif installer in ('gztar', 'bztar', 'xztar'): + compress = installer.replace('tar', '') + if compress == 'bz': + compress = 'bz2' + + self.create_tarball(basename, build_dir, compress) + elif installer == 'nsis': + if not platform.startswith('win'): + self.announce( + '\tNSIS installer not supported for platform: {}'.format(platform), + distutils.log.ERROR + ) + continue + try: + subprocess.call(['makensis', '--version']) + except OSError: + self.announce( + '\tCould not find makensis tool that is required to build NSIS installers', + distutils.log.ERROR + ) + # continue + is_64bit = platform == 'win_amd64' + self.create_nsis(basename, build_dir, is_64bit) + + else: + self.announce('\tUnknown installer: {}'.format(installer), distutils.log.ERROR) diff --git a/direct/src/dist/pefile.py b/direct/src/dist/pefile.py new file mode 100755 index 0000000000..548c09e1b7 --- /dev/null +++ b/direct/src/dist/pefile.py @@ -0,0 +1,908 @@ +""" Tools for manipulating Portable Executable files. + +This can be used, for example, to extract a list of dependencies from an .exe +or .dll file, or to add version information and an icon resource to it. """ + +__all__ = ["PEFile"] + +from struct import Struct, unpack, pack, pack_into +from collections import namedtuple +from array import array +import time +from io import BytesIO +import sys + +if sys.version_info >= (3, 0): + unicode = str + unichr = chr + +# Define some internally used structures. +RVASize = namedtuple('RVASize', ('addr', 'size')) +impdirtab = namedtuple('impdirtab', ('lookup', 'timdat', 'forward', 'name', 'impaddr')) +expdirtab = namedtuple('expdirtab', ('flags', 'timdat', 'majver', 'minver', 'name', 'ordinal_base', 'nentries', 'nnames', 'entries', 'names', 'ordinals')) + + +def _unpack_zstring(mem, offs=0): + "Read a zero-terminated string from memory." + c = mem[offs] + str = "" + while c: + str += chr(c) + offs += 1 + c = mem[offs] + return str + +def _unpack_wstring(mem, offs=0): + "Read a UCS-2 string from memory." + name_len, = unpack('" % (self.name, self.vaddr, self.vaddr + self.vsize) + + def __gt__(self, other): + return self.vaddr > other.vaddr + + def __lt__(self, other): + return self.vaddr < other.vaddr + + +class DataResource(object): + """ A resource entry in the resource table. """ + + # Resource types. + cursor = 1 + bitmap = 2 + icon = 3 + menu = 4 + dialog = 5 + string = 6 + font_directory = 7 + font = 8 + accelerator = 9 + rcdata = 10 + message_table = 11 + cursor_group = 12 + icon_group = 14 + version = 16 + dlg_include = 17 + plug_play = 19 + vxd = 20 + animated_cursor = 21 + animated_icon = 22 + html = 23 + manifest = 24 + + def __init__(self): + self._ident = () + self.data = None + self.code_page = 0 + + @property + def encoding(self): + if self.code_page == 0: + return 'ascii' + else: + return 'cp%d' % (self.code_page) + + def get_data(self): + return self.data + + def get_text(self, errors='strict'): + return self.data.decode(self.encoding, errors) + + +class IconGroupResource(object): + code_page = 0 + type = 14 + _entry = Struct('= 256: + colors = 0 + if width >= 256: + width = 0 + if height >= 256: + height = 0 + data += self._entry.pack(width, height, colors, planes, bpp, size, id) + return data + + def unpack_from(self, data, offs=0): + type, count = unpack(' 0: + self.signature = dwords[0] + if len(dwords) > 1: + self.struct_version = dwords[1] + if len(dwords) > 3: + self.file_version = \ + (int(dwords[2] >> 16), int(dwords[2] & 0xffff), + int(dwords[3] >> 16), int(dwords[3] & 0xffff)) + if len(dwords) > 5: + self.product_version = \ + (int(dwords[4] >> 16), int(dwords[4] & 0xffff), + int(dwords[5] >> 16), int(dwords[5] & 0xffff)) + if len(dwords) > 7: + self.file_flags_mask = dwords[6] + self.file_flags = dwords[7] + if len(dwords) > 8: + self.file_os = dwords[8] + if len(dwords) > 9: + self.file_type = dwords[9] + if len(dwords) > 10: + self.file_subtype = dwords[10] + if len(dwords) > 12: + self.file_date = (dwords[11], dwords[12]) + + while offset < length: + offset += self._unpack_info(self, data, offset) + + def __getitem__(self, key): + if key == 'StringFileInfo': + return self.string_info + elif key == 'VarFileInfo': + return self.var_info + else: + raise KeyError("%s does not exist" % (key)) + + def __contains__(self, key): + return key in ('StringFileInfo', 'VarFileInfo') + + def _unpack_info(self, dict, data, offset): + length, value_length, type = unpack(' 0 + end = offset + length + offset += 6 + key = "" + c, = unpack(' 0: + # It contains a value. + if type: + # It's a wchar array value. + value = u"" + c, = unpack('= key: + if key == idname: + return leaf + break + i += 1 + if not isinstance(key, int): + self._strings_size += _padded(len(key) * 2 + 2, 4) + leaf = ResourceTable(ident=self._ident + (key,)) + leaves.insert(i, (key, leaf)) + return leaf + + def __setitem__(self, key, value): + """ Adds the given item to the table. Maintains sort order. """ + if isinstance(key, int): + leaves = self._id_leaves + else: + leaves = self._name_leaves + + if not isinstance(value, ResourceTable): + self._descs_size += 16 + + value._ident = self._ident + (key,) + i = 0 + while i < len(leaves): + idname, leaf = leaves[i] + if idname >= key: + if key == idname: + if not isinstance(leaves[i][1], ResourceTable): + self._descs_size -= 16 + leaves[i] = (key, value) + return + break + i += 1 + if not isinstance(key, int): + self._strings_size += _padded(len(key) * 2 + 2, 4) + leaves.insert(i, (key, value)) + + def __len__(self): + return len(self._name_leaves) + len(self._id_leaves) + + def __iter__(self): + keys = [] + for name, leaf in self._name_leaves: + keys.append(name) + for id, leaf in self._id_leaves: + keys.append(id) + return iter(keys) + + def items(self): + return self._name_leaves + self._id_leaves + + def count_resources(self): + """Counts all of the resources.""" + count = 0 + for key, leaf in self._name_leaves + self._id_leaves: + if isinstance(leaf, ResourceTable): + count += leaf.count_resources() + else: + count += 1 + return count + + def get_nested_tables(self): + """Returns all tables in this table and subtables.""" + # First we yield child tables, then nested tables. This is the + # order in which pack_into assumes the tables will be written. + for key, leaf in self._name_leaves + self._id_leaves: + if isinstance(leaf, ResourceTable): + yield leaf + + for key, leaf in self._name_leaves + self._id_leaves: + if isinstance(leaf, ResourceTable): + for table in leaf.get_nested_tables(): + yield table + + def pack_header(self, data, offs): + self._header.pack_into(data, offs, self.flags, self.timdat, + self.version[0], self.version[1], + len(self._name_leaves), len(self._id_leaves)) + + def unpack_from(self, mem, addr=0, offs=0): + start = addr + offs + self.flags, self.timdat, majver, minver, nnames, nids = \ + self._header.unpack(mem[start:start+16]) + self.version = (majver, minver) + start += 16 + + # Subtables/entries specified by string name. + self._name_leaves = [] + for i in range(nnames): + name_p, data = unpack('= 1: + self.exp_rva = RVASize(*unpack('= 2: + self.imp_rva = RVASize(*unpack('= 3: + self.res_rva = RVASize(*unpack('= 4: + fp.seek((numrvas - 3) * 8, 1) + + # Loop through the sections to find the ones containing our tables. + self.sections = [] + for i in range(nscns): + section = Section() + section.read_header(fp) + self.sections.append(section) + + self.sections.sort() + + # Read the sections into some kind of virtual memory. + self.vmem = bytearray(self.sections[-1].vaddr + self.sections[-1].size) + memview = memoryview(self.vmem) + + for section in self.sections: + fp.seek(section.offset) + fp.readinto(memview[section.vaddr:section.vaddr+section.size]) + + # Read the import table. + start = self.imp_rva.addr + dir = impdirtab(*unpack('= 0 and ordinal < expdir.nentries + start = expdir.entries + 4 * ordinal + addr, = unpack('= section.vaddr and addr < section.vaddr + section.size: + return section + + def add_icon(self, icon, ordinal=2): + """ Adds an icon resource from the given Icon object. Requires + calling add_resource_section() afterwards. """ + + group = IconGroupResource() + self.resources[group.type][ordinal][1033] = group + + images = sorted(icon.images.items(), key=lambda x:-x[0]) + id = 1 + + # Write 8-bpp image headers for sizes under 256x256. + for size, image in images: + if size >= 256: + continue + + xorsize = size + if xorsize % 4 != 0: + xorsize += 4 - (xorsize % 4) + andsize = (size + 7) >> 3 + if andsize % 4 != 0: + andsize += 4 - (andsize % 4) + datasize = 40 + 256 * 4 + (xorsize + andsize) * size + group.add_icon(size, size, 1, 8, datasize, id) + + buf = BytesIO() + icon._write_bitmap(buf, image, size, 8) + + res = DataResource() + res.data = buf.getvalue() + self.resources[3][id][1033] = res + id += 1 + + # And now the 24/32 bpp versions. + for size, image in images: + if size > 256: + continue + + # Calculate the size so we can write the offset within the file. + if image.hasAlpha(): + bpp = 32 + xorsize = size * 4 + else: + bpp = 24 + xorsize = size * 3 + (-(size * 3) & 3) + andsize = (size + 7) >> 3 + if andsize % 4 != 0: + andsize += 4 - (andsize % 4) + datasize = 40 + (xorsize + andsize) * size + + buf = BytesIO() + icon._write_bitmap(buf, image, size, bpp) + + res = DataResource() + res.data = buf.getvalue() + self.resources[3][id][1033] = res + group.add_icon(size, size, 1, bpp, datasize, id) + id += 1 + + def add_section(self, name, flags, data): + """ Adds a new section with the given name, flags and data. The + virtual address space is automatically resized to fit the new data. + + Returns the newly created Section object. """ + + if isinstance(name, unicode): + name = name.encode('ascii') + + section = Section() + section.name = name + section.flags = flags + + # Put it at the end of all the other sections. + section.offset = 0 + for s in self.sections: + section.offset = max(section.offset, s.offset + s.size) + + # Align the offset. + section.offset = _padded(section.offset, self.file_alignment) + + # Find a place to put it in the virtual address space. + section.vaddr = len(self.vmem) + align = section.vaddr % self.section_alignment + if align: + pad = self.section_alignment - align + self.vmem += bytearray(pad) + section.vaddr += pad + + section.vsize = len(data) + section.size = _padded(section.vsize, self.file_alignment) + self.vmem += data + self.sections.append(section) + + # Update the size tallies from the opthdr. + self.image_size += _padded(section.vsize, self.section_alignment) + if flags & 0x20: + self.code_size += section.size + if flags & 0x40: + self.initialized_size += section.size + if flags & 0x80: + self.uninitialized_size += section.size + + return section + + def add_version_info(self, file_ver, product_ver, data, lang=1033, codepage=1200): + """ Adds a version info resource to the file. """ + + if "FileVersion" not in data: + data["FileVersion"] = '.'.join(file_ver) + if "ProductVersion" not in data: + data["ProductVersion"] = '.'.join(product_ver) + + assert len(file_ver) == 4 + assert len(product_ver) == 4 + + res = VersionInfoResource() + res.file_version = file_ver + res.product_version = product_ver + res.string_info = { + "%04x%04x" % (lang, codepage): data + } + res.var_info = { + "Translation": bytearray(pack("= 3 + + fp.seek(self.rva_offset + 4) + if numrvas >= 1: + fp.write(pack('= 2: + fp.write(pack('= 3: + fp.write(pack('= 4: + fp.seek((numrvas - 3) * 8, 1) + + # Write the modified section headers. + for section in self.sections: + section.write_header(fp) + assert fp.tell() <= self.header_size + + # Write the section data of modified sections. + for section in self.sections: + if not section.modified: + continue + + fp.seek(section.offset) + size = min(section.vsize, section.size) + fp.write(self.vmem[section.vaddr:section.vaddr+size]) + + pad = section.size - size + assert pad >= 0 + if pad > 0: + fp.write(bytearray(pad)) + + section.modified = False diff --git a/direct/src/showutil/pfreeze.py b/direct/src/dist/pfreeze.py similarity index 99% rename from direct/src/showutil/pfreeze.py rename to direct/src/dist/pfreeze.py index 2c2827e051..112dbcca0b 100755 --- a/direct/src/showutil/pfreeze.py +++ b/direct/src/dist/pfreeze.py @@ -59,7 +59,7 @@ Options:: import getopt import sys import os -from direct.showutil import FreezeTool +from . import FreezeTool def usage(code, msg = ''): if __doc__: diff --git a/direct/src/distributed/cConnectionRepository.cxx b/direct/src/distributed/cConnectionRepository.cxx index 6e429bfbec..16aea6e8dd 100644 --- a/direct/src/distributed/cConnectionRepository.cxx +++ b/direct/src/distributed/cConnectionRepository.cxx @@ -891,7 +891,7 @@ describe_message(std::ostream &out, const string &prefix, const Datagram &dg) const { DCPacker packer; - packer.set_unpack_data(dg.get_message()); + packer.set_unpack_data((const char *)dg.get_data(), dg.get_length(), false); CHANNEL_TYPE do_id; int msg_type; bool is_update = false; diff --git a/direct/src/filter/CommonFilters.py b/direct/src/filter/CommonFilters.py index 9e54cb82b8..1ba9ec91d4 100644 --- a/direct/src/filter/CommonFilters.py +++ b/direct/src/filter/CommonFilters.py @@ -16,6 +16,13 @@ clunky approach. - Josh """ from .FilterManager import FilterManager +from .filterBloomI import BLOOM_I +from .filterBloomX import BLOOM_X +from .filterBloomY import BLOOM_Y +from .filterBlurX import BLUR_X +from .filterBlurY import BLUR_Y +from .filterCopy import COPY +from .filterDown4 import DOWN_4 from panda3d.core import LVecBase4, LPoint2 from panda3d.core import Filename from panda3d.core import AuxBitplaneAttrib @@ -102,12 +109,6 @@ class CommonFilters: self.task = None self.cleanup() - def loadShader(self, name): - fn = os.path.join(os.path.abspath(os.path.dirname(__file__)), name) - fn = Filename.fromOsSpecific(fn) - fn.makeTrueCase() - return Shader.load(fn) - def cleanup(self): self.manager.cleanup() self.textures = {} @@ -187,9 +188,9 @@ class CommonFilters: self.blur.append(self.manager.renderQuadInto("filter-blur0", colortex=blur0,div=2)) self.blur.append(self.manager.renderQuadInto("filter-blur1", colortex=blur1)) self.blur[0].setShaderInput("src", self.textures["color"]) - self.blur[0].setShader(self.loadShader("filter-blurx.sha")) + self.blur[0].setShader(Shader.make(BLUR_X, Shader.SL_Cg)) self.blur[1].setShaderInput("src", blur0) - self.blur[1].setShader(self.loadShader("filter-blury.sha")) + self.blur[1].setShader(Shader.make(BLUR_Y, Shader.SL_Cg)) if ("AmbientOcclusion" in configuration): ssao0=self.textures["ssao0"] @@ -203,9 +204,9 @@ class CommonFilters: self.ssao[0].setShaderInput("random", loader.loadTexture("maps/random.rgb")) self.ssao[0].setShader(Shader.make(SSAO_BODY % configuration["AmbientOcclusion"].numsamples, Shader.SL_Cg)) self.ssao[1].setShaderInput("src", ssao0) - self.ssao[1].setShader(self.loadShader("filter-blurx.sha")) + self.ssao[1].setShader(Shader.make(BLUR_X, Shader.SL_Cg)) self.ssao[2].setShaderInput("src", ssao1) - self.ssao[2].setShader(self.loadShader("filter-blury.sha")) + self.ssao[2].setShader(Shader.make(BLUR_Y, Shader.SL_Cg)) if ("Bloom" in configuration): bloomconf = configuration["Bloom"] @@ -215,25 +216,28 @@ class CommonFilters: bloom3=self.textures["bloom3"] if (bloomconf.size == "large"): scale=8 - downsampler="filter-down4" + downsamplerName="filter-down4" + downsampler=DOWN_4 elif (bloomconf.size == "medium"): scale=4 - downsampler="filter-copy" + downsamplerName="filter-copy" + downsampler=COPY else: scale=2 - downsampler="filter-copy" + downsamplerName="filter-copy" + downsampler=COPY self.bloom.append(self.manager.renderQuadInto("filter-bloomi", colortex=bloom0, div=2, align=scale)) - self.bloom.append(self.manager.renderQuadInto(downsampler, colortex=bloom1, div=scale, align=scale)) + self.bloom.append(self.manager.renderQuadInto(downsamplerName, colortex=bloom1, div=scale, align=scale)) self.bloom.append(self.manager.renderQuadInto("filter-bloomx", colortex=bloom2, div=scale, align=scale)) self.bloom.append(self.manager.renderQuadInto("filter-bloomy", colortex=bloom3, div=scale, align=scale)) self.bloom[0].setShaderInput("src", self.textures["color"]) - self.bloom[0].setShader(self.loadShader("filter-bloomi.sha")) + self.bloom[0].setShader(Shader.make(BLOOM_I, Shader.SL_Cg)) self.bloom[1].setShaderInput("src", bloom0) - self.bloom[1].setShader(self.loadShader(downsampler + ".sha")) + self.bloom[1].setShader(Shader.make(downsampler, Shader.SL_Cg)) self.bloom[2].setShaderInput("src", bloom1) - self.bloom[2].setShader(self.loadShader("filter-bloomx.sha")) + self.bloom[2].setShader(Shader.make(BLOOM_X, Shader.SL_Cg)) self.bloom[3].setShaderInput("src", bloom2) - self.bloom[3].setShader(self.loadShader("filter-bloomy.sha")) + self.bloom[3].setShader(Shader.make(BLOOM_Y, Shader.SL_Cg)) texcoords = {} texcoordPadding = {} @@ -558,7 +562,6 @@ class CommonFilters: set_cartoon_ink = setCartoonInk del_bloom = delBloom del_ambient_occlusion = delAmbientOcclusion - load_shader = loadShader set_blur_sharpen = setBlurSharpen del_blur_sharpen = delBlurSharpen del_volumetric_lighting = delVolumetricLighting diff --git a/direct/src/filter/filter-bloomi.sha b/direct/src/filter/filterBloomI.py similarity index 99% rename from direct/src/filter/filter-bloomi.sha rename to direct/src/filter/filterBloomI.py index 8b58c21669..6c44fb3ecb 100644 --- a/direct/src/filter/filter-bloomi.sha +++ b/direct/src/filter/filterBloomI.py @@ -1,3 +1,4 @@ +BLOOM_I = """ //Cg // // blend.rgb @@ -23,7 +24,7 @@ // trigger.y // // Must be equal to (1.0/(maxtrigger-mintrigger)) where -// +// // mintrigger is the minimum brightness to trigger a bloom, // and maxtrigger is the brightness at which the bloom // reaches maximum intensity. @@ -83,3 +84,4 @@ void fshader(float2 l_texcoordNW : TEXCOORD0, o_color = (colorNW + colorNE + colorSW + colorSE) * 0.25; } +""" diff --git a/direct/src/filter/filter-bloomx.sha b/direct/src/filter/filterBloomX.py similarity index 99% rename from direct/src/filter/filter-bloomx.sha rename to direct/src/filter/filterBloomX.py index 300ffeff2e..220cd776b7 100644 --- a/direct/src/filter/filter-bloomx.sha +++ b/direct/src/filter/filterBloomX.py @@ -1,3 +1,4 @@ +BLOOM_X = """ //Cg void vshader(float4 vtx_position : POSITION, @@ -46,3 +47,4 @@ void fshader(float4 l_texcoord0 : TEXCOORD0, color += 50 * tex2D(k_src, l_texcoord2.zw); o_color = color / 1200.0; } +""" diff --git a/direct/src/filter/filter-bloomy.sha b/direct/src/filter/filterBloomY.py similarity index 98% rename from direct/src/filter/filter-bloomy.sha rename to direct/src/filter/filterBloomY.py index 51912b8b60..18e2885b1e 100644 --- a/direct/src/filter/filter-bloomy.sha +++ b/direct/src/filter/filterBloomY.py @@ -1,3 +1,4 @@ +BLOOM_Y = """ //Cg void vshader(float4 vtx_position : POSITION, @@ -37,3 +38,4 @@ void fshader(float4 l_texcoord0 : TEXCOORD0, o_color = color / 1200.0; o_color = o_color * k_intensity; } +""" diff --git a/direct/src/filter/filter-blurx.sha b/direct/src/filter/filterBlurX.py similarity index 98% rename from direct/src/filter/filter-blurx.sha rename to direct/src/filter/filterBlurX.py index 47a2217af3..b472a5d7a7 100644 --- a/direct/src/filter/filter-blurx.sha +++ b/direct/src/filter/filterBlurX.py @@ -1,3 +1,4 @@ +BLUR_X = """ //Cg // //Cg profile arbvp1 arbfp1 @@ -33,3 +34,4 @@ void fshader(float2 l_texcoord0 : TEXCOORD0, o_color.w = 1; } +""" diff --git a/direct/src/filter/filter-blury.sha b/direct/src/filter/filterBlurY.py similarity index 98% rename from direct/src/filter/filter-blury.sha rename to direct/src/filter/filterBlurY.py index ab135c363a..173c7a73b5 100644 --- a/direct/src/filter/filter-blury.sha +++ b/direct/src/filter/filterBlurY.py @@ -1,3 +1,4 @@ +BLUR_Y = """ //Cg // //Cg profile arbvp1 arbfp1 @@ -32,4 +33,4 @@ void fshader(float2 l_texcoord0 : TEXCOORD0, o_color /= 7; o_color.w = 1; } - +""" diff --git a/direct/src/filter/filter-copy.sha b/direct/src/filter/filterCopy.py similarity index 97% rename from direct/src/filter/filter-copy.sha rename to direct/src/filter/filterCopy.py index ce7f867554..d9770fb04b 100644 --- a/direct/src/filter/filter-copy.sha +++ b/direct/src/filter/filterCopy.py @@ -1,3 +1,4 @@ +COPY = """ //Cg @@ -17,4 +18,4 @@ void fshader(float2 l_texcoord : TEXCOORD0, { o_color = tex2D(k_src, l_texcoord); } - +""" diff --git a/direct/src/filter/filter-down4.sha b/direct/src/filter/filterDown4.py similarity index 98% rename from direct/src/filter/filter-down4.sha rename to direct/src/filter/filterDown4.py index 54d2967262..14595dbbd7 100644 --- a/direct/src/filter/filter-down4.sha +++ b/direct/src/filter/filterDown4.py @@ -1,3 +1,4 @@ +DOWN_4 = """ //Cg void vshader(float4 vtx_position : POSITION, @@ -30,4 +31,4 @@ void fshader(float2 l_texcoordNW : TEXCOORD0, float4 colorSE = tex2D(k_src, l_texcoordSE); o_color = (colorNW + colorNE + colorSW + colorSE); } - +""" diff --git a/direct/src/p3d/Packager.py b/direct/src/p3d/Packager.py index 784055b771..4e8a303f7f 100644 --- a/direct/src/p3d/Packager.py +++ b/direct/src/p3d/Packager.py @@ -19,7 +19,7 @@ from direct.p3d.SeqValue import SeqValue from direct.p3d.HostInfo import HostInfo from direct.showbase import Loader from direct.showbase import AppRunnerGlobal -from direct.showutil import FreezeTool +from direct.dist import FreezeTool from direct.directnotify.DirectNotifyGlobal import * vfs = VirtualFileSystem.getGlobalPtr() diff --git a/direct/src/p3d/panda3d.pdef b/direct/src/p3d/panda3d.pdef index 860c0b4671..78e7af2cf0 100644 --- a/direct/src/p3d/panda3d.pdef +++ b/direct/src/p3d/panda3d.pdef @@ -71,10 +71,6 @@ class panda3d(package): if sys.version_info < (3, 0): module('encodings.string_escape') - # Pick up the shader files that appear in direct/src/filter. - import direct - file(Filename(direct.__path__[0], 'filter/*.sha'), newDir = 'direct/filter') - # pandac.PandaModules pulls in other Panda3D libs automatically. # Exclude these Panda3D libs; they are big and many applications don't # use them. We define them as separate, optional packages, below, diff --git a/direct/src/plugin_standalone/panda3d.ico b/direct/src/plugin_standalone/panda3d.ico index 381a41388b..3a2c1c33d4 100644 Binary files a/direct/src/plugin_standalone/panda3d.ico and b/direct/src/plugin_standalone/panda3d.ico differ diff --git a/direct/src/showbase/ShowBase.py b/direct/src/showbase/ShowBase.py index fcd790e0bf..bff3b4652c 100644 --- a/direct/src/showbase/ShowBase.py +++ b/direct/src/showbase/ShowBase.py @@ -170,6 +170,7 @@ class ShowBase(DirectObject.DirectObject): self.trackball = None self.texmem = None self.showVertices = None + self.deviceButtonThrowers = [] ## This is a NodePath pointing to the Camera object set up for the 3D scene. ## This is usually a child of self.camera. @@ -294,6 +295,7 @@ class ShowBase(DirectObject.DirectObject): ## The global job manager, as imported from JobManagerGlobal. self.jobMgr = jobMgr + ## Particle manager self.particleMgr = None self.particleMgrEnabled = 0 @@ -303,6 +305,11 @@ class ShowBase(DirectObject.DirectObject): self.physicsMgrEnabled = 0 self.physicsMgrAngular = 0 + ## This is the global input device manager, which keeps track of + ## connected input devices. + self.devices = InputDeviceManager.getGlobalPtr() + self.__inputDeviceNodes = {} + self.createStats() self.AppHasAudioFocus = 1 @@ -1670,6 +1677,57 @@ class ShowBase(DirectObject.DirectObject): return self.mouseWatcherNode.getModifierButtons().isDown( KeyboardButton.meta()) + def attachInputDevice(self, device, prefix=None): + """ + This function attaches an input device to the data graph, which will + cause the device to be polled and generate events. If a prefix is + given and not None, it is used to prefix events generated by this + device, separated by a hyphen. + + If you call this, you should consider calling detachInputDevice when + you are done with the device or when it is disconnected. + """ + + # Protect against the same device being attached multiple times. + assert device not in self.__inputDeviceNodes + + idn = self.dataRoot.attachNewNode(InputDeviceNode(device, device.name)) + + # Setup the button thrower to generate events for the device. + bt = idn.attachNewNode(ButtonThrower(device.name)) + if prefix is not None: + bt.node().setPrefix(prefix + '-') + + assert self.notify.debug("Attached input device {0} with prefix {1}".format(device, prefix)) + self.__inputDeviceNodes[device] = idn + self.deviceButtonThrowers.append(bt) + + def detachInputDevice(self, device): + """ + This should be called after attaching an input device using + attachInputDevice and the device is disconnected or you no longer wish + to keep polling this device for events. + + You do not strictly need to call this if you expect the device to be + reconnected (but be careful that you don't reattach it). + """ + + if device not in self.__inputDeviceNodes: + assert device in self.__inputDeviceNodes + return + + assert self.notify.debug("Detached device {0}".format(device.name)) + + # Remove the ButtonThrower from the deviceButtonThrowers list. + idn = self.__inputDeviceNodes[device] + for bt in self.deviceButtonThrowers: + if idn.isAncestorOf(bt): + self.deviceButtonThrowers.remove(bt) + break + + idn.removeNode() + del self.__inputDeviceNodes[device] + def addAngularIntegrator(self): if not self.physicsMgrAngular: physics = importlib.import_module('panda3d.physics') @@ -1859,6 +1917,9 @@ class ShowBase(DirectObject.DirectObject): return Task.cont def __dataLoop(self, state): + # Check if there were newly connected devices. + self.devices.update() + # traverse the data graph. This reads all the control # inputs (from the mouse and keyboard, for instance) and also # directly acts upon them (for instance, to move the avatar). @@ -3074,6 +3135,8 @@ class ShowBase(DirectObject.DirectObject): setup_mouse = setupMouse setup_mouse_cb = setupMouseCB enable_software_mouse_pointer = enableSoftwareMousePointer + detach_input_device = detachInputDevice + attach_input_device = attachInputDevice add_angular_integrator = addAngularIntegrator enable_particles = enableParticles disable_particles = disableParticles diff --git a/direct/src/showbase/Transitions.py b/direct/src/showbase/Transitions.py index 9e05daad3e..54d7bd2baa 100644 --- a/direct/src/showbase/Transitions.py +++ b/direct/src/showbase/Transitions.py @@ -89,6 +89,8 @@ class Transitions: self.fade.setBin('unsorted', 0) self.fade.setColor(0,0,0,0) + self.fade.setScale(max(base.a2dRight, base.a2dTop)) + def getFadeInIval(self, t=0.5, finishIval=None, blendType='noBlend'): """ Returns an interval without starting it. This is particularly useful in @@ -97,8 +99,7 @@ class Transitions: #self.noTransitions() masad: this creates a one frame pop, is it necessary? self.loadFade() - parent = aspect2d if self.fadeModel else render2d - transitionIval = Sequence(Func(self.fade.reparentTo, parent, DGG.FADE_SORT_INDEX), + transitionIval = Sequence(Func(self.fade.reparentTo, aspect2d, DGG.FADE_SORT_INDEX), Func(self.fade.showThrough), # in case aspect2d is hidden for some reason self.lerpFunc(self.fade, t, self.alphaOff, @@ -120,8 +121,7 @@ class Transitions: self.noTransitions() self.loadFade() - parent = aspect2d if self.fadeModel else render2d - transitionIval = Sequence(Func(self.fade.reparentTo, parent, DGG.FADE_SORT_INDEX), + transitionIval = Sequence(Func(self.fade.reparentTo, aspect2d, DGG.FADE_SORT_INDEX), Func(self.fade.showThrough), # in case aspect2d is hidden for some reason self.lerpFunc(self.fade, t, self.alphaOn, @@ -181,8 +181,7 @@ class Transitions: self.noTransitions() self.loadFade() - parent = aspect2d if self.fadeModel else render2d - self.fade.reparentTo(parent, DGG.FADE_SORT_INDEX) + self.fade.reparentTo(aspect2d, DGG.FADE_SORT_INDEX) self.fade.setColor(self.alphaOn) elif ConfigVariableBool('no-loading-screen', False): if finishIval: @@ -215,8 +214,7 @@ class Transitions: self.noTransitions() self.loadFade() - parent = aspect2d if self.fadeModel else render2d - self.fade.reparentTo(parent, DGG.FADE_SORT_INDEX) + self.fade.reparentTo(aspect2d, DGG.FADE_SORT_INDEX) self.fade.setColor(self.alphaOn[0], self.alphaOn[1], self.alphaOn[2], @@ -232,8 +230,7 @@ class Transitions: self.noTransitions() self.loadFade() - parent = aspect2d if self.fadeModel else render2d - self.fade.reparentTo(parent, DGG.FADE_SORT_INDEX) + self.fade.reparentTo(aspect2d, DGG.FADE_SORT_INDEX) self.fade.setColor(color) def noFade(self): diff --git a/direct/src/stdpy/thread.py b/direct/src/stdpy/thread.py index c10675dd4c..29844b32e2 100644 --- a/direct/src/stdpy/thread.py +++ b/direct/src/stdpy/thread.py @@ -277,13 +277,6 @@ class _local(object): d = _get_thread_locals(core.Thread.getCurrentThread(), id(self)) d[key] = value -## def __getattr__(self, key): -## d = _get_thread_locals(core.Thread.getCurrentThread(), id(self)) -## try: -## return d[key] -## except KeyError: -## raise AttributeError - def __getattribute__(self, key): d = _get_thread_locals(core.Thread.getCurrentThread(), id(self)) if key == '__dict__': @@ -292,6 +285,3 @@ class _local(object): return d[key] except KeyError: return object.__getattribute__(self, key) - - - diff --git a/doc/INSTALL b/doc/INSTALL index 4157e03957..6a9606391e 100644 --- a/doc/INSTALL +++ b/doc/INSTALL @@ -194,7 +194,6 @@ it will show you the available command-line options: --use-wx --no-wx (enable/disable use of WX) --use-fltk --no-fltk (enable/disable use of FLTK) --use-rocket --no-rocket (enable/disable use of ROCKET) - --use-awesomium --no-awesomium (enable/disable use of AWESOMIUM) --use-carbon --no-carbon (enable/disable use of CARBON) --use-cocoa --no-cocoa (enable/disable use of COCOA) --use-x11 --no-x11 (enable/disable use of X11) diff --git a/doc/ReleaseNotes b/doc/ReleaseNotes index 4a8a13ee64..062f129ebd 100644 --- a/doc/ReleaseNotes +++ b/doc/ReleaseNotes @@ -1,3 +1,151 @@ +------------------------ RELEASE 1.10.0 ----------------------- + +This is a major release with significant changes. Please review the +changes when upgrading. The list below is by no means exhaustive, but +should contain the most important changes. + +General +* Experimental ability to build for Android +* New input framework to natively support gamepads, joysticks, etc. +* Multi-threaded render pipeline is a lot more stable now +* New setuptools-based deployment pipeline +* Improvements to mouselook smoothness +* Cache is now at $XDG_CACHE_HOME/panda3d (~/.cache/panda3d), not ~/.panda3d +* Addition of unit test suite +* Many improvements to thread safety +* Many performance improvements +* Tons of bugfixes +* Big style cleanup of C++ source code + +Python API +* Complete support for Python 3 +* Support for coroutines and async/await +* Property interfaces have been added for many settings +* More flexible handling for keyboard arguments in C++ APIs +* Python bindings are completely separated out of the C++ libraries. +* Interrogate binding generator has many improvements. +* Use of pandac.PandaModules is discouraged, use panda3d.core et al +* Use of libRocket is discouraged due to lack of Python 3 support +* Tasks are now sorted in addition order when lacking a sort value +* Fixes iris/fade transitions for extreme aspect ratios +* WeakNodePath is now exposed to Python +* WindowProperties.size(x, y) deprecated; use WindowProperties(size=(x, y)) +* Calling bare run() is deprecated, use base.run() instead +* downcastTo*() methods have been removed, they were already no-ops + +Rendering +* Add new shader-based terrain rendering method (ShaderTerrainMesh) +* The default ColorAttrib mode is now T_vertex +* The ColorAttrib T_off mode now properly disables vertex colors entirely +* Make handling of color attributes more consistent between renderers +* Ability to create an OpenGL core profile context; set "gl-version 3 2" +* Experimental support for reverse-Z rendering for best depth precision +* sRGB framebuffers supported more widely +* Support for infinite near/far clip in lens +* Add some PBR material parameters to material class +* Addition of more built-in GLSL shader inputs; see manual. +* Add p3d_FragData[] GLSL output for MRT in GLSL 1.30 +* Add flag enabling vertex shader control over point size +* Support signed ints and double-precision floats in vertex data with GLSL +* Support unsigned 11/10/10-bit floating-point textures and vertex data +* Support for SSBOs via ShaderBuffer class +* Support OpenGL FBO buffers without any attachments +* Support passing uint variables to GLSL shader +* Allow rendering objects with empty vertex data (for vertex pulling) +* Add LogicOpAttrib, for supporting logical operator blending +* Improvements to OpenGL ES support +* Support for geometry with adjacency information +* Change default alpha blending to improve blending rendered result +* New method for obtaining native OpenGL texture object +* Support windowless offscreen rendering on macOS +* Panda resets OpenGL state better before and after draw callbacks +* OpenGL renderer better supports debugging tools like apitrace +* Support fixed-depth billboards, useful for 2D tags that don't change size + +Shader generator +* Significant performance improvements +* Support for point light shadows +* Hardware skinning support +* Changes to match fixed-function pipeline better +* Fixes for normal vector normalization +* Support multiple normal maps (uses Reoriented Normal Mapping) +* Tracks modifications to materials and texture stages automatically + +Lighting +* Allow specifying light color based on color temperature +* Setting specular color of a light separately is deprecated +* New GLSL inputs to make implementing lighting in shaders much easier +* Add representation for sphere light and rectangle light +* Efficiency improvements for passing light information to shader +* Interocular distance for shadow cameras now always defaults to 0 +* Add low-level lighting module from RenderPipeline + +Textures +* Support cube map arrays +* Support buffer textures +* Many more texture formats supported +* BC4 and BC5 compression modes supported +* Proper depth textures supported in DirectX 9 renderer +* set_ram_image(_as) directly supports buffer protocol +* TexturePeeker supports more formats and component types + +Text +* Dramatic improvements to text rendering performance +* Support for HarfBuzz for higher-quality text shaping and kerning +* Support for right-to-left text +* Support for signed-distance-field rendering in egg-mkfont + +Audio/video +* The default unit for audio is now 1 meter for each Panda unit. +* Native .flac loader +* Support videos with alpha channel in ffmpeg +* OpenAL stability improvements, especially on macOS +* Support loading .opus files with libopusfile +* Fix various memory leaks + +Physics / collisions +* CollisionTube is renamed to CollisionCapsule. +* Box-box collision test is improved to work well with the Pusher +* More box tests for collision system: box-into-plane, box-into-poly +* Capsule (tube) can be used as "from" shape into plane, sphere, capsule, box +* Bullet objects are serializable to .bam files. +* Bullet bindings are now thread safe. +* Bullet debug drawer is more efficient; no longer inherits GeomNode. +* Various fixes to bullet vehicle wheel synchronization +* PhysX bindings are deprecated. + +Pipeline / loading +* Support for Assimp library to load a broad variety of model formats +* Ability to specify min-lod, max-lod, lod-bias in .egg file +* Egg file materials support PBR-style material parameterization +* Support loading more DDS files, including DX10-style ones +* Add support for OpenEXR and HDR textures +* Support line/point thickness in bam2egg +* bam2egg no longer inserts a vestigial ModelNode at the top +* bam2egg supports depth test, offset, cull bin attributes +* Accept a .gz file wherever a .pz file is accepted +* egg-palettize supports mirror and border-color wrap modes +* More robust checks against memory corruptions when loading bad .bam files +* Support for Maya 2017 and 2018 +* Support preprocessing GLSL shaders created with Shader.make + +Build +* We now require using MSVC 2015 or 2017 to compile on Windows. +* At least GCC 4.8 is now required. +* With GCC/clang, enabling C++11 is now required. +* Allow building with more recent ffmpeg versions +* Support for old FFMpeg versions (before 1.1) dropped. +* The ppremake build system has been removed. +* Support for OpenSSL versions before 0.9.7 has been dropped. + +C++ +* Use of NULL is replaced with nullptr +* WeakPointerTo now requires use of lock() method for thread safety +* Mutex et al now satisfy C++11 Lockable constraints +* Panda headers no longer contain `using namespace std;` +* PN_int32 et al have been removed, use stdint.h types instead +* The need to link with pystub and add Python include dirs is removed. + ------------------------ RELEASE 1.9.4 ------------------------ One of the bugfixes in the last 1.9.3 release introduced a regression, diff --git a/dtool/metalibs/dtoolconfig/pydtool.cxx b/dtool/metalibs/dtoolconfig/pydtool.cxx index 7f2e0185f6..39719b385d 100644 --- a/dtool/metalibs/dtoolconfig/pydtool.cxx +++ b/dtool/metalibs/dtoolconfig/pydtool.cxx @@ -17,7 +17,7 @@ #if PYTHON_FRAMEWORK #include #else - #include + #include "Python.h" #endif static PyObject *_inP07yttbRf(PyObject *self, PyObject *args); @@ -45,6 +45,8 @@ static PyObject *_inP07ytDId0(PyObject *self, PyObject *args); static PyObject *_inP07ytHuAm(PyObject *self, PyObject *args); static PyObject *_inP07yt_xr0(PyObject *self, PyObject *args); static PyObject *_inP07ytH5qp(PyObject *self, PyObject *args); +static PyObject *_inP07ytq45U(PyObject *self, PyObject *args); +static PyObject *_inP07yt6IPa(PyObject *self, PyObject *args); static PyObject *_inP07ytU2_B(PyObject *self, PyObject *args); static PyObject *_inP07ytHFO2(PyObject *self, PyObject *args); static PyObject *_inP07ytcfjm(PyObject *self, PyObject *args); @@ -166,10 +168,10 @@ static PyObject * _inP07yttbRf(PyObject *, PyObject *args) { char *param0; if (PyArg_ParseTuple(args, "s", ¶m0)) { - interrogate_add_search_directory((char const *)param0); + ::interrogate_add_search_directory((char const *)param0); return Py_BuildValue(""); } - return (PyObject *)NULL; + return nullptr; } /* @@ -180,10 +182,10 @@ static PyObject * _inP07ytda_g(PyObject *, PyObject *args) { char *param0; if (PyArg_ParseTuple(args, "s", ¶m0)) { - interrogate_add_search_path((char const *)param0); + ::interrogate_add_search_path((char const *)param0); return Py_BuildValue(""); } - return (PyObject *)NULL; + return nullptr; } /* @@ -193,10 +195,10 @@ _inP07ytda_g(PyObject *, PyObject *args) { static PyObject * _inP07yt4RgX(PyObject *, PyObject *args) { if (PyArg_ParseTuple(args, "")) { - bool return_value = interrogate_error_flag(); + bool return_value = ::interrogate_error_flag(); return PyBool_FromLong(return_value); } - return (PyObject *)NULL; + return nullptr; } /* @@ -206,14 +208,14 @@ _inP07yt4RgX(PyObject *, PyObject *args) { static PyObject * _inP07yt3Gip(PyObject *, PyObject *args) { if (PyArg_ParseTuple(args, "")) { - int return_value = interrogate_number_of_manifests(); + int return_value = ::interrogate_number_of_manifests(); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -224,14 +226,14 @@ static PyObject * _inP07ytRKDz(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - ManifestIndex return_value = interrogate_get_manifest((int)param0); + ManifestIndex return_value = ::interrogate_get_manifest((int)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -242,14 +244,14 @@ static PyObject * _inP07ytgZ9N(PyObject *, PyObject *args) { char *param0; if (PyArg_ParseTuple(args, "s", ¶m0)) { - ManifestIndex return_value = interrogate_get_manifest_by_name((char const *)param0); + ManifestIndex return_value = ::interrogate_get_manifest_by_name((char const *)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -260,14 +262,14 @@ static PyObject * _inP07ytFnRZ(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = interrogate_manifest_name((ManifestIndex)param0); + char const *return_value = ::interrogate_manifest_name((ManifestIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else return PyString_FromString(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -278,14 +280,14 @@ static PyObject * _inP07ytg0Qv(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = interrogate_manifest_definition((ManifestIndex)param0); + char const *return_value = ::interrogate_manifest_definition((ManifestIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else return PyString_FromString(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -296,10 +298,10 @@ static PyObject * _inP07yttrqw(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = interrogate_manifest_has_type((ManifestIndex)param0); + bool return_value = ::interrogate_manifest_has_type((ManifestIndex)param0); return PyBool_FromLong(return_value); } - return (PyObject *)NULL; + return nullptr; } /* @@ -310,14 +312,14 @@ static PyObject * _inP07ytdmpW(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - TypeIndex return_value = interrogate_manifest_get_type((ManifestIndex)param0); + TypeIndex return_value = ::interrogate_manifest_get_type((ManifestIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -328,10 +330,10 @@ static PyObject * _inP07ytUYgQ(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = interrogate_manifest_has_getter((ManifestIndex)param0); + bool return_value = ::interrogate_manifest_has_getter((ManifestIndex)param0); return PyBool_FromLong(return_value); } - return (PyObject *)NULL; + return nullptr; } /* @@ -342,14 +344,14 @@ static PyObject * _inP07yt0k7F(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - FunctionIndex return_value = interrogate_manifest_getter((ManifestIndex)param0); + FunctionIndex return_value = ::interrogate_manifest_getter((ManifestIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -360,10 +362,10 @@ static PyObject * _inP07ytfIsr(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = interrogate_manifest_has_int_value((ManifestIndex)param0); + bool return_value = ::interrogate_manifest_has_int_value((ManifestIndex)param0); return PyBool_FromLong(return_value); } - return (PyObject *)NULL; + return nullptr; } /* @@ -374,14 +376,14 @@ static PyObject * _inP07ytvysR(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - int return_value = interrogate_manifest_get_int_value((ManifestIndex)param0); + int return_value = ::interrogate_manifest_get_int_value((ManifestIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -392,14 +394,14 @@ static PyObject * _inP07ytYQ_2(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = interrogate_element_name((ElementIndex)param0); + char const *return_value = ::interrogate_element_name((ElementIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else return PyString_FromString(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -410,14 +412,14 @@ static PyObject * _inP07yt3kdv(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = interrogate_element_scoped_name((ElementIndex)param0); + char const *return_value = ::interrogate_element_scoped_name((ElementIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else return PyString_FromString(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -428,10 +430,10 @@ static PyObject * _inP07ytew01(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = interrogate_element_has_comment((ElementIndex)param0); + bool return_value = ::interrogate_element_has_comment((ElementIndex)param0); return PyBool_FromLong(return_value); } - return (PyObject *)NULL; + return nullptr; } /* @@ -442,14 +444,14 @@ static PyObject * _inP07ytQna7(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = interrogate_element_comment((ElementIndex)param0); + char const *return_value = ::interrogate_element_comment((ElementIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else return PyString_FromString(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -460,14 +462,14 @@ static PyObject * _inP07ytkg95(PyObject *, PyObject *args) { char *param0; if (PyArg_ParseTuple(args, "s", ¶m0)) { - ElementIndex return_value = interrogate_get_element_by_name((char const *)param0); + ElementIndex return_value = ::interrogate_get_element_by_name((char const *)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -478,14 +480,14 @@ static PyObject * _inP07ytluRc(PyObject *, PyObject *args) { char *param0; if (PyArg_ParseTuple(args, "s", ¶m0)) { - ElementIndex return_value = interrogate_get_element_by_scoped_name((char const *)param0); + ElementIndex return_value = ::interrogate_get_element_by_scoped_name((char const *)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -496,14 +498,14 @@ static PyObject * _inP07yttHdM(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - TypeIndex return_value = interrogate_element_type((ElementIndex)param0); + TypeIndex return_value = ::interrogate_element_type((ElementIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -514,10 +516,10 @@ static PyObject * _inP07ytDId0(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = interrogate_element_has_getter((ElementIndex)param0); + bool return_value = ::interrogate_element_has_getter((ElementIndex)param0); return PyBool_FromLong(return_value); } - return (PyObject *)NULL; + return nullptr; } /* @@ -528,14 +530,14 @@ static PyObject * _inP07ytHuAm(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - FunctionIndex return_value = interrogate_element_getter((ElementIndex)param0); + FunctionIndex return_value = ::interrogate_element_getter((ElementIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -546,10 +548,10 @@ static PyObject * _inP07yt_xr0(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = interrogate_element_has_setter((ElementIndex)param0); + bool return_value = ::interrogate_element_has_setter((ElementIndex)param0); return PyBool_FromLong(return_value); } - return (PyObject *)NULL; + return nullptr; } /* @@ -560,14 +562,42 @@ static PyObject * _inP07ytH5qp(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - FunctionIndex return_value = interrogate_element_setter((ElementIndex)param0); + FunctionIndex return_value = ::interrogate_element_setter((ElementIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; +} + +/* + * Python simple wrapper for + * bool interrogate_element_is_sequence(ElementIndex element) + */ +static PyObject * +_inP07ytq45U(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + bool return_value = ::interrogate_element_is_sequence((ElementIndex)param0); + return PyBool_FromLong(return_value); + } + return nullptr; +} + +/* + * Python simple wrapper for + * bool interrogate_element_is_mapping(ElementIndex element) + */ +static PyObject * +_inP07yt6IPa(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + bool return_value = ::interrogate_element_is_mapping((ElementIndex)param0); + return PyBool_FromLong(return_value); + } + return nullptr; } /* @@ -577,14 +607,14 @@ _inP07ytH5qp(PyObject *, PyObject *args) { static PyObject * _inP07ytU2_B(PyObject *, PyObject *args) { if (PyArg_ParseTuple(args, "")) { - int return_value = interrogate_number_of_globals(); + int return_value = ::interrogate_number_of_globals(); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -595,14 +625,14 @@ static PyObject * _inP07ytHFO2(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - ElementIndex return_value = interrogate_get_global((int)param0); + ElementIndex return_value = ::interrogate_get_global((int)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -612,14 +642,14 @@ _inP07ytHFO2(PyObject *, PyObject *args) { static PyObject * _inP07ytcfjm(PyObject *, PyObject *args) { if (PyArg_ParseTuple(args, "")) { - int return_value = interrogate_number_of_global_functions(); + int return_value = ::interrogate_number_of_global_functions(); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -630,14 +660,14 @@ static PyObject * _inP07yt3Sjw(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - FunctionIndex return_value = interrogate_get_global_function((int)param0); + FunctionIndex return_value = ::interrogate_get_global_function((int)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -647,14 +677,14 @@ _inP07yt3Sjw(PyObject *, PyObject *args) { static PyObject * _inP07ytgJcX(PyObject *, PyObject *args) { if (PyArg_ParseTuple(args, "")) { - int return_value = interrogate_number_of_functions(); + int return_value = ::interrogate_number_of_functions(); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -665,14 +695,14 @@ static PyObject * _inP07ytYlw6(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - FunctionIndex return_value = interrogate_get_function((int)param0); + FunctionIndex return_value = ::interrogate_get_function((int)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -683,14 +713,14 @@ static PyObject * _inP07ytsmnz(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = interrogate_function_name((FunctionIndex)param0); + char const *return_value = ::interrogate_function_name((FunctionIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else return PyString_FromString(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -701,14 +731,14 @@ static PyObject * _inP07ytxQ10(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = interrogate_function_scoped_name((FunctionIndex)param0); + char const *return_value = ::interrogate_function_scoped_name((FunctionIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else return PyString_FromString(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -719,10 +749,10 @@ static PyObject * _inP07yt6gPB(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = interrogate_function_has_comment((FunctionIndex)param0); + bool return_value = ::interrogate_function_has_comment((FunctionIndex)param0); return PyBool_FromLong(return_value); } - return (PyObject *)NULL; + return nullptr; } /* @@ -733,14 +763,14 @@ static PyObject * _inP07ytISgV(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = interrogate_function_comment((FunctionIndex)param0); + char const *return_value = ::interrogate_function_comment((FunctionIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else return PyString_FromString(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -751,14 +781,14 @@ static PyObject * _inP07ytH3bx(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = interrogate_function_prototype((FunctionIndex)param0); + char const *return_value = ::interrogate_function_prototype((FunctionIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else return PyString_FromString(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -769,10 +799,10 @@ static PyObject * _inP07ytzeUk(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = interrogate_function_is_method((FunctionIndex)param0); + bool return_value = ::interrogate_function_is_method((FunctionIndex)param0); return PyBool_FromLong(return_value); } - return (PyObject *)NULL; + return nullptr; } /* @@ -783,14 +813,14 @@ static PyObject * _inP07ytUeI5(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - TypeIndex return_value = interrogate_function_class((FunctionIndex)param0); + TypeIndex return_value = ::interrogate_function_class((FunctionIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -801,10 +831,10 @@ static PyObject * _inP07ytuSvx(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = interrogate_function_has_module_name((FunctionIndex)param0); + bool return_value = ::interrogate_function_has_module_name((FunctionIndex)param0); return PyBool_FromLong(return_value); } - return (PyObject *)NULL; + return nullptr; } /* @@ -815,14 +845,14 @@ static PyObject * _inP07ytwpYd(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = interrogate_function_module_name((FunctionIndex)param0); + char const *return_value = ::interrogate_function_module_name((FunctionIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else return PyString_FromString(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -833,10 +863,10 @@ static PyObject * _inP07ytOfNh(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = interrogate_function_has_library_name((FunctionIndex)param0); + bool return_value = ::interrogate_function_has_library_name((FunctionIndex)param0); return PyBool_FromLong(return_value); } - return (PyObject *)NULL; + return nullptr; } /* @@ -847,14 +877,14 @@ static PyObject * _inP07ytf5_U(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = interrogate_function_library_name((FunctionIndex)param0); + char const *return_value = ::interrogate_function_library_name((FunctionIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else return PyString_FromString(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -865,10 +895,10 @@ static PyObject * _inP07ytL3ZB(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = interrogate_function_is_virtual((FunctionIndex)param0); + bool return_value = ::interrogate_function_is_virtual((FunctionIndex)param0); return PyBool_FromLong(return_value); } - return (PyObject *)NULL; + return nullptr; } /* @@ -879,14 +909,14 @@ static PyObject * _inP07ytXw0I(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - int return_value = interrogate_function_number_of_c_wrappers((FunctionIndex)param0); + int return_value = ::interrogate_function_number_of_c_wrappers((FunctionIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -898,14 +928,14 @@ _inP07yt3zru(PyObject *, PyObject *args) { int param0; int param1; if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { - FunctionWrapperIndex return_value = interrogate_function_c_wrapper((FunctionIndex)param0, (int)param1); + FunctionWrapperIndex return_value = ::interrogate_function_c_wrapper((FunctionIndex)param0, (int)param1); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -916,14 +946,14 @@ static PyObject * _inP07ytRrg2(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - int return_value = interrogate_function_number_of_python_wrappers((FunctionIndex)param0); + int return_value = ::interrogate_function_number_of_python_wrappers((FunctionIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -935,14 +965,14 @@ _inP07ytEJCx(PyObject *, PyObject *args) { int param0; int param1; if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { - FunctionWrapperIndex return_value = interrogate_function_python_wrapper((FunctionIndex)param0, (int)param1); + FunctionWrapperIndex return_value = ::interrogate_function_python_wrapper((FunctionIndex)param0, (int)param1); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -953,14 +983,14 @@ static PyObject * _inP07ytWAZr(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = interrogate_wrapper_name((FunctionWrapperIndex)param0); + char const *return_value = ::interrogate_wrapper_name((FunctionWrapperIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else return PyString_FromString(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -971,10 +1001,10 @@ static PyObject * _inP07ytrD_M(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = interrogate_wrapper_is_callable_by_name((FunctionWrapperIndex)param0); + bool return_value = ::interrogate_wrapper_is_callable_by_name((FunctionWrapperIndex)param0); return PyBool_FromLong(return_value); } - return (PyObject *)NULL; + return nullptr; } /* @@ -985,10 +1015,10 @@ static PyObject * _inP07ytjolz(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = interrogate_wrapper_has_comment((FunctionWrapperIndex)param0); + bool return_value = ::interrogate_wrapper_has_comment((FunctionWrapperIndex)param0); return PyBool_FromLong(return_value); } - return (PyObject *)NULL; + return nullptr; } /* @@ -999,14 +1029,14 @@ static PyObject * _inP07ytt_JD(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = interrogate_wrapper_comment((FunctionWrapperIndex)param0); + char const *return_value = ::interrogate_wrapper_comment((FunctionWrapperIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else return PyString_FromString(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -1017,10 +1047,10 @@ static PyObject * _inP07ytwEts(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = interrogate_wrapper_has_return_value((FunctionWrapperIndex)param0); + bool return_value = ::interrogate_wrapper_has_return_value((FunctionWrapperIndex)param0); return PyBool_FromLong(return_value); } - return (PyObject *)NULL; + return nullptr; } /* @@ -1031,14 +1061,14 @@ static PyObject * _inP07ytrJWs(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - TypeIndex return_value = interrogate_wrapper_return_type((FunctionWrapperIndex)param0); + TypeIndex return_value = ::interrogate_wrapper_return_type((FunctionWrapperIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -1049,10 +1079,10 @@ static PyObject * _inP07ytpmFD(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = interrogate_wrapper_caller_manages_return_value((FunctionWrapperIndex)param0); + bool return_value = ::interrogate_wrapper_caller_manages_return_value((FunctionWrapperIndex)param0); return PyBool_FromLong(return_value); } - return (PyObject *)NULL; + return nullptr; } /* @@ -1063,14 +1093,14 @@ static PyObject * _inP07ytyYUX(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - FunctionIndex return_value = interrogate_wrapper_return_value_destructor((FunctionWrapperIndex)param0); + FunctionIndex return_value = ::interrogate_wrapper_return_value_destructor((FunctionWrapperIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -1081,14 +1111,14 @@ static PyObject * _inP07yt54dn(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - int return_value = interrogate_wrapper_number_of_parameters((FunctionWrapperIndex)param0); + int return_value = ::interrogate_wrapper_number_of_parameters((FunctionWrapperIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -1100,14 +1130,14 @@ _inP07ytGMpW(PyObject *, PyObject *args) { int param0; int param1; if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { - TypeIndex return_value = interrogate_wrapper_parameter_type((FunctionWrapperIndex)param0, (int)param1); + TypeIndex return_value = ::interrogate_wrapper_parameter_type((FunctionWrapperIndex)param0, (int)param1); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -1119,10 +1149,10 @@ _inP07ytNuBV(PyObject *, PyObject *args) { int param0; int param1; if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { - bool return_value = interrogate_wrapper_parameter_has_name((FunctionWrapperIndex)param0, (int)param1); + bool return_value = ::interrogate_wrapper_parameter_has_name((FunctionWrapperIndex)param0, (int)param1); return PyBool_FromLong(return_value); } - return (PyObject *)NULL; + return nullptr; } /* @@ -1134,14 +1164,14 @@ _inP07yt9UwA(PyObject *, PyObject *args) { int param0; int param1; if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { - char const *return_value = interrogate_wrapper_parameter_name((FunctionWrapperIndex)param0, (int)param1); + char const *return_value = ::interrogate_wrapper_parameter_name((FunctionWrapperIndex)param0, (int)param1); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else return PyString_FromString(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -1153,10 +1183,10 @@ _inP07yt3FDt(PyObject *, PyObject *args) { int param0; int param1; if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { - bool return_value = interrogate_wrapper_parameter_is_this((FunctionWrapperIndex)param0, (int)param1); + bool return_value = ::interrogate_wrapper_parameter_is_this((FunctionWrapperIndex)param0, (int)param1); return PyBool_FromLong(return_value); } - return (PyObject *)NULL; + return nullptr; } /* @@ -1167,10 +1197,10 @@ static PyObject * _inP07ytf513(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = interrogate_wrapper_has_pointer((FunctionWrapperIndex)param0); + bool return_value = ::interrogate_wrapper_has_pointer((FunctionWrapperIndex)param0); return PyBool_FromLong(return_value); } - return (PyObject *)NULL; + return nullptr; } /* @@ -1181,10 +1211,10 @@ static PyObject * _inP07ytsqGH(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - interrogate_wrapper_pointer((FunctionWrapperIndex)param0); + ::interrogate_wrapper_pointer((FunctionWrapperIndex)param0); return Py_BuildValue(""); } - return (PyObject *)NULL; + return nullptr; } /* @@ -1195,14 +1225,14 @@ static PyObject * _inP07yt7shV(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = interrogate_wrapper_unique_name((FunctionWrapperIndex)param0); + char const *return_value = ::interrogate_wrapper_unique_name((FunctionWrapperIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else return PyString_FromString(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -1213,14 +1243,14 @@ static PyObject * _inP07ytA1eF(PyObject *, PyObject *args) { char *param0; if (PyArg_ParseTuple(args, "s", ¶m0)) { - FunctionWrapperIndex return_value = interrogate_get_wrapper_by_unique_name((char const *)param0); + FunctionWrapperIndex return_value = ::interrogate_get_wrapper_by_unique_name((char const *)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -1231,14 +1261,14 @@ static PyObject * _inP07yt776V(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = interrogate_make_seq_seq_name((MakeSeqIndex)param0); + char const *return_value = ::interrogate_make_seq_seq_name((MakeSeqIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else return PyString_FromString(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -1249,14 +1279,14 @@ static PyObject * _inP07ytryup(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = interrogate_make_seq_scoped_name((MakeSeqIndex)param0); + char const *return_value = ::interrogate_make_seq_scoped_name((MakeSeqIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else return PyString_FromString(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -1267,10 +1297,10 @@ static PyObject * _inP07ytiytI(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = interrogate_make_seq_has_comment((ElementIndex)param0); + bool return_value = ::interrogate_make_seq_has_comment((ElementIndex)param0); return PyBool_FromLong(return_value); } - return (PyObject *)NULL; + return nullptr; } /* @@ -1281,14 +1311,14 @@ static PyObject * _inP07ytZc07(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = interrogate_make_seq_comment((ElementIndex)param0); + char const *return_value = ::interrogate_make_seq_comment((ElementIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else return PyString_FromString(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -1299,14 +1329,14 @@ static PyObject * _inP07ytfaH0(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = interrogate_make_seq_num_name((MakeSeqIndex)param0); + char const *return_value = ::interrogate_make_seq_num_name((MakeSeqIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else return PyString_FromString(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -1317,14 +1347,14 @@ static PyObject * _inP07ytGB9D(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = interrogate_make_seq_element_name((MakeSeqIndex)param0); + char const *return_value = ::interrogate_make_seq_element_name((MakeSeqIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else return PyString_FromString(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -1334,14 +1364,14 @@ _inP07ytGB9D(PyObject *, PyObject *args) { static PyObject * _inP07ytsxxs(PyObject *, PyObject *args) { if (PyArg_ParseTuple(args, "")) { - int return_value = interrogate_number_of_global_types(); + int return_value = ::interrogate_number_of_global_types(); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -1352,14 +1382,14 @@ static PyObject * _inP07ytMT0z(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - TypeIndex return_value = interrogate_get_global_type((int)param0); + TypeIndex return_value = ::interrogate_get_global_type((int)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -1369,14 +1399,14 @@ _inP07ytMT0z(PyObject *, PyObject *args) { static PyObject * _inP07ytiW3v(PyObject *, PyObject *args) { if (PyArg_ParseTuple(args, "")) { - int return_value = interrogate_number_of_types(); + int return_value = ::interrogate_number_of_types(); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -1387,14 +1417,14 @@ static PyObject * _inP07yt4Px8(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - TypeIndex return_value = interrogate_get_type((int)param0); + TypeIndex return_value = ::interrogate_get_type((int)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -1405,14 +1435,14 @@ static PyObject * _inP07ytNHcs(PyObject *, PyObject *args) { char *param0; if (PyArg_ParseTuple(args, "s", ¶m0)) { - TypeIndex return_value = interrogate_get_type_by_name((char const *)param0); + TypeIndex return_value = ::interrogate_get_type_by_name((char const *)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -1423,14 +1453,14 @@ static PyObject * _inP07ytqHrb(PyObject *, PyObject *args) { char *param0; if (PyArg_ParseTuple(args, "s", ¶m0)) { - TypeIndex return_value = interrogate_get_type_by_scoped_name((char const *)param0); + TypeIndex return_value = ::interrogate_get_type_by_scoped_name((char const *)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -1441,14 +1471,14 @@ static PyObject * _inP07ytaOqq(PyObject *, PyObject *args) { char *param0; if (PyArg_ParseTuple(args, "s", ¶m0)) { - TypeIndex return_value = interrogate_get_type_by_true_name((char const *)param0); + TypeIndex return_value = ::interrogate_get_type_by_true_name((char const *)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -1459,10 +1489,10 @@ static PyObject * _inP07ytpTBb(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = interrogate_type_is_global((TypeIndex)param0); + bool return_value = ::interrogate_type_is_global((TypeIndex)param0); return PyBool_FromLong(return_value); } - return (PyObject *)NULL; + return nullptr; } /* @@ -1473,14 +1503,14 @@ static PyObject * _inP07ytqWOw(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = interrogate_type_name((TypeIndex)param0); + char const *return_value = ::interrogate_type_name((TypeIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else return PyString_FromString(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -1491,14 +1521,14 @@ static PyObject * _inP07ytHu7x(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = interrogate_type_scoped_name((TypeIndex)param0); + char const *return_value = ::interrogate_type_scoped_name((TypeIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else return PyString_FromString(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -1509,14 +1539,14 @@ static PyObject * _inP07ytwGnA(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = interrogate_type_true_name((TypeIndex)param0); + char const *return_value = ::interrogate_type_true_name((TypeIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else return PyString_FromString(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -1527,10 +1557,10 @@ static PyObject * _inP07ytXGxx(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = interrogate_type_is_nested((TypeIndex)param0); + bool return_value = ::interrogate_type_is_nested((TypeIndex)param0); return PyBool_FromLong(return_value); } - return (PyObject *)NULL; + return nullptr; } /* @@ -1541,14 +1571,14 @@ static PyObject * _inP07ytj04Z(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - TypeIndex return_value = interrogate_type_outer_class((TypeIndex)param0); + TypeIndex return_value = ::interrogate_type_outer_class((TypeIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -1559,10 +1589,10 @@ static PyObject * _inP07ytEOv4(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = interrogate_type_has_comment((TypeIndex)param0); + bool return_value = ::interrogate_type_has_comment((TypeIndex)param0); return PyBool_FromLong(return_value); } - return (PyObject *)NULL; + return nullptr; } /* @@ -1573,14 +1603,14 @@ static PyObject * _inP07ytpCqJ(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = interrogate_type_comment((TypeIndex)param0); + char const *return_value = ::interrogate_type_comment((TypeIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else return PyString_FromString(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -1591,10 +1621,10 @@ static PyObject * _inP07yt_Pz3(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = interrogate_type_has_module_name((TypeIndex)param0); + bool return_value = ::interrogate_type_has_module_name((TypeIndex)param0); return PyBool_FromLong(return_value); } - return (PyObject *)NULL; + return nullptr; } /* @@ -1605,14 +1635,14 @@ static PyObject * _inP07ytt_06(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = interrogate_type_module_name((TypeIndex)param0); + char const *return_value = ::interrogate_type_module_name((TypeIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else return PyString_FromString(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -1623,10 +1653,10 @@ static PyObject * _inP07ytmuPs(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = interrogate_type_has_library_name((TypeIndex)param0); + bool return_value = ::interrogate_type_has_library_name((TypeIndex)param0); return PyBool_FromLong(return_value); } - return (PyObject *)NULL; + return nullptr; } /* @@ -1637,14 +1667,14 @@ static PyObject * _inP07ytvM8B(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = interrogate_type_library_name((TypeIndex)param0); + char const *return_value = ::interrogate_type_library_name((TypeIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else return PyString_FromString(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -1655,10 +1685,10 @@ static PyObject * _inP07ytap97(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = interrogate_type_is_atomic((TypeIndex)param0); + bool return_value = ::interrogate_type_is_atomic((TypeIndex)param0); return PyBool_FromLong(return_value); } - return (PyObject *)NULL; + return nullptr; } /* @@ -1669,14 +1699,14 @@ static PyObject * _inP07yt0o8D(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - AtomicToken return_value = interrogate_type_atomic_token((TypeIndex)param0); + AtomicToken return_value = ::interrogate_type_atomic_token((TypeIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -1687,10 +1717,10 @@ static PyObject * _inP07ytOoQ2(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = interrogate_type_is_unsigned((TypeIndex)param0); + bool return_value = ::interrogate_type_is_unsigned((TypeIndex)param0); return PyBool_FromLong(return_value); } - return (PyObject *)NULL; + return nullptr; } /* @@ -1701,10 +1731,10 @@ static PyObject * _inP07ytKuFh(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = interrogate_type_is_signed((TypeIndex)param0); + bool return_value = ::interrogate_type_is_signed((TypeIndex)param0); return PyBool_FromLong(return_value); } - return (PyObject *)NULL; + return nullptr; } /* @@ -1715,10 +1745,10 @@ static PyObject * _inP07yto5L6(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = interrogate_type_is_long((TypeIndex)param0); + bool return_value = ::interrogate_type_is_long((TypeIndex)param0); return PyBool_FromLong(return_value); } - return (PyObject *)NULL; + return nullptr; } /* @@ -1729,10 +1759,10 @@ static PyObject * _inP07ytzgKK(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = interrogate_type_is_longlong((TypeIndex)param0); + bool return_value = ::interrogate_type_is_longlong((TypeIndex)param0); return PyBool_FromLong(return_value); } - return (PyObject *)NULL; + return nullptr; } /* @@ -1743,10 +1773,10 @@ static PyObject * _inP07yt0FIF(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = interrogate_type_is_short((TypeIndex)param0); + bool return_value = ::interrogate_type_is_short((TypeIndex)param0); return PyBool_FromLong(return_value); } - return (PyObject *)NULL; + return nullptr; } /* @@ -1757,10 +1787,10 @@ static PyObject * _inP07ytZqvD(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = interrogate_type_is_wrapped((TypeIndex)param0); + bool return_value = ::interrogate_type_is_wrapped((TypeIndex)param0); return PyBool_FromLong(return_value); } - return (PyObject *)NULL; + return nullptr; } /* @@ -1771,10 +1801,10 @@ static PyObject * _inP07ytDyRd(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = interrogate_type_is_pointer((TypeIndex)param0); + bool return_value = ::interrogate_type_is_pointer((TypeIndex)param0); return PyBool_FromLong(return_value); } - return (PyObject *)NULL; + return nullptr; } /* @@ -1785,10 +1815,10 @@ static PyObject * _inP07ytMnKa(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = interrogate_type_is_const((TypeIndex)param0); + bool return_value = ::interrogate_type_is_const((TypeIndex)param0); return PyBool_FromLong(return_value); } - return (PyObject *)NULL; + return nullptr; } /* @@ -1799,10 +1829,10 @@ static PyObject * _inP07ytRtji(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = interrogate_type_is_typedef((TypeIndex)param0); + bool return_value = ::interrogate_type_is_typedef((TypeIndex)param0); return PyBool_FromLong(return_value); } - return (PyObject *)NULL; + return nullptr; } /* @@ -1813,14 +1843,14 @@ static PyObject * _inP07ytCnbQ(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - TypeIndex return_value = interrogate_type_wrapped_type((TypeIndex)param0); + TypeIndex return_value = ::interrogate_type_wrapped_type((TypeIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -1831,10 +1861,10 @@ static PyObject * _inP07ytdUVN(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = interrogate_type_is_enum((TypeIndex)param0); + bool return_value = ::interrogate_type_is_enum((TypeIndex)param0); return PyBool_FromLong(return_value); } - return (PyObject *)NULL; + return nullptr; } /* @@ -1845,14 +1875,14 @@ static PyObject * _inP07ytihbt(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - int return_value = interrogate_type_number_of_enum_values((TypeIndex)param0); + int return_value = ::interrogate_type_number_of_enum_values((TypeIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -1864,14 +1894,14 @@ _inP07ytbyPY(PyObject *, PyObject *args) { int param0; int param1; if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { - char const *return_value = interrogate_type_enum_value_name((TypeIndex)param0, (int)param1); + char const *return_value = ::interrogate_type_enum_value_name((TypeIndex)param0, (int)param1); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else return PyString_FromString(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -1883,14 +1913,14 @@ _inP07ytAaT6(PyObject *, PyObject *args) { int param0; int param1; if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { - char const *return_value = interrogate_type_enum_value_scoped_name((TypeIndex)param0, (int)param1); + char const *return_value = ::interrogate_type_enum_value_scoped_name((TypeIndex)param0, (int)param1); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else return PyString_FromString(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -1902,14 +1932,14 @@ _inP07ytgL9q(PyObject *, PyObject *args) { int param0; int param1; if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { - char const *return_value = interrogate_type_enum_value_comment((TypeIndex)param0, (int)param1); + char const *return_value = ::interrogate_type_enum_value_comment((TypeIndex)param0, (int)param1); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else return PyString_FromString(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -1921,14 +1951,14 @@ _inP07ytWB97(PyObject *, PyObject *args) { int param0; int param1; if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { - int return_value = interrogate_type_enum_value((TypeIndex)param0, (int)param1); + int return_value = ::interrogate_type_enum_value((TypeIndex)param0, (int)param1); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -1939,10 +1969,10 @@ static PyObject * _inP07ytDUAl(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = interrogate_type_is_struct((TypeIndex)param0); + bool return_value = ::interrogate_type_is_struct((TypeIndex)param0); return PyBool_FromLong(return_value); } - return (PyObject *)NULL; + return nullptr; } /* @@ -1953,10 +1983,10 @@ static PyObject * _inP07yt1_Kf(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = interrogate_type_is_class((TypeIndex)param0); + bool return_value = ::interrogate_type_is_class((TypeIndex)param0); return PyBool_FromLong(return_value); } - return (PyObject *)NULL; + return nullptr; } /* @@ -1967,10 +1997,10 @@ static PyObject * _inP07yt98lD(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = interrogate_type_is_union((TypeIndex)param0); + bool return_value = ::interrogate_type_is_union((TypeIndex)param0); return PyBool_FromLong(return_value); } - return (PyObject *)NULL; + return nullptr; } /* @@ -1981,10 +2011,10 @@ static PyObject * _inP07yt9SHr(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = interrogate_type_is_fully_defined((TypeIndex)param0); + bool return_value = ::interrogate_type_is_fully_defined((TypeIndex)param0); return PyBool_FromLong(return_value); } - return (PyObject *)NULL; + return nullptr; } /* @@ -1995,10 +2025,10 @@ static PyObject * _inP07ytdiZP(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = interrogate_type_is_unpublished((TypeIndex)param0); + bool return_value = ::interrogate_type_is_unpublished((TypeIndex)param0); return PyBool_FromLong(return_value); } - return (PyObject *)NULL; + return nullptr; } /* @@ -2009,14 +2039,14 @@ static PyObject * _inP07ytTdER(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - int return_value = interrogate_type_number_of_constructors((TypeIndex)param0); + int return_value = ::interrogate_type_number_of_constructors((TypeIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -2028,14 +2058,14 @@ _inP07ytYO56(PyObject *, PyObject *args) { int param0; int param1; if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { - FunctionIndex return_value = interrogate_type_get_constructor((TypeIndex)param0, (int)param1); + FunctionIndex return_value = ::interrogate_type_get_constructor((TypeIndex)param0, (int)param1); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -2046,10 +2076,10 @@ static PyObject * _inP07ytxtCG(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = interrogate_type_has_destructor((TypeIndex)param0); + bool return_value = ::interrogate_type_has_destructor((TypeIndex)param0); return PyBool_FromLong(return_value); } - return (PyObject *)NULL; + return nullptr; } /* @@ -2060,10 +2090,10 @@ static PyObject * _inP07yt_EB2(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = interrogate_type_destructor_is_inherited((TypeIndex)param0); + bool return_value = ::interrogate_type_destructor_is_inherited((TypeIndex)param0); return PyBool_FromLong(return_value); } - return (PyObject *)NULL; + return nullptr; } /* @@ -2074,14 +2104,14 @@ static PyObject * _inP07ytEG1l(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - FunctionIndex return_value = interrogate_type_get_destructor((TypeIndex)param0); + FunctionIndex return_value = ::interrogate_type_get_destructor((TypeIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -2092,14 +2122,14 @@ static PyObject * _inP07yt7tUq(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - int return_value = interrogate_type_number_of_elements((TypeIndex)param0); + int return_value = ::interrogate_type_number_of_elements((TypeIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -2111,14 +2141,14 @@ _inP07ytyStU(PyObject *, PyObject *args) { int param0; int param1; if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { - ElementIndex return_value = interrogate_type_get_element((TypeIndex)param0, (int)param1); + ElementIndex return_value = ::interrogate_type_get_element((TypeIndex)param0, (int)param1); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -2129,14 +2159,14 @@ static PyObject * _inP07ytdM85(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - int return_value = interrogate_type_number_of_methods((TypeIndex)param0); + int return_value = ::interrogate_type_number_of_methods((TypeIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -2148,14 +2178,14 @@ _inP07ytk_GN(PyObject *, PyObject *args) { int param0; int param1; if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { - FunctionIndex return_value = interrogate_type_get_method((TypeIndex)param0, (int)param1); + FunctionIndex return_value = ::interrogate_type_get_method((TypeIndex)param0, (int)param1); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -2166,14 +2196,14 @@ static PyObject * _inP07yt8QjG(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - int return_value = interrogate_type_number_of_make_seqs((TypeIndex)param0); + int return_value = ::interrogate_type_number_of_make_seqs((TypeIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -2185,14 +2215,14 @@ _inP07ytyMtj(PyObject *, PyObject *args) { int param0; int param1; if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { - MakeSeqIndex return_value = interrogate_type_get_make_seq((TypeIndex)param0, (int)param1); + MakeSeqIndex return_value = ::interrogate_type_get_make_seq((TypeIndex)param0, (int)param1); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -2203,14 +2233,14 @@ static PyObject * _inP07ytHDtN(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - int return_value = interrogate_type_number_of_casts((TypeIndex)param0); + int return_value = ::interrogate_type_number_of_casts((TypeIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -2222,14 +2252,14 @@ _inP07ytHFjA(PyObject *, PyObject *args) { int param0; int param1; if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { - FunctionIndex return_value = interrogate_type_get_cast((TypeIndex)param0, (int)param1); + FunctionIndex return_value = ::interrogate_type_get_cast((TypeIndex)param0, (int)param1); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -2240,14 +2270,14 @@ static PyObject * _inP07yt_NPR(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - int return_value = interrogate_type_number_of_derivations((TypeIndex)param0); + int return_value = ::interrogate_type_number_of_derivations((TypeIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -2259,14 +2289,14 @@ _inP07ytcTOH(PyObject *, PyObject *args) { int param0; int param1; if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { - TypeIndex return_value = interrogate_type_get_derivation((TypeIndex)param0, (int)param1); + TypeIndex return_value = ::interrogate_type_get_derivation((TypeIndex)param0, (int)param1); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -2278,10 +2308,10 @@ _inP07ythdU7(PyObject *, PyObject *args) { int param0; int param1; if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { - bool return_value = interrogate_type_derivation_has_upcast((TypeIndex)param0, (int)param1); + bool return_value = ::interrogate_type_derivation_has_upcast((TypeIndex)param0, (int)param1); return PyBool_FromLong(return_value); } - return (PyObject *)NULL; + return nullptr; } /* @@ -2293,14 +2323,14 @@ _inP07ytQPxU(PyObject *, PyObject *args) { int param0; int param1; if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { - FunctionIndex return_value = interrogate_type_get_upcast((TypeIndex)param0, (int)param1); + FunctionIndex return_value = ::interrogate_type_get_upcast((TypeIndex)param0, (int)param1); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -2312,10 +2342,10 @@ _inP07ytO7Pz(PyObject *, PyObject *args) { int param0; int param1; if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { - bool return_value = interrogate_type_derivation_downcast_is_impossible((TypeIndex)param0, (int)param1); + bool return_value = ::interrogate_type_derivation_downcast_is_impossible((TypeIndex)param0, (int)param1); return PyBool_FromLong(return_value); } - return (PyObject *)NULL; + return nullptr; } /* @@ -2327,10 +2357,10 @@ _inP07ytvu_E(PyObject *, PyObject *args) { int param0; int param1; if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { - bool return_value = interrogate_type_derivation_has_downcast((TypeIndex)param0, (int)param1); + bool return_value = ::interrogate_type_derivation_has_downcast((TypeIndex)param0, (int)param1); return PyBool_FromLong(return_value); } - return (PyObject *)NULL; + return nullptr; } /* @@ -2342,14 +2372,14 @@ _inP07ytxGUt(PyObject *, PyObject *args) { int param0; int param1; if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { - FunctionIndex return_value = interrogate_type_get_downcast((TypeIndex)param0, (int)param1); + FunctionIndex return_value = ::interrogate_type_get_downcast((TypeIndex)param0, (int)param1); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -2360,14 +2390,14 @@ static PyObject * _inP07ytzM1P(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - int return_value = interrogate_type_number_of_nested_types((TypeIndex)param0); + int return_value = ::interrogate_type_number_of_nested_types((TypeIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -2379,14 +2409,14 @@ _inP07ytoY5L(PyObject *, PyObject *args) { int param0; int param1; if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { - TypeIndex return_value = interrogate_type_get_nested_type((TypeIndex)param0, (int)param1); + TypeIndex return_value = ::interrogate_type_get_nested_type((TypeIndex)param0, (int)param1); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else return PyInt_FromLong(return_value); #endif } - return (PyObject *)NULL; + return nullptr; } /* @@ -2397,10 +2427,10 @@ static PyObject * _inP07yte_7S(PyObject *, PyObject *args) { char *param0; if (PyArg_ParseTuple(args, "s", ¶m0)) { - interrogate_request_database((char const *)param0); + ::interrogate_request_database((char const *)param0); return Py_BuildValue(""); } - return (PyObject *)NULL; + return nullptr; } /* @@ -2411,10 +2441,10 @@ static PyObject * _inP07ytw_15(PyObject *, PyObject *args) { Py_ssize_t param0; if (PyArg_ParseTuple(args, "n", ¶m0)) { - interrogate_request_module((InterrogateModuleDef *)param0); + ::interrogate_request_module((InterrogateModuleDef *)param0); return Py_BuildValue(""); } - return (PyObject *)NULL; + return nullptr; } @@ -2444,6 +2474,8 @@ static PyMethodDef python_simple_funcs[] = { { "interrogate_element_getter", &_inP07ytHuAm, METH_VARARGS }, { "interrogate_element_has_setter", &_inP07yt_xr0, METH_VARARGS }, { "interrogate_element_setter", &_inP07ytH5qp, METH_VARARGS }, + { "interrogate_element_is_sequence", &_inP07ytq45U, METH_VARARGS }, + { "interrogate_element_is_mapping", &_inP07yt6IPa, METH_VARARGS }, { "interrogate_number_of_globals", &_inP07ytU2_B, METH_VARARGS }, { "interrogate_get_global", &_inP07ytHFO2, METH_VARARGS }, { "interrogate_number_of_global_functions", &_inP07ytcfjm, METH_VARARGS }, @@ -2555,17 +2587,17 @@ static PyMethodDef python_simple_funcs[] = { { "interrogate_type_get_nested_type", &_inP07ytoY5L, METH_VARARGS }, { "interrogate_request_database", &_inP07yte_7S, METH_VARARGS }, { "interrogate_request_module", &_inP07ytw_15, METH_VARARGS }, - { NULL, NULL } + { nullptr, nullptr } }; #if PY_MAJOR_VERSION >= 3 static struct PyModuleDef python_simple_module = { PyModuleDef_HEAD_INIT, "interrogatedb", - NULL, + nullptr, -1, python_simple_funcs, - NULL, NULL, NULL, NULL + nullptr, nullptr, nullptr, nullptr }; #define INIT_FUNC PyObject *PyInit_interrogatedb diff --git a/dtool/src/cppparser/cppExpression.cxx b/dtool/src/cppparser/cppExpression.cxx index 2b2cc0bd50..78da68b5a7 100644 --- a/dtool/src/cppparser/cppExpression.cxx +++ b/dtool/src/cppparser/cppExpression.cxx @@ -25,6 +25,7 @@ #include "cppFunctionGroup.h" #include "cppFunctionType.h" #include "cppClosureType.h" +#include "cppReferenceType.h" #include "cppStructType.h" #include "cppBison.h" #include "pdtoa.h" @@ -260,12 +261,12 @@ CPPExpression(CPPIdentifier *ident, CPPScope *current_scope, _u._variable = inst; return; } - CPPFunctionGroup *fgroup = decl->as_function_group(); + /*CPPFunctionGroup *fgroup = decl->as_function_group(); if (fgroup != nullptr) { _type = T_function; _u._fgroup = fgroup; return; - } + }*/ } _type = T_unknown_ident; @@ -1232,6 +1233,61 @@ determine_type() const { return nullptr; // Compiler kludge; can't get here. } +/** + * Returns true if this is an lvalue expression. + */ +bool CPPExpression:: +is_lvalue() const { + switch (_type) { + case T_variable: + case T_function: + case T_unknown_ident: + return true; + + case T_typecast: + case T_static_cast: + case T_dynamic_cast: + case T_const_cast: + case T_reinterpret_cast: + { + CPPReferenceType *ref_type = _u._typecast._to->as_reference_type(); + return ref_type != nullptr && ref_type->_value_category == CPPReferenceType::VC_lvalue; + } + + case T_unary_operation: + if (_u._op._operator == 'f') { + // A function returning an lvalue reference. + CPPType *return_type = determine_type(); + if (return_type != nullptr) { + CPPReferenceType *ref_type = return_type->as_reference_type(); + return ref_type != nullptr && ref_type->_value_category == CPPReferenceType::VC_lvalue; + } + } + return _u._op._operator == PLUSPLUS + || _u._op._operator == MINUSMINUS + || _u._op._operator == '*'; + + case T_binary_operation: + if (_u._op._operator == ',') { + CPPReferenceType *ref_type = _u._op._op2->as_reference_type(); + return ref_type != nullptr && ref_type->_value_category == CPPReferenceType::VC_lvalue; + } + return (_u._op._operator == POINTSAT || _u._op._operator == ','); + + case T_trinary_operation: + return _u._op._op2->is_lvalue() && _u._op._op3->is_lvalue(); + + case T_literal: + case T_raw_literal: + return true; + + default: + break; + } + + return false; +} + /** * Returns true if this declaration is an actual, factual declaration, or * false if some part of the declaration depends on a template parameter which diff --git a/dtool/src/cppparser/cppExpression.h b/dtool/src/cppparser/cppExpression.h index 4482727f53..3cea871679 100644 --- a/dtool/src/cppparser/cppExpression.h +++ b/dtool/src/cppparser/cppExpression.h @@ -133,6 +133,7 @@ public: Result evaluate() const; CPPType *determine_type() const; + bool is_lvalue() const; bool is_tbd() const; virtual bool is_fully_specified() const; diff --git a/dtool/src/dtoolutil/globPattern.cxx b/dtool/src/dtoolutil/globPattern.cxx index 982fe3bb1d..d2ea916466 100644 --- a/dtool/src/dtoolutil/globPattern.cxx +++ b/dtool/src/dtoolutil/globPattern.cxx @@ -12,6 +12,7 @@ */ #include "globPattern.h" +#include "string_utils.h" #include using std::string; @@ -199,9 +200,7 @@ r_match_files(const Filename &prefix, const string &suffix, next_glob = *this; } - vector_string::const_iterator fi; - for (fi = dir_files.begin(); fi != dir_files.end(); ++fi) { - const string &local_file = (*fi); + for (const string &local_file : dir_files) { if (_pattern[0] == '.' || (local_file.empty() || local_file[0] != '.')) { if (matches(local_file)) { // We have a match; continue. @@ -227,6 +226,126 @@ r_match_files(const Filename &prefix, const string &suffix, return num_matched; } +/** + * Treats the GlobPattern as a filename pattern, and returns true if the given + * filename matches the pattern. Unlike matches(), this will not match slash + * characters for single asterisk characters, and it will ignore path + * components that only contain a dot. + */ +bool GlobPattern:: +matches_file(Filename candidate) const { + if (_pattern.empty()) { + // Special case. + return candidate.empty(); + } + + // Either both must be absolute, or both must be relative. + if ((_pattern[0] != '/') != candidate.is_local()) { + return false; + } + + return r_matches_file(_pattern, candidate); +} + +/** + * The recursive implementation of matches_file(). + */ +bool GlobPattern:: +r_matches_file(const string &pattern, const Filename &candidate) const { + // Split off the next bit of pattern. + std::string next_pattern; + GlobPattern glob; + glob.set_case_sensitive(get_case_sensitive()); + glob.set_nomatch_chars(get_nomatch_chars()); + + bool pattern_end; + size_t slash = pattern.find('/'); + if (slash == string::npos) { + glob.set_pattern(pattern); + pattern_end = true; + } else { + glob.set_pattern(pattern.substr(0, slash)); + next_pattern = pattern.substr(slash + 1); + pattern_end = false; + + if (slash == 0 || (slash == 1 && pattern[0] == '.')) { + // Ignore // and /./ in patterns + return r_matches_file(next_pattern, candidate); + } + } + + // Also split off the next component in the candidate filename. + std::string part; + Filename next_candidate; + + bool candidate_end; + size_t fn_slash = ((const std::string &)candidate).find('/'); + if (fn_slash == string::npos) { + part = candidate; + candidate_end = true; + } else { + part = candidate.substr(0, fn_slash); + next_candidate = candidate.substr(fn_slash + 1); + candidate_end = false; + + // Ignore // and /./ in filenames. + if (fn_slash == 0 || part == ".") { + return r_matches_file(pattern, next_candidate); + } + } + + // Now check if the current part matches the current pattern. + bool part_matches; + if (glob.get_pattern() == "**") { + // This matches any number of parts. + if (pattern_end) { + // We might as well stop checking here; it matches whatever might come. + return true; + } + // We branch out to three options: either we match nothing, we match this + // part only, or we match this part and maybe more. + return r_matches_file(next_pattern, candidate) + || (!candidate_end && r_matches_file(next_pattern, next_candidate)) + || (!candidate_end && r_matches_file(pattern, next_candidate)); + } + else if (glob.get_pattern() == "*" && _nomatch_chars.empty()) { + // Matches any part (faster version of below) + part_matches = true; + } + else if ((glob.get_pattern() == "." && part.empty()) + || (glob.get_pattern().empty() && part == ".")) { + // So that /path/. matches /path/, and vice versa. + part_matches = true; + } + else if (glob.has_glob_characters()) { + part_matches = glob.matches(part); + } + else if (get_case_sensitive()) { + part_matches = (part == glob.get_pattern()); + } + else { + part_matches = (cmp_nocase(part, glob.get_pattern()) == 0); + } + + if (!part_matches) { + // It doesn't match, so we end our search here. + return false; + } + + if (candidate_end && pattern_end) { + // We've reached the end of both candidate and pattern, so it matches. + return true; + } + + if (pattern_end != candidate_end) { + // One of them has ended, but the other hasn't, so it's not a match. + return false; + } + + // It matches; move on to the next part. + return r_matches_file(next_pattern, next_candidate); +} + /** * The recursive implementation of matches(). This returns true if the * pattern substring [pi, pend) matches the candidate substring [ci, cend), diff --git a/dtool/src/dtoolutil/globPattern.h b/dtool/src/dtoolutil/globPattern.h index 27dfb67777..af181691ab 100644 --- a/dtool/src/dtoolutil/globPattern.h +++ b/dtool/src/dtoolutil/globPattern.h @@ -52,6 +52,7 @@ PUBLISHED: MAKE_PROPERTY(nomatch_chars, get_nomatch_chars, set_nomatch_chars); INLINE bool matches(const std::string &candidate) const; + bool matches_file(Filename candidate) const; INLINE void output(std::ostream &out) const; @@ -74,6 +75,7 @@ private: int r_match_files(const Filename &prefix, const std::string &suffix, vector_string &results, const Filename &cwd); + bool r_matches_file(const std::string &suffix, const Filename &candidate) const; std::string _pattern; bool _case_sensitive; diff --git a/dtool/src/interrogate/interfaceMakerPythonNative.cxx b/dtool/src/interrogate/interfaceMakerPythonNative.cxx index 59575668d4..ca2ed7eb8d 100644 --- a/dtool/src/interrogate/interfaceMakerPythonNative.cxx +++ b/dtool/src/interrogate/interfaceMakerPythonNative.cxx @@ -1532,13 +1532,7 @@ write_module(ostream &out, ostream *out_h, InterrogateModuleDef *def) { << " nullptr, nullptr, nullptr, nullptr\n" << "};\n" << "\n" - << "#ifdef _WIN32\n" - << "extern \"C\" __declspec(dllexport) PyObject *PyInit_" << def->module_name << "();\n" - << "#elif __GNUC__ >= 4\n" - << "extern \"C\" __attribute__((visibility(\"default\"))) PyObject *PyInit_" << def->module_name << "();\n" - << "#else\n" - << "extern \"C\" PyObject *PyInit_" << def->module_name << "();\n" - << "#endif\n" + << "extern \"C\" EXPORT_CLASS PyObject *PyInit_" << def->module_name << "();\n" << "\n" << "PyObject *PyInit_" << def->module_name << "() {\n" << " LibraryDef *refs[] = {&" << def->library_name << "_moddef, nullptr};\n" @@ -1549,13 +1543,7 @@ write_module(ostream &out, ostream *out_h, InterrogateModuleDef *def) { << "\n" << "#else // Python 2 case\n" << "\n" - << "#ifdef _WIN32\n" - << "extern \"C\" __declspec(dllexport) void init" << def->module_name << "();\n" - << "#elif __GNUC__ >= 4\n" - << "extern \"C\" __attribute__((visibility(\"default\"))) void init" << def->module_name << "();\n" - << "#else\n" - << "extern \"C\" void init" << def->module_name << "();\n" - << "#endif\n" + << "extern \"C\" EXPORT_CLASS void init" << def->module_name << "();\n" << "\n" << "void init" << def->module_name << "() {\n" << " LibraryDef *refs[] = {&" << def->library_name << "_moddef, nullptr};\n" @@ -4078,13 +4066,9 @@ write_coerce_constructor(ostream &out, Object *obj, bool is_const) { /** * Special case optimization: if the last map is a subset of the map before - * it, and the last parameter is only a simple parameter type (that we have - * special default argument handling for), we can merge the cases. When this - * happens, we can make use of a special feature of PyArg_ParseTuple for - * handling of these last few default arguments. This doesn't work well for - * all types of default expressions, though, hence the need for this elaborate - * checking mechanism down here, which goes in parallel with the actual - * optional arg handling logic in write_function_instance. + * it, we can merge the cases. When this happens, we can make use of a + * special feature of PyArg_ParseTuple for handling of these last few default + * arguments. * * This isn't just to help reduce the amount of generated code; it also * enables arbitrary selection of keyword arguments for many functions, ie. @@ -4095,14 +4079,6 @@ write_coerce_constructor(ostream &out, Object *obj, bool is_const) { * Thanks to this mechanism, we can call it like so: * * func(c=True, d=".") - * - * The return value is the minimum of the number of maximum arguments. - * - * Sorry, let me try that again: it returns the largest number of arguments - * for which the overloads will be separated out rather than handled via the - * special default handling mechanism. Or something. - * - * Please don't hate me. */ int InterfaceMakerPythonNative:: collapse_default_remaps(std::map > &map_sets, @@ -4118,70 +4094,8 @@ collapse_default_remaps(std::map > &map_sets, if (std::includes(rmi_next->second.begin(), rmi_next->second.end(), rmi->second.begin(), rmi->second.end())) { - // Check if the nth argument is something we can easily create a default - // for. - std::set::iterator sii; - for (sii = rmi->second.begin(); sii != rmi->second.end(); ++sii) { - FunctionRemap *remap = (*sii); - size_t pn = (size_t)rmi->first; - if (!remap->_has_this || remap->_type == FunctionRemap::T_constructor) { - --pn; - } - nassertd(pn < remap->_parameters.size()) goto abort_iteration; - - ParameterRemap *param = remap->_parameters[pn]._remap; - CPPType *type = param->get_new_type(); - - if (param->new_type_is_atomic_string()) { - CPPType *orig_type = param->get_orig_type(); - if (TypeManager::is_char_pointer(orig_type)) { - } else if (TypeManager::is_wchar_pointer(orig_type)) { - goto abort_iteration; - } else if (TypeManager::is_wstring(orig_type)) { - goto abort_iteration; - } else if (TypeManager::is_const_ptr_to_basic_string_wchar(orig_type)) { - goto abort_iteration; - } else { - // Regular strings are OK if the default argument is a string - // literal or the default string constructor, since those are - // trivial to handle. This actually covers almost all of the - // cases of default string args. - CPPExpression::Type expr_type = param->get_default_value()->_type; - if (expr_type != CPPExpression::T_default_construct && - expr_type != CPPExpression::T_string) { - goto abort_iteration; - } - } - } else if (TypeManager::is_integer(type)) { - } else if (TypeManager::is_float(type)) { - } else if (TypeManager::is_const_char_pointer(type)) { - } else if (TypeManager::is_pointer_to_PyTypeObject(type)) { - } else if (TypeManager::is_pointer_to_PyStringObject(type)) { - } else if (TypeManager::is_pointer_to_PyUnicodeObject(type)) { - } else if (TypeManager::is_pointer_to_PyObject(type)) { - } else if (TypeManager::is_pointer_to_Py_buffer(type)) { - goto abort_iteration; - } else if (TypeManager::is_pointer_to_simple(type)) { - goto abort_iteration; - } else if (TypeManager::is_pointer(type)) { - // I'm allowing other pointer types, but only if the expression - // happens to evaluate to a numeric constant (which will likely only - // be NULL). There are too many issues to resolve right now with - // allowing more complex default expressions, including issues in - // the C++ parser (but the reader is welcome to give it a try!) - CPPExpression::Result res = param->get_default_value()->evaluate(); - if (res._type != CPPExpression::RT_integer && - res._type != CPPExpression::RT_pointer) { - goto abort_iteration; - } - } else { - goto abort_iteration; - } - } - // rmi_next has a superset of the remaps in rmi, and we are going to - // erase rmi_next, so put all the remaps in rmi. rmi->second = - // rmi_next->second; + // erase rmi_next, so put all the remaps in rmi. max_required_args = rmi_next->first; rmi = rmi_next; @@ -4191,7 +4105,6 @@ collapse_default_remaps(std::map > &map_sets, } } -abort_iteration: // Now erase the other remap sets. Reverse iterators are weird, we first // need to get forward iterators and decrement them by one. std::map >::iterator erase_begin, erase_end; @@ -4738,6 +4651,7 @@ write_function_instance(ostream &out, FunctionRemap *remap, "(" + orig_type->get_local_name(&parser) + ")" + param_name; string default_expr; + const char *null_assign = ""; if (is_optional) { // If this is an optional argument, PyArg_ParseTuple will leave the @@ -4747,6 +4661,7 @@ write_function_instance(ostream &out, FunctionRemap *remap, default_expr_str << " = "; default_value->output(default_expr_str, 0, &parser, false); default_expr = default_expr_str.str(); + null_assign = " = nullptr"; // We should only ever have to consider optional arguments for functions // taking a variable number of arguments. @@ -4776,23 +4691,40 @@ write_function_instance(ostream &out, FunctionRemap *remap, expected_params += "str"; } else if (TypeManager::is_wchar_pointer(orig_type)) { - indent(out, indent_level) << "#if PY_VERSION_HEX >= 0x03020000\n"; - indent(out, indent_level) << "PyObject *" << param_name << ";\n"; - indent(out, indent_level) << "#else\n"; - indent(out, indent_level) << "PyUnicodeObject *" << param_name << ";\n"; - indent(out, indent_level) << "#endif\n"; + out << "#if PY_VERSION_HEX >= 0x03020000\n"; + indent(out, indent_level) << "PyObject *" << param_name << null_assign << ";\n"; + out << "#else\n"; + indent(out, indent_level) << "PyUnicodeObject *" << param_name << null_assign << ";\n"; + out << "#endif\n"; format_specifiers += "U"; parameter_list += ", &" + param_name; - extra_convert - << "#if PY_VERSION_HEX >= 0x03030000\n" - << "wchar_t *" << param_name << "_str = PyUnicode_AsWideCharString(" << param_name << ", nullptr);\n" - << "#else" - << "Py_ssize_t " << param_name << "_len = PyUnicode_GET_SIZE(" << param_name << ");\n" - << "wchar_t *" << param_name << "_str = (wchar_t *)alloca(sizeof(wchar_t) * (" + param_name + "_len + 1));\n" - << "PyUnicode_AsWideChar(" << param_name << ", " << param_name << "_str, " << param_name << "_len);\n" - << param_name << "_str[" << param_name << "_len] = 0;\n" - << "#endif\n"; + if (is_optional) { + extra_convert + << "wchar_t *" << param_name << "_str;\n" + << "if (" << param_name << " != nullptr) {\n" + << "#if PY_VERSION_HEX >= 0x03030000\n" + << " " << param_name << "_str = PyUnicode_AsWideCharString(" << param_name << ", nullptr);\n" + << "#else" + << "Py_ssize_t " << param_name << "_len = PyUnicode_GET_SIZE(" << param_name << ");\n" + << " " << param_name << "_str = (wchar_t *)alloca(sizeof(wchar_t) * (" + param_name + "_len + 1));\n" + << "PyUnicode_AsWideChar(" << param_name << ", " << param_name << "_str, " << param_name << "_len);\n" + << param_name << "_str[" << param_name << "_len] = 0;\n" + << "#endif\n" + << "} else {\n" + << " " << param_name << "_str" << default_expr << ";\n" + << "}\n"; + } else { + extra_convert + << "#if PY_VERSION_HEX >= 0x03030000\n" + << "wchar_t *" << param_name << "_str = PyUnicode_AsWideCharString(" << param_name << ", nullptr);\n" + << "#else" + << "Py_ssize_t " << param_name << "_len = PyUnicode_GET_SIZE(" << param_name << ");\n" + << "wchar_t *" << param_name << "_str = (wchar_t *)alloca(sizeof(wchar_t) * (" + param_name + "_len + 1));\n" + << "PyUnicode_AsWideChar(" << param_name << ", " << param_name << "_str, " << param_name << "_len);\n" + << param_name << "_str[" << param_name << "_len] = 0;\n" + << "#endif\n"; + } pexpr_string = param_name + "_str"; @@ -4803,56 +4735,48 @@ write_function_instance(ostream &out, FunctionRemap *remap, expected_params += "unicode"; - } else if (TypeManager::is_wstring(orig_type)) { - indent(out, indent_level) << "#if PY_VERSION_HEX >= 0x03020000\n"; - indent(out, indent_level) << "PyObject *" << param_name << ";\n"; - indent(out, indent_level) << "#else\n"; - indent(out, indent_level) << "PyUnicodeObject *" << param_name << ";\n"; - indent(out, indent_level) << "#endif\n"; + } else if (TypeManager::is_wstring(orig_type) || + TypeManager::is_const_ptr_to_basic_string_wchar(orig_type)) { + out << "#if PY_VERSION_HEX >= 0x03020000\n"; + indent(out, indent_level) << "PyObject *" << param_name << null_assign << ";\n"; + out << "#else\n"; + indent(out, indent_level) << "PyUnicodeObject *" << param_name << null_assign << ";\n"; + out << "#endif\n"; format_specifiers += "U"; parameter_list += ", &" + param_name; - extra_convert - << "#if PY_VERSION_HEX >= 0x03030000\n" - << "Py_ssize_t " << param_name << "_len;\n" - << "wchar_t *" << param_name << "_str = PyUnicode_AsWideCharString(" - << param_name << ", &" << param_name << "_len);\n" - << "#else\n" - << "Py_ssize_t " << param_name << "_len = PyUnicode_GET_SIZE(" << param_name << ");\n" - << "wchar_t *" << param_name << "_str = (wchar_t *)alloca(sizeof(wchar_t) * (" + param_name + "_len + 1));\n" - << "PyUnicode_AsWideChar(" << param_name << ", " << param_name << "_str, " << param_name << "_len);\n" - << "#endif\n"; - - pexpr_string = param_name + "_str, " + param_name + "_len"; - - extra_cleanup - << "#if PY_VERSION_HEX >= 0x03030000\n" - << "PyMem_Free(" << param_name << "_str);\n" - << "#endif\n"; - - expected_params += "unicode"; - - } else if (TypeManager::is_const_ptr_to_basic_string_wchar(orig_type)) { - indent(out, indent_level) << "#if PY_VERSION_HEX >= 0x03020000\n"; - indent(out, indent_level) << "PyObject *" << param_name << ";\n"; - indent(out, indent_level) << "#else\n"; - indent(out, indent_level) << "PyUnicodeObject *" << param_name << ";\n"; - indent(out, indent_level) << "#endif\n"; - format_specifiers += "U"; - parameter_list += ", &" + param_name; - - extra_convert - << "#if PY_VERSION_HEX >= 0x03030000\n" - << "Py_ssize_t " << param_name << "_len;\n" - << "wchar_t *" << param_name << "_str = PyUnicode_AsWideCharString(" - << param_name << ", &" << param_name << "_len);\n" - << "#else\n" - << "Py_ssize_t " << param_name << "_len = PyUnicode_GET_SIZE(" << param_name << ");\n" - << "wchar_t *" << param_name << "_str = (wchar_t *)alloca(sizeof(wchar_t) * (" + param_name + "_len + 1));\n" - << "PyUnicode_AsWideChar(" << param_name << ", " << param_name << "_str, " << param_name << "_len);\n" - << "#endif\n"; - - pexpr_string = param_name + "_str, " + param_name + "_len"; + if (is_optional) { + extra_convert + << "Py_ssize_t " << param_name << "_len;\n" + << "wchar_t *" << param_name << "_str;\n" + << "std::wstring " << param_name << "_wstr;\n" + << "if (" << param_name << " != nullptr) {\n" + << "#if PY_VERSION_HEX >= 0x03030000\n" + << " " << param_name << "_str = PyUnicode_AsWideCharString(" + << param_name << ", &" << param_name << "_len);\n" + << "#else\n" + << " " << param_name << "_len = PyUnicode_GET_SIZE(" << param_name << ");\n" + << " " << param_name << "_str = (wchar_t *)alloca(sizeof(wchar_t) * (" + param_name + "_len + 1));\n" + << "PyUnicode_AsWideChar(" << param_name << ", " << param_name << "_str, " << param_name << "_len);\n" + << "#endif\n" + << " " << param_name << "_wstr.assign(" << param_name << "_str, " << param_name << "_len);\n" + << "} else {\n" + << " " << param_name << "_wstr" << default_expr << ";\n" + << "}\n"; + pexpr_string = "std::move(" + param_name + "_wstr)"; + } else { + extra_convert + << "#if PY_VERSION_HEX >= 0x03030000\n" + << "Py_ssize_t " << param_name << "_len;\n" + << "wchar_t *" << param_name << "_str = PyUnicode_AsWideCharString(" + << param_name << ", &" << param_name << "_len);\n" + << "#else\n" + << "Py_ssize_t " << param_name << "_len = PyUnicode_GET_SIZE(" << param_name << ");\n" + << "wchar_t *" << param_name << "_str = (wchar_t *)alloca(sizeof(wchar_t) * (" + param_name + "_len + 1));\n" + << "PyUnicode_AsWideChar(" << param_name << ", " << param_name << "_str, " << param_name << "_len);\n" + << "#endif\n"; + pexpr_string = param_name + "_str, " + param_name + "_len"; + } extra_cleanup << "#if PY_VERSION_HEX >= 0x03030000\n" @@ -5019,11 +4943,11 @@ write_function_instance(ostream &out, FunctionRemap *remap, only_pyobjects = false; } else if (TypeManager::is_wchar(type)) { - indent(out, indent_level) << "#if PY_VERSION_HEX >= 0x03020000\n"; + out << "#if PY_VERSION_HEX >= 0x03020000\n"; indent(out, indent_level) << "PyObject *" << param_name << ";\n"; - indent(out, indent_level) << "#else\n"; + out << "#else\n"; indent(out, indent_level) << "PyUnicodeObject *" << param_name << ";\n"; - indent(out, indent_level) << "#endif\n"; + out << "#endif\n"; format_specifiers += "U"; parameter_list += ", &" + param_name; @@ -5348,17 +5272,36 @@ write_function_instance(ostream &out, FunctionRemap *remap, if (args_type == AT_single_arg) { param_name = "arg"; } else { - indent(out, indent_level) << "PyObject *" << param_name << ";\n"; + indent(out, indent_level) << "PyObject *" << param_name << null_assign << ";\n"; format_specifiers += "O"; parameter_list += ", &" + param_name; } indent(out, indent_level) << "Py_buffer " << param_name << "_view;\n"; - extra_param_check << " && PyObject_GetBuffer(" - << param_name << ", &" - << param_name << "_view, PyBUF_FULL) == 0"; - pexpr_string = "&" + param_name + "_view"; - extra_cleanup << "PyBuffer_Release(&" << param_name << "_view);\n"; + if (is_optional) { + indent(out, indent_level) << "Py_buffer *" << param_name << "_viewp;\n"; + + extra_convert + << "bool " << param_name << "_success;\n" + << "if (" << param_name << " != nullptr) {\n" + << " " << param_name << "_success = (PyObject_GetBuffer(" + << param_name << ", &" << param_name << "_view, PyBUF_FULL) == 0);\n" + << " " << param_name << "_viewp = &" << param_name << "_view;\n" + << "} else {\n" + << " " << param_name << "_viewp" << default_expr << ";\n" + << " " << param_name << "_success = true;\n" + << "}\n"; + + extra_param_check << " && " << param_name << "_success"; + pexpr_string = param_name + "_viewp"; + extra_cleanup << "if (" << param_name << " != nullptr) PyBuffer_Release(&" << param_name << "_view);\n"; + } else { + extra_param_check << " && PyObject_GetBuffer(" + << param_name << ", &" + << param_name << "_view, PyBUF_FULL) == 0"; + pexpr_string = "&" + param_name + "_view"; + extra_cleanup << "PyBuffer_Release(&" << param_name << "_view);\n"; + } expected_params += "buffer"; may_raise_typeerror = true; clear_error = true; @@ -5367,7 +5310,7 @@ write_function_instance(ostream &out, FunctionRemap *remap, if (args_type == AT_single_arg) { param_name = "arg"; } else { - indent(out, indent_level) << "PyObject *" << param_name << ";\n"; + indent(out, indent_level) << "PyObject *" << param_name << null_assign << ";\n"; format_specifiers += "O"; parameter_list += ", &" + param_name; } @@ -5496,18 +5439,15 @@ write_function_instance(ostream &out, FunctionRemap *remap, if (args_type == AT_single_arg) { param_name = "arg"; } else { - indent(out, indent_level) << "PyObject *" << param_name; - if (is_optional) { - out << " = nullptr"; - } - out << ";\n"; + indent(out, indent_level) << "PyObject *" << param_name << null_assign << ";\n"; format_specifiers += "O"; parameter_list += ", &" + param_name; } // If the default value is NULL, we also accept a None value. bool maybe_none = false; - if (default_value != nullptr && (return_flags & RF_coerced) == 0) { + if (default_value != nullptr && (return_flags & RF_coerced) == 0 && + TypeManager::is_pointer(orig_type)) { CPPExpression::Result res = param->get_default_value()->evaluate(); if (res._type == CPPExpression::RT_integer || res._type == CPPExpression::RT_pointer) { @@ -5576,8 +5516,11 @@ write_function_instance(ostream &out, FunctionRemap *remap, << "if (" << param_name << " != nullptr && " << param_name << " != Py_None) {\n" << " " << param_name << "_this"; } else if (is_optional) { + if (TypeManager::is_pointer(orig_type)) { + extra_convert << default_expr; + } extra_convert - << default_expr << ";\n" + << ";\n" << "if (" << param_name << " != nullptr) {\n" << " " << param_name << "_this"; } else if (maybe_none) { @@ -5590,7 +5533,13 @@ write_function_instance(ostream &out, FunctionRemap *remap, extra_convert << " = Dtool_Coerce_" + make_safe_name(class_name) + "(" + param_name + ", " + param_name + "_local);\n"; - if (is_optional || maybe_none) { + if (is_optional && !TypeManager::is_pointer(orig_type)) { + extra_convert + << "} else {\n" + << " " << param_name << "_local" << default_expr << ";\n" + << " " << param_name << "_this = &" << param_name << "_local;\n" + << "}\n"; + } else if (is_optional || maybe_none) { extra_convert << "}\n"; } @@ -5645,21 +5594,60 @@ write_function_instance(ostream &out, FunctionRemap *remap, } else { // The regular, non-coercion case. type->output_instance(extra_convert, param_name + "_this", &parser); if (is_optional && maybe_none) { + // This parameter has a default value of nullptr, so we need to also + // allow passing in None. extra_convert << default_expr << ";\n" << "if (" << param_name << " != nullptr && " << param_name << " != Py_None) {\n" << " " << param_name << "_this"; - } else if (is_optional) { + } + else if (is_optional && !TypeManager::is_pointer(orig_type) && !default_value->is_lvalue()) { + // Most annoying case, where we have to use an rvalue reference to + // extend the lifetime of the default argument. In this case, the + // default expression is invoked even if not used. + extra_convert << ";\n"; + if (TypeManager::is_const_pointer_to_anything(type)) { + extra_convert << "const "; + obj_type->output_instance(extra_convert, "&" + param_name + "_ref", &parser); + } else { + obj_type->output_instance(extra_convert, "&&" + param_name + "_ref", &parser); + } extra_convert << default_expr << ";\n" - << "if (" << param_name << " != nullptr) {\n" + << "if (" << param_name << " == nullptr) {\n" + << " " << param_name << "_this = &" << param_name << "_ref;\n" + << "} else {\n" << " " << param_name << "_this"; - } else if (maybe_none) { + } + else if (is_optional) { + // General case where the default argument is either an lvalue or a + // pointer. + extra_convert + << ";\n" + << "if (" << param_name << " == nullptr) {\n" + << " " << param_name << "_this = "; + if (TypeManager::is_pointer(orig_type)) { + default_value->output(extra_convert, 0, &parser, false); + extra_convert << ";\n"; + } else { + // The rvalue case was handled above, so this is an lvalue, which + // means we can safely take a reference to it. + extra_convert << "&("; + default_value->output(extra_convert, 0, &parser, false); + extra_convert << ");\n"; + } + extra_convert + << "} else {\n" + << " " << param_name << "_this"; + } + else if (maybe_none) { + // No default argument, but we still need to check for None. extra_convert << " = nullptr;\n" << "if (" << param_name << " != Py_None) {\n" << " " << param_name << "_this"; } + if (const_ok && !report_errors) { // This function does the same thing in this case and is slightly // simpler. But maybe we should just reorganize these functions diff --git a/dtool/src/interrogate/interrogate.cxx b/dtool/src/interrogate/interrogate.cxx index ef06833324..e3cab85a6b 100644 --- a/dtool/src/interrogate/interrogate.cxx +++ b/dtool/src/interrogate/interrogate.cxx @@ -312,7 +312,10 @@ main(int argc, char **argv) { string command_line; int i; for (i = 0; i < argc; i++) { - command_line += string(argv[i]) + " "; + if (i > 0) { + command_line += ' '; + } + command_line += string(argv[i]); } Filename fn; diff --git a/dtool/src/interrogate/interrogateBuilder.cxx b/dtool/src/interrogate/interrogateBuilder.cxx index b740d59e1a..7473d085d3 100644 --- a/dtool/src/interrogate/interrogateBuilder.cxx +++ b/dtool/src/interrogate/interrogateBuilder.cxx @@ -656,13 +656,13 @@ get_preferred_name(CPPType *type) { */ string InterrogateBuilder:: hash_string(const string &name, int shift_offset) { - int hash = 0; + unsigned int hash = 0; - int shift = 0; + unsigned int shift = 0; string::const_iterator ni; for (ni = name.begin(); ni != name.end(); ++ni) { - int c = (int)(unsigned char)(*ni); - int shifted_c = (c << shift) & 0xffffff; + unsigned int c = (unsigned char)*ni; + unsigned int shifted_c = (c << shift) & 0xffffff; if (shift > 16) { // We actually want a circular shift, not an arithmetic shift. shifted_c |= ((c >> (24 - shift)) & 0xff) ; @@ -675,10 +675,9 @@ hash_string(const string &name, int shift_offset) { // bits back at the bottom, to scramble up the bits a bit. This helps // reduce hash conflicts from names that are similar to each other, by // separating adjacent hash codes. - int prime = 4999; - int low_order = (hash * prime) & 0xffffff; - int high_order = (int)((double)hash * (double)prime / (double)(1 << 24)); - hash = low_order ^ high_order; + const unsigned int prime = 4999; + unsigned long long product = (unsigned long long)hash * prime; + hash = (product ^ (product >> 24)) & 0xffffff; // Also add in the additional_number, times some prime factor. hash = (hash // + additional_number * 1657) & 0xffffff; @@ -690,10 +689,9 @@ hash_string(const string &name, int shift_offset) { // deal, since we have to resolve hash conflicts anyway. string result; - int extract_h = hash; for (int i = 0; i < 4; i++) { - int value = (extract_h & 0x3f); - extract_h >>= 6; + unsigned int value = (hash & 0x3f); + hash >>= 6; if (value < 26) { result += (char)('A' + value); @@ -2024,6 +2022,7 @@ get_make_property(CPPMakeProperty *make_property, CPPStructType *struct_type, CP if (iproperty._type != 0 && iproperty._type != return_index) { cerr << "Property " << property_name << " has inconsistent element type!\n"; } + iproperty._type = return_index; } else { iproperty._type = 0; } diff --git a/dtool/src/interrogate/interrogate_module.cxx b/dtool/src/interrogate/interrogate_module.cxx index 6f7bc4bfeb..663e57762f 100644 --- a/dtool/src/interrogate/interrogate_module.cxx +++ b/dtool/src/interrogate/interrogate_module.cxx @@ -295,24 +295,10 @@ int write_python_table_native(std::ostream &out) { out.put('\n'); - out << "#if PY_MAJOR_VERSION >= 3 || !defined(NDEBUG)\n" - << "#ifdef _WIN32\n" - << "extern \"C\" __declspec(dllexport) PyObject *PyInit_" << library_name << "();\n" - << "#elif __GNUC__ >= 4\n" - << "extern \"C\" __attribute__((visibility(\"default\"))) PyObject *PyInit_" << library_name << "();\n" + out << "#if PY_MAJOR_VERSION >= 3\n" + << "extern \"C\" EXPORT_CLASS PyObject *PyInit_" << library_name << "();\n" << "#else\n" - << "extern \"C\" PyObject *PyInit_" << library_name << "();\n" - << "#endif\n" - << "#endif\n"; - - out << "#if PY_MAJOR_VERSION < 3 || !defined(NDEBUG)\n" - << "#ifdef _WIN32\n" - << "extern \"C\" __declspec(dllexport) void init" << library_name << "();\n" - << "#elif __GNUC__ >= 4\n" - << "extern \"C\" __attribute__((visibility(\"default\"))) void init" << library_name << "();\n" - << "#else\n" - << "extern \"C\" void init" << library_name << "();\n" - << "#endif\n" + << "extern \"C\" EXPORT_CLASS void init" << library_name << "();\n" << "#endif\n"; out << "\n" @@ -361,14 +347,6 @@ int write_python_table_native(std::ostream &out) { << "}\n" << "\n" - << "#ifndef NDEBUG\n" - << "void init" << library_name << "() {\n" - << " PyErr_SetString(PyExc_ImportError, \"" << module_name << " was " - << "compiled for Python \" PY_VERSION \", which is incompatible " - << "with Python 2\");\n" - << "}\n" - << "#endif\n" - << "#else // Python 2 case\n" << "\n" << "void init" << library_name << "() {\n"; @@ -402,15 +380,6 @@ int write_python_table_native(std::ostream &out) { out << " }\n" << "}\n" - << "\n" - << "#ifndef NDEBUG\n" - << "PyObject *PyInit_" << library_name << "() {\n" - << " PyErr_SetString(PyExc_ImportError, \"" << module_name << " was " - << "compiled for Python \" PY_VERSION \", which is incompatible " - << "with Python 3\");\n" - << " return nullptr;\n" - << "}\n" - << "#endif\n" << "#endif\n" << "\n"; diff --git a/dtool/src/interrogatedb/dtool_super_base.cxx b/dtool/src/interrogatedb/dtool_super_base.cxx index 01dcdef077..9422084299 100644 --- a/dtool/src/interrogatedb/dtool_super_base.cxx +++ b/dtool/src/interrogatedb/dtool_super_base.cxx @@ -15,6 +15,16 @@ #ifdef HAVE_PYTHON +static PyMemberDef standard_type_members[] = { + {(char *)"this", (sizeof(void*) == sizeof(int)) ? T_UINT : T_ULONGLONG, offsetof(Dtool_PyInstDef, _ptr_to_object), READONLY, (char *)"C++ 'this' pointer, if any"}, + {(char *)"this_ownership", T_BOOL, offsetof(Dtool_PyInstDef, _memory_rules), READONLY, (char *)"C++ 'this' ownership rules"}, + {(char *)"this_const", T_BOOL, offsetof(Dtool_PyInstDef, _is_const), READONLY, (char *)"C++ 'this' const flag"}, +// {(char *)"this_signature", T_INT, offsetof(Dtool_PyInstDef, _signature), +// READONLY, (char *)"A type check signature"}, + {(char *)"this_metatype", T_OBJECT, offsetof(Dtool_PyInstDef, _My_Type), READONLY, (char *)"The dtool meta object"}, + {nullptr} /* Sentinel */ +}; + static PyObject *GetSuperBase(PyObject *self) { Dtool_PyTypedObject *super_base = Dtool_GetSuperBase(); Py_XINCREF((PyTypeObject *)super_base); // order is important .. this is used for static functions diff --git a/dtool/src/interrogatedb/interrogate_interface.cxx b/dtool/src/interrogatedb/interrogate_interface.cxx index 2cd08d4adf..3fc2053855 100644 --- a/dtool/src/interrogatedb/interrogate_interface.cxx +++ b/dtool/src/interrogatedb/interrogate_interface.cxx @@ -176,6 +176,18 @@ interrogate_element_setter(ElementIndex element) { return InterrogateDatabase::get_ptr()->get_element(element).get_setter(); } +bool +interrogate_element_is_sequence(ElementIndex element) { + // cerr << "interrogate_element_is_sequence(" << element << ")\n"; + return InterrogateDatabase::get_ptr()->get_element(element).is_sequence(); +} + +bool +interrogate_element_is_mapping(ElementIndex element) { + // cerr << "interrogate_element_is_mapping(" << element << ")\n"; + return InterrogateDatabase::get_ptr()->get_element(element).is_mapping(); +} + int interrogate_number_of_globals() { // cerr << "interrogate_number_of_globals()\n"; diff --git a/dtool/src/interrogatedb/interrogate_interface.h b/dtool/src/interrogatedb/interrogate_interface.h index 6c434d7833..cc88af041d 100644 --- a/dtool/src/interrogatedb/interrogate_interface.h +++ b/dtool/src/interrogatedb/interrogate_interface.h @@ -153,6 +153,9 @@ EXPCL_INTERROGATEDB FunctionIndex interrogate_element_getter(ElementIndex elemen EXPCL_INTERROGATEDB bool interrogate_element_has_setter(ElementIndex element); EXPCL_INTERROGATEDB FunctionIndex interrogate_element_setter(ElementIndex element); +EXPCL_INTERROGATEDB bool interrogate_element_is_sequence(ElementIndex element); +EXPCL_INTERROGATEDB bool interrogate_element_is_mapping(ElementIndex element); + // Global Data // This is the list of global data elements. diff --git a/dtool/src/interrogatedb/py_compat.h b/dtool/src/interrogatedb/py_compat.h index 4872faedc7..11771d1948 100644 --- a/dtool/src/interrogatedb/py_compat.h +++ b/dtool/src/interrogatedb/py_compat.h @@ -31,6 +31,14 @@ #include +#ifndef LINK_ALL_STATIC +# define EXPCL_PYPANDA +#elif defined(__GNUC__) +# define EXPCL_PYPANDA __attribute__((weak)) +#else +# define EXPCL_PYPANDA extern inline +#endif + /* Python 2.4 */ // 2.4 macros which aren't available in 2.3 @@ -99,7 +107,7 @@ typedef int Py_ssize_t; // PyInt_FromSize_t automatically picks the right type. # define PyLongOrInt_AS_LONG PyInt_AsLong -size_t PyLongOrInt_AsSize_t(PyObject *); +EXPCL_PYPANDA size_t PyLongOrInt_AsSize_t(PyObject *); #endif // Which character to use in PyArg_ParseTuple et al for a byte string. diff --git a/dtool/src/interrogatedb/py_panda.cxx b/dtool/src/interrogatedb/py_panda.cxx index bb7b5717f4..5d17d8851f 100644 --- a/dtool/src/interrogatedb/py_panda.cxx +++ b/dtool/src/interrogatedb/py_panda.cxx @@ -12,16 +12,6 @@ using std::string; -PyMemberDef standard_type_members[] = { - {(char *)"this", (sizeof(void*) == sizeof(int)) ? T_UINT : T_ULONGLONG, offsetof(Dtool_PyInstDef, _ptr_to_object), READONLY, (char *)"C++ 'this' pointer, if any"}, - {(char *)"this_ownership", T_BOOL, offsetof(Dtool_PyInstDef, _memory_rules), READONLY, (char *)"C++ 'this' ownership rules"}, - {(char *)"this_const", T_BOOL, offsetof(Dtool_PyInstDef, _is_const), READONLY, (char *)"C++ 'this' const flag"}, -// {(char *)"this_signature", T_INT, offsetof(Dtool_PyInstDef, _signature), -// READONLY, (char *)"A type check signature"}, - {(char *)"this_metatype", T_OBJECT, offsetof(Dtool_PyInstDef, _My_Type), READONLY, (char *)"The dtool meta object"}, - {nullptr} /* Sentinel */ -}; - /** */ @@ -296,6 +286,43 @@ PyObject *_Dtool_Return(PyObject *value) { } #if PY_VERSION_HEX < 0x03040000 +/** + * This function converts an int value to the appropriate enum instance. + */ +static PyObject *Dtool_EnumType_New(PyTypeObject *subtype, PyObject *args, PyObject *kwds) { + PyObject *arg; + if (!Dtool_ExtractArg(&arg, args, kwds, "value")) { + return PyErr_Format(PyExc_TypeError, + "%s() missing 1 required argument: 'value'", + subtype->tp_name); + } + + if (Py_TYPE(arg) == subtype) { + Py_INCREF(arg); + return arg; + } + + PyObject *value2member = PyDict_GetItemString(subtype->tp_dict, "_value2member_map_"); + nassertr_always(value2member != nullptr, nullptr); + + PyObject *member = PyDict_GetItem(value2member, arg); + if (member != nullptr) { + Py_INCREF(member); + return member; + } + + PyObject *repr = PyObject_Repr(arg); + PyErr_Format(PyExc_ValueError, "%s is not a valid %s", +#if PY_MAJOR_VERSION >= 3 + PyUnicode_AS_STRING(repr), +#else + PyString_AS_STRING(repr), +#endif + subtype->tp_name); + Py_DECREF(repr); + return nullptr; +} + static PyObject *Dtool_EnumType_Str(PyObject *self) { PyObject *name = PyObject_GetAttrString(self, "name"); #if PY_MAJOR_VERSION >= 3 @@ -347,6 +374,7 @@ PyTypeObject *Dtool_EnumType_Create(const char *name, PyObject *names, const cha static PyObject *name_sunder_str; static PyObject *value_str; static PyObject *value_sunder_str; + static PyObject *value2member_map_sunder_str; // Emulate something vaguely like the enum module. if (enum_class == nullptr) { #if PY_MAJOR_VERSION >= 3 @@ -354,11 +382,13 @@ PyTypeObject *Dtool_EnumType_Create(const char *name, PyObject *names, const cha value_str = PyUnicode_InternFromString("value"); name_sunder_str = PyUnicode_InternFromString("_name_"); value_sunder_str = PyUnicode_InternFromString("_value_"); + value2member_map_sunder_str = PyUnicode_InternFromString("_value2member_map_"); #else name_str = PyString_InternFromString("name"); value_str = PyString_InternFromString("value"); name_sunder_str = PyString_InternFromString("_name_"); value_sunder_str = PyString_InternFromString("_value_"); + value2member_map_sunder_str = PyString_InternFromString("_value2member_map_"); #endif PyObject *name_value_tuple = PyTuple_New(4); PyTuple_SET_ITEM(name_value_tuple, 0, name_str); @@ -375,27 +405,39 @@ PyTypeObject *Dtool_EnumType_Create(const char *name, PyObject *names, const cha enum_class = PyObject_CallFunction((PyObject *)&PyType_Type, (char *)"s()N", "Enum", slots_dict); nassertr(enum_class != nullptr, nullptr); } - PyObject *result = PyObject_CallFunction((PyObject *)&PyType_Type, (char *)"s(O)N", name, enum_class, PyDict_New()); + + // Create a subclass of this generic Enum class we just created. + PyObject *value2member = PyDict_New(); + PyObject *dict = PyDict_New(); + PyDict_SetItem(dict, value2member_map_sunder_str, value2member); + PyObject *result = PyObject_CallFunction((PyObject *)&PyType_Type, (char *)"s(O)N", name, enum_class, dict); nassertr(result != nullptr, nullptr); + ((PyTypeObject *)result)->tp_new = Dtool_EnumType_New; ((PyTypeObject *)result)->tp_str = Dtool_EnumType_Str; ((PyTypeObject *)result)->tp_repr = Dtool_EnumType_Repr; - // Copy the names as instances of the above to the class dict. + PyObject *empty_tuple = PyTuple_New(0); + + // Copy the names as instances of the above to the class dict, and create a + // reverse mapping in the _value2member_map_ dict. Py_ssize_t size = PyTuple_GET_SIZE(names); for (Py_ssize_t i = 0; i < size; ++i) { PyObject *item = PyTuple_GET_ITEM(names, i); PyObject *name = PyTuple_GET_ITEM(item, 0); PyObject *value = PyTuple_GET_ITEM(item, 1); - PyObject *member = _PyObject_CallNoArg(result); + PyObject *member = PyType_GenericNew((PyTypeObject *)result, empty_tuple, nullptr); PyObject_SetAttr(member, name_str, name); PyObject_SetAttr(member, name_sunder_str, name); PyObject_SetAttr(member, value_str, value); PyObject_SetAttr(member, value_sunder_str, value); PyObject_SetAttr(result, name, member); + PyDict_SetItem(value2member, value, member); Py_DECREF(member); } Py_DECREF(names); + Py_DECREF(value2member); + Py_DECREF(empty_tuple); #endif if (module != nullptr) { @@ -588,46 +630,48 @@ PyObject *Dtool_PyModuleInitHelper(const LibraryDef *defs[], const char *modulen << "Python " << version << "\n"; } - // Grab the __main__ module. - PyObject *main_module = PyImport_ImportModule("__main__"); - if (main_module == nullptr) { - interrogatedb_cat.warning() << "Unable to import __main__\n"; - } - - // Extract the __file__ attribute, if present. - Filename main_dir; - PyObject *file_attr = nullptr; - if (main_module != nullptr) { - file_attr = PyObject_GetAttrString(main_module, "__file__"); - } - if (file_attr == nullptr) { - // Must be running in the interactive interpreter. Use the CWD. - main_dir = ExecutionEnvironment::get_cwd(); - } else { + if (!ExecutionEnvironment::has_environment_variable("MAIN_DIR")) { + // Grab the __main__ module. + PyObject *main_module = PyImport_ImportModule("__main__"); + if (main_module == NULL) { + interrogatedb_cat.warning() << "Unable to import __main__\n"; + } + + // Extract the __file__ attribute, if present. + Filename main_dir; + PyObject *file_attr = nullptr; + if (main_module != nullptr) { + file_attr = PyObject_GetAttrString(main_module, "__file__"); + } + if (file_attr == nullptr) { + // Must be running in the interactive interpreter. Use the CWD. + main_dir = ExecutionEnvironment::get_cwd(); + } else { #if PY_MAJOR_VERSION >= 3 - Py_ssize_t length; - wchar_t *buffer = PyUnicode_AsWideCharString(file_attr, &length); - if (buffer != nullptr) { - main_dir = Filename::from_os_specific_w(std::wstring(buffer, length)); - main_dir.make_absolute(); - main_dir = main_dir.get_dirname(); - PyMem_Free(buffer); - } + Py_ssize_t length; + wchar_t *buffer = PyUnicode_AsWideCharString(file_attr, &length); + if (buffer != nullptr) { + main_dir = Filename::from_os_specific_w(std::wstring(buffer, length)); + main_dir.make_absolute(); + main_dir = main_dir.get_dirname(); + PyMem_Free(buffer); + } #else - char *buffer; - Py_ssize_t length; - if (PyString_AsStringAndSize(file_attr, &buffer, &length) != -1) { - main_dir = Filename::from_os_specific(std::string(buffer, length)); - main_dir.make_absolute(); - main_dir = main_dir.get_dirname(); - } + char *buffer; + Py_ssize_t length; + if (PyString_AsStringAndSize(file_attr, &buffer, &length) != -1) { + main_dir = Filename::from_os_specific(std::string(buffer, length)); + main_dir.make_absolute(); + main_dir = main_dir.get_dirname(); + } #endif - else { - interrogatedb_cat.warning() << "Invalid string for __main__.__file__\n"; + else { + interrogatedb_cat.warning() << "Invalid string for __main__.__file__\n"; + } } + ExecutionEnvironment::shadow_environment_variable("MAIN_DIR", main_dir.to_os_specific()); + PyErr_Clear(); } - ExecutionEnvironment::shadow_environment_variable("MAIN_DIR", main_dir.to_os_specific()); - PyErr_Clear(); initialized_main_dir = true; // Also, while we are at it, initialize the thread swap hook. @@ -673,7 +717,8 @@ PyObject *Dtool_BorrowThisReference(PyObject *self, PyObject *args) { // We do expose a dictionay for dtool classes .. this should be removed at // some point.. -PyObject *Dtool_AddToDictionary(PyObject *self1, PyObject *args) { +EXPCL_PYPANDA PyObject * +Dtool_AddToDictionary(PyObject *self1, PyObject *args) { PyObject *self; PyObject *subject; PyObject *key; diff --git a/dtool/src/interrogatedb/py_panda.h b/dtool/src/interrogatedb/py_panda.h index fa7981697b..353af53608 100644 --- a/dtool/src/interrogatedb/py_panda.h +++ b/dtool/src/interrogatedb/py_panda.h @@ -67,9 +67,6 @@ struct Dtool_PyInstDef { bool _is_const; }; -// A Offset Dictionary Defining How to read the Above Object.. -extern PyMemberDef standard_type_members[]; - // The Class Definition Structor For a Dtool python type. struct Dtool_PyTypedObject { // Standard Python Features.. @@ -182,19 +179,19 @@ static void Dtool_FreeInstance_##CLASS_NAME(PyObject *self) {\ typedef std::map Dtool_TypeMap; -Dtool_TypeMap *Dtool_GetGlobalTypeMap(); +EXPCL_PYPANDA Dtool_TypeMap *Dtool_GetGlobalTypeMap(); /** */ -void DTOOL_Call_ExtractThisPointerForType(PyObject *self, Dtool_PyTypedObject *classdef, void **answer); +EXPCL_PYPANDA void DTOOL_Call_ExtractThisPointerForType(PyObject *self, Dtool_PyTypedObject *classdef, void **answer); -void *DTOOL_Call_GetPointerThisClass(PyObject *self, Dtool_PyTypedObject *classdef, int param, const std::string &function_name, bool const_ok, bool report_errors); +EXPCL_PYPANDA void *DTOOL_Call_GetPointerThisClass(PyObject *self, Dtool_PyTypedObject *classdef, int param, const std::string &function_name, bool const_ok, bool report_errors); -bool Dtool_Call_ExtractThisPointer(PyObject *self, Dtool_PyTypedObject &classdef, void **answer); +EXPCL_PYPANDA bool Dtool_Call_ExtractThisPointer(PyObject *self, Dtool_PyTypedObject &classdef, void **answer); -bool Dtool_Call_ExtractThisPointer_NonConst(PyObject *self, Dtool_PyTypedObject &classdef, - void **answer, const char *method_name); +EXPCL_PYPANDA bool Dtool_Call_ExtractThisPointer_NonConst(PyObject *self, Dtool_PyTypedObject &classdef, + void **answer, const char *method_name); template INLINE bool DtoolInstance_GetPointer(PyObject *self, T *&into); template INLINE bool DtoolInstance_GetPointer(PyObject *self, T *&into, Dtool_PyTypedObject &classdef); @@ -204,7 +201,7 @@ INLINE int DtoolInstance_ComparePointers(PyObject *v1, PyObject *v2); INLINE PyObject *DtoolInstance_RichComparePointers(PyObject *v1, PyObject *v2, int op); // Functions related to error reporting. -bool _Dtool_CheckErrorOccurred(); +EXPCL_PYPANDA bool _Dtool_CheckErrorOccurred(); #ifdef NDEBUG #define Dtool_CheckErrorOccurred() (UNLIKELY(_PyErr_OCCURRED() != nullptr)) @@ -212,12 +209,12 @@ bool _Dtool_CheckErrorOccurred(); #define Dtool_CheckErrorOccurred() (UNLIKELY(_Dtool_CheckErrorOccurred())) #endif -PyObject *Dtool_Raise_AssertionError(); -PyObject *Dtool_Raise_TypeError(const char *message); -PyObject *Dtool_Raise_ArgTypeError(PyObject *obj, int param, const char *function_name, const char *type_name); -PyObject *Dtool_Raise_AttributeError(PyObject *obj, const char *attribute); +EXPCL_PYPANDA PyObject *Dtool_Raise_AssertionError(); +EXPCL_PYPANDA PyObject *Dtool_Raise_TypeError(const char *message); +EXPCL_PYPANDA PyObject *Dtool_Raise_ArgTypeError(PyObject *obj, int param, const char *function_name, const char *type_name); +EXPCL_PYPANDA PyObject *Dtool_Raise_AttributeError(PyObject *obj, const char *attribute); -PyObject *_Dtool_Raise_BadArgumentsError(); +EXPCL_PYPANDA PyObject *_Dtool_Raise_BadArgumentsError(); #ifdef NDEBUG // Define it to a function that just prints a generic message. #define Dtool_Raise_BadArgumentsError(x) _Dtool_Raise_BadArgumentsError() @@ -229,9 +226,9 @@ PyObject *_Dtool_Raise_BadArgumentsError(); // These functions are similar to Dtool_WrapValue, except that they also // contain code for checking assertions and exceptions when compiling with // NDEBUG mode on. -PyObject *_Dtool_Return_None(); -PyObject *Dtool_Return_Bool(bool value); -PyObject *_Dtool_Return(PyObject *value); +EXPCL_PYPANDA PyObject *_Dtool_Return_None(); +EXPCL_PYPANDA PyObject *Dtool_Return_Bool(bool value); +EXPCL_PYPANDA PyObject *_Dtool_Return(PyObject *value); #ifdef NDEBUG #define Dtool_Return_None() (LIKELY(_PyErr_OCCURRED() == nullptr) ? (Py_INCREF(Py_None), Py_None) : nullptr) @@ -244,19 +241,19 @@ PyObject *_Dtool_Return(PyObject *value); /** * Wrapper around Python 3.4's enum library, which does not have a C API. */ -PyTypeObject *Dtool_EnumType_Create(const char *name, PyObject *names, - const char *module = nullptr); +EXPCL_PYPANDA PyTypeObject *Dtool_EnumType_Create(const char *name, PyObject *names, + const char *module = nullptr); INLINE long Dtool_EnumValue_AsLong(PyObject *value); /** */ -PyObject *DTool_CreatePyInstanceTyped(void *local_this_in, Dtool_PyTypedObject &known_class_type, bool memory_rules, bool is_const, int RunTimeType); +EXPCL_PYPANDA PyObject *DTool_CreatePyInstanceTyped(void *local_this_in, Dtool_PyTypedObject &known_class_type, bool memory_rules, bool is_const, int RunTimeType); // DTool_CreatePyInstance .. wrapper function to finalize the existance of a // general dtool py instance.. -PyObject *DTool_CreatePyInstance(void *local_this, Dtool_PyTypedObject &in_classdef, bool memory_rules, bool is_const); +EXPCL_PYPANDA PyObject *DTool_CreatePyInstance(void *local_this, Dtool_PyTypedObject &in_classdef, bool memory_rules, bool is_const); // These template methods allow use when the Dtool_PyTypedObject is not known. // They require a get_class_type() to be defined for the class. @@ -323,26 +320,26 @@ struct LibraryDef { }; #if PY_MAJOR_VERSION >= 3 -PyObject *Dtool_PyModuleInitHelper(const LibraryDef *defs[], PyModuleDef *module_def); +EXPCL_PYPANDA PyObject *Dtool_PyModuleInitHelper(const LibraryDef *defs[], PyModuleDef *module_def); #else -PyObject *Dtool_PyModuleInitHelper(const LibraryDef *defs[], const char *modulename); +EXPCL_PYPANDA PyObject *Dtool_PyModuleInitHelper(const LibraryDef *defs[], const char *modulename); #endif // HACK.... Be carefull Dtool_BorrowThisReference This function can be used to // grab the "THIS" pointer from an object and use it Required to support fom // historical inharatence in the for of "is this instance of".. -PyObject *Dtool_BorrowThisReference(PyObject *self, PyObject *args); +EXPCL_PYPANDA PyObject *Dtool_BorrowThisReference(PyObject *self, PyObject *args); #define DTOOL_PyObject_HashPointer DtoolInstance_HashPointer #define DTOOL_PyObject_ComparePointers DtoolInstance_ComparePointers -PyObject * +EXPCL_PYPANDA PyObject * copy_from_make_copy(PyObject *self, PyObject *noargs); -PyObject * +EXPCL_PYPANDA PyObject * copy_from_copy_constructor(PyObject *self, PyObject *noargs); -PyObject * +EXPCL_PYPANDA PyObject * map_deepcopy_to_copy(PyObject *self, PyObject *args); /** @@ -351,14 +348,14 @@ map_deepcopy_to_copy(PyObject *self, PyObject *args); */ ALWAYS_INLINE bool Dtool_CheckNoArgs(PyObject *args); ALWAYS_INLINE bool Dtool_CheckNoArgs(PyObject *args, PyObject *kwds); -bool Dtool_ExtractArg(PyObject **result, PyObject *args, - PyObject *kwds, const char *keyword); -bool Dtool_ExtractArg(PyObject **result, PyObject *args, - PyObject *kwds); -bool Dtool_ExtractOptionalArg(PyObject **result, PyObject *args, - PyObject *kwds, const char *keyword); -bool Dtool_ExtractOptionalArg(PyObject **result, PyObject *args, - PyObject *kwds); +EXPCL_PYPANDA bool Dtool_ExtractArg(PyObject **result, PyObject *args, + PyObject *kwds, const char *keyword); +EXPCL_PYPANDA bool Dtool_ExtractArg(PyObject **result, PyObject *args, + PyObject *kwds); +EXPCL_PYPANDA bool Dtool_ExtractOptionalArg(PyObject **result, PyObject *args, + PyObject *kwds, const char *keyword); +EXPCL_PYPANDA bool Dtool_ExtractOptionalArg(PyObject **result, PyObject *args, + PyObject *kwds); /** * These functions convert a C++ value into the corresponding Python object. @@ -393,7 +390,7 @@ ALWAYS_INLINE PyObject *Dtool_WrapValue(Py_buffer *value); template ALWAYS_INLINE PyObject *Dtool_WrapValue(const std::pair &value); -Dtool_PyTypedObject *Dtool_GetSuperBase(); +EXPCL_PYPANDA Dtool_PyTypedObject *Dtool_GetSuperBase(); #include "py_panda.I" diff --git a/dtool/src/interrogatedb/py_wrappers.h b/dtool/src/interrogatedb/py_wrappers.h index 96375d3598..eb5658cb6b 100644 --- a/dtool/src/interrogatedb/py_wrappers.h +++ b/dtool/src/interrogatedb/py_wrappers.h @@ -49,12 +49,12 @@ struct Dtool_GeneratorWrapper { iternextfunc _iternext_func; }; -Dtool_SequenceWrapper *Dtool_NewSequenceWrapper(PyObject *self, const char *name); -Dtool_MutableSequenceWrapper *Dtool_NewMutableSequenceWrapper(PyObject *self, const char *name); -Dtool_MappingWrapper *Dtool_NewMappingWrapper(PyObject *self, const char *name); -Dtool_MappingWrapper *Dtool_NewMutableMappingWrapper(PyObject *self, const char *name); -PyObject *Dtool_NewGenerator(PyObject *self, iternextfunc func); -PyObject *Dtool_NewStaticProperty(PyTypeObject *obj, const PyGetSetDef *getset); +EXPCL_PYPANDA Dtool_SequenceWrapper *Dtool_NewSequenceWrapper(PyObject *self, const char *name); +EXPCL_PYPANDA Dtool_MutableSequenceWrapper *Dtool_NewMutableSequenceWrapper(PyObject *self, const char *name); +EXPCL_PYPANDA Dtool_MappingWrapper *Dtool_NewMappingWrapper(PyObject *self, const char *name); +EXPCL_PYPANDA Dtool_MappingWrapper *Dtool_NewMutableMappingWrapper(PyObject *self, const char *name); +EXPCL_PYPANDA PyObject *Dtool_NewGenerator(PyObject *self, iternextfunc func); +EXPCL_PYPANDA PyObject *Dtool_NewStaticProperty(PyTypeObject *obj, const PyGetSetDef *getset); #endif // HAVE_PYTHON diff --git a/dtool/src/parser-inc/XInput.h b/dtool/src/parser-inc/XInput.h new file mode 100644 index 0000000000..0674961fc0 --- /dev/null +++ b/dtool/src/parser-inc/XInput.h @@ -0,0 +1,5 @@ +typedef struct _XINPUT_STATE XINPUT_STATE, *PXINPUT_STATE; +typedef struct _XINPUT_CAPABILITIES XINPUT_CAPABILITIES, *PXINPUT_CAPABILITIES; +typedef struct _XINPUT_VIBRATION XINPUT_VIBRATION, *PXINPUT_VIBRATION; +typedef struct _XINPUT_GAMEPAD XINPUT_GAMEPAD, *PXINPUT_GAMEPAD; +typedef struct _XINPUT_KEYSTROKE XINPUT_KEYSTROKE, *PXINPUT_KEYSTROKE; diff --git a/dtool/src/parser-inc/windows.h b/dtool/src/parser-inc/windows.h index 81343a9744..79c20acefb 100644 --- a/dtool/src/parser-inc/windows.h +++ b/dtool/src/parser-inc/windows.h @@ -38,8 +38,10 @@ typedef unsigned long ULONG_PTR; // http://msdn.microsoft.com/en-us/library/cc230309.aspx typedef bool BOOL; typedef unsigned long DWORD; +typedef unsigned short WORD; typedef long LONG; typedef long UINT; +typedef unsigned char BYTE; typedef unsigned long ULONG; typedef long long LONGLONG; typedef long HRESULT; diff --git a/dtool/src/prc/configPageManager.cxx b/dtool/src/prc/configPageManager.cxx index 18f8b12784..72e00f0189 100644 --- a/dtool/src/prc/configPageManager.cxx +++ b/dtool/src/prc/configPageManager.cxx @@ -38,6 +38,10 @@ #include #include +#ifndef _MSC_VER +#include +#endif + using std::string; ConfigPageManager *ConfigPageManager::_global_ptr = nullptr; @@ -93,11 +97,60 @@ reload_implicit_pages() { } _implicit_pages.clear(); + // If we are running inside a deployed application, see if it exposes + // information about how the PRC data should be initialized. + struct BlobInfo { + uint64_t blob_offset; + uint64_t blob_size; + uint16_t version; + uint16_t num_pointers; + uint16_t codepage; + uint16_t flags; + uint64_t reserved; + const void *module_table; + const char *prc_data; + const char *default_prc_dir; + const char *prc_dir_envvars; + const char *prc_path_envvars; + const char *prc_patterns; + const char *prc_encrypted_patterns; + const char *prc_encryption_key; + const char *prc_executable_patterns; + const char *prc_executable_args_envvar; + const char *main_dir; + const char *log_filename; + }; +#ifdef _MSC_VER + const BlobInfo *blobinfo = (const BlobInfo *)GetProcAddress(GetModuleHandle(NULL), "blobinfo"); +#elif defined(RTLD_MAIN_ONLY) + const BlobInfo *blobinfo = (const BlobInfo *)dlsym(RTLD_MAIN_ONLY, "blobinfo"); +//#elif defined(RTLD_SELF) +// const BlobInfo *blobinfo = (const BlobInfo *)dlsym(RTLD_SELF, "blobinfo"); +#else + const BlobInfo *blobinfo = (const BlobInfo *)dlsym(dlopen(NULL, RTLD_NOW), "blobinfo"); +#endif + if (blobinfo != nullptr && (blobinfo->version == 0 || blobinfo->num_pointers < 10)) { + blobinfo = nullptr; + } + + if (blobinfo != nullptr) { + if (blobinfo->num_pointers >= 11 && blobinfo->main_dir != nullptr) { + ExecutionEnvironment::set_environment_variable("MAIN_DIR", blobinfo->main_dir); + } else { + // Make sure that py_panda.cxx won't override MAIN_DIR. + ExecutionEnvironment::set_environment_variable("MAIN_DIR", + ExecutionEnvironment::get_environment_variable("MAIN_DIR")); + } + } + // PRC_PATTERNS lists one or more filename templates separated by spaces. // Pull them out and store them in _prc_patterns. _prc_patterns.clear(); string prc_patterns = PRC_PATTERNS; + if (blobinfo != nullptr && blobinfo->prc_patterns != nullptr) { + prc_patterns = blobinfo->prc_patterns; + } if (!prc_patterns.empty()) { vector_string pat_list; ConfigDeclaration::extract_words(prc_patterns, pat_list); @@ -117,6 +170,9 @@ reload_implicit_pages() { _prc_encrypted_patterns.clear(); string prc_encrypted_patterns = PRC_ENCRYPTED_PATTERNS; + if (blobinfo != nullptr && blobinfo->prc_encrypted_patterns != nullptr) { + prc_encrypted_patterns = blobinfo->prc_encrypted_patterns; + } if (!prc_encrypted_patterns.empty()) { vector_string pat_list; ConfigDeclaration::extract_words(prc_encrypted_patterns, pat_list); @@ -134,6 +190,9 @@ reload_implicit_pages() { _prc_executable_patterns.clear(); string prc_executable_patterns = PRC_EXECUTABLE_PATTERNS; + if (blobinfo != nullptr && blobinfo->prc_executable_patterns != nullptr) { + prc_executable_patterns = blobinfo->prc_executable_patterns; + } if (!prc_executable_patterns.empty()) { vector_string pat_list; ConfigDeclaration::extract_words(prc_executable_patterns, pat_list); @@ -154,6 +213,9 @@ reload_implicit_pages() { // spaces. Pull them out, and each of those contains the name of a single // directory to search. Add it to the search path. string prc_dir_envvars = PRC_DIR_ENVVARS; + if (blobinfo != nullptr && blobinfo->prc_dir_envvars != nullptr) { + prc_dir_envvars = blobinfo->prc_dir_envvars; + } if (!prc_dir_envvars.empty()) { vector_string prc_dir_envvar_list; ConfigDeclaration::extract_words(prc_dir_envvars, prc_dir_envvar_list); @@ -173,6 +235,9 @@ reload_implicit_pages() { // spaces. Pull them out, and then each one of those contains a list of // directories to search. Add each of those to the search path. string prc_path_envvars = PRC_PATH_ENVVARS; + if (blobinfo != nullptr && blobinfo->prc_path_envvars != nullptr) { + prc_path_envvars = blobinfo->prc_path_envvars; + } if (!prc_path_envvars.empty()) { vector_string prc_path_envvar_list; ConfigDeclaration::extract_words(prc_path_envvars, prc_path_envvar_list); @@ -204,7 +269,7 @@ reload_implicit_pages() { * DEFAULT_PATHSEP. */ string prc_path2_envvars = PRC_PATH2_ENVVARS; - if (!prc_path2_envvars.empty()) { + if (!prc_path2_envvars.empty() && blobinfo == nullptr) { vector_string prc_path_envvar_list; ConfigDeclaration::extract_words(prc_path2_envvars, prc_path_envvar_list); for (size_t i = 0; i < prc_path_envvar_list.size(); ++i) { @@ -228,6 +293,9 @@ reload_implicit_pages() { // If nothing's on the search path (PRC_DIR and PRC_PATH were not // defined), then use the DEFAULT_PRC_DIR. string default_prc_dir = DEFAULT_PRC_DIR; + if (blobinfo != nullptr && blobinfo->default_prc_dir != nullptr) { + default_prc_dir = blobinfo->default_prc_dir; + } if (!default_prc_dir.empty()) { // It's already from-os-specific by ppremake. Filename prc_dir_filename = default_prc_dir; @@ -300,12 +368,24 @@ reload_implicit_pages() { } } + int i = 1; + + // If prc_data is predefined, we load it as an implicit page. + if (blobinfo != nullptr && blobinfo->prc_data != nullptr) { + ConfigPage *page = new ConfigPage("builtin", true, i); + ++i; + _implicit_pages.push_back(page); + _pages_sorted = false; + + std::istringstream in(blobinfo->prc_data); + page->read_prc(in); + } + // Now we have a list of filenames in order from most important to least // important. Walk through the list in reverse order to load their // contents, because we want the first file in the list (the most important) // to be on the top of the stack. ConfigFiles::reverse_iterator ci; - int i = 1; for (ci = config_files.rbegin(); ci != config_files.rend(); ++ci) { const ConfigFile &file = (*ci); Filename filename = file._filename; @@ -316,6 +396,9 @@ reload_implicit_pages() { string command = filename.to_os_specific(); string envvar = PRC_EXECUTABLE_ARGS_ENVVAR; + if (blobinfo != nullptr && blobinfo->prc_executable_args_envvar != nullptr) { + envvar = blobinfo->prc_executable_args_envvar; + } if (!envvar.empty()) { string args = ExecutionEnvironment::get_environment_variable(envvar); if (!args.empty()) { @@ -346,7 +429,11 @@ reload_implicit_pages() { _implicit_pages.push_back(page); _pages_sorted = false; - page->read_encrypted_prc(in, PRC_ENCRYPTION_KEY); + if (blobinfo != nullptr && blobinfo->prc_encryption_key != nullptr) { + page->read_encrypted_prc(in, blobinfo->prc_encryption_key); + } else { + page->read_encrypted_prc(in, PRC_ENCRYPTION_KEY); + } } } else if ((file._file_flags & FF_read) != 0) { diff --git a/makepanda/installer.nsi b/makepanda/installer.nsi index fc10ce927c..60de4bcc9d 100644 --- a/makepanda/installer.nsi +++ b/makepanda/installer.nsi @@ -14,7 +14,7 @@ ; ; BUILT - location of panda install tree. ; SOURCE - location of the panda source-tree if available, OR location of panda install tree. -; PYVER - version of Python that Panda was built with (ie, "2.7") +; INCLUDE_PYVER - version of Python that Panda was built with (eg. "2.7", "3.5-32") ; REGVIEW - either 32 or 64, depending on the build architecture. ; @@ -46,7 +46,9 @@ SetCompressor ${COMPRESSOR} !insertmacro MUI_PAGE_LICENSE "${SOURCE}/doc/LICENSE" !insertmacro MUI_PAGE_DIRECTORY +!ifdef INCLUDE_PYVER !define MUI_PAGE_CUSTOMFUNCTION_LEAVE ConfirmPythonSelection +!endif !insertmacro MUI_PAGE_COMPONENTS !insertmacro MUI_PAGE_INSTFILES @@ -64,8 +66,9 @@ ShowUninstDetails hide LicenseData "${SOURCE}/doc/LICENSE" -InstType "Full (Recommended)" -InstType "Minimal" +InstType "Auto (Recommended)" +InstType "Full" +InstType "Light" LangString DESC_SecCore ${LANG_ENGLISH} "The Panda3D core libraries, configuration files and models/textures that are needed to use Panda3D." LangString DESC_SecOpenGL ${LANG_ENGLISH} "The OpenGL graphics back-end is the most well-supported renderer." @@ -78,8 +81,9 @@ LangString DESC_SecODE ${LANG_ENGLISH} "Support for the Open Dynamics Engine to LangString DESC_SecPhysX ${LANG_ENGLISH} "Support for NVIDIA PhysX to implement physics." LangString DESC_SecRocket ${LANG_ENGLISH} "Support for the libRocket GUI library. This is an optional library that offers an HTML/CSS-like approach to creating user interfaces." LangString DESC_SecTools ${LANG_ENGLISH} "Useful tools and model converters to help with Panda3D development. Recommended." -LangString DESC_SecPyBindings ${LANG_ENGLISH} "Contains the Python modules that allow use of Panda3D using Python. These will only work with a ${REGVIEW}-bit version of Python ${PYVER}." -LangString DESC_SecPython ${LANG_ENGLISH} "Contains a ${REGVIEW}-bit copy of Python ${PYVER} preconfigured to make use of Panda3D." +LangString DESC_SecGroupPython ${LANG_ENGLISH} "Contains modules that provide Python support for Panda3D." +LangString DESC_SecPyShared ${LANG_ENGLISH} "Contains the common Python code used by the Panda3D Python bindings." +LangString DESC_SecPython ${LANG_ENGLISH} "Contains a ${REGVIEW}-bit copy of Python ${INCLUDE_PYVER} preconfigured to make use of Panda3D." LangString DESC_SecHeadersLibs ${LANG_ENGLISH} "Headers and libraries needed for C++ development with Panda3D." LangString DESC_SecSamples ${LANG_ENGLISH} "The sample programs demonstrate how to make Python applications with Panda3D." LangString DESC_SecMaxPlugins ${LANG_ENGLISH} "Plug-ins for Autodesk 3ds Max (${REGVIEW}-bit) that can be used to export models to Panda3D." @@ -112,228 +116,41 @@ var MANPAGE !insertmacro !defineifexist HAVE_ODE "${BUILT}\bin\libpandaode.dll" !insertmacro !defineifexist HAVE_PHYSX "${BUILT}\bin\libpandaphysx.dll" !insertmacro !defineifexist HAVE_ROCKET "${BUILT}\bin\libp3rocket.dll" -!insertmacro !defineifexist HAVE_PYTHON "${BUILT}\python" !insertmacro !defineifexist HAVE_SAMPLES "${SOURCE}\samples" !insertmacro !defineifexist HAVE_MAX_PLUGINS "${BUILT}\plugins\*.dlo" !insertmacro !defineifexist HAVE_MAYA_PLUGINS "${BUILT}\plugins\*.mll" -Function runFunction - ExecShell "open" "$SMPROGRAMS\${TITLE}\Panda3D Manual.lnk" -FunctionEnd +!macro RemovePythonPath PYVER + ReadRegStr $0 HKCU "Software\Python\PythonCore\${PYVER}\PythonPath\Panda3D" "" + StrCmp $0 "$INSTDIR" 0 +2 + DeleteRegKey HKCU "Software\Python\PythonCore\${PYVER}\PythonPath\Panda3D" +!macroend -Function .onInit - ${If} ${REGVIEW} = 64 - ${AndIfNot} ${RunningX64} - MessageBox MB_OK|MB_ICONEXCLAMATION "You are attempting to install the 64-bit version of Panda3D on a 32-bit version of Windows. Please download and install the 32-bit version of Panda3D instead." - Abort - ${EndIf} -FunctionEnd +!macro PyBindingSection PYVER EXT_SUFFIX + LangString DESC_SecPyBindings${PYVER} ${LANG_ENGLISH} "Contains the Python modules that allow use of Panda3D using a ${REGVIEW}-bit version of Python ${PYVER}." -SectionGroup "Panda3D Libraries" - Section "Core Libraries" SecCore - SectionIn 1 2 RO - - SetShellVarContext current - SetOverwrite try + !insertmacro !defineifexist _present "${BUILT}\panda3d\core${EXT_SUFFIX}" + !ifdef _present + Section "${PYVER} bindings" SecPyBindings${PYVER} + !if "${PYVER}" == "${INCLUDE_PYVER}" + SectionIn 1 2 3 + !else + !if "${PYVER}" == "2.7" + SectionIn 1 2 + !else + ; See .onInit function where this is dynamically enabled. + SectionIn 2 + !endif + !endif SetDetailsPrint both - DetailPrint "Installing Panda3D libraries..." + DetailPrint "Installing Panda3D bindings for Python ${PYVER}..." SetDetailsPrint listonly - SetOutPath "$INSTDIR" - File /nonfatal "${BUILT}\LICENSE" - File /nonfatal "${BUILT}\ReleaseNotes" - File /nonfatal "${BUILT}\pandaIcon.ico" - - SetOutPath $INSTDIR\etc - File /r "${BUILT}\etc\*" - - SetOutPath $INSTDIR\bin - File /r /x api-ms-win-*.dll /x ucrtbase.dll /x libpandagl.dll /x libpandadx9.dll /x cgD3D*.dll /x python*.dll /x libpandaode.dll /x libp3fmod_audio.dll /x fmodex*.dll /x libp3ffmpeg.dll /x av*.dll /x postproc*.dll /x swscale*.dll /x swresample*.dll /x NxCharacter*.dll /x cudart*.dll /x PhysX*.dll /x libpandaphysx.dll /x libp3rocket.dll /x boost_python*.dll /x Rocket*.dll /x _rocket*.pyd /x libpandabullet.dll /x OpenAL32.dll /x *_oal.dll /x libp3openal_audio.dll "${BUILT}\bin\*.dll" - File /nonfatal /r "${BUILT}\bin\Microsoft.*.manifest" - - ; Before Windows 10, we need these stubs for the UCRT as well. - ReadRegDWORD $0 HKLM "Software\Microsoft\Windows NT\CurrentVersion" "CurrentMajorVersionNumber" - ${If} $0 < 10 - ClearErrors - File /nonfatal /r "${BUILT}\bin\api-ms-win-*.dll" - File /nonfatal "${BUILT}\bin\ucrtbase.dll" - ${Endif} - - SetDetailsPrint both - DetailPrint "Installing models..." - SetDetailsPrint listonly - - SetOutPath $INSTDIR\models - File /r /x CVS "${BUILT}\models\*" - - SetDetailsPrint both - DetailPrint "Installing optional components..." - SetDetailsPrint listonly - - RMDir /r "$SMPROGRAMS\${TITLE}" - CreateDirectory "$SMPROGRAMS\${TITLE}" - SectionEnd - - !ifdef HAVE_GL - Section "OpenGL" SecOpenGL - SectionIn 1 2 RO - - SetOutPath "$INSTDIR\bin" - File "${BUILT}\bin\libpandagl.dll" - SectionEnd - !endif - - !ifdef HAVE_DX9 - Section "Direct3D 9" SecDirect3D9 - SectionIn 1 - - SetOutPath "$INSTDIR\bin" - File "${BUILT}\bin\libpandadx9.dll" - File /nonfatal /r "${BUILT}\bin\cgD3D9.dll" - SectionEnd - !endif - - !ifdef HAVE_OPENAL - Section "OpenAL Audio" SecOpenAL - SectionIn 1 2 - - SetOutPath "$INSTDIR\bin" - File "${BUILT}\bin\libp3openal_audio.dll" - File /nonfatal /r "${BUILT}\bin\OpenAL32.dll" - File /nonfatal /r "${BUILT}\bin\*_oal.dll" - SectionEnd - !endif - - !ifdef HAVE_FMOD - Section "FMOD Audio" SecFMOD - SectionIn 1 - - SetOutPath "$INSTDIR\bin" - File "${BUILT}\bin\libp3fmod_audio.dll" - File /r "${BUILT}\bin\fmodex*.dll" - SectionEnd - !endif - - !ifdef HAVE_FFMPEG - Section "FFMpeg" SecFFMpeg - SectionIn 1 - - SetOutPath "$INSTDIR\bin" - File "${BUILT}\bin\libp3ffmpeg.dll" - File /nonfatal /r "${BUILT}\bin\av*.dll" - File /nonfatal /r "${BUILT}\bin\swscale*.dll" - File /nonfatal /r "${BUILT}\bin\swresample*.dll" - File /nonfatal /r "${BUILT}\bin\postproc*.dll" - SectionEnd - !endif - - !ifdef HAVE_BULLET - Section "Bullet Physics" SecBullet - SectionIn 1 - - SetOutPath "$INSTDIR\bin" - File "${BUILT}\bin\libpandabullet.dll" - SectionEnd - !endif - - !ifdef HAVE_ODE - Section "ODE Physics" SecODE - SectionIn 1 - - SetOutPath "$INSTDIR\bin" - File "${BUILT}\bin\libpandaode.dll" - SectionEnd - !endif - - !ifdef HAVE_PHYSX - Section "NVIDIA PhysX" SecPhysX - SectionIn 1 - - SetOutPath "$INSTDIR\bin" - File "${BUILT}\bin\libpandaphysx.dll" - File /nonfatal /r "${BUILT}\bin\PhysX*.dll" - File /nonfatal /r "${BUILT}\bin\NxCharacter*.dll" - File /nonfatal /r "${BUILT}\bin\cudart*.dll" - SectionEnd - !endif - - !ifdef HAVE_ROCKET - Section "libRocket GUI" SecRocket - SectionIn 1 - - SetOutPath "$INSTDIR\bin" - File "${BUILT}\bin\libp3rocket.dll" - File /nonfatal /r "${BUILT}\bin\Rocket*.dll" - File /nonfatal /r "${BUILT}\bin\_rocket*.pyd" - File /nonfatal /r "${BUILT}\bin\boost_python*.dll" - SectionEnd - !endif -SectionGroupEnd - -Section "Tools and utilities" SecTools - SectionIn 1 2 - - SetDetailsPrint both - DetailPrint "Installing utilities..." - SetDetailsPrint listonly - - SetOutPath "$INSTDIR\bin" - File /r "${BUILT}\bin\*.exe" - File /nonfatal /r "${BUILT}\bin\*.p3d" - SetOutPath "$INSTDIR\NSIS" - File /r /x CVS "${NSISDIR}\*" - - WriteRegStr HKCU "Software\Classes\Panda3D.Model" "" "Panda3D model/animation" - WriteRegStr HKCU "Software\Classes\Panda3D.Model\DefaultIcon" "" "$INSTDIR\bin\pview.exe" - WriteRegStr HKCU "Software\Classes\Panda3D.Model\shell" "" "open" - WriteRegStr HKCU "Software\Classes\Panda3D.Model\shell\open\command" "" '"$INSTDIR\bin\pview.exe" -l "%1"' - WriteRegStr HKCU "Software\Classes\Panda3D.Model\shell\compress" "" "Compress to .pz" - WriteRegStr HKCU "Software\Classes\Panda3D.Model\shell\compress\command" "" '"$INSTDIR\bin\pzip.exe" "%1"' - - WriteRegStr HKCU "Software\Classes\Panda3D.Compressed" "" "Compressed file" - WriteRegStr HKCU "Software\Classes\Panda3D.Compressed\DefaultIcon" "" "$INSTDIR\bin\pzip.exe" - WriteRegStr HKCU "Software\Classes\Panda3D.Compressed\shell" "" "open" - WriteRegStr HKCU "Software\Classes\Panda3D.Compressed\shell\open\command" "" '"$INSTDIR\bin\pview.exe" -l "%1"' - WriteRegStr HKCU "Software\Classes\Panda3D.Compressed\shell\decompress" "" "Decompress" - WriteRegStr HKCU "Software\Classes\Panda3D.Compressed\shell\decompress\command" "" '"$INSTDIR\bin\punzip.exe" "%1"' - - WriteRegStr HKCU "Software\Classes\Panda3D.Multifile" "" "Panda3D Multifile" - WriteRegStr HKCU "Software\Classes\Panda3D.Multifile\DefaultIcon" "" "$INSTDIR\bin\multify.exe" - WriteRegStr HKCU "Software\Classes\Panda3D.Multifile\shell" "" "open" - WriteRegStr HKCU "Software\Classes\Panda3D.Multifile\shell\extract" "" "Extract here" - WriteRegStr HKCU "Software\Classes\Panda3D.Multifile\shell\extract\command" "" '"$INSTDIR\bin\multify.exe" -xf "%1"' -SectionEnd - -SectionGroup "Python support" - Section "Python bindings" SecPyBindings - SectionIn 1 2 - - SetDetailsPrint both - DetailPrint "Installing Panda3D Python modules..." - SetDetailsPrint listonly - - SetOutPath "$INSTDIR\bin" - File /nonfatal /r "${BUILT}\bin\*.pyd" - - SetOutPath $INSTDIR\direct\directscripts - File /r /x CVS /x Opt?-Win32 "${BUILT}\direct\directscripts\*" - SetOutPath $INSTDIR\direct\filter - File /r /x CVS /x Opt?-Win32 "${BUILT}\direct\filter\*.sha" - SetOutPath $INSTDIR\direct - File /r /x CVS /x Opt?-Win32 "${BUILT}\direct\*.py" - - Delete "$INSTDIR\panda3d.py" - Delete "$INSTDIR\panda3d.pyc" - Delete "$INSTDIR\panda3d.pyo" - SetOutPath $INSTDIR\pandac - File /r "${BUILT}\pandac\*.py" SetOutPath $INSTDIR\panda3d - File /r "${BUILT}\panda3d\*.py" File /nonfatal /r "${BUILT}\panda3d\core${EXT_SUFFIX}" File /nonfatal /r "${BUILT}\panda3d\ai${EXT_SUFFIX}" - File /nonfatal /r "${BUILT}\panda3d\awesomium${EXT_SUFFIX}" File /nonfatal /r "${BUILT}\panda3d\direct${EXT_SUFFIX}" File /nonfatal /r "${BUILT}\panda3d\egg${EXT_SUFFIX}" File /nonfatal /r "${BUILT}\panda3d\fx${EXT_SUFFIX}" @@ -380,92 +197,349 @@ SectionGroup "Python support" File /r "${BUILT}\pandac\input\*" SetOutPath $INSTDIR\Pmw File /nonfatal /r /x CVS "${BUILT}\Pmw\*" + SetOutPath $INSTDIR\panda3d.dist-info + File /nonfatal /r "${BUILT}\panda3d.dist-info\*" !ifdef REGVIEW SetRegView ${REGVIEW} !endif - ; Check for a non-Panda3D system-wide Python installation. - ReadRegStr $0 HKLM "Software\Python\PythonCore\${PYVER}\InstallPath" "" - StrCmp $0 "$INSTDIR\python" UserExternalPthCheck 0 - StrCmp $0 "" UserExternalPthCheck 0 - IfFileExists "$0\ppython.exe" UserExternalPthCheck 0 - IfFileExists "$0\python.exe" AskExternalPth UserExternalPthCheck - - ; Check for a non-Panda3D user installation of Python. - UserExternalPthCheck: - ReadRegStr $0 HKCU "Software\Python\PythonCore\${PYVER}\InstallPath" "" - StrCmp $0 "$INSTDIR\python" SkipExternalPth 0 - StrCmp $0 "" SkipExternalPth 0 - IfFileExists "$0\ppython.exe" SkipExternalPth 0 - IfFileExists "$0\python.exe" AskExternalPth SkipExternalPth - - ; We're pretty sure this Python build is of the right architecture. - AskExternalPth: - MessageBox MB_YESNO|MB_ICONQUESTION \ - "Your system already has a copy of Python ${PYVER} installed in:$\r$\n$0$\r$\nWould you like to configure it to be able to use the Panda3D libraries?$\r$\nIf you choose no, you will only be able to use Panda3D's own copy of Python." \ - IDYES WriteExternalPth IDNO SkipExternalPth - - WriteExternalPth: - ;FileOpen $1 "$0\Lib\site-packages\panda.pth" w - ;FileWrite $1 "$INSTDIR$\r$\n" - ;FileWrite $1 "$INSTDIR\bin$\r$\n" - ;FileClose $1 - - ; Actually, it looks like we can just do this instead: + ; Install a Panda3D path into the global PythonPath for this version + ; of Python. WriteRegStr HKCU "Software\Python\PythonCore\${PYVER}\PythonPath\Panda3D" "" "$INSTDIR" + SectionEnd + !undef _present + !endif +!macroend - SkipExternalPth: +Function runFunction + ExecShell "open" "$SMPROGRAMS\${TITLE}\Panda3D Manual.lnk" +FunctionEnd + +SectionGroup "Panda3D Libraries" + Section "Core Libraries" SecCore + SectionIn 1 2 3 RO + + SetShellVarContext current + SetOverwrite try + + SetDetailsPrint both + DetailPrint "Installing Panda3D libraries..." + SetDetailsPrint listonly + + SetOutPath "$INSTDIR" + File /nonfatal "${BUILT}\LICENSE" + File /nonfatal "${BUILT}\ReleaseNotes" + File /nonfatal "${BUILT}\pandaIcon.ico" + + SetOutPath $INSTDIR\etc + File /r "${BUILT}\etc\*" + + SetOutPath $INSTDIR\bin + File /r /x api-ms-win-*.dll /x ucrtbase.dll /x libpandagl.dll /x libpandadx9.dll /x cgD3D*.dll /x python*.dll /x libpandaode.dll /x libp3fmod_audio.dll /x fmodex*.dll /x libp3ffmpeg.dll /x av*.dll /x postproc*.dll /x swscale*.dll /x swresample*.dll /x NxCharacter*.dll /x cudart*.dll /x PhysX*.dll /x libpandaphysx.dll /x libp3rocket.dll /x boost_python*.dll /x Rocket*.dll /x _rocket*.pyd /x libpandabullet.dll /x OpenAL32.dll /x *_oal.dll /x libp3openal_audio.dll "${BUILT}\bin\*.dll" + File /nonfatal /r "${BUILT}\bin\Microsoft.*.manifest" + + ; Before Windows 10, we need these stubs for the UCRT as well. + ReadRegDWORD $0 HKLM "Software\Microsoft\Windows NT\CurrentVersion" "CurrentMajorVersionNumber" + ${If} $0 < 10 + ClearErrors + File /nonfatal /r "${BUILT}\bin\api-ms-win-*.dll" + File /nonfatal "${BUILT}\bin\ucrtbase.dll" + ${Endif} + + SetDetailsPrint both + DetailPrint "Installing models..." + SetDetailsPrint listonly + + SetOutPath $INSTDIR\models + File /r /x CVS "${BUILT}\models\*" + + SetDetailsPrint both + DetailPrint "Installing optional components..." + SetDetailsPrint listonly + + RMDir /r "$SMPROGRAMS\${TITLE}" + CreateDirectory "$SMPROGRAMS\${TITLE}" SectionEnd - !ifdef HAVE_PYTHON - Section "Python ${PYVER}" SecPython - SectionIn 1 2 - - !ifdef REGVIEW - SetRegView ${REGVIEW} - !endif - - SetDetailsPrint both - DetailPrint "Installing Python ${PYVER} (${REGVIEW}-bit)..." - SetDetailsPrint listonly + !ifdef HAVE_GL + Section "OpenGL" SecOpenGL + SectionIn 1 2 3 RO SetOutPath "$INSTDIR\bin" - File /nonfatal "${BUILT}\bin\python*.dll" + File "${BUILT}\bin\libpandagl.dll" + SectionEnd + !endif - SetOutPath "$INSTDIR\python" - File /r /x *.pdb "${BUILT}\python\*" + !ifdef HAVE_DX9 + Section "Direct3D 9" SecDirect3D9 + SectionIn 1 2 - SetDetailsPrint both - DetailPrint "Adding registry keys for Python..." - SetDetailsPrint listonly + SetOutPath "$INSTDIR\bin" + File "${BUILT}\bin\libpandadx9.dll" + File /nonfatal /r "${BUILT}\bin\cgD3D9.dll" + SectionEnd + !endif - ; Check if a copy of Python is installed for this user. - ReadRegStr $0 HKCU "Software\Python\PythonCore\${PYVER}\InstallPath" "" - StrCmp "$0" "$INSTDIR\python" RegPath 0 - StrCmp "$0" "" SkipFileCheck 0 - IfFileExists "$0\python.exe" AskRegPath 0 - SkipFileCheck: + !ifdef HAVE_OPENAL + Section "OpenAL Audio" SecOpenAL + SectionIn 1 2 3 - ; Check if a system-wide copy of Python is installed. - ReadRegStr $0 HKLM "Software\Python\PythonCore\${PYVER}\InstallPath" "" - StrCmp "$0" "$INSTDIR\python" RegPath 0 - StrCmp "$0" "" RegPath 0 - IfFileExists "$0\python.exe" AskRegPath RegPath + SetOutPath "$INSTDIR\bin" + File "${BUILT}\bin\libp3openal_audio.dll" + File /nonfatal /r "${BUILT}\bin\OpenAL32.dll" + File /nonfatal /r "${BUILT}\bin\*_oal.dll" + SectionEnd + !endif - AskRegPath: - MessageBox MB_YESNO|MB_ICONQUESTION \ - "You already have a copy of Python ${PYVER} installed in:$\r$\n$0$\r$\n$\r$\nPanda3D installs its own copy of Python ${PYVER}, which will install alongside your existing copy. Would you like to make Panda's copy the default Python for your user account?" \ - IDNO SkipRegPath + !ifdef HAVE_FMOD + Section "FMOD Audio" SecFMOD + SectionIn 1 2 - RegPath: - WriteRegStr HKCU "Software\Python\PythonCore\${PYVER}\InstallPath" "" "$INSTDIR\python" - SkipRegPath: + SetOutPath "$INSTDIR\bin" + File "${BUILT}\bin\libp3fmod_audio.dll" + File /r "${BUILT}\bin\fmodex*.dll" + SectionEnd + !endif + !ifdef HAVE_FFMPEG + Section "FFMpeg" SecFFMpeg + SectionIn 1 2 + + SetOutPath "$INSTDIR\bin" + File "${BUILT}\bin\libp3ffmpeg.dll" + File /nonfatal /r "${BUILT}\bin\av*.dll" + File /nonfatal /r "${BUILT}\bin\swscale*.dll" + File /nonfatal /r "${BUILT}\bin\swresample*.dll" + File /nonfatal /r "${BUILT}\bin\postproc*.dll" + SectionEnd + !endif + + !ifdef HAVE_BULLET + Section "Bullet Physics" SecBullet + SectionIn 1 2 + + SetOutPath "$INSTDIR\bin" + File "${BUILT}\bin\libpandabullet.dll" + SectionEnd + !endif + + !ifdef HAVE_ODE + Section "ODE Physics" SecODE + SectionIn 1 2 + + SetOutPath "$INSTDIR\bin" + File "${BUILT}\bin\libpandaode.dll" + SectionEnd + !endif + + !ifdef HAVE_PHYSX + Section "NVIDIA PhysX" SecPhysX + ; Only enable in "Full" + SectionIn 2 + + SetOutPath "$INSTDIR\bin" + File "${BUILT}\bin\libpandaphysx.dll" + File /nonfatal /r "${BUILT}\bin\PhysX*.dll" + File /nonfatal /r "${BUILT}\bin\NxCharacter*.dll" + File /nonfatal /r "${BUILT}\bin\cudart*.dll" + SectionEnd + !endif + + !ifdef HAVE_ROCKET + Section "libRocket GUI" SecRocket + SectionIn 1 2 + + SetOutPath "$INSTDIR\bin" + File "${BUILT}\bin\libp3rocket.dll" + File /nonfatal /r "${BUILT}\bin\Rocket*.dll" + File /nonfatal /r "${BUILT}\bin\_rocket*.pyd" + File /nonfatal /r "${BUILT}\bin\boost_python*.dll" SectionEnd !endif SectionGroupEnd +Section "Tools and utilities" SecTools + SectionIn 1 2 3 + + SetDetailsPrint both + DetailPrint "Installing utilities..." + SetDetailsPrint listonly + + SetOutPath "$INSTDIR\bin" + File /r /x deploy-stub.exe /x deploy-stubw.exe "${BUILT}\bin\*.exe" + File /nonfatal /r "${BUILT}\bin\*.p3d" + SetOutPath "$INSTDIR\NSIS" + File /r /x CVS "${NSISDIR}\*" + + WriteRegStr HKCU "Software\Classes\Panda3D.Model" "" "Panda3D model/animation" + WriteRegStr HKCU "Software\Classes\Panda3D.Model\DefaultIcon" "" "$INSTDIR\bin\pview.exe" + WriteRegStr HKCU "Software\Classes\Panda3D.Model\shell" "" "open" + WriteRegStr HKCU "Software\Classes\Panda3D.Model\shell\open\command" "" '"$INSTDIR\bin\pview.exe" -l "%1"' + WriteRegStr HKCU "Software\Classes\Panda3D.Model\shell\compress" "" "Compress to .pz" + WriteRegStr HKCU "Software\Classes\Panda3D.Model\shell\compress\command" "" '"$INSTDIR\bin\pzip.exe" "%1"' + + WriteRegStr HKCU "Software\Classes\Panda3D.Compressed" "" "Compressed file" + WriteRegStr HKCU "Software\Classes\Panda3D.Compressed\DefaultIcon" "" "$INSTDIR\bin\pzip.exe" + WriteRegStr HKCU "Software\Classes\Panda3D.Compressed\shell" "" "open" + WriteRegStr HKCU "Software\Classes\Panda3D.Compressed\shell\open\command" "" '"$INSTDIR\bin\pview.exe" -l "%1"' + WriteRegStr HKCU "Software\Classes\Panda3D.Compressed\shell\decompress" "" "Decompress" + WriteRegStr HKCU "Software\Classes\Panda3D.Compressed\shell\decompress\command" "" '"$INSTDIR\bin\punzip.exe" "%1"' + + WriteRegStr HKCU "Software\Classes\Panda3D.Multifile" "" "Panda3D Multifile" + WriteRegStr HKCU "Software\Classes\Panda3D.Multifile\DefaultIcon" "" "$INSTDIR\bin\multify.exe" + WriteRegStr HKCU "Software\Classes\Panda3D.Multifile\shell" "" "open" + WriteRegStr HKCU "Software\Classes\Panda3D.Multifile\shell\extract" "" "Extract here" + WriteRegStr HKCU "Software\Classes\Panda3D.Multifile\shell\extract\command" "" '"$INSTDIR\bin\multify.exe" -xf "%1"' +SectionEnd + +SectionGroup "Python modules" SecGroupPython + Section "Shared code" SecPyShared + SectionIn 1 2 3 + + SetDetailsPrint both + DetailPrint "Installing Panda3D shared Python modules..." + SetDetailsPrint listonly + + SetOutPath $INSTDIR\direct\directscripts + File /r /x CVS /x Opt?-Win32 "${BUILT}\direct\directscripts\*" + SetOutPath $INSTDIR\direct + File /r /x CVS /x Opt?-Win32 "${BUILT}\direct\*.py" + + Delete "$INSTDIR\panda3d.py" + Delete "$INSTDIR\panda3d.pyc" + Delete "$INSTDIR\panda3d.pyo" + SetOutPath $INSTDIR\pandac + File /r "${BUILT}\pandac\*.py" + SetOutPath $INSTDIR\panda3d + File /r "${BUILT}\panda3d\*.py" + SectionEnd + + !insertmacro PyBindingSection 2.7 .pyd + !if "${REGVIEW}" == "32" + !insertmacro PyBindingSection 3.5-32 .cp35-win32.pyd + !insertmacro PyBindingSection 3.6-32 .cp36-win32.pyd + !insertmacro PyBindingSection 3.7-32 .cp37-win32.pyd + !insertmacro PyBindingSection 3.8-32 .cp38-win32.pyd + !insertmacro PyBindingSection 3.9-32 .cp39-win32.pyd + !else + !insertmacro PyBindingSection 3.5 .cp35-win_amd64.pyd + !insertmacro PyBindingSection 3.6 .cp36-win_amd64.pyd + !insertmacro PyBindingSection 3.7 .cp37-win_amd64.pyd + !insertmacro PyBindingSection 3.8 .cp38-win_amd64.pyd + !insertmacro PyBindingSection 3.9 .cp39-win_amd64.pyd + !endif +SectionGroupEnd + +!ifdef INCLUDE_PYVER +Section "Python ${INCLUDE_PYVER}" SecPython + SectionIn 1 2 3 + + !ifdef REGVIEW + SetRegView ${REGVIEW} + !endif + + SetDetailsPrint both + DetailPrint "Installing Python ${INCLUDE_PYVER} interpreter (${REGVIEW}-bit)..." + SetDetailsPrint listonly + + SetOutPath "$INSTDIR\bin" + File /nonfatal "${BUILT}\bin\python*.dll" + + SetOutPath "$INSTDIR\python" + File /r /x *.pdb "${BUILT}\python\*" + + SetDetailsPrint both + DetailPrint "Adding registry keys for Python..." + SetDetailsPrint listonly + + ; Check if a copy of Python is installed for this user. + ReadRegStr $0 HKCU "Software\Python\PythonCore\${INCLUDE_PYVER}\InstallPath" "" + StrCmp "$0" "$INSTDIR\python" RegPath 0 + StrCmp "$0" "" SkipFileCheck 0 + IfFileExists "$0\python.exe" AskRegPath 0 + SkipFileCheck: + + ; Check if a system-wide copy of Python is installed. + ReadRegStr $0 HKLM "Software\Python\PythonCore\${INCLUDE_PYVER}\InstallPath" "" + StrCmp "$0" "$INSTDIR\python" RegPath 0 + StrCmp "$0" "" RegPath 0 + IfFileExists "$0\python.exe" AskRegPath RegPath + + AskRegPath: + MessageBox MB_YESNO|MB_ICONQUESTION \ + "You already have a copy of Python ${INCLUDE_PYVER} installed in:$\r$\n$0$\r$\n$\r$\nPanda3D installs its own copy of Python ${INCLUDE_PYVER}, which will install alongside your existing copy. Would you like to make Panda's copy the default Python for your user account?" \ + IDNO SkipRegPath + + RegPath: + WriteRegStr HKCU "Software\Python\PythonCore\${INCLUDE_PYVER}\InstallPath" "" "$INSTDIR\python" + WriteRegStr HKCU "Software\Python\PythonCore\${INCLUDE_PYVER}\InstallPath" "ExecutablePath" "$INSTDIR\python\python.exe" + SkipRegPath: + +SectionEnd +!endif + +!macro MaybeEnablePyBindingSection PYVER + !if "${INCLUDE_PYVER}" != "${PYVER}" + !ifdef SecPyBindings${PYVER} + ; Check if a copy of Python is installed for this user. + Push $0 + ReadRegStr $0 HKCU "Software\Python\PythonCore\${PYVER}\InstallPath" "" + StrCmp "$0" "" +2 0 + IfFileExists "$0\python.exe" Py${PYVER}Exists 0 + + ; Check if a system-wide copy of Python is installed. + ReadRegStr $0 HKLM "Software\Python\PythonCore\${PYVER}\InstallPath" "" + StrCmp "$0" "" Py${PYVER}ExistsNot 0 + IfFileExists "$0\python.exe" Py${PYVER}Exists Py${PYVER}ExistsNot + + Py${PYVER}Exists: + SectionSetFlags ${SecPyBindings${PYVER}} ${SF_SELECTED} + SectionSetInstTypes ${SecPyBindings${PYVER}} 3 + + Py${PYVER}ExistsNot: + Pop $0 + !endif + !endif +!macroend + +Function .onInit + ${If} ${REGVIEW} = 64 + ${AndIfNot} ${RunningX64} + MessageBox MB_OK|MB_ICONEXCLAMATION "You are attempting to install the 64-bit version of Panda3D on a 32-bit version of Windows. Please download and install the 32-bit version of Panda3D instead." + Abort + ${EndIf} + + !ifdef REGVIEW + SetRegView ${REGVIEW} + !endif + + ; We never check for 2.7; it is always enabled in Auto mode + !if "${REGVIEW}" == "32" + !insertmacro MaybeEnablePyBindingSection 3.5-32 + !insertmacro MaybeEnablePyBindingSection 3.6-32 + !insertmacro MaybeEnablePyBindingSection 3.7-32 + !insertmacro MaybeEnablePyBindingSection 3.8-32 + !insertmacro MaybeEnablePyBindingSection 3.9-32 + !else + !insertmacro MaybeEnablePyBindingSection 3.5 + !insertmacro MaybeEnablePyBindingSection 3.6 + !insertmacro MaybeEnablePyBindingSection 3.7 + !insertmacro MaybeEnablePyBindingSection 3.8 + !insertmacro MaybeEnablePyBindingSection 3.9 + !endif +FunctionEnd + +Function .onSelChange + ; If someone selects any Python version, the "shared modules" must be on. + ${If} ${SectionIsPartiallySelected} ${SecGroupPython} + SectionGetFlags ${SecPyShared} $R0 + IntOp $R0 $R0 | ${SF_SELECTED} + SectionSetFlags ${SecPyShared} $R0 + ${EndIf} +FunctionEnd + +!ifdef INCLUDE_PYVER Function ConfirmPythonSelection ; Check the current state of the "Python" section selection. SectionGetFlags ${SecPython} $R0 @@ -475,16 +549,14 @@ Function ConfirmPythonSelection StrCmp $R1 ${SF_SELECTED} SkipCheck 0 ; Maybe the user just doesn't want Python support at all? - SectionGetFlags ${SecPyBindings} $R1 - IntOp $R1 $R1 & ${SF_SELECTED} - StrCmp $R1 ${SF_SELECTED} 0 SkipCheck + !insertmacro SectionFlagIsSet ${SecGroupPython} ${SF_PSELECTED} 0 SkipCheck !ifdef REGVIEW SetRegView ${REGVIEW} !endif ; Check for a user installation of Python. - ReadRegStr $0 HKCU "Software\Python\PythonCore\${PYVER}\InstallPath" "" + ReadRegStr $0 HKCU "Software\Python\PythonCore\${INCLUDE_PYVER}\InstallPath" "" StrCmp $0 "$INSTDIR\python" CheckSystemWidePython 0 StrCmp $0 "" CheckSystemWidePython 0 IfFileExists "$0\ppython.exe" CheckSystemWidePython 0 @@ -492,7 +564,7 @@ Function ConfirmPythonSelection ; Check for a system-wide Python installation. CheckSystemWidePython: - ReadRegStr $0 HKLM "Software\Python\PythonCore\${PYVER}\InstallPath" "" + ReadRegStr $0 HKLM "Software\Python\PythonCore\${INCLUDE_PYVER}\InstallPath" "" StrCmp $0 "$INSTDIR\python" AskConfirmation 0 StrCmp $0 "" AskConfirmation 0 IfFileExists "$0\ppython.exe" AskConfirmation 0 @@ -502,7 +574,7 @@ Function ConfirmPythonSelection ; of a different Panda3D build.) Ask the user if he's sure about this. AskConfirmation: MessageBox MB_YESNO|MB_ICONQUESTION \ - "You do not appear to have a ${REGVIEW}-bit version of Python ${PYVER} installed. Are you sure you don't want Panda to install a compatible copy of Python?$\r$\n$\r$\nIf you choose Yes, you will not be able to do Python development with Panda3D until you install a ${REGVIEW}-bit version of Python ${PYVER} and manually configure it to be able to use Panda3D." \ + "You do not appear to have a ${REGVIEW}-bit version of Python ${INCLUDE_PYVER} installed. Are you sure you don't want Panda to install a compatible copy of Python?$\r$\n$\r$\nIf you choose Yes, you will not be able to do Python development with Panda3D until you install a ${REGVIEW}-bit version of Python and install the bindings for this version." \ IDYES SkipCheck ; User clicked no, so re-enable the select box and abort. @@ -512,9 +584,10 @@ Function ConfirmPythonSelection SkipCheck: FunctionEnd +!endif Section "C++ support" SecHeadersLibs - SectionIn 1 + SectionIn 1 2 SetDetailsPrint both DetailPrint "Installing header files..." @@ -533,7 +606,7 @@ SectionEnd !ifdef HAVE_SAMPLES Section "Sample programs" SecSamples - SectionIn 1 + SectionIn 1 2 ; Necessary for proper start menu shortcut installation SetShellVarContext current @@ -600,7 +673,7 @@ SectionEnd !ifdef HAVE_MAX_PLUGINS Section "3ds Max plug-ins" SecMaxPlugins - SectionIn 1 3 + SectionIn 1 2 SetDetailsPrint both DetailPrint "Installing Autodesk 3ds Max plug-ins..." @@ -615,7 +688,7 @@ SectionEnd !ifdef HAVE_MAYA_PLUGINS Section "Maya plug-ins" SecMayaPlugins - SectionIn 1 3 + SectionIn 1 2 SetDetailsPrint both DetailPrint "Installing Autodesk Maya plug-ins..." @@ -738,17 +811,30 @@ Section Uninstall DeleteRegKey HKCU "Software\Classes\Panda3D.Multifile\DefaultIcon" DeleteRegKey HKCU "Software\Classes\Panda3D.Multifile\shell" - ReadRegStr $0 HKLM "Software\Python\PythonCore\${PYVER}\InstallPath" "" - StrCmp $0 "$INSTDIR\python" 0 +2 - DeleteRegKey HKLM "Software\Python\PythonCore\${PYVER}" + !ifdef INCLUDE_PYVER + ReadRegStr $0 HKLM "Software\Python\PythonCore\${INCLUDE_PYVER}\InstallPath" "" + StrCmp $0 "$INSTDIR\python" 0 +2 + DeleteRegKey HKLM "Software\Python\PythonCore\${INCLUDE_PYVER}" - ReadRegStr $0 HKCU "Software\Python\PythonCore\${PYVER}\InstallPath" "" - StrCmp $0 "$INSTDIR\python" 0 +2 - DeleteRegKey HKCU "Software\Python\PythonCore\${PYVER}" + ReadRegStr $0 HKCU "Software\Python\PythonCore\${INCLUDE_PYVER}\InstallPath" "" + StrCmp $0 "$INSTDIR\python" 0 +2 + DeleteRegKey HKCU "Software\Python\PythonCore\${INCLUDE_PYVER}" + !endif - ReadRegStr $0 HKCU "Software\Python\PythonCore\${PYVER}\PythonPath\Panda3D" "" - StrCmp $0 "$INSTDIR" 0 +2 - DeleteRegKey HKCU "Software\Python\PythonCore\${PYVER}\PythonPath\Panda3D" + !insertmacro RemovePythonPath 2.7 + !if "${REGVIEW}" == "32" + !insertmacro RemovePythonPath 3.5-32 + !insertmacro RemovePythonPath 3.6-32 + !insertmacro RemovePythonPath 3.7-32 + !insertmacro RemovePythonPath 3.8-32 + !insertmacro RemovePythonPath 3.9-32 + !else + !insertmacro RemovePythonPath 3.5 + !insertmacro RemovePythonPath 3.6 + !insertmacro RemovePythonPath 3.7 + !insertmacro RemovePythonPath 3.8 + !insertmacro RemovePythonPath 3.9 + !endif SetDetailsPrint both DetailPrint "Deleting files..." @@ -813,8 +899,23 @@ SectionEnd !insertmacro MUI_DESCRIPTION_TEXT ${SecRocket} $(DESC_SecRocket) !endif !insertmacro MUI_DESCRIPTION_TEXT ${SecTools} $(DESC_SecTools) - !insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings} $(DESC_SecPyBindings) - !ifdef HAVE_PYTHON + !insertmacro MUI_DESCRIPTION_TEXT ${SecGroupPython} $(DESC_SecGroupPython) + !insertmacro MUI_DESCRIPTION_TEXT ${SecPyShared} $(DESC_SecPyShared) + !insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings2.7} $(DESC_SecPyBindings2.7) + !if "${REGVIEW}" == "32" + !insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.5-32} $(DESC_SecPyBindings3.5-32) + !insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.6-32} $(DESC_SecPyBindings3.6-32) + !insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.7-32} $(DESC_SecPyBindings3.7-32) + !insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.8-32} $(DESC_SecPyBindings3.8-32) + !insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.9-32} $(DESC_SecPyBindings3.9-32) + !else + !insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.5} $(DESC_SecPyBindings3.5) + !insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.6} $(DESC_SecPyBindings3.6) + !insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.7} $(DESC_SecPyBindings3.7) + !insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.8} $(DESC_SecPyBindings3.8) + !insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.9} $(DESC_SecPyBindings3.9) + !endif + !ifdef INCLUDE_PYVER !insertmacro MUI_DESCRIPTION_TEXT ${SecPython} $(DESC_SecPython) !endif !insertmacro MUI_DESCRIPTION_TEXT ${SecHeadersLibs} $(DESC_SecHeadersLibs) diff --git a/makepanda/installpanda.py b/makepanda/installpanda.py index 410847efc7..7f9b79cd60 100644 --- a/makepanda/installpanda.py +++ b/makepanda/installpanda.py @@ -13,10 +13,6 @@ from distutils.sysconfig import get_python_lib from optparse import OptionParser from makepandacore import * -def python_sitepackages_path(): - from distutils.sysconfig import get_python_lib - return get_python_lib(1) -PYTHON_SITEPACKAGES=python_sitepackages_path() MIME_INFO = ( ("egg", "model/x-egg", "EGG model file", "pview"), @@ -153,18 +149,11 @@ def GetLibDir(): return "lib" -def InstallPanda(destdir="", prefix="/usr", outputdir="built", libdir=GetLibDir()): +def InstallPanda(destdir="", prefix="/usr", outputdir="built", libdir=GetLibDir(), python_versions=[]): if (not prefix.startswith("/")): prefix = "/" + prefix libdir = prefix + "/" + libdir - # Determine the location of the Python executable and site-packages dir. - PPATH = get_python_lib(1) - if os.path.islink(sys.executable): - PEXEC = os.path.join(os.path.dirname(sys.executable), os.readlink(sys.executable)) - else: - PEXEC = sys.executable - # Create the directory structure that we will be putting our files in. oscmd("mkdir -m 0755 -p "+destdir+prefix+"/bin") oscmd("mkdir -m 0755 -p "+destdir+prefix+"/include") @@ -174,8 +163,10 @@ def InstallPanda(destdir="", prefix="/usr", outputdir="built", libdir=GetLibDir( oscmd("mkdir -m 0755 -p "+destdir+prefix+"/share/application-registry") oscmd("mkdir -m 0755 -p "+destdir+prefix+"/share/applications") oscmd("mkdir -m 0755 -p "+destdir+libdir+"/panda3d") - oscmd("mkdir -m 0755 -p "+destdir+PPATH) - oscmd("mkdir -m 0755 -p "+destdir+PPATH+"/panda3d") + + for python_version in python_versions: + oscmd("mkdir -m 0755 -p "+destdir+python_version["purelib"]) + oscmd("mkdir -m 0755 -p "+destdir+python_version["platlib"]+"/panda3d") if (sys.platform.startswith("freebsd")): oscmd("mkdir -m 0755 -p "+destdir+prefix+"/etc") @@ -201,10 +192,12 @@ def InstallPanda(destdir="", prefix="/usr", outputdir="built", libdir=GetLibDir( if os.path.isdir(outputdir+"/Pmw"): oscmd("cp -R "+outputdir+"/Pmw "+destdir+prefix+"/share/panda3d/") if os.path.isdir(outputdir+"/plugins"): oscmd("cp -R "+outputdir+"/plugins "+destdir+prefix+"/share/panda3d/") - suffix = GetExtensionSuffix() - for base in os.listdir(outputdir + "/panda3d"): - if base.endswith(".py") or (base.endswith(suffix) and '.' not in base[:-len(suffix)]): - oscmd("cp "+outputdir+"/panda3d/"+base+" "+destdir+PPATH+"/panda3d/"+base) + for python_version in python_versions: + for base in os.listdir(outputdir + "/panda3d"): + suffix = python_version["ext_suffix"] + platlib = python_version["platlib"] + if base.endswith(".py") or (base.endswith(suffix) and '.' not in base[:-len(suffix)]): + oscmd("cp "+outputdir+"/panda3d/"+base+" "+destdir+platlib+"/panda3d/"+base) WriteMimeFile(destdir+prefix+"/share/mime-info/panda3d.mime", MIME_INFO) WriteKeysFile(destdir+prefix+"/share/mime-info/panda3d.keys", MIME_INFO) @@ -214,19 +207,28 @@ def InstallPanda(destdir="", prefix="/usr", outputdir="built", libdir=GetLibDir( oscmd("cp makepanda/pview.desktop "+destdir+prefix+"/share/applications/pview.desktop") oscmd("cp doc/ReleaseNotes "+destdir+prefix+"/share/panda3d/ReleaseNotes") - oscmd("echo '"+prefix+"/share/panda3d' > "+destdir+PPATH+"/panda3d.pth") - oscmd("echo '"+libdir+"/panda3d'>> "+destdir+PPATH+"/panda3d.pth") + + for python_version in python_versions: + pth_file = python_version["purelib"] + "/panda3d.pth" + oscmd("echo '"+prefix+"/share/panda3d' > "+destdir+pth_file) + + if os.path.isdir(outputdir+"/panda3d.dist-info"): + oscmd("cp -R "+outputdir+"/panda3d.dist-info "+destdir+python_version["platlib"]) + if (sys.platform.startswith("freebsd")): oscmd("echo '"+libdir+"/panda3d'> "+destdir+"/usr/local/libdata/ldconfig/panda3d") else: oscmd("echo '"+libdir+"/panda3d'> "+destdir+"/etc/ld.so.conf.d/panda3d.conf") - oscmd("cp "+outputdir+"/bin/* "+destdir+prefix+"/bin/") for base in os.listdir(outputdir+"/lib"): if (not base.endswith(".a")) or base == "libp3pystub.a": # We really need to specify -R in order not to follow symlinks on non-GNU oscmd("cp -R -P "+outputdir+"/lib/"+base+" "+destdir+libdir+"/panda3d/"+base) + for base in os.listdir(outputdir+"/bin"): + if not base.startswith("deploy-stub"): + oscmd("cp -R -P "+outputdir+"/bin/"+base+" "+destdir+prefix+"/bin/"+base) + DeleteVCS(destdir+prefix+"/share/panda3d") DeleteBuildFiles(destdir+prefix+"/share/panda3d") DeleteEmptyDirs(destdir+prefix+"/share/panda3d") @@ -300,6 +302,8 @@ if (__name__ == "__main__"): if (destdir != "" and not os.path.isdir(destdir)): exit("Directory '%s' does not exist!" % destdir) + SetOutputDir(options.outputdir) + if options.verbose: SetVerbose(True) @@ -308,6 +312,8 @@ if (__name__ == "__main__"): InstallRuntime(destdir = destdir, prefix = options.prefix, outputdir = options.outputdir) else: print("Installing Panda3D SDK into " + destdir + options.prefix) - InstallPanda(destdir = destdir, prefix = options.prefix, outputdir = options.outputdir) + InstallPanda(destdir=destdir, + prefix=options.prefix, + outputdir=options.outputdir, + python_versions=ReadPythonVersionInfoFile()) print("Installation finished!") - diff --git a/makepanda/makepackage.py b/makepanda/makepackage.py new file mode 100755 index 0000000000..daf0877544 --- /dev/null +++ b/makepanda/makepackage.py @@ -0,0 +1,1066 @@ +#!/usr/bin/env python + +from makepandacore import * +from installpanda import * +import sys +import os +import shutil + + +INSTALLER_DEB_FILE = """ +Package: panda3dMAJOR +Version: VERSION +Section: libdevel +Priority: optional +Architecture: ARCH +Essential: no +Depends: DEPENDS +Recommends: RECOMMENDS +Provides: PROVIDES +Conflicts: PROVIDES +Replaces: PROVIDES +Maintainer: rdb +Installed-Size: INSTSIZE +Description: Panda3D free 3D engine SDK + Panda3D is a game engine which includes graphics, audio, I/O, collision detection, and other abilities relevant to the creation of 3D games. Panda3D is open source and free software under the revised BSD license, and can be used for both free and commercial game development at no financial cost. + Panda3D's intended game-development language is Python. The engine itself is written in C++, and utilizes an automatic wrapper-generator to expose the complete functionality of the engine in a Python interface. + . + This package contains the SDK for development with Panda3D. + +""" + +RUNTIME_INSTALLER_DEB_FILE = """ +Package: panda3d-runtime +Version: VERSION +Section: web +Priority: optional +Architecture: ARCH +Essential: no +Depends: DEPENDS +Provides: panda3d-runtime +Maintainer: rdb +Installed-Size: INSTSIZE +Description: Runtime binary and browser plugin for the Panda3D Game Engine + This package contains the runtime distribution and browser plugin of the Panda3D engine. It allows you view webpages that contain Panda3D content and to run games created with Panda3D that are packaged as .p3d file. + +""" + + +# We're not putting "python" in the "Requires" field, +# since the rpm-based distros don't have a common +# naming for the Python package. +INSTALLER_SPEC_FILE = """ +Summary: The Panda3D free 3D engine SDK +Name: panda3d +Version: VERSION +Release: RPMRELEASE +License: BSD License +Group: Development/Libraries +BuildRoot: PANDASOURCE/targetroot +%description +Panda3D is a game engine which includes graphics, audio, I/O, collision detection, and other abilities relevant to the creation of 3D games. Panda3D is open source and free software under the revised BSD license, and can be used for both free and commercial game development at no financial cost. +Panda3D's intended game-development language is Python. The engine itself is written in C++, and utilizes an automatic wrapper-generator to expose the complete functionality of the engine in a Python interface. + +This package contains the SDK for development with Panda3D, install panda3d-runtime for the runtime files. +%post +/sbin/ldconfig +%postun +/sbin/ldconfig +%files +%defattr(-,root,root) +/etc/Confauto.prc +/etc/Config.prc +/usr/share/panda3d +/etc/ld.so.conf.d/panda3d.conf +/usr/%_lib/panda3d +/usr/include/panda3d +""" +INSTALLER_SPEC_FILE_PVIEW = \ +"""/usr/share/applications/pview.desktop +/usr/share/mime-info/panda3d.mime +/usr/share/mime-info/panda3d.keys +/usr/share/mime/packages/panda3d.xml +/usr/share/application-registry/panda3d.applications +""" + +RUNTIME_INSTALLER_SPEC_FILE = """ +Summary: Runtime binary and browser plugin for the Panda3D Game Engine +Name: panda3d-runtime +Version: VERSION +Release: RPMRELEASE +License: BSD License +Group: Productivity/Graphics/Other +BuildRoot: PANDASOURCE/targetroot +%description +This package contains the runtime distribution and browser plugin of the Panda3D engine. It allows you view webpages that contain Panda3D content and to run games created with Panda3D that are packaged as .p3d file. +%files +%defattr(-,root,root) +/usr/bin/panda3d +/usr/%_lib/nppanda3d.so +/usr/%_lib/mozilla/plugins/nppanda3d.so +/usr/%_lib/mozilla-firefox/plugins/nppanda3d.so +/usr/%_lib/xulrunner-addons/plugins/nppanda3d.so +/usr/share/mime-info/panda3d-runtime.mime +/usr/share/mime-info/panda3d-runtime.keys +/usr/share/mime/packages/panda3d-runtime.xml +/usr/share/application-registry/panda3d-runtime.applications +/usr/share/applications/*.desktop +""" + +# plist file for Mac OSX +Info_plist = """ + + + + CFBundleIdentifier + {package_id} + CFBundleShortVersionString + {version} + IFPkgFlagRelocatable + + IFPkgFlagAuthorizationAction + RootAuthorization + IFPkgFlagAllowBackRev + + + +""" + +# FreeBSD pkg-descr +INSTALLER_PKG_DESCR_FILE = """ +Panda3D is a game engine which includes graphics, audio, I/O, collision detection, and other abilities relevant to the creation of 3D games. Panda3D is open source and free software under the revised BSD license, and can be used for both free and commercial game development at no financial cost. +Panda3D's intended game-development language is Python. The engine itself is written in C++, and utilizes an automatic wrapper-generator to expose the complete functionality of the engine in a Python interface. + +This package contains the SDK for development with Panda3D, install panda3d-runtime for the runtime files. + +WWW: https://www.panda3d.org/ +""" + +# FreeBSD pkg-descr +RUNTIME_INSTALLER_PKG_DESCR_FILE = """ +Runtime binary and browser plugin for the Panda3D Game Engine + +This package contains the runtime distribution and browser plugin of the Panda3D engine. It allows you view webpages that contain Panda3D content and to run games created with Panda3D that are packaged as .p3d file. + +WWW: https://www.panda3d.org/ +""" + +# FreeBSD PKG Manifest template file +INSTALLER_PKG_MANIFEST_FILE = """ +name: NAME +version: VERSION +arch: ARCH +origin: ORIGIN +comment: "Panda3D free 3D engine SDK" +www: https://www.panda3d.org +maintainer: rdb +prefix: /usr/local +flatsize: INSTSIZEMB +deps: {DEPENDS} +""" + + +def MakeInstallerNSIS(version, file, title, installdir, runtime=False, compressor="lzma", **kwargs): + outputdir = GetOutputDir() + + if os.path.isfile(file): + os.remove(file) + elif os.path.isdir(file): + shutil.rmtree(file) + + if GetTargetArch() == 'x64': + regview = '64' + else: + regview = '32' + + if runtime: + # Invoke the make_installer script. + AddToPathEnv("PATH", outputdir + "\\bin") + AddToPathEnv("PATH", outputdir + "\\plugins") + + cmd = sys.executable + " -B -u " + os.path.join("direct", "src", "plugin_installer", "make_installer.py") + cmd += " --version %s --regview %s" % (version, regview) + + if GetTargetArch() == 'x64': + cmd += " --install \"$PROGRAMFILES64\\Panda3D\" " + else: + cmd += " --install \"$PROGRAMFILES32\\Panda3D\" " + + oscmd(cmd) + shutil.move(os.path.join("direct", "src", "plugin_installer", "p3d-setup.exe"), file) + return + + print("Building "+title+" installer at %s" % (file)) + if compressor != "lzma": + print("Note: you are using zlib, which is faster, but lzma gives better compression.") + if os.path.exists("nsis-output.exe"): + os.remove("nsis-output.exe") + WriteFile(outputdir+"/tmp/__init__.py", "") + + nsis_defs = { + 'COMPRESSOR': compressor, + 'TITLE' : title, + 'INSTALLDIR': installdir, + 'OUTFILE' : '..\\' + file, + 'BUILT' : '..\\' + outputdir, + 'SOURCE' : '..', + 'REGVIEW' : regview, + } + + # Are we shipping a version of Python? + if os.path.isfile(os.path.join(outputdir, "python", "python.exe")): + py_dlls = glob.glob(os.path.join(outputdir, "python", "python[0-9][0-9].dll")) \ + + glob.glob(os.path.join(outputdir, "python", "python[0-9][0-9]_d.dll")) + assert py_dlls + py_dll = os.path.basename(py_dlls[0]) + pyver = py_dll[6] + "." + py_dll[7] + + if GetTargetArch() != 'x64': + pyver += '-32' + + nsis_defs['INCLUDE_PYVER'] = pyver + + if GetHost() == 'windows': + cmd = os.path.join(GetThirdpartyBase(), 'win-nsis', 'makensis') + ' /V2' + for item in nsis_defs.items(): + cmd += ' /D%s="%s"' % item + else: + cmd = 'makensis -V2' + for item in nsis_defs.items(): + cmd += ' -D%s="%s"' % item + + cmd += ' "makepanda\\installer.nsi"' + oscmd(cmd) + + +def MakeDebugSymbolArchive(zipname, dirname): + outputdir = GetOutputDir() + + import zipfile + zip = zipfile.ZipFile(zipname, 'w', zipfile.ZIP_DEFLATED) + + for fn in glob.glob(os.path.join(outputdir, 'bin', '*.pdb')): + zip.write(fn, dirname + '/bin/' + os.path.basename(fn)) + + for fn in glob.glob(os.path.join(outputdir, 'panda3d', '*.pdb')): + zip.write(fn, dirname + '/panda3d/' + os.path.basename(fn)) + + for fn in glob.glob(os.path.join(outputdir, 'plugins', '*.pdb')): + zip.write(fn, dirname + '/plugins/' + os.path.basename(fn)) + + for fn in glob.glob(os.path.join(outputdir, 'python', '*.pdb')): + zip.write(fn, dirname + '/python/' + os.path.basename(fn)) + + for fn in glob.glob(os.path.join(outputdir, 'python', 'DLLs', '*.pdb')): + zip.write(fn, dirname + '/python/DLLs/' + os.path.basename(fn)) + + zip.close() + + +def MakeInstallerLinux(version, debversion=None, rpmrelease=1, runtime=False, + python_versions=[], **kwargs): + outputdir = GetOutputDir() + + # We pack Python 2 and Python 3, if we built with support for it. + python2_ver = None + python3_ver = None + install_python_versions = [] + + if not runtime: + # What's the system version of Python 3? + oscmd('python3 -V > "%s/tmp/python3_version.txt"' % (outputdir)) + sys_python3_ver = '.'.join(ReadFile(outputdir + "/tmp/python3_version.txt").strip().split(' ')[1].split('.')[:2]) + + # Check that we built with support for these. + for version_info in python_versions: + if version_info["version"] == "2.7": + python2_ver = "2.7" + install_python_versions.append(version_info) + elif version_info["version"] == sys_python3_ver: + python3_ver = sys_python3_ver + install_python_versions.append(version_info) + + major_version = '.'.join(version.split('.')[:2]) + if not debversion: + debversion = version + + # Clean and set up a directory to install Panda3D into + oscmd("rm -rf targetroot data.tar.gz control.tar.gz panda3d.spec") + oscmd("mkdir -m 0755 targetroot") + + dpkg_present = False + if os.path.exists("/usr/bin/dpkg-architecture") and os.path.exists("/usr/bin/dpkg-deb"): + dpkg_present = True + rpmbuild_present = False + if os.path.exists("/usr/bin/rpmbuild"): + rpmbuild_present = True + + if dpkg_present and rpmbuild_present: + Warn("both dpkg and rpmbuild present.") + + if dpkg_present: + # Invoke installpanda.py to install it into a temporary dir + lib_dir = GetDebLibDir() + if runtime: + InstallRuntime(destdir="targetroot", prefix="/usr", + outputdir=outputdir, libdir=lib_dir) + else: + InstallPanda(destdir="targetroot", prefix="/usr", + outputdir=outputdir, libdir=lib_dir, + python_versions=install_python_versions) + oscmd("chmod -R 755 targetroot/usr/share/panda3d") + oscmd("mkdir -m 0755 -p targetroot/usr/share/man/man1") + oscmd("install -m 0644 doc/man/*.1 targetroot/usr/share/man/man1/") + + oscmd("dpkg --print-architecture > "+outputdir+"/tmp/architecture.txt") + pkg_arch = ReadFile(outputdir+"/tmp/architecture.txt").strip() + if runtime: + txt = RUNTIME_INSTALLER_DEB_FILE[1:] + else: + txt = INSTALLER_DEB_FILE[1:] + txt = txt.replace("VERSION", debversion).replace("ARCH", pkg_arch).replace("MAJOR", major_version) + txt = txt.replace("INSTSIZE", str(GetDirectorySize("targetroot") // 1024)) + + oscmd("mkdir -m 0755 -p targetroot/DEBIAN") + oscmd("cd targetroot && (find usr -type f -exec md5sum {} ;) > DEBIAN/md5sums") + if not runtime: + oscmd("cd targetroot && (find etc -type f -exec md5sum {} ;) >> DEBIAN/md5sums") + WriteFile("targetroot/DEBIAN/conffiles","/etc/Config.prc\n") + WriteFile("targetroot/DEBIAN/postinst","#!/bin/sh\necho running ldconfig\nldconfig\n") + oscmd("cp targetroot/DEBIAN/postinst targetroot/DEBIAN/postrm") + + # Determine the package name and the locations that + # dpkg-shlibdeps should look in for executables. + pkg_version = debversion + if runtime: + pkg_name = "panda3d-runtime" + lib_pattern = "debian/%s/usr/%s/*.so" % (pkg_name, lib_dir) + else: + pkg_name = "panda3d" + major_version + lib_pattern = "debian/%s/usr/%s/panda3d/*.so*" % (pkg_name, lib_dir) + bin_pattern = "debian/%s/usr/bin/*" % (pkg_name) + + # dpkg-shlibdeps looks in the debian/{pkg_name}/DEBIAN/shlibs directory + # and also expects a debian/control file, so we create this dummy set-up. + oscmd("mkdir targetroot/debian") + oscmd("ln -s .. targetroot/debian/" + pkg_name) + WriteFile("targetroot/debian/control", "") + + dpkg_shlibdeps = "dpkg-shlibdeps" + if GetVerbose(): + dpkg_shlibdeps += " -v" + + if runtime: + # The runtime doesn't export any useful symbols, so just query the dependencies. + oscmd("cd targetroot && %(dpkg_shlibdeps)s -x%(pkg_name)s %(lib_pattern)s %(bin_pattern)s*" % locals()) + depends = ReadFile("targetroot/debian/substvars").replace("shlibs:Depends=", "").strip() + recommends = "" + provides = "panda3d-runtime" + else: + pkg_name = "panda3d" + major_version + pkg_dir = "debian/panda3d" + major_version + + # Generate a symbols file so that other packages can know which symbols we export. + oscmd("cd targetroot && dpkg-gensymbols -q -ODEBIAN/symbols -v%(pkg_version)s -p%(pkg_name)s -e%(lib_pattern)s" % locals()) + + # Library dependencies are required, binary dependencies are recommended. + # We explicitly exclude libphysx-extras since we don't want to depend on PhysX. + oscmd("cd targetroot && LD_LIBRARY_PATH=usr/%(lib_dir)s/panda3d %(dpkg_shlibdeps)s -Tdebian/substvars_dep --ignore-missing-info -x%(pkg_name)s -xlibphysx-extras %(lib_pattern)s" % locals()) + oscmd("cd targetroot && LD_LIBRARY_PATH=usr/%(lib_dir)s/panda3d %(dpkg_shlibdeps)s -Tdebian/substvars_rec --ignore-missing-info -x%(pkg_name)s %(bin_pattern)s" % locals()) + + # Parse the substvars files generated by dpkg-shlibdeps. + depends = ReadFile("targetroot/debian/substvars_dep").replace("shlibs:Depends=", "").strip() + recommends = ReadFile("targetroot/debian/substvars_rec").replace("shlibs:Depends=", "").strip() + provides = "panda3d" + + if python2_ver or python3_ver: + recommends += ", python-pmw" + + if python2_ver: + depends += ", python%s" % (python2_ver) + recommends += ", python-wxversion" + recommends += ", python-tk (>= %s)" % (python2_ver) + provides += ", python2-panda3d" + + if python3_ver: + depends += ", python%s" % (python3_ver) + recommends += ", python3-tk (>= %s)" % (python3_ver) + provides += ", python3-panda3d" + + if not PkgSkip("NVIDIACG"): + depends += ", nvidia-cg-toolkit" + + # Write back the dependencies, and delete the dummy set-up. + txt = txt.replace("DEPENDS", depends.strip(', ')) + txt = txt.replace("RECOMMENDS", recommends.strip(', ')) + txt = txt.replace("PROVIDES", provides.strip(', ')) + WriteFile("targetroot/DEBIAN/control", txt) + oscmd("rm -rf targetroot/debian") + + # Package it all up into a .deb file. + oscmd("chmod -R 755 targetroot/DEBIAN") + oscmd("chmod 644 targetroot/DEBIAN/control targetroot/DEBIAN/md5sums") + if not runtime: + oscmd("chmod 644 targetroot/DEBIAN/conffiles targetroot/DEBIAN/symbols") + oscmd("fakeroot dpkg-deb -b targetroot %s_%s_%s.deb" % (pkg_name, pkg_version, pkg_arch)) + + elif rpmbuild_present: + # Invoke installpanda.py to install it into a temporary dir + if runtime: + InstallRuntime(destdir="targetroot", prefix="/usr", outputdir=outputdir, libdir=GetRPMLibDir()) + else: + InstallPanda(destdir="targetroot", prefix="/usr", + outputdir=outputdir, libdir=GetRPMLibDir(), + python_versions=install_python_versions) + oscmd("chmod -R 755 targetroot/usr/share/panda3d") + + oscmd("rpm -E '%_target_cpu' > "+outputdir+"/tmp/architecture.txt") + arch = ReadFile(outputdir+"/tmp/architecture.txt").strip() + pandasource = os.path.abspath(os.getcwd()) + + if runtime: + txt = RUNTIME_INSTALLER_SPEC_FILE[1:] + else: + txt = INSTALLER_SPEC_FILE[1:] + + # Add the MIME associations if we have pview + if not PkgSkip("PVIEW"): + txt += INSTALLER_SPEC_FILE_PVIEW + + # Add the platform-specific Python directories. + dirs = set() + for version_info in install_python_versions: + dirs.add(version_info["platlib"]) + dirs.add(version_info["purelib"]) + + for dir in dirs: + txt += dir + "\n" + + # Add the binaries in /usr/bin explicitly to the spec file + for base in os.listdir(outputdir + "/bin"): + txt += "/usr/bin/%s\n" % (base) + + # Write out the spec file. + txt = txt.replace("VERSION", version) + txt = txt.replace("RPMRELEASE", str(rpmrelease)) + txt = txt.replace("PANDASOURCE", pandasource) + WriteFile("panda3d.spec", txt) + + oscmd("fakeroot rpmbuild --define '_rpmdir "+pandasource+"' --buildroot '"+os.path.abspath("targetroot")+"' -bb panda3d.spec") + if runtime: + oscmd("mv "+arch+"/panda3d-runtime-"+version+"-"+rpmrelease+"."+arch+".rpm .") + else: + oscmd("mv "+arch+"/panda3d-"+version+"-"+rpmrelease+"."+arch+".rpm .") + oscmd("rm -rf "+arch, True) + + else: + exit("To build an installer, either rpmbuild or dpkg-deb must be present on your system!") + + +def MakeInstallerOSX(version, runtime=False, python_versions=[], **kwargs): + outputdir = GetOutputDir() + + if runtime: + # Invoke the make_installer script. + AddToPathEnv("DYLD_LIBRARY_PATH", outputdir + "/plugins") + cmdstr = sys.executable + " " + if sys.version_info >= (2, 6): + cmdstr += "-B " + + cmdstr += "direct/src/plugin_installer/make_installer.py --version %s" % version + oscmd(cmdstr) + return + + dmg_name = "Panda3D-" + version + if len(python_versions) == 1 and not python_versions[0]["version"].startswith("2."): + dmg_name += "-py" + pyver + dmg_name += ".dmg" + + if (os.path.isfile(dmg_name)): oscmd("rm -f %s" % dmg_name) + if (os.path.exists("dstroot")): oscmd("rm -rf dstroot") + if (os.path.exists("Panda3D-rw.dmg")): oscmd('rm -f Panda3D-rw.dmg') + + oscmd("mkdir -p dstroot/base/Developer/Panda3D/lib") + oscmd("mkdir -p dstroot/base/Developer/Panda3D/etc") + oscmd("cp %s/etc/Config.prc dstroot/base/Developer/Panda3D/etc/Config.prc" % outputdir) + oscmd("cp %s/etc/Confauto.prc dstroot/base/Developer/Panda3D/etc/Confauto.prc" % outputdir) + oscmd("cp -R %s/models dstroot/base/Developer/Panda3D/models" % outputdir) + oscmd("cp -R doc/LICENSE dstroot/base/Developer/Panda3D/LICENSE") + oscmd("cp -R doc/ReleaseNotes dstroot/base/Developer/Panda3D/ReleaseNotes") + oscmd("cp -R %s/Frameworks dstroot/base/Developer/Panda3D/Frameworks" % outputdir) + if os.path.isdir(outputdir+"/plugins"): + oscmd("cp -R %s/plugins dstroot/base/Developer/Panda3D/plugins" % outputdir) + + # Libraries that shouldn't be in base, but are instead in other modules. + no_base_libs = ['libp3ffmpeg', 'libp3fmod_audio', 'libfmodex', 'libfmodexL'] + + for base in os.listdir(outputdir+"/lib"): + if not base.endswith(".a") and base.split('.')[0] not in no_base_libs: + libname = "dstroot/base/Developer/Panda3D/lib/" + base + # We really need to specify -R in order not to follow symlinks + # On OSX, just specifying -P is not enough to do that. + oscmd("cp -R -P " + outputdir + "/lib/" + base + " " + libname) + + oscmd("mkdir -p dstroot/tools/Developer/Panda3D/bin") + oscmd("mkdir -p dstroot/tools/Developer/Tools") + oscmd("ln -s ../Panda3D/bin dstroot/tools/Developer/Tools/Panda3D") + oscmd("mkdir -p dstroot/tools/etc/paths.d") + # Trailing newline is important, works around a bug in OSX + WriteFile("dstroot/tools/etc/paths.d/Panda3D", "/Developer/Panda3D/bin\n") + + oscmd("mkdir -m 0755 -p dstroot/tools/usr/local/share/man/man1") + oscmd("install -m 0644 doc/man/*.1 dstroot/tools/usr/local/share/man/man1/") + + for base in os.listdir(outputdir+"/bin"): + if not base.startswith("deploy-stub"): + binname = "dstroot/tools/Developer/Panda3D/bin/" + base + # OSX needs the -R argument to copy symbolic links correctly, it doesn't have -d. How weird. + oscmd("cp -R " + outputdir + "/bin/" + base + " " + binname) + + if python_versions: + # Let's only write a ppython link if there is only one Python version. + if len(python_versions) == 1: + oscmd("mkdir -p dstroot/pythoncode/usr/local/bin") + oscmd("ln -s %s dstroot/pythoncode/usr/local/bin/ppython" % (python_versions[0]["executable"])) + + oscmd("mkdir -p dstroot/pythoncode/Developer/Panda3D/panda3d") + oscmd("cp -R %s/pandac dstroot/pythoncode/Developer/Panda3D/pandac" % outputdir) + oscmd("cp -R %s/direct dstroot/pythoncode/Developer/Panda3D/direct" % outputdir) + oscmd("cp -R %s/*.so dstroot/pythoncode/Developer/Panda3D/" % outputdir, True) + oscmd("cp -R %s/*.py dstroot/pythoncode/Developer/Panda3D/" % outputdir, True) + if os.path.isdir(outputdir+"/Pmw"): + oscmd("cp -R %s/Pmw dstroot/pythoncode/Developer/Panda3D/Pmw" % outputdir) + + # Copy over panda3d.dist-info directory. + if os.path.isdir(outputdir + "/panda3d.dist-info"): + oscmd("cp -R %s/panda3d.dist-info dstroot/pythoncode/Developer/Panda3D/panda3d.dist-info" % (outputdir)) + + for base in os.listdir(outputdir+"/panda3d"): + if base.endswith('.py'): + libname = "dstroot/pythoncode/Developer/Panda3D/panda3d/" + base + oscmd("cp -R " + outputdir + "/panda3d/" + base + " " + libname) + + for version_info in python_versions: + pyver = version_info["version"] + oscmd("mkdir -p dstroot/pybindings%s/Library/Python/%s/site-packages" % (pyver, pyver)) + oscmd("mkdir -p dstroot/pybindings%s/Developer/Panda3D/panda3d" % (pyver)) + + # Copy over extension modules. + suffix = version_info["ext_suffix"] + for base in os.listdir(outputdir+"/panda3d"): + if base.endswith(suffix) and '.' not in base[:-len(suffix)]: + libname = "dstroot/pybindings%s/Developer/Panda3D/panda3d/%s" % (pyver, base) + # We really need to specify -R in order not to follow symlinks + # On OSX, just specifying -P is not enough to do that. + oscmd("cp -R -P " + outputdir + "/panda3d/" + base + " " + libname) + + # Write a .pth file. + oscmd("mkdir -p dstroot/pybindings%s/Library/Python/%s/site-packages" % (pyver, pyver)) + WriteFile("dstroot/pybindings%s/Library/Python/%s/site-packages/Panda3D.pth" % (pyver, pyver), "/Developer/Panda3D") + + # Evidently not all Python 3.7 installations read the above path, so do this for now + # See GitHub #502 + if pyver == "3.7": + dir = "dstroot/pybindings%s/Library/Frameworks/Python.framework/Versions/%s/lib/python%s/site-packages" % (pyver, pyver, pyver) + oscmd("mkdir -p %s" % (dir)) + WriteFile("%s/Panda3D.pth" % (dir), "/Developer/Panda3D") + + if not PkgSkip("FFMPEG"): + oscmd("mkdir -p dstroot/ffmpeg/Developer/Panda3D/lib") + oscmd("cp -R %s/lib/libp3ffmpeg.* dstroot/ffmpeg/Developer/Panda3D/lib/" % outputdir) + + #if not PkgSkip("OPENAL"): + # oscmd("mkdir -p dstroot/openal/Developer/Panda3D/lib") + # oscmd("cp -R %s/lib/libp3openal_audio.* dstroot/openal/Developer/Panda3D/lib/" % outputdir) + + if not PkgSkip("FMODEX"): + oscmd("mkdir -p dstroot/fmodex/Developer/Panda3D/lib") + oscmd("cp -R %s/lib/libp3fmod_audio.* dstroot/fmodex/Developer/Panda3D/lib/" % outputdir) + oscmd("cp -R %s/lib/libfmodex* dstroot/fmodex/Developer/Panda3D/lib/" % outputdir) + + oscmd("mkdir -p dstroot/headers/Developer/Panda3D/lib") + oscmd("cp -R %s/include dstroot/headers/Developer/Panda3D/include" % outputdir) + if os.path.isfile(outputdir + "/lib/libp3pystub.a"): + oscmd("cp -R -P %s/lib/libp3pystub.a dstroot/headers/Developer/Panda3D/lib/" % outputdir) + + if os.path.isdir("samples"): + oscmd("mkdir -p dstroot/samples/Developer/Examples/Panda3D") + oscmd("cp -R samples/* dstroot/samples/Developer/Examples/Panda3D/") + + DeleteVCS("dstroot") + DeleteBuildFiles("dstroot") + + # Compile Python files. Do this *after* the DeleteVCS step, above, which + # deletes __pycache__ directories. + for version_info in python_versions: + if os.path.isdir("dstroot/pythoncode/Developer/Panda3D/Pmw"): + oscmd("%s -m compileall -q -f -d /Developer/Panda3D/Pmw dstroot/pythoncode/Developer/Panda3D/Pmw" % (version_info["executable"]), True) + oscmd("%s -m compileall -q -f -d /Developer/Panda3D/direct dstroot/pythoncode/Developer/Panda3D/direct" % (version_info["executable"])) + oscmd("%s -m compileall -q -f -d /Developer/Panda3D/pandac dstroot/pythoncode/Developer/Panda3D/pandac" % (version_info["executable"])) + oscmd("%s -m compileall -q -f -d /Developer/Panda3D/panda3d dstroot/pythoncode/Developer/Panda3D/panda3d" % (version_info["executable"])) + + oscmd("chmod -R 0775 dstroot/*") + # We need to be root to perform a chown. Bleh. + # Fortunately PackageMaker does it for us, on 10.5 and above. + #oscmd("chown -R root:admin dstroot/*", True) + + oscmd("mkdir -p dstroot/Panda3D/Panda3D.mpkg/Contents/Packages/") + oscmd("mkdir -p dstroot/Panda3D/Panda3D.mpkg/Contents/Resources/en.lproj/") + + pkgs = ["base", "tools", "headers"] + if python_versions: + pkgs.append("pythoncode") + for version_info in python_versions: + pkgs.append("pybindings" + version_info["version"]) + if not PkgSkip("FFMPEG"): pkgs.append("ffmpeg") + #if not PkgSkip("OPENAL"): pkgs.append("openal") + if not PkgSkip("FMODEX"): pkgs.append("fmodex") + if os.path.isdir("samples"): pkgs.append("samples") + for pkg in pkgs: + identifier = "org.panda3d.panda3d.%s.pkg" % pkg + plist = open("/tmp/Info_plist", "w") + plist.write(Info_plist.format(package_id=identifier, version=version)) + plist.close() + if not os.path.isdir("dstroot/" + pkg): + os.makedirs("dstroot/" + pkg) + + if os.path.exists("/usr/bin/pkgbuild"): + # This new package builder is used in Lion and above. + cmd = '/usr/bin/pkgbuild --identifier ' + identifier + ' --version ' + version + ' --root dstroot/' + pkg + '/ dstroot/Panda3D/Panda3D.mpkg/Contents/Packages/' + pkg + '.pkg' + + # In older versions, we use PackageMaker. Apple keeps changing its location. + elif os.path.exists("/Developer/usr/bin/packagemaker"): + cmd = '/Developer/usr/bin/packagemaker --info /tmp/Info_plist --version ' + version + ' --out dstroot/Panda3D/Panda3D.mpkg/Contents/Packages/' + pkg + '.pkg ' + target + ' --domain system --root dstroot/' + pkg + '/ --no-relocate' + elif os.path.exists("/Applications/Xcode.app/Contents/Applications/PackageMaker.app/Contents/MacOS/PackageMaker"): + cmd = '/Applications/Xcode.app/Contents/Applications/PackageMaker.app/Contents/MacOS/PackageMaker --info /tmp/Info_plist --version ' + version + ' --out dstroot/Panda3D/Panda3D.mpkg/Contents/Packages/' + pkg + '.pkg ' + target + ' --domain system --root dstroot/' + pkg + '/ --no-relocate' + elif os.path.exists("/Developer/Tools/PackageMaker.app/Contents/MacOS/PackageMaker"): + cmd = '/Developer/Tools/PackageMaker.app/Contents/MacOS/PackageMaker --info /tmp/Info_plist --version ' + version + ' --out dstroot/Panda3D/Panda3D.mpkg/Contents/Packages/' + pkg + '.pkg ' + target + ' --domain system --root dstroot/' + pkg + '/ --no-relocate' + elif os.path.exists("/Developer/Tools/packagemaker"): + cmd = '/Developer/Tools/packagemaker -build -f dstroot/' + pkg + '/ -p dstroot/Panda3D/Panda3D.mpkg/Contents/Packages/' + pkg + '.pkg -i /tmp/Info_plist' + elif os.path.exists("/Applications/PackageMaker.app/Contents/MacOS/PackageMaker"): + cmd = '/Applications/PackageMaker.app/Contents/MacOS/PackageMaker --info /tmp/Info_plist --version ' + version + ' --out dstroot/Panda3D/Panda3D.mpkg/Contents/Packages/' + pkg + '.pkg ' + target + ' --domain system --root dstroot/' + pkg + '/ --no-relocate' + else: + exit("Neither pkgbuild nor PackageMaker could be found!") + oscmd(cmd) + + if os.path.isfile("/tmp/Info_plist"): + oscmd("rm -f /tmp/Info_plist") + + # Now that we've built all of the individual packages, build the metapackage. + dist = open("dstroot/Panda3D/Panda3D.mpkg/Contents/distribution.dist", "w") + dist.write('\n') + dist.write('\n') + dist.write(' Panda3D SDK %s\n' % (version)) + dist.write(' \n') + dist.write(' %s\n' % ReadFile("doc/LICENSE")) + dist.write(' \n') + dist.write(' \n') + dist.write(' \n') + if python_versions: + dist.write(' \n') + for version_info in sorted(python_versions, key=lambda info:info["version"], reverse=True): + dist.write(' \n' % (version_info["version"])) + dist.write(' \n') + dist.write(' \n') + if os.path.isdir("samples"): + dist.write(' \n') + if not PkgSkip("FFMPEG"): + dist.write(' \n') + if not PkgSkip("FMODEX"): + dist.write(' \n') + dist.write(' \n') + dist.write(' \n') + dist.write(' \n') + dist.write(' \n') + dist.write(' \n') + dist.write(' \n') + dist.write(' \n') + dist.write(' \n') + + if python_versions: + dist.write(' \n') + dist.write(' \n') + dist.write(' \n') + + for version_info in python_versions: + pyver = version_info["version"] + if pyver == "2.7": + # Always install Python 2.7 by default; it's included on macOS. + cond = "true" + else: + cond = "isPythonVersionInstalled('%s')" % (pyver) + dist.write(' \n' % (pyver, cond, pyver, pyver)) + dist.write(' \n' % (pyver)) + dist.write(' \n') + + if not PkgSkip("FFMPEG"): + dist.write(' \n') + elif PkgSkip("VORBIS"): + dist.write(' It is not required for loading .wav or .opus files, which Panda3D can read out of the box.">\n') + elif PkgSkip("OPUS"): + dist.write(' It is not required for loading .wav or .ogg files, which Panda3D can read out of the box.">\n') + else: + dist.write(' It is not required for loading .wav, .ogg or .opus files, which Panda3D can read out of the box.">\n') + dist.write(' \n') + dist.write(' \n') + + #if not PkgSkip("OPENAL"): + # dist.write(' \n') + # dist.write(' \n') + # dist.write(' \n') + + if not PkgSkip("FMODEX"): + dist.write(' \n') + dist.write(' \n') + dist.write(' \n') + + if os.path.isdir("samples"): + dist.write(' \n') + dist.write(' \n') + dist.write(' \n') + + dist.write(' \n') + dist.write(' \n') + dist.write(' \n') + for pkg in pkgs: + size = GetDirectorySize("dstroot/" + pkg) // 1024 + dist.write(' file:./Contents/Packages/%s.pkg\n' % (pkg, size, pkg)) + dist.write('\n') + dist.close() + + oscmd('hdiutil create Panda3D-rw.dmg -volname "Panda3D SDK %s" -srcfolder dstroot/Panda3D' % (version)) + oscmd('hdiutil convert Panda3D-rw.dmg -format UDBZ -o %s' % (dmg_name)) + oscmd('rm -f Panda3D-rw.dmg') + + +def MakeInstallerFreeBSD(version, runtime=False, **kwargs): + outputdir = GetOutputDir() + + oscmd("rm -rf targetroot +DESC pkg-plist +MANIFEST") + oscmd("mkdir targetroot") + + # Invoke installpanda.py to install it into a temporary dir + if runtime: + InstallRuntime(destdir="targetroot", prefix="/usr/local", outputdir=outputdir) + else: + InstallPanda(destdir="targetroot", prefix="/usr/local", outputdir=outputdir) + + if not os.path.exists("/usr/sbin/pkg"): + exit("Cannot create an installer without pkg") + + plist_txt = '' + for root, dirs, files in os.walk("targetroot/usr/local/", True): + for f in files: + plist_txt += os.path.join(root, f)[21:] + "\n" + + if not runtime: + plist_txt += "@postexec /sbin/ldconfig -m /usr/local/lib/panda3d\n" + plist_txt += "@postunexec /sbin/ldconfig -R\n" + + for remdir in ("lib/panda3d", "share/panda3d", "include/panda3d"): + for root, dirs, files in os.walk("targetroot/usr/local/" + remdir, False): + for d in dirs: + plist_txt += "@dir %s\n" % os.path.join(root, d)[21:] + plist_txt += "@dir %s\n" % remdir + + oscmd("echo \"`pkg config abi | tr '[:upper:]' '[:lower:]' | cut -d: -f1,2`:*\" > " + outputdir + "/tmp/architecture.txt") + pkg_arch = ReadFile(outputdir+"/tmp/architecture.txt").strip() + + dependencies = '' + if not PkgSkip("PYTHON"): + # If this version of Python was installed from a package or ports, let's mark it as dependency. + oscmd("rm -f %s/tmp/python_dep" % outputdir) + + if "PYTHONVERSION" in SDK: + pyver_nodot = SDK["PYTHONVERSION"][6:9:2] + else: + pyver_nodot = "%d%d" % (sys.version_info[:2]) + + oscmd("pkg query \"\n\t%%n : {\n\t\torigin : %%o,\n\t\tversion : %%v\n\t},\n\" python%s > %s/tmp/python_dep" % (pyver_nodot, outputdir), True) + if os.path.isfile(outputdir + "/tmp/python_dep"): + python_pkg = ReadFile(outputdir + "/tmp/python_dep") + if python_pkg: + dependencies += python_pkg + + manifest_txt = INSTALLER_PKG_MANIFEST_FILE[1:].replace("NAME", 'panda3d' if not runtime else 'panda3d-runtime') + manifest_txt = manifest_txt.replace("VERSION", version) + manifest_txt = manifest_txt.replace("ARCH", pkg_arch) + manifest_txt = manifest_txt.replace("ORIGIN", 'devel/panda3d' if not runtime else 'graphics/panda3d-runtime') + manifest_txt = manifest_txt.replace("DEPENDS", dependencies) + manifest_txt = manifest_txt.replace("INSTSIZE", str(GetDirectorySize("targetroot") // 1024 // 1024)) + + WriteFile("pkg-plist", plist_txt) + WriteFile("+DESC", INSTALLER_PKG_DESCR_FILE[1:] if not runtime else RUNTIME_INSTALLER_PKG_DESCR_FILE[1:]) + WriteFile("+MANIFEST", manifest_txt) + oscmd("pkg create -p pkg-plist -r %s -m . -o . %s" % (os.path.abspath("targetroot"), "--verbose" if GetVerbose() else "--quiet")) + + +def MakeInstallerAndroid(version, **kwargs): + outputdir = GetOutputDir() + oscmd("rm -rf apkroot") + oscmd("mkdir apkroot") + + # Also remove the temporary apks. + apk_unaligned = os.path.join(outputdir, "tmp", "panda3d-unaligned.apk") + apk_unsigned = os.path.join(outputdir, "tmp", "panda3d-unsigned.apk") + if os.path.exists(apk_unaligned): + os.unlink(apk_unaligned) + if os.path.exists(apk_unsigned): + os.unlink(apk_unsigned) + + # Compile the Java classes into a Dalvik executable. + dx_cmd = "dx --dex --output=apkroot/classes.dex " + if GetOptimize() <= 2: + dx_cmd += "--debug " + if GetVerbose(): + dx_cmd += "--verbose " + if "ANDROID_API" in SDK: + dx_cmd += "--min-sdk-version=%d " % (SDK["ANDROID_API"]) + dx_cmd += os.path.join(outputdir, "classes") + oscmd(dx_cmd) + + # Copy the libraries one by one. In case of library dependencies, strip + # off any suffix (eg. libfile.so.1.0), as Android does not support them. + source_dir = os.path.join(outputdir, "lib") + target_dir = os.path.join("apkroot", "lib", SDK["ANDROID_ABI"]) + oscmd("mkdir -p %s" % (target_dir)) + + # Determine the library directories we should look in. + libpath = [source_dir] + for dir in os.environ.get("LD_LIBRARY_PATH", "").split(':'): + dir = os.path.expandvars(dir) + dir = os.path.expanduser(dir) + if os.path.isdir(dir): + dir = os.path.realpath(dir) + if not dir.startswith("/system") and not dir.startswith("/vendor"): + libpath.append(dir) + + def copy_library(source, base): + # Copy file to destination, stripping version suffix. + target = os.path.join(target_dir, base) + if not target.endswith('.so'): + target = target.rpartition('.so.')[0] + '.so' + + if os.path.isfile(target): + # Already processed. + return + + oscmd("cp %s %s" % (source, target)) + + # Walk through the library dependencies. + oscmd("ldd %s | grep .so > %s/tmp/otool-libs.txt" % (target, outputdir), True) + for line in open(outputdir + "/tmp/otool-libs.txt", "r"): + line = line.strip() + if not line: + continue + if '.so.' in line: + dep = line.rpartition('.so.')[0] + '.so' + oscmd("patchelf --replace-needed %s %s %s" % (line, dep, target), True) + else: + dep = line + + # Find it on the LD_LIBRARY_PATH. + for dir in libpath: + fulldep = os.path.join(dir, dep) + if os.path.isfile(fulldep): + copy_library(os.path.realpath(fulldep), dep) + break + + # Now copy every lib in the lib dir, and its dependencies. + for base in os.listdir(source_dir): + if not base.startswith('lib'): + continue + if not base.endswith('.so') and '.so.' not in base: + continue + + source = os.path.join(source_dir, base) + if os.path.islink(source): + continue + copy_library(source, base) + + # Same for Python extension modules. However, Android is strict about + # library naming, so we have a special naming scheme for these, in + # conjunction with a custom import hook to find these modules. + if not PkgSkip("PYTHON"): + suffix = GetExtensionSuffix() + source_dir = os.path.join(outputdir, "panda3d") + for base in os.listdir(source_dir): + if not base.endswith(suffix): + continue + modname = base[:-len(suffix)] + if '.' not in modname: + source = os.path.join(source_dir, base) + copy_library(source, "libpy.panda3d.{}.so".format(modname)) + + # Same for standard Python modules. + import _ctypes + source_dir = os.path.dirname(_ctypes.__file__) + for base in os.listdir(source_dir): + if not base.endswith('.so'): + continue + modname = base.partition('.')[0] + source = os.path.join(source_dir, base) + copy_library(source, "libpy.{}.so".format(modname)) + + def copy_python_tree(source_root, target_root): + for source_dir, dirs, files in os.walk(source_root): + if 'site-packages' in dirs: + dirs.remove('site-packages') + + if not any(base.endswith('.py') for base in files): + continue + + target_dir = os.path.join(target_root, os.path.relpath(source_dir, source_root)) + target_dir = os.path.normpath(target_dir) + os.makedirs(target_dir, 0o755) + + for base in files: + if base.endswith('.py'): + target = os.path.join(target_dir, base) + shutil.copy(os.path.join(source_dir, base), target) + + # Copy the Python standard library to the .apk as well. + from distutils.sysconfig import get_python_lib + stdlib_source = get_python_lib(False, True) + stdlib_target = os.path.join("apkroot", "lib", "python{0}.{1}".format(*sys.version_info)) + copy_python_tree(stdlib_source, stdlib_target) + + # But also copy over our custom site.py. + shutil.copy("panda/src/android/site.py", os.path.join(stdlib_target, "site.py")) + + # And now make a site-packages directory containing our direct/panda3d/pandac modules. + for tree in "panda3d", "direct", "pandac": + copy_python_tree(os.path.join(outputdir, tree), os.path.join(stdlib_target, "site-packages", tree)) + + # Copy the models and config files to the virtual assets filesystem. + oscmd("mkdir apkroot/assets") + oscmd("cp -R %s apkroot/assets/models" % (os.path.join(outputdir, "models"))) + oscmd("cp -R %s apkroot/assets/etc" % (os.path.join(outputdir, "etc"))) + + # Make an empty res folder. It's needed for the apk to be installable, apparently. + oscmd("mkdir apkroot/res") + + # Now package up the application + oscmd("cp panda/src/android/pview_manifest.xml apkroot/AndroidManifest.xml") + aapt_cmd = "aapt package" + aapt_cmd += " -F %s" % (apk_unaligned) + aapt_cmd += " -M apkroot/AndroidManifest.xml" + aapt_cmd += " -A apkroot/assets -S apkroot/res" + aapt_cmd += " -I $PREFIX/share/aapt/android.jar" + oscmd(aapt_cmd) + + # And add all the libraries to it. + oscmd("cd apkroot && aapt add ../%s classes.dex" % (apk_unaligned)) + for path, dirs, files in os.walk('apkroot/lib'): + if files: + rel = os.path.relpath(path, 'apkroot') + oscmd("cd apkroot && aapt add ../%s %s/*" % (apk_unaligned, rel)) + + # Now align the .apk, which is necessary for Android to load it. + oscmd("zipalign -v -p 4 %s %s" % (apk_unaligned, apk_unsigned)) + + # Finally, sign it using a debug key. This is generated if it doesn't exist. + oscmd("apksigner debug.ks %s panda3d.apk" % (apk_unsigned)) + + # Clean up. + oscmd("rm -rf apkroot") + os.unlink(apk_unaligned) + os.unlink(apk_unsigned) + + +def MakeInstaller(version, **kwargs): + target = GetTarget() + if target == 'windows': + fn = "Panda3D-" + dir = "Panda3D-" + version + + runtime = kwargs.get('runtime', False) + if runtime: + fn += "Runtime-" + title = "Panda3D " + version + else: + title = "Panda3D SDK " + version + + fn += version + + python_versions = kwargs.get('python_versions', []) + if not runtime and len(python_versions) == 1 and python_versions[0]["version"] != "2.7": + fn += '-py' + python_versions[0]["version"] + + if GetOptimize() <= 2: + fn += "-dbg" + if GetTargetArch() == 'x64': + fn += '-x64' + dir += '-x64' + + MakeInstallerNSIS(version, fn + '.exe', title, 'C:\\' + dir, **kwargs) + if not runtime: + MakeDebugSymbolArchive(fn + '-pdb.zip', dir) + elif target == 'linux': + MakeInstallerLinux(version, **kwargs) + elif target == 'darwin': + MakeInstallerOSX(version, **kwargs) + elif target == 'freebsd': + MakeInstallerFreeBSD(version, **kwargs) + elif target == 'android': + MakeInstallerAndroid(version, **kwargs) + else: + exit("Do not know how to make an installer for this platform") + + +if __name__ == "__main__": + version = ParsePandaVersion("dtool/PandaVersion.pp") + + parser = OptionParser() + parser.add_option('', '--version', dest='version', help='Panda3D version number (default: %s)' % (version), default=version) + parser.add_option('', '--debversion', dest='debversion', help='Version number for .deb file', default=None) + parser.add_option('', '--rpmrelease', dest='rpmrelease', help='Release number for .rpm file', default='1') + parser.add_option('', '--outputdir', dest='outputdir', help='Makepanda\'s output directory (default: built)', default='built') + parser.add_option('', '--verbose', dest='verbose', help='Enable verbose output', action='store_true', default=False) + parser.add_option('', '--runtime', dest='runtime', help='Runtime instead of SDK', action='store_true', default=False) + parser.add_option('', '--lzma', dest='compressor', help='Use LZMA compression', action='store_const', const='lzma', default='zlib') + (options, args) = parser.parse_args() + + SetVerbose(options.verbose) + SetOutputDir(options.outputdir) + + # Read out the optimize option. + opt = ReadFile(os.path.join(options.outputdir, "tmp", "optimize.dat")) + SetOptimize(int(opt.strip())) + + # Read out whether we should set PkgSkip("PYTHON") and some others. + # Temporary hack; needs better solution. + pkg_list = "PYTHON", "NVIDIACG", "FFMPEG", "OPENAL", "FMODEX", "PVIEW", "NVIDIACG", "VORBIS", "OPUS" + PkgListSet(pkg_list) + for pkg in pkg_list: + dat_path = "dtool_have_%s.dat" % (pkg.lower()) + content = ReadFile(os.path.join(options.outputdir, "tmp", dat_path)) + if int(content.strip()): + PkgEnable(pkg) + else: + PkgDisable(pkg) + + # Parse the version. + match = re.match(r'^\d+\.\d+\.\d+', options.version) + if not match: + exit("version requires three digits") + + MakeInstaller(version=match.group(), + outputdir=options.outputdir, + optimize=GetOptimize(), + compressor=options.compressor, + debversion=options.debversion, + rpmrelease=options.rpmrelease, + runtime=options.runtime, + python_versions=ReadPythonVersionInfoFile()) diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index c6a01e7cd5..40febe3cd0 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -23,7 +23,7 @@ except: exit(1) from makepandacore import * -from installpanda import * +from distutils.util import get_platform import time import os import sys @@ -71,6 +71,8 @@ WINDOWS_SDK = None MSVC_VERSION = None BOOUSEINTELCOMPILER = False OPENCV_VER_23 = False +PLATFORM = None +COPY_PYTHON = True if "MACOSX_DEPLOYMENT_TARGET" in os.environ: OSXTARGET=os.environ["MACOSX_DEPLOYMENT_TARGET"] @@ -91,7 +93,7 @@ PkgListSet(["PYTHON", "DIRECT", # Python support "ARTOOLKIT", "OPENCV", "DIRECTCAM", "VISION", # Augmented Reality "GTK2", # GTK2 is used for PStats on Unix "MFC", "WX", "FLTK", # Used for web plug-in only - "ROCKET", "AWESOMIUM", # GUI libraries + "ROCKET", # GUI libraries "CARBON", "COCOA", # Mac OS X toolkits "X11", # Unix platform support "PANDATOOL", "PVIEW", "DEPLOYTOOLS", # Toolchain @@ -171,14 +173,23 @@ def parseopts(args): global COMPRESSOR,THREADCOUNT,OSXTARGET,OSX_ARCHS,HOST_URL global DEBVERSION,WHLVERSION,RPMRELEASE,GIT_COMMIT,P3DSUFFIX,RTDIST_VERSION global STRDXSDKVERSION, WINDOWS_SDK, MSVC_VERSION, BOOUSEINTELCOMPILER + global COPY_PYTHON + + # Options for which to display a deprecation warning. + removedopts = [ + "use-touchinput", "no-touchinput", "no-awesomium", "no-directscripts", + ] + + # All recognized options. longopts = [ "help","distributor=","verbose","runtime","osxtarget=","tests", "optimize=","everything","nothing","installer","wheel","rtdist","nocolor", "version=","lzma","no-python","threads=","outputdir=","override=", "static","host=","debversion=","rpmrelease=","p3dsuffix=","rtdist-version=", "directx-sdk=", "windows-sdk=", "msvc-version=", "clean", "use-icl", - "universal", "target=", "arch=", "git-commit=", "no-directscripts", - "use-touchinput", "no-touchinput"] + "universal", "target=", "arch=", "git-commit=", "no-copy-python", + ] + removedopts + anything = 0 optimize = "" target = None @@ -190,6 +201,7 @@ def parseopts(args): longopts.append("no-" + pkg.lower()) longopts.append(pkg.lower() + "-incdir=") longopts.append(pkg.lower() + "-libdir=") + try: opts, extras = getopt.getopt(args, "", longopts) for option, value in opts: @@ -230,7 +242,6 @@ def parseopts(args): # Backward compatibility, OPENGL was renamed to GL elif (option=="--use-opengl"): PkgEnable("GL") elif (option=="--no-opengl"): PkgDisable("GL") - elif (option=="--no-directscripts"): pass elif (option=="--directx-sdk"): STRDXSDKVERSION = value.strip().lower() if STRDXSDKVERSION == '': @@ -242,6 +253,9 @@ def parseopts(args): MSVC_VERSION = value.strip().lower() elif (option=="--use-icl"): BOOUSEINTELCOMPILER = True elif (option=="--clean"): clean_build = True + elif (option=="--no-copy-python"): COPY_PYTHON = False + elif (option[2:] in removedopts): + Warn("Ignoring removed option %s" % (option)) else: for pkg in PkgListGet(): if option == "--use-" + pkg.lower(): @@ -403,6 +417,67 @@ MAJOR_VERSION = '.'.join(VERSION.split('.')[:2]) if P3DSUFFIX is None: P3DSUFFIX = MAJOR_VERSION +# Now determine the distutils-style platform tag for the target system. +target = GetTarget() +if target == 'windows': + if GetTargetArch() == 'x64': + PLATFORM = 'win-amd64' + else: + PLATFORM = 'win32' + +elif target == 'darwin': + if OSXTARGET: + osxver = OSXTARGET + else: + maj, min = platform.mac_ver()[0].split('.')[:2] + osxver = int(maj), int(min) + + arch_tag = None + if not OSX_ARCHS: + arch_tag = GetTargetArch() + elif len(OSX_ARCHS) == 1: + arch_tag = OSX_ARCHS[0] + elif frozenset(OSX_ARCHS) == frozenset(('i386', 'ppc')): + arch_tag = 'fat' + elif frozenset(OSX_ARCHS) == frozenset(('x86_64', 'i386')): + arch_tag = 'intel' + elif frozenset(OSX_ARCHS) == frozenset(('x86_64', 'ppc64')): + arch_tag = 'fat64' + elif frozenset(OSX_ARCHS) == frozenset(('x86_64', 'i386', 'ppc')): + arch_tag = 'fat32' + else: + raise RuntimeError('No arch tag for arch combination %s' % OSX_ARCHS) + + PLATFORM = 'macosx-{0}.{1}-{2}'.format(osxver[0], osxver[1], arch_tag) + +elif target == 'linux' and (os.path.isfile("/lib/libc-2.5.so") or os.path.isfile("/lib64/libc-2.5.so")) and os.path.isdir("/opt/python"): + # This is manylinux1. A bit of a sloppy check, though. + if GetTargetArch() in ('x86_64', 'amd64'): + PLATFORM = 'manylinux1-x86_64' + else: + PLATFORM = 'manylinux1-i686' + +elif not CrossCompiling(): + if HasTargetArch(): + # Replace the architecture in the platform string. + platform_parts = get_platform().rsplit('-', 1) + target_arch = GetTargetArch() + if target_arch == 'amd64': + target_arch = 'x86_64' + PLATFORM = platform_parts[0] + '-' + target_arch + else: + # We're not cross-compiling; just take the host arch. + PLATFORM = get_platform() + +else: + target_arch = GetTargetArch() + if target_arch == 'amd64': + target_arch = 'x86_64' + PLATFORM = '{0}-{1}' % (target, target_arch) + + +print("Platform: %s" % PLATFORM) + outputdir_suffix = "" if (RUNTIME or RTDIST): @@ -616,6 +691,7 @@ if (COMPILER == "MSVC"): LibName("WINGDI", "gdi32.lib") LibName("ADVAPI", "advapi32.lib") LibName("IPHLPAPI", "iphlpapi.lib") + LibName("SETUPAPI", "setupapi.lib") LibName("GL", "opengl32.lib") LibName("GLES", "libgles_cm.lib") LibName("GLES2", "libGLESv2.lib") @@ -646,10 +722,17 @@ if (COMPILER == "MSVC"): suffix = "" if os.path.isfile(GetThirdpartyDir() + "openexr/lib/IlmImf-2_2.lib"): suffix = "-2_2" + elif os.path.isfile(GetThirdpartyDir() + "openexr/lib/IlmImf-2_3.lib"): + suffix = "-2_3" + if os.path.isfile(GetThirdpartyDir() + "openexr/lib/IlmImf" + suffix + "_s.lib"): + suffix += "_s" LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/IlmImf" + suffix + ".lib") LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/IlmThread" + suffix + ".lib") LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/Iex" + suffix + ".lib") - LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/Half.lib") + if suffix == "-2_2": + LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/Half.lib") + else: + LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/Half" + suffix + ".lib") IncDirectory("OPENEXR", GetThirdpartyDir() + "openexr/include/OpenEXR") if (PkgSkip("JPEG")==0): LibName("JPEG", GetThirdpartyDir() + "jpeg/lib/jpeg-static.lib") if (PkgSkip("ZLIB")==0): LibName("ZLIB", GetThirdpartyDir() + "zlib/lib/zlibstatic.lib") @@ -669,7 +752,6 @@ if (COMPILER == "MSVC"): if (PkgSkip("OPENCV")==0): LibName("OPENCV", GetThirdpartyDir() + "opencv/lib/cvaux.lib") if (PkgSkip("OPENCV")==0): LibName("OPENCV", GetThirdpartyDir() + "opencv/lib/ml.lib") if (PkgSkip("OPENCV")==0): LibName("OPENCV", GetThirdpartyDir() + "opencv/lib/cxcore.lib") - if (PkgSkip("AWESOMIUM")==0):LibName("AWESOMIUM",GetThirdpartyDir() + "awesomium/lib/Awesomium.lib") if (PkgSkip("FFMPEG")==0): LibName("FFMPEG", GetThirdpartyDir() + "ffmpeg/lib/avcodec.lib") if (PkgSkip("FFMPEG")==0): LibName("FFMPEG", GetThirdpartyDir() + "ffmpeg/lib/avformat.lib") if (PkgSkip("FFMPEG")==0): LibName("FFMPEG", GetThirdpartyDir() + "ffmpeg/lib/avutil.lib") @@ -680,8 +762,7 @@ if (COMPILER == "MSVC"): IncDirectory("FCOLLADA", GetThirdpartyDir() + "fcollada/include/FCollada") if (PkgSkip("ASSIMP")==0): LibName("ASSIMP", GetThirdpartyDir() + "assimp/lib/assimp.lib") - path = GetThirdpartyDir() + "assimp/lib/IrrXML.lib" - if os.path.isfile(path): + if os.path.isfile(GetThirdpartyDir() + "assimp/lib/IrrXML.lib"): LibName("ASSIMP", GetThirdpartyDir() + "assimp/lib/IrrXML.lib") IncDirectory("ASSIMP", GetThirdpartyDir() + "assimp/include") if (PkgSkip("SQUISH")==0): @@ -727,9 +808,12 @@ if (COMPILER == "MSVC"): path = GetThirdpartyDir() + "vorbis/lib/{0}.lib".format(lib) LibName("VORBIS", path) if (PkgSkip("OPUS")==0): - LibName("OPUS", GetThirdpartyDir() + "opus/lib/libogg_static.lib") - LibName("OPUS", GetThirdpartyDir() + "opus/lib/libopus_static.lib") - LibName("OPUS", GetThirdpartyDir() + "opus/lib/libopusfile_static.lib") + IncDirectory("OPUS", GetThirdpartyDir() + "opus/include/opus") + for lib in ('ogg', 'opus', 'opusfile'): + path = GetThirdpartyDir() + "opus/lib/lib{0}_static.lib".format(lib) + if not os.path.isfile(path): + path = GetThirdpartyDir() + "opus/lib/{0}.lib".format(lib) + LibName("OPUS", path) for pkg in MAYAVERSIONS: if (PkgSkip(pkg)==0): LibName(pkg, '"' + SDK[pkg] + '/lib/Foundation.lib"') @@ -790,7 +874,6 @@ if (COMPILER == "MSVC"): LibName("BULLET", GetThirdpartyDir() + "bullet/lib/BulletSoftBody" + suffix) if (COMPILER=="GCC"): - PkgDisable("AWESOMIUM") if GetTarget() != "darwin": PkgDisable("CARBON") PkgDisable("COCOA") @@ -887,11 +970,12 @@ if (COMPILER=="GCC"): if not PkgSkip("PYTHON"): python_lib = SDK["PYTHONVERSION"] - if not RTDIST and GetTarget() != 'android': - # We don't link anything in the SDK with libpython. - python_lib = "" SmartPkgEnable("PYTHON", "", python_lib, (SDK["PYTHONVERSION"], SDK["PYTHONVERSION"] + "/Python.h")) + if GetTarget() == "linux": + LibName("PYTHON", "-lutil") + LibName("PYTHON", "-lrt") + SmartPkgEnable("OPENSSL", "openssl", ("ssl", "crypto"), ("openssl/ssl.h", "openssl/crypto.h")) SmartPkgEnable("ZLIB", "zlib", ("z"), "zlib.h") SmartPkgEnable("GTK2", "gtk+-2.0") @@ -1983,7 +2067,7 @@ def FreezePy(target, inputs, opts): if sys.version_info >= (2, 6): cmdstr += "-B " - cmdstr += os.path.join(GetOutputDir(), "direct", "showutil", "pfreeze.py") + cmdstr += os.path.join(GetOutputDir(), "direct", "dist", "pfreeze.py") if 'FREEZE_STARTUP' in opts: cmdstr += " -s" @@ -2440,6 +2524,7 @@ def WriteConfigSettings(): dtool_config["HAVE_GLX"] = 'UNDEF' dtool_config["IS_LINUX"] = 'UNDEF' dtool_config["HAVE_VIDEO4LINUX"] = 'UNDEF' + dtool_config["PHAVE_LINUX_INPUT_H"] = 'UNDEF' dtool_config["IS_OSX"] = '1' # 10.4 had a broken ucontext implementation if int(platform.mac_ver()[0][3]) <= 4: @@ -2576,6 +2661,14 @@ def WriteConfigSettings(): if (PkgSkip(x)): ConditionalWriteFile(GetOutputDir() + '/tmp/dtool_have_'+x.lower()+'.dat', "0\n") else: ConditionalWriteFile(GetOutputDir() + '/tmp/dtool_have_'+x.lower()+'.dat', "1\n") + # Finally, write a platform.dat with the platform we are compiling for. + ConditionalWriteFile(GetOutputDir() + '/tmp/platform.dat', PLATFORM) + + # This is useful for tools like makepackage that need to know things about + # the build parameters. + ConditionalWriteFile(GetOutputDir() + '/tmp/optimize.dat', str(GetOptimize())) + + WriteConfigSettings() WarnConflictingFiles() @@ -2802,16 +2895,16 @@ __version__ = '%s' if GetTarget() == 'windows': p3d_init += """ -import os +if '__file__' in locals(): + import os -bindir = os.path.join(os.path.dirname(__file__), '..', 'bin') -if os.path.isdir(bindir): - if not os.environ.get('PATH'): - os.environ['PATH'] = bindir - else: - os.environ['PATH'] = bindir + os.pathsep + os.environ['PATH'] - -del os, bindir + bindir = os.path.join(os.path.dirname(__file__), '..', 'bin') + if os.path.isdir(bindir): + if not os.environ.get('PATH'): + os.environ['PATH'] = bindir + else: + os.environ['PATH'] = bindir + os.pathsep + os.environ['PATH'] + del os, bindir """ if not PkgSkip("PYTHON"): @@ -2842,8 +2935,6 @@ if not PkgSkip("SKEL"): panda_modules.append('skel') if not PkgSkip("EGG"): panda_modules.append('egg') -if not PkgSkip("AWESOMIUM"): - panda_modules.append('awesomium') if not PkgSkip("ODE"): panda_modules.append('ode') if not PkgSkip("VRPN"): @@ -2888,6 +2979,36 @@ if not PkgSkip("PYTHON"): ConditionalWriteFile(GetOutputDir() + '/pandac/extension_native_helpers.py', exthelpers_code) ConditionalWriteFile(GetOutputDir() + '/pandac/__init__.py', '') +########################################################################################## +# +# Write the dist-info directory. +# +########################################################################################## + +# This is just some basic stuff since setuptools just needs this file to +# exist, otherwise it will not read the entry_points.txt file. Maybe we will +# eventually want to merge this with the metadata generator in makewheel.py. +METADATA = """Metadata-Version: 2.0 +Name: Panda3D +Version: {version} +License: BSD +Home-page: https://www.panda3d.org/ +Author: Panda3D Team +Author-email: etc-panda3d@lists.andrew.cmu.edu +""" + +ENTRY_POINTS = """[distutils.commands] +build_apps = direct.dist.commands:build_apps +bdist_apps = direct.dist.commands:bdist_apps +""" + +if not PkgSkip("DIRECT"): + dist_dir = os.path.join(GetOutputDir(), 'panda3d.dist-info') + MakeDirectory(dist_dir) + + ConditionalWriteFile(os.path.join(dist_dir, 'METADATA'), METADATA.format(version=VERSION)) + ConditionalWriteFile(os.path.join(dist_dir, 'entry_points.txt'), ENTRY_POINTS) + ########################################################################################## # # Generate the PRC files into the ETC directory. @@ -2916,7 +3037,7 @@ else: configprc = configprc.replace("aux-display pandadx9", "") if (GetTarget() == 'darwin'): - configprc = configprc.replace("$XDG_CACHE_HOME/panda3d", "Library/Caches/Panda3D-%s" % MAJOR_VERSION) + configprc = configprc.replace("$XDG_CACHE_HOME/panda3d", "$HOME/Library/Caches/Panda3D-%s" % MAJOR_VERSION) # OpenAL is not yet working well on OSX for us, so let's do this for now. configprc = configprc.replace("p3openal_audio", "p3fmod_audio") @@ -3040,7 +3161,8 @@ if tp_dir is not None: CopyFile(GetOutputDir() + "/bin/", fn) # Copy the whole Python directory. - CopyTree(GetOutputDir() + "/python", SDK["PYTHON"]) + if COPY_PYTHON: + CopyTree(GetOutputDir() + "/python", SDK["PYTHON"]) # NB: Python does not always ship with the correct manifest/dll. # Figure out the correct one to ship, and grab it from WinSxS dir. @@ -3049,7 +3171,7 @@ if tp_dir is not None: os.unlink(manifest) oscmd('mt -inputresource:"%s\\python.exe";#1 -out:"%s" -nologo' % (SDK["PYTHON"], manifest), True) - if os.path.isfile(manifest): + if COPY_PYTHON and os.path.isfile(manifest): import xml.etree.ElementTree as ET tree = ET.parse(manifest) idents = tree.findall('./{urn:schemas-microsoft-com:asm.v1}dependency/{urn:schemas-microsoft-com:asm.v1}dependentAssembly/{urn:schemas-microsoft-com:asm.v1}assemblyIdentity') @@ -3079,11 +3201,12 @@ if tp_dir is not None: CopyFile(GetOutputDir() + "/python/", file) # Copy python.exe to ppython.exe. - if not os.path.isfile(SDK["PYTHON"] + "/ppython.exe") and os.path.isfile(SDK["PYTHON"] + "/python.exe"): - CopyFile(GetOutputDir() + "/python/ppython.exe", SDK["PYTHON"] + "/python.exe") - if not os.path.isfile(SDK["PYTHON"] + "/ppythonw.exe") and os.path.isfile(SDK["PYTHON"] + "/pythonw.exe"): - CopyFile(GetOutputDir() + "/python/ppythonw.exe", SDK["PYTHON"] + "/pythonw.exe") - ConditionalWriteFile(GetOutputDir() + "/python/panda.pth", "..\n../bin\n") + if COPY_PYTHON: + if not os.path.isfile(SDK["PYTHON"] + "/ppython.exe") and os.path.isfile(SDK["PYTHON"] + "/python.exe"): + CopyFile(GetOutputDir() + "/python/ppython.exe", SDK["PYTHON"] + "/python.exe") + if not os.path.isfile(SDK["PYTHON"] + "/ppythonw.exe") and os.path.isfile(SDK["PYTHON"] + "/pythonw.exe"): + CopyFile(GetOutputDir() + "/python/ppythonw.exe", SDK["PYTHON"] + "/pythonw.exe") + ConditionalWriteFile(GetOutputDir() + "/python/panda.pth", "..\n../bin\n") # Copy over the MSVC runtime. if GetTarget() == 'windows' and "VISUALSTUDIO" in SDK: @@ -3189,7 +3312,6 @@ CopyAllHeaders('panda/src/text') CopyAllHeaders('panda/src/grutil') if (PkgSkip("VISION")==0): CopyAllHeaders('panda/src/vision') -CopyAllHeaders('panda/src/awesomium') if (PkgSkip("FFMPEG")==0): CopyAllHeaders('panda/src/ffmpeg') CopyAllHeaders('panda/src/tform') @@ -3867,6 +3989,34 @@ if (not RUNTIME): TargetAdd('libp3cull.in', opts=OPTS, input=IGATEFILES) TargetAdd('libp3cull.in', opts=['IMOD:panda3d.core', 'ILIB:libp3cull', 'SRCDIR:panda/src/cull']) +# +# DIRECTORY: panda/src/dgraph/ +# + +if (not RUNTIME): + OPTS=['DIR:panda/src/dgraph', 'BUILDING:PANDA'] + TargetAdd('p3dgraph_composite1.obj', opts=OPTS, input='p3dgraph_composite1.cxx') + TargetAdd('p3dgraph_composite2.obj', opts=OPTS, input='p3dgraph_composite2.cxx') + + OPTS=['DIR:panda/src/dgraph'] + IGATEFILES=GetDirectoryContents('panda/src/dgraph', ["*.h", "*_composite*.cxx"]) + TargetAdd('libp3dgraph.in', opts=OPTS, input=IGATEFILES) + TargetAdd('libp3dgraph.in', opts=['IMOD:panda3d.core', 'ILIB:libp3dgraph', 'SRCDIR:panda/src/dgraph']) + +# +# DIRECTORY: panda/src/device/ +# + +if (not RUNTIME): + OPTS=['DIR:panda/src/device', 'BUILDING:PANDA'] + TargetAdd('p3device_composite1.obj', opts=OPTS, input='p3device_composite1.cxx') + TargetAdd('p3device_composite2.obj', opts=OPTS, input='p3device_composite2.cxx') + + OPTS=['DIR:panda/src/device'] + IGATEFILES=GetDirectoryContents('panda/src/device', ["*.h", "*_composite*.cxx"]) + TargetAdd('libp3device.in', opts=OPTS, input=IGATEFILES) + TargetAdd('libp3device.in', opts=['IMOD:panda3d.core', 'ILIB:libp3device', 'SRCDIR:panda/src/device']) + # # DIRECTORY: panda/src/display/ # @@ -3919,34 +4069,6 @@ if (not RUNTIME): TargetAdd('libp3char.in', opts=OPTS, input=IGATEFILES) TargetAdd('libp3char.in', opts=['IMOD:panda3d.core', 'ILIB:libp3char', 'SRCDIR:panda/src/char']) -# -# DIRECTORY: panda/src/dgraph/ -# - -if (not RUNTIME): - OPTS=['DIR:panda/src/dgraph', 'BUILDING:PANDA'] - TargetAdd('p3dgraph_composite1.obj', opts=OPTS, input='p3dgraph_composite1.cxx') - TargetAdd('p3dgraph_composite2.obj', opts=OPTS, input='p3dgraph_composite2.cxx') - - OPTS=['DIR:panda/src/dgraph'] - IGATEFILES=GetDirectoryContents('panda/src/dgraph', ["*.h", "*_composite*.cxx"]) - TargetAdd('libp3dgraph.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3dgraph.in', opts=['IMOD:panda3d.core', 'ILIB:libp3dgraph', 'SRCDIR:panda/src/dgraph']) - -# -# DIRECTORY: panda/src/device/ -# - -if (not RUNTIME): - OPTS=['DIR:panda/src/device', 'BUILDING:PANDA'] - TargetAdd('p3device_composite1.obj', opts=OPTS, input='p3device_composite1.cxx') - TargetAdd('p3device_composite2.obj', opts=OPTS, input='p3device_composite2.cxx') - - OPTS=['DIR:panda/src/device'] - IGATEFILES=GetDirectoryContents('panda/src/device', ["*.h", "*_composite*.cxx"]) - TargetAdd('libp3device.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3device.in', opts=['IMOD:panda3d.core', 'ILIB:libp3device', 'SRCDIR:panda/src/device']) - # # DIRECTORY: panda/src/pnmtext/ # @@ -4111,7 +4233,8 @@ if (not RUNTIME): if (not RUNTIME): OPTS=['DIR:panda/metalibs/panda', 'BUILDING:PANDA', 'JPEG', 'PNG', 'HARFBUZZ', 'TIFF', 'OPENEXR', 'ZLIB', 'OPENSSL', 'FREETYPE', 'FFTW', 'ADVAPI', 'WINSOCK2', - 'SQUISH', 'NVIDIACG', 'VORBIS', 'OPUS', 'WINUSER', 'WINMM', 'WINGDI', 'IPHLPAPI'] + 'SQUISH', 'NVIDIACG', 'VORBIS', 'OPUS', 'WINUSER', 'WINMM', 'WINGDI', 'IPHLPAPI', + 'SETUPAPI'] TargetAdd('panda_panda.obj', opts=OPTS, input='panda.cxx') @@ -4363,32 +4486,6 @@ if (PkgSkip("ROCKET") == 0) and (not RUNTIME): PyTargetAdd('rocket.pyd', input=COMMON_PANDA_LIBS) PyTargetAdd('rocket.pyd', opts=['ROCKET']) -# -# DIRECTORY: panda/src/p3awesomium -# -if PkgSkip("AWESOMIUM") == 0 and not RUNTIME: - OPTS=['DIR:panda/src/awesomium', 'BUILDING:PANDAAWESOMIUM', 'AWESOMIUM'] - TargetAdd('pandaawesomium_composite1.obj', opts=OPTS, input='pandaawesomium_composite1.cxx') - TargetAdd('libp3awesomium.dll', input='pandaawesomium_composite1.obj') - TargetAdd('libp3awesomium.dll', input=COMMON_PANDA_LIBS) - TargetAdd('libp3awesomium.dll', opts=OPTS) - - OPTS=['DIR:panda/src/awesomium', 'AWESOMIUM'] - IGATEFILES=GetDirectoryContents('panda/src/awesomium', ["*.h", "*_composite1.cxx"]) - TargetAdd('libp3awesomium.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3awesomium.in', opts=['IMOD:panda3d.awesomium', 'ILIB:libp3awesomium', 'SRCDIR:panda/src/awesomium']) - - - PyTargetAdd('awesomium_module.obj', input='libp3awesomium.in') - PyTargetAdd('awesomium_module.obj', opts=OPTS) - PyTargetAdd('awesomium_module.obj', opts=['IMOD:panda3d.awesomium', 'ILIB:awesomium', 'IMPORT:panda3d.core']) - - PyTargetAdd('awesomium.pyd', input='awesomium_module.obj') - PyTargetAdd('awesomium.pyd', input='libp3awesomium_igate.obj') - PyTargetAdd('awesomium.pyd', input='libp3awesomium.dll') - PyTargetAdd('awesomium.pyd', input='libp3interrogatedb.dll') - PyTargetAdd('awesomium.pyd', input=COMMON_PANDA_LIBS) - # # DIRECTORY: panda/src/p3skel # @@ -6537,6 +6634,36 @@ if not PkgSkip("CONTRIB") and not PkgSkip("PYTHON") and not RUNTIME: PyTargetAdd('_rplight.pyd', input='libp3interrogatedb.dll') PyTargetAdd('_rplight.pyd', input=COMMON_PANDA_LIBS) +# +# DIRECTORY: pandatool/src/deploy-stub +# +if PkgSkip("PYTHON") == 0: + OPTS=['DIR:pandatool/src/deploy-stub', 'BUILDING:DEPLOYSTUB'] + PyTargetAdd('deploy-stub.obj', opts=OPTS, input='deploy-stub.c') + if GetTarget() == 'windows': + PyTargetAdd('frozen_dllmain.obj', opts=OPTS, input='frozen_dllmain.c') + + if GetTarget() == 'linux' or GetTarget() == 'freebsd': + # Setup rpath so libs can be found in the same directory as the deployed game + LibName('DEPLOYSTUB', "-Wl,-rpath,\$ORIGIN") + LibName('DEPLOYSTUB', "-Wl,-z,origin") + LibName('DEPLOYSTUB', "-rdynamic") + PyTargetAdd('deploy-stub.exe', input='deploy-stub.obj') + if GetTarget() == 'windows': + PyTargetAdd('deploy-stub.exe', input='frozen_dllmain.obj') + PyTargetAdd('deploy-stub.exe', opts=['WINSHELL', 'DEPLOYSTUB', 'NOICON']) + + if GetTarget() == 'windows': + PyTargetAdd('deploy-stubw.exe', input='deploy-stub.obj') + PyTargetAdd('deploy-stubw.exe', input='frozen_dllmain.obj') + PyTargetAdd('deploy-stubw.exe', opts=['SUBSYSTEM:WINDOWS', 'WINSHELL', 'DEPLOYSTUB', 'NOICON']) + elif GetTarget() == 'darwin': + DefSymbol('MACOS_APP_BUNDLE', 'MACOS_APP_BUNDLE') + OPTS = OPTS + ['MACOS_APP_BUNDLE'] + PyTargetAdd('deploy-stubw.obj', opts=OPTS, input='deploy-stub.c') + PyTargetAdd('deploy-stubw.exe', input='deploy-stubw.obj') + PyTargetAdd('deploy-stubw.exe', opts=['MACOS_APP_BUNDLE', 'DEPLOYSTUB', 'NOICON']) + # # Generate the models directory and samples directory # @@ -6727,17 +6854,18 @@ def SequentialMake(tasklist): JustBuilt(task[2], task[3]) i += 1 + def RunDependencyQueue(tasklist): if (THREADCOUNT!=0): ParallelMake(tasklist) else: SequentialMake(tasklist) + try: RunDependencyQueue(DEPENDENCYQUEUE) -except: +finally: SaveDependencyCache() - raise # Run the test suite. if RUNTESTS: @@ -6749,6 +6877,10 @@ if RUNTESTS: cmdstr += " --verbose" oscmd(cmdstr) +# Write out information about the Python versions in the built dir. +python_version_info = GetCurrentPythonVersionInfo() +UpdatePythonVersionInfoFile(python_version_info) + ########################################################################################## # # The Installers @@ -6759,879 +6891,24 @@ if RUNTESTS: # ########################################################################################## -def MakeInstallerNSIS(file, title, installdir): - if (os.path.isfile(file)): - os.remove(file) - elif (os.path.isdir(file)): - shutil.rmtree(file) - - pyver = SDK["PYTHONVERSION"][6:9] - if GetTargetArch() == 'x64': - regview = '64' - else: - regview = '32' - if int(pyver[0]) == 3 and int(pyver[2]) >= 5: - pyver += '-32' - - if (RUNTIME): - # Invoke the make_installer script. - AddToPathEnv("PATH", GetOutputDir() + "\\bin") - AddToPathEnv("PATH", GetOutputDir() + "\\plugins") - - cmd = sys.executable + " -B -u " + os.path.join("direct", "src", "plugin_installer", "make_installer.py") - cmd += " --version %s --regview %s" % (VERSION, regview) - - if GetTargetArch() == 'x64': - cmd += " --install \"$PROGRAMFILES64\\Panda3D\" " - else: - cmd += " --install \"$PROGRAMFILES32\\Panda3D\" " - - oscmd(cmd) - shutil.move(os.path.join("direct", "src", "plugin_installer", "p3d-setup.exe"), file) - return - - print("Building "+title+" installer at %s" % (file)) - if (COMPRESSOR != "lzma"): - print("Note: you are using zlib, which is faster, but lzma gives better compression.") - if (os.path.exists("nsis-output.exe")): - os.remove("nsis-output.exe") - WriteFile(GetOutputDir()+"/tmp/__init__.py", "") - - nsis_defs = { - 'COMPRESSOR' : COMPRESSOR, - 'TITLE' : title, - 'INSTALLDIR' : installdir, - 'OUTFILE' : '..\\' + file, - 'BUILT' : '..\\' + GetOutputDir(), - 'SOURCE' : '..', - 'PYVER' : pyver, - 'REGVIEW' : regview, - 'EXT_SUFFIX' : GetExtensionSuffix(), - } - - if GetHost() == 'windows': - cmd = os.path.join(GetThirdpartyBase(), 'win-nsis', 'makensis') + ' /V2' - for item in nsis_defs.items(): - cmd += ' /D%s="%s"' % item - else: - cmd = 'makensis -V2' - for item in nsis_defs.items(): - cmd += ' -D%s="%s"' % item - - cmd += ' "makepanda\\installer.nsi"' - oscmd(cmd) - -def MakeDebugSymbolArchive(zipname, dirname): - import zipfile - zip = zipfile.ZipFile(zipname, 'w', zipfile.ZIP_DEFLATED) - - for fn in glob.glob(os.path.join(GetOutputDir(), 'bin', '*.pdb')): - zip.write(fn, dirname + '/bin/' + os.path.basename(fn)) - - for fn in glob.glob(os.path.join(GetOutputDir(), 'panda3d', '*.pdb')): - zip.write(fn, dirname + '/panda3d/' + os.path.basename(fn)) - - for fn in glob.glob(os.path.join(GetOutputDir(), 'plugins', '*.pdb')): - zip.write(fn, dirname + '/plugins/' + os.path.basename(fn)) - - for fn in glob.glob(os.path.join(GetOutputDir(), 'python', '*.pdb')): - zip.write(fn, dirname + '/python/' + os.path.basename(fn)) - - for fn in glob.glob(os.path.join(GetOutputDir(), 'python', 'DLLs', '*.pdb')): - zip.write(fn, dirname + '/python/DLLs/' + os.path.basename(fn)) - - zip.close() - -INSTALLER_DEB_FILE=""" -Package: panda3dMAJOR -Version: VERSION -Section: libdevel -Priority: optional -Architecture: ARCH -Essential: no -Depends: DEPENDS -Recommends: RECOMMENDS -Provides: panda3d, pythonPV-panda3d -Conflicts: panda3d, pythonPV-panda3d -Replaces: panda3d, pythonPV-panda3d -Maintainer: rdb -Installed-Size: INSTSIZE -Description: Panda3D free 3D engine SDK - Panda3D is a game engine which includes graphics, audio, I/O, collision detection, and other abilities relevant to the creation of 3D games. Panda3D is open source and free software under the revised BSD license, and can be used for both free and commercial game development at no financial cost. - Panda3D's intended game-development language is Python. The engine itself is written in C++, and utilizes an automatic wrapper-generator to expose the complete functionality of the engine in a Python interface. - . - This package contains the SDK for development with Panda3D. - -""" - -RUNTIME_INSTALLER_DEB_FILE=""" -Package: panda3d-runtime -Version: VERSION -Section: web -Priority: optional -Architecture: ARCH -Essential: no -Depends: DEPENDS -Provides: panda3d-runtime -Maintainer: rdb -Installed-Size: INSTSIZE -Description: Runtime binary and browser plugin for the Panda3D Game Engine - This package contains the runtime distribution and browser plugin of the Panda3D engine. It allows you view webpages that contain Panda3D content and to run games created with Panda3D that are packaged as .p3d file. - -""" - - -# We're not putting "python" in the "Requires" field, -# since the rpm-based distros don't have a common -# naming for the Python package. -INSTALLER_SPEC_FILE=""" -Summary: The Panda3D free 3D engine SDK -Name: panda3d -Version: VERSION -Release: RPMRELEASE -License: BSD License -Group: Development/Libraries -BuildRoot: PANDASOURCE/targetroot -%description -Panda3D is a game engine which includes graphics, audio, I/O, collision detection, and other abilities relevant to the creation of 3D games. Panda3D is open source and free software under the revised BSD license, and can be used for both free and commercial game development at no financial cost. -Panda3D's intended game-development language is Python. The engine itself is written in C++, and utilizes an automatic wrapper-generator to expose the complete functionality of the engine in a Python interface. - -This package contains the SDK for development with Panda3D, install panda3d-runtime for the runtime files. -%post -/sbin/ldconfig -%postun -/sbin/ldconfig -%files -%defattr(-,root,root) -/etc/Confauto.prc -/etc/Config.prc -/usr/share/panda3d -/etc/ld.so.conf.d/panda3d.conf -/usr/%_lib/panda3d -""" + PYTHON_SITEPACKAGES + """ -/usr/include/panda3d -""" -if not PkgSkip("PVIEW"): - INSTALLER_SPEC_FILE += \ -"""/usr/share/applications/pview.desktop -/usr/share/mime-info/panda3d.mime -/usr/share/mime-info/panda3d.keys -/usr/share/mime/packages/panda3d.xml -/usr/share/application-registry/panda3d.applications -""" - -RUNTIME_INSTALLER_SPEC_FILE=""" -Summary: Runtime binary and browser plugin for the Panda3D Game Engine -Name: panda3d-runtime -Version: VERSION -Release: RPMRELEASE -License: BSD License -Group: Productivity/Graphics/Other -BuildRoot: PANDASOURCE/targetroot -%description -This package contains the runtime distribution and browser plugin of the Panda3D engine. It allows you view webpages that contain Panda3D content and to run games created with Panda3D that are packaged as .p3d file. -%files -%defattr(-,root,root) -/usr/bin/panda3d -/usr/%_lib/nppanda3d.so -/usr/%_lib/mozilla/plugins/nppanda3d.so -/usr/%_lib/mozilla-firefox/plugins/nppanda3d.so -/usr/%_lib/xulrunner-addons/plugins/nppanda3d.so -/usr/share/mime-info/panda3d-runtime.mime -/usr/share/mime-info/panda3d-runtime.keys -/usr/share/mime/packages/panda3d-runtime.xml -/usr/share/application-registry/panda3d-runtime.applications -/usr/share/applications/*.desktop -""" - -# plist file for Mac OSX -Info_plist = """ - - - - CFBundleIdentifier - %(package_id)s - CFBundleShortVersionString - %(version)s - IFPkgFlagRelocatable - - IFPkgFlagAuthorizationAction - RootAuthorization - IFPkgFlagAllowBackRev - - - -""" - -# FreeBSD pkg-descr -INSTALLER_PKG_DESCR_FILE = """ -Panda3D is a game engine which includes graphics, audio, I/O, collision detection, and other abilities relevant to the creation of 3D games. Panda3D is open source and free software under the revised BSD license, and can be used for both free and commercial game development at no financial cost. -Panda3D's intended game-development language is Python. The engine itself is written in C++, and utilizes an automatic wrapper-generator to expose the complete functionality of the engine in a Python interface. - -This package contains the SDK for development with Panda3D, install panda3d-runtime for the runtime files. - -WWW: https://www.panda3d.org/ -""" - -# FreeBSD pkg-descr -RUNTIME_INSTALLER_PKG_DESCR_FILE = """ -Runtime binary and browser plugin for the Panda3D Game Engine - -This package contains the runtime distribution and browser plugin of the Panda3D engine. It allows you view webpages that contain Panda3D content and to run games created with Panda3D that are packaged as .p3d file. - -WWW: https://www.panda3d.org/ -""" - -# FreeBSD PKG Manifest template file -INSTALLER_PKG_MANIFEST_FILE = """ -name: NAME -version: VERSION -arch: ARCH -origin: ORIGIN -comment: "Panda3D free 3D engine SDK" -www: https://www.panda3d.org -maintainer: rdb -prefix: /usr/local -flatsize: INSTSIZEMB -deps: {DEPENDS} -""" - -def MakeInstallerLinux(): - if not RUNTIME and not PkgSkip("PYTHON"): - PYTHONV = SDK["PYTHONVERSION"].rstrip('dmu') - else: - PYTHONV = "python" - PV = PYTHONV.replace("python", "") - - # Clean and set up a directory to install Panda3D into - oscmd("rm -rf targetroot data.tar.gz control.tar.gz panda3d.spec") - oscmd("mkdir --mode=0755 targetroot") - - dpkg_present = False - if os.path.exists("/usr/bin/dpkg-architecture") and os.path.exists("/usr/bin/dpkg-deb"): - dpkg_present = True - rpmbuild_present = False - if os.path.exists("/usr/bin/rpmbuild"): - rpmbuild_present = True - - if dpkg_present and rpmbuild_present: - Warn("both dpkg and rpmbuild present.") - - if dpkg_present: - # Invoke installpanda.py to install it into a temporary dir - lib_dir = GetDebLibDir() - if RUNTIME: - InstallRuntime(destdir="targetroot", prefix="/usr", outputdir=GetOutputDir(), libdir=lib_dir) - else: - InstallPanda(destdir="targetroot", prefix="/usr", outputdir=GetOutputDir(), libdir=lib_dir) - oscmd("chmod -R 755 targetroot/usr/share/panda3d") - oscmd("mkdir -m 0755 -p targetroot/usr/share/man/man1") - oscmd("install -m 0644 doc/man/*.1 targetroot/usr/share/man/man1/") - - oscmd("dpkg --print-architecture > "+GetOutputDir()+"/tmp/architecture.txt") - pkg_arch = ReadFile(GetOutputDir()+"/tmp/architecture.txt").strip() - if (RUNTIME): - txt = RUNTIME_INSTALLER_DEB_FILE[1:] - else: - txt = INSTALLER_DEB_FILE[1:] - txt = txt.replace("VERSION", DEBVERSION).replace("ARCH", pkg_arch).replace("PV", PV).replace("MAJOR", MAJOR_VERSION) - txt = txt.replace("INSTSIZE", str(GetDirectorySize("targetroot") / 1024)) - oscmd("mkdir --mode=0755 -p targetroot/DEBIAN") - oscmd("cd targetroot && (find usr -type f -exec md5sum {} ;) > DEBIAN/md5sums") - if (not RUNTIME): - oscmd("cd targetroot && (find etc -type f -exec md5sum {} ;) >> DEBIAN/md5sums") - WriteFile("targetroot/DEBIAN/conffiles","/etc/Config.prc\n") - WriteFile("targetroot/DEBIAN/postinst","#!/bin/sh\necho running ldconfig\nldconfig\n") - oscmd("cp targetroot/DEBIAN/postinst targetroot/DEBIAN/postrm") - - # Determine the package name and the locations that - # dpkg-shlibdeps should look in for executables. - pkg_version = DEBVERSION - if RUNTIME: - pkg_name = "panda3d-runtime" - lib_pattern = "debian/%s/usr/%s/*.so" % (pkg_name, lib_dir) - else: - pkg_name = "panda3d" + MAJOR_VERSION - lib_pattern = "debian/%s/usr/%s/panda3d/*.so*" % (pkg_name, lib_dir) - bin_pattern = "debian/%s/usr/bin/*" % (pkg_name) - - # dpkg-shlibdeps looks in the debian/{pkg_name}/DEBIAN/shlibs directory - # and also expects a debian/control file, so we create this dummy set-up. - oscmd("mkdir targetroot/debian") - oscmd("ln -s .. targetroot/debian/" + pkg_name) - WriteFile("targetroot/debian/control", "") - - dpkg_shlibdeps = "dpkg-shlibdeps" - if GetVerbose(): - dpkg_shlibdeps += " -v" - - if RUNTIME: - # The runtime doesn't export any useful symbols, so just query the dependencies. - oscmd("cd targetroot && %(dpkg_shlibdeps)s -x%(pkg_name)s %(lib_pattern)s %(bin_pattern)s*" % locals()) - depends = ReadFile("targetroot/debian/substvars").replace("shlibs:Depends=", "").strip() - recommends = "" - else: - pkg_name = "panda3d" + MAJOR_VERSION - pkg_dir = "debian/panda3d" + MAJOR_VERSION - - # Generate a symbols file so that other packages can know which symbols we export. - oscmd("cd targetroot && dpkg-gensymbols -q -ODEBIAN/symbols -v%(pkg_version)s -p%(pkg_name)s -e%(lib_pattern)s" % locals()) - - # Library dependencies are required, binary dependencies are recommended. - # We explicitly exclude libphysx-extras since we don't want to depend on PhysX. - oscmd("cd targetroot && LD_LIBRARY_PATH=usr/%(lib_dir)s/panda3d %(dpkg_shlibdeps)s -Tdebian/substvars_dep --ignore-missing-info -x%(pkg_name)s -xlibphysx-extras %(lib_pattern)s" % locals()) - oscmd("cd targetroot && LD_LIBRARY_PATH=usr/%(lib_dir)s/panda3d %(dpkg_shlibdeps)s -Tdebian/substvars_rec --ignore-missing-info -x%(pkg_name)s %(bin_pattern)s" % locals()) - - # Parse the substvars files generated by dpkg-shlibdeps. - depends = ReadFile("targetroot/debian/substvars_dep").replace("shlibs:Depends=", "").strip() - recommends = ReadFile("targetroot/debian/substvars_rec").replace("shlibs:Depends=", "").strip() - if PkgSkip("PYTHON")==0: - depends += ", " + PYTHONV - recommends += ", python-wxversion, python-profiler (>= " + PV + "), python-pmw, python-tk (>= " + PV + ")" - if PkgSkip("NVIDIACG")==0: - depends += ", nvidia-cg-toolkit" - - # Write back the dependencies, and delete the dummy set-up. - txt = txt.replace("DEPENDS", depends.strip(', ')) - txt = txt.replace("RECOMMENDS", recommends.strip(', ')) - WriteFile("targetroot/DEBIAN/control", txt) - oscmd("rm -rf targetroot/debian") - - # Package it all up into a .deb file. - oscmd("chmod -R 755 targetroot/DEBIAN") - oscmd("chmod 644 targetroot/DEBIAN/control targetroot/DEBIAN/md5sums") - if not RUNTIME: - oscmd("chmod 644 targetroot/DEBIAN/conffiles targetroot/DEBIAN/symbols") - oscmd("fakeroot dpkg-deb -b targetroot %s_%s_%s.deb" % (pkg_name, pkg_version, pkg_arch)) - - elif rpmbuild_present: - # Invoke installpanda.py to install it into a temporary dir - if RUNTIME: - InstallRuntime(destdir="targetroot", prefix="/usr", outputdir=GetOutputDir(), libdir=GetRPMLibDir()) - else: - InstallPanda(destdir="targetroot", prefix="/usr", outputdir=GetOutputDir(), libdir=GetRPMLibDir()) - oscmd("chmod -R 755 targetroot/usr/share/panda3d") - - oscmd("rpm -E '%_target_cpu' > "+GetOutputDir()+"/tmp/architecture.txt") - ARCH = ReadFile(GetOutputDir()+"/tmp/architecture.txt").strip() - pandasource = os.path.abspath(os.getcwd()) - - if RUNTIME: - txt = RUNTIME_INSTALLER_SPEC_FILE[1:] - else: - txt = INSTALLER_SPEC_FILE[1:] - # Add the binaries in /usr/bin explicitly to the spec file - for base in os.listdir(GetOutputDir() + "/bin"): - txt += "/usr/bin/%s\n" % (base) - - # Write out the spec file. - txt = txt.replace("VERSION", VERSION) - txt = txt.replace("RPMRELEASE", RPMRELEASE) - txt = txt.replace("PANDASOURCE", pandasource) - txt = txt.replace("PV", PV) - WriteFile("panda3d.spec", txt) - - oscmd("fakeroot rpmbuild --define '_rpmdir "+pandasource+"' --buildroot '"+os.path.abspath("targetroot")+"' -bb panda3d.spec") - if (RUNTIME): - oscmd("mv "+ARCH+"/panda3d-runtime-"+VERSION+"-"+RPMRELEASE+"."+ARCH+".rpm .") - else: - oscmd("mv "+ARCH+"/panda3d-"+VERSION+"-"+RPMRELEASE+"."+ARCH+".rpm .") - oscmd("rm -rf "+ARCH, True) - - else: - exit("To build an installer, either rpmbuild or dpkg-deb must be present on your system!") - -def MakeInstallerOSX(): - if (RUNTIME): - # Invoke the make_installer script. - AddToPathEnv("DYLD_LIBRARY_PATH", GetOutputDir() + "/plugins") - cmdstr = sys.executable + " " - if sys.version_info >= (2, 6): - cmdstr += "-B " - - cmdstr += "direct/src/plugin_installer/make_installer.py --version %s" % VERSION - oscmd(cmdstr) - return - - dmg_name = "Panda3D-" + VERSION - if not SDK["PYTHONVERSION"].startswith("python2."): - dmg_name += '-py' + SDK["PYTHONVERSION"][6:9] - dmg_name += ".dmg" - - import compileall - if (os.path.isfile(dmg_name)): oscmd("rm -f %s" % dmg_name) - if (os.path.exists("dstroot")): oscmd("rm -rf dstroot") - if (os.path.exists("Panda3D-rw.dmg")): oscmd('rm -f Panda3D-rw.dmg') - - oscmd("mkdir -p dstroot/base/Developer/Panda3D/lib") - oscmd("mkdir -p dstroot/base/Developer/Panda3D/etc") - oscmd("cp %s/etc/Config.prc dstroot/base/Developer/Panda3D/etc/Config.prc" % GetOutputDir()) - oscmd("cp %s/etc/Confauto.prc dstroot/base/Developer/Panda3D/etc/Confauto.prc" % GetOutputDir()) - oscmd("cp -R %s/models dstroot/base/Developer/Panda3D/models" % GetOutputDir()) - oscmd("cp -R doc/LICENSE dstroot/base/Developer/Panda3D/LICENSE") - oscmd("cp -R doc/ReleaseNotes dstroot/base/Developer/Panda3D/ReleaseNotes") - oscmd("cp -R %s/Frameworks dstroot/base/Developer/Panda3D/Frameworks" % GetOutputDir()) - if os.path.isdir(GetOutputDir()+"/plugins"): - oscmd("cp -R %s/plugins dstroot/base/Developer/Panda3D/plugins" % GetOutputDir()) - - # Libraries that shouldn't be in base, but are instead in other modules. - no_base_libs = ['libp3ffmpeg', 'libp3fmod_audio', 'libfmodex', 'libfmodexL'] - - for base in os.listdir(GetOutputDir()+"/lib"): - if not base.endswith(".a") and base.split('.')[0] not in no_base_libs: - libname = "dstroot/base/Developer/Panda3D/lib/" + base - # We really need to specify -R in order not to follow symlinks - # On OSX, just specifying -P is not enough to do that. - oscmd("cp -R -P " + GetOutputDir() + "/lib/" + base + " " + libname) - - oscmd("mkdir -p dstroot/tools/Developer/Panda3D/bin") - oscmd("mkdir -p dstroot/tools/Developer/Tools") - oscmd("ln -s ../Panda3D/bin dstroot/tools/Developer/Tools/Panda3D") - oscmd("mkdir -p dstroot/tools/etc/paths.d") - # Trailing newline is important, works around a bug in OSX - WriteFile("dstroot/tools/etc/paths.d/Panda3D", "/Developer/Panda3D/bin\n") - - oscmd("mkdir -m 0755 -p dstroot/tools/usr/local/share/man/man1") - oscmd("install -m 0644 doc/man/*.1 dstroot/tools/usr/local/share/man/man1/") - - for base in os.listdir(GetOutputDir()+"/bin"): - binname = "dstroot/tools/Developer/Panda3D/bin/" + base - # OSX needs the -R argument to copy symbolic links correctly, it doesn't have -d. How weird. - oscmd("cp -R " + GetOutputDir() + "/bin/" + base + " " + binname) - - if PkgSkip("PYTHON")==0: - PV = SDK["PYTHONVERSION"][6:9] - oscmd("mkdir -p dstroot/pythoncode/usr/local/bin") - oscmd("mkdir -p dstroot/pythoncode/Developer/Panda3D/panda3d") - oscmd("mkdir -p dstroot/pythoncode/Library/Python/%s/site-packages" % PV) - WriteFile("dstroot/pythoncode/Library/Python/%s/site-packages/Panda3D.pth" % PV, "/Developer/Panda3D") - oscmd("cp -R %s/pandac dstroot/pythoncode/Developer/Panda3D/pandac" % GetOutputDir()) - oscmd("cp -R %s/direct dstroot/pythoncode/Developer/Panda3D/direct" % GetOutputDir()) - oscmd("ln -s %s dstroot/pythoncode/usr/local/bin/ppython" % SDK["PYTHONEXEC"]) - oscmd("cp -R %s/*.so dstroot/pythoncode/Developer/Panda3D/" % GetOutputDir(), True) - oscmd("cp -R %s/*.py dstroot/pythoncode/Developer/Panda3D/" % GetOutputDir(), True) - if os.path.isdir(GetOutputDir()+"/Pmw"): - oscmd("cp -R %s/Pmw dstroot/pythoncode/Developer/Panda3D/Pmw" % GetOutputDir()) - compileall.compile_dir("dstroot/pythoncode/Developer/Panda3D/Pmw") - WriteFile("dstroot/pythoncode/Developer/Panda3D/direct/__init__.py", "") - for base in os.listdir("dstroot/pythoncode/Developer/Panda3D/direct"): - if ((base != "extensions") and (base != "extensions_native")): - compileall.compile_dir("dstroot/pythoncode/Developer/Panda3D/direct/"+base) - - suffix = GetExtensionSuffix() - for base in os.listdir(GetOutputDir()+"/panda3d"): - if base.endswith('.py') or (base.endswith(suffix) and '.' not in base[:-len(suffix)]): - libname = "dstroot/pythoncode/Developer/Panda3D/panda3d/" + base - # We really need to specify -R in order not to follow symlinks - # On OSX, just specifying -P is not enough to do that. - oscmd("cp -R -P " + GetOutputDir() + "/panda3d/" + base + " " + libname) - - if not PkgSkip("FFMPEG"): - oscmd("mkdir -p dstroot/ffmpeg/Developer/Panda3D/lib") - oscmd("cp -R %s/lib/libp3ffmpeg.* dstroot/ffmpeg/Developer/Panda3D/lib/" % GetOutputDir()) - - #if not PkgSkip("OPENAL"): - # oscmd("mkdir -p dstroot/openal/Developer/Panda3D/lib") - # oscmd("cp -R %s/lib/libp3openal_audio.* dstroot/openal/Developer/Panda3D/lib/" % GetOutputDir()) - - if not PkgSkip("FMODEX"): - oscmd("mkdir -p dstroot/fmodex/Developer/Panda3D/lib") - oscmd("cp -R %s/lib/libp3fmod_audio.* dstroot/fmodex/Developer/Panda3D/lib/" % GetOutputDir()) - oscmd("cp -R %s/lib/libfmodex* dstroot/fmodex/Developer/Panda3D/lib/" % GetOutputDir()) - - oscmd("mkdir -p dstroot/headers/Developer/Panda3D/lib") - oscmd("cp -R %s/include dstroot/headers/Developer/Panda3D/include" % GetOutputDir()) - if os.path.isfile(GetOutputDir() + "/lib/libp3pystub.a"): - oscmd("cp -R -P %s/lib/libp3pystub.a dstroot/headers/Developer/Panda3D/lib/" % GetOutputDir()) - - if os.path.isdir("samples"): - oscmd("mkdir -p dstroot/samples/Developer/Examples/Panda3D") - oscmd("cp -R samples/* dstroot/samples/Developer/Examples/Panda3D/") - - oscmd("chmod -R 0775 dstroot/*") - DeleteVCS("dstroot") - DeleteBuildFiles("dstroot") - # We need to be root to perform a chown. Bleh. - # Fortunately PackageMaker does it for us, on 10.5 and above. - #oscmd("chown -R root:admin dstroot/*", True) - - oscmd("mkdir -p dstroot/Panda3D/Panda3D.mpkg/Contents/Packages/") - oscmd("mkdir -p dstroot/Panda3D/Panda3D.mpkg/Contents/Resources/en.lproj/") - - pkgs = ["base", "tools", "headers"] - if not PkgSkip("PYTHON"): pkgs.append("pythoncode") - if not PkgSkip("FFMPEG"): pkgs.append("ffmpeg") - #if not PkgSkip("OPENAL"): pkgs.append("openal") - if not PkgSkip("FMODEX"): pkgs.append("fmodex") - if os.path.isdir("samples"): pkgs.append("samples") - for pkg in pkgs: - identifier = "org.panda3d.panda3d.%s.pkg" % pkg - plist = open("/tmp/Info_plist", "w") - plist.write(Info_plist % { "package_id" : identifier, "version" : VERSION }) - plist.close() - if not os.path.isdir("dstroot/" + pkg): - os.makedirs("dstroot/" + pkg) - - if OSXTARGET and OSXTARGET <= (10, 5): - target = '--target %d.%d' % (OSXTARGET) - else: - target = '' - - if os.path.exists("/usr/bin/pkgbuild"): - # This new package builder is used in Lion and above. - cmd = '/usr/bin/pkgbuild --identifier ' + identifier + ' --version ' + VERSION + ' --root dstroot/' + pkg + '/ dstroot/Panda3D/Panda3D.mpkg/Contents/Packages/' + pkg + '.pkg' - - # In older versions, we use PackageMaker. Apple keeps changing its location. - elif os.path.exists("/Developer/usr/bin/packagemaker"): - cmd = '/Developer/usr/bin/packagemaker --info /tmp/Info_plist --version ' + VERSION + ' --out dstroot/Panda3D/Panda3D.mpkg/Contents/Packages/' + pkg + '.pkg ' + target + ' --domain system --root dstroot/' + pkg + '/ --no-relocate' - elif os.path.exists("/Applications/Xcode.app/Contents/Applications/PackageMaker.app/Contents/MacOS/PackageMaker"): - cmd = '/Applications/Xcode.app/Contents/Applications/PackageMaker.app/Contents/MacOS/PackageMaker --info /tmp/Info_plist --version ' + VERSION + ' --out dstroot/Panda3D/Panda3D.mpkg/Contents/Packages/' + pkg + '.pkg ' + target + ' --domain system --root dstroot/' + pkg + '/ --no-relocate' - elif os.path.exists("/Developer/Tools/PackageMaker.app/Contents/MacOS/PackageMaker"): - cmd = '/Developer/Tools/PackageMaker.app/Contents/MacOS/PackageMaker --info /tmp/Info_plist --version ' + VERSION + ' --out dstroot/Panda3D/Panda3D.mpkg/Contents/Packages/' + pkg + '.pkg ' + target + ' --domain system --root dstroot/' + pkg + '/ --no-relocate' - elif os.path.exists("/Developer/Tools/packagemaker"): - cmd = '/Developer/Tools/packagemaker -build -f dstroot/' + pkg + '/ -p dstroot/Panda3D/Panda3D.mpkg/Contents/Packages/' + pkg + '.pkg -i /tmp/Info_plist' - elif os.path.exists("/Applications/PackageMaker.app/Contents/MacOS/PackageMaker"): - cmd = '/Applications/PackageMaker.app/Contents/MacOS/PackageMaker --info /tmp/Info_plist --version ' + VERSION + ' --out dstroot/Panda3D/Panda3D.mpkg/Contents/Packages/' + pkg + '.pkg ' + target + ' --domain system --root dstroot/' + pkg + '/ --no-relocate' - else: - exit("Neither pkgbuild nor PackageMaker could be found!") - oscmd(cmd) - - if os.path.isfile("/tmp/Info_plist"): - oscmd("rm -f /tmp/Info_plist") - - # Now that we've built all of the individual packages, build the metapackage. - dist = open("dstroot/Panda3D/Panda3D.mpkg/Contents/distribution.dist", "w") - dist.write('\n') - dist.write('\n') - dist.write(' Panda3D SDK %s\n' % (VERSION)) - dist.write(' \n') - dist.write(' %s\n' % ReadFile("doc/LICENSE")) - dist.write(' \n') - for pkg in pkgs: - dist.write(' \n' % (pkg)) - dist.write(' \n') - dist.write(' \n') - dist.write(' \n') - dist.write(' \n') - dist.write(' \n') - dist.write(' \n') - dist.write(' \n') - - if not PkgSkip("PYTHON"): - dist.write(' \n') - dist.write(' \n') - dist.write(' \n') - - if not PkgSkip("FFMPEG"): - dist.write(' \n') - elif PkgSkip("VORBIS"): - dist.write(' It is not required for loading .wav or .opus files, which Panda3D can read out of the box.">\n') - elif PkgSkip("OPUS"): - dist.write(' It is not required for loading .wav or .ogg files, which Panda3D can read out of the box.">\n') - else: - dist.write(' It is not required for loading .wav, .ogg or .opus files, which Panda3D can read out of the box.">\n') - dist.write(' \n') - dist.write(' \n') - - #if not PkgSkip("OPENAL"): - # dist.write(' \n') - # dist.write(' \n') - # dist.write(' \n') - - if not PkgSkip("FMODEX"): - dist.write(' \n') - dist.write(' \n') - dist.write(' \n') - - if os.path.isdir("samples"): - dist.write(' \n') - dist.write(' \n') - dist.write(' \n') - - dist.write(' \n') - dist.write(' \n') - dist.write(' \n') - for pkg in pkgs: - size = GetDirectorySize("dstroot/" + pkg) // 1024 - dist.write(' file:./Contents/Packages/%s.pkg\n' % (pkg, size, pkg)) - dist.write('\n') - dist.close() - - oscmd('hdiutil create Panda3D-rw.dmg -volname "Panda3D SDK %s" -srcfolder dstroot/Panda3D' % (VERSION)) - oscmd('hdiutil convert Panda3D-rw.dmg -format UDBZ -o %s' % (dmg_name)) - oscmd('rm -f Panda3D-rw.dmg') - -def MakeInstallerFreeBSD(): - oscmd("rm -rf targetroot +DESC pkg-plist +MANIFEST") - oscmd("mkdir targetroot") - - # Invoke installpanda.py to install it into a temporary dir - if RUNTIME: - InstallRuntime(destdir = "targetroot", prefix = "/usr/local", outputdir = GetOutputDir()) - else: - InstallPanda(destdir = "targetroot", prefix = "/usr/local", outputdir = GetOutputDir()) - - if not os.path.exists("/usr/sbin/pkg"): - exit("Cannot create an installer without pkg") - - plist_txt = '' - for root, dirs, files in os.walk("targetroot/usr/local/", True): - for f in files: - plist_txt += os.path.join(root, f)[21:] + "\n" - - if not RUNTIME: - plist_txt += "@postexec /sbin/ldconfig -m /usr/local/lib/panda3d\n" - plist_txt += "@postunexec /sbin/ldconfig -R\n" - - for remdir in ("lib/panda3d", "share/panda3d", "include/panda3d"): - for root, dirs, files in os.walk("targetroot/usr/local/" + remdir, False): - for d in dirs: - plist_txt += "@dir %s\n" % os.path.join(root, d)[21:] - plist_txt += "@dir %s\n" % remdir - - oscmd("echo \"`pkg config abi | tr '[:upper:]' '[:lower:]' | cut -d: -f1,2`:*\" > " + GetOutputDir() + "/tmp/architecture.txt") - pkg_arch = ReadFile(GetOutputDir()+"/tmp/architecture.txt").strip() - - dependencies = '' - if PkgSkip("PYTHON") == 0: - # If this version of Python was installed from a package or ports, let's mark it as dependency. - oscmd("rm -f %s/tmp/python_dep" % GetOutputDir()) - oscmd("pkg query \"\n\t%%n : {\n\t\torigin : %%o,\n\t\tversion : %%v\n\t},\n\" python%s > %s/tmp/python_dep" % (SDK["PYTHONVERSION"][6:9:2], GetOutputDir()), True) - if os.path.isfile(GetOutputDir() + "/tmp/python_dep"): - python_pkg = ReadFile(GetOutputDir() + "/tmp/python_dep") - if python_pkg: - dependencies += python_pkg - - manifest_txt = INSTALLER_PKG_MANIFEST_FILE[1:].replace("NAME", 'panda3d' if not RUNTIME else 'panda3d-runtime') - manifest_txt = manifest_txt.replace("VERSION", VERSION) - manifest_txt = manifest_txt.replace("ARCH", pkg_arch) - manifest_txt = manifest_txt.replace("ORIGIN", 'devel/panda3d' if not RUNTIME else 'graphics/panda3d-runtime') - manifest_txt = manifest_txt.replace("DEPENDS", dependencies) - manifest_txt = manifest_txt.replace("INSTSIZE", str(GetDirectorySize("targetroot") / 1024 / 1024)) - - WriteFile("pkg-plist", plist_txt) - WriteFile("+DESC", INSTALLER_PKG_DESCR_FILE[1:] if not RUNTIME else RUNTIME_INSTALLER_PKG_DESCR_FILE[1:]) - WriteFile("+MANIFEST", manifest_txt) - oscmd("pkg create -p pkg-plist -r %s -m . -o . %s" % (os.path.abspath("targetroot"), "--verbose" if GetVerbose() else "--quiet")) - -def MakeInstallerAndroid(): - oscmd("rm -rf apkroot") - oscmd("mkdir apkroot") - - # Also remove the temporary apks. - apk_unaligned = os.path.join(GetOutputDir(), "tmp", "panda3d-unaligned.apk") - apk_unsigned = os.path.join(GetOutputDir(), "tmp", "panda3d-unsigned.apk") - if os.path.exists(apk_unaligned): - os.unlink(apk_unaligned) - if os.path.exists(apk_unsigned): - os.unlink(apk_unsigned) - - # Compile the Java classes into a Dalvik executable. - dx_cmd = "dx --dex --output=apkroot/classes.dex " - if GetOptimize() <= 2: - dx_cmd += "--debug " - if GetVerbose(): - dx_cmd += "--verbose " - if "ANDROID_API" in SDK: - dx_cmd += "--min-sdk-version=%d " % (SDK["ANDROID_API"]) - dx_cmd += os.path.join(GetOutputDir(), "classes") - oscmd(dx_cmd) - - # Copy the libraries one by one. In case of library dependencies, strip - # off any suffix (eg. libfile.so.1.0), as Android does not support them. - source_dir = os.path.join(GetOutputDir(), "lib") - target_dir = os.path.join("apkroot", "lib", SDK["ANDROID_ABI"]) - oscmd("mkdir -p %s" % (target_dir)) - - # Determine the library directories we should look in. - libpath = [source_dir] - for dir in os.environ.get("LD_LIBRARY_PATH", "").split(':'): - dir = os.path.expandvars(dir) - dir = os.path.expanduser(dir) - if os.path.isdir(dir): - dir = os.path.realpath(dir) - if not dir.startswith("/system") and not dir.startswith("/vendor"): - libpath.append(dir) - - def copy_library(source, base): - # Copy file to destination, stripping version suffix. - target = os.path.join(target_dir, base) - if not target.endswith('.so'): - target = target.rpartition('.so.')[0] + '.so' - - if os.path.isfile(target): - # Already processed. - return - - oscmd("cp %s %s" % (source, target)) - - # Walk through the library dependencies. - oscmd("ldd %s | grep .so > %s/tmp/otool-libs.txt" % (target, GetOutputDir()), True) - for line in open(GetOutputDir() + "/tmp/otool-libs.txt", "r"): - line = line.strip() - if not line: - continue - if '.so.' in line: - dep = line.rpartition('.so.')[0] + '.so' - oscmd("patchelf --replace-needed %s %s %s" % (line, dep, target), True) - else: - dep = line - - # Find it on the LD_LIBRARY_PATH. - for dir in libpath: - fulldep = os.path.join(dir, dep) - if os.path.isfile(fulldep): - copy_library(os.path.realpath(fulldep), dep) - break - - # Now copy every lib in the lib dir, and its dependencies. - for base in os.listdir(source_dir): - if not base.startswith('lib'): - continue - if not base.endswith('.so') and '.so.' not in base: - continue - - source = os.path.join(source_dir, base) - if os.path.islink(source): - continue - copy_library(source, base) - - # Same for Python extension modules. However, Android is strict about - # library naming, so we have a special naming scheme for these, in - # conjunction with a custom import hook to find these modules. - if not PkgSkip("PYTHON"): - suffix = GetExtensionSuffix() - source_dir = os.path.join(GetOutputDir(), "panda3d") - for base in os.listdir(source_dir): - if not base.endswith(suffix): - continue - modname = base[:-len(suffix)] - if '.' not in modname: - source = os.path.join(source_dir, base) - copy_library(source, "libpy.panda3d.{}.so".format(modname)) - - # Same for standard Python modules. - import _ctypes - source_dir = os.path.dirname(_ctypes.__file__) - for base in os.listdir(source_dir): - if not base.endswith('.so'): - continue - modname = base.partition('.')[0] - source = os.path.join(source_dir, base) - copy_library(source, "libpy.{}.so".format(modname)) - - def copy_python_tree(source_root, target_root): - for source_dir, dirs, files in os.walk(source_root): - if 'site-packages' in dirs: - dirs.remove('site-packages') - - if not any(base.endswith('.py') for base in files): - continue - - target_dir = os.path.join(target_root, os.path.relpath(source_dir, source_root)) - target_dir = os.path.normpath(target_dir) - os.makedirs(target_dir, 0o755) - - for base in files: - if base.endswith('.py'): - target = os.path.join(target_dir, base) - shutil.copy(os.path.join(source_dir, base), target) - - # Copy the Python standard library to the .apk as well. - from distutils.sysconfig import get_python_lib - stdlib_source = get_python_lib(False, True) - stdlib_target = os.path.join("apkroot", "lib", "python{0}.{1}".format(*sys.version_info)) - copy_python_tree(stdlib_source, stdlib_target) - - # But also copy over our custom site.py. - shutil.copy("panda/src/android/site.py", os.path.join(stdlib_target, "site.py")) - - # And now make a site-packages directory containing our direct/panda3d/pandac modules. - for tree in "panda3d", "direct", "pandac": - copy_python_tree(os.path.join(GetOutputDir(), tree), os.path.join(stdlib_target, "site-packages", tree)) - - # Copy the models and config files to the virtual assets filesystem. - oscmd("mkdir apkroot/assets") - oscmd("cp -R %s apkroot/assets/models" % (os.path.join(GetOutputDir(), "models"))) - oscmd("cp -R %s apkroot/assets/etc" % (os.path.join(GetOutputDir(), "etc"))) - - # Make an empty res folder. It's needed for the apk to be installable, apparently. - oscmd("mkdir apkroot/res") - - # Now package up the application - oscmd("cp panda/src/android/pview_manifest.xml apkroot/AndroidManifest.xml") - aapt_cmd = "aapt package" - aapt_cmd += " -F %s" % (apk_unaligned) - aapt_cmd += " -M apkroot/AndroidManifest.xml" - aapt_cmd += " -A apkroot/assets -S apkroot/res" - aapt_cmd += " -I $PREFIX/share/aapt/android.jar" - oscmd(aapt_cmd) - - # And add all the libraries to it. - oscmd("cd apkroot && aapt add ../%s classes.dex" % (apk_unaligned)) - for path, dirs, files in os.walk('apkroot/lib'): - if files: - rel = os.path.relpath(path, 'apkroot') - oscmd("cd apkroot && aapt add ../%s %s/*" % (apk_unaligned, rel)) - - # Now align the .apk, which is necessary for Android to load it. - oscmd("zipalign -v -p 4 %s %s" % (apk_unaligned, apk_unsigned)) - - # Finally, sign it using a debug key. This is generated if it doesn't exist. - oscmd("apksigner debug.ks %s panda3d.apk" % (apk_unsigned)) - - # Clean up. - oscmd("rm -rf apkroot") - os.unlink(apk_unaligned) - os.unlink(apk_unsigned) - -try: - if INSTALLER: - ProgressOutput(100.0, "Building installer") - target = GetTarget() - if target == 'windows': - fn = "Panda3D-" - dir = "Panda3D-" + VERSION - - if RUNTIME: - fn += "Runtime-" - title = "Panda3D " + VERSION - else: - title = "Panda3D SDK " + VERSION - - fn += VERSION - - if not RUNTIME and SDK["PYTHONVERSION"] != "python2.7": - fn += '-py' + SDK["PYTHONVERSION"][6:] - - if GetOptimize() <= 2: - fn += "-dbg" - if GetTargetArch() == 'x64': - fn += '-x64' - dir += '-x64' - - MakeInstallerNSIS(fn + '.exe', title, 'C:\\' + dir) - if not RUNTIME: - MakeDebugSymbolArchive(fn + '-pdb.zip', dir) - elif (target == 'linux'): - MakeInstallerLinux() - elif (target == 'darwin'): - MakeInstallerOSX() - elif (target == 'freebsd'): - MakeInstallerFreeBSD() - elif (target == 'android'): - MakeInstallerAndroid() - else: - exit("Do not know how to make an installer for this platform") - - if WHEEL: - ProgressOutput(100.0, "Building wheel") - from makewheel import makewheel - makewheel(WHLVERSION, GetOutputDir()) -finally: - SaveDependencyCache() +if INSTALLER: + ProgressOutput(100.0, "Building installer") + from makepackage import MakeInstaller + + # When using the --installer flag, only install for the current version. + python_versions = [] + if python_version_info: + python_versions.append(python_version_info) + + MakeInstaller(version=VERSION, outputdir=GetOutputDir(), + optimize=GetOptimize(), compressor=COMPRESSOR, + debversion=DEBVERSION, rpmrelease=RPMRELEASE, + runtime=RUNTIME, python_versions=python_versions) + +if WHEEL: + ProgressOutput(100.0, "Building wheel") + from makewheel import makewheel + makewheel(WHLVERSION, GetOutputDir()) ########################################################################################## # diff --git a/makepanda/makepanda.vcproj b/makepanda/makepanda.vcproj index c4c34db4e3..03324fb60e 100644 --- a/makepanda/makepanda.vcproj +++ b/makepanda/makepanda.vcproj @@ -1043,7 +1043,7 @@ - + @@ -1054,7 +1054,7 @@ - + @@ -1089,7 +1089,7 @@ - + @@ -3578,25 +3578,6 @@ - - - - - - - - - - - - - - - - - - - diff --git a/makepanda/makepandacore.py b/makepanda/makepandacore.py index 435b45a023..8790538395 100644 --- a/makepanda/makepandacore.py +++ b/makepanda/makepandacore.py @@ -3362,8 +3362,13 @@ def CalcLocation(fn, ipath): def FindLocation(fn, ipath, pyabi=None): - if (GetLinkAllStatic() and fn.endswith(".dll")): - fn = fn[:-4] + ".lib" + if GetLinkAllStatic(): + if fn.endswith(".dll"): + fn = fn[:-4] + ".lib" + elif fn.endswith(".pyd"): + fn = "libpy.panda3d." \ + + os.path.splitext(fn[:-4] + GetExtensionSuffix())[0] + ".lib" + loc = CalcLocation(fn, ipath) base, ext = os.path.splitext(fn) @@ -3383,6 +3388,71 @@ def FindLocation(fn, ipath, pyabi=None): ORIG_EXT[loc] = ext return loc + +######################################################################## +## +## These files maintain a python_versions.json file in the built/tmp +## directory that can be used by the other scripts in this directory. +## +######################################################################## + + +def GetCurrentPythonVersionInfo(): + if PkgSkip("PYTHON"): + return + + from distutils.sysconfig import get_python_lib + return { + "version": SDK["PYTHONVERSION"][6:9], + "soabi": GetPythonABI(), + "ext_suffix": GetExtensionSuffix(), + "executable": sys.executable, + "purelib": get_python_lib(False), + "platlib": get_python_lib(True), + } + + +def UpdatePythonVersionInfoFile(new_info): + import json + + json_file = os.path.join(GetOutputDir(), "tmp", "python_versions.json") + json_data = [] + if os.path.isfile(json_file) and not PkgSkip("PYTHON"): + try: + json_data = json.load(open(json_file, 'r')) + except: + json_data = [] + + # Prune the list by removing the entries that conflict with our build, + # plus the entries that no longer exist + for version_info in json_data[:]: + core_pyd = os.path.join(GetOutputDir(), "panda3d", "core" + version_info["ext_suffix"]) + if version_info["ext_suffix"] == new_info["ext_suffix"] or \ + version_info["soabi"] == new_info["soabi"] or \ + not os.path.isfile(core_pyd): + json_data.remove(version_info) + + if not PkgSkip("PYTHON"): + json_data.append(new_info) + + if VERBOSE: + print("Writing %s" % (json_file)) + json.dump(json_data, open(json_file, 'w'), indent=4) + + +def ReadPythonVersionInfoFile(): + import json + + json_file = os.path.join(GetOutputDir(), "tmp", "python_versions.json") + if os.path.isfile(json_file): + try: + return json.load(open(json_file, 'r')) + except: + pass + + return [] + + ######################################################################## ## ## TargetAdd @@ -3513,11 +3583,15 @@ def TargetAdd(target, dummy=0, opts=[], input=[], dep=[], ipath=None, winrc=None if winrc and GetTarget() == 'windows': TargetAdd(target, input=WriteResourceFile(target.split("/")[-1].split(".")[0], **winrc)) - if target.endswith(".in"): + ext = os.path.splitext(target)[1] + if ext == ".in": if not CrossCompiling(): t.deps[FindLocation("interrogate.exe", [])] = 1 t.deps[FindLocation("dtool_have_python.dat", [])] = 1 + if ext in (".obj", ".tlb", ".res", ".plugin", ".app") or ext in SUFFIX_DLL or ext in SUFFIX_LIB: + t.deps[FindLocation("platform.dat", [])] = 1 + if target.endswith(".obj") and any(x.endswith(".in") for x in input): if not CrossCompiling(): t.deps[FindLocation("interrogate_module.exe", [])] = 1 diff --git a/makepanda/makewheel.py b/makepanda/makewheel.py index b74ad0116d..18f92379b8 100644 --- a/makepanda/makewheel.py +++ b/makepanda/makewheel.py @@ -24,14 +24,6 @@ from makepandacore import ColorText, LocateBinary, ParsePandaVersion, GetExtensi from base64 import urlsafe_b64encode -default_platform = get_platform() - -if default_platform.startswith("linux-"): - # Is this manylinux1? - if (os.path.isfile("/lib/libc-2.5.so") or os.path.isfile("/lib64/libc-2.5.so")) and os.path.isdir("/opt/python"): - default_platform = default_platform.replace("linux", "manylinux1") - - def get_abi_tag(): if sys.version_info >= (3, 0): soabi = get_config_var('SOABI') @@ -68,26 +60,32 @@ def is_elf_file(path): open(path, 'rb').read(4) == b'\x7FELF' -def is_mach_o_file(path): +def is_macho_or_fat_file(path): base = os.path.basename(path) return os.path.isfile(path) and '.' not in base and \ - open(path, 'rb').read(4) in (b'\xCA\xFE\xBA\xBE', b'\xBE\xBA\xFE\bCA', - b'\xFE\xED\xFA\xCE', b'\xCE\xFA\xED\xFE', - b'\xFE\xED\xFA\xCF', b'\xCF\xFA\xED\xFE') + open(path, 'rb').read(4) in (b'\xFE\xED\xFA\xCE', b'\xCE\xFA\xED\xFE', + b'\xFE\xED\xFA\xCF', b'\xCF\xFA\xED\xFE', + b'\xCA\xFE\xBA\xBE', b'\xBE\xBA\xFE\xCA', + b'\xCA\xFE\xBA\xBF', b'\xBF\xBA\xFE\xCA') def is_fat_file(path): return os.path.isfile(path) and \ - open(path, 'rb').read(4) in (b'\xCA\xFE\xBA\xBE', b'\xBE\xBA\xFE\bCA') + open(path, 'rb').read(4) in (b'\xCA\xFE\xBA\xBE', b'\xBE\xBA\xFE\xCA', + b'\xCA\xFE\xBA\xBF', b'\xBF\xBA\xFE\xCA') + + +def get_python_ext_module_dir(): + import _ctypes + return os.path.dirname(_ctypes.__file__) if sys.platform in ('win32', 'cygwin'): is_executable = is_exe_file elif sys.platform == 'darwin': - is_executable = is_mach_o_file + is_executable = is_macho_or_fat_file else: is_executable = is_elf_file - # Other global parameters PY_VERSION = "cp{0}{1}".format(*sys.version_info) ABI_TAG = get_abi_tag() @@ -96,6 +94,24 @@ EXCLUDE_EXT = [".pyc", ".pyo", ".N", ".prebuilt", ".xcf", ".plist", ".vcproj", " # Plug-ins to install. PLUGIN_LIBS = ["pandagl", "pandagles", "pandagles2", "pandadx9", "p3tinydisplay", "p3ptloader", "p3assimp", "p3ffmpeg", "p3openal_audio", "p3fmod_audio"] +# Libraries included in manylinux ABI that should be ignored. See PEP 513/571. +MANYLINUX_LIBS = [ + "libgcc_s.so.1", "libstdc++.so.6", "libm.so.6", "libdl.so.2", "librt.so.1", + "libcrypt.so.1", "libc.so.6", "libnsl.so.1", "libutil.so.1", + "libpthread.so.0", "libresolv.so.2", "libX11.so.6", "libXext.so.6", + "libXrender.so.1", "libICE.so.6", "libSM.so.6", "libGL.so.1", + "libgobject-2.0.so.0", "libgthread-2.0.so.0", "libglib-2.0.so.0", + + # These are not mentioned in manylinux1 spec but should nonetheless always + # be excluded. + "linux-vdso.so.1", "linux-gate.so.1", "ld-linux.so.2", +] + +# Binaries to never scan for dependencies on non-Windows systems. +IGNORE_UNIX_DEPS_OF = [ + "panda3d_tools/pstats", +] + WHEEL_DATA = """Wheel-Version: 1.0 Generator: makepanda Root-Is-Purelib: false @@ -265,6 +281,7 @@ class WheelFile(object): # Used to locate dependency libraries. self.lib_path = [] self.dep_paths = {} + self.ignore_deps = set() def consider_add_dependency(self, target_path, dep, search_path=None): """Considers adding a dependency library. @@ -277,29 +294,38 @@ class WheelFile(object): self.dep_paths[dep] = None - if dep.lower().startswith("python") or os.path.basename(dep).startswith("libpython"): - # Don't include the Python library. + if dep in self.ignore_deps or dep.lower().startswith("python") or os.path.basename(dep).startswith("libpython"): + # Don't include the Python library, or any other explicit ignore. + if GetVerbose(): + print("Ignoring {0} (explicitly ignored)".format(dep)) return if sys.platform == "darwin" and dep.endswith(".so"): # Temporary hack for 1.9, which had link deps on modules. return - source_path = None + if sys.platform == "darwin" and dep.startswith("/System/"): + return - if search_path is None: - search_path = self.lib_path + if dep.startswith('/'): + source_path = dep + else: + source_path = None - for lib_dir in search_path: - # Ignore static stuff. - path = os.path.join(lib_dir, dep) - if os.path.isfile(path): - source_path = os.path.normpath(path) - break + if search_path is None: + search_path = self.lib_path + + for lib_dir in search_path: + # Ignore static stuff. + path = os.path.join(lib_dir, dep) + if os.path.isfile(path): + source_path = os.path.normpath(path) + break if not source_path: # Couldn't find library in the panda3d lib dir. - #print("Ignoring %s" % (dep)) + if GetVerbose(): + print("Ignoring {0} (not in search path)".format(dep)) return self.dep_paths[dep] = target_path @@ -309,20 +335,19 @@ class WheelFile(object): def write_file(self, target_path, source_path): """Adds the given file to the .whl file.""" + orig_source_path = source_path + # If this is a .so file, we should set the rpath appropriately. temp = None - ext = os.path.splitext(source_path)[1] + basename, ext = os.path.splitext(source_path) if ext in ('.so', '.dylib') or '.so.' in os.path.basename(source_path) or \ (not ext and is_executable(source_path)): - # Scan and add Unix dependencies. - deps = scan_dependencies(source_path) - for dep in deps: - # Only include dependencies with relative path. Otherwise we - # end up overwriting system files like /lib/ld-linux.so.2! - # Yes, it happened to me. - if '/' not in dep: - target_dep = os.path.dirname(target_path) + '/' + dep - self.consider_add_dependency(target_dep, dep) + + # Scan Unix dependencies. + if target_path not in IGNORE_UNIX_DEPS_OF: + deps = scan_dependencies(source_path) + else: + deps = [] suffix = '' if '.so' in os.path.basename(source_path): @@ -333,7 +358,10 @@ class WheelFile(object): temp = tempfile.NamedTemporaryFile(suffix=suffix, prefix='whl', delete=False) # On macOS, if no fat wheel was requested, extract the right architecture. - if sys.platform == "darwin" and is_fat_file(source_path) and not self.platform.endswith("_intel"): + if sys.platform == "darwin" and is_fat_file(source_path) \ + and not self.platform.endswith("_intel") \ + and "_fat" not in self.platform: + if self.platform.endswith("_x86_64"): arch = 'x86_64' else: @@ -343,26 +371,55 @@ class WheelFile(object): # Otherwise, just copy it over. temp.write(open(source_path, 'rb').read()) - os.fchmod(temp.fileno(), os.fstat(temp.fileno()).st_mode | 0o111) temp.close() + os.chmod(temp.name, os.stat(temp.name).st_mode | 0o711) - # Fix things like @loader_path/../lib references + # Now add dependencies. On macOS, fix @loader_path references. if sys.platform == "darwin": + if source_path.endswith('deploy-stubw'): + deps_path = '@executable_path/../Frameworks' + else: + deps_path = '@loader_path' loader_path = [os.path.dirname(source_path)] for dep in deps: - if '@loader_path' not in dep: + if dep.endswith('/Python'): + # If this references the Python framework, change it + # to reference libpython instead. + new_dep = deps_path + '/libpython{0}.{1}.dylib'.format(*sys.version_info) + + elif '@loader_path' in dep: + dep_path = dep.replace('@loader_path', '.') + target_dep = os.path.dirname(target_path) + '/' + os.path.basename(dep) + target_dep = self.consider_add_dependency(target_dep, dep_path, loader_path) + if not target_dep: + # It won't be included, so no use adjusting the path. + continue + new_dep = os.path.join(deps_path, os.path.relpath(target_dep, os.path.dirname(target_path))) + + elif dep.startswith('/Library/Frameworks/Python.framework/'): + # Add this dependency if it's in the Python directory. + target_dep = os.path.dirname(target_path) + '/' + os.path.basename(dep) + target_dep = self.consider_add_dependency(target_dep, dep, loader_path) + if not target_dep: + # It won't be included, so no use adjusting the path. + continue + new_dep = os.path.join(deps_path, os.path.relpath(target_dep, os.path.dirname(target_path))) + + else: + if '/' in dep: + if GetVerbose(): + print("Ignoring dependency %s" % (dep)) continue - dep_path = dep.replace('@loader_path', '.') - target_dep = os.path.dirname(target_path) + '/' + os.path.basename(dep) - target_dep = self.consider_add_dependency(target_dep, dep_path, loader_path) - if not target_dep: - # It won't be included, so no use adjusting the path. - continue - - new_dep = os.path.join('@loader_path', os.path.relpath(target_dep, os.path.dirname(target_path))) subprocess.call(["install_name_tool", "-change", dep, new_dep, temp.name]) else: + # On other unixes, we just add dependencies normally. + for dep in deps: + # Only include dependencies with relative path, for now. + if '/' not in dep: + target_dep = os.path.dirname(target_path) + '/' + dep + self.consider_add_dependency(target_dep, dep) + subprocess.call(["strip", "-s", temp.name]) subprocess.call(["patchelf", "--set-rpath", "$ORIGIN", temp.name]) @@ -392,7 +449,7 @@ class WheelFile(object): self.records.append("{0},sha256={1},{2}\n".format(target_path, digest, size)) if GetVerbose(): - print("Adding %s from %s" % (target_path, source_path)) + print("Adding {0} from {1}".format(target_path, orig_source_path)) self.zip_file.write(source_path, target_path) #if temp: @@ -433,11 +490,24 @@ class WheelFile(object): self.zip_file.close() -def makewheel(version, output_dir, platform=default_platform): +def makewheel(version, output_dir, platform=None): if sys.platform not in ("win32", "darwin") and not sys.platform.startswith("cygwin"): if not LocateBinary("patchelf"): raise Exception("patchelf is required when building a Linux wheel.") + if platform is None: + # Determine the platform from the build. + platform_dat = os.path.join(output_dir, 'tmp', 'platform.dat') + if os.path.isfile(platform_dat): + platform = open(platform_dat, 'r').read().strip() + else: + print("Could not find platform.dat in build directory") + platform = get_platform() + if platform.startswith("linux-"): + # Is this manylinux1? + if os.path.isfile("/lib/libc-2.5.so") and os.path.isdir("/opt/python"): + platform = platform.replace("linux", "manylinux1") + platform = platform.replace('-', '_').replace('.', '_') # Global filepaths @@ -483,6 +553,21 @@ def makewheel(version, output_dir, platform=default_platform): whl = WheelFile('panda3d', version, platform) whl.lib_path = [libs_dir] + if sys.platform == "win32": + whl.lib_path.append(join(output_dir, "python", "DLLs")) + + if platform.startswith("manylinux"): + # On manylinux1, we pick up all libraries except for the ones specified + # by the manylinux1 ABI. + whl.lib_path.append("/usr/local/lib") + + if platform.endswith("_x86_64"): + whl.lib_path += ["/lib64", "/usr/lib64"] + else: + whl.lib_path += ["/lib", "/usr/lib"] + + whl.ignore_deps.update(MANYLINUX_LIBS) + # Add the trees with Python modules. whl.write_directory('direct', direct_dir) @@ -493,6 +578,7 @@ def makewheel(version, output_dir, platform=default_platform): __version__ = '{0}' """.format(version)) + # Copy the extension modules from the panda3d directory. ext_suffix = GetExtensionSuffix() for file in os.listdir(panda3d_dir): @@ -509,6 +595,23 @@ __version__ = '{0}' whl.write_file(target_path, source_path) + # And copy the extension modules from the Python installation into the + # deploy_libs directory, for use by deploy-ng. + ext_suffix = '.pyd' if sys.platform in ('win32', 'cygwin') else '.so' + ext_mod_dir = get_python_ext_module_dir() + + for file in os.listdir(ext_mod_dir): + if file.endswith(ext_suffix): + source_path = os.path.join(ext_mod_dir, file) + + if file.endswith('.pyd') and platform.startswith('cygwin'): + # Rename it to .dll for cygwin Python to be able to load it. + target_path = 'deploy_libs/' + os.path.splitext(file)[0] + '.dll' + else: + target_path = 'deploy_libs/' + file + + whl.write_file(target_path, source_path) + # Add plug-ins. for lib in PLUGIN_LIBS: plugin_name = 'lib' + lib @@ -540,7 +643,7 @@ __version__ = '{0}' # Add a panda3d-tools directory containing the executables. entry_points = '[console_scripts]\n' entry_points += 'eggcacher = direct.directscripts.eggcacher:main\n' - entry_points += 'pfreeze = direct.showutil.pfreeze:main\n' + entry_points += 'pfreeze = direct.dist.pfreeze:main\n' tools_init = '' for file in os.listdir(bin_dir): basename = os.path.splitext(file)[0] @@ -557,6 +660,9 @@ __version__ = '{0}' funcname = basename.replace('-', '_') entry_points += '{0} = panda3d_tools:{1}\n'.format(basename, funcname) tools_init += '{0} = lambda: _exec_tool({1!r})\n'.format(funcname, file) + entry_points += '[distutils.commands]\n' + entry_points += 'build_apps = direct.dist.commands:build_apps\n' + entry_points += 'bdist_apps = direct.dist.commands:bdist_apps\n' whl.write_file_data('panda3d_tools/__init__.py', PANDA3D_TOOLS_INIT.format(tools_init)) @@ -570,6 +676,18 @@ __version__ = '{0}' whl.write_file(info_dir + '/README.md', readme_src) whl.write_file_data(info_dir + '/top_level.txt', 'direct\npanda3d\npandac\npanda3d_tools\n') + # Add libpython for deployment + if sys.platform in ('win32', 'cygwin'): + pylib_name = 'python{0}{1}.dll'.format(*sys.version_info) + pylib_path = os.path.join(get_config_var('BINDIR'), pylib_name) + elif sys.platform == 'darwin': + pylib_name = 'libpython{0}.{1}.dylib'.format(*sys.version_info) + pylib_path = os.path.join(get_config_var('LIBDIR'), pylib_name) + else: + pylib_name = get_config_var('LDLIBRARY') + pylib_path = os.path.join(get_config_var('LIBDIR'), pylib_name) + whl.write_file('deploy_libs/' + pylib_name, pylib_path) + whl.close() @@ -580,7 +698,7 @@ if __name__ == "__main__": parser.add_option('', '--version', dest = 'version', help = 'Panda3D version number (default: %s)' % (version), default = version) parser.add_option('', '--outputdir', dest = 'outputdir', help = 'Makepanda\'s output directory (default: built)', default = 'built') parser.add_option('', '--verbose', dest = 'verbose', help = 'Enable verbose output', action = 'store_true', default = False) - parser.add_option('', '--platform', dest = 'platform', help = 'Override platform tag (default: %s)' % (default_platform), default = get_platform()) + parser.add_option('', '--platform', dest = 'platform', help = 'Override platform tag', default = None) (options, args) = parser.parse_args() SetVerbose(options.verbose) diff --git a/makepanda/panda-install.bmp b/makepanda/panda-install.bmp index bfe7063586..2e6b5860f5 100644 Binary files a/makepanda/panda-install.bmp and b/makepanda/panda-install.bmp differ diff --git a/makepanda/test_imports.py b/makepanda/test_imports.py index ecd87cbb66..a9b22943c8 100644 --- a/makepanda/test_imports.py +++ b/makepanda/test_imports.py @@ -62,6 +62,7 @@ import direct.directutil.LargeBlobSenderConsts import direct.directutil.Mopath import direct.directutil.Verify import direct.directutil.WeightedChoice +import direct.dist.FreezeTool import direct.distributed.AsyncRequest import direct.distributed.CRCache import direct.distributed.CRDataCache @@ -234,7 +235,6 @@ import direct.showbase.VFSImporter import direct.showbase.WxGlobal import direct.showutil.BuildGeometry import direct.showutil.Effects -import direct.showutil.FreezeTool import direct.showutil.Rope import direct.showutil.TexMemWatcher import direct.showutil.TexViewer diff --git a/models/plugin_images/installer.bmp b/models/plugin_images/installer.bmp index bfe7063586..2e6b5860f5 100644 Binary files a/models/plugin_images/installer.bmp and b/models/plugin_images/installer.bmp differ diff --git a/panda/src/androiddisplay/androidGraphicsWindow.cxx b/panda/src/androiddisplay/androidGraphicsWindow.cxx index 431f9419b7..cc04022e81 100644 --- a/panda/src/androiddisplay/androidGraphicsWindow.cxx +++ b/panda/src/androiddisplay/androidGraphicsWindow.cxx @@ -55,9 +55,9 @@ AndroidGraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe, _app = panda_android_app; - GraphicsWindowInputDevice device = - GraphicsWindowInputDevice::pointer_and_keyboard(this, "keyboard_mouse"); + PT(GraphicsWindowInputDevice) device = GraphicsWindowInputDevice::pointer_and_keyboard(this, "keyboard_mouse"); add_input_device(device); + _input = device; } /** @@ -486,22 +486,22 @@ handle_key_event(const AInputEvent *event) { /* int32_t meta = AKeyEvent_getMetaState(event); if (meta | AMETA_ALT_ON) { - _input_devices[0].button_down(KeyboardButton.alt()); + _input->button_down(KeyboardButton.alt()); } if (meta | AMETA_ALT_LEFT_ON) { - _input_devices[0].button_down(KeyboardButton.lalt()); + _input->button_down(KeyboardButton.lalt()); } if (meta | AMETA_ALT_RIGHT_ON) { - _input_devices[0].button_down(KeyboardButton.ralt()); + _input->button_down(KeyboardButton.ralt()); } if (meta | AMETA_SHIFT_ON) { - _input_devices[0].button_down(KeyboardButton.shift()); + _input->button_down(KeyboardButton.shift()); } if (meta | AMETA_SHIFT_LEFT_ON) { - _input_devices[0].button_down(KeyboardButton.lshift()); + _input->button_down(KeyboardButton.lshift()); } if (meta | AMETA_SHIFT_RIGHT_ON) { - _input_devices[0].button_down(KeyboardButton.rshift()); + _input->button_down(KeyboardButton.rshift()); }*/ int32_t keycode = AKeyEvent_getKeyCode(event); @@ -517,12 +517,12 @@ handle_key_event(const AInputEvent *event) { int32_t action = AKeyEvent_getAction(event); if (action == AKEY_EVENT_ACTION_DOWN) { if (AKeyEvent_getRepeatCount(event) > 0) { - _input_devices[0].button_resume_down(button); + _input->button_resume_down(button); } else { - _input_devices[0].button_down(button); + _input->button_down(button); } } else if (action == AKEY_EVENT_ACTION_UP) { - _input_devices[0].button_up(button); + _input->button_up(button); } // TODO AKEY_EVENT_ACTION_MULTIPLE @@ -549,16 +549,16 @@ handle_motion_event(const AInputEvent *event) { if (changed != 0) { if (changed & AMOTION_EVENT_BUTTON_PRIMARY) { if (button_state & AMOTION_EVENT_BUTTON_PRIMARY) { - _input_devices[0].button_down(MouseButton::one()); + _input->button_down(MouseButton::one()); } else { - _input_devices[0].button_up(MouseButton::one()); + _input->button_up(MouseButton::one()); } } if (changed & AMOTION_EVENT_BUTTON_SECONDARY) { if (button_state & AMOTION_EVENT_BUTTON_SECONDARY) { - _input_devices[0].button_down(MouseButton::three()); + _input->button_down(MouseButton::three()); } else { - _input_devices[0].button_up(MouseButton::three()); + _input->button_up(MouseButton::three()); } } _mouse_button_state = button_state; @@ -568,7 +568,7 @@ handle_motion_event(const AInputEvent *event) { float x = AMotionEvent_getX(event, 0) - _app->contentRect.left; float y = AMotionEvent_getY(event, 0) - _app->contentRect.top; - _input_devices[0].set_pointer_in_window(x, y); + _input->set_pointer_in_window(x, y); return 1; } diff --git a/panda/src/androiddisplay/androidGraphicsWindow.h b/panda/src/androiddisplay/androidGraphicsWindow.h index 0842b18f2b..f8b75421f6 100644 --- a/panda/src/androiddisplay/androidGraphicsWindow.h +++ b/panda/src/androiddisplay/androidGraphicsWindow.h @@ -73,7 +73,7 @@ private: int32_t _mouse_button_state; - const ARect *rect; + GraphicsWindowInputDevice *_input; public: static TypeHandle get_class_type() { diff --git a/panda/src/audiotraits/openalAudioSound.cxx b/panda/src/audiotraits/openalAudioSound.cxx index 8d41a61fa3..9244b7509f 100644 --- a/panda/src/audiotraits/openalAudioSound.cxx +++ b/panda/src/audiotraits/openalAudioSound.cxx @@ -44,7 +44,7 @@ OpenALAudioSound(OpenALAudioManager* manager, _loops_completed(0), _source(0), _manager(manager), - _volume(manager->get_volume()), + _volume(1.0f), _balance(0), _play_rate(1.0), _positional(positional), diff --git a/panda/src/awesomium/AwMouseAndKeyboard.cxx b/panda/src/awesomium/AwMouseAndKeyboard.cxx deleted file mode 100644 index 9948c0d49d..0000000000 --- a/panda/src/awesomium/AwMouseAndKeyboard.cxx +++ /dev/null @@ -1,47 +0,0 @@ -/** - * 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 AwMouseAndKeyboard.cxx - * @author Bei Yang - * @date 2010-03 - */ - -#include "config_awesomium.h" -#include "AwMouseAndKeyboard.h" -#include "dataNodeTransmit.h" - -TypeHandle AwMouseAndKeyboard::_type_handle; - -AwMouseAndKeyboard::AwMouseAndKeyboard(const std::string &name): -DataNode(name) -{ - _button_events_input = define_input("button_events", ButtonEventList::get_class_type()); - _button_events_output = define_output("button_events", ButtonEventList::get_class_type()); -} - - -void AwMouseAndKeyboard::do_transmit_data(DataGraphTraverser *trav, const DataNodeTransmit &input, DataNodeTransmit &output){ - - if (input.has_data(_button_events_input)) { - const ButtonEventList *button_events; - DCAST_INTO_V(button_events, input.get_data(_button_events_input).get_ptr()); - - int num_events = button_events->get_num_events(); - for (int i = 0; i < num_events; i++) { - const ButtonEvent &be = button_events->get_event(i); - std::string event_name = be._button.get_name(); - printf("Button Event! : %s with code %i and index %i ", event_name.c_str(), be._keycode, be._button.get_index()); - if(be._type == ButtonEvent::T_down) printf("down"); - if(be._type == ButtonEvent::T_repeat) printf("repeat"); - if(be._type == ButtonEvent::T_up) printf("up"); - if(be._type == ButtonEvent::T_resume_down) printf("T_resume_down"); - printf("\n"); - } - } - -} diff --git a/panda/src/awesomium/ReadMe.txt b/panda/src/awesomium/ReadMe.txt deleted file mode 100644 index faa064455a..0000000000 --- a/panda/src/awesomium/ReadMe.txt +++ /dev/null @@ -1,8 +0,0 @@ -See http://www.sirikata.com/wiki/index.php?title=Compiling_Awesomium - -But the Chromium revision 22725. -The awesomium git repository used is at: http://github.com/pathorn/awesomium/network -The version of awesomium is from the master August 7 checkin, tree fc3239b4f49031285682cb5d78256d56e9001b66 - -Any method that had a wstring in the parameter, a second method was created which just accepts all std::string. This fixes the VC7 - VC9 linker error. - diff --git a/panda/src/awesomium/WebBrowserTexture.cxx b/panda/src/awesomium/WebBrowserTexture.cxx deleted file mode 100644 index 1051a33e36..0000000000 --- a/panda/src/awesomium/WebBrowserTexture.cxx +++ /dev/null @@ -1,194 +0,0 @@ -/** - * 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 WebBrowserTexture.cxx - * @author bei yang - * @date 2010-03 - */ - -#include "config_awesomium.h" -#include "WebBrowserTexture.h" - -TypeHandle WebBrowserTexture::_type_handle; - -/** - * Copy constructor for web browser texture. The behavior of copying a - * webtexture is that will be the same as a standard texture copy. However, - * the content will remain the system until set_web_view is called. - */ -WebBrowserTexture::WebBrowserTexture(const WebBrowserTexture ©): -Texture(copy) -{ - // this kind of assumes that the previous texture was initialized properly - _aw_web_view = copy._aw_web_view; - _update_active = copy._update_active; - _flip_texture_active = copy._flip_texture_active; -} - -/** - * This initializes a web browser texture with the given AwWebView class. - */ -WebBrowserTexture::WebBrowserTexture(const std::string &name, AwWebView* aw_web_view): -Texture(name), -_update_active(true), -_flip_texture_active(false) -{ - set_web_view(aw_web_view); - set_minfilter(FT_linear); - set_magfilter(FT_linear); - -} - -/** - * Standard destructor... doesn't do anything. All destructing happens in - * parent texture class. - */ -WebBrowserTexture::~WebBrowserTexture() -{ - // do nothing -} - - -/** - * Standard destructor... doesn't do anything. All destructing happens in - * parent texture class. - */ -bool WebBrowserTexture::get_keep_ram_image() const { - return true; -} - -/** - * A WebBrowserTexture must always keep its ram image. This is essentially a - * sub. - */ -void WebBrowserTexture::do_reload_ram_image() { - // A MovieTexture should never dump its RAM image. Therefore, this is not - // needed. -} - -/** - * Should be overridden by derived classes to return true if cull_callback() - * has been defined. Otherwise, returns false to indicate cull_callback() - * does not need to be called for this node during the cull traversal. - * - * This one returns true because it uses the cull traverser method to do the - * texture udpate. - */ -bool WebBrowserTexture::has_cull_callback() const { - return true; -} - -/** - * Sets the internal AwWebView of this texture. After calling this, the - * texture will automatically set it's width and height to match the AwWebView - * at the next time it is culled and rendered. - */ -void WebBrowserTexture::set_web_view(AwWebView* aw_web_view){ - _aw_web_view = aw_web_view; -} - - -/** - * Gets the current internal AwWebView of this texture. - */ -AwWebView* WebBrowserTexture::get_web_view() const{ - return _aw_web_view; -} - - -/** - * Gives the ability to toggle updating this texture or not. This can be - * disabled to improve performance so that only the one that needs to be - * active is active. - */ -void WebBrowserTexture::set_update_active(bool active_flag){ - _update_active = active_flag; -} - - -/** - * Gets whether or not this texture is updating itself every time it is - * rendered. - */ -bool WebBrowserTexture::get_update_active() const{ - return _update_active; -} - - -/** - * This toggles on/off automatic flipping of the of the texture at a source - * level. Awesomium renders things that are flipped vertically. This enables - * automatic flipping of that. - * - * Since it is doing byte manipulation, this can get rather slow. Turning - * this on should be avoided. Instead, flipping should be taken care of via - * UV coordinates or shaders. - */ -void WebBrowserTexture::set_flip_texture_active(bool active_flag){ - _flip_texture_active = active_flag; -} - -/** - * Returns whether automatic texture flipping is enabled. - */ -bool WebBrowserTexture::get_flip_texture_active() const { - return _flip_texture_active; -} - - -/** - * This function will be called during the cull traversal to update the - * WebBrowserTexture. This method calls the render method of AwWebView but - * does not call the update method of AwWebCore. - */ -bool WebBrowserTexture::cull_callback(CullTraverser *trav, const CullTraverserData &data) const{ - // see if we are in a state where udpates can happen. else just return - if( !_update_active ) return true; - if( _aw_web_view == nullptr ) return true; - - // do we even need to update? - if( !_aw_web_view->is_dirty() ) return true; - - // see if we're the same size, if not we need to make sure this texture - // matches the webview - if( _aw_web_view->get_width() != get_x_size() || _aw_web_view->get_height() != get_y_size() || get_texture_type() != TT_2d_texture){ - // these casts are so dirty especially when the method itself is - // labled as const. Really Texture::cull_callback should be not const - // first clean up - ((WebBrowserTexture*)this)->clear_ram_mipmap_images(); - ((WebBrowserTexture*)this)->clear_ram_image(); - // now set up the texture again - ((WebBrowserTexture*)this)->setup_2d_texture( _aw_web_view->get_width(), _aw_web_view->get_height(), T_unsigned_byte, F_rgba ); - // should be good to go at this point - } - - // get the pointer - PTA_uchar ram_image = ((WebBrowserTexture*)this)->modify_ram_image(); - unsigned char* cp_data = ram_image.p(); - // render it - _aw_web_view->render((void*)cp_data, get_x_size()*4, 4); - - if(_flip_texture_active){ - // flips the texture around... this is super slow. Really this should - // never be enabled. However beginners might find this useful - size_t width = get_x_size(); - size_t height = get_y_size(); - for(size_t i=0; i < height/2; i++){ - for(size_t j=0; j < width; j++){ - unsigned char tmp[4]; - size_t a_pos = j+width*i; - size_t b_pos = j + width*(height-i-1); - memcpy(tmp,&cp_data[4*a_pos], 4); //tmp = a - memcpy(&cp_data[4*a_pos], &cp_data[4*b_pos], 4); //a = b - memcpy(&cp_data[4*b_pos], tmp, 4); //b = tmp - } - } - } - // success - return true; -} diff --git a/panda/src/awesomium/WebBrowserTexture.h b/panda/src/awesomium/WebBrowserTexture.h deleted file mode 100644 index 3c9e6aaa08..0000000000 --- a/panda/src/awesomium/WebBrowserTexture.h +++ /dev/null @@ -1,77 +0,0 @@ -/** - * 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 WebBrowserTexture.h - * @author Bei Yang - * @date 2010-08-03 - */ - -#ifndef WebBrowserTexture_H -#define WebBrowserTexture_H - -#include "pandabase.h" -#include "texture.h" -#include "awWebView.h" - - -/** - * A Wrapper class for Awesomium webview. This implements most of Awesomium's - * features and updates on the cull_traverser callback much much like a movie - * texture. - * - * The use of class means that you will have to follow Awesomium license - * agreement give below http://www.khrona.com/products/awesomium/licensing - */ -class EXPCL_PANDAAWESOMIUM WebBrowserTexture : public Texture { -protected: - AwWebView* _aw_web_view; - bool _update_active; - bool _flip_texture_active; - -// Constructors & Destructors ------------ -private: - WebBrowserTexture(const WebBrowserTexture ©); -PUBLISHED: - WebBrowserTexture(const std::string &name, AwWebView* aw_web_view = nullptr); - virtual ~WebBrowserTexture(); - - -// methods -------------- -protected: - bool get_keep_ram_image() const; - void do_reload_ram_image(); -public: - virtual bool has_cull_callback() const; - virtual bool cull_callback(CullTraverser *trav, const CullTraverserData &data) const; -PUBLISHED: - void set_web_view(AwWebView* aw_web_view); - AwWebView* get_web_view() const; - void set_update_active(bool active_flag); - bool get_update_active() const; - void set_flip_texture_active(bool active_flag); - bool get_flip_texture_active() const; - -// Type handles ---------------- -public: - static TypeHandle get_class_type() { - return _type_handle; - } - static void init_type() { - Texture::init_type(); - register_type(_type_handle, "WebBrowserTexture", - Texture::get_class_type()); - } - virtual TypeHandle get_type() const { - return get_class_type(); - } - virtual TypeHandle force_init_type() {init_type(); return get_class_type();} - -private: - static TypeHandle _type_handle; -}; -#endif diff --git a/panda/src/awesomium/awWebCore.I b/panda/src/awesomium/awWebCore.I deleted file mode 100644 index 7829f6a370..0000000000 --- a/panda/src/awesomium/awWebCore.I +++ /dev/null @@ -1,48 +0,0 @@ -/** - * 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 awWebCore.I - * @author rurbino - * @date 2009-10-12 - */ - -INLINE void AwWebCore:: -setBaseDirectory(const std::string& baseDirectory) { - WebCore::setBaseDirectory(baseDirectory); -} - -INLINE void AwWebCore:: -setCustomResponsePage(int statusCode, const std::string& filePath) { - WebCore::setCustomResponsePage(statusCode, filePath); -} - -INLINE void AwWebCore:: -update() { - WebCore::update(); -} - -INLINE const std::string& AwWebCore:: -getBaseDirectory() const { - return WebCore::getBaseDirectory(); -} - -INLINE bool AwWebCore:: -arePluginsEnabled() const { - return WebCore::arePluginsEnabled(); -} - - -INLINE void AwWebCore:: -pause(){ - WebCore::pause(); -} - -INLINE void AwWebCore:: -resume() { - WebCore::resume(); -} diff --git a/panda/src/awesomium/awWebCore.cxx b/panda/src/awesomium/awWebCore.cxx deleted file mode 100644 index adc5989367..0000000000 --- a/panda/src/awesomium/awWebCore.cxx +++ /dev/null @@ -1,56 +0,0 @@ -/** - * 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 awWebCore.cxx - * @author rurbino - * @date 2009-10-12 - */ - -#include "config_awesomium.h" -#include "awWebCore.h" - -#include - -TypeHandle AwWebCore::_type_handle; - -AwWebCore:: -AwWebCore(AwWebCore::LogLevel level, bool enablePlugins , AwWebCore::PixelFormat pixelFormat) -#ifndef CPPPARSER -: - WebCore(static_cast(level), enablePlugins, static_cast(pixelFormat)) -#endif - { - awesomium_cat.info() << "constructing webcore\n"; -} - -AwWebCore:: -~AwWebCore() { - awesomium_cat.info() << "destructor webcore\n"; -} - -Awesomium::WebCore& AwWebCore:: -Get() { - return WebCore::Get(); -} - -Awesomium::WebCore* AwWebCore:: -GetPointer() { - return WebCore::GetPointer(); -} - -AwWebView * AwWebCore:: -createWebView(int width, int height, bool isTransparent , bool enableAsyncRendering , int maxAsyncRenderPerSec ) { - Awesomium::WebView * newView = WebCore::createWebView(width, height, isTransparent, enableAsyncRendering, maxAsyncRenderPerSec); - AwWebView * result = new AwWebView(newView); - return result; -} - -AwWebCore::PixelFormat AwWebCore:: -getPixelFormat() const { - return ( static_cast( WebCore::getPixelFormat()) ); -} diff --git a/panda/src/awesomium/awWebCore.h b/panda/src/awesomium/awWebCore.h deleted file mode 100644 index db3160bf21..0000000000 --- a/panda/src/awesomium/awWebCore.h +++ /dev/null @@ -1,94 +0,0 @@ -/** - * 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 awWebCore.h - * @author rurbino - * @date 2009-10-12 - */ - -#ifndef AWWEBCORE_H -#define AWWEBCORE_H - -#include "pandabase.h" -#include "typedReferenceCount.h" -#include "luse.h" - -#include "awesomium_includes.h" - -class AwWebView; -/** - * Thin wrappings arround WebCore.h - */ -class EXPCL_PANDAAWESOMIUM AwWebCore : public TypedReferenceCount, public Awesomium::WebCore { -PUBLISHED: - /** - * An enumeration of the three verbosity settings for the Awesomium Log. - */ - enum LogLevel - { - LOG_NONE , // No log is created - LOG_NORMAL , // Logs only errors - LOG_VERBOSE // Logs everything - }; - - /** - * An enumeration of the two output pixel formats that WebView::render will use. - */ - enum PixelFormat - { - PF_BGRA, // BGRA byte ordering [Blue, Green, Red, Alpha] - PF_RGBA // RGBA byte ordering [Red, Green, Blue, Alpha] - }; - - AwWebCore(LogLevel level = LOG_NORMAL, bool enablePlugins = true, PixelFormat pixelFormat = PF_BGRA); - - virtual ~AwWebCore(); - - static Awesomium::WebCore& Get(); - - static Awesomium::WebCore* GetPointer(); - - INLINE void setBaseDirectory(const std::string& baseDirectory); - - AwWebView * createWebView(int width, int height, bool isTransparent = false, bool enableAsyncRendering = false, int maxAsyncRenderPerSec = 70); - - INLINE void setCustomResponsePage(int statusCode, const std::string& filePath); - - INLINE void update(); - - INLINE const std::string& getBaseDirectory() const; - - AwWebCore::PixelFormat getPixelFormat() const; - - INLINE bool arePluginsEnabled() const; - - INLINE void pause(); - - INLINE void resume(); - -public: - static TypeHandle get_class_type() { - return _type_handle; - } - static void init_type() { - TypedReferenceCount::init_type(); - register_type(_type_handle, "AwWebCore", - TypedReferenceCount::get_class_type()); - } - virtual TypeHandle get_type() const { - return get_class_type(); - } - virtual TypeHandle force_init_type() {init_type(); return get_class_type();} - -private: - static TypeHandle _type_handle; -}; - -#include "awWebCore.I" - -#endif diff --git a/panda/src/awesomium/awWebView.I b/panda/src/awesomium/awWebView.I deleted file mode 100644 index c7ecf913e7..0000000000 --- a/panda/src/awesomium/awWebView.I +++ /dev/null @@ -1,82 +0,0 @@ -/** - * 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 awWebView.I - * @author rurbino - * @date 2009-10-12 - */ - -INLINE void AwWebView:: -destroy(void) -{ - _myWebView->destroy(); -} - - -INLINE void AwWebView:: -setListener(Awesomium::WebViewListener * listener) { - _myWebView->setListener(listener); -} - -INLINE Awesomium::WebViewListener* AwWebView:: -getListener() { - return _myWebView->getListener(); -} - -INLINE void AwWebView:: -goToHistoryOffset(int offset) { - _myWebView->goToHistoryOffset(offset); -} - -INLINE void AwWebView:: -executeJavascript2(const std::string& javascript, const std::string& frameName ) { - _myWebView->executeJavascript2(javascript, frameName); -} - -INLINE Awesomium::FutureJSValue AwWebView:: -executeJavascriptWithResult2(const std::string& javascript, const std::string& frameName ) { - return _myWebView->executeJavascriptWithResult2(javascript, frameName); -} - -INLINE void AwWebView:: -setProperty(const std::string& name, const Awesomium::JSValue& value) { - _myWebView->setProperty(name, value); -} - -INLINE void AwWebView:: -setCallback(const std::string& name) { - _myWebView->setCallback(name); -} - -INLINE bool AwWebView:: -isDirty() { - return _myWebView->isDirty(); -} - -INLINE void AwWebView:: -render( size_t destination, int destRowSpan, int destDepth) -{ - _myWebView->render( reinterpret_cast(destination), destRowSpan, destDepth, 0); -} - - - -INLINE void AwWebView:: -injectMouseUp(AwWebView::MouseButton button) { - _myWebView->injectMouseUp(static_cast(button)); -} - -INLINE void AwWebView:: -injectMouseWheelXY(int scrollAmountX, int scrollAmountY){ - _myWebView->injectMouseWheelXY(scrollAmountX, scrollAmountY); -} - -INLINE void AwWebView:: -injectKeyEvent(bool press, int modifiers, int windowsCode, int nativeCode) { - _myWebView->injectKeyEvent(press, modifiers, windowsCode, nativeCode); -} diff --git a/panda/src/awesomium/awWebView.cxx b/panda/src/awesomium/awWebView.cxx deleted file mode 100644 index ebc3a63f89..0000000000 --- a/panda/src/awesomium/awWebView.cxx +++ /dev/null @@ -1,74 +0,0 @@ -/** - * 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 awWebView.cxx - * @author rurbino - * @date 2009-10-12 - */ - -#include "config_awesomium.h" -#include "awWebView.h" - -TypeHandle AwWebView::_type_handle; - -AwWebView:: -AwWebView(Awesomium::WebView * webViewPtr) { - _myWebView = webViewPtr; - -} - -AwWebView:: -~AwWebView() { -} - - -void AwWebView:: -loadURL2(const std::string& url, const std::string& frameName , const std::string& username , const std::string& password ) -{ - _myWebView->loadURL2(url, frameName, username, password); - -} - -void AwWebView:: -loadHTML2(const std::string& html, const std::string& frameName ) -{ - _myWebView->loadHTML2(html, frameName); -} - - -void AwWebView:: -loadFile2(const std::string& file, const std::string& frameName ) -{ - _myWebView->loadFile2(file, frameName); -} - - -void AwWebView:: -render(size_t destination, int destRowSpan, int destDepth, AwWebView::Rect * renderedRect) { - if (renderedRect) { - Awesomium::Rect rect(renderedRect->x, renderedRect->y, renderedRect->width, renderedRect->height); - _myWebView->Awesomium::WebView::render( reinterpret_cast(destination), destRowSpan, destDepth, &rect); - } - else - { - AwWebView::render(destination, destRowSpan, destDepth, 0); - } -} - -void AwWebView:: -injectMouseDown(AwWebView::MouseButton button) { - awesomium_cat.debug() <<"got mouse down " << button << "\n"; - _myWebView->injectMouseDown(static_cast(button)); -} - - -void AwWebView:: -injectMouseMove(int x, int y) { - // awesomium_cat.debug() <<"got mouse move " << x << " " << y << "\n"; - _myWebView->injectMouseMove(x,y); -} diff --git a/panda/src/awesomium/awWebView.h b/panda/src/awesomium/awWebView.h deleted file mode 100644 index ad5c9d223c..0000000000 --- a/panda/src/awesomium/awWebView.h +++ /dev/null @@ -1,131 +0,0 @@ -/** - * 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 awWebView.h - * @author rurbino - * @date 2009-10-12 - */ - -#ifndef AWWEBVIEW_H -#define AWWEBVIEW_H - -#include "pandabase.h" -#include "typedReferenceCount.h" -#include "luse.h" - -#include "awesomium_includes.h" - -class WebViewListener; - -/** - * Thin bindings, wraps a WebView * returned from WebCore.createWebView - */ -class EXPCL_PANDAAWESOMIUM AwWebView : public TypedReferenceCount{ -PUBLISHED: - - /** - * Mouse button enumerations, used with WebView::injectMouseDown - * and WebView::injectMouseUp - */ - enum MouseButton { - LEFT_MOUSE_BTN, - MIDDLE_MOUSE_BTN, - RIGHT_MOUSE_BTN - }; - - /** - * A simple rectangle class, used with WebView::render - */ - struct Rect { - int x, y, width, height; - - Rect(); - Rect(int x, int y, int width, int height); - bool isEmpty() const; - }; - - -PUBLISHED: - AwWebView(Awesomium::WebView * webView); - - virtual ~AwWebView(); - - INLINE void destroy(void); - - INLINE void setListener(Awesomium::WebViewListener * listener); - - INLINE Awesomium::WebViewListener* getListener(); - - // VC7 linker doesn't like wstring from VS2008, hence using the all regular - // string version - void loadURL2(const std::string& url, const std::string& frameName ="", const std::string& username="" , const std::string& password=""); - - // VC7 linker doesn't like wstring from VS2008, hence using the all regular - // string version - void loadHTML2(const std::string& html, const std::string& frameName = ""); - - // VC7 linker doesn't like wstring from VS2008, hence using the all regular - // string version - void loadFile2(const std::string& file, const std::string& frameName = "" ); - - INLINE void goToHistoryOffset(int offset); - - // VC7 linker doesn't like wstring from VS2008, hence using the all regular - // string version - INLINE void executeJavascript2(const std::string& javascript, const std::string& frameName = "" ); - - INLINE Awesomium::FutureJSValue executeJavascriptWithResult2(const std::string& javascript, const std::string& frameName = ""); - - INLINE void setProperty(const std::string& name, const Awesomium::JSValue& value); - - INLINE void setCallback(const std::string& name); - - INLINE bool isDirty(); - - INLINE void render(size_t destination, int destRowSpan, int destDepth); - - void render(size_t destination, int destRowSpan, int destDepth, AwWebView::Rect * renderedRect); - - void injectMouseMove(int x, int y); - - void injectMouseDown(AwWebView::MouseButton button); - - INLINE void injectMouseUp(AwWebView::MouseButton button); - - INLINE void injectMouseWheelXY(int scrollAmountX, int scrollAmountY); - - INLINE void injectMouseWheel(int scrollAmountY) { - injectMouseWheelXY(0, scrollAmountY); - } - - INLINE void injectKeyEvent(bool press, int modifiers, int windowsCode, int nativeCode=0); - -private: - Awesomium::WebView * _myWebView; - -public: - static TypeHandle get_class_type() { - return _type_handle; - } - static void init_type() { - TypedReferenceCount::init_type(); - register_type(_type_handle, "AwWebView", - TypedReferenceCount::get_class_type()); - } - virtual TypeHandle get_type() const { - return get_class_type(); - } - virtual TypeHandle force_init_type() {init_type(); return get_class_type();} - -private: - static TypeHandle _type_handle; -}; - -#include "awWebView.I" - -#endif diff --git a/panda/src/awesomium/awWebViewListener.cxx b/panda/src/awesomium/awWebViewListener.cxx deleted file mode 100644 index 0af38b5da8..0000000000 --- a/panda/src/awesomium/awWebViewListener.cxx +++ /dev/null @@ -1,80 +0,0 @@ -/** - * 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 awWebViewListener.cxx - * @author rurbino - * @date 2009-10-12 - */ - -#include "config_awesomium.h" -#include "awWebViewListener.h" - -TypeHandle AwWebViewListener::_type_handle; - -AwWebViewListener:: -AwWebViewListener() { - awesomium_cat.info() << "constructing WebViewListner" ; -} - -void AwWebViewListener::onBeginNavigation(const std::string& url, const std::wstring& frameName){ -} - -void AwWebViewListener::onBeginLoading(const std::string& url, const std::wstring& frameName, int statusCode, const std::wstring& mimeType) { - awesomium_cat.info() << "onBeginLoading" ; -} - - /** - * This event is fired when all loads have finished for a WebView. - */ -void AwWebViewListener::onFinishLoading() { -} - - /** - * This event is fired when a Client callback has been invoked via Javascript from a page. - * - * @param name The name of the client callback that was invoked (specifically, "Client._this_name_here_(...)"). - * - * @param args The arguments passed to the callback. - */ -void AwWebViewListener::onCallback(const std::string& name, const Awesomium::JSArguments& args) { -} - - /** - * This event is fired when a page title is received. - * - * @param title The page title. - * - * @param frameName The name of the frame that this event originated from. - */ -void AwWebViewListener::onReceiveTitle(const std::wstring& title, const std::wstring& frameName) { -} - - /** - * This event is fired when a tooltip has changed state. - * - * @param tooltip The tooltip text (or, is an empty string when the tooltip should disappear). - */ -void AwWebViewListener::onChangeTooltip(const std::wstring& tooltip) { -} - - /** - * This event is fired when keyboard focus has changed. - * - * @param isFocused Whether or not the keyboard is currently focused. - */ -void AwWebViewListener::onChangeKeyboardFocus(bool isFocused) { -} - - /** - * This event is fired when the target URL has changed. This is usually the result of - * hovering over a link on the page. - * - * @param url The updated target URL (or empty if the target URL is cleared). - */ -void AwWebViewListener::onChangeTargetURL(const std::string& url) { -} diff --git a/panda/src/awesomium/awWebViewListener.h b/panda/src/awesomium/awWebViewListener.h deleted file mode 100644 index 924754762d..0000000000 --- a/panda/src/awesomium/awWebViewListener.h +++ /dev/null @@ -1,123 +0,0 @@ -/** - * 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 awWebViewListener.h - * @author rurbino - * @date 2009-10-12 - */ - -#ifndef AWWEBVIEWLISTENER_H -#define AWWEBVIEWLISTENER_H - -#include "pandabase.h" -#include "typedReferenceCount.h" -#include "luse.h" - -#include "awesomium_includes.h" - -/** - * Thin bindings, wraps a WebViewListener - */ -class EXPCL_PANDAAWESOMIUM AwWebViewListener : public TypedReferenceCount, public Awesomium::WebCore { -PUBLISHED: - - -PUBLISHED: - AwWebViewListener(); - - virtual ~AwWebViewListener() {} - - /** - * This event is fired when a WebView begins navigating to a new URL. - * - * @param url The URL that is being navigated to. - * - * @param frameName The name of the frame that this event originated from. - */ - void onBeginNavigation(const std::string& url, const std::wstring& frameName) ; - - /** - * This event is fired when a WebView begins to actually receive data from a server. - * - * @param url The URL of the frame that is being loaded. - * - * @param frameName The name of the frame that this event originated from. - * - * @param statusCode The HTTP status code returned by the server. - * - * @param mimeType The mime-type of the content that is being loaded. - */ - void onBeginLoading(const std::string& url, const std::wstring& frameName, int statusCode, const std::wstring& mimeType); - - /** - * This event is fired when all loads have finished for a WebView. - */ - void onFinishLoading(); - - /** - * This event is fired when a Client callback has been invoked via Javascript from a page. - * - * @param name The name of the client callback that was invoked (specifically, "Client._this_name_here_(...)"). - * - * @param args The arguments passed to the callback. - */ - void onCallback(const std::string& name, const Awesomium::JSArguments& args); - - /** - * This event is fired when a page title is received. - * - * @param title The page title. - * - * @param frameName The name of the frame that this event originated from. - */ - void onReceiveTitle(const std::wstring& title, const std::wstring& frameName) ; - - /** - * This event is fired when a tooltip has changed state. - * - * @param tooltip The tooltip text (or, is an empty string when the tooltip should disappear). - */ - void onChangeTooltip(const std::wstring& tooltip); - - /** - * This event is fired when keyboard focus has changed. - * - * @param isFocused Whether or not the keyboard is currently focused. - */ - void onChangeKeyboardFocus(bool isFocused) ; - - /** - * This event is fired when the target URL has changed. This is usually the result of - * hovering over a link on the page. - * - * @param url The updated target URL (or empty if the target URL is cleared). - */ - void onChangeTargetURL(const std::string& url) ; - - -public: - static TypeHandle get_class_type() { - return _type_handle; - } - static void init_type() { - TypedReferenceCount::init_type(); - register_type(_type_handle, "AwWebViewListener", - TypedReferenceCount::get_class_type()); - } - virtual TypeHandle get_type() const { - return get_class_type(); - } - virtual TypeHandle force_init_type() {init_type(); return get_class_type();} - -private: - static TypeHandle _type_handle; -}; - -// #include "awWebViewListener.I" - -#endif diff --git a/panda/src/awesomium/awesomium_includes.h b/panda/src/awesomium/awesomium_includes.h deleted file mode 100644 index 27b8b9b9b0..0000000000 --- a/panda/src/awesomium/awesomium_includes.h +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 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 awesomium_includes.h - * @author rurbino - * @date 2010-10-08 - */ - -#ifndef _AWESOMIUM_INCLUDES_H_ -#define _AWESOMIUM_INCLUDES_H_ - -#include -#include -#include - -#endif diff --git a/panda/src/awesomium/config_awesomium.cxx b/panda/src/awesomium/config_awesomium.cxx deleted file mode 100644 index 35987d0e27..0000000000 --- a/panda/src/awesomium/config_awesomium.cxx +++ /dev/null @@ -1,51 +0,0 @@ -/** - * 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 config_awesomium.cxx - * @author rurbino - * @date 2009-10-12 - */ - -#include "config_awesomium.h" -#include "awWebCore.h" -#include "awWebView.h" -#include "awWebViewListener.h" -#include "dconfig.h" - - -#if !defined(CPPPARSER) && !defined(LINK_ALL_STATIC) && !defined(BUILDING_PANDAAWESOMIUM) - #error Buildsystem error: BUILDING_PANDAAWESOMIUM not defined -#endif - -Configure(config_awesomium); -NotifyCategoryDef(awesomium, ""); - - -ConfigureFn(config_awesomium) { - init_libawesomium(); -} - -/** - * Initializes the library. This must be called at least once before any of - * the functions or classes in this library can be used. Normally it will be - * called by the static initializers and need not be called explicitly, but - * special cases exist. - */ -void -init_libawesomium() { - static bool initialized = false; - if (initialized) { - return; - } - initialized = true; - - AwWebCore::init_type(); - AwWebView::init_type(); - AwWebViewListener::init_type(); - -} diff --git a/panda/src/awesomium/config_awesomium.h b/panda/src/awesomium/config_awesomium.h deleted file mode 100644 index e02f047e16..0000000000 --- a/panda/src/awesomium/config_awesomium.h +++ /dev/null @@ -1,26 +0,0 @@ -/** - * 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 config_awesomium.h - * @author rurbino - * @date 2009-10-12 - */ - -#ifndef CONFIG_AWESOMIUM_H -#define CONFIG_AWESOMIUM_H - -#include "pandabase.h" -#include "notifyCategoryProxy.h" - -#include "dconfig.h" - -NotifyCategoryDecl(awesomium, EXPCL_PANDAAWESOMIUM, EXPTP_PANDAAWESOMIUM); - -extern EXPCL_PANDAAWESOMIUM void init_libawesomium(); - -#endif /* CONFIG_AWESOMIUM_H */ diff --git a/panda/src/awesomium/pandaawesomium_composite1.cxx b/panda/src/awesomium/pandaawesomium_composite1.cxx deleted file mode 100644 index 1b0ef139ec..0000000000 --- a/panda/src/awesomium/pandaawesomium_composite1.cxx +++ /dev/null @@ -1,4 +0,0 @@ -#include "config_awesomium.cxx" -#include "awWebCore.cxx" -#include "awWebView.cxx" -#include "awWebViewListener.cxx" diff --git a/panda/src/bullet/bulletBodyNode.cxx b/panda/src/bullet/bulletBodyNode.cxx index 81a056adc2..cb9d7ed9c0 100644 --- a/panda/src/bullet/bulletBodyNode.cxx +++ b/panda/src/bullet/bulletBodyNode.cxx @@ -28,7 +28,7 @@ #include "collisionPlane.h" #include "collisionSphere.h" #include "collisionPolygon.h" -#include "collisionTube.h" +#include "collisionCapsule.h" TypeHandle BulletBodyNode::_type_handle; @@ -813,12 +813,12 @@ add_shapes_from_collision_solids(CollisionNode *cnode) { do_add_shape(BulletBoxShape::make_from_solid(box), ts); } - // CollisionTube - else if (CollisionTube::get_class_type() == type) { - CPT(CollisionTube) tube = DCAST(CollisionTube, solid); - CPT(TransformState) ts = TransformState::make_pos((tube->get_point_b() + tube->get_point_a()) / 2.0); + // CollisionCapsule + else if (CollisionCapsule::get_class_type() == type) { + CPT(CollisionCapsule) capsule = DCAST(CollisionCapsule, solid); + CPT(TransformState) ts = TransformState::make_pos((capsule->get_point_b() + capsule->get_point_a()) / 2.0); - do_add_shape(BulletCapsuleShape::make_from_solid(tube), ts); + do_add_shape(BulletCapsuleShape::make_from_solid(capsule), ts); } // CollisionPlane diff --git a/panda/src/bullet/bulletCapsuleShape.cxx b/panda/src/bullet/bulletCapsuleShape.cxx index 8ff2b3e37a..cde5e64050 100644 --- a/panda/src/bullet/bulletCapsuleShape.cxx +++ b/panda/src/bullet/bulletCapsuleShape.cxx @@ -87,16 +87,16 @@ ptr() const { /** * Constructs a new BulletCapsuleShape using the information from a - * CollisionTube from the builtin collision system. + * CollisionCapsule from the builtin collision system. */ BulletCapsuleShape *BulletCapsuleShape:: -make_from_solid(const CollisionTube *solid) { - - PN_stdfloat radius = solid->get_radius(); - // CollisionTube height includes the hemispheres, Bullet only wants the cylinder height. - PN_stdfloat height = (solid->get_point_b() - solid->get_point_a()).length() - (radius * 2); +make_from_solid(const CollisionCapsule *solid) { - // CollisionTubes are always Z-Up. + PN_stdfloat radius = solid->get_radius(); + // Get capsule's cylinder height: length from point A to point B + PN_stdfloat height = (solid->get_point_b() - solid->get_point_a()).length(); + + // CollisionCapsules are always Z-Up. return new BulletCapsuleShape(radius, height, Z_up); } diff --git a/panda/src/bullet/bulletCapsuleShape.h b/panda/src/bullet/bulletCapsuleShape.h index f8eecfe3d7..e78b06bc08 100644 --- a/panda/src/bullet/bulletCapsuleShape.h +++ b/panda/src/bullet/bulletCapsuleShape.h @@ -20,7 +20,7 @@ #include "bullet_utils.h" #include "bulletShape.h" -#include "collisionTube.h" +#include "collisionCapsule.h" /** * @@ -35,7 +35,7 @@ PUBLISHED: BulletCapsuleShape(const BulletCapsuleShape ©); INLINE ~BulletCapsuleShape(); - static BulletCapsuleShape *make_from_solid(const CollisionTube *solid); + static BulletCapsuleShape *make_from_solid(const CollisionCapsule *solid); INLINE PN_stdfloat get_radius() const; INLINE PN_stdfloat get_half_height() const; diff --git a/panda/src/bullet/bulletVehicle.cxx b/panda/src/bullet/bulletVehicle.cxx index f4321cd911..ba7ee35d6d 100644 --- a/panda/src/bullet/bulletVehicle.cxx +++ b/panda/src/bullet/bulletVehicle.cxx @@ -235,10 +235,13 @@ void BulletVehicle:: do_sync_b2p() { for (int i=0; i < _vehicle->getNumWheels(); i++) { - // synchronize the wheels with the (interpolated) chassis worldtransform - _vehicle->updateWheelTransform(i, true); + btWheelInfo &info = _vehicle->getWheelInfo(i); - btWheelInfo info = _vehicle->getWheelInfo(i); + // synchronize the wheels with the (interpolated) chassis worldtransform. + // It resets the m_isInContact flag, so restore that afterwards. + bool in_contact = info.m_raycastInfo.m_isInContact; + _vehicle->updateWheelTransform(i, true); + info.m_raycastInfo.m_isInContact = in_contact; PandaNode *node = (PandaNode *)info.m_clientInfo; if (node) { diff --git a/panda/src/cocoadisplay/cocoaGraphicsStateGuardian.h b/panda/src/cocoadisplay/cocoaGraphicsStateGuardian.h index 2e83e60c34..c8820a73d3 100644 --- a/panda/src/cocoadisplay/cocoaGraphicsStateGuardian.h +++ b/panda/src/cocoadisplay/cocoaGraphicsStateGuardian.h @@ -20,6 +20,7 @@ #import #import +#import /** * A tiny specialization on GLGraphicsStateGuardian to add some Cocoa-specific @@ -38,14 +39,21 @@ public: CocoaGraphicsStateGuardian *share_with); virtual ~CocoaGraphicsStateGuardian(); + bool setup_vsync(); INLINE void lock_context(); INLINE void unlock_context(); NSOpenGLContext *_share_context; NSOpenGLContext *_context; + NSOpenGLPixelFormat *_format = nullptr; FrameBufferProperties _fbprops; + CVDisplayLinkRef _display_link = nullptr; + TrueMutexImpl _swap_lock; + TrueConditionVarImpl _swap_condition; + AtomicAdjust::Integer _last_wait_frame = 0; + protected: virtual void query_gl_version(); virtual void *do_get_extension_func(const char *name); diff --git a/panda/src/cocoadisplay/cocoaGraphicsStateGuardian.mm b/panda/src/cocoadisplay/cocoaGraphicsStateGuardian.mm index d0dbd37c16..aac8beb033 100644 --- a/panda/src/cocoadisplay/cocoaGraphicsStateGuardian.mm +++ b/panda/src/cocoadisplay/cocoaGraphicsStateGuardian.mm @@ -28,6 +28,21 @@ #define NSAppKitVersionNumber10_7 1138 #endif +/** + * Called whenever a display wants a frame. The context argument contains the + * applicable CocoaGraphicsStateGuardian. + */ +static CVReturn +display_link_cb(CVDisplayLinkRef link, const CVTimeStamp *now, + const CVTimeStamp* output_time, CVOptionFlags flags_in, + CVOptionFlags *flags_out, void *context) { + CocoaGraphicsStateGuardian *gsg = (CocoaGraphicsStateGuardian *)context; + gsg->_swap_lock.lock(); + gsg->_swap_condition.notify(); + gsg->_swap_lock.unlock(); + return kCVReturnSuccess; +} + TypeHandle CocoaGraphicsStateGuardian::_type_handle; /** @@ -36,7 +51,8 @@ TypeHandle CocoaGraphicsStateGuardian::_type_handle; CocoaGraphicsStateGuardian:: CocoaGraphicsStateGuardian(GraphicsEngine *engine, GraphicsPipe *pipe, CocoaGraphicsStateGuardian *share_with) : - GLGraphicsStateGuardian(engine, pipe) + GLGraphicsStateGuardian(engine, pipe), + _swap_condition(_swap_lock) { _share_context = nil; _context = nil; @@ -52,12 +68,60 @@ CocoaGraphicsStateGuardian(GraphicsEngine *engine, GraphicsPipe *pipe, */ CocoaGraphicsStateGuardian:: ~CocoaGraphicsStateGuardian() { + if (_format != nil) { + [_format release]; + } + if (_display_link != nil) { + CVDisplayLinkRelease(_display_link); + _display_link = nil; + _swap_lock.lock(); + _swap_condition.notify(); + _swap_lock.unlock(); + } if (_context != nil) { [_context clearDrawable]; [_context release]; } } +/** + * Creates a CVDisplayLink, which tells us when the display the window is on + * will want a frame. + */ +bool CocoaGraphicsStateGuardian:: +setup_vsync() { + if (_display_link != nil) { + // Already set up. + return true; + } + + CVReturn result = CVDisplayLinkCreateWithActiveCGDisplays(&_display_link); + if (result != kCVReturnSuccess) { + cocoadisplay_cat.error() << "Failed to create CVDisplayLink.\n"; + return false; + } + + result = CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(_display_link, (CGLContextObj)[_context CGLContextObj], (CGLPixelFormatObj)[_format CGLPixelFormatObj]); + if (result != kCVReturnSuccess) { + cocoadisplay_cat.error() << "Failed to set CVDisplayLink's current display.\n"; + return false; + } + + result = CVDisplayLinkSetOutputCallback(_display_link, &display_link_cb, this); + if (result != kCVReturnSuccess) { + cocoadisplay_cat.error() << "Failed to set CVDisplayLink output callback.\n"; + return false; + } + + result = CVDisplayLinkStart(_display_link); + if (result != kCVReturnSuccess) { + cocoadisplay_cat.error() << "Failed to start the CVDisplayLink.\n"; + return false; + } + + return true; +} + /** * Gets the FrameBufferProperties to match the indicated config. */ @@ -262,15 +326,15 @@ choose_pixel_format(const FrameBufferProperties &properties, // TODO: print out renderer _context = [[NSOpenGLContext alloc] initWithFormat:format shareContext:_share_context]; - [format release]; + _format = format; if (_context == nil) { cocoadisplay_cat.error() << "Failed to create OpenGL context!\n"; return; } - // Set vsync setting on the context - GLint swap = sync_video ? 1 : 0; + // Disable vsync via the built-in mechanism, which doesn't work on Mojave + GLint swap = 0; [_context setValues:&swap forParameter:NSOpenGLCPSwapInterval]; cocoadisplay_cat.debug() diff --git a/panda/src/cocoadisplay/cocoaGraphicsWindow.h b/panda/src/cocoadisplay/cocoaGraphicsWindow.h index 353212f056..a053cb11b9 100644 --- a/panda/src/cocoadisplay/cocoaGraphicsWindow.h +++ b/panda/src/cocoadisplay/cocoaGraphicsWindow.h @@ -23,6 +23,8 @@ #import #import +#import + /** * An interface to the Cocoa system for managing OpenGL windows under Mac OS * X. @@ -89,8 +91,10 @@ private: NSUInteger _modifier_keys; UInt32 _dead_key_state; CGDirectDisplayID _display; + PT(GraphicsWindowInputDevice) _input; bool _mouse_hidden; bool _context_needs_update; + bool _vsync_enabled = false; #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 CGDisplayModeRef _fullscreen_mode; diff --git a/panda/src/cocoadisplay/cocoaGraphicsWindow.mm b/panda/src/cocoadisplay/cocoaGraphicsWindow.mm index 7d461817f1..c3f23d4013 100644 --- a/panda/src/cocoadisplay/cocoaGraphicsWindow.mm +++ b/panda/src/cocoadisplay/cocoaGraphicsWindow.mm @@ -99,9 +99,10 @@ CocoaGraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe, [NSApp finishLaunching]; } - GraphicsWindowInputDevice device = + PT(GraphicsWindowInputDevice) device = GraphicsWindowInputDevice::pointer_and_keyboard(this, "keyboard_mouse"); - add_input_device(device); + _input_devices.push_back(device.p()); + _input = std::move(device); CocoaGraphicsPipe *cocoa_pipe; DCAST_INTO_V(cocoa_pipe, _pipe); @@ -273,6 +274,15 @@ end_flip() { CocoaGraphicsStateGuardian *cocoagsg; DCAST_INTO_V(cocoagsg, _gsg); + if (_vsync_enabled) { + AtomicAdjust::Integer cur_frame = ClockObject::get_global_clock()->get_frame_count(); + if (AtomicAdjust::set(cocoagsg->_last_wait_frame, cur_frame) != cur_frame) { + cocoagsg->_swap_lock.lock(); + cocoagsg->_swap_condition.wait(); + cocoagsg->_swap_lock.unlock(); + } + } + cocoagsg->lock_context(); // Swap the front and back buffer. @@ -668,6 +678,8 @@ open_window() { mouse_mode_relative(); } + _vsync_enabled = sync_video && cocoagsg->setup_vsync(); + return true; } @@ -709,6 +721,8 @@ close_window() { _view = nil; } + _vsync_enabled = false; + GraphicsWindow::close_window(); } @@ -981,7 +995,7 @@ set_properties_now(WindowProperties &properties) { if (properties.has_cursor_hidden()) { if (properties.get_cursor_hidden() != _properties.get_cursor_hidden()) { - if (properties.get_cursor_hidden() && _input_devices[0].get_pointer().get_in_window()) { + if (properties.get_cursor_hidden() && _input->get_pointer().get_in_window()) { [NSCursor hide]; _mouse_hidden = true; } else if (_mouse_hidden) { @@ -1490,6 +1504,7 @@ handle_close_event() { cocoagsg->unlock_context(); } _gsg.clear(); + _vsync_enabled = false; } // Dump the view, too @@ -1558,7 +1573,7 @@ handle_key_event(NSEvent *event) { // flaws are: - OS eats unmodified F11, F12, scroll lock, pause - no up // events for caps lock - no robust way to distinguish updown for modkeys if ([event type] == NSKeyUp) { - _input_devices[0].raw_button_up(raw_button); + _input->raw_button_up(raw_button); } else if ([event type] == NSFlagsChanged) { bool down = false; @@ -1580,15 +1595,15 @@ handle_key_event(NSEvent *event) { down = (modifierFlags & 0x0010); } else if (raw_button == KeyboardButton::caps_lock()) { // Emulate down-up, annoying hack! - _input_devices[0].raw_button_down(raw_button); + _input->raw_button_down(raw_button); } if (down) { - _input_devices[0].raw_button_down(raw_button); + _input->raw_button_down(raw_button); } else { - _input_devices[0].raw_button_up(raw_button); + _input->raw_button_up(raw_button); } } else if (![event isARepeat]) { - _input_devices[0].raw_button_down(raw_button); + _input->raw_button_down(raw_button); } } @@ -1622,7 +1637,7 @@ handle_key_event(NSEvent *event) { } cocoadisplay_cat.spam(false) << "\n"; } - _input_devices[0].keystroke(c); + _input->keystroke(c); } } @@ -1634,6 +1649,7 @@ handle_key_event(NSEvent *event) { unichar c = [str characterAtIndex: 0]; ButtonHandle button = map_key(c); + if (button == ButtonHandle::none()) { // That done, continue trying to find out the button handle. if ([str canBeConvertedToEncoding: NSASCIIStringEncoding]) { @@ -1659,9 +1675,9 @@ handle_key_event(NSEvent *event) { // Let's get it off our chest. if ([event type] == NSKeyUp) { - _input_devices[0].button_up(button); + _input->button_up(button); } else { - _input_devices[0].button_down(button); + _input->button_down(button); } } @@ -1672,9 +1688,9 @@ void CocoaGraphicsWindow:: handle_modifier(NSUInteger modifierFlags, NSUInteger mask, ButtonHandle button) { if ((modifierFlags ^ _modifier_keys) & mask) { if (modifierFlags & mask) { - _input_devices[0].button_down(button); + _input->button_down(button); } else { - _input_devices[0].button_up(button); + _input->button_up(button); } } } @@ -1686,14 +1702,14 @@ handle_modifier(NSUInteger modifierFlags, NSUInteger mask, ButtonHandle button) void CocoaGraphicsWindow:: handle_mouse_button_event(int button, bool down) { if (down) { - _input_devices[0].button_down(MouseButton::button(button)); + _input->button_down(MouseButton::button(button)); #ifndef NDEBUG cocoadisplay_cat.spam() << "Mouse button " << button << " down\n"; #endif } else { - _input_devices[0].button_up(MouseButton::button(button)); + _input->button_up(MouseButton::button(button)); #ifndef NDEBUG cocoadisplay_cat.spam() @@ -1712,7 +1728,7 @@ handle_mouse_moved_event(bool in_window, double x, double y, bool absolute) { if (absolute) { if (cocoadisplay_cat.is_spam()) { - if (in_window != _input_devices[0].get_pointer().get_in_window()) { + if (in_window != _input->get_pointer().get_in_window()) { if (in_window) { cocoadisplay_cat.spam() << "Mouse pointer entered window\n"; } else { @@ -1727,7 +1743,7 @@ handle_mouse_moved_event(bool in_window, double x, double y, bool absolute) { } else { // We received deltas, so add it to the current mouse position. - MouseData md = _input_devices[0].get_pointer(); + MouseData md = _input->get_pointer(); nx = md.get_x() + x; ny = md.get_y() + y; } @@ -1753,8 +1769,11 @@ handle_mouse_moved_event(bool in_window, double x, double y, bool absolute) { } } - _input_devices[0].set_pointer(in_window, nx, ny, - ClockObject::get_global_clock()->get_frame_time()); + if (in_window) { + _input->set_pointer_in_window(nx, ny); + } else { + _input->set_pointer_out_of_window(); + } if (in_window != _mouse_hidden && _properties.get_cursor_hidden()) { // Hide the cursor if the mouse enters the window, and unhide it when the @@ -1777,20 +1796,20 @@ handle_wheel_event(double x, double y) { << "Wheel delta " << x << ", " << y << "\n"; if (y > 0.0) { - _input_devices[0].button_down(MouseButton::wheel_up()); - _input_devices[0].button_up(MouseButton::wheel_up()); + _input->button_down(MouseButton::wheel_up()); + _input->button_up(MouseButton::wheel_up()); } else if (y < 0.0) { - _input_devices[0].button_down(MouseButton::wheel_down()); - _input_devices[0].button_up(MouseButton::wheel_down()); + _input->button_down(MouseButton::wheel_down()); + _input->button_up(MouseButton::wheel_down()); } // TODO: check if this is correct, I don't own a MacBook if (x > 0.0) { - _input_devices[0].button_down(MouseButton::wheel_right()); - _input_devices[0].button_up(MouseButton::wheel_right()); + _input->button_down(MouseButton::wheel_right()); + _input->button_up(MouseButton::wheel_right()); } else if (x < 0.0) { - _input_devices[0].button_down(MouseButton::wheel_left()); - _input_devices[0].button_up(MouseButton::wheel_left()); + _input->button_down(MouseButton::wheel_left()); + _input->button_up(MouseButton::wheel_left()); } } diff --git a/panda/src/cocoadisplay/cocoaPandaView.mm b/panda/src/cocoadisplay/cocoaPandaView.mm index c3471ec36b..6a3f48b39a 100644 --- a/panda/src/cocoadisplay/cocoaPandaView.mm +++ b/panda/src/cocoadisplay/cocoaPandaView.mm @@ -26,6 +26,10 @@ [self setCanDrawConcurrently:YES]; #endif + // If a layer ends up becoming attached to the view, tell AppKit we'll manage + // the redrawing since we're doing things our own way. + self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawNever; + cocoadisplay_cat.debug() << "Created CocoaPandaView " << self << " for GraphicsWindow " << window << "\n"; _graphicsWindow = window; @@ -167,4 +171,15 @@ - (BOOL) isOpaque { return YES; } + +-(void)setLayer:(CALayer*)layer +{ + [super setLayer:layer]; + + // Starting in macOS 10.14, a CALayer will still be attached to a view even + // if `wantsLayer` is false. If we don't update the context now, only a + // black screen will be rendered until the context is updated some other + // way (like through a window resize event). + [_context update]; +} @end diff --git a/panda/src/collide/collisionBox.cxx b/panda/src/collide/collisionBox.cxx index a8af257456..8528e7bc27 100644 --- a/panda/src/collide/collisionBox.cxx +++ b/panda/src/collide/collisionBox.cxx @@ -16,7 +16,7 @@ #include "collisionRay.h" #include "collisionSphere.h" #include "collisionSegment.h" -#include "collisionTube.h" +#include "collisionCapsule.h" #include "collisionHandler.h" #include "collisionEntry.h" #include "config_collide.h" @@ -547,19 +547,19 @@ test_intersection_from_segment(const CollisionEntry &entry) const { } /** - * Double dispatch point for tube as a FROM object + * Double dispatch point for capsule as a FROM object */ PT(CollisionEntry) CollisionBox:: -test_intersection_from_tube(const CollisionEntry &entry) const { - const CollisionTube *tube; - DCAST_INTO_R(tube, entry.get_from(), nullptr); +test_intersection_from_capsule(const CollisionEntry &entry) const { + const CollisionCapsule *capsule; + DCAST_INTO_R(capsule, entry.get_from(), nullptr); const LMatrix4 &wrt_mat = entry.get_wrt_mat(); - LPoint3 from_a = tube->get_point_a() * wrt_mat; - LPoint3 from_b = tube->get_point_b() * wrt_mat; + LPoint3 from_a = capsule->get_point_a() * wrt_mat; + LPoint3 from_b = capsule->get_point_b() * wrt_mat; LVector3 from_direction = from_b - from_a; - PN_stdfloat radius_sq = wrt_mat.xform_vec(LVector3(0, 0, tube->get_radius())).length_squared(); + PN_stdfloat radius_sq = wrt_mat.xform_vec(LVector3(0, 0, capsule->get_radius())).length_squared(); PN_stdfloat radius = csqrt(radius_sq); LPoint3 box_min = get_min(); @@ -620,7 +620,7 @@ test_intersection_from_tube(const CollisionEntry &entry) const { LVector3 delta(0); delta[edges[i].axis] = dimensions[edges[i].axis]; double u1, u2; - CollisionTube::calc_closest_segment_points(u1, u2, from_a, from_direction, vertex, delta); + CollisionCapsule::calc_closest_segment_points(u1, u2, from_a, from_direction, vertex, delta); PN_stdfloat dist_sq = ((from_a + from_direction * u1) - (vertex + delta * u2)).length_squared(); if (dist_sq < best_dist_sq) { best_dist_sq = dist_sq; @@ -682,7 +682,7 @@ test_intersection_from_tube(const CollisionEntry &entry) const { new_entry->set_interior_point(point - interior_vec * radius); new_entry->set_surface_point(surface_point); - if (has_effective_normal() && tube->get_respect_effective_normal()) { + if (has_effective_normal() && capsule->get_respect_effective_normal()) { new_entry->set_surface_normal(get_effective_normal()); } else { new_entry->set_surface_normal(normal); diff --git a/panda/src/collide/collisionBox.h b/panda/src/collide/collisionBox.h index cf9e8bc15b..db9d2c2aa9 100644 --- a/panda/src/collide/collisionBox.h +++ b/panda/src/collide/collisionBox.h @@ -82,7 +82,7 @@ protected: virtual PT(CollisionEntry) test_intersection_from_segment(const CollisionEntry &entry) const; virtual PT(CollisionEntry) - test_intersection_from_tube(const CollisionEntry &entry) const; + test_intersection_from_capsule(const CollisionEntry &entry) const; virtual PT(CollisionEntry) test_intersection_from_box(const CollisionEntry &entry) const; diff --git a/panda/src/collide/collisionTube.I b/panda/src/collide/collisionCapsule.I similarity index 70% rename from panda/src/collide/collisionTube.I rename to panda/src/collide/collisionCapsule.I index 53e0f4cff6..78b8c3559b 100644 --- a/panda/src/collide/collisionTube.I +++ b/panda/src/collide/collisionCapsule.I @@ -6,7 +6,7 @@ * license. You should have received a copy of this license along * with this source code in a file named "LICENSE." * - * @file collisionTube.I + * @file collisionCapsule.I * @author drose * @date 2003-09-25 */ @@ -14,8 +14,8 @@ /** * */ -INLINE CollisionTube:: -CollisionTube(const LPoint3 &a, const LPoint3 &b, PN_stdfloat radius) : +INLINE CollisionCapsule:: +CollisionCapsule(const LPoint3 &a, const LPoint3 &b, PN_stdfloat radius) : _a(a), _b(b), _radius(radius) { recalc_internals(); @@ -25,8 +25,8 @@ CollisionTube(const LPoint3 &a, const LPoint3 &b, PN_stdfloat radius) : /** * */ -INLINE CollisionTube:: -CollisionTube(PN_stdfloat ax, PN_stdfloat ay, PN_stdfloat az, +INLINE CollisionCapsule:: +CollisionCapsule(PN_stdfloat ax, PN_stdfloat ay, PN_stdfloat az, PN_stdfloat bx, PN_stdfloat by, PN_stdfloat bz, PN_stdfloat radius) : _a(ax, ay, az), _b(bx, by, bz), _radius(radius) @@ -36,17 +36,17 @@ CollisionTube(PN_stdfloat ax, PN_stdfloat ay, PN_stdfloat az, } /** - * Creates an invalid tube. Only used when reading from a bam file. + * Creates an invalid capsule. Only used when reading from a bam file. */ -INLINE CollisionTube:: -CollisionTube() { +INLINE CollisionCapsule:: +CollisionCapsule() { } /** * */ -INLINE CollisionTube:: -CollisionTube(const CollisionTube ©) : +INLINE CollisionCapsule:: +CollisionCapsule(const CollisionCapsule ©) : CollisionSolid(copy), _a(copy._a), _b(copy._b), @@ -58,7 +58,7 @@ CollisionTube(const CollisionTube ©) : /** * Flushes the PStatCollectors used during traversal. */ -INLINE void CollisionTube:: +INLINE void CollisionCapsule:: flush_level() { _volume_pcollector.flush_level(); _test_pcollector.flush_level(); @@ -67,7 +67,7 @@ flush_level() { /** * */ -INLINE void CollisionTube:: +INLINE void CollisionCapsule:: set_point_a(const LPoint3 &a) { _a = a; recalc_internals(); @@ -76,7 +76,7 @@ set_point_a(const LPoint3 &a) { /** * */ -INLINE void CollisionTube:: +INLINE void CollisionCapsule:: set_point_a(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z) { set_point_a(LPoint3(x, y, z)); } @@ -84,7 +84,7 @@ set_point_a(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z) { /** * */ -INLINE const LPoint3 &CollisionTube:: +INLINE const LPoint3 &CollisionCapsule:: get_point_a() const { return _a; } @@ -92,7 +92,7 @@ get_point_a() const { /** * */ -INLINE void CollisionTube:: +INLINE void CollisionCapsule:: set_point_b(const LPoint3 &b) { _b = b; recalc_internals(); @@ -101,7 +101,7 @@ set_point_b(const LPoint3 &b) { /** * */ -INLINE void CollisionTube:: +INLINE void CollisionCapsule:: set_point_b(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z) { set_point_b(LPoint3(x, y, z)); } @@ -109,7 +109,7 @@ set_point_b(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z) { /** * */ -INLINE const LPoint3 &CollisionTube:: +INLINE const LPoint3 &CollisionCapsule:: get_point_b() const { return _b; } @@ -117,7 +117,7 @@ get_point_b() const { /** * */ -INLINE void CollisionTube:: +INLINE void CollisionCapsule:: set_radius(PN_stdfloat radius) { nassertv(radius >= 0.0f); _radius = radius; @@ -131,7 +131,7 @@ set_radius(PN_stdfloat radius) { /** * */ -INLINE PN_stdfloat CollisionTube:: +INLINE PN_stdfloat CollisionCapsule:: get_radius() const { return _radius; } diff --git a/panda/src/collide/collisionTube.cxx b/panda/src/collide/collisionCapsule.cxx similarity index 89% rename from panda/src/collide/collisionTube.cxx rename to panda/src/collide/collisionCapsule.cxx index 90f82a3630..91ab319735 100644 --- a/panda/src/collide/collisionTube.cxx +++ b/panda/src/collide/collisionCapsule.cxx @@ -6,12 +6,12 @@ * license. You should have received a copy of this license along * with this source code in a file named "LICENSE." * - * @file collisionTube.cxx + * @file collisionCapsule.cxx * @author drose * @date 2003-09-25 */ -#include "collisionTube.h" +#include "collisionCapsule.h" #include "collisionSphere.h" #include "collisionLine.h" #include "collisionRay.h" @@ -35,30 +35,30 @@ #include "geomVertexWriter.h" #include "boundingSphere.h" -PStatCollector CollisionTube::_volume_pcollector("Collision Volumes:CollisionTube"); -PStatCollector CollisionTube::_test_pcollector("Collision Tests:CollisionTube"); -TypeHandle CollisionTube::_type_handle; +PStatCollector CollisionCapsule::_volume_pcollector("Collision Volumes:CollisionCapsule"); +PStatCollector CollisionCapsule::_test_pcollector("Collision Tests:CollisionCapsule"); +TypeHandle CollisionCapsule::_type_handle; /** * */ -CollisionSolid *CollisionTube:: +CollisionSolid *CollisionCapsule:: make_copy() { - return new CollisionTube(*this); + return new CollisionCapsule(*this); } /** * */ -PT(CollisionEntry) CollisionTube:: +PT(CollisionEntry) CollisionCapsule:: test_intersection(const CollisionEntry &entry) const { - return entry.get_into()->test_intersection_from_tube(entry); + return entry.get_into()->test_intersection_from_capsule(entry); } /** * Transforms the solid by the indicated matrix. */ -void CollisionTube:: +void CollisionCapsule:: xform(const LMatrix4 &mat) { _a = _a * mat; _b = _b * mat; @@ -77,7 +77,7 @@ xform(const LMatrix4 &mat) { * collision purposes. The closest intersection point to this origin point is * considered to be the most significant. */ -LPoint3 CollisionTube:: +LPoint3 CollisionCapsule:: get_collision_origin() const { return get_point_a(); } @@ -86,7 +86,7 @@ get_collision_origin() const { * Returns a PStatCollector that is used to count the number of bounding * volume tests made against a solid of this type in a given frame. */ -PStatCollector &CollisionTube:: +PStatCollector &CollisionCapsule:: get_volume_pcollector() { return _volume_pcollector; } @@ -95,7 +95,7 @@ get_volume_pcollector() { * Returns a PStatCollector that is used to count the number of intersection * tests made against a solid of this type in a given frame. */ -PStatCollector &CollisionTube:: +PStatCollector &CollisionCapsule:: get_test_pcollector() { return _test_pcollector; } @@ -103,15 +103,15 @@ get_test_pcollector() { /** * */ -void CollisionTube:: +void CollisionCapsule:: output(std::ostream &out) const { - out << "tube, a (" << _a << "), b (" << _b << "), r " << _radius; + out << "capsule, a (" << _a << "), b (" << _b << "), r " << _radius; } /** * */ -PT(BoundingVolume) CollisionTube:: +PT(BoundingVolume) CollisionCapsule:: compute_internal_bounds() const { PT(BoundingVolume) bound = CollisionSolid::compute_internal_bounds(); @@ -143,7 +143,7 @@ compute_internal_bounds() const { /** * */ -PT(CollisionEntry) CollisionTube:: +PT(CollisionEntry) CollisionCapsule:: test_intersection_from_sphere(const CollisionEntry &entry) const { const CollisionSphere *sphere; DCAST_INTO_R(sphere, entry.get_from(), nullptr); @@ -160,7 +160,7 @@ test_intersection_from_sphere(const CollisionEntry &entry) const { PN_stdfloat actual_t = 0.0f; if (wrt_prev_space != wrt_space) { - // If the sphere is moving relative to the tube, it becomes a tube itself. + // If the sphere is moving relative to the capsule, it becomes a capsule itself. from_a = sphere->get_center() * wrt_prev_space->get_mat(); } @@ -195,11 +195,11 @@ test_intersection_from_sphere(const CollisionEntry &entry) const { LPoint3 into_intersection_point; if (t2 > 1.0) { - // Point b is within the tube. The first intersection point is point b + // Point b is within the capsule. The first intersection point is point b // itself. into_intersection_point = from_b; } else { - // Point b is outside the tube, and point a is either inside the tube or + // Point b is outside the capsule, and point a is either inside the capsule or // beyond it. The first intersection point is at t2. into_intersection_point = from_a + t2 * from_direction; } @@ -221,7 +221,7 @@ test_intersection_from_sphere(const CollisionEntry &entry) const { /** * */ -PT(CollisionEntry) CollisionTube:: +PT(CollisionEntry) CollisionCapsule:: test_intersection_from_line(const CollisionEntry &entry) const { const CollisionLine *line; DCAST_INTO_R(line, entry.get_from(), nullptr); @@ -269,7 +269,7 @@ test_intersection_from_line(const CollisionEntry &entry) const { /** * */ -PT(CollisionEntry) CollisionTube:: +PT(CollisionEntry) CollisionCapsule:: test_intersection_from_ray(const CollisionEntry &entry) const { const CollisionRay *ray; DCAST_INTO_R(ray, entry.get_from(), nullptr); @@ -299,11 +299,11 @@ test_intersection_from_ray(const CollisionEntry &entry) const { LPoint3 into_intersection_point; if (t1 < 0.0) { - // Point a is within the tube. The first intersection point is point a + // Point a is within the capsule. The first intersection point is point a // itself. into_intersection_point = from_origin; } else { - // Point a is outside the tube. The first intersection point is at t1. + // Point a is outside the capsule. The first intersection point is at t1. into_intersection_point = from_origin + t1 * from_direction; } set_intersection_point(new_entry, into_intersection_point, 0.0); @@ -330,7 +330,7 @@ test_intersection_from_ray(const CollisionEntry &entry) const { /** * */ -PT(CollisionEntry) CollisionTube:: +PT(CollisionEntry) CollisionCapsule:: test_intersection_from_segment(const CollisionEntry &entry) const { const CollisionSegment *segment; DCAST_INTO_R(segment, entry.get_from(), nullptr); @@ -362,11 +362,11 @@ test_intersection_from_segment(const CollisionEntry &entry) const { LPoint3 into_intersection_point; if (t1 < 0.0) { - // Point a is within the tube. The first intersection point is point a + // Point a is within the capsule. The first intersection point is point a // itself. into_intersection_point = from_a; } else { - // Point a is outside the tube, and point b is either inside the tube or + // Point a is outside the capsule, and point b is either inside the capsule or // beyond it. The first intersection point is at t1. into_intersection_point = from_a + t1 * from_direction; } @@ -394,22 +394,22 @@ test_intersection_from_segment(const CollisionEntry &entry) const { /** * */ -PT(CollisionEntry) CollisionTube:: -test_intersection_from_tube(const CollisionEntry &entry) const { - const CollisionTube *tube; - DCAST_INTO_R(tube, entry.get_from(), nullptr); +PT(CollisionEntry) CollisionCapsule:: +test_intersection_from_capsule(const CollisionEntry &entry) const { + const CollisionCapsule *capsule; + DCAST_INTO_R(capsule, entry.get_from(), nullptr); LPoint3 into_a = _a; LVector3 into_direction = _b - into_a; const LMatrix4 &wrt_mat = entry.get_wrt_mat(); - LPoint3 from_a = tube->get_point_a() * wrt_mat; - LPoint3 from_b = tube->get_point_b() * wrt_mat; + LPoint3 from_a = capsule->get_point_a() * wrt_mat; + LPoint3 from_b = capsule->get_point_b() * wrt_mat; LVector3 from_direction = from_b - from_a; LVector3 from_radius_v = - LVector3(tube->get_radius(), 0.0f, 0.0f) * wrt_mat; + LVector3(capsule->get_radius(), 0.0f, 0.0f) * wrt_mat; PN_stdfloat from_radius = length(from_radius_v); // Determine the points on each segment with the smallest distance between. @@ -420,7 +420,7 @@ test_intersection_from_tube(const CollisionEntry &entry) const { LPoint3 into_closest = into_a + into_direction * into_t; LPoint3 from_closest = from_a + from_direction * from_t; - // If the distance is greater than the sum of tube radii, the test fails. + // If the distance is greater than the sum of capsule radii, the test fails. LVector3 closest_vec = from_closest - into_closest; PN_stdfloat distance = closest_vec.length(); if (distance > _radius + from_radius) { @@ -442,7 +442,7 @@ test_intersection_from_tube(const CollisionEntry &entry) const { new_entry->set_surface_point(into_closest + surface_normal * _radius); new_entry->set_interior_point(from_closest - surface_normal * from_radius); - if (has_effective_normal() && tube->get_respect_effective_normal()) { + if (has_effective_normal() && capsule->get_respect_effective_normal()) { new_entry->set_surface_normal(get_effective_normal()); } else if (distance != 0) { new_entry->set_surface_normal(surface_normal); @@ -458,7 +458,7 @@ test_intersection_from_tube(const CollisionEntry &entry) const { /** * */ -PT(CollisionEntry) CollisionTube:: +PT(CollisionEntry) CollisionCapsule:: test_intersection_from_parabola(const CollisionEntry &entry) const { const CollisionParabola *parabola; DCAST_INTO_R(parabola, entry.get_from(), nullptr); @@ -510,14 +510,14 @@ test_intersection_from_parabola(const CollisionEntry &entry) const { * Fills the _viz_geom GeomNode up with Geoms suitable for rendering this * solid. */ -void CollisionTube:: +void CollisionCapsule:: fill_viz_geom() { if (collide_cat.is_debug()) { collide_cat.debug() << "Recomputing viz for " << *this << "\n"; } - // Generate the vertices such that we draw a tube with one endpoint at (0, + // Generate the vertices such that we draw a capsule with one endpoint at (0, // 0, 0), and another at (0, length, 0). Then we'll rotate and translate it // into place with the appropriate look_at matrix. LVector3 direction = (_b - _a); @@ -576,9 +576,9 @@ fill_viz_geom() { /** * Should be called internally to recompute the matrix and length when the - * properties of the tube have changed. + * properties of the capsule have changed. */ -void CollisionTube:: +void CollisionCapsule:: recalc_internals() { LVector3 direction = (_b - _a); _length = direction.length(); @@ -595,7 +595,7 @@ recalc_internals() { * Calculates a particular vertex on the surface of the first endcap * hemisphere, for use in generating the viz geometry. */ -LVertex CollisionTube:: +LVertex CollisionCapsule:: calc_sphere1_vertex(int ri, int si, int num_rings, int num_slices) { PN_stdfloat r = (PN_stdfloat)ri / (PN_stdfloat)num_rings; PN_stdfloat s = (PN_stdfloat)si / (PN_stdfloat)num_slices; @@ -620,7 +620,7 @@ calc_sphere1_vertex(int ri, int si, int num_rings, int num_slices) { * Calculates a particular vertex on the surface of the second endcap * hemisphere, for use in generating the viz geometry. */ -LVertex CollisionTube:: +LVertex CollisionCapsule:: calc_sphere2_vertex(int ri, int si, int num_rings, int num_slices, PN_stdfloat length) { PN_stdfloat r = (PN_stdfloat)ri / (PN_stdfloat)num_rings; @@ -646,7 +646,7 @@ calc_sphere2_vertex(int ri, int si, int num_rings, int num_slices, * Given line segments s1 and s2 defined by two points each, computes the * point on each segment with the closest distance between them. */ -void CollisionTube:: +void CollisionCapsule:: calc_closest_segment_points(double &t1, double &t2, const LPoint3 &from1, const LVector3 &delta1, const LPoint3 &from2, const LVector3 &delta2) { @@ -717,18 +717,18 @@ calc_closest_segment_points(double &t1, double &t2, } /** - * Determine the point(s) of intersection of a parametric line with the tube. + * Determine the point(s) of intersection of a parametric line with the capsule. * The line is infinite in both directions, and passes through "from" and - * from+delta. If the line does not intersect the tube, the function returns - * false, and t1 and t2 are undefined. If it does intersect the tube, it + * from+delta. If the line does not intersect the capsule, the function returns + * false, and t1 and t2 are undefined. If it does intersect the capsule, it * returns true, and t1 and t2 are set to the points along the equation * from+t*delta that correspond to the two points of intersection. */ -bool CollisionTube:: +bool CollisionCapsule:: intersects_line(double &t1, double &t2, const LPoint3 &from0, const LVector3 &delta0, PN_stdfloat inflate_radius) const { - // Convert the line into our canonical coordinate space: the tube is aligned + // Convert the line into our canonical coordinate space: the capsule is aligned // with the y axis. LPoint3 from = from0 * _inv_mat; LVector3 delta = delta0 * _inv_mat; @@ -776,7 +776,7 @@ intersects_line(double &t1, double &t2, } } - // The point is within the tube! + // The point is within the capsule! t1 = t2 = 0.0; return true; } @@ -819,15 +819,15 @@ intersects_line(double &t1, double &t2, PN_stdfloat t2_y = from[1] + t2 * delta[1]; if (t1_y < -radius && t2_y < -radius) { - // Both points are way off the bottom of the tube; no intersection. + // Both points are way off the bottom of the capsule; no intersection. return false; } else if (t1_y > _length + radius && t2_y > _length + radius) { - // Both points are way off the top of the tube; no intersection. + // Both points are way off the top of the capsule; no intersection. return false; } if (t1_y < 0.0f) { - // The starting point is off the bottom of the tube. Test the line + // The starting point is off the bottom of the capsule. Test the line // against the first endcap. double t1a, t2a; if (!sphere_intersects_line(t1a, t2a, 0.0f, from, delta, radius)) { @@ -838,7 +838,7 @@ intersects_line(double &t1, double &t2, t1 = t1a; } else if (t1_y > _length) { - // The starting point is off the top of the tube. Test the line against + // The starting point is off the top of the capsule. Test the line against // the second endcap. double t1b, t2b; if (!sphere_intersects_line(t1b, t2b, _length, from, delta, radius)) { @@ -850,7 +850,7 @@ intersects_line(double &t1, double &t2, } if (t2_y < 0.0f) { - // The ending point is off the bottom of the tube. Test the line against + // The ending point is off the bottom of the capsule. Test the line against // the first endcap. double t1a, t2a; if (!sphere_intersects_line(t1a, t2a, 0.0f, from, delta, radius)) { @@ -861,7 +861,7 @@ intersects_line(double &t1, double &t2, t2 = t2a; } else if (t2_y > _length) { - // The ending point is off the top of the tube. Test the line against the + // The ending point is off the top of the capsule. Test the line against the // second endcap. double t1b, t2b; if (!sphere_intersects_line(t1b, t2b, _length, from, delta, radius)) { @@ -880,7 +880,7 @@ intersects_line(double &t1, double &t2, * whether it intersects one or the other endcaps. The y parameter specifies * the center of the sphere (and hence the particular endcap). */ -bool CollisionTube:: +bool CollisionCapsule:: sphere_intersects_line(double &t1, double &t2, PN_stdfloat center_y, const LPoint3 &from, const LVector3 &delta, PN_stdfloat radius) { @@ -917,14 +917,14 @@ sphere_intersects_line(double &t1, double &t2, PN_stdfloat center_y, } /** - * Determine a point of intersection of a parametric parabola with the tube. + * Determine a point of intersection of a parametric parabola with the capsule. * * We only consider the segment of the parabola between t1 and t2, which has * already been computed as corresponding to points p1 and p2. If there is an * intersection, t is set to the parametric point of intersection, and true is * returned; otherwise, false is returned. */ -bool CollisionTube:: +bool CollisionCapsule:: intersects_parabola(double &t, const LParabola ¶bola, double t1, double t2, const LPoint3 &p1, const LPoint3 &p2) const { @@ -965,11 +965,11 @@ intersects_parabola(double &t, const LParabola ¶bola, } /** - * Calculates a point that is exactly on the surface of the tube and its + * Calculates a point that is exactly on the surface of the capsule and its * corresponding normal, given a point that is supposedly on the surface of - * the tube. + * the capsule. */ -void CollisionTube:: +void CollisionCapsule:: calculate_surface_point_and_normal(const LPoint3 &surface_point, double extra_radius, LPoint3 &result_point, @@ -1015,7 +1015,7 @@ calculate_surface_point_and_normal(const LPoint3 &surface_point, * point in the CollisionEntry, and also compute the relevant normal based on * that point. */ -void CollisionTube:: +void CollisionCapsule:: set_intersection_point(CollisionEntry *new_entry, const LPoint3 &into_intersection_point, double extra_radius) const { @@ -1033,16 +1033,16 @@ set_intersection_point(CollisionEntry *new_entry, new_entry->set_surface_normal(normal); new_entry->set_surface_point(point); - // Also adjust the original point into the tube by the amount of - // extra_radius, which should put it on the surface of the tube if our + // Also adjust the original point into the capsule by the amount of + // extra_radius, which should put it on the surface of the capsule if our // collision was tangential. new_entry->set_interior_point(into_intersection_point - normal * extra_radius); } /** - * Tells the BamReader how to create objects of type CollisionTube. + * Tells the BamReader how to create objects of type CollisionCapsule. */ -void CollisionTube:: +void CollisionCapsule:: register_with_read_factory() { BamReader::get_factory()->register_factory(get_class_type(), make_from_bam); } @@ -1051,7 +1051,7 @@ register_with_read_factory() { * Writes the contents of this object to the datagram for shipping out to a * Bam file. */ -void CollisionTube:: +void CollisionCapsule:: write_datagram(BamWriter *manager, Datagram &dg) { CollisionSolid::write_datagram(manager, dg); _a.write_datagram(dg); @@ -1061,12 +1061,12 @@ write_datagram(BamWriter *manager, Datagram &dg) { /** * This function is called by the BamReader's factory when a new object of - * type CollisionTube is encountered in the Bam file. It should create the - * CollisionTube and extract its information from the file. + * type CollisionCapsule is encountered in the Bam file. It should create the + * CollisionCapsule and extract its information from the file. */ -TypedWritable *CollisionTube:: +TypedWritable *CollisionCapsule:: make_from_bam(const FactoryParams ¶ms) { - CollisionTube *node = new CollisionTube(); + CollisionCapsule *node = new CollisionCapsule(); DatagramIterator scan; BamReader *manager; @@ -1078,9 +1078,9 @@ make_from_bam(const FactoryParams ¶ms) { /** * This internal function is called by make_from_bam to read in all of the - * relevant data from the BamFile for the new CollisionTube. + * relevant data from the BamFile for the new CollisionCapsule. */ -void CollisionTube:: +void CollisionCapsule:: fillin(DatagramIterator &scan, BamReader *manager) { CollisionSolid::fillin(scan, manager); _a.read_datagram(scan); diff --git a/panda/src/collide/collisionCapsule.h b/panda/src/collide/collisionCapsule.h new file mode 100644 index 0000000000..b6d375ff44 --- /dev/null +++ b/panda/src/collide/collisionCapsule.h @@ -0,0 +1,161 @@ +/** + * 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 collisionCapsule.h + * @author drose + * @date 2003-09-25 + */ + +#ifndef COLLISIONCAPSULE_H +#define COLLISIONCAPSULE_H + +#include "pandabase.h" +#include "collisionSolid.h" +#include "parabola.h" + +/** + * This implements a solid consisting of a cylinder with hemispherical endcaps, + * also known as a capsule or a spherocylinder. + * + * This shape was previously erroneously called CollisionTube. + */ +class EXPCL_PANDA_COLLIDE CollisionCapsule : public CollisionSolid { +PUBLISHED: + INLINE explicit CollisionCapsule(const LPoint3 &a, const LPoint3 &db, + PN_stdfloat radius); + INLINE explicit CollisionCapsule(PN_stdfloat ax, PN_stdfloat ay, PN_stdfloat az, + PN_stdfloat bx, PN_stdfloat by, PN_stdfloat bz, + PN_stdfloat radius); + + virtual LPoint3 get_collision_origin() const; + +private: + INLINE CollisionCapsule(); + +public: + INLINE CollisionCapsule(const CollisionCapsule ©); + virtual CollisionSolid *make_copy(); + + virtual PT(CollisionEntry) + test_intersection(const CollisionEntry &entry) const; + + virtual void xform(const LMatrix4 &mat); + + virtual PStatCollector &get_volume_pcollector(); + virtual PStatCollector &get_test_pcollector(); + + virtual void output(std::ostream &out) const; + + INLINE static void flush_level(); + +PUBLISHED: + INLINE void set_point_a(const LPoint3 &a); + INLINE void set_point_a(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z); + INLINE const LPoint3 &get_point_a() const; + + INLINE void set_point_b(const LPoint3 &b); + INLINE void set_point_b(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z); + INLINE const LPoint3 &get_point_b() const; + + INLINE void set_radius(PN_stdfloat radius); + INLINE PN_stdfloat get_radius() const; + +PUBLISHED: + MAKE_PROPERTY(point_a, get_point_a, set_point_a); + MAKE_PROPERTY(point_b, get_point_b, set_point_b); + MAKE_PROPERTY(radius, get_radius, set_radius); + +protected: + virtual PT(BoundingVolume) compute_internal_bounds() const; + +protected: + virtual PT(CollisionEntry) + test_intersection_from_sphere(const CollisionEntry &entry) const; + virtual PT(CollisionEntry) + test_intersection_from_line(const CollisionEntry &entry) const; + virtual PT(CollisionEntry) + test_intersection_from_ray(const CollisionEntry &entry) const; + virtual PT(CollisionEntry) + test_intersection_from_segment(const CollisionEntry &entry) const; + virtual PT(CollisionEntry) + test_intersection_from_capsule(const CollisionEntry &entry) const; + virtual PT(CollisionEntry) + test_intersection_from_parabola(const CollisionEntry &entry) const; + + virtual void fill_viz_geom(); + +private: + void recalc_internals(); + + LVertex calc_sphere1_vertex(int ri, int si, int num_rings, int num_slices); + LVertex calc_sphere2_vertex(int ri, int si, int num_rings, int num_slices, + PN_stdfloat length); + + static void calc_closest_segment_points(double &t1, double &t2, + const LPoint3 &from1, const LVector3 &delta1, + const LPoint3 &from2, const LVector3 &delta2); + bool intersects_line(double &t1, double &t2, + const LPoint3 &from, const LVector3 &delta, + PN_stdfloat inflate_radius) const; + static bool sphere_intersects_line(double &t1, double &t2, PN_stdfloat center_y, + const LPoint3 &from, const LVector3 &delta, + PN_stdfloat radius); + bool intersects_parabola(double &t, const LParabola ¶bola, + double t1, double t2, + const LPoint3 &p1, const LPoint3 &p2) const; + void calculate_surface_point_and_normal(const LPoint3 &surface_point, + double extra_radius, + LPoint3 &result_point, + LVector3 &result_normal) const; + void set_intersection_point(CollisionEntry *new_entry, + const LPoint3 &into_intersection_point, + double extra_radius) const; + +private: + LPoint3 _a, _b; + PN_stdfloat _radius; + + // These are derived from the above. + LMatrix4 _mat; + LMatrix4 _inv_mat; + PN_stdfloat _length; + + static PStatCollector _volume_pcollector; + static PStatCollector _test_pcollector; + +public: + static void register_with_read_factory(); + virtual void write_datagram(BamWriter *manager, Datagram &dg); + +protected: + static TypedWritable *make_from_bam(const FactoryParams ¶ms); + void fillin(DatagramIterator &scan, BamReader *manager); + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + CollisionSolid::init_type(); + register_type(_type_handle, "CollisionCapsule", + CollisionSolid::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; + + friend class CollisionBox; +}; + +#include "collisionCapsule.I" + +#endif diff --git a/panda/src/collide/collisionHandlerFluidPusher.cxx b/panda/src/collide/collisionHandlerFluidPusher.cxx index df15e8bacc..622f5da00d 100644 --- a/panda/src/collide/collisionHandlerFluidPusher.cxx +++ b/panda/src/collide/collisionHandlerFluidPusher.cxx @@ -72,7 +72,7 @@ handle_entries() { PosB = collider's current position M = movement vector (PosB - PosA) BV = bounding sphere that includes collider at PosA and PosB - CS = 'collision set', all 'collidables' within BV (collision polys, tubes, etc) + CS = 'collision set', all 'collidables' within BV (collision polys, capsules, etc) VARIABLES N = movement vector since most recent collision (or start of frame) diff --git a/panda/src/collide/collisionPlane.cxx b/panda/src/collide/collisionPlane.cxx index 8a325ea082..2e174d4aa8 100644 --- a/panda/src/collide/collisionPlane.cxx +++ b/panda/src/collide/collisionPlane.cxx @@ -18,7 +18,7 @@ #include "collisionLine.h" #include "collisionRay.h" #include "collisionSegment.h" -#include "collisionTube.h" +#include "collisionCapsule.h" #include "collisionParabola.h" #include "config_collide.h" #include "pointerToArray.h" @@ -298,16 +298,16 @@ test_intersection_from_segment(const CollisionEntry &entry) const { * */ PT(CollisionEntry) CollisionPlane:: -test_intersection_from_tube(const CollisionEntry &entry) const { - const CollisionTube *tube; - DCAST_INTO_R(tube, entry.get_from(), nullptr); +test_intersection_from_capsule(const CollisionEntry &entry) const { + const CollisionCapsule *capsule; + DCAST_INTO_R(capsule, entry.get_from(), nullptr); const LMatrix4 &wrt_mat = entry.get_wrt_mat(); - LPoint3 from_a = tube->get_point_a() * wrt_mat; - LPoint3 from_b = tube->get_point_b() * wrt_mat; + LPoint3 from_a = capsule->get_point_a() * wrt_mat; + LPoint3 from_b = capsule->get_point_b() * wrt_mat; LVector3 from_radius_v = - LVector3(tube->get_radius(), 0.0f, 0.0f) * wrt_mat; + LVector3(capsule->get_radius(), 0.0f, 0.0f) * wrt_mat; PN_stdfloat from_radius = length(from_radius_v); PN_stdfloat dist_a = _plane.dist_to_plane(from_a); @@ -325,7 +325,7 @@ test_intersection_from_tube(const CollisionEntry &entry) const { } PT(CollisionEntry) new_entry = new CollisionEntry(entry); - LVector3 normal = (has_effective_normal() && tube->get_respect_effective_normal()) ? get_effective_normal() : get_normal(); + LVector3 normal = (has_effective_normal() && capsule->get_respect_effective_normal()) ? get_effective_normal() : get_normal(); new_entry->set_surface_normal(normal); PN_stdfloat t; @@ -339,17 +339,17 @@ test_intersection_from_tube(const CollisionEntry &entry) const { new_entry->set_surface_point(from_a - get_normal() * dist_a); } else { - // Within the tube! Yay, that means we have a surface point. + // Within the capsule! Yay, that means we have a surface point. new_entry->set_surface_point(from_a + t * from_direction); } } else { // If it's completely parallel, pretend it's colliding in the center of - // the tube. + // the capsule. new_entry->set_surface_point(from_a + 0.5f * from_direction - get_normal() * dist_a); } if (IS_NEARLY_EQUAL(dist_a, dist_b)) { - // Let's be fair and choose the center of the tube. + // Let's be fair and choose the center of the capsule. new_entry->set_interior_point(from_a + 0.5f * from_direction - get_normal() * from_radius); } else if (dist_a < dist_b) { diff --git a/panda/src/collide/collisionPlane.h b/panda/src/collide/collisionPlane.h index bc7602ba8b..719e33f489 100644 --- a/panda/src/collide/collisionPlane.h +++ b/panda/src/collide/collisionPlane.h @@ -72,7 +72,7 @@ protected: virtual PT(CollisionEntry) test_intersection_from_segment(const CollisionEntry &entry) const; virtual PT(CollisionEntry) - test_intersection_from_tube(const CollisionEntry &entry) const; + test_intersection_from_capsule(const CollisionEntry &entry) const; virtual PT(CollisionEntry) test_intersection_from_parabola(const CollisionEntry &entry) const; virtual PT(CollisionEntry) diff --git a/panda/src/collide/collisionSolid.cxx b/panda/src/collide/collisionSolid.cxx index b862ec7c73..b68673be15 100644 --- a/panda/src/collide/collisionSolid.cxx +++ b/panda/src/collide/collisionSolid.cxx @@ -17,7 +17,7 @@ #include "collisionLine.h" #include "collisionRay.h" #include "collisionSegment.h" -#include "collisionTube.h" +#include "collisionCapsule.h" #include "collisionParabola.h" #include "collisionBox.h" #include "collisionEntry.h" @@ -240,11 +240,11 @@ test_intersection_from_segment(const CollisionEntry &) const { /** * This is part of the double-dispatch implementation of test_intersection(). - * It is called when the "from" object is a tube. + * It is called when the "from" object is a capsule. */ PT(CollisionEntry) CollisionSolid:: -test_intersection_from_tube(const CollisionEntry &) const { - report_undefined_intersection_test(CollisionTube::get_class_type(), +test_intersection_from_capsule(const CollisionEntry &) const { + report_undefined_intersection_test(CollisionCapsule::get_class_type(), get_type()); return nullptr; } diff --git a/panda/src/collide/collisionSolid.h b/panda/src/collide/collisionSolid.h index 6b2914e954..4721d70381 100644 --- a/panda/src/collide/collisionSolid.h +++ b/panda/src/collide/collisionSolid.h @@ -108,7 +108,7 @@ protected: virtual PT(CollisionEntry) test_intersection_from_segment(const CollisionEntry &entry) const; virtual PT(CollisionEntry) - test_intersection_from_tube(const CollisionEntry &entry) const; + test_intersection_from_capsule(const CollisionEntry &entry) const; virtual PT(CollisionEntry) test_intersection_from_parabola(const CollisionEntry &entry) const; virtual PT(CollisionEntry) @@ -177,7 +177,7 @@ private: friend class CollisionLine; friend class CollisionRay; friend class CollisionSegment; - friend class CollisionTube; + friend class CollisionCapsule; friend class CollisionParabola; friend class CollisionHandlerFluidPusher; friend class CollisionBox; diff --git a/panda/src/collide/collisionSphere.cxx b/panda/src/collide/collisionSphere.cxx index a07b1fbcbc..aacc43fcb2 100644 --- a/panda/src/collide/collisionSphere.cxx +++ b/panda/src/collide/collisionSphere.cxx @@ -17,7 +17,7 @@ #include "collisionHandler.h" #include "collisionEntry.h" #include "collisionSegment.h" -#include "collisionTube.h" +#include "collisionCapsule.h" #include "collisionParabola.h" #include "collisionBox.h" #include "config_collide.h" @@ -444,18 +444,18 @@ test_intersection_from_segment(const CollisionEntry &entry) const { * */ PT(CollisionEntry) CollisionSphere:: -test_intersection_from_tube(const CollisionEntry &entry) const { - const CollisionTube *tube; - DCAST_INTO_R(tube, entry.get_from(), nullptr); +test_intersection_from_capsule(const CollisionEntry &entry) const { + const CollisionCapsule *capsule; + DCAST_INTO_R(capsule, entry.get_from(), nullptr); const LMatrix4 &wrt_mat = entry.get_wrt_mat(); - LPoint3 from_a = tube->get_point_a() * wrt_mat; - LPoint3 from_b = tube->get_point_b() * wrt_mat; + LPoint3 from_a = capsule->get_point_a() * wrt_mat; + LPoint3 from_b = capsule->get_point_b() * wrt_mat; LVector3 from_direction = from_b - from_a; LVector3 from_radius_v = - LVector3(tube->get_radius(), 0.0f, 0.0f) * wrt_mat; + LVector3(capsule->get_radius(), 0.0f, 0.0f) * wrt_mat; PN_stdfloat from_radius = length(from_radius_v); double t1, t2; @@ -465,8 +465,8 @@ test_intersection_from_tube(const CollisionEntry &entry) const { } if (t2 < 0.0 || t1 > 1.0) { - // Both intersection points are before the start of the tube or after - // the end of the tube. + // Both intersection points are before the start of the capsule or after + // the end of the capsule. return nullptr; } @@ -487,7 +487,7 @@ test_intersection_from_tube(const CollisionEntry &entry) const { new_entry->set_surface_point(get_center() + normal * get_radius()); new_entry->set_interior_point(inner_point - normal * from_radius); - if (has_effective_normal() && tube->get_respect_effective_normal()) { + if (has_effective_normal() && capsule->get_respect_effective_normal()) { new_entry->set_surface_normal(get_effective_normal()); } else { new_entry->set_surface_normal(normal); diff --git a/panda/src/collide/collisionSphere.h b/panda/src/collide/collisionSphere.h index 8337644b42..39b21d59f1 100644 --- a/panda/src/collide/collisionSphere.h +++ b/panda/src/collide/collisionSphere.h @@ -72,7 +72,7 @@ protected: virtual PT(CollisionEntry) test_intersection_from_segment(const CollisionEntry &entry) const; virtual PT(CollisionEntry) - test_intersection_from_tube(const CollisionEntry &entry) const; + test_intersection_from_capsule(const CollisionEntry &entry) const; virtual PT(CollisionEntry) test_intersection_from_parabola(const CollisionEntry &entry) const; virtual PT(CollisionEntry) diff --git a/panda/src/collide/collisionTraverser.cxx b/panda/src/collide/collisionTraverser.cxx index 35f7966a3f..794e7552da 100644 --- a/panda/src/collide/collisionTraverser.cxx +++ b/panda/src/collide/collisionTraverser.cxx @@ -20,7 +20,7 @@ #include "collisionVisualizer.h" #include "collisionSphere.h" #include "collisionBox.h" -#include "collisionTube.h" +#include "collisionCapsule.h" #include "collisionPolygon.h" #include "collisionPlane.h" #include "config_collide.h" @@ -345,7 +345,7 @@ traverse(const NodePath &root) { _geom_volume_pcollector.flush_level(); CollisionSphere::flush_level(); - CollisionTube::flush_level(); + CollisionCapsule::flush_level(); CollisionPolygon::flush_level(); CollisionPlane::flush_level(); CollisionBox::flush_level(); diff --git a/panda/src/collide/collisionTube.h b/panda/src/collide/collisionTube.h index 3d91ce2506..c67708628e 100644 --- a/panda/src/collide/collisionTube.h +++ b/panda/src/collide/collisionTube.h @@ -7,154 +7,21 @@ * with this source code in a file named "LICENSE." * * @file collisionTube.h - * @author drose - * @date 2003-09-25 + * @author rdb + * @date 2018-12-23 */ #ifndef COLLISIONTUBE_H #define COLLISIONTUBE_H -#include "pandabase.h" -#include "collisionSolid.h" -#include "parabola.h" +#include "collisionCapsule.h" +BEGIN_PUBLISH /** - * This implements a solid roughly in cylindrical shape. It's not called a - * CollisionCylinder because it's not a true cylinder; specifically, it has - * rounded ends instead of flat ends. It looks more like a Contac pill. + * Alias for backward compatibility. + * @deprecated use CollisionCapsule instead. */ -class EXPCL_PANDA_COLLIDE CollisionTube : public CollisionSolid { -PUBLISHED: - INLINE explicit CollisionTube(const LPoint3 &a, const LPoint3 &db, - PN_stdfloat radius); - INLINE explicit CollisionTube(PN_stdfloat ax, PN_stdfloat ay, PN_stdfloat az, - PN_stdfloat bx, PN_stdfloat by, PN_stdfloat bz, - PN_stdfloat radius); - - virtual LPoint3 get_collision_origin() const; - -private: - INLINE CollisionTube(); - -public: - INLINE CollisionTube(const CollisionTube ©); - virtual CollisionSolid *make_copy(); - - virtual PT(CollisionEntry) - test_intersection(const CollisionEntry &entry) const; - - virtual void xform(const LMatrix4 &mat); - - virtual PStatCollector &get_volume_pcollector(); - virtual PStatCollector &get_test_pcollector(); - - virtual void output(std::ostream &out) const; - - INLINE static void flush_level(); - -PUBLISHED: - INLINE void set_point_a(const LPoint3 &a); - INLINE void set_point_a(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z); - INLINE const LPoint3 &get_point_a() const; - - INLINE void set_point_b(const LPoint3 &b); - INLINE void set_point_b(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z); - INLINE const LPoint3 &get_point_b() const; - - INLINE void set_radius(PN_stdfloat radius); - INLINE PN_stdfloat get_radius() const; - -PUBLISHED: - MAKE_PROPERTY(point_a, get_point_a, set_point_a); - MAKE_PROPERTY(point_b, get_point_b, set_point_b); - MAKE_PROPERTY(radius, get_radius, set_radius); - -protected: - virtual PT(BoundingVolume) compute_internal_bounds() const; - -protected: - virtual PT(CollisionEntry) - test_intersection_from_sphere(const CollisionEntry &entry) const; - virtual PT(CollisionEntry) - test_intersection_from_line(const CollisionEntry &entry) const; - virtual PT(CollisionEntry) - test_intersection_from_ray(const CollisionEntry &entry) const; - virtual PT(CollisionEntry) - test_intersection_from_segment(const CollisionEntry &entry) const; - virtual PT(CollisionEntry) - test_intersection_from_tube(const CollisionEntry &entry) const; - virtual PT(CollisionEntry) - test_intersection_from_parabola(const CollisionEntry &entry) const; - - virtual void fill_viz_geom(); - -private: - void recalc_internals(); - - LVertex calc_sphere1_vertex(int ri, int si, int num_rings, int num_slices); - LVertex calc_sphere2_vertex(int ri, int si, int num_rings, int num_slices, - PN_stdfloat length); - - static void calc_closest_segment_points(double &t1, double &t2, - const LPoint3 &from1, const LVector3 &delta1, - const LPoint3 &from2, const LVector3 &delta2); - bool intersects_line(double &t1, double &t2, - const LPoint3 &from, const LVector3 &delta, - PN_stdfloat inflate_radius) const; - static bool sphere_intersects_line(double &t1, double &t2, PN_stdfloat center_y, - const LPoint3 &from, const LVector3 &delta, - PN_stdfloat radius); - bool intersects_parabola(double &t, const LParabola ¶bola, - double t1, double t2, - const LPoint3 &p1, const LPoint3 &p2) const; - void calculate_surface_point_and_normal(const LPoint3 &surface_point, - double extra_radius, - LPoint3 &result_point, - LVector3 &result_normal) const; - void set_intersection_point(CollisionEntry *new_entry, - const LPoint3 &into_intersection_point, - double extra_radius) const; - -private: - LPoint3 _a, _b; - PN_stdfloat _radius; - - // These are derived from the above. - LMatrix4 _mat; - LMatrix4 _inv_mat; - PN_stdfloat _length; - - static PStatCollector _volume_pcollector; - static PStatCollector _test_pcollector; - -public: - static void register_with_read_factory(); - virtual void write_datagram(BamWriter *manager, Datagram &dg); - -protected: - static TypedWritable *make_from_bam(const FactoryParams ¶ms); - void fillin(DatagramIterator &scan, BamReader *manager); - -public: - static TypeHandle get_class_type() { - return _type_handle; - } - static void init_type() { - CollisionSolid::init_type(); - register_type(_type_handle, "CollisionTube", - CollisionSolid::get_class_type()); - } - virtual TypeHandle get_type() const { - return get_class_type(); - } - virtual TypeHandle force_init_type() {init_type(); return get_class_type();} - -private: - static TypeHandle _type_handle; - - friend class CollisionBox; -}; - -#include "collisionTube.I" +typedef CollisionCapsule CollisionTube; +END_PUBLISH #endif diff --git a/panda/src/collide/config_collide.cxx b/panda/src/collide/config_collide.cxx index 38c821ed05..b9ca4e368a 100644 --- a/panda/src/collide/config_collide.cxx +++ b/panda/src/collide/config_collide.cxx @@ -13,6 +13,7 @@ #include "config_collide.h" #include "collisionBox.h" +#include "collisionCapsule.h" #include "collisionEntry.h" #include "collisionHandler.h" #include "collisionHandlerEvent.h" @@ -38,7 +39,6 @@ #include "collisionSolid.h" #include "collisionSphere.h" #include "collisionTraverser.h" -#include "collisionTube.h" #include "collisionVisualizer.h" #include "dconfig.h" @@ -126,6 +126,7 @@ init_libcollide() { initialized = true; CollisionBox::init_type(); + CollisionCapsule::init_type(); CollisionEntry::init_type(); CollisionHandler::init_type(); CollisionHandlerEvent::init_type(); @@ -150,14 +151,18 @@ init_libcollide() { CollisionSolid::init_type(); CollisionSphere::init_type(); CollisionTraverser::init_type(); - CollisionTube::init_type(); #ifdef DO_COLLISION_RECORDING CollisionRecorder::init_type(); CollisionVisualizer::init_type(); #endif + // Record the old name for CollisionCapsule for backwards compatibility. + BamWriter::record_obsolete_type_name(CollisionCapsule::get_class_type(), + "CollisionTube", 6, 44); + CollisionBox::register_with_read_factory(); + CollisionCapsule::register_with_read_factory(); CollisionInvSphere::register_with_read_factory(); CollisionLine::register_with_read_factory(); CollisionNode::register_with_read_factory(); @@ -168,5 +173,4 @@ init_libcollide() { CollisionRay::register_with_read_factory(); CollisionSegment::register_with_read_factory(); CollisionSphere::register_with_read_factory(); - CollisionTube::register_with_read_factory(); } diff --git a/panda/src/collide/p3collide_composite1.cxx b/panda/src/collide/p3collide_composite1.cxx index d66b71b18c..af8ecc07d8 100644 --- a/panda/src/collide/p3collide_composite1.cxx +++ b/panda/src/collide/p3collide_composite1.cxx @@ -1,5 +1,6 @@ #include "config_collide.cxx" #include "collisionBox.cxx" +#include "collisionCapsule.cxx" #include "collisionEntry.cxx" #include "collisionGeom.cxx" #include "collisionHandler.cxx" @@ -12,5 +13,3 @@ #include "collisionHandlerFluidPusher.cxx" #include "collisionHandlerQueue.cxx" #include "collisionInvSphere.cxx" -#include "collisionLevelStateBase.cxx" -#include "collisionLevelState.cxx" diff --git a/panda/src/collide/p3collide_composite2.cxx b/panda/src/collide/p3collide_composite2.cxx index 50a6acdddc..4b74e9dd9e 100644 --- a/panda/src/collide/p3collide_composite2.cxx +++ b/panda/src/collide/p3collide_composite2.cxx @@ -1,3 +1,5 @@ +#include "collisionLevelStateBase.cxx" +#include "collisionLevelState.cxx" #include "collisionLine.cxx" #include "collisionNode.cxx" #include "collisionParabola.cxx" @@ -10,5 +12,4 @@ #include "collisionSolid.cxx" #include "collisionSphere.cxx" #include "collisionTraverser.cxx" -#include "collisionTube.cxx" #include "collisionVisualizer.cxx" diff --git a/panda/src/configfiles/pandaIcon.ico b/panda/src/configfiles/pandaIcon.ico index cd6f45759f..3a2c1c33d4 100644 Binary files a/panda/src/configfiles/pandaIcon.ico and b/panda/src/configfiles/pandaIcon.ico differ diff --git a/panda/src/device/analogNode.I b/panda/src/device/analogNode.I index f02b638603..a61ff00b43 100644 --- a/panda/src/device/analogNode.I +++ b/panda/src/device/analogNode.I @@ -35,10 +35,7 @@ is_valid() const { */ INLINE int AnalogNode:: get_num_controls() const { - _analog->acquire(); - int result = _analog->get_num_controls(); - _analog->unlock(); - return result; + return _analog->get_num_axes(); } /** @@ -48,10 +45,7 @@ get_num_controls() const { */ INLINE double AnalogNode:: get_control_state(int index) const { - _analog->acquire(); - double result = _analog->get_control_state(index); - _analog->unlock(); - return result; + return _analog->get_axis_value(index); } /** @@ -60,10 +54,7 @@ get_control_state(int index) const { */ INLINE bool AnalogNode:: is_control_known(int index) const { - _analog->acquire(); - bool result = _analog->is_control_known(index); - _analog->unlock(); - return result; + return _analog->is_axis_known(index); } /** diff --git a/panda/src/device/analogNode.cxx b/panda/src/device/analogNode.cxx index 59e57f4179..6f9b42811c 100644 --- a/panda/src/device/analogNode.cxx +++ b/panda/src/device/analogNode.cxx @@ -27,7 +27,7 @@ AnalogNode(ClientBase *client, const std::string &device_name) : DataNode(device_name) { _xy_output = define_output("xy", EventStoreVec2::get_class_type()); - _xy = new EventStoreVec2(LPoint2(0.0f, 0.0f)); + _xy = new EventStoreVec2(LPoint2(0)); nassertv(client != nullptr); PT(ClientDevice) device = @@ -46,7 +46,21 @@ AnalogNode(ClientBase *client, const std::string &device_name) : return; } - _analog = DCAST(ClientAnalogDevice, device); + _analog = device; +} + +/** + * + */ +AnalogNode:: +AnalogNode(InputDevice *device) : + DataNode(device->get_name()), + _analog(device) +{ + _xy_output = define_output("xy", EventStoreVec2::get_class_type()); + _xy = new EventStoreVec2(LPoint2(0)); + + nassertv(device != nullptr); } /** @@ -67,9 +81,7 @@ write(std::ostream &out, int indent_level) const { DataNode::write(out, indent_level); if (_analog != nullptr) { - _analog->acquire(); - _analog->write_controls(out, indent_level + 2); - _analog->unlock(); + _analog->write_axes(out, indent_level + 2); } } @@ -88,19 +100,18 @@ do_transmit_data(DataGraphTraverser *, const DataNodeTransmit &, _analog->poll(); LPoint2 out(0.0f, 0.0f); - - _analog->acquire(); for (int i = 0; i < max_outputs; i++) { - if (_outputs[i]._index >= 0 && - _analog->is_control_known(_outputs[i]._index)) { - if (_outputs[i]._flip) { - out[i] = -_analog->get_control_state(_outputs[i]._index); - } else { - out[i] = _analog->get_control_state(_outputs[i]._index); + if (_outputs[i]._index >= 0) { + InputDevice::AxisState state = _analog->get_axis(_outputs[i]._index); + if (state.known) { + if (_outputs[i]._flip) { + out[i] = -state.value; + } else { + out[i] = state.value; + } } } } - _analog->unlock(); _xy->set_value(out); output.set_data(_xy_output, EventParameter(_xy)); } diff --git a/panda/src/device/analogNode.h b/panda/src/device/analogNode.h index 11626e0ae8..2b522423d2 100644 --- a/panda/src/device/analogNode.h +++ b/panda/src/device/analogNode.h @@ -39,6 +39,7 @@ class EXPCL_PANDA_DEVICE AnalogNode : public DataNode { PUBLISHED: explicit AnalogNode(ClientBase *client, const std::string &device_name); + explicit AnalogNode(InputDevice *device); virtual ~AnalogNode(); INLINE bool is_valid() const; @@ -67,7 +68,7 @@ private: enum { max_outputs = 2 }; OutputData _outputs[max_outputs]; - PT(ClientAnalogDevice) _analog; + PT(InputDevice) _analog; protected: // Inherited from DataNode diff --git a/panda/src/device/buttonNode.I b/panda/src/device/buttonNode.I index c4ccc2c610..013f66e41d 100644 --- a/panda/src/device/buttonNode.I +++ b/panda/src/device/buttonNode.I @@ -17,7 +17,7 @@ */ INLINE bool ButtonNode:: is_valid() const { - return (_button != nullptr) && _button->is_connected(); + return (_device != nullptr) && _device->is_connected(); } /** @@ -28,10 +28,7 @@ is_valid() const { */ INLINE int ButtonNode:: get_num_buttons() const { - _button->acquire(); - int result = _button->get_num_buttons(); - _button->unlock(); - return result; + return _device->get_num_buttons(); } /** @@ -46,9 +43,7 @@ get_num_buttons() const { */ INLINE void ButtonNode:: set_button_map(int index, ButtonHandle button) { - _button->acquire(); - _button->set_button_map(index, button); - _button->unlock(); + _device->map_button(index, button); } /** @@ -58,10 +53,7 @@ set_button_map(int index, ButtonHandle button) { */ INLINE ButtonHandle ButtonNode:: get_button_map(int index) const { - _button->acquire(); - ButtonHandle result = _button->get_button_map(index); - _button->unlock(); - return result; + return _device->get_button_map(index); } /** @@ -70,10 +62,7 @@ get_button_map(int index) const { */ INLINE bool ButtonNode:: get_button_state(int index) const { - _button->acquire(); - bool result = _button->get_button_state(index); - _button->unlock(); - return result; + return _device->is_button_pressed(index); } /** @@ -82,8 +71,5 @@ get_button_state(int index) const { */ INLINE bool ButtonNode:: is_button_known(int index) const { - _button->acquire(); - bool result = _button->is_button_known(index); - _button->unlock(); - return result; + return _device->is_button_known(index); } diff --git a/panda/src/device/buttonNode.cxx b/panda/src/device/buttonNode.cxx index 67803cf658..c5f8242c01 100644 --- a/panda/src/device/buttonNode.cxx +++ b/panda/src/device/buttonNode.cxx @@ -27,7 +27,6 @@ ButtonNode(ClientBase *client, const std::string &device_name) : DataNode(device_name) { _button_events_output = define_output("button_events", ButtonEventList::get_class_type()); - _button_events = new ButtonEventList; nassertv(client != nullptr); PT(ClientDevice) device = @@ -46,7 +45,19 @@ ButtonNode(ClientBase *client, const std::string &device_name) : return; } - _button = DCAST(ClientButtonDevice, device); + _device = device; +} + +/** + * + */ +ButtonNode:: +ButtonNode(InputDevice *device) : + DataNode(device->get_name()), + _device(device) +{ + _button_events_output = define_output("button_events", ButtonEventList::get_class_type()); + _device = device; } /** @@ -66,11 +77,9 @@ void ButtonNode:: output(std::ostream &out) const { DataNode::output(out); - if (_button != nullptr) { + if (_device != nullptr) { out << " ("; - _button->acquire(); - _button->output_buttons(out); - _button->unlock(); + _device->output_buttons(out); out << ")"; } } @@ -82,10 +91,8 @@ void ButtonNode:: write(std::ostream &out, int indent_level) const { DataNode::write(out, indent_level); - if (_button != nullptr) { - _button->acquire(); - _button->write_buttons(out, indent_level + 2); - _button->unlock(); + if (_device != nullptr) { + _device->write_buttons(out, indent_level + 2); } } @@ -101,14 +108,7 @@ void ButtonNode:: do_transmit_data(DataGraphTraverser *, const DataNodeTransmit &, DataNodeTransmit &output) { if (is_valid()) { - _button->poll(); - _button->acquire(); - - (*_button_events) = (*_button->get_button_events()); - - _button->get_button_events()->clear(); - _button->unlock(); - - output.set_data(_button_events_output, EventParameter(_button_events)); + PT(ButtonEventList) bel = _device->get_button_events(); + output.set_data(_button_events_output, EventParameter(bel)); } } diff --git a/panda/src/device/buttonNode.h b/panda/src/device/buttonNode.h index 02b141fbbf..2739c440a7 100644 --- a/panda/src/device/buttonNode.h +++ b/panda/src/device/buttonNode.h @@ -19,8 +19,6 @@ #include "clientBase.h" #include "clientButtonDevice.h" #include "dataNode.h" -#include "buttonEventList.h" - /** * This is the primary interface to on/off button devices associated with a @@ -35,6 +33,7 @@ class EXPCL_PANDA_DEVICE ButtonNode : public DataNode { PUBLISHED: explicit ButtonNode(ClientBase *client, const std::string &device_name); + explicit ButtonNode(InputDevice *device); virtual ~ButtonNode(); INLINE bool is_valid() const; @@ -52,7 +51,7 @@ public: virtual void write(std::ostream &out, int indent_level = 0) const; private: - PT(ClientButtonDevice) _button; + PT(InputDevice) _device; protected: // Inherited from DataNode @@ -63,7 +62,6 @@ protected: private: // outputs int _button_events_output; - PT(ButtonEventList) _button_events; public: static TypeHandle get_class_type() { diff --git a/panda/src/device/clientAnalogDevice.I b/panda/src/device/clientAnalogDevice.I index d392d46aba..490eedb079 100644 --- a/panda/src/device/clientAnalogDevice.I +++ b/panda/src/device/clientAnalogDevice.I @@ -11,16 +11,6 @@ * @date 2001-01-26 */ -/** - * - */ -INLINE ClientAnalogDevice::AnalogState:: -AnalogState() : - _state(0.0), - _known(false) -{ -} - /** * */ @@ -29,53 +19,3 @@ ClientAnalogDevice(ClientBase *client, const std::string &device_name): ClientDevice(client, get_class_type(), device_name) { } - -/** - * Returns the number of analog controls known to the ClientAnalogDevice. - * This number may change as more controls are discovered. - */ -INLINE int ClientAnalogDevice:: -get_num_controls() const { - return _controls.size(); -} - -/** - * Sets the state of the indicated analog index. The caller should ensure - * that acquire() is in effect while this call is made. This should be a - * number in the range -1.0 to 1.0, representing the current position of the - * control within its total range of movement. - */ -INLINE void ClientAnalogDevice:: -set_control_state(int index, double state) { - ensure_control_index(index); - nassertv(index >= 0 && index < (int)_controls.size()); - _controls[index]._state = state; - _controls[index]._known = true; -} - -/** - * Returns the current position of indicated analog control (identified by its - * index number), or 0.0 if the control is unknown. The normal range of a - * single control is -1.0 to 1.0. - */ -INLINE double ClientAnalogDevice:: -get_control_state(int index) const { - if (index >= 0 && index < (int)_controls.size()) { - return _controls[index]._state; - } else { - return 0.0; - } -} - -/** - * Returns true if the state of the indicated analog control is known, or - * false if we have never heard anything about this particular control. - */ -INLINE bool ClientAnalogDevice:: -is_control_known(int index) const { - if (index >= 0 && index < (int)_controls.size()) { - return _controls[index]._known; - } else { - return false; - } -} diff --git a/panda/src/device/clientAnalogDevice.cxx b/panda/src/device/clientAnalogDevice.cxx index eb9c72ee06..f9eef0f884 100644 --- a/panda/src/device/clientAnalogDevice.cxx +++ b/panda/src/device/clientAnalogDevice.cxx @@ -17,50 +17,11 @@ TypeHandle ClientAnalogDevice::_type_handle; - - -/** - * Guarantees that there is a slot in the array for the indicated index - * number, by filling the array up to that index if necessary. - */ -void ClientAnalogDevice:: -ensure_control_index(int index) { - nassertv(index >= 0); - - _controls.reserve(index + 1); - while ((int)_controls.size() <= index) { - _controls.push_back(AnalogState()); - } -} - /** * */ void ClientAnalogDevice:: write(std::ostream &out, int indent_level) const { - indent(out, indent_level) << get_type() << " " << get_device_name() << ":\n"; - write_controls(out, indent_level + 2); -} - -/** - * Writes a multi-line description of the current analog control states. - */ -void ClientAnalogDevice:: -write_controls(std::ostream &out, int indent_level) const { - bool any_controls = false; - Controls::const_iterator ai; - for (ai = _controls.begin(); ai != _controls.end(); ++ai) { - const AnalogState &state = (*ai); - if (state._known) { - any_controls = true; - - indent(out, indent_level) - << (int)(ai - _controls.begin()) << ". " << state._state << "\n"; - } - } - - if (!any_controls) { - indent(out, indent_level) - << "(no known analog controls)\n"; - } + indent(out, indent_level) << get_type() << " " << get_name() << ":\n"; + write_axes(out, indent_level + 2); } diff --git a/panda/src/device/clientAnalogDevice.h b/panda/src/device/clientAnalogDevice.h index 436b399240..0db13d573f 100644 --- a/panda/src/device/clientAnalogDevice.h +++ b/panda/src/device/clientAnalogDevice.h @@ -31,30 +31,7 @@ protected: INLINE ClientAnalogDevice(ClientBase *client, const std::string &device_name); public: - INLINE int get_num_controls() const; - - INLINE void set_control_state(int index, double state); - INLINE double get_control_state(int index) const; - INLINE bool is_control_known(int index) const; - virtual void write(std::ostream &out, int indent_level = 0) const; - void write_controls(std::ostream &out, int indent_level) const; - -private: - void ensure_control_index(int index); - -protected: - class AnalogState { - public: - INLINE AnalogState(); - - double _state; - bool _known; - }; - - typedef pvector Controls; - Controls _controls; - public: static TypeHandle get_class_type() { diff --git a/panda/src/device/clientBase.cxx b/panda/src/device/clientBase.cxx index b7c6c3347c..a6f8bb3051 100644 --- a/panda/src/device/clientBase.cxx +++ b/panda/src/device/clientBase.cxx @@ -128,7 +128,6 @@ get_device(TypeHandle device_type, const std::string &device_name) { if (device != nullptr) { dbn.insert(DevicesByName::value_type(device_name, device)); - device->_is_connected = true; } return device; diff --git a/panda/src/device/clientButtonDevice.I b/panda/src/device/clientButtonDevice.I index 90aafce0c5..5b83d2947b 100644 --- a/panda/src/device/clientButtonDevice.I +++ b/panda/src/device/clientButtonDevice.I @@ -10,91 +10,3 @@ * @author drose * @date 2001-01-26 */ - -/** - * - */ -INLINE ClientButtonDevice::ButtonState:: -ButtonState() : - _handle(ButtonHandle::none()), - _state(S_unknown) -{ -} - - -/** - * Returns the number of buttons known to the ClientButtonDevice. This - * includes those buttons whose state has been seen, as well as buttons that - * have been associated with a ButtonHandle even if their state is unknown. - * This number may change as more buttons are discovered. - */ -INLINE int ClientButtonDevice:: -get_num_buttons() const { - return _buttons.size(); -} - -/** - * Associates the indicated ButtonHandle with the button of the indicated - * index number. When the given button index changes state, a corresponding - * ButtonEvent will be generated with the given ButtonHandle. Pass - * ButtonHandle::none() to turn off any association. - * - * It is not necessary to call this if you simply want to query the state of - * the various buttons by index number; this is only necessary in order to - * generate ButtonEvents when the buttons change state. - */ -INLINE void ClientButtonDevice:: -set_button_map(int index, ButtonHandle button) { - ensure_button_index(index); - nassertv(index >= 0 && index < (int)_buttons.size()); - _buttons[index]._handle = button; -} - -/** - * Returns the ButtonHandle that was previously associated with the given - * index number by a call to set_button_map(), or ButtonHandle::none() if no - * button was associated. - */ -INLINE ButtonHandle ClientButtonDevice:: -get_button_map(int index) const { - if (index >= 0 && index < (int)_buttons.size()) { - return _buttons[index]._handle; - } else { - return ButtonHandle::none(); - } -} - -/** - * Returns true if the indicated button (identified by its index number) is - * currently known to be down, or false if it is up or unknown. - */ -INLINE bool ClientButtonDevice:: -get_button_state(int index) const { - if (index >= 0 && index < (int)_buttons.size()) { - return (_buttons[index]._state == S_down); - } else { - return false; - } -} - -/** - * Returns true if the state of the indicated button is known, or false if we - * have never heard anything about this particular button. - */ -INLINE bool ClientButtonDevice:: -is_button_known(int index) const { - if (index >= 0 && index < (int)_buttons.size()) { - return _buttons[index]._state != S_unknown; - } else { - return false; - } -} - -/** - * Returns the list of recently-generated ButtonEvents. This must be - * periodically cleared, or the buttons will accumulate. - */ -INLINE ButtonEventList *ClientButtonDevice:: -get_button_events() const { - return _button_events; -} diff --git a/panda/src/device/clientButtonDevice.cxx b/panda/src/device/clientButtonDevice.cxx index 27451c2887..9ae2b4844c 100644 --- a/panda/src/device/clientButtonDevice.cxx +++ b/panda/src/device/clientButtonDevice.cxx @@ -26,41 +26,6 @@ ClientButtonDevice:: ClientButtonDevice(ClientBase *client, const std::string &device_name): ClientDevice(client, get_class_type(), device_name) { - _button_events = new ButtonEventList(); -} - - -/** - * Sets the state of the indicated button index, where true indicates down, - * and false indicates up. This may generate a ButtonEvent if the button has - * an associated ButtonHandle. The caller should ensure that acquire() is in - * effect while this call is made. - */ -void ClientButtonDevice:: -set_button_state(int index, bool down) { - ensure_button_index(index); - nassertv(index >= 0 && index < (int)_buttons.size()); - _buttons[index]._state = down ? S_down : S_up; - - ButtonHandle handle = _buttons[index]._handle; - if (handle != ButtonHandle::none()) { - _button_events->add_event(ButtonEvent(handle, down ? ButtonEvent::T_down : ButtonEvent::T_up)); - } -} - - -/** - * Guarantees that there is a slot in the array for the indicated index - * number, by filling the array up to that index if necessary. - */ -void ClientButtonDevice:: -ensure_button_index(int index) { - nassertv(index >= 0); - - _buttons.reserve(index + 1); - while ((int)_buttons.size() <= index) { - _buttons.push_back(ButtonState()); - } } /** @@ -68,7 +33,7 @@ ensure_button_index(int index) { */ void ClientButtonDevice:: output(ostream &out) const { - out << get_type() << " " << get_device_name() << " ("; + out << get_type() << " " << get_name() << " ("; output_buttons(out); out << ")"; } @@ -78,68 +43,6 @@ output(ostream &out) const { */ void ClientButtonDevice:: write(ostream &out, int indent_level) const { - indent(out, indent_level) << get_type() << " " << get_device_name() << ":\n"; + indent(out, indent_level) << get_type() << " " << get_name() << ":\n"; write_buttons(out, indent_level + 2); } - -/** - * Writes a one-line string of all of the current button states. - */ -void ClientButtonDevice:: -output_buttons(ostream &out) const { - bool any_buttons = false; - Buttons::const_iterator bi; - for (bi = _buttons.begin(); bi != _buttons.end(); ++bi) { - const ButtonState &state = (*bi); - if (state._state != S_unknown) { - if (any_buttons) { - out << ", "; - } - any_buttons = true; - out << (int)(bi - _buttons.begin()) << "="; - if (state._state == S_up) { - out << "up"; - } else { - out << "down"; - } - } - } - - if (!any_buttons) { - out << "no known buttons"; - } -} - -/** - * Writes a multi-line description of the current button states. - */ -void ClientButtonDevice:: -write_buttons(ostream &out, int indent_level) const { - bool any_buttons = false; - Buttons::const_iterator bi; - for (bi = _buttons.begin(); bi != _buttons.end(); ++bi) { - const ButtonState &state = (*bi); - if (state._state != S_unknown) { - any_buttons = true; - - indent(out, indent_level) - << (int)(bi - _buttons.begin()) << ". "; - - if (state._handle != ButtonHandle::none()) { - out << "(" << state._handle << ") "; - } - - if (state._state == S_up) { - out << "up"; - } else { - out << "down"; - } - out << "\n"; - } - } - - if (!any_buttons) { - indent(out, indent_level) - << "(no known buttons)\n"; - } -} diff --git a/panda/src/device/clientButtonDevice.h b/panda/src/device/clientButtonDevice.h index 3b089b707b..3e5b9c57df 100644 --- a/panda/src/device/clientButtonDevice.h +++ b/panda/src/device/clientButtonDevice.h @@ -34,46 +34,9 @@ protected: ClientButtonDevice(ClientBase *client, const std::string &device_name); public: - INLINE int get_num_buttons() const; - - INLINE void set_button_map(int index, ButtonHandle button); - INLINE ButtonHandle get_button_map(int index) const; - - void set_button_state(int index, bool down); - INLINE bool get_button_state(int index) const; - INLINE bool is_button_known(int index) const; - - INLINE ButtonEventList *get_button_events() const; - virtual void output(std::ostream &out) const; virtual void write(std::ostream &out, int indent_level = 0) const; - void output_buttons(std::ostream &out) const; - void write_buttons(std::ostream &out, int indent_level) const; - -private: - void ensure_button_index(int index); - -protected: - enum State { - S_unknown, - S_up, - S_down - }; - - class ButtonState { - public: - INLINE ButtonState(); - - ButtonHandle _handle; - State _state; - }; - - typedef pvector Buttons; - Buttons _buttons; - - PT(ButtonEventList) _button_events; - public: static TypeHandle get_class_type() { return _type_handle; @@ -90,7 +53,6 @@ public: private: static TypeHandle _type_handle; - friend class ButtonState; }; #include "clientButtonDevice.I" diff --git a/panda/src/device/clientDevice.I b/panda/src/device/clientDevice.I index 84edaf594f..5b4baba980 100644 --- a/panda/src/device/clientDevice.I +++ b/panda/src/device/clientDevice.I @@ -19,15 +19,6 @@ get_client() const { return _client; } -/** - * Returns true if the device is still connected to its ClientBase, false - * otherwise. - */ -INLINE bool ClientDevice:: -is_connected() const { - return _is_connected; -} - /** * Returns the type of device this is considered to be to the ClientBase: a * ClientTrackerDevice, ClientAnalogDevice, or what have you. This is not @@ -39,35 +30,3 @@ INLINE TypeHandle ClientDevice:: get_device_type() const { return _device_type; } - -/** - * Returns the device name reported to the ClientBase. This has some - * implementation-defined meaning to identify particular devices. - */ -INLINE const std::string &ClientDevice:: -get_device_name() const { - return _device_name; -} - -/** - * Grabs the mutex associated with this particular device. The device will - * not update asynchronously while the mutex is held, allowing the user to - * copy the data out without fear of getting a partial update during the copy. - */ -INLINE void ClientDevice:: -acquire() { -#ifdef OLD_HAVE_IPC - _lock.acquire(); -#endif -} - -/** - * Releases the mutex associated with this particular device. This should be - * called after all the data has been successfully copied out. See acquire(). - */ -INLINE void ClientDevice:: -unlock() { -#ifdef OLD_HAVE_IPC - _lock.unlock(); -#endif -} diff --git a/panda/src/device/clientDevice.cxx b/panda/src/device/clientDevice.cxx index 6da404eca4..278977f30b 100644 --- a/panda/src/device/clientDevice.cxx +++ b/panda/src/device/clientDevice.cxx @@ -24,14 +24,13 @@ TypeHandle ClientDevice::_type_handle; ClientDevice:: ClientDevice(ClientBase *client, TypeHandle device_type, const std::string &device_name) : + InputDevice(device_name, DeviceClass::unknown), _client(client), - _device_type(device_type), - _device_name(device_name) + _device_type(device_type) { // We have to explicitly ref the client pointer, since we can't use a // PT(ClientBase) for circular include reasons. _client->ref(); - _is_connected = false; } /** @@ -41,7 +40,7 @@ ClientDevice(ClientBase *client, TypeHandle device_type, */ ClientDevice:: ~ClientDevice() { - nassertv(!_is_connected); + nassertv(!is_connected()); // And now we explicitly unref the client pointer. unref_delete(_client); @@ -60,12 +59,10 @@ ClientDevice:: */ void ClientDevice:: disconnect() { - if (_is_connected) { - acquire(); + if (is_connected()) { bool disconnected = - _client->disconnect_device(_device_type, _device_name, this); - _is_connected = false; - unlock(); + _client->disconnect_device(_device_type, get_name(), this); + set_connected(false); nassertv(disconnected); } } @@ -79,7 +76,7 @@ disconnect() { * ClientDevice to ensure that it is fresh. */ void ClientDevice:: -poll() { +do_poll() { _client->poll(); } @@ -88,7 +85,7 @@ poll() { */ void ClientDevice:: output(std::ostream &out) const { - out << get_type() << " " << get_device_name(); + out << get_type() << " " << get_name(); } /** diff --git a/panda/src/device/clientDevice.h b/panda/src/device/clientDevice.h index 0e2ad03778..b77d707484 100644 --- a/panda/src/device/clientDevice.h +++ b/panda/src/device/clientDevice.h @@ -15,12 +15,7 @@ #define CLIENTDEVICE_H #include "pandabase.h" - -#include "typedReferenceCount.h" - -#ifdef OLD_HAVE_IPC -#include -#endif +#include "inputDevice.h" class ClientBase; @@ -29,7 +24,7 @@ class ClientBase; * ClientBase, including trackers, etc. This is an abstract interface; the * actual implementations are in ClientTrackerDevice, etc. */ -class EXPCL_PANDA_DEVICE ClientDevice : public TypedReferenceCount { +class EXPCL_PANDA_DEVICE ClientDevice : public InputDevice { protected: ClientDevice(ClientBase *client, TypeHandle device_type, const std::string &device_name); @@ -39,14 +34,10 @@ public: INLINE ClientBase *get_client() const; INLINE TypeHandle get_device_type() const; - INLINE const std::string &get_device_name() const; - INLINE bool is_connected() const; void disconnect(); - void poll(); - INLINE void acquire(); - INLINE void unlock(); + virtual void do_poll() final; virtual void output(std::ostream &out) const; virtual void write(std::ostream &out, int indent_level = 0) const; @@ -54,21 +45,15 @@ public: private: ClientBase *_client; TypeHandle _device_type; - std::string _device_name; - bool _is_connected; - -#ifdef OLD_HAVE_IPC - mutex _lock; -#endif public: static TypeHandle get_class_type() { return _type_handle; } static void init_type() { - TypedReferenceCount::init_type(); + InputDevice::init_type(); register_type(_type_handle, "ClientDevice", - TypedReferenceCount::get_class_type()); + InputDevice::get_class_type()); } virtual TypeHandle get_type() const { return get_class_type(); diff --git a/panda/src/device/clientTrackerDevice.I b/panda/src/device/clientTrackerDevice.I index f4be74fc4d..6ff99528e5 100644 --- a/panda/src/device/clientTrackerDevice.I +++ b/panda/src/device/clientTrackerDevice.I @@ -18,12 +18,5 @@ INLINE ClientTrackerDevice:: ClientTrackerDevice(ClientBase *client, const std::string &device_name): ClientDevice(client, get_class_type(), device_name) { -} - -/** - * Returns the TrackerData that this device is reporting. - */ -INLINE const TrackerData &ClientTrackerDevice:: -get_data() const { - return _data; + enable_feature(Feature::tracker); } diff --git a/panda/src/device/clientTrackerDevice.h b/panda/src/device/clientTrackerDevice.h index bd134a5e4c..22ff983fa0 100644 --- a/panda/src/device/clientTrackerDevice.h +++ b/panda/src/device/clientTrackerDevice.h @@ -27,12 +27,6 @@ class EXPCL_PANDA_DEVICE ClientTrackerDevice : public ClientDevice { protected: INLINE ClientTrackerDevice(ClientBase *client, const std::string &device_name); -public: - INLINE const TrackerData &get_data() const; - -protected: - TrackerData _data; - public: static TypeHandle get_class_type() { return _type_handle; diff --git a/panda/src/device/config_device.cxx b/panda/src/device/config_device.cxx index fb7f4d8928..f06c91422d 100644 --- a/panda/src/device/config_device.cxx +++ b/panda/src/device/config_device.cxx @@ -21,9 +21,12 @@ #include "clientDialDevice.h" #include "clientTrackerDevice.h" #include "dialNode.h" -#include "mouseAndKeyboard.h" +#include "evdevInputDevice.h" +#include "inputDevice.h" +#include "linuxJoystickDevice.h" #include "trackerNode.h" #include "virtualMouse.h" +#include "xInputDevice.h" #include "dconfig.h" @@ -64,7 +67,16 @@ init_libdevice() { ClientDialDevice::init_type(); ClientTrackerDevice::init_type(); DialNode::init_type(); - MouseAndKeyboard::init_type(); + InputDevice::init_type(); TrackerNode::init_type(); VirtualMouse::init_type(); + +#ifdef PHAVE_LINUX_INPUT_H + EvdevInputDevice::init_type(); + LinuxJoystickDevice::init_type(); +#endif + +#ifdef _WIN32 + XInputDevice::init_type(); +#endif } diff --git a/panda/src/device/config_device.h b/panda/src/device/config_device.h index 8a7b6542a7..0feb449b97 100644 --- a/panda/src/device/config_device.h +++ b/panda/src/device/config_device.h @@ -17,6 +17,7 @@ #include "pandabase.h" #include "notifyCategoryProxy.h" #include "configVariableBool.h" +#include "configVariableInt.h" NotifyCategoryDecl(device, EXPCL_PANDA_DEVICE, EXPTP_PANDA_DEVICE); diff --git a/panda/src/device/dialNode.I b/panda/src/device/dialNode.I index 1f6abdbc64..be59fbc019 100644 --- a/panda/src/device/dialNode.I +++ b/panda/src/device/dialNode.I @@ -26,10 +26,7 @@ is_valid() const { */ INLINE int DialNode:: get_num_dials() const { - _dial->acquire(); - int result = _dial->get_num_dials(); - _dial->unlock(); - return result; + return _dial->get_num_dials(); } /** @@ -39,10 +36,7 @@ get_num_dials() const { */ INLINE double DialNode:: read_dial(int index) { - _dial->acquire(); - double result = _dial->read_dial(index); - _dial->unlock(); - return result; + return _dial->read_dial(index); } /** @@ -51,8 +45,5 @@ read_dial(int index) { */ INLINE bool DialNode:: is_dial_known(int index) const { - _dial->acquire(); - bool result = _dial->is_dial_known(index); - _dial->unlock(); - return result; + return _dial->is_dial_known(index); } diff --git a/panda/src/awesomium/awWebViewListener.I b/panda/src/device/evdevInputDevice.I similarity index 81% rename from panda/src/awesomium/awWebViewListener.I rename to panda/src/device/evdevInputDevice.I index 10b7745aef..3e0884e193 100644 --- a/panda/src/awesomium/awWebViewListener.I +++ b/panda/src/device/evdevInputDevice.I @@ -6,7 +6,7 @@ * license. You should have received a copy of this license along * with this source code in a file named "LICENSE." * - * @file awWebViewListener.I - * @author rurbino - * @date 2009-10-12 + * @file evdevInputDevice.I + * @author rdb + * @date 2015-08-24 */ diff --git a/panda/src/device/evdevInputDevice.cxx b/panda/src/device/evdevInputDevice.cxx new file mode 100644 index 0000000000..b137888d6d --- /dev/null +++ b/panda/src/device/evdevInputDevice.cxx @@ -0,0 +1,1070 @@ +/** + * 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 evdevInputDevice.cxx + * @author rdb + * @date 2015-08-24 + */ + +#include "evdevInputDevice.h" + +#ifdef PHAVE_LINUX_INPUT_H + +#include "gamepadButton.h" +#include "keyboardButton.h" +#include "mouseButton.h" +#include "linuxInputDeviceManager.h" + +#include +#include + +#ifndef BTN_DPAD_UP +#define BTN_DPAD_UP 0x220 +#define BTN_DPAD_DOWN 0x221 +#define BTN_DPAD_LEFT 0x222 +#define BTN_DPAD_RIGHT 0x223 +#endif + + +// Android introduces these in API level 21. +#ifndef BTN_TRIGGER_HAPPY +#define BTN_TRIGGER_HAPPY 0x2c0 +#define BTN_TRIGGER_HAPPY1 0x2c0 +#define BTN_TRIGGER_HAPPY2 0x2c1 +#define BTN_TRIGGER_HAPPY3 0x2c2 +#define BTN_TRIGGER_HAPPY4 0x2c3 +#endif + +#define test_bit(bit, array) ((array)[(bit)>>3] & (1<<((bit)&7))) + +enum QuirkBits { + // Right stick uses Z and Rz inputs. + QB_rstick_from_z = 1, + + // Throttle goes from -1 to 1 rather than from 0 to 1. + QB_centered_throttle = 2, + + // Throttle is reversed. + QB_reversed_throttle = 4, + + // Only consider the device "connected" if all axes are non-zero. + QB_connect_if_nonzero = 8, + + // ABS_THROTTLE maps to rudder + QB_rudder_from_throttle = 16, + + // Special handling for Steam Controller, which has many peculiarities. + // We only connect it if it is reporting any events, because when Steam is + // running, the Steam controller is muted in favour of a dummy Xbox device. + QB_steam_controller = 32, +}; + +static const struct DeviceMapping { + unsigned short vendor; + unsigned short product; + InputDevice::DeviceClass device_class; + int quirks; +} mapping_presets[] = { + // NVIDIA Shield Controller + {0x0955, 0x7214, InputDevice::DeviceClass::gamepad, QB_rstick_from_z}, + // T.Flight Hotas X + {0x044f, 0xb108, InputDevice::DeviceClass::flight_stick, QB_centered_throttle | QB_reversed_throttle | QB_rudder_from_throttle}, + // Xbox 360 Wireless Controller + {0x045e, 0x0719, InputDevice::DeviceClass::gamepad, QB_connect_if_nonzero}, + // Steam Controller (wired) + {0x28de, 0x1102, InputDevice::DeviceClass::unknown, QB_steam_controller}, + // Steam Controller (wireless) + {0x28de, 0x1142, InputDevice::DeviceClass::unknown, QB_steam_controller}, + // Jess Tech Colour Rumble Pad + {0x0f30, 0x0111, InputDevice::DeviceClass::gamepad, 0}, + // Trust GXT 24 + {0x0079, 0x0006, InputDevice::DeviceClass::gamepad, 0}, + // 3Dconnexion Space Traveller 3D Mouse + {0x046d, 0xc623, InputDevice::DeviceClass::spatial_mouse, 0}, + // 3Dconnexion Space Pilot 3D Mouse + {0x046d, 0xc625, InputDevice::DeviceClass::spatial_mouse, 0}, + // 3Dconnexion Space Navigator 3D Mouse + {0x046d, 0xc626, InputDevice::DeviceClass::spatial_mouse, 0}, + // 3Dconnexion Space Explorer 3D Mouse + {0x046d, 0xc627, InputDevice::DeviceClass::spatial_mouse, 0}, + // 3Dconnexion Space Navigator for Notebooks + {0x046d, 0xc628, InputDevice::DeviceClass::spatial_mouse, 0}, + // 3Dconnexion SpacePilot Pro 3D Mouse + {0x046d, 0xc629, InputDevice::DeviceClass::spatial_mouse, 0}, + // 3Dconnexion Space Mouse Pro + {0x046d, 0xc62b, InputDevice::DeviceClass::spatial_mouse, 0}, + {0}, +}; + +TypeHandle EvdevInputDevice::_type_handle; + +/** + * Creates a new device representing the evdev device with the given index. + */ +EvdevInputDevice:: +EvdevInputDevice(LinuxInputDeviceManager *manager, size_t index) : + _manager(manager), + _index(index), + _fd(-1), + _can_write(false), + _ff_id(-1), + _ff_playing(false), + _ff_strong(-1), + _ff_weak(-1), + _dpad_x_axis(-1), + _dpad_y_axis(-1), + _dpad_left_button(-1), + _dpad_up_button(-1), + _ltrigger_code(-1), + _rtrigger_code(-1), + _quirks(0) { + + char path[64]; + sprintf(path, "/dev/input/event%zd", index); + + _fd = open(path, O_RDWR | O_NONBLOCK); + if (_fd >= 0) { + _can_write = true; + } else { + // On failure, open device as read-only. + _fd = open(path, O_RDONLY | O_NONBLOCK); + } + + if (_fd >= 0) { + init_device(); + } else { + _is_connected = false; + device_cat.error() + << "Opening raw input device: " << strerror(errno) << " " << path << "\n"; + } +} + +/** + * + */ +EvdevInputDevice:: +~EvdevInputDevice() { + if (_fd != -1) { + if (_ff_id != -1) { + // Remove force-feedback effect. + do_set_vibration(0, 0); + ioctl(_fd, EVIOCRMFF, _ff_id); + _ff_id = -1; + } + + close(_fd); + _fd = -1; + } +} + +/** + * Sets the vibration strength. The first argument controls a low-frequency + * motor, if present, and the latter controls a high-frequency motor. + * The values are within the 0-1 range. + */ +void EvdevInputDevice:: +do_set_vibration(double strong, double weak) { + if (_fd == -1 || !_can_write) { + return; + } + + int strong_level = strong * 0xffff; + int weak_level = weak * 0xffff; + + if (strong_level == _ff_strong && weak_level == _ff_weak) { + // No change. + return; + } + + // Upload the new effect parameters. Do this even if we are about + // to stop the effect, because some drivers don't respond to simply + // stopping the effect. + struct ff_effect effect; + effect.type = FF_RUMBLE; + effect.id = _ff_id; + effect.direction = 0; + effect.trigger.button = 0; + effect.trigger.interval = 0; + effect.replay.length = 0; + effect.replay.delay = 0; + effect.u.rumble.strong_magnitude = strong_level; + effect.u.rumble.weak_magnitude = weak_level; + + if (ioctl(_fd, EVIOCSFF, &effect) < 0) { + return; + } else { + _ff_id = effect.id; + _ff_strong = strong_level; + _ff_weak = weak_level; + } + + if (!_ff_playing) { + // Start the effect. We could pass 0 as value to stop the effect + // when a level of 0 is requested, but my driver seems to ignore it. + _ff_playing = true; + + struct input_event play; + play.type = EV_FF; + play.code = _ff_id; + play.value = 1; + + if (write(_fd, &play, sizeof(play)) < 0) { + device_cat.warning() + << "Failed to write force-feedback event: " << strerror(errno) << "\n"; + } + } +} + +/** + * Special case for Steam controllers; called if a Steam virtual device has + * just been disconnected, and this is currently an inactive Steam Controller + * previously blocked by Steam, waiting to be reactivated. + * Returns true if the device has just been reconnected. + */ +bool EvdevInputDevice:: +reactivate_steam_controller() { + LightMutexHolder holder(_lock); + if (!_is_connected && (_quirks & QB_steam_controller) != 0) { + // Just check to make sure the device is still readable. + process_events(); + if (_fd != -1) { + _is_connected = true; + return true; + } + } + return false; +} + +/** + * Polls the input device for new activity, to ensure it contains the latest + * events. This will only have any effect for some types of input devices; + * others may be updated automatically, and this method will be a no-op. + */ +void EvdevInputDevice:: +do_poll() { + if (_fd != -1 && process_events()) { + while (process_events()) {} + + // If we got events, we are obviously connected. Mark us so. + if (!_is_connected && _fd != -1) { + _is_connected = true; + if (_manager != nullptr) { + _manager->add_device(this); + } + } + } +} + +/** + * Reads basic properties from the device. + */ +bool EvdevInputDevice:: +init_device() { + using std::string; + + nassertr(_fd >= 0, false); + + LightMutexHolder holder(_lock); + + uint8_t evtypes[(EV_MAX + 8) >> 3] = {0}; + char name[128]; + if (ioctl(_fd, EVIOCGNAME(sizeof(name)), name) < 0 || + ioctl(_fd, EVIOCGBIT(0, sizeof(evtypes)), evtypes) < 0) { + close(_fd); + _fd = -1; + _is_connected = false; + device_cat.error() << "Opening raw input device: ioctl failed\n"; + return false; + } + + _name.assign(name); + //cerr << "##### Now initializing device " << name << "\n"; + + struct input_id id; + if (ioctl(_fd, EVIOCGID, &id) >= 0) { + _vendor_id = id.vendor; + _product_id = id.product; + } + + bool all_values_zero = true; + bool emulate_dpad = true; + bool have_analog_triggers = false; + + bool has_keys = false; + bool has_axes = false; + + uint8_t keys[(KEY_MAX + 8) >> 3] = {0}; + if (test_bit(EV_KEY, evtypes)) { + // Check which buttons are on the device. + ioctl(_fd, EVIOCGBIT(EV_KEY, sizeof(keys)), keys); + has_keys = true; + + if (test_bit(KEY_A, keys) && test_bit(KEY_Z, keys)) { + enable_feature(Feature::keyboard); + } + } + + int num_bits = 0; + uint8_t axes[(ABS_MAX + 8) >> 3] = {0}; + if (test_bit(EV_ABS, evtypes)) { + // Check which axes are on the device. + num_bits = ioctl(_fd, EVIOCGBIT(EV_ABS, sizeof(axes)), axes) << 3; + has_axes = true; + } + + // Do we have a preset device mapping? + int quirks = 0; + const DeviceMapping *mapping = mapping_presets; + while (mapping->vendor != 0) { + if (_vendor_id == mapping->vendor && _product_id == mapping->product) { + _device_class = mapping->device_class; + quirks = mapping->quirks; + break; + } + ++mapping; + } + + // The Steam Controller reports as multiple devices, one of which a gamepad. + if (quirks & QB_steam_controller) { + if (test_bit(BTN_GAMEPAD, keys)) { + _device_class = DeviceClass::gamepad; + + // If we have a virtual gamepad on the system, then careful: if Steam is + // running, it may disable its own gamepad in favour of the virtual + // device it registers. If the virtual device is present, we will only + // register this gamepad as connected when it registers input. + if (_manager->has_virtual_device(0x28de, 0x11ff)) { + device_cat.debug() + << "Detected Steam virtual gamepad, disabling Steam Controller\n"; + quirks |= QB_connect_if_nonzero; + } + } + } + _quirks = quirks; + + // Try to detect which type of device we have here + if (_device_class == DeviceClass::unknown) { + int device_scores[(size_t)DeviceClass::spatial_mouse] = {0}; + + // Test for specific keys + if (test_bit(BTN_GAMEPAD, keys) && test_bit(ABS_X, axes) && test_bit(ABS_RX, axes)) { + device_scores[(size_t)DeviceClass::gamepad] += 5; + device_scores[(size_t)DeviceClass::steering_wheel] += 5; + device_scores[(size_t)DeviceClass::flight_stick] += 5; + } + + if (test_bit(ABS_WHEEL, axes) && test_bit(ABS_GAS, axes) && test_bit(ABS_BRAKE, axes)) { + device_scores[(size_t)DeviceClass::steering_wheel] += 10; + } + if (test_bit(BTN_GEAR_DOWN, keys) && test_bit(BTN_GEAR_UP, keys)) { + device_scores[(size_t)DeviceClass::steering_wheel] += 10; + } + if (test_bit(BTN_JOYSTICK, keys) && test_bit(ABS_X, axes)) { + device_scores[(size_t)DeviceClass::flight_stick] += 10; + } + if (test_bit(BTN_MOUSE, keys) && test_bit(EV_REL, evtypes)) { + device_scores[(size_t)DeviceClass::mouse] += 20; + } + uint8_t unknown_keys[] = {KEY_POWER}; + for (int i = 0; i < 1; i++) { + if (test_bit(unknown_keys[i], keys)) { + if (unknown_keys[i] == KEY_POWER) { + } + device_scores[(size_t)DeviceClass::unknown] += 20; + } + } + if (_features & (unsigned int)Feature::keyboard) { + device_scores[(size_t)DeviceClass::keyboard] += 20; + } + + // Test for specific name tags + string lowercase_name = _name; + for(size_t x = 0; x < _name.length(); ++x) { + lowercase_name[x] = tolower(lowercase_name[x]); + } + if (lowercase_name.find("gamepad") != string::npos) { + device_scores[(size_t)DeviceClass::gamepad] += 10; + } + if (lowercase_name.find("wheel") != string::npos) { + device_scores[(size_t)DeviceClass::steering_wheel] += 10; + } + if (lowercase_name.find("mouse") != string::npos || lowercase_name.find("touchpad") != string::npos) { + device_scores[(size_t)DeviceClass::mouse] += 10; + } + if (lowercase_name.find("keyboard") != string::npos) { + device_scores[(size_t)DeviceClass::keyboard] += 10; + } + // List of lowercase names that occur in unknown devices + string unknown_names[] = {"video bus", "power button", "sleep button"}; + for(int i = 0; i < 3; i++) { + if (lowercase_name.find(unknown_names[i]) != string::npos) { + device_scores[(size_t)DeviceClass::unknown] += 20; + } + } + + // Check which device type got the most points + int highest_score = 0; + for (size_t i = 0; i < (size_t)DeviceClass::spatial_mouse; i++) { + if (device_scores[i] > highest_score) { + highest_score = device_scores[i]; + _device_class = (DeviceClass)i; + } + } + //std::cerr << "Found highscore class " << _device_class << " with this score: " << highest_score << "\n"; + } + + if (has_keys) { + // Also check whether the buttons are currently pressed. + uint8_t states[(KEY_MAX + 8) >> 3] = {0}; + ioctl(_fd, EVIOCGKEY(sizeof(states)), states); + + for (int i = 0; i <= KEY_MAX; ++i) { + if (test_bit(i, keys)) { + ButtonState button; + button.handle = map_button(i, _device_class, quirks); + + int button_index = (int)_buttons.size(); + if (button.handle == ButtonHandle::none()) { + if (device_cat.is_debug()) { + device_cat.debug() << "Unmapped /dev/input/event" << _index + << " button " << button_index << ": 0x" << std::hex << i << std::dec << "\n"; + } + } + + if (test_bit(i, states)) { + button._state = S_down; + all_values_zero = false; + } else { + button._state = S_up; + } + if (button.handle == GamepadButton::dpad_left()) { + emulate_dpad = false; + } else if (button.handle == GamepadButton::ltrigger()) { + _ltrigger_code = i; + } else if (button.handle == GamepadButton::rtrigger()) { + _rtrigger_code = i; + } + + _buttons.push_back(button); + if ((size_t)i >= _button_indices.size()) { + _button_indices.resize(i + 1, -1); + } + _button_indices[i] = button_index; + } + } + } + + if (has_axes) { + _axis_indices.resize(num_bits, -1); + + for (int i = 0; i < num_bits; ++i) { + if (test_bit(i, axes)) { + Axis axis = Axis::none; + switch (i) { + case ABS_X: + if (_device_class == DeviceClass::gamepad) { + axis = InputDevice::Axis::left_x; + } else if (_device_class == DeviceClass::flight_stick) { + axis = InputDevice::Axis::roll; + } else { + axis = InputDevice::Axis::x; + } + break; + case ABS_Y: + if (_device_class == DeviceClass::gamepad) { + axis = InputDevice::Axis::left_y; + } else if (_device_class == DeviceClass::flight_stick) { + axis = InputDevice::Axis::pitch; + } else { + axis = InputDevice::Axis::y; + } + break; + case ABS_Z: + if (quirks & QB_rstick_from_z) { + axis = InputDevice::Axis::right_x; + } else if (_device_class == DeviceClass::gamepad) { + axis = InputDevice::Axis::left_trigger; + have_analog_triggers = true; + } else if (_device_class == DeviceClass::spatial_mouse) { + axis = InputDevice::Axis::z; + } else { + axis = InputDevice::Axis::throttle; + } + break; + case ABS_RX: + if (_device_class == DeviceClass::spatial_mouse) { + axis = InputDevice::Axis::pitch; + } else if ((quirks & QB_rstick_from_z) == 0) { + axis = InputDevice::Axis::right_x; + } + break; + case ABS_RY: + if (_device_class == DeviceClass::spatial_mouse) { + axis = InputDevice::Axis::roll; + } else if ((quirks & QB_rstick_from_z) == 0) { + axis = InputDevice::Axis::right_y; + } + break; + case ABS_RZ: + if (quirks & QB_rstick_from_z) { + axis = InputDevice::Axis::right_y; + } else if (_device_class == DeviceClass::gamepad) { + axis = InputDevice::Axis::right_trigger; + have_analog_triggers = true; + } else { + axis = InputDevice::Axis::yaw; + } + break; + case ABS_THROTTLE: + if (quirks & QB_rudder_from_throttle) { + axis = InputDevice::Axis::rudder; + } else { + axis = InputDevice::Axis::throttle; + } + break; + case ABS_RUDDER: + axis = InputDevice::Axis::rudder; + break; + case ABS_WHEEL: + axis = InputDevice::Axis::wheel; + break; + case ABS_GAS: + if (_device_class == DeviceClass::gamepad) { + axis = InputDevice::Axis::right_trigger; + have_analog_triggers = true; + } else { + axis = InputDevice::Axis::accelerator; + } + break; + case ABS_BRAKE: + if (_device_class == DeviceClass::gamepad) { + axis = InputDevice::Axis::left_trigger; + have_analog_triggers = true; + } else { + axis = InputDevice::Axis::brake; + } + break; + case ABS_HAT0X: + if (emulate_dpad) { + _dpad_x_axis = i; + _dpad_left_button = (int)_buttons.size(); + if (_device_class == DeviceClass::gamepad) { + _buttons.push_back(ButtonState(GamepadButton::dpad_left())); + _buttons.push_back(ButtonState(GamepadButton::dpad_right())); + } else { + _buttons.push_back(ButtonState(GamepadButton::hat_left())); + _buttons.push_back(ButtonState(GamepadButton::hat_right())); + } + } + break; + case ABS_HAT0Y: + if (emulate_dpad) { + _dpad_y_axis = i; + _dpad_up_button = (int)_buttons.size(); + if (_device_class == DeviceClass::gamepad) { + _buttons.push_back(ButtonState(GamepadButton::dpad_up())); + _buttons.push_back(ButtonState(GamepadButton::dpad_down())); + } else { + _buttons.push_back(ButtonState(GamepadButton::hat_up())); + _buttons.push_back(ButtonState(GamepadButton::hat_down())); + } + } + break; + case ABS_HAT2X: + if (quirks & QB_steam_controller) { + axis = InputDevice::Axis::right_trigger; + have_analog_triggers = true; + } + break; + case ABS_HAT2Y: + if (quirks & QB_steam_controller) { + axis = InputDevice::Axis::left_trigger; + have_analog_triggers = true; + } + break; + } + + // Check the initial value and ranges. + struct input_absinfo absinfo; + if (ioctl(_fd, EVIOCGABS(i), &absinfo) >= 0) { + int index; + // We'd like to reverse the Y axis to match the XInput behavior. + // Also reverse the yaw axis to match right-hand coordinate system. + // Also T.Flight Hotas X throttle is reversed and can go backwards. + if (axis == Axis::yaw || axis == Axis::rudder || axis == Axis::left_y || axis == Axis::right_y || + (axis == Axis::throttle && (quirks & QB_reversed_throttle) != 0) || + (_device_class == DeviceClass::spatial_mouse && (axis == Axis::y || axis == Axis::z || axis == Axis::roll))) { + std::swap(absinfo.maximum, absinfo.minimum); + } + if (axis == Axis::throttle && (quirks & QB_centered_throttle) != 0) { + index = add_axis(axis, absinfo.minimum, absinfo.maximum, true); + } else { + index = add_axis(axis, absinfo.minimum, absinfo.maximum); + } + axis_changed(index, absinfo.value); + _axis_indices[i] = index; + + if (absinfo.value != 0) { + all_values_zero = false; + } + } + } + } + } + + if (test_bit(EV_REL, evtypes)) { + enable_feature(Feature::pointer); + add_pointer(PointerType::unknown, 0); + } + + if (test_bit(EV_FF, evtypes)) { + uint8_t effects[(FF_MAX + 8) >> 3] = {0}; + ioctl(_fd, EVIOCGBIT(EV_FF, sizeof(effects)), effects); + + if (test_bit(FF_RUMBLE, effects)) { + if (_can_write) { + enable_feature(Feature::vibration); + } else { + // Let the user know what he's missing out on. + device_cat.warning() + << "/dev/input/event" << _index << " is not writable, vibration " + << "effects will be unavailable.\n"; + } + } + } + + if (_ltrigger_code >= 0 && _rtrigger_code >= 0 && !have_analog_triggers) { + // Emulate analog triggers. + _ltrigger_axis = (int)_axes.size(); + add_axis(Axis::left_trigger, 0, 1, false); + add_axis(Axis::right_trigger, 0, 1, false); + } else { + _ltrigger_code = -1; + _rtrigger_code = -1; + } + + char path[64]; + char buffer[256]; + const char *parent = ""; + sprintf(path, "/sys/class/input/event%zd/device/device/../product", _index); + FILE *f = fopen(path, "r"); + if (!f) { + parent = "../"; + sprintf(path, "/sys/class/input/event%zd/device/device/%s../product", _index, parent); + f = fopen(path, "r"); + } + if (f) { + if (fgets(buffer, sizeof(buffer), f) != nullptr) { + buffer[strcspn(buffer, "\r\n")] = 0; + if (buffer[0] != 0) { + _name.assign(buffer); + } + } + fclose(f); + } + sprintf(path, "/sys/class/input/event%zd/device/device/%s../manufacturer", _index, parent); + f = fopen(path, "r"); + if (f) { + if (fgets(buffer, sizeof(buffer), f) != nullptr) { + buffer[strcspn(buffer, "\r\n")] = 0; + _manufacturer.assign(buffer); + } + fclose(f); + } + sprintf(path, "/sys/class/input/event%zd/device/device/%s../serial", _index, parent); + f = fopen(path, "r"); + if (f) { + if (fgets(buffer, sizeof(buffer), f) != nullptr) { + buffer[strcspn(buffer, "\r\n")] = 0; + _serial_number.assign(buffer); + } + fclose(f); + } + + // Special-case fix for Xbox 360 Wireless Receiver: the Linux kernel + // driver always reports 4 connected gamepads, regardless of the number + // of gamepads actually present. This hack partially remedies this. + if (all_values_zero && (quirks & QB_connect_if_nonzero) != 0) { + _is_connected = false; + } else { + _is_connected = true; + } + return true; +} + +/** + * Reads a number of events from the device. Returns true if events were read, + * meaning this function should keep being called until it returns false. + */ +bool EvdevInputDevice:: +process_events() { + // Read 8 events at a time. + struct input_event events[8]; + + int n_read = read(_fd, events, sizeof(events)); + if (n_read < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + // No data available for now. + + } else if (errno == ENODEV || errno == EINVAL) { + // The device ceased to exist, so we better close it. No need + // to worry about removing it from the InputDeviceManager, as it + // will get an inotify event sooner or later about this. + close(_fd); + _fd = -1; + //_is_connected = false; + errno = 0; + + } else { + device_cat.error() << "read: " << strerror(errno) << "\n"; + } + return false; + } + + if (n_read == 0) { + return false; + } + + n_read /= sizeof(struct input_event); + + int rel_x = 0; + int rel_y = 0; + double time = ClockObject::get_global_clock()->get_frame_time(); + int index; + + // It seems that some devices send a single EV_SYN event when being + // unplugged. Boo. Ignore it. + if (n_read == 1 && events[0].code == EV_SYN) { + return false; + } + + for (int i = 0; i < n_read; ++i) { + int code = events[i].code; + + switch (events[i].type) { + case EV_SYN: + break; + + case EV_REL: + if (code == REL_X) rel_x += events[i].value; + if (code == REL_Y) rel_y += events[i].value; + break; + + case EV_ABS: + if (code == _dpad_x_axis) { + button_changed(_dpad_left_button, events[i].value < 0); + button_changed(_dpad_left_button+1, events[i].value > 0); + } else if (code == _dpad_y_axis) { + button_changed(_dpad_up_button, events[i].value < 0); + button_changed(_dpad_up_button+1, events[i].value > 0); + } + nassertd(code >= 0 && (size_t)code < _axis_indices.size()) break; + index = _axis_indices[code]; + if (index >= 0) { + axis_changed(index, events[i].value); + } + break; + + case EV_KEY: + nassertd(code >= 0 && (size_t)code < _button_indices.size()) break; + index = _button_indices[code]; + if (index >= 0) { + button_changed(index, events[i].value != 0); + } + if (code == _ltrigger_code) { + axis_changed(_ltrigger_axis, events[i].value); + } else if (code == _rtrigger_code) { + axis_changed(_ltrigger_axis + 1, events[i].value); + } + break; + + default: + //cerr << "event " << events[i].type << " - " << events[i].code << " - " << events[i].value << "\n"; + break; + } + } + + if (rel_x != 0 || rel_y != 0) { + pointer_moved(0, rel_x, rel_y, time); + } + + return true; +} + +/** + * Static function to map an evdev code to a ButtonHandle. + */ +ButtonHandle EvdevInputDevice:: +map_button(int code, DeviceClass device_class, int quirks) { + if (code >= 0 && code < 0x80) { + // See linux/input.h for the source of this mapping. + static const ButtonHandle keyboard_map[] = { + ButtonHandle::none(), + KeyboardButton::escape(), + KeyboardButton::ascii_key('1'), + KeyboardButton::ascii_key('2'), + KeyboardButton::ascii_key('3'), + KeyboardButton::ascii_key('4'), + KeyboardButton::ascii_key('5'), + KeyboardButton::ascii_key('6'), + KeyboardButton::ascii_key('7'), + KeyboardButton::ascii_key('8'), + KeyboardButton::ascii_key('9'), + KeyboardButton::ascii_key('0'), + KeyboardButton::ascii_key('-'), + KeyboardButton::ascii_key('='), + KeyboardButton::backspace(), + KeyboardButton::tab(), + KeyboardButton::ascii_key('q'), + KeyboardButton::ascii_key('w'), + KeyboardButton::ascii_key('e'), + KeyboardButton::ascii_key('r'), + KeyboardButton::ascii_key('t'), + KeyboardButton::ascii_key('y'), + KeyboardButton::ascii_key('u'), + KeyboardButton::ascii_key('i'), + KeyboardButton::ascii_key('o'), + KeyboardButton::ascii_key('p'), + KeyboardButton::ascii_key('['), + KeyboardButton::ascii_key(']'), + KeyboardButton::enter(), + KeyboardButton::lcontrol(), + KeyboardButton::ascii_key('a'), + KeyboardButton::ascii_key('s'), + KeyboardButton::ascii_key('d'), + KeyboardButton::ascii_key('f'), + KeyboardButton::ascii_key('g'), + KeyboardButton::ascii_key('h'), + KeyboardButton::ascii_key('j'), + KeyboardButton::ascii_key('k'), + KeyboardButton::ascii_key('l'), + KeyboardButton::ascii_key(';'), + KeyboardButton::ascii_key('\''), + KeyboardButton::ascii_key('`'), + KeyboardButton::lshift(), + KeyboardButton::ascii_key('\\'), + KeyboardButton::ascii_key('z'), + KeyboardButton::ascii_key('x'), + KeyboardButton::ascii_key('c'), + KeyboardButton::ascii_key('v'), + KeyboardButton::ascii_key('b'), + KeyboardButton::ascii_key('n'), + KeyboardButton::ascii_key('m'), + KeyboardButton::ascii_key(','), + KeyboardButton::ascii_key('.'), + KeyboardButton::ascii_key('/'), + KeyboardButton::rshift(), + KeyboardButton::ascii_key('*'), + KeyboardButton::lalt(), + KeyboardButton::space(), + KeyboardButton::caps_lock(), + KeyboardButton::f1(), + KeyboardButton::f2(), + KeyboardButton::f3(), + KeyboardButton::f4(), + KeyboardButton::f5(), + KeyboardButton::f6(), + KeyboardButton::f7(), + KeyboardButton::f8(), + KeyboardButton::f9(), + KeyboardButton::f10(), + KeyboardButton::num_lock(), + KeyboardButton::scroll_lock(), + KeyboardButton::ascii_key('7'), + KeyboardButton::ascii_key('8'), + KeyboardButton::ascii_key('9'), + KeyboardButton::ascii_key('-'), + KeyboardButton::ascii_key('4'), + KeyboardButton::ascii_key('5'), + KeyboardButton::ascii_key('6'), + KeyboardButton::ascii_key('+'), + KeyboardButton::ascii_key('1'), + KeyboardButton::ascii_key('2'), + KeyboardButton::ascii_key('3'), + KeyboardButton::ascii_key('0'), + KeyboardButton::ascii_key('.'), + ButtonHandle::none(), + ButtonHandle::none(), + ButtonHandle::none(), + KeyboardButton::f11(), + KeyboardButton::f12(), + ButtonHandle::none(), + ButtonHandle::none(), + ButtonHandle::none(), + ButtonHandle::none(), + ButtonHandle::none(), + ButtonHandle::none(), + ButtonHandle::none(), + KeyboardButton::enter(), + KeyboardButton::rcontrol(), + KeyboardButton::ascii_key('/'), + KeyboardButton::print_screen(), + KeyboardButton::ralt(), + ButtonHandle::none(), + KeyboardButton::home(), + KeyboardButton::up(), + KeyboardButton::page_up(), + KeyboardButton::left(), + KeyboardButton::right(), + KeyboardButton::end(), + KeyboardButton::down(), + KeyboardButton::page_down(), + KeyboardButton::insert(), + KeyboardButton::del(), + ButtonHandle::none(), + ButtonHandle::none(), + ButtonHandle::none(), + ButtonHandle::none(), + ButtonHandle::none(), + ButtonHandle::none(), + ButtonHandle::none(), + KeyboardButton::pause(), + ButtonHandle::none(), + ButtonHandle::none(), + ButtonHandle::none(), + ButtonHandle::none(), + ButtonHandle::none(), + KeyboardButton::lmeta(), + KeyboardButton::rmeta(), + KeyboardButton::menu(), + }; + return keyboard_map[code]; + + } else if (code == KEY_BACK) { + // Used by NVIDIA Shield Controller + return GamepadButton::back(); + + } else if (code == KEY_SEARCH) { + // Used by NVIDIA Shield Controller + return GamepadButton::guide(); + + } else if (code < 0x100) { + return ButtonHandle::none(); + + } else if ((code & 0xfff0) == BTN_MOUSE) { + // The number for these is reversed in Panda. + if (code == BTN_RIGHT) { + return MouseButton::three(); + } else if (code == BTN_MIDDLE) { + return MouseButton::two(); + } else { + return MouseButton::button(code - BTN_MOUSE); + } + + } else if ((code & 0xfff0) == BTN_JOYSTICK) { + if (quirks & QB_steam_controller) { + // BTN_THUMB and BTN_THUMB2 detect touching the touchpads. + return ButtonHandle::none(); + + } else if (device_class == DeviceClass::gamepad) { + // Based on "Jess Tech Colour Rumble Pad" + static const ButtonHandle mapping[] = { + GamepadButton::face_x(), + GamepadButton::face_y(), + GamepadButton::face_a(), + GamepadButton::face_b(), + GamepadButton::lshoulder(), + GamepadButton::ltrigger(), + GamepadButton::rshoulder(), + GamepadButton::rtrigger(), + GamepadButton::back(), + GamepadButton::start(), + GamepadButton::lstick(), + GamepadButton::rstick(), + }; + if ((code & 0xf) < 12) { + return mapping[code & 0xf]; + } + } else { + return GamepadButton::joystick(code & 0xf); + } + } + + switch (code) { + case BTN_A: + return GamepadButton::face_a(); + + case BTN_B: + return GamepadButton::face_b(); + + case BTN_C: + return GamepadButton::face_c(); + + case BTN_X: + return GamepadButton::face_x(); + + case BTN_Y: + return GamepadButton::face_y(); + + case BTN_Z: + return GamepadButton::face_z(); + + case BTN_TL: + return GamepadButton::lshoulder(); + + case BTN_TR: + return GamepadButton::rshoulder(); + + case BTN_TL2: + return GamepadButton::ltrigger(); + + case BTN_TR2: + return GamepadButton::rtrigger(); + + case BTN_1: + return GamepadButton::face_1(); + + case BTN_2: + return GamepadButton::face_2(); + + case BTN_SELECT: + case KEY_PREVIOUS: + return GamepadButton::back(); + + case BTN_START: + case KEY_NEXT: + return GamepadButton::start(); + + case BTN_MODE: + return GamepadButton::guide(); + + case BTN_THUMBL: + return GamepadButton::lstick(); + + case BTN_THUMBR: + return GamepadButton::rstick(); + + case BTN_DPAD_LEFT: + case BTN_TRIGGER_HAPPY1: + return GamepadButton::dpad_left(); + + case BTN_DPAD_RIGHT: + case BTN_TRIGGER_HAPPY2: + return GamepadButton::dpad_right(); + + case BTN_DPAD_UP: + case BTN_TRIGGER_HAPPY3: + return GamepadButton::dpad_up(); + + case BTN_DPAD_DOWN: + case BTN_TRIGGER_HAPPY4: + return GamepadButton::dpad_down(); + + // The next two are for the Steam Controller's grip buttons. + case BTN_GEAR_DOWN: + return GamepadButton::lgrip(); + + case BTN_GEAR_UP: + return GamepadButton::rgrip(); + + default: + return ButtonHandle::none(); + } +} + +#endif diff --git a/panda/src/device/evdevInputDevice.h b/panda/src/device/evdevInputDevice.h new file mode 100644 index 0000000000..ffc3503ff0 --- /dev/null +++ b/panda/src/device/evdevInputDevice.h @@ -0,0 +1,96 @@ +/** + * 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 evdevInputDevice.h + * @author rdb + * @date 2015-08-24 + */ + +#ifndef EVDEVINPUTDEVICE_H +#define EVDEVINPUTDEVICE_H + +#include "inputDevice.h" + +#ifdef PHAVE_LINUX_INPUT_H + +class LinuxInputDeviceManager; + +/** + * This is a type of device that uses the Linux /dev/input/event# API to read + * data from a raw mouse or other input device. Unlike the joystick API, the + * evdev API supports sending force feedback (ie. rumble) events. + */ +class EXPCL_PANDA_DEVICE EvdevInputDevice : public InputDevice { +public: + EvdevInputDevice(LinuxInputDeviceManager *manager, size_t index); + virtual ~EvdevInputDevice(); + + bool reactivate_steam_controller(); + +private: + virtual void do_set_vibration(double strong, double weak); + virtual void do_poll(); + + bool init_device(); + bool process_events(); + +private: + LinuxInputDeviceManager *_manager; + + int _fd; + int _quirks; + size_t _index; + + bool _can_write; + int _ff_id; + bool _ff_playing; + int _ff_strong; + int _ff_weak; + + pvector _axis_indices; + pvector _button_indices; + + // These are used for D-pad emulation. + int _dpad_x_axis; + int _dpad_y_axis; + int _dpad_left_button; + int _dpad_up_button; + + // This is used for axis emulation. + int _ltrigger_axis; + int _ltrigger_code; + int _rtrigger_code; + +public: + static ButtonHandle map_button(int code, + DeviceClass device_class = DeviceClass::unknown, + int quirks = 0); + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + InputDevice::init_type(); + register_type(_type_handle, "EvdevInputDevice", + InputDevice::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "evdevInputDevice.I" + +#endif // PHAVE_LINUX_INPUT_H + +#endif // EVDEVINPUTDEVICE_H diff --git a/panda/src/device/inputDevice.I b/panda/src/device/inputDevice.I new file mode 100644 index 0000000000..532e77f796 --- /dev/null +++ b/panda/src/device/inputDevice.I @@ -0,0 +1,410 @@ +/** + * 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 inputDevice.I + * @author rdb + * @date 2015-12-11 + */ + +#include "config_device.h" + +/** + * + */ +INLINE InputDevice:: +InputDevice() { + _button_events = new ButtonEventList; + _pointer_events = new PointerEventList; +} + +/** + * Returns a human-readable name for the device. Not necessarily unique. + */ +INLINE std::string InputDevice:: +get_name() const { + LightMutexHolder holder(_lock); + return _name; +} + +/** + * Returns a string containing the manufacturer of the device, if this + * information is known. + */ +INLINE std::string InputDevice:: +get_manufacturer() const { + LightMutexHolder holder(_lock); + return _manufacturer; +} + +/** + * Returns a string containing the serial number of the device, if this + * information is known. + */ +INLINE std::string InputDevice:: +get_serial_number() const { + LightMutexHolder holder(_lock); + return _serial_number; +} + +/** + * Returns a string containing the USB vendor ID of the device, if this + * information is known. + */ +INLINE unsigned short InputDevice:: +get_vendor_id() const { + LightMutexHolder holder(_lock); + return _vendor_id; +} + +/** + * Returns a string containing the USB product ID of the device, if this + * information is known. + */ +INLINE unsigned short InputDevice:: +get_product_id() const { + LightMutexHolder holder(_lock); + return _product_id; +} + +/** + * Returns true if the device is still connected and able to receive data, + * false otherwise. May return false positives. + */ +INLINE bool InputDevice:: +is_connected() const { + LightMutexHolder holder(_lock); + return _is_connected; +} + +/** + * Returns an identification of the general type of device. If this could not + * be determined, returns DeviceClass.unknown. + */ +INLINE InputDevice::DeviceClass InputDevice:: +get_device_class() const { + LightMutexHolder holder(_lock); + return _device_class; +} + +/** + * Returns true if the device supports the indicated feature. + */ +INLINE bool InputDevice:: +has_feature(Feature feature) const { + LightMutexHolder holder(_lock); + return (_features & (1 << (unsigned int)feature)) != 0; +} + +/** + * Returns true if this is a pointing device. + */ +INLINE bool InputDevice:: +has_pointer() const { + return has_feature(Feature::pointer); +} + +/** + * Returns true if the device has a physical keyboard designed for text entry. + */ +INLINE bool InputDevice:: +has_keyboard() const { + return has_feature(Feature::keyboard); +} + +/** + * Returns true if the device features a tracker that can track position and/or + * orientation in 3D space. + */ +INLINE bool InputDevice:: +has_tracker() const { + return has_feature(Feature::tracker); +} + +/** + * Returns true if the device has vibration motors that can be controlled by + * calling set_vibration(). + */ +INLINE bool InputDevice:: +has_vibration() const { + return has_feature(Feature::vibration); +} + +/** + * Returns true if the device may be able to provide information about its + * battery life. + */ +INLINE bool InputDevice:: +has_battery() const { + return has_feature(Feature::battery); +} + +/** + * Returns the TrackerData associated with the input device's tracker. This + * only makes sense if has_tracker() also returns true. + */ +INLINE TrackerData InputDevice:: +get_tracker() const { + LightMutexHolder holder(_lock); + return _tracker_data; +} + +/** + * Returns a rough indication of the battery level, ranging from 0 (completely + * empty battery) to the indicated max_level value. + */ +INLINE InputDevice::BatteryData InputDevice:: +get_battery() const { + LightMutexHolder holder(_lock); + return _battery_data; +} + +/** + * Returns the number of buttons known to the device. This includes those + * buttons whose state has been seen, as well as buttons that have been + * associated with a ButtonHandle even if their state is unknown. This number + * may change as more buttons are discovered. + */ +INLINE size_t InputDevice:: +get_num_buttons() const { + LightMutexHolder holder(_lock); + return _buttons.size(); +} + +/** + * Associates the indicated ButtonHandle with the button of the indicated index + * number. When the given button index changes state, a corresponding + * ButtonEvent will be generated with the given ButtonHandle. Pass + * ButtonHandle::none() to turn off any association. + * + * It is not necessary to call this if you simply want to query the state of + * the various buttons by index number; this is only necessary in order to + * generate ButtonEvents when the buttons change state. + */ +INLINE void InputDevice:: +map_button(size_t index, ButtonHandle button) { + LightMutexHolder holder(_lock); + if (index >= _buttons.size()) { + _buttons.resize(index + 1, ButtonState()); + } + + _buttons[index].handle = button; +} + +/** + * Returns the ButtonHandle that was previously associated with the given index + * number by a call to map_button(), or ButtonHandle::none() if no button + * was associated. + */ +INLINE ButtonHandle InputDevice:: +get_button_map(size_t index) const { + if (index < _buttons.size()) { + return _buttons[index].handle; + } else { + return ButtonHandle::none(); + } +} + +/** + * Returns true if the indicated button (identified by its index number) is + * currently known to be down, or false if it is up or unknown. + */ +INLINE bool InputDevice:: +is_button_pressed(size_t index) const { + if (index < _buttons.size()) { + return (_buttons[index]._state == S_down); + } else { + return false; + } +} + +/** + * Returns true if the state of the indicated button is known, or false if we + * have never heard anything about this particular button. + */ +INLINE bool InputDevice:: +is_button_known(size_t index) const { + if (index < _buttons.size()) { + return _buttons[index]._state != S_unknown; + } else { + return false; + } +} + +/** + * Returns the ButtonState that is set at the given index, or throw an assert + * if the index was not found in the list. + */ +INLINE InputDevice::ButtonState InputDevice:: +get_button(size_t index) const { + nassertr_always(index < _buttons.size(), ButtonState()); + return _buttons[index]; +} + +/** + * Returns the first ButtonState found with the given axis, or throw an assert + * if the button handle was not found in the list. + */ +INLINE InputDevice::ButtonState InputDevice:: +find_button(ButtonHandle handle) const { + for (size_t i = 0; i < _buttons.size(); ++i) { + if (_buttons[i].handle == handle) { + return _buttons[i]; + } + } + return ButtonState(); +} + +/** + * Returns the number of analog axes known to the InputDevice. This number + * may change as more axes are discovered. + */ +INLINE size_t InputDevice:: +get_num_axes() const { + return _axes.size(); +} + +/** + * Associates the indicated Axis with the axis of the indicated index + * number. Pass Axis::none to turn off any association. + * + * It is not necessary to call this if you simply want to query the state of + * the various axes by index number. + */ +INLINE void InputDevice:: +map_axis(size_t index, InputDevice::Axis axis) { + LightMutexHolder holder(_lock); + if (index >= _axes.size()) { + _axes.resize(index + 1, AxisState()); + } + + _axes[index].axis = axis; +} + +/** + * Returns the current position of indicated analog axis (identified by its + * index number), or 0.0 if the axis is unknown. The normal range of a + * single axis is -1.0 to 1.0. + */ +INLINE double InputDevice:: +get_axis_value(size_t index) const { + if (index < _axes.size()) { + return _axes[index].value; + } else { + return 0.0; + } +} + +/** + * Returns the axis state that is set at the given index, or throw an assert + * if the index was not found in the list. + */ +INLINE InputDevice::AxisState InputDevice:: +get_axis(size_t index) const { + nassertr_always(index < _axes.size(), AxisState()); + return _axes[index]; +} + +/** + * Returns the first AnalogAxis found with the given axis, or throw an assert + * if the axis was not found in the list. + */ +INLINE InputDevice::AxisState InputDevice:: +find_axis(InputDevice::Axis axis) const { + for (size_t i = 0; i < _axes.size(); ++i) { + if (_axes[i].axis == axis) { + return _axes[i]; + } + } + return AxisState(); +} + +/** + * Returns true if the state of the indicated analog axis is known, or false + * if we have never heard anything about this particular axis. + */ +INLINE bool InputDevice:: +is_axis_known(size_t index) const { + if (index < _axes.size()) { + return _axes[index].known; + } else { + return false; + } +} + +/** + * Sets the strength of the vibration effect, if supported. The values are + * clamped to 0-1 range. The first value axes the low-frequency rumble + * motor, whereas the second axes the high-frequency motor, if present. + */ +INLINE void InputDevice:: +set_vibration(double strong, double weak) { + LightMutexHolder holder(_lock); + do_set_vibration(std::max(std::min(strong, 1.0), 0.0), std::max(std::min(weak, 1.0), 0.0)); +} + +/** + * Enables the generation of mouse-movement events. + */ +INLINE void InputDevice:: +enable_pointer_events() { + LightMutexHolder holder(_lock); + _enable_pointer_events = true; +} + +/** + * Disables the generation of mouse-movement events. + */ +INLINE void InputDevice:: +disable_pointer_events() { + LightMutexHolder holder(_lock); + _enable_pointer_events = false; + _pointer_events.clear(); +} + +/** + * Called to indicate that the device supports the given feature. + * Assumes the lock is held. + */ +INLINE void InputDevice:: +enable_feature(Feature feature) { + _features |= (1 << (unsigned int)feature); +} + +/** + * Called to indicate that the device has been disconnected or connected from + * its host. + */ +INLINE void InputDevice:: +set_connected(bool connected) { + LightMutexHolder holder(_lock); + _is_connected = connected; +} + +/** + * + */ +INLINE InputDevice::ButtonState:: +ButtonState(ButtonHandle handle) : + handle(handle) { +} + +/** + * True if the button state is currently known. + */ +ALWAYS_INLINE bool InputDevice::ButtonState:: +is_known() const { + return (_state != S_unknown); +} + +/** + * True if the button is currently known to be pressed. + */ +ALWAYS_INLINE bool InputDevice::ButtonState:: +is_pressed() const { + return (_state == S_down); +} diff --git a/panda/src/device/inputDevice.cxx b/panda/src/device/inputDevice.cxx new file mode 100644 index 0000000000..130854834e --- /dev/null +++ b/panda/src/device/inputDevice.cxx @@ -0,0 +1,661 @@ +/** + * 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 inputDevice.cxx + * @author rdb + * @date 2015-12-11 + */ + +#include "inputDevice.h" + +#if defined(_MSC_VER) && _MSC_VER < 1700 +#define fma(a, b, c) ((a) * (b) + (c)) +#endif + +TypeHandle InputDevice::_type_handle; + +/** + * Defines a new InputDevice. + */ +InputDevice:: +InputDevice(const std::string &name, DeviceClass dev_class) : + _name(name), + _device_class(dev_class), + _is_connected(true) +{ + _button_events = new ButtonEventList; + _pointer_events = new PointerEventList; +} + +/** + * + */ +InputDevice:: +~InputDevice() { +} + +/** + * Polls the input device for new activity, to ensure it contains the latest + * events. This will only have any effect for some types of input devices; + * others may be updated automatically, and this method will be a no-op. + */ +void InputDevice:: +poll() { + LightMutexHolder holder(_lock); + do_poll(); +} + +/** + * Returns true if this device has a pending button event (a mouse button or + * keyboard button down/up), false otherwise. If this returns true, the + * particular event may be extracted via get_button_event(). + */ +bool InputDevice:: +has_button_event() const { + LightMutexHolder holder(_lock); + return !_button_events.is_null() && _button_events->get_num_events() > 0; +} + +/** + * Returns the list of recently-generated ButtonEvents. + * The list is also cleared. + */ +PT(ButtonEventList) InputDevice:: +get_button_events() { + LightMutexHolder holder(_lock); + PT(ButtonEventList) result = new ButtonEventList; + swap(_button_events, result); + return result; +} + +/** + * Returns true if this device has a pending pointer event (a mouse movement), + * or false otherwise. If this returns true, the particular event may be + * extracted via get_pointer_event(). + */ +bool InputDevice:: +has_pointer_event() const { + LightMutexHolder holder(_lock); + return _pointer_events != nullptr && !_pointer_events->empty(); +} + +/** + * Returns a PointerEventList containing all the recent pointer events. + * Clears the list. + */ +PT(PointerEventList) InputDevice:: +get_pointer_events() { + LightMutexHolder holder(_lock); + PT(PointerEventList) result = new PointerEventList; + swap(_pointer_events, result); + return result; +} + +/** + * Called by the implementation to add a new known button. + */ +int InputDevice:: +add_button(ButtonHandle button) { + int index = (int)_buttons.size(); + _buttons.push_back(ButtonState(button)); + return index; +} + +/** + * Called by the implementation to add a new known axis. + */ +int InputDevice:: +add_axis(Axis axis, int minimum, int maximum, bool centered) { + AxisState state; + state.axis = axis; + if (centered) { + // Centered, eg. for sticks. + state._scale = 2.0 / (maximum - minimum); + state._bias = (maximum + minimum) / (double)(minimum - maximum); + } else { + // 0-based, eg. for triggers. + state._scale = 1.0 / maximum; + state._bias = 0.0; + } + int index = (int)_axes.size(); + _axes.push_back(state); + return index; +} + +/** + * Called by the implementation to add a new known axis. This version tries + * to guess whether the axis is centered or not. + */ +int InputDevice:: +add_axis(Axis axis, int minimum, int maximum) { + bool centered = (minimum < 0) + || axis == Axis::x + || axis == Axis::y + || axis == Axis::z + || axis == Axis::yaw + || axis == Axis::pitch + || axis == Axis::roll + || axis == Axis::left_x + || axis == Axis::left_y + || axis == Axis::right_x + || axis == Axis::right_y + || axis == Axis::wheel + || axis == Axis::rudder; + return add_axis(axis, minimum, maximum, centered); +} + +/** + * Records that a new pointer was found. + */ +int InputDevice:: +add_pointer(PointerType type, int id, bool primary) { + //nassertr(_lock.debug_is_locked(), -1); + + PointerData data; + data._id = id; + data._type = type; + + int index = (int)_pointers.size(); + if (_num_pointers == _pointers.size()) { + _pointers.push_back(data); + } else { + _pointers[index] = data; + } + ++_num_pointers; + + return index; +} + +/** + * Removes a previously added pointer. If the current pressure is not zero, + * it will generate an event doing so. + */ +void InputDevice:: +remove_pointer(int id) { + nassertv(_lock.debug_is_locked()); + + size_t i; + for (i = 0; i < _pointers.size(); ++i) { + if (_pointers[i]._id == id) { + break; + } + } + + if (i < _pointers.size()) { + if (_pointers[i]._pressure != 0.0) { + _pointers[i]._pressure = 0.0; + + if (_enable_pointer_events) { + int seq = _event_sequence++; + double time = ClockObject::get_global_clock()->get_frame_time(); + _pointer_events->add_event(_pointers[i], seq, time); + } + } + + // Replace it with the last one. + if (i != _pointers.size() - 1) { + _pointers[i] = _pointers.back(); + } + --_num_pointers; + } +} + +/** + * Records that pointer data for a pointer has changed. This can also be used + * to add a new pointer. + */ +void InputDevice:: +update_pointer(PointerData data, double time) { + nassertv(_lock.debug_is_locked()); + + PointerData *ptr = nullptr; + for (size_t i = 0; i < _pointers.size(); ++i) { + if (_pointers[i]._id == data._id) { + ptr = &_pointers[i]; + *ptr = data; + break; + } + } + if (ptr == nullptr) { + _pointers.push_back(data); + ptr = &_pointers.back(); + } + + if (_enable_pointer_events) { + int seq = _event_sequence++; + _pointer_events->add_event(*ptr, seq, time); + } +} + +/** + * Records that a relative pointer movement has taken place. + */ +void InputDevice:: +pointer_moved(int id, double x, double y, double time) { + nassertv(_lock.debug_is_locked()); + + PointerData *ptr = nullptr; + for (size_t i = 0; i < _pointers.size(); ++i) { + if (_pointers[i]._id == id) { + ptr = &_pointers[i]; + _pointers[i]._xpos = x; + _pointers[i]._ypos = y; + break; + } + } + nassertv_always(ptr != nullptr); + + if (device_cat.is_spam() && (x != 0 || y != 0)) { + device_cat.spam() + << "Pointer " << id << " moved by " << x << " x " << y << "\n"; + } + + if (_enable_pointer_events) { + int seq = _event_sequence++; + _pointer_events->add_event(ptr->_in_window, + ptr->_xpos, + ptr->_ypos, + x, y, seq, time); + } +} + +/** + * Sets the state of the indicated button index, where true indicates down, + * and false indicates up. This may generate a ButtonEvent if the button has + * an associated ButtonHandle. The caller should ensure that the lock is held + * while this call is made. + */ +void InputDevice:: +button_changed(int index, bool down) { + nassertv(_lock.debug_is_locked()); + nassertv(index >= 0); + if (index >= (int)_buttons.size()) { + _buttons.resize(index + 1, ButtonState()); + } + + State new_state = down ? S_down : S_up; + if (_buttons[index]._state == new_state) { + return; + } + _buttons[index]._state = new_state; + + ButtonHandle handle = _buttons[index].handle; + + if (device_cat.is_spam()) { + device_cat.spam() + << "Changed button " << index; + + if (handle != ButtonHandle::none()) { + device_cat.spam(false) << " (" << handle << ")"; + } + + device_cat.spam(false) << " to " << (down ? "down" : "up") << "\n"; + } + + if (handle != ButtonHandle::none()) { + _button_events->add_event(ButtonEvent(handle, down ? ButtonEvent::T_down : ButtonEvent::T_up)); + } +} + +/** + * Sets the state of the indicated analog index. The caller should ensure that + * the lock is held while this call is made. This should be a number in the + * range -1.0 to 1.0, representing the current position of the axis within its + * total range of movement. + */ +void InputDevice:: +set_axis_value(int index, double value) { + LightMutexHolder holder(_lock); + + nassertv(index >= 0); + if ((size_t)index >= _axes.size()) { + _axes.resize((size_t)index + 1u, AxisState()); + } + + if (device_cat.is_spam() && _axes[index].value != value) { + device_cat.spam() + << "Changed axis " << index; + + if (_axes[index].axis != Axis::none) { + device_cat.spam(false) << " (" << _axes[index].axis << ")"; + } + + device_cat.spam(false) << " to " << value << "\n"; + } + + _axes[index].value = value; + _axes[index].known = true; +} + +/** + * Called by the implementation during do_poll to indicate that the indicated + * axis has received a new raw value. Assumes the lock is held. + */ +void InputDevice:: +axis_changed(int index, int state) { + nassertv(_lock.debug_is_locked()); + nassertv(index >= 0); + if ((size_t)index >= _axes.size()) { + _axes.resize((size_t)index + 1u, AxisState()); + } + + double value = fma((double)state, _axes[index]._scale, _axes[index]._bias); + + if (device_cat.is_spam() && !IS_NEARLY_EQUAL(_axes[index].value, value)) { + device_cat.spam() + << "Changed axis " << index; + + if (_axes[index].axis != Axis::none) { + device_cat.spam(false) << " (" << _axes[index].axis << ")"; + } + + device_cat.spam(false) << " to " << value << " (raw value " << state << ")\n"; + } + + _axes[index].value = value; + _axes[index].known = true; +} + +/** + * Records that a tracker movement has taken place. + */ +void InputDevice:: +tracker_changed(const LPoint3 &pos, const LOrientation &orient, double time) { + nassertv(_lock.debug_is_locked()); + + _tracker_data.set_pos(pos); + _tracker_data.set_orient(orient); + _tracker_data.set_time(time); +} + +/** + * Writes a one-line string describing the device. + */ +void InputDevice:: +output(std::ostream &out) const { + LightMutexHolder holder(_lock); + + out << _name << " ("; + + if (!_is_connected) { + out << "dis"; + } + + out << "connected)"; + + if (_device_class != DeviceClass::unknown) { + out << ", " << _device_class; + } + + if (_buttons.size() > 0) { + out << ", " << _buttons.size() << " button"; + if (_buttons.size() != 1) { + out.put('s'); + } + } + + if (_axes.size() > 0) { + out << ", " << _axes.size() << " ax" + << (_axes.size() != 1 ? 'e' : 'i') << 's'; + } + + if (_features & (unsigned int)Feature::pointer) { + out << ", pointer"; + } + if (_features & (unsigned int)Feature::keyboard) { + out << ", keyboard"; + } + if (_features & (unsigned int)Feature::tracker) { + out << ", tracker"; + } + if (_features & (unsigned int)Feature::vibration) { + out << ", vibration"; + } + if (_features & (unsigned int)Feature::battery) { + out << ", battery"; + + if (_battery_data.level > 0 && _battery_data.max_level > 0) { + out << " ["; + short i = 0; + for (; i < _battery_data.level; ++i) { + out << '='; + } + for (; i < _battery_data.max_level; ++i) { + out << ' '; + } + out << ']'; + } + } +} + +/** + * Writes a one-line string of all of the current button states. + */ +void InputDevice:: +output_buttons(std::ostream &out) const { + LightMutexHolder holder(_lock); + + bool any_buttons = false; + Buttons::const_iterator bi; + for (bi = _buttons.begin(); bi != _buttons.end(); ++bi) { + const ButtonState &state = (*bi); + if (state.is_known()) { + if (any_buttons) { + out << ", "; + } + any_buttons = true; + out << (int)(bi - _buttons.begin()) << "="; + if (state._state == S_up) { + out << "up"; + } else { + out << "down"; + } + } + } + + if (!any_buttons) { + out << "no known buttons"; + } +} + +/** + * Writes a multi-line description of the current button states. + */ +void InputDevice:: +write_buttons(std::ostream &out, int indent_level) const { + bool any_buttons = false; + Buttons::const_iterator bi; + for (bi = _buttons.begin(); bi != _buttons.end(); ++bi) { + const ButtonState &state = (*bi); + if (state.is_known()) { + any_buttons = true; + + indent(out, indent_level) + << (int)(bi - _buttons.begin()) << ". "; + + if (state.handle != ButtonHandle::none()) { + out << "(" << state.handle << ") "; + } + + if (state._state == S_up) { + out << "up"; + } else { + out << "down"; + } + out << "\n"; + } + } + + if (!any_buttons) { + indent(out, indent_level) + << "(no known buttons)\n"; + } +} + +/** + * Writes a multi-line description of the current analog axis states. + */ +void InputDevice:: +write_axes(std::ostream &out, int indent_level) const { + LightMutexHolder holder(_lock); + + bool any_axis = false; + Axes::const_iterator ai; + for (ai = _axes.begin(); ai != _axes.end(); ++ai) { + const AxisState &state = (*ai); + if (state.known) { + any_axis = true; + + indent(out, indent_level) + << (int)(ai - _axes.begin()) << ". " << state.value << "\n"; + } + } + + if (!any_axis) { + indent(out, indent_level) + << "(no known analog axes)\n"; + } +} + +/** + * Sets the vibration strength. The first argument controls a low-frequency + * motor, if present, and the latter controls a high-frequency motor. + * The values are within the 0-1 range. + */ +void InputDevice:: +do_set_vibration(double strong, double weak) { +} + +/** + * Polls the input device for new activity, to ensure it contains the latest + * events. This will only have any effect for some types of input devices; + * others may be updated automatically, and this method will be a no-op. + */ +void InputDevice:: +do_poll() { +} + +/** + * Returns a string describing the given device class enumerant. + */ +std::string InputDevice:: +format_device_class(DeviceClass dc) { + switch (dc) { + case InputDevice::DeviceClass::unknown: + return "unknown"; + + case InputDevice::DeviceClass::virtual_device: + return "virtual_device"; + + case InputDevice::DeviceClass::keyboard: + return "keyboard"; + + case InputDevice::DeviceClass::mouse: + return "mouse"; + + case InputDevice::DeviceClass::touch: + return "touch"; + + case InputDevice::DeviceClass::gamepad: + return "gamepad"; + + case InputDevice::DeviceClass::flight_stick: + return "flight_stick"; + + case InputDevice::DeviceClass::steering_wheel: + return "steering_wheel"; + + case InputDevice::DeviceClass::dance_pad: + return "dance_pad"; + + case InputDevice::DeviceClass::hmd: + return "hmd"; + + case InputDevice::DeviceClass::spatial_mouse: + return "spatial_mouse"; + } + return "**invalid**"; +} + +/** + * Returns a string describing the given axis enumerant. + */ +std::string InputDevice:: +format_axis(Axis axis) { + switch (axis) { + case InputDevice::Axis::none: + return "none"; + + case InputDevice::Axis::x: + return "x"; + + case InputDevice::Axis::y: + return "y"; + + case InputDevice::Axis::z: + return "z"; + + case InputDevice::Axis::yaw: + return "yaw"; + + case InputDevice::Axis::pitch: + return "pitch"; + + case InputDevice::Axis::roll: + return "roll"; + + case InputDevice::Axis::left_x: + return "left_x"; + + case InputDevice::Axis::left_y: + return "left_y"; + + case InputDevice::Axis::left_trigger: + return "left_trigger"; + + case InputDevice::Axis::right_x: + return "right_x"; + + case InputDevice::Axis::right_y: + return "right_y"; + + case InputDevice::Axis::right_trigger: + return "right_trigger"; + + //case InputDevice::Axis::trigger: + // return "trigger"; + + case InputDevice::Axis::throttle: + return "throttle"; + + case InputDevice::Axis::rudder: + return "rudder"; + + case InputDevice::Axis::wheel: + return "wheel"; + + case InputDevice::Axis::accelerator: + return "accelerator"; + + case InputDevice::Axis::brake: + return "brake"; + } + return "**invalid**"; +} + +std::ostream & +operator << (std::ostream &out, InputDevice::DeviceClass dc) { + out << InputDevice::format_device_class(dc); + return out; +} + +std::ostream & +operator << (std::ostream &out, InputDevice::Axis axis) { + out << InputDevice::format_axis(axis); + return out; +} diff --git a/panda/src/device/inputDevice.h b/panda/src/device/inputDevice.h new file mode 100644 index 0000000000..3e4e37313e --- /dev/null +++ b/panda/src/device/inputDevice.h @@ -0,0 +1,366 @@ +/** + * 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 inputDevice.h + * @author rdb + * @date 2015-12-11 + */ + +#ifndef INPUTDEVICE_H +#define INPUTDEVICE_H + +#include "pandabase.h" + +#include "buttonEvent.h" +#include "buttonEventList.h" +#include "pointerEvent.h" +#include "pointerEventList.h" +#include "pointerData.h" +#include "trackerData.h" +#include "clockObject.h" + +#include "pdeque.h" +#include "pvector.h" +#include "lightMutex.h" +#include "lightMutexHolder.h" + +/** + * This is a structure representing a single input device. Input devices may + * have zero or more buttons, pointers, or axes associated with them, and + * optionally a motion tracker. + * + * These devices are brought under a common interface because there is such a + * large range of devices out there that may support any number of these types + * of axes, we couldn't even begin to cover them with type-specific + * subclasses. + * + * Use the various has_() and get_num_() methods to determine information about + * the device capabilities. For instance, has_keyboard() will give an + * indication that you can receive keystroke events from this device, and + * get_num_buttons() will tell you that the device may send button events. + * + * There is the DeviceType enumeration, however, which will (if known) contain + * identification of the general category of devices this fits in, such as + * keyboard, mouse, gamepad, or flight stick. + */ +class EXPCL_PANDA_DEVICE InputDevice : public TypedReferenceCount { +PUBLISHED: + // This enum contains information that can be used to identify the + // type of input device. + enum class DeviceClass { + // It is not known what type of device this is. + unknown, + + // This means that the device doesn't correspond to a physical + // device, but rather to a dynamic source of input events. + virtual_device, + + // A physical, alphabetical keyboard. + keyboard, + + mouse, + touch, + + // A gamepad with action buttons, a D-pad, and thumbsticks. + gamepad, + + flight_stick, + steering_wheel, + dance_pad, + + // Head-mounted display. + hmd, + + // 3D mouse, such as produced by 3Dconnexion. + spatial_mouse, + }; + + enum class Feature { + // The device provides absolute screen coordinates. + pointer, + + // The device has an interface for providing text input. + keyboard, + + // The device has a motion tracker, such as an HMD. + tracker, + + // The device can produce force feedback. + vibration, + + // The device provides information about battery life. + battery, + }; + + enum class Axis { + none, + + // Generic translational axes + x, + y, + z, + + // Generic rotational axes, used by joysticks and 3D mice + yaw, + pitch, + roll, + + // Gamepad + left_x, + left_y, + left_trigger, + right_x, + right_y, + right_trigger, + + // Flight stick specific + throttle, + rudder, // When available separately from yaw + + // Steering wheel / pedals + wheel, + accelerator, + brake, + }; + + enum State { + S_unknown, + S_up, + S_down + }; + + class ButtonState { + public: + constexpr ButtonState() = default; + INLINE ButtonState(ButtonHandle handle); + ALWAYS_INLINE bool is_known() const; + ALWAYS_INLINE bool is_pressed() const; + + PUBLISHED: + operator bool() { return _state != S_unknown; } + + MAKE_PROPERTY(known, is_known); + MAKE_PROPERTY(pressed, is_pressed); + + ButtonHandle handle = ButtonHandle::none(); + + public: + State _state = S_unknown; + }; + + class AxisState { + public: + constexpr AxisState() = default; + + PUBLISHED: + operator bool() { return known && value != 0.0; } + + Axis axis = Axis::none; + double value = 0.0; + bool known = false; + + public: + double _scale = 1.0; + double _bias = 0.0; + }; + + class BatteryData { + PUBLISHED: + // Ranges from 0 through max_level. + short level = -1; + + // Maximum value of 'level' field. + short max_level = -1; + }; + +protected: + InputDevice(const std::string &name, DeviceClass dev_class); + +public: + InputDevice(); + InputDevice(const InputDevice ©) = delete; + InputDevice &operator = (const InputDevice ©) = delete; + virtual ~InputDevice(); + + INLINE std::string get_name() const; + INLINE std::string get_manufacturer() const; + INLINE std::string get_serial_number() const; + INLINE unsigned short get_vendor_id() const; + INLINE unsigned short get_product_id() const; + INLINE bool is_connected() const; + INLINE DeviceClass get_device_class() const; + + INLINE bool has_pointer() const; + INLINE bool has_keyboard() const; + INLINE bool has_tracker() const; + INLINE bool has_vibration() const; + INLINE bool has_battery() const; + + INLINE TrackerData get_tracker() const; + INLINE BatteryData get_battery() const; + + INLINE size_t get_num_buttons() const; + INLINE ButtonHandle get_button_map(size_t index) const; + INLINE bool is_button_pressed(size_t index) const; + INLINE bool is_button_known(size_t index) const; + INLINE ButtonState get_button(size_t index) const; + + INLINE size_t get_num_axes() const; + INLINE double get_axis_value(size_t index) const; + INLINE bool is_axis_known(size_t index) const; + INLINE AxisState get_axis(size_t index) const; + +PUBLISHED: + // The human-readable name of this input device. + MAKE_PROPERTY(name, get_name); + + // The device's manufacturer, or the empty string if not known. + MAKE_PROPERTY(manufacturer, get_manufacturer); + + // The device's serial number, or the empty string if not known. + MAKE_PROPERTY(serial_number, get_serial_number); + + // USB vendor ID of the device, or 0 if not known. + MAKE_PROPERTY(vendor_id, get_vendor_id); + + // USB product ID of the device, or 0 if not known. + MAKE_PROPERTY(product_id, get_product_id); + + // This is false if we know that the device is not currently connected. + // May report false positives if we can't know this with certainty. + MAKE_PROPERTY(connected, is_connected); + + // This contains an identification of the general type of device. If + // this could not be determined, it is set to DC_unknown. + MAKE_PROPERTY(device_class, get_device_class); + + // Determine supported features + INLINE bool has_feature(Feature feature) const; + + // Getters for the various types of device data. + MAKE_PROPERTY2(tracker, has_tracker, get_tracker); + MAKE_PROPERTY2(battery, has_battery, get_battery); + + // Make device buttons and axes iterable + MAKE_SEQ_PROPERTY(buttons, get_num_buttons, get_button); + MAKE_SEQ_PROPERTY(axes, get_num_axes, get_axis); + + // Associate buttons/axes with symbolic handles. + INLINE void map_button(size_t index, ButtonHandle handle); + INLINE void map_axis(size_t index, Axis axis); + INLINE ButtonState find_button(ButtonHandle handle) const; + INLINE AxisState find_axis(Axis axis) const; + + // Enable rumble force-feedback effects + INLINE void set_vibration(double strong, double weak); + + INLINE void enable_pointer_events(); + INLINE void disable_pointer_events(); + + void poll(); + + bool has_button_event() const; + PT(ButtonEventList) get_button_events(); + bool has_pointer_event() const; + PT(PointerEventList) get_pointer_events(); + + virtual void output(std::ostream &out) const; + +public: + static std::string format_device_class(DeviceClass dc); + static std::string format_axis(Axis axis); + +protected: + INLINE void enable_feature(Feature feature); + + // Called during the constructor to add new axes or buttons + int add_button(ButtonHandle handle); + int add_axis(Axis axis, int minimum, int maximum, bool centered); + int add_axis(Axis axis, int minimum, int maximum); + + int add_pointer(PointerType type, int id, bool primary = false); + void remove_pointer(int id); + void update_pointer(PointerData data, double time); + void pointer_moved(int id, double x, double y, double time); + void button_changed(int index, bool down); + void axis_changed(int index, int value); + void set_axis_value(int index, double state); + + void tracker_changed(const LPoint3 &pos, const LOrientation &orient, double time); + + virtual void do_set_vibration(double low, double high); + virtual void do_poll(); + +public: + INLINE void set_connected(bool connected); + + void output_buttons(std::ostream &out) const; + void write_buttons(std::ostream &out, int indent_level) const; + void write_axes(std::ostream &out, int indent_level) const; + +protected: + typedef pdeque ButtonEvents; + + LightMutex _lock {"InputDevice"}; + + std::string _name; + std::string _serial_number; + std::string _manufacturer; + DeviceClass _device_class = DeviceClass::unknown; + unsigned int _features = 0; + int _event_sequence = 0; + unsigned short _vendor_id = 0; + unsigned short _product_id = 0; + bool _is_connected = false; + bool _enable_pointer_events = false; + PT(ButtonEventList) _button_events; + PT(PointerEventList) _pointer_events; + + size_t _num_pointers = 0; + typedef pvector Pointers; + Pointers _pointers; + +PUBLISHED: + typedef pvector Buttons; + typedef pvector Axes; + Buttons _buttons; + Axes _axes; + + PointerData _pointer_data; + BatteryData _battery_data; + TrackerData _tracker_data; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedReferenceCount::init_type(); + register_type(_type_handle, "InputDevice", + TypedReferenceCount::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +INLINE std::ostream &operator << (std::ostream &out, const InputDevice &device) { + device.output(out); + return out; +} + +EXPCL_PANDA_DEVICE std::ostream &operator << (std::ostream &out, InputDevice::DeviceClass dc); +EXPCL_PANDA_DEVICE std::ostream &operator << (std::ostream &out, InputDevice::Axis axis); + +#include "inputDevice.I" + +#endif diff --git a/panda/src/device/inputDeviceManager.I b/panda/src/device/inputDeviceManager.I new file mode 100644 index 0000000000..731051aa3c --- /dev/null +++ b/panda/src/device/inputDeviceManager.I @@ -0,0 +1,25 @@ +/** + * 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 inputDeviceManager.I + * @author rdb + * @date 2015-12-09 + */ + +/** + * Returns the singleton InputDeviceManager instance. + */ +INLINE InputDeviceManager *InputDeviceManager:: +get_global_ptr() { + if (_global_ptr != nullptr) { + return _global_ptr; + } else { + make_global_ptr(); + return _global_ptr; + } +} diff --git a/panda/src/device/inputDeviceManager.cxx b/panda/src/device/inputDeviceManager.cxx new file mode 100644 index 0000000000..8cd470e271 --- /dev/null +++ b/panda/src/device/inputDeviceManager.cxx @@ -0,0 +1,128 @@ +/** + * 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 inputDeviceManager.cxx + * @author rdb + * @date 2015-12-09 + */ + +#include "inputDeviceManager.h" +#include "ioKitInputDeviceManager.h" +#include "linuxInputDeviceManager.h" +#include "winInputDeviceManager.h" +#include "throw_event.h" +#include "config_putil.h" + +InputDeviceManager *InputDeviceManager::_global_ptr = nullptr; + +/** + * Initializes the input device manager by scanning which devices are currently + * connected and setting up any platform-dependent structures necessary for + * listening for future device connect events. + */ +InputDeviceManager:: +InputDeviceManager() : _lock("InputDeviceManager") { +} + +/** + * Creates the global input manager. + */ +void InputDeviceManager:: +make_global_ptr() { + init_libputil(); + +#ifdef _WIN32 + _global_ptr = new WinInputDeviceManager; +#elif defined(__APPLE__) + _global_ptr = new IOKitInputDeviceManager; +#elif defined(PHAVE_LINUX_INPUT_H) + _global_ptr = new LinuxInputDeviceManager; +#else + _global_ptr = new InputDeviceManager; +#endif +} + +/** + * Description: Returns all currently connected devices. + */ +InputDeviceSet InputDeviceManager:: +get_devices() const { + InputDeviceSet devices; + LightMutexHolder holder(_lock); + + for (size_t i = 0; i < _connected_devices.size(); ++i) { + InputDevice *device = _connected_devices[i]; + devices.add_device(device); + } + + return devices; +} + +/** + * Description: Returns all currently connected devices of the given device class. + */ +InputDeviceSet InputDeviceManager:: +get_devices(InputDevice::DeviceClass device_class) const { + InputDeviceSet devices; + LightMutexHolder holder(_lock); + + for (size_t i = 0; i < _connected_devices.size(); ++i) { + InputDevice *device = _connected_devices[i]; + if (device->get_device_class() == device_class) { + devices.add_device(device); + } + } + + return devices; +} + + +/** + * Called when a new device has been discovered. This may also be used to + * register virtual devices. + * + * This causes a connect-device event to be thrown. + */ +void InputDeviceManager:: +add_device(InputDevice *device) { + { + LightMutexHolder holder(_lock); + _connected_devices.add_device(device); + +#ifdef PHAVE_LINUX_INPUT_H + // If we had added it pending activity on the device, remove it + // from the list of inactive devices. + _inactive_devices.remove_device(device); +#endif + } + throw_event("connect-device", device); +} + +/** + * Called when a device has been removed, or when a device should otherwise no + * longer be tracked. + * + * This causes a disconnect-device event to be thrown. + */ +void InputDeviceManager:: +remove_device(InputDevice *device) { + { + LightMutexHolder holder(_lock); + _connected_devices.remove_device(device); + } + + throw_event("disconnect-device", device); +} + +/** + * Polls the system to see if there are any new devices. In some + * implementations this is a no-op. + */ +void InputDeviceManager:: +update() { +} diff --git a/panda/src/device/inputDeviceManager.h b/panda/src/device/inputDeviceManager.h new file mode 100644 index 0000000000..7fd69b804e --- /dev/null +++ b/panda/src/device/inputDeviceManager.h @@ -0,0 +1,63 @@ +/** + * 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 inputDeviceManager.h + * @author rdb + * @date 2015-12-09 + */ + +#ifndef INPUTDEVICEMANAGER_H +#define INPUTDEVICEMANAGER_H + +#include "pandabase.h" +#include "lightMutex.h" +#include "inputDevice.h" +#include "inputDeviceSet.h" + +#ifdef _WIN32 +#include "xInputDevice.h" +class WinRawInputDevice; +#endif + +/** + * This class keeps track of all the devices on a system, and sends out events + * when a device has been hot-plugged. + */ +class EXPCL_PANDA_DEVICE InputDeviceManager { +protected: + InputDeviceManager(); + ~InputDeviceManager() = default; + + static void make_global_ptr(); + +PUBLISHED: + InputDeviceSet get_devices() const; + InputDeviceSet get_devices(InputDevice::DeviceClass device_class) const; + + void add_device(InputDevice *device); + void remove_device(InputDevice *device); + + virtual void update(); + + INLINE static InputDeviceManager *get_global_ptr(); + +protected: + LightMutex _lock; + +#ifdef PHAVE_LINUX_INPUT_H + InputDeviceSet _inactive_devices; +#endif + + InputDeviceSet _connected_devices; + + static InputDeviceManager *_global_ptr; +}; + +#include "inputDeviceManager.I" + +#endif diff --git a/panda/src/device/inputDeviceNode.cxx b/panda/src/device/inputDeviceNode.cxx new file mode 100644 index 0000000000..bdf849b93b --- /dev/null +++ b/panda/src/device/inputDeviceNode.cxx @@ -0,0 +1,79 @@ +/** + * 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 InputDeviceNode.cxx + * @author fireclaw + * @date 2016-07-14 + */ + +#include "config_device.h" +#include "inputDeviceNode.h" +#include "dataNodeTransmit.h" +#include "inputDeviceManager.h" + +TypeHandle InputDeviceNode::_type_handle; + +InputDeviceNode:: +InputDeviceNode(InputDevice *device, const std::string &name) : + DataNode(name), + _device(device) +{ + _button_events_output = define_output("button_events", ButtonEventList::get_class_type()); +} + +/** + * Redirects the class to get the data from a different device. + */ +void InputDeviceNode:: +set_device(InputDevice *device) { + _device = device; +} + +/** + * Returns the associated device. + */ +PT(InputDevice) InputDeviceNode:: +get_device() const { + return _device; +} + +/** + * The virtual implementation of transmit_data(). This function receives an + * array of input parameters and should generate an array of output + * parameters. The input parameters may be accessed with the index numbers + * returned by the define_input() calls that were made earlier (presumably in + * the constructor); likewise, the output parameters should be set with the + * index numbers returned by the define_output() calls. + */ +void InputDeviceNode:: +do_transmit_data(DataGraphTraverser *, const DataNodeTransmit &, + DataNodeTransmit &output) { + + if (_device == nullptr && !_device->is_connected()) { + return; + } + + _device->poll(); + + // get all button events of the device and forward them to the data graph + if (_device->has_button_event()) { + PT(ButtonEventList) bel = _device->get_button_events(); + + // Register the current state for each button. + for (int i = 0; i < bel->get_num_events(); ++i) { + const ButtonEvent &event = bel->get_event(i); + if (event._type == ButtonEvent::T_down) { + _button_states[event._button] = true; + } else if (event._type == ButtonEvent::T_down) { + _button_states[event._button] = false; + } + } + + output.set_data(_button_events_output, EventParameter(bel)); + } +} diff --git a/panda/src/awesomium/AwMouseAndKeyboard.h b/panda/src/device/inputDeviceNode.h similarity index 51% rename from panda/src/awesomium/AwMouseAndKeyboard.h rename to panda/src/device/inputDeviceNode.h index e338939001..7fad39e5ea 100644 --- a/panda/src/awesomium/AwMouseAndKeyboard.h +++ b/panda/src/device/inputDeviceNode.h @@ -6,33 +6,36 @@ * license. You should have received a copy of this license along * with this source code in a file named "LICENSE." * - * @file AwMouseAndKeyboard.h - * @author rurbino - * @date 2009-10-12 + * @file InputDeviceNode.h + * @author fireclaw + * @date 2016-07-14 */ -#ifndef AWWEBKEYBOARDMOUSE_H -#define AWWEBKEYBOARDMOUSE_H +#ifndef INPUTDEVICENODE_H +#define INPUTDEVICENODE_H #include "pandabase.h" -#include "typedReferenceCount.h" -#include "luse.h" - -#include "mouseAndKeyboard.h" +#include "dataNode.h" +#include "inputDeviceManager.h" +#include "linmath_events.h" /** - * Thin wrappings arround WebCore.h + * Reads the controller data sent from the InputDeviceManager, and transmits + * it down the data graph. + * + * This is intended to only be accessed from the app thread. */ -class EXPCL_PANDAAWESOMIUM AwMouseAndKeyboard : public DataNode { -// member data data -protected: - // inputs adn output indices... initialized in constructor - int _button_events_input; - int _button_events_output; +class EXPCL_PANDA_DEVICE InputDeviceNode : public DataNode { +PUBLISHED: + InputDeviceNode(InputDevice *device, const std::string &name); + +public: + void set_device(InputDevice *device); + PT(InputDevice) get_device() const; PUBLISHED: - AwMouseAndKeyboard(const std::string &name); + MAKE_PROPERTY(device, get_device, set_device); protected: // Inherited from DataNode @@ -40,13 +43,22 @@ protected: const DataNodeTransmit &input, DataNodeTransmit &output); +private: + pmap _button_states; + pmap _control_states; + + // outputs + int _button_events_output; + + PT(InputDevice) _device; + public: static TypeHandle get_class_type() { return _type_handle; } static void init_type() { - MouseAndKeyboard::init_type(); - register_type(_type_handle, "AwMouseAndKeyboard", + DataNode::init_type(); + register_type(_type_handle, "InputDeviceNode", DataNode::get_class_type()); } virtual TypeHandle get_type() const { @@ -58,4 +70,4 @@ private: static TypeHandle _type_handle; }; -#endif +#endif // INPUTDEVICENODE_H diff --git a/panda/src/device/inputDeviceSet.I b/panda/src/device/inputDeviceSet.I new file mode 100755 index 0000000000..f266055407 --- /dev/null +++ b/panda/src/device/inputDeviceSet.I @@ -0,0 +1,36 @@ +/** + * 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 inputDeviceSet.I + * @author rdb + * @date 2015-12-16 + */ + +/** + * + */ +INLINE InputDeviceSet:: +~InputDeviceSet() { +} + +/** + * Returns the nth InputDevice in the collection. + */ +INLINE InputDevice *InputDeviceSet:: +operator [] (size_t index) const { + nassertr(index < _devices.size(), nullptr); + return _devices[index]; +} + +/** + * Returns the number of devices in the collection. + */ +INLINE size_t InputDeviceSet:: +size() const { + return _devices.size(); +} diff --git a/panda/src/device/inputDeviceSet.cxx b/panda/src/device/inputDeviceSet.cxx new file mode 100755 index 0000000000..9aa27c6df4 --- /dev/null +++ b/panda/src/device/inputDeviceSet.cxx @@ -0,0 +1,141 @@ +/** + * 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 inputDeviceSet.cxx + * @author rdb + * @date 2015-12-16 + */ + +#include "inputDeviceSet.h" +#include "indent.h" + +/** + * + */ +InputDeviceSet:: +InputDeviceSet() { +} + +/** + * + */ +InputDeviceSet:: +InputDeviceSet(const InputDeviceSet ©) : + _devices(copy._devices) +{ +} + +/** + * + */ +void InputDeviceSet:: +operator = (const InputDeviceSet ©) { + _devices = copy._devices; +} + +/** + * Adds a new InputDevice to the collection. + */ +void InputDeviceSet:: +add_device(InputDevice *device) { + _devices.insert(device); +} + +/** + * Removes the indicated InputDevice from the collection. + * @return true if the device was removed, false if it was not a member. + */ +bool InputDeviceSet:: +remove_device(InputDevice *device) { + return _devices.erase(device) > 0; +} + +/** + * Adds all the InputDevices indicated in the other collection to this set. + */ +void InputDeviceSet:: +add_devices_from(const InputDeviceSet &other) { + size_t other_num_devices = other.size(); + for (size_t i = 0; i < other_num_devices; i++) { + _devices.push_back(other[i]); + } + _devices.sort(); +} + +/** + * Removes from this collection all of the devices listed in the other set. + */ +void InputDeviceSet:: +remove_devices_from(const InputDeviceSet &other) { + InputDevices new_devices; + InputDevices::const_iterator it; + for (it = _devices.begin(); it != _devices.end(); ++it) { + if (!other.has_device(*it)) { + new_devices.push_back(*it); + } + } + new_devices.sort(); + _devices.swap(new_devices); +} + +/** + * Returns true if the indicated InputDevice appears in this collection, false + * otherwise. + */ +bool InputDeviceSet:: +has_device(InputDevice *device) const { + InputDevices::const_iterator it = _devices.find(device); + return (it != _devices.end()); +} + +/** + * Removes all InputDevices from the collection. + */ +void InputDeviceSet:: +clear() { + _devices.clear(); +} + +/** + * This is a hint to Panda to allocate enough memory to hold the given number + * of InputDevices, if you know ahead of time how many you will be adding. + */ +void InputDeviceSet:: +reserve(size_t num) { + _devices.reserve(num); +} + +/** + * Writes a brief one-line description of the InputDeviceSet to the indicated + * output stream. + */ +void InputDeviceSet:: +output(std::ostream &out) const { + if (_devices.size() == 1) { + out << "1 input device"; + } else { + out << _devices.size() << " input devices"; + } +} + +/** + * Writes a complete multi-line description of the InputDeviceSet to the + * indicated output stream. + */ +void InputDeviceSet:: +write(std::ostream &out, int indent_level) const { + output(indent(out, indent_level)); + out << ":\n"; + indent_level += 2; + + InputDevices::const_iterator it; + for (it = _devices.begin(); it != _devices.end(); ++it) { + (*it)->output(indent(out, indent_level)); + out << '\n'; + } +} diff --git a/panda/src/device/inputDeviceSet.h b/panda/src/device/inputDeviceSet.h new file mode 100755 index 0000000000..e9dfdef366 --- /dev/null +++ b/panda/src/device/inputDeviceSet.h @@ -0,0 +1,64 @@ +/** + * 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 inputDeviceSet.h + * @author rdb + * @date 2015-12-16 + */ + +#ifndef INPUTDEVICESET_H +#define INPUTDEVICESET_H + +#include "pandabase.h" +#include "ordered_vector.h" +#include "inputDevice.h" + +/** + * Manages a list of InputDevice objects, as returned by various + * InputDeviceManager methods. This is implemented like a set, meaning the + * same device cannot occur more than once. + */ +class EXPCL_PANDA_DEVICE InputDeviceSet { +PUBLISHED: + InputDeviceSet(); + InputDeviceSet(const InputDeviceSet ©); + void operator = (const InputDeviceSet ©); + INLINE ~InputDeviceSet(); + +public: + void add_device(InputDevice *device); + bool remove_device(InputDevice *device); + void add_devices_from(const InputDeviceSet &other); + void remove_devices_from(const InputDeviceSet &other); + bool has_device(InputDevice *device) const; + +PUBLISHED: + void clear(); + void reserve(size_t num); + + INLINE InputDevice *operator [] (size_t index) const; + INLINE size_t size() const; + + void output(std::ostream &out) const; + void write(std::ostream &out, int indent_level = 0) const; + +private: + // This is currently implemented as ov_set instead of a regular set so that + // we can still support random access. + typedef ov_set InputDevices; + InputDevices _devices; +}; + +INLINE std::ostream &operator << (std::ostream &out, const InputDeviceSet &col) { + col.output(out); + return out; +} + +#include "inputDeviceSet.I" + +#endif diff --git a/panda/src/device/ioKitInputDevice.cxx b/panda/src/device/ioKitInputDevice.cxx new file mode 100644 index 0000000000..da1369932e --- /dev/null +++ b/panda/src/device/ioKitInputDevice.cxx @@ -0,0 +1,798 @@ +/** + * 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 ioKitInputDevice.cxx + * @author rdb + * @date 2017-12-21 + */ + +#include "ioKitInputDevice.h" + +#if defined(__APPLE__) && !defined(CPPPARSER) + +#include + +#include "keyboardButton.h" +#include "gamepadButton.h" +#include "mouseButton.h" + +static void removal_callback(void *ctx, IOReturn result, void *sender) { + IOKitInputDevice *input_device = (IOKitInputDevice *)ctx; + nassertv(input_device != nullptr); + input_device->on_remove(); +} + +/** + * Protected constructor. + */ +IOKitInputDevice:: +IOKitInputDevice(IOHIDDeviceRef device) : + _device(device), + _hat_element(nullptr), + _pointer_x(nullptr), + _pointer_y(nullptr), + _scroll_wheel(nullptr), + _pointer_x_timestamp(0), + _pointer_y_timestamp(0), + _scroll_wheel_timestamp(0) { + nassertv(device); + + char buffer[4096]; + + CFStringRef name = (CFStringRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey)); + if (name) { + buffer[0] = 0; + CFStringGetCString(name, buffer, sizeof(buffer), kCFStringEncodingUTF8); + + // Strip trailing spaces. + size_t len = strlen(buffer); + while (isspace(buffer[len - 1])) { + --len; + } + _name.assign(buffer, len); + } + + CFStringRef mfg = (CFStringRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDManufacturerKey)); + if (mfg) { + CFStringGetCString(mfg, buffer, sizeof(buffer), kCFStringEncodingUTF8); + _manufacturer = buffer; + } + + CFStringRef serial = (CFStringRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDSerialNumberKey)); + if (serial) { + CFStringGetCString(serial, buffer, sizeof(buffer), kCFStringEncodingUTF8); + _serial_number = buffer; + } + + CFNumberRef vendor = (CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey)); + if (vendor) { + int32_t value = 0; + CFNumberGetValue(vendor, kCFNumberSInt32Type, &value); + _vendor_id = (unsigned short)value; + } + + CFNumberRef product = (CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductIDKey)); + if (product) { + int32_t value = 0; + CFNumberGetValue(product, kCFNumberSInt32Type, &value); + _product_id = (unsigned short)value; + } + + if (IOHIDDeviceConformsTo(device, kHIDPage_GenericDesktop, kHIDUsage_GD_Mouse)) { + _device_class = DeviceClass::mouse; + } else if (IOHIDDeviceConformsTo(device, kHIDPage_GenericDesktop, kHIDUsage_GD_Keyboard)) { + _device_class = DeviceClass::keyboard; + } else if (IOHIDDeviceConformsTo(device, kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad)) { + _device_class = DeviceClass::gamepad; + } else if (IOHIDDeviceConformsTo(device, kHIDPage_Simulation, kHIDUsage_Sim_FlightStick)) { + _device_class = DeviceClass::flight_stick; + } else if (IOHIDDeviceConformsTo(device, kHIDPage_Simulation, kHIDUsage_GD_Joystick)) { + _device_class = DeviceClass::flight_stick; + } else if (_vendor_id == 0x044f && _product_id == 0xb108) { + // T.Flight Hotas X + _device_class = DeviceClass::flight_stick; + } else if (_vendor_id == 0x046d && + (_product_id == 0xc623 || + _product_id == 0xc625 || + _product_id == 0xc626 || + _product_id == 0xc627 || + _product_id == 0xc628 || + _product_id == 0xc629 || + _product_id == 0xc62b)) { + // 3Dconnexion SpaceNavigator and friends. + _device_class = DeviceClass::spatial_mouse; + } else if (_name == "usb gamepad") { + _device_class = DeviceClass::gamepad; + } + + CFArrayRef elements = IOHIDDeviceCopyMatchingElements(device, nullptr, 0); + CFIndex count = CFArrayGetCount(elements); + for (CFIndex i = 0; i < count; ++i) { + IOHIDElementRef element = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, i); + parse_element(element); + } + CFRelease(elements); + + if (_hat_element != nullptr) { + _hat_left_button = (int)_buttons.size(); + _buttons.push_back(ButtonState(GamepadButton::hat_left())); + _buttons.push_back(ButtonState(GamepadButton::hat_right())); + _buttons.push_back(ButtonState(GamepadButton::hat_down())); + _buttons.push_back(ButtonState(GamepadButton::hat_up())); + } + + if (_pointer_x != nullptr && _pointer_y != nullptr) { + enable_feature(Feature::pointer); + add_pointer(PointerType::unknown, 0); + } + + _is_connected = true; + IOHIDDeviceRegisterRemovalCallback(device, removal_callback, this); +} + +/** + * + */ +IOKitInputDevice:: +~IOKitInputDevice() { +} + +/** + * The nonstatic version of on_remove_device. + */ +void IOKitInputDevice:: +on_remove() { + { + LightMutexHolder holder(_lock); + if (!_is_connected) { + return; + } + _is_connected = false; + } + + if (device_cat.is_debug()) { + device_cat.debug() + << "Removed input device " << *this << "\n"; + } + + IOHIDDeviceClose(_device, kIOHIDOptionsTypeNone); + + InputDeviceManager *mgr = InputDeviceManager::get_global_ptr(); + nassertv(mgr != nullptr); + mgr->remove_device(this); +} + +/** + * + */ +void IOKitInputDevice:: +parse_element(IOHIDElementRef element) { + ButtonHandle handle = ButtonHandle::none(); + Axis axis = Axis::none; + uint32_t page = IOHIDElementGetUsagePage(element); + uint32_t usage = IOHIDElementGetUsage(element); + + switch (IOHIDElementGetType(element)) { + case kIOHIDElementTypeInput_Misc: + switch (page) { + case kHIDPage_GenericDesktop: + switch (usage) { + case kHIDUsage_GD_X: + if (_device_class == DeviceClass::gamepad) { + axis = Axis::left_x; + } else if (_device_class == DeviceClass::flight_stick) { + axis = Axis::roll; + } else if (_device_class == DeviceClass::mouse) { + _pointer_x = element; + return; + } else { + axis = Axis::x; + } + break; + case kHIDUsage_GD_Y: + if (_device_class == DeviceClass::gamepad) { + axis = Axis::left_y; + } else if (_device_class == DeviceClass::flight_stick) { + axis = Axis::pitch; + } else if (_device_class == DeviceClass::mouse) { + _pointer_y = element; + return; + } else { + axis = Axis::y; + } + break; + case kHIDUsage_GD_Z: + if (_device_class == DeviceClass::gamepad) { + axis = Axis::left_trigger; + } else if (_device_class == DeviceClass::flight_stick) { + axis = Axis::throttle; + } else { + axis = Axis::z; + } + break; + case kHIDUsage_GD_Rx: + if (_device_class == DeviceClass::gamepad) { + axis = Axis::right_x; + } else { + axis = Axis::pitch; + } + break; + case kHIDUsage_GD_Ry: + if (_device_class == DeviceClass::gamepad) { + axis = Axis::right_y; + } else { + axis = Axis::roll; + } + break; + case kHIDUsage_GD_Rz: + if (_device_class == DeviceClass::gamepad) { + axis = Axis::right_trigger; + } else { + axis = Axis::yaw; + } + break; + case kHIDUsage_GD_Slider: + axis = Axis::rudder; + break; + case kHIDUsage_GD_Dial: + break; + case kHIDUsage_GD_Wheel: + _scroll_wheel = element; + return; + case kHIDUsage_GD_Hatswitch: + _hat_element = element; + return; + case kHIDUsage_GD_DPadUp: + handle = GamepadButton::dpad_up(); + break; + case kHIDUsage_GD_DPadDown: + handle = GamepadButton::dpad_down(); + break; + case kHIDUsage_GD_DPadRight: + handle = GamepadButton::dpad_right(); + break; + case kHIDUsage_GD_DPadLeft: + handle = GamepadButton::dpad_left(); + break; + case 0xffffffffu: + return; + default: + break; + } + break; + + case kHIDPage_Simulation: + switch (usage) { + case kHIDUsage_Sim_Rudder: + axis = Axis::rudder; + break; + case kHIDUsage_Sim_Throttle: + axis = Axis::throttle; + break; + case kHIDUsage_Sim_Accelerator: + axis = Axis::accelerator; + break; + case kHIDUsage_Sim_Brake: + axis = Axis::brake; + break; + } + break; + } + if (axis != Axis::none) { + int min = IOHIDElementGetLogicalMin(element); + int max = IOHIDElementGetLogicalMax(element); + if (_vendor_id == 0x044f && _product_id == 0xb108 && axis == Axis::throttle) { + // T.Flight Hotas X throttle is reversed and can go backwards. + add_axis(axis, max, min, true); + } else if (axis == Axis::yaw || axis == Axis::rudder || axis == Axis::left_y || axis == Axis::right_y || + (_device_class == DeviceClass::spatial_mouse && (axis == Axis::y || axis == Axis::z || axis == Axis::roll))) { + // We'd like to reverse the Y axis to match the XInput behavior. + // We also reverse yaw to obey the right-hand rule. + add_axis(axis, max, min); + } else { + add_axis(axis, min, max); + } + + _analog_elements.push_back(element); + } + break; + + case kIOHIDElementTypeInput_Button: + switch (page) { + case kHIDPage_GenericDesktop: + switch (usage) { + case kHIDUsage_GD_DPadUp: + handle = GamepadButton::dpad_up(); + break; + case kHIDUsage_GD_DPadDown: + handle = GamepadButton::dpad_down(); + break; + case kHIDUsage_GD_DPadRight: + handle = GamepadButton::dpad_right(); + break; + case kHIDUsage_GD_DPadLeft: + handle = GamepadButton::dpad_left(); + break; + default: + break; + } + break; + + case kHIDPage_KeyboardOrKeypad: + switch (usage) { + case kHIDUsage_KeyboardA: + case kHIDUsage_KeyboardB: + case kHIDUsage_KeyboardC: + case kHIDUsage_KeyboardD: + case kHIDUsage_KeyboardE: + case kHIDUsage_KeyboardF: + case kHIDUsage_KeyboardG: + case kHIDUsage_KeyboardH: + case kHIDUsage_KeyboardI: + case kHIDUsage_KeyboardJ: + case kHIDUsage_KeyboardK: + case kHIDUsage_KeyboardL: + case kHIDUsage_KeyboardM: + case kHIDUsage_KeyboardN: + case kHIDUsage_KeyboardO: + case kHIDUsage_KeyboardP: + case kHIDUsage_KeyboardQ: + case kHIDUsage_KeyboardR: + case kHIDUsage_KeyboardS: + case kHIDUsage_KeyboardT: + case kHIDUsage_KeyboardU: + case kHIDUsage_KeyboardV: + case kHIDUsage_KeyboardW: + case kHIDUsage_KeyboardX: + case kHIDUsage_KeyboardY: + case kHIDUsage_KeyboardZ: + handle = KeyboardButton::ascii_key('a' + (usage - kHIDUsage_KeyboardA)); + break; + case kHIDUsage_Keyboard1: + handle = KeyboardButton::ascii_key('1'); + break; + case kHIDUsage_Keyboard2: + handle = KeyboardButton::ascii_key('2'); + break; + case kHIDUsage_Keyboard3: + handle = KeyboardButton::ascii_key('3'); + break; + case kHIDUsage_Keyboard4: + handle = KeyboardButton::ascii_key('4'); + break; + case kHIDUsage_Keyboard5: + handle = KeyboardButton::ascii_key('5'); + break; + case kHIDUsage_Keyboard6: + handle = KeyboardButton::ascii_key('6'); + break; + case kHIDUsage_Keyboard7: + handle = KeyboardButton::ascii_key('7'); + break; + case kHIDUsage_Keyboard8: + handle = KeyboardButton::ascii_key('8'); + break; + case kHIDUsage_Keyboard9: + handle = KeyboardButton::ascii_key('9'); + break; + case kHIDUsage_Keyboard0: + handle = KeyboardButton::ascii_key('0'); + break; + case kHIDUsage_KeyboardReturnOrEnter: + handle = KeyboardButton::enter(); + break; + case kHIDUsage_KeyboardEscape: + handle = KeyboardButton::escape(); + break; + case kHIDUsage_KeyboardDeleteOrBackspace: + handle = KeyboardButton::backspace(); + break; + case kHIDUsage_KeyboardTab: + handle = KeyboardButton::tab(); + break; + case kHIDUsage_KeyboardSpacebar: + handle = KeyboardButton::ascii_key(' '); + break; + case kHIDUsage_KeyboardHyphen: + handle = KeyboardButton::ascii_key('-'); + break; + case kHIDUsage_KeyboardEqualSign: + handle = KeyboardButton::ascii_key('='); + break; + case kHIDUsage_KeyboardOpenBracket: + handle = KeyboardButton::ascii_key('['); + break; + case kHIDUsage_KeyboardCloseBracket: + handle = KeyboardButton::ascii_key(']'); + break; + case kHIDUsage_KeyboardBackslash: + handle = KeyboardButton::ascii_key('\\'); + break; + case kHIDUsage_KeyboardNonUSPound: + handle = KeyboardButton::ascii_key('$'); + break; + case kHIDUsage_KeyboardSemicolon: + handle = KeyboardButton::ascii_key(';'); + break; + case kHIDUsage_KeyboardQuote: + handle = KeyboardButton::ascii_key('\''); + break; + case kHIDUsage_KeyboardGraveAccentAndTilde: + handle = KeyboardButton::ascii_key('`'); + break; + case kHIDUsage_KeyboardComma: + handle = KeyboardButton::ascii_key(','); + break; + case kHIDUsage_KeyboardPeriod: + handle = KeyboardButton::ascii_key('.'); + break; + case kHIDUsage_KeyboardSlash: + handle = KeyboardButton::ascii_key('/'); + break; + case kHIDUsage_KeyboardCapsLock: + handle = KeyboardButton::caps_lock(); + break; + case kHIDUsage_KeyboardF1: + handle = KeyboardButton::f1(); + break; + case kHIDUsage_KeyboardF2: + handle = KeyboardButton::f2(); + break; + case kHIDUsage_KeyboardF3: + handle = KeyboardButton::f3(); + break; + case kHIDUsage_KeyboardF4: + handle = KeyboardButton::f4(); + break; + case kHIDUsage_KeyboardF5: + handle = KeyboardButton::f5(); + break; + case kHIDUsage_KeyboardF6: + handle = KeyboardButton::f6(); + break; + case kHIDUsage_KeyboardF7: + handle = KeyboardButton::f7(); + break; + case kHIDUsage_KeyboardF8: + handle = KeyboardButton::f8(); + break; + case kHIDUsage_KeyboardF9: + handle = KeyboardButton::f9(); + break; + case kHIDUsage_KeyboardF10: + handle = KeyboardButton::f10(); + break; + case kHIDUsage_KeyboardF11: + handle = KeyboardButton::f11(); + break; + case kHIDUsage_KeyboardF12: + handle = KeyboardButton::f12(); + break; + case kHIDUsage_KeyboardPrintScreen: + handle = KeyboardButton::print_screen(); + break; + case kHIDUsage_KeyboardScrollLock: + handle = KeyboardButton::scroll_lock(); + break; + case kHIDUsage_KeyboardPause: + handle = KeyboardButton::pause(); + break; + case kHIDUsage_KeyboardInsert: + handle = KeyboardButton::insert(); + break; + case kHIDUsage_KeyboardHome: + handle = KeyboardButton::home(); + break; + case kHIDUsage_KeyboardPageUp: + handle = KeyboardButton::page_up(); + break; + case kHIDUsage_KeyboardDeleteForward: + handle = KeyboardButton::del(); + break; + case kHIDUsage_KeyboardEnd: + handle = KeyboardButton::end(); + break; + case kHIDUsage_KeyboardPageDown: + handle = KeyboardButton::page_down(); + break; + case kHIDUsage_KeyboardRightArrow: + handle = KeyboardButton::right(); + break; + case kHIDUsage_KeyboardLeftArrow: + handle = KeyboardButton::left(); + break; + case kHIDUsage_KeyboardDownArrow: + handle = KeyboardButton::down(); + break; + case kHIDUsage_KeyboardUpArrow: + handle = KeyboardButton::up(); + break; + case kHIDUsage_KeypadNumLock: + handle = KeyboardButton::num_lock(); + break; + case kHIDUsage_KeypadSlash: + handle = KeyboardButton::ascii_key('/'); + break; + case kHIDUsage_KeypadAsterisk: + handle = KeyboardButton::ascii_key('*'); + break; + case kHIDUsage_KeypadHyphen: + handle = KeyboardButton::ascii_key('-'); + break; + case kHIDUsage_KeypadPlus: + handle = KeyboardButton::ascii_key('+'); + break; + case kHIDUsage_KeypadEnter: + handle = KeyboardButton::enter(); + break; + case kHIDUsage_Keypad1: + handle = KeyboardButton::ascii_key('1'); + break; + case kHIDUsage_Keypad2: + handle = KeyboardButton::ascii_key('2'); + break; + case kHIDUsage_Keypad3: + handle = KeyboardButton::ascii_key('3'); + break; + case kHIDUsage_Keypad4: + handle = KeyboardButton::ascii_key('4'); + break; + case kHIDUsage_Keypad5: + handle = KeyboardButton::ascii_key('5'); + break; + case kHIDUsage_Keypad6: + handle = KeyboardButton::ascii_key('6'); + break; + case kHIDUsage_Keypad7: + handle = KeyboardButton::ascii_key('7'); + break; + case kHIDUsage_Keypad8: + handle = KeyboardButton::ascii_key('8'); + break; + case kHIDUsage_Keypad9: + handle = KeyboardButton::ascii_key('9'); + break; + case kHIDUsage_Keypad0: + handle = KeyboardButton::ascii_key('0'); + break; + case kHIDUsage_KeypadPeriod: + handle = KeyboardButton::ascii_key('.'); + break; + case kHIDUsage_KeyboardNonUSBackslash: + handle = KeyboardButton::ascii_key('\\'); + break; + case kHIDUsage_KeypadEqualSign: + handle = KeyboardButton::ascii_key('='); + break; + case kHIDUsage_KeyboardF13: + handle = KeyboardButton::f13(); + break; + case kHIDUsage_KeyboardF14: + handle = KeyboardButton::f14(); + break; + case kHIDUsage_KeyboardF15: + handle = KeyboardButton::f15(); + break; + case kHIDUsage_KeyboardF16: + handle = KeyboardButton::f16(); + break; + case kHIDUsage_KeyboardExecute: + break; + case kHIDUsage_KeyboardHelp: + handle = KeyboardButton::help(); + break; + case kHIDUsage_KeyboardMenu: + handle = KeyboardButton::menu(); + break; + case kHIDUsage_KeypadComma: + handle = KeyboardButton::ascii_key(','); + break; + case kHIDUsage_KeypadEqualSignAS400: + handle = KeyboardButton::ascii_key('='); + break; + case kHIDUsage_KeyboardReturn: + handle = KeyboardButton::enter(); + break; + case kHIDUsage_KeyboardLeftControl: + handle = KeyboardButton::lcontrol(); + break; + case kHIDUsage_KeyboardLeftShift: + handle = KeyboardButton::lshift(); + break; + case kHIDUsage_KeyboardLeftAlt: + handle = KeyboardButton::lalt(); + break; + case kHIDUsage_KeyboardLeftGUI: + handle = KeyboardButton::lmeta(); + break; + case kHIDUsage_KeyboardRightControl: + handle = KeyboardButton::rcontrol(); + break; + case kHIDUsage_KeyboardRightShift: + handle = KeyboardButton::rshift(); + break; + case kHIDUsage_KeyboardRightAlt: + handle = KeyboardButton::ralt(); + break; + case kHIDUsage_KeyboardRightGUI: + handle = KeyboardButton::rmeta(); + break; + default: + break; + } + break; + + case kHIDPage_Button: + if (_device_class == DeviceClass::gamepad) { + if (_vendor_id == 0x0810 && _product_id == 0xe501) { + // SNES-style USB gamepad + static const ButtonHandle gamepad_buttons[] = { + ButtonHandle::none(), + GamepadButton::face_x(), + GamepadButton::face_a(), + GamepadButton::face_b(), + GamepadButton::face_y(), + GamepadButton::lshoulder(), + GamepadButton::rshoulder(), + ButtonHandle::none(), + ButtonHandle::none(), + GamepadButton::back(), + GamepadButton::start(), + }; + if (usage < sizeof(gamepad_buttons) / sizeof(ButtonHandle)) { + handle = gamepad_buttons[usage]; + } + } else { + // These seem to be the button mappings exposed by the 360Controller + // driver. I don't know if other drivers do the same thing at all. + static const ButtonHandle gamepad_buttons[] = { + ButtonHandle::none(), + GamepadButton::face_a(), + GamepadButton::face_b(), + GamepadButton::face_x(), + GamepadButton::face_y(), + GamepadButton::lshoulder(), + GamepadButton::rshoulder(), + GamepadButton::lstick(), + GamepadButton::rstick(), + GamepadButton::start(), + GamepadButton::back(), + GamepadButton::guide(), + GamepadButton::dpad_up(), + GamepadButton::dpad_down(), + GamepadButton::dpad_left(), + GamepadButton::dpad_right(), + }; + if (usage < sizeof(gamepad_buttons) / sizeof(ButtonHandle)) { + handle = gamepad_buttons[usage]; + } + } + } else if (_device_class == DeviceClass::flight_stick) { + if (usage > 0) { + handle = GamepadButton::joystick(usage - 1); + } + } else if (_device_class == DeviceClass::mouse) { + // In Panda, wheel and right button are flipped around... + int button = (usage == 2 || usage == 3) ? (4 - usage) : (usage - 1); + handle = MouseButton::button(button); + } + break; + } + _buttons.push_back(ButtonState(handle)); + _button_elements.push_back(element); + break; + + case kIOHIDElementTypeInput_Axis: + break; + + case kIOHIDElementTypeInput_ScanCodes: + break; + + case kIOHIDElementTypeOutput: + break; + + case kIOHIDElementTypeFeature: + break; + + case kIOHIDElementTypeCollection: + { + // This doesn't seem to be necessary and instead leads to duplication of + // axes and buttons. + /* + CFArrayRef children = IOHIDElementGetChildren(element); + CFIndex count = CFArrayGetCount(children); + for (CFIndex i = 0; i < count; ++i) { + IOHIDElementRef element = (IOHIDElementRef)CFArrayGetValueAtIndex(children, i); + parse_element(element, depth + 2); + }*/ + } + break; + } +} + +/** + * Polls the input device for new activity, to ensure it contains the latest + * events. This will only have any effect for some types of input devices; + * others may be updated automatically, and this method will be a no-op. + */ +void IOKitInputDevice:: +do_poll() { + for (size_t i = 0; i < _button_elements.size(); ++i) { + IOHIDValueRef value_ref; + if (!_button_elements[i]) continue; + if (IOHIDDeviceGetValue(_device, _button_elements[i], &value_ref) == kIOReturnSuccess) { + int value = IOHIDValueGetIntegerValue(value_ref); + button_changed(i, value != 0); + } + } + + for (size_t i = 0; i < _analog_elements.size(); ++i) { + IOHIDValueRef value_ref; + if (IOHIDDeviceGetValue(_device, _analog_elements[i], &value_ref) == kIOReturnSuccess) { + int value = IOHIDValueGetIntegerValue(value_ref); + axis_changed(i, value); + } + } + + if (_hat_element != nullptr) { + IOHIDValueRef value_ref; + if (IOHIDDeviceGetValue(_device, _hat_element, &value_ref) == kIOReturnSuccess) { + int value = IOHIDValueGetIntegerValue(value_ref); + button_changed(_hat_left_button + 0, value >= 5 && value <= 7); // left + button_changed(_hat_left_button + 1, value >= 1 && value <= 3); // right + button_changed(_hat_left_button + 2, value >= 3 && value <= 5); // down + button_changed(_hat_left_button + 3, value == 7 || value == 0 || value == 1); // up + } + } + + int x = 0, y = 0; + if (_pointer_x != nullptr) { + IOHIDValueRef value_ref; + if (IOHIDDeviceGetValue(_device, _pointer_x, &value_ref) == kIOReturnSuccess) { + uint64_t timestamp = IOHIDValueGetTimeStamp(value_ref); + if (timestamp != _pointer_x_timestamp) { + x = IOHIDValueGetIntegerValue(value_ref); + _pointer_x_timestamp = timestamp; + } + } + } + if (_pointer_y != nullptr) { + IOHIDValueRef value_ref; + if (IOHIDDeviceGetValue(_device, _pointer_y, &value_ref) == kIOReturnSuccess) { + uint64_t timestamp = IOHIDValueGetTimeStamp(value_ref); + if (timestamp != _pointer_y_timestamp) { + y = IOHIDValueGetIntegerValue(value_ref); + _pointer_y_timestamp = timestamp; + } + } + } + if (x != 0 || y != 0) { + pointer_moved(0, x, y, ClockObject::get_global_clock()->get_frame_time()); + } + + // Do we have a scroll wheel axis? + if (_scroll_wheel != nullptr) { + IOHIDValueRef value_ref; + if (IOHIDDeviceGetValue(_device, _scroll_wheel, &value_ref) == kIOReturnSuccess) { + uint64_t timestamp = IOHIDValueGetTimeStamp(value_ref); + if (timestamp != _scroll_wheel_timestamp) { + int value = IOHIDValueGetIntegerValue(value_ref); + if (value != 0) { + // Just fire off a rapid down/up event. + double time = ClockObject::get_global_clock()->get_frame_time(); + ButtonHandle handle = (value > 0) ? MouseButton::wheel_up() : MouseButton::wheel_down(); + _button_events->add_event(ButtonEvent(handle, ButtonEvent::T_down, time)); + _button_events->add_event(ButtonEvent(handle, ButtonEvent::T_up, time)); + } + _scroll_wheel_timestamp = timestamp; + } + } + } +} + +#endif // __APPLE__ diff --git a/panda/src/device/ioKitInputDevice.h b/panda/src/device/ioKitInputDevice.h new file mode 100644 index 0000000000..f32f9c4256 --- /dev/null +++ b/panda/src/device/ioKitInputDevice.h @@ -0,0 +1,55 @@ +/** + * 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 ioKitInputDevice.h + * @author rdb + * @date 2017-12-21 + */ + +#ifndef IOKITINPUTDEVICE_H +#define IOKITINPUTDEVICE_H + +#include "pandabase.h" +#include "inputDevice.h" + +#if defined(__APPLE__) && !defined(CPPPARSER) + +#include + +/** + * This implementation uses the IOKit HID code that was introduced with macOS + * 10.5 to interface with USB HID devices. + */ +class EXPCL_PANDA_DEVICE IOKitInputDevice final : public InputDevice { +public: + IOKitInputDevice(IOHIDDeviceRef device); + ~IOKitInputDevice(); + + void on_remove(); + +private: + void parse_element(IOHIDElementRef element); + + virtual void do_poll(); + + IOHIDDeviceRef _device; + pvector _button_elements; + pvector _analog_elements; + IOHIDElementRef _hat_element; + int _hat_left_button; + IOHIDElementRef _pointer_x; + IOHIDElementRef _pointer_y; + IOHIDElementRef _scroll_wheel; + uint64_t _pointer_x_timestamp; + uint64_t _pointer_y_timestamp; + uint64_t _scroll_wheel_timestamp; +}; + +#endif // __APPLE__ + +#endif diff --git a/panda/src/device/ioKitInputDeviceManager.cxx b/panda/src/device/ioKitInputDeviceManager.cxx new file mode 100644 index 0000000000..6874d9baba --- /dev/null +++ b/panda/src/device/ioKitInputDeviceManager.cxx @@ -0,0 +1,93 @@ +/** + * 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 ioKitInputDeviceManager.cxx + * @author rdb + * @date 2018-02-04 + */ + +#include "ioKitInputDeviceManager.h" +#include "ioKitInputDevice.h" + +#if defined(__APPLE__) && !defined(CPPPARSER) + +/** + * Initializes the input device manager by scanning which devices are currently + * connected and setting up any platform-dependent structures necessary for + * listening for future device connect events. + */ +IOKitInputDeviceManager:: +IOKitInputDeviceManager() { + _hid_manager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); + if (!_hid_manager) { + device_cat.error() + << "Failed to create an IOHIDManager.\n"; + return; + } + + // The types of devices we're interested in. + int page = kHIDPage_GenericDesktop; + int usages[] = {kHIDUsage_GD_GamePad, + kHIDUsage_GD_Joystick, + kHIDUsage_GD_Mouse, + kHIDUsage_GD_Keyboard, + kHIDUsage_GD_MultiAxisController, 0}; + int *usage = usages; + + // This giant mess is necessary to create an array of match dictionaries + // that will match the devices we're interested in. + CFMutableArrayRef match = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + nassertv(match); + while (*usage) { + CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFNumberRef page_ref = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &page); + CFDictionarySetValue(dict, CFSTR(kIOHIDDeviceUsagePageKey), page_ref); + CFRelease(page_ref); + CFNumberRef usage_ref = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, usage); + CFDictionarySetValue(dict, CFSTR(kIOHIDDeviceUsageKey), usage_ref); + CFRelease(usage_ref); + CFArrayAppendValue(match, dict); + CFRelease(dict); + ++usage; + } + IOHIDManagerSetDeviceMatchingMultiple(_hid_manager, match); + CFRelease(match); + + IOHIDManagerRegisterDeviceMatchingCallback(_hid_manager, on_match_device, this); + IOHIDManagerScheduleWithRunLoop(_hid_manager, CFRunLoopGetMain(), kCFRunLoopCommonModes); + IOHIDManagerOpen(_hid_manager, kIOHIDOptionsTypeNone); +} + +/** + * Closes any resources that the device manager was using to listen for events. + */ +IOKitInputDeviceManager:: +~IOKitInputDeviceManager() { + IOHIDManagerUnscheduleFromRunLoop(_hid_manager, CFRunLoopGetMain(), kCFRunLoopCommonModes); + IOHIDManagerClose(_hid_manager, kIOHIDOptionsTypeNone); + CFRelease(_hid_manager); +} + +/** + * Called by IOKit when an input device matching our filters has been found. + */ +void IOKitInputDeviceManager:: +on_match_device(void *ctx, IOReturn result, void *sender, IOHIDDeviceRef device) { + InputDeviceManager *mgr = (InputDeviceManager *)ctx; + nassertv(mgr != nullptr); + nassertv(device); + + PT(InputDevice) input_device = new IOKitInputDevice(device); + if (device_cat.is_debug()) { + device_cat.debug() + << "Discovered input device " << *input_device << "\n"; + } + mgr->add_device(input_device); +} + +#endif diff --git a/panda/src/device/ioKitInputDeviceManager.h b/panda/src/device/ioKitInputDeviceManager.h new file mode 100644 index 0000000000..f028a5f153 --- /dev/null +++ b/panda/src/device/ioKitInputDeviceManager.h @@ -0,0 +1,40 @@ +/** + * 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 ioKitInputDeviceManager.h + * @author rdb + * @date 2018-02-04 + */ + +#ifndef IOKITINPUTDEVICEMANAGER_H +#define IOKITINPUTDEVICEMANAGER_H + +#include "inputDeviceManager.h" + +#if defined(__APPLE__) && !defined(CPPPARSER) +#include + +/** + * The macOS implementation of InputDeviceManager. + */ +class EXPCL_PANDA_DEVICE IOKitInputDeviceManager final : public InputDeviceManager { +protected: + IOKitInputDeviceManager(); + ~IOKitInputDeviceManager(); + +protected: + IOHIDManagerRef _hid_manager; + + static void on_match_device(void *ctx, IOReturn result, void *sender, IOHIDDeviceRef device); + + friend class InputDeviceManager; +}; + +#endif + +#endif diff --git a/panda/src/device/linuxInputDeviceManager.cxx b/panda/src/device/linuxInputDeviceManager.cxx new file mode 100644 index 0000000000..8eed3e1cc3 --- /dev/null +++ b/panda/src/device/linuxInputDeviceManager.cxx @@ -0,0 +1,370 @@ +/** + * 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 linuxInputDeviceManager.cxx + * @author rdb + * @date 2018-01-25 + */ + +#include "linuxInputDeviceManager.h" +#include "throw_event.h" + +#ifdef PHAVE_LINUX_INPUT_H + +#include "evdevInputDevice.h" +#include "linuxJoystickDevice.h" + +#include +#include +#include +#include + +/** + * Initializes the device manager by scanning which devices are currently + * connected and setting up any platform-dependent structures necessary for + * listening for future device connect events. + */ +LinuxInputDeviceManager:: +LinuxInputDeviceManager() { + // Use inotify to watch /dev/input for hotplugging of devices. + _inotify_fd = inotify_init(); + fcntl(_inotify_fd, F_SETFL, O_NONBLOCK); + fcntl(_inotify_fd, F_SETFD, FD_CLOEXEC); + + if (_inotify_fd < 0) { + device_cat.error() + << "Error initializing inotify: " << strerror(errno) << "\n"; + + } else if (inotify_add_watch(_inotify_fd, "/dev/input", IN_CREATE | IN_ATTRIB | IN_DELETE) < 0) { + device_cat.error() + << "Error adding inotify watch on /dev/input: " << strerror(errno) << "\n"; + } + + // Scan /dev/input for a list of input devices. + DIR *dir = opendir("/dev/input"); + if (dir) { + std::vector indices; + dirent *entry = readdir(dir); + while (entry != nullptr) { + size_t index; + if (entry->d_type == DT_CHR && sscanf(entry->d_name, "event%zd", &index) == 1) { + indices.push_back(index); + } + entry = readdir(dir); + } + closedir(dir); + + // We'll want to sort the devices by index, since the order may be + // meaningful (eg. for the Xbox wireless receiver). + std::sort(indices.begin(), indices.end()); + _evdev_devices.resize(indices.back() + 1, nullptr); + + for (size_t index : indices) { + consider_add_evdev_device(index); + } + } else { + device_cat.error() + << "Error opening directory /dev/input: " << strerror(errno) << "\n"; + return; + } +} + +/** + * Closes any resources that the device manager was using to listen for events. + */ +LinuxInputDeviceManager:: +~LinuxInputDeviceManager() { + if (_inotify_fd >= 0) { + close(_inotify_fd); + _inotify_fd = -1; + } +} + +/** + * Checks whether the event device with the given index is accessible, and if + * so, adds it. Returns the device if it was newly connected. + * + * This is the preferred interface for input devices on Linux. + */ +InputDevice *LinuxInputDeviceManager:: +consider_add_evdev_device(size_t ev_index) { + if (ev_index < _evdev_devices.size()) { + if (_evdev_devices[ev_index] != nullptr) { + // We already have this device. FIXME: probe it and add it to the + // list of connected devices? + return nullptr; + } + } else { + // Make room to store this index. + _evdev_devices.resize(ev_index + 1, nullptr); + } + + // Check if we can directly read the event device. + char path[64]; + sprintf(path, "/dev/input/event%zd", ev_index); + + if (access(path, R_OK) == 0) { + PT(InputDevice) device = new EvdevInputDevice(this, ev_index); + if (device_cat.is_debug()) { + device_cat.debug() + << "Discovered evdev input device " << *device << "\n"; + } + + _evdev_devices[ev_index] = device; + + if (device->is_connected()) { + _connected_devices.add_device(std::move(device)); + } else { + // Wait for activity on the device before it is considered connected. + _inactive_devices.add_device(std::move(device)); + } + return _evdev_devices[ev_index]; + } + + // Nope. The permissions haven't been configured to allow it. Check if this + // corresponds to a /dev/input/jsX interface, which has a better chance of + // having read permissions set, but doesn't export all of the features + // (notably, force feedback). + + // We do this by checking for a js# directory inside the sysfs directory. + sprintf(path, "/sys/class/input/event%zd/device", ev_index); + + DIR *dir = opendir(path); + if (dir == nullptr) { + if (device_cat.is_debug()) { + device_cat.debug() + << "Error opening directory " << path << ": " << strerror(errno) << "\n"; + } + return nullptr; + } + + dirent *entry = readdir(dir); + while (entry != nullptr) { + size_t js_index; + if (sscanf(entry->d_name, "js%zd", &js_index) == 1) { + // Yes, we fonud a corresponding js device. Try adding it. + closedir(dir); + + InputDevice *device = consider_add_js_device(js_index); + if (device != nullptr && device_cat.is_warning()) { + // This seemed to work. Display a warning to the user indicating + // that they might want to configure udev properly. + device_cat.warning() + << "/dev/input/event" << ev_index << " is not readable, some " + "features will be unavailable.\n"; + } + _evdev_devices[ev_index] = device; + return device; + } + entry = readdir(dir); + } + + closedir(dir); + return nullptr; +} + +/** + * Checks whether the joystick device with the given index is accessible, and + * if so, adds it. Returns the device if it was newly connected. + * + * This is only used on Linux as a fallback interface for when an evdev device + * cannot be accessed. + */ +InputDevice *LinuxInputDeviceManager:: +consider_add_js_device(size_t js_index) { + char path[64]; + sprintf(path, "/dev/input/js%zd", js_index); + + if (access(path, R_OK) == 0) { + PT(LinuxJoystickDevice) device = new LinuxJoystickDevice(this, js_index); + if (device_cat.is_debug()) { + device_cat.debug() + << "Discovered joydev input device " << *device << "\n"; + } + InputDevice *device_p = device.p(); + + if (device->is_connected()) { + _connected_devices.add_device(std::move(device)); + } else { + // Wait for activity on the device before it is considered connected. + _inactive_devices.add_device(std::move(device)); + } + return device_p; + } + + return nullptr; +} + +/** + * Scans the "virtual" input devices on the system to check whether one with + * the given vendor and product ID exists. + */ +bool LinuxInputDeviceManager:: +has_virtual_device(unsigned short vendor_id, unsigned short product_id) const { + char path[294]; + sprintf(path, "/sys/devices/virtual/input"); + + DIR *dir = opendir(path); + if (dir != nullptr) { + dirent *entry = readdir(dir); + while (entry != nullptr) { + if (entry->d_name[0] != 'i') { + entry = readdir(dir); + continue; + } + FILE *f; + + char vendor[5] = {0}; + sprintf(path, "/sys/devices/virtual/input/%s/id/vendor", entry->d_name); + f = fopen(path, "r"); + if (f) { + fgets(vendor, sizeof(vendor), f); + fclose(f); + } + + char product[5] = {0}; + sprintf(path, "/sys/devices/virtual/input/%s/id/product", entry->d_name); + f = fopen(path, "r"); + if (f) { + fgets(product, sizeof(product), f); + fclose(f); + } + + if (vendor[0] && std::stoi(std::string(vendor), nullptr, 16) == (int)vendor_id && + product[0] && std::stoi(std::string(product), nullptr, 16) == (int)product_id) { + closedir(dir); + return true; + } + + entry = readdir(dir); + } + closedir(dir); + } + + return false; +} + +/** + * Polls the system to see if there are any new devices. In some + * implementations this is a no-op. + */ +void LinuxInputDeviceManager:: +update() { + // Check for any devices that may be disconnected and need to be probed in + // order to see whether they have been reconnected. + InputDeviceSet inactive_devices; + { + LightMutexHolder holder(_lock); + inactive_devices = _inactive_devices; + } + for (size_t i = 0; i < inactive_devices.size(); ++i) { + InputDevice *device = inactive_devices[i]; + if (device != nullptr && !device->is_connected()) { + device->poll(); + } + } + + // We use inotify to tell us whether a device was added, removed, or has + // changed permissions to allow us to access it. + unsigned int avail = 0; + ioctl(_inotify_fd, FIONREAD, &avail); + if (avail == 0) { + return; + } + + char buffer[avail]; + int n_read = read(_inotify_fd, buffer, avail); + if (n_read < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + // No data available for now. + + } else { + device_cat.error() << "read: " << strerror(errno) << "\n"; + } + return; + } + + LightMutexHolder holder(_lock); + + // Iterate over the events in the buffer. + bool removed_steam_virtual_device = false; + char *ptr = buffer; + char *end = buffer + avail; + while (ptr < end) { + inotify_event *event = (inotify_event *)ptr; + + std::string name(event->name); + + if (event->mask & IN_DELETE) { + // The device was deleted. If we have it, remove it. + + size_t index; + if (sscanf(event->name, "event%zd", &index) == 1) { + // Check if we have this evdev device. If so, disconnect it. + if (index < _evdev_devices.size()) { + PT(InputDevice) device = _evdev_devices[index]; + if (device != nullptr) { + device->set_connected(false); + _evdev_devices[index] = nullptr; + _inactive_devices.remove_device(device); + if (_connected_devices.remove_device(device)) { + throw_event("disconnect-device", device.p()); + } + + if (device_cat.is_debug()) { + device_cat.debug() + << "Removed input device " << *device << "\n"; + } + + // Check for Steam virtual device; see comment below. + if (device->get_vendor_id() == 0x28de && + device->get_product_id() == 0x11ff) { + removed_steam_virtual_device = true; + } + } + } + } + + } else if (event->mask & (IN_CREATE | IN_ATTRIB)) { + // The device was created, or it was chmodded to be accessible. We need + // to check for the latter since it seems that the device may get the + // IN_CREATE event before the driver gets the permissions set properly. + + size_t index; + if (sscanf(event->name, "event%zd", &index) == 1) { + InputDevice *device = consider_add_evdev_device(index); + if (device != nullptr && device->is_connected()) { + throw_event("connect-device", device); + } + } + } + + ptr += sizeof(inotify_event) + event->len; + } + + // If the Steam virtual device was just disconnected, the user may have just + // shut down Steam, and we need to reactivate the real Steam Controller + // device that was previously suppressed by Steam. + if (removed_steam_virtual_device) { + inactive_devices = _inactive_devices; + + for (size_t i = 0; i < inactive_devices.size(); ++i) { + InputDevice *device = inactive_devices[i]; + if (device != nullptr && device->is_of_type(EvdevInputDevice::get_class_type())) { + PT(EvdevInputDevice) evdev_device = (EvdevInputDevice *)device; + if (evdev_device->reactivate_steam_controller()) { + _inactive_devices.remove_device(device); + _connected_devices.add_device(device); + throw_event("connect-device", device); + } + } + } + } +} + +#endif // PHAVE_LINUX_INPUT_H diff --git a/panda/src/device/linuxInputDeviceManager.h b/panda/src/device/linuxInputDeviceManager.h new file mode 100644 index 0000000000..36fce94842 --- /dev/null +++ b/panda/src/device/linuxInputDeviceManager.h @@ -0,0 +1,48 @@ +/** + * 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 linuxInputDeviceManager.h + * @author rdb + * @date 2018-01-25 + */ + +#ifndef LINUXINPUTDEVICEMANAGER_H +#define LINUXINPUTDEVICEMANAGER_H + +#include "inputDeviceManager.h" + +#ifdef PHAVE_LINUX_INPUT_H + +/** + * This class keeps track of all the devices on a system, and sends out events + * when a device has been hot-plugged. + */ +class EXPCL_PANDA_DEVICE LinuxInputDeviceManager final : public InputDeviceManager { +private: + LinuxInputDeviceManager(); + ~LinuxInputDeviceManager(); + + InputDevice *consider_add_evdev_device(size_t index); + InputDevice *consider_add_js_device(size_t index); + +public: + bool has_virtual_device(unsigned short vendor_id, unsigned short product_id) const; + + virtual void update(); + +protected: + int _inotify_fd; + + pvector _evdev_devices; + + friend class InputDeviceManager; +}; + +#endif // PHAVE_LINUX_INPUT_H + +#endif diff --git a/panda/src/putil/weakKeyHashMap.cxx b/panda/src/device/linuxJoystickDevice.I similarity index 79% rename from panda/src/putil/weakKeyHashMap.cxx rename to panda/src/device/linuxJoystickDevice.I index 54aecdb527..423880a300 100644 --- a/panda/src/putil/weakKeyHashMap.cxx +++ b/panda/src/device/linuxJoystickDevice.I @@ -6,9 +6,7 @@ * license. You should have received a copy of this license along * with this source code in a file named "LICENSE." * - * @file weakKeyHashMap.cxx + * @file linuxJoystickDevice.I * @author rdb - * @date 2015-07-13 + * @date 2015-08-21 */ - -#include "weakKeyHashMap.h" diff --git a/panda/src/device/linuxJoystickDevice.cxx b/panda/src/device/linuxJoystickDevice.cxx new file mode 100644 index 0000000000..c964ea8499 --- /dev/null +++ b/panda/src/device/linuxJoystickDevice.cxx @@ -0,0 +1,438 @@ +/** + * 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 linuxJoystickDevice.cxx + * @author rdb + * @date 2015-08-21 + */ + +#include "linuxJoystickDevice.h" +#include "evdevInputDevice.h" + +#ifdef PHAVE_LINUX_INPUT_H + +#include "gamepadButton.h" +#include "linuxInputDeviceManager.h" + +#include +#include + +TypeHandle LinuxJoystickDevice::_type_handle; + +/** + * Creates a new device using the Linux joystick device with the given index. + */ +LinuxJoystickDevice:: +LinuxJoystickDevice(LinuxInputDeviceManager *manager, size_t index) : + _manager(manager), + _fd(-1), + _index(index), + _dpad_x_axis(-1), + _dpad_y_axis(-1), + _dpad_left_button(-1), + _dpad_up_button(-1), + _ltrigger_button(-1), + _rtrigger_button(-1) +{ + LightMutexHolder holder(_lock); + if (!open_device()) { + device_cat.error() + << "Could not open joystick device /dev/input/js" << index + << ": " << strerror(errno) << "\n"; + } +} + +/** + * + */ +LinuxJoystickDevice:: +~LinuxJoystickDevice() { + if (_fd != -1) { + close(_fd); + _fd = -1; + } +} + +/** + * Returns true if there are pending events. + */ +bool LinuxJoystickDevice:: +check_events() const { + unsigned int avail = 0; + ioctl(_fd, FIONREAD, &avail); + return (avail != 0); +} + +/** + * Polls the input device for new activity, to ensure it contains the latest + * events. This will only have any effect for some types of input devices; + * others may be updated automatically, and this method will be a no-op. + */ +void LinuxJoystickDevice:: +do_poll() { + if (_fd != -1 && process_events()) { + while (process_events()) {} + + // If we got events, we are obviously connected. Mark us so. + if (!_is_connected) { + _is_connected = true; + if (_manager != nullptr) { + _manager->add_device(this); + } + } + } +} + +/** + * Opens or reopens the joystick device, and reads out the button and axis + * mappings. Assumes the lock is already held. + */ +bool LinuxJoystickDevice:: +open_device() { + nassertr(_lock.debug_is_locked(), false); + + char path[64]; + sprintf(path, "/dev/input/js%zd", _index); + + _fd = open(path, O_RDONLY | O_NONBLOCK); + + if (_fd == -1) { + _is_connected = false; + return false; + } + + // Read the name from the device. We'll later try to use sysfs to read + // the proper product name from the device, but this is a good fallback. + char name[128]; + name[0] = 0; + ioctl(_fd, JSIOCGNAME(sizeof(name)), name); + _name = name; + + bool emulate_dpad = true; + bool have_analog_triggers = false; + + // Get the number of axes. + uint8_t num_axes = 0, num_buttons = 0; + ioctl(_fd, JSIOCGAXES, &num_axes); + ioctl(_fd, JSIOCGBUTTONS, &num_buttons); + + _buttons.resize(num_buttons); + _axes.resize(num_axes); + + if (num_buttons > 0) { + uint16_t btnmap[512]; + ioctl(_fd, JSIOCGBTNMAP, btnmap); + + for (uint8_t i = 0; i < num_buttons; ++i) { + ButtonHandle handle = EvdevInputDevice::map_button(btnmap[i]); + if (handle == ButtonHandle::none()) { + if (device_cat.is_debug()) { + device_cat.debug() << "Unmapped /dev/input/js" << _index + << " button " << (int)i << ": 0x" << std::hex << btnmap[i] << "\n"; + } + } else if (handle == GamepadButton::face_a()) { + _device_class = DeviceClass::gamepad; + } else if (handle == GamepadButton::trigger()) { + _device_class = DeviceClass::flight_stick; + } else if (handle == GamepadButton::dpad_left()) { + emulate_dpad = false; + } else if (handle == GamepadButton::ltrigger()) { + _ltrigger_button = i; + } else if (handle == GamepadButton::rtrigger()) { + _rtrigger_button = i; + } + _buttons[i].handle = handle; + } + } + + if (num_axes > 0) { + uint8_t axmap[512]; + ioctl(_fd, JSIOCGAXMAP, axmap); + + for (uint8_t i = 0; i < num_axes; ++i) { + Axis axis = Axis::none; + + switch (axmap[i]) { + case ABS_X: + if (_device_class == DeviceClass::gamepad) { + axis = InputDevice::Axis::left_x; + } else if (_device_class == DeviceClass::flight_stick) { + axis = InputDevice::Axis::roll; + } else { + axis = InputDevice::Axis::x; + } + break; + + case ABS_Y: + if (_device_class == DeviceClass::gamepad) { + axis = InputDevice::Axis::left_y; + } else if (_device_class == DeviceClass::flight_stick) { + axis = InputDevice::Axis::pitch; + } else { + axis = InputDevice::Axis::y; + } + break; + + case ABS_Z: + if (_device_class == DeviceClass::gamepad) { + axis = Axis::left_trigger; + } else { + //axis = Axis::trigger; + } + break; + + case ABS_RX: + axis = Axis::right_x; + break; + + case ABS_RY: + axis = Axis::right_y; + break; + + case ABS_RZ: + if (_device_class == DeviceClass::gamepad) { + axis = InputDevice::Axis::right_trigger; + } else { + axis = InputDevice::Axis::yaw; + } + break; + + case ABS_THROTTLE: + axis = InputDevice::Axis::throttle; + break; + + case ABS_RUDDER: + axis = InputDevice::Axis::rudder; + break; + + case ABS_WHEEL: + axis = InputDevice::Axis::wheel; + break; + + case ABS_GAS: + axis = InputDevice::Axis::accelerator; + break; + + case ABS_BRAKE: + axis = InputDevice::Axis::brake; + break; + + case ABS_HAT0X: + if (emulate_dpad) { + // Emulate D-Pad or hat switch. + _dpad_x_axis = i; + _dpad_left_button = (int)_buttons.size(); + if (_device_class == DeviceClass::gamepad) { + add_button(GamepadButton::dpad_left()); + add_button(GamepadButton::dpad_right()); + } else { + add_button(GamepadButton::hat_left()); + add_button(GamepadButton::hat_right()); + } + axis = Axis::none; + } + break; + + case ABS_HAT0Y: + if (emulate_dpad) { + // Emulate D-Pad. + _dpad_y_axis = i; + _dpad_up_button = (int)_buttons.size(); + if (_device_class == DeviceClass::gamepad) { + add_button(GamepadButton::dpad_up()); + add_button(GamepadButton::dpad_down()); + } else { + add_button(GamepadButton::hat_up()); + add_button(GamepadButton::hat_down()); + } + axis = Axis::none; + } + break; + + case ABS_HAT2X: + if (_device_class == DeviceClass::gamepad) { + axis = InputDevice::Axis::right_trigger; + } + break; + + case ABS_HAT2Y: + if (_device_class == DeviceClass::gamepad) { + axis = InputDevice::Axis::left_trigger; + } + break; + + default: + if (device_cat.is_debug()) { + device_cat.debug() << "Unmapped /dev/input/js" << _index + << " axis " << (int)i << ": 0x" << std::hex << (int)axmap[i] << "\n"; + } + axis = Axis::none; + break; + } + _axes[i].axis = axis; + + if (axis == Axis::left_trigger || axis == Axis::right_trigger) { + // We'd like to use 0.0 to indicate the resting position. + _axes[i]._scale = 1.0 / 65534.0; + _axes[i]._bias = 0.5; + have_analog_triggers = true; + } else if (axis == Axis::left_y || axis == Axis::right_y || axis == Axis::y) { + _axes[i]._scale = 1.0 / -32767.0; + _axes[i]._bias = 0.0; + } else { + _axes[i]._scale = 1.0 / 32767.0; + _axes[i]._bias = 0.0; + } + } + } + + if (_ltrigger_button >= 0 && _rtrigger_button >= 0 && !have_analog_triggers) { + // Emulate analog triggers. + _ltrigger_axis = (int)_axes.size(); + add_axis(Axis::left_trigger, 0, 1, false); + add_axis(Axis::right_trigger, 0, 1, false); + } else { + _ltrigger_button = -1; + _rtrigger_button = -1; + } + + // Get additional information from sysfs. + sprintf(path, "/sys/class/input/js%zd/device/id/vendor", _index); + FILE *f = fopen(path, "r"); + if (f) { + if (fscanf(f, "%hx", &_vendor_id) < 1) { + _vendor_id = 0; + } + fclose(f); + } + sprintf(path, "/sys/class/input/js%zd/device/id/product", _index); + f = fopen(path, "r"); + if (f) { + if (fscanf(f, "%hx", &_product_id) < 1) { + _product_id = 0; + } + fclose(f); + } + char buffer[256]; + sprintf(path, "/sys/class/input/js%zd/device/device/../product", _index); + f = fopen(path, "r"); + if (f) { + if (fgets(buffer, sizeof(buffer), f) != nullptr) { + buffer[strcspn(buffer, "\r\n")] = 0; + if (buffer[0] != 0) { + _name.assign(buffer); + } + } + fclose(f); + } + sprintf(path, "/sys/class/input/js%zd/device/device/../manufacturer", _index); + f = fopen(path, "r"); + if (f) { + if (fgets(buffer, sizeof(buffer), f) != nullptr) { + buffer[strcspn(buffer, "\r\n")] = 0; + _manufacturer.assign(buffer); + } + fclose(f); + } + sprintf(path, "/sys/class/input/js%zd/device/device/../serial", _index); + f = fopen(path, "r"); + if (f) { + if (fgets(buffer, sizeof(buffer), f) != nullptr) { + buffer[strcspn(buffer, "\r\n")] = 0; + _serial_number.assign(buffer); + } + fclose(f); + } + + // Read the init events. + while (process_events()) {}; + + // Special case handling for the wireless Xbox receiver - the Linux + // joystick API doesn't seem to have a way to report whether the device + // is actually turned on. The best we can do is check whether the axes + // are all 0, which indicates that the driver hasn't received any data for + // this gamepad yet (which means it hasn't been plugged in for this session) + if (strncmp(name, "Xbox 360 Wireless Receiver", 26) == 0) { + for (const auto &control : _axes) { + if (control.value != 0.0) { + _is_connected = true; + return true; + } + } + _is_connected = false; + } else { + _is_connected = true; + } + + return true; +} + +/** + * Reads a number of events from the device. Returns true if events were + * read, meaning this function should keep being called until it returns + * false. + */ +bool LinuxJoystickDevice:: +process_events() { + // Read 8 events at a time. + struct js_event events[8]; + + int n_read = read(_fd, events, sizeof(events)); + if (n_read < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + // No data available for now. + + } else if (errno == ENODEV) { + // The device ceased to exist, so we better close it. No need + // to worry about removing it from the InputDeviceManager, as it + // will get an inotify event sooner or later about this. + close(_fd); + _fd = -1; + //_is_connected = false; + errno = 0; + + } else { + device_cat.error() << "read: " << strerror(errno) << "\n"; + } + return false; + } + + if (n_read == 0) { + return false; + } + + n_read /= sizeof(struct js_event); + + for (int i = 0; i < n_read; ++i) { + int index = events[i].number; + + if (events[i].type & JS_EVENT_BUTTON) { + if (index == _ltrigger_button) { + axis_changed(_ltrigger_axis, events[i].value); + } else if (index == _rtrigger_button) { + axis_changed(_ltrigger_axis + 1, events[i].value); + } + button_changed(index, (events[i].value != 0)); + + } else if (events[i].type & JS_EVENT_AXIS) { + if (index == _dpad_x_axis) { + button_changed(_dpad_left_button, events[i].value < -1000); + button_changed(_dpad_left_button+1, events[i].value > 1000); + } else if (index == _dpad_y_axis) { + button_changed(_dpad_up_button, events[i].value < -1000); + button_changed(_dpad_up_button+1, events[i].value > 1000); + } + + axis_changed(index, events[i].value); + } + } + + return true; +} + +#endif diff --git a/panda/src/device/linuxJoystickDevice.h b/panda/src/device/linuxJoystickDevice.h new file mode 100644 index 0000000000..61d05cd3e8 --- /dev/null +++ b/panda/src/device/linuxJoystickDevice.h @@ -0,0 +1,75 @@ +/** + * 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 linuxJoystickDevice.h + * @author rdb + * @date 2015-08-21 + */ + +#ifndef LINUXJOYSTICKDEVICE_H +#define LINUXJOYSTICKDEVICE_H + +#include "inputDevice.h" + +#ifdef PHAVE_LINUX_INPUT_H + +class LinuxInputDeviceManager; + +/** + * This is a type of device that uses the Linux /dev/input/js# API to read + * data from a game controller. + */ +class EXPCL_PANDA_DEVICE LinuxJoystickDevice : public InputDevice { +PUBLISHED: + LinuxJoystickDevice(LinuxInputDeviceManager *manager, size_t index); + virtual ~LinuxJoystickDevice(); + + bool check_events() const; + +private: + virtual void do_poll(); + + bool open_device(); + bool process_events(); + +private: + LinuxInputDeviceManager *_manager; + + int _fd; + size_t _index; + + // These are used for D-pad / hat switch emulation. + int _dpad_x_axis; + int _dpad_y_axis; + int _dpad_left_button; + int _dpad_up_button; + + // This is used for axis emulation. + int _ltrigger_axis; + int _ltrigger_button; + int _rtrigger_button; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + InputDevice::init_type(); + register_type(_type_handle, "LinuxJoystickDevice", + InputDevice::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "linuxJoystickDevice.I" + +#endif // PHAVE_LINUX_INPUT_H + +#endif // LINUXJOYSTICKDEVICE_H diff --git a/panda/src/device/p3device_composite1.cxx b/panda/src/device/p3device_composite1.cxx index b48fae48ec..6e7da5b78b 100644 --- a/panda/src/device/p3device_composite1.cxx +++ b/panda/src/device/p3device_composite1.cxx @@ -5,4 +5,14 @@ #include "clientDevice.cxx" #include "clientDialDevice.cxx" #include "clientTrackerDevice.cxx" - +#include "evdevInputDevice.cxx" +#include "inputDevice.cxx" +#include "inputDeviceManager.cxx" +#include "inputDeviceNode.cxx" +#include "inputDeviceSet.cxx" +#include "ioKitInputDevice.cxx" +#include "ioKitInputDeviceManager.cxx" +#include "linuxInputDeviceManager.cxx" +#include "linuxJoystickDevice.cxx" +#include "winInputDeviceManager.cxx" +#include "winRawInputDevice.cxx" diff --git a/panda/src/device/p3device_composite2.cxx b/panda/src/device/p3device_composite2.cxx index 9457fca14c..674c1865fe 100644 --- a/panda/src/device/p3device_composite2.cxx +++ b/panda/src/device/p3device_composite2.cxx @@ -3,8 +3,7 @@ #include "analogNode.cxx" #include "buttonNode.cxx" #include "dialNode.cxx" -#include "mouseAndKeyboard.cxx" #include "trackerData.cxx" #include "trackerNode.cxx" #include "virtualMouse.cxx" - +#include "xInputDevice.cxx" diff --git a/panda/src/device/trackerNode.I b/panda/src/device/trackerNode.I index 99929d4af2..5370253d96 100644 --- a/panda/src/device/trackerNode.I +++ b/panda/src/device/trackerNode.I @@ -17,10 +17,9 @@ */ INLINE bool TrackerNode:: is_valid() const { - return (_tracker != nullptr) && _tracker->is_connected(); + return (!_tracker.is_null()/* && _tracker->is_connected()*/); } - /** * Returns the current position of the tracker, if it is available. */ diff --git a/panda/src/device/trackerNode.cxx b/panda/src/device/trackerNode.cxx index 0ec33af92f..f97849e754 100644 --- a/panda/src/device/trackerNode.cxx +++ b/panda/src/device/trackerNode.cxx @@ -12,7 +12,9 @@ */ #include "trackerNode.h" + #include "config_device.h" +#include "clientTrackerDevice.h" #include "dataNodeTransmit.h" TypeHandle TrackerNode::_type_handle; @@ -48,15 +50,15 @@ TrackerNode(ClientBase *client, const std::string &device_name) : return; } - _tracker = DCAST(ClientTrackerDevice, device); + _tracker = device; } /** * */ TrackerNode:: -TrackerNode(ClientTrackerDevice *device) : - DataNode(device->get_device_name()), +TrackerNode(InputDevice *device) : + DataNode(device->get_name()), _tracker(device) { _transform_output = define_output("transform", TransformState::get_class_type()); @@ -64,9 +66,10 @@ TrackerNode(ClientTrackerDevice *device) : _transform = TransformState::make_identity(); nassertv(device != nullptr); - ClientBase *client = device->get_client(); - nassertv(client != nullptr); - set_tracker_coordinate_system(client->get_coordinate_system()); + nassertv(device->has_tracker()); + + //TODO: get coordinate system from tracker object? + set_tracker_coordinate_system(CS_default); set_graph_coordinate_system(CS_default); } @@ -93,9 +96,7 @@ do_transmit_data(DataGraphTraverser *, const DataNodeTransmit &, DataNodeTransmit &output) { if (is_valid()) { _tracker->poll(); - _tracker->acquire(); - _data = _tracker->get_data(); - _tracker->unlock(); + _data = _tracker->get_tracker(); _data.get_orient().extract_to_matrix(_mat); if (_tracker_cs != _graph_cs) { diff --git a/panda/src/device/trackerNode.h b/panda/src/device/trackerNode.h index 29e88047bb..d4be61c1dd 100644 --- a/panda/src/device/trackerNode.h +++ b/panda/src/device/trackerNode.h @@ -18,21 +18,22 @@ #include "clientBase.h" #include "trackerData.h" -#include "clientTrackerDevice.h" +#include "inputDevice.h" #include "dataNode.h" #include "luse.h" #include "linmath_events.h" #include "pointerTo.h" /** - * This is the primary interface to a Tracker object associated with a - * ClientBase. It reads the position and orientation information from the - * tracker and makes it available as a transformation on the data graph. + * This class reads the position and orientation information from a tracker + * device and makes it available as a transformation on the data graph. + * It is also the primary interface to a Tracker object associated with a + * ClientBase. */ class EXPCL_PANDA_DEVICE TrackerNode : public DataNode { PUBLISHED: explicit TrackerNode(ClientBase *client, const std::string &device_name); - explicit TrackerNode(ClientTrackerDevice *device); + explicit TrackerNode(InputDevice *device); virtual ~TrackerNode(); INLINE bool is_valid() const; @@ -62,7 +63,7 @@ private: CPT(TransformState) _transform; private: - PT(ClientTrackerDevice) _tracker; + PT(InputDevice) _tracker; TrackerData _data; LMatrix4 _mat; CoordinateSystem _tracker_cs, _graph_cs; diff --git a/panda/src/device/winInputDeviceManager.cxx b/panda/src/device/winInputDeviceManager.cxx new file mode 100644 index 0000000000..033a174f6b --- /dev/null +++ b/panda/src/device/winInputDeviceManager.cxx @@ -0,0 +1,431 @@ +/** + * 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 winInputDeviceManager.cxx + * @author rdb + * @date 2018-01-21 + */ + +#include "winInputDeviceManager.h" +#include "winRawInputDevice.h" +#include "throw_event.h" + +#if defined(_WIN32) && !defined(CPPPARSER) + +/** + * Initializes the input device manager by scanning which devices are currently + * connected and setting up any platform-dependent structures necessary for + * listening for future device connect events. + */ +WinInputDeviceManager:: +WinInputDeviceManager() : + _xinput_device0(0), + _xinput_device1(1), + _xinput_device2(2), + _xinput_device3(3), + _message_hwnd(nullptr) { + + // XInput provides four device slots, so we simply create four XInputDevice + // objects that are bound to the lifetime of the input manager. + _xinput_device0.local_object(); + _xinput_device1.local_object(); + _xinput_device2.local_object(); + _xinput_device3.local_object(); + +// This function is only available in Vista and later, so we use a wrapper. + HMODULE module = LoadLibraryA("cfgmgr32.dll"); + if (module) { + _CM_Get_DevNode_PropertyW = (pCM_Get_DevNode_Property)GetProcAddress(module, "CM_Get_DevNode_PropertyW"); + } else { + _CM_Get_DevNode_PropertyW = nullptr; + } + + // Now create a message-only window for the raw input. + WNDCLASSEX wc = {}; + wc.cbSize = sizeof(WNDCLASSEX); + wc.lpfnWndProc = window_proc; + wc.hInstance = GetModuleHandle(nullptr); + wc.lpszClassName = "InputDeviceManager"; + if (!RegisterClassEx(&wc)) { + device_cat.warning() + << "Failed to register message-only window class.\n"; + return; + } + + _message_hwnd = CreateWindowEx(0, wc.lpszClassName, "InputDeviceManager", 0, 0, 0, 0, 0, HWND_MESSAGE, nullptr, nullptr, nullptr); + if (!_message_hwnd) { + device_cat.warning() + << "Failed to create message-only window.\n"; + return; + } + + // Now listen for raw input devices using the created message loop. + RAWINPUTDEVICE rid[3]; + rid[0].usUsagePage = 1; + rid[0].usUsage = 4; // Joysticks + rid[0].dwFlags = RIDEV_DEVNOTIFY | RIDEV_INPUTSINK; + rid[0].hwndTarget = _message_hwnd; + rid[1].usUsagePage = 1; + rid[1].usUsage = 5; // Gamepads + rid[1].dwFlags = RIDEV_DEVNOTIFY | RIDEV_INPUTSINK; + rid[1].hwndTarget = _message_hwnd; + rid[2].usUsagePage = 1; + rid[2].usUsage = 8; // Multi-axis controllers (including 3D mice) + rid[2].dwFlags = RIDEV_DEVNOTIFY | RIDEV_INPUTSINK; + rid[2].hwndTarget = _message_hwnd; + if (!RegisterRawInputDevices(rid, 3, sizeof(RAWINPUTDEVICE))) { + device_cat.warning() + << "Failed to register raw input devices.\n"; + } + + // Do we have any XInput devices plugged in now? + int num_xinput = 0; + HANDLE xinput_handle; + RAWINPUTDEVICELIST devices[64]; + UINT num_devices = 64; + num_devices = GetRawInputDeviceList(devices, &num_devices, sizeof(RAWINPUTDEVICELIST)); + if (num_devices == (UINT)-1) { + return; + } + for (UINT i = 0; i < num_devices; ++i) { + if (devices[i].dwType != RIM_TYPEHID) { + continue; + } + HANDLE handle = devices[i].hDevice; + UINT size; + if (GetRawInputDeviceInfoA(handle, RIDI_DEVICENAME, nullptr, &size) != 0) { + continue; + } + + char *path = (char *)alloca(size); + if (path == nullptr || + GetRawInputDeviceInfoA(handle, RIDI_DEVICENAME, (void *)path, &size) < 0) { + continue; + } + + if (strstr(path, "&IG_") != nullptr) { + xinput_handle = handle; + ++num_xinput; + } + } + if (num_xinput == 1) { + // There's only one XInput device, so we know which one it is. + on_input_device_arrival(xinput_handle); + } else if (num_xinput > 0) { + // Just poll all the XInput devices. + _xinput_device0.detect(this); + _xinput_device1.detect(this); + _xinput_device2.detect(this); + _xinput_device3.detect(this); + } +} + +/** + * Closes any resources that the device manager was using to listen for events. + */ +WinInputDeviceManager:: +~WinInputDeviceManager() { + if (_message_hwnd != nullptr) { + DestroyWindow(_message_hwnd); + _message_hwnd = nullptr; + } +} + +/** + * Called by the raw input device destructor. + */ +void WinInputDeviceManager:: +device_destroyed(WinRawInputDevice *device) { + LightMutexHolder holder(_lock); + // It shouldn't be in here, but let's check to be sure. + if (device->_handle != nullptr) { + _raw_devices.erase(device->_handle); + } + + _raw_devices_by_path.erase(device->_path); +} + +/** + * Called upon receiving a WM_INPUT message. + */ +void WinInputDeviceManager:: +on_input(HRAWINPUT handle) { + UINT size; + if (GetRawInputData(handle, RID_INPUT, nullptr, &size, sizeof(RAWINPUTHEADER)) < 0) { + return; + } + + PRAWINPUT data = (PRAWINPUT)alloca(size); + if (GetRawInputData(handle, RID_INPUT, data, &size, sizeof(RAWINPUTHEADER)) <= 0) { + return; + } + + // Look up the device in the map. + PT(WinRawInputDevice) device; + { + LightMutexHolder holder(_lock); + auto it = _raw_devices.find(data->header.hDevice); + if (it != _raw_devices.end()) { + device = it->second; + } + } + if (device != nullptr) { + device->on_input(data); + } +} + +/** + * Called upon receiving WM_INPUT_DEVICE_CHANGE with wparam GIDC_ARRIVAL. + */ +void WinInputDeviceManager:: +on_input_device_arrival(HANDLE handle) { + // Get the device path. + UINT size; + if (GetRawInputDeviceInfoA(handle, RIDI_DEVICENAME, nullptr, &size) != 0) { + return; + } + + char *path = (char *)alloca(size); + if (path == nullptr || + GetRawInputDeviceInfoA(handle, RIDI_DEVICENAME, (void *)path, &size) < 0) { + return; + } + + if (device_cat.is_debug()) { + device_cat.debug() + << "GIDC_ARRIVAL: " << path << "\n"; + } + + // Get the device info. + RID_DEVICE_INFO info; + info.cbSize = sizeof(RID_DEVICE_INFO); + size = sizeof(RID_DEVICE_INFO); + if (GetRawInputDeviceInfoA(handle, RIDI_DEVICEINFO, &info, &size) <= 0) { + return; + } + + // Strip the \\?\ prefix from the path. + while (path[0] == '\\' || path[0] == '?' || path[0] == '.') { + ++path; + } + + // Now, replace # with \\ in the path, but only up to three components. + char *p = path; + int i = 0; + while (*p != '\0') { + if (*p == '#') { + if (i++ < 2) { + *p = '\\'; + } else { + *p = '\0'; + break; + } + } + ++p; + } + + // Find the device node, which will be something like "HID\VID_0123..." + // Then we walk the device tree upward to get the USB node, which which will + // be something like a "USB\VID..." node, from which we can fetch the real + // USB device information (such as the product name). + std::string name, manufacturer; + DEVINST inst; + CONFIGRET ret = CM_Locate_DevNodeA(&inst, (DEVINSTID_A)path, CM_LOCATE_DEVNODE_PHANTOM); + if (ret == CR_SUCCESS) { + char buffer[4096]; + ULONG buflen = 4096; + if (CM_Get_DevNode_Registry_Property(inst, CM_DRP_DEVICEDESC, 0, buffer, &buflen, 0) == CR_SUCCESS) { + name.assign(buffer); + } + buflen = 4096; + if (CM_Get_DevNode_Registry_Property(inst, CM_DRP_MFG, 0, buffer, &buflen, 0) == CR_SUCCESS) { + if (strcmp(buffer, "(Standard system devices)") != 0) { + manufacturer.assign(buffer); + } + } + + // Now walk the device tree upwards so fetch the bus-reported name of the + // parent USB device, which we prefer over the regular device description + // that is probably boring like "HID-compliant game controller". + DEVINST cur = inst; + DEVINST parent; + while (CM_Get_Parent(&parent, cur, 0) == CR_SUCCESS) { + buflen = 4096; + std::string dev_class; + if (CM_Get_DevNode_Registry_Property(parent, CM_DRP_CLASS, 0, buffer, &buflen, 0) == CR_SUCCESS) { + if (strcmp(buffer, "USB") == 0) { + // This is some generic USB device, like a hub. We've gone too far. + break; + } + dev_class.assign(buffer); + } + cur = parent; + + // While we're at it, maybe this one defines a manufacturer? + buflen = 4096; + if (manufacturer.empty() && + CM_Get_DevNode_Registry_Property(cur, CM_DRP_MFG, 0, buffer, &buflen, 0) == CR_SUCCESS) { + if (strcmp(buffer, "(Standard system devices)") != 0) { + manufacturer.assign(buffer); + } + } + + // If it's a generic HID device, take the name from the USB bus. + // See devpkey.h for the available property keys. + static const DEVPROPKEY bus_reported_device_desc = { + {0x540b947e, 0x8b40, 0x45bc, {0xa8, 0xa2, 0x6a, 0x0b, 0x89, 0x4c, 0xbd, 0xa2}}, + 4, + }; + DEVPROPTYPE type; + buflen = 4096; + if (dev_class == "HIDClass" && _CM_Get_DevNode_PropertyW != nullptr && + _CM_Get_DevNode_PropertyW(cur, &bus_reported_device_desc, &type, (PBYTE)buffer, &buflen, 0) == CR_SUCCESS && + type == DEVPROP_TYPE_STRING) { + + // Some devices insert quite some trailing space here. + wchar_t *wbuffer = (wchar_t *)buffer; + size_t wlen = wcslen(wbuffer); + while (iswspace(wbuffer[wlen - 1])) { + wbuffer[--wlen] = 0; + } + TextEncoder encoder; + name.assign(encoder.encode_wtext(std::wstring(wbuffer, wlen))); + break; + } else { + buflen = 4096; + if (CM_Get_DevNode_Registry_Property(cur, CM_DRP_DEVICEDESC, 0, buffer, &buflen, 0) == CR_SUCCESS) { + // We'll pass if it has this awfully boring name. Is there a + // language-independent way to check this? + if (strcmp(buffer, "USB Input Device") != 0) { + name.assign(buffer); + } + } + } + } + } else if (device_cat.is_debug()) { + // No big deal, we just won't be able to get the name. + device_cat.debug() + << "Could not locate device node " << path << " (" << ret << ")\n"; + } + + // Is this an XInput device? If so, handle it via XInput, which allows us + // to handle independent left/right triggers as well as vibration output. + if (info.dwType == RIM_TYPEHID && strstr(path, "&IG_") != nullptr) { + // This is a device we should handle via the XInput API. Check which of + // the four players was the lucky one. + if (_xinput_device0.check_arrival(info, inst, name, manufacturer)) { + add_device(&_xinput_device0); + } + if (_xinput_device1.check_arrival(info, inst, name, manufacturer)) { + add_device(&_xinput_device1); + } + if (_xinput_device2.check_arrival(info, inst, name, manufacturer)) { + add_device(&_xinput_device2); + } + if (_xinput_device3.check_arrival(info, inst, name, manufacturer)) { + add_device(&_xinput_device3); + } + return; + } + + LightMutexHolder holder(_lock); + + // Do we have a device by this path already? This can happen if the + // user keeps around a pointer to a disconnected device in the hope that + // it will reconnect later. + PT(WinRawInputDevice) device; + auto it = _raw_devices_by_path.find(path); + if (it != _raw_devices_by_path.end()) { + device = it->second; + } else { + device = new WinRawInputDevice(this, path); + _raw_devices_by_path[path] = device; + } + + if (device->on_arrival(handle, info, move(name))) { + _raw_devices[handle] = device; + _connected_devices.add_device(device); + + if (device_cat.is_debug()) { + device_cat.debug() + << "Discovered input device " << *device << "\n"; + } + throw_event("connect-device", device.p()); + } +} + +/** + * Called upon receiving WM_INPUT_DEVICE_CHANGE with wparam GIDC_REMOVAL. + */ +void WinInputDeviceManager:: +on_input_device_removal(HANDLE handle) { + // The handle will probably no longer be valid after this, so find the + // device and remove it from _raw_devices. However, we keep it in + // _raw_devices_by_path in case there's still a pointer around to it. + + // We keep the pointer outside the lock because the input device + // destructor calls back to InputDeviceManager. + PT(WinRawInputDevice) device; + { + LightMutexHolder holder(_lock); + auto it = _raw_devices.find(handle); + if (it != _raw_devices.end()) { + device = std::move(it->second); + _raw_devices.erase(it); + device->on_removal(); + + if (_connected_devices.remove_device(device)) { + throw_event("disconnect-device", device.p()); + } + if (device_cat.is_debug()) { + device_cat.debug() + << "Removed input device " << *device << "\n"; + } + } + } +} + +/** + * Implementation of the message loop. + */ +LRESULT WINAPI WinInputDeviceManager:: +window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + WinInputDeviceManager *mgr; + switch (msg) { + case WM_INPUT: + mgr = (WinInputDeviceManager *)InputDeviceManager::get_global_ptr(); + if (mgr != nullptr) { + mgr->on_input((HRAWINPUT)lparam); + } + break; + + case WM_INPUT_DEVICE_CHANGE: + switch (LOWORD(wparam)) { + case GIDC_ARRIVAL: + mgr = (WinInputDeviceManager *)InputDeviceManager::get_global_ptr(); + if (mgr != nullptr) { + mgr->on_input_device_arrival((HANDLE)lparam); + } + break; + + case GIDC_REMOVAL: + mgr = (WinInputDeviceManager *)InputDeviceManager::get_global_ptr(); + if (mgr != nullptr) { + mgr->on_input_device_removal((HANDLE)lparam); + } + break; + } + break; + + default: + break; + } + return DefWindowProcW(hwnd, msg, wparam, lparam); +} + +#endif diff --git a/panda/src/device/winInputDeviceManager.h b/panda/src/device/winInputDeviceManager.h new file mode 100644 index 0000000000..6c3fefd8c1 --- /dev/null +++ b/panda/src/device/winInputDeviceManager.h @@ -0,0 +1,64 @@ +/** + * 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 winInputDeviceManager.h + * @author rdb + * @date 2018-01-21 + */ + +#ifndef WININPUTDEVICEMANAGER_H +#define WININPUTDEVICEMANAGER_H + +#include "inputDeviceManager.h" + +#if defined(_WIN32) && !defined(CPPPARSER) + +#include "xInputDevice.h" + +#include +#include + +class WinRawInputDevice; + +/** + * This is the Windows implementation of InputDeviceManager, managing both + * XInput controllers and raw input devices. + */ +class EXPCL_PANDA_DEVICE WinInputDeviceManager final : public InputDeviceManager { +private: + WinInputDeviceManager(); + ~WinInputDeviceManager(); + +public: + void device_destroyed(WinRawInputDevice *device); + + void on_input(HRAWINPUT handle); + void on_input_device_arrival(HANDLE handle); + void on_input_device_removal(HANDLE handle); + +private: + // There are always exactly four of these in existence. + XInputDevice _xinput_device0; + XInputDevice _xinput_device1; + XInputDevice _xinput_device2; + XInputDevice _xinput_device3; + + HWND _message_hwnd; + pmap _raw_devices; + pmap _raw_devices_by_path; + + static LRESULT WINAPI window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); + + typedef CONFIGRET (*pCM_Get_DevNode_Property)(DEVINST, const DEVPROPKEY *, DEVPROPTYPE *, PBYTE, PULONG, ULONG); + pCM_Get_DevNode_Property _CM_Get_DevNode_PropertyW; + + friend class InputDeviceManager; +}; + +#endif +#endif diff --git a/panda/src/device/winRawInputDevice.cxx b/panda/src/device/winRawInputDevice.cxx new file mode 100644 index 0000000000..eeb98f112d --- /dev/null +++ b/panda/src/device/winRawInputDevice.cxx @@ -0,0 +1,701 @@ +/** + * 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 winRawInputDevice.cxx + * @author rdb + * @date 2018-01-19 + */ + +#include "winRawInputDevice.h" +#include "gamepadButton.h" +#include "mouseButton.h" + +#if defined(_WIN32) && !defined(CPPPARSER) + +#include +#include + +// Copy definitions from hidusage.h, until we can drop support for the 7.1 SDK +typedef USHORT USAGE, *PUSAGE; + +#define HID_USAGE_PAGE_UNDEFINED ((USAGE) 0x00) +#define HID_USAGE_PAGE_GENERIC ((USAGE) 0x01) +#define HID_USAGE_PAGE_SIMULATION ((USAGE) 0x02) +#define HID_USAGE_PAGE_VR ((USAGE) 0x03) +#define HID_USAGE_PAGE_SPORT ((USAGE) 0x04) +#define HID_USAGE_PAGE_GAME ((USAGE) 0x05) +#define HID_USAGE_PAGE_KEYBOARD ((USAGE) 0x07) +#define HID_USAGE_PAGE_LED ((USAGE) 0x08) +#define HID_USAGE_PAGE_BUTTON ((USAGE) 0x09) + +#define HID_USAGE_GENERIC_POINTER ((USAGE) 0x01) +#define HID_USAGE_GENERIC_MOUSE ((USAGE) 0x02) +#define HID_USAGE_GENERIC_JOYSTICK ((USAGE) 0x04) +#define HID_USAGE_GENERIC_GAMEPAD ((USAGE) 0x05) +#define HID_USAGE_GENERIC_KEYBOARD ((USAGE) 0x06) +#define HID_USAGE_GENERIC_KEYPAD ((USAGE) 0x07) +#define HID_USAGE_GENERIC_SYSTEM_CTL ((USAGE) 0x80) + +#define HID_USAGE_GENERIC_X ((USAGE) 0x30) +#define HID_USAGE_GENERIC_Y ((USAGE) 0x31) +#define HID_USAGE_GENERIC_Z ((USAGE) 0x32) +#define HID_USAGE_GENERIC_RX ((USAGE) 0x33) +#define HID_USAGE_GENERIC_RY ((USAGE) 0x34) +#define HID_USAGE_GENERIC_RZ ((USAGE) 0x35) +#define HID_USAGE_GENERIC_SLIDER ((USAGE) 0x36) +#define HID_USAGE_GENERIC_DIAL ((USAGE) 0x37) +#define HID_USAGE_GENERIC_WHEEL ((USAGE) 0x38) +#define HID_USAGE_GENERIC_HATSWITCH ((USAGE) 0x39) + +// Copy definitions from hidpi.h, until we can drop support for the 7.1 SDK +#define HIDP_STATUS_SUCCESS ((NTSTATUS)(0x11 << 16)) + +typedef enum _HIDP_REPORT_TYPE { + HidP_Input, + HidP_Output, + HidP_Feature +} HIDP_REPORT_TYPE; + +typedef struct _HIDP_BUTTON_CAPS { + USAGE UsagePage; + UCHAR ReportID; + BOOLEAN IsAlias; + USHORT BitField; + USHORT LinkCollection; + USAGE LinkUsage; + USAGE LinkUsagePage; + BOOLEAN IsRange; + BOOLEAN IsStringRange; + BOOLEAN IsDesignatorRange; + BOOLEAN IsAbsolute; + ULONG Reserved[10]; + union { + struct { + USAGE UsageMin, UsageMax; + USHORT StringMin, StringMax; + USHORT DesignatorMin, DesignatorMax; + USHORT DataIndexMin, DataIndexMax; + } Range; + struct { + USAGE Usage, Reserved1; + USHORT StringIndex, Reserved2; + USHORT DesignatorIndex, Reserved3; + USHORT DataIndex, Reserved4; + } NotRange; + }; +} HIDP_BUTTON_CAPS, *PHIDP_BUTTON_CAPS; + +typedef struct _HIDP_VALUE_CAPS { + USAGE UsagePage; + UCHAR ReportID; + BOOLEAN IsAlias; + USHORT BitField; + USHORT LinkCollection; + USAGE LinkUsage; + USAGE LinkUsagePage; + BOOLEAN IsRange; + BOOLEAN IsStringRange; + BOOLEAN IsDesignatorRange; + BOOLEAN IsAbsolute; + BOOLEAN HasNull; + UCHAR Reserved; + USHORT BitSize; + USHORT ReportCount; + USHORT Reserved2[5]; + ULONG UnitsExp; + ULONG Units; + LONG LogicalMin, LogicalMax; + LONG PhysicalMin, PhysicalMax; + union { + struct { + USAGE UsageMin, UsageMax; + USHORT StringMin, StringMax; + USHORT DesignatorMin, DesignatorMax; + USHORT DataIndexMin, DataIndexMax; + } Range; + struct { + USAGE Usage, Reserved1; + USHORT StringIndex, Reserved2; + USHORT DesignatorIndex, Reserved3; + USHORT DataIndex, Reserved4; + } NotRange; + }; +} HIDP_VALUE_CAPS, *PHIDP_VALUE_CAPS; + +typedef PUCHAR PHIDP_REPORT_DESCRIPTOR; +typedef struct _HIDP_PREPARSED_DATA *PHIDP_PREPARSED_DATA; + +typedef struct _HIDP_CAPS { + USAGE Usage; + USAGE UsagePage; + USHORT InputReportByteLength; + USHORT OutputReportByteLength; + USHORT FeatureReportByteLength; + USHORT Reserved[17]; + USHORT NumberLinkCollectionNodes; + USHORT NumberInputButtonCaps; + USHORT NumberInputValueCaps; + USHORT NumberInputDataIndices; + USHORT NumberOutputButtonCaps; + USHORT NumberOutputValueCaps; + USHORT NumberOutputDataIndices; + USHORT NumberFeatureButtonCaps; + USHORT NumberFeatureValueCaps; + USHORT NumberFeatureDataIndices; +} HIDP_CAPS, *PHIDP_CAPS; + +typedef struct _HIDP_DATA { + USHORT DataIndex; + USHORT Reserved; + union { + ULONG RawValue; + BOOLEAN On; + }; +} HIDP_DATA, *PHIDP_DATA; + +typedef LONG NTSTATUS; +typedef NTSTATUS (*pHidP_GetCaps)(PHIDP_PREPARSED_DATA, PHIDP_CAPS); +typedef NTSTATUS (*pHidP_GetButtonCaps)(HIDP_REPORT_TYPE, PHIDP_BUTTON_CAPS, PUSHORT, PHIDP_PREPARSED_DATA); +typedef NTSTATUS (*pHidP_GetValueCaps)(HIDP_REPORT_TYPE, PHIDP_VALUE_CAPS, PUSHORT, PHIDP_PREPARSED_DATA); +typedef NTSTATUS (*pHidP_GetData)(HIDP_REPORT_TYPE, PHIDP_DATA, PULONG, PHIDP_PREPARSED_DATA, PCHAR, ULONG); +typedef ULONG (*pHidP_MaxDataListLength)(HIDP_REPORT_TYPE, PHIDP_PREPARSED_DATA); + +static pHidP_GetCaps _HidP_GetCaps = nullptr; +static pHidP_GetButtonCaps _HidP_GetButtonCaps = nullptr; +static pHidP_GetValueCaps _HidP_GetValueCaps = nullptr; +static pHidP_GetData _HidP_GetData = nullptr; +static pHidP_MaxDataListLength _HidP_MaxDataListLength = nullptr; + +/** + * Static method to initialize the HID parser library. We load it dynamically + * because the Windows 7.1 SDK doesn't ship hid.lib. + */ +static bool init_hidp() { + HMODULE module = LoadLibraryA("hid.dll"); + if (module) { + if (device_cat.is_debug()) { + device_cat.debug() + << "Successfully loaded hid.dll\n"; + } + + _HidP_GetCaps = (pHidP_GetCaps)GetProcAddress(module, "HidP_GetCaps"); + _HidP_GetButtonCaps = (pHidP_GetButtonCaps)GetProcAddress(module, "HidP_GetButtonCaps"); + _HidP_GetValueCaps = (pHidP_GetValueCaps)GetProcAddress(module, "HidP_GetValueCaps"); + _HidP_GetData = (pHidP_GetData)GetProcAddress(module, "HidP_GetData"); + _HidP_MaxDataListLength = (pHidP_MaxDataListLength)GetProcAddress(module, "HidP_MaxDataListLength"); + + if (_HidP_GetCaps == nullptr || _HidP_GetButtonCaps == nullptr || + _HidP_GetValueCaps == nullptr || _HidP_GetData == nullptr || + _HidP_MaxDataListLength == nullptr) { + device_cat.error() + << "Failed to locate function pointers in hid.dll\n"; + return false; + } + + return true; + } + + device_cat.error() + << "Failed to load hid.dll.\n"; + return false; +} + +/** + * Protected constructor. Given a raw device handle. + */ +WinRawInputDevice:: +WinRawInputDevice(WinInputDeviceManager *manager, const char *path) : + _manager(manager), + _path(path), + _max_data_count(0), + _preparsed(nullptr) { +} + +/** + * + */ +WinRawInputDevice:: +~WinRawInputDevice() { + // Unregister the device from the manager. + LightMutexHolder holder(_lock); + if (_manager != nullptr) { + _manager->device_destroyed(this); + } + if (_preparsed != nullptr) { + free(_preparsed); + _preparsed = nullptr; + } +} + +/** + * Called by InputDeviceManager when this device is connected. Returns true + * if the device was connected successfully. + */ +bool WinRawInputDevice:: +on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) { + using std::hex; + using std::dec; + using std::swap; + + LightMutexHolder holder(_lock); + + _name = std::move(name); + + switch (info.dwType) { + case RIM_TYPEMOUSE: + _device_class = DeviceClass::mouse; + break; + + case RIM_TYPEKEYBOARD: + _device_class = DeviceClass::keyboard; + break; + + case RIM_TYPEHID: + _vendor_id = info.hid.dwVendorId; + _product_id = info.hid.dwProductId; + + // Gamepads + if (info.hid.usUsagePage == HID_USAGE_PAGE_GENERIC && + info.hid.usUsage == HID_USAGE_GENERIC_GAMEPAD) { + _device_class = DeviceClass::gamepad; + + // Flight sticks + } else if (info.hid.usUsagePage == HID_USAGE_PAGE_GENERIC && + info.hid.usUsage == HID_USAGE_GENERIC_JOYSTICK) { + _device_class = DeviceClass::flight_stick; + + if (_name == "usb gamepad") { + // Well, it claims to be a gamepad... + _device_class = DeviceClass::gamepad; + } + //TODO: better solution for this + if (_vendor_id == 0x0079 && _product_id == 0x0006) { + // Trust GXT 24 + _device_class = DeviceClass::gamepad; + } + + // Mice + } else if (info.hid.usUsagePage == HID_USAGE_PAGE_GENERIC && + info.hid.usUsage == HID_USAGE_GENERIC_MOUSE) { + _device_class = DeviceClass::mouse; + + // Keyboards + } else if (info.hid.usUsagePage == HID_USAGE_PAGE_GENERIC && + info.hid.usUsage == HID_USAGE_GENERIC_KEYBOARD) { + _device_class = DeviceClass::keyboard; + + // 3Dconnexion SpaceNavigator and friends. + } else if (_vendor_id == 0x046d && + (_product_id == 0xc623 || + _product_id == 0xc625 || + _product_id == 0xc626 || + _product_id == 0xc627 || + _product_id == 0xc628 || + _product_id == 0xc629 || + _product_id == 0xc62b)) { + _device_class = DeviceClass::spatial_mouse; + } + break; + + default: + return false; + } + + // Initialize hid.dll, which provides the HID parser functions. + static bool hid_initialized = false; + if (!hid_initialized) { + if (!init_hidp()) { + return false; + } + hid_initialized = true; + } + + // Get the "preparsed data", which we can parse with the HID parser API. + UINT size = 0; + if (GetRawInputDeviceInfo(handle, RIDI_PREPARSEDDATA, nullptr, &size) < 0) { + return false; + } + + PHIDP_PREPARSED_DATA buffer = (PHIDP_PREPARSED_DATA)malloc(size); + if (GetRawInputDeviceInfo(handle, RIDI_PREPARSEDDATA, buffer, &size) <= 0) { + return false; + } + _preparsed = buffer; + + HIDP_CAPS caps; + if (_HidP_GetCaps(buffer, &caps) != HIDP_STATUS_SUCCESS) { + device_cat.warning() + << "Failed to get capabilities from HID preparsed data.\n"; + return false; + } + + // Do we have a button mapping? + static const ButtonHandle gamepad_buttons_common[] = { + ButtonHandle::none(), + GamepadButton::face_a(), + GamepadButton::face_b(), + GamepadButton::face_x(), + GamepadButton::face_y(), + GamepadButton::lshoulder(), + GamepadButton::rshoulder(), + GamepadButton::start(), + GamepadButton::back(), + GamepadButton::lstick(), + GamepadButton::rstick(), + }; + const ButtonHandle *gamepad_buttons = gamepad_buttons_common; + if (_vendor_id == 0x0810 && _product_id == 0xe501) { + // SNES-style USB gamepad + static const ButtonHandle gamepad_buttons_snes[] = { + ButtonHandle::none(), + GamepadButton::face_x(), + GamepadButton::face_a(), + GamepadButton::face_b(), + GamepadButton::face_y(), + GamepadButton::lshoulder(), + GamepadButton::rshoulder(), + ButtonHandle::none(), + ButtonHandle::none(), + GamepadButton::back(), + GamepadButton::start(), + }; + gamepad_buttons = gamepad_buttons_snes; + } + + // Prepare a mapping of data indices to button/axis indices. + _indices.resize(caps.NumberInputDataIndices); + + _buttons.clear(); + _axes.clear(); + + USHORT num_button_caps = caps.NumberInputButtonCaps; + PHIDP_BUTTON_CAPS button_caps = (PHIDP_BUTTON_CAPS)alloca(num_button_caps * sizeof(HIDP_BUTTON_CAPS)); + _HidP_GetButtonCaps(HidP_Input, button_caps, &num_button_caps, buffer); + + for (USHORT i = 0; i < num_button_caps; ++i) { + HIDP_BUTTON_CAPS &cap = button_caps[i]; + int upper = 0; + if (cap.IsRange) { + upper = (cap.Range.UsageMax - cap.Range.UsageMin); + + if (device_cat.is_debug()) { + device_cat.debug() + << "Found button range: DataIndex=" << dec + << cap.Range.DataIndexMin << ".." << cap.Range.DataIndexMax + << ", ReportID=" << (int)cap.ReportID + << ", UsagePage=0x" << hex << cap.UsagePage + << ", Usage=0x" << cap.Range.UsageMin << "..0x" << cap.Range.UsageMax + << dec << "\n"; + } + } else { + if (device_cat.is_debug()) { + device_cat.debug() + << "Found button: DataIndex=" << dec << cap.NotRange.DataIndex + << ", ReportID=" << dec << (int)cap.ReportID + << ", UsagePage=0x" << cap.UsagePage + << ", Usage=0x" << cap.NotRange.Usage + << dec << "\n"; + } + } + + // Windows will only tell us which buttons in a report are "on", so we + // need to keep track of which buttons exist in which report so that we + // can figure out which ones are off. + if (cap.ReportID >= _report_buttons.size()) { + _report_buttons.resize(cap.ReportID + 1); + } + for (int j = 0; j <= upper; ++j) { + USAGE usage = j + cap.Range.UsageMin; + USHORT data_index = j + cap.Range.DataIndexMin; + ButtonHandle handle = ButtonHandle::none(); + switch (cap.UsagePage) { + case HID_USAGE_PAGE_BUTTON: + if (_device_class == DeviceClass::gamepad) { + if (usage < sizeof(gamepad_buttons_common) / sizeof(ButtonHandle)) { + handle = gamepad_buttons[usage]; + } + } else if (_device_class == DeviceClass::flight_stick) { + if (usage > 0) { + handle = GamepadButton::joystick(usage - 1); + } + } else if (_device_class == DeviceClass::mouse) { + // In Panda, wheel and right button are flipped around... + int button = (usage == 2 || usage == 3) ? (4 - usage) : (usage - 1); + handle = MouseButton::button(button); + } + break; + } + + int button_index = _buttons.size(); + _report_buttons[cap.ReportID].set_bit(button_index); + _indices[data_index] = Index::button(button_index); + _buttons.push_back(ButtonState(handle)); + } + } + + USHORT num_value_caps = caps.NumberInputValueCaps; + PHIDP_VALUE_CAPS value_caps = (PHIDP_VALUE_CAPS)alloca(num_value_caps * sizeof(HIDP_VALUE_CAPS)); + _HidP_GetValueCaps(HidP_Input, value_caps, &num_value_caps, buffer); + + _hat_data_index = -1; + + for (USHORT i = 0; i < num_value_caps; ++i) { + HIDP_VALUE_CAPS &cap = value_caps[i]; + int upper = 0; + if (cap.IsRange) { + upper = (cap.Range.UsageMax - cap.Range.UsageMin); + + if (device_cat.is_debug()) { + device_cat.debug() + << "Found value range: DataIndex=" << dec + << cap.Range.DataIndexMin << ".." << cap.Range.DataIndexMax + << ", ReportID=" << (int)cap.ReportID + << ", UsagePage=0x" << hex << cap.UsagePage + << ", Usage=0x" << cap.Range.UsageMin << "..0x" << cap.Range.UsageMax + << dec << ", LogicalMin=" << cap.LogicalMin + << ", LogicalMax=" << cap.LogicalMax << "\n"; + } + } else { + if (device_cat.is_debug()) { + device_cat.debug() + << "Found value: DataIndex=" << dec << cap.NotRange.DataIndex + << ", ReportID=" << dec << (int)cap.ReportID + << ", UsagePage=0x" << hex << cap.UsagePage + << ", Usage=0x" << cap.NotRange.Usage + << dec << ", LogicalMin=" << cap.LogicalMin + << ", LogicalMax=" << cap.LogicalMax << "\n"; + } + } + + for (int j = 0; j <= upper; ++j) { + USAGE usage = j + cap.Range.UsageMin; + USHORT data_index = j + cap.Range.DataIndexMin; + bool is_signed = true; + + // My gamepads give this odd invalid range. + if (cap.LogicalMin == 0 && cap.LogicalMax == -1) { + cap.LogicalMax = 65535; + is_signed = false; + } + + Axis axis = Axis::none; + switch (cap.UsagePage) { + case HID_USAGE_PAGE_GENERIC: + switch (usage) { + case HID_USAGE_GENERIC_X: + if (_device_class == DeviceClass::gamepad) { + axis = Axis::left_x; + } else if (_device_class == DeviceClass::flight_stick) { + axis = Axis::roll; + } else { + axis = Axis::x; + } + break; + case HID_USAGE_GENERIC_Y: + if (_device_class == DeviceClass::gamepad) { + axis = Axis::left_y; + swap(cap.LogicalMin, cap.LogicalMax); + } else if (_device_class == DeviceClass::flight_stick) { + axis = Axis::pitch; + } else { + axis = Axis::y; + swap(cap.LogicalMin, cap.LogicalMax); + } + break; + case HID_USAGE_GENERIC_Z: + if (_device_class == DeviceClass::gamepad) { + axis = Axis::left_trigger; + } else if (_device_class == DeviceClass::flight_stick) { + axis = Axis::throttle; + } else { + axis = Axis::z; + swap(cap.LogicalMin, cap.LogicalMax); + } + break; + case HID_USAGE_GENERIC_RX: + if (_device_class == DeviceClass::gamepad) { + axis = Axis::right_x; + } else { + axis = Axis::pitch; + } + break; + case HID_USAGE_GENERIC_RY: + if (_device_class == DeviceClass::gamepad) { + axis = Axis::right_y; + } else { + axis = Axis::roll; + } + swap(cap.LogicalMin, cap.LogicalMax); + break; + case HID_USAGE_GENERIC_RZ: + if (_device_class == DeviceClass::gamepad) { + axis = Axis::right_trigger; + } else { + // Flip to match Panda's convention for heading. + axis = Axis::yaw; + swap(cap.LogicalMin, cap.LogicalMax); + } + break; + case HID_USAGE_GENERIC_SLIDER: + // Flip to match Panda's convention for heading. + axis = Axis::rudder; + swap(cap.LogicalMin, cap.LogicalMax); + break; + case HID_USAGE_GENERIC_WHEEL: + axis = Axis::wheel; + break; + case HID_USAGE_GENERIC_HATSWITCH: + // This is handled specially. + _hat_data_index = data_index; + _hat_data_minimum = cap.LogicalMin; + continue; + } + break; + } + + int axis_index; + if (_vendor_id == 0x044f && _product_id == 0xb108 && axis == Axis::throttle) { + // T.Flight Hotas X throttle is reversed and can go backwards. + axis_index = add_axis(axis, cap.LogicalMax, cap.LogicalMin, true); + } else if (!is_signed) { + // All axes on the weird XInput-style mappings go from -1 to 1 + axis_index = add_axis(axis, cap.LogicalMin, cap.LogicalMax, true); + } else { + axis_index = add_axis(axis, cap.LogicalMin, cap.LogicalMax); + } + _indices[data_index] = Index::axis(axis_index, is_signed); + } + } + + // Do we need to emulate a hat switch or directional pad? + if (_hat_data_index != -1) { + _hat_left_button = (int)_buttons.size(); + if (_device_class == DeviceClass::gamepad) { + _buttons.push_back(ButtonState(GamepadButton::dpad_left())); + _buttons.push_back(ButtonState(GamepadButton::dpad_right())); + _buttons.push_back(ButtonState(GamepadButton::dpad_down())); + _buttons.push_back(ButtonState(GamepadButton::dpad_up())); + } else { + _buttons.push_back(ButtonState(GamepadButton::hat_left())); + _buttons.push_back(ButtonState(GamepadButton::hat_right())); + _buttons.push_back(ButtonState(GamepadButton::hat_down())); + _buttons.push_back(ButtonState(GamepadButton::hat_up())); + } + } + + _max_data_count = _HidP_MaxDataListLength(HidP_Input, buffer); + + _handle = handle; + _is_connected = true; + return true; +} + +/** + * Called by InputDeviceManager when this device is removed. + */ +void WinRawInputDevice:: +on_removal() { + LightMutexHolder holder(_lock); + _is_connected = false; + _handle = nullptr; + if (_preparsed != nullptr) { + free(_preparsed); + _preparsed = nullptr; + } + _indices.clear(); + _report_buttons.clear(); +} + +/** + * Called by InputDeviceManager when raw input is received for this device. + */ +void WinRawInputDevice:: +on_input(PRAWINPUT input) { + nassertv(input != nullptr); + nassertv(_preparsed != nullptr); + + BYTE *ptr = input->data.hid.bRawData; + if (input->data.hid.dwSizeHid == 0) { + return; + } + + LightMutexHolder holder(_lock); + + for (DWORD i = 0; i < input->data.hid.dwCount; ++i) { + process_report((PCHAR)ptr, input->data.hid.dwSizeHid); + ptr += input->data.hid.dwSizeHid; + } +} + +/** + * Processes a single HID report. Assumes the lock is held. + */ +void WinRawInputDevice:: +process_report(PCHAR ptr, size_t size) { + // The first byte is the report identifier. We need it to figure out which + // buttons are off, since each report only contains the "on" buttons. + UCHAR report_id = ptr[0]; + BitArray unset_buttons; + + if (report_id < _report_buttons.size()) { + unset_buttons = _report_buttons[report_id]; + } + + PHIDP_DATA data = (PHIDP_DATA)alloca(sizeof(HIDP_DATA) * _max_data_count); + nassertv(data != nullptr); + + ULONG count = _max_data_count; + NTSTATUS status = _HidP_GetData(HidP_Input, data, &count, (PHIDP_PREPARSED_DATA)_preparsed, ptr, size); + if (status == HIDP_STATUS_SUCCESS) { + for (ULONG di = 0; di < count; ++di) { + if (data[di].DataIndex != _hat_data_index) { + const Index &idx = _indices[data[di].DataIndex]; + if (idx._axis >= 0) { + if (idx._signed) { + axis_changed(idx._axis, (SHORT)data[di].RawValue); + } else { + axis_changed(idx._axis, data[di].RawValue); + } + } + if (idx._button >= 0) { + unset_buttons.clear_bit(idx._button); + button_changed(idx._button, (data[di].On != FALSE)); + } + } else { + int value = (int)data[di].RawValue - _hat_data_minimum; + button_changed(_hat_left_button + 0, value >= 5 && value <= 7); // left + button_changed(_hat_left_button + 1, value >= 1 && value <= 3); // right + button_changed(_hat_left_button + 2, value >= 3 && value <= 5); // down + button_changed(_hat_left_button + 3, value == 7 || value == 0 || value == 1); // up + } + } + + // Now unset the buttons in this report that aren't pressed. + int button_index = unset_buttons.get_lowest_on_bit(); + while (button_index >= 0) { + button_changed(button_index, false); + unset_buttons.clear_bit(button_index); + button_index = unset_buttons.get_lowest_on_bit(); + } + } else if (device_cat.is_spam()) { + device_cat.spam() + << "Failed to get data from raw device " << _path + << " (error 0x" << std::hex << (status & 0xffffffffu) << std::dec << ")\n"; + } +} + +/** + * Polls the input device for new activity, to ensure it contains the latest + * events. This will only have any effect for some types of input devices; + * others may be updated automatically, and this method will be a no-op. + */ +void WinRawInputDevice:: +do_poll() { +} + +#endif // _WIN32 diff --git a/panda/src/device/winRawInputDevice.h b/panda/src/device/winRawInputDevice.h new file mode 100644 index 0000000000..ce4fe9a19c --- /dev/null +++ b/panda/src/device/winRawInputDevice.h @@ -0,0 +1,84 @@ +/** + * 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 winRawInputDevice.h + * @author rdb + * @date 2018-01-19 + */ + +#ifndef WINRAWINPUTDEVICE_H +#define WINRAWINPUTDEVICE_H + +#include "pandabase.h" +#include "inputDevice.h" +#include "bitArray.h" + +#if defined(_WIN32) && !defined(CPPPARSER) + +class WinInputDeviceManager; + +/** + * This implementation of InputDevice uses the Win32 raw input API and the HID + * parser library to support a wide range of devices. + */ +class EXPCL_PANDA_DEVICE WinRawInputDevice final : public InputDevice { +public: + WinRawInputDevice(WinInputDeviceManager *manager, const char *path); + ~WinRawInputDevice(); + + bool on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name); + void on_removal(); + void on_input(PRAWINPUT input); + void process_report(PCHAR ptr, size_t size); + +private: + virtual void do_poll(); + +private: + const std::string _path; + HANDLE _handle; + void *_preparsed; + ULONG _max_data_count; + + // Indexed by report ID + pvector _report_buttons; + + // Either a button index or a axis index. + struct Index { + Index() : _button(-1), _axis(-1) {} + + static Index button(int index) { + Index idx; + idx._button = index; + return idx; + } + static Index axis(int index, bool is_signed=true) { + Index idx; + idx._axis = index; + idx._signed = is_signed; + return idx; + } + + int _button; + int _axis; + bool _signed; + }; + + // Maps a "data index" to either button index or axis index. + pvector _indices; + int _hat_data_index; + int _hat_data_minimum; + int _hat_left_button; + + WinInputDeviceManager *_manager; + friend class WinInputDeviceManager; +}; + +#endif // _WIN32 + +#endif diff --git a/panda/src/device/xInputDevice.cxx b/panda/src/device/xInputDevice.cxx new file mode 100644 index 0000000000..4621d467b9 --- /dev/null +++ b/panda/src/device/xInputDevice.cxx @@ -0,0 +1,518 @@ +/** + * 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 xInputDevice.cxx + * @author rdb + * @date 2015-07-15 + */ + +#include "xInputDevice.h" + +#if defined(_WIN32) && !defined(CPPPARSER) + +#include "gamepadButton.h" +#include "inputDeviceManager.h" +#include "string_utils.h" + +#include +#include + +#ifndef XUSER_MAX_COUNT +#define XUSER_MAX_COUNT 4 +#endif + +#ifndef XINPUT_CAPS_FFB_SUPPORTED +#define XINPUT_CAPS_FFB_SUPPORTED 0x0001 +#endif +#ifndef XINPUT_CAPS_NO_NAVIGATION +#define XINPUT_CAPS_NO_NAVIGATION 0x0010 +#endif + +#ifndef BATTERY_DEVTYPE_GAMEPAD +#define BATTERY_DEVTYPE_GAMEPAD 0x00 +#endif + +#ifndef XINPUT_DEVSUBTYPE_WHEEL +#define XINPUT_DEVSUBTYPE_WHEEL 0x02 +#endif +#ifndef XINPUT_DEVSUBTYPE_ARCADE_STICK +#define XINPUT_DEVSUBTYPE_ARCADE_STICK 0x03 +#endif +#ifndef XINPUT_DEVSUBTYPE_FLIGHT_STICK +#define XINPUT_DEVSUBTYPE_FLIGHT_STICK 0x04 +#endif +#ifndef XINPUT_DEVSUBTYPE_DANCE_PAD +#define XINPUT_DEVSUBTYPE_DANCE_PAD 0x05 +#endif +#ifndef XINPUT_DEVSUBTYPE_GUITAR +#define XINPUT_DEVSUBTYPE_GUITAR 0x06 +#endif +#ifndef XINPUT_DEVSUBTYPE_DRUM_KIT +#define XINPUT_DEVSUBTYPE_DRUM_KIT 0x08 +#endif + +#ifndef BATTERY_TYPE_DISCONNECTED +#define BATTERY_TYPE_DISCONNECTED 0x00 +#endif + +#ifndef BATTERY_TYPE_WIRED +#define BATTERY_TYPE_WIRED 0x01 +#endif + +#ifndef BATTERY_LEVEL_FULL +#define BATTERY_LEVEL_FULL 0x03 +#endif + +typedef struct _XINPUT_BATTERY_INFORMATION { + BYTE BatteryType; + BYTE BatteryLevel; +} XINPUT_BATTERY_INFORMATION; + +// Undocumented, I figured out how this looks by trial and error. +typedef struct _XINPUT_BUSINFO { + WORD VendorID; + WORD ProductID; + WORD RevisionID; + WORD Unknown1; // Unknown - padding? + DWORD InstanceID; + DWORD Unknown2; + WORD Unknown3; +} XINPUT_BUSINFO; + +typedef struct _XINPUT_CAPABILITIES_EX { + BYTE Type; + BYTE SubType; + WORD Flags; + XINPUT_GAMEPAD Gamepad; + XINPUT_VIBRATION Vibration; + + // The following fields are undocumented. + WORD VendorID; + WORD ProductID; + WORD RevisionID; + WORD Unknown1; + WORD Unknown2; +} XINPUT_CAPABILITIES_EX; + +typedef DWORD (*pXInputGetState)(DWORD, XINPUT_STATE *); +typedef DWORD (*pXInputSetState)(DWORD, XINPUT_VIBRATION *); +typedef DWORD (*pXInputGetCapabilities)(DWORD, DWORD, XINPUT_CAPABILITIES *); +typedef DWORD (*pXInputGetCapabilitiesEx)(DWORD, DWORD, DWORD, XINPUT_CAPABILITIES_EX *); +typedef DWORD (*pXInputGetBatteryInformation)(DWORD, BYTE, XINPUT_BATTERY_INFORMATION *); +typedef DWORD (*pXInputGetBaseBusInformation)(DWORD, XINPUT_BUSINFO *); + +static pXInputGetState get_state = nullptr; +static pXInputSetState set_state = nullptr; +static pXInputGetCapabilities get_capabilities = nullptr; +static pXInputGetCapabilitiesEx get_capabilities_ex = nullptr; +static pXInputGetBatteryInformation get_battery_information = nullptr; +static pXInputGetBaseBusInformation get_base_bus_information = nullptr; + +bool XInputDevice::_initialized = false; + +/** + * Protected constructor. user_index is a number 0-3. + */ +XInputDevice:: +XInputDevice(DWORD user_index) : + _index(user_index), + _last_packet(-1), + _last_buttons(0) { + + nassertv(user_index >= 0 && user_index < XUSER_MAX_COUNT); + + _axes.resize(6); + _buttons.resize(16); +} + +/** + * + */ +XInputDevice:: +~XInputDevice() { + do_set_vibration(0, 0); +} + +/** + * Called when a new input device arrives in the InputDeviceManager. This + * method checks whether it matches this XInput device. + */ +bool XInputDevice:: +check_arrival(const RID_DEVICE_INFO &info, DEVINST inst, + const std::string &name, const std::string &manufacturer) { + LightMutexHolder holder(_lock); + if (_is_connected) { + return false; + } + + if (!_initialized) { + nassertr_always(init_xinput(), false); + } + + XINPUT_CAPABILITIES_EX caps = {0}; + XINPUT_STATE state; + if ((get_capabilities_ex && get_capabilities_ex(1, _index, 0, &caps) != ERROR_SUCCESS) && + get_capabilities(_index, 0, (XINPUT_CAPABILITIES *)&caps) != ERROR_SUCCESS) { + return false; + } + + if (get_state(_index, &state) != ERROR_SUCCESS) { + return false; + } + + // Extra check for VID/PID if we have it, just to be sure. + if ((caps.VendorID != 0 && caps.VendorID != info.hid.dwVendorId) || + (caps.ProductID != 0 && caps.ProductID != info.hid.dwProductId)) { + return false; + } + + // Yes, take the name and manufacturer. + if (!name.empty()) { + _name = name; + } else { + _name = "XInput Device #"; + _name += format_string(_index + 1); + } + _manufacturer = manufacturer; + + if (inst && caps.ProductID == 0 && caps.RevisionID != 0) { + // XInput does not report a product ID for the Xbox 360 wireless adapter. + // Instead, we check that the RevisionID matches. + char buffer[4096]; + ULONG buflen = sizeof(buffer); + if (CM_Get_DevNode_Registry_Property(inst, CM_DRP_HARDWAREID, 0, buffer, &buflen, 0) == CR_SUCCESS) { + std::string ids(buffer, buflen); + char revstr[16]; + sprintf(revstr, "REV_%04x", caps.RevisionID); + if (ids.find(revstr) == std::string::npos) { + return false; + } + } + } + + _is_connected = true; + init_device(caps, state); + _vendor_id = info.hid.dwVendorId; + _product_id = info.hid.dwProductId; + return true; +} + +/** + * Called periodically by the InputDeviceManager to detect whether the device + * is currently connected. + * Returns true if the device wasn't connected, but now is. + */ +void XInputDevice:: +detect(InputDeviceManager *mgr) { + if (!_initialized) { + nassertv_always(init_xinput()); + } + + bool connected = false; + + XINPUT_CAPABILITIES_EX caps = {0}; + XINPUT_STATE state; + if (((get_capabilities_ex && get_capabilities_ex(1, _index, 0, &caps) == ERROR_SUCCESS) || + get_capabilities(_index, 0, (XINPUT_CAPABILITIES *)&caps) == ERROR_SUCCESS) && + get_state(_index, &state) == ERROR_SUCCESS) { + connected = true; + } else { + connected = false; + } + + LightMutexHolder holder(_lock); + if (connected == _is_connected) { + // Nothing changed. + return; + } + _is_connected = connected; + + if (connected) { + _name = "XInput Device #"; + _name += format_string(_index + 1); + _vendor_id = caps.VendorID; + _product_id = caps.ProductID; + init_device(caps, state); + mgr->add_device(this); + } else { + mgr->remove_device(this); + } +} + +/** + * Static method to initialize the XInput library. + */ +bool XInputDevice:: +init_xinput() { + if (device_cat.is_debug()) { + device_cat.debug() << "Initializing XInput library.\n"; + } + + _initialized = true; + const char *dll_name = "Xinput1_4.dll"; + HMODULE module = LoadLibraryA(dll_name); + + // If we didn't find XInput 1.4, fall back to the older 1.3 version. + if (!module) { + if (device_cat.is_debug()) { + device_cat.debug() + << "Xinput1_4.dll not found, falling back to Xinput1_3.dll\n"; + } + + dll_name = "Xinput1_3.dll"; + module = LoadLibraryA(dll_name); + } + + if (module) { + if (device_cat.is_debug()) { + device_cat.debug() + << "Successfully loaded " << dll_name << "\n"; + } + + // Undocumented version (XInputGetStateEx) that includes a + // state bit for the guide button. + get_state = (pXInputGetState)GetProcAddress(module, MAKEINTRESOURCE(100)); + if (get_state == nullptr) { + get_state = (pXInputGetState)GetProcAddress(module, "XInputGetState"); + if (get_state == nullptr) { + device_cat.error() + << "Failed to find function XInputGetState in " << dll_name << ".\n"; + return false; + } + } + + set_state = (pXInputSetState)GetProcAddress(module, "XInputSetState"); + if (set_state == nullptr) { + device_cat.error() + << "Failed to find function XInputSetState in " << dll_name << ".\n"; + return false; + } + + get_capabilities = (pXInputGetCapabilities)GetProcAddress(module, "XInputGetCapabilities"); + if (get_capabilities == nullptr) { + device_cat.error() + << "Failed to find function XInputGetCapabilities in " << dll_name << ".\n"; + return false; + } + + get_battery_information = (pXInputGetBatteryInformation)GetProcAddress(module, "XInputGetBatteryInformation"); + get_base_bus_information = (pXInputGetBaseBusInformation)GetProcAddress(module, MAKEINTRESOURCE(104)); + get_capabilities_ex = (pXInputGetCapabilitiesEx)GetProcAddress(module, MAKEINTRESOURCE(108)); + return true; + } + + device_cat.error() + << "Failed to load Xinput1_4.dll or Xinput1_3.dll.\n"; + return false; +} + +/** + * Initializes the device. Called when the device was just connected. + * Assumes either the lock is held or this is called from the constructor. + */ +void XInputDevice:: +init_device(const XINPUT_CAPABILITIES_EX &caps, const XINPUT_STATE &state) { + nassertv(_initialized); + // It seems that the Xbox One controller is reported as having a DevType of + // zero, at least when I tested in with XInput 1.3 on Windows 7. + //if (caps.Type == XINPUT_DEVTYPE_GAMEPAD) { + + // For subtypes and mappings, see this page: + // https://msdn.microsoft.com/en-us/library/windows/desktop/hh405050.aspx + switch (caps.SubType) { + default: + case XINPUT_DEVSUBTYPE_GAMEPAD: + _device_class = DeviceClass::gamepad; + _axes[0].axis = Axis::left_trigger; + _axes[1].axis = Axis::right_trigger; + _axes[2].axis = Axis::left_x; + _axes[3].axis = Axis::left_y; + _axes[4].axis = Axis::right_x; + _axes[5].axis = Axis::right_y; + break; + + case XINPUT_DEVSUBTYPE_WHEEL: + _device_class = DeviceClass::steering_wheel; + _axes[0].axis = Axis::brake; + _axes[1].axis = Axis::accelerator; + _axes[2].axis = Axis::wheel; + _axes[3].axis = Axis::none; + _axes[4].axis = Axis::none; + _axes[5].axis = Axis::none; + break; + + case XINPUT_DEVSUBTYPE_FLIGHT_STICK: + _device_class = DeviceClass::flight_stick; + _axes[0].axis = Axis::yaw; + _axes[1].axis = Axis::throttle; + _axes[2].axis = Axis::roll; + _axes[3].axis = Axis::pitch; + _axes[4].axis = Axis::none; + _axes[5].axis = Axis::none; + break; + + case XINPUT_DEVSUBTYPE_DANCE_PAD: + _device_class = DeviceClass::dance_pad; + _axes[0].axis = Axis::none; + _axes[1].axis = Axis::none; + _axes[2].axis = Axis::none; + _axes[3].axis = Axis::none; + _axes[4].axis = Axis::none; + _axes[5].axis = Axis::none; + break; + } + + _axes[0]._scale = 1.0 / 255.0; + _axes[1]._scale = 1.0 / 255.0; + _axes[2]._scale = 1.0 / 32767.5; + _axes[3]._scale = 1.0 / 32767.5; + _axes[4]._scale = 1.0 / 32767.5; + _axes[5]._scale = 1.0 / 32767.5; + + _axes[2]._bias = 0.5 / 32767.5; + _axes[3]._bias = 0.5 / 32767.5; + _axes[4]._bias = 0.5 / 32767.5; + _axes[5]._bias = 0.5 / 32767.5; + + if (caps.Flags & XINPUT_CAPS_NO_NAVIGATION) { + _buttons[0].handle = ButtonHandle::none(); + _buttons[1].handle = ButtonHandle::none(); + _buttons[2].handle = ButtonHandle::none(); + _buttons[3].handle = ButtonHandle::none(); + _buttons[4].handle = ButtonHandle::none(); + _buttons[5].handle = ButtonHandle::none(); + } else { + _buttons[0].handle = GamepadButton::dpad_up(); + _buttons[1].handle = GamepadButton::dpad_down(); + _buttons[2].handle = GamepadButton::dpad_left(); + _buttons[3].handle = GamepadButton::dpad_right(); + _buttons[4].handle = GamepadButton::start(); + _buttons[5].handle = GamepadButton::back(); + } + _buttons[6].handle = GamepadButton::lstick(); + _buttons[7].handle = GamepadButton::rstick(); + _buttons[8].handle = GamepadButton::lshoulder(); + _buttons[9].handle = GamepadButton::rshoulder(); + _buttons[10].handle = GamepadButton::guide(); + _buttons[11].handle = GamepadButton::face_a(); + _buttons[12].handle = GamepadButton::face_b(); + _buttons[13].handle = GamepadButton::face_x(); + _buttons[14].handle = GamepadButton::face_y(); + + if (caps.Vibration.wLeftMotorSpeed != 0 || + caps.Vibration.wRightMotorSpeed != 0) { + enable_feature(Feature::vibration); + } + + if (get_battery_information != nullptr) { + XINPUT_BATTERY_INFORMATION batt; + if (get_battery_information(_index, BATTERY_DEVTYPE_GAMEPAD, &batt) == ERROR_SUCCESS) { + if (batt.BatteryType != BATTERY_TYPE_DISCONNECTED && + batt.BatteryType != BATTERY_TYPE_WIRED) { + // This device has a battery. Report the battery level. + enable_feature(Feature::battery); + _battery_data.level = batt.BatteryLevel; + _battery_data.max_level = BATTERY_LEVEL_FULL; + } + } + } + + WORD buttons = state.Gamepad.wButtons; + WORD mask = 1; + for (int i = 0; i < 16; ++i) { + // Set the state without triggering a button event. + _buttons[i]._state = (buttons & mask) ? S_down : S_up; + mask <<= 1; + if (i == 10) { + // XInput skips 0x0800. + mask <<= 1; + } + } + + axis_changed(0, state.Gamepad.bLeftTrigger); + axis_changed(1, state.Gamepad.bRightTrigger); + axis_changed(2, state.Gamepad.sThumbLX); + axis_changed(3, state.Gamepad.sThumbLY); + axis_changed(4, state.Gamepad.sThumbRX); + axis_changed(5, state.Gamepad.sThumbRY); + + _last_buttons = buttons; + _last_packet = state.dwPacketNumber; +} + +/** + * Sets the vibration strength. The first argument controls a low-frequency + * motor, if present, and the latter controls a high-frequency motor. + * The values are within the 0-1 range. + */ +void XInputDevice:: +do_set_vibration(double strong, double weak) { + nassertv_always(_is_connected); + + XINPUT_VIBRATION vibration; + vibration.wLeftMotorSpeed = strong * 0xffff; + vibration.wRightMotorSpeed = weak * 0xffff; + set_state(_index, &vibration); +} + +/** + * Polls the input device for new activity, to ensure it contains the latest + * events. This will only have any effect for some types of input devices; + * others may be updated automatically, and this method will be a no-op. + */ +void XInputDevice:: +do_poll() { + // Not sure why someone would call this on a disconnected device. + if (!_is_connected) { + return; + } + + XINPUT_STATE state; + + if (get_state(_index, &state) != ERROR_SUCCESS) { + // Device was disconnected. + if (_is_connected) { + _is_connected = false; + InputDeviceManager *mgr = InputDeviceManager::get_global_ptr(); + mgr->remove_device(this); + } + return; + } + + if (state.dwPacketNumber == _last_packet) { + // No change since last time we asked. + return; + } + + // Did any buttons change state? + WORD changed_buttons = _last_buttons ^ state.Gamepad.wButtons; + + WORD mask = 1; + for (int i = 0; i < 16; ++i) { + if (changed_buttons & mask) { + button_changed(i, (state.Gamepad.wButtons & mask) != 0); + } + mask <<= 1; + if (i == 10) { + // XInput skips 0x0800. + mask <<= 1; + } + } + + axis_changed(0, state.Gamepad.bLeftTrigger); + axis_changed(1, state.Gamepad.bRightTrigger); + axis_changed(2, state.Gamepad.sThumbLX); + axis_changed(3, state.Gamepad.sThumbLY); + axis_changed(4, state.Gamepad.sThumbRX); + axis_changed(5, state.Gamepad.sThumbRY); + + _last_buttons = state.Gamepad.wButtons; + _last_packet = state.dwPacketNumber; +} + +#endif // _WIN32 diff --git a/panda/src/device/xInputDevice.h b/panda/src/device/xInputDevice.h new file mode 100644 index 0000000000..0bf3900ec4 --- /dev/null +++ b/panda/src/device/xInputDevice.h @@ -0,0 +1,60 @@ +/** + * 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 xInputDevice.h + * @author rdb + * @date 2015-07-15 + */ + +#ifndef XINPUTDEVICE_H +#define XINPUTDEVICE_H + +#include "pandabase.h" +#include "inputDevice.h" + +#if defined(_WIN32) && !defined(CPPPARSER) + +#include + +class InputDeviceManager; + +typedef struct _XINPUT_CAPABILITIES_EX XINPUT_CAPABILITIES_EX; +typedef struct _XINPUT_STATE XINPUT_STATE; + +typedef struct tagRID_DEVICE_INFO RID_DEVICE_INFO; + +/** + * This implementation of InputDevice uses Microsoft's XInput library to + * interface with an Xbox 360 game controller. + */ +class EXPCL_PANDA_DEVICE XInputDevice final : public InputDevice { +public: + XInputDevice(DWORD user_index); + ~XInputDevice(); + + bool check_arrival(const RID_DEVICE_INFO &info, DEVINST inst, + const std::string &name, const std::string &manufacturer); + void detect(InputDeviceManager *mgr); + static bool init_xinput(); + +private: + void init_device(const XINPUT_CAPABILITIES_EX &caps, const XINPUT_STATE &state); + virtual void do_set_vibration(double strong, double weak); + virtual void do_poll(); + +private: + const DWORD _index; + DWORD _last_packet; + WORD _last_buttons; + + static bool _initialized; +}; + +#endif // _WIN32 + +#endif diff --git a/panda/src/display/callbackGraphicsWindow.cxx b/panda/src/display/callbackGraphicsWindow.cxx index ebe2442d17..7372f81f64 100644 --- a/panda/src/display/callbackGraphicsWindow.cxx +++ b/panda/src/display/callbackGraphicsWindow.cxx @@ -47,28 +47,13 @@ CallbackGraphicsWindow:: ~CallbackGraphicsWindow() { } - -/** - * Returns a writable reference to the nth input device (mouse). This is - * intended to be used for the window implementation to record mouse and - * keyboard input information for the Panda system. - */ -GraphicsWindowInputDevice &CallbackGraphicsWindow:: -get_input_device(int device) { - LightMutexHolder holder(_input_lock); - nassertr(device >= 0 && device < (int)_input_devices.size(), _input_devices[0]); - return _input_devices[device]; -} - /** * Adds a new input device (mouse) to the window with the indicated name. * Returns the index of the new device. */ int CallbackGraphicsWindow:: create_input_device(const std::string &name) { - GraphicsWindowInputDevice device = - GraphicsWindowInputDevice::pointer_and_keyboard(this, name); - return add_input_device(device); + return add_input_device(GraphicsWindowInputDevice::pointer_and_keyboard(this, name)); } /** diff --git a/panda/src/display/callbackGraphicsWindow.h b/panda/src/display/callbackGraphicsWindow.h index fc7d8310f5..dee8558a53 100644 --- a/panda/src/display/callbackGraphicsWindow.h +++ b/panda/src/display/callbackGraphicsWindow.h @@ -178,7 +178,6 @@ PUBLISHED: INLINE void clear_render_callback(); INLINE CallbackObject *get_render_callback() const; - GraphicsWindowInputDevice &get_input_device(int device); int create_input_device(const std::string &name); public: diff --git a/panda/src/display/config_display.cxx b/panda/src/display/config_display.cxx index 1c2f612fcc..965d68f4e3 100644 --- a/panda/src/display/config_display.cxx +++ b/panda/src/display/config_display.cxx @@ -21,9 +21,11 @@ #include "graphicsPipe.h" #include "graphicsOutput.h" #include "graphicsBuffer.h" -#include "graphicsWindow.h" #include "graphicsDevice.h" +#include "graphicsWindow.h" +#include "graphicsWindowInputDevice.h" #include "graphicsWindowProcCallbackData.h" +#include "mouseAndKeyboard.h" #include "nativeWindowHandle.h" #include "parasiteBuffer.h" #include "pandaSystem.h" @@ -476,15 +478,6 @@ ConfigVariableBool sync_video "cheesy estimate of scene complexity. Some drivers may ignore " "this request.")); -ConfigVariableBool basic_shaders_only -("basic-shaders-only", false, - PRC_DESC("Set this to true if you aren't interested in shader model three " - "and beyond. Setting this flag will cause panda to disable " - "bleeding-edge shader functionality which tends to be unreliable " - "or broken. At some point, when functionality that is currently " - "flaky becomes reliable, we may expand the definition of what " - "constitutes 'basic' shaders.")); - /** * Initializes the library. This must be called at least once before any of * the functions or classes in this library can be used. Normally it will be @@ -510,7 +503,9 @@ init_libdisplay() { GraphicsPipe::init_type(); GraphicsStateGuardian::init_type(); GraphicsWindow::init_type(); + GraphicsWindowInputDevice::init_type(); GraphicsWindowProcCallbackData::init_type(); + MouseAndKeyboard::init_type(); NativeWindowHandle::init_type(); ParasiteBuffer::init_type(); StandardMunger::init_type(); diff --git a/panda/src/display/config_display.h b/panda/src/display/config_display.h index 0b76e06472..21f1acb510 100644 --- a/panda/src/display/config_display.h +++ b/panda/src/display/config_display.h @@ -108,7 +108,6 @@ extern EXPCL_PANDA_DISPLAY ConfigVariableDouble pixel_zoom; extern EXPCL_PANDA_DISPLAY ConfigVariableColor background_color; extern EXPCL_PANDA_DISPLAY ConfigVariableBool sync_video; -extern EXPCL_PANDA_DISPLAY ConfigVariableBool basic_shaders_only; extern EXPCL_PANDA_DISPLAY void init_libdisplay(); diff --git a/panda/src/display/graphicsEngine.cxx b/panda/src/display/graphicsEngine.cxx index c06c2b207f..ae1052ba1d 100644 --- a/panda/src/display/graphicsEngine.cxx +++ b/panda/src/display/graphicsEngine.cxx @@ -110,8 +110,8 @@ PStatCollector GraphicsEngine::_volume_sphere_pcollector("Collision Volumes:Coll PStatCollector GraphicsEngine::_test_sphere_pcollector("Collision Tests:CollisionSphere"); PStatCollector GraphicsEngine::_volume_box_pcollector("Collision Volumes:CollisionBox"); PStatCollector GraphicsEngine::_test_box_pcollector("Collision Tests:CollisionBox"); -PStatCollector GraphicsEngine::_volume_tube_pcollector("Collision Volumes:CollisionTube"); -PStatCollector GraphicsEngine::_test_tube_pcollector("Collision Tests:CollisionTube"); +PStatCollector GraphicsEngine::_volume_capsule_pcollector("Collision Volumes:CollisionCapsule"); +PStatCollector GraphicsEngine::_test_capsule_pcollector("Collision Tests:CollisionCapsule"); PStatCollector GraphicsEngine::_volume_inv_sphere_pcollector("Collision Volumes:CollisionInvSphere"); PStatCollector GraphicsEngine::_test_inv_sphere_pcollector("Collision Tests:CollisionInvSphere"); PStatCollector GraphicsEngine::_volume_geom_pcollector("Collision Volumes:CollisionGeom"); @@ -875,8 +875,8 @@ render_frame() { _test_sphere_pcollector.clear_level(); _volume_box_pcollector.clear_level(); _test_box_pcollector.clear_level(); - _volume_tube_pcollector.clear_level(); - _test_tube_pcollector.clear_level(); + _volume_capsule_pcollector.clear_level(); + _test_capsule_pcollector.clear_level(); _volume_inv_sphere_pcollector.clear_level(); _test_inv_sphere_pcollector.clear_level(); _volume_geom_pcollector.clear_level(); diff --git a/panda/src/display/graphicsEngine.h b/panda/src/display/graphicsEngine.h index 3371f20b39..e3e7df66c8 100644 --- a/panda/src/display/graphicsEngine.h +++ b/panda/src/display/graphicsEngine.h @@ -401,8 +401,8 @@ private: static PStatCollector _test_sphere_pcollector; static PStatCollector _volume_box_pcollector; static PStatCollector _test_box_pcollector; - static PStatCollector _volume_tube_pcollector; - static PStatCollector _test_tube_pcollector; + static PStatCollector _volume_capsule_pcollector; + static PStatCollector _test_capsule_pcollector; static PStatCollector _volume_inv_sphere_pcollector; static PStatCollector _test_inv_sphere_pcollector; static PStatCollector _volume_geom_pcollector; diff --git a/panda/src/display/graphicsStateGuardian.cxx b/panda/src/display/graphicsStateGuardian.cxx index 1b0caeabd0..93a3f36651 100644 --- a/panda/src/display/graphicsStateGuardian.cxx +++ b/panda/src/display/graphicsStateGuardian.cxx @@ -1255,7 +1255,8 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name, return &(_scene_setup->get_camera_transform()->get_mat()); } case Shader::SMO_model_to_view: { - return &(_inv_cs_transform->compose(_internal_transform)->get_mat()); + t = _inv_cs_transform->compose(_internal_transform)->get_mat(); + return &t; } case Shader::SMO_model_to_apiview: { return &(_internal_transform->get_mat()); diff --git a/panda/src/display/graphicsWindow.cxx b/panda/src/display/graphicsWindow.cxx index 8d618f3d9b..ee686528da 100644 --- a/panda/src/display/graphicsWindow.cxx +++ b/panda/src/display/graphicsWindow.cxx @@ -247,6 +247,17 @@ get_num_input_devices() const { return result; } +/** + * Returns the nth input device associated with the window. Typically, a + * window will have exactly one input device: the keyboard/mouse pair. + */ +InputDevice *GraphicsWindow:: +get_input_device(int device) const { + LightMutexHolder holder(_input_lock); + nassertr(device >= 0 && device < (int)_input_devices.size(), NULL); + return _input_devices[device]; +} + /** * Returns the name of the nth input device. */ @@ -256,7 +267,7 @@ get_input_device_name(int device) const { { LightMutexHolder holder(_input_lock); nassertr(device >= 0 && device < (int)_input_devices.size(), ""); - result = _input_devices[device].get_name(); + result = _input_devices[device]->get_name(); } return result; } @@ -271,7 +282,7 @@ has_pointer(int device) const { { LightMutexHolder holder(_input_lock); nassertr(device >= 0 && device < (int)_input_devices.size(), false); - result = _input_devices[device].has_pointer(); + result = _input_devices[device]->has_pointer(); } return result; } @@ -285,7 +296,7 @@ has_keyboard(int device) const { { LightMutexHolder holder(_input_lock); nassertr(device >= 0 && device < (int)_input_devices.size(), false); - result = _input_devices[device].has_keyboard(); + result = _input_devices[device]->has_keyboard(); } return result; } @@ -306,7 +317,7 @@ void GraphicsWindow:: enable_pointer_events(int device) { LightMutexHolder holder(_input_lock); nassertv(device >= 0 && device < (int)_input_devices.size()); - _input_devices[device].enable_pointer_events(); + _input_devices[device]->enable_pointer_events(); } /** @@ -316,28 +327,30 @@ void GraphicsWindow:: disable_pointer_events(int device) { LightMutexHolder holder(_input_lock); nassertv(device >= 0 && device < (int)_input_devices.size()); - _input_devices[device].disable_pointer_events(); + _input_devices[device]->disable_pointer_events(); } /** * See GraphicsWindowInputDevice::enable_pointer_mode */ +/* void GraphicsWindow:: enable_pointer_mode(int device, double speed) { LightMutexHolder holder(_input_lock); nassertv(device >= 0 && device < (int)_input_devices.size()); - _input_devices[device].enable_pointer_mode(speed); -} + _input_devices[device]->enable_pointer_mode(speed); +}*/ /** * See GraphicsWindowInputDevice::disable_pointer_mode */ +/* void GraphicsWindow:: disable_pointer_mode(int device) { LightMutexHolder holder(_input_lock); nassertv(device >= 0 && device < (int)_input_devices.size()); - _input_devices[device].disable_pointer_mode(); -} + _input_devices[device]->disable_pointer_mode(); +}*/ /** * Returns the MouseData associated with the nth input device's pointer. This @@ -350,7 +363,7 @@ get_pointer(int device) const { { LightMutexHolder holder(_input_lock); nassertr(device >= 0 && device < (int)_input_devices.size(), MouseData()); - result = _input_devices[device].get_pointer(); + result = ((const GraphicsWindowInputDevice *)_input_devices[device].p())->get_pointer(); } return result; } @@ -377,70 +390,6 @@ close_ime() { return; } -/** - * Returns true if the indicated device has a pending button event (a mouse - * button or keyboard button down/up), false otherwise. If this returns true, - * the particular event may be extracted via get_button_event(). - */ -bool GraphicsWindow:: -has_button_event(int device) const { - bool result; - { - LightMutexHolder holder(_input_lock); - nassertr(device >= 0 && device < (int)_input_devices.size(), false); - result = _input_devices[device].has_button_event(); - } - return result; -} - -/** - * Assuming a previous call to has_button_event() returned true, this returns - * the pending button event. - */ -ButtonEvent GraphicsWindow:: -get_button_event(int device) { - ButtonEvent result; - { - LightMutexHolder holder(_input_lock); - nassertr(device >= 0 && device < (int)_input_devices.size(), ButtonEvent()); - nassertr(_input_devices[device].has_button_event(), ButtonEvent()); - result = _input_devices[device].get_button_event(); - } - return result; -} - -/** - * Returns true if the indicated device has a pending pointer event (a mouse - * movement). If this returns true, the particular event may be extracted via - * get_pointer_events(). - */ -bool GraphicsWindow:: -has_pointer_event(int device) const { - bool result; - { - LightMutexHolder holder(_input_lock); - nassertr(device >= 0 && device < (int)_input_devices.size(), false); - result = _input_devices[device].has_pointer_event(); - } - return result; -} - -/** - * Assuming a previous call to has_pointer_event() returned true, this returns - * the pending pointer event list. - */ -PT(PointerEventList) GraphicsWindow:: -get_pointer_events(int device) { - PT(PointerEventList) result; - { - LightMutexHolder holder(_input_lock); - nassertr(device >= 0 && device < (int)_input_devices.size(), nullptr); - nassertr(_input_devices[device].has_pointer_event(), nullptr); - result = _input_devices[device].get_pointer_events(); - } - return result; -} - /** * Determines which of the indicated window sizes are supported by available * hardware (e.g. in fullscreen mode). @@ -739,11 +688,10 @@ system_changed_size(int x_size, int y_size) { * new device. */ int GraphicsWindow:: -add_input_device(const GraphicsWindowInputDevice &device) { +add_input_device(InputDevice *device) { LightMutexHolder holder(_input_lock); int index = (int)_input_devices.size(); _input_devices.push_back(device); - _input_devices.back().set_device_index(index); return index; } diff --git a/panda/src/display/graphicsWindow.h b/panda/src/display/graphicsWindow.h index c9c9900d3e..074aa8a41f 100644 --- a/panda/src/display/graphicsWindow.h +++ b/panda/src/display/graphicsWindow.h @@ -82,7 +82,9 @@ PUBLISHED: // Mouse and keyboard routines int get_num_input_devices() const; + InputDevice *get_input_device(int i) const; std::string get_input_device_name(int device) const; + MAKE_SEQ(get_input_devices, get_num_input_devices, get_input_device); MAKE_SEQ(get_input_device_names, get_num_input_devices, get_input_device_name); bool has_pointer(int device) const; bool has_keyboard(int device) const; @@ -90,20 +92,14 @@ PUBLISHED: void enable_pointer_events(int device); void disable_pointer_events(int device); - void enable_pointer_mode(int device, double speed); - void disable_pointer_mode(int device); + /*void enable_pointer_mode(int device, double speed); + void disable_pointer_mode(int device);*/ virtual MouseData get_pointer(int device) const; virtual bool move_pointer(int device, int x, int y); virtual void close_ime(); public: - // No need to publish these. - bool has_button_event(int device) const; - ButtonEvent get_button_event(int device); - bool has_pointer_event(int device) const; - PT(PointerEventList) get_pointer_events(int device); - virtual void add_window_proc( const GraphicsWindowProc* wnd_proc_object ){}; virtual void remove_window_proc( const GraphicsWindowProc* wnd_proc_object ){}; virtual void clear_window_procs(){}; @@ -143,8 +139,8 @@ protected: void system_changed_size(int x_size, int y_size); protected: - int add_input_device(const GraphicsWindowInputDevice &device); - typedef vector_GraphicsWindowInputDevice InputDevices; + int add_input_device(InputDevice *device); + typedef pvector InputDevices; InputDevices _input_devices; LightMutex _input_lock; diff --git a/panda/src/display/graphicsWindowInputDevice.I b/panda/src/display/graphicsWindowInputDevice.I index 446dca6ab6..d313af2c82 100644 --- a/panda/src/display/graphicsWindowInputDevice.I +++ b/panda/src/display/graphicsWindowInputDevice.I @@ -12,213 +12,45 @@ */ /** - * + * Returns the PointerData associated with the input device's pointer. This + * only makes sense if has_pointer() also returns true. */ -INLINE GraphicsWindowInputDevice:: -GraphicsWindowInputDevice() { - LightMutexHolder holder(_lock); - _flags = 0; -} - -/** - * - */ -INLINE std::string GraphicsWindowInputDevice:: -get_name() const { - LightMutexHolder holder(_lock); - return _name; -} - -/** - * - */ -INLINE bool GraphicsWindowInputDevice:: -has_pointer() const { - LightMutexHolder holder(_lock); - return ((_flags & IDF_has_pointer) != 0); -} - -/** - * - */ -INLINE bool GraphicsWindowInputDevice:: -has_keyboard() const { - LightMutexHolder holder(_lock); - return ((_flags & IDF_has_keyboard) != 0); -} - -/** - * Returns the MouseData associated with the input device's pointer. - */ -INLINE MouseData GraphicsWindowInputDevice:: +INLINE PointerData GraphicsWindowInputDevice:: get_pointer() const { LightMutexHolder holder(_lock); - return _mouse_data; -} - -/** - * Returns the MouseData associated with the input device's pointer, in raw - * form (ie, prior to any pointer_mode interpretation). - */ -INLINE MouseData GraphicsWindowInputDevice:: -get_raw_pointer() const { - LightMutexHolder holder(_lock); - return _true_mouse_data; -} - -/** - * Set the device index. This is reported in pointer events. The device - * index will be equal to the position of the GraphicsWindowInputDevice in the - * window's list. - */ -INLINE void GraphicsWindowInputDevice:: -set_device_index(int index) { - LightMutexHolder holder(_lock); - _device_index = index; -} - -/** - * Enables the generation of mouse-movement events. - */ -INLINE void GraphicsWindowInputDevice:: -enable_pointer_events() { - LightMutexHolder holder(_lock); - _enable_pointer_events = true; -} - -/** - * Disables the generation of mouse-movement events. - */ -INLINE void GraphicsWindowInputDevice:: -disable_pointer_events() { - LightMutexHolder holder(_lock); - _enable_pointer_events = false; - _pointer_events.clear(); -} - -/** - * Records that the indicated button has been depressed. - */ -INLINE void GraphicsWindowInputDevice:: -button_down(ButtonHandle button) { - button_down(button, ClockObject::get_global_clock()->get_frame_time()); -} - -/** - * Records that the indicated button was depressed earlier, and we only just - * detected the event after the fact. This is mainly useful for tracking the - * state of modifier keys. - */ -INLINE void GraphicsWindowInputDevice:: -button_resume_down(ButtonHandle button) { - button_resume_down(button, ClockObject::get_global_clock()->get_frame_time()); -} - -/** - * Records that the indicated button has been released. - */ -INLINE void GraphicsWindowInputDevice:: -button_up(ButtonHandle button) { - button_up(button, ClockObject::get_global_clock()->get_frame_time()); -} - -/** - * Records that the indicated keystroke has been generated. - */ -INLINE void GraphicsWindowInputDevice:: -keystroke(int keycode) { - keystroke(keycode, ClockObject::get_global_clock()->get_frame_time()); -} - -/** - * This should be called when the window focus is lost, so that we may miss - * upcoming button events (especially "up" events) for the next period of - * time. It generates keyboard and mouse "up" events for those buttons that - * we previously sent unpaired "down" events, so that the Panda application - * will believe all buttons are now released. - */ -INLINE void GraphicsWindowInputDevice:: -focus_lost() { - focus_lost(ClockObject::get_global_clock()->get_frame_time()); -} - -/** - * Records that the indicated button has been depressed. - */ -INLINE void GraphicsWindowInputDevice:: -raw_button_down(ButtonHandle button) { - raw_button_down(button, ClockObject::get_global_clock()->get_frame_time()); -} - -/** - * Records that the indicated button has been released. - */ -INLINE void GraphicsWindowInputDevice:: -raw_button_up(ButtonHandle button) { - raw_button_up(button, ClockObject::get_global_clock()->get_frame_time()); + if (!_pointers.empty()) { + return _pointers[0]; + } else { + return PointerData(); + } } /** * To be called by a particular kind of GraphicsWindow to indicate that the - * pointer is within the window, at the given pixel coordinates. + * pointer data has changed. */ INLINE void GraphicsWindowInputDevice:: -set_pointer_in_window(double x, double y) { - // mutex is handled in set pointer .. convience function - set_pointer(true, x, y, ClockObject::get_global_clock()->get_frame_time()); +update_pointer(PointerData data, double time) { + LightMutexHolder holder(_lock); + InputDevice::update_pointer(std::move(data), time); } /** * To be called by a particular kind of GraphicsWindow to indicate that the - * pointer is no longer within the window. + * pointer has moved by the given relative amount. */ INLINE void GraphicsWindowInputDevice:: -set_pointer_out_of_window() { - // mutex is handled in set pointer .. convience function - set_pointer(false, _mouse_data._xpos, _mouse_data._ypos, - ClockObject::get_global_clock()->get_frame_time()); +pointer_moved(double x, double y, double time) { + LightMutexHolder holder(_lock); + InputDevice::pointer_moved(0, x, y, time); } /** * To be called by a particular kind of GraphicsWindow to indicate that the - * pointer is within the window, at the given pixel coordinates. + * pointer no longer exists. */ INLINE void GraphicsWindowInputDevice:: -set_pointer_in_window(double x, double y, double time) { - // mutex is handled in set pointer .. convience function - set_pointer(true, x, y, time); -} - -/** - * To be called by a particular kind of GraphicsWindow to indicate that the - * pointer is no longer within the window. - */ -INLINE void GraphicsWindowInputDevice:: -set_pointer_out_of_window(double time) { - // mutex is handled in set pointer .. convience function - set_pointer(false, _mouse_data._xpos, _mouse_data._ypos, time); -} - -/** - * - */ -INLINE bool GraphicsWindowInputDevice:: -operator == (const GraphicsWindowInputDevice &) const { - return true; -} - -/** - * - */ -INLINE bool GraphicsWindowInputDevice:: -operator != (const GraphicsWindowInputDevice &) const { - return false; -} - -/** - * - */ -INLINE bool GraphicsWindowInputDevice:: -operator < (const GraphicsWindowInputDevice &) const { - return false; +remove_pointer(int id) { + LightMutexHolder holder(_lock); + InputDevice::remove_pointer(id); } diff --git a/panda/src/display/graphicsWindowInputDevice.cxx b/panda/src/display/graphicsWindowInputDevice.cxx index 377f3986e4..04bac6d81d 100644 --- a/panda/src/display/graphicsWindowInputDevice.cxx +++ b/panda/src/display/graphicsWindowInputDevice.cxx @@ -16,12 +16,7 @@ #include "mouseButton.h" #include "keyboardButton.h" -#define EXPCL EXPCL_PANDA_DISPLAY -#define EXPTP EXPTP_PANDA_DISPLAY -#define TYPE GraphicsWindowInputDevice -#define NAME vector_GraphicsWindowInputDevice - -#include "vector_src.cxx" +TypeHandle GraphicsWindowInputDevice::_type_handle; using std::string; @@ -34,206 +29,43 @@ using std::string; * below. */ GraphicsWindowInputDevice:: -GraphicsWindowInputDevice(GraphicsWindow *host, const string &name, int flags) : - _host(host), - _name(name), - _flags(flags), - _device_index(0), - _event_sequence(0), - _pointer_mode_enable(false), - _pointer_speed(1.0), - _enable_pointer_events(false) +GraphicsWindowInputDevice(GraphicsWindow *host, const string &name, bool pointer, bool keyboard) : + InputDevice(name, DeviceClass::virtual_device) { + if (pointer) { + enable_feature(Feature::pointer); + add_pointer(PointerType::mouse, 0); + } + if (keyboard) { + enable_feature(Feature::keyboard); + } } /** * This named constructor returns an input device that only has a pointing * device, no keyboard. */ -GraphicsWindowInputDevice GraphicsWindowInputDevice:: +PT(GraphicsWindowInputDevice) GraphicsWindowInputDevice:: pointer_only(GraphicsWindow *host, const string &name) { - return GraphicsWindowInputDevice(host, name, IDF_has_pointer); + return new GraphicsWindowInputDevice(host, name, true, false); } /** * This named constructor returns an input device that only has a keyboard, no * pointing device. */ -GraphicsWindowInputDevice GraphicsWindowInputDevice:: +PT(GraphicsWindowInputDevice) GraphicsWindowInputDevice:: keyboard_only(GraphicsWindow *host, const string &name) { - return GraphicsWindowInputDevice(host, name, IDF_has_keyboard); + return new GraphicsWindowInputDevice(host, name, false, true); } /** * This named constructor returns an input device that has both a keyboard and * pointer. */ -GraphicsWindowInputDevice GraphicsWindowInputDevice:: +PT(GraphicsWindowInputDevice) GraphicsWindowInputDevice:: pointer_and_keyboard(GraphicsWindow *host, const string &name) { - return - GraphicsWindowInputDevice(host, name, IDF_has_pointer | IDF_has_keyboard); -} - -/** - * - */ -GraphicsWindowInputDevice:: -GraphicsWindowInputDevice(const GraphicsWindowInputDevice ©) -{ - *this = copy; -} - -/** - * - */ -void GraphicsWindowInputDevice:: -operator = (const GraphicsWindowInputDevice ©) -{ - LightMutexHolder holder(_lock); - LightMutexHolder holder1(copy._lock); - _host = copy._host; - _name = copy._name; - _flags = copy._flags; - _device_index = copy._device_index; - _event_sequence = copy._event_sequence; - _pointer_mode_enable = copy._pointer_mode_enable; - _pointer_speed = copy._pointer_speed; - _enable_pointer_events = copy._enable_pointer_events; - _mouse_data = copy._mouse_data; - _true_mouse_data = copy._true_mouse_data; - _button_events = copy._button_events; - _pointer_events = copy._pointer_events; -} - -/** - * - */ -GraphicsWindowInputDevice:: -~GraphicsWindowInputDevice() { -} - -/** - * Returns true if this device has a pending button event (a mouse button or - * keyboard button down/up), false otherwise. If this returns true, the - * particular event may be extracted via get_button_event(). - */ -bool GraphicsWindowInputDevice:: -has_button_event() const { - LightMutexHolder holder(_lock); - return !_button_events.empty(); -} - -/** - * Assuming a previous call to has_button_event() returned true, this returns - * the pending button event. - */ -ButtonEvent GraphicsWindowInputDevice:: -get_button_event() { - LightMutexHolder holder(_lock); - ButtonEvent be = _button_events.front(); - _button_events.pop_front(); - return be; -} - -/** - * Returns true if this device has a pending pointer event (a mouse movement), - * or false otherwise. If this returns true, the particular event may be - * extracted via get_pointer_event(). - */ -bool GraphicsWindowInputDevice:: -has_pointer_event() const { - LightMutexHolder holder(_lock); - return (_pointer_events != nullptr); -} - -/** - * Returns a PointerEventList containing all the recent pointer events. - */ -PT(PointerEventList) GraphicsWindowInputDevice:: -get_pointer_events() { - LightMutexHolder holder(_lock); - PT(PointerEventList) result = _pointer_events; - _pointer_events = nullptr; - return result; -} - -/** - * There are two modes: raw mode, and pointer mode. In pointer mode, the - * mouse stops when it reaches the edges of the window. In raw mode, the - * mouse ignores the screen boundaries and can continue indefinitely, even - * into negative coordinates. In raw mode, each "blip" from the mouse - * hardware corresponds to a change of 1 unit in the mouse's (x,y) coordinate. - * In pointer mode, a variety of speed adjustment factors and concepts like - * "mouse acceleration" may be applied. - * - * Mouse zero represents the system mouse pointer. This is by definition a - * pointer, not a raw mouse. It is an error to try to enable or disable - * pointer mode on mouse zero. - */ -void GraphicsWindowInputDevice:: -enable_pointer_mode(double speed) { - LightMutexHolder holder(_lock); - nassertv(_device_index != 0); - _pointer_mode_enable = true; - _pointer_speed = speed; - _mouse_data._xpos = _host->get_x_size() * 0.5; - _mouse_data._ypos = _host->get_y_size() * 0.5; - _mouse_data._in_window = true; -} - -/** - * see enable_pointer_mode. - */ -void GraphicsWindowInputDevice:: -disable_pointer_mode() { - LightMutexHolder holder(_lock); - nassertv(_device_index != 0); - _pointer_mode_enable = false; - _pointer_speed = 1.0; - _mouse_data = _true_mouse_data; -} - -/** - * Records that a mouse movement has taken place. - */ -void GraphicsWindowInputDevice:: -set_pointer(bool inwin, double x, double y, double time) { - LightMutexHolder holder(_lock); - - double delta_x = x - _true_mouse_data._xpos; - double delta_y = y - _true_mouse_data._ypos; - _true_mouse_data._in_window = inwin; - _true_mouse_data._xpos = x; - _true_mouse_data._ypos = y; - - if (_pointer_mode_enable) { - double pointer_x = _mouse_data._xpos; - double pointer_y = _mouse_data._ypos; - pointer_x += (delta_x * _pointer_speed); - pointer_y += (delta_y * _pointer_speed); - double xhi = _host->get_x_size(); - double yhi = _host->get_y_size(); - if (pointer_x < 0.0) pointer_x = 0.0; - if (pointer_y < 0.0) pointer_y = 0.0; - if (pointer_x > xhi) pointer_x = xhi; - if (pointer_y > yhi) pointer_y = yhi; - _mouse_data._in_window = true; - _mouse_data._xpos = pointer_x; - _mouse_data._ypos = pointer_y; - } else { - _mouse_data = _true_mouse_data; - } - - if (_enable_pointer_events) { - int seq = _event_sequence++; - if (_pointer_events == nullptr) { - _pointer_events = new PointerEventList(); - } - _pointer_events->add_event(_mouse_data._in_window, - _mouse_data._xpos, - _mouse_data._ypos, - seq, time); - } + return new GraphicsWindowInputDevice(host, name, true, true); } /** @@ -242,7 +74,7 @@ set_pointer(bool inwin, double x, double y, double time) { void GraphicsWindowInputDevice:: button_down(ButtonHandle button, double time) { LightMutexHolder holder(_lock); - _button_events.push_back(ButtonEvent(button, ButtonEvent::T_down, time)); + _button_events->add_event(ButtonEvent(button, ButtonEvent::T_down, time)); _buttons_held.insert(button); } @@ -254,7 +86,7 @@ button_down(ButtonHandle button, double time) { void GraphicsWindowInputDevice:: button_resume_down(ButtonHandle button, double time) { LightMutexHolder holder(_lock); - _button_events.push_back(ButtonEvent(button, ButtonEvent::T_resume_down, time)); + _button_events->add_event(ButtonEvent(button, ButtonEvent::T_resume_down, time)); _buttons_held.insert(button); } @@ -264,7 +96,7 @@ button_resume_down(ButtonHandle button, double time) { void GraphicsWindowInputDevice:: button_up(ButtonHandle button, double time) { LightMutexHolder holder(_lock); - _button_events.push_back(ButtonEvent(button, ButtonEvent::T_up, time)); + _button_events->add_event(ButtonEvent(button, ButtonEvent::T_up, time)); _buttons_held.erase(button); } @@ -274,7 +106,7 @@ button_up(ButtonHandle button, double time) { void GraphicsWindowInputDevice:: keystroke(int keycode, double time) { LightMutexHolder holder(_lock); - _button_events.push_back(ButtonEvent(keycode, time)); + _button_events->add_event(ButtonEvent(keycode, time)); } /** @@ -286,7 +118,7 @@ void GraphicsWindowInputDevice:: candidate(const std::wstring &candidate_string, size_t highlight_start, size_t highlight_end, size_t cursor_pos) { LightMutexHolder holder(_lock); - _button_events.push_back(ButtonEvent(candidate_string, + _button_events->add_event(ButtonEvent(candidate_string, highlight_start, highlight_end, cursor_pos)); } @@ -303,7 +135,7 @@ focus_lost(double time) { LightMutexHolder holder(_lock); ButtonsHeld::iterator bi; for (bi = _buttons_held.begin(); bi != _buttons_held.end(); ++bi) { - _button_events.push_back(ButtonEvent(*bi, ButtonEvent::T_up, time)); + _button_events->add_event(ButtonEvent(*bi, ButtonEvent::T_up, time)); } _buttons_held.clear(); } @@ -314,7 +146,7 @@ focus_lost(double time) { void GraphicsWindowInputDevice:: raw_button_down(ButtonHandle button, double time) { LightMutexHolder holder(_lock); - _button_events.push_back(ButtonEvent(button, ButtonEvent::T_raw_down, time)); + _button_events->add_event(ButtonEvent(button, ButtonEvent::T_raw_down, time)); } /** @@ -323,5 +155,47 @@ raw_button_down(ButtonHandle button, double time) { void GraphicsWindowInputDevice:: raw_button_up(ButtonHandle button, double time) { LightMutexHolder holder(_lock); - _button_events.push_back(ButtonEvent(button, ButtonEvent::T_raw_up, time)); + _button_events->add_event(ButtonEvent(button, ButtonEvent::T_raw_up, time)); +} + +/** + * To be called by a particular kind of GraphicsWindow to indicate that the + * pointer is within the window, at the given pixel coordinates. + */ +void GraphicsWindowInputDevice:: +set_pointer_in_window(double x, double y, double time) { + LightMutexHolder holder(_lock); + PointerData data = _pointers[0]; + data._id = 0; + data._type = PointerType::mouse; + data._xpos = x; + data._ypos = y; + data._in_window = true; + InputDevice::update_pointer(data, time); +} + +/** + * To be called by a particular kind of GraphicsWindow to indicate that the + * pointer is no longer within the window. + */ +void GraphicsWindowInputDevice:: +set_pointer_out_of_window(double time) { + LightMutexHolder holder(_lock); + if (_pointers.empty()) { + return; + } + + _pointers[0]._in_window = false; + _pointers[0]._pressure = 0.0; + + if (_enable_pointer_events) { + int seq = _event_sequence++; + if (_pointer_events.is_null()) { + _pointer_events = new PointerEventList(); + } + _pointer_events->add_event(_pointers[0]._in_window, + _pointers[0]._xpos, + _pointers[0]._ypos, + seq, time); + } } diff --git a/panda/src/display/graphicsWindowInputDevice.h b/panda/src/display/graphicsWindowInputDevice.h index b570b5f384..22317a1ab0 100644 --- a/panda/src/display/graphicsWindowInputDevice.h +++ b/panda/src/display/graphicsWindowInputDevice.h @@ -15,131 +15,73 @@ #define GRAPHICSWINDOWINPUTDEVICE_H #include "pandabase.h" - -#include "buttonEvent.h" -#include "pointerEvent.h" -#include "pointerEventList.h" -#include "mouseData.h" -#include "clockObject.h" - -#include "pdeque.h" -#include "pvector.h" -#include "lightMutex.h" -#include "lightMutexHolder.h" +#include "inputDevice.h" // Forward declarations class GraphicsWindow; /** - * This is a structure representing a single input device that may be - * associated with a window. Typically this will be a keyboard/mouse pair, - * and there will be exactly one of these associated with each window, but - * other variants are possible. + * This is a virtual input device that represents the keyboard and mouse pair + * that is associated with a particular window. It collects mouse and + * keyboard events from the windowing system while the window is in focus. */ -class EXPCL_PANDA_DISPLAY GraphicsWindowInputDevice { +class EXPCL_PANDA_DISPLAY GraphicsWindowInputDevice : public InputDevice { private: - GraphicsWindowInputDevice(GraphicsWindow *host, const std::string &name, int flags); + GraphicsWindowInputDevice(GraphicsWindow *host, const std::string &name, + bool pointer, bool keyboard); public: - static GraphicsWindowInputDevice pointer_only(GraphicsWindow *host, const std::string &name); - static GraphicsWindowInputDevice keyboard_only(GraphicsWindow *host, const std::string &name); - static GraphicsWindowInputDevice pointer_and_keyboard(GraphicsWindow *host, const std::string &name); + static PT(GraphicsWindowInputDevice) pointer_only(GraphicsWindow *host, const std::string &name); + static PT(GraphicsWindowInputDevice) keyboard_only(GraphicsWindow *host, const std::string &name); + static PT(GraphicsWindowInputDevice) pointer_and_keyboard(GraphicsWindow *host, const std::string &name); - INLINE GraphicsWindowInputDevice(); - GraphicsWindowInputDevice(const GraphicsWindowInputDevice ©); - void operator = (const GraphicsWindowInputDevice ©); - ~GraphicsWindowInputDevice(); - - INLINE std::string get_name() const; - INLINE bool has_pointer() const; - INLINE bool has_keyboard() const; - - INLINE void set_device_index(int index); - - INLINE MouseData get_pointer() const; - INLINE MouseData get_raw_pointer() const; - - INLINE void enable_pointer_events(); - INLINE void disable_pointer_events(); - - void enable_pointer_mode(double speed); - void disable_pointer_mode(); - - bool has_button_event() const; - ButtonEvent get_button_event(); - bool has_pointer_event() const; - PT(PointerEventList) get_pointer_events(); + GraphicsWindowInputDevice() = default; PUBLISHED: // The following interface is for the various kinds of GraphicsWindows to // record the data incoming on the device. - INLINE void button_down(ButtonHandle button); - INLINE void button_resume_down(ButtonHandle button); - INLINE void button_up(ButtonHandle button); - INLINE void keystroke(int keycode); - INLINE void focus_lost(); - INLINE void raw_button_down(ButtonHandle button); - INLINE void raw_button_up(ButtonHandle button); - INLINE void set_pointer_in_window(double x, double y); - INLINE void set_pointer_out_of_window(); + void button_down(ButtonHandle button, double time = ClockObject::get_global_clock()->get_frame_time()); + void button_resume_down(ButtonHandle button, double time = ClockObject::get_global_clock()->get_frame_time()); + void button_up(ButtonHandle button, double time = ClockObject::get_global_clock()->get_frame_time()); - void button_down(ButtonHandle button, double time); - void button_resume_down(ButtonHandle button, double time); - void button_up(ButtonHandle button, double time); - void keystroke(int keycode, double time); + void keystroke(int keycode, double time = ClockObject::get_global_clock()->get_frame_time()); void candidate(const std::wstring &candidate_string, size_t highlight_start, size_t highlight_end, size_t cursor_pos); - void focus_lost(double time); - void raw_button_down(ButtonHandle button, double time); - void raw_button_up(ButtonHandle button, double time); - INLINE void set_pointer_in_window(double x, double y, double time); - INLINE void set_pointer_out_of_window(double time); - void set_pointer(bool inwin, double x, double y, double time); + void focus_lost(double time = ClockObject::get_global_clock()->get_frame_time()); -public: - // We need these methods to make VC++ happy when we try to instantiate a - // pvector. They don't do anything useful. - INLINE bool operator == (const GraphicsWindowInputDevice &other) const; - INLINE bool operator != (const GraphicsWindowInputDevice &other) const; - INLINE bool operator < (const GraphicsWindowInputDevice &other) const; + void raw_button_down(ButtonHandle button, double time = ClockObject::get_global_clock()->get_frame_time()); + void raw_button_up(ButtonHandle button, double time = ClockObject::get_global_clock()->get_frame_time()); + + INLINE PointerData get_pointer() const; + void set_pointer_in_window(double x, double y, double time = ClockObject::get_global_clock()->get_frame_time()); + void set_pointer_out_of_window(double time = ClockObject::get_global_clock()->get_frame_time()); + INLINE void update_pointer(PointerData data, double time = ClockObject::get_global_clock()->get_frame_time()); + INLINE void pointer_moved(double x, double y, double time = ClockObject::get_global_clock()->get_frame_time()); + INLINE void remove_pointer(int id); private: - enum InputDeviceFlags { - IDF_has_pointer = 0x01, - IDF_has_keyboard = 0x02 - }; - typedef pdeque ButtonEvents; - - LightMutex _lock; - - GraphicsWindow *_host; - - std::string _name; - int _flags; - int _device_index; - int _event_sequence; - - bool _pointer_mode_enable; - double _pointer_speed; - - bool _enable_pointer_events; - MouseData _mouse_data; - MouseData _true_mouse_data; - ButtonEvents _button_events; - PT(PointerEventList) _pointer_events; - typedef pset ButtonsHeld; ButtonsHeld _buttons_held; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + InputDevice::init_type(); + register_type(_type_handle, "GraphicsWindowInputDevice", + InputDevice::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; }; #include "graphicsWindowInputDevice.I" -#define EXPCL EXPCL_PANDA_DISPLAY -#define EXPTP EXPTP_PANDA_DISPLAY -#define TYPE GraphicsWindowInputDevice -#define NAME vector_GraphicsWindowInputDevice - -#include "vector_src.h" - #endif diff --git a/panda/src/device/mouseAndKeyboard.cxx b/panda/src/display/mouseAndKeyboard.cxx similarity index 77% rename from panda/src/device/mouseAndKeyboard.cxx rename to panda/src/display/mouseAndKeyboard.cxx index 4a8e599afe..b1eb49027d 100644 --- a/panda/src/device/mouseAndKeyboard.cxx +++ b/panda/src/display/mouseAndKeyboard.cxx @@ -27,7 +27,7 @@ MouseAndKeyboard:: MouseAndKeyboard(GraphicsWindow *window, int device, const std::string &name) : DataNode(name), _window(window), - _device(device) + _device(window->get_input_device(device)) { _pixel_xy_output = define_output("pixel_xy", EventStoreVec2::get_class_type()); _pixel_size_output = define_output("pixel_size", EventStoreVec2::get_class_type()); @@ -38,7 +38,8 @@ MouseAndKeyboard(GraphicsWindow *window, int device, const std::string &name) : _pixel_xy = new EventStoreVec2(LPoint2(0.0f, 0.0f)); _pixel_size = new EventStoreVec2(LPoint2(0.0f, 0.0f)); _xy = new EventStoreVec2(LPoint2(0.0f, 0.0f)); - _button_events = new ButtonEventList; + + _device->enable_pointer_events(); } /** @@ -48,23 +49,9 @@ MouseAndKeyboard(GraphicsWindow *window, int device, const std::string &name) : void MouseAndKeyboard:: set_source(GraphicsWindow *window, int device) { _window = window; - _device = device; -} + _device = window->get_input_device(device); -/** - * Returns the associated source window. - */ -PT(GraphicsWindow) MouseAndKeyboard:: -get_source_window() const { - return _window; -} - -/** - * Returns the associated source device. - */ -int MouseAndKeyboard:: -get_source_device() const { - return _device; + //_device->enable_pointer_events(); } /** @@ -78,17 +65,15 @@ get_source_device() const { void MouseAndKeyboard:: do_transmit_data(DataGraphTraverser *, const DataNodeTransmit &, DataNodeTransmit &output) { - if (_window->has_button_event(_device)) { - // Fill up the button events. - _button_events->clear(); - while (_window->has_button_event(_device)) { - ButtonEvent be = _window->get_button_event(_device); - _button_events->add_event(be); - } - output.set_data(_button_events_output, EventParameter(_button_events)); + + GraphicsWindowInputDevice *device = (GraphicsWindowInputDevice *)_device.p(); + + if (device->has_button_event()) { + PT(ButtonEventList) bel = device->get_button_events(); + output.set_data(_button_events_output, EventParameter(bel)); } - if (_window->has_pointer_event(_device)) { - PT(PointerEventList) pel = _window->get_pointer_events(_device); + if (device->has_pointer_event()) { + PT(PointerEventList) pel = device->get_pointer_events(); output.set_data(_pointer_events_output, EventParameter(pel)); } @@ -101,8 +86,8 @@ do_transmit_data(DataGraphTraverser *, const DataNodeTransmit &, _pixel_size->set_value(LPoint2(w, h)); output.set_data(_pixel_size_output, EventParameter(_pixel_size)); - if (_window->has_pointer(_device)) { - const MouseData &mdata = _window->get_pointer(_device); + if (device->has_pointer()) { + PointerData mdata = device->get_pointer(); if (mdata._in_window) { // Get mouse motion in pixels. diff --git a/panda/src/device/mouseAndKeyboard.h b/panda/src/display/mouseAndKeyboard.h similarity index 92% rename from panda/src/device/mouseAndKeyboard.h rename to panda/src/display/mouseAndKeyboard.h index 15f7544fb8..035822a6ff 100644 --- a/panda/src/device/mouseAndKeyboard.h +++ b/panda/src/display/mouseAndKeyboard.h @@ -38,14 +38,11 @@ * the system, you must attach an EventThrower to the MouseAndKeyboard object; * otherwise, the events will be discarded. */ -class EXPCL_PANDA_DEVICE MouseAndKeyboard : public DataNode { +class EXPCL_PANDA_DISPLAY MouseAndKeyboard : public DataNode { PUBLISHED: explicit MouseAndKeyboard(GraphicsWindow *window, int device, const std::string &name); void set_source(GraphicsWindow *window, int device); - PT(GraphicsWindow) get_source_window() const; - int get_source_device() const; - protected: // Inherited from DataNode virtual void do_transmit_data(DataGraphTraverser *trav, @@ -63,10 +60,9 @@ private: PT(EventStoreVec2) _pixel_xy; PT(EventStoreVec2) _pixel_size; PT(EventStoreVec2) _xy; - PT(ButtonEventList) _button_events; PT(GraphicsWindow) _window; - int _device; + PT(InputDevice) _device; public: static TypeHandle get_class_type() { diff --git a/panda/src/display/p3display_composite2.cxx b/panda/src/display/p3display_composite2.cxx index b9d33ab445..ef13357435 100644 --- a/panda/src/display/p3display_composite2.cxx +++ b/panda/src/display/p3display_composite2.cxx @@ -4,6 +4,7 @@ #include "graphicsWindowProc.cxx" #include "graphicsWindowProcCallbackData.cxx" #include "graphicsWindowInputDevice.cxx" +#include "mouseAndKeyboard.cxx" #include "nativeWindowHandle.cxx" #include "parasiteBuffer.cxx" #include "standardMunger.cxx" diff --git a/panda/src/display/subprocessWindow.cxx b/panda/src/display/subprocessWindow.cxx index 5ea85c3cc9..21e4b634c2 100644 --- a/panda/src/display/subprocessWindow.cxx +++ b/panda/src/display/subprocessWindow.cxx @@ -39,9 +39,8 @@ SubprocessWindow(GraphicsEngine *engine, GraphicsPipe *pipe, GraphicsOutput *host) : GraphicsWindow(engine, pipe, name, fb_prop, win_prop, flags, gsg, host) { - GraphicsWindowInputDevice device = - GraphicsWindowInputDevice::pointer_and_keyboard(this, "keyboard/mouse"); - _input_devices.push_back(device); + _input = GraphicsWindowInputDevice::pointer_and_keyboard(this, "keyboard/mouse"); + _input_devices.push_back(_input.p()); // This will be an offscreen buffer that we use to render the actual // contents. @@ -83,9 +82,9 @@ process_events() { while (_swbuffer->get_event(swb_event)) { // Deal with this event. if (swb_event._flags & SubprocessWindowBuffer::EF_mouse_position) { - _input_devices[0].set_pointer_in_window(swb_event._x, swb_event._y); + _input->set_pointer_in_window(swb_event._x, swb_event._y); } else if ((swb_event._flags & SubprocessWindowBuffer::EF_has_mouse) == 0) { - _input_devices[0].set_pointer_out_of_window(); + _input->set_pointer_out_of_window(); } unsigned int diff = swb_event._flags ^ _last_event_flags; @@ -112,14 +111,14 @@ process_events() { int keycode; button = translate_key(keycode, swb_event._code, swb_event._flags); if (keycode != 0 && swb_event._type != SubprocessWindowBuffer::ET_button_up) { - _input_devices[0].keystroke(keycode); + _input->keystroke(keycode); } } if (swb_event._type == SubprocessWindowBuffer::ET_button_up) { - _input_devices[0].button_up(button); + _input->button_up(button); } else if (swb_event._type == SubprocessWindowBuffer::ET_button_down) { - _input_devices[0].button_down(button); + _input->button_down(button); } } } @@ -556,9 +555,9 @@ translate_key(int &keycode, int os_code, unsigned int flags) const { void SubprocessWindow:: transition_button(unsigned int flags, ButtonHandle button) { if (flags) { - _input_devices[0].button_down(button); + _input->button_down(button); } else { - _input_devices[0].button_up(button); + _input->button_up(button); } } diff --git a/panda/src/display/subprocessWindow.h b/panda/src/display/subprocessWindow.h index 1339b8f90d..0a887d9e8e 100644 --- a/panda/src/display/subprocessWindow.h +++ b/panda/src/display/subprocessWindow.h @@ -75,6 +75,7 @@ private: private: PT(GraphicsBuffer) _buffer; PT(Texture) _texture; + PT(GraphicsWindowInputDevice) _input; int _fd; size_t _mmap_size; diff --git a/panda/src/doc/collisionFlags.txt b/panda/src/doc/collisionFlags.txt index 4989ef8e33..04a890f42b 100644 --- a/panda/src/doc/collisionFlags.txt +++ b/panda/src/doc/collisionFlags.txt @@ -6,7 +6,7 @@ camera-collide: for things that the camera should avoid trigger: for things (usually not barriers or floors) that should trigger an event when avatars intersect with them sphere: for things that should have a collision sphere around them -tube: for things that should have a collision tube (cylinder) around them +tube: for things that should have a collision capsule around them NOTES diff --git a/panda/src/dxml/config_dxml.cxx b/panda/src/dxml/config_dxml.cxx index bbc119cb14..8cb75706f9 100644 --- a/panda/src/dxml/config_dxml.cxx +++ b/panda/src/dxml/config_dxml.cxx @@ -30,14 +30,12 @@ ConfigureFn(config_dxml) { init_libdxml(); } -//////////////////////////////////////////////////////////////////// -// Function: init_libdxml -// Description: Initializes the library. This must be called at -// least once before any of the functions or classes in -// this library can be used. Normally it will be -// called by the static initializers and need not be -// called explicitly, but special cases exist. -//////////////////////////////////////////////////////////////////// +/** + * Initializes the library. This must be called at least once before any of + * the functions or classes in this library can be used. Normally it will be + * called by the static initializers and need not be called explicitly, but + * special cases exist. + */ void init_libdxml() { static bool initialized = false; @@ -48,11 +46,10 @@ init_libdxml() { } BEGIN_PUBLISH -//////////////////////////////////////////////////////////////////// -// Function: read_xml_stream -// Description: Reads an XML document from the indicated stream. -// Returns the document, or NULL on error. -//////////////////////////////////////////////////////////////////// +/** + * Reads an XML document from the indicated stream. + * @returns the document, or NULL on error. + */ TiXmlDocument * read_xml_stream(std::istream &in) { TiXmlDocument *doc = new TiXmlDocument; @@ -67,10 +64,9 @@ read_xml_stream(std::istream &in) { END_PUBLISH BEGIN_PUBLISH -//////////////////////////////////////////////////////////////////// -// Function: write_xml_stream -// Description: Writes an XML document to the indicated stream. -//////////////////////////////////////////////////////////////////// +/** + * Writes an XML document to the indicated stream. + */ void write_xml_stream(std::ostream &out, TiXmlDocument *doc) { out << *doc; @@ -78,10 +74,9 @@ write_xml_stream(std::ostream &out, TiXmlDocument *doc) { END_PUBLISH BEGIN_PUBLISH -//////////////////////////////////////////////////////////////////// -// Function: print_xml -// Description: Writes an XML object to stdout, with formatting. -//////////////////////////////////////////////////////////////////// +/** + * Writes an XML object to stdout, with formatting. + */ void print_xml(TiXmlNode *xnode) { xnode->Print(stdout, 0); @@ -89,12 +84,10 @@ print_xml(TiXmlNode *xnode) { END_PUBLISH BEGIN_PUBLISH -//////////////////////////////////////////////////////////////////// -// Function: print_xml_to_file -// Description: Writes an XML object to the indicated file, with -// formatting. Unfortunately the VFS cannot be -// supported; the file must be a real filename on disk. -//////////////////////////////////////////////////////////////////// +/** + * Writes an XML object to the indicated file, with formatting. Unfortunately + * the VFS cannot be supported; the file must be a real filename on disk. + */ void print_xml_to_file(const Filename &filename, TiXmlNode *xnode) { std::string os_name = filename.to_os_specific(); diff --git a/panda/src/egg/eggGroup.cxx b/panda/src/egg/eggGroup.cxx index 6bc6f152f0..fe36f2b123 100644 --- a/panda/src/egg/eggGroup.cxx +++ b/panda/src/egg/eggGroup.cxx @@ -897,7 +897,8 @@ string_cs_type(const string &strval) { } else if (cmp_nocase_uh(strval, "inv-sphere") == 0 || cmp_nocase_uh(strval, "invsphere") == 0) { return CST_inv_sphere; - } else if (cmp_nocase_uh(strval, "tube") == 0) { + } else if (cmp_nocase_uh(strval, "tube") == 0 || + cmp_nocase_uh(strval, "capsule") == 0) { return CST_tube; } else if (cmp_nocase_uh(strval, "floor-mesh") == 0 || cmp_nocase_uh(strval, "floormesh") == 0) { diff --git a/panda/src/egg2pg/eggLoader.cxx b/panda/src/egg2pg/eggLoader.cxx index 9d885e0234..6aeb84f54f 100644 --- a/panda/src/egg2pg/eggLoader.cxx +++ b/panda/src/egg2pg/eggLoader.cxx @@ -74,7 +74,7 @@ #include "collisionNode.h" #include "collisionSphere.h" #include "collisionInvSphere.h" -#include "collisionTube.h" +#include "collisionCapsule.h" #include "collisionPlane.h" #include "collisionPolygon.h" #include "collisionFloorMesh.h" @@ -2865,7 +2865,7 @@ make_collision_solids(EggGroup *start_group, EggGroup *egg_group, break; case EggGroup::CST_tube: - make_collision_tube(egg_group, cnode, start_group->get_collide_flags()); + make_collision_capsule(egg_group, cnode, start_group->get_collide_flags()); break; case EggGroup::CST_floor_mesh: @@ -3045,12 +3045,12 @@ make_collision_inv_sphere(EggGroup *egg_group, CollisionNode *cnode, } /** - * Creates a single CollisionTube corresponding to the polygons associated + * Creates a single CollisionCapsule corresponding to the polygons associated * with this group. */ void EggLoader:: -make_collision_tube(EggGroup *egg_group, CollisionNode *cnode, - EggGroup::CollideFlags flags) { +make_collision_capsule(EggGroup *egg_group, CollisionNode *cnode, + EggGroup::CollideFlags flags) { EggGroup *geom_group = find_collision_geometry(egg_group, flags); if (geom_group != nullptr) { // Collect all of the vertices. @@ -3175,7 +3175,7 @@ make_collision_tube(EggGroup *egg_group, CollisionNode *cnode, // Transform all of the points so that the major axis is along the Y // axis, and the origin is the center. This is very similar to the - // CollisionTube's idea of its canonical orientation (although not + // CollisionCapsule's idea of its canonical orientation (although not // exactly the same, since it is centered on the origin instead of // having point_a on the origin). It makes it easier to determine the // length and radius of the cylinder. @@ -3230,11 +3230,11 @@ make_collision_tube(EggGroup *egg_group, CollisionNode *cnode, LPoint3d point_a = center - half; LPoint3d point_b = center + half; - CollisionTube *cstube = - new CollisionTube(LCAST(PN_stdfloat, point_a), LCAST(PN_stdfloat, point_b), + CollisionCapsule *cscapsule = + new CollisionCapsule(LCAST(PN_stdfloat, point_a), LCAST(PN_stdfloat, point_b), radius); - apply_collision_flags(cstube, flags); - cnode->add_solid(cstube); + apply_collision_flags(cscapsule, flags); + cnode->add_solid(cscapsule); } } } diff --git a/panda/src/egg2pg/eggLoader.h b/panda/src/egg2pg/eggLoader.h index eb81317bd8..0f974f41c2 100644 --- a/panda/src/egg2pg/eggLoader.h +++ b/panda/src/egg2pg/eggLoader.h @@ -180,8 +180,8 @@ private: EggGroup::CollideFlags flags); void make_collision_inv_sphere(EggGroup *egg_group, CollisionNode *cnode, EggGroup::CollideFlags flags); - void make_collision_tube(EggGroup *egg_group, CollisionNode *cnode, - EggGroup::CollideFlags flags); + void make_collision_capsule(EggGroup *egg_group, CollisionNode *cnode, + EggGroup::CollideFlags flags); void make_collision_floor_mesh(EggGroup *egg_group, CollisionNode *cnode, EggGroup::CollideFlags flags); void apply_collision_flags(CollisionSolid *solid, diff --git a/panda/src/egg2pg/eggSaver.cxx b/panda/src/egg2pg/eggSaver.cxx index fb6bb3a59b..451497381c 100644 --- a/panda/src/egg2pg/eggSaver.cxx +++ b/panda/src/egg2pg/eggSaver.cxx @@ -39,7 +39,7 @@ #include "collisionSphere.h" #include "collisionBox.h" #include "collisionInvSphere.h" -#include "collisionTube.h" +#include "collisionCapsule.h" #include "textureStage.h" #include "geomNode.h" #include "geom.h" @@ -52,7 +52,6 @@ #include "modelNode.h" #include "animBundleNode.h" #include "animChannelMatrixXfmTable.h" -#include "characterJointEffect.h" #include "characterJoint.h" #include "character.h" #include "string_utils.h" @@ -159,16 +158,6 @@ convert_node(const WorkingNodePath &node_path, EggGroupNode *egg_parent, convert_character_node(DCAST(Character, node), node_path, egg_parent, has_decal); } else { - // Is this a ModelNode that represents an exposed joint? If so, skip it, - // as we'll take care of it when building the joint hierarchy. - if (node->get_type() == ModelNode::get_class_type()) { - ModelNode *model_node = (ModelNode *)node; - if (model_node->get_preserve_transform() == ModelNode::PT_net && - model_node->has_effect(CharacterJointEffect::get_class_type())) { - return; - } - } - // Just a generic node. EggGroup *egg_group = new EggGroup(node->get_name()); egg_parent->add_child(egg_group); @@ -368,17 +357,6 @@ convert_character_bundle(PartGroup *bundleNode, EggGroupNode *egg_parent, Charac EggGroup *joint = new EggGroup(bundleNode->get_name()); joint->add_matrix4(transformd); joint->set_group_type(EggGroup::GT_joint); - - // Is this joint exposed? - NodePathCollection coll = character_joint->get_net_transforms(); - for (size_t i = 0; i < coll.size(); ++i) { - const NodePath &np = coll[i]; - if (np.get_name() == bundleNode->get_name() && np.node()->is_of_type(ModelNode::get_class_type())) { - joint->set_dcs_type(EggGroup::DC_net); - break; - } - } - joint_group = joint; egg_parent->add_child(joint_group); if (joint_map != nullptr) { @@ -409,33 +387,16 @@ convert_character_node(Character *node, const WorkingNodePath &node_path, // A sequence node gets converted to an ordinary EggGroup, we only apply the // appropriate switch attributes to turn it into a sequence. + // We have to use DT_structured since it is the only mode that preserves the + // node hierarchy, including LODNodes and CollisionNodes that may be under + // this Character node. EggGroup *egg_group = new EggGroup(node->get_name()); + egg_group->set_dart_type(EggGroup::DT_structured); egg_parent->add_child(egg_group); apply_node_properties(egg_group, node); CharacterJointMap joint_map; - bool is_structured = false; - - int num_children = node->get_num_children(); - for (int i = 0; i < num_children; i++) { - PandaNode *child = node->get_child(i); - convert_node(WorkingNodePath(node_path, child), egg_parent, has_decal, &joint_map); - - TypeHandle type = child->get_type(); - if (child->get_num_children() > 0 || - (type != GeomNode::get_class_type() && type != ModelNode::get_class_type())) { - is_structured = true; - } - } - - // We have to use DT_structured if it is necessary to preserve any node - // hierarchy, such as LODNodes and CollisionNodes that may be under this - // Character node. - if (is_structured) { - egg_group->set_dart_type(EggGroup::DT_structured); - } else { - egg_group->set_dart_type(EggGroup::DT_default); - } + recurse_nodes(node_path, egg_group, has_decal, &joint_map); // turn it into a switch.. egg_group->set_switch_flag(true); @@ -620,13 +581,13 @@ convert_collision_node(CollisionNode *node, const WorkingNodePath &node_path, egg_poly->add_vertex(cvpool->create_unique_vertex(ev0)); egg_poly->add_vertex(cvpool->create_unique_vertex(ev1)); - } else if (child->is_of_type(CollisionTube::get_class_type())) { - CPT(CollisionTube) tube = DCAST(CollisionTube, child); - LPoint3 point_a = tube->get_point_a(); - LPoint3 point_b = tube->get_point_b(); + } else if (child->is_of_type(CollisionCapsule::get_class_type())) { + CPT(CollisionCapsule) capsule = DCAST(CollisionCapsule, child); + LPoint3 point_a = capsule->get_point_a(); + LPoint3 point_b = capsule->get_point_b(); LPoint3 centroid = (point_a + point_b) * 0.5f; - // Also get an arbitrary vector perpendicular to the tube. + // Also get an arbitrary vector perpendicular to the capsule. LVector3 axis = point_b - point_a; LVector3 sideways; if (std::fabs(axis[2]) > std::fabs(axis[1])) { @@ -635,18 +596,18 @@ convert_collision_node(CollisionNode *node, const WorkingNodePath &node_path, sideways = axis.cross(LVector3(0, 0, 1)); } sideways.normalize(); - sideways *= tube->get_radius(); - LVector3 extend = axis.normalized() * tube->get_radius(); + sideways *= capsule->get_radius(); + LVector3 extend = axis.normalized() * capsule->get_radius(); - EggGroup *egg_tube; + EggGroup *egg_capsule; if (num_solids == 1) { - egg_tube = egg_group; + egg_capsule = egg_group; } else { - egg_tube = new EggGroup; - egg_group->add_child(egg_tube); + egg_capsule = new EggGroup; + egg_group->add_child(egg_capsule); } - egg_tube->set_cs_type(EggGroup::CST_tube); - egg_tube->set_collide_flags(flags); + egg_capsule->set_cs_type(EggGroup::CST_tube); + egg_capsule->set_collide_flags(flags); // Add two points for the endcaps, and then two points around the // centroid to indicate the radius. @@ -657,7 +618,7 @@ convert_collision_node(CollisionNode *node, const WorkingNodePath &node_path, ev3.set_pos(LCAST(double, (centroid - sideways) * net_mat)); EggPolygon *egg_poly = new EggPolygon; - egg_tube->add_child(egg_poly); + egg_capsule->add_child(egg_poly); egg_poly->add_vertex(cvpool->create_unique_vertex(ev0)); egg_poly->add_vertex(cvpool->create_unique_vertex(ev1)); diff --git a/panda/src/egldisplay/eglGraphicsWindow.cxx b/panda/src/egldisplay/eglGraphicsWindow.cxx index efb550c728..89deb47deb 100644 --- a/panda/src/egldisplay/eglGraphicsWindow.cxx +++ b/panda/src/egldisplay/eglGraphicsWindow.cxx @@ -55,44 +55,6 @@ eglGraphicsWindow:: ~eglGraphicsWindow() { } -/** - * Forces the pointer to the indicated position within the window, if - * possible. - * - * Returns true if successful, false on failure. This may fail if the mouse - * is not currently within the window, or if the API doesn't support this - * operation. - */ -bool eglGraphicsWindow:: -move_pointer(int device, int x, int y) { - // Note: this is not thread-safe; it should be called only from App. - // Probably not an issue. - if (device == 0) { - // Move the system mouse pointer. - if (!_properties.get_foreground() || - !_input_devices[0].get_pointer().get_in_window()) { - // If the window doesn't have input focus, or the mouse isn't currently - // within the window, forget it. - return false; - } - - const MouseData &md = _input_devices[0].get_pointer(); - if (!md.get_in_window() || md.get_x() != x || md.get_y() != y) { - XWarpPointer(_display, None, _xwindow, 0, 0, 0, 0, x, y); - _input_devices[0].set_pointer_in_window(x, y); - } - return true; - } else { - // Move a raw mouse. - if (device < 1 || (size_t)device >= _input_devices.size()) { - return false; - } - _input_devices[device].set_pointer_in_window(x, y); - return true; - } -} - - /** * This function will be called within the draw thread before beginning * rendering for a given frame. It should do whatever setup is required, and diff --git a/panda/src/egldisplay/eglGraphicsWindow.h b/panda/src/egldisplay/eglGraphicsWindow.h index 7647605fd3..3d4ef62e44 100644 --- a/panda/src/egldisplay/eglGraphicsWindow.h +++ b/panda/src/egldisplay/eglGraphicsWindow.h @@ -33,7 +33,6 @@ public: GraphicsOutput *host); virtual ~eglGraphicsWindow(); - virtual bool move_pointer(int device, int x, int y); virtual bool begin_frame(FrameMode mode, Thread *current_thread); virtual void end_frame(FrameMode mode, Thread *current_thread); virtual void end_flip(); diff --git a/panda/src/event/buttonEventList.h b/panda/src/event/buttonEventList.h index c9d9479490..0a7822fc63 100644 --- a/panda/src/event/buttonEventList.h +++ b/panda/src/event/buttonEventList.h @@ -31,7 +31,7 @@ class DatagramIterator; * but it may be used anywhere a list of ButtonEvents is desired. */ class EXPCL_PANDA_EVENT ButtonEventList : public ParamValueBase { -public: +PUBLISHED: INLINE ButtonEventList(); INLINE ButtonEventList(const ButtonEventList ©); INLINE void operator = (const ButtonEventList ©); diff --git a/panda/src/event/pointerEvent.I b/panda/src/event/pointerEvent.I index 1321efe477..003cbf5df0 100644 --- a/panda/src/event/pointerEvent.I +++ b/panda/src/event/pointerEvent.I @@ -11,59 +11,6 @@ * @date 2007-09-20 */ -/** - * - */ -INLINE PointerEvent:: -PointerEvent() : - _in_window(false), - _xpos(0), - _ypos(0), - _dx(0), - _dy(0), - _length(0.0), - _direction(0.0), - _rotation(0.0), - _sequence(0), - _time(0.0) -{ -} - -/** - * - */ -INLINE PointerEvent:: -PointerEvent(const PointerEvent ©) : - _in_window(copy._in_window), - _xpos(copy._xpos), - _ypos(copy._ypos), - _dx(copy._dx), - _dy(copy._dy), - _length(copy._length), - _direction(copy._direction), - _rotation(copy._rotation), - _sequence(copy._sequence), - _time(copy._time) -{ -} - -/** - * - */ -INLINE void PointerEvent:: -operator = (const PointerEvent ©) { - _in_window = copy._in_window; - _xpos = copy._xpos; - _ypos = copy._ypos; - _dx = copy._dx; - _dy = copy._dy; - _sequence = copy._sequence; - _length = copy._length; - _direction = copy._direction; - _rotation = copy._rotation; - _time = copy._time; -} - /** * The equality operator does not consider time significant. */ diff --git a/panda/src/event/pointerEvent.h b/panda/src/event/pointerEvent.h index d98cc99989..c0c354efd6 100644 --- a/panda/src/event/pointerEvent.h +++ b/panda/src/event/pointerEvent.h @@ -15,7 +15,7 @@ #define POINTEREVENT_H #include "pandabase.h" -#include "mouseData.h" +#include "pointerData.h" class Datagram; class DatagramIterator; @@ -25,10 +25,7 @@ class DatagramIterator; */ class EXPCL_PANDA_EVENT PointerEvent { public: - - INLINE PointerEvent(); - INLINE PointerEvent(const PointerEvent ©); - INLINE void operator = (const PointerEvent ©); + PointerEvent() = default; INLINE bool operator == (const PointerEvent &other) const; INLINE bool operator != (const PointerEvent &other) const; @@ -40,16 +37,19 @@ public: void read_datagram(DatagramIterator &scan); public: - bool _in_window; - int _xpos; - int _ypos; - int _dx; - int _dy; - double _length; - double _direction; - double _rotation; - int _sequence; - double _time; + bool _in_window = false; + int _id = 0; + PointerType _type = PointerType::unknown; + double _xpos = 0.0; + double _ypos = 0.0; + double _dx = 0.0; + double _dy = 0.0; + double _length = 0.0; + double _direction = 0.0; + double _pressure = 0.0; + double _rotation = 0.0; + int _sequence = 0; + double _time = 0.0; }; INLINE std::ostream &operator << (std::ostream &out, const PointerEvent &pe) { diff --git a/panda/src/event/pointerEventList.I b/panda/src/event/pointerEventList.I index eda98b6473..8309609832 100644 --- a/panda/src/event/pointerEventList.I +++ b/panda/src/event/pointerEventList.I @@ -38,99 +38,115 @@ operator = (const PointerEventList ©) { /** * Returns the number of events in the list. */ -INLINE int PointerEventList:: +INLINE size_t PointerEventList:: get_num_events() const { return _events.size(); } +/** + * Returns true if the list is empty. + */ +INLINE bool PointerEventList:: +empty() const { + return _events.empty(); +} + +/** + * Returns the nth event in the list. + */ +INLINE const PointerEvent &PointerEventList:: +get_event(size_t n) const { + return _events[n]; +} + /** * Get the in-window flag of the nth event. */ INLINE bool PointerEventList:: -get_in_window(int evt) const { - nassertr((evt >= 0) && (evt < (int)_events.size()), 0); - return _events[evt]._in_window; +get_in_window(size_t n) const { + nassertr(n < _events.size(), 0); + return _events[n]._in_window; } /** * Get the x-coordinate of the nth event. */ INLINE int PointerEventList:: -get_xpos(int evt) const { - nassertr((evt >= 0) && (evt < (int)_events.size()), 0); - return _events[evt]._xpos; +get_xpos(size_t n) const { + nassertr(n < _events.size(), 0); + return _events[n]._xpos; } /** * Get the y-coordinate of the nth event. */ INLINE int PointerEventList:: -get_ypos(int evt) const { - nassertr((evt >= 0) && (evt < (int)_events.size()), 0); - return _events[evt]._ypos; +get_ypos(size_t n) const { + nassertr(n < _events.size(), 0); + return _events[n]._ypos; } /** - * Get the x-coordinate of the nth event. + * Get the x-delta of the nth event. */ -INLINE int PointerEventList:: -get_dx(int evt) const { - nassertr((evt >= 0) && (evt < (int)_events.size()), 0); - return _events[evt]._dx; +INLINE double PointerEventList:: +get_dx(size_t n) const { + nassertr(n < _events.size(), 0); + return _events[n]._dx; } /** - * Get the y-coordinate of the nth event. + * Get the y-delta of the nth event. */ -INLINE int PointerEventList:: -get_dy(int evt) const { - nassertr((evt >= 0) && (evt < (int)_events.size()), 0); - return _events[evt]._dy; +INLINE double PointerEventList:: +get_dy(size_t n) const { + nassertr(n < _events.size(), 0); + return _events[n]._dy; } /** * Get the length of the nth event. */ INLINE double PointerEventList:: -get_length(int evt) const { - nassertr((evt >= 0) && (evt < (int)_events.size()), 0); - return _events[evt]._length; +get_length(size_t n) const { + nassertr(n < _events.size(), 0); + return _events[n]._length; } /** * Get the direction of the nth event. */ INLINE double PointerEventList:: -get_direction(int evt) const { - nassertr((evt >= 0) && (evt < (int)_events.size()), 0); - return _events[evt]._direction; +get_direction(size_t n) const { + nassertr(n < _events.size(), 0); + return _events[n]._direction; } /** * Get the rotation of the nth event. */ INLINE double PointerEventList:: -get_rotation(int evt) const { - nassertr((evt >= 0) && (evt < (int)_events.size()), 0); - return _events[evt]._rotation; +get_rotation(size_t n) const { + nassertr(n < _events.size(), 0); + return _events[n]._rotation; } /** * Get the sequence number of the nth event. */ INLINE int PointerEventList:: -get_sequence(int evt) const { - nassertr((evt >= 0) && (evt < (int)_events.size()), 0); - return _events[evt]._sequence; +get_sequence(size_t n) const { + nassertr(n < _events.size(), 0); + return _events[n]._sequence; } /** * Get the timestamp of the nth event. */ INLINE double PointerEventList:: -get_time(int evt) const { - nassertr((evt >= 0) && (evt < (int)_events.size()), 0); - return _events[evt]._time; +get_time(size_t n) const { + nassertr(n < _events.size(), 0); + return _events[n]._time; } /** diff --git a/panda/src/event/pointerEventList.cxx b/panda/src/event/pointerEventList.cxx index 4c9d8472c2..ea160ea9b9 100644 --- a/panda/src/event/pointerEventList.cxx +++ b/panda/src/event/pointerEventList.cxx @@ -76,6 +76,42 @@ write(std::ostream &out, int indent_level) const { } } +/** + * Adds a new event from the given PointerData object. + */ +void PointerEventList:: +add_event(const PointerData &data, int seq, double time) { + PointerEvent pe; + pe._in_window = data._in_window; + pe._type = data._type; + pe._id = data._id; + pe._xpos = data._xpos; + pe._ypos = data._ypos; + pe._pressure = data._pressure; + pe._sequence = seq; + pe._time = time; + if (_events.size() > 0) { + pe._dx = data._xpos - _events.back()._xpos; + pe._dy = data._ypos - _events.back()._ypos; + double ddx = pe._dx; + double ddy = pe._dy; + pe._length = csqrt(ddx*ddx + ddy*ddy); + if (pe._length > 0.0) { + pe._direction = normalize_angle(rad_2_deg(catan2(-ddy,ddx))); + } else { + pe._direction = _events.back()._direction; + } + pe._rotation = delta_angle(_events.back()._direction, pe._direction); + } else { + pe._dx = 0; + pe._dy = 0; + pe._length = 0.0; + pe._direction = 0.0; + pe._rotation = 0.0; + } + _events.push_back(pe); +} + /** * Adds a new event to the end of the list. Automatically calculates the dx, * dy, length, direction, and rotation for all but the first event. @@ -110,6 +146,35 @@ add_event(bool in_win, int xpos, int ypos, int seq, double time) { _events.push_back(pe); } +/** + * Adds a new event to the end of the list based on the given mouse movement. + */ +void PointerEventList:: +add_event(bool in_win, int xpos, int ypos, double xdelta, double ydelta, int seq, double time) { + PointerEvent pe; + pe._in_window = in_win; + pe._xpos = xpos; + pe._ypos = ypos; + pe._dx = xdelta; + pe._dy = ydelta; + pe._sequence = seq; + pe._time = time; + pe._length = csqrt(xdelta*xdelta + ydelta*ydelta); + if (pe._length > 0.0) { + pe._direction = normalize_angle(rad_2_deg(catan2(-ydelta,xdelta))); + } else if (!_events.empty()) { + pe._direction = _events.back()._direction; + } else { + pe._direction = 0.0; + } + if (!_events.empty()) { + pe._rotation = delta_angle(_events.back()._direction, pe._direction); + } else { + pe._rotation = 0.0; + } + _events.push_back(pe); +} + /** * Returns true if the trail loops around the specified point. */ diff --git a/panda/src/event/pointerEventList.h b/panda/src/event/pointerEventList.h index b9daf9ca4d..e9ae1c9610 100644 --- a/panda/src/event/pointerEventList.h +++ b/panda/src/event/pointerEventList.h @@ -34,21 +34,24 @@ class EXPCL_PANDA_EVENT PointerEventList : public ParamValueBase { PUBLISHED: INLINE PointerEventList(); - INLINE int get_num_events() const; - INLINE bool get_in_window(int n) const; - INLINE int get_xpos(int n) const; - INLINE int get_ypos(int n) const; - INLINE int get_dx(int n) const; - INLINE int get_dy(int n) const; - INLINE int get_sequence(int n) const; - INLINE double get_length(int n) const; - INLINE double get_direction(int n) const; - INLINE double get_rotation(int n) const; - INLINE double get_time(int n) const; + INLINE size_t get_num_events() const; + INLINE bool get_in_window(size_t n) const; + INLINE int get_xpos(size_t n) const; + INLINE int get_ypos(size_t n) const; + INLINE double get_dx(size_t n) const; + INLINE double get_dy(size_t n) const; + INLINE int get_sequence(size_t n) const; + INLINE double get_length(size_t n) const; + INLINE double get_direction(size_t n) const; + INLINE double get_rotation(size_t n) const; + INLINE double get_time(size_t n) const; INLINE void clear(); INLINE void pop_front(); - void add_event(bool in_win, int xpos, int ypos, int seq, double time); + void add_event(const PointerData &data, int seq, double time); + void add_event(bool in_win, int xpos, int ypos, int seq, double time); + void add_event(bool in_win, int xpos, int ypos, double xdelta, double ydelta, + int seq, double time); bool encircles(int x, int y) const; double total_turns(double sec) const; @@ -58,6 +61,9 @@ public: INLINE PointerEventList(const PointerEventList ©); INLINE void operator = (const PointerEventList ©); + INLINE bool empty() const; + INLINE const PointerEvent &get_event(size_t n) const; + virtual void output(std::ostream &out) const; void write(std::ostream &out, int indent_level = 0) const; diff --git a/panda/src/express/config_express.N b/panda/src/express/config_express.N index e68b43fbfd..abc2d79e63 100644 --- a/panda/src/express/config_express.N +++ b/panda/src/express/config_express.N @@ -1,5 +1,3 @@ -forcetype DConfig - forcetype PTA_uchar forcetype CPTA_uchar forcetype PTA_float diff --git a/panda/src/ffmpeg/ffmpegVideoCursor.cxx b/panda/src/ffmpeg/ffmpegVideoCursor.cxx index ef23a9414e..fa5789b6c4 100644 --- a/panda/src/ffmpeg/ffmpegVideoCursor.cxx +++ b/panda/src/ffmpeg/ffmpegVideoCursor.cxx @@ -79,8 +79,6 @@ init_from(FfmpegVideo *source) { return; } - ReMutexHolder av_holder(_av_lock); - #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55, 45, 101) _frame = av_frame_alloc(); _frame_out = av_frame_alloc(); @@ -109,6 +107,8 @@ init_from(FfmpegVideo *source) { _eof_known = false; _eof_frame = 0; + ReMutexHolder av_holder(_av_lock); + // Check if we got an alpha format. Please note that some video codecs // (eg. libvpx) change the pix_fmt after decoding the first frame, which is // why we didn't do this earlier. diff --git a/panda/src/glstuff/glBufferContext_src.I b/panda/src/glstuff/glBufferContext_src.I index bec8283a18..56ecf2102a 100644 --- a/panda/src/glstuff/glBufferContext_src.I +++ b/panda/src/glstuff/glBufferContext_src.I @@ -16,8 +16,9 @@ */ INLINE CLP(BufferContext):: CLP(BufferContext)(CLP(GraphicsStateGuardian) *glgsg, - PreparedGraphicsObjects *pgo) : - BufferContext(&pgo->_sbuffer_residency), + PreparedGraphicsObjects *pgo, + TypedWritableReferenceCount *object) : + BufferContext(&pgo->_sbuffer_residency, object), AdaptiveLruPage(0), _glgsg(glgsg) { diff --git a/panda/src/glstuff/glBufferContext_src.h b/panda/src/glstuff/glBufferContext_src.h index b6c46aab76..b67a9d214e 100644 --- a/panda/src/glstuff/glBufferContext_src.h +++ b/panda/src/glstuff/glBufferContext_src.h @@ -21,7 +21,8 @@ class EXPCL_GL CLP(BufferContext) : public BufferContext, public AdaptiveLruPage { public: INLINE CLP(BufferContext)(CLP(GraphicsStateGuardian) *glgsg, - PreparedGraphicsObjects *pgo); + PreparedGraphicsObjects *pgo, + TypedWritableReferenceCount *object); ALLOC_DELETED_CHAIN(CLP(BufferContext)); virtual void evict_lru(); diff --git a/panda/src/glstuff/glCgShaderContext_src.cxx b/panda/src/glstuff/glCgShaderContext_src.cxx index 649208fb47..6e7fc565cc 100644 --- a/panda/src/glstuff/glCgShaderContext_src.cxx +++ b/panda/src/glstuff/glCgShaderContext_src.cxx @@ -157,6 +157,16 @@ CLP(CgShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderConte break; case 2: // gl_Normal loc = CA_normal; + if (cgGetParameterColumns(p) == 4) { + // Don't declare vtx_normal with 4 coordinates; it results in it + // reading the w coordinate from random memory. + GLCAT.error() + << "Cg varying " << cgGetParameterName(p); + if (cgGetParameterSemantic(p)) { + GLCAT.error(false) << " : " << cgGetParameterSemantic(p); + } + GLCAT.error(false) << " should be declared as float4, not float3!\n"; + } break; case 3: // gl_Color loc = CA_color; diff --git a/panda/src/glstuff/glGraphicsBuffer_src.cxx b/panda/src/glstuff/glGraphicsBuffer_src.cxx index 06c26a590d..ebb038de7a 100644 --- a/panda/src/glstuff/glGraphicsBuffer_src.cxx +++ b/panda/src/glstuff/glGraphicsBuffer_src.cxx @@ -921,20 +921,37 @@ bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot, if (_fb_properties.get_alpha_bits() == 0) { if (_fb_properties.get_srgb_color()) { gl_format = GL_SRGB8; + } else if (_fb_properties.get_color_bits() > 16 * 3 || + _fb_properties.get_red_bits() > 16 || + _fb_properties.get_green_bits() > 16 || + _fb_properties.get_blue_bits() > 16) { + // 32-bit, which is always floating-point. + if (_fb_properties.get_blue_bits() > 0 || + _fb_properties.get_color_bits() == 1 || + _fb_properties.get_color_bits() > 32 * 2) { + gl_format = GL_RGB32F; + } else if (_fb_properties.get_green_bits() > 0 || + _fb_properties.get_color_bits() > 32) { + gl_format = GL_RG32F; + } else { + gl_format = GL_R32F; + } } else if (_fb_properties.get_float_color()) { - if (_fb_properties.get_color_bits() > 16 * 3) { - gl_format = GL_RGB32F_ARB; + // 16-bit floating-point. + if (_fb_properties.get_blue_bits() > 0 || + _fb_properties.get_color_bits() == 1 || + _fb_properties.get_color_bits() > 16 * 2) { + gl_format = GL_RGB16F; + } else if (_fb_properties.get_green_bits() > 0 || + _fb_properties.get_color_bits() > 16) { + gl_format = GL_RG16F; } else { - gl_format = GL_RGB16F_ARB; + gl_format = GL_R16F; } + } else if (_fb_properties.get_color_bits() > 8 * 3) { + gl_format = GL_RGB16_EXT; } else { - if (_fb_properties.get_color_bits() > 16 * 3) { - gl_format = GL_RGB32F_ARB; - } else if (_fb_properties.get_color_bits() > 8 * 3) { - gl_format = GL_RGB16_EXT; - } else { - gl_format = GL_RGB; - } + gl_format = GL_RGB; } } else { if (_fb_properties.get_srgb_color()) { @@ -1396,7 +1413,7 @@ open_buffer() { } if (_rb_context == nullptr) { - _rb_context = new BufferContext(&(glgsg->_renderbuffer_residency)); + _rb_context = new BufferContext(&(glgsg->_renderbuffer_residency), nullptr); } /* diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index 14fec5d146..6151a2c00d 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -771,6 +771,28 @@ reset() { _supported_geom_rendering |= Geom::GR_point_sprite; } + // Determine whether we support wide lines (and how wide they can be). + { + GLfloat aliased_range[2] = {1.0f, 1.0f}; + glGetFloatv(GL_ALIASED_LINE_WIDTH_RANGE, aliased_range); + + _max_line_width = aliased_range[1]; + +#ifdef SUPPORT_FIXED_FUNCTION + if (has_fixed_function_pipeline()) { +#ifndef OPENGLES + GLfloat range[2] = {1.0f, 1.0f}; + glGetFloatv(GL_LINE_WIDTH_RANGE, range); + _max_line_width = std::max(_max_line_width, range[1]); +#endif + + GLfloat smooth_range[2] = {1.0f, 1.0f}; + glGetFloatv(GL_SMOOTH_LINE_WIDTH_RANGE, smooth_range); + _max_line_width = std::max(_max_line_width, smooth_range[1]); + } +#endif + } + #ifdef OPENGLES_1 // OpenGL ES 1.0 does not support primitive restart indices. @@ -1662,6 +1684,8 @@ reset() { } } + Shader::set_default_caps(_shader_caps); + } else if (_supports_glsl) { // No, but we do support GLSL... _shader_caps._active_vprofile = (int)CG_PROFILE_GLSLV; @@ -6497,7 +6521,7 @@ prepare_shader_buffer(ShaderBuffer *data) { if (_supports_shader_buffers) { PStatGPUTimer timer(this, _prepare_shader_buffer_pcollector); - CLP(BufferContext) *gbc = new CLP(BufferContext)(this, _prepared_objects); + CLP(BufferContext) *gbc = new CLP(BufferContext)(this, _prepared_objects, data); _glGenBuffers(1, &gbc->_index); if (GLCAT.is_debug() && gl_debug_buffers) { @@ -7027,6 +7051,16 @@ framebuffer_copy_to_ram(Texture *tex, int view, int z, } else { format = Texture::F_srgb; } + } else if (_current_properties->get_float_color()) { + if (_current_properties->get_alpha_bits()) { + format = Texture::F_rgba32; + } else if (_current_properties->get_blue_bits()) { + format = Texture::F_rgb32; + } else if (_current_properties->get_green_bits()) { + format = Texture::F_rg32; + } else { + format = Texture::F_r32; + } } else { if (_current_properties->get_alpha_bits()) { format = Texture::F_rgba; @@ -7036,7 +7070,11 @@ framebuffer_copy_to_ram(Texture *tex, int view, int z, } if (_current_properties->get_float_color()) { component_type = Texture::T_float; - } else if (_current_properties->get_color_bits() <= 24) { + } else if (_current_properties->get_color_bits() <= 24 + && _current_properties->get_red_bits() <= 8 + && _current_properties->get_green_bits() <= 8 + && _current_properties->get_blue_bits() <= 8 + && _current_properties->get_alpha_bits() <= 8) { component_type = Texture::T_unsigned_byte; } else { component_type = Texture::T_unsigned_short; @@ -7352,7 +7390,7 @@ do_issue_render_mode() { GLCAT.spam() << "setting thickness to " << thickness << "\n"; } - glLineWidth(thickness); + glLineWidth(std::min((GLfloat)thickness, _max_line_width)); #ifndef OPENGLES_2 glPointSize(thickness); #endif diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.h b/panda/src/glstuff/glGraphicsStateGuardian_src.h index 4329938d06..3fd89c8ad1 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.h +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.h @@ -685,6 +685,8 @@ protected: #endif #endif + GLfloat _max_line_width; + #ifdef HAVE_CG CGcontext _cg_context; #endif diff --git a/panda/src/glstuff/glShaderContext_src.cxx b/panda/src/glstuff/glShaderContext_src.cxx index bf6c6097fa..ae7a845efc 100644 --- a/panda/src/glstuff/glShaderContext_src.cxx +++ b/panda/src/glstuff/glShaderContext_src.cxx @@ -2797,14 +2797,24 @@ update_shader_texture_bindings(ShaderContext *prev) { } if (tex->get_texture_type() != spec._desired_type) { - if (id != nullptr) { + switch (spec._part) { + case Shader::STO_named_input: GLCAT.error() << "Sampler type of GLSL shader input '" << *id << "' does not " "match type of texture " << *tex << ".\n"; - } else { + break; + + case Shader::STO_stage_i: GLCAT.error() << "Sampler type of GLSL shader input p3d_Texture" << spec._stage << " does not match type of texture " << *tex << ".\n"; + break; + + case Shader::STO_light_i_shadow_map: + GLCAT.error() + << "Sampler type of GLSL shader input p3d_LightSource[" << spec._stage + << "].shadowMap does not match type of texture " << *tex << ".\n"; + break; } // TODO: also check whether shadow sampler textures have shadow filter // enabled. diff --git a/panda/src/gobj/bufferContext.I b/panda/src/gobj/bufferContext.I index 3c34d93a8a..6d564cef02 100644 --- a/panda/src/gobj/bufferContext.I +++ b/panda/src/gobj/bufferContext.I @@ -11,6 +11,14 @@ * @date 2006-03-16 */ +/** + * Returns the associated object. + */ +INLINE TypedWritableReferenceCount *BufferContext:: +get_object() const { + return _object; +} + /** * Returns the number of bytes previously reported for the data object. This * is used to track changes in the data object's allocated size; if it changes diff --git a/panda/src/gobj/bufferContext.cxx b/panda/src/gobj/bufferContext.cxx index 52cac06ce4..b268400d35 100644 --- a/panda/src/gobj/bufferContext.cxx +++ b/panda/src/gobj/bufferContext.cxx @@ -19,7 +19,8 @@ TypeHandle BufferContext::_type_handle; * */ BufferContext:: -BufferContext(BufferResidencyTracker *residency) : +BufferContext(BufferResidencyTracker *residency, TypedWritableReferenceCount *object) : + _object(object), _residency(residency), _residency_state(0), _data_size_bytes(0), diff --git a/panda/src/gobj/bufferContext.h b/panda/src/gobj/bufferContext.h index dedf5411e9..60c9ab3333 100644 --- a/panda/src/gobj/bufferContext.h +++ b/panda/src/gobj/bufferContext.h @@ -23,6 +23,7 @@ #include "bufferResidencyTracker.h" class PreparedGraphicsObjects; +class TypedWritableReferenceCount; /** * This is a base class for those kinds of SavedContexts that occupy an @@ -36,15 +37,19 @@ class PreparedGraphicsObjects; */ class EXPCL_PANDA_GOBJ BufferContext : public SavedContext, private LinkedListNode { public: - BufferContext(BufferResidencyTracker *residency); + BufferContext(BufferResidencyTracker *residency, TypedWritableReferenceCount *object); virtual ~BufferContext(); + INLINE TypedWritableReferenceCount *get_object() const; + PUBLISHED: INLINE size_t get_data_size_bytes() const; INLINE UpdateSeq get_modified() const; INLINE bool get_active() const; INLINE bool get_resident() const; + MAKE_PROPERTY(object, get_object); + MAKE_PROPERTY(data_size_bytes, get_data_size_bytes); MAKE_PROPERTY(modified, get_modified); MAKE_PROPERTY(active, get_active); @@ -62,6 +67,11 @@ public: private: void set_owning_chain(BufferContextChain *chain); +protected: + // This cannot be a PT(), because the object and the GSG both own their + // BufferContexts! That would create a circular reference count. + TypedWritableReferenceCount *_object; + private: BufferResidencyTracker *_residency; int _residency_state; diff --git a/panda/src/gobj/config_gobj.cxx b/panda/src/gobj/config_gobj.cxx index 12840f7d04..07075a94a8 100644 --- a/panda/src/gobj/config_gobj.cxx +++ b/panda/src/gobj/config_gobj.cxx @@ -511,6 +511,15 @@ ConfigVariableBool stereo_lens_old_convergence "old, incorrect behavior, this may be set to 'true' to switch " "back to the old calculation.")); +ConfigVariableBool basic_shaders_only +("basic-shaders-only", false, + PRC_DESC("Set this to true if you aren't interested in shader model three " + "and beyond. Setting this flag will cause panda to disable " + "bleeding-edge shader functionality which tends to be unreliable " + "or broken. At some point, when functionality that is currently " + "flaky becomes reliable, we may expand the definition of what " + "constitutes 'basic' shaders.")); + ConfigVariableString cg_glsl_version ("cg-glsl-version", "", PRC_DESC("If this is set, it forces the Cg compiler to generate GLSL " diff --git a/panda/src/gobj/config_gobj.h b/panda/src/gobj/config_gobj.h index 7e769c0f45..90a5673c12 100644 --- a/panda/src/gobj/config_gobj.h +++ b/panda/src/gobj/config_gobj.h @@ -87,6 +87,7 @@ extern EXPCL_PANDA_GOBJ ConfigVariableDouble async_load_delay; extern EXPCL_PANDA_GOBJ ConfigVariableInt lens_geom_segments; extern EXPCL_PANDA_GOBJ ConfigVariableBool stereo_lens_old_convergence; +extern EXPCL_PANDA_GOBJ ConfigVariableBool basic_shaders_only; extern EXPCL_PANDA_GOBJ ConfigVariableString cg_glsl_version; extern EXPCL_PANDA_GOBJ ConfigVariableBool glsl_preprocess; extern EXPCL_PANDA_GOBJ ConfigVariableInt glsl_include_recursion_limit; diff --git a/panda/src/gobj/geom.cxx b/panda/src/gobj/geom.cxx index 8bede7327b..3b6dee026a 100644 --- a/panda/src/gobj/geom.cxx +++ b/panda/src/gobj/geom.cxx @@ -1338,7 +1338,7 @@ compute_internal_bounds(Geom::CData *cdata, Thread *current_thread) const { // Now actually compute the bounding volume. We do this by using // calc_tight_bounds to determine our box first. LPoint3 pmin, pmax; - PN_stdfloat sq_center_dist; + PN_stdfloat sq_center_dist = 0.0f; bool found_any = false; do_calc_tight_bounds(pmin, pmax, sq_center_dist, found_any, vertex_data, false, LMatrix4::ident_mat(), @@ -1379,7 +1379,7 @@ compute_internal_bounds(Geom::CData *cdata, Thread *current_thread) const { LPoint3 aabb_center = (pmin + pmax) * 0.5f; PN_stdfloat best_sq_radius = (pmax - aabb_center).length_squared(); - if (btype != BoundingVolume::BT_fastest && + if (btype != BoundingVolume::BT_fastest && best_sq_radius > 0.0f && aabb_center.length_squared() / best_sq_radius >= (0.2f * 0.2f)) { // Hmm, this is an off-center model. Maybe we can do a better job // by calculating the bounding sphere from the AABB center. @@ -1389,7 +1389,8 @@ compute_internal_bounds(Geom::CData *cdata, Thread *current_thread) const { do_calc_sphere_radius(aabb_center, better_sq_radius, found_any, vertex_data, cdata, current_thread); - if (found_any && better_sq_radius <= best_sq_radius) { + if (found_any && better_sq_radius > 0.0f && + better_sq_radius <= best_sq_radius) { // Great. This is as good a sphere as we're going to get. if (btype == BoundingVolume::BT_best && avg_box_area < better_sq_radius * MathNumbers::pi) { @@ -1409,7 +1410,7 @@ compute_internal_bounds(Geom::CData *cdata, Thread *current_thread) const { cdata->_internal_bounds = new BoundingBox(pmin, pmax); break; - } else if (sq_center_dist <= best_sq_radius) { + } else if (sq_center_dist >= 0.0f && sq_center_dist <= best_sq_radius) { // No, but a sphere centered on the origin is apparently still // better than a sphere around the bounding box. cdata->_internal_bounds = @@ -1420,7 +1421,8 @@ compute_internal_bounds(Geom::CData *cdata, Thread *current_thread) const { // This is the worst sphere we can make, which is why we will only // do it when the user specifically requests a sphere. cdata->_internal_bounds = - new BoundingSphere(aabb_center, csqrt(best_sq_radius)); + new BoundingSphere(aabb_center, + (best_sq_radius > 0.0f) ? csqrt(best_sq_radius) : 0.0f); break; } } diff --git a/panda/src/gobj/geomPrimitive.cxx b/panda/src/gobj/geomPrimitive.cxx index 011d66b132..6fa66329ba 100644 --- a/panda/src/gobj/geomPrimitive.cxx +++ b/panda/src/gobj/geomPrimitive.cxx @@ -538,15 +538,26 @@ offset_vertices(int offset, int begin_row, int end_row) { void GeomPrimitive:: make_nonindexed(GeomVertexData *dest, const GeomVertexData *source) { Thread *current_thread = Thread::get_current_thread(); - int num_vertices = get_num_vertices(); - int dest_start = dest->get_num_rows(); - int strip_cut_index = get_strip_cut_index(); - dest->set_num_rows(dest_start + num_vertices); - for (int i = 0; i < num_vertices; ++i) { - int v = get_vertex(i); - nassertd(v != strip_cut_index) continue; - dest->copy_row_from(dest_start + i, source, v, current_thread); + int num_vertices, dest_start; + { + GeomPrimitivePipelineReader reader(this, current_thread); + num_vertices = reader.get_num_vertices(); + int strip_cut_index = reader.get_strip_cut_index(); + + GeomVertexDataPipelineWriter data_writer(dest, false, current_thread); + data_writer.check_array_writers(); + dest_start = data_writer.get_num_rows(); + data_writer.set_num_rows(dest_start + num_vertices); + + GeomVertexDataPipelineReader data_reader(source, current_thread); + data_reader.check_array_readers(); + + for (int i = 0; i < num_vertices; ++i) { + int v = reader.get_vertex(i); + nassertd(v != strip_cut_index) continue; + data_writer.copy_row_from(dest_start + i, data_reader, v); + } } set_nonindexed_vertices(dest_start, num_vertices); @@ -1596,13 +1607,16 @@ calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, } if (got_mat) { - if (!found_any) { - reader.set_row_unsafe(cdata->_first_vertex); + // Find the first non-NaN vertex. + while (!found_any && i < cdata->_num_vertices) { + reader.set_row(cdata->_first_vertex + i); LPoint3 first_vertex = mat.xform_point(reader.get_data3()); - min_point = first_vertex; - max_point = first_vertex; - sq_center_dist = first_vertex.length_squared(); - found_any = true; + if (!first_vertex.is_nan()) { + min_point = first_vertex; + max_point = first_vertex; + sq_center_dist = first_vertex.length_squared(); + found_any = true; + } ++i; } @@ -1619,13 +1633,16 @@ calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, sq_center_dist = max(sq_center_dist, vertex.length_squared()); } } else { - if (!found_any) { - reader.set_row_unsafe(cdata->_first_vertex); - const LVecBase3 &first_vertex = reader.get_data3(); - min_point = first_vertex; - max_point = first_vertex; - sq_center_dist = first_vertex.length_squared(); - found_any = true; + // Find the first non-NaN vertex. + while (!found_any && i < cdata->_num_vertices) { + reader.set_row(cdata->_first_vertex + i); + LPoint3 first_vertex = reader.get_data3(); + if (!first_vertex.is_nan()) { + min_point = first_vertex; + max_point = first_vertex; + sq_center_dist = first_vertex.length_squared(); + found_any = true; + } ++i; } @@ -1653,15 +1670,19 @@ calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, int strip_cut_index = get_strip_cut_index(cdata->_index_type); if (got_mat) { - if (!found_any) { - int first_index = index.get_data1i(); - nassertv(first_index != strip_cut_index); - reader.set_row_unsafe(first_index); - LPoint3 first_vertex = mat.xform_point(reader.get_data3()); - min_point = first_vertex; - max_point = first_vertex; - sq_center_dist = first_vertex.length_squared(); - found_any = true; + // Find the first non-NaN vertex. + while (!found_any && !index.is_at_end()) { + int ii = index.get_data1i(); + if (ii != strip_cut_index) { + reader.set_row(ii); + LPoint3 first_vertex = mat.xform_point(reader.get_data3()); + if (!first_vertex.is_nan()) { + min_point = first_vertex; + max_point = first_vertex; + sq_center_dist = first_vertex.length_squared(); + found_any = true; + } + } } while (!index.is_at_end()) { @@ -1681,15 +1702,19 @@ calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, sq_center_dist = max(sq_center_dist, vertex.length_squared()); } } else { - if (!found_any) { - int first_index = index.get_data1i(); - nassertv(first_index != strip_cut_index); - reader.set_row_unsafe(first_index); - const LVecBase3 &first_vertex = reader.get_data3(); - min_point = first_vertex; - max_point = first_vertex; - sq_center_dist = first_vertex.length_squared(); - found_any = true; + // Find the first non-NaN vertex. + while (!found_any && !index.is_at_end()) { + int ii = index.get_data1i(); + if (ii != strip_cut_index) { + reader.set_row(ii); + LVecBase3 first_vertex = reader.get_data3(); + if (!first_vertex.is_nan()) { + min_point = first_vertex; + max_point = first_vertex; + sq_center_dist = first_vertex.length_squared(); + found_any = true; + } + } } while (!index.is_at_end()) { diff --git a/panda/src/gobj/geomVertexData.cxx b/panda/src/gobj/geomVertexData.cxx index 45522427c2..d7bf35ba9d 100644 --- a/panda/src/gobj/geomVertexData.cxx +++ b/panda/src/gobj/geomVertexData.cxx @@ -686,32 +686,13 @@ copy_from(const GeomVertexData *source, bool keep_data_objects, void GeomVertexData:: copy_row_from(int dest_row, const GeomVertexData *source, int source_row, Thread *current_thread) { - const GeomVertexFormat *source_format = source->get_format(); - const GeomVertexFormat *dest_format = get_format(); - nassertv(source_format == dest_format); - nassertv(source_row >= 0 && source_row < source->get_num_rows()); - if (dest_row >= get_num_rows()) { - // Implicitly add enough rows to get to the indicated row. - set_num_rows(dest_row + 1); - } + GeomVertexDataPipelineReader reader(source, current_thread); + reader.check_array_readers(); - int num_arrays = source_format->get_num_arrays(); - - for (int i = 0; i < num_arrays; ++i) { - PT(GeomVertexArrayDataHandle) dest_handle = modify_array_handle(i); - unsigned char *dest_array_data = dest_handle->get_write_pointer(); - - CPT(GeomVertexArrayDataHandle) source_array_handle = source->get_array_handle(i); - const unsigned char *source_array_data = source_array_handle->get_read_pointer(true); - - const GeomVertexArrayFormat *array_format = source_format->get_array(i); - int stride = array_format->get_stride(); - - memcpy(dest_array_data + stride * dest_row, - source_array_data + stride * source_row, - stride); - } + GeomVertexDataPipelineWriter writer(this, true, current_thread); + writer.check_array_writers(); + writer.copy_row_from(dest_row, reader, source_row); } /** @@ -2601,6 +2582,45 @@ set_array(size_t i, const GeomVertexArrayData *array) { } } +/** + * Copies a single row of the data from the other array into the indicated row + * of this array. In this case, the source format must exactly match the + * destination format. + * + * Don't call this in a downstream thread unless you don't mind it blowing + * away other changes you might have recently made in an upstream thread. + */ +void GeomVertexDataPipelineWriter:: +copy_row_from(int dest_row, const GeomVertexDataPipelineReader &source, + int source_row) { + const GeomVertexFormat *source_format = source.get_format(); + const GeomVertexFormat *dest_format = get_format(); + nassertv(source_format == dest_format); + nassertv(source_row >= 0 && source_row < source.get_num_rows()); + nassertv(_got_array_writers); + + if (dest_row >= get_num_rows()) { + // Implicitly add enough rows to get to the indicated row. + set_num_rows(dest_row + 1); + } + + size_t num_arrays = source_format->get_num_arrays(); + for (size_t i = 0; i < num_arrays; ++i) { + GeomVertexArrayDataHandle *dest_handle = get_array_writer(i); + unsigned char *dest_array_data = dest_handle->get_write_pointer(); + + const GeomVertexArrayDataHandle *source_array_handle = source.get_array_reader(i); + const unsigned char *source_array_data = source_array_handle->get_read_pointer(true); + + const GeomVertexArrayFormat *array_format = source_format->get_array(i); + int stride = array_format->get_stride(); + + memcpy(dest_array_data + stride * dest_row, + source_array_data + stride * source_row, + stride); + } +} + /** * */ diff --git a/panda/src/gobj/geomVertexData.h b/panda/src/gobj/geomVertexData.h index 9b861c1cbc..2b267045c5 100644 --- a/panda/src/gobj/geomVertexData.h +++ b/panda/src/gobj/geomVertexData.h @@ -526,6 +526,9 @@ public: bool unclean_set_num_rows(int n); bool reserve_num_rows(int n); + void copy_row_from(int dest_row, const GeomVertexDataPipelineReader &source, + int source_row); + private: void make_array_writers(); void delete_array_writers(); diff --git a/panda/src/gobj/indexBufferContext.I b/panda/src/gobj/indexBufferContext.I index caf5e7f34a..e55304bfcb 100644 --- a/panda/src/gobj/indexBufferContext.I +++ b/panda/src/gobj/indexBufferContext.I @@ -16,9 +16,8 @@ */ INLINE IndexBufferContext:: IndexBufferContext(PreparedGraphicsObjects *pgo, GeomPrimitive *data) : - BufferContext(&pgo->_ibuffer_residency), - AdaptiveLruPage(0), - _data(data) + BufferContext(&pgo->_ibuffer_residency, data), + AdaptiveLruPage(0) { } @@ -27,7 +26,7 @@ IndexBufferContext(PreparedGraphicsObjects *pgo, GeomPrimitive *data) : */ INLINE GeomPrimitive *IndexBufferContext:: get_data() const { - return _data; + return (GeomPrimitive *)_object; } /** @@ -36,7 +35,7 @@ get_data() const { */ INLINE bool IndexBufferContext:: changed_size(const GeomPrimitivePipelineReader *reader) const { - nassertr(reader->get_object() == _data, false); + nassertr(reader->get_object() == get_data(), false); return get_data_size_bytes() != (size_t)reader->get_data_size_bytes(); } @@ -46,7 +45,7 @@ changed_size(const GeomPrimitivePipelineReader *reader) const { */ INLINE bool IndexBufferContext:: changed_usage_hint(const GeomPrimitivePipelineReader *reader) const { - nassertr(reader->get_object() == _data, false); + nassertr(reader->get_object() == get_data(), false); return _usage_hint != reader->get_usage_hint(); } @@ -56,7 +55,7 @@ changed_usage_hint(const GeomPrimitivePipelineReader *reader) const { */ INLINE bool IndexBufferContext:: was_modified(const GeomPrimitivePipelineReader *reader) const { - nassertr(reader->get_object() == _data, false); + nassertr(reader->get_object() == get_data(), false); return get_modified() != reader->get_modified(); } @@ -76,7 +75,7 @@ update_data_size_bytes(size_t new_data_size_bytes) { */ INLINE void IndexBufferContext:: mark_loaded(const GeomPrimitivePipelineReader *reader) { - nassertv(reader->get_object() == _data); + nassertv(reader->get_object() == get_data()); update_data_size_bytes(reader->get_data_size_bytes()); update_modified(reader->get_modified()); _usage_hint = reader->get_usage_hint(); diff --git a/panda/src/gobj/indexBufferContext.h b/panda/src/gobj/indexBufferContext.h index 6b2004fac2..139ab69362 100644 --- a/panda/src/gobj/indexBufferContext.h +++ b/panda/src/gobj/indexBufferContext.h @@ -50,9 +50,6 @@ public: virtual void write(std::ostream &out, int indent_level) const; private: - // This cannot be a PT(GeomPrimitive), because the data and the GSG both own - // their IndexBufferContexts! That would create a circular reference count. - GeomPrimitive *_data; GeomEnums::UsageHint _usage_hint; public: diff --git a/panda/src/gobj/preparedGraphicsObjects.cxx b/panda/src/gobj/preparedGraphicsObjects.cxx index 96fcd80269..e3ab0e8d83 100644 --- a/panda/src/gobj/preparedGraphicsObjects.cxx +++ b/panda/src/gobj/preparedGraphicsObjects.cxx @@ -270,11 +270,11 @@ void PreparedGraphicsObjects:: release_texture(TextureContext *tc) { ReMutexHolder holder(_lock); - tc->_texture->clear_prepared(tc->get_view(), this); + tc->get_texture()->clear_prepared(tc->get_view(), this); // We have to set the Texture pointer to NULL at this point, since the // Texture itself might destruct at any time after it has been released. - tc->_texture = nullptr; + tc->_object = nullptr; bool removed = (_prepared_textures.erase(tc) != 0); nassertv(removed); @@ -307,8 +307,8 @@ release_all_textures() { tci != _prepared_textures.end(); ++tci) { TextureContext *tc = (*tci); - tc->_texture->clear_prepared(tc->get_view(), this); - tc->_texture = nullptr; + tc->get_texture()->clear_prepared(tc->get_view(), this); + tc->_object = nullptr; _released_textures.insert(tc); } @@ -946,14 +946,14 @@ void PreparedGraphicsObjects:: release_vertex_buffer(VertexBufferContext *vbc) { ReMutexHolder holder(_lock); - vbc->_data->clear_prepared(this); + vbc->get_data()->clear_prepared(this); - size_t data_size_bytes = vbc->_data->get_data_size_bytes(); - GeomEnums::UsageHint usage_hint = vbc->_data->get_usage_hint(); + size_t data_size_bytes = vbc->get_data()->get_data_size_bytes(); + GeomEnums::UsageHint usage_hint = vbc->get_data()->get_usage_hint(); // We have to set the Data pointer to NULL at this point, since the Data // itself might destruct at any time after it has been released. - vbc->_data = nullptr; + vbc->_object = nullptr; bool removed = (_prepared_vertex_buffers.erase(vbc) != 0); nassertv(removed); @@ -985,8 +985,8 @@ release_all_vertex_buffers() { vbci != _prepared_vertex_buffers.end(); ++vbci) { VertexBufferContext *vbc = (VertexBufferContext *)(*vbci); - vbc->_data->clear_prepared(this); - vbc->_data = nullptr; + vbc->get_data()->clear_prepared(this); + vbc->_object = nullptr; _released_vertex_buffers.insert(vbc); } @@ -1061,7 +1061,7 @@ prepare_vertex_buffer_now(GeomVertexArrayData *data, GraphicsStateGuardianBase * _vertex_buffer_cache, _vertex_buffer_cache_lru, _vertex_buffer_cache_size); if (vbc != nullptr) { - vbc->_data = data; + vbc->_object = data; } else { // Ask the GSG to create a brand new VertexBufferContext. There might be @@ -1144,14 +1144,14 @@ void PreparedGraphicsObjects:: release_index_buffer(IndexBufferContext *ibc) { ReMutexHolder holder(_lock); - ibc->_data->clear_prepared(this); + ibc->get_data()->clear_prepared(this); - size_t data_size_bytes = ibc->_data->get_data_size_bytes(); - GeomEnums::UsageHint usage_hint = ibc->_data->get_usage_hint(); + size_t data_size_bytes = ibc->get_data()->get_data_size_bytes(); + GeomEnums::UsageHint usage_hint = ibc->get_data()->get_usage_hint(); // We have to set the Data pointer to NULL at this point, since the Data // itself might destruct at any time after it has been released. - ibc->_data = nullptr; + ibc->_object = nullptr; bool removed = (_prepared_index_buffers.erase(ibc) != 0); nassertv(removed); @@ -1183,8 +1183,8 @@ release_all_index_buffers() { ibci != _prepared_index_buffers.end(); ++ibci) { IndexBufferContext *ibc = (IndexBufferContext *)(*ibci); - ibc->_data->clear_prepared(this); - ibc->_data = nullptr; + ibc->get_data()->clear_prepared(this); + ibc->_object = nullptr; _released_index_buffers.insert(ibc); } @@ -1258,7 +1258,7 @@ prepare_index_buffer_now(GeomPrimitive *data, GraphicsStateGuardianBase *gsg) { _index_buffer_cache, _index_buffer_cache_lru, _index_buffer_cache_size); if (ibc != nullptr) { - ibc->_data = data; + ibc->_object = data; } else { // Ask the GSG to create a brand new IndexBufferContext. There might be @@ -1341,6 +1341,13 @@ void PreparedGraphicsObjects:: release_shader_buffer(BufferContext *bc) { ReMutexHolder holder(_lock); + ShaderBuffer *buffer = (ShaderBuffer *)bc->_object; + buffer->clear_prepared(this); + + // We have to set the ShaderBuffer pointer to NULL at this point, since the + // buffer itself might destruct at any time after it has been released. + bc->_object = nullptr; + bool removed = (_prepared_shader_buffers.erase(bc) != 0); nassertv(removed); diff --git a/panda/src/gobj/shader.cxx b/panda/src/gobj/shader.cxx index 4a1092eb9c..d42e6a4734 100644 --- a/panda/src/gobj/shader.cxx +++ b/panda/src/gobj/shader.cxx @@ -1589,6 +1589,15 @@ get_compiled(unsigned int &format, string &binary) const { return !binary.empty(); } +/** + * Called by the graphics back-end to specify the caps with which we will + * likely want to be compiling our shaders. + */ +void Shader:: +set_default_caps(const ShaderCaps &caps) { + _default_caps = caps; +} + #ifdef HAVE_CG /** * @@ -2364,9 +2373,15 @@ Shader(ShaderLanguage lang) : _cg_fprofile = CG_PROFILE_UNKNOWN; _cg_gprofile = CG_PROFILE_UNKNOWN; if (_default_caps._ultimate_vprofile == 0 || _default_caps._ultimate_vprofile == CG_PROFILE_UNKNOWN) { - _default_caps._active_vprofile = CG_PROFILE_GENERIC; - _default_caps._active_fprofile = CG_PROFILE_GENERIC; - _default_caps._active_gprofile = CG_PROFILE_GENERIC; + if (basic_shaders_only) { + _default_caps._active_vprofile = CG_PROFILE_ARBVP1; + _default_caps._active_fprofile = CG_PROFILE_ARBFP1; + _default_caps._active_gprofile = CG_PROFILE_UNKNOWN; + } else { + _default_caps._active_vprofile = CG_PROFILE_UNKNOWN; + _default_caps._active_fprofile = CG_PROFILE_UNKNOWN; + _default_caps._active_gprofile = CG_PROFILE_UNKNOWN; + } _default_caps._ultimate_vprofile = cgGetProfile("glslv"); _default_caps._ultimate_fprofile = cgGetProfile("glslf"); _default_caps._ultimate_gprofile = cgGetProfile("glslg"); @@ -2750,6 +2765,7 @@ r_preprocess_source(ostream &out, istream &in, const Filename &fn, int ext_google_include = 0; // 1 = warn, 2 = enable int ext_google_line = 0; bool had_include = false; + bool had_version = false; int lineno = 0; bool write_line_directive = (fileno != 0); @@ -2920,6 +2936,9 @@ r_preprocess_source(ostream &out, istream &in, const Filename &fn, write_line_directive = true; } + } else if (strcmp(directive, "version") == 0) { + had_version = true; + } else if (strcmp(directive, "extension") == 0) { // Check for special preprocessing extensions. char extension[256]; @@ -3047,6 +3066,11 @@ r_preprocess_source(ostream &out, istream &in, const Filename &fn, out << line << "\n"; } + if (fileno == 0 && !had_version) { + shader_cat.warning() + << "GLSL shader " << fn << " does not contain a #version line!\n"; + } + return true; } diff --git a/panda/src/gobj/shader.h b/panda/src/gobj/shader.h index 35971b243b..940e2d0ccf 100644 --- a/panda/src/gobj/shader.h +++ b/panda/src/gobj/shader.h @@ -527,6 +527,8 @@ public: void set_compiled(unsigned int format, const char *data, size_t length); bool get_compiled(unsigned int &format, std::string &binary) const; + static void set_default_caps(const ShaderCaps &caps); + private: #ifdef HAVE_CG ShaderArgClass cg_parameter_class(CGparameter p); diff --git a/panda/src/gobj/shaderBuffer.cxx b/panda/src/gobj/shaderBuffer.cxx index 4e21319b49..fa16293d4d 100644 --- a/panda/src/gobj/shaderBuffer.cxx +++ b/panda/src/gobj/shaderBuffer.cxx @@ -143,6 +143,31 @@ release_all() { return num_freed; } +/** + * Removes the indicated PreparedGraphicsObjects table from the buffer's + * table, without actually releasing the texture. This is intended to be + * called only from PreparedGraphicsObjects::release_shader_buffer(); it + * should never be called by user code. + */ +void ShaderBuffer:: +clear_prepared(PreparedGraphicsObjects *prepared_objects) { + nassertv(_contexts != nullptr); + + Contexts::iterator ci; + ci = _contexts->find(prepared_objects); + if (ci != _contexts->end()) { + _contexts->erase(ci); + if (_contexts->empty()) { + delete _contexts; + _contexts = nullptr; + } + } else { + // If this assertion fails, clear_prepared() was given a prepared_objects + // which the data array didn't know about. + nassert_raise("unknown PreparedGraphicsObjects"); + } +} + /** * Tells the BamReader how to create objects of type ParamValue. */ diff --git a/panda/src/gobj/shaderBuffer.h b/panda/src/gobj/shaderBuffer.h index bdf0538857..0cc0660433 100644 --- a/panda/src/gobj/shaderBuffer.h +++ b/panda/src/gobj/shaderBuffer.h @@ -57,6 +57,9 @@ PUBLISHED: bool release(PreparedGraphicsObjects *prepared_objects); int release_all(); +private: + void clear_prepared(PreparedGraphicsObjects *prepared_objects); + private: uint64_t _data_size_bytes; UsageHint _usage_hint; @@ -91,6 +94,8 @@ public: private: static TypeHandle _type_handle; + + friend class PreparedGraphicsObjects; }; INLINE std::ostream &operator << (std::ostream &out, const ShaderBuffer &m) { diff --git a/panda/src/gobj/textureContext.I b/panda/src/gobj/textureContext.I index fa947755d0..2c58bc7b07 100644 --- a/panda/src/gobj/textureContext.I +++ b/panda/src/gobj/textureContext.I @@ -16,9 +16,8 @@ */ INLINE TextureContext:: TextureContext(PreparedGraphicsObjects *pgo, Texture *tex, int view) : - BufferContext(&pgo->_texture_residency), + BufferContext(&pgo->_texture_residency, tex), AdaptiveLruPage(0), - _texture(tex), _view(view) { } @@ -28,7 +27,7 @@ TextureContext(PreparedGraphicsObjects *pgo, Texture *tex, int view) : */ INLINE Texture *TextureContext:: get_texture() const { - return _texture; + return (Texture *)_object; } /** @@ -56,7 +55,7 @@ was_modified() const { */ INLINE bool TextureContext:: was_properties_modified() const { - return _properties_modified != _texture->get_properties_modified(); + return _properties_modified != get_texture()->get_properties_modified(); } /** @@ -65,7 +64,7 @@ was_properties_modified() const { */ INLINE bool TextureContext:: was_image_modified() const { - return _image_modified != _texture->get_image_modified(); + return _image_modified != get_texture()->get_image_modified(); } /** @@ -74,7 +73,7 @@ was_image_modified() const { */ INLINE bool TextureContext:: was_simple_image_modified() const { - return _simple_image_modified != _texture->get_simple_image_modified(); + return _simple_image_modified != get_texture()->get_simple_image_modified(); } /** @@ -121,8 +120,8 @@ update_data_size_bytes(size_t new_data_size_bytes) { INLINE void TextureContext:: mark_loaded() { // _data_size_bytes = _data->get_texture_size_bytes(); - _properties_modified = _texture->get_properties_modified(); - _image_modified = _texture->get_image_modified(); + _properties_modified = get_texture()->get_properties_modified(); + _image_modified = get_texture()->get_image_modified(); update_modified(std::max(_properties_modified, _image_modified)); // Assume the texture is now resident. @@ -135,8 +134,8 @@ mark_loaded() { */ INLINE void TextureContext:: mark_simple_loaded() { - _properties_modified = _texture->get_properties_modified(); - _simple_image_modified = _texture->get_simple_image_modified(); + _properties_modified = get_texture()->get_properties_modified(); + _simple_image_modified = get_texture()->get_simple_image_modified(); update_modified(std::max(_properties_modified, _simple_image_modified)); // The texture's not exactly resident now, but some part of it is. diff --git a/panda/src/gobj/textureContext.h b/panda/src/gobj/textureContext.h index c7ae367ccc..2962fd38c0 100644 --- a/panda/src/gobj/textureContext.h +++ b/panda/src/gobj/textureContext.h @@ -60,9 +60,6 @@ public: virtual void write(std::ostream &out, int indent_level) const; private: - // This cannot be a PT(Texture), because the texture and the GSG both own - // their TextureContexts! That would create a circular reference count. - Texture *_texture; int _view; UpdateSeq _properties_modified; UpdateSeq _image_modified; diff --git a/panda/src/gobj/texturePeeker.cxx b/panda/src/gobj/texturePeeker.cxx index 7bb17ab87a..ece87f36a5 100644 --- a/panda/src/gobj/texturePeeker.cxx +++ b/panda/src/gobj/texturePeeker.cxx @@ -140,6 +140,12 @@ TexturePeeker(Texture *tex, Texture::CData *cdata) { _get_texel = get_texel_la; break; + case Texture::F_rg16: + case Texture::F_rg32: + case Texture::F_rg: + _get_texel = get_texel_rg; + break; + case Texture::F_rgb: case Texture::F_rgb5: case Texture::F_rgb8: @@ -148,6 +154,7 @@ TexturePeeker(Texture *tex, Texture::CData *cdata) { case Texture::F_rgb332: case Texture::F_r11_g11_b10: case Texture::F_rgb9_e5: + case Texture::F_rgb32: _get_texel = get_texel_rgb; break; @@ -582,6 +589,18 @@ get_texel_la(LColor &color, const unsigned char *&p, GetComponentFunc *get_compo color[3] = (*get_component)(p); } +/** + * Gets the color of the texel at byte p, given that the texture is in format + * F_rg or similar. + */ +void TexturePeeker:: +get_texel_rg(LColor &color, const unsigned char *&p, GetComponentFunc *get_component) { + color[0] = (*get_component)(p); + color[1] = (*get_component)(p); + color[2] = 0.0f; + color[3] = 1.0f; +} + /** * Gets the color of the texel at byte p, given that the texture is in format * F_rgb or similar. diff --git a/panda/src/gobj/texturePeeker.h b/panda/src/gobj/texturePeeker.h index ffe3aba2b1..e88603bcea 100644 --- a/panda/src/gobj/texturePeeker.h +++ b/panda/src/gobj/texturePeeker.h @@ -77,6 +77,7 @@ private: static void get_texel_a(LColor &color, const unsigned char *&p, GetComponentFunc *get_component); static void get_texel_l(LColor &color, const unsigned char *&p, GetComponentFunc *get_component); static void get_texel_la(LColor &color, const unsigned char *&p, GetComponentFunc *get_component); + static void get_texel_rg(LColor &color, const unsigned char *&p, GetComponentFunc *get_component); static void get_texel_rgb(LColor &color, const unsigned char *&p, GetComponentFunc *get_component); static void get_texel_rgba(LColor &color, const unsigned char *&p, GetComponentFunc *get_component); static void get_texel_srgb(LColor &color, const unsigned char *&p, GetComponentFunc *get_component); diff --git a/panda/src/gobj/vertexBufferContext.I b/panda/src/gobj/vertexBufferContext.I index b57739cf09..bbfff0bb27 100644 --- a/panda/src/gobj/vertexBufferContext.I +++ b/panda/src/gobj/vertexBufferContext.I @@ -16,9 +16,8 @@ */ INLINE VertexBufferContext:: VertexBufferContext(PreparedGraphicsObjects *pgo, GeomVertexArrayData *data) : - BufferContext(&pgo->_vbuffer_residency), - AdaptiveLruPage(0), - _data(data) + BufferContext(&pgo->_vbuffer_residency, data), + AdaptiveLruPage(0) { } @@ -27,7 +26,7 @@ VertexBufferContext(PreparedGraphicsObjects *pgo, GeomVertexArrayData *data) : */ INLINE GeomVertexArrayData *VertexBufferContext:: get_data() const { - return _data; + return (GeomVertexArrayData *)_object; } /** @@ -36,7 +35,7 @@ get_data() const { */ INLINE bool VertexBufferContext:: changed_size(const GeomVertexArrayDataHandle *reader) const { - nassertr(reader->get_object() == _data, false); + nassertr(reader->get_object() == get_data(), false); return get_data_size_bytes() != (size_t)reader->get_data_size_bytes(); } @@ -46,7 +45,7 @@ changed_size(const GeomVertexArrayDataHandle *reader) const { */ INLINE bool VertexBufferContext:: changed_usage_hint(const GeomVertexArrayDataHandle *reader) const { - nassertr(reader->get_object() == _data, false); + nassertr(reader->get_object() == get_data(), false); return _usage_hint != reader->get_usage_hint(); } @@ -56,7 +55,7 @@ changed_usage_hint(const GeomVertexArrayDataHandle *reader) const { */ INLINE bool VertexBufferContext:: was_modified(const GeomVertexArrayDataHandle *reader) const { - nassertr(reader->get_object() == _data, false); + nassertr(reader->get_object() == get_data(), false); return get_modified() != reader->get_modified(); } @@ -77,7 +76,7 @@ update_data_size_bytes(size_t new_data_size_bytes) { */ INLINE void VertexBufferContext:: mark_loaded(const GeomVertexArrayDataHandle *reader) { - nassertv(reader->get_object() == _data); + nassertv(reader->get_object() == get_data()); update_data_size_bytes(reader->get_data_size_bytes()); update_modified(reader->get_modified()); _usage_hint = reader->get_usage_hint(); diff --git a/panda/src/gobj/vertexBufferContext.h b/panda/src/gobj/vertexBufferContext.h index 69f02e0ed7..c1696ba6cb 100644 --- a/panda/src/gobj/vertexBufferContext.h +++ b/panda/src/gobj/vertexBufferContext.h @@ -51,10 +51,6 @@ public: virtual void write(std::ostream &out, int indent_level) const; private: - // This cannot be a PT(GeomVertexArrayData), because the data and the GSG - // both own their VertexBufferContexts! That would create a circular - // reference count. - GeomVertexArrayData *_data; GeomEnums::UsageHint _usage_hint; public: diff --git a/panda/src/grutil/shaderTerrainMesh.cxx b/panda/src/grutil/shaderTerrainMesh.cxx index a0f3c34690..e8078f7b68 100644 --- a/panda/src/grutil/shaderTerrainMesh.cxx +++ b/panda/src/grutil/shaderTerrainMesh.cxx @@ -156,7 +156,9 @@ bool ShaderTerrainMesh::generate() { * the chunks, and the PNMImage is destroyed afterwards. */ void ShaderTerrainMesh::do_extract_heightfield() { - nassertv(_heightfield_tex->has_ram_image()); // Heightfield not in RAM, extract ram image first + if (!_heightfield_tex->has_ram_image()) { + _heightfield_tex->reload(); + } _heightfield_tex->store(_heightfield); diff --git a/panda/src/iphone/ipfreeze.py b/panda/src/iphone/ipfreeze.py index 5a7462eb9e..d7fe343a15 100755 --- a/panda/src/iphone/ipfreeze.py +++ b/panda/src/iphone/ipfreeze.py @@ -14,7 +14,7 @@ Usage: import getopt import sys import os -from direct.showutil import FreezeTool +from direct.dist import FreezeTool def usage(code, msg = ''): print >> sys.stderr, __doc__ diff --git a/panda/src/mathutil/boundingSphere.h b/panda/src/mathutil/boundingSphere.h index 8c105daa01..2e63fb3396 100644 --- a/panda/src/mathutil/boundingSphere.h +++ b/panda/src/mathutil/boundingSphere.h @@ -47,6 +47,8 @@ PUBLISHED: INLINE_MATHUTIL void set_center(const LPoint3 ¢er); INLINE_MATHUTIL void set_radius(PN_stdfloat radius); + MAKE_PROPERTY(center, get_center, set_center); + MAKE_PROPERTY(radius, get_radius, set_radius); public: virtual const BoundingSphere *as_bounding_sphere() const; diff --git a/panda/src/mathutil/finiteBoundingVolume.h b/panda/src/mathutil/finiteBoundingVolume.h index e344151685..e527faa88b 100644 --- a/panda/src/mathutil/finiteBoundingVolume.h +++ b/panda/src/mathutil/finiteBoundingVolume.h @@ -30,6 +30,10 @@ PUBLISHED: virtual LPoint3 get_max() const=0; virtual PN_stdfloat get_volume() const; + MAKE_PROPERTY(min, get_min); + MAKE_PROPERTY(max, get_max); + MAKE_PROPERTY(volume, get_volume); + public: virtual const FiniteBoundingVolume *as_finite_bounding_volume() const; diff --git a/panda/src/pandabase/pandasymbols.h b/panda/src/pandabase/pandasymbols.h index ef83b4314e..3f529dab27 100644 --- a/panda/src/pandabase/pandasymbols.h +++ b/panda/src/pandabase/pandasymbols.h @@ -476,14 +476,6 @@ #define EXPTP_PANDA_WGLDISPLAY IMPORT_TEMPL #endif -#ifdef BUILDING_PANDAAWESOMIUM - #define EXPCL_PANDAAWESOMIUM EXPORT_CLASS - #define EXPTP_PANDAAWESOMIUM EXPORT_TEMPL -#else - #define EXPCL_PANDAAWESOMIUM IMPORT_CLASS - #define EXPTP_PANDAAWESOMIUM IMPORT_TEMPL -#endif - #ifdef BUILDING_PANDAGL #define EXPCL_PANDAGL EXPORT_CLASS #define EXPTP_PANDAGL EXPORT_TEMPL diff --git a/panda/src/pgraph/billboardEffect.I b/panda/src/pgraph/billboardEffect.I index d94d8135f7..b6ee6f31b0 100644 --- a/panda/src/pgraph/billboardEffect.I +++ b/panda/src/pgraph/billboardEffect.I @@ -86,6 +86,15 @@ get_axial_rotate() const { return _axial_rotate; } +/** + * Returns true if this billboard always appears at a fixed distance from the + * camera. + */ +INLINE bool BillboardEffect:: +get_fixed_depth() const { + return _fixed_depth; +} + /** * Returns the distance toward the camera (or the look_at_point) the billboard * is moved towards, after rotating. This can be used to ensure the billboard diff --git a/panda/src/pgraph/billboardEffect.cxx b/panda/src/pgraph/billboardEffect.cxx index c5843bb667..ff44e11202 100644 --- a/panda/src/pgraph/billboardEffect.cxx +++ b/panda/src/pgraph/billboardEffect.cxx @@ -29,7 +29,7 @@ TypeHandle BillboardEffect::_type_handle; CPT(RenderEffect) BillboardEffect:: make(const LVector3 &up_vector, bool eye_relative, bool axial_rotate, PN_stdfloat offset, const NodePath &look_at, - const LPoint3 &look_at_point) { + const LPoint3 &look_at_point, bool fixed_depth) { BillboardEffect *effect = new BillboardEffect; effect->_up_vector = up_vector; effect->_eye_relative = eye_relative; @@ -37,6 +37,7 @@ make(const LVector3 &up_vector, bool eye_relative, effect->_offset = offset; effect->_look_at = look_at; effect->_look_at_point = look_at_point; + effect->_fixed_depth = fixed_depth; effect->_off = false; return return_new(effect); } @@ -82,7 +83,9 @@ output(std::ostream &out) const { if (_eye_relative) { out << " eye"; } - if (_offset != 0.0f) { + if (_fixed_depth) { + out << " depth " << -_offset; + } else if (_offset != 0.0f) { out << " offset " << _offset; } if (!_look_at.is_empty()) { @@ -201,6 +204,9 @@ compare_to_impl(const RenderEffect *other) const { if (_eye_relative != ta->_eye_relative) { return (int)_eye_relative - (int)ta->_eye_relative; } + if (_fixed_depth != ta->_fixed_depth) { + return (int)_fixed_depth - (int)ta->_fixed_depth; + } if (_offset != ta->_offset) { return _offset < ta->_offset ? -1 : 1; } @@ -274,11 +280,17 @@ compute_billboard(CPT(TransformState) &node_transform, // Also slide the billboard geometry towards the camera according to the // offset factor. - if (_offset != 0.0f) { + if (_offset != 0.0f || _fixed_depth) { LVector3 translate(rel_mat(3, 0), rel_mat(3, 1), rel_mat(3, 2)); + LPoint3 pos; + if (_fixed_depth) { + pos = translate / rel_mat(3, 3); + } else { + pos.fill(0.0f); + } translate.normalize(); translate *= _offset; - rotate.set_row(3, translate); + rotate.set_row(3, pos + translate); } node_transform = translate->compose(TransformState::make_mat(rotate))->compose(node_transform); @@ -307,7 +319,25 @@ write_datagram(BamWriter *manager, Datagram &dg) { dg.add_stdfloat(_offset); _look_at_point.write_datagram(dg); - // *** We don't write out the _look_at NodePath right now. Maybe we should. + if (manager->get_file_minor_ver() >= 43) { + _look_at.write_datagram(manager, dg); + dg.add_bool(_fixed_depth); + } +} + +/** + * Receives an array of pointers, one for each time manager->read_pointer() + * was called in fillin(). Returns the number of pointers processed. + */ +int BillboardEffect:: +complete_pointers(TypedWritable **p_list, BamReader *manager) { + int pi = RenderEffect::complete_pointers(p_list, manager); + + if (manager->get_file_minor_ver() >= 43) { + pi += _look_at.complete_pointers(p_list + pi, manager); + } + + return pi; } /** @@ -341,4 +371,11 @@ fillin(DatagramIterator &scan, BamReader *manager) { _axial_rotate = scan.get_bool(); _offset = scan.get_stdfloat(); _look_at_point.read_datagram(scan); + + if (manager->get_file_minor_ver() >= 43) { + _look_at.fillin(scan, manager); + _fixed_depth = scan.get_bool(); + } else { + _fixed_depth = false; + } } diff --git a/panda/src/pgraph/billboardEffect.h b/panda/src/pgraph/billboardEffect.h index f83ecc5323..d080cec0d4 100644 --- a/panda/src/pgraph/billboardEffect.h +++ b/panda/src/pgraph/billboardEffect.h @@ -34,7 +34,8 @@ PUBLISHED: bool axial_rotate, PN_stdfloat offset, const NodePath &look_at, - const LPoint3 &look_at_point); + const LPoint3 &look_at_point, + bool fixed_depth = false); INLINE static CPT(RenderEffect) make_axis(); INLINE static CPT(RenderEffect) make_point_eye(); INLINE static CPT(RenderEffect) make_point_world(); @@ -43,6 +44,7 @@ PUBLISHED: INLINE const LVector3 &get_up_vector() const; INLINE bool get_eye_relative() const; INLINE bool get_axial_rotate() const; + INLINE bool get_fixed_depth() const; INLINE PN_stdfloat get_offset() const; INLINE const NodePath &get_look_at() const; INLINE const LPoint3 &get_look_at_point() const; @@ -72,9 +74,10 @@ private: private: bool _off; - LVector3 _up_vector; bool _eye_relative; bool _axial_rotate; + bool _fixed_depth; + LVector3 _up_vector; PN_stdfloat _offset; NodePath _look_at; LPoint3 _look_at_point; @@ -82,6 +85,8 @@ private: public: static void register_with_read_factory(); virtual void write_datagram(BamWriter *manager, Datagram &dg); + virtual int complete_pointers(TypedWritable **plist, + BamReader *manager); protected: static TypedWritable *make_from_bam(const FactoryParams ¶ms); diff --git a/panda/src/pgraph/camera.cxx b/panda/src/pgraph/camera.cxx index 5a10d6145e..ddf8006862 100644 --- a/panda/src/pgraph/camera.cxx +++ b/panda/src/pgraph/camera.cxx @@ -280,13 +280,10 @@ write_datagram(BamWriter *manager, Datagram &dg) { } } -//////////////////////////////////////////////////////////////////// -// Function: Camera::complete_pointers -// Access: Public, Virtual -// Description: Receives an array of pointers, one for each time -// manager->read_pointer() was called in fillin(). -// Returns the number of pointers processed. -//////////////////////////////////////////////////////////////////// +/** + * Receives an array of pointers, one for each time manager->read_pointer() + * was called in fillin(). Returns the number of pointers processed. + */ int Camera:: complete_pointers(TypedWritable **p_list, BamReader *manager) { int pi = LensNode::complete_pointers(p_list, manager); diff --git a/panda/src/pgraph/compassEffect.cxx b/panda/src/pgraph/compassEffect.cxx index 41bd9af880..fe39b03bf3 100644 --- a/panda/src/pgraph/compassEffect.cxx +++ b/panda/src/pgraph/compassEffect.cxx @@ -284,8 +284,25 @@ void CompassEffect:: write_datagram(BamWriter *manager, Datagram &dg) { RenderEffect::write_datagram(manager, dg); dg.add_uint16(_properties); - // *** We don't write out the _reference NodePath right now. Maybe we - // should. + + if (manager->get_file_minor_ver() >= 43) { + _reference.write_datagram(manager, dg); + } +} + +/** + * Receives an array of pointers, one for each time manager->read_pointer() + * was called in fillin(). Returns the number of pointers processed. + */ +int CompassEffect:: +complete_pointers(TypedWritable **p_list, BamReader *manager) { + int pi = RenderEffect::complete_pointers(p_list, manager); + + if (manager->get_file_minor_ver() >= 43) { + pi += _reference.complete_pointers(p_list + pi, manager); + } + + return pi; } /** @@ -313,4 +330,8 @@ void CompassEffect:: fillin(DatagramIterator &scan, BamReader *manager) { RenderEffect::fillin(scan, manager); _properties = scan.get_uint16(); + + if (manager->get_file_minor_ver() >= 43) { + _reference.fillin(scan, manager); + } } diff --git a/panda/src/pgraph/compassEffect.h b/panda/src/pgraph/compassEffect.h index adba81f9e6..a8aada7a59 100644 --- a/panda/src/pgraph/compassEffect.h +++ b/panda/src/pgraph/compassEffect.h @@ -90,6 +90,8 @@ private: public: static void register_with_read_factory(); virtual void write_datagram(BamWriter *manager, Datagram &dg); + virtual int complete_pointers(TypedWritable **plist, + BamReader *manager); protected: static TypedWritable *make_from_bam(const FactoryParams ¶ms); diff --git a/panda/src/pgraph/config_pgraph.cxx b/panda/src/pgraph/config_pgraph.cxx index e969dee72f..5729e22067 100644 --- a/panda/src/pgraph/config_pgraph.cxx +++ b/panda/src/pgraph/config_pgraph.cxx @@ -492,6 +492,7 @@ init_libpgraph() { ModelNode::register_with_read_factory(); ModelRoot::register_with_read_factory(); PandaNode::register_with_read_factory(); + ParamNodePath::register_with_read_factory(); PlaneNode::register_with_read_factory(); PolylightNode::register_with_read_factory(); PortalNode::register_with_read_factory(); diff --git a/panda/src/pgraph/loader.cxx b/panda/src/pgraph/loader.cxx index 3c04578a55..ab4db6ea2f 100644 --- a/panda/src/pgraph/loader.cxx +++ b/panda/src/pgraph/loader.cxx @@ -165,32 +165,25 @@ load_file(const Filename &filename, const LoaderOptions &options) const { } LoaderFileTypeRegistry *reg = LoaderFileTypeRegistry::get_global_ptr(); - LoaderFileType *requested_type = - reg->get_type_from_extension(extension); - if (requested_type == nullptr) { - if (report_errors) { - loader_cat.error() - << "Extension of file " << this_filename - << " is unrecognized; cannot load.\n"; - loader_cat.error(false) - << "Currently known scene file types are:\n"; - reg->write(loader_cat.error(false), 2); + LoaderFileType *requested_type = reg->get_type_from_extension(extension); + // requested_type may be nullptr at this point, because there's still a + // chance we can load it via the model-cache or by adding .bam to the end. + if (requested_type != nullptr) { + if (!requested_type->supports_load()) { + if (report_errors) { + loader_cat.error() + << requested_type->get_name() << " file type (." + << extension << ") does not support loading.\n"; + } + return nullptr; + } else if (compressed && !requested_type->supports_compressed()) { + if (report_errors) { + loader_cat.error() + << requested_type->get_name() << " file type (." + << extension << ") does not support in-line compression.\n"; + } + return nullptr; } - return nullptr; - } else if (!requested_type->supports_load()) { - if (report_errors) { - loader_cat.error() - << requested_type->get_name() << " file type (." - << extension << ") does not support loading.\n"; - } - return nullptr; - } else if (compressed && !requested_type->supports_compressed()) { - if (report_errors) { - loader_cat.error() - << requested_type->get_name() << " file type (." - << extension << ") does not support in-line compression.\n"; - } - return nullptr; } bool search = (this_options.get_flags() & LoaderOptions::LF_search) != 0; @@ -229,6 +222,14 @@ load_file(const Filename &filename, const LoaderOptions &options) const { } if (any_exist) { + if (requested_type == nullptr) { + loader_cat.error() + << "Extension of file " << this_filename + << " is unrecognized; cannot load.\n"; + loader_cat.error(false) + << "Currently known scene file types are:\n"; + reg->write(loader_cat.error(false), 2); + } loader_cat.error() << "Couldn't load file " << this_filename << ": all matching files on model path invalid " @@ -250,6 +251,14 @@ load_file(const Filename &filename, const LoaderOptions &options) const { } if (report_errors) { if (vfs->exists(this_filename)) { + if (requested_type == nullptr) { + loader_cat.error() + << "Extension of file " << this_filename + << " is unrecognized; cannot load.\n"; + loader_cat.error(false) + << "Currently known scene file types are:\n"; + reg->write(loader_cat.error(false), 2); + } loader_cat.error() << "Couldn't load file " << this_filename << ": invalid.\n"; } else { @@ -270,7 +279,8 @@ try_load_file(const Filename &pathname, const LoaderOptions &options, LoaderFileType *requested_type) const { BamCache *cache = BamCache::get_global_ptr(); - bool allow_ram_cache = requested_type->get_allow_ram_cache(options); + bool allow_ram_cache = + ((options.get_flags() & LoaderOptions::LF_no_ram_cache) == 0); if (allow_ram_cache) { // If we're allowing a RAM cache, use the ModelPool to load the file. @@ -291,7 +301,8 @@ try_load_file(const Filename &pathname, const LoaderOptions &options, bool report_errors = ((options.get_flags() & LoaderOptions::LF_report_errors) != 0 || loader_cat.is_debug()); PT(BamCacheRecord) record; - if (cache->get_cache_models() && requested_type->get_allow_disk_cache(options)) { + if (cache->get_cache_models() && + (options.get_flags() & LoaderOptions::LF_no_disk_cache) == 0) { // See if the model can be found in the on-disk cache, if it is active. record = cache->lookup(pathname, "bam"); if (record != nullptr) { @@ -332,36 +343,66 @@ try_load_file(const Filename &pathname, const LoaderOptions &options, } } - bool cache_only = (options.get_flags() & LoaderOptions::LF_cache_only) != 0; - if (!cache_only) { - // Load the model from disk. - PT(PandaNode) result = requested_type->load_file(pathname, options, record); - if (result != nullptr) { - if (record != nullptr) { - // Store the loaded model in the model cache. - record->set_data(result); - cache->store(record); - } + if (options.get_flags() & LoaderOptions::LF_cache_only) { + // We're not allowed to read from disk. + return nullptr; + } - if (premunge_data) { - SceneGraphReducer sgr; - sgr.premunge(result, RenderState::make_empty()); - } + // Load the model from disk. + PT(PandaNode) result; + if (requested_type != nullptr) { + result = requested_type->load_file(pathname, options, record); + } + if (result != nullptr) { + if (record != nullptr) { + // Store the loaded model in the model cache. + record->set_data(result); + cache->store(record); + } + } else { + // Do we have the same filename, but with .bam appended to the end? + string extension = pathname.get_extension(); + Filename pathname_bam = pathname; + if (extension == "pz" || extension == "gz") { + // Strip .pz/.gz, so that model.egg.pz -> model.egg.bam + extension = pathname_bam.get_extension(); + pathname_bam = pathname_bam.get_fullpath_wo_extension(); + } + if (extension == "bam") { + // Don't try to load .bam.bam files, that is just silly. + return nullptr; + } + pathname_bam += ".bam"; - if (allow_ram_cache && result->is_of_type(ModelRoot::get_class_type())) { - // Store the loaded model in the RAM cache, and make sure we return a - // copy so that this node can be modified independently from the RAM - // cached version. - ModelPool::add_model(pathname, DCAST(ModelRoot, result.p())); - if ((options.get_flags() & LoaderOptions::LF_allow_instance) == 0) { - result = result->copy_subgraph(); - } - } - return result; + BamFile bam_file; + if (!bam_file.open_read(pathname_bam, report_errors)) { + return nullptr; + } + + bam_file.get_reader()->set_loader_options(options); + result = bam_file.read_node(report_errors); + + nassertr_always(result != nullptr && result->is_of_type(ModelRoot::get_class_type()), nullptr); + // We don't bother with the model-cache here, since this .bam file is + // already effectively a cached version of the original model. + } + + if (premunge_data) { + SceneGraphReducer sgr; + sgr.premunge(result, RenderState::make_empty()); + } + + if (allow_ram_cache && result->is_of_type(ModelRoot::get_class_type())) { + // Store the loaded model in the RAM cache, and make sure we return a + // copy so that this node can be modified independently from the RAM + // cached version. + ModelPool::add_model(pathname, DCAST(ModelRoot, result.p())); + if ((options.get_flags() & LoaderOptions::LF_allow_instance) == 0) { + result = result->copy_subgraph(); } } - return nullptr; + return result; } /** diff --git a/panda/src/pgraph/nodePath.I b/panda/src/pgraph/nodePath.I index 5b7e80f7b7..1e0c6b3ab0 100644 --- a/panda/src/pgraph/nodePath.I +++ b/panda/src/pgraph/nodePath.I @@ -1752,8 +1752,8 @@ set_billboard_axis(PN_stdfloat offset) { * the camera. */ INLINE void NodePath:: -set_billboard_point_eye(PN_stdfloat offset) { - set_billboard_point_eye(NodePath(), offset); +set_billboard_point_eye(PN_stdfloat offset, bool fixed_depth) { + set_billboard_point_eye(NodePath(), offset, fixed_depth); } /** diff --git a/panda/src/pgraph/nodePath.cxx b/panda/src/pgraph/nodePath.cxx index 0f2639bccc..8732168212 100644 --- a/panda/src/pgraph/nodePath.cxx +++ b/panda/src/pgraph/nodePath.cxx @@ -4163,6 +4163,20 @@ get_material() const { return nullptr; } +/** + * Recursively searches the scene graph for references to the given material, + * and replaces them with the new material. + */ +void NodePath:: +replace_material(Material *mat, Material *new_mat) { + nassertv_always(!is_empty()); + nassertv(mat != nullptr); + nassertv(new_mat != nullptr); + + CPT(RenderAttrib) new_attrib = MaterialAttrib::make(new_mat); + r_replace_material(node(), mat, (const MaterialAttrib *)new_attrib.p()); +} + /** * Sets the geometry at this level and below to render using the indicated * fog. @@ -4744,11 +4758,11 @@ set_billboard_axis(const NodePath &camera, PN_stdfloat offset) { * the camera, towards a specified "camera" instead of to the viewing camera. */ void NodePath:: -set_billboard_point_eye(const NodePath &camera, PN_stdfloat offset) { +set_billboard_point_eye(const NodePath &camera, PN_stdfloat offset, bool fixed_depth) { nassertv_always(!is_empty()); CPT(RenderEffect) billboard = BillboardEffect::make (LVector3::up(), true, false, - offset, camera, LPoint3(0.0f, 0.0f, 0.0f)); + offset, camera, LPoint3(0.0f, 0.0f, 0.0f), fixed_depth); node()->set_effect(billboard); } @@ -5677,7 +5691,9 @@ encode_to_bam_stream(vector_uchar &data, BamWriter *writer) const { // Tell the BamWriter which node is the root node, for making NodePaths // relative to when writing them out to the file. - writer->set_root_node(node()); + if (!is_empty()) { + writer->set_root_node(node()); + } // Write an initial Datagram to represent the error type and number of // nodes. @@ -6632,12 +6648,63 @@ r_find_all_materials(PandaNode *node, const RenderState *state, } } +/** + * + */ +void NodePath:: +r_replace_material(PandaNode *node, Material *mat, + const MaterialAttrib *new_attrib) { + // Consider the state of the node itself. + { + CPT(RenderState) node_state = node->get_state(); + const MaterialAttrib *ma; + if (node_state->get_attrib(ma)) { + if (mat == ma->get_material()) { + node->set_state(node_state->set_attrib(new_attrib)); + } + } + } + + // If this is a GeomNode, consider the state of any of its Geoms. + if (node->is_geom_node()) { + GeomNode *gnode; + DCAST_INTO_V(gnode, node); + + int num_geoms = gnode->get_num_geoms(); + for (int i = 0; i < num_geoms; i++) { + CPT(RenderState) geom_state = gnode->get_geom_state(i); + + // Look for a MaterialAttrib on the state. + const MaterialAttrib *ma; + if (geom_state->get_attrib(ma)) { + if (mat == ma->get_material()) { + // Replace it + gnode->set_geom_state(i, geom_state->set_attrib(new_attrib)); + } + } + } + } + + // Now consider children. + PandaNode::Children cr = node->get_children(); + size_t num_children = cr.get_num_children(); + for (size_t i = 0; i < num_children; ++i) { + PandaNode *child = cr.get_child(i); + r_replace_material(child, mat, new_attrib); + } +} + /** * Writes the contents of this object to the datagram for shipping out to a * Bam file. */ void NodePath:: write_datagram(BamWriter *manager, Datagram &dg) const { + if (is_empty()) { + manager->write_pointer(dg, nullptr); + return; + } + PandaNode *root = DCAST(PandaNode, manager->get_root_node()); // We have no root node to measure from. diff --git a/panda/src/pgraph/nodePath.h b/panda/src/pgraph/nodePath.h index cf159972c5..3d5d4a6aa3 100644 --- a/panda/src/pgraph/nodePath.h +++ b/panda/src/pgraph/nodePath.h @@ -771,6 +771,7 @@ PUBLISHED: void clear_material(); bool has_material() const; PT(Material) get_material() const; + void replace_material(Material *mat, Material *new_mat); void set_fog(Fog *fog, int priority = 0); void set_fog_off(int priority = 0); @@ -815,10 +816,10 @@ PUBLISHED: void do_billboard_point_eye(const NodePath &camera, PN_stdfloat offset); void do_billboard_point_world(const NodePath &camera, PN_stdfloat offset); INLINE void set_billboard_axis(PN_stdfloat offset = 0.0); - INLINE void set_billboard_point_eye(PN_stdfloat offset = 0.0); + INLINE void set_billboard_point_eye(PN_stdfloat offset = 0.0, bool fixed_depth = false); INLINE void set_billboard_point_world(PN_stdfloat offset = 0.0); void set_billboard_axis(const NodePath &camera, PN_stdfloat offset); - void set_billboard_point_eye(const NodePath &camera, PN_stdfloat offset); + void set_billboard_point_eye(const NodePath &camera, PN_stdfloat offset, bool fixed_depth = false); void set_billboard_point_world(const NodePath &camera, PN_stdfloat offset); void clear_billboard(); bool has_billboard() const; @@ -1016,6 +1017,8 @@ private: const GlobPattern &glob) const; void r_find_all_materials(PandaNode *node, const RenderState *state, Materials &materials) const; + static void r_replace_material(PandaNode *node, Material *mat, + const MaterialAttrib *new_attrib); PT(NodePathComponent) _head; int _backup_key; diff --git a/panda/src/pgraph/pandaNode.cxx b/panda/src/pgraph/pandaNode.cxx index 7d53cbcaf7..25a65e19ae 100644 --- a/panda/src/pgraph/pandaNode.cxx +++ b/panda/src/pgraph/pandaNode.cxx @@ -1084,6 +1084,8 @@ set_effects(const RenderEffects *effects, Thread *current_thread) { */ void PandaNode:: set_transform(const TransformState *transform, Thread *current_thread) { + nassertv(!transform->is_invalid()); + // Need to have this held before we grab any other locks. LightMutexHolder holder(_dirty_prev_transforms._lock); @@ -1120,6 +1122,8 @@ set_transform(const TransformState *transform, Thread *current_thread) { */ void PandaNode:: set_prev_transform(const TransformState *transform, Thread *current_thread) { + nassertv(!transform->is_invalid()); + // Need to have this held before we grab any other locks. LightMutexHolder holder(_dirty_prev_transforms._lock); @@ -3510,12 +3514,12 @@ update_cached(bool update_bounds, int pipeline_stage, PandaNode::CDLockedStageRe const BoundingVolume **child_begin = &child_volumes[0]; const BoundingVolume **child_end = child_begin + child_volumes_i; ((BoundingVolume *)gbv)->around(child_begin, child_end); - } - // If we have a transform, apply it to the bounding volume we just - // computed. - if (!transform->is_identity()) { - gbv->xform(transform->get_mat()); + // If we have a transform, apply it to the bounding volume we just + // computed. + if (!transform->is_identity()) { + gbv->xform(transform->get_mat()); + } } cdataw->_external_bounds = gbv; @@ -3849,6 +3853,9 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) { // Mark the bounds stale. ++_next_update; + nassertr(!_transform->is_invalid(), pi); + nassertr(!_prev_transform->is_invalid(), pi); + return pi; } diff --git a/panda/src/pgraph/stateMunger.h b/panda/src/pgraph/stateMunger.h index c7fa6ef862..9fe71ab63e 100644 --- a/panda/src/pgraph/stateMunger.h +++ b/panda/src/pgraph/stateMunger.h @@ -17,7 +17,6 @@ #include "pandabase.h" #include "geomMunger.h" #include "renderState.h" -#include "weakKeyHashMap.h" /** * This is just a simple derivative of GeomMunger that adds the ability to diff --git a/panda/src/pgraph/transformState.cxx b/panda/src/pgraph/transformState.cxx index 4d02425683..fc8b3de606 100644 --- a/panda/src/pgraph/transformState.cxx +++ b/panda/src/pgraph/transformState.cxx @@ -96,6 +96,10 @@ TransformState:: // true now, probably we've been double-deleted. nassertv(get_ref_count() == 0); _cache_stats.add_num_states(-1); + +#ifndef NDEBUG + _flags = F_is_invalid | F_is_destructing; +#endif } /** diff --git a/panda/src/pgraphnodes/lightLensNode.I b/panda/src/pgraphnodes/lightLensNode.I index 9c59b6c998..4fd1138b03 100644 --- a/panda/src/pgraphnodes/lightLensNode.I +++ b/panda/src/pgraphnodes/lightLensNode.I @@ -28,51 +28,6 @@ is_shadow_caster() const { return _shadow_caster; } -/** - * Sets the flag indicating whether this light should cast shadows or not. - * This is the variant without buffer size, meaning that the current buffer - * size will be kept (512x512 is the default). Note that enabling shadows will - * require the shader generator to be enabled on the scene. - */ -INLINE void LightLensNode:: -set_shadow_caster(bool caster) { - if (_shadow_caster && !caster) { - clear_shadow_buffers(); - } - _shadow_caster = caster; - set_active(caster); - if (caster) { - setup_shadow_map(); - } -} - -/** - * Sets the flag indicating whether this light should cast shadows or not. - * The xsize and ysize parameters specify the size of the shadow buffer that - * will be set up, the sort parameter specifies the sort. Note that enabling - * shadows will require the shader generator to be enabled on the scene. - */ -INLINE void LightLensNode:: -set_shadow_caster(bool caster, int buffer_xsize, int buffer_ysize, int buffer_sort) { - if ((_shadow_caster && !caster) || buffer_xsize != _sb_size[0] || buffer_ysize != _sb_size[1]) { - clear_shadow_buffers(); - } - _shadow_caster = caster; - _sb_size.set(buffer_xsize, buffer_ysize); - - if (buffer_sort != _sb_sort) { - ShadowBuffers::iterator it; - for(it = _sbuffers.begin(); it != _sbuffers.end(); ++it) { - (*it).second->set_sort(buffer_sort); - } - _sb_sort = buffer_sort; - } - set_active(caster); - if (caster) { - setup_shadow_map(); - } -} - /** * Returns the sort of the shadow buffer to be created for this light source. */ @@ -115,3 +70,11 @@ get_shadow_buffer(GraphicsStateGuardianBase *gsg) { return (*it).second; } } + +/** + * Marks this light as having been used by the auto shader. + */ +INLINE void LightLensNode:: +mark_used_by_auto_shader() const { + _used_by_auto_shader = true; +} diff --git a/panda/src/pgraphnodes/lightLensNode.cxx b/panda/src/pgraphnodes/lightLensNode.cxx index 87f9a5bca8..aaa290e08b 100644 --- a/panda/src/pgraphnodes/lightLensNode.cxx +++ b/panda/src/pgraphnodes/lightLensNode.cxx @@ -19,6 +19,7 @@ #include "renderState.h" #include "cullFaceAttrib.h" #include "colorWriteAttrib.h" +#include "graphicsStateGuardianBase.h" TypeHandle LightLensNode::_type_handle; @@ -29,7 +30,8 @@ LightLensNode:: LightLensNode(const std::string &name, Lens *lens) : Camera(name, lens), _has_specular_color(false), - _attrib_count(0) + _attrib_count(0), + _used_by_auto_shader(false) { set_active(false); _shadow_caster = false; @@ -65,13 +67,67 @@ LightLensNode(const LightLensNode ©) : _sb_size(copy._sb_size), _sb_sort(-10), _has_specular_color(copy._has_specular_color), - _attrib_count(0) + _attrib_count(0), + _used_by_auto_shader(false) { if (_shadow_caster) { setup_shadow_map(); } } +/** + * Sets the flag indicating whether this light should cast shadows or not. + * This is the variant without buffer size, meaning that the current buffer + * size will be kept (512x512 is the default). Note that enabling shadows will + * require the shader generator to be enabled on the scene. + */ +void LightLensNode:: +set_shadow_caster(bool caster) { + if (_shadow_caster && !caster) { + clear_shadow_buffers(); + } + if (_shadow_caster != caster && _used_by_auto_shader) { + // Make sure any shaders using this light are regenerated. + GraphicsStateGuardianBase::mark_rehash_generated_shaders(); + } + _shadow_caster = caster; + set_active(caster); + if (caster) { + setup_shadow_map(); + } +} + +/** + * Sets the flag indicating whether this light should cast shadows or not. + * The xsize and ysize parameters specify the size of the shadow buffer that + * will be set up, the sort parameter specifies the sort. Note that enabling + * shadows will require the shader generator to be enabled on the scene. + */ +void LightLensNode:: +set_shadow_caster(bool caster, int buffer_xsize, int buffer_ysize, int buffer_sort) { + if ((_shadow_caster && !caster) || buffer_xsize != _sb_size[0] || buffer_ysize != _sb_size[1]) { + clear_shadow_buffers(); + } + if (_shadow_caster != caster && _used_by_auto_shader) { + // Make sure any shaders using this light are regenerated. + GraphicsStateGuardianBase::mark_rehash_generated_shaders(); + } + _shadow_caster = caster; + _sb_size.set(buffer_xsize, buffer_ysize); + + if (buffer_sort != _sb_sort) { + ShadowBuffers::iterator it; + for(it = _sbuffers.begin(); it != _sbuffers.end(); ++it) { + (*it).second->set_sort(buffer_sort); + } + _sb_sort = buffer_sort; + } + set_active(caster); + if (caster) { + setup_shadow_map(); + } +} + /** * Clears the shadow buffers, meaning they will be automatically recreated * when the Shader Generator needs them. diff --git a/panda/src/pgraphnodes/lightLensNode.h b/panda/src/pgraphnodes/lightLensNode.h index 4398f02bfd..f045c539b3 100644 --- a/panda/src/pgraphnodes/lightLensNode.h +++ b/panda/src/pgraphnodes/lightLensNode.h @@ -38,8 +38,8 @@ PUBLISHED: INLINE bool has_specular_color() const; INLINE bool is_shadow_caster() const; - INLINE void set_shadow_caster(bool caster); - INLINE void set_shadow_caster(bool caster, int buffer_xsize, int buffer_ysize, int sort = -10); + void set_shadow_caster(bool caster); + void set_shadow_caster(bool caster, int buffer_xsize, int buffer_ysize, int sort = -10); INLINE int get_shadow_buffer_sort() const; @@ -52,6 +52,9 @@ PUBLISHED: MAKE_PROPERTY(shadow_caster, is_shadow_caster); MAKE_PROPERTY(shadow_buffer_size, get_shadow_buffer_size, set_shadow_buffer_size); +public: + INLINE void mark_used_by_auto_shader() const; + protected: LightLensNode(const LightLensNode ©); void clear_shadow_buffers(); @@ -61,6 +64,7 @@ protected: bool _shadow_caster; bool _has_specular_color; int _sb_sort; + mutable bool _used_by_auto_shader = false; PT(Texture) _shadow_map; diff --git a/panda/src/pgraphnodes/shaderGenerator.cxx b/panda/src/pgraphnodes/shaderGenerator.cxx index cf8f0783d4..16a7483478 100644 --- a/panda/src/pgraphnodes/shaderGenerator.cxx +++ b/panda/src/pgraphnodes/shaderGenerator.cxx @@ -294,8 +294,14 @@ analyze_renderstate(ShaderKey &key, const RenderState *rs) { if (node->is_of_type(LightLensNode::get_class_type())) { const LightLensNode *llnode = (const LightLensNode *)node; - if (shader_attrib->auto_shadow_on() && llnode->is_shadow_caster()) { - info._flags |= ShaderKey::LF_has_shadows; + if (shader_attrib->auto_shadow_on()) { + if (llnode->is_shadow_caster()) { + info._flags |= ShaderKey::LF_has_shadows; + } + + // Make sure that the next time the shadows are toggled on this + // light, it triggers a state rehash. + llnode->mark_used_by_auto_shader(); } if (llnode->has_specular_color()) { info._flags |= ShaderKey::LF_has_specular_color; diff --git a/panda/src/pipeline/conditionVarImpl.h b/panda/src/pipeline/conditionVarImpl.h index 499a5b0c62..7837622ba6 100644 --- a/panda/src/pipeline/conditionVarImpl.h +++ b/panda/src/pipeline/conditionVarImpl.h @@ -50,4 +50,17 @@ typedef ConditionVarPosixImpl ConditionVarFullImpl; #endif +#if defined(WIN32_VC) +#include "conditionVarWin32Impl.h" +typedef ConditionVarWin32Impl TrueConditionVarImpl; + +#elif defined(HAVE_POSIX_THREADS) +#include "conditionVarPosixImpl.h" +typedef ConditionVarPosixImpl TrueConditionVarImpl; + +#else +// No true threads, sorry. Better not try to use 'em. + +#endif + #endif diff --git a/panda/src/pipeline/lightReMutexDirect.h b/panda/src/pipeline/lightReMutexDirect.h index d1bc022740..384330587e 100644 --- a/panda/src/pipeline/lightReMutexDirect.h +++ b/panda/src/pipeline/lightReMutexDirect.h @@ -17,8 +17,7 @@ #include "pandabase.h" #include "mutexImpl.h" #include "reMutexDirect.h" - -class Thread; +#include "thread.h" #ifndef DEBUG_THREADS diff --git a/panda/src/pipeline/reMutexDirect.h b/panda/src/pipeline/reMutexDirect.h index faf46d0fb0..9a82b728ba 100644 --- a/panda/src/pipeline/reMutexDirect.h +++ b/panda/src/pipeline/reMutexDirect.h @@ -17,8 +17,7 @@ #include "pandabase.h" #include "mutexTrueImpl.h" #include "conditionVarImpl.h" - -class Thread; +#include "thread.h" #ifndef DEBUG_THREADS diff --git a/panda/src/pnmimagetypes/pnmFileTypePNG.cxx b/panda/src/pnmimagetypes/pnmFileTypePNG.cxx index 799f4d1dce..7d64faed50 100644 --- a/panda/src/pnmimagetypes/pnmFileTypePNG.cxx +++ b/panda/src/pnmimagetypes/pnmFileTypePNG.cxx @@ -338,11 +338,12 @@ read_data(xel *array, xelval *alpha_data) { // format, mainly because we keep array and alpha data separately, and there // doesn't appear to be good support to get this stuff out row-at-a-time for // interlaced files. - png_bytep *rows = (png_bytep *)PANDA_MALLOC_ARRAY(num_rows * sizeof(png_bytep)); + png_bytep *rows = (png_bytep *)alloca(num_rows * sizeof(png_bytep)); int yi; + png_byte *alloc = (png_byte *)PANDA_MALLOC_ARRAY(row_byte_length * sizeof(png_byte) * num_rows); for (yi = 0; yi < num_rows; yi++) { - rows[yi] = (png_byte *)PANDA_MALLOC_ARRAY(row_byte_length * sizeof(png_byte)); + rows[yi] = alloc + (row_byte_length * sizeof(png_byte)) * yi; } png_read_image(_png, rows); @@ -402,12 +403,10 @@ read_data(xel *array, xelval *alpha_data) { } nassertr(source <= rows[yi] + row_byte_length, yi); - PANDA_FREE_ARRAY(rows[yi]); } - PANDA_FREE_ARRAY(rows); - png_read_end(_png, nullptr); + PANDA_FREE_ARRAY(alloc); return _y_size; } diff --git a/panda/src/putil/bam.h b/panda/src/putil/bam.h index ad7d000fd2..e21dadbc14 100644 --- a/panda/src/putil/bam.h +++ b/panda/src/putil/bam.h @@ -32,7 +32,7 @@ static const unsigned short _bam_major_ver = 6; // Bumped to major version 6 on 2006-02-11 to factor out PandaNode::CData. static const unsigned short _bam_first_minor_ver = 14; -static const unsigned short _bam_minor_ver = 42; +static const unsigned short _bam_minor_ver = 44; // Bumped to minor version 14 on 2007-12-19 to change default ColorAttrib. // Bumped to minor version 15 on 2008-04-09 to add TextureAttrib::_implicit_sort. // Bumped to minor version 16 on 2008-05-13 to add Texture::_quality_level. @@ -62,5 +62,7 @@ static const unsigned short _bam_minor_ver = 42; // Bumped to minor version 40 on 2016-01-11 to make NodePaths writable. // Bumped to minor version 41 on 2016-03-02 to change LensNode, Lens, and Camera. // Bumped to minor version 42 on 2016-04-08 to expand ColorBlendAttrib. +// Bumped to minor version 43 on 2018-12-06 to expand BillboardEffect and CompassEffect. +// Bumped to minor version 44 on 2018-12-23 to rename CollisionTube to CollisionCapsule. #endif diff --git a/panda/src/putil/bamWriter.cxx b/panda/src/putil/bamWriter.cxx index d8aa3e0b30..3de731d27d 100644 --- a/panda/src/putil/bamWriter.cxx +++ b/panda/src/putil/bamWriter.cxx @@ -20,9 +20,28 @@ #include "bamWriter.h" #include "bamReader.h" #include "lightMutexHolder.h" +#include "simpleHashMap.h" #include +// Keeps track of older type names in case we want to write out older .bam +// files. +struct ObsoleteName { + std::string _name; + int _before_major; + int _before_minor; + + bool operator < (const ObsoleteName &other) const { + if (_before_major != other._before_major) { + return _before_major < other._before_major; + } + return _before_minor < other._before_minor; + } +}; + +// This is a SimpleHashMap to avoid static init ordering issues. +static SimpleHashMap > obsolete_type_names; + /** * */ @@ -504,7 +523,14 @@ write_handle(Datagram &packet, TypeHandle type) { if (inserted) { // This is the first time this TypeHandle has been written, so also // write out its definition. - packet.add_string(type.get_name()); + + if (_file_major == _bam_major_ver && _file_minor == _bam_minor_ver) { + packet.add_string(type.get_name()); + } else { + // We are writing an older .bam format, so we need to look up whether + // we may need to write an older type name. + packet.add_string(get_obsolete_type_name(type, _file_major, _file_minor)); + } // We also need to write the derivation of the TypeHandle, in case the // program reading this file later has never heard of this type before. @@ -518,6 +544,45 @@ write_handle(Datagram &packet, TypeHandle type) { } } +/** + * Returns the name that the given type had in an older .bam version. + */ +std::string BamWriter:: +get_obsolete_type_name(TypeHandle type, int major, int minor) { + int index = obsolete_type_names.find(type); + if (index >= 0) { + // Iterate over the names. It is sorted such that the lower versions are + // listed first. + for (const ObsoleteName &name : obsolete_type_names.get_data((size_t)index)) { + if (major < name._before_major || + (major == name._before_major && minor < name._before_minor)) { + // We have a hit. + return name._name; + } + } + } + + return TypeRegistry::ptr()->get_name(type, nullptr); +} + +/** + * Registers the given type as having an older name in .bam files *before* the + * indicated version. You can call this multiple times for the same type in + * order to establish a history of renames for this type. + */ +void BamWriter:: +record_obsolete_type_name(TypeHandle type, std::string name, + int before_major, int before_minor) { + // Make sure it is registered as alternate name for reading. + TypeRegistry *reg = TypeRegistry::ptr(); + reg->record_alternate_name(type, name); + + ObsoleteName obsolete_name; + obsolete_name._name = std::move(name); + obsolete_name._before_major = before_major; + obsolete_name._before_minor = before_minor; + obsolete_type_names[type].insert(std::move(obsolete_name)); +} /** * This is called by the TypedWritable destructor. It should remove the diff --git a/panda/src/putil/bamWriter.h b/panda/src/putil/bamWriter.h index b8c53aac38..780104b61c 100644 --- a/panda/src/putil/bamWriter.h +++ b/panda/src/putil/bamWriter.h @@ -111,6 +111,10 @@ public: bool register_pta(Datagram &packet, const void *ptr); void write_handle(Datagram &packet, TypeHandle type); + static std::string get_obsolete_type_name(TypeHandle type, int major, int minor); + static void record_obsolete_type_name(TypeHandle type, std::string name, + int before_major, int before_minor); + private: void object_destructs(TypedWritable *object); diff --git a/panda/src/putil/config_putil.cxx b/panda/src/putil/config_putil.cxx index 593da51fd0..93694b3d30 100644 --- a/panda/src/putil/config_putil.cxx +++ b/panda/src/putil/config_putil.cxx @@ -31,6 +31,7 @@ #include "datagram.h" #include "doubleBitMask.h" #include "factoryParam.h" +#include "gamepadButton.h" #include "namable.h" #include "nodeCachedReferenceCount.h" #include "paramValue.h" @@ -238,6 +239,7 @@ init_libputil() { WritableConfigurable::init_type(); WritableParam::init_type(); + GamepadButton::init_gamepad_buttons(); KeyboardButton::init_keyboard_buttons(); MouseButton::init_mouse_buttons(); diff --git a/panda/src/putil/gamepadButton.cxx b/panda/src/putil/gamepadButton.cxx new file mode 100644 index 0000000000..4e3c8be22c --- /dev/null +++ b/panda/src/putil/gamepadButton.cxx @@ -0,0 +1,122 @@ +/** + * 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 gamepadButton.cxx + * @author rdb + * @date 2015-08-21 + */ + +#include "gamepadButton.h" +#include "buttonRegistry.h" + +#define DEFINE_GAMEPAD_BUTTON_HANDLE(KeyName) \ + static ButtonHandle _##KeyName; \ + ButtonHandle GamepadButton::KeyName() { return _##KeyName; } + +DEFINE_GAMEPAD_BUTTON_HANDLE(lstick) +DEFINE_GAMEPAD_BUTTON_HANDLE(rstick) +DEFINE_GAMEPAD_BUTTON_HANDLE(lshoulder) +DEFINE_GAMEPAD_BUTTON_HANDLE(rshoulder) +DEFINE_GAMEPAD_BUTTON_HANDLE(ltrigger) +DEFINE_GAMEPAD_BUTTON_HANDLE(rtrigger) +DEFINE_GAMEPAD_BUTTON_HANDLE(lgrip) +DEFINE_GAMEPAD_BUTTON_HANDLE(rgrip) + +DEFINE_GAMEPAD_BUTTON_HANDLE(dpad_left) +DEFINE_GAMEPAD_BUTTON_HANDLE(dpad_right) +DEFINE_GAMEPAD_BUTTON_HANDLE(dpad_up) +DEFINE_GAMEPAD_BUTTON_HANDLE(dpad_down) + +DEFINE_GAMEPAD_BUTTON_HANDLE(back) +DEFINE_GAMEPAD_BUTTON_HANDLE(guide) +DEFINE_GAMEPAD_BUTTON_HANDLE(start) + +DEFINE_GAMEPAD_BUTTON_HANDLE(next) +DEFINE_GAMEPAD_BUTTON_HANDLE(previous) + +DEFINE_GAMEPAD_BUTTON_HANDLE(face_a) +DEFINE_GAMEPAD_BUTTON_HANDLE(face_b) +DEFINE_GAMEPAD_BUTTON_HANDLE(face_c) +DEFINE_GAMEPAD_BUTTON_HANDLE(face_x) +DEFINE_GAMEPAD_BUTTON_HANDLE(face_y) +DEFINE_GAMEPAD_BUTTON_HANDLE(face_z) + +DEFINE_GAMEPAD_BUTTON_HANDLE(face_1) +DEFINE_GAMEPAD_BUTTON_HANDLE(face_2) + +DEFINE_GAMEPAD_BUTTON_HANDLE(trigger) +DEFINE_GAMEPAD_BUTTON_HANDLE(hat_up) +DEFINE_GAMEPAD_BUTTON_HANDLE(hat_down) +DEFINE_GAMEPAD_BUTTON_HANDLE(hat_left) +DEFINE_GAMEPAD_BUTTON_HANDLE(hat_right) + +/** + * Returns the ButtonHandle associated with the particular numbered joystick + * button (zero-based), if there is one, or ButtonHandle::none() if there is + * not. + */ +ButtonHandle GamepadButton:: +joystick(int button_number) { + if (button_number >= 0) { + // "button1" does not exist, it is called "trigger" instead + static pvector buttons(1, _trigger); + while ((size_t)button_number >= buttons.size()) { + char numstr[20]; + sprintf(numstr, "joystick%d", (int)buttons.size() + 1); + ButtonHandle handle; + ButtonRegistry::ptr()->register_button(handle, numstr); + buttons.push_back(handle); + } + return buttons[button_number]; + } + return ButtonHandle::none(); +} + +/** + * This is intended to be called only once, by the static initialization + * performed in config_util.cxx. + */ +void GamepadButton:: +init_gamepad_buttons() { + ButtonRegistry::ptr()->register_button(_lstick, "lstick"); + ButtonRegistry::ptr()->register_button(_rstick, "rstick"); + ButtonRegistry::ptr()->register_button(_lshoulder, "lshoulder"); + ButtonRegistry::ptr()->register_button(_rshoulder, "rshoulder"); + ButtonRegistry::ptr()->register_button(_ltrigger, "ltrigger"); + ButtonRegistry::ptr()->register_button(_rtrigger, "rtrigger"); + ButtonRegistry::ptr()->register_button(_lgrip, "lgrip"); + ButtonRegistry::ptr()->register_button(_rgrip, "rgrip"); + + ButtonRegistry::ptr()->register_button(_dpad_left, "dpad_left"); + ButtonRegistry::ptr()->register_button(_dpad_right, "dpad_right"); + ButtonRegistry::ptr()->register_button(_dpad_up, "dpad_up"); + ButtonRegistry::ptr()->register_button(_dpad_down, "dpad_down"); + + ButtonRegistry::ptr()->register_button(_back, "back"); + ButtonRegistry::ptr()->register_button(_guide, "guide"); + ButtonRegistry::ptr()->register_button(_start, "start"); + + ButtonRegistry::ptr()->register_button(_next, "next"); + ButtonRegistry::ptr()->register_button(_previous, "previous"); + + ButtonRegistry::ptr()->register_button(_face_a, "face_a"); + ButtonRegistry::ptr()->register_button(_face_b, "face_b"); + ButtonRegistry::ptr()->register_button(_face_c, "face_c"); + ButtonRegistry::ptr()->register_button(_face_x, "face_x"); + ButtonRegistry::ptr()->register_button(_face_y, "face_y"); + ButtonRegistry::ptr()->register_button(_face_z, "face_z"); + + ButtonRegistry::ptr()->register_button(_face_1, "face_1"); + ButtonRegistry::ptr()->register_button(_face_2, "face_2"); + + ButtonRegistry::ptr()->register_button(_trigger, "trigger"); + ButtonRegistry::ptr()->register_button(_hat_up, "hat_up"); + ButtonRegistry::ptr()->register_button(_hat_down, "hat_down"); + ButtonRegistry::ptr()->register_button(_hat_left, "hat_left"); + ButtonRegistry::ptr()->register_button(_hat_right, "hat_right"); +} diff --git a/panda/src/putil/gamepadButton.h b/panda/src/putil/gamepadButton.h new file mode 100644 index 0000000000..ff89c7e294 --- /dev/null +++ b/panda/src/putil/gamepadButton.h @@ -0,0 +1,70 @@ +/** + * 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 gamepadButton.h + * @author rdb + * @date 2015-08-21 + */ + +#ifndef GAMEPADBUTTON_H +#define GAMEPADBUTTON_H + +#include "pandabase.h" + +#include "buttonHandle.h" + +/** + * This class is just used as a convenient namespace for grouping all of these + * handy functions that return buttons which map to gamepad buttons. + */ +class EXPCL_PANDA_PUTIL GamepadButton { +PUBLISHED: + static ButtonHandle lstick(); + static ButtonHandle rstick(); + static ButtonHandle lshoulder(); + static ButtonHandle rshoulder(); + static ButtonHandle ltrigger(); + static ButtonHandle rtrigger(); + static ButtonHandle lgrip(); + static ButtonHandle rgrip(); + + static ButtonHandle dpad_left(); + static ButtonHandle dpad_right(); + static ButtonHandle dpad_up(); + static ButtonHandle dpad_down(); + + static ButtonHandle back(); + static ButtonHandle guide(); + static ButtonHandle start(); + + static ButtonHandle next(); + static ButtonHandle previous(); + + static ButtonHandle face_a(); + static ButtonHandle face_b(); + static ButtonHandle face_c(); + static ButtonHandle face_x(); + static ButtonHandle face_y(); + static ButtonHandle face_z(); + + static ButtonHandle face_1(); + static ButtonHandle face_2(); + + // Flight stick buttons, takes zero-based index. First is always trigger. + static ButtonHandle trigger(); + static ButtonHandle joystick(int button_number); + static ButtonHandle hat_up(); + static ButtonHandle hat_down(); + static ButtonHandle hat_left(); + static ButtonHandle hat_right(); + +public: + static void init_gamepad_buttons(); +}; + +#endif diff --git a/panda/src/putil/mouseData.I b/panda/src/putil/mouseData.I deleted file mode 100644 index c0ab6997ef..0000000000 --- a/panda/src/putil/mouseData.I +++ /dev/null @@ -1,73 +0,0 @@ -/** - * 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 mouseData.I - * @author drose - * @date 2002-07-15 - */ - -/** - * - */ -INLINE MouseData:: -MouseData() { - _in_window = false; - _xpos = 0; - _ypos = 0; -} - -/** - * - */ -INLINE MouseData:: -MouseData(const MouseData ©) : - _in_window(copy._in_window), - _xpos(copy._xpos), - _ypos(copy._ypos) -{ -} - -/** - * - */ -INLINE void MouseData:: -operator = (const MouseData ©) { - _in_window = copy._in_window; - _xpos = copy._xpos; - _ypos = copy._ypos; -} - -/** - * - */ -INLINE double MouseData:: -get_x() const { - return _xpos; -} - -/** - * - */ -INLINE double MouseData:: -get_y() const { - return _ypos; -} - -/** - * - */ -INLINE bool MouseData:: -get_in_window() const { - return _in_window; -} - - -INLINE std::ostream &operator << (std::ostream &out, const MouseData &md) { - md.output(out); - return out; -} diff --git a/panda/src/putil/mouseData.h b/panda/src/putil/mouseData.h index 0d2a294568..2747c26061 100644 --- a/panda/src/putil/mouseData.h +++ b/panda/src/putil/mouseData.h @@ -7,45 +7,21 @@ * with this source code in a file named "LICENSE." * * @file mouseData.h - * @author drose - * @date 1999-02-08 + * @author rdb + * @date 2018-09-24 */ #ifndef MOUSEDATA_H #define MOUSEDATA_H -#include "pandabase.h" - -#include "modifierButtons.h" +#include "pointerData.h" +BEGIN_PUBLISH /** - * Holds the data that might be generated by a 2-d pointer input device, such - * as the mouse in the GraphicsWindow. + * Deprecated alias for PointerData. */ -class EXPCL_PANDA_PUTIL MouseData { -PUBLISHED: - INLINE MouseData(); - INLINE MouseData(const MouseData ©); - INLINE void operator = (const MouseData ©); +typedef PointerData MouseData; - INLINE double get_x() const; - INLINE double get_y() const; - INLINE bool get_in_window() const; - - void output(std::ostream &out) const; - - MAKE_PROPERTY(x, get_x); - MAKE_PROPERTY(y, get_y); - MAKE_PROPERTY(in_window, get_in_window); - -public: - bool _in_window; - double _xpos; - double _ypos; -}; - -INLINE std::ostream &operator << (std::ostream &out, const MouseData &md); - -#include "mouseData.I" +END_PUBLISH #endif diff --git a/panda/src/putil/p3putil_composite1.cxx b/panda/src/putil/p3putil_composite1.cxx index 446f7f1f07..f78459eff7 100644 --- a/panda/src/putil/p3putil_composite1.cxx +++ b/panda/src/putil/p3putil_composite1.cxx @@ -29,4 +29,3 @@ #include "factoryBase.cxx" #include "factoryParam.cxx" #include "factoryParams.cxx" -#include "globalPointerRegistry.cxx" diff --git a/panda/src/putil/p3putil_composite2.cxx b/panda/src/putil/p3putil_composite2.cxx index a4ce5c9cfc..2b907ef7f7 100644 --- a/panda/src/putil/p3putil_composite2.cxx +++ b/panda/src/putil/p3putil_composite2.cxx @@ -1,3 +1,5 @@ +#include "gamepadButton.cxx" +#include "globalPointerRegistry.cxx" #include "ioPtaDatagramFloat.cxx" #include "ioPtaDatagramInt.cxx" #include "ioPtaDatagramShort.cxx" @@ -7,11 +9,11 @@ #include "loaderOptions.cxx" #include "modifierButtons.cxx" #include "mouseButton.cxx" -#include "mouseData.cxx" #include "nameUniquifier.cxx" #include "nodeCachedReferenceCount.cxx" #include "paramValue.cxx" #include "pbitops.cxx" +#include "pointerData.cxx" #include "pta_ushort.cxx" #include "simpleHashMap.cxx" #include "sparseArray.cxx" @@ -23,7 +25,6 @@ #include "vector_typedWritable.cxx" #include "vector_ushort.cxx" #include "vector_writable.cxx" -#include "weakKeyHashMap.cxx" #include "writableConfigurable.cxx" #include "writableParam.cxx" diff --git a/panda/src/putil/pointerData.I b/panda/src/putil/pointerData.I new file mode 100644 index 0000000000..db508d8e71 --- /dev/null +++ b/panda/src/putil/pointerData.I @@ -0,0 +1,71 @@ +/** + * 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 pointerData.I + * @author drose + * @date 2002-07-15 + */ + +/** + * + */ +INLINE double PointerData:: +get_x() const { + return _xpos; +} + +/** + * + */ +INLINE double PointerData:: +get_y() const { + return _ypos; +} + +/** + * If this returns false, the pointer is not currently present in the window + * and the values returned by get_x() and get_y() may not be meaningful. + */ +INLINE bool PointerData:: +get_in_window() const { + return _in_window; +} + +/** + * Returns a unique identifier for this pointer. This is for tracking + * individual fingers. This value should not be assumed to have a specific + * meaning other than that there will not be two different pointers active + * simultaneously with the same identifier. + */ +INLINE int PointerData:: +get_id() const { + return _id; +} + +/** + * Returns the type of pointing device. + */ +INLINE PointerType PointerData:: +get_type() const { + return _type; +} + +/** + * Returns the pressure of the pointer. For mice, this will be 1.0 if any + * button is pressed, 0.0 otherwise. + */ +INLINE double PointerData:: +get_pressure() const { + return _pressure; +} + + +INLINE std::ostream &operator << (std::ostream &out, const PointerData &md) { + md.output(out); + return out; +} diff --git a/panda/src/putil/mouseData.cxx b/panda/src/putil/pointerData.cxx similarity index 70% rename from panda/src/putil/mouseData.cxx rename to panda/src/putil/pointerData.cxx index de83bd1505..ccf8e9d9f9 100644 --- a/panda/src/putil/mouseData.cxx +++ b/panda/src/putil/pointerData.cxx @@ -6,21 +6,21 @@ * license. You should have received a copy of this license along * with this source code in a file named "LICENSE." * - * @file mouseData.cxx + * @file pointerData.cxx * @author drose * @date 1999-02-08 */ -#include "mouseData.h" +#include "pointerData.h" /** * */ -void MouseData:: +void PointerData:: output(std::ostream &out) const { if (!_in_window) { - out << "MouseData: Not in window"; + out << "PointerData: Not in window"; } else { - out << "MouseData: (" << _xpos << ", " << _ypos << ")"; + out << "PointerData: (" << _xpos << ", " << _ypos << ")"; } } diff --git a/panda/src/putil/pointerData.h b/panda/src/putil/pointerData.h new file mode 100644 index 0000000000..30580cbd6a --- /dev/null +++ b/panda/src/putil/pointerData.h @@ -0,0 +1,72 @@ +/** + * 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 pointerData.h + * @author drose + * @date 1999-02-08 + */ + +#ifndef POINTERDATA_H +#define POINTERDATA_H + +#include "pandabase.h" + +#include "modifierButtons.h" + +BEGIN_PUBLISH +/** + * Contains the types of pointer device. + */ +enum class PointerType { + unknown, + mouse, + finger, + stylus, + eraser, +}; +END_PUBLISH + +/** + * Holds the data that might be generated by a 2-d pointer input device, such + * as the mouse in the GraphicsWindow. + */ +class EXPCL_PANDA_PUTIL PointerData { +PUBLISHED: + INLINE double get_x() const; + INLINE double get_y() const; + INLINE bool get_in_window() const; + +public: + INLINE int get_id() const; + INLINE PointerType get_type() const; + INLINE double get_pressure() const; + + void output(std::ostream &out) const; + +PUBLISHED: + MAKE_PROPERTY(x, get_x); + MAKE_PROPERTY(y, get_y); + MAKE_PROPERTY(type, get_type); + MAKE_PROPERTY(id, get_id); + MAKE_PROPERTY(in_window, get_in_window); + MAKE_PROPERTY(pressure, get_pressure); + +public: + bool _in_window = false; + double _xpos = 0.0; + double _ypos = 0.0; + double _pressure = 0.0; + PointerType _type = PointerType::unknown; + int _id = 0; +}; + +INLINE std::ostream &operator << (std::ostream &out, const PointerData &md); + +#include "pointerData.I" + +#endif diff --git a/panda/src/putil/weakKeyHashMap.I b/panda/src/putil/weakKeyHashMap.I deleted file mode 100644 index b2dc97c3f4..0000000000 --- a/panda/src/putil/weakKeyHashMap.I +++ /dev/null @@ -1,627 +0,0 @@ -/** - * 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 weakKeyHashMap.I - * @author rdb - * @date 2015-07-13 - */ - -/** - * - */ -template -INLINE WeakKeyHashMap:: -WeakKeyHashMap() : - _table(nullptr), - _deleted_chain(nullptr), - _table_size(0), - _num_entries(0) -{ -} - -/** - * - */ -template -INLINE WeakKeyHashMap:: -~WeakKeyHashMap() { - clear(); -} - -/** - * Quickly exchanges the contents of this map and the other map. - */ -template -INLINE void WeakKeyHashMap:: -swap(WeakKeyHashMap &other) { - TableEntry *t0 = _table; - _table = other._table; - other._table = t0; - - DeletedBufferChain *t1 = _deleted_chain; - _deleted_chain = other._deleted_chain; - other._deleted_chain = t1; - - size_t t2 = _table_size; - _table_size = other._table_size; - other._table_size = t2; - - size_t t3 = _num_entries; - _num_entries = other._num_entries; - other._num_entries = t3; -} - -/** - * Searches for the indicated key in the table. Returns its index number if - * it is found, or -1 if it is not present in the table. - */ -template -int WeakKeyHashMap:: -find(const Key *key) const { - if (_table_size == 0) { - // Special case: the table is empty. - return -1; - } - - size_t index = get_hash(key); - if (!has_element(index)) { - return -1; - } - if (is_element(index, key)) { - return index; - } - - // There was some other key at the hashed slot. That's a hash conflict. - // Maybe our entry was recorded at a later slot position; scan the - // subsequent positions until we find the entry or an unused slot, - // indicating the end of the scan. - size_t i = index; - i = (i + 1) & (_table_size - 1); - while (i != index && has_element(i)) { - if (is_element(i, key)) { - return i; - } - i = (i + 1) & (_table_size - 1); - } - - // The key is not in the table. - return -1; -} - -/** - * Records the indicated key/data pair in the map. If the key was already - * present, silently replaces it. Returns the index at which it was stored. - */ -template -int WeakKeyHashMap:: -store(const Key *key, const Value &data) { - if (_table_size == 0) { - // Special case: the first key in an empty table. - nassertr(_num_entries == 0, -1); - new_table(); - size_t index = get_hash(key); - store_new_element(index, key, data); - ++_num_entries; -#ifdef _DEBUG - nassertr(validate(), index); -#endif - return index; - } - - size_t index = get_hash(key); - if (!has_element(index)) { - // This element is not already in the map; add it. - if (consider_expand_table()) { - return store(key, data); - } - store_new_element(index, key, data); - ++_num_entries; -#ifdef _DEBUG - nassertr(validate(), index); -#endif - return index; - } - if (is_element(index, key)) { - // This element is already in the map; replace the data at that key. - _table[index]._data = data; -#ifdef _DEBUG - nassertr(validate(), index); -#endif - return index; - } - - // There was some other key at the hashed slot. That's a hash conflict. - // Record this entry at a later position. - size_t i = index; - i = (i + 1) & (_table_size - 1); - while (i != index) { - if (!has_element(i)) { - if (consider_expand_table()) { - return store(key, data); - } - store_new_element(i, key, data); - ++_num_entries; -#ifdef _DEBUG - nassertr(validate(), i); -#endif - return i; - } - if (is_element(i, key)) { - _table[i]._data = data; -#ifdef _DEBUG - nassertr(validate(), i); -#endif - return i; - } - i = (i + 1) & (_table_size - 1); - } - - // Shouldn't get here unless _num_entries == _table_size, which shouldn't be - // possible due to consider_expand_table(). - nassertr(false, -1); - return -1; // To satisfy compiler -} - -/** - * Removes the indicated key and its associated data from the table. Returns - * true if the key was removed, false if it was not present. - */ -template -INLINE bool WeakKeyHashMap:: -remove(const Key *key) { - int index = find(key); - if (index == -1) { - return false; - } - remove_element(index); - return true; -} - -/** - * Completely empties the table. - */ -template -void WeakKeyHashMap:: -clear() { - if (_table_size != 0) { - for (size_t i = 0; i < _table_size; ++i) { - if (get_exists_array()[i] != 0) { - clear_element(i); - } - } - - _deleted_chain->deallocate(_table, TypeHandle::none()); - _table = nullptr; - _deleted_chain = nullptr; - _table_size = 0; - _num_entries = 0; - } -} - -/** - * Returns a modifiable reference to the data associated with the indicated - * key, or creates a new data entry and returns its reference. - */ -template -INLINE Value &WeakKeyHashMap:: -operator [] (const Key *key) { - int index = find(key); - if (index == -1) { - index = store(key, Value()); - } - return modify_data(index); -} - -/** - * Returns the total number of slots in the table. - */ -template -INLINE size_t WeakKeyHashMap:: -get_size() const { - return _table_size; -} - -/** - * Returns true if there is an element stored in the nth slot, false - * otherwise. - * - * n should be in the range 0 <= n < get_size(). - */ -template -INLINE bool WeakKeyHashMap:: -has_element(size_t n) const { - nassertr(n < _table_size, false); - return (get_exists_array()[n] != 0 && !_table[n]._key.was_deleted()); -} - -/** - * Returns the key in the nth slot of the table. - * - * It is an error to call this if there is nothing stored in the nth slot (use - * has_element() to check this first). n should be in the range 0 <= n < - * get_size(). - */ -template -INLINE const Key *WeakKeyHashMap:: -get_key(size_t n) const { - nassertr(has_element(n), _table[n]._key); - return _table[n]._key; -} - -/** - * Returns the data in the nth slot of the table. - * - * It is an error to call this if there is nothing stored in the nth slot (use - * has_element() to check this first). n should be in the range 0 <= n < - * get_size(). - */ -template -INLINE const Value &WeakKeyHashMap:: -get_data(size_t n) const { - nassertr(has_element(n), _table[n]._data); - return _table[n]._data; -} - -/** - * Returns a modifiable reference to the data in the nth slot of the table. - * - * It is an error to call this if there is nothing stored in the nth slot (use - * has_element() to check this first). n should be in the range 0 <= n < - * get_size(). - */ -template -INLINE Value &WeakKeyHashMap:: -modify_data(size_t n) { - nassertr(has_element(n), _table[n]._data); - return _table[n]._data; -} - -/** - * Changes the data for the nth slot of the table. - * - * It is an error to call this if there is nothing stored in the nth slot (use - * has_element() to check this first). n should be in the range 0 <= n < - * get_size(). - */ -template -INLINE void WeakKeyHashMap:: -set_data(size_t n, const Value &data) { - nassertv(has_element(n)); - _table[n]._data = data; -} - -/** - * Changes the data for the nth slot of the table. - * - * It is an error to call this if there is nothing stored in the nth slot (use - * has_element() to check this first). n should be in the range 0 <= n < - * get_size(). - */ -template -INLINE void WeakKeyHashMap:: -set_data(size_t n, Value &&data) { - nassertv(has_element(n)); - _table[n]._data = std::move(data); -} - -/** - * Removes the nth slot from the table. - * - * It is an error to call this if there is nothing stored in the nth slot (use - * has_element() to check this first). n should be in the range 0 <= n < - * get_size(). - */ -template -void WeakKeyHashMap:: -remove_element(size_t n) { - nassertv(get_exists_array()[n] != 0); - - clear_element(n); - nassertv(_num_entries > 0); - --_num_entries; - - // Now we have put a hole in the table. If there was a hash conflict in the - // slot following this one, we have to move it down to close the hole. - size_t i = n; - i = (i + 1) & (_table_size - 1); - while (get_exists_array()[i] != 0) { - if (_table[i]._key.was_deleted()) { - // It was deleted. Forget about it. - clear_element(i); - --_num_entries; - } else { - size_t wants_index = get_hash(_table[i]._key.get_orig()); - if (wants_index != i) { - // This one was a hash conflict; try to put it where it belongs. We - // can't just put it in n, since maybe it belongs somewhere after n. - while (wants_index != i && has_element(wants_index)) { - // Hash conflict; move it up. - wants_index = (wants_index + 1) & (_table_size - 1); - } - if (wants_index != i) { - store_new_element(wants_index, _table[i]._key, _table[i]._data); - clear_element(i); - } - } - } - - // Continue until we encounter the next unused slot. Until we do, we - // can't be sure we've found all of the potential hash conflicts. - i = (i + 1) & (_table_size - 1); - } - -#ifdef _DEBUG - nassertv(validate()); -#endif -} - -/** - * Returns the number of active entries in the table. This is not necessarily - * related to the number of slots in the table as reported by get_size(). Use - * get_size() to iterate through all of the slots, not get_num_entries(). - * - * This is merely an upper bound on the number of entries; it may also count - * false positives for pointers that were recently deleted. - */ -template -INLINE size_t WeakKeyHashMap:: -get_num_entries() const { - return _num_entries; -} - -/** - * Returns true if the table is empty; i.e. get_num_entries() == 0. This may - * return a false negatives if a pointer was recently deleted; if this returns - * true, though, you can be sure it's empty. - */ -template -INLINE bool WeakKeyHashMap:: -is_empty() const { - return (_num_entries == 0); -} - -/** - * - */ -template -void WeakKeyHashMap:: -output(std::ostream &out) const { - out << "WeakKeyHashMap (" << _num_entries << " entries): ["; - for (size_t i = 0; i < _table_size; ++i) { - if (get_exists_array()[i] == 0) { - out << " *"; - - } else { - out << " " << _table[i]._key; - size_t index = get_hash(_table[i]._key.get_orig()); - if (index != i) { - // This was misplaced as the result of a hash conflict. Report how - // far off it is. - out << "(" << ((_table_size + i - index) & (_table_size - 1)) << ")"; - } - } - } - out << " ]"; -} - -/** - * - */ -template -void WeakKeyHashMap:: -write(std::ostream &out) const { - output(out); - out << "\n"; -} - -/** - * Returns true if the internal table appears to be consistent, false if there - * are some internal errors. - */ -template -bool WeakKeyHashMap:: -validate() const { - size_t count = 0; - - const unsigned char *exists_array = get_exists_array(); - - for (size_t i = 0; i < _table_size; ++i) { - if (exists_array[i] != 0) { - ++count; - if (_table[i]._key.was_deleted()) { - continue; - } - size_t ideal_index = get_hash(_table[i]._key.get_orig()); - size_t wants_index = ideal_index; - while (wants_index != i && exists_array[wants_index] != 0) { - wants_index = (wants_index + 1) & (_table_size - 1); - } - if (wants_index != i) { - util_cat.error() - << "WeakKeyHashMap is invalid: key " << _table[i]._key - << " should be in slot " << wants_index << " instead of " - << i << " (ideal is " << ideal_index << ")\n"; - write(util_cat.error(false)); - return false; - } - } - } - - if (count != _num_entries) { - util_cat.error() - << "WeakKeyHashMap is invalid: reports " << _num_entries - << " entries, actually has " << count << "\n"; - write(util_cat.error(false)); - return false; - } - - return true; -} - -/** - * Computes an appropriate index number to store the given pointer. - */ -template -INLINE size_t WeakKeyHashMap:: -get_hash(const Key *key) const { - /* - // We want a hash constant 0 < k < 1. This one is suggested by Knuth: - static const double hash_constant = (sqrt(5.0) - 1.0) / 2.0; - double f = ((double)(size_t)key * hash_constant); - f -= floor(f); - return (size_t)floor(f * _table_size); - */ - - return (((size_t)key * (size_t)9973) >> 8) & (_table_size - 1); -} - -/** - * Returns true if element n matches key. - */ -template -INLINE bool WeakKeyHashMap:: -is_element(size_t n, const Key *key) const { - nassertr(has_element(n), false); - return _table[n]._key == key; -} - -/** - * Constructs a new TableEntry at position n, storing the indicated key and - * value. - */ -template -INLINE void WeakKeyHashMap:: -store_new_element(size_t n, const Key *key, const Value &data) { - if (get_exists_array()[n] != 0) { - // There was already an element in this spot. This can happen if it was a - // pointer that had already been deleted. - nassertv(_table[n]._key.was_deleted()); - _table[n].~TableEntry(); - --_num_entries; - } - new(&_table[n]) TableEntry(key, data); - get_exists_array()[n] = true; -} - -/** - * Destructs the TableEntry at position n. - */ -template -INLINE void WeakKeyHashMap:: -clear_element(size_t n) { - _table[n].~TableEntry(); - get_exists_array()[n] = false; -} - -/** - * Returns the beginning of the array of _table_size unsigned chars that are - * the boolean flags for whether each element exists (has been constructed) - * within the table. - */ -template -INLINE unsigned char *WeakKeyHashMap:: -get_exists_array() const { - return (unsigned char *)(_table + _table_size); -} - -/** - * Allocates a brand new table. - */ -template -void WeakKeyHashMap:: -new_table() { - nassertv(_table_size == 0 && _num_entries == 0); - - // Pick a good initial table size. For now, we make it really small. Maybe - // that's the right answer. - _table_size = 4; - - // We allocate enough bytes for _table_size elements of TableEntry, plus - // _table_size more bytes at the end (for the exists array). - size_t alloc_size = _table_size * sizeof(TableEntry) + _table_size; - - _deleted_chain = memory_hook->get_deleted_chain(alloc_size); - _table = (TableEntry *)_deleted_chain->allocate(alloc_size, TypeHandle::none()); - memset(get_exists_array(), 0, _table_size); -} - -/** - * Expands the table if it will need it (assuming one more element is about to - * be added). Returns true if the table was modified, false otherwise. - */ -template -INLINE bool WeakKeyHashMap:: -consider_expand_table() { - if (_num_entries >= (_table_size >> 1)) { - // Actually, first, we should see if there are any deleted pointers. - // Clean those up and see how much space we save. - for (size_t i = 0; i < _table_size; ++i) { - if (get_exists_array()[i] != 0 && _table[i]._key.was_deleted()) { - remove_element(i); - } - } - if (_num_entries >= (_table_size >> 1)) { - // Still not enough space. - expand_table(); - } - return true; - } - return false; -} - -/** - * Doubles the size of the existing table. - */ -template -void WeakKeyHashMap:: -expand_table() { - nassertv(_table_size != 0); - - WeakKeyHashMap old_map; - swap(old_map); - - // Double the table size. - size_t old_table_size = old_map._table_size; - _table_size = (old_table_size << 1); - nassertv(_table == nullptr); - - // We allocate enough bytes for _table_size elements of TableEntry, plus - // _table_size more bytes at the end (for the exists array). - size_t alloc_size = _table_size * sizeof(TableEntry) + _table_size; - _deleted_chain = memory_hook->get_deleted_chain(alloc_size); - _table = (TableEntry *)_deleted_chain->allocate(alloc_size, TypeHandle::none()); - unsigned char *exists_array = get_exists_array(); - memset(exists_array, 0, _table_size); - nassertv(_num_entries == 0); - - // Now copy the entries from the old table into the new table. - for (size_t i = 0; i < old_table_size; ++i) { - if (old_map.has_element(i)) { - size_t new_index = get_hash(old_map._table[i]._key.get_orig()); - - while (exists_array[new_index] != 0) { - // Hash conflict; look for a better spot. This has to succeed. - new_index = (new_index + 1) & (_table_size - 1); - } - - // Use C++11 rvalue references to invoke the move constructor, which may - // be more efficient. - new(&_table[new_index]) TableEntry(std::move(old_map._table[i])); - exists_array[new_index] = true; - ++_num_entries; - } - } - - nassertv(validate()); - nassertv(old_map.validate()); - - // Note that since has_element(i) also checks whether the pointer has been - // deleted, we may end up with fewer entries than we started with. Good - // riddance. - nassertv(_num_entries <= old_map._num_entries); -} diff --git a/panda/src/putil/weakKeyHashMap.h b/panda/src/putil/weakKeyHashMap.h deleted file mode 100644 index 8e51ae700f..0000000000 --- a/panda/src/putil/weakKeyHashMap.h +++ /dev/null @@ -1,108 +0,0 @@ -/** - * 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 weakKeyHashMap.h - * @author rdb - * @date 2015-07-13 - */ - -#ifndef WEAKKEYHASHMAP_H -#define WEAKKEYHASHMAP_H - -#include "pandabase.h" -#include "pvector.h" -#include "config_putil.h" -#include "weakPointerTo.h" - -/** - * This is a variation on WeakKeyHashMap that stores weak pointers as keys, - * and automatically frees up entries from the map when the associated key has - * been deleted. - * - * This is more efficient than using a naive map of WeakPointerTo keys since - * that would incur the cost of constructing a weak reference every time a - * find operation is used. - */ -template -class WeakKeyHashMap { -public: -#ifndef CPPPARSER - INLINE WeakKeyHashMap(); - INLINE ~WeakKeyHashMap(); - - INLINE void swap(WeakKeyHashMap &other); - - int find(const Key *key) const; - int store(const Key *key, const Value &data); - INLINE bool remove(const Key *key); - void clear(); - - INLINE Value &operator [] (const Key *key); - - INLINE size_t get_size() const; - INLINE bool has_element(size_t n) const; - INLINE const Key *get_key(size_t n) const; - INLINE const Value &get_data(size_t n) const; - INLINE Value &modify_data(size_t n); - INLINE void set_data(size_t n, const Value &data); - INLINE void set_data(size_t n, Value &&data); - void remove_element(size_t n); - - INLINE size_t get_num_entries() const; - INLINE bool is_empty() const; - - void output(std::ostream &out) const; - void write(std::ostream &out) const; - bool validate() const; - -private: - INLINE size_t get_hash(const Key *key) const; - - INLINE bool is_element(size_t n, const Key *key) const; - INLINE void store_new_element(size_t n, const Key *key, const Value &data); - INLINE void clear_element(size_t n); - INLINE unsigned char *get_exists_array() const; - - void new_table(); - INLINE bool consider_expand_table(); - void expand_table(); - - class TableEntry { - public: - INLINE TableEntry(const Key *key, const Value &data) : - _key(key), - _data(data) {} - INLINE TableEntry(const TableEntry ©) : - _key(copy._key), - _data(copy._data) {} - INLINE TableEntry(TableEntry &&from) noexcept : - _key(std::move(from._key)), - _data(std::move(from._data)) {} - - WCPT(Key) _key; - Value _data; - }; - - TableEntry *_table; - DeletedBufferChain *_deleted_chain; - size_t _table_size; - size_t _num_entries; -#endif // CPPPARSER -}; - -template -inline std::ostream &operator << (std::ostream &out, const WeakKeyHashMap &shm) { - shm.output(out); - return out; -} - -#ifndef CPPPARSER -#include "weakKeyHashMap.I" -#endif // CPPPARSER - -#endif diff --git a/panda/src/tform/mouseWatcher.I b/panda/src/tform/mouseWatcher.I index f47c07e6e2..2b52834551 100644 --- a/panda/src/tform/mouseWatcher.I +++ b/panda/src/tform/mouseWatcher.I @@ -542,7 +542,7 @@ get_trail_log() const { * frame. The trail log is updated once per frame, during the process_events * operation. */ -INLINE int MouseWatcher:: +INLINE size_t MouseWatcher:: num_trail_recent() const { return _num_trail_recent; } diff --git a/panda/src/tform/mouseWatcher.cxx b/panda/src/tform/mouseWatcher.cxx index c4b5c0a6e7..9344d51d7d 100644 --- a/panda/src/tform/mouseWatcher.cxx +++ b/panda/src/tform/mouseWatcher.cxx @@ -1326,7 +1326,7 @@ do_transmit_data(DataGraphTraverser *trav, const DataNodeTransmit &input, const PointerEventList *this_pointer_events; DCAST_INTO_V(this_pointer_events, input.get_data(_pointer_events_input).get_ptr()); _num_trail_recent = this_pointer_events->get_num_events(); - for (int i = 0; i < _num_trail_recent; i++) { + for (size_t i = 0; i < _num_trail_recent; i++) { bool in_win = this_pointer_events->get_in_window(i); int xpos = this_pointer_events->get_xpos(i); int ypos = this_pointer_events->get_ypos(i); diff --git a/panda/src/tform/mouseWatcher.h b/panda/src/tform/mouseWatcher.h index 9f21f3d09b..f81da04ba7 100644 --- a/panda/src/tform/mouseWatcher.h +++ b/panda/src/tform/mouseWatcher.h @@ -138,7 +138,7 @@ PUBLISHED: INLINE const std::string &get_inactivity_timeout_event() const; INLINE CPT(PointerEventList) get_trail_log() const; - INLINE int num_trail_recent() const; + INLINE size_t num_trail_recent() const; void set_trail_log_duration(double duration); PT(GeomNode) get_trail_node(); void clear_trail_node(); @@ -220,7 +220,7 @@ private: LVecBase4 _frame; PT(PointerEventList) _trail_log; - int _num_trail_recent; + size_t _num_trail_recent; double _trail_log_duration; PT(GeomNode) _trail_node; diff --git a/panda/src/tform/mouseWatcherRegion.I b/panda/src/tform/mouseWatcherRegion.I index 50dfada487..91ccbc7063 100644 --- a/panda/src/tform/mouseWatcherRegion.I +++ b/panda/src/tform/mouseWatcherRegion.I @@ -17,11 +17,11 @@ INLINE MouseWatcherRegion:: MouseWatcherRegion(const std::string &name, PN_stdfloat left, PN_stdfloat right, PN_stdfloat bottom, PN_stdfloat top) : - Namable(name), - _frame(left, right, bottom, top) + Namable(name) { _sort = 0; _flags = F_active; + set_frame(left, right, bottom, top); } /** diff --git a/panda/src/tform/mouseWatcherRegion.h b/panda/src/tform/mouseWatcherRegion.h index a49cfcbc6e..c3e668bf34 100644 --- a/panda/src/tform/mouseWatcherRegion.h +++ b/panda/src/tform/mouseWatcherRegion.h @@ -61,6 +61,15 @@ PUBLISHED: void output(std::ostream &out) const; void write(std::ostream &out, int indent_level = 0) const; +PUBLISHED: + MAKE_PROPERTY(frame, get_frame, set_frame); + MAKE_PROPERTY(area, get_area); + + MAKE_PROPERTY(sort, get_sort, set_sort); + MAKE_PROPERTY(active, get_active, set_active); + MAKE_PROPERTY(keyboard, get_keyboard, set_keyboard); + MAKE_PROPERTY(suppress_flags, get_suppress_flags, set_suppress_flags); + public: INLINE bool operator < (const MouseWatcherRegion &other) const; diff --git a/panda/src/tinydisplay/tinyOsxGraphicsWindow.mm b/panda/src/tinydisplay/tinyOsxGraphicsWindow.mm index 01892094b0..d1df628415 100644 --- a/panda/src/tinydisplay/tinyOsxGraphicsWindow.mm +++ b/panda/src/tinydisplay/tinyOsxGraphicsWindow.mm @@ -397,7 +397,7 @@ OSStatus TinyOsxGraphicsWindow::handleTextInput (EventHandlerCallRef myHandler, } for (unsigned int x = 0; x < actualSize/sizeof(UniChar); ++x) { - _input_devices[0].keystroke(text[x]); + _input_devices[0]->keystroke(text[x]); } DisposePtr((char *)text); } @@ -466,10 +466,10 @@ TinyOsxGraphicsWindow::TinyOsxGraphicsWindow(GraphicsEngine *engine, GraphicsPip _current_icon(NULL), _ID(id_seed++), _originalMode(NULL) { - GraphicsWindowInputDevice device = + PT(InputDevice) device = GraphicsWindowInputDevice::pointer_and_keyboard(this, "keyboard/mouse"); _input_devices.push_back(device); - _input_devices[0].set_pointer_in_window(0, 0); + device->set_pointer_in_window(0, 0); _last_key_modifiers = 0; _last_buttons = 0; @@ -545,7 +545,7 @@ bool TinyOsxGraphicsWindow::set_icon_filename(const Filename &icon_filename) { */ void TinyOsxGraphicsWindow:: set_pointer_in_window(int x, int y) { - _input_devices[0].set_pointer_in_window(x, y); + _input_devices[0]->set_pointer_in_window(x, y); if (_cursor_hidden != _display_hide_cursor) { if (_cursor_hidden) { @@ -563,7 +563,7 @@ set_pointer_in_window(int x, int y) { */ void TinyOsxGraphicsWindow:: set_pointer_out_of_window() { - _input_devices[0].set_pointer_out_of_window(); + _input_devices[0]->set_pointer_out_of_window(); if (_display_hide_cursor) { CGDisplayShowCursor(kCGDirectMainDisplay); @@ -1253,19 +1253,19 @@ void TinyOsxGraphicsWindow::SystemPointToLocalPoint(Point &qdGlobalPoint) { SystemPointToLocalPoint(qdGlobalPoint); if (wheelAxis == kEventMouseWheelAxisY) { - set_pointer_in_window((int)qdGlobalPoint.h, (int)qdGlobalPoint.v); - _wheel_delta += this_wheel_delta; - SInt32 wheel_scale = osx_mouse_wheel_scale; - while (_wheel_delta > wheel_scale) { - _input_devices[0].button_down(MouseButton::wheel_up()); - _input_devices[0].button_up(MouseButton::wheel_up()); - _wheel_delta -= wheel_scale; - } - while (_wheel_delta < -wheel_scale) { - _input_devices[0].button_down(MouseButton::wheel_down()); - _input_devices[0].button_up(MouseButton::wheel_down()); - _wheel_delta += wheel_scale; - } + set_pointer_in_window((int)qdGlobalPoint.h, (int)qdGlobalPoint.v); + _wheel_delta += this_wheel_delta; + SInt32 wheel_scale = osx_mouse_wheel_scale; + while (_wheel_delta > wheel_scale) { + _input_devices[0]->button_down(MouseButton::wheel_up()); + _input_devices[0]->button_up(MouseButton::wheel_up()); + _wheel_delta -= wheel_scale; + } + while (_wheel_delta < -wheel_scale) { + _input_devices[0]->button_down(MouseButton::wheel_down()); + _input_devices[0]->button_up(MouseButton::wheel_down()); + _wheel_delta += wheel_scale; + } } result = noErr; break; @@ -1438,25 +1438,25 @@ HandleButtonDelta(UInt32 new_buttons) { if (changed & 0x01) { if (new_buttons & 0x01) { - _input_devices[0].button_down(MouseButton::one()); + _input_devices[0]->button_down(MouseButton::one()); } else { - _input_devices[0].button_up(MouseButton::one()); + _input_devices[0]->button_up(MouseButton::one()); } } if (changed & 0x04) { if (new_buttons & 0x04) { - _input_devices[0].button_down(MouseButton::two()); + _input_devices[0]->button_down(MouseButton::two()); } else { - _input_devices[0].button_up(MouseButton::two()); + _input_devices[0]->button_up(MouseButton::two()); } } if (changed & 0x02) { if (new_buttons & 0x02) { - _input_devices[0].button_down(MouseButton::three()); + _input_devices[0]->button_down(MouseButton::three()); } else { - _input_devices[0].button_up(MouseButton::three()); + _input_devices[0]->button_up(MouseButton::three()); } } @@ -1657,7 +1657,7 @@ void TinyOsxGraphicsWindow::set_properties_now(WindowProperties &properties) { if (properties.has_cursor_hidden()) { _properties.set_cursor_hidden(properties.get_cursor_hidden()); _cursor_hidden = properties.get_cursor_hidden(); - if (_cursor_hidden && _input_devices[0].has_pointer()) { + if (_cursor_hidden && _input_devices[0]->has_pointer()) { if (!_display_hide_cursor) { CGDisplayHideCursor(kCGDirectMainDisplay); _display_hide_cursor = true; diff --git a/panda/src/tinydisplay/tinySDLGraphicsWindow.cxx b/panda/src/tinydisplay/tinySDLGraphicsWindow.cxx index 640adf3d5d..9c221846eb 100644 --- a/panda/src/tinydisplay/tinySDLGraphicsWindow.cxx +++ b/panda/src/tinydisplay/tinySDLGraphicsWindow.cxx @@ -43,9 +43,7 @@ TinySDLGraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe, _pitch = 0; update_pixel_factor(); - GraphicsWindowInputDevice device = - GraphicsWindowInputDevice::pointer_and_keyboard(this, "keyboard_mouse"); - add_input_device(device); + add_input_device(GraphicsWindowInputDevice::pointer_and_keyboard(this, "keyboard_mouse")); } /** @@ -168,35 +166,35 @@ process_events() { switch(evt.type) { case SDL_KEYDOWN: if (evt.key.keysym.unicode) { - _input_devices[0].keystroke(evt.key.keysym.unicode); + _input_devices[0]->keystroke(evt.key.keysym.unicode); } button = get_keyboard_button(evt.key.keysym.sym); if (button != ButtonHandle::none()) { - _input_devices[0].button_down(button); + _input_devices[0]->button_down(button); } break; case SDL_KEYUP: button = get_keyboard_button(evt.key.keysym.sym); if (button != ButtonHandle::none()) { - _input_devices[0].button_up(button); + _input_devices[0]->button_up(button); } break; case SDL_MOUSEBUTTONDOWN: button = get_mouse_button(evt.button.button); - _input_devices[0].set_pointer_in_window(evt.button.x, evt.button.y); - _input_devices[0].button_down(button); + _input_devices[0]->set_pointer_in_window(evt.button.x, evt.button.y); + _input_devices[0]->button_down(button); break; case SDL_MOUSEBUTTONUP: button = get_mouse_button(evt.button.button); - _input_devices[0].set_pointer_in_window(evt.button.x, evt.button.y); - _input_devices[0].button_up(button); + _input_devices[0]->set_pointer_in_window(evt.button.x, evt.button.y); + _input_devices[0]->button_up(button); break; case SDL_MOUSEMOTION: - _input_devices[0].set_pointer_in_window(evt.motion.x, evt.motion.y); + _input_devices[0]->set_pointer_in_window(evt.motion.x, evt.motion.y); break; case SDL_VIDEORESIZE: diff --git a/panda/src/tinydisplay/tinyXGraphicsWindow.cxx b/panda/src/tinydisplay/tinyXGraphicsWindow.cxx index e84fa4d834..1e71324baf 100644 --- a/panda/src/tinydisplay/tinyXGraphicsWindow.cxx +++ b/panda/src/tinydisplay/tinyXGraphicsWindow.cxx @@ -195,8 +195,6 @@ process_events() { return; } - poll_raw_mice(); - XEvent event; XKeyEvent keyrelease_event; bool got_keyrelease_event = false; @@ -271,18 +269,18 @@ process_events() { case ButtonPress: // This refers to the mouse buttons. button = get_mouse_button(event.xbutton); - _input_devices[0].set_pointer_in_window(event.xbutton.x, event.xbutton.y); - _input_devices[0].button_down(button); + _input->set_pointer_in_window(event.xbutton.x, event.xbutton.y); + _input->button_down(button); break; case ButtonRelease: button = get_mouse_button(event.xbutton); - _input_devices[0].set_pointer_in_window(event.xbutton.x, event.xbutton.y); - _input_devices[0].button_up(button); + _input->set_pointer_in_window(event.xbutton.x, event.xbutton.y); + _input->button_up(button); break; case MotionNotify: - _input_devices[0].set_pointer_in_window(event.xmotion.x, event.xmotion.y); + _input->set_pointer_in_window(event.xmotion.x, event.xmotion.y); break; case KeyPress: @@ -299,11 +297,11 @@ process_events() { break; case EnterNotify: - _input_devices[0].set_pointer_in_window(event.xcrossing.x, event.xcrossing.y); + _input->set_pointer_in_window(event.xcrossing.x, event.xcrossing.y); break; case LeaveNotify: - _input_devices[0].set_pointer_out_of_window(); + _input->set_pointer_out_of_window(); break; case FocusIn: @@ -312,7 +310,7 @@ process_events() { break; case FocusOut: - _input_devices[0].focus_lost(); + _input->focus_lost(); properties.set_foreground(false); system_changed_properties(properties); break; diff --git a/panda/src/vrpn/vrpnAnalog.cxx b/panda/src/vrpn/vrpnAnalog.cxx index d0d1b773ca..2745aab75e 100644 --- a/panda/src/vrpn/vrpnAnalog.cxx +++ b/panda/src/vrpn/vrpnAnalog.cxx @@ -99,16 +99,8 @@ vrpn_analog_callback(void *userdata, const vrpn_ANALOGCB info) { Devices::iterator di; for (di = self->_devices.begin(); di != self->_devices.end(); ++di) { VrpnAnalogDevice *device = (*di); - device->acquire(); for (int i = 0; i < info.num_channel; i++) { - if (vrpn_cat.is_debug()) { - if (device->get_control_state(i) != info.channel[i]) { - vrpn_cat.debug() - << *self << " got analog " << i << " = " << info.channel[i] << "\n"; - } - } - device->set_control_state(i, info.channel[i]); + device->set_axis_value(i, info.channel[i]); } - device->unlock(); } } diff --git a/panda/src/vrpn/vrpnButton.cxx b/panda/src/vrpn/vrpnButton.cxx index 71af239f88..3312afa31d 100644 --- a/panda/src/vrpn/vrpnButton.cxx +++ b/panda/src/vrpn/vrpnButton.cxx @@ -103,8 +103,6 @@ vrpn_button_callback(void *userdata, const vrpn_BUTTONCB info) { Devices::iterator di; for (di = self->_devices.begin(); di != self->_devices.end(); ++di) { VrpnButtonDevice *device = (*di); - device->acquire(); - device->set_button_state(info.button, info.state != 0); - device->unlock(); + device->button_changed(info.button, info.state != 0); } } diff --git a/panda/src/vrpn/vrpnDial.cxx b/panda/src/vrpn/vrpnDial.cxx index 5c06f797ee..62b1fe73df 100644 --- a/panda/src/vrpn/vrpnDial.cxx +++ b/panda/src/vrpn/vrpnDial.cxx @@ -104,8 +104,6 @@ vrpn_dial_callback(void *userdata, const vrpn_DIALCB info) { Devices::iterator di; for (di = self->_devices.begin(); di != self->_devices.end(); ++di) { VrpnDialDevice *device = (*di); - device->acquire(); device->push_dial(info.dial, info.change); - device->unlock(); } } diff --git a/panda/src/vrpn/vrpnTracker.cxx b/panda/src/vrpn/vrpnTracker.cxx index 6e4e196769..e1b08e2a72 100644 --- a/panda/src/vrpn/vrpnTracker.cxx +++ b/panda/src/vrpn/vrpnTracker.cxx @@ -107,11 +107,10 @@ vrpn_position_callback(void *userdata, const vrpn_TRACKERCB info) { VrpnTrackerDevice *device = (*di); if (device->get_sensor() == info.sensor && device->get_data_type() == VrpnTrackerDevice::DT_position) { - device->acquire(); - device->_data.set_time(VrpnClient::convert_to_secs(info.msg_time)); - device->_data.set_pos(LPoint3(info.pos[0], info.pos[1], info.pos[2])); - device->_data.set_orient(LOrientation(info.quat[3], info.quat[0], info.quat[1], info.quat[2])); - device->unlock(); + device->tracker_changed(LPoint3(info.pos[0], info.pos[1], info.pos[2]), + LOrientation(info.quat[3], info.quat[0], + info.quat[1], info.quat[2]), + VrpnClient::convert_to_secs(info.msg_time)); } } } @@ -133,13 +132,10 @@ vrpn_velocity_callback(void *userdata, const vrpn_TRACKERVELCB info) { VrpnTrackerDevice *device = (*di); if (device->get_sensor() == info.sensor && device->get_data_type() == VrpnTrackerDevice::DT_velocity) { - device->acquire(); - device->_data.set_time(VrpnClient::convert_to_secs(info.msg_time)); - device->_data.set_pos(LPoint3(info.vel[0], info.vel[1], info.vel[2])); - device->_data.set_orient(LOrientation(info.vel_quat[3], info.vel_quat[0], - info.vel_quat[1], info.vel_quat[2])); - device->_data.set_dt(info.vel_quat_dt); - device->unlock(); + device->tracker_changed(LPoint3(info.vel[0], info.vel[1], info.vel[2]), + LOrientation(info.vel_quat[3], info.vel_quat[0], + info.vel_quat[1], info.vel_quat[2]), + VrpnClient::convert_to_secs(info.msg_time)); } } } @@ -161,13 +157,10 @@ vrpn_acceleration_callback(void *userdata, const vrpn_TRACKERACCCB info) { VrpnTrackerDevice *device = (*di); if (device->get_sensor() == info.sensor && device->get_data_type() == VrpnTrackerDevice::DT_acceleration) { - device->acquire(); - device->_data.set_time(VrpnClient::convert_to_secs(info.msg_time)); - device->_data.set_pos(LPoint3(info.acc[0], info.acc[1], info.acc[2])); - device->_data.set_orient(LOrientation(info.acc_quat[3], info.acc_quat[0], - info.acc_quat[1], info.acc_quat[2])); - device->_data.set_dt(info.acc_quat_dt); - device->unlock(); + device->tracker_changed(LPoint3(info.acc[0], info.acc[1], info.acc[2]), + LOrientation(info.acc_quat[3], info.acc_quat[0], + info.acc_quat[1], info.acc_quat[2]), + VrpnClient::convert_to_secs(info.msg_time)); } } } diff --git a/panda/src/windisplay/winDetectDx.h b/panda/src/windisplay/winDetectDx.h index df7d89f0ff..4b979de6f0 100644 --- a/panda/src/windisplay/winDetectDx.h +++ b/panda/src/windisplay/winDetectDx.h @@ -21,18 +21,6 @@ typedef struct { } DISPLAY_FORMAT; -typedef union -{ - struct - { - unsigned int day : 8; - unsigned int month : 8; - unsigned int year : 16; - }; - DWORD whql; -} -WHQL; - static DISPLAY_FORMAT display_format_array [ ] = { D3DFMT_X8R8G8B8, 32, FALSE, D3DFMT_R5G6B5, 16, FALSE, @@ -321,14 +309,6 @@ static int get_display_information (DisplaySearchParameters &display_search_para if (debug) { printf ("DRIVER VERSION: %d.%d.%d.%d \n", product, version, sub_version, build); } - - WHQL whql; - - whql.whql= d3d_adapter_identifier.WHQLLevel; - - if (debug) { - printf ("WHQL: %d %d %d \n", whql.day, whql.day, whql.year); - } } if (direct_3d -> GetDeviceCaps (adapter, device_type, &d3d_caps) == D3D_OK) { diff --git a/panda/src/windisplay/winGraphicsWindow.cxx b/panda/src/windisplay/winGraphicsWindow.cxx index 703aba0149..195189ac9e 100644 --- a/panda/src/windisplay/winGraphicsWindow.cxx +++ b/panda/src/windisplay/winGraphicsWindow.cxx @@ -130,7 +130,7 @@ get_pointer(int device) const { LightMutexHolder holder(_input_lock); nassertr(device >= 0 && device < (int)_input_devices.size(), MouseData()); - result = _input_devices[device].get_pointer(); + result = ((const GraphicsWindowInputDevice *)_input_devices[device].p())->get_pointer(); // We recheck this immediately to get the most up-to-date value. POINT cpos; @@ -138,7 +138,7 @@ get_pointer(int device) const { double time = ClockObject::get_global_clock()->get_real_time(); result._xpos = cpos.x; result._ypos = cpos.y; - ((GraphicsWindowInputDevice &)_input_devices[0]).set_pointer(result._in_window, result._xpos, result._ypos, time); + ((GraphicsWindowInputDevice *)_input_devices[0].p())->set_pointer_in_window(result._xpos, result._ypos, time); } } return result; @@ -164,7 +164,7 @@ move_pointer(int device, int x, int y) { if (device == 0) { // Move the system mouse pointer. if (!_properties.get_foreground() ) - // !_input_devices[0].get_pointer().get_in_window()) + // !_input->get_pointer().get_in_window()) { // If the window doesn't have input focus, or the mouse isn't // currently within the window, forget it. @@ -175,14 +175,14 @@ move_pointer(int device, int x, int y) { get_client_rect_screen(_hWnd, &view_rect); SetCursorPos(view_rect.left + x, view_rect.top + y); - _input_devices[0].set_pointer_in_window(x, y); + _input->set_pointer_in_window(x, y); return true; } else { // Move a raw mouse. if ((device < 1)||(device >= (int)_input_devices.size())) { return false; } - _input_devices[device].set_pointer_in_window(x, y); + //_input_devices[device].set_pointer_in_window(x, y); return true; } } @@ -626,7 +626,6 @@ open_window() { * initializes a parallel array, _input_device_handle, with the win32 handle * of each raw input device. */ - void WinGraphicsWindow:: initialize_input_devices() { UINT nInputDevices; @@ -636,9 +635,10 @@ initialize_input_devices() { // Clear the handle array, and set up the system keyboardmouse memset(_input_device_handle, 0, sizeof(_input_device_handle)); - GraphicsWindowInputDevice device = + PT(GraphicsWindowInputDevice) device = GraphicsWindowInputDevice::pointer_and_keyboard(this, "keyboard_mouse"); add_input_device(device); + _input = device; // Get the number of devices. if (GetRawInputDeviceList(nullptr, &nInputDevices, sizeof(RAWINPUTDEVICELIST)) != 0) { @@ -679,14 +679,15 @@ initialize_input_devices() { char *pound3 = pound2 ? strchr(pound2+1,'#') : 0; if (pound3) *pound3 = 0; for (char *p = psName; *p; p++) { - if (((*p<'a')||(*p>'z')) && ((*p<'A')||(*p>'Z')) && ((*p<'0')||(*p>'9'))) { + if (!isalnum(*p)) { *p = '_'; } } if (pound2) *pound2 = '.'; _input_device_handle[_input_devices.size()] = pRawInputDeviceList[i].hDevice; - GraphicsWindowInputDevice device = GraphicsWindowInputDevice::pointer_only(this, psName); - device.set_pointer_in_window(0,0); + + PT(GraphicsWindowInputDevice) device = GraphicsWindowInputDevice::pointer_only(this, psName); + device->set_pointer_in_window(0, 0); add_input_device(device); } } @@ -1547,8 +1548,8 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { resend_lost_keypresses(); } SetCapture(hwnd); - _input_devices[0].set_pointer_in_window(translate_mouse(LOWORD(lparam)), translate_mouse(HIWORD(lparam))); - _input_devices[0].button_down(MouseButton::button(0), get_message_time()); + _input->set_pointer_in_window(translate_mouse(LOWORD(lparam)), translate_mouse(HIWORD(lparam))); + _input->button_down(MouseButton::button(0), get_message_time()); // A button-click in the window means to grab the keyboard focus. set_focus(); @@ -1559,8 +1560,8 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { resend_lost_keypresses(); } SetCapture(hwnd); - _input_devices[0].set_pointer_in_window(translate_mouse(LOWORD(lparam)), translate_mouse(HIWORD(lparam))); - _input_devices[0].button_down(MouseButton::button(1), get_message_time()); + _input->set_pointer_in_window(translate_mouse(LOWORD(lparam)), translate_mouse(HIWORD(lparam))); + _input->button_down(MouseButton::button(1), get_message_time()); // A button-click in the window means to grab the keyboard focus. set_focus(); return 0; @@ -1570,8 +1571,8 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { resend_lost_keypresses(); } SetCapture(hwnd); - _input_devices[0].set_pointer_in_window(translate_mouse(LOWORD(lparam)), translate_mouse(HIWORD(lparam))); - _input_devices[0].button_down(MouseButton::button(2), get_message_time()); + _input->set_pointer_in_window(translate_mouse(LOWORD(lparam)), translate_mouse(HIWORD(lparam))); + _input->button_down(MouseButton::button(2), get_message_time()); // A button-click in the window means to grab the keyboard focus. set_focus(); return 0; @@ -1583,11 +1584,11 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { } SetCapture(hwnd); int whichButton = GET_XBUTTON_WPARAM(wparam); - _input_devices[0].set_pointer_in_window(translate_mouse(LOWORD(lparam)), translate_mouse(HIWORD(lparam))); + _input->set_pointer_in_window(translate_mouse(LOWORD(lparam)), translate_mouse(HIWORD(lparam))); if (whichButton == XBUTTON1) { - _input_devices[0].button_down(MouseButton::button(3), get_message_time()); + _input->button_down(MouseButton::button(3), get_message_time()); } else if (whichButton == XBUTTON2) { - _input_devices[0].button_down(MouseButton::button(4), get_message_time()); + _input->button_down(MouseButton::button(4), get_message_time()); } } return 0; @@ -1597,7 +1598,7 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { resend_lost_keypresses(); } ReleaseCapture(); - _input_devices[0].button_up(MouseButton::button(0), get_message_time()); + _input->button_up(MouseButton::button(0), get_message_time()); return 0; case WM_MBUTTONUP: @@ -1605,7 +1606,7 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { resend_lost_keypresses(); } ReleaseCapture(); - _input_devices[0].button_up(MouseButton::button(1), get_message_time()); + _input->button_up(MouseButton::button(1), get_message_time()); return 0; case WM_RBUTTONUP: @@ -1613,7 +1614,7 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { resend_lost_keypresses(); } ReleaseCapture(); - _input_devices[0].button_up(MouseButton::button(2), get_message_time()); + _input->button_up(MouseButton::button(2), get_message_time()); return 0; case WM_XBUTTONUP: @@ -1624,9 +1625,9 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { ReleaseCapture(); int whichButton = GET_XBUTTON_WPARAM(wparam); if (whichButton == XBUTTON1) { - _input_devices[0].button_up(MouseButton::button(3), get_message_time()); + _input->button_up(MouseButton::button(3), get_message_time()); } else if (whichButton == XBUTTON2) { - _input_devices[0].button_up(MouseButton::button(4), get_message_time()); + _input->button_up(MouseButton::button(4), get_message_time()); } } return 0; @@ -1746,7 +1747,7 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { if (ime_aware) { wstring ws; - _input_devices[0].candidate(ws, 0, 0, 0); + _input->candidate(ws, 0, 0, 0); } break; @@ -1777,7 +1778,7 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { ime_buffer, ime_buffer_size_bytes); size_t num_chars = result_size / sizeof(wchar_t); for (size_t i = 0; i < num_chars; ++i) { - _input_devices[0].keystroke(ime_buffer[i]); + _input->keystroke(ime_buffer[i]); } } @@ -1790,10 +1791,10 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { result_size = ImmGetCompositionStringW(hIMC, GCS_COMPSTR, ime_buffer, ime_buffer_size); size_t num_chars = result_size / sizeof(wchar_t); - _input_devices[0].candidate(wstring(ime_buffer, num_chars), - std::min(cursor_pos, delta_start), - std::max(cursor_pos, delta_start), - cursor_pos); + _input->candidate(wstring(ime_buffer, num_chars), + std::min(cursor_pos, delta_start), + std::max(cursor_pos, delta_start), + cursor_pos); } ImmReleaseContext(hwnd, hIMC); } @@ -1811,7 +1812,7 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { // are using RegisterClassW etc., which means WM_CHAR is absolutely // supposed to be utf-16. if (!_ime_open) { - _input_devices[0].keystroke(wparam); + _input->keystroke(wparam); } break; @@ -1937,8 +1938,8 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { lptstr = (char *) GlobalLock(hglb); if (lptstr != nullptr) { char *pChar; - for (pChar=lptstr; *pChar; pChar++) { - _input_devices[0].keystroke((uchar)*pChar); + for (pChar = lptstr; *pChar; pChar++) { + _input->keystroke((uchar)*pChar); } GlobalUnlock(hglb); } @@ -2059,7 +2060,7 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { << "killfocus\n"; } - _input_devices[0].focus_lost(get_message_time()); + _input->focus_lost(get_message_time()); properties.set_foreground(false); system_changed_properties(properties); break; @@ -2371,9 +2372,9 @@ show_error_message(DWORD message_id) { */ void WinGraphicsWindow:: handle_keypress(ButtonHandle key, int x, int y, double time) { - _input_devices[0].set_pointer_in_window(x, y); + _input->set_pointer_in_window(x, y); if (key != ButtonHandle::none()) { - _input_devices[0].button_down(key, time); + _input->button_down(key, time); } } @@ -2384,7 +2385,7 @@ handle_keypress(ButtonHandle key, int x, int y, double time) { void WinGraphicsWindow:: handle_keyresume(ButtonHandle key, double time) { if (key != ButtonHandle::none()) { - _input_devices[0].button_resume_down(key, time); + _input->button_resume_down(key, time); } } @@ -2394,7 +2395,7 @@ handle_keyresume(ButtonHandle key, double time) { void WinGraphicsWindow:: handle_keyrelease(ButtonHandle key, double time) { if (key != ButtonHandle::none()) { - _input_devices[0].button_up(key, time); + _input->button_up(key, time); } } @@ -2404,7 +2405,7 @@ handle_keyrelease(ButtonHandle key, double time) { void WinGraphicsWindow:: handle_raw_keypress(ButtonHandle key, double time) { if (key != ButtonHandle::none()) { - _input_devices[0].raw_button_down(key, time); + _input->raw_button_down(key, time); } } @@ -2414,7 +2415,7 @@ handle_raw_keypress(ButtonHandle key, double time) { void WinGraphicsWindow:: handle_raw_keyrelease(ButtonHandle key, double time) { if (key != ButtonHandle::none()) { - _input_devices[0].raw_button_up(key, time); + _input->raw_button_up(key, time); } } @@ -2721,48 +2722,49 @@ handle_raw_input(HRAWINPUT hraw) { return; } - for (int i = 1; i < (int)(_input_devices.size()); ++i) { + for (size_t i = 1; i < _input_devices.size(); ++i) { if (_input_device_handle[i] == raw->header.hDevice) { + PT(GraphicsWindowInputDevice) input = + DCAST(GraphicsWindowInputDevice, _input_devices[i]); + int adjx = raw->data.mouse.lLastX; int adjy = raw->data.mouse.lLastY; if (raw->data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) { - _input_devices[i].set_pointer_in_window(adjx, adjy); + input->set_pointer_in_window(adjx, adjy); } else { - int oldx = _input_devices[i].get_raw_pointer().get_x(); - int oldy = _input_devices[i].get_raw_pointer().get_y(); - _input_devices[i].set_pointer_in_window(oldx + adjx, oldy + adjy); + input->pointer_moved(adjx, adjy); } if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_DOWN) { - _input_devices[i].button_down(MouseButton::button(0), get_message_time()); + input->button_down(MouseButton::button(0), get_message_time()); } if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_UP) { - _input_devices[i].button_up(MouseButton::button(0), get_message_time()); + input->button_up(MouseButton::button(0), get_message_time()); } if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_DOWN) { - _input_devices[i].button_down(MouseButton::button(2), get_message_time()); + input->button_down(MouseButton::button(2), get_message_time()); } if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_UP) { - _input_devices[i].button_up(MouseButton::button(2), get_message_time()); + input->button_up(MouseButton::button(2), get_message_time()); } if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_DOWN) { - _input_devices[i].button_down(MouseButton::button(1), get_message_time()); + input->button_down(MouseButton::button(1), get_message_time()); } if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_UP) { - _input_devices[i].button_up(MouseButton::button(1), get_message_time()); + input->button_up(MouseButton::button(1), get_message_time()); } if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_DOWN) { - _input_devices[i].button_down(MouseButton::button(3), get_message_time()); + input->button_down(MouseButton::button(3), get_message_time()); } if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_UP) { - _input_devices[i].button_up(MouseButton::button(3), get_message_time()); + input->button_up(MouseButton::button(3), get_message_time()); } if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_DOWN) { - _input_devices[i].button_down(MouseButton::button(4), get_message_time()); + input->button_down(MouseButton::button(4), get_message_time()); } if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_UP) { - _input_devices[i].button_up(MouseButton::button(4), get_message_time()); + input->button_up(MouseButton::button(4), get_message_time()); } } } @@ -2773,7 +2775,7 @@ handle_raw_input(HRAWINPUT hraw) { */ bool WinGraphicsWindow:: handle_mouse_motion(int x, int y) { - _input_devices[0].set_pointer_in_window(x, y); + _input->set_pointer_in_window(x, y); return false; } @@ -2783,7 +2785,7 @@ handle_mouse_motion(int x, int y) { void WinGraphicsWindow:: handle_mouse_exit() { // note: 'mouse_motion' is considered the 'entry' event - _input_devices[0].set_pointer_out_of_window(); + _input->set_pointer_out_of_window(); } /** diff --git a/panda/src/windisplay/winGraphicsWindow.h b/panda/src/windisplay/winGraphicsWindow.h index 5b0bb208c1..841e2b9854 100644 --- a/panda/src/windisplay/winGraphicsWindow.h +++ b/panda/src/windisplay/winGraphicsWindow.h @@ -189,6 +189,8 @@ private: bool _lalt_down; bool _ralt_down; + GraphicsWindowInputDevice *_input; + // following adds support platform specfic window processing functions. typedef pset WinProcClasses; WinProcClasses _window_proc_classes; diff --git a/panda/src/x11display/x11GraphicsPipe.cxx b/panda/src/x11display/x11GraphicsPipe.cxx index 479b3fde0c..e5e8da18c8 100644 --- a/panda/src/x11display/x11GraphicsPipe.cxx +++ b/panda/src/x11display/x11GraphicsPipe.cxx @@ -241,6 +241,7 @@ x11GraphicsPipe(const std::string &display) : _net_wm_state_below = XInternAtom(_display, "_NET_WM_STATE_BELOW", false); _net_wm_state_add = XInternAtom(_display, "_NET_WM_STATE_ADD", false); _net_wm_state_remove = XInternAtom(_display, "_NET_WM_STATE_REMOVE", false); + _net_wm_bypass_compositor = XInternAtom(_display, "_NET_WM_BYPASS_COMPOSITOR", false); } /** diff --git a/panda/src/x11display/x11GraphicsPipe.h b/panda/src/x11display/x11GraphicsPipe.h index c13b192627..3b8802e6e2 100644 --- a/panda/src/x11display/x11GraphicsPipe.h +++ b/panda/src/x11display/x11GraphicsPipe.h @@ -80,6 +80,7 @@ public: Atom _net_wm_state_below; Atom _net_wm_state_add; Atom _net_wm_state_remove; + Atom _net_wm_bypass_compositor; // Extension functions. typedef int (*pfn_XcursorGetDefaultSize)(X11_Display *); diff --git a/panda/src/x11display/x11GraphicsWindow.cxx b/panda/src/x11display/x11GraphicsWindow.cxx index 9a1555c27e..df038a6dad 100644 --- a/panda/src/x11display/x11GraphicsWindow.cxx +++ b/panda/src/x11display/x11GraphicsWindow.cxx @@ -29,14 +29,10 @@ #include "get_x11.h" #include "pnmImage.h" #include "pnmFileTypeRegistry.h" +#include "evdevInputDevice.h" -#include -#include #include - -#ifdef PHAVE_LINUX_INPUT_H -#include -#endif +#include using std::istream; using std::ostringstream; @@ -90,8 +86,6 @@ static int xcursor_seek(XcursorFile *file, long offset, int whence) { TypeHandle x11GraphicsWindow::_type_handle; -#define test_bit(bit, array) ((array)[(bit)/8] & (1<<((bit)&7))) - /** * */ @@ -126,9 +120,9 @@ x11GraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe, _override_redirect = False; _wm_delete_window = x11_pipe->_wm_delete_window; - GraphicsWindowInputDevice device = - GraphicsWindowInputDevice::pointer_and_keyboard(this, "keyboard_mouse"); + PT(GraphicsWindowInputDevice) device = GraphicsWindowInputDevice::pointer_and_keyboard(this, "keyboard_mouse"); add_input_device(device); + _input = device; } /** @@ -156,7 +150,7 @@ get_pointer(int device) const { LightMutexHolder holder(_input_lock); nassertr(device >= 0 && device < (int)_input_devices.size(), MouseData()); - result = _input_devices[device].get_pointer(); + result = ((const GraphicsWindowInputDevice *)_input_devices[device].p())->get_pointer(); // We recheck this immediately to get the most up-to-date value, but we // won't bother waiting for the lock if we can't. @@ -170,7 +164,7 @@ get_pointer(int device) const { double time = ClockObject::get_global_clock()->get_real_time(); result._xpos = event.xbutton.x; result._ypos = event.xbutton.y; - ((GraphicsWindowInputDevice &)_input_devices[0]).set_pointer_in_window(result._xpos, result._ypos, time); + ((GraphicsWindowInputDevice *)_input_devices[0].p())->set_pointer_in_window(result._xpos, result._ypos, time); } x11GraphicsPipe::_x_mutex.release(); } @@ -192,29 +186,24 @@ move_pointer(int device, int x, int y) { // Probably not an issue. if (device == 0) { // Move the system mouse pointer. - if (!_properties.get_foreground() || - !_input_devices[0].get_pointer().get_in_window()) { + PointerData md = _input->get_pointer(); + if (!_properties.get_foreground() || !md.get_in_window()) { // If the window doesn't have input focus, or the mouse isn't currently // within the window, forget it. return false; } - const MouseData &md = _input_devices[0].get_pointer(); if (!md.get_in_window() || md.get_x() != x || md.get_y() != y) { if (!_dga_mouse_enabled) { LightReMutexHolder holder(x11GraphicsPipe::_x_mutex); XWarpPointer(_display, None, _xwindow, 0, 0, 0, 0, x, y); } - _input_devices[0].set_pointer_in_window(x, y); + _input->set_pointer_in_window(x, y); } return true; } else { - // Move a raw mouse. - if (device < 1 || (size_t)device >= _input_devices.size()) { - return false; - } - _input_devices[device].set_pointer_in_window(x, y); - return true; + // Can't move a raw mouse. + return false; } } @@ -309,8 +298,6 @@ process_events() { return; } - poll_raw_mice(); - XEvent event; XKeyEvent keyrelease_event; bool got_keyrelease_event = false; @@ -369,25 +356,25 @@ process_events() { // This refers to the mouse buttons. button = get_mouse_button(event.xbutton); if (!_dga_mouse_enabled) { - _input_devices[0].set_pointer_in_window(event.xbutton.x, event.xbutton.y); + _input->set_pointer_in_window(event.xbutton.x, event.xbutton.y); } - _input_devices[0].button_down(button); + _input->button_down(button); break; case ButtonRelease: button = get_mouse_button(event.xbutton); if (!_dga_mouse_enabled) { - _input_devices[0].set_pointer_in_window(event.xbutton.x, event.xbutton.y); + _input->set_pointer_in_window(event.xbutton.x, event.xbutton.y); } - _input_devices[0].button_up(button); + _input->button_up(button); break; case MotionNotify: if (_dga_mouse_enabled) { - const MouseData &md = _input_devices[0].get_raw_pointer(); - _input_devices[0].set_pointer_in_window(md.get_x() + event.xmotion.x_root, md.get_y() + event.xmotion.y_root); + PointerData md = _input->get_pointer(); + _input->set_pointer_in_window(md.get_x() + event.xmotion.x_root, md.get_y() + event.xmotion.y_root); } else { - _input_devices[0].set_pointer_in_window(event.xmotion.x, event.xmotion.y); + _input->set_pointer_in_window(event.xmotion.x, event.xmotion.y); } break; @@ -406,15 +393,15 @@ process_events() { case EnterNotify: if (_dga_mouse_enabled) { - const MouseData &md = _input_devices[0].get_raw_pointer(); - _input_devices[0].set_pointer_in_window(md.get_x(), md.get_y()); + PointerData md = _input->get_pointer(); + _input->set_pointer_in_window(md.get_x(), md.get_y()); } else { - _input_devices[0].set_pointer_in_window(event.xcrossing.x, event.xcrossing.y); + _input->set_pointer_in_window(event.xcrossing.x, event.xcrossing.y); } break; case LeaveNotify: - _input_devices[0].set_pointer_out_of_window(); + _input->set_pointer_out_of_window(); break; case FocusIn: @@ -423,7 +410,7 @@ process_events() { break; case FocusOut: - _input_devices[0].focus_lost(); + _input->focus_lost(); properties.set_foreground(false); changed_properties = true; break; @@ -521,6 +508,36 @@ process_events() { changed_properties = true; } + if (properties.has_foreground() && ( + _properties.get_mouse_mode() == WindowProperties::M_confined || + _dga_mouse_enabled)) { + x11GraphicsPipe *x11_pipe; + DCAST_INTO_V(x11_pipe, _pipe); + + // Focus has changed, let's let go of the pointer if we've grabbed or re-grab it if needed + if (properties.get_foreground()) { + // Window is going to the foreground, re-grab the pointer + X11_Cursor cursor = None; + if (_properties.get_cursor_hidden()) { + cursor = x11_pipe->get_hidden_cursor(); + } + + XGrabPointer(_display, _xwindow, True, 0, GrabModeAsync, GrabModeAsync, + _xwindow, cursor, CurrentTime); + if (_dga_mouse_enabled) { + x11_pipe->enable_relative_mouse(); + } + } + else { + // window is leaving the foreground, ungrab the pointer + if (_dga_mouse_enabled) { + x11_pipe->disable_relative_mouse(); + } else if (_properties.get_mouse_mode() == WindowProperties::M_confined) { + XUngrabPointer(_display, CurrentTime); + } + } + } + if (changed_properties) { system_changed_properties(properties); } @@ -812,7 +829,7 @@ set_properties_now(WindowProperties &properties) { XQueryPointer(_display, _xwindow, &event.xbutton.root, &event.xbutton.window, &event.xbutton.x_root, &event.xbutton.y_root, &event.xbutton.x, &event.xbutton.y, &event.xbutton.state); - _input_devices[0].set_pointer_in_window(event.xbutton.x, event.xbutton.y); + _input->set_pointer_in_window(event.xbutton.x, event.xbutton.y); } } else { x11display_cat.info() @@ -1255,6 +1272,14 @@ set_wm_properties(const WindowProperties &properties, bool already_mapped) { XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&pid, 1); + // Disable compositing effects in fullscreen mode. + if (properties.has_fullscreen()) { + int32_t compositor = properties.get_fullscreen() ? 1 : 0; + XChangeProperty(_display, _xwindow, x11_pipe->_net_wm_bypass_compositor, + XA_CARDINAL, 32, PropModeReplace, + (unsigned char *)&compositor, 1); + } + XChangeProperty(_display, _xwindow, x11_pipe->_net_wm_window_type, XA_ATOM, 32, PropModeReplace, (unsigned char *)type_data, next_type_data); @@ -1330,6 +1355,7 @@ setup_colormap(XVisualInfo *visual) { /** * Adds raw mice to the _input_devices list. + * @deprecated obtain raw devices via the device manager instead. */ void x11GraphicsWindow:: open_raw_mice() { @@ -1338,66 +1364,43 @@ open_raw_mice() { bool any_mice = false; for (int i=0; i<64; i++) { - uint8_t evtypes[EV_MAX/8 + 1]; ostringstream fnb; fnb << "/dev/input/event" << i; string fn = fnb.str(); int fd = open(fn.c_str(), O_RDONLY | O_NONBLOCK, 0); if (fd >= 0) { - any_present = true; - char name[256]; - char phys[256]; - char uniq[256]; - if ((ioctl(fd, EVIOCGNAME(sizeof(name)), name) < 0)|| - (ioctl(fd, EVIOCGPHYS(sizeof(phys)), phys) < 0)|| - (ioctl(fd, EVIOCGPHYS(sizeof(uniq)), uniq) < 0)|| - (ioctl(fd, EVIOCGBIT(0, EV_MAX), &evtypes) < 0)) { - close(fd); - x11display_cat.error() << - "Opening raw mice: ioctl failed on " << fn << "\n"; - } else { - if (test_bit(EV_REL, evtypes) || test_bit(EV_ABS, evtypes)) { - for (char *p=name; *p; p++) { - if (((*p<'a')||(*p>'z')) && ((*p<'A')||(*p>'Z')) && ((*p<'0')||(*p>'9'))) { - *p = '_'; - } - } - for (char *p=uniq; *p; p++) { - if (((*p<'a')||(*p>'z')) && ((*p<'A')||(*p>'Z')) && ((*p<'0')||(*p>'9'))) { - *p = '_'; - } - } - string full_id = ((string)name) + "." + uniq; - MouseDeviceInfo inf; - inf._fd = fd; - inf._input_device_index = _input_devices.size(); - inf._io_buffer = ""; - _mouse_device_info.push_back(inf); - GraphicsWindowInputDevice device = - GraphicsWindowInputDevice::pointer_only(this, full_id); - add_input_device(device); - x11display_cat.info() << "Raw mouse " << - inf._input_device_index << " detected: " << full_id << "\n"; - any_mice = true; - } else { - close(fd); - } + EvdevInputDevice *device = new EvdevInputDevice(nullptr, fd); + nassertd(device != NULL) continue; + + if (device->has_pointer()) { + add_input_device(device); + + x11display_cat.info() + << "Raw mouse " << _input_devices.size() + << " detected: " << device->get_name() << "\n"; + + any_mice = true; + any_present = true; } } else { - if ((errno == ENOENT)||(errno == ENOTDIR)) { + if (errno == ENOENT || errno == ENOTDIR) { break; } else { any_present = true; - x11display_cat.error() << - "Opening raw mice: " << strerror(errno) << " " << fn << "\n"; + x11display_cat.error() + << "Opening raw mice: " << strerror(errno) << " " << fn << "\n"; } } } - if (!any_present) { + if (any_mice) { + _properties.set_raw_mice(true); + + } else if (!any_present) { x11display_cat.error() << "Opening raw mice: files not found: /dev/input/event*\n"; - } else if (!any_mice) { + + } else { x11display_cat.error() << "Opening raw mice: no mouse devices detected in /dev/input/event*\n"; } @@ -1407,72 +1410,13 @@ open_raw_mice() { #endif } -/** - * Reads events from the raw mouse device files. - */ -void x11GraphicsWindow:: -poll_raw_mice() { -#ifdef PHAVE_LINUX_INPUT_H - for (MouseDeviceInfo &inf : _mouse_device_info) { - // Read all bytes into buffer. - if (inf._fd >= 0) { - while (1) { - char tbuf[1024]; - int nread = read(inf._fd, tbuf, sizeof(tbuf)); - if (nread > 0) { - inf._io_buffer += string(tbuf, nread); - } else { - if ((nread < 0) && ((errno == EWOULDBLOCK) || (errno==EAGAIN))) { - break; - } - close(inf._fd); - inf._fd = -1; - break; - } - } - } - - // Process events. - int nevents = inf._io_buffer.size() / sizeof(struct input_event); - if (nevents == 0) { - continue; - } - const input_event *events = (const input_event *)(inf._io_buffer.c_str()); - GraphicsWindowInputDevice &dev = _input_devices[inf._input_device_index]; - int x = dev.get_raw_pointer().get_x(); - int y = dev.get_raw_pointer().get_y(); - for (int i = 0; i < nevents; i++) { - if (events[i].type == EV_REL) { - if (events[i].code == REL_X) x += events[i].value; - if (events[i].code == REL_Y) y += events[i].value; - } else if (events[i].type == EV_ABS) { - if (events[i].code == ABS_X) x = events[i].value; - if (events[i].code == ABS_Y) y = events[i].value; - } else if (events[i].type == EV_KEY) { - if ((events[i].code >= BTN_MOUSE) && (events[i].code < BTN_MOUSE + 8)) { - int btn = events[i].code - BTN_MOUSE; - dev.set_pointer_in_window(x, y); - if (events[i].value) { - dev.button_down(MouseButton::button(btn)); - } else { - dev.button_up(MouseButton::button(btn)); - } - } - } - } - inf._io_buffer.erase(0, nevents * sizeof(struct input_event)); - dev.set_pointer_in_window(x, y); - } -#endif -} - /** * Generates a keystroke corresponding to the indicated X KeyPress event. */ void x11GraphicsWindow:: handle_keystroke(XKeyEvent &event) { if (!_dga_mouse_enabled) { - _input_devices[0].set_pointer_in_window(event.x, event.y); + _input->set_pointer_in_window(event.x, event.y); } if (_ic) { @@ -1489,14 +1433,14 @@ handle_keystroke(XKeyEvent &event) { // Now each of the returned wide characters represents a keystroke. for (int i = 0; i < len; i++) { - _input_devices[0].keystroke(buffer[i]); + _input->keystroke(buffer[i]); } } else { // Without an input context, just get the ascii keypress. ButtonHandle button = get_button(event, true); if (button.has_ascii_equivalent()) { - _input_devices[0].keystroke(button.get_ascii_equivalent()); + _input->keystroke(button.get_ascii_equivalent()); } } } @@ -1507,30 +1451,32 @@ handle_keystroke(XKeyEvent &event) { void x11GraphicsWindow:: handle_keypress(XKeyEvent &event) { if (!_dga_mouse_enabled) { - _input_devices[0].set_pointer_in_window(event.x, event.y); + _input->set_pointer_in_window(event.x, event.y); } // Now get the raw unshifted button. ButtonHandle button = get_button(event, false); if (button != ButtonHandle::none()) { if (button == KeyboardButton::lcontrol() || button == KeyboardButton::rcontrol()) { - _input_devices[0].button_down(KeyboardButton::control()); + _input->button_down(KeyboardButton::control()); } if (button == KeyboardButton::lshift() || button == KeyboardButton::rshift()) { - _input_devices[0].button_down(KeyboardButton::shift()); + _input->button_down(KeyboardButton::shift()); } if (button == KeyboardButton::lalt() || button == KeyboardButton::ralt()) { - _input_devices[0].button_down(KeyboardButton::alt()); + _input->button_down(KeyboardButton::alt()); } if (button == KeyboardButton::lmeta() || button == KeyboardButton::rmeta()) { - _input_devices[0].button_down(KeyboardButton::meta()); + _input->button_down(KeyboardButton::meta()); } - _input_devices[0].button_down(button); + _input->button_down(button); } - ButtonHandle raw_button = map_raw_button(event.keycode); - if (raw_button != ButtonHandle::none()) { - _input_devices[0].raw_button_down(raw_button); + if (event.keycode >= 9 && event.keycode <= 135) { + ButtonHandle raw_button = map_raw_button(event.keycode); + if (raw_button != ButtonHandle::none()) { + _input->raw_button_down(raw_button); + } } } @@ -1540,30 +1486,32 @@ handle_keypress(XKeyEvent &event) { void x11GraphicsWindow:: handle_keyrelease(XKeyEvent &event) { if (!_dga_mouse_enabled) { - _input_devices[0].set_pointer_in_window(event.x, event.y); + _input->set_pointer_in_window(event.x, event.y); } // Now get the raw unshifted button. ButtonHandle button = get_button(event, false); if (button != ButtonHandle::none()) { if (button == KeyboardButton::lcontrol() || button == KeyboardButton::rcontrol()) { - _input_devices[0].button_up(KeyboardButton::control()); + _input->button_up(KeyboardButton::control()); } if (button == KeyboardButton::lshift() || button == KeyboardButton::rshift()) { - _input_devices[0].button_up(KeyboardButton::shift()); + _input->button_up(KeyboardButton::shift()); } if (button == KeyboardButton::lalt() || button == KeyboardButton::ralt()) { - _input_devices[0].button_up(KeyboardButton::alt()); + _input->button_up(KeyboardButton::alt()); } if (button == KeyboardButton::lmeta() || button == KeyboardButton::rmeta()) { - _input_devices[0].button_up(KeyboardButton::meta()); + _input->button_up(KeyboardButton::meta()); } - _input_devices[0].button_up(button); + _input->button_up(button); } - ButtonHandle raw_button = map_raw_button(event.keycode); - if (raw_button != ButtonHandle::none()) { - _input_devices[0].raw_button_up(raw_button); + if (event.keycode >= 9 && event.keycode <= 135) { + ButtonHandle raw_button = map_raw_button(event.keycode); + if (raw_button != ButtonHandle::none()) { + _input->raw_button_up(raw_button); + } } } @@ -1644,7 +1592,7 @@ get_button(XKeyEvent &key_event, bool allow_shift) { // this in just the ASCII set, because we handle international keyboards // elsewhere (via an input context). if ((key_event.state & (ShiftMask | LockMask)) != 0) { - if (key >= XK_a and key <= XK_z) { + if (key >= XK_a && key <= XK_z) { key += (XK_A - XK_a); } } @@ -1985,117 +1933,16 @@ map_button(KeySym key) const { */ ButtonHandle x11GraphicsWindow:: map_raw_button(KeyCode key) const { - switch (key) { - case 9: return KeyboardButton::escape(); - case 10: return KeyboardButton::ascii_key('1'); - case 11: return KeyboardButton::ascii_key('2'); - case 12: return KeyboardButton::ascii_key('3'); - case 13: return KeyboardButton::ascii_key('4'); - case 14: return KeyboardButton::ascii_key('5'); - case 15: return KeyboardButton::ascii_key('6'); - case 16: return KeyboardButton::ascii_key('7'); - case 17: return KeyboardButton::ascii_key('8'); - case 18: return KeyboardButton::ascii_key('9'); - case 19: return KeyboardButton::ascii_key('0'); - case 20: return KeyboardButton::ascii_key('-'); - case 21: return KeyboardButton::ascii_key('='); - case 22: return KeyboardButton::backspace(); - case 23: return KeyboardButton::tab(); - case 24: return KeyboardButton::ascii_key('q'); - case 25: return KeyboardButton::ascii_key('w'); - case 26: return KeyboardButton::ascii_key('e'); - case 27: return KeyboardButton::ascii_key('r'); - case 28: return KeyboardButton::ascii_key('t'); - case 29: return KeyboardButton::ascii_key('y'); - case 30: return KeyboardButton::ascii_key('u'); - case 31: return KeyboardButton::ascii_key('i'); - case 32: return KeyboardButton::ascii_key('o'); - case 33: return KeyboardButton::ascii_key('p'); - case 34: return KeyboardButton::ascii_key('['); - case 35: return KeyboardButton::ascii_key(']'); - case 36: return KeyboardButton::enter(); - case 37: return KeyboardButton::lcontrol(); - case 38: return KeyboardButton::ascii_key('a'); - case 39: return KeyboardButton::ascii_key('s'); - case 40: return KeyboardButton::ascii_key('d'); - case 41: return KeyboardButton::ascii_key('f'); - case 42: return KeyboardButton::ascii_key('g'); - case 43: return KeyboardButton::ascii_key('h'); - case 44: return KeyboardButton::ascii_key('j'); - case 45: return KeyboardButton::ascii_key('k'); - case 46: return KeyboardButton::ascii_key('l'); - case 47: return KeyboardButton::ascii_key(';'); - case 48: return KeyboardButton::ascii_key('\''); - case 49: return KeyboardButton::ascii_key('`'); - case 50: return KeyboardButton::lshift(); - case 51: return KeyboardButton::ascii_key('\\'); - case 52: return KeyboardButton::ascii_key('z'); - case 53: return KeyboardButton::ascii_key('x'); - case 54: return KeyboardButton::ascii_key('c'); - case 55: return KeyboardButton::ascii_key('v'); - case 56: return KeyboardButton::ascii_key('b'); - case 57: return KeyboardButton::ascii_key('n'); - case 58: return KeyboardButton::ascii_key('m'); - case 59: return KeyboardButton::ascii_key(','); - case 60: return KeyboardButton::ascii_key('.'); - case 61: return KeyboardButton::ascii_key('/'); - case 62: return KeyboardButton::rshift(); - case 63: return KeyboardButton::ascii_key('*'); - case 64: return KeyboardButton::lalt(); - case 65: return KeyboardButton::space(); - case 66: return KeyboardButton::caps_lock(); - case 67: return KeyboardButton::f1(); - case 68: return KeyboardButton::f2(); - case 69: return KeyboardButton::f3(); - case 70: return KeyboardButton::f4(); - case 71: return KeyboardButton::f5(); - case 72: return KeyboardButton::f6(); - case 73: return KeyboardButton::f7(); - case 74: return KeyboardButton::f8(); - case 75: return KeyboardButton::f9(); - case 76: return KeyboardButton::f10(); - case 77: return KeyboardButton::num_lock(); - case 78: return KeyboardButton::scroll_lock(); - case 79: return KeyboardButton::ascii_key('7'); - case 80: return KeyboardButton::ascii_key('8'); - case 81: return KeyboardButton::ascii_key('9'); - case 82: return KeyboardButton::ascii_key('-'); - case 83: return KeyboardButton::ascii_key('4'); - case 84: return KeyboardButton::ascii_key('5'); - case 85: return KeyboardButton::ascii_key('6'); - case 86: return KeyboardButton::ascii_key('+'); - case 87: return KeyboardButton::ascii_key('1'); - case 88: return KeyboardButton::ascii_key('2'); - case 89: return KeyboardButton::ascii_key('3'); - case 90: return KeyboardButton::ascii_key('0'); - case 91: return KeyboardButton::ascii_key('.'); - - case 95: return KeyboardButton::f11(); - case 96: return KeyboardButton::f12(); - - case 104: return KeyboardButton::enter(); - case 105: return KeyboardButton::rcontrol(); - case 106: return KeyboardButton::ascii_key('/'); - case 107: return KeyboardButton::print_screen(); - case 108: return KeyboardButton::ralt(); - - case 110: return KeyboardButton::home(); - case 111: return KeyboardButton::up(); - case 112: return KeyboardButton::page_up(); - case 113: return KeyboardButton::left(); - case 114: return KeyboardButton::right(); - case 115: return KeyboardButton::end(); - case 116: return KeyboardButton::down(); - case 117: return KeyboardButton::page_down(); - case 118: return KeyboardButton::insert(); - case 119: return KeyboardButton::del(); - - case 127: return KeyboardButton::pause(); - - case 133: return KeyboardButton::lmeta(); - case 134: return KeyboardButton::rmeta(); - case 135: return KeyboardButton::menu(); +#ifdef PHAVE_LINUX_INPUT_H + // Most X11 servers are configured to use the evdev driver, which + // adds 8 to the underlying evdev keycodes (not sure why). + // In any case, this means we can use the same mapping as our raw + // input code, which uses evdev directly. + int index = key - 8; + if (index >= 0) { + return EvdevInputDevice::map_button(index); } +#endif return ButtonHandle::none(); } diff --git a/panda/src/x11display/x11GraphicsWindow.h b/panda/src/x11display/x11GraphicsWindow.h index 906a64b623..f512b16a19 100644 --- a/panda/src/x11display/x11GraphicsWindow.h +++ b/panda/src/x11display/x11GraphicsWindow.h @@ -70,7 +70,6 @@ protected: static Bool check_event(X11_Display *display, XEvent *event, char *arg); void open_raw_mice(); - void poll_raw_mice(); private: X11_Cursor get_cursor(const Filename &filename); @@ -88,19 +87,14 @@ protected: LVecBase2i _fixed_size; + GraphicsWindowInputDevice *_input; + long _event_mask; bool _awaiting_configure; bool _dga_mouse_enabled; Bool _override_redirect; Atom _wm_delete_window; - struct MouseDeviceInfo { - int _fd; - int _input_device_index; - std::string _io_buffer; - }; - pvector _mouse_device_info; - x11GraphicsPipe::pfn_XRRGetScreenInfo _XRRGetScreenInfo; x11GraphicsPipe::pfn_XRRSetScreenConfig _XRRSetScreenConfig; diff --git a/pandatool/src/deploy-stub/deploy-stub.c b/pandatool/src/deploy-stub/deploy-stub.c new file mode 100644 index 0000000000..12079da46d --- /dev/null +++ b/pandatool/src/deploy-stub/deploy-stub.c @@ -0,0 +1,690 @@ +/* Python interpreter main program for frozen scripts */ + +#include "Python.h" +#ifdef _WIN32 +# include "malloc.h" +# include +#else +# include +# include +#endif + +#ifdef __FreeBSD__ +# include +#endif + +#ifdef __APPLE__ +# include +# include +#endif + +#include +#include +#include + +#if PY_MAJOR_VERSION >= 3 +# include + +# if PY_MINOR_VERSION < 5 +# define Py_DecodeLocale _Py_char2wchar +# endif +#endif + +/* Leave room for future expansion. We only read pointer 0, but there are + other pointers that are being read by configPageManager.cxx. */ +#define MAX_NUM_POINTERS 24 + +/* Stored in the flags field of the blobinfo structure below. */ +enum Flags { + F_log_append = 1, +}; + +/* Define an exposed symbol where we store the offset to the module data. */ +#ifdef _MSC_VER +__declspec(dllexport) +#else +__attribute__((__visibility__("default"), used)) +#endif +volatile struct { + uint64_t blob_offset; + uint64_t blob_size; + uint16_t version; + uint16_t num_pointers; + uint16_t codepage; + uint16_t flags; + uint64_t reserved; + void *pointers[MAX_NUM_POINTERS]; + + // The reason we initialize it to -1 is because otherwise, smart linkers may + // end up putting it in the .bss section for zero-initialized data. +} blobinfo = {(uint64_t)-1}; + +#ifdef MS_WINDOWS +# define WIN32_LEAN_AND_MEAN +# include + +extern void PyWinFreeze_ExeInit(void); +extern void PyWinFreeze_ExeTerm(void); + +static struct _inittab extensions[] = { + {0, 0}, +}; + +#if PY_MAJOR_VERSION >= 3 +# define WIN_UNICODE +#endif +#endif + +#if defined(_WIN32) && PY_VERSION_HEX < 0x03060000 +static int supports_code_page(UINT cp) { + if (cp == 0) { + cp = GetACP(); + } + + /* Shortcut, because we know that these encodings are bundled by default-- + * see FreezeTool.py and Python's encodings/aliases.py */ + if (cp != 0 && cp != 1252 && cp != 367 && cp != 437 && cp != 850 && cp != 819) { + const struct _frozen *moddef; + char codec[100]; + + /* Check if the codec was frozen into the program. We can't check this + * using _PyCodec_Lookup, since Python hasn't been initialized yet. */ + PyOS_snprintf(codec, sizeof(codec), "encodings.cp%u", (unsigned int)cp); + + moddef = PyImport_FrozenModules; + while (moddef->name) { + if (strcmp(moddef->name, codec) == 0) { + return 1; + } + ++moddef; + } + return 0; + } + + return 1; +} +#endif + +/** + * Sets the main_dir field of the blobinfo structure, but only if it wasn't + * already set. + */ +static void set_main_dir(char *main_dir) { + if (blobinfo.num_pointers >= 10) { + if (blobinfo.num_pointers == 10) { + ++blobinfo.num_pointers; + blobinfo.pointers[10] = NULL; + } + if (blobinfo.pointers[10] == NULL) { + blobinfo.pointers[10] = main_dir; + } + } +} + +/** + * Creates the parent directories of the given path. Returns 1 on success. + */ +#ifdef _WIN32 +static int mkdir_parent(const wchar_t *path) { + // Copy the path to a temporary buffer. + wchar_t buffer[4096]; + size_t buflen = wcslen(path); + if (buflen + 1 >= _countof(buffer)) { + return 0; + } + wcscpy_s(buffer, _countof(buffer), path); + + // Seek back to find the last path separator. + while (buflen-- > 0) { + if (buffer[buflen] == '/' || buffer[buflen] == '\\') { + buffer[buflen] = 0; + break; + } + } + if (buflen == (size_t)-1 || buflen == 0) { + // There was no path separator, or this was the root directory. + return 0; + } + + if (CreateDirectoryW(buffer, NULL) != 0) { + // Success! + return 1; + } + + // Failed. + DWORD last_error = GetLastError(); + if (last_error == ERROR_ALREADY_EXISTS) { + // Not really an error: the directory is already there. + return 1; + } + + if (last_error == ERROR_PATH_NOT_FOUND) { + // We need to make the parent directory first. + if (mkdir_parent(buffer)) { + // Parent successfully created. Try again to make the child. + if (CreateDirectoryW(buffer, NULL) != 0) { + // Got it! + return 1; + } + } + } + return 0; +} +#else +static int mkdir_parent(const char *path) { + // Copy the path to a temporary buffer. + char buffer[4096]; + size_t buflen = strlen(path); + if (buflen + 1 >= sizeof(buffer)) { + return 0; + } + strcpy(buffer, path); + + // Seek back to find the last path separator. + while (buflen-- > 0) { + if (buffer[buflen] == '/') { + buffer[buflen] = 0; + break; + } + } + if (buflen == (size_t)-1 || buflen == 0) { + // There was no path separator, or this was the root directory. + return 0; + } + if (mkdir(buffer, 0755) == 0) { + // Success! + return 1; + } + + // Failed. + if (errno == EEXIST) { + // Not really an error: the directory is already there. + return 1; + } + + if (errno == ENOENT || errno == EACCES) { + // We need to make the parent directory first. + if (mkdir_parent(buffer)) { + // Parent successfully created. Try again to make the child. + if (mkdir(buffer, 0755) == 0) { + // Got it! + return 1; + } + } + } + return 0; +} +#endif + +/** + * Redirects the output streams to point to the log file with the given path. + * + * @param path specifies the location of log file, may start with ~ + * @param append should be nonzero if it should not truncate the log file. + */ +static int setup_logging(const char *path, int append) { +#ifdef _WIN32 + // Does it start with a tilde? Perform tilde expansion if so. + wchar_t pathw[MAX_PATH * 2]; + size_t offset = 0; + if (path[0] == '~' && (path[1] == 0 || path[1] == '/' || path[1] == '\\')) { + // Strip off the tilde. + ++path; + + // Get the home directory path for the current user. + if (!SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_PROFILE, NULL, 0, pathw))) { + return 0; + } + offset = wcslen(pathw); + } + + // We need to convert the rest of the path from UTF-8 to UTF-16. + if (MultiByteToWideChar(CP_UTF8, 0, path, -1, pathw + offset, + (int)(_countof(pathw) - offset)) == 0) { + return 0; + } + + DWORD access = append ? FILE_APPEND_DATA : (GENERIC_READ | GENERIC_WRITE); + int creation = append ? OPEN_ALWAYS : CREATE_ALWAYS; + HANDLE handle = CreateFileW(pathw, access, FILE_SHARE_DELETE | FILE_SHARE_READ, + NULL, creation, FILE_ATTRIBUTE_NORMAL, NULL); + + if (handle == INVALID_HANDLE_VALUE) { + // Make the parent directories first. + mkdir_parent(pathw); + handle = CreateFileW(pathw, access, FILE_SHARE_DELETE | FILE_SHARE_READ, + NULL, creation, FILE_ATTRIBUTE_NORMAL, NULL); + } + + if (handle == INVALID_HANDLE_VALUE) { + return 0; + } + + if (append) { + SetFilePointer(handle, 0, NULL, FILE_END); + } + + SetStdHandle(STD_OUTPUT_HANDLE, handle); + SetStdHandle(STD_ERROR_HANDLE, handle); + + // If we are running under the UCRT in a GUI application, we can't be sure + // that we have valid fds for stdout and stderr, so we have to set them up. + // One way to do this is to reopen them to something silly (like NUL). + if (_fileno(stdout) < 0) { + _close(1); + _wfreopen(L"\\\\.\\NUL", L"w", stdout); + } + + if (_fileno(stderr) < 0) { + _close(2); + _wfreopen(L"\\\\.\\NUL", L"w", stderr); + } + + // Now replace the stdout and stderr file descriptors with one pointing to + // our desired handle. + int fd = _open_osfhandle((intptr_t)handle, _O_WRONLY | _O_TEXT | (append ? _O_APPEND : 0)); + _dup2(fd, _fileno(stdout)); + _dup2(fd, _fileno(stderr)); + _close(fd); + + return 1; +#else + // Does it start with a tilde? Perform tilde expansion if so. + char buffer[PATH_MAX * 2]; + size_t offset = 0; + if (path[0] == '~' && (path[1] == 0 || path[1] == '/')) { + // Strip off the tilde. + ++path; + + // Get the home directory path for the current user. + const char *home_dir = getenv("HOME"); + if (home_dir == NULL) { + home_dir = getpwuid(getuid())->pw_dir; + } + offset = strlen(home_dir); + assert(offset < sizeof(buffer)); + strncpy(buffer, home_dir, sizeof(buffer)); + } + + // Copy over the rest of the path. + strcpy(buffer + offset, path); + + mode_t mode = O_CREAT | O_WRONLY | (append ? O_APPEND : O_TRUNC); + int fd = open(buffer, mode, 0644); + if (fd == -1) { + // Make the parent directories first. + mkdir_parent(buffer); + fd = open(buffer, mode, 0644); + } + + if (fd == -1) { + perror(buffer); + return 0; + } + + fflush(stdout); + fflush(stderr); + + dup2(fd, 1); + dup2(fd, 2); + + close(fd); + return 1; +#endif +} + +/* Main program */ + +#ifdef WIN_UNICODE +int Py_FrozenMain(int argc, wchar_t **argv) +#else +int Py_FrozenMain(int argc, char **argv) +#endif +{ + char *p; + int n, sts = 1; + int inspect = 0; + int unbuffered = 0; + +#if PY_MAJOR_VERSION >= 3 && !defined(WIN_UNICODE) + int i; + char *oldloc; + wchar_t **argv_copy = NULL; + /* We need a second copies, as Python might modify the first one. */ + wchar_t **argv_copy2 = NULL; + + if (argc > 0) { + argv_copy = (wchar_t **)alloca(sizeof(wchar_t *) * argc); + argv_copy2 = (wchar_t **)alloca(sizeof(wchar_t *) * argc); + } +#endif + +#if defined(MS_WINDOWS) && PY_VERSION_HEX >= 0x03040000 && PY_VERSION_HEX < 0x03060000 + if (!supports_code_page(GetConsoleOutputCP()) || + !supports_code_page(GetConsoleCP())) { + /* Revert to the active codepage, and tell Python to use the 'mbcs' + * encoding (which always uses the active codepage). In 99% of cases, + * this will be the same thing anyway. */ + UINT acp = GetACP(); + SetConsoleCP(acp); + SetConsoleOutputCP(acp); + Py_SetStandardStreamEncoding("mbcs", NULL); + } +#endif + + Py_FrozenFlag = 1; /* Suppress errors from getpath.c */ + Py_NoSiteFlag = 0; + Py_NoUserSiteDirectory = 1; + + if ((p = Py_GETENV("PYTHONINSPECT")) && *p != '\0') + inspect = 1; + if ((p = Py_GETENV("PYTHONUNBUFFERED")) && *p != '\0') + unbuffered = 1; + + if (unbuffered) { + setbuf(stdin, (char *)NULL); + setbuf(stdout, (char *)NULL); + setbuf(stderr, (char *)NULL); + } + +#if PY_MAJOR_VERSION >= 3 && !defined(WIN_UNICODE) + oldloc = setlocale(LC_ALL, NULL); + setlocale(LC_ALL, ""); + for (i = 0; i < argc; i++) { + argv_copy[i] = Py_DecodeLocale(argv[i], NULL); + argv_copy2[i] = argv_copy[i]; + if (!argv_copy[i]) { + fprintf(stderr, "Unable to decode the command line argument #%i\n", + i + 1); + argc = i; + goto error; + } + } + setlocale(LC_ALL, oldloc); +#endif + +#ifdef MS_WINDOWS + PyImport_ExtendInittab(extensions); +#endif /* MS_WINDOWS */ + + if (argc >= 1) { +#if PY_MAJOR_VERSION >= 3 && !defined(WIN_UNICODE) + Py_SetProgramName(argv_copy[0]); +#else + Py_SetProgramName(argv[0]); +#endif + } + + Py_Initialize(); +#ifdef MS_WINDOWS + PyWinFreeze_ExeInit(); +#endif + +#if defined(MS_WINDOWS) && PY_VERSION_HEX < 0x03040000 + if (!supports_code_page(GetConsoleOutputCP()) || + !supports_code_page(GetConsoleCP())) { + /* Same hack as before except for Python 2.7, which doesn't seem to have + * a way to set the encoding ahead of time, and setting PYTHONIOENCODING + * doesn't seem to work. Fortunately, Python 2.7 doesn't usually start + * causing codec errors until the first print statement. */ + PyObject *sys_stream; + UINT acp = GetACP(); + SetConsoleCP(acp); + SetConsoleOutputCP(acp); + + sys_stream = PySys_GetObject("stdin"); + if (sys_stream && PyFile_Check(sys_stream)) { + PyFile_SetEncodingAndErrors(sys_stream, "mbcs", NULL); + } + sys_stream = PySys_GetObject("stdout"); + if (sys_stream && PyFile_Check(sys_stream)) { + PyFile_SetEncodingAndErrors(sys_stream, "mbcs", NULL); + } + sys_stream = PySys_GetObject("stderr"); + if (sys_stream && PyFile_Check(sys_stream)) { + PyFile_SetEncodingAndErrors(sys_stream, "mbcs", NULL); + } + } +#endif + + if (Py_VerboseFlag) + fprintf(stderr, "Python %s\n%s\n", + Py_GetVersion(), Py_GetCopyright()); + +#if PY_MAJOR_VERSION >= 3 && !defined(WIN_UNICODE) + PySys_SetArgv(argc, argv_copy); +#else + PySys_SetArgv(argc, argv); +#endif + +#ifdef MACOS_APP_BUNDLE + // Add the Frameworks directory to sys.path. + char buffer[PATH_MAX]; + uint32_t bufsize = sizeof(buffer); + if (_NSGetExecutablePath(buffer, &bufsize) != 0) { + assert(false); + return 1; + } + char resolved[PATH_MAX]; + if (!realpath(buffer, resolved)) { + perror("realpath"); + return 1; + } + const char *dir = dirname(resolved); + sprintf(buffer, "%s/../Frameworks", dir); + + PyObject *sys_path = PyList_New(1); + #if PY_MAJOR_VERSION >= 3 + PyList_SET_ITEM(sys_path, 0, PyUnicode_FromString(buffer)); + #else + PyList_SET_ITEM(sys_path, 0, PyString_FromString(buffer)); + #endif + PySys_SetObject("path", sys_path); + Py_DECREF(sys_path); + + // Now, store a path to the Resources directory into the main_dir pointer, + // for ConfigPageManager to read out and assign to MAIN_DIR. + sprintf(buffer, "%s/../Resources", dir); + set_main_dir(buffer); +#endif + + n = PyImport_ImportFrozenModule("__main__"); + if (n == 0) + Py_FatalError("__main__ not frozen"); + if (n < 0) { + PyErr_Print(); + sts = 1; + } + else + sts = 0; + + if (inspect && isatty((int)fileno(stdin))) + sts = PyRun_AnyFile(stdin, "") != 0; + +#ifdef MS_WINDOWS + PyWinFreeze_ExeTerm(); +#endif + Py_Finalize(); + +#if PY_MAJOR_VERSION >= 3 && !defined(WIN_UNICODE) +error: + if (argv_copy2) { + for (i = 0; i < argc; i++) { +#if PY_MINOR_VERSION >= 4 + PyMem_RawFree(argv_copy2[i]); +#else + PyMem_Free(argv_copy2[i]); +#endif + } + } +#endif + return sts; +} + +/** + * Maps the binary blob at the given memory address to memory, and returns the + * pointer to the beginning of it. + */ +static void *map_blob(off_t offset, size_t size) { + void *blob; + FILE *runtime; + +#ifdef _WIN32 + wchar_t buffer[2048]; + GetModuleFileNameW(NULL, buffer, 2048); + runtime = _wfopen(buffer, L"rb"); +#elif defined(__FreeBSD__) + size_t bufsize = 4096; + char buffer[4096]; + int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1}; + mib[3] = getpid(); + if (sysctl(mib, 4, (void *)buffer, &bufsize, NULL, 0) == -1) { + perror("sysctl"); + return NULL; + } + runtime = fopen(buffer, "rb"); +#elif defined(__APPLE__) + char buffer[4096]; + uint32_t bufsize = sizeof(buffer); + if (_NSGetExecutablePath(buffer, &bufsize) != 0) { + return NULL; + } + runtime = fopen(buffer, "rb"); +#else + char buffer[4096]; + ssize_t pathlen = readlink("/proc/self/exe", buffer, sizeof(buffer) - 1); + if (pathlen <= 0) { + perror("readlink(/proc/self/exe)"); + return NULL; + } + buffer[pathlen] = '\0'; + runtime = fopen(buffer, "rb"); +#endif + + // Get offsets. In version 0, we read it from the end of the file. + if (blobinfo.version == 0) { + uint64_t end, begin; + fseek(runtime, -8, SEEK_END); + end = ftell(runtime); + fread(&begin, 8, 1, runtime); + + offset = (off_t)begin; + size = (size_t)(end - begin); + } + + // mmap the section indicated by the offset (or malloc/fread on windows) +#ifdef _WIN32 + blob = (void *)malloc(size); + assert(blob != NULL); + fseek(runtime, (long)offset, SEEK_SET); + fread(blob, size, 1, runtime); +#else + blob = (void *)mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fileno(runtime), offset); + assert(blob != MAP_FAILED); +#endif + + fclose(runtime); + return blob; +} + +/** + * The inverse of map_blob. + */ +static void unmap_blob(void *blob) { + if (blob) { +#ifdef _WIN32 + free(blob); +#else + munmap(blob, blobinfo.blob_size); +#endif + } +} + +/** + * Main entry point to deploy-stub. + */ +#if defined(_WIN32) && PY_MAJOR_VERSION >= 3 +int wmain(int argc, wchar_t *argv[]) { +#else +int main(int argc, char *argv[]) { +#endif + int retval; + struct _frozen *moddef; + const char *log_filename; + void *blob = NULL; + log_filename = NULL; + + /* + printf("blob_offset: %d\n", (int)blobinfo.blob_offset); + printf("blob_size: %d\n", (int)blobinfo.blob_size); + printf("version: %d\n", (int)blobinfo.version); + printf("num_pointers: %d\n", (int)blobinfo.num_pointers); + printf("codepage: %d\n", (int)blobinfo.codepage); + printf("flags: %d\n", (int)blobinfo.flags); + printf("reserved: %d\n", (int)blobinfo.reserved); + */ + + // If we have a blob offset, we have to map the blob to memory. + if (blobinfo.version == 0 || blobinfo.blob_offset != 0) { + void *blob = map_blob((off_t)blobinfo.blob_offset, (size_t)blobinfo.blob_size); + assert(blob != NULL); + + // Offset the pointers in the header using the base mmap address. + if (blobinfo.version > 0 && blobinfo.num_pointers > 0) { + uint32_t i; + assert(blobinfo.num_pointers <= MAX_NUM_POINTERS); + for (i = 0; i < blobinfo.num_pointers; ++i) { + // Only offset if the pointer is non-NULL. Except for the first + // pointer, which may never be NULL and usually (but not always) + // points to the beginning of the blob. + if (i == 0 || blobinfo.pointers[i] != 0) { + blobinfo.pointers[i] = (void *)((uintptr_t)blobinfo.pointers[i] + (uintptr_t)blob); + } + } + if (blobinfo.num_pointers >= 12) { + log_filename = blobinfo.pointers[11]; + } + } else { + blobinfo.pointers[0] = blob; + } + + // Offset the pointers in the module table using the base mmap address. + moddef = blobinfo.pointers[0]; + while (moddef->name) { + moddef->name = (char *)((uintptr_t)moddef->name + (uintptr_t)blob); + if (moddef->code != 0) { + moddef->code = (unsigned char *)((uintptr_t)moddef->code + (uintptr_t)blob); + } + //printf("MOD: %s %p %d\n", moddef->name, (void*)moddef->code, moddef->size); + moddef++; + } + } + + if (log_filename != NULL) { + setup_logging(log_filename, (blobinfo.flags & F_log_append) != 0); + } + +#ifdef _WIN32 + if (blobinfo.codepage != 0) { + SetConsoleCP(blobinfo.codepage); + SetConsoleOutputCP(blobinfo.codepage); + } +#endif + + // Run frozen application + PyImport_FrozenModules = blobinfo.pointers[0]; + retval = Py_FrozenMain(argc, argv); + + unmap_blob(blob); + return retval; +} + +#ifdef WIN_UNICODE +int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, wchar_t *lpCmdLine, int nCmdShow) { + return wmain(__argc, __wargv); +} +#elif defined(_WIN32) +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, char *lpCmdLine, int nCmdShow) { + return main(__argc, __argv); +} +#endif diff --git a/pandatool/src/deploy-stub/frozen_dllmain.c b/pandatool/src/deploy-stub/frozen_dllmain.c new file mode 100644 index 0000000000..3e374cf558 --- /dev/null +++ b/pandatool/src/deploy-stub/frozen_dllmain.c @@ -0,0 +1,134 @@ +/* FreezeDLLMain.cpp + +This is a DLLMain suitable for frozen applications/DLLs on +a Windows platform. + +The general problem is that many Python extension modules may define +DLL main functions, but when statically linked together to form +a frozen application, this DLLMain symbol exists multiple times. + +The solution is: +* Each module checks for a frozen build, and if so, defines its DLLMain + function as "__declspec(dllexport) DllMain%module%" + (eg, DllMainpythoncom, or DllMainpywintypes) + +* The frozen .EXE/.DLL links against this module, which provides + the single DllMain. + +* This DllMain attempts to locate and call the DllMain for each + of the extension modules. + +* This code also has hooks to "simulate" DllMain when used from + a frozen .EXE. + +At this stage, there is a static table of "possibly embedded modules". +This should change to something better, but it will work OK for now. + +Note that this scheme does not handle dependencies in the order +of DllMain calls - except it does call pywintypes first :-) + +As an example of how an extension module with a DllMain should be +changed, here is a snippet from the pythoncom extension module. + + // end of example code from pythoncom's DllMain.cpp + #ifndef BUILD_FREEZE + #define DLLMAIN DllMain + #define DLLMAIN_DECL + #else + #define DLLMAIN DllMainpythoncom + #define DLLMAIN_DECL __declspec(dllexport) + #endif + + extern "C" DLLMAIN_DECL + BOOL WINAPI DLLMAIN(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) + // end of example code from pythoncom's DllMain.cpp + +***************************************************************************/ +#include "windows.h" + +static char *possibleModules[] = { + "pywintypes", + "pythoncom", + "win32ui", + NULL, +}; + +BOOL CallModuleDllMain(char *modName, DWORD dwReason); + + +/* + Called by a frozen .EXE only, so that built-in extension + modules are initialized correctly +*/ +void PyWinFreeze_ExeInit(void) +{ + char **modName; + for (modName = possibleModules;*modName;*modName++) { +/* printf("Initialising '%s'\n", *modName); */ + CallModuleDllMain(*modName, DLL_PROCESS_ATTACH); + } +} + +/* + Called by a frozen .EXE only, so that built-in extension + modules are cleaned up +*/ +void PyWinFreeze_ExeTerm(void) +{ + // Must go backwards + char **modName; + for (modName = possibleModules+(sizeof(possibleModules) / sizeof(char *))-2; + modName >= possibleModules; + *modName--) { +/* printf("Terminating '%s'\n", *modName);*/ + CallModuleDllMain(*modName, DLL_PROCESS_DETACH); + } +} + +BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) +{ + BOOL ret = TRUE; + switch (dwReason) { + case DLL_PROCESS_ATTACH: + { + char **modName; + for (modName = possibleModules;*modName;*modName++) { + BOOL ok = CallModuleDllMain(*modName, dwReason); + if (!ok) + ret = FALSE; + } + break; + } + case DLL_PROCESS_DETACH: + { + // Must go backwards + char **modName; + for (modName = possibleModules+(sizeof(possibleModules) / sizeof(char *))-2; + modName >= possibleModules; + *modName--) + CallModuleDllMain(*modName, DLL_PROCESS_DETACH); + break; + } + } + return ret; +} + +BOOL CallModuleDllMain(char *modName, DWORD dwReason) +{ + BOOL (WINAPI * pfndllmain)(HINSTANCE, DWORD, LPVOID); + + char funcName[255]; + HMODULE hmod = GetModuleHandleW(NULL); + strcpy(funcName, "_DllMain"); + strcat(funcName, modName); + strcat(funcName, "@12"); // stdcall convention. + pfndllmain = (BOOL (WINAPI *)(HINSTANCE, DWORD, LPVOID))GetProcAddress(hmod, funcName); + if (pfndllmain==NULL) { + /* No function by that name exported - then that module does + not appear in our frozen program - return OK + */ + return TRUE; + } + return (*pfndllmain)(hmod, dwReason, NULL); +} + diff --git a/pandatool/src/eggprogs/eggMakeTube.cxx b/pandatool/src/eggprogs/eggMakeTube.cxx index 7017e97dcd..85ec85081d 100644 --- a/pandatool/src/eggprogs/eggMakeTube.cxx +++ b/pandatool/src/eggprogs/eggMakeTube.cxx @@ -29,7 +29,7 @@ EggMakeTube() { set_program_description ("egg-make-tube generates an egg file representing a \"tube\" model, " "a cylinder capped on both ends by hemispheres. This is similar " - "in shape to the CollisionTube object within Panda.\n\n" + "in shape to the CollisionCapsule object within Panda.\n\n" "This program can also generate spheres if you omit -b; in this " "case, you are generating a degenerate tube of length 0."); diff --git a/pandatool/src/eggprogs/eggMakeTube.h b/pandatool/src/eggprogs/eggMakeTube.h index 520d178aee..3031b1c74e 100644 --- a/pandatool/src/eggprogs/eggMakeTube.h +++ b/pandatool/src/eggprogs/eggMakeTube.h @@ -24,7 +24,7 @@ class EggVertex; /** * A program to generate an egg file representing a tube model, similar in - * shape to a CollisionTube. + * shape to a CollisionCapsule. */ class EggMakeTube : public EggMakeSomething { public: diff --git a/pandatool/src/mayaegg/mayaEggLoader.cxx b/pandatool/src/mayaegg/mayaEggLoader.cxx index 68fcb8d0f7..a60e5d2372 100644 --- a/pandatool/src/mayaegg/mayaEggLoader.cxx +++ b/pandatool/src/mayaegg/mayaEggLoader.cxx @@ -975,7 +975,9 @@ MayaEggMesh *MayaEggLoader::GetMesh(EggVertexPool *pool, EggGroup *parent) MayaEggMesh *result = _mesh_tab[parent]; if (result == 0) { result = new MayaEggMesh; - result->_name = parent->get_name(); + if (parent != nullptr) { + result->_name = parent->get_name(); + } result->_pool = pool; result->_parent = parent; result->_vert_count = 0; diff --git a/samples/asteroids/requirements.txt b/samples/asteroids/requirements.txt new file mode 100644 index 0000000000..a8e341eee1 --- /dev/null +++ b/samples/asteroids/requirements.txt @@ -0,0 +1,2 @@ +--pre --extra-index-url https://archive.panda3d.org/branches/deploy-ng +panda3d diff --git a/samples/asteroids/setup.py b/samples/asteroids/setup.py new file mode 100644 index 0000000000..95a3e5259f --- /dev/null +++ b/samples/asteroids/setup.py @@ -0,0 +1,23 @@ +from setuptools import setup + +setup( + name="asteroids", + options = { + 'build_apps': { + 'include_patterns': [ + '**/*.png', + '**/*.jpg', + '**/*.egg', + ], + 'gui_apps': { + 'asteroids': 'main.py', + }, + 'log_filename': '$USER_APPDATA/Asteroids/output.log', + 'log_append': False, + 'plugins': [ + 'pandagl', + 'p3openal_audio', + ], + } + } +) diff --git a/samples/cartoon-shader/normalGen.sha b/samples/cartoon-shader/normalGen.sha index a3642b0630..53b994b1ae 100644 --- a/samples/cartoon-shader/normalGen.sha +++ b/samples/cartoon-shader/normalGen.sha @@ -3,14 +3,14 @@ //Cg profile arbvp1 arbfp1 void vshader(float4 vtx_position : POSITION, - float4 vtx_normal : NORMAL, - out float4 l_position : POSITION, - out float3 l_color : TEXCOORD0, - uniform float4x4 mat_modelproj, - uniform float4x4 itp_modelview) + float3 vtx_normal : NORMAL, + out float4 l_position : POSITION, + out float3 l_color : TEXCOORD0, + uniform float4x4 mat_modelproj, + uniform float4x4 itp_modelview) { - l_position=mul(mat_modelproj, vtx_position); - l_color=(float3)mul(itp_modelview, vtx_normal); + l_position = mul(mat_modelproj, vtx_position); + l_color = (float3)mul(itp_modelview, float4(vtx_normal, 0)); } void fshader(float3 l_color: TEXCOORD0, @@ -18,8 +18,6 @@ void fshader(float3 l_color: TEXCOORD0, { l_color = normalize(l_color); l_color = l_color/2; - o_color.rgb = l_color + float4(0.5, 0.5, 0.5, 0.5); + o_color.rgb = l_color.rgb + float3(0.5, 0.5, 0.5); o_color.a = 1; } - - diff --git a/samples/fireflies/model.sha b/samples/fireflies/model.sha index f1867ee5da..0c2e7fd920 100644 --- a/samples/fireflies/model.sha +++ b/samples/fireflies/model.sha @@ -4,7 +4,7 @@ void vshader(float4 vtx_position : POSITION, float2 vtx_texcoord0 : TEXCOORD0, - float4 vtx_normal : NORMAL, + float3 vtx_normal : NORMAL, float4 vtx_color : COLOR, out float4 l_position : POSITION, out float2 l_texcoord0 : TEXCOORD0, @@ -16,7 +16,7 @@ void vshader(float4 vtx_position : POSITION, l_position=mul(mat_modelproj, vtx_position); l_texcoord0 = vtx_texcoord0; l_color = vtx_color; - l_normal = (float3)mul(itp_modelview, vtx_normal); + l_normal = (float3)mul(itp_modelview, float4(vtx_normal, 0)); } void fshader(float2 l_texcoord0: TEXCOORD0, diff --git a/samples/gamepad/device_tester.py b/samples/gamepad/device_tester.py new file mode 100644 index 0000000000..ee2fad6c30 --- /dev/null +++ b/samples/gamepad/device_tester.py @@ -0,0 +1,458 @@ +#!/usr/bin/env python + +import sys + +from direct.showbase.ShowBase import ShowBase +from direct.showbase.DirectObject import DirectObject +from panda3d.core import InputDeviceManager, InputDevice +from panda3d.core import VBase4, Vec2 +from panda3d.core import TextNode +from direct.gui.DirectGui import ( + DGG, + DirectFrame, + DirectButton, + DirectLabel, + DirectScrolledFrame, + DirectSlider, +) + + +class Main(ShowBase): + def __init__(self): + super().__init__() + base.disableMouse() + self.accept("escape", sys.exit) + self.device_connectivity_monitor = DeviceConnectivityMonitor() + + +class DeviceConnectivityMonitor(DirectObject): + def __init__(self): + super().__init__() + self.mgr = InputDeviceManager.get_global_ptr() + self.create_device_menu() + + self.devices = {} + for device in self.mgr.get_devices(): + self.connect_device(device) + + self.accept("connect-device", self.connect_device) + self.accept("disconnect-device", self.disconnect_device) + + def create_device_menu(self): + self.current_panel = None + self.buttons = {} + self.devices_frame = DirectScrolledFrame( + frameSize=VBase4( + 0, + base.a2dLeft*-0.75, + base.a2dBottom - base.a2dTop, + 0, + ), + frameColor=VBase4(0, 0, 0.25, 1.0), + canvasSize=VBase4( + 0, + base.a2dLeft*-0.75, + 0, + 0, + ), + scrollBarWidth=0.08, + manageScrollBars=True, + autoHideScrollBars=True, + pos=(base.a2dLeft, 0, base.a2dTop), + parent=base.aspect2d, + ) + + self.devices_frame.setCanvasSize() + + def create_menu_button(self, device): + button = DirectButton( + command=self.switch_to_panel, + extraArgs=[device], + text=device.name, + text_scale=0.05, + text_align=TextNode.ALeft, + text_fg=VBase4(0.0, 0.0, 0.0, 1.0), + text_pos=Vec2(0.01, base.a2dBottom / 10.0), + relief=1, + pad=Vec2(0.01, 0.01), + frameColor=VBase4(0.8, 0.8, 0.8, 1.0), + frameSize=VBase4( + 0.0, + base.a2dLeft*-0.75 - 0.081, # 0.08=Scrollbar, 0.001=inaccuracy + base.a2dBottom / 5.0, + 0.0, + ), + parent=self.devices_frame.getCanvas(), + ) + self.buttons[device] = button + + def destroy_menu_button(self, device): + self.buttons[device].detach_node() + del self.buttons[device] + + def refresh_device_menu(self): + self.devices_frame['canvasSize'] = VBase4( + 0, + base.a2dLeft*-0.75, + base.a2dBottom / 5.0 * len(self.buttons), + 0, + ) + self.devices_frame.setCanvasSize() + sorted_buttons = sorted(self.buttons.items(), key=lambda i: i[0].name) + for idx, (dev, button) in enumerate(sorted_buttons): + button.set_pos( + 0, + 0, + (base.a2dBottom / 5.0) * idx, + ) + + def switch_to_panel(self, device): + if self.current_panel is not None: + self.devices[self.current_panel].hide() + self.current_panel = device + self.devices[self.current_panel].show() + + def connect_device(self, device): + if device in self.devices: + return + self.devices[device] = DeviceMonitor(device) + self.switch_to_panel(device) + self.create_menu_button(device) + self.refresh_device_menu() + + def disconnect_device(self, device): + self.devices[device].deactivate() + del self.devices[device] + if self.current_panel == device: + self.current_panel = None + if len(self.devices) > 0: + active_device = sorted( + self.devices.keys(), + key=lambda d: d.name, + )[0] + self.switch_to_panel(active_device) + + self.destroy_menu_button(device) + self.refresh_device_menu() + + +class DeviceMonitor(DirectObject): + def __init__(self, device): + super().__init__() + self.device = device + self.create_panel() + self.activate() + self.hide() + + def activate(self): + print("Device connected") + print(" Name : {}".format(self.device.name)) + print(" Type : {}".format(self.device.device_class.name)) + print(" Manufacturer: {}".format(self.device.manufacturer)) + print(" ID : {:04x}:{:04x}".format(self.device.vendor_id, + self.device.product_id)) + axis_names = [axis.axis.name for axis in self.device.axes] + print(" Axes : {} ({})".format(len(self.device.axes), + ', '.join(axis_names))) + button_names = [button.handle.name for button in self.device.buttons] + print(" Buttons : {} ({})".format(len(self.device.buttons), + ', '.join(button_names))) + + base.attachInputDevice(self.device) + + self.task = base.taskMgr.add( + self.update, + "Monitor for {}".format(self.device.name), + sort=10, + ) + + def deactivate(self): + print("\"{}\" disconnected".format(self.device.name)) + base.taskMgr.remove(self.task) + self.panel.detach_node() + + def create_panel(self): + panel_width = base.a2dLeft * -0.25 + base.a2dRight + scroll_bar_width = 0.08 + # NOTE: -0.001 because thanks to inaccuracy the vertical bar appears... + canvas_width = panel_width - scroll_bar_width - 0.001 + canvas_height = base.a2dBottom - base.a2dTop + + self.panel = DirectScrolledFrame( + frameSize=VBase4( + 0, + panel_width, + canvas_height, + 0, + ), + frameColor=VBase4(0.8, 0.8, 0.8, 1), + canvasSize=VBase4( + 0, + canvas_width, + canvas_height, + 0, + ), + scrollBarWidth=scroll_bar_width, + manageScrollBars=True, + autoHideScrollBars=True, + pos=(base.a2dLeft * 0.25, 0, base.a2dTop), + parent=base.aspect2d, + ) + panel_canvas = self.panel.getCanvas() + offset = -0.0 + + # Style sheets + + half_width_entry = dict( + frameSize=VBase4( + 0, + canvas_width / 2, + -0.1, + 0, + ), + parent=panel_canvas, + frameColor=VBase4(0.8, 0.8, 0.8, 1), + ) + left_aligned_small_text = dict( + text_align=TextNode.ALeft, + text_scale=0.05, + text_fg=VBase4(0,0,0,1), + text_pos=(0.05, -0.06), + ) + half_width_text_frame = dict( + **half_width_entry, + **left_aligned_small_text, + ) + + header = dict( + frameSize=VBase4( + 0, + canvas_width, + -0.1, + 0, + ), + parent=panel_canvas, + frameColor=VBase4(0.6, 0.6, 0.6, 1), + text_align=TextNode.ALeft, + text_scale=0.1, + text_fg=VBase4(0,0,0,1), + text_pos=(0.05, -0.075), + ) + + # Basic device data (name, device class, manufacturer, USB ID) + + self.device_header = DirectLabel( + text="Device data", + pos=(0, 0, offset), + **header, + ) + offset -= 0.1 + + def add_data_entry(offset, label, text): + self.name = DirectLabel( + text=label, + pos=(0, 0, offset), + **half_width_text_frame, + ) + self.name = DirectLabel( + text=text, + pos=(canvas_width / 2, 0, offset), + **half_width_text_frame, + ) + + metadata = [ + ('Name', self.device.name), + ('Device class', self.device.device_class.name), + ('Manufacturer', self.device.manufacturer), + ('USB ID', + "{:04x}:{:04x}".format( + self.device.vendor_id, + self.device.product_id, + ), + ), + ] + for label, text in metadata: + add_data_entry(offset, label, text) + offset -= 0.1 + + # Axes + + self.axis_sliders = [] + if len(self.device.axes) > 0: + offset -= 0.1 + self.axes_header = DirectLabel( + text="Axes", + pos=(0, 0, offset), + **header, + ) + offset -= 0.1 + + def add_axis(offset, axis_name): + slider_width = canvas_width / 2 + label = DirectLabel( + text=axis_name, + **left_aligned_small_text, + pos=(0.05, 0, offset), + parent=panel_canvas, + ) + slider = DirectSlider( + value=0.0, + range=(-1.0, 1.0), + state=DGG.DISABLED, + frameSize=VBase4( + 0, + slider_width, + -0.1, + 0, + ), + thumb_frameSize=VBase4( + 0.0, + 0.04, + -0.04, + 0.04), + frameColor=VBase4(0.3, 0.3, 0.3, 1), + pos=(canvas_width - slider_width, 0, offset), + parent=panel_canvas, + ) + return slider + + for axis in self.device.axes: + axis_slider = add_axis(offset, axis.axis.name) + self.axis_sliders.append(axis_slider) + offset -= 0.1 + + # Buttons + + self.button_buttons = [] + if len(self.device.buttons) > 0: + offset -= 0.1 + self.buttons_header = DirectLabel( + text="Buttons", + pos=(0, 0, offset), + **header, + ) + offset -= 0.1 + + def add_button(offset, button_name): + button_width = canvas_width / 2 + label = DirectLabel( + text=button_name, + **left_aligned_small_text, + pos=(0.05, 0, offset), + parent=panel_canvas, + ) + button = DirectFrame( + frameSize=VBase4( + 0, + button_width, + -0.1, + 0, + ), + text="", + text_align=TextNode.ACenter, + text_scale=0.05, + text_fg=VBase4(0,0,0,1), + text_pos=(button_width / 2, -0.06), + frameColor=VBase4(0.3, 0.3, 0.3, 1), + pos=(canvas_width - button_width, 0, offset), + parent=panel_canvas, + ) + return button + + for i in range(len(self.device.buttons)): + button_name = self.device.buttons[i].handle.name + button_button = add_button(offset, button_name) + self.button_buttons.append(button_button) + offset -= 0.1 + + # Vibration + + self.vibration = [] + if self.device.has_feature(InputDevice.Feature.vibration): + offset -= 0.1 + self.vibration_header = DirectLabel( + text="Vibration", + pos=(0, 0, offset), + **header, + ) + offset -= 0.1 + + def add_vibration(offset, axis_name, index): + slider_width = canvas_width / 2 + label = DirectLabel( + text=axis_name, + **left_aligned_small_text, + pos=(0.05, 0, offset), + parent=panel_canvas, + ) + slider = DirectSlider( + value=0.0, + range=(0.0, 1.0), + command=self.update_vibration, + frameSize=VBase4( + 0, + slider_width, + -0.1, + 0, + ), + thumb_frameSize=VBase4( + 0.0, + 0.04, + -0.04, + 0.04), + frameColor=VBase4(0.3, 0.3, 0.3, 1), + pos=(canvas_width - slider_width, 0, offset), + parent=panel_canvas, + ) + return slider + + for index, name in enumerate(["low frequency", "high frequency"]): + self.vibration.append(add_vibration(offset, name, index)) + offset -= 0.1 + + # Resize the panel's canvas to the widgets actually in it. + if -offset > -canvas_height: + self.panel['canvasSize'] = VBase4( + 0, + canvas_width, + offset, + 0, + ) + self.panel.setCanvasSize() + + def show(self): + # FIXME: Activate update task here, and deactivate it in hide()? + self.panel.show() + + def hide(self): + self.panel.hide() + + def update_vibration(self): + low = self.vibration[0]['value'] + high = self.vibration[1]['value'] + self.device.set_vibration(low, high) + + def update(self, task): + # FIXME: There needs to be a demo of events here, too. + for idx, slider in enumerate(self.axis_sliders): + slider["value"] = self.device.axes[idx].value + for idx, button in enumerate(self.button_buttons): + if self.device.buttons[idx].known: + if self.device.buttons[idx].pressed: + button['frameColor'] = VBase4(0.0, 0.8, 0.0, 1) + button['text'] = "down" + else: + button['frameColor'] = VBase4(0.3, 0.3, 0.3, 1) + button['text'] = "up" + else: + # State is InputDevice.S_unknown. This happens if the device + # manager hasn't polled yet, and in some cases before a button + # has been pressed after the program's start. + button['frameColor'] = VBase4(0.8, 0.8, 0.0, 1) + button['text'] = "unknown" + return task.cont + + +if __name__ == '__main__': + main = Main() + main.run() diff --git a/samples/gamepad/flightstick.py b/samples/gamepad/flightstick.py new file mode 100644 index 0000000000..4520cfefb2 --- /dev/null +++ b/samples/gamepad/flightstick.py @@ -0,0 +1,169 @@ +#!/usr/bin/env python +''' +Demonstrate usage of flight stick + +In this sample you can use a flight stick to control the camera and show some +messages on screen. You can accelerate using the throttle. +''' + +from direct.showbase.ShowBase import ShowBase +from panda3d.core import TextNode, InputDevice, loadPrcFileData, Vec3 +from direct.gui.OnscreenText import OnscreenText + +loadPrcFileData("", """ + default-fov 60 + notify-level-device debug +""") + +STICK_DEAD_ZONE = 0.02 +THROTTLE_DEAD_ZONE = 0.02 + + +class App(ShowBase): + def __init__(self): + ShowBase.__init__(self) + # Print all events sent through the messenger + #self.messenger.toggleVerbose() + + self.lblWarning = OnscreenText( + text = "No devices found", + fg=(1,0,0,1), + scale = .25) + + self.lblAction = OnscreenText( + text = "Action", + fg=(1,1,1,1), + scale = .15) + self.lblAction.hide() + + # Is there a gamepad connected? + self.flightStick = None + devices = self.devices.getDevices(InputDevice.DeviceClass.flight_stick) + if devices: + self.connect(devices[0]) + + self.currentMoveSpeed = 0.0 + self.maxAccleration = 28.0 + self.deaccleration = 10.0 + self.deaclerationBreak = 37.0 + self.maxSpeed = 80.0 + + # Accept device dis-/connection events + self.accept("connect-device", self.connect) + self.accept("disconnect-device", self.disconnect) + + self.accept("escape", exit) + self.accept("flight_stick0-start", exit) + + # Accept button events of the first connected flight stick + self.accept("flight_stick0-trigger", self.action, extraArgs=["Trigger"]) + self.accept("flight_stick0-trigger-up", self.actionUp) + + self.environment = loader.loadModel("environment") + self.environment.reparentTo(render) + + # disable pandas default mouse-camera controls so we can handle the camera + # movements by ourself + self.disableMouse() + self.reset() + + self.taskMgr.add(self.moveTask, "movement update task") + + def connect(self, device): + """Event handler that is called when a device is discovered.""" + + # We're only interested if this is a flight stick and we don't have a + # flight stick yet. + if device.device_class == InputDevice.DeviceClass.flight_stick and not self.flightStick: + print("Found %s" % (device)) + self.flightStick = device + + # Enable this device to ShowBase so that we can receive events. + # We set up the events with a prefix of "flight_stick0-". + self.attachInputDevice(device, prefix="flight_stick0") + + # Hide the warning that we have no devices. + self.lblWarning.hide() + + def disconnect(self, device): + """Event handler that is called when a device is removed.""" + + if self.flightStick != device: + # We don't care since it's not our gamepad. + return + + # Tell ShowBase that the device is no longer needed. + print("Disconnected %s" % (device)) + self.detachInputDevice(device) + self.flightStick = None + + # Do we have any other gamepads? Attach the first other gamepad. + devices = self.devices.getDevices(InputDevice.DeviceClass.flight_stick) + if devices: + self.connect(devices[0]) + else: + # No devices. Show the warning. + self.lblWarning.show() + + def reset(self): + """Reset the camera to the initial position.""" + + self.camera.setPosHpr(0, -200, 10, 0, 0, 0) + + def action(self, button): + # Just show which button has been pressed. + self.lblAction.text = "Pressed %s" % button + self.lblAction.show() + + def actionUp(self): + # Hide the label showing which button is pressed. + self.lblAction.hide() + + def moveTask(self, task): + dt = globalClock.getDt() + + if not self.flightStick: + return task.cont + + if self.currentMoveSpeed > 0: + self.currentMoveSpeed -= dt * self.deaccleration + if self.currentMoveSpeed < 0: + self.currentMoveSpeed = 0 + + # Accelerate using the throttle. Apply deadzone of 0.01. + throttle = self.flightStick.findAxis(InputDevice.Axis.throttle).value + if abs(throttle) < THROTTLE_DEAD_ZONE: + throttle = 0 + accleration = throttle * self.maxAccleration + if self.currentMoveSpeed > throttle * self.maxSpeed: + self.currentMoveSpeed -= dt * self.deaccleration + self.currentMoveSpeed += dt * accleration + + # Steering + + # Control the cameras yaw/Headding + stick_yaw = self.flightStick.findAxis(InputDevice.Axis.yaw) + if abs(stick_yaw.value) > STICK_DEAD_ZONE: + base.camera.setH(base.camera, 100 * dt * stick_yaw.value) + + # Control the cameras pitch + stick_y = self.flightStick.findAxis(InputDevice.Axis.pitch) + if abs(stick_y.value) > STICK_DEAD_ZONE: + base.camera.setP(base.camera, 100 * dt * stick_y.value) + + # Control the cameras roll + stick_x = self.flightStick.findAxis(InputDevice.Axis.roll) + if abs(stick_x.value) > STICK_DEAD_ZONE: + base.camera.setR(base.camera, 100 * dt * stick_x.value) + + # calculate movement + base.camera.setY(base.camera, dt * self.currentMoveSpeed) + + # Make sure camera does not go below the ground. + if base.camera.getZ() < 1: + base.camera.setZ(1) + + return task.cont + +app = App() +app.run() diff --git a/samples/gamepad/gamepad.py b/samples/gamepad/gamepad.py new file mode 100644 index 0000000000..8679a74187 --- /dev/null +++ b/samples/gamepad/gamepad.py @@ -0,0 +1,195 @@ +#!/usr/bin/env python +''' +Demonstrate usage of gamepads and other input devices + +In this sample you can use a gamepad type device to control the camera and +show some messages on screen. Using the left stick on the controler will +move the camera where the right stick will rotate the camera. +''' + +from direct.showbase.ShowBase import ShowBase +from panda3d.core import TextNode, InputDevice, loadPrcFileData, Vec3 +from panda3d.core import TextPropertiesManager +from direct.gui.OnscreenText import OnscreenText + +loadPrcFileData("", """ + default-fov 60 + notify-level-device debug +""") + +# Informational text in the bottom-left corner. We use the special \5 +# character to embed an image representing the gamepad buttons. +INFO_TEXT = """Move \5lstick\5 to strafe, \5rstick\5 to turn +Press \5ltrigger\5 and \5rtrigger\5 to go down/up +Press \5face_x\5 to reset camera""" + + +class App(ShowBase): + def __init__(self): + ShowBase.__init__(self) + # Print all events sent through the messenger + #self.messenger.toggleVerbose() + + # Load the graphics for the gamepad buttons and register them, so that + # we can embed them in our information text. + graphics = loader.loadModel("models/xbone-icons.egg") + mgr = TextPropertiesManager.getGlobalPtr() + for name in ["face_a", "face_b", "face_x", "face_y", "ltrigger", "rtrigger", "lstick", "rstick"]: + graphic = graphics.find("**/" + name) + graphic.setScale(1.5) + mgr.setGraphic(name, graphic) + graphic.setZ(-0.5) + + # Show the informational text in the corner. + self.lblInfo = OnscreenText( + parent = self.a2dBottomLeft, + pos = (0.1, 0.3), + fg = (1, 1, 1, 1), + bg = (0.2, 0.2, 0.2, 0.9), + align = TextNode.A_left, + text = INFO_TEXT) + self.lblInfo.textNode.setCardAsMargin(0.5, 0.5, 0.5, 0.2) + + self.lblWarning = OnscreenText( + text = "No devices found", + fg=(1,0,0,1), + scale = .25) + + self.lblAction = OnscreenText( + text = "Action", + fg=(1,1,1,1), + scale = .15) + self.lblAction.hide() + + # Is there a gamepad connected? + self.gamepad = None + devices = self.devices.getDevices(InputDevice.DeviceClass.gamepad) + if devices: + self.connect(devices[0]) + + # Accept device dis-/connection events + self.accept("connect-device", self.connect) + self.accept("disconnect-device", self.disconnect) + + self.accept("escape", exit) + + # Accept button events of the first connected gamepad + self.accept("gamepad-back", exit) + self.accept("gamepad-start", exit) + self.accept("gamepad-face_x", self.reset) + self.accept("gamepad-face_a", self.action, extraArgs=["face_a"]) + self.accept("gamepad-face_a-up", self.actionUp) + self.accept("gamepad-face_b", self.action, extraArgs=["face_b"]) + self.accept("gamepad-face_b-up", self.actionUp) + self.accept("gamepad-face_y", self.action, extraArgs=["face_y"]) + self.accept("gamepad-face_y-up", self.actionUp) + + self.environment = loader.loadModel("environment") + self.environment.reparentTo(render) + + # Disable the default mouse-camera controls since we need to handle + # our own camera controls. + self.disableMouse() + self.reset() + + self.taskMgr.add(self.moveTask, "movement update task") + + def connect(self, device): + """Event handler that is called when a device is discovered.""" + + # We're only interested if this is a gamepad and we don't have a + # gamepad yet. + if device.device_class == InputDevice.DeviceClass.gamepad and not self.gamepad: + print("Found %s" % (device)) + self.gamepad = device + + # Enable this device to ShowBase so that we can receive events. + # We set up the events with a prefix of "gamepad-". + self.attachInputDevice(device, prefix="gamepad") + + # Hide the warning that we have no devices. + self.lblWarning.hide() + + def disconnect(self, device): + """Event handler that is called when a device is removed.""" + + if self.gamepad != device: + # We don't care since it's not our gamepad. + return + + # Tell ShowBase that the device is no longer needed. + print("Disconnected %s" % (device)) + self.detachInputDevice(device) + self.gamepad = None + + # Do we have any other gamepads? Attach the first other gamepad. + devices = self.devices.getDevices(InputDevice.DeviceClass.gamepad) + if devices: + self.connect(devices[0]) + else: + # No devices. Show the warning. + self.lblWarning.show() + + def reset(self): + """Reset the camera to the initial position.""" + + self.camera.setPosHpr(0, -200, 10, 0, 0, 0) + + def action(self, button): + # Just show which button has been pressed. + self.lblAction.text = "Pressed \5%s\5" % button + self.lblAction.show() + + def actionUp(self): + # Hide the label showing which button is pressed. + self.lblAction.hide() + + def moveTask(self, task): + dt = globalClock.getDt() + + if not self.gamepad: + return task.cont + + strafe_speed = 85 + vert_speed = 50 + turn_speed = 100 + + # If the left stick is pressed, we will go faster. + lstick = self.gamepad.findButton("lstick") + if lstick.pressed: + strafe_speed *= 2.0 + + # we will use the first found gamepad + # Move the camera left/right + strafe = Vec3(0) + left_x = self.gamepad.findAxis(InputDevice.Axis.left_x) + left_y = self.gamepad.findAxis(InputDevice.Axis.left_y) + strafe.x = left_x.value + strafe.y = left_y.value + + # Apply some deadzone, since the sticks don't center exactly at 0 + if strafe.lengthSquared() >= 0.01: + self.camera.setPos(self.camera, strafe * strafe_speed * dt) + + # Use the triggers for the vertical position. + trigger_l = self.gamepad.findAxis(InputDevice.Axis.left_trigger) + trigger_r = self.gamepad.findAxis(InputDevice.Axis.right_trigger) + lift = trigger_r.value - trigger_l.value + self.camera.setZ(self.camera.getZ() + (lift * vert_speed * dt)) + + # Move the camera forward/backward + right_x = self.gamepad.findAxis(InputDevice.Axis.right_x) + right_y = self.gamepad.findAxis(InputDevice.Axis.right_y) + + # Again, some deadzone + if abs(right_x.value) >= 0.1 or abs(right_y.value) >= 0.1: + self.camera.setH(self.camera, turn_speed * dt * -right_x.value) + self.camera.setP(self.camera, turn_speed * dt * right_y.value) + + # Reset the roll so that the camera remains upright. + self.camera.setR(0) + + return task.cont + +app = App() +app.run() diff --git a/samples/gamepad/mappingGUI.py b/samples/gamepad/mappingGUI.py new file mode 100644 index 0000000000..17896c33a6 --- /dev/null +++ b/samples/gamepad/mappingGUI.py @@ -0,0 +1,401 @@ +#!/usr/bin/env python +''' +Demonstrate how a simple button mapping gui can be written +''' + +from direct.showbase.ShowBase import ShowBase +from direct.gui.DirectGui import ( + DGG, + DirectFrame, + DirectButton, + DirectLabel, + OkCancelDialog, + DirectScrolledFrame) +from panda3d.core import ( + VBase4, + TextNode, + Vec2, + InputDevice, + loadPrcFileData, + GamepadButton, + KeyboardButton) + +# Make sure the textures look crisp on every device that supports +# non-power-2 textures +loadPrcFileData("", "textures-auto-power-2 #t") + +# How much an axis should have moved for it to register as a movement. +DEAD_ZONE = 0.33 + + +class InputMapping(object): + """A container class for storing a mapping from a string action to either + an axis or a button. You could extend this with additional methods to load + the default mappings from a configuration file. """ + + # Define all the possible actions. + actions = ( + "Move forward", "Move backward", "Move left", "Move right", "Jump", + "Buy", "Use", "Break", "Fix", "Trash", "Change", "Mail", "Upgrade", + ) + + def __init__(self): + self.__map = dict.fromkeys(self.actions) + + def mapButton(self, action, button): + self.__map[action] = ("button", str(button)) + + def mapAxis(self, action, axis): + self.__map[action] = ("axis", axis.name) + + def unmap(self): + self.__map[action] = None + + def formatMapping(self, action): + """Returns a string label describing the mapping for a given action, + for displaying in a GUI. """ + mapping = self.__map.get(action) + if not mapping: + return "Unmapped" + + # Format the symbolic string from Panda nicely. In a real-world game, + # you might want to look these up in a translation table, or so. + label = mapping[1].replace('_', ' ').title() + if mapping[0] == "axis": + return "Axis: " + label + else: + return "Button: " + label + + +class ChangeActionDialog(object): + """Encapsulates the UI dialog that opens up when changing a mapping. It + holds the state of which action is being set and which button is pressed + and invokes a callback function when the dialog is exited.""" + + def __init__(self, action, button_geom, command): + # This stores which action we are remapping. + self.action = action + + # This will store the key/axis that we want to asign to an action + self.newInputType = "" + self.newInput = "" + self.setKeyCalled = False + + self.__command = command + + self.attachedDevices = [] + + # Initialize the DirectGUI stuff. + self.dialog = OkCancelDialog( + dialogName="dlg_device_input", + pos=(0, 0, 0.25), + text="Hit desired key:", + text_fg=VBase4(0.898, 0.839, 0.730, 1.0), + text_shadow=VBase4(0, 0, 0, 0.75), + text_shadowOffset=Vec2(0.05, 0.05), + text_scale=0.05, + text_align=TextNode.ACenter, + fadeScreen=0.65, + frameColor=VBase4(0.3, 0.3, 0.3, 1), + button_geom=button_geom, + button_scale=0.15, + button_text_scale=0.35, + button_text_align=TextNode.ALeft, + button_text_fg=VBase4(0.898, 0.839, 0.730, 1.0), + button_text_pos=Vec2(-0.9, -0.125), + button_relief=1, + button_pad=Vec2(0.01, 0.01), + button_frameColor=VBase4(0, 0, 0, 0), + button_frameSize=VBase4(-1.0, 1.0, -0.25, 0.25), + button_pressEffect=False, + command=self.onClose) + self.dialog.setTransparency(True) + self.dialog.configureDialog() + scale = self.dialog["image_scale"] + self.dialog["image_scale"] = (scale[0]/2.0, scale[1], scale[2]/2.0) + self.dialog["text_pos"] = (self.dialog["text_pos"][0], self.dialog["text_pos"][1] + 0.06) + + def buttonPressed(self, button): + if any(button.guiItem.getState() == 1 for button in self.dialog.buttonList): + # Ignore events while any of the dialog buttons are active, because + # otherwise we register mouse clicks when the user is trying to + # exit the dialog. + return + + text = str(button).replace('_', ' ').title() + self.dialog["text"] = "New event will be:\n\nButton: " + text + self.newInputType = "button" + self.newInput = button + + def axisMoved(self, axis): + text = axis.name.replace('_', ' ').title() + self.dialog["text"] = "New event will be:\n\nAxis: " + text + self.newInputType = "axis" + self.newInput = axis + + def onClose(self, result): + """Called when the OK or Cancel button is pressed.""" + self.dialog.cleanup() + + # Call the constructor-supplied callback with our new setting, if any. + if self.newInput and result == DGG.DIALOG_OK: + self.__command(self.action, self.newInputType, self.newInput) + else: + # Cancel (or no input was pressed) + self.__command(self.action, None, None) + + +class MappingGUIDemo(ShowBase): + def __init__(self): + ShowBase.__init__(self) + + self.setBackgroundColor(0, 0, 0) + # make the font look nice at a big scale + DGG.getDefaultFont().setPixelsPerUnit(100) + + # Store our mapping, with some sensible defaults. In a real game, you + # will want to load these from a configuration file. + self.mapping = InputMapping() + self.mapping.mapAxis("Move forward", InputDevice.Axis.left_y) + self.mapping.mapAxis("Move backward", InputDevice.Axis.left_y) + self.mapping.mapAxis("Move left", InputDevice.Axis.left_x) + self.mapping.mapAxis("Move right", InputDevice.Axis.left_x) + self.mapping.mapButton("Jump", GamepadButton.face_a()) + self.mapping.mapButton("Use", GamepadButton.face_b()) + self.mapping.mapButton("Break", GamepadButton.face_x()) + self.mapping.mapButton("Fix", GamepadButton.face_y()) + + # The geometry for our basic buttons + maps = loader.loadModel("models/button_map") + self.buttonGeom = ( + maps.find("**/ready"), + maps.find("**/click"), + maps.find("**/hover"), + maps.find("**/disabled")) + + # Change the default dialog skin. + DGG.setDefaultDialogGeom("models/dialog.png") + + # create a sample title + self.textscale = 0.1 + self.title = DirectLabel( + scale=self.textscale, + pos=(base.a2dLeft + 0.05, 0.0, base.a2dTop - (self.textscale + 0.05)), + frameColor=VBase4(0, 0, 0, 0), + text="Button Mapping", + text_align=TextNode.ALeft, + text_fg=VBase4(1, 1, 1, 1), + text_shadow=VBase4(0, 0, 0, 0.75), + text_shadowOffset=Vec2(0.05, 0.05)) + self.title.setTransparency(1) + + # Set up the list of actions that we can map keys to + # create a frame that will create the scrollbars for us + # Load the models for the scrollbar elements + thumbMaps = loader.loadModel("models/thumb_map") + thumbGeom = ( + thumbMaps.find("**/thumb_ready"), + thumbMaps.find("**/thumb_click"), + thumbMaps.find("**/thumb_hover"), + thumbMaps.find("**/thumb_disabled")) + incMaps = loader.loadModel("models/inc_map") + incGeom = ( + incMaps.find("**/inc_ready"), + incMaps.find("**/inc_click"), + incMaps.find("**/inc_hover"), + incMaps.find("**/inc_disabled")) + decMaps = loader.loadModel("models/dec_map") + decGeom = ( + decMaps.find("**/dec_ready"), + decMaps.find("**/dec_click"), + decMaps.find("**/dec_hover"), + decMaps.find("**/dec_disabled")) + + # create the scrolled frame that will hold our list + self.lstActionMap = DirectScrolledFrame( + # make the frame occupy the whole window + frameSize=VBase4(base.a2dLeft, base.a2dRight, 0.0, 1.55), + # make the canvas as big as the frame + canvasSize=VBase4(base.a2dLeft, base.a2dRight, 0.0, 0.0), + # set the frames color to white + frameColor=VBase4(0, 0, 0.25, 0.75), + pos=(0, 0, -0.8), + + verticalScroll_scrollSize=0.2, + verticalScroll_frameColor=VBase4(0.02, 0.02, 0.02, 1), + + verticalScroll_thumb_relief=1, + verticalScroll_thumb_geom=thumbGeom, + verticalScroll_thumb_pressEffect=False, + verticalScroll_thumb_frameColor=VBase4(0, 0, 0, 0), + + verticalScroll_incButton_relief=1, + verticalScroll_incButton_geom=incGeom, + verticalScroll_incButton_pressEffect=False, + verticalScroll_incButton_frameColor=VBase4(0, 0, 0, 0), + + verticalScroll_decButton_relief=1, + verticalScroll_decButton_geom=decGeom, + verticalScroll_decButton_pressEffect=False, + verticalScroll_decButton_frameColor=VBase4(0, 0, 0, 0),) + + # creat the list items + idx = 0 + self.listBGEven = base.loader.loadModel("models/list_item_even") + self.listBGOdd = base.loader.loadModel("models/list_item_odd") + self.actionLabels = {} + for action in self.mapping.actions: + mapped = self.mapping.formatMapping(action) + item = self.__makeListItem(action, mapped, idx) + item.reparentTo(self.lstActionMap.getCanvas()) + idx += 1 + + # recalculate the canvas size to set scrollbars if necesary + self.lstActionMap["canvasSize"] = ( + base.a2dLeft+0.05, base.a2dRight-0.05, + -(len(self.mapping.actions)*0.1), 0.09) + self.lstActionMap.setCanvasSize() + + def closeDialog(self, action, newInputType, newInput): + """Called in callback when the dialog is closed. newInputType will be + "button" or "axis", or None if the remapping was cancelled.""" + + self.dlgInput = None + + if newInputType is not None: + # map the event to the given action + if newInputType == "axis": + self.mapping.mapAxis(action, newInput) + else: + self.mapping.mapButton(action, newInput) + + # actualize the label in the list that shows the current + # event for the action + self.actionLabels[action]["text"] = self.mapping.formatMapping(action) + + # cleanup + for bt in base.buttonThrowers: + bt.node().setSpecificFlag(True) + bt.node().setButtonDownEvent("") + for bt in base.deviceButtonThrowers: + bt.node().setSpecificFlag(True) + bt.node().setButtonDownEvent("") + taskMgr.remove("checkControls") + + # Now detach all the input devices. + for device in self.attachedDevices: + base.detachInputDevice(device) + self.attachedDevices.clear() + + def changeMapping(self, action): + # Create the dialog window + self.dlgInput = ChangeActionDialog(action, button_geom=self.buttonGeom, command=self.closeDialog) + + # Attach all input devices. + devices = base.devices.getDevices() + for device in devices: + base.attachInputDevice(device) + self.attachedDevices = devices + + # Disable regular button events on all button event throwers, and + # instead broadcast a generic event. + for bt in base.buttonThrowers: + bt.node().setSpecificFlag(False) + bt.node().setButtonDownEvent("keyListenEvent") + for bt in base.deviceButtonThrowers: + bt.node().setSpecificFlag(False) + bt.node().setButtonDownEvent("deviceListenEvent") + + self.accept("keyListenEvent", self.dlgInput.buttonPressed) + self.accept("deviceListenEvent", self.dlgInput.buttonPressed) + + # As there are no events thrown for control changes, we set up a task + # to check if the controls were moved + # This list will help us for checking which controls were moved + self.axisStates = {None: {}} + # fill it with all available controls + for device in devices: + for axis in device.axes: + if device not in self.axisStates.keys(): + self.axisStates.update({device: {axis.axis: axis.value}}) + else: + self.axisStates[device].update({axis.axis: axis.value}) + # start the task + taskMgr.add(self.watchControls, "checkControls") + + def watchControls(self, task): + # move through all devices and all it's controls + for device in self.attachedDevices: + if device.device_class == InputDevice.DeviceClass.mouse: + # Ignore mouse axis movement, or the user can't even navigate + # to the OK/Cancel buttons! + continue + + for axis in device.axes: + # if a control got changed more than the given dead zone + if self.axisStates[device][axis.axis] + DEAD_ZONE < axis.value or \ + self.axisStates[device][axis.axis] - DEAD_ZONE > axis.value: + # set the current state in the dict + self.axisStates[device][axis.axis] = axis.value + + # Format the axis for being displayed. + if axis.axis != InputDevice.Axis.none: + #label = axis.axis.name.replace('_', ' ').title() + self.dlgInput.axisMoved(axis.axis) + + return task.cont + + def __makeListItem(self, action, event, index): + def dummy(): pass + if index % 2 == 0: + bg = self.listBGEven + else: + bg = self.listBGOdd + item = DirectFrame( + text=action, + geom=bg, + geom_scale=(base.a2dRight-0.05, 1, 0.1), + frameSize=VBase4(base.a2dLeft+0.05, base.a2dRight-0.05, -0.05, 0.05), + frameColor=VBase4(1,0,0,0), + text_align=TextNode.ALeft, + text_scale=0.05, + text_fg=VBase4(1,1,1,1), + text_pos=(base.a2dLeft + 0.3, -0.015), + text_shadow=VBase4(0, 0, 0, 0.35), + text_shadowOffset=Vec2(-0.05, -0.05), + pos=(0.05, 0, -(0.10 * index))) + item.setTransparency(True) + lbl = DirectLabel( + text=event, + text_fg=VBase4(1, 1, 1, 1), + text_scale=0.05, + text_pos=Vec2(0, -0.015), + frameColor=VBase4(0, 0, 0, 0), + ) + lbl.reparentTo(item) + lbl.setTransparency(True) + self.actionLabels[action] = lbl + + buttonScale = 0.15 + btn = DirectButton( + text="Change", + geom=self.buttonGeom, + scale=buttonScale, + text_scale=0.25, + text_align=TextNode.ALeft, + text_fg=VBase4(0.898, 0.839, 0.730, 1.0), + text_pos=Vec2(-0.9, -0.085), + relief=1, + pad=Vec2(0.01, 0.01), + frameColor=VBase4(0, 0, 0, 0), + frameSize=VBase4(-1.0, 1.0, -0.25, 0.25), + pos=(base.a2dRight-(0.898*buttonScale+0.3), 0, 0), + pressEffect=False, + command=self.changeMapping, + extraArgs=[action]) + btn.setTransparency(True) + btn.reparentTo(item) + return item + +app = MappingGUIDemo() +app.run() diff --git a/samples/gamepad/models/button_map.egg b/samples/gamepad/models/button_map.egg new file mode 100644 index 0000000000..75a305e0da --- /dev/null +++ b/samples/gamepad/models/button_map.egg @@ -0,0 +1,117 @@ + { + "egg-texture-cards -o button_map.egg -p 768,192 -g -1.0,1.0,-0.25,0.25 -wm clamp ready.png click.png hover.png disabled.png" +} + ready { + ready.png + wrap { clamp } +} + hover { + hover.png + wrap { clamp } +} + disabled { + disabled.png + wrap { clamp } +} + click { + click.png + wrap { clamp } +} + { + { 1 } + fps { 2 } + vpool { + 0 { + -1 0.25 0 + { 0 1 } + } + 1 { + -1 -0.25 0 + { 0 0 } + } + 2 { + 1 -0.25 0 + { 1 0 } + } + 3 { + 1 0.25 0 + { 1 1 } + } + 4 { + -1 0.25 0 + { 0 1 } + } + 5 { + -1 -0.25 0 + { 0 0 } + } + 6 { + 1 -0.25 0 + { 1 0 } + } + 7 { + 1 0.25 0 + { 1 1 } + } + 8 { + -1 0.25 0 + { 0 1 } + } + 9 { + -1 -0.25 0 + { 0 0 } + } + 10 { + 1 -0.25 0 + { 1 0 } + } + 11 { + 1 0.25 0 + { 1 1 } + } + 12 { + -1 0.25 0 + { 0 1 } + } + 13 { + -1 -0.25 0 + { 0 0 } + } + 14 { + 1 -0.25 0 + { 1 0 } + } + 15 { + 1 0.25 0 + { 1 1 } + } + } + ready { + { + { 1 1 1 1 } + { ready } + { 0 1 2 3 { vpool } } + } + } + click { + { + { 1 1 1 1 } + { click } + { 4 5 6 7 { vpool } } + } + } + hover { + { + { 1 1 1 1 } + { hover } + { 8 9 10 11 { vpool } } + } + } + disabled { + { + { 1 1 1 1 } + { disabled } + { 12 13 14 15 { vpool } } + } + } +} diff --git a/samples/gamepad/models/click.png b/samples/gamepad/models/click.png new file mode 100644 index 0000000000..fbd06a4df3 Binary files /dev/null and b/samples/gamepad/models/click.png differ diff --git a/samples/gamepad/models/dec_click.png b/samples/gamepad/models/dec_click.png new file mode 100644 index 0000000000..4c28814535 Binary files /dev/null and b/samples/gamepad/models/dec_click.png differ diff --git a/samples/gamepad/models/dec_disabled.png b/samples/gamepad/models/dec_disabled.png new file mode 100644 index 0000000000..d1978f241f Binary files /dev/null and b/samples/gamepad/models/dec_disabled.png differ diff --git a/samples/gamepad/models/dec_hover.png b/samples/gamepad/models/dec_hover.png new file mode 100644 index 0000000000..87f1a267b5 Binary files /dev/null and b/samples/gamepad/models/dec_hover.png differ diff --git a/samples/gamepad/models/dec_map.egg b/samples/gamepad/models/dec_map.egg new file mode 100644 index 0000000000..71190d251a --- /dev/null +++ b/samples/gamepad/models/dec_map.egg @@ -0,0 +1,117 @@ + { + "egg-texture-cards -o dec_map.egg -p 32,32 -g -0.04,0.04,-0.04,0.04 -wm clamp dec_ready.png dec_click.png dec_hover.png dec_disabled.png" +} + dec_ready { + dec_ready.png + wrap { clamp } +} + dec_hover { + dec_hover.png + wrap { clamp } +} + dec_disabled { + dec_disabled.png + wrap { clamp } +} + dec_click { + dec_click.png + wrap { clamp } +} + { + { 1 } + fps { 2 } + vpool { + 0 { + -0.04 0.04 0 + { 0 1 } + } + 1 { + -0.04 -0.04 0 + { 0 0 } + } + 2 { + 0.04 -0.04 0 + { 1 0 } + } + 3 { + 0.04 0.04 0 + { 1 1 } + } + 4 { + -0.04 0.04 0 + { 0 1 } + } + 5 { + -0.04 -0.04 0 + { 0 0 } + } + 6 { + 0.04 -0.04 0 + { 1 0 } + } + 7 { + 0.04 0.04 0 + { 1 1 } + } + 8 { + -0.04 0.04 0 + { 0 1 } + } + 9 { + -0.04 -0.04 0 + { 0 0 } + } + 10 { + 0.04 -0.04 0 + { 1 0 } + } + 11 { + 0.04 0.04 0 + { 1 1 } + } + 12 { + -0.04 0.04 0 + { 0 1 } + } + 13 { + -0.04 -0.04 0 + { 0 0 } + } + 14 { + 0.04 -0.04 0 + { 1 0 } + } + 15 { + 0.04 0.04 0 + { 1 1 } + } + } + dec_ready { + { + { 1 1 1 1 } + { dec_ready } + { 0 1 2 3 { vpool } } + } + } + dec_click { + { + { 1 1 1 1 } + { dec_click } + { 4 5 6 7 { vpool } } + } + } + dec_hover { + { + { 1 1 1 1 } + { dec_hover } + { 8 9 10 11 { vpool } } + } + } + dec_disabled { + { + { 1 1 1 1 } + { dec_disabled } + { 12 13 14 15 { vpool } } + } + } +} diff --git a/samples/gamepad/models/dec_ready.png b/samples/gamepad/models/dec_ready.png new file mode 100644 index 0000000000..a097c1e5cb Binary files /dev/null and b/samples/gamepad/models/dec_ready.png differ diff --git a/samples/gamepad/models/dialog.png b/samples/gamepad/models/dialog.png new file mode 100644 index 0000000000..7ceb2c1809 Binary files /dev/null and b/samples/gamepad/models/dialog.png differ diff --git a/samples/gamepad/models/disabled.png b/samples/gamepad/models/disabled.png new file mode 100644 index 0000000000..9b9dfe5014 Binary files /dev/null and b/samples/gamepad/models/disabled.png differ diff --git a/samples/gamepad/models/hover.png b/samples/gamepad/models/hover.png new file mode 100644 index 0000000000..f77986b296 Binary files /dev/null and b/samples/gamepad/models/hover.png differ diff --git a/samples/gamepad/models/inc_click.png b/samples/gamepad/models/inc_click.png new file mode 100644 index 0000000000..7afed6b761 Binary files /dev/null and b/samples/gamepad/models/inc_click.png differ diff --git a/samples/gamepad/models/inc_disabled.png b/samples/gamepad/models/inc_disabled.png new file mode 100644 index 0000000000..83817cc587 Binary files /dev/null and b/samples/gamepad/models/inc_disabled.png differ diff --git a/samples/gamepad/models/inc_hover.png b/samples/gamepad/models/inc_hover.png new file mode 100644 index 0000000000..089df4352e Binary files /dev/null and b/samples/gamepad/models/inc_hover.png differ diff --git a/samples/gamepad/models/inc_map.egg b/samples/gamepad/models/inc_map.egg new file mode 100644 index 0000000000..9bd07862da --- /dev/null +++ b/samples/gamepad/models/inc_map.egg @@ -0,0 +1,117 @@ + { + "egg-texture-cards -o inc_map.egg -p 32,32 -g -0.04,0.04,-0.04,0.04 -wm clamp inc_ready.png inc_click.png inc_hover.png inc_disabled.png" +} + inc_ready { + inc_ready.png + wrap { clamp } +} + inc_hover { + inc_hover.png + wrap { clamp } +} + inc_disabled { + inc_disabled.png + wrap { clamp } +} + inc_click { + inc_click.png + wrap { clamp } +} + { + { 1 } + fps { 2 } + vpool { + 0 { + -0.04 0.04 0 + { 0 1 } + } + 1 { + -0.04 -0.04 0 + { 0 0 } + } + 2 { + 0.04 -0.04 0 + { 1 0 } + } + 3 { + 0.04 0.04 0 + { 1 1 } + } + 4 { + -0.04 0.04 0 + { 0 1 } + } + 5 { + -0.04 -0.04 0 + { 0 0 } + } + 6 { + 0.04 -0.04 0 + { 1 0 } + } + 7 { + 0.04 0.04 0 + { 1 1 } + } + 8 { + -0.04 0.04 0 + { 0 1 } + } + 9 { + -0.04 -0.04 0 + { 0 0 } + } + 10 { + 0.04 -0.04 0 + { 1 0 } + } + 11 { + 0.04 0.04 0 + { 1 1 } + } + 12 { + -0.04 0.04 0 + { 0 1 } + } + 13 { + -0.04 -0.04 0 + { 0 0 } + } + 14 { + 0.04 -0.04 0 + { 1 0 } + } + 15 { + 0.04 0.04 0 + { 1 1 } + } + } + inc_ready { + { + { 1 1 1 1 } + { inc_ready } + { 0 1 2 3 { vpool } } + } + } + inc_click { + { + { 1 1 1 1 } + { inc_click } + { 4 5 6 7 { vpool } } + } + } + inc_hover { + { + { 1 1 1 1 } + { inc_hover } + { 8 9 10 11 { vpool } } + } + } + inc_disabled { + { + { 1 1 1 1 } + { inc_disabled } + { 12 13 14 15 { vpool } } + } + } +} diff --git a/samples/gamepad/models/inc_ready.png b/samples/gamepad/models/inc_ready.png new file mode 100644 index 0000000000..9d4c153e44 Binary files /dev/null and b/samples/gamepad/models/inc_ready.png differ diff --git a/samples/gamepad/models/li_ready_even.png b/samples/gamepad/models/li_ready_even.png new file mode 100644 index 0000000000..87736e8570 Binary files /dev/null and b/samples/gamepad/models/li_ready_even.png differ diff --git a/samples/gamepad/models/li_ready_odd.png b/samples/gamepad/models/li_ready_odd.png new file mode 100644 index 0000000000..dcc22f71a2 Binary files /dev/null and b/samples/gamepad/models/li_ready_odd.png differ diff --git a/samples/gamepad/models/list_item_even.egg b/samples/gamepad/models/list_item_even.egg new file mode 100644 index 0000000000..d4e747a2a0 --- /dev/null +++ b/samples/gamepad/models/list_item_even.egg @@ -0,0 +1,33 @@ + { + "egg-texture-cards -o list_item_even.egg -g -1,1,-0.5,0.5 -p 1,30 li_ready_even.png" +} + li_ready_even { + li_ready_even.png +} + { + vpool { + 0 { + -1 0.5 0 + { 0 1 } + } + 1 { + -1 -0.5 0 + { 0 0 } + } + 2 { + 1 -0.5 0 + { 1 0 } + } + 3 { + 1 0.5 0 + { 1 1 } + } + } + li_ready_even { + { + { 1 1 1 1 } + { li_ready_even } + { 0 1 2 3 { vpool } } + } + } +} diff --git a/samples/gamepad/models/list_item_odd.egg b/samples/gamepad/models/list_item_odd.egg new file mode 100644 index 0000000000..cde6c8642f --- /dev/null +++ b/samples/gamepad/models/list_item_odd.egg @@ -0,0 +1,33 @@ + { + "egg-texture-cards -o list_item_odd.egg -g -1,1,-0.5,0.5 -p 1,30 li_ready_odd.png" +} + li_ready_odd { + li_ready_odd.png +} + { + vpool { + 0 { + -1 0.5 0 + { 0 1 } + } + 1 { + -1 -0.5 0 + { 0 0 } + } + 2 { + 1 -0.5 0 + { 1 0 } + } + 3 { + 1 0.5 0 + { 1 1 } + } + } + li_ready_odd { + { + { 1 1 1 1 } + { li_ready_odd } + { 0 1 2 3 { vpool } } + } + } +} diff --git a/samples/gamepad/models/ready.png b/samples/gamepad/models/ready.png new file mode 100644 index 0000000000..e9cfed43da Binary files /dev/null and b/samples/gamepad/models/ready.png differ diff --git a/samples/gamepad/models/thumb_click.png b/samples/gamepad/models/thumb_click.png new file mode 100644 index 0000000000..2fc059da82 Binary files /dev/null and b/samples/gamepad/models/thumb_click.png differ diff --git a/samples/gamepad/models/thumb_disabled.png b/samples/gamepad/models/thumb_disabled.png new file mode 100644 index 0000000000..8ad2805f10 Binary files /dev/null and b/samples/gamepad/models/thumb_disabled.png differ diff --git a/samples/gamepad/models/thumb_hover.png b/samples/gamepad/models/thumb_hover.png new file mode 100644 index 0000000000..258392f3b1 Binary files /dev/null and b/samples/gamepad/models/thumb_hover.png differ diff --git a/samples/gamepad/models/thumb_map.egg b/samples/gamepad/models/thumb_map.egg new file mode 100644 index 0000000000..1b49062ef5 --- /dev/null +++ b/samples/gamepad/models/thumb_map.egg @@ -0,0 +1,117 @@ + { + "egg-texture-cards -o thumb_map.egg -p 32,440 -g -0.04,0.04,-0.55,0.55 -wm clamp thumb_ready.png thumb_click.png thumb_hover.png thumb_disabled.png" +} + thumb_ready { + thumb_ready.png + wrap { clamp } +} + thumb_hover { + thumb_hover.png + wrap { clamp } +} + thumb_disabled { + thumb_disabled.png + wrap { clamp } +} + thumb_click { + thumb_click.png + wrap { clamp } +} + { + { 1 } + fps { 2 } + vpool { + 0 { + -0.04 0.55 0 + { 0 1 } + } + 1 { + -0.04 -0.55 0 + { 0 0 } + } + 2 { + 0.04 -0.55 0 + { 1 0 } + } + 3 { + 0.04 0.55 0 + { 1 1 } + } + 4 { + -0.04 0.55 0 + { 0 1 } + } + 5 { + -0.04 -0.55 0 + { 0 0 } + } + 6 { + 0.04 -0.55 0 + { 1 0 } + } + 7 { + 0.04 0.55 0 + { 1 1 } + } + 8 { + -0.04 0.55 0 + { 0 1 } + } + 9 { + -0.04 -0.55 0 + { 0 0 } + } + 10 { + 0.04 -0.55 0 + { 1 0 } + } + 11 { + 0.04 0.55 0 + { 1 1 } + } + 12 { + -0.04 0.55 0 + { 0 1 } + } + 13 { + -0.04 -0.55 0 + { 0 0 } + } + 14 { + 0.04 -0.55 0 + { 1 0 } + } + 15 { + 0.04 0.55 0 + { 1 1 } + } + } + thumb_ready { + { + { 1 1 1 1 } + { thumb_ready } + { 0 1 2 3 { vpool } } + } + } + thumb_click { + { + { 1 1 1 1 } + { thumb_click } + { 4 5 6 7 { vpool } } + } + } + thumb_hover { + { + { 1 1 1 1 } + { thumb_hover } + { 8 9 10 11 { vpool } } + } + } + thumb_disabled { + { + { 1 1 1 1 } + { thumb_disabled } + { 12 13 14 15 { vpool } } + } + } +} diff --git a/samples/gamepad/models/thumb_ready.png b/samples/gamepad/models/thumb_ready.png new file mode 100644 index 0000000000..36a41ae599 Binary files /dev/null and b/samples/gamepad/models/thumb_ready.png differ diff --git a/samples/gamepad/models/xbone-icons.egg b/samples/gamepad/models/xbone-icons.egg new file mode 100644 index 0000000000..cc2fb417ad --- /dev/null +++ b/samples/gamepad/models/xbone-icons.egg @@ -0,0 +1,384 @@ + { Y-Up } + + xbone-icons { + xbone-icons.png + format { rgba } + alpha { dual } +} + + vpool { + 0 { + -0.5 0.5 0 + { 0.00390625 0.9921875 } + } + 1 { + -0.5 -0.5 0 + { 0.00390625 0.7578125 } + } + 2 { + 0.5 -0.5 0 + { 0.12109375 0.7578125 } + } + 3 { + 0.5 0.5 0 + { 0.12109375 0.9921875 } + } + 4 { + -0.5 0.5 0 + { 0.12890625 0.9921875 } + } + 5 { + -0.5 -0.5 0 + { 0.12890625 0.7578125 } + } + 6 { + 0.5 -0.5 0 + { 0.24609375 0.7578125 } + } + 7 { + 0.5 0.5 0 + { 0.24609375 0.9921875 } + } + 8 { + -0.5 0.5 0 + { 0.25390625 0.9921875 } + } + 9 { + -0.5 -0.5 0 + { 0.25390625 0.7578125 } + } + 10 { + 0.5 -0.5 0 + { 0.37109375 0.7578125 } + } + 11 { + 0.5 0.5 0 + { 0.37109375 0.9921875 } + } + 12 { + -0.5 0.5 0 + { 0.37890625 0.9921875 } + } + 13 { + -0.5 -0.5 0 + { 0.37890625 0.7578125 } + } + 14 { + 0.5 -0.5 0 + { 0.49609375 0.7578125 } + } + 15 { + 0.5 0.5 0 + { 0.49609375 0.9921875 } + } + 16 { + -0.5 0.5 0 + { 0.50390625 0.9921875 } + } + 17 { + -0.5 -0.5 0 + { 0.50390625 0.7578125 } + } + 18 { + 0.5 -0.5 0 + { 0.62109375 0.7578125 } + } + 19 { + 0.5 0.5 0 + { 0.62109375 0.9921875 } + } + 20 { + -0.5 0.5 0 + { 0.62890625 0.9921875 } + } + 21 { + -0.5 -0.5 0 + { 0.62890625 0.7578125 } + } + 22 { + 0.5 -0.5 0 + { 0.74609375 0.7578125 } + } + 23 { + 0.5 0.5 0 + { 0.74609375 0.9921875 } + } + 24 { + -0.5 0.5 0 + { 0.75390625 0.9921875 } + } + 25 { + -0.5 -0.5 0 + { 0.75390625 0.7578125 } + } + 26 { + 0.5 -0.5 0 + { 0.87109375 0.7578125 } + } + 27 { + 0.5 0.5 0 + { 0.87109375 0.9921875 } + } + 28 { + -0.5 0.5 0 + { 0.87890625 0.9921875 } + } + 29 { + -0.5 -0.5 0 + { 0.87890625 0.7578125 } + } + 30 { + 0.5 -0.5 0 + { 0.99609375 0.7578125 } + } + 31 { + 0.5 0.5 0 + { 0.99609375 0.9921875 } + } + 32 { + -0.5 0.5 0 + { 0.12890625 0.7421875 } + } + 33 { + -0.5 -0.5 0 + { 0.12890625 0.5078125 } + } + 34 { + 0.5 -0.5 0 + { 0.24609375 0.5078125 } + } + 35 { + 0.5 0.5 0 + { 0.24609375 0.7421875 } + } + 36 { + -0.5 0.5 0 + { 0.00390625 0.7421875 } + } + 37 { + -0.5 -0.5 0 + { 0.00390625 0.5078125 } + } + 38 { + 0.5 -0.5 0 + { 0.12109375 0.5078125 } + } + 39 { + 0.5 0.5 0 + { 0.12109375 0.7421875 } + } + 40 { + -0.5 0.5 0 + { 0.25390625 0.7421875 } + } + 41 { + -0.5 -0.5 0 + { 0.25390625 0.5078125 } + } + 42 { + 0.5 -0.5 0 + { 0.37109375 0.5078125 } + } + 43 { + 0.5 0.5 0 + { 0.37109375 0.7421875 } + } + 44 { + -0.5 0.5 0 + { 0.37890625 0.7421875 } + } + 45 { + -0.5 -0.5 0 + { 0.37890625 0.5078125 } + } + 46 { + 0.5 -0.5 0 + { 0.49609375 0.5078125 } + } + 47 { + 0.5 0.5 0 + { 0.49609375 0.7421875 } + } + 48 { + -0.5 0.5 0 + { 0.62890625 0.7421875 } + } + 49 { + -0.5 -0.5 0 + { 0.62890625 0.5078125 } + } + 50 { + 0.5 -0.5 0 + { 0.74609375 0.5078125 } + } + 51 { + 0.5 0.5 0 + { 0.74609375 0.7421875 } + } + 52 { + -0.5 0.5 0 + { 0.50390625 0.7421875 } + } + 53 { + -0.5 -0.5 0 + { 0.50390625 0.5078125 } + } + 54 { + 0.5 -0.5 0 + { 0.62109375 0.5078125 } + } + 55 { + 0.5 0.5 0 + { 0.62109375 0.7421875 } + } + 56 { + -0.5 0.5 0 + { 0.75390625 0.7421875 } + } + 57 { + -0.5 -0.5 0 + { 0.75390625 0.5078125 } + } + 58 { + 0.5 -0.5 0 + { 0.87109375 0.5078125 } + } + 59 { + 0.5 0.5 0 + { 0.87109375 0.7421875 } + } + 60 { + -0.5 0.5 0 + { 0.87890625 0.7421875 } + } + 61 { + -0.5 -0.5 0 + { 0.87890625 0.5078125 } + } + 62 { + 0.5 -0.5 0 + { 0.99609375 0.5078125 } + } + 63 { + 0.5 0.5 0 + { 0.99609375 0.7421875 } + } + 64 { + -0.5 0.5 0 + { 0.00390625 0.4921875 } + } + 65 { + -0.5 -0.5 0 + { 0.00390625 0.2578125 } + } + 66 { + 0.5 -0.5 0 + { 0.12109375 0.2578125 } + } + 67 { + 0.5 0.5 0 + { 0.12109375 0.4921875 } + } +} + face_a { + { + { xbone-icons } + { 0 1 2 3 { vpool } } + } +} + face_b { + { + { xbone-icons } + { 4 5 6 7 { vpool } } + } +} + dpad { + { + { xbone-icons } + { 8 9 10 11 { vpool } } + } +} + dpad_down { + { + { xbone-icons } + { 12 13 14 15 { vpool } } + } +} + dpad_left { + { + { xbone-icons } + { 16 17 18 19 { vpool } } + } +} + dpad_right { + { + { xbone-icons } + { 20 21 22 23 { vpool } } + } +} + dpad_up { + { + { xbone-icons } + { 24 25 26 27 { vpool } } + } +} + XboxOne_LB { + { + { xbone-icons } + { 28 29 30 31 { vpool } } + } +} + lstick { + { + { xbone-icons } + { 32 33 34 35 { vpool } } + } +} + ltrigger { + { + { xbone-icons } + { 36 37 38 39 { vpool } } + } +} + start { + { + { xbone-icons } + { 40 41 42 43 { vpool } } + } +} + rshoulder { + { + { xbone-icons } + { 44 45 46 47 { vpool } } + } +} + rstick { + { + { xbone-icons } + { 48 49 50 51 { vpool } } + } +} + rtrigger { + { + { xbone-icons } + { 52 53 54 55 { vpool } } + } +} + back { + { + { xbone-icons } + { 56 57 58 59 { vpool } } + } +} + face_x { + { + { xbone-icons } + { 60 61 62 63 { vpool } } + } +} + face_y { + { + { xbone-icons } + { 64 65 66 67 { vpool } } + } +} diff --git a/samples/gamepad/models/xbone-icons.png b/samples/gamepad/models/xbone-icons.png new file mode 100644 index 0000000000..e8fd66b487 Binary files /dev/null and b/samples/gamepad/models/xbone-icons.png differ diff --git a/samples/gamepad/steeringWheel.py b/samples/gamepad/steeringWheel.py new file mode 100644 index 0000000000..09b0d9d167 --- /dev/null +++ b/samples/gamepad/steeringWheel.py @@ -0,0 +1,167 @@ +#!/usr/bin/env python +''' +Demonstrate usage of steering wheels + +In this sample you can use a wheel type device to control the camera and +show some messages on screen. You can acclerate forward using the +accleration pedal and slow down using the break pedal. +''' + +from direct.showbase.ShowBase import ShowBase +from panda3d.core import TextNode, InputDevice, loadPrcFileData, Vec3 +from direct.gui.OnscreenText import OnscreenText + +loadPrcFileData("", """ + default-fov 60 + notify-level-device debug +""") + +class App(ShowBase): + def __init__(self): + ShowBase.__init__(self) + # Print all events sent through the messenger + #self.messenger.toggleVerbose() + + self.lblWarning = OnscreenText( + text = "No devices found", + fg=(1,0,0,1), + scale = .25) + + self.lblAction = OnscreenText( + text = "Action", + fg=(1,1,1,1), + scale = .15) + self.lblAction.hide() + + # Is there a steering wheel connected? + self.wheel = None + devices = self.devices.getDevices(InputDevice.DeviceClass.steering_wheel) + if devices: + self.connect(devices[0]) + + self.currentMoveSpeed = 0.0 + self.maxAccleration = 28.0 + self.deaccleration = 10.0 + self.deaclerationBreak = 37.0 + self.maxSpeed = 80.0 + + # Accept device dis-/connection events + self.accept("connect-device", self.connect) + self.accept("disconnect-device", self.disconnect) + + self.accept("escape", exit) + + # Accept button events of the first connected steering wheel + self.accept("steering_wheel0-face_a", self.action, extraArgs=["Action"]) + self.accept("steering_wheel0-face_a-up", self.actionUp) + self.accept("steering_wheel0-hat_up", self.center_wheel) + + self.environment = loader.loadModel("environment") + self.environment.reparentTo(render) + + # save the center position of the wheel + # NOTE: here we assume, that the wheel is centered when the application get started. + # In real world applications, you should notice the user and give him enough time + # to center the wheel until you store the center position of the controler! + self.wheelCenter = 0 + if self.wheel is not None: + self.wheelCenter = self.wheel.findAxis(InputDevice.Axis.wheel).value + + # disable pandas default mouse-camera controls so we can handle the camera + # movements by ourself + self.disableMouse() + self.reset() + + self.taskMgr.add(self.moveTask, "movement update task") + + def connect(self, device): + """Event handler that is called when a device is discovered.""" + + # We're only interested if this is a steering wheel and we don't have a + # wheel yet. + if device.device_class == InputDevice.DeviceClass.steering_wheel and not self.wheel: + print("Found %s" % (device)) + self.wheel = device + + # Enable this device to ShowBase so that we can receive events. + # We set up the events with a prefix of "steering_wheel0-". + self.attachInputDevice(device, prefix="steering_wheel0") + + # Hide the warning that we have no devices. + self.lblWarning.hide() + + def disconnect(self, device): + """Event handler that is called when a device is removed.""" + + if self.wheel != device: + # We don't care since it's not our wheel. + return + + # Tell ShowBase that the device is no longer needed. + print("Disconnected %s" % (device)) + self.detachInputDevice(device) + self.wheel = None + + # Do we have any steering wheels? Attach the first other steering wheel. + devices = self.devices.getDevices(InputDevice.DeviceClass.steering_wheel) + if devices: + self.connect(devices[0]) + else: + # No devices. Show the warning. + self.lblWarning.show() + + def reset(self): + """Reset the camera to the initial position.""" + self.camera.setPosHpr(0, -200, 2, 0, 0, 0) + + def action(self, button): + # Just show which button has been pressed. + self.lblAction.text = "Pressed %s" % button + self.lblAction.show() + + def actionUp(self): + # Hide the label showing which button is pressed. + self.lblAction.hide() + + def center_wheel(self): + """Reset the wheels center rotation to the current rotation of the wheel""" + self.wheelCenter = self.wheel.findAxis(InputDevice.Axis.wheel).value + + def moveTask(self, task): + dt = globalClock.getDt() + movementVec = Vec3() + + if not self.wheel: + return task.cont + + if self.currentMoveSpeed > 0: + self.currentMoveSpeed -= dt * self.deaccleration + if self.currentMoveSpeed < 0: + self.currentMoveSpeed = 0 + + # we will use the first found wheel + # Acclerate + accleratorPedal = self.wheel.findAxis(InputDevice.Axis.accelerator).value + accleration = accleratorPedal * self.maxAccleration + if self.currentMoveSpeed > accleratorPedal * self.maxSpeed: + self.currentMoveSpeed -= dt * self.deaccleration + self.currentMoveSpeed += dt * accleration + + # Break + breakPedal = self.wheel.findAxis(InputDevice.Axis.brake).value + deacleration = breakPedal * self.deaclerationBreak + self.currentMoveSpeed -= dt * deacleration + if self.currentMoveSpeed < 0: + self.currentMoveSpeed = 0 + + # Steering + rotation = self.wheelCenter - self.wheel.findAxis(InputDevice.Axis.wheel).value + base.camera.setH(base.camera, 100 * dt * rotation) + + # calculate movement + base.camera.setY(base.camera, dt * self.currentMoveSpeed) + + return task.cont + +app = App() +app.run() diff --git a/tests/build_samples.py b/tests/build_samples.py new file mode 100644 index 0000000000..222a7df354 --- /dev/null +++ b/tests/build_samples.py @@ -0,0 +1,20 @@ +import os +import subprocess +import sys + +SAMPLES_TO_BUILD = [ + 'asteroids', +] +SAMPLES_DIR = os.path.join(os.path.dirname(__file__), '..', 'samples') + +def main(): + for sample in SAMPLES_TO_BUILD: + sampledir = os.path.join(SAMPLES_DIR, sample) + os.chdir(sampledir) + + # This will raise a CalledProcessError if the build fails, which will cause + # this script to fail + subprocess.check_call([sys.executable, 'setup.py', 'bdist_apps']) + +if __name__ == '__main__': + main() diff --git a/tests/collide/test_collision_tube.py b/tests/collide/test_collision_tube.py new file mode 100644 index 0000000000..3d97754d87 --- /dev/null +++ b/tests/collide/test_collision_tube.py @@ -0,0 +1,63 @@ +from panda3d import core + + +def test_collision_tube_alias(): + assert hasattr(core, 'CollisionCapsule') + assert hasattr(core, 'CollisionTube') + assert core.CollisionTube is core.CollisionCapsule + + +def test_collision_tube_write_old(): + buffer = core.DatagramBuffer() + writer = core.BamWriter(buffer) + assert writer.get_file_major_ver() == 6 + writer.set_file_minor_ver(43) + + capsule = core.CollisionCapsule((0, 0, -1), (0, 0, 1), 0.5) + writer.init() + writer.write_object(capsule) + writer.flush() + + data = buffer.data + assert b'CollisionTube' in data + assert b'CollisionCapsule' not in data + + +def test_collision_tube_write_new(): + buffer = core.DatagramBuffer() + writer = core.BamWriter(buffer) + assert writer.get_file_major_ver() == 6 + writer.set_file_minor_ver(44) + + capsule = core.CollisionCapsule((0, 0, -1), (0, 0, 1), 0.5) + writer.init() + writer.write_object(capsule) + writer.flush() + + data = buffer.data + assert b'CollisionTube' not in data + assert b'CollisionCapsule' in data + + +def test_collision_tube_read_old(): + # Make sure we can read an older file that contains CollisionTube. + buffer = core.DatagramBuffer(b'\x06\x00\x00\x00\x06\x00+\x00\x01\x00\xd6\x00\x00\x00\x00j\x01\r\x00CollisionTube\x01h\x01\x0e\x00CollisionSolid\x01B\x00\x11\x00CopyOnWriteObject\x01A\x00!\x00CachedTypedWritableReferenceCount\x01=\x00\x1b\x00TypedWritableReferenceCount\x02<\x00\r\x00TypedWritable\x01\x03\x00\x0b\x00TypedObject\x00\x07\x00\x0e\x00ReferenceCount\x00\x01\x00\x15\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\xbf\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80?\x00\x00\x00?\x01\x00\x00\x00\x01') + reader = core.BamReader(buffer) + reader.init() + assert reader.file_version == (6, 43) + + capsule = reader.read_object() + reader.resolve() + assert isinstance(capsule, core.CollisionCapsule) + + +def test_collision_tube_read_new(): + # Make sure we can read a newer file that contains CollisionCapsule. + buffer = core.DatagramBuffer(b'\x06\x00\x00\x00\x06\x00,\x00\x01\x00\xd9\x00\x00\x00\x00j\x01\x10\x00CollisionCapsule\x01h\x01\x0e\x00CollisionSolid\x01B\x00\x11\x00CopyOnWriteObject\x01A\x00!\x00CachedTypedWritableReferenceCount\x01=\x00\x1b\x00TypedWritableReferenceCount\x02<\x00\r\x00TypedWritable\x01\x03\x00\x0b\x00TypedObject\x00\x07\x00\x0e\x00ReferenceCount\x00\x01\x00\x15\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\xbf\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80?\x00\x00\x00?\x01\x00\x00\x00\x01') + reader = core.BamReader(buffer) + reader.init() + assert reader.file_version == (6, 44) + + capsule = reader.read_object() + reader.resolve() + assert isinstance(capsule, core.CollisionCapsule) diff --git a/tests/display/test_glsl_shader.py b/tests/display/test_glsl_shader.py index 0a0a5f5737..8deadf40d9 100644 --- a/tests/display/test_glsl_shader.py +++ b/tests/display/test_glsl_shader.py @@ -38,7 +38,7 @@ void main() {{ """ -def run_glsl_test(gsg, body, preamble="", inputs={}, version=130, exts=set()): +def run_glsl_test(gsg, body, preamble="", inputs={}, version=150, exts=set()): """ Runs a GLSL test on the given GSG. The given body is executed in the main function and should call assert(). The preamble should contain all of the shader inputs. """ diff --git a/tests/dtoolutil/test_globpattern.py b/tests/dtoolutil/test_globpattern.py new file mode 100644 index 0000000000..d653c97142 --- /dev/null +++ b/tests/dtoolutil/test_globpattern.py @@ -0,0 +1,85 @@ +from panda3d.core import GlobPattern + + +def test_globpattern_matches_file(): + patt = GlobPattern('/a/b/c') + assert patt.matches_file('/a/b/c') + assert patt.matches_file('///a////b//c') + assert patt.matches_file('/a/b/././c') + assert not patt.matches_file('') + assert not patt.matches_file('/') + assert not patt.matches_file('/a/b/d') + assert not patt.matches_file('/A/b/c') + assert not patt.matches_file('/a/b/c/') + assert not patt.matches_file('/a/b/c/.') + assert not patt.matches_file('a/b/c') + assert not patt.matches_file('./a/b/c') + + # Test regular pattern + patt = GlobPattern('*a') + assert patt.matches_file('a') + assert patt.matches_file('aa') + assert patt.matches_file('xa') + assert not patt.matches_file('A') + assert not patt.matches_file('ax') + assert not patt.matches_file('xax') + + # Test path ending in directory + for patt in GlobPattern('/a/b/c/'), \ + GlobPattern('/a/b/c/.'), \ + GlobPattern('/a/b//c//'), \ + GlobPattern('/a/b/./c/./'): + assert patt.matches_file('/a/b/c/') + assert patt.matches_file('///a////b//c//') + assert patt.matches_file('/a/b/././c/') + assert patt.matches_file('/a/b/c/.') + assert not patt.matches_file('/a/b/c') + assert not patt.matches_file('/a/b/c/./d') + assert not patt.matches_file('a/b/c/') + assert not patt.matches_file('./a/b/c/') + + # Test globstar in middle + for patt in GlobPattern('/a/**/c'), GlobPattern('/a/**/**/c'): + assert patt.matches_file('/a/c') + assert patt.matches_file('/a/b/c') + assert patt.matches_file('/a/b/d/c') + assert not patt.matches_file('/a/b/c/d') + assert not patt.matches_file('/d/b/c') + assert not patt.matches_file('/a/b/d') + + # Test globstar in beginning + for patt in GlobPattern('/**/b/c'), GlobPattern('/**/**/**/b/c'): + assert patt.matches_file('/a/b/c') + assert patt.matches_file('/a/d/b/c') + assert patt.matches_file('/a/b/c') + assert patt.matches_file('/a/b/c/./b//c') + assert not patt.matches_file('/a/b/c/d') + assert not patt.matches_file('/a/c') + assert not patt.matches_file('/a/b/d') + + # Test globstar at end + for patt in GlobPattern('/a/b/**'), \ + GlobPattern('/a/b/**/**'), \ + GlobPattern('/a/b//**//**/**'): + assert patt.matches_file('/a/b/') + assert patt.matches_file('/a/b/.') + assert patt.matches_file('/a/b//') + assert patt.matches_file('/a/b/c') + assert patt.matches_file('/a/b/c/d/e/f/g/h') + assert patt.matches_file('/a/b/d/c') + assert not patt.matches_file('/a/') + assert not patt.matches_file('/a/c/b') + + # Test multiple globstars at multiple locations + patt = GlobPattern('/a/**/b/**/c') + assert patt.matches_file('/a/b/c') + assert patt.matches_file('/a/./b/./c') + assert patt.matches_file('/a//b//c') + assert patt.matches_file('/a/x/y/b/c') + assert patt.matches_file('/a/b/x/y/c') + assert patt.matches_file('/a/b/c/a/b/c') + assert patt.matches_file('/a/x/y/b/x/y/c') + assert not patt.matches_file('/a/b/x') + assert not patt.matches_file('/a/b/c/x') + assert not patt.matches_file('/a/b/c/') + assert not patt.matches_file('/a/b/c/.') diff --git a/tests/gobj/test_geom.py b/tests/gobj/test_geom.py index 2a5a462659..24eeab852f 100644 --- a/tests/gobj/test_geom.py +++ b/tests/gobj/test_geom.py @@ -40,3 +40,45 @@ def test_geom_decompose(): # Old primitive should still be unchanged assert prim == geom.get_primitive(0) + + +def test_geom_calc_sphere_bounds(): + # Ensure that it ignores NaN + data = core.GeomVertexData("", core.GeomVertexFormat.get_v3(), core.Geom.UH_static) + vertex = core.GeomVertexWriter(data, "vertex") + vertex.add_data3((float("NaN"), 0, 0)) + vertex.add_data3((1, 1, 1)) + vertex.add_data3((1, 1, 2)) + + prim = core.GeomPoints(core.Geom.UH_static) + prim.add_next_vertices(3) + + geom = core.Geom(data) + geom.add_primitive(prim) + geom.set_bounds_type(core.BoundingVolume.BT_sphere) + + bounds = geom.get_bounds() + assert isinstance(bounds, core.BoundingSphere) + assert bounds.get_center() == (1, 1, 1.5) + assert bounds.get_radius() == 0.5 + + +def test_geom_calc_box_bounds(): + # Ensure that it ignores NaN + data = core.GeomVertexData("", core.GeomVertexFormat.get_v3(), core.Geom.UH_static) + vertex = core.GeomVertexWriter(data, "vertex") + vertex.add_data3((float("NaN"), 0, 0)) + vertex.add_data3((1, 1, 1)) + vertex.add_data3((1, 1, 2)) + + prim = core.GeomPoints(core.Geom.UH_static) + prim.add_next_vertices(3) + + geom = core.Geom(data) + geom.add_primitive(prim) + geom.set_bounds_type(core.BoundingVolume.BT_box) + + bounds = geom.get_bounds() + assert isinstance(bounds, core.BoundingBox) + assert bounds.get_min() == (1, 1, 1) + assert bounds.get_max() == (1, 1, 2) diff --git a/tests/main.py b/tests/main.py new file mode 100644 index 0000000000..4bc1849bda --- /dev/null +++ b/tests/main.py @@ -0,0 +1,13 @@ +import _pytest +if not hasattr(_pytest, '__file__'): + import os + import _pytest._pluggy + import py + + _pytest.__file__ = os.getcwd() + _pytest._pluggy.__file__ = os.getcwd() + py.__file__ = os.getcwd() + +import pytest + +pytest.main() diff --git a/tests/pgraph/test_nodepath.py b/tests/pgraph/test_nodepath.py index 1b98da32d7..19a40021ca 100644 --- a/tests/pgraph/test_nodepath.py +++ b/tests/pgraph/test_nodepath.py @@ -2,13 +2,39 @@ import pytest, sys def test_nodepath_empty(): """Tests NodePath behavior for empty NodePaths.""" + from panda3d.core import NodePath, ParamNodePath + import pickle + + empty = NodePath() + assert empty.is_empty() + assert not empty + + # Try pickling, which uses __reduce__ + dumped = pickle.dumps(empty) + empty2 = pickle.loads(dumped) + assert empty2.is_empty() + assert not empty2 + assert empty == empty2 + + # Test write_datagram/fillin, which are invoked when the NodePath is being + # serialized indirectly, such as via ParamNodePath + dumped = pickle.dumps(ParamNodePath(empty)) + empty2 = pickle.loads(dumped).get_value() + assert empty2.is_empty() + assert not empty2 + assert empty == empty2 + +def test_nodepath_single(): + """Tests NodePath behavior for single-node NodePaths.""" from panda3d.core import NodePath - empty = NodePath('np') + np = NodePath('np') + assert not np.is_empty() + assert np - assert empty.get_pos() == (0, 0, 0) - assert empty.get_hpr() == (0, 0, 0) - assert empty.get_scale() == (1, 1, 1) + assert np.get_pos() == (0, 0, 0) + assert np.get_hpr() == (0, 0, 0) + assert np.get_scale() == (1, 1, 1) def test_nodepath_parent(): """Tests NodePath.reparentTo().""" diff --git a/tests/requirements.txt b/tests/requirements.txt new file mode 100644 index 0000000000..6bb511ebbf --- /dev/null +++ b/tests/requirements.txt @@ -0,0 +1,3 @@ +pytest==3.2.0 +--pre --extra-index-url https://archive.panda3d.org/branches/deploy-ng +panda3d diff --git a/tests/setup.py b/tests/setup.py new file mode 100644 index 0000000000..b5967f3b14 --- /dev/null +++ b/tests/setup.py @@ -0,0 +1,17 @@ +from setuptools import setup +import pytest + +setup( + name="panda3d-tester", + options={ + 'build_apps': { + 'gui_apps': { + 'tester': 'main.py', + }, + 'plugins': [ + 'pandagl', + 'p3openal_audio', + ], + } + } +)