Skip to content

Commit 3e750c5

Browse files
Cong Liurogerwang
authored andcommitted
[linux] enabled global menubar on Ubuntu
Global menubar is implemented via dbus API. And it will fallback to local menubar when no global menubar available. fixed nwjs#2718
1 parent 8ccf719 commit 3e750c5

File tree

8 files changed

+679
-4
lines changed

8 files changed

+679
-4
lines changed

docs/References/Menu.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ See [Customize Menubar](../For Users/Advanced/Customize Menubar.md) for detailed
5959

6060
* `option` `{Object}` _Optional_
6161
- `type` `{String}` _Optional_ two types are accepted by this method: "menubar" or "contextmenu". The value is set to "contextmenu" by default.
62+
- `global` `{Boolean}` _Optional_ *(Linux Only)* specify if using global menubar on Ubuntu. It will fallback to local window based menubar if no global menubar available. The value is set to `false` by default.
6263

6364
Create a `Menu` object.
6465

src/api/menu/menu.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,8 @@ class Menu : public Base {
135135
// rebuild the menu before next show.
136136
bool is_menu_modified_;
137137

138+
bool is_global_menu_bar_;
139+
138140
std::unique_ptr<MenuDelegate> menu_delegate_;
139141
std::unique_ptr<ui::NwMenuModel> menu_model_;
140142
#endif

src/api/menu/menu_views.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ void Menu::Create(const base::DictionaryValue& option) {
6060
focus_manager_ = NULL;
6161
window_ = NULL;
6262

63-
std::string type;
63+
is_global_menu_bar_ = false;
64+
option.GetBoolean("global", &is_global_menu_bar_);
6465

6566
menu_items_.empty();
6667
}

src/api/nw_window_api.cc

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626

2727
#include "content/nw/src/api/nw_current_window_internal.h"
2828

29+
#include "chrome/browser/ui/webui/print_preview/print_preview_handler.h"
30+
2931
#if defined(OS_WIN)
3032
#include <shobjidl.h>
3133
#include <dwmapi.h>
@@ -42,6 +44,9 @@
4244

4345
#if defined(OS_LINUX)
4446
#include "chrome/browser/ui/libgtk2ui/gtk2_ui.h"
47+
#include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h"
48+
#include "ui/aura/window.h"
49+
#include "content/nw/src/browser/global_menu_bar_x11.h"
4550
#endif
4651

4752
#if defined(OS_LINUX) || defined(OS_WIN)
@@ -54,8 +59,6 @@ using nw::BrowserViewLayout;
5459
#include "content/nw/src/nw_content_mac.h"
5560
#endif
5661

57-
#include "chrome/browser/ui/webui/print_preview/print_preview_handler.h"
58-
5962
using content::RenderWidgetHost;
6063
using content::RenderWidgetHostView;
6164
using content::WebContents;
@@ -327,6 +330,14 @@ bool NwCurrentWindowInternalClearMenuFunction::RunAsync() {
327330
#endif
328331

329332
#if defined(OS_LINUX) || defined(OS_WIN)
333+
334+
#if defined(OS_LINUX)
335+
if (nw::GlobalMenuBarX11::IsGlobalMenuBarEnabled()) {
336+
views::DesktopWindowTreeHostX11 *host = static_cast<views::DesktopWindowTreeHostX11*>(window->GetNativeWindow()->GetHost());
337+
host->SetGlobalMenu(nullptr);
338+
}
339+
#endif
340+
330341
native_app_window::NativeAppWindowViews* native_app_window_views =
331342
static_cast<native_app_window::NativeAppWindowViews*>(
332343
window->GetBaseWindow());
@@ -369,16 +380,26 @@ bool NwCurrentWindowInternalSetMenuFunction::RunNWSync(base::ListValue* response
369380
native_app_window::NativeAppWindowViews* native_app_window_views =
370381
static_cast<native_app_window::NativeAppWindowViews*>(
371382
window->GetBaseWindow());
383+
menu->UpdateKeys( native_app_window_views->widget()->GetFocusManager() );
384+
385+
#if defined(OS_LINUX)
386+
if (menu->is_global_menu_bar_ && nw::GlobalMenuBarX11::IsGlobalMenuBarEnabled()) {
387+
views::DesktopWindowTreeHostX11 *host = static_cast<views::DesktopWindowTreeHostX11*>(window->GetNativeWindow()->GetHost());
388+
host->SetGlobalMenu(menu->model());
389+
response->Append(std::unique_ptr<base::ListValue>(new base::ListValue()));
390+
return true;
391+
}
392+
#endif
372393

373394
MenuBarView* menubar = new MenuBarView();
374395
static_cast<BrowserViewLayout*>(native_app_window_views->GetLayoutManager())->set_menu_bar(menubar);
375396
native_app_window_views->AddChildView(menubar);
376397
menubar->UpdateMenu(menu->model());
377398
native_app_window_views->layout_();
378399
native_app_window_views->SchedulePaint();
379-
menu->UpdateKeys( native_app_window_views->widget()->GetFocusManager() );
380400
response->Append(std::unique_ptr<base::ListValue>(new base::ListValue()));
381401
#endif
402+
382403
return true;
383404
}
384405

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
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 "content/nw/src/browser/global_menu_bar_registrar_x11.h"
6+
7+
#include "base/bind.h"
8+
#include "base/debug/leak_annotations.h"
9+
#include "base/logging.h"
10+
#include "content/nw/src/browser/global_menu_bar_x11.h"
11+
12+
namespace nw {
13+
14+
namespace {
15+
16+
const char kAppMenuRegistrarName[] = "com.canonical.AppMenu.Registrar";
17+
const char kAppMenuRegistrarPath[] = "/com/canonical/AppMenu/Registrar";
18+
19+
} // namespace
20+
21+
// static
22+
GlobalMenuBarRegistrarX11* GlobalMenuBarRegistrarX11::GetInstance() {
23+
return base::Singleton<GlobalMenuBarRegistrarX11>::get();
24+
}
25+
26+
void GlobalMenuBarRegistrarX11::OnWindowMapped(unsigned long xid) {
27+
live_xids_.insert(xid);
28+
29+
if (registrar_proxy_)
30+
RegisterXID(xid);
31+
}
32+
33+
void GlobalMenuBarRegistrarX11::OnWindowUnmapped(unsigned long xid) {
34+
if (registrar_proxy_)
35+
UnregisterXID(xid);
36+
37+
live_xids_.erase(xid);
38+
}
39+
40+
GlobalMenuBarRegistrarX11::GlobalMenuBarRegistrarX11()
41+
: registrar_proxy_(nullptr) {
42+
// libdbusmenu uses the gio version of dbus; I tried using the code in dbus/,
43+
// but it looks like that's isn't sharing the bus name with the gio version,
44+
// even when |connection_type| is set to SHARED.
45+
g_dbus_proxy_new_for_bus(
46+
G_BUS_TYPE_SESSION,
47+
static_cast<GDBusProxyFlags>(
48+
G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
49+
G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS |
50+
G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START),
51+
nullptr,
52+
kAppMenuRegistrarName,
53+
kAppMenuRegistrarPath,
54+
kAppMenuRegistrarName,
55+
nullptr, // TODO: Probalby want a real cancelable.
56+
static_cast<GAsyncReadyCallback>(OnProxyCreatedThunk),
57+
this);
58+
}
59+
60+
GlobalMenuBarRegistrarX11::~GlobalMenuBarRegistrarX11() {
61+
if (registrar_proxy_) {
62+
g_signal_handlers_disconnect_by_func(
63+
registrar_proxy_,
64+
reinterpret_cast<void*>(OnNameOwnerChangedThunk),
65+
this);
66+
g_object_unref(registrar_proxy_);
67+
}
68+
}
69+
70+
void GlobalMenuBarRegistrarX11::RegisterXID(unsigned long xid) {
71+
DCHECK(registrar_proxy_);
72+
std::string path = GlobalMenuBarX11::GetPathForWindow(xid);
73+
74+
ANNOTATE_SCOPED_MEMORY_LEAK; // http://crbug.com/314087
75+
// TODO(erg): The mozilla implementation goes to a lot of callback trouble
76+
// just to make sure that they react to make sure there's some sort of
77+
// cancelable object; including making a whole callback just to handle the
78+
// cancelable.
79+
//
80+
// I don't see any reason why we should care if "RegisterWindow" completes or
81+
// not.
82+
g_dbus_proxy_call(registrar_proxy_,
83+
"RegisterWindow",
84+
g_variant_new("(uo)", xid, path.c_str()),
85+
G_DBUS_CALL_FLAGS_NONE, -1,
86+
nullptr,
87+
nullptr,
88+
nullptr);
89+
}
90+
91+
void GlobalMenuBarRegistrarX11::UnregisterXID(unsigned long xid) {
92+
DCHECK(registrar_proxy_);
93+
std::string path = GlobalMenuBarX11::GetPathForWindow(xid);
94+
95+
ANNOTATE_SCOPED_MEMORY_LEAK; // http://crbug.com/314087
96+
// TODO(erg): The mozilla implementation goes to a lot of callback trouble
97+
// just to make sure that they react to make sure there's some sort of
98+
// cancelable object; including making a whole callback just to handle the
99+
// cancelable.
100+
//
101+
// I don't see any reason why we should care if "UnregisterWindow" completes
102+
// or not.
103+
g_dbus_proxy_call(registrar_proxy_,
104+
"UnregisterWindow",
105+
g_variant_new("(u)", xid),
106+
G_DBUS_CALL_FLAGS_NONE, -1,
107+
nullptr,
108+
nullptr,
109+
nullptr);
110+
}
111+
112+
void GlobalMenuBarRegistrarX11::OnProxyCreated(GObject* source,
113+
GAsyncResult* result) {
114+
GError* error = nullptr;
115+
GDBusProxy* proxy = g_dbus_proxy_new_for_bus_finish(result, &error);
116+
if (error) {
117+
g_error_free(error);
118+
return;
119+
}
120+
121+
// TODO(erg): Mozilla's implementation has a workaround for GDBus
122+
// cancellation here. However, it's marked as fixed. If there's weird
123+
// problems with cancelation, look at how they fixed their issues.
124+
125+
registrar_proxy_ = proxy;
126+
127+
g_signal_connect(registrar_proxy_, "notify::g-name-owner",
128+
G_CALLBACK(OnNameOwnerChangedThunk), this);
129+
130+
OnNameOwnerChanged(nullptr, nullptr);
131+
}
132+
133+
void GlobalMenuBarRegistrarX11::OnNameOwnerChanged(GObject* /* ignored */,
134+
GParamSpec* /* ignored */) {
135+
// If the name owner changed, we need to reregister all the live xids with
136+
// the system.
137+
for (std::set<unsigned long>::const_iterator it = live_xids_.begin();
138+
it != live_xids_.end(); ++it) {
139+
RegisterXID(*it);
140+
}
141+
}
142+
143+
} // namespace nw
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
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+
// NW fix: modified from src/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.h
6+
7+
#ifndef CONTENT_NW_SRC_BROWSER_GLOBAL_MENU_BAR_REGISTRAR_X11_H_
8+
#define CONTENT_NW_SRC_BROWSER_GLOBAL_MENU_BAR_REGISTRAR_X11_H_
9+
10+
#include <gio/gio.h>
11+
12+
#include <set>
13+
14+
#include "base/macros.h"
15+
#include "base/memory/ref_counted.h"
16+
#include "base/memory/singleton.h"
17+
#include "ui/base/glib/glib_signal.h"
18+
19+
namespace nw {
20+
21+
// Advertises our menu bars to Unity.
22+
//
23+
// GlobalMenuBarX11 is responsible for managing the DbusmenuServer for each
24+
// XID. We need a separate object to own the dbus channel to
25+
// com.canonical.AppMenu.Registrar and to register/unregister the mapping
26+
// between a XID and the DbusmenuServer instance we are offering.
27+
class GlobalMenuBarRegistrarX11 {
28+
public:
29+
static GlobalMenuBarRegistrarX11* GetInstance();
30+
31+
void OnWindowMapped(unsigned long xid);
32+
void OnWindowUnmapped(unsigned long xid);
33+
34+
private:
35+
friend struct base::DefaultSingletonTraits<GlobalMenuBarRegistrarX11>;
36+
37+
GlobalMenuBarRegistrarX11();
38+
~GlobalMenuBarRegistrarX11();
39+
40+
// Sends the actual message.
41+
void RegisterXID(unsigned long xid);
42+
void UnregisterXID(unsigned long xid);
43+
44+
CHROMEG_CALLBACK_1(GlobalMenuBarRegistrarX11, void, OnProxyCreated,
45+
GObject*, GAsyncResult*);
46+
CHROMEG_CALLBACK_1(GlobalMenuBarRegistrarX11, void, OnNameOwnerChanged,
47+
GObject*, GParamSpec*);
48+
49+
GDBusProxy* registrar_proxy_;
50+
51+
// Window XIDs which want to be registered, but haven't yet been because
52+
// we're waiting for the proxy to become available.
53+
std::set<unsigned long> live_xids_;
54+
55+
DISALLOW_COPY_AND_ASSIGN(GlobalMenuBarRegistrarX11);
56+
};
57+
58+
} // namespace nw
59+
60+
#endif // CONTENT_NW_SRC_BROWSER_GLOBAL_MENU_BAR_REGISTRAR_X11_H_

0 commit comments

Comments
 (0)