Home | History | Annotate | Download | only in desktop_aura
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "ui/views/widget/desktop_aura/x11_desktop_handler.h"
      6 
      7 #include <X11/Xatom.h>
      8 #include <X11/Xlib.h>
      9 
     10 #include "base/message_loop/message_loop.h"
     11 #include "ui/aura/env.h"
     12 #include "ui/aura/window_event_dispatcher.h"
     13 #include "ui/base/x/x11_foreign_window_manager.h"
     14 #include "ui/base/x/x11_menu_list.h"
     15 #include "ui/base/x/x11_util.h"
     16 #include "ui/events/platform/platform_event_source.h"
     17 #include "ui/gfx/x/x11_error_tracker.h"
     18 #include "ui/views/ime/input_method.h"
     19 #include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h"
     20 
     21 namespace {
     22 
     23 const char* kAtomsToCache[] = {
     24   "_NET_ACTIVE_WINDOW",
     25   NULL
     26 };
     27 
     28 // Our global instance. Deleted when our Env() is deleted.
     29 views::X11DesktopHandler* g_handler = NULL;
     30 
     31 }  // namespace
     32 
     33 namespace views {
     34 
     35 // static
     36 X11DesktopHandler* X11DesktopHandler::get() {
     37   if (!g_handler)
     38     g_handler = new X11DesktopHandler;
     39 
     40   return g_handler;
     41 }
     42 
     43 X11DesktopHandler::X11DesktopHandler()
     44     : xdisplay_(gfx::GetXDisplay()),
     45       x_root_window_(DefaultRootWindow(xdisplay_)),
     46       wm_user_time_ms_(0),
     47       current_window_(None),
     48       current_window_active_state_(NOT_ACTIVE),
     49       atom_cache_(xdisplay_, kAtomsToCache),
     50       wm_supports_active_window_(false) {
     51   if (ui::PlatformEventSource::GetInstance())
     52     ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this);
     53   aura::Env::GetInstance()->AddObserver(this);
     54 
     55   XWindowAttributes attr;
     56   XGetWindowAttributes(xdisplay_, x_root_window_, &attr);
     57   XSelectInput(xdisplay_, x_root_window_,
     58                attr.your_event_mask | PropertyChangeMask |
     59                StructureNotifyMask | SubstructureNotifyMask);
     60 
     61   wm_supports_active_window_ =
     62       ui::WmSupportsHint(atom_cache_.GetAtom("_NET_ACTIVE_WINDOW"));
     63 }
     64 
     65 X11DesktopHandler::~X11DesktopHandler() {
     66   aura::Env::GetInstance()->RemoveObserver(this);
     67   if (ui::PlatformEventSource::GetInstance())
     68     ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this);
     69 }
     70 
     71 void X11DesktopHandler::ActivateWindow(::Window window) {
     72   if (current_window_ == window &&
     73       current_window_active_state_ == NOT_ACTIVE) {
     74     // |window| is most likely still active wrt to the X server. Undo the
     75     // changes made in DeactivateWindow().
     76     OnActiveWindowChanged(window, ACTIVE);
     77 
     78     // Go through the regular activation path such that calling
     79     // DeactivateWindow() and ActivateWindow() immediately afterwards results
     80     // in an active X window.
     81   }
     82 
     83   XRaiseWindow(xdisplay_, window);
     84 
     85   if (wm_supports_active_window_) {
     86     DCHECK_EQ(gfx::GetXDisplay(), xdisplay_);
     87 
     88     XEvent xclient;
     89     memset(&xclient, 0, sizeof(xclient));
     90     xclient.type = ClientMessage;
     91     xclient.xclient.window = window;
     92     xclient.xclient.message_type = atom_cache_.GetAtom("_NET_ACTIVE_WINDOW");
     93     xclient.xclient.format = 32;
     94     xclient.xclient.data.l[0] = 1;  // Specified we are an app.
     95     xclient.xclient.data.l[1] = wm_user_time_ms_;
     96     xclient.xclient.data.l[2] = None;
     97     xclient.xclient.data.l[3] = 0;
     98     xclient.xclient.data.l[4] = 0;
     99 
    100     XSendEvent(xdisplay_, x_root_window_, False,
    101                SubstructureRedirectMask | SubstructureNotifyMask,
    102                &xclient);
    103   } else {
    104     // XRaiseWindow will not give input focus to the window. We now need to ask
    105     // the X server to do that. Note that the call will raise an X error if the
    106     // window is not mapped.
    107     XSetInputFocus(xdisplay_, window, RevertToParent, CurrentTime);
    108 
    109     OnActiveWindowChanged(window, ACTIVE);
    110   }
    111 }
    112 
    113 void X11DesktopHandler::DeactivateWindow(::Window window) {
    114   if (!IsActiveWindow(window))
    115     return;
    116 
    117   XLowerWindow(xdisplay_, window);
    118 
    119   // Per ICCCM: http://tronche.com/gui/x/icccm/sec-4.html#s-4.1.7
    120   // "Clients should not give up the input focus of their own volition.
    121   // They should ignore input that they receive instead."
    122   //
    123   // There is nothing else that we can do. Pretend that we have been
    124   // deactivated and ignore keyboard input in DesktopWindowTreeHostX11.
    125   OnActiveWindowChanged(window, NOT_ACTIVE);
    126 }
    127 
    128 bool X11DesktopHandler::IsActiveWindow(::Window window) const {
    129   return window == current_window_ && current_window_active_state_ == ACTIVE;
    130 }
    131 
    132 void X11DesktopHandler::ProcessXEvent(XEvent* event) {
    133   switch (event->type) {
    134     case FocusIn:
    135       if (current_window_ != event->xfocus.window)
    136         OnActiveWindowChanged(event->xfocus.window, ACTIVE);
    137       break;
    138     case FocusOut:
    139       if (current_window_ == event->xfocus.window)
    140         OnActiveWindowChanged(None, NOT_ACTIVE);
    141       break;
    142     default:
    143       NOTREACHED();
    144   }
    145 }
    146 
    147 bool X11DesktopHandler::CanDispatchEvent(const ui::PlatformEvent& event) {
    148   return event->type == CreateNotify || event->type == DestroyNotify ||
    149          (event->type == PropertyNotify &&
    150           event->xproperty.window == x_root_window_);
    151 }
    152 
    153 uint32_t X11DesktopHandler::DispatchEvent(const ui::PlatformEvent& event) {
    154   switch (event->type) {
    155     case PropertyNotify: {
    156       // Check for a change to the active window.
    157       CHECK_EQ(x_root_window_, event->xproperty.window);
    158       ::Atom active_window_atom = atom_cache_.GetAtom("_NET_ACTIVE_WINDOW");
    159       if (event->xproperty.atom == active_window_atom) {
    160         ::Window window;
    161         if (ui::GetXIDProperty(x_root_window_, "_NET_ACTIVE_WINDOW", &window) &&
    162             window) {
    163           OnActiveWindowChanged(window, ACTIVE);
    164         }
    165       }
    166       break;
    167     }
    168 
    169     case CreateNotify:
    170       OnWindowCreatedOrDestroyed(event->type, event->xcreatewindow.window);
    171       break;
    172     case DestroyNotify:
    173       OnWindowCreatedOrDestroyed(event->type, event->xdestroywindow.window);
    174       break;
    175     default:
    176       NOTREACHED();
    177   }
    178 
    179   return ui::POST_DISPATCH_NONE;
    180 }
    181 
    182 void X11DesktopHandler::OnWindowInitialized(aura::Window* window) {
    183 }
    184 
    185 void X11DesktopHandler::OnWillDestroyEnv() {
    186   g_handler = NULL;
    187   delete this;
    188 }
    189 
    190 void X11DesktopHandler::OnActiveWindowChanged(::Window xid,
    191                                               ActiveState active_state) {
    192   if (current_window_ == xid && current_window_active_state_ == active_state)
    193     return;
    194 
    195   if (current_window_active_state_ == ACTIVE) {
    196     DesktopWindowTreeHostX11* old_host =
    197         views::DesktopWindowTreeHostX11::GetHostForXID(current_window_);
    198     if (old_host)
    199       old_host->HandleNativeWidgetActivationChanged(false);
    200   }
    201 
    202   // Update the current window ID to effectively change the active widget.
    203   current_window_ = xid;
    204   current_window_active_state_ = active_state;
    205 
    206   if (active_state == ACTIVE) {
    207     DesktopWindowTreeHostX11* new_host =
    208         views::DesktopWindowTreeHostX11::GetHostForXID(xid);
    209     if (new_host)
    210       new_host->HandleNativeWidgetActivationChanged(true);
    211   }
    212 }
    213 
    214 void X11DesktopHandler::OnWindowCreatedOrDestroyed(int event_type,
    215                                                    XID window) {
    216   // Menus created by Chrome can be drag and drop targets. Since they are
    217   // direct children of the screen root window and have override_redirect
    218   // we cannot use regular _NET_CLIENT_LIST_STACKING property to find them
    219   // and use a separate cache to keep track of them.
    220   // TODO(varkha): Implement caching of all top level X windows and their
    221   // coordinates and stacking order to eliminate repeated calls to the X server
    222   // during mouse movement, drag and shaping events.
    223   if (event_type == CreateNotify) {
    224     // The window might be destroyed if the message pump did not get a chance to
    225     // run but we can safely ignore the X error.
    226     gfx::X11ErrorTracker error_tracker;
    227     ui::XMenuList::GetInstance()->MaybeRegisterMenu(window);
    228   } else {
    229     ui::XMenuList::GetInstance()->MaybeUnregisterMenu(window);
    230   }
    231 
    232   if (event_type == DestroyNotify) {
    233     // Notify the XForeignWindowManager that |window| has been destroyed.
    234     ui::XForeignWindowManager::GetInstance()->OnWindowDestroyed(window);
    235   }
    236 }
    237 
    238 }  // namespace views
    239