WaylandMouseInput.cpp 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. #include "WaylandMouseInput.h"
  2. #include "WallpaperEngine/Render/Drivers/WaylandOpenGLDriver.h"
  3. #include <glm/common.hpp>
  4. #include <chrono>
  5. #include <cstdlib>
  6. #include <cstring>
  7. #include <regex>
  8. #include <string>
  9. #include <sys/socket.h>
  10. #include <sys/time.h>
  11. #include <sys/un.h>
  12. #include <unistd.h>
  13. using namespace WallpaperEngine::Input::Drivers;
  14. namespace {
  15. const WallpaperEngine::Render::Drivers::Output::WaylandOutputViewport* getActiveViewport (
  16. const WallpaperEngine::Render::Drivers::WaylandOpenGLDriver& driver
  17. ) {
  18. if (driver.viewportInFocus && driver.viewportInFocus->rendering) {
  19. return driver.viewportInFocus;
  20. }
  21. for (const auto* viewport : driver.m_screens) {
  22. if (viewport && viewport->rendering) {
  23. return viewport;
  24. }
  25. }
  26. return nullptr;
  27. }
  28. }
  29. WaylandMouseInput::WaylandMouseInput (const WallpaperEngine::Render::Drivers::WaylandOpenGLDriver& driver) :
  30. m_waylandDriver (driver) { }
  31. void WaylandMouseInput::update () {
  32. if (!this->m_waylandDriver.getApp ().getContext ().settings.mouse.enabled) {
  33. this->m_pos = { 0, 0 };
  34. return;
  35. }
  36. if (m_waylandDriver.viewportInFocus && m_waylandDriver.viewportInFocus->rendering) {
  37. this->m_pos = m_waylandDriver.viewportInFocus->mousePos;
  38. return;
  39. }
  40. const auto now = std::chrono::steady_clock::now ();
  41. if (now - this->m_lastHyprlandQuery < std::chrono::milliseconds (16)) {
  42. return;
  43. }
  44. this->m_lastHyprlandQuery = now;
  45. const auto globalCursor = this->queryHyprlandCursorPosition ();
  46. if (!globalCursor.has_value ()) {
  47. this->m_pos = { 0, 0 };
  48. return;
  49. }
  50. for (const auto* viewport : this->m_waylandDriver.m_screens) {
  51. if (!viewport || viewport->size.x <= 0 || viewport->size.y <= 0) {
  52. continue;
  53. }
  54. const double localX = globalCursor->x - viewport->position.x;
  55. const double localY = globalCursor->y - viewport->position.y;
  56. if (localX < 0.0 || localY < 0.0 || localX > viewport->size.x || localY > viewport->size.y) {
  57. continue;
  58. }
  59. this->m_pos = { localX * viewport->scale, (viewport->size.y - localY) * viewport->scale };
  60. return;
  61. }
  62. this->m_pos = { 0, 0 };
  63. }
  64. glm::dvec2 WaylandMouseInput::position () const {
  65. if (!this->m_waylandDriver.getApp ().getContext ().settings.mouse.enabled) {
  66. return { 0, 0 };
  67. }
  68. const auto* viewport = getActiveViewport (m_waylandDriver);
  69. if (!viewport) {
  70. return { 0, 0 };
  71. }
  72. if (viewport == m_waylandDriver.viewportInFocus) {
  73. return viewport->mousePos;
  74. }
  75. if (viewport->mousePos.x != 0 || viewport->mousePos.y != 0) {
  76. return viewport->mousePos;
  77. }
  78. return {
  79. static_cast<double> (viewport->size.x * viewport->scale) / 2.0,
  80. static_cast<double> (viewport->size.y * viewport->scale) / 2.0,
  81. };
  82. }
  83. WallpaperEngine::Input::MouseClickStatus WaylandMouseInput::leftClick () const {
  84. const auto* viewport = getActiveViewport (m_waylandDriver);
  85. if (viewport) {
  86. return viewport->leftClick;
  87. }
  88. return MouseClickStatus::Released;
  89. }
  90. std::optional<glm::dvec2> WaylandMouseInput::queryHyprlandCursorPosition () const {
  91. const char* signature = std::getenv ("HYPRLAND_INSTANCE_SIGNATURE");
  92. const char* runtime = std::getenv ("XDG_RUNTIME_DIR");
  93. if (!signature || !runtime) {
  94. return std::nullopt;
  95. }
  96. const std::string socketPath = std::string (runtime) + "/hypr/" + signature + "/.socket.sock";
  97. int fd = socket (AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
  98. if (fd < 0) {
  99. return std::nullopt;
  100. }
  101. timeval timeout {};
  102. timeout.tv_usec = 50000;
  103. if (
  104. setsockopt (fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof (timeout)) != 0
  105. || setsockopt (fd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof (timeout)) != 0
  106. ) {
  107. close (fd);
  108. return std::nullopt;
  109. }
  110. sockaddr_un addr {};
  111. addr.sun_family = AF_UNIX;
  112. if (socketPath.size () >= sizeof (addr.sun_path)) {
  113. close (fd);
  114. return std::nullopt;
  115. }
  116. std::strncpy (addr.sun_path, socketPath.c_str (), sizeof (addr.sun_path) - 1);
  117. if (connect (fd, reinterpret_cast<sockaddr*> (&addr), sizeof (addr)) != 0) {
  118. close (fd);
  119. return std::nullopt;
  120. }
  121. constexpr const char* request = "j/cursorpos";
  122. if (send (fd, request, std::strlen (request), MSG_NOSIGNAL) < 0) {
  123. close (fd);
  124. return std::nullopt;
  125. }
  126. shutdown (fd, SHUT_WR);
  127. std::string response;
  128. char buffer[256];
  129. ssize_t readBytes = 0;
  130. while ((readBytes = recv (fd, buffer, sizeof (buffer), 0)) > 0) {
  131. response.append (buffer, static_cast<std::size_t> (readBytes));
  132. }
  133. close (fd);
  134. static const std::regex xRegex (R"("x"\s*:\s*(-?\d+(?:\.\d+)?))");
  135. static const std::regex yRegex (R"("y"\s*:\s*(-?\d+(?:\.\d+)?))");
  136. std::smatch xMatch;
  137. std::smatch yMatch;
  138. if (!std::regex_search (response, xMatch, xRegex) || !std::regex_search (response, yMatch, yRegex)) {
  139. return std::nullopt;
  140. }
  141. try {
  142. return glm::dvec2 { std::stod (xMatch[1].str ()), std::stod (yMatch[1].str ()) };
  143. } catch (const std::exception&) {
  144. return std::nullopt;
  145. }
  146. }
  147. WallpaperEngine::Input::MouseClickStatus WaylandMouseInput::rightClick () const {
  148. const auto* viewport = getActiveViewport (m_waylandDriver);
  149. if (viewport) {
  150. return viewport->rightClick;
  151. }
  152. return MouseClickStatus::Released;
  153. }