OLD | NEW |
| (Empty) |
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_root_window_host_linux.h" | |
6 | |
7 #include <X11/extensions/XInput2.h> | |
8 #include <X11/Xatom.h> | |
9 #include <X11/Xutil.h> | |
10 | |
11 #include "base/message_pump_aurax11.h" | |
12 #include "base/stringprintf.h" | |
13 #include "base/utf_string_conversions.h" | |
14 #include "ui/aura/client/default_capture_client.h" | |
15 #include "ui/aura/client/screen_position_client.h" | |
16 #include "ui/aura/client/user_action_client.h" | |
17 #include "ui/aura/focus_manager.h" | |
18 #include "ui/aura/root_window.h" | |
19 #include "ui/views/corewm/compound_event_filter.h" | |
20 #include "ui/views/corewm/input_method_event_filter.h" | |
21 #include "ui/aura/window_property.h" | |
22 #include "ui/base/events/event_utils.h" | |
23 #include "ui/base/native_theme/native_theme.h" | |
24 #include "ui/base/touch/touch_factory.h" | |
25 #include "ui/base/x/x11_util.h" | |
26 #include "ui/views/ime/input_method_bridge.h" | |
27 #include "ui/views/widget/desktop_aura/desktop_activation_client.h" | |
28 #include "ui/views/widget/desktop_aura/desktop_cursor_client.h" | |
29 #include "ui/views/widget/desktop_aura/desktop_dispatcher_client.h" | |
30 #include "ui/views/widget/desktop_layout_manager.h" | |
31 #include "ui/views/widget/desktop_native_widget_aura.h" | |
32 #include "ui/views/widget/desktop_screen_position_client.h" | |
33 #include "ui/views/widget/x11_desktop_handler.h" | |
34 #include "ui/views/widget/x11_desktop_window_move_client.h" | |
35 #include "ui/views/widget/x11_window_event_filter.h" | |
36 | |
37 namespace views { | |
38 | |
39 DesktopRootWindowHostLinux* DesktopRootWindowHostLinux::g_current_capture = | |
40 NULL; | |
41 | |
42 DEFINE_WINDOW_PROPERTY_KEY( | |
43 aura::Window*, kViewsWindowForRootWindow, NULL); | |
44 | |
45 DEFINE_WINDOW_PROPERTY_KEY( | |
46 DesktopRootWindowHostLinux*, kHostForRootWindow, NULL); | |
47 | |
48 namespace { | |
49 | |
50 // Standard Linux mouse buttons for going back and forward. | |
51 const int kBackMouseButton = 8; | |
52 const int kForwardMouseButton = 9; | |
53 | |
54 // Constants that are part of EWMH. | |
55 const int k_NET_WM_STATE_ADD = 1; | |
56 const int k_NET_WM_STATE_REMOVE = 0; | |
57 | |
58 const char* kAtomsToCache[] = { | |
59 "WM_DELETE_WINDOW", | |
60 "WM_S0", | |
61 "_NET_WM_PID", | |
62 "_NET_WM_PING", | |
63 "_NET_WM_STATE", | |
64 "_NET_WM_STATE_FULLSCREEN", | |
65 "_NET_WM_STATE_HIDDEN", | |
66 "_NET_WM_STATE_MAXIMIZED_HORZ", | |
67 "_NET_WM_STATE_MAXIMIZED_VERT", | |
68 NULL | |
69 }; | |
70 | |
71 } // namespace | |
72 | |
73 //////////////////////////////////////////////////////////////////////////////// | |
74 // DesktopRootWindowHostLinux, public: | |
75 | |
76 DesktopRootWindowHostLinux::DesktopRootWindowHostLinux( | |
77 internal::NativeWidgetDelegate* native_widget_delegate, | |
78 DesktopNativeWidgetAura* desktop_native_widget_aura, | |
79 const gfx::Rect& initial_bounds) | |
80 : close_widget_factory_(this), | |
81 xdisplay_(base::MessagePumpAuraX11::GetDefaultXDisplay()), | |
82 xwindow_(0), | |
83 x_root_window_(DefaultRootWindow(xdisplay_)), | |
84 atom_cache_(xdisplay_, kAtomsToCache), | |
85 window_mapped_(false), | |
86 focus_when_shown_(false), | |
87 current_cursor_(ui::kCursorNull), | |
88 native_widget_delegate_(native_widget_delegate), | |
89 desktop_native_widget_aura_(desktop_native_widget_aura) { | |
90 } | |
91 | |
92 DesktopRootWindowHostLinux::~DesktopRootWindowHostLinux() { | |
93 root_window_->ClearProperty(kHostForRootWindow); | |
94 } | |
95 | |
96 // static | |
97 ui::NativeTheme* DesktopRootWindowHost::GetNativeTheme(aura::Window* window) { | |
98 return ui::NativeTheme::instance(); | |
99 } | |
100 | |
101 //////////////////////////////////////////////////////////////////////////////// | |
102 // DesktopRootWindowHostLinux, private: | |
103 | |
104 void DesktopRootWindowHostLinux::InitX11Window( | |
105 const Widget::InitParams& params) { | |
106 unsigned long attribute_mask = CWBackPixmap; | |
107 XSetWindowAttributes swa; | |
108 memset(&swa, 0, sizeof(swa)); | |
109 swa.background_pixmap = None; | |
110 | |
111 if (params.type == Widget::InitParams::TYPE_MENU) { | |
112 swa.override_redirect = True; | |
113 attribute_mask |= CWOverrideRedirect; | |
114 } | |
115 | |
116 xwindow_ = XCreateWindow( | |
117 xdisplay_, x_root_window_, | |
118 params.bounds.x(), params.bounds.y(), | |
119 params.bounds.width(), params.bounds.height(), | |
120 0, // border width | |
121 CopyFromParent, // depth | |
122 InputOutput, | |
123 CopyFromParent, // visual | |
124 attribute_mask, | |
125 &swa); | |
126 base::MessagePumpAuraX11::Current()->AddDispatcherForWindow(this, xwindow_); | |
127 | |
128 // TODO(erg): Maybe need to set a ViewProp here like in RWHL::RWHL(). | |
129 | |
130 long event_mask = ButtonPressMask | ButtonReleaseMask | FocusChangeMask | | |
131 KeyPressMask | KeyReleaseMask | | |
132 EnterWindowMask | LeaveWindowMask | | |
133 ExposureMask | VisibilityChangeMask | | |
134 StructureNotifyMask | PropertyChangeMask | | |
135 PointerMotionMask; | |
136 XSelectInput(xdisplay_, xwindow_, event_mask); | |
137 XFlush(xdisplay_); | |
138 | |
139 if (base::MessagePumpForUI::HasXInput2()) | |
140 ui::TouchFactory::GetInstance()->SetupXI2ForXWindow(xwindow_); | |
141 | |
142 invisible_cursor_ = ui::CreateInvisibleCursor(); | |
143 | |
144 // TODO(erg): We currently only request window deletion events. We also | |
145 // should listen for activation events and anything else that GTK+ listens | |
146 // for, and do something useful. | |
147 ::Atom protocols[2]; | |
148 protocols[0] = atom_cache_.GetAtom("WM_DELETE_WINDOW"); | |
149 protocols[1] = atom_cache_.GetAtom("_NET_WM_PING"); | |
150 XSetWMProtocols(xdisplay_, xwindow_, protocols, 2); | |
151 | |
152 // We need a WM_CLIENT_MACHINE and WM_LOCALE_NAME value so we integrate with | |
153 // the desktop environment. | |
154 XSetWMProperties(xdisplay_, xwindow_, NULL, NULL, NULL, 0, NULL, NULL, NULL); | |
155 | |
156 // Likewise, the X server needs to know this window's pid so it knows which | |
157 // program to kill if the window hangs. | |
158 pid_t pid = getpid(); | |
159 XChangeProperty(xdisplay_, | |
160 xwindow_, | |
161 atom_cache_.GetAtom("_NET_WM_PID"), | |
162 XA_CARDINAL, | |
163 32, | |
164 PropModeReplace, | |
165 reinterpret_cast<unsigned char*>(&pid), 1); | |
166 } | |
167 | |
168 // static | |
169 aura::Window* DesktopRootWindowHostLinux::GetContentWindowForXID(XID xid) { | |
170 aura::RootWindow* root = aura::RootWindow::GetForAcceleratedWidget(xid); | |
171 return root ? root->GetProperty(kViewsWindowForRootWindow) : NULL; | |
172 } | |
173 | |
174 // static | |
175 DesktopRootWindowHostLinux* DesktopRootWindowHostLinux::GetHostForXID(XID xid) { | |
176 aura::RootWindow* root = aura::RootWindow::GetForAcceleratedWidget(xid); | |
177 return root ? root->GetProperty(kHostForRootWindow) : NULL; | |
178 } | |
179 | |
180 void DesktopRootWindowHostLinux::HandleNativeWidgetActivationChanged( | |
181 bool active) { | |
182 native_widget_delegate_->OnNativeWidgetActivationChanged(active); | |
183 native_widget_delegate_->AsWidget()->GetRootView()->SchedulePaint(); | |
184 } | |
185 | |
186 // TODO(erg): This method should basically be everything I need form | |
187 // RootWindowHostLinux::RootWindowHostLinux(). | |
188 aura::RootWindow* DesktopRootWindowHostLinux::InitRootWindow( | |
189 const Widget::InitParams& params) { | |
190 bounds_ = params.bounds; | |
191 | |
192 aura::RootWindow::CreateParams rw_params(bounds_); | |
193 rw_params.host = this; | |
194 root_window_ = new aura::RootWindow(rw_params); | |
195 root_window_->Init(); | |
196 root_window_->AddChild(content_window_); | |
197 root_window_->SetLayoutManager(new DesktopLayoutManager(root_window_)); | |
198 root_window_->SetProperty(kViewsWindowForRootWindow, content_window_); | |
199 root_window_->SetProperty(kHostForRootWindow, this); | |
200 root_window_host_delegate_ = root_window_; | |
201 | |
202 // If we're given a parent, we need to mark ourselves as transient to another | |
203 // window. Otherwise activation gets screwy. | |
204 gfx::NativeView parent = params.GetParent(); | |
205 if (!params.child && params.GetParent()) | |
206 parent->AddTransientChild(content_window_); | |
207 | |
208 native_widget_delegate_->OnNativeWidgetCreated(); | |
209 | |
210 capture_client_.reset(new aura::client::DefaultCaptureClient(root_window_)); | |
211 aura::client::SetCaptureClient(root_window_, capture_client_.get()); | |
212 | |
213 // Ensure that the X11DesktopHandler exists so that it dispatches activation | |
214 // messages to us. | |
215 X11DesktopHandler::get(); | |
216 | |
217 focus_manager_.reset(new aura::FocusManager); | |
218 root_window_->set_focus_manager(focus_manager_.get()); | |
219 | |
220 activation_client_.reset( | |
221 new DesktopActivationClient(root_window_->GetFocusManager())); | |
222 aura::client::SetActivationClient(root_window_, | |
223 activation_client_.get()); | |
224 | |
225 dispatcher_client_.reset(new DesktopDispatcherClient); | |
226 aura::client::SetDispatcherClient(root_window_, | |
227 dispatcher_client_.get()); | |
228 | |
229 cursor_client_.reset(new DesktopCursorClient(root_window_)); | |
230 aura::client::SetCursorClient(root_window_, | |
231 cursor_client_.get()); | |
232 | |
233 position_client_.reset(new DesktopScreenPositionClient); | |
234 aura::client::SetScreenPositionClient(root_window_, | |
235 position_client_.get()); | |
236 | |
237 // No event filter for aura::Env. Create CompoundEvnetFilter per RootWindow. | |
238 root_window_event_filter_ = new corewm::CompoundEventFilter; | |
239 // Pass ownership of the filter to the root_window. | |
240 root_window_->SetEventFilter(root_window_event_filter_); | |
241 | |
242 input_method_filter_.reset(new corewm::InputMethodEventFilter); | |
243 input_method_filter_->SetInputMethodPropertyInRootWindow(root_window_); | |
244 root_window_event_filter_->AddFilter(input_method_filter_.get()); | |
245 | |
246 // TODO(erg): Unify this code once the other consumer goes away. | |
247 x11_window_event_filter_.reset( | |
248 new X11WindowEventFilter(root_window_, activation_client_.get())); | |
249 x11_window_event_filter_->SetUseHostWindowBorders(false); | |
250 root_window_event_filter_->AddFilter(x11_window_event_filter_.get()); | |
251 | |
252 x11_window_move_client_.reset(new X11DesktopWindowMoveClient); | |
253 aura::client::SetWindowMoveClient(root_window_, | |
254 x11_window_move_client_.get()); | |
255 | |
256 focus_manager_->SetFocusedWindow(content_window_, NULL); | |
257 return root_window_; | |
258 } | |
259 | |
260 bool DesktopRootWindowHostLinux::IsWindowManagerPresent() { | |
261 // Per ICCCM 2.8, "Manager Selections", window managers should take ownership | |
262 // of WM_Sn selections (where n is a screen number). | |
263 return XGetSelectionOwner( | |
264 xdisplay_, atom_cache_.GetAtom("WM_S0")) != None; | |
265 } | |
266 | |
267 void DesktopRootWindowHostLinux::SetWMSpecState(bool enabled, | |
268 ::Atom state1, | |
269 ::Atom state2) { | |
270 XEvent xclient; | |
271 memset(&xclient, 0, sizeof(xclient)); | |
272 xclient.type = ClientMessage; | |
273 xclient.xclient.window = xwindow_; | |
274 xclient.xclient.message_type = atom_cache_.GetAtom("_NET_WM_STATE"); | |
275 xclient.xclient.format = 32; | |
276 xclient.xclient.data.l[0] = | |
277 enabled ? k_NET_WM_STATE_ADD : k_NET_WM_STATE_REMOVE; | |
278 xclient.xclient.data.l[1] = state1; | |
279 xclient.xclient.data.l[2] = state2; | |
280 xclient.xclient.data.l[3] = 1; | |
281 xclient.xclient.data.l[4] = 0; | |
282 | |
283 XSendEvent(xdisplay_, x_root_window_, False, | |
284 SubstructureRedirectMask | SubstructureNotifyMask, | |
285 &xclient); | |
286 } | |
287 | |
288 bool DesktopRootWindowHostLinux::HasWMSpecProperty(const char* property) const { | |
289 return window_properties_.find(atom_cache_.GetAtom(property)) != | |
290 window_properties_.end(); | |
291 } | |
292 | |
293 //////////////////////////////////////////////////////////////////////////////// | |
294 // DesktopRootWindowHostLinux, DesktopRootWindowHost implementation: | |
295 | |
296 aura::RootWindow* DesktopRootWindowHostLinux::Init( | |
297 aura::Window* content_window, | |
298 const Widget::InitParams& params) { | |
299 content_window_ = content_window; | |
300 | |
301 // TODO(erg): Check whether we *should* be building a RootWindowHost here, or | |
302 // whether we should be proxying requests to another DRWHL. | |
303 | |
304 // In some situations, views tries to make a zero sized window, and that | |
305 // makes us crash. Make sure we have valid sizes. | |
306 Widget::InitParams sanitized_params = params; | |
307 if (sanitized_params.bounds.width() == 0) | |
308 sanitized_params.bounds.set_width(100); | |
309 if (sanitized_params.bounds.height() == 0) | |
310 sanitized_params.bounds.set_height(100); | |
311 | |
312 InitX11Window(sanitized_params); | |
313 return InitRootWindow(sanitized_params); | |
314 } | |
315 | |
316 void DesktopRootWindowHostLinux::Close() { | |
317 // TODO(erg): Might need to do additional hiding tasks here. | |
318 | |
319 if (!close_widget_factory_.HasWeakPtrs()) { | |
320 // And we delay the close so that if we are called from an ATL callback, | |
321 // we don't destroy the window before the callback returned (as the caller | |
322 // may delete ourselves on destroy and the ATL callback would still | |
323 // dereference us when the callback returns). | |
324 MessageLoop::current()->PostTask( | |
325 FROM_HERE, | |
326 base::Bind(&DesktopRootWindowHostLinux::CloseNow, | |
327 close_widget_factory_.GetWeakPtr())); | |
328 } | |
329 } | |
330 | |
331 void DesktopRootWindowHostLinux::CloseNow() { | |
332 // Remove the event listeners we've installed. We need to remove these | |
333 // because otherwise we get assert during ~RootWindow(). | |
334 root_window_event_filter_->RemoveFilter(x11_window_event_filter_.get()); | |
335 root_window_event_filter_->RemoveFilter(input_method_filter_.get()); | |
336 | |
337 // Actually free our native resources. | |
338 base::MessagePumpAuraX11::Current()->RemoveDispatcherForWindow(xwindow_); | |
339 XDestroyWindow(xdisplay_, xwindow_); | |
340 xwindow_ = None; | |
341 | |
342 desktop_native_widget_aura_->OnHostClosed(); | |
343 } | |
344 | |
345 aura::RootWindowHost* DesktopRootWindowHostLinux::AsRootWindowHost() { | |
346 return this; | |
347 } | |
348 | |
349 void DesktopRootWindowHostLinux::ShowWindowWithState( | |
350 ui::WindowShowState show_state) { | |
351 if (show_state != ui::SHOW_STATE_DEFAULT && | |
352 show_state != ui::SHOW_STATE_NORMAL) { | |
353 // Only forwarding to Show(). | |
354 NOTIMPLEMENTED(); | |
355 } | |
356 | |
357 Show(); | |
358 } | |
359 | |
360 void DesktopRootWindowHostLinux::ShowMaximizedWithBounds( | |
361 const gfx::Rect& restored_bounds) { | |
362 // TODO(erg): | |
363 NOTIMPLEMENTED(); | |
364 | |
365 // TODO(erg): We shouldn't completely fall down here. | |
366 Show(); | |
367 } | |
368 | |
369 bool DesktopRootWindowHostLinux::IsVisible() const { | |
370 return window_mapped_; | |
371 } | |
372 | |
373 void DesktopRootWindowHostLinux::SetSize(const gfx::Size& size) { | |
374 // TODO(erg): | |
375 NOTIMPLEMENTED(); | |
376 } | |
377 | |
378 void DesktopRootWindowHostLinux::CenterWindow(const gfx::Size& size) { | |
379 gfx::Rect parent_bounds = GetWorkAreaBoundsInScreen(); | |
380 | |
381 // If |window_|'s transient parent bounds are big enough to contain |size|, | |
382 // use them instead. | |
383 if (content_window_->transient_parent()) { | |
384 gfx::Rect transient_parent_rect = | |
385 content_window_->transient_parent()->GetBoundsInScreen(); | |
386 if (transient_parent_rect.height() >= size.height() && | |
387 transient_parent_rect.width() >= size.width()) { | |
388 parent_bounds = transient_parent_rect; | |
389 } | |
390 } | |
391 | |
392 gfx::Rect window_bounds( | |
393 parent_bounds.x() + (parent_bounds.width() - size.width()) / 2, | |
394 parent_bounds.y() + (parent_bounds.height() - size.height()) / 2, | |
395 size.width(), | |
396 size.height()); | |
397 // Don't size the window bigger than the parent, otherwise the user may not be | |
398 // able to close or move it. | |
399 window_bounds.AdjustToFit(parent_bounds); | |
400 | |
401 SetBounds(window_bounds); | |
402 } | |
403 | |
404 void DesktopRootWindowHostLinux::GetWindowPlacement( | |
405 gfx::Rect* bounds, | |
406 ui::WindowShowState* show_state) const { | |
407 *bounds = bounds_; | |
408 | |
409 // TODO(erg): This needs a better implementation. For now, we're just pass | |
410 // back the normal state until we keep track of this. | |
411 *show_state = ui::SHOW_STATE_NORMAL; | |
412 } | |
413 | |
414 gfx::Rect DesktopRootWindowHostLinux::GetWindowBoundsInScreen() const { | |
415 return bounds_; | |
416 } | |
417 | |
418 gfx::Rect DesktopRootWindowHostLinux::GetClientAreaBoundsInScreen() const { | |
419 // TODO(erg): The NativeWidgetAura version returns |bounds_|, claiming its | |
420 // needed for View::ConvertPointToScreen() to work | |
421 // correctly. DesktopRootWindowHostWin::GetClientAreaBoundsInScreen() just | |
422 // asks windows what it thinks the client rect is. | |
423 // | |
424 // Attempts to calculate the rect by asking the NonClientFrameView what it | |
425 // thought its GetBoundsForClientView() were broke combobox drop down | |
426 // placement. | |
427 return bounds_; | |
428 } | |
429 | |
430 gfx::Rect DesktopRootWindowHostLinux::GetRestoredBounds() const { | |
431 // TODO(erg): | |
432 NOTIMPLEMENTED(); | |
433 return gfx::Rect(); | |
434 } | |
435 | |
436 gfx::Rect DesktopRootWindowHostLinux::GetWorkAreaBoundsInScreen() const { | |
437 std::vector<int> value; | |
438 if (ui::GetIntArrayProperty(x_root_window_, "_NET_WORKAREA", &value) && | |
439 value.size() >= 4) { | |
440 return gfx::Rect(value[0], value[1], value[2], value[3]); | |
441 } | |
442 | |
443 // TODO(erg): As a fallback, we should return the bounds for the current | |
444 // monitor. However, that's pretty difficult and requires futzing with XRR. | |
445 NOTIMPLEMENTED(); | |
446 return gfx::Rect(); | |
447 } | |
448 | |
449 void DesktopRootWindowHostLinux::SetShape(gfx::NativeRegion native_region) { | |
450 // TODO(erg): | |
451 NOTIMPLEMENTED(); | |
452 } | |
453 | |
454 void DesktopRootWindowHostLinux::Activate() { | |
455 X11DesktopHandler::get()->ActivateWindow(xwindow_); | |
456 } | |
457 | |
458 void DesktopRootWindowHostLinux::Deactivate() { | |
459 // Deactivating a window means activating nothing. | |
460 X11DesktopHandler::get()->ActivateWindow(None); | |
461 } | |
462 | |
463 bool DesktopRootWindowHostLinux::IsActive() const { | |
464 return X11DesktopHandler::get()->IsActiveWindow(xwindow_); | |
465 } | |
466 | |
467 void DesktopRootWindowHostLinux::Maximize() { | |
468 SetWMSpecState(true, | |
469 atom_cache_.GetAtom("_NET_WM_STATE_MAXIMIZED_VERT"), | |
470 atom_cache_.GetAtom("_NET_WM_STATE_MAXIMIZED_HORZ")); | |
471 } | |
472 | |
473 void DesktopRootWindowHostLinux::Minimize() { | |
474 XIconifyWindow(xdisplay_, xwindow_, 0); | |
475 } | |
476 | |
477 void DesktopRootWindowHostLinux::Restore() { | |
478 SetWMSpecState(false, | |
479 atom_cache_.GetAtom("_NET_WM_STATE_MAXIMIZED_VERT"), | |
480 atom_cache_.GetAtom("_NET_WM_STATE_MAXIMIZED_HORZ")); | |
481 } | |
482 | |
483 bool DesktopRootWindowHostLinux::IsMaximized() const { | |
484 return (HasWMSpecProperty("_NET_WM_STATE_MAXIMIZED_VERT") || | |
485 HasWMSpecProperty("_NET_WM_STATE_MAXIMIZED_HORZ")); | |
486 } | |
487 | |
488 bool DesktopRootWindowHostLinux::IsMinimized() const { | |
489 return HasWMSpecProperty("_NET_WM_STATE_HIDDEN"); | |
490 } | |
491 | |
492 void DesktopRootWindowHostLinux::OnCaptureReleased() { | |
493 native_widget_delegate_->OnMouseCaptureLost(); | |
494 g_current_capture = NULL; | |
495 } | |
496 | |
497 void DesktopRootWindowHostLinux::DispatchMouseEvent(ui::MouseEvent* event) { | |
498 if (!g_current_capture || g_current_capture == this) { | |
499 root_window_host_delegate_->OnHostMouseEvent(event); | |
500 } else { | |
501 // Another DesktopRootWindowHostLinux has installed itself as | |
502 // capture. Translate the event's location and dispatch to the other. | |
503 event->ConvertLocationToTarget(root_window_, | |
504 g_current_capture->root_window_); | |
505 g_current_capture->root_window_host_delegate_->OnHostMouseEvent(event); | |
506 } | |
507 } | |
508 | |
509 bool DesktopRootWindowHostLinux::HasCapture() const { | |
510 return g_current_capture == this; | |
511 } | |
512 | |
513 void DesktopRootWindowHostLinux::SetAlwaysOnTop(bool always_on_top) { | |
514 // TODO(erg): | |
515 NOTIMPLEMENTED(); | |
516 } | |
517 | |
518 InputMethod* DesktopRootWindowHostLinux::CreateInputMethod() { | |
519 ui::InputMethod* host = input_method_filter_->input_method(); | |
520 return new InputMethodBridge(this, host); | |
521 } | |
522 | |
523 internal::InputMethodDelegate* | |
524 DesktopRootWindowHostLinux::GetInputMethodDelegate() { | |
525 return this; | |
526 } | |
527 | |
528 void DesktopRootWindowHostLinux::SetWindowTitle(const string16& title) { | |
529 XStoreName(xdisplay_, xwindow_, UTF16ToUTF8(title).c_str()); | |
530 } | |
531 | |
532 void DesktopRootWindowHostLinux::ClearNativeFocus() { | |
533 // This method is weird and misnamed. Instead of clearing the native focus, | |
534 // it sets the focus to our |content_window_|, which will trigger a cascade | |
535 // of focus changes into views. | |
536 if (content_window_ && content_window_->GetFocusManager() && | |
537 content_window_->Contains( | |
538 content_window_->GetFocusManager()->GetFocusedWindow())) { | |
539 content_window_->GetFocusManager()->SetFocusedWindow(content_window_, NULL); | |
540 } | |
541 } | |
542 | |
543 Widget::MoveLoopResult DesktopRootWindowHostLinux::RunMoveLoop( | |
544 const gfx::Vector2d& drag_offset) { | |
545 SetCapture(); | |
546 | |
547 if (x11_window_move_client_->RunMoveLoop(content_window_, drag_offset) == | |
548 aura::client::MOVE_SUCCESSFUL) | |
549 return Widget::MOVE_LOOP_SUCCESSFUL; | |
550 | |
551 return Widget::MOVE_LOOP_CANCELED; | |
552 } | |
553 | |
554 void DesktopRootWindowHostLinux::EndMoveLoop() { | |
555 x11_window_move_client_->EndMoveLoop(); | |
556 } | |
557 | |
558 void DesktopRootWindowHostLinux::SetVisibilityChangedAnimationsEnabled( | |
559 bool value) { | |
560 // Much like the previous NativeWidgetGtk, we don't have anything to do here. | |
561 } | |
562 | |
563 bool DesktopRootWindowHostLinux::ShouldUseNativeFrame() { | |
564 return false; | |
565 } | |
566 | |
567 void DesktopRootWindowHostLinux::FrameTypeChanged() { | |
568 } | |
569 | |
570 NonClientFrameView* DesktopRootWindowHostLinux::CreateNonClientFrameView() { | |
571 return NULL; | |
572 } | |
573 | |
574 void DesktopRootWindowHostLinux::SetFullscreen(bool fullscreen) { | |
575 SetWMSpecState(fullscreen, | |
576 atom_cache_.GetAtom("_NET_WM_STATE_FULLSCREEN"), | |
577 None); | |
578 } | |
579 | |
580 bool DesktopRootWindowHostLinux::IsFullscreen() const { | |
581 return HasWMSpecProperty("_NET_WM_STATE_FULLSCREEN"); | |
582 } | |
583 | |
584 void DesktopRootWindowHostLinux::SetOpacity(unsigned char opacity) { | |
585 // TODO(erg): | |
586 NOTIMPLEMENTED(); | |
587 } | |
588 | |
589 void DesktopRootWindowHostLinux::SetWindowIcons( | |
590 const gfx::ImageSkia& window_icon, const gfx::ImageSkia& app_icon) { | |
591 // TODO(erg): | |
592 NOTIMPLEMENTED(); | |
593 } | |
594 | |
595 void DesktopRootWindowHostLinux::SetAccessibleName(const string16& name) { | |
596 // TODO(erg): | |
597 NOTIMPLEMENTED(); | |
598 } | |
599 | |
600 void DesktopRootWindowHostLinux::SetAccessibleRole( | |
601 ui::AccessibilityTypes::Role role) { | |
602 // TODO(erg): | |
603 NOTIMPLEMENTED(); | |
604 } | |
605 | |
606 void DesktopRootWindowHostLinux::SetAccessibleState( | |
607 ui::AccessibilityTypes::State state) { | |
608 // TODO(erg): | |
609 NOTIMPLEMENTED(); | |
610 } | |
611 | |
612 void DesktopRootWindowHostLinux::InitModalType(ui::ModalType modal_type) { | |
613 // TODO(erg): | |
614 NOTIMPLEMENTED(); | |
615 } | |
616 | |
617 void DesktopRootWindowHostLinux::FlashFrame(bool flash_frame) { | |
618 // TODO(erg): | |
619 NOTIMPLEMENTED(); | |
620 } | |
621 | |
622 void DesktopRootWindowHostLinux::OnNativeWidgetFocus() { | |
623 native_widget_delegate_->AsWidget()->GetInputMethod()->OnFocus(); | |
624 } | |
625 | |
626 void DesktopRootWindowHostLinux::OnNativeWidgetBlur() { | |
627 if (xwindow_) | |
628 native_widget_delegate_->AsWidget()->GetInputMethod()->OnBlur(); | |
629 } | |
630 | |
631 //////////////////////////////////////////////////////////////////////////////// | |
632 // DesktopRootWindowHostLinux, aura::RootWindowHost implementation: | |
633 | |
634 void DesktopRootWindowHostLinux::SetDelegate( | |
635 aura::RootWindowHostDelegate* delegate) { | |
636 root_window_host_delegate_ = delegate; | |
637 } | |
638 | |
639 aura::RootWindow* DesktopRootWindowHostLinux::GetRootWindow() { | |
640 return root_window_; | |
641 } | |
642 | |
643 gfx::AcceleratedWidget DesktopRootWindowHostLinux::GetAcceleratedWidget() { | |
644 return xwindow_; | |
645 } | |
646 | |
647 void DesktopRootWindowHostLinux::Show() { | |
648 if (!window_mapped_) { | |
649 // Before we map the window, set size hints. Otherwise, some window managers | |
650 // will ignore toplevel XMoveWindow commands. | |
651 XSizeHints size_hints; | |
652 size_hints.flags = PPosition; | |
653 size_hints.x = bounds_.x(); | |
654 size_hints.y = bounds_.y(); | |
655 XSetWMNormalHints(xdisplay_, xwindow_, &size_hints); | |
656 | |
657 XMapWindow(xdisplay_, xwindow_); | |
658 | |
659 // We now block until our window is mapped. Some X11 APIs will crash and | |
660 // burn if passed |xwindow_| before the window is mapped, and XMapWindow is | |
661 // asynchronous. | |
662 base::MessagePumpAuraX11::Current()->BlockUntilWindowMapped(xwindow_); | |
663 window_mapped_ = true; | |
664 } | |
665 } | |
666 | |
667 void DesktopRootWindowHostLinux::Hide() { | |
668 if (window_mapped_) { | |
669 XWithdrawWindow(xdisplay_, xwindow_, 0); | |
670 window_mapped_ = false; | |
671 } | |
672 } | |
673 | |
674 void DesktopRootWindowHostLinux::ToggleFullScreen() { | |
675 NOTIMPLEMENTED(); | |
676 } | |
677 | |
678 gfx::Rect DesktopRootWindowHostLinux::GetBounds() const { | |
679 return bounds_; | |
680 } | |
681 | |
682 void DesktopRootWindowHostLinux::SetBounds(const gfx::Rect& bounds) { | |
683 bool origin_changed = bounds_.origin() != bounds.origin(); | |
684 bool size_changed = bounds_.size() != bounds.size(); | |
685 XWindowChanges changes = {0}; | |
686 unsigned value_mask = 0; | |
687 | |
688 if (size_changed) { | |
689 changes.width = bounds.width(); | |
690 changes.height = bounds.height(); | |
691 value_mask |= CWHeight | CWWidth; | |
692 } | |
693 | |
694 if (origin_changed) { | |
695 changes.x = bounds.x(); | |
696 changes.y = bounds.y(); | |
697 value_mask |= CWX | CWY; | |
698 } | |
699 if (value_mask) | |
700 XConfigureWindow(xdisplay_, xwindow_, value_mask, &changes); | |
701 | |
702 // Assume that the resize will go through as requested, which should be the | |
703 // case if we're running without a window manager. If there's a window | |
704 // manager, it can modify or ignore the request, but (per ICCCM) we'll get a | |
705 // (possibly synthetic) ConfigureNotify about the actual size and correct | |
706 // |bounds_| later. | |
707 bounds_ = bounds; | |
708 | |
709 if (origin_changed) | |
710 native_widget_delegate_->AsWidget()->OnNativeWidgetMove(); | |
711 if (size_changed) | |
712 root_window_host_delegate_->OnHostResized(bounds.size()); | |
713 else | |
714 root_window_host_delegate_->OnHostPaint(); | |
715 } | |
716 | |
717 gfx::Point DesktopRootWindowHostLinux::GetLocationOnNativeScreen() const { | |
718 return bounds_.origin(); | |
719 } | |
720 | |
721 void DesktopRootWindowHostLinux::SetCapture() { | |
722 // This is vaguely based on the old NativeWidgetGtk implementation. | |
723 // | |
724 // X11's XPointerGrab() shouldn't be used for everything; it doesn't map | |
725 // cleanly to Windows' SetCapture(). GTK only provides a separate concept of | |
726 // a grab that wasn't the X11 pointer grab, but was instead a manual | |
727 // redirection of the event. (You need to drop into GDK if you want to | |
728 // perform a raw X11 grab). | |
729 | |
730 if (g_current_capture) | |
731 g_current_capture->OnCaptureReleased(); | |
732 | |
733 g_current_capture = this; | |
734 | |
735 // TODO(erg): In addition to the above, NativeWidgetGtk performs a full X | |
736 // pointer grab when our NativeWidget is of type Menu. However, things work | |
737 // without it. Clicking inside a chrome window causes a release capture, and | |
738 // clicking outside causes an activation change. Since previous attempts at | |
739 // using XPointerGrab() to implement this have locked my X server, I'm going | |
740 // to skip this for now. | |
741 } | |
742 | |
743 void DesktopRootWindowHostLinux::ReleaseCapture() { | |
744 if (g_current_capture) | |
745 g_current_capture->OnCaptureReleased(); | |
746 } | |
747 | |
748 void DesktopRootWindowHostLinux::SetCursor(gfx::NativeCursor cursor) { | |
749 XDefineCursor(xdisplay_, xwindow_, cursor.platform()); | |
750 } | |
751 | |
752 bool DesktopRootWindowHostLinux::QueryMouseLocation( | |
753 gfx::Point* location_return) { | |
754 ::Window root_return, child_return; | |
755 int root_x_return, root_y_return, win_x_return, win_y_return; | |
756 unsigned int mask_return; | |
757 XQueryPointer(xdisplay_, | |
758 xwindow_, | |
759 &root_return, | |
760 &child_return, | |
761 &root_x_return, &root_y_return, | |
762 &win_x_return, &win_y_return, | |
763 &mask_return); | |
764 *location_return = gfx::Point( | |
765 std::max(0, std::min(bounds_.width(), win_x_return)), | |
766 std::max(0, std::min(bounds_.height(), win_y_return))); | |
767 return (win_x_return >= 0 && win_x_return < bounds_.width() && | |
768 win_y_return >= 0 && win_y_return < bounds_.height()); | |
769 } | |
770 | |
771 bool DesktopRootWindowHostLinux::ConfineCursorToRootWindow() { | |
772 NOTIMPLEMENTED(); | |
773 return false; | |
774 } | |
775 | |
776 void DesktopRootWindowHostLinux::UnConfineCursor() { | |
777 NOTIMPLEMENTED(); | |
778 } | |
779 | |
780 void DesktopRootWindowHostLinux::MoveCursorTo(const gfx::Point& location) { | |
781 NOTIMPLEMENTED(); | |
782 } | |
783 | |
784 void DesktopRootWindowHostLinux::SetFocusWhenShown(bool focus_when_shown) { | |
785 static const char* k_NET_WM_USER_TIME = "_NET_WM_USER_TIME"; | |
786 focus_when_shown_ = focus_when_shown; | |
787 if (IsWindowManagerPresent() && !focus_when_shown_) { | |
788 ui::SetIntProperty(xwindow_, | |
789 k_NET_WM_USER_TIME, | |
790 k_NET_WM_USER_TIME, | |
791 0); | |
792 } | |
793 } | |
794 | |
795 bool DesktopRootWindowHostLinux::CopyAreaToSkCanvas( | |
796 const gfx::Rect& source_bounds, | |
797 const gfx::Point& dest_offset, | |
798 SkCanvas* canvas) { | |
799 NOTIMPLEMENTED(); | |
800 return false; | |
801 } | |
802 | |
803 bool DesktopRootWindowHostLinux::GrabSnapshot( | |
804 const gfx::Rect& snapshot_bounds, | |
805 std::vector<unsigned char>* png_representation) { | |
806 NOTIMPLEMENTED(); | |
807 return false; | |
808 } | |
809 | |
810 void DesktopRootWindowHostLinux::PostNativeEvent( | |
811 const base::NativeEvent& native_event) { | |
812 DCHECK(xwindow_); | |
813 DCHECK(xdisplay_); | |
814 XEvent xevent = *native_event; | |
815 xevent.xany.display = xdisplay_; | |
816 xevent.xany.window = xwindow_; | |
817 | |
818 switch (xevent.type) { | |
819 case EnterNotify: | |
820 case LeaveNotify: | |
821 case MotionNotify: | |
822 case KeyPress: | |
823 case KeyRelease: | |
824 case ButtonPress: | |
825 case ButtonRelease: { | |
826 // The fields used below are in the same place for all of events | |
827 // above. Using xmotion from XEvent's unions to avoid repeating | |
828 // the code. | |
829 xevent.xmotion.root = x_root_window_; | |
830 xevent.xmotion.time = CurrentTime; | |
831 | |
832 gfx::Point point(xevent.xmotion.x, xevent.xmotion.y); | |
833 root_window_->ConvertPointToNativeScreen(&point); | |
834 xevent.xmotion.x_root = point.x(); | |
835 xevent.xmotion.y_root = point.y(); | |
836 } | |
837 default: | |
838 break; | |
839 } | |
840 XSendEvent(xdisplay_, xwindow_, False, 0, &xevent); | |
841 } | |
842 | |
843 void DesktopRootWindowHostLinux::OnDeviceScaleFactorChanged( | |
844 float device_scale_factor) { | |
845 } | |
846 | |
847 void DesktopRootWindowHostLinux::PrepareForShutdown() { | |
848 } | |
849 | |
850 //////////////////////////////////////////////////////////////////////////////// | |
851 // DesktopRootWindowHostLinux, views::internal::InputMethodDelegate: | |
852 | |
853 void DesktopRootWindowHostLinux::DispatchKeyEventPostIME( | |
854 const ui::KeyEvent& key) { | |
855 FocusManager* focus_manager = | |
856 native_widget_delegate_->AsWidget()->GetFocusManager(); | |
857 if (native_widget_delegate_->OnKeyEvent(key) || !focus_manager) | |
858 return; | |
859 focus_manager->OnKeyEvent(key); | |
860 } | |
861 | |
862 //////////////////////////////////////////////////////////////////////////////// | |
863 // DesktopRootWindowHostLinux, MessageLoop::Dispatcher implementation: | |
864 | |
865 bool DesktopRootWindowHostLinux::Dispatch(const base::NativeEvent& event) { | |
866 XEvent* xev = event; | |
867 | |
868 // May want to factor CheckXEventForConsistency(xev); into a common location | |
869 // since it is called here. | |
870 switch (xev->type) { | |
871 case Expose: | |
872 // TODO(erg): Can we only redraw the affected areas? | |
873 root_window_host_delegate_->OnHostPaint(); | |
874 break; | |
875 case KeyPress: { | |
876 ui::KeyEvent keydown_event(xev, false); | |
877 root_window_host_delegate_->OnHostKeyEvent(&keydown_event); | |
878 break; | |
879 } | |
880 case KeyRelease: { | |
881 ui::KeyEvent keyup_event(xev, false); | |
882 root_window_host_delegate_->OnHostKeyEvent(&keyup_event); | |
883 break; | |
884 } | |
885 case ButtonPress: { | |
886 if (static_cast<int>(xev->xbutton.button) == kBackMouseButton || | |
887 static_cast<int>(xev->xbutton.button) == kForwardMouseButton) { | |
888 aura::client::UserActionClient* gesture_client = | |
889 aura::client::GetUserActionClient(root_window_); | |
890 if (gesture_client) { | |
891 gesture_client->OnUserAction( | |
892 static_cast<int>(xev->xbutton.button) == kBackMouseButton ? | |
893 aura::client::UserActionClient::BACK : | |
894 aura::client::UserActionClient::FORWARD); | |
895 } | |
896 break; | |
897 } | |
898 } // fallthrough | |
899 case ButtonRelease: { | |
900 ui::MouseEvent mouseev(xev); | |
901 DispatchMouseEvent(&mouseev); | |
902 break; | |
903 } | |
904 case FocusOut: | |
905 if (xev->xfocus.mode != NotifyGrab) { | |
906 ReleaseCapture(); | |
907 root_window_host_delegate_->OnHostLostWindowCapture(); | |
908 } else { | |
909 root_window_host_delegate_->OnHostLostMouseGrab(); | |
910 } | |
911 break; | |
912 case ConfigureNotify: { | |
913 DCHECK_EQ(xwindow_, xev->xconfigure.window); | |
914 DCHECK_EQ(xwindow_, xev->xconfigure.event); | |
915 // It's possible that the X window may be resized by some other means than | |
916 // from within aura (e.g. the X window manager can change the size). Make | |
917 // sure the root window size is maintained properly. | |
918 gfx::Rect bounds(xev->xconfigure.x, xev->xconfigure.y, | |
919 xev->xconfigure.width, xev->xconfigure.height); | |
920 bool size_changed = bounds_.size() != bounds.size(); | |
921 bool origin_changed = bounds_.origin() != bounds.origin(); | |
922 bounds_ = bounds; | |
923 if (size_changed) | |
924 root_window_host_delegate_->OnHostResized(bounds.size()); | |
925 if (origin_changed) | |
926 root_window_host_delegate_->OnHostMoved(bounds_.origin()); | |
927 break; | |
928 } | |
929 case GenericEvent: { | |
930 ui::TouchFactory* factory = ui::TouchFactory::GetInstance(); | |
931 if (!factory->ShouldProcessXI2Event(xev)) | |
932 break; | |
933 | |
934 ui::EventType type = ui::EventTypeFromNative(xev); | |
935 XEvent last_event; | |
936 int num_coalesced = 0; | |
937 | |
938 switch (type) { | |
939 // case ui::ET_TOUCH_MOVED: | |
940 // num_coalesced = CoalescePendingMotionEvents(xev, &last_event); | |
941 // if (num_coalesced > 0) | |
942 // xev = &last_event; | |
943 // // fallthrough | |
944 // case ui::ET_TOUCH_PRESSED: | |
945 // case ui::ET_TOUCH_RELEASED: { | |
946 // ui::TouchEvent touchev(xev); | |
947 // root_window_host_delegate_->OnHostTouchEvent(&touchev); | |
948 // break; | |
949 // } | |
950 case ui::ET_MOUSE_MOVED: | |
951 case ui::ET_MOUSE_DRAGGED: | |
952 case ui::ET_MOUSE_PRESSED: | |
953 case ui::ET_MOUSE_RELEASED: | |
954 case ui::ET_MOUSE_ENTERED: | |
955 case ui::ET_MOUSE_EXITED: { | |
956 if (type == ui::ET_MOUSE_MOVED || type == ui::ET_MOUSE_DRAGGED) { | |
957 // If this is a motion event, we want to coalesce all pending motion | |
958 // events that are at the top of the queue. | |
959 num_coalesced = ui::CoalescePendingMotionEvents(xev, &last_event); | |
960 if (num_coalesced > 0) | |
961 xev = &last_event; | |
962 } else if (type == ui::ET_MOUSE_PRESSED) { | |
963 XIDeviceEvent* xievent = | |
964 static_cast<XIDeviceEvent*>(xev->xcookie.data); | |
965 int button = xievent->detail; | |
966 if (button == kBackMouseButton || button == kForwardMouseButton) { | |
967 aura::client::UserActionClient* gesture_client = | |
968 aura::client::GetUserActionClient( | |
969 root_window_host_delegate_->AsRootWindow()); | |
970 if (gesture_client) { | |
971 bool reverse_direction = | |
972 ui::IsTouchpadEvent(xev) && ui::IsNaturalScrollEnabled(); | |
973 gesture_client->OnUserAction( | |
974 (button == kBackMouseButton && !reverse_direction) || | |
975 (button == kForwardMouseButton && reverse_direction) ? | |
976 aura::client::UserActionClient::BACK : | |
977 aura::client::UserActionClient::FORWARD); | |
978 } | |
979 break; | |
980 } | |
981 } | |
982 ui::MouseEvent mouseev(xev); | |
983 DispatchMouseEvent(&mouseev); | |
984 break; | |
985 } | |
986 case ui::ET_MOUSEWHEEL: { | |
987 ui::MouseWheelEvent mouseev(xev); | |
988 DispatchMouseEvent(&mouseev); | |
989 break; | |
990 } | |
991 case ui::ET_SCROLL_FLING_START: | |
992 case ui::ET_SCROLL_FLING_CANCEL: | |
993 case ui::ET_SCROLL: { | |
994 ui::ScrollEvent scrollev(xev); | |
995 root_window_host_delegate_->OnHostScrollEvent(&scrollev); | |
996 break; | |
997 } | |
998 case ui::ET_UNKNOWN: | |
999 break; | |
1000 default: | |
1001 NOTREACHED(); | |
1002 } | |
1003 | |
1004 // If we coalesced an event we need to free its cookie. | |
1005 if (num_coalesced > 0) | |
1006 XFreeEventData(xev->xgeneric.display, &last_event.xcookie); | |
1007 break; | |
1008 } | |
1009 case MapNotify: { | |
1010 // If there's no window manager running, we need to assign the X input | |
1011 // focus to our host window. | |
1012 if (!IsWindowManagerPresent() && focus_when_shown_) | |
1013 XSetInputFocus(xdisplay_, xwindow_, RevertToNone, CurrentTime); | |
1014 break; | |
1015 } | |
1016 case ClientMessage: { | |
1017 Atom message_type = static_cast<Atom>(xev->xclient.data.l[0]); | |
1018 if (message_type == atom_cache_.GetAtom("WM_DELETE_WINDOW")) { | |
1019 // We have received a close message from the window manager. | |
1020 root_window_->OnRootWindowHostCloseRequested(); | |
1021 } else if (message_type == atom_cache_.GetAtom("_NET_WM_PING")) { | |
1022 XEvent reply_event = *xev; | |
1023 reply_event.xclient.window = x_root_window_; | |
1024 | |
1025 XSendEvent(xdisplay_, | |
1026 reply_event.xclient.window, | |
1027 False, | |
1028 SubstructureRedirectMask | SubstructureNotifyMask, | |
1029 &reply_event); | |
1030 } | |
1031 break; | |
1032 } | |
1033 case MappingNotify: { | |
1034 switch (xev->xmapping.request) { | |
1035 case MappingModifier: | |
1036 case MappingKeyboard: | |
1037 XRefreshKeyboardMapping(&xev->xmapping); | |
1038 root_window_->OnKeyboardMappingChanged(); | |
1039 break; | |
1040 case MappingPointer: | |
1041 ui::UpdateButtonMap(); | |
1042 break; | |
1043 default: | |
1044 NOTIMPLEMENTED() << " Unknown request: " << xev->xmapping.request; | |
1045 break; | |
1046 } | |
1047 break; | |
1048 } | |
1049 case MotionNotify: { | |
1050 // Discard all but the most recent motion event that targets the same | |
1051 // window with unchanged state. | |
1052 XEvent last_event; | |
1053 while (XPending(xev->xany.display)) { | |
1054 XEvent next_event; | |
1055 XPeekEvent(xev->xany.display, &next_event); | |
1056 if (next_event.type == MotionNotify && | |
1057 next_event.xmotion.window == xev->xmotion.window && | |
1058 next_event.xmotion.subwindow == xev->xmotion.subwindow && | |
1059 next_event.xmotion.state == xev->xmotion.state) { | |
1060 XNextEvent(xev->xany.display, &last_event); | |
1061 xev = &last_event; | |
1062 } else { | |
1063 break; | |
1064 } | |
1065 } | |
1066 | |
1067 ui::MouseEvent mouseev(xev); | |
1068 DispatchMouseEvent(&mouseev); | |
1069 break; | |
1070 } | |
1071 case PropertyNotify: { | |
1072 // Get our new window property state if the WM has told us its changed. | |
1073 ::Atom state = atom_cache_.GetAtom("_NET_WM_STATE"); | |
1074 | |
1075 std::vector< ::Atom> atom_list; | |
1076 if (xev->xproperty.atom == state && | |
1077 ui::GetAtomArrayProperty(xwindow_, "_NET_WM_STATE", &atom_list)) { | |
1078 window_properties_.clear(); | |
1079 std::copy(atom_list.begin(), atom_list.end(), | |
1080 inserter(window_properties_, window_properties_.begin())); | |
1081 | |
1082 // Now that we have different window properties, we may need to | |
1083 // relayout the window. (The windows code doesn't need this because | |
1084 // their window change is synchronous.) | |
1085 native_widget_delegate_->AsWidget()->GetRootView()->Layout(); | |
1086 } | |
1087 } | |
1088 } | |
1089 return true; | |
1090 } | |
1091 | |
1092 //////////////////////////////////////////////////////////////////////////////// | |
1093 // DesktopRootWindowHost, public: | |
1094 | |
1095 // static | |
1096 DesktopRootWindowHost* DesktopRootWindowHost::Create( | |
1097 internal::NativeWidgetDelegate* native_widget_delegate, | |
1098 DesktopNativeWidgetAura* desktop_native_widget_aura, | |
1099 const gfx::Rect& initial_bounds) { | |
1100 return new DesktopRootWindowHostLinux(native_widget_delegate, | |
1101 desktop_native_widget_aura, | |
1102 initial_bounds); | |
1103 } | |
1104 | |
1105 } // namespace views | |
OLD | NEW |