From 3ca3dfd13aa04eacbf670c9ffe6093d5a12f177f Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 14 Apr 2019 17:10:52 +0200 Subject: [PATCH 1/7] interrogate: fix in-place or (|=) operators (see #588) --- dtool/src/interrogate/interfaceMakerPythonNative.cxx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dtool/src/interrogate/interfaceMakerPythonNative.cxx b/dtool/src/interrogate/interfaceMakerPythonNative.cxx index c617b6597e..dadd094ca4 100644 --- a/dtool/src/interrogate/interfaceMakerPythonNative.cxx +++ b/dtool/src/interrogate/interfaceMakerPythonNative.cxx @@ -431,6 +431,12 @@ get_slotted_function_def(Object *obj, Function *func, FunctionRemap *remap, return true; } + if (method_name == "operator |=") { + def._answer_location = "nb_inplace_or"; + def._wrapper_type = WT_inplace_binary_operator; + return true; + } + if (method_name == "__ipow__") { def._answer_location = "nb_inplace_power"; def._wrapper_type = WT_inplace_ternary_operator; From 98227daaa5c29d7618d3b4e12426ca59e9860867 Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 14 Apr 2019 17:11:42 +0200 Subject: [PATCH 2/7] putil: fix SparseArray::clear_range et al Fixes #588 --- panda/src/putil/sparseArray.cxx | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/panda/src/putil/sparseArray.cxx b/panda/src/putil/sparseArray.cxx index 6373e53039..5404728aa1 100644 --- a/panda/src/putil/sparseArray.cxx +++ b/panda/src/putil/sparseArray.cxx @@ -262,8 +262,8 @@ compare_to(const SparseArray &other) const { return -1; } - --ai; - --bi; + ++ai; + ++bi; } if (ai != _subranges.rend()) { @@ -440,9 +440,9 @@ do_remove_range(int begin, int end) { if (si == _subranges.end()) { if (!_subranges.empty()) { si = _subranges.begin() + _subranges.size() - 1; - if ((*si)._end >= begin) { + if ((*si)._end > begin) { // The new range shortens the last element of the array on the right. - end = std::min(end, (*si)._begin); + end = std::max(begin, (*si)._begin); (*si)._end = end; // It might also shorten it on the left; fall through. } else { @@ -462,10 +462,10 @@ do_remove_range(int begin, int end) { if (si != _subranges.begin()) { Subranges::iterator si2 = si; --si2; - if ((*si2)._end >= begin) { + if ((*si2)._end > begin) { // The new range shortens an element within the array on the right // (but does not intersect the next element). - end = std::min(end, (*si2)._begin); + end = std::max(begin, (*si2)._begin); (*si2)._end = end; // It might also shorten it on the left; fall through. si = si2; @@ -488,7 +488,7 @@ do_remove_range(int begin, int end) { } // Check if the new range removes any elements to the left. - while (begin <= (*si)._begin) { + while (begin <= (*si)._begin || (*si)._begin >= (*si)._end) { if (si == _subranges.begin()) { _subranges.erase(si); return; @@ -500,6 +500,7 @@ do_remove_range(int begin, int end) { } (*si)._end = std::min((*si)._end, begin); + nassertv((*si)._end > (*si)._begin); } /** From 83723d38a5e68a2b3af35d9e0958d71781f2d8d6 Mon Sep 17 00:00:00 2001 From: Epihaius Date: Mon, 25 Mar 2019 15:15:06 +0100 Subject: [PATCH 3/7] tests: Create test_sparsearray.py Closes #590 --- tests/putil/test_sparsearray.py | 219 ++++++++++++++++++++++++++++++++ 1 file changed, 219 insertions(+) create mode 100644 tests/putil/test_sparsearray.py diff --git a/tests/putil/test_sparsearray.py b/tests/putil/test_sparsearray.py new file mode 100644 index 0000000000..ae6d5208b5 --- /dev/null +++ b/tests/putil/test_sparsearray.py @@ -0,0 +1,219 @@ +from panda3d import core + + +def test_sparse_array_set_bit_to(): + """Tests SparseArray behavior for set_bit_to().""" + + s = core.SparseArray() + s.set_bit_to(5, True) + assert s.get_bit(5) + + s = core.SparseArray.all_on() + s.set_bit_to(5, False) + assert not s.get_bit(5) + + +def test_sparse_array_clear(): + """Tests SparseArray behavior for clear().""" + + s = core.SparseArray.all_on() + s.clear() + assert s.is_zero() + assert not s.is_inverse() + assert s.get_num_subranges() == 0 + assert s.get_num_on_bits() == 0 + assert s.get_num_bits() == 0 + + s = core.SparseArray() + s.set_range(5, 10) + s.clear() + assert s.is_zero() + assert not s.is_inverse() + assert s.get_num_subranges() == 0 + assert s.get_num_on_bits() == 0 + assert s.get_num_bits() == 0 + + +def test_sparse_array_clear_range(): + """Tests SparseArray behavior for clear_range().""" + + # test clear_range with single overlapping on-range + # (clear_range extends beyond highest on-bit) + s = core.SparseArray() + s.set_range(2, 3) + s.clear_range(3, 3) + assert s.get_bit(2) + assert not s.get_bit(3) + + # same as above, using set_range_to + s = core.SparseArray() + s.set_range_to(True, 2, 3) + s.set_range_to(False, 3, 3) + assert s.get_bit(2) + assert not s.get_bit(3) + + # test clear_range using off-range which overlaps two on-ranges + # (lowest off-bit in lowest on-range, highest off-bit in highest on-range) + s = core.SparseArray() + s.set_range(2, 3) + s.set_range(7, 3) + s.clear_range(3, 6) + assert s.get_bit(2) + assert not s.get_bit(3) + assert not s.get_bit(8) + assert s.get_bit(9) + + # same as above, using set_range_to + s = core.SparseArray() + s.set_range_to(True, 2, 3) + s.set_range_to(True, 7, 3) + s.set_range_to(False, 3, 6) + assert s.get_bit(2) + assert not s.get_bit(3) + assert not s.get_bit(8) + assert s.get_bit(9) + + +def test_sparse_array_set_range(): + """Tests SparseArray behavior for set_range().""" + + # test set_range with single overlapping off-range + # (set_range extends beyond highest off-bit) + s = core.SparseArray.all_on() + s.clear_range(2, 3) + s.set_range(3, 3) + assert not s.get_bit(2) + assert s.get_bit(3) + + # same as above, using set_range_to + s = core.SparseArray.all_on() + s.set_range_to(False, 2, 3) + s.set_range_to(True, 3, 3) + assert not s.get_bit(2) + assert s.get_bit(3) + + # test set_range using on-range which overlaps two off-ranges + # (lowest on-bit in lowest off-range, highest on-bit in highest off-range) + s = core.SparseArray.all_on() + s.clear_range(2, 3) + s.clear_range(7, 3) + s.set_range(3, 6) + assert not s.get_bit(2) + assert s.get_bit(3) + assert s.get_bit(8) + assert not s.get_bit(9) + + # same as above, using set_range_to + s = core.SparseArray.all_on() + s.set_range_to(False, 2, 3) + s.set_range_to(False, 7, 3) + s.set_range_to(True, 3, 6) + assert not s.get_bit(2) + assert s.get_bit(3) + assert s.get_bit(8) + assert not s.get_bit(9) + + +def test_sparse_array_bits_in_common(): + """Tests SparseArray behavior for has_bits_in_common().""" + + s = core.SparseArray() + t = core.SparseArray() + s.set_range(2, 4) + t.set_range(5, 4) + assert s.has_bits_in_common(t) + + s = core.SparseArray() + t = core.SparseArray() + s.set_range(2, 4) + t.set_range(6, 4) + assert not s.has_bits_in_common(t) + + +def test_sparse_array_operations(): + """Tests SparseArray behavior for various operations.""" + + # test bitshift to left + s = core.SparseArray() + s.set_bit(2) + t = s << 2 + assert t.get_bit(4) + assert not t.get_bit(2) + + # test bitshift to right + s = core.SparseArray() + s.set_bit(4) + t = s >> 2 + assert t.get_bit(2) + assert not t.get_bit(4) + + # test bitwise AND + s = core.SparseArray() + t = core.SparseArray() + s.set_bit(2) + s.set_bit(3) + t.set_bit(1) + t.set_bit(3) + u = s & t + assert not u.get_bit(0) + assert not u.get_bit(1) + assert not u.get_bit(2) + assert u.get_bit(3) + + # test bitwise OR + s = core.SparseArray() + t = core.SparseArray() + s.set_bit(2) + s.set_bit(3) + t.set_bit(1) + t.set_bit(3) + u = s | t + assert not u.get_bit(0) + assert u.get_bit(1) + assert u.get_bit(2) + assert u.get_bit(3) + + # test bitwise XOR + s = core.SparseArray() + t = core.SparseArray() + s.set_bit(2) + s.set_bit(3) + t.set_bit(1) + t.set_bit(3) + u = s ^ t + assert not u.get_bit(0) + assert u.get_bit(1) + assert u.get_bit(2) + assert not u.get_bit(3) + + +def test_sparse_array_augm_assignment(): + """Tests SparseArray behavior for augmented assignments.""" + + # test in-place bitshift to left + s = t = core.SparseArray() + t <<= 2 + assert s is t + + # test in-place bitshift to right + s = t = core.SparseArray() + t >>= 2 + assert s is t + + # test in-place bitwise AND + s = t = core.SparseArray() + u = core.SparseArray() + t &= u + assert s is t + + # test in-place bitwise OR + s = t = core.SparseArray() + u = core.SparseArray() + t |= u + assert s is t + + # test in-place bitwise XOR + s = t = core.SparseArray() + u = core.SparseArray() + t ^= u + assert s is t From 6464327e6f41282109c671b6b5159fc2a2b5e5c7 Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 14 Apr 2019 17:59:55 +0200 Subject: [PATCH 4/7] tests: add more thorough unit test for SparseArray::clear_range --- tests/putil/test_sparsearray.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/tests/putil/test_sparsearray.py b/tests/putil/test_sparsearray.py index ae6d5208b5..b7767d6e8a 100644 --- a/tests/putil/test_sparsearray.py +++ b/tests/putil/test_sparsearray.py @@ -35,7 +35,22 @@ def test_sparse_array_clear(): def test_sparse_array_clear_range(): - """Tests SparseArray behavior for clear_range().""" + # Not using parametrize because there are too many values for that. + for mask in range(0x7f): + for begin in range(8): + for size in range(8): + b = core.BitArray(mask) + s = core.SparseArray(b) + + s.clear_range(begin, size) + b.clear_range(begin, size) + + assert core.BitArray(s) == b + assert s == core.SparseArray(b) + + +def test_sparse_array_set_clear_ranges(): + """Tests SparseArray behavior for setting and clearing ranges.""" # test clear_range with single overlapping on-range # (clear_range extends beyond highest on-bit) From 5d3499dc6483bcf5c3069f3b92240143cad1e250 Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 14 Apr 2019 18:01:23 +0200 Subject: [PATCH 5/7] x11display: fix crash when starting in fullscreen on Linux/X11 Fixes #618 --- panda/src/x11display/x11GraphicsWindow.cxx | 38 +++++----------------- 1 file changed, 8 insertions(+), 30 deletions(-) diff --git a/panda/src/x11display/x11GraphicsWindow.cxx b/panda/src/x11display/x11GraphicsWindow.cxx index 5599959cdb..0a5d969bdf 100644 --- a/panda/src/x11display/x11GraphicsWindow.cxx +++ b/panda/src/x11display/x11GraphicsWindow.cxx @@ -589,7 +589,13 @@ set_properties_now(WindowProperties &properties) { // OK, first figure out which CRTC the window is on. It may be on more // than one, actually, so grab a point in the center in order to figure // out which one it's more-or-less mostly on. - LPoint2i center = _properties.get_origin() + _properties.get_size() / 2; + LPoint2i center(0, 0); + if (_properties.has_origin()) { + center = _properties.get_origin(); + if (_properties.has_size()) { + center += _properties.get_size() / 2; + } + } int x, y, width, height; x11_pipe->find_fullscreen_crtc(center, x, y, width, height); @@ -628,7 +634,7 @@ set_properties_now(WindowProperties &properties) { // We may need to change the screen resolution. The code below is // suboptimal; in the future, we probably want to only touch the CRTC // that the window is on. - XRRScreenConfiguration *conf = _XRRGetScreenInfo(_display, _xwindow); + XRRScreenConfiguration *conf = _XRRGetScreenInfo(_display, _xwindow ? _xwindow : x11_pipe->get_root()); SizeID old_size_id = x11_pipe->_XRRConfigCurrentConfiguration(conf, &_orig_rotation); SizeID new_size_id = (SizeID) -1; int num_sizes = 0; @@ -1010,34 +1016,6 @@ open_window() { // Make sure we are not making X11 calls from other threads. LightReMutexHolder holder(x11GraphicsPipe::_x_mutex); - if (_properties.get_fullscreen() && x11_pipe->_have_xrandr) { - XRRScreenConfiguration* conf = _XRRGetScreenInfo(_display, x11_pipe->get_root()); - if (_orig_size_id == (SizeID) -1) { - _orig_size_id = x11_pipe->_XRRConfigCurrentConfiguration(conf, &_orig_rotation); - } - int num_sizes, new_size_id = -1; - XRRScreenSize *xrrs; - xrrs = x11_pipe->_XRRSizes(_display, 0, &num_sizes); - for (int i = 0; i < num_sizes; ++i) { - if (xrrs[i].width == _properties.get_x_size() && - xrrs[i].height == _properties.get_y_size()) { - new_size_id = i; - } - } - if (new_size_id == -1) { - x11display_cat.error() - << "Videocard has no supported display resolutions at specified res (" - << _properties.get_x_size() << " x " << _properties.get_y_size() <<")\n"; - _orig_size_id = -1; - return false; - } - if (new_size_id != _orig_size_id) { - _XRRSetScreenConfig(_display, conf, x11_pipe->get_root(), new_size_id, _orig_rotation, CurrentTime); - } else { - _orig_size_id = -1; - } - } - X11_Window parent_window = x11_pipe->get_root(); WindowHandle *window_handle = _properties.get_parent_window(); if (window_handle != nullptr) { From 5530074945d6b94f02b5a89edfc835009abcb726 Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 14 Apr 2019 22:10:32 +0200 Subject: [PATCH 6/7] cocoa: fix crash when typing with RIME as input method Fixes #620 --- panda/src/cocoadisplay/cocoaGraphicsWindow.mm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/panda/src/cocoadisplay/cocoaGraphicsWindow.mm b/panda/src/cocoadisplay/cocoaGraphicsWindow.mm index 0a140109c7..42490b43da 100644 --- a/panda/src/cocoadisplay/cocoaGraphicsWindow.mm +++ b/panda/src/cocoadisplay/cocoaGraphicsWindow.mm @@ -1618,7 +1618,7 @@ handle_key_event(NSEvent *event) { if ([event type] == NSKeyDown) { // Translate it to a unicode character for keystrokes. I would use // interpretKeyEvents and insertText, but that doesn't handle dead keys. - TISInputSourceRef input_source = TISCopyCurrentKeyboardInputSource(); + TISInputSourceRef input_source = TISCopyCurrentKeyboardLayoutInputSource(); CFDataRef layout_data = (CFDataRef)TISGetInputSourceProperty(input_source, kTISPropertyUnicodeKeyLayoutData); const UCKeyboardLayout *layout = (const UCKeyboardLayout *)CFDataGetBytePtr(layout_data); @@ -1827,7 +1827,7 @@ get_keyboard_map() const { const UCKeyboardLayout *layout; // Get the current keyboard layout data. - input_source = TISCopyCurrentKeyboardInputSource(); + input_source = TISCopyCurrentKeyboardLayoutInputSource(); layout_data = (CFDataRef) TISGetInputSourceProperty(input_source, kTISPropertyUnicodeKeyLayoutData); layout = (const UCKeyboardLayout *)CFDataGetBytePtr(layout_data); From 239dc400325fa530316fbc40a60987eca353ff89 Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 14 Apr 2019 22:11:20 +0200 Subject: [PATCH 7/7] device: fix crash when unplugging certain devices on macOS Fixes #621 --- panda/src/device/ioKitInputDevice.cxx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/panda/src/device/ioKitInputDevice.cxx b/panda/src/device/ioKitInputDevice.cxx index da1369932e..8468fd4816 100644 --- a/panda/src/device/ioKitInputDevice.cxx +++ b/panda/src/device/ioKitInputDevice.cxx @@ -22,8 +22,11 @@ #include "mouseButton.h" static void removal_callback(void *ctx, IOReturn result, void *sender) { - IOKitInputDevice *input_device = (IOKitInputDevice *)ctx; + // We need to hold a reference to this because it may otherwise be destroyed + // during the call to on_remove(). + PT(IOKitInputDevice) input_device = (IOKitInputDevice *)ctx; nassertv(input_device != nullptr); + nassertv(input_device->test_ref_count_integrity()); input_device->on_remove(); }