1 // Copyright (c) 2011 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 "chrome/browser/chromeos/wm_ipc.h" 6 7 #include <gdk/gdkx.h> 8 extern "C" { 9 #include <X11/Xlib.h> 10 } 11 12 #include "base/lazy_instance.h" 13 #include "base/logging.h" 14 #include "base/memory/scoped_ptr.h" 15 #include "ui/base/x/x11_util.h" 16 17 namespace chromeos { 18 19 namespace { 20 21 // A value from the Atom enum and the actual name that should be used to 22 // look up its ID on the X server. 23 struct AtomInfo { 24 WmIpc::AtomType atom; 25 const char* name; 26 }; 27 28 // Each value from the Atom enum must be present here. 29 static const AtomInfo kAtomInfos[] = { 30 { WmIpc::ATOM_CHROME_LOGGED_IN, "_CHROME_LOGGED_IN" }, 31 { WmIpc::ATOM_CHROME_WINDOW_TYPE, "_CHROME_WINDOW_TYPE" }, 32 { WmIpc::ATOM_CHROME_WM_MESSAGE, "_CHROME_WM_MESSAGE" }, 33 { WmIpc::ATOM_MANAGER, "MANAGER" }, 34 { WmIpc::ATOM_STRING, "STRING" }, 35 { WmIpc::ATOM_UTF8_STRING, "UTF8_STRING" }, 36 { WmIpc::ATOM_WM_S0, "WM_S0" }, 37 }; 38 39 bool SetIntProperty(XID xid, Atom xatom, const std::vector<int>& values) { 40 DCHECK(!values.empty()); 41 42 // XChangeProperty expects values of type 32 to be longs. 43 scoped_array<long> data(new long[values.size()]); 44 for (size_t i = 0; i < values.size(); ++i) 45 data[i] = values[i]; 46 47 // TODO: Trap errors and return false on failure. 48 XChangeProperty(ui::GetXDisplay(), 49 xid, 50 xatom, 51 xatom, 52 32, // size in bits of items in 'value' 53 PropModeReplace, 54 reinterpret_cast<const unsigned char*>(data.get()), 55 values.size()); // num items 56 XFlush(ui::GetXDisplay()); 57 return true; 58 } 59 60 } // namespace 61 62 static base::LazyInstance<WmIpc> g_wm_ipc(base::LINKER_INITIALIZED); 63 64 // static 65 WmIpc* WmIpc::instance() { 66 return g_wm_ipc.Pointer(); 67 } 68 69 bool WmIpc::SetWindowType(GtkWidget* widget, 70 WmIpcWindowType type, 71 const std::vector<int>* params) { 72 std::vector<int> values; 73 values.push_back(type); 74 if (params) 75 values.insert(values.end(), params->begin(), params->end()); 76 return SetIntProperty(ui::GetX11WindowFromGtkWidget(widget), 77 type_to_atom_[ATOM_CHROME_WINDOW_TYPE], values); 78 } 79 80 WmIpcWindowType WmIpc::GetWindowType(GtkWidget* widget, 81 std::vector<int>* params) { 82 std::vector<int> properties; 83 if (ui::GetIntArrayProperty( 84 ui::GetX11WindowFromGtkWidget(widget), 85 atom_to_string_[type_to_atom_[ATOM_CHROME_WINDOW_TYPE]], 86 &properties)) { 87 int type = properties.front(); 88 if (params) { 89 params->clear(); 90 params->insert(params->begin(), properties.begin() + 1, properties.end()); 91 } 92 return static_cast<WmIpcWindowType>(type); 93 } else { 94 return WM_IPC_WINDOW_UNKNOWN; 95 } 96 } 97 98 void WmIpc::SendMessage(const Message& msg) { 99 XEvent e; 100 e.xclient.type = ClientMessage; 101 e.xclient.window = wm_; 102 e.xclient.message_type = type_to_atom_[ATOM_CHROME_WM_MESSAGE]; 103 e.xclient.format = 32; // 32-bit values 104 e.xclient.data.l[0] = msg.type(); 105 106 // XClientMessageEvent only gives us five 32-bit items, and we're using 107 // the first one for our message type. 108 DCHECK_LE(msg.max_params(), 4); 109 for (int i = 0; i < msg.max_params(); ++i) 110 e.xclient.data.l[i+1] = msg.param(i); 111 112 XSendEvent(ui::GetXDisplay(), 113 wm_, 114 False, // propagate 115 0, // empty event mask 116 &e); 117 } 118 119 bool WmIpc::DecodeMessage(const GdkEventClient& event, Message* msg) { 120 if (wm_message_atom_ != gdk_x11_atom_to_xatom(event.message_type)) 121 return false; 122 123 if (event.data_format != 32) { 124 DLOG(WARNING) << "Ignoring ClientEventMessage with invalid bit " 125 << "format " << event.data_format 126 << " (expected 32-bit values)"; 127 return false; 128 } 129 130 msg->set_type(static_cast<WmIpcMessageType>(event.data.l[0])); 131 if (msg->type() < 0) { 132 DLOG(WARNING) << "Ignoring ClientEventMessage with invalid message " 133 << "type " << msg->type(); 134 return false; 135 } 136 137 // XClientMessageEvent only gives us five 32-bit items, and we're using 138 // the first one for our message type. 139 DCHECK_LE(msg->max_params(), 4); 140 for (int i = 0; i < msg->max_params(); ++i) 141 msg->set_param(i, event.data.l[i+1]); // l[0] contains message type 142 143 return true; 144 } 145 146 void WmIpc::HandleNonChromeClientMessageEvent(const GdkEventClient& event) { 147 // Only do these lookups once; they should never change. 148 static GdkAtom manager_gdk_atom = 149 gdk_x11_xatom_to_atom(type_to_atom_[ATOM_MANAGER]); 150 static Atom wm_s0_atom = type_to_atom_[ATOM_WM_S0]; 151 152 if (event.message_type == manager_gdk_atom && 153 static_cast<Atom>(event.data.l[1]) == wm_s0_atom) { 154 InitWmInfo(); 155 } 156 } 157 158 void WmIpc::SetLoggedInProperty(bool logged_in) { 159 std::vector<int> values; 160 values.push_back(static_cast<int>(logged_in)); 161 SetIntProperty(gdk_x11_get_default_root_xwindow(), 162 type_to_atom_[ATOM_CHROME_LOGGED_IN], 163 values); 164 } 165 166 void WmIpc::NotifyAboutSignout() { 167 Message msg(chromeos::WM_IPC_MESSAGE_WM_NOTIFY_SIGNING_OUT); 168 SendMessage(msg); 169 XFlush(ui::GetXDisplay()); 170 } 171 172 WmIpc::WmIpc() { 173 scoped_array<char*> names(new char*[kNumAtoms]); 174 scoped_array<Atom> atoms(new Atom[kNumAtoms]); 175 176 for (int i = 0; i < kNumAtoms; ++i) { 177 // Need to const_cast because XInternAtoms() wants a char**. 178 names[i] = const_cast<char*>(kAtomInfos[i].name); 179 } 180 181 XInternAtoms(ui::GetXDisplay(), names.get(), kNumAtoms, 182 False, // only_if_exists 183 atoms.get()); 184 185 for (int i = 0; i < kNumAtoms; ++i) { 186 type_to_atom_[kAtomInfos[i].atom] = atoms[i]; 187 atom_to_string_[atoms[i]] = kAtomInfos[i].name; 188 } 189 190 wm_message_atom_ = type_to_atom_[ATOM_CHROME_WM_MESSAGE]; 191 192 // Make sure that we're selecting structure changes on the root window; 193 // the window manager uses StructureNotifyMask when sending the ClientMessage 194 // event to announce that it's taken the manager selection. 195 GdkWindow* root = gdk_get_default_root_window(); 196 GdkEventMask event_mask = gdk_window_get_events(root); 197 gdk_window_set_events( 198 root, static_cast<GdkEventMask>(event_mask | GDK_STRUCTURE_MASK)); 199 200 InitWmInfo(); 201 } 202 203 void WmIpc::InitWmInfo() { 204 wm_ = XGetSelectionOwner(ui::GetXDisplay(), type_to_atom_[ATOM_WM_S0]); 205 206 // Let the window manager know which version of the IPC messages we support. 207 Message msg(chromeos::WM_IPC_MESSAGE_WM_NOTIFY_IPC_VERSION); 208 // TODO: The version number is the latest listed in wm_ipc.h -- 209 // ideally, once this header is shared between Chrome and the Chrome OS window 210 // manager, we'll just define the version statically in the header. 211 msg.set_param(0, 1); 212 SendMessage(msg); 213 } 214 215 } // namespace chromeos 216