-
Notifications
You must be signed in to change notification settings - Fork 664
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
client: Add support for running as a system tray process with icon
- Loading branch information
Chris Townsend
committed
Jan 18, 2019
1 parent
e162dc1
commit 17ae950
Showing
6 changed files
with
245 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
# Copyright © 2017-2018 Canonical Ltd. | ||
# Copyright © 2017-2019 Canonical Ltd. | ||
# | ||
# This program is free software: you can redistribute it and/or modify | ||
# it under the terms of the GNU General Public License version 3 as | ||
|
@@ -11,8 +11,8 @@ | |
# | ||
# You should have received a copy of the GNU General Public License | ||
# along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
# | ||
# Authored by: Alberto Aguirre <[email protected]> | ||
|
||
set(CMAKE_AUTOMOC ON) | ||
|
||
add_library(commands STATIC | ||
animated_spinner.cpp | ||
|
@@ -31,6 +31,7 @@ add_library(commands STATIC | |
start.cpp | ||
stop.cpp | ||
suspend.cpp | ||
systray.cpp | ||
restart.cpp | ||
delete.cpp | ||
umount.cpp | ||
|
@@ -45,4 +46,5 @@ target_link_libraries(commands | |
rpc | ||
Qt5::Core | ||
Qt5::Network | ||
Qt5::Widgets | ||
yaml) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
/* | ||
* Copyright (C) 2019 Canonical, Ltd. | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation; version 3. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
* | ||
*/ | ||
|
||
#include "systray.h" | ||
#include "common_cli.h" | ||
|
||
#include <multipass/cli/argparser.h> | ||
#include <multipass/cli/format_utils.h> | ||
|
||
namespace mp = multipass; | ||
namespace cmd = multipass::cmd; | ||
using RpcMethod = mp::Rpc::Stub; | ||
|
||
void cmd::Systray::run(mp::ArgParser* parser) | ||
{ | ||
if (!QSystemTrayIcon::isSystemTrayAvailable()) | ||
{ | ||
cerr << "System tray not supported\n"; | ||
return command_done(ReturnCode::CommandFail); | ||
} | ||
|
||
auto ret = parse_args(parser); | ||
if (ret != ParseCode::Ok) | ||
{ | ||
return command_done(parser->returnCodeFrom(ret)); | ||
} | ||
|
||
create_actions(); | ||
create_menu(); | ||
tray_icon.show(); | ||
} | ||
|
||
std::string cmd::Systray::name() const | ||
{ | ||
return "systray"; | ||
} | ||
|
||
QString cmd::Systray::short_help() const | ||
{ | ||
return QStringLiteral("Run client in system tray"); | ||
} | ||
|
||
QString cmd::Systray::description() const | ||
{ | ||
return QStringLiteral("Run the client in the system tray."); | ||
} | ||
|
||
mp::ParseCode cmd::Systray::parse_args(mp::ArgParser* parser) | ||
{ | ||
return parser->commandParse(this); | ||
} | ||
|
||
void cmd::Systray::create_actions() | ||
{ | ||
retrieving_action = tray_icon_menu.addAction("Retrieving instances..."); | ||
about_separator = tray_icon_menu.addSeparator(); | ||
about_action = tray_icon_menu.addAction("About"); | ||
quit_action = tray_icon_menu.addAction("Quit"); | ||
} | ||
|
||
void cmd::Systray::create_menu() | ||
{ | ||
tray_icon.setContextMenu(&tray_icon_menu); | ||
|
||
tray_icon.setIcon(QIcon{"./ubuntu-icon.png"}); | ||
|
||
QObject::connect(&tray_icon_menu, &QMenu::aboutToShow, [this]() { | ||
if (failure_action.isVisible()) | ||
{ | ||
tray_icon_menu.removeAction(&failure_action); | ||
} | ||
|
||
std::unique_lock<decltype(worker_mutex)> lock{worker_mutex}; | ||
if (!worker_running) | ||
{ | ||
worker_running = true; | ||
worker = std::thread([this] { | ||
for (const auto& instance_menu : instances_menus) | ||
{ | ||
tray_icon_menu.removeAction(instance_menu->menuAction()); | ||
} | ||
instances_menus.clear(); | ||
tray_icon_menu.insertAction(about_separator, retrieving_action); | ||
|
||
using namespace std::literals::chrono_literals; | ||
std::this_thread::sleep_for(5s); | ||
retrieve_all_instances(); | ||
|
||
std::unique_lock<decltype(worker_mutex)> lock{worker_mutex}; | ||
worker_running = false; | ||
}); | ||
|
||
worker.detach(); | ||
} | ||
}); | ||
|
||
QObject::connect(quit_action, &QAction::triggered, [this]() { QCoreApplication::quit(); }); | ||
} | ||
|
||
void cmd::Systray::retrieve_all_instances() | ||
{ | ||
auto on_success = [this](ListReply& reply) { | ||
tray_icon_menu.removeAction(retrieving_action); | ||
|
||
for (const auto& instance : reply.instances()) | ||
{ | ||
auto state = QString::fromStdString(mp::format::status_string_for(instance.instance_status())); | ||
auto action_string = QString("%1 (%2)").arg(QString::fromStdString(instance.name())).arg(state); | ||
|
||
instances_menus.push_back(std::make_unique<QMenu>(action_string)); | ||
|
||
if (state != "DELETED") | ||
{ | ||
auto shell_action = instances_menus.back()->addAction("Open shell"); | ||
|
||
if (state != "RUNNING") | ||
{ | ||
shell_action->setDisabled(true); | ||
} | ||
|
||
if (state != "STOPPED") | ||
{ | ||
instances_actions.push_back(instances_menus.back()->addAction("Stop")); | ||
QObject::connect(instances_actions.back(), &QAction::triggered, | ||
[this]() { fmt::print("We would have stopped here\n"); }); | ||
} | ||
else | ||
{ | ||
instances_menus.back()->addAction("Start"); | ||
} | ||
|
||
tray_icon_menu.insertMenu(about_separator, instances_menus.back().get()); | ||
} | ||
} | ||
|
||
return ReturnCode::Ok; | ||
}; | ||
|
||
auto on_failure = [this](grpc::Status& status) { | ||
tray_icon_menu.removeAction(retrieving_action); | ||
tray_icon_menu.insertAction(about_separator, &failure_action); | ||
|
||
return standard_failure_handler_for(name(), cerr, status); | ||
}; | ||
|
||
ListRequest request; | ||
dispatch(&RpcMethod::list, request, on_success, on_failure); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
/* | ||
* Copyright (C) 2019 Canonical, Ltd. | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation; version 3. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
* | ||
*/ | ||
|
||
#ifndef MULTIPASS_SYSTRAY_H | ||
#define MULTIPASS_SYSTRAY_H | ||
|
||
#include <multipass/cli/command.h> | ||
|
||
#include <QMenu> | ||
#include <QObject> | ||
#include <QSystemTrayIcon> | ||
|
||
#include <memory> | ||
#include <thread> | ||
#include <vector> | ||
|
||
namespace multipass | ||
{ | ||
namespace cmd | ||
{ | ||
class Systray final : public QObject, public Command | ||
{ | ||
Q_OBJECT | ||
public: | ||
using Command::Command; | ||
void run(ArgParser* parser) override; | ||
|
||
std::string name() const override; | ||
QString short_help() const override; | ||
QString description() const override; | ||
|
||
private: | ||
ParseCode parse_args(ArgParser* parser) override; | ||
void create_actions(); | ||
void create_menu(); | ||
void retrieve_all_instances(); | ||
|
||
QSystemTrayIcon tray_icon; | ||
QMenu tray_icon_menu; | ||
|
||
QAction* retrieving_action; | ||
QAction* about_separator; | ||
QAction* about_action; | ||
QAction* quit_action; | ||
QAction failure_action{"Failure retrieving instances"}; | ||
|
||
std::vector<std::unique_ptr<QMenu>> instances_menus; | ||
std::vector<QAction*> instances_actions; | ||
|
||
bool worker_running{false}; | ||
std::mutex worker_mutex; | ||
std::thread worker; | ||
|
||
signals: | ||
void get_instances(); | ||
}; | ||
} // namespace cmd | ||
} // namespace multipass | ||
#endif // MULTIPASS_SYSTRAY_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters