blob: f41a787aed85646d4df13a56ad76299c0f426654 [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/window_manager.h"
#include <cstring>
#include <ctime>
#include <list>
#include <queue>
extern "C" {
#include <X11/cursorfont.h>
#include <X11/Xatom.h>
#include <X11/XF86keysym.h>
#include <X11/Xcursor/Xcursor.h>
}
#include <gflags/gflags.h>
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/logging.h"
#include "base/string_util.h"
#include "cros/chromeos_wm_ipc_enums.h"
#include "metrics/metrics_library.h"
#include "window_manager/callback.h"
#include "window_manager/chrome_watchdog.h"
#include "window_manager/dbus_interface.h"
#include "window_manager/event_consumer.h"
#include "window_manager/event_loop.h"
#include "window_manager/focus_manager.h"
#include "window_manager/geometry.h"
#include "window_manager/image_container.h"
#include "window_manager/image_enums.h"
#include "window_manager/key_bindings.h"
#include "window_manager/layout/layout_manager.h"
#include "window_manager/login/login_controller.h"
#include "window_manager/modality_handler.h"
#include "window_manager/panels/panel_manager.h"
#include "window_manager/profiler.h"
#include "window_manager/screen_locker_handler.h"
#include "window_manager/stacking_manager.h"
#include "window_manager/util.h"
#include "window_manager/window.h"
#include "window_manager/x11/x_connection.h"
DEFINE_string(xterm_command, "xterm", "Command to launch a terminal");
DEFINE_string(background_color, "#000", "Background color");
DEFINE_string(configure_monitor_command,
"/usr/bin/monitor_reconfigure",
"Command to configure an external monitor");
DEFINE_string(screenshot_binary,
"/usr/bin/screenshot",
"Path to the screenshot binary");
DEFINE_string(logged_in_screenshot_output_dir,
".", "Output directory for screenshots when logged in");
DEFINE_string(logged_out_screenshot_output_dir,
".", "Output directory for screenshots when not logged in");
DEFINE_string(logged_in_log_dir,
".", "Directory to write logs to when logged in");
DEFINE_string(logged_out_log_dir,
".", "Directory to write logs to when not logged in");
DEFINE_string(unaccelerated_graphics_image,
"../assets/images/unaccelerated_graphics.png",
"Image to display when using unaccelerated rendering");
DEFINE_bool(unredirect_fullscreen_window,
false,
"Enable/disable compositing optimization that automatically turns"
"off compositing if a topmost fullscreen window is present");
DEFINE_bool(report_metrics, false, "Report user action metrics via Chrome");
using base::hash_map;
using base::TimeDelta;
using base::TimeTicks;
using chromeos::WmIpcMessageType;
using std::list;
using std::make_pair;
using std::map;
using std::set;
using std::string;
using std::tr1::shared_ptr;
using std::vector;
using window_manager::util::FindWithDefault;
using window_manager::util::GetTimeAsString;
using window_manager::util::GetCurrentTimeSec;
using window_manager::util::GetMonotonicTime;
using window_manager::util::SetUpLogSymlink;
using window_manager::util::XidStr;
namespace window_manager {
// How many pixels should |unaccelerated_graphics_actor_| be offset from
// the upper-left corner of the screen?
static const int kUnacceleratedGraphicsActorOffsetPixels = 5;
// How long should we wait before hiding |unaccelerated_graphics_actor_|?
static const int kUnacceleratedGraphicsActorHideTimeoutMs = 15000;
// How quickly should we fade out |unaccelerated_graphics_actor_| when
// hiding it?
static const int kUnacceleratedGraphicsActorHideAnimMs = 500;
// How frequently should we send _NET_WM_PING messages to Chrome, and how
// long should we wait for a response to each before killing the process?
static const int kPingChromeFrequencyMs = 5000;
static const int kPingChromeTimeoutMs = 4000;
COMPILE_ASSERT(kPingChromeFrequencyMs > kPingChromeTimeoutMs,
ping_timeout_is_greater_than_ping_frequency);
// Names of key binding actions that we register.
static const char* kLaunchTerminalAction = "launch-terminal";
#ifndef NDEBUG
static const char* kToggleClientWindowDebuggingAction =
"toggle-client-window-debugging";
static const char* kToggleProfilerAction = "toggle-profiler";
#endif
static const char* kConfigureMonitorAction = "configure-monitor";
static const char* kTakeRootScreenshotAction = "take-root-screenshot";
static const char* kTakeWindowScreenshotAction = "take-window-screenshot";
const int WindowManager::kVideoTimePropertyUpdateSec = 5;
// Invoke |function_call| for each event consumer in |consumers| (a set).
#define FOR_EACH_EVENT_CONSUMER(consumers, function_call) \
do { \
for (set<EventConsumer*>::iterator it = \
consumers.begin(); it != consumers.end(); ++it) { \
(*it)->function_call; \
} \
} while (0)
// Look up the event consumers that have registered interest in |key| in
// |consumer_map| (one of the WindowManager::*_event_consumers_ member
// variables), and invoke |function_call| (e.g.
// "HandleWindowPropertyChange(e.window, e.atom)") on each. Helper macro
// used by WindowManager's event-handling methods.
#define FOR_EACH_INTERESTED_EVENT_CONSUMER(consumer_map, key, function_call) \
do { \
typeof(consumer_map.begin()) it = consumer_map.find(key); \
if (it != consumer_map.end()) { \
for (set<EventConsumer*>::iterator ec_it = \
it->second.begin(); ec_it != it->second.end(); ++ec_it) { \
(*ec_it)->function_call; \
} \
} \
} while (0)
// Used by helper functions to generate |case| statements.
#define CASE_RETURN_LABEL(label) \
case label: return #label
#undef DEBUG_EVENTS // Turn this on if you want to debug events.
#ifdef DEBUG_EVENTS
static const char* XEventTypeToName(int type) {
switch (type) {
CASE_RETURN_LABEL(ButtonPress);
CASE_RETURN_LABEL(ButtonRelease);
CASE_RETURN_LABEL(CirculateNotify);
CASE_RETURN_LABEL(CirculateRequest);
CASE_RETURN_LABEL(ClientMessage);
CASE_RETURN_LABEL(ColormapNotify);
CASE_RETURN_LABEL(ConfigureNotify);
CASE_RETURN_LABEL(ConfigureRequest);
CASE_RETURN_LABEL(CreateNotify);
CASE_RETURN_LABEL(DestroyNotify);
CASE_RETURN_LABEL(EnterNotify);
CASE_RETURN_LABEL(Expose);
CASE_RETURN_LABEL(FocusIn);
CASE_RETURN_LABEL(FocusOut);
CASE_RETURN_LABEL(GraphicsExpose);
CASE_RETURN_LABEL(GravityNotify);
CASE_RETURN_LABEL(KeymapNotify);
CASE_RETURN_LABEL(KeyPress);
CASE_RETURN_LABEL(KeyRelease);
CASE_RETURN_LABEL(LeaveNotify);
CASE_RETURN_LABEL(MapNotify);
CASE_RETURN_LABEL(MappingNotify);
CASE_RETURN_LABEL(MapRequest);
CASE_RETURN_LABEL(MotionNotify);
CASE_RETURN_LABEL(NoExpose);
CASE_RETURN_LABEL(PropertyNotify);
CASE_RETURN_LABEL(ReparentNotify);
CASE_RETURN_LABEL(ResizeRequest);
CASE_RETURN_LABEL(SelectionClear);
CASE_RETURN_LABEL(SelectionNotify);
CASE_RETURN_LABEL(SelectionRequest);
CASE_RETURN_LABEL(UnmapNotify);
CASE_RETURN_LABEL(VisibilityNotify);
default: return "Unknown";
}
}
#endif
WindowManager::WindowManager(EventLoop* event_loop,
XConnection* xconn,
Compositor* compositor,
DBusInterface* dbus)
: event_loop_(event_loop),
xconn_(xconn),
compositor_(compositor),
dbus_(dbus),
root_(0),
width_(0),
height_(0),
wm_xid_(0),
stage_(NULL),
stage_xid_(0),
overlay_xid_(0),
startup_pixmap_(0),
mapped_xids_(new Stacker<XWindow>),
stacked_xids_(new Stacker<XWindow>),
active_window_xid_(0),
query_keyboard_state_timeout_id_(-1),
unredirected_fullscreen_xid_(0),
wm_ipc_version_(1),
logged_in_(false),
initialize_logging_(false),
hide_unaccelerated_graphics_actor_timeout_id_(-1),
chrome_watchdog_timeout_id_(-1) {
CHECK(event_loop_);
CHECK(xconn_);
CHECK(compositor_);
}
WindowManager::~WindowManager() {
if (startup_pixmap_)
xconn_->FreePixmap(startup_pixmap_);
if (query_keyboard_state_timeout_id_ >= 0)
event_loop_->RemoveTimeout(query_keyboard_state_timeout_id_);
if (chrome_watchdog_timeout_id_ >= 0)
event_loop_->RemoveTimeout(chrome_watchdog_timeout_id_);
if (hide_unaccelerated_graphics_actor_timeout_id_ >= 0)
event_loop_->RemoveTimeout(hide_unaccelerated_graphics_actor_timeout_id_);
if (panel_manager_.get())
panel_manager_->UnregisterAreaChangeListener(this);
if (compositor_)
compositor_->UnregisterCompositionChangeListener(this);
}
void WindowManager::HandlePanelManagerAreaChange() {
SetEwmhWorkareaProperty();
}
void WindowManager::HandleTopFullscreenActorChange(
const Compositor::TexturePixmapActor* top_fullscreen_actor) {
if (!FLAGS_unredirect_fullscreen_window)
return;
const bool was_compositing = unredirected_fullscreen_xid_ == 0;
bool should_composite = true;
XWindow window_to_unredirect = 0;
if (top_fullscreen_actor) {
Window* win = GetWindowOwningActor(*top_fullscreen_actor);
if (win != NULL &&
win->client_x() == win->composited_x() &&
win->client_y() == win->composited_y() &&
win->composited_scale_x() == 1.0 &&
win->composited_scale_y() == 1.0) {
window_to_unredirect = win->xid();
should_composite = false;
}
}
if (unredirected_fullscreen_xid_) {
if (unredirected_fullscreen_xid_ == window_to_unredirect) {
window_to_unredirect = 0;
} else {
Window* win = GetWindow(unredirected_fullscreen_xid_);
if (win) {
// Grab the server here to avoid a race condition between Chrome and
// window manager that result in window been reset while Chrome is
// writing into it.
scoped_ptr<XConnection::ScopedServerGrab> grab(
xconn_->CreateScopedServerGrab());
xconn_->RedirectWindowForCompositing(unredirected_fullscreen_xid_);
win->HandleRedirect();
} else {
DLOG(WARNING) << "The previously unredirected window with XID="
<< unredirected_fullscreen_xid_ << " no longer exists";
}
unredirected_fullscreen_xid_ = 0;
// Force the frame to draw when changing from one fullscreen actor to
// another fullscreen actor in case X does not redraw the entire
// screen and we get a partially updated frame.
should_composite = true;
}
}
if (window_to_unredirect) {
unredirected_fullscreen_xid_ = window_to_unredirect;
event_loop_->PostTask(
NewPermanentCallback(this, &WindowManager::DisableCompositing));
// Don't update should_draw_frame here because we want to draw the current
// frame before we disable compositing. The flag is updated in the
// DisableCompositing callback, which does the actual disabling.
}
if (!was_compositing && should_composite) {
xconn_->SetWindowBoundingRegionToRect(overlay_xid_,
Rect(0, 0, width_, height_));
DLOG(INFO) << "Turned compositing on";
compositor_->set_should_draw_frame(true);
}
}
bool WindowManager::Init() {
CHECK(!root_) << "Init() may only be called once";
root_ = xconn_->GetRootWindow();
xconn_->SelectRandREventsOnWindow(root_);
xconn_->SelectInputOnWindow(root_, StructureNotifyMask, true);
XConnection::WindowGeometry root_geometry;
CHECK(xconn_->GetWindowGeometry(root_, &root_geometry));
width_ = root_geometry.bounds.width;
height_ = root_geometry.bounds.height;
root_depth_ = root_geometry.depth;
if (FLAGS_unredirect_fullscreen_window) {
// Disable automatic background painting for the root window. It
// should never be visible, and the automatic updates cause flickering
// when enabling and disabling compositing.
xconn_->SetWindowBackgroundPixmap(root_, None);
}
// Create the atom cache first; RegisterExistence() needs it.
atom_cache_.reset(new AtomCache(xconn_));
CHECK(RegisterExistence());
SetEwmhGeneralProperties();
SetEwmhSizeProperties();
// First make sure that we'll get notified if the login state changes
// and then query its current value.
CHECK(xconn_->SelectInputOnWindow(root_, PropertyChangeMask, true));
int logged_in_value = 0;
xconn_->GetIntProperty(
root_, GetXAtom(ATOM_CHROME_LOGGED_IN), &logged_in_value);
logged_in_ = logged_in_value;
stage_ = compositor_->GetDefaultStage();
stage_xid_ = stage_->GetStageXWindow();
stage_->SetName("stage");
stage_->SetSize(width_, height_);
stage_->SetStageColor(Compositor::Color(FLAGS_background_color));
stage_->Show();
wm_ipc_.reset(new WmIpc(xconn_, atom_cache_.get()));
stacking_manager_.reset(
new StackingManager(xconn_, compositor_, atom_cache_.get()));
focus_manager_.reset(new FocusManager(this));
if (FLAGS_report_metrics) {
metrics_library_.reset(new MetricsLibrary);
metrics_library_->Init();
}
if (!logged_in_)
CreateStartupBackground();
// Draw the scene first to make sure that it's ready.
compositor_->Draw();
CHECK(xconn_->RedirectSubwindowsForCompositing(root_));
// Create the compositing overlay, put the stage's window inside of it,
// and make events fall through both to the client windows underneath.
overlay_xid_ = xconn_->GetCompositingOverlayWindow(root_);
CHECK(overlay_xid_);
LOG(INFO) << "Reparenting stage window " << XidStr(stage_xid_)
<< " into Xcomposite overlay window " << XidStr(overlay_xid_);
CHECK(xconn_->ReparentWindow(stage_xid_, overlay_xid_, Point()));
CHECK(xconn_->RemoveInputRegionFromWindow(overlay_xid_));
CHECK(xconn_->RemoveInputRegionFromWindow(stage_xid_));
if (!compositor_->TexturePixmapActorUsesFastPath() &&
!FLAGS_unaccelerated_graphics_image.empty()) {
unaccelerated_graphics_actor_.reset(
compositor_->CreateImageFromFile(FLAGS_unaccelerated_graphics_image));
unaccelerated_graphics_actor_->Move(
kUnacceleratedGraphicsActorOffsetPixels,
kUnacceleratedGraphicsActorOffsetPixels,
0);
unaccelerated_graphics_actor_->SetOpacity(1.f, 0);
stage_->AddActor(unaccelerated_graphics_actor_.get());
stacking_manager_->StackActorAtTopOfLayer(
unaccelerated_graphics_actor_.get(), StackingManager::LAYER_DEBUGGING);
unaccelerated_graphics_actor_->Show();
hide_unaccelerated_graphics_actor_timeout_id_ =
event_loop_->AddTimeout(
NewPermanentCallback(
this, &WindowManager::HideUnacceleratedGraphicsActor),
kUnacceleratedGraphicsActorHideTimeoutMs, 0);
}
compositor_->RegisterCompositionChangeListener(this);
key_bindings_.reset(new KeyBindings(xconn()));
key_bindings_actions_.reset(
new KeyBindingsActionRegistrar(key_bindings_.get()));
logged_in_key_bindings_group_.reset(
new KeyBindingsGroup(key_bindings_.get()));
if (!logged_in_)
logged_in_key_bindings_group_->Disable();
RegisterKeyBindings();
SetLoggedInState(logged_in_, true); // initial=true
screen_locker_handler_.reset(new ScreenLockerHandler(this));
event_consumers_.insert(screen_locker_handler_.get());
modality_handler_.reset(new ModalityHandler(this));
event_consumers_.insert(modality_handler_.get());
chrome_watchdog_.reset(new ChromeWatchdog(this));
event_consumers_.insert(chrome_watchdog_.get());
chrome_watchdog_timeout_id_ =
event_loop_->AddTimeout(
NewPermanentCallback(this, &WindowManager::PingChrome),
kPingChromeFrequencyMs, kPingChromeFrequencyMs);
// Select window management events before we look up existing windows --
// we want to make sure that we eventually hear about any resizes that we
// did while setting up existing windows so that the Window class will
// know that it needs to fetch new redirected pixmaps for them.
scoped_ptr<XConnection::ScopedServerGrab> grab(
xconn_->CreateScopedServerGrab());
CHECK(xconn_->SelectInputOnWindow(
root_,
SubstructureRedirectMask | SubstructureNotifyMask,
true)); // preserve the existing event mask
ManageExistingWindows();
grab.reset();
return true;
}
void WindowManager::SetLoggedInState(bool logged_in, bool initial) {
if (!initial && logged_in_ && !logged_in) {
LOG(WARNING) << "Ignoring request to transition from logged-in to "
<< "not-logged-in state";
return;
}
DLOG(INFO) << "User " << (logged_in ? "is" : "isn't") << " logged in";
if (!initial && logged_in == logged_in_)
return;
logged_in_ = logged_in;
if (initialize_logging_) {
const string& log_dir = logged_in ?
FLAGS_logged_in_log_dir :
FLAGS_logged_out_log_dir;
const string log_basename = StringPrintf(
"%s.%s", WindowManager::GetWmName(),
GetTimeAsString(GetCurrentTimeSec()).c_str());
if (!file_util::CreateDirectory(FilePath(log_dir))) {
LOG(ERROR) << "Unable to create logging directory " << log_dir;
} else {
SetUpLogSymlink(StringPrintf("%s/%s.LATEST",
log_dir.c_str(),
WindowManager::GetWmName()),
log_basename);
}
const string log_path = log_dir + "/" + log_basename;
LOG(INFO) << "Switching to log " << log_path;
logging::InitLogging(log_path.c_str(),
logging::LOG_ONLY_TO_FILE,
logging::DONT_LOCK_LOG_FILE,
logging::APPEND_TO_OLD_LOG_FILE);
}
if (logged_in_key_bindings_group_.get()) {
if (logged_in_)
logged_in_key_bindings_group_->Enable();
else
logged_in_key_bindings_group_->Disable();
}
if (logged_in_) {
DCHECK(!panel_manager_.get());
panel_manager_.reset(new PanelManager(this));
event_consumers_.insert(panel_manager_.get());
panel_manager_->RegisterAreaChangeListener(this);
HandlePanelManagerAreaChange();
DCHECK(!layout_manager_.get());
layout_manager_.reset(new LayoutManager(this, panel_manager_.get()));
event_consumers_.insert(layout_manager_.get());
// We've probably already dropped the background containing the initial
// contents of the root window in response to the login background
// window being shown, but it doesn't hurt to be sure here.
DropStartupBackground();
} else {
DCHECK(!login_controller_.get());
login_controller_.reset(new LoginController(this));
event_consumers_.insert(login_controller_.get());
}
FOR_EACH_EVENT_CONSUMER(event_consumers_, HandleLoggedInStateChange());
}
void WindowManager::ProcessPendingEvents() {
while (xconn_->IsEventPending()) {
XEvent event;
xconn_->GetNextEvent(&event);
HandleEvent(&event);
}
}
void WindowManager::HandleEvent(XEvent* event) {
DCHECK(root_) << "Init() must be called before events can be handled";
#ifdef DEBUG_EVENTS
if (event->type == xconn_->damage_event_base() + XDamageNotify) {
DLOG(INFO) << "Got DAMAGE" << " event (" << event->type << ")";
} else {
DLOG(INFO) << "Got " << XEventTypeToName(event->type)
<< " event (" << event->type << ") in window manager.";
}
#endif
static int damage_notify = xconn_->damage_event_base() + XDamageNotify;
static int shape_notify = xconn_->shape_event_base() + ShapeNotify;
static int randr_notify = xconn_->randr_event_base() + RRScreenChangeNotify;
static int sync_alarm_notify = xconn_->sync_event_base() + XSyncAlarmNotify;
switch (event->type) {
case ButtonPress:
HandleButtonPress(event->xbutton); break;
case ButtonRelease:
HandleButtonRelease(event->xbutton); break;
case ClientMessage:
HandleClientMessage(event->xclient); break;
case ConfigureNotify:
HandleConfigureNotify(event->xconfigure); break;
case ConfigureRequest:
HandleConfigureRequest(event->xconfigurerequest); break;
case CreateNotify:
HandleCreateNotify(event->xcreatewindow); break;
case DestroyNotify:
HandleDestroyNotify(event->xdestroywindow); break;
case EnterNotify:
HandleEnterNotify(event->xcrossing); break;
case KeyPress:
HandleKeyPress(event->xkey); break;
case KeyRelease:
HandleKeyRelease(event->xkey); break;
case LeaveNotify:
HandleLeaveNotify(event->xcrossing); break;
case MapNotify:
HandleMapNotify(event->xmap); break;
case MapRequest:
HandleMapRequest(event->xmaprequest); break;
case MappingNotify:
HandleMappingNotify(event->xmapping); break;
case MotionNotify:
HandleMotionNotify(event->xmotion); break;
case PropertyNotify:
HandlePropertyNotify(event->xproperty); break;
case ReparentNotify:
HandleReparentNotify(event->xreparent); break;
case UnmapNotify:
HandleUnmapNotify(event->xunmap); break;
default:
if (event->type == damage_notify) {
HandleDamageNotify(*(reinterpret_cast<XDamageNotifyEvent*>(event)));
} else if (event->type == sync_alarm_notify) {
HandleSyncAlarmNotify(
*(reinterpret_cast<XSyncAlarmNotifyEvent*>(event)));
} else if (event->type == shape_notify) {
HandleShapeNotify(*(reinterpret_cast<XShapeEvent*>(event)));
} else if (event->type == randr_notify) {
HandleRRScreenChangeNotify(
*(reinterpret_cast<XRRScreenChangeNotifyEvent*>(event)));
}
}
}
XWindow WindowManager::CreateInputWindow(
int x, int y, int width, int height, int event_mask) {
XWindow xid = xconn_->CreateWindow(
root_, // parent
Rect(x, y, width, height),
true, // override redirect
true, // input only
event_mask,
0); // visual
CHECK(xid);
// Since the stage has been reparented into the overlay window, we need
// to stack the input window under the overlay instead of under the stage
// (because the stage isn't a sibling of the input window).
CHECK(xconn_->StackWindow(xid, overlay_xid_, false));
CHECK(xconn_->MapWindow(xid));
return xid;
}
bool WindowManager::ConfigureInputWindow(
XWindow xid, int x, int y, int width, int height) {
Rect rect(x, y, width, height);
DLOG(INFO) << "Configuring input window " << XidStr(xid) << " to " << rect;
return xconn_->ConfigureWindow(xid, rect);
}
XAtom WindowManager::GetXAtom(Atom atom) {
return atom_cache_->GetXAtom(atom);
}
const string& WindowManager::GetXAtomName(XAtom xatom) {
return atom_cache_->GetName(xatom);
}
XTime WindowManager::GetCurrentTimeFromServer() {
// Just set a bogus property on our window and wait for the
// PropertyNotify event so we can get its timestamp.
CHECK(xconn_->SetIntProperty(
wm_xid_,
GetXAtom(ATOM_CHROME_GET_SERVER_TIME), // atom
XA_ATOM, // type
GetXAtom(ATOM_CHROME_GET_SERVER_TIME))); // value
XTime timestamp = 0;
xconn_->WaitForPropertyChange(wm_xid_, &timestamp);
return timestamp;
}
Window* WindowManager::GetWindow(XWindow xid) {
return FindWithDefault(client_windows_, xid, shared_ptr<Window>()).get();
}
Window* WindowManager::GetWindowOrDie(XWindow xid) {
Window* win = GetWindow(xid);
CHECK(win) << "Unable to find window " << XidStr(xid);
return win;
}
Window* WindowManager::GetWindowOwningActor(
const Compositor::TexturePixmapActor& actor) {
for (WindowMap::const_iterator it = client_windows_.begin();
it != client_windows_.end(); it++) {
if (it->second->actor() == &actor)
return it->second.get();
}
return NULL;
}
void WindowManager::FocusWindow(Window* win, XTime timestamp) {
focus_manager_->FocusWindow(win, timestamp);
}
bool WindowManager::IsModalWindowFocused() const {
return modality_handler_->modal_window_is_focused();
}
void WindowManager::TakeFocus(XTime timestamp) {
if (layout_manager_.get() && layout_manager_->TakeFocus(timestamp))
return;
if (panel_manager_.get() && panel_manager_->TakeFocus(timestamp))
return;
focus_manager_->FocusWindow(NULL, timestamp);
}
bool WindowManager::SetActiveWindowProperty(XWindow xid) {
if (active_window_xid_ == xid)
return true;
DLOG(INFO) << "Setting active window to " << XidStr(xid);
if (!xconn_->SetIntProperty(
root_, GetXAtom(ATOM_NET_ACTIVE_WINDOW), XA_WINDOW, xid)) {
return false;
}
active_window_xid_ = xid;
return true;
}
bool WindowManager::SetNamePropertiesForXid(XWindow xid, const string& name) {
bool success = xconn_->SetStringProperty(
xid, atom_cache_->GetXAtom(ATOM_WM_NAME), name);
success &= xconn_->SetStringProperty(
xid, atom_cache_->GetXAtom(ATOM_NET_WM_NAME), name);
return success;
}
bool WindowManager::SetVideoTimeProperty(time_t video_time) {
// Rate-limit how often we'll update the property.
TimeTicks now = GetMonotonicTime();
if (video_property_update_time_.is_null() ||
(now - video_property_update_time_) >
TimeDelta::FromSeconds(kVideoTimePropertyUpdateSec)) {
video_property_update_time_ = now;
XAtom atom = atom_cache_->GetXAtom(ATOM_CHROME_VIDEO_TIME);
return xconn_->SetIntProperty(
root_, atom, atom, static_cast<int>(video_time));
}
return true;
}
void WindowManager::HandleWindowInitialPixmap(Window* win) {
DCHECK(win);
FOR_EACH_INTERESTED_EVENT_CONSUMER(
window_event_consumers_, win->xid(), HandleWindowInitialPixmap(win));
}
void WindowManager::RegisterEventConsumerForWindowEvents(
XWindow xid, EventConsumer* event_consumer) {
DCHECK(event_consumer);
if (!window_event_consumers_[xid].insert(event_consumer).second) {
LOG(WARNING) << "Got request to register already-present window event "
<< "consumer " << event_consumer << " for window "
<< XidStr(xid);
}
}
void WindowManager::UnregisterEventConsumerForWindowEvents(
XWindow xid, EventConsumer* event_consumer) {
DCHECK(event_consumer);
WindowEventConsumerMap::iterator it = window_event_consumers_.find(xid);
if (it == window_event_consumers_.end() ||
it->second.erase(event_consumer) != 1) {
LOG(WARNING) << "Got request to unregister not-registered window event "
<< "consumer " << event_consumer << " for window "
<< XidStr(xid);
} else {
if (it->second.empty())
window_event_consumers_.erase(it);
}
}
void WindowManager::RegisterEventConsumerForPropertyChanges(
XWindow xid, XAtom xatom, EventConsumer* event_consumer) {
DCHECK(event_consumer);
if (!property_change_event_consumers_[make_pair(xid, xatom)].insert(
event_consumer).second) {
LOG(WARNING) << "Got request to register already-present window property "
<< "listener " << event_consumer << " for window "
<< XidStr(xid) << " and atom " << XidStr(xatom) << " ("
<< GetXAtomName(xatom) << ")";
}
}
void WindowManager::UnregisterEventConsumerForPropertyChanges(
XWindow xid, XAtom xatom, EventConsumer* event_consumer) {
DCHECK(event_consumer);
PropertyChangeEventConsumerMap::iterator it =
property_change_event_consumers_.find(make_pair(xid, xatom));
if (it == property_change_event_consumers_.end() ||
it->second.erase(event_consumer) != 1) {
LOG(WARNING) << "Got request to unregister not-registered window property "
<< "listener " << event_consumer << " for window "
<< XidStr(xid) << " and atom " << XidStr(xatom) << " ("
<< GetXAtomName(xatom) << ")";
} else {
if (it->second.empty())
property_change_event_consumers_.erase(it);
}
}
void WindowManager::RegisterEventConsumerForChromeMessages(
WmIpcMessageType message_type, EventConsumer* event_consumer) {
DCHECK(event_consumer);
if (!chrome_message_event_consumers_[message_type].insert(
event_consumer).second) {
LOG(WARNING) << "Got request to register already-present Chrome message "
<< "event consumer " << event_consumer << " for message type "
<< message_type;
}
}
void WindowManager::UnregisterEventConsumerForChromeMessages(
WmIpcMessageType message_type, EventConsumer* event_consumer) {
DCHECK(event_consumer);
ChromeMessageEventConsumerMap::iterator it =
chrome_message_event_consumers_.find(message_type);
if (it == chrome_message_event_consumers_.end() ||
it->second.erase(event_consumer) != 1) {
LOG(WARNING) << "Got request to unregister not-registered Chrome message "
<< "event consumer " << event_consumer << " for message type "
<< message_type;
} else {
if (it->second.empty())
chrome_message_event_consumers_.erase(it);
}
}
void WindowManager::RegisterEventConsumerForDestroyedWindow(
XWindow xid, EventConsumer* event_consumer) {
DCHECK(xid);
DCHECK(event_consumer);
CHECK(destroyed_window_event_consumers_.insert(
make_pair(xid, event_consumer)).second)
<< "Another EventConsumer already requested ownership of window "
<< XidStr(xid) << " after it gets destroyed";
}
void WindowManager::UnregisterEventConsumerForDestroyedWindow(
XWindow xid, EventConsumer* event_consumer) {
hash_map<XWindow, EventConsumer*>::iterator it =
destroyed_window_event_consumers_.find(xid);
CHECK(it != destroyed_window_event_consumers_.end())
<< "No event consumer requested ownership of window " << XidStr(xid)
<< " after it gets destroyed, but got a request to unregister "
<< event_consumer;
CHECK(it->second == event_consumer)
<< "Event consumer " << it->second << " requested ownership of window "
<< XidStr(xid) << " after it gets destroyed, but got a request to "
<< "unregister " << event_consumer;
destroyed_window_event_consumers_.erase(it);
}
void WindowManager::RegisterSyncAlarm(XID alarm_id, Window* win) {
base::hash_map<XID, Window*>::const_iterator it =
sync_alarms_to_windows_.find(alarm_id);
DCHECK(it == sync_alarms_to_windows_.end())
<< "Registering sync alarm " << XidStr(alarm_id) << " for window "
<< win->xid_str() << " while it's already being used by "
<< it->second->xid_str();
sync_alarms_to_windows_[alarm_id] = win;
}
void WindowManager::UnregisterSyncAlarm(XID alarm_id) {
size_t num_erased = sync_alarms_to_windows_.erase(alarm_id);
DCHECK_EQ(num_erased, static_cast<size_t>(1))
<< "Tried to unregister unknown sync alarm " << XidStr(alarm_id);
}
void WindowManager::ToggleClientWindowDebugging() {
if (client_window_debugging_enabled()) {
client_window_debugging_actors_.clear();
return;
}
UpdateClientWindowDebugging();
}
void WindowManager::ToggleProfiler() {
#if defined(PROFILE_BUILD)
Profiler* profiler = Singleton<Profiler>::get();
if (profiler->status() == Profiler::STATUS_RUN) {
profiler->Pause();
} else if (profiler->status() == Profiler::STATUS_SUSPEND) {
profiler->Resume();
}
#endif
}
void WindowManager::UpdateClientWindowDebugging() {
DLOG(INFO) << "Compositing actors:\n" << stage_->GetDebugString(0);
ActorVector new_actors;
vector<XWindow> xids;
if (!xconn_->GetChildWindows(root_, &xids))
return;
static const int kDebugFadeMs = 100;
int cnt = 0;
float step = 6.f / xids.size();
for (vector<XWindow>::iterator it = xids.begin(); it != xids.end(); ++it) {
Compositor::Color bg_color;
bg_color.SetHsv(cnt++ * step, 1.f, 1.f);
XConnection::WindowGeometry geometry;
if (!xconn_->GetWindowGeometry(*it, &geometry))
continue;
Compositor::ColoredBoxActor* rect =
compositor_->CreateColoredBox(
geometry.bounds.width, geometry.bounds.height, bg_color);
stage_->AddActor(rect);
stacking_manager_->StackActorAtTopOfLayer(
rect, StackingManager::LAYER_DEBUGGING);
rect->SetName("debug box");
rect->Move(geometry.bounds.x, geometry.bounds.y, 0);
rect->SetOpacity(0, 0);
rect->SetOpacity(0.3, kDebugFadeMs);
rect->Show();
new_actors.push_back(shared_ptr<Compositor::Actor>(rect));
}
client_window_debugging_actors_.swap(new_actors);
}
void WindowManager::DropStartupBackground() {
if (startup_background_.get()) {
startup_background_.reset();
xconn_->FreePixmap(startup_pixmap_);
startup_pixmap_ = 0;
}
}
int WindowManager::GetNumWindows() const {
int num_windows = 0;
if (panel_manager_.get())
num_windows += panel_manager_->num_panels();
if (layout_manager_.get())
num_windows += layout_manager_->num_toplevels();
return num_windows;
}
void WindowManager::DestroyLoginController() {
event_loop_->PostTask(
NewPermanentCallback(
this, &WindowManager::DestroyLoginControllerInternal));
}
void WindowManager::ReportUserAction(const string& action) {
if (metrics_library_.get())
metrics_library_->SendUserActionToUMA(action);
}
bool WindowManager::IsSessionEnding() const {
if (!screen_locker_handler_.get())
return false;
return screen_locker_handler_->session_ending();
}
bool WindowManager::GetManagerSelection(
XAtom atom, XWindow manager_win, XTime timestamp) {
// Find the current owner of the selection and select events on it so
// we'll know when it's gone away.
XWindow current_manager = xconn_->GetSelectionOwner(atom);
if (current_manager)
xconn_->SelectInputOnWindow(current_manager, StructureNotifyMask, false);
// Take ownership of the selection.
CHECK(xconn_->SetSelectionOwner(atom, manager_win, timestamp));
if (xconn_->GetSelectionOwner(atom) != manager_win) {
LOG(WARNING) << "Couldn't take ownership of "
<< GetXAtomName(atom) << " selection";
return false;
}
// Announce that we're here.
long data[5];
memset(data, 0, sizeof(data));
data[0] = timestamp;
data[1] = atom;
data[2] = manager_win;
CHECK(xconn_->SendClientMessageEvent(
root_, root_, GetXAtom(ATOM_MANAGER), data, StructureNotifyMask));
// If there was an old manager running, wait for its window to go away.
if (current_manager)
CHECK(xconn_->WaitForWindowToBeDestroyed(current_manager));
return true;
}
bool WindowManager::RegisterExistence() {
// Create an offscreen window to take ownership of the selection and
// receive properties.
wm_xid_ = xconn_->CreateWindow(root_, // parent
Rect(-1, -1, 1, 1),
true, // override redirect
false, // input only
PropertyChangeMask, // event mask
0); // visual
CHECK(wm_xid_);
LOG(INFO) << "Created window " << XidStr(wm_xid_)
<< " for registering ourselves as the window manager";
wm_xid_destroyer_.reset(new XConnection::WindowDestroyer(xconn_, wm_xid_));
// Set the window's title and wait for the notify event so we can get a
// timestamp from the server.
CHECK(SetNamePropertiesForXid(wm_xid_, GetWmName()));
XTime timestamp = 0;
xconn_->WaitForPropertyChange(wm_xid_, &timestamp);
if (!GetManagerSelection(GetXAtom(ATOM_WM_S0), wm_xid_, timestamp) ||
!GetManagerSelection(GetXAtom(ATOM_NET_WM_CM_S0), wm_xid_, timestamp)) {
return false;
}
return true;
}
bool WindowManager::SetEwmhGeneralProperties() {
bool success = true;
success &= xconn_->SetIntProperty(
root_, GetXAtom(ATOM_NET_NUMBER_OF_DESKTOPS), XA_CARDINAL, 1);
success &= xconn_->SetIntProperty(
root_, GetXAtom(ATOM_NET_CURRENT_DESKTOP), XA_CARDINAL, 0);
// Let clients know that we're the current WM and that we at least
// partially conform to EWMH.
XAtom check_atom = GetXAtom(ATOM_NET_SUPPORTING_WM_CHECK);
success &= xconn_->SetIntProperty(root_, check_atom, XA_WINDOW, wm_xid_);
success &= xconn_->SetIntProperty(wm_xid_, check_atom, XA_WINDOW, wm_xid_);
// State which parts of EWMH we support.
vector<int> supported;
supported.push_back(GetXAtom(ATOM_NET_ACTIVE_WINDOW));
supported.push_back(GetXAtom(ATOM_NET_CLIENT_LIST));
supported.push_back(GetXAtom(ATOM_NET_CLIENT_LIST_STACKING));
supported.push_back(GetXAtom(ATOM_NET_CURRENT_DESKTOP));
supported.push_back(GetXAtom(ATOM_NET_DESKTOP_GEOMETRY));
supported.push_back(GetXAtom(ATOM_NET_DESKTOP_VIEWPORT));
supported.push_back(GetXAtom(ATOM_NET_NUMBER_OF_DESKTOPS));
supported.push_back(GetXAtom(ATOM_NET_WM_MOVERESIZE));
supported.push_back(GetXAtom(ATOM_NET_WM_NAME));
supported.push_back(GetXAtom(ATOM_NET_WM_STATE));
supported.push_back(GetXAtom(ATOM_NET_WM_STATE_FULLSCREEN));
supported.push_back(GetXAtom(ATOM_NET_WM_STATE_MODAL));
supported.push_back(GetXAtom(ATOM_NET_WM_SYNC_REQUEST));
supported.push_back(GetXAtom(ATOM_NET_WM_SYNC_REQUEST_COUNTER));
supported.push_back(GetXAtom(ATOM_NET_WM_WINDOW_OPACITY));
supported.push_back(GetXAtom(ATOM_NET_WORKAREA));
success &= xconn_->SetIntArrayProperty(
root_, GetXAtom(ATOM_NET_SUPPORTED), XA_ATOM, supported);
return success;
}
bool WindowManager::SetEwmhSizeProperties() {
bool success = true;
// We don't use pseudo-large desktops, so this is just the screen size.
vector<int> geometry;
geometry.push_back(width_);
geometry.push_back(height_);
success &= xconn_->SetIntArrayProperty(
root_, GetXAtom(ATOM_NET_DESKTOP_GEOMETRY), XA_CARDINAL, geometry);
// The viewport (top-left corner of the desktop) is just (0, 0) for us.
vector<int> viewport(2, 0);
success &= xconn_->SetIntArrayProperty(
root_, GetXAtom(ATOM_NET_DESKTOP_VIEWPORT), XA_CARDINAL, viewport);
success &= SetEwmhWorkareaProperty();
return success;
}
bool WindowManager::SetEwmhWorkareaProperty() {
// _NET_WORKAREA describes the region of the screen "minus space occupied
// by dock and panel windows", so we subtract out the space used by panel
// docks. :-P Chrome can use this to guess the initial size for new
// windows.
int panel_manager_left_width = 0, panel_manager_right_width = 0;
if (panel_manager_.get()) {
// We invoke this before the panel manager has been created to try to
// get the hints set as soon as possible.
panel_manager_->GetArea(&panel_manager_left_width,
&panel_manager_right_width);
}
vector<int> workarea;
workarea.push_back(panel_manager_left_width); // x
workarea.push_back(0); // y
workarea.push_back(
width_ - panel_manager_left_width - panel_manager_right_width);
workarea.push_back(height_);
return xconn_->SetIntArrayProperty(
root_, GetXAtom(ATOM_NET_WORKAREA), XA_CARDINAL, workarea);
}
void WindowManager::RegisterKeyBindings() {
key_bindings_actions_->AddAction(
kLaunchTerminalAction,
NewPermanentCallback(
this, &WindowManager::RunCommand, FLAGS_xterm_command),
NULL, NULL);
logged_in_key_bindings_group_->AddBinding(
KeyBindings::KeyCombo(
XK_t, KeyBindings::kControlMask | KeyBindings::kAltMask),
kLaunchTerminalAction);
#ifndef NDEBUG
key_bindings_actions_->AddAction(
kToggleClientWindowDebuggingAction,
NewPermanentCallback(this, &WindowManager::ToggleClientWindowDebugging),
NULL, NULL);
key_bindings_->AddBinding(
KeyBindings::KeyCombo(
XK_d, KeyBindings::kControlMask | KeyBindings::kAltMask),
kToggleClientWindowDebuggingAction);
key_bindings_actions_->AddAction(
kToggleProfilerAction,
NewPermanentCallback(this, &WindowManager::ToggleProfiler),
NULL, NULL);
key_bindings_->AddBinding(
KeyBindings::KeyCombo(
XK_p, KeyBindings::kControlMask | KeyBindings::kAltMask),
kToggleProfilerAction);
#endif
key_bindings_actions_->AddAction(
kConfigureMonitorAction,
NewPermanentCallback(this, &WindowManager::RunCommand,
FLAGS_configure_monitor_command),
NULL, NULL);
key_bindings_->AddBinding(
KeyBindings::KeyCombo(XK_F4, KeyBindings::kControlMask),
kConfigureMonitorAction);
key_bindings_actions_->AddAction(
kTakeRootScreenshotAction,
NewPermanentCallback(this, &WindowManager::TakeScreenshot, false),
NULL, NULL);
key_bindings_->AddBinding(
KeyBindings::KeyCombo(XK_F5, KeyBindings::kControlMask),
kTakeRootScreenshotAction);
key_bindings_->AddBinding(
KeyBindings::KeyCombo(XK_Print, 0), kTakeRootScreenshotAction);
key_bindings_actions_->AddAction(
kTakeWindowScreenshotAction,
NewPermanentCallback(this, &WindowManager::TakeScreenshot, true),
NULL, NULL);
key_bindings_->AddBinding(
KeyBindings::KeyCombo(XK_Print, KeyBindings::kShiftMask),
kTakeWindowScreenshotAction);
}
bool WindowManager::ManageExistingWindows() {
vector<XWindow> windows;
if (!xconn_->GetChildWindows(root_, &windows)) {
return false;
}
// Snapshot and panel content windows that are already mapped. We defer
// calling HandleMappedWindow() on these until we've handled all other
// windows to make sure that we handle the corresponding panel titlebar
// windows and Chrome toplevel windows first -- the panel code requires
// that the titlebars be mapped before the content windows, and the
// snapshot code requires that the toplevel windows are mapped before
// their snapshots.
vector<Window*> first_deferred_mapped_windows;
vector<Window*> second_deferred_mapped_windows;
LOG(INFO) << "Taking ownership of " << windows.size() << " window"
<< (windows.size() == 1 ? "" : "s");
for (size_t i = 0; i < windows.size(); ++i) {
XWindow xid = windows[i];
XConnection::WindowAttributes attr;
XConnection::WindowGeometry geometry;
if (!xconn_->GetWindowAttributes(xid, &attr) ||
!xconn_->GetWindowGeometry(xid, &geometry))
continue;
// XQueryTree() returns child windows in bottom-to-top stacking order.
stacked_xids_->AddOnTop(xid);
Window* win = TrackWindow(xid, attr.override_redirect, geometry);
if (win && win->FetchMapState()) {
if (win->type() == chromeos::WM_IPC_WINDOW_CHROME_PANEL_CONTENT ||
win->type() == chromeos::WM_IPC_WINDOW_CHROME_TAB_SNAPSHOT) {
first_deferred_mapped_windows.push_back(win);
} else if (win->type() == chromeos::WM_IPC_WINDOW_CHROME_TAB_TITLE ||
win->type() == chromeos::WM_IPC_WINDOW_CHROME_TAB_FAV_ICON) {
second_deferred_mapped_windows.push_back(win);
} else {
HandleMappedWindow(win);
}
}
}
for (vector<Window*>::iterator it = first_deferred_mapped_windows.begin();
it != first_deferred_mapped_windows.end(); ++it) {
HandleMappedWindow(*it);
}
for (vector<Window*>::iterator it = second_deferred_mapped_windows.begin();
it != second_deferred_mapped_windows.end(); ++it) {
HandleMappedWindow(*it);
}
UpdateClientListStackingProperty();
return true;
}
Window* WindowManager::TrackWindow(XWindow xid,
bool override_redirect,
XConnection::WindowGeometry& geometry) {
// Don't manage our internal windows.
if (IsInternalWindow(xid) || stacking_manager_->IsInternalWindow(xid))
return NULL;
for (set<EventConsumer*>::const_iterator it = event_consumers_.begin();
it != event_consumers_.end(); ++it) {
if ((*it)->IsInputWindow(xid))
return NULL;
}
// We don't care about InputOnly windows either.
// TODO: Don't call GetWindowAttributes() so many times; we call in it
// Window's c'tor as well.
XConnection::WindowAttributes attr;
if (xconn_->GetWindowAttributes(xid, &attr) &&
attr.window_class ==
XConnection::WindowAttributes::WINDOW_CLASS_INPUT_ONLY)
return NULL;
DLOG(INFO) << "Managing window " << XidStr(xid);
Window* win = GetWindow(xid);
if (win) {
LOG(WARNING) << "Window " << XidStr(xid) << " is already being managed";
} else {
shared_ptr<Window> win_ref(
new Window(this, xid, override_redirect, geometry));
client_windows_.insert(make_pair(xid, win_ref));
win = win_ref.get();
}
return win;
}
void WindowManager::HandleMappedWindow(Window* win) {
// We need to get a new pixmap for the window.
win->HandleMapNotify();
FOR_EACH_EVENT_CONSUMER(event_consumers_, HandleWindowMap(win));
if (win->override_redirect()) {
if (!win->is_rgba()) {
// Check if this window has a menu hint; if so, display a shadow under it.
const static XAtom combo_xatom = GetXAtom(ATOM_NET_WM_WINDOW_TYPE_COMBO);
const static XAtom dropdown_xatom =
GetXAtom(ATOM_NET_WM_WINDOW_TYPE_DROPDOWN_MENU);
const static XAtom menu_xatom = GetXAtom(ATOM_NET_WM_WINDOW_TYPE_MENU);
const static XAtom popup_xatom =
GetXAtom(ATOM_NET_WM_WINDOW_TYPE_POPUP_MENU);
for (vector<XAtom>::const_iterator it =
win->wm_window_type_xatoms().begin();
it != win->wm_window_type_xatoms().end(); ++it) {
if (*it == combo_xatom || *it == dropdown_xatom || *it == menu_xatom ||
*it == popup_xatom) {
win->SetShadowType(Shadow::TYPE_RECTANGULAR);
break;
}
}
}
win->ShowComposited();
return;
}
if (mapped_xids_->Contains(win->xid())) {
LOG(WARNING) << "Handling mapped window " << win->xid_str()
<< ", which is already listed in |mapped_xids_|";
} else {
mapped_xids_->AddOnTop(win->xid());
UpdateClientListProperty();
// This only includes mapped windows, so we need to update it now.
UpdateClientListStackingProperty();
}
SetWmStateProperty(win->xid(), 1); // NormalState
}
void WindowManager::HandleScreenResize(int new_width, int new_height) {
// We move windows offscreen to prevent them from receiving input.
// Check that the screen doesn't get so huge that they end up onscreen.
DCHECK_LE(new_width, Window::kOffscreenX);
DCHECK_LE(new_height, Window::kOffscreenY);
width_ = new_width;
height_ = new_height;
SetEwmhSizeProperties();
stage_->SetSize(width_, height_);
FOR_EACH_EVENT_CONSUMER(event_consumers_, HandleScreenResize());
}
bool WindowManager::SetWmStateProperty(XWindow xid, int state) {
vector<int> values;
values.push_back(state);
values.push_back(0); // we don't use icons
XAtom xatom = GetXAtom(ATOM_WM_STATE);
return xconn_->SetIntArrayProperty(xid, xatom, xatom, values);
}
bool WindowManager::UpdateClientListProperty() {
vector<int> values;
const list<XWindow>& xids = mapped_xids_->items();
// We store windows in most-to-least-recently-mapped order, but
// _NET_CLIENT_LIST is least-to-most-recently-mapped.
for (list<XWindow>::const_reverse_iterator it = xids.rbegin();
it != xids.rend(); ++it) {
const Window* win = GetWindow(*it);
if (!win || !win->mapped() || win->override_redirect()) {
LOG(WARNING) << "Skipping "
<< (!win ? "missing" :
(!win->mapped() ? "unmapped" : "override-redirect"))
<< " window " << XidStr(*it)
<< " when updating _NET_CLIENT_LIST";
} else {
values.push_back(*it);
}
}
if (!values.empty()) {
return xconn_->SetIntArrayProperty(
root_, GetXAtom(ATOM_NET_CLIENT_LIST), XA_WINDOW, values);
} else {
return xconn_->DeletePropertyIfExists(
root_, GetXAtom(ATOM_NET_CLIENT_LIST));
}
}
bool WindowManager::UpdateClientListStackingProperty() {
vector<int> values;
const list<XWindow>& xids = stacked_xids_->items();
// We store windows in top-to-bottom stacking order, but
// _NET_CLIENT_LIST_STACKING is bottom-to-top.
for (list<XWindow>::const_reverse_iterator it = xids.rbegin();
it != xids.rend(); ++it) {
const Window* win = GetWindow(*it);
if (win && win->mapped() && !win->override_redirect())
values.push_back(*it);
}
if (!values.empty()) {
return xconn_->SetIntArrayProperty(
root_, GetXAtom(ATOM_NET_CLIENT_LIST_STACKING), XA_WINDOW, values);
} else {
return xconn_->DeletePropertyIfExists(
root_, GetXAtom(ATOM_NET_CLIENT_LIST_STACKING));
}
}
void WindowManager::HandleButtonPress(const XButtonEvent& e) {
DLOG(INFO) << "Handling button press in window " << XidStr(e.window)
<< " at relative (" << e.x << ", " << e.y << "), absolute ("
<< e.x_root << ", " << e.y_root << ") with button " << e.button;
Window* win = GetWindow(e.window);
if (win)
focus_manager_->HandleButtonPressInWindow(win, e.time);
FOR_EACH_INTERESTED_EVENT_CONSUMER(
window_event_consumers_,
e.window,
HandleButtonPress(e.window, e.x, e.y, e.x_root,
e.y_root, e.button, e.time));
}
void WindowManager::HandleButtonRelease(const XButtonEvent& e) {
DLOG(INFO) << "Handling button release in window " << XidStr(e.window)
<< " at relative (" << e.x << ", " << e.y << "), absolute ("
<< e.x_root << ", " << e.y_root << ") with button " << e.button;
FOR_EACH_INTERESTED_EVENT_CONSUMER(
window_event_consumers_,
e.window,
HandleButtonRelease(e.window, e.x, e.y, e.x_root,
e.y_root, e.button, e.time));
}
void WindowManager::HandleClientMessage(const XClientMessageEvent& e) {
// _NET_WM_PING responses are spammy; don't log them.
if (!(e.message_type == GetXAtom(ATOM_WM_PROTOCOLS) &&
e.format == XConnection::kLongFormat &&
static_cast<XAtom>(e.data.l[0]) == GetXAtom(ATOM_NET_WM_PING))) {
DLOG(INFO) << "Handling client message for window " << XidStr(e.window)
<< " with type " << XidStr(e.message_type) << " ("
<< GetXAtomName(e.message_type) << ") and format " << e.format;
}
WmIpc::Message msg;
if (wm_ipc_->GetMessage(e.window, e.message_type, e.format, e.data.l, &msg)) {
if (msg.type() == chromeos::WM_IPC_MESSAGE_WM_NOTIFY_IPC_VERSION) {
wm_ipc_version_ = msg.param(0);
LOG(INFO) << "Got WM_NOTIFY_IPC_VERSION message saying that Chrome is "
<< "using version " << wm_ipc_version_;
} else {
DLOG(INFO) << "Decoded " << chromeos::WmIpcMessageTypeToString(msg.type())
<< " message";
FOR_EACH_INTERESTED_EVENT_CONSUMER(chrome_message_event_consumers_,
msg.type(),
HandleChromeMessage(msg));
}
} else {
if (static_cast<XAtom>(e.message_type) == GetXAtom(ATOM_MANAGER) &&
e.format == XConnection::kLongFormat &&
(static_cast<XAtom>(e.data.l[1]) == GetXAtom(ATOM_WM_S0) ||
static_cast<XAtom>(e.data.l[1]) == GetXAtom(ATOM_NET_WM_CM_S0))) {
if (static_cast<XWindow>(e.data.l[2]) != wm_xid_) {
LOG(WARNING) << "Ignoring client message saying that window "
<< XidStr(e.data.l[2]) << " got the "
<< GetXAtomName(e.data.l[1]) << " manager selection";
}
return;
}
if (e.format == XConnection::kLongFormat) {
FOR_EACH_INTERESTED_EVENT_CONSUMER(
window_event_consumers_,
e.window,
HandleClientMessage(e.window, e.message_type, e.data.l));
} else {
LOG(WARNING) << "Ignoring client message event with unsupported format "
<< e.format << " (we only handle 32-bit data currently)";
}
}
}
void WindowManager::HandleConfigureNotify(const XConfigureEvent& e) {
if (e.window == root_ && (e.width != width_ || e.height != height_)) {
DLOG(INFO) << "Got configure notify saying that root window has been "
<< "resized to " << e.width << "x" << e.height;
HandleScreenResize(e.width, e.height);
return;
}
// Even though _NET_CLIENT_LIST_STACKING only contains client windows
// that we're managing, we also need to keep track of other (e.g.
// override-redirect, or even untracked) windows: we receive
// notifications of the form "X is on top of Y", so we need to know where
// Y is even if we're not managing it so that we can stack X correctly.
// If this isn't an immediate child of the root window, ignore the
// ConfigureNotify.
if (!stacked_xids_->Contains(e.window))
return;
// Did the window get restacked from its previous position in
// |stacked_xids_|?
bool restacked = false;
// Check whether the stacking order changed.
const XWindow* prev_above = stacked_xids_->GetUnder(e.window);
// If we're on the bottom but weren't previously, or aren't on the bottom
// now but previously were or are now above a different sibling, update
// the order.
if ((!e.above && prev_above != NULL) ||
(e.above && (prev_above == NULL || *prev_above != e.above))) {
restacked = true;
stacked_xids_->Remove(e.window);
if (e.above && stacked_xids_->Contains(e.above)) {
stacked_xids_->AddAbove(e.window, e.above);
} else {
// |above| being unset means that the window is stacked beneath its
// siblings.
if (e.above) {
LOG(WARNING) << "ConfigureNotify for " << XidStr(e.window)
<< " said that it's stacked above " << XidStr(e.above)
<< ", which we don't know about";
}
stacked_xids_->AddOnBottom(e.window);
}
}
Window* win = GetWindow(e.window);
if (!win)
return;
DLOG(INFO) << "Handling configure notify for " << XidStr(e.window)
<< " to pos (" << e.x << ", " << e.y << ") and size "
<< e.width << "x" << e.height << ", above " << XidStr(e.above);
// There are several cases to consider here:
//
// - Override-redirect windows' calls to configure themselves are honored
// by the X server without any intervention on our part, so we only
// need to update their composited positions here.
// - Regular non-override-redirect windows' configuration calls are
// passed to us as ConfigureRequest events, so we would've already
// updated both their X and composited configuration in
// HandleConfigureRequest(). We don't need to do anything here.
// - For both types of window, we may have decided to move or resize the
// window ourselves earlier through a direct call to Window::Move() or
// Resize(). In that case, we would've already updated their
// composited position (or at least started the animation) then.
if (win->override_redirect()) {
win->MoveComposited(e.x, e.y, 0);
win->SaveClientPosition(e.x, e.y);
win->SaveClientSize(e.width, e.height);
// When we see a stacking change for an override-redirect window, we
// attempt to restack its actor correspondingly. If we don't have an
// actor for the X window directly under it, we walk down the stack
// until we find one.
XWindow above_xid = e.above;
while (above_xid) {
Window* above_win = GetWindow(above_xid);
Compositor::Actor* above_actor =
above_win ? above_win->actor() :
stacking_manager_->GetActorIfLayerXid(above_xid);
if (above_actor) {
DLOG(INFO) << "Stacking override-redirect window " << win->xid_str()
<< "'s actor above window " << XidStr(above_xid) << "'s";
win->StackCompositedAbove(above_actor, NULL, false);
break;
}
const XWindow* above_ptr = stacked_xids_->GetUnder(above_xid);
above_xid = above_ptr ? *above_ptr : 0;
}
} else {
if (restacked) {
// _NET_CLIENT_LIST_STACKING only includes managed (i.e.
// non-override-redirect) windows, so we only update it when a
// managed window's stacking position changed.
UpdateClientListStackingProperty();
}
}
win->HandleConfigureNotify(e.width, e.height);
}
void WindowManager::HandleConfigureRequest(const XConfigureRequestEvent& e) {
Window* win = GetWindow(e.window);
if (!win)
return;
DLOG(INFO)
<< "Handling configure request for " << XidStr(e.window)
<< " to pos ("
<< ((e.value_mask & CWX) ? StringPrintf("%d", e.x) : string("undef"))
<< ", "
<< ((e.value_mask & CWY) ? StringPrintf("%d", e.y) : string("undef"))
<< ") and size "
<< ((e.value_mask & CWWidth) ?
StringPrintf("%d", e.width) : string("undef "))
<< "x"
<< ((e.value_mask & CWHeight) ?
StringPrintf("%d", e.height) : string(" undef"));
if (win->override_redirect()) {
LOG(WARNING) << "Huh? Got a ConfigureRequest event for override-redirect "
<< "window " << win->xid_str();
}
const int req_x = (e.value_mask & CWX) ? e.x : win->client_x();
const int req_y = (e.value_mask & CWY) ? e.y : win->client_y();
const int req_width =
(e.value_mask & CWWidth) ? e.width : win->client_width();
const int req_height =
(e.value_mask & CWHeight) ? e.height : win->client_height();
// The X server should reject bogus requests before they get to us, but
// just in case...
if (req_width <= 0 || req_height <= 0) {
LOG(WARNING) << "Ignoring request to resize window " << win->xid_str()
<< " to " << req_width << "x" << req_height;
return;
}
if (!win->mapped()) {
// If the window is unmapped, it's unlikely that any event consumers
// will know what to do with it. Do whatever we were asked to do.
if (req_x != win->client_x() || req_y != win->client_y())
win->MoveClient(req_x, req_y);
if (req_width != win->client_width() || req_height != win->client_height())
win->ResizeClient(req_width, req_height, GRAVITY_NORTHWEST);
} else {
FOR_EACH_INTERESTED_EVENT_CONSUMER(
window_event_consumers_,
e.window,
HandleWindowConfigureRequest(win, req_x, req_y, req_width, req_height));
}
}
void WindowManager::HandleCreateNotify(const XCreateWindowEvent& e) {
if (GetWindow(e.window)) {
LOG(WARNING) << "Ignoring create notify for already-known window "
<< XidStr(e.window);
return;
}
DLOG(INFO) << "Handling create notify for "
<< (e.override_redirect ? "override-redirect" : "normal")
<< " window " << XidStr(e.window) << " at (" << e.x << ", " << e.y
<< ") with size " << e.width << "x" << e.height;
// Don't bother doing anything else for windows which aren't direct
// children of the root window.
if (e.parent != root_)
return;
// Grab the server while we're doing all of this; we can get a ton of
// errors when trying to track short-lived windows otherwise.
scoped_ptr<XConnection::ScopedServerGrab> grab(
xconn_->CreateScopedServerGrab());
// We want to pass the geometry from the CreateNotify event to Window's
// constructor, but we also need to include the window's depth, which sadly
// isn't included in CreateNotify events. Query the geometry again here to
// get the depth (and also to check that the window still exists).
XConnection::WindowGeometry new_geometry;
if (!xconn_->GetWindowGeometry(e.window, &new_geometry)) {
LOG(WARNING) << "Window " << XidStr(e.window)
<< " went away while we were handling its CreateNotify event";
return;
}
// CreateWindow stacks the new window on top of its siblings.
DCHECK(!stacked_xids_->Contains(e.window));
stacked_xids_->AddOnTop(e.window);
XConnection::WindowGeometry geometry;
geometry.bounds.reset(e.x, e.y, e.width, e.height);
geometry.border_width = e.border_width;
geometry.depth = new_geometry.depth;
// override-redirect means that the window manager isn't going to
// intercept this window's structure events, but we still need to
// composite the window, so we'll create a Window object for it
// regardless.
TrackWindow(e.window, e.override_redirect, geometry);
}
void WindowManager::HandleDamageNotify(const XDamageNotifyEvent& e) {
Window* win = GetWindow(e.drawable);
if (!win)
return;
win->HandleDamageNotify(
Rect(e.area.x, e.area.y, e.area.width, e.area.height));
}
void WindowManager::HandleDestroyNotify(const XDestroyWindowEvent& e) {
DLOG(INFO) << "Handling destroy notify for " << XidStr(e.window);
DCHECK(window_event_consumers_.find(e.window) ==
window_event_consumers_.end())
<< "One or more event consumers are still registered for destroyed "
<< "window " << XidStr(e.window);
if (stacked_xids_->Contains(e.window))
stacked_xids_->Remove(e.window);
// Don't bother doing anything else for windows which aren't direct
// children of the root window.
Window* win = GetWindow(e.window);
if (!win)
return;
if (!win->override_redirect())
UpdateClientListStackingProperty();
hash_map<XWindow, EventConsumer*>::iterator ec_it =
destroyed_window_event_consumers_.find(e.window);
if (ec_it != destroyed_window_event_consumers_.end()) {
// Transfer ownership of the window's compositing-related resources to
// the event consumer that wanted it.
DestroyedWindow* destroyed_win = win->HandleDestroyNotify();
ec_it->second->OwnDestroyedWindow(destroyed_win, e.window);
destroyed_window_event_consumers_.erase(ec_it);
}
client_windows_.erase(e.window);
win = NULL; // erasing from client_windows_ deletes window.
}
void WindowManager::HandleEnterNotify(const XEnterWindowEvent& e) {
DLOG(INFO) << "Handling enter notify for " << XidStr(e.window);
FOR_EACH_INTERESTED_EVENT_CONSUMER(
window_event_consumers_,
e.window,
HandlePointerEnter(e.window, e.x, e.y, e.x_root, e.y_root, e.time));
}
void WindowManager::HandleKeyPress(const XKeyEvent& e) {
// We grab the keyboard while shutting down or signing out; ignore any events
// that we get.
if (IsSessionEnding())
return;
key_bindings_->HandleKeyPress(e.keycode, e.state, e.time);
}
void WindowManager::HandleKeyRelease(const XKeyEvent& e) {
if (IsSessionEnding())
return;
key_bindings_->HandleKeyRelease(e.keycode, e.state, e.time);
}
void WindowManager::HandleLeaveNotify(const XLeaveWindowEvent& e) {
DLOG(INFO) << "Handling leave notify for " << XidStr(e.window);
FOR_EACH_INTERESTED_EVENT_CONSUMER(
window_event_consumers_,
e.window,
HandlePointerLeave(e.window, e.x, e.y, e.x_root, e.y_root, e.time));
}
void WindowManager::HandleMapNotify(const XMapEvent& e) {
DLOG(INFO) << "Handling map notify for " << XidStr(e.window);
Window* win = GetWindow(e.window);
if (!win || win->mapped())
return;
HandleMappedWindow(win);
}
void WindowManager::HandleMapRequest(const XMapRequestEvent& e) {
DLOG(INFO) << "Handling map request for " << XidStr(e.window);
Window* win = GetWindow(e.window);
if (!win) {
// This probably won't do much good; if we don't know about the window,
// we're not going to be compositing it.
LOG(WARNING) << "Mapping " << XidStr(e.window)
<< ", which we somehow didn't already know about";
xconn_->MapWindow(e.window);
return;
}
// We've seen this happen before (see http://crosbug.com/4176), and it
// can cause problems in code further down the pipe.
if (win->mapped()) {
LOG(WARNING) << "Ignoring map request for already-mapped window "
<< win->xid_str();
return;
}
if (win->override_redirect()) {
LOG(WARNING) << "Huh? Got a MapRequest event for override-redirect "
<< "window " << win->xid_str();
}
for (set<EventConsumer*>::iterator it = event_consumers_.begin();
it != event_consumers_.end(); ++it) {
if ((*it)->HandleWindowMapRequest(win)) {
// Map the window if the consumer approved it. If the request fails
// (probably because the window has already been destroyed), bail out --
// we won't get an UnmapNotify event later, so we don't want to
// incorrectly think that the window is mapped.
if (win->MapClient()) {
win->HandleMapRequested();
HandleMappedWindow(win);
}
return;
}
}
// Nobody was interested in the window.
LOG(WARNING) << "Not mapping window " << win->xid_str() << " with type "
<< win->type();
}
void WindowManager::HandleMappingNotify(const XMappingEvent& e) {
DLOG(INFO) << "Handling (keyboard) mapping notify";
xconn()->RefreshKeyboardMap(e.request, e.first_keycode, e.count);
key_bindings_->RefreshKeyMappings();
}
void WindowManager::HandleMotionNotify(const XMotionEvent& e) {
FOR_EACH_INTERESTED_EVENT_CONSUMER(
window_event_consumers_,
e.window,
HandlePointerMotion(e.window, e.x, e.y, e.x_root, e.y_root, e.time));
}
void WindowManager::HandlePropertyNotify(const XPropertyEvent& e) {
if (e.window == root_ && e.atom == GetXAtom(ATOM_CHROME_LOGGED_IN)) {
int value = 0;
bool logged_in = xconn_->GetIntProperty(root_, e.atom, &value) && value;
SetLoggedInState(logged_in, false); // initial=false
return;
}
// TODO: These can currently be very spammy. The property is changed in
// response to user interaction, including scrollwheel events, which can
// be generated very quickly (thousands per second!). Exit early for now
// so we don't get bogged down writing to the log, but this can be
// deleted later once we have a better solution for handling fast
// scrolling.
if (e.atom == GetXAtom(ATOM_NET_WM_USER_TIME))
return;
Window* win = GetWindow(e.window);
if (win) {
bool deleted = (e.state == PropertyDelete);
DLOG(INFO) << "Handling property notify for " << win->xid_str() << " about "
<< (deleted ? "deleted " : "") << "property "
<< XidStr(e.atom) << " (" << GetXAtomName(e.atom) << ")";
if (e.atom == GetXAtom(ATOM_NET_WM_NAME)) {
win->FetchAndApplyTitle();
} else if (e.atom == GetXAtom(ATOM_WM_HINTS)) {
win->FetchAndApplyWmHints();
} else if (e.atom == GetXAtom(ATOM_WM_NORMAL_HINTS)) {
win->FetchAndApplySizeHints();
} else if (e.atom == GetXAtom(ATOM_WM_TRANSIENT_FOR)) {
win->FetchAndApplyTransientHint();
} else if (e.atom == GetXAtom(ATOM_WM_PROTOCOLS)) {
win->FetchAndApplyWmProtocols();
} else if (e.atom == GetXAtom(ATOM_CHROME_WINDOW_TYPE)) {
win->FetchAndApplyWindowType();
} else if (e.atom == GetXAtom(ATOM_NET_WM_WINDOW_TYPE)) {
win->FetchAndApplyWmWindowType();
} else if (e.atom == GetXAtom(ATOM_NET_WM_WINDOW_OPACITY)) {
win->FetchAndApplyWindowOpacity();
} else if (e.atom == GetXAtom(ATOM_NET_WM_STATE)) {
win->FetchAndApplyWmState();
} else if (e.atom == GetXAtom(ATOM_WM_CLIENT_MACHINE)) {
win->FetchAndApplyWmClientMachine();
} else if (e.atom == GetXAtom(ATOM_NET_WM_PID)) {
win->FetchAndApplyWmPid();
} else if (e.atom == GetXAtom(ATOM_NET_WM_SYNC_REQUEST_COUNTER)) {
// Just re-fetch WM_PROTOCOLS here; this property is sometimes only set
// after we've already read WM_PROTOCOLS -- see comment #3 at
// http://crosbug.com/5846.
win->FetchAndApplyWmProtocols();
}
}
// Notify any event consumers that were interested in this property.
FOR_EACH_INTERESTED_EVENT_CONSUMER(
property_change_event_consumers_,
make_pair(e.window, e.atom),
HandleWindowPropertyChange(e.window, e.atom));
}
void WindowManager::HandleReparentNotify(const XReparentEvent& e) {
DLOG(INFO) << "Handling reparent notify for " << XidStr(e.window)
<< " to " << XidStr(e.parent);
if (e.parent == root_) {
// If a window got reparented *to* the root window, we want to track
// it in the stacking order (we don't bother tracking it as a true
// toplevel window; we don't see this happen much outside of Flash
// windows). The window gets stacked on top of its new siblings.
DCHECK(!stacked_xids_->Contains(e.window));
stacked_xids_->AddOnTop(e.window);
} else {
// Otherwise, if it got reparented away from us, stop tracking it.
// We ignore windows that aren't immediate children of the root window.
if (!stacked_xids_->Contains(e.window))
return;
stacked_xids_->Remove(e.window);
if (mapped_xids_->Contains(e.window)) {
mapped_xids_->Remove(e.window);
UpdateClientListProperty();
}
Window* win = GetWindow(e.window);
if (win) {
if (!win->override_redirect())
UpdateClientListStackingProperty();
// Make sure that all event consumers know that the window's going away.
if (win->mapped())
FOR_EACH_EVENT_CONSUMER(event_consumers_, HandleWindowUnmap(win));
focus_manager_->HandleWindowUnmap(win);
client_windows_.erase(e.window);
// We're not going to be compositing the window anymore, so
// unredirect it so it'll get drawn using the usual path.
xconn_->UnredirectWindowForCompositing(e.window);
}
}
}
void WindowManager::HandleRRScreenChangeNotify(
const XRRScreenChangeNotifyEvent& e) {
DLOG(INFO) << "Got RRScreenChangeNotify event to "
<< e.width << "x" << e.height;
// TODO: This doesn't seem to work reliably (we're not seeing any events
// right now), so we get size change notifications via ConfigureNotify
// events on the root window instead. Either stop using randr or figure
// out what's wrong if we need it for e.g. rotate.
}
void WindowManager::HandleShapeNotify(const XShapeEvent& e) {
Window* win = GetWindow(e.window);
if (!win)
return;
DLOG(INFO) << "Handling " << (e.kind == ShapeBounding ? "bounding" : "clip")
<< " shape notify for " << XidStr(e.window);
if (e.kind == ShapeBounding)
win->FetchAndApplyShape();
}
void WindowManager::HandleSyncAlarmNotify(const XSyncAlarmNotifyEvent& e) {
int64_t value =
(static_cast<int64_t>(XSyncValueHigh32(e.counter_value)) << 32) |
XSyncValueLow32(e.counter_value);
DLOG(INFO) << "Handling sync alarm notify for alarm " << XidStr(e.alarm)
<< " with value " << value;
Window* win = FindWithDefault(
sync_alarms_to_windows_, e.alarm, static_cast<Window*>(NULL));
if (!win) {
// We get notification that counters have been reset to 0 after the
// corresponding windows are destroyed; don't log a warning for that.
if (value != 0)
LOG(WARNING) << "Ignoring unregistered alarm " << XidStr(e.alarm);
return;
}
win->HandleSyncAlarmNotify(e.alarm, value);
}
void WindowManager::HandleUnmapNotify(const XUnmapEvent& e) {
DLOG(INFO) << "Handling unmap notify for " << XidStr(e.window);
Window* win = GetWindow(e.window);
if (!win || !win->mapped())
return;
win->HandleUnmapNotify();
FOR_EACH_EVENT_CONSUMER(event_consumers_, HandleWindowUnmap(win));
if (win->override_redirect()) {
win->HideComposited();
return;
}
SetWmStateProperty(e.window, 0); // WithdrawnState
// Notify the focus manager last in case any event consumers need to do
// something special when they see the focused window getting unmapped.
focus_manager_->HandleWindowUnmap(win);
if (mapped_xids_->Contains(win->xid())) {
mapped_xids_->Remove(win->xid());
UpdateClientListProperty();
UpdateClientListStackingProperty();
}
}
void WindowManager::RunCommand(string command) {
if (command.empty())
return;
command += " &";
DLOG(INFO) << "Running command \"" << command << "\"";
if (system(command.c_str()) < 0)
LOG(WARNING) << "Got error while running \"" << command << "\"";
}
XWindow WindowManager::GetArbitraryChromeWindow() {
for (WindowMap::const_iterator i = client_windows_.begin();
i != client_windows_.end(); ++i) {
if (WmIpcWindowTypeIsChrome(i->second->type()))
return i->first;
}
return 0;
}
void WindowManager::TakeScreenshot(bool use_active_window) {
const string& dir = logged_in_ ?
FLAGS_logged_in_screenshot_output_dir :
FLAGS_logged_out_screenshot_output_dir;
if (access(dir.c_str(), F_OK) != 0 &&
!file_util::CreateDirectory(FilePath(dir))) {
LOG(ERROR) << "Unable to create screenshot directory " << dir;
return;
}
string command = FLAGS_screenshot_binary;
if (use_active_window) {
if (!active_window_xid_) {
LOG(WARNING) << "No active window to use for screenshot";
return;
}
command += StringPrintf(" --window=0x%lx", active_window_xid_);
}
string filename = StringPrintf("%s/screenshot-%s.png", dir.c_str(),
GetTimeAsString(GetCurrentTimeSec()).c_str());
command += " " + filename + " &";
if (system(command.c_str()) < 0) {
LOG(ERROR) << "Taking screenshot via \"" << command << "\" failed";
} else {
LOG(INFO) << "Saved screenshot to " << filename;
}
}
void WindowManager::CreateStartupBackground() {
startup_pixmap_ =
xconn_->CreatePixmap(root_, Size(width_, height_), root_depth_);
xconn_->CopyArea(root_, // src
startup_pixmap_, // dest
Point(0, 0), // src_pos
Point(0, 0), // dest_pos
Size(width_, height_));
Compositor::TexturePixmapActor* pixmap_actor =
compositor_->CreateTexturePixmap();
pixmap_actor->SetPixmap(startup_pixmap_);
startup_background_.reset(pixmap_actor);
startup_background_->SetName("startup background");
stage_->AddActor(startup_background_.get());
stacking_manager_->StackActorAtTopOfLayer(
startup_background_.get(), StackingManager::LAYER_BACKGROUND);
startup_background_->Show();
}
void WindowManager::HideUnacceleratedGraphicsActor() {
if (unaccelerated_graphics_actor_.get())
unaccelerated_graphics_actor_->SetOpacity(
0.f, kUnacceleratedGraphicsActorHideAnimMs );
event_loop_->RemoveTimeout(hide_unaccelerated_graphics_actor_timeout_id_);
hide_unaccelerated_graphics_actor_timeout_id_ = 0;
}
void WindowManager::DisableCompositing() {
// Make sure to remove window bounding region before unredirect window
// because unredirect window will not create exposure events, so we need
// to remove bounding region first to let X know that it should refresh
// the content, otherwise the content will stay stale.
DCHECK(unredirected_fullscreen_xid_);
xconn_->RemoveWindowBoundingRegion(overlay_xid_);
xconn_->UnredirectWindowForCompositing(unredirected_fullscreen_xid_);
compositor_->set_should_draw_frame(false);
DLOG(INFO) << "Turned compositing off";
}
void WindowManager::DestroyLoginControllerInternal() {
if (!login_controller_.get())
return;
DLOG(INFO) << "Destroying login controller";
event_consumers_.erase(login_controller_.get());
login_controller_.reset();
}
void WindowManager::PingChrome() {
chrome_watchdog_->SendPingToChrome(GetCurrentTimeFromServer(),
kPingChromeTimeoutMs);
}
} // namespace window_manager