Skip to content

Commit

Permalink
Implement StatusIconLinuxX11
Browse files Browse the repository at this point in the history
Still left is to figure out if/how the icon should be scaled to fit the
view.

BUG=419673
R=thestig

Change-Id: Ica7812b5b44d4c17e9d8d7bf207efec0b6f73e47
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1668487
Reviewed-by: Lei Zhang <thestig@chromium.org>
Reviewed-by: Scott Violet <sky@chromium.org>
Commit-Queue: Thomas Anderson <thomasanderson@chromium.org>
Cr-Commit-Position: refs/heads/master@{#671028}
  • Loading branch information
tanderson-google authored and Commit Bot committed Jun 20, 2019
1 parent 0d28499 commit b137555
Show file tree
Hide file tree
Showing 19 changed files with 301 additions and 51 deletions.
1 change: 1 addition & 0 deletions chrome/browser/ui/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -2433,6 +2433,7 @@ jumbo_split_static_library("ui") {
deps += [
"//ui/events/devices",
"//ui/events/devices/x11",
"//ui/events/platform/x11",
]
}
if (use_ozone) {
Expand Down
4 changes: 2 additions & 2 deletions chrome/browser/ui/libgtkui/app_indicator_icon.cc
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ AppIndicatorIcon::AppIndicatorIcon(std::string id,

EnsureLibAppIndicatorLoaded();
tool_tip_ = base::UTF16ToUTF8(tool_tip);
SetImage(image);
SetIcon(image);
}
AppIndicatorIcon::~AppIndicatorIcon() {
if (icon_) {
Expand All @@ -180,7 +180,7 @@ bool AppIndicatorIcon::CouldOpen() {
return g_opened;
}

void AppIndicatorIcon::SetImage(const gfx::ImageSkia& image) {
void AppIndicatorIcon::SetIcon(const gfx::ImageSkia& image) {
if (!g_opened)
return;

Expand Down
2 changes: 1 addition & 1 deletion chrome/browser/ui/libgtkui/app_indicator_icon.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class AppIndicatorIcon : public views::StatusIconLinux {
static bool CouldOpen();

// Overridden from views::StatusIconLinux:
void SetImage(const gfx::ImageSkia& image) override;
void SetIcon(const gfx::ImageSkia& image) override;
void SetToolTip(const base::string16& tool_tip) override;
void UpdatePlatformContextMenu(ui::MenuModel* menu) override;
void RefreshPlatformContextMenu() override;
Expand Down
2 changes: 1 addition & 1 deletion chrome/browser/ui/libgtkui/gtk_status_icon.cc
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ GtkStatusIcon::~GtkStatusIcon() {
g_object_unref(gtk_status_icon_);
}

void GtkStatusIcon::SetImage(const gfx::ImageSkia& image) {
void GtkStatusIcon::SetIcon(const gfx::ImageSkia& image) {
GdkPixbuf* pixbuf = GdkPixbufFromSkBitmap(*image.bitmap());
gtk_status_icon_set_from_pixbuf(gtk_status_icon_, pixbuf);
g_object_unref(pixbuf);
Expand Down
2 changes: 1 addition & 1 deletion chrome/browser/ui/libgtkui/gtk_status_icon.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class GtkStatusIcon : public views::StatusIconLinux {
~GtkStatusIcon() override;

// Overridden from views::StatusIconLinux:
void SetImage(const gfx::ImageSkia& image) override;
void SetIcon(const gfx::ImageSkia& image) override;
void SetToolTip(const base::string16& tool_tip) override;
void UpdatePlatformContextMenu(ui::MenuModel* menu) override;
void RefreshPlatformContextMenu() override;
Expand Down
13 changes: 7 additions & 6 deletions chrome/browser/ui/views/status_icons/status_icon_linux_dbus.cc
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ StatusIconLinuxDbus::~StatusIconLinuxDbus() {
FROM_HERE, base::BindOnce(&dbus::Bus::ShutdownAndBlock, bus_));
}

void StatusIconLinuxDbus::SetImage(const gfx::ImageSkia& image) {
void StatusIconLinuxDbus::SetIcon(const gfx::ImageSkia& image) {
if (!properties_)
return;

Expand Down Expand Up @@ -202,14 +202,14 @@ void StatusIconLinuxDbus::ExecuteCommand(int command_id, int event_flags) {

void StatusIconLinuxDbus::OnHostRegisteredResponse(dbus::Response* response) {
if (!response) {
delegate_->OnImplInitialized(false);
delegate_->OnImplInitializationFailed();
return;
}

dbus::MessageReader reader(response);
bool registered = false;
if (!reader.PopVariantOfBool(&registered) || !registered) {
delegate_->OnImplInitialized(false);
delegate_->OnImplInitializationFailed();
return;
}

Expand All @@ -223,7 +223,7 @@ void StatusIconLinuxDbus::OnHostRegisteredResponse(dbus::Response* response) {
void StatusIconLinuxDbus::OnOwnership(const std::string& service_name,
bool success) {
if (!success) {
delegate_->OnImplInitialized(false);
delegate_->OnImplInitializationFailed();
return;
}

Expand Down Expand Up @@ -293,7 +293,7 @@ void StatusIconLinuxDbus::OnExported(const std::string& interface_name,

void StatusIconLinuxDbus::OnInitialized(bool success) {
if (!success) {
delegate_->OnImplInitialized(false);
delegate_->OnImplInitializationFailed();
return;
}

Expand All @@ -307,7 +307,8 @@ void StatusIconLinuxDbus::OnInitialized(bool success) {
}

void StatusIconLinuxDbus::OnRegistered(dbus::Response* response) {
delegate_->OnImplInitialized(response);
if (!response)
delegate_->OnImplInitializationFailed();
}

void StatusIconLinuxDbus::OnActivate(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class StatusIconLinuxDbus : public views::StatusIconLinux,
~StatusIconLinuxDbus() override;

// StatusIcon:
void SetImage(const gfx::ImageSkia& image) override;
void SetIcon(const gfx::ImageSkia& image) override;
void SetToolTip(const base::string16& tool_tip) override;
void UpdatePlatformContextMenu(ui::MenuModel* model) override;
void RefreshPlatformContextMenu() override;
Expand Down
26 changes: 17 additions & 9 deletions chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.cc
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ StatusIconLinuxWrapper::StatusIconLinuxWrapper(
image_(image),
tool_tip_(tool_tip),
menu_model_(nullptr) {
status_icon_->set_delegate(this);
status_icon_->SetDelegate(this);
}

StatusIconLinuxWrapper::~StatusIconLinuxWrapper() {
Expand All @@ -48,11 +48,15 @@ StatusIconLinuxWrapper::~StatusIconLinuxWrapper() {
}

void StatusIconLinuxWrapper::SetImage(const gfx::ImageSkia& image) {
status_icon_->SetImage(image);
image_ = image;
if (status_icon_)
status_icon_->SetIcon(image);
}

void StatusIconLinuxWrapper::SetToolTip(const base::string16& tool_tip) {
status_icon_->SetToolTip(tool_tip);
tool_tip_ = tool_tip;
if (status_icon_)
status_icon_->SetToolTip(tool_tip);
}

void StatusIconLinuxWrapper::DisplayBalloon(
Expand Down Expand Up @@ -83,16 +87,13 @@ ui::MenuModel* StatusIconLinuxWrapper::GetMenuModel() const {
return menu_model_;
}

void StatusIconLinuxWrapper::OnImplInitialized(bool success) {
if (success)
return;

void StatusIconLinuxWrapper::OnImplInitializationFailed() {
switch (status_icon_type_) {
case kTypeDbus:
#if defined(USE_X11)
status_icon_ = std::make_unique<StatusIconLinuxX11>();
status_icon_->set_delegate(this);
status_icon_type_ = kTypeX11;
status_icon_->SetDelegate(this);
return;
#else
// Fallthrough needs to be omitted if USE_X11, otherwise clang will
Expand All @@ -102,14 +103,18 @@ void StatusIconLinuxWrapper::OnImplInitialized(bool success) {
case kTypeX11:
status_icon_.reset();
status_icon_type_ = kTypeOther;
if (menu_model_)
menu_model_->RemoveObserver(this);
menu_model_ = nullptr;
return;
case kTypeOther:
NOTREACHED();
}
}

void StatusIconLinuxWrapper::OnMenuStateChanged() {
status_icon_->RefreshPlatformContextMenu();
if (status_icon_)
status_icon_->RefreshPlatformContextMenu();
}

std::unique_ptr<StatusIconLinuxWrapper>
Expand Down Expand Up @@ -141,6 +146,9 @@ StatusIconLinuxWrapper::CreateWrappedStatusIcon(

void StatusIconLinuxWrapper::UpdatePlatformContextMenu(
StatusIconMenuModel* model) {
if (!status_icon_)
return;

// If a menu already exists, remove ourself from its observer list.
if (menu_model_)
menu_model_->RemoveObserver(this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class StatusIconLinuxWrapper : public StatusIcon,
const gfx::ImageSkia& GetImage() const override;
const base::string16& GetToolTip() const override;
ui::MenuModel* GetMenuModel() const override;
void OnImplInitialized(bool success) override;
void OnImplInitializationFailed() override;

// StatusIconMenuModel::Observer overrides:
void OnMenuStateChanged() override;
Expand Down
143 changes: 138 additions & 5 deletions chrome/browser/ui/views/status_icons/status_icon_linux_x11.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,153 @@

#include "chrome/browser/ui/views/status_icons/status_icon_linux_x11.h"

#include <X11/X.h>
#include <X11/Xlib.h>

#include <limits>
#include <memory>

#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "chrome/browser/shell_integration_linux.h"
#include "ui/aura/window.h"
#include "ui/aura/window_tree_host.h"
#include "ui/base/x/x11_util.h"
#include "ui/events/platform/x11/x11_event_source.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/x/x11.h"
#include "ui/gfx/x/x11_atom_cache.h"
#include "ui/gfx/x/x11_error_tracker.h"
#include "ui/gfx/x/x11_types.h"
#include "ui/views/background.h"
#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
#include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h"

namespace {

constexpr long kSystemTrayRequestDock = 0;

constexpr int kXembedInfoProtocolVersion = 0;
constexpr int kXembedInfoFlags = 0;

const int16_t kInitialWindowPos = std::numeric_limits<int16_t>::min();

} // namespace

StatusIconLinuxX11::StatusIconLinuxX11() = default;
StatusIconLinuxX11::StatusIconLinuxX11() : ImageButton(this) {}

StatusIconLinuxX11::~StatusIconLinuxX11() = default;

void StatusIconLinuxX11::SetImage(const gfx::ImageSkia& image) {
NOTIMPLEMENTED();
void StatusIconLinuxX11::SetIcon(const gfx::ImageSkia& image) {
SetImage(views::Button::STATE_NORMAL, image);
}

void StatusIconLinuxX11::SetToolTip(const base::string16& tool_tip) {
NOTIMPLEMENTED();
SetTooltipText(tool_tip);
}

void StatusIconLinuxX11::UpdatePlatformContextMenu(ui::MenuModel* model) {
NOTIMPLEMENTED();
// Nothing to do.
}

void StatusIconLinuxX11::OnSetDelegate() {
XDisplay* const display = gfx::GetXDisplay();
std::string atom_name =
"_NET_SYSTEM_TRAY_S" + base::NumberToString(DefaultScreen(display));
XID manager = XGetSelectionOwner(display, gfx::GetAtom(atom_name.c_str()));
if (manager == x11::None) {
delegate_->OnImplInitializationFailed();
// |this| may be destroyed!
return;
}

widget_ = std::make_unique<views::Widget>();

auto native_widget =
std::make_unique<views::DesktopNativeWidgetAura>(widget_.get());

auto host = std::make_unique<views::DesktopWindowTreeHostX11>(
widget_.get(), native_widget.get());
aura::WindowTreeHost* window_tree_host = host.get();

int visual_id;
if (ui::GetIntProperty(manager, "_NET_SYSTEM_TRAY_VISUAL", &visual_id))
host->SetVisualId(visual_id);

const int width = std::max(1, delegate_->GetImage().width());
const int height = std::max(1, delegate_->GetImage().height());

views::Widget::InitParams params;
params.type = views::Widget::InitParams::TYPE_CONTROL;
params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
params.activatable = views::Widget::InitParams::ACTIVATABLE_NO;
params.remove_standard_frame = true;
params.bounds =
gfx::Rect(kInitialWindowPos, kInitialWindowPos, width, height);
params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
params.native_widget = native_widget.release();
params.desktop_window_tree_host = host.release();
params.wm_class_name = shell_integration_linux::GetProgramClassName();
params.wm_class_class = shell_integration_linux::GetProgramClassClass();
// The status icon is a tiny window that doesn't update very often, so
// creating a compositor would only be wasteful of resources.
params.force_software_compositing = true;

widget_->Init(params);

Window window = window_tree_host->GetAcceleratedWidget();
DCHECK(window);

widget_->SetContentsView(this);
set_owned_by_client();

SetIcon(delegate_->GetImage());
SetImageHorizontalAlignment(ALIGN_CENTER);
SetImageVerticalAlignment(ALIGN_MIDDLE);
SetTooltipText(delegate_->GetToolTip());
set_context_menu_controller(this);

widget_->Show();

ui::SetIntArrayProperty(window, "_XEMBED_INFO", "CARDINAL",
{kXembedInfoProtocolVersion, kXembedInfoFlags});

XEvent ev;
memset(&ev, 0, sizeof(ev));
ev.xclient.type = ClientMessage;
ev.xclient.window = manager;
ev.xclient.message_type = gfx::GetAtom("_NET_SYSTEM_TRAY_OPCODE");
ev.xclient.format = 32;
ev.xclient.data.l[0] = ui::X11EventSource::GetInstance()->GetTimestamp();
ev.xclient.data.l[1] = kSystemTrayRequestDock;
ev.xclient.data.l[2] = window;

bool error;
{
gfx::X11ErrorTracker error_tracker;
XSendEvent(display, manager, false, NoEventMask, &ev);
error = error_tracker.FoundNewError();
}
if (error) {
delegate_->OnImplInitializationFailed();
// |this| may be destroyed!
}
}

void StatusIconLinuxX11::ShowContextMenuForViewImpl(
View* source,
const gfx::Point& point,
ui::MenuSourceType source_type) {
ui::MenuModel* menu = delegate_->GetMenuModel();
if (!menu)
return;
menu_runner_ = std::make_unique<views::MenuRunner>(
menu, views::MenuRunner::HAS_MNEMONICS | views::MenuRunner::CONTEXT_MENU |
views::MenuRunner::FIXED_ANCHOR);
menu_runner_->RunMenuAt(widget_.get(), nullptr, gfx::Rect(point, gfx::Size()),
views::MenuAnchorPosition::kTopLeft, source_type);
}

void StatusIconLinuxX11::ButtonPressed(Button* sender, const ui::Event& event) {
delegate_->OnClick();
}
Loading

0 comments on commit b137555

Please sign in to comment.