| // Copyright (c) 2009 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.h" |
| |
| #include <algorithm> |
| |
| #include <gflags/gflags.h> |
| |
| #include "base/logging.h" |
| #include "base/string_util.h" |
| #include "chromeos/obsolete_logging.h" |
| |
| #include "window_manager/atom_cache.h" |
| #include "window_manager/shadow.h" |
| #include "window_manager/util.h" |
| #include "window_manager/window_manager.h" |
| #include "window_manager/x_connection.h" |
| |
| DEFINE_bool(window_drop_shadows, true, "Display drop shadows under windows"); |
| |
| DECLARE_bool(wm_use_compositing); // from window_manager.cc |
| |
| namespace chromeos { |
| |
| Window::Window(WindowManager* wm, XWindow xid) |
| : xid_(xid), |
| xid_str_(XidStr(xid_)), |
| wm_(wm), |
| actor_(wm_->clutter()->CreateTexturePixmap()), |
| shadow_(FLAGS_window_drop_shadows ? new Shadow(wm->clutter()) : NULL), |
| transient_for_xid_(None), |
| override_redirect_(false), |
| mapped_(false), |
| shaped_(false), |
| type_(WmIpc::WINDOW_TYPE_UNKNOWN), |
| client_x_(-1), |
| client_y_(-1), |
| client_width_(1), |
| client_height_(1), |
| client_opacity_(1.0), |
| composited_shown_(false), |
| composited_x_(-1), |
| composited_y_(-1), |
| composited_scale_x_(1.0), |
| composited_scale_y_(1.0), |
| composited_opacity_(1.0), |
| using_shadow_(false), |
| shadow_opacity_(1.0), |
| min_width_hint_(-1), |
| min_height_hint_(-1), |
| max_width_hint_(-1), |
| max_height_hint_(-1), |
| base_width_hint_(-1), |
| base_height_hint_(-1), |
| width_inc_hint_(-1), |
| height_inc_hint_(-1), |
| supports_wm_take_focus_(false), |
| supports_wm_delete_window_(false), |
| wm_state_fullscreen_(false), |
| wm_state_maximized_horz_(false), |
| wm_state_maximized_vert_(false), |
| wm_state_modal_(false) { |
| // Listen for focus, property, and shape changes on this window. |
| wm_->xconn()->SelectInputOnWindow( |
| xid_, FocusChangeMask|PropertyChangeMask, true); |
| wm_->xconn()->SelectShapeEventsOnWindow(xid_); |
| |
| // Get the window's initial state. |
| XWindowAttributes attr; |
| if (wm_->xconn()->GetWindowAttributes(xid_, &attr)) { |
| // We update 'mapped_' when we get the MapNotify event instead of doing |
| // it here; things get tricky otherwise since there's a race as to |
| // whether override-redirect windows are mapped or not at this point. |
| override_redirect_ = attr.override_redirect; |
| client_x_ = composited_x_ = attr.x; |
| client_y_ = composited_y_ = attr.y; |
| client_width_ = attr.width; |
| client_height_ = attr.height; |
| |
| // If the window has a border, remove it -- they make things more confusing |
| // (we need to include the border when telling Clutter the window's size, |
| // but it's not included when telling X to resize the window, etc.). |
| // TODO: Reconsider this if there's actually a case where borders are useful |
| // to have. |
| if (attr.border_width > 0) |
| wm_->xconn()->SetWindowBorderWidth(xid_, 0); |
| } |
| |
| if (FLAGS_wm_use_compositing) { |
| // Send the window to an offscreen pixmap. |
| // Something within clutter_x11_texture_pixmap_set_window() appears to |
| // be triggering a BadAccess error when we don't sync with the server |
| // after calling XCompositeRedirectWindow() (but we get the sync |
| // implicitly from the error-trapping in |
| // XConnection::RedirectWindowForCompositing()). |
| VLOG(1) << "Redirecting " |
| << (override_redirect_ ? "override-redirect " : "") |
| << "window " << xid_str() << " " |
| << "at (" << client_x_ << ", " << client_y_ << ") " |
| << "with dimensions " << client_width_ << "x" << client_height_ |
| << " for compositing"; |
| wm_->xconn()->RedirectWindowForCompositing(xid_); |
| } |
| |
| if (!actor_->IsUsingTexturePixmapExtension()) { |
| static bool logged = false; |
| LOG_IF(WARNING, !logged) << |
| "Not using texture-from-pixmap extension -- expect slowness"; |
| logged = true; |
| } |
| actor_->SetTexturePixmapWindow(xid_); |
| actor_->Move(composited_x_, composited_y_, 0); |
| actor_->SetSize(client_width_, client_height_); |
| actor_->SetVisibility(false); |
| actor_->SetName(std::string("window ") + xid_str()); |
| wm_->stage()->AddActor(actor_.get()); |
| |
| if (shadow_.get()) { |
| shadow_->group()->SetName( |
| std::string("shadow group for window " + xid_str())); |
| wm_->stage()->AddActor(shadow_->group()); |
| shadow_->Move(composited_x_, composited_y_, 0); |
| shadow_->SetOpacity(shadow_opacity_, 0); |
| shadow_->Resize(composited_scale_x_ * client_width_, |
| composited_scale_y_ * client_height_, 0); |
| } |
| |
| // Properties could've been set on this window after it was created but |
| // before we selected on PropertyChangeMask, so we need to query them |
| // here. Don't create a shadow yet; we still need to check if it's |
| // shaped. |
| FetchAndApplyWindowType(false); |
| |
| // Check if the window is shaped. |
| FetchAndApplyShape(true); |
| |
| // Check if the client window has set _NET_WM_WINDOW_OPACITY. |
| FetchAndApplyWindowOpacity(); |
| |
| // Apply the size hints, which may resize the actor. |
| FetchAndApplySizeHints(); |
| |
| // Load other properties that might've gotten set before we started |
| // listening for property changes on the window. |
| FetchAndApplyWmProtocols(); |
| FetchAndApplyWmState(); |
| FetchAndApplyChromeState(); |
| FetchAndApplyTransientHint(); |
| } |
| |
| Window::~Window() { |
| } |
| |
| bool Window::FetchAndApplySizeHints() { |
| // Once windows have been mapped, they should just request any desired |
| // changes themselves. |
| if (mapped_) |
| return true; |
| |
| XSizeHints size_hints; |
| memset(&size_hints, 0, sizeof(size_hints)); |
| long supplied_hints = 0; |
| if (!wm_->xconn()->GetSizeHintsForWindow( |
| xid_, &size_hints, &supplied_hints)) { |
| return false; |
| } |
| |
| if (size_hints.flags & PMinSize) { |
| min_width_hint_ = size_hints.min_width; |
| min_height_hint_ = size_hints.min_height; |
| } else { |
| min_width_hint_ = -1; |
| min_height_hint_ = -1; |
| } |
| if (size_hints.flags & PMaxSize) { |
| max_width_hint_ = size_hints.max_width; |
| max_height_hint_ = size_hints.max_height; |
| } else { |
| max_width_hint_ = -1; |
| max_height_hint_ = -1; |
| } |
| if (size_hints.flags & PBaseSize) { |
| base_width_hint_ = size_hints.base_width; |
| base_height_hint_ = size_hints.base_height; |
| } else { |
| base_width_hint_ = -1; |
| base_height_hint_ = -1; |
| } |
| if (size_hints.flags & PResizeInc) { |
| width_inc_hint_ = size_hints.width_inc; |
| height_inc_hint_ = size_hints.height_inc; |
| } else { |
| width_inc_hint_ = -1; |
| height_inc_hint_ = -1; |
| } |
| VLOG(1) << "Size hints for " << xid_str() << ":" |
| << " min=" << min_width_hint_ << "x" << min_height_hint_ |
| << " max=" << max_width_hint_ << "x" << max_height_hint_ |
| << " base=" << base_width_hint_ << "x" << base_height_hint_ |
| << " inc=" << width_inc_hint_ << "x" << height_inc_hint_; |
| |
| // Ignore position, aspect ratio, etc. hints for now. |
| if (!override_redirect_ && |
| (size_hints.flags & USSize || size_hints.flags & PSize)) { |
| VLOG(1) << "Got " << (size_hints.flags & USSize ? "user" : "program") |
| << "-specified size hints for " << xid_str() << ": " |
| << size_hints.width << "x" << size_hints.width; |
| ResizeClient(size_hints.width, size_hints.height, GRAVITY_NORTHWEST); |
| } |
| |
| return true; |
| } |
| |
| bool Window::FetchAndApplyTransientHint() { |
| if (!wm_->xconn()->GetTransientHintForWindow(xid_, &transient_for_xid_)) |
| return false; |
| return true; |
| } |
| |
| bool Window::FetchAndApplyWindowType(bool update_shadow) { |
| bool result = wm_->wm_ipc()->GetWindowType(xid_, &type_, &type_params_); |
| VLOG(1) << "Window " << xid_str() << " has type " << type_; |
| if (update_shadow) |
| UpdateShadowIfNecessary(); |
| return result; |
| } |
| |
| void Window::FetchAndApplyWindowOpacity() { |
| static const uint32 kMaxOpacity = 0xffffffffU; |
| |
| uint32 opacity = kMaxOpacity; |
| wm_->xconn()->GetIntProperty( |
| xid_, |
| wm_->GetXAtom(ATOM_NET_WM_WINDOW_OPACITY), |
| reinterpret_cast<int32*>(&opacity)); |
| client_opacity_ = (opacity == kMaxOpacity) ? |
| 1.0 : static_cast<double>(opacity) / kMaxOpacity; |
| |
| // TODO: It'd be nicer if we didn't interrupt any in-progress opacity |
| // animations. |
| SetCompositedOpacity(composited_opacity_, 0); |
| } |
| |
| void Window::FetchAndApplyWmProtocols() { |
| supports_wm_take_focus_ = false; |
| supports_wm_delete_window_ = false; |
| |
| std::vector<int> wm_protocols; |
| if (!wm_->xconn()->GetIntArrayProperty( |
| xid_, wm_->GetXAtom(ATOM_WM_PROTOCOLS), &wm_protocols)) { |
| return; |
| } |
| |
| XAtom wm_take_focus = wm_->GetXAtom(ATOM_WM_TAKE_FOCUS); |
| XAtom wm_delete_window = wm_->GetXAtom(ATOM_WM_DELETE_WINDOW); |
| for (std::vector<int>::const_iterator it = wm_protocols.begin(); |
| it != wm_protocols.end(); ++it) { |
| if (static_cast<XAtom>(*it) == wm_take_focus) { |
| VLOG(2) << "Window " << xid_str() << " supports WM_TAKE_FOCUS"; |
| supports_wm_take_focus_ = true; |
| } else if (static_cast<XAtom>(*it) == wm_delete_window) { |
| VLOG(2) << "Window " << xid_str() << " supports WM_DELETE_WINDOW"; |
| supports_wm_delete_window_ = true; |
| } |
| } |
| } |
| |
| void Window::FetchAndApplyWmState() { |
| wm_state_fullscreen_ = false; |
| wm_state_maximized_horz_ = false; |
| wm_state_maximized_vert_ = false; |
| wm_state_modal_ = false; |
| |
| std::vector<int> state_atoms; |
| if (!wm_->xconn()->GetIntArrayProperty( |
| xid_, wm_->GetXAtom(ATOM_NET_WM_STATE), &state_atoms)) { |
| return; |
| } |
| |
| XAtom fullscreen_atom = wm_->GetXAtom(ATOM_NET_WM_STATE_FULLSCREEN); |
| XAtom max_horz_atom = wm_->GetXAtom(ATOM_NET_WM_STATE_MAXIMIZED_HORZ); |
| XAtom max_vert_atom = wm_->GetXAtom(ATOM_NET_WM_STATE_MAXIMIZED_VERT); |
| XAtom modal_atom = wm_->GetXAtom(ATOM_NET_WM_STATE_MODAL); |
| for (std::vector<int>::const_iterator it = state_atoms.begin(); |
| it != state_atoms.end(); ++it) { |
| XAtom atom = static_cast<XAtom>(*it); |
| if (atom == fullscreen_atom) |
| wm_state_fullscreen_ = true; |
| if (atom == max_horz_atom) |
| wm_state_maximized_horz_ = true; |
| if (atom == max_vert_atom) |
| wm_state_maximized_vert_ = true; |
| else if (atom == modal_atom) |
| wm_state_modal_ = true; |
| } |
| |
| VLOG(1) << "Fetched _NET_WM_STATE for " << xid_str() << ":" |
| << " fullscreen=" << wm_state_fullscreen_ |
| << " maximized_horz=" << wm_state_maximized_horz_ |
| << " maximized_vert=" << wm_state_maximized_vert_ |
| << " modal=" << wm_state_modal_; |
| } |
| |
| void Window::FetchAndApplyChromeState() { |
| XAtom state_xatom = wm_->GetXAtom(ATOM_CHROME_STATE); |
| chrome_state_xatoms_.clear(); |
| std::vector<int> state_xatoms; |
| if (!wm_->xconn()->GetIntArrayProperty(xid_, state_xatom, &state_xatoms)) |
| return; |
| |
| std::string debug_str; |
| for (std::vector<int>::const_iterator it = state_xatoms.begin(); |
| it != state_xatoms.end(); ++it) { |
| chrome_state_xatoms_.insert(static_cast<XAtom>(*it)); |
| if (!debug_str.empty()) |
| debug_str += " "; |
| debug_str += wm_->GetXAtomName(static_cast<XAtom>(*it)); |
| } |
| VLOG(1) << "Fetched " << wm_->GetXAtomName(state_xatom) << " for " |
| << xid_str() << ": " << debug_str; |
| } |
| |
| void Window::FetchAndApplyShape(bool update_shadow) { |
| shaped_ = false; |
| ByteMap bytemap(client_width_, client_height_); |
| |
| // We don't grab the server around these two requests, so it's possible |
| // that a shaped window will have become unshaped between them and we'll |
| // think that the window is shaped but get back an unshaped region. This |
| // should be okay; we should get another ShapeNotify event for the window |
| // becoming unshaped and clear the useless mask then. |
| if (wm_->xconn()->IsWindowShaped(xid_) && |
| wm_->xconn()->GetWindowBoundingRegion(xid_, &bytemap)) { |
| shaped_ = true; |
| } |
| |
| if (!shaped_) { |
| actor_->ClearAlphaMask(); |
| } else { |
| VLOG(1) << "Got shape for " << xid_str(); |
| actor_->SetAlphaMask(bytemap.bytes(), bytemap.width(), bytemap.height()); |
| } |
| if (update_shadow) |
| UpdateShadowIfNecessary(); |
| } |
| |
| bool Window::FetchMapState() { |
| XWindowAttributes attr; |
| if (!wm_->xconn()->GetWindowAttributes(xid_, &attr)) |
| return false; |
| return (attr.map_state != IsUnmapped); |
| } |
| |
| bool Window::HandleWmStateMessage(const XClientMessageEvent& event) { |
| XAtom wm_state_atom = wm_->GetXAtom(ATOM_NET_WM_STATE); |
| if (event.message_type != wm_state_atom || |
| event.format != XConnection::kLongFormat) { |
| return false; |
| } |
| |
| XAtom fullscreen_atom = wm_->GetXAtom(ATOM_NET_WM_STATE_FULLSCREEN); |
| if (static_cast<XAtom>(event.data.l[1]) == fullscreen_atom || |
| static_cast<XAtom>(event.data.l[2]) == fullscreen_atom) { |
| SetWmStateInternal(event.data.l[0], &wm_state_fullscreen_); |
| } |
| |
| XAtom modal_atom = wm_->GetXAtom(ATOM_NET_WM_STATE_MODAL); |
| if (static_cast<XAtom>(event.data.l[1]) == modal_atom || |
| static_cast<XAtom>(event.data.l[2]) == modal_atom) { |
| SetWmStateInternal(event.data.l[0], &wm_state_modal_); |
| } |
| |
| // We don't let clients toggle their maximized state currently. |
| |
| return UpdateWmStateProperty(); |
| } |
| |
| bool Window::ChangeWmState(const std::vector<std::pair<XAtom, bool> >& states) { |
| for (std::vector<std::pair<XAtom, bool> >::const_iterator it = states.begin(); |
| it != states.end(); ++it) { |
| XAtom xatom = it->first; |
| int action = it->second; // 0 is remove, 1 is add |
| |
| if (xatom == wm_->GetXAtom(ATOM_NET_WM_STATE_FULLSCREEN)) |
| SetWmStateInternal(action, &wm_state_fullscreen_); |
| else if (xatom == wm_->GetXAtom(ATOM_NET_WM_STATE_MAXIMIZED_HORZ)) |
| SetWmStateInternal(action, &wm_state_maximized_horz_); |
| else if (xatom == wm_->GetXAtom(ATOM_NET_WM_STATE_MAXIMIZED_VERT)) |
| SetWmStateInternal(action, &wm_state_maximized_vert_); |
| else if (xatom == wm_->GetXAtom(ATOM_NET_WM_STATE_MODAL)) |
| SetWmStateInternal(action, &wm_state_modal_); |
| else |
| LOG(ERROR) << "Unsupported _NET_WM_STATE " << xatom |
| << " for " << xid_str(); |
| } |
| return UpdateWmStateProperty(); |
| } |
| |
| bool Window::ChangeChromeState( |
| const std::vector<std::pair<XAtom, bool> >& states) { |
| for (std::vector<std::pair<XAtom, bool> >::const_iterator it = states.begin(); |
| it != states.end(); ++it) { |
| if (it->second) |
| chrome_state_xatoms_.insert(it->first); |
| else |
| chrome_state_xatoms_.erase(it->first); |
| } |
| return UpdateChromeStateProperty(); |
| } |
| |
| |
| bool Window::TakeFocus(Time timestamp) { |
| VLOG(2) << "Focusing " << xid_str() << " using time " << timestamp; |
| if (supports_wm_take_focus_) { |
| XEvent event; |
| XClientMessageEvent* client_event = &(event.xclient); |
| client_event->type = ClientMessage; |
| client_event->window = xid_; |
| client_event->message_type = wm_->GetXAtom(ATOM_WM_PROTOCOLS); |
| client_event->format = XConnection::kLongFormat; |
| client_event->data.l[0] = wm_->GetXAtom(ATOM_WM_TAKE_FOCUS); |
| client_event->data.l[1] = timestamp; |
| if (!wm_->xconn()->SendEvent(xid_, &event, 0)) |
| return false; |
| } else { |
| if (!wm_->xconn()->FocusWindow(xid_, timestamp)) |
| return false; |
| } |
| focused_ = true; |
| return true; |
| } |
| |
| bool Window::SendDeleteRequest(Time timestamp) { |
| VLOG(2) << "Maybe asking " << xid_str() << " to delete itself with time " |
| << timestamp; |
| if (!supports_wm_delete_window_) |
| return false; |
| |
| XEvent event; |
| XClientMessageEvent* client_event = &(event.xclient); |
| client_event->type = ClientMessage; |
| client_event->window = xid_; |
| client_event->message_type = wm_->GetXAtom(ATOM_WM_PROTOCOLS); |
| client_event->format = XConnection::kLongFormat; |
| client_event->data.l[0] = wm_->GetXAtom(ATOM_WM_DELETE_WINDOW); |
| client_event->data.l[1] = timestamp; |
| return wm_->xconn()->SendEvent(xid_, &event, 0); |
| } |
| |
| bool Window::AddPassiveButtonGrab() { |
| VLOG(2) << "Adding passive button grab for " << xid_str(); |
| return wm_->xconn()->AddPassiveButtonGrabOnWindow( |
| xid_, AnyButton, ButtonPressMask); |
| } |
| |
| bool Window::RemovePassiveButtonGrab() { |
| VLOG(2) << "Removing passive button grab for " << xid_str(); |
| return wm_->xconn()->RemovePassiveButtonGrabOnWindow(xid_, AnyButton); |
| } |
| |
| void Window::GetMaxSize(int desired_width, int desired_height, |
| int* width_out, int* height_out) const { |
| CHECK_GT(desired_width, 0); |
| CHECK_GT(desired_height, 0); |
| |
| if (max_width_hint_ > 0) |
| desired_width = std::min(max_width_hint_, desired_width); |
| if (min_width_hint_ > 0) |
| desired_width = std::max(min_width_hint_, desired_width); |
| |
| if (width_inc_hint_ > 0) { |
| int base_width = |
| (base_width_hint_ > 0) ? base_width_hint_ : |
| (min_width_hint_ > 0) ? min_width_hint_ : |
| 0; |
| *width_out = base_width + |
| ((desired_width - base_width) / width_inc_hint_) * width_inc_hint_; |
| } else { |
| *width_out = desired_width; |
| } |
| |
| if (max_height_hint_ > 0) |
| desired_height = std::min(max_height_hint_, desired_height); |
| if (min_height_hint_ > 0) |
| desired_height = std::max(min_height_hint_, desired_height); |
| |
| if (height_inc_hint_ > 0) { |
| int base_height = |
| (base_height_hint_ > 0) ? base_height_hint_ : |
| (min_height_hint_ > 0) ? min_height_hint_ : |
| 0; |
| *height_out = base_height + |
| ((desired_height - base_height) / height_inc_hint_) * height_inc_hint_; |
| } else { |
| *height_out = desired_height; |
| } |
| VLOG(2) << "Max size for " << xid_str() << " is " |
| << *width_out << "x" << *height_out |
| << " (desired was " << desired_width << "x" << desired_height << ")"; |
| } |
| |
| bool Window::MapClient() { |
| VLOG(2) << "Mapping " << xid_str(); |
| if (!wm_->xconn()->MapWindow(xid_)) |
| return false; |
| return true; |
| } |
| |
| bool Window::UnmapClient() { |
| VLOG(2) << "Unmapping " << xid_str(); |
| if (!wm_->xconn()->UnmapWindow(xid_)) |
| return false; |
| return true; |
| } |
| |
| void Window::SaveClientAndCompositedSize(int width, int height) { |
| VLOG(2) << "Setting " << xid_str() << "'s client and composited size to " |
| << width << "x" << height; |
| client_width_ = width; |
| client_height_ = height; |
| actor_->SetSize(client_width_, client_height_); |
| if (shadow_.get()) { |
| shadow_->Resize(composited_scale_x_ * client_width_, |
| composited_scale_y_ * client_height_, |
| 0); // anim_ms |
| } |
| } |
| |
| bool Window::MoveClient(int x, int y) { |
| VLOG(2) << "Moving " << xid_str() << "'s client window to (" |
| << x << ", " << y << ")"; |
| if (!wm_->xconn()->MoveWindow(xid_, x, y)) |
| return false; |
| SaveClientPosition(x, y); |
| return true; |
| } |
| |
| bool Window::MoveClientOffscreen() { |
| return MoveClient(wm_->width(), wm_->height()); |
| } |
| |
| bool Window::MoveClientToComposited() { |
| return MoveClient(composited_x_, composited_y_); |
| } |
| |
| bool Window::CenterClientOverWindow(Window* win) { |
| CHECK(win); |
| int center_x = win->client_x() + 0.5 * win->client_width(); |
| int center_y = win->client_y() + 0.5 * win->client_height(); |
| return MoveClient(center_x - 0.5 * client_width_, |
| center_y - 0.5 * client_height_); |
| } |
| |
| bool Window::ResizeClient(int width, int height, Gravity gravity) { |
| int dx = (gravity == GRAVITY_NORTHEAST || gravity == GRAVITY_SOUTHEAST) ? |
| width - client_width_ : 0; |
| int dy = (gravity == GRAVITY_SOUTHWEST || gravity == GRAVITY_SOUTHEAST) ? |
| height - client_height_ : 0; |
| |
| VLOG(2) << "Resizing " << xid_str() << "'s client window to " |
| << width << "x" << height; |
| if (dx || dy) { |
| // If we need to move the window as well due to gravity, do it all in |
| // one ConfigureWindow request to the server. |
| if (!wm_->xconn()->ConfigureWindow( |
| xid_, client_x_ - dx, client_y_ - dy, width, height)) { |
| return false; |
| } |
| SaveClientPosition(client_x_ - dx, client_y_ - dy); |
| // TODO: Test whether this works for scaled windows. |
| MoveComposited(composited_x_ - composited_scale_x_ * dx, |
| composited_y_ - composited_scale_y_ * dy, |
| 0); |
| } else { |
| if (!wm_->xconn()->ResizeWindow(xid_, width, height)) |
| return false; |
| } |
| |
| SaveClientAndCompositedSize(width, height); |
| return true; |
| } |
| |
| bool Window::RaiseClient() { |
| bool result = wm_->xconn()->RaiseWindow(xid_); |
| return result; |
| } |
| |
| bool Window::StackClientAbove(XWindow sibling_xid) { |
| CHECK(sibling_xid != None); |
| bool result = wm_->xconn()->StackWindow(xid_, sibling_xid, true); |
| return result; |
| } |
| |
| bool Window::StackClientBelow(XWindow sibling_xid) { |
| CHECK(sibling_xid != None); |
| bool result = wm_->xconn()->StackWindow(xid_, sibling_xid, false); |
| return result; |
| } |
| |
| void Window::MoveComposited(int x, int y, int anim_ms) { |
| VLOG(2) << "Moving " << xid_str() << "'s composited window to (" |
| << x << ", " << y << ") over " << anim_ms << " ms"; |
| composited_x_ = x; |
| composited_y_ = y; |
| actor_->Move(x, y, anim_ms); |
| if (shadow_.get()) |
| shadow_->Move(x, y, anim_ms); |
| } |
| |
| void Window::MoveCompositedX(int x, int anim_ms) { |
| VLOG(2) << "Setting " << xid_str() << "'s composited window's X position to " |
| << x << " over " << anim_ms << " ms"; |
| composited_x_ = x; |
| actor_->MoveX(x, anim_ms); |
| if (shadow_.get()) |
| shadow_->MoveX(x, anim_ms); |
| } |
| |
| void Window::MoveCompositedY(int y, int anim_ms) { |
| VLOG(2) << "Setting " << xid_str() << "'s composited window's Y position to " |
| << y << " over " << anim_ms << " ms"; |
| composited_y_ = y; |
| actor_->MoveY(y, anim_ms); |
| if (shadow_.get()) |
| shadow_->MoveY(y, anim_ms); |
| } |
| |
| void Window::ShowComposited() { |
| VLOG(2) << "Showing " << xid_str() << "'s composited window"; |
| actor_->SetVisibility(true); |
| composited_shown_ = true; |
| if (shadow_.get() && using_shadow_) |
| shadow_->Show(); |
| } |
| |
| void Window::HideComposited() { |
| VLOG(2) << "Hiding " << xid_str() << "'s composited window"; |
| actor_->SetVisibility(false); |
| composited_shown_ = false; |
| if (shadow_.get() && using_shadow_) |
| shadow_->Hide(); |
| } |
| |
| void Window::SetCompositedOpacity(double opacity, int anim_ms) { |
| composited_opacity_ = opacity; |
| |
| // The client might've already requested that the window be translucent. |
| double combined_opacity = composited_opacity_ * client_opacity_; |
| |
| // Reset the shadow's opacity as well. |
| shadow_opacity_ = combined_opacity; |
| |
| VLOG(2) << "Setting " << xid_str() << "'s composited window opacity to " |
| << opacity << " (combined is " << combined_opacity << ") over " |
| << anim_ms << " ms"; |
| |
| actor_->SetOpacity(combined_opacity, anim_ms); |
| if (shadow_.get()) |
| shadow_->SetOpacity(shadow_opacity_, anim_ms); |
| } |
| |
| void Window::ScaleComposited(double scale_x, double scale_y, int anim_ms) { |
| VLOG(2) << "Scaling " << xid_str() << "'s composited window by (" |
| << scale_x << ", " << scale_y << ") over " << anim_ms << " ms"; |
| composited_scale_x_ = scale_x; |
| composited_scale_y_ = scale_y; |
| |
| actor_->Scale(scale_x, scale_y, anim_ms); |
| if (shadow_.get()) |
| shadow_->Resize(scale_x * client_width_, scale_y * client_height_, anim_ms); |
| } |
| |
| void Window::SetShadowOpacity(double opacity, int anim_ms) { |
| VLOG(2) << "Setting " << xid_str() << "'s shadow opacity to " << opacity |
| << " over " << anim_ms << " ms"; |
| shadow_opacity_ = opacity; |
| if (shadow_.get()) |
| shadow_->SetOpacity(opacity, anim_ms); |
| } |
| |
| void Window::StackCompositedAbove(ClutterInterface::Actor* actor, |
| ClutterInterface::Actor* shadow_actor, |
| bool stack_above_shadow_actor) { |
| if (actor) |
| actor_->Raise(actor); |
| if (shadow_.get()) { |
| if (!shadow_actor || !stack_above_shadow_actor) { |
| shadow_->group()->Lower(shadow_actor ? shadow_actor : actor_.get()); |
| } else { |
| shadow_->group()->Raise(shadow_actor); |
| } |
| } |
| } |
| |
| void Window::StackCompositedBelow(ClutterInterface::Actor* actor, |
| ClutterInterface::Actor* shadow_actor, |
| bool stack_above_shadow_actor) { |
| if (actor) |
| actor_->Lower(actor); |
| if (shadow_.get()) { |
| if (!shadow_actor || !stack_above_shadow_actor) { |
| shadow_->group()->Lower(shadow_actor ? shadow_actor : actor_.get()); |
| } else { |
| shadow_->group()->Raise(shadow_actor); |
| } |
| } |
| } |
| |
| void Window::UpdateShadowIfNecessary() { |
| if (!shadow_.get()) |
| return; |
| |
| bool should_use_shadow = |
| !override_redirect_ && |
| type_ != WmIpc::WINDOW_TYPE_CHROME_FLOATING_TAB && |
| type_ != WmIpc::WINDOW_TYPE_CHROME_INFO_BUBBLE && |
| type_ != WmIpc::WINDOW_TYPE_CHROME_TAB_SUMMARY && |
| type_ != WmIpc::WINDOW_TYPE_CREATE_BROWSER_WINDOW && |
| !shaped_; |
| |
| if (!should_use_shadow && using_shadow_) { |
| shadow_->Hide(); |
| using_shadow_ = false; |
| } else if (should_use_shadow && !using_shadow_) { |
| if (composited_shown_) |
| shadow_->Show(); |
| using_shadow_ = true; |
| } |
| } |
| |
| void Window::SetWmStateInternal(int action, bool* value) { |
| switch (action) { |
| case 0: // _NET_WM_STATE_REMOVE |
| *value = false; |
| break; |
| case 1: // _NET_WM_STATE_ADD |
| *value = true; |
| break; |
| case 2: // _NET_WM_STATE_TOGGLE |
| *value = !(*value); |
| break; |
| default: |
| LOG(WARNING) << "Got _NET_WM_STATE message for " << xid_str() |
| << " with invalid action " << action; |
| } |
| } |
| |
| bool Window::UpdateWmStateProperty() { |
| std::vector<int> values; |
| if (wm_state_fullscreen_) |
| values.push_back(wm_->GetXAtom(ATOM_NET_WM_STATE_FULLSCREEN)); |
| if (wm_state_maximized_horz_) |
| values.push_back(wm_->GetXAtom(ATOM_NET_WM_STATE_MAXIMIZED_HORZ)); |
| if (wm_state_maximized_vert_) |
| values.push_back(wm_->GetXAtom(ATOM_NET_WM_STATE_MAXIMIZED_VERT)); |
| if (wm_state_modal_) |
| values.push_back(wm_->GetXAtom(ATOM_NET_WM_STATE_MODAL)); |
| |
| VLOG(1) << "Updating _NET_WM_STATE for " << xid_str() << ":" |
| << " fullscreen=" << wm_state_fullscreen_ |
| << " maximized_horz=" << wm_state_maximized_horz_ |
| << " maximized_vert=" << wm_state_maximized_vert_ |
| << " modal=" << wm_state_modal_; |
| XAtom wm_state_atom = wm_->GetXAtom(ATOM_NET_WM_STATE); |
| if (!values.empty()) { |
| return wm_->xconn()->SetIntArrayProperty( |
| xid_, wm_state_atom, XA_ATOM, values); |
| } else { |
| return wm_->xconn()->DeletePropertyIfExists(xid_, wm_state_atom); |
| } |
| } |
| |
| bool Window::UpdateChromeStateProperty() { |
| std::vector<int> values; |
| for (std::set<XAtom>::const_iterator it = chrome_state_xatoms_.begin(); |
| it != chrome_state_xatoms_.end(); ++it) { |
| values.push_back(static_cast<int>(*it)); |
| } |
| |
| XAtom state_xatom = wm_->GetXAtom(ATOM_CHROME_STATE); |
| if (!values.empty()) { |
| return wm_->xconn()->SetIntArrayProperty( |
| xid_, state_xatom, XA_ATOM, values); |
| } else { |
| return wm_->xconn()->DeletePropertyIfExists(xid_, state_xatom); |
| } |
| } |
| |
| } // namespace chromeos |