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_menu_list.h"
     14 #include "ui/base/x/x11_util.h"
     15 #include "ui/events/platform/platform_event_source.h"
     16 #include "ui/gfx/x/x11_error_tracker.h"
     17 #include "ui/views/ime/input_method.h"
     18 #include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h"
     19 
     20 namespace {
     21 
     22 const char* kAtomsToCache[] = {
     23   "_NET_ACTIVE_WINDOW",
     24   NULL
     25 };
     26 
     27 // Our global instance. Deleted when our Env() is deleted.
     28 views::X11DesktopHandler* g_handler = NULL;
     29 
     30 }  // namespace
     31 
     32 namespace views {
     33 
     34 // static
     35 X11DesktopHandler* X11DesktopHandler::get() {
     36   if (!g_handler)
     37     g_handler = new X11DesktopHandler;
     38 
     39   return g_handler;
     40 }
     41 
     42 X11DesktopHandler::X11DesktopHandler()
     43     : xdisplay_(gfx::GetXDisplay()),
     44       x_root_window_(DefaultRootWindow(xdisplay_)),
     45       wm_user_time_ms_(0),
     46       current_window_(None),
     47       atom_cache_(xdisplay_, kAtomsToCache),
     48       wm_supports_active_window_(false) {
     49   if (ui::PlatformEventSource::GetInstance())
     50     ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this);
     51   aura::Env::GetInstance()->AddObserver(this);
     52 
     53   XWindowAttributes attr;
     54   XGetWindowAttributes(xdisplay_, x_root_window_, &attr);
     55   XSelectInput(xdisplay_, x_root_window_,
     56                attr.your_event_mask | PropertyChangeMask |
     57                StructureNotifyMask | SubstructureNotifyMask);
     58 
     59   ::Window active_window;
     60   wm_supports_active_window_ =
     61     ui::GetXIDProperty(x_root_window_, "_NET_ACTIVE_WINDOW", &active_window) &&
     62     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 (wm_supports_active_window_) {
     73     DCHECK_EQ(gfx::GetXDisplay(), xdisplay_);
     74 
     75     XEvent xclient;
     76     memset(&xclient, 0, sizeof(xclient));
     77     xclient.type = ClientMessage;
     78     xclient.xclient.window = window;
     79     xclient.xclient.message_type = atom_cache_.GetAtom("_NET_ACTIVE_WINDOW");
     80     xclient.xclient.format = 32;
     81     xclient.xclient.data.l[0] = 1;  // Specified we are an app.
     82     xclient.xclient.data.l[1] = wm_user_time_ms_;
     83     xclient.xclient.data.l[2] = None;
     84     xclient.xclient.data.l[3] = 0;
     85     xclient.xclient.data.l[4] = 0;
     86 
     87     XSendEvent(xdisplay_, x_root_window_, False,
     88                SubstructureRedirectMask | SubstructureNotifyMask,
     89                &xclient);
     90   } else {
     91     XRaiseWindow(xdisplay_, window);
     92 
     93     // XRaiseWindow will not give input focus to the window. We now need to ask
     94     // the X server to do that. Note that the call will raise an X error if the
     95     // window is not mapped.
     96     XSetInputFocus(xdisplay_, window, RevertToParent, CurrentTime);
     97 
     98     OnActiveWindowChanged(window);
     99   }
    100 }
    101 
    102 bool X11DesktopHandler::IsActiveWindow(::Window window) const {
    103   return window == current_window_;
    104 }
    105 
    106 void X11DesktopHandler::ProcessXEvent(XEvent* event) {
    107   switch (event->type) {
    108     case FocusIn:
    109       if (current_window_ != event->xfocus.window)
    110         OnActiveWindowChanged(event->xfocus.window);
    111       break;
    112     case FocusOut:
    113       if (current_window_ == event->xfocus.window)
    114         OnActiveWindowChanged(None);
    115       break;
    116     default:
    117       NOTREACHED();
    118   }
    119 }
    120 
    121 bool X11DesktopHandler::CanDispatchEvent(const ui::PlatformEvent& event) {
    122   return event->type == CreateNotify || event->type == DestroyNotify ||
    123          (event->type == PropertyNotify &&
    124           event->xproperty.window == x_root_window_);
    125 }
    126 
    127 uint32_t X11DesktopHandler::DispatchEvent(const ui::PlatformEvent& event) {
    128   switch (event->type) {
    129     case PropertyNotify: {
    130       // Check for a change to the active window.
    131       CHECK_EQ(x_root_window_, event->xproperty.window);
    132       ::Atom active_window_atom = atom_cache_.GetAtom("_NET_ACTIVE_WINDOW");
    133       if (event->xproperty.atom == active_window_atom) {
    134         ::Window window;
    135         if (ui::GetXIDProperty(x_root_window_, "_NET_ACTIVE_WINDOW", &window) &&
    136             window) {
    137           OnActiveWindowChanged(window);
    138         }
    139       }
    140       break;
    141     }
    142 
    143     // Menus created by Chrome can be drag and drop targets. Since they are
    144     // direct children of the screen root window and have override_redirect
    145     // we cannot use regular _NET_CLIENT_LIST_STACKING property to find them
    146     // and use a separate cache to keep track of them.
    147     // TODO(varkha): Implement caching of all top level X windows and their
    148     // coordinates and stacking order to eliminate repeated calls to X server
    149     // during mouse movement, drag and shaping events.
    150     case CreateNotify: {
    151       // The window might be destroyed if the message pump haven't gotten a
    152       // chance to run but we can safely ignore the X error.
    153       gfx::X11ErrorTracker error_tracker;
    154       XCreateWindowEvent *xcwe = &event->xcreatewindow;
    155       ui::XMenuList::GetInstance()->MaybeRegisterMenu(xcwe->window);
    156       break;
    157     }
    158     case DestroyNotify: {
    159       XDestroyWindowEvent *xdwe = &event->xdestroywindow;
    160       ui::XMenuList::GetInstance()->MaybeUnregisterMenu(xdwe->window);
    161       break;
    162     }
    163     default:
    164       NOTREACHED();
    165   }
    166 
    167   return ui::POST_DISPATCH_NONE;
    168 }
    169 
    170 void X11DesktopHandler::OnWindowInitialized(aura::Window* window) {
    171 }
    172 
    173 void X11DesktopHandler::OnWillDestroyEnv() {
    174   g_handler = NULL;
    175   delete this;
    176 }
    177 
    178 void X11DesktopHandler::OnActiveWindowChanged(::Window xid) {
    179   if (current_window_ == xid)
    180     return;
    181   DesktopWindowTreeHostX11* old_host =
    182       views::DesktopWindowTreeHostX11::GetHostForXID(current_window_);
    183   if (old_host)
    184     old_host->HandleNativeWidgetActivationChanged(false);
    185 
    186   // Update the current window ID to effectively change the active widget.
    187   current_window_ = xid;
    188 
    189   DesktopWindowTreeHostX11* new_host =
    190       views::DesktopWindowTreeHostX11::GetHostForXID(xid);
    191   if (new_host)
    192     new_host->HandleNativeWidgetActivationChanged(true);
    193 }
    194 
    195 }  // namespace views
    196