blob: 4d1d09380bab580da3a298d11b75c15337e789a4 [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "remoting/host/linux/gnome_interaction_strategy.h"
#include <memory>
#include <utility>
#include "base/functional/bind.h"
#include "base/memory/ptr_util.h"
#include "base/memory/weak_ptr.h"
#include "base/task/sequenced_task_runner.h"
#include "remoting/base/logging.h"
#include "remoting/host/action_executor.h"
#include "remoting/host/audio_capturer.h"
#include "remoting/host/curtain_mode.h"
#include "remoting/host/desktop_capturer_proxy.h"
#include "remoting/host/desktop_display_info_monitor.h"
#include "remoting/host/desktop_resizer.h"
#include "remoting/host/desktop_resizer_proxy.h"
#include "remoting/host/input_monitor/local_input_monitor.h"
#include "remoting/host/keyboard_layout_monitor.h"
#include "remoting/host/linux/curtain_mode_wayland.h"
#include "remoting/host/linux/gnome_action_executor.h"
#include "remoting/host/linux/gnome_desktop_display_info_monitor.h"
#include "remoting/host/linux/gnome_input_injector.h"
#include "remoting/host/linux/gnome_keyboard_layout_monitor.h"
#include "remoting/host/linux/gnome_local_input_monitor.h"
#include "remoting/host/linux/pipewire_desktop_capturer.h"
#include "remoting/host/linux/pipewire_mouse_cursor_monitor.h"
#include "third_party/webrtc/modules/desktop_capture/desktop_capture_types.h"
namespace remoting {
namespace {
template <typename Ret, typename Success, typename Error>
base::OnceCallback<Ret(base::expected<Success, Error>)> MakeExpectedCallback(
base::OnceCallback<Ret(Success)> success,
base::OnceCallback<Ret(Error)> error) {
return base::BindOnce(
[](base::OnceCallback<Ret(Success)> success,
base::OnceCallback<Ret(Error)> error,
base::expected<Success, Error> result) {
if (result.has_value()) {
return std::move(success).Run(result.value());
} else {
return std::move(error).Run(result.error());
}
},
std::move(success), std::move(error));
}
} // namespace
GnomeInteractionStrategy::~GnomeInteractionStrategy() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
GnomeInteractionStrategy::GnomeInteractionStrategy(
scoped_refptr<base::SequencedTaskRunner> ui_task_runner)
: ui_task_runner_(std::move(ui_task_runner)) {
capture_stream_manager_subscription_ =
remote_desktop_session_->capture_stream_manager()->AddObserver(this);
}
std::unique_ptr<ActionExecutor>
GnomeInteractionStrategy::CreateActionExecutor() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return std::make_unique<GnomeActionExecutor>(connection_);
}
std::unique_ptr<AudioCapturer> GnomeInteractionStrategy::CreateAudioCapturer() {
// TODO(jamiewalch): Support both pipe and session capture.
return AudioCapturer::Create();
}
std::unique_ptr<InputInjector> GnomeInteractionStrategy::CreateInputInjector() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// The EI session is guaranteed to exist, because this InteractionStrategy
// (and DesktopEnvironment) are only returned to the caller (ClientSession)
// after the EI session is initialized.
DCHECK(remote_desktop_session_->ei_session());
auto result = std::make_unique<GnomeInputInjector>(
remote_desktop_session_->ei_session(),
remote_desktop_session_->capture_stream_manager(),
remote_desktop_session_->connection(),
remote_desktop_session_->session_path());
remote_desktop_session_->ei_session()->SetInputInjector(result->GetWeakPtr());
return result;
}
std::unique_ptr<DesktopResizer>
GnomeInteractionStrategy::CreateDesktopResizer() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return std::make_unique<DesktopResizerProxy>(
remote_desktop_session_->desktop_resizer());
}
std::unique_ptr<DesktopCapturer> GnomeInteractionStrategy::CreateVideoCapturer(
webrtc::ScreenId id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto proxy = std::make_unique<DesktopCapturerProxy>(
base::SequencedTaskRunner::GetCurrentDefault());
proxy->set_supports_frame_callbacks(
PipewireDesktopCapturer::kSupportsFrameCallbacks);
base::WeakPtr<CaptureStream> stream =
remote_desktop_session_->capture_stream_manager()->GetStream(id);
if (stream) {
proxy->set_capturer(std::make_unique<PipewireDesktopCapturer>(stream));
} else {
HOST_LOG << "Video capturer for screen ID " << id
<< " will be initialized after the stream is ready.";
pending_desktop_capturer_proxies_[id] = proxy->GetWeakPtr();
}
return proxy;
}
std::unique_ptr<MouseCursorMonitor>
GnomeInteractionStrategy::CreateMouseCursorMonitor() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return std::make_unique<PipewireMouseCursorMonitor>(
remote_desktop_session_->mouse_cursor_capturer());
}
std::unique_ptr<KeyboardLayoutMonitor>
GnomeInteractionStrategy::CreateKeyboardLayoutMonitor(
base::RepeatingCallback<void(const protocol::KeyboardLayout&)> callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto result =
std::make_unique<GnomeKeyboardLayoutMonitor>(std::move(callback));
remote_desktop_session_->ei_session()->SetKeyboardLayoutMonitor(
result->GetWeakPtr());
return result;
}
std::unique_ptr<ActiveDisplayMonitor>
GnomeInteractionStrategy::CreateActiveDisplayMonitor(
base::RepeatingCallback<void(webrtc::ScreenId)> callback) {
return nullptr;
}
std::unique_ptr<DesktopDisplayInfoMonitor>
GnomeInteractionStrategy::CreateDisplayInfoMonitor() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return std::make_unique<GnomeDesktopDisplayInfoMonitor>(
remote_desktop_session_->display_config_monitor());
}
std::unique_ptr<LocalInputMonitor>
GnomeInteractionStrategy::CreateLocalInputMonitor() {
return std::make_unique<GnomeLocalInputMonitor>();
}
std::unique_ptr<CurtainMode> GnomeInteractionStrategy::CreateCurtainMode(
base::WeakPtr<ClientSessionControl> client_session_control) {
return std::make_unique<CurtainModeWayland>();
}
void GnomeInteractionStrategy::Init(InitCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
remote_desktop_session_->Init(base::BindOnce(
[](base::WeakPtr<GnomeInteractionStrategy> that, InitCallback callback,
base::expected<void, std::string> result) {
// Prevent `callback` from being called if the interaction strategy is
// already deleted.
if (that) {
std::move(callback).Run(result);
}
},
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void GnomeInteractionStrategy::OnPipewireCaptureStreamAdded(
base::WeakPtr<CaptureStream> stream) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!stream) {
return;
}
auto it = pending_desktop_capturer_proxies_.find(stream->screen_id());
if (it == pending_desktop_capturer_proxies_.end()) {
return;
}
if (it->second) {
it->second->set_capturer(std::make_unique<PipewireDesktopCapturer>(stream));
}
pending_desktop_capturer_proxies_.erase(it);
}
GnomeInteractionStrategyFactory::GnomeInteractionStrategyFactory(
scoped_refptr<base::SequencedTaskRunner> ui_task_runner)
: ui_task_runner_(std::move(ui_task_runner)) {}
GnomeInteractionStrategyFactory::~GnomeInteractionStrategyFactory() = default;
void GnomeInteractionStrategyFactory::Create(
const DesktopEnvironmentOptions& options,
CreateCallback callback) {
auto session =
base::WrapUnique(new GnomeInteractionStrategy(ui_task_runner_));
auto* raw = session.get();
raw->Init(base::BindOnce(
[](std::unique_ptr<GnomeInteractionStrategy> session,
CreateCallback callback, base::expected<void, std::string> result) {
if (!result.has_value()) {
LOG(ERROR) << "Failed to initialize Gnome Remote Desktop session: "
<< result.error();
std::move(callback).Run(nullptr);
return;
}
std::move(callback).Run(std::move(session));
},
std::move(session), std::move(callback)));
}
} // namespace remoting