Skip to content

Commit

Permalink
input-capture: impl keymap
Browse files Browse the repository at this point in the history
  • Loading branch information
3l0w committed Oct 7, 2024
1 parent fb0e34d commit b9b17ef
Show file tree
Hide file tree
Showing 9 changed files with 191 additions and 20 deletions.
3 changes: 2 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ pkg_check_modules(
gbm
hyprlang>=0.2.0
hyprutils
hyprwayland-scanner>=0.4.2)
hyprwayland-scanner>=0.4.2
uuid)

# check whether we can find sdbus-c++ through pkg-config
pkg_check_modules(SDBUS IMPORTED_TARGET sdbus-c++)
Expand Down
85 changes: 85 additions & 0 deletions src/helpers/MiscFunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
#include <vector>
#include <string>
#include <algorithm>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <uuid/uuid.h>

std::string execAndGet(const char* cmd) {
Debug::log(LOG, "execAndGet: {}", cmd);
Expand Down Expand Up @@ -64,3 +68,84 @@ void sendEmptyDbusMethodReply(sdbus::MethodCall& call, u_int32_t responseCode) {
reply << (uint32_t)responseCode;
reply.send();
}

std::string getRandomUUID() {
std::string uuid;
uuid_t uuid_;
uuid_generate_random(uuid_);
return std::format("{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}", (uint16_t)uuid_[0], (uint16_t)uuid_[1],
(uint16_t)uuid_[2], (uint16_t)uuid_[3], (uint16_t)uuid_[4], (uint16_t)uuid_[5], (uint16_t)uuid_[6], (uint16_t)uuid_[7], (uint16_t)uuid_[8],
(uint16_t)uuid_[9], (uint16_t)uuid_[10], (uint16_t)uuid_[11], (uint16_t)uuid_[12], (uint16_t)uuid_[13], (uint16_t)uuid_[14], (uint16_t)uuid_[15]);
}

std::pair<int, std::string> openExclusiveShm() {
// Only absolute paths can be shared across different shm_open() calls
std::string name = "/" + getRandomUUID();

for (size_t i = 0; i < 69; ++i) {
int fd = shm_open(name.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600);
if (fd >= 0)
return {fd, name};
}

return {-1, ""};
}

int allocateSHMFile(size_t len) {
auto [fd, name] = openExclusiveShm();
if (fd < 0)
return -1;

shm_unlink(name.c_str());

int ret;
do {
ret = ftruncate(fd, len);
} while (ret < 0 && errno == EINTR);

if (ret < 0) {
close(fd);
return -1;
}

return fd;
}

bool allocateSHMFilePair(size_t size, int* rw_fd_ptr, int* ro_fd_ptr) {
auto [fd, name] = openExclusiveShm();
if (fd < 0) {
return false;
}

// CLOEXEC is guaranteed to be set by shm_open
int ro_fd = shm_open(name.c_str(), O_RDONLY, 0);
if (ro_fd < 0) {
shm_unlink(name.c_str());
close(fd);
return false;
}

shm_unlink(name.c_str());

// Make sure the file cannot be re-opened in read-write mode (e.g. via
// "/proc/self/fd/" on Linux)
if (fchmod(fd, 0) != 0) {
close(fd);
close(ro_fd);
return false;
}

int ret;
do {
ret = ftruncate(fd, size);
} while (ret < 0 && errno == EINTR);
if (ret < 0) {
close(fd);
close(ro_fd);
return false;
}

*rw_fd_ptr = fd;
*ro_fd_ptr = ro_fd;
return true;
}
4 changes: 3 additions & 1 deletion src/helpers/MiscFunctions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@
std::string execAndGet(const char* cmd);
void addHyprlandNotification(const std::string& icon, float timeMs, const std::string& color, const std::string& message);
bool inShellPath(const std::string& exec);
void sendEmptyDbusMethodReply(sdbus::MethodCall& call, u_int32_t responseCode);
void sendEmptyDbusMethodReply(sdbus::MethodCall& call, u_int32_t responseCode);
int allocateSHMFile(size_t len);
bool allocateSHMFilePair(size_t size, int* rw_fd_ptr, int* ro_fd_ptr);
1 change: 1 addition & 0 deletions src/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ executable('xdg-desktop-portal-hyprland',
dependency('sdbus-c++'),
dependency('threads'),
dependency('wayland-client'),
dependency('uuid'),
],
include_directories: inc,
install: true,
Expand Down
20 changes: 19 additions & 1 deletion src/portals/InputCapture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ CInputCapturePortal::CInputCapturePortal(SP<CCHyprlandInputCaptureManagerV1> mgr
onMotion(wl_fixed_to_double(x), wl_fixed_to_double(y), wl_fixed_to_double(dx), wl_fixed_to_double(dy));
});

