text: // 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
  