text: // Copyright (c) 2011 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "ui/gfx/gtk_native_view_id_manager.h"
  
  #include <gtk/gtk.h>
  #include <gdk/gdkx.h>
  
  #include "base/logging.h"
  #include "base/rand_util.h"
  #include "ui/gfx/gtk_preserve_window.h"
  
  // -----------------------------------------------------------------------------
  // Bounce functions for GTK to callback into a C++ object...
  
  void OnRealize(gfx::NativeView widget, void* arg) {
    GtkNativeViewManager* manager = reinterpret_cast<GtkNativeViewManager*>(arg);
    manager->OnRealize(widget);
  }
  
  void OnUnrealize(gfx::NativeView widget, void *arg) {
    GtkNativeViewManager* manager = reinterpret_cast<GtkNativeViewManager*>(arg);
    manager->OnUnrealize(widget);
  }
  
  static void OnDestroy(GtkObject* obj, void* arg) {
    GtkNativeViewManager* manager = reinterpret_cast<GtkNativeViewManager*>(arg);
    manager->OnDestroy(reinterpret_cast<GtkWidget*>(obj));
  }
  
  // -----------------------------------------------------------------------------
  
  
  // -----------------------------------------------------------------------------
  // Public functions...
  
  GtkNativeViewManager::GtkNativeViewManager() {
  }
  
  GtkNativeViewManager::~GtkNativeViewManager() {
  }
  
  // static
  GtkNativeViewManager* GtkNativeViewManager::GetInstance() {
    return Singleton<GtkNativeViewManager>::get();
  }
  
  gfx::NativeViewId GtkNativeViewManager::GetIdForWidget(gfx::NativeView widget) {
    // This is just for unit tests:
    if (!widget)
      return 0;
  
    base::AutoLock locked(lock_);
  
    std::map<gfx::NativeView, gfx::NativeViewId>::const_iterator i =
      native_view_to_id_.find(widget);
  
    if (i != native_view_to_id_.end())
      return i->second;
  
    gfx::NativeViewId new_id =
        static_cast<gfx::NativeViewId>(base::RandUint64());
    while (id_to_info_.find(new_id) != id_to_info_.end())
      new_id = static_cast<gfx::NativeViewId>(base::RandUint64());
  
    NativeViewInfo info;
    info.widget = widget;
    if (GTK_WIDGET_REALIZED(widget)) {
      GdkWindow *gdk_window = widget->window;
      DCHECK(gdk_window);
      info.x_window_id = GDK_WINDOW_XID(gdk_window);
    }
  
    native_view_to_id_[widget] = new_id;
    id_to_info_[new_id] = info;
  
    g_signal_connect(widget, "realize", G_CALLBACK(::OnRealize), this);
    g_signal_connect(widget, "unrealize", G_CALLBACK(::OnUnrealize), this);
    g_signal_connect(widget, "destroy", G_CALLBACK(::OnDestroy), this);
  
    return new_id;
  }
  
  bool GtkNativeViewManager::GetXIDForId(XID* output, gfx::NativeViewId id) {
    base::AutoLock locked(lock_);
  
    std::map<gfx::NativeViewId, NativeViewInfo>::const_iterator i =
      id_to_info_.find(id);
  
    if (i == id_to_info_.end())
      return false;
  
    *output = i->second.x_window_id;
    return true;
  }
  
  bool GtkNativeViewManager::GetPermanentXIDForId(XID* output,
                                                  gfx::NativeViewId id) {
    base::AutoLock locked(lock_);
  
    std::map<gfx::NativeViewId, NativeViewInfo>::iterator i =
        id_to_info_.find(id);
  
    if (i == id_to_info_.end())
      return false;
  
    // We only return permanent XIDs for widgets that allow us to guarantee that
    // the XID will not change.
    DCHECK(GTK_IS_PRESERVE_WINDOW(i->second.widget));
    GtkPreserveWindow* widget =
        reinterpret_cast<GtkPreserveWindow*>(i->second.widget);
    gtk_preserve_window_set_preserve(widget, TRUE);
  
    *output = GDK_WINDOW_XID(i->second.widget->window);
  
    // Update the reference count on the permanent XID.
    PermanentXIDInfo info;
    info.widget = widget;
    info.ref_count = 1;
    std::pair<std::map<XID, PermanentXIDInfo>::iterator, bool> ret =
      perm_xid_to_info_.insert(std::make_pair(*output, info));
  
    if (!ret.second) {
      DCHECK(ret.first->second.widget == widget);
      ret.first->second.ref_count++;
    }
  
    return true;
  }
  
  bool GtkNativeViewManager::AddRefPermanentXID(XID xid) {
    base::AutoLock locked(lock_);
  
    std::map<XID, PermanentXIDInfo>::iterator i =
      perm_xid_to_info_.find(xid);
  
    if (i == perm_xid_to_info_.end())
      return false;
  
    i->second.ref_count++;
  
    return true;
  }
  
  void GtkNativeViewManager::ReleasePermanentXID(XID xid) {
    base::AutoLock locked(lock_);
  
    std::map<XID, PermanentXIDInfo>::iterator i =
      perm_xid_to_info_.find(xid);
  
    if (i == perm_xid_to_info_.end())
      return;
  
    if (i->second.ref_count > 1) {
      i->second.ref_count--;
    } else {
      if (i->second.widget) {
        gtk_preserve_window_set_preserve(i->second.widget, FALSE);
      } else {
        GdkWindow* window = reinterpret_cast<GdkWindow*>(
            gdk_xid_table_lookup(xid));
        DCHECK(window);
        gdk_window_destroy(window);
      }
      perm_xid_to_info_.erase(i);
    }
  }
  
  // -----------------------------------------------------------------------------
  
  
  // -----------------------------------------------------------------------------
  // Private functions...
  
  gfx::NativeViewId GtkNativeViewManager::GetWidgetId(gfx::NativeView widget) {
    lock_.AssertAcquired();
  
    std::map<gfx::NativeView, gfx::NativeViewId>::const_iterator i =
      native_view_to_id_.find(widget);
  
    CHECK(i != native_view_to_id_.end());
    return i->second;
  }
  
  void GtkNativeViewManager::OnRealize(gfx::NativeView widget) {
    base::AutoLock locked(lock_);
  
    const gfx::NativeViewId id = GetWidgetId(widget);
    std::map<gfx::NativeViewId, NativeViewInfo>::iterator i =
      id_to_info_.find(id);
  
    CHECK(i != id_to_info_.end());
    CHECK(widget->window);
  
    i->second.x_window_id = GDK_WINDOW_XID(widget->window);
  }
  
  void GtkNativeViewManager::OnUnrealize(gfx::NativeView widget) {
    base::AutoLock unrealize_locked(unrealize_lock_);
    base::AutoLock locked(lock_);
  
    const gfx::NativeViewId id = GetWidgetId(widget);
    std::map<gfx::NativeViewId, NativeViewInfo>::iterator i =
      id_to_info_.find(id);
  
    CHECK(i != id_to_info_.end());
  }
  
  void GtkNativeViewManager::OnDestroy(gfx::NativeView widget) {
    base::AutoLock locked(lock_);
  
    std::map<gfx::NativeView, gfx::NativeViewId>::iterator i =
      native_view_to_id_.find(widget);
    CHECK(i != native_view_to_id_.end());
  
    std::map<gfx::NativeViewId, NativeViewInfo>::iterator j =
      id_to_info_.find(i->second);
    CHECK(j != id_to_info_.end());
  
    // If the XID is supposed to outlive the widget, mark it
    // in the lookup table.
    if (GTK_IS_PRESERVE_WINDOW(widget) &&
        gtk_preserve_window_get_preserve(
            reinterpret_cast<GtkPreserveWindow*>(widget))) {
      std::map<XID, PermanentXIDInfo>::iterator k =
        perm_xid_to_info_.find(GDK_WINDOW_XID(widget->window));
  
      if (k != perm_xid_to_info_.end())
        k->second.widget = NULL;
    }
  
    native_view_to_id_.erase(i);
    id_to_info_.erase(j);
  }
  
  // -----------------------------------------------------------------------------
  