mgr->setKeymap([this](CCHyprlandInputCaptureManagerV1* r, hyprlandInputCaptureManagerV1KeymapFormat format, int32_t fd, uint32_t size) {
onKeymap(format == HYPRLAND_INPUT_CAPTURE_MANAGER_V1_KEYMAP_FORMAT_XKB_V1 ? fd : 0, size);
});

mgr->setKey([this](CCHyprlandInputCaptureManagerV1* r, uint32_t key, hyprlandInputCaptureManagerV1KeyState state) { onKey(key, state); });

mgr->setButton([this](CCHyprlandInputCaptureManagerV1* r, uint32_t button, hyprlandInputCaptureManagerV1ButtonState state) { onButton(button, state); });
Expand Down Expand Up @@ -107,7 +111,7 @@ void CInputCapturePortal::onCreateSession(sdbus::MethodCall& call) {
session->request = createDBusRequest(requestHandle);
session->request->onDestroy = [session]() { session->request.release(); };

session->eis = std::make_unique<EmulatedInputServer>("eis-" + sessionId);
session->eis = std::make_unique<EmulatedInputServer>("eis-" + sessionId, keymap);

sessions.emplace(sessionHandle, session);

Expand Down Expand Up @@ -370,6 +374,13 @@ void CInputCapturePortal::onKey(uint32_t id, bool pressed) {
value->key(id, pressed);
}

void CInputCapturePortal::onKeymap(int32_t fd, uint32_t size) {
keymap.fd = fd;
keymap.size = size;
for (const auto& [key, value] : sessions)
value->keymap(keymap);
}

void CInputCapturePortal::onButton(uint32_t button, bool pressed) {
for (const auto& [key, session] : sessions)
session->button(button, pressed);
Expand Down Expand Up @@ -525,6 +536,13 @@ void CInputCapturePortal::SSession::motion(double dx, double dy) {
eis->sendMotion(dx, dy);
}

void CInputCapturePortal::SSession::keymap(Keymap keymap) {
if (status == STOPPED)
return;

eis->setKeymap(keymap);
}

void CInputCapturePortal::SSession::key(uint32_t key, bool pressed) {
if (status != ACTIVATED)
return;
Expand Down
18 changes: 11 additions & 7 deletions src/portals/InputCapture.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class CInputCapturePortal {
void onConnectToEIS(sdbus::MethodCall& methodCall);

void onMotion(double x, double y, double dx, double dy);
void onKeymap(int32_t fd, uint32_t size);
void onKey(uint32_t key, bool pressed);
void onButton(uint32_t button, bool pressed);
void onAxis(bool axis, double value);
Expand All @@ -51,7 +52,7 @@ class CInputCapturePortal {

std::unordered_map<uint32_t, SBarrier> barriers;
uint32_t activationId = 0;
ClientStatus status = CREATED;
ClientStatus status = CREATED;

//
bool activate(double x, double y, uint32_t borderId);
Expand All @@ -61,6 +62,7 @@ class CInputCapturePortal {

void motion(double dx, double dy);
void key(uint32_t key, bool pressed);
void keymap(Keymap keymap);
void button(uint32_t button, bool pressed);
void axis(bool axis, double value);
void axisValue120(bool axis, int32_t value120);
Expand All @@ -81,12 +83,14 @@ class CInputCapturePortal {
uint sessionCounter = 0;
uint lastZoneSet = 0;

const std::string INTERFACE_NAME = "org.freedesktop.impl.portal.InputCapture";
const std::string OBJECT_PATH = "/org/freedesktop/portal/desktop";
Keymap keymap; //We store the active keymap ready to be sent when creating EIS

bool sessionValid(sdbus::ObjectPath sessionHandle);
const std::string INTERFACE_NAME = "org.freedesktop.impl.portal.InputCapture";
const std::string OBJECT_PATH = "/org/freedesktop/portal/desktop";

void activate(sdbus::ObjectPath sessionHandle, double x, double y, uint32_t borderId);
void deactivate(sdbus::ObjectPath sessionHandle);
void disable(sdbus::ObjectPath sessionHandle);
bool sessionValid(sdbus::ObjectPath sessionHandle);

void activate(sdbus::ObjectPath sessionHandle, double x, double y, uint32_t borderId);
void deactivate(sdbus::ObjectPath sessionHandle);
void disable(sdbus::ObjectPath sessionHandle);
};
54 changes: 52 additions & 2 deletions src/shared/Eis.cpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
#include "Eis.hpp"
#include "../core/PortalManager.hpp"
#include "../helpers/MiscFunctions.hpp"
#include "src/helpers/Log.hpp"
#include <alloca.h>
#include <libeis.h>
#include <sys/mman.h>
#include <unistd.h>

EmulatedInputServer::EmulatedInputServer(std::string socketName) {
EmulatedInputServer::EmulatedInputServer(std::string socketName, Keymap _keymap) {
Debug::log(LOG, "[EIS] Init socket: {}", socketName);

keymap = _keymap;

const char* xdg = getenv("XDG_RUNTIME_DIR");
if (xdg)
socketPath = std::string(xdg) + "/" + socketName;
Expand Down Expand Up @@ -152,13 +158,53 @@ void EmulatedInputServer::ensureKeyboard(eis_event* event) {
eis_device* keyboard = eis_seat_new_device(client.seat);
eis_device_configure_name(keyboard, "captured keyboard");
eis_device_configure_capability(keyboard, EIS_DEVICE_CAP_KEYBOARD);
// TODO: layout

if (keymap.fd != 0) {
Keymap _keymap = openKeymap();
Debug::log(LOG, "Using keymap {}", _keymap.fd);
eis_keymap* eis_keymap = eis_device_new_keymap(keyboard, EIS_KEYMAP_TYPE_XKB, _keymap.fd, _keymap.size);
eis_keymap_add(eis_keymap);
eis_keymap_unref(eis_keymap);
}

eis_device_add(keyboard);
eis_device_resume(keyboard);

client.keyboard = keyboard;
}

Keymap EmulatedInputServer::openKeymap() {
Keymap _keymap;

void* src = mmap(nullptr, keymap.size, PROT_READ, MAP_PRIVATE, keymap.fd, 0);
if (src == MAP_FAILED) {
Debug::log(ERR, "Failed to mmap the compositor keymap fd");
return _keymap;
}

int keymapFD = allocateSHMFile(keymap.size);
if (keymapFD < 0) {
Debug::log(ERR, "Failed to create a keymap file for keyboard grab");
return _keymap;
}

char* dst = (char*)mmap(nullptr, keymap.size, PROT_READ | PROT_WRITE, MAP_SHARED, keymapFD, 0);
if (dst == MAP_FAILED) {
Debug::log(ERR, "Failed to mmap a keymap file for keyboard grab");
close(keymapFD);
return _keymap;
}

memcpy(dst, src, keymap.size);
munmap(dst, keymap.size);
munmap(src, keymap.size);

_keymap.fd = keymapFD;
_keymap.size = keymap.size;

return _keymap;
}

//TODO: remove and re-add devices when monitors change (see: mutter/meta-input-capture-session.c:1107)

void EmulatedInputServer::clearPointer() {
Expand Down Expand Up @@ -205,6 +251,10 @@ void EmulatedInputServer::stopEmulating() {
eis_device_stop_emulating(client.keyboard);
}

void EmulatedInputServer::setKeymap(Keymap _keymap) {
keymap = _keymap;
}

void EmulatedInputServer::sendMotion(double x, double y) {
if (!client.pointer)
return;
Expand Down
24 changes: 17 additions & 7 deletions src/shared/Eis.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,24 @@
#include <libeis.h>
#include <string>

struct Keymap {
int32_t fd = 0;
uint32_t size = 0;
};

/*
* Responsible to creating a socket for input communication
*/
class EmulatedInputServer {
public:
EmulatedInputServer(std::string socketPath);
EmulatedInputServer(std::string socketPath, Keymap keymap);
std::string socketPath;

void startEmulating(int activationId);
void stopEmulating();

void setKeymap(Keymap _keymap);

void sendMotion(double x, double y);
void sendKey(uint32_t key, bool pressed);
void sendButton(uint32_t button, bool pressed);
Expand All @@ -38,10 +45,13 @@ class EmulatedInputServer {
eis_device* keyboard = nullptr;
} client;

int onEvent(eis_event* e);
void pollEvents();
void ensurePointer(eis_event* event);
void ensureKeyboard(eis_event* event);
void clearPointer();
void clearKeyboard();
Keymap keymap;

int onEvent(eis_event* e);
void pollEvents();
void ensurePointer(eis_event* event);
void ensureKeyboard(eis_event* event);
Keymap openKeymap();
void clearPointer();
void clearKeyboard();
};
2 changes: 1 addition & 1 deletion subprojects/hyprland-protocols

0 comments on commit b9b17ef

Please sign in to comment.