OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/ui/gtk/extensions/native_app_window_gtk.h" | 5 #include "chrome/browser/ui/gtk/extensions/native_app_window_gtk.h" |
6 | 6 |
| 7 #include <gdk/gdkx.h> |
| 8 #include <vector> |
| 9 |
| 10 #include "base/message_loop/message_pump_gtk.h" |
7 #include "base/strings/utf_string_conversions.h" | 11 #include "base/strings/utf_string_conversions.h" |
8 #include "chrome/browser/profiles/profile.h" | 12 #include "chrome/browser/profiles/profile.h" |
9 #include "chrome/browser/ui/gtk/extensions/extension_keybinding_registry_gtk.h" | 13 #include "chrome/browser/ui/gtk/extensions/extension_keybinding_registry_gtk.h" |
10 #include "chrome/browser/ui/gtk/gtk_util.h" | 14 #include "chrome/browser/ui/gtk/gtk_util.h" |
11 #include "chrome/browser/ui/gtk/gtk_window_util.h" | 15 #include "chrome/browser/ui/gtk/gtk_window_util.h" |
12 #include "chrome/browser/web_applications/web_app.h" | 16 #include "chrome/browser/web_applications/web_app.h" |
13 #include "chrome/common/extensions/extension.h" | 17 #include "chrome/common/extensions/extension.h" |
14 #include "content/public/browser/render_view_host.h" | 18 #include "content/public/browser/render_view_host.h" |
15 #include "content/public/browser/render_widget_host_view.h" | 19 #include "content/public/browser/render_widget_host_view.h" |
16 #include "content/public/browser/web_contents.h" | 20 #include "content/public/browser/web_contents.h" |
17 #include "content/public/browser/web_contents_view.h" | 21 #include "content/public/browser/web_contents_view.h" |
18 #include "ui/base/x/active_window_watcher_x.h" | 22 #include "ui/base/x/active_window_watcher_x.h" |
19 #include "ui/gfx/gtk_util.h" | 23 #include "ui/gfx/gtk_util.h" |
20 #include "ui/gfx/image/image.h" | 24 #include "ui/gfx/image/image.h" |
21 #include "ui/gfx/rect.h" | 25 #include "ui/gfx/rect.h" |
22 | 26 |
23 using apps::ShellWindow; | 27 using apps::ShellWindow; |
24 | 28 |
25 namespace { | 29 namespace { |
26 | 30 |
27 // The timeout in milliseconds before we'll get the true window position with | 31 // The timeout in milliseconds before we'll get the true window position with |
28 // gtk_window_get_position() after the last GTK configure-event signal. | 32 // gtk_window_get_position() after the last GTK configure-event signal. |
29 const int kDebounceTimeoutMilliseconds = 100; | 33 const int kDebounceTimeoutMilliseconds = 100; |
30 | 34 |
| 35 const char* kAtomsToCache[] = { |
| 36 "_NET_WM_STATE", |
| 37 "_NET_WM_STATE_HIDDEN", |
| 38 NULL |
| 39 }; |
| 40 |
31 } // namespace | 41 } // namespace |
32 | 42 |
33 NativeAppWindowGtk::NativeAppWindowGtk(ShellWindow* shell_window, | 43 NativeAppWindowGtk::NativeAppWindowGtk(ShellWindow* shell_window, |
34 const ShellWindow::CreateParams& params) | 44 const ShellWindow::CreateParams& params) |
35 : shell_window_(shell_window), | 45 : shell_window_(shell_window), |
36 window_(NULL), | 46 window_(NULL), |
37 state_(GDK_WINDOW_STATE_WITHDRAWN), | 47 state_(GDK_WINDOW_STATE_WITHDRAWN), |
38 is_active_(false), | 48 is_active_(false), |
39 content_thinks_its_fullscreen_(false), | 49 content_thinks_its_fullscreen_(false), |
40 frameless_(params.frame == ShellWindow::FRAME_NONE), | 50 frameless_(params.frame == ShellWindow::FRAME_NONE), |
41 frame_cursor_(NULL) { | 51 frame_cursor_(NULL), |
| 52 atom_cache_(base::MessagePumpGtk::GetDefaultXDisplay(), kAtomsToCache), |
| 53 is_x_event_listened_(false) { |
42 window_ = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL)); | 54 window_ = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL)); |
43 | 55 |
44 gfx::NativeView native_view = | 56 gfx::NativeView native_view = |
45 web_contents()->GetView()->GetNativeView(); | 57 web_contents()->GetView()->GetNativeView(); |
46 gtk_container_add(GTK_CONTAINER(window_), native_view); | 58 gtk_container_add(GTK_CONTAINER(window_), native_view); |
47 | 59 |
48 if (params.bounds.x() != INT_MIN && params.bounds.y() != INT_MIN) | 60 if (params.bounds.x() != INT_MIN && params.bounds.y() != INT_MIN) |
49 gtk_window_move(window_, params.bounds.x(), params.bounds.y()); | 61 gtk_window_move(window_, params.bounds.x(), params.bounds.y()); |
50 | 62 |
51 // This is done to avoid a WM "feature" where setting the window size to | 63 // This is done to avoid a WM "feature" where setting the window size to |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
121 G_CALLBACK(OnConfigureThunk), this); | 133 G_CALLBACK(OnConfigureThunk), this); |
122 g_signal_connect(window_, "window-state-event", | 134 g_signal_connect(window_, "window-state-event", |
123 G_CALLBACK(OnWindowStateThunk), this); | 135 G_CALLBACK(OnWindowStateThunk), this); |
124 if (frameless_) { | 136 if (frameless_) { |
125 g_signal_connect(window_, "button-press-event", | 137 g_signal_connect(window_, "button-press-event", |
126 G_CALLBACK(OnButtonPressThunk), this); | 138 G_CALLBACK(OnButtonPressThunk), this); |
127 g_signal_connect(window_, "motion-notify-event", | 139 g_signal_connect(window_, "motion-notify-event", |
128 G_CALLBACK(OnMouseMoveEventThunk), this); | 140 G_CALLBACK(OnMouseMoveEventThunk), this); |
129 } | 141 } |
130 | 142 |
| 143 // If _NET_WM_STATE_HIDDEN is in _NET_SUPPORTED, listen for XEvent to work |
| 144 // around GTK+ not reporting minimization state changes. See comment in the |
| 145 // |OnXEvent|. |
| 146 std::vector< ::Atom> supported_atoms; |
| 147 if (ui::GetAtomArrayProperty(ui::GetX11RootWindow(), |
| 148 "_NET_SUPPORTED", |
| 149 &supported_atoms)) { |
| 150 if (std::find(supported_atoms.begin(), |
| 151 supported_atoms.end(), |
| 152 atom_cache_.GetAtom("_NET_WM_STATE_HIDDEN")) != |
| 153 supported_atoms.end()) { |
| 154 GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(window_)); |
| 155 gdk_window_add_filter(window, |
| 156 &NativeAppWindowGtk::OnXEventThunk, |
| 157 this); |
| 158 is_x_event_listened_ = true; |
| 159 } |
| 160 } |
| 161 |
131 // Add the keybinding registry. | 162 // Add the keybinding registry. |
132 extension_keybinding_registry_.reset(new ExtensionKeybindingRegistryGtk( | 163 extension_keybinding_registry_.reset(new ExtensionKeybindingRegistryGtk( |
133 shell_window_->profile(), | 164 shell_window_->profile(), |
134 window_, | 165 window_, |
135 extensions::ExtensionKeybindingRegistry::PLATFORM_APPS_ONLY, | 166 extensions::ExtensionKeybindingRegistry::PLATFORM_APPS_ONLY, |
136 shell_window_)); | 167 shell_window_)); |
137 | 168 |
138 ui::ActiveWindowWatcherX::AddObserver(this); | 169 ui::ActiveWindowWatcherX::AddObserver(this); |
139 } | 170 } |
140 | 171 |
141 NativeAppWindowGtk::~NativeAppWindowGtk() { | 172 NativeAppWindowGtk::~NativeAppWindowGtk() { |
142 ui::ActiveWindowWatcherX::RemoveObserver(this); | 173 ui::ActiveWindowWatcherX::RemoveObserver(this); |
| 174 if (is_x_event_listened_) { |
| 175 gdk_window_remove_filter(NULL, |
| 176 &NativeAppWindowGtk::OnXEventThunk, |
| 177 this); |
| 178 } |
143 } | 179 } |
144 | 180 |
145 bool NativeAppWindowGtk::IsActive() const { | 181 bool NativeAppWindowGtk::IsActive() const { |
146 if (ui::ActiveWindowWatcherX::WMSupportsActivation()) | 182 if (ui::ActiveWindowWatcherX::WMSupportsActivation()) |
147 return is_active_; | 183 return is_active_; |
148 | 184 |
149 // This still works even though we don't get the activation notification. | 185 // This still works even though we don't get the activation notification. |
150 return gtk_window_is_active(window_); | 186 return gtk_window_is_active(window_); |
151 } | 187 } |
152 | 188 |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
217 | 253 |
218 void NativeAppWindowGtk::Activate() { | 254 void NativeAppWindowGtk::Activate() { |
219 gtk_window_present(window_); | 255 gtk_window_present(window_); |
220 } | 256 } |
221 | 257 |
222 void NativeAppWindowGtk::Deactivate() { | 258 void NativeAppWindowGtk::Deactivate() { |
223 gdk_window_lower(gtk_widget_get_window(GTK_WIDGET(window_))); | 259 gdk_window_lower(gtk_widget_get_window(GTK_WIDGET(window_))); |
224 } | 260 } |
225 | 261 |
226 void NativeAppWindowGtk::Maximize() { | 262 void NativeAppWindowGtk::Maximize() { |
| 263 // Represent the window first in order to keep the maximization behavior |
| 264 // consistency with Windows platform. Otherwise the window will be hidden if |
| 265 // it has been minimized. |
| 266 gtk_window_present(window_); |
227 gtk_window_maximize(window_); | 267 gtk_window_maximize(window_); |
228 } | 268 } |
229 | 269 |
230 void NativeAppWindowGtk::Minimize() { | 270 void NativeAppWindowGtk::Minimize() { |
231 gtk_window_iconify(window_); | 271 gtk_window_iconify(window_); |
232 } | 272 } |
233 | 273 |
234 void NativeAppWindowGtk::Restore() { | 274 void NativeAppWindowGtk::Restore() { |
235 if (IsMaximized()) | 275 if (IsMaximized()) |
236 gtk_window_unmaximize(window_); | 276 gtk_window_unmaximize(window_); |
237 else if (IsMinimized()) | 277 else if (IsMinimized()) |
238 gtk_window_deiconify(window_); | 278 gtk_window_deiconify(window_); |
| 279 |
| 280 // Represent the window to keep restoration behavior consistency with Windows |
| 281 // platform. |
| 282 // TODO(zhchbin): verify whether we need this until http://crbug.com/261013 is |
| 283 // fixed. |
| 284 gtk_window_present(window_); |
239 } | 285 } |
240 | 286 |
241 void NativeAppWindowGtk::SetBounds(const gfx::Rect& bounds) { | 287 void NativeAppWindowGtk::SetBounds(const gfx::Rect& bounds) { |
242 gfx::Rect content_bounds = bounds; | 288 gfx::Rect content_bounds = bounds; |
243 content_bounds.Inset(GetFrameInsets()); | 289 content_bounds.Inset(GetFrameInsets()); |
244 gtk_window_move(window_, content_bounds.x(), content_bounds.y()); | 290 gtk_window_move(window_, content_bounds.x(), content_bounds.y()); |
245 if (!resizable_) { | 291 if (!resizable_) { |
246 if (frameless_ && | 292 if (frameless_ && |
247 gtk_window_util::BoundsMatchMonitorSize(window_, content_bounds)) { | 293 gtk_window_util::BoundsMatchMonitorSize(window_, content_bounds)) { |
248 content_bounds.set_height(content_bounds.height() - 1); | 294 content_bounds.set_height(content_bounds.height() - 1); |
249 } | 295 } |
250 // TODO(jeremya): set_size_request doesn't honor min/max size, so the | 296 // TODO(jeremya): set_size_request doesn't honor min/max size, so the |
251 // bounds should be constrained manually. | 297 // bounds should be constrained manually. |
252 gtk_widget_set_size_request(GTK_WIDGET(window_), | 298 gtk_widget_set_size_request(GTK_WIDGET(window_), |
253 content_bounds.width(), content_bounds.height()); | 299 content_bounds.width(), content_bounds.height()); |
254 } else { | 300 } else { |
255 gtk_window_util::SetWindowSize(window_, | 301 gtk_window_util::SetWindowSize(window_, |
256 gfx::Size(bounds.width(), bounds.height())); | 302 gfx::Size(bounds.width(), bounds.height())); |
257 } | 303 } |
258 } | 304 } |
259 | 305 |
| 306 GdkFilterReturn NativeAppWindowGtk::OnXEvent(GdkXEvent* gdk_x_event, |
| 307 GdkEvent* gdk_event) { |
| 308 // Work around GTK+ not reporting minimization state changes. Listen |
| 309 // for _NET_WM_STATE property changes and use _NET_WM_STATE_HIDDEN's |
| 310 // presence to set or clear the iconified bit if _NET_WM_STATE_HIDDEN |
| 311 // is supported. http://crbug.com/162794. |
| 312 XEvent* x_event = static_cast<XEvent*>(gdk_x_event); |
| 313 std::vector< ::Atom> atom_list; |
| 314 |
| 315 if (x_event->type == PropertyNotify && |
| 316 x_event->xproperty.atom == atom_cache_.GetAtom("_NET_WM_STATE") && |
| 317 ui::GetAtomArrayProperty(GDK_WINDOW_XWINDOW(GTK_WIDGET(window_)->window), |
| 318 "_NET_WM_STATE", |
| 319 &atom_list)) { |
| 320 std::vector< ::Atom>::iterator it = |
| 321 std::find(atom_list.begin(), |
| 322 atom_list.end(), |
| 323 atom_cache_.GetAtom("_NET_WM_STATE_HIDDEN")); |
| 324 state_ = (it != atom_list.end()) ? GDK_WINDOW_STATE_ICONIFIED : |
| 325 static_cast<GdkWindowState>(state_ & ~GDK_WINDOW_STATE_ICONIFIED); |
| 326 |
| 327 shell_window_->OnNativeWindowChanged(); |
| 328 } |
| 329 |
| 330 return GDK_FILTER_CONTINUE; |
| 331 } |
| 332 |
260 void NativeAppWindowGtk::FlashFrame(bool flash) { | 333 void NativeAppWindowGtk::FlashFrame(bool flash) { |
261 gtk_window_set_urgency_hint(window_, flash); | 334 gtk_window_set_urgency_hint(window_, flash); |
262 } | 335 } |
263 | 336 |
264 bool NativeAppWindowGtk::IsAlwaysOnTop() const { | 337 bool NativeAppWindowGtk::IsAlwaysOnTop() const { |
265 return false; | 338 return false; |
266 } | 339 } |
267 | 340 |
268 void NativeAppWindowGtk::RenderViewHostChanged() { | 341 void NativeAppWindowGtk::RenderViewHostChanged() { |
269 web_contents()->GetView()->Focus(); | 342 web_contents()->GetView()->Focus(); |
(...skipping 277 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
547 | 620 |
548 draggable_region_.reset(ShellWindow::RawDraggableRegionsToSkRegion(regions)); | 621 draggable_region_.reset(ShellWindow::RawDraggableRegionsToSkRegion(regions)); |
549 } | 622 } |
550 | 623 |
551 // static | 624 // static |
552 NativeAppWindow* NativeAppWindow::Create( | 625 NativeAppWindow* NativeAppWindow::Create( |
553 ShellWindow* shell_window, | 626 ShellWindow* shell_window, |
554 const ShellWindow::CreateParams& params) { | 627 const ShellWindow::CreateParams& params) { |
555 return new NativeAppWindowGtk(shell_window, params); | 628 return new NativeAppWindowGtk(shell_window, params); |
556 } | 629 } |
OLD | NEW |