From 29177cb867be5a0ffb6152777691f68df23e080c Mon Sep 17 00:00:00 2001 From: Sebastian Woetzel Date: Wed, 3 Apr 2024 16:19:33 +0200 Subject: [PATCH 1/4] display: use XFIXES pointer barriers to make sure we can't leave the screen We now can only leave the screen/monitor if the wey device told us so. This fixes issues when the pointer was briefly visible on a neighbor screen before the wey device had time to reposition the pointer. This may happen when the displayed layout on the video wall doesn't match the configuration of the layout in the X server config. --- CMakeLists.txt | 6 +++++- src/display.cpp | 24 ++++++++++++++++++++++++ src/display.hpp | 5 ++++- 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 96a14f8..844eb8f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,6 +17,7 @@ find_package(PkgConfig REQUIRED) pkg_check_modules(XRandR REQUIRED IMPORTED_TARGET xrandr) pkg_check_modules(X11 REQUIRED IMPORTED_TARGET x11) pkg_check_modules(Xi REQUIRED IMPORTED_TARGET xi) +pkg_check_modules(Xfixes REQUIRED IMPORTED_TARGET xfixes) set(XDG_AUTOSTART_DIR "/etc/xdg/autostart" CACHE PATH "Path to the freedesktop autostart directory") @@ -42,7 +43,8 @@ target_link_libraries(lmss PUBLIC PkgConfig::XRandR PkgConfig::X11 - PkgConfig::Xi) + PkgConfig::Xi + PkgConfig::Xfixes) target_compile_options(lmss PRIVATE -Wall -Wextra -std=c++20) @@ -68,6 +70,7 @@ set(CPACK_GENERATOR "TGZ;DEB") # DEB set(CPACK_DEBIAN_PACKAGE_DEPENDS "libxi6 (>= 1.7.0)") +set(CPACK_DEBIAN_PACKAGE_DEPENDS "libxfixes3 (>= 5.0.3)") set(CPACK_DEBIAN_PACKAGE_MAINTAINER "WEY Technology AG") set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_CURRENT_SOURCE_DIR}/install/postinst;${CMAKE_CURRENT_SOURCE_DIR}/install/prerm;" ) @@ -83,6 +86,7 @@ set(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION set(CPACK_RPM_POST_INSTALL_SCRIPT_FILE "${CMAKE_CURRENT_SOURCE_DIR}/install/postinst") set(CPACK_RPM_PRE_UNINSTALL_SCRIPT_FILE "${CMAKE_CURRENT_SOURCE_DIR}/install/prerm") set(CPACK_RPM_PACKAGE_REQUIRES "libXi >= 1.7.0") +set(CPACK_RPM_PACKAGE_REQUIRES "libXfixes >= 5.0.3") set(CPACK_SOURCE_IGNORE_FILES /.git diff --git a/src/display.cpp b/src/display.cpp index 396748c..614ca41 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -39,6 +39,19 @@ display::display(logger & log, context & ctx) throw std::runtime_error("XInput 2.0 not supported"); } + int fixes_opcode; + + if (!XQueryExtension(dsp.get(), "XFIXES", &fixes_opcode, &event, &error)) { + throw std::runtime_error("XFIXES extension not supported"); + } + + major = 5; + minor = 0; + XFixesQueryVersion(dsp.get(), &major, &minor); + if (major < 5) { + throw std::runtime_error("XFIXES 5.0 not supported"); + } + if (std::filesystem::exists(screen_config_file)) { read_screen_layout_from_file(screen_config_file); } else { @@ -50,6 +63,12 @@ display::display(logger & log, context & ctx) ctx.get_el().add_fd(*xfd, std::bind(&display::handle_events, this, std::placeholders::_1)); } +display::~display() { + for (auto b : barriers) { + XFixesDestroyPointerBarrier(dsp.get(), b); + } +} + void display::read_screen_layout_from_file(std::string const & config_file) { std::ifstream cf(config_file); @@ -133,6 +152,11 @@ void display::add_monitor(int mon, int x, int y, int w, int h, Window root) { border_rects.emplace_back(x + w - BORDER_WIDTH, y, BORDER_WIDTH, h, mon, border_t::RIGHT, root); border_rects.emplace_back(x, y + h - BORDER_WIDTH, w, BORDER_WIDTH, mon, border_t::BOTTOM, root); + barriers.emplace_back(XFixesCreatePointerBarrier(dsp.get(), root, x, y, x, y + h, 0, 0, nullptr)); + barriers.emplace_back(XFixesCreatePointerBarrier(dsp.get(), root, x + w, y, x + w, y + h, 0, 0, nullptr)); + barriers.emplace_back(XFixesCreatePointerBarrier(dsp.get(), root, x, y, x + w, y, 0, 0, nullptr)); + barriers.emplace_back(XFixesCreatePointerBarrier(dsp.get(), root, x, y + h, x + w, y + h, 0, 0, nullptr)); + monitors.emplace_back(monitor_t { .id = mon, .x = x, .y = y, .w = w, .h = h, .root = root }); width = std::max(x + w, width); height = std::max(y + h, height); diff --git a/src/display.hpp b/src/display.hpp index 1f566c3..92d06a1 100644 --- a/src/display.hpp +++ b/src/display.hpp @@ -2,9 +2,10 @@ #pragma once +#include +#include #include #include -#include #include #include @@ -18,6 +19,7 @@ class display final { public: display(logger &, context &); + ~display(); void set_mouse_pos(mouse_pos_t const &); void handle_events(int); @@ -64,6 +66,7 @@ class display final { int xi_opcode = 0; dsp_t dsp; std::vector border_rects; + std::vector barriers; std::vector monitors; bool hidden = false; int width = 0; From 4621ab3d53b526e5f7588dd7491b3065cdd03aff Mon Sep 17 00:00:00 2001 From: Sebastian Woetzel Date: Thu, 4 Apr 2024 15:39:58 +0200 Subject: [PATCH 2/4] display: more barrier things --- src/display.cpp | 298 ++++++++++++++++++++++++++++++------------------ src/display.hpp | 10 +- 2 files changed, 194 insertions(+), 114 deletions(-) diff --git a/src/display.cpp b/src/display.cpp index 614ca41..60d6968 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -65,7 +65,7 @@ display::display(logger & log, context & ctx) display::~display() { for (auto b : barriers) { - XFixesDestroyPointerBarrier(dsp.get(), b); + XFixesDestroyPointerBarrier(dsp.get(), b.id); } } @@ -98,7 +98,23 @@ void display::read_screen_layout_from_file(std::string const & config_file) { throw std::runtime_error("empty screen layout configuration"); } - subscribe_to_motion_events(XDefaultRootWindow(dsp.get())); +// subscribe_to_motion_events(XDefaultRootWindow(dsp.get())); + subscribe_to_barrier_events(XDefaultRootWindow(dsp.get())); +} + +void display::subscribe_to_barrier_events(Window win) { + unsigned char mask_bytes[(XI_LASTEVENT + 7) / 8] = {0}; + XISetMask(mask_bytes, XI_BarrierHit); + + XIEventMask evmasks[1]; + evmasks[0].deviceid = XIAllDevices; + evmasks[0].mask_len = sizeof(mask_bytes); + evmasks[0].mask = mask_bytes; + + if (auto mask = XISelectEvents(dsp.get(), win, evmasks, 1); mask > 0) { + throw std::runtime_error("failed to select XI events"); + } + XSync(dsp.get(), False); } void display::subscribe_to_motion_events(Window win) { @@ -142,7 +158,8 @@ void display::detect_screen_layout() { } log.info("display size: " + std::to_string(width) + "x" + std::to_string(height)); - subscribe_to_motion_events(root); + //subscribe_to_motion_events(root); + subscribe_to_barrier_events(root); } } @@ -152,10 +169,10 @@ void display::add_monitor(int mon, int x, int y, int w, int h, Window root) { border_rects.emplace_back(x + w - BORDER_WIDTH, y, BORDER_WIDTH, h, mon, border_t::RIGHT, root); border_rects.emplace_back(x, y + h - BORDER_WIDTH, w, BORDER_WIDTH, mon, border_t::BOTTOM, root); - barriers.emplace_back(XFixesCreatePointerBarrier(dsp.get(), root, x, y, x, y + h, 0, 0, nullptr)); - barriers.emplace_back(XFixesCreatePointerBarrier(dsp.get(), root, x + w, y, x + w, y + h, 0, 0, nullptr)); - barriers.emplace_back(XFixesCreatePointerBarrier(dsp.get(), root, x, y, x + w, y, 0, 0, nullptr)); - barriers.emplace_back(XFixesCreatePointerBarrier(dsp.get(), root, x, y + h, x + w, y + h, 0, 0, nullptr)); + barriers.emplace_back(XFixesCreatePointerBarrier(dsp.get(), root, x + 1, y, x + 1, y + h, BarrierPositiveX, 0, nullptr), border_t::LEFT, mon); + barriers.emplace_back(XFixesCreatePointerBarrier(dsp.get(), root, x + w, y, x + w, y + h, BarrierNegativeX, 0, nullptr), border_t::RIGHT, mon); + barriers.emplace_back(XFixesCreatePointerBarrier(dsp.get(), root, x, y + 1, x + w, y + 1, BarrierPositiveY, 0, nullptr), border_t::TOP, mon); + barriers.emplace_back(XFixesCreatePointerBarrier(dsp.get(), root, x, y + h, x + w, y + h, BarrierNegativeY, 0, nullptr), border_t::BOTTOM, mon); monitors.emplace_back(monitor_t { .id = mon, .x = x, .y = y, .w = w, .h = h, .root = root }); width = std::max(x + w, width); @@ -169,8 +186,24 @@ void display::set_mouse_pos(mouse_pos_t const & mp) { << " pos:" << mp.pos; log.debug(ss.str()); + if (hidden) { + hidden = false; + for (auto const & m : monitors) { + XFixesShowCursor(dsp.get(), m.root); + XSync(dsp.get(), False); + } + } + + // auto const & m_last = get_mon_for_pos(last_pos); + // auto const & m_next = monitors[mp.screen]; + + //if (mp.border < border_t::HIDE && m_last == m_next) { + if (mp.border < border_t::HIDE && mp.screen == last_mon) { + log.debug("ignoring repositioning on border of same screen"); + return; + } + int x, y; - hidden = false; switch (mp.border) { case border_t::TOP: x = monitors[mp.screen].x + monitors[mp.screen].w * mp.pos / RESOLUTION; @@ -190,11 +223,16 @@ void display::set_mouse_pos(mouse_pos_t const & mp) { break; case border_t::HIDE: log.debug("hiding cursor"); + for (auto const & m : monitors) { + XFixesHideCursor(dsp.get(), m.root); + XSync(dsp.get(), False); + } + hidden = true; + return; // we position the pointer in the left bottom border, there is still 1 pixel visible // maybe we could really hide it by overriding the cursor bitmap - x = monitors[mp.screen].x; - y = monitors[mp.screen].y + height; - hidden = true; + // x = monitors[mp.screen].x; + // y = monitors[mp.screen].y + height; break; default: log.debug("centering cursor"); @@ -202,7 +240,8 @@ void display::set_mouse_pos(mouse_pos_t const & mp) { y = monitors[mp.screen].y + monitors[mp.screen].h / 2; } XWarpPointer(dsp.get(), None, monitors[mp.screen].root, 0, 0, 0, 0, x, y); - last_pos = { x, y, monitors[mp.screen].root }; + last_mon = mp.screen; +// last_pos = { x, y, monitors[mp.screen].root }; XFlush(dsp.get()); } @@ -217,117 +256,150 @@ void display::handle_events(int) { continue; } - if (ev.xcookie.evtype != XI_RawMotion || hidden) { + auto b = static_cast(ev.xcookie.data); + if (b->evtype != XI_BarrierHit || hidden) { XFreeEventData(dsp.get(), &ev.xcookie); continue; } - XFreeEventData(dsp.get(), &ev.xcookie); - - Window root, child; - int root_x, root_y; - int x, y; - unsigned int mask; - for (auto & m : monitors) { - if (XQueryPointer(dsp.get(), m.root, &root, &child, &root_x, &root_y, - &x, &y, &mask)) { + std::stringstream ss; + ss << "Barrier " << b->barrier << " hit: " << b->root_x << " / " << b->root_y << " dx/dy: " << b->dx << " / " << b->dy; + log.debug(ss.str()); + + int root_x = b->root_x; + int root_y = b->root_y; + + for (auto const & barrier : barriers) { + if (barrier.id == b->barrier) { + log.debug(std::to_string(barrier.mon)); + auto const & m_cur = monitors[barrier.mon]; //get_mon_for_pos({ root_x, root_y, b->root}); + log.debug("m_cur: " + std::to_string(m_cur.x) + " / " + std::to_string(m_cur.y) + " " + std::to_string(m_cur.w) + " x " + std::to_string(m_cur.h)); + + uint16_t pos = 0; + if (barrier.border == border_t::TOP || barrier.border == border_t::BOTTOM) { + pos = RESOLUTION * (root_x - m_cur.x) / m_cur.w; + } else { + pos = RESOLUTION * (root_y - m_cur.y) / m_cur.h; + } + last_mon = barrier.mon; + ctx.mouse_at_border({ + .screen = static_cast(barrier.mon), + .border = barrier.border, + .pos = pos + }); break; } } + last_pos = { root_x, root_y, b->root }; - log.debug("pointer: " + std::to_string(root_x) + "/" + std::to_string(root_y)); - - if (mask & (Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask)) { - log.debug("mouse button pressed, skipping border detection"); - continue; - } - - auto const & m_last = get_mon_for_pos(last_pos); - auto const & m_cur = get_mon_for_pos({root_x, root_y, root}); - auto diff_x = std::abs(root_x - last_pos.x); - auto diff_y = std::abs(root_y - last_pos.y); - uint16_t pos = 0; - border_t border; - - // we need to check if we crossed a border since last pointer update - if (root != last_pos.root) { - if (diff_x < diff_y) { // top/bottom - pos = RESOLUTION * (last_pos.x - m_last.x) / m_last.w; - if (root_y < m_cur.h / 2) { // top - border = border_t::TOP; - } else { //bottom - border = border_t::BOTTOM; - } - } else { // left/right - pos = RESOLUTION * (last_pos.y - m_last.y) / m_last.h; - if (root_x < m_cur.w / 2) { // right - border = border_t::RIGHT; - } else { // left - border = border_t::LEFT; - } - } + XFreeEventData(dsp.get(), &ev.xcookie); - log.debug("different root window, left at border: " + std::to_string(border)); - - ctx.mouse_at_border({ - .screen = static_cast(m_last.id), - .border = border, - .pos = pos - }); - } else if (m_last != m_cur && (diff_x >= 1 || diff_y >= 1)) { - if (m_cur.x == m_last.x) { - log.debug("mons are above each other: " - + std::to_string(m_cur.y) + "+" + std::to_string(m_cur.h) + " | " - + std::to_string(m_last.y) + "+" + std::to_string(m_last.h)); - - pos = RESOLUTION * (root_x - m_cur.x) / m_cur.w; - border = m_cur.y + m_cur.h == m_last.y ? border_t::TOP : border_t::BOTTOM; - } else { - log.debug("mons are next to each other: " - + std::to_string(m_cur.x) + "+" + std::to_string(m_cur.w) + " | " - + std::to_string(m_last.x) + "+" + std::to_string(m_last.w)); - - border = m_cur.x + m_cur.w == m_last.x ? border_t::LEFT : border_t::RIGHT; - pos = RESOLUTION * (root_y - m_cur.y) / m_cur.h; - } - log.debug("border: " + std::to_string(border)); - - ctx.mouse_at_border({ - .screen = static_cast(m_last.id), - .border = border, - .pos = pos - }); - } else { - for (auto & b : border_rects) { - if (b.inside(root_x, root_y) && b.root == root) { - switch (b.border) { - case border_t::TOP: - case border_t::BOTTOM: - pos = RESOLUTION * (root_x - b.x) / b.w; - break; - case border_t::LEFT: - case border_t::RIGHT: - pos = RESOLUTION * (root_y - b.y) / b.h; - break; - default: - throw std::runtime_error("invalid border"); - } - - log.debug("pointer at border " + std::to_string(b.border) + " of screen " - + std::to_string(b.screen)); - - ctx.mouse_at_border({ - .screen = static_cast(b.screen), - .border = b.border, - .pos = pos - }); - - break; - } - } - } - last_pos = { root_x, root_y, root }; + // Window root, child; + // int root_x, root_y; + // int x, y; + // unsigned int mask; + + // for (auto & m : monitors) { + // if (XQueryPointer(dsp.get(), m.root, &root, &child, &root_x, &root_y, + // &x, &y, &mask)) { + + // break; + // } + // } + + // log.debug("pointer: " + std::to_string(root_x) + "/" + std::to_string(root_y)); + + // if (mask & (Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask)) { + // log.debug("mouse button pressed, skipping border detection"); + // continue; + // } + + // auto const & m_last = get_mon_for_pos(last_pos); + // auto const & m_cur = get_mon_for_pos({root_x, root_y, root}); + // auto diff_x = std::abs(root_x - last_pos.x); + // auto diff_y = std::abs(root_y - last_pos.y); + // uint16_t pos = 0; + // border_t border; + + // // we need to check if we crossed a border since last pointer update + // if (root != last_pos.root) { + // if (diff_x < diff_y) { // top/bottom + // pos = RESOLUTION * (last_pos.x - m_last.x) / m_last.w; + // if (root_y < m_cur.h / 2) { // top + // border = border_t::TOP; + // } else { //bottom + // border = border_t::BOTTOM; + // } + // } else { // left/right + // pos = RESOLUTION * (last_pos.y - m_last.y) / m_last.h; + // if (root_x < m_cur.w / 2) { // right + // border = border_t::RIGHT; + // } else { // left + // border = border_t::LEFT; + // } + // } + + // log.debug("different root window, left at border: " + std::to_string(border)); + + // ctx.mouse_at_border({ + // .screen = static_cast(m_last.id), + // .border = border, + // .pos = pos + // }); + // } else if (m_last != m_cur && (diff_x >= 1 || diff_y >= 1)) { + // if (m_cur.x == m_last.x) { + // log.debug("mons are above each other: " + // + std::to_string(m_cur.y) + "+" + std::to_string(m_cur.h) + " | " + // + std::to_string(m_last.y) + "+" + std::to_string(m_last.h)); + + // pos = RESOLUTION * (root_x - m_cur.x) / m_cur.w; + // border = m_cur.y + m_cur.h == m_last.y ? border_t::TOP : border_t::BOTTOM; + // } else { + // log.debug("mons are next to each other: " + // + std::to_string(m_cur.x) + "+" + std::to_string(m_cur.w) + " | " + // + std::to_string(m_last.x) + "+" + std::to_string(m_last.w)); + + // border = m_cur.x + m_cur.w == m_last.x ? border_t::LEFT : border_t::RIGHT; + // pos = RESOLUTION * (root_y - m_cur.y) / m_cur.h; + // } + // log.debug("border: " + std::to_string(border)); + + // ctx.mouse_at_border({ + // .screen = static_cast(m_last.id), + // .border = border, + // .pos = pos + // }); + // } else { + // for (auto & b : border_rects) { + // if (b.inside(root_x, root_y) && b.root == root) { + // switch (b.border) { + // case border_t::TOP: + // case border_t::BOTTOM: + // pos = RESOLUTION * (root_x - b.x) / b.w; + // break; + // case border_t::LEFT: + // case border_t::RIGHT: + // pos = RESOLUTION * (root_y - b.y) / b.h; + // break; + // default: + // throw std::runtime_error("invalid border"); + // } + + // log.debug("pointer at border " + std::to_string(b.border) + " of screen " + // + std::to_string(b.screen)); + + // ctx.mouse_at_border({ + // .screen = static_cast(b.screen), + // .border = b.border, + // .pos = pos + // }); + + // break; + // } + // } + // } + // last_pos = { root_x, root_y, root }; } } } diff --git a/src/display.hpp b/src/display.hpp index 92d06a1..bd1c504 100644 --- a/src/display.hpp +++ b/src/display.hpp @@ -53,20 +53,28 @@ class display final { Window root = 0; }; + struct barrier_t { + PointerBarrier id = 0; + border_t border; + int mon = 0; + }; + monitor_t const & get_mon_for_pos(pos_t const &) const; void detect_screen_layout(); void read_screen_layout_from_file(std::string const &); void add_monitor(int mon, int x, int y, int w, int h, Window); void subscribe_to_motion_events(Window); + void subscribe_to_barrier_events(Window); pos_t last_pos; + size_t last_mon = 0; file_descriptor xfd; logger & log; context & ctx; int xi_opcode = 0; dsp_t dsp; std::vector border_rects; - std::vector barriers; + std::vector barriers; std::vector monitors; bool hidden = false; int width = 0; From 2afbd4c1d0ea5bad287c4ba28daa39a27763adef Mon Sep 17 00:00:00 2001 From: Sebastian Woetzel Date: Thu, 4 Apr 2024 15:41:24 +0200 Subject: [PATCH 3/4] main,display,lmss: add cmd argument do disable drag'n'drop over internal borders By default we disable the events generated when a monitor is left with a mouse button pressed. This allows dragging of elements such as windows across to a monitor next to the one we're leaving. If the monitors are not displayed the way they are configured in XRandR or Xorg, or it is generally not desired to be able to drag things around an extended desktop, we can now disable this behavior by providing the '-d' command line argument. With this option set, barrier events will be generated regardless of the mouse button state. --- src/display.cpp | 24 +++++++++++++++++++++++- src/display.hpp | 3 ++- src/lmss.cpp | 4 ++-- src/lmss.hpp | 2 +- src/main.cpp | 6 +++++- 5 files changed, 33 insertions(+), 6 deletions(-) diff --git a/src/display.cpp b/src/display.cpp index 60d6968..de47702 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -16,8 +16,9 @@ static const int BORDER_WIDTH = 1; static const int BORDER_CLEARANCE = 1; static const int RESOLUTION = 65536; -display::display(logger & log, context & ctx) +display::display(logger & log, bool dnd, context & ctx) : log(log) + , enable_dnd(dnd) , ctx(ctx) { auto dsp_var = getenv("DISPLAY"); @@ -269,6 +270,27 @@ void display::handle_events(int) { int root_x = b->root_x; int root_y = b->root_y; + if (enable_dnd) { + Window root, child; + int rx, ry; + int x, y; + unsigned int mask; + + for (auto & m : monitors) { + if (XQueryPointer(dsp.get(), m.root, &root, &child, &rx, &ry, &x, &y, &mask)) { + break; + } + } + + if (mask & (Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask)) { + log.debug("mouse button pressed, disabling barrier for next move"); + XIBarrierReleasePointer(dsp.get(), b->deviceid, b->barrier, b->eventid); + XFlush(dsp.get()); + XFreeEventData(dsp.get(), &ev.xcookie); + continue; + } + } + for (auto const & barrier : barriers) { if (barrier.id == b->barrier) { log.debug(std::to_string(barrier.mon)); diff --git a/src/display.hpp b/src/display.hpp index bd1c504..1858678 100644 --- a/src/display.hpp +++ b/src/display.hpp @@ -18,7 +18,7 @@ class display final { public: - display(logger &, context &); + display(logger &, bool dnd, context &); ~display(); void set_mouse_pos(mouse_pos_t const &); @@ -70,6 +70,7 @@ class display final { size_t last_mon = 0; file_descriptor xfd; logger & log; + bool enable_dnd; context & ctx; int xi_opcode = 0; dsp_t dsp; diff --git a/src/lmss.cpp b/src/lmss.cpp index bd0ff77..e83704d 100644 --- a/src/lmss.cpp +++ b/src/lmss.cpp @@ -4,11 +4,11 @@ #include -lmss::lmss(logger & log) +lmss::lmss(logger & log, bool dnd) : log(log) , el(log) , usb(log, *this) - , dsp(log, *this) + , dsp(log, dnd, *this) , tfd(timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC)) { if (!tfd.valid()) { diff --git a/src/lmss.hpp b/src/lmss.hpp index cd768ca..839b0ef 100644 --- a/src/lmss.hpp +++ b/src/lmss.hpp @@ -10,7 +10,7 @@ class lmss final : public context { public: - lmss(logger &); + lmss(logger &, bool dnd); event_loop & get_el() override { return el; } void set_mouse_pos(mouse_pos_t const &) override; diff --git a/src/main.cpp b/src/main.cpp index 08cbb66..d15e118 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -21,6 +21,10 @@ int main(int argc, char* argv[]) { .default_value(false) .implicit_value(true) .help("print lmss version"); + app.add_argument("-d", "--disable-dnd") + .default_value(true) + .implicit_value(false) + .help("disable drag'n'drop over internal borders"); try { app.parse_args(argc, argv); @@ -41,7 +45,7 @@ int main(int argc, char* argv[]) { while (true) { try { - lmss l(log); + lmss l(log, app.get("-d")); l.run(); } catch (std::runtime_error const & e) { log.err(e.what()); From 8396258c7a7df9f9f7432b3e39ba206b318b5de3 Mon Sep 17 00:00:00 2001 From: Sebastian Woetzel Date: Thu, 4 Apr 2024 15:56:00 +0200 Subject: [PATCH 4/4] display: cleanup --- src/display.cpp | 181 ++++-------------------------------------------- src/display.hpp | 12 ---- 2 files changed, 15 insertions(+), 178 deletions(-) diff --git a/src/display.cpp b/src/display.cpp index de47702..6b65243 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -12,7 +12,6 @@ static const char screen_config_file[] = "/etc/lmss.sl"; -static const int BORDER_WIDTH = 1; static const int BORDER_CLEARANCE = 1; static const int RESOLUTION = 65536; @@ -99,7 +98,6 @@ void display::read_screen_layout_from_file(std::string const & config_file) { throw std::runtime_error("empty screen layout configuration"); } -// subscribe_to_motion_events(XDefaultRootWindow(dsp.get())); subscribe_to_barrier_events(XDefaultRootWindow(dsp.get())); } @@ -118,20 +116,6 @@ void display::subscribe_to_barrier_events(Window win) { XSync(dsp.get(), False); } -void display::subscribe_to_motion_events(Window win) { - unsigned char mask_bytes[(XI_LASTEVENT + 7) / 8] = {0}; - XISetMask(mask_bytes, XI_RawMotion); - - XIEventMask evmasks[1]; - evmasks[0].deviceid = XIAllDevices; - evmasks[0].mask_len = sizeof(mask_bytes); - evmasks[0].mask = mask_bytes; - - if (auto mask = XISelectEvents(dsp.get(), win, evmasks, 1); mask > 0) { - throw std::runtime_error("failed to select XI events"); - } -} - void display::detect_screen_layout() { int screens = XScreenCount(dsp.get()); log.info("display has " + std::to_string(screens) + " screens"); @@ -158,26 +142,25 @@ void display::detect_screen_layout() { ++id; } - log.info("display size: " + std::to_string(width) + "x" + std::to_string(height)); - //subscribe_to_motion_events(root); subscribe_to_barrier_events(root); } } void display::add_monitor(int mon, int x, int y, int w, int h, Window root) { - border_rects.emplace_back(x, y, BORDER_WIDTH, h, mon, border_t::LEFT, root); - border_rects.emplace_back(x, y, w, BORDER_WIDTH, mon, border_t::TOP, root); - border_rects.emplace_back(x + w - BORDER_WIDTH, y, BORDER_WIDTH, h, mon, border_t::RIGHT, root); - border_rects.emplace_back(x, y + h - BORDER_WIDTH, w, BORDER_WIDTH, mon, border_t::BOTTOM, root); - - barriers.emplace_back(XFixesCreatePointerBarrier(dsp.get(), root, x + 1, y, x + 1, y + h, BarrierPositiveX, 0, nullptr), border_t::LEFT, mon); - barriers.emplace_back(XFixesCreatePointerBarrier(dsp.get(), root, x + w, y, x + w, y + h, BarrierNegativeX, 0, nullptr), border_t::RIGHT, mon); - barriers.emplace_back(XFixesCreatePointerBarrier(dsp.get(), root, x, y + 1, x + w, y + 1, BarrierPositiveY, 0, nullptr), border_t::TOP, mon); - barriers.emplace_back(XFixesCreatePointerBarrier(dsp.get(), root, x, y + h, x + w, y + h, BarrierNegativeY, 0, nullptr), border_t::BOTTOM, mon); + barriers.emplace_back( + XFixesCreatePointerBarrier(dsp.get(), root, x + 1, y, x + 1, y + h, BarrierPositiveX, 0, nullptr), + border_t::LEFT, mon); + barriers.emplace_back( + XFixesCreatePointerBarrier(dsp.get(), root, x + w, y, x + w, y + h, BarrierNegativeX, 0, nullptr), + border_t::RIGHT, mon); + barriers.emplace_back( + XFixesCreatePointerBarrier(dsp.get(), root, x, y + 1, x + w, y + 1, BarrierPositiveY, 0, nullptr), + border_t::TOP, mon); + barriers.emplace_back( + XFixesCreatePointerBarrier(dsp.get(), root, x, y + h, x + w, y + h, BarrierNegativeY, 0, nullptr), + border_t::BOTTOM, mon); monitors.emplace_back(monitor_t { .id = mon, .x = x, .y = y, .w = w, .h = h, .root = root }); - width = std::max(x + w, width); - height = std::max(y + h, height); } void display::set_mouse_pos(mouse_pos_t const & mp) { @@ -195,10 +178,6 @@ void display::set_mouse_pos(mouse_pos_t const & mp) { } } - // auto const & m_last = get_mon_for_pos(last_pos); - // auto const & m_next = monitors[mp.screen]; - - //if (mp.border < border_t::HIDE && m_last == m_next) { if (mp.border < border_t::HIDE && mp.screen == last_mon) { log.debug("ignoring repositioning on border of same screen"); return; @@ -230,11 +209,6 @@ void display::set_mouse_pos(mouse_pos_t const & mp) { } hidden = true; return; - // we position the pointer in the left bottom border, there is still 1 pixel visible - // maybe we could really hide it by overriding the cursor bitmap - // x = monitors[mp.screen].x; - // y = monitors[mp.screen].y + height; - break; default: log.debug("centering cursor"); x = monitors[mp.screen].x + monitors[mp.screen].w / 2; @@ -242,7 +216,6 @@ void display::set_mouse_pos(mouse_pos_t const & mp) { } XWarpPointer(dsp.get(), None, monitors[mp.screen].root, 0, 0, 0, 0, x, y); last_mon = mp.screen; -// last_pos = { x, y, monitors[mp.screen].root }; XFlush(dsp.get()); } @@ -264,7 +237,8 @@ void display::handle_events(int) { } std::stringstream ss; - ss << "Barrier " << b->barrier << " hit: " << b->root_x << " / " << b->root_y << " dx/dy: " << b->dx << " / " << b->dy; + ss << "Barrier " << b->barrier << " hit: " << b->root_x << " / " << b->root_y + << " dx/dy: " << b->dx << " / " << b->dy; log.debug(ss.str()); int root_x = b->root_x; @@ -293,9 +267,7 @@ void display::handle_events(int) { for (auto const & barrier : barriers) { if (barrier.id == b->barrier) { - log.debug(std::to_string(barrier.mon)); - auto const & m_cur = monitors[barrier.mon]; //get_mon_for_pos({ root_x, root_y, b->root}); - log.debug("m_cur: " + std::to_string(m_cur.x) + " / " + std::to_string(m_cur.y) + " " + std::to_string(m_cur.w) + " x " + std::to_string(m_cur.h)); + auto const & m_cur = monitors[barrier.mon]; uint16_t pos = 0; if (barrier.border == border_t::TOP || barrier.border == border_t::BOTTOM) { @@ -313,130 +285,7 @@ void display::handle_events(int) { break; } } - last_pos = { root_x, root_y, b->root }; - XFreeEventData(dsp.get(), &ev.xcookie); - - // Window root, child; - // int root_x, root_y; - // int x, y; - // unsigned int mask; - - // for (auto & m : monitors) { - // if (XQueryPointer(dsp.get(), m.root, &root, &child, &root_x, &root_y, - // &x, &y, &mask)) { - - // break; - // } - // } - - // log.debug("pointer: " + std::to_string(root_x) + "/" + std::to_string(root_y)); - - // if (mask & (Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask)) { - // log.debug("mouse button pressed, skipping border detection"); - // continue; - // } - - // auto const & m_last = get_mon_for_pos(last_pos); - // auto const & m_cur = get_mon_for_pos({root_x, root_y, root}); - // auto diff_x = std::abs(root_x - last_pos.x); - // auto diff_y = std::abs(root_y - last_pos.y); - // uint16_t pos = 0; - // border_t border; - - // // we need to check if we crossed a border since last pointer update - // if (root != last_pos.root) { - // if (diff_x < diff_y) { // top/bottom - // pos = RESOLUTION * (last_pos.x - m_last.x) / m_last.w; - // if (root_y < m_cur.h / 2) { // top - // border = border_t::TOP; - // } else { //bottom - // border = border_t::BOTTOM; - // } - // } else { // left/right - // pos = RESOLUTION * (last_pos.y - m_last.y) / m_last.h; - // if (root_x < m_cur.w / 2) { // right - // border = border_t::RIGHT; - // } else { // left - // border = border_t::LEFT; - // } - // } - - // log.debug("different root window, left at border: " + std::to_string(border)); - - // ctx.mouse_at_border({ - // .screen = static_cast(m_last.id), - // .border = border, - // .pos = pos - // }); - // } else if (m_last != m_cur && (diff_x >= 1 || diff_y >= 1)) { - // if (m_cur.x == m_last.x) { - // log.debug("mons are above each other: " - // + std::to_string(m_cur.y) + "+" + std::to_string(m_cur.h) + " | " - // + std::to_string(m_last.y) + "+" + std::to_string(m_last.h)); - - // pos = RESOLUTION * (root_x - m_cur.x) / m_cur.w; - // border = m_cur.y + m_cur.h == m_last.y ? border_t::TOP : border_t::BOTTOM; - // } else { - // log.debug("mons are next to each other: " - // + std::to_string(m_cur.x) + "+" + std::to_string(m_cur.w) + " | " - // + std::to_string(m_last.x) + "+" + std::to_string(m_last.w)); - - // border = m_cur.x + m_cur.w == m_last.x ? border_t::LEFT : border_t::RIGHT; - // pos = RESOLUTION * (root_y - m_cur.y) / m_cur.h; - // } - // log.debug("border: " + std::to_string(border)); - - // ctx.mouse_at_border({ - // .screen = static_cast(m_last.id), - // .border = border, - // .pos = pos - // }); - // } else { - // for (auto & b : border_rects) { - // if (b.inside(root_x, root_y) && b.root == root) { - // switch (b.border) { - // case border_t::TOP: - // case border_t::BOTTOM: - // pos = RESOLUTION * (root_x - b.x) / b.w; - // break; - // case border_t::LEFT: - // case border_t::RIGHT: - // pos = RESOLUTION * (root_y - b.y) / b.h; - // break; - // default: - // throw std::runtime_error("invalid border"); - // } - - // log.debug("pointer at border " + std::to_string(b.border) + " of screen " - // + std::to_string(b.screen)); - - // ctx.mouse_at_border({ - // .screen = static_cast(b.screen), - // .border = b.border, - // .pos = pos - // }); - - // break; - // } - // } - // } - // last_pos = { root_x, root_y, root }; } } } - -display::monitor_t const & display::get_mon_for_pos(pos_t const & pos) const { - for (auto const & m : monitors) { - if ((pos.root == m.root || pos.root == 0) - && pos.x >= m.x - && pos.x <= m.x + m.w - && pos.y >= m.y - && pos.y <= m.y + m.h) { - - return m; - } - } - - throw std::logic_error("pointer not inside of any known monitor"); -} diff --git a/src/display.hpp b/src/display.hpp index 1858678..10db9e8 100644 --- a/src/display.hpp +++ b/src/display.hpp @@ -47,26 +47,17 @@ class display final { inline bool operator!=(monitor_t const & other) const { return !operator==(other); } }; - struct pos_t { - int x = 0; - int y = 0; - Window root = 0; - }; - struct barrier_t { PointerBarrier id = 0; border_t border; int mon = 0; }; - monitor_t const & get_mon_for_pos(pos_t const &) const; void detect_screen_layout(); void read_screen_layout_from_file(std::string const &); void add_monitor(int mon, int x, int y, int w, int h, Window); - void subscribe_to_motion_events(Window); void subscribe_to_barrier_events(Window); - pos_t last_pos; size_t last_mon = 0; file_descriptor xfd; logger & log; @@ -74,10 +65,7 @@ class display final { context & ctx; int xi_opcode = 0; dsp_t dsp; - std::vector border_rects; std::vector barriers; std::vector monitors; bool hidden = false; - int width = 0; - int height = 0; };