From 65edba33216420eee276175a4fdb669265bd2444 Mon Sep 17 00:00:00 2001 From: NBonaparte Date: Sat, 2 Sep 2017 21:45:45 -0700 Subject: [PATCH] feat: add cursor change --- cmake/02-opts.cmake | 2 + cmake/03-libs.cmake | 1 + cmake/05-summary.cmake | 1 + include/components/bar.hpp | 7 ++- include/components/types.hpp | 4 ++ include/events/signal.hpp | 3 ++ include/events/signal_fwd.hpp | 1 + include/x11/cursor.hpp | 22 ++++++++++ src/components/bar.cpp | 80 ++++++++++++++++++++++++++++++++++- src/x11/cursor.cpp | 33 +++++++++++++++ 10 files changed, 150 insertions(+), 4 deletions(-) create mode 100644 include/x11/cursor.hpp create mode 100644 src/x11/cursor.cpp diff --git a/cmake/02-opts.cmake b/cmake/02-opts.cmake index 905696f4..70ec1fbf 100644 --- a/cmake/02-opts.cmake +++ b/cmake/02-opts.cmake @@ -9,6 +9,7 @@ checklib(ENABLE_MPD "pkg-config" libmpdclient) checklib(ENABLE_NETWORK "cmake" Libiw) checklib(WITH_XRM "pkg-config" xcb-xrm) checklib(WITH_XRANDR_MONITORS "pkg-config" "xcb-randr>=1.12") +checklib(WITH_CURSOR "pkg-config" "xcb-cursor") if(NOT DEFINED ENABLE_CCACHE AND CMAKE_BUILD_TYPE_UPPER MATCHES DEBUG) set(ENABLE_CCACHE ON) @@ -36,6 +37,7 @@ option(WITH_XSYNC "xcb-sync support" OFF) option(WITH_XCOMPOSITE "xcb-composite support" OFF) option(WITH_XKB "xcb-xkb support" ON) option(WITH_XRM "xcb-xrm support" ON) +option(WITH_CURSOR "xcb-cursor support" ON) if(CMAKE_BUILD_TYPE_UPPER MATCHES DEBUG) option(DEBUG_LOGGER "Debug logging" ON) diff --git a/cmake/03-libs.cmake b/cmake/03-libs.cmake index de176a91..e7cea39c 100644 --- a/cmake/03-libs.cmake +++ b/cmake/03-libs.cmake @@ -20,3 +20,4 @@ querylib(WITH_XRANDR_MONITORS "pkg-config" "xcb-randr>=1.12" libs dirs) querylib(WITH_XRENDER "pkg-config" xcb-render libs dirs) querylib(WITH_XRM "pkg-config" xcb-xrm libs dirs) querylib(WITH_XSYNC "pkg-config" xcb-sync libs dirs) +querylib(WITH_CURSOR "pkg-config" xcb-cursor libs dirs) diff --git a/cmake/05-summary.cmake b/cmake/05-summary.cmake index b5c3f58b..f56b13ee 100644 --- a/cmake/05-summary.cmake +++ b/cmake/05-summary.cmake @@ -28,6 +28,7 @@ colored_option(" xcb-sync" WITH_XSYNC) colored_option(" xcb-composite" WITH_XCOMPOSITE) colored_option(" xcb-xkb" WITH_XKB) colored_option(" xcb-xrm" WITH_XRM) +colored_option(" xcb-cursor" WITH_CURSOR) if(CMAKE_BUILD_TYPE_UPPER MATCHES DEBUG) message(STATUS " Debug options:") diff --git a/include/components/bar.hpp b/include/components/bar.hpp index 616ecdcc..cf90c5b3 100644 --- a/include/components/bar.hpp +++ b/include/components/bar.hpp @@ -26,9 +26,9 @@ class tray_manager; // }}} class bar : public xpp::event::sink, + evt::leave_notify, evt::motion_notify, evt::destroy_notify, evt::client_message>, public signal_receiver { + signals::ui::shade_window, signals::ui::unshade_window, signals::ui::dim_window, signals::ui::cursor_change> { public: using make_type = unique_ptr; static make_type make(bool only_initialize_values = false); @@ -56,6 +56,7 @@ class bar : public xpp::event::sink actions{}; bool dimmed{false}; diff --git a/include/events/signal.hpp b/include/events/signal.hpp index b9fc4250..986a6125 100644 --- a/include/events/signal.hpp +++ b/include/events/signal.hpp @@ -102,6 +102,9 @@ namespace signals { struct button_press : public detail::value_signal { using base_type::base_type; }; + struct cursor_change : public detail::value_signal { + using base_type::base_type; + }; struct visibility_change : public detail::value_signal { using base_type::base_type; }; diff --git a/include/events/signal_fwd.hpp b/include/events/signal_fwd.hpp index a2ffb911..f7da2f89 100644 --- a/include/events/signal_fwd.hpp +++ b/include/events/signal_fwd.hpp @@ -32,6 +32,7 @@ namespace signals { struct changed; struct tick; struct button_press; + struct cursor_change; struct visibility_change; struct dim_window; struct shade_window; diff --git a/include/x11/cursor.hpp b/include/x11/cursor.hpp new file mode 100644 index 00000000..9653f628 --- /dev/null +++ b/include/x11/cursor.hpp @@ -0,0 +1,22 @@ +#pragma once + +//#if not WITH_CURSOR +//#error "Not built with support for xcb-cursor..." +//#endif + +#include + +#include "common.hpp" +#include "x11/connection.hpp" +#include "utils/string.hpp" + +POLYBAR_NS + +namespace cursor_util { + static const vector pointer_names {"pointing_hand", "pointer", "hand", "hand1", "hand2", "e29285e634086352946a0e7090d73106", "9d800788f1b08800ae810202380a0822"}; + static const vector arrow_names {"left_ptr", "arrow", "dnd-none", "op_left_arrow"}; + static const vector ns_resize_names {"size_ver", "sb_v_double_arrow", "v_double_arrow", "n-resize", "s-resize", "col-resize", "top_side", "bottom_side", "base_arrow_up", "base_arrow_down", "based_arrow_down", "based_arrow_up", "00008160000006810000408080010102"}; + bool set_cursor(xcb_connection_t *c, xcb_screen_t *screen, xcb_window_t w, string name); +} + +POLYBAR_NS_END diff --git a/src/components/bar.cpp b/src/components/bar.cpp index 4807f8f6..6274a5bb 100644 --- a/src/components/bar.cpp +++ b/src/components/bar.cpp @@ -16,6 +16,7 @@ #include "utils/string.hpp" #include "x11/atoms.hpp" #include "x11/connection.hpp" +#include "x11/cursor.hpp" #include "x11/ewmh.hpp" #include "x11/extensions/all.hpp" #include "x11/icccm.hpp" @@ -126,6 +127,8 @@ bar::bar(connection& conn, signal_emitter& emitter, const config& config, const m_opts.dimvalue = m_conf.get(bs, "dim-value", 1.0); m_opts.dimvalue = math_util::cap(m_opts.dimvalue, 0.0, 1.0); + m_opts.cursor_click = m_conf.get(bs, "cursor-click", ""s); + m_opts.cursor_scroll = m_conf.get(bs, "cursor-scroll", ""s); // Build WM_NAME m_opts.wmname = m_conf.get(bs, "wm-name", "polybar-" + bs.substr(4) + "_" + m_opts.monitor->name); m_opts.wmname = string_util::replace(m_opts.wmname, " ", "-"); @@ -554,7 +557,6 @@ void bar::handle(const evt::enter_notify&) { } #endif #endif - if (m_opts.dimmed) { m_taskqueue->defer_unique("window-dim", 25ms, [&](size_t) { m_opts.dimmed = false; @@ -580,7 +582,6 @@ void bar::handle(const evt::leave_notify&) { } #endif #endif - if (!m_opts.dimmed) { m_taskqueue->defer_unique("window-dim", 3s, [&](size_t) { m_opts.dimmed = true; @@ -589,6 +590,70 @@ void bar::handle(const evt::leave_notify&) { } } +/** + * Event handler for XCB_MOTION_NOTIFY events + * + * Used to change the cursor depending on the module + */ +void bar::handle(const evt::motion_notify& evt) { + m_log.trace("bar: Detected motion: %i at pos(%i, %i)", evt->detail, evt->event_x, evt->event_y); + + m_motion_pos = evt->event_x; + // scroll cursor is less important than click cursor, so we shouldn't return until we are sure there is no click action + bool found_scroll = false; + const auto find_click_area = [&](action action) { + if (!m_opts.cursor_click.empty() && + (action.button == mousebtn::LEFT || action.button == mousebtn::MIDDLE || action.button == mousebtn::RIGHT)) { + if (!string_util::compare(m_opts.cursor, m_opts.cursor_click)) { + m_opts.cursor = m_opts.cursor_click; + m_sig.emit(cursor_change{string{m_opts.cursor}}); + } + return true; + } else if (!m_opts.cursor_scroll.empty() && (action.button == mousebtn::SCROLL_UP || action.button == mousebtn::SCROLL_DOWN)) { + if (!found_scroll) { + if (!string_util::compare(m_opts.cursor, m_opts.cursor_scroll)) { + found_scroll = true; + } else { + return true; + } + } + } + return false; + }; + + for (auto&& action : m_renderer->actions()) { + if (action.test(m_motion_pos)) { + m_log.trace("Found matching input area"); + if(find_click_area(action)) + return; + } + } + if(found_scroll) { + m_opts.cursor = m_opts.cursor_scroll; + m_sig.emit(cursor_change{string{m_opts.cursor}}); + return; + } + for (auto&& action : m_opts.actions) { + if (!action.command.empty()) { + m_log.trace("Found matching fallback handler"); + if(find_click_area(action)) + return; + } + } + if(found_scroll) { + m_opts.cursor = m_opts.cursor_scroll; + m_sig.emit(cursor_change{string{m_opts.cursor}}); + return; + } + if (!string_util::compare(m_opts.cursor, "default")) { + m_log.trace("No matching cursor area found"); + m_opts.cursor = "default"; + m_sig.emit(cursor_change{string{m_opts.cursor}}); + return; + } + +} + /** * Event handler for XCB_BUTTON_PRESS events * @@ -703,6 +768,9 @@ bool bar::on(const signals::eventqueue::start&) { if (m_opts.dimvalue != 1.0) { m_connection.ensure_event_mask(m_opts.window, XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW); } + if (!m_opts.cursor_click.empty() || !m_opts.cursor_scroll.empty() ) { + m_connection.ensure_event_mask(m_opts.window, XCB_EVENT_MASK_POINTER_MOTION); + } m_log.info("Bar window: %s", m_connection.id(m_opts.window)); restack_window(); @@ -841,4 +909,12 @@ bool bar::on(const signals::ui::dim_window& sig) { return false; } +bool bar::on(const signals::ui::cursor_change& sig) { + if(!cursor_util::set_cursor(m_connection, m_connection.screen(), m_opts.window, sig.cast())) { + m_log.warn("Failed to create cursor context"); + } + m_connection.flush(); + return false; +} + POLYBAR_NS_END diff --git a/src/x11/cursor.cpp b/src/x11/cursor.cpp new file mode 100644 index 00000000..708154b0 --- /dev/null +++ b/src/x11/cursor.cpp @@ -0,0 +1,33 @@ +#include "x11/cursor.hpp" + +POLYBAR_NS + +namespace cursor_util { + bool set_cursor(xcb_connection_t *c, xcb_screen_t *screen, xcb_window_t w, string name) { + xcb_cursor_t cursor = XCB_CURSOR_NONE; + xcb_cursor_context_t *ctx; + + if (xcb_cursor_context_new(c, screen, &ctx) < 0) { + return false; + } + + const vector *name_list; + if (string_util::compare("pointer", name)) { + name_list = &pointer_names; + } else if (string_util::compare("ns-resize", name)) { + name_list = &ns_resize_names; + } else { //default + name_list = &arrow_names; + } + + for (auto&& cursor_name : *name_list) { + cursor = xcb_cursor_load_cursor(ctx, cursor_name.c_str()); + if (cursor != XCB_CURSOR_NONE) + break; + } + xcb_change_window_attributes(c, w, XCB_CW_CURSOR, &cursor); + xcb_cursor_context_free(ctx); + return true; + } +} +POLYBAR_NS_END