| // Copyright 2016 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ui/base/x/x11_window_event_manager.h" |
| |
| #include <stddef.h> |
| #include <xcb/xcb.h> |
| |
| #include "base/memory/singleton.h" |
| #include "ui/gfx/x/x11.h" |
| |
| namespace ui { |
| |
| namespace { |
| |
| // Asks the X server to set |xid|'s event mask to |new_mask|. |
| void SetEventMask(XID xid, uint32_t new_mask) { |
| XDisplay* display = gfx::GetXDisplay(); |
| xcb_connection_t* connection = XGetXCBConnection(display); |
| auto cookie = xcb_change_window_attributes(connection, xid, XCB_CW_EVENT_MASK, |
| &new_mask); |
| // Window |xid| may already be destroyed at this point, so the |
| // change_attributes request may give a BadWindow error. In this case, just |
| // ignore the error. |
| xcb_discard_reply(connection, cookie.sequence); |
| } |
| |
| } // anonymous namespace |
| |
| XScopedEventSelector::XScopedEventSelector(XID xid, uint32_t event_mask) |
| : xid_(xid), |
| event_mask_(event_mask), |
| event_manager_( |
| XWindowEventManager::GetInstance()->weak_ptr_factory_.GetWeakPtr()) { |
| event_manager_->SelectEvents(xid_, event_mask_); |
| } |
| |
| XScopedEventSelector::~XScopedEventSelector() { |
| if (event_manager_) |
| event_manager_->DeselectEvents(xid_, event_mask_); |
| } |
| |
| // static |
| XWindowEventManager* XWindowEventManager::GetInstance() { |
| return base::Singleton<XWindowEventManager>::get(); |
| } |
| |
| class XWindowEventManager::MultiMask { |
| public: |
| MultiMask() { |
| for (int i = 0; i < kMaskSize; i++) |
| mask_bits_[i] = 0; |
| } |
| |
| ~MultiMask() {} |
| |
| void AddMask(uint32_t mask) { |
| for (int i = 0; i < kMaskSize; i++) { |
| if (mask & (1 << i)) |
| mask_bits_[i]++; |
| } |
| } |
| |
| void RemoveMask(uint32_t mask) { |
| for (int i = 0; i < kMaskSize; i++) { |
| if (mask & (1 << i)) { |
| DCHECK(mask_bits_[i]); |
| mask_bits_[i]--; |
| } |
| } |
| } |
| |
| uint32_t ToMask() const { |
| uint32_t mask = NoEventMask; |
| for (int i = 0; i < kMaskSize; i++) { |
| if (mask_bits_[i]) |
| mask |= (1 << i); |
| } |
| return mask; |
| } |
| |
| private: |
| static constexpr auto kMaskSize = 25; |
| |
| int mask_bits_[kMaskSize]; |
| |
| DISALLOW_COPY_AND_ASSIGN(MultiMask); |
| }; |
| |
| XWindowEventManager::XWindowEventManager() : weak_ptr_factory_(this) {} |
| |
| XWindowEventManager::~XWindowEventManager() { |
| // Clear events still requested by not-yet-deleted XScopedEventSelectors. |
| for (const auto& mask_pair : mask_map_) |
| SetEventMask(mask_pair.first, NoEventMask); |
| } |
| |
| void XWindowEventManager::SelectEvents(XID xid, uint32_t event_mask) { |
| std::unique_ptr<MultiMask>& mask = mask_map_[xid]; |
| if (!mask) |
| mask.reset(new MultiMask()); |
| uint32_t old_mask = mask_map_[xid]->ToMask(); |
| mask->AddMask(event_mask); |
| AfterMaskChanged(xid, old_mask); |
| } |
| |
| void XWindowEventManager::DeselectEvents(XID xid, uint32_t event_mask) { |
| DCHECK(mask_map_.find(xid) != mask_map_.end()); |
| std::unique_ptr<MultiMask>& mask = mask_map_[xid]; |
| uint32_t old_mask = mask->ToMask(); |
| mask->RemoveMask(event_mask); |
| AfterMaskChanged(xid, old_mask); |
| } |
| |
| void XWindowEventManager::AfterMaskChanged(XID xid, uint32_t old_mask) { |
| uint32_t new_mask = mask_map_[xid]->ToMask(); |
| if (new_mask == old_mask) |
| return; |
| |
| SetEventMask(xid, new_mask); |
| |
| if (new_mask == NoEventMask) |
| mask_map_.erase(xid); |
| } |
| |
| } // namespace ui |