diff --git a/dtool/src/interrogate/interrogateBuilder.cxx b/dtool/src/interrogate/interrogateBuilder.cxx index da0e2a5318..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); 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 5c984cec82..6ebfff5b4b 100644 --- a/dtool/src/interrogatedb/py_panda.cxx +++ b/dtool/src/interrogatedb/py_panda.cxx @@ -286,6 +286,43 @@ PyObject *_Dtool_Return(PyObject *value) { } #if PY_VERSION_HEX < 0x03040000 +/** + * This function converts an int value to the appropriate enum instance. + */ +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 @@ -337,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 @@ -344,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); @@ -365,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) { @@ -665,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 ec2765e77a..353af53608 100644 --- a/dtool/src/interrogatedb/py_panda.h +++ b/dtool/src/interrogatedb/py_panda.h @@ -179,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); @@ -201,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)) @@ -209,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() @@ -226,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) @@ -241,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. @@ -320,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); /** @@ -348,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. @@ -390,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/makepanda/makepanda.py b/makepanda/makepanda.py index f4f3da2357..40febe3cd0 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -808,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"') diff --git a/makepanda/makepandacore.py b/makepanda/makepandacore.py index f84ac24316..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) 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/device/evdevInputDevice.cxx b/panda/src/device/evdevInputDevice.cxx index a5c916d8bc..b137888d6d 100644 --- a/panda/src/device/evdevInputDevice.cxx +++ b/panda/src/device/evdevInputDevice.cxx @@ -57,6 +57,11 @@ enum QuirkBits { // 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 { @@ -71,8 +76,14 @@ static const struct DeviceMapping { {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 @@ -110,7 +121,8 @@ EvdevInputDevice(LinuxInputDeviceManager *manager, size_t index) : _dpad_left_button(-1), _dpad_up_button(-1), _ltrigger_code(-1), - _rtrigger_code(-1) { + _rtrigger_code(-1), + _quirks(0) { char path[64]; sprintf(path, "/dev/input/event%zd", index); @@ -208,6 +220,26 @@ do_set_vibration(double strong, double weak) { } } +/** + * 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; @@ -219,7 +251,7 @@ do_poll() { while (process_events()) {} // If we got events, we are obviously connected. Mark us so. - if (!_is_connected) { + if (!_is_connected && _fd != -1) { _is_connected = true; if (_manager != nullptr) { _manager->add_device(this); @@ -297,6 +329,24 @@ init_device() { ++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}; @@ -376,7 +426,7 @@ init_device() { for (int i = 0; i <= KEY_MAX; ++i) { if (test_bit(i, keys)) { ButtonState button; - button.handle = map_button(i, _device_class); + button.handle = map_button(i, _device_class, quirks); int button_index = (int)_buttons.size(); if (button.handle == ButtonHandle::none()) { @@ -525,6 +575,18 @@ init_device() { } } 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. @@ -738,7 +800,7 @@ process_events() { * Static function to map an evdev code to a ButtonHandle. */ ButtonHandle EvdevInputDevice:: -map_button(int code, DeviceClass device_class) { +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[] = { @@ -895,7 +957,11 @@ map_button(int code, DeviceClass device_class) { } } else if ((code & 0xfff0) == BTN_JOYSTICK) { - if (device_class == DeviceClass::gamepad) { + 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(), @@ -989,6 +1055,13 @@ map_button(int code, DeviceClass device_class) { 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(); } diff --git a/panda/src/device/evdevInputDevice.h b/panda/src/device/evdevInputDevice.h index 53c495ad08..ffc3503ff0 100644 --- a/panda/src/device/evdevInputDevice.h +++ b/panda/src/device/evdevInputDevice.h @@ -30,6 +30,8 @@ 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(); @@ -41,6 +43,7 @@ private: LinuxInputDeviceManager *_manager; int _fd; + int _quirks; size_t _index; bool _can_write; @@ -64,7 +67,9 @@ private: int _rtrigger_code; public: - static ButtonHandle map_button(int code, DeviceClass device_class = DeviceClass::unknown); + static ButtonHandle map_button(int code, + DeviceClass device_class = DeviceClass::unknown, + int quirks = 0); public: static TypeHandle get_class_type() { @@ -75,6 +80,10 @@ public: 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; diff --git a/panda/src/device/linuxInputDeviceManager.cxx b/panda/src/device/linuxInputDeviceManager.cxx index 45a83bfdb3..8eed3e1cc3 100644 --- a/panda/src/device/linuxInputDeviceManager.cxx +++ b/panda/src/device/linuxInputDeviceManager.cxx @@ -200,6 +200,55 @@ consider_add_js_device(size_t js_index) { 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. @@ -243,6 +292,7 @@ update() { 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) { @@ -270,6 +320,12 @@ update() { 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; + } } } } @@ -290,6 +346,25 @@ update() { 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 index c5ad6a013f..36fce94842 100644 --- a/panda/src/device/linuxInputDeviceManager.h +++ b/panda/src/device/linuxInputDeviceManager.h @@ -30,6 +30,9 @@ private: 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: diff --git a/panda/src/device/linuxJoystickDevice.cxx b/panda/src/device/linuxJoystickDevice.cxx index 1871a1e948..c964ea8499 100644 --- a/panda/src/device/linuxJoystickDevice.cxx +++ b/panda/src/device/linuxJoystickDevice.cxx @@ -113,6 +113,7 @@ open_device() { ioctl(_fd, JSIOCGNAME(sizeof(name)), name); _name = name; + bool emulate_dpad = true; bool have_analog_triggers = false; // Get the number of axes. @@ -138,6 +139,8 @@ open_device() { _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()) { @@ -220,7 +223,7 @@ open_device() { break; case ABS_HAT0X: - if (_dpad_left_button == -1) { + if (emulate_dpad) { // Emulate D-Pad or hat switch. _dpad_x_axis = i; _dpad_left_button = (int)_buttons.size(); @@ -236,7 +239,7 @@ open_device() { break; case ABS_HAT0Y: - if (_dpad_up_button == -1) { + if (emulate_dpad) { // Emulate D-Pad. _dpad_y_axis = i; _dpad_up_button = (int)_buttons.size(); @@ -251,6 +254,18 @@ open_device() { } 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 @@ -278,7 +293,7 @@ open_device() { if (_ltrigger_button >= 0 && _rtrigger_button >= 0 && !have_analog_triggers) { // Emulate analog triggers. - _ltrigger_control = (int)_axes.size(); + _ltrigger_axis = (int)_axes.size(); add_axis(Axis::left_trigger, 0, 1, false); add_axis(Axis::right_trigger, 0, 1, false); } else { @@ -398,9 +413,9 @@ process_events() { if (events[i].type & JS_EVENT_BUTTON) { if (index == _ltrigger_button) { - axis_changed(_ltrigger_control, events[i].value); + axis_changed(_ltrigger_axis, events[i].value); } else if (index == _rtrigger_button) { - axis_changed(_ltrigger_control + 1, events[i].value); + axis_changed(_ltrigger_axis + 1, events[i].value); } button_changed(index, (events[i].value != 0)); diff --git a/panda/src/device/linuxJoystickDevice.h b/panda/src/device/linuxJoystickDevice.h index af0a74e3e6..61d05cd3e8 100644 --- a/panda/src/device/linuxJoystickDevice.h +++ b/panda/src/device/linuxJoystickDevice.h @@ -50,7 +50,7 @@ private: int _dpad_up_button; // This is used for axis emulation. - int _ltrigger_control; + int _ltrigger_axis; int _ltrigger_button; int _rtrigger_button; diff --git a/panda/src/device/winInputDeviceManager.cxx b/panda/src/device/winInputDeviceManager.cxx index b7fefc7c3e..033a174f6b 100644 --- a/panda/src/device/winInputDeviceManager.cxx +++ b/panda/src/device/winInputDeviceManager.cxx @@ -82,6 +82,47 @@ WinInputDeviceManager() : 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); + } } /** @@ -277,7 +318,6 @@ on_input_device_arrival(HANDLE handle) { 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. - WinRawInputDevice idev(this, path); if (_xinput_device0.check_arrival(info, inst, name, manufacturer)) { add_device(&_xinput_device0); } diff --git a/panda/src/device/winRawInputDevice.cxx b/panda/src/device/winRawInputDevice.cxx index 1af01cd4b4..eeb98f112d 100644 --- a/panda/src/device/winRawInputDevice.cxx +++ b/panda/src/device/winRawInputDevice.cxx @@ -273,6 +273,11 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) { // 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 && @@ -599,13 +604,16 @@ on_removal() { _is_connected = false; _handle = nullptr; if (_preparsed != nullptr) { - delete _preparsed; + 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); @@ -616,59 +624,68 @@ on_input(PRAWINPUT input) { return; } - PHIDP_DATA data = (PHIDP_DATA)alloca(sizeof(HIDP_DATA) * _max_data_count); - nassertv(data != nullptr); - ULONG count; - LightMutexHolder holder(_lock); for (DWORD i = 0; i < input->data.hid.dwCount; ++i) { - // The first byte is the report identifier. We need it to figure out - // which buttons are off, since each report only contains the buttons that - // are "on". - UCHAR report_id = ptr[0]; - BitArray unset_buttons = _report_buttons[report_id]; + process_report((PCHAR)ptr, input->data.hid.dwSizeHid); + ptr += input->data.hid.dwSizeHid; + } +} - count = _max_data_count; - NTSTATUS status = _HidP_GetData(HidP_Input, data, &count, (PHIDP_PREPARSED_DATA)_preparsed, (PCHAR)ptr, input->data.hid.dwSizeHid); - 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); - } +/** + * 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 } + 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"; } - ptr += input->data.hid.dwSizeHid; + // 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"; } } diff --git a/panda/src/device/winRawInputDevice.h b/panda/src/device/winRawInputDevice.h index c5cba8c615..8c880604be 100644 --- a/panda/src/device/winRawInputDevice.h +++ b/panda/src/device/winRawInputDevice.h @@ -34,6 +34,7 @@ public: 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(); diff --git a/panda/src/device/xInputDevice.cxx b/panda/src/device/xInputDevice.cxx index 42f9c4fbef..4621d467b9 100644 --- a/panda/src/device/xInputDevice.cxx +++ b/panda/src/device/xInputDevice.cxx @@ -161,6 +161,10 @@ check_arrival(const RID_DEVICE_INFO &info, DEVINST inst, 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)) { @@ -205,6 +209,10 @@ check_arrival(const RID_DEVICE_INFO &info, DEVINST inst, */ void XInputDevice:: detect(InputDeviceManager *mgr) { + if (!_initialized) { + nassertv_always(init_xinput()); + } + bool connected = false; XINPUT_CAPABILITIES_EX caps = {0}; @@ -225,6 +233,10 @@ detect(InputDeviceManager *mgr) { _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 { diff --git a/panda/src/display/config_display.cxx b/panda/src/display/config_display.cxx index 78c0b17418..965d68f4e3 100644 --- a/panda/src/display/config_display.cxx +++ b/panda/src/display/config_display.cxx @@ -478,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 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/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/egg2pg/eggSaver.cxx b/panda/src/egg2pg/eggSaver.cxx index 9aba56cc01..451497381c 100644 --- a/panda/src/egg2pg/eggSaver.cxx +++ b/panda/src/egg2pg/eggSaver.cxx @@ -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); 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/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/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index 1e6d1781cc..6151a2c00d 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -1684,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; 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/shader.cxx b/panda/src/gobj/shader.cxx index fde084aa18..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"); 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/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/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/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/gamepadButton.cxx b/panda/src/putil/gamepadButton.cxx index 2924f0bf90..4e3c8be22c 100644 --- a/panda/src/putil/gamepadButton.cxx +++ b/panda/src/putil/gamepadButton.cxx @@ -24,6 +24,8 @@ 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) @@ -87,6 +89,8 @@ init_gamepad_buttons() { 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"); diff --git a/panda/src/putil/gamepadButton.h b/panda/src/putil/gamepadButton.h index 774ee8ef2f..ff89c7e294 100644 --- a/panda/src/putil/gamepadButton.h +++ b/panda/src/putil/gamepadButton.h @@ -30,6 +30,8 @@ PUBLISHED: static ButtonHandle rshoulder(); static ButtonHandle ltrigger(); static ButtonHandle rtrigger(); + static ButtonHandle lgrip(); + static ButtonHandle rgrip(); static ButtonHandle dpad_left(); static ButtonHandle dpad_right(); 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 be28a6254f..df038a6dad 100644 --- a/panda/src/x11display/x11GraphicsWindow.cxx +++ b/panda/src/x11display/x11GraphicsWindow.cxx @@ -1272,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); 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 index a3f70b96b4..ee2fad6c30 100644 --- a/samples/gamepad/device_tester.py +++ b/samples/gamepad/device_tester.py @@ -113,6 +113,8 @@ class DeviceConnectivityMonitor(DirectObject): 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)