1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
|
// Copyright 2018 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/input_monitor/local_hotkey_input_monitor_x11.h"
#include <sys/select.h>
#include <unistd.h>
#include <utility>
#include "base/compiler_specific.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/location.h"
#include "base/logging.h"
#include "remoting/host/input_monitor/local_input_monitor_x11_common.h"
#include "ui/gfx/x/keysyms/keysyms.h"
#include "ui/gfx/x/xinput.h"
namespace remoting {
LocalHotkeyInputMonitorX11::LocalHotkeyInputMonitorX11(
scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
scoped_refptr<base::SingleThreadTaskRunner> input_task_runner,
base::OnceClosure disconnect_callback)
: core_(new Core(caller_task_runner,
input_task_runner,
std::move(disconnect_callback))) {
core_->Start();
}
LocalHotkeyInputMonitorX11::~LocalHotkeyInputMonitorX11() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
core_->Stop();
}
LocalHotkeyInputMonitorX11::Core::Core(
scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
scoped_refptr<base::SingleThreadTaskRunner> input_task_runner,
base::OnceClosure disconnect_callback)
: caller_task_runner_(caller_task_runner),
input_task_runner_(input_task_runner),
disconnect_callback_(std::move(disconnect_callback)) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
DCHECK(disconnect_callback_);
}
void LocalHotkeyInputMonitorX11::Core::Start() {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
input_task_runner_->PostTask(FROM_HERE,
base::BindOnce(&Core::StartOnInputThread, this));
}
void LocalHotkeyInputMonitorX11::Core::Stop() {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
input_task_runner_->PostTask(FROM_HERE,
base::BindOnce(&Core::StopOnInputThread, this));
}
LocalHotkeyInputMonitorX11::Core::~Core() = default;
void LocalHotkeyInputMonitorX11::Core::StartOnInputThread() {
DCHECK(input_task_runner_->BelongsToCurrentThread());
DCHECK(!connection_);
connection_ = x11::Connection::Get();
connection_->AddEventObserver(this);
if (!connection_->xinput().present()) {
LOG(ERROR) << "X Input extension not available.";
return;
}
auto mask = CommonXIEventMaskForRootWindow();
connection_->xinput().XISelectEvents(
{connection_->default_root(),
{{x11::Input::DeviceId::AllMaster, {mask}}}});
connection_->Flush();
}
void LocalHotkeyInputMonitorX11::Core::StopOnInputThread() {
DCHECK(input_task_runner_->BelongsToCurrentThread());
connection_->RemoveEventObserver(this);
}
void LocalHotkeyInputMonitorX11::Core::OnEvent(const x11::Event& event) {
DCHECK(input_task_runner_->BelongsToCurrentThread());
// Ignore input if we've already initiated a disconnect.
if (!disconnect_callback_) {
return;
}
const auto* raw = event.As<x11::Input::RawDeviceEvent>();
// The X server may send unsolicited MappingNotify events without having
// selected them.
if (!raw) {
return;
}
if (raw->opcode != x11::Input::RawDeviceEvent::RawKeyPress &&
raw->opcode != x11::Input::RawDeviceEvent::RawKeyRelease) {
return;
}
const bool down = raw->opcode == x11::Input::RawDeviceEvent::RawKeyPress;
const auto key_sym =
connection_->KeycodeToKeysym(static_cast<x11::KeyCode>(raw->detail), 0);
if (key_sym == XK_Control_L || key_sym == XK_Control_R) {
ctrl_pressed_ = down;
} else if (key_sym == XK_Alt_L || key_sym == XK_Alt_R) {
alt_pressed_ = down;
} else if (key_sym == XK_Escape && down && alt_pressed_ && ctrl_pressed_) {
caller_task_runner_->PostTask(FROM_HERE, std::move(disconnect_callback_));
}
}
} // namespace remoting
|