WaylandMouseInput.cpp 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. #include "WaylandMouseInput.h"
  2. #include "WallpaperEngine/Render/Drivers/WaylandOpenGLDriver.h"
  3. #include <chrono>
  4. #include <cstdlib>
  5. #include <cstring>
  6. #include <glm/common.hpp>
  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. #ifdef ENABLE_X11
  14. #include <X11/Xlib.h>
  15. #endif /* ENABLE_X11 */
  16. #ifdef ENABLE_KDE_FEATURES
  17. #include "WallpaperEngine/Desktop/KDESession.h"
  18. #endif /* ENABLE_KDE_FEATURES */
  19. using namespace WallpaperEngine::Input::Drivers;
  20. WaylandMouseInput::WaylandMouseInput (const WallpaperEngine::Render::Drivers::WaylandOpenGLDriver& driver) :
  21. m_waylandDriver (driver) {
  22. #ifdef ENABLE_KDE_FEATURES
  23. // outside Plasma there is no KWin to load the cursor script into
  24. if (WallpaperEngine::Desktop::isKDESession ()) {
  25. this->m_kdeCursor = std::make_unique<KDECursorInput> ();
  26. }
  27. #endif /* ENABLE_KDE_FEATURES */
  28. }
  29. void WaylandMouseInput::update () {
  30. if (!this->m_waylandDriver.getApp ().getContext ().settings.mouse.enabled) {
  31. this->m_pos = { 0, 0 };
  32. return;
  33. }
  34. if (m_waylandDriver.viewportInFocus && m_waylandDriver.viewportInFocus->rendering) {
  35. this->m_pos = m_waylandDriver.viewportInFocus->mousePos;
  36. return;
  37. }
  38. #ifdef ENABLE_KDE_FEATURES
  39. if (const auto kdeCursor = this->m_kdeCursor ? this->m_kdeCursor->position () : std::nullopt;
  40. kdeCursor.has_value () && this->matchViewport (*kdeCursor)) {
  41. return;
  42. }
  43. #endif /* ENABLE_KDE_FEATURES */
  44. const auto now = std::chrono::steady_clock::now ();
  45. if (now - this->m_lastGlobalCursorQuery < std::chrono::milliseconds (16)) {
  46. return;
  47. }
  48. this->m_lastGlobalCursorQuery = now;
  49. auto globalCursor = this->queryHyprlandCursorPosition ();
  50. #ifdef ENABLE_X11
  51. if (!globalCursor.has_value ()) {
  52. globalCursor = this->queryX11CursorPosition ();
  53. }
  54. #endif /* ENABLE_X11 */
  55. if (!globalCursor.has_value () || !this->matchViewport (*globalCursor)) {
  56. this->m_pos = { 0, 0 };
  57. }
  58. }
  59. bool WaylandMouseInput::matchViewport (const glm::dvec2& globalCursor) {
  60. for (const auto* viewport : this->m_waylandDriver.m_screens) {
  61. if (!viewport || viewport->size.x <= 0 || viewport->size.y <= 0) {
  62. continue;
  63. }
  64. const double localX = globalCursor.x - viewport->position.x;
  65. const double localY = globalCursor.y - viewport->position.y;
  66. if (localX < 0.0 || localY < 0.0 || localX > viewport->size.x || localY > viewport->size.y) {
  67. continue;
  68. }
  69. this->m_pos = { localX * viewport->scale, (viewport->size.y - localY) * viewport->scale };
  70. return true;
  71. }
  72. return false;
  73. }
  74. glm::dvec2 WaylandMouseInput::position () const {
  75. if (!this->m_waylandDriver.getApp ().getContext ().settings.mouse.enabled) {
  76. return { 0, 0 };
  77. }
  78. return this->m_pos;
  79. }
  80. WallpaperEngine::Input::MouseClickStatus WaylandMouseInput::leftClick () const {
  81. const auto* viewport = this->getActiveOutputViewport ();
  82. if (viewport) {
  83. return viewport->leftClick;
  84. }
  85. return MouseClickStatus::Released;
  86. }
  87. const WallpaperEngine::Render::Drivers::Output::WaylandOutputViewport*
  88. WaylandMouseInput::getActiveOutputViewport () const {
  89. if (this->m_waylandDriver.viewportInFocus && this->m_waylandDriver.viewportInFocus->rendering) {
  90. return this->m_waylandDriver.viewportInFocus;
  91. }
  92. for (const auto* viewport : this->m_waylandDriver.m_screens) {
  93. if (viewport && viewport->rendering) {
  94. return viewport;
  95. }
  96. }
  97. return nullptr;
  98. }
  99. std::optional<glm::dvec2> WaylandMouseInput::queryHyprlandCursorPosition () const {
  100. const char* signature = std::getenv ("HYPRLAND_INSTANCE_SIGNATURE");
  101. const char* runtime = std::getenv ("XDG_RUNTIME_DIR");
  102. if (!signature || !runtime) {
  103. return std::nullopt;
  104. }
  105. const std::string socketPath = std::string (runtime) + "/hypr/" + signature + "/.socket.sock";
  106. int fd = socket (AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
  107. if (fd < 0) {
  108. return std::nullopt;
  109. }
  110. timeval timeout {};
  111. timeout.tv_usec = 50000;
  112. if (setsockopt (fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof (timeout)) != 0
  113. || setsockopt (fd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof (timeout)) != 0) {
  114. close (fd);
  115. return std::nullopt;
  116. }
  117. sockaddr_un addr {};
  118. addr.sun_family = AF_UNIX;
  119. if (socketPath.size () >= sizeof (addr.sun_path)) {
  120. close (fd);
  121. return std::nullopt;
  122. }
  123. std::strncpy (addr.sun_path, socketPath.c_str (), sizeof (addr.sun_path) - 1);
  124. if (connect (fd, reinterpret_cast<sockaddr*> (&addr), sizeof (addr)) != 0) {
  125. close (fd);
  126. return std::nullopt;
  127. }
  128. constexpr const char* request = "j/cursorpos";
  129. if (send (fd, request, std::strlen (request), MSG_NOSIGNAL) < 0) {
  130. close (fd);
  131. return std::nullopt;
  132. }
  133. shutdown (fd, SHUT_WR);
  134. std::string response;
  135. char buffer[256];
  136. ssize_t readBytes = 0;
  137. while ((readBytes = recv (fd, buffer, sizeof (buffer), 0)) > 0) {
  138. response.append (buffer, static_cast<std::size_t> (readBytes));
  139. }
  140. close (fd);
  141. static const std::regex xRegex (R"("x"\s*:\s*(-?\d+(?:\.\d+)?))");
  142. static const std::regex yRegex (R"("y"\s*:\s*(-?\d+(?:\.\d+)?))");
  143. std::smatch xMatch;
  144. std::smatch yMatch;
  145. if (!std::regex_search (response, xMatch, xRegex) || !std::regex_search (response, yMatch, yRegex)) {
  146. return std::nullopt;
  147. }
  148. try {
  149. return glm::dvec2 { std::stod (xMatch[1].str ()), std::stod (yMatch[1].str ()) };
  150. } catch (const std::exception&) {
  151. return std::nullopt;
  152. }
  153. }
  154. #ifdef ENABLE_X11
  155. std::optional<glm::dvec2> WaylandMouseInput::queryX11CursorPosition () const {
  156. Display* display = XOpenDisplay (nullptr);
  157. if (!display) {
  158. return std::nullopt;
  159. }
  160. const Window root = DefaultRootWindow (display);
  161. Window returnedRoot = 0;
  162. Window returnedChild = 0;
  163. int rootX = 0;
  164. int rootY = 0;
  165. int childX = 0;
  166. int childY = 0;
  167. unsigned int mask = 0;
  168. const Bool ok
  169. = XQueryPointer (display, root, &returnedRoot, &returnedChild, &rootX, &rootY, &childX, &childY, &mask);
  170. XCloseDisplay (display);
  171. if (!ok) {
  172. return std::nullopt;
  173. }
  174. return glm::dvec2 { static_cast<double> (rootX), static_cast<double> (rootY) };
  175. }
  176. #endif /* ENABLE_X11 */
  177. WallpaperEngine::Input::MouseClickStatus WaylandMouseInput::rightClick () const {
  178. const auto* viewport = this->getActiveOutputViewport ();
  179. if (viewport) {
  180. return viewport->rightClick;
  181. }
  182. return MouseClickStatus::Released;
  183. }