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