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.
5 #include "chrome/browser/ui/gtk/apps/native_app_window_gtk.h"
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 "content/public/browser/render_view_host.h"
18 #include "content/public/browser/render_widget_host_view.h"
19 #include "content/public/browser/web_contents.h"
20 #include "content/public/browser/web_contents_view.h"
21 #include "extensions/common/extension.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"
27 using apps::ShellWindow
;
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.
33 const int kDebounceTimeoutMilliseconds
= 100;
35 const char* kAtomsToCache
[] = {
37 "_NET_WM_STATE_HIDDEN",
43 NativeAppWindowGtk::NativeAppWindowGtk(ShellWindow
* shell_window
,
44 const ShellWindow::CreateParams
& params
)
45 : shell_window_(shell_window
),
47 state_(GDK_WINDOW_STATE_WITHDRAWN
),
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
),
54 atom_cache_(base::MessagePumpGtk::GetDefaultXDisplay(), kAtomsToCache
),
55 is_x_event_listened_(false) {
56 Observe(web_contents());
58 window_
= GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL
));
60 gfx::NativeView native_view
=
61 web_contents()->GetView()->GetNativeView();
62 gtk_container_add(GTK_CONTAINER(window_
), native_view
);
64 if (params
.bounds
.x() != INT_MIN
&& params
.bounds
.y() != INT_MIN
)
65 gtk_window_move(window_
, params
.bounds
.x(), params
.bounds
.y());
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();
71 gtk_window_util::BoundsMatchMonitorSize(window_
, params
.bounds
)) {
74 gtk_window_set_default_size(window_
, params
.bounds
.width(), win_height
);
76 resizable_
= params
.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
);
85 // make sure bounds_ and restored_bounds_ have correct values until we
86 // get our first configure-event
87 bounds_
= restored_bounds_
= params
.bounds
;
89 gtk_window_get_position(window_
, &x
, &y
);
90 bounds_
.set_origin(gfx::Point(x
, y
));
92 // Hide titlebar when {frame: 'none'} specified on ShellWindow.
94 gtk_window_set_decorated(window_
, false);
97 gtk_window_set_keep_above(window_
, TRUE
);
99 UpdateWindowMinMaxSize();
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;
108 gtk_window_set_title(window_
, extension()->name().c_str());
110 std::string app_name
= web_app::GenerateApplicationNameFromExtensionId(
112 gtk_window_util::SetWindowCustomClass(window_
,
113 web_app::GetWMClassFromAppName(app_name
));
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);
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);
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
131 std::vector
< ::Atom
> supported_atoms
;
132 if (ui::GetAtomArrayProperty(ui::GetX11RootWindow(),
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
,
143 is_x_event_listened_
= true;
147 // Add the keybinding registry.
148 extension_keybinding_registry_
.reset(new ExtensionKeybindingRegistryGtk(
149 shell_window_
->profile(),
151 extensions::ExtensionKeybindingRegistry::PLATFORM_APPS_ONLY
,
154 ui::ActiveWindowWatcherX::AddObserver(this);
157 NativeAppWindowGtk::~NativeAppWindowGtk() {
158 ui::ActiveWindowWatcherX::RemoveObserver(this);
159 if (is_x_event_listened_
) {
160 gdk_window_remove_filter(NULL
,
161 &NativeAppWindowGtk::OnXEventThunk
,
166 bool NativeAppWindowGtk::IsActive() const {
167 if (ui::ActiveWindowWatcherX::WMSupportsActivation())
170 // This still works even though we don't get the activation notification.
171 return gtk_window_is_active(window_
);
174 bool NativeAppWindowGtk::IsMaximized() const {
175 return (state_
& GDK_WINDOW_STATE_MAXIMIZED
);
178 bool NativeAppWindowGtk::IsMinimized() const {
179 return (state_
& GDK_WINDOW_STATE_ICONIFIED
);
182 bool NativeAppWindowGtk::IsFullscreen() const {
183 return (state_
& GDK_WINDOW_STATE_FULLSCREEN
);
186 gfx::NativeWindow
NativeAppWindowGtk::GetNativeWindow() {
190 gfx::Rect
NativeAppWindowGtk::GetRestoredBounds() const {
191 gfx::Rect window_bounds
= restored_bounds_
;
192 window_bounds
.Inset(-GetFrameInsets());
193 return window_bounds
;
196 ui::WindowShowState
NativeAppWindowGtk::GetRestoredState() const {
198 return ui::SHOW_STATE_MAXIMIZED
;
200 return ui::SHOW_STATE_FULLSCREEN
;
201 return ui::SHOW_STATE_NORMAL
;
204 gfx::Rect
NativeAppWindowGtk::GetBounds() const {
205 // :GetBounds() is expecting the outer window bounds to be returned (ie.
206 // including window decorations). The internal |bounds_| is not including them
207 // and trying to add the decoration to |bounds_| would require calling
208 // gdk_window_get_frame_extents. The best thing to do is to directly get the
209 // frame bounds and only use the internal value if we can't.
210 GdkWindow
* gdk_window
= gtk_widget_get_window(GTK_WIDGET(window_
));
214 GdkRectangle window_bounds
= {0};
215 gdk_window_get_frame_extents(gdk_window
, &window_bounds
);
216 return gfx::Rect(window_bounds
.x
, window_bounds
.y
,
217 window_bounds
.width
, window_bounds
.height
);
220 void NativeAppWindowGtk::Show() {
221 gtk_window_present(window_
);
224 void NativeAppWindowGtk::ShowInactive() {
225 gtk_window_set_focus_on_map(window_
, false);
226 gtk_widget_show(GTK_WIDGET(window_
));
229 void NativeAppWindowGtk::Hide() {
230 gtk_widget_hide(GTK_WIDGET(window_
));
233 void NativeAppWindowGtk::Close() {
234 shell_window_
->OnNativeWindowChanged();
236 // Cancel any pending callback from the window configure debounce timer.
237 window_configure_debounce_timer_
.Stop();
239 GtkWidget
* window
= GTK_WIDGET(window_
);
240 // To help catch bugs in any event handlers that might get fired during the
241 // destruction, set window_ to NULL before any handlers will run.
244 // OnNativeClose does a delete this so no other members should
245 // be accessed after. gtk_widget_destroy is safe (and must
247 shell_window_
->OnNativeClose();
248 gtk_widget_destroy(window
);
251 void NativeAppWindowGtk::Activate() {
252 gtk_window_present(window_
);
255 void NativeAppWindowGtk::Deactivate() {
256 gdk_window_lower(gtk_widget_get_window(GTK_WIDGET(window_
)));
259 void NativeAppWindowGtk::Maximize() {
260 // Represent the window first in order to keep the maximization behavior
261 // consistency with Windows platform. Otherwise the window will be hidden if
262 // it has been minimized.
263 gtk_window_present(window_
);
266 // When the window is not resizable, we still want to make this call succeed
267 // but gtk will not allow it if the window is not resizable. The actual
268 // maximization will happen with the subsequent OnConfigureDebounced call,
269 // that will be triggered when the window manager's resizable property
271 maximize_pending_
= true;
272 gtk_window_set_resizable(window_
, TRUE
);
274 gtk_window_maximize(window_
);
278 void NativeAppWindowGtk::Minimize() {
279 gtk_window_iconify(window_
);
282 void NativeAppWindowGtk::Restore() {
284 gtk_window_unmaximize(window_
);
285 else if (IsMinimized())
286 gtk_window_deiconify(window_
);
288 // Represent the window to keep restoration behavior consistency with Windows
290 // TODO(zhchbin): verify whether we need this until http://crbug.com/261013 is
292 gtk_window_present(window_
);
295 void NativeAppWindowGtk::SetBounds(const gfx::Rect
& bounds
) {
296 gfx::Rect content_bounds
= bounds
;
297 gtk_window_move(window_
, content_bounds
.x(), content_bounds
.y());
300 gtk_window_util::BoundsMatchMonitorSize(window_
, content_bounds
)) {
301 content_bounds
.set_height(content_bounds
.height() - 1);
303 // TODO(jeremya): set_size_request doesn't honor min/max size, so the
304 // bounds should be constrained manually.
305 gtk_widget_set_size_request(GTK_WIDGET(window_
),
306 content_bounds
.width(), content_bounds
.height());
308 gtk_window_util::SetWindowSize(window_
,
309 gfx::Size(bounds
.width(), bounds
.height()));
313 GdkFilterReturn
NativeAppWindowGtk::OnXEvent(GdkXEvent
* gdk_x_event
,
314 GdkEvent
* gdk_event
) {
315 // Work around GTK+ not reporting minimization state changes. Listen
316 // for _NET_WM_STATE property changes and use _NET_WM_STATE_HIDDEN's
317 // presence to set or clear the iconified bit if _NET_WM_STATE_HIDDEN
318 // is supported. http://crbug.com/162794.
319 XEvent
* x_event
= static_cast<XEvent
*>(gdk_x_event
);
320 std::vector
< ::Atom
> atom_list
;
322 if (x_event
->type
== PropertyNotify
&&
323 x_event
->xproperty
.atom
== atom_cache_
.GetAtom("_NET_WM_STATE") &&
324 GTK_WIDGET(window_
)->window
&&
325 ui::GetAtomArrayProperty(GDK_WINDOW_XWINDOW(GTK_WIDGET(window_
)->window
),
328 std::vector
< ::Atom
>::iterator it
=
329 std::find(atom_list
.begin(),
331 atom_cache_
.GetAtom("_NET_WM_STATE_HIDDEN"));
333 GdkWindowState previous_state
= state_
;
334 state_
= (it
!= atom_list
.end()) ? GDK_WINDOW_STATE_ICONIFIED
:
335 static_cast<GdkWindowState
>(state_
& ~GDK_WINDOW_STATE_ICONIFIED
);
337 if (previous_state
!= state_
) {
338 shell_window_
->OnNativeWindowChanged();
342 return GDK_FILTER_CONTINUE
;
345 void NativeAppWindowGtk::FlashFrame(bool flash
) {
346 gtk_window_set_urgency_hint(window_
, flash
);
349 bool NativeAppWindowGtk::IsAlwaysOnTop() const {
350 return always_on_top_
;
353 void NativeAppWindowGtk::RenderViewHostChanged(
354 content::RenderViewHost
* old_host
,
355 content::RenderViewHost
* new_host
) {
356 web_contents()->GetView()->Focus();
359 void NativeAppWindowGtk::SetAlwaysOnTop(bool always_on_top
) {
360 if (always_on_top_
!= always_on_top
) {
361 // gdk_window_get_state() does not give us the correct value for the
362 // GDK_WINDOW_STATE_ABOVE bit. Cache the current state.
363 always_on_top_
= always_on_top
;
364 gtk_window_set_keep_above(window_
, always_on_top_
? TRUE
: FALSE
);
368 gfx::NativeView
NativeAppWindowGtk::GetHostView() const {
373 gfx::Point
NativeAppWindowGtk::GetDialogPosition(const gfx::Size
& size
) {
374 gint current_width
= 0;
375 gint current_height
= 0;
376 gtk_window_get_size(window_
, ¤t_width
, ¤t_height
);
377 return gfx::Point(current_width
/ 2 - size
.width() / 2,
378 current_height
/ 2 - size
.height() / 2);
381 gfx::Size
NativeAppWindowGtk::GetMaximumDialogSize() {
382 gint current_width
= 0;
383 gint current_height
= 0;
384 gtk_window_get_size(window_
, ¤t_width
, ¤t_height
);
385 return gfx::Size(current_width
, current_height
);
388 void NativeAppWindowGtk::AddObserver(
389 web_modal::ModalDialogHostObserver
* observer
) {
390 observer_list_
.AddObserver(observer
);
393 void NativeAppWindowGtk::RemoveObserver(
394 web_modal::ModalDialogHostObserver
* observer
) {
395 observer_list_
.RemoveObserver(observer
);
398 void NativeAppWindowGtk::ActiveWindowChanged(GdkWindow
* active_window
) {
399 // Do nothing if we're in the process of closing the browser window.
403 is_active_
= gtk_widget_get_window(GTK_WIDGET(window_
)) == active_window
;
405 shell_window_
->OnNativeWindowActivated();
408 // Callback for the delete event. This event is fired when the user tries to
409 // close the window (e.g., clicking on the X in the window manager title bar).
410 gboolean
NativeAppWindowGtk::OnMainWindowDeleteEvent(GtkWidget
* widget
,
414 // Return true to prevent the GTK window from being destroyed. Close will
415 // destroy it for us.
419 gboolean
NativeAppWindowGtk::OnConfigure(GtkWidget
* widget
,
420 GdkEventConfigure
* event
) {
421 // We update |bounds_| but not |restored_bounds_| here. The latter needs
422 // to be updated conditionally when the window is non-maximized and non-
423 // fullscreen, but whether those state updates have been processed yet is
424 // window-manager specific. We update |restored_bounds_| in the debounced
425 // handler below, after the window state has been updated.
426 bounds_
.SetRect(event
->x
, event
->y
, event
->width
, event
->height
);
428 // The GdkEventConfigure* we get here doesn't have quite the right
429 // coordinates though (they're relative to the drawable window area, rather
430 // than any window manager decorations, if enabled), so we need to call
431 // gtk_window_get_position() to get the right values. (Otherwise session
432 // restore, if enabled, will restore windows to incorrect positions.) That's
433 // a round trip to the X server though, so we set a debounce timer and only
434 // call it (in OnConfigureDebounced() below) after we haven't seen a
435 // reconfigure event in a short while.
436 // We don't use Reset() because the timer may not yet be running.
437 // (In that case Stop() is a no-op.)
438 window_configure_debounce_timer_
.Stop();
439 window_configure_debounce_timer_
.Start(FROM_HERE
,
440 base::TimeDelta::FromMilliseconds(kDebounceTimeoutMilliseconds
), this,
441 &NativeAppWindowGtk::OnConfigureDebounced
);
446 void NativeAppWindowGtk::OnConfigureDebounced() {
447 gtk_window_util::UpdateWindowPosition(this, &bounds_
, &restored_bounds_
);
448 shell_window_
->OnNativeWindowChanged();
450 FOR_EACH_OBSERVER(web_modal::ModalDialogHostObserver
,
452 OnPositionRequiresUpdate());
454 // Fullscreen of non-resizable windows requires them to be made resizable
455 // first. After that takes effect and OnConfigure is called we transition
457 if (!IsFullscreen() && IsFullscreenOrPending()) {
458 gtk_window_fullscreen(window_
);
461 // maximize_pending_ is the boolean that lets us know that the window is in
462 // the process of being maximized but was set as not resizable.
463 // This function will be called twice during the maximization process:
464 // 1. gtk_window_maximize() is called to maximize the window;
465 // 2. gtk_set_resizable(, FALSE) is called to make the window no longer
467 // gtk_window_maximize() will cause ::OnConfigureDebounced to be called
468 // again, at which time we will run into the second step.
469 if (maximize_pending_
) {
470 if (!(state_
& GDK_WINDOW_STATE_MAXIMIZED
)) {
471 gtk_window_maximize(window_
);
473 maximize_pending_
= false;
475 gtk_window_set_resizable(window_
, FALSE
);
480 gboolean
NativeAppWindowGtk::OnWindowState(GtkWidget
* sender
,
481 GdkEventWindowState
* event
) {
482 state_
= event
->new_window_state
;
484 if (content_thinks_its_fullscreen_
&&
485 !(state_
& GDK_WINDOW_STATE_FULLSCREEN
)) {
486 content_thinks_its_fullscreen_
= false;
487 content::RenderViewHost
* rvh
= web_contents()->GetRenderViewHost();
489 rvh
->ExitFullscreen();
495 bool NativeAppWindowGtk::GetWindowEdge(int x
, int y
, GdkWindowEdge
* edge
) {
499 if (IsMaximized() || IsFullscreen())
502 return gtk_window_util::GetWindowEdge(bounds_
.size(), 0, x
, y
, edge
);
505 gboolean
NativeAppWindowGtk::OnMouseMoveEvent(GtkWidget
* widget
,
506 GdkEventMotion
* event
) {
510 frame_cursor_
= NULL
;
511 gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(window_
)), NULL
);
519 // Update the cursor if we're on the custom frame border.
521 bool has_hit_edge
= GetWindowEdge(static_cast<int>(event
->x
),
522 static_cast<int>(event
->y
), &edge
);
523 GdkCursorType new_cursor
= GDK_LAST_CURSOR
;
525 new_cursor
= gtk_window_util::GdkWindowEdgeToGdkCursorType(edge
);
527 GdkCursorType last_cursor
= GDK_LAST_CURSOR
;
529 last_cursor
= frame_cursor_
->type
;
531 if (last_cursor
!= new_cursor
) {
532 frame_cursor_
= has_hit_edge
? gfx::GetCursor(new_cursor
) : NULL
;
533 gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(window_
)),
539 gboolean
NativeAppWindowGtk::OnButtonPress(GtkWidget
* widget
,
540 GdkEventButton
* event
) {
542 // Make the button press coordinate relative to the browser window.
544 GdkWindow
* gdk_window
= gtk_widget_get_window(GTK_WIDGET(window_
));
545 gdk_window_get_origin(gdk_window
, &win_x
, &win_y
);
548 gfx::Point
point(static_cast<int>(event
->x_root
- win_x
),
549 static_cast<int>(event
->y_root
- win_y
));
550 bool has_hit_edge
= resizable_
&& GetWindowEdge(point
.x(), point
.y(), &edge
);
551 bool has_hit_titlebar
=
552 draggable_region_
&& draggable_region_
->contains(event
->x
, event
->y
);
554 if (event
->button
== 1) {
555 if (GDK_BUTTON_PRESS
== event
->type
) {
556 // Raise the window after a click on either the titlebar or the border to
557 // match the behavior of most window managers, unless that behavior has
559 if ((has_hit_titlebar
|| has_hit_edge
) && !suppress_window_raise_
)
560 gdk_window_raise(GTK_WIDGET(widget
)->window
);
563 gtk_window_begin_resize_drag(window_
, edge
, event
->button
,
564 static_cast<gint
>(event
->x_root
),
565 static_cast<gint
>(event
->y_root
),
568 } else if (has_hit_titlebar
) {
569 return gtk_window_util::HandleTitleBarLeftMousePress(
570 window_
, bounds_
, event
);
572 } else if (GDK_2BUTTON_PRESS
== event
->type
) {
573 if (has_hit_titlebar
&& resizable_
) {
574 // Maximize/restore on double click.
576 gtk_window_util::UnMaximize(GTK_WINDOW(widget
),
577 bounds_
, restored_bounds_
);
579 gtk_window_maximize(window_
);
584 } else if (event
->button
== 2) {
585 if (has_hit_titlebar
|| has_hit_edge
)
586 gdk_window_lower(gdk_window
);
593 // NativeAppWindow implementation:
595 void NativeAppWindowGtk::SetFullscreen(int fullscreen_types
) {
596 bool fullscreen
= (fullscreen_types
!= ShellWindow::FULLSCREEN_TYPE_NONE
);
597 content_thinks_its_fullscreen_
= fullscreen
;
600 gtk_window_fullscreen(window_
);
602 // We must first make the window resizable. That won't take effect
603 // immediately, so OnConfigureDebounced completes the fullscreen call.
604 gtk_window_set_resizable(window_
, TRUE
);
607 gtk_window_unfullscreen(window_
);
609 gtk_window_set_resizable(window_
, FALSE
);
613 bool NativeAppWindowGtk::IsFullscreenOrPending() const {
614 // |content_thinks_its_fullscreen_| is used when transitioning, and when
615 // the state change will not be made for some time. However, it is possible
616 // for a state update to be made before the final fullscreen state comes.
617 // In that case, |content_thinks_its_fullscreen_| will be cleared, but we
618 // will fall back to |IsFullscreen| which will soon have the correct state.
619 return content_thinks_its_fullscreen_
|| IsFullscreen();
622 bool NativeAppWindowGtk::IsDetached() const {
626 void NativeAppWindowGtk::UpdateWindowIcon() {
627 Profile
* profile
= shell_window_
->profile();
628 gfx::Image app_icon
= shell_window_
->app_icon();
629 if (!app_icon
.IsEmpty())
630 gtk_util::SetWindowIcon(window_
, profile
, app_icon
.ToGdkPixbuf());
632 gtk_util::SetWindowIcon(window_
, profile
);
635 void NativeAppWindowGtk::UpdateWindowTitle() {
636 base::string16 title
= shell_window_
->GetTitle();
637 gtk_window_set_title(window_
, base::UTF16ToUTF8(title
).c_str());
640 void NativeAppWindowGtk::UpdateDraggableRegions(
641 const std::vector
<extensions::DraggableRegion
>& regions
) {
642 // Draggable region is not supported for non-frameless window.
646 draggable_region_
.reset(ShellWindow::RawDraggableRegionsToSkRegion(regions
));
649 SkRegion
* NativeAppWindowGtk::GetDraggableRegion() {
650 return draggable_region_
.get();
653 void NativeAppWindowGtk::UpdateShape(scoped_ptr
<SkRegion
> region
) {
657 void NativeAppWindowGtk::HandleKeyboardEvent(
658 const content::NativeWebKeyboardEvent
& event
) {
662 bool NativeAppWindowGtk::IsFrameless() const {
666 gfx::Insets
NativeAppWindowGtk::GetFrameInsets() const {
668 return gfx::Insets();
669 GdkWindow
* gdk_window
= gtk_widget_get_window(GTK_WIDGET(window_
));
671 return gfx::Insets();
673 gint current_width
= 0;
674 gint current_height
= 0;
675 gtk_window_get_size(window_
, ¤t_width
, ¤t_height
);
678 gdk_window_get_position(gdk_window
, ¤t_x
, ¤t_y
);
679 GdkRectangle rect_with_decorations
= {0};
680 gdk_window_get_frame_extents(gdk_window
,
681 &rect_with_decorations
);
683 int left_inset
= current_x
- rect_with_decorations
.x
;
684 int top_inset
= current_y
- rect_with_decorations
.y
;
688 rect_with_decorations
.height
- current_height
- top_inset
,
689 rect_with_decorations
.width
- current_width
- left_inset
);
692 void NativeAppWindowGtk::HideWithApp() {}
693 void NativeAppWindowGtk::ShowWithApp() {}
695 void NativeAppWindowGtk::UpdateWindowMinMaxSize() {
698 if (shell_window_
->size_constraints().HasMinimumSize()) {
699 gfx::Size min_size
= shell_window_
->size_constraints().GetMinimumSize();
700 hints
.min_height
= min_size
.height();
701 hints
.min_width
= min_size
.width();
702 hints_mask
|= GDK_HINT_MIN_SIZE
;
704 if (shell_window_
->size_constraints().HasMaximumSize()) {
705 gfx::Size max_size
= shell_window_
->size_constraints().GetMaximumSize();
706 const int kUnboundedSize
= ShellWindow::SizeConstraints::kUnboundedSize
;
707 hints
.max_height
= max_size
.height() == kUnboundedSize
?
708 G_MAXINT
: max_size
.height();
709 hints
.max_width
= max_size
.width() == kUnboundedSize
?
710 G_MAXINT
: max_size
.width();
711 hints_mask
|= GDK_HINT_MAX_SIZE
;
714 gtk_window_set_geometry_hints(
718 static_cast<GdkWindowHints
>(hints_mask
));