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 "chrome/browser/fullscreen.h"
6
7#include <gdk/gdk.h>
8#include <gdk/gdkx.h>
9
10#include <algorithm>
11#include <vector>
12
13#include "base/basictypes.h"
14#include "ui/base/x/x11_util.h"
15#include "ui/gfx/rect.h"
16
17namespace {
18
19// TODO (jianli): Merge with ui::EnumerateTopLevelWindows.
20void EnumerateAllChildWindows(ui::EnumerateWindowsDelegate* delegate,
21                              XID window) {
22  std::vector<XID> windows;
23
24  if (!ui::GetXWindowStack(window, &windows)) {
25    // Window Manager doesn't support _NET_CLIENT_LIST_STACKING, so fall back
26    // to old school enumeration of all X windows.
27    XID root, parent, *children;
28    unsigned int num_children;
29    int status = XQueryTree(ui::GetXDisplay(), window, &root, &parent,
30                            &children, &num_children);
31    if (status) {
32      for (long i = static_cast<long>(num_children) - 1; i >= 0; i--)
33        windows.push_back(children[i]);
34      XFree(children);
35    }
36  }
37
38  std::vector<XID>::iterator iter;
39  for (iter = windows.begin(); iter != windows.end(); iter++) {
40    if (delegate->ShouldStopIterating(*iter))
41      return;
42  }
43}
44
45// To find the top-most window:
46// 1) Enumerate all top-level windows from the top to the bottom.
47// 2) For each window:
48//    2.1) If it is hidden, continue the iteration.
49//    2.2) If it is managed by the Window Manager (has a WM_STATE property).
50//         Return this window as the top-most window.
51//    2.3) Enumerate all its child windows. If there is a child window that is
52//         managed by the Window Manager (has a WM_STATE property). Return this
53//         child window as the top-most window.
54//    2.4) Otherwise, continue the iteration.
55
56class WindowManagerWindowFinder : public ui::EnumerateWindowsDelegate {
57 public:
58  WindowManagerWindowFinder() : window_(None) { }
59
60  XID window() const { return window_; }
61
62 protected:
63  virtual bool ShouldStopIterating(XID window) OVERRIDE {
64    if (ui::PropertyExists(window, "WM_STATE")) {
65      window_ = window;
66      return true;
67    }
68    return false;
69  }
70
71 private:
72  XID window_;
73
74  DISALLOW_COPY_AND_ASSIGN(WindowManagerWindowFinder);
75};
76
77class TopMostWindowFinder : public ui::EnumerateWindowsDelegate {
78 public:
79  TopMostWindowFinder()
80      : top_most_window_(None) {}
81
82  XID top_most_window() const { return top_most_window_; }
83
84 protected:
85   virtual bool ShouldStopIterating(XID window) OVERRIDE {
86     if (!ui::IsWindowVisible(window))
87       return false;
88     if (ui::PropertyExists(window, "WM_STATE")) {
89      top_most_window_ = window;
90      return true;
91    }
92     WindowManagerWindowFinder child_finder;
93     EnumerateAllChildWindows(&child_finder, window);
94     XID child_window = child_finder.window();
95     if (child_window == None)
96       return false;
97     top_most_window_ = child_window;
98     return true;
99   }
100
101 private:
102  XID top_most_window_;
103
104  DISALLOW_COPY_AND_ASSIGN(TopMostWindowFinder);
105};
106
107bool IsTopMostWindowFullScreen() {
108  // Find the topmost window.
109  TopMostWindowFinder finder;
110  EnumerateAllChildWindows(&finder, ui::GetX11RootWindow());
111  XID window = finder.top_most_window();
112  if (window == None)
113    return false;
114
115  // Make sure it is not the desktop window.
116  static Atom desktop_atom = gdk_x11_get_xatom_by_name_for_display(
117      gdk_display_get_default(), "_NET_WM_WINDOW_TYPE_DESKTOP");
118
119  std::vector<Atom> atom_properties;
120  if (ui::GetAtomArrayProperty(window,
121                               "_NET_WM_WINDOW_TYPE",
122                               &atom_properties) &&
123      std::find(atom_properties.begin(), atom_properties.end(), desktop_atom)
124          != atom_properties.end())
125    return false;
126
127  // If it is a GDK window, check it using gdk function.
128  GdkWindow* gwindow = gdk_window_lookup(window);
129  if (gwindow && window != GDK_ROOT_WINDOW())
130    return gdk_window_get_state(gwindow) == GDK_WINDOW_STATE_FULLSCREEN;
131
132  // Otherwise, do the check via xlib function.
133  return ui::IsX11WindowFullScreen(window);
134}
135
136}
137
138bool IsFullScreenMode() {
139  gdk_error_trap_push();
140  bool result = IsTopMostWindowFullScreen();
141  bool got_error = gdk_error_trap_pop();
142  return result && !got_error;
143}
144