diff --git a/atom.gyp b/atom.gyp index c02acbd82b7b..defe6215d9cd 100644 --- a/atom.gyp +++ b/atom.gyp @@ -139,6 +139,8 @@ 'atom/browser/ui/tray_icon_cocoa.mm', 'atom/browser/ui/tray_icon_observer.h', 'atom/browser/ui/tray_icon_win.cc', + 'atom/browser/ui/views/global_menu_bar_x11.cc', + 'atom/browser/ui/views/global_menu_bar_x11.h', 'atom/browser/ui/views/linux_frame_view.cc', 'atom/browser/ui/views/linux_frame_view.h', 'atom/browser/ui/win/menu_2.cc', @@ -223,6 +225,8 @@ 'chromium_src/chrome/browser/ui/libgtk2ui/app_indicator_icon_menu.h', 'chromium_src/chrome/browser/ui/libgtk2ui/gtk2_status_icon.cc', 'chromium_src/chrome/browser/ui/libgtk2ui/gtk2_status_icon.h', + 'chromium_src/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.cc', + 'chromium_src/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.h', 'chromium_src/chrome/browser/ui/views/status_icons/status_tray_state_changer_win.cc', 'chromium_src/chrome/browser/ui/views/status_icons/status_tray_state_changer_win.h', '<@(native_mate_files)', diff --git a/atom/browser/native_window_views.cc b/atom/browser/native_window_views.cc index 9fb59d10e7f0..1ad84ba2d14a 100644 --- a/atom/browser/native_window_views.cc +++ b/atom/browser/native_window_views.cc @@ -26,7 +26,10 @@ #include "ui/views/widget/widget.h" #if defined(USE_X11) +#include "atom/browser/ui/views/global_menu_bar_x11.h" #include "atom/browser/ui/views/linux_frame_view.h" +#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" +#include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h" #endif namespace atom { @@ -71,6 +74,15 @@ NativeWindowViews::NativeWindowViews(content::WebContents* web_contents, params.top_level = true; params.remove_standard_frame = true; +#if defined(USE_X11) + // Expose DesktopWindowTreeHostX11 for GlobalMenuBarX11. + params.native_widget = new views::DesktopNativeWidgetAura(window_.get()); + host_ = new views::DesktopWindowTreeHostX11( + window_.get(), + static_cast(params.native_widget)); + params.desktop_window_tree_host = host_; +#endif + bool skip_taskbar = false; if (options.Get(switches::kSkipTaskbar, &skip_taskbar) && skip_taskbar) params.type = views::Widget::InitParams::TYPE_BUBBLE; @@ -265,6 +277,12 @@ bool NativeWindowViews::IsKiosk() { void NativeWindowViews::SetMenu(ui::MenuModel* menu_model) { // FIXME RegisterAccelerators(menu_model); + +#if defined(USE_X11) + if (!global_menu_bar_) + global_menu_bar_.reset(new GlobalMenuBarX11(this)); + global_menu_bar_->SetMenu(menu_model); +#endif } gfx::NativeWindow NativeWindowViews::GetNativeWindow() { diff --git a/atom/browser/native_window_views.h b/atom/browser/native_window_views.h index 63f69d61b500..545cd2ede60f 100644 --- a/atom/browser/native_window_views.h +++ b/atom/browser/native_window_views.h @@ -15,12 +15,15 @@ #include "ui/views/widget/widget_observer.h" namespace views { +class DesktopWindowTreeHostX11; class UnhandledKeyboardEventHandler; class Widget; } namespace atom { +class GlobalMenuBarX11; + class NativeWindowViews : public NativeWindow, public views::WidgetDelegateView, public views::WidgetObserver { @@ -72,6 +75,10 @@ class NativeWindowViews : public NativeWindow, SkRegion* draggable_region() const { return draggable_region_.get(); } views::Widget* widget() const { return window_.get(); } +#if defined(USE_X11) + views::DesktopWindowTreeHostX11* host() const { return host_; } +#endif + private: // NativeWindow: virtual void UpdateDraggableRegions( @@ -114,6 +121,11 @@ class NativeWindowViews : public NativeWindow, scoped_ptr window_; views::View* web_view_; // Managed by inspectable_web_contents_. +#if defined(USE_X11) + views::DesktopWindowTreeHostX11* host_; // Managed by native_widget. + scoped_ptr global_menu_bar_; +#endif + // Handles unhandled keyboard messages coming back from the renderer process. scoped_ptr keyboard_event_handler_; diff --git a/atom/browser/ui/views/global_menu_bar_x11.cc b/atom/browser/ui/views/global_menu_bar_x11.cc new file mode 100644 index 000000000000..05e4c51aa87e --- /dev/null +++ b/atom/browser/ui/views/global_menu_bar_x11.cc @@ -0,0 +1,302 @@ +// Copyright (c) 2014 GitHub, Inc. All rights reserved. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "atom/browser/ui/views/global_menu_bar_x11.h" + +#include + +#include +#include + +#include "atom/browser/native_window_views.h" +#include "base/logging.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.h" +#include "ui/aura/window.h" +#include "ui/aura/window_tree_host.h" +#include "ui/base/accelerators/menu_label_accelerator_util_linux.h" +#include "ui/base/models/menu_model.h" +#include "ui/events/keycodes/keyboard_code_conversion_x.h" + +// libdbusmenu-glib types +typedef struct _DbusmenuMenuitem DbusmenuMenuitem; +typedef DbusmenuMenuitem* (*dbusmenu_menuitem_new_func)(); +typedef DbusmenuMenuitem* (*dbusmenu_menuitem_new_with_id_func)(int id); + +typedef int (*dbusmenu_menuitem_get_id_func)(DbusmenuMenuitem* item); +typedef GList* (*dbusmenu_menuitem_get_children_func)(DbusmenuMenuitem* item); +typedef DbusmenuMenuitem* (*dbusmenu_menuitem_child_append_func)( + DbusmenuMenuitem* parent, + DbusmenuMenuitem* child); +typedef DbusmenuMenuitem* (*dbusmenu_menuitem_property_set_func)( + DbusmenuMenuitem* item, + const char* property, + const char* value); +typedef DbusmenuMenuitem* (*dbusmenu_menuitem_property_set_variant_func)( + DbusmenuMenuitem* item, + const char* property, + GVariant* value); +typedef DbusmenuMenuitem* (*dbusmenu_menuitem_property_set_bool_func)( + DbusmenuMenuitem* item, + const char* property, + bool value); +typedef DbusmenuMenuitem* (*dbusmenu_menuitem_property_set_int_func)( + DbusmenuMenuitem* item, + const char* property, + int value); + +typedef struct _DbusmenuServer DbusmenuServer; +typedef DbusmenuServer* (*dbusmenu_server_new_func)(const char* object); +typedef void (*dbusmenu_server_set_root_func)(DbusmenuServer* self, + DbusmenuMenuitem* root); + +namespace atom { + +namespace { + +// Retrieved functions from libdbusmenu-glib. + +// DbusmenuMenuItem methods: +dbusmenu_menuitem_new_func menuitem_new = NULL; +dbusmenu_menuitem_new_with_id_func menuitem_new_with_id = NULL; +dbusmenu_menuitem_get_id_func menuitem_get_id = NULL; +dbusmenu_menuitem_get_children_func menuitem_get_children = NULL; +dbusmenu_menuitem_get_children_func menuitem_take_children = NULL; +dbusmenu_menuitem_child_append_func menuitem_child_append = NULL; +dbusmenu_menuitem_property_set_func menuitem_property_set = NULL; +dbusmenu_menuitem_property_set_variant_func menuitem_property_set_variant = + NULL; +dbusmenu_menuitem_property_set_bool_func menuitem_property_set_bool = NULL; +dbusmenu_menuitem_property_set_int_func menuitem_property_set_int = NULL; + +// DbusmenuServer methods: +dbusmenu_server_new_func server_new = NULL; +dbusmenu_server_set_root_func server_set_root = NULL; + +// Properties that we set on menu items: +const char kPropertyEnabled[] = "enabled"; +const char kPropertyLabel[] = "label"; +const char kPropertyShortcut[] = "shortcut"; +const char kPropertyType[] = "type"; +const char kPropertyToggleType[] = "toggle-type"; +const char kPropertyToggleState[] = "toggle-state"; +const char kPropertyVisible[] = "visible"; +const char kPropertyChildrenDisplay[] = "children-display"; + +const char kToggleCheck[] = "checkmark"; +const char kToggleRadio[] = "radio"; +const char kTypeSeparator[] = "separator"; +const char kDisplaySubmenu[] = "submenu"; + +void EnsureMethodsLoaded() { + static bool attempted_load = false; + if (attempted_load) + return; + attempted_load = true; + + void* dbusmenu_lib = dlopen("libdbusmenu-glib.so", RTLD_LAZY); + if (!dbusmenu_lib) + dbusmenu_lib = dlopen("libdbusmenu-glib.so.4", RTLD_LAZY); + if (!dbusmenu_lib) + return; + + // DbusmenuMenuItem methods. + menuitem_new = reinterpret_cast( + dlsym(dbusmenu_lib, "dbusmenu_menuitem_new")); + menuitem_new_with_id = reinterpret_cast( + dlsym(dbusmenu_lib, "dbusmenu_menuitem_new_with_id")); + menuitem_get_id = reinterpret_cast( + dlsym(dbusmenu_lib, "dbusmenu_menuitem_get_id")); + menuitem_get_children = reinterpret_cast( + dlsym(dbusmenu_lib, "dbusmenu_menuitem_get_children")); + menuitem_take_children = + reinterpret_cast( + dlsym(dbusmenu_lib, "dbusmenu_menuitem_take_children")); + menuitem_child_append = reinterpret_cast( + dlsym(dbusmenu_lib, "dbusmenu_menuitem_child_append")); + menuitem_property_set = reinterpret_cast( + dlsym(dbusmenu_lib, "dbusmenu_menuitem_property_set")); + menuitem_property_set_variant = + reinterpret_cast( + dlsym(dbusmenu_lib, "dbusmenu_menuitem_property_set_variant")); + menuitem_property_set_bool = + reinterpret_cast( + dlsym(dbusmenu_lib, "dbusmenu_menuitem_property_set_bool")); + menuitem_property_set_int = + reinterpret_cast( + dlsym(dbusmenu_lib, "dbusmenu_menuitem_property_set_int")); + + // DbusmenuServer methods. + server_new = reinterpret_cast( + dlsym(dbusmenu_lib, "dbusmenu_server_new")); + server_set_root = reinterpret_cast( + dlsym(dbusmenu_lib, "dbusmenu_server_set_root")); +} + +ui::MenuModel* ModelForMenuItem(DbusmenuMenuitem* item) { + return reinterpret_cast( + g_object_get_data(G_OBJECT(item), "model")); +} + +bool GetMenuItemID(DbusmenuMenuitem* item, int *id) { + gpointer id_ptr = g_object_get_data(G_OBJECT(item), "menu-id"); + if (id_ptr != NULL) { + *id = GPOINTER_TO_INT(id_ptr) - 1; + return true; + } + + return false; +} + +void SetMenuItemID(DbusmenuMenuitem* item, int id) { + DCHECK_GE(id, 0); + + // Add 1 to the menu_id to avoid setting zero (null) to "menu-id". + g_object_set_data(G_OBJECT(item), "menu-id", GINT_TO_POINTER(id + 1)); +} + +} // namespace + +GlobalMenuBarX11::GlobalMenuBarX11(NativeWindowViews* window) + : window_(window), + xid_(window_->GetNativeWindow()->GetHost()->GetAcceleratedWidget()), + server_(NULL), + root_item_(NULL) { + EnsureMethodsLoaded(); + if (server_new) + InitServer(xid_); + + GlobalMenuBarRegistrarX11::GetInstance()->OnWindowMapped(xid_); +} + +GlobalMenuBarX11::~GlobalMenuBarX11() { + if (server_) + g_object_unref(server_); + + GlobalMenuBarRegistrarX11::GetInstance()->OnWindowUnmapped(xid_); +} + +// static +std::string GlobalMenuBarX11::GetPathForWindow(unsigned long xid) { + return base::StringPrintf("/com/canonical/menu/%lX", xid); +} + +void GlobalMenuBarX11::SetMenu(ui::MenuModel* menu_model) { + if (!server_) + return; + + root_item_ = menuitem_new(); + menuitem_property_set(root_item_, kPropertyLabel, "Root"); + menuitem_property_set_bool(root_item_, kPropertyVisible, true); + BuildMenuFromModel(menu_model, root_item_); + + server_set_root(server_, root_item_); + g_object_unref(root_item_); +} + +void GlobalMenuBarX11::InitServer(unsigned long xid) { + std::string path = GetPathForWindow(xid); + server_ = server_new(path.c_str()); +} + +void GlobalMenuBarX11::BuildMenuFromModel(ui::MenuModel* model, + DbusmenuMenuitem* parent) { + for (int i = 0; i < model->GetItemCount(); ++i) { + DbusmenuMenuitem* item = menuitem_new(); + menuitem_property_set_bool(item, kPropertyVisible, model->IsVisibleAt(i)); + + ui::MenuModel::ItemType type = model->GetTypeAt(i); + if (type == ui::MenuModel::TYPE_SEPARATOR) { + menuitem_property_set(item, kPropertyType, kTypeSeparator); + } else { + std::string label = ui::ConvertAcceleratorsFromWindowsStyle( + base::UTF16ToUTF8(model->GetLabelAt(i))); + menuitem_property_set(item, kPropertyLabel, label.c_str()); + menuitem_property_set_bool(item, kPropertyEnabled, model->IsEnabledAt(i)); + + g_object_set_data(G_OBJECT(item), "model", model); + SetMenuItemID(item, i); + + if (type == ui::MenuModel::TYPE_SUBMENU) { + menuitem_property_set(item, kPropertyChildrenDisplay, kDisplaySubmenu); + g_signal_connect(item, "about-to-show", + G_CALLBACK(OnSubMenuShowThunk), this); + } else { + ui::Accelerator accelerator; + if (model->GetAcceleratorAt(i, &accelerator)) + RegisterAccelerator(item, accelerator); + + g_signal_connect(item, "item-activated", + G_CALLBACK(OnItemActivatedThunk), this); + + if (type == ui::MenuModel::TYPE_CHECK || + type == ui::MenuModel::TYPE_RADIO) { + menuitem_property_set(item, kPropertyToggleType, + type == ui::MenuModel::TYPE_CHECK ? kToggleCheck : kToggleRadio); + menuitem_property_set_int(item, kPropertyToggleState, + model->IsItemCheckedAt(i)); + } + } + } + + menuitem_child_append(parent, item); + g_object_unref(item); + } +} + +void GlobalMenuBarX11::RegisterAccelerator(DbusmenuMenuitem* item, + const ui::Accelerator& accelerator) { + // A translation of libdbusmenu-gtk's menuitem_property_set_shortcut() + // translated from GDK types to ui::Accelerator types. + GVariantBuilder builder; + g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); + + if (accelerator.IsCtrlDown()) + g_variant_builder_add(&builder, "s", "Control"); + if (accelerator.IsAltDown()) + g_variant_builder_add(&builder, "s", "Alt"); + if (accelerator.IsShiftDown()) + g_variant_builder_add(&builder, "s", "Shift"); + + char* name = XKeysymToString(XKeysymForWindowsKeyCode( + accelerator.key_code(), false)); + if (!name) { + NOTIMPLEMENTED(); + return; + } + g_variant_builder_add(&builder, "s", name); + + GVariant* inside_array = g_variant_builder_end(&builder); + g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); + g_variant_builder_add_value(&builder, inside_array); + GVariant* outside_array = g_variant_builder_end(&builder); + + menuitem_property_set_variant(item, kPropertyShortcut, outside_array); +} + +void GlobalMenuBarX11::OnItemActivated(DbusmenuMenuitem* item, + unsigned int timestamp) { + int id; + ui::MenuModel* model = ModelForMenuItem(item); + if (model && GetMenuItemID(item, &id)) + model->ActivatedAt(id, 0); +} + +void GlobalMenuBarX11::OnSubMenuShow(DbusmenuMenuitem* item) { + int id; + ui::MenuModel* model = ModelForMenuItem(item); + if (!model || !GetMenuItemID(item, &id)) + return; + + // Clear children. + GList *children = menuitem_take_children(item); + g_list_foreach(children, reinterpret_cast(g_object_unref), NULL); + g_list_free(children); + + // Build children. + BuildMenuFromModel(model->GetSubmenuModelAt(id), item); +} + +} // namespace atom diff --git a/atom/browser/ui/views/global_menu_bar_x11.h b/atom/browser/ui/views/global_menu_bar_x11.h new file mode 100644 index 000000000000..b2674ba0ba37 --- /dev/null +++ b/atom/browser/ui/views/global_menu_bar_x11.h @@ -0,0 +1,73 @@ +// Copyright (c) 2014 GitHub, Inc. All rights reserved. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef ATOM_BROWSER_UI_VIEWS_GLOBAL_MENU_BAR_X11_H_ +#define ATOM_BROWSER_UI_VIEWS_GLOBAL_MENU_BAR_X11_H_ + +#include + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "ui/base/glib/glib_signal.h" + +typedef struct _DbusmenuMenuitem DbusmenuMenuitem; +typedef struct _DbusmenuServer DbusmenuServer; + +namespace ui { +class Accelerator; +class MenuModel; +} + +namespace atom { + +class NativeWindowViews; + +// Controls the Mac style menu bar on Unity. +// +// Unity has an Apple-like menu bar at the top of the screen that changes +// depending on the active window. In the GTK port, we had a hidden GtkMenuBar +// object in each GtkWindow which existed only to be scrapped by the +// libdbusmenu-gtk code. Since we don't have GtkWindows anymore, we need to +// interface directly with the lower level libdbusmenu-glib, which we +// opportunistically dlopen() since not everyone is running Ubuntu. +// +// This class is like the chrome's corresponding one, but it generates the menu +// from menu models instead, and it is also per-window specific. +class GlobalMenuBarX11 { + public: + GlobalMenuBarX11(NativeWindowViews* window); + virtual ~GlobalMenuBarX11(); + + // Creates the object path for DbusemenuServer which is attached to |xid|. + static std::string GetPathForWindow(unsigned long xid); + + void SetMenu(ui::MenuModel* menu_model); + + private: + // Creates a DbusmenuServer. + void InitServer(unsigned long xid); + + // Create a menu from menu model. + void BuildMenuFromModel(ui::MenuModel* model, DbusmenuMenuitem* parent); + + // Sets the accelerator for |item|. + void RegisterAccelerator(DbusmenuMenuitem* item, + const ui::Accelerator& accelerator); + + CHROMEG_CALLBACK_1(GlobalMenuBarX11, void, OnItemActivated, DbusmenuMenuitem*, + unsigned int); + CHROMEG_CALLBACK_0(GlobalMenuBarX11, void, OnSubMenuShow, DbusmenuMenuitem*); + + NativeWindowViews* window_; + int xid_; + + DbusmenuServer* server_; + DbusmenuMenuitem* root_item_; + + DISALLOW_COPY_AND_ASSIGN(GlobalMenuBarX11); +}; + +} // namespace atom + +#endif // ATOM_BROWSER_UI_VIEWS_GLOBAL_MENU_BAR_X11_H_ diff --git a/chromium_src/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.cc b/chromium_src/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.cc new file mode 100644 index 000000000000..0d2a6dd73824 --- /dev/null +++ b/chromium_src/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.cc @@ -0,0 +1,142 @@ +// Copyright 2013 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 "chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.h" + +#include "atom/browser/ui/views/global_menu_bar_x11.h" +#include "base/bind.h" +#include "base/debug/leak_annotations.h" +#include "base/logging.h" +#include "content/public/browser/browser_thread.h" + +using content::BrowserThread; + +namespace { + +const char kAppMenuRegistrarName[] = "com.canonical.AppMenu.Registrar"; +const char kAppMenuRegistrarPath[] = "/com/canonical/AppMenu/Registrar"; + +} // namespace + +// static +GlobalMenuBarRegistrarX11* GlobalMenuBarRegistrarX11::GetInstance() { + return Singleton::get(); +} + +void GlobalMenuBarRegistrarX11::OnWindowMapped(unsigned long xid) { + live_xids_.insert(xid); + + if (registrar_proxy_) + RegisterXID(xid); +} + +void GlobalMenuBarRegistrarX11::OnWindowUnmapped(unsigned long xid) { + if (registrar_proxy_) + UnregisterXID(xid); + + live_xids_.erase(xid); +} + +GlobalMenuBarRegistrarX11::GlobalMenuBarRegistrarX11() + : registrar_proxy_(NULL) { + // libdbusmenu uses the gio version of dbus; I tried using the code in dbus/, + // but it looks like that's isn't sharing the bus name with the gio version, + // even when |connection_type| is set to SHARED. + g_dbus_proxy_new_for_bus( + G_BUS_TYPE_SESSION, + static_cast( + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | + G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS | + G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START), + NULL, + kAppMenuRegistrarName, + kAppMenuRegistrarPath, + kAppMenuRegistrarName, + NULL, // TODO: Probalby want a real cancelable. + static_cast(OnProxyCreatedThunk), + this); +} + +GlobalMenuBarRegistrarX11::~GlobalMenuBarRegistrarX11() { + if (registrar_proxy_) { + g_signal_handlers_disconnect_by_func( + registrar_proxy_, + reinterpret_cast(OnNameOwnerChangedThunk), + this); + g_object_unref(registrar_proxy_); + } +} + +void GlobalMenuBarRegistrarX11::RegisterXID(unsigned long xid) { + DCHECK(registrar_proxy_); + std::string path = atom::GlobalMenuBarX11::GetPathForWindow(xid); + + ANNOTATE_SCOPED_MEMORY_LEAK; // http://crbug.com/314087 + // TODO(erg): The mozilla implementation goes to a lot of callback trouble + // just to make sure that they react to make sure there's some sort of + // cancelable object; including making a whole callback just to handle the + // cancelable. + // + // I don't see any reason why we should care if "RegisterWindow" completes or + // not. + g_dbus_proxy_call(registrar_proxy_, + "RegisterWindow", + g_variant_new("(uo)", xid, path.c_str()), + G_DBUS_CALL_FLAGS_NONE, -1, + NULL, + NULL, + NULL); +} + +void GlobalMenuBarRegistrarX11::UnregisterXID(unsigned long xid) { + DCHECK(registrar_proxy_); + std::string path = atom::GlobalMenuBarX11::GetPathForWindow(xid); + + ANNOTATE_SCOPED_MEMORY_LEAK; // http://crbug.com/314087 + // TODO(erg): The mozilla implementation goes to a lot of callback trouble + // just to make sure that they react to make sure there's some sort of + // cancelable object; including making a whole callback just to handle the + // cancelable. + // + // I don't see any reason why we should care if "UnregisterWindow" completes + // or not. + g_dbus_proxy_call(registrar_proxy_, + "UnregisterWindow", + g_variant_new("(u)", xid), + G_DBUS_CALL_FLAGS_NONE, -1, + NULL, + NULL, + NULL); +} + +void GlobalMenuBarRegistrarX11::OnProxyCreated(GObject* source, + GAsyncResult* result) { + GError* error = NULL; + GDBusProxy* proxy = g_dbus_proxy_new_for_bus_finish(result, &error); + if (error) { + g_error_free(error); + return; + } + + // TODO(erg): Mozilla's implementation has a workaround for GDBus + // cancellation here. However, it's marked as fixed. If there's weird + // problems with cancelation, look at how they fixed their issues. + + registrar_proxy_ = proxy; + + g_signal_connect(registrar_proxy_, "notify::g-name-owner", + G_CALLBACK(OnNameOwnerChangedThunk), this); + + OnNameOwnerChanged(NULL, NULL); +} + +void GlobalMenuBarRegistrarX11::OnNameOwnerChanged(GObject* /* ignored */, + GParamSpec* /* ignored */) { + // If the name owner changed, we need to reregister all the live xids with + // the system. + for (std::set::const_iterator it = live_xids_.begin(); + it != live_xids_.end(); ++it) { + RegisterXID(*it); + } +} diff --git a/chromium_src/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.h b/chromium_src/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.h new file mode 100644 index 000000000000..e35e87c0d20c --- /dev/null +++ b/chromium_src/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.h @@ -0,0 +1,54 @@ +// Copyright 2013 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. + +#ifndef CHROME_BROWSER_UI_VIEWS_FRAME_GLOBAL_MENU_BAR_REGISTRAR_X11_H_ +#define CHROME_BROWSER_UI_VIEWS_FRAME_GLOBAL_MENU_BAR_REGISTRAR_X11_H_ + +#include + +#include + +#include "base/basictypes.h" +#include "base/memory/ref_counted.h" +#include "base/memory/singleton.h" +#include "ui/base/glib/glib_signal.h" + +// Advertises our menu bars to Unity. +// +// GlobalMenuBarX11 is responsible for managing the DbusmenuServer for each +// XID. We need a separate object to own the dbus channel to +// com.canonical.AppMenu.Registrar and to register/unregister the mapping +// between a XID and the DbusmenuServer instance we are offering. +class GlobalMenuBarRegistrarX11 { + public: + static GlobalMenuBarRegistrarX11* GetInstance(); + + void OnWindowMapped(unsigned long xid); + void OnWindowUnmapped(unsigned long xid); + + private: + friend struct DefaultSingletonTraits; + + GlobalMenuBarRegistrarX11(); + ~GlobalMenuBarRegistrarX11(); + + // Sends the actual message. + void RegisterXID(unsigned long xid); + void UnregisterXID(unsigned long xid); + + CHROMEG_CALLBACK_1(GlobalMenuBarRegistrarX11, void, OnProxyCreated, + GObject*, GAsyncResult*); + CHROMEG_CALLBACK_1(GlobalMenuBarRegistrarX11, void, OnNameOwnerChanged, + GObject*, GParamSpec*); + + GDBusProxy* registrar_proxy_; + + // Window XIDs which want to be registered, but haven't yet been because + // we're waiting for the proxy to become available. + std::set live_xids_; + + DISALLOW_COPY_AND_ASSIGN(GlobalMenuBarRegistrarX11); +}; + +#endif // CHROME_BROWSER_UI_VIEWS_FRAME_GLOBAL_MENU_BAR_REGISTRAR_X11_H_