blob: 6072c9f5fbae0ab07a5ccce1da2bb6470715eb97 [file] [log] [blame]
// 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 <X11/Xlib.h>
#include <X11/Xlib-xcb.h>
#include <xcb/xcb.h>
#include "base/memory/singleton.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