x11_desktop_handler.cc revision f2477e01787aa58f445919b809d89e252beef54f
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/root_window.h"
13#include "ui/base/x/x11_util.h"
14
15#if !defined(OS_CHROMEOS)
16#include "ui/views/ime/input_method.h"
17#include "ui/views/widget/desktop_aura/desktop_root_window_host_x11.h"
18#endif
19
20namespace {
21
22const char* kAtomsToCache[] = {
23  "_NET_ACTIVE_WINDOW",
24  "_NET_SUPPORTED",
25  NULL
26};
27
28// Our global instance. Deleted when our Env() is deleted.
29views::X11DesktopHandler* g_handler = NULL;
30
31}  // namespace
32
33namespace views {
34
35// static
36X11DesktopHandler* X11DesktopHandler::get() {
37  if (!g_handler)
38    g_handler = new X11DesktopHandler;
39
40  return g_handler;
41}
42
43X11DesktopHandler::X11DesktopHandler()
44    : xdisplay_(gfx::GetXDisplay()),
45      x_root_window_(DefaultRootWindow(xdisplay_)),
46      current_window_(None),
47      atom_cache_(xdisplay_, kAtomsToCache),
48      wm_supports_active_window_(false) {
49  base::MessagePumpX11::Current()->AddDispatcherForRootWindow(this);
50  aura::Env::GetInstance()->AddObserver(this);
51
52  XWindowAttributes attr;
53  XGetWindowAttributes(xdisplay_, x_root_window_, &attr);
54  XSelectInput(xdisplay_, x_root_window_,
55               attr.your_event_mask | PropertyChangeMask |
56               StructureNotifyMask | SubstructureNotifyMask);
57
58  std::vector<Atom> atoms;
59  if (ui::GetAtomArrayProperty(x_root_window_, "_NET_ACTIVE_WINDOW", &atoms)) {
60    Atom active_window = atom_cache_.GetAtom("_NET_ACTIVE_WINDOW");
61    for (std::vector<Atom>::iterator iter = atoms.begin(); iter != atoms.end();
62         ++iter) {
63      if (*(iter) == active_window) {
64        wm_supports_active_window_ = true;
65        break;
66      }
67    }
68  }
69}
70
71X11DesktopHandler::~X11DesktopHandler() {
72  aura::Env::GetInstance()->RemoveObserver(this);
73  base::MessagePumpX11::Current()->RemoveDispatcherForRootWindow(this);
74}
75
76void X11DesktopHandler::ActivateWindow(::Window window) {
77  if (wm_supports_active_window_) {
78    DCHECK_EQ(gfx::GetXDisplay(), xdisplay_);
79
80    XEvent xclient;
81    memset(&xclient, 0, sizeof(xclient));
82    xclient.type = ClientMessage;
83    xclient.xclient.window = window;
84    xclient.xclient.message_type = atom_cache_.GetAtom("_NET_ACTIVE_WINDOW");
85    xclient.xclient.format = 32;
86    xclient.xclient.data.l[0] = 1;  // Specified we are an app.
87    xclient.xclient.data.l[1] = CurrentTime;
88    xclient.xclient.data.l[2] = None;
89    xclient.xclient.data.l[3] = 0;
90    xclient.xclient.data.l[4] = 0;
91
92    XSendEvent(xdisplay_, x_root_window_, False,
93               SubstructureRedirectMask | SubstructureNotifyMask,
94               &xclient);
95  } else {
96    XRaiseWindow(xdisplay_, window);
97    OnActiveWindowChanged(window);
98  }
99}
100
101bool X11DesktopHandler::IsActiveWindow(::Window window) const {
102  return window == current_window_;
103}
104
105void X11DesktopHandler::ProcessXEvent(const base::NativeEvent& event) {
106  switch (event->type) {
107    case EnterNotify:
108      if (event->xcrossing.focus == True &&
109          current_window_ != event->xcrossing.window)
110        OnActiveWindowChanged(event->xcrossing.window);
111      break;
112    case LeaveNotify:
113      if (event->xcrossing.focus == False &&
114          current_window_ == event->xcrossing.window)
115        OnActiveWindowChanged(None);
116      break;
117    default:
118      NOTREACHED();
119  }
120}
121
122bool X11DesktopHandler::Dispatch(const base::NativeEvent& event) {
123  // Check for a change to the active window.
124  switch (event->type) {
125    case PropertyNotify: {
126      ::Atom active_window = atom_cache_.GetAtom("_NET_ACTIVE_WINDOW");
127
128      if (event->xproperty.window == x_root_window_ &&
129          event->xproperty.atom == active_window) {
130        int window;
131        if (ui::GetIntProperty(x_root_window_, "_NET_ACTIVE_WINDOW", &window) &&
132            window) {
133          OnActiveWindowChanged(static_cast< ::Window>(window));
134        }
135      }
136      break;
137    }
138  }
139
140  return true;
141}
142
143void X11DesktopHandler::OnWindowInitialized(aura::Window* window) {
144}
145
146void X11DesktopHandler::OnWillDestroyEnv() {
147  g_handler = NULL;
148  delete this;
149}
150
151void X11DesktopHandler::OnActiveWindowChanged(::Window xid) {
152  if (current_window_ == xid)
153    return;
154  DesktopRootWindowHostX11* old_host =
155      views::DesktopRootWindowHostX11::GetHostForXID(current_window_);
156  if (old_host)
157    old_host->HandleNativeWidgetActivationChanged(false);
158
159  DesktopRootWindowHostX11* new_host =
160      views::DesktopRootWindowHostX11::GetHostForXID(xid);
161  if (new_host)
162    new_host->HandleNativeWidgetActivationChanged(true);
163
164  current_window_ = xid;
165}
166
167}  // namespace views
168