blob: fbf2894c28702e5d7b9c7fb610f4c58ad9fafc19 [file] [log] [blame]
// Copyright (c) 2010 The Chromium OS 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 "window_manager/x11/mock_x_connection.h"
#include <fcntl.h>
#include <unistd.h>
#include <list>
extern "C" {
#include <X11/extensions/sync.h>
#include <X11/extensions/Xdamage.h>
}
#include "base/logging.h"
#include "base/eintr_wrapper.h"
#include "window_manager/image_enums.h"
#include "window_manager/util.h"
#include "window_manager/x11/x_connection_internal.h"
using std::list;
using std::make_pair;
using std::map;
using std::pair;
using std::string;
using std::tr1::shared_ptr;
using std::vector;
using window_manager::util::FindWithDefault;
using window_manager::util::XidStr;
namespace window_manager {
const int MockXConnection::kDisplayWidth = 1280;
const int MockXConnection::kDisplayHeight = 800;
const XID MockXConnection::kTransparentCursor = 1000; // arbitrary
MockXConnection::MockXConnection()
: windows_(),
stacked_xids_(new Stacker<XWindow>),
next_xid_(1),
root_(CreateWindow(0, // parent
Rect(0, 0, kDisplayWidth, kDisplayHeight),
true, // override_redirect
false, // input_only
0, // event_mask
0)), // visual
overlay_(CreateWindow(root_, // parent
Rect(0, 0, kDisplayWidth, kDisplayHeight),
true, // override_redirect
false, // input_only
0, // event_mask
0)), // visual
next_atom_(1000),
focused_xid_(None),
last_focus_timestamp_(0),
current_time_(0),
pointer_grab_xid_(None),
keyboard_grab_xid_(None),
num_keymap_refreshes_(0),
pointer_pos_(kDisplayWidth / 2, kDisplayHeight / 2),
cursor_shown_(true),
using_detectable_keyboard_auto_repeat_(false),
connection_pipe_has_data_(false),
num_pointer_ungrabs_with_replayed_events_(0) {
PCHECK(HANDLE_EINTR(pipe(connection_pipe_fds_)) != -1);
PCHECK(HANDLE_EINTR(
fcntl(connection_pipe_fds_[0], F_SETFL, O_NONBLOCK)) != -1);
PCHECK(HANDLE_EINTR(
fcntl(connection_pipe_fds_[1], F_SETFL, O_NONBLOCK)) != -1);
// Arbitrary large numbers unlikely to be used by other events.
damage_event_base_ = 10000;
shape_event_base_ = 10010;
randr_event_base_ = 10020;
sync_event_base_ = 10030;
}
MockXConnection::~MockXConnection() {
PCHECK(HANDLE_EINTR(close(connection_pipe_fds_[0])) != -1);
PCHECK(HANDLE_EINTR(close(connection_pipe_fds_[1])) != -1);
}
bool MockXConnection::GetWindowGeometry(XWindow xid, WindowGeometry* geom_out) {
CHECK(geom_out);
if (WindowInfo* window_info = GetWindowInfo(xid)) {
geom_out->bounds = window_info->bounds;
geom_out->border_width = window_info->border_width;
geom_out->depth = window_info->depth;
return true;
}
if (PixmapInfo* pixmap_info = GetPixmapInfo(xid)) {
geom_out->bounds.reset(Point(), pixmap_info->size);
geom_out->border_width = 0;
geom_out->depth = pixmap_info->depth;
return true;
}
return false;
}
bool MockXConnection::MapWindow(XWindow xid) {
WindowInfo* info = GetWindowInfo(xid);
if (!info)
return false;
info->mapped = true;
info->changed = true;
return true;
}
bool MockXConnection::UnmapWindow(XWindow xid) {
WindowInfo* info = GetWindowInfo(xid);
if (!info)
return false;
info->mapped = false;
if (focused_xid_ == xid)
focused_xid_ = None;
info->changed = true;
return true;
}
bool MockXConnection::MoveWindow(XWindow xid, const Point& pos) {
WindowInfo* info = GetWindowInfo(xid);
if (!info)
return false;
info->bounds.move(pos);
info->changed = true;
info->num_configures++;
return true;
}
bool MockXConnection::ResizeWindow(XWindow xid, const Size& size) {
WindowInfo* info = GetWindowInfo(xid);
if (!info)
return false;
info->bounds.resize(size, GRAVITY_NORTHWEST);
info->changed = true;
info->num_configures++;
return true;
}
bool MockXConnection::ConfigureWindow(XWindow xid, const Rect& bounds) {
WindowInfo* info = GetWindowInfo(xid);
if (!info)
return false;
info->bounds = bounds;
info->changed = true;
info->num_configures++;
return true;
}
bool MockXConnection::RaiseWindow(XWindow xid) {
if (!stacked_xids_->Contains(xid))
return false;
WindowInfo* info = GetWindowInfo(xid);
if (!info)
return false;
stacked_xids_->Remove(xid);
stacked_xids_->AddOnTop(xid);
info->num_configures++;
return true;
}
bool MockXConnection::FocusWindow(XWindow xid, XTime event_time) {
WindowInfo* info = GetWindowInfo(xid);
if (!info)
return false;
// The X server ignores requests with old timestamps.
if (event_time < last_focus_timestamp_)
return true;
focused_xid_ = xid;
last_focus_timestamp_ = event_time;
return true;
}
bool MockXConnection::StackWindow(XWindow xid, XWindow other, bool above) {
if (!stacked_xids_->Contains(xid) || !stacked_xids_->Contains(other))
return false;
WindowInfo* info = GetWindowInfo(xid);
if (!info)
return false;
stacked_xids_->Remove(xid);
if (above)
stacked_xids_->AddAbove(xid, other);
else
stacked_xids_->AddBelow(xid, other);
info->num_configures++;
return true;
}
bool MockXConnection::AddButtonGrabOnWindow(
XWindow xid, int button, int event_mask, bool synchronous) {
WindowInfo* info = GetWindowInfo(xid);
if (!info)
return false;
info->button_grabs[button] =
WindowInfo::ButtonGrabInfo(event_mask, synchronous);
return true;
}
bool MockXConnection::SetWindowBorderWidth(XWindow xid, int width) {
WindowInfo* info = GetWindowInfo(xid);
if (!info)
return false;
info->border_width = width;
info->num_configures++;
return true;
}
bool MockXConnection::SelectInputOnWindow(
XWindow xid, int event_mask, bool preserve_existing) {
WindowInfo* info = GetWindowInfo(xid);
if (!info)
return false;
info->event_mask = preserve_existing ?
(info->event_mask | event_mask) : event_mask;
return true;
}
bool MockXConnection::DeselectInputOnWindow(XWindow xid, int event_mask) {
WindowInfo* info = GetWindowInfo(xid);
if (!info)
return false;
info->event_mask &= ~event_mask;
return true;
}
bool MockXConnection::RemoveButtonGrabOnWindow(XWindow xid, int button) {
WindowInfo* info = GetWindowInfo(xid);
if (!info)
return false;
info->button_grabs.erase(button);
return true;
}
bool MockXConnection::GrabPointer(XWindow xid,
int event_mask,
XTime timestamp,
XID cursor) {
WindowInfo* info = GetWindowInfo(xid);
if (!info)
return false;
if (pointer_grab_xid_ != None) {
LOG(ERROR) << "Pointer is already grabbed for " << XidStr(pointer_grab_xid_)
<< "; ignoring request to grab it for " << XidStr(xid);
return false;
}
pointer_grab_xid_ = xid;
return true;
}
bool MockXConnection::UngrabPointer(bool replay_events, XTime timestamp) {
pointer_grab_xid_ = None;
if (replay_events)
num_pointer_ungrabs_with_replayed_events_++;
return true;
}
bool MockXConnection::GrabKeyboard(XWindow xid, XTime timestamp) {
WindowInfo* info = GetWindowInfo(xid);
if (!info)
return false;
if (keyboard_grab_xid_ != None) {
LOG(ERROR) << "Keyboard is already grabbed for "
<< XidStr(keyboard_grab_xid_)
<< "; ignoring request to grab it for " << XidStr(xid);
return false;
}
keyboard_grab_xid_ = xid;
return true;
}
bool MockXConnection::GetSizeHintsForWindow(XWindow xid, SizeHints* hints_out) {
CHECK(hints_out);
WindowInfo* info = GetWindowInfo(xid);
if (!info)
return false;
*hints_out = info->size_hints;
return true;
}
bool MockXConnection::GetTransientHintForWindow(XWindow xid,
XWindow* owner_out) {
CHECK(owner_out);
WindowInfo* info = GetWindowInfo(xid);
if (!info)
return false;
*owner_out = info->transient_for;
return true;
}
bool MockXConnection::GetWindowAttributes(XWindow xid,
WindowAttributes* attr_out) {
CHECK(attr_out);
WindowInfo* info = GetWindowInfo(xid);
if (!info)
return false;
attr_out->window_class = info->input_only ?
WindowAttributes::WINDOW_CLASS_INPUT_ONLY :
WindowAttributes::WINDOW_CLASS_INPUT_OUTPUT;
attr_out->map_state = info->mapped ?
WindowAttributes::MAP_STATE_VIEWABLE :
WindowAttributes::MAP_STATE_UNMAPPED;
attr_out->override_redirect = info->override_redirect;
return true;
}
bool MockXConnection::RedirectSubwindowsForCompositing(XWindow xid) {
WindowInfo* info = GetWindowInfo(xid);
if (!info)
return false;
info->redirect_subwindows = true;
for (map<XWindow, shared_ptr<WindowInfo> >::iterator it = windows_.begin();
it != windows_.end(); ++it) {
WindowInfo* other_win_info = it->second.get();
if (other_win_info->parent == xid)
other_win_info->redirected = true;
}
return true;
}
bool MockXConnection::RedirectWindowForCompositing(XWindow xid) {
WindowInfo* info = GetWindowInfo(xid);
if (!info)
return false;
info->redirected = true;
return true;
}
bool MockXConnection::UnredirectWindowForCompositing(XWindow xid) {
WindowInfo* info = GetWindowInfo(xid);
if (!info)
return false;
info->redirected = false;
return true;
}
XPixmap MockXConnection::CreatePixmap(XDrawable drawable,
const Size& size,
int depth) {
XID xid = next_xid_++;
shared_ptr<PixmapInfo> info(new PixmapInfo(xid, size, depth));
pixmaps_[xid] = info;
return xid;
}
XPixmap MockXConnection::GetCompositingPixmapForWindow(XWindow xid) {
WindowInfo* info = GetWindowInfo(xid);
if (!info)
return 0;
return CreatePixmap(xid, info->bounds.size(), info->depth);
}
bool MockXConnection::FreePixmap(XPixmap pixmap) {
map<XID, shared_ptr<PixmapInfo> >::iterator it = pixmaps_.find(pixmap);
if (it == pixmaps_.end())
return false;
pixmaps_.erase(it);
return true;
}
XWindow MockXConnection::CreateWindow(
XWindow parent,
const Rect& bounds,
bool override_redirect,
bool input_only,
int event_mask,
XVisualID visual) {
XWindow xid = next_xid_++;
shared_ptr<WindowInfo> info(new WindowInfo(xid, parent));
info->bounds = bounds;
info->override_redirect = override_redirect;
info->input_only = input_only;
info->event_mask = event_mask;
info->visual = visual;
windows_[xid] = info;
stacked_xids_->AddOnTop(xid);
const WindowInfo* parent_info = GetWindowInfo(parent);
if (parent_info && parent_info->redirect_subwindows)
info->redirected = true;
return xid;
}
bool MockXConnection::DestroyWindow(XWindow xid) {
map<XWindow, shared_ptr<WindowInfo> >::iterator it = windows_.find(xid);
if (it == windows_.end())
return false;
windows_.erase(it);
stacked_xids_->Remove(xid);
if (focused_xid_ == xid)
focused_xid_ = None;
// Release any selections held by this window.
vector<XAtom> orphaned_selections;
for (map<XAtom, XWindow>::const_iterator it = selection_owners_.begin();
it != selection_owners_.end(); ++it) {
if (it->second == xid)
orphaned_selections.push_back(it->first);
}
for (vector<XAtom>::const_iterator it = orphaned_selections.begin();
it != orphaned_selections.end(); ++it) {
selection_owners_.erase(*it);
}
return true;
}
bool MockXConnection::IsWindowShaped(XWindow xid) {
WindowInfo* info = GetWindowInfo(xid);
if (!info)
return false;
return (info->shape.get() != NULL);
}
bool MockXConnection::SelectShapeEventsOnWindow(XWindow xid) {
WindowInfo* info = GetWindowInfo(xid);
if (!info)
return false;
info->shape_events_selected = true;
return true;
}
bool MockXConnection::GetWindowBoundingRegion(XWindow xid, ByteMap* bytemap) {
WindowInfo* info = GetWindowInfo(xid);
if (!info)
return false;
bytemap->Resize(info->bounds.size());
bytemap->Clear(0);
if (info->shape.get())
bytemap->Copy(*(info->shape.get()));
else
bytemap->Clear(0xff);
return true;
}
bool MockXConnection::SetWindowBoundingRegionToRect(XWindow xid,
const Rect& region) {
WindowInfo* info = GetWindowInfo(xid);
if (!info)
return false;
info->shape.reset(
new ByteMap(Size(region.x + region.width, region.y + region.height)));
info->shape->Clear(0);
info->shape->SetRectangle(region, 0xff);
return true;
}
bool MockXConnection::ResetWindowBoundingRegionToDefault(XWindow xid) {
WindowInfo* info = GetWindowInfo(xid);
if (!info)
return false;
info->shape.reset();
return true;
}
bool MockXConnection::SelectRandREventsOnWindow(XWindow xid) {
WindowInfo* info = GetWindowInfo(xid);
if (!info)
return false;
info->randr_events_selected = true;
return true;
}
bool MockXConnection::GetAtoms(const vector<string>& names,
vector<XAtom>* atoms_out) {
CHECK(atoms_out);
atoms_out->clear();
for (vector<string>::const_iterator name_it = names.begin();
name_it != names.end(); ++name_it) {
map<string, XAtom>::const_iterator find_it = name_to_atom_.find(*name_it);
if (find_it != name_to_atom_.end()) {
atoms_out->push_back(find_it->second);
} else {
XAtom atom = next_atom_;
atoms_out->push_back(atom);
name_to_atom_[*name_it] = atom;
atom_to_name_[atom] = *name_it;
next_atom_++;
}
}
return true;
}
bool MockXConnection::GetIntArrayProperty(XWindow xid,
XAtom xatom,
vector<int>* values) {
WindowInfo* info = GetWindowInfo(xid);
if (!info)
return false;
map<XAtom, vector<int> >::const_iterator it =
info->int_properties.find(xatom);
if (it == info->int_properties.end())
return false;
*values = it->second;
return true;
}
bool MockXConnection::SetIntArrayProperty(XWindow xid,
XAtom xatom,
XAtom type,
const vector<int>& values) {
WindowInfo* info = GetWindowInfo(xid);
if (!info)
return false;
info->int_properties[xatom] = values;
// TODO: Also save type.
Closure* cb =
FindWithDefault(property_callbacks_,
make_pair(xid, xatom),
shared_ptr<Closure>(static_cast<Closure*>(NULL))).get();
if (cb)
cb->Run();
return true;
}
bool MockXConnection::GetStringProperty(XWindow xid, XAtom xatom, string* out) {
WindowInfo* info = GetWindowInfo(xid);
if (!info)
return false;
map<XAtom, string>::const_iterator it = info->string_properties.find(xatom);
if (it == info->string_properties.end())
return false;
*out = it->second;
return true;
}
bool MockXConnection::SetStringProperty(XWindow xid,
XAtom xatom,
const string& value) {
WindowInfo* info = GetWindowInfo(xid);
if (!info)
return false;
info->string_properties[xatom] = value;
Closure* cb =
FindWithDefault(property_callbacks_,
make_pair(xid, xatom),
shared_ptr<Closure>(static_cast<Closure*>(NULL))).get();
if (cb)
cb->Run();
return true;
}
bool MockXConnection::DeletePropertyIfExists(XWindow xid, XAtom xatom) {
WindowInfo* info = GetWindowInfo(xid);
if (!info)
return false;
info->int_properties.erase(xatom);
info->string_properties.erase(xatom);
return true;
}
bool MockXConnection::IsEventPending() {
return !queued_events_.empty();
}
bool MockXConnection::WaitForPropertyChange(XWindow xid, XTime* timestamp_out) {
if (timestamp_out) {
current_time_ += 10;
*timestamp_out = current_time_;
}
return true;
}
bool MockXConnection::SendClientMessageEvent(XWindow dest_xid,
XWindow xid,
XAtom message_type,
long data[5],
int event_mask) {
WindowInfo* info = GetWindowInfo(dest_xid);
if (!info)
return false;
XEvent event;
x_connection_internal::InitXClientMessageEvent(
&event, xid, message_type, data);
info->client_messages.push_back(event.xclient);
return true;
}
bool MockXConnection::SendConfigureNotifyEvent(XWindow xid,
const Rect& bounds,
int border_width,
XWindow above_xid,
bool override_redirect) {
WindowInfo* info = GetWindowInfo(xid);
if (!info)
return false;
XEvent event;
x_connection_internal::InitXConfigureEvent(
&event, xid, bounds, border_width, above_xid, override_redirect);
info->configure_notify_events.push_back(event.xconfigure);
return true;
}
bool MockXConnection::GetAtomName(XAtom atom, string* name) {
CHECK(name);
map<XAtom, string>::const_iterator it = atom_to_name_.find(atom);
if (it == atom_to_name_.end())
return false;
*name = it->second;
return true;
}
XWindow MockXConnection::GetSelectionOwner(XAtom atom) {
map<XAtom, XWindow>::const_iterator it = selection_owners_.find(atom);
return (it == selection_owners_.end()) ? None : it->second;
}
bool MockXConnection::SetSelectionOwner(
XAtom atom, XWindow xid, XTime timestamp) {
selection_owners_[atom] = xid;
return true;
}
bool MockXConnection::GetImage(XID drawable,
const Rect& bounds,
int drawable_depth,
scoped_ptr_malloc<uint8_t>* data_out,
ImageFormat* format_out) {
CHECK(data_out);
CHECK(format_out);
WindowInfo* info = GetWindowInfo(drawable);
if (!info)
return false;
// TODO: Make data settable in the WindowInfo so it can be tested.
data_out->reset(NULL);
*format_out = (drawable_depth == 32) ?
IMAGE_FORMAT_RGBA_32 :
IMAGE_FORMAT_RGBX_32;
return true;
}
bool MockXConnection::SetWindowCursor(XWindow xid, XID cursor) {
WindowInfo* info = GetWindowInfo(xid);
if (!info)
return false;
info->cursor = cursor;
return true;
}
bool MockXConnection::GrabKey(KeyCode keycode, uint32 modifiers) {
grabbed_keys_.insert(make_pair(keycode, modifiers));
return true;
}
bool MockXConnection::UngrabKey(KeyCode keycode, uint32 modifiers) {
grabbed_keys_.erase(make_pair(keycode, modifiers));
return true;
}
void MockXConnection::SetSyncCounter(XID counter_id, int64_t value) {
sync_counters_[counter_id] = value;
}
XID MockXConnection::CreateSyncCounterAlarm(XID counter_id,
int64_t initial_trigger_value) {
if (sync_counters_.count(counter_id) == 0)
sync_counters_[counter_id] = 0;
XID alarm_id = next_xid_++;
sync_counter_alarms_[alarm_id] =
shared_ptr<SyncCounterAlarmInfo>(
new SyncCounterAlarmInfo(counter_id, initial_trigger_value));
return alarm_id;
}
void MockXConnection::DestroySyncCounterAlarm(XID alarm_id) {
CHECK(sync_counter_alarms_.erase(alarm_id))
<< "Sync counter alarm " << XidStr(alarm_id) << " not registered";
}
bool MockXConnection::QueryPointerPosition(Point* absolute_pos_out) {
*absolute_pos_out = pointer_pos_;
return true;
}
bool MockXConnection::SetWindowBackgroundPixmap(XWindow xid, XPixmap pixmap) {
WindowInfo* info = GetWindowInfo(xid);
if (!info)
return false;
info->background_pixmap = pixmap;
return true;
}
bool MockXConnection::GetParentWindow(XWindow xid, XWindow* parent_out) {
DCHECK(parent_out);
WindowInfo* info = GetWindowInfo(xid);
if (!info)
return false;
*parent_out = info->parent;
return true;
}
bool MockXConnection::GetChildWindows(XWindow xid,
vector<XWindow>* children_out) {
CHECK(children_out);
children_out->clear();
WindowInfo* info = GetWindowInfo(xid);
if (!info)
return false;
// Add the children in bottom-to-top order to match XQueryTree().
for (list<XWindow>::const_reverse_iterator it =
stacked_xids_->items().rbegin();
it != stacked_xids_->items().rend(); ++it) {
const WindowInfo* child_info = GetWindowInfo(*it);
CHECK(child_info) << "No info found for window " << *it;
if (child_info->parent == xid)
children_out->push_back(*it);
}
return true;
}
KeySym MockXConnection::GetKeySymFromKeyCode(KeyCode keycode) {
map<KeyCode, vector<KeySym> >::iterator it =
keycodes_to_keysyms_.find(keycode);
if (it == keycodes_to_keysyms_.end() || it->second.empty())
return 0;
return it->second.front();
}
KeyCode MockXConnection::GetKeyCodeFromKeySym(KeySym keysym) {
return FindWithDefault(keysyms_to_keycodes_, keysym, static_cast<KeyCode>(0));
}
MockXConnection::WindowInfo::WindowInfo(XWindow xid, XWindow parent)
: xid(xid),
parent(parent),
bounds(-1, -1, 1, 1),
border_width(0),
depth(24),
mapped(false),
override_redirect(false),
input_only(false),
redirect_subwindows(false),
redirected(false),
event_mask(0),
transient_for(None),
cursor(0),
shape(NULL),
shape_events_selected(false),
randr_events_selected(false),
changed(false),
num_configures(0),
background_pixmap(0) {
}
MockXConnection::PixmapInfo::PixmapInfo(XWindow xid,
const Size& size,
int depth)
: xid(xid),
size(size),
depth(depth) {
}
MockXConnection::WindowInfo* MockXConnection::GetWindowInfo(XWindow xid) const {
map<XWindow, shared_ptr<WindowInfo> >::const_iterator it = windows_.find(xid);
return (it != windows_.end()) ? it->second.get() : NULL;
}
MockXConnection::PixmapInfo* MockXConnection::GetPixmapInfo(XPixmap xid) const {
map<XPixmap, shared_ptr<PixmapInfo> >::const_iterator it = pixmaps_.find(xid);
return (it != pixmaps_.end()) ? it->second.get() : NULL;
}
MockXConnection::SyncCounterAlarmInfo* MockXConnection::GetSyncCounterAlarmInfo(
XID xid) const {
map<XID, shared_ptr<SyncCounterAlarmInfo> >::const_iterator it =
sync_counter_alarms_.find(xid);
return (it != sync_counter_alarms_.end()) ? it->second.get() : NULL;
}
int64_t MockXConnection::GetSyncCounterValueOrDie(XID counter_id) const {
map<XID, int64_t>::const_iterator it = sync_counters_.find(counter_id);
CHECK(it != sync_counters_.end());
return it->second;
}
void MockXConnection::AddKeyMapping(KeyCode keycode, KeySym keysym) {
keycodes_to_keysyms_[keycode].push_back(keysym);
CHECK(keysyms_to_keycodes_.insert(make_pair(keysym, keycode)).second)
<< "Keysym " << keysym << " is already mapped to a keycode";
}
void MockXConnection::RemoveKeyMapping(KeyCode keycode, KeySym keysym) {
map<KeyCode, vector<KeySym> >::iterator keycode_it =
keycodes_to_keysyms_.find(keycode);
CHECK(keycode_it != keycodes_to_keysyms_.end())
<< "Keycode " << keycode << " isn't mapped to anything";
vector<KeySym>::iterator keysym_vector_it =
find(keycode_it->second.begin(), keycode_it->second.end(), keysym);
CHECK(keysym_vector_it != keycode_it->second.end())
<< "Keycode " << keycode << " isn't mapped to keysym " << keysym;
keycode_it->second.erase(keysym_vector_it);
map<KeySym, KeyCode>::iterator keysym_it =
keysyms_to_keycodes_.find(keysym);
CHECK(keysym_it != keysyms_to_keycodes_.end())
<< "Keysym " << keysym << " isn't mapped";
CHECK(keysym_it->second == keycode)
<< "Keysym " << keysym << " is mapped to keycode " << keysym_it->second
<< " rather than " << keycode;
keysyms_to_keycodes_.erase(keysym_it);
}
XWindow MockXConnection::GetWindowBelowWindow(XWindow xid) const {
const XWindow* other_xid = stacked_xids_->GetUnder(xid);
return other_xid ? *other_xid : 0;
}
void MockXConnection::AppendEventToQueue(const XEvent& event,
bool write_to_fd) {
queued_events_.push(event);
if (write_to_fd && !connection_pipe_has_data_) {
unsigned char data = 1;
PCHECK(HANDLE_EINTR(write(connection_pipe_fds_[1], &data, 1)) == 1);
connection_pipe_has_data_ = true;
}
}
void MockXConnection::RegisterPropertyCallback(
XWindow xid, XAtom xatom, Closure* cb) {
CHECK(cb);
CHECK(property_callbacks_.insert(
make_pair(make_pair(xid, xatom), shared_ptr<Closure>(cb))).second);
}
void MockXConnection::InitButtonEvent(XEvent* event,
XWindow xid,
const Point& pos,
int button,
bool press) const {
CHECK(event);
const WindowInfo* info = GetWindowInfoOrDie(xid);
XButtonEvent* button_event = &(event->xbutton);
memset(button_event, 0, sizeof(*button_event));
button_event->type = press ? ButtonPress : ButtonRelease;
button_event->window = info->xid;
button_event->x = pos.x;
button_event->y = pos.y;
button_event->x_root = info->bounds.x + pos.x;
button_event->y_root = info->bounds.y + pos.y;
button_event->button = button;
}
void MockXConnection::InitKeyEvent(XEvent* event,
XWindow xid,
KeyCode key_code,
uint32_t modifiers,
XTime time,
bool press) const {
CHECK(event);
XKeyEvent* key_event = &(event->xkey);
memset(key_event, 0, sizeof(*key_event));
key_event->type = press ? KeyPress : KeyRelease;
key_event->window = xid;
key_event->state = modifiers;
key_event->keycode = key_code;
key_event->time = time;
}
void MockXConnection::InitClientMessageEvent(XEvent* event,
XWindow xid,
XAtom type,
long arg1,
long arg2,
long arg3,
long arg4,
long arg5) const {
CHECK(event);
XClientMessageEvent* client_event = &(event->xclient);
memset(client_event, 0, sizeof(*client_event));
client_event->type = ClientMessage;
client_event->window = xid;
client_event->message_type = type;
client_event->format = kLongFormat;
client_event->data.l[0] = arg1;
client_event->data.l[1] = arg2;
client_event->data.l[2] = arg3;
client_event->data.l[3] = arg4;
client_event->data.l[4] = arg5;
}
void MockXConnection::InitConfigureNotifyEvent(XEvent* event,
XWindow xid) const {
CHECK(event);
const WindowInfo* info = GetWindowInfoOrDie(xid);
XConfigureEvent* conf_event = &(event->xconfigure);
memset(conf_event, 0, sizeof(*conf_event));
conf_event->type = ConfigureNotify;
conf_event->window = info->xid;
conf_event->above = GetWindowBelowWindow(xid);
conf_event->override_redirect = info->override_redirect;
conf_event->x = info->bounds.x;
conf_event->y = info->bounds.y;
conf_event->width = info->bounds.width;
conf_event->height = info->bounds.height;
}
void MockXConnection::InitConfigureRequestEvent(XEvent* event,
XWindow xid,
const Rect& bounds) const {
CHECK(event);
XConfigureRequestEvent* conf_event = &(event->xconfigurerequest);
memset(conf_event, 0, sizeof(*conf_event));
conf_event->type = ConfigureRequest;
conf_event->window = xid;
conf_event->x = bounds.x;
conf_event->y = bounds.y;
conf_event->width = bounds.width;
conf_event->height = bounds.height;
conf_event->value_mask = CWX | CWY | CWWidth | CWHeight;
}
void MockXConnection::InitCreateWindowEvent(XEvent* event, XWindow xid) const {
CHECK(event);
const WindowInfo* info = GetWindowInfoOrDie(xid);
XCreateWindowEvent* create_event = &(event->xcreatewindow);
memset(create_event, 0, sizeof(*create_event));
create_event->type = CreateNotify;
create_event->parent = info->parent;
create_event->window = info->xid;
create_event->x = info->bounds.x;
create_event->y = info->bounds.y;
create_event->width = info->bounds.width;
create_event->height = info->bounds.height;
create_event->border_width = info->border_width;
create_event->override_redirect = info->override_redirect ? True : False;
}
void MockXConnection::InitDamageNotifyEvent(XEvent* event,
XWindow drawable,
const Rect& bounds) const {
CHECK(event);
XDamageNotifyEvent* damage_event =
reinterpret_cast<XDamageNotifyEvent*>(event);
memset(damage_event, 0, sizeof(*damage_event));
damage_event->type = damage_event_base_ + XDamageNotify;
damage_event->drawable = drawable;
damage_event->area.x = bounds.x;
damage_event->area.y = bounds.y;
damage_event->area.width = bounds.width;
damage_event->area.height = bounds.height;
}
void MockXConnection::InitDestroyWindowEvent(XEvent* event, XWindow xid) const {
CHECK(event);
XDestroyWindowEvent* destroy_event = &(event->xdestroywindow);
memset(destroy_event, 0, sizeof(*destroy_event));
destroy_event->type = DestroyNotify;
destroy_event->window = xid;
}
void MockXConnection::InitEnterOrLeaveWindowEvent(XEvent* event,
XWindow xid,
const Point& pos,
bool enter) const {
CHECK(event);
const WindowInfo* info = GetWindowInfoOrDie(xid);
XEnterWindowEvent* enter_event = &(event->xcrossing);
memset(enter_event, 0, sizeof(*enter_event));
enter_event->type = enter ? EnterNotify : LeaveNotify;
enter_event->window = info->xid;
enter_event->x = pos.x;
enter_event->y = pos.y;
enter_event->x_root = info->bounds.x + pos.x;
enter_event->y_root = info->bounds.y + pos.y;
// Leave everything else blank for now; we don't use it.
}
void MockXConnection::InitMapEvent(XEvent* event, XWindow xid) const {
CHECK(event);
XMapEvent* map_event = &(event->xmap);
memset(map_event, 0, sizeof(*map_event));
map_event->type = MapNotify;
map_event->window = xid;
}
void MockXConnection::InitMapRequestEvent(XEvent* event, XWindow xid) const {
CHECK(event);
const WindowInfo* info = GetWindowInfoOrDie(xid);
XMapRequestEvent* req_event = &(event->xmaprequest);
memset(req_event, 0, sizeof(*req_event));
req_event->type = MapRequest;
req_event->window = info->xid;
req_event->parent = info->parent;
}
void MockXConnection::InitMotionNotifyEvent(XEvent* event,
XWindow xid,
const Point& pos) const {
CHECK(event);
const WindowInfo* info = GetWindowInfoOrDie(xid);
XMotionEvent* motion_event = &(event->xmotion);
memset(motion_event, 0, sizeof(*motion_event));
motion_event->type = MotionNotify;
motion_event->window = info->xid;
motion_event->x = pos.x;
motion_event->y = pos.y;
motion_event->x_root = info->bounds.x + pos.x;
motion_event->y_root = info->bounds.y + pos.y;
// Leave everything else blank for now; we don't use it.
}
void MockXConnection::InitPropertyNotifyEvent(XEvent* event,
XWindow xid,
XAtom xatom) const {
CHECK(event);
XPropertyEvent* property_event = &(event->xproperty);
memset(property_event, 0, sizeof(*property_event));
property_event->type = PropertyNotify;
property_event->window = xid;
property_event->atom = xatom;
property_event->state = PropertyNewValue;
}
void MockXConnection::InitSyncAlarmNotifyEvent(XEvent* event,
XID alarm_xid,
int64_t value) const {
CHECK(event);
XSyncAlarmNotifyEvent* alarm_event =
reinterpret_cast<XSyncAlarmNotifyEvent*>(event);
memset(alarm_event, 0, sizeof(*alarm_event));
alarm_event->type = sync_event_base_ + XSyncAlarmNotify;
alarm_event->alarm = alarm_xid;
x_connection_internal::StoreInt64InXSyncValue(
value, &(alarm_event->counter_value));
}
void MockXConnection::InitUnmapEvent(XEvent* event, XWindow xid) const {
CHECK(event);
XUnmapEvent* unmap_event = &(event->xunmap);
memset(unmap_event, 0, sizeof(*unmap_event));
unmap_event->type = UnmapNotify;
unmap_event->window = xid;
}
void MockXConnection::GetEventInternal(XEvent* event, bool remove_from_queue) {
CHECK(event);
CHECK(!queued_events_.empty())
<< "GetEventInternal() called while no events are queued in "
<< "single-threaded testing code -- we would block forever";
*event = queued_events_.front();
if (remove_from_queue)
queued_events_.pop();
if (connection_pipe_has_data_) {
unsigned char data = 0;
PCHECK(HANDLE_EINTR(read(connection_pipe_fds_[0], &data, 1)) == 1);
connection_pipe_has_data_ = false;
}
}
} // namespace window_manager