native_app_window_gtk.cc revision 4e180b6a0b4720a9b8e9e959a882386f690f08ff
1// Copyright 2013 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/ui/gtk/apps/native_app_window_gtk.h" 6 7#include <gdk/gdkx.h> 8#include <vector> 9 10#include "base/message_loop/message_pump_gtk.h" 11#include "base/strings/utf_string_conversions.h" 12#include "chrome/browser/profiles/profile.h" 13#include "chrome/browser/ui/gtk/extensions/extension_keybinding_registry_gtk.h" 14#include "chrome/browser/ui/gtk/gtk_util.h" 15#include "chrome/browser/ui/gtk/gtk_window_util.h" 16#include "chrome/browser/web_applications/web_app.h" 17#include "chrome/common/extensions/extension.h" 18#include "content/public/browser/render_view_host.h" 19#include "content/public/browser/render_widget_host_view.h" 20#include "content/public/browser/web_contents.h" 21#include "content/public/browser/web_contents_view.h" 22#include "ui/base/x/active_window_watcher_x.h" 23#include "ui/gfx/gtk_util.h" 24#include "ui/gfx/image/image.h" 25#include "ui/gfx/rect.h" 26 27using apps::ShellWindow; 28 29namespace { 30 31// The timeout in milliseconds before we'll get the true window position with 32// gtk_window_get_position() after the last GTK configure-event signal. 33const int kDebounceTimeoutMilliseconds = 100; 34 35const char* kAtomsToCache[] = { 36 "_NET_WM_STATE", 37 "_NET_WM_STATE_HIDDEN", 38 NULL 39}; 40 41} // namespace 42 43NativeAppWindowGtk::NativeAppWindowGtk(ShellWindow* shell_window, 44 const ShellWindow::CreateParams& params) 45 : shell_window_(shell_window), 46 window_(NULL), 47 state_(GDK_WINDOW_STATE_WITHDRAWN), 48 is_active_(false), 49 content_thinks_its_fullscreen_(false), 50 frameless_(params.frame == ShellWindow::FRAME_NONE), 51 always_on_top_(params.always_on_top), 52 frame_cursor_(NULL), 53 atom_cache_(base::MessagePumpGtk::GetDefaultXDisplay(), kAtomsToCache), 54 is_x_event_listened_(false) { 55 Observe(web_contents()); 56 57 window_ = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL)); 58 59 gfx::NativeView native_view = 60 web_contents()->GetView()->GetNativeView(); 61 gtk_container_add(GTK_CONTAINER(window_), native_view); 62 63 if (params.bounds.x() != INT_MIN && params.bounds.y() != INT_MIN) 64 gtk_window_move(window_, params.bounds.x(), params.bounds.y()); 65 66 // This is done to avoid a WM "feature" where setting the window size to 67 // the monitor size causes the WM to set the EWMH for full screen mode. 68 int win_height = params.bounds.height(); 69 if (frameless_ && 70 gtk_window_util::BoundsMatchMonitorSize(window_, params.bounds)) { 71 win_height -= 1; 72 } 73 gtk_window_set_default_size(window_, params.bounds.width(), win_height); 74 75 resizable_ = params.resizable; 76 if (!resizable_) { 77 // If the window doesn't have a size request when we set resizable to 78 // false, GTK will shrink the window to 1x1px. 79 gtk_widget_set_size_request(GTK_WIDGET(window_), 80 params.bounds.width(), win_height); 81 gtk_window_set_resizable(window_, FALSE); 82 } 83 84 // make sure bounds_ and restored_bounds_ have correct values until we 85 // get our first configure-event 86 bounds_ = restored_bounds_ = params.bounds; 87 gint x, y; 88 gtk_window_get_position(window_, &x, &y); 89 bounds_.set_origin(gfx::Point(x, y)); 90 91 // Hide titlebar when {frame: 'none'} specified on ShellWindow. 92 if (frameless_) 93 gtk_window_set_decorated(window_, false); 94 95 if (always_on_top_) 96 gtk_window_set_keep_above(window_, TRUE); 97 98 int min_width = params.minimum_size.width(); 99 int min_height = params.minimum_size.height(); 100 int max_width = params.maximum_size.width(); 101 int max_height = params.maximum_size.height(); 102 GdkGeometry hints; 103 int hints_mask = 0; 104 if (min_width || min_height) { 105 hints.min_height = min_height; 106 hints.min_width = min_width; 107 hints_mask |= GDK_HINT_MIN_SIZE; 108 } 109 if (max_width || max_height) { 110 hints.max_height = max_height ? max_height : G_MAXINT; 111 hints.max_width = max_width ? max_width : G_MAXINT; 112 hints_mask |= GDK_HINT_MAX_SIZE; 113 } 114 if (hints_mask) { 115 gtk_window_set_geometry_hints( 116 window_, 117 GTK_WIDGET(window_), 118 &hints, 119 static_cast<GdkWindowHints>(hints_mask)); 120 } 121 122 // In some (older) versions of compiz, raising top-level windows when they 123 // are partially off-screen causes them to get snapped back on screen, not 124 // always even on the current virtual desktop. If we are running under 125 // compiz, suppress such raises, as they are not necessary in compiz anyway. 126 if (ui::GuessWindowManager() == ui::WM_COMPIZ) 127 suppress_window_raise_ = true; 128 129 gtk_window_set_title(window_, extension()->name().c_str()); 130 131 std::string app_name = web_app::GenerateApplicationNameFromExtensionId( 132 extension()->id()); 133 gtk_window_util::SetWindowCustomClass(window_, 134 web_app::GetWMClassFromAppName(app_name)); 135 136 g_signal_connect(window_, "delete-event", 137 G_CALLBACK(OnMainWindowDeleteEventThunk), this); 138 g_signal_connect(window_, "configure-event", 139 G_CALLBACK(OnConfigureThunk), this); 140 g_signal_connect(window_, "window-state-event", 141 G_CALLBACK(OnWindowStateThunk), this); 142 if (frameless_) { 143 g_signal_connect(window_, "button-press-event", 144 G_CALLBACK(OnButtonPressThunk), this); 145 g_signal_connect(window_, "motion-notify-event", 146 G_CALLBACK(OnMouseMoveEventThunk), this); 147 } 148 149 // If _NET_WM_STATE_HIDDEN is in _NET_SUPPORTED, listen for XEvent to work 150 // around GTK+ not reporting minimization state changes. See comment in the 151 // |OnXEvent|. 152 std::vector< ::Atom> supported_atoms; 153 if (ui::GetAtomArrayProperty(ui::GetX11RootWindow(), 154 "_NET_SUPPORTED", 155 &supported_atoms)) { 156 if (std::find(supported_atoms.begin(), 157 supported_atoms.end(), 158 atom_cache_.GetAtom("_NET_WM_STATE_HIDDEN")) != 159 supported_atoms.end()) { 160 GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(window_)); 161 gdk_window_add_filter(window, 162 &NativeAppWindowGtk::OnXEventThunk, 163 this); 164 is_x_event_listened_ = true; 165 } 166 } 167 168 // Add the keybinding registry. 169 extension_keybinding_registry_.reset(new ExtensionKeybindingRegistryGtk( 170 shell_window_->profile(), 171 window_, 172 extensions::ExtensionKeybindingRegistry::PLATFORM_APPS_ONLY, 173 shell_window_)); 174 175 ui::ActiveWindowWatcherX::AddObserver(this); 176} 177 178NativeAppWindowGtk::~NativeAppWindowGtk() { 179 ui::ActiveWindowWatcherX::RemoveObserver(this); 180 if (is_x_event_listened_) { 181 gdk_window_remove_filter(NULL, 182 &NativeAppWindowGtk::OnXEventThunk, 183 this); 184 } 185} 186 187bool NativeAppWindowGtk::IsActive() const { 188 if (ui::ActiveWindowWatcherX::WMSupportsActivation()) 189 return is_active_; 190 191 // This still works even though we don't get the activation notification. 192 return gtk_window_is_active(window_); 193} 194 195bool NativeAppWindowGtk::IsMaximized() const { 196 return (state_ & GDK_WINDOW_STATE_MAXIMIZED); 197} 198 199bool NativeAppWindowGtk::IsMinimized() const { 200 return (state_ & GDK_WINDOW_STATE_ICONIFIED); 201} 202 203bool NativeAppWindowGtk::IsFullscreen() const { 204 return (state_ & GDK_WINDOW_STATE_FULLSCREEN); 205} 206 207gfx::NativeWindow NativeAppWindowGtk::GetNativeWindow() { 208 return window_; 209} 210 211gfx::Rect NativeAppWindowGtk::GetRestoredBounds() const { 212 gfx::Rect window_bounds = restored_bounds_; 213 window_bounds.Inset(-GetFrameInsets()); 214 return window_bounds; 215} 216 217ui::WindowShowState NativeAppWindowGtk::GetRestoredState() const { 218 if (IsMaximized()) 219 return ui::SHOW_STATE_MAXIMIZED; 220 if (IsFullscreen()) 221 return ui::SHOW_STATE_FULLSCREEN; 222 return ui::SHOW_STATE_NORMAL; 223} 224 225gfx::Rect NativeAppWindowGtk::GetBounds() const { 226 gfx::Rect window_bounds = bounds_; 227 window_bounds.Inset(-GetFrameInsets()); 228 return window_bounds; 229} 230 231void NativeAppWindowGtk::Show() { 232 gtk_window_present(window_); 233} 234 235void NativeAppWindowGtk::ShowInactive() { 236 gtk_window_set_focus_on_map(window_, false); 237 gtk_widget_show(GTK_WIDGET(window_)); 238} 239 240void NativeAppWindowGtk::Hide() { 241 gtk_widget_hide(GTK_WIDGET(window_)); 242} 243 244void NativeAppWindowGtk::Close() { 245 shell_window_->OnNativeWindowChanged(); 246 247 // Cancel any pending callback from the window configure debounce timer. 248 window_configure_debounce_timer_.Stop(); 249 250 GtkWidget* window = GTK_WIDGET(window_); 251 // To help catch bugs in any event handlers that might get fired during the 252 // destruction, set window_ to NULL before any handlers will run. 253 window_ = NULL; 254 255 // OnNativeClose does a delete this so no other members should 256 // be accessed after. gtk_widget_destroy is safe (and must 257 // be last). 258 shell_window_->OnNativeClose(); 259 gtk_widget_destroy(window); 260} 261 262void NativeAppWindowGtk::Activate() { 263 gtk_window_present(window_); 264} 265 266void NativeAppWindowGtk::Deactivate() { 267 gdk_window_lower(gtk_widget_get_window(GTK_WIDGET(window_))); 268} 269 270void NativeAppWindowGtk::Maximize() { 271 // Represent the window first in order to keep the maximization behavior 272 // consistency with Windows platform. Otherwise the window will be hidden if 273 // it has been minimized. 274 gtk_window_present(window_); 275 gtk_window_maximize(window_); 276} 277 278void NativeAppWindowGtk::Minimize() { 279 gtk_window_iconify(window_); 280} 281 282void NativeAppWindowGtk::Restore() { 283 if (IsMaximized()) 284 gtk_window_unmaximize(window_); 285 else if (IsMinimized()) 286 gtk_window_deiconify(window_); 287 288 // Represent the window to keep restoration behavior consistency with Windows 289 // platform. 290 // TODO(zhchbin): verify whether we need this until http://crbug.com/261013 is 291 // fixed. 292 gtk_window_present(window_); 293} 294 295void NativeAppWindowGtk::SetBounds(const gfx::Rect& bounds) { 296 gfx::Rect content_bounds = bounds; 297 content_bounds.Inset(GetFrameInsets()); 298 gtk_window_move(window_, content_bounds.x(), content_bounds.y()); 299 if (!resizable_) { 300 if (frameless_ && 301 gtk_window_util::BoundsMatchMonitorSize(window_, content_bounds)) { 302 content_bounds.set_height(content_bounds.height() - 1); 303 } 304 // TODO(jeremya): set_size_request doesn't honor min/max size, so the 305 // bounds should be constrained manually. 306 gtk_widget_set_size_request(GTK_WIDGET(window_), 307 content_bounds.width(), content_bounds.height()); 308 } else { 309 gtk_window_util::SetWindowSize(window_, 310 gfx::Size(bounds.width(), bounds.height())); 311 } 312} 313 314GdkFilterReturn NativeAppWindowGtk::OnXEvent(GdkXEvent* gdk_x_event, 315 GdkEvent* gdk_event) { 316 // Work around GTK+ not reporting minimization state changes. Listen 317 // for _NET_WM_STATE property changes and use _NET_WM_STATE_HIDDEN's 318 // presence to set or clear the iconified bit if _NET_WM_STATE_HIDDEN 319 // is supported. http://crbug.com/162794. 320 XEvent* x_event = static_cast<XEvent*>(gdk_x_event); 321 std::vector< ::Atom> atom_list; 322 323 if (x_event->type == PropertyNotify && 324 x_event->xproperty.atom == atom_cache_.GetAtom("_NET_WM_STATE") && 325 ui::GetAtomArrayProperty(GDK_WINDOW_XWINDOW(GTK_WIDGET(window_)->window), 326 "_NET_WM_STATE", 327 &atom_list)) { 328 std::vector< ::Atom>::iterator it = 329 std::find(atom_list.begin(), 330 atom_list.end(), 331 atom_cache_.GetAtom("_NET_WM_STATE_HIDDEN")); 332 state_ = (it != atom_list.end()) ? GDK_WINDOW_STATE_ICONIFIED : 333 static_cast<GdkWindowState>(state_ & ~GDK_WINDOW_STATE_ICONIFIED); 334 335 shell_window_->OnNativeWindowChanged(); 336 } 337 338 return GDK_FILTER_CONTINUE; 339} 340 341void NativeAppWindowGtk::FlashFrame(bool flash) { 342 gtk_window_set_urgency_hint(window_, flash); 343} 344 345bool NativeAppWindowGtk::IsAlwaysOnTop() const { 346 return always_on_top_; 347} 348 349void NativeAppWindowGtk::RenderViewHostChanged( 350 content::RenderViewHost* old_host, 351 content::RenderViewHost* new_host) { 352 web_contents()->GetView()->Focus(); 353} 354 355void NativeAppWindowGtk::SetAlwaysOnTop(bool always_on_top) { 356 if (always_on_top_ != always_on_top) { 357 // gdk_window_get_state() does not give us the correct value for the 358 // GDK_WINDOW_STATE_ABOVE bit. Cache the current state. 359 always_on_top_ = always_on_top; 360 gtk_window_set_keep_above(window_, always_on_top_ ? TRUE : FALSE); 361 shell_window_->OnNativeWindowChanged(); 362 } 363} 364 365gfx::NativeView NativeAppWindowGtk::GetHostView() const { 366 NOTIMPLEMENTED(); 367 return NULL; 368} 369 370gfx::Point NativeAppWindowGtk::GetDialogPosition(const gfx::Size& size) { 371 gint current_width = 0; 372 gint current_height = 0; 373 gtk_window_get_size(window_, ¤t_width, ¤t_height); 374 return gfx::Point(current_width / 2 - size.width() / 2, 375 current_height / 2 - size.height() / 2); 376} 377 378gfx::Size NativeAppWindowGtk::GetMaximumDialogSize() { 379 gint current_width = 0; 380 gint current_height = 0; 381 gtk_window_get_size(window_, ¤t_width, ¤t_height); 382 return gfx::Size(current_width, current_height); 383} 384 385void NativeAppWindowGtk::AddObserver( 386 web_modal::ModalDialogHostObserver* observer) { 387 observer_list_.AddObserver(observer); 388} 389 390void NativeAppWindowGtk::RemoveObserver( 391 web_modal::ModalDialogHostObserver* observer) { 392 observer_list_.RemoveObserver(observer); 393} 394 395void NativeAppWindowGtk::ActiveWindowChanged(GdkWindow* active_window) { 396 // Do nothing if we're in the process of closing the browser window. 397 if (!window_) 398 return; 399 400 is_active_ = gtk_widget_get_window(GTK_WIDGET(window_)) == active_window; 401 if (is_active_) 402 shell_window_->OnNativeWindowActivated(); 403} 404 405// Callback for the delete event. This event is fired when the user tries to 406// close the window (e.g., clicking on the X in the window manager title bar). 407gboolean NativeAppWindowGtk::OnMainWindowDeleteEvent(GtkWidget* widget, 408 GdkEvent* event) { 409 Close(); 410 411 // Return true to prevent the GTK window from being destroyed. Close will 412 // destroy it for us. 413 return TRUE; 414} 415 416gboolean NativeAppWindowGtk::OnConfigure(GtkWidget* widget, 417 GdkEventConfigure* event) { 418 // We update |bounds_| but not |restored_bounds_| here. The latter needs 419 // to be updated conditionally when the window is non-maximized and non- 420 // fullscreen, but whether those state updates have been processed yet is 421 // window-manager specific. We update |restored_bounds_| in the debounced 422 // handler below, after the window state has been updated. 423 bounds_.SetRect(event->x, event->y, event->width, event->height); 424 425 // The GdkEventConfigure* we get here doesn't have quite the right 426 // coordinates though (they're relative to the drawable window area, rather 427 // than any window manager decorations, if enabled), so we need to call 428 // gtk_window_get_position() to get the right values. (Otherwise session 429 // restore, if enabled, will restore windows to incorrect positions.) That's 430 // a round trip to the X server though, so we set a debounce timer and only 431 // call it (in OnConfigureDebounced() below) after we haven't seen a 432 // reconfigure event in a short while. 433 // We don't use Reset() because the timer may not yet be running. 434 // (In that case Stop() is a no-op.) 435 window_configure_debounce_timer_.Stop(); 436 window_configure_debounce_timer_.Start(FROM_HERE, 437 base::TimeDelta::FromMilliseconds(kDebounceTimeoutMilliseconds), this, 438 &NativeAppWindowGtk::OnConfigureDebounced); 439 440 return FALSE; 441} 442 443void NativeAppWindowGtk::OnConfigureDebounced() { 444 gtk_window_util::UpdateWindowPosition(this, &bounds_, &restored_bounds_); 445 shell_window_->OnNativeWindowChanged(); 446 447 FOR_EACH_OBSERVER(web_modal::ModalDialogHostObserver, 448 observer_list_, 449 OnPositionRequiresUpdate()); 450 451 // Fullscreen of non-resizable windows requires them to be made resizable 452 // first. After that takes effect and OnConfigure is called we transition 453 // to fullscreen. 454 if (!IsFullscreen() && IsFullscreenOrPending()) { 455 gtk_window_fullscreen(window_); 456 } 457} 458 459gboolean NativeAppWindowGtk::OnWindowState(GtkWidget* sender, 460 GdkEventWindowState* event) { 461 state_ = event->new_window_state; 462 463 if (content_thinks_its_fullscreen_ && 464 !(state_ & GDK_WINDOW_STATE_FULLSCREEN)) { 465 content_thinks_its_fullscreen_ = false; 466 content::RenderViewHost* rvh = web_contents()->GetRenderViewHost(); 467 if (rvh) 468 rvh->ExitFullscreen(); 469 } 470 471 shell_window_->OnNativeWindowChanged(); 472 473 return FALSE; 474} 475 476bool NativeAppWindowGtk::GetWindowEdge(int x, int y, GdkWindowEdge* edge) { 477 if (!frameless_) 478 return false; 479 480 if (IsMaximized() || IsFullscreen()) 481 return false; 482 483 return gtk_window_util::GetWindowEdge(bounds_.size(), 0, x, y, edge); 484} 485 486gboolean NativeAppWindowGtk::OnMouseMoveEvent(GtkWidget* widget, 487 GdkEventMotion* event) { 488 if (!frameless_) { 489 // Reset the cursor. 490 if (frame_cursor_) { 491 frame_cursor_ = NULL; 492 gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(window_)), NULL); 493 } 494 return FALSE; 495 } 496 497 if (!resizable_) 498 return FALSE; 499 500 // Update the cursor if we're on the custom frame border. 501 GdkWindowEdge edge; 502 bool has_hit_edge = GetWindowEdge(static_cast<int>(event->x), 503 static_cast<int>(event->y), &edge); 504 GdkCursorType new_cursor = GDK_LAST_CURSOR; 505 if (has_hit_edge) 506 new_cursor = gtk_window_util::GdkWindowEdgeToGdkCursorType(edge); 507 508 GdkCursorType last_cursor = GDK_LAST_CURSOR; 509 if (frame_cursor_) 510 last_cursor = frame_cursor_->type; 511 512 if (last_cursor != new_cursor) { 513 frame_cursor_ = has_hit_edge ? gfx::GetCursor(new_cursor) : NULL; 514 gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(window_)), 515 frame_cursor_); 516 } 517 return FALSE; 518} 519 520gboolean NativeAppWindowGtk::OnButtonPress(GtkWidget* widget, 521 GdkEventButton* event) { 522 DCHECK(frameless_); 523 // Make the button press coordinate relative to the browser window. 524 int win_x, win_y; 525 GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window_)); 526 gdk_window_get_origin(gdk_window, &win_x, &win_y); 527 528 GdkWindowEdge edge; 529 gfx::Point point(static_cast<int>(event->x_root - win_x), 530 static_cast<int>(event->y_root - win_y)); 531 bool has_hit_edge = resizable_ && GetWindowEdge(point.x(), point.y(), &edge); 532 bool has_hit_titlebar = 533 draggable_region_ && draggable_region_->contains(event->x, event->y); 534 535 if (event->button == 1) { 536 if (GDK_BUTTON_PRESS == event->type) { 537 // Raise the window after a click on either the titlebar or the border to 538 // match the behavior of most window managers, unless that behavior has 539 // been suppressed. 540 if ((has_hit_titlebar || has_hit_edge) && !suppress_window_raise_) 541 gdk_window_raise(GTK_WIDGET(widget)->window); 542 543 if (has_hit_edge) { 544 gtk_window_begin_resize_drag(window_, edge, event->button, 545 static_cast<gint>(event->x_root), 546 static_cast<gint>(event->y_root), 547 event->time); 548 return TRUE; 549 } else if (has_hit_titlebar) { 550 return gtk_window_util::HandleTitleBarLeftMousePress( 551 window_, bounds_, event); 552 } 553 } else if (GDK_2BUTTON_PRESS == event->type) { 554 if (has_hit_titlebar && resizable_) { 555 // Maximize/restore on double click. 556 if (IsMaximized()) { 557 gtk_window_util::UnMaximize(GTK_WINDOW(widget), 558 bounds_, restored_bounds_); 559 } else { 560 gtk_window_maximize(window_); 561 } 562 return TRUE; 563 } 564 } 565 } else if (event->button == 2) { 566 if (has_hit_titlebar || has_hit_edge) 567 gdk_window_lower(gdk_window); 568 return TRUE; 569 } 570 571 return FALSE; 572} 573 574// NativeAppWindow implementation: 575 576void NativeAppWindowGtk::SetFullscreen(bool fullscreen) { 577 content_thinks_its_fullscreen_ = fullscreen; 578 if (fullscreen){ 579 if (resizable_) { 580 gtk_window_fullscreen(window_); 581 } else { 582 // We must first make the window resizable. That won't take effect 583 // immediately, so OnConfigureDebounced completes the fullscreen call. 584 gtk_window_set_resizable(window_, TRUE); 585 } 586 } else { 587 gtk_window_unfullscreen(window_); 588 if (!resizable_) 589 gtk_window_set_resizable(window_, FALSE); 590 } 591} 592 593bool NativeAppWindowGtk::IsFullscreenOrPending() const { 594 // |content_thinks_its_fullscreen_| is used when transitioning, and when 595 // the state change will not be made for some time. However, it is possible 596 // for a state update to be made before the final fullscreen state comes. 597 // In that case, |content_thinks_its_fullscreen_| will be cleared, but we 598 // will fall back to |IsFullscreen| which will soon have the correct state. 599 return content_thinks_its_fullscreen_ || IsFullscreen(); 600} 601 602bool NativeAppWindowGtk::IsDetached() const { 603 return false; 604} 605 606void NativeAppWindowGtk::UpdateWindowIcon() { 607 Profile* profile = shell_window_->profile(); 608 gfx::Image app_icon = shell_window_->app_icon(); 609 if (!app_icon.IsEmpty()) 610 gtk_util::SetWindowIcon(window_, profile, app_icon.ToGdkPixbuf()); 611 else 612 gtk_util::SetWindowIcon(window_, profile); 613} 614 615void NativeAppWindowGtk::UpdateWindowTitle() { 616 string16 title = shell_window_->GetTitle(); 617 gtk_window_set_title(window_, UTF16ToUTF8(title).c_str()); 618} 619 620void NativeAppWindowGtk::UpdateDraggableRegions( 621 const std::vector<extensions::DraggableRegion>& regions) { 622 // Draggable region is not supported for non-frameless window. 623 if (!frameless_) 624 return; 625 626 draggable_region_.reset(ShellWindow::RawDraggableRegionsToSkRegion(regions)); 627} 628 629SkRegion* NativeAppWindowGtk::GetDraggableRegion() { 630 return draggable_region_.get(); 631} 632 633void NativeAppWindowGtk::UpdateInputRegion(scoped_ptr<SkRegion> region) { 634 NOTIMPLEMENTED(); 635} 636 637void NativeAppWindowGtk::HandleKeyboardEvent( 638 const content::NativeWebKeyboardEvent& event) { 639 // No-op. 640} 641 642bool NativeAppWindowGtk::IsFrameless() const { 643 return frameless_; 644} 645 646gfx::Insets NativeAppWindowGtk::GetFrameInsets() const { 647 if (frameless_) 648 return gfx::Insets(); 649 GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window_)); 650 if (!gdk_window) 651 return gfx::Insets(); 652 653 gint current_width = 0; 654 gint current_height = 0; 655 gtk_window_get_size(window_, ¤t_width, ¤t_height); 656 gint current_x = 0; 657 gint current_y = 0; 658 gdk_window_get_position(gdk_window, ¤t_x, ¤t_y); 659 GdkRectangle rect_with_decorations = {0}; 660 gdk_window_get_frame_extents(gdk_window, 661 &rect_with_decorations); 662 663 int left_inset = current_x - rect_with_decorations.x; 664 int top_inset = current_y - rect_with_decorations.y; 665 return gfx::Insets( 666 top_inset, 667 left_inset, 668 rect_with_decorations.height - current_height - top_inset, 669 rect_with_decorations.width - current_width - left_inset); 670} 671 672bool NativeAppWindowGtk::IsVisible() const { 673 return gtk_widget_get_visible(GTK_WIDGET(window_)); 674} 675 676void NativeAppWindowGtk::HideWithApp() {} 677void NativeAppWindowGtk::ShowWithApp() {} 